summaryrefslogtreecommitdiffstats
path: root/gnu/llvm/lldb/tools
diff options
context:
space:
mode:
authorpatrick <patrick@openbsd.org>2020-08-03 14:33:06 +0000
committerpatrick <patrick@openbsd.org>2020-08-03 14:33:06 +0000
commit061da546b983eb767bad15e67af1174fb0bcf31c (patch)
tree83c78b820819d70aa40c36d90447978b300078c5 /gnu/llvm/lldb/tools
parentImport LLVM 10.0.0 release including clang, lld and lldb. (diff)
downloadwireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.tar.xz
wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.zip
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom tested by plenty
Diffstat (limited to 'gnu/llvm/lldb/tools')
-rw-r--r--gnu/llvm/lldb/tools/CMakeLists.txt22
-rw-r--r--gnu/llvm/lldb/tools/argdumper/CMakeLists.txt6
-rw-r--r--gnu/llvm/lldb/tools/argdumper/argdumper.cpp20
-rw-r--r--gnu/llvm/lldb/tools/argdumper/argdumper.exports0
-rw-r--r--gnu/llvm/lldb/tools/compact-unwind/compact-unwind-dumper.c1515
-rw-r--r--gnu/llvm/lldb/tools/darwin-debug/CMakeLists.txt3
-rw-r--r--gnu/llvm/lldb/tools/darwin-debug/darwin-debug.cpp334
-rw-r--r--gnu/llvm/lldb/tools/darwin-threads/examine-threads.c507
-rw-r--r--gnu/llvm/lldb/tools/debugserver/CMakeLists.txt20
-rw-r--r--gnu/llvm/lldb/tools/debugserver/debugnub-exports2
-rw-r--r--gnu/llvm/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj1901
-rw-r--r--gnu/llvm/lldb/tools/debugserver/debugserver.xcodeproj/project.xcworkspace/contents.xcworkspacedata7
-rw-r--r--gnu/llvm/lldb/tools/debugserver/debugserver.xcodeproj/xcshareddata/xcschemes/debugserver.xcscheme110
-rw-r--r--gnu/llvm/lldb/tools/debugserver/resources/lldb-debugserver-Info.plist21
-rw-r--r--gnu/llvm/lldb/tools/debugserver/scripts/diagnose-termination.d18
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/ARM_DWARF_Registers.h205
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/ARM_ehframe_Registers.h33
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/CMakeLists.txt272
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/ChangeLog1515
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/DNB.cpp1734
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/DNB.h237
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/DNBArch.cpp79
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/DNBArch.h126
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/DNBBreakpoint.cpp177
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/DNBBreakpoint.h148
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/DNBDataRef.cpp320
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/DNBDataRef.h124
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/DNBDefs.h363
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/DNBError.cpp115
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/DNBError.h97
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/DNBLog.cpp268
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/DNBLog.h152
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/DNBRegisterInfo.cpp251
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/DNBRegisterInfo.h29
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/DNBRuntimeAction.h23
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/DNBThreadResumeActions.cpp88
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/DNBThreadResumeActions.h65
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/DNBTimer.h134
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/JSON.cpp592
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/JSON.h302
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/JSONGenerator.h314
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFBundle.cpp69
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFBundle.h35
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFString.cpp154
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFString.h40
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFUtils.h75
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/CMakeLists.txt43
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStore.cpp13
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStore.h29
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStreamSPI.h190
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/CMakeLists.txt17
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogCollector.cpp699
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogCollector.h107
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogEvent.h26
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogInterfaces.h24
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogTypes.h21
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilter.cpp11
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilter.h29
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterChain.cpp41
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterChain.h37
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterExactMatch.cpp48
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterExactMatch.h30
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterRegex.cpp94
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterRegex.h43
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessage.cpp13
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessage.h39
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessageOsLog.cpp67
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessageOsLog.h56
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/Genealogy.cpp315
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/Genealogy.h119
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/GenealogySPI.h94
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachException.cpp514
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachException.h132
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachProcess.h433
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachProcess.mm4029
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachTask.h109
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachTask.mm963
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThread.cpp782
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThread.h169
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp586
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThreadList.h96
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp296
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h47
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp184
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h72
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/OsLogger.cpp63
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/OsLogger.h19
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/ThreadInfo.h25
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp2189
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h274
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp2201
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.h248
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/dbgnub-mig.defs5
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp2382
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h238
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/i386/MachRegisterStatesI386.h241
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.cpp487
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.h159
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/stack_logging.h158
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp2885
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h241
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/MacOSX/x86_64/MachRegisterStatesX86_64.h313
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/PThreadCondition.h34
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/PThreadEvent.cpp195
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/PThreadEvent.h61
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/PThreadMutex.cpp66
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/PThreadMutex.h119
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/PseudoTerminal.cpp179
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/PseudoTerminal.h79
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/RNBContext.cpp290
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/RNBContext.h156
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/RNBDefs.h98
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/RNBRemote.cpp6313
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/RNBRemote.h431
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/RNBServices.cpp234
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/RNBServices.h28
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/RNBSocket.cpp391
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/RNBSocket.h77
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/StdStringExtractor.cpp344
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/StdStringExtractor.h105
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/SysSignal.cpp94
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/SysSignal.h21
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/TTYState.cpp93
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/TTYState.h58
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.applist.internal.plist16
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.applist.plist19
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.internal.plist15
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.plist18
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.posix.plist18
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/com.apple.internal.xpc.remote.debugserver.plist35
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/debugserver-entitlements.plist30
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/debugserver-macosx-entitlements.plist10
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/debugserver.cpp1678
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/debugserver_vers.c.in2
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/libdebugserver.cpp376
-rw-r--r--gnu/llvm/lldb/tools/debugserver/source/libdebugserver.h14
-rw-r--r--gnu/llvm/lldb/tools/driver/CMakeLists.txt48
-rw-r--r--gnu/llvm/lldb/tools/driver/Driver.cpp896
-rw-r--r--gnu/llvm/lldb/tools/driver/Driver.h102
-rw-r--r--gnu/llvm/lldb/tools/driver/Options.td238
-rw-r--r--gnu/llvm/lldb/tools/driver/Platform.cpp59
-rw-r--r--gnu/llvm/lldb/tools/driver/Platform.h90
-rw-r--r--gnu/llvm/lldb/tools/driver/lldb-Info.plist.in21
-rw-r--r--gnu/llvm/lldb/tools/intel-features/CMakeLists.txt67
-rw-r--r--gnu/llvm/lldb/tools/intel-features/README.txt73
-rw-r--r--gnu/llvm/lldb/tools/intel-features/cli-wrapper.cpp42
-rw-r--r--gnu/llvm/lldb/tools/intel-features/intel-mpx/CMakeLists.txt9
-rw-r--r--gnu/llvm/lldb/tools/intel-features/intel-mpx/cli-wrapper-mpxtable.cpp422
-rw-r--r--gnu/llvm/lldb/tools/intel-features/intel-mpx/cli-wrapper-mpxtable.h11
-rw-r--r--gnu/llvm/lldb/tools/intel-features/intel-mpx/test/Makefile7
-rw-r--r--gnu/llvm/lldb/tools/intel-features/intel-mpx/test/README.txt6
-rw-r--r--gnu/llvm/lldb/tools/intel-features/intel-mpx/test/TestMPXTable.py169
-rw-r--r--gnu/llvm/lldb/tools/intel-features/intel-mpx/test/main.cpp48
-rw-r--r--gnu/llvm/lldb/tools/intel-features/intel-pt/CMakeLists.txt31
-rw-r--r--gnu/llvm/lldb/tools/intel-features/intel-pt/Decoder.cpp901
-rw-r--r--gnu/llvm/lldb/tools/intel-features/intel-pt/Decoder.h309
-rw-r--r--gnu/llvm/lldb/tools/intel-features/intel-pt/PTDecoder.cpp149
-rw-r--r--gnu/llvm/lldb/tools/intel-features/intel-pt/PTDecoder.h270
-rw-r--r--gnu/llvm/lldb/tools/intel-features/intel-pt/README_CLI.txt123
-rw-r--r--gnu/llvm/lldb/tools/intel-features/intel-pt/README_TOOL.txt311
-rw-r--r--gnu/llvm/lldb/tools/intel-features/intel-pt/cli-wrapper-pt.cpp582
-rw-r--r--gnu/llvm/lldb/tools/intel-features/intel-pt/cli-wrapper-pt.h12
-rw-r--r--gnu/llvm/lldb/tools/intel-features/intel-pt/interface/PTDecoder.i10
-rw-r--r--gnu/llvm/lldb/tools/intel-features/scripts/CMakeLists.txt37
-rw-r--r--gnu/llvm/lldb/tools/intel-features/scripts/lldb-intel-features.swig16
-rw-r--r--gnu/llvm/lldb/tools/intel-features/scripts/python-typemaps.txt31
-rw-r--r--gnu/llvm/lldb/tools/lldb-instr/CMakeLists.txt16
-rw-r--r--gnu/llvm/lldb/tools/lldb-instr/Instrument.cpp356
-rw-r--r--gnu/llvm/lldb/tools/lldb-mi/lldb-mi.exports0
-rw-r--r--gnu/llvm/lldb/tools/lldb-perf/darwin/sketch/foobar.sketch2bin0 -> 10027 bytes
-rw-r--r--gnu/llvm/lldb/tools/lldb-server/Acceptor.cpp135
-rw-r--r--gnu/llvm/lldb/tools/lldb-server/Acceptor.h60
-rw-r--r--gnu/llvm/lldb/tools/lldb-server/CMakeLists.txt58
-rw-r--r--gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-Info.plist21
-rw-r--r--gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-entitlements.plist28
-rw-r--r--gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-macos-entitlements.plist8
-rw-r--r--gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-mig.defs5
-rw-r--r--gnu/llvm/lldb/tools/lldb-server/LLDBServerUtilities.cpp63
-rw-r--r--gnu/llvm/lldb/tools/lldb-server/LLDBServerUtilities.h23
-rw-r--r--gnu/llvm/lldb/tools/lldb-server/SystemInitializerLLGS.cpp79
-rw-r--r--gnu/llvm/lldb/tools/lldb-server/SystemInitializerLLGS.h21
-rw-r--r--gnu/llvm/lldb/tools/lldb-server/lldb-gdbserver.cpp547
-rw-r--r--gnu/llvm/lldb/tools/lldb-server/lldb-platform.cpp385
-rw-r--r--gnu/llvm/lldb/tools/lldb-server/lldb-server.cpp82
-rw-r--r--gnu/llvm/lldb/tools/lldb-server/lldb-server.exports0
-rw-r--r--gnu/llvm/lldb/tools/lldb-test/CMakeLists.txt27
-rw-r--r--gnu/llvm/lldb/tools/lldb-test/FormatUtil.cpp65
-rw-r--r--gnu/llvm/lldb/tools/lldb-test/FormatUtil.h77
-rw-r--r--gnu/llvm/lldb/tools/lldb-test/SystemInitializerTest.cpp351
-rw-r--r--gnu/llvm/lldb/tools/lldb-test/SystemInitializerTest.h32
-rw-r--r--gnu/llvm/lldb/tools/lldb-test/lldb-test.cpp1103
-rw-r--r--gnu/llvm/lldb/tools/lldb-vscode/BreakpointBase.cpp36
-rw-r--r--gnu/llvm/lldb/tools/lldb-vscode/BreakpointBase.h43
-rw-r--r--gnu/llvm/lldb/tools/lldb-vscode/CMakeLists.txt54
-rw-r--r--gnu/llvm/lldb/tools/lldb-vscode/ExceptionBreakpoint.cpp31
-rw-r--r--gnu/llvm/lldb/tools/lldb-vscode/ExceptionBreakpoint.h37
-rw-r--r--gnu/llvm/lldb/tools/lldb-vscode/FunctionBreakpoint.cpp27
-rw-r--r--gnu/llvm/lldb/tools/lldb-vscode/FunctionBreakpoint.h28
-rw-r--r--gnu/llvm/lldb/tools/lldb-vscode/IOStream.cpp158
-rw-r--r--gnu/llvm/lldb/tools/lldb-vscode/IOStream.h69
-rw-r--r--gnu/llvm/lldb/tools/lldb-vscode/JSONUtils.cpp873
-rw-r--r--gnu/llvm/lldb/tools/lldb-vscode/JSONUtils.h395
-rw-r--r--gnu/llvm/lldb/tools/lldb-vscode/LLDBUtils.cpp97
-rw-r--r--gnu/llvm/lldb/tools/lldb-vscode/LLDBUtils.h151
-rw-r--r--gnu/llvm/lldb/tools/lldb-vscode/README.md195
-rw-r--r--gnu/llvm/lldb/tools/lldb-vscode/SourceBreakpoint.cpp26
-rw-r--r--gnu/llvm/lldb/tools/lldb-vscode/SourceBreakpoint.h38
-rw-r--r--gnu/llvm/lldb/tools/lldb-vscode/SourceReference.h32
-rw-r--r--gnu/llvm/lldb/tools/lldb-vscode/VSCode.cpp306
-rw-r--r--gnu/llvm/lldb/tools/lldb-vscode/VSCode.h141
-rw-r--r--gnu/llvm/lldb/tools/lldb-vscode/VSCodeForward.h46
-rw-r--r--gnu/llvm/lldb/tools/lldb-vscode/lldb-vscode-Info.plist.in21
-rw-r--r--gnu/llvm/lldb/tools/lldb-vscode/lldb-vscode.cpp2817
-rw-r--r--gnu/llvm/lldb/tools/lldb-vscode/package.json245
214 files changed, 61562 insertions, 0 deletions
diff --git a/gnu/llvm/lldb/tools/CMakeLists.txt b/gnu/llvm/lldb/tools/CMakeLists.txt
new file mode 100644
index 00000000000..1585fd4dc4b
--- /dev/null
+++ b/gnu/llvm/lldb/tools/CMakeLists.txt
@@ -0,0 +1,22 @@
+add_subdirectory(argdumper)
+add_subdirectory(driver)
+add_subdirectory(intel-features)
+
+# We want lldb-test to be built only when it's needed,
+# i.e. if a target requires it as dependency. The typical
+# example is `check-lldb`. So, we pass EXCLUDE_FROM_ALL here.
+add_subdirectory(lldb-test EXCLUDE_FROM_ALL)
+
+add_lldb_tool_subdirectory(lldb-instr)
+add_lldb_tool_subdirectory(lldb-vscode)
+
+if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
+ add_lldb_tool_subdirectory(darwin-debug)
+ if(NOT LLDB_USE_SYSTEM_DEBUGSERVER)
+ add_lldb_tool_subdirectory(debugserver)
+ endif()
+endif()
+
+if (LLDB_CAN_USE_LLDB_SERVER)
+ add_lldb_tool_subdirectory(lldb-server)
+endif()
diff --git a/gnu/llvm/lldb/tools/argdumper/CMakeLists.txt b/gnu/llvm/lldb/tools/argdumper/CMakeLists.txt
new file mode 100644
index 00000000000..92494632519
--- /dev/null
+++ b/gnu/llvm/lldb/tools/argdumper/CMakeLists.txt
@@ -0,0 +1,6 @@
+add_lldb_tool(lldb-argdumper ADD_TO_FRAMEWORK
+ argdumper.cpp
+
+ LINK_COMPONENTS
+ Support
+)
diff --git a/gnu/llvm/lldb/tools/argdumper/argdumper.cpp b/gnu/llvm/lldb/tools/argdumper/argdumper.cpp
new file mode 100644
index 00000000000..1cf0d6dae7a
--- /dev/null
+++ b/gnu/llvm/lldb/tools/argdumper/argdumper.cpp
@@ -0,0 +1,20 @@
+//===-- argdumper.cpp --------------------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/JSON.h"
+
+using namespace llvm;
+
+int main(int argc, char *argv[]) {
+ json::Array Arguments;
+ for (int i = 1; i < argc; i++) {
+ Arguments.push_back(argv[i]);
+ }
+ llvm::outs() << json::Object({{"arguments", std::move(Arguments)}});
+ return 0;
+}
diff --git a/gnu/llvm/lldb/tools/argdumper/argdumper.exports b/gnu/llvm/lldb/tools/argdumper/argdumper.exports
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/gnu/llvm/lldb/tools/argdumper/argdumper.exports
diff --git a/gnu/llvm/lldb/tools/compact-unwind/compact-unwind-dumper.c b/gnu/llvm/lldb/tools/compact-unwind/compact-unwind-dumper.c
new file mode 100644
index 00000000000..d4706eaf538
--- /dev/null
+++ b/gnu/llvm/lldb/tools/compact-unwind/compact-unwind-dumper.c
@@ -0,0 +1,1515 @@
+#include <fcntl.h>
+#include <inttypes.h>
+#include <mach-o/compact_unwind_encoding.h>
+#include <mach-o/loader.h>
+#include <mach-o/nlist.h>
+#include <mach/machine.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/errno.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+enum {
+ UNWIND_ARM64_MODE_MASK = 0x0F000000,
+ UNWIND_ARM64_MODE_FRAMELESS = 0x02000000,
+ UNWIND_ARM64_MODE_DWARF = 0x03000000,
+ UNWIND_ARM64_MODE_FRAME = 0x04000000,
+
+ UNWIND_ARM64_FRAME_X19_X20_PAIR = 0x00000001,
+ UNWIND_ARM64_FRAME_X21_X22_PAIR = 0x00000002,
+ UNWIND_ARM64_FRAME_X23_X24_PAIR = 0x00000004,
+ UNWIND_ARM64_FRAME_X25_X26_PAIR = 0x00000008,
+ UNWIND_ARM64_FRAME_X27_X28_PAIR = 0x00000010,
+ UNWIND_ARM64_FRAME_D8_D9_PAIR = 0x00000100,
+ UNWIND_ARM64_FRAME_D10_D11_PAIR = 0x00000200,
+ UNWIND_ARM64_FRAME_D12_D13_PAIR = 0x00000400,
+ UNWIND_ARM64_FRAME_D14_D15_PAIR = 0x00000800,
+
+ UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK = 0x00FFF000,
+ UNWIND_ARM64_DWARF_SECTION_OFFSET = 0x00FFFFFF,
+};
+
+enum {
+ UNWIND_ARM_MODE_MASK = 0x0F000000,
+ UNWIND_ARM_MODE_FRAME = 0x01000000,
+ UNWIND_ARM_MODE_FRAME_D = 0x02000000,
+ UNWIND_ARM_MODE_DWARF = 0x04000000,
+
+ UNWIND_ARM_FRAME_STACK_ADJUST_MASK = 0x00C00000,
+
+ UNWIND_ARM_FRAME_FIRST_PUSH_R4 = 0x00000001,
+ UNWIND_ARM_FRAME_FIRST_PUSH_R5 = 0x00000002,
+ UNWIND_ARM_FRAME_FIRST_PUSH_R6 = 0x00000004,
+
+ UNWIND_ARM_FRAME_SECOND_PUSH_R8 = 0x00000008,
+ UNWIND_ARM_FRAME_SECOND_PUSH_R9 = 0x00000010,
+ UNWIND_ARM_FRAME_SECOND_PUSH_R10 = 0x00000020,
+ UNWIND_ARM_FRAME_SECOND_PUSH_R11 = 0x00000040,
+ UNWIND_ARM_FRAME_SECOND_PUSH_R12 = 0x00000080,
+
+ UNWIND_ARM_FRAME_D_REG_COUNT_MASK = 0x00000700,
+
+ UNWIND_ARM_DWARF_SECTION_OFFSET = 0x00FFFFFF,
+};
+
+#define EXTRACT_BITS(value, mask) \
+ ((value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask))) - 1))
+
+// A quick sketch of a program which can parse the compact unwind info
+// used on Darwin systems for exception handling. The output of
+// unwinddump will be more authoritative/reliable but this program
+// can dump at least the UNWIND_X86_64_MODE_RBP_FRAME format entries
+// correctly.
+
+struct symbol {
+ uint64_t file_address;
+ const char *name;
+};
+
+int symbol_compare(const void *a, const void *b) {
+ return (int)((struct symbol *)a)->file_address -
+ ((struct symbol *)b)->file_address;
+}
+
+struct baton {
+ cpu_type_t cputype;
+
+ uint8_t *mach_header_start; // pointer into this program's address space
+ uint8_t *compact_unwind_start; // pointer into this program's address space
+
+ int addr_size; // 4 or 8 bytes, the size of addresses in this file
+
+ uint64_t text_segment_vmaddr; // __TEXT segment vmaddr
+ uint64_t text_segment_file_offset;
+
+ uint64_t text_section_vmaddr; // __TEXT,__text section vmaddr
+ uint64_t text_section_file_offset;
+
+ uint64_t eh_section_file_address; // the file address of the __TEXT,__eh_frame
+ // section
+
+ uint8_t
+ *lsda_array_start; // for the currently-being-processed first-level index
+ uint8_t
+ *lsda_array_end; // the lsda_array_start for the NEXT first-level index
+
+ struct symbol *symbols;
+ int symbols_count;
+
+ uint64_t *function_start_addresses;
+ int function_start_addresses_count;
+
+ int current_index_table_number;
+
+ struct unwind_info_section_header unwind_header;
+ struct unwind_info_section_header_index_entry first_level_index_entry;
+ struct unwind_info_compressed_second_level_page_header
+ compressed_second_level_page_header;
+ struct unwind_info_regular_second_level_page_header
+ regular_second_level_page_header;
+};
+
+uint64_t read_leb128(uint8_t **offset) {
+ uint64_t result = 0;
+ int shift = 0;
+ while (1) {
+ uint8_t byte = **offset;
+ *offset = *offset + 1;
+ result |= (byte & 0x7f) << shift;
+ if ((byte & 0x80) == 0)
+ break;
+ shift += 7;
+ }
+
+ return result;
+}
+
+// step through the load commands in a thin mach-o binary,
+// find the cputype and the start of the __TEXT,__unwind_info
+// section, return a pointer to that section or NULL if not found.
+
+static void scan_macho_load_commands(struct baton *baton) {
+ struct symtab_command symtab_cmd;
+ uint64_t linkedit_segment_vmaddr;
+ uint64_t linkedit_segment_file_offset;
+
+ baton->compact_unwind_start = 0;
+
+ uint32_t *magic = (uint32_t *)baton->mach_header_start;
+
+ if (*magic != MH_MAGIC && *magic != MH_MAGIC_64) {
+ printf("Unexpected magic number 0x%x in header, exiting.", *magic);
+ exit(1);
+ }
+
+ bool is_64bit = false;
+ if (*magic == MH_MAGIC_64)
+ is_64bit = true;
+
+ uint8_t *offset = baton->mach_header_start;
+
+ struct mach_header mh;
+ memcpy(&mh, offset, sizeof(struct mach_header));
+ if (is_64bit)
+ offset += sizeof(struct mach_header_64);
+ else
+ offset += sizeof(struct mach_header);
+
+ if (is_64bit)
+ baton->addr_size = 8;
+ else
+ baton->addr_size = 4;
+
+ baton->cputype = mh.cputype;
+
+ uint8_t *start_of_load_commands = offset;
+
+ uint32_t cur_cmd = 0;
+ while (cur_cmd < mh.ncmds &&
+ (offset - start_of_load_commands) < mh.sizeofcmds) {
+ struct load_command lc;
+ uint32_t *lc_cmd = (uint32_t *)offset;
+ uint32_t *lc_cmdsize = (uint32_t *)offset + 1;
+ uint8_t *start_of_this_load_cmd = offset;
+
+ if (*lc_cmd == LC_SEGMENT || *lc_cmd == LC_SEGMENT_64) {
+ char segment_name[17];
+ segment_name[0] = '\0';
+ uint32_t nsects = 0;
+ uint64_t segment_offset = 0;
+ uint64_t segment_vmaddr = 0;
+
+ if (*lc_cmd == LC_SEGMENT_64) {
+ struct segment_command_64 seg;
+ memcpy(&seg, offset, sizeof(struct segment_command_64));
+ memcpy(&segment_name, &seg.segname, 16);
+ segment_name[16] = '\0';
+ nsects = seg.nsects;
+ segment_offset = seg.fileoff;
+ segment_vmaddr = seg.vmaddr;
+ offset += sizeof(struct segment_command_64);
+ if ((seg.flags & SG_PROTECTED_VERSION_1) == SG_PROTECTED_VERSION_1) {
+ printf("Segment '%s' is encrypted.\n", segment_name);
+ }
+ }
+
+ if (*lc_cmd == LC_SEGMENT) {
+ struct segment_command seg;
+ memcpy(&seg, offset, sizeof(struct segment_command));
+ memcpy(&segment_name, &seg.segname, 16);
+ segment_name[16] = '\0';
+ nsects = seg.nsects;
+ segment_offset = seg.fileoff;
+ segment_vmaddr = seg.vmaddr;
+ offset += sizeof(struct segment_command);
+ if ((seg.flags & SG_PROTECTED_VERSION_1) == SG_PROTECTED_VERSION_1) {
+ printf("Segment '%s' is encrypted.\n", segment_name);
+ }
+ }
+
+ if (nsects != 0 && strcmp(segment_name, "__TEXT") == 0) {
+ baton->text_segment_vmaddr = segment_vmaddr;
+ baton->text_segment_file_offset = segment_offset;
+
+ uint32_t current_sect = 0;
+ while (current_sect < nsects &&
+ (offset - start_of_this_load_cmd) < *lc_cmdsize) {
+ char sect_name[17];
+ memcpy(&sect_name, offset, 16);
+ sect_name[16] = '\0';
+ if (strcmp(sect_name, "__unwind_info") == 0) {
+ if (is_64bit) {
+ struct section_64 sect;
+ memset(&sect, 0, sizeof(struct section_64));
+ memcpy(&sect, offset, sizeof(struct section_64));
+ baton->compact_unwind_start =
+ baton->mach_header_start + sect.offset;
+ } else {
+ struct section sect;
+ memset(&sect, 0, sizeof(struct section));
+ memcpy(&sect, offset, sizeof(struct section));
+ baton->compact_unwind_start =
+ baton->mach_header_start + sect.offset;
+ }
+ }
+ if (strcmp(sect_name, "__eh_frame") == 0) {
+ if (is_64bit) {
+ struct section_64 sect;
+ memset(&sect, 0, sizeof(struct section_64));
+ memcpy(&sect, offset, sizeof(struct section_64));
+ baton->eh_section_file_address = sect.addr;
+ } else {
+ struct section sect;
+ memset(&sect, 0, sizeof(struct section));
+ memcpy(&sect, offset, sizeof(struct section));
+ baton->eh_section_file_address = sect.addr;
+ }
+ }
+ if (strcmp(sect_name, "__text") == 0) {
+ if (is_64bit) {
+ struct section_64 sect;
+ memset(&sect, 0, sizeof(struct section_64));
+ memcpy(&sect, offset, sizeof(struct section_64));
+ baton->text_section_vmaddr = sect.addr;
+ baton->text_section_file_offset = sect.offset;
+ } else {
+ struct section sect;
+ memset(&sect, 0, sizeof(struct section));
+ memcpy(&sect, offset, sizeof(struct section));
+ baton->text_section_vmaddr = sect.addr;
+ }
+ }
+ if (is_64bit) {
+ offset += sizeof(struct section_64);
+ } else {
+ offset += sizeof(struct section);
+ }
+ }
+ }
+
+ if (strcmp(segment_name, "__LINKEDIT") == 0) {
+ linkedit_segment_vmaddr = segment_vmaddr;
+ linkedit_segment_file_offset = segment_offset;
+ }
+ }
+
+ if (*lc_cmd == LC_SYMTAB) {
+ memcpy(&symtab_cmd, offset, sizeof(struct symtab_command));
+ }
+
+ if (*lc_cmd == LC_DYSYMTAB) {
+ struct dysymtab_command dysymtab_cmd;
+ memcpy(&dysymtab_cmd, offset, sizeof(struct dysymtab_command));
+
+ int nlist_size = 12;
+ if (is_64bit)
+ nlist_size = 16;
+
+ char *string_table =
+ (char *)(baton->mach_header_start + symtab_cmd.stroff);
+ uint8_t *local_syms = baton->mach_header_start + symtab_cmd.symoff +
+ (dysymtab_cmd.ilocalsym * nlist_size);
+ int local_syms_count = dysymtab_cmd.nlocalsym;
+ uint8_t *exported_syms = baton->mach_header_start + symtab_cmd.symoff +
+ (dysymtab_cmd.iextdefsym * nlist_size);
+ int exported_syms_count = dysymtab_cmd.nextdefsym;
+
+ // We're only going to create records for a small number of these symbols
+ // but to
+ // simplify the memory management I'll allocate enough space to store all
+ // of them.
+ baton->symbols = (struct symbol *)malloc(
+ sizeof(struct symbol) * (local_syms_count + exported_syms_count));
+ baton->symbols_count = 0;
+
+ for (int i = 0; i < local_syms_count; i++) {
+ struct nlist_64 nlist;
+ memset(&nlist, 0, sizeof(struct nlist_64));
+ if (is_64bit) {
+ memcpy(&nlist, local_syms + (i * nlist_size),
+ sizeof(struct nlist_64));
+ } else {
+ struct nlist nlist_32;
+ memset(&nlist_32, 0, sizeof(struct nlist));
+ memcpy(&nlist_32, local_syms + (i * nlist_size),
+ sizeof(struct nlist));
+ nlist.n_un.n_strx = nlist_32.n_un.n_strx;
+ nlist.n_type = nlist_32.n_type;
+ nlist.n_sect = nlist_32.n_sect;
+ nlist.n_desc = nlist_32.n_desc;
+ nlist.n_value = nlist_32.n_value;
+ }
+ if ((nlist.n_type & N_STAB) == 0 &&
+ ((nlist.n_type & N_EXT) == 1 ||
+ ((nlist.n_type & N_TYPE) == N_TYPE && nlist.n_sect != NO_SECT)) &&
+ nlist.n_value != 0 && nlist.n_value != baton->text_segment_vmaddr) {
+ baton->symbols[baton->symbols_count].file_address = nlist.n_value;
+ if (baton->cputype == CPU_TYPE_ARM)
+ baton->symbols[baton->symbols_count].file_address =
+ baton->symbols[baton->symbols_count].file_address & ~1;
+ baton->symbols[baton->symbols_count].name =
+ string_table + nlist.n_un.n_strx;
+ baton->symbols_count++;
+ }
+ }
+
+ for (int i = 0; i < exported_syms_count; i++) {
+ struct nlist_64 nlist;
+ memset(&nlist, 0, sizeof(struct nlist_64));
+ if (is_64bit) {
+ memcpy(&nlist, exported_syms + (i * nlist_size),
+ sizeof(struct nlist_64));
+ } else {
+ struct nlist nlist_32;
+ memcpy(&nlist_32, exported_syms + (i * nlist_size),
+ sizeof(struct nlist));
+ nlist.n_un.n_strx = nlist_32.n_un.n_strx;
+ nlist.n_type = nlist_32.n_type;
+ nlist.n_sect = nlist_32.n_sect;
+ nlist.n_desc = nlist_32.n_desc;
+ nlist.n_value = nlist_32.n_value;
+ }
+ if ((nlist.n_type & N_STAB) == 0 &&
+ ((nlist.n_type & N_EXT) == 1 ||
+ ((nlist.n_type & N_TYPE) == N_TYPE && nlist.n_sect != NO_SECT)) &&
+ nlist.n_value != 0 && nlist.n_value != baton->text_segment_vmaddr) {
+ baton->symbols[baton->symbols_count].file_address = nlist.n_value;
+ if (baton->cputype == CPU_TYPE_ARM)
+ baton->symbols[baton->symbols_count].file_address =
+ baton->symbols[baton->symbols_count].file_address & ~1;
+ baton->symbols[baton->symbols_count].name =
+ string_table + nlist.n_un.n_strx;
+ baton->symbols_count++;
+ }
+ }
+
+ qsort(baton->symbols, baton->symbols_count, sizeof(struct symbol),
+ symbol_compare);
+ }
+
+ if (*lc_cmd == LC_FUNCTION_STARTS) {
+ struct linkedit_data_command function_starts_cmd;
+ memcpy(&function_starts_cmd, offset,
+ sizeof(struct linkedit_data_command));
+
+ uint8_t *funcstarts_offset =
+ baton->mach_header_start + function_starts_cmd.dataoff;
+ uint8_t *function_end = funcstarts_offset + function_starts_cmd.datasize;
+ int count = 0;
+
+ while (funcstarts_offset < function_end) {
+ if (read_leb128(&funcstarts_offset) != 0) {
+ count++;
+ }
+ }
+
+ baton->function_start_addresses =
+ (uint64_t *)malloc(sizeof(uint64_t) * count);
+ baton->function_start_addresses_count = count;
+
+ funcstarts_offset =
+ baton->mach_header_start + function_starts_cmd.dataoff;
+ uint64_t current_pc = baton->text_segment_vmaddr;
+ int i = 0;
+ while (funcstarts_offset < function_end) {
+ uint64_t func_start = read_leb128(&funcstarts_offset);
+ if (func_start != 0) {
+ current_pc += func_start;
+ baton->function_start_addresses[i++] = current_pc;
+ }
+ }
+ }
+
+ offset = start_of_this_load_cmd + *lc_cmdsize;
+ cur_cmd++;
+ }
+
+ // Augment the symbol table with the function starts table -- adding symbol
+ // entries
+ // for functions that were stripped.
+
+ int unnamed_functions_to_add = 0;
+ for (int i = 0; i < baton->function_start_addresses_count; i++) {
+ struct symbol search_key;
+ search_key.file_address = baton->function_start_addresses[i];
+ if (baton->cputype == CPU_TYPE_ARM)
+ search_key.file_address = search_key.file_address & ~1;
+ struct symbol *sym =
+ bsearch(&search_key, baton->symbols, baton->symbols_count,
+ sizeof(struct symbol), symbol_compare);
+ if (sym == NULL)
+ unnamed_functions_to_add++;
+ }
+
+ baton->symbols = (struct symbol *)realloc(
+ baton->symbols, sizeof(struct symbol) *
+ (baton->symbols_count + unnamed_functions_to_add));
+
+ int current_unnamed_symbol = 1;
+ int number_symbols_added = 0;
+ for (int i = 0; i < baton->function_start_addresses_count; i++) {
+ struct symbol search_key;
+ search_key.file_address = baton->function_start_addresses[i];
+ if (baton->cputype == CPU_TYPE_ARM)
+ search_key.file_address = search_key.file_address & ~1;
+ struct symbol *sym =
+ bsearch(&search_key, baton->symbols, baton->symbols_count,
+ sizeof(struct symbol), symbol_compare);
+ if (sym == NULL) {
+ char *name;
+ asprintf(&name, "unnamed function #%d", current_unnamed_symbol++);
+ baton->symbols[baton->symbols_count + number_symbols_added].file_address =
+ baton->function_start_addresses[i];
+ baton->symbols[baton->symbols_count + number_symbols_added].name = name;
+ number_symbols_added++;
+ }
+ }
+ baton->symbols_count += number_symbols_added;
+ qsort(baton->symbols, baton->symbols_count, sizeof(struct symbol),
+ symbol_compare);
+
+ // printf ("function start addresses\n");
+ // for (int i = 0; i < baton->function_start_addresses_count; i++)
+ // {
+ // printf ("0x%012llx\n", baton->function_start_addresses[i]);
+ // }
+
+ // printf ("symbol table names & addresses\n");
+ // for (int i = 0; i < baton->symbols_count; i++)
+ // {
+ // printf ("0x%012llx %s\n", baton->symbols[i].file_address,
+ // baton->symbols[i].name);
+ // }
+}
+
+void print_encoding_x86_64(struct baton baton, uint8_t *function_start,
+ uint32_t encoding) {
+ int mode = encoding & UNWIND_X86_64_MODE_MASK;
+ switch (mode) {
+ case UNWIND_X86_64_MODE_RBP_FRAME: {
+ printf("frame func: CFA is rbp+%d ", 16);
+ printf(" rip=[CFA-8] rbp=[CFA-16]");
+ uint32_t saved_registers_offset =
+ EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_OFFSET);
+
+ uint32_t saved_registers_locations =
+ EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_REGISTERS);
+
+ saved_registers_offset += 2;
+
+ for (int i = 0; i < 5; i++) {
+ switch (saved_registers_locations & 0x7) {
+ case UNWIND_X86_64_REG_NONE:
+ break;
+ case UNWIND_X86_64_REG_RBX:
+ printf(" rbx=[CFA-%d]", saved_registers_offset * 8);
+ break;
+ case UNWIND_X86_64_REG_R12:
+ printf(" r12=[CFA-%d]", saved_registers_offset * 8);
+ break;
+ case UNWIND_X86_64_REG_R13:
+ printf(" r13=[CFA-%d]", saved_registers_offset * 8);
+ break;
+ case UNWIND_X86_64_REG_R14:
+ printf(" r14=[CFA-%d]", saved_registers_offset * 8);
+ break;
+ case UNWIND_X86_64_REG_R15:
+ printf(" r15=[CFA-%d]", saved_registers_offset * 8);
+ break;
+ }
+ saved_registers_offset--;
+ saved_registers_locations >>= 3;
+ }
+ } break;
+
+ case UNWIND_X86_64_MODE_STACK_IND:
+ case UNWIND_X86_64_MODE_STACK_IMMD: {
+ uint32_t stack_size =
+ EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);
+ uint32_t register_count =
+ EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT);
+ uint32_t permutation =
+ EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION);
+
+ if (mode == UNWIND_X86_64_MODE_STACK_IND && function_start) {
+ uint32_t stack_adjust =
+ EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST);
+
+ // offset into the function instructions; 0 == beginning of first
+ // instruction
+ uint32_t offset_to_subl_insn =
+ EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);
+
+ stack_size = *((uint32_t *)(function_start + offset_to_subl_insn));
+
+ stack_size += stack_adjust * 8;
+
+ printf("large stack ");
+ }
+
+ if (mode == UNWIND_X86_64_MODE_STACK_IND) {
+ printf("frameless function: stack size %d, register count %d ",
+ stack_size * 8, register_count);
+ } else {
+ printf("frameless function: stack size %d, register count %d ",
+ stack_size, register_count);
+ }
+
+ if (register_count == 0) {
+ printf(" no registers saved");
+ } else {
+
+ // We need to include (up to) 6 registers in 10 bits.
+ // That would be 18 bits if we just used 3 bits per reg to indicate
+ // the order they're saved on the stack.
+ //
+ // This is done with Lehmer code permutation, e.g. see
+ // http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms
+ int permunreg[6];
+
+ // This decodes the variable-base number in the 10 bits
+ // and gives us the Lehmer code sequence which can then
+ // be decoded.
+
+ switch (register_count) {
+ case 6:
+ permunreg[0] = permutation / 120; // 120 == 5!
+ permutation -= (permunreg[0] * 120);
+ permunreg[1] = permutation / 24; // 24 == 4!
+ permutation -= (permunreg[1] * 24);
+ permunreg[2] = permutation / 6; // 6 == 3!
+ permutation -= (permunreg[2] * 6);
+ permunreg[3] = permutation / 2; // 2 == 2!
+ permutation -= (permunreg[3] * 2);
+ permunreg[4] = permutation; // 1 == 1!
+ permunreg[5] = 0;
+ break;
+ case 5:
+ permunreg[0] = permutation / 120;
+ permutation -= (permunreg[0] * 120);
+ permunreg[1] = permutation / 24;
+ permutation -= (permunreg[1] * 24);
+ permunreg[2] = permutation / 6;
+ permutation -= (permunreg[2] * 6);
+ permunreg[3] = permutation / 2;
+ permutation -= (permunreg[3] * 2);
+ permunreg[4] = permutation;
+ break;
+ case 4:
+ permunreg[0] = permutation / 60;
+ permutation -= (permunreg[0] * 60);
+ permunreg[1] = permutation / 12;
+ permutation -= (permunreg[1] * 12);
+ permunreg[2] = permutation / 3;
+ permutation -= (permunreg[2] * 3);
+ permunreg[3] = permutation;
+ break;
+ case 3:
+ permunreg[0] = permutation / 20;
+ permutation -= (permunreg[0] * 20);
+ permunreg[1] = permutation / 4;
+ permutation -= (permunreg[1] * 4);
+ permunreg[2] = permutation;
+ break;
+ case 2:
+ permunreg[0] = permutation / 5;
+ permutation -= (permunreg[0] * 5);
+ permunreg[1] = permutation;
+ break;
+ case 1:
+ permunreg[0] = permutation;
+ break;
+ }
+
+ // Decode the Lehmer code for this permutation of
+ // the registers v. http://en.wikipedia.org/wiki/Lehmer_code
+
+ int registers[6];
+ bool used[7] = {false, false, false, false, false, false, false};
+ for (int i = 0; i < register_count; i++) {
+ int renum = 0;
+ for (int j = 1; j < 7; j++) {
+ if (used[j] == false) {
+ if (renum == permunreg[i]) {
+ registers[i] = j;
+ used[j] = true;
+ break;
+ }
+ renum++;
+ }
+ }
+ }
+
+ if (mode == UNWIND_X86_64_MODE_STACK_IND) {
+ printf(" CFA is rsp+%d ", stack_size);
+ } else {
+ printf(" CFA is rsp+%d ", stack_size * 8);
+ }
+
+ uint32_t saved_registers_offset = 1;
+ printf(" rip=[CFA-%d]", saved_registers_offset * 8);
+ saved_registers_offset++;
+
+ for (int i = (sizeof(registers) / sizeof(int)) - 1; i >= 0; i--) {
+ switch (registers[i]) {
+ case UNWIND_X86_64_REG_NONE:
+ break;
+ case UNWIND_X86_64_REG_RBX:
+ printf(" rbx=[CFA-%d]", saved_registers_offset * 8);
+ saved_registers_offset++;
+ break;
+ case UNWIND_X86_64_REG_R12:
+ printf(" r12=[CFA-%d]", saved_registers_offset * 8);
+ saved_registers_offset++;
+ break;
+ case UNWIND_X86_64_REG_R13:
+ printf(" r13=[CFA-%d]", saved_registers_offset * 8);
+ saved_registers_offset++;
+ break;
+ case UNWIND_X86_64_REG_R14:
+ printf(" r14=[CFA-%d]", saved_registers_offset * 8);
+ saved_registers_offset++;
+ break;
+ case UNWIND_X86_64_REG_R15:
+ printf(" r15=[CFA-%d]", saved_registers_offset * 8);
+ saved_registers_offset++;
+ break;
+ case UNWIND_X86_64_REG_RBP:
+ printf(" rbp=[CFA-%d]", saved_registers_offset * 8);
+ saved_registers_offset++;
+ break;
+ }
+ }
+ }
+
+ } break;
+
+ case UNWIND_X86_64_MODE_DWARF: {
+ uint32_t dwarf_offset = encoding & UNWIND_X86_DWARF_SECTION_OFFSET;
+ printf(
+ "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64
+ ")",
+ dwarf_offset, dwarf_offset + baton.eh_section_file_address);
+ } break;
+
+ case 0: {
+ printf(" no unwind information");
+ } break;
+ }
+}
+
+void print_encoding_i386(struct baton baton, uint8_t *function_start,
+ uint32_t encoding) {
+ int mode = encoding & UNWIND_X86_MODE_MASK;
+ switch (mode) {
+ case UNWIND_X86_MODE_EBP_FRAME: {
+ printf("frame func: CFA is ebp+%d ", 8);
+ printf(" eip=[CFA-4] ebp=[CFA-8]");
+ uint32_t saved_registers_offset =
+ EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_OFFSET);
+
+ uint32_t saved_registers_locations =
+ EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_REGISTERS);
+
+ saved_registers_offset += 2;
+
+ for (int i = 0; i < 5; i++) {
+ switch (saved_registers_locations & 0x7) {
+ case UNWIND_X86_REG_NONE:
+ break;
+ case UNWIND_X86_REG_EBX:
+ printf(" ebx=[CFA-%d]", saved_registers_offset * 4);
+ break;
+ case UNWIND_X86_REG_ECX:
+ printf(" ecx=[CFA-%d]", saved_registers_offset * 4);
+ break;
+ case UNWIND_X86_REG_EDX:
+ printf(" edx=[CFA-%d]", saved_registers_offset * 4);
+ break;
+ case UNWIND_X86_REG_EDI:
+ printf(" edi=[CFA-%d]", saved_registers_offset * 4);
+ break;
+ case UNWIND_X86_REG_ESI:
+ printf(" esi=[CFA-%d]", saved_registers_offset * 4);
+ break;
+ }
+ saved_registers_offset--;
+ saved_registers_locations >>= 3;
+ }
+ } break;
+
+ case UNWIND_X86_MODE_STACK_IND:
+ case UNWIND_X86_MODE_STACK_IMMD: {
+ uint32_t stack_size =
+ EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE);
+ uint32_t register_count =
+ EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT);
+ uint32_t permutation =
+ EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION);
+
+ if (mode == UNWIND_X86_MODE_STACK_IND && function_start) {
+ uint32_t stack_adjust =
+ EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST);
+
+ // offset into the function instructions; 0 == beginning of first
+ // instruction
+ uint32_t offset_to_subl_insn =
+ EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE);
+
+ stack_size = *((uint32_t *)(function_start + offset_to_subl_insn));
+
+ stack_size += stack_adjust * 4;
+
+ printf("large stack ");
+ }
+
+ if (mode == UNWIND_X86_MODE_STACK_IND) {
+ printf("frameless function: stack size %d, register count %d ",
+ stack_size, register_count);
+ } else {
+ printf("frameless function: stack size %d, register count %d ",
+ stack_size * 4, register_count);
+ }
+
+ if (register_count == 0) {
+ printf(" no registers saved");
+ } else {
+
+ // We need to include (up to) 6 registers in 10 bits.
+ // That would be 18 bits if we just used 3 bits per reg to indicate
+ // the order they're saved on the stack.
+ //
+ // This is done with Lehmer code permutation, e.g. see
+ // http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms
+ int permunreg[6];
+
+ // This decodes the variable-base number in the 10 bits
+ // and gives us the Lehmer code sequence which can then
+ // be decoded.
+
+ switch (register_count) {
+ case 6:
+ permunreg[0] = permutation / 120; // 120 == 5!
+ permutation -= (permunreg[0] * 120);
+ permunreg[1] = permutation / 24; // 24 == 4!
+ permutation -= (permunreg[1] * 24);
+ permunreg[2] = permutation / 6; // 6 == 3!
+ permutation -= (permunreg[2] * 6);
+ permunreg[3] = permutation / 2; // 2 == 2!
+ permutation -= (permunreg[3] * 2);
+ permunreg[4] = permutation; // 1 == 1!
+ permunreg[5] = 0;
+ break;
+ case 5:
+ permunreg[0] = permutation / 120;
+ permutation -= (permunreg[0] * 120);
+ permunreg[1] = permutation / 24;
+ permutation -= (permunreg[1] * 24);
+ permunreg[2] = permutation / 6;
+ permutation -= (permunreg[2] * 6);
+ permunreg[3] = permutation / 2;
+ permutation -= (permunreg[3] * 2);
+ permunreg[4] = permutation;
+ break;
+ case 4:
+ permunreg[0] = permutation / 60;
+ permutation -= (permunreg[0] * 60);
+ permunreg[1] = permutation / 12;
+ permutation -= (permunreg[1] * 12);
+ permunreg[2] = permutation / 3;
+ permutation -= (permunreg[2] * 3);
+ permunreg[3] = permutation;
+ break;
+ case 3:
+ permunreg[0] = permutation / 20;
+ permutation -= (permunreg[0] * 20);
+ permunreg[1] = permutation / 4;
+ permutation -= (permunreg[1] * 4);
+ permunreg[2] = permutation;
+ break;
+ case 2:
+ permunreg[0] = permutation / 5;
+ permutation -= (permunreg[0] * 5);
+ permunreg[1] = permutation;
+ break;
+ case 1:
+ permunreg[0] = permutation;
+ break;
+ }
+
+ // Decode the Lehmer code for this permutation of
+ // the registers v. http://en.wikipedia.org/wiki/Lehmer_code
+
+ int registers[6];
+ bool used[7] = {false, false, false, false, false, false, false};
+ for (int i = 0; i < register_count; i++) {
+ int renum = 0;
+ for (int j = 1; j < 7; j++) {
+ if (used[j] == false) {
+ if (renum == permunreg[i]) {
+ registers[i] = j;
+ used[j] = true;
+ break;
+ }
+ renum++;
+ }
+ }
+ }
+
+ if (mode == UNWIND_X86_MODE_STACK_IND) {
+ printf(" CFA is esp+%d ", stack_size);
+ } else {
+ printf(" CFA is esp+%d ", stack_size * 4);
+ }
+
+ uint32_t saved_registers_offset = 1;
+ printf(" eip=[CFA-%d]", saved_registers_offset * 4);
+ saved_registers_offset++;
+
+ for (int i = (sizeof(registers) / sizeof(int)) - 1; i >= 0; i--) {
+ switch (registers[i]) {
+ case UNWIND_X86_REG_NONE:
+ break;
+ case UNWIND_X86_REG_EBX:
+ printf(" ebx=[CFA-%d]", saved_registers_offset * 4);
+ saved_registers_offset++;
+ break;
+ case UNWIND_X86_REG_ECX:
+ printf(" ecx=[CFA-%d]", saved_registers_offset * 4);
+ saved_registers_offset++;
+ break;
+ case UNWIND_X86_REG_EDX:
+ printf(" edx=[CFA-%d]", saved_registers_offset * 4);
+ saved_registers_offset++;
+ break;
+ case UNWIND_X86_REG_EDI:
+ printf(" edi=[CFA-%d]", saved_registers_offset * 4);
+ saved_registers_offset++;
+ break;
+ case UNWIND_X86_REG_ESI:
+ printf(" esi=[CFA-%d]", saved_registers_offset * 4);
+ saved_registers_offset++;
+ break;
+ case UNWIND_X86_REG_EBP:
+ printf(" ebp=[CFA-%d]", saved_registers_offset * 4);
+ saved_registers_offset++;
+ break;
+ }
+ }
+ }
+
+ } break;
+
+ case UNWIND_X86_MODE_DWARF: {
+ uint32_t dwarf_offset = encoding & UNWIND_X86_DWARF_SECTION_OFFSET;
+ printf(
+ "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64
+ ")",
+ dwarf_offset, dwarf_offset + baton.eh_section_file_address);
+ } break;
+
+ case 0: {
+ printf(" no unwind information");
+ } break;
+ }
+}
+
+void print_encoding_arm64(struct baton baton, uint8_t *function_start,
+ uint32_t encoding) {
+ const int wordsize = 8;
+ int mode = encoding & UNWIND_ARM64_MODE_MASK;
+ switch (mode) {
+ case UNWIND_ARM64_MODE_FRAME: {
+ printf("frame func: CFA is fp+%d ", 16);
+ printf(" pc=[CFA-8] fp=[CFA-16]");
+ int reg_pairs_saved_count = 1;
+ uint32_t saved_register_bits = encoding & 0xfff;
+ if (saved_register_bits & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
+ int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
+ cfa_offset -= wordsize;
+ printf(" x19=[CFA%d]", cfa_offset);
+ cfa_offset -= wordsize;
+ printf(" x20=[CFA%d]", cfa_offset);
+ reg_pairs_saved_count++;
+ }
+ if (saved_register_bits & UNWIND_ARM64_FRAME_X21_X22_PAIR) {
+ int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
+ cfa_offset -= wordsize;
+ printf(" x21=[CFA%d]", cfa_offset);
+ cfa_offset -= wordsize;
+ printf(" x22=[CFA%d]", cfa_offset);
+ reg_pairs_saved_count++;
+ }
+ if (saved_register_bits & UNWIND_ARM64_FRAME_X23_X24_PAIR) {
+ int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
+ cfa_offset -= wordsize;
+ printf(" x23=[CFA%d]", cfa_offset);
+ cfa_offset -= wordsize;
+ printf(" x24=[CFA%d]", cfa_offset);
+ reg_pairs_saved_count++;
+ }
+ if (saved_register_bits & UNWIND_ARM64_FRAME_X25_X26_PAIR) {
+ int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
+ cfa_offset -= wordsize;
+ printf(" x25=[CFA%d]", cfa_offset);
+ cfa_offset -= wordsize;
+ printf(" x26=[CFA%d]", cfa_offset);
+ reg_pairs_saved_count++;
+ }
+ if (saved_register_bits & UNWIND_ARM64_FRAME_X27_X28_PAIR) {
+ int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
+ cfa_offset -= wordsize;
+ printf(" x27=[CFA%d]", cfa_offset);
+ cfa_offset -= wordsize;
+ printf(" x28=[CFA%d]", cfa_offset);
+ reg_pairs_saved_count++;
+ }
+ if (saved_register_bits & UNWIND_ARM64_FRAME_D8_D9_PAIR) {
+ int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
+ cfa_offset -= wordsize;
+ printf(" d8=[CFA%d]", cfa_offset);
+ cfa_offset -= wordsize;
+ printf(" d9=[CFA%d]", cfa_offset);
+ reg_pairs_saved_count++;
+ }
+ if (saved_register_bits & UNWIND_ARM64_FRAME_D10_D11_PAIR) {
+ int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
+ cfa_offset -= wordsize;
+ printf(" d10=[CFA%d]", cfa_offset);
+ cfa_offset -= wordsize;
+ printf(" d11=[CFA%d]", cfa_offset);
+ reg_pairs_saved_count++;
+ }
+ if (saved_register_bits & UNWIND_ARM64_FRAME_D12_D13_PAIR) {
+ int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
+ cfa_offset -= wordsize;
+ printf(" d12=[CFA%d]", cfa_offset);
+ cfa_offset -= wordsize;
+ printf(" d13=[CFA%d]", cfa_offset);
+ reg_pairs_saved_count++;
+ }
+ if (saved_register_bits & UNWIND_ARM64_FRAME_D14_D15_PAIR) {
+ int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
+ cfa_offset -= wordsize;
+ printf(" d14=[CFA%d]", cfa_offset);
+ cfa_offset -= wordsize;
+ printf(" d15=[CFA%d]", cfa_offset);
+ reg_pairs_saved_count++;
+ }
+
+ } break;
+
+ case UNWIND_ARM64_MODE_FRAMELESS: {
+ uint32_t stack_size = encoding & UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK;
+ printf("frameless function: stack size %d ", stack_size * 16);
+
+ } break;
+
+ case UNWIND_ARM64_MODE_DWARF: {
+ uint32_t dwarf_offset = encoding & UNWIND_ARM64_DWARF_SECTION_OFFSET;
+ printf(
+ "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64
+ ")",
+ dwarf_offset, dwarf_offset + baton.eh_section_file_address);
+ } break;
+
+ case 0: {
+ printf(" no unwind information");
+ } break;
+ }
+}
+
+void print_encoding_armv7(struct baton baton, uint8_t *function_start,
+ uint32_t encoding) {
+ const int wordsize = 4;
+ int mode = encoding & UNWIND_ARM_MODE_MASK;
+ switch (mode) {
+ case UNWIND_ARM_MODE_FRAME_D:
+ case UNWIND_ARM_MODE_FRAME: {
+ int stack_adjust =
+ EXTRACT_BITS(encoding, UNWIND_ARM_FRAME_STACK_ADJUST_MASK) * wordsize;
+
+ printf("frame func: CFA is fp+%d ", (2 * wordsize) + stack_adjust);
+ int cfa_offset = -stack_adjust;
+
+ cfa_offset -= wordsize;
+ printf(" pc=[CFA%d]", cfa_offset);
+ cfa_offset -= wordsize;
+ printf(" fp=[CFA%d]", cfa_offset);
+
+ uint32_t saved_register_bits = encoding & 0xff;
+ if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R6) {
+ cfa_offset -= wordsize;
+ printf(" r6=[CFA%d]", cfa_offset);
+ }
+ if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R5) {
+ cfa_offset -= wordsize;
+ printf(" r5=[CFA%d]", cfa_offset);
+ }
+ if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R4) {
+ cfa_offset -= wordsize;
+ printf(" r4=[CFA%d]", cfa_offset);
+ }
+ if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R12) {
+ cfa_offset -= wordsize;
+ printf(" r12=[CFA%d]", cfa_offset);
+ }
+ if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R11) {
+ cfa_offset -= wordsize;
+ printf(" r11=[CFA%d]", cfa_offset);
+ }
+ if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R10) {
+ cfa_offset -= wordsize;
+ printf(" r10=[CFA%d]", cfa_offset);
+ }
+ if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R9) {
+ cfa_offset -= wordsize;
+ printf(" r9=[CFA%d]", cfa_offset);
+ }
+ if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R8) {
+ cfa_offset -= wordsize;
+ printf(" r8=[CFA%d]", cfa_offset);
+ }
+
+ if (mode == UNWIND_ARM_MODE_FRAME_D) {
+ uint32_t d_reg_bits =
+ EXTRACT_BITS(encoding, UNWIND_ARM_FRAME_D_REG_COUNT_MASK);
+ switch (d_reg_bits) {
+ case 0:
+ // vpush {d8}
+ cfa_offset -= 8;
+ printf(" d8=[CFA%d]", cfa_offset);
+ break;
+ case 1:
+ // vpush {d10}
+ // vpush {d8}
+ cfa_offset -= 8;
+ printf(" d10=[CFA%d]", cfa_offset);
+ cfa_offset -= 8;
+ printf(" d8=[CFA%d]", cfa_offset);
+ break;
+ case 2:
+ // vpush {d12}
+ // vpush {d10}
+ // vpush {d8}
+ cfa_offset -= 8;
+ printf(" d12=[CFA%d]", cfa_offset);
+ cfa_offset -= 8;
+ printf(" d10=[CFA%d]", cfa_offset);
+ cfa_offset -= 8;
+ printf(" d8=[CFA%d]", cfa_offset);
+ break;
+ case 3:
+ // vpush {d14}
+ // vpush {d12}
+ // vpush {d10}
+ // vpush {d8}
+ cfa_offset -= 8;
+ printf(" d14=[CFA%d]", cfa_offset);
+ cfa_offset -= 8;
+ printf(" d12=[CFA%d]", cfa_offset);
+ cfa_offset -= 8;
+ printf(" d10=[CFA%d]", cfa_offset);
+ cfa_offset -= 8;
+ printf(" d8=[CFA%d]", cfa_offset);
+ break;
+ case 4:
+ // vpush {d14}
+ // vpush {d12}
+ // sp = (sp - 24) & (-16);
+ // vst {d8, d9, d10}
+ printf(" d14, d12, d10, d9, d8");
+ break;
+ case 5:
+ // vpush {d14}
+ // sp = (sp - 40) & (-16);
+ // vst {d8, d9, d10, d11}
+ // vst {d12}
+ printf(" d14, d11, d10, d9, d8, d12");
+ break;
+ case 6:
+ // sp = (sp - 56) & (-16);
+ // vst {d8, d9, d10, d11}
+ // vst {d12, d13, d14}
+ printf(" d11, d10, d9, d8, d14, d13, d12");
+ break;
+ case 7:
+ // sp = (sp - 64) & (-16);
+ // vst {d8, d9, d10, d11}
+ // vst {d12, d13, d14, d15}
+ printf(" d11, d10, d9, d8, d15, d14, d13, d12");
+ break;
+ }
+ }
+ } break;
+
+ case UNWIND_ARM_MODE_DWARF: {
+ uint32_t dwarf_offset = encoding & UNWIND_ARM_DWARF_SECTION_OFFSET;
+ printf(
+ "DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64
+ ")",
+ dwarf_offset, dwarf_offset + baton.eh_section_file_address);
+ } break;
+
+ case 0: {
+ printf(" no unwind information");
+ } break;
+ }
+}
+
+void print_encoding(struct baton baton, uint8_t *function_start,
+ uint32_t encoding) {
+
+ if (baton.cputype == CPU_TYPE_X86_64) {
+ print_encoding_x86_64(baton, function_start, encoding);
+ } else if (baton.cputype == CPU_TYPE_I386) {
+ print_encoding_i386(baton, function_start, encoding);
+ } else if (baton.cputype == CPU_TYPE_ARM64 || baton.cputype == CPU_TYPE_ARM64_32) {
+ print_encoding_arm64(baton, function_start, encoding);
+ } else if (baton.cputype == CPU_TYPE_ARM) {
+ print_encoding_armv7(baton, function_start, encoding);
+ } else {
+ printf(" -- unsupported encoding arch -- ");
+ }
+}
+
+void print_function_encoding(struct baton baton, uint32_t idx,
+ uint32_t encoding, uint32_t entry_encoding_index,
+ uint32_t entry_func_offset) {
+
+ char *entry_encoding_index_str = "";
+ if (entry_encoding_index != (uint32_t)-1) {
+ asprintf(&entry_encoding_index_str, ", encoding #%d", entry_encoding_index);
+ } else {
+ asprintf(&entry_encoding_index_str, "");
+ }
+
+ uint64_t file_address = baton.first_level_index_entry.functionOffset +
+ entry_func_offset + baton.text_segment_vmaddr;
+
+ if (baton.cputype == CPU_TYPE_ARM)
+ file_address = file_address & ~1;
+
+ printf(
+ " func [%d] offset %d (file addr 0x%" PRIx64 ")%s, encoding is 0x%x",
+ idx, entry_func_offset, file_address, entry_encoding_index_str, encoding);
+
+ struct symbol *symbol = NULL;
+ for (int i = 0; i < baton.symbols_count; i++) {
+ if (i == baton.symbols_count - 1 &&
+ baton.symbols[i].file_address <= file_address) {
+ symbol = &(baton.symbols[i]);
+ break;
+ } else {
+ if (baton.symbols[i].file_address <= file_address &&
+ baton.symbols[i + 1].file_address > file_address) {
+ symbol = &(baton.symbols[i]);
+ break;
+ }
+ }
+ }
+
+ printf("\n ");
+ if (symbol) {
+ int offset = file_address - symbol->file_address;
+
+ // FIXME this is a poor heuristic - if we're greater than 16 bytes past the
+ // start of the function, this is the unwind info for a stripped function.
+ // In reality the compact unwind entry may not line up exactly with the
+ // function bounds.
+ if (offset >= 0) {
+ printf("name: %s", symbol->name);
+ if (offset > 0) {
+ printf(" + %d", offset);
+ }
+ }
+ printf("\n ");
+ }
+
+ print_encoding(baton, baton.mach_header_start +
+ baton.first_level_index_entry.functionOffset +
+ baton.text_section_file_offset + entry_func_offset,
+ encoding);
+
+ bool has_lsda = encoding & UNWIND_HAS_LSDA;
+
+ if (has_lsda) {
+ uint32_t func_offset =
+ entry_func_offset + baton.first_level_index_entry.functionOffset;
+
+ int lsda_entry_number = -1;
+
+ uint32_t low = 0;
+ uint32_t high = (baton.lsda_array_end - baton.lsda_array_start) /
+ sizeof(struct unwind_info_section_header_lsda_index_entry);
+
+ while (low < high) {
+ uint32_t mid = (low + high) / 2;
+
+ uint8_t *mid_lsda_entry_addr =
+ (baton.lsda_array_start +
+ (mid * sizeof(struct unwind_info_section_header_lsda_index_entry)));
+ struct unwind_info_section_header_lsda_index_entry mid_lsda_entry;
+ memcpy(&mid_lsda_entry, mid_lsda_entry_addr,
+ sizeof(struct unwind_info_section_header_lsda_index_entry));
+ if (mid_lsda_entry.functionOffset == func_offset) {
+ lsda_entry_number =
+ (mid_lsda_entry_addr - baton.lsda_array_start) /
+ sizeof(struct unwind_info_section_header_lsda_index_entry);
+ break;
+ } else if (mid_lsda_entry.functionOffset < func_offset) {
+ low = mid + 1;
+ } else {
+ high = mid;
+ }
+ }
+
+ if (lsda_entry_number != -1) {
+ printf(", LSDA entry #%d", lsda_entry_number);
+ } else {
+ printf(", LSDA entry not found");
+ }
+ }
+
+ uint32_t pers_idx = EXTRACT_BITS(encoding, UNWIND_PERSONALITY_MASK);
+ if (pers_idx != 0) {
+ pers_idx--; // Change 1-based to 0-based index
+ printf(", personality entry #%d", pers_idx);
+ }
+
+ printf("\n");
+}
+
+void print_second_level_index_regular(struct baton baton) {
+ uint8_t *page_entries =
+ baton.compact_unwind_start +
+ baton.first_level_index_entry.secondLevelPagesSectionOffset +
+ baton.regular_second_level_page_header.entryPageOffset;
+ uint32_t entries_count = baton.regular_second_level_page_header.entryCount;
+
+ uint8_t *offset = page_entries;
+
+ uint32_t idx = 0;
+ while (idx < entries_count) {
+ uint32_t func_offset = *((uint32_t *)(offset));
+ uint32_t encoding = *((uint32_t *)(offset + 4));
+
+ // UNWIND_SECOND_LEVEL_REGULAR entries have a funcOffset which includes the
+ // functionOffset from the containing index table already.
+ // UNWIND_SECOND_LEVEL_COMPRESSED
+ // entries only have the offset from the containing index table
+ // functionOffset.
+ // So strip off the containing index table functionOffset value here so they
+ // can
+ // be treated the same at the lower layers.
+
+ print_function_encoding(baton, idx, encoding, (uint32_t)-1,
+ func_offset -
+ baton.first_level_index_entry.functionOffset);
+ idx++;
+ offset += 8;
+ }
+}
+
+void print_second_level_index_compressed(struct baton baton) {
+ uint8_t *this_index =
+ baton.compact_unwind_start +
+ baton.first_level_index_entry.secondLevelPagesSectionOffset;
+ uint8_t *start_of_entries =
+ this_index + baton.compressed_second_level_page_header.entryPageOffset;
+ uint8_t *offset = start_of_entries;
+ for (uint16_t idx = 0;
+ idx < baton.compressed_second_level_page_header.entryCount; idx++) {
+ uint32_t entry = *((uint32_t *)offset);
+ offset += 4;
+ uint32_t encoding;
+
+ uint32_t entry_encoding_index =
+ UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entry);
+ uint32_t entry_func_offset =
+ UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entry);
+
+ if (entry_encoding_index < baton.unwind_header.commonEncodingsArrayCount) {
+ // encoding is in common table in section header
+ encoding =
+ *((uint32_t *)(baton.compact_unwind_start +
+ baton.unwind_header.commonEncodingsArraySectionOffset +
+ (entry_encoding_index * sizeof(uint32_t))));
+ } else {
+ // encoding is in page specific table
+ uint32_t page_encoding_index =
+ entry_encoding_index - baton.unwind_header.commonEncodingsArrayCount;
+ encoding = *((uint32_t *)(this_index +
+ baton.compressed_second_level_page_header
+ .encodingsPageOffset +
+ (page_encoding_index * sizeof(uint32_t))));
+ }
+
+ print_function_encoding(baton, idx, encoding, entry_encoding_index,
+ entry_func_offset);
+ }
+}
+
+void print_second_level_index(struct baton baton) {
+ uint8_t *index_start =
+ baton.compact_unwind_start +
+ baton.first_level_index_entry.secondLevelPagesSectionOffset;
+
+ if ((*(uint32_t *)index_start) == UNWIND_SECOND_LEVEL_REGULAR) {
+ struct unwind_info_regular_second_level_page_header header;
+ memcpy(&header, index_start,
+ sizeof(struct unwind_info_regular_second_level_page_header));
+ printf(
+ " UNWIND_SECOND_LEVEL_REGULAR #%d entryPageOffset %d, entryCount %d\n",
+ baton.current_index_table_number, header.entryPageOffset,
+ header.entryCount);
+ baton.regular_second_level_page_header = header;
+ print_second_level_index_regular(baton);
+ }
+
+ if ((*(uint32_t *)index_start) == UNWIND_SECOND_LEVEL_COMPRESSED) {
+ struct unwind_info_compressed_second_level_page_header header;
+ memcpy(&header, index_start,
+ sizeof(struct unwind_info_compressed_second_level_page_header));
+ printf(" UNWIND_SECOND_LEVEL_COMPRESSED #%d entryPageOffset %d, "
+ "entryCount %d, encodingsPageOffset %d, encodingsCount %d\n",
+ baton.current_index_table_number, header.entryPageOffset,
+ header.entryCount, header.encodingsPageOffset,
+ header.encodingsCount);
+ baton.compressed_second_level_page_header = header;
+ print_second_level_index_compressed(baton);
+ }
+}
+
+void print_index_sections(struct baton baton) {
+ uint8_t *index_section_offset =
+ baton.compact_unwind_start + baton.unwind_header.indexSectionOffset;
+ uint32_t index_count = baton.unwind_header.indexCount;
+
+ uint32_t cur_idx = 0;
+
+ uint8_t *offset = index_section_offset;
+ while (cur_idx < index_count) {
+ baton.current_index_table_number = cur_idx;
+ struct unwind_info_section_header_index_entry index_entry;
+ memcpy(&index_entry, offset,
+ sizeof(struct unwind_info_section_header_index_entry));
+ printf("index section #%d: functionOffset %d, "
+ "secondLevelPagesSectionOffset %d, lsdaIndexArraySectionOffset %d\n",
+ cur_idx, index_entry.functionOffset,
+ index_entry.secondLevelPagesSectionOffset,
+ index_entry.lsdaIndexArraySectionOffset);
+
+ // secondLevelPagesSectionOffset == 0 means this is a sentinel entry
+ if (index_entry.secondLevelPagesSectionOffset != 0) {
+ struct unwind_info_section_header_index_entry next_index_entry;
+ memcpy(&next_index_entry,
+ offset + sizeof(struct unwind_info_section_header_index_entry),
+ sizeof(struct unwind_info_section_header_index_entry));
+
+ baton.lsda_array_start =
+ baton.compact_unwind_start + index_entry.lsdaIndexArraySectionOffset;
+ baton.lsda_array_end = baton.compact_unwind_start +
+ next_index_entry.lsdaIndexArraySectionOffset;
+
+ uint8_t *lsda_entry_offset = baton.lsda_array_start;
+ uint32_t lsda_count = 0;
+ while (lsda_entry_offset < baton.lsda_array_end) {
+ struct unwind_info_section_header_lsda_index_entry lsda_entry;
+ memcpy(&lsda_entry, lsda_entry_offset,
+ sizeof(struct unwind_info_section_header_lsda_index_entry));
+ uint64_t function_file_address =
+ baton.first_level_index_entry.functionOffset +
+ lsda_entry.functionOffset + baton.text_segment_vmaddr;
+ uint64_t lsda_file_address =
+ lsda_entry.lsdaOffset + baton.text_segment_vmaddr;
+ printf(" LSDA [%d] functionOffset %d (%d) (file address 0x%" PRIx64
+ "), lsdaOffset %d (file address 0x%" PRIx64 ")\n",
+ lsda_count, lsda_entry.functionOffset,
+ lsda_entry.functionOffset - index_entry.functionOffset,
+ function_file_address, lsda_entry.lsdaOffset, lsda_file_address);
+ lsda_count++;
+ lsda_entry_offset +=
+ sizeof(struct unwind_info_section_header_lsda_index_entry);
+ }
+
+ printf("\n");
+
+ baton.first_level_index_entry = index_entry;
+ print_second_level_index(baton);
+ }
+
+ printf("\n");
+
+ cur_idx++;
+ offset += sizeof(struct unwind_info_section_header_index_entry);
+ }
+}
+
+int main(int argc, char **argv) {
+ struct stat st;
+ char *file = argv[0];
+ if (argc > 1)
+ file = argv[1];
+ int fd = open(file, O_RDONLY);
+ if (fd == -1) {
+ printf("Failed to open '%s'\n", file);
+ exit(1);
+ }
+ fstat(fd, &st);
+ uint8_t *file_mem =
+ (uint8_t *)mmap(0, st.st_size, PROT_READ, MAP_PRIVATE | MAP_FILE, fd, 0);
+ if (file_mem == MAP_FAILED) {
+ printf("Failed to mmap() '%s'\n", file);
+ }
+
+ FILE *f = fopen("a.out", "r");
+
+ struct baton baton;
+ baton.mach_header_start = file_mem;
+ baton.symbols = NULL;
+ baton.symbols_count = 0;
+ baton.function_start_addresses = NULL;
+ baton.function_start_addresses_count = 0;
+
+ scan_macho_load_commands(&baton);
+
+ if (baton.compact_unwind_start == NULL) {
+ printf("could not find __TEXT,__unwind_info section\n");
+ exit(1);
+ }
+
+ struct unwind_info_section_header header;
+ memcpy(&header, baton.compact_unwind_start,
+ sizeof(struct unwind_info_section_header));
+ printf("Header:\n");
+ printf(" version %u\n", header.version);
+ printf(" commonEncodingsArraySectionOffset is %d\n",
+ header.commonEncodingsArraySectionOffset);
+ printf(" commonEncodingsArrayCount is %d\n",
+ header.commonEncodingsArrayCount);
+ printf(" personalityArraySectionOffset is %d\n",
+ header.personalityArraySectionOffset);
+ printf(" personalityArrayCount is %d\n", header.personalityArrayCount);
+ printf(" indexSectionOffset is %d\n", header.indexSectionOffset);
+ printf(" indexCount is %d\n", header.indexCount);
+
+ uint8_t *common_encodings =
+ baton.compact_unwind_start + header.commonEncodingsArraySectionOffset;
+ uint32_t encoding_idx = 0;
+ while (encoding_idx < header.commonEncodingsArrayCount) {
+ uint32_t encoding = *((uint32_t *)common_encodings);
+ printf(" Common Encoding [%d]: 0x%x ", encoding_idx, encoding);
+ print_encoding(baton, NULL, encoding);
+ printf("\n");
+ common_encodings += sizeof(uint32_t);
+ encoding_idx++;
+ }
+
+ uint8_t *pers_arr =
+ baton.compact_unwind_start + header.personalityArraySectionOffset;
+ uint32_t pers_idx = 0;
+ while (pers_idx < header.personalityArrayCount) {
+ int32_t pers_delta = *((int32_t *)(baton.compact_unwind_start +
+ header.personalityArraySectionOffset +
+ (pers_idx * sizeof(uint32_t))));
+ printf(" Personality [%d]: personality function ptr @ offset %d (file "
+ "address 0x%" PRIx64 ")\n",
+ pers_idx, pers_delta, baton.text_segment_vmaddr + pers_delta);
+ pers_idx++;
+ pers_arr += sizeof(uint32_t);
+ }
+
+ printf("\n");
+
+ baton.unwind_header = header;
+
+ print_index_sections(baton);
+
+ return 0;
+}
diff --git a/gnu/llvm/lldb/tools/darwin-debug/CMakeLists.txt b/gnu/llvm/lldb/tools/darwin-debug/CMakeLists.txt
new file mode 100644
index 00000000000..b902788f05a
--- /dev/null
+++ b/gnu/llvm/lldb/tools/darwin-debug/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_lldb_tool(darwin-debug ADD_TO_FRAMEWORK
+ darwin-debug.cpp
+)
diff --git a/gnu/llvm/lldb/tools/darwin-debug/darwin-debug.cpp b/gnu/llvm/lldb/tools/darwin-debug/darwin-debug.cpp
new file mode 100644
index 00000000000..b184cb5a652
--- /dev/null
+++ b/gnu/llvm/lldb/tools/darwin-debug/darwin-debug.cpp
@@ -0,0 +1,334 @@
+//===-- darwin-debug.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// Darwin launch helper
+//
+// This program was written to allow programs to be launched in a new
+// Terminal.app window and have the application be stopped for debugging
+// at the program entry point.
+//
+// Although it uses posix_spawn(), it uses Darwin specific posix spawn
+// attribute flags to accomplish its task. It uses an "exec only" flag
+// which avoids forking this process, and it uses a "stop at entry"
+// flag to stop the program at the entry point.
+//
+// Since it uses darwin specific flags this code should not be compiled
+// on other systems.
+#if defined(__APPLE__)
+
+#include <crt_externs.h>
+#include <getopt.h>
+#include <limits.h>
+#include <mach/machine.h>
+#include <signal.h>
+#include <spawn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <string>
+
+#ifndef _POSIX_SPAWN_DISABLE_ASLR
+#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
+#endif
+
+#define streq(a, b) strcmp(a, b) == 0
+
+static struct option g_long_options[] = {
+ {"arch", required_argument, NULL, 'a'},
+ {"disable-aslr", no_argument, NULL, 'd'},
+ {"no-env", no_argument, NULL, 'e'},
+ {"help", no_argument, NULL, 'h'},
+ {"setsid", no_argument, NULL, 's'},
+ {"unix-socket", required_argument, NULL, 'u'},
+ {"working-dir", required_argument, NULL, 'w'},
+ {"env", required_argument, NULL, 'E'},
+ {NULL, 0, NULL, 0}};
+
+static void usage() {
+ puts("NAME\n"
+ " darwin-debug -- posix spawn a process that is stopped at the entry "
+ "point\n"
+ " for debugging.\n"
+ "\n"
+ "SYNOPSIS\n"
+ " darwin-debug --unix-socket=<SOCKET> [--arch=<ARCH>] "
+ "[--working-dir=<PATH>] [--disable-aslr] [--no-env] [--setsid] [--help] "
+ "-- <PROGRAM> [<PROGRAM-ARG> <PROGRAM-ARG> ....]\n"
+ "\n"
+ "DESCRIPTION\n"
+ " darwin-debug will exec itself into a child process <PROGRAM> that "
+ "is\n"
+ " halted for debugging. It does this by using posix_spawn() along "
+ "with\n"
+ " darwin specific posix_spawn flags that allows exec only (no fork), "
+ "and\n"
+ " stop at the program entry point. Any program arguments "
+ "<PROGRAM-ARG> are\n"
+ " passed on to the exec as the arguments for the new process. The "
+ "current\n"
+ " environment will be passed to the new process unless the "
+ "\"--no-env\"\n"
+ " option is used. A unix socket must be supplied using the\n"
+ " --unix-socket=<SOCKET> option so the calling program can handshake "
+ "with\n"
+ " this process and get its process id.\n"
+ "\n"
+ "EXAMPLE\n"
+ " darwin-debug --arch=i386 -- /bin/ls -al /tmp\n");
+ exit(1);
+}
+
+static void exit_with_errno(int err, const char *prefix) {
+ if (err) {
+ fprintf(stderr, "%s%s", prefix ? prefix : "", strerror(err));
+ exit(err);
+ }
+}
+
+pid_t posix_spawn_for_debug(char *const *argv, char *const *envp,
+ const char *working_dir, cpu_type_t cpu_type,
+ int disable_aslr) {
+ pid_t pid = 0;
+
+ const char *path = argv[0];
+
+ posix_spawnattr_t attr;
+
+ exit_with_errno(::posix_spawnattr_init(&attr),
+ "::posix_spawnattr_init (&attr) error: ");
+
+ // Here we are using a darwin specific feature that allows us to exec only
+ // since we want this program to turn into the program we want to debug,
+ // and also have the new program start suspended (right at __dyld_start)
+ // so we can debug it
+ short flags = POSIX_SPAWN_START_SUSPENDED | POSIX_SPAWN_SETEXEC |
+ POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
+
+ // Disable ASLR if we were asked to
+ if (disable_aslr)
+ flags |= _POSIX_SPAWN_DISABLE_ASLR;
+
+ sigset_t no_signals;
+ sigset_t all_signals;
+ sigemptyset(&no_signals);
+ sigfillset(&all_signals);
+ ::posix_spawnattr_setsigmask(&attr, &no_signals);
+ ::posix_spawnattr_setsigdefault(&attr, &all_signals);
+
+ // Set the flags we just made into our posix spawn attributes
+ exit_with_errno(::posix_spawnattr_setflags(&attr, flags),
+ "::posix_spawnattr_setflags (&attr, flags) error: ");
+
+ // Another darwin specific thing here where we can select the architecture
+ // of the binary we want to re-exec as.
+ if (cpu_type != 0) {
+ size_t ocount = 0;
+ exit_with_errno(
+ ::posix_spawnattr_setbinpref_np(&attr, 1, &cpu_type, &ocount),
+ "posix_spawnattr_setbinpref_np () error: ");
+ }
+
+ // I wish there was a posix_spawn flag to change the working directory of
+ // the inferior process we will spawn, but there currently isn't. If there
+ // ever is a better way to do this, we should use it. I would rather not
+ // manually fork, chdir in the child process, and then posix_spawn with exec
+ // as the whole reason for doing posix_spawn is to not hose anything up
+ // after the fork and prior to the exec...
+ if (working_dir)
+ ::chdir(working_dir);
+
+ exit_with_errno(::posix_spawnp(&pid, path, NULL, &attr, (char *const *)argv,
+ (char *const *)envp),
+ "posix_spawn() error: ");
+
+ // This code will only be reached if the posix_spawn exec failed...
+ ::posix_spawnattr_destroy(&attr);
+
+ return pid;
+}
+
+int main(int argc, char *const *argv, char *const *envp, const char **apple) {
+#if defined(DEBUG_LLDB_LAUNCHER)
+ const char *program_name = strrchr(apple[0], '/');
+
+ if (program_name)
+ program_name++; // Skip the last slash..
+ else
+ program_name = apple[0];
+
+ printf("%s called with:\n", program_name);
+ for (int i = 0; i < argc; ++i)
+ printf("argv[%u] = '%s'\n", i, argv[i]);
+#endif
+
+ cpu_type_t cpu_type = 0;
+ bool show_usage = false;
+ int ch;
+ int disable_aslr = 0; // By default we disable ASLR
+ bool pass_env = true;
+ std::string unix_socket_name;
+ std::string working_dir;
+
+#if __GLIBC__
+ optind = 0;
+#else
+ optreset = 1;
+ optind = 1;
+#endif
+
+ while ((ch = getopt_long_only(argc, argv, "a:deE:hsu:?", g_long_options,
+ NULL)) != -1) {
+ switch (ch) {
+ case 0:
+ break;
+
+ case 'a': // "-a i386" or "--arch=i386"
+ if (optarg) {
+ if (streq(optarg, "i386"))
+ cpu_type = CPU_TYPE_I386;
+ else if (streq(optarg, "x86_64"))
+ cpu_type = CPU_TYPE_X86_64;
+ else if (streq(optarg, "x86_64h"))
+ cpu_type = 0; // Don't set CPU type when we have x86_64h
+ else if (strstr(optarg, "arm") == optarg)
+ cpu_type = CPU_TYPE_ARM;
+ else {
+ ::fprintf(stderr, "error: unsupported cpu type '%s'\n", optarg);
+ ::exit(1);
+ }
+ }
+ break;
+
+ case 'd':
+ disable_aslr = 1;
+ break;
+
+ case 'e':
+ pass_env = false;
+ break;
+
+ case 'E': {
+ // Since we will exec this program into our new program, we can just set
+ // environment
+ // variables in this process and they will make it into the child process.
+ std::string name;
+ std::string value;
+ const char *equal_pos = strchr(optarg, '=');
+ if (equal_pos) {
+ name.assign(optarg, equal_pos - optarg);
+ value.assign(equal_pos + 1);
+ } else {
+ name = optarg;
+ }
+ ::setenv(name.c_str(), value.c_str(), 1);
+ } break;
+
+ case 's':
+ // Create a new session to avoid having control-C presses kill our current
+ // terminal session when this program is launched from a .command file
+ ::setsid();
+ break;
+
+ case 'u':
+ unix_socket_name.assign(optarg);
+ break;
+
+ case 'w': {
+ struct stat working_dir_stat;
+ if (stat(optarg, &working_dir_stat) == 0)
+ working_dir.assign(optarg);
+ else
+ ::fprintf(stderr, "warning: working directory doesn't exist: '%s'\n",
+ optarg);
+ } break;
+
+ case 'h':
+ case '?':
+ default:
+ show_usage = true;
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (show_usage || argc <= 0 || unix_socket_name.empty())
+ usage();
+
+#if defined(DEBUG_LLDB_LAUNCHER)
+ printf("\n%s post options:\n", program_name);
+ for (int i = 0; i < argc; ++i)
+ printf("argv[%u] = '%s'\n", i, argv[i]);
+#endif
+
+ // Open the socket that was passed in as an option
+ struct sockaddr_un saddr_un;
+ int s = ::socket(AF_UNIX, SOCK_STREAM, 0);
+ if (s < 0) {
+ perror("error: socket (AF_UNIX, SOCK_STREAM, 0)");
+ exit(1);
+ }
+
+ saddr_un.sun_family = AF_UNIX;
+ ::strncpy(saddr_un.sun_path, unix_socket_name.c_str(),
+ sizeof(saddr_un.sun_path) - 1);
+ saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0';
+ saddr_un.sun_len = SUN_LEN(&saddr_un);
+
+ if (::connect(s, (struct sockaddr *)&saddr_un, SUN_LEN(&saddr_un)) < 0) {
+ perror("error: connect (socket, &saddr_un, saddr_un_len)");
+ exit(1);
+ }
+
+ // We were able to connect to the socket, now write our PID so whomever
+ // launched us will know this process's ID
+ char pid_str[64];
+ const int pid_str_len =
+ ::snprintf(pid_str, sizeof(pid_str), "%i", ::getpid());
+ const int bytes_sent = ::send(s, pid_str, pid_str_len, 0);
+
+ if (pid_str_len != bytes_sent) {
+ perror("error: send (s, pid_str, pid_str_len, 0)");
+ exit(1);
+ }
+
+ // We are done with the socket
+ close(s);
+
+ system("clear");
+ printf("Launching: '%s'\n", argv[0]);
+ if (working_dir.empty()) {
+ char cwd[PATH_MAX];
+ const char *cwd_ptr = getcwd(cwd, sizeof(cwd));
+ printf("Working directory: '%s'\n", cwd_ptr);
+ } else {
+ printf("Working directory: '%s'\n", working_dir.c_str());
+ }
+ printf("%i arguments:\n", argc);
+
+ for (int i = 0; i < argc; ++i)
+ printf("argv[%u] = '%s'\n", i, argv[i]);
+
+ // Now we posix spawn to exec this process into the inferior that we want
+ // to debug.
+ posix_spawn_for_debug(
+ argv,
+ pass_env ? *_NSGetEnviron() : NULL, // Pass current environment as we may
+ // have modified it if "--env" options
+ // was used, do NOT pass "envp" here
+ working_dir.empty() ? NULL : working_dir.c_str(), cpu_type, disable_aslr);
+
+ return 0;
+}
+
+#endif // #if defined (__APPLE__)
diff --git a/gnu/llvm/lldb/tools/darwin-threads/examine-threads.c b/gnu/llvm/lldb/tools/darwin-threads/examine-threads.c
new file mode 100644
index 00000000000..51f35b6b0cc
--- /dev/null
+++ b/gnu/llvm/lldb/tools/darwin-threads/examine-threads.c
@@ -0,0 +1,507 @@
+#include <ctype.h>
+#include <dispatch/dispatch.h>
+#include <errno.h>
+#include <libproc.h>
+#include <mach/mach.h>
+#include <mach/task_info.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/sysctl.h>
+#include <time.h>
+
+// from System.framework/Versions/B/PrivateHeaders/sys/codesign.h
+#define CS_OPS_STATUS 0 /* return status */
+#define CS_RESTRICT 0x0000800 /* tell dyld to treat restricted */
+int csops(pid_t pid, unsigned int ops, void *useraddr, size_t usersize);
+
+/* Step through the process table, find a matching process name, return
+ the pid of that matched process.
+ If there are multiple processes with that name, issue a warning on stdout
+ and return the highest numbered process.
+ The proc_pidpath() call is used which gets the full process name including
+ directories to the executable and the full (longer than 16 character)
+ executable name. */
+
+pid_t get_pid_for_process_name(const char *procname) {
+ int process_count = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0) / sizeof(pid_t);
+ if (process_count < 1) {
+ printf("Only found %d processes running!\n", process_count);
+ exit(1);
+ }
+
+ // Allocate a few extra slots in case new processes are spawned
+ int all_pids_size = sizeof(pid_t) * (process_count + 3);
+ pid_t *all_pids = (pid_t *)malloc(all_pids_size);
+
+ // re-set process_count in case the number of processes changed (got smaller;
+ // we won't do bigger)
+ process_count =
+ proc_listpids(PROC_ALL_PIDS, 0, all_pids, all_pids_size) / sizeof(pid_t);
+
+ int i;
+ pid_t highest_pid = 0;
+ int match_count = 0;
+ for (i = 1; i < process_count; i++) {
+ char pidpath[PATH_MAX];
+ int pidpath_len = proc_pidpath(all_pids[i], pidpath, sizeof(pidpath));
+ if (pidpath_len == 0)
+ continue;
+ char *j = strrchr(pidpath, '/');
+ if ((j == NULL && strcmp(procname, pidpath) == 0) ||
+ (j != NULL && strcmp(j + 1, procname) == 0)) {
+ match_count++;
+ if (all_pids[i] > highest_pid)
+ highest_pid = all_pids[i];
+ }
+ }
+ free(all_pids);
+
+ if (match_count == 0) {
+ printf("Did not find process '%s'.\n", procname);
+ exit(1);
+ }
+ if (match_count > 1) {
+ printf("Warning: More than one process '%s'!\n", procname);
+ printf(" defaulting to the highest-pid one, %d\n", highest_pid);
+ }
+ return highest_pid;
+}
+
+/* Given a pid, get the full executable name (including directory
+ paths and the longer-than-16-chars executable name) and return
+ the basename of that (i.e. do not include the directory components).
+ This function mallocs the memory for the string it returns;
+ the caller must free this memory. */
+
+const char *get_process_name_for_pid(pid_t pid) {
+ char tmp_name[PATH_MAX];
+ if (proc_pidpath(pid, tmp_name, sizeof(tmp_name)) == 0) {
+ printf("Could not find process with pid of %d\n", (int)pid);
+ exit(1);
+ }
+ if (strrchr(tmp_name, '/'))
+ return strdup(strrchr(tmp_name, '/') + 1);
+ else
+ return strdup(tmp_name);
+}
+
+/* Get a struct kinfo_proc structure for a given pid.
+ Process name is required for error printing.
+ Gives you the current state of the process and whether it is being debugged
+ by anyone.
+ memory is malloc()'ed for the returned struct kinfo_proc
+ and must be freed by the caller. */
+
+struct kinfo_proc *get_kinfo_proc_for_pid(pid_t pid, const char *process_name) {
+ struct kinfo_proc *kinfo =
+ (struct kinfo_proc *)malloc(sizeof(struct kinfo_proc));
+ int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
+ size_t len = sizeof(struct kinfo_proc);
+ if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), kinfo, &len, NULL, 0) != 0) {
+ free((void *)kinfo);
+ printf("Could not get kinfo_proc for pid %d\n", (int)pid);
+ exit(1);
+ }
+ return kinfo;
+}
+
+/* Get the basic information (thread_basic_info_t) about a given
+ thread.
+ Gives you the suspend count; thread state; user time; system time; sleep
+ time; etc.
+ The return value is a pointer to malloc'ed memory - it is the caller's
+ responsibility to free it. */
+
+thread_basic_info_t get_thread_basic_info(thread_t thread) {
+ kern_return_t kr;
+ integer_t *thinfo = (integer_t *)malloc(sizeof(integer_t) * THREAD_INFO_MAX);
+ mach_msg_type_number_t thread_info_count = THREAD_INFO_MAX;
+ kr = thread_info(thread, THREAD_BASIC_INFO, (thread_info_t)thinfo,
+ &thread_info_count);
+ if (kr != KERN_SUCCESS) {
+ printf("Error - unable to get basic thread info for a thread\n");
+ exit(1);
+ }
+ return (thread_basic_info_t)thinfo;
+}
+
+/* Get the thread identifier info (thread_identifier_info_data_t)
+ about a given thread.
+ Gives you the system-wide unique thread number; the pthread identifier number
+*/
+
+thread_identifier_info_data_t get_thread_identifier_info(thread_t thread) {
+ kern_return_t kr;
+ thread_identifier_info_data_t tident;
+ mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT;
+ kr = thread_info(thread, THREAD_IDENTIFIER_INFO, (thread_info_t)&tident,
+ &tident_count);
+ if (kr != KERN_SUCCESS) {
+ printf("Error - unable to get thread ident for a thread\n");
+ exit(1);
+ }
+ return tident;
+}
+
+/* Given a mach port # (in the examine-threads mach port namespace) for a
+ thread,
+ find the mach port # in the inferior program's port namespace.
+ Sets inferior_port if successful.
+ Returns true if successful, false if unable to find the port number. */
+
+bool inferior_namespace_mach_port_num(task_t task,
+ thread_t examine_threads_port,
+ thread_t *inferior_port) {
+ kern_return_t retval;
+ mach_port_name_array_t names;
+ mach_msg_type_number_t nameslen;
+ mach_port_type_array_t types;
+ mach_msg_type_number_t typeslen;
+
+ if (inferior_port == NULL)
+ return false;
+
+ retval = mach_port_names(task, &names, &nameslen, &types, &typeslen);
+ if (retval != KERN_SUCCESS) {
+ printf("Error - unable to get mach port names for inferior.\n");
+ return false;
+ }
+ int i = 0;
+ for (i = 0; i < nameslen; i++) {
+ mach_port_t local_name;
+ mach_msg_type_name_t local_type;
+ retval = mach_port_extract_right(task, names[i], MACH_MSG_TYPE_COPY_SEND,
+ &local_name, &local_type);
+ if (retval == KERN_SUCCESS) {
+ mach_port_deallocate(mach_task_self(), local_name);
+ if (local_name == examine_threads_port) {
+ *inferior_port = names[i];
+ vm_deallocate(mach_task_self(), (vm_address_t)names,
+ nameslen * sizeof(mach_port_t));
+ vm_deallocate(mach_task_self(), (vm_address_t)types,
+ typeslen * sizeof(mach_port_t));
+ return true;
+ }
+ }
+ }
+ vm_deallocate(mach_task_self(), (vm_address_t)names,
+ nameslen * sizeof(mach_port_t));
+ vm_deallocate(mach_task_self(), (vm_address_t)types,
+ typeslen * sizeof(mach_port_t));
+ return false;
+}
+
+/* Get the current pc value for a given thread. */
+
+uint64_t get_current_pc(thread_t thread, int *wordsize) {
+ kern_return_t kr;
+
+#if defined(__x86_64__) || defined(__i386__)
+ x86_thread_state_t gp_regs;
+ mach_msg_type_number_t gp_count = x86_THREAD_STATE_COUNT;
+ kr = thread_get_state(thread, x86_THREAD_STATE, (thread_state_t)&gp_regs,
+ &gp_count);
+ if (kr != KERN_SUCCESS) {
+ printf("Error - unable to get registers for a thread\n");
+ exit(1);
+ }
+
+ if (gp_regs.tsh.flavor == x86_THREAD_STATE64) {
+ *wordsize = 8;
+ return gp_regs.uts.ts64.__rip;
+ } else {
+ *wordsize = 4;
+ return gp_regs.uts.ts32.__eip;
+ }
+#endif
+
+#if defined(__arm__)
+ arm_thread_state_t gp_regs;
+ mach_msg_type_number_t gp_count = ARM_THREAD_STATE_COUNT;
+ kr = thread_get_state(thread, ARM_THREAD_STATE, (thread_state_t)&gp_regs,
+ &gp_count);
+ if (kr != KERN_SUCCESS) {
+ printf("Error - unable to get registers for a thread\n");
+ exit(1);
+ }
+ *wordsize = 4;
+ return gp_regs.__pc;
+#endif
+
+#if defined(__arm64__)
+ arm_thread_state64_t gp_regs;
+ mach_msg_type_number_t gp_count = ARM_THREAD_STATE64_COUNT;
+ kr = thread_get_state(thread, ARM_THREAD_STATE64, (thread_state_t)&gp_regs,
+ &gp_count);
+ if (kr != KERN_SUCCESS) {
+ printf("Error - unable to get registers for a thread\n");
+ exit(1);
+ }
+ *wordsize = 8;
+ return gp_regs.__pc;
+#endif
+}
+
+/* Get the proc_threadinfo for a given thread.
+ Gives you the thread name, if set; current and max priorities.
+ Returns 1 if successful
+ Returns 0 if proc_pidinfo() failed
+*/
+
+int get_proc_threadinfo(pid_t pid, uint64_t thread_handle,
+ struct proc_threadinfo *pth) {
+ pth->pth_name[0] = '\0';
+ int ret = proc_pidinfo(pid, PROC_PIDTHREADINFO, thread_handle, pth,
+ sizeof(struct proc_threadinfo));
+ if (ret != 0)
+ return 1;
+ else
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ kern_return_t kr;
+ task_t task;
+ pid_t pid = 0;
+ char *procname = NULL;
+ int arg_is_procname = 0;
+ int do_loop = 0;
+ int verbose = 0;
+ int resume_when_done = 0;
+ mach_port_t mytask = mach_task_self();
+
+ if (argc != 2 && argc != 3 && argc != 4 && argc != 5) {
+ printf("Usage: tdump [-l] [-v] [-r] pid/procname\n");
+ exit(1);
+ }
+
+ if (argc == 3 || argc == 4) {
+ int i = 1;
+ while (i < argc - 1) {
+ if (strcmp(argv[i], "-l") == 0)
+ do_loop = 1;
+ if (strcmp(argv[i], "-v") == 0)
+ verbose = 1;
+ if (strcmp(argv[i], "-r") == 0)
+ resume_when_done++;
+ i++;
+ }
+ }
+
+ char *c = argv[argc - 1];
+ if (*c == '\0') {
+ printf("Usage: tdump [-l] [-v] pid/procname\n");
+ exit(1);
+ }
+ while (*c != '\0') {
+ if (!isdigit(*c)) {
+ arg_is_procname = 1;
+ procname = argv[argc - 1];
+ break;
+ }
+ c++;
+ }
+
+ if (arg_is_procname && procname) {
+ pid = get_pid_for_process_name(procname);
+ } else {
+ errno = 0;
+ pid = (pid_t)strtol(argv[argc - 1], NULL, 10);
+ if (pid == 0 && errno == EINVAL) {
+ printf("Usage: tdump [-l] [-v] pid/procname\n");
+ exit(1);
+ }
+ }
+
+ const char *process_name = get_process_name_for_pid(pid);
+
+ // At this point "pid" is the process id and "process_name" is the process
+ // name
+ // Now we have to get the process list from the kernel (which only has the
+ // truncated
+ // 16 char names)
+
+ struct kinfo_proc *kinfo = get_kinfo_proc_for_pid(pid, process_name);
+
+ printf("pid %d (%s) is currently ", pid, process_name);
+ switch (kinfo->kp_proc.p_stat) {
+ case SIDL:
+ printf("being created by fork");
+ break;
+ case SRUN:
+ printf("runnable");
+ break;
+ case SSLEEP:
+ printf("sleeping on an address");
+ break;
+ case SSTOP:
+ printf("suspended");
+ break;
+ case SZOMB:
+ printf("zombie state - awaiting collection by parent");
+ break;
+ default:
+ printf("unknown");
+ }
+ if (kinfo->kp_proc.p_flag & P_TRACED)
+ printf(" and is being debugged.");
+ free((void *)kinfo);
+
+ printf("\n");
+
+ int csops_flags = 0;
+ if (csops(pid, CS_OPS_STATUS, &csops_flags, sizeof(csops_flags)) != -1 &&
+ (csops_flags & CS_RESTRICT)) {
+ printf("pid %d (%s) is restricted so nothing can attach to it.\n", pid,
+ process_name);
+ }
+
+ kr = task_for_pid(mach_task_self(), pid, &task);
+ if (kr != KERN_SUCCESS) {
+ printf("Error - unable to task_for_pid()\n");
+ exit(1);
+ }
+
+ struct task_basic_info info;
+ unsigned int info_count = TASK_BASIC_INFO_COUNT;
+
+ kr = task_info(task, TASK_BASIC_INFO, (task_info_t)&info, &info_count);
+ if (kr != KERN_SUCCESS) {
+ printf("Error - unable to call task_info.\n");
+ exit(1);
+ }
+ printf("Task suspend count: %d.\n", info.suspend_count);
+
+ struct timespec *rqtp = (struct timespec *)malloc(sizeof(struct timespec));
+ rqtp->tv_sec = 0;
+ rqtp->tv_nsec = 150000000;
+
+ int loop_cnt = 1;
+ do {
+ int i;
+ if (do_loop)
+ printf("Iteration %d:\n", loop_cnt++);
+ thread_array_t thread_list;
+ mach_msg_type_number_t thread_count;
+
+ kr = task_threads(task, &thread_list, &thread_count);
+ if (kr != KERN_SUCCESS) {
+ printf("Error - unable to get thread list\n");
+ exit(1);
+ }
+ printf("pid %d has %d threads\n", pid, thread_count);
+ if (verbose)
+ printf("\n");
+
+ for (i = 0; i < thread_count; i++) {
+ thread_basic_info_t basic_info = get_thread_basic_info(thread_list[i]);
+
+ thread_identifier_info_data_t identifier_info =
+ get_thread_identifier_info(thread_list[i]);
+
+ int wordsize;
+ uint64_t pc = get_current_pc(thread_list[i], &wordsize);
+
+ printf("thread #%d, system-wide-unique-tid 0x%llx, suspend count is %d, ",
+ i, identifier_info.thread_id, basic_info->suspend_count);
+ if (wordsize == 8)
+ printf("pc 0x%016llx, ", pc);
+ else
+ printf("pc 0x%08llx, ", pc);
+ printf("run state is ");
+ switch (basic_info->run_state) {
+ case TH_STATE_RUNNING:
+ puts("running");
+ break;
+ case TH_STATE_STOPPED:
+ puts("stopped");
+ break;
+ case TH_STATE_WAITING:
+ puts("waiting");
+ break;
+ case TH_STATE_UNINTERRUPTIBLE:
+ puts("uninterruptible");
+ break;
+ case TH_STATE_HALTED:
+ puts("halted");
+ break;
+ default:
+ puts("");
+ }
+
+ printf(" pthread handle id 0x%llx (not the same value as "
+ "pthread_self() returns)\n",
+ (uint64_t)identifier_info.thread_handle);
+
+ struct proc_threadinfo pth;
+ int proc_threadinfo_succeeded =
+ get_proc_threadinfo(pid, identifier_info.thread_handle, &pth);
+
+ if (proc_threadinfo_succeeded && pth.pth_name[0] != '\0')
+ printf(" thread name '%s'\n", pth.pth_name);
+
+ printf(" libdispatch qaddr 0x%llx (not the same as the "
+ "dispatch_queue_t token)\n",
+ (uint64_t)identifier_info.dispatch_qaddr);
+
+ if (verbose) {
+ printf(
+ " (examine-threads port namespace) mach port # 0x%4.4x\n",
+ (int)thread_list[i]);
+ thread_t mach_port_inferior_namespace;
+ if (inferior_namespace_mach_port_num(task, thread_list[i],
+ &mach_port_inferior_namespace))
+ printf(" (inferior port namepsace) mach port # 0x%4.4x\n",
+ (int)mach_port_inferior_namespace);
+ printf(" user %d.%06ds, system %d.%06ds",
+ basic_info->user_time.seconds,
+ basic_info->user_time.microseconds,
+ basic_info->system_time.seconds,
+ basic_info->system_time.microseconds);
+ if (basic_info->cpu_usage > 0) {
+ float cpu_percentage = basic_info->cpu_usage / 10.0;
+ printf(", using %.1f%% cpu currently", cpu_percentage);
+ }
+ if (basic_info->sleep_time > 0)
+ printf(", this thread has slept for %d seconds",
+ basic_info->sleep_time);
+
+ printf("\n ");
+ printf("scheduling policy %d", basic_info->policy);
+
+ if (basic_info->flags != 0) {
+ printf(", flags %d", basic_info->flags);
+ if ((basic_info->flags | TH_FLAGS_SWAPPED) == TH_FLAGS_SWAPPED)
+ printf(" (thread is swapped out)");
+ if ((basic_info->flags | TH_FLAGS_IDLE) == TH_FLAGS_IDLE)
+ printf(" (thread is idle)");
+ }
+ if (proc_threadinfo_succeeded)
+ printf(", current pri %d, max pri %d", pth.pth_curpri,
+ pth.pth_maxpriority);
+
+ printf("\n\n");
+ }
+
+ free((void *)basic_info);
+ }
+ if (do_loop)
+ printf("\n");
+ vm_deallocate(mytask, (vm_address_t)thread_list,
+ thread_count * sizeof(thread_act_t));
+ nanosleep(rqtp, NULL);
+ } while (do_loop);
+
+ while (resume_when_done > 0) {
+ kern_return_t err = task_resume(task);
+ if (err != KERN_SUCCESS)
+ printf("Error resuming task: %d.", err);
+ resume_when_done--;
+ }
+
+ vm_deallocate(mytask, (vm_address_t)task, sizeof(task_t));
+ free((void *)process_name);
+
+ return 0;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/CMakeLists.txt b/gnu/llvm/lldb/tools/debugserver/CMakeLists.txt
new file mode 100644
index 00000000000..1dc32434ba4
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.4.3)
+
+project(Debugserver LANGUAGES C CXX ASM-ATT)
+
+if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
+ set(CMAKE_MODULE_PATH
+ ${CMAKE_MODULE_PATH}
+ "${CMAKE_SOURCE_DIR}/../../cmake"
+ "${CMAKE_SOURCE_DIR}/../../cmake/modules"
+ )
+
+ include(LLDBStandalone)
+ include(debugserverConfig)
+ include(AddLLDB)
+
+ set(LLDB_SOURCE_DIR "${CMAKE_SOURCE_DIR}/../../")
+ include_directories(${LLDB_SOURCE_DIR}/include)
+endif()
+
+add_subdirectory(source)
diff --git a/gnu/llvm/lldb/tools/debugserver/debugnub-exports b/gnu/llvm/lldb/tools/debugserver/debugnub-exports
new file mode 100644
index 00000000000..662bf9308a6
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/debugnub-exports
@@ -0,0 +1,2 @@
+_DNB*
+__DNB*
diff --git a/gnu/llvm/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj b/gnu/llvm/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj
new file mode 100644
index 00000000000..f4267b7633a
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj
@@ -0,0 +1,1901 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 23562ED61D342A5A00AB2BD4 /* ActivityStore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23562ED51D342A5A00AB2BD4 /* ActivityStore.cpp */; };
+ 23562ED71D342A5A00AB2BD4 /* ActivityStore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23562ED51D342A5A00AB2BD4 /* ActivityStore.cpp */; };
+ 26CE05C5115C36590022F371 /* CFBundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2695DD910D3EBFF6007E4CA2 /* CFBundle.cpp */; };
+ 456F67641AD46CE9002850C2 /* CFBundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2695DD910D3EBFF6007E4CA2 /* CFBundle.cpp */; };
+ 26CE05C3115C36580022F371 /* CFString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2695DD9B0D3EC160007E4CA2 /* CFString.cpp */; };
+ 456F67621AD46CE9002850C2 /* CFString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2695DD9B0D3EC160007E4CA2 /* CFString.cpp */; };
+ 26CE05CF115C36F70022F371 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26ACA3340D3E956300A2120B /* CoreFoundation.framework */; settings = {ATTRIBUTES = (Required, ); }; };
+ 456F676B1AD46CE9002850C2 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26ACA3340D3E956300A2120B /* CoreFoundation.framework */; settings = {ATTRIBUTES = (Required, ); }; };
+ 26CE05B7115C363B0022F371 /* DNB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637D60C71334A0024798E /* DNB.cpp */; };
+ 456F67551AD46CE9002850C2 /* DNB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637D60C71334A0024798E /* DNB.cpp */; };
+ 264D5D581293835600ED4C01 /* DNBArch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 264D5D571293835600ED4C01 /* DNBArch.cpp */; };
+ 456F67671AD46CE9002850C2 /* DNBArch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 264D5D571293835600ED4C01 /* DNBArch.cpp */; };
+ 26CE05C1115C36510022F371 /* DNBArchImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2675D4220CCEB705000F49AF /* DNBArchImpl.cpp */; };
+ 26CE05C2115C36550022F371 /* DNBArchImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637FB0C71334A0024798E /* DNBArchImpl.cpp */; };
+ 456F67601AD46CE9002850C2 /* DNBArchImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2675D4220CCEB705000F49AF /* DNBArchImpl.cpp */; };
+ 456F67611AD46CE9002850C2 /* DNBArchImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637FB0C71334A0024798E /* DNBArchImpl.cpp */; };
+ 266B5ED11460A68200E43F0A /* DNBArchImplARM64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266B5ECF1460A68200E43F0A /* DNBArchImplARM64.cpp */; };
+ 456F67691AD46CE9002850C2 /* DNBArchImplARM64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 266B5ECF1460A68200E43F0A /* DNBArchImplARM64.cpp */; };
+ 26CE05C0115C364F0022F371 /* DNBArchImplI386.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637EA0C71334A0024798E /* DNBArchImplI386.cpp */; };
+ 456F675F1AD46CE9002850C2 /* DNBArchImplI386.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637EA0C71334A0024798E /* DNBArchImplI386.cpp */; };
+ 26CE05BF115C364D0022F371 /* DNBArchImplX86_64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26CF99A21142EB7400011AAB /* DNBArchImplX86_64.cpp */; };
+ 456F675E1AD46CE9002850C2 /* DNBArchImplX86_64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26CF99A21142EB7400011AAB /* DNBArchImplX86_64.cpp */; };
+ 26CE05B8115C363C0022F371 /* DNBBreakpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637D90C71334A0024798E /* DNBBreakpoint.cpp */; };
+ 456F67571AD46CE9002850C2 /* DNBBreakpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637D90C71334A0024798E /* DNBBreakpoint.cpp */; };
+ 26CE05B9115C363D0022F371 /* DNBDataRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637DB0C71334A0024798E /* DNBDataRef.cpp */; };
+ 456F67581AD46CE9002850C2 /* DNBDataRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637DB0C71334A0024798E /* DNBDataRef.cpp */; };
+ 26CE05A7115C360D0022F371 /* DNBError.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637DE0C71334A0024798E /* DNBError.cpp */; };
+ 456F67461AD46CE9002850C2 /* DNBError.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637DE0C71334A0024798E /* DNBError.cpp */; };
+ 26CE05BA115C363E0022F371 /* DNBLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637E00C71334A0024798E /* DNBLog.cpp */; };
+ 456F67591AD46CE9002850C2 /* DNBLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637E00C71334A0024798E /* DNBLog.cpp */; };
+ 26CE05BB115C363F0022F371 /* DNBRegisterInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637E20C71334A0024798E /* DNBRegisterInfo.cpp */; };
+ 456F675A1AD46CE9002850C2 /* DNBRegisterInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637E20C71334A0024798E /* DNBRegisterInfo.cpp */; };
+ 26CE05A8115C36170022F371 /* DNBThreadResumeActions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E7331114BFFE600D1DFB3 /* DNBThreadResumeActions.cpp */; };
+ 456F67471AD46CE9002850C2 /* DNBThreadResumeActions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E7331114BFFE600D1DFB3 /* DNBThreadResumeActions.cpp */; };
+ 23AE72E41D25DECF00945BCE /* DarwinLogCollector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23AE72E21D25DECF00945BCE /* DarwinLogCollector.cpp */; };
+ 23AE72E51D25DEE100945BCE /* DarwinLogCollector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23AE72E21D25DECF00945BCE /* DarwinLogCollector.cpp */; };
+ 49D404621E39260F00570CDC /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49D404611E39260F00570CDC /* Foundation.framework */; };
+ AFA3FCA11E39984900218D5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49D404611E39260F00570CDC /* Foundation.framework */; };
+ 456F67561AD46CE9002850C2 /* Genealogy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFEC3363194A8B0B00FF05C6 /* Genealogy.cpp */; };
+ AFEC3364194A8B0B00FF05C6 /* Genealogy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFEC3363194A8B0B00FF05C6 /* Genealogy.cpp */; };
+ 23043C9D1D35DBEC00FC25CA /* JSON.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 233B4EA51D2DB54300E98261 /* JSON.cpp */; };
+ 233B4EA71D2DB54300E98261 /* JSON.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 233B4EA51D2DB54300E98261 /* JSON.cpp */; };
+ 23AC04C61D2F41A00072351D /* LogFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23AC04C41D2F41A00072351D /* LogFilter.cpp */; };
+ 23AC04C71D2F41A00072351D /* LogFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23AC04C41D2F41A00072351D /* LogFilter.cpp */; };
+ 23AC04CA1D2F42250072351D /* LogFilterChain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23AC04C81D2F42250072351D /* LogFilterChain.cpp */; };
+ 23AC04CB1D2F42250072351D /* LogFilterChain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23AC04C81D2F42250072351D /* LogFilterChain.cpp */; };
+ 2307CCCB1D4A5D630016ABC0 /* LogFilterExactMatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 237821AE1D4917D20028B7A1 /* LogFilterExactMatch.cpp */; };
+ 237821B01D4917D20028B7A1 /* LogFilterExactMatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 237821AE1D4917D20028B7A1 /* LogFilterExactMatch.cpp */; };
+ 23AC04CF1D2F58AF0072351D /* LogFilterRegex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23AC04CD1D2F58AF0072351D /* LogFilterRegex.cpp */; };
+ 23AC04D01D2F58AF0072351D /* LogFilterRegex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23AC04CD1D2F58AF0072351D /* LogFilterRegex.cpp */; };
+ 23562ED91D342B0000AB2BD4 /* LogMessage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23562ED81D342B0000AB2BD4 /* LogMessage.cpp */; };
+ 23562EDA1D342B0000AB2BD4 /* LogMessage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23562ED81D342B0000AB2BD4 /* LogMessage.cpp */; };
+ 23562ED21D3424DF00AB2BD4 /* LogMessageOsLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23562ED01D3424DF00AB2BD4 /* LogMessageOsLog.cpp */; };
+ 23562ED31D3424DF00AB2BD4 /* LogMessageOsLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23562ED01D3424DF00AB2BD4 /* LogMessageOsLog.cpp */; };
+ 26CE05B0115C36340022F371 /* MachException.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637EE0C71334A0024798E /* MachException.cpp */; };
+ 456F674E1AD46CE9002850C2 /* MachException.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637EE0C71334A0024798E /* MachException.cpp */; };
+ 26CE05B1115C36350022F371 /* MachProcess.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F00C71334A0024798E /* MachProcess.mm */; };
+ 456F674F1AD46CE9002850C2 /* MachProcess.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F00C71334A0024798E /* MachProcess.mm */; };
+ 26CE05B6115C36390022F371 /* MachTask.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26B67DE10EE9BC30006C8BC0 /* MachTask.mm */; };
+ 456F67541AD46CE9002850C2 /* MachTask.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26B67DE10EE9BC30006C8BC0 /* MachTask.mm */; };
+ 26CE05B2115C36360022F371 /* MachThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F20C71334A0024798E /* MachThread.cpp */; };
+ 456F67501AD46CE9002850C2 /* MachThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F20C71334A0024798E /* MachThread.cpp */; };
+ 26CE05B3115C36370022F371 /* MachThreadList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F40C71334A0024798E /* MachThreadList.cpp */; };
+ 456F67511AD46CE9002850C2 /* MachThreadList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F40C71334A0024798E /* MachThreadList.cpp */; };
+ 26CE05B4115C36380022F371 /* MachVMMemory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F60C71334A0024798E /* MachVMMemory.cpp */; };
+ 456F67521AD46CE9002850C2 /* MachVMMemory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F60C71334A0024798E /* MachVMMemory.cpp */; };
+ 26CE05B5115C36380022F371 /* MachVMRegion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F80C71334A0024798E /* MachVMRegion.cpp */; };
+ 456F67531AD46CE9002850C2 /* MachVMRegion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637F80C71334A0024798E /* MachVMRegion.cpp */; };
+ 23D1B0291D497E8B00FF831B /* OsLogger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23D1B0271D497E8B00FF831B /* OsLogger.cpp */; };
+ 23D1B02A1D497E8B00FF831B /* OsLogger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23D1B0271D497E8B00FF831B /* OsLogger.cpp */; };
+ 26CE05BC115C36420022F371 /* PThreadEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637FE0C71334A0024798E /* PThreadEvent.cpp */; };
+ 456F675B1AD46CE9002850C2 /* PThreadEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C637FE0C71334A0024798E /* PThreadEvent.cpp */; };
+ 26CE05BD115C36430022F371 /* PThreadMutex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2672DBEE0EEF446700E92059 /* PThreadMutex.cpp */; };
+ 456F675C1AD46CE9002850C2 /* PThreadMutex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2672DBEE0EEF446700E92059 /* PThreadMutex.cpp */; };
+ 26CE05F1115C387C0022F371 /* PseudoTerminal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF67ABFF0D34604D0022D128 /* PseudoTerminal.cpp */; };
+ 456F67651AD46CE9002850C2 /* PseudoTerminal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF67ABFF0D34604D0022D128 /* PseudoTerminal.cpp */; };
+ 26CE05AA115C36260022F371 /* RNBContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A68F7E0D104EC800665A9E /* RNBContext.cpp */; };
+ 456F67491AD46CE9002850C2 /* RNBContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A68F7E0D104EC800665A9E /* RNBContext.cpp */; };
+ 26CE05AD115C36280022F371 /* RNBRemote.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A68FD60D10574500665A9E /* RNBRemote.cpp */; };
+ 456F674C1AD46CE9002850C2 /* RNBRemote.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A68FD60D10574500665A9E /* RNBRemote.cpp */; };
+ 26CE05AB115C36270022F371 /* RNBServices.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EF8878A00D9C797C001831DA /* RNBServices.cpp */; };
+ 456F674A1AD46CE9002850C2 /* RNBServices.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EF8878A00D9C797C001831DA /* RNBServices.cpp */; };
+ 26CE05AC115C36280022F371 /* RNBSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A68FB00D1054DA00665A9E /* RNBSocket.cpp */; };
+ 456F674B1AD46CE9002850C2 /* RNBSocket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A68FB00D1054DA00665A9E /* RNBSocket.cpp */; };
+ AF588449206077BD00A0CB5A /* SocketAddress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D6631CA81E848FE9006A7B11 /* SocketAddress.cpp */; };
+ D6631CA91E848FE9006A7B11 /* SocketAddress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D6631CA81E848FE9006A7B11 /* SocketAddress.cpp */; };
+ AF48558C1D75126800D19C07 /* StdStringExtractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF48558B1D75126800D19C07 /* StdStringExtractor.cpp */; };
+ AF48558D1D75127500D19C07 /* StdStringExtractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF48558B1D75126800D19C07 /* StdStringExtractor.cpp */; };
+ 23043C9E1D35DBFA00FC25CA /* StringConvert.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 233B4EA81D2DB96A00E98261 /* StringConvert.cpp */; };
+ 233B4EA91D2DB96A00E98261 /* StringConvert.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 233B4EA81D2DB96A00E98261 /* StringConvert.cpp */; };
+ 26CE05BE115C36440022F371 /* SysSignal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C638010C71334A0024798E /* SysSignal.cpp */; };
+ 456F675D1AD46CE9002850C2 /* SysSignal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C638010C71334A0024798E /* SysSignal.cpp */; };
+ 26CE05AE115C36320022F371 /* dbgnub-mig.defs in Sources */ = {isa = PBXBuildFile; fileRef = 26C637E80C71334A0024798E /* dbgnub-mig.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; };
+ 456F674D1AD46CE9002850C2 /* dbgnub-mig.defs in Sources */ = {isa = PBXBuildFile; fileRef = 26C637E80C71334A0024798E /* dbgnub-mig.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; };
+ 26CE05A9115C36250022F371 /* debugserver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A02918114AB9240029C479 /* debugserver.cpp */; };
+ 456F67481AD46CE9002850C2 /* debugserver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A02918114AB9240029C479 /* debugserver.cpp */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 23562ED51D342A5A00AB2BD4 /* ActivityStore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ActivityStore.cpp; sourceTree = "<group>"; };
+ 23562ED41D3426DD00AB2BD4 /* ActivityStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ActivityStore.h; sourceTree = "<group>"; };
+ 23AE72E61D25DEFB00945BCE /* ActivityStreamSPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ActivityStreamSPI.h; sourceTree = "<group>"; };
+ 2695DD910D3EBFF6007E4CA2 /* CFBundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CFBundle.cpp; sourceTree = "<group>"; };
+ 2695DD920D3EBFF6007E4CA2 /* CFBundle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFBundle.h; sourceTree = "<group>"; };
+ 2695DD9B0D3EC160007E4CA2 /* CFString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CFString.cpp; sourceTree = "<group>"; };
+ 2695DD9A0D3EC160007E4CA2 /* CFString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFString.h; sourceTree = "<group>"; };
+ 26C637E70C71334A0024798E /* CFUtils.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CFUtils.h; sourceTree = "<group>"; };
+ 2307CCCC1D4A5DAE0016ABC0 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = "<group>"; };
+ 237821AD1D4917D20028B7A1 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = "<group>"; };
+ 26593A060D4931CC001C9FE3 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ChangeLog; sourceTree = "<group>"; };
+ 26ACA3340D3E956300A2120B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
+ 26C637D60C71334A0024798E /* DNB.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNB.cpp; sourceTree = "<group>"; };
+ 26C637D70C71334A0024798E /* DNB.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNB.h; sourceTree = "<group>"; };
+ 264D5D571293835600ED4C01 /* DNBArch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DNBArch.cpp; sourceTree = "<group>"; };
+ 26C637D80C71334A0024798E /* DNBArch.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBArch.h; sourceTree = "<group>"; };
+ 2675D4220CCEB705000F49AF /* DNBArchImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = DNBArchImpl.cpp; path = arm/DNBArchImpl.cpp; sourceTree = "<group>"; };
+ 26C637FB0C71334A0024798E /* DNBArchImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNBArchImpl.cpp; sourceTree = "<group>"; };
+ 2675D4230CCEB705000F49AF /* DNBArchImpl.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = DNBArchImpl.h; path = arm/DNBArchImpl.h; sourceTree = "<group>"; };
+ 26C637FC0C71334A0024798E /* DNBArchImpl.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBArchImpl.h; sourceTree = "<group>"; };
+ 266B5ECF1460A68200E43F0A /* DNBArchImplARM64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DNBArchImplARM64.cpp; sourceTree = "<group>"; };
+ 266B5ED01460A68200E43F0A /* DNBArchImplARM64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNBArchImplARM64.h; sourceTree = "<group>"; };
+ 26C637EA0C71334A0024798E /* DNBArchImplI386.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNBArchImplI386.cpp; sourceTree = "<group>"; };
+ 26C637EB0C71334A0024798E /* DNBArchImplI386.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBArchImplI386.h; sourceTree = "<group>"; };
+ 26CF99A21142EB7400011AAB /* DNBArchImplX86_64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DNBArchImplX86_64.cpp; sourceTree = "<group>"; };
+ 26CF99A31142EB7400011AAB /* DNBArchImplX86_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNBArchImplX86_64.h; sourceTree = "<group>"; };
+ 26C637D90C71334A0024798E /* DNBBreakpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNBBreakpoint.cpp; sourceTree = "<group>"; };
+ 26C637DA0C71334A0024798E /* DNBBreakpoint.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBBreakpoint.h; sourceTree = "<group>"; };
+ 26C637DB0C71334A0024798E /* DNBDataRef.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNBDataRef.cpp; sourceTree = "<group>"; };
+ 26C637DC0C71334A0024798E /* DNBDataRef.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBDataRef.h; sourceTree = "<group>"; };
+ 26C637DD0C71334A0024798E /* DNBDefs.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = DNBDefs.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
+ 26C637DE0C71334A0024798E /* DNBError.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNBError.cpp; sourceTree = "<group>"; };
+ 26C637DF0C71334A0024798E /* DNBError.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBError.h; sourceTree = "<group>"; };
+ 26C637E00C71334A0024798E /* DNBLog.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNBLog.cpp; sourceTree = "<group>"; };
+ 26C637E10C71334A0024798E /* DNBLog.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBLog.h; sourceTree = "<group>"; };
+ 26C637E20C71334A0024798E /* DNBRegisterInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNBRegisterInfo.cpp; sourceTree = "<group>"; };
+ 26C637E30C71334A0024798E /* DNBRegisterInfo.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBRegisterInfo.h; sourceTree = "<group>"; };
+ 260828DE0CBAF7F400F95054 /* DNBRuntimeAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNBRuntimeAction.h; sourceTree = "<group>"; };
+ 260E7331114BFFE600D1DFB3 /* DNBThreadResumeActions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DNBThreadResumeActions.cpp; sourceTree = "<group>"; };
+ 260E7332114BFFE600D1DFB3 /* DNBThreadResumeActions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNBThreadResumeActions.h; sourceTree = "<group>"; };
+ 26A8FE1E0D11A77B00203048 /* DNBTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNBTimer.h; sourceTree = "<group>"; };
+ 23AE72E21D25DECF00945BCE /* DarwinLogCollector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DarwinLogCollector.cpp; sourceTree = "<group>"; };
+ 23AE72E31D25DECF00945BCE /* DarwinLogCollector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DarwinLogCollector.h; sourceTree = "<group>"; };
+ 23CF6F5E1D28A3760088ADC9 /* DarwinLogEvent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DarwinLogEvent.h; sourceTree = "<group>"; };
+ 23AC04CC1D2F42F10072351D /* DarwinLogInterfaces.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DarwinLogInterfaces.h; sourceTree = "<group>"; };
+ 23562ECF1D34110D00AB2BD4 /* DarwinLogTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DarwinLogTypes.h; sourceTree = "<group>"; };
+ 49D404611E39260F00570CDC /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+ AFEC3363194A8B0B00FF05C6 /* Genealogy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Genealogy.cpp; sourceTree = "<group>"; };
+ AF0934BA18E12B92005A11FD /* Genealogy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Genealogy.h; sourceTree = "<group>"; };
+ AF0934BB18E12B92005A11FD /* GenealogySPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GenealogySPI.h; sourceTree = "<group>"; };
+ 233B4EA51D2DB54300E98261 /* JSON.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSON.cpp; sourceTree = "<group>"; };
+ 233B4EA61D2DB54300E98261 /* JSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSON.h; sourceTree = "<group>"; };
+ 264F679A1B2F9EB200140093 /* JSONGenerator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSONGenerator.h; sourceTree = "<group>"; };
+ 23AC04C41D2F41A00072351D /* LogFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LogFilter.cpp; sourceTree = "<group>"; };
+ 23AC04C51D2F41A00072351D /* LogFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LogFilter.h; sourceTree = "<group>"; };
+ 23AC04C81D2F42250072351D /* LogFilterChain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LogFilterChain.cpp; sourceTree = "<group>"; };
+ 23AC04C91D2F42250072351D /* LogFilterChain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LogFilterChain.h; sourceTree = "<group>"; };
+ 237821AE1D4917D20028B7A1 /* LogFilterExactMatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LogFilterExactMatch.cpp; sourceTree = "<group>"; };
+ 237821AF1D4917D20028B7A1 /* LogFilterExactMatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LogFilterExactMatch.h; sourceTree = "<group>"; };
+ 23AC04CD1D2F58AF0072351D /* LogFilterRegex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LogFilterRegex.cpp; sourceTree = "<group>"; };
+ 23AC04CE1D2F58AF0072351D /* LogFilterRegex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LogFilterRegex.h; sourceTree = "<group>"; };
+ 23562ED81D342B0000AB2BD4 /* LogMessage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LogMessage.cpp; sourceTree = "<group>"; };
+ 23AC04D11D2F60130072351D /* LogMessage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LogMessage.h; sourceTree = "<group>"; };
+ 23562ED01D3424DF00AB2BD4 /* LogMessageOsLog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LogMessageOsLog.cpp; sourceTree = "<group>"; };
+ 23562ED11D3424DF00AB2BD4 /* LogMessageOsLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LogMessageOsLog.h; sourceTree = "<group>"; };
+ 26C637EE0C71334A0024798E /* MachException.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = MachException.cpp; sourceTree = "<group>"; };
+ 26C637EF0C71334A0024798E /* MachException.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MachException.h; sourceTree = "<group>"; };
+ 26C637F10C71334A0024798E /* MachProcess.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MachProcess.h; sourceTree = "<group>"; };
+ 26C637F00C71334A0024798E /* MachProcess.mm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.objcpp; path = MachProcess.mm; sourceTree = "<group>"; };
+ 49F530111331519C008956F6 /* MachRegisterStatesI386.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MachRegisterStatesI386.h; sourceTree = "<group>"; };
+ 49F5301213316D7F008956F6 /* MachRegisterStatesX86_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MachRegisterStatesX86_64.h; sourceTree = "<group>"; };
+ 26B67DE00EE9BC30006C8BC0 /* MachTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MachTask.h; sourceTree = "<group>"; };
+ 26B67DE10EE9BC30006C8BC0 /* MachTask.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MachTask.mm; sourceTree = "<group>"; };
+ 26C637F20C71334A0024798E /* MachThread.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = MachThread.cpp; sourceTree = "<group>"; };
+ 26C637F30C71334A0024798E /* MachThread.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MachThread.h; sourceTree = "<group>"; };
+ 26C637F40C71334A0024798E /* MachThreadList.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = MachThreadList.cpp; sourceTree = "<group>"; };
+ 26C637F50C71334A0024798E /* MachThreadList.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MachThreadList.h; sourceTree = "<group>"; };
+ 26C637F60C71334A0024798E /* MachVMMemory.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = MachVMMemory.cpp; sourceTree = "<group>"; };
+ 26C637F70C71334A0024798E /* MachVMMemory.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MachVMMemory.h; sourceTree = "<group>"; };
+ 26C637F80C71334A0024798E /* MachVMRegion.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = MachVMRegion.cpp; sourceTree = "<group>"; };
+ 26C637F90C71334A0024798E /* MachVMRegion.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MachVMRegion.h; sourceTree = "<group>"; };
+ 23D1B0271D497E8B00FF831B /* OsLogger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OsLogger.cpp; sourceTree = "<group>"; };
+ 23D1B0281D497E8B00FF831B /* OsLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OsLogger.h; sourceTree = "<group>"; };
+ 26C637FD0C71334A0024798E /* PThreadCondition.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PThreadCondition.h; sourceTree = "<group>"; };
+ 26C637FE0C71334A0024798E /* PThreadEvent.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = PThreadEvent.cpp; sourceTree = "<group>"; };
+ 26C637FF0C71334A0024798E /* PThreadEvent.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PThreadEvent.h; sourceTree = "<group>"; };
+ 2672DBEE0EEF446700E92059 /* PThreadMutex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PThreadMutex.cpp; sourceTree = "<group>"; };
+ 26C638000C71334A0024798E /* PThreadMutex.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PThreadMutex.h; sourceTree = "<group>"; };
+ AF67ABFF0D34604D0022D128 /* PseudoTerminal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PseudoTerminal.cpp; sourceTree = "<group>"; };
+ AF67AC000D34604D0022D128 /* PseudoTerminal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PseudoTerminal.h; sourceTree = "<group>"; };
+ 26A68F7E0D104EC800665A9E /* RNBContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RNBContext.cpp; sourceTree = "<group>"; };
+ 26A68F7D0D104EC800665A9E /* RNBContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBContext.h; sourceTree = "<group>"; };
+ 26E6B9DA0D1329010037ECDD /* RNBDefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBDefs.h; sourceTree = "<group>"; };
+ 26A68FD60D10574500665A9E /* RNBRemote.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RNBRemote.cpp; sourceTree = "<group>"; };
+ 26A68FD50D10574500665A9E /* RNBRemote.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBRemote.h; sourceTree = "<group>"; };
+ EF8878A00D9C797C001831DA /* RNBServices.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RNBServices.cpp; sourceTree = "<group>"; };
+ EF88789F0D9C797C001831DA /* RNBServices.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBServices.h; sourceTree = "<group>"; };
+ 26A68FB00D1054DA00665A9E /* RNBSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RNBSocket.cpp; sourceTree = "<group>"; };
+ 26A68FAF0D1054DA00665A9E /* RNBSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBSocket.h; sourceTree = "<group>"; };
+ D6631CA81E848FE9006A7B11 /* SocketAddress.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SocketAddress.cpp; path = ../../source/Host/common/SocketAddress.cpp; sourceTree = "<group>"; };
+ AF48558B1D75126800D19C07 /* StdStringExtractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StdStringExtractor.cpp; sourceTree = "<group>"; };
+ 233B4EA81D2DB96A00E98261 /* StringConvert.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StringConvert.cpp; path = ../../../source/Host/common/StringConvert.cpp; sourceTree = "<group>"; };
+ 26C638010C71334A0024798E /* SysSignal.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = SysSignal.cpp; sourceTree = "<group>"; };
+ 26C638020C71334A0024798E /* SysSignal.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SysSignal.h; sourceTree = "<group>"; };
+ 26C638050C71334A0024798E /* TTYState.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = TTYState.cpp; sourceTree = "<group>"; };
+ 26C638060C71334A0024798E /* TTYState.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = TTYState.h; sourceTree = "<group>"; };
+ 26203D1C1641EFB200A662F7 /* com.apple.debugserver.applist.internal.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.debugserver.applist.internal.plist; sourceTree = "<group>"; };
+ EF88788B0D9C7558001831DA /* com.apple.debugserver.applist.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.debugserver.applist.plist; sourceTree = "<group>"; };
+ 26203D1D1641EFB200A662F7 /* com.apple.debugserver.internal.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.debugserver.internal.plist; sourceTree = "<group>"; };
+ 26A4BAED0D498B7D00A9BEAB /* com.apple.debugserver.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.debugserver.plist; sourceTree = "<group>"; };
+ 269E8DF8164B2ED200AD65F6 /* com.apple.debugserver.posix.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.debugserver.posix.plist; sourceTree = "<group>"; };
+ AF949ED620605DC2002A91F9 /* com.apple.internal.xpc.remote.debugserver.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = com.apple.internal.xpc.remote.debugserver.plist; sourceTree = "<group>"; };
+ 26C637E80C71334A0024798E /* dbgnub-mig.defs */ = {isa = PBXFileReference; explicitFileType = sourcecode.mig; fileEncoding = 30; path = "dbgnub-mig.defs"; sourceTree = "<group>"; };
+ 260FC7320E5B290400043FC9 /* debugnub-exports */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "debugnub-exports"; sourceTree = SOURCE_ROOT; };
+ 26CE0594115C31C20022F371 /* debugserver */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = debugserver; sourceTree = BUILT_PRODUCTS_DIR; };
+ 26242C390DDBD33C0054A4CC /* debugserver-entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "debugserver-entitlements.plist"; sourceTree = "<group>"; };
+ AF61C60418F75ABC00B48D9D /* debugserver-macosx-entitlements.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "debugserver-macosx-entitlements.plist"; sourceTree = "<group>"; };
+ 456F67721AD46CE9002850C2 /* debugserver-nonui */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "debugserver-nonui"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 26A02918114AB9240029C479 /* debugserver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = debugserver.cpp; sourceTree = "<group>"; };
+ 9457ECF61419864100DFE7D8 /* stack_logging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = stack_logging.h; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 26CE0592115C31C20022F371 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 49D404621E39260F00570CDC /* Foundation.framework in Frameworks */,
+ 26CE05CF115C36F70022F371 /* CoreFoundation.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 456F676A1AD46CE9002850C2 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 456F676B1AD46CE9002850C2 /* CoreFoundation.framework in Frameworks */,
+ AFA3FCA11E39984900218D5E /* Foundation.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 08FB7794FE84155DC02AAC07 /* dbgnub */ = {
+ isa = PBXGroup;
+ children = (
+ D6631CA81E848FE9006A7B11 /* SocketAddress.cpp */,
+ 26ACA3330D3E94F200A2120B /* Framework */,
+ 26C637D50C71334A0024798E /* source */,
+ 1AB674ADFE9D54B511CA2CBB /* Products */,
+ 49D404601E39260F00570CDC /* Frameworks */,
+ );
+ name = dbgnub;
+ sourceTree = "<group>";
+ };
+ 1AB674ADFE9D54B511CA2CBB /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 26CE0594115C31C20022F371 /* debugserver */,
+ 456F67721AD46CE9002850C2 /* debugserver-nonui */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 23AC04C31D2F3E9A0072351D /* DarwinLog */ = {
+ isa = PBXGroup;
+ children = (
+ 237821AD1D4917D20028B7A1 /* CMakeLists.txt */,
+ 23562ED41D3426DD00AB2BD4 /* ActivityStore.h */,
+ 23562ED51D342A5A00AB2BD4 /* ActivityStore.cpp */,
+ 23AE72E61D25DEFB00945BCE /* ActivityStreamSPI.h */,
+ 23AE72E31D25DECF00945BCE /* DarwinLogCollector.h */,
+ 23AE72E21D25DECF00945BCE /* DarwinLogCollector.cpp */,
+ 23CF6F5E1D28A3760088ADC9 /* DarwinLogEvent.h */,
+ 23AC04CC1D2F42F10072351D /* DarwinLogInterfaces.h */,
+ 23562ECF1D34110D00AB2BD4 /* DarwinLogTypes.h */,
+ 23AC04C51D2F41A00072351D /* LogFilter.h */,
+ 23AC04C41D2F41A00072351D /* LogFilter.cpp */,
+ 23AC04C91D2F42250072351D /* LogFilterChain.h */,
+ 23AC04C81D2F42250072351D /* LogFilterChain.cpp */,
+ 237821AF1D4917D20028B7A1 /* LogFilterExactMatch.h */,
+ 237821AE1D4917D20028B7A1 /* LogFilterExactMatch.cpp */,
+ 23AC04CE1D2F58AF0072351D /* LogFilterRegex.h */,
+ 23AC04CD1D2F58AF0072351D /* LogFilterRegex.cpp */,
+ 23AC04D11D2F60130072351D /* LogMessage.h */,
+ 23562ED81D342B0000AB2BD4 /* LogMessage.cpp */,
+ 23562ED11D3424DF00AB2BD4 /* LogMessageOsLog.h */,
+ 23562ED01D3424DF00AB2BD4 /* LogMessageOsLog.cpp */,
+ );
+ path = DarwinLog;
+ sourceTree = "<group>";
+ };
+ 266B5ECE1460A68200E43F0A /* arm64 */ = {
+ isa = PBXGroup;
+ children = (
+ 266B5ECF1460A68200E43F0A /* DNBArchImplARM64.cpp */,
+ 266B5ED01460A68200E43F0A /* DNBArchImplARM64.h */,
+ );
+ path = arm64;
+ sourceTree = "<group>";
+ };
+ 2675D41C0CCEB6CF000F49AF /* arm */ = {
+ isa = PBXGroup;
+ children = (
+ 2675D4220CCEB705000F49AF /* DNBArchImpl.cpp */,
+ 2675D4230CCEB705000F49AF /* DNBArchImpl.h */,
+ );
+ name = arm;
+ sourceTree = "<group>";
+ };
+ 26A028FE114AB6A60029C479 /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 26A4BAED0D498B7D00A9BEAB /* com.apple.debugserver.plist */,
+ EF88788B0D9C7558001831DA /* com.apple.debugserver.applist.plist */,
+ 269E8DF8164B2ED200AD65F6 /* com.apple.debugserver.posix.plist */,
+ 26203D1C1641EFB200A662F7 /* com.apple.debugserver.applist.internal.plist */,
+ 26203D1D1641EFB200A662F7 /* com.apple.debugserver.internal.plist */,
+ AF949ED620605DC2002A91F9 /* com.apple.internal.xpc.remote.debugserver.plist */,
+ 260FC7320E5B290400043FC9 /* debugnub-exports */,
+ 26242C390DDBD33C0054A4CC /* debugserver-entitlements.plist */,
+ AF61C60418F75ABC00B48D9D /* debugserver-macosx-entitlements.plist */,
+ );
+ name = Resources;
+ sourceTree = "<group>";
+ };
+ 26A028FF114AB6BB0029C479 /* libdebugnub */ = {
+ isa = PBXGroup;
+ children = (
+ 26C637E60C71334A0024798E /* MacOSX */,
+ 260828DE0CBAF7F400F95054 /* DNBRuntimeAction.h */,
+ 26A8FE1E0D11A77B00203048 /* DNBTimer.h */,
+ 26C637D70C71334A0024798E /* DNB.h */,
+ 26C637D60C71334A0024798E /* DNB.cpp */,
+ 26C637D80C71334A0024798E /* DNBArch.h */,
+ 264D5D571293835600ED4C01 /* DNBArch.cpp */,
+ 26C637DA0C71334A0024798E /* DNBBreakpoint.h */,
+ 26C637D90C71334A0024798E /* DNBBreakpoint.cpp */,
+ 26C637DC0C71334A0024798E /* DNBDataRef.h */,
+ 26C637DB0C71334A0024798E /* DNBDataRef.cpp */,
+ 26C637DD0C71334A0024798E /* DNBDefs.h */,
+ 26C637DF0C71334A0024798E /* DNBError.h */,
+ 26C637DE0C71334A0024798E /* DNBError.cpp */,
+ 26C637E10C71334A0024798E /* DNBLog.h */,
+ 26C637E00C71334A0024798E /* DNBLog.cpp */,
+ 26C637E30C71334A0024798E /* DNBRegisterInfo.h */,
+ 26C637E20C71334A0024798E /* DNBRegisterInfo.cpp */,
+ 260E7332114BFFE600D1DFB3 /* DNBThreadResumeActions.h */,
+ 260E7331114BFFE600D1DFB3 /* DNBThreadResumeActions.cpp */,
+ 233B4EA61D2DB54300E98261 /* JSON.h */,
+ 233B4EA51D2DB54300E98261 /* JSON.cpp */,
+ 264F679A1B2F9EB200140093 /* JSONGenerator.h */,
+ AF67AC000D34604D0022D128 /* PseudoTerminal.h */,
+ AF67ABFF0D34604D0022D128 /* PseudoTerminal.cpp */,
+ 26C637FD0C71334A0024798E /* PThreadCondition.h */,
+ 26C637FF0C71334A0024798E /* PThreadEvent.h */,
+ 26C637FE0C71334A0024798E /* PThreadEvent.cpp */,
+ 26C638000C71334A0024798E /* PThreadMutex.h */,
+ 2672DBEE0EEF446700E92059 /* PThreadMutex.cpp */,
+ 233B4EA81D2DB96A00E98261 /* StringConvert.cpp */,
+ 26C638020C71334A0024798E /* SysSignal.h */,
+ 26C638010C71334A0024798E /* SysSignal.cpp */,
+ 26C638060C71334A0024798E /* TTYState.h */,
+ 26C638050C71334A0024798E /* TTYState.cpp */,
+ );
+ name = libdebugnub;
+ sourceTree = "<group>";
+ };
+ 26ACA3330D3E94F200A2120B /* Framework */ = {
+ isa = PBXGroup;
+ children = (
+ 26ACA3340D3E956300A2120B /* CoreFoundation.framework */,
+ );
+ name = Framework;
+ sourceTree = "<group>";
+ };
+ 26C637D50C71334A0024798E /* source */ = {
+ isa = PBXGroup;
+ children = (
+ 26593A060D4931CC001C9FE3 /* ChangeLog */,
+ 26DEFD6C0D104C23008A5A07 /* debugserver */,
+ 26A028FF114AB6BB0029C479 /* libdebugnub */,
+ );
+ indentWidth = 4;
+ path = source;
+ sourceTree = "<group>";
+ tabWidth = 4;
+ usesTabs = 0;
+ };
+ 26C637E60C71334A0024798E /* MacOSX */ = {
+ isa = PBXGroup;
+ children = (
+ 23AC04C31D2F3E9A0072351D /* DarwinLog */,
+ 2695DD920D3EBFF6007E4CA2 /* CFBundle.h */,
+ 2695DD910D3EBFF6007E4CA2 /* CFBundle.cpp */,
+ 2695DD9A0D3EC160007E4CA2 /* CFString.h */,
+ 2695DD9B0D3EC160007E4CA2 /* CFString.cpp */,
+ 26C637E70C71334A0024798E /* CFUtils.h */,
+ 2307CCCC1D4A5DAE0016ABC0 /* CMakeLists.txt */,
+ 2675D41C0CCEB6CF000F49AF /* arm */,
+ 266B5ECE1460A68200E43F0A /* arm64 */,
+ 26C637E90C71334A0024798E /* i386 */,
+ 26C637FA0C71334A0024798E /* ppc */,
+ 26CF99A11142EB7400011AAB /* x86_64 */,
+ 26C637E80C71334A0024798E /* dbgnub-mig.defs */,
+ AFEC3363194A8B0B00FF05C6 /* Genealogy.cpp */,
+ AF0934BA18E12B92005A11FD /* Genealogy.h */,
+ AF0934BB18E12B92005A11FD /* GenealogySPI.h */,
+ 26C637EF0C71334A0024798E /* MachException.h */,
+ 26C637EE0C71334A0024798E /* MachException.cpp */,
+ 26C637F10C71334A0024798E /* MachProcess.h */,
+ 26C637F00C71334A0024798E /* MachProcess.mm */,
+ 26C637F30C71334A0024798E /* MachThread.h */,
+ 26C637F20C71334A0024798E /* MachThread.cpp */,
+ 26C637F50C71334A0024798E /* MachThreadList.h */,
+ 26C637F40C71334A0024798E /* MachThreadList.cpp */,
+ 26C637F70C71334A0024798E /* MachVMMemory.h */,
+ 26C637F60C71334A0024798E /* MachVMMemory.cpp */,
+ 26C637F90C71334A0024798E /* MachVMRegion.h */,
+ 26C637F80C71334A0024798E /* MachVMRegion.cpp */,
+ 26B67DE00EE9BC30006C8BC0 /* MachTask.h */,
+ 26B67DE10EE9BC30006C8BC0 /* MachTask.mm */,
+ 23D1B0281D497E8B00FF831B /* OsLogger.h */,
+ 23D1B0271D497E8B00FF831B /* OsLogger.cpp */,
+ 9457ECF61419864100DFE7D8 /* stack_logging.h */,
+ );
+ path = MacOSX;
+ sourceTree = "<group>";
+ };
+ 26C637E90C71334A0024798E /* i386 */ = {
+ isa = PBXGroup;
+ children = (
+ 26C637EA0C71334A0024798E /* DNBArchImplI386.cpp */,
+ 26C637EB0C71334A0024798E /* DNBArchImplI386.h */,
+ 49F530111331519C008956F6 /* MachRegisterStatesI386.h */,
+ );
+ path = i386;
+ sourceTree = "<group>";
+ };
+ 26C637FA0C71334A0024798E /* ppc */ = {
+ isa = PBXGroup;
+ children = (
+ 26C637FB0C71334A0024798E /* DNBArchImpl.cpp */,
+ 26C637FC0C71334A0024798E /* DNBArchImpl.h */,
+ );
+ path = ppc;
+ sourceTree = "<group>";
+ };
+ 26CF99A11142EB7400011AAB /* x86_64 */ = {
+ isa = PBXGroup;
+ children = (
+ 26CF99A21142EB7400011AAB /* DNBArchImplX86_64.cpp */,
+ 26CF99A31142EB7400011AAB /* DNBArchImplX86_64.h */,
+ 49F5301213316D7F008956F6 /* MachRegisterStatesX86_64.h */,
+ );
+ path = x86_64;
+ sourceTree = "<group>";
+ };
+ 26DEFD6C0D104C23008A5A07 /* debugserver */ = {
+ isa = PBXGroup;
+ children = (
+ 26A02918114AB9240029C479 /* debugserver.cpp */,
+ 26A028FE114AB6A60029C479 /* Resources */,
+ 26A68F7D0D104EC800665A9E /* RNBContext.h */,
+ 26A68F7E0D104EC800665A9E /* RNBContext.cpp */,
+ EF88789F0D9C797C001831DA /* RNBServices.h */,
+ EF8878A00D9C797C001831DA /* RNBServices.cpp */,
+ 26A68FAF0D1054DA00665A9E /* RNBSocket.h */,
+ 26A68FB00D1054DA00665A9E /* RNBSocket.cpp */,
+ 26A68FD50D10574500665A9E /* RNBRemote.h */,
+ 26A68FD60D10574500665A9E /* RNBRemote.cpp */,
+ 26E6B9DA0D1329010037ECDD /* RNBDefs.h */,
+ AF48558B1D75126800D19C07 /* StdStringExtractor.cpp */,
+ );
+ name = debugserver;
+ sourceTree = "<group>";
+ usesTabs = 0;
+ };
+ 49D404601E39260F00570CDC /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 49D404611E39260F00570CDC /* Foundation.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 26CE0593115C31C20022F371 /* debugserver */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 26CE05A4115C31ED0022F371 /* Build configuration list for PBXNativeTarget "debugserver" */;
+ buildPhases = (
+ 26CE0591115C31C20022F371 /* Sources */,
+ 26CE0592115C31C20022F371 /* Frameworks */,
+ 4C3326CB18B2A2F600EB5DD7 /* ShellScript */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = debugserver;
+ productName = "lldb-debugserver";
+ productReference = 26CE0594115C31C20022F371 /* debugserver */;
+ productType = "com.apple.product-type.tool";
+ };
+ 456F67431AD46CE9002850C2 /* debugserver-mini */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 456F676D1AD46CE9002850C2 /* Build configuration list for PBXNativeTarget "debugserver-mini" */;
+ buildPhases = (
+ 456F67451AD46CE9002850C2 /* Sources */,
+ 456F676A1AD46CE9002850C2 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "debugserver-mini";
+ productName = "lldb-debugserver";
+ productReference = 456F67721AD46CE9002850C2 /* debugserver-nonui */;
+ productType = "com.apple.product-type.tool";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 08FB7793FE84155DC02AAC07 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastSwiftUpdateCheck = 0700;
+ LastUpgradeCheck = 0720;
+ };
+ buildConfigurationList = 1DEB914E08733D8E0010E9CD /* Build configuration list for PBXProject "debugserver" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 1;
+ knownRegions = (
+ English,
+ Japanese,
+ French,
+ German,
+ );
+ mainGroup = 08FB7794FE84155DC02AAC07 /* dbgnub */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 26CE0593115C31C20022F371 /* debugserver */,
+ 456F67431AD46CE9002850C2 /* debugserver-mini */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 4C3326CB18B2A2F600EB5DD7 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = "/bin/sh -x";
+ shellScript = "if [ \"${CONFIGURATION}\" != BuildAndIntegration ]\nthen\n if [ -n \"${DEBUGSERVER_USE_FROM_SYSTEM}\" ]\n then\n\t\tditto \"${DEVELOPER_DIR}/../SharedFrameworks/LLDB.framework/Resources/debugserver\" \"${TARGET_BUILD_DIR}/${TARGET_NAME}\"\n elif [ \"${DEBUGSERVER_DISABLE_CODESIGN}\" == \"\" ]\n then\n codesign -f -s lldb_codesign --entitlements ${SRCROOT}/../../resources/debugserver-macosx-entitlements.plist \"${TARGET_BUILD_DIR}/${TARGET_NAME}\"\n fi\nfi\n";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 26CE0591115C31C20022F371 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ D6631CA91E848FE9006A7B11 /* SocketAddress.cpp in Sources */,
+ 26CE05A7115C360D0022F371 /* DNBError.cpp in Sources */,
+ 26CE05A8115C36170022F371 /* DNBThreadResumeActions.cpp in Sources */,
+ 26CE05A9115C36250022F371 /* debugserver.cpp in Sources */,
+ AF48558C1D75126800D19C07 /* StdStringExtractor.cpp in Sources */,
+ 26CE05AA115C36260022F371 /* RNBContext.cpp in Sources */,
+ 26CE05AB115C36270022F371 /* RNBServices.cpp in Sources */,
+ 23D1B0291D497E8B00FF831B /* OsLogger.cpp in Sources */,
+ 26CE05AC115C36280022F371 /* RNBSocket.cpp in Sources */,
+ 26CE05AD115C36280022F371 /* RNBRemote.cpp in Sources */,
+ 26CE05AE115C36320022F371 /* dbgnub-mig.defs in Sources */,
+ 26CE05B0115C36340022F371 /* MachException.cpp in Sources */,
+ 26CE05B1115C36350022F371 /* MachProcess.mm in Sources */,
+ 26CE05B2115C36360022F371 /* MachThread.cpp in Sources */,
+ 233B4EA71D2DB54300E98261 /* JSON.cpp in Sources */,
+ 26CE05B3115C36370022F371 /* MachThreadList.cpp in Sources */,
+ 26CE05B4115C36380022F371 /* MachVMMemory.cpp in Sources */,
+ 26CE05B5115C36380022F371 /* MachVMRegion.cpp in Sources */,
+ 26CE05B6115C36390022F371 /* MachTask.mm in Sources */,
+ 26CE05B7115C363B0022F371 /* DNB.cpp in Sources */,
+ AFEC3364194A8B0B00FF05C6 /* Genealogy.cpp in Sources */,
+ 23AC04CF1D2F58AF0072351D /* LogFilterRegex.cpp in Sources */,
+ 233B4EA91D2DB96A00E98261 /* StringConvert.cpp in Sources */,
+ 23562ED21D3424DF00AB2BD4 /* LogMessageOsLog.cpp in Sources */,
+ 26CE05B8115C363C0022F371 /* DNBBreakpoint.cpp in Sources */,
+ 26CE05B9115C363D0022F371 /* DNBDataRef.cpp in Sources */,
+ 23AC04CA1D2F42250072351D /* LogFilterChain.cpp in Sources */,
+ 23562ED61D342A5A00AB2BD4 /* ActivityStore.cpp in Sources */,
+ 26CE05BA115C363E0022F371 /* DNBLog.cpp in Sources */,
+ 23AC04C61D2F41A00072351D /* LogFilter.cpp in Sources */,
+ 26CE05BB115C363F0022F371 /* DNBRegisterInfo.cpp in Sources */,
+ 26CE05BC115C36420022F371 /* PThreadEvent.cpp in Sources */,
+ 26CE05BD115C36430022F371 /* PThreadMutex.cpp in Sources */,
+ 26CE05BE115C36440022F371 /* SysSignal.cpp in Sources */,
+ 23AE72E41D25DECF00945BCE /* DarwinLogCollector.cpp in Sources */,
+ 26CE05BF115C364D0022F371 /* DNBArchImplX86_64.cpp in Sources */,
+ 26CE05C0115C364F0022F371 /* DNBArchImplI386.cpp in Sources */,
+ 26CE05C1115C36510022F371 /* DNBArchImpl.cpp in Sources */,
+ 26CE05C2115C36550022F371 /* DNBArchImpl.cpp in Sources */,
+ 26CE05C5115C36590022F371 /* CFBundle.cpp in Sources */,
+ 26CE05C3115C36580022F371 /* CFString.cpp in Sources */,
+ 23562ED91D342B0000AB2BD4 /* LogMessage.cpp in Sources */,
+ 26CE05F1115C387C0022F371 /* PseudoTerminal.cpp in Sources */,
+ 264D5D581293835600ED4C01 /* DNBArch.cpp in Sources */,
+ 237821B01D4917D20028B7A1 /* LogFilterExactMatch.cpp in Sources */,
+ 266B5ED11460A68200E43F0A /* DNBArchImplARM64.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 456F67451AD46CE9002850C2 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 456F67461AD46CE9002850C2 /* DNBError.cpp in Sources */,
+ 456F67471AD46CE9002850C2 /* DNBThreadResumeActions.cpp in Sources */,
+ 456F67481AD46CE9002850C2 /* debugserver.cpp in Sources */,
+ 23043C9D1D35DBEC00FC25CA /* JSON.cpp in Sources */,
+ 456F67491AD46CE9002850C2 /* RNBContext.cpp in Sources */,
+ 23D1B02A1D497E8B00FF831B /* OsLogger.cpp in Sources */,
+ 456F674A1AD46CE9002850C2 /* RNBServices.cpp in Sources */,
+ 456F674B1AD46CE9002850C2 /* RNBSocket.cpp in Sources */,
+ 456F674C1AD46CE9002850C2 /* RNBRemote.cpp in Sources */,
+ 456F674D1AD46CE9002850C2 /* dbgnub-mig.defs in Sources */,
+ 456F674E1AD46CE9002850C2 /* MachException.cpp in Sources */,
+ 456F674F1AD46CE9002850C2 /* MachProcess.mm in Sources */,
+ 2307CCCB1D4A5D630016ABC0 /* LogFilterExactMatch.cpp in Sources */,
+ 456F67501AD46CE9002850C2 /* MachThread.cpp in Sources */,
+ 456F67511AD46CE9002850C2 /* MachThreadList.cpp in Sources */,
+ 456F67521AD46CE9002850C2 /* MachVMMemory.cpp in Sources */,
+ 456F67531AD46CE9002850C2 /* MachVMRegion.cpp in Sources */,
+ 23562ED71D342A5A00AB2BD4 /* ActivityStore.cpp in Sources */,
+ 456F67541AD46CE9002850C2 /* MachTask.mm in Sources */,
+ 456F67551AD46CE9002850C2 /* DNB.cpp in Sources */,
+ 456F67561AD46CE9002850C2 /* Genealogy.cpp in Sources */,
+ 456F67571AD46CE9002850C2 /* DNBBreakpoint.cpp in Sources */,
+ 456F67581AD46CE9002850C2 /* DNBDataRef.cpp in Sources */,
+ 456F67591AD46CE9002850C2 /* DNBLog.cpp in Sources */,
+ 23562ED31D3424DF00AB2BD4 /* LogMessageOsLog.cpp in Sources */,
+ 456F675A1AD46CE9002850C2 /* DNBRegisterInfo.cpp in Sources */,
+ 456F675B1AD46CE9002850C2 /* PThreadEvent.cpp in Sources */,
+ 456F675C1AD46CE9002850C2 /* PThreadMutex.cpp in Sources */,
+ 456F675D1AD46CE9002850C2 /* SysSignal.cpp in Sources */,
+ 23AE72E51D25DEE100945BCE /* DarwinLogCollector.cpp in Sources */,
+ 456F675E1AD46CE9002850C2 /* DNBArchImplX86_64.cpp in Sources */,
+ 23562EDA1D342B0000AB2BD4 /* LogMessage.cpp in Sources */,
+ 456F675F1AD46CE9002850C2 /* DNBArchImplI386.cpp in Sources */,
+ 456F67601AD46CE9002850C2 /* DNBArchImpl.cpp in Sources */,
+ 23AC04C71D2F41A00072351D /* LogFilter.cpp in Sources */,
+ 23043C9E1D35DBFA00FC25CA /* StringConvert.cpp in Sources */,
+ 456F67611AD46CE9002850C2 /* DNBArchImpl.cpp in Sources */,
+ AF588449206077BD00A0CB5A /* SocketAddress.cpp in Sources */,
+ 456F67621AD46CE9002850C2 /* CFString.cpp in Sources */,
+ 23AC04CB1D2F42250072351D /* LogFilterChain.cpp in Sources */,
+ AF48558D1D75127500D19C07 /* StdStringExtractor.cpp in Sources */,
+ 456F67641AD46CE9002850C2 /* CFBundle.cpp in Sources */,
+ 456F67651AD46CE9002850C2 /* PseudoTerminal.cpp in Sources */,
+ 456F67671AD46CE9002850C2 /* DNBArch.cpp in Sources */,
+ 23AC04D01D2F58AF0072351D /* LogFilterRegex.cpp in Sources */,
+ 456F67691AD46CE9002850C2 /* DNBArchImplARM64.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ 1DEB914F08733D8E0010E9CD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ "ARCHS[sdk=iphoneos*]" = arm64;
+ "ARCHS[sdk=macosx*]" = "$(ARCHS_STANDARD_64_BIT)";
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "";
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 360.99.0;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ LLDB_COMPRESSION_LDFLAGS = "-lcompression";
+ LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)";
+ LLDB_USE_OS_LOG = 0;
+ ONLY_ACTIVE_ARCH = YES;
+ OTHER_CFLAGS = "";
+ STRIP_INSTALLED_PRODUCT = NO;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_BUILDER = "$(USER)";
+ };
+ name = Debug;
+ };
+ 1DEB915008733D8E0010E9CD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ "ARCHS[sdk=iphoneos*]" = arm64;
+ "ARCHS[sdk=macosx*]" = "$(ARCHS_STANDARD_64_BIT)";
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CURRENT_PROJECT_VERSION = 360.99.0;
+ DEAD_CODE_STRIPPING = YES;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ LLDB_COMPRESSION_LDFLAGS = "-lcompression";
+ LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample";
+ LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)";
+ LLDB_USE_OS_LOG = 0;
+ ONLY_ACTIVE_ARCH = YES;
+ OTHER_CFLAGS = "";
+ STRIPFLAGS = "-x";
+ STRIP_STYLE = debugging;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_BUILDER = "$(USER)";
+ };
+ name = Release;
+ };
+ 262419A11198A93E00067686 /* BuildAndIntegration */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ "ARCHS[sdk=iphoneos*]" = arm64;
+ "ARCHS[sdk=macosx*]" = "$(ARCHS_STANDARD_64_BIT)";
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CURRENT_PROJECT_VERSION = 360.99.0;
+ DEAD_CODE_STRIPPING = YES;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ LLDB_COMPRESSION_LDFLAGS = "-lcompression";
+ LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)";
+ LLDB_USE_OS_LOG = 1;
+ OTHER_CFLAGS = "";
+ STRIPFLAGS = "-x";
+ STRIP_STYLE = debugging;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_BUILDER = "$(USER)";
+ };
+ name = BuildAndIntegration;
+ };
+ 262419A21198A93E00067686 /* BuildAndIntegration */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "-";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 360.99.0;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks;
+ "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = (
+ "$(SDKROOT)/System/Library/PrivateFrameworks",
+ "$(SDKROOT)/Developer/Library/PrivateFrameworks",
+ );
+ "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_BUILDANDINTEGRATION;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders;
+ INSTALL_PATH = /usr/bin;
+ "INSTALL_PATH[sdk=iphoneos*]" = /Developer/usr/bin/;
+ LLDB_COMPRESSION_LDFLAGS = "-lcompression";
+ LLDB_DEBUGSERVER = 1;
+ LLDB_ENERGY_CFLAGS = "";
+ "LLDB_ENERGY_CFLAGS[sdk=*internal]" = "-DLLDB_ENERGY";
+ LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample";
+ OTHER_CFLAGS = (
+ "-Wparentheses",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "-DDT_VARIANT_$(DT_VARIANT)",
+ "$(LLDB_OS_LOG_CFLAGS)",
+ );
+ "OTHER_CFLAGS[sdk=iphoneos*]" = (
+ "-Wparentheses",
+ "-DWITH_LOCKDOWN",
+ "-DWITH_FBS",
+ "-DWITH_BKS",
+ "-DOS_OBJECT_USE_OBJC=0",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "$(LLDB_OS_LOG_CFLAGS)",
+ "-isystem",
+ "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders",
+ );
+ "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+ OTHER_LDFLAGS = "";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-framework",
+ SpringBoardServices,
+ "-framework",
+ BackBoardServices,
+ "-llockdown",
+ "-framework",
+ FrontBoardServices,
+ "-framework",
+ MobileCoreServices,
+ "$(LLDB_ENERGY_LDFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ );
+ "OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
+ "$(LLDB_ENERGY_LDFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ );
+ OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
+ PRODUCT_NAME = debugserver;
+ SDKROOT = macosx;
+ SKIP_INSTALL = YES;
+ "SKIP_INSTALL[sdk=iphoneos*]" = NO;
+ STRIP_INSTALLED_PRODUCT = YES;
+ USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include";
+ ZERO_LINK = NO;
+ };
+ name = BuildAndIntegration;
+ };
+ 26CE0596115C31C30022F371 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "";
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 360.99.0;
+ FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks;
+ "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = (
+ "$(SDKROOT)/System/Library/PrivateFrameworks",
+ "$(SDKROOT)/Developer/Library/PrivateFrameworks",
+ );
+ "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ INSTALL_PATH = /usr/bin;
+ LLDB_COMPRESSION_LDFLAGS = "-lcompression";
+ LLDB_DEBUGSERVER = 1;
+ LLDB_ENERGY_CFLAGS = "";
+ "LLDB_ENERGY_CFLAGS[sdk=*internal]" = "-DLLDB_ENERGY";
+ LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample";
+ OTHER_CFLAGS = (
+ "-Wparentheses",
+ "-DDT_VARIANT_$(DT_VARIANT)",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "$(LLDB_OS_LOG_CFLAGS)",
+ );
+ "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-Wparentheses",
+ "-DWITH_LOCKDOWN",
+ "-DWITH_BKS",
+ "-DWITH_FBS",
+ "-DOS_OBJECT_USE_OBJC=0",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "$(LLDB_OS_LOG_CFLAGS)",
+ "-isystem",
+ "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders",
+ );
+ "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+ OTHER_LDFLAGS = "";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-framework",
+ SpringBoardServices,
+ "-framework",
+ BackBoardServices,
+ "-llockdown",
+ "-framework",
+ FrontBoardServices,
+ "-framework",
+ MobileCoreServices,
+ "$(LLDB_ENERGY_LDFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ );
+ "OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
+ "$(LLDB_ENERGY_LDFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ );
+ OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
+ PRODUCT_NAME = debugserver;
+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
+ "PROVISIONING_PROFILE[sdk=macosx*]" = "";
+ SDKROOT = macosx;
+ SKIP_INSTALL = YES;
+ USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include";
+ ZERO_LINK = NO;
+ };
+ name = Debug;
+ };
+ 26CE0597115C31C30022F371 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "";
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 360.99.0;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks;
+ "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = (
+ "$(SDKROOT)/System/Library/PrivateFrameworks",
+ "$(SDKROOT)/Developer/Library/PrivateFrameworks",
+ );
+ "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_RELEASE;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ INSTALL_PATH = /usr/bin;
+ LLDB_COMPRESSION_LDFLAGS = "-lcompression";
+ LLDB_DEBUGSERVER = 1;
+ LLDB_ENERGY_CFLAGS = "";
+ "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY";
+ LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample";
+ OTHER_CFLAGS = (
+ "-Wparentheses",
+ "-DDT_VARIANT_$(DT_VARIANT)",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "$(LLDB_OS_LOG_CFLAGS)",
+ );
+ "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-Wparentheses",
+ "-DWITH_LOCKDOWN",
+ "-DWITH_FBS",
+ "-DWITH_BKS",
+ "-DOS_OBJECT_USE_OBJC=0",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "$(LLDB_OS_LOG_CFLAGS)",
+ "-isystem",
+ "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders",
+ );
+ "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+ OTHER_LDFLAGS = "";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-framework",
+ SpringBoardServices,
+ "-framework",
+ BackBoardServices,
+ "-llockdown",
+ "-framework",
+ FrontBoardServices,
+ "-framework",
+ MobileCoreServices,
+ "$(LLDB_ENERGY_LDFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ );
+ "OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
+ "$(LLDB_ENERGY_LDFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ );
+ OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
+ PRODUCT_NAME = debugserver;
+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
+ "PROVISIONING_PROFILE[sdk=macosx*]" = "";
+ SDKROOT = macosx;
+ SKIP_INSTALL = YES;
+ USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include";
+ ZERO_LINK = NO;
+ };
+ name = Release;
+ };
+ 456F676E1AD46CE9002850C2 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist";
+ CODE_SIGN_IDENTITY = "";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "";
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 360.99.0;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks;
+ "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = (
+ "$(SDKROOT)/System/Library/PrivateFrameworks",
+ "$(SDKROOT)/Developer/Library/PrivateFrameworks",
+ );
+ "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ INSTALL_PATH = /usr/local/bin;
+ LLDB_COMPRESSION_LDFLAGS = "-lcompression";
+ LLDB_DEBUGSERVER = 1;
+ LLDB_ENERGY_CFLAGS = "";
+ "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY";
+ LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample";
+ ONLY_ACTIVE_ARCH = YES;
+ OTHER_CFLAGS = (
+ "-Wparentheses",
+ "-DDT_VARIANT_$(DT_VARIANT)",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "$(LLDB_OS_LOG_CFLAGS)",
+ );
+ "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-Wparentheses",
+ "-DOS_OBJECT_USE_OBJC=0",
+ "-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "$(LLDB_OS_LOG_CFLAGS)",
+ );
+ "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+ OTHER_LDFLAGS = "";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-framework",
+ Foundation,
+ "$(LLDB_ENERGY_LDFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ );
+ "OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
+ "$(LLDB_ENERGY_LDFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ );
+ OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
+ PRODUCT_NAME = "debugserver-nonui";
+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
+ "PROVISIONING_PROFILE[sdk=macosx*]" = "";
+ SDKROOT = macosx;
+ SKIP_INSTALL = YES;
+ STRIP_INSTALLED_PRODUCT = NO;
+ USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include";
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_BUILDER = "$(USER)";
+ ZERO_LINK = NO;
+ };
+ name = Debug;
+ };
+ 456F676F1AD46CE9002850C2 /* DebugClang */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "";
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 360.99.0;
+ FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks;
+ "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = (
+ "$(SDKROOT)/System/Library/PrivateFrameworks",
+ "$(SDKROOT)/Developer/Library/PrivateFrameworks",
+ );
+ "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ INSTALL_PATH = /usr/local/bin;
+ LLDB_DEBUGSERVER = 1;
+ LLDB_ENERGY_CFLAGS = "";
+ "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY";
+ LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample";
+ OTHER_CFLAGS = (
+ "$(LLDB_ENERGY_CFLAGS)",
+ );
+ "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-Wparentheses",
+ "-DOS_OBJECT_USE_OBJC=0",
+ "-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui",
+ "$(LLDB_OS_LOG_CFLAGS)",
+ );
+ "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+ OTHER_LDFLAGS = "";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-framework",
+ Foundation,
+ "$(LLDB_ENERGY_LDFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ );
+ "OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
+ "$(LLDB_ENERGY_LDFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ );
+ OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
+ PRODUCT_NAME = "debugserver-nonui";
+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
+ "PROVISIONING_PROFILE[sdk=macosx*]" = "";
+ SDKROOT = macosx;
+ SKIP_INSTALL = YES;
+ USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include";
+ ZERO_LINK = NO;
+ };
+ name = DebugClang;
+ };
+ 456F67701AD46CE9002850C2 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "";
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 360.99.0;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks;
+ "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = (
+ "$(SDKROOT)/System/Library/PrivateFrameworks",
+ "$(SDKROOT)/Developer/Library/PrivateFrameworks",
+ );
+ "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_RELEASE;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders;
+ INSTALL_PATH = /usr/local/bin;
+ LLDB_COMPRESSION_LDFLAGS = "-lcompression";
+ LLDB_DEBUGSERVER = 1;
+ LLDB_ENERGY_CFLAGS = "";
+ "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY";
+ LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample";
+ OTHER_CFLAGS = (
+ "$(LLDB_ENERGY_CFLAGS)",
+ "-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui",
+ "$(LLDB_OS_LOG_CFLAGS)",
+ );
+ "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-Wparentheses",
+ "-DOS_OBJECT_USE_OBJC=0",
+ "-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "$(LLDB_OS_LOG_CFLAGS)",
+ );
+ "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+ OTHER_LDFLAGS = "";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-framework",
+ Foundation,
+ "$(LLDB_ENERGY_LDFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ );
+ "OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
+ "$(LLDB_ENERGY_LDFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ );
+ OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
+ PRODUCT_NAME = "debugserver-nonui";
+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
+ "PROVISIONING_PROFILE[sdk=macosx*]" = "";
+ SDKROOT = macosx;
+ SKIP_INSTALL = YES;
+ USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include";
+ ZERO_LINK = NO;
+ };
+ name = Release;
+ };
+ 456F67711AD46CE9002850C2 /* BuildAndIntegration */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "-";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 360.99.0;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks;
+ "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = (
+ "$(SDKROOT)/System/Library/PrivateFrameworks",
+ "$(SDKROOT)/Developer/Library/PrivateFrameworks",
+ );
+ "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_BUILDANDINTEGRATION;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders;
+ INSTALL_PATH = /usr/local/bin;
+ "INSTALL_PATH[sdk=iphoneos*]" = /usr/local/bin;
+ LLDB_DEBUGSERVER = 1;
+ LLDB_ENERGY_CFLAGS = "";
+ "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY";
+ LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample";
+ OTHER_CFLAGS = (
+ "-Wparentheses",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui",
+ "$(LLDB_OS_LOG_CFLAGS)",
+ );
+ "OTHER_CFLAGS[sdk=iphoneos*]" = (
+ "-Wparentheses",
+ "-DOS_OBJECT_USE_OBJC=0",
+ "-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui",
+ "$(LLDB_OS_LOG_CFLAGS)",
+ );
+ OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
+ "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-framework",
+ Foundation,
+ );
+ "OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
+ "$(LLDB_ENERGY_LDFLAGS)",
+ );
+ OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
+ PRODUCT_NAME = "debugserver-nonui";
+ SDKROOT = macosx;
+ SKIP_INSTALL = YES;
+ "SKIP_INSTALL[sdk=iphoneos*]" = NO;
+ STRIP_INSTALLED_PRODUCT = YES;
+ USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include";
+ ZERO_LINK = NO;
+ };
+ name = BuildAndIntegration;
+ };
+ 4968B7A916657FAE00741ABB /* DebugClang */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ "ARCHS[sdk=iphoneos*]" = arm64;
+ "ARCHS[sdk=macosx*]" = "$(ARCHS_STANDARD_64_BIT)";
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "";
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 360.99.0;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ LLDB_COMPRESSION_LDFLAGS = "-lcompression";
+ LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)";
+ LLDB_USE_OS_LOG = 0;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ STRIP_INSTALLED_PRODUCT = NO;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_BUILDER = "$(USER)";
+ };
+ name = DebugClang;
+ };
+ 4968B7AA16657FAE00741ABB /* DebugClang */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "";
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 360.99.0;
+ FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks;
+ "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = (
+ "$(SDKROOT)/System/Library/PrivateFrameworks",
+ "$(SDKROOT)/Developer/Library/PrivateFrameworks",
+ );
+ "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ INSTALL_PATH = /usr/bin;
+ LLDB_COMPRESSION_LDFLAGS = "-lcompression";
+ LLDB_DEBUGSERVER = 1;
+ LLDB_ENERGY_CFLAGS = "";
+ "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY";
+ LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample";
+ OTHER_CFLAGS = (
+ "-Wparentheses",
+ "-DDT_VARIANT_$(DT_VARIANT)",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "$(LLDB_OS_LOG_CFLAGS)",
+ );
+ "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-Wparentheses",
+ "-DWITH_LOCKDOWN",
+ "-DWITH_FBS",
+ "-DWITH_BKS",
+ "-DOS_OBJECT_USE_OBJC=0",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "$(LLDB_OS_LOG_CFLAGS)",
+ "-isystem",
+ "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders",
+ );
+ "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-framework",
+ SpringBoardServices,
+ "-framework",
+ BackBoardServices,
+ "-llockdown",
+ "-framework",
+ FrontBoardServices,
+ "-framework",
+ MobileCoreServices,
+ "$(LLDB_ENERGY_LDFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ );
+ "OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
+ "$(LLDB_ENERGY_LDFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ );
+ OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
+ PRODUCT_NAME = debugserver;
+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
+ "PROVISIONING_PROFILE[sdk=macosx*]" = "";
+ SDKROOT = macosx;
+ SKIP_INSTALL = YES;
+ USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include";
+ ZERO_LINK = NO;
+ };
+ name = DebugClang;
+ };
+ 940AD5251B1FE3B10051E88F /* DebugPresubmission */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ "ARCHS[sdk=iphoneos*]" = arm64;
+ "ARCHS[sdk=macosx*]" = "$(ARCHS_STANDARD_64_BIT)";
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "";
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 360.99.0;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ LLDB_COMPRESSION_LDFLAGS = "-lcompression";
+ LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)";
+ LLDB_USE_OS_LOG = 0;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ STRIP_INSTALLED_PRODUCT = NO;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_BUILDER = "$(USER)";
+ };
+ name = DebugPresubmission;
+ };
+ 940AD5261B1FE3B10051E88F /* DebugPresubmission */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "";
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 360.99.0;
+ FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks;
+ "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = (
+ "$(SDKROOT)/System/Library/PrivateFrameworks",
+ "$(SDKROOT)/Developer/Library/PrivateFrameworks",
+ );
+ "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ INSTALL_PATH = /usr/bin;
+ LLDB_COMPRESSION_LDFLAGS = "-lcompression";
+ LLDB_DEBUGSERVER = 1;
+ LLDB_ENERGY_CFLAGS = "";
+ "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY";
+ LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample";
+ OTHER_CFLAGS = (
+ "-Wparentheses",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "$(LLDB_OS_LOG_CFLAGS)",
+ );
+ "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-Wparentheses",
+ "-DWITH_LOCKDOWN",
+ "-DWITH_FBS",
+ "-DWITH_BKS",
+ "-DOS_OBJECT_USE_OBJC=0",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "$(LLDB_OS_LOG_CFLAGS)",
+ "-isystem",
+ "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders",
+ );
+ "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-framework",
+ SpringBoardServices,
+ "-framework",
+ BackBoardServices,
+ "-llockdown",
+ "-framework",
+ FrontBoardServices,
+ "-framework",
+ MobileCoreServices,
+ "$(LLDB_ENERGY_LDFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ );
+ "OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
+ "$(LLDB_ENERGY_LDFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ );
+ OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
+ PRODUCT_NAME = debugserver;
+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
+ "PROVISIONING_PROFILE[sdk=macosx*]" = "";
+ SDKROOT = macosx;
+ SKIP_INSTALL = YES;
+ USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include";
+ ZERO_LINK = NO;
+ };
+ name = DebugPresubmission;
+ };
+ 940AD5271B1FE3B10051E88F /* DebugPresubmission */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "";
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 360.99.0;
+ FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks;
+ "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = (
+ "$(SDKROOT)/System/Library/PrivateFrameworks",
+ "$(SDKROOT)/Developer/Library/PrivateFrameworks",
+ );
+ "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ INSTALL_PATH = /usr/local/bin;
+ LLDB_DEBUGSERVER = 1;
+ LLDB_ENERGY_CFLAGS = "";
+ "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY";
+ LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample";
+ OTHER_CFLAGS = (
+ "-Wparentheses",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui",
+ "$(LLDB_OS_LOG_CFLAGS)",
+ );
+ "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-Wparentheses",
+ "-DOS_OBJECT_USE_OBJC=0",
+ "-DDEBUGSERVER_PROGRAM_SYMBOL=debugserver_nonui",
+ "$(LLDB_OS_LOG_CFLAGS)",
+ );
+ "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-framework",
+ Foundation,
+ );
+ "OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
+ "$(LLDB_ENERGY_LDFLAGS)",
+ );
+ OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
+ PRODUCT_NAME = "debugserver-nonui";
+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
+ "PROVISIONING_PROFILE[sdk=macosx*]" = "";
+ SDKROOT = macosx;
+ SKIP_INSTALL = YES;
+ USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include";
+ ZERO_LINK = NO;
+ };
+ name = DebugPresubmission;
+ };
+ 94BA9B361B1A7C5700035A23 /* CustomSwift-Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "lldb-debugserver-nonui";
+ };
+ name = "CustomSwift-Debug";
+ };
+ 94BA9B371B1A7C5700035A23 /* CustomSwift-Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "lldb-debugserver-nonui";
+ };
+ name = "CustomSwift-Release";
+ };
+ 94D72C871ADF10AA00A3F718 /* CustomSwift-Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ "ARCHS[sdk=iphoneos*]" = arm64;
+ "ARCHS[sdk=macosx*]" = "$(ARCHS_STANDARD_64_BIT)";
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "";
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 360.99.0;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ LLDB_COMPRESSION_LDFLAGS = "-lcompression";
+ LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)";
+ LLDB_USE_OS_LOG = 0;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ STRIP_INSTALLED_PRODUCT = NO;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_BUILDER = "$(USER)";
+ };
+ name = "CustomSwift-Debug";
+ };
+ 94D72C881ADF10AA00A3F718 /* CustomSwift-Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "";
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 360.99.0;
+ FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks;
+ "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = (
+ "$(SDKROOT)/System/Library/PrivateFrameworks",
+ "$(SDKROOT)/Developer/Library/PrivateFrameworks",
+ );
+ "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_DEBUG;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ INSTALL_PATH = /usr/bin;
+ LLDB_COMPRESSION_LDFLAGS = "-lcompression";
+ LLDB_DEBUGSERVER = 1;
+ LLDB_ENERGY_CFLAGS = "";
+ "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY";
+ LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample";
+ OTHER_CFLAGS = (
+ "-Wparentheses",
+ "-DDT_VARIANT_$(DT_VARIANT)",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "$(LLDB_OS_LOG_CFLAGS)",
+ );
+ "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-Wparentheses",
+ "-DWITH_LOCKDOWN",
+ "-DWITH_BKS",
+ "-DWITH_FBS",
+ "-DOS_OBJECT_USE_OBJC=0",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "$(LLDB_OS_LOG_CFLAGS)",
+ "-isystem",
+ "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders",
+ );
+ "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+ OTHER_LDFLAGS = "";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-framework",
+ SpringBoardServices,
+ "-framework",
+ BackBoardServices,
+ "-llockdown",
+ "-framework",
+ FrontBoardServices,
+ "-framework",
+ MobileCoreServices,
+ "$(LLDB_ENERGY_LDFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ );
+ "OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
+ "$(LLDB_ENERGY_LDFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ );
+ OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
+ PRODUCT_NAME = debugserver;
+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
+ "PROVISIONING_PROFILE[sdk=macosx*]" = "";
+ SDKROOT = macosx;
+ SKIP_INSTALL = YES;
+ USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include";
+ ZERO_LINK = NO;
+ };
+ name = "CustomSwift-Debug";
+ };
+ 94D72C891ADF10B000A3F718 /* CustomSwift-Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ "ARCHS[sdk=iphoneos*]" = arm64;
+ "ARCHS[sdk=macosx*]" = "$(ARCHS_STANDARD_64_BIT)";
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CURRENT_PROJECT_VERSION = 360.99.0;
+ DEAD_CODE_STRIPPING = YES;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ LLDB_COMPRESSION_LDFLAGS = "-lcompression";
+ LLDB_OS_LOG_CFLAGS = "-DLLDB_USE_OS_LOG=$(LLDB_USE_OS_LOG)";
+ LLDB_USE_OS_LOG = 0;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ STRIPFLAGS = "-x";
+ STRIP_STYLE = debugging;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_BUILDER = "$(USER)";
+ };
+ name = "CustomSwift-Release";
+ };
+ 94D72C8A1ADF10B000A3F718 /* CustomSwift-Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "source/debugserver-entitlements.plist";
+ "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "source/debugserver-macosx-entitlements.plist";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "";
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 360.99.0;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks;
+ "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*][arch=*]" = (
+ "$(SDKROOT)/System/Library/PrivateFrameworks",
+ "$(SDKROOT)/Developer/Library/PrivateFrameworks",
+ );
+ "FRAMEWORK_SEARCH_PATHS[sdk=macosx*][arch=*]" = "$(SDKROOT)/System/Library/PrivateFrameworks";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_PREPROCESSOR_DEFINITIONS = LLDB_DEBUGSERVER_RELEASE;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ HEADER_SEARCH_PATHS = /System/Library/Frameworks/System.framework/PrivateHeaders;
+ INSTALL_PATH = /usr/bin;
+ LLDB_COMPRESSION_LDFLAGS = "-lcompression";
+ LLDB_DEBUGSERVER = 1;
+ LLDB_ENERGY_CFLAGS = "";
+ "LLDB_ENERGY_CFLAGS[sdk=*.internal]" = "-DLLDB_ENERGY";
+ LLDB_ENERGY_LDFLAGS = "-lpmenergy -lpmsample";
+ OTHER_CFLAGS = (
+ "-Wparentheses",
+ "-DDT_VARIANT_$(DT_VARIANT)",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "$(LLDB_OS_LOG_CFLAGS)",
+ );
+ "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-Wparentheses",
+ "-DWITH_LOCKDOWN",
+ "-DWITH_FBS",
+ "-DWITH_BKS",
+ "-DOS_OBJECT_USE_OBJC=0",
+ "$(LLDB_ENERGY_CFLAGS)",
+ "$(LLDB_OS_LOG_CFLAGS)",
+ "-isystem",
+ "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders",
+ );
+ "OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-framework",
+ SpringBoardServices,
+ "-framework",
+ BackBoardServices,
+ "-llockdown",
+ "-framework",
+ FrontBoardServices,
+ "-framework",
+ MobileCoreServices,
+ "$(LLDB_ENERGY_LDFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ );
+ "OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
+ "$(LLDB_ENERGY_LDFLAGS)",
+ "$(LLDB_COMPRESSION_LDFLAGS)",
+ );
+ OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
+ PRODUCT_NAME = debugserver;
+ "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
+ "PROVISIONING_PROFILE[sdk=macosx*]" = "";
+ SDKROOT = macosx;
+ SKIP_INSTALL = YES;
+ USER_HEADER_SEARCH_PATHS = "./source ../../source $(DERIVED_SOURCES_DIR) ../../include";
+ ZERO_LINK = NO;
+ };
+ name = "CustomSwift-Release";
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 1DEB914E08733D8E0010E9CD /* Build configuration list for PBXProject "debugserver" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1DEB914F08733D8E0010E9CD /* Debug */,
+ 94D72C871ADF10AA00A3F718 /* CustomSwift-Debug */,
+ 4968B7A916657FAE00741ABB /* DebugClang */,
+ 940AD5251B1FE3B10051E88F /* DebugPresubmission */,
+ 1DEB915008733D8E0010E9CD /* Release */,
+ 94D72C891ADF10B000A3F718 /* CustomSwift-Release */,
+ 262419A11198A93E00067686 /* BuildAndIntegration */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = BuildAndIntegration;
+ };
+ 26CE05A4115C31ED0022F371 /* Build configuration list for PBXNativeTarget "debugserver" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 26CE0596115C31C30022F371 /* Debug */,
+ 94D72C881ADF10AA00A3F718 /* CustomSwift-Debug */,
+ 4968B7AA16657FAE00741ABB /* DebugClang */,
+ 940AD5261B1FE3B10051E88F /* DebugPresubmission */,
+ 26CE0597115C31C30022F371 /* Release */,
+ 94D72C8A1ADF10B000A3F718 /* CustomSwift-Release */,
+ 262419A21198A93E00067686 /* BuildAndIntegration */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = BuildAndIntegration;
+ };
+ 456F676D1AD46CE9002850C2 /* Build configuration list for PBXNativeTarget "debugserver-mini" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 456F676E1AD46CE9002850C2 /* Debug */,
+ 456F676F1AD46CE9002850C2 /* DebugClang */,
+ 940AD5271B1FE3B10051E88F /* DebugPresubmission */,
+ 456F67701AD46CE9002850C2 /* Release */,
+ 456F67711AD46CE9002850C2 /* BuildAndIntegration */,
+ 94BA9B361B1A7C5700035A23 /* CustomSwift-Debug */,
+ 94BA9B371B1A7C5700035A23 /* CustomSwift-Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = BuildAndIntegration;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/debugserver.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/gnu/llvm/lldb/tools/debugserver/debugserver.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000000..c24931480d4
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/debugserver.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:debugserver.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/gnu/llvm/lldb/tools/debugserver/debugserver.xcodeproj/xcshareddata/xcschemes/debugserver.xcscheme b/gnu/llvm/lldb/tools/debugserver/debugserver.xcodeproj/xcshareddata/xcschemes/debugserver.xcscheme
new file mode 100644
index 00000000000..2f27b5e4eff
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/debugserver.xcodeproj/xcshareddata/xcschemes/debugserver.xcscheme
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0720"
+ version = "1.8">
+ <BuildAction
+ parallelizeBuildables = "NO"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "26CE0593115C31C20022F371"
+ BuildableName = "debugserver"
+ BlueprintName = "debugserver"
+ ReferencedContainer = "container:debugserver.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <Testables>
+ </Testables>
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "26CE0593115C31C20022F371"
+ BuildableName = "debugserver"
+ BlueprintName = "debugserver"
+ ReferencedContainer = "container:debugserver.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <EnvironmentVariables>
+ <EnvironmentVariable
+ key = "UBBY"
+ value = ""
+ isEnabled = "YES">
+ </EnvironmentVariable>
+ </EnvironmentVariables>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ displayScaleIsEnabled = "NO"
+ displayScale = "1.00"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES"
+ queueDebuggingEnabled = "No">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "26CE0593115C31C20022F371"
+ BuildableName = "debugserver"
+ BlueprintName = "debugserver"
+ ReferencedContainer = "container:debugserver.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ displayScaleIsEnabled = "NO"
+ displayScale = "1.00"
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "26CE0593115C31C20022F371"
+ BuildableName = "debugserver"
+ BlueprintName = "debugserver"
+ ReferencedContainer = "container:debugserver.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ <EnvironmentVariables>
+ <EnvironmentVariable
+ key = "UBBY"
+ value = ""
+ isEnabled = "YES">
+ </EnvironmentVariable>
+ </EnvironmentVariables>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/gnu/llvm/lldb/tools/debugserver/resources/lldb-debugserver-Info.plist b/gnu/llvm/lldb/tools/debugserver/resources/lldb-debugserver-Info.plist
new file mode 100644
index 00000000000..343325c2765
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/resources/lldb-debugserver-Info.plist
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.apple.debugserver</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>debugserver</string>
+ <key>CFBundleVersion</key>
+ <string>2</string>
+ <key>SecTaskAccess</key>
+ <array>
+ <string>allowed</string>
+ <string>debug</string>
+ </array>
+</dict>
+</plist>
diff --git a/gnu/llvm/lldb/tools/debugserver/scripts/diagnose-termination.d b/gnu/llvm/lldb/tools/debugserver/scripts/diagnose-termination.d
new file mode 100644
index 00000000000..d216c975003
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/scripts/diagnose-termination.d
@@ -0,0 +1,18 @@
+fbt::exception_deliver:entry
+{
+ printf("pid %d got an exception of type %d\n", pid, arg1);
+ stack();
+ ustack();
+}
+
+syscall::kill:entry
+{
+ printf("pid %d called kill(%d, %d)\n", pid, arg0, arg1);
+ ustack();
+}
+
+syscall::__pthread_kill:entry
+{
+ printf("pid %d called pthread_kill(%p, %d)\n", pid, arg0, arg1);
+ ustack();
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/ARM_DWARF_Registers.h b/gnu/llvm/lldb/tools/debugserver/source/ARM_DWARF_Registers.h
new file mode 100644
index 00000000000..e892927f6cf
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/ARM_DWARF_Registers.h
@@ -0,0 +1,205 @@
+//===-- ARM_DWARF_Registers.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef ARM_DWARF_Registers_h_
+#define ARM_DWARF_Registers_h_
+
+enum {
+ dwarf_r0 = 0,
+ dwarf_r1,
+ dwarf_r2,
+ dwarf_r3,
+ dwarf_r4,
+ dwarf_r5,
+ dwarf_r6,
+ dwarf_r7,
+ dwarf_r8,
+ dwarf_r9,
+ dwarf_r10,
+ dwarf_r11,
+ dwarf_r12,
+ dwarf_sp,
+ dwarf_lr,
+ dwarf_pc,
+ dwarf_cpsr,
+
+ dwarf_s0 = 64,
+ dwarf_s1,
+ dwarf_s2,
+ dwarf_s3,
+ dwarf_s4,
+ dwarf_s5,
+ dwarf_s6,
+ dwarf_s7,
+ dwarf_s8,
+ dwarf_s9,
+ dwarf_s10,
+ dwarf_s11,
+ dwarf_s12,
+ dwarf_s13,
+ dwarf_s14,
+ dwarf_s15,
+ dwarf_s16,
+ dwarf_s17,
+ dwarf_s18,
+ dwarf_s19,
+ dwarf_s20,
+ dwarf_s21,
+ dwarf_s22,
+ dwarf_s23,
+ dwarf_s24,
+ dwarf_s25,
+ dwarf_s26,
+ dwarf_s27,
+ dwarf_s28,
+ dwarf_s29,
+ dwarf_s30,
+ dwarf_s31,
+
+ // FPA Registers 0-7
+ dwarf_f0 = 96,
+ dwarf_f1,
+ dwarf_f2,
+ dwarf_f3,
+ dwarf_f4,
+ dwarf_f5,
+ dwarf_f6,
+ dwarf_f7,
+
+ // Intel wireless MMX general purpose registers 0 - 7
+ dwarf_wCGR0 = 104,
+ dwarf_wCGR1,
+ dwarf_wCGR2,
+ dwarf_wCGR3,
+ dwarf_wCGR4,
+ dwarf_wCGR5,
+ dwarf_wCGR6,
+ dwarf_wCGR7,
+
+ // XScale accumulator register 0–7 (they do overlap with wCGR0 - wCGR7)
+ dwarf_ACC0 = 104,
+ dwarf_ACC1,
+ dwarf_ACC2,
+ dwarf_ACC3,
+ dwarf_ACC4,
+ dwarf_ACC5,
+ dwarf_ACC6,
+ dwarf_ACC7,
+
+ // Intel wireless MMX data registers 0 - 15
+ dwarf_wR0 = 112,
+ dwarf_wR1,
+ dwarf_wR2,
+ dwarf_wR3,
+ dwarf_wR4,
+ dwarf_wR5,
+ dwarf_wR6,
+ dwarf_wR7,
+ dwarf_wR8,
+ dwarf_wR9,
+ dwarf_wR10,
+ dwarf_wR11,
+ dwarf_wR12,
+ dwarf_wR13,
+ dwarf_wR14,
+ dwarf_wR15,
+
+ dwarf_spsr = 128,
+ dwarf_spsr_fiq,
+ dwarf_spsr_irq,
+ dwarf_spsr_abt,
+ dwarf_spsr_und,
+ dwarf_spsr_svc,
+
+ dwarf_r8_usr = 144,
+ dwarf_r9_usr,
+ dwarf_r10_usr,
+ dwarf_r11_usr,
+ dwarf_r12_usr,
+ dwarf_r13_usr,
+ dwarf_r14_usr,
+ dwarf_r8_fiq,
+ dwarf_r9_fiq,
+ dwarf_r10_fiq,
+ dwarf_r11_fiq,
+ dwarf_r12_fiq,
+ dwarf_r13_fiq,
+ dwarf_r14_fiq,
+ dwarf_r13_irq,
+ dwarf_r14_irq,
+ dwarf_r13_abt,
+ dwarf_r14_abt,
+ dwarf_r13_und,
+ dwarf_r14_und,
+ dwarf_r13_svc,
+ dwarf_r14_svc,
+
+ // Intel wireless MMX control register in co-processor 0 - 7
+ dwarf_wC0 = 192,
+ dwarf_wC1,
+ dwarf_wC2,
+ dwarf_wC3,
+ dwarf_wC4,
+ dwarf_wC5,
+ dwarf_wC6,
+ dwarf_wC7,
+
+ // VFP-v3/Neon
+ dwarf_d0 = 256,
+ dwarf_d1,
+ dwarf_d2,
+ dwarf_d3,
+ dwarf_d4,
+ dwarf_d5,
+ dwarf_d6,
+ dwarf_d7,
+ dwarf_d8,
+ dwarf_d9,
+ dwarf_d10,
+ dwarf_d11,
+ dwarf_d12,
+ dwarf_d13,
+ dwarf_d14,
+ dwarf_d15,
+ dwarf_d16,
+ dwarf_d17,
+ dwarf_d18,
+ dwarf_d19,
+ dwarf_d20,
+ dwarf_d21,
+ dwarf_d22,
+ dwarf_d23,
+ dwarf_d24,
+ dwarf_d25,
+ dwarf_d26,
+ dwarf_d27,
+ dwarf_d28,
+ dwarf_d29,
+ dwarf_d30,
+ dwarf_d31,
+
+ // Neon quadword registers
+ dwarf_q0 = 288,
+ dwarf_q1,
+ dwarf_q2,
+ dwarf_q3,
+ dwarf_q4,
+ dwarf_q5,
+ dwarf_q6,
+ dwarf_q7,
+ dwarf_q8,
+ dwarf_q9,
+ dwarf_q10,
+ dwarf_q11,
+ dwarf_q12,
+ dwarf_q13,
+ dwarf_q14,
+ dwarf_q15
+};
+
+#endif // ARM_DWARF_Registers_h_
diff --git a/gnu/llvm/lldb/tools/debugserver/source/ARM_ehframe_Registers.h b/gnu/llvm/lldb/tools/debugserver/source/ARM_ehframe_Registers.h
new file mode 100644
index 00000000000..b1a085a506e
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/ARM_ehframe_Registers.h
@@ -0,0 +1,33 @@
+//===-- ARM_ehframe_Registers.h -------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef utility_ARM_ehframe_Registers_h_
+#define utility_ARM_ehframe_Registers_h_
+
+enum {
+ ehframe_r0 = 0,
+ ehframe_r1,
+ ehframe_r2,
+ ehframe_r3,
+ ehframe_r4,
+ ehframe_r5,
+ ehframe_r6,
+ ehframe_r7,
+ ehframe_r8,
+ ehframe_r9,
+ ehframe_r10,
+ ehframe_r11,
+ ehframe_r12,
+ ehframe_sp,
+ ehframe_lr,
+ ehframe_pc,
+ ehframe_cpsr
+};
+
+#endif // utility_ARM_ehframe_Registers_h_
diff --git a/gnu/llvm/lldb/tools/debugserver/source/CMakeLists.txt b/gnu/llvm/lldb/tools/debugserver/source/CMakeLists.txt
new file mode 100644
index 00000000000..a7d789dd9e1
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/CMakeLists.txt
@@ -0,0 +1,272 @@
+include(CheckCXXCompilerFlag)
+include(CheckLibraryExists)
+include_directories(${CMAKE_CURRENT_BINARY_DIR}/..)
+include_directories(${LLDB_SOURCE_DIR}/source)
+include_directories(MacOSX/DarwinLog)
+
+include_directories(MacOSX)
+
+function(check_certificate identity result_valid)
+ execute_process(
+ COMMAND security find-certificate -Z -p -c ${identity} /Library/Keychains/System.keychain
+ RESULT_VARIABLE exit_code OUTPUT_QUIET ERROR_QUIET)
+ if(exit_code)
+ set(${result_valid} FALSE PARENT_SCOPE)
+ else()
+ set(${result_valid} TRUE PARENT_SCOPE)
+ endif()
+endfunction()
+
+function(get_debugserver_codesign_identity result)
+ string(CONCAT not_found_help
+ "This will cause failures in the test suite."
+ "Pass '-DLLDB_USE_SYSTEM_DEBUGSERVER=ON' to use the system one instead."
+ "See 'Code Signing on macOS' in the documentation."
+ )
+
+ # Explicit override: warn if unavailable
+ if(LLDB_CODESIGN_IDENTITY)
+ set(${result} ${LLDB_CODESIGN_IDENTITY} PARENT_SCOPE)
+ check_certificate(${LLDB_CODESIGN_IDENTITY} available)
+ if(NOT available)
+ message(WARNING "LLDB_CODESIGN_IDENTITY not found: '${LLDB_CODESIGN_IDENTITY}' ${not_found_help}")
+ endif()
+ return()
+ endif()
+
+ # Development signing identity: use if available
+ check_certificate(lldb_codesign available)
+ if(available)
+ set(${result} lldb_codesign PARENT_SCOPE)
+ return()
+ endif()
+
+ message(WARNING "Development code sign identiy not found: 'lldb_codesign' ${not_found_help}")
+
+ # LLVM pendant: fallback if available
+ if(LLVM_CODESIGNING_IDENTITY)
+ check_certificate(${LLVM_CODESIGNING_IDENTITY} available)
+ if(available)
+ set(${result} ${LLVM_CODESIGNING_IDENTITY} PARENT_SCOPE)
+ return()
+ endif()
+ endif()
+
+ # Ad-hoc signing: last resort
+ set(${result} "-" PARENT_SCOPE)
+endfunction()
+
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -Wl,-sectcreate,__TEXT,__info_plist,${CMAKE_CURRENT_SOURCE_DIR}/../resources/lldb-debugserver-Info.plist")
+
+check_cxx_compiler_flag("-Wno-gnu-zero-variadic-macro-arguments"
+ CXX_SUPPORTS_NO_GNU_ZERO_VARIADIC_MACRO_ARGUMENTS)
+if (CXX_SUPPORTS_NO_GNU_ZERO_VARIADIC_MACRO_ARGUMENTS)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-gnu-zero-variadic-macro-arguments")
+endif ()
+
+check_cxx_compiler_flag("-Wno-zero-length-array"
+ CXX_SUPPORTS_NO_ZERO_LENGTH_ARRAY)
+if (CXX_SUPPORTS_NO_ZERO_LENGTH_ARRAY)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-zero-length-array")
+endif ()
+
+check_cxx_compiler_flag("-Wno-extended-offsetof"
+ CXX_SUPPORTS_NO_EXTENDED_OFFSETOF)
+if (CXX_SUPPORTS_NO_EXTENDED_OFFSETOF)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-extended-offsetof")
+endif ()
+
+check_library_exists(compression compression_encode_buffer "" HAVE_LIBCOMPRESSION)
+
+add_subdirectory(MacOSX)
+
+set(LLDB_CODESIGN_IDENTITY "" CACHE STRING
+ "Identity override for debugserver; see 'Code Signing on macOS' in the documentation (Darwin only)")
+
+get_debugserver_codesign_identity(debugserver_codesign_identity)
+
+# Override locally, so the identity is used for targets created in this scope.
+set(LLVM_CODESIGNING_IDENTITY ${debugserver_codesign_identity})
+
+# Use the same identity later in the test suite.
+set_property(GLOBAL PROPERTY
+ LLDB_DEBUGSERVER_CODESIGN_IDENTITY ${debugserver_codesign_identity})
+
+if(APPLE)
+ if(IOS)
+ find_library(BACKBOARD_LIBRARY BackBoardServices
+ PATHS ${CMAKE_OSX_SYSROOT}/System/Library/PrivateFrameworks)
+ find_library(FRONTBOARD_LIBRARY FrontBoardServices
+ PATHS ${CMAKE_OSX_SYSROOT}/System/Library/PrivateFrameworks)
+ find_library(SPRINGBOARD_LIBRARY SpringBoardServices
+ PATHS ${CMAKE_OSX_SYSROOT}/System/Library/PrivateFrameworks)
+ find_library(MOBILESERVICES_LIBRARY MobileCoreServices
+ PATHS ${CMAKE_OSX_SYSROOT}/System/Library/PrivateFrameworks)
+ find_library(LOCKDOWN_LIBRARY lockdown)
+
+ if(NOT BACKBOARD_LIBRARY)
+ set(SKIP_TEST_DEBUGSERVER ON CACHE BOOL "" FORCE)
+ endif()
+ endif()
+endif()
+
+if(HAVE_LIBCOMPRESSION)
+ set(LIBCOMPRESSION compression)
+endif()
+
+if(LLDB_USE_ENTITLEMENTS)
+ if(IOS)
+ set(entitlements ${CMAKE_CURRENT_SOURCE_DIR}/debugserver-entitlements.plist)
+ else()
+ # Same entitlements file as used for lldb-server
+ set(entitlements ${LLDB_SOURCE_DIR}/resources/debugserver-macosx-entitlements.plist)
+ endif()
+endif()
+
+set(generated_mach_interfaces
+ ${CMAKE_CURRENT_BINARY_DIR}/mach_exc.h
+ ${CMAKE_CURRENT_BINARY_DIR}/mach_excServer.c
+ ${CMAKE_CURRENT_BINARY_DIR}/mach_excUser.c
+ )
+add_custom_command(OUTPUT ${generated_mach_interfaces}
+ COMMAND mig ${CMAKE_CURRENT_SOURCE_DIR}/MacOSX/dbgnub-mig.defs
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/MacOSX/dbgnub-mig.defs
+ )
+
+set(DEBUGSERVER_VERS_GENERATED_FILE ${CMAKE_CURRENT_BINARY_DIR}/debugserver_vers.c)
+configure_file(debugserver_vers.c.in
+ ${DEBUGSERVER_VERS_GENERATED_FILE} @ONLY)
+
+set(lldbDebugserverCommonSources
+ DNBArch.cpp
+ DNBBreakpoint.cpp
+ DNB.cpp
+ DNBDataRef.cpp
+ DNBError.cpp
+ DNBLog.cpp
+ DNBRegisterInfo.cpp
+ DNBThreadResumeActions.cpp
+ JSON.cpp
+ StdStringExtractor.cpp
+ # JSON reader depends on the following LLDB-common files
+ ${LLDB_SOURCE_DIR}/source/Host/common/StringConvert.cpp
+ ${LLDB_SOURCE_DIR}/source/Host/common/SocketAddress.cpp
+ # end JSON reader dependencies
+ libdebugserver.cpp
+ PseudoTerminal.cpp
+ PThreadEvent.cpp
+ PThreadMutex.cpp
+ RNBContext.cpp
+ RNBRemote.cpp
+ RNBServices.cpp
+ RNBSocket.cpp
+ SysSignal.cpp
+ TTYState.cpp
+
+ MacOSX/CFBundle.cpp
+ MacOSX/CFString.cpp
+ MacOSX/Genealogy.cpp
+ MacOSX/MachException.cpp
+ MacOSX/MachProcess.mm
+ MacOSX/MachTask.mm
+ MacOSX/MachThread.cpp
+ MacOSX/MachThreadList.cpp
+ MacOSX/MachVMMemory.cpp
+ MacOSX/MachVMRegion.cpp
+ MacOSX/OsLogger.cpp
+ ${generated_mach_interfaces}
+ ${DEBUGSERVER_VERS_GENERATED_FILE})
+
+add_library(lldbDebugserverCommon ${lldbDebugserverCommonSources})
+set_target_properties(lldbDebugserverCommon PROPERTIES FOLDER "lldb libraries/debugserver")
+
+target_link_libraries(lldbDebugserverCommon
+ INTERFACE ${COCOA_LIBRARY}
+ ${CORE_FOUNDATION_LIBRARY}
+ ${FOUNDATION_LIBRARY}
+ ${BACKBOARD_LIBRARY}
+ ${FRONTBOARD_LIBRARY}
+ ${SPRINGBOARD_LIBRARY}
+ ${MOBILESERVICES_LIBRARY}
+ ${LOCKDOWN_LIBRARY}
+ lldbDebugserverArchSupport
+ lldbDebugserverDarwin_DarwinLog
+ ${LIBCOMPRESSION})
+if(HAVE_LIBCOMPRESSION)
+ set_property(TARGET lldbDebugserverCommon APPEND PROPERTY
+ COMPILE_DEFINITIONS HAVE_LIBCOMPRESSION)
+endif()
+set(LLVM_OPTIONAL_SOURCES ${lldbDebugserverCommonSources})
+add_lldb_tool(debugserver ADD_TO_FRAMEWORK
+ debugserver.cpp
+ LINK_LIBS lldbDebugserverCommon
+ ENTITLEMENTS ${entitlements}
+)
+
+# Workaround for Xcode-specific code-signing behavior:
+# The XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY option causes debugserver to be copied
+# into the framework first and code-signed afterwards. Sign the copy manually.
+if (debugserver_codesign_identity AND LLDB_BUILD_FRAMEWORK AND
+ CMAKE_GENERATOR STREQUAL "Xcode")
+ if(NOT CMAKE_CODESIGN_ALLOCATE)
+ execute_process(
+ COMMAND xcrun -f codesign_allocate
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ OUTPUT_VARIABLE CMAKE_CODESIGN_ALLOCATE
+ )
+ endif()
+ if(entitlements)
+ set(pass_entitlements --entitlements ${entitlements})
+ endif()
+
+ set(copy_location ${LLDB_FRAMEWORK_ABSOLUTE_BUILD_DIR}/LLDB.framework/Versions/${LLDB_FRAMEWORK_VERSION}/Resources/debugserver)
+
+ add_custom_command(TARGET debugserver POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E
+ env CODESIGN_ALLOCATE=${CMAKE_CODESIGN_ALLOCATE}
+ xcrun codesign -f -s ${debugserver_codesign_identity}
+ ${pass_entitlements} ${copy_location}
+ COMMENT "Code-sign debugserver copy in the build-tree framework: ${copy_location}"
+ )
+endif()
+
+set_target_properties(debugserver PROPERTIES FOLDER "lldb libraries/debugserver")
+
+if(IOS)
+ set_property(TARGET lldbDebugserverCommon APPEND PROPERTY COMPILE_DEFINITIONS
+ WITH_LOCKDOWN
+ WITH_FBS
+ WITH_BKS
+ )
+ set_property(TARGET debugserver APPEND PROPERTY COMPILE_DEFINITIONS
+ WITH_LOCKDOWN
+ WITH_FBS
+ WITH_BKS
+ )
+ set_property(TARGET lldbDebugserverCommon APPEND PROPERTY COMPILE_FLAGS
+ -F${CMAKE_OSX_SYSROOT}/System/Library/PrivateFrameworks
+ )
+
+ add_library(lldbDebugserverCommon_NonUI ${lldbDebugserverCommonSources})
+ target_link_libraries(lldbDebugserverCommon_NonUI
+ INTERFACE ${COCOA_LIBRARY}
+ ${CORE_FOUNDATION_LIBRARY}
+ ${FOUNDATION_LIBRARY}
+ lldbDebugserverArchSupport
+ lldbDebugserverDarwin_DarwinLog
+ ${LIBCOMPRESSION})
+ if(HAVE_LIBCOMPRESSION)
+ set_property(TARGET lldbDebugserverCommon_NonUI APPEND PROPERTY
+ COMPILE_DEFINITIONS HAVE_LIBCOMPRESSION)
+ endif()
+
+ add_lldb_tool(debugserver-nonui
+ debugserver.cpp
+
+ LINK_LIBS
+ lldbDebugserverCommon_NonUI
+
+ ENTITLEMENTS
+ ${entitlements}
+ )
+endif()
diff --git a/gnu/llvm/lldb/tools/debugserver/source/ChangeLog b/gnu/llvm/lldb/tools/debugserver/source/ChangeLog
new file mode 100644
index 00000000000..898f2fba7b0
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/ChangeLog
@@ -0,0 +1,1515 @@
+2010-01-29 Greg Clayton <gclayton@apple.com>
+
+ * MachProcess.cpp (MachProcess::PrepareForAttach): No longer use the
+ SBSLaunchApplication macro from the SpringBoard.framework, use the actual
+ function name SBSLaunchApplicationForDebugging.
+ (MachProcess::CleanupAfterAttach): Ditto.
+ (MachProcess::SBForkChildForPTraceDebugging): Ditto.
+ (debugserver-entitlements.plist): Added the "seatbelt-profiles" entitlement
+ so debugserver can be sandboxed.
+
+2009-07-06 Greg Clayton <gclayton@apple.com>
+
+ * MachTask.cpp (MachTask::GetDYLDAllImageInfosAddress): Hack around bad
+ kernel code that renamed the first member of the TASK_DYLD_INFO without
+ any way to detect it has changed.
+
+2009-06-29 Greg Clayton <gclayton@apple.com>
+
+ * DNB.cpp (GetAllInfosMatchingName): Correctly truncate process name string
+ to MAXCOMLEN when searching kinfo_proc structs for process matches by name.
+ * MachProcess.cpp (MachProcess::PrepareForAttach): Added logging when
+ attaching to a program by name.
+
+2009-06-25 Greg Clayton <gclayton@apple.com>
+
+ * DNB.cpp (DNBProcessLaunch): Added a stat on the incoming path that we are
+ about to launch to make sure the file exists. If the file doesn't, then an
+ appropriate error string is returned. Also if we fail to get the task for
+ our process ID, we return an error string right away instead of letting the
+ debug session go for a little bit and then later failing after a few more
+ packets.
+
+2009-04-07 Jim Ingham <jingham@apple.com>
+
+ * RNBRemote.h: Add vAttachWait
+ * RNBRemote.cpp (RNBRemote::CreatePacketTable): Add vattachwait.
+ (RNBRemoteShouldCancelCallback): New function.
+ (RNBRemote::HandlePacket_v): Handle vattachwait.
+ * RNBSocket.cpp (RNBSocket::Read): Mark the connection as closed when the
+ port goes away.
+ * DNB.cpp (DNBProcessAttachByName): New function.
+ (DNBProcessAttach): Make this handle catching the attach when done and
+ dealing with timeout & return conditions.
+ (GetAllInfos): New function.
+ (GetAlInfosMatchingName): New function.
+ (DNBProcessAttachWait): New function.
+ DNB.h: Declare DNBProcessAttachByName, DNBProcessAttachWait, change
+ signature of DNBProcessAttach.
+ * MachProcess.cpp (MachProcess::PrepareForAttach): New function.
+ (MachProcess::CheckForProcess): New function.
+ (MachProcess::CleanupAfterAttach): New function.
+ (CopyBundleIDForPath): New function.
+ (MachProcess::SBForkChildForPTraceDebugging): Convert to using
+ CopyBundleIDForPath.
+ * MachProcess.h: Declare PrepareForAttach, CleanupAfterAttach and
+ CheckForProcess.
+ * DNBTimer.h (TimeOfDayLaterThan): New function.
+ * test-remotenub.cpp (RNBRunLoopGetStartModeFromRemote): Rename from
+ RNBRunLoopGetArgsFromRemote, and handle vattachwait.
+ (RNBRunLoopLaunchAttaching): Code was moved from here into DNBProcessAttach.
+ (StartListening): New function.
+ (GetAllProcessInfos, GetAllProcessInfosMatchingName): Moved to
+ DNBProcess.cpp.
+ (main): Handle attach waitfor, and make debugserver with only a host and
+ port wait on commands from gdb.
+
+2009-04-03 Greg Clayton <gclayton@apple.com>
+
+ * RNBRemote.h (PacketEnum): Added enum for qShlibInfoAddr.
+ * RNBRemote.cpp (RNBRemote::CreatePacketTable) Added the qShlibInfoAddr
+ packet definition to m_packets.
+ (RNBRemote::GetPacket): Log when we run into an unimplemented packet.
+ (RNBRemote::HandleReceivedPacket): Only log the packet when logging
+ LOG_RNB_REMOTE.
+ (RNBRemote::HandlePacket_q): Add support for the new qShlibInfoAddr packet.
+ * DNB.h (DNBProcessGetSharedLibraryInfoAddress): New prototype.
+ * DNB.cpp (DNBProcessGetSharedLibraryInfoAddress): New function.
+ * MachTask.h (MachProcess::GetDYLDAllImageInfosAddress): New prototype.
+ * MachTask.cpp (MachProcess::GetDYLDAllImageInfosAddress): New function.
+
+2009-04-01 Greg Clayton <gclayton@apple.com>
+
+ * test-remotenub.cpp (main): Display the detailed error message if any when
+ attaching fails.
+
+2009-03-25 Greg Clayton <gclayton@apple.com>
+
+ * test-remotenub.cpp (RNBRunLoopGetArgsFromRemote): Cleaned up logging and
+ removed time deltas form the messages.
+ (RNBRunLoopLaunchAttaching): Ditto.
+ (RNBRunLoopLaunchInferior): Ditto and also use new DNBProcessLaunch that
+ takes an error string pointer.
+ * RNBContext.h (class RNBContext): Removed the m_timer member.
+ * RNBContext.cpp (RNBContext::StartProcessStatusThread): Cleaned up logging
+ and removed time deltas form the messages.
+ (RNBContext::ThreadFunctionProcessStatus): Ditto.
+ * RNBSocket.h (class RNBSocket): Removed unused m_last_errno member and
+ accessor functions.
+ * RNBSocket.cpp (RNBSocket::Listen): Cleaned up logging and
+ removed time deltas form the messages.
+ (RNBSocket::ConnectToService): Ditto.
+ (RNBSocket::Read): Ditto.
+ (RNBSocket::Write): Ditto.
+ (RNBSocket::SaveErrno): Removed.
+ (RNBSocket::ClosePort): Don't call RNBSocket::SaveErrno().
+ * RNBRemote.cpp (RNBRemote::RNBRemote): Cleaned up logging and
+ removed time deltas form the messages.
+ (RNBRemote::~RNBRemote): Ditto.
+ (RNBRemote::SendPacket): Ditto.
+ (RNBRemote::GetPacketPayload): Ditto.
+ (RNBRemote::GetPacket): Ditto): Ditto.
+ (RNBRemote::HandleAsyncPacket): Ditto.
+ (RNBRemote::HandleReceivedPacket): Ditto.
+ (RNBRemote::CommDataReceived): Ditto.
+ * DNB.cpp (DNBProcessLaunch): Changed to take a eror string pointer with
+ size for more desciptive error reporting (instead of a uint32_t pointer).
+ * DNB.h (DNBProcessLaunch): Ditto.
+ * DNBError.cpp (DNBError::AsString): Now returns NULL if there is no error.
+ * DNBError.h (DNBError::SetErrorString): New accessor to allow custom error
+ strings.
+ * arm/DNBArchImpl.cpp (DNBArchMachARM::GetGPRState): Improved logging.
+ * MachProcess.cpp (MachProcess::SBForkChildForPTraceDebugging): Improved
+ error messages when a file doesn't exist, or when unable to extract the
+ CFBundleIdentifier.
+ * PThreadEvent.cpp (class PThreadEvent): Commented out all logging calls.
+
+2009-03-07 Greg Clayton <gclayton@apple.com>
+
+ * test-remotenub.cpp (GetAllProcessInfosMatchingName): New function that
+ returns matching kinfo_proc structs given a process name.
+ (main): Enhanced the --attach option to be able to take a PROCNAME or
+ a PID. Changed the --waitfor=PROCNAME option to ignore any existing
+ processes with PROCNAME so we only catch new process invocations.
+
+2009-03-07 Greg Clayton <gclayton@apple.com>
+
+ * RNBRemote.cpp (RNBRemote::HandlePacket_p): Use the correct get current
+ thread function call so we get the correct thread registers.
+
+2009-03-03 Greg Clayton <gclayton@apple.com>
+
+ * test-remotenub.cpp (g_isatty): New global that gets set to non-zero if
+ STDOUT is a TTY in the beginning of main.
+ (RNBLogSTDOUT): New macro that logs to STDOUT if g_isatty is non-zero, else
+ it logs to asl.
+ (RNBLogSTDERR): New macro that logs to STDERR if g_isatty is non-zero, else
+ it logs to asl.
+ (RNBRunLoopGetArgsFromRemote): Use new RNBLogSTDOUT/RNBLogSTDERR macros.
+ (GetAllProcessInfos): Get all process info structs for everything on the
+ system.
+ (main): Implemented new --waitfor=NAME option to allow waiting for a process
+ to run by polling the system processes. The new --waitfor-interval=N option
+ allows fine control over the polling interval where N is the number of mirco
+ seconds (usec) to wait between polls (defaults to 1000). The new
+ --waitfor-duration=N allows a timeout in seconds to be specified when
+ waiting for a process (defaults to infinite).
+
+2009-03-02 Greg Clayton <gclayton@apple.com>
+
+ * DNBArchImpl.cpp (DNBArchMachARM::EvaluateNextInstructionForSoftwareBreakpointSetup):
+ Take care of a case where no instructions execute in a Thumb IT block and
+ the last of which is a branch.
+
+2009-02-10 Greg Clayton <gclayton@apple.com>
+
+ * RNBRemote.h (PacketEnum): Added 'detach' enumeration.
+ (RNBRemote::HandlePacket_D): New member function prototype.
+ * RNBRemote.cpp (RNBRemote::CreatePacketTable): Added detach support.
+ (RNBRemote::HandlePacket_D): New function for detach support.
+
+2009-02-10 Greg Clayton <gclayton@apple.com>
+
+ * RNBRemote.cpp (RNBRemote::HandlePacket_UNIMPLEMENTED): Log this
+ packet with the packet that is unimplemented.
+ (RNBRemote::GetPacket): Call RNBRemote::HandlePacket_UNIMPLEMENTED()
+ when we don't recognize a packet.
+ (RNBRemote::HandleReceivedPacket): Don't reply to packets we don't
+ recognize with unimplemented in this function as that should have
+ already been done for us in RNBRemote::GetPacket().
+
+2009-02-10 Greg Clayton <gclayton@apple.com>
+
+ * RNBRemote.h (PacketEnum): Added query_step_packet_supported.
+ * RNBRemot.cpp (RNBRemote::CreatePacketTable): Added new
+ qStepPacketSupported packet.
+ (RNBRemote::HandlePacket_q): Added support for the new
+ "qStepPacketSupported" packet.
+ (RNBRemote::HandlePacket_G): Some cleanup when reading registers
+ to avoid spurious console logging.
+
+2009-01-30 Greg Clayton <gclayton@apple.com>
+
+ * debugserver-entitlements.plist: Changed the entitlement
+ "run-invalid-allow" to "run-unsigned-code".
+
+2009-01-23 Greg Clayton <gclayton@apple.com>
+
+ * DNBArchImpl.cpp (DNBArchMachARM::EvaluateNextInstructionForSoftwareBreakpointSetup):
+ Merged Yusuf's changes to make software single stepping work.
+ * test-remotenub.cpp (RNBRunLoopLaunchInferior): Call new
+ DNBResolveExecutablePath function to resolve executable paths.
+ * DNB.h (DNBResolveExecutablePath): New function prototype.
+ * DNB.cpp (DNBResolveExecutablePath): New function that will resolve
+ relative paths and also executable paths for executables that aren't relative
+ but yet are in the shell PATH environment variable.
+
+2009-01-22 Greg Clayton <gclayton@apple.com>
+
+ * DNBArchImpl.h (class DBNArchMachARM): Renamed member variable
+ m_chained_hw_single_step_addr to m_hw_single_chained_step_addr. Added
+ new member variables: m_sw_single_step_itblock_break_id, m_last_decode_pc,
+ and m_sw_single_step_itblock_break_count. Renamed m_thumbStaticData to
+ m_last_decode_thumb, and renamed m_decodedInstruction to m_last_decode_arm.
+ (DBNArchMachARM::DecodeITBlockInstructions): New prototype.
+ (DBNArchMachARM::DecodeInstructionUsingDisassembler): New prototype.
+ (DBNArchMachARM::BreakpointHit): New prototype.
+ * DNBArchImpl.cpp (DNBArchMachARM::ThreadDidStop): Disable any of the
+ many software single step breakpoints if any are set.
+ (DNBArchMachARM::StepNotComplete): Changed renamed member accesses.
+ (DNBArchMachARM::DecodeITBlockInstructions): New function for software
+ single stepping through Thumb IT blocks.
+ (DNBArchMachARM::EnableHardwareSingleStep): Cleaned up logging.
+ (DNBArchMachARM::ComputeNextPC): Ditto.
+ (DNBArchMachARM::EvaluateNextInstructionForSoftwareBreakpointSetup): Now
+ properly handles Thumb IT software single stepping.
+ (DNBArchMachARM::SetSingleStepSoftwareBreakpoints): Ditto.
+ (DNBArchMachARM::DecodeInstructionUsingDisassembler): New function.
+ (DNBArchMachARM::BreakpointHit): New breakpoint callback function.
+
+2009-01-21 Greg Clayton <gclayton@apple.com>
+
+ * MachProcess.cpp (MachProcess::PrivateResume): Set the process state before
+ we actually resume so we are sure to get the events in the correct order.
+
+2009-01-16 Greg Clayton <gclayton@apple.com>
+
+ * RNBRemote.cpp (RNBRemote::HandlePacket_last_signal): Include only
+ registers which are to be expedited in the T packets.
+ (RNBRemote::HandlePacket_p): Enable for all targets.
+ (struct register_map_entry): Added an expedite member so we know which
+ registers need to be sent up to the host with each stop reply packet.
+ (register_map): Updated each array members' expedite member with an
+ appropriate value.
+
+2009-01-16 Greg Clayton <gclayton@apple.com>
+
+ * RNBRemote.cpp (RNBRemote::HandlePacket_s): Enabled the step command ("s"
+ packet) for ARM now that libdebugnub.dylib can do both hardware and software
+ single stepping.
+
+2009-01-13 Greg Clayton <gclayton@apple.com>
+
+ *DNBArchImpl.cpp (bit): New function.
+ (bits): New function.
+ (DNBArchMachARM::ConditionPassed): Use new "bit" function.
+ (DNBArchMachARM::ComputeNextPC): Use new "bit" function, remove inline
+ assembly for "RSC" instruction so this compiles for armv7 (which defaults
+ to thumb)
+ (DNBArchMachARM::NumSupportedHardwareBreakpoints): Use new "bits" function.
+ (DNBArchMachARM::NumSupportedHardwareWatchpoints): Use new "bits" function.
+
+2009-01-12 Greg Clayton <gclayton@apple.com>
+
+ * DNBArch.h (DNBArchProtocol::NumSupportedHardwareBreakpoints()): Removed
+ the "const" qualifier to allow arches to auto detect how many hardware
+ breakpoints they have.
+ (DNBArchProtocol::NumSupportedHardwareWatchpoints()): Removed the "const"
+ qualifier to allow arches to auto detect how many hardware watchpoints they
+ have.
+ * DNBArchImpl.h (DNBArchMachARM::NumSupportedHardwareBreakpoints()): Auto
+ detect how many BRP pairs are avialable and disable for armv7 for the time
+ being (rdar://problem/6372672).
+ (DNBArchMachARM::NumSupportedHardwareWatchpoints()): Auto detect how many
+ WRP pairs are avialable and disable for armv7 for the time being
+ (rdar://problem/6372672).
+
+2009-01-09 Greg Clayton <gclayton@apple.com>
+
+ * test-remotenub.cpp (main): Filled in short argument versions for
+ --applist (-t) and --lockdown (-k) options.
+ * DNBArchImpl.h (DNBArchMachARM::ConditionPassed): New protected
+ member function.
+ (DNBArchMachARM::ComputeNextPC): New protected member function.
+ (DNBArchMachARM::EvaluateNextInstructionForSoftwareBreakpointSetup): New
+ protected member function.
+ (DNBArchMachARM::m_thumbStaticData): New protected member variable.
+ (DNBArchMachARM::m_decodedInstruction): New protected member variable.
+ * DNBArchImpl.cpp (DNBArchMachARM::ThreadDidStop): Added extra code that
+ will log and exit when we are verifying software single stepping (a
+ compile time option).
+ (DNBArchMachARM::ConditionPassed): New function.
+ (DNBArchMachARM::ComputeNextPC): New function.
+ (DNBArchMachARM::EvaluateNextInstructionForSoftwareBreakpointSetup): New
+ function.
+ (DNBArchMachARM::SetSingleStepSoftwareBreakpoints): Added the guts of the
+ software single stepping.
+ (DNBArchMachARM::NumSupportedHardwareBreakpoints): Prepared for adding
+ auto detection code.
+ (DNBArchMachARM::NumSupportedHardwareWatchpoints): Prepared for adding
+ auto detection code.
+
+2008-12-11 Greg Clayton <gclayton@apple.com>
+
+ * DNB.h (DNBProcessWaitForEvent): Renamed to DNBProcessWaitForEvents.
+ (DNBProcessSetEvents): Removed (deprecated).
+ (DNBProcessGetWaitForResetMask): Removed (unused).
+ (DNBProcessSetWaitForResetMask): Removed (unused).
+ (DNBProcessInterruptEvents): New function prototype.
+ * DNB.cpp (DNBProcessWaitForEvent): Renamed to DNBProcessWaitForEvents.
+ (DNBProcessSetEvents): Removed (deprecated).
+ (DNBProcessGetWaitForResetMask): Removed (unused).
+ (DNBProcessSetWaitForResetMask): Removed (unused).
+ (DNBProcessInterruptEvents): New function that can be used to
+ asynchronously interrupt infinite wait for events calls.
+ RNBRemote.cpp (RNBRemote::HandlePacket_v): Call DNBProcessWaitForEvents.
+ RNBContext.cpp (RNBContext::ThreadFunctionProcessStatus): Ditto.
+ test-remotenub.cpp (RNBRunLoopLaunchInferior): Ditto.
+ (RNBRunLoopLaunchAttaching): Ditto.
+
+2008-12-11 Greg Clayton <gclayton@apple.com>
+
+ * DNB.cpp (GetProcessMap): Use new PTHREAD_MUTEX_LOCKER macro to ease
+ debugging of deadlocks.
+ (DNBProcessLaunch): Improved logging.
+ (DNBProcessMemoryRead): Call MachProcess::ReadMemory so breakpoint
+ opcodes can be removed from memory.
+ (DNBProcessMemoryWrite): Call MachProcess::WriteMemory so that we work
+ around enabled software breakpoint traps.
+ * DNBLog.cpp (GetLogThreadedMutex): New function.
+ (_DNBLogThreaded): Use new PTHREAD_MUTEX_LOCKER macro to ease
+ debugging of deadlocks.
+ (_DNBLogThreadedIf): Ditto.
+ * DNBBreakpoint.h (DNBBreakpoint::IntersectsRange): New function.
+ * DNBBreakpoint.cpp (DNBBreakpointList::FindIDByAddress): Improved
+ logging.
+ * MacOSX/MachThread.cpp (MachThread::MachThread): Improved logging.
+ (MachThread::~MachThread): Ditto.
+ (MachThread::Suspend): Ditto.
+ (MachThread::Resume): Ditto.
+ (MachThread::RestoreSuspendCount): Ditto.
+ (MachThread::GetState): Use new PTHREAD_MUTEX_LOCKER macro to ease
+ debugging of deadlocks.
+ (MachThread::SetState): Ditto.
+ * MacOSX/MachVMMemory.cpp (MachVMMemory::Read): Improved logging.
+ (MachVMMemory::Write): Ditto.
+ (MachVMMemory::WriteRegion): Ditto.
+ * MacOSX/MachProcess.cpp (MachProcess::GetState): Use new
+ PTHREAD_MUTEX_LOCKER macro to ease debugging of deadlocks.
+ (MachProcess::SetState): Ditto.
+ (MachProcess::Clear): Ditto.
+ (MachProcess::PrivateResume): Ditto.
+ (MachProcess::ReplyToAllExceptions): Ditto.
+ (MachProcess::ExceptionMessageReceived): Ditto.
+ (MachProcess::AppendSTDOUT): Ditto.
+ (MachProcess::GetAvailableSTDOUT): Ditto.
+ (MachProcess::ThreadFunctionSTDIO): Renamed from to
+ MachProcess::STDIOThread.
+ (MachProcess::StartSTDIOThread): Improved logging.
+ (MachProcess::CreateBreakpoint): Ditto.
+ (MachProcess::CreateWatchpoint): Ditto.
+ (MachProcess::DisableAllBreakpoints): Ditto.
+ (MachProcess::DisableBreakpoint): Ditto.
+ (MachProcess::DisableWatchpoint): Ditto.
+ (MachProcess::EnableBreakpoint): Ditto.
+ (MachProcess::EnableWatchpoint): Ditto.
+ (MachProcess::LaunchForDebug): Ditto.
+ (MachProcess::PosixSpawnChildForPTraceDebugging): Ditto.
+ (MachProcess::Detach): Reset the running event bit after resuming prior
+ to issuing the SIGSTOP to avoid a pause.
+ (MachProcess::RemoveTrapsFromBuffer): New function that removes
+ breakpoint traps from a memory buffer.
+ (MachProcess::ReadMemory): Read memory from the task, then removes any
+ breakpoint traps prior to returning the buffer.
+ (MachProcess::WriteMemory): Write memory and any needed data to the
+ breakpoint saved opcodes for any software breakpoint traps that are
+ enabled.
+ * MacOSX/MachProcess.h (MachProcess::ThreadFunctionException): Removed.
+ (MachProcess::ThreadFunctionSTDIO): Renamed to MachProcess::STDIOThread().
+ (MachProcess::RemoveTrapsFromBuffer): New function.
+ * MacOSX/MachVMRegion.cpp (MachVMRegion::SetProtections): Improved
+ logging.
+ (MachVMRegion::RestoreProtections): Ditto.
+ (MachVMRegion::GetRegionForAddress): Ditto.
+ * MacOSX/MachException.cpp (catch_mach_exception_raise_state): Improved
+ logging.
+ (catch_mach_exception_raise_state_identity): Ditto.
+ (catch_mach_exception_raise): Ditto.
+ (MachException::Message::Dump): Ditto.
+ (MachException::Data::GetStopInfo): Ditto.
+ (MachException::Message::Receive): Ditto.
+ (MachException::Message::Reply): Ditto.
+ (MachException::Data::Dump): Ditto.
+ (MachException::PortInfo::Save): Ditto.
+ (MachException::PortInfo::Restore): Ditto.
+ * MacOSX/MachTask.cpp (MachTask::Suspend): Improved logging.
+ (MachTask::Resume): Ditto.
+ (MachTask::ReadMemory): Ditto.
+ (MachTask::WriteMemory): Ditto.
+ (MachTask::TaskPortForProcessID): Ditto.
+ (MachTask::BasicInfo): Ditto.
+ (MachTask::StartExceptionThread): Ditto.
+ (MachTask::ShutDownExcecptionThread): Ditto and use pthread_cancel to
+ interrupt the exception thread.
+ (MachTask::ExceptionThread): Ditto and revert back to infinite timeout
+ as pthread_cancel will break us out of infinite mach_msg receive calls.
+ * MacOSX/MachThreadList.cpp (MachThreadList::UpdateThreadList): Improved
+ logging.
+ (MachThreadList::CurrentThread): Use new PTHREAD_MUTEX_LOCKER macro to
+ ease debugging of deadlocks.
+ * DNBTimer.h (DNBTimer::DNBTimer): Initialize the mutex with a recursive
+ pthread.
+ (DNBTimer::Reset): Use new PTHREAD_MUTEX_LOCKER macro to ease debugging
+ of deadlocks.
+ (DNBTimer::TotalMicroSeconds): Ditto.
+ (DNBTimer::GetTime): Ditto.
+ (DNBTimer::ElapsedMicroSeconds): Ditto.
+ (DNBTimer::GetTimeOfDay): New class function.
+ * DNBError.cpp (DNBError::LogThreaded): Improved logging.
+ * test-dbgnub.cpp
+ * PThreadMutex.h: Added the ability to debug deadlocks by defining
+ DEBUG_PTHREAD_MUTEX_DEADLOCKS.
+ * FunctionProfiler.cpp
+ * PThreadEvent.cpp (PThreadEvent::NewEventBit): Use new
+ PTHREAD_MUTEX_LOCKER macro to ease debugging of deadlocks.
+ (PThreadEvent::FreeEventBits): Ditto.
+ (PThreadEvent::GetEventBits): Ditto.
+ (PThreadEvent::ReplaceEventBits): Ditto.
+ (PThreadEvent::SetEvents): Ditto.
+ (PThreadEvent::ResetEvents): Ditto.
+ (PThreadEvent::WaitForSetEvents): Ditto.
+ (PThreadEvent::WaitForEventsToReset): Ditto.
+
+2008-12-05 Greg Clayton <gclayton@apple.com>
+
+ * DNBDefs.h (LOG_TASK): New log bit.
+ * DNB.cpp (DNBProcessIsAlive): User newly abstracted MachTask class.
+ (DNBProcessMemoryRead): Ditto.
+ (DNBProcessMemoryWrite): Ditto.
+ * DNBArchImpl.cpp (DNBArchMachARM::EnableHardwareSingleStep): Ditto.
+ (DNBArchMachARM::SetSingleStepSoftwareBreakpoints) Ditto.
+ * MachException.cpp (MachException::Message::Receive): Cleaned up logging
+ so it doesn't always log timeout errors.
+ (MachException::Message::Reply): Use abstracted MachTask class for any
+ task related queries.
+ (MachException::PortInfo::Save): Cleaned up logging.
+ (MachException::PortInfo::Restore): Cleaned up logging and now return an
+ error instead of the number of restored port infos.
+ * MachProcess.cpp (class MachProcess): Abstracted out all of the task_t
+ related stuff (suspend, resume, exception ports, exception thread, and
+ more) into a new class MachTask.
+ (MachProcess::Task): Now returns a reference to a MachTask class.
+ (MachProcess::Clear): Uses new abstracted MachTask class.
+ (MachProcess::Detach): Ditto.
+ (MachProcess::PrivateResume): Ditto.
+ (MachProcess::DisableBreakpoint): Ditto.
+ (MachProcess::ExceptionMessageReceived): Ditto.
+ (MachProcess::ExceptionMessageBundleComplete): Ditto.
+ (MachProcess::AttachForDebug): Ditto.
+ (MachProcess::LaunchForDebug): Ditto.
+ (MachProcess::SBLaunchForDebug): Ditto.
+ (MachProcess::TaskIsValid): Removed (replaced by similar functionality
+ in the new MachTask class).
+ (MachProcess::ExceptionPort): Ditto.
+ (MachProcess::ExceptionPortIsValid): Ditto.
+ (MachProcess::StartExceptionThread): Ditto.
+ (MachProcess::Suspend): Ditto.
+ (MachProcess::TaskResume): Ditto.
+ (MachProcess::TaskBasicInfo): Ditto.
+ (MachProcess::TaskBasicInfo): Ditto.
+ (MachProcess::ReadMemory): Ditto.
+ (MachProcess::WriteMemory): Ditto.
+ (MachProcess::ThreadFunctionException): Ditto.
+
+2008-12-04 Greg Clayton <gclayton@apple.com>
+
+ * DNB.h (DNBProcessSetEvents): New API function prototype.
+ * DNB.cpp (DNBProcessSetEvents): New API function.
+ (DNBProcessHalt): Send our process a SIGINT instead of suspending
+ the task.
+ * DNBDefs.h (NUB_STATE_IS_STOPPED): Removed up duplicate entry in macro.
+ (eEventPrcoessAsyncInterrupt): New prcoess event bit that allows async
+ interrupting of infinite DNBProcessWaitForEvent() function calls.
+ * MachException.cpp (MachException::Message::Receive): Improved logging.
+ (MachException::Message::Reply): Improved logging.
+ * MachProcess.h (MachProcess::TaskBasicInfo): New member and static
+ functions.
+ * MachProcess.cpp (MachProcess::TaskIsValid): Use new TaskBasicInfo()
+ member function.
+ (MachProcess::Resume): Removed the detach parameter from the PrivateResume()
+ function call.
+ (MachProcess::Kill): Added a absolute timeout pointer to allow callers to
+ wait for the signal to be received if the timeout is non-NULL.
+ (MachProcess::TaskBasicInfo): New member and static function.
+ (MachProcess::TaskResume): New function that resumes the task by making sure
+ the suspend count is correctly ref counted.
+ (MachProcess::Detach): When detaching from a process make sure it is
+ stopped (SIGSTOP) first, then we can successfully detach. The exception
+ thread now also properly exits.
+ (MachProcess::PrivateResume): Call new TaskResume function, and removed the
+ detach functionality.
+ (MachProcess::DisableBreakpoint): Only notify the thread list that a
+ breakpoint has changed if the breakpoint is going to be removed.
+ (MachProcess::ThreadFunctionException): Added a permanent 1 second timeout
+ for each call to mach_msg() so we can exit the thread in the event that
+ we detach from a process/task.
+ * test-debugnub (main): Modified to show an example of how to detach using
+ a signal_handler to asynchronously receive a SIGINT and properly interrupt
+ and detach from a running process.
+
+2008-11-26 Greg Clayton <gclayton@apple.com>
+
+ * DNBDefs.h (LOG_STEP): New logging define.
+ * DNBError.cpp (DNBError::LogThreaded): If there is no error, then
+ log with "success: " as a prefix instead of "error: ".
+ * arm/DBNArchImpl.cpp (DNBArchMachARM::EnableHardwareSingleStep): Log using
+ new LOG_STEP instead of LOG_BREAKPOINTS.
+ (DNBArchMachARM::SetSingleStepSoftwareBreakpoints): Ditto.
+ * MachException.cpp (MachException::Message::Dump): Log exception header
+ and reply header on two separate lines.
+ * MachProcess.cpp (IsSBProcess): Check for NULL CFArrayRef returned from
+ SBSCopyApplicationDisplayIdentifiers for SkankPhone.
+ (MachProcess::Suspend): Check if process state is not running instead of
+ having to receive an event after a timeout if one is given.
+ (MachProcess::Detach): Deallocate the exception port when detaching and
+ restore the inferior task exception ports prior to clearing and detaching.
+ (MachProcess::PrivateResume): Grab the task's basic info and make sure we
+ get the resume the correct number of times.
+ (MachProcess::DisableBreakpoint): Removed unused variable opcode_restored
+ and make sure the breakpoint is enabled before we start warning that
+ our opcode wasn't there.
+ * ppc/DBNArchImpl.cpp (DNBArchMachPPC::EnableHardwareSingleStep): Log
+ using LOG_STEP instead of LOAD_BREAKPOINTS.
+ * RNBServices.cpp (IsSBProcess): Check for NULL CFArrayRef returned from
+ SBSCopyApplicationDisplayIdentifiers for SkankPhone.
+
+2008-11-26 Greg Clayton <gclayton@apple.com>
+
+ * MachProcess.h (MachProcess::Suspend): Now takes an optional absolute
+ timeout that, if non-NULL, will case the function to return after the
+ process has been suspended and is in a stopped state. If the timeout is
+ NULL, then no waiting will occur.
+ * MachProcess.cpp (MachProcess::Suspend): Ditto.
+ (MachProcess::Detach): Now replies to all exceptions, un-suspends all
+ threads and resumes the task.
+ (MachProcess::ReplyToAllExceptions): New function.
+ (MachProcess::PrivateResume): Now takes an additional parameter named
+ detach that will do the right thing when detaching from a process.
+ * DNBArchImpl.h (DNBArchMachI386::ThreadWillResume): Returns void.
+ * DNBArchImpl.cpp (DNBArchMachI386::ThreadWillResume): Returns void.
+ * RNBServices.cpp (ListApplications): #ifdef-ed for ARM only as it
+ currently uses SpringBoard.
+ (IsSBProcess): Ditto.
+ * test-remotenub.cpp (RNBRunLoopLaunchInferior): #ifdef-ed around
+ ARM parts so it compiles for i386.
+ (main): Ditto.
+
+2008-11-24 Greg Clayton <gclayton@apple.com>
+
+ * DNBArchProtocol.h (DNBArchProtocol::ThreadWillResume): Now returns void.
+ * DNBArchImpl.cpp (DNBArchMachARM::ThreadWillResume): Returns void and
+ has hollowed out support for software single step.
+ (DNBArchMachARM::ThreadDidStop): Has a debug mode that uses hardware single
+ step to verify software single step that can be enabled by defining
+ DNB_ARCH_MACH_ARM_DEBUG_SW_STEP.
+ (DNBArchMachARM::SetSingleStepSoftwareBreakpoints): New function.
+ * DNBArchImpl.h (DNBArchMachARM::ThreadWillResume): Returns void.
+ (DNBArchMachARM::SetSingleStepSoftwareBreakpoints): New prototype.
+ (DNBArchMachARM::m_sw_single_step_next_pc): New member variable.
+ (DNBArchMachARM::m_sw_single_step_break_id): New member variable.
+ * MachThread.cpp (MachThread::ThreadWillResume): Now returns void.
+ * MachThread.h (MachThread::ThreadWillResume): Now returns void.
+
+2008-11-19 Greg Clayton <gclayton@apple.com>
+
+ * DNBError.h (FlavorType): Added SpringBoard error type for arm builds.
+ * DNBError.cpp (DNBError::AsString): Now returns SpringBoard error strings
+ if the error type is SpringBoard.
+ * test-remotenub.cpp (RNBRunLoopLaunchInferior): Set the error into
+ RNBContext as either a POSIX error or a SpringBoard error.
+ * RNBContext.h (m_launch_status): Changed this member to be a DNBError
+ instead of a uint32_t.
+ (RNBContext::LaunchStatus): Now returns a reference to the DNBError object
+ in m_launch_status.
+ * RNBContext.cpp (RNBContext::LaunchStatusAsString): Let DNBError handle
+ any error string descriptions, including SpringBoard errors.
+ * RNBRemote.cpp (RNBRemote::HandlePacket_q): Use new error class in
+ RNBContext.
+ (RNBRemote::HandlePacket_C): Return without an erroneous error when resuming
+ a process with a signal.
+ * DNBArch.h (DNBArchProtocol::StepNotComplete): New protocol function with
+ default return value.
+ * DNBArchImpl.cpp (DNBArchMachARM::StepNotComplete): New function.
+ (DNBArchMachARM::EnableHardwareSingleStep): Handle hardware single stepping
+ over 32 bit thumb instructions better so we always do a true instruction
+ level single step.
+ * MachProcess.cpp (MachProcess::ExceptionMessageBundleComplete): Now resumes
+ if single stepping wasn't able to complete in a single run.
+ * MachThread.cpp (MachThread::ShouldStop): Fills in new step_more parameter
+ if stepping is not complete.
+ * MachThreadList.cpp (MachThreadList::ShouldStop): Pass step_more parameter
+ to each MachThread::ShouldStop call.
+
+2008-11-13 Greg Clayton <gclayton@apple.com>
+
+ * MachProcess.cpp (MachProcess::PosixSpawnChildForPTraceDebugging): Don't
+ call posix_spawnattr_setbinpref_np when launching with posix_spawn on ARM
+ targets as it currently selects the incorrect slice due to multiple slices
+ that contain the same cputype, yet they all have differing cpusubtypes.
+
+2008-11-04 Greg Clayton <gclayton@apple.com>
+
+ * RNBRemote.h (GetContinueThread): Don't return the current thread when
+ the continue thread is zero or -1.
+ * RNBRemote.cpp (RNBRemote::HandlePacket_c): Resume the process if we
+ have no continue thread set.
+ (RNBRemote::HandlePacket_s): Ditto.
+ (RNBRemote::HandlePacket_C): Ditto unless a continue address is specified
+ in which case we will only succeed if we have one thread when the continue
+ with signal and address doesn't have a continue thread specified.
+ (RNBRemote::HandlePacket_S): Ditto.
+ * DNB.cpp (DNBProcessResumeWithSignal): New function.
+ (DNBProcessResume): Added better logging.
+ (DNBProcessHalt): Ditto.
+ (DNBThreadResume): Ditto.
+ (DNBThreadResumeWithSignal): Ditto.
+ * DNB.h (DNBProcessResumeWithSignal): New prototype.
+ * DNBError.cpp (DNBError::LogThreaded): New function.
+ * DNBError.h (DNBError::LogThreaded): New prototype.
+ * DNBLog.cpp (_DNBLogThreaded): Added sequence ID for threaded logs.
+ (_DNBLogThreadedIf): Ditto.
+ * MachException.cpp (MachException::Data::GetStopInfo): Use new SoftSignal()
+ accessor.
+ (MachException::Data::DumpStopReason): Ditto.
+ (MachException::Message::Reply): Added better logging and log using the
+ soft signal if our task matches that in the exception.
+ (MachException::Data::Dump): Added better logging.
+ * MachException.h (IsSoftSignal): Removed.
+ (SoftSignal): New function that returns the soft signal in the exception
+ data if there is one, or zero otherwise.
+ * MachProcess.cpp (MachProcess::Suspend): Improved logging.
+ (MachProcess::Resume): Ditto.
+ (MachProcess::PrivateResume): Handle the case where the process is told
+ to resume with a signal by matching the signal up to the thread that had
+ the soft signal if no thread id is specified.
+ * MachThread.cpp (MachThread::Suspend): Improved logging.
+ (MachThread::Resume): Improved logging.
+ (MachThread::RestoreSuspendCount): Improved logging.
+ (MachThread::Resume): Improved logging.
+ (MachThread::Dump): Improved logging.
+ * MachThreadList.cpp (MachThreadList::Dump): Improved logging.
+
+2008-10-22 Greg Clayton <gclayton@apple.com>
+
+ * test-remotenub.cpp (RNBRunLoopMode): Added a new enum value
+ eRNBRunLoopModeInferiorAttaching.
+ (g_long_options): Added "--attach=PID" for attaching to existing processes
+ and "--launch=(auto|posix|fork|springboard)" options.
+ (RNBRunLoopLaunchInferior): Now launches process with new
+ nub_launch_flavor_t enum that can be overridden with the --launch option.
+ (RNBRunLoopLaunchAttaching): New function for attaching to existing
+ processes.
+ (main): Added command line option support for the "--attach" and "--launch"
+ options and added attach to pid support and better logging.
+ * DNB.cpp/h: (DNBProcessLaunch): Added nub_launch_flavor_t and error
+ parameter for more precise control when launching processes.
+ (DNBProcessSBLaunch): Removed function as launching with SpringBoard can
+ now be done using DNBProcessLaunch with launch_flavor being set to
+ eLaunchTypeSpringBoard (arm only).
+ (DNBProcessSBAttach): Removed function (SpringBoard processes are now auto
+ detected in the MachProcess::AttachForDebug function on ARM).
+ * DNBDefs.h (NUB_GENERIC_ERROR): New generic error definition.
+ (nub_launch_flavor_t): New enumeration used for control over process
+ launching.
+ * MachProcess.cpp (IsSBProcess): New function.
+ (MachProcess::AttachForDebug): Removed flags parameter that was being used
+ for SpringBoard flags and we now detect if a process belongs to SpringBoard
+ by calling IsSBProcess.
+ (MachProcess::LaunchForDebug): Now has launch parameter that tells it how
+ to launch the inferior process and there is also an error code that gets
+ returned. This function can now launch using fork + exec, posix_spawn,
+ or SpringBoard on ARM targets.
+ (MachProcess::SBLaunchForDebug): Now uses DNBError reference instead of
+ uint32_t pointer for the error code.
+ (MachProcess::SBForkChildForPTraceDebugging): Ditto.
+
+2008-10-22 Greg Clayton <gclayton@apple.com>
+
+ * MacOSX/arm/DNBArchImpl.cpp (DNBArchMachARM::GetRegisterValue): Set
+ register value to a uint32 value instead of a float64 value for s0 -
+ s31.
+
+2008-10-17 Greg Clayton <gclayton@apple.com>
+
+ * test-remotenub.cpp (RNBRunLoopLaunchInferior): Don't listen for
+ the qLaunchSuccess if we aren't doing a lockdown connnection.
+
+2008-10-13 Greg Clayton <gclayton@apple.com>
+
+ * RNBRemote.h (class RNBRemote): Added m_watchpoints member.
+ * DNB.cpp (DNBBreakpointSet): Added boolean hardware parameter for
+ requesting that a hardware breakpoint be set.
+ (DNBWatchpointSet): New function.
+ (DNBWatchpointClear): New function.
+ (DNBWatchpointGetHitCount): New function.
+ (DNBWatchpointGetIgnoreCount): New function.
+ (DNBWatchpointSetIgnoreCount): New function.
+ (DNBWatchpointSetCallback): New function.
+ (DNBWatchpointPrint): New function.
+ * DNBRegisterInfo.cpp (DNBRegisterValueClass::Dump): Modified to emit
+ a single DNBLog() call so there aren't multiple newlines when logging
+ to ASL.
+ * RNBContext.cpp (RNBContext::ThreadFunctionProcessStatus): Use new
+ process state changed events.
+ * DNBBreakpoint.h (class DNBBreakpoint): Removed m_state member and
+ added m_tid, m_enabled, m_hw_preferred, m_is_watchpoint, m_watch_read,
+ m_watch_write, and m_hw_index.
+ (DNBBreakpoint::ThreadID()): New accessor.
+ (DNBBreakpoint::IsEnabled()): New accessor.
+ (DNBBreakpoint::SetEnabled()): New accessor.
+ (DNBBreakpoint::IsWatchpoint()): New accessor.
+ (DNBBreakpoint::IsBreakpoint()): New accessor.
+ (DNBBreakpoint::SetIsWatchpoint()): New accessor.
+ (DNBBreakpoint::WatchpointRead()): New accessor.
+ (DNBBreakpoint::WatchpointWrite()): New accessor.
+ (DNBBreakpoint::HardwarePreferred()): New accessor.
+ (DNBBreakpoint::IsHardware()): New accessor.
+ (DNBBreakpoint::GetHardwareIndex()): New accessor.
+ (DNBBreakpoint::SetHardwareIndex()): New accessor.
+ (DNBBreakpoint::ThreadID()): New accessor.
+ (DNBBreakpoint::GetState()): Removed accessor.
+ (DNBBreakpoint::SetState()): Removed accessor.
+ (DNBBreakpoint::AddBreakpoint()): Renamed to Add().
+ (DNBBreakpoint::RemoveBreakpoint()): Renamed to Remove().
+ (DNBBreakpoint::FindBreakIDForAddress()): Renamed to FindIDByAddress().
+ (DNBBreakpoint::ShouldStopAtBreakpoint()): Renamed to ShouldStop().
+ (DNBBreakpoint::SetBreakpointCallback()): Renamed to SetCallback().
+ (DNBBreakpoint::FindBreakpointWithAddress()): Renamed to
+ FindByAddress().
+ (DNBBreakpoint::FindBreakpointWithBreakID()): Renamed to FindByID().
+ (DNBBreakpoint::GetBreakpointAtIndex()): Renamed to GetByIndex().
+ * FunctionProfiler.h: New header for subclass of DNBRuntimeAction.
+ * RNBRemote.cpp (RNBRemote::HandlePacket_v): Use new process state
+ changed events.
+ (RNBRemote::HandlePacket_z): Implement the hardware breakpoint and
+ watchpoint commands z1, Z1, z2, Z2, z3 and Z3
+ * PThreadEvent.h (PThreadEvent::GetEventBits): Made member function
+ const.
+ (PThreadEvent::WaitForSetEvents): Ditto.
+ (PThreadEvent::WaitForEventsToReset): Ditto.
+ (PThreadEvent::WaitForResetAck): Ditto.
+ (PThreadEvent::m_mutex): Made class member mutable.
+ (PThreadEvent::m_set_condition): Made class member mutable.
+ (PThreadEvent::m_reset_condition): New mutable class member.
+ * ProfileObjectiveC.cpp
+ * DNBArch.h (DNBArch::NotifyException): Now has default implementation
+ that returns false.
+ (DNBArch::NumSupportedHardwareBreakpoints): New virtual member
+ function with a default implementation.
+ (DNBArch::NumSupportedHardwareWatchpoints): Ditto.
+ (DNBArch::EnableHardwareBreakpoint): Ditto.
+ (DNBArch::EnableHardwareWatchpoint): Ditto.
+ (DNBArch::DisableHardwareBreakpoint): Ditto.
+ (DNBArch::DisableHardwareWatchpoint): Ditto.
+ * DNB.h (DNBBreakpointSet): New take a HARDWARE parameter that allows
+ requests for setting hardware breakpoints.
+ (DNBWatchpointSet): New function prototype.
+ (DNBWatchpointClear): New function prototype.
+ (DNBWatchpointGetHitCount): New function prototype.
+ (DNBWatchpointGetIgnoreCount): New function prototype.
+ (DNBWatchpointSetIgnoreCount): New function prototype.
+ (DNBWatchpointSetCallback): New function prototype.
+ (DNBWatchpointPrint): New function prototype.
+ * MacOSX/arm/DNBArchImpl.cpp: Added hardware breakpoint and watchpoint
+ support for ARM.
+ (DNBArchMachARM::GetCPUType): New function.
+ (DNBArchMachARM::DumpDBGState): New function.
+ (DNBArchMachARM::GetDBGState): New function.
+ (DNBArchMachARM::SetDBGState): New function.
+ (DNBArchMachARM::EnableHardwareSingleStep): New function.
+ (DNBArchMachARM::EnableHardwareBreakpoint): New function.
+ (DNBArchMachARM::NotifyException): Removed.
+ (DNBArchMachARM::DisableHardwareBreakpoint): New function.
+ (DNBArchMachARM::EnableHardwareWatchpoint): New function.
+ (DNBArchMachARM::DisableHardwareWatchpoint): New function.
+ * MacOSX/MachThread.cpp (MachThread::Suspend): Added better logging.
+ (MachThread::Resume): Ditto.
+ (MachThread::RestoreSuspendCount): Ditto.
+ (MachThread::Dump): Ditto.
+ (MachThread::EnableHardwareBreakpoint): New function.
+ (MachThread::EnableHardwareWatchpoint): New function.
+ (MachThread::DisableHardwareBreakpoint): New function.
+ (MachThread::DisableHardwareWatchpoint): New function.
+ * MacOSX/MachThreadList.h (MachThreadList::GetLastError): Removed.
+ (MachThread::EnableHardwareBreakpoint): New prototype.
+ (MachThread::DisableHardwareBreakpoint): New prototype.
+ (MachThread::EnableHardwareWatchpoint): New prototype.
+ (MachThread::DisableHardwareWatchpoint): New prototype.
+ (class MachThread): Remove m_err member variable.
+ * MacOSX/ppc/DNBArchImpl.cpp (DNBArchMachPPC::GetCPUType) New
+ function.
+ (DNBArchMachPPC::NotifyException): Removed.
+ * MacOSX/ppc/DNBArchImpl.h (DNBArchMachPPC::NotifyException): Removed.
+ * MacOSX/MachThread.h (MachThread::EnableHardwareBreakpoint): New
+ prototype.
+ (MachThread::EnableHardwareWatchpoint): New prototype.
+ (MachThread::DisableHardwareBreakpoint): New prototype.
+ (MachThread::DisableHardwareWatchpoint): New prototype.
+ (class MachThread): Renambed class member m_exception to
+ m_stop_exception.
+ * MacOSX/MachProcess.cpp (MachProcess::SetState): Updated to use new
+ process event enumerations.
+ (MachProcess::PrivateResume): Added better logging.
+ (MachProcess::CreateBreakpoint): Added bool HARDWARE parameter for
+ requesting hardware breakpoints.
+ (MachProcess::CreateWatchpoint): New function.
+ (MachProcess::DisableAllWatchpoints): New function.
+ (MachProcess::DisableWatchpoint): New function.
+ (MachProcess::DumpWatchpoint): New function.
+ (MachProcess::EnableBreakpoint): Enabled breakpoints in hardware if
+ requested and supported.
+ (MachProcess::DisableBreakpoint): Disable hardware breakpoints if that
+ is how they were set.
+ (MachProcess::EnableWatchpoint): New function.
+ (MachProcess::ExceptionMessageBundleComplete): Wait for the
+ eEventProcessRunningStateChanged event to be reset before changing
+ state to stopped to avoid race condition with very fast start/stops.
+ (MachProcess::LaunchForDebug): Added posix_spawn support.
+ (MachProcess::PosixSpawnChildForPTraceDebugging): New function.
+ * MacOSX/i386/DNBArchImpl.cpp (DNBArchMachI386::GetCPUType): New
+ function.
+ * MacOSX/i386/DNBArchImpl.h (DNBArchMachI386::GetCPUType): New
+ prototype.
+ * MacOSX/MachProcess.h (PosixSpawnChildForPTraceDebugging): New
+ prototype.
+ * MacOSX/MachException.cpp (class MachException::ThreadMessage):
+ Renamed class to MachException::Data.
+ * MacOSX/MachThreadList.cpp (class MachThreadList): Removed m_err
+ class member.
+ (MachThreadList::EnableHardwareBreakpoint): New function.
+ (MachThreadList::DisableHardwareBreakpoint): New function.
+ (MachThreadList::EnableHardwareWatchpoint): New function.
+ (MachThreadList::DisableHardwareWatchpoint): New function.
+ * MacOSX/MachException.h (class MachException::ThreadMessage):
+ Renamed class to MachException::Data.
+ * DNBDefs.h (nub_watch_t): New typedef.
+ (INVALID_NUB_HW_INDEX): New macro definition.
+ (WATCH_TYPE_READ): New macro definition.
+ (WATCH_TYPE_WRITE): New macro definition.
+ (NUB_STATE_IS_RUNNING): New macro to see if state is a running state.
+ (NUB_STATE_IS_STOPPED): New macro to see if state is a stopped state.
+ (eEventProcessStateChanged): Deprecated.
+ (eEventProcessRunningStateChanged): New process event state.
+ (eEventProcessStoppedStateChanged): New process event state.
+ (LOG_WATCHPOINTS): New macro definition for logging watchpoints.
+ * test-remotenub.cpp (RNBRunLoopLaunchInferior): Use new process
+ event states.
+ * FunctionProfiler.cpp: New class that allows single stepping through
+ an address range for tracing exact call graphs.
+
+2008-09-22 Greg Clayton <gclayton@apple.com>
+
+ * RNBRemote.h (GetContinueThread): If the continue thread is zero or
+ -1 then return GetCurrentThread().
+ * RNBRemote.cpp (m_packets): Made the vCont functions call
+ RNBRemote::HandlePacket_v().
+ (RNBRemote::HandlePacket_H): Cleaned up whitespace.
+ (RNBRemote::HandlePacket_last_signal): Return actual signal values for
+ EXE_SOFTWARE/EXC_SOFT_SIGNAL mach exceptions.
+ (RNBRemote::HandlePacket_v): Implemented the 'vCont?' and 'vCont;'
+ packets.
+ (RNBRemote::HandlePacket_c): Handle the case where an address is
+ provided.
+ (RNBRemote::HandlePacket_C): Implemented the continue with signal
+ including when an address is provided.
+ (RNBRemote::HandlePacket_S): Implemented the step with signal
+ including when an address is provided.
+ * DNB.cpp (DNBProcessResume): Pass 0 as the signal when resuming
+ a process without specifying a thread.
+ (DNBThreadResume): Pass 0 as the signal when resuming a specific thread.
+ (DNBThreadResumeWithSignal): New function.
+ * DNB.h (DNBThreadResumeWithSignal): New prototype.
+ * MachException.h (MachException::Message::Reply): Added a signal
+ parameter.
+ * MachException.cpp (MachException::Message::Reply): Update the thread
+ with the new SIGNAL parameter instead of always zero so signals can be
+ passed on to programs.
+ * MachProcess.h (MachProcess::Resume): Added a signal parameter.
+ * MachProcess.h (MachProcess::PrivateResume): Added a signal parameter.
+ * MachProcess.cpp (MachProcess::Resume): Pass new SIGNAL parameter to
+ MachProcess::PrivateResume.
+ * MachProcess.cpp (MachProcess::PrivateResume): Pass new SIGNAL
+ parameter to the mach exception reply.
+
+2008-08-08 Greg Clayton <gclayton@apple.com>
+
+ * DNB.cpp (gProcessMap): Removed static C++ global.
+ (GetProcessMap): New Function.
+ (AddProcessToMap): New function.
+ (RemoveProcessFromMap): New function.
+ (GetProcessSP): Use new GetProcessMap function to get process list.
+
+2008-07-30 Greg Clayton <gclayton@apple.com>
+
+ * debugserver-entitlements.plist (get-task-allow): Removed.
+ (run-invalid-allow): Added boolean value set to TRUE.
+
+2008-04-18 Greg Clayton <gclayton@apple.com>
+
+ * MachProcess.cpp (MachProcess::Task): Added getuid(), geteuid(),
+ getgid(), getegid() to the log message if task for pid fails.
+
+2008-04-07 Greg Clayton <gclayton@apple.com>
+
+ * RNBContext.cpp (RNBContext::LaunchStatusAsString): Removed unused
+ tmp_str variable.
+
+2008-04-04 Greg Clayton <gclayton@apple.com>
+
+ * CFString.cpp/h (UTF8): Made a static function that can convert
+ a CFStringRef to UTF8.
+
+2008-04-04 Greg Clayton <gclayton@apple.com>
+
+ * test-remotenub.cpp (main): Make sure we exit after we send the
+ application list.
+
+2008-04-04 Greg Clayton <gclayton@apple.com>
+
+ * RNBServices.h (IsSBProcess): New prototype;
+ * RNBServices.cpp (IsSBProcess): New function that returns true it
+ SpringBoard owns or knows about the process.
+ * RNBRemote.cpp (RNBRemote::HandlePacket_v): Made attach work correctly.
+ * DNB.cpp (DNBProcessSBAttach): New function for use when attaching to
+ a process owned by SpringBoard.
+ (DNBProcessAttach): Fixed an issue where a local was shadowing a
+ parameter.
+ * DNB.h (DNBProcessSBAttach): New prototype.
+ * MachProcess.cpp (MachProcess::AttachForDebug): AttachForDebug now
+ takes some flags so it knows to enable SpringBoard functionality.
+ * MachProcess.h (MachProcess::AttachForDebug): Added flags parameter
+ to prototype.
+
+2008-04-04 Greg Clayton <gclayton@apple.com>
+
+ * test-remotenub.cpp (RNBRunLoopGetArgsFromRemote): Handle the new
+ attach packet and watch for connection being lost.
+ (main): handle the --applist option when there we aren't using lockdown
+ by printing the results to stdout and exiting with appropriate error code
+ if we failed. Also handle the new prototype for ListApplications.
+ * RNBServices.h (ListApplications): Change first parameter to be a std::string
+ that will get the contents of the plist so we can use this for more than
+ just lockdown.
+ * RNBServices.cpp (ListApplications): Change first parameter to be a std::string
+ that will get the contents of the plist so we can use this for more than
+ just lockdown and also fixed the logic so we actually create a full list of
+ applications instead of just overwriting the first entry.
+ * RNBRemote.h (PacketEnum): Added a new 'vattach' enum for the "vAttach;PID"
+ gdb remote command.
+ (RNBRemote::HandlePacket_v): New prototype;
+ * RNBRemote.cpp (RNBRemote::CreatePacketTable): add the vattach packet definition
+ to m_packets.
+ (RNBRemote::HandlePacket_v): New function that handles attach to a process.
+
+2008-04-03 Jim Ingham <jingham@apple.com>
+
+ * RNBRemote.h: Add query_launch_success to packet enum.
+ * RNBRemote.cpp (RNBRemote::CreatePacketTable_): Add query_launch_success.
+ (HandlePacket_q): Handle query_launch_success.
+ * DNB.cpp (DNBProcessSBLaunch): Pass in launch_retval.
+ * DNB.h: Change prototype of DNBProcessSBLaunch to take launch_retval.
+ * RNBContext.cpp (RNBContext::LaunchStatusAsString): New function.
+ * RNBContext.h (RNBContext): Add m_launch_status & accessors.
+ * macosx/MachProcess.cpp (MachProcess::SBLaunchForDebug): Pass launch_retval.
+ (MachProcess::SBForkChildForPTraceDebugging): Accept & set launch_retval.
+ * Macosx/MachProcess.h: Change prototypes of SBLaunchForDebug &
+ ForkChildForPTraceDebugging to accept launch_retval.
+ * test-remotenub.cpp (RNBRunLoopLaunchInferior): Get the launch status and
+ put it in the context, then wait for the qLaunchStatus packet.
+
+2008-04-03 Greg Clayton <gclayton@apple.com>
+
+ * com.apple.debugserver.plist: Changed plist so debugserver
+ runs as mobile user.
+ * com.apple.debugserver.applist.plist: Ditto.
+
+2008-04-03 Greg Clayton <gclayton@apple.com>
+
+ * MachProcess.cpp: (MachProcess::SBForkChildForPTraceDebugging):
+ Increased SBS application launch timeout to 30 seconds.
+
+2008-03-27 Christopher Friesen <friesen@apple.com>
+
+ * RNBServices.h: Pass tasks from SpringBoard as a plist
+ * RNBServices.cpp: Ditto.
+ * test-remotenub.cpp: added --applist flag
+ * com.apple.debugserver.applist.plist: Agent plist
+
+2008-03-17 Jim Ingham <jingham@apple.com>
+
+ * DNB.h: Pass envp to DNBProcessLaunch & DNBProcessSBLaunch.
+ * DNB.cpp: Ditto.
+ * MachProcess.h: Ditto for *LaunchForDebug and
+ *ForkChildForPtraceDebugging.
+ * MachProcess.cpp (MachProcess::LaunchForDebug): Pass on envp.
+ (MachProcess::SBLaunchForDebug): Ditto.
+ (MachProcess::ForkChildForPtraceDebugging): Accept envp, haven't actually
+ implemented the passing yet.
+ (MachProcess::SBForkChildForPtraceDebuggin): Accept envp, convert to
+ CFDictionary and pass to SBSLaunchApplication.
+ * RNBContext.h: Add environment to the context.
+ * RBNContext.cpp (RNBContext::EnvironmentAtIndex): New function.
+ * RNBRemote.h: Add set_environment_variable to the PacketEnum.
+ * RNBRemote.cpp (RNBRemote::CreatePacketTable): Add QEnvironment:.
+ * (RNBRemote::HandlePacket_Q): Ingest the environment variable.
+ * test-remotenub.cpp (RNBRunLoppLaunchInferior): Convert the env
+ array in the context into an array, and pass it to the DNBProcess*Launch
+ methods.
+
+2008-03-17 Greg Clayton <gclayton@apple.com>
+
+ * DNBBreakpoint.cpp (DNBBreakpointList::GetBreakpointAtIndex): New
+ functions (const and non-const versions).
+ * DNBBreakpoint.h (DNBBreakpointList::GetBreakpointAtIndex): New
+ prototypes (const and non-const versions).
+ * DNBError.h (DNBError::Success()): Don't use KERN_SUCCESS define.
+ (DNBError::Fail()): Don't use KERN_SUCCESS define.
+ * MachProcess.cpp (MachProcess::DisableAllBreakpoints): New function.
+ (MachProcess::Detach): Added initial implementation that will halt
+ the process, disable all breakpoints and call PT_DETACH.
+ * MachProcess.h (MachProcess::DisableAllBreakpoints): New prototype.
+
+2008-03-04 Greg Clayton <gclayton@apple.com>
+
+ * RNBRemote.h (RNBRemote::SendHexEncodedBytePacket): New prototype.
+ * RNBRemote.cpp (RNBRemote::SendHexEncodedBytePacket): New function.
+ (RNBRemote::SendSTDOUTPacket): Use SendHexEncodedBytePacket function
+ to send bytes.
+ (RNBRemote::SendSTDERRPacket): Ditto.
+ (RNBRemote::HandlePacket_q): Return a valid thread info string for
+ qThreadExtraInfo queries.
+ * DNB.cpp (DNBThreadPrintStopReason): Commented out unused function.
+ (DNBThreadGetInfo): New function.
+ * DNB.h (DNBThreadPrintStopReason): Commented out prototype.
+ (DNBThreadGetInfo): New prototype.
+ * MachProcess.cpp (MachProcess::GetThreadInfo): New function.
+ * MachProcess.h (MachProcess::GetThreadInfo): New prototype.
+ * MachThreadList.cpp (MachThreadList::GetThreadInfo): New function.
+ * MachThreadList.h (MachThreadList::GetThreadInfo): New prototype.
+ * MachThread.cpp (MachThread::GetBasicInfoAsString): New function.
+ (MachThread::InferiorThreadID): New function.
+ * MachThread.cpp (MachThread::GetBasicInfoAsString): New prototype.
+ (MachThread::InferiorThreadID): New prototype.
+
+2008-02-27 Greg Clayton <gclayton@apple.com>
+
+ * RNBRemote.cpp (RNBRemote::HandlePacket_last_signal): Set the
+ current thread when we notify a thread has stopped to subsequent
+ g and p packets get the correct data.
+
+2008-02-26 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.h: Add query_thread_extra_info enum.
+ * RNBRemote.cpp: Add support for qThreadExtraInfo.
+ Currently we return 'Ok' as the packet status for
+ every thread.
+
+2008-02-26 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.cpp (RNBRemote::HandlePacket_q): Correct handling
+ of qfThreadInfo/qsThreadInfo.
+
+2008-02-20 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.h: Change default for gdb's max incoming packet size to
+ reflect the real default size.
+ * RNBRemote.cpp (HandlePacket_Q): Correct the string comparisons for
+ the QSetMaxPayloadSize and QSetMaxPacketSize packets.
+
+2008-02-19 Christopher Friesen <friesen@apple.com>
+
+ * CFDataFormatters.c: CoreFoundation data formatters added to project.
+
+2008-02-19 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.h: Record the max payload size, not the max packet
+ size for less ambiguous meaning.
+ * RNBRemote.cpp: Add support for QSetMaxPayloadSize: packet which
+ should have a clearer meaning than QSetMaxPacketSize.
+ QSetMaxPacketSize will be removed once we get have a chance to get
+ a new debugserver and gdb submitted.
+
+2008-02-18 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.h: Make default size 1024.
+ * RNBRemote.cpp: Questionmark packet should stay under
+ max_packet_size - 5 to allow for start, end, checksum and nul
+ char bytes.
+
+2008-02-18 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.h: Add m_max_packet_size to class defn.
+ * RNBRemote.cpp: Initialize it, use it.
+
+2008-02-18 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.h: Add set_max_packet_size.
+ * RNBRemote.cpp: Add QSetMaxPacketSize packet handling.
+
+2008-02-18 Greg Clayton <gclayton@apple.com>
+
+ * test-remotenub.cpp (HandleProcessStateChange): Call new
+ RNBRemote::FlushSTDIO function.
+ (RNBRunLoopInferiorExecuting): Ditto.
+ * RNBRemote.h (RNBRemote::FlushSTDIO): New prototype.
+ * RNBRemote.cpp (RNBRemote::FlushSTDIO): New function to
+ centralize the stdio.
+
+2008-02-18 Greg Clayton <gclayton@apple.com>
+
+ * DNB.cpp (DNBProcessWaitForEvent): Added timeout pointer as
+ parameter that can be NULL for infinite timeout to simplify
+ the DNB interface.
+ (DNBProcessTimedWaitForEvent): Removed function.
+ * DNB.h (DNBProcessWaitForEvent): Added timeout argument.
+ (DNBProcessTimedWaitForEvent): Removed prototype.
+ * DNBTimer.h (DNBTimer::OffsetTimeOfDay): New function.
+ * CFString.cpp (CFString::GetLength() const): New function.
+ * CFString.h (CFString::GetLength() const): New prototype.
+ * MachProcess.h (MachProcess class): Removed m_attached and
+ added m_flags.
+ * MachProcess.cpp (MachProcess::AttachForDebug): Set m_flags
+ to indicate we attached.
+ (MachProcess::SBLaunchForDebug): Set m_flags to indicate we
+ attached using SpringBoard and that we attached.
+ (MachProcess::SBForkChildForPTraceDebugging): Changed to new
+ SpringBoardServices API.
+ (MachProcess::ThreadFunctionException): Added code that will
+ renew a watchdog assertion when we launch apps through
+ SpringBoardServices.
+ * PThreadEvent.cpp (PThreadEvent::WaitForSetEvents): Simplified
+ PThreadEvent API to have only one version of WaitForSetEvents
+ that has an optional timeout pointer argument.
+ * RNBContext.cpp (RNBContext::StopProcessStatusThread): Adapt
+ to new PThreadEvent API changes.
+ (RNBContext::ThreadFunctionProcessStatus): Adapt to new
+ DNBProcessWaitForEvent API changes.
+ * RNBRemote.cpp (RNBRemote::StopReadRemoteDataThread): Adapt
+ to new PThreadEvent API changes.
+ * test-remotenub.cpp (RNBRunLoopLaunchInferior): Adapt to new
+ DNBProcessWaitForEvent API changes.
+ (RNBRunLoopInferiorExecuting): Process STDIO first, then
+ incoming packets.
+
+2008-02-14 Jason Molenda (jmolenda@apple.com)
+
+ * MachProcess.cpp: (MachProcess::SBForkChildForPTraceDebugging):
+ Set mode bits on slave side of pty.
+
+2008-02-12 Greg Clayton <gclayton@apple.com>
+
+ * DNB.cpp (DNBEnableLogging): Removed function.
+ (DNBThreadPrintStopReason): Removed the file handle from this
+ function and use DNBLog calls.
+ * DNB.h (DNBEnableLogging): Removed function prototype.
+ (DNBThreadPrintStopReason): Removed the file handle
+ from the function prototype in favor of using DNBLog calls.
+ * DNBDataRef.cpp (DNBDataRef::Dump): Removed file handle to use
+ DNBLog for the logging and print a log line each time a full line
+ is ready for output after caching it in a local buffer.
+ * DNBDataRef.cpp (DNBDataRef::Dump): Removed file handle from
+ prototype.
+ * DNBDefs.h (DNBCallbackLog): New callback prototype for all
+ logging.
+ DNBLog.cpp(g_debug_opt): Renamed to d_debug and made it a file
+ static.
+ (DNBLogGetDebug): New accessor function for g_debug.
+ (DNBLogSetDebug): New accessor function for g_debug.
+ (g_verbose): Made into a file static and added accessors.
+ (DNBLogGetVerbose): New accessor function for g_verbose.
+ (DNBLogSetVerbose): New accessor function for g_verbose.
+ (DNBLogSetLogCallback): New function call that registers a logging
+ callback for all logging in libdebugnub.dylib and any code that
+ loads it.
+ (DNBLogToASL): Removed function as it is deprecated in favor of
+ using DNBLogSetLogCallback to register a callback function that
+ implements the logging.
+ (DNBLogToFile): Ditto.
+ (DNBLogCloseLogFile): Ditto.
+ (DNBLogToFile): Ditto.
+ (DNBLogToFile): Ditto.
+ (_DNBLogPuts): Removed unused function.
+ (_DNBLogVAPrintf): Calls the callback function to do the logging
+ if one has been registered.
+ * DNBLog.h (DNBLOG_FLAG_FATAL): New defines that get passed to
+ any registered logging callback functions.
+ (DNBLOG_FLAG_FATAL): Ditto.
+ (DNBLOG_FLAG_ERROR): Ditto.
+ (DNBLOG_FLAG_WARNING): Ditto.
+ (DNBLOG_FLAG_DEBUG): Ditto.
+ (DNBLOG_FLAG_VERBOSE): Ditto.
+ (DNBLOG_FLAG_THREADED): Ditto.
+ (DNBLog*): All logging calls are now exported from libdebugnub.dylib
+ so there aren't two copies (one in debugserver and one in debugnub).
+ C99 vararg Macros wrap all logging calls so no var arg processing
+ occurs when logging is disabled.
+ * DNBRegisterInfo.cpp (DNBRegisterValueClass::Dump): Removed file
+ handle and now use DNBLog calls.
+ * DNBRegisterInfo.h (DNBRegisterValueClass::Dump): Removed file
+ handle from prototype.
+ * MachException.cpp (catch_mach_exception_raise_state_identity):
+ Removed newlines from logging call.
+ (catch_mach_exception_raise): Ditto.
+ (MachException::Message::Dump): Removed file handle from params
+ and removed newlines from logging call.
+ (MachException::ThreadMessage::DumpStopReason): Removed file handle
+ from params and use DNBLog for logging output.
+ (MachException::ThreadMessage::Dump): Log using DNBLog instead of
+ file handle.
+ * MachProcess.cpp (MachProcess::DumpThreadStoppedReason): Ditto.
+ (MachProcess::ReadMemory): Ditto.
+ (MachProcess::WriteMemory): Ditto.
+ (ExceptionMessageBundleComplete): Ditto.
+ * MachThread.cpp (MachThread::Dump): Ditto.
+ (MachThread::DumpRegisterState): Ditto.
+ * MachThreadList.cpp (MachThreadList::DumpThreadStoppedReason): Ditto.
+ (MachThreadList::Dump): Ditto.
+ * RNBRemote.cpp (set_logging): Use new function callback registration
+ calls when enabling ASL logging.
+ test-remotenub.cpp (ASLLogCallback): New function to handle all ASL
+ logging. This function gets registered with libdebugnub.dylib when we
+ want to log using ASL.
+ (FileLogCallback): New function to handle all file logging. This
+ function gets registered with libdebugnub.dylib when we want to log
+ to a 'FILE *'.
+ (main): Register the logging callback functions when we want to log
+ to file or using ASL.
+
+2008-02-12 Greg Clayton <gclayton@apple.com>
+
+ * test-remotenub.cpp (main): Default to ASL logging with no log
+ bits set to allow for warning and error logging.
+ * RNBRemote.h (struct Breakpoint): New structure for ref counting
+ breakpoints in Z and z packets.
+ * RNBRemote.cpp (RNBRemote::SendPacket): Use new LOG_RNB_PACKETS
+ defined when logging actual packet content.
+ (RNBRemote::HandleAsyncPacket): Ditto.
+ (RNBRemote::HandleReceivedPacket): Ditto.
+ (RNBRemote::HandlePacket_z): Ref count the setting and removing
+ of breakpoints with the Z and z packets using new struct
+ RNBRemote::Breakpoint.
+ * RNBDefs.h (LOG_RNB_PACKETS): New define for logging the sending
+ and receiving of packets data.
+ * DNB.cpp (DNBPrintf): Check for NULL file handle.
+ * DNBBreakpoint.cpp (DNBBreakpoint::Dump): Ditto.
+ (DNBBreakpointList::Dump): Ditto.
+ * DNBDefs.h (LOG_EVENTS): New define for logging PThreadEvent.
+ * DNBLog.cpp (g_debug_opt): Relocated outside of #if that turns off
+ logging completely to allow option parsing code that uses it to
+ still compile.
+ (g_verbose): Ditto.
+ * DNBLog.h (DNBLogToASL): Added prototype for when logging is
+ disabled via preprocessor macro.
+ (DNBLogToFile): Ditto.
+ * DNBRegisterInfo.cpp (DNBRegisterValueClass::Dump): Check for NULL
+ file handle.
+ * MachException.cpp (MachException::ThreadMessage::DumpStopReason): Ditto.
+ (MachException::ThreadMessage::Dump): Ditto.
+ * MachProcess.cpp (MachProcess::CreateBreakpoint): Improved logging.
+ (MachProcess::DisableBreakpoint): Verify the original opcode gets
+ restored, improved logging and added unconditional logging for when
+ things go wrong.
+ (MachProcess::EnableBreakpoint): Verify the breakpoint opcode gets
+ written, improved logging and added unconditional logging for when
+ things go wrong.
+ * MachThread.cpp (MachThread::Dump): Check for NULL file handle.
+ * MachVMMemory.cpp (MachVMMemory::WriteRegion): Flush caches in inferior
+ after writing to inferior memory.
+ * PThreadEvent.cpp: Changed all logging calls to key off of LOG_EVENTS
+ instead of LOG_VERBOSE.
+ MachDYLD.cpp (MachDYLD::Dump): Check for NULL file handle.
+ (MachDYLD::DYLIBInfo::Dump): Ditto.
+ ProfileObjectiveC.cpp (ProfileObjectiveC::DumpStats): Ditto.
+
+2008-02-09 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.cpp (set_logging): Log to ASL unconditionally when
+ processing a QSetLogging packet.
+
+2008-02-06 Greg Clayton <gclayton@apple.com>
+
+ * test-remotenub.cpp (main): Dup stdout and stderr to /dev/NULL
+ when we use lockdown.
+
+2008-02-06 Greg Clayton <gclayton@apple.com>
+
+ * RNBSocket.cpp (RNBSocket::Disconnect): Removed unused var ERR.
+ * RNBRemote.cpp(RNBRemote::HandlePacket_Q): Removed unused var PID.
+ * DNBError.cpp (DNBError::LogThreadedIfError): Removed unused var
+ ERR_MSG.
+ * test-remotenub.cpp (RNBRunLoopLaunchInferior): Removed unused
+ variable EXECUTABLE_LENGTH.
+ (main): Removed unused variable ARG_IDX.
+
+2008-02-06 Chris Marcellino (cmarcellino@apple.com) and Myke Olson (molson@apple.com)
+
+ * MachProcess.cpp (SBForkChildForPTraceDebugging): Bring up to date with
+ current SpringBoardServices.framework types and imports.
+
+2008-02-05 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.cpp (set_logging): Remove the mode=file and filename=
+ options to the QSetLogging packet. We're only going to support logging
+ to ASL for now. Logging to a file can still be accomplished by the
+ -l command line argument.
+
+2008-02-02 Christopher Friesen (cfriesen@apple.com)
+
+ * Added libXcodeDebugerSupport.dylib target
+ * XCDebuggerIntrospection.[hc]: Support for Xcode's debugger introspection.
+
+2008-02-01 Jason Molenda (jmolenda@apple.com)
+
+ * DNBLog.cpp (DNBLogCloseLogFile): New function to close a logfile
+ at exit.
+ * DNBLog.h: Prototype.
+ * test-remotenub.cpp (main): Close the log file before exiting.
+
+2008-02-01 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.cpp (set_logging): Recognize the "filename=" argument
+ to the QSetLogging directive.
+ * DNBLog.cpp (DNBLogGetLogMask): New fun.c
+ * DNBLog.h: Prototype.
+
+2008-01-31 Jason Molenda (jmolenda@apple.com)
+
+ * DNBLog.cpp: Add ASL logging as a run-time selectable option.
+ (DNBLogToASL, DNBLogToFile): Functions to switch between logging to
+ a file and logging via ASL.
+ * DNBLog.h: Prototypes.
+ * RNBRemote.cpp (set_logging): Recognize the "mode=" field to enable
+ asl logging. Skip unrecognized keys.
+
+2008-01-31 Greg Clayton (gclayton@apple.com)
+
+ * DNB.cpp (sigchld_handler): Better logging when we get a
+ SIGCHILD and we are watching for process related logging events.
+ * test-remotenub.cpp (RNBRunLoopInferiorExecuting): Only reset
+ events when we still have event bits set.
+
+2008-01-29 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.h: Add set_logging_mode.
+ * RNBRemote.cpp (RNBRemote::CreatePacketTable): Recognize
+ QSetLogging.
+
+2008-01-29 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.cpp (set_logging): New function to parse the QSetLogging
+ packet.
+ (RNBRemote::HandlePacket_Q): Call it.
+
+2008-01-28 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.h: Minimal packet size is 1024 in our gdb now.
+ * RNBRemote.cpp: Add the stop_pc value in big-endian order to the
+ T response packet to make it a little easier to follow where gdb
+ is stepping.
+
+2008-01-28 Greg Clayton <gclayton@apple.com>
+
+ * RNBContext.h: Removed m_pid_state from RNBContext class so that
+ it couldn't get out of sync with the actual process and its accessors
+ SetProcessState() and GetProcessState().
+ * RNBContext.cpp (RNBContext::ProcessStateRunning): Always return the
+ current state of the process instead of a cached value.
+ * test-remotenub.cpp (RNBRunLoopLaunchInferior): Remove call to
+ deprecated RNBContext::SetProcessState().
+ (HandleProcessStateChange): Ditto.
+
+2008-01-24 Greg Clayton (gclayton@apple.com)
+
+ * RNBRemote.cpp (RNBRemote::HandlePacket_q): See if command starts with
+ "qSymbol" (no trailing "s") and return the empty string.
+
+2008-01-24 Greg Clayton (gclayton@apple.com)
+
+ * RNBRemote.cpp (RNBRemote::HandlePacket_q): See if command starts with
+ "qSymbols" and return the empty string.
+
+2008-01-24 Greg Clayton (gclayton@apple.com)
+
+ * DNBError.h (DNBError::DumpIfError): Removed prototype.
+ * DNBError.cpp (DNBError::DumpIfError): Removed function.
+ (DNBError::LogThreadedIfError): Output error as hex.
+ * MachException.cpp (MachException::Message::Receive): Don't use
+ DNBError::DumpIfError, now use DNBError::LogThreadedIfError.
+ * MachProcess.cpp (MachProcess::StartExceptionThread): Ditto.
+ (MachProcess::Suspend): Ditto.
+ (MachProcess::SBForkChildForPTraceDebugging): Ditto.
+ * MachVMMemory.cpp (MachVMMemory::Read): Cleaned up logging
+ calls.
+ (MachVMMemory::Write): Ditto.
+ (MachVMMemory::WriteRegion): Added logging.
+ * RNBContenxt.cpp (display_thread_info): Removed function.
+ * RNBRemote.cpp (RNBRemote::GetPacket): Commented out stderr
+ messages to avoid SpringBoard from killing us.
+ (RNBRemote::HandlePacket_p): Ditto.
+ (RNBRemote::HandlePacket_P): Ditto.
+ (RNBRemote::HandlePacket_c): Ditto.
+ (RNBRemote::HandlePacket_A): Removed code that was already
+ * RNBSocket.cpp (RNBSocket::Listen): Commented out stdout
+ messages to avoid SpringBoard from killing us.
+ (RNBSocket::ConnectToService): Ditto.
+
+2008-01-24 Jim Ingham <jingham@apple.com>
+
+ * RNBRemote.cpp (RNBRemote::HandlePacket_q): Reply "" to qSymbols
+ and qOffsets.
+
+2008-01-23 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.h: m_noack_mode to RNBRemote class.
+ * RNBRemote.cpp: Change #ifdef NO_ACKS code blocks
+ to use m_noack_mode instance variable.
+ (RNBRemote::HandlePacket_Q): New function to handle
+ QStartNoAckMode packet and set m_noack_mode appropriately.
+ * test-remotenub.cpp: Remove NO_ACKS ifdefs.
+
+2008-01-22 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.cpp (RNBRemote::CreatePacketTable): Recognize
+ QStartNoAckMode as an unsupported remote protocol request.
+ * RNBRemote.h: Add start_noack_mode enum entry.
+
+2008-01-22 Greg Clayton (gclayton@apple.com)
+
+ * DNBLog.h: Removed C++ namespace for DNBLog (changed all DNBLog::
+ to DNBLog) so C99 var arg macros can be used to completely disable
+ all logging and any functions that may be called when making the
+ variable arguments.
+ * DNBLog.cpp: Ditto.
+ * DNB.cpp: Ditto.
+ * DNBBreakpoint.cpp: Ditto.
+ * DNBError.cpp: Ditto.
+ * MacOSX/MachDYLD.cpp: Ditto.
+ * MacOSX/MachException.cpp: Ditto.
+ * MacOSX/MachProcess.cpp: Ditto.
+ * MacOSX/MachThread.cpp: Ditto.
+ * MacOSX/MachThreadList.cpp: Ditto.
+ * MacOSX/MachVMMemory.cpp: Ditto.
+ * MacOSX/MachVMRegion.cpp: Ditto.
+ * MacOSX/arm/DNBArchImpl.cpp: Ditto.
+ * MacOSX/ppc/DNBArchImpl.cpp: Ditto.
+ * PThreadEvent.cpp: Ditto.
+ * RNBContext.cpp: Ditto.
+ * RNBRemote.cpp: Ditto.
+ * RNBSocket.cpp: Ditto.
+ * test-remotenub.cpp: Ditto.
+
+2008-01-21 Jason Molenda (jmolenda@apple.com)
+
+ * test-remotenub.cpp: Add NO_SPRINGBOARD for turning off SpringBoard
+ dependency ala NO_ACKS.
+
+2008-01-18 Jason Molenda (jmolenda@apple.com)
+
+ * RNBSocket.h (RNBSocket::RNBSocket): Take either a port # or
+ an already-opened socket, with a boolean to indicate which it is.
+ * RNBRemote.cpp (RNBRemote::RNBRemote): Ditto.
+ * RNBRemote.h: Prototype update.
+ * test-remotenub.cpp: Include lockdown.h. Take --lockdown command
+ line arg, get the socket from liblockdown.dylib instead of opening
+ our own socket if it is specified. --lockdown indicates that
+ the program name/args will be provided via remote protocol instead
+ of on the command line.
+
+2008-01-17 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.cpp: Add NO_ACKS #ifdefs around code that computes
+ the checksums and sends/expects the gdb remote protocol ACK packets.
+ If NO_ACKS is defined, debugserver will not send or expect acks.
+ * test-remotenub.cpp (main): Print a different version string
+ if NO_ACKS is defined.
+
+2008-01-16 Greg Clayton (gclayton@apple.com)
+
+ * PThreadEvent.cpp: Added this pointer to all logging calls.
+
+2008-01-16 Greg Clayton (gclayton@apple.com)
+
+ * RNBSocket.cpp (RNBSocket::Connect()): Use TCP so we can try the
+ TCP_NODELAY socket option.
+ (RNBSocket::SetSocketOption()): New function.
+ * RNBSocket.h (RNBSocket::SetSocketOption()): New class function.
+
+2008-01-14 Jason Molenda (jmolenda@apple.com)
+
+ * RNBRemote.cpp (RNBRemote::HandlePacket_last_signal): When printing
+ registers, skip over gdb regs which don't map to DNB regs.
+
+2008-01-14 Jim Ingham <jingham@apple.com>
+
+ * ChangeLog - created.
+ * RBNContext.h: Added m_arg_vec and accessors.
+ * RNBContext.cpp (SetProcessID): New function.
+ * RBNRemote.h: Added packet type to HandlePacket & HandleAsyncPacket
+ * RNBRemote.cpp (HandlePacket, HandleAsyncPacket): Return type.
+ (HandlePacket_A): Fix a few bugs.
+ (HandlePacket_H): Return OK if target is not yet running.
+ (HandlePacket_q): Return PID of 0 if target is not yet running.
+ * testremotenub.cpp (RNBRunLoopGetArgsFromRemote): Implement.
+ (RNBRunLoopLaunchInferior): Fetch arguments from context.
+ (main) Store arguments in context, call RNBRunLoopGetArgsFromRemote
+ if appropriate.
diff --git a/gnu/llvm/lldb/tools/debugserver/source/DNB.cpp b/gnu/llvm/lldb/tools/debugserver/source/DNB.cpp
new file mode 100644
index 00000000000..8d9c691f9d3
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/DNB.cpp
@@ -0,0 +1,1734 @@
+//===-- DNB.cpp -------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 3/23/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DNB.h"
+#include <inttypes.h>
+#include <libproc.h>
+#include <map>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <vector>
+
+#if defined(__APPLE__)
+#include <pthread.h>
+#include <sched.h>
+#endif
+
+#define TRY_KQUEUE 1
+
+#ifdef TRY_KQUEUE
+#include <sys/event.h>
+#include <sys/time.h>
+#ifdef NOTE_EXIT_DETAIL
+#define USE_KQUEUE
+#endif
+#endif
+
+#include "CFBundle.h"
+#include "CFString.h"
+#include "DNBDataRef.h"
+#include "DNBLog.h"
+#include "DNBThreadResumeActions.h"
+#include "DNBTimer.h"
+#include "MacOSX/DarwinLog/DarwinLogCollector.h"
+#include "MacOSX/Genealogy.h"
+#include "MacOSX/MachProcess.h"
+#include "MacOSX/MachTask.h"
+#include "MacOSX/ThreadInfo.h"
+
+typedef std::shared_ptr<MachProcess> MachProcessSP;
+typedef std::map<nub_process_t, MachProcessSP> ProcessMap;
+typedef ProcessMap::iterator ProcessMapIter;
+typedef ProcessMap::const_iterator ProcessMapConstIter;
+
+size_t GetAllInfos(std::vector<struct kinfo_proc> &proc_infos);
+static size_t
+GetAllInfosMatchingName(const char *process_name,
+ std::vector<struct kinfo_proc> &matching_proc_infos);
+
+// A Thread safe singleton to get a process map pointer.
+//
+// Returns a pointer to the existing process map, or a pointer to a
+// newly created process map if CAN_CREATE is non-zero.
+static ProcessMap *GetProcessMap(bool can_create) {
+ static ProcessMap *g_process_map_ptr = NULL;
+
+ if (can_create && g_process_map_ptr == NULL) {
+ static pthread_mutex_t g_process_map_mutex = PTHREAD_MUTEX_INITIALIZER;
+ PTHREAD_MUTEX_LOCKER(locker, &g_process_map_mutex);
+ if (g_process_map_ptr == NULL)
+ g_process_map_ptr = new ProcessMap;
+ }
+ return g_process_map_ptr;
+}
+
+// Add PID to the shared process pointer map.
+//
+// Return non-zero value if we succeed in adding the process to the map.
+// The only time this should fail is if we run out of memory and can't
+// allocate a ProcessMap.
+static nub_bool_t AddProcessToMap(nub_process_t pid, MachProcessSP &procSP) {
+ ProcessMap *process_map = GetProcessMap(true);
+ if (process_map) {
+ process_map->insert(std::make_pair(pid, procSP));
+ return true;
+ }
+ return false;
+}
+
+// Remove the shared pointer for PID from the process map.
+//
+// Returns the number of items removed from the process map.
+// static size_t
+// RemoveProcessFromMap (nub_process_t pid)
+//{
+// ProcessMap* process_map = GetProcessMap(false);
+// if (process_map)
+// {
+// return process_map->erase(pid);
+// }
+// return 0;
+//}
+
+// Get the shared pointer for PID from the existing process map.
+//
+// Returns true if we successfully find a shared pointer to a
+// MachProcess object.
+static nub_bool_t GetProcessSP(nub_process_t pid, MachProcessSP &procSP) {
+ ProcessMap *process_map = GetProcessMap(false);
+ if (process_map != NULL) {
+ ProcessMapIter pos = process_map->find(pid);
+ if (pos != process_map->end()) {
+ procSP = pos->second;
+ return true;
+ }
+ }
+ procSP.reset();
+ return false;
+}
+
+#ifdef USE_KQUEUE
+void *kqueue_thread(void *arg) {
+ int kq_id = (int)(intptr_t)arg;
+
+#if defined(__APPLE__)
+ pthread_setname_np("kqueue thread");
+#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
+ struct sched_param thread_param;
+ int thread_sched_policy;
+ if (pthread_getschedparam(pthread_self(), &thread_sched_policy,
+ &thread_param) == 0) {
+ thread_param.sched_priority = 47;
+ pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);
+ }
+#endif
+#endif
+
+ struct kevent death_event;
+ while (true) {
+ int n_events = kevent(kq_id, NULL, 0, &death_event, 1, NULL);
+ if (n_events == -1) {
+ if (errno == EINTR)
+ continue;
+ else {
+ DNBLogError("kqueue failed with error: (%d): %s", errno,
+ strerror(errno));
+ return NULL;
+ }
+ } else if (death_event.flags & EV_ERROR) {
+ int error_no = static_cast<int>(death_event.data);
+ const char *error_str = strerror(error_no);
+ if (error_str == NULL)
+ error_str = "Unknown error";
+ DNBLogError("Failed to initialize kqueue event: (%d): %s", error_no,
+ error_str);
+ return NULL;
+ } else {
+ int status;
+ const pid_t pid = (pid_t)death_event.ident;
+ const pid_t child_pid = waitpid(pid, &status, 0);
+
+ bool exited = false;
+ int signal = 0;
+ int exit_status = 0;
+ if (WIFSTOPPED(status)) {
+ signal = WSTOPSIG(status);
+ DNBLogThreadedIf(LOG_PROCESS, "waitpid (%i) -> STOPPED (signal = %i)",
+ child_pid, signal);
+ } else if (WIFEXITED(status)) {
+ exit_status = WEXITSTATUS(status);
+ exited = true;
+ DNBLogThreadedIf(LOG_PROCESS, "waitpid (%i) -> EXITED (status = %i)",
+ child_pid, exit_status);
+ } else if (WIFSIGNALED(status)) {
+ signal = WTERMSIG(status);
+ if (child_pid == abs(pid)) {
+ DNBLogThreadedIf(LOG_PROCESS,
+ "waitpid (%i) -> SIGNALED and EXITED (signal = %i)",
+ child_pid, signal);
+ char exit_info[64];
+ ::snprintf(exit_info, sizeof(exit_info),
+ "Terminated due to signal %i", signal);
+ DNBProcessSetExitInfo(child_pid, exit_info);
+ exited = true;
+ exit_status = INT8_MAX;
+ } else {
+ DNBLogThreadedIf(LOG_PROCESS,
+ "waitpid (%i) -> SIGNALED (signal = %i)", child_pid,
+ signal);
+ }
+ }
+
+ if (exited) {
+ if (death_event.data & NOTE_EXIT_MEMORY)
+ DNBProcessSetExitInfo(child_pid, "Terminated due to memory issue");
+ else if (death_event.data & NOTE_EXIT_DECRYPTFAIL)
+ DNBProcessSetExitInfo(child_pid, "Terminated due to decrypt failure");
+ else if (death_event.data & NOTE_EXIT_CSERROR)
+ DNBProcessSetExitInfo(child_pid,
+ "Terminated due to code signing error");
+
+ DNBLogThreadedIf(
+ LOG_PROCESS,
+ "waitpid_process_thread (): setting exit status for pid = %i to %i",
+ child_pid, exit_status);
+ DNBProcessSetExitStatus(child_pid, status);
+ return NULL;
+ }
+ }
+ }
+}
+
+static bool spawn_kqueue_thread(pid_t pid) {
+ pthread_t thread;
+ int kq_id;
+
+ kq_id = kqueue();
+ if (kq_id == -1) {
+ DNBLogError("Could not get kqueue for pid = %i.", pid);
+ return false;
+ }
+
+ struct kevent reg_event;
+
+ EV_SET(&reg_event, pid, EVFILT_PROC, EV_ADD,
+ NOTE_EXIT | NOTE_EXITSTATUS | NOTE_EXIT_DETAIL, 0, NULL);
+ // Register the event:
+ int result = kevent(kq_id, &reg_event, 1, NULL, 0, NULL);
+ if (result != 0) {
+ DNBLogError(
+ "Failed to register kqueue NOTE_EXIT event for pid %i, error: %d.", pid,
+ result);
+ return false;
+ }
+
+ int ret =
+ ::pthread_create(&thread, NULL, kqueue_thread, (void *)(intptr_t)kq_id);
+
+ // pthread_create returns 0 if successful
+ if (ret == 0) {
+ ::pthread_detach(thread);
+ return true;
+ }
+ return false;
+}
+#endif // #if USE_KQUEUE
+
+static void *waitpid_thread(void *arg) {
+ const pid_t pid = (pid_t)(intptr_t)arg;
+ int status;
+
+#if defined(__APPLE__)
+ pthread_setname_np("waitpid thread");
+#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
+ struct sched_param thread_param;
+ int thread_sched_policy;
+ if (pthread_getschedparam(pthread_self(), &thread_sched_policy,
+ &thread_param) == 0) {
+ thread_param.sched_priority = 47;
+ pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);
+ }
+#endif
+#endif
+
+ while (true) {
+ pid_t child_pid = waitpid(pid, &status, 0);
+ DNBLogThreadedIf(LOG_PROCESS, "waitpid_thread (): waitpid (pid = %i, "
+ "&status, 0) => %i, status = %i, errno = %i",
+ pid, child_pid, status, errno);
+
+ if (child_pid < 0) {
+ if (errno == EINTR)
+ continue;
+ break;
+ } else {
+ if (WIFSTOPPED(status)) {
+ continue;
+ } else // if (WIFEXITED(status) || WIFSIGNALED(status))
+ {
+ DNBLogThreadedIf(
+ LOG_PROCESS,
+ "waitpid_thread (): setting exit status for pid = %i to %i",
+ child_pid, status);
+ DNBProcessSetExitStatus(child_pid, status);
+ return NULL;
+ }
+ }
+ }
+
+ // We should never exit as long as our child process is alive, so if we
+ // do something else went wrong and we should exit...
+ DNBLogThreadedIf(LOG_PROCESS, "waitpid_thread (): main loop exited, setting "
+ "exit status to an invalid value (-1) for pid "
+ "%i",
+ pid);
+ DNBProcessSetExitStatus(pid, -1);
+ return NULL;
+}
+static bool spawn_waitpid_thread(pid_t pid) {
+#ifdef USE_KQUEUE
+ bool success = spawn_kqueue_thread(pid);
+ if (success)
+ return true;
+#endif
+
+ pthread_t thread;
+ int ret =
+ ::pthread_create(&thread, NULL, waitpid_thread, (void *)(intptr_t)pid);
+ // pthread_create returns 0 if successful
+ if (ret == 0) {
+ ::pthread_detach(thread);
+ return true;
+ }
+ return false;
+}
+
+nub_process_t DNBProcessLaunch(
+ const char *path, char const *argv[], const char *envp[],
+ const char *working_directory, // NULL => don't change, non-NULL => set
+ // working directory for inferior to this
+ const char *stdin_path, const char *stdout_path, const char *stderr_path,
+ bool no_stdio, nub_launch_flavor_t launch_flavor, int disable_aslr,
+ const char *event_data, char *err_str, size_t err_len) {
+ DNBLogThreadedIf(LOG_PROCESS, "%s ( path='%s', argv = %p, envp = %p, "
+ "working_dir=%s, stdin=%s, stdout=%s, "
+ "stderr=%s, no-stdio=%i, launch_flavor = %u, "
+ "disable_aslr = %d, err = %p, err_len = "
+ "%llu) called...",
+ __FUNCTION__, path, static_cast<void *>(argv),
+ static_cast<void *>(envp), working_directory, stdin_path,
+ stdout_path, stderr_path, no_stdio, launch_flavor,
+ disable_aslr, static_cast<void *>(err_str),
+ static_cast<uint64_t>(err_len));
+
+ if (err_str && err_len > 0)
+ err_str[0] = '\0';
+ struct stat path_stat;
+ if (::stat(path, &path_stat) == -1) {
+ char stat_error[256];
+ ::strerror_r(errno, stat_error, sizeof(stat_error));
+ snprintf(err_str, err_len, "%s (%s)", stat_error, path);
+ return INVALID_NUB_PROCESS;
+ }
+
+ MachProcessSP processSP(new MachProcess);
+ if (processSP.get()) {
+ DNBError launch_err;
+ pid_t pid = processSP->LaunchForDebug(path, argv, envp, working_directory,
+ stdin_path, stdout_path, stderr_path,
+ no_stdio, launch_flavor, disable_aslr,
+ event_data, launch_err);
+ if (err_str) {
+ *err_str = '\0';
+ if (launch_err.Fail()) {
+ const char *launch_err_str = launch_err.AsString();
+ if (launch_err_str) {
+ strlcpy(err_str, launch_err_str, err_len - 1);
+ err_str[err_len - 1] =
+ '\0'; // Make sure the error string is terminated
+ }
+ }
+ }
+
+ DNBLogThreadedIf(LOG_PROCESS, "(DebugNub) new pid is %d...", pid);
+
+ if (pid != INVALID_NUB_PROCESS) {
+ // Spawn a thread to reap our child inferior process...
+ spawn_waitpid_thread(pid);
+
+ if (processSP->Task().TaskPortForProcessID(launch_err) == TASK_NULL) {
+ // We failed to get the task for our process ID which is bad.
+ // Kill our process otherwise it will be stopped at the entry
+ // point and get reparented to someone else and never go away.
+ DNBLog("Could not get task port for process, sending SIGKILL and "
+ "exiting.");
+ kill(SIGKILL, pid);
+
+ if (err_str && err_len > 0) {
+ if (launch_err.AsString()) {
+ ::snprintf(err_str, err_len,
+ "failed to get the task for process %i (%s)", pid,
+ launch_err.AsString());
+ } else {
+ ::snprintf(err_str, err_len,
+ "failed to get the task for process %i", pid);
+ }
+ }
+ } else {
+ bool res = AddProcessToMap(pid, processSP);
+ UNUSED_IF_ASSERT_DISABLED(res);
+ assert(res && "Couldn't add process to map!");
+ return pid;
+ }
+ }
+ }
+ return INVALID_NUB_PROCESS;
+}
+
+// If there is one process with a given name, return the pid for that process.
+nub_process_t DNBProcessGetPIDByName(const char *name) {
+ std::vector<struct kinfo_proc> matching_proc_infos;
+ size_t num_matching_proc_infos =
+ GetAllInfosMatchingName(name, matching_proc_infos);
+ if (num_matching_proc_infos == 1) {
+ return matching_proc_infos[0].kp_proc.p_pid;
+ }
+ return INVALID_NUB_PROCESS;
+}
+
+nub_process_t DNBProcessAttachByName(const char *name, struct timespec *timeout,
+ char *err_str, size_t err_len) {
+ if (err_str && err_len > 0)
+ err_str[0] = '\0';
+ std::vector<struct kinfo_proc> matching_proc_infos;
+ size_t num_matching_proc_infos =
+ GetAllInfosMatchingName(name, matching_proc_infos);
+ if (num_matching_proc_infos == 0) {
+ DNBLogError("error: no processes match '%s'\n", name);
+ return INVALID_NUB_PROCESS;
+ } else if (num_matching_proc_infos > 1) {
+ DNBLogError("error: %llu processes match '%s':\n",
+ (uint64_t)num_matching_proc_infos, name);
+ size_t i;
+ for (i = 0; i < num_matching_proc_infos; ++i)
+ DNBLogError("%6u - %s\n", matching_proc_infos[i].kp_proc.p_pid,
+ matching_proc_infos[i].kp_proc.p_comm);
+ return INVALID_NUB_PROCESS;
+ }
+
+ return DNBProcessAttach(matching_proc_infos[0].kp_proc.p_pid, timeout,
+ err_str, err_len);
+}
+
+nub_process_t DNBProcessAttach(nub_process_t attach_pid,
+ struct timespec *timeout, char *err_str,
+ size_t err_len) {
+ if (err_str && err_len > 0)
+ err_str[0] = '\0';
+
+ pid_t pid = INVALID_NUB_PROCESS;
+ MachProcessSP processSP(new MachProcess);
+ if (processSP.get()) {
+ DNBLogThreadedIf(LOG_PROCESS, "(DebugNub) attaching to pid %d...",
+ attach_pid);
+ pid = processSP->AttachForDebug(attach_pid, err_str, err_len);
+
+ if (pid != INVALID_NUB_PROCESS) {
+ bool res = AddProcessToMap(pid, processSP);
+ UNUSED_IF_ASSERT_DISABLED(res);
+ assert(res && "Couldn't add process to map!");
+ spawn_waitpid_thread(pid);
+ }
+ }
+
+ while (pid != INVALID_NUB_PROCESS) {
+ // Wait for process to start up and hit entry point
+ DNBLogThreadedIf(LOG_PROCESS, "%s DNBProcessWaitForEvent (%4.4x, "
+ "eEventProcessRunningStateChanged | "
+ "eEventProcessStoppedStateChanged, true, "
+ "INFINITE)...",
+ __FUNCTION__, pid);
+ nub_event_t set_events =
+ DNBProcessWaitForEvents(pid, eEventProcessRunningStateChanged |
+ eEventProcessStoppedStateChanged,
+ true, timeout);
+
+ DNBLogThreadedIf(LOG_PROCESS, "%s DNBProcessWaitForEvent (%4.4x, "
+ "eEventProcessRunningStateChanged | "
+ "eEventProcessStoppedStateChanged, true, "
+ "INFINITE) => 0x%8.8x",
+ __FUNCTION__, pid, set_events);
+
+ if (set_events == 0) {
+ if (err_str && err_len > 0)
+ snprintf(err_str, err_len, "operation timed out");
+ pid = INVALID_NUB_PROCESS;
+ } else {
+ if (set_events & (eEventProcessRunningStateChanged |
+ eEventProcessStoppedStateChanged)) {
+ nub_state_t pid_state = DNBProcessGetState(pid);
+ DNBLogThreadedIf(
+ LOG_PROCESS,
+ "%s process %4.4x state changed (eEventProcessStateChanged): %s",
+ __FUNCTION__, pid, DNBStateAsString(pid_state));
+
+ switch (pid_state) {
+ case eStateInvalid:
+ case eStateUnloaded:
+ case eStateAttaching:
+ case eStateLaunching:
+ case eStateSuspended:
+ break; // Ignore
+
+ case eStateRunning:
+ case eStateStepping:
+ // Still waiting to stop at entry point...
+ break;
+
+ case eStateStopped:
+ case eStateCrashed:
+ return pid;
+
+ case eStateDetached:
+ case eStateExited:
+ if (err_str && err_len > 0)
+ snprintf(err_str, err_len, "process exited");
+ return INVALID_NUB_PROCESS;
+ }
+ }
+
+ DNBProcessResetEvents(pid, set_events);
+ }
+ }
+
+ return INVALID_NUB_PROCESS;
+}
+
+size_t GetAllInfos(std::vector<struct kinfo_proc> &proc_infos) {
+ size_t size = 0;
+ int name[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};
+ u_int namelen = sizeof(name) / sizeof(int);
+ int err;
+
+ // Try to find out how many processes are around so we can
+ // size the buffer appropriately. sysctl's man page specifically suggests
+ // this approach, and says it returns a bit larger size than needed to
+ // handle any new processes created between then and now.
+
+ err = ::sysctl(name, namelen, NULL, &size, NULL, 0);
+
+ if ((err < 0) && (err != ENOMEM)) {
+ proc_infos.clear();
+ perror("sysctl (mib, miblen, NULL, &num_processes, NULL, 0)");
+ return 0;
+ }
+
+ // Increase the size of the buffer by a few processes in case more have
+ // been spawned
+ proc_infos.resize(size / sizeof(struct kinfo_proc));
+ size = proc_infos.size() *
+ sizeof(struct kinfo_proc); // Make sure we don't exceed our resize...
+ err = ::sysctl(name, namelen, &proc_infos[0], &size, NULL, 0);
+ if (err < 0) {
+ proc_infos.clear();
+ return 0;
+ }
+
+ // Trim down our array to fit what we actually got back
+ proc_infos.resize(size / sizeof(struct kinfo_proc));
+ return proc_infos.size();
+}
+
+static size_t
+GetAllInfosMatchingName(const char *full_process_name,
+ std::vector<struct kinfo_proc> &matching_proc_infos) {
+
+ matching_proc_infos.clear();
+ if (full_process_name && full_process_name[0]) {
+ // We only get the process name, not the full path, from the proc_info. So
+ // just take the
+ // base name of the process name...
+ const char *process_name;
+ process_name = strrchr(full_process_name, '/');
+ if (process_name == NULL)
+ process_name = full_process_name;
+ else
+ process_name++;
+
+ const size_t process_name_len = strlen(process_name);
+ std::vector<struct kinfo_proc> proc_infos;
+ const size_t num_proc_infos = GetAllInfos(proc_infos);
+ if (num_proc_infos > 0) {
+ uint32_t i;
+ for (i = 0; i < num_proc_infos; i++) {
+ // Skip zombie processes and processes with unset status
+ if (proc_infos[i].kp_proc.p_stat == 0 ||
+ proc_infos[i].kp_proc.p_stat == SZOMB)
+ continue;
+
+ // Check for process by name. We only check the first MAXCOMLEN
+ // chars as that is all that kp_proc.p_comm holds.
+
+ if (::strncasecmp(process_name, proc_infos[i].kp_proc.p_comm,
+ MAXCOMLEN) == 0) {
+ if (process_name_len > MAXCOMLEN) {
+ // We found a matching process name whose first MAXCOMLEN
+ // characters match, but there is more to the name than
+ // this. We need to get the full process name. Use proc_pidpath,
+ // which will get
+ // us the full path to the executed process.
+
+ char proc_path_buf[PATH_MAX];
+
+ int return_val = proc_pidpath(proc_infos[i].kp_proc.p_pid,
+ proc_path_buf, PATH_MAX);
+ if (return_val > 0) {
+ // Okay, now search backwards from that to see if there is a
+ // slash in the name. Note, even though we got all the args we
+ // don't care
+ // because the list data is just a bunch of concatenated null
+ // terminated strings
+ // so strrchr will start from the end of argv0.
+
+ const char *argv_basename = strrchr(proc_path_buf, '/');
+ if (argv_basename) {
+ // Skip the '/'
+ ++argv_basename;
+ } else {
+ // We didn't find a directory delimiter in the process argv[0],
+ // just use what was in there
+ argv_basename = proc_path_buf;
+ }
+
+ if (argv_basename) {
+ if (::strncasecmp(process_name, argv_basename, PATH_MAX) == 0) {
+ matching_proc_infos.push_back(proc_infos[i]);
+ }
+ }
+ }
+ } else {
+ // We found a matching process, add it to our list
+ matching_proc_infos.push_back(proc_infos[i]);
+ }
+ }
+ }
+ }
+ }
+ // return the newly added matches.
+ return matching_proc_infos.size();
+}
+
+nub_process_t DNBProcessAttachWait(
+ const char *waitfor_process_name, nub_launch_flavor_t launch_flavor,
+ bool ignore_existing, struct timespec *timeout_abstime,
+ useconds_t waitfor_interval, char *err_str, size_t err_len,
+ DNBShouldCancelCallback should_cancel_callback, void *callback_data) {
+ DNBError prepare_error;
+ std::vector<struct kinfo_proc> exclude_proc_infos;
+ size_t num_exclude_proc_infos;
+
+ // If the PrepareForAttach returns a valid token, use MachProcess to check
+ // for the process, otherwise scan the process table.
+
+ const void *attach_token = MachProcess::PrepareForAttach(
+ waitfor_process_name, launch_flavor, true, prepare_error);
+
+ if (prepare_error.Fail()) {
+ DNBLogError("Error in PrepareForAttach: %s", prepare_error.AsString());
+ return INVALID_NUB_PROCESS;
+ }
+
+ if (attach_token == NULL) {
+ if (ignore_existing)
+ num_exclude_proc_infos =
+ GetAllInfosMatchingName(waitfor_process_name, exclude_proc_infos);
+ else
+ num_exclude_proc_infos = 0;
+ }
+
+ DNBLogThreadedIf(LOG_PROCESS, "Waiting for '%s' to appear...\n",
+ waitfor_process_name);
+
+ // Loop and try to find the process by name
+ nub_process_t waitfor_pid = INVALID_NUB_PROCESS;
+
+ while (waitfor_pid == INVALID_NUB_PROCESS) {
+ if (attach_token != NULL) {
+ nub_process_t pid;
+ pid = MachProcess::CheckForProcess(attach_token, launch_flavor);
+ if (pid != INVALID_NUB_PROCESS) {
+ waitfor_pid = pid;
+ break;
+ }
+ } else {
+
+ // Get the current process list, and check for matches that
+ // aren't in our original list. If anyone wants to attach
+ // to an existing process by name, they should do it with
+ // --attach=PROCNAME. Else we will wait for the first matching
+ // process that wasn't in our exclusion list.
+ std::vector<struct kinfo_proc> proc_infos;
+ const size_t num_proc_infos =
+ GetAllInfosMatchingName(waitfor_process_name, proc_infos);
+ for (size_t i = 0; i < num_proc_infos; i++) {
+ nub_process_t curr_pid = proc_infos[i].kp_proc.p_pid;
+ for (size_t j = 0; j < num_exclude_proc_infos; j++) {
+ if (curr_pid == exclude_proc_infos[j].kp_proc.p_pid) {
+ // This process was in our exclusion list, don't use it.
+ curr_pid = INVALID_NUB_PROCESS;
+ break;
+ }
+ }
+
+ // If we didn't find CURR_PID in our exclusion list, then use it.
+ if (curr_pid != INVALID_NUB_PROCESS) {
+ // We found our process!
+ waitfor_pid = curr_pid;
+ break;
+ }
+ }
+ }
+
+ // If we haven't found our process yet, check for a timeout
+ // and then sleep for a bit until we poll again.
+ if (waitfor_pid == INVALID_NUB_PROCESS) {
+ if (timeout_abstime != NULL) {
+ // Check to see if we have a waitfor-duration option that
+ // has timed out?
+ if (DNBTimer::TimeOfDayLaterThan(*timeout_abstime)) {
+ if (err_str && err_len > 0)
+ snprintf(err_str, err_len, "operation timed out");
+ DNBLogError("error: waiting for process '%s' timed out.\n",
+ waitfor_process_name);
+ return INVALID_NUB_PROCESS;
+ }
+ }
+
+ // Call the should cancel callback as well...
+
+ if (should_cancel_callback != NULL &&
+ should_cancel_callback(callback_data)) {
+ DNBLogThreadedIf(
+ LOG_PROCESS,
+ "DNBProcessAttachWait cancelled by should_cancel callback.");
+ waitfor_pid = INVALID_NUB_PROCESS;
+ break;
+ }
+
+ ::usleep(waitfor_interval); // Sleep for WAITFOR_INTERVAL, then poll again
+ }
+ }
+
+ if (waitfor_pid != INVALID_NUB_PROCESS) {
+ DNBLogThreadedIf(LOG_PROCESS, "Attaching to %s with pid %i...\n",
+ waitfor_process_name, waitfor_pid);
+ waitfor_pid =
+ DNBProcessAttach(waitfor_pid, timeout_abstime, err_str, err_len);
+ }
+
+ bool success = waitfor_pid != INVALID_NUB_PROCESS;
+ MachProcess::CleanupAfterAttach(attach_token, launch_flavor, success,
+ prepare_error);
+
+ return waitfor_pid;
+}
+
+nub_bool_t DNBProcessDetach(nub_process_t pid) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ const bool remove = true;
+ DNBLogThreaded(
+ "Disabling breakpoints and watchpoints, and detaching from %d.", pid);
+ procSP->DisableAllBreakpoints(remove);
+ procSP->DisableAllWatchpoints(remove);
+ return procSP->Detach();
+ }
+ return false;
+}
+
+nub_bool_t DNBProcessKill(nub_process_t pid) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ return procSP->Kill();
+ }
+ return false;
+}
+
+nub_bool_t DNBProcessSignal(nub_process_t pid, int signal) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ return procSP->Signal(signal);
+ }
+ return false;
+}
+
+nub_bool_t DNBProcessInterrupt(nub_process_t pid) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->Interrupt();
+ return false;
+}
+
+nub_bool_t DNBProcessSendEvent(nub_process_t pid, const char *event) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ // FIXME: Do something with the error...
+ DNBError send_error;
+ return procSP->SendEvent(event, send_error);
+ }
+ return false;
+}
+
+nub_bool_t DNBProcessIsAlive(nub_process_t pid) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ return MachTask::IsValid(procSP->Task().TaskPort());
+ }
+ return eStateInvalid;
+}
+
+// Process and Thread state information
+nub_state_t DNBProcessGetState(nub_process_t pid) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ return procSP->GetState();
+ }
+ return eStateInvalid;
+}
+
+// Process and Thread state information
+nub_bool_t DNBProcessGetExitStatus(nub_process_t pid, int *status) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ return procSP->GetExitStatus(status);
+ }
+ return false;
+}
+
+nub_bool_t DNBProcessSetExitStatus(nub_process_t pid, int status) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ procSP->SetExitStatus(status);
+ return true;
+ }
+ return false;
+}
+
+const char *DNBProcessGetExitInfo(nub_process_t pid) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ return procSP->GetExitInfo();
+ }
+ return NULL;
+}
+
+nub_bool_t DNBProcessSetExitInfo(nub_process_t pid, const char *info) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ procSP->SetExitInfo(info);
+ return true;
+ }
+ return false;
+}
+
+const char *DNBThreadGetName(nub_process_t pid, nub_thread_t tid) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->ThreadGetName(tid);
+ return NULL;
+}
+
+nub_bool_t
+DNBThreadGetIdentifierInfo(nub_process_t pid, nub_thread_t tid,
+ thread_identifier_info_data_t *ident_info) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->GetThreadList().GetIdentifierInfo(tid, ident_info);
+ return false;
+}
+
+nub_state_t DNBThreadGetState(nub_process_t pid, nub_thread_t tid) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ return procSP->ThreadGetState(tid);
+ }
+ return eStateInvalid;
+}
+
+const char *DNBStateAsString(nub_state_t state) {
+ switch (state) {
+ case eStateInvalid:
+ return "Invalid";
+ case eStateUnloaded:
+ return "Unloaded";
+ case eStateAttaching:
+ return "Attaching";
+ case eStateLaunching:
+ return "Launching";
+ case eStateStopped:
+ return "Stopped";
+ case eStateRunning:
+ return "Running";
+ case eStateStepping:
+ return "Stepping";
+ case eStateCrashed:
+ return "Crashed";
+ case eStateDetached:
+ return "Detached";
+ case eStateExited:
+ return "Exited";
+ case eStateSuspended:
+ return "Suspended";
+ }
+ return "nub_state_t ???";
+}
+
+Genealogy::ThreadActivitySP DNBGetGenealogyInfoForThread(nub_process_t pid,
+ nub_thread_t tid,
+ bool &timed_out) {
+ Genealogy::ThreadActivitySP thread_activity_sp;
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ thread_activity_sp = procSP->GetGenealogyInfoForThread(tid, timed_out);
+ return thread_activity_sp;
+}
+
+Genealogy::ProcessExecutableInfoSP DNBGetGenealogyImageInfo(nub_process_t pid,
+ size_t idx) {
+ Genealogy::ProcessExecutableInfoSP image_info_sp;
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ image_info_sp = procSP->GetGenealogyImageInfo(idx);
+ }
+ return image_info_sp;
+}
+
+ThreadInfo::QoS DNBGetRequestedQoSForThread(nub_process_t pid, nub_thread_t tid,
+ nub_addr_t tsd,
+ uint64_t dti_qos_class_index) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ return procSP->GetRequestedQoS(tid, tsd, dti_qos_class_index);
+ }
+ return ThreadInfo::QoS();
+}
+
+nub_addr_t DNBGetPThreadT(nub_process_t pid, nub_thread_t tid) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ return procSP->GetPThreadT(tid);
+ }
+ return INVALID_NUB_ADDRESS;
+}
+
+nub_addr_t DNBGetDispatchQueueT(nub_process_t pid, nub_thread_t tid) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ return procSP->GetDispatchQueueT(tid);
+ }
+ return INVALID_NUB_ADDRESS;
+}
+
+nub_addr_t
+DNBGetTSDAddressForThread(nub_process_t pid, nub_thread_t tid,
+ uint64_t plo_pthread_tsd_base_address_offset,
+ uint64_t plo_pthread_tsd_base_offset,
+ uint64_t plo_pthread_tsd_entry_size) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ return procSP->GetTSDAddressForThread(
+ tid, plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset,
+ plo_pthread_tsd_entry_size);
+ }
+ return INVALID_NUB_ADDRESS;
+}
+
+JSONGenerator::ObjectSP DNBGetLoadedDynamicLibrariesInfos(
+ nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ return procSP->GetLoadedDynamicLibrariesInfos(pid, image_list_address,
+ image_count);
+ }
+ return JSONGenerator::ObjectSP();
+}
+
+JSONGenerator::ObjectSP DNBGetAllLoadedLibrariesInfos(nub_process_t pid) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ return procSP->GetAllLoadedLibrariesInfos(pid);
+ }
+ return JSONGenerator::ObjectSP();
+}
+
+JSONGenerator::ObjectSP
+DNBGetLibrariesInfoForAddresses(nub_process_t pid,
+ std::vector<uint64_t> &macho_addresses) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ return procSP->GetLibrariesInfoForAddresses(pid, macho_addresses);
+ }
+ return JSONGenerator::ObjectSP();
+}
+
+JSONGenerator::ObjectSP DNBGetSharedCacheInfo(nub_process_t pid) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ return procSP->GetSharedCacheInfo(pid);
+ }
+ return JSONGenerator::ObjectSP();
+}
+
+const char *DNBProcessGetExecutablePath(nub_process_t pid) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ return procSP->Path();
+ }
+ return NULL;
+}
+
+nub_size_t DNBProcessGetArgumentCount(nub_process_t pid) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ return procSP->ArgumentCount();
+ }
+ return 0;
+}
+
+const char *DNBProcessGetArgumentAtIndex(nub_process_t pid, nub_size_t idx) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ return procSP->ArgumentAtIndex(idx);
+ }
+ return NULL;
+}
+
+// Execution control
+nub_bool_t DNBProcessResume(nub_process_t pid,
+ const DNBThreadResumeAction *actions,
+ size_t num_actions) {
+ DNBLogThreadedIf(LOG_PROCESS, "%s(pid = %4.4x)", __FUNCTION__, pid);
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ DNBThreadResumeActions thread_actions(actions, num_actions);
+
+ // Below we add a default thread plan just in case one wasn't
+ // provided so all threads always know what they were supposed to do
+ if (thread_actions.IsEmpty()) {
+ // No thread plans were given, so the default it to run all threads
+ thread_actions.SetDefaultThreadActionIfNeeded(eStateRunning, 0);
+ } else {
+ // Some thread plans were given which means anything that wasn't
+ // specified should remain stopped.
+ thread_actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0);
+ }
+ return procSP->Resume(thread_actions);
+ }
+ return false;
+}
+
+nub_bool_t DNBProcessHalt(nub_process_t pid) {
+ DNBLogThreadedIf(LOG_PROCESS, "%s(pid = %4.4x)", __FUNCTION__, pid);
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->Signal(SIGSTOP);
+ return false;
+}
+//
+// nub_bool_t
+// DNBThreadResume (nub_process_t pid, nub_thread_t tid, nub_bool_t step)
+//{
+// DNBLogThreadedIf(LOG_THREAD, "%s(pid = %4.4x, tid = %4.4x, step = %u)",
+// __FUNCTION__, pid, tid, (uint32_t)step);
+// MachProcessSP procSP;
+// if (GetProcessSP (pid, procSP))
+// {
+// return procSP->Resume(tid, step, 0);
+// }
+// return false;
+//}
+//
+// nub_bool_t
+// DNBThreadResumeWithSignal (nub_process_t pid, nub_thread_t tid, nub_bool_t
+// step, int signal)
+//{
+// DNBLogThreadedIf(LOG_THREAD, "%s(pid = %4.4x, tid = %4.4x, step = %u,
+// signal = %i)", __FUNCTION__, pid, tid, (uint32_t)step, signal);
+// MachProcessSP procSP;
+// if (GetProcessSP (pid, procSP))
+// {
+// return procSP->Resume(tid, step, signal);
+// }
+// return false;
+//}
+
+nub_event_t DNBProcessWaitForEvents(nub_process_t pid, nub_event_t event_mask,
+ bool wait_for_set,
+ struct timespec *timeout) {
+ nub_event_t result = 0;
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ if (wait_for_set)
+ result = procSP->Events().WaitForSetEvents(event_mask, timeout);
+ else
+ result = procSP->Events().WaitForEventsToReset(event_mask, timeout);
+ }
+ return result;
+}
+
+void DNBProcessResetEvents(nub_process_t pid, nub_event_t event_mask) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ procSP->Events().ResetEvents(event_mask);
+}
+
+// Breakpoints
+nub_bool_t DNBBreakpointSet(nub_process_t pid, nub_addr_t addr, nub_size_t size,
+ nub_bool_t hardware) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->CreateBreakpoint(addr, size, hardware) != NULL;
+ return false;
+}
+
+nub_bool_t DNBBreakpointClear(nub_process_t pid, nub_addr_t addr) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->DisableBreakpoint(addr, true);
+ return false; // Failed
+}
+
+// Watchpoints
+nub_bool_t DNBWatchpointSet(nub_process_t pid, nub_addr_t addr, nub_size_t size,
+ uint32_t watch_flags, nub_bool_t hardware) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->CreateWatchpoint(addr, size, watch_flags, hardware) != NULL;
+ return false;
+}
+
+nub_bool_t DNBWatchpointClear(nub_process_t pid, nub_addr_t addr) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->DisableWatchpoint(addr, true);
+ return false; // Failed
+}
+
+// Return the number of supported hardware watchpoints.
+uint32_t DNBWatchpointGetNumSupportedHWP(nub_process_t pid) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->GetNumSupportedHardwareWatchpoints();
+ return 0;
+}
+
+// Read memory in the address space of process PID. This call will take
+// care of setting and restoring permissions and breaking up the memory
+// read into multiple chunks as required.
+//
+// RETURNS: number of bytes actually read
+nub_size_t DNBProcessMemoryRead(nub_process_t pid, nub_addr_t addr,
+ nub_size_t size, void *buf) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->ReadMemory(addr, size, buf);
+ return 0;
+}
+
+uint64_t DNBProcessMemoryReadInteger(nub_process_t pid, nub_addr_t addr,
+ nub_size_t integer_size,
+ uint64_t fail_value) {
+ union Integers {
+ uint8_t u8;
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ };
+
+ if (integer_size <= sizeof(uint64_t)) {
+ Integers ints;
+ if (DNBProcessMemoryRead(pid, addr, integer_size, &ints) == integer_size) {
+ switch (integer_size) {
+ case 1:
+ return ints.u8;
+ case 2:
+ return ints.u16;
+ case 3:
+ return ints.u32 & 0xffffffu;
+ case 4:
+ return ints.u32;
+ case 5:
+ return ints.u32 & 0x000000ffffffffffull;
+ case 6:
+ return ints.u32 & 0x0000ffffffffffffull;
+ case 7:
+ return ints.u32 & 0x00ffffffffffffffull;
+ case 8:
+ return ints.u64;
+ }
+ }
+ }
+ return fail_value;
+}
+
+nub_addr_t DNBProcessMemoryReadPointer(nub_process_t pid, nub_addr_t addr) {
+ cpu_type_t cputype = DNBProcessGetCPUType(pid);
+ if (cputype) {
+ const nub_size_t pointer_size = (cputype & CPU_ARCH_ABI64) ? 8 : 4;
+ return DNBProcessMemoryReadInteger(pid, addr, pointer_size, 0);
+ }
+ return 0;
+}
+
+std::string DNBProcessMemoryReadCString(nub_process_t pid, nub_addr_t addr) {
+ std::string cstr;
+ char buffer[256];
+ const nub_size_t max_buffer_cstr_length = sizeof(buffer) - 1;
+ buffer[max_buffer_cstr_length] = '\0';
+ nub_size_t length = 0;
+ nub_addr_t curr_addr = addr;
+ do {
+ nub_size_t bytes_read =
+ DNBProcessMemoryRead(pid, curr_addr, max_buffer_cstr_length, buffer);
+ if (bytes_read == 0)
+ break;
+ length = strlen(buffer);
+ cstr.append(buffer, length);
+ curr_addr += length;
+ } while (length == max_buffer_cstr_length);
+ return cstr;
+}
+
+std::string DNBProcessMemoryReadCStringFixed(nub_process_t pid, nub_addr_t addr,
+ nub_size_t fixed_length) {
+ std::string cstr;
+ char buffer[fixed_length + 1];
+ buffer[fixed_length] = '\0';
+ nub_size_t bytes_read = DNBProcessMemoryRead(pid, addr, fixed_length, buffer);
+ if (bytes_read > 0)
+ cstr.assign(buffer);
+ return cstr;
+}
+
+// Write memory to the address space of process PID. This call will take
+// care of setting and restoring permissions and breaking up the memory
+// write into multiple chunks as required.
+//
+// RETURNS: number of bytes actually written
+nub_size_t DNBProcessMemoryWrite(nub_process_t pid, nub_addr_t addr,
+ nub_size_t size, const void *buf) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->WriteMemory(addr, size, buf);
+ return 0;
+}
+
+nub_addr_t DNBProcessMemoryAllocate(nub_process_t pid, nub_size_t size,
+ uint32_t permissions) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->Task().AllocateMemory(size, permissions);
+ return 0;
+}
+
+nub_bool_t DNBProcessMemoryDeallocate(nub_process_t pid, nub_addr_t addr) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->Task().DeallocateMemory(addr);
+ return 0;
+}
+
+// Find attributes of the memory region that contains ADDR for process PID,
+// if possible, and return a string describing those attributes.
+//
+// Returns 1 if we could find attributes for this region and OUTBUF can
+// be sent to the remote debugger.
+//
+// Returns 0 if we couldn't find the attributes for a region of memory at
+// that address and OUTBUF should not be sent.
+//
+// Returns -1 if this platform cannot look up information about memory regions
+// or if we do not yet have a valid launched process.
+//
+int DNBProcessMemoryRegionInfo(nub_process_t pid, nub_addr_t addr,
+ DNBRegionInfo *region_info) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->Task().GetMemoryRegionInfo(addr, region_info);
+
+ return -1;
+}
+
+std::string DNBProcessGetProfileData(nub_process_t pid,
+ DNBProfileDataScanType scanType) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->Task().GetProfileData(scanType);
+
+ return std::string("");
+}
+
+nub_bool_t DNBProcessSetEnableAsyncProfiling(nub_process_t pid,
+ nub_bool_t enable,
+ uint64_t interval_usec,
+ DNBProfileDataScanType scan_type) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ procSP->SetEnableAsyncProfiling(enable, interval_usec, scan_type);
+ return true;
+ }
+
+ return false;
+}
+
+// Get the number of threads for the specified process.
+nub_size_t DNBProcessGetNumThreads(nub_process_t pid) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->GetNumThreads();
+ return 0;
+}
+
+// Get the thread ID of the current thread.
+nub_thread_t DNBProcessGetCurrentThread(nub_process_t pid) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->GetCurrentThread();
+ return 0;
+}
+
+// Get the mach port number of the current thread.
+nub_thread_t DNBProcessGetCurrentThreadMachPort(nub_process_t pid) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->GetCurrentThreadMachPort();
+ return 0;
+}
+
+// Change the current thread.
+nub_thread_t DNBProcessSetCurrentThread(nub_process_t pid, nub_thread_t tid) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->SetCurrentThread(tid);
+ return INVALID_NUB_THREAD;
+}
+
+// Dump a string describing a thread's stop reason to the specified file
+// handle
+nub_bool_t DNBThreadGetStopReason(nub_process_t pid, nub_thread_t tid,
+ struct DNBThreadStopInfo *stop_info) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->GetThreadStoppedReason(tid, stop_info);
+ return false;
+}
+
+// Return string description for the specified thread.
+//
+// RETURNS: NULL if the thread isn't valid, else a NULL terminated C
+// string from a static buffer that must be copied prior to subsequent
+// calls.
+const char *DNBThreadGetInfo(nub_process_t pid, nub_thread_t tid) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->GetThreadInfo(tid);
+ return NULL;
+}
+
+// Get the thread ID given a thread index.
+nub_thread_t DNBProcessGetThreadAtIndex(nub_process_t pid, size_t thread_idx) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->GetThreadAtIndex(thread_idx);
+ return INVALID_NUB_THREAD;
+}
+
+// Do whatever is needed to sync the thread's register state with it's kernel
+// values.
+nub_bool_t DNBProcessSyncThreadState(nub_process_t pid, nub_thread_t tid) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->SyncThreadState(tid);
+ return false;
+}
+
+nub_addr_t DNBProcessGetSharedLibraryInfoAddress(nub_process_t pid) {
+ MachProcessSP procSP;
+ DNBError err;
+ if (GetProcessSP(pid, procSP))
+ return procSP->Task().GetDYLDAllImageInfosAddress(err);
+ return INVALID_NUB_ADDRESS;
+}
+
+nub_bool_t DNBProcessSharedLibrariesUpdated(nub_process_t pid) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ procSP->SharedLibrariesUpdated();
+ return true;
+ }
+ return false;
+}
+
+const char *DNBGetDeploymentInfo(nub_process_t pid,
+ const struct load_command& lc,
+ uint64_t load_command_address,
+ uint32_t& major_version,
+ uint32_t& minor_version,
+ uint32_t& patch_version) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->GetDeploymentInfo(lc, load_command_address,
+ major_version, minor_version,
+ patch_version);
+ return nullptr;
+}
+
+
+// Get the current shared library information for a process. Only return
+// the shared libraries that have changed since the last shared library
+// state changed event if only_changed is non-zero.
+nub_size_t
+DNBProcessGetSharedLibraryInfo(nub_process_t pid, nub_bool_t only_changed,
+ struct DNBExecutableImageInfo **image_infos) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->CopyImageInfos(image_infos, only_changed);
+
+ // If we have no process, then return NULL for the shared library info
+ // and zero for shared library count
+ *image_infos = NULL;
+ return 0;
+}
+
+uint32_t DNBGetRegisterCPUType() {
+ return DNBArchProtocol::GetRegisterCPUType();
+}
+// Get the register set information for a specific thread.
+const DNBRegisterSetInfo *DNBGetRegisterSetInfo(nub_size_t *num_reg_sets) {
+ return DNBArchProtocol::GetRegisterSetInfo(num_reg_sets);
+}
+
+// Read a register value by register set and register index.
+nub_bool_t DNBThreadGetRegisterValueByID(nub_process_t pid, nub_thread_t tid,
+ uint32_t set, uint32_t reg,
+ DNBRegisterValue *value) {
+ MachProcessSP procSP;
+ ::bzero(value, sizeof(DNBRegisterValue));
+ if (GetProcessSP(pid, procSP)) {
+ if (tid != INVALID_NUB_THREAD)
+ return procSP->GetRegisterValue(tid, set, reg, value);
+ }
+ return false;
+}
+
+nub_bool_t DNBThreadSetRegisterValueByID(nub_process_t pid, nub_thread_t tid,
+ uint32_t set, uint32_t reg,
+ const DNBRegisterValue *value) {
+ if (tid != INVALID_NUB_THREAD) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->SetRegisterValue(tid, set, reg, value);
+ }
+ return false;
+}
+
+nub_size_t DNBThreadGetRegisterContext(nub_process_t pid, nub_thread_t tid,
+ void *buf, size_t buf_len) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ if (tid != INVALID_NUB_THREAD)
+ return procSP->GetThreadList().GetRegisterContext(tid, buf, buf_len);
+ }
+ ::bzero(buf, buf_len);
+ return 0;
+}
+
+nub_size_t DNBThreadSetRegisterContext(nub_process_t pid, nub_thread_t tid,
+ const void *buf, size_t buf_len) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ if (tid != INVALID_NUB_THREAD)
+ return procSP->GetThreadList().SetRegisterContext(tid, buf, buf_len);
+ }
+ return 0;
+}
+
+uint32_t DNBThreadSaveRegisterState(nub_process_t pid, nub_thread_t tid) {
+ if (tid != INVALID_NUB_THREAD) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->GetThreadList().SaveRegisterState(tid);
+ }
+ return 0;
+}
+nub_bool_t DNBThreadRestoreRegisterState(nub_process_t pid, nub_thread_t tid,
+ uint32_t save_id) {
+ if (tid != INVALID_NUB_THREAD) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->GetThreadList().RestoreRegisterState(tid, save_id);
+ }
+ return false;
+}
+
+// Read a register value by name.
+nub_bool_t DNBThreadGetRegisterValueByName(nub_process_t pid, nub_thread_t tid,
+ uint32_t reg_set,
+ const char *reg_name,
+ DNBRegisterValue *value) {
+ MachProcessSP procSP;
+ ::bzero(value, sizeof(DNBRegisterValue));
+ if (GetProcessSP(pid, procSP)) {
+ const struct DNBRegisterSetInfo *set_info;
+ nub_size_t num_reg_sets = 0;
+ set_info = DNBGetRegisterSetInfo(&num_reg_sets);
+ if (set_info) {
+ uint32_t set = reg_set;
+ uint32_t reg;
+ if (set == REGISTER_SET_ALL) {
+ for (set = 1; set < num_reg_sets; ++set) {
+ for (reg = 0; reg < set_info[set].num_registers; ++reg) {
+ if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0)
+ return procSP->GetRegisterValue(tid, set, reg, value);
+ }
+ }
+ } else {
+ for (reg = 0; reg < set_info[set].num_registers; ++reg) {
+ if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0)
+ return procSP->GetRegisterValue(tid, set, reg, value);
+ }
+ }
+ }
+ }
+ return false;
+}
+
+// Read a register set and register number from the register name.
+nub_bool_t DNBGetRegisterInfoByName(const char *reg_name,
+ DNBRegisterInfo *info) {
+ const struct DNBRegisterSetInfo *set_info;
+ nub_size_t num_reg_sets = 0;
+ set_info = DNBGetRegisterSetInfo(&num_reg_sets);
+ if (set_info) {
+ uint32_t set, reg;
+ for (set = 1; set < num_reg_sets; ++set) {
+ for (reg = 0; reg < set_info[set].num_registers; ++reg) {
+ if (strcasecmp(reg_name, set_info[set].registers[reg].name) == 0) {
+ *info = set_info[set].registers[reg];
+ return true;
+ }
+ }
+ }
+
+ for (set = 1; set < num_reg_sets; ++set) {
+ uint32_t reg;
+ for (reg = 0; reg < set_info[set].num_registers; ++reg) {
+ if (set_info[set].registers[reg].alt == NULL)
+ continue;
+
+ if (strcasecmp(reg_name, set_info[set].registers[reg].alt) == 0) {
+ *info = set_info[set].registers[reg];
+ return true;
+ }
+ }
+ }
+ }
+
+ ::bzero(info, sizeof(DNBRegisterInfo));
+ return false;
+}
+
+// Set the name to address callback function that this nub can use
+// for any name to address lookups that are needed.
+nub_bool_t DNBProcessSetNameToAddressCallback(nub_process_t pid,
+ DNBCallbackNameToAddress callback,
+ void *baton) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ procSP->SetNameToAddressCallback(callback, baton);
+ return true;
+ }
+ return false;
+}
+
+// Set the name to address callback function that this nub can use
+// for any name to address lookups that are needed.
+nub_bool_t DNBProcessSetSharedLibraryInfoCallback(
+ nub_process_t pid, DNBCallbackCopyExecutableImageInfos callback,
+ void *baton) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ procSP->SetSharedLibraryInfoCallback(callback, baton);
+ return true;
+ }
+ return false;
+}
+
+nub_addr_t DNBProcessLookupAddress(nub_process_t pid, const char *name,
+ const char *shlib) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP)) {
+ return procSP->LookupSymbol(name, shlib);
+ }
+ return INVALID_NUB_ADDRESS;
+}
+
+nub_size_t DNBProcessGetAvailableSTDOUT(nub_process_t pid, char *buf,
+ nub_size_t buf_size) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->GetAvailableSTDOUT(buf, buf_size);
+ return 0;
+}
+
+nub_size_t DNBProcessGetAvailableSTDERR(nub_process_t pid, char *buf,
+ nub_size_t buf_size) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->GetAvailableSTDERR(buf, buf_size);
+ return 0;
+}
+
+nub_size_t DNBProcessGetAvailableProfileData(nub_process_t pid, char *buf,
+ nub_size_t buf_size) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->GetAsyncProfileData(buf, buf_size);
+ return 0;
+}
+
+DarwinLogEventVector DNBProcessGetAvailableDarwinLogEvents(nub_process_t pid) {
+ return DarwinLogCollector::GetEventsForProcess(pid);
+}
+
+nub_size_t DNBProcessGetStopCount(nub_process_t pid) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->StopCount();
+ return 0;
+}
+
+uint32_t DNBProcessGetCPUType(nub_process_t pid) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->GetCPUType();
+ return 0;
+}
+
+nub_bool_t DNBResolveExecutablePath(const char *path, char *resolved_path,
+ size_t resolved_path_size) {
+ if (path == NULL || path[0] == '\0')
+ return false;
+
+ char max_path[PATH_MAX];
+ std::string result;
+ CFString::GlobPath(path, result);
+
+ if (result.empty())
+ result = path;
+
+ struct stat path_stat;
+ if (::stat(path, &path_stat) == 0) {
+ if ((path_stat.st_mode & S_IFMT) == S_IFDIR) {
+ CFBundle bundle(path);
+ CFReleaser<CFURLRef> url(bundle.CopyExecutableURL());
+ if (url.get()) {
+ if (::CFURLGetFileSystemRepresentation(
+ url.get(), true, (UInt8 *)resolved_path, resolved_path_size))
+ return true;
+ }
+ }
+ }
+
+ if (realpath(path, max_path)) {
+ // Found the path relatively...
+ ::strlcpy(resolved_path, max_path, resolved_path_size);
+ return strlen(resolved_path) + 1 < resolved_path_size;
+ } else {
+ // Not a relative path, check the PATH environment variable if the
+ const char *PATH = getenv("PATH");
+ if (PATH) {
+ const char *curr_path_start = PATH;
+ const char *curr_path_end;
+ while (curr_path_start && *curr_path_start) {
+ curr_path_end = strchr(curr_path_start, ':');
+ if (curr_path_end == NULL) {
+ result.assign(curr_path_start);
+ curr_path_start = NULL;
+ } else if (curr_path_end > curr_path_start) {
+ size_t len = curr_path_end - curr_path_start;
+ result.assign(curr_path_start, len);
+ curr_path_start += len + 1;
+ } else
+ break;
+
+ result += '/';
+ result += path;
+ struct stat s;
+ if (stat(result.c_str(), &s) == 0) {
+ ::strlcpy(resolved_path, result.c_str(), resolved_path_size);
+ return result.size() + 1 < resolved_path_size;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool DNBGetOSVersionNumbers(uint64_t *major, uint64_t *minor, uint64_t *patch) {
+ return MachProcess::GetOSVersionNumbers(major, minor, patch);
+}
+
+std::string DNBGetMacCatalystVersionString() {
+ return MachProcess::GetMacCatalystVersionString();
+}
+
+void DNBInitialize() {
+ DNBLogThreadedIf(LOG_PROCESS, "DNBInitialize ()");
+#if defined(__i386__) || defined(__x86_64__)
+ DNBArchImplI386::Initialize();
+ DNBArchImplX86_64::Initialize();
+#elif defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
+ DNBArchMachARM::Initialize();
+ DNBArchMachARM64::Initialize();
+#endif
+}
+
+void DNBTerminate() {}
+
+nub_bool_t DNBSetArchitecture(const char *arch) {
+ if (arch && arch[0]) {
+ if (strcasecmp(arch, "i386") == 0)
+ return DNBArchProtocol::SetArchitecture(CPU_TYPE_I386);
+ else if ((strcasecmp(arch, "x86_64") == 0) ||
+ (strcasecmp(arch, "x86_64h") == 0))
+ return DNBArchProtocol::SetArchitecture(CPU_TYPE_X86_64);
+ else if (strstr(arch, "arm64_32") == arch ||
+ strstr(arch, "aarch64_32") == arch)
+ return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64_32);
+ else if (strstr(arch, "arm64e") == arch)
+ return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64);
+ else if (strstr(arch, "arm64") == arch || strstr(arch, "armv8") == arch ||
+ strstr(arch, "aarch64") == arch)
+ return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64);
+ else if (strstr(arch, "arm") == arch)
+ return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM);
+ }
+ return false;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/DNB.h b/gnu/llvm/lldb/tools/debugserver/source/DNB.h
new file mode 100644
index 00000000000..e29fa0fa636
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/DNB.h
@@ -0,0 +1,237 @@
+//===-- DNB.h ---------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 3/23/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNB_h__
+#define __DNB_h__
+
+#include "DNBDefs.h"
+#include "JSONGenerator.h"
+#include "MacOSX/DarwinLog/DarwinLogEvent.h"
+#include "MacOSX/Genealogy.h"
+#include "MacOSX/ThreadInfo.h"
+#include <mach/thread_info.h>
+#include <string>
+#include <Availability.h>
+#include <mach/machine.h>
+
+#define DNB_EXPORT __attribute__((visibility("default")))
+
+#ifndef CPU_TYPE_ARM64
+#define CPU_TYPE_ARM64 ((cpu_type_t)12 | 0x01000000)
+#endif
+
+#ifndef CPU_TYPE_ARM64_32
+#define CPU_TYPE_ARM64_32 ((cpu_type_t)12 | 0x02000000)
+#endif
+
+typedef bool (*DNBShouldCancelCallback)(void *);
+
+void DNBInitialize();
+void DNBTerminate();
+
+nub_bool_t DNBSetArchitecture(const char *arch);
+
+// Process control
+nub_process_t DNBProcessLaunch(
+ const char *path, char const *argv[], const char *envp[],
+ const char *working_directory, // NULL => don't change, non-NULL => set
+ // working directory for inferior to this
+ const char *stdin_path, const char *stdout_path, const char *stderr_path,
+ bool no_stdio, nub_launch_flavor_t launch_flavor, int disable_aslr,
+ const char *event_data, char *err_str, size_t err_len);
+
+nub_process_t DNBProcessGetPIDByName(const char *name);
+nub_process_t DNBProcessAttach(nub_process_t pid, struct timespec *timeout,
+ char *err_str, size_t err_len);
+nub_process_t DNBProcessAttachByName(const char *name, struct timespec *timeout,
+ char *err_str, size_t err_len);
+nub_process_t
+DNBProcessAttachWait(const char *wait_name, nub_launch_flavor_t launch_flavor,
+ bool ignore_existing, struct timespec *timeout,
+ useconds_t interval, char *err_str, size_t err_len,
+ DNBShouldCancelCallback should_cancel = NULL,
+ void *callback_data = NULL);
+// Resume a process with exact instructions on what to do with each thread:
+// - If no thread actions are supplied (actions is NULL or num_actions is zero),
+// then all threads are continued.
+// - If any thread actions are supplied, then each thread will do as it is told
+// by the action. A default actions for any threads that don't have an
+// explicit thread action can be made by making a thread action with a tid of
+// INVALID_NUB_THREAD. If there is no default action, those threads will
+// remain stopped.
+nub_bool_t DNBProcessResume(nub_process_t pid,
+ const DNBThreadResumeAction *actions,
+ size_t num_actions) DNB_EXPORT;
+nub_bool_t DNBProcessHalt(nub_process_t pid) DNB_EXPORT;
+nub_bool_t DNBProcessDetach(nub_process_t pid) DNB_EXPORT;
+nub_bool_t DNBProcessSignal(nub_process_t pid, int signal) DNB_EXPORT;
+nub_bool_t DNBProcessInterrupt(nub_process_t pid) DNB_EXPORT;
+nub_bool_t DNBProcessKill(nub_process_t pid) DNB_EXPORT;
+nub_bool_t DNBProcessSendEvent(nub_process_t pid, const char *event) DNB_EXPORT;
+nub_size_t DNBProcessMemoryRead(nub_process_t pid, nub_addr_t addr,
+ nub_size_t size, void *buf) DNB_EXPORT;
+uint64_t DNBProcessMemoryReadInteger(nub_process_t pid, nub_addr_t addr,
+ nub_size_t integer_size,
+ uint64_t fail_value) DNB_EXPORT;
+nub_addr_t DNBProcessMemoryReadPointer(nub_process_t pid,
+ nub_addr_t addr) DNB_EXPORT;
+std::string DNBProcessMemoryReadCString(nub_process_t pid,
+ nub_addr_t addr) DNB_EXPORT;
+std::string
+DNBProcessMemoryReadCStringFixed(nub_process_t pid, nub_addr_t addr,
+ nub_size_t fixed_length) DNB_EXPORT;
+nub_size_t DNBProcessMemoryWrite(nub_process_t pid, nub_addr_t addr,
+ nub_size_t size, const void *buf) DNB_EXPORT;
+nub_addr_t DNBProcessMemoryAllocate(nub_process_t pid, nub_size_t size,
+ uint32_t permissions) DNB_EXPORT;
+nub_bool_t DNBProcessMemoryDeallocate(nub_process_t pid,
+ nub_addr_t addr) DNB_EXPORT;
+int DNBProcessMemoryRegionInfo(nub_process_t pid, nub_addr_t addr,
+ DNBRegionInfo *region_info) DNB_EXPORT;
+std::string
+DNBProcessGetProfileData(nub_process_t pid,
+ DNBProfileDataScanType scanType) DNB_EXPORT;
+nub_bool_t
+DNBProcessSetEnableAsyncProfiling(nub_process_t pid, nub_bool_t enable,
+ uint64_t interval_usec,
+ DNBProfileDataScanType scan_type) DNB_EXPORT;
+DarwinLogEventVector DNBProcessGetAvailableDarwinLogEvents(nub_process_t pid);
+
+// Process status
+nub_bool_t DNBProcessIsAlive(nub_process_t pid) DNB_EXPORT;
+nub_state_t DNBProcessGetState(nub_process_t pid) DNB_EXPORT;
+nub_bool_t DNBProcessGetExitStatus(nub_process_t pid, int *status) DNB_EXPORT;
+nub_bool_t DNBProcessSetExitStatus(nub_process_t pid, int status) DNB_EXPORT;
+const char *DNBProcessGetExitInfo(nub_process_t pid) DNB_EXPORT;
+nub_bool_t DNBProcessSetExitInfo(nub_process_t pid,
+ const char *info) DNB_EXPORT;
+nub_size_t DNBProcessGetNumThreads(nub_process_t pid) DNB_EXPORT;
+nub_thread_t DNBProcessGetCurrentThread(nub_process_t pid) DNB_EXPORT;
+nub_thread_t DNBProcessGetCurrentThreadMachPort(nub_process_t pid) DNB_EXPORT;
+nub_thread_t DNBProcessSetCurrentThread(nub_process_t pid,
+ nub_thread_t tid) DNB_EXPORT;
+nub_thread_t DNBProcessGetThreadAtIndex(nub_process_t pid,
+ nub_size_t thread_idx) DNB_EXPORT;
+nub_bool_t DNBProcessSyncThreadState(nub_process_t pid,
+ nub_thread_t tid) DNB_EXPORT;
+nub_addr_t DNBProcessGetSharedLibraryInfoAddress(nub_process_t pid) DNB_EXPORT;
+nub_bool_t DNBProcessSharedLibrariesUpdated(nub_process_t pid) DNB_EXPORT;
+nub_size_t
+DNBProcessGetSharedLibraryInfo(nub_process_t pid, nub_bool_t only_changed,
+ DNBExecutableImageInfo **image_infos) DNB_EXPORT;
+const char *DNBGetDeploymentInfo(nub_process_t pid,
+ const struct load_command& lc,
+ uint64_t load_command_address,
+ uint32_t& major_version,
+ uint32_t& minor_version,
+ uint32_t& patch_version);
+nub_bool_t DNBProcessSetNameToAddressCallback(nub_process_t pid,
+ DNBCallbackNameToAddress callback,
+ void *baton) DNB_EXPORT;
+nub_bool_t DNBProcessSetSharedLibraryInfoCallback(
+ nub_process_t pid, DNBCallbackCopyExecutableImageInfos callback,
+ void *baton) DNB_EXPORT;
+nub_addr_t DNBProcessLookupAddress(nub_process_t pid, const char *name,
+ const char *shlib) DNB_EXPORT;
+nub_size_t DNBProcessGetAvailableSTDOUT(nub_process_t pid, char *buf,
+ nub_size_t buf_size) DNB_EXPORT;
+nub_size_t DNBProcessGetAvailableSTDERR(nub_process_t pid, char *buf,
+ nub_size_t buf_size) DNB_EXPORT;
+nub_size_t DNBProcessGetAvailableProfileData(nub_process_t pid, char *buf,
+ nub_size_t buf_size) DNB_EXPORT;
+nub_size_t DNBProcessGetStopCount(nub_process_t pid) DNB_EXPORT;
+uint32_t DNBProcessGetCPUType(nub_process_t pid) DNB_EXPORT;
+
+// Process executable and arguments
+const char *DNBProcessGetExecutablePath(nub_process_t pid);
+const char *DNBProcessGetArgumentAtIndex(nub_process_t pid, nub_size_t idx);
+nub_size_t DNBProcessGetArgumentCount(nub_process_t pid);
+
+// Process events
+nub_event_t DNBProcessWaitForEvents(nub_process_t pid, nub_event_t event_mask,
+ bool wait_for_set,
+ struct timespec *timeout);
+void DNBProcessResetEvents(nub_process_t pid, nub_event_t event_mask);
+
+// Thread functions
+const char *DNBThreadGetName(nub_process_t pid, nub_thread_t tid);
+nub_bool_t
+DNBThreadGetIdentifierInfo(nub_process_t pid, nub_thread_t tid,
+ thread_identifier_info_data_t *ident_info);
+nub_state_t DNBThreadGetState(nub_process_t pid, nub_thread_t tid);
+nub_bool_t DNBThreadGetRegisterValueByID(nub_process_t pid, nub_thread_t tid,
+ uint32_t set, uint32_t reg,
+ DNBRegisterValue *value);
+nub_bool_t DNBThreadSetRegisterValueByID(nub_process_t pid, nub_thread_t tid,
+ uint32_t set, uint32_t reg,
+ const DNBRegisterValue *value);
+nub_size_t DNBThreadGetRegisterContext(nub_process_t pid, nub_thread_t tid,
+ void *buf, size_t buf_len);
+nub_size_t DNBThreadSetRegisterContext(nub_process_t pid, nub_thread_t tid,
+ const void *buf, size_t buf_len);
+uint32_t DNBThreadSaveRegisterState(nub_process_t pid, nub_thread_t tid);
+nub_bool_t DNBThreadRestoreRegisterState(nub_process_t pid, nub_thread_t tid,
+ uint32_t save_id);
+nub_bool_t DNBThreadGetRegisterValueByName(nub_process_t pid, nub_thread_t tid,
+ uint32_t set, const char *name,
+ DNBRegisterValue *value);
+nub_bool_t DNBThreadGetStopReason(nub_process_t pid, nub_thread_t tid,
+ DNBThreadStopInfo *stop_info);
+const char *DNBThreadGetInfo(nub_process_t pid, nub_thread_t tid);
+Genealogy::ThreadActivitySP DNBGetGenealogyInfoForThread(nub_process_t pid,
+ nub_thread_t tid,
+ bool &timed_out);
+Genealogy::ProcessExecutableInfoSP DNBGetGenealogyImageInfo(nub_process_t pid,
+ size_t idx);
+ThreadInfo::QoS DNBGetRequestedQoSForThread(nub_process_t pid, nub_thread_t tid,
+ nub_addr_t tsd,
+ uint64_t dti_qos_class_index);
+nub_addr_t DNBGetPThreadT(nub_process_t pid, nub_thread_t tid);
+nub_addr_t DNBGetDispatchQueueT(nub_process_t pid, nub_thread_t tid);
+nub_addr_t
+DNBGetTSDAddressForThread(nub_process_t pid, nub_thread_t tid,
+ uint64_t plo_pthread_tsd_base_address_offset,
+ uint64_t plo_pthread_tsd_base_offset,
+ uint64_t plo_pthread_tsd_entry_size);
+JSONGenerator::ObjectSP DNBGetLoadedDynamicLibrariesInfos(
+ nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count);
+JSONGenerator::ObjectSP DNBGetAllLoadedLibrariesInfos(nub_process_t pid);
+JSONGenerator::ObjectSP
+DNBGetLibrariesInfoForAddresses(nub_process_t pid,
+ std::vector<uint64_t> &macho_addresses);
+JSONGenerator::ObjectSP DNBGetSharedCacheInfo(nub_process_t pid);
+
+//
+// Breakpoint functions
+nub_bool_t DNBBreakpointSet(nub_process_t pid, nub_addr_t addr, nub_size_t size,
+ nub_bool_t hardware);
+nub_bool_t DNBBreakpointClear(nub_process_t pid, nub_addr_t addr);
+
+// Watchpoint functions
+nub_bool_t DNBWatchpointSet(nub_process_t pid, nub_addr_t addr, nub_size_t size,
+ uint32_t watch_flags, nub_bool_t hardware);
+nub_bool_t DNBWatchpointClear(nub_process_t pid, nub_addr_t addr);
+uint32_t DNBWatchpointGetNumSupportedHWP(nub_process_t pid);
+
+uint32_t DNBGetRegisterCPUType();
+const DNBRegisterSetInfo *DNBGetRegisterSetInfo(nub_size_t *num_reg_sets);
+nub_bool_t DNBGetRegisterInfoByName(const char *reg_name,
+ DNBRegisterInfo *info);
+
+// Other static nub information calls.
+const char *DNBStateAsString(nub_state_t state);
+nub_bool_t DNBResolveExecutablePath(const char *path, char *resolved_path,
+ size_t resolved_path_size);
+bool DNBGetOSVersionNumbers(uint64_t *major, uint64_t *minor, uint64_t *patch);
+/// \return the iOSSupportVersion of the host OS.
+std::string DNBGetMacCatalystVersionString();
+#endif
diff --git a/gnu/llvm/lldb/tools/debugserver/source/DNBArch.cpp b/gnu/llvm/lldb/tools/debugserver/source/DNBArch.cpp
new file mode 100644
index 00000000000..931d623647f
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/DNBArch.cpp
@@ -0,0 +1,79 @@
+//===-- DNBArch.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/24/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DNBArch.h"
+#include <assert.h>
+#include <mach/mach.h>
+
+#include <map>
+
+#include "DNBLog.h"
+
+typedef std::map<uint32_t, DNBArchPluginInfo> CPUPluginInfoMap;
+
+static uint32_t g_current_cpu_type = 0;
+CPUPluginInfoMap g_arch_plugins;
+
+static const DNBArchPluginInfo *GetArchInfo() {
+ CPUPluginInfoMap::const_iterator pos =
+ g_arch_plugins.find(g_current_cpu_type);
+ if (pos != g_arch_plugins.end())
+ return &pos->second;
+ return NULL;
+}
+
+uint32_t DNBArchProtocol::GetArchitecture() { return g_current_cpu_type; }
+
+bool DNBArchProtocol::SetArchitecture(uint32_t cpu_type) {
+ g_current_cpu_type = cpu_type;
+ bool result = g_arch_plugins.find(g_current_cpu_type) != g_arch_plugins.end();
+ DNBLogThreadedIf(
+ LOG_PROCESS,
+ "DNBArchProtocol::SetDefaultArchitecture (cpu_type=0x%8.8x) => %i",
+ cpu_type, result);
+ return result;
+}
+
+void DNBArchProtocol::RegisterArchPlugin(const DNBArchPluginInfo &arch_info) {
+ if (arch_info.cpu_type)
+ g_arch_plugins[arch_info.cpu_type] = arch_info;
+}
+
+uint32_t DNBArchProtocol::GetRegisterCPUType() {
+ const DNBArchPluginInfo *arch_info = GetArchInfo();
+ if (arch_info)
+ return arch_info->cpu_type;
+ return 0;
+}
+
+const DNBRegisterSetInfo *
+DNBArchProtocol::GetRegisterSetInfo(nub_size_t *num_reg_sets) {
+ const DNBArchPluginInfo *arch_info = GetArchInfo();
+ if (arch_info)
+ return arch_info->GetRegisterSetInfo(num_reg_sets);
+ *num_reg_sets = 0;
+ return NULL;
+}
+
+DNBArchProtocol *DNBArchProtocol::Create(MachThread *thread) {
+ const DNBArchPluginInfo *arch_info = GetArchInfo();
+ if (arch_info)
+ return arch_info->Create(thread);
+ return NULL;
+}
+
+const uint8_t *DNBArchProtocol::GetBreakpointOpcode(nub_size_t byte_size) {
+ const DNBArchPluginInfo *arch_info = GetArchInfo();
+ if (arch_info)
+ return arch_info->GetBreakpointOpcode(byte_size);
+ return NULL;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/DNBArch.h b/gnu/llvm/lldb/tools/debugserver/source/DNBArch.h
new file mode 100644
index 00000000000..b5e2e25ef47
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/DNBArch.h
@@ -0,0 +1,126 @@
+//===-- DNBArch.h -----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/24/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DebugNubArch_h__
+#define __DebugNubArch_h__
+
+#include "DNBDefs.h"
+#include "MacOSX/MachException.h"
+
+#include <mach/mach.h>
+#include <stdio.h>
+
+struct DNBRegisterValue;
+struct DNBRegisterSetInfo;
+class DNBArchProtocol;
+class MachThread;
+
+typedef DNBArchProtocol *(*DNBArchCallbackCreate)(MachThread *thread);
+typedef const DNBRegisterSetInfo *(*DNBArchCallbackGetRegisterSetInfo)(
+ nub_size_t *num_reg_sets);
+typedef const uint8_t *(*DNBArchCallbackGetBreakpointOpcode)(
+ nub_size_t byte_size);
+
+typedef struct DNBArchPluginInfoTag {
+ uint32_t cpu_type;
+ DNBArchCallbackCreate Create;
+ DNBArchCallbackGetRegisterSetInfo GetRegisterSetInfo;
+ DNBArchCallbackGetBreakpointOpcode GetBreakpointOpcode;
+} DNBArchPluginInfo;
+
+class DNBArchProtocol {
+public:
+ static DNBArchProtocol *Create(MachThread *thread);
+
+ static uint32_t GetRegisterCPUType();
+
+ static const DNBRegisterSetInfo *GetRegisterSetInfo(nub_size_t *num_reg_sets);
+
+ static const uint8_t *GetBreakpointOpcode(nub_size_t byte_size);
+
+ static void RegisterArchPlugin(const DNBArchPluginInfo &arch_info);
+
+ static uint32_t GetArchitecture();
+
+ static bool SetArchitecture(uint32_t cpu_type);
+
+ DNBArchProtocol() : m_save_id(0) {}
+
+ virtual ~DNBArchProtocol() {}
+ virtual bool GetRegisterValue(uint32_t set, uint32_t reg,
+ DNBRegisterValue *value) = 0;
+ virtual bool SetRegisterValue(uint32_t set, uint32_t reg,
+ const DNBRegisterValue *value) = 0;
+ virtual nub_size_t GetRegisterContext(void *buf, nub_size_t buf_len) = 0;
+ virtual nub_size_t SetRegisterContext(const void *buf,
+ nub_size_t buf_len) = 0;
+ virtual uint32_t SaveRegisterState() = 0;
+ virtual bool RestoreRegisterState(uint32_t save_id) = 0;
+
+ virtual kern_return_t GetRegisterState(int set, bool force) = 0;
+ virtual kern_return_t SetRegisterState(int set) = 0;
+ virtual bool RegisterSetStateIsValid(int set) const = 0;
+
+ virtual uint64_t GetPC(uint64_t failValue) = 0; // Get program counter
+ virtual kern_return_t SetPC(uint64_t value) = 0;
+ virtual uint64_t GetSP(uint64_t failValue) = 0; // Get stack pointer
+ virtual void ThreadWillResume() = 0;
+ virtual bool ThreadDidStop() = 0;
+ virtual bool NotifyException(MachException::Data &exc) { return false; }
+ virtual uint32_t NumSupportedHardwareBreakpoints() { return 0; }
+ virtual uint32_t NumSupportedHardwareWatchpoints() { return 0; }
+ virtual uint32_t EnableHardwareBreakpoint(nub_addr_t addr, nub_size_t size) {
+ return INVALID_NUB_HW_INDEX;
+ }
+ virtual uint32_t EnableHardwareWatchpoint(nub_addr_t addr, nub_size_t size,
+ bool read, bool write,
+ bool also_set_on_task) {
+ return INVALID_NUB_HW_INDEX;
+ }
+ virtual bool DisableHardwareBreakpoint(uint32_t hw_index) { return false; }
+ virtual bool DisableHardwareWatchpoint(uint32_t hw_index,
+ bool also_set_on_task) {
+ return false;
+ }
+ virtual uint32_t GetHardwareWatchpointHit(nub_addr_t &addr) {
+ return INVALID_NUB_HW_INDEX;
+ }
+ virtual bool StepNotComplete() { return false; }
+
+protected:
+ friend class MachThread;
+
+ uint32_t GetNextRegisterStateSaveID() { return ++m_save_id; }
+
+ enum {
+ Trans_Pending =
+ 0, // Transaction is pending, and checkpoint state has been snapshotted.
+ Trans_Done = 1, // Transaction is done, the current state is committed, and
+ // checkpoint state is irrelevant.
+ Trans_Rolled_Back = 2 // Transaction is done, the current state has been
+ // rolled back to the checkpoint state.
+ };
+ virtual bool StartTransForHWP() { return true; }
+ virtual bool RollbackTransForHWP() { return true; }
+ virtual bool FinishTransForHWP() { return true; }
+
+ uint32_t m_save_id; // An always incrementing integer ID used with
+ // SaveRegisterState/RestoreRegisterState
+};
+
+#include "MacOSX/arm/DNBArchImpl.h"
+#include "MacOSX/arm64/DNBArchImplARM64.h"
+#include "MacOSX/i386/DNBArchImplI386.h"
+#include "MacOSX/ppc/DNBArchImpl.h"
+#include "MacOSX/x86_64/DNBArchImplX86_64.h"
+
+#endif
diff --git a/gnu/llvm/lldb/tools/debugserver/source/DNBBreakpoint.cpp b/gnu/llvm/lldb/tools/debugserver/source/DNBBreakpoint.cpp
new file mode 100644
index 00000000000..890bde024bf
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/DNBBreakpoint.cpp
@@ -0,0 +1,177 @@
+//===-- DNBBreakpoint.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/29/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DNBBreakpoint.h"
+#include "DNBLog.h"
+#include "MachProcess.h"
+#include <algorithm>
+#include <assert.h>
+#include <inttypes.h>
+
+#pragma mark-- DNBBreakpoint
+DNBBreakpoint::DNBBreakpoint(nub_addr_t addr, nub_size_t byte_size,
+ bool hardware)
+ : m_retain_count(1), m_byte_size(static_cast<uint32_t>(byte_size)),
+ m_opcode(), m_addr(addr), m_enabled(0), m_hw_preferred(hardware),
+ m_is_watchpoint(0), m_watch_read(0), m_watch_write(0),
+ m_hw_index(INVALID_NUB_HW_INDEX) {}
+
+DNBBreakpoint::~DNBBreakpoint() {}
+
+void DNBBreakpoint::Dump() const {
+ if (IsBreakpoint()) {
+ DNBLog("DNBBreakpoint addr = 0x%llx state = %s type = %s breakpoint "
+ "hw_index = %i",
+ (uint64_t)m_addr, m_enabled ? "enabled " : "disabled",
+ IsHardware() ? "hardware" : "software", GetHardwareIndex());
+ } else {
+ DNBLog("DNBBreakpoint addr = 0x%llx size = %llu state = %s type = %s "
+ "watchpoint (%s%s) hw_index = %i",
+ (uint64_t)m_addr, (uint64_t)m_byte_size,
+ m_enabled ? "enabled " : "disabled",
+ IsHardware() ? "hardware" : "software", m_watch_read ? "r" : "",
+ m_watch_write ? "w" : "", GetHardwareIndex());
+ }
+}
+
+#pragma mark-- DNBBreakpointList
+
+DNBBreakpointList::DNBBreakpointList() {}
+
+DNBBreakpointList::~DNBBreakpointList() {}
+
+DNBBreakpoint *DNBBreakpointList::Add(nub_addr_t addr, nub_size_t length,
+ bool hardware) {
+ m_breakpoints.insert(
+ std::make_pair(addr, DNBBreakpoint(addr, length, hardware)));
+ iterator pos = m_breakpoints.find(addr);
+ return &pos->second;
+}
+
+bool DNBBreakpointList::Remove(nub_addr_t addr) {
+ iterator pos = m_breakpoints.find(addr);
+ if (pos != m_breakpoints.end()) {
+ m_breakpoints.erase(pos);
+ return true;
+ }
+ return false;
+}
+
+DNBBreakpoint *DNBBreakpointList::FindByAddress(nub_addr_t addr) {
+ iterator pos = m_breakpoints.find(addr);
+ if (pos != m_breakpoints.end())
+ return &pos->second;
+
+ return NULL;
+}
+
+const DNBBreakpoint *DNBBreakpointList::FindByAddress(nub_addr_t addr) const {
+ const_iterator pos = m_breakpoints.find(addr);
+ if (pos != m_breakpoints.end())
+ return &pos->second;
+
+ return NULL;
+}
+
+// Finds the next breakpoint at an address greater than or equal to "addr"
+size_t DNBBreakpointList::FindBreakpointsThatOverlapRange(
+ nub_addr_t addr, nub_addr_t size, std::vector<DNBBreakpoint *> &bps) {
+ bps.clear();
+ iterator end = m_breakpoints.end();
+ // Find the first breakpoint with an address >= to "addr"
+ iterator pos = m_breakpoints.lower_bound(addr);
+ if (pos != end) {
+ if (pos != m_breakpoints.begin()) {
+ // Watch out for a breakpoint at an address less than "addr" that might
+ // still overlap
+ iterator prev_pos = pos;
+ --prev_pos;
+ if (prev_pos->second.IntersectsRange(addr, size, NULL, NULL, NULL))
+ bps.push_back(&pos->second);
+ }
+
+ while (pos != end) {
+ // When we hit a breakpoint whose start address is greater than "addr +
+ // size" we are done.
+ // Do the math in a way that doesn't risk unsigned overflow with bad
+ // input.
+ if ((pos->second.Address() - addr) >= size)
+ break;
+
+ // Check if this breakpoint overlaps, and if it does, add it to the list
+ if (pos->second.IntersectsRange(addr, size, NULL, NULL, NULL)) {
+ bps.push_back(&pos->second);
+ ++pos;
+ }
+ }
+ }
+ return bps.size();
+}
+
+void DNBBreakpointList::Dump() const {
+ const_iterator pos;
+ const_iterator end = m_breakpoints.end();
+ for (pos = m_breakpoints.begin(); pos != end; ++pos)
+ pos->second.Dump();
+}
+
+void DNBBreakpointList::DisableAll() {
+ iterator pos, end = m_breakpoints.end();
+ for (pos = m_breakpoints.begin(); pos != end; ++pos)
+ pos->second.SetEnabled(false);
+}
+
+void DNBBreakpointList::RemoveTrapsFromBuffer(nub_addr_t addr, nub_size_t size,
+ void *p) const {
+ uint8_t *buf = (uint8_t *)p;
+ const_iterator end = m_breakpoints.end();
+ const_iterator pos = m_breakpoints.lower_bound(addr);
+ while (pos != end && (pos->first < (addr + size))) {
+ nub_addr_t intersect_addr;
+ nub_size_t intersect_size;
+ nub_size_t opcode_offset;
+ const DNBBreakpoint &bp = pos->second;
+ if (bp.IntersectsRange(addr, size, &intersect_addr, &intersect_size,
+ &opcode_offset)) {
+ assert(addr <= intersect_addr && intersect_addr < addr + size);
+ assert(addr < intersect_addr + intersect_size &&
+ intersect_addr + intersect_size <= addr + size);
+ assert(opcode_offset + intersect_size <= bp.ByteSize());
+ nub_size_t buf_offset = intersect_addr - addr;
+ ::memcpy(buf + buf_offset, bp.SavedOpcodeBytes() + opcode_offset,
+ intersect_size);
+ }
+ ++pos;
+ }
+}
+
+void DNBBreakpointList::DisableAllBreakpoints(MachProcess *process) {
+ iterator pos, end = m_breakpoints.end();
+ for (pos = m_breakpoints.begin(); pos != end; ++pos)
+ process->DisableBreakpoint(pos->second.Address(), false);
+}
+
+void DNBBreakpointList::DisableAllWatchpoints(MachProcess *process) {
+ iterator pos, end = m_breakpoints.end();
+ for (pos = m_breakpoints.begin(); pos != end; ++pos)
+ process->DisableWatchpoint(pos->second.Address(), false);
+}
+
+void DNBBreakpointList::RemoveDisabled() {
+ iterator pos = m_breakpoints.begin();
+ while (pos != m_breakpoints.end()) {
+ if (!pos->second.IsEnabled())
+ pos = m_breakpoints.erase(pos);
+ else
+ ++pos;
+ }
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/DNBBreakpoint.h b/gnu/llvm/lldb/tools/debugserver/source/DNBBreakpoint.h
new file mode 100644
index 00000000000..0b4e077ae8a
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/DNBBreakpoint.h
@@ -0,0 +1,148 @@
+//===-- DNBBreakpoint.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/29/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNBBreakpoint_h__
+#define __DNBBreakpoint_h__
+
+#include <mach/mach.h>
+
+#include <map>
+#include <vector>
+
+#include "DNBDefs.h"
+
+class MachProcess;
+
+class DNBBreakpoint {
+public:
+ DNBBreakpoint(nub_addr_t m_addr, nub_size_t byte_size, bool hardware);
+ ~DNBBreakpoint();
+
+ nub_size_t ByteSize() const { return m_byte_size; }
+ uint8_t *SavedOpcodeBytes() { return &m_opcode[0]; }
+ const uint8_t *SavedOpcodeBytes() const { return &m_opcode[0]; }
+ nub_addr_t Address() const { return m_addr; }
+ // nub_thread_t ThreadID() const { return m_tid; }
+ bool IsEnabled() const { return m_enabled; }
+ bool IntersectsRange(nub_addr_t addr, nub_size_t size,
+ nub_addr_t *intersect_addr, nub_size_t *intersect_size,
+ nub_size_t *opcode_offset) const {
+ // We only use software traps for software breakpoints
+ if (IsBreakpoint() && IsEnabled() && !IsHardware()) {
+ if (m_byte_size > 0) {
+ const nub_addr_t bp_end_addr = m_addr + m_byte_size;
+ const nub_addr_t end_addr = addr + size;
+ // Is the breakpoint end address before the passed in start address?
+ if (bp_end_addr <= addr)
+ return false;
+ // Is the breakpoint start address after passed in end address?
+ if (end_addr <= m_addr)
+ return false;
+ if (intersect_addr || intersect_size || opcode_offset) {
+ if (m_addr < addr) {
+ if (intersect_addr)
+ *intersect_addr = addr;
+ if (intersect_size)
+ *intersect_size =
+ std::min<nub_addr_t>(bp_end_addr, end_addr) - addr;
+ if (opcode_offset)
+ *opcode_offset = addr - m_addr;
+ } else {
+ if (intersect_addr)
+ *intersect_addr = m_addr;
+ if (intersect_size)
+ *intersect_size =
+ std::min<nub_addr_t>(bp_end_addr, end_addr) - m_addr;
+ if (opcode_offset)
+ *opcode_offset = 0;
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+ void SetEnabled(bool enabled) {
+ if (!enabled)
+ SetHardwareIndex(INVALID_NUB_HW_INDEX);
+ m_enabled = enabled;
+ }
+ void SetIsWatchpoint(uint32_t type) {
+ m_is_watchpoint = 1;
+ m_watch_read = (type & WATCH_TYPE_READ) != 0;
+ m_watch_write = (type & WATCH_TYPE_WRITE) != 0;
+ }
+ bool IsBreakpoint() const { return m_is_watchpoint == 0; }
+ bool IsWatchpoint() const { return m_is_watchpoint == 1; }
+ bool WatchpointRead() const { return m_watch_read != 0; }
+ bool WatchpointWrite() const { return m_watch_write != 0; }
+ bool HardwarePreferred() const { return m_hw_preferred; }
+ bool IsHardware() const { return m_hw_index != INVALID_NUB_HW_INDEX; }
+ uint32_t GetHardwareIndex() const { return m_hw_index; }
+ void SetHardwareIndex(uint32_t hw_index) { m_hw_index = hw_index; }
+ void Dump() const;
+ uint32_t Retain() { return ++m_retain_count; }
+ uint32_t Release() {
+ if (m_retain_count == 0)
+ return 0;
+ return --m_retain_count;
+ }
+
+private:
+ uint32_t m_retain_count; // Each breakpoint is maintained by address and is
+ // ref counted in case multiple people set a
+ // breakpoint at the same address
+ uint32_t m_byte_size; // Length in bytes of the breakpoint if set in memory
+ uint8_t m_opcode[8]; // Saved opcode bytes
+ nub_addr_t m_addr; // Address of this breakpoint
+ uint32_t m_enabled : 1, // Flags for this breakpoint
+ m_hw_preferred : 1, // 1 if this point has been requested to be set using
+ // hardware (which may fail due to lack of resources)
+ m_is_watchpoint : 1, // 1 if this is a watchpoint
+ m_watch_read : 1, // 1 if we stop when the watched data is read from
+ m_watch_write : 1; // 1 if we stop when the watched data is written to
+ uint32_t
+ m_hw_index; // The hardware resource index for this breakpoint/watchpoint
+};
+
+class DNBBreakpointList {
+public:
+ DNBBreakpointList();
+ ~DNBBreakpointList();
+
+ DNBBreakpoint *Add(nub_addr_t addr, nub_size_t length, bool hardware);
+ bool Remove(nub_addr_t addr);
+ DNBBreakpoint *FindByAddress(nub_addr_t addr);
+ const DNBBreakpoint *FindByAddress(nub_addr_t addr) const;
+
+ size_t FindBreakpointsThatOverlapRange(nub_addr_t addr, nub_addr_t size,
+ std::vector<DNBBreakpoint *> &bps);
+
+ void Dump() const;
+
+ size_t Size() const { return m_breakpoints.size(); }
+ void DisableAll();
+
+ void RemoveTrapsFromBuffer(nub_addr_t addr, nub_size_t size, void *buf) const;
+
+ void DisableAllBreakpoints(MachProcess *process);
+ void DisableAllWatchpoints(MachProcess *process);
+ void RemoveDisabled();
+
+protected:
+ typedef std::map<nub_addr_t, DNBBreakpoint> collection;
+ typedef collection::iterator iterator;
+ typedef collection::const_iterator const_iterator;
+ collection m_breakpoints;
+};
+
+#endif
diff --git a/gnu/llvm/lldb/tools/debugserver/source/DNBDataRef.cpp b/gnu/llvm/lldb/tools/debugserver/source/DNBDataRef.cpp
new file mode 100644
index 00000000000..f19c913f65d
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/DNBDataRef.cpp
@@ -0,0 +1,320 @@
+//===-- DNBDataRef.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/11/06.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DNBDataRef.h"
+#include "DNBLog.h"
+#include <assert.h>
+#include <ctype.h>
+#include <libkern/OSByteOrder.h>
+
+// Constructor
+
+DNBDataRef::DNBDataRef()
+ : m_start(NULL), m_end(NULL), m_swap(false), m_ptrSize(0),
+ m_addrPCRelative(INVALID_NUB_ADDRESS), m_addrTEXT(INVALID_NUB_ADDRESS),
+ m_addrDATA(INVALID_NUB_ADDRESS) {}
+
+// Constructor
+
+DNBDataRef::DNBDataRef(const uint8_t *start, size_t size, bool swap)
+ : m_start(start), m_end(start + size), m_swap(swap), m_ptrSize(0),
+ m_addrPCRelative(INVALID_NUB_ADDRESS), m_addrTEXT(INVALID_NUB_ADDRESS),
+ m_addrDATA(INVALID_NUB_ADDRESS) {}
+
+// Destructor
+
+DNBDataRef::~DNBDataRef() {}
+
+// Get8
+uint8_t DNBDataRef::Get8(offset_t *offset_ptr) const {
+ uint8_t val = 0;
+ if (ValidOffsetForDataOfSize(*offset_ptr, sizeof(val))) {
+ val = *(m_start + *offset_ptr);
+ *offset_ptr += sizeof(val);
+ }
+ return val;
+}
+
+// Get16
+uint16_t DNBDataRef::Get16(offset_t *offset_ptr) const {
+ uint16_t val = 0;
+ if (ValidOffsetForDataOfSize(*offset_ptr, sizeof(val))) {
+ const uint8_t *p = m_start + *offset_ptr;
+ memcpy(&val, p, sizeof(uint16_t));
+
+ if (m_swap)
+ val = OSSwapInt16(val);
+
+ // Advance the offset
+ *offset_ptr += sizeof(val);
+ }
+ return val;
+}
+
+// Get32
+uint32_t DNBDataRef::Get32(offset_t *offset_ptr) const {
+ uint32_t val = 0;
+ if (ValidOffsetForDataOfSize(*offset_ptr, sizeof(val))) {
+ const uint8_t *p = m_start + *offset_ptr;
+ memcpy(&val, p, sizeof(uint32_t));
+ if (m_swap)
+ val = OSSwapInt32(val);
+
+ // Advance the offset
+ *offset_ptr += sizeof(val);
+ }
+ return val;
+}
+
+// Get64
+uint64_t DNBDataRef::Get64(offset_t *offset_ptr) const {
+ uint64_t val = 0;
+ if (ValidOffsetForDataOfSize(*offset_ptr, sizeof(val))) {
+ const uint8_t *p = m_start + *offset_ptr;
+ memcpy(&val, p, sizeof(uint64_t));
+ if (m_swap)
+ val = OSSwapInt64(val);
+
+ // Advance the offset
+ *offset_ptr += sizeof(val);
+ }
+ return val;
+}
+
+// GetMax32
+//
+// Used for calls when the size can vary. Fill in extra cases if they
+// are ever needed.
+uint32_t DNBDataRef::GetMax32(offset_t *offset_ptr, uint32_t byte_size) const {
+ switch (byte_size) {
+ case 1:
+ return Get8(offset_ptr);
+ break;
+ case 2:
+ return Get16(offset_ptr);
+ break;
+ case 4:
+ return Get32(offset_ptr);
+ break;
+ default:
+ assert(false && "GetMax32 unhandled case!");
+ break;
+ }
+ return 0;
+}
+
+// GetMax64
+//
+// Used for calls when the size can vary. Fill in extra cases if they
+// are ever needed.
+uint64_t DNBDataRef::GetMax64(offset_t *offset_ptr, uint32_t size) const {
+ switch (size) {
+ case 1:
+ return Get8(offset_ptr);
+ break;
+ case 2:
+ return Get16(offset_ptr);
+ break;
+ case 4:
+ return Get32(offset_ptr);
+ break;
+ case 8:
+ return Get64(offset_ptr);
+ break;
+ default:
+ assert(false && "GetMax64 unhandled case!");
+ break;
+ }
+ return 0;
+}
+
+// GetPointer
+//
+// Extract a pointer value from the buffer. The pointer size must be
+// set prior to using this using one of the SetPointerSize functions.
+uint64_t DNBDataRef::GetPointer(offset_t *offset_ptr) const {
+ // Must set pointer size prior to using this call
+ assert(m_ptrSize != 0);
+ return GetMax64(offset_ptr, m_ptrSize);
+}
+// GetCStr
+const char *DNBDataRef::GetCStr(offset_t *offset_ptr,
+ uint32_t fixed_length) const {
+ const char *s = NULL;
+ if (m_start < m_end) {
+ s = (const char *)m_start + *offset_ptr;
+
+ // Advance the offset
+ if (fixed_length)
+ *offset_ptr += fixed_length;
+ else
+ *offset_ptr += strlen(s) + 1;
+ }
+ return s;
+}
+
+// GetData
+const uint8_t *DNBDataRef::GetData(offset_t *offset_ptr,
+ uint32_t length) const {
+ const uint8_t *data = NULL;
+ if (length > 0 && ValidOffsetForDataOfSize(*offset_ptr, length)) {
+ data = m_start + *offset_ptr;
+ *offset_ptr += length;
+ }
+ return data;
+}
+
+// Get_ULEB128
+uint64_t DNBDataRef::Get_ULEB128(offset_t *offset_ptr) const {
+ uint64_t result = 0;
+ if (m_start < m_end) {
+ int shift = 0;
+ const uint8_t *src = m_start + *offset_ptr;
+ uint8_t byte;
+ int bytecount = 0;
+
+ while (src < m_end) {
+ bytecount++;
+ byte = *src++;
+ result |= (uint64_t)(byte & 0x7f) << shift;
+ shift += 7;
+ if ((byte & 0x80) == 0)
+ break;
+ }
+
+ *offset_ptr += bytecount;
+ }
+ return result;
+}
+
+// Get_SLEB128
+int64_t DNBDataRef::Get_SLEB128(offset_t *offset_ptr) const {
+ int64_t result = 0;
+
+ if (m_start < m_end) {
+ int shift = 0;
+ int size = sizeof(uint32_t) * 8;
+ const uint8_t *src = m_start + *offset_ptr;
+
+ uint8_t byte = 0;
+ int bytecount = 0;
+
+ while (src < m_end) {
+ bytecount++;
+ byte = *src++;
+ result |= (int64_t)(byte & 0x7f) << shift;
+ shift += 7;
+ if ((byte & 0x80) == 0)
+ break;
+ }
+
+ // Sign bit of byte is 2nd high order bit (0x40)
+ if (shift < size && (byte & 0x40))
+ result |= -(1ll << shift);
+
+ *offset_ptr += bytecount;
+ }
+ return result;
+}
+
+// Skip_LEB128
+//
+// Skips past ULEB128 and SLEB128 numbers (just updates the offset)
+void DNBDataRef::Skip_LEB128(offset_t *offset_ptr) const {
+ if (m_start < m_end) {
+ const uint8_t *start = m_start + *offset_ptr;
+ const uint8_t *src = start;
+
+ while ((src < m_end) && (*src++ & 0x80))
+ /* Do nothing */;
+
+ *offset_ptr += src - start;
+ }
+}
+
+uint32_t DNBDataRef::Dump(uint32_t startOffset, uint32_t endOffset,
+ uint64_t offsetBase, DNBDataRef::Type type,
+ uint32_t numPerLine, const char *format) {
+ uint32_t offset;
+ uint32_t count;
+ char str[1024];
+ str[0] = '\0';
+ size_t str_offset = 0;
+
+ for (offset = startOffset, count = 0;
+ ValidOffset(offset) && offset < endOffset; ++count) {
+ if ((count % numPerLine) == 0) {
+ // Print out any previous string
+ if (str[0] != '\0')
+ DNBLog("%s", str);
+ // Reset string offset and fill the current line string with address:
+ str_offset = 0;
+ str_offset += snprintf(str, sizeof(str), "0x%8.8llx:",
+ (uint64_t)(offsetBase + (offset - startOffset)));
+ }
+
+ // Make sure we don't pass the bounds of our current string buffer on each
+ // iteration through this loop
+ if (str_offset >= sizeof(str)) {
+ // The last snprintf consumed our string buffer, we will need to dump this
+ // out
+ // and reset the string with no address
+ DNBLog("%s", str);
+ str_offset = 0;
+ str[0] = '\0';
+ }
+
+ // We already checked that there is at least some room in the string str
+ // above, so it is safe to make
+ // the snprintf call each time through this loop
+ switch (type) {
+ case TypeUInt8:
+ str_offset += snprintf(str + str_offset, sizeof(str) - str_offset,
+ format ? format : " %2.2x", Get8(&offset));
+ break;
+ case TypeChar: {
+ char ch = Get8(&offset);
+ str_offset += snprintf(str + str_offset, sizeof(str) - str_offset,
+ format ? format : " %c", isprint(ch) ? ch : ' ');
+ } break;
+ case TypeUInt16:
+ str_offset += snprintf(str + str_offset, sizeof(str) - str_offset,
+ format ? format : " %4.4x", Get16(&offset));
+ break;
+ case TypeUInt32:
+ str_offset += snprintf(str + str_offset, sizeof(str) - str_offset,
+ format ? format : " %8.8x", Get32(&offset));
+ break;
+ case TypeUInt64:
+ str_offset += snprintf(str + str_offset, sizeof(str) - str_offset,
+ format ? format : " %16.16llx", Get64(&offset));
+ break;
+ case TypePointer:
+ str_offset += snprintf(str + str_offset, sizeof(str) - str_offset,
+ format ? format : " 0x%llx", GetPointer(&offset));
+ break;
+ case TypeULEB128:
+ str_offset += snprintf(str + str_offset, sizeof(str) - str_offset,
+ format ? format : " 0x%llx", Get_ULEB128(&offset));
+ break;
+ case TypeSLEB128:
+ str_offset += snprintf(str + str_offset, sizeof(str) - str_offset,
+ format ? format : " %lld", Get_SLEB128(&offset));
+ break;
+ }
+ }
+
+ if (str[0] != '\0')
+ DNBLog("%s", str);
+
+ return offset; // Return the offset at which we ended up
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/DNBDataRef.h b/gnu/llvm/lldb/tools/debugserver/source/DNBDataRef.h
new file mode 100644
index 00000000000..d521700d151
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/DNBDataRef.h
@@ -0,0 +1,124 @@
+//===-- DNBDataRef.h --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/11/06.
+//
+//===----------------------------------------------------------------------===//
+//
+// DNBDataRef is a class that can extract data in normal or byte
+// swapped order from a data buffer that someone else owns. The data
+// buffer needs to remain intact as long as the DNBDataRef object
+// needs the data. Strings returned are pointers into the data buffer
+// and will need to be copied if they are needed after the data buffer
+// is no longer around.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNBDataRef_h__
+#define __DNBDataRef_h__
+
+#include "DNBDefs.h"
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+class DNBDataRef {
+public:
+ // For use with Dump
+ enum Type {
+ TypeUInt8 = 0,
+ TypeChar,
+ TypeUInt16,
+ TypeUInt32,
+ TypeUInt64,
+ TypePointer,
+ TypeULEB128,
+ TypeSLEB128
+ };
+ typedef uint32_t offset_t;
+ typedef nub_addr_t addr_t;
+
+ DNBDataRef();
+ DNBDataRef(const uint8_t *start, size_t size, bool swap);
+ ~DNBDataRef();
+ void Clear() {
+ DNBDataRef::SetData(NULL, 0);
+ m_swap = false;
+ }
+
+ size_t BytesLeft(size_t offset) const {
+ const size_t size = GetSize();
+ if (size > offset)
+ return size - offset;
+ return 0;
+ }
+
+ bool ValidOffset(offset_t offset) const { return BytesLeft(offset) > 0; }
+ bool ValidOffsetForDataOfSize(offset_t offset, uint32_t num_bytes) const {
+ return num_bytes <= BytesLeft(offset);
+ }
+ size_t GetSize() const { return m_end - m_start; }
+ const uint8_t *GetDataStart() const { return m_start; }
+ const uint8_t *GetDataEnd() const { return m_end; }
+ bool GetSwap() const { return m_swap; }
+ void SetSwap(bool swap) { m_swap = swap; }
+ void SetData(const uint8_t *start, size_t size) {
+ m_start = start;
+ if (m_start != NULL)
+ m_end = start + size;
+ else
+ m_end = NULL;
+ }
+ uint8_t GetPointerSize() const { return m_ptrSize; }
+ void SetPointerSize(uint8_t size) { m_ptrSize = size; }
+ void SetEHPtrBaseAddrPCRelative(addr_t addr = INVALID_NUB_ADDRESS) {
+ m_addrPCRelative = addr;
+ }
+ void SetEHPtrBaseAddrTEXT(addr_t addr = INVALID_NUB_ADDRESS) {
+ m_addrTEXT = addr;
+ }
+ void SetEHPtrBaseAddrDATA(addr_t addr = INVALID_NUB_ADDRESS) {
+ m_addrDATA = addr;
+ }
+ uint8_t Get8(offset_t *offset_ptr) const;
+ uint16_t Get16(offset_t *offset_ptr) const;
+ uint32_t Get32(offset_t *offset_ptr) const;
+ uint64_t Get64(offset_t *offset_ptr) const;
+ uint32_t GetMax32(offset_t *offset_ptr, uint32_t byte_size) const;
+ uint64_t GetMax64(offset_t *offset_ptr, uint32_t byte_size) const;
+ uint64_t GetPointer(offset_t *offset_ptr) const;
+ // uint64_t GetDwarfEHPtr(offset_t *offset_ptr, uint32_t eh_ptr_enc)
+ // const;
+ const char *GetCStr(offset_t *offset_ptr, uint32_t fixed_length = 0) const;
+ const char *PeekCStr(offset_t offset) const {
+ if (ValidOffset(offset))
+ return (const char *)m_start + offset;
+ return NULL;
+ }
+
+ const uint8_t *GetData(offset_t *offset_ptr, uint32_t length) const;
+ uint64_t Get_ULEB128(offset_t *offset_ptr) const;
+ int64_t Get_SLEB128(offset_t *offset_ptr) const;
+ void Skip_LEB128(offset_t *offset_ptr) const;
+
+ uint32_t Dump(offset_t startOffset, offset_t endOffset, uint64_t offsetBase,
+ DNBDataRef::Type type, uint32_t numPerLine,
+ const char *typeFormat = NULL);
+
+protected:
+ const uint8_t *m_start;
+ const uint8_t *m_end;
+ bool m_swap;
+ uint8_t m_ptrSize;
+ addr_t m_addrPCRelative;
+ addr_t m_addrTEXT;
+ addr_t m_addrDATA;
+};
+
+#endif // #ifndef __DNBDataRef_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/DNBDefs.h b/gnu/llvm/lldb/tools/debugserver/source/DNBDefs.h
new file mode 100644
index 00000000000..22cfce1757f
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/DNBDefs.h
@@ -0,0 +1,363 @@
+//===-- DNBDefs.h -----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/26/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNBDefs_h__
+#define __DNBDefs_h__
+
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/syslimits.h>
+#include <unistd.h>
+
+// Define nub_addr_t and the invalid address value from the architecture
+#if defined(__x86_64__) || defined(__ppc64__) || defined(__arm64__) || \
+ defined(__aarch64__)
+
+// 64 bit address architectures
+typedef uint64_t nub_addr_t;
+#define INVALID_NUB_ADDRESS ((nub_addr_t)~0ull)
+
+#elif defined(__i386__) || defined(__powerpc__) || defined(__ppc__) || \
+ defined(__arm__)
+
+// 32 bit address architectures
+
+typedef uint32_t nub_addr_t;
+#define INVALID_NUB_ADDRESS ((nub_addr_t)~0ul)
+
+#else
+
+// Default to 64 bit address for unrecognized architectures.
+
+#warning undefined architecture, defaulting to 8 byte addresses
+typedef uint64_t nub_addr_t;
+#define INVALID_NUB_ADDRESS ((nub_addr_t)~0ull)
+
+#endif
+
+typedef size_t nub_size_t;
+typedef ssize_t nub_ssize_t;
+typedef uint32_t nub_index_t;
+typedef pid_t nub_process_t;
+typedef uint64_t nub_thread_t;
+typedef uint32_t nub_event_t;
+typedef uint32_t nub_bool_t;
+
+#define INVALID_NUB_PROCESS ((nub_process_t)0)
+#define INVALID_NUB_THREAD ((nub_thread_t)0)
+#define INVALID_NUB_WATCH_ID ((nub_watch_t)0)
+#define INVALID_NUB_HW_INDEX UINT32_MAX
+#define INVALID_NUB_REGNUM UINT32_MAX
+#define NUB_GENERIC_ERROR UINT32_MAX
+
+// Watchpoint types
+#define WATCH_TYPE_READ (1u << 0)
+#define WATCH_TYPE_WRITE (1u << 1)
+
+enum nub_state_t {
+ eStateInvalid = 0,
+ eStateUnloaded,
+ eStateAttaching,
+ eStateLaunching,
+ eStateStopped,
+ eStateRunning,
+ eStateStepping,
+ eStateCrashed,
+ eStateDetached,
+ eStateExited,
+ eStateSuspended
+};
+
+enum nub_launch_flavor_t {
+ eLaunchFlavorDefault = 0,
+ eLaunchFlavorPosixSpawn = 1,
+ eLaunchFlavorForkExec = 2,
+#ifdef WITH_SPRINGBOARD
+ eLaunchFlavorSpringBoard = 3,
+#endif
+#ifdef WITH_BKS
+ eLaunchFlavorBKS = 4,
+#endif
+#ifdef WITH_FBS
+ eLaunchFlavorFBS = 5
+#endif
+};
+
+#define NUB_STATE_IS_RUNNING(s) \
+ ((s) == eStateAttaching || (s) == eStateLaunching || (s) == eStateRunning || \
+ (s) == eStateStepping || (s) == eStateDetached)
+
+#define NUB_STATE_IS_STOPPED(s) \
+ ((s) == eStateUnloaded || (s) == eStateStopped || (s) == eStateCrashed || \
+ (s) == eStateExited)
+
+enum {
+ eEventProcessRunningStateChanged =
+ 1 << 0, // The process has changed state to running
+ eEventProcessStoppedStateChanged =
+ 1 << 1, // The process has changed state to stopped
+ eEventSharedLibsStateChange =
+ 1 << 2, // Shared libraries loaded/unloaded state has changed
+ eEventStdioAvailable = 1 << 3, // Something is available on stdout/stderr
+ eEventProfileDataAvailable = 1 << 4, // Profile data ready for retrieval
+ kAllEventsMask = eEventProcessRunningStateChanged |
+ eEventProcessStoppedStateChanged |
+ eEventSharedLibsStateChange | eEventStdioAvailable |
+ eEventProfileDataAvailable
+};
+
+#define LOG_VERBOSE (1u << 0)
+#define LOG_PROCESS (1u << 1)
+#define LOG_THREAD (1u << 2)
+#define LOG_EXCEPTIONS (1u << 3)
+#define LOG_SHLIB (1u << 4)
+#define LOG_MEMORY (1u << 5) // Log memory reads/writes calls
+#define LOG_MEMORY_DATA_SHORT (1u << 6) // Log short memory reads/writes bytes
+#define LOG_MEMORY_DATA_LONG (1u << 7) // Log all memory reads/writes bytes
+#define LOG_MEMORY_PROTECTIONS (1u << 8) // Log memory protection changes
+#define LOG_BREAKPOINTS (1u << 9)
+#define LOG_EVENTS (1u << 10)
+#define LOG_WATCHPOINTS (1u << 11)
+#define LOG_STEP (1u << 12)
+#define LOG_TASK (1u << 13)
+#define LOG_DARWIN_LOG (1u << 14)
+#define LOG_LO_USER (1u << 16)
+#define LOG_HI_USER (1u << 31)
+#define LOG_ALL 0xFFFFFFFFu
+#define LOG_DEFAULT \
+ ((LOG_PROCESS) | (LOG_TASK) | (LOG_THREAD) | (LOG_EXCEPTIONS) | \
+ (LOG_SHLIB) | (LOG_MEMORY) | (LOG_BREAKPOINTS) | (LOG_WATCHPOINTS) | \
+ (LOG_STEP))
+
+#define REGISTER_SET_ALL 0
+// Generic Register set to be defined by each architecture for access to common
+// register values.
+#define REGISTER_SET_GENERIC ((uint32_t)0xFFFFFFFFu)
+#define GENERIC_REGNUM_PC 0 // Program Counter
+#define GENERIC_REGNUM_SP 1 // Stack Pointer
+#define GENERIC_REGNUM_FP 2 // Frame Pointer
+#define GENERIC_REGNUM_RA 3 // Return Address
+#define GENERIC_REGNUM_FLAGS 4 // Processor flags register
+#define GENERIC_REGNUM_ARG1 \
+ 5 // The register that would contain pointer size or less argument 1 (if any)
+#define GENERIC_REGNUM_ARG2 \
+ 6 // The register that would contain pointer size or less argument 2 (if any)
+#define GENERIC_REGNUM_ARG3 \
+ 7 // The register that would contain pointer size or less argument 3 (if any)
+#define GENERIC_REGNUM_ARG4 \
+ 8 // The register that would contain pointer size or less argument 4 (if any)
+#define GENERIC_REGNUM_ARG5 \
+ 9 // The register that would contain pointer size or less argument 5 (if any)
+#define GENERIC_REGNUM_ARG6 \
+ 10 // The register that would contain pointer size or less argument 6 (if any)
+#define GENERIC_REGNUM_ARG7 \
+ 11 // The register that would contain pointer size or less argument 7 (if any)
+#define GENERIC_REGNUM_ARG8 \
+ 12 // The register that would contain pointer size or less argument 8 (if any)
+
+enum DNBRegisterType {
+ InvalidRegType = 0,
+ Uint, // unsigned integer
+ Sint, // signed integer
+ IEEE754, // float
+ Vector // vector registers
+};
+
+enum DNBRegisterFormat {
+ InvalidRegFormat = 0,
+ Binary,
+ Decimal,
+ Hex,
+ Float,
+ VectorOfSInt8,
+ VectorOfUInt8,
+ VectorOfSInt16,
+ VectorOfUInt16,
+ VectorOfSInt32,
+ VectorOfUInt32,
+ VectorOfFloat32,
+ VectorOfUInt128
+};
+
+struct DNBRegisterInfo {
+ uint32_t set; // Register set
+ uint32_t reg; // Register number
+ const char *name; // Name of this register
+ const char *alt; // Alternate name
+ uint16_t type; // Type of the register bits (DNBRegisterType)
+ uint16_t format; // Default format for display (DNBRegisterFormat),
+ uint32_t size; // Size in bytes of the register
+ uint32_t offset; // Offset from the beginning of the register context
+ uint32_t
+ reg_ehframe; // eh_frame register number (INVALID_NUB_REGNUM when none)
+ uint32_t reg_dwarf; // DWARF register number (INVALID_NUB_REGNUM when none)
+ uint32_t
+ reg_generic; // Generic register number (INVALID_NUB_REGNUM when none)
+ uint32_t reg_debugserver; // The debugserver register number we'll use over
+ // gdb-remote protocol (INVALID_NUB_REGNUM when
+ // none)
+ const char **value_regs; // If this register is a part of other registers,
+ // list the register names terminated by NULL
+ const char **update_regs; // If modifying this register will invalidate other
+ // registers, list the register names terminated by
+ // NULL
+};
+
+struct DNBRegisterSetInfo {
+ const char *name; // Name of this register set
+ const struct DNBRegisterInfo *registers; // An array of register descriptions
+ nub_size_t num_registers; // The number of registers in REGISTERS array above
+};
+
+struct DNBThreadResumeAction {
+ nub_thread_t tid; // The thread ID that this action applies to,
+ // INVALID_NUB_THREAD for the default thread action
+ nub_state_t state; // Valid values are eStateStopped/eStateSuspended,
+ // eStateRunning, and eStateStepping.
+ int signal; // When resuming this thread, resume it with this signal
+ nub_addr_t addr; // If not INVALID_NUB_ADDRESS, then set the PC for the thread
+ // to ADDR before resuming/stepping
+};
+
+enum DNBThreadStopType {
+ eStopTypeInvalid = 0,
+ eStopTypeSignal,
+ eStopTypeException,
+ eStopTypeExec
+};
+
+enum DNBMemoryPermissions {
+ eMemoryPermissionsWritable = (1 << 0),
+ eMemoryPermissionsReadable = (1 << 1),
+ eMemoryPermissionsExecutable = (1 << 2)
+};
+
+#define DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH 256
+#define DNB_THREAD_STOP_INFO_MAX_EXC_DATA 8
+
+// DNBThreadStopInfo
+//
+// Describes the reason a thread stopped.
+struct DNBThreadStopInfo {
+ DNBThreadStopType reason;
+ char description[DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH];
+ union {
+ // eStopTypeSignal
+ struct {
+ uint32_t signo;
+ } signal;
+
+ // eStopTypeException
+ struct {
+ uint32_t type;
+ nub_size_t data_count;
+ nub_addr_t data[DNB_THREAD_STOP_INFO_MAX_EXC_DATA];
+ } exception;
+ } details;
+};
+
+struct DNBRegisterValue {
+ struct DNBRegisterInfo info; // Register information for this register
+ union {
+ int8_t sint8;
+ int16_t sint16;
+ int32_t sint32;
+ int64_t sint64;
+ uint8_t uint8;
+ uint16_t uint16;
+ uint32_t uint32;
+ uint64_t uint64;
+ float float32;
+ double float64;
+ int8_t v_sint8[64];
+ int16_t v_sint16[32];
+ int32_t v_sint32[16];
+ int64_t v_sint64[8];
+ uint8_t v_uint8[64];
+ uint16_t v_uint16[32];
+ uint32_t v_uint32[16];
+ uint64_t v_uint64[8];
+ float v_float32[16];
+ double v_float64[8];
+ void *pointer;
+ char *c_str;
+ } value;
+};
+
+enum DNBSharedLibraryState { eShlibStateUnloaded = 0, eShlibStateLoaded = 1 };
+
+#ifndef DNB_MAX_SEGMENT_NAME_LENGTH
+#define DNB_MAX_SEGMENT_NAME_LENGTH 32
+#endif
+
+struct DNBSegment {
+ char name[DNB_MAX_SEGMENT_NAME_LENGTH];
+ nub_addr_t addr;
+ nub_addr_t size;
+};
+
+struct DNBExecutableImageInfo {
+ char name[PATH_MAX]; // Name of the executable image (usually a full path)
+ uint32_t
+ state; // State of the executable image (see enum DNBSharedLibraryState)
+ nub_addr_t header_addr; // Executable header address
+ uuid_t uuid; // Unique identifier for matching with symbols
+ uint32_t
+ num_segments; // Number of contiguous memory segments to in SEGMENTS array
+ DNBSegment *segments; // Array of contiguous memory segments in executable
+};
+
+struct DNBRegionInfo {
+ nub_addr_t addr;
+ nub_addr_t size;
+ uint32_t permissions;
+};
+
+enum DNBProfileDataScanType {
+ eProfileHostCPU = (1 << 0),
+ eProfileCPU = (1 << 1),
+
+ eProfileThreadsCPU =
+ (1 << 2), // By default excludes eProfileThreadName and eProfileQueueName.
+ eProfileThreadName =
+ (1 << 3), // Assume eProfileThreadsCPU, get thread name as well.
+ eProfileQueueName =
+ (1 << 4), // Assume eProfileThreadsCPU, get queue name as well.
+
+ eProfileHostMemory = (1 << 5),
+
+ eProfileMemory = (1 << 6),
+ eProfileMemoryAnonymous =
+ (1 << 8), // Assume eProfileMemory, get Anonymous memory as well.
+
+ eProfileEnergy = (1 << 9),
+ eProfileEnergyCPUCap = (1 << 10),
+
+ eProfileMemoryCap = (1 << 15),
+
+ eProfileAll = 0xffffffff
+};
+
+typedef nub_addr_t (*DNBCallbackNameToAddress)(nub_process_t pid,
+ const char *name,
+ const char *shlib_regex,
+ void *baton);
+typedef nub_size_t (*DNBCallbackCopyExecutableImageInfos)(
+ nub_process_t pid, struct DNBExecutableImageInfo **image_infos,
+ nub_bool_t only_changed, void *baton);
+typedef void (*DNBCallbackLog)(void *baton, uint32_t flags, const char *format,
+ va_list args);
+
+#define UNUSED_IF_ASSERT_DISABLED(x) ((void)(x))
+
+#endif // #ifndef __DNBDefs_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/DNBError.cpp b/gnu/llvm/lldb/tools/debugserver/source/DNBError.cpp
new file mode 100644
index 00000000000..00933bce1a7
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/DNBError.cpp
@@ -0,0 +1,115 @@
+//===-- DNBError.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/26/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DNBError.h"
+#include "CFString.h"
+#include "DNBLog.h"
+#include "PThreadMutex.h"
+
+#ifdef WITH_SPRINGBOARD
+#include <SpringBoardServices/SpringBoardServer.h>
+#endif
+
+const char *DNBError::AsString() const {
+ if (Success())
+ return NULL;
+
+ if (m_str.empty()) {
+ const char *s = NULL;
+ switch (m_flavor) {
+ case MachKernel:
+ s = ::mach_error_string(m_err);
+ break;
+
+ case POSIX:
+ s = ::strerror(m_err);
+ break;
+
+#ifdef WITH_SPRINGBOARD
+ case SpringBoard: {
+ CFStringRef statusStr = SBSApplicationLaunchingErrorString(m_err);
+ if (CFString::UTF8(statusStr, m_str) == NULL)
+ m_str.clear();
+ } break;
+#endif
+#ifdef WITH_BKS
+ case BackBoard: {
+ // You have to call ObjC routines to get the error string from
+ // BackBoardServices.
+ // Not sure I want to make DNBError.cpp an .mm file. For now just make
+ // sure you
+ // pre-populate the error string when you make the DNBError of type
+ // BackBoard.
+ m_str.assign(
+ "Should have set BackBoard error when making the error string.");
+ } break;
+#endif
+#ifdef WITH_FBS
+ case FrontBoard: {
+ // You have to call ObjC routines to get the error string from
+ // FrontBoardServices.
+ // Not sure I want to make DNBError.cpp an .mm file. For now just make
+ // sure you
+ // pre-populate the error string when you make the DNBError of type
+ // FrontBoard.
+ m_str.assign(
+ "Should have set FrontBoard error when making the error string.");
+ } break;
+#endif
+ default:
+ break;
+ }
+ if (s)
+ m_str.assign(s);
+ }
+ if (m_str.empty())
+ return NULL;
+ return m_str.c_str();
+}
+
+void DNBError::LogThreadedIfError(const char *format, ...) const {
+ if (Fail()) {
+ char *arg_msg = NULL;
+ va_list args;
+ va_start(args, format);
+ ::vasprintf(&arg_msg, format, args);
+ va_end(args);
+
+ if (arg_msg != NULL) {
+ const char *err_str = AsString();
+ if (err_str == NULL)
+ err_str = "???";
+ DNBLogThreaded("error: %s err = %s (0x%8.8x)", arg_msg, err_str, m_err);
+ free(arg_msg);
+ }
+ }
+}
+
+void DNBError::LogThreaded(const char *format, ...) const {
+ char *arg_msg = NULL;
+ va_list args;
+ va_start(args, format);
+ ::vasprintf(&arg_msg, format, args);
+ va_end(args);
+
+ if (arg_msg != NULL) {
+ if (Fail()) {
+ const char *err_str = AsString();
+ if (err_str == NULL)
+ err_str = "???";
+ DNBLogThreaded("error: %s err = %s (0x%8.8x)", arg_msg, err_str, m_err);
+ } else {
+ DNBLogThreaded("%s err = 0x%8.8x", arg_msg, m_err);
+ }
+ free(arg_msg);
+ }
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/DNBError.h b/gnu/llvm/lldb/tools/debugserver/source/DNBError.h
new file mode 100644
index 00000000000..7e67cf2c553
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/DNBError.h
@@ -0,0 +1,97 @@
+//===-- DNBError.h ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/26/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNBError_h__
+#define __DNBError_h__
+
+#include <errno.h>
+#include <mach/mach.h>
+#include <stdio.h>
+#include <string>
+
+class DNBError {
+public:
+ typedef uint32_t ValueType;
+ enum FlavorType {
+ Generic = 0,
+ MachKernel = 1,
+ POSIX = 2
+#ifdef WITH_SPRINGBOARD
+ ,
+ SpringBoard = 3
+#endif
+#ifdef WITH_BKS
+ ,
+ BackBoard = 4
+#endif
+#ifdef WITH_FBS
+ ,
+ FrontBoard = 5
+#endif
+ };
+
+ explicit DNBError(ValueType err = 0, FlavorType flavor = Generic)
+ : m_err(err), m_flavor(flavor) {}
+
+ const char *AsString() const;
+ void Clear() {
+ m_err = 0;
+ m_flavor = Generic;
+ m_str.clear();
+ }
+ ValueType Status() const { return m_err; }
+ FlavorType Flavor() const { return m_flavor; }
+
+ ValueType operator=(kern_return_t err) {
+ m_err = err;
+ m_flavor = MachKernel;
+ m_str.clear();
+ return m_err;
+ }
+
+ void SetError(kern_return_t err) {
+ m_err = err;
+ m_flavor = MachKernel;
+ m_str.clear();
+ }
+
+ void SetErrorToErrno() {
+ m_err = errno;
+ m_flavor = POSIX;
+ m_str.clear();
+ }
+
+ void SetError(ValueType err, FlavorType flavor) {
+ m_err = err;
+ m_flavor = flavor;
+ m_str.clear();
+ }
+
+ // Generic errors can set their own string values
+ void SetErrorString(const char *err_str) {
+ if (err_str && err_str[0])
+ m_str = err_str;
+ else
+ m_str.clear();
+ }
+ bool Success() const { return m_err == 0; }
+ bool Fail() const { return m_err != 0; }
+ void LogThreadedIfError(const char *format, ...) const;
+ void LogThreaded(const char *format, ...) const;
+
+protected:
+ ValueType m_err;
+ FlavorType m_flavor;
+ mutable std::string m_str;
+};
+
+#endif // #ifndef __DNBError_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/DNBLog.cpp b/gnu/llvm/lldb/tools/debugserver/source/DNBLog.cpp
new file mode 100644
index 00000000000..11b2d0a04b7
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/DNBLog.cpp
@@ -0,0 +1,268 @@
+//===-- DNBLog.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/18/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DNBLog.h"
+
+static int g_debug = 0;
+static int g_verbose = 0;
+
+#if defined(DNBLOG_ENABLED)
+
+#include "PThreadMutex.h"
+#include <mach/mach.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+uint32_t g_log_bits = 0;
+static DNBCallbackLog g_log_callback = NULL;
+static void *g_log_baton = NULL;
+
+int DNBLogGetDebug() { return g_debug; }
+
+void DNBLogSetDebug(int g) { g_debug = g; }
+
+int DNBLogGetVerbose() { return g_verbose; }
+
+void DNBLogSetVerbose(int v) { g_verbose = v; }
+
+bool DNBLogCheckLogBit(uint32_t bit) { return (g_log_bits & bit) != 0; }
+
+uint32_t DNBLogSetLogMask(uint32_t mask) {
+ uint32_t old = g_log_bits;
+ g_log_bits = mask;
+ return old;
+}
+
+uint32_t DNBLogGetLogMask() { return g_log_bits; }
+
+void DNBLogSetLogCallback(DNBCallbackLog callback, void *baton) {
+ g_log_callback = callback;
+ g_log_baton = baton;
+}
+
+DNBCallbackLog DNBLogGetLogCallback() { return g_log_callback; }
+
+bool DNBLogEnabled() { return g_log_callback != NULL; }
+
+bool DNBLogEnabledForAny(uint32_t mask) {
+ if (g_log_callback)
+ return (g_log_bits & mask) != 0;
+ return false;
+}
+static inline void _DNBLogVAPrintf(uint32_t flags, const char *format,
+ va_list args) {
+ static PThreadMutex g_LogThreadedMutex(PTHREAD_MUTEX_RECURSIVE);
+ PTHREAD_MUTEX_LOCKER(locker, g_LogThreadedMutex);
+
+ if (g_log_callback)
+ g_log_callback(g_log_baton, flags, format, args);
+}
+
+void _DNBLog(uint32_t flags, const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ _DNBLogVAPrintf(flags, format, args);
+ va_end(args);
+}
+
+// Print debug strings if and only if the global g_debug is set to
+// a non-zero value.
+void _DNBLogDebug(const char *format, ...) {
+ if (DNBLogEnabled() && g_debug) {
+ va_list args;
+ va_start(args, format);
+ _DNBLogVAPrintf(DNBLOG_FLAG_DEBUG, format, args);
+ va_end(args);
+ }
+}
+
+// Print debug strings if and only if the global g_debug is set to
+// a non-zero value.
+void _DNBLogDebugVerbose(const char *format, ...) {
+ if (DNBLogEnabled() && g_debug && g_verbose) {
+ va_list args;
+ va_start(args, format);
+ _DNBLogVAPrintf(DNBLOG_FLAG_DEBUG | DNBLOG_FLAG_VERBOSE, format, args);
+ va_end(args);
+ }
+}
+
+static uint32_t g_message_id = 0;
+
+// Prefix the formatted log string with process and thread IDs and
+// suffix it with a newline.
+void _DNBLogThreaded(const char *format, ...) {
+ if (DNBLogEnabled()) {
+ // PTHREAD_MUTEX_LOCKER(locker, GetLogThreadedMutex());
+
+ char *arg_msg = NULL;
+ va_list args;
+ va_start(args, format);
+ ::vasprintf(&arg_msg, format, args);
+ va_end(args);
+
+ if (arg_msg != NULL) {
+ static struct timeval g_timeval = {0, 0};
+ static struct timeval tv;
+ static struct timeval delta;
+ gettimeofday(&tv, NULL);
+ if (g_timeval.tv_sec == 0) {
+ delta.tv_sec = 0;
+ delta.tv_usec = 0;
+ } else {
+ timersub(&tv, &g_timeval, &delta);
+ }
+ g_timeval = tv;
+
+ // Calling "mach_port_deallocate()" bumps the reference count on the
+ // thread
+ // port, so we need to deallocate it. mach_task_self() doesn't bump the
+ // ref
+ // count.
+ thread_port_t thread_self = mach_thread_self();
+
+ _DNBLog(DNBLOG_FLAG_THREADED, "%u +%lu.%06u sec [%4.4x/%4.4x]: %s",
+ ++g_message_id, delta.tv_sec, delta.tv_usec, getpid(),
+ thread_self, arg_msg);
+
+ mach_port_deallocate(mach_task_self(), thread_self);
+ free(arg_msg);
+ }
+ }
+}
+
+// Prefix the formatted log string with process and thread IDs and
+// suffix it with a newline.
+void _DNBLogThreadedIf(uint32_t log_bit, const char *format, ...) {
+ if (DNBLogEnabled() && (log_bit & g_log_bits) == log_bit) {
+ // PTHREAD_MUTEX_LOCKER(locker, GetLogThreadedMutex());
+
+ char *arg_msg = NULL;
+ va_list args;
+ va_start(args, format);
+ ::vasprintf(&arg_msg, format, args);
+ va_end(args);
+
+ if (arg_msg != NULL) {
+ static struct timeval g_timeval = {0, 0};
+ static struct timeval tv;
+ static struct timeval delta;
+ gettimeofday(&tv, NULL);
+ if (g_timeval.tv_sec == 0) {
+ delta.tv_sec = 0;
+ delta.tv_usec = 0;
+ } else {
+ timersub(&tv, &g_timeval, &delta);
+ }
+ g_timeval = tv;
+
+ // Calling "mach_port_deallocate()" bumps the reference count on the
+ // thread
+ // port, so we need to deallocate it. mach_task_self() doesn't bump the
+ // ref
+ // count.
+ thread_port_t thread_self = mach_thread_self();
+
+ _DNBLog(DNBLOG_FLAG_THREADED, "%u +%lu.%06u sec [%4.4x/%4.4x]: %s",
+ ++g_message_id, delta.tv_sec, delta.tv_usec, getpid(),
+ thread_self, arg_msg);
+
+ mach_port_deallocate(mach_task_self(), thread_self);
+
+ free(arg_msg);
+ }
+ }
+}
+
+// Printing of errors that are not fatal.
+void _DNBLogError(const char *format, ...) {
+ if (DNBLogEnabled()) {
+ char *arg_msg = NULL;
+ va_list args;
+ va_start(args, format);
+ ::vasprintf(&arg_msg, format, args);
+ va_end(args);
+
+ if (arg_msg != NULL) {
+ _DNBLog(DNBLOG_FLAG_ERROR, "error: %s", arg_msg);
+ free(arg_msg);
+ }
+ }
+}
+
+// Printing of errors that ARE fatal. Exit with ERR exit code
+// immediately.
+void _DNBLogFatalError(int err, const char *format, ...) {
+ if (DNBLogEnabled()) {
+ char *arg_msg = NULL;
+ va_list args;
+ va_start(args, format);
+ ::vasprintf(&arg_msg, format, args);
+ va_end(args);
+
+ if (arg_msg != NULL) {
+ _DNBLog(DNBLOG_FLAG_ERROR | DNBLOG_FLAG_FATAL, "error: %s", arg_msg);
+ free(arg_msg);
+ }
+ ::exit(err);
+ }
+}
+
+// Printing of warnings that are not fatal only if verbose mode is
+// enabled.
+void _DNBLogVerbose(const char *format, ...) {
+ if (DNBLogEnabled() && g_verbose) {
+ va_list args;
+ va_start(args, format);
+ _DNBLogVAPrintf(DNBLOG_FLAG_VERBOSE, format, args);
+ va_end(args);
+ }
+}
+
+// Printing of warnings that are not fatal only if verbose mode is
+// enabled.
+void _DNBLogWarningVerbose(const char *format, ...) {
+ if (DNBLogEnabled() && g_verbose) {
+ char *arg_msg = NULL;
+ va_list args;
+ va_start(args, format);
+ ::vasprintf(&arg_msg, format, args);
+ va_end(args);
+
+ if (arg_msg != NULL) {
+ _DNBLog(DNBLOG_FLAG_WARNING | DNBLOG_FLAG_VERBOSE, "warning: %s",
+ arg_msg);
+ free(arg_msg);
+ }
+ }
+}
+// Printing of warnings that are not fatal.
+void _DNBLogWarning(const char *format, ...) {
+ if (DNBLogEnabled()) {
+ char *arg_msg = NULL;
+ va_list args;
+ va_start(args, format);
+ ::vasprintf(&arg_msg, format, args);
+ va_end(args);
+
+ if (arg_msg != NULL) {
+ _DNBLog(DNBLOG_FLAG_WARNING, "warning: %s", arg_msg);
+ free(arg_msg);
+ }
+ }
+}
+
+#endif
diff --git a/gnu/llvm/lldb/tools/debugserver/source/DNBLog.h b/gnu/llvm/lldb/tools/debugserver/source/DNBLog.h
new file mode 100644
index 00000000000..47d8aeb066e
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/DNBLog.h
@@ -0,0 +1,152 @@
+//===-- DNBLog.h ------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/18/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNBLog_h__
+#define __DNBLog_h__
+
+#include "DNBDefs.h"
+#include <stdint.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Flags that get filled in automatically before calling the log callback
+// function
+#define DNBLOG_FLAG_FATAL (1u << 0)
+#define DNBLOG_FLAG_ERROR (1u << 1)
+#define DNBLOG_FLAG_WARNING (1u << 2)
+#define DNBLOG_FLAG_DEBUG (1u << 3)
+#define DNBLOG_FLAG_VERBOSE (1u << 4)
+#define DNBLOG_FLAG_THREADED (1u << 5)
+
+#define DNBLOG_ENABLED
+
+#if defined(DNBLOG_ENABLED)
+
+void _DNBLog(uint32_t flags, const char *format, ...)
+ __attribute__((format(printf, 2, 3)));
+void _DNBLogDebug(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+void _DNBLogDebugVerbose(const char *fmt, ...)
+ __attribute__((format(printf, 1, 2)));
+void _DNBLogThreaded(const char *fmt, ...)
+ __attribute__((format(printf, 1, 2)));
+void _DNBLogThreadedIf(uint32_t mask, const char *fmt, ...)
+ __attribute__((format(printf, 2, 3)));
+void _DNBLogError(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+void _DNBLogFatalError(int err, const char *fmt, ...)
+ __attribute__((format(printf, 2, 3)));
+void _DNBLogVerbose(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+void _DNBLogWarning(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+void _DNBLogWarningVerbose(const char *fmt, ...)
+ __attribute__((format(printf, 1, 2)));
+bool DNBLogCheckLogBit(uint32_t bit);
+uint32_t DNBLogSetLogMask(uint32_t mask);
+uint32_t DNBLogGetLogMask();
+void DNBLogSetLogCallback(DNBCallbackLog callback, void *baton);
+DNBCallbackLog DNBLogGetLogCallback();
+bool DNBLogEnabled();
+bool DNBLogEnabledForAny(uint32_t mask);
+int DNBLogGetDebug();
+void DNBLogSetDebug(int g);
+int DNBLogGetVerbose();
+void DNBLogSetVerbose(int g);
+
+#define DNBLog(fmt, ...) \
+ do { \
+ if (DNBLogEnabled()) { \
+ _DNBLog(0, fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+#define DNBLogDebug(fmt, ...) \
+ do { \
+ if (DNBLogEnabled()) { \
+ _DNBLogDebug(fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+#define DNBLogDebugVerbose(fmt, ...) \
+ do { \
+ if (DNBLogEnabled()) { \
+ _DNBLogDebugVerbose(fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+#define DNBLogThreaded(fmt, ...) \
+ do { \
+ if (DNBLogEnabled()) { \
+ _DNBLogThreaded(fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+#define DNBLogThreadedIf(mask, fmt, ...) \
+ do { \
+ if (DNBLogEnabledForAny(mask)) { \
+ _DNBLogThreaded(fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+#define DNBLogError(fmt, ...) \
+ do { \
+ if (DNBLogEnabled()) { \
+ _DNBLogError(fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+#define DNBLogFatalError(err, fmt, ...) \
+ do { \
+ if (DNBLogEnabled()) { \
+ _DNBLogFatalError(err, fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+#define DNBLogVerbose(fmt, ...) \
+ do { \
+ if (DNBLogEnabled()) { \
+ _DNBLogVerbose(fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+#define DNBLogWarning(fmt, ...) \
+ do { \
+ if (DNBLogEnabled()) { \
+ _DNBLogWarning(fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+#define DNBLogWarningVerbose(fmt, ...) \
+ do { \
+ if (DNBLogEnabled()) { \
+ _DNBLogWarningVerbose(fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+#else // #if defined(DNBLOG_ENABLED)
+
+#define DNBLogDebug(...) ((void)0)
+#define DNBLogDebugVerbose(...) ((void)0)
+#define DNBLogThreaded(...) ((void)0)
+#define DNBLogThreadedIf(...) ((void)0)
+#define DNBLogError(...) ((void)0)
+#define DNBLogFatalError(...) ((void)0)
+#define DNBLogVerbose(...) ((void)0)
+#define DNBLogWarning(...) ((void)0)
+#define DNBLogWarningVerbose(...) ((void)0)
+#define DNBLogGetLogFile() ((FILE *)NULL)
+#define DNBLogSetLogFile(f) ((void)0)
+#define DNBLogCheckLogBit(bit) ((bool)false)
+#define DNBLogSetLogMask(mask) ((uint32_t)0u)
+#define DNBLogGetLogMask() ((uint32_t)0u)
+#define DNBLogToASL() ((void)0)
+#define DNBLogToFile() ((void)0)
+#define DNBLogCloseLogFile() ((void)0)
+
+#endif // #else defined(DNBLOG_ENABLED)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // #ifndef __DNBLog_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/DNBRegisterInfo.cpp b/gnu/llvm/lldb/tools/debugserver/source/DNBRegisterInfo.cpp
new file mode 100644
index 00000000000..c224fc7056d
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/DNBRegisterInfo.cpp
@@ -0,0 +1,251 @@
+//===-- DNBRegisterInfo.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 8/3/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DNBRegisterInfo.h"
+#include "DNBLog.h"
+#include <string.h>
+
+DNBRegisterValueClass::DNBRegisterValueClass(const DNBRegisterInfo *regInfo) {
+ Clear();
+ if (regInfo)
+ info = *regInfo;
+}
+
+void DNBRegisterValueClass::Clear() {
+ memset(&info, 0, sizeof(DNBRegisterInfo));
+ memset(&value, 0, sizeof(value));
+}
+
+bool DNBRegisterValueClass::IsValid() const {
+ return info.name != NULL && info.type != InvalidRegType && info.size > 0 &&
+ info.size <= sizeof(value);
+}
+
+#define PRINT_COMMA_SEPARATOR \
+ do { \
+ if (pos < end) { \
+ if (i > 0) { \
+ strlcpy(pos, ", ", end - pos); \
+ pos += 2; \
+ } \
+ } \
+ } while (0)
+
+void DNBRegisterValueClass::Dump(const char *pre, const char *post) const {
+ if (info.name != NULL) {
+ char str[1024];
+ char *pos;
+ char *end = str + sizeof(str);
+ if (info.format == Hex) {
+ switch (info.size) {
+ case 0:
+ snprintf(str, sizeof(str), "%s",
+ "error: invalid register size of zero.");
+ break;
+ case 1:
+ snprintf(str, sizeof(str), "0x%2.2x", value.uint8);
+ break;
+ case 2:
+ snprintf(str, sizeof(str), "0x%4.4x", value.uint16);
+ break;
+ case 4:
+ snprintf(str, sizeof(str), "0x%8.8x", value.uint32);
+ break;
+ case 8:
+ snprintf(str, sizeof(str), "0x%16.16llx", value.uint64);
+ break;
+ case 16:
+ snprintf(str, sizeof(str), "0x%16.16llx%16.16llx", value.v_uint64[0],
+ value.v_uint64[1]);
+ break;
+ default:
+ strlcpy(str, "0x", 3);
+ pos = str + 2;
+ for (uint32_t i = 0; i < info.size; ++i) {
+ if (pos < end)
+ pos +=
+ snprintf(pos, end - pos, "%2.2x", (uint32_t)value.v_uint8[i]);
+ }
+ break;
+ }
+ } else {
+ switch (info.type) {
+ case Uint:
+ switch (info.size) {
+ case 1:
+ snprintf(str, sizeof(str), "%u", value.uint8);
+ break;
+ case 2:
+ snprintf(str, sizeof(str), "%u", value.uint16);
+ break;
+ case 4:
+ snprintf(str, sizeof(str), "%u", value.uint32);
+ break;
+ case 8:
+ snprintf(str, sizeof(str), "%llu", value.uint64);
+ break;
+ default:
+ snprintf(str, sizeof(str), "error: unsupported uint byte size %d.",
+ info.size);
+ break;
+ }
+ break;
+
+ case Sint:
+ switch (info.size) {
+ case 1:
+ snprintf(str, sizeof(str), "%d", value.sint8);
+ break;
+ case 2:
+ snprintf(str, sizeof(str), "%d", value.sint16);
+ break;
+ case 4:
+ snprintf(str, sizeof(str), "%d", value.sint32);
+ break;
+ case 8:
+ snprintf(str, sizeof(str), "%lld", value.sint64);
+ break;
+ default:
+ snprintf(str, sizeof(str), "error: unsupported sint byte size %d.",
+ info.size);
+ break;
+ }
+ break;
+
+ case IEEE754:
+ switch (info.size) {
+ case 4:
+ snprintf(str, sizeof(str), "%f", value.float32);
+ break;
+ case 8:
+ snprintf(str, sizeof(str), "%g", value.float64);
+ break;
+ default:
+ snprintf(str, sizeof(str), "error: unsupported float byte size %d.",
+ info.size);
+ break;
+ }
+ break;
+
+ case Vector:
+ if (info.size > 0) {
+ switch (info.format) {
+ case VectorOfSInt8:
+ snprintf(str, sizeof(str), "%s", "sint8 { ");
+ pos = str + strlen(str);
+ for (uint32_t i = 0; i < info.size; ++i) {
+ PRINT_COMMA_SEPARATOR;
+ if (pos < end)
+ pos +=
+ snprintf(pos, end - pos, "%d", (int32_t)value.v_sint8[i]);
+ }
+ strlcat(str, " }", sizeof(str));
+ break;
+
+ default:
+ DNBLogError(
+ "unsupported vector format %d, defaulting to hex bytes.",
+ info.format);
+ [[clang::fallthrough]];
+ case VectorOfUInt8:
+ snprintf(str, sizeof(str), "%s", "uint8 { ");
+ pos = str + strlen(str);
+ for (uint32_t i = 0; i < info.size; ++i) {
+ PRINT_COMMA_SEPARATOR;
+ if (pos < end)
+ pos +=
+ snprintf(pos, end - pos, "%u", (uint32_t)value.v_uint8[i]);
+ }
+ break;
+
+ case VectorOfSInt16:
+ snprintf(str, sizeof(str), "%s", "sint16 { ");
+ pos = str + strlen(str);
+ for (uint32_t i = 0; i < info.size / 2; ++i) {
+ PRINT_COMMA_SEPARATOR;
+ if (pos < end)
+ pos +=
+ snprintf(pos, end - pos, "%d", (int32_t)value.v_sint16[i]);
+ }
+ break;
+
+ case VectorOfUInt16:
+ snprintf(str, sizeof(str), "%s", "uint16 { ");
+ pos = str + strlen(str);
+ for (uint32_t i = 0; i < info.size / 2; ++i) {
+ PRINT_COMMA_SEPARATOR;
+ if (pos < end)
+ pos +=
+ snprintf(pos, end - pos, "%u", (uint32_t)value.v_uint16[i]);
+ }
+ break;
+
+ case VectorOfSInt32:
+ snprintf(str, sizeof(str), "%s", "sint32 { ");
+ pos = str + strlen(str);
+ for (uint32_t i = 0; i < info.size / 4; ++i) {
+ PRINT_COMMA_SEPARATOR;
+ if (pos < end)
+ pos +=
+ snprintf(pos, end - pos, "%d", (int32_t)value.v_sint32[i]);
+ }
+ break;
+
+ case VectorOfUInt32:
+ snprintf(str, sizeof(str), "%s", "uint32 { ");
+ pos = str + strlen(str);
+ for (uint32_t i = 0; i < info.size / 4; ++i) {
+ PRINT_COMMA_SEPARATOR;
+ if (pos < end)
+ pos +=
+ snprintf(pos, end - pos, "%u", (uint32_t)value.v_uint32[i]);
+ }
+ break;
+
+ case VectorOfFloat32:
+ snprintf(str, sizeof(str), "%s", "float32 { ");
+ pos = str + strlen(str);
+ for (uint32_t i = 0; i < info.size / 4; ++i) {
+ PRINT_COMMA_SEPARATOR;
+ if (pos < end)
+ pos += snprintf(pos, end - pos, "%f", value.v_float32[i]);
+ }
+ break;
+
+ case VectorOfUInt128:
+ snprintf(str, sizeof(str), "%s", "uint128 { ");
+ pos = str + strlen(str);
+ for (uint32_t i = 0; i < info.size / 16; ++i) {
+ PRINT_COMMA_SEPARATOR;
+ if (pos < end)
+ pos += snprintf(pos, end - pos, "0x%16.16llx%16.16llx",
+ value.v_uint64[i], value.v_uint64[i + 1]);
+ }
+ break;
+ }
+ strlcat(str, " }", sizeof(str));
+ } else {
+ snprintf(str, sizeof(str), "error: unsupported vector size %d.",
+ info.size);
+ }
+ break;
+
+ default:
+ snprintf(str, sizeof(str), "error: unsupported register type %d.",
+ info.type);
+ break;
+ }
+ }
+
+ DNBLog("%s%4s = %s%s", pre ? pre : "", info.name, str, post ? post : "");
+ }
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/DNBRegisterInfo.h b/gnu/llvm/lldb/tools/debugserver/source/DNBRegisterInfo.h
new file mode 100644
index 00000000000..3aa0e03eaa1
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/DNBRegisterInfo.h
@@ -0,0 +1,29 @@
+//===-- DNBRegisterInfo.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 8/3/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNBRegisterInfo_h__
+#define __DNBRegisterInfo_h__
+
+#include "DNBDefs.h"
+#include <stdint.h>
+#include <stdio.h>
+
+struct DNBRegisterValueClass : public DNBRegisterValue {
+#ifdef __cplusplus
+ DNBRegisterValueClass(const DNBRegisterInfo *regInfo = NULL);
+ void Clear();
+ void Dump(const char *pre, const char *post) const;
+ bool IsValid() const;
+#endif
+};
+
+#endif
diff --git a/gnu/llvm/lldb/tools/debugserver/source/DNBRuntimeAction.h b/gnu/llvm/lldb/tools/debugserver/source/DNBRuntimeAction.h
new file mode 100644
index 00000000000..3f0d6021aea
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/DNBRuntimeAction.h
@@ -0,0 +1,23 @@
+//===-- DNBRuntimeAction.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 10/8/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNBRuntimeAction_h__
+#define __DNBRuntimeAction_h__
+
+class DNBRuntimeAction {
+ virtual void Initialize(nub_process_t pid) = 0;
+ virtual void ProcessStateChanged(nub_state_t state) = 0;
+ virtual void SharedLibraryStateChanged(DNBExecutableImageInfo *image_infos,
+ nub_size_t num_image_infos) = 0;
+};
+
+#endif // #ifndef __DNBRuntimeAction_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/DNBThreadResumeActions.cpp b/gnu/llvm/lldb/tools/debugserver/source/DNBThreadResumeActions.cpp
new file mode 100644
index 00000000000..cb747eeeaa1
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/DNBThreadResumeActions.cpp
@@ -0,0 +1,88 @@
+//===-- DNBThreadResumeActions.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 03/13/2010
+//
+//===----------------------------------------------------------------------===//
+
+#include "DNBThreadResumeActions.h"
+
+DNBThreadResumeActions::DNBThreadResumeActions()
+ : m_actions(), m_signal_handled() {}
+
+DNBThreadResumeActions::DNBThreadResumeActions(
+ const DNBThreadResumeAction *actions, size_t num_actions)
+ : m_actions(), m_signal_handled() {
+ if (actions && num_actions) {
+ m_actions.assign(actions, actions + num_actions);
+ m_signal_handled.assign(num_actions, false);
+ }
+}
+
+DNBThreadResumeActions::DNBThreadResumeActions(nub_state_t default_action,
+ int signal)
+ : m_actions(), m_signal_handled() {
+ SetDefaultThreadActionIfNeeded(default_action, signal);
+}
+
+void DNBThreadResumeActions::Append(const DNBThreadResumeAction &action) {
+ m_actions.push_back(action);
+ m_signal_handled.push_back(false);
+}
+
+void DNBThreadResumeActions::AppendAction(nub_thread_t tid, nub_state_t state,
+ int signal, nub_addr_t addr) {
+ DNBThreadResumeAction action = {tid, state, signal, addr};
+ Append(action);
+}
+
+const DNBThreadResumeAction *
+DNBThreadResumeActions::GetActionForThread(nub_thread_t tid,
+ bool default_ok) const {
+ const size_t num_actions = m_actions.size();
+ for (size_t i = 0; i < num_actions; ++i) {
+ if (m_actions[i].tid == tid)
+ return &m_actions[i];
+ }
+ if (default_ok && tid != INVALID_NUB_THREAD)
+ return GetActionForThread(INVALID_NUB_THREAD, false);
+ return NULL;
+}
+
+size_t DNBThreadResumeActions::NumActionsWithState(nub_state_t state) const {
+ size_t count = 0;
+ const size_t num_actions = m_actions.size();
+ for (size_t i = 0; i < num_actions; ++i) {
+ if (m_actions[i].state == state)
+ ++count;
+ }
+ return count;
+}
+
+bool DNBThreadResumeActions::SetDefaultThreadActionIfNeeded(nub_state_t action,
+ int signal) {
+ if (GetActionForThread(INVALID_NUB_THREAD, true) == NULL) {
+ // There isn't a default action so we do need to set it.
+ DNBThreadResumeAction default_action = {INVALID_NUB_THREAD, action, signal,
+ INVALID_NUB_ADDRESS};
+ m_actions.push_back(default_action);
+ m_signal_handled.push_back(false);
+ return true; // Return true as we did add the default action
+ }
+ return false;
+}
+
+void DNBThreadResumeActions::SetSignalHandledForThread(nub_thread_t tid) const {
+ if (tid != INVALID_NUB_THREAD) {
+ const size_t num_actions = m_actions.size();
+ for (size_t i = 0; i < num_actions; ++i) {
+ if (m_actions[i].tid == tid)
+ m_signal_handled[i] = true;
+ }
+ }
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/DNBThreadResumeActions.h b/gnu/llvm/lldb/tools/debugserver/source/DNBThreadResumeActions.h
new file mode 100644
index 00000000000..e2a25abca2a
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/DNBThreadResumeActions.h
@@ -0,0 +1,65 @@
+//===-- DNBThreadResumeActions.h --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 03/13/2010
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNBThreadResumeActions_h__
+#define __DNBThreadResumeActions_h__
+
+#include <vector>
+
+#include "DNBDefs.h"
+
+class DNBThreadResumeActions {
+public:
+ DNBThreadResumeActions();
+
+ DNBThreadResumeActions(nub_state_t default_action, int signal);
+
+ DNBThreadResumeActions(const DNBThreadResumeAction *actions,
+ size_t num_actions);
+
+ bool IsEmpty() const { return m_actions.empty(); }
+
+ void Append(const DNBThreadResumeAction &action);
+
+ void AppendAction(nub_thread_t tid, nub_state_t state, int signal = 0,
+ nub_addr_t addr = INVALID_NUB_ADDRESS);
+
+ void AppendResumeAll() { AppendAction(INVALID_NUB_THREAD, eStateRunning); }
+
+ void AppendSuspendAll() { AppendAction(INVALID_NUB_THREAD, eStateStopped); }
+
+ void AppendStepAll() { AppendAction(INVALID_NUB_THREAD, eStateStepping); }
+
+ const DNBThreadResumeAction *GetActionForThread(nub_thread_t tid,
+ bool default_ok) const;
+
+ size_t NumActionsWithState(nub_state_t state) const;
+
+ bool SetDefaultThreadActionIfNeeded(nub_state_t action, int signal);
+
+ void SetSignalHandledForThread(nub_thread_t tid) const;
+
+ const DNBThreadResumeAction *GetFirst() const { return m_actions.data(); }
+
+ size_t GetSize() const { return m_actions.size(); }
+
+ void Clear() {
+ m_actions.clear();
+ m_signal_handled.clear();
+ }
+
+protected:
+ std::vector<DNBThreadResumeAction> m_actions;
+ mutable std::vector<bool> m_signal_handled;
+};
+
+#endif // #ifndef __DNBThreadResumeActions_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/DNBTimer.h b/gnu/llvm/lldb/tools/debugserver/source/DNBTimer.h
new file mode 100644
index 00000000000..21ee2351d69
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/DNBTimer.h
@@ -0,0 +1,134 @@
+//===-- DNBTimer.h ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 12/13/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNBTimer_h__
+#define __DNBTimer_h__
+
+#include "DNBDefs.h"
+#include "PThreadMutex.h"
+#include <memory>
+#include <stdint.h>
+#include <sys/time.h>
+
+class DNBTimer {
+public:
+ // Constructors and Destructors
+ DNBTimer(bool threadSafe) : m_mutexAP() {
+ if (threadSafe)
+ m_mutexAP.reset(new PThreadMutex(PTHREAD_MUTEX_RECURSIVE));
+ Reset();
+ }
+
+ DNBTimer(const DNBTimer &rhs) : m_mutexAP() {
+ // Create a new mutex to make this timer thread safe as well if
+ // the timer we are copying is thread safe
+ if (rhs.IsThreadSafe())
+ m_mutexAP.reset(new PThreadMutex(PTHREAD_MUTEX_RECURSIVE));
+ m_timeval = rhs.m_timeval;
+ }
+
+ DNBTimer &operator=(const DNBTimer &rhs) {
+ // Create a new mutex to make this timer thread safe as well if
+ // the timer we are copying is thread safe
+ if (rhs.IsThreadSafe())
+ m_mutexAP.reset(new PThreadMutex(PTHREAD_MUTEX_RECURSIVE));
+ m_timeval = rhs.m_timeval;
+ return *this;
+ }
+
+ ~DNBTimer() {}
+
+ bool IsThreadSafe() const { return m_mutexAP.get() != NULL; }
+ // Reset the time value to now
+ void Reset() {
+ PTHREAD_MUTEX_LOCKER(locker, m_mutexAP.get());
+ gettimeofday(&m_timeval, NULL);
+ }
+ // Get the total mircoseconds since Jan 1, 1970
+ uint64_t TotalMicroSeconds() const {
+ PTHREAD_MUTEX_LOCKER(locker, m_mutexAP.get());
+ return (uint64_t)(m_timeval.tv_sec) * 1000000ull +
+ (uint64_t)m_timeval.tv_usec;
+ }
+
+ void GetTime(uint64_t &sec, uint32_t &usec) const {
+ PTHREAD_MUTEX_LOCKER(locker, m_mutexAP.get());
+ sec = m_timeval.tv_sec;
+ usec = m_timeval.tv_usec;
+ }
+ // Return the number of microseconds elapsed between now and the
+ // m_timeval
+ uint64_t ElapsedMicroSeconds(bool update) {
+ PTHREAD_MUTEX_LOCKER(locker, m_mutexAP.get());
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ uint64_t now_usec =
+ (uint64_t)(now.tv_sec) * 1000000ull + (uint64_t)now.tv_usec;
+ uint64_t this_usec =
+ (uint64_t)(m_timeval.tv_sec) * 1000000ull + (uint64_t)m_timeval.tv_usec;
+ uint64_t elapsed = now_usec - this_usec;
+ // Update the timer time value if requeseted
+ if (update)
+ m_timeval = now;
+ return elapsed;
+ }
+
+ static uint64_t GetTimeOfDay() {
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ uint64_t now_usec =
+ (uint64_t)(now.tv_sec) * 1000000ull + (uint64_t)now.tv_usec;
+ return now_usec;
+ }
+
+ static void OffsetTimeOfDay(struct timespec *ts,
+ __darwin_time_t sec_offset = 0,
+ long nsec_offset = 0) {
+ if (ts == NULL)
+ return;
+ // Get the current time in a timeval structure
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ // Morph it into a timespec
+ TIMEVAL_TO_TIMESPEC(&now, ts);
+ // Offset the timespec if requested
+ if (sec_offset != 0 || nsec_offset != 0) {
+ // Offset the nano seconds
+ ts->tv_nsec += nsec_offset;
+ // Offset the seconds taking into account a nano-second overflow
+ ts->tv_sec = ts->tv_sec + ts->tv_nsec / 1000000000 + sec_offset;
+ // Trim the nanoseconds back there was an overflow
+ ts->tv_nsec = ts->tv_nsec % 1000000000;
+ }
+ }
+ static bool TimeOfDayLaterThan(struct timespec &ts) {
+ struct timespec now;
+ OffsetTimeOfDay(&now);
+ if (now.tv_sec > ts.tv_sec)
+ return true;
+ else if (now.tv_sec < ts.tv_sec)
+ return false;
+ else {
+ if (now.tv_nsec > ts.tv_nsec)
+ return true;
+ else
+ return false;
+ }
+ }
+
+protected:
+ // Classes that inherit from DNBTimer can see and modify these
+ std::unique_ptr<PThreadMutex> m_mutexAP;
+ struct timeval m_timeval;
+};
+
+#endif // #ifndef __DNBTimer_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/JSON.cpp b/gnu/llvm/lldb/tools/debugserver/source/JSON.cpp
new file mode 100644
index 00000000000..12d96d4ed4d
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/JSON.cpp
@@ -0,0 +1,592 @@
+//===--------------------- JSON.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "JSON.h"
+
+// C includes
+#include <assert.h>
+#include <limits.h>
+
+// C++ includes
+#include "lldb/Host/StringConvert.h"
+#include <iomanip>
+#include <sstream>
+
+using namespace lldb_private;
+
+std::string JSONString::json_string_quote_metachars(const std::string &s) {
+ if (s.find('"') == std::string::npos)
+ return s;
+
+ std::string output;
+ const size_t s_size = s.size();
+ const char *s_chars = s.c_str();
+ for (size_t i = 0; i < s_size; i++) {
+ unsigned char ch = *(s_chars + i);
+ if (ch == '"') {
+ output.push_back('\\');
+ }
+ output.push_back(ch);
+ }
+ return output;
+}
+
+JSONString::JSONString() : JSONValue(JSONValue::Kind::String), m_data() {}
+
+JSONString::JSONString(const char *s)
+ : JSONValue(JSONValue::Kind::String), m_data(s ? s : "") {}
+
+JSONString::JSONString(const std::string &s)
+ : JSONValue(JSONValue::Kind::String), m_data(s) {}
+
+void JSONString::Write(std::ostream &s) {
+ s << "\"" << json_string_quote_metachars(m_data).c_str() << "\"";
+}
+
+uint64_t JSONNumber::GetAsUnsigned() const {
+ switch (m_data_type) {
+ case DataType::Unsigned:
+ return m_data.m_unsigned;
+ case DataType::Signed:
+ return (uint64_t)m_data.m_signed;
+ case DataType::Double:
+ return (uint64_t)m_data.m_double;
+ }
+}
+
+int64_t JSONNumber::GetAsSigned() const {
+ switch (m_data_type) {
+ case DataType::Unsigned:
+ return (int64_t)m_data.m_unsigned;
+ case DataType::Signed:
+ return m_data.m_signed;
+ case DataType::Double:
+ return (int64_t)m_data.m_double;
+ }
+}
+
+double JSONNumber::GetAsDouble() const {
+ switch (m_data_type) {
+ case DataType::Unsigned:
+ return (double)m_data.m_unsigned;
+ case DataType::Signed:
+ return (double)m_data.m_signed;
+ case DataType::Double:
+ return m_data.m_double;
+ }
+}
+
+void JSONNumber::Write(std::ostream &s) {
+ switch (m_data_type) {
+ case DataType::Unsigned:
+ s << m_data.m_unsigned;
+ break;
+ case DataType::Signed:
+ s << m_data.m_signed;
+ break;
+ case DataType::Double:
+ // Set max precision to emulate %g.
+ s << std::setprecision(std::numeric_limits<double>::digits10 + 1);
+ s << m_data.m_double;
+ break;
+ }
+}
+
+JSONTrue::JSONTrue() : JSONValue(JSONValue::Kind::True) {}
+
+void JSONTrue::Write(std::ostream &s) { s << "true"; }
+
+JSONFalse::JSONFalse() : JSONValue(JSONValue::Kind::False) {}
+
+void JSONFalse::Write(std::ostream &s) { s << "false"; }
+
+JSONNull::JSONNull() : JSONValue(JSONValue::Kind::Null) {}
+
+void JSONNull::Write(std::ostream &s) { s << "null"; }
+
+JSONObject::JSONObject() : JSONValue(JSONValue::Kind::Object) {}
+
+void JSONObject::Write(std::ostream &s) {
+ bool first = true;
+ s << '{';
+ auto iter = m_elements.begin(), end = m_elements.end();
+ for (; iter != end; iter++) {
+ if (first)
+ first = false;
+ else
+ s << ',';
+ JSONString key(iter->first);
+ JSONValue::SP value(iter->second);
+ key.Write(s);
+ s << ':';
+ value->Write(s);
+ }
+ s << '}';
+}
+
+bool JSONObject::SetObject(const std::string &key, JSONValue::SP value) {
+ if (key.empty() || nullptr == value.get())
+ return false;
+ m_elements[key] = value;
+ return true;
+}
+
+JSONValue::SP JSONObject::GetObject(const std::string &key) const {
+ auto iter = m_elements.find(key), end = m_elements.end();
+ if (iter == end)
+ return JSONValue::SP();
+ return iter->second;
+}
+
+bool JSONObject::GetObjectAsBool(const std::string &key, bool &value) const {
+ auto value_sp = GetObject(key);
+ if (!value_sp) {
+ // The given key doesn't exist, so we have no value.
+ return false;
+ }
+
+ if (JSONTrue::classof(value_sp.get())) {
+ // We have the value, and it is true.
+ value = true;
+ return true;
+ } else if (JSONFalse::classof(value_sp.get())) {
+ // We have the value, and it is false.
+ value = false;
+ return true;
+ } else {
+ // We don't have a valid bool value for the given key.
+ return false;
+ }
+}
+
+bool JSONObject::GetObjectAsString(const std::string &key,
+ std::string &value) const {
+ auto value_sp = GetObject(key);
+ if (!value_sp) {
+ // The given key doesn't exist, so we have no value.
+ return false;
+ }
+
+ if (!JSONString::classof(value_sp.get()))
+ return false;
+
+ value = static_cast<JSONString *>(value_sp.get())->GetData();
+ return true;
+}
+
+JSONArray::JSONArray() : JSONValue(JSONValue::Kind::Array) {}
+
+void JSONArray::Write(std::ostream &s) {
+ bool first = true;
+ s << '[';
+ auto iter = m_elements.begin(), end = m_elements.end();
+ for (; iter != end; iter++) {
+ if (first)
+ first = false;
+ else
+ s << ',';
+ (*iter)->Write(s);
+ }
+ s << ']';
+}
+
+bool JSONArray::SetObject(Index i, JSONValue::SP value) {
+ if (value.get() == nullptr)
+ return false;
+ if (i < m_elements.size()) {
+ m_elements[i] = value;
+ return true;
+ }
+ if (i == m_elements.size()) {
+ m_elements.push_back(value);
+ return true;
+ }
+ return false;
+}
+
+bool JSONArray::AppendObject(JSONValue::SP value) {
+ if (value.get() == nullptr)
+ return false;
+ m_elements.push_back(value);
+ return true;
+}
+
+JSONValue::SP JSONArray::GetObject(Index i) {
+ if (i < m_elements.size())
+ return m_elements[i];
+ return JSONValue::SP();
+}
+
+JSONArray::Size JSONArray::GetNumElements() { return m_elements.size(); }
+
+JSONParser::JSONParser(const char *cstr) : StdStringExtractor(cstr) {}
+
+JSONParser::Token JSONParser::GetToken(std::string &value) {
+ std::ostringstream error;
+
+ value.clear();
+ SkipSpaces();
+ const uint64_t start_index = m_index;
+ const char ch = GetChar();
+ switch (ch) {
+ case '{':
+ return Token::ObjectStart;
+ case '}':
+ return Token::ObjectEnd;
+ case '[':
+ return Token::ArrayStart;
+ case ']':
+ return Token::ArrayEnd;
+ case ',':
+ return Token::Comma;
+ case ':':
+ return Token::Colon;
+ case '\0':
+ return Token::EndOfFile;
+ case 't':
+ if (GetChar() == 'r')
+ if (GetChar() == 'u')
+ if (GetChar() == 'e')
+ return Token::True;
+ break;
+
+ case 'f':
+ if (GetChar() == 'a')
+ if (GetChar() == 'l')
+ if (GetChar() == 's')
+ if (GetChar() == 'e')
+ return Token::False;
+ break;
+
+ case 'n':
+ if (GetChar() == 'u')
+ if (GetChar() == 'l')
+ if (GetChar() == 'l')
+ return Token::Null;
+ break;
+
+ case '"': {
+ while (true) {
+ bool was_escaped = false;
+ int escaped_ch = GetEscapedChar(was_escaped);
+ if (escaped_ch == -1) {
+ error << "error: an error occurred getting a character from offset "
+ << start_index;
+ value = error.str();
+ return Token::Status;
+
+ } else {
+ const bool is_end_quote = escaped_ch == '"';
+ const bool is_null = escaped_ch == 0;
+ if (was_escaped || (!is_end_quote && !is_null)) {
+ if (CHAR_MIN <= escaped_ch && escaped_ch <= CHAR_MAX) {
+ value.append(1, (char)escaped_ch);
+ } else {
+ error << "error: wide character support is needed for unicode "
+ "character 0x"
+ << std::setprecision(4) << std::hex << escaped_ch;
+ error << " at offset " << start_index;
+ value = error.str();
+ return Token::Status;
+ }
+ } else if (is_end_quote) {
+ return Token::String;
+ } else if (is_null) {
+ value = "error: missing end quote for string";
+ return Token::Status;
+ }
+ }
+ }
+ } break;
+
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': {
+ bool done = false;
+ bool got_decimal_point = false;
+ uint64_t exp_index = 0;
+ bool got_int_digits = (ch >= '0') && (ch <= '9');
+ bool got_frac_digits = false;
+ bool got_exp_digits = false;
+ while (!done) {
+ const char next_ch = PeekChar();
+ switch (next_ch) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (exp_index != 0) {
+ got_exp_digits = true;
+ } else if (got_decimal_point) {
+ got_frac_digits = true;
+ } else {
+ got_int_digits = true;
+ }
+ ++m_index; // Skip this character
+ break;
+
+ case '.':
+ if (got_decimal_point) {
+ error << "error: extra decimal point found at offset " << start_index;
+ value = error.str();
+ return Token::Status;
+ } else {
+ got_decimal_point = true;
+ ++m_index; // Skip this character
+ }
+ break;
+
+ case 'e':
+ case 'E':
+ if (exp_index != 0) {
+ error << "error: extra exponent character found at offset "
+ << start_index;
+ value = error.str();
+ return Token::Status;
+ } else {
+ exp_index = m_index;
+ ++m_index; // Skip this character
+ }
+ break;
+
+ case '+':
+ case '-':
+ // The '+' and '-' can only come after an exponent character...
+ if (exp_index == m_index - 1) {
+ ++m_index; // Skip the exponent sign character
+ } else {
+ error << "error: unexpected " << next_ch << " character at offset "
+ << start_index;
+ value = error.str();
+ return Token::Status;
+ }
+ break;
+
+ default:
+ done = true;
+ break;
+ }
+ }
+
+ if (m_index > start_index) {
+ value = m_packet.substr(start_index, m_index - start_index);
+ if (got_decimal_point) {
+ if (exp_index != 0) {
+ // We have an exponent, make sure we got exponent digits
+ if (got_exp_digits) {
+ return Token::Float;
+ } else {
+ error << "error: got exponent character but no exponent digits at "
+ "offset in float value \""
+ << value.c_str() << "\"";
+ value = error.str();
+ return Token::Status;
+ }
+ } else {
+ // No exponent, but we need at least one decimal after the decimal
+ // point
+ if (got_frac_digits) {
+ return Token::Float;
+ } else {
+ error << "error: no digits after decimal point \"" << value.c_str()
+ << "\"";
+ value = error.str();
+ return Token::Status;
+ }
+ }
+ } else {
+ // No decimal point
+ if (got_int_digits) {
+ // We need at least some integer digits to make an integer
+ return Token::Integer;
+ } else {
+ error << "error: no digits negate sign \"" << value.c_str() << "\"";
+ value = error.str();
+ return Token::Status;
+ }
+ }
+ } else {
+ error << "error: invalid number found at offset " << start_index;
+ value = error.str();
+ return Token::Status;
+ }
+ } break;
+ default:
+ break;
+ }
+ error << "error: failed to parse token at offset " << start_index
+ << " (around character '" << ch << "')";
+ value = error.str();
+ return Token::Status;
+}
+
+int JSONParser::GetEscapedChar(bool &was_escaped) {
+ was_escaped = false;
+ const char ch = GetChar();
+ if (ch == '\\') {
+ was_escaped = true;
+ const char ch2 = GetChar();
+ switch (ch2) {
+ case '"':
+ case '\\':
+ case '/':
+ default:
+ break;
+
+ case 'b':
+ return '\b';
+ case 'f':
+ return '\f';
+ case 'n':
+ return '\n';
+ case 'r':
+ return '\r';
+ case 't':
+ return '\t';
+ case 'u': {
+ const int hi_byte = DecodeHexU8();
+ const int lo_byte = DecodeHexU8();
+ if (hi_byte >= 0 && lo_byte >= 0)
+ return hi_byte << 8 | lo_byte;
+ return -1;
+ } break;
+ }
+ return ch2;
+ }
+ return ch;
+}
+
+JSONValue::SP JSONParser::ParseJSONObject() {
+ // The "JSONParser::Token::ObjectStart" token should have already been
+ // consumed
+ // by the time this function is called
+ std::unique_ptr<JSONObject> dict_up(new JSONObject());
+
+ std::string value;
+ std::string key;
+ while (true) {
+ JSONParser::Token token = GetToken(value);
+
+ if (token == JSONParser::Token::String) {
+ key.swap(value);
+ token = GetToken(value);
+ if (token == JSONParser::Token::Colon) {
+ JSONValue::SP value_sp = ParseJSONValue();
+ if (value_sp)
+ dict_up->SetObject(key, value_sp);
+ else
+ break;
+ }
+ } else if (token == JSONParser::Token::ObjectEnd) {
+ return JSONValue::SP(dict_up.release());
+ } else if (token == JSONParser::Token::Comma) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ return JSONValue::SP();
+}
+
+JSONValue::SP JSONParser::ParseJSONArray() {
+ // The "JSONParser::Token::ObjectStart" token should have already been
+ // consumed
+ // by the time this function is called
+ std::unique_ptr<JSONArray> array_up(new JSONArray());
+
+ std::string value;
+ std::string key;
+ while (true) {
+ JSONParser::Token token = GetToken(value);
+ if (token == JSONParser::Token::ArrayEnd)
+ return JSONValue::SP(array_up.release());
+ JSONValue::SP value_sp = ParseJSONValue(value, token);
+ if (value_sp)
+ array_up->AppendObject(value_sp);
+ else
+ break;
+
+ token = GetToken(value);
+ if (token == JSONParser::Token::Comma) {
+ continue;
+ } else if (token == JSONParser::Token::ArrayEnd) {
+ return JSONValue::SP(array_up.release());
+ } else {
+ break;
+ }
+ }
+ return JSONValue::SP();
+}
+
+JSONValue::SP JSONParser::ParseJSONValue() {
+ std::string value;
+ const JSONParser::Token token = GetToken(value);
+ return ParseJSONValue(value, token);
+}
+
+JSONValue::SP JSONParser::ParseJSONValue(const std::string &value,
+ const Token &token) {
+ switch (token) {
+ case JSONParser::Token::ObjectStart:
+ return ParseJSONObject();
+
+ case JSONParser::Token::ArrayStart:
+ return ParseJSONArray();
+
+ case JSONParser::Token::Integer: {
+ if (value.front() == '-') {
+ bool success = false;
+ int64_t sval = StringConvert::ToSInt64(value.c_str(), 0, 0, &success);
+ if (success)
+ return JSONValue::SP(new JSONNumber(sval));
+ } else {
+ bool success = false;
+ uint64_t uval = StringConvert::ToUInt64(value.c_str(), 0, 0, &success);
+ if (success)
+ return JSONValue::SP(new JSONNumber(uval));
+ }
+ } break;
+
+ case JSONParser::Token::Float: {
+ bool success = false;
+ double val = StringConvert::ToDouble(value.c_str(), 0.0, &success);
+ if (success)
+ return JSONValue::SP(new JSONNumber(val));
+ } break;
+
+ case JSONParser::Token::String:
+ return JSONValue::SP(new JSONString(value));
+
+ case JSONParser::Token::True:
+ return JSONValue::SP(new JSONTrue());
+
+ case JSONParser::Token::False:
+ return JSONValue::SP(new JSONFalse());
+
+ case JSONParser::Token::Null:
+ return JSONValue::SP(new JSONNull());
+
+ default:
+ break;
+ }
+ return JSONValue::SP();
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/JSON.h b/gnu/llvm/lldb/tools/debugserver/source/JSON.h
new file mode 100644
index 00000000000..70bfdd7259a
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/JSON.h
@@ -0,0 +1,302 @@
+//===---------------------JSON.h --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef utility_JSON_h_
+#define utility_JSON_h_
+
+#include "StdStringExtractor.h"
+
+// C includes
+#include <inttypes.h>
+#include <stdint.h>
+
+// C++ includes
+#include <map>
+#include <memory>
+#include <ostream>
+#include <string>
+#include <vector>
+
+class JSONValue {
+public:
+ virtual void Write(std::ostream &s) = 0;
+
+ typedef std::shared_ptr<JSONValue> SP;
+
+ enum class Kind { String, Number, True, False, Null, Object, Array };
+
+ JSONValue(Kind k) : m_kind(k) {}
+
+ Kind GetKind() const { return m_kind; }
+
+ virtual ~JSONValue() = default;
+
+private:
+ const Kind m_kind;
+};
+
+class JSONString : public JSONValue {
+public:
+ JSONString();
+ JSONString(const char *s);
+ JSONString(const std::string &s);
+
+ JSONString(const JSONString &s) = delete;
+ JSONString &operator=(const JSONString &s) = delete;
+
+ void Write(std::ostream &s) override;
+
+ typedef std::shared_ptr<JSONString> SP;
+
+ std::string GetData() { return m_data; }
+
+ static bool classof(const JSONValue *V) {
+ return V->GetKind() == JSONValue::Kind::String;
+ }
+
+ ~JSONString() override = default;
+
+private:
+ static std::string json_string_quote_metachars(const std::string &);
+
+ std::string m_data;
+};
+
+class JSONNumber : public JSONValue {
+public:
+ typedef std::shared_ptr<JSONNumber> SP;
+
+ // We cretae a constructor for all integer and floating point type with using
+ // templates and
+ // SFINAE to avoid having ambiguous overloads because of the implicit type
+ // promotion. If we
+ // would have constructors only with int64_t, uint64_t and double types then
+ // constructing a
+ // JSONNumber from an int32_t (or any other similar type) would fail to
+ // compile.
+
+ template <typename T, typename std::enable_if<
+ std::is_integral<T>::value &&
+ std::is_unsigned<T>::value>::type * = nullptr>
+ explicit JSONNumber(T u)
+ : JSONValue(JSONValue::Kind::Number), m_data_type(DataType::Unsigned) {
+ m_data.m_unsigned = u;
+ }
+
+ template <typename T,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_signed<T>::value>::type * = nullptr>
+ explicit JSONNumber(T s)
+ : JSONValue(JSONValue::Kind::Number), m_data_type(DataType::Signed) {
+ m_data.m_signed = s;
+ }
+
+ template <typename T, typename std::enable_if<
+ std::is_floating_point<T>::value>::type * = nullptr>
+ explicit JSONNumber(T d)
+ : JSONValue(JSONValue::Kind::Number), m_data_type(DataType::Double) {
+ m_data.m_double = d;
+ }
+
+ ~JSONNumber() override = default;
+
+ JSONNumber(const JSONNumber &s) = delete;
+ JSONNumber &operator=(const JSONNumber &s) = delete;
+
+ void Write(std::ostream &s) override;
+
+ uint64_t GetAsUnsigned() const;
+
+ int64_t GetAsSigned() const;
+
+ double GetAsDouble() const;
+
+ static bool classof(const JSONValue *V) {
+ return V->GetKind() == JSONValue::Kind::Number;
+ }
+
+private:
+ enum class DataType : uint8_t { Unsigned, Signed, Double } m_data_type;
+
+ union {
+ uint64_t m_unsigned;
+ int64_t m_signed;
+ double m_double;
+ } m_data;
+};
+
+class JSONTrue : public JSONValue {
+public:
+ JSONTrue();
+
+ JSONTrue(const JSONTrue &s) = delete;
+ JSONTrue &operator=(const JSONTrue &s) = delete;
+
+ void Write(std::ostream &s) override;
+
+ typedef std::shared_ptr<JSONTrue> SP;
+
+ static bool classof(const JSONValue *V) {
+ return V->GetKind() == JSONValue::Kind::True;
+ }
+
+ ~JSONTrue() override = default;
+};
+
+class JSONFalse : public JSONValue {
+public:
+ JSONFalse();
+
+ JSONFalse(const JSONFalse &s) = delete;
+ JSONFalse &operator=(const JSONFalse &s) = delete;
+
+ void Write(std::ostream &s) override;
+
+ typedef std::shared_ptr<JSONFalse> SP;
+
+ static bool classof(const JSONValue *V) {
+ return V->GetKind() == JSONValue::Kind::False;
+ }
+
+ ~JSONFalse() override = default;
+};
+
+class JSONNull : public JSONValue {
+public:
+ JSONNull();
+
+ JSONNull(const JSONNull &s) = delete;
+ JSONNull &operator=(const JSONNull &s) = delete;
+
+ void Write(std::ostream &s) override;
+
+ typedef std::shared_ptr<JSONNull> SP;
+
+ static bool classof(const JSONValue *V) {
+ return V->GetKind() == JSONValue::Kind::Null;
+ }
+
+ ~JSONNull() override = default;
+};
+
+class JSONObject : public JSONValue {
+public:
+ JSONObject();
+
+ JSONObject(const JSONObject &s) = delete;
+ JSONObject &operator=(const JSONObject &s) = delete;
+
+ void Write(std::ostream &s) override;
+
+ typedef std::shared_ptr<JSONObject> SP;
+
+ static bool classof(const JSONValue *V) {
+ return V->GetKind() == JSONValue::Kind::Object;
+ }
+
+ bool SetObject(const std::string &key, JSONValue::SP value);
+
+ JSONValue::SP GetObject(const std::string &key) const;
+
+ /// Return keyed value as bool
+ ///
+ /// \param[in] key
+ /// The value of the key to lookup
+ ///
+ /// \param[out] value
+ /// The value of the key as a bool. Undefined if the key doesn't
+ /// exist or if the key is not either true or false.
+ ///
+ /// \return
+ /// true if the key existed as was a bool value; false otherwise.
+ /// Note the return value is *not* the value of the bool, use
+ /// \b value for that.
+ bool GetObjectAsBool(const std::string &key, bool &value) const;
+
+ bool GetObjectAsString(const std::string &key, std::string &value) const;
+
+ ~JSONObject() override = default;
+
+private:
+ typedef std::map<std::string, JSONValue::SP> Map;
+ typedef Map::iterator Iterator;
+ Map m_elements;
+};
+
+class JSONArray : public JSONValue {
+public:
+ JSONArray();
+
+ JSONArray(const JSONArray &s) = delete;
+ JSONArray &operator=(const JSONArray &s) = delete;
+
+ void Write(std::ostream &s) override;
+
+ typedef std::shared_ptr<JSONArray> SP;
+
+ static bool classof(const JSONValue *V) {
+ return V->GetKind() == JSONValue::Kind::Array;
+ }
+
+private:
+ typedef std::vector<JSONValue::SP> Vector;
+ typedef Vector::iterator Iterator;
+ typedef Vector::size_type Index;
+ typedef Vector::size_type Size;
+
+public:
+ bool SetObject(Index i, JSONValue::SP value);
+
+ bool AppendObject(JSONValue::SP value);
+
+ JSONValue::SP GetObject(Index i);
+
+ Size GetNumElements();
+
+ ~JSONArray() override = default;
+
+ Vector m_elements;
+};
+
+class JSONParser : public StdStringExtractor {
+public:
+ enum Token {
+ Invalid,
+ Status,
+ ObjectStart,
+ ObjectEnd,
+ ArrayStart,
+ ArrayEnd,
+ Comma,
+ Colon,
+ String,
+ Integer,
+ Float,
+ True,
+ False,
+ Null,
+ EndOfFile
+ };
+
+ JSONParser(const char *cstr);
+
+ int GetEscapedChar(bool &was_escaped);
+
+ Token GetToken(std::string &value);
+
+ JSONValue::SP ParseJSONValue();
+
+protected:
+ JSONValue::SP ParseJSONValue(const std::string &value, const Token &token);
+
+ JSONValue::SP ParseJSONObject();
+
+ JSONValue::SP ParseJSONArray();
+};
+
+#endif // utility_JSON_h_
diff --git a/gnu/llvm/lldb/tools/debugserver/source/JSONGenerator.h b/gnu/llvm/lldb/tools/debugserver/source/JSONGenerator.h
new file mode 100644
index 00000000000..70708f9a5ac
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/JSONGenerator.h
@@ -0,0 +1,314 @@
+//===-- JSONGenerator.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __JSONGenerator_h_
+#define __JSONGenerator_h_
+
+
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+/// \class JSONGenerator JSONGenerator.h
+/// A class which can construct structured data for the sole purpose
+/// of printing it in JSON format.
+///
+/// A stripped down version of lldb's StructuredData objects which are much
+/// general purpose. This variant is intended only for assembling information
+/// and printing it as a JSON string.
+
+class JSONGenerator {
+public:
+ class Object;
+ class Array;
+ class Integer;
+ class Float;
+ class Boolean;
+ class String;
+ class Dictionary;
+ class Generic;
+
+ typedef std::shared_ptr<Object> ObjectSP;
+ typedef std::shared_ptr<Array> ArraySP;
+ typedef std::shared_ptr<Integer> IntegerSP;
+ typedef std::shared_ptr<Float> FloatSP;
+ typedef std::shared_ptr<Boolean> BooleanSP;
+ typedef std::shared_ptr<String> StringSP;
+ typedef std::shared_ptr<Dictionary> DictionarySP;
+ typedef std::shared_ptr<Generic> GenericSP;
+
+ enum class Type {
+ eTypeInvalid = -1,
+ eTypeNull = 0,
+ eTypeGeneric,
+ eTypeArray,
+ eTypeInteger,
+ eTypeFloat,
+ eTypeBoolean,
+ eTypeString,
+ eTypeDictionary
+ };
+
+ class Object : public std::enable_shared_from_this<Object> {
+ public:
+ Object(Type t = Type::eTypeInvalid) : m_type(t) {}
+
+ virtual ~Object() {}
+
+ virtual bool IsValid() const { return true; }
+
+ virtual void Clear() { m_type = Type::eTypeInvalid; }
+
+ Type GetType() const { return m_type; }
+
+ void SetType(Type t) { m_type = t; }
+
+ Array *GetAsArray() {
+ if (m_type == Type::eTypeArray)
+ return (Array *)this;
+ return NULL;
+ }
+
+ Dictionary *GetAsDictionary() {
+ if (m_type == Type::eTypeDictionary)
+ return (Dictionary *)this;
+ return NULL;
+ }
+
+ Integer *GetAsInteger() {
+ if (m_type == Type::eTypeInteger)
+ return (Integer *)this;
+ return NULL;
+ }
+
+ Float *GetAsFloat() {
+ if (m_type == Type::eTypeFloat)
+ return (Float *)this;
+ return NULL;
+ }
+
+ Boolean *GetAsBoolean() {
+ if (m_type == Type::eTypeBoolean)
+ return (Boolean *)this;
+ return NULL;
+ }
+
+ String *GetAsString() {
+ if (m_type == Type::eTypeString)
+ return (String *)this;
+ return NULL;
+ }
+
+ Generic *GetAsGeneric() {
+ if (m_type == Type::eTypeGeneric)
+ return (Generic *)this;
+ return NULL;
+ }
+
+ virtual void Dump(std::ostream &s) const = 0;
+
+ private:
+ Type m_type;
+ };
+
+ class Array : public Object {
+ public:
+ Array() : Object(Type::eTypeArray) {}
+
+ virtual ~Array() {}
+
+ void AddItem(ObjectSP item) { m_items.push_back(item); }
+
+ void Dump(std::ostream &s) const override {
+ s << "[";
+ const size_t arrsize = m_items.size();
+ for (size_t i = 0; i < arrsize; ++i) {
+ m_items[i]->Dump(s);
+ if (i + 1 < arrsize)
+ s << ",";
+ }
+ s << "]";
+ }
+
+ protected:
+ typedef std::vector<ObjectSP> collection;
+ collection m_items;
+ };
+
+ class Integer : public Object {
+ public:
+ Integer(uint64_t value = 0) : Object(Type::eTypeInteger), m_value(value) {}
+
+ virtual ~Integer() {}
+
+ void SetValue(uint64_t value) { m_value = value; }
+
+ void Dump(std::ostream &s) const override { s << m_value; }
+
+ protected:
+ uint64_t m_value;
+ };
+
+ class Float : public Object {
+ public:
+ Float(double d = 0.0) : Object(Type::eTypeFloat), m_value(d) {}
+
+ virtual ~Float() {}
+
+ void SetValue(double value) { m_value = value; }
+
+ void Dump(std::ostream &s) const override { s << m_value; }
+
+ protected:
+ double m_value;
+ };
+
+ class Boolean : public Object {
+ public:
+ Boolean(bool b = false) : Object(Type::eTypeBoolean), m_value(b) {}
+
+ virtual ~Boolean() {}
+
+ void SetValue(bool value) { m_value = value; }
+
+ void Dump(std::ostream &s) const override {
+ if (m_value)
+ s << "true";
+ else
+ s << "false";
+ }
+
+ protected:
+ bool m_value;
+ };
+
+ class String : public Object {
+ public:
+ String() : Object(Type::eTypeString), m_value() {}
+
+ String(const std::string &s) : Object(Type::eTypeString), m_value(s) {}
+
+ String(const std::string &&s) : Object(Type::eTypeString), m_value(s) {}
+
+ void SetValue(const std::string &string) { m_value = string; }
+
+ void Dump(std::ostream &s) const override {
+ std::string quoted;
+ const size_t strsize = m_value.size();
+ for (size_t i = 0; i < strsize; ++i) {
+ char ch = m_value[i];
+ if (ch == '"')
+ quoted.push_back('\\');
+ quoted.push_back(ch);
+ }
+ s << '"' << quoted.c_str() << '"';
+ }
+
+ protected:
+ std::string m_value;
+ };
+
+ class Dictionary : public Object {
+ public:
+ Dictionary() : Object(Type::eTypeDictionary), m_dict() {}
+
+ virtual ~Dictionary() {}
+
+ void AddItem(std::string key, ObjectSP value) {
+ m_dict.push_back(Pair(key, value));
+ }
+
+ void AddIntegerItem(std::string key, uint64_t value) {
+ AddItem(key, ObjectSP(new Integer(value)));
+ }
+
+ void AddFloatItem(std::string key, double value) {
+ AddItem(key, ObjectSP(new Float(value)));
+ }
+
+ void AddStringItem(std::string key, std::string value) {
+ AddItem(key, ObjectSP(new String(std::move(value))));
+ }
+
+ void AddBytesAsHexASCIIString(std::string key, const uint8_t *src,
+ size_t src_len) {
+ if (src && src_len) {
+ std::ostringstream strm;
+ for (size_t i = 0; i < src_len; i++)
+ strm << std::setfill('0') << std::hex << std::right << std::setw(2)
+ << ((uint32_t)(src[i]));
+ AddItem(key, ObjectSP(new String(std::move(strm.str()))));
+ } else {
+ AddItem(key, ObjectSP(new String()));
+ }
+ }
+
+ void AddBooleanItem(std::string key, bool value) {
+ AddItem(key, ObjectSP(new Boolean(value)));
+ }
+
+ void Dump(std::ostream &s) const override {
+ bool have_printed_one_elem = false;
+ s << "{";
+ for (collection::const_iterator iter = m_dict.begin();
+ iter != m_dict.end(); ++iter) {
+ if (!have_printed_one_elem) {
+ have_printed_one_elem = true;
+ } else {
+ s << ",";
+ }
+ s << "\"" << iter->first.c_str() << "\":";
+ iter->second->Dump(s);
+ }
+ s << "}";
+ }
+
+ protected:
+ // Keep the dictionary as a vector so the dictionary doesn't reorder itself
+ // when you dump it
+ // We aren't accessing keys by name, so this won't affect performance
+ typedef std::pair<std::string, ObjectSP> Pair;
+ typedef std::vector<Pair> collection;
+ collection m_dict;
+ };
+
+ class Null : public Object {
+ public:
+ Null() : Object(Type::eTypeNull) {}
+
+ virtual ~Null() {}
+
+ bool IsValid() const override { return false; }
+
+ void Dump(std::ostream &s) const override { s << "null"; }
+
+ protected:
+ };
+
+ class Generic : public Object {
+ public:
+ explicit Generic(void *object = nullptr)
+ : Object(Type::eTypeGeneric), m_object(object) {}
+
+ void SetValue(void *value) { m_object = value; }
+
+ void *GetValue() const { return m_object; }
+
+ bool IsValid() const override { return m_object != nullptr; }
+
+ void Dump(std::ostream &s) const override;
+
+ private:
+ void *m_object;
+ };
+
+}; // class JSONGenerator
+
+#endif // __JSONGenerator_h_
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFBundle.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFBundle.cpp
new file mode 100644
index 00000000000..6971c1f4c32
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFBundle.cpp
@@ -0,0 +1,69 @@
+//===-- CFBundle.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/16/08.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CFBundle.h"
+#include "CFString.h"
+
+// CFBundle constructor
+CFBundle::CFBundle(const char *path)
+ : CFReleaser<CFBundleRef>(), m_bundle_url() {
+ if (path && path[0])
+ SetPath(path);
+}
+
+// CFBundle copy constructor
+CFBundle::CFBundle(const CFBundle &rhs)
+ : CFReleaser<CFBundleRef>(rhs), m_bundle_url(rhs.m_bundle_url) {}
+
+// CFBundle copy constructor
+CFBundle &CFBundle::operator=(const CFBundle &rhs) {
+ if (this != &rhs)
+ *this = rhs;
+ return *this;
+}
+
+// Destructor
+CFBundle::~CFBundle() {}
+
+// Set the path for a bundle by supplying a
+bool CFBundle::SetPath(const char *path) {
+ CFAllocatorRef alloc = kCFAllocatorDefault;
+ // Release our old bundle and ULR
+ reset(); // This class is a CFReleaser<CFBundleRef>
+ m_bundle_url.reset();
+ // Make a CFStringRef from the supplied path
+ CFString cf_path;
+ cf_path.SetFileSystemRepresentation(path);
+ if (cf_path.get()) {
+ // Make our Bundle URL
+ m_bundle_url.reset(::CFURLCreateWithFileSystemPath(
+ alloc, cf_path.get(), kCFURLPOSIXPathStyle, true));
+ if (m_bundle_url.get()) {
+ reset(::CFBundleCreate(alloc, m_bundle_url.get()));
+ }
+ }
+ return get() != NULL;
+}
+
+CFStringRef CFBundle::GetIdentifier() const {
+ CFBundleRef bundle = get();
+ if (bundle != NULL)
+ return ::CFBundleGetIdentifier(bundle);
+ return NULL;
+}
+
+CFURLRef CFBundle::CopyExecutableURL() const {
+ CFBundleRef bundle = get();
+ if (bundle != NULL)
+ return CFBundleCopyExecutableURL(bundle);
+ return NULL;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFBundle.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFBundle.h
new file mode 100644
index 00000000000..f49dc30f1f8
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFBundle.h
@@ -0,0 +1,35 @@
+//===-- CFBundle.h ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/16/08.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __CFBundle_h__
+#define __CFBundle_h__
+
+#include "CFUtils.h"
+
+class CFBundle : public CFReleaser<CFBundleRef> {
+public:
+ // Constructors and Destructors
+ CFBundle(const char *path = NULL);
+ CFBundle(const CFBundle &rhs);
+ CFBundle &operator=(const CFBundle &rhs);
+ virtual ~CFBundle();
+ bool SetPath(const char *path);
+
+ CFStringRef GetIdentifier() const;
+
+ CFURLRef CopyExecutableURL() const;
+
+protected:
+ CFReleaser<CFURLRef> m_bundle_url;
+};
+
+#endif // #ifndef __CFBundle_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFString.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFString.cpp
new file mode 100644
index 00000000000..637ba65ae4d
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFString.cpp
@@ -0,0 +1,154 @@
+//===-- CFString.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/16/08.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CFString.h"
+#include <glob.h>
+#include <string>
+
+// CFString constructor
+CFString::CFString(CFStringRef s) : CFReleaser<CFStringRef>(s) {}
+
+// CFString copy constructor
+CFString::CFString(const CFString &rhs) : CFReleaser<CFStringRef>(rhs) {}
+
+// CFString copy constructor
+CFString &CFString::operator=(const CFString &rhs) {
+ if (this != &rhs)
+ *this = rhs;
+ return *this;
+}
+
+CFString::CFString(const char *cstr, CFStringEncoding cstr_encoding)
+ : CFReleaser<CFStringRef>() {
+ if (cstr && cstr[0]) {
+ reset(
+ ::CFStringCreateWithCString(kCFAllocatorDefault, cstr, cstr_encoding));
+ }
+}
+
+// Destructor
+CFString::~CFString() {}
+
+const char *CFString::GetFileSystemRepresentation(std::string &s) {
+ return CFString::FileSystemRepresentation(get(), s);
+}
+
+CFStringRef CFString::SetFileSystemRepresentation(const char *path) {
+ CFStringRef new_value = NULL;
+ if (path && path[0])
+ new_value =
+ ::CFStringCreateWithFileSystemRepresentation(kCFAllocatorDefault, path);
+ reset(new_value);
+ return get();
+}
+
+CFStringRef CFString::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
+CFString::SetFileSystemRepresentationAndExpandTilde(const char *path) {
+ std::string expanded_path;
+ if (CFString::GlobPath(path, expanded_path))
+ SetFileSystemRepresentation(expanded_path.c_str());
+ else
+ reset();
+ return get();
+}
+
+const char *CFString::UTF8(std::string &str) {
+ return CFString::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 *CFString::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;
+}
+
+// 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 *CFString::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 CFString::GetLength() const {
+ CFStringRef str = get();
+ if (str)
+ return CFStringGetLength(str);
+ return 0;
+}
+
+const char *CFString::GlobPath(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();
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFString.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFString.h
new file mode 100644
index 00000000000..d1bd5682689
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFString.h
@@ -0,0 +1,40 @@
+//===-- CFString.h ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/16/08.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __CFString_h__
+#define __CFString_h__
+
+#include "CFUtils.h"
+#include <iosfwd>
+
+class CFString : public CFReleaser<CFStringRef> {
+public:
+ // Constructors and Destructors
+ CFString(CFStringRef cf_str = NULL);
+ CFString(const char *s, CFStringEncoding encoding = kCFStringEncodingUTF8);
+ CFString(const CFString &rhs);
+ CFString &operator=(const CFString &rhs);
+ virtual ~CFString();
+
+ 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 *GlobPath(const char *path, std::string &expanded_path);
+};
+
+#endif // #ifndef __CFString_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFUtils.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFUtils.h
new file mode 100644
index 00000000000..b567524ce63
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CFUtils.h
@@ -0,0 +1,75 @@
+//===-- CFUtils.h -----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 3/5/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __CFUtils_h__
+#define __CFUtils_h__
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#ifdef __cplusplus
+
+// 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.
+template <class T> class CFReleaser {
+public:
+ // Type names for the avlue
+ typedef T element_type;
+
+ // Constructors and destructors
+ CFReleaser(T ptr = NULL) : _ptr(ptr) {}
+ CFReleaser(const CFReleaser &copy) : _ptr(copy.get()) {
+ if (get())
+ ::CFRetain(get());
+ }
+ virtual ~CFReleaser() { reset(); }
+
+ // Assignments
+ CFReleaser &operator=(const CFReleaser<T> &copy) {
+ if (copy != *this) {
+ // Replace our owned pointer with the new one
+ reset(copy.get());
+ // Retain the current pointer that we own
+ if (get())
+ ::CFRetain(get());
+ }
+ }
+ // Get the address of the contained type
+ T *ptr_address() { return &_ptr; }
+
+ // Access the pointer itself
+ const T get() const { return _ptr; }
+ T get() { 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 != _ptr) {
+ if (_ptr != NULL)
+ ::CFRelease(_ptr);
+ _ptr = ptr;
+ }
+ }
+
+ // Release ownership without calling CFRelease
+ T release() {
+ T tmp = _ptr;
+ _ptr = NULL;
+ return tmp;
+ }
+
+private:
+ element_type _ptr;
+};
+
+#endif // #ifdef __cplusplus
+#endif // #ifndef __CFUtils_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CMakeLists.txt b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CMakeLists.txt
new file mode 100644
index 00000000000..73ba6492a0e
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/CMakeLists.txt
@@ -0,0 +1,43 @@
+# The debugserver build needs to conditionally include files depending on the
+# target architecture.
+#
+# Switch on the architecture specified by TARGET_TRIPLE, as
+# the llvm and swift build systems use this variable to identify the
+# target (through LLVM_HOST_TRIPLE).
+#
+# It would be possible to switch on CMAKE_OSX_ARCHITECTURES, but the swift
+# build does not provide it, preferring instead to pass arch-specific
+# CFLAGS etc explicitly. Switching on LLVM_HOST_TRIPLE is also an option,
+# but it breaks down when cross-compiling.
+
+if(TARGET_TRIPLE)
+ string(REGEX MATCH "^[^-]*" LLDB_DEBUGSERVER_ARCH ${TARGET_TRIPLE})
+else()
+ set(LLDB_DEBUGSERVER_ARCH ${CMAKE_OSX_ARCHITECTURES})
+endif()
+
+if("${LLDB_DEBUGSERVER_ARCH}" MATCHES ".*arm.*")
+ list(APPEND SOURCES arm/DNBArchImpl.cpp arm64/DNBArchImplARM64.cpp)
+ include_directories(${CURRENT_SOURCE_DIR}/arm ${CURRENT_SOURCE_DIR}/arm64)
+endif()
+
+if(NOT LLDB_DEBUGSERVER_ARCH OR "${LLDB_DEBUGSERVER_ARCH}" MATCHES ".*86.*")
+ list(APPEND SOURCES i386/DNBArchImplI386.cpp x86_64/DNBArchImplX86_64.cpp)
+ include_directories(${CURRENT_SOURCE_DIR}/i386 ${CURRENT_SOURCE_DIR}/x86_64)
+endif()
+
+if("${LLDB_DEBUGSERVER_ARCH}" MATCHES ".*ppc.*")
+ list(APPEND SOURCES ppc/DNBArchImpl.cpp)
+ include_directories(${CURRENT_SOURCE_DIR}/ppc)
+endif()
+
+add_subdirectory(DarwinLog)
+
+include_directories(..)
+
+include_directories(${LLDB_SOURCE_DIR}/tools/debugserver/source)
+add_library(lldbDebugserverArchSupport
+ ${SOURCES}
+ )
+
+set_target_properties(lldbDebugserverArchSupport PROPERTIES FOLDER "lldb libraries/debugserver")
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStore.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStore.cpp
new file mode 100644
index 00000000000..2d831252bfe
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStore.cpp
@@ -0,0 +1,13 @@
+//===-- ActivityStore.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ActivityStore.h"
+
+ActivityStore::ActivityStore() {}
+
+ActivityStore::~ActivityStore() {}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStore.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStore.h
new file mode 100644
index 00000000000..b66a789592c
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStore.h
@@ -0,0 +1,29 @@
+//===-- ActivityStore.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef ActivityStore_h
+#define ActivityStore_h
+
+#include <string>
+
+#include "ActivityStreamSPI.h"
+
+class ActivityStore {
+public:
+ virtual ~ActivityStore();
+
+ virtual const char *GetActivityForID(os_activity_id_t activity_id) const = 0;
+
+ virtual std::string
+ GetActivityChainForID(os_activity_id_t activity_id) const = 0;
+
+protected:
+ ActivityStore();
+};
+
+#endif /* ActivityStore_h */
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStreamSPI.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStreamSPI.h
new file mode 100644
index 00000000000..99721f69a2d
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/ActivityStreamSPI.h
@@ -0,0 +1,190 @@
+//===-- ActivityStreamSPI.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef ActivityStreamSPI_h
+#define ActivityStreamSPI_h
+
+#include <sys/time.h>
+#include <xpc/xpc.h>
+
+#define OS_ACTIVITY_MAX_CALLSTACK 32
+
+// Enums
+
+enum {
+ OS_ACTIVITY_STREAM_PROCESS_ONLY = 0x00000001,
+ OS_ACTIVITY_STREAM_SKIP_DECODE = 0x00000002,
+ OS_ACTIVITY_STREAM_PAYLOAD = 0x00000004,
+ OS_ACTIVITY_STREAM_HISTORICAL = 0x00000008,
+ OS_ACTIVITY_STREAM_CALLSTACK = 0x00000010,
+ OS_ACTIVITY_STREAM_DEBUG = 0x00000020,
+ OS_ACTIVITY_STREAM_BUFFERED = 0x00000040,
+ OS_ACTIVITY_STREAM_NO_SENSITIVE = 0x00000080,
+ OS_ACTIVITY_STREAM_INFO = 0x00000100,
+ OS_ACTIVITY_STREAM_PROMISCUOUS = 0x00000200,
+ OS_ACTIVITY_STREAM_PRECISE_TIMESTAMPS = 0x00000200
+};
+typedef uint32_t os_activity_stream_flag_t;
+
+enum {
+ OS_ACTIVITY_STREAM_TYPE_ACTIVITY_CREATE = 0x0201,
+ OS_ACTIVITY_STREAM_TYPE_ACTIVITY_TRANSITION = 0x0202,
+ OS_ACTIVITY_STREAM_TYPE_ACTIVITY_USERACTION = 0x0203,
+
+ OS_ACTIVITY_STREAM_TYPE_TRACE_MESSAGE = 0x0300,
+
+ OS_ACTIVITY_STREAM_TYPE_LOG_MESSAGE = 0x0400,
+ OS_ACTIVITY_STREAM_TYPE_LEGACY_LOG_MESSAGE = 0x0480,
+
+ OS_ACTIVITY_STREAM_TYPE_SIGNPOST_BEGIN = 0x0601,
+ OS_ACTIVITY_STREAM_TYPE_SIGNPOST_END = 0x0602,
+ OS_ACTIVITY_STREAM_TYPE_SIGNPOST_EVENT = 0x0603,
+
+ OS_ACTIVITY_STREAM_TYPE_STATEDUMP_EVENT = 0x0A00,
+};
+typedef uint32_t os_activity_stream_type_t;
+
+enum {
+ OS_ACTIVITY_STREAM_EVENT_STARTED = 1,
+ OS_ACTIVITY_STREAM_EVENT_STOPPED = 2,
+ OS_ACTIVITY_STREAM_EVENT_FAILED = 3,
+ OS_ACTIVITY_STREAM_EVENT_CHUNK_STARTED = 4,
+ OS_ACTIVITY_STREAM_EVENT_CHUNK_FINISHED = 5,
+};
+typedef uint32_t os_activity_stream_event_t;
+
+// Types
+
+typedef uint64_t os_activity_id_t;
+typedef struct os_activity_stream_s *os_activity_stream_t;
+typedef struct os_activity_stream_entry_s *os_activity_stream_entry_t;
+
+#define OS_ACTIVITY_STREAM_COMMON() \
+ uint64_t trace_id; \
+ uint64_t timestamp; \
+ uint64_t thread; \
+ const uint8_t *image_uuid; \
+ const char *image_path; \
+ struct timeval tv_gmt; \
+ struct timezone tz; \
+ uint32_t offset
+
+typedef struct os_activity_stream_common_s {
+ OS_ACTIVITY_STREAM_COMMON();
+} * os_activity_stream_common_t;
+
+struct os_activity_create_s {
+ OS_ACTIVITY_STREAM_COMMON();
+ const char *name;
+ os_activity_id_t creator_aid;
+ uint64_t unique_pid;
+};
+
+struct os_activity_transition_s {
+ OS_ACTIVITY_STREAM_COMMON();
+ os_activity_id_t transition_id;
+};
+
+typedef struct os_log_message_s {
+ OS_ACTIVITY_STREAM_COMMON();
+ const char *format;
+ const uint8_t *buffer;
+ size_t buffer_sz;
+ const uint8_t *privdata;
+ size_t privdata_sz;
+ const char *subsystem;
+ const char *category;
+ uint32_t oversize_id;
+ uint8_t ttl;
+ bool persisted;
+} * os_log_message_t;
+
+typedef struct os_trace_message_v2_s {
+ OS_ACTIVITY_STREAM_COMMON();
+ const char *format;
+ const void *buffer;
+ size_t bufferLen;
+ xpc_object_t __unsafe_unretained payload;
+} * os_trace_message_v2_t;
+
+typedef struct os_activity_useraction_s {
+ OS_ACTIVITY_STREAM_COMMON();
+ const char *action;
+ bool persisted;
+} * os_activity_useraction_t;
+
+typedef struct os_signpost_s {
+ OS_ACTIVITY_STREAM_COMMON();
+ const char *format;
+ const uint8_t *buffer;
+ size_t buffer_sz;
+ const uint8_t *privdata;
+ size_t privdata_sz;
+ const char *subsystem;
+ const char *category;
+ uint64_t duration_nsec;
+ uint32_t callstack_depth;
+ uint64_t callstack[OS_ACTIVITY_MAX_CALLSTACK];
+} * os_signpost_t;
+
+typedef struct os_activity_statedump_s {
+ OS_ACTIVITY_STREAM_COMMON();
+ char *message;
+ size_t message_size;
+ char image_path_buffer[PATH_MAX];
+} * os_activity_statedump_t;
+
+struct os_activity_stream_entry_s {
+ os_activity_stream_type_t type;
+
+ // information about the process streaming the data
+ pid_t pid;
+ uint64_t proc_id;
+ const uint8_t *proc_imageuuid;
+ const char *proc_imagepath;
+
+ // the activity associated with this streamed event
+ os_activity_id_t activity_id;
+ os_activity_id_t parent_id;
+
+ union {
+ struct os_activity_stream_common_s common;
+ struct os_activity_create_s activity_create;
+ struct os_activity_transition_s activity_transition;
+ struct os_log_message_s log_message;
+ struct os_trace_message_v2_s trace_message;
+ struct os_activity_useraction_s useraction;
+ struct os_signpost_s signpost;
+ struct os_activity_statedump_s statedump;
+ };
+};
+
+// Blocks
+
+typedef bool (^os_activity_stream_block_t)(os_activity_stream_entry_t entry,
+ int error);
+
+typedef void (^os_activity_stream_event_block_t)(
+ os_activity_stream_t stream, os_activity_stream_event_t event);
+
+// SPI entry point prototypes
+
+typedef os_activity_stream_t (*os_activity_stream_for_pid_t)(
+ pid_t pid, os_activity_stream_flag_t flags,
+ os_activity_stream_block_t stream_block);
+
+typedef void (*os_activity_stream_resume_t)(os_activity_stream_t stream);
+
+typedef void (*os_activity_stream_cancel_t)(os_activity_stream_t stream);
+
+typedef char *(*os_log_copy_formatted_message_t)(os_log_message_t log_message);
+
+typedef void (*os_activity_stream_set_event_handler_t)(
+ os_activity_stream_t stream, os_activity_stream_event_block_t block);
+
+#endif /* ActivityStreamSPI_h */
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/CMakeLists.txt b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/CMakeLists.txt
new file mode 100644
index 00000000000..71abb36358a
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/CMakeLists.txt
@@ -0,0 +1,17 @@
+# Due to sources including headers like:
+# #include "MacOSX/i386/DNBArchImplI386.h"
+# we must include the grandparent directory...
+include_directories(${LLDB_SOURCE_DIR}/tools/debugserver/source)
+
+add_library(lldbDebugserverDarwin_DarwinLog
+ ActivityStore.cpp
+ DarwinLogCollector.cpp
+ LogFilter.cpp
+ LogFilterChain.cpp
+ LogFilterExactMatch.cpp
+ LogFilterRegex.cpp
+ LogMessage.cpp
+ LogMessageOsLog.cpp
+ )
+
+set_target_properties(lldbDebugserverDarwin_DarwinLog PROPERTIES FOLDER "lldb libraries/debugserver")
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogCollector.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogCollector.cpp
new file mode 100644
index 00000000000..a9f8956b8d4
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogCollector.cpp
@@ -0,0 +1,699 @@
+//===-- DarwinLogCollector.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DarwinLogCollector.h"
+#include "ActivityStreamSPI.h"
+
+#include <dlfcn.h>
+
+#include <cinttypes>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+#include "DNB.h"
+#include "DNBLog.h"
+#include "DarwinLogTypes.h"
+#include "LogFilterChain.h"
+#include "LogFilterExactMatch.h"
+#include "LogFilterRegex.h"
+#include "LogMessageOsLog.h"
+#include "MachProcess.h"
+#include "RNBContext.h"
+#include "RNBDefs.h"
+#include "RNBRemote.h"
+
+// Use an anonymous namespace for variables and methods that have no
+// reason to leak out through the interface.
+namespace {
+/// Specify max depth that the activity parent-child chain will search
+/// back to get the full activity chain name. If we do more than this,
+/// we assume either we hit a loop or it's just too long.
+static const size_t MAX_ACTIVITY_CHAIN_DEPTH = 10;
+
+// Used to tap into and retrieve logs from target process.
+// (Consumer of os_log).
+static os_activity_stream_for_pid_t s_os_activity_stream_for_pid;
+static os_activity_stream_resume_t s_os_activity_stream_resume;
+static os_activity_stream_cancel_t s_os_activity_stream_cancel;
+static os_log_copy_formatted_message_t s_os_log_copy_formatted_message;
+static os_activity_stream_set_event_handler_t
+ s_os_activity_stream_set_event_handler;
+
+bool LookupSPICalls() {
+ static std::once_flag s_once_flag;
+ static bool s_has_spi;
+
+ std::call_once(s_once_flag, [] {
+ dlopen ("/System/Library/PrivateFrameworks/LoggingSupport.framework/LoggingSupport", RTLD_NOW);
+ s_os_activity_stream_for_pid = (os_activity_stream_for_pid_t)dlsym(
+ RTLD_DEFAULT, "os_activity_stream_for_pid");
+ s_os_activity_stream_resume = (os_activity_stream_resume_t)dlsym(
+ RTLD_DEFAULT, "os_activity_stream_resume");
+ s_os_activity_stream_cancel = (os_activity_stream_cancel_t)dlsym(
+ RTLD_DEFAULT, "os_activity_stream_cancel");
+ s_os_log_copy_formatted_message = (os_log_copy_formatted_message_t)dlsym(
+ RTLD_DEFAULT, "os_log_copy_formatted_message");
+ s_os_activity_stream_set_event_handler =
+ (os_activity_stream_set_event_handler_t)dlsym(
+ RTLD_DEFAULT, "os_activity_stream_set_event_handler");
+
+ // We'll indicate we're all set if every function entry point
+ // was found.
+ s_has_spi = (s_os_activity_stream_for_pid != nullptr) &&
+ (s_os_activity_stream_resume != nullptr) &&
+ (s_os_activity_stream_cancel != nullptr) &&
+ (s_os_log_copy_formatted_message != nullptr) &&
+ (s_os_activity_stream_set_event_handler != nullptr);
+ if (s_has_spi) {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "Found os_log SPI calls.");
+ // Tell LogMessageOsLog how to format messages when search
+ // criteria requires it.
+ LogMessageOsLog::SetFormatterFunction(s_os_log_copy_formatted_message);
+ } else {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "Failed to find os_log SPI "
+ "calls.");
+ }
+ });
+
+ return s_has_spi;
+}
+
+using Mutex = std::mutex;
+static Mutex s_collector_mutex;
+static std::vector<DarwinLogCollectorSP> s_collectors;
+
+static void TrackCollector(const DarwinLogCollectorSP &collector_sp) {
+ std::lock_guard<Mutex> locker(s_collector_mutex);
+ if (std::find(s_collectors.begin(), s_collectors.end(), collector_sp) !=
+ s_collectors.end()) {
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "attempted to add same collector multiple times");
+ return;
+ }
+ s_collectors.push_back(collector_sp);
+}
+
+static void StopTrackingCollector(const DarwinLogCollectorSP &collector_sp) {
+ std::lock_guard<Mutex> locker(s_collector_mutex);
+ s_collectors.erase(
+ std::remove(s_collectors.begin(), s_collectors.end(), collector_sp),
+ s_collectors.end());
+}
+
+static DarwinLogCollectorSP FindCollectorForProcess(pid_t pid) {
+ std::lock_guard<Mutex> locker(s_collector_mutex);
+ for (const auto &collector_sp : s_collectors) {
+ if (collector_sp && (collector_sp->GetProcessID() == pid))
+ return collector_sp;
+ }
+ return DarwinLogCollectorSP();
+}
+
+static FilterTarget TargetStringToEnum(const std::string &filter_target_name) {
+ if (filter_target_name == "activity")
+ return eFilterTargetActivity;
+ else if (filter_target_name == "activity-chain")
+ return eFilterTargetActivityChain;
+ else if (filter_target_name == "category")
+ return eFilterTargetCategory;
+ else if (filter_target_name == "message")
+ return eFilterTargetMessage;
+ else if (filter_target_name == "subsystem")
+ return eFilterTargetSubsystem;
+ else
+ return eFilterTargetInvalid;
+}
+
+class Configuration {
+public:
+ Configuration(const JSONObject &config)
+ : m_is_valid(false),
+ m_activity_stream_flags(OS_ACTIVITY_STREAM_PROCESS_ONLY),
+ m_filter_chain_sp(nullptr) {
+ // Parse out activity stream flags
+ if (!ParseSourceFlags(config)) {
+ m_is_valid = false;
+ return;
+ }
+
+ // Parse filter rules
+ if (!ParseFilterRules(config)) {
+ m_is_valid = false;
+ return;
+ }
+
+ // Everything worked.
+ m_is_valid = true;
+ }
+
+ bool ParseSourceFlags(const JSONObject &config) {
+ // Get the source-flags dictionary.
+ auto source_flags_sp = config.GetObject("source-flags");
+ if (!source_flags_sp)
+ return false;
+ if (!JSONObject::classof(source_flags_sp.get()))
+ return false;
+
+ const JSONObject &source_flags =
+ *static_cast<JSONObject *>(source_flags_sp.get());
+
+ // Parse out the flags.
+ bool include_any_process = false;
+ bool include_callstacks = false;
+ bool include_info_level = false;
+ bool include_debug_level = false;
+ bool live_stream = false;
+
+ if (!source_flags.GetObjectAsBool("any-process", include_any_process)) {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "Source-flag 'any-process' missing from "
+ "configuration.");
+ return false;
+ }
+ if (!source_flags.GetObjectAsBool("callstacks", include_callstacks)) {
+ // We currently suppress the availability of this on the lldb
+ // side. We include here for devices when we enable in the
+ // future.
+ // DNBLogThreadedIf(LOG_DARWIN_LOG,
+ // "Source-flag 'callstacks' missing from "
+ // "configuration.");
+
+ // OK. We just skip callstacks.
+ // return false;
+ }
+ if (!source_flags.GetObjectAsBool("info-level", include_info_level)) {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "Source-flag 'info-level' missing from "
+ "configuration.");
+ return false;
+ }
+ if (!source_flags.GetObjectAsBool("debug-level", include_debug_level)) {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "Source-flag 'debug-level' missing from "
+ "configuration.");
+ return false;
+ }
+ if (!source_flags.GetObjectAsBool("live-stream", live_stream)) {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "Source-flag 'live-stream' missing from "
+ "configuration.");
+ return false;
+ }
+
+ // Setup the SPI flags based on this.
+ m_activity_stream_flags = 0;
+ if (!include_any_process)
+ m_activity_stream_flags |= OS_ACTIVITY_STREAM_PROCESS_ONLY;
+ if (include_callstacks)
+ m_activity_stream_flags |= OS_ACTIVITY_STREAM_CALLSTACK;
+ if (include_info_level)
+ m_activity_stream_flags |= OS_ACTIVITY_STREAM_INFO;
+ if (include_debug_level)
+ m_activity_stream_flags |= OS_ACTIVITY_STREAM_DEBUG;
+ if (!live_stream)
+ m_activity_stream_flags |= OS_ACTIVITY_STREAM_BUFFERED;
+
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "m_activity_stream_flags = 0x%03x",
+ m_activity_stream_flags);
+
+ return true;
+ }
+
+ bool ParseFilterRules(const JSONObject &config) {
+ // Retrieve the default rule.
+ bool filter_default_accept = true;
+ if (!config.GetObjectAsBool("filter-fall-through-accepts",
+ filter_default_accept)) {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "Setting 'filter-fall-through-accepts' "
+ "missing from configuration.");
+ return false;
+ }
+ m_filter_chain_sp = std::make_shared<LogFilterChain>(filter_default_accept);
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "DarwinLog no-match rule: %s.",
+ filter_default_accept ? "accept" : "reject");
+
+ // If we don't have the filter-rules array, we're done.
+ auto filter_rules_sp = config.GetObject("filter-rules");
+ if (!filter_rules_sp) {
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "No 'filter-rules' config element, all log "
+ "entries will use the no-match action (%s).",
+ filter_default_accept ? "accept" : "reject");
+ return true;
+ }
+ if (!JSONArray::classof(filter_rules_sp.get()))
+ return false;
+ const JSONArray &rules_config =
+ *static_cast<JSONArray *>(filter_rules_sp.get());
+
+ // Create the filters.
+ for (auto &rule_sp : rules_config.m_elements) {
+ if (!JSONObject::classof(rule_sp.get()))
+ return false;
+ const JSONObject &rule_config = *static_cast<JSONObject *>(rule_sp.get());
+
+ // Get whether this filter accepts or rejects.
+ bool filter_accepts = true;
+ if (!rule_config.GetObjectAsBool("accept", filter_accepts)) {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "Filter 'accept' element missing.");
+ return false;
+ }
+
+ // Grab the target log field attribute for the match.
+ std::string target_attribute;
+ if (!rule_config.GetObjectAsString("attribute", target_attribute)) {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "Filter 'attribute' element missing.");
+ return false;
+ }
+ auto target_enum = TargetStringToEnum(target_attribute);
+ if (target_enum == eFilterTargetInvalid) {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "Filter attribute '%s' unsupported.",
+ target_attribute.c_str());
+ return false;
+ }
+
+ // Handle operation-specific fields and filter creation.
+ std::string filter_type;
+ if (!rule_config.GetObjectAsString("type", filter_type)) {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "Filter 'type' element missing.");
+ return false;
+ }
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "Reading filter of type '%s'",
+ filter_type.c_str());
+
+ LogFilterSP filter_sp;
+ if (filter_type == "regex") {
+ // Grab the regex for the match.
+ std::string regex;
+ if (!rule_config.GetObjectAsString("regex", regex)) {
+ DNBLogError("Regex filter missing 'regex' element.");
+ return false;
+ }
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "regex for filter: \"%s\"",
+ regex.c_str());
+
+ // Create the regex filter.
+ auto regex_filter =
+ new LogFilterRegex(filter_accepts, target_enum, regex);
+ filter_sp.reset(regex_filter);
+
+ // Validate that the filter is okay.
+ if (!regex_filter->IsValid()) {
+ DNBLogError("Invalid regex in filter: "
+ "regex=\"%s\", error=%s",
+ regex.c_str(), regex_filter->GetErrorAsCString());
+ return false;
+ }
+ } else if (filter_type == "match") {
+ // Grab the regex for the match.
+ std::string exact_text;
+ if (!rule_config.GetObjectAsString("exact_text", exact_text)) {
+ DNBLogError("Exact match filter missing "
+ "'exact_text' element.");
+ return false;
+ }
+
+ // Create the filter.
+ filter_sp = std::make_shared<LogFilterExactMatch>(
+ filter_accepts, target_enum, exact_text);
+ }
+
+ // Add the filter to the chain.
+ m_filter_chain_sp->AppendFilter(filter_sp);
+ }
+ return true;
+ }
+
+ bool IsValid() const { return m_is_valid; }
+
+ os_activity_stream_flag_t GetActivityStreamFlags() const {
+ return m_activity_stream_flags;
+ }
+
+ const LogFilterChainSP &GetLogFilterChain() const {
+ return m_filter_chain_sp;
+ }
+
+private:
+ bool m_is_valid;
+ os_activity_stream_flag_t m_activity_stream_flags;
+ LogFilterChainSP m_filter_chain_sp;
+};
+}
+
+bool DarwinLogCollector::IsSupported() {
+ // We're supported if we have successfully looked up the SPI entry points.
+ return LookupSPICalls();
+}
+
+bool DarwinLogCollector::StartCollectingForProcess(nub_process_t pid,
+ const JSONObject &config) {
+ // If we're currently collecting for this process, kill the existing
+ // collector.
+ if (CancelStreamForProcess(pid)) {
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "%s() killed existing DarwinLog collector for pid %d.",
+ __FUNCTION__, pid);
+ }
+
+ // If the process isn't alive, we're done.
+ if (!DNBProcessIsAlive(pid)) {
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "%s() cannot collect for pid %d: process not alive.",
+ __FUNCTION__, pid);
+ return false;
+ }
+
+ // Validate the configuration.
+ auto spi_config = Configuration(config);
+ if (!spi_config.IsValid()) {
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "%s() invalid configuration, will not enable log "
+ "collection",
+ __FUNCTION__);
+ return false;
+ }
+
+ // Create the stream collector that will manage collected data
+ // for this pid.
+ DarwinLogCollectorSP collector_sp(
+ new DarwinLogCollector(pid, spi_config.GetLogFilterChain()));
+ std::weak_ptr<DarwinLogCollector> collector_wp(collector_sp);
+
+ // Setup the stream handling block.
+ os_activity_stream_block_t block =
+ ^bool(os_activity_stream_entry_t entry, int error) {
+ // Check if our collector is still alive.
+ DarwinLogCollectorSP inner_collector_sp = collector_wp.lock();
+ if (!inner_collector_sp)
+ return false;
+ return inner_collector_sp->HandleStreamEntry(entry, error);
+ };
+
+ os_activity_stream_event_block_t stream_event_block = ^void(
+ os_activity_stream_t stream, os_activity_stream_event_t event) {
+ switch (event) {
+ case OS_ACTIVITY_STREAM_EVENT_STARTED:
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "received stream event: "
+ "OS_ACTIVITY_STREAM_EVENT_STARTED, stream %p.",
+ (void *)stream);
+ break;
+ case OS_ACTIVITY_STREAM_EVENT_STOPPED:
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "received stream event: "
+ "OS_ACTIVITY_STREAM_EVENT_STOPPED, stream %p.",
+ (void *)stream);
+ break;
+ case OS_ACTIVITY_STREAM_EVENT_FAILED:
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "received stream event: "
+ "OS_ACTIVITY_STREAM_EVENT_FAILED, stream %p.",
+ (void *)stream);
+ break;
+ case OS_ACTIVITY_STREAM_EVENT_CHUNK_STARTED:
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "received stream event: "
+ "OS_ACTIVITY_STREAM_EVENT_CHUNK_STARTED, stream %p.",
+ (void *)stream);
+ break;
+ case OS_ACTIVITY_STREAM_EVENT_CHUNK_FINISHED:
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "received stream event: "
+ "OS_ACTIVITY_STREAM_EVENT_CHUNK_FINISHED, stream %p.",
+ (void *)stream);
+ break;
+ }
+ };
+
+ // Create the stream.
+ os_activity_stream_t activity_stream = (*s_os_activity_stream_for_pid)(
+ pid, spi_config.GetActivityStreamFlags(), block);
+ collector_sp->SetActivityStream(activity_stream);
+
+ // Specify the stream-related event handler.
+ (*s_os_activity_stream_set_event_handler)(activity_stream,
+ stream_event_block);
+
+ // Start the stream.
+ (*s_os_activity_stream_resume)(activity_stream);
+
+ TrackCollector(collector_sp);
+ return true;
+}
+
+DarwinLogEventVector
+DarwinLogCollector::GetEventsForProcess(nub_process_t pid) {
+ auto collector_sp = FindCollectorForProcess(pid);
+ if (!collector_sp) {
+ // We're not tracking a stream for this process.
+ return DarwinLogEventVector();
+ }
+
+ return collector_sp->RemoveEvents();
+}
+
+bool DarwinLogCollector::CancelStreamForProcess(nub_process_t pid) {
+ auto collector_sp = FindCollectorForProcess(pid);
+ if (!collector_sp) {
+ // We're not tracking a stream for this process.
+ return false;
+ }
+
+ collector_sp->CancelActivityStream();
+ StopTrackingCollector(collector_sp);
+
+ return true;
+}
+
+const char *
+DarwinLogCollector::GetActivityForID(os_activity_id_t activity_id) const {
+ auto find_it = m_activity_map.find(activity_id);
+ return (find_it != m_activity_map.end()) ? find_it->second.m_name.c_str()
+ : nullptr;
+}
+
+/// Retrieve the full parent-child chain for activity names. These
+/// can be arbitrarily deep. This method assumes the caller has already
+/// locked the activity mutex.
+void DarwinLogCollector::GetActivityChainForID_internal(
+ os_activity_id_t activity_id, std::string &result, size_t depth) const {
+ if (depth > MAX_ACTIVITY_CHAIN_DEPTH) {
+ // Terminating condition - too deeply nested.
+ return;
+ } else if (activity_id == 0) {
+ // Terminating condition - no activity.
+ return;
+ }
+
+ auto find_it = m_activity_map.find(activity_id);
+ if (find_it == m_activity_map.end()) {
+ // Terminating condition - no data for activity_id.
+ return;
+ }
+
+ // Activity name becomes parent activity name chain + ':' + our activity
+ // name.
+ GetActivityChainForID_internal(find_it->second.m_parent_id, result,
+ depth + 1);
+ if (!result.empty())
+ result += ':';
+ result += find_it->second.m_name;
+}
+
+std::string
+DarwinLogCollector::GetActivityChainForID(os_activity_id_t activity_id) const {
+ std::string result;
+ {
+ std::lock_guard<std::mutex> locker(m_activity_info_mutex);
+ GetActivityChainForID_internal(activity_id, result, 1);
+ }
+ return result;
+}
+
+DarwinLogCollector::DarwinLogCollector(nub_process_t pid,
+ const LogFilterChainSP &filter_chain_sp)
+ : ActivityStore(), m_pid(pid), m_activity_stream(0), m_events(),
+ m_events_mutex(), m_filter_chain_sp(filter_chain_sp),
+ m_activity_info_mutex(), m_activity_map() {}
+
+DarwinLogCollector::~DarwinLogCollector() {
+ // Cancel the stream.
+ if (m_activity_stream) {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "tearing down activity stream "
+ "collector for %d",
+ m_pid);
+ (*s_os_activity_stream_cancel)(m_activity_stream);
+ m_activity_stream = 0;
+ } else {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "no stream to tear down for %d", m_pid);
+ }
+}
+
+void DarwinLogCollector::SignalDataAvailable() {
+ RNBRemoteSP remoteSP(g_remoteSP);
+ if (!remoteSP) {
+ // We're done. This is unexpected.
+ StopTrackingCollector(shared_from_this());
+ return;
+ }
+
+ RNBContext &ctx = remoteSP->Context();
+ ctx.Events().SetEvents(RNBContext::event_darwin_log_data_available);
+ // Wait for the main thread to consume this notification if it requested
+ // we wait for it.
+ ctx.Events().WaitForResetAck(RNBContext::event_darwin_log_data_available);
+}
+
+void DarwinLogCollector::SetActivityStream(
+ os_activity_stream_t activity_stream) {
+ m_activity_stream = activity_stream;
+}
+
+bool DarwinLogCollector::HandleStreamEntry(os_activity_stream_entry_t entry,
+ int error) {
+ if ((error == 0) && (entry != nullptr)) {
+ if (entry->pid != m_pid) {
+ // For now, skip messages not originating from our process.
+ // Later we might want to keep all messages related to an event
+ // that we're tracking, even when it came from another process,
+ // possibly doing work on our behalf.
+ return true;
+ }
+
+ switch (entry->type) {
+ case OS_ACTIVITY_STREAM_TYPE_ACTIVITY_CREATE:
+ DNBLogThreadedIf(
+ LOG_DARWIN_LOG, "received activity create: "
+ "%s, creator aid %" PRIu64 ", unique_pid %" PRIu64
+ "(activity id=%" PRIu64 ", parent id=%" PRIu64 ")",
+ entry->activity_create.name, entry->activity_create.creator_aid,
+ entry->activity_create.unique_pid, entry->activity_id,
+ entry->parent_id);
+ {
+ std::lock_guard<std::mutex> locker(m_activity_info_mutex);
+ m_activity_map.insert(
+ std::make_pair(entry->activity_id,
+ ActivityInfo(entry->activity_create.name,
+ entry->activity_id, entry->parent_id)));
+ }
+ break;
+
+ case OS_ACTIVITY_STREAM_TYPE_ACTIVITY_TRANSITION:
+ DNBLogThreadedIf(
+ LOG_DARWIN_LOG, "received activity transition:"
+ "new aid: %" PRIu64 "(activity id=%" PRIu64
+ ", parent id=%" PRIu64 ", tid %" PRIu64 ")",
+ entry->activity_transition.transition_id, entry->activity_id,
+ entry->parent_id, entry->activity_transition.thread);
+ break;
+
+ case OS_ACTIVITY_STREAM_TYPE_LOG_MESSAGE: {
+ DNBLogThreadedIf(
+ LOG_DARWIN_LOG, "received log message: "
+ "(activity id=%" PRIu64 ", parent id=%" PRIu64 ", "
+ "tid %" PRIu64 "): format %s",
+ entry->activity_id, entry->parent_id, entry->log_message.thread,
+ entry->log_message.format ? entry->log_message.format
+ : "<invalid-format>");
+
+ // Do the real work here.
+ {
+ // Ensure our process is still alive. If not, we can
+ // cancel the collection.
+ if (!DNBProcessIsAlive(m_pid)) {
+ // We're outta here. This is the manner in which we
+ // stop collecting for a process.
+ StopTrackingCollector(shared_from_this());
+ return false;
+ }
+
+ LogMessageOsLog os_log_message(*this, *entry);
+ if (!m_filter_chain_sp ||
+ !m_filter_chain_sp->GetAcceptMessage(os_log_message)) {
+ // This log message was rejected by the filter,
+ // so stop processing it now.
+ return true;
+ }
+
+ // Copy over the relevant bits from the message.
+ const struct os_log_message_s &log_message = entry->log_message;
+
+ DarwinLogEventSP message_sp(new DarwinLogEvent());
+ // Indicate this event is a log message event.
+ message_sp->AddStringItem("type", "log");
+
+ // Add the message contents (fully expanded).
+ // Consider expanding on the remote side.
+ // Then we don't pay for expansion until when it is
+ // used.
+ const char *message_text = os_log_message.GetMessage();
+ if (message_text)
+ message_sp->AddStringItem("message", message_text);
+
+ // Add some useful data fields.
+ message_sp->AddIntegerItem("timestamp", log_message.timestamp);
+
+ // Do we want to do all activity name resolution on this
+ // side? Maybe. For now, send IDs and ID->name mappings
+ // and fix this up on that side. Later, when we add
+ // debugserver-side filtering, we'll want to get the
+ // activity names over here, so we should probably
+ // just send them as resolved strings.
+ message_sp->AddIntegerItem("activity_id", entry->activity_id);
+ message_sp->AddIntegerItem("parent_id", entry->parent_id);
+ message_sp->AddIntegerItem("thread_id", log_message.thread);
+ if (log_message.subsystem && strlen(log_message.subsystem) > 0)
+ message_sp->AddStringItem("subsystem", log_message.subsystem);
+ if (log_message.category && strlen(log_message.category) > 0)
+ message_sp->AddStringItem("category", log_message.category);
+ if (entry->activity_id != 0) {
+ std::string activity_chain =
+ GetActivityChainForID(entry->activity_id);
+ if (!activity_chain.empty())
+ message_sp->AddStringItem("activity-chain", activity_chain);
+ }
+
+ // Add it to the list for later collection.
+ {
+ std::lock_guard<std::mutex> locker(m_events_mutex);
+ m_events.push_back(message_sp);
+ }
+ SignalDataAvailable();
+ }
+ break;
+ }
+ }
+ } else {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "HandleStreamEntry: final call, "
+ "error %d",
+ error);
+ }
+ return true;
+}
+
+DarwinLogEventVector DarwinLogCollector::RemoveEvents() {
+ DarwinLogEventVector returned_events;
+ {
+ std::lock_guard<std::mutex> locker(m_events_mutex);
+ returned_events.swap(m_events);
+ }
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "DarwinLogCollector::%s(): removing %lu "
+ "queued log entries",
+ __FUNCTION__, returned_events.size());
+ return returned_events;
+}
+
+void DarwinLogCollector::CancelActivityStream() {
+ if (!m_activity_stream)
+ return;
+
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "DarwinLogCollector::%s(): canceling "
+ "activity stream %p",
+ __FUNCTION__, static_cast<void *>(m_activity_stream));
+ (*s_os_activity_stream_cancel)(m_activity_stream);
+ m_activity_stream = nullptr;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogCollector.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogCollector.h
new file mode 100644
index 00000000000..24ab4230e38
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogCollector.h
@@ -0,0 +1,107 @@
+//===-- DarwinLogCollector.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef DarwinLogCollector_h
+#define DarwinLogCollector_h
+
+#include <sys/types.h>
+
+#include <memory>
+#include <mutex>
+#include <unordered_map>
+
+#include "ActivityStore.h"
+#include "ActivityStreamSPI.h"
+#include "DNBDefs.h"
+#include "DarwinLogEvent.h"
+#include "DarwinLogInterfaces.h"
+#include "JSON.h"
+
+class DarwinLogCollector;
+typedef std::shared_ptr<DarwinLogCollector> DarwinLogCollectorSP;
+
+class DarwinLogCollector
+ : public std::enable_shared_from_this<DarwinLogCollector>,
+ public ActivityStore {
+public:
+ /// Return whether the os_log and activity tracing SPI is available.
+ ///
+ /// \return \b true if the activity stream support is available,
+ /// \b false otherwise.
+ static bool IsSupported();
+
+ /// Return a log function suitable for DNBLog to use as the internal
+ /// logging function.
+ ///
+ /// \return a DNBLog-style logging function if IsSupported() returns
+ /// true; otherwise, returns nullptr.
+ static DNBCallbackLog GetLogFunction();
+
+ static bool StartCollectingForProcess(nub_process_t pid,
+ const JSONObject &config);
+
+ static bool CancelStreamForProcess(nub_process_t pid);
+
+ static DarwinLogEventVector GetEventsForProcess(nub_process_t pid);
+
+ ~DarwinLogCollector();
+
+ pid_t GetProcessID() const { return m_pid; }
+
+ // ActivityStore API
+ const char *GetActivityForID(os_activity_id_t activity_id) const override;
+
+ std::string
+ GetActivityChainForID(os_activity_id_t activity_id) const override;
+
+private:
+ DarwinLogCollector() = delete;
+ DarwinLogCollector(const DarwinLogCollector &) = delete;
+ DarwinLogCollector &operator=(const DarwinLogCollector &) = delete;
+
+ explicit DarwinLogCollector(nub_process_t pid,
+ const LogFilterChainSP &filter_chain_sp);
+
+ void SignalDataAvailable();
+
+ void SetActivityStream(os_activity_stream_t activity_stream);
+
+ bool HandleStreamEntry(os_activity_stream_entry_t entry, int error);
+
+ DarwinLogEventVector RemoveEvents();
+
+ void CancelActivityStream();
+
+ void GetActivityChainForID_internal(os_activity_id_t activity_id,
+ std::string &result, size_t depth) const;
+
+ struct ActivityInfo {
+ ActivityInfo(const char *name, os_activity_id_t activity_id,
+ os_activity_id_t parent_activity_id)
+ : m_name(name), m_id(activity_id), m_parent_id(parent_activity_id) {}
+
+ const std::string m_name;
+ const os_activity_id_t m_id;
+ const os_activity_id_t m_parent_id;
+ };
+
+ using ActivityMap = std::unordered_map<os_activity_id_t, ActivityInfo>;
+
+ const nub_process_t m_pid;
+ os_activity_stream_t m_activity_stream;
+ DarwinLogEventVector m_events;
+ std::mutex m_events_mutex;
+ LogFilterChainSP m_filter_chain_sp;
+
+ /// Mutex to protect activity info (activity name and parent structures)
+ mutable std::mutex m_activity_info_mutex;
+ /// Map of activity id to ActivityInfo
+ ActivityMap m_activity_map;
+};
+
+#endif /* LogStreamCollector_h */
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogEvent.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogEvent.h
new file mode 100644
index 00000000000..fb142146bf8
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogEvent.h
@@ -0,0 +1,26 @@
+//===-- DarwinLogEvent.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef DarwinLogEvent_h
+#define DarwinLogEvent_h
+
+#include <memory>
+#include <vector>
+
+#include "JSONGenerator.h"
+
+// =============================================================================
+/// Each discrete unit of information is described as an event, such as
+/// the emission of a single log message.
+// =============================================================================
+
+using DarwinLogEvent = JSONGenerator::Dictionary;
+using DarwinLogEventSP = std::shared_ptr<DarwinLogEvent>;
+using DarwinLogEventVector = std::vector<DarwinLogEventSP>;
+
+#endif
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogInterfaces.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogInterfaces.h
new file mode 100644
index 00000000000..2bfcb50068b
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogInterfaces.h
@@ -0,0 +1,24 @@
+//===-- DarwinLogInterfaces.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef DarwinLogInterfaces_h
+#define DarwinLogInterfaces_h
+
+#include <memory>
+
+class ActivityStore;
+
+class LogFilter;
+using LogFilterSP = std::shared_ptr<LogFilter>;
+
+class LogFilterChain;
+using LogFilterChainSP = std::shared_ptr<LogFilterChain>;
+
+class LogMessage;
+
+#endif /* DarwinLogInterfaces_h */
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogTypes.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogTypes.h
new file mode 100644
index 00000000000..e9d84991a4a
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/DarwinLogTypes.h
@@ -0,0 +1,21 @@
+//===-- DarwinLogTypes.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef DarwinLogTypes_h
+#define DarwinLogTypes_h
+
+enum FilterTarget {
+ eFilterTargetInvalid,
+ eFilterTargetActivity,
+ eFilterTargetActivityChain,
+ eFilterTargetCategory,
+ eFilterTargetMessage,
+ eFilterTargetSubsystem
+};
+
+#endif /* DarwinLogTypes_h */
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilter.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilter.cpp
new file mode 100644
index 00000000000..c533c59fa9e
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilter.cpp
@@ -0,0 +1,11 @@
+//===-- LogFilter.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LogFilter.h"
+
+LogFilter::~LogFilter() {}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilter.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilter.h
new file mode 100644
index 00000000000..76ab60f1d9f
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilter.h
@@ -0,0 +1,29 @@
+//===-- LogFilter.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LogFilter_h
+#define LogFilter_h
+
+#include "DarwinLogInterfaces.h"
+
+class LogFilter {
+public:
+ virtual ~LogFilter();
+
+ virtual bool DoesMatch(const LogMessage &message) const = 0;
+
+ bool MatchesAreAccepted() const { return m_matches_accept; }
+
+protected:
+ LogFilter(bool matches_accept) : m_matches_accept(matches_accept) {}
+
+private:
+ bool m_matches_accept;
+};
+
+#endif /* LogFilter_h */
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterChain.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterChain.cpp
new file mode 100644
index 00000000000..2d8c655fcf0
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterChain.cpp
@@ -0,0 +1,41 @@
+//===-- LogFilterChain.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LogFilterChain.h"
+
+#include "LogFilter.h"
+
+LogFilterChain::LogFilterChain(bool default_accept)
+ : m_filters(), m_default_accept(default_accept) {}
+
+void LogFilterChain::AppendFilter(const LogFilterSP &filter_sp) {
+ if (filter_sp)
+ m_filters.push_back(filter_sp);
+}
+
+void LogFilterChain::ClearFilterChain() { m_filters.clear(); }
+
+bool LogFilterChain::GetDefaultAccepts() const { return m_default_accept; }
+
+void LogFilterChain::SetDefaultAccepts(bool default_accept) {
+ m_default_accept = default_accept;
+}
+
+bool LogFilterChain::GetAcceptMessage(const LogMessage &message) const {
+ for (auto filter_sp : m_filters) {
+ if (filter_sp->DoesMatch(message)) {
+ // This message matches this filter. If the filter accepts matches,
+ // this message matches; otherwise, it rejects matches.
+ return filter_sp->MatchesAreAccepted();
+ }
+ }
+
+ // None of the filters matched. Therefore, we do whatever the
+ // default fall-through rule says.
+ return m_default_accept;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterChain.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterChain.h
new file mode 100644
index 00000000000..f231c308232
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterChain.h
@@ -0,0 +1,37 @@
+//===-- LogFilterChain.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LogFilterChain_h
+#define LogFilterChain_h
+
+#include <vector>
+
+#include "DarwinLogInterfaces.h"
+
+class LogFilterChain {
+public:
+ LogFilterChain(bool default_accept);
+
+ void AppendFilter(const LogFilterSP &filter_sp);
+
+ void ClearFilterChain();
+
+ bool GetDefaultAccepts() const;
+
+ void SetDefaultAccepts(bool default_accepts);
+
+ bool GetAcceptMessage(const LogMessage &message) const;
+
+private:
+ using FilterVector = std::vector<LogFilterSP>;
+
+ FilterVector m_filters;
+ bool m_default_accept;
+};
+
+#endif /* LogFilterChain_hpp */
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterExactMatch.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterExactMatch.cpp
new file mode 100644
index 00000000000..bbf911f28cd
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterExactMatch.cpp
@@ -0,0 +1,48 @@
+//===-- LogFilterExactMatch.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LogFilterExactMatch.h"
+#include "LogMessage.h"
+
+LogFilterExactMatch::LogFilterExactMatch(bool match_accepts,
+ FilterTarget filter_target,
+ const std::string &match_text)
+ : LogFilter(match_accepts), m_filter_target(filter_target),
+ m_match_text(match_text) {}
+
+bool LogFilterExactMatch::DoesMatch(const LogMessage &message) const {
+ switch (m_filter_target) {
+ case eFilterTargetActivity:
+ // Empty fields never match a condition.
+ if (!message.HasActivity())
+ return false;
+ return m_match_text == message.GetActivity();
+ case eFilterTargetActivityChain:
+ // Empty fields never match a condition.
+ if (!message.HasActivity())
+ return false;
+ return m_match_text == message.GetActivityChain();
+ case eFilterTargetCategory:
+ // Empty fields never match a condition.
+ if (!message.HasCategory())
+ return false;
+ return m_match_text == message.GetCategory();
+ case eFilterTargetMessage: {
+ const char *message_text = message.GetMessage();
+ return (message_text != nullptr) && (m_match_text == message_text);
+ }
+ case eFilterTargetSubsystem:
+ // Empty fields never match a condition.
+ if (!message.HasSubsystem())
+ return false;
+ return m_match_text == message.GetSubsystem();
+ default:
+ // We don't know this type.
+ return false;
+ }
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterExactMatch.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterExactMatch.h
new file mode 100644
index 00000000000..5c0b9c22356
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterExactMatch.h
@@ -0,0 +1,30 @@
+//===-- LogFilterExactMatch.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LogFilterExactMatch_h
+#define LogFilterExactMatch_h
+
+#include <string>
+
+#include "DarwinLogInterfaces.h"
+#include "DarwinLogTypes.h"
+#include "LogFilter.h"
+
+class LogFilterExactMatch : public LogFilter {
+public:
+ LogFilterExactMatch(bool match_accepts, FilterTarget filter_target,
+ const std::string &match_text);
+
+ bool DoesMatch(const LogMessage &message) const override;
+
+private:
+ const FilterTarget m_filter_target;
+ const std::string m_match_text;
+};
+
+#endif
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterRegex.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterRegex.cpp
new file mode 100644
index 00000000000..489c056fa81
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterRegex.cpp
@@ -0,0 +1,94 @@
+//===-- LogFilterRegex.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LogFilterRegex.h"
+
+#include "DNBLog.h"
+#include "LogMessage.h"
+
+// Enable enhanced mode if it is available. This allows for things like
+// \d for digit, \s for space, and many more, but it isn't available
+// everywhere.
+#if defined(REG_ENHANCED)
+#define DEFAULT_COMPILE_FLAGS (REG_ENHANCED | REG_EXTENDED)
+#else
+#define DEFAULT_COMPILE_FLAGS (REG_EXTENDED)
+#endif
+
+LogFilterRegex::LogFilterRegex(bool match_accepts, FilterTarget filter_target,
+ const std::string &regex)
+ : LogFilter(match_accepts), m_filter_target(filter_target),
+ m_regex_text(regex), m_regex(), m_is_valid(false), m_error_text() {
+ // Clear it.
+ memset(&m_regex, 0, sizeof(m_regex));
+
+ // Compile it.
+ if (!regex.empty()) {
+ auto comp_err = ::regcomp(&m_regex, regex.c_str(), DEFAULT_COMPILE_FLAGS);
+ m_is_valid = (comp_err == 0);
+ if (!m_is_valid) {
+ char buffer[256];
+ buffer[0] = '\0';
+ ::regerror(comp_err, &m_regex, buffer, sizeof(buffer));
+ m_error_text = buffer;
+ }
+ }
+}
+
+LogFilterRegex::~LogFilterRegex() {
+ if (m_is_valid) {
+ // Free the regex internals.
+ regfree(&m_regex);
+ }
+}
+
+bool LogFilterRegex::DoesMatch(const LogMessage &message) const {
+ switch (m_filter_target) {
+ case eFilterTargetActivity:
+ // Empty fields never match a condition.
+ if (!message.HasActivity())
+ return false;
+ return ::regexec(&m_regex, message.GetActivity(), 0, nullptr, 0) == 0;
+ case eFilterTargetActivityChain:
+ // Empty fields never match a condition.
+ if (!message.HasActivity())
+ return false;
+ return ::regexec(&m_regex, message.GetActivityChain().c_str(), 0, nullptr,
+ 0) == 0;
+ case eFilterTargetCategory:
+ // Empty fields never match a condition.
+ if (!message.HasCategory())
+ return false;
+ return ::regexec(&m_regex, message.GetCategory(), 0, nullptr, 0) == 0;
+ case eFilterTargetMessage: {
+ const char *message_text = message.GetMessage();
+ if (!message_text) {
+ DNBLogThreadedIf(LOG_DARWIN_LOG,
+ "LogFilterRegex: regex "
+ "\"%s\" no match due to nullptr message.",
+ m_regex_text.c_str());
+ return false;
+ }
+
+ bool match = ::regexec(&m_regex, message_text, 0, nullptr, 0) == 0;
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "LogFilterRegex: regex "
+ "\"%s\" %s message \"%s\".",
+ m_regex_text.c_str(), match ? "matches" : "does not match",
+ message_text);
+ return match;
+ }
+ case eFilterTargetSubsystem:
+ // Empty fields never match a condition.
+ if (!message.HasSubsystem())
+ return false;
+ return ::regexec(&m_regex, message.GetSubsystem(), 0, nullptr, 0) == 0;
+ default:
+ // We don't know this type.
+ return false;
+ }
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterRegex.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterRegex.h
new file mode 100644
index 00000000000..5bcc366f077
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogFilterRegex.h
@@ -0,0 +1,43 @@
+//===-- LogFilterRegex.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LogFilterRegex_h
+#define LogFilterRegex_h
+
+// C includes
+#include <regex.h>
+
+// C++ includes
+#include <string>
+
+#include "DarwinLogInterfaces.h"
+#include "DarwinLogTypes.h"
+#include "LogFilter.h"
+
+class LogFilterRegex : public LogFilter {
+public:
+ LogFilterRegex(bool match_accepts, FilterTarget filter_target,
+ const std::string &regex);
+
+ virtual ~LogFilterRegex();
+
+ bool IsValid() const { return m_is_valid; }
+
+ const char *GetErrorAsCString() const { return m_error_text.c_str(); }
+
+ bool DoesMatch(const LogMessage &message) const override;
+
+private:
+ const FilterTarget m_filter_target;
+ const std::string m_regex_text;
+ regex_t m_regex;
+ bool m_is_valid;
+ std::string m_error_text;
+};
+
+#endif /* LogFilterSubsystemRegex_hpp */
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessage.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessage.cpp
new file mode 100644
index 00000000000..6e5e26ddf89
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessage.cpp
@@ -0,0 +1,13 @@
+//===-- LogMessage.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LogMessage.h"
+
+LogMessage::LogMessage() {}
+
+LogMessage::~LogMessage() {}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessage.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessage.h
new file mode 100644
index 00000000000..1f5808c2cd1
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessage.h
@@ -0,0 +1,39 @@
+//===-- LogMessage.h --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LogMessage_h
+#define LogMessage_h
+
+#include <string>
+
+class LogMessage {
+public:
+ virtual ~LogMessage();
+
+ virtual bool HasActivity() const = 0;
+
+ virtual const char *GetActivity() const = 0;
+
+ virtual std::string GetActivityChain() const = 0;
+
+ virtual bool HasCategory() const = 0;
+
+ virtual const char *GetCategory() const = 0;
+
+ virtual bool HasSubsystem() const = 0;
+
+ virtual const char *GetSubsystem() const = 0;
+
+ // This can be expensive, so once we ask for it, we'll cache the result.
+ virtual const char *GetMessage() const = 0;
+
+protected:
+ LogMessage();
+};
+
+#endif /* LogMessage_h */
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessageOsLog.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessageOsLog.cpp
new file mode 100644
index 00000000000..b5855a9ff5e
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessageOsLog.cpp
@@ -0,0 +1,67 @@
+//===-- LogMessageOsLog.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LogMessageOsLog.h"
+
+#include "ActivityStore.h"
+#include "ActivityStreamSPI.h"
+
+namespace {
+static os_log_copy_formatted_message_t s_log_copy_formatted_message;
+}
+
+void LogMessageOsLog::SetFormatterFunction(
+ os_log_copy_formatted_message_t format_func) {
+ s_log_copy_formatted_message = format_func;
+}
+
+LogMessageOsLog::LogMessageOsLog(const ActivityStore &activity_store,
+ ActivityStreamEntry &entry)
+ : LogMessage(), m_activity_store(activity_store), m_entry(entry),
+ m_message() {}
+
+bool LogMessageOsLog::HasActivity() const { return m_entry.activity_id != 0; }
+
+const char *LogMessageOsLog::GetActivity() const {
+ return m_activity_store.GetActivityForID(m_entry.activity_id);
+}
+
+std::string LogMessageOsLog::GetActivityChain() const {
+ return m_activity_store.GetActivityChainForID(m_entry.activity_id);
+}
+
+bool LogMessageOsLog::HasCategory() const {
+ return m_entry.log_message.category && (m_entry.log_message.category[0] != 0);
+}
+
+const char *LogMessageOsLog::GetCategory() const {
+ return m_entry.log_message.category;
+}
+
+bool LogMessageOsLog::HasSubsystem() const {
+ return m_entry.log_message.subsystem &&
+ (m_entry.log_message.subsystem[0] != 0);
+}
+
+const char *LogMessageOsLog::GetSubsystem() const {
+ return m_entry.log_message.subsystem;
+}
+
+const char *LogMessageOsLog::GetMessage() const {
+ if (m_message.empty()) {
+ std::unique_ptr<char[]> formatted_message(
+ s_log_copy_formatted_message(&m_entry.log_message));
+ if (formatted_message)
+ m_message = formatted_message.get();
+ // else
+ // TODO log
+ }
+
+ // This is safe to return as we're not modifying it once we've formatted it.
+ return m_message.c_str();
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessageOsLog.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessageOsLog.h
new file mode 100644
index 00000000000..39b6adccb1b
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/DarwinLog/LogMessageOsLog.h
@@ -0,0 +1,56 @@
+//===-- LogMessageOsLog.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LogMessageOsLog_h
+#define LogMessageOsLog_h
+
+#include "DarwinLogInterfaces.h"
+
+#include "ActivityStreamSPI.h"
+#include "LogMessage.h"
+
+using ActivityStreamEntry = struct os_activity_stream_entry_s;
+
+/// Provides a unified wrapper around os_log()-style log messages.
+///
+/// The lifetime of this class is intended to be very short. The caller
+/// must ensure that the passed in ActivityStore and ActivityStreamEntry
+/// outlive this LogMessageOsLog entry.
+
+class LogMessageOsLog : public LogMessage {
+public:
+ static void SetFormatterFunction(os_log_copy_formatted_message_t format_func);
+
+ LogMessageOsLog(const ActivityStore &activity_store,
+ ActivityStreamEntry &entry);
+
+ // API methods
+
+ bool HasActivity() const override;
+
+ const char *GetActivity() const override;
+
+ std::string GetActivityChain() const override;
+
+ bool HasCategory() const override;
+
+ const char *GetCategory() const override;
+
+ bool HasSubsystem() const override;
+
+ const char *GetSubsystem() const override;
+
+ const char *GetMessage() const override;
+
+private:
+ const ActivityStore &m_activity_store;
+ ActivityStreamEntry &m_entry;
+ mutable std::string m_message;
+};
+
+#endif /* LogMessageOsLog_h */
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/Genealogy.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/Genealogy.cpp
new file mode 100644
index 00000000000..72d923309a6
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/Genealogy.cpp
@@ -0,0 +1,315 @@
+//===-- Genealogy.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <Availability.h>
+#include <dlfcn.h>
+#include <string>
+#include <uuid/uuid.h>
+
+#include "DNBDefs.h"
+#include "Genealogy.h"
+#include "GenealogySPI.h"
+#include "MachThreadList.h"
+
+/// Constructor
+
+Genealogy::Genealogy()
+ : m_os_activity_diagnostic_for_pid(nullptr),
+ m_os_activity_iterate_processes(nullptr),
+ m_os_activity_iterate_breadcrumbs(nullptr),
+ m_os_activity_iterate_messages(nullptr),
+ m_os_activity_iterate_activities(nullptr), m_os_trace_get_type(nullptr),
+ m_os_trace_copy_formatted_message(nullptr),
+ m_os_activity_for_thread(nullptr), m_os_activity_for_task_thread(nullptr),
+ m_thread_activities(), m_process_executable_infos(),
+ m_diagnosticd_call_timed_out(false) {
+ m_os_activity_diagnostic_for_pid =
+ (bool (*)(pid_t, os_activity_t, uint32_t, os_diagnostic_block_t))dlsym(
+ RTLD_DEFAULT, "os_activity_diagnostic_for_pid");
+ m_os_activity_iterate_processes =
+ (void (*)(os_activity_process_list_t, bool (^)(os_activity_process_t)))
+ dlsym(RTLD_DEFAULT, "os_activity_iterate_processes");
+ m_os_activity_iterate_breadcrumbs =
+ (void (*)(os_activity_process_t, bool (^)(os_activity_breadcrumb_t)))
+ dlsym(RTLD_DEFAULT, "os_activity_iterate_breadcrumbs");
+ m_os_activity_iterate_messages = (void (*)(
+ os_trace_message_list_t, os_activity_process_t,
+ bool (^)(os_trace_message_t)))dlsym(RTLD_DEFAULT,
+ "os_activity_iterate_messages");
+ m_os_activity_iterate_activities = (void (*)(
+ os_activity_list_t, os_activity_process_t,
+ bool (^)(os_activity_entry_t)))dlsym(RTLD_DEFAULT,
+ "os_activity_iterate_activities");
+ m_os_trace_get_type =
+ (uint8_t(*)(os_trace_message_t))dlsym(RTLD_DEFAULT, "os_trace_get_type");
+ m_os_trace_copy_formatted_message = (char *(*)(os_trace_message_t))dlsym(
+ RTLD_DEFAULT, "os_trace_copy_formatted_message");
+ m_os_activity_for_thread =
+ (os_activity_t(*)(os_activity_process_t, uint64_t))dlsym(
+ RTLD_DEFAULT, "os_activity_for_thread");
+ m_os_activity_for_task_thread = (os_activity_t(*)(task_t, uint64_t))dlsym(
+ RTLD_DEFAULT, "os_activity_for_task_thread");
+ m_os_activity_messages_for_thread = (os_trace_message_list_t(*)(
+ os_activity_process_t process, os_activity_t activity,
+ uint64_t thread_id))dlsym(RTLD_DEFAULT,
+ "os_activity_messages_for_thread");
+}
+
+Genealogy::ThreadActivitySP
+Genealogy::GetGenealogyInfoForThread(pid_t pid, nub_thread_t tid,
+ const MachThreadList &thread_list,
+ task_t task, bool &timed_out) {
+ ThreadActivitySP activity;
+ //
+ // if we've timed out trying to get the activities, don't try again at this
+ // process stop.
+ // (else we'll need to hit the timeout for every thread we're asked about.)
+ // We'll try again at the next public stop.
+
+ if (m_thread_activities.size() == 0 && !m_diagnosticd_call_timed_out) {
+ GetActivities(pid, thread_list, task);
+ }
+ std::map<nub_thread_t, ThreadActivitySP>::const_iterator search;
+ search = m_thread_activities.find(tid);
+ if (search != m_thread_activities.end()) {
+ activity = search->second;
+ }
+ timed_out = m_diagnosticd_call_timed_out;
+ return activity;
+}
+
+void Genealogy::Clear() {
+ m_thread_activities.clear();
+ m_diagnosticd_call_timed_out = false;
+}
+
+void Genealogy::GetActivities(pid_t pid, const MachThreadList &thread_list,
+ task_t task) {
+ if (m_os_activity_diagnostic_for_pid != nullptr &&
+ m_os_activity_iterate_processes != nullptr &&
+ m_os_activity_iterate_breadcrumbs != nullptr &&
+ m_os_activity_iterate_messages != nullptr &&
+ m_os_activity_iterate_activities != nullptr &&
+ m_os_trace_get_type != nullptr &&
+ m_os_trace_copy_formatted_message != nullptr &&
+ (m_os_activity_for_thread != nullptr ||
+ m_os_activity_for_task_thread != nullptr)) {
+ __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
+ __block BreadcrumbList breadcrumbs;
+ __block ActivityList activities;
+ __block MessageList messages;
+ __block std::map<nub_thread_t, uint64_t> thread_activity_mapping;
+
+ os_activity_diagnostic_flag_t flags =
+ OS_ACTIVITY_DIAGNOSTIC_ALL_ACTIVITIES |
+ OS_ACTIVITY_DIAGNOSTIC_PROCESS_ONLY;
+ if (m_os_activity_diagnostic_for_pid(
+ pid, 0, flags, ^(os_activity_process_list_t processes, int error) {
+ if (error == 0) {
+ m_os_activity_iterate_processes(processes, ^bool(
+ os_activity_process_t
+ process_info) {
+ if (pid == process_info->pid) {
+ // Collect all the Breadcrumbs
+ m_os_activity_iterate_breadcrumbs(
+ process_info,
+ ^bool(os_activity_breadcrumb_t breadcrumb) {
+ Breadcrumb bc;
+ bc.breadcrumb_id = breadcrumb->breadcrumb_id;
+ bc.activity_id = breadcrumb->activity_id;
+ bc.timestamp = breadcrumb->timestamp;
+ if (breadcrumb->name)
+ bc.name = breadcrumb->name;
+ breadcrumbs.push_back(bc);
+ return true;
+ });
+
+ // Collect all the Activites
+ m_os_activity_iterate_activities(
+ process_info->activities, process_info,
+ ^bool(os_activity_entry_t activity) {
+ Activity ac;
+ ac.activity_start = activity->activity_start;
+ ac.activity_id = activity->activity_id;
+ ac.parent_id = activity->parent_id;
+ if (activity->activity_name)
+ ac.activity_name = activity->activity_name;
+ if (activity->reason)
+ ac.reason = activity->reason;
+ activities.push_back(ac);
+ return true;
+ });
+
+ // Collect all the Messages -- messages not associated with
+ // any thread
+ m_os_activity_iterate_messages(
+ process_info->messages, process_info,
+ ^bool(os_trace_message_t trace_msg) {
+ Message msg;
+ msg.timestamp = trace_msg->timestamp;
+ msg.trace_id = trace_msg->trace_id;
+ msg.thread = trace_msg->thread;
+ msg.type = m_os_trace_get_type(trace_msg);
+ msg.activity_id = 0;
+ if (trace_msg->image_uuid && trace_msg->image_path) {
+ ProcessExecutableInfoSP process_info_sp(
+ new ProcessExecutableInfo());
+ uuid_copy(process_info_sp->image_uuid,
+ trace_msg->image_uuid);
+ process_info_sp->image_path = trace_msg->image_path;
+ msg.process_info_index =
+ AddProcessExecutableInfo(process_info_sp);
+ }
+ const char *message_text =
+ m_os_trace_copy_formatted_message(trace_msg);
+ if (message_text)
+ msg.message = message_text;
+ messages.push_back(msg);
+ return true;
+ });
+
+ // Discover which activities are said to be running on
+ // threads currently
+ const nub_size_t num_threads = thread_list.NumThreads();
+ for (nub_size_t i = 0; i < num_threads; ++i) {
+ nub_thread_t thread_id = thread_list.ThreadIDAtIndex(i);
+ os_activity_t act = 0;
+ if (m_os_activity_for_task_thread != nullptr) {
+ act = m_os_activity_for_task_thread(task, thread_id);
+ } else if (m_os_activity_for_thread != nullptr) {
+ act = m_os_activity_for_thread(process_info, thread_id);
+ }
+ if (act != 0)
+ thread_activity_mapping[thread_id] = act;
+ }
+
+ // Collect all Messages -- messages associated with a thread
+
+ // When there's no genealogy information, an early version
+ // of os_activity_messages_for_thread
+ // can crash in rare circumstances. Check to see if this
+ // process has any activities before
+ // making the call to get messages.
+ if (process_info->activities != nullptr &&
+ thread_activity_mapping.size() > 0) {
+ std::map<nub_thread_t, uint64_t>::const_iterator iter;
+ for (iter = thread_activity_mapping.begin();
+ iter != thread_activity_mapping.end(); ++iter) {
+ nub_thread_t thread_id = iter->first;
+ os_activity_t act = iter->second;
+ os_trace_message_list_t this_thread_messages =
+ m_os_activity_messages_for_thread(process_info, act,
+ thread_id);
+ m_os_activity_iterate_messages(
+ this_thread_messages, process_info,
+ ^bool(os_trace_message_t trace_msg) {
+ Message msg;
+ msg.timestamp = trace_msg->timestamp;
+ msg.trace_id = trace_msg->trace_id;
+ msg.thread = trace_msg->thread;
+ msg.type = m_os_trace_get_type(trace_msg);
+ msg.activity_id = act;
+ if (trace_msg->image_uuid &&
+ trace_msg->image_path) {
+ ProcessExecutableInfoSP process_info_sp(
+ new ProcessExecutableInfo());
+ uuid_copy(process_info_sp->image_uuid,
+ trace_msg->image_uuid);
+ process_info_sp->image_path =
+ trace_msg->image_path;
+ msg.process_info_index =
+ AddProcessExecutableInfo(process_info_sp);
+ }
+ const char *message_text =
+ m_os_trace_copy_formatted_message(trace_msg);
+ if (message_text)
+ msg.message = message_text;
+ messages.push_back(msg);
+ return true;
+ });
+ }
+ }
+ }
+ return true;
+ });
+ }
+ dispatch_semaphore_signal(semaphore);
+ }) == true) {
+ // Wait for the diagnosticd xpc calls to all finish up -- or half a second
+ // to elapse.
+ dispatch_time_t timeout =
+ dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 2);
+ bool success = dispatch_semaphore_wait(semaphore, timeout) == 0;
+ if (!success) {
+ m_diagnosticd_call_timed_out = true;
+ return;
+ }
+ }
+
+ // breadcrumbs, activities, and messages have all now been filled in.
+
+ std::map<nub_thread_t, uint64_t>::const_iterator iter;
+ for (iter = thread_activity_mapping.begin();
+ iter != thread_activity_mapping.end(); ++iter) {
+ nub_thread_t thread_id = iter->first;
+ uint64_t activity_id = iter->second;
+ ActivityList::const_iterator activity_search;
+ for (activity_search = activities.begin();
+ activity_search != activities.end(); ++activity_search) {
+ if (activity_search->activity_id == activity_id) {
+ ThreadActivitySP thread_activity_sp(new ThreadActivity());
+ thread_activity_sp->current_activity = *activity_search;
+
+ BreadcrumbList::const_iterator breadcrumb_search;
+ for (breadcrumb_search = breadcrumbs.begin();
+ breadcrumb_search != breadcrumbs.end(); ++breadcrumb_search) {
+ if (breadcrumb_search->activity_id == activity_id) {
+ thread_activity_sp->breadcrumbs.push_back(*breadcrumb_search);
+ }
+ }
+ MessageList::const_iterator message_search;
+ for (message_search = messages.begin();
+ message_search != messages.end(); ++message_search) {
+ if (message_search->thread == thread_id) {
+ thread_activity_sp->messages.push_back(*message_search);
+ }
+ }
+
+ m_thread_activities[thread_id] = thread_activity_sp;
+ break;
+ }
+ }
+ }
+ }
+}
+
+uint32_t
+Genealogy::AddProcessExecutableInfo(ProcessExecutableInfoSP process_exe_info) {
+ const uint32_t info_size =
+ static_cast<uint32_t>(m_process_executable_infos.size());
+ for (uint32_t idx = 0; idx < info_size; ++idx) {
+ if (uuid_compare(m_process_executable_infos[idx]->image_uuid,
+ process_exe_info->image_uuid) == 0) {
+ return idx + 1;
+ }
+ }
+ m_process_executable_infos.push_back(process_exe_info);
+ return info_size + 1;
+}
+
+Genealogy::ProcessExecutableInfoSP
+Genealogy::GetProcessExecutableInfosAtIndex(size_t idx) {
+ ProcessExecutableInfoSP info_sp;
+ if (idx > 0) {
+ idx--;
+ if (idx <= m_process_executable_infos.size()) {
+ info_sp = m_process_executable_infos[idx];
+ }
+ }
+ return info_sp;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/Genealogy.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/Genealogy.h
new file mode 100644
index 00000000000..969405a18af
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/Genealogy.h
@@ -0,0 +1,119 @@
+//===-- Genealogy.h ---------------------------------------------*- C++ -*-===//
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __Genealogy_h__
+#define __Genealogy_h__
+
+#include <mach/task.h>
+#include <map>
+#include <pthread.h>
+#include <string>
+#include <vector>
+
+#include "GenealogySPI.h"
+#include "MachThreadList.h"
+
+class Genealogy {
+public:
+ Genealogy();
+
+ ~Genealogy() {}
+
+ void Clear();
+
+ struct Breadcrumb {
+ uint32_t breadcrumb_id;
+ uint64_t activity_id;
+ uint64_t timestamp;
+ std::string name;
+ };
+
+ struct Activity {
+ uint64_t activity_start;
+ uint64_t activity_id;
+ uint64_t parent_id;
+ std::string activity_name;
+ std::string reason;
+ };
+
+ struct Message {
+ uint64_t timestamp;
+ uint64_t activity_id;
+ uint64_t trace_id;
+ uint64_t thread;
+ uint8_t type; // OS_TRACE_TYPE_RELEASE, OS_TRACE_TYPE_DEBUG,
+ // OS_TRACE_TYPE_ERROR, OS_TRACE_TYPE_FAULT
+ uint32_t process_info_index; // index # of the image uuid/file path, 0 means
+ // unknown
+ std::string message;
+ };
+
+ typedef std::vector<Message> MessageList;
+ typedef std::vector<Breadcrumb> BreadcrumbList;
+ typedef std::vector<Activity> ActivityList;
+
+ struct ThreadActivity {
+ Activity current_activity;
+ MessageList messages;
+ BreadcrumbList breadcrumbs; // should be 0 or 1 breadcrumbs; no more than 1
+ // BC for any given activity
+ };
+
+ typedef std::shared_ptr<ThreadActivity> ThreadActivitySP;
+
+ ThreadActivitySP GetGenealogyInfoForThread(pid_t pid, nub_thread_t tid,
+ const MachThreadList &thread_list,
+ task_t task, bool &timed_out);
+
+ struct ProcessExecutableInfo {
+ std::string image_path;
+ uuid_t image_uuid;
+ };
+
+ typedef std::shared_ptr<ProcessExecutableInfo> ProcessExecutableInfoSP;
+
+ ProcessExecutableInfoSP GetProcessExecutableInfosAtIndex(size_t idx);
+
+ uint32_t AddProcessExecutableInfo(ProcessExecutableInfoSP process_exe_info);
+
+private:
+ void GetActivities(pid_t pid, const MachThreadList &thread_list, task_t task);
+
+ // the spi we need to call into libtrace - look them up via dlsym at runtime
+ bool (*m_os_activity_diagnostic_for_pid)(pid_t pid, os_activity_t activity,
+ uint32_t flags,
+ os_diagnostic_block_t block);
+ void (*m_os_activity_iterate_processes)(
+ os_activity_process_list_t processes,
+ bool (^iterator)(os_activity_process_t process_info));
+ void (*m_os_activity_iterate_breadcrumbs)(
+ os_activity_process_t process_info,
+ bool (^iterator)(os_activity_breadcrumb_t breadcrumb));
+ void (*m_os_activity_iterate_messages)(
+ os_trace_message_list_t messages, os_activity_process_t process_info,
+ bool (^iterator)(os_trace_message_t tracemsg));
+ void (*m_os_activity_iterate_activities)(
+ os_activity_list_t activities, os_activity_process_t process_info,
+ bool (^iterator)(os_activity_entry_t activity));
+ uint8_t (*m_os_trace_get_type)(os_trace_message_t trace_msg);
+ char *(*m_os_trace_copy_formatted_message)(os_trace_message_t trace_msg);
+ os_activity_t (*m_os_activity_for_thread)(os_activity_process_t process,
+ uint64_t thread_id);
+ os_activity_t (*m_os_activity_for_task_thread)(task_t target,
+ uint64_t thread_id);
+ os_trace_message_list_t (*m_os_activity_messages_for_thread)(
+ os_activity_process_t process, os_activity_t activity,
+ uint64_t thread_id);
+
+ std::map<nub_thread_t, ThreadActivitySP> m_thread_activities;
+ std::vector<ProcessExecutableInfoSP> m_process_executable_infos;
+ bool m_diagnosticd_call_timed_out;
+};
+
+#endif // __Genealogy_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/GenealogySPI.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/GenealogySPI.h
new file mode 100644
index 00000000000..0aea512b369
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/GenealogySPI.h
@@ -0,0 +1,94 @@
+//===-- GenealogySPI.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//===----------------------------------------------------------------------===//
+
+#ifndef __GenealogySPI_h__
+#define __GenealogySPI_h__
+
+#include <xpc/xpc.h>
+
+typedef void *os_activity_process_list_t;
+typedef void *os_activity_list_t;
+typedef void *os_trace_message_list_t;
+typedef struct os_activity_watch_s *os_activity_watch_t;
+typedef uint64_t os_activity_t;
+
+struct os_activity_breadcrumb_s {
+ uint32_t breadcrumb_id;
+ uint64_t activity_id;
+ uint64_t timestamp;
+ const char *name;
+};
+
+typedef struct os_activity_breadcrumb_s *os_activity_breadcrumb_t;
+
+typedef struct os_trace_message_s {
+ uint64_t trace_id;
+ uint64_t thread;
+ uint64_t timestamp;
+ uint32_t offset;
+ xpc_object_t __unsafe_unretained payload;
+ const uint8_t *image_uuid;
+ const char *image_path;
+ const char *format;
+ const void *buffer;
+ size_t bufferLen;
+} * os_trace_message_t;
+
+typedef struct os_activity_process_s {
+ os_activity_process_list_t child_procs;
+ os_trace_message_list_t messages;
+ os_activity_list_t activities;
+ void *breadcrumbs;
+ uint64_t proc_id;
+ const uint8_t *image_uuid;
+ const char *image_path;
+ pid_t pid;
+} * os_activity_process_t;
+
+typedef struct os_activity_entry_s {
+ uint64_t activity_start;
+ os_activity_t activity_id;
+ os_activity_t parent_id;
+ const char *activity_name;
+ const char *reason;
+ os_trace_message_list_t messages;
+} * os_activity_entry_t;
+
+enum {
+ OS_ACTIVITY_DIAGNOSTIC_DEFAULT = 0x00000000,
+ OS_ACTIVITY_DIAGNOSTIC_PROCESS_ONLY = 0x00000001,
+ OS_ACTIVITY_DIAGNOSTIC_SKIP_DECODE = 0x00000002,
+ OS_ACTIVITY_DIAGNOSTIC_FLATTENED = 0x00000004,
+ OS_ACTIVITY_DIAGNOSTIC_ALL_ACTIVITIES = 0x00000008,
+ OS_ACTIVITY_DIAGNOSTIC_MAX = 0x0000000f
+};
+typedef uint32_t os_activity_diagnostic_flag_t;
+
+enum {
+ OS_ACTIVITY_WATCH_DEFAULT = 0x00000000,
+ OS_ACTIVITY_WATCH_PROCESS_ONLY = 0x00000001,
+ OS_ACTIVITY_WATCH_SKIP_DECODE = 0x00000002,
+ OS_ACTIVITY_WATCH_PAYLOAD = 0x00000004,
+ OS_ACTIVITY_WATCH_ERRORS = 0x00000008,
+ OS_ACTIVITY_WATCH_FAULTS = 0x00000010,
+ OS_ACTIVITY_WATCH_MAX = 0x0000001f
+};
+typedef uint32_t os_activity_watch_flag_t;
+
+// Return values from os_trace_get_type()
+#define OS_TRACE_TYPE_RELEASE (1u << 0)
+#define OS_TRACE_TYPE_DEBUG (1u << 1)
+#define OS_TRACE_TYPE_ERROR ((1u << 6) | (1u << 0))
+#define OS_TRACE_TYPE_FAULT ((1u << 7) | (1u << 6) | (1u << 0))
+
+typedef void (^os_activity_watch_block_t)(os_activity_watch_t watch,
+ os_activity_process_t process_info,
+ bool canceled);
+typedef void (^os_diagnostic_block_t)(os_activity_process_list_t processes,
+ int error);
+
+#endif
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachException.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachException.cpp
new file mode 100644
index 00000000000..8690960e4cd
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachException.cpp
@@ -0,0 +1,514 @@
+//===-- MachException.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/18/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachException.h"
+#include "DNB.h"
+#include "DNBError.h"
+#include "DNBLog.h"
+#include "MachProcess.h"
+#include "PThreadMutex.h"
+#include "SysSignal.h"
+#include <errno.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+
+// Routine mach_exception_raise
+extern "C" kern_return_t
+catch_mach_exception_raise(mach_port_t exception_port, mach_port_t thread,
+ mach_port_t task, exception_type_t exception,
+ mach_exception_data_t code,
+ mach_msg_type_number_t codeCnt);
+
+extern "C" kern_return_t catch_mach_exception_raise_state(
+ mach_port_t exception_port, exception_type_t exception,
+ const mach_exception_data_t code, mach_msg_type_number_t codeCnt,
+ int *flavor, const thread_state_t old_state,
+ mach_msg_type_number_t old_stateCnt, thread_state_t new_state,
+ mach_msg_type_number_t *new_stateCnt);
+
+// Routine mach_exception_raise_state_identity
+extern "C" kern_return_t catch_mach_exception_raise_state_identity(
+ mach_port_t exception_port, mach_port_t thread, mach_port_t task,
+ exception_type_t exception, mach_exception_data_t code,
+ mach_msg_type_number_t codeCnt, int *flavor, thread_state_t old_state,
+ mach_msg_type_number_t old_stateCnt, thread_state_t new_state,
+ mach_msg_type_number_t *new_stateCnt);
+
+extern "C" boolean_t mach_exc_server(mach_msg_header_t *InHeadP,
+ mach_msg_header_t *OutHeadP);
+
+// Note: g_message points to the storage allocated to catch the data from
+// catching the current exception raise. It's populated when we catch a raised
+// exception which can't immediately be replied to.
+//
+// If it becomes possible to catch exceptions from multiple threads
+// simultaneously, accesses to g_message would need to be mutually exclusive.
+static MachException::Data *g_message = NULL;
+
+extern "C" kern_return_t catch_mach_exception_raise_state(
+ mach_port_t exc_port, exception_type_t exc_type,
+ const mach_exception_data_t exc_data, mach_msg_type_number_t exc_data_count,
+ int *flavor, const thread_state_t old_state,
+ mach_msg_type_number_t old_stateCnt, thread_state_t new_state,
+ mach_msg_type_number_t *new_stateCnt) {
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) {
+ DNBLogThreaded("::%s ( exc_port = 0x%4.4x, exc_type = %d ( %s ), exc_data "
+ "= 0x%llx, exc_data_count = %d)",
+ __FUNCTION__, exc_port, exc_type,
+ MachException::Name(exc_type), (uint64_t)exc_data,
+ exc_data_count);
+ }
+ return KERN_FAILURE;
+}
+
+extern "C" kern_return_t catch_mach_exception_raise_state_identity(
+ mach_port_t exc_port, mach_port_t thread_port, mach_port_t task_port,
+ exception_type_t exc_type, mach_exception_data_t exc_data,
+ mach_msg_type_number_t exc_data_count, int *flavor,
+ thread_state_t old_state, mach_msg_type_number_t old_stateCnt,
+ thread_state_t new_state, mach_msg_type_number_t *new_stateCnt) {
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) {
+ DNBLogThreaded("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = "
+ "0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { 0x%llx, "
+ "0x%llx })",
+ __FUNCTION__, exc_port, thread_port, task_port, exc_type,
+ MachException::Name(exc_type), exc_data_count,
+ (uint64_t)(exc_data_count > 0 ? exc_data[0] : 0xBADDBADD),
+ (uint64_t)(exc_data_count > 1 ? exc_data[1] : 0xBADDBADD));
+ }
+
+ return KERN_FAILURE;
+}
+
+extern "C" kern_return_t
+catch_mach_exception_raise(mach_port_t exc_port, mach_port_t thread_port,
+ mach_port_t task_port, exception_type_t exc_type,
+ mach_exception_data_t exc_data,
+ mach_msg_type_number_t exc_data_count) {
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) {
+ DNBLogThreaded("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = "
+ "0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { 0x%llx, "
+ "0x%llx })",
+ __FUNCTION__, exc_port, thread_port, task_port, exc_type,
+ MachException::Name(exc_type), exc_data_count,
+ (uint64_t)(exc_data_count > 0 ? exc_data[0] : 0xBADDBADD),
+ (uint64_t)(exc_data_count > 1 ? exc_data[1] : 0xBADDBADD));
+ }
+ g_message->exc_type = 0;
+ g_message->exc_data.clear();
+
+ if (task_port == g_message->task_port) {
+ g_message->task_port = task_port;
+ g_message->thread_port = thread_port;
+ g_message->exc_type = exc_type;
+ g_message->AppendExceptionData(exc_data, exc_data_count);
+ return KERN_SUCCESS;
+ } else if (!MachTask::IsValid(g_message->task_port)) {
+ // Our original exception port isn't valid anymore check for a SIGTRAP
+ if (exc_type == EXC_SOFTWARE && exc_data_count == 2 &&
+ exc_data[0] == EXC_SOFT_SIGNAL && exc_data[1] == SIGTRAP) {
+ // We got a SIGTRAP which indicates we might have exec'ed and possibly
+ // lost our old task port during the exec, so we just need to switch over
+ // to using this new task port
+ g_message->task_port = task_port;
+ g_message->thread_port = thread_port;
+ g_message->exc_type = exc_type;
+ g_message->AppendExceptionData(exc_data, exc_data_count);
+ return KERN_SUCCESS;
+ }
+ }
+ return KERN_FAILURE;
+}
+
+void MachException::Message::Dump() const {
+ DNBLogThreadedIf(LOG_EXCEPTIONS, " exc_msg { bits = 0x%8.8x size = 0x%8.8x "
+ "remote-port = 0x%8.8x local-port = 0x%8.8x "
+ "reserved = 0x%8.8x id = 0x%8.8x } ",
+ exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size,
+ exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port,
+ exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id);
+
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "reply_msg { bits = 0x%8.8x size = 0x%8.8x "
+ "remote-port = 0x%8.8x local-port = 0x%8.8x "
+ "reserved = 0x%8.8x id = 0x%8.8x }",
+ reply_msg.hdr.msgh_bits, reply_msg.hdr.msgh_size,
+ reply_msg.hdr.msgh_remote_port,
+ reply_msg.hdr.msgh_local_port, reply_msg.hdr.msgh_reserved,
+ reply_msg.hdr.msgh_id);
+
+ state.Dump();
+}
+
+bool MachException::Data::GetStopInfo(
+ struct DNBThreadStopInfo *stop_info) const {
+ // Zero out the structure.
+ memset(stop_info, 0, sizeof(struct DNBThreadStopInfo));
+
+ if (exc_type == 0) {
+ stop_info->reason = eStopTypeInvalid;
+ return true;
+ }
+
+ // We always stop with a mach exceptions
+ stop_info->reason = eStopTypeException;
+ // Save the EXC_XXXX exception type
+ stop_info->details.exception.type = exc_type;
+
+ // Fill in a text description
+ const char *exc_name = MachException::Name(exc_type);
+ char *desc = stop_info->description;
+ const char *end_desc = desc + DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH;
+ if (exc_name)
+ desc +=
+ snprintf(desc, DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH, "%s", exc_name);
+ else
+ desc +=
+ snprintf(desc, DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH, "%i", exc_type);
+
+ stop_info->details.exception.data_count = exc_data.size();
+
+ int soft_signal = SoftSignal();
+ if (soft_signal) {
+ if (desc < end_desc) {
+ const char *sig_str = SysSignal::Name(soft_signal);
+ snprintf(desc, end_desc - desc, " EXC_SOFT_SIGNAL( %i ( %s ))",
+ soft_signal, sig_str ? sig_str : "unknown signal");
+ }
+ } else {
+ // No special disassembly for exception data, just
+ size_t idx;
+ if (desc < end_desc) {
+ desc += snprintf(desc, end_desc - desc, " data[%llu] = {",
+ (uint64_t)stop_info->details.exception.data_count);
+
+ for (idx = 0;
+ desc < end_desc && idx < stop_info->details.exception.data_count;
+ ++idx)
+ desc += snprintf(
+ desc, end_desc - desc, "0x%llx%c", (uint64_t)exc_data[idx],
+ ((idx + 1 == stop_info->details.exception.data_count) ? '}' : ','));
+ }
+ }
+
+ // Copy the exception data
+ size_t i;
+ for (i = 0; i < stop_info->details.exception.data_count; i++)
+ stop_info->details.exception.data[i] = exc_data[i];
+
+ return true;
+}
+
+void MachException::Data::DumpStopReason() const {
+ int soft_signal = SoftSignal();
+ if (soft_signal) {
+ const char *signal_str = SysSignal::Name(soft_signal);
+ if (signal_str)
+ DNBLog("signal(%s)", signal_str);
+ else
+ DNBLog("signal(%i)", soft_signal);
+ return;
+ }
+ DNBLog("%s", Name(exc_type));
+}
+
+kern_return_t MachException::Message::Receive(mach_port_t port,
+ mach_msg_option_t options,
+ mach_msg_timeout_t timeout,
+ mach_port_t notify_port) {
+ DNBError err;
+ const bool log_exceptions = DNBLogCheckLogBit(LOG_EXCEPTIONS);
+ mach_msg_timeout_t mach_msg_timeout =
+ options & MACH_RCV_TIMEOUT ? timeout : 0;
+ if (log_exceptions && ((options & MACH_RCV_TIMEOUT) == 0)) {
+ // Dump this log message if we have no timeout in case it never returns
+ DNBLogThreaded("::mach_msg ( msg->{bits = %#x, size = %u remote_port = "
+ "%#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option "
+ "= %#x, send_size = 0, rcv_size = %llu, rcv_name = %#x, "
+ "timeout = %u, notify = %#x)",
+ exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size,
+ exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port,
+ exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id, options,
+ (uint64_t)sizeof(exc_msg.data), port, mach_msg_timeout,
+ notify_port);
+ }
+
+ err = ::mach_msg(&exc_msg.hdr,
+ options, // options
+ 0, // Send size
+ sizeof(exc_msg.data), // Receive size
+ port, // exception port to watch for exception on
+ mach_msg_timeout, // timeout in msec (obeyed only if
+ // MACH_RCV_TIMEOUT is ORed into the
+ // options parameter)
+ notify_port);
+
+ // Dump any errors we get
+ if (log_exceptions) {
+ err.LogThreaded("::mach_msg ( msg->{bits = %#x, size = %u remote_port = "
+ "%#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, "
+ "option = %#x, send_size = %u, rcv_size = %u, rcv_name = "
+ "%#x, timeout = %u, notify = %#x)",
+ exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size,
+ exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port,
+ exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id, options, 0,
+ sizeof(exc_msg.data), port, mach_msg_timeout, notify_port);
+ }
+ return err.Status();
+}
+
+bool MachException::Message::CatchExceptionRaise(task_t task) {
+ bool success = false;
+ state.task_port = task;
+ g_message = &state;
+ // The exc_server function is the MIG generated server handling function
+ // to handle messages from the kernel relating to the occurrence of an
+ // exception in a thread. Such messages are delivered to the exception port
+ // set via thread_set_exception_ports or task_set_exception_ports. When an
+ // exception occurs in a thread, the thread sends an exception message to
+ // its exception port, blocking in the kernel waiting for the receipt of a
+ // reply. The exc_server function performs all necessary argument handling
+ // for this kernel message and calls catch_exception_raise,
+ // catch_exception_raise_state or catch_exception_raise_state_identity,
+ // which should handle the exception. If the called routine returns
+ // KERN_SUCCESS, a reply message will be sent, allowing the thread to
+ // continue from the point of the exception; otherwise, no reply message
+ // is sent and the called routine must have dealt with the exception
+ // thread directly.
+ if (mach_exc_server(&exc_msg.hdr, &reply_msg.hdr)) {
+ success = true;
+ } else if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) {
+ DNBLogThreaded("mach_exc_server returned zero...");
+ }
+ g_message = NULL;
+ return success;
+}
+
+kern_return_t MachException::Message::Reply(MachProcess *process, int signal) {
+ // Reply to the exception...
+ DNBError err;
+
+ // If we had a soft signal, we need to update the thread first so it can
+ // continue without signaling
+ int soft_signal = state.SoftSignal();
+ if (soft_signal) {
+ int state_pid = -1;
+ if (process->Task().TaskPort() == state.task_port) {
+ // This is our task, so we can update the signal to send to it
+ state_pid = process->ProcessID();
+ soft_signal = signal;
+ } else {
+ err = ::pid_for_task(state.task_port, &state_pid);
+ }
+
+ assert(state_pid != -1);
+ if (state_pid != -1) {
+ errno = 0;
+ if (::ptrace(PT_THUPDATE, state_pid,
+ (caddr_t)((uintptr_t)state.thread_port), soft_signal) != 0)
+ err.SetError(errno, DNBError::POSIX);
+ else
+ err.Clear();
+
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail())
+ err.LogThreaded("::ptrace (request = PT_THUPDATE, pid = 0x%4.4x, tid = "
+ "0x%4.4x, signal = %i)",
+ state_pid, state.thread_port, soft_signal);
+ }
+ }
+
+ DNBLogThreadedIf(
+ LOG_EXCEPTIONS, "::mach_msg ( msg->{bits = %#x, size = %u, remote_port = "
+ "%#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, "
+ "option = %#x, send_size = %u, rcv_size = %u, rcv_name = "
+ "%#x, timeout = %u, notify = %#x)",
+ reply_msg.hdr.msgh_bits, reply_msg.hdr.msgh_size,
+ reply_msg.hdr.msgh_remote_port, reply_msg.hdr.msgh_local_port,
+ reply_msg.hdr.msgh_reserved, reply_msg.hdr.msgh_id,
+ MACH_SEND_MSG | MACH_SEND_INTERRUPT, reply_msg.hdr.msgh_size, 0,
+ MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+
+ err = ::mach_msg(&reply_msg.hdr, MACH_SEND_MSG | MACH_SEND_INTERRUPT,
+ reply_msg.hdr.msgh_size, 0, MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+
+ if (err.Fail()) {
+ if (err.Status() == MACH_SEND_INTERRUPTED) {
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
+ err.LogThreaded("::mach_msg() - send interrupted");
+ // TODO: keep retrying to reply???
+ } else {
+ if (state.task_port == process->Task().TaskPort()) {
+ DNBLogThreaded("error: mach_msg() returned an error when replying to a "
+ "mach exception: error = %u",
+ err.Status());
+ } else {
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
+ err.LogThreaded("::mach_msg() - failed (child of task)");
+ }
+ }
+ }
+
+ return err.Status();
+}
+
+void MachException::Data::Dump() const {
+ const char *exc_type_name = MachException::Name(exc_type);
+ DNBLogThreadedIf(
+ LOG_EXCEPTIONS, " state { task_port = 0x%4.4x, thread_port = "
+ "0x%4.4x, exc_type = %i (%s) ...",
+ task_port, thread_port, exc_type, exc_type_name ? exc_type_name : "???");
+
+ const size_t exc_data_count = exc_data.size();
+ // Dump any special exception data contents
+ int soft_signal = SoftSignal();
+ if (soft_signal != 0) {
+ const char *sig_str = SysSignal::Name(soft_signal);
+ DNBLogThreadedIf(LOG_EXCEPTIONS,
+ " exc_data: EXC_SOFT_SIGNAL (%i (%s))",
+ soft_signal, sig_str ? sig_str : "unknown signal");
+ } else {
+ // No special disassembly for this data, just dump the data
+ size_t idx;
+ for (idx = 0; idx < exc_data_count; ++idx) {
+ DNBLogThreadedIf(LOG_EXCEPTIONS, " exc_data[%llu]: 0x%llx",
+ (uint64_t)idx, (uint64_t)exc_data[idx]);
+ }
+ }
+}
+
+// The EXC_MASK_ALL value hard-coded here so that lldb can be built
+// on a new OS with an older deployment target . The new OS may have
+// an addition to its EXC_MASK_ALL that the old OS will not recognize -
+// <mach/exception_types.h> doesn't vary the value based on the deployment
+// target. So we need a known set of masks that can be assumed to be
+// valid when running on an older OS. We'll fall back to trying
+// PREV_EXC_MASK_ALL if the EXC_MASK_ALL value lldb was compiled with is
+// not recognized.
+
+#define PREV_EXC_MASK_ALL (EXC_MASK_BAD_ACCESS | \
+ EXC_MASK_BAD_INSTRUCTION | \
+ EXC_MASK_ARITHMETIC | \
+ EXC_MASK_EMULATION | \
+ EXC_MASK_SOFTWARE | \
+ EXC_MASK_BREAKPOINT | \
+ EXC_MASK_SYSCALL | \
+ EXC_MASK_MACH_SYSCALL | \
+ EXC_MASK_RPC_ALERT | \
+ EXC_MASK_RESOURCE | \
+ EXC_MASK_GUARD | \
+ EXC_MASK_MACHINE)
+
+#define LLDB_EXC_MASK EXC_MASK_ALL
+
+kern_return_t MachException::PortInfo::Save(task_t task) {
+ DNBLogThreadedIf(LOG_EXCEPTIONS | LOG_VERBOSE,
+ "MachException::PortInfo::Save ( task = 0x%4.4x )", task);
+ // Be careful to be able to have debugserver built on a newer OS than what
+ // it is currently running on by being able to start with all exceptions
+ // and back off to just what is supported on the current system
+ DNBError err;
+
+ mask = LLDB_EXC_MASK;
+
+ count = (sizeof(ports) / sizeof(ports[0]));
+ err = ::task_get_exception_ports(task, mask, masks, &count, ports, behaviors,
+ flavors);
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail())
+ err.LogThreaded("::task_get_exception_ports ( task = 0x%4.4x, mask = 0x%x, "
+ "maskCnt => %u, ports, behaviors, flavors )",
+ task, mask, count);
+
+ if (err.Status() == KERN_INVALID_ARGUMENT && mask != PREV_EXC_MASK_ALL) {
+ mask = PREV_EXC_MASK_ALL;
+ count = (sizeof(ports) / sizeof(ports[0]));
+ err = ::task_get_exception_ports(task, mask, masks, &count, ports,
+ behaviors, flavors);
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail())
+ err.LogThreaded("::task_get_exception_ports ( task = 0x%4.4x, mask = "
+ "0x%x, maskCnt => %u, ports, behaviors, flavors )",
+ task, mask, count);
+ }
+ if (err.Fail()) {
+ mask = 0;
+ count = 0;
+ }
+ return err.Status();
+}
+
+kern_return_t MachException::PortInfo::Restore(task_t task) {
+ DNBLogThreadedIf(LOG_EXCEPTIONS | LOG_VERBOSE,
+ "MachException::PortInfo::Restore( task = 0x%4.4x )", task);
+ uint32_t i = 0;
+ DNBError err;
+ if (count > 0) {
+ for (i = 0; i < count; i++) {
+ err = ::task_set_exception_ports(task, masks[i], ports[i], behaviors[i],
+ flavors[i]);
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) {
+ err.LogThreaded("::task_set_exception_ports ( task = 0x%4.4x, "
+ "exception_mask = 0x%8.8x, new_port = 0x%4.4x, "
+ "behavior = 0x%8.8x, new_flavor = 0x%8.8x )",
+ task, masks[i], ports[i], behaviors[i], flavors[i]);
+ // Bail if we encounter any errors
+ }
+
+ if (err.Fail())
+ break;
+ }
+ }
+ count = 0;
+ return err.Status();
+}
+
+const char *MachException::Name(exception_type_t exc_type) {
+ switch (exc_type) {
+ case EXC_BAD_ACCESS:
+ return "EXC_BAD_ACCESS";
+ case EXC_BAD_INSTRUCTION:
+ return "EXC_BAD_INSTRUCTION";
+ case EXC_ARITHMETIC:
+ return "EXC_ARITHMETIC";
+ case EXC_EMULATION:
+ return "EXC_EMULATION";
+ case EXC_SOFTWARE:
+ return "EXC_SOFTWARE";
+ case EXC_BREAKPOINT:
+ return "EXC_BREAKPOINT";
+ case EXC_SYSCALL:
+ return "EXC_SYSCALL";
+ case EXC_MACH_SYSCALL:
+ return "EXC_MACH_SYSCALL";
+ case EXC_RPC_ALERT:
+ return "EXC_RPC_ALERT";
+#ifdef EXC_CRASH
+ case EXC_CRASH:
+ return "EXC_CRASH";
+#endif
+ case EXC_RESOURCE:
+ return "EXC_RESOURCE";
+#ifdef EXC_GUARD
+ case EXC_GUARD:
+ return "EXC_GUARD";
+#endif
+#ifdef EXC_CORPSE_NOTIFY
+ case EXC_CORPSE_NOTIFY:
+ return "EXC_CORPSE_NOTIFY";
+#endif
+#ifdef EXC_CORPSE_VARIANT_BIT
+ case EXC_CORPSE_VARIANT_BIT:
+ return "EXC_CORPSE_VARIANT_BIT";
+#endif
+ default:
+ break;
+ }
+ return NULL;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachException.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachException.h
new file mode 100644
index 00000000000..0a1d34170d6
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachException.h
@@ -0,0 +1,132 @@
+//===-- MachException.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/18/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachException_h__
+#define __MachException_h__
+
+#include <mach/mach.h>
+#include <vector>
+
+class MachProcess;
+class PThreadMutex;
+
+typedef union MachMessageTag {
+ mach_msg_header_t hdr;
+ char data[1024];
+} MachMessage;
+
+class MachException {
+public:
+ struct PortInfo {
+ exception_mask_t mask; // the exception mask for this device which may be a
+ // subset of EXC_MASK_ALL...
+ exception_mask_t masks[EXC_TYPES_COUNT];
+ mach_port_t ports[EXC_TYPES_COUNT];
+ exception_behavior_t behaviors[EXC_TYPES_COUNT];
+ thread_state_flavor_t flavors[EXC_TYPES_COUNT];
+ mach_msg_type_number_t count;
+
+ kern_return_t Save(task_t task);
+ kern_return_t Restore(task_t task);
+ };
+
+ struct Data {
+ task_t task_port;
+ thread_t thread_port;
+ exception_type_t exc_type;
+ std::vector<mach_exception_data_type_t> exc_data;
+ Data()
+ : task_port(TASK_NULL), thread_port(THREAD_NULL), exc_type(0),
+ exc_data() {}
+
+ void Clear() {
+ task_port = TASK_NULL;
+ thread_port = THREAD_NULL;
+ exc_type = 0;
+ exc_data.clear();
+ }
+ bool IsValid() const {
+ return task_port != TASK_NULL && thread_port != THREAD_NULL &&
+ exc_type != 0;
+ }
+ // Return the SoftSignal for this MachException data, or zero if there is
+ // none
+ int SoftSignal() const {
+ if (exc_type == EXC_SOFTWARE && exc_data.size() == 2 &&
+ exc_data[0] == EXC_SOFT_SIGNAL)
+ return static_cast<int>(exc_data[1]);
+ return 0;
+ }
+ bool IsBreakpoint() const {
+ return (exc_type == EXC_BREAKPOINT ||
+ ((exc_type == EXC_SOFTWARE) && exc_data[0] == 1));
+ }
+ void AppendExceptionData(mach_exception_data_t Data,
+ mach_msg_type_number_t Count) {
+ mach_exception_data_type_t Buf;
+ for (mach_msg_type_number_t i = 0; i < Count; ++i) {
+ // Perform an unaligned copy.
+ memcpy(&Buf, Data + i, sizeof(mach_exception_data_type_t));
+ exc_data.push_back(Buf);
+ }
+ }
+ void Dump() const;
+ void DumpStopReason() const;
+ bool GetStopInfo(struct DNBThreadStopInfo *stop_info) const;
+ };
+
+ struct Message {
+ MachMessage exc_msg;
+ MachMessage reply_msg;
+ Data state;
+
+ Message() : state() {
+ memset(&exc_msg, 0, sizeof(exc_msg));
+ memset(&reply_msg, 0, sizeof(reply_msg));
+ }
+ bool CatchExceptionRaise(task_t task);
+ void Dump() const;
+ kern_return_t Reply(MachProcess *process, int signal);
+ kern_return_t Receive(mach_port_t receive_port, mach_msg_option_t options,
+ mach_msg_timeout_t timeout,
+ mach_port_t notify_port = MACH_PORT_NULL);
+
+ typedef std::vector<Message> collection;
+ typedef collection::iterator iterator;
+ typedef collection::const_iterator const_iterator;
+ };
+
+ enum {
+ e_actionForward, // Forward signal to inferior process
+ e_actionStop, // Stop when this signal is received
+ };
+ struct Action {
+ task_t task_port; // Set to TASK_NULL for any TASK
+ thread_t thread_port; // Set to THREAD_NULL for any thread
+ exception_type_t exc_mask; // Mach exception mask to watch for
+ std::vector<mach_exception_data_type_t> exc_data_mask; // Mask to apply to
+ // exception data, or
+ // empty to ignore
+ // exc_data value for
+ // exception
+ std::vector<mach_exception_data_type_t> exc_data_value; // Value to compare
+ // to exception data
+ // after masking, or
+ // empty to ignore
+ // exc_data value
+ // for exception
+ uint8_t flags; // Action flags describing what to do with the exception
+ };
+ static const char *Name(exception_type_t exc_type);
+};
+
+#endif
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachProcess.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachProcess.h
new file mode 100644
index 00000000000..e31486cc39b
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachProcess.h
@@ -0,0 +1,433 @@
+//===-- MachProcess.h -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/15/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachProcess_h__
+#define __MachProcess_h__
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <mach-o/loader.h>
+#include <mach/mach.h>
+#include <pthread.h>
+#include <sys/signal.h>
+#include <uuid/uuid.h>
+#include <vector>
+
+#include "DNBBreakpoint.h"
+#include "DNBDefs.h"
+#include "DNBError.h"
+#include "DNBThreadResumeActions.h"
+#include "Genealogy.h"
+#include "JSONGenerator.h"
+#include "MachException.h"
+#include "MachTask.h"
+#include "MachThreadList.h"
+#include "MachVMMemory.h"
+#include "PThreadCondition.h"
+#include "PThreadEvent.h"
+#include "PThreadMutex.h"
+#include "ThreadInfo.h"
+
+class DNBThreadResumeActions;
+
+class MachProcess {
+public:
+ // Constructors and Destructors
+ MachProcess();
+ ~MachProcess();
+
+ // A structure that can hold everything debugserver needs to know from
+ // a binary's Mach-O header / load commands.
+
+ struct mach_o_segment {
+ std::string name;
+ uint64_t vmaddr;
+ uint64_t vmsize;
+ uint64_t fileoff;
+ uint64_t filesize;
+ uint64_t maxprot;
+ uint64_t initprot;
+ uint64_t nsects;
+ uint64_t flags;
+ };
+
+ struct mach_o_information {
+ struct mach_header_64 mach_header;
+ std::vector<struct mach_o_segment> segments;
+ uuid_t uuid;
+ std::string min_version_os_name;
+ std::string min_version_os_version;
+ };
+
+ struct binary_image_information {
+ std::string filename;
+ uint64_t load_address;
+ uint64_t mod_date; // may not be available - 0 if so
+ struct mach_o_information macho_info;
+
+ binary_image_information()
+ : filename(), load_address(INVALID_NUB_ADDRESS), mod_date(0) {}
+ };
+
+ // Child process control
+ pid_t AttachForDebug(pid_t pid, char *err_str, size_t err_len);
+ pid_t LaunchForDebug(const char *path, char const *argv[], char const *envp[],
+ const char *working_directory, const char *stdin_path,
+ const char *stdout_path, const char *stderr_path,
+ bool no_stdio, nub_launch_flavor_t launch_flavor,
+ int disable_aslr, const char *event_data, DNBError &err);
+
+ static uint32_t GetCPUTypeForLocalProcess(pid_t pid);
+ static pid_t ForkChildForPTraceDebugging(const char *path, char const *argv[],
+ char const *envp[],
+ MachProcess *process, DNBError &err);
+ static pid_t PosixSpawnChildForPTraceDebugging(
+ const char *path, cpu_type_t cpu_type, char const *argv[],
+ char const *envp[], const char *working_directory, const char *stdin_path,
+ const char *stdout_path, const char *stderr_path, bool no_stdio,
+ MachProcess *process, int disable_aslr, DNBError &err);
+ nub_addr_t GetDYLDAllImageInfosAddress();
+ static const void *PrepareForAttach(const char *path,
+ nub_launch_flavor_t launch_flavor,
+ bool waitfor, DNBError &err_str);
+ static void CleanupAfterAttach(const void *attach_token,
+ nub_launch_flavor_t launch_flavor,
+ bool success, DNBError &err_str);
+ static nub_process_t CheckForProcess(const void *attach_token,
+ nub_launch_flavor_t launch_flavor);
+#if defined(WITH_BKS) || defined(WITH_FBS)
+ pid_t BoardServiceLaunchForDebug(const char *app_bundle_path,
+ char const *argv[], char const *envp[],
+ bool no_stdio, bool disable_aslr,
+ const char *event_data,
+ DNBError &launch_err);
+ pid_t BoardServiceForkChildForPTraceDebugging(
+ const char *path, char const *argv[], char const *envp[], bool no_stdio,
+ bool disable_aslr, const char *event_data, DNBError &launch_err);
+ bool BoardServiceSendEvent(const char *event, DNBError &error);
+#endif
+ static bool GetOSVersionNumbers(uint64_t *major, uint64_t *minor,
+ uint64_t *patch);
+ static std::string GetMacCatalystVersionString();
+#ifdef WITH_BKS
+ static void BKSCleanupAfterAttach(const void *attach_token,
+ DNBError &err_str);
+#endif // WITH_BKS
+#ifdef WITH_FBS
+ static void FBSCleanupAfterAttach(const void *attach_token,
+ DNBError &err_str);
+#endif // WITH_FBS
+#ifdef WITH_SPRINGBOARD
+ pid_t SBLaunchForDebug(const char *app_bundle_path, char const *argv[],
+ char const *envp[], bool no_stdio, bool disable_aslr,
+ DNBError &launch_err);
+ static pid_t SBForkChildForPTraceDebugging(const char *path,
+ char const *argv[],
+ char const *envp[], bool no_stdio,
+ MachProcess *process,
+ DNBError &launch_err);
+#endif // WITH_SPRINGBOARD
+ nub_addr_t LookupSymbol(const char *name, const char *shlib);
+ void SetNameToAddressCallback(DNBCallbackNameToAddress callback,
+ void *baton) {
+ m_name_to_addr_callback = callback;
+ m_name_to_addr_baton = baton;
+ }
+ void
+ SetSharedLibraryInfoCallback(DNBCallbackCopyExecutableImageInfos callback,
+ void *baton) {
+ m_image_infos_callback = callback;
+ m_image_infos_baton = baton;
+ }
+
+ bool Resume(const DNBThreadResumeActions &thread_actions);
+ bool Signal(int signal, const struct timespec *timeout_abstime = NULL);
+ bool Interrupt();
+ bool SendEvent(const char *event, DNBError &send_err);
+ bool Kill(const struct timespec *timeout_abstime = NULL);
+ bool Detach();
+ nub_size_t ReadMemory(nub_addr_t addr, nub_size_t size, void *buf);
+ nub_size_t WriteMemory(nub_addr_t addr, nub_size_t size, const void *buf);
+
+ // Path and arg accessors
+ const char *Path() const { return m_path.c_str(); }
+ size_t ArgumentCount() const { return m_args.size(); }
+ const char *ArgumentAtIndex(size_t arg_idx) const {
+ if (arg_idx < m_args.size())
+ return m_args[arg_idx].c_str();
+ return NULL;
+ }
+
+ // Breakpoint functions
+ DNBBreakpoint *CreateBreakpoint(nub_addr_t addr, nub_size_t length,
+ bool hardware);
+ bool DisableBreakpoint(nub_addr_t addr, bool remove);
+ void DisableAllBreakpoints(bool remove);
+ bool EnableBreakpoint(nub_addr_t addr);
+ DNBBreakpointList &Breakpoints() { return m_breakpoints; }
+ const DNBBreakpointList &Breakpoints() const { return m_breakpoints; }
+
+ // Watchpoint functions
+ DNBBreakpoint *CreateWatchpoint(nub_addr_t addr, nub_size_t length,
+ uint32_t watch_type, bool hardware);
+ bool DisableWatchpoint(nub_addr_t addr, bool remove);
+ void DisableAllWatchpoints(bool remove);
+ bool EnableWatchpoint(nub_addr_t addr);
+ uint32_t GetNumSupportedHardwareWatchpoints() const;
+ DNBBreakpointList &Watchpoints() { return m_watchpoints; }
+ const DNBBreakpointList &Watchpoints() const { return m_watchpoints; }
+
+ // Exception thread functions
+ bool StartSTDIOThread();
+ static void *STDIOThread(void *arg);
+ void ExceptionMessageReceived(const MachException::Message &exceptionMessage);
+ task_t ExceptionMessageBundleComplete();
+ void SharedLibrariesUpdated();
+ nub_size_t CopyImageInfos(struct DNBExecutableImageInfo **image_infos,
+ bool only_changed);
+
+ // Profile functions
+ void SetEnableAsyncProfiling(bool enable, uint64_t internal_usec,
+ DNBProfileDataScanType scan_type);
+ bool IsProfilingEnabled() { return m_profile_enabled; }
+ useconds_t ProfileInterval() { return m_profile_interval_usec; }
+ bool StartProfileThread();
+ static void *ProfileThread(void *arg);
+ void SignalAsyncProfileData(const char *info);
+ size_t GetAsyncProfileData(char *buf, size_t buf_size);
+
+ // Accessors
+ pid_t ProcessID() const { return m_pid; }
+ bool ProcessIDIsValid() const { return m_pid > 0; }
+ pid_t SetProcessID(pid_t pid);
+ MachTask &Task() { return m_task; }
+ const MachTask &Task() const { return m_task; }
+
+ PThreadEvent &Events() { return m_events; }
+ const DNBRegisterSetInfo *GetRegisterSetInfo(nub_thread_t tid,
+ nub_size_t *num_reg_sets) const;
+ bool GetRegisterValue(nub_thread_t tid, uint32_t set, uint32_t reg,
+ DNBRegisterValue *reg_value) const;
+ bool SetRegisterValue(nub_thread_t tid, uint32_t set, uint32_t reg,
+ const DNBRegisterValue *value) const;
+ nub_bool_t SyncThreadState(nub_thread_t tid);
+ const char *ThreadGetName(nub_thread_t tid);
+ nub_state_t ThreadGetState(nub_thread_t tid);
+ ThreadInfo::QoS GetRequestedQoS(nub_thread_t tid, nub_addr_t tsd,
+ uint64_t dti_qos_class_index);
+ nub_addr_t GetPThreadT(nub_thread_t tid);
+ nub_addr_t GetDispatchQueueT(nub_thread_t tid);
+ nub_addr_t
+ GetTSDAddressForThread(nub_thread_t tid,
+ uint64_t plo_pthread_tsd_base_address_offset,
+ uint64_t plo_pthread_tsd_base_offset,
+ uint64_t plo_pthread_tsd_entry_size);
+ const char *
+ GetDeploymentInfo(const struct load_command&, uint64_t load_command_address,
+ uint32_t& major_version, uint32_t& minor_version,
+ uint32_t& patch_version);
+ bool GetMachOInformationFromMemory(uint32_t platform,
+ nub_addr_t mach_o_header_addr,
+ int wordsize,
+ struct mach_o_information &inf);
+ JSONGenerator::ObjectSP FormatDynamicLibrariesIntoJSON(
+ const std::vector<struct binary_image_information> &image_infos);
+ uint32_t GetAllLoadedBinariesViaDYLDSPI(
+ std::vector<struct binary_image_information> &image_infos);
+ JSONGenerator::ObjectSP GetLoadedDynamicLibrariesInfos(
+ nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count);
+ JSONGenerator::ObjectSP
+ GetLibrariesInfoForAddresses(nub_process_t pid,
+ std::vector<uint64_t> &macho_addresses);
+ JSONGenerator::ObjectSP GetAllLoadedLibrariesInfos(nub_process_t pid);
+ JSONGenerator::ObjectSP GetSharedCacheInfo(nub_process_t pid);
+
+ nub_size_t GetNumThreads() const;
+ nub_thread_t GetThreadAtIndex(nub_size_t thread_idx) const;
+ nub_thread_t GetCurrentThread();
+ nub_thread_t GetCurrentThreadMachPort();
+ nub_thread_t SetCurrentThread(nub_thread_t tid);
+ MachThreadList &GetThreadList() { return m_thread_list; }
+ bool GetThreadStoppedReason(nub_thread_t tid,
+ struct DNBThreadStopInfo *stop_info);
+ void DumpThreadStoppedReason(nub_thread_t tid) const;
+ const char *GetThreadInfo(nub_thread_t tid) const;
+
+ nub_thread_t GetThreadIDForMachPortNumber(thread_t mach_port_number) const;
+
+ uint32_t GetCPUType();
+ nub_state_t GetState();
+ void SetState(nub_state_t state);
+ bool IsRunning(nub_state_t state) {
+ return state == eStateRunning || IsStepping(state);
+ }
+ bool IsStepping(nub_state_t state) { return state == eStateStepping; }
+ bool CanResume(nub_state_t state) { return state == eStateStopped; }
+
+ bool GetExitStatus(int *status) {
+ if (GetState() == eStateExited) {
+ if (status)
+ *status = m_exit_status;
+ return true;
+ }
+ return false;
+ }
+ void SetExitStatus(int status) {
+ m_exit_status = status;
+ SetState(eStateExited);
+ }
+ const char *GetExitInfo() { return m_exit_info.c_str(); }
+
+ void SetExitInfo(const char *info);
+
+ uint32_t StopCount() const { return m_stop_count; }
+ void SetChildFileDescriptors(int stdin_fileno, int stdout_fileno,
+ int stderr_fileno) {
+ m_child_stdin = stdin_fileno;
+ m_child_stdout = stdout_fileno;
+ m_child_stderr = stderr_fileno;
+ }
+
+ int GetStdinFileDescriptor() const { return m_child_stdin; }
+ int GetStdoutFileDescriptor() const { return m_child_stdout; }
+ int GetStderrFileDescriptor() const { return m_child_stderr; }
+ void AppendSTDOUT(char *s, size_t len);
+ size_t GetAvailableSTDOUT(char *buf, size_t buf_size);
+ size_t GetAvailableSTDERR(char *buf, size_t buf_size);
+ void CloseChildFileDescriptors() {
+ if (m_child_stdin >= 0) {
+ ::close(m_child_stdin);
+ m_child_stdin = -1;
+ }
+ if (m_child_stdout >= 0) {
+ ::close(m_child_stdout);
+ m_child_stdout = -1;
+ }
+ if (m_child_stderr >= 0) {
+ ::close(m_child_stderr);
+ m_child_stderr = -1;
+ }
+ }
+
+ void CalculateBoardStatus();
+
+ bool ProcessUsingBackBoard();
+
+ bool ProcessUsingFrontBoard();
+
+ Genealogy::ThreadActivitySP GetGenealogyInfoForThread(nub_thread_t tid,
+ bool &timed_out);
+
+ Genealogy::ProcessExecutableInfoSP GetGenealogyImageInfo(size_t idx);
+
+ DNBProfileDataScanType GetProfileScanType() { return m_profile_scan_type; }
+
+private:
+ enum {
+ eMachProcessFlagsNone = 0,
+ eMachProcessFlagsAttached = (1 << 0),
+ eMachProcessFlagsUsingBKS = (1 << 2), // only read via ProcessUsingBackBoard()
+ eMachProcessFlagsUsingFBS = (1 << 3), // only read via ProcessUsingFrontBoard()
+ eMachProcessFlagsBoardCalculated = (1 << 4)
+ };
+ void Clear(bool detaching = false);
+ void ReplyToAllExceptions();
+ void PrivateResume();
+
+ uint32_t Flags() const { return m_flags; }
+ nub_state_t DoSIGSTOP(bool clear_bps_and_wps, bool allow_running,
+ uint32_t *thread_idx_ptr);
+
+ pid_t m_pid; // Process ID of child process
+ cpu_type_t m_cpu_type; // The CPU type of this process
+ int m_child_stdin;
+ int m_child_stdout;
+ int m_child_stderr;
+ std::string m_path; // A path to the executable if we have one
+ std::vector<std::string>
+ m_args; // The arguments with which the process was lauched
+ int m_exit_status; // The exit status for the process
+ std::string m_exit_info; // Any extra info that we may have about the exit
+ MachTask m_task; // The mach task for this process
+ uint32_t m_flags; // Process specific flags (see eMachProcessFlags enums)
+ uint32_t m_stop_count; // A count of many times have we stopped
+ pthread_t m_stdio_thread; // Thread ID for the thread that watches for child
+ // process stdio
+ PThreadMutex m_stdio_mutex; // Multithreaded protection for stdio
+ std::string m_stdout_data;
+
+ bool m_profile_enabled; // A flag to indicate if profiling is enabled
+ useconds_t m_profile_interval_usec; // If enable, the profiling interval in
+ // microseconds
+ DNBProfileDataScanType
+ m_profile_scan_type; // Indicates what needs to be profiled
+ pthread_t
+ m_profile_thread; // Thread ID for the thread that profiles the inferior
+ PThreadMutex
+ m_profile_data_mutex; // Multithreaded protection for profile info data
+ std::vector<std::string>
+ m_profile_data; // Profile data, must be protected by m_profile_data_mutex
+
+ DNBThreadResumeActions m_thread_actions; // The thread actions for the current
+ // MachProcess::Resume() call
+ MachException::Message::collection m_exception_messages; // A collection of
+ // exception messages
+ // caught when
+ // listening to the
+ // exception port
+ PThreadMutex m_exception_messages_mutex; // Multithreaded protection for
+ // m_exception_messages
+
+ MachThreadList m_thread_list; // A list of threads that is maintained/updated
+ // after each stop
+ Genealogy m_activities; // A list of activities that is updated after every
+ // stop lazily
+ nub_state_t m_state; // The state of our process
+ PThreadMutex m_state_mutex; // Multithreaded protection for m_state
+ PThreadEvent m_events; // Process related events in the child processes
+ // lifetime can be waited upon
+ PThreadEvent m_private_events; // Used to coordinate running and stopping the
+ // process without affecting m_events
+ DNBBreakpointList m_breakpoints; // Breakpoint list for this process
+ DNBBreakpointList m_watchpoints; // Watchpoint list for this process
+ DNBCallbackNameToAddress m_name_to_addr_callback;
+ void *m_name_to_addr_baton;
+ DNBCallbackCopyExecutableImageInfos m_image_infos_callback;
+ void *m_image_infos_baton;
+ std::string
+ m_bundle_id; // If we are a SB or BKS process, this will be our bundle ID.
+ int m_sent_interrupt_signo; // When we call MachProcess::Interrupt(), we want
+ // to send a single signal
+ // to the inferior and only send the signal if we aren't already stopped.
+ // If we end up sending a signal to stop the process we store it until we
+ // receive an exception with this signal. This helps us to verify we got
+ // the signal that interrupted the process. We might stop due to another
+ // reason after an interrupt signal is sent, so this helps us ensure that
+ // we don't report a spurious stop on the next resume.
+ int m_auto_resume_signo; // If we resume the process and still haven't
+ // received our interrupt signal
+ // acknownledgement, we will shortly after the next resume. We store the
+ // interrupt signal in this variable so when we get the interrupt signal
+ // as the sole reason for the process being stopped, we can auto resume
+ // the process.
+ bool m_did_exec;
+
+ void *(*m_dyld_process_info_create)(task_t task, uint64_t timestamp,
+ kern_return_t *kernelError);
+ void (*m_dyld_process_info_for_each_image)(
+ void *info, void (^callback)(uint64_t machHeaderAddress,
+ const uuid_t uuid, const char *path));
+ void (*m_dyld_process_info_release)(void *info);
+ void (*m_dyld_process_info_get_cache)(void *info, void *cacheInfo);
+ uint32_t (*m_dyld_process_info_get_platform)(void *info);
+};
+
+#endif // __MachProcess_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachProcess.mm b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachProcess.mm
new file mode 100644
index 00000000000..40facdfb5cf
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachProcess.mm
@@ -0,0 +1,4029 @@
+//===-- MachProcess.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/15/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DNB.h"
+#include "MacOSX/CFUtils.h"
+#include "SysSignal.h"
+#include <dlfcn.h>
+#include <inttypes.h>
+#include <mach-o/loader.h>
+#include <mach/mach.h>
+#include <mach/task.h>
+#include <pthread.h>
+#include <signal.h>
+#include <spawn.h>
+#include <sys/fcntl.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <uuid/uuid.h>
+
+#include <algorithm>
+#include <map>
+
+#import <Foundation/Foundation.h>
+
+#include "DNBDataRef.h"
+#include "DNBLog.h"
+#include "DNBThreadResumeActions.h"
+#include "DNBTimer.h"
+#include "MachProcess.h"
+#include "PseudoTerminal.h"
+
+#include "CFBundle.h"
+#include "CFString.h"
+
+#ifndef PLATFORM_BRIDGEOS
+#define PLATFORM_BRIDGEOS 5
+#endif
+
+#ifndef PLATFORM_MACCATALYST
+#define PLATFORM_MACCATALYST 6
+#endif
+
+#ifndef PLATFORM_IOSSIMULATOR
+#define PLATFORM_IOSSIMULATOR 7
+#endif
+
+#ifndef PLATFORM_TVOSSIMULATOR
+#define PLATFORM_TVOSSIMULATOR 8
+#endif
+
+#ifndef PLATFORM_WATCHOSSIMULATOR
+#define PLATFORM_WATCHOSSIMULATOR 9
+#endif
+
+#ifndef PLATFORM_DRIVERKIT
+#define PLATFORM_DRIVERKIT 10
+#endif
+
+#ifdef WITH_SPRINGBOARD
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <SpringBoardServices/SBSWatchdogAssertion.h>
+#include <SpringBoardServices/SpringBoardServer.h>
+
+#endif // WITH_SPRINGBOARD
+
+#if defined(WITH_SPRINGBOARD) || defined(WITH_BKS) || defined(WITH_FBS)
+// This returns a CFRetained pointer to the Bundle ID for app_bundle_path,
+// or NULL if there was some problem getting the bundle id.
+static CFStringRef CopyBundleIDForPath(const char *app_bundle_path,
+ DNBError &err_str);
+#endif
+
+#if defined(WITH_BKS) || defined(WITH_FBS)
+#import <Foundation/Foundation.h>
+static const int OPEN_APPLICATION_TIMEOUT_ERROR = 111;
+typedef void (*SetErrorFunction)(NSInteger, std::string, DNBError &);
+typedef bool (*CallOpenApplicationFunction)(NSString *bundleIDNSStr,
+ NSDictionary *options,
+ DNBError &error, pid_t *return_pid);
+// This function runs the BKSSystemService (or FBSSystemService) method
+// openApplication:options:clientPort:withResult,
+// messaging the app passed in bundleIDNSStr.
+// The function should be run inside of an NSAutoReleasePool.
+//
+// It will use the "options" dictionary passed in, and fill the error passed in
+// if there is an error.
+// If return_pid is not NULL, we'll fetch the pid that was made for the
+// bundleID.
+// If bundleIDNSStr is NULL, then the system application will be messaged.
+
+template <typename OpenFlavor, typename ErrorFlavor,
+ ErrorFlavor no_error_enum_value, SetErrorFunction error_function>
+static bool CallBoardSystemServiceOpenApplication(NSString *bundleIDNSStr,
+ NSDictionary *options,
+ DNBError &error,
+ pid_t *return_pid) {
+ // Now make our systemService:
+ OpenFlavor *system_service = [[OpenFlavor alloc] init];
+
+ if (bundleIDNSStr == nil) {
+ bundleIDNSStr = [system_service systemApplicationBundleIdentifier];
+ if (bundleIDNSStr == nil) {
+ // Okay, no system app...
+ error.SetErrorString("No system application to message.");
+ return false;
+ }
+ }
+
+ mach_port_t client_port = [system_service createClientPort];
+ __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
+ __block ErrorFlavor open_app_error = no_error_enum_value;
+ __block std::string open_app_error_string;
+ bool wants_pid = (return_pid != NULL);
+ __block pid_t pid_in_block;
+
+ const char *cstr = [bundleIDNSStr UTF8String];
+ if (!cstr)
+ cstr = "<Unknown Bundle ID>";
+
+ NSString *description = [options description];
+ DNBLog("About to launch process for bundle ID: %s - options:\n%s", cstr,
+ [description UTF8String]);
+ [system_service
+ openApplication:bundleIDNSStr
+ options:options
+ clientPort:client_port
+ withResult:^(NSError *bks_error) {
+ // The system service will cleanup the client port we created for
+ // us.
+ if (bks_error)
+ open_app_error = (ErrorFlavor)[bks_error code];
+
+ if (open_app_error == no_error_enum_value) {
+ if (wants_pid) {
+ pid_in_block =
+ [system_service pidForApplication:bundleIDNSStr];
+ DNBLog(
+ "In completion handler, got pid for bundle id, pid: %d.",
+ pid_in_block);
+ DNBLogThreadedIf(
+ LOG_PROCESS,
+ "In completion handler, got pid for bundle id, pid: %d.",
+ pid_in_block);
+ } else
+ DNBLogThreadedIf(LOG_PROCESS,
+ "In completion handler: success.");
+ } else {
+ const char *error_str =
+ [(NSString *)[bks_error localizedDescription] UTF8String];
+ if (error_str) {
+ open_app_error_string = error_str;
+ }
+ DNBLogThreadedIf(LOG_PROCESS, "In completion handler for send "
+ "event, got error \"%s\"(%ld).",
+ error_str ? error_str : "<unknown error>",
+ open_app_error);
+ // REMOVE ME
+ DNBLogError("In completion handler for send event, got error "
+ "\"%s\"(%ld).",
+ error_str ? error_str : "<unknown error>",
+ open_app_error);
+ }
+
+ [system_service release];
+ dispatch_semaphore_signal(semaphore);
+ }
+
+ ];
+
+ const uint32_t timeout_secs = 30;
+
+ dispatch_time_t timeout =
+ dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC);
+
+ long success = dispatch_semaphore_wait(semaphore, timeout) == 0;
+
+ dispatch_release(semaphore);
+
+ if (!success) {
+ DNBLogError("timed out trying to send openApplication to %s.", cstr);
+ error.SetError(OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic);
+ error.SetErrorString("timed out trying to launch app");
+ } else if (open_app_error != no_error_enum_value) {
+ error_function(open_app_error, open_app_error_string, error);
+ DNBLogError("unable to launch the application with CFBundleIdentifier '%s' "
+ "bks_error = %u",
+ cstr, open_app_error);
+ success = false;
+ } else if (wants_pid) {
+ *return_pid = pid_in_block;
+ DNBLogThreadedIf(
+ LOG_PROCESS,
+ "Out of completion handler, pid from block %d and passing out: %d",
+ pid_in_block, *return_pid);
+ }
+
+ return success;
+}
+#endif
+
+#if defined(WITH_BKS) || defined(WITH_FBS)
+static void SplitEventData(const char *data, std::vector<std::string> &elements)
+{
+ elements.clear();
+ if (!data)
+ return;
+
+ const char *start = data;
+
+ while (*start != '\0') {
+ const char *token = strchr(start, ':');
+ if (!token) {
+ elements.push_back(std::string(start));
+ return;
+ }
+ if (token != start)
+ elements.push_back(std::string(start, token - start));
+ start = ++token;
+ }
+}
+#endif
+
+#ifdef WITH_BKS
+#import <Foundation/Foundation.h>
+extern "C" {
+#import <BackBoardServices/BKSOpenApplicationConstants_Private.h>
+#import <BackBoardServices/BKSSystemService_LaunchServices.h>
+#import <BackBoardServices/BackBoardServices.h>
+}
+
+static bool IsBKSProcess(nub_process_t pid) {
+ BKSApplicationStateMonitor *state_monitor =
+ [[BKSApplicationStateMonitor alloc] init];
+ BKSApplicationState app_state =
+ [state_monitor mostElevatedApplicationStateForPID:pid];
+ return app_state != BKSApplicationStateUnknown;
+}
+
+static void SetBKSError(NSInteger error_code,
+ std::string error_description,
+ DNBError &error) {
+ error.SetError(error_code, DNBError::BackBoard);
+ NSString *err_nsstr = ::BKSOpenApplicationErrorCodeToString(
+ (BKSOpenApplicationErrorCode)error_code);
+ std::string err_str = "unknown BKS error";
+ if (error_description.empty() == false) {
+ err_str = error_description;
+ } else if (err_nsstr != nullptr) {
+ err_str = [err_nsstr UTF8String];
+ }
+ error.SetErrorString(err_str.c_str());
+}
+
+static bool BKSAddEventDataToOptions(NSMutableDictionary *options,
+ const char *event_data,
+ DNBError &option_error) {
+ std::vector<std::string> values;
+ SplitEventData(event_data, values);
+ bool found_one = false;
+ for (std::string value : values)
+ {
+ if (value.compare("BackgroundContentFetching") == 0) {
+ DNBLog("Setting ActivateForEvent key in options dictionary.");
+ NSDictionary *event_details = [NSDictionary dictionary];
+ NSDictionary *event_dictionary = [NSDictionary
+ dictionaryWithObject:event_details
+ forKey:
+ BKSActivateForEventOptionTypeBackgroundContentFetching];
+ [options setObject:event_dictionary
+ forKey:BKSOpenApplicationOptionKeyActivateForEvent];
+ found_one = true;
+ } else if (value.compare("ActivateSuspended") == 0) {
+ DNBLog("Setting ActivateSuspended key in options dictionary.");
+ [options setObject:@YES forKey: BKSOpenApplicationOptionKeyActivateSuspended];
+ found_one = true;
+ } else {
+ DNBLogError("Unrecognized event type: %s. Ignoring.", value.c_str());
+ option_error.SetErrorString("Unrecognized event data");
+ }
+ }
+ return found_one;
+}
+
+static NSMutableDictionary *BKSCreateOptionsDictionary(
+ const char *app_bundle_path, NSMutableArray *launch_argv,
+ NSMutableDictionary *launch_envp, NSString *stdio_path, bool disable_aslr,
+ const char *event_data) {
+ NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
+ if (launch_argv != nil)
+ [debug_options setObject:launch_argv forKey:BKSDebugOptionKeyArguments];
+ if (launch_envp != nil)
+ [debug_options setObject:launch_envp forKey:BKSDebugOptionKeyEnvironment];
+
+ [debug_options setObject:stdio_path forKey:BKSDebugOptionKeyStandardOutPath];
+ [debug_options setObject:stdio_path
+ forKey:BKSDebugOptionKeyStandardErrorPath];
+ [debug_options setObject:[NSNumber numberWithBool:YES]
+ forKey:BKSDebugOptionKeyWaitForDebugger];
+ if (disable_aslr)
+ [debug_options setObject:[NSNumber numberWithBool:YES]
+ forKey:BKSDebugOptionKeyDisableASLR];
+
+ // That will go in the overall dictionary:
+
+ NSMutableDictionary *options = [NSMutableDictionary dictionary];
+ [options setObject:debug_options
+ forKey:BKSOpenApplicationOptionKeyDebuggingOptions];
+ // And there are some other options at the top level in this dictionary:
+ [options setObject:[NSNumber numberWithBool:YES]
+ forKey:BKSOpenApplicationOptionKeyUnlockDevice];
+
+ DNBError error;
+ BKSAddEventDataToOptions(options, event_data, error);
+
+ return options;
+}
+
+static CallOpenApplicationFunction BKSCallOpenApplicationFunction =
+ CallBoardSystemServiceOpenApplication<
+ BKSSystemService, BKSOpenApplicationErrorCode,
+ BKSOpenApplicationErrorCodeNone, SetBKSError>;
+#endif // WITH_BKS
+
+#ifdef WITH_FBS
+#import <Foundation/Foundation.h>
+extern "C" {
+#import <FrontBoardServices/FBSOpenApplicationConstants_Private.h>
+#import <FrontBoardServices/FBSSystemService_LaunchServices.h>
+#import <FrontBoardServices/FrontBoardServices.h>
+#import <MobileCoreServices/LSResourceProxy.h>
+#import <MobileCoreServices/MobileCoreServices.h>
+}
+
+#ifdef WITH_BKS
+static bool IsFBSProcess(nub_process_t pid) {
+ BKSApplicationStateMonitor *state_monitor =
+ [[BKSApplicationStateMonitor alloc] init];
+ BKSApplicationState app_state =
+ [state_monitor mostElevatedApplicationStateForPID:pid];
+ return app_state != BKSApplicationStateUnknown;
+}
+#else
+static bool IsFBSProcess(nub_process_t pid) {
+ // FIXME: What is the FBS equivalent of BKSApplicationStateMonitor
+ return false;
+}
+#endif
+
+static void SetFBSError(NSInteger error_code,
+ std::string error_description,
+ DNBError &error) {
+ error.SetError((DNBError::ValueType)error_code, DNBError::FrontBoard);
+ NSString *err_nsstr = ::FBSOpenApplicationErrorCodeToString(
+ (FBSOpenApplicationErrorCode)error_code);
+ std::string err_str = "unknown FBS error";
+ if (error_description.empty() == false) {
+ err_str = error_description;
+ } else if (err_nsstr != nullptr) {
+ err_str = [err_nsstr UTF8String];
+ }
+ error.SetErrorString(err_str.c_str());
+}
+
+static bool FBSAddEventDataToOptions(NSMutableDictionary *options,
+ const char *event_data,
+ DNBError &option_error) {
+ std::vector<std::string> values;
+ SplitEventData(event_data, values);
+ bool found_one = false;
+ for (std::string value : values)
+ {
+ if (value.compare("BackgroundContentFetching") == 0) {
+ DNBLog("Setting ActivateForEvent key in options dictionary.");
+ NSDictionary *event_details = [NSDictionary dictionary];
+ NSDictionary *event_dictionary = [NSDictionary
+ dictionaryWithObject:event_details
+ forKey:
+ FBSActivateForEventOptionTypeBackgroundContentFetching];
+ [options setObject:event_dictionary
+ forKey:FBSOpenApplicationOptionKeyActivateForEvent];
+ found_one = true;
+ } else if (value.compare("ActivateSuspended") == 0) {
+ DNBLog("Setting ActivateSuspended key in options dictionary.");
+ [options setObject:@YES forKey: FBSOpenApplicationOptionKeyActivateSuspended];
+ found_one = true;
+ } else {
+ DNBLogError("Unrecognized event type: %s. Ignoring.", value.c_str());
+ option_error.SetErrorString("Unrecognized event data.");
+ }
+ }
+ return found_one;
+}
+
+static NSMutableDictionary *
+FBSCreateOptionsDictionary(const char *app_bundle_path,
+ NSMutableArray *launch_argv,
+ NSDictionary *launch_envp, NSString *stdio_path,
+ bool disable_aslr, const char *event_data) {
+ NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
+
+ if (launch_argv != nil)
+ [debug_options setObject:launch_argv forKey:FBSDebugOptionKeyArguments];
+ if (launch_envp != nil)
+ [debug_options setObject:launch_envp forKey:FBSDebugOptionKeyEnvironment];
+
+ [debug_options setObject:stdio_path forKey:FBSDebugOptionKeyStandardOutPath];
+ [debug_options setObject:stdio_path
+ forKey:FBSDebugOptionKeyStandardErrorPath];
+ [debug_options setObject:[NSNumber numberWithBool:YES]
+ forKey:FBSDebugOptionKeyWaitForDebugger];
+ if (disable_aslr)
+ [debug_options setObject:[NSNumber numberWithBool:YES]
+ forKey:FBSDebugOptionKeyDisableASLR];
+
+ // That will go in the overall dictionary:
+
+ NSMutableDictionary *options = [NSMutableDictionary dictionary];
+ [options setObject:debug_options
+ forKey:FBSOpenApplicationOptionKeyDebuggingOptions];
+ // And there are some other options at the top level in this dictionary:
+ [options setObject:[NSNumber numberWithBool:YES]
+ forKey:FBSOpenApplicationOptionKeyUnlockDevice];
+
+ // We have to get the "sequence ID & UUID" for this app bundle path and send
+ // them to FBS:
+
+ NSURL *app_bundle_url =
+ [NSURL fileURLWithPath:[NSString stringWithUTF8String:app_bundle_path]
+ isDirectory:YES];
+ LSApplicationProxy *app_proxy =
+ [LSApplicationProxy applicationProxyForBundleURL:app_bundle_url];
+ if (app_proxy) {
+ DNBLog("Sending AppProxy info: sequence no: %lu, GUID: %s.",
+ app_proxy.sequenceNumber,
+ [app_proxy.cacheGUID.UUIDString UTF8String]);
+ [options
+ setObject:[NSNumber numberWithUnsignedInteger:app_proxy.sequenceNumber]
+ forKey:FBSOpenApplicationOptionKeyLSSequenceNumber];
+ [options setObject:app_proxy.cacheGUID.UUIDString
+ forKey:FBSOpenApplicationOptionKeyLSCacheGUID];
+ }
+
+ DNBError error;
+ FBSAddEventDataToOptions(options, event_data, error);
+
+ return options;
+}
+static CallOpenApplicationFunction FBSCallOpenApplicationFunction =
+ CallBoardSystemServiceOpenApplication<
+ FBSSystemService, FBSOpenApplicationErrorCode,
+ FBSOpenApplicationErrorCodeNone, SetFBSError>;
+#endif // WITH_FBS
+
+#if 0
+#define DEBUG_LOG(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define DEBUG_LOG(fmt, ...)
+#endif
+
+#ifndef MACH_PROCESS_USE_POSIX_SPAWN
+#define MACH_PROCESS_USE_POSIX_SPAWN 1
+#endif
+
+#ifndef _POSIX_SPAWN_DISABLE_ASLR
+#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
+#endif
+
+MachProcess::MachProcess()
+ : m_pid(0), m_cpu_type(0), m_child_stdin(-1), m_child_stdout(-1),
+ m_child_stderr(-1), m_path(), m_args(), m_task(this),
+ m_flags(eMachProcessFlagsNone), m_stdio_thread(0),
+ m_stdio_mutex(PTHREAD_MUTEX_RECURSIVE), m_stdout_data(),
+ m_profile_enabled(false), m_profile_interval_usec(0), m_profile_thread(0),
+ m_profile_data_mutex(PTHREAD_MUTEX_RECURSIVE), m_profile_data(),
+ m_thread_actions(), m_exception_messages(),
+ m_exception_messages_mutex(PTHREAD_MUTEX_RECURSIVE), m_thread_list(),
+ m_activities(), m_state(eStateUnloaded),
+ m_state_mutex(PTHREAD_MUTEX_RECURSIVE), m_events(0, kAllEventsMask),
+ m_private_events(0, kAllEventsMask), m_breakpoints(), m_watchpoints(),
+ m_name_to_addr_callback(NULL), m_name_to_addr_baton(NULL),
+ m_image_infos_callback(NULL), m_image_infos_baton(NULL),
+ m_sent_interrupt_signo(0), m_auto_resume_signo(0), m_did_exec(false),
+ m_dyld_process_info_create(nullptr),
+ m_dyld_process_info_for_each_image(nullptr),
+ m_dyld_process_info_release(nullptr),
+ m_dyld_process_info_get_cache(nullptr) {
+ m_dyld_process_info_create =
+ (void *(*)(task_t task, uint64_t timestamp, kern_return_t * kernelError))
+ dlsym(RTLD_DEFAULT, "_dyld_process_info_create");
+ m_dyld_process_info_for_each_image =
+ (void (*)(void *info, void (^)(uint64_t machHeaderAddress,
+ const uuid_t uuid, const char *path)))
+ dlsym(RTLD_DEFAULT, "_dyld_process_info_for_each_image");
+ m_dyld_process_info_release =
+ (void (*)(void *info))dlsym(RTLD_DEFAULT, "_dyld_process_info_release");
+ m_dyld_process_info_get_cache = (void (*)(void *info, void *cacheInfo))dlsym(
+ RTLD_DEFAULT, "_dyld_process_info_get_cache");
+ m_dyld_process_info_get_platform = (uint32_t (*)(void *info))dlsym(
+ RTLD_DEFAULT, "_dyld_process_info_get_platform");
+
+ DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__);
+}
+
+MachProcess::~MachProcess() {
+ DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__);
+ Clear();
+}
+
+pid_t MachProcess::SetProcessID(pid_t pid) {
+ // Free any previous process specific data or resources
+ Clear();
+ // Set the current PID appropriately
+ if (pid == 0)
+ m_pid = ::getpid();
+ else
+ m_pid = pid;
+ return m_pid; // Return actually PID in case a zero pid was passed in
+}
+
+nub_state_t MachProcess::GetState() {
+ // If any other threads access this we will need a mutex for it
+ PTHREAD_MUTEX_LOCKER(locker, m_state_mutex);
+ return m_state;
+}
+
+const char *MachProcess::ThreadGetName(nub_thread_t tid) {
+ return m_thread_list.GetName(tid);
+}
+
+nub_state_t MachProcess::ThreadGetState(nub_thread_t tid) {
+ return m_thread_list.GetState(tid);
+}
+
+nub_size_t MachProcess::GetNumThreads() const {
+ return m_thread_list.NumThreads();
+}
+
+nub_thread_t MachProcess::GetThreadAtIndex(nub_size_t thread_idx) const {
+ return m_thread_list.ThreadIDAtIndex(thread_idx);
+}
+
+nub_thread_t
+MachProcess::GetThreadIDForMachPortNumber(thread_t mach_port_number) const {
+ return m_thread_list.GetThreadIDByMachPortNumber(mach_port_number);
+}
+
+nub_bool_t MachProcess::SyncThreadState(nub_thread_t tid) {
+ MachThreadSP thread_sp(m_thread_list.GetThreadByID(tid));
+ if (!thread_sp)
+ return false;
+ kern_return_t kret = ::thread_abort_safely(thread_sp->MachPortNumber());
+ DNBLogThreadedIf(LOG_THREAD, "thread = 0x%8.8" PRIx32
+ " calling thread_abort_safely (tid) => %u "
+ "(GetGPRState() for stop_count = %u)",
+ thread_sp->MachPortNumber(), kret,
+ thread_sp->Process()->StopCount());
+
+ if (kret == KERN_SUCCESS)
+ return true;
+ else
+ return false;
+}
+
+ThreadInfo::QoS MachProcess::GetRequestedQoS(nub_thread_t tid, nub_addr_t tsd,
+ uint64_t dti_qos_class_index) {
+ return m_thread_list.GetRequestedQoS(tid, tsd, dti_qos_class_index);
+}
+
+nub_addr_t MachProcess::GetPThreadT(nub_thread_t tid) {
+ return m_thread_list.GetPThreadT(tid);
+}
+
+nub_addr_t MachProcess::GetDispatchQueueT(nub_thread_t tid) {
+ return m_thread_list.GetDispatchQueueT(tid);
+}
+
+nub_addr_t MachProcess::GetTSDAddressForThread(
+ nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset,
+ uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size) {
+ return m_thread_list.GetTSDAddressForThread(
+ tid, plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset,
+ plo_pthread_tsd_entry_size);
+}
+
+
+const char *MachProcess::GetDeploymentInfo(const struct load_command& lc,
+ uint64_t load_command_address,
+ uint32_t& major_version,
+ uint32_t& minor_version,
+ uint32_t& patch_version) {
+ uint32_t cmd = lc.cmd & ~LC_REQ_DYLD;
+ bool lc_cmd_known =
+ cmd == LC_VERSION_MIN_IPHONEOS || cmd == LC_VERSION_MIN_MACOSX ||
+ cmd == LC_VERSION_MIN_TVOS || cmd == LC_VERSION_MIN_WATCHOS;
+
+ if (lc_cmd_known) {
+ struct version_min_command vers_cmd;
+ if (ReadMemory(load_command_address, sizeof(struct version_min_command),
+ &vers_cmd) != sizeof(struct version_min_command)) {
+ return nullptr;
+ }
+ major_version = vers_cmd.sdk >> 16;
+ minor_version = (vers_cmd.sdk >> 8) & 0xffu;
+ patch_version = vers_cmd.sdk & 0xffu;
+
+ switch (cmd) {
+ case LC_VERSION_MIN_IPHONEOS:
+ return "ios";
+ case LC_VERSION_MIN_MACOSX:
+ return "macosx";
+ case LC_VERSION_MIN_TVOS:
+ return "tvos";
+ case LC_VERSION_MIN_WATCHOS:
+ return "watchos";
+ default:
+ return nullptr;
+ }
+ }
+#if defined (LC_BUILD_VERSION)
+ if (cmd == LC_BUILD_VERSION) {
+ struct build_version_command build_vers;
+ if (ReadMemory(load_command_address, sizeof(struct build_version_command),
+ &build_vers) != sizeof(struct build_version_command)) {
+ return nullptr;
+ }
+ major_version = build_vers.sdk >> 16;;
+ minor_version = (build_vers.sdk >> 8) & 0xffu;
+ patch_version = build_vers.sdk & 0xffu;
+
+ switch (build_vers.platform) {
+ case PLATFORM_MACOS:
+ return "macosx";
+ case PLATFORM_MACCATALYST:
+ return "maccatalyst";
+ case PLATFORM_IOS:
+ case PLATFORM_IOSSIMULATOR:
+ return "ios";
+ case PLATFORM_TVOS:
+ case PLATFORM_TVOSSIMULATOR:
+ return "tvos";
+ case PLATFORM_WATCHOS:
+ case PLATFORM_WATCHOSSIMULATOR:
+ return "watchos";
+ case PLATFORM_BRIDGEOS:
+ return "bridgeos";
+ case PLATFORM_DRIVERKIT:
+ return "driverkit";
+ }
+ }
+#endif
+ return nullptr;
+}
+
+// Given an address, read the mach-o header and load commands out of memory to
+// fill in
+// the mach_o_information "inf" object.
+//
+// Returns false if there was an error in reading this mach-o file header/load
+// commands.
+
+bool MachProcess::GetMachOInformationFromMemory(
+ uint32_t dyld_platform, nub_addr_t mach_o_header_addr, int wordsize,
+ struct mach_o_information &inf) {
+ uint64_t load_cmds_p;
+ if (wordsize == 4) {
+ struct mach_header header;
+ if (ReadMemory(mach_o_header_addr, sizeof(struct mach_header), &header) !=
+ sizeof(struct mach_header)) {
+ return false;
+ }
+ load_cmds_p = mach_o_header_addr + sizeof(struct mach_header);
+ inf.mach_header.magic = header.magic;
+ inf.mach_header.cputype = header.cputype;
+ // high byte of cpusubtype is used for "capability bits", v.
+ // CPU_SUBTYPE_MASK, CPU_SUBTYPE_LIB64 in machine.h
+ inf.mach_header.cpusubtype = header.cpusubtype & 0x00ffffff;
+ inf.mach_header.filetype = header.filetype;
+ inf.mach_header.ncmds = header.ncmds;
+ inf.mach_header.sizeofcmds = header.sizeofcmds;
+ inf.mach_header.flags = header.flags;
+ } else {
+ struct mach_header_64 header;
+ if (ReadMemory(mach_o_header_addr, sizeof(struct mach_header_64),
+ &header) != sizeof(struct mach_header_64)) {
+ return false;
+ }
+ load_cmds_p = mach_o_header_addr + sizeof(struct mach_header_64);
+ inf.mach_header.magic = header.magic;
+ inf.mach_header.cputype = header.cputype;
+ // high byte of cpusubtype is used for "capability bits", v.
+ // CPU_SUBTYPE_MASK, CPU_SUBTYPE_LIB64 in machine.h
+ inf.mach_header.cpusubtype = header.cpusubtype & 0x00ffffff;
+ inf.mach_header.filetype = header.filetype;
+ inf.mach_header.ncmds = header.ncmds;
+ inf.mach_header.sizeofcmds = header.sizeofcmds;
+ inf.mach_header.flags = header.flags;
+ }
+ for (uint32_t j = 0; j < inf.mach_header.ncmds; j++) {
+ struct load_command lc;
+ if (ReadMemory(load_cmds_p, sizeof(struct load_command), &lc) !=
+ sizeof(struct load_command)) {
+ return false;
+ }
+ if (lc.cmd == LC_SEGMENT) {
+ struct segment_command seg;
+ if (ReadMemory(load_cmds_p, sizeof(struct segment_command), &seg) !=
+ sizeof(struct segment_command)) {
+ return false;
+ }
+ struct mach_o_segment this_seg;
+ char name[17];
+ ::memset(name, 0, sizeof(name));
+ memcpy(name, seg.segname, sizeof(seg.segname));
+ this_seg.name = name;
+ this_seg.vmaddr = seg.vmaddr;
+ this_seg.vmsize = seg.vmsize;
+ this_seg.fileoff = seg.fileoff;
+ this_seg.filesize = seg.filesize;
+ this_seg.maxprot = seg.maxprot;
+ this_seg.initprot = seg.initprot;
+ this_seg.nsects = seg.nsects;
+ this_seg.flags = seg.flags;
+ inf.segments.push_back(this_seg);
+ }
+ if (lc.cmd == LC_SEGMENT_64) {
+ struct segment_command_64 seg;
+ if (ReadMemory(load_cmds_p, sizeof(struct segment_command_64), &seg) !=
+ sizeof(struct segment_command_64)) {
+ return false;
+ }
+ struct mach_o_segment this_seg;
+ char name[17];
+ ::memset(name, 0, sizeof(name));
+ memcpy(name, seg.segname, sizeof(seg.segname));
+ this_seg.name = name;
+ this_seg.vmaddr = seg.vmaddr;
+ this_seg.vmsize = seg.vmsize;
+ this_seg.fileoff = seg.fileoff;
+ this_seg.filesize = seg.filesize;
+ this_seg.maxprot = seg.maxprot;
+ this_seg.initprot = seg.initprot;
+ this_seg.nsects = seg.nsects;
+ this_seg.flags = seg.flags;
+ inf.segments.push_back(this_seg);
+ }
+ if (lc.cmd == LC_UUID) {
+ struct uuid_command uuidcmd;
+ if (ReadMemory(load_cmds_p, sizeof(struct uuid_command), &uuidcmd) ==
+ sizeof(struct uuid_command))
+ uuid_copy(inf.uuid, uuidcmd.uuid);
+ }
+
+ uint32_t major_version, minor_version, patch_version;
+ if (const char *lc_platform = GetDeploymentInfo(
+ lc, load_cmds_p, major_version, minor_version, patch_version)) {
+ // macCatalyst support.
+ //
+ // This handles two special cases:
+ //
+ // 1. Frameworks that have both a PLATFORM_MACOS and a
+ // PLATFORM_MACCATALYST load command. Make sure to select
+ // the requested one.
+ //
+ // 2. The xctest binary is a pure macOS binary but is launched
+ // with DYLD_FORCE_PLATFORM=6.
+ if (dyld_platform == PLATFORM_MACCATALYST &&
+ inf.mach_header.filetype == MH_EXECUTE &&
+ inf.min_version_os_name.empty() &&
+ (strcmp("macosx", lc_platform) == 0)) {
+ // DYLD says this *is* a macCatalyst process. If we haven't
+ // parsed any load commands, transform a macOS load command
+ // into a generic macCatalyst load command. It will be
+ // overwritten by a more specific one if there is one. This
+ // is only done for the main executable. It is perfectly fine
+ // for a macCatalyst binary to link against a macOS-only framework.
+ inf.min_version_os_name = "maccatalyst";
+ inf.min_version_os_version = GetMacCatalystVersionString();
+ } else if (dyld_platform != PLATFORM_MACCATALYST &&
+ inf.min_version_os_name == "macosx") {
+ // This is a binary with both PLATFORM_MACOS and
+ // PLATFORM_MACCATALYST load commands and the process is not
+ // running as PLATFORM_MACCATALYST. Stick with the
+ // "macosx" load command that we've already processed,
+ // ignore this one, which is presumed to be a
+ // PLATFORM_MACCATALYST one.
+ } else {
+ inf.min_version_os_name = lc_platform;
+ inf.min_version_os_version = "";
+ inf.min_version_os_version += std::to_string(major_version);
+ inf.min_version_os_version += ".";
+ inf.min_version_os_version += std::to_string(minor_version);
+ if (patch_version != 0) {
+ inf.min_version_os_version += ".";
+ inf.min_version_os_version += std::to_string(patch_version);
+ }
+ }
+ }
+
+ load_cmds_p += lc.cmdsize;
+ }
+ return true;
+}
+
+// Given completely filled in array of binary_image_information structures,
+// create a JSONGenerator object
+// with all the details we want to send to lldb.
+JSONGenerator::ObjectSP MachProcess::FormatDynamicLibrariesIntoJSON(
+ const std::vector<struct binary_image_information> &image_infos) {
+
+ JSONGenerator::ArraySP image_infos_array_sp(new JSONGenerator::Array());
+
+ const size_t image_count = image_infos.size();
+
+ for (size_t i = 0; i < image_count; i++) {
+ JSONGenerator::DictionarySP image_info_dict_sp(
+ new JSONGenerator::Dictionary());
+ image_info_dict_sp->AddIntegerItem("load_address",
+ image_infos[i].load_address);
+ image_info_dict_sp->AddIntegerItem("mod_date", image_infos[i].mod_date);
+ image_info_dict_sp->AddStringItem("pathname", image_infos[i].filename);
+
+ uuid_string_t uuidstr;
+ uuid_unparse_upper(image_infos[i].macho_info.uuid, uuidstr);
+ image_info_dict_sp->AddStringItem("uuid", uuidstr);
+
+ if (!image_infos[i].macho_info.min_version_os_name.empty() &&
+ !image_infos[i].macho_info.min_version_os_version.empty()) {
+ image_info_dict_sp->AddStringItem(
+ "min_version_os_name", image_infos[i].macho_info.min_version_os_name);
+ image_info_dict_sp->AddStringItem(
+ "min_version_os_sdk",
+ image_infos[i].macho_info.min_version_os_version);
+ }
+
+ JSONGenerator::DictionarySP mach_header_dict_sp(
+ new JSONGenerator::Dictionary());
+ mach_header_dict_sp->AddIntegerItem(
+ "magic", image_infos[i].macho_info.mach_header.magic);
+ mach_header_dict_sp->AddIntegerItem(
+ "cputype", (uint32_t)image_infos[i].macho_info.mach_header.cputype);
+ mach_header_dict_sp->AddIntegerItem(
+ "cpusubtype",
+ (uint32_t)image_infos[i].macho_info.mach_header.cpusubtype);
+ mach_header_dict_sp->AddIntegerItem(
+ "filetype", image_infos[i].macho_info.mach_header.filetype);
+ mach_header_dict_sp->AddIntegerItem ("flags",
+ image_infos[i].macho_info.mach_header.flags);
+
+ // DynamicLoaderMacOSX doesn't currently need these fields, so
+ // don't send them.
+ // mach_header_dict_sp->AddIntegerItem ("ncmds",
+ // image_infos[i].macho_info.mach_header.ncmds);
+ // mach_header_dict_sp->AddIntegerItem ("sizeofcmds",
+ // image_infos[i].macho_info.mach_header.sizeofcmds);
+ image_info_dict_sp->AddItem("mach_header", mach_header_dict_sp);
+
+ JSONGenerator::ArraySP segments_sp(new JSONGenerator::Array());
+ for (size_t j = 0; j < image_infos[i].macho_info.segments.size(); j++) {
+ JSONGenerator::DictionarySP segment_sp(new JSONGenerator::Dictionary());
+ segment_sp->AddStringItem("name",
+ image_infos[i].macho_info.segments[j].name);
+ segment_sp->AddIntegerItem("vmaddr",
+ image_infos[i].macho_info.segments[j].vmaddr);
+ segment_sp->AddIntegerItem("vmsize",
+ image_infos[i].macho_info.segments[j].vmsize);
+ segment_sp->AddIntegerItem("fileoff",
+ image_infos[i].macho_info.segments[j].fileoff);
+ segment_sp->AddIntegerItem(
+ "filesize", image_infos[i].macho_info.segments[j].filesize);
+ segment_sp->AddIntegerItem("maxprot",
+ image_infos[i].macho_info.segments[j].maxprot);
+
+ // DynamicLoaderMacOSX doesn't currently need these fields,
+ // so don't send them.
+ // segment_sp->AddIntegerItem ("initprot",
+ // image_infos[i].macho_info.segments[j].initprot);
+ // segment_sp->AddIntegerItem ("nsects",
+ // image_infos[i].macho_info.segments[j].nsects);
+ // segment_sp->AddIntegerItem ("flags",
+ // image_infos[i].macho_info.segments[j].flags);
+ segments_sp->AddItem(segment_sp);
+ }
+ image_info_dict_sp->AddItem("segments", segments_sp);
+
+ image_infos_array_sp->AddItem(image_info_dict_sp);
+ }
+
+ JSONGenerator::DictionarySP reply_sp(new JSONGenerator::Dictionary());
+ ;
+ reply_sp->AddItem("images", image_infos_array_sp);
+
+ return reply_sp;
+}
+
+// Get the shared library information using the old (pre-macOS 10.12, pre-iOS
+// 10, pre-tvOS 10, pre-watchOS 3)
+// code path. We'll be given the address of an array of structures in the form
+// {void* load_addr, void* mod_date, void* pathname}
+//
+// In macOS 10.12 etc and newer, we'll use SPI calls into dyld to gather this
+// information.
+JSONGenerator::ObjectSP MachProcess::GetLoadedDynamicLibrariesInfos(
+ nub_process_t pid, nub_addr_t image_list_address, nub_addr_t image_count) {
+ JSONGenerator::DictionarySP reply_sp;
+
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
+ struct kinfo_proc processInfo;
+ size_t bufsize = sizeof(processInfo);
+ if (sysctl(mib, (unsigned)(sizeof(mib) / sizeof(int)), &processInfo, &bufsize,
+ NULL, 0) == 0 &&
+ bufsize > 0) {
+ uint32_t pointer_size = 4;
+ if (processInfo.kp_proc.p_flag & P_LP64)
+ pointer_size = 8;
+
+ std::vector<struct binary_image_information> image_infos;
+ size_t image_infos_size = image_count * 3 * pointer_size;
+
+ uint8_t *image_info_buf = (uint8_t *)malloc(image_infos_size);
+ if (image_info_buf == NULL) {
+ return reply_sp;
+ }
+ if (ReadMemory(image_list_address, image_infos_size, image_info_buf) !=
+ image_infos_size) {
+ return reply_sp;
+ }
+
+ //// First the image_infos array with (load addr, pathname, mod date)
+ ///tuples
+
+ for (size_t i = 0; i < image_count; i++) {
+ struct binary_image_information info;
+ nub_addr_t pathname_address;
+ if (pointer_size == 4) {
+ uint32_t load_address_32;
+ uint32_t pathname_address_32;
+ uint32_t mod_date_32;
+ ::memcpy(&load_address_32, image_info_buf + (i * 3 * pointer_size), 4);
+ ::memcpy(&pathname_address_32,
+ image_info_buf + (i * 3 * pointer_size) + pointer_size, 4);
+ ::memcpy(&mod_date_32, image_info_buf + (i * 3 * pointer_size) +
+ pointer_size + pointer_size,
+ 4);
+ info.load_address = load_address_32;
+ info.mod_date = mod_date_32;
+ pathname_address = pathname_address_32;
+ } else {
+ uint64_t load_address_64;
+ uint64_t pathname_address_64;
+ uint64_t mod_date_64;
+ ::memcpy(&load_address_64, image_info_buf + (i * 3 * pointer_size), 8);
+ ::memcpy(&pathname_address_64,
+ image_info_buf + (i * 3 * pointer_size) + pointer_size, 8);
+ ::memcpy(&mod_date_64, image_info_buf + (i * 3 * pointer_size) +
+ pointer_size + pointer_size,
+ 8);
+ info.load_address = load_address_64;
+ info.mod_date = mod_date_64;
+ pathname_address = pathname_address_64;
+ }
+ char strbuf[17];
+ info.filename = "";
+ uint64_t pathname_ptr = pathname_address;
+ bool still_reading = true;
+ while (still_reading &&
+ ReadMemory(pathname_ptr, sizeof(strbuf) - 1, strbuf) ==
+ sizeof(strbuf) - 1) {
+ strbuf[sizeof(strbuf) - 1] = '\0';
+ info.filename += strbuf;
+ pathname_ptr += sizeof(strbuf) - 1;
+ // Stop if we found nul byte indicating the end of the string
+ for (size_t i = 0; i < sizeof(strbuf) - 1; i++) {
+ if (strbuf[i] == '\0') {
+ still_reading = false;
+ break;
+ }
+ }
+ }
+ uuid_clear(info.macho_info.uuid);
+ image_infos.push_back(info);
+ }
+ if (image_infos.size() == 0) {
+ return reply_sp;
+ }
+
+ free(image_info_buf);
+
+ //// Second, read the mach header / load commands for all the dylibs
+
+ for (size_t i = 0; i < image_count; i++) {
+ // The SPI to provide platform is not available on older systems.
+ uint32_t platform = 0;
+ if (!GetMachOInformationFromMemory(platform,
+ image_infos[i].load_address,
+ pointer_size,
+ image_infos[i].macho_info)) {
+ return reply_sp;
+ }
+ }
+
+ //// Third, format all of the above in the JSONGenerator object.
+
+ return FormatDynamicLibrariesIntoJSON(image_infos);
+ }
+
+ return reply_sp;
+}
+
+// From dyld SPI header dyld_process_info.h
+typedef void *dyld_process_info;
+struct dyld_process_cache_info {
+ uuid_t cacheUUID; // UUID of cache used by process
+ uint64_t cacheBaseAddress; // load address of dyld shared cache
+ bool noCache; // process is running without a dyld cache
+ bool privateCache; // process is using a private copy of its dyld cache
+};
+
+// Use the dyld SPI present in macOS 10.12, iOS 10, tvOS 10, watchOS 3 and newer
+// to get
+// the load address, uuid, and filenames of all the libraries.
+// This only fills in those three fields in the 'struct
+// binary_image_information' - call
+// GetMachOInformationFromMemory to fill in the mach-o header/load command
+// details.
+uint32_t MachProcess::GetAllLoadedBinariesViaDYLDSPI(
+ std::vector<struct binary_image_information> &image_infos) {
+ uint32_t platform = 0;
+ kern_return_t kern_ret;
+ if (m_dyld_process_info_create) {
+ dyld_process_info info =
+ m_dyld_process_info_create(m_task.TaskPort(), 0, &kern_ret);
+ if (info) {
+ m_dyld_process_info_for_each_image(
+ info,
+ ^(uint64_t mach_header_addr, const uuid_t uuid, const char *path) {
+ struct binary_image_information image;
+ image.filename = path;
+ uuid_copy(image.macho_info.uuid, uuid);
+ image.load_address = mach_header_addr;
+ image_infos.push_back(image);
+ });
+ if (m_dyld_process_info_get_platform)
+ platform = m_dyld_process_info_get_platform(info);
+ m_dyld_process_info_release(info);
+ }
+ }
+ return platform;
+}
+
+// Fetch information about all shared libraries using the dyld SPIs that exist
+// in
+// macOS 10.12, iOS 10, tvOS 10, watchOS 3 and newer.
+JSONGenerator::ObjectSP
+MachProcess::GetAllLoadedLibrariesInfos(nub_process_t pid) {
+ JSONGenerator::DictionarySP reply_sp;
+
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
+ struct kinfo_proc processInfo;
+ size_t bufsize = sizeof(processInfo);
+ if (sysctl(mib, (unsigned)(sizeof(mib) / sizeof(int)), &processInfo, &bufsize,
+ NULL, 0) == 0 &&
+ bufsize > 0) {
+ uint32_t pointer_size = 4;
+ if (processInfo.kp_proc.p_flag & P_LP64)
+ pointer_size = 8;
+
+ std::vector<struct binary_image_information> image_infos;
+ uint32_t platform = GetAllLoadedBinariesViaDYLDSPI(image_infos);
+ const size_t image_count = image_infos.size();
+ for (size_t i = 0; i < image_count; i++) {
+ GetMachOInformationFromMemory(platform,
+ image_infos[i].load_address, pointer_size,
+ image_infos[i].macho_info);
+ }
+ return FormatDynamicLibrariesIntoJSON(image_infos);
+ }
+ return reply_sp;
+}
+
+// Fetch information about the shared libraries at the given load addresses
+// using the
+// dyld SPIs that exist in macOS 10.12, iOS 10, tvOS 10, watchOS 3 and newer.
+JSONGenerator::ObjectSP MachProcess::GetLibrariesInfoForAddresses(
+ nub_process_t pid, std::vector<uint64_t> &macho_addresses) {
+ JSONGenerator::DictionarySP reply_sp;
+
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
+ struct kinfo_proc processInfo;
+ size_t bufsize = sizeof(processInfo);
+ if (sysctl(mib, (unsigned)(sizeof(mib) / sizeof(int)), &processInfo, &bufsize,
+ NULL, 0) == 0 &&
+ bufsize > 0) {
+ uint32_t pointer_size = 4;
+ if (processInfo.kp_proc.p_flag & P_LP64)
+ pointer_size = 8;
+
+ std::vector<struct binary_image_information> all_image_infos;
+ uint32_t platform = GetAllLoadedBinariesViaDYLDSPI(all_image_infos);
+
+ std::vector<struct binary_image_information> image_infos;
+ const size_t macho_addresses_count = macho_addresses.size();
+ const size_t all_image_infos_count = all_image_infos.size();
+ for (size_t i = 0; i < macho_addresses_count; i++) {
+ for (size_t j = 0; j < all_image_infos_count; j++) {
+ if (all_image_infos[j].load_address == macho_addresses[i]) {
+ image_infos.push_back(all_image_infos[j]);
+ }
+ }
+ }
+
+ const size_t image_infos_count = image_infos.size();
+ for (size_t i = 0; i < image_infos_count; i++) {
+ GetMachOInformationFromMemory(platform,
+ image_infos[i].load_address, pointer_size,
+ image_infos[i].macho_info);
+ }
+ return FormatDynamicLibrariesIntoJSON(image_infos);
+ }
+ return reply_sp;
+}
+
+// From dyld's internal podyld_process_info.h:
+
+JSONGenerator::ObjectSP MachProcess::GetSharedCacheInfo(nub_process_t pid) {
+ JSONGenerator::DictionarySP reply_sp(new JSONGenerator::Dictionary());
+ ;
+ kern_return_t kern_ret;
+ if (m_dyld_process_info_create && m_dyld_process_info_get_cache) {
+ dyld_process_info info =
+ m_dyld_process_info_create(m_task.TaskPort(), 0, &kern_ret);
+ if (info) {
+ struct dyld_process_cache_info shared_cache_info;
+ m_dyld_process_info_get_cache(info, &shared_cache_info);
+
+ reply_sp->AddIntegerItem("shared_cache_base_address",
+ shared_cache_info.cacheBaseAddress);
+
+ uuid_string_t uuidstr;
+ uuid_unparse_upper(shared_cache_info.cacheUUID, uuidstr);
+ reply_sp->AddStringItem("shared_cache_uuid", uuidstr);
+
+ reply_sp->AddBooleanItem("no_shared_cache", shared_cache_info.noCache);
+ reply_sp->AddBooleanItem("shared_cache_private_cache",
+ shared_cache_info.privateCache);
+
+ m_dyld_process_info_release(info);
+ }
+ }
+ return reply_sp;
+}
+
+nub_thread_t MachProcess::GetCurrentThread() {
+ return m_thread_list.CurrentThreadID();
+}
+
+nub_thread_t MachProcess::GetCurrentThreadMachPort() {
+ return m_thread_list.GetMachPortNumberByThreadID(
+ m_thread_list.CurrentThreadID());
+}
+
+nub_thread_t MachProcess::SetCurrentThread(nub_thread_t tid) {
+ return m_thread_list.SetCurrentThread(tid);
+}
+
+bool MachProcess::GetThreadStoppedReason(nub_thread_t tid,
+ struct DNBThreadStopInfo *stop_info) {
+ if (m_thread_list.GetThreadStoppedReason(tid, stop_info)) {
+ if (m_did_exec)
+ stop_info->reason = eStopTypeExec;
+ return true;
+ }
+ return false;
+}
+
+void MachProcess::DumpThreadStoppedReason(nub_thread_t tid) const {
+ return m_thread_list.DumpThreadStoppedReason(tid);
+}
+
+const char *MachProcess::GetThreadInfo(nub_thread_t tid) const {
+ return m_thread_list.GetThreadInfo(tid);
+}
+
+uint32_t MachProcess::GetCPUType() {
+ if (m_cpu_type == 0 && m_pid != 0)
+ m_cpu_type = MachProcess::GetCPUTypeForLocalProcess(m_pid);
+ return m_cpu_type;
+}
+
+const DNBRegisterSetInfo *
+MachProcess::GetRegisterSetInfo(nub_thread_t tid,
+ nub_size_t *num_reg_sets) const {
+ MachThreadSP thread_sp(m_thread_list.GetThreadByID(tid));
+ if (thread_sp) {
+ DNBArchProtocol *arch = thread_sp->GetArchProtocol();
+ if (arch)
+ return arch->GetRegisterSetInfo(num_reg_sets);
+ }
+ *num_reg_sets = 0;
+ return NULL;
+}
+
+bool MachProcess::GetRegisterValue(nub_thread_t tid, uint32_t set, uint32_t reg,
+ DNBRegisterValue *value) const {
+ return m_thread_list.GetRegisterValue(tid, set, reg, value);
+}
+
+bool MachProcess::SetRegisterValue(nub_thread_t tid, uint32_t set, uint32_t reg,
+ const DNBRegisterValue *value) const {
+ return m_thread_list.SetRegisterValue(tid, set, reg, value);
+}
+
+void MachProcess::SetState(nub_state_t new_state) {
+ // If any other threads access this we will need a mutex for it
+ uint32_t event_mask = 0;
+
+ // Scope for mutex locker
+ {
+ PTHREAD_MUTEX_LOCKER(locker, m_state_mutex);
+ const nub_state_t old_state = m_state;
+
+ if (old_state == eStateExited) {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState(%s) ignoring new "
+ "state since current state is exited",
+ DNBStateAsString(new_state));
+ } else if (old_state == new_state) {
+ DNBLogThreadedIf(
+ LOG_PROCESS,
+ "MachProcess::SetState(%s) ignoring redundant state change...",
+ DNBStateAsString(new_state));
+ } else {
+ if (NUB_STATE_IS_STOPPED(new_state))
+ event_mask = eEventProcessStoppedStateChanged;
+ else
+ event_mask = eEventProcessRunningStateChanged;
+
+ DNBLogThreadedIf(
+ LOG_PROCESS, "MachProcess::SetState(%s) upating state (previous "
+ "state was %s), event_mask = 0x%8.8x",
+ DNBStateAsString(new_state), DNBStateAsString(old_state), event_mask);
+
+ m_state = new_state;
+ if (new_state == eStateStopped)
+ m_stop_count++;
+ }
+ }
+
+ if (event_mask != 0) {
+ m_events.SetEvents(event_mask);
+ m_private_events.SetEvents(event_mask);
+ if (event_mask == eEventProcessStoppedStateChanged)
+ m_private_events.ResetEvents(eEventProcessRunningStateChanged);
+ else
+ m_private_events.ResetEvents(eEventProcessStoppedStateChanged);
+
+ // Wait for the event bit to reset if a reset ACK is requested
+ m_events.WaitForResetAck(event_mask);
+ }
+}
+
+void MachProcess::Clear(bool detaching) {
+ // Clear any cached thread list while the pid and task are still valid
+
+ m_task.Clear();
+ // Now clear out all member variables
+ m_pid = INVALID_NUB_PROCESS;
+ if (!detaching)
+ CloseChildFileDescriptors();
+
+ m_path.clear();
+ m_args.clear();
+ SetState(eStateUnloaded);
+ m_flags = eMachProcessFlagsNone;
+ m_stop_count = 0;
+ m_thread_list.Clear();
+ {
+ PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex);
+ m_exception_messages.clear();
+ }
+ m_activities.Clear();
+ if (m_profile_thread) {
+ pthread_join(m_profile_thread, NULL);
+ m_profile_thread = NULL;
+ }
+}
+
+bool MachProcess::StartSTDIOThread() {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( )", __FUNCTION__);
+ // Create the thread that watches for the child STDIO
+ return ::pthread_create(&m_stdio_thread, NULL, MachProcess::STDIOThread,
+ this) == 0;
+}
+
+void MachProcess::SetEnableAsyncProfiling(bool enable, uint64_t interval_usec,
+ DNBProfileDataScanType scan_type) {
+ m_profile_enabled = enable;
+ m_profile_interval_usec = static_cast<useconds_t>(interval_usec);
+ m_profile_scan_type = scan_type;
+
+ if (m_profile_enabled && (m_profile_thread == NULL)) {
+ StartProfileThread();
+ } else if (!m_profile_enabled && m_profile_thread) {
+ pthread_join(m_profile_thread, NULL);
+ m_profile_thread = NULL;
+ }
+}
+
+bool MachProcess::StartProfileThread() {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s ( )", __FUNCTION__);
+ // Create the thread that profiles the inferior and reports back if enabled
+ return ::pthread_create(&m_profile_thread, NULL, MachProcess::ProfileThread,
+ this) == 0;
+}
+
+nub_addr_t MachProcess::LookupSymbol(const char *name, const char *shlib) {
+ if (m_name_to_addr_callback != NULL && name && name[0])
+ return m_name_to_addr_callback(ProcessID(), name, shlib,
+ m_name_to_addr_baton);
+ return INVALID_NUB_ADDRESS;
+}
+
+bool MachProcess::Resume(const DNBThreadResumeActions &thread_actions) {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Resume ()");
+ nub_state_t state = GetState();
+
+ if (CanResume(state)) {
+ m_thread_actions = thread_actions;
+ PrivateResume();
+ return true;
+ } else if (state == eStateRunning) {
+ DNBLog("Resume() - task 0x%x is already running, ignoring...",
+ m_task.TaskPort());
+ return true;
+ }
+ DNBLog("Resume() - task 0x%x has state %s, can't continue...",
+ m_task.TaskPort(), DNBStateAsString(state));
+ return false;
+}
+
+bool MachProcess::Kill(const struct timespec *timeout_abstime) {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill ()");
+ nub_state_t state = DoSIGSTOP(true, false, NULL);
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Kill() DoSIGSTOP() state = %s",
+ DNBStateAsString(state));
+ errno = 0;
+ DNBLog("Sending ptrace PT_KILL to terminate inferior process.");
+ ::ptrace(PT_KILL, m_pid, 0, 0);
+ DNBError err;
+ err.SetErrorToErrno();
+ if (DNBLogCheckLogBit(LOG_PROCESS) || err.Fail()) {
+ err.LogThreaded("MachProcess::Kill() DoSIGSTOP() ::ptrace "
+ "(PT_KILL, pid=%u, 0, 0) => 0x%8.8x (%s)",
+ m_pid, err.Status(), err.AsString());
+ }
+ m_thread_actions = DNBThreadResumeActions(eStateRunning, 0);
+ PrivateResume();
+
+ // Try and reap the process without touching our m_events since
+ // we want the code above this to still get the eStateExited event
+ const uint32_t reap_timeout_usec =
+ 1000000; // Wait 1 second and try to reap the process
+ const uint32_t reap_interval_usec = 10000; //
+ uint32_t reap_time_elapsed;
+ for (reap_time_elapsed = 0; reap_time_elapsed < reap_timeout_usec;
+ reap_time_elapsed += reap_interval_usec) {
+ if (GetState() == eStateExited)
+ break;
+ usleep(reap_interval_usec);
+ }
+ DNBLog("Waited %u ms for process to be reaped (state = %s)",
+ reap_time_elapsed / 1000, DNBStateAsString(GetState()));
+ return true;
+}
+
+bool MachProcess::Interrupt() {
+ nub_state_t state = GetState();
+ if (IsRunning(state)) {
+ if (m_sent_interrupt_signo == 0) {
+ m_sent_interrupt_signo = SIGSTOP;
+ if (Signal(m_sent_interrupt_signo)) {
+ DNBLogThreadedIf(
+ LOG_PROCESS,
+ "MachProcess::Interrupt() - sent %i signal to interrupt process",
+ m_sent_interrupt_signo);
+ return true;
+ } else {
+ m_sent_interrupt_signo = 0;
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - failed to "
+ "send %i signal to interrupt process",
+ m_sent_interrupt_signo);
+ }
+ } else {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - previously "
+ "sent an interrupt signal %i that hasn't "
+ "been received yet, interrupt aborted",
+ m_sent_interrupt_signo);
+ }
+ } else {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Interrupt() - process already "
+ "stopped, no interrupt sent");
+ }
+ return false;
+}
+
+bool MachProcess::Signal(int signal, const struct timespec *timeout_abstime) {
+ DNBLogThreadedIf(LOG_PROCESS,
+ "MachProcess::Signal (signal = %d, timeout = %p)", signal,
+ static_cast<const void *>(timeout_abstime));
+ nub_state_t state = GetState();
+ if (::kill(ProcessID(), signal) == 0) {
+ // If we were running and we have a timeout, wait for the signal to stop
+ if (IsRunning(state) && timeout_abstime) {
+ DNBLogThreadedIf(LOG_PROCESS,
+ "MachProcess::Signal (signal = %d, timeout "
+ "= %p) waiting for signal to stop "
+ "process...",
+ signal, static_cast<const void *>(timeout_abstime));
+ m_private_events.WaitForSetEvents(eEventProcessStoppedStateChanged,
+ timeout_abstime);
+ state = GetState();
+ DNBLogThreadedIf(
+ LOG_PROCESS,
+ "MachProcess::Signal (signal = %d, timeout = %p) state = %s", signal,
+ static_cast<const void *>(timeout_abstime), DNBStateAsString(state));
+ return !IsRunning(state);
+ }
+ DNBLogThreadedIf(
+ LOG_PROCESS,
+ "MachProcess::Signal (signal = %d, timeout = %p) not waiting...",
+ signal, static_cast<const void *>(timeout_abstime));
+ return true;
+ }
+ DNBError err(errno, DNBError::POSIX);
+ err.LogThreadedIfError("kill (pid = %d, signo = %i)", ProcessID(), signal);
+ return false;
+}
+
+bool MachProcess::SendEvent(const char *event, DNBError &send_err) {
+ DNBLogThreadedIf(LOG_PROCESS,
+ "MachProcess::SendEvent (event = %s) to pid: %d", event,
+ m_pid);
+ if (m_pid == INVALID_NUB_PROCESS)
+ return false;
+// FIXME: Shouldn't we use the launch flavor we were started with?
+#if defined(WITH_FBS) || defined(WITH_BKS)
+ return BoardServiceSendEvent(event, send_err);
+#endif
+ return true;
+}
+
+nub_state_t MachProcess::DoSIGSTOP(bool clear_bps_and_wps, bool allow_running,
+ uint32_t *thread_idx_ptr) {
+ nub_state_t state = GetState();
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::DoSIGSTOP() state = %s",
+ DNBStateAsString(state));
+
+ if (!IsRunning(state)) {
+ if (clear_bps_and_wps) {
+ DisableAllBreakpoints(true);
+ DisableAllWatchpoints(true);
+ clear_bps_and_wps = false;
+ }
+
+ // If we already have a thread stopped due to a SIGSTOP, we don't have
+ // to do anything...
+ uint32_t thread_idx =
+ m_thread_list.GetThreadIndexForThreadStoppedWithSignal(SIGSTOP);
+ if (thread_idx_ptr)
+ *thread_idx_ptr = thread_idx;
+ if (thread_idx != UINT32_MAX)
+ return GetState();
+
+ // No threads were stopped with a SIGSTOP, we need to run and halt the
+ // process with a signal
+ DNBLogThreadedIf(LOG_PROCESS,
+ "MachProcess::DoSIGSTOP() state = %s -- resuming process",
+ DNBStateAsString(state));
+ if (allow_running)
+ m_thread_actions = DNBThreadResumeActions(eStateRunning, 0);
+ else
+ m_thread_actions = DNBThreadResumeActions(eStateSuspended, 0);
+
+ PrivateResume();
+
+ // Reset the event that says we were indeed running
+ m_events.ResetEvents(eEventProcessRunningStateChanged);
+ state = GetState();
+ }
+
+ // We need to be stopped in order to be able to detach, so we need
+ // to send ourselves a SIGSTOP
+
+ DNBLogThreadedIf(LOG_PROCESS,
+ "MachProcess::DoSIGSTOP() state = %s -- sending SIGSTOP",
+ DNBStateAsString(state));
+ struct timespec sigstop_timeout;
+ DNBTimer::OffsetTimeOfDay(&sigstop_timeout, 2, 0);
+ Signal(SIGSTOP, &sigstop_timeout);
+ if (clear_bps_and_wps) {
+ DisableAllBreakpoints(true);
+ DisableAllWatchpoints(true);
+ // clear_bps_and_wps = false;
+ }
+ uint32_t thread_idx =
+ m_thread_list.GetThreadIndexForThreadStoppedWithSignal(SIGSTOP);
+ if (thread_idx_ptr)
+ *thread_idx_ptr = thread_idx;
+ return GetState();
+}
+
+bool MachProcess::Detach() {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach()");
+
+ uint32_t thread_idx = UINT32_MAX;
+ nub_state_t state = DoSIGSTOP(true, true, &thread_idx);
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Detach() DoSIGSTOP() returned %s",
+ DNBStateAsString(state));
+
+ {
+ m_thread_actions.Clear();
+ m_activities.Clear();
+ DNBThreadResumeAction thread_action;
+ thread_action.tid = m_thread_list.ThreadIDAtIndex(thread_idx);
+ thread_action.state = eStateRunning;
+ thread_action.signal = -1;
+ thread_action.addr = INVALID_NUB_ADDRESS;
+
+ m_thread_actions.Append(thread_action);
+ m_thread_actions.SetDefaultThreadActionIfNeeded(eStateRunning, 0);
+
+ PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex);
+
+ ReplyToAllExceptions();
+ }
+
+ m_task.ShutDownExcecptionThread();
+
+ // Detach from our process
+ errno = 0;
+ nub_process_t pid = m_pid;
+ int ret = ::ptrace(PT_DETACH, pid, (caddr_t)1, 0);
+ DNBError err(errno, DNBError::POSIX);
+ if (DNBLogCheckLogBit(LOG_PROCESS) || err.Fail() || (ret != 0))
+ err.LogThreaded("::ptrace (PT_DETACH, %u, (caddr_t)1, 0)", pid);
+
+ // Resume our task
+ m_task.Resume();
+
+ // NULL our task out as we have already restored all exception ports
+ m_task.Clear();
+
+ // Clear out any notion of the process we once were
+ const bool detaching = true;
+ Clear(detaching);
+
+ SetState(eStateDetached);
+
+ return true;
+}
+
+//----------------------------------------------------------------------
+// ReadMemory from the MachProcess level will always remove any software
+// breakpoints from the memory buffer before returning. If you wish to
+// read memory and see those traps, read from the MachTask
+// (m_task.ReadMemory()) as that version will give you what is actually
+// in inferior memory.
+//----------------------------------------------------------------------
+nub_size_t MachProcess::ReadMemory(nub_addr_t addr, nub_size_t size,
+ void *buf) {
+ // We need to remove any current software traps (enabled software
+ // breakpoints) that we may have placed in our tasks memory.
+
+ // First just read the memory as is
+ nub_size_t bytes_read = m_task.ReadMemory(addr, size, buf);
+
+ // Then place any opcodes that fall into this range back into the buffer
+ // before we return this to callers.
+ if (bytes_read > 0)
+ m_breakpoints.RemoveTrapsFromBuffer(addr, bytes_read, buf);
+ return bytes_read;
+}
+
+//----------------------------------------------------------------------
+// WriteMemory from the MachProcess level will always write memory around
+// any software breakpoints. Any software breakpoints will have their
+// opcodes modified if they are enabled. Any memory that doesn't overlap
+// with software breakpoints will be written to. If you wish to write to
+// inferior memory without this interference, then write to the MachTask
+// (m_task.WriteMemory()) as that version will always modify inferior
+// memory.
+//----------------------------------------------------------------------
+nub_size_t MachProcess::WriteMemory(nub_addr_t addr, nub_size_t size,
+ const void *buf) {
+ // We need to write any data that would go where any current software traps
+ // (enabled software breakpoints) any software traps (breakpoints) that we
+ // may have placed in our tasks memory.
+
+ std::vector<DNBBreakpoint *> bps;
+
+ const size_t num_bps =
+ m_breakpoints.FindBreakpointsThatOverlapRange(addr, size, bps);
+ if (num_bps == 0)
+ return m_task.WriteMemory(addr, size, buf);
+
+ nub_size_t bytes_written = 0;
+ nub_addr_t intersect_addr;
+ nub_size_t intersect_size;
+ nub_size_t opcode_offset;
+ const uint8_t *ubuf = (const uint8_t *)buf;
+
+ for (size_t i = 0; i < num_bps; ++i) {
+ DNBBreakpoint *bp = bps[i];
+
+ const bool intersects = bp->IntersectsRange(
+ addr, size, &intersect_addr, &intersect_size, &opcode_offset);
+ UNUSED_IF_ASSERT_DISABLED(intersects);
+ assert(intersects);
+ assert(addr <= intersect_addr && intersect_addr < addr + size);
+ assert(addr < intersect_addr + intersect_size &&
+ intersect_addr + intersect_size <= addr + size);
+ assert(opcode_offset + intersect_size <= bp->ByteSize());
+
+ // Check for bytes before this breakpoint
+ const nub_addr_t curr_addr = addr + bytes_written;
+ if (intersect_addr > curr_addr) {
+ // There are some bytes before this breakpoint that we need to
+ // just write to memory
+ nub_size_t curr_size = intersect_addr - curr_addr;
+ nub_size_t curr_bytes_written =
+ m_task.WriteMemory(curr_addr, curr_size, ubuf + bytes_written);
+ bytes_written += curr_bytes_written;
+ if (curr_bytes_written != curr_size) {
+ // We weren't able to write all of the requested bytes, we
+ // are done looping and will return the number of bytes that
+ // we have written so far.
+ break;
+ }
+ }
+
+ // Now write any bytes that would cover up any software breakpoints
+ // directly into the breakpoint opcode buffer
+ ::memcpy(bp->SavedOpcodeBytes() + opcode_offset, ubuf + bytes_written,
+ intersect_size);
+ bytes_written += intersect_size;
+ }
+
+ // Write any remaining bytes after the last breakpoint if we have any left
+ if (bytes_written < size)
+ bytes_written += m_task.WriteMemory(
+ addr + bytes_written, size - bytes_written, ubuf + bytes_written);
+
+ return bytes_written;
+}
+
+void MachProcess::ReplyToAllExceptions() {
+ PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex);
+ if (!m_exception_messages.empty()) {
+ MachException::Message::iterator pos;
+ MachException::Message::iterator begin = m_exception_messages.begin();
+ MachException::Message::iterator end = m_exception_messages.end();
+ for (pos = begin; pos != end; ++pos) {
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "Replying to exception %u...",
+ (uint32_t)std::distance(begin, pos));
+ int thread_reply_signal = 0;
+
+ nub_thread_t tid =
+ m_thread_list.GetThreadIDByMachPortNumber(pos->state.thread_port);
+ const DNBThreadResumeAction *action = NULL;
+ if (tid != INVALID_NUB_THREAD) {
+ action = m_thread_actions.GetActionForThread(tid, false);
+ }
+
+ if (action) {
+ thread_reply_signal = action->signal;
+ if (thread_reply_signal)
+ m_thread_actions.SetSignalHandledForThread(tid);
+ }
+
+ DNBError err(pos->Reply(this, thread_reply_signal));
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
+ err.LogThreadedIfError("Error replying to exception");
+ }
+
+ // Erase all exception message as we should have used and replied
+ // to them all already.
+ m_exception_messages.clear();
+ }
+}
+void MachProcess::PrivateResume() {
+ PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex);
+
+ m_auto_resume_signo = m_sent_interrupt_signo;
+ if (m_auto_resume_signo)
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::PrivateResume() - task 0x%x "
+ "resuming (with unhandled interrupt signal "
+ "%i)...",
+ m_task.TaskPort(), m_auto_resume_signo);
+ else
+ DNBLogThreadedIf(LOG_PROCESS,
+ "MachProcess::PrivateResume() - task 0x%x resuming...",
+ m_task.TaskPort());
+
+ ReplyToAllExceptions();
+ // bool stepOverBreakInstruction = step;
+
+ // Let the thread prepare to resume and see if any threads want us to
+ // step over a breakpoint instruction (ProcessWillResume will modify
+ // the value of stepOverBreakInstruction).
+ m_thread_list.ProcessWillResume(this, m_thread_actions);
+
+ // Set our state accordingly
+ if (m_thread_actions.NumActionsWithState(eStateStepping))
+ SetState(eStateStepping);
+ else
+ SetState(eStateRunning);
+
+ // Now resume our task.
+ m_task.Resume();
+}
+
+DNBBreakpoint *MachProcess::CreateBreakpoint(nub_addr_t addr, nub_size_t length,
+ bool hardware) {
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::CreateBreakpoint ( addr = "
+ "0x%8.8llx, length = %llu, hardware = %i)",
+ (uint64_t)addr, (uint64_t)length, hardware);
+
+ DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr);
+ if (bp)
+ bp->Retain();
+ else
+ bp = m_breakpoints.Add(addr, length, hardware);
+
+ if (EnableBreakpoint(addr)) {
+ DNBLogThreadedIf(LOG_BREAKPOINTS,
+ "MachProcess::CreateBreakpoint ( addr = "
+ "0x%8.8llx, length = %llu) => %p",
+ (uint64_t)addr, (uint64_t)length, static_cast<void *>(bp));
+ return bp;
+ } else if (bp->Release() == 0) {
+ m_breakpoints.Remove(addr);
+ }
+ // We failed to enable the breakpoint
+ return NULL;
+}
+
+DNBBreakpoint *MachProcess::CreateWatchpoint(nub_addr_t addr, nub_size_t length,
+ uint32_t watch_flags,
+ bool hardware) {
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = "
+ "0x%8.8llx, length = %llu, flags = "
+ "0x%8.8x, hardware = %i)",
+ (uint64_t)addr, (uint64_t)length, watch_flags, hardware);
+
+ DNBBreakpoint *wp = m_watchpoints.FindByAddress(addr);
+ // since the Z packets only send an address, we can only have one watchpoint
+ // at
+ // an address. If there is already one, we must refuse to create another
+ // watchpoint
+ if (wp)
+ return NULL;
+
+ wp = m_watchpoints.Add(addr, length, hardware);
+ wp->SetIsWatchpoint(watch_flags);
+
+ if (EnableWatchpoint(addr)) {
+ DNBLogThreadedIf(LOG_WATCHPOINTS,
+ "MachProcess::CreateWatchpoint ( addr = "
+ "0x%8.8llx, length = %llu) => %p",
+ (uint64_t)addr, (uint64_t)length, static_cast<void *>(wp));
+ return wp;
+ } else {
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::CreateWatchpoint ( addr = "
+ "0x%8.8llx, length = %llu) => FAILED",
+ (uint64_t)addr, (uint64_t)length);
+ m_watchpoints.Remove(addr);
+ }
+ // We failed to enable the watchpoint
+ return NULL;
+}
+
+void MachProcess::DisableAllBreakpoints(bool remove) {
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::%s (remove = %d )",
+ __FUNCTION__, remove);
+
+ m_breakpoints.DisableAllBreakpoints(this);
+
+ if (remove)
+ m_breakpoints.RemoveDisabled();
+}
+
+void MachProcess::DisableAllWatchpoints(bool remove) {
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::%s (remove = %d )",
+ __FUNCTION__, remove);
+
+ m_watchpoints.DisableAllWatchpoints(this);
+
+ if (remove)
+ m_watchpoints.RemoveDisabled();
+}
+
+bool MachProcess::DisableBreakpoint(nub_addr_t addr, bool remove) {
+ DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr);
+ if (bp) {
+ // After "exec" we might end up with a bunch of breakpoints that were
+ // disabled
+ // manually, just ignore them
+ if (!bp->IsEnabled()) {
+ // Breakpoint might have been disabled by an exec
+ if (remove && bp->Release() == 0) {
+ m_thread_list.NotifyBreakpointChanged(bp);
+ m_breakpoints.Remove(addr);
+ }
+ return true;
+ }
+
+ // We have multiple references to this breakpoint, decrement the ref count
+ // and if it isn't zero, then return true;
+ if (remove && bp->Release() > 0)
+ return true;
+
+ DNBLogThreadedIf(
+ LOG_BREAKPOINTS | LOG_VERBOSE,
+ "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = %d )",
+ (uint64_t)addr, remove);
+
+ if (bp->IsHardware()) {
+ bool hw_disable_result = m_thread_list.DisableHardwareBreakpoint(bp);
+
+ if (hw_disable_result) {
+ bp->SetEnabled(false);
+ // Let the thread list know that a breakpoint has been modified
+ if (remove) {
+ m_thread_list.NotifyBreakpointChanged(bp);
+ m_breakpoints.Remove(addr);
+ }
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::DisableBreakpoint ( "
+ "addr = 0x%8.8llx, remove = %d ) "
+ "(hardware) => success",
+ (uint64_t)addr, remove);
+ return true;
+ }
+
+ return false;
+ }
+
+ const nub_size_t break_op_size = bp->ByteSize();
+ assert(break_op_size > 0);
+ const uint8_t *const break_op =
+ DNBArchProtocol::GetBreakpointOpcode(bp->ByteSize());
+ if (break_op_size > 0) {
+ // Clear a software breakpoint instruction
+ uint8_t curr_break_op[break_op_size];
+ bool break_op_found = false;
+
+ // Read the breakpoint opcode
+ if (m_task.ReadMemory(addr, break_op_size, curr_break_op) ==
+ break_op_size) {
+ bool verify = false;
+ if (bp->IsEnabled()) {
+ // Make sure a breakpoint opcode exists at this address
+ if (memcmp(curr_break_op, break_op, break_op_size) == 0) {
+ break_op_found = true;
+ // We found a valid breakpoint opcode at this address, now restore
+ // the saved opcode.
+ if (m_task.WriteMemory(addr, break_op_size,
+ bp->SavedOpcodeBytes()) == break_op_size) {
+ verify = true;
+ } else {
+ DNBLogError("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, "
+ "remove = %d ) memory write failed when restoring "
+ "original opcode",
+ (uint64_t)addr, remove);
+ }
+ } else {
+ DNBLogWarning("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, "
+ "remove = %d ) expected a breakpoint opcode but "
+ "didn't find one.",
+ (uint64_t)addr, remove);
+ // Set verify to true and so we can check if the original opcode has
+ // already been restored
+ verify = true;
+ }
+ } else {
+ DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE,
+ "MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, "
+ "remove = %d ) is not enabled",
+ (uint64_t)addr, remove);
+ // Set verify to true and so we can check if the original opcode is
+ // there
+ verify = true;
+ }
+
+ if (verify) {
+ uint8_t verify_opcode[break_op_size];
+ // Verify that our original opcode made it back to the inferior
+ if (m_task.ReadMemory(addr, break_op_size, verify_opcode) ==
+ break_op_size) {
+ // compare the memory we just read with the original opcode
+ if (memcmp(bp->SavedOpcodeBytes(), verify_opcode, break_op_size) ==
+ 0) {
+ // SUCCESS
+ bp->SetEnabled(false);
+ // Let the thread list know that a breakpoint has been modified
+ if (remove && bp->Release() == 0) {
+ m_thread_list.NotifyBreakpointChanged(bp);
+ m_breakpoints.Remove(addr);
+ }
+ DNBLogThreadedIf(LOG_BREAKPOINTS,
+ "MachProcess::DisableBreakpoint ( addr = "
+ "0x%8.8llx, remove = %d ) => success",
+ (uint64_t)addr, remove);
+ return true;
+ } else {
+ if (break_op_found)
+ DNBLogError("MachProcess::DisableBreakpoint ( addr = "
+ "0x%8.8llx, remove = %d ) : failed to restore "
+ "original opcode",
+ (uint64_t)addr, remove);
+ else
+ DNBLogError("MachProcess::DisableBreakpoint ( addr = "
+ "0x%8.8llx, remove = %d ) : opcode changed",
+ (uint64_t)addr, remove);
+ }
+ } else {
+ DNBLogWarning("MachProcess::DisableBreakpoint: unable to disable "
+ "breakpoint 0x%8.8llx",
+ (uint64_t)addr);
+ }
+ }
+ } else {
+ DNBLogWarning("MachProcess::DisableBreakpoint: unable to read memory "
+ "at 0x%8.8llx",
+ (uint64_t)addr);
+ }
+ }
+ } else {
+ DNBLogError("MachProcess::DisableBreakpoint ( addr = 0x%8.8llx, remove = "
+ "%d ) invalid breakpoint address",
+ (uint64_t)addr, remove);
+ }
+ return false;
+}
+
+bool MachProcess::DisableWatchpoint(nub_addr_t addr, bool remove) {
+ DNBLogThreadedIf(LOG_WATCHPOINTS,
+ "MachProcess::%s(addr = 0x%8.8llx, remove = %d)",
+ __FUNCTION__, (uint64_t)addr, remove);
+ DNBBreakpoint *wp = m_watchpoints.FindByAddress(addr);
+ if (wp) {
+ // If we have multiple references to a watchpoint, removing the watchpoint
+ // shouldn't clear it
+ if (remove && wp->Release() > 0)
+ return true;
+
+ nub_addr_t addr = wp->Address();
+ DNBLogThreadedIf(
+ LOG_WATCHPOINTS,
+ "MachProcess::DisableWatchpoint ( addr = 0x%8.8llx, remove = %d )",
+ (uint64_t)addr, remove);
+
+ if (wp->IsHardware()) {
+ bool hw_disable_result = m_thread_list.DisableHardwareWatchpoint(wp);
+
+ if (hw_disable_result) {
+ wp->SetEnabled(false);
+ if (remove)
+ m_watchpoints.Remove(addr);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "MachProcess::Disablewatchpoint ( "
+ "addr = 0x%8.8llx, remove = %d ) "
+ "(hardware) => success",
+ (uint64_t)addr, remove);
+ return true;
+ }
+ }
+
+ // TODO: clear software watchpoints if we implement them
+ } else {
+ DNBLogError("MachProcess::DisableWatchpoint ( addr = 0x%8.8llx, remove = "
+ "%d ) invalid watchpoint ID",
+ (uint64_t)addr, remove);
+ }
+ return false;
+}
+
+uint32_t MachProcess::GetNumSupportedHardwareWatchpoints() const {
+ return m_thread_list.NumSupportedHardwareWatchpoints();
+}
+
+bool MachProcess::EnableBreakpoint(nub_addr_t addr) {
+ DNBLogThreadedIf(LOG_BREAKPOINTS,
+ "MachProcess::EnableBreakpoint ( addr = 0x%8.8llx )",
+ (uint64_t)addr);
+ DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr);
+ if (bp) {
+ if (bp->IsEnabled()) {
+ DNBLogWarning("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): "
+ "breakpoint already enabled.",
+ (uint64_t)addr);
+ return true;
+ } else {
+ if (bp->HardwarePreferred()) {
+ bp->SetHardwareIndex(m_thread_list.EnableHardwareBreakpoint(bp));
+ if (bp->IsHardware()) {
+ bp->SetEnabled(true);
+ return true;
+ }
+ }
+
+ const nub_size_t break_op_size = bp->ByteSize();
+ assert(break_op_size != 0);
+ const uint8_t *const break_op =
+ DNBArchProtocol::GetBreakpointOpcode(break_op_size);
+ if (break_op_size > 0) {
+ // Save the original opcode by reading it
+ if (m_task.ReadMemory(addr, break_op_size, bp->SavedOpcodeBytes()) ==
+ break_op_size) {
+ // Write a software breakpoint in place of the original opcode
+ if (m_task.WriteMemory(addr, break_op_size, break_op) ==
+ break_op_size) {
+ uint8_t verify_break_op[4];
+ if (m_task.ReadMemory(addr, break_op_size, verify_break_op) ==
+ break_op_size) {
+ if (memcmp(break_op, verify_break_op, break_op_size) == 0) {
+ bp->SetEnabled(true);
+ // Let the thread list know that a breakpoint has been modified
+ m_thread_list.NotifyBreakpointChanged(bp);
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "MachProcess::"
+ "EnableBreakpoint ( addr = "
+ "0x%8.8llx ) : SUCCESS.",
+ (uint64_t)addr);
+ return true;
+ } else {
+ DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx "
+ "): breakpoint opcode verification failed.",
+ (uint64_t)addr);
+ }
+ } else {
+ DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): "
+ "unable to read memory to verify breakpoint opcode.",
+ (uint64_t)addr);
+ }
+ } else {
+ DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): "
+ "unable to write breakpoint opcode to memory.",
+ (uint64_t)addr);
+ }
+ } else {
+ DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): "
+ "unable to read memory at breakpoint address.",
+ (uint64_t)addr);
+ }
+ } else {
+ DNBLogError("MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ) no "
+ "software breakpoint opcode for current architecture.",
+ (uint64_t)addr);
+ }
+ }
+ }
+ return false;
+}
+
+bool MachProcess::EnableWatchpoint(nub_addr_t addr) {
+ DNBLogThreadedIf(LOG_WATCHPOINTS,
+ "MachProcess::EnableWatchpoint(addr = 0x%8.8llx)",
+ (uint64_t)addr);
+ DNBBreakpoint *wp = m_watchpoints.FindByAddress(addr);
+ if (wp) {
+ nub_addr_t addr = wp->Address();
+ if (wp->IsEnabled()) {
+ DNBLogWarning("MachProcess::EnableWatchpoint(addr = 0x%8.8llx): "
+ "watchpoint already enabled.",
+ (uint64_t)addr);
+ return true;
+ } else {
+ // Currently only try and set hardware watchpoints.
+ wp->SetHardwareIndex(m_thread_list.EnableHardwareWatchpoint(wp));
+ if (wp->IsHardware()) {
+ wp->SetEnabled(true);
+ return true;
+ }
+ // TODO: Add software watchpoints by doing page protection tricks.
+ }
+ }
+ return false;
+}
+
+// Called by the exception thread when an exception has been received from
+// our process. The exception message is completely filled and the exception
+// data has already been copied.
+void MachProcess::ExceptionMessageReceived(
+ const MachException::Message &exceptionMessage) {
+ PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex);
+
+ if (m_exception_messages.empty())
+ m_task.Suspend();
+
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "MachProcess::ExceptionMessageReceived ( )");
+
+ // Use a locker to automatically unlock our mutex in case of exceptions
+ // Add the exception to our internal exception stack
+ m_exception_messages.push_back(exceptionMessage);
+}
+
+task_t MachProcess::ExceptionMessageBundleComplete() {
+ // We have a complete bundle of exceptions for our child process.
+ PTHREAD_MUTEX_LOCKER(locker, m_exception_messages_mutex);
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "%s: %llu exception messages.",
+ __PRETTY_FUNCTION__, (uint64_t)m_exception_messages.size());
+ bool auto_resume = false;
+ if (!m_exception_messages.empty()) {
+ m_did_exec = false;
+ // First check for any SIGTRAP and make sure we didn't exec
+ const task_t task = m_task.TaskPort();
+ size_t i;
+ if (m_pid != 0) {
+ bool received_interrupt = false;
+ uint32_t num_task_exceptions = 0;
+ for (i = 0; i < m_exception_messages.size(); ++i) {
+ if (m_exception_messages[i].state.task_port == task) {
+ ++num_task_exceptions;
+ const int signo = m_exception_messages[i].state.SoftSignal();
+ if (signo == SIGTRAP) {
+ // SIGTRAP could mean that we exec'ed. We need to check the
+ // dyld all_image_infos.infoArray to see if it is NULL and if
+ // so, say that we exec'ed.
+ const nub_addr_t aii_addr = GetDYLDAllImageInfosAddress();
+ if (aii_addr != INVALID_NUB_ADDRESS) {
+ const nub_addr_t info_array_count_addr = aii_addr + 4;
+ uint32_t info_array_count = 0;
+ if (m_task.ReadMemory(info_array_count_addr, 4,
+ &info_array_count) == 4) {
+ if (info_array_count == 0) {
+ m_did_exec = true;
+ // Force the task port to update itself in case the task port
+ // changed after exec
+ DNBError err;
+ const task_t old_task = m_task.TaskPort();
+ const task_t new_task =
+ m_task.TaskPortForProcessID(err, true);
+ if (old_task != new_task)
+ DNBLogThreadedIf(
+ LOG_PROCESS,
+ "exec: task changed from 0x%4.4x to 0x%4.4x", old_task,
+ new_task);
+ }
+ } else {
+ DNBLog("error: failed to read all_image_infos.infoArrayCount "
+ "from 0x%8.8llx",
+ (uint64_t)info_array_count_addr);
+ }
+ }
+ break;
+ } else if (m_sent_interrupt_signo != 0 &&
+ signo == m_sent_interrupt_signo) {
+ received_interrupt = true;
+ }
+ }
+ }
+
+ if (m_did_exec) {
+ cpu_type_t process_cpu_type =
+ MachProcess::GetCPUTypeForLocalProcess(m_pid);
+ if (m_cpu_type != process_cpu_type) {
+ DNBLog("arch changed from 0x%8.8x to 0x%8.8x", m_cpu_type,
+ process_cpu_type);
+ m_cpu_type = process_cpu_type;
+ DNBArchProtocol::SetArchitecture(process_cpu_type);
+ }
+ m_thread_list.Clear();
+ m_activities.Clear();
+ m_breakpoints.DisableAll();
+ }
+
+ if (m_sent_interrupt_signo != 0) {
+ if (received_interrupt) {
+ DNBLogThreadedIf(LOG_PROCESS,
+ "MachProcess::ExceptionMessageBundleComplete(): "
+ "process successfully interrupted with signal %i",
+ m_sent_interrupt_signo);
+
+ // Mark that we received the interrupt signal
+ m_sent_interrupt_signo = 0;
+ // Not check if we had a case where:
+ // 1 - We called MachProcess::Interrupt() but we stopped for another
+ // reason
+ // 2 - We called MachProcess::Resume() (but still haven't gotten the
+ // interrupt signal)
+ // 3 - We are now incorrectly stopped because we are handling the
+ // interrupt signal we missed
+ // 4 - We might need to resume if we stopped only with the interrupt
+ // signal that we never handled
+ if (m_auto_resume_signo != 0) {
+ // Only auto_resume if we stopped with _only_ the interrupt signal
+ if (num_task_exceptions == 1) {
+ auto_resume = true;
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::"
+ "ExceptionMessageBundleComplete(): "
+ "auto resuming due to unhandled "
+ "interrupt signal %i",
+ m_auto_resume_signo);
+ }
+ m_auto_resume_signo = 0;
+ }
+ } else {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::"
+ "ExceptionMessageBundleComplete(): "
+ "didn't get signal %i after "
+ "MachProcess::Interrupt()",
+ m_sent_interrupt_signo);
+ }
+ }
+ }
+
+ // Let all threads recover from stopping and do any clean up based
+ // on the previous thread state (if any).
+ m_thread_list.ProcessDidStop(this);
+ m_activities.Clear();
+
+ // Let each thread know of any exceptions
+ for (i = 0; i < m_exception_messages.size(); ++i) {
+ // Let the thread list figure use the MachProcess to forward all
+ // exceptions
+ // on down to each thread.
+ if (m_exception_messages[i].state.task_port == task)
+ m_thread_list.NotifyException(m_exception_messages[i].state);
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
+ m_exception_messages[i].Dump();
+ }
+
+ if (DNBLogCheckLogBit(LOG_THREAD))
+ m_thread_list.Dump();
+
+ bool step_more = false;
+ if (m_thread_list.ShouldStop(step_more) && !auto_resume) {
+ // Wait for the eEventProcessRunningStateChanged event to be reset
+ // before changing state to stopped to avoid race condition with
+ // very fast start/stops
+ struct timespec timeout;
+ // DNBTimer::OffsetTimeOfDay(&timeout, 0, 250 * 1000); // Wait for 250
+ // ms
+ DNBTimer::OffsetTimeOfDay(&timeout, 1, 0); // Wait for 250 ms
+ m_events.WaitForEventsToReset(eEventProcessRunningStateChanged, &timeout);
+ SetState(eStateStopped);
+ } else {
+ // Resume without checking our current state.
+ PrivateResume();
+ }
+ } else {
+ DNBLogThreadedIf(
+ LOG_EXCEPTIONS, "%s empty exception messages bundle (%llu exceptions).",
+ __PRETTY_FUNCTION__, (uint64_t)m_exception_messages.size());
+ }
+ return m_task.TaskPort();
+}
+
+nub_size_t
+MachProcess::CopyImageInfos(struct DNBExecutableImageInfo **image_infos,
+ bool only_changed) {
+ if (m_image_infos_callback != NULL)
+ return m_image_infos_callback(ProcessID(), image_infos, only_changed,
+ m_image_infos_baton);
+ return 0;
+}
+
+void MachProcess::SharedLibrariesUpdated() {
+ uint32_t event_bits = eEventSharedLibsStateChange;
+ // Set the shared library event bit to let clients know of shared library
+ // changes
+ m_events.SetEvents(event_bits);
+ // Wait for the event bit to reset if a reset ACK is requested
+ m_events.WaitForResetAck(event_bits);
+}
+
+void MachProcess::SetExitInfo(const char *info) {
+ if (info && info[0]) {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s(\"%s\")", __FUNCTION__,
+ info);
+ m_exit_info.assign(info);
+ } else {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s(NULL)", __FUNCTION__);
+ m_exit_info.clear();
+ }
+}
+
+void MachProcess::AppendSTDOUT(char *s, size_t len) {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (<%llu> %s) ...", __FUNCTION__,
+ (uint64_t)len, s);
+ PTHREAD_MUTEX_LOCKER(locker, m_stdio_mutex);
+ m_stdout_data.append(s, len);
+ m_events.SetEvents(eEventStdioAvailable);
+
+ // Wait for the event bit to reset if a reset ACK is requested
+ m_events.WaitForResetAck(eEventStdioAvailable);
+}
+
+size_t MachProcess::GetAvailableSTDOUT(char *buf, size_t buf_size) {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (&%p[%llu]) ...", __FUNCTION__,
+ static_cast<void *>(buf), (uint64_t)buf_size);
+ PTHREAD_MUTEX_LOCKER(locker, m_stdio_mutex);
+ size_t bytes_available = m_stdout_data.size();
+ if (bytes_available > 0) {
+ if (bytes_available > buf_size) {
+ memcpy(buf, m_stdout_data.data(), buf_size);
+ m_stdout_data.erase(0, buf_size);
+ bytes_available = buf_size;
+ } else {
+ memcpy(buf, m_stdout_data.data(), bytes_available);
+ m_stdout_data.clear();
+ }
+ }
+ return bytes_available;
+}
+
+nub_addr_t MachProcess::GetDYLDAllImageInfosAddress() {
+ DNBError err;
+ return m_task.GetDYLDAllImageInfosAddress(err);
+}
+
+size_t MachProcess::GetAvailableSTDERR(char *buf, size_t buf_size) { return 0; }
+
+void *MachProcess::STDIOThread(void *arg) {
+ MachProcess *proc = (MachProcess *)arg;
+ DNBLogThreadedIf(LOG_PROCESS,
+ "MachProcess::%s ( arg = %p ) thread starting...",
+ __FUNCTION__, arg);
+
+#if defined(__APPLE__)
+ pthread_setname_np("stdio monitoring thread");
+#endif
+
+ // We start use a base and more options so we can control if we
+ // are currently using a timeout on the mach_msg. We do this to get a
+ // bunch of related exceptions on our exception port so we can process
+ // then together. When we have multiple threads, we can get an exception
+ // per thread and they will come in consecutively. The main thread loop
+ // will start by calling mach_msg to without having the MACH_RCV_TIMEOUT
+ // flag set in the options, so we will wait forever for an exception on
+ // our exception port. After we get one exception, we then will use the
+ // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current
+ // exceptions for our process. After we have received the last pending
+ // exception, we will get a timeout which enables us to then notify
+ // our main thread that we have an exception bundle available. We then wait
+ // for the main thread to tell this exception thread to start trying to get
+ // exceptions messages again and we start again with a mach_msg read with
+ // infinite timeout.
+ DNBError err;
+ int stdout_fd = proc->GetStdoutFileDescriptor();
+ int stderr_fd = proc->GetStderrFileDescriptor();
+ if (stdout_fd == stderr_fd)
+ stderr_fd = -1;
+
+ while (stdout_fd >= 0 || stderr_fd >= 0) {
+ ::pthread_testcancel();
+
+ fd_set read_fds;
+ FD_ZERO(&read_fds);
+ if (stdout_fd >= 0)
+ FD_SET(stdout_fd, &read_fds);
+ if (stderr_fd >= 0)
+ FD_SET(stderr_fd, &read_fds);
+ int nfds = std::max<int>(stdout_fd, stderr_fd) + 1;
+
+ int num_set_fds = select(nfds, &read_fds, NULL, NULL, NULL);
+ DNBLogThreadedIf(LOG_PROCESS,
+ "select (nfds, &read_fds, NULL, NULL, NULL) => %d",
+ num_set_fds);
+
+ if (num_set_fds < 0) {
+ int select_errno = errno;
+ if (DNBLogCheckLogBit(LOG_PROCESS)) {
+ err.SetError(select_errno, DNBError::POSIX);
+ err.LogThreadedIfError(
+ "select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds);
+ }
+
+ switch (select_errno) {
+ case EAGAIN: // The kernel was (perhaps temporarily) unable to allocate
+ // the requested number of file descriptors, or we have
+ // non-blocking IO
+ break;
+ case EBADF: // One of the descriptor sets specified an invalid descriptor.
+ return NULL;
+ break;
+ case EINTR: // A signal was delivered before the time limit expired and
+ // before any of the selected events occurred.
+ case EINVAL: // The specified time limit is invalid. One of its components
+ // is negative or too large.
+ default: // Other unknown error
+ break;
+ }
+ } else if (num_set_fds == 0) {
+ } else {
+ char s[1024];
+ s[sizeof(s) - 1] = '\0'; // Ensure we have NULL termination
+ ssize_t bytes_read = 0;
+ if (stdout_fd >= 0 && FD_ISSET(stdout_fd, &read_fds)) {
+ do {
+ bytes_read = ::read(stdout_fd, s, sizeof(s) - 1);
+ if (bytes_read < 0) {
+ int read_errno = errno;
+ DNBLogThreadedIf(LOG_PROCESS,
+ "read (stdout_fd, ) => %zd errno: %d (%s)",
+ bytes_read, read_errno, strerror(read_errno));
+ } else if (bytes_read == 0) {
+ // EOF...
+ DNBLogThreadedIf(
+ LOG_PROCESS,
+ "read (stdout_fd, ) => %zd (reached EOF for child STDOUT)",
+ bytes_read);
+ stdout_fd = -1;
+ } else if (bytes_read > 0) {
+ proc->AppendSTDOUT(s, bytes_read);
+ }
+
+ } while (bytes_read > 0);
+ }
+
+ if (stderr_fd >= 0 && FD_ISSET(stderr_fd, &read_fds)) {
+ do {
+ bytes_read = ::read(stderr_fd, s, sizeof(s) - 1);
+ if (bytes_read < 0) {
+ int read_errno = errno;
+ DNBLogThreadedIf(LOG_PROCESS,
+ "read (stderr_fd, ) => %zd errno: %d (%s)",
+ bytes_read, read_errno, strerror(read_errno));
+ } else if (bytes_read == 0) {
+ // EOF...
+ DNBLogThreadedIf(
+ LOG_PROCESS,
+ "read (stderr_fd, ) => %zd (reached EOF for child STDERR)",
+ bytes_read);
+ stderr_fd = -1;
+ } else if (bytes_read > 0) {
+ proc->AppendSTDOUT(s, bytes_read);
+ }
+
+ } while (bytes_read > 0);
+ }
+ }
+ }
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (%p): thread exiting...",
+ __FUNCTION__, arg);
+ return NULL;
+}
+
+void MachProcess::SignalAsyncProfileData(const char *info) {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (%s) ...", __FUNCTION__, info);
+ PTHREAD_MUTEX_LOCKER(locker, m_profile_data_mutex);
+ m_profile_data.push_back(info);
+ m_events.SetEvents(eEventProfileDataAvailable);
+
+ // Wait for the event bit to reset if a reset ACK is requested
+ m_events.WaitForResetAck(eEventProfileDataAvailable);
+}
+
+size_t MachProcess::GetAsyncProfileData(char *buf, size_t buf_size) {
+ DNBLogThreadedIf(LOG_PROCESS, "MachProcess::%s (&%p[%llu]) ...", __FUNCTION__,
+ static_cast<void *>(buf), (uint64_t)buf_size);
+ PTHREAD_MUTEX_LOCKER(locker, m_profile_data_mutex);
+ if (m_profile_data.empty())
+ return 0;
+
+ size_t bytes_available = m_profile_data.front().size();
+ if (bytes_available > 0) {
+ if (bytes_available > buf_size) {
+ memcpy(buf, m_profile_data.front().data(), buf_size);
+ m_profile_data.front().erase(0, buf_size);
+ bytes_available = buf_size;
+ } else {
+ memcpy(buf, m_profile_data.front().data(), bytes_available);
+ m_profile_data.erase(m_profile_data.begin());
+ }
+ }
+ return bytes_available;
+}
+
+void *MachProcess::ProfileThread(void *arg) {
+ MachProcess *proc = (MachProcess *)arg;
+ DNBLogThreadedIf(LOG_PROCESS,
+ "MachProcess::%s ( arg = %p ) thread starting...",
+ __FUNCTION__, arg);
+
+#if defined(__APPLE__)
+ pthread_setname_np("performance profiling thread");
+#endif
+
+ while (proc->IsProfilingEnabled()) {
+ nub_state_t state = proc->GetState();
+ if (state == eStateRunning) {
+ std::string data =
+ proc->Task().GetProfileData(proc->GetProfileScanType());
+ if (!data.empty()) {
+ proc->SignalAsyncProfileData(data.c_str());
+ }
+ } else if ((state == eStateUnloaded) || (state == eStateDetached) ||
+ (state == eStateUnloaded)) {
+ // Done. Get out of this thread.
+ break;
+ }
+
+ // A simple way to set up the profile interval. We can also use select() or
+ // dispatch timer source if necessary.
+ usleep(proc->ProfileInterval());
+ }
+ return NULL;
+}
+
+pid_t MachProcess::AttachForDebug(pid_t pid, char *err_str, size_t err_len) {
+ // Clear out and clean up from any current state
+ Clear();
+ if (pid != 0) {
+ DNBError err;
+ // Make sure the process exists...
+ if (::getpgid(pid) < 0) {
+ err.SetErrorToErrno();
+ const char *err_cstr = err.AsString();
+ ::snprintf(err_str, err_len, "%s",
+ err_cstr ? err_cstr : "No such process");
+ DNBLogError ("MachProcess::AttachForDebug pid %d does not exist", pid);
+ return INVALID_NUB_PROCESS;
+ }
+
+ SetState(eStateAttaching);
+ m_pid = pid;
+ if (!m_task.StartExceptionThread(err)) {
+ const char *err_cstr = err.AsString();
+ ::snprintf(err_str, err_len, "%s",
+ err_cstr ? err_cstr : "unable to start the exception thread");
+ DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", pid);
+ DNBLogError ("MachProcess::AttachForDebug failed to start exception thread: %s", err_str);
+ m_pid = INVALID_NUB_PROCESS;
+ return INVALID_NUB_PROCESS;
+ }
+
+ errno = 0;
+ if (::ptrace(PT_ATTACHEXC, pid, 0, 0)) {
+ err.SetError(errno);
+ DNBLogError ("MachProcess::AttachForDebug failed to ptrace(PT_ATTACHEXC): %s", err.AsString());
+ } else {
+ err.Clear();
+ }
+
+ if (err.Success()) {
+ m_flags |= eMachProcessFlagsAttached;
+ // Sleep a bit to let the exception get received and set our process
+ // status
+ // to stopped.
+ ::usleep(250000);
+ DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", pid);
+ return m_pid;
+ } else {
+ ::snprintf(err_str, err_len, "%s", err.AsString());
+ DNBLogError ("MachProcess::AttachForDebug error: failed to attach to pid %d", pid);
+
+ struct kinfo_proc kinfo;
+ int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
+ size_t len = sizeof(struct kinfo_proc);
+ if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &kinfo, &len, NULL, 0) == 0 && len > 0) {
+ if (kinfo.kp_proc.p_flag & P_TRACED) {
+ ::snprintf(err_str, err_len, "%s - process %d is already being debugged", err.AsString(), pid);
+ DNBLogError ("MachProcess::AttachForDebug pid %d is already being debugged", pid);
+ }
+ }
+ }
+ }
+ return INVALID_NUB_PROCESS;
+}
+
+Genealogy::ThreadActivitySP
+MachProcess::GetGenealogyInfoForThread(nub_thread_t tid, bool &timed_out) {
+ return m_activities.GetGenealogyInfoForThread(m_pid, tid, m_thread_list,
+ m_task.TaskPort(), timed_out);
+}
+
+Genealogy::ProcessExecutableInfoSP
+MachProcess::GetGenealogyImageInfo(size_t idx) {
+ return m_activities.GetProcessExecutableInfosAtIndex(idx);
+}
+
+bool MachProcess::GetOSVersionNumbers(uint64_t *major, uint64_t *minor,
+ uint64_t *patch) {
+#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \
+ (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101000)
+ return false;
+#else
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ NSOperatingSystemVersion vers =
+ [[NSProcessInfo processInfo] operatingSystemVersion];
+ if (major)
+ *major = vers.majorVersion;
+ if (minor)
+ *minor = vers.minorVersion;
+ if (patch)
+ *patch = vers.patchVersion;
+
+ [pool drain];
+
+ return true;
+#endif
+}
+
+std::string MachProcess::GetMacCatalystVersionString() {
+ @autoreleasepool {
+ NSDictionary *version_info =
+ [NSDictionary dictionaryWithContentsOfFile:
+ @"/System/Library/CoreServices/SystemVersion.plist"];
+ NSString *version_value = [version_info objectForKey: @"iOSSupportVersion"];
+ if (const char *version_str = [version_value UTF8String])
+ return version_str;
+ }
+ return {};
+}
+
+// Do the process specific setup for attach. If this returns NULL, then there's
+// no
+// platform specific stuff to be done to wait for the attach. If you get
+// non-null,
+// pass that token to the CheckForProcess method, and then to
+// CleanupAfterAttach.
+
+// Call PrepareForAttach before attaching to a process that has not yet
+// launched
+// This returns a token that can be passed to CheckForProcess, and to
+// CleanupAfterAttach.
+// You should call CleanupAfterAttach to free the token, and do whatever other
+// cleanup seems good.
+
+const void *MachProcess::PrepareForAttach(const char *path,
+ nub_launch_flavor_t launch_flavor,
+ bool waitfor, DNBError &attach_err) {
+#if defined(WITH_SPRINGBOARD) || defined(WITH_BKS) || defined(WITH_FBS)
+ // Tell SpringBoard to halt the next launch of this application on startup.
+
+ if (!waitfor)
+ return NULL;
+
+ const char *app_ext = strstr(path, ".app");
+ const bool is_app =
+ app_ext != NULL && (app_ext[4] == '\0' || app_ext[4] == '/');
+ if (!is_app) {
+ DNBLogThreadedIf(
+ LOG_PROCESS,
+ "MachProcess::PrepareForAttach(): path '%s' doesn't contain .app, "
+ "we can't tell springboard to wait for launch...",
+ path);
+ return NULL;
+ }
+
+#if defined(WITH_FBS)
+ if (launch_flavor == eLaunchFlavorDefault)
+ launch_flavor = eLaunchFlavorFBS;
+ if (launch_flavor != eLaunchFlavorFBS)
+ return NULL;
+#elif defined(WITH_BKS)
+ if (launch_flavor == eLaunchFlavorDefault)
+ launch_flavor = eLaunchFlavorBKS;
+ if (launch_flavor != eLaunchFlavorBKS)
+ return NULL;
+#elif defined(WITH_SPRINGBOARD)
+ if (launch_flavor == eLaunchFlavorDefault)
+ launch_flavor = eLaunchFlavorSpringBoard;
+ if (launch_flavor != eLaunchFlavorSpringBoard)
+ return NULL;
+#endif
+
+ std::string app_bundle_path(path, app_ext + strlen(".app"));
+
+ CFStringRef bundleIDCFStr =
+ CopyBundleIDForPath(app_bundle_path.c_str(), attach_err);
+ std::string bundleIDStr;
+ CFString::UTF8(bundleIDCFStr, bundleIDStr);
+ DNBLogThreadedIf(LOG_PROCESS,
+ "CopyBundleIDForPath (%s, err_str) returned @\"%s\"",
+ app_bundle_path.c_str(), bundleIDStr.c_str());
+
+ if (bundleIDCFStr == NULL) {
+ return NULL;
+ }
+
+#if defined(WITH_FBS)
+ if (launch_flavor == eLaunchFlavorFBS) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ NSString *stdio_path = nil;
+ NSFileManager *file_manager = [NSFileManager defaultManager];
+ const char *null_path = "/dev/null";
+ stdio_path =
+ [file_manager stringWithFileSystemRepresentation:null_path
+ length:strlen(null_path)];
+
+ NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
+ NSMutableDictionary *options = [NSMutableDictionary dictionary];
+
+ DNBLogThreadedIf(LOG_PROCESS, "Calling BKSSystemService openApplication: "
+ "@\"%s\",options include stdio path: \"%s\", "
+ "BKSDebugOptionKeyDebugOnNextLaunch & "
+ "BKSDebugOptionKeyWaitForDebugger )",
+ bundleIDStr.c_str(), null_path);
+
+ [debug_options setObject:stdio_path
+ forKey:FBSDebugOptionKeyStandardOutPath];
+ [debug_options setObject:stdio_path
+ forKey:FBSDebugOptionKeyStandardErrorPath];
+ [debug_options setObject:[NSNumber numberWithBool:YES]
+ forKey:FBSDebugOptionKeyWaitForDebugger];
+ [debug_options setObject:[NSNumber numberWithBool:YES]
+ forKey:FBSDebugOptionKeyDebugOnNextLaunch];
+
+ [options setObject:debug_options
+ forKey:FBSOpenApplicationOptionKeyDebuggingOptions];
+
+ FBSSystemService *system_service = [[FBSSystemService alloc] init];
+
+ mach_port_t client_port = [system_service createClientPort];
+ __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
+ __block FBSOpenApplicationErrorCode attach_error_code =
+ FBSOpenApplicationErrorCodeNone;
+
+ NSString *bundleIDNSStr = (NSString *)bundleIDCFStr;
+
+ [system_service openApplication:bundleIDNSStr
+ options:options
+ clientPort:client_port
+ withResult:^(NSError *error) {
+ // The system service will cleanup the client port we
+ // created for us.
+ if (error)
+ attach_error_code =
+ (FBSOpenApplicationErrorCode)[error code];
+
+ [system_service release];
+ dispatch_semaphore_signal(semaphore);
+ }];
+
+ const uint32_t timeout_secs = 9;
+
+ dispatch_time_t timeout =
+ dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC);
+
+ long success = dispatch_semaphore_wait(semaphore, timeout) == 0;
+
+ if (!success) {
+ DNBLogError("timed out trying to launch %s.", bundleIDStr.c_str());
+ attach_err.SetErrorString(
+ "debugserver timed out waiting for openApplication to complete.");
+ attach_err.SetError(OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic);
+ } else if (attach_error_code != FBSOpenApplicationErrorCodeNone) {
+ std::string empty_str;
+ SetFBSError(attach_error_code, empty_str, attach_err);
+ DNBLogError("unable to launch the application with CFBundleIdentifier "
+ "'%s' bks_error = %ld",
+ bundleIDStr.c_str(), (NSInteger)attach_error_code);
+ }
+ dispatch_release(semaphore);
+ [pool drain];
+ }
+#endif
+#if defined(WITH_BKS)
+ if (launch_flavor == eLaunchFlavorBKS) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ NSString *stdio_path = nil;
+ NSFileManager *file_manager = [NSFileManager defaultManager];
+ const char *null_path = "/dev/null";
+ stdio_path =
+ [file_manager stringWithFileSystemRepresentation:null_path
+ length:strlen(null_path)];
+
+ NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
+ NSMutableDictionary *options = [NSMutableDictionary dictionary];
+
+ DNBLogThreadedIf(LOG_PROCESS, "Calling BKSSystemService openApplication: "
+ "@\"%s\",options include stdio path: \"%s\", "
+ "BKSDebugOptionKeyDebugOnNextLaunch & "
+ "BKSDebugOptionKeyWaitForDebugger )",
+ bundleIDStr.c_str(), null_path);
+
+ [debug_options setObject:stdio_path
+ forKey:BKSDebugOptionKeyStandardOutPath];
+ [debug_options setObject:stdio_path
+ forKey:BKSDebugOptionKeyStandardErrorPath];
+ [debug_options setObject:[NSNumber numberWithBool:YES]
+ forKey:BKSDebugOptionKeyWaitForDebugger];
+ [debug_options setObject:[NSNumber numberWithBool:YES]
+ forKey:BKSDebugOptionKeyDebugOnNextLaunch];
+
+ [options setObject:debug_options
+ forKey:BKSOpenApplicationOptionKeyDebuggingOptions];
+
+ BKSSystemService *system_service = [[BKSSystemService alloc] init];
+
+ mach_port_t client_port = [system_service createClientPort];
+ __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
+ __block BKSOpenApplicationErrorCode attach_error_code =
+ BKSOpenApplicationErrorCodeNone;
+
+ NSString *bundleIDNSStr = (NSString *)bundleIDCFStr;
+
+ [system_service openApplication:bundleIDNSStr
+ options:options
+ clientPort:client_port
+ withResult:^(NSError *error) {
+ // The system service will cleanup the client port we
+ // created for us.
+ if (error)
+ attach_error_code =
+ (BKSOpenApplicationErrorCode)[error code];
+
+ [system_service release];
+ dispatch_semaphore_signal(semaphore);
+ }];
+
+ const uint32_t timeout_secs = 9;
+
+ dispatch_time_t timeout =
+ dispatch_time(DISPATCH_TIME_NOW, timeout_secs * NSEC_PER_SEC);
+
+ long success = dispatch_semaphore_wait(semaphore, timeout) == 0;
+
+ if (!success) {
+ DNBLogError("timed out trying to launch %s.", bundleIDStr.c_str());
+ attach_err.SetErrorString(
+ "debugserver timed out waiting for openApplication to complete.");
+ attach_err.SetError(OPEN_APPLICATION_TIMEOUT_ERROR, DNBError::Generic);
+ } else if (attach_error_code != BKSOpenApplicationErrorCodeNone) {
+ std::string empty_str;
+ SetBKSError(attach_error_code, empty_str, attach_err);
+ DNBLogError("unable to launch the application with CFBundleIdentifier "
+ "'%s' bks_error = %ld",
+ bundleIDStr.c_str(), attach_error_code);
+ }
+ dispatch_release(semaphore);
+ [pool drain];
+ }
+#endif
+
+#if defined(WITH_SPRINGBOARD)
+ if (launch_flavor == eLaunchFlavorSpringBoard) {
+ SBSApplicationLaunchError sbs_error = 0;
+
+ const char *stdout_err = "/dev/null";
+ CFString stdio_path;
+ stdio_path.SetFileSystemRepresentation(stdout_err);
+
+ DNBLogThreadedIf(LOG_PROCESS, "SBSLaunchApplicationForDebugging ( @\"%s\" "
+ ", NULL, NULL, NULL, @\"%s\", @\"%s\", "
+ "SBSApplicationDebugOnNextLaunch | "
+ "SBSApplicationLaunchWaitForDebugger )",
+ bundleIDStr.c_str(), stdout_err, stdout_err);
+
+ sbs_error = SBSLaunchApplicationForDebugging(
+ bundleIDCFStr,
+ (CFURLRef)NULL, // openURL
+ NULL, // launch_argv.get(),
+ NULL, // launch_envp.get(), // CFDictionaryRef environment
+ stdio_path.get(), stdio_path.get(),
+ SBSApplicationDebugOnNextLaunch | SBSApplicationLaunchWaitForDebugger);
+
+ if (sbs_error != SBSApplicationLaunchErrorSuccess) {
+ attach_err.SetError(sbs_error, DNBError::SpringBoard);
+ return NULL;
+ }
+ }
+#endif // WITH_SPRINGBOARD
+
+ DNBLogThreadedIf(LOG_PROCESS, "Successfully set DebugOnNextLaunch.");
+ return bundleIDCFStr;
+#else // !(defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined
+ // (WITH_FBS))
+ return NULL;
+#endif
+}
+
+// Pass in the token you got from PrepareForAttach. If there is a process
+// for that token, then the pid will be returned, otherwise INVALID_NUB_PROCESS
+// will be returned.
+
+nub_process_t MachProcess::CheckForProcess(const void *attach_token,
+ nub_launch_flavor_t launch_flavor) {
+ if (attach_token == NULL)
+ return INVALID_NUB_PROCESS;
+
+#if defined(WITH_FBS)
+ if (launch_flavor == eLaunchFlavorFBS) {
+ NSString *bundleIDNSStr = (NSString *)attach_token;
+ FBSSystemService *systemService = [[FBSSystemService alloc] init];
+ pid_t pid = [systemService pidForApplication:bundleIDNSStr];
+ [systemService release];
+ if (pid == 0)
+ return INVALID_NUB_PROCESS;
+ else
+ return pid;
+ }
+#endif
+
+#if defined(WITH_BKS)
+ if (launch_flavor == eLaunchFlavorBKS) {
+ NSString *bundleIDNSStr = (NSString *)attach_token;
+ BKSSystemService *systemService = [[BKSSystemService alloc] init];
+ pid_t pid = [systemService pidForApplication:bundleIDNSStr];
+ [systemService release];
+ if (pid == 0)
+ return INVALID_NUB_PROCESS;
+ else
+ return pid;
+ }
+#endif
+
+#if defined(WITH_SPRINGBOARD)
+ if (launch_flavor == eLaunchFlavorSpringBoard) {
+ CFStringRef bundleIDCFStr = (CFStringRef)attach_token;
+ Boolean got_it;
+ nub_process_t attach_pid;
+ got_it = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &attach_pid);
+ if (got_it)
+ return attach_pid;
+ else
+ return INVALID_NUB_PROCESS;
+ }
+#endif
+ return INVALID_NUB_PROCESS;
+}
+
+// Call this to clean up after you have either attached or given up on the
+// attach.
+// Pass true for success if you have attached, false if you have not.
+// The token will also be freed at this point, so you can't use it after calling
+// this method.
+
+void MachProcess::CleanupAfterAttach(const void *attach_token,
+ nub_launch_flavor_t launch_flavor,
+ bool success, DNBError &err_str) {
+ if (attach_token == NULL)
+ return;
+
+#if defined(WITH_FBS)
+ if (launch_flavor == eLaunchFlavorFBS) {
+ if (!success) {
+ FBSCleanupAfterAttach(attach_token, err_str);
+ }
+ CFRelease((CFStringRef)attach_token);
+ }
+#endif
+
+#if defined(WITH_BKS)
+
+ if (launch_flavor == eLaunchFlavorBKS) {
+ if (!success) {
+ BKSCleanupAfterAttach(attach_token, err_str);
+ }
+ CFRelease((CFStringRef)attach_token);
+ }
+#endif
+
+#if defined(WITH_SPRINGBOARD)
+ // Tell SpringBoard to cancel the debug on next launch of this application
+ // if we failed to attach
+ if (launch_flavor == eMachProcessFlagsUsingSpringBoard) {
+ if (!success) {
+ SBSApplicationLaunchError sbs_error = 0;
+ CFStringRef bundleIDCFStr = (CFStringRef)attach_token;
+
+ sbs_error = SBSLaunchApplicationForDebugging(
+ bundleIDCFStr, (CFURLRef)NULL, NULL, NULL, NULL, NULL,
+ SBSApplicationCancelDebugOnNextLaunch);
+
+ if (sbs_error != SBSApplicationLaunchErrorSuccess) {
+ err_str.SetError(sbs_error, DNBError::SpringBoard);
+ return;
+ }
+ }
+
+ CFRelease((CFStringRef)attach_token);
+ }
+#endif
+}
+
+pid_t MachProcess::LaunchForDebug(
+ const char *path, char const *argv[], char const *envp[],
+ const char *working_directory, // NULL => don't change, non-NULL => set
+ // working directory for inferior to this
+ const char *stdin_path, const char *stdout_path, const char *stderr_path,
+ bool no_stdio, nub_launch_flavor_t launch_flavor, int disable_aslr,
+ const char *event_data, DNBError &launch_err) {
+ // Clear out and clean up from any current state
+ Clear();
+
+ DNBLogThreadedIf(LOG_PROCESS,
+ "%s( path = '%s', argv = %p, envp = %p, "
+ "launch_flavor = %u, disable_aslr = %d )",
+ __FUNCTION__, path, static_cast<const void *>(argv),
+ static_cast<const void *>(envp), launch_flavor,
+ disable_aslr);
+
+ // Fork a child process for debugging
+ SetState(eStateLaunching);
+
+ switch (launch_flavor) {
+ case eLaunchFlavorForkExec:
+ m_pid = MachProcess::ForkChildForPTraceDebugging(path, argv, envp, this,
+ launch_err);
+ break;
+#ifdef WITH_FBS
+ case eLaunchFlavorFBS: {
+ const char *app_ext = strstr(path, ".app");
+ if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/')) {
+ std::string app_bundle_path(path, app_ext + strlen(".app"));
+ m_flags |= (eMachProcessFlagsUsingFBS | eMachProcessFlagsBoardCalculated);
+ if (BoardServiceLaunchForDebug(app_bundle_path.c_str(), argv, envp,
+ no_stdio, disable_aslr, event_data,
+ launch_err) != 0)
+ return m_pid; // A successful SBLaunchForDebug() returns and assigns a
+ // non-zero m_pid.
+ else
+ break; // We tried a FBS launch, but didn't succeed lets get out
+ }
+ } break;
+#endif
+#ifdef WITH_BKS
+ case eLaunchFlavorBKS: {
+ const char *app_ext = strstr(path, ".app");
+ if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/')) {
+ std::string app_bundle_path(path, app_ext + strlen(".app"));
+ m_flags |= (eMachProcessFlagsUsingBKS | eMachProcessFlagsBoardCalculated);
+ if (BoardServiceLaunchForDebug(app_bundle_path.c_str(), argv, envp,
+ no_stdio, disable_aslr, event_data,
+ launch_err) != 0)
+ return m_pid; // A successful SBLaunchForDebug() returns and assigns a
+ // non-zero m_pid.
+ else
+ break; // We tried a BKS launch, but didn't succeed lets get out
+ }
+ } break;
+#endif
+#ifdef WITH_SPRINGBOARD
+
+ case eLaunchFlavorSpringBoard: {
+ // .../whatever.app/whatever ?
+ // Or .../com.apple.whatever.app/whatever -- be careful of ".app" in
+ // "com.apple.whatever" here
+ const char *app_ext = strstr(path, ".app/");
+ if (app_ext == NULL) {
+ // .../whatever.app ?
+ int len = strlen(path);
+ if (len > 5) {
+ if (strcmp(path + len - 4, ".app") == 0) {
+ app_ext = path + len - 4;
+ }
+ }
+ }
+ if (app_ext) {
+ std::string app_bundle_path(path, app_ext + strlen(".app"));
+ if (SBLaunchForDebug(app_bundle_path.c_str(), argv, envp, no_stdio,
+ disable_aslr, launch_err) != 0)
+ return m_pid; // A successful SBLaunchForDebug() returns and assigns a
+ // non-zero m_pid.
+ else
+ break; // We tried a springboard launch, but didn't succeed lets get out
+ }
+ } break;
+
+#endif
+
+ case eLaunchFlavorPosixSpawn:
+ m_pid = MachProcess::PosixSpawnChildForPTraceDebugging(
+ path, DNBArchProtocol::GetArchitecture(), argv, envp, working_directory,
+ stdin_path, stdout_path, stderr_path, no_stdio, this, disable_aslr,
+ launch_err);
+ break;
+
+ default:
+ // Invalid launch
+ launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic);
+ return INVALID_NUB_PROCESS;
+ }
+
+ if (m_pid == INVALID_NUB_PROCESS) {
+ // If we don't have a valid process ID and no one has set the error,
+ // then return a generic error
+ if (launch_err.Success())
+ launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic);
+ } else {
+ m_path = path;
+ size_t i;
+ char const *arg;
+ for (i = 0; (arg = argv[i]) != NULL; i++)
+ m_args.push_back(arg);
+
+ m_task.StartExceptionThread(launch_err);
+ if (launch_err.Fail()) {
+ if (launch_err.AsString() == NULL)
+ launch_err.SetErrorString("unable to start the exception thread");
+ DNBLog("Could not get inferior's Mach exception port, sending ptrace "
+ "PT_KILL and exiting.");
+ ::ptrace(PT_KILL, m_pid, 0, 0);
+ m_pid = INVALID_NUB_PROCESS;
+ return INVALID_NUB_PROCESS;
+ }
+
+ StartSTDIOThread();
+
+ if (launch_flavor == eLaunchFlavorPosixSpawn) {
+
+ SetState(eStateAttaching);
+ errno = 0;
+ int err = ::ptrace(PT_ATTACHEXC, m_pid, 0, 0);
+ if (err == 0) {
+ m_flags |= eMachProcessFlagsAttached;
+ DNBLogThreadedIf(LOG_PROCESS, "successfully spawned pid %d", m_pid);
+ launch_err.Clear();
+ } else {
+ SetState(eStateExited);
+ DNBError ptrace_err(errno, DNBError::POSIX);
+ DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to spawned pid "
+ "%d (err = %i, errno = %i (%s))",
+ m_pid, err, ptrace_err.Status(),
+ ptrace_err.AsString());
+ launch_err.SetError(NUB_GENERIC_ERROR, DNBError::Generic);
+ }
+ } else {
+ launch_err.Clear();
+ }
+ }
+ return m_pid;
+}
+
+pid_t MachProcess::PosixSpawnChildForPTraceDebugging(
+ const char *path, cpu_type_t cpu_type, char const *argv[],
+ char const *envp[], const char *working_directory, const char *stdin_path,
+ const char *stdout_path, const char *stderr_path, bool no_stdio,
+ MachProcess *process, int disable_aslr, DNBError &err) {
+ posix_spawnattr_t attr;
+ short flags;
+ DNBLogThreadedIf(LOG_PROCESS,
+ "%s ( path='%s', argv=%p, envp=%p, "
+ "working_dir=%s, stdin=%s, stdout=%s "
+ "stderr=%s, no-stdio=%i)",
+ __FUNCTION__, path, static_cast<const void *>(argv),
+ static_cast<const void *>(envp), working_directory,
+ stdin_path, stdout_path, stderr_path, no_stdio);
+
+ err.SetError(::posix_spawnattr_init(&attr), DNBError::POSIX);
+ if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+ err.LogThreaded("::posix_spawnattr_init ( &attr )");
+ if (err.Fail())
+ return INVALID_NUB_PROCESS;
+
+ flags = POSIX_SPAWN_START_SUSPENDED | POSIX_SPAWN_SETSIGDEF |
+ POSIX_SPAWN_SETSIGMASK;
+ if (disable_aslr)
+ flags |= _POSIX_SPAWN_DISABLE_ASLR;
+
+ sigset_t no_signals;
+ sigset_t all_signals;
+ sigemptyset(&no_signals);
+ sigfillset(&all_signals);
+ ::posix_spawnattr_setsigmask(&attr, &no_signals);
+ ::posix_spawnattr_setsigdefault(&attr, &all_signals);
+
+ err.SetError(::posix_spawnattr_setflags(&attr, flags), DNBError::POSIX);
+ if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+ err.LogThreaded(
+ "::posix_spawnattr_setflags ( &attr, POSIX_SPAWN_START_SUSPENDED%s )",
+ flags & _POSIX_SPAWN_DISABLE_ASLR ? " | _POSIX_SPAWN_DISABLE_ASLR"
+ : "");
+ if (err.Fail())
+ return INVALID_NUB_PROCESS;
+
+// Don't do this on SnowLeopard, _sometimes_ the TASK_BASIC_INFO will fail
+// and we will fail to continue with our process...
+
+// On SnowLeopard we should set "DYLD_NO_PIE" in the inferior environment....
+
+#if !defined(__arm__)
+
+ // We don't need to do this for ARM, and we really shouldn't now that we
+ // have multiple CPU subtypes and no posix_spawnattr call that allows us
+ // to set which CPU subtype to launch...
+ if (cpu_type != 0) {
+ size_t ocount = 0;
+ err.SetError(::posix_spawnattr_setbinpref_np(&attr, 1, &cpu_type, &ocount),
+ DNBError::POSIX);
+ if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+ err.LogThreaded("::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = "
+ "0x%8.8x, count => %llu )",
+ cpu_type, (uint64_t)ocount);
+
+ if (err.Fail() != 0 || ocount != 1)
+ return INVALID_NUB_PROCESS;
+ }
+#endif
+
+ PseudoTerminal pty;
+
+ posix_spawn_file_actions_t file_actions;
+ err.SetError(::posix_spawn_file_actions_init(&file_actions), DNBError::POSIX);
+ int file_actions_valid = err.Success();
+ if (!file_actions_valid || DNBLogCheckLogBit(LOG_PROCESS))
+ err.LogThreaded("::posix_spawn_file_actions_init ( &file_actions )");
+ int pty_error = -1;
+ pid_t pid = INVALID_NUB_PROCESS;
+ if (file_actions_valid) {
+ if (stdin_path == NULL && stdout_path == NULL && stderr_path == NULL &&
+ !no_stdio) {
+ pty_error = pty.OpenFirstAvailableMaster(O_RDWR | O_NOCTTY);
+ if (pty_error == PseudoTerminal::success) {
+ stdin_path = stdout_path = stderr_path = pty.SlaveName();
+ }
+ }
+
+ // if no_stdio or std paths not supplied, then route to "/dev/null".
+ if (no_stdio || stdin_path == NULL || stdin_path[0] == '\0')
+ stdin_path = "/dev/null";
+ if (no_stdio || stdout_path == NULL || stdout_path[0] == '\0')
+ stdout_path = "/dev/null";
+ if (no_stdio || stderr_path == NULL || stderr_path[0] == '\0')
+ stderr_path = "/dev/null";
+
+ err.SetError(::posix_spawn_file_actions_addopen(&file_actions, STDIN_FILENO,
+ stdin_path,
+ O_RDONLY | O_NOCTTY, 0),
+ DNBError::POSIX);
+ if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+ err.LogThreaded("::posix_spawn_file_actions_addopen (&file_actions, "
+ "filedes=STDIN_FILENO, path='%s')",
+ stdin_path);
+
+ err.SetError(::posix_spawn_file_actions_addopen(
+ &file_actions, STDOUT_FILENO, stdout_path,
+ O_WRONLY | O_NOCTTY | O_CREAT, 0640),
+ DNBError::POSIX);
+ if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+ err.LogThreaded("::posix_spawn_file_actions_addopen (&file_actions, "
+ "filedes=STDOUT_FILENO, path='%s')",
+ stdout_path);
+
+ err.SetError(::posix_spawn_file_actions_addopen(
+ &file_actions, STDERR_FILENO, stderr_path,
+ O_WRONLY | O_NOCTTY | O_CREAT, 0640),
+ DNBError::POSIX);
+ if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+ err.LogThreaded("::posix_spawn_file_actions_addopen (&file_actions, "
+ "filedes=STDERR_FILENO, path='%s')",
+ stderr_path);
+
+ // TODO: Verify if we can set the working directory back immediately
+ // after the posix_spawnp call without creating a race condition???
+ if (working_directory)
+ ::chdir(working_directory);
+
+ err.SetError(::posix_spawnp(&pid, path, &file_actions, &attr,
+ const_cast<char *const *>(argv),
+ const_cast<char *const *>(envp)),
+ DNBError::POSIX);
+ if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+ err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = "
+ "%p, attr = %p, argv = %p, envp = %p )",
+ pid, path, &file_actions, &attr, argv, envp);
+ } else {
+ // TODO: Verify if we can set the working directory back immediately
+ // after the posix_spawnp call without creating a race condition???
+ if (working_directory)
+ ::chdir(working_directory);
+
+ err.SetError(::posix_spawnp(&pid, path, NULL, &attr,
+ const_cast<char *const *>(argv),
+ const_cast<char *const *>(envp)),
+ DNBError::POSIX);
+ if (err.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+ err.LogThreaded("::posix_spawnp ( pid => %i, path = '%s', file_actions = "
+ "%p, attr = %p, argv = %p, envp = %p )",
+ pid, path, NULL, &attr, argv, envp);
+ }
+
+ // We have seen some cases where posix_spawnp was returning a valid
+ // looking pid even when an error was returned, so clear it out
+ if (err.Fail())
+ pid = INVALID_NUB_PROCESS;
+
+ if (pty_error == 0) {
+ if (process != NULL) {
+ int master_fd = pty.ReleaseMasterFD();
+ process->SetChildFileDescriptors(master_fd, master_fd, master_fd);
+ }
+ }
+ ::posix_spawnattr_destroy(&attr);
+
+ if (pid != INVALID_NUB_PROCESS) {
+ cpu_type_t pid_cpu_type = MachProcess::GetCPUTypeForLocalProcess(pid);
+ DNBLogThreadedIf(LOG_PROCESS,
+ "MachProcess::%s ( ) pid=%i, cpu_type=0x%8.8x",
+ __FUNCTION__, pid, pid_cpu_type);
+ if (pid_cpu_type)
+ DNBArchProtocol::SetArchitecture(pid_cpu_type);
+ }
+
+ if (file_actions_valid) {
+ DNBError err2;
+ err2.SetError(::posix_spawn_file_actions_destroy(&file_actions),
+ DNBError::POSIX);
+ if (err2.Fail() || DNBLogCheckLogBit(LOG_PROCESS))
+ err2.LogThreaded("::posix_spawn_file_actions_destroy ( &file_actions )");
+ }
+
+ return pid;
+}
+
+uint32_t MachProcess::GetCPUTypeForLocalProcess(pid_t pid) {
+ int mib[CTL_MAXNAME] = {
+ 0,
+ };
+ size_t len = CTL_MAXNAME;
+ if (::sysctlnametomib("sysctl.proc_cputype", mib, &len))
+ return 0;
+
+ mib[len] = pid;
+ len++;
+
+ cpu_type_t cpu;
+ size_t cpu_len = sizeof(cpu);
+ if (::sysctl(mib, static_cast<u_int>(len), &cpu, &cpu_len, 0, 0))
+ cpu = 0;
+ return cpu;
+}
+
+pid_t MachProcess::ForkChildForPTraceDebugging(const char *path,
+ char const *argv[],
+ char const *envp[],
+ MachProcess *process,
+ DNBError &launch_err) {
+ PseudoTerminal::Status pty_error = PseudoTerminal::success;
+
+ // Use a fork that ties the child process's stdin/out/err to a pseudo
+ // terminal so we can read it in our MachProcess::STDIOThread
+ // as unbuffered io.
+ PseudoTerminal pty;
+ pid_t pid = pty.Fork(pty_error);
+
+ if (pid < 0) {
+ //--------------------------------------------------------------
+ // Status during fork.
+ //--------------------------------------------------------------
+ return pid;
+ } else if (pid == 0) {
+ //--------------------------------------------------------------
+ // Child process
+ //--------------------------------------------------------------
+ ::ptrace(PT_TRACE_ME, 0, 0, 0); // Debug this process
+ ::ptrace(PT_SIGEXC, 0, 0, 0); // Get BSD signals as mach exceptions
+
+ // If our parent is setgid, lets make sure we don't inherit those
+ // extra powers due to nepotism.
+ if (::setgid(getgid()) == 0) {
+
+ // Let the child have its own process group. We need to execute
+ // this call in both the child and parent to avoid a race condition
+ // between the two processes.
+ ::setpgid(0, 0); // Set the child process group to match its pid
+
+ // Sleep a bit to before the exec call
+ ::sleep(1);
+
+ // Turn this process into
+ ::execv(path, const_cast<char *const *>(argv));
+ }
+ // Exit with error code. Child process should have taken
+ // over in above exec call and if the exec fails it will
+ // exit the child process below.
+ ::exit(127);
+ } else {
+ //--------------------------------------------------------------
+ // Parent process
+ //--------------------------------------------------------------
+ // Let the child have its own process group. We need to execute
+ // this call in both the child and parent to avoid a race condition
+ // between the two processes.
+ ::setpgid(pid, pid); // Set the child process group to match its pid
+
+ if (process != NULL) {
+ // Release our master pty file descriptor so the pty class doesn't
+ // close it and so we can continue to use it in our STDIO thread
+ int master_fd = pty.ReleaseMasterFD();
+ process->SetChildFileDescriptors(master_fd, master_fd, master_fd);
+ }
+ }
+ return pid;
+}
+
+#if defined(WITH_SPRINGBOARD) || defined(WITH_BKS) || defined(WITH_FBS)
+// This returns a CFRetained pointer to the Bundle ID for app_bundle_path,
+// or NULL if there was some problem getting the bundle id.
+static CFStringRef CopyBundleIDForPath(const char *app_bundle_path,
+ DNBError &err_str) {
+ CFBundle bundle(app_bundle_path);
+ CFStringRef bundleIDCFStr = bundle.GetIdentifier();
+ std::string bundleID;
+ if (CFString::UTF8(bundleIDCFStr, bundleID) == NULL) {
+ struct stat app_bundle_stat;
+ char err_msg[PATH_MAX];
+
+ if (::stat(app_bundle_path, &app_bundle_stat) < 0) {
+ err_str.SetError(errno, DNBError::POSIX);
+ snprintf(err_msg, sizeof(err_msg), "%s: \"%s\"", err_str.AsString(),
+ app_bundle_path);
+ err_str.SetErrorString(err_msg);
+ DNBLogThreadedIf(LOG_PROCESS, "%s() error: %s", __FUNCTION__, err_msg);
+ } else {
+ err_str.SetError(-1, DNBError::Generic);
+ snprintf(err_msg, sizeof(err_msg),
+ "failed to extract CFBundleIdentifier from %s", app_bundle_path);
+ err_str.SetErrorString(err_msg);
+ DNBLogThreadedIf(
+ LOG_PROCESS,
+ "%s() error: failed to extract CFBundleIdentifier from '%s'",
+ __FUNCTION__, app_bundle_path);
+ }
+ return NULL;
+ }
+
+ DNBLogThreadedIf(LOG_PROCESS, "%s() extracted CFBundleIdentifier: %s",
+ __FUNCTION__, bundleID.c_str());
+ CFRetain(bundleIDCFStr);
+
+ return bundleIDCFStr;
+}
+#endif // #if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) || defined
+ // (WITH_FBS)
+#ifdef WITH_SPRINGBOARD
+
+pid_t MachProcess::SBLaunchForDebug(const char *path, char const *argv[],
+ char const *envp[], bool no_stdio,
+ bool disable_aslr, DNBError &launch_err) {
+ // Clear out and clean up from any current state
+ Clear();
+
+ DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path);
+
+ // Fork a child process for debugging
+ SetState(eStateLaunching);
+ m_pid = MachProcess::SBForkChildForPTraceDebugging(path, argv, envp, no_stdio,
+ this, launch_err);
+ if (m_pid != 0) {
+ m_path = path;
+ size_t i;
+ char const *arg;
+ for (i = 0; (arg = argv[i]) != NULL; i++)
+ m_args.push_back(arg);
+ m_task.StartExceptionThread(launch_err);
+
+ if (launch_err.Fail()) {
+ if (launch_err.AsString() == NULL)
+ launch_err.SetErrorString("unable to start the exception thread");
+ DNBLog("Could not get inferior's Mach exception port, sending ptrace "
+ "PT_KILL and exiting.");
+ ::ptrace(PT_KILL, m_pid, 0, 0);
+ m_pid = INVALID_NUB_PROCESS;
+ return INVALID_NUB_PROCESS;
+ }
+
+ StartSTDIOThread();
+ SetState(eStateAttaching);
+ int err = ::ptrace(PT_ATTACHEXC, m_pid, 0, 0);
+ if (err == 0) {
+ m_flags |= eMachProcessFlagsAttached;
+ DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", m_pid);
+ } else {
+ SetState(eStateExited);
+ DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", m_pid);
+ }
+ }
+ return m_pid;
+}
+
+#include <servers/bootstrap.h>
+
+pid_t MachProcess::SBForkChildForPTraceDebugging(
+ const char *app_bundle_path, char const *argv[], char const *envp[],
+ bool no_stdio, MachProcess *process, DNBError &launch_err) {
+ DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__,
+ app_bundle_path, process);
+ CFAllocatorRef alloc = kCFAllocatorDefault;
+
+ if (argv[0] == NULL)
+ return INVALID_NUB_PROCESS;
+
+ size_t argc = 0;
+ // Count the number of arguments
+ while (argv[argc] != NULL)
+ argc++;
+
+ // Enumerate the arguments
+ size_t first_launch_arg_idx = 1;
+ CFReleaser<CFMutableArrayRef> launch_argv;
+
+ if (argv[first_launch_arg_idx]) {
+ size_t launch_argc = argc > 0 ? argc - 1 : 0;
+ launch_argv.reset(
+ ::CFArrayCreateMutable(alloc, launch_argc, &kCFTypeArrayCallBacks));
+ size_t i;
+ char const *arg;
+ CFString launch_arg;
+ for (i = first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL);
+ i++) {
+ launch_arg.reset(
+ ::CFStringCreateWithCString(alloc, arg, kCFStringEncodingUTF8));
+ if (launch_arg.get() != NULL)
+ CFArrayAppendValue(launch_argv.get(), launch_arg.get());
+ else
+ break;
+ }
+ }
+
+ // Next fill in the arguments dictionary. Note, the envp array is of the form
+ // Variable=value but SpringBoard wants a CF dictionary. So we have to
+ // convert
+ // this here.
+
+ CFReleaser<CFMutableDictionaryRef> launch_envp;
+
+ if (envp[0]) {
+ launch_envp.reset(
+ ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+ const char *value;
+ int name_len;
+ CFString name_string, value_string;
+
+ for (int i = 0; envp[i] != NULL; i++) {
+ value = strstr(envp[i], "=");
+
+ // If the name field is empty or there's no =, skip it. Somebody's
+ // messing with us.
+ if (value == NULL || value == envp[i])
+ continue;
+
+ name_len = value - envp[i];
+
+ // Now move value over the "="
+ value++;
+
+ name_string.reset(
+ ::CFStringCreateWithBytes(alloc, (const UInt8 *)envp[i], name_len,
+ kCFStringEncodingUTF8, false));
+ value_string.reset(
+ ::CFStringCreateWithCString(alloc, value, kCFStringEncodingUTF8));
+ CFDictionarySetValue(launch_envp.get(), name_string.get(),
+ value_string.get());
+ }
+ }
+
+ CFString stdio_path;
+
+ PseudoTerminal pty;
+ if (!no_stdio) {
+ PseudoTerminal::Status pty_err =
+ pty.OpenFirstAvailableMaster(O_RDWR | O_NOCTTY);
+ if (pty_err == PseudoTerminal::success) {
+ const char *slave_name = pty.SlaveName();
+ DNBLogThreadedIf(LOG_PROCESS,
+ "%s() successfully opened master pty, slave is %s",
+ __FUNCTION__, slave_name);
+ if (slave_name && slave_name[0]) {
+ ::chmod(slave_name, S_IRWXU | S_IRWXG | S_IRWXO);
+ stdio_path.SetFileSystemRepresentation(slave_name);
+ }
+ }
+ }
+
+ if (stdio_path.get() == NULL) {
+ stdio_path.SetFileSystemRepresentation("/dev/null");
+ }
+
+ CFStringRef bundleIDCFStr = CopyBundleIDForPath(app_bundle_path, launch_err);
+ if (bundleIDCFStr == NULL)
+ return INVALID_NUB_PROCESS;
+
+ // This is just for logging:
+ std::string bundleID;
+ CFString::UTF8(bundleIDCFStr, bundleID);
+
+ DNBLogThreadedIf(LOG_PROCESS, "%s() serialized launch arg array",
+ __FUNCTION__);
+
+ // Find SpringBoard
+ SBSApplicationLaunchError sbs_error = 0;
+ sbs_error = SBSLaunchApplicationForDebugging(
+ bundleIDCFStr,
+ (CFURLRef)NULL, // openURL
+ launch_argv.get(),
+ launch_envp.get(), // CFDictionaryRef environment
+ stdio_path.get(), stdio_path.get(),
+ SBSApplicationLaunchWaitForDebugger | SBSApplicationLaunchUnlockDevice);
+
+ launch_err.SetError(sbs_error, DNBError::SpringBoard);
+
+ if (sbs_error == SBSApplicationLaunchErrorSuccess) {
+ static const useconds_t pid_poll_interval = 200000;
+ static const useconds_t pid_poll_timeout = 30000000;
+
+ useconds_t pid_poll_total = 0;
+
+ nub_process_t pid = INVALID_NUB_PROCESS;
+ Boolean pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid);
+ // Poll until the process is running, as long as we are getting valid
+ // responses and the timeout hasn't expired
+ // A return PID of 0 means the process is not running, which may be because
+ // it hasn't been (asynchronously) started
+ // yet, or that it died very quickly (if you weren't using waitForDebugger).
+ while (!pid_found && pid_poll_total < pid_poll_timeout) {
+ usleep(pid_poll_interval);
+ pid_poll_total += pid_poll_interval;
+ DNBLogThreadedIf(LOG_PROCESS,
+ "%s() polling Springboard for pid for %s...",
+ __FUNCTION__, bundleID.c_str());
+ pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid);
+ }
+
+ CFRelease(bundleIDCFStr);
+ if (pid_found) {
+ if (process != NULL) {
+ // Release our master pty file descriptor so the pty class doesn't
+ // close it and so we can continue to use it in our STDIO thread
+ int master_fd = pty.ReleaseMasterFD();
+ process->SetChildFileDescriptors(master_fd, master_fd, master_fd);
+ }
+ DNBLogThreadedIf(LOG_PROCESS, "%s() => pid = %4.4x", __FUNCTION__, pid);
+ } else {
+ DNBLogError("failed to lookup the process ID for CFBundleIdentifier %s.",
+ bundleID.c_str());
+ }
+ return pid;
+ }
+
+ DNBLogError("unable to launch the application with CFBundleIdentifier '%s' "
+ "sbs_error = %u",
+ bundleID.c_str(), sbs_error);
+ return INVALID_NUB_PROCESS;
+}
+
+#endif // #ifdef WITH_SPRINGBOARD
+
+#if defined(WITH_BKS) || defined(WITH_FBS)
+pid_t MachProcess::BoardServiceLaunchForDebug(
+ const char *path, char const *argv[], char const *envp[], bool no_stdio,
+ bool disable_aslr, const char *event_data, DNBError &launch_err) {
+ DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path);
+
+ // Fork a child process for debugging
+ SetState(eStateLaunching);
+ m_pid = BoardServiceForkChildForPTraceDebugging(
+ path, argv, envp, no_stdio, disable_aslr, event_data, launch_err);
+ if (m_pid != 0) {
+ m_path = path;
+ size_t i;
+ char const *arg;
+ for (i = 0; (arg = argv[i]) != NULL; i++)
+ m_args.push_back(arg);
+ m_task.StartExceptionThread(launch_err);
+
+ if (launch_err.Fail()) {
+ if (launch_err.AsString() == NULL)
+ launch_err.SetErrorString("unable to start the exception thread");
+ DNBLog("Could not get inferior's Mach exception port, sending ptrace "
+ "PT_KILL and exiting.");
+ ::ptrace(PT_KILL, m_pid, 0, 0);
+ m_pid = INVALID_NUB_PROCESS;
+ return INVALID_NUB_PROCESS;
+ }
+
+ StartSTDIOThread();
+ SetState(eStateAttaching);
+ int err = ::ptrace(PT_ATTACHEXC, m_pid, 0, 0);
+ if (err == 0) {
+ m_flags |= eMachProcessFlagsAttached;
+ DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", m_pid);
+ } else {
+ SetState(eStateExited);
+ DNBLogThreadedIf(LOG_PROCESS, "error: failed to attach to pid %d", m_pid);
+ }
+ }
+ return m_pid;
+}
+
+pid_t MachProcess::BoardServiceForkChildForPTraceDebugging(
+ const char *app_bundle_path, char const *argv[], char const *envp[],
+ bool no_stdio, bool disable_aslr, const char *event_data,
+ DNBError &launch_err) {
+ if (argv[0] == NULL)
+ return INVALID_NUB_PROCESS;
+
+ DNBLogThreadedIf(LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__,
+ app_bundle_path, this);
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ size_t argc = 0;
+ // Count the number of arguments
+ while (argv[argc] != NULL)
+ argc++;
+
+ // Enumerate the arguments
+ size_t first_launch_arg_idx = 1;
+
+ NSMutableArray *launch_argv = nil;
+
+ if (argv[first_launch_arg_idx]) {
+ size_t launch_argc = argc > 0 ? argc - 1 : 0;
+ launch_argv = [NSMutableArray arrayWithCapacity:launch_argc];
+ size_t i;
+ char const *arg;
+ NSString *launch_arg;
+ for (i = first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL);
+ i++) {
+ launch_arg = [NSString stringWithUTF8String:arg];
+ // FIXME: Should we silently eat an argument that we can't convert into a
+ // UTF8 string?
+ if (launch_arg != nil)
+ [launch_argv addObject:launch_arg];
+ else
+ break;
+ }
+ }
+
+ NSMutableDictionary *launch_envp = nil;
+ if (envp[0]) {
+ launch_envp = [[NSMutableDictionary alloc] init];
+ const char *value;
+ int name_len;
+ NSString *name_string, *value_string;
+
+ for (int i = 0; envp[i] != NULL; i++) {
+ value = strstr(envp[i], "=");
+
+ // If the name field is empty or there's no =, skip it. Somebody's
+ // messing with us.
+ if (value == NULL || value == envp[i])
+ continue;
+
+ name_len = value - envp[i];
+
+ // Now move value over the "="
+ value++;
+ name_string = [[NSString alloc] initWithBytes:envp[i]
+ length:name_len
+ encoding:NSUTF8StringEncoding];
+ value_string = [NSString stringWithUTF8String:value];
+ [launch_envp setObject:value_string forKey:name_string];
+ }
+ }
+
+ NSString *stdio_path = nil;
+ NSFileManager *file_manager = [NSFileManager defaultManager];
+
+ PseudoTerminal pty;
+ if (!no_stdio) {
+ PseudoTerminal::Status pty_err =
+ pty.OpenFirstAvailableMaster(O_RDWR | O_NOCTTY);
+ if (pty_err == PseudoTerminal::success) {
+ const char *slave_name = pty.SlaveName();
+ DNBLogThreadedIf(LOG_PROCESS,
+ "%s() successfully opened master pty, slave is %s",
+ __FUNCTION__, slave_name);
+ if (slave_name && slave_name[0]) {
+ ::chmod(slave_name, S_IRWXU | S_IRWXG | S_IRWXO);
+ stdio_path = [file_manager
+ stringWithFileSystemRepresentation:slave_name
+ length:strlen(slave_name)];
+ }
+ }
+ }
+
+ if (stdio_path == nil) {
+ const char *null_path = "/dev/null";
+ stdio_path =
+ [file_manager stringWithFileSystemRepresentation:null_path
+ length:strlen(null_path)];
+ }
+
+ CFStringRef bundleIDCFStr = CopyBundleIDForPath(app_bundle_path, launch_err);
+ if (bundleIDCFStr == NULL) {
+ [pool drain];
+ return INVALID_NUB_PROCESS;
+ }
+
+ // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use
+ // toll-free bridging here:
+ NSString *bundleIDNSStr = (NSString *)bundleIDCFStr;
+
+ // Okay, now let's assemble all these goodies into the BackBoardServices
+ // options mega-dictionary:
+
+ NSMutableDictionary *options = nullptr;
+ pid_t return_pid = INVALID_NUB_PROCESS;
+ bool success = false;
+
+#ifdef WITH_BKS
+ if (ProcessUsingBackBoard()) {
+ options =
+ BKSCreateOptionsDictionary(app_bundle_path, launch_argv, launch_envp,
+ stdio_path, disable_aslr, event_data);
+ success = BKSCallOpenApplicationFunction(bundleIDNSStr, options, launch_err,
+ &return_pid);
+ }
+#endif
+#ifdef WITH_FBS
+ if (ProcessUsingFrontBoard()) {
+ options =
+ FBSCreateOptionsDictionary(app_bundle_path, launch_argv, launch_envp,
+ stdio_path, disable_aslr, event_data);
+ success = FBSCallOpenApplicationFunction(bundleIDNSStr, options, launch_err,
+ &return_pid);
+ }
+#endif
+
+ if (success) {
+ int master_fd = pty.ReleaseMasterFD();
+ SetChildFileDescriptors(master_fd, master_fd, master_fd);
+ CFString::UTF8(bundleIDCFStr, m_bundle_id);
+ }
+
+ [pool drain];
+
+ return return_pid;
+}
+
+bool MachProcess::BoardServiceSendEvent(const char *event_data,
+ DNBError &send_err) {
+ bool return_value = true;
+
+ if (event_data == NULL || *event_data == '\0') {
+ DNBLogError("SendEvent called with NULL event data.");
+ send_err.SetErrorString("SendEvent called with empty event data");
+ return false;
+ }
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ if (strcmp(event_data, "BackgroundApplication") == 0) {
+// This is an event I cooked up. What you actually do is foreground the system
+// app, so:
+#ifdef WITH_BKS
+ if (ProcessUsingBackBoard()) {
+ return_value = BKSCallOpenApplicationFunction(nil, nil, send_err, NULL);
+ }
+#endif
+#ifdef WITH_FBS
+ if (ProcessUsingFrontBoard()) {
+ return_value = FBSCallOpenApplicationFunction(nil, nil, send_err, NULL);
+ }
+#endif
+ if (!return_value) {
+ DNBLogError("Failed to background application, error: %s.",
+ send_err.AsString());
+ }
+ } else {
+ if (m_bundle_id.empty()) {
+ // See if we can figure out the bundle ID for this PID:
+
+ DNBLogError(
+ "Tried to send event \"%s\" to a process that has no bundle ID.",
+ event_data);
+ return false;
+ }
+
+ NSString *bundleIDNSStr =
+ [NSString stringWithUTF8String:m_bundle_id.c_str()];
+
+ NSMutableDictionary *options = [NSMutableDictionary dictionary];
+
+#ifdef WITH_BKS
+ if (ProcessUsingBackBoard()) {
+ if (!BKSAddEventDataToOptions(options, event_data, send_err)) {
+ [pool drain];
+ return false;
+ }
+ return_value = BKSCallOpenApplicationFunction(bundleIDNSStr, options,
+ send_err, NULL);
+ DNBLogThreadedIf(LOG_PROCESS,
+ "Called BKSCallOpenApplicationFunction to send event.");
+ }
+#endif
+#ifdef WITH_FBS
+ if (ProcessUsingFrontBoard()) {
+ if (!FBSAddEventDataToOptions(options, event_data, send_err)) {
+ [pool drain];
+ return false;
+ }
+ return_value = FBSCallOpenApplicationFunction(bundleIDNSStr, options,
+ send_err, NULL);
+ DNBLogThreadedIf(LOG_PROCESS,
+ "Called FBSCallOpenApplicationFunction to send event.");
+ }
+#endif
+
+ if (!return_value) {
+ DNBLogError("Failed to send event: %s, error: %s.", event_data,
+ send_err.AsString());
+ }
+ }
+
+ [pool drain];
+ return return_value;
+}
+#endif // defined(WITH_BKS) || defined (WITH_FBS)
+
+#ifdef WITH_BKS
+void MachProcess::BKSCleanupAfterAttach(const void *attach_token,
+ DNBError &err_str) {
+ bool success;
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use
+ // toll-free bridging here:
+ NSString *bundleIDNSStr = (NSString *)attach_token;
+
+ // Okay, now let's assemble all these goodies into the BackBoardServices
+ // options mega-dictionary:
+
+ // First we have the debug sub-dictionary:
+ NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
+ [debug_options setObject:[NSNumber numberWithBool:YES]
+ forKey:BKSDebugOptionKeyCancelDebugOnNextLaunch];
+
+ // That will go in the overall dictionary:
+
+ NSMutableDictionary *options = [NSMutableDictionary dictionary];
+ [options setObject:debug_options
+ forKey:BKSOpenApplicationOptionKeyDebuggingOptions];
+
+ success =
+ BKSCallOpenApplicationFunction(bundleIDNSStr, options, err_str, NULL);
+
+ if (!success) {
+ DNBLogError("error trying to cancel debug on next launch for %s: %s",
+ [bundleIDNSStr UTF8String], err_str.AsString());
+ }
+
+ [pool drain];
+}
+#endif // WITH_BKS
+
+#ifdef WITH_FBS
+void MachProcess::FBSCleanupAfterAttach(const void *attach_token,
+ DNBError &err_str) {
+ bool success;
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ // Instead of rewriting CopyBundleIDForPath for NSStrings, we'll just use
+ // toll-free bridging here:
+ NSString *bundleIDNSStr = (NSString *)attach_token;
+
+ // Okay, now let's assemble all these goodies into the BackBoardServices
+ // options mega-dictionary:
+
+ // First we have the debug sub-dictionary:
+ NSMutableDictionary *debug_options = [NSMutableDictionary dictionary];
+ [debug_options setObject:[NSNumber numberWithBool:YES]
+ forKey:FBSDebugOptionKeyCancelDebugOnNextLaunch];
+
+ // That will go in the overall dictionary:
+
+ NSMutableDictionary *options = [NSMutableDictionary dictionary];
+ [options setObject:debug_options
+ forKey:FBSOpenApplicationOptionKeyDebuggingOptions];
+
+ success =
+ FBSCallOpenApplicationFunction(bundleIDNSStr, options, err_str, NULL);
+
+ if (!success) {
+ DNBLogError("error trying to cancel debug on next launch for %s: %s",
+ [bundleIDNSStr UTF8String], err_str.AsString());
+ }
+
+ [pool drain];
+}
+#endif // WITH_FBS
+
+
+void MachProcess::CalculateBoardStatus()
+{
+ if (m_flags & eMachProcessFlagsBoardCalculated)
+ return;
+ if (m_pid == 0)
+ return;
+
+#if defined (WITH_FBS) || defined (WITH_BKS)
+ bool found_app_flavor = false;
+#endif
+
+#if defined(WITH_FBS)
+ if (!found_app_flavor && IsFBSProcess(m_pid)) {
+ found_app_flavor = true;
+ m_flags |= eMachProcessFlagsUsingFBS;
+ }
+#endif
+#if defined(WITH_BKS)
+ if (!found_app_flavor && IsBKSProcess(m_pid)) {
+ found_app_flavor = true;
+ m_flags |= eMachProcessFlagsUsingBKS;
+ }
+#endif
+
+ m_flags |= eMachProcessFlagsBoardCalculated;
+}
+
+bool MachProcess::ProcessUsingBackBoard() {
+ CalculateBoardStatus();
+ return (m_flags & eMachProcessFlagsUsingBKS) != 0;
+}
+
+bool MachProcess::ProcessUsingFrontBoard() {
+ CalculateBoardStatus();
+ return (m_flags & eMachProcessFlagsUsingFBS) != 0;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachTask.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachTask.h
new file mode 100644
index 00000000000..c975e15a555
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachTask.h
@@ -0,0 +1,109 @@
+//===-- MachTask.h ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// MachTask.h
+// debugserver
+//
+// Created by Greg Clayton on 12/5/08.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachTask_h__
+#define __MachTask_h__
+
+#include <mach/mach.h>
+#include <sys/socket.h>
+#include <map>
+#include <string>
+#include "DNBDefs.h"
+#include "MachException.h"
+#include "MachVMMemory.h"
+#include "PThreadMutex.h"
+
+class MachProcess;
+
+typedef uint64_t MachMallocEventId;
+
+enum MachMallocEventType {
+ eMachMallocEventTypeAlloc = 2,
+ eMachMallocEventTypeDealloc = 4,
+ eMachMallocEventTypeOther = 1
+};
+
+struct MachMallocEvent {
+ mach_vm_address_t m_base_address;
+ uint64_t m_size;
+ MachMallocEventType m_event_type;
+ MachMallocEventId m_event_id;
+};
+
+class MachTask {
+public:
+ // Constructors and Destructors
+ MachTask(MachProcess *process);
+ virtual ~MachTask();
+
+ void Clear();
+
+ kern_return_t Suspend();
+ kern_return_t Resume();
+
+ nub_size_t ReadMemory(nub_addr_t addr, nub_size_t size, void *buf);
+ nub_size_t WriteMemory(nub_addr_t addr, nub_size_t size, const void *buf);
+ int GetMemoryRegionInfo(nub_addr_t addr, DNBRegionInfo *region_info);
+ std::string GetProfileData(DNBProfileDataScanType scanType);
+
+ nub_addr_t AllocateMemory(nub_size_t size, uint32_t permissions);
+ nub_bool_t DeallocateMemory(nub_addr_t addr);
+
+ mach_port_t ExceptionPort() const;
+ bool ExceptionPortIsValid() const;
+ kern_return_t SaveExceptionPortInfo();
+ kern_return_t RestoreExceptionPortInfo();
+ kern_return_t ShutDownExcecptionThread();
+
+ bool StartExceptionThread(DNBError &err);
+ nub_addr_t GetDYLDAllImageInfosAddress(DNBError &err);
+ kern_return_t BasicInfo(struct task_basic_info *info);
+ static kern_return_t BasicInfo(task_t task, struct task_basic_info *info);
+ bool IsValid() const;
+ static bool IsValid(task_t task);
+ static void *ExceptionThread(void *arg);
+ void TaskPortChanged(task_t task);
+ task_t TaskPort() const { return m_task; }
+ task_t TaskPortForProcessID(DNBError &err, bool force = false);
+ static task_t TaskPortForProcessID(pid_t pid, DNBError &err,
+ uint32_t num_retries = 10,
+ uint32_t usec_interval = 10000);
+
+ MachProcess *Process() { return m_process; }
+ const MachProcess *Process() const { return m_process; }
+
+ nub_size_t PageSize();
+
+protected:
+ MachProcess *m_process; // The mach process that owns this MachTask
+ task_t m_task;
+ MachVMMemory m_vm_memory; // Special mach memory reading class that will take
+ // care of watching for page and region boundaries
+ MachException::PortInfo
+ m_exc_port_info; // Saved settings for all exception ports
+ pthread_t m_exception_thread; // Thread ID for the exception thread in case we
+ // need it
+ mach_port_t m_exception_port; // Exception port on which we will receive child
+ // exceptions
+
+ typedef std::map<mach_vm_address_t, size_t> allocation_collection;
+ allocation_collection m_allocations;
+
+private:
+ MachTask(const MachTask &) = delete;
+ MachTask &operator=(const MachTask &rhs) = delete;
+};
+
+#endif // __MachTask_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachTask.mm b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachTask.mm
new file mode 100644
index 00000000000..0d5a63a28f2
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachTask.mm
@@ -0,0 +1,963 @@
+//===-- MachTask.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//----------------------------------------------------------------------
+//
+// MachTask.cpp
+// debugserver
+//
+// Created by Greg Clayton on 12/5/08.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachTask.h"
+
+// C Includes
+
+#include <mach-o/dyld_images.h>
+#include <mach/mach_vm.h>
+#import <sys/sysctl.h>
+
+#if defined(__APPLE__)
+#include <pthread.h>
+#include <sched.h>
+#endif
+
+// C++ Includes
+#include <iomanip>
+#include <sstream>
+
+// Other libraries and framework includes
+// Project includes
+#include "CFUtils.h"
+#include "DNB.h"
+#include "DNBDataRef.h"
+#include "DNBError.h"
+#include "DNBLog.h"
+#include "MachProcess.h"
+
+#ifdef WITH_SPRINGBOARD
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <SpringBoardServices/SBSWatchdogAssertion.h>
+#include <SpringBoardServices/SpringBoardServer.h>
+
+#endif
+
+#ifdef WITH_BKS
+extern "C" {
+#import <BackBoardServices/BKSWatchdogAssertion.h>
+#import <BackBoardServices/BackBoardServices.h>
+#import <Foundation/Foundation.h>
+}
+#endif
+
+#include <AvailabilityMacros.h>
+
+#ifdef LLDB_ENERGY
+#include <mach/mach_time.h>
+#include <pmenergy.h>
+#include <pmsample.h>
+#endif
+
+//----------------------------------------------------------------------
+// MachTask constructor
+//----------------------------------------------------------------------
+MachTask::MachTask(MachProcess *process)
+ : m_process(process), m_task(TASK_NULL), m_vm_memory(),
+ m_exception_thread(0), m_exception_port(MACH_PORT_NULL) {
+ memset(&m_exc_port_info, 0, sizeof(m_exc_port_info));
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+MachTask::~MachTask() { Clear(); }
+
+//----------------------------------------------------------------------
+// MachTask::Suspend
+//----------------------------------------------------------------------
+kern_return_t MachTask::Suspend() {
+ DNBError err;
+ task_t task = TaskPort();
+ err = ::task_suspend(task);
+ if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
+ err.LogThreaded("::task_suspend ( target_task = 0x%4.4x )", task);
+ return err.Status();
+}
+
+//----------------------------------------------------------------------
+// MachTask::Resume
+//----------------------------------------------------------------------
+kern_return_t MachTask::Resume() {
+ struct task_basic_info task_info;
+ task_t task = TaskPort();
+ if (task == TASK_NULL)
+ return KERN_INVALID_ARGUMENT;
+
+ DNBError err;
+ err = BasicInfo(task, &task_info);
+
+ if (err.Success()) {
+ // task_resume isn't counted like task_suspend calls are, are, so if the
+ // task is not suspended, don't try and resume it since it is already
+ // running
+ if (task_info.suspend_count > 0) {
+ err = ::task_resume(task);
+ if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
+ err.LogThreaded("::task_resume ( target_task = 0x%4.4x )", task);
+ }
+ }
+ return err.Status();
+}
+
+//----------------------------------------------------------------------
+// MachTask::ExceptionPort
+//----------------------------------------------------------------------
+mach_port_t MachTask::ExceptionPort() const { return m_exception_port; }
+
+//----------------------------------------------------------------------
+// MachTask::ExceptionPortIsValid
+//----------------------------------------------------------------------
+bool MachTask::ExceptionPortIsValid() const {
+ return MACH_PORT_VALID(m_exception_port);
+}
+
+//----------------------------------------------------------------------
+// MachTask::Clear
+//----------------------------------------------------------------------
+void MachTask::Clear() {
+ // Do any cleanup needed for this task
+ m_task = TASK_NULL;
+ m_exception_thread = 0;
+ m_exception_port = MACH_PORT_NULL;
+}
+
+//----------------------------------------------------------------------
+// MachTask::SaveExceptionPortInfo
+//----------------------------------------------------------------------
+kern_return_t MachTask::SaveExceptionPortInfo() {
+ return m_exc_port_info.Save(TaskPort());
+}
+
+//----------------------------------------------------------------------
+// MachTask::RestoreExceptionPortInfo
+//----------------------------------------------------------------------
+kern_return_t MachTask::RestoreExceptionPortInfo() {
+ return m_exc_port_info.Restore(TaskPort());
+}
+
+//----------------------------------------------------------------------
+// MachTask::ReadMemory
+//----------------------------------------------------------------------
+nub_size_t MachTask::ReadMemory(nub_addr_t addr, nub_size_t size, void *buf) {
+ nub_size_t n = 0;
+ task_t task = TaskPort();
+ if (task != TASK_NULL) {
+ n = m_vm_memory.Read(task, addr, buf, size);
+
+ DNBLogThreadedIf(LOG_MEMORY, "MachTask::ReadMemory ( addr = 0x%8.8llx, "
+ "size = %llu, buf = %p) => %llu bytes read",
+ (uint64_t)addr, (uint64_t)size, buf, (uint64_t)n);
+ if (DNBLogCheckLogBit(LOG_MEMORY_DATA_LONG) ||
+ (DNBLogCheckLogBit(LOG_MEMORY_DATA_SHORT) && size <= 8)) {
+ DNBDataRef data((uint8_t *)buf, n, false);
+ data.Dump(0, static_cast<DNBDataRef::offset_t>(n), addr,
+ DNBDataRef::TypeUInt8, 16);
+ }
+ }
+ return n;
+}
+
+//----------------------------------------------------------------------
+// MachTask::WriteMemory
+//----------------------------------------------------------------------
+nub_size_t MachTask::WriteMemory(nub_addr_t addr, nub_size_t size,
+ const void *buf) {
+ nub_size_t n = 0;
+ task_t task = TaskPort();
+ if (task != TASK_NULL) {
+ n = m_vm_memory.Write(task, addr, buf, size);
+ DNBLogThreadedIf(LOG_MEMORY, "MachTask::WriteMemory ( addr = 0x%8.8llx, "
+ "size = %llu, buf = %p) => %llu bytes written",
+ (uint64_t)addr, (uint64_t)size, buf, (uint64_t)n);
+ if (DNBLogCheckLogBit(LOG_MEMORY_DATA_LONG) ||
+ (DNBLogCheckLogBit(LOG_MEMORY_DATA_SHORT) && size <= 8)) {
+ DNBDataRef data((const uint8_t *)buf, n, false);
+ data.Dump(0, static_cast<DNBDataRef::offset_t>(n), addr,
+ DNBDataRef::TypeUInt8, 16);
+ }
+ }
+ return n;
+}
+
+//----------------------------------------------------------------------
+// MachTask::MemoryRegionInfo
+//----------------------------------------------------------------------
+int MachTask::GetMemoryRegionInfo(nub_addr_t addr, DNBRegionInfo *region_info) {
+ task_t task = TaskPort();
+ if (task == TASK_NULL)
+ return -1;
+
+ int ret = m_vm_memory.GetMemoryRegionInfo(task, addr, region_info);
+ DNBLogThreadedIf(LOG_MEMORY, "MachTask::MemoryRegionInfo ( addr = 0x%8.8llx "
+ ") => %i (start = 0x%8.8llx, size = 0x%8.8llx, "
+ "permissions = %u)",
+ (uint64_t)addr, ret, (uint64_t)region_info->addr,
+ (uint64_t)region_info->size, region_info->permissions);
+ return ret;
+}
+
+#define TIME_VALUE_TO_TIMEVAL(a, r) \
+ do { \
+ (r)->tv_sec = (a)->seconds; \
+ (r)->tv_usec = (a)->microseconds; \
+ } while (0)
+
+// We should consider moving this into each MacThread.
+static void get_threads_profile_data(DNBProfileDataScanType scanType,
+ task_t task, nub_process_t pid,
+ std::vector<uint64_t> &threads_id,
+ std::vector<std::string> &threads_name,
+ std::vector<uint64_t> &threads_used_usec) {
+ kern_return_t kr;
+ thread_act_array_t threads;
+ mach_msg_type_number_t tcnt;
+
+ kr = task_threads(task, &threads, &tcnt);
+ if (kr != KERN_SUCCESS)
+ return;
+
+ for (mach_msg_type_number_t i = 0; i < tcnt; i++) {
+ thread_identifier_info_data_t identifier_info;
+ mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
+ kr = ::thread_info(threads[i], THREAD_IDENTIFIER_INFO,
+ (thread_info_t)&identifier_info, &count);
+ if (kr != KERN_SUCCESS)
+ continue;
+
+ thread_basic_info_data_t basic_info;
+ count = THREAD_BASIC_INFO_COUNT;
+ kr = ::thread_info(threads[i], THREAD_BASIC_INFO,
+ (thread_info_t)&basic_info, &count);
+ if (kr != KERN_SUCCESS)
+ continue;
+
+ if ((basic_info.flags & TH_FLAGS_IDLE) == 0) {
+ nub_thread_t tid =
+ MachThread::GetGloballyUniqueThreadIDForMachPortID(threads[i]);
+ threads_id.push_back(tid);
+
+ if ((scanType & eProfileThreadName) &&
+ (identifier_info.thread_handle != 0)) {
+ struct proc_threadinfo proc_threadinfo;
+ int len = ::proc_pidinfo(pid, PROC_PIDTHREADINFO,
+ identifier_info.thread_handle,
+ &proc_threadinfo, PROC_PIDTHREADINFO_SIZE);
+ if (len && proc_threadinfo.pth_name[0]) {
+ threads_name.push_back(proc_threadinfo.pth_name);
+ } else {
+ threads_name.push_back("");
+ }
+ } else {
+ threads_name.push_back("");
+ }
+ struct timeval tv;
+ struct timeval thread_tv;
+ TIME_VALUE_TO_TIMEVAL(&basic_info.user_time, &thread_tv);
+ TIME_VALUE_TO_TIMEVAL(&basic_info.system_time, &tv);
+ timeradd(&thread_tv, &tv, &thread_tv);
+ uint64_t used_usec = thread_tv.tv_sec * 1000000ULL + thread_tv.tv_usec;
+ threads_used_usec.push_back(used_usec);
+ }
+
+ mach_port_deallocate(mach_task_self(), threads[i]);
+ }
+ mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)(uintptr_t)threads,
+ tcnt * sizeof(*threads));
+}
+
+#define RAW_HEXBASE std::setfill('0') << std::hex << std::right
+#define DECIMAL std::dec << std::setfill(' ')
+std::string MachTask::GetProfileData(DNBProfileDataScanType scanType) {
+ std::string result;
+
+ static int32_t numCPU = -1;
+ struct host_cpu_load_info host_info;
+ if (scanType & eProfileHostCPU) {
+ int32_t mib[] = {CTL_HW, HW_AVAILCPU};
+ size_t len = sizeof(numCPU);
+ if (numCPU == -1) {
+ if (sysctl(mib, sizeof(mib) / sizeof(int32_t), &numCPU, &len, NULL, 0) !=
+ 0)
+ return result;
+ }
+
+ mach_port_t localHost = mach_host_self();
+ mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
+ kern_return_t kr = host_statistics(localHost, HOST_CPU_LOAD_INFO,
+ (host_info_t)&host_info, &count);
+ if (kr != KERN_SUCCESS)
+ return result;
+ }
+
+ task_t task = TaskPort();
+ if (task == TASK_NULL)
+ return result;
+
+ pid_t pid = m_process->ProcessID();
+
+ struct task_basic_info task_info;
+ DNBError err;
+ err = BasicInfo(task, &task_info);
+
+ if (!err.Success())
+ return result;
+
+ uint64_t elapsed_usec = 0;
+ uint64_t task_used_usec = 0;
+ if (scanType & eProfileCPU) {
+ // Get current used time.
+ struct timeval current_used_time;
+ struct timeval tv;
+ TIME_VALUE_TO_TIMEVAL(&task_info.user_time, &current_used_time);
+ TIME_VALUE_TO_TIMEVAL(&task_info.system_time, &tv);
+ timeradd(&current_used_time, &tv, &current_used_time);
+ task_used_usec =
+ current_used_time.tv_sec * 1000000ULL + current_used_time.tv_usec;
+
+ struct timeval current_elapsed_time;
+ int res = gettimeofday(&current_elapsed_time, NULL);
+ if (res == 0) {
+ elapsed_usec = current_elapsed_time.tv_sec * 1000000ULL +
+ current_elapsed_time.tv_usec;
+ }
+ }
+
+ std::vector<uint64_t> threads_id;
+ std::vector<std::string> threads_name;
+ std::vector<uint64_t> threads_used_usec;
+
+ if (scanType & eProfileThreadsCPU) {
+ get_threads_profile_data(scanType, task, pid, threads_id, threads_name,
+ threads_used_usec);
+ }
+
+ vm_statistics64_data_t vminfo;
+ uint64_t physical_memory = 0;
+ uint64_t anonymous = 0;
+ uint64_t phys_footprint = 0;
+ uint64_t memory_cap = 0;
+ if (m_vm_memory.GetMemoryProfile(scanType, task, task_info,
+ m_process->GetCPUType(), pid, vminfo,
+ physical_memory, anonymous,
+ phys_footprint, memory_cap)) {
+ std::ostringstream profile_data_stream;
+
+ if (scanType & eProfileHostCPU) {
+ profile_data_stream << "num_cpu:" << numCPU << ';';
+ profile_data_stream << "host_user_ticks:"
+ << host_info.cpu_ticks[CPU_STATE_USER] << ';';
+ profile_data_stream << "host_sys_ticks:"
+ << host_info.cpu_ticks[CPU_STATE_SYSTEM] << ';';
+ profile_data_stream << "host_idle_ticks:"
+ << host_info.cpu_ticks[CPU_STATE_IDLE] << ';';
+ }
+
+ if (scanType & eProfileCPU) {
+ profile_data_stream << "elapsed_usec:" << elapsed_usec << ';';
+ profile_data_stream << "task_used_usec:" << task_used_usec << ';';
+ }
+
+ if (scanType & eProfileThreadsCPU) {
+ const size_t num_threads = threads_id.size();
+ for (size_t i = 0; i < num_threads; i++) {
+ profile_data_stream << "thread_used_id:" << std::hex << threads_id[i]
+ << std::dec << ';';
+ profile_data_stream << "thread_used_usec:" << threads_used_usec[i]
+ << ';';
+
+ if (scanType & eProfileThreadName) {
+ profile_data_stream << "thread_used_name:";
+ const size_t len = threads_name[i].size();
+ if (len) {
+ const char *thread_name = threads_name[i].c_str();
+ // Make sure that thread name doesn't interfere with our delimiter.
+ profile_data_stream << RAW_HEXBASE << std::setw(2);
+ const uint8_t *ubuf8 = (const uint8_t *)(thread_name);
+ for (size_t j = 0; j < len; j++) {
+ profile_data_stream << (uint32_t)(ubuf8[j]);
+ }
+ // Reset back to DECIMAL.
+ profile_data_stream << DECIMAL;
+ }
+ profile_data_stream << ';';
+ }
+ }
+ }
+
+ if (scanType & eProfileHostMemory)
+ profile_data_stream << "total:" << physical_memory << ';';
+
+ if (scanType & eProfileMemory) {
+ static vm_size_t pagesize = vm_kernel_page_size;
+
+ // This mimicks Activity Monitor.
+ uint64_t total_used_count =
+ (physical_memory / pagesize) -
+ (vminfo.free_count - vminfo.speculative_count) -
+ vminfo.external_page_count - vminfo.purgeable_count;
+ profile_data_stream << "used:" << total_used_count * pagesize << ';';
+
+ if (scanType & eProfileMemoryAnonymous) {
+ profile_data_stream << "anonymous:" << anonymous << ';';
+ }
+
+ profile_data_stream << "phys_footprint:" << phys_footprint << ';';
+ }
+
+ if (scanType & eProfileMemoryCap) {
+ profile_data_stream << "mem_cap:" << memory_cap << ';';
+ }
+
+#ifdef LLDB_ENERGY
+ if (scanType & eProfileEnergy) {
+ struct rusage_info_v2 info;
+ int rc = proc_pid_rusage(pid, RUSAGE_INFO_V2, (rusage_info_t *)&info);
+ if (rc == 0) {
+ uint64_t now = mach_absolute_time();
+ pm_task_energy_data_t pm_energy;
+ memset(&pm_energy, 0, sizeof(pm_energy));
+ /*
+ * Disable most features of pm_sample_pid. It will gather
+ * network/GPU/WindowServer information; fill in the rest.
+ */
+ pm_sample_task_and_pid(task, pid, &pm_energy, now,
+ PM_SAMPLE_ALL & ~PM_SAMPLE_NAME &
+ ~PM_SAMPLE_INTERVAL & ~PM_SAMPLE_CPU &
+ ~PM_SAMPLE_DISK);
+ pm_energy.sti.total_user = info.ri_user_time;
+ pm_energy.sti.total_system = info.ri_system_time;
+ pm_energy.sti.task_interrupt_wakeups = info.ri_interrupt_wkups;
+ pm_energy.sti.task_platform_idle_wakeups = info.ri_pkg_idle_wkups;
+ pm_energy.diskio_bytesread = info.ri_diskio_bytesread;
+ pm_energy.diskio_byteswritten = info.ri_diskio_byteswritten;
+ pm_energy.pageins = info.ri_pageins;
+
+ uint64_t total_energy =
+ (uint64_t)(pm_energy_impact(&pm_energy) * NSEC_PER_SEC);
+ // uint64_t process_age = now - info.ri_proc_start_abstime;
+ // uint64_t avg_energy = 100.0 * (double)total_energy /
+ // (double)process_age;
+
+ profile_data_stream << "energy:" << total_energy << ';';
+ }
+ }
+#endif
+
+ profile_data_stream << "--end--;";
+
+ result = profile_data_stream.str();
+ }
+
+ return result;
+}
+
+//----------------------------------------------------------------------
+// MachTask::TaskPortForProcessID
+//----------------------------------------------------------------------
+task_t MachTask::TaskPortForProcessID(DNBError &err, bool force) {
+ if (((m_task == TASK_NULL) || force) && m_process != NULL)
+ m_task = MachTask::TaskPortForProcessID(m_process->ProcessID(), err);
+ return m_task;
+}
+
+//----------------------------------------------------------------------
+// MachTask::TaskPortForProcessID
+//----------------------------------------------------------------------
+task_t MachTask::TaskPortForProcessID(pid_t pid, DNBError &err,
+ uint32_t num_retries,
+ uint32_t usec_interval) {
+ if (pid != INVALID_NUB_PROCESS) {
+ DNBError err;
+ mach_port_t task_self = mach_task_self();
+ task_t task = TASK_NULL;
+ for (uint32_t i = 0; i < num_retries; i++) {
+ err = ::task_for_pid(task_self, pid, &task);
+
+ if (DNBLogCheckLogBit(LOG_TASK) || err.Fail()) {
+ char str[1024];
+ ::snprintf(str, sizeof(str), "::task_for_pid ( target_tport = 0x%4.4x, "
+ "pid = %d, &task ) => err = 0x%8.8x (%s)",
+ task_self, pid, err.Status(),
+ err.AsString() ? err.AsString() : "success");
+ if (err.Fail()) {
+ err.SetErrorString(str);
+ DNBLogError ("MachTask::TaskPortForProcessID task_for_pid failed: %s", str);
+ }
+ err.LogThreaded(str);
+ }
+
+ if (err.Success())
+ return task;
+
+ // Sleep a bit and try again
+ ::usleep(usec_interval);
+ }
+ }
+ return TASK_NULL;
+}
+
+//----------------------------------------------------------------------
+// MachTask::BasicInfo
+//----------------------------------------------------------------------
+kern_return_t MachTask::BasicInfo(struct task_basic_info *info) {
+ return BasicInfo(TaskPort(), info);
+}
+
+//----------------------------------------------------------------------
+// MachTask::BasicInfo
+//----------------------------------------------------------------------
+kern_return_t MachTask::BasicInfo(task_t task, struct task_basic_info *info) {
+ if (info == NULL)
+ return KERN_INVALID_ARGUMENT;
+
+ DNBError err;
+ mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
+ err = ::task_info(task, TASK_BASIC_INFO, (task_info_t)info, &count);
+ const bool log_process = DNBLogCheckLogBit(LOG_TASK);
+ if (log_process || err.Fail())
+ err.LogThreaded("::task_info ( target_task = 0x%4.4x, flavor = "
+ "TASK_BASIC_INFO, task_info_out => %p, task_info_outCnt => "
+ "%u )",
+ task, info, count);
+ if (DNBLogCheckLogBit(LOG_TASK) && DNBLogCheckLogBit(LOG_VERBOSE) &&
+ err.Success()) {
+ float user = (float)info->user_time.seconds +
+ (float)info->user_time.microseconds / 1000000.0f;
+ float system = (float)info->user_time.seconds +
+ (float)info->user_time.microseconds / 1000000.0f;
+ DNBLogThreaded("task_basic_info = { suspend_count = %i, virtual_size = "
+ "0x%8.8llx, resident_size = 0x%8.8llx, user_time = %f, "
+ "system_time = %f }",
+ info->suspend_count, (uint64_t)info->virtual_size,
+ (uint64_t)info->resident_size, user, system);
+ }
+ return err.Status();
+}
+
+//----------------------------------------------------------------------
+// MachTask::IsValid
+//
+// Returns true if a task is a valid task port for a current process.
+//----------------------------------------------------------------------
+bool MachTask::IsValid() const { return MachTask::IsValid(TaskPort()); }
+
+//----------------------------------------------------------------------
+// MachTask::IsValid
+//
+// Returns true if a task is a valid task port for a current process.
+//----------------------------------------------------------------------
+bool MachTask::IsValid(task_t task) {
+ if (task != TASK_NULL) {
+ struct task_basic_info task_info;
+ return BasicInfo(task, &task_info) == KERN_SUCCESS;
+ }
+ return false;
+}
+
+bool MachTask::StartExceptionThread(DNBError &err) {
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "MachTask::%s ( )", __FUNCTION__);
+
+ task_t task = TaskPortForProcessID(err);
+ if (MachTask::IsValid(task)) {
+ // Got the mach port for the current process
+ mach_port_t task_self = mach_task_self();
+
+ // Allocate an exception port that we will use to track our child process
+ err = ::mach_port_allocate(task_self, MACH_PORT_RIGHT_RECEIVE,
+ &m_exception_port);
+ if (err.Fail())
+ return false;
+
+ // Add the ability to send messages on the new exception port
+ err = ::mach_port_insert_right(task_self, m_exception_port,
+ m_exception_port, MACH_MSG_TYPE_MAKE_SEND);
+ if (err.Fail())
+ return false;
+
+ // Save the original state of the exception ports for our child process
+ SaveExceptionPortInfo();
+
+ // We weren't able to save the info for our exception ports, we must stop...
+ if (m_exc_port_info.mask == 0) {
+ err.SetErrorString("failed to get exception port info");
+ return false;
+ }
+
+ // Set the ability to get all exceptions on this port
+ err = ::task_set_exception_ports(
+ task, m_exc_port_info.mask, m_exception_port,
+ EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE);
+ if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) {
+ err.LogThreaded("::task_set_exception_ports ( task = 0x%4.4x, "
+ "exception_mask = 0x%8.8x, new_port = 0x%4.4x, behavior "
+ "= 0x%8.8x, new_flavor = 0x%8.8x )",
+ task, m_exc_port_info.mask, m_exception_port,
+ (EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES),
+ THREAD_STATE_NONE);
+ }
+
+ if (err.Fail())
+ return false;
+
+ // Create the exception thread
+ err = ::pthread_create(&m_exception_thread, NULL, MachTask::ExceptionThread,
+ this);
+ return err.Success();
+ } else {
+ DNBLogError("MachTask::%s (): task invalid, exception thread start failed.",
+ __FUNCTION__);
+ }
+ return false;
+}
+
+kern_return_t MachTask::ShutDownExcecptionThread() {
+ DNBError err;
+
+ err = RestoreExceptionPortInfo();
+
+ // NULL our our exception port and let our exception thread exit
+ mach_port_t exception_port = m_exception_port;
+ m_exception_port = 0;
+
+ err.SetError(::pthread_cancel(m_exception_thread), DNBError::POSIX);
+ if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
+ err.LogThreaded("::pthread_cancel ( thread = %p )", m_exception_thread);
+
+ err.SetError(::pthread_join(m_exception_thread, NULL), DNBError::POSIX);
+ if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
+ err.LogThreaded("::pthread_join ( thread = %p, value_ptr = NULL)",
+ m_exception_thread);
+
+ // Deallocate our exception port that we used to track our child process
+ mach_port_t task_self = mach_task_self();
+ err = ::mach_port_deallocate(task_self, exception_port);
+ if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
+ err.LogThreaded("::mach_port_deallocate ( task = 0x%4.4x, name = 0x%4.4x )",
+ task_self, exception_port);
+
+ return err.Status();
+}
+
+void *MachTask::ExceptionThread(void *arg) {
+ if (arg == NULL)
+ return NULL;
+
+ MachTask *mach_task = (MachTask *)arg;
+ MachProcess *mach_proc = mach_task->Process();
+ DNBLogThreadedIf(LOG_EXCEPTIONS,
+ "MachTask::%s ( arg = %p ) starting thread...", __FUNCTION__,
+ arg);
+
+#if defined(__APPLE__)
+ pthread_setname_np("exception monitoring thread");
+#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
+ struct sched_param thread_param;
+ int thread_sched_policy;
+ if (pthread_getschedparam(pthread_self(), &thread_sched_policy,
+ &thread_param) == 0) {
+ thread_param.sched_priority = 47;
+ pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);
+ }
+#endif
+#endif
+
+ // We keep a count of the number of consecutive exceptions received so
+ // we know to grab all exceptions without a timeout. We do this to get a
+ // bunch of related exceptions on our exception port so we can process
+ // then together. When we have multiple threads, we can get an exception
+ // per thread and they will come in consecutively. The main loop in this
+ // thread can stop periodically if needed to service things related to this
+ // process.
+ // flag set in the options, so we will wait forever for an exception on
+ // our exception port. After we get one exception, we then will use the
+ // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current
+ // exceptions for our process. After we have received the last pending
+ // exception, we will get a timeout which enables us to then notify
+ // our main thread that we have an exception bundle available. We then wait
+ // for the main thread to tell this exception thread to start trying to get
+ // exceptions messages again and we start again with a mach_msg read with
+ // infinite timeout.
+ uint32_t num_exceptions_received = 0;
+ DNBError err;
+ task_t task = mach_task->TaskPort();
+ mach_msg_timeout_t periodic_timeout = 0;
+
+#if defined(WITH_SPRINGBOARD) && !defined(WITH_BKS)
+ mach_msg_timeout_t watchdog_elapsed = 0;
+ mach_msg_timeout_t watchdog_timeout = 60 * 1000;
+ pid_t pid = mach_proc->ProcessID();
+ CFReleaser<SBSWatchdogAssertionRef> watchdog;
+
+ if (mach_proc->ProcessUsingSpringBoard()) {
+ // Request a renewal for every 60 seconds if we attached using SpringBoard
+ watchdog.reset(::SBSWatchdogAssertionCreateForPID(NULL, pid, 60));
+ DNBLogThreadedIf(
+ LOG_TASK, "::SBSWatchdogAssertionCreateForPID (NULL, %4.4x, 60 ) => %p",
+ pid, watchdog.get());
+
+ if (watchdog.get()) {
+ ::SBSWatchdogAssertionRenew(watchdog.get());
+
+ CFTimeInterval watchdogRenewalInterval =
+ ::SBSWatchdogAssertionGetRenewalInterval(watchdog.get());
+ DNBLogThreadedIf(
+ LOG_TASK,
+ "::SBSWatchdogAssertionGetRenewalInterval ( %p ) => %g seconds",
+ watchdog.get(), watchdogRenewalInterval);
+ if (watchdogRenewalInterval > 0.0) {
+ watchdog_timeout = (mach_msg_timeout_t)watchdogRenewalInterval * 1000;
+ if (watchdog_timeout > 3000)
+ watchdog_timeout -= 1000; // Give us a second to renew our timeout
+ else if (watchdog_timeout > 1000)
+ watchdog_timeout -=
+ 250; // Give us a quarter of a second to renew our timeout
+ }
+ }
+ if (periodic_timeout == 0 || periodic_timeout > watchdog_timeout)
+ periodic_timeout = watchdog_timeout;
+ }
+#endif // #if defined (WITH_SPRINGBOARD) && !defined (WITH_BKS)
+
+#ifdef WITH_BKS
+ CFReleaser<BKSWatchdogAssertionRef> watchdog;
+ if (mach_proc->ProcessUsingBackBoard()) {
+ pid_t pid = mach_proc->ProcessID();
+ CFAllocatorRef alloc = kCFAllocatorDefault;
+ watchdog.reset(::BKSWatchdogAssertionCreateForPID(alloc, pid));
+ }
+#endif // #ifdef WITH_BKS
+
+ while (mach_task->ExceptionPortIsValid()) {
+ ::pthread_testcancel();
+
+ MachException::Message exception_message;
+
+ if (num_exceptions_received > 0) {
+ // No timeout, just receive as many exceptions as we can since we already
+ // have one and we want
+ // to get all currently available exceptions for this task
+ err = exception_message.Receive(
+ mach_task->ExceptionPort(),
+ MACH_RCV_MSG | MACH_RCV_INTERRUPT | MACH_RCV_TIMEOUT, 1);
+ } else if (periodic_timeout > 0) {
+ // We need to stop periodically in this loop, so try and get a mach
+ // message with a valid timeout (ms)
+ err = exception_message.Receive(mach_task->ExceptionPort(),
+ MACH_RCV_MSG | MACH_RCV_INTERRUPT |
+ MACH_RCV_TIMEOUT,
+ periodic_timeout);
+ } else {
+ // We don't need to parse all current exceptions or stop periodically,
+ // just wait for an exception forever.
+ err = exception_message.Receive(mach_task->ExceptionPort(),
+ MACH_RCV_MSG | MACH_RCV_INTERRUPT, 0);
+ }
+
+ if (err.Status() == MACH_RCV_INTERRUPTED) {
+ // If we have no task port we should exit this thread
+ if (!mach_task->ExceptionPortIsValid()) {
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "thread cancelled...");
+ break;
+ }
+
+ // Make sure our task is still valid
+ if (MachTask::IsValid(task)) {
+ // Task is still ok
+ DNBLogThreadedIf(LOG_EXCEPTIONS,
+ "interrupted, but task still valid, continuing...");
+ continue;
+ } else {
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "task has exited...");
+ mach_proc->SetState(eStateExited);
+ // Our task has died, exit the thread.
+ break;
+ }
+ } else if (err.Status() == MACH_RCV_TIMED_OUT) {
+ if (num_exceptions_received > 0) {
+ // We were receiving all current exceptions with a timeout of zero
+ // it is time to go back to our normal looping mode
+ num_exceptions_received = 0;
+
+ // Notify our main thread we have a complete exception message
+ // bundle available and get the possibly updated task port back
+ // from the process in case we exec'ed and our task port changed
+ task = mach_proc->ExceptionMessageBundleComplete();
+
+ // in case we use a timeout value when getting exceptions...
+ // Make sure our task is still valid
+ if (MachTask::IsValid(task)) {
+ // Task is still ok
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "got a timeout, continuing...");
+ continue;
+ } else {
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "task has exited...");
+ mach_proc->SetState(eStateExited);
+ // Our task has died, exit the thread.
+ break;
+ }
+ }
+
+#if defined(WITH_SPRINGBOARD) && !defined(WITH_BKS)
+ if (watchdog.get()) {
+ watchdog_elapsed += periodic_timeout;
+ if (watchdog_elapsed >= watchdog_timeout) {
+ DNBLogThreadedIf(LOG_TASK, "SBSWatchdogAssertionRenew ( %p )",
+ watchdog.get());
+ ::SBSWatchdogAssertionRenew(watchdog.get());
+ watchdog_elapsed = 0;
+ }
+ }
+#endif
+ } else if (err.Status() != KERN_SUCCESS) {
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "got some other error, do something "
+ "about it??? nah, continuing for "
+ "now...");
+ // TODO: notify of error?
+ } else {
+ if (exception_message.CatchExceptionRaise(task)) {
+ if (exception_message.state.task_port != task) {
+ if (exception_message.state.IsValid()) {
+ // We exec'ed and our task port changed on us.
+ DNBLogThreadedIf(LOG_EXCEPTIONS,
+ "task port changed from 0x%4.4x to 0x%4.4x",
+ task, exception_message.state.task_port);
+ task = exception_message.state.task_port;
+ mach_task->TaskPortChanged(exception_message.state.task_port);
+ }
+ }
+ ++num_exceptions_received;
+ mach_proc->ExceptionMessageReceived(exception_message);
+ }
+ }
+ }
+
+#if defined(WITH_SPRINGBOARD) && !defined(WITH_BKS)
+ if (watchdog.get()) {
+ // TODO: change SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel
+ // when we
+ // all are up and running on systems that support it. The SBS framework has
+ // a #define
+ // that will forward SBSWatchdogAssertionRelease to
+ // SBSWatchdogAssertionCancel for now
+ // so it should still build either way.
+ DNBLogThreadedIf(LOG_TASK, "::SBSWatchdogAssertionRelease(%p)",
+ watchdog.get());
+ ::SBSWatchdogAssertionRelease(watchdog.get());
+ }
+#endif // #if defined (WITH_SPRINGBOARD) && !defined (WITH_BKS)
+
+ DNBLogThreadedIf(LOG_EXCEPTIONS, "MachTask::%s (%p): thread exiting...",
+ __FUNCTION__, arg);
+ return NULL;
+}
+
+// So the TASK_DYLD_INFO used to just return the address of the all image infos
+// as a single member called "all_image_info". Then someone decided it would be
+// a good idea to rename this first member to "all_image_info_addr" and add a
+// size member called "all_image_info_size". This of course can not be detected
+// using code or #defines. So to hack around this problem, we define our own
+// version of the TASK_DYLD_INFO structure so we can guarantee what is inside
+// it.
+
+struct hack_task_dyld_info {
+ mach_vm_address_t all_image_info_addr;
+ mach_vm_size_t all_image_info_size;
+};
+
+nub_addr_t MachTask::GetDYLDAllImageInfosAddress(DNBError &err) {
+ struct hack_task_dyld_info dyld_info;
+ mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
+ // Make sure that COUNT isn't bigger than our hacked up struct
+ // hack_task_dyld_info.
+ // If it is, then make COUNT smaller to match.
+ if (count > (sizeof(struct hack_task_dyld_info) / sizeof(natural_t)))
+ count = (sizeof(struct hack_task_dyld_info) / sizeof(natural_t));
+
+ task_t task = TaskPortForProcessID(err);
+ if (err.Success()) {
+ err = ::task_info(task, TASK_DYLD_INFO, (task_info_t)&dyld_info, &count);
+ if (err.Success()) {
+ // We now have the address of the all image infos structure
+ return dyld_info.all_image_info_addr;
+ }
+ }
+ return INVALID_NUB_ADDRESS;
+}
+
+//----------------------------------------------------------------------
+// MachTask::AllocateMemory
+//----------------------------------------------------------------------
+nub_addr_t MachTask::AllocateMemory(size_t size, uint32_t permissions) {
+ mach_vm_address_t addr;
+ task_t task = TaskPort();
+ if (task == TASK_NULL)
+ return INVALID_NUB_ADDRESS;
+
+ DNBError err;
+ err = ::mach_vm_allocate(task, &addr, size, TRUE);
+ if (err.Status() == KERN_SUCCESS) {
+ // Set the protections:
+ vm_prot_t mach_prot = VM_PROT_NONE;
+ if (permissions & eMemoryPermissionsReadable)
+ mach_prot |= VM_PROT_READ;
+ if (permissions & eMemoryPermissionsWritable)
+ mach_prot |= VM_PROT_WRITE;
+ if (permissions & eMemoryPermissionsExecutable)
+ mach_prot |= VM_PROT_EXECUTE;
+
+ err = ::mach_vm_protect(task, addr, size, 0, mach_prot);
+ if (err.Status() == KERN_SUCCESS) {
+ m_allocations.insert(std::make_pair(addr, size));
+ return addr;
+ }
+ ::mach_vm_deallocate(task, addr, size);
+ }
+ return INVALID_NUB_ADDRESS;
+}
+
+//----------------------------------------------------------------------
+// MachTask::DeallocateMemory
+//----------------------------------------------------------------------
+nub_bool_t MachTask::DeallocateMemory(nub_addr_t addr) {
+ task_t task = TaskPort();
+ if (task == TASK_NULL)
+ return false;
+
+ // We have to stash away sizes for the allocations...
+ allocation_collection::iterator pos, end = m_allocations.end();
+ for (pos = m_allocations.begin(); pos != end; pos++) {
+ if ((*pos).first == addr) {
+ m_allocations.erase(pos);
+#define ALWAYS_ZOMBIE_ALLOCATIONS 0
+ if (ALWAYS_ZOMBIE_ALLOCATIONS ||
+ getenv("DEBUGSERVER_ZOMBIE_ALLOCATIONS")) {
+ ::mach_vm_protect(task, (*pos).first, (*pos).second, 0, VM_PROT_NONE);
+ return true;
+ } else
+ return ::mach_vm_deallocate(task, (*pos).first, (*pos).second) ==
+ KERN_SUCCESS;
+ }
+ }
+ return false;
+}
+
+void MachTask::TaskPortChanged(task_t task)
+{
+ m_task = task;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThread.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThread.cpp
new file mode 100644
index 00000000000..80d6042caa5
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThread.cpp
@@ -0,0 +1,782 @@
+//===-- MachThread.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/19/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachThread.h"
+#include "DNB.h"
+#include "DNBLog.h"
+#include "MachProcess.h"
+#include "ThreadInfo.h"
+#include <dlfcn.h>
+#include <inttypes.h>
+#include <mach/thread_policy.h>
+
+static uint32_t GetSequenceID() {
+ static uint32_t g_nextID = 0;
+ return ++g_nextID;
+}
+
+MachThread::MachThread(MachProcess *process, bool is_64_bit,
+ uint64_t unique_thread_id, thread_t mach_port_num)
+ : m_process(process), m_unique_id(unique_thread_id),
+ m_mach_port_number(mach_port_num), m_seq_id(GetSequenceID()),
+ m_state(eStateUnloaded), m_state_mutex(PTHREAD_MUTEX_RECURSIVE),
+ m_suspend_count(0), m_stop_exception(),
+ m_arch_up(DNBArchProtocol::Create(this)), m_reg_sets(NULL),
+ m_num_reg_sets(0), m_ident_info(), m_proc_threadinfo(),
+ m_dispatch_queue_name(), m_is_64_bit(is_64_bit),
+ m_pthread_qos_class_decode(nullptr) {
+ nub_size_t num_reg_sets = 0;
+ m_reg_sets = m_arch_up->GetRegisterSetInfo(&num_reg_sets);
+ m_num_reg_sets = num_reg_sets;
+
+ m_pthread_qos_class_decode =
+ (unsigned int (*)(unsigned long, int *, unsigned long *))dlsym(
+ RTLD_DEFAULT, "_pthread_qos_class_decode");
+
+ // Get the thread state so we know if a thread is in a state where we can't
+ // muck with it and also so we get the suspend count correct in case it was
+ // already suspended
+ GetBasicInfo();
+ DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE,
+ "MachThread::MachThread ( process = %p, tid = 0x%8.8" PRIx64
+ ", seq_id = %u )",
+ static_cast<void *>(&m_process), m_unique_id, m_seq_id);
+}
+
+MachThread::~MachThread() {
+ DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE,
+ "MachThread::~MachThread() for tid = 0x%8.8" PRIx64 " (%u)",
+ m_unique_id, m_seq_id);
+}
+
+void MachThread::Suspend() {
+ DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )",
+ __FUNCTION__);
+ if (MachPortNumberIsValid(m_mach_port_number)) {
+ DNBError err(::thread_suspend(m_mach_port_number), DNBError::MachKernel);
+ if (err.Success())
+ m_suspend_count++;
+ if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
+ err.LogThreaded("::thread_suspend (%4.4" PRIx32 ")", m_mach_port_number);
+ }
+}
+
+void MachThread::Resume(bool others_stopped) {
+ DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )",
+ __FUNCTION__);
+ if (MachPortNumberIsValid(m_mach_port_number)) {
+ SetSuspendCountBeforeResume(others_stopped);
+ }
+}
+
+bool MachThread::SetSuspendCountBeforeResume(bool others_stopped) {
+ DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )",
+ __FUNCTION__);
+ DNBError err;
+ if (!MachPortNumberIsValid(m_mach_port_number))
+ return false;
+
+ integer_t times_to_resume;
+
+ if (others_stopped) {
+ if (GetBasicInfo()) {
+ times_to_resume = m_basic_info.suspend_count;
+ m_suspend_count = -(times_to_resume - m_suspend_count);
+ } else
+ times_to_resume = 0;
+ } else {
+ times_to_resume = m_suspend_count;
+ m_suspend_count = 0;
+ }
+
+ if (times_to_resume > 0) {
+ while (times_to_resume > 0) {
+ err = ::thread_resume(m_mach_port_number);
+ if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
+ err.LogThreaded("::thread_resume (%4.4" PRIx32 ")", m_mach_port_number);
+ if (err.Success())
+ --times_to_resume;
+ else {
+ if (GetBasicInfo())
+ times_to_resume = m_basic_info.suspend_count;
+ else
+ times_to_resume = 0;
+ }
+ }
+ }
+ return true;
+}
+
+bool MachThread::RestoreSuspendCountAfterStop() {
+ DNBLogThreadedIf(LOG_THREAD | LOG_VERBOSE, "MachThread::%s ( )",
+ __FUNCTION__);
+ DNBError err;
+ if (!MachPortNumberIsValid(m_mach_port_number))
+ return false;
+
+ if (m_suspend_count > 0) {
+ while (m_suspend_count > 0) {
+ err = ::thread_resume(m_mach_port_number);
+ if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
+ err.LogThreaded("::thread_resume (%4.4" PRIx32 ")", m_mach_port_number);
+ if (err.Success())
+ --m_suspend_count;
+ else {
+ if (GetBasicInfo())
+ m_suspend_count = m_basic_info.suspend_count;
+ else
+ m_suspend_count = 0;
+ return false; // ???
+ }
+ }
+ } else if (m_suspend_count < 0) {
+ while (m_suspend_count < 0) {
+ err = ::thread_suspend(m_mach_port_number);
+ if (err.Success())
+ ++m_suspend_count;
+ if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) {
+ err.LogThreaded("::thread_suspend (%4.4" PRIx32 ")",
+ m_mach_port_number);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+const char *MachThread::GetBasicInfoAsString() const {
+ static char g_basic_info_string[1024];
+ struct thread_basic_info basicInfo;
+
+ if (GetBasicInfo(m_mach_port_number, &basicInfo)) {
+
+ // char run_state_str[32];
+ // size_t run_state_str_size = sizeof(run_state_str);
+ // switch (basicInfo.run_state)
+ // {
+ // case TH_STATE_RUNNING: strlcpy(run_state_str, "running",
+ // run_state_str_size); break;
+ // case TH_STATE_STOPPED: strlcpy(run_state_str, "stopped",
+ // run_state_str_size); break;
+ // case TH_STATE_WAITING: strlcpy(run_state_str, "waiting",
+ // run_state_str_size); break;
+ // case TH_STATE_UNINTERRUPTIBLE: strlcpy(run_state_str,
+ // "uninterruptible", run_state_str_size); break;
+ // case TH_STATE_HALTED: strlcpy(run_state_str, "halted",
+ // run_state_str_size); break;
+ // default: snprintf(run_state_str,
+ // run_state_str_size, "%d", basicInfo.run_state); break; // ???
+ // }
+ float user = (float)basicInfo.user_time.seconds +
+ (float)basicInfo.user_time.microseconds / 1000000.0f;
+ float system = (float)basicInfo.user_time.seconds +
+ (float)basicInfo.user_time.microseconds / 1000000.0f;
+ snprintf(g_basic_info_string, sizeof(g_basic_info_string),
+ "Thread 0x%8.8" PRIx64 ": user=%f system=%f cpu=%d sleep_time=%d",
+ m_unique_id, user, system, basicInfo.cpu_usage,
+ basicInfo.sleep_time);
+
+ return g_basic_info_string;
+ }
+ return NULL;
+}
+
+// Finds the Mach port number for a given thread in the inferior process' port
+// namespace.
+thread_t MachThread::InferiorThreadID() const {
+ mach_msg_type_number_t i;
+ mach_port_name_array_t names;
+ mach_port_type_array_t types;
+ mach_msg_type_number_t ncount, tcount;
+ thread_t inferior_tid = INVALID_NUB_THREAD;
+ task_t my_task = ::mach_task_self();
+ task_t task = m_process->Task().TaskPort();
+
+ kern_return_t kret =
+ ::mach_port_names(task, &names, &ncount, &types, &tcount);
+ if (kret == KERN_SUCCESS) {
+
+ for (i = 0; i < ncount; i++) {
+ mach_port_t my_name;
+ mach_msg_type_name_t my_type;
+
+ kret = ::mach_port_extract_right(task, names[i], MACH_MSG_TYPE_COPY_SEND,
+ &my_name, &my_type);
+ if (kret == KERN_SUCCESS) {
+ ::mach_port_deallocate(my_task, my_name);
+ if (my_name == m_mach_port_number) {
+ inferior_tid = names[i];
+ break;
+ }
+ }
+ }
+ // Free up the names and types
+ ::vm_deallocate(my_task, (vm_address_t)names,
+ ncount * sizeof(mach_port_name_t));
+ ::vm_deallocate(my_task, (vm_address_t)types,
+ tcount * sizeof(mach_port_type_t));
+ }
+ return inferior_tid;
+}
+
+bool MachThread::IsUserReady() {
+ if (m_basic_info.run_state == 0)
+ GetBasicInfo();
+
+ switch (m_basic_info.run_state) {
+ default:
+ case TH_STATE_UNINTERRUPTIBLE:
+ break;
+
+ case TH_STATE_RUNNING:
+ case TH_STATE_STOPPED:
+ case TH_STATE_WAITING:
+ case TH_STATE_HALTED:
+ return true;
+ }
+ return GetPC(0) != 0;
+}
+
+struct thread_basic_info *MachThread::GetBasicInfo() {
+ if (MachThread::GetBasicInfo(m_mach_port_number, &m_basic_info))
+ return &m_basic_info;
+ return NULL;
+}
+
+bool MachThread::GetBasicInfo(thread_t thread,
+ struct thread_basic_info *basicInfoPtr) {
+ if (MachPortNumberIsValid(thread)) {
+ unsigned int info_count = THREAD_BASIC_INFO_COUNT;
+ kern_return_t err = ::thread_info(thread, THREAD_BASIC_INFO,
+ (thread_info_t)basicInfoPtr, &info_count);
+ if (err == KERN_SUCCESS)
+ return true;
+ }
+ ::memset(basicInfoPtr, 0, sizeof(struct thread_basic_info));
+ return false;
+}
+
+bool MachThread::ThreadIDIsValid(uint64_t thread) { return thread != 0; }
+
+bool MachThread::MachPortNumberIsValid(thread_t thread) {
+ return thread != THREAD_NULL;
+}
+
+bool MachThread::GetRegisterState(int flavor, bool force) {
+ return m_arch_up->GetRegisterState(flavor, force) == KERN_SUCCESS;
+}
+
+bool MachThread::SetRegisterState(int flavor) {
+ return m_arch_up->SetRegisterState(flavor) == KERN_SUCCESS;
+}
+
+uint64_t MachThread::GetPC(uint64_t failValue) {
+ // Get program counter
+ return m_arch_up->GetPC(failValue);
+}
+
+bool MachThread::SetPC(uint64_t value) {
+ // Set program counter
+ return m_arch_up->SetPC(value);
+}
+
+uint64_t MachThread::GetSP(uint64_t failValue) {
+ // Get stack pointer
+ return m_arch_up->GetSP(failValue);
+}
+
+nub_process_t MachThread::ProcessID() const {
+ if (m_process)
+ return m_process->ProcessID();
+ return INVALID_NUB_PROCESS;
+}
+
+void MachThread::Dump(uint32_t index) {
+ const char *thread_run_state = NULL;
+
+ switch (m_basic_info.run_state) {
+ case TH_STATE_RUNNING:
+ thread_run_state = "running";
+ break; // 1 thread is running normally
+ case TH_STATE_STOPPED:
+ thread_run_state = "stopped";
+ break; // 2 thread is stopped
+ case TH_STATE_WAITING:
+ thread_run_state = "waiting";
+ break; // 3 thread is waiting normally
+ case TH_STATE_UNINTERRUPTIBLE:
+ thread_run_state = "uninter";
+ break; // 4 thread is in an uninterruptible wait
+ case TH_STATE_HALTED:
+ thread_run_state = "halted ";
+ break; // 5 thread is halted at a
+ default:
+ thread_run_state = "???";
+ break;
+ }
+
+ DNBLogThreaded(
+ "[%3u] #%3u tid: 0x%8.8" PRIx64 ", pc: 0x%16.16" PRIx64
+ ", sp: 0x%16.16" PRIx64
+ ", user: %d.%6.6d, system: %d.%6.6d, cpu: %2d, policy: %2d, run_state: "
+ "%2d (%s), flags: %2d, suspend_count: %2d (current %2d), sleep_time: %d",
+ index, m_seq_id, m_unique_id, GetPC(INVALID_NUB_ADDRESS),
+ GetSP(INVALID_NUB_ADDRESS), m_basic_info.user_time.seconds,
+ m_basic_info.user_time.microseconds, m_basic_info.system_time.seconds,
+ m_basic_info.system_time.microseconds, m_basic_info.cpu_usage,
+ m_basic_info.policy, m_basic_info.run_state, thread_run_state,
+ m_basic_info.flags, m_basic_info.suspend_count, m_suspend_count,
+ m_basic_info.sleep_time);
+ // DumpRegisterState(0);
+}
+
+void MachThread::ThreadWillResume(const DNBThreadResumeAction *thread_action,
+ bool others_stopped) {
+ if (thread_action->addr != INVALID_NUB_ADDRESS)
+ SetPC(thread_action->addr);
+
+ SetState(thread_action->state);
+ switch (thread_action->state) {
+ case eStateStopped:
+ case eStateSuspended:
+ assert(others_stopped == false);
+ Suspend();
+ break;
+
+ case eStateRunning:
+ case eStateStepping:
+ Resume(others_stopped);
+ break;
+ default:
+ break;
+ }
+ m_arch_up->ThreadWillResume();
+ m_stop_exception.Clear();
+}
+
+DNBBreakpoint *MachThread::CurrentBreakpoint() {
+ return m_process->Breakpoints().FindByAddress(GetPC());
+}
+
+bool MachThread::ShouldStop(bool &step_more) {
+ // See if this thread is at a breakpoint?
+ DNBBreakpoint *bp = CurrentBreakpoint();
+
+ if (bp) {
+ // This thread is sitting at a breakpoint, ask the breakpoint
+ // if we should be stopping here.
+ return true;
+ } else {
+ if (m_arch_up->StepNotComplete()) {
+ step_more = true;
+ return false;
+ }
+ // The thread state is used to let us know what the thread was
+ // trying to do. MachThread::ThreadWillResume() will set the
+ // thread state to various values depending if the thread was
+ // the current thread and if it was to be single stepped, or
+ // resumed.
+ if (GetState() == eStateRunning) {
+ // If our state is running, then we should continue as we are in
+ // the process of stepping over a breakpoint.
+ return false;
+ } else {
+ // Stop if we have any kind of valid exception for this
+ // thread.
+ if (GetStopException().IsValid())
+ return true;
+ }
+ }
+ return false;
+}
+bool MachThread::IsStepping() { return GetState() == eStateStepping; }
+
+bool MachThread::ThreadDidStop() {
+ // This thread has existed prior to resuming under debug nub control,
+ // and has just been stopped. Do any cleanup that needs to be done
+ // after running.
+
+ // The thread state and breakpoint will still have the same values
+ // as they had prior to resuming the thread, so it makes it easy to check
+ // if we were trying to step a thread, or we tried to resume while being
+ // at a breakpoint.
+
+ // When this method gets called, the process state is still in the
+ // state it was in while running so we can act accordingly.
+ m_arch_up->ThreadDidStop();
+
+ // We may have suspended this thread so the primary thread could step
+ // without worrying about race conditions, so lets restore our suspend
+ // count.
+ RestoreSuspendCountAfterStop();
+
+ // Update the basic information for a thread
+ MachThread::GetBasicInfo(m_mach_port_number, &m_basic_info);
+
+ if (m_basic_info.suspend_count > 0)
+ SetState(eStateSuspended);
+ else
+ SetState(eStateStopped);
+ return true;
+}
+
+bool MachThread::NotifyException(MachException::Data &exc) {
+ // Allow the arch specific protocol to process (MachException::Data &)exc
+ // first before possible reassignment of m_stop_exception with exc.
+ // See also MachThread::GetStopException().
+ bool handled = m_arch_up->NotifyException(exc);
+
+ if (m_stop_exception.IsValid()) {
+ // We may have more than one exception for a thread, but we need to
+ // only remember the one that we will say is the reason we stopped.
+ // We may have been single stepping and also gotten a signal exception,
+ // so just remember the most pertinent one.
+ if (m_stop_exception.IsBreakpoint())
+ m_stop_exception = exc;
+ } else {
+ m_stop_exception = exc;
+ }
+
+ return handled;
+}
+
+nub_state_t MachThread::GetState() {
+ // If any other threads access this we will need a mutex for it
+ PTHREAD_MUTEX_LOCKER(locker, m_state_mutex);
+ return m_state;
+}
+
+void MachThread::SetState(nub_state_t state) {
+ PTHREAD_MUTEX_LOCKER(locker, m_state_mutex);
+ m_state = state;
+ DNBLogThreadedIf(LOG_THREAD,
+ "MachThread::SetState ( %s ) for tid = 0x%8.8" PRIx64 "",
+ DNBStateAsString(state), m_unique_id);
+}
+
+nub_size_t MachThread::GetNumRegistersInSet(nub_size_t regSet) const {
+ if (regSet < m_num_reg_sets)
+ return m_reg_sets[regSet].num_registers;
+ return 0;
+}
+
+const char *MachThread::GetRegisterSetName(nub_size_t regSet) const {
+ if (regSet < m_num_reg_sets)
+ return m_reg_sets[regSet].name;
+ return NULL;
+}
+
+const DNBRegisterInfo *MachThread::GetRegisterInfo(nub_size_t regSet,
+ nub_size_t regIndex) const {
+ if (regSet < m_num_reg_sets)
+ if (regIndex < m_reg_sets[regSet].num_registers)
+ return &m_reg_sets[regSet].registers[regIndex];
+ return NULL;
+}
+void MachThread::DumpRegisterState(nub_size_t regSet) {
+ if (regSet == REGISTER_SET_ALL) {
+ for (regSet = 1; regSet < m_num_reg_sets; regSet++)
+ DumpRegisterState(regSet);
+ } else {
+ if (m_arch_up->RegisterSetStateIsValid((int)regSet)) {
+ const size_t numRegisters = GetNumRegistersInSet(regSet);
+ uint32_t regIndex = 0;
+ DNBRegisterValueClass reg;
+ for (regIndex = 0; regIndex < numRegisters; ++regIndex) {
+ if (m_arch_up->GetRegisterValue((uint32_t)regSet, regIndex, &reg)) {
+ reg.Dump(NULL, NULL);
+ }
+ }
+ } else {
+ DNBLog("%s: registers are not currently valid.",
+ GetRegisterSetName(regSet));
+ }
+ }
+}
+
+const DNBRegisterSetInfo *
+MachThread::GetRegisterSetInfo(nub_size_t *num_reg_sets) const {
+ *num_reg_sets = m_num_reg_sets;
+ return &m_reg_sets[0];
+}
+
+bool MachThread::GetRegisterValue(uint32_t set, uint32_t reg,
+ DNBRegisterValue *value) {
+ return m_arch_up->GetRegisterValue(set, reg, value);
+}
+
+bool MachThread::SetRegisterValue(uint32_t set, uint32_t reg,
+ const DNBRegisterValue *value) {
+ return m_arch_up->SetRegisterValue(set, reg, value);
+}
+
+nub_size_t MachThread::GetRegisterContext(void *buf, nub_size_t buf_len) {
+ return m_arch_up->GetRegisterContext(buf, buf_len);
+}
+
+nub_size_t MachThread::SetRegisterContext(const void *buf, nub_size_t buf_len) {
+ return m_arch_up->SetRegisterContext(buf, buf_len);
+}
+
+uint32_t MachThread::SaveRegisterState() {
+ return m_arch_up->SaveRegisterState();
+}
+bool MachThread::RestoreRegisterState(uint32_t save_id) {
+ return m_arch_up->RestoreRegisterState(save_id);
+}
+
+uint32_t MachThread::EnableHardwareBreakpoint(const DNBBreakpoint *bp) {
+ if (bp != NULL && bp->IsBreakpoint())
+ return m_arch_up->EnableHardwareBreakpoint(bp->Address(), bp->ByteSize());
+ return INVALID_NUB_HW_INDEX;
+}
+
+uint32_t MachThread::EnableHardwareWatchpoint(const DNBBreakpoint *wp,
+ bool also_set_on_task) {
+ if (wp != NULL && wp->IsWatchpoint())
+ return m_arch_up->EnableHardwareWatchpoint(
+ wp->Address(), wp->ByteSize(), wp->WatchpointRead(),
+ wp->WatchpointWrite(), also_set_on_task);
+ return INVALID_NUB_HW_INDEX;
+}
+
+bool MachThread::RollbackTransForHWP() {
+ return m_arch_up->RollbackTransForHWP();
+}
+
+bool MachThread::FinishTransForHWP() { return m_arch_up->FinishTransForHWP(); }
+
+bool MachThread::DisableHardwareBreakpoint(const DNBBreakpoint *bp) {
+ if (bp != NULL && bp->IsHardware())
+ return m_arch_up->DisableHardwareBreakpoint(bp->GetHardwareIndex());
+ return false;
+}
+
+bool MachThread::DisableHardwareWatchpoint(const DNBBreakpoint *wp,
+ bool also_set_on_task) {
+ if (wp != NULL && wp->IsHardware())
+ return m_arch_up->DisableHardwareWatchpoint(wp->GetHardwareIndex(),
+ also_set_on_task);
+ return false;
+}
+
+uint32_t MachThread::NumSupportedHardwareWatchpoints() const {
+ return m_arch_up->NumSupportedHardwareWatchpoints();
+}
+
+bool MachThread::GetIdentifierInfo() {
+ // Don't try to get the thread info once and cache it for the life of the
+ // thread. It changes over time, for instance
+ // if the thread name changes, then the thread_handle also changes... So you
+ // have to refetch it every time.
+ mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
+ kern_return_t kret = ::thread_info(m_mach_port_number, THREAD_IDENTIFIER_INFO,
+ (thread_info_t)&m_ident_info, &count);
+ return kret == KERN_SUCCESS;
+
+ return false;
+}
+
+const char *MachThread::GetName() {
+ if (GetIdentifierInfo()) {
+ int len = ::proc_pidinfo(m_process->ProcessID(), PROC_PIDTHREADINFO,
+ m_ident_info.thread_handle, &m_proc_threadinfo,
+ sizeof(m_proc_threadinfo));
+
+ if (len && m_proc_threadinfo.pth_name[0])
+ return m_proc_threadinfo.pth_name;
+ }
+ return NULL;
+}
+
+uint64_t
+MachThread::GetGloballyUniqueThreadIDForMachPortID(thread_t mach_port_id) {
+ kern_return_t kr;
+ thread_identifier_info_data_t tident;
+ mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT;
+ kr = thread_info(mach_port_id, THREAD_IDENTIFIER_INFO, (thread_info_t)&tident,
+ &tident_count);
+ if (kr != KERN_SUCCESS) {
+ return mach_port_id;
+ }
+ return tident.thread_id;
+}
+
+nub_addr_t MachThread::GetPThreadT() {
+ nub_addr_t pthread_t_value = INVALID_NUB_ADDRESS;
+ if (MachPortNumberIsValid(m_mach_port_number)) {
+ kern_return_t kr;
+ thread_identifier_info_data_t tident;
+ mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT;
+ kr = thread_info(m_mach_port_number, THREAD_IDENTIFIER_INFO,
+ (thread_info_t)&tident, &tident_count);
+ if (kr == KERN_SUCCESS) {
+ // Dereference thread_handle to get the pthread_t value for this thread.
+ if (m_is_64_bit) {
+ uint64_t addr;
+ if (m_process->ReadMemory(tident.thread_handle, 8, &addr) == 8) {
+ if (addr != 0) {
+ pthread_t_value = addr;
+ }
+ }
+ } else {
+ uint32_t addr;
+ if (m_process->ReadMemory(tident.thread_handle, 4, &addr) == 4) {
+ if (addr != 0) {
+ pthread_t_value = addr;
+ }
+ }
+ }
+ }
+ }
+ return pthread_t_value;
+}
+
+// Return this thread's TSD (Thread Specific Data) address.
+// This is computed based on this thread's pthread_t value.
+//
+// We compute the TSD from the pthread_t by one of two methods.
+//
+// If plo_pthread_tsd_base_offset is non-zero, this is a simple offset that we
+// add to
+// the pthread_t to get the TSD base address.
+//
+// Else we read a pointer from memory at pthread_t +
+// plo_pthread_tsd_base_address_offset and
+// that gives us the TSD address.
+//
+// These plo_pthread_tsd_base values must be read out of libpthread by lldb &
+// provided to debugserver.
+
+nub_addr_t
+MachThread::GetTSDAddressForThread(uint64_t plo_pthread_tsd_base_address_offset,
+ uint64_t plo_pthread_tsd_base_offset,
+ uint64_t plo_pthread_tsd_entry_size) {
+ nub_addr_t tsd_addr = INVALID_NUB_ADDRESS;
+ nub_addr_t pthread_t_value = GetPThreadT();
+ if (plo_pthread_tsd_base_offset != 0 &&
+ plo_pthread_tsd_base_offset != INVALID_NUB_ADDRESS) {
+ tsd_addr = pthread_t_value + plo_pthread_tsd_base_offset;
+ } else {
+ if (plo_pthread_tsd_entry_size == 4) {
+ uint32_t addr = 0;
+ if (m_process->ReadMemory(pthread_t_value +
+ plo_pthread_tsd_base_address_offset,
+ 4, &addr) == 4) {
+ if (addr != 0) {
+ tsd_addr = addr;
+ }
+ }
+ }
+ if (plo_pthread_tsd_entry_size == 4) {
+ uint64_t addr = 0;
+ if (m_process->ReadMemory(pthread_t_value +
+ plo_pthread_tsd_base_address_offset,
+ 8, &addr) == 8) {
+ if (addr != 0) {
+ tsd_addr = addr;
+ }
+ }
+ }
+ }
+ return tsd_addr;
+}
+
+nub_addr_t MachThread::GetDispatchQueueT() {
+ nub_addr_t dispatch_queue_t_value = INVALID_NUB_ADDRESS;
+ if (MachPortNumberIsValid(m_mach_port_number)) {
+ kern_return_t kr;
+ thread_identifier_info_data_t tident;
+ mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT;
+ kr = thread_info(m_mach_port_number, THREAD_IDENTIFIER_INFO,
+ (thread_info_t)&tident, &tident_count);
+ if (kr == KERN_SUCCESS && tident.dispatch_qaddr != 0 &&
+ tident.dispatch_qaddr != INVALID_NUB_ADDRESS) {
+ // Dereference dispatch_qaddr to get the dispatch_queue_t value for this
+ // thread's queue, if any.
+ if (m_is_64_bit) {
+ uint64_t addr;
+ if (m_process->ReadMemory(tident.dispatch_qaddr, 8, &addr) == 8) {
+ if (addr != 0)
+ dispatch_queue_t_value = addr;
+ }
+ } else {
+ uint32_t addr;
+ if (m_process->ReadMemory(tident.dispatch_qaddr, 4, &addr) == 4) {
+ if (addr != 0)
+ dispatch_queue_t_value = addr;
+ }
+ }
+ }
+ }
+ return dispatch_queue_t_value;
+}
+
+ThreadInfo::QoS MachThread::GetRequestedQoS(nub_addr_t tsd,
+ uint64_t dti_qos_class_index) {
+ ThreadInfo::QoS qos_value;
+ if (MachPortNumberIsValid(m_mach_port_number) &&
+ m_pthread_qos_class_decode != nullptr) {
+ uint64_t pthread_priority_value = 0;
+ if (m_is_64_bit) {
+ uint64_t pri;
+ if (m_process->ReadMemory(tsd + (dti_qos_class_index * 8), 8, &pri) ==
+ 8) {
+ pthread_priority_value = pri;
+ }
+ } else {
+ uint32_t pri;
+ if (m_process->ReadMemory(tsd + (dti_qos_class_index * 4), 4, &pri) ==
+ 4) {
+ pthread_priority_value = pri;
+ }
+ }
+
+ uint32_t requested_qos =
+ m_pthread_qos_class_decode(pthread_priority_value, NULL, NULL);
+
+ switch (requested_qos) {
+ // These constants from <pthread/qos.h>
+ case 0x21:
+ qos_value.enum_value = requested_qos;
+ qos_value.constant_name = "QOS_CLASS_USER_INTERACTIVE";
+ qos_value.printable_name = "User Interactive";
+ break;
+ case 0x19:
+ qos_value.enum_value = requested_qos;
+ qos_value.constant_name = "QOS_CLASS_USER_INITIATED";
+ qos_value.printable_name = "User Initiated";
+ break;
+ case 0x15:
+ qos_value.enum_value = requested_qos;
+ qos_value.constant_name = "QOS_CLASS_DEFAULT";
+ qos_value.printable_name = "Default";
+ break;
+ case 0x11:
+ qos_value.enum_value = requested_qos;
+ qos_value.constant_name = "QOS_CLASS_UTILITY";
+ qos_value.printable_name = "Utility";
+ break;
+ case 0x09:
+ qos_value.enum_value = requested_qos;
+ qos_value.constant_name = "QOS_CLASS_BACKGROUND";
+ qos_value.printable_name = "Background";
+ break;
+ case 0x00:
+ qos_value.enum_value = requested_qos;
+ qos_value.constant_name = "QOS_CLASS_UNSPECIFIED";
+ qos_value.printable_name = "Unspecified";
+ break;
+ }
+ }
+ return qos_value;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThread.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThread.h
new file mode 100644
index 00000000000..1634522fde4
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThread.h
@@ -0,0 +1,169 @@
+//===-- MachThread.h --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/19/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachThread_h__
+#define __MachThread_h__
+
+#include <string>
+#include <vector>
+
+#include <libproc.h>
+#include <mach/mach.h>
+#include <pthread.h>
+#include <sys/signal.h>
+
+#include "DNBArch.h"
+#include "DNBRegisterInfo.h"
+#include "MachException.h"
+#include "PThreadCondition.h"
+#include "PThreadMutex.h"
+
+#include "ThreadInfo.h"
+
+class DNBBreakpoint;
+class MachProcess;
+class MachThreadList;
+
+class MachThread {
+public:
+ MachThread(MachProcess *process, bool is_64_bit,
+ uint64_t unique_thread_id = 0, thread_t mach_port_number = 0);
+ ~MachThread();
+
+ MachProcess *Process() { return m_process; }
+ const MachProcess *Process() const { return m_process; }
+ nub_process_t ProcessID() const;
+ void Dump(uint32_t index);
+ uint64_t ThreadID() const { return m_unique_id; }
+ thread_t MachPortNumber() const { return m_mach_port_number; }
+ thread_t InferiorThreadID() const;
+
+ uint32_t SequenceID() const { return m_seq_id; }
+ static bool ThreadIDIsValid(
+ uint64_t thread); // The 64-bit system-wide unique thread identifier
+ static bool MachPortNumberIsValid(thread_t thread); // The mach port # for
+ // this thread in
+ // debugserver namespace
+ void Resume(bool others_stopped);
+ void Suspend();
+ bool SetSuspendCountBeforeResume(bool others_stopped);
+ bool RestoreSuspendCountAfterStop();
+
+ bool GetRegisterState(int flavor, bool force);
+ bool SetRegisterState(int flavor);
+ uint64_t
+ GetPC(uint64_t failValue = INVALID_NUB_ADDRESS); // Get program counter
+ bool SetPC(uint64_t value); // Set program counter
+ uint64_t GetSP(uint64_t failValue = INVALID_NUB_ADDRESS); // Get stack pointer
+
+ DNBBreakpoint *CurrentBreakpoint();
+ uint32_t EnableHardwareBreakpoint(const DNBBreakpoint *breakpoint);
+ uint32_t EnableHardwareWatchpoint(const DNBBreakpoint *watchpoint,
+ bool also_set_on_task);
+ bool DisableHardwareBreakpoint(const DNBBreakpoint *breakpoint);
+ bool DisableHardwareWatchpoint(const DNBBreakpoint *watchpoint,
+ bool also_set_on_task);
+ uint32_t NumSupportedHardwareWatchpoints() const;
+ bool RollbackTransForHWP();
+ bool FinishTransForHWP();
+
+ nub_state_t GetState();
+ void SetState(nub_state_t state);
+
+ void ThreadWillResume(const DNBThreadResumeAction *thread_action,
+ bool others_stopped = false);
+ bool ShouldStop(bool &step_more);
+ bool IsStepping();
+ bool ThreadDidStop();
+ bool NotifyException(MachException::Data &exc);
+ const MachException::Data &GetStopException() { return m_stop_exception; }
+
+ nub_size_t GetNumRegistersInSet(nub_size_t regSet) const;
+ const char *GetRegisterSetName(nub_size_t regSet) const;
+ const DNBRegisterInfo *GetRegisterInfo(nub_size_t regSet,
+ nub_size_t regIndex) const;
+ void DumpRegisterState(nub_size_t regSet);
+ const DNBRegisterSetInfo *GetRegisterSetInfo(nub_size_t *num_reg_sets) const;
+ bool GetRegisterValue(uint32_t reg_set_idx, uint32_t reg_idx,
+ DNBRegisterValue *reg_value);
+ bool SetRegisterValue(uint32_t reg_set_idx, uint32_t reg_idx,
+ const DNBRegisterValue *reg_value);
+ nub_size_t GetRegisterContext(void *buf, nub_size_t buf_len);
+ nub_size_t SetRegisterContext(const void *buf, nub_size_t buf_len);
+ uint32_t SaveRegisterState();
+ bool RestoreRegisterState(uint32_t save_id);
+
+ void NotifyBreakpointChanged(const DNBBreakpoint *bp) {}
+
+ bool IsUserReady();
+ struct thread_basic_info *GetBasicInfo();
+ const char *GetBasicInfoAsString() const;
+ const char *GetName();
+
+ DNBArchProtocol *GetArchProtocol() { return m_arch_up.get(); }
+
+ ThreadInfo::QoS GetRequestedQoS(nub_addr_t tsd, uint64_t dti_qos_class_index);
+ nub_addr_t GetPThreadT();
+ nub_addr_t GetDispatchQueueT();
+ nub_addr_t
+ GetTSDAddressForThread(uint64_t plo_pthread_tsd_base_address_offset,
+ uint64_t plo_pthread_tsd_base_offset,
+ uint64_t plo_pthread_tsd_entry_size);
+
+ static uint64_t GetGloballyUniqueThreadIDForMachPortID(thread_t mach_port_id);
+
+protected:
+ static bool GetBasicInfo(thread_t threadID,
+ struct thread_basic_info *basic_info);
+
+ bool GetIdentifierInfo();
+
+ // const char *
+ // GetDispatchQueueName();
+ //
+ MachProcess *m_process; // The process that owns this thread
+ uint64_t m_unique_id; // The globally unique ID for this thread (nub_thread_t)
+ thread_t m_mach_port_number; // The mach port # for this thread in debugserver
+ // namesp.
+ uint32_t m_seq_id; // A Sequential ID that increments with each new thread
+ nub_state_t m_state; // The state of our process
+ PThreadMutex m_state_mutex; // Multithreaded protection for m_state
+ struct thread_basic_info m_basic_info; // Basic information for a thread used
+ // to see if a thread is valid
+ int32_t m_suspend_count; // The current suspend count > 0 means we have
+ // suspended m_suspendCount times,
+ // < 0 means we have resumed it m_suspendCount
+ // times.
+ MachException::Data m_stop_exception; // The best exception that describes why
+ // this thread is stopped
+ std::unique_ptr<DNBArchProtocol>
+ m_arch_up; // Arch specific information for register state and more
+ const DNBRegisterSetInfo
+ *m_reg_sets; // Register set information for this thread
+ nub_size_t m_num_reg_sets;
+ thread_identifier_info_data_t m_ident_info;
+ struct proc_threadinfo m_proc_threadinfo;
+ std::string m_dispatch_queue_name;
+ bool m_is_64_bit;
+
+ // qos_class_t _pthread_qos_class_decode(pthread_priority_t priority, int *,
+ // unsigned long *);
+ unsigned int (*m_pthread_qos_class_decode)(unsigned long priority, int *,
+ unsigned long *);
+
+private:
+ friend class MachThreadList;
+};
+
+typedef std::shared_ptr<MachThread> MachThreadSP;
+
+#endif
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp
new file mode 100644
index 00000000000..d2aae9da0c4
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThreadList.cpp
@@ -0,0 +1,586 @@
+//===-- MachThreadList.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/19/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachThreadList.h"
+
+#include "DNB.h"
+#include "DNBLog.h"
+#include "DNBThreadResumeActions.h"
+#include "MachProcess.h"
+
+#include <inttypes.h>
+#include <sys/sysctl.h>
+
+#include <memory>
+
+MachThreadList::MachThreadList()
+ : m_threads(), m_threads_mutex(PTHREAD_MUTEX_RECURSIVE),
+ m_is_64_bit(false) {}
+
+MachThreadList::~MachThreadList() {}
+
+nub_state_t MachThreadList::GetState(nub_thread_t tid) {
+ MachThreadSP thread_sp(GetThreadByID(tid));
+ if (thread_sp)
+ return thread_sp->GetState();
+ return eStateInvalid;
+}
+
+const char *MachThreadList::GetName(nub_thread_t tid) {
+ MachThreadSP thread_sp(GetThreadByID(tid));
+ if (thread_sp)
+ return thread_sp->GetName();
+ return NULL;
+}
+
+ThreadInfo::QoS MachThreadList::GetRequestedQoS(nub_thread_t tid,
+ nub_addr_t tsd,
+ uint64_t dti_qos_class_index) {
+ MachThreadSP thread_sp(GetThreadByID(tid));
+ if (thread_sp)
+ return thread_sp->GetRequestedQoS(tsd, dti_qos_class_index);
+ return ThreadInfo::QoS();
+}
+
+nub_addr_t MachThreadList::GetPThreadT(nub_thread_t tid) {
+ MachThreadSP thread_sp(GetThreadByID(tid));
+ if (thread_sp)
+ return thread_sp->GetPThreadT();
+ return INVALID_NUB_ADDRESS;
+}
+
+nub_addr_t MachThreadList::GetDispatchQueueT(nub_thread_t tid) {
+ MachThreadSP thread_sp(GetThreadByID(tid));
+ if (thread_sp)
+ return thread_sp->GetDispatchQueueT();
+ return INVALID_NUB_ADDRESS;
+}
+
+nub_addr_t MachThreadList::GetTSDAddressForThread(
+ nub_thread_t tid, uint64_t plo_pthread_tsd_base_address_offset,
+ uint64_t plo_pthread_tsd_base_offset, uint64_t plo_pthread_tsd_entry_size) {
+ MachThreadSP thread_sp(GetThreadByID(tid));
+ if (thread_sp)
+ return thread_sp->GetTSDAddressForThread(
+ plo_pthread_tsd_base_address_offset, plo_pthread_tsd_base_offset,
+ plo_pthread_tsd_entry_size);
+ return INVALID_NUB_ADDRESS;
+}
+
+nub_thread_t MachThreadList::SetCurrentThread(nub_thread_t tid) {
+ MachThreadSP thread_sp(GetThreadByID(tid));
+ if (thread_sp) {
+ m_current_thread = thread_sp;
+ return tid;
+ }
+ return INVALID_NUB_THREAD;
+}
+
+bool MachThreadList::GetThreadStoppedReason(
+ nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const {
+ MachThreadSP thread_sp(GetThreadByID(tid));
+ if (thread_sp)
+ return thread_sp->GetStopException().GetStopInfo(stop_info);
+ return false;
+}
+
+bool MachThreadList::GetIdentifierInfo(
+ nub_thread_t tid, thread_identifier_info_data_t *ident_info) {
+ thread_t mach_port_number = GetMachPortNumberByThreadID(tid);
+
+ mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
+ return ::thread_info(mach_port_number, THREAD_IDENTIFIER_INFO,
+ (thread_info_t)ident_info, &count) == KERN_SUCCESS;
+}
+
+void MachThreadList::DumpThreadStoppedReason(nub_thread_t tid) const {
+ MachThreadSP thread_sp(GetThreadByID(tid));
+ if (thread_sp)
+ thread_sp->GetStopException().DumpStopReason();
+}
+
+const char *MachThreadList::GetThreadInfo(nub_thread_t tid) const {
+ MachThreadSP thread_sp(GetThreadByID(tid));
+ if (thread_sp)
+ return thread_sp->GetBasicInfoAsString();
+ return NULL;
+}
+
+MachThreadSP MachThreadList::GetThreadByID(nub_thread_t tid) const {
+ PTHREAD_MUTEX_LOCKER(locker, m_threads_mutex);
+ MachThreadSP thread_sp;
+ const size_t num_threads = m_threads.size();
+ for (size_t idx = 0; idx < num_threads; ++idx) {
+ if (m_threads[idx]->ThreadID() == tid) {
+ thread_sp = m_threads[idx];
+ break;
+ }
+ }
+ return thread_sp;
+}
+
+MachThreadSP
+MachThreadList::GetThreadByMachPortNumber(thread_t mach_port_number) const {
+ PTHREAD_MUTEX_LOCKER(locker, m_threads_mutex);
+ MachThreadSP thread_sp;
+ const size_t num_threads = m_threads.size();
+ for (size_t idx = 0; idx < num_threads; ++idx) {
+ if (m_threads[idx]->MachPortNumber() == mach_port_number) {
+ thread_sp = m_threads[idx];
+ break;
+ }
+ }
+ return thread_sp;
+}
+
+nub_thread_t
+MachThreadList::GetThreadIDByMachPortNumber(thread_t mach_port_number) const {
+ PTHREAD_MUTEX_LOCKER(locker, m_threads_mutex);
+ MachThreadSP thread_sp;
+ const size_t num_threads = m_threads.size();
+ for (size_t idx = 0; idx < num_threads; ++idx) {
+ if (m_threads[idx]->MachPortNumber() == mach_port_number) {
+ return m_threads[idx]->ThreadID();
+ }
+ }
+ return INVALID_NUB_THREAD;
+}
+
+thread_t MachThreadList::GetMachPortNumberByThreadID(
+ nub_thread_t globally_unique_id) const {
+ PTHREAD_MUTEX_LOCKER(locker, m_threads_mutex);
+ MachThreadSP thread_sp;
+ const size_t num_threads = m_threads.size();
+ for (size_t idx = 0; idx < num_threads; ++idx) {
+ if (m_threads[idx]->ThreadID() == globally_unique_id) {
+ return m_threads[idx]->MachPortNumber();
+ }
+ }
+ return 0;
+}
+
+bool MachThreadList::GetRegisterValue(nub_thread_t tid, uint32_t set,
+ uint32_t reg,
+ DNBRegisterValue *reg_value) const {
+ MachThreadSP thread_sp(GetThreadByID(tid));
+ if (thread_sp)
+ return thread_sp->GetRegisterValue(set, reg, reg_value);
+
+ return false;
+}
+
+bool MachThreadList::SetRegisterValue(nub_thread_t tid, uint32_t set,
+ uint32_t reg,
+ const DNBRegisterValue *reg_value) const {
+ MachThreadSP thread_sp(GetThreadByID(tid));
+ if (thread_sp)
+ return thread_sp->SetRegisterValue(set, reg, reg_value);
+
+ return false;
+}
+
+nub_size_t MachThreadList::GetRegisterContext(nub_thread_t tid, void *buf,
+ size_t buf_len) {
+ MachThreadSP thread_sp(GetThreadByID(tid));
+ if (thread_sp)
+ return thread_sp->GetRegisterContext(buf, buf_len);
+ return 0;
+}
+
+nub_size_t MachThreadList::SetRegisterContext(nub_thread_t tid, const void *buf,
+ size_t buf_len) {
+ MachThreadSP thread_sp(GetThreadByID(tid));
+ if (thread_sp)
+ return thread_sp->SetRegisterContext(buf, buf_len);
+ return 0;
+}
+
+uint32_t MachThreadList::SaveRegisterState(nub_thread_t tid) {
+ MachThreadSP thread_sp(GetThreadByID(tid));
+ if (thread_sp)
+ return thread_sp->SaveRegisterState();
+ return 0;
+}
+
+bool MachThreadList::RestoreRegisterState(nub_thread_t tid, uint32_t save_id) {
+ MachThreadSP thread_sp(GetThreadByID(tid));
+ if (thread_sp)
+ return thread_sp->RestoreRegisterState(save_id);
+ return false;
+}
+
+nub_size_t MachThreadList::NumThreads() const {
+ PTHREAD_MUTEX_LOCKER(locker, m_threads_mutex);
+ return m_threads.size();
+}
+
+nub_thread_t MachThreadList::ThreadIDAtIndex(nub_size_t idx) const {
+ PTHREAD_MUTEX_LOCKER(locker, m_threads_mutex);
+ if (idx < m_threads.size())
+ return m_threads[idx]->ThreadID();
+ return INVALID_NUB_THREAD;
+}
+
+nub_thread_t MachThreadList::CurrentThreadID() {
+ MachThreadSP thread_sp;
+ CurrentThread(thread_sp);
+ if (thread_sp.get())
+ return thread_sp->ThreadID();
+ return INVALID_NUB_THREAD;
+}
+
+bool MachThreadList::NotifyException(MachException::Data &exc) {
+ MachThreadSP thread_sp(GetThreadByMachPortNumber(exc.thread_port));
+ if (thread_sp) {
+ thread_sp->NotifyException(exc);
+ return true;
+ }
+ return false;
+}
+
+void MachThreadList::Clear() {
+ PTHREAD_MUTEX_LOCKER(locker, m_threads_mutex);
+ m_threads.clear();
+}
+
+uint32_t
+MachThreadList::UpdateThreadList(MachProcess *process, bool update,
+ MachThreadList::collection *new_threads) {
+ // locker will keep a mutex locked until it goes out of scope
+ DNBLogThreadedIf(LOG_THREAD, "MachThreadList::UpdateThreadList (pid = %4.4x, "
+ "update = %u) process stop count = %u",
+ process->ProcessID(), update, process->StopCount());
+ PTHREAD_MUTEX_LOCKER(locker, m_threads_mutex);
+
+ if (process->StopCount() == 0) {
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, process->ProcessID()};
+ struct kinfo_proc processInfo;
+ size_t bufsize = sizeof(processInfo);
+ if (sysctl(mib, (unsigned)(sizeof(mib) / sizeof(int)), &processInfo,
+ &bufsize, NULL, 0) == 0 &&
+ bufsize > 0) {
+ if (processInfo.kp_proc.p_flag & P_LP64)
+ m_is_64_bit = true;
+ }
+#if defined(__i386__) || defined(__x86_64__)
+ if (m_is_64_bit)
+ DNBArchProtocol::SetArchitecture(CPU_TYPE_X86_64);
+ else
+ DNBArchProtocol::SetArchitecture(CPU_TYPE_I386);
+#elif defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
+ if (m_is_64_bit)
+ DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64);
+ else {
+ if (process->GetCPUType() == CPU_TYPE_ARM64_32)
+ DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64_32);
+ else
+ DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM);
+ }
+#endif
+ }
+
+ if (m_threads.empty() || update) {
+ thread_array_t thread_list = NULL;
+ mach_msg_type_number_t thread_list_count = 0;
+ task_t task = process->Task().TaskPort();
+ DNBError err(::task_threads(task, &thread_list, &thread_list_count),
+ DNBError::MachKernel);
+
+ if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail())
+ err.LogThreaded("::task_threads ( task = 0x%4.4x, thread_list => %p, "
+ "thread_list_count => %u )",
+ task, thread_list, thread_list_count);
+
+ if (err.Status() == KERN_SUCCESS && thread_list_count > 0) {
+ MachThreadList::collection currThreads;
+ size_t idx;
+ // Iterator through the current thread list and see which threads
+ // we already have in our list (keep them), which ones we don't
+ // (add them), and which ones are not around anymore (remove them).
+ for (idx = 0; idx < thread_list_count; ++idx) {
+ const thread_t mach_port_num = thread_list[idx];
+
+ uint64_t unique_thread_id =
+ MachThread::GetGloballyUniqueThreadIDForMachPortID(mach_port_num);
+ MachThreadSP thread_sp(GetThreadByID(unique_thread_id));
+ if (thread_sp) {
+ // Keep the existing thread class
+ currThreads.push_back(thread_sp);
+ } else {
+ // We don't have this thread, lets add it.
+ thread_sp = std::make_shared<MachThread>(
+ process, m_is_64_bit, unique_thread_id, mach_port_num);
+
+ // Add the new thread regardless of its is user ready state...
+ // Make sure the thread is ready to be displayed and shown to users
+ // before we add this thread to our list...
+ if (thread_sp->IsUserReady()) {
+ if (new_threads)
+ new_threads->push_back(thread_sp);
+
+ currThreads.push_back(thread_sp);
+ }
+ }
+ }
+
+ m_threads.swap(currThreads);
+ m_current_thread.reset();
+
+ // Free the vm memory given to us by ::task_threads()
+ vm_size_t thread_list_size =
+ (vm_size_t)(thread_list_count * sizeof(thread_t));
+ ::vm_deallocate(::mach_task_self(), (vm_address_t)thread_list,
+ thread_list_size);
+ }
+ }
+ return static_cast<uint32_t>(m_threads.size());
+}
+
+void MachThreadList::CurrentThread(MachThreadSP &thread_sp) {
+ // locker will keep a mutex locked until it goes out of scope
+ PTHREAD_MUTEX_LOCKER(locker, m_threads_mutex);
+ if (m_current_thread.get() == NULL) {
+ // Figure out which thread is going to be our current thread.
+ // This is currently done by finding the first thread in the list
+ // that has a valid exception.
+ const size_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx) {
+ if (m_threads[idx]->GetStopException().IsValid()) {
+ m_current_thread = m_threads[idx];
+ break;
+ }
+ }
+ }
+ thread_sp = m_current_thread;
+}
+
+void MachThreadList::Dump() const {
+ PTHREAD_MUTEX_LOCKER(locker, m_threads_mutex);
+ const size_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx) {
+ m_threads[idx]->Dump(idx);
+ }
+}
+
+void MachThreadList::ProcessWillResume(
+ MachProcess *process, const DNBThreadResumeActions &thread_actions) {
+ PTHREAD_MUTEX_LOCKER(locker, m_threads_mutex);
+
+ // Update our thread list, because sometimes libdispatch or the kernel
+ // will spawn threads while a task is suspended.
+ MachThreadList::collection new_threads;
+
+ // First figure out if we were planning on running only one thread, and if so
+ // force that thread to resume.
+ bool run_one_thread;
+ nub_thread_t solo_thread = INVALID_NUB_THREAD;
+ if (thread_actions.GetSize() > 0 &&
+ thread_actions.NumActionsWithState(eStateStepping) +
+ thread_actions.NumActionsWithState(eStateRunning) ==
+ 1) {
+ run_one_thread = true;
+ const DNBThreadResumeAction *action_ptr = thread_actions.GetFirst();
+ size_t num_actions = thread_actions.GetSize();
+ for (size_t i = 0; i < num_actions; i++, action_ptr++) {
+ if (action_ptr->state == eStateStepping ||
+ action_ptr->state == eStateRunning) {
+ solo_thread = action_ptr->tid;
+ break;
+ }
+ }
+ } else
+ run_one_thread = false;
+
+ UpdateThreadList(process, true, &new_threads);
+
+ DNBThreadResumeAction resume_new_threads = {-1U, eStateRunning, 0,
+ INVALID_NUB_ADDRESS};
+ // If we are planning to run only one thread, any new threads should be
+ // suspended.
+ if (run_one_thread)
+ resume_new_threads.state = eStateSuspended;
+
+ const size_t num_new_threads = new_threads.size();
+ const size_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx) {
+ MachThread *thread = m_threads[idx].get();
+ bool handled = false;
+ for (uint32_t new_idx = 0; new_idx < num_new_threads; ++new_idx) {
+ if (thread == new_threads[new_idx].get()) {
+ thread->ThreadWillResume(&resume_new_threads);
+ handled = true;
+ break;
+ }
+ }
+
+ if (!handled) {
+ const DNBThreadResumeAction *thread_action =
+ thread_actions.GetActionForThread(thread->ThreadID(), true);
+ // There must always be a thread action for every thread.
+ assert(thread_action);
+ bool others_stopped = false;
+ if (solo_thread == thread->ThreadID())
+ others_stopped = true;
+ thread->ThreadWillResume(thread_action, others_stopped);
+ }
+ }
+
+ if (new_threads.size()) {
+ for (uint32_t idx = 0; idx < num_new_threads; ++idx) {
+ DNBLogThreadedIf(
+ LOG_THREAD, "MachThreadList::ProcessWillResume (pid = %4.4x) "
+ "stop-id=%u, resuming newly discovered thread: "
+ "0x%8.8" PRIx64 ", thread-is-user-ready=%i)",
+ process->ProcessID(), process->StopCount(),
+ new_threads[idx]->ThreadID(), new_threads[idx]->IsUserReady());
+ }
+ }
+}
+
+uint32_t MachThreadList::ProcessDidStop(MachProcess *process) {
+ PTHREAD_MUTEX_LOCKER(locker, m_threads_mutex);
+ // Update our thread list
+ const uint32_t num_threads = UpdateThreadList(process, true);
+ for (uint32_t idx = 0; idx < num_threads; ++idx) {
+ m_threads[idx]->ThreadDidStop();
+ }
+ return num_threads;
+}
+
+// Check each thread in our thread list to see if we should notify our
+// client of the current halt in execution.
+//
+// Breakpoints can have callback functions associated with them than
+// can return true to stop, or false to continue executing the inferior.
+//
+// RETURNS
+// true if we should stop and notify our clients
+// false if we should resume our child process and skip notification
+bool MachThreadList::ShouldStop(bool &step_more) {
+ PTHREAD_MUTEX_LOCKER(locker, m_threads_mutex);
+ uint32_t should_stop = false;
+ const size_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; !should_stop && idx < num_threads; ++idx) {
+ should_stop = m_threads[idx]->ShouldStop(step_more);
+ }
+ return should_stop;
+}
+
+void MachThreadList::NotifyBreakpointChanged(const DNBBreakpoint *bp) {
+ PTHREAD_MUTEX_LOCKER(locker, m_threads_mutex);
+ const size_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx) {
+ m_threads[idx]->NotifyBreakpointChanged(bp);
+ }
+}
+
+uint32_t
+MachThreadList::EnableHardwareBreakpoint(const DNBBreakpoint *bp) const {
+ if (bp != NULL) {
+ const size_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ m_threads[idx]->EnableHardwareBreakpoint(bp);
+ }
+ return INVALID_NUB_HW_INDEX;
+}
+
+bool MachThreadList::DisableHardwareBreakpoint(const DNBBreakpoint *bp) const {
+ if (bp != NULL) {
+ const size_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ m_threads[idx]->DisableHardwareBreakpoint(bp);
+ }
+ return false;
+}
+
+// DNBWatchpointSet() -> MachProcess::CreateWatchpoint() ->
+// MachProcess::EnableWatchpoint()
+// -> MachThreadList::EnableHardwareWatchpoint().
+uint32_t
+MachThreadList::EnableHardwareWatchpoint(const DNBBreakpoint *wp) const {
+ uint32_t hw_index = INVALID_NUB_HW_INDEX;
+ if (wp != NULL) {
+ PTHREAD_MUTEX_LOCKER(locker, m_threads_mutex);
+ const size_t num_threads = m_threads.size();
+ // On Mac OS X we have to prime the control registers for new threads. We
+ // do this
+ // using the control register data for the first thread, for lack of a
+ // better way of choosing.
+ bool also_set_on_task = true;
+ for (uint32_t idx = 0; idx < num_threads; ++idx) {
+ if ((hw_index = m_threads[idx]->EnableHardwareWatchpoint(
+ wp, also_set_on_task)) == INVALID_NUB_HW_INDEX) {
+ // We know that idx failed for some reason. Let's rollback the
+ // transaction for [0, idx).
+ for (uint32_t i = 0; i < idx; ++i)
+ m_threads[i]->RollbackTransForHWP();
+ return INVALID_NUB_HW_INDEX;
+ }
+ also_set_on_task = false;
+ }
+ // Notify each thread to commit the pending transaction.
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ m_threads[idx]->FinishTransForHWP();
+ }
+ return hw_index;
+}
+
+bool MachThreadList::DisableHardwareWatchpoint(const DNBBreakpoint *wp) const {
+ if (wp != NULL) {
+ PTHREAD_MUTEX_LOCKER(locker, m_threads_mutex);
+ const size_t num_threads = m_threads.size();
+
+ // On Mac OS X we have to prime the control registers for new threads. We
+ // do this
+ // using the control register data for the first thread, for lack of a
+ // better way of choosing.
+ bool also_set_on_task = true;
+ for (uint32_t idx = 0; idx < num_threads; ++idx) {
+ if (!m_threads[idx]->DisableHardwareWatchpoint(wp, also_set_on_task)) {
+ // We know that idx failed for some reason. Let's rollback the
+ // transaction for [0, idx).
+ for (uint32_t i = 0; i < idx; ++i)
+ m_threads[i]->RollbackTransForHWP();
+ return false;
+ }
+ also_set_on_task = false;
+ }
+ // Notify each thread to commit the pending transaction.
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ m_threads[idx]->FinishTransForHWP();
+
+ return true;
+ }
+ return false;
+}
+
+uint32_t MachThreadList::NumSupportedHardwareWatchpoints() const {
+ PTHREAD_MUTEX_LOCKER(locker, m_threads_mutex);
+ const size_t num_threads = m_threads.size();
+ // Use an arbitrary thread to retrieve the number of supported hardware
+ // watchpoints.
+ if (num_threads)
+ return m_threads[0]->NumSupportedHardwareWatchpoints();
+ return 0;
+}
+
+uint32_t MachThreadList::GetThreadIndexForThreadStoppedWithSignal(
+ const int signo) const {
+ PTHREAD_MUTEX_LOCKER(locker, m_threads_mutex);
+ uint32_t should_stop = false;
+ const size_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; !should_stop && idx < num_threads; ++idx) {
+ if (m_threads[idx]->GetStopException().SoftSignal() == signo)
+ return idx;
+ }
+ return UINT32_MAX;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThreadList.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThreadList.h
new file mode 100644
index 00000000000..b9b7aa8c4c9
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachThreadList.h
@@ -0,0 +1,96 @@
+//===-- MachThreadList.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/19/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachThreadList_h__
+#define __MachThreadList_h__
+
+#include "MachThread.h"
+#include "ThreadInfo.h"
+
+class DNBThreadResumeActions;
+
+class MachThreadList {
+public:
+ MachThreadList();
+ ~MachThreadList();
+
+ void Clear();
+ void Dump() const;
+ bool GetRegisterValue(nub_thread_t tid, uint32_t set, uint32_t reg,
+ DNBRegisterValue *reg_value) const;
+ bool SetRegisterValue(nub_thread_t tid, uint32_t set, uint32_t reg,
+ const DNBRegisterValue *reg_value) const;
+ nub_size_t GetRegisterContext(nub_thread_t tid, void *buf, size_t buf_len);
+ nub_size_t SetRegisterContext(nub_thread_t tid, const void *buf,
+ size_t buf_len);
+ uint32_t SaveRegisterState(nub_thread_t tid);
+ bool RestoreRegisterState(nub_thread_t tid, uint32_t save_id);
+ const char *GetThreadInfo(nub_thread_t tid) const;
+ void ProcessWillResume(MachProcess *process,
+ const DNBThreadResumeActions &thread_actions);
+ uint32_t ProcessDidStop(MachProcess *process);
+ bool NotifyException(MachException::Data &exc);
+ bool ShouldStop(bool &step_more);
+ const char *GetName(nub_thread_t tid);
+ nub_state_t GetState(nub_thread_t tid);
+ nub_thread_t SetCurrentThread(nub_thread_t tid);
+
+ ThreadInfo::QoS GetRequestedQoS(nub_thread_t tid, nub_addr_t tsd,
+ uint64_t dti_qos_class_index);
+ nub_addr_t GetPThreadT(nub_thread_t tid);
+ nub_addr_t GetDispatchQueueT(nub_thread_t tid);
+ nub_addr_t
+ GetTSDAddressForThread(nub_thread_t tid,
+ uint64_t plo_pthread_tsd_base_address_offset,
+ uint64_t plo_pthread_tsd_base_offset,
+ uint64_t plo_pthread_tsd_entry_size);
+
+ bool GetThreadStoppedReason(nub_thread_t tid,
+ struct DNBThreadStopInfo *stop_info) const;
+ void DumpThreadStoppedReason(nub_thread_t tid) const;
+ bool GetIdentifierInfo(nub_thread_t tid,
+ thread_identifier_info_data_t *ident_info);
+ nub_size_t NumThreads() const;
+ nub_thread_t ThreadIDAtIndex(nub_size_t idx) const;
+ nub_thread_t CurrentThreadID();
+ void CurrentThread(MachThreadSP &threadSP);
+ void NotifyBreakpointChanged(const DNBBreakpoint *bp);
+ uint32_t EnableHardwareBreakpoint(const DNBBreakpoint *bp) const;
+ bool DisableHardwareBreakpoint(const DNBBreakpoint *bp) const;
+ uint32_t EnableHardwareWatchpoint(const DNBBreakpoint *wp) const;
+ bool DisableHardwareWatchpoint(const DNBBreakpoint *wp) const;
+ uint32_t NumSupportedHardwareWatchpoints() const;
+
+ uint32_t GetThreadIndexForThreadStoppedWithSignal(const int signo) const;
+
+ MachThreadSP GetThreadByID(nub_thread_t tid) const;
+
+ MachThreadSP GetThreadByMachPortNumber(thread_t mach_port_number) const;
+ nub_thread_t GetThreadIDByMachPortNumber(thread_t mach_port_number) const;
+ thread_t GetMachPortNumberByThreadID(nub_thread_t globally_unique_id) const;
+
+protected:
+ typedef std::vector<MachThreadSP> collection;
+ typedef collection::iterator iterator;
+ typedef collection::const_iterator const_iterator;
+
+ uint32_t UpdateThreadList(MachProcess *process, bool update,
+ collection *num_threads = NULL);
+ // const_iterator FindThreadByID (thread_t tid) const;
+
+ collection m_threads;
+ mutable PThreadMutex m_threads_mutex;
+ MachThreadSP m_current_thread;
+ bool m_is_64_bit;
+};
+
+#endif // #ifndef __MachThreadList_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp
new file mode 100644
index 00000000000..2b039c7b16c
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp
@@ -0,0 +1,296 @@
+//===-- MachVMMemory.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/26/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachVMMemory.h"
+#include "DNBLog.h"
+#include "MachVMRegion.h"
+#include <dlfcn.h>
+#include <mach/mach_vm.h>
+#include <mach/shared_region.h>
+#include <sys/sysctl.h>
+
+#if defined(WITH_FBS) || defined(WITH_BKS)
+extern "C" {
+#import <System/sys/kern_memorystatus.h>
+}
+#endif
+
+static const vm_size_t kInvalidPageSize = ~0;
+
+MachVMMemory::MachVMMemory() : m_page_size(kInvalidPageSize), m_err(0) {}
+
+MachVMMemory::~MachVMMemory() {}
+
+nub_size_t MachVMMemory::PageSize(task_t task) {
+ if (m_page_size == kInvalidPageSize) {
+#if defined(TASK_VM_INFO) && TASK_VM_INFO >= 22
+ if (task != TASK_NULL) {
+ kern_return_t kr;
+ mach_msg_type_number_t info_count = TASK_VM_INFO_COUNT;
+ task_vm_info_data_t vm_info;
+ kr = task_info(task, TASK_VM_INFO, (task_info_t)&vm_info, &info_count);
+ if (kr == KERN_SUCCESS) {
+ DNBLogThreadedIf(
+ LOG_TASK,
+ "MachVMMemory::PageSize task_info returned page size of 0x%x",
+ (int)vm_info.page_size);
+ m_page_size = vm_info.page_size;
+ return m_page_size;
+ } else {
+ DNBLogThreadedIf(LOG_TASK, "MachVMMemory::PageSize task_info call "
+ "failed to get page size, TASK_VM_INFO %d, "
+ "TASK_VM_INFO_COUNT %d, kern return %d",
+ TASK_VM_INFO, TASK_VM_INFO_COUNT, kr);
+ }
+ }
+#endif
+ m_err = ::host_page_size(::mach_host_self(), &m_page_size);
+ if (m_err.Fail())
+ m_page_size = 0;
+ }
+ return m_page_size;
+}
+
+nub_size_t MachVMMemory::MaxBytesLeftInPage(task_t task, nub_addr_t addr,
+ nub_size_t count) {
+ const nub_size_t page_size = PageSize(task);
+ if (page_size > 0) {
+ nub_size_t page_offset = (addr % page_size);
+ nub_size_t bytes_left_in_page = page_size - page_offset;
+ if (count > bytes_left_in_page)
+ count = bytes_left_in_page;
+ }
+ return count;
+}
+
+nub_bool_t MachVMMemory::GetMemoryRegionInfo(task_t task, nub_addr_t address,
+ DNBRegionInfo *region_info) {
+ MachVMRegion vmRegion(task);
+
+ if (vmRegion.GetRegionForAddress(address)) {
+ region_info->addr = vmRegion.StartAddress();
+ region_info->size = vmRegion.GetByteSize();
+ region_info->permissions = vmRegion.GetDNBPermissions();
+ } else {
+ region_info->addr = address;
+ region_info->size = 0;
+ if (vmRegion.GetError().Success()) {
+ // vmRegion.GetRegionForAddress() return false, indicating that "address"
+ // wasn't in a valid region, but the "vmRegion" info was successfully
+ // read from the task which means the info describes the next valid
+ // region from which we can infer the size of this invalid region
+ mach_vm_address_t start_addr = vmRegion.StartAddress();
+ if (address < start_addr)
+ region_info->size = start_addr - address;
+ }
+ // If we can't get any info about the size from the next region it means
+ // we asked about an address that was past all mappings, so the size
+ // of this region will take up all remaining address space.
+ if (region_info->size == 0)
+ region_info->size = INVALID_NUB_ADDRESS - region_info->addr;
+
+ // Not readable, writeable or executable
+ region_info->permissions = 0;
+ }
+ return true;
+}
+
+static uint64_t GetPhysicalMemory() {
+ // This doesn't change often at all. No need to poll each time.
+ static uint64_t physical_memory = 0;
+ static bool calculated = false;
+ if (calculated)
+ return physical_memory;
+
+ size_t len = sizeof(physical_memory);
+ sysctlbyname("hw.memsize", &physical_memory, &len, NULL, 0);
+
+ calculated = true;
+ return physical_memory;
+}
+
+nub_bool_t MachVMMemory::GetMemoryProfile(
+ DNBProfileDataScanType scanType, task_t task, struct task_basic_info ti,
+ cpu_type_t cputype, nub_process_t pid, vm_statistics64_data_t &vminfo,
+ uint64_t &physical_memory, uint64_t &anonymous,
+ uint64_t &phys_footprint, uint64_t &memory_cap)
+{
+ if (scanType & eProfileHostMemory)
+ physical_memory = GetPhysicalMemory();
+
+ if (scanType & eProfileMemory) {
+ static mach_port_t localHost = mach_host_self();
+ mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;
+ host_statistics64(localHost, HOST_VM_INFO64, (host_info64_t)&vminfo,
+ &count);
+
+ kern_return_t kr;
+ mach_msg_type_number_t info_count;
+ task_vm_info_data_t vm_info;
+
+ info_count = TASK_VM_INFO_COUNT;
+ kr = task_info(task, TASK_VM_INFO_PURGEABLE, (task_info_t)&vm_info, &info_count);
+ if (kr == KERN_SUCCESS) {
+ if (scanType & eProfileMemoryAnonymous) {
+ anonymous = vm_info.internal + vm_info.compressed - vm_info.purgeable_volatile_pmap;
+ }
+
+ phys_footprint = vm_info.phys_footprint;
+ }
+ }
+
+#if defined(WITH_FBS) || defined(WITH_BKS)
+ if (scanType & eProfileMemoryCap) {
+ memorystatus_memlimit_properties_t memlimit_properties;
+ memset(&memlimit_properties, 0, sizeof(memlimit_properties));
+ if (memorystatus_control(MEMORYSTATUS_CMD_GET_MEMLIMIT_PROPERTIES, pid, 0, &memlimit_properties, sizeof(memlimit_properties)) == 0) {
+ memory_cap = memlimit_properties.memlimit_active;
+ }
+ }
+#endif
+
+ return true;
+}
+
+nub_size_t MachVMMemory::Read(task_t task, nub_addr_t address, void *data,
+ nub_size_t data_count) {
+ if (data == NULL || data_count == 0)
+ return 0;
+
+ nub_size_t total_bytes_read = 0;
+ nub_addr_t curr_addr = address;
+ uint8_t *curr_data = (uint8_t *)data;
+ while (total_bytes_read < data_count) {
+ mach_vm_size_t curr_size =
+ MaxBytesLeftInPage(task, curr_addr, data_count - total_bytes_read);
+ mach_msg_type_number_t curr_bytes_read = 0;
+ vm_offset_t vm_memory = 0;
+ m_err = ::mach_vm_read(task, curr_addr, curr_size, &vm_memory,
+ &curr_bytes_read);
+
+ if (DNBLogCheckLogBit(LOG_MEMORY))
+ m_err.LogThreaded("::mach_vm_read ( task = 0x%4.4x, addr = 0x%8.8llx, "
+ "size = %llu, data => %8.8p, dataCnt => %i )",
+ task, (uint64_t)curr_addr, (uint64_t)curr_size,
+ vm_memory, curr_bytes_read);
+
+ if (m_err.Success()) {
+ if (curr_bytes_read != curr_size) {
+ if (DNBLogCheckLogBit(LOG_MEMORY))
+ m_err.LogThreaded(
+ "::mach_vm_read ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, "
+ "data => %8.8p, dataCnt=>%i ) only read %u of %llu bytes",
+ task, (uint64_t)curr_addr, (uint64_t)curr_size, vm_memory,
+ curr_bytes_read, curr_bytes_read, (uint64_t)curr_size);
+ }
+ ::memcpy(curr_data, (void *)vm_memory, curr_bytes_read);
+ ::vm_deallocate(mach_task_self(), vm_memory, curr_bytes_read);
+ total_bytes_read += curr_bytes_read;
+ curr_addr += curr_bytes_read;
+ curr_data += curr_bytes_read;
+ } else {
+ break;
+ }
+ }
+ return total_bytes_read;
+}
+
+nub_size_t MachVMMemory::Write(task_t task, nub_addr_t address,
+ const void *data, nub_size_t data_count) {
+ MachVMRegion vmRegion(task);
+
+ nub_size_t total_bytes_written = 0;
+ nub_addr_t curr_addr = address;
+ const uint8_t *curr_data = (const uint8_t *)data;
+
+ while (total_bytes_written < data_count) {
+ if (vmRegion.GetRegionForAddress(curr_addr)) {
+ mach_vm_size_t curr_data_count = data_count - total_bytes_written;
+ mach_vm_size_t region_bytes_left = vmRegion.BytesRemaining(curr_addr);
+ if (region_bytes_left == 0) {
+ break;
+ }
+ if (curr_data_count > region_bytes_left)
+ curr_data_count = region_bytes_left;
+
+ if (vmRegion.SetProtections(curr_addr, curr_data_count,
+ VM_PROT_READ | VM_PROT_WRITE)) {
+ nub_size_t bytes_written =
+ WriteRegion(task, curr_addr, curr_data, curr_data_count);
+ if (bytes_written <= 0) {
+ // Status should have already be posted by WriteRegion...
+ break;
+ } else {
+ total_bytes_written += bytes_written;
+ curr_addr += bytes_written;
+ curr_data += bytes_written;
+ }
+ } else {
+ DNBLogThreadedIf(
+ LOG_MEMORY_PROTECTIONS, "Failed to set read/write protections on "
+ "region for address: [0x%8.8llx-0x%8.8llx)",
+ (uint64_t)curr_addr, (uint64_t)(curr_addr + curr_data_count));
+ break;
+ }
+ } else {
+ DNBLogThreadedIf(LOG_MEMORY_PROTECTIONS,
+ "Failed to get region for address: 0x%8.8llx",
+ (uint64_t)address);
+ break;
+ }
+ }
+
+ return total_bytes_written;
+}
+
+nub_size_t MachVMMemory::WriteRegion(task_t task, const nub_addr_t address,
+ const void *data,
+ const nub_size_t data_count) {
+ if (data == NULL || data_count == 0)
+ return 0;
+
+ nub_size_t total_bytes_written = 0;
+ nub_addr_t curr_addr = address;
+ const uint8_t *curr_data = (const uint8_t *)data;
+ while (total_bytes_written < data_count) {
+ mach_msg_type_number_t curr_data_count =
+ static_cast<mach_msg_type_number_t>(MaxBytesLeftInPage(
+ task, curr_addr, data_count - total_bytes_written));
+ m_err =
+ ::mach_vm_write(task, curr_addr, (pointer_t)curr_data, curr_data_count);
+ if (DNBLogCheckLogBit(LOG_MEMORY) || m_err.Fail())
+ m_err.LogThreaded("::mach_vm_write ( task = 0x%4.4x, addr = 0x%8.8llx, "
+ "data = %8.8p, dataCnt = %u )",
+ task, (uint64_t)curr_addr, curr_data, curr_data_count);
+
+#if !defined(__i386__) && !defined(__x86_64__)
+ vm_machine_attribute_val_t mattr_value = MATTR_VAL_CACHE_FLUSH;
+
+ m_err = ::vm_machine_attribute(task, curr_addr, curr_data_count,
+ MATTR_CACHE, &mattr_value);
+ if (DNBLogCheckLogBit(LOG_MEMORY) || m_err.Fail())
+ m_err.LogThreaded("::vm_machine_attribute ( task = 0x%4.4x, addr = "
+ "0x%8.8llx, size = %u, attr = MATTR_CACHE, mattr_value "
+ "=> MATTR_VAL_CACHE_FLUSH )",
+ task, (uint64_t)curr_addr, curr_data_count);
+#endif
+
+ if (m_err.Success()) {
+ total_bytes_written += curr_data_count;
+ curr_addr += curr_data_count;
+ curr_data += curr_data_count;
+ } else {
+ break;
+ }
+ }
+ return total_bytes_written;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h
new file mode 100644
index 00000000000..538e11232a8
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h
@@ -0,0 +1,47 @@
+//===-- MachVMMemory.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/26/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachVMMemory_h__
+#define __MachVMMemory_h__
+
+#include "DNBDefs.h"
+#include "DNBError.h"
+#include <mach/mach.h>
+
+class MachVMMemory {
+public:
+ MachVMMemory();
+ ~MachVMMemory();
+ nub_size_t Read(task_t task, nub_addr_t address, void *data,
+ nub_size_t data_count);
+ nub_size_t Write(task_t task, nub_addr_t address, const void *data,
+ nub_size_t data_count);
+ nub_size_t PageSize(task_t task);
+ nub_bool_t GetMemoryRegionInfo(task_t task, nub_addr_t address,
+ DNBRegionInfo *region_info);
+ nub_bool_t GetMemoryProfile(DNBProfileDataScanType scanType, task_t task,
+ struct task_basic_info ti, cpu_type_t cputype,
+ nub_process_t pid, vm_statistics64_data_t &vminfo,
+ uint64_t &physical_memory, uint64_t &anonymous,
+ uint64_t &phys_footprint, uint64_t &memory_cap);
+
+protected:
+ nub_size_t MaxBytesLeftInPage(task_t task, nub_addr_t addr, nub_size_t count);
+
+ nub_size_t WriteRegion(task_t task, const nub_addr_t address,
+ const void *data, const nub_size_t data_count);
+
+ vm_size_t m_page_size;
+ DNBError m_err;
+};
+
+#endif // #ifndef __MachVMMemory_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp
new file mode 100644
index 00000000000..6cb34292252
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp
@@ -0,0 +1,184 @@
+//===-- MachVMRegion.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/26/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachVMRegion.h"
+#include "DNBLog.h"
+#include <assert.h>
+#include <mach/mach_vm.h>
+
+MachVMRegion::MachVMRegion(task_t task)
+ : m_task(task), m_addr(INVALID_NUB_ADDRESS), m_err(),
+ m_start(INVALID_NUB_ADDRESS), m_size(0), m_depth(-1),
+ m_curr_protection(0), m_protection_addr(INVALID_NUB_ADDRESS),
+ m_protection_size(0) {
+ memset(&m_data, 0, sizeof(m_data));
+}
+
+MachVMRegion::~MachVMRegion() {
+ // Restore any original protections and clear our vars
+ Clear();
+}
+
+void MachVMRegion::Clear() {
+ RestoreProtections();
+ m_addr = INVALID_NUB_ADDRESS;
+ m_err.Clear();
+ m_start = INVALID_NUB_ADDRESS;
+ m_size = 0;
+ m_depth = -1;
+ memset(&m_data, 0, sizeof(m_data));
+ m_curr_protection = 0;
+ m_protection_addr = INVALID_NUB_ADDRESS;
+ m_protection_size = 0;
+}
+
+bool MachVMRegion::SetProtections(mach_vm_address_t addr, mach_vm_size_t size,
+ vm_prot_t prot) {
+ if (ContainsAddress(addr)) {
+ mach_vm_size_t prot_size = size;
+ mach_vm_address_t end_addr = EndAddress();
+ if (prot_size > (end_addr - addr))
+ prot_size = end_addr - addr;
+
+ if (prot_size > 0) {
+ if (prot == (m_curr_protection & VM_PROT_ALL)) {
+ DNBLogThreadedIf(LOG_MEMORY_PROTECTIONS | LOG_VERBOSE,
+ "MachVMRegion::%s: protections (%u) already "
+ "sufficient for task 0x%4.4x at address 0x%8.8llx) ",
+ __FUNCTION__, prot, m_task, (uint64_t)addr);
+ // Protections are already set as requested...
+ return true;
+ } else {
+ m_err = ::mach_vm_protect(m_task, addr, prot_size, 0, prot);
+ if (DNBLogCheckLogBit(LOG_MEMORY_PROTECTIONS))
+ m_err.LogThreaded("::mach_vm_protect ( task = 0x%4.4x, addr = "
+ "0x%8.8llx, size = %llu, set_max = %i, prot = %u )",
+ m_task, (uint64_t)addr, (uint64_t)prot_size, 0,
+ prot);
+ if (m_err.Fail()) {
+ // Try again with the ability to create a copy on write region
+ m_err = ::mach_vm_protect(m_task, addr, prot_size, 0,
+ prot | VM_PROT_COPY);
+ if (DNBLogCheckLogBit(LOG_MEMORY_PROTECTIONS) || m_err.Fail())
+ m_err.LogThreaded("::mach_vm_protect ( task = 0x%4.4x, addr = "
+ "0x%8.8llx, size = %llu, set_max = %i, prot = %u "
+ ")",
+ m_task, (uint64_t)addr, (uint64_t)prot_size, 0,
+ prot | VM_PROT_COPY);
+ }
+ if (m_err.Success()) {
+ m_curr_protection = prot;
+ m_protection_addr = addr;
+ m_protection_size = prot_size;
+ return true;
+ }
+ }
+ } else {
+ DNBLogThreadedIf(LOG_MEMORY_PROTECTIONS | LOG_VERBOSE,
+ "%s: Zero size for task 0x%4.4x at address 0x%8.8llx) ",
+ __FUNCTION__, m_task, (uint64_t)addr);
+ }
+ }
+ return false;
+}
+
+bool MachVMRegion::RestoreProtections() {
+ if (m_curr_protection != m_data.protection && m_protection_size > 0) {
+ m_err = ::mach_vm_protect(m_task, m_protection_addr, m_protection_size, 0,
+ m_data.protection);
+ if (DNBLogCheckLogBit(LOG_MEMORY_PROTECTIONS) || m_err.Fail())
+ m_err.LogThreaded("::mach_vm_protect ( task = 0x%4.4x, addr = 0x%8.8llx, "
+ "size = %llu, set_max = %i, prot = %u )",
+ m_task, (uint64_t)m_protection_addr,
+ (uint64_t)m_protection_size, 0, m_data.protection);
+ if (m_err.Success()) {
+ m_protection_size = 0;
+ m_protection_addr = INVALID_NUB_ADDRESS;
+ m_curr_protection = m_data.protection;
+ return true;
+ }
+ } else {
+ m_err.Clear();
+ return true;
+ }
+
+ return false;
+}
+
+bool MachVMRegion::GetRegionForAddress(nub_addr_t addr) {
+ // Restore any original protections and clear our vars
+ Clear();
+ m_err.Clear();
+ m_addr = addr;
+ m_start = addr;
+ m_depth = 1024;
+ mach_msg_type_number_t info_size = kRegionInfoSize;
+ static_assert(sizeof(info_size) == 4, "");
+ m_err =
+ ::mach_vm_region_recurse(m_task, &m_start, &m_size, &m_depth,
+ (vm_region_recurse_info_t)&m_data, &info_size);
+
+ const bool failed = m_err.Fail();
+ const bool log_protections = DNBLogCheckLogBit(LOG_MEMORY_PROTECTIONS);
+
+ if (log_protections || failed)
+ m_err.LogThreaded("::mach_vm_region_recurse ( task = 0x%4.4x, address => "
+ "0x%8.8llx, size => %llu, nesting_depth => %d, info => "
+ "%p, infoCnt => %d) addr = 0x%8.8llx ",
+ m_task, (uint64_t)m_start, (uint64_t)m_size, m_depth,
+ &m_data, info_size, (uint64_t)addr);
+
+ if (failed)
+ return false;
+ if (log_protections) {
+ DNBLogThreaded("info = { prot = %u, "
+ "max_prot = %u, "
+ "inheritance = 0x%8.8x, "
+ "offset = 0x%8.8llx, "
+ "user_tag = 0x%8.8x, "
+ "ref_count = %u, "
+ "shadow_depth = %u, "
+ "ext_pager = %u, "
+ "share_mode = %u, "
+ "is_submap = %d, "
+ "behavior = %d, "
+ "object_id = 0x%8.8x, "
+ "user_wired_count = 0x%4.4x }",
+ m_data.protection, m_data.max_protection, m_data.inheritance,
+ (uint64_t)m_data.offset, m_data.user_tag, m_data.ref_count,
+ m_data.shadow_depth, m_data.external_pager,
+ m_data.share_mode, m_data.is_submap, m_data.behavior,
+ m_data.object_id, m_data.user_wired_count);
+ }
+ m_curr_protection = m_data.protection;
+
+ // We make a request for an address and got no error back, but this
+ // doesn't mean that "addr" is in the range. The data in this object will
+ // be valid though, so you could see where the next region begins. So we
+ // return false, yet leave "m_err" with a successfull return code.
+ return !((addr < m_start) || (addr >= (m_start + m_size)));
+}
+
+uint32_t MachVMRegion::GetDNBPermissions() const {
+ if (m_addr == INVALID_NUB_ADDRESS || m_start == INVALID_NUB_ADDRESS ||
+ m_size == 0)
+ return 0;
+ uint32_t dnb_permissions = 0;
+
+ if ((m_data.protection & VM_PROT_READ) == VM_PROT_READ)
+ dnb_permissions |= eMemoryPermissionsReadable;
+ if ((m_data.protection & VM_PROT_WRITE) == VM_PROT_WRITE)
+ dnb_permissions |= eMemoryPermissionsWritable;
+ if ((m_data.protection & VM_PROT_EXECUTE) == VM_PROT_EXECUTE)
+ dnb_permissions |= eMemoryPermissionsExecutable;
+ return dnb_permissions;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h
new file mode 100644
index 00000000000..2e6303c4b39
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h
@@ -0,0 +1,72 @@
+//===-- MachVMRegion.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/26/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachVMRegion_h__
+#define __MachVMRegion_h__
+
+#include "DNBDefs.h"
+#include "DNBError.h"
+#include <mach/mach.h>
+
+class MachVMRegion {
+public:
+ MachVMRegion(task_t task);
+ ~MachVMRegion();
+
+ void Clear();
+ mach_vm_address_t StartAddress() const { return m_start; }
+ mach_vm_address_t EndAddress() const { return m_start + m_size; }
+ mach_vm_size_t GetByteSize() const { return m_size; }
+ mach_vm_address_t BytesRemaining(mach_vm_address_t addr) const {
+ if (ContainsAddress(addr))
+ return m_size - (addr - m_start);
+ else
+ return 0;
+ }
+ bool ContainsAddress(mach_vm_address_t addr) const {
+ return addr >= StartAddress() && addr < EndAddress();
+ }
+
+ bool SetProtections(mach_vm_address_t addr, mach_vm_size_t size,
+ vm_prot_t prot);
+ bool RestoreProtections();
+ bool GetRegionForAddress(nub_addr_t addr);
+
+ uint32_t GetDNBPermissions() const;
+
+ const DNBError &GetError() { return m_err; }
+
+protected:
+#if defined(VM_REGION_SUBMAP_SHORT_INFO_COUNT_64)
+ typedef vm_region_submap_short_info_data_64_t RegionInfo;
+ enum { kRegionInfoSize = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 };
+#else
+ typedef vm_region_submap_info_data_64_t RegionInfo;
+ enum { kRegionInfoSize = VM_REGION_SUBMAP_INFO_COUNT_64 };
+#endif
+
+ task_t m_task;
+ mach_vm_address_t m_addr;
+ DNBError m_err;
+ mach_vm_address_t m_start;
+ mach_vm_size_t m_size;
+ natural_t m_depth;
+ RegionInfo m_data;
+ vm_prot_t m_curr_protection; // The current, possibly modified protections.
+ // Original value is saved in m_data.protections.
+ mach_vm_address_t
+ m_protection_addr; // The start address at which protections were changed
+ mach_vm_size_t
+ m_protection_size; // The size of memory that had its protections changed
+};
+
+#endif // #ifndef __MachVMRegion_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/OsLogger.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/OsLogger.cpp
new file mode 100644
index 00000000000..a83afb62b36
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/OsLogger.cpp
@@ -0,0 +1,63 @@
+//===-- OsLogger.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "OsLogger.h"
+#include <Availability.h>
+
+#if (LLDB_USE_OS_LOG) && (__MAC_OS_X_VERSION_MAX_ALLOWED >= 101200)
+
+#include <os/log.h>
+
+#include "DNBDefs.h"
+#include "DNBLog.h"
+
+#define LLDB_OS_LOG_MAX_BUFFER_LENGTH 256
+
+namespace {
+// Darwin os_log logging callback that can be registered with
+// DNBLogSetLogCallback
+void DarwinLogCallback(void *baton, uint32_t flags, const char *format,
+ va_list args) {
+ if (format == nullptr)
+ return;
+
+ static os_log_t g_logger;
+ if (!g_logger) {
+ g_logger = os_log_create("com.apple.dt.lldb", "debugserver");
+ if (!g_logger)
+ return;
+ }
+
+ os_log_type_t log_type;
+ if (flags & DNBLOG_FLAG_FATAL)
+ log_type = OS_LOG_TYPE_FAULT;
+ else if (flags & DNBLOG_FLAG_ERROR)
+ log_type = OS_LOG_TYPE_ERROR;
+ else if (flags & DNBLOG_FLAG_WARNING)
+ log_type = OS_LOG_TYPE_DEFAULT;
+ else if (flags & DNBLOG_FLAG_VERBOSE)
+ log_type = OS_LOG_TYPE_DEBUG;
+ else
+ log_type = OS_LOG_TYPE_DEFAULT;
+
+ // This code is unfortunate. os_log* only takes static strings, but
+ // our current log API isn't set up to make use of that style.
+ char buffer[LLDB_OS_LOG_MAX_BUFFER_LENGTH];
+ vsnprintf(buffer, sizeof(buffer), format, args);
+ os_log_with_type(g_logger, log_type, "%{public}s", buffer);
+}
+}
+
+DNBCallbackLog OsLogger::GetLogFunction() { return DarwinLogCallback; }
+
+#else
+
+DNBCallbackLog OsLogger::GetLogFunction() { return nullptr; }
+
+#endif
+
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/OsLogger.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/OsLogger.h
new file mode 100644
index 00000000000..5a173c42b83
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/OsLogger.h
@@ -0,0 +1,19 @@
+//===-- OsLogger.h ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef OsLogger_h
+#define OsLogger_h
+
+#include "DNBDefs.h"
+
+class OsLogger {
+public:
+ static DNBCallbackLog GetLogFunction();
+};
+
+#endif /* OsLogger_h */
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/ThreadInfo.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/ThreadInfo.h
new file mode 100644
index 00000000000..00c368c6ab0
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/ThreadInfo.h
@@ -0,0 +1,25 @@
+//===-- ThreadInfo.h -----------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __ThreadInfo_h__
+#define __ThreadInfo_h__
+
+namespace ThreadInfo {
+
+class QoS {
+public:
+ QoS() : constant_name(), printable_name(), enum_value(UINT32_MAX) {}
+ bool IsValid() { return enum_value != UINT32_MAX; }
+ std::string constant_name;
+ std::string printable_name;
+ uint32_t enum_value;
+};
+};
+
+#endif // __ThreadInfo_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp
new file mode 100644
index 00000000000..45d05d6e0bd
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.cpp
@@ -0,0 +1,2189 @@
+//===-- DNBArchImpl.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
+
+#include "MacOSX/arm/DNBArchImpl.h"
+#include "ARM_DWARF_Registers.h"
+#include "ARM_ehframe_Registers.h"
+#include "DNB.h"
+#include "DNBBreakpoint.h"
+#include "DNBLog.h"
+#include "DNBRegisterInfo.h"
+#include "MacOSX/MachProcess.h"
+#include "MacOSX/MachThread.h"
+
+#include <inttypes.h>
+#include <sys/sysctl.h>
+
+// BCR address match type
+#define BCR_M_IMVA_MATCH ((uint32_t)(0u << 21))
+#define BCR_M_CONTEXT_ID_MATCH ((uint32_t)(1u << 21))
+#define BCR_M_IMVA_MISMATCH ((uint32_t)(2u << 21))
+#define BCR_M_RESERVED ((uint32_t)(3u << 21))
+
+// Link a BVR/BCR or WVR/WCR pair to another
+#define E_ENABLE_LINKING ((uint32_t)(1u << 20))
+
+// Byte Address Select
+#define BAS_IMVA_PLUS_0 ((uint32_t)(1u << 5))
+#define BAS_IMVA_PLUS_1 ((uint32_t)(1u << 6))
+#define BAS_IMVA_PLUS_2 ((uint32_t)(1u << 7))
+#define BAS_IMVA_PLUS_3 ((uint32_t)(1u << 8))
+#define BAS_IMVA_0_1 ((uint32_t)(3u << 5))
+#define BAS_IMVA_2_3 ((uint32_t)(3u << 7))
+#define BAS_IMVA_ALL ((uint32_t)(0xfu << 5))
+
+// Break only in privileged or user mode
+#define S_RSVD ((uint32_t)(0u << 1))
+#define S_PRIV ((uint32_t)(1u << 1))
+#define S_USER ((uint32_t)(2u << 1))
+#define S_PRIV_USER ((S_PRIV) | (S_USER))
+
+#define BCR_ENABLE ((uint32_t)(1u))
+#define WCR_ENABLE ((uint32_t)(1u))
+
+// Watchpoint load/store
+#define WCR_LOAD ((uint32_t)(1u << 3))
+#define WCR_STORE ((uint32_t)(1u << 4))
+
+// Definitions for the Debug Status and Control Register fields:
+// [5:2] => Method of debug entry
+//#define WATCHPOINT_OCCURRED ((uint32_t)(2u))
+// I'm seeing this, instead.
+#define WATCHPOINT_OCCURRED ((uint32_t)(10u))
+
+// 0xE120BE70
+static const uint8_t g_arm_breakpoint_opcode[] = {0x70, 0xBE, 0x20, 0xE1};
+static const uint8_t g_thumb_breakpoint_opcode[] = {0x70, 0xBE};
+
+// A watchpoint may need to be implemented using two watchpoint registers.
+// e.g. watching an 8-byte region when the device can only watch 4-bytes.
+//
+// This stores the lo->hi mappings. It's safe to initialize to all 0's
+// since hi > lo and therefore LoHi[i] cannot be 0.
+static uint32_t LoHi[16] = {0};
+
+// ARM constants used during decoding
+#define REG_RD 0
+#define LDM_REGLIST 1
+#define PC_REG 15
+#define PC_REGLIST_BIT 0x8000
+
+// ARM conditions
+#define COND_EQ 0x0
+#define COND_NE 0x1
+#define COND_CS 0x2
+#define COND_HS 0x2
+#define COND_CC 0x3
+#define COND_LO 0x3
+#define COND_MI 0x4
+#define COND_PL 0x5
+#define COND_VS 0x6
+#define COND_VC 0x7
+#define COND_HI 0x8
+#define COND_LS 0x9
+#define COND_GE 0xA
+#define COND_LT 0xB
+#define COND_GT 0xC
+#define COND_LE 0xD
+#define COND_AL 0xE
+#define COND_UNCOND 0xF
+
+#define MASK_CPSR_T (1u << 5)
+#define MASK_CPSR_J (1u << 24)
+
+#define MNEMONIC_STRING_SIZE 32
+#define OPERAND_STRING_SIZE 128
+
+// Returns true if the first 16 bit opcode of a thumb instruction indicates
+// the instruction will be a 32 bit thumb opcode
+static bool IsThumb32Opcode(uint16_t opcode) {
+ if (((opcode & 0xE000) == 0xE000) && (opcode & 0x1800))
+ return true;
+ return false;
+}
+
+void DNBArchMachARM::Initialize() {
+ DNBArchPluginInfo arch_plugin_info = {
+ CPU_TYPE_ARM, DNBArchMachARM::Create, DNBArchMachARM::GetRegisterSetInfo,
+ DNBArchMachARM::SoftwareBreakpointOpcode};
+
+ // Register this arch plug-in with the main protocol class
+ DNBArchProtocol::RegisterArchPlugin(arch_plugin_info);
+}
+
+DNBArchProtocol *DNBArchMachARM::Create(MachThread *thread) {
+ DNBArchMachARM *obj = new DNBArchMachARM(thread);
+ return obj;
+}
+
+const uint8_t *DNBArchMachARM::SoftwareBreakpointOpcode(nub_size_t byte_size) {
+ switch (byte_size) {
+ case 2:
+ return g_thumb_breakpoint_opcode;
+ case 4:
+ return g_arm_breakpoint_opcode;
+ }
+ return NULL;
+}
+
+uint32_t DNBArchMachARM::GetCPUType() { return CPU_TYPE_ARM; }
+
+uint64_t DNBArchMachARM::GetPC(uint64_t failValue) {
+ // Get program counter
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.context.gpr.__pc;
+ return failValue;
+}
+
+kern_return_t DNBArchMachARM::SetPC(uint64_t value) {
+ // Get program counter
+ kern_return_t err = GetGPRState(false);
+ if (err == KERN_SUCCESS) {
+ m_state.context.gpr.__pc = (uint32_t)value;
+ err = SetGPRState();
+ }
+ return err == KERN_SUCCESS;
+}
+
+uint64_t DNBArchMachARM::GetSP(uint64_t failValue) {
+ // Get stack pointer
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.context.gpr.__sp;
+ return failValue;
+}
+
+kern_return_t DNBArchMachARM::GetGPRState(bool force) {
+ int set = e_regSetGPR;
+ // Check if we have valid cached registers
+ if (!force && m_state.GetError(set, Read) == KERN_SUCCESS)
+ return KERN_SUCCESS;
+
+ // Read the registers from our thread
+ mach_msg_type_number_t count = ARM_THREAD_STATE_COUNT;
+ kern_return_t kret =
+ ::thread_get_state(m_thread->MachPortNumber(), ARM_THREAD_STATE,
+ (thread_state_t)&m_state.context.gpr, &count);
+ uint32_t *r = &m_state.context.gpr.__r[0];
+ DNBLogThreadedIf(
+ LOG_THREAD, "thread_get_state(0x%4.4x, %u, &gpr, %u) => 0x%8.8x (count = "
+ "%u) regs r0=%8.8x r1=%8.8x r2=%8.8x r3=%8.8x r4=%8.8x "
+ "r5=%8.8x r6=%8.8x r7=%8.8x r8=%8.8x r9=%8.8x r10=%8.8x "
+ "r11=%8.8x s12=%8.8x sp=%8.8x lr=%8.8x pc=%8.8x cpsr=%8.8x",
+ m_thread->MachPortNumber(), ARM_THREAD_STATE, ARM_THREAD_STATE_COUNT,
+ kret, count, r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9],
+ r[10], r[11], r[12], r[13], r[14], r[15], r[16]);
+ m_state.SetError(set, Read, kret);
+ return kret;
+}
+
+kern_return_t DNBArchMachARM::GetVFPState(bool force) {
+ int set = e_regSetVFP;
+ // Check if we have valid cached registers
+ if (!force && m_state.GetError(set, Read) == KERN_SUCCESS)
+ return KERN_SUCCESS;
+
+ kern_return_t kret;
+
+#if defined(__arm64__) || defined(__aarch64__)
+ // Read the registers from our thread
+ mach_msg_type_number_t count = ARM_NEON_STATE_COUNT;
+ kret = ::thread_get_state(m_thread->MachPortNumber(), ARM_NEON_STATE,
+ (thread_state_t)&m_state.context.vfp, &count);
+ if (DNBLogEnabledForAny(LOG_THREAD)) {
+ DNBLogThreaded(
+ "thread_get_state(0x%4.4x, %u, &vfp, %u) => 0x%8.8x (count = %u) regs"
+ "\n q0 = 0x%16.16llx%16.16llx"
+ "\n q1 = 0x%16.16llx%16.16llx"
+ "\n q2 = 0x%16.16llx%16.16llx"
+ "\n q3 = 0x%16.16llx%16.16llx"
+ "\n q4 = 0x%16.16llx%16.16llx"
+ "\n q5 = 0x%16.16llx%16.16llx"
+ "\n q6 = 0x%16.16llx%16.16llx"
+ "\n q7 = 0x%16.16llx%16.16llx"
+ "\n q8 = 0x%16.16llx%16.16llx"
+ "\n q9 = 0x%16.16llx%16.16llx"
+ "\n q10 = 0x%16.16llx%16.16llx"
+ "\n q11 = 0x%16.16llx%16.16llx"
+ "\n q12 = 0x%16.16llx%16.16llx"
+ "\n q13 = 0x%16.16llx%16.16llx"
+ "\n q14 = 0x%16.16llx%16.16llx"
+ "\n q15 = 0x%16.16llx%16.16llx"
+ "\n fpsr = 0x%8.8x"
+ "\n fpcr = 0x%8.8x\n\n",
+ m_thread->MachPortNumber(), ARM_NEON_STATE, ARM_NEON_STATE_COUNT, kret,
+ count, ((uint64_t *)&m_state.context.vfp.__v[0])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[0])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[1])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[1])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[2])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[2])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[3])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[3])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[4])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[4])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[5])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[5])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[6])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[6])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[7])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[7])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[8])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[8])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[9])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[9])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[10])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[10])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[11])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[11])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[12])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[12])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[13])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[13])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[14])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[14])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[15])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[15])[1],
+ m_state.context.vfp.__fpsr, m_state.context.vfp.__fpcr);
+ }
+#else
+ // Read the registers from our thread
+ mach_msg_type_number_t count = ARM_VFP_STATE_COUNT;
+ kret = ::thread_get_state(m_thread->MachPortNumber(), ARM_VFP_STATE,
+ (thread_state_t)&m_state.context.vfp, &count);
+
+ if (DNBLogEnabledForAny(LOG_THREAD)) {
+ uint32_t *r = &m_state.context.vfp.__r[0];
+ DNBLogThreaded(
+ "thread_get_state(0x%4.4x, %u, &gpr, %u) => 0x%8.8x (count => %u)",
+ m_thread->MachPortNumber(), ARM_THREAD_STATE, ARM_THREAD_STATE_COUNT,
+ kret, count);
+ DNBLogThreaded(" s0=%8.8x s1=%8.8x s2=%8.8x s3=%8.8x s4=%8.8x "
+ "s5=%8.8x s6=%8.8x s7=%8.8x",
+ r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7]);
+ DNBLogThreaded(" s8=%8.8x s9=%8.8x s10=%8.8x s11=%8.8x s12=%8.8x "
+ "s13=%8.8x s14=%8.8x s15=%8.8x",
+ r[8], r[9], r[10], r[11], r[12], r[13], r[14], r[15]);
+ DNBLogThreaded(" s16=%8.8x s17=%8.8x s18=%8.8x s19=%8.8x s20=%8.8x "
+ "s21=%8.8x s22=%8.8x s23=%8.8x",
+ r[16], r[17], r[18], r[19], r[20], r[21], r[22], r[23]);
+ DNBLogThreaded(" s24=%8.8x s25=%8.8x s26=%8.8x s27=%8.8x s28=%8.8x "
+ "s29=%8.8x s30=%8.8x s31=%8.8x",
+ r[24], r[25], r[26], r[27], r[28], r[29], r[30], r[31]);
+ DNBLogThreaded(" s32=%8.8x s33=%8.8x s34=%8.8x s35=%8.8x s36=%8.8x "
+ "s37=%8.8x s38=%8.8x s39=%8.8x",
+ r[32], r[33], r[34], r[35], r[36], r[37], r[38], r[39]);
+ DNBLogThreaded(" s40=%8.8x s41=%8.8x s42=%8.8x s43=%8.8x s44=%8.8x "
+ "s45=%8.8x s46=%8.8x s47=%8.8x",
+ r[40], r[41], r[42], r[43], r[44], r[45], r[46], r[47]);
+ DNBLogThreaded(" s48=%8.8x s49=%8.8x s50=%8.8x s51=%8.8x s52=%8.8x "
+ "s53=%8.8x s54=%8.8x s55=%8.8x",
+ r[48], r[49], r[50], r[51], r[52], r[53], r[54], r[55]);
+ DNBLogThreaded(" s56=%8.8x s57=%8.8x s58=%8.8x s59=%8.8x s60=%8.8x "
+ "s61=%8.8x s62=%8.8x s63=%8.8x fpscr=%8.8x",
+ r[56], r[57], r[58], r[59], r[60], r[61], r[62], r[63],
+ r[64]);
+ }
+
+#endif
+ m_state.SetError(set, Read, kret);
+ return kret;
+}
+
+kern_return_t DNBArchMachARM::GetEXCState(bool force) {
+ int set = e_regSetEXC;
+ // Check if we have valid cached registers
+ if (!force && m_state.GetError(set, Read) == KERN_SUCCESS)
+ return KERN_SUCCESS;
+
+ // Read the registers from our thread
+ mach_msg_type_number_t count = ARM_EXCEPTION_STATE_COUNT;
+ kern_return_t kret =
+ ::thread_get_state(m_thread->MachPortNumber(), ARM_EXCEPTION_STATE,
+ (thread_state_t)&m_state.context.exc, &count);
+ m_state.SetError(set, Read, kret);
+ return kret;
+}
+
+static void DumpDBGState(const DNBArchMachARM::DBG &dbg) {
+ uint32_t i = 0;
+ for (i = 0; i < 16; i++) {
+ DNBLogThreadedIf(LOG_STEP, "BVR%-2u/BCR%-2u = { 0x%8.8x, 0x%8.8x } "
+ "WVR%-2u/WCR%-2u = { 0x%8.8x, 0x%8.8x }",
+ i, i, dbg.__bvr[i], dbg.__bcr[i], i, i, dbg.__wvr[i],
+ dbg.__wcr[i]);
+ }
+}
+
+kern_return_t DNBArchMachARM::GetDBGState(bool force) {
+ int set = e_regSetDBG;
+
+ // Check if we have valid cached registers
+ if (!force && m_state.GetError(set, Read) == KERN_SUCCESS)
+ return KERN_SUCCESS;
+
+// Read the registers from our thread
+#if defined(ARM_DEBUG_STATE32) && (defined(__arm64__) || defined(__aarch64__))
+ mach_msg_type_number_t count = ARM_DEBUG_STATE32_COUNT;
+ kern_return_t kret =
+ ::thread_get_state(m_thread->MachPortNumber(), ARM_DEBUG_STATE32,
+ (thread_state_t)&m_state.dbg, &count);
+#else
+ mach_msg_type_number_t count = ARM_DEBUG_STATE_COUNT;
+ kern_return_t kret =
+ ::thread_get_state(m_thread->MachPortNumber(), ARM_DEBUG_STATE,
+ (thread_state_t)&m_state.dbg, &count);
+#endif
+ m_state.SetError(set, Read, kret);
+
+ return kret;
+}
+
+kern_return_t DNBArchMachARM::SetGPRState() {
+ int set = e_regSetGPR;
+ kern_return_t kret = ::thread_set_state(
+ m_thread->MachPortNumber(), ARM_THREAD_STATE,
+ (thread_state_t)&m_state.context.gpr, ARM_THREAD_STATE_COUNT);
+ m_state.SetError(set, Write,
+ kret); // Set the current write error for this register set
+ m_state.InvalidateRegisterSetState(set); // Invalidate the current register
+ // state in case registers are read
+ // back differently
+ return kret; // Return the error code
+}
+
+kern_return_t DNBArchMachARM::SetVFPState() {
+ int set = e_regSetVFP;
+ kern_return_t kret;
+ mach_msg_type_number_t count;
+
+#if defined(__arm64__) || defined(__aarch64__)
+ count = ARM_NEON_STATE_COUNT;
+ kret = ::thread_set_state(m_thread->MachPortNumber(), ARM_NEON_STATE,
+ (thread_state_t)&m_state.context.vfp, count);
+#else
+ count = ARM_VFP_STATE_COUNT;
+ kret = ::thread_set_state(m_thread->MachPortNumber(), ARM_VFP_STATE,
+ (thread_state_t)&m_state.context.vfp, count);
+#endif
+
+#if defined(__arm64__) || defined(__aarch64__)
+ if (DNBLogEnabledForAny(LOG_THREAD)) {
+ DNBLogThreaded(
+ "thread_set_state(0x%4.4x, %u, &vfp, %u) => 0x%8.8x (count = %u) regs"
+ "\n q0 = 0x%16.16llx%16.16llx"
+ "\n q1 = 0x%16.16llx%16.16llx"
+ "\n q2 = 0x%16.16llx%16.16llx"
+ "\n q3 = 0x%16.16llx%16.16llx"
+ "\n q4 = 0x%16.16llx%16.16llx"
+ "\n q5 = 0x%16.16llx%16.16llx"
+ "\n q6 = 0x%16.16llx%16.16llx"
+ "\n q7 = 0x%16.16llx%16.16llx"
+ "\n q8 = 0x%16.16llx%16.16llx"
+ "\n q9 = 0x%16.16llx%16.16llx"
+ "\n q10 = 0x%16.16llx%16.16llx"
+ "\n q11 = 0x%16.16llx%16.16llx"
+ "\n q12 = 0x%16.16llx%16.16llx"
+ "\n q13 = 0x%16.16llx%16.16llx"
+ "\n q14 = 0x%16.16llx%16.16llx"
+ "\n q15 = 0x%16.16llx%16.16llx"
+ "\n fpsr = 0x%8.8x"
+ "\n fpcr = 0x%8.8x\n\n",
+ m_thread->MachPortNumber(), ARM_NEON_STATE, ARM_NEON_STATE_COUNT, kret,
+ count, ((uint64_t *)&m_state.context.vfp.__v[0])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[0])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[1])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[1])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[2])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[2])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[3])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[3])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[4])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[4])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[5])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[5])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[6])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[6])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[7])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[7])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[8])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[8])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[9])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[9])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[10])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[10])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[11])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[11])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[12])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[12])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[13])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[13])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[14])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[14])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[15])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[15])[1],
+ m_state.context.vfp.__fpsr, m_state.context.vfp.__fpcr);
+ }
+#else
+ if (DNBLogEnabledForAny(LOG_THREAD)) {
+ uint32_t *r = &m_state.context.vfp.__r[0];
+ DNBLogThreaded(
+ "thread_get_state(0x%4.4x, %u, &gpr, %u) => 0x%8.8x (count => %u)",
+ m_thread->MachPortNumber(), ARM_THREAD_STATE, ARM_THREAD_STATE_COUNT,
+ kret, count);
+ DNBLogThreaded(" s0=%8.8x s1=%8.8x s2=%8.8x s3=%8.8x s4=%8.8x "
+ "s5=%8.8x s6=%8.8x s7=%8.8x",
+ r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7]);
+ DNBLogThreaded(" s8=%8.8x s9=%8.8x s10=%8.8x s11=%8.8x s12=%8.8x "
+ "s13=%8.8x s14=%8.8x s15=%8.8x",
+ r[8], r[9], r[10], r[11], r[12], r[13], r[14], r[15]);
+ DNBLogThreaded(" s16=%8.8x s17=%8.8x s18=%8.8x s19=%8.8x s20=%8.8x "
+ "s21=%8.8x s22=%8.8x s23=%8.8x",
+ r[16], r[17], r[18], r[19], r[20], r[21], r[22], r[23]);
+ DNBLogThreaded(" s24=%8.8x s25=%8.8x s26=%8.8x s27=%8.8x s28=%8.8x "
+ "s29=%8.8x s30=%8.8x s31=%8.8x",
+ r[24], r[25], r[26], r[27], r[28], r[29], r[30], r[31]);
+ DNBLogThreaded(" s32=%8.8x s33=%8.8x s34=%8.8x s35=%8.8x s36=%8.8x "
+ "s37=%8.8x s38=%8.8x s39=%8.8x",
+ r[32], r[33], r[34], r[35], r[36], r[37], r[38], r[39]);
+ DNBLogThreaded(" s40=%8.8x s41=%8.8x s42=%8.8x s43=%8.8x s44=%8.8x "
+ "s45=%8.8x s46=%8.8x s47=%8.8x",
+ r[40], r[41], r[42], r[43], r[44], r[45], r[46], r[47]);
+ DNBLogThreaded(" s48=%8.8x s49=%8.8x s50=%8.8x s51=%8.8x s52=%8.8x "
+ "s53=%8.8x s54=%8.8x s55=%8.8x",
+ r[48], r[49], r[50], r[51], r[52], r[53], r[54], r[55]);
+ DNBLogThreaded(" s56=%8.8x s57=%8.8x s58=%8.8x s59=%8.8x s60=%8.8x "
+ "s61=%8.8x s62=%8.8x s63=%8.8x fpscr=%8.8x",
+ r[56], r[57], r[58], r[59], r[60], r[61], r[62], r[63],
+ r[64]);
+ }
+#endif
+
+ m_state.SetError(set, Write,
+ kret); // Set the current write error for this register set
+ m_state.InvalidateRegisterSetState(set); // Invalidate the current register
+ // state in case registers are read
+ // back differently
+ return kret; // Return the error code
+}
+
+kern_return_t DNBArchMachARM::SetEXCState() {
+ int set = e_regSetEXC;
+ kern_return_t kret = ::thread_set_state(
+ m_thread->MachPortNumber(), ARM_EXCEPTION_STATE,
+ (thread_state_t)&m_state.context.exc, ARM_EXCEPTION_STATE_COUNT);
+ m_state.SetError(set, Write,
+ kret); // Set the current write error for this register set
+ m_state.InvalidateRegisterSetState(set); // Invalidate the current register
+ // state in case registers are read
+ // back differently
+ return kret; // Return the error code
+}
+
+kern_return_t DNBArchMachARM::SetDBGState(bool also_set_on_task) {
+ int set = e_regSetDBG;
+#if defined(ARM_DEBUG_STATE32) && (defined(__arm64__) || defined(__aarch64__))
+ kern_return_t kret =
+ ::thread_set_state(m_thread->MachPortNumber(), ARM_DEBUG_STATE32,
+ (thread_state_t)&m_state.dbg, ARM_DEBUG_STATE32_COUNT);
+ if (also_set_on_task) {
+ kern_return_t task_kret = ::task_set_state(
+ m_thread->Process()->Task().TaskPort(), ARM_DEBUG_STATE32,
+ (thread_state_t)&m_state.dbg, ARM_DEBUG_STATE32_COUNT);
+ if (task_kret != KERN_SUCCESS)
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::SetDBGState failed to "
+ "set debug control register state: "
+ "0x%8.8x.",
+ kret);
+ }
+#else
+ kern_return_t kret =
+ ::thread_set_state(m_thread->MachPortNumber(), ARM_DEBUG_STATE,
+ (thread_state_t)&m_state.dbg, ARM_DEBUG_STATE_COUNT);
+ if (also_set_on_task) {
+ kern_return_t task_kret = ::task_set_state(
+ m_thread->Process()->Task().TaskPort(), ARM_DEBUG_STATE,
+ (thread_state_t)&m_state.dbg, ARM_DEBUG_STATE_COUNT);
+ if (task_kret != KERN_SUCCESS)
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::SetDBGState failed to "
+ "set debug control register state: "
+ "0x%8.8x.",
+ kret);
+ }
+#endif
+
+ m_state.SetError(set, Write,
+ kret); // Set the current write error for this register set
+ m_state.InvalidateRegisterSetState(set); // Invalidate the current register
+ // state in case registers are read
+ // back differently
+ return kret; // Return the error code
+}
+
+void DNBArchMachARM::ThreadWillResume() {
+ // Do we need to step this thread? If so, let the mach thread tell us so.
+ if (m_thread->IsStepping()) {
+ // This is the primary thread, let the arch do anything it needs
+ if (NumSupportedHardwareBreakpoints() > 0) {
+ if (EnableHardwareSingleStep(true) != KERN_SUCCESS) {
+ DNBLogThreaded("DNBArchMachARM::ThreadWillResume() failed to enable "
+ "hardware single step");
+ }
+ }
+ }
+
+ // Disable the triggered watchpoint temporarily before we resume.
+ // Plus, we try to enable hardware single step to execute past the instruction
+ // which triggered our watchpoint.
+ if (m_watchpoint_did_occur) {
+ if (m_watchpoint_hw_index >= 0) {
+ kern_return_t kret = GetDBGState(false);
+ if (kret == KERN_SUCCESS &&
+ !IsWatchpointEnabled(m_state.dbg, m_watchpoint_hw_index)) {
+ // The watchpoint might have been disabled by the user. We don't need
+ // to do anything at all
+ // to enable hardware single stepping.
+ m_watchpoint_did_occur = false;
+ m_watchpoint_hw_index = -1;
+ return;
+ }
+
+ DisableHardwareWatchpoint(m_watchpoint_hw_index, false);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::ThreadWillResume() "
+ "DisableHardwareWatchpoint(%d) called",
+ m_watchpoint_hw_index);
+
+ // Enable hardware single step to move past the watchpoint-triggering
+ // instruction.
+ m_watchpoint_resume_single_step_enabled =
+ (EnableHardwareSingleStep(true) == KERN_SUCCESS);
+
+ // If we are not able to enable single step to move past the
+ // watchpoint-triggering instruction,
+ // at least we should reset the two watchpoint member variables so that
+ // the next time around
+ // this callback function is invoked, the enclosing logical branch is
+ // skipped.
+ if (!m_watchpoint_resume_single_step_enabled) {
+ // Reset the two watchpoint member variables.
+ m_watchpoint_did_occur = false;
+ m_watchpoint_hw_index = -1;
+ DNBLogThreadedIf(
+ LOG_WATCHPOINTS,
+ "DNBArchMachARM::ThreadWillResume() failed to enable single step");
+ } else
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::ThreadWillResume() "
+ "succeeded to enable single step");
+ }
+ }
+}
+
+bool DNBArchMachARM::ThreadDidStop() {
+ bool success = true;
+
+ m_state.InvalidateRegisterSetState(e_regSetALL);
+
+ if (m_watchpoint_resume_single_step_enabled) {
+ // Great! We now disable the hardware single step as well as re-enable the
+ // hardware watchpoint.
+ // See also ThreadWillResume().
+ if (EnableHardwareSingleStep(false) == KERN_SUCCESS) {
+ if (m_watchpoint_did_occur && m_watchpoint_hw_index >= 0) {
+ ReenableHardwareWatchpoint(m_watchpoint_hw_index);
+ m_watchpoint_resume_single_step_enabled = false;
+ m_watchpoint_did_occur = false;
+ m_watchpoint_hw_index = -1;
+ } else {
+ DNBLogError("internal error detected: m_watchpoint_resume_step_enabled "
+ "is true but (m_watchpoint_did_occur && "
+ "m_watchpoint_hw_index >= 0) does not hold!");
+ }
+ } else {
+ DNBLogError("internal error detected: m_watchpoint_resume_step_enabled "
+ "is true but unable to disable single step!");
+ }
+ }
+
+ // Are we stepping a single instruction?
+ if (GetGPRState(true) == KERN_SUCCESS) {
+ // We are single stepping, was this the primary thread?
+ if (m_thread->IsStepping()) {
+ success = EnableHardwareSingleStep(false) == KERN_SUCCESS;
+ } else {
+ // The MachThread will automatically restore the suspend count
+ // in ThreadDidStop(), so we don't need to do anything here if
+ // we weren't the primary thread the last time
+ }
+ }
+ return success;
+}
+
+bool DNBArchMachARM::NotifyException(MachException::Data &exc) {
+ switch (exc.exc_type) {
+ default:
+ break;
+ case EXC_BREAKPOINT:
+ if (exc.exc_data.size() == 2 && exc.exc_data[0] == EXC_ARM_DA_DEBUG) {
+ // The data break address is passed as exc_data[1].
+ nub_addr_t addr = exc.exc_data[1];
+ // Find the hardware index with the side effect of possibly massaging the
+ // addr to return the starting address as seen from the debugger side.
+ uint32_t hw_index = GetHardwareWatchpointHit(addr);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::NotifyException "
+ "watchpoint %d was hit on address "
+ "0x%llx",
+ hw_index, (uint64_t)addr);
+ const int num_watchpoints = NumSupportedHardwareWatchpoints();
+ for (int i = 0; i < num_watchpoints; i++) {
+ if (LoHi[i] != 0 && LoHi[i] == hw_index && LoHi[i] != i &&
+ GetWatchpointAddressByIndex(i) != INVALID_NUB_ADDRESS) {
+ addr = GetWatchpointAddressByIndex(i);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::NotifyException "
+ "It is a linked watchpoint; "
+ "rewritten to index %d addr 0x%llx",
+ LoHi[i], (uint64_t)addr);
+ }
+ }
+ if (hw_index != INVALID_NUB_HW_INDEX) {
+ m_watchpoint_did_occur = true;
+ m_watchpoint_hw_index = hw_index;
+ exc.exc_data[1] = addr;
+ // Piggyback the hw_index in the exc.data.
+ exc.exc_data.push_back(hw_index);
+ }
+
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+bool DNBArchMachARM::StepNotComplete() {
+ if (m_hw_single_chained_step_addr != INVALID_NUB_ADDRESS) {
+ kern_return_t kret = KERN_INVALID_ARGUMENT;
+ kret = GetGPRState(false);
+ if (kret == KERN_SUCCESS) {
+ if (m_state.context.gpr.__pc == m_hw_single_chained_step_addr) {
+ DNBLogThreadedIf(LOG_STEP, "Need to step some more at 0x%8.8llx",
+ (uint64_t)m_hw_single_chained_step_addr);
+ return true;
+ }
+ }
+ }
+
+ m_hw_single_chained_step_addr = INVALID_NUB_ADDRESS;
+ return false;
+}
+
+// Set the single step bit in the processor status register.
+kern_return_t DNBArchMachARM::EnableHardwareSingleStep(bool enable) {
+ DNBError err;
+ DNBLogThreadedIf(LOG_STEP, "%s( enable = %d )", __FUNCTION__, enable);
+
+ err = GetGPRState(false);
+
+ if (err.Fail()) {
+ err.LogThreaded("%s: failed to read the GPR registers", __FUNCTION__);
+ return err.Status();
+ }
+
+ err = GetDBGState(false);
+
+ if (err.Fail()) {
+ err.LogThreaded("%s: failed to read the DBG registers", __FUNCTION__);
+ return err.Status();
+ }
+
+// The use of __arm64__ here is not ideal. If debugserver is running on
+// an armv8 device, regardless of whether it was built for arch arm or arch
+// arm64,
+// it needs to use the MDSCR_EL1 SS bit to single instruction step.
+
+#if defined(__arm64__) || defined(__aarch64__)
+ if (enable) {
+ DNBLogThreadedIf(LOG_STEP,
+ "%s: Setting MDSCR_EL1 Single Step bit at pc 0x%llx",
+ __FUNCTION__, (uint64_t)m_state.context.gpr.__pc);
+ m_state.dbg.__mdscr_el1 |=
+ 1; // Set bit 0 (single step, SS) in the MDSCR_EL1.
+ } else {
+ DNBLogThreadedIf(LOG_STEP,
+ "%s: Clearing MDSCR_EL1 Single Step bit at pc 0x%llx",
+ __FUNCTION__, (uint64_t)m_state.context.gpr.__pc);
+ m_state.dbg.__mdscr_el1 &=
+ ~(1ULL); // Clear bit 0 (single step, SS) in the MDSCR_EL1.
+ }
+#else
+ const uint32_t i = 0;
+ if (enable) {
+ m_hw_single_chained_step_addr = INVALID_NUB_ADDRESS;
+
+ // Save our previous state
+ m_dbg_save = m_state.dbg;
+ // Set a breakpoint that will stop when the PC doesn't match the current
+ // one!
+ m_state.dbg.__bvr[i] =
+ m_state.context.gpr.__pc &
+ 0xFFFFFFFCu; // Set the current PC as the breakpoint address
+ m_state.dbg.__bcr[i] = BCR_M_IMVA_MISMATCH | // Stop on address mismatch
+ S_USER | // Stop only in user mode
+ BCR_ENABLE; // Enable this breakpoint
+ if (m_state.context.gpr.__cpsr & 0x20) {
+ // Thumb breakpoint
+ if (m_state.context.gpr.__pc & 2)
+ m_state.dbg.__bcr[i] |= BAS_IMVA_2_3;
+ else
+ m_state.dbg.__bcr[i] |= BAS_IMVA_0_1;
+
+ uint16_t opcode;
+ if (sizeof(opcode) ==
+ m_thread->Process()->Task().ReadMemory(m_state.context.gpr.__pc,
+ sizeof(opcode), &opcode)) {
+ if (IsThumb32Opcode(opcode)) {
+ // 32 bit thumb opcode...
+ if (m_state.context.gpr.__pc & 2) {
+ // We can't take care of a 32 bit thumb instruction single step
+ // with just IVA mismatching. We will need to chain an extra
+ // hardware single step in order to complete this single step...
+ m_hw_single_chained_step_addr = m_state.context.gpr.__pc + 2;
+ } else {
+ // Extend the number of bits to ignore for the mismatch
+ m_state.dbg.__bcr[i] |= BAS_IMVA_ALL;
+ }
+ }
+ }
+ } else {
+ // ARM breakpoint
+ m_state.dbg.__bcr[i] |= BAS_IMVA_ALL; // Stop when any address bits change
+ }
+
+ DNBLogThreadedIf(LOG_STEP, "%s: BVR%u=0x%8.8x BCR%u=0x%8.8x", __FUNCTION__,
+ i, m_state.dbg.__bvr[i], i, m_state.dbg.__bcr[i]);
+
+ for (uint32_t j = i + 1; j < 16; ++j) {
+ // Disable all others
+ m_state.dbg.__bvr[j] = 0;
+ m_state.dbg.__bcr[j] = 0;
+ }
+ } else {
+ // Just restore the state we had before we did single stepping
+ m_state.dbg = m_dbg_save;
+ }
+#endif
+
+ return SetDBGState(false);
+}
+
+// return 1 if bit "BIT" is set in "value"
+static inline uint32_t bit(uint32_t value, uint32_t bit) {
+ return (value >> bit) & 1u;
+}
+
+// return the bitfield "value[msbit:lsbit]".
+static inline uint32_t bits(uint32_t value, uint32_t msbit, uint32_t lsbit) {
+ assert(msbit >= lsbit);
+ uint32_t shift_left = sizeof(value) * 8 - 1 - msbit;
+ value <<=
+ shift_left; // shift anything above the msbit off of the unsigned edge
+ value >>= (shift_left + lsbit); // shift it back again down to the lsbit
+ // (including undoing any shift from above)
+ return value; // return our result
+}
+
+bool DNBArchMachARM::ConditionPassed(uint8_t condition, uint32_t cpsr) {
+ uint32_t cpsr_n = bit(cpsr, 31); // Negative condition code flag
+ uint32_t cpsr_z = bit(cpsr, 30); // Zero condition code flag
+ uint32_t cpsr_c = bit(cpsr, 29); // Carry condition code flag
+ uint32_t cpsr_v = bit(cpsr, 28); // Overflow condition code flag
+
+ switch (condition) {
+ case COND_EQ: // (0x0)
+ if (cpsr_z == 1)
+ return true;
+ break;
+ case COND_NE: // (0x1)
+ if (cpsr_z == 0)
+ return true;
+ break;
+ case COND_CS: // (0x2)
+ if (cpsr_c == 1)
+ return true;
+ break;
+ case COND_CC: // (0x3)
+ if (cpsr_c == 0)
+ return true;
+ break;
+ case COND_MI: // (0x4)
+ if (cpsr_n == 1)
+ return true;
+ break;
+ case COND_PL: // (0x5)
+ if (cpsr_n == 0)
+ return true;
+ break;
+ case COND_VS: // (0x6)
+ if (cpsr_v == 1)
+ return true;
+ break;
+ case COND_VC: // (0x7)
+ if (cpsr_v == 0)
+ return true;
+ break;
+ case COND_HI: // (0x8)
+ if ((cpsr_c == 1) && (cpsr_z == 0))
+ return true;
+ break;
+ case COND_LS: // (0x9)
+ if ((cpsr_c == 0) || (cpsr_z == 1))
+ return true;
+ break;
+ case COND_GE: // (0xA)
+ if (cpsr_n == cpsr_v)
+ return true;
+ break;
+ case COND_LT: // (0xB)
+ if (cpsr_n != cpsr_v)
+ return true;
+ break;
+ case COND_GT: // (0xC)
+ if ((cpsr_z == 0) && (cpsr_n == cpsr_v))
+ return true;
+ break;
+ case COND_LE: // (0xD)
+ if ((cpsr_z == 1) || (cpsr_n != cpsr_v))
+ return true;
+ break;
+ default:
+ return true;
+ break;
+ }
+
+ return false;
+}
+
+uint32_t DNBArchMachARM::NumSupportedHardwareBreakpoints() {
+ // Set the init value to something that will let us know that we need to
+ // autodetect how many breakpoints are supported dynamically...
+ static uint32_t g_num_supported_hw_breakpoints = UINT_MAX;
+ if (g_num_supported_hw_breakpoints == UINT_MAX) {
+ // Set this to zero in case we can't tell if there are any HW breakpoints
+ g_num_supported_hw_breakpoints = 0;
+
+ size_t len;
+ uint32_t n = 0;
+ len = sizeof(n);
+ if (::sysctlbyname("hw.optional.breakpoint", &n, &len, NULL, 0) == 0) {
+ g_num_supported_hw_breakpoints = n;
+ DNBLogThreadedIf(LOG_THREAD, "hw.optional.breakpoint=%u", n);
+ } else {
+#if !defined(__arm64__) && !defined(__aarch64__)
+ // Read the DBGDIDR to get the number of available hardware breakpoints
+ // However, in some of our current armv7 processors, hardware
+ // breakpoints/watchpoints were not properly connected. So detect those
+ // cases using a field in a sysctl. For now we are using "hw.cpusubtype"
+ // field to distinguish CPU architectures. This is a hack until we can
+ // get <rdar://problem/6372672> fixed, at which point we will switch to
+ // using a different sysctl string that will tell us how many BRPs
+ // are available to us directly without having to read DBGDIDR.
+ uint32_t register_DBGDIDR;
+
+ asm("mrc p14, 0, %0, c0, c0, 0" : "=r"(register_DBGDIDR));
+ uint32_t numBRPs = bits(register_DBGDIDR, 27, 24);
+ // Zero is reserved for the BRP count, so don't increment it if it is zero
+ if (numBRPs > 0)
+ numBRPs++;
+ DNBLogThreadedIf(LOG_THREAD, "DBGDIDR=0x%8.8x (number BRP pairs = %u)",
+ register_DBGDIDR, numBRPs);
+
+ if (numBRPs > 0) {
+ uint32_t cpusubtype;
+ len = sizeof(cpusubtype);
+ // TODO: remove this hack and change to using hw.optional.xx when
+ // implmented
+ if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0) {
+ DNBLogThreadedIf(LOG_THREAD, "hw.cpusubtype=%d", cpusubtype);
+ if (cpusubtype == CPU_SUBTYPE_ARM_V7)
+ DNBLogThreadedIf(LOG_THREAD, "Hardware breakpoints disabled for "
+ "armv7 (rdar://problem/6372672)");
+ else
+ g_num_supported_hw_breakpoints = numBRPs;
+ }
+ }
+#endif
+ }
+ }
+ return g_num_supported_hw_breakpoints;
+}
+
+uint32_t DNBArchMachARM::NumSupportedHardwareWatchpoints() {
+ // Set the init value to something that will let us know that we need to
+ // autodetect how many watchpoints are supported dynamically...
+ static uint32_t g_num_supported_hw_watchpoints = UINT_MAX;
+ if (g_num_supported_hw_watchpoints == UINT_MAX) {
+ // Set this to zero in case we can't tell if there are any HW breakpoints
+ g_num_supported_hw_watchpoints = 0;
+
+ size_t len;
+ uint32_t n = 0;
+ len = sizeof(n);
+ if (::sysctlbyname("hw.optional.watchpoint", &n, &len, NULL, 0) == 0) {
+ g_num_supported_hw_watchpoints = n;
+ DNBLogThreadedIf(LOG_THREAD, "hw.optional.watchpoint=%u", n);
+ } else {
+#if !defined(__arm64__) && !defined(__aarch64__)
+ // Read the DBGDIDR to get the number of available hardware breakpoints
+ // However, in some of our current armv7 processors, hardware
+ // breakpoints/watchpoints were not properly connected. So detect those
+ // cases using a field in a sysctl. For now we are using "hw.cpusubtype"
+ // field to distinguish CPU architectures. This is a hack until we can
+ // get <rdar://problem/6372672> fixed, at which point we will switch to
+ // using a different sysctl string that will tell us how many WRPs
+ // are available to us directly without having to read DBGDIDR.
+
+ uint32_t register_DBGDIDR;
+ asm("mrc p14, 0, %0, c0, c0, 0" : "=r"(register_DBGDIDR));
+ uint32_t numWRPs = bits(register_DBGDIDR, 31, 28) + 1;
+ DNBLogThreadedIf(LOG_THREAD, "DBGDIDR=0x%8.8x (number WRP pairs = %u)",
+ register_DBGDIDR, numWRPs);
+
+ if (numWRPs > 0) {
+ uint32_t cpusubtype;
+ size_t len;
+ len = sizeof(cpusubtype);
+ // TODO: remove this hack and change to using hw.optional.xx when
+ // implmented
+ if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0) {
+ DNBLogThreadedIf(LOG_THREAD, "hw.cpusubtype=0x%d", cpusubtype);
+
+ if (cpusubtype == CPU_SUBTYPE_ARM_V7)
+ DNBLogThreadedIf(LOG_THREAD, "Hardware watchpoints disabled for "
+ "armv7 (rdar://problem/6372672)");
+ else
+ g_num_supported_hw_watchpoints = numWRPs;
+ }
+ }
+#endif
+ }
+ }
+ return g_num_supported_hw_watchpoints;
+}
+
+uint32_t DNBArchMachARM::EnableHardwareBreakpoint(nub_addr_t addr,
+ nub_size_t size) {
+ // Make sure our address isn't bogus
+ if (addr & 1)
+ return INVALID_NUB_HW_INDEX;
+
+ kern_return_t kret = GetDBGState(false);
+
+ if (kret == KERN_SUCCESS) {
+ const uint32_t num_hw_breakpoints = NumSupportedHardwareBreakpoints();
+ uint32_t i;
+ for (i = 0; i < num_hw_breakpoints; ++i) {
+ if ((m_state.dbg.__bcr[i] & BCR_ENABLE) == 0)
+ break; // We found an available hw breakpoint slot (in i)
+ }
+
+ // See if we found an available hw breakpoint slot above
+ if (i < num_hw_breakpoints) {
+ // Make sure bits 1:0 are clear in our address
+ m_state.dbg.__bvr[i] = addr & ~((nub_addr_t)3);
+
+ if (size == 2 || addr & 2) {
+ uint32_t byte_addr_select = (addr & 2) ? BAS_IMVA_2_3 : BAS_IMVA_0_1;
+
+ // We have a thumb breakpoint
+ // We have an ARM breakpoint
+ m_state.dbg.__bcr[i] =
+ BCR_M_IMVA_MATCH | // Stop on address mismatch
+ byte_addr_select | // Set the correct byte address select so we only
+ // trigger on the correct opcode
+ S_USER | // Which modes should this breakpoint stop in?
+ BCR_ENABLE; // Enable this hardware breakpoint
+ DNBLogThreadedIf(LOG_BREAKPOINTS,
+ "DNBArchMachARM::EnableHardwareBreakpoint( addr = "
+ "0x%8.8llx, size = %llu ) - BVR%u/BCR%u = 0x%8.8x / "
+ "0x%8.8x (Thumb)",
+ (uint64_t)addr, (uint64_t)size, i, i,
+ m_state.dbg.__bvr[i], m_state.dbg.__bcr[i]);
+ } else if (size == 4) {
+ // We have an ARM breakpoint
+ m_state.dbg.__bcr[i] =
+ BCR_M_IMVA_MATCH | // Stop on address mismatch
+ BAS_IMVA_ALL | // Stop on any of the four bytes following the IMVA
+ S_USER | // Which modes should this breakpoint stop in?
+ BCR_ENABLE; // Enable this hardware breakpoint
+ DNBLogThreadedIf(LOG_BREAKPOINTS,
+ "DNBArchMachARM::EnableHardwareBreakpoint( addr = "
+ "0x%8.8llx, size = %llu ) - BVR%u/BCR%u = 0x%8.8x / "
+ "0x%8.8x (ARM)",
+ (uint64_t)addr, (uint64_t)size, i, i,
+ m_state.dbg.__bvr[i], m_state.dbg.__bcr[i]);
+ }
+
+ kret = SetDBGState(false);
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "DNBArchMachARM::"
+ "EnableHardwareBreakpoint() "
+ "SetDBGState() => 0x%8.8x.",
+ kret);
+
+ if (kret == KERN_SUCCESS)
+ return i;
+ } else {
+ DNBLogThreadedIf(LOG_BREAKPOINTS,
+ "DNBArchMachARM::EnableHardwareBreakpoint(addr = "
+ "0x%8.8llx, size = %llu) => all hardware breakpoint "
+ "resources are being used.",
+ (uint64_t)addr, (uint64_t)size);
+ }
+ }
+
+ return INVALID_NUB_HW_INDEX;
+}
+
+bool DNBArchMachARM::DisableHardwareBreakpoint(uint32_t hw_index) {
+ kern_return_t kret = GetDBGState(false);
+
+ const uint32_t num_hw_points = NumSupportedHardwareBreakpoints();
+ if (kret == KERN_SUCCESS) {
+ if (hw_index < num_hw_points) {
+ m_state.dbg.__bcr[hw_index] = 0;
+ DNBLogThreadedIf(LOG_BREAKPOINTS, "DNBArchMachARM::SetHardwareBreakpoint("
+ " %u ) - BVR%u = 0x%8.8x BCR%u = "
+ "0x%8.8x",
+ hw_index, hw_index, m_state.dbg.__bvr[hw_index],
+ hw_index, m_state.dbg.__bcr[hw_index]);
+
+ kret = SetDBGState(false);
+
+ if (kret == KERN_SUCCESS)
+ return true;
+ }
+ }
+ return false;
+}
+
+// ARM v7 watchpoints may be either word-size or double-word-size.
+// It's implementation defined which they can handle. It looks like on an
+// armv8 device, armv7 processes can watch dwords. But on a genuine armv7
+// device I tried, only word watchpoints are supported.
+
+#if defined(__arm64__) || defined(__aarch64__)
+#define WATCHPOINTS_ARE_DWORD 1
+#else
+#undef WATCHPOINTS_ARE_DWORD
+#endif
+
+uint32_t DNBArchMachARM::EnableHardwareWatchpoint(nub_addr_t addr,
+ nub_size_t size, bool read,
+ bool write,
+ bool also_set_on_task) {
+
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::EnableHardwareWatchpoint("
+ "addr = 0x%8.8llx, size = %zu, read = %u, "
+ "write = %u)",
+ (uint64_t)addr, size, read, write);
+
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+
+ // Can't watch zero bytes
+ if (size == 0)
+ return INVALID_NUB_HW_INDEX;
+
+ // We must watch for either read or write
+ if (read == false && write == false)
+ return INVALID_NUB_HW_INDEX;
+
+ // Otherwise, can't watch more than 8 bytes per WVR/WCR pair
+ if (size > 8)
+ return INVALID_NUB_HW_INDEX;
+
+// Treat arm watchpoints as having an 8-byte alignment requirement. You can put
+// a watchpoint on a 4-byte
+// offset address but you can only watch 4 bytes with that watchpoint.
+
+// arm watchpoints on an 8-byte (double word) aligned addr can watch any bytes
+// in that
+// 8-byte long region of memory. They can watch the 1st byte, the 2nd byte, 3rd
+// byte, etc, or any
+// combination therein by setting the bits in the BAS [12:5] (Byte Address
+// Select) field of
+// the DBGWCRn_EL1 reg for the watchpoint.
+
+// If the MASK [28:24] bits in the DBGWCRn_EL1 allow a single watchpoint to
+// monitor a larger region
+// of memory (16 bytes, 32 bytes, or 2GB) but the Byte Address Select bitfield
+// then selects a larger
+// range of bytes, instead of individual bytes. See the ARMv8 Debug
+// Architecture manual for details.
+// This implementation does not currently use the MASK bits; the largest single
+// region watched by a single
+// watchpoint right now is 8-bytes.
+
+#if defined(WATCHPOINTS_ARE_DWORD)
+ nub_addr_t aligned_wp_address = addr & ~0x7;
+ uint32_t addr_dword_offset = addr & 0x7;
+ const int max_watchpoint_size = 8;
+#else
+ nub_addr_t aligned_wp_address = addr & ~0x3;
+ uint32_t addr_dword_offset = addr & 0x3;
+ const int max_watchpoint_size = 4;
+#endif
+
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::EnableHardwareWatchpoint "
+ "aligned_wp_address is 0x%llx and "
+ "addr_dword_offset is 0x%x",
+ (uint64_t)aligned_wp_address, addr_dword_offset);
+
+ // Do we need to split up this logical watchpoint into two hardware watchpoint
+ // registers?
+ // e.g. a watchpoint of length 4 on address 6. We need do this with
+ // one watchpoint on address 0 with bytes 6 & 7 being monitored
+ // one watchpoint on address 8 with bytes 0, 1, 2, 3 being monitored
+
+ if (addr_dword_offset + size > max_watchpoint_size) {
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::"
+ "EnableHardwareWatchpoint(addr = "
+ "0x%8.8llx, size = %zu) needs two "
+ "hardware watchpoints slots to monitor",
+ (uint64_t)addr, size);
+ int low_watchpoint_size = max_watchpoint_size - addr_dword_offset;
+ int high_watchpoint_size = addr_dword_offset + size - max_watchpoint_size;
+
+ uint32_t lo = EnableHardwareWatchpoint(addr, low_watchpoint_size, read,
+ write, also_set_on_task);
+ if (lo == INVALID_NUB_HW_INDEX)
+ return INVALID_NUB_HW_INDEX;
+ uint32_t hi = EnableHardwareWatchpoint(
+ aligned_wp_address + max_watchpoint_size, high_watchpoint_size, read,
+ write, also_set_on_task);
+ if (hi == INVALID_NUB_HW_INDEX) {
+ DisableHardwareWatchpoint(lo, also_set_on_task);
+ return INVALID_NUB_HW_INDEX;
+ }
+ // Tag this lo->hi mapping in our database.
+ LoHi[lo] = hi;
+ return lo;
+ }
+
+ // At this point
+ // 1 aligned_wp_address is the requested address rounded down to 8-byte
+ // alignment
+ // 2 addr_dword_offset is the offset into that double word (8-byte) region
+ // that we are watching
+ // 3 size is the number of bytes within that 8-byte region that we are
+ // watching
+
+ // Set the Byte Address Selects bits DBGWCRn_EL1 bits [12:5] based on the
+ // above.
+ // The bit shift and negation operation will give us 0b11 for 2, 0b1111 for 4,
+ // etc, up to 0b11111111 for 8.
+ // then we shift those bits left by the offset into this dword that we are
+ // interested in.
+ // e.g. if we are watching bytes 4,5,6,7 in a dword we want a BAS of
+ // 0b11110000.
+ uint32_t byte_address_select = ((1 << size) - 1) << addr_dword_offset;
+
+ // Read the debug state
+ kern_return_t kret = GetDBGState(true);
+
+ if (kret == KERN_SUCCESS) {
+ // Check to make sure we have the needed hardware support
+ uint32_t i = 0;
+
+ for (i = 0; i < num_hw_watchpoints; ++i) {
+ if ((m_state.dbg.__wcr[i] & WCR_ENABLE) == 0)
+ break; // We found an available hw watchpoint slot (in i)
+ }
+
+ // See if we found an available hw watchpoint slot above
+ if (i < num_hw_watchpoints) {
+ // DumpDBGState(m_state.dbg);
+
+ // Clear any previous LoHi joined-watchpoint that may have been in use
+ LoHi[i] = 0;
+
+ // shift our Byte Address Select bits up to the correct bit range for the
+ // DBGWCRn_EL1
+ byte_address_select = byte_address_select << 5;
+
+ // Make sure bits 1:0 are clear in our address
+ m_state.dbg.__wvr[i] = aligned_wp_address; // DVA (Data Virtual Address)
+ m_state.dbg.__wcr[i] = byte_address_select | // Which bytes that follow
+ // the DVA that we will watch
+ S_USER | // Stop only in user mode
+ (read ? WCR_LOAD : 0) | // Stop on read access?
+ (write ? WCR_STORE : 0) | // Stop on write access?
+ WCR_ENABLE; // Enable this watchpoint;
+
+ DNBLogThreadedIf(
+ LOG_WATCHPOINTS, "DNBArchMachARM::EnableHardwareWatchpoint() adding "
+ "watchpoint on address 0x%llx with control register "
+ "value 0x%x",
+ (uint64_t)m_state.dbg.__wvr[i], (uint32_t)m_state.dbg.__wcr[i]);
+
+ // The kernel will set the MDE_ENABLE bit in the MDSCR_EL1 for us
+ // automatically, don't need to do it here.
+
+ kret = SetDBGState(also_set_on_task);
+ // DumpDBGState(m_state.dbg);
+
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::"
+ "EnableHardwareWatchpoint() "
+ "SetDBGState() => 0x%8.8x.",
+ kret);
+
+ if (kret == KERN_SUCCESS)
+ return i;
+ } else {
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::"
+ "EnableHardwareWatchpoint(): All "
+ "hardware resources (%u) are in use.",
+ num_hw_watchpoints);
+ }
+ }
+ return INVALID_NUB_HW_INDEX;
+}
+
+bool DNBArchMachARM::ReenableHardwareWatchpoint(uint32_t hw_index) {
+ // If this logical watchpoint # is actually implemented using
+ // two hardware watchpoint registers, re-enable both of them.
+
+ if (hw_index < NumSupportedHardwareWatchpoints() && LoHi[hw_index]) {
+ return ReenableHardwareWatchpoint_helper(hw_index) &&
+ ReenableHardwareWatchpoint_helper(LoHi[hw_index]);
+ } else {
+ return ReenableHardwareWatchpoint_helper(hw_index);
+ }
+}
+
+bool DNBArchMachARM::ReenableHardwareWatchpoint_helper(uint32_t hw_index) {
+ kern_return_t kret = GetDBGState(false);
+ if (kret != KERN_SUCCESS)
+ return false;
+ const uint32_t num_hw_points = NumSupportedHardwareWatchpoints();
+ if (hw_index >= num_hw_points)
+ return false;
+
+ m_state.dbg.__wvr[hw_index] = m_disabled_watchpoints[hw_index].addr;
+ m_state.dbg.__wcr[hw_index] = m_disabled_watchpoints[hw_index].control;
+
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::EnableHardwareWatchpoint( "
+ "%u ) - WVR%u = 0x%8.8llx WCR%u = "
+ "0x%8.8llx",
+ hw_index, hw_index, (uint64_t)m_state.dbg.__wvr[hw_index],
+ hw_index, (uint64_t)m_state.dbg.__wcr[hw_index]);
+
+ // The kernel will set the MDE_ENABLE bit in the MDSCR_EL1 for us
+ // automatically, don't need to do it here.
+
+ kret = SetDBGState(false);
+
+ return (kret == KERN_SUCCESS);
+}
+
+bool DNBArchMachARM::DisableHardwareWatchpoint(uint32_t hw_index,
+ bool also_set_on_task) {
+ if (hw_index < NumSupportedHardwareWatchpoints() && LoHi[hw_index]) {
+ return DisableHardwareWatchpoint_helper(hw_index, also_set_on_task) &&
+ DisableHardwareWatchpoint_helper(LoHi[hw_index], also_set_on_task);
+ } else {
+ return DisableHardwareWatchpoint_helper(hw_index, also_set_on_task);
+ }
+}
+
+bool DNBArchMachARM::DisableHardwareWatchpoint_helper(uint32_t hw_index,
+ bool also_set_on_task) {
+ kern_return_t kret = GetDBGState(false);
+ if (kret != KERN_SUCCESS)
+ return false;
+
+ const uint32_t num_hw_points = NumSupportedHardwareWatchpoints();
+ if (hw_index >= num_hw_points)
+ return false;
+
+ m_disabled_watchpoints[hw_index].addr = m_state.dbg.__wvr[hw_index];
+ m_disabled_watchpoints[hw_index].control = m_state.dbg.__wcr[hw_index];
+
+ m_state.dbg.__wvr[hw_index] = 0;
+ m_state.dbg.__wcr[hw_index] = 0;
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::DisableHardwareWatchpoint("
+ " %u ) - WVR%u = 0x%8.8llx WCR%u = "
+ "0x%8.8llx",
+ hw_index, hw_index, (uint64_t)m_state.dbg.__wvr[hw_index],
+ hw_index, (uint64_t)m_state.dbg.__wcr[hw_index]);
+
+ kret = SetDBGState(also_set_on_task);
+
+ return (kret == KERN_SUCCESS);
+}
+
+// Returns -1 if the trailing bit patterns are not one of:
+// { 0b???1, 0b??10, 0b?100, 0b1000 }.
+static inline int32_t LowestBitSet(uint32_t val) {
+ for (unsigned i = 0; i < 4; ++i) {
+ if (bit(val, i))
+ return i;
+ }
+ return -1;
+}
+
+// Iterate through the debug registers; return the index of the first watchpoint
+// whose address matches.
+// As a side effect, the starting address as understood by the debugger is
+// returned which could be
+// different from 'addr' passed as an in/out argument.
+uint32_t DNBArchMachARM::GetHardwareWatchpointHit(nub_addr_t &addr) {
+ // Read the debug state
+ kern_return_t kret = GetDBGState(true);
+ // DumpDBGState(m_state.dbg);
+ DNBLogThreadedIf(
+ LOG_WATCHPOINTS,
+ "DNBArchMachARM::GetHardwareWatchpointHit() GetDBGState() => 0x%8.8x.",
+ kret);
+ DNBLogThreadedIf(LOG_WATCHPOINTS,
+ "DNBArchMachARM::GetHardwareWatchpointHit() addr = 0x%llx",
+ (uint64_t)addr);
+
+// This is the watchpoint value to match against, i.e., word address.
+#if defined(WATCHPOINTS_ARE_DWORD)
+ nub_addr_t wp_val = addr & ~((nub_addr_t)7);
+#else
+ nub_addr_t wp_val = addr & ~((nub_addr_t)3);
+#endif
+ if (kret == KERN_SUCCESS) {
+ DBG &debug_state = m_state.dbg;
+ uint32_t i, num = NumSupportedHardwareWatchpoints();
+ for (i = 0; i < num; ++i) {
+ nub_addr_t wp_addr = GetWatchAddress(debug_state, i);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::"
+ "GetHardwareWatchpointHit() slot: %u "
+ "(addr = 0x%llx).",
+ i, (uint64_t)wp_addr);
+ if (wp_val == wp_addr) {
+#if defined(WATCHPOINTS_ARE_DWORD)
+ uint32_t byte_mask = bits(debug_state.__wcr[i], 12, 5);
+#else
+ uint32_t byte_mask = bits(debug_state.__wcr[i], 8, 5);
+#endif
+
+ // Sanity check the byte_mask, first.
+ if (LowestBitSet(byte_mask) < 0)
+ continue;
+
+ // Compute the starting address (from the point of view of the
+ // debugger).
+ addr = wp_addr + LowestBitSet(byte_mask);
+ return i;
+ }
+ }
+ }
+ return INVALID_NUB_HW_INDEX;
+}
+
+nub_addr_t DNBArchMachARM::GetWatchpointAddressByIndex(uint32_t hw_index) {
+ kern_return_t kret = GetDBGState(true);
+ if (kret != KERN_SUCCESS)
+ return INVALID_NUB_ADDRESS;
+ const uint32_t num = NumSupportedHardwareWatchpoints();
+ if (hw_index >= num)
+ return INVALID_NUB_ADDRESS;
+ if (IsWatchpointEnabled(m_state.dbg, hw_index))
+ return GetWatchAddress(m_state.dbg, hw_index);
+ return INVALID_NUB_ADDRESS;
+}
+
+bool DNBArchMachARM::IsWatchpointEnabled(const DBG &debug_state,
+ uint32_t hw_index) {
+ // Watchpoint Control Registers, bitfield definitions
+ // ...
+ // Bits Value Description
+ // [0] 0 Watchpoint disabled
+ // 1 Watchpoint enabled.
+ return (debug_state.__wcr[hw_index] & 1u);
+}
+
+nub_addr_t DNBArchMachARM::GetWatchAddress(const DBG &debug_state,
+ uint32_t hw_index) {
+ // Watchpoint Value Registers, bitfield definitions
+ // Bits Description
+ // [31:2] Watchpoint value (word address, i.e., 4-byte aligned)
+ // [1:0] RAZ/SBZP
+ return bits(debug_state.__wvr[hw_index], 31, 0);
+}
+
+// Register information definitions for 32 bit ARMV7.
+enum gpr_regnums {
+ gpr_r0 = 0,
+ gpr_r1,
+ gpr_r2,
+ gpr_r3,
+ gpr_r4,
+ gpr_r5,
+ gpr_r6,
+ gpr_r7,
+ gpr_r8,
+ gpr_r9,
+ gpr_r10,
+ gpr_r11,
+ gpr_r12,
+ gpr_sp,
+ gpr_lr,
+ gpr_pc,
+ gpr_cpsr
+};
+
+enum {
+ vfp_s0 = 0,
+ vfp_s1,
+ vfp_s2,
+ vfp_s3,
+ vfp_s4,
+ vfp_s5,
+ vfp_s6,
+ vfp_s7,
+ vfp_s8,
+ vfp_s9,
+ vfp_s10,
+ vfp_s11,
+ vfp_s12,
+ vfp_s13,
+ vfp_s14,
+ vfp_s15,
+ vfp_s16,
+ vfp_s17,
+ vfp_s18,
+ vfp_s19,
+ vfp_s20,
+ vfp_s21,
+ vfp_s22,
+ vfp_s23,
+ vfp_s24,
+ vfp_s25,
+ vfp_s26,
+ vfp_s27,
+ vfp_s28,
+ vfp_s29,
+ vfp_s30,
+ vfp_s31,
+ vfp_d0,
+ vfp_d1,
+ vfp_d2,
+ vfp_d3,
+ vfp_d4,
+ vfp_d5,
+ vfp_d6,
+ vfp_d7,
+ vfp_d8,
+ vfp_d9,
+ vfp_d10,
+ vfp_d11,
+ vfp_d12,
+ vfp_d13,
+ vfp_d14,
+ vfp_d15,
+ vfp_d16,
+ vfp_d17,
+ vfp_d18,
+ vfp_d19,
+ vfp_d20,
+ vfp_d21,
+ vfp_d22,
+ vfp_d23,
+ vfp_d24,
+ vfp_d25,
+ vfp_d26,
+ vfp_d27,
+ vfp_d28,
+ vfp_d29,
+ vfp_d30,
+ vfp_d31,
+ vfp_q0,
+ vfp_q1,
+ vfp_q2,
+ vfp_q3,
+ vfp_q4,
+ vfp_q5,
+ vfp_q6,
+ vfp_q7,
+ vfp_q8,
+ vfp_q9,
+ vfp_q10,
+ vfp_q11,
+ vfp_q12,
+ vfp_q13,
+ vfp_q14,
+ vfp_q15,
+#if defined(__arm64__) || defined(__aarch64__)
+ vfp_fpsr,
+ vfp_fpcr,
+#else
+ vfp_fpscr
+#endif
+};
+
+enum {
+ exc_exception,
+ exc_fsr,
+ exc_far,
+};
+
+#define GPR_OFFSET_IDX(idx) (offsetof(DNBArchMachARM::GPR, __r[idx]))
+#define GPR_OFFSET_NAME(reg) (offsetof(DNBArchMachARM::GPR, __##reg))
+
+#define EXC_OFFSET(reg) \
+ (offsetof(DNBArchMachARM::EXC, __##reg) + \
+ offsetof(DNBArchMachARM::Context, exc))
+
+// These macros will auto define the register name, alt name, register size,
+// register offset, encoding, format and native register. This ensures that
+// the register state structures are defined correctly and have the correct
+// sizes and offsets.
+#define DEFINE_GPR_IDX(idx, reg, alt, gen) \
+ { \
+ e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, 4, GPR_OFFSET_IDX(idx), \
+ ehframe_##reg, dwarf_##reg, gen, INVALID_NUB_REGNUM, NULL, NULL \
+ }
+#define DEFINE_GPR_NAME(reg, alt, gen, inval) \
+ { \
+ e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, 4, GPR_OFFSET_NAME(reg), \
+ ehframe_##reg, dwarf_##reg, gen, INVALID_NUB_REGNUM, NULL, inval \
+ }
+
+// In case we are debugging to a debug target that the ability to
+// change into the protected modes with folded registers (ABT, IRQ,
+// FIQ, SYS, USR, etc..), we should invalidate r8-r14 if the CPSR
+// gets modified.
+
+const char *g_invalidate_cpsr[] = {"r8", "r9", "r10", "r11",
+ "r12", "sp", "lr", NULL};
+
+// General purpose registers
+const DNBRegisterInfo DNBArchMachARM::g_gpr_registers[] = {
+ DEFINE_GPR_IDX(0, r0, "arg1", GENERIC_REGNUM_ARG1),
+ DEFINE_GPR_IDX(1, r1, "arg2", GENERIC_REGNUM_ARG2),
+ DEFINE_GPR_IDX(2, r2, "arg3", GENERIC_REGNUM_ARG3),
+ DEFINE_GPR_IDX(3, r3, "arg4", GENERIC_REGNUM_ARG4),
+ DEFINE_GPR_IDX(4, r4, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(5, r5, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(6, r6, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(7, r7, "fp", GENERIC_REGNUM_FP),
+ DEFINE_GPR_IDX(8, r8, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(9, r9, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(10, r10, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(11, r11, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(12, r12, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_NAME(sp, "r13", GENERIC_REGNUM_SP, NULL),
+ DEFINE_GPR_NAME(lr, "r14", GENERIC_REGNUM_RA, NULL),
+ DEFINE_GPR_NAME(pc, "r15", GENERIC_REGNUM_PC, NULL),
+ DEFINE_GPR_NAME(cpsr, "flags", GENERIC_REGNUM_FLAGS, g_invalidate_cpsr)};
+
+const char *g_contained_q0[]{"q0", NULL};
+const char *g_contained_q1[]{"q1", NULL};
+const char *g_contained_q2[]{"q2", NULL};
+const char *g_contained_q3[]{"q3", NULL};
+const char *g_contained_q4[]{"q4", NULL};
+const char *g_contained_q5[]{"q5", NULL};
+const char *g_contained_q6[]{"q6", NULL};
+const char *g_contained_q7[]{"q7", NULL};
+const char *g_contained_q8[]{"q8", NULL};
+const char *g_contained_q9[]{"q9", NULL};
+const char *g_contained_q10[]{"q10", NULL};
+const char *g_contained_q11[]{"q11", NULL};
+const char *g_contained_q12[]{"q12", NULL};
+const char *g_contained_q13[]{"q13", NULL};
+const char *g_contained_q14[]{"q14", NULL};
+const char *g_contained_q15[]{"q15", NULL};
+
+const char *g_invalidate_q0[]{"q0", "d0", "d1", "s0", "s1", "s2", "s3", NULL};
+const char *g_invalidate_q1[]{"q1", "d2", "d3", "s4", "s5", "s6", "s7", NULL};
+const char *g_invalidate_q2[]{"q2", "d4", "d5", "s8", "s9", "s10", "s11", NULL};
+const char *g_invalidate_q3[]{"q3", "d6", "d7", "s12",
+ "s13", "s14", "s15", NULL};
+const char *g_invalidate_q4[]{"q4", "d8", "d9", "s16",
+ "s17", "s18", "s19", NULL};
+const char *g_invalidate_q5[]{"q5", "d10", "d11", "s20",
+ "s21", "s22", "s23", NULL};
+const char *g_invalidate_q6[]{"q6", "d12", "d13", "s24",
+ "s25", "s26", "s27", NULL};
+const char *g_invalidate_q7[]{"q7", "d14", "d15", "s28",
+ "s29", "s30", "s31", NULL};
+const char *g_invalidate_q8[]{"q8", "d16", "d17", NULL};
+const char *g_invalidate_q9[]{"q9", "d18", "d19", NULL};
+const char *g_invalidate_q10[]{"q10", "d20", "d21", NULL};
+const char *g_invalidate_q11[]{"q11", "d22", "d23", NULL};
+const char *g_invalidate_q12[]{"q12", "d24", "d25", NULL};
+const char *g_invalidate_q13[]{"q13", "d26", "d27", NULL};
+const char *g_invalidate_q14[]{"q14", "d28", "d29", NULL};
+const char *g_invalidate_q15[]{"q15", "d30", "d31", NULL};
+
+#define VFP_S_OFFSET_IDX(idx) \
+ (((idx) % 4) * 4) // offset into q reg: 0, 4, 8, 12
+#define VFP_D_OFFSET_IDX(idx) (((idx) % 2) * 8) // offset into q reg: 0, 8
+#define VFP_Q_OFFSET_IDX(idx) (VFP_S_OFFSET_IDX((idx)*4))
+
+#define VFP_OFFSET_NAME(reg) \
+ (offsetof(DNBArchMachARM::FPU, __##reg) + \
+ offsetof(DNBArchMachARM::Context, vfp))
+
+#define FLOAT_FORMAT Float
+
+#define DEFINE_VFP_S_IDX(idx) \
+ e_regSetVFP, vfp_s##idx, "s" #idx, NULL, IEEE754, FLOAT_FORMAT, 4, \
+ VFP_S_OFFSET_IDX(idx), INVALID_NUB_REGNUM, dwarf_s##idx, \
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM
+#define DEFINE_VFP_D_IDX(idx) \
+ e_regSetVFP, vfp_d##idx, "d" #idx, NULL, IEEE754, FLOAT_FORMAT, 8, \
+ VFP_D_OFFSET_IDX(idx), INVALID_NUB_REGNUM, dwarf_d##idx, \
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM
+#define DEFINE_VFP_Q_IDX(idx) \
+ e_regSetVFP, vfp_q##idx, "q" #idx, NULL, Vector, VectorOfUInt8, 16, \
+ VFP_Q_OFFSET_IDX(idx), INVALID_NUB_REGNUM, dwarf_q##idx, \
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM
+
+// Floating point registers
+const DNBRegisterInfo DNBArchMachARM::g_vfp_registers[] = {
+ {DEFINE_VFP_S_IDX(0), g_contained_q0, g_invalidate_q0},
+ {DEFINE_VFP_S_IDX(1), g_contained_q0, g_invalidate_q0},
+ {DEFINE_VFP_S_IDX(2), g_contained_q0, g_invalidate_q0},
+ {DEFINE_VFP_S_IDX(3), g_contained_q0, g_invalidate_q0},
+ {DEFINE_VFP_S_IDX(4), g_contained_q1, g_invalidate_q1},
+ {DEFINE_VFP_S_IDX(5), g_contained_q1, g_invalidate_q1},
+ {DEFINE_VFP_S_IDX(6), g_contained_q1, g_invalidate_q1},
+ {DEFINE_VFP_S_IDX(7), g_contained_q1, g_invalidate_q1},
+ {DEFINE_VFP_S_IDX(8), g_contained_q2, g_invalidate_q2},
+ {DEFINE_VFP_S_IDX(9), g_contained_q2, g_invalidate_q2},
+ {DEFINE_VFP_S_IDX(10), g_contained_q2, g_invalidate_q2},
+ {DEFINE_VFP_S_IDX(11), g_contained_q2, g_invalidate_q2},
+ {DEFINE_VFP_S_IDX(12), g_contained_q3, g_invalidate_q3},
+ {DEFINE_VFP_S_IDX(13), g_contained_q3, g_invalidate_q3},
+ {DEFINE_VFP_S_IDX(14), g_contained_q3, g_invalidate_q3},
+ {DEFINE_VFP_S_IDX(15), g_contained_q3, g_invalidate_q3},
+ {DEFINE_VFP_S_IDX(16), g_contained_q4, g_invalidate_q4},
+ {DEFINE_VFP_S_IDX(17), g_contained_q4, g_invalidate_q4},
+ {DEFINE_VFP_S_IDX(18), g_contained_q4, g_invalidate_q4},
+ {DEFINE_VFP_S_IDX(19), g_contained_q4, g_invalidate_q4},
+ {DEFINE_VFP_S_IDX(20), g_contained_q5, g_invalidate_q5},
+ {DEFINE_VFP_S_IDX(21), g_contained_q5, g_invalidate_q5},
+ {DEFINE_VFP_S_IDX(22), g_contained_q5, g_invalidate_q5},
+ {DEFINE_VFP_S_IDX(23), g_contained_q5, g_invalidate_q5},
+ {DEFINE_VFP_S_IDX(24), g_contained_q6, g_invalidate_q6},
+ {DEFINE_VFP_S_IDX(25), g_contained_q6, g_invalidate_q6},
+ {DEFINE_VFP_S_IDX(26), g_contained_q6, g_invalidate_q6},
+ {DEFINE_VFP_S_IDX(27), g_contained_q6, g_invalidate_q6},
+ {DEFINE_VFP_S_IDX(28), g_contained_q7, g_invalidate_q7},
+ {DEFINE_VFP_S_IDX(29), g_contained_q7, g_invalidate_q7},
+ {DEFINE_VFP_S_IDX(30), g_contained_q7, g_invalidate_q7},
+ {DEFINE_VFP_S_IDX(31), g_contained_q7, g_invalidate_q7},
+
+ {DEFINE_VFP_D_IDX(0), g_contained_q0, g_invalidate_q0},
+ {DEFINE_VFP_D_IDX(1), g_contained_q0, g_invalidate_q0},
+ {DEFINE_VFP_D_IDX(2), g_contained_q1, g_invalidate_q1},
+ {DEFINE_VFP_D_IDX(3), g_contained_q1, g_invalidate_q1},
+ {DEFINE_VFP_D_IDX(4), g_contained_q2, g_invalidate_q2},
+ {DEFINE_VFP_D_IDX(5), g_contained_q2, g_invalidate_q2},
+ {DEFINE_VFP_D_IDX(6), g_contained_q3, g_invalidate_q3},
+ {DEFINE_VFP_D_IDX(7), g_contained_q3, g_invalidate_q3},
+ {DEFINE_VFP_D_IDX(8), g_contained_q4, g_invalidate_q4},
+ {DEFINE_VFP_D_IDX(9), g_contained_q4, g_invalidate_q4},
+ {DEFINE_VFP_D_IDX(10), g_contained_q5, g_invalidate_q5},
+ {DEFINE_VFP_D_IDX(11), g_contained_q5, g_invalidate_q5},
+ {DEFINE_VFP_D_IDX(12), g_contained_q6, g_invalidate_q6},
+ {DEFINE_VFP_D_IDX(13), g_contained_q6, g_invalidate_q6},
+ {DEFINE_VFP_D_IDX(14), g_contained_q7, g_invalidate_q7},
+ {DEFINE_VFP_D_IDX(15), g_contained_q7, g_invalidate_q7},
+ {DEFINE_VFP_D_IDX(16), g_contained_q8, g_invalidate_q8},
+ {DEFINE_VFP_D_IDX(17), g_contained_q8, g_invalidate_q8},
+ {DEFINE_VFP_D_IDX(18), g_contained_q9, g_invalidate_q9},
+ {DEFINE_VFP_D_IDX(19), g_contained_q9, g_invalidate_q9},
+ {DEFINE_VFP_D_IDX(20), g_contained_q10, g_invalidate_q10},
+ {DEFINE_VFP_D_IDX(21), g_contained_q10, g_invalidate_q10},
+ {DEFINE_VFP_D_IDX(22), g_contained_q11, g_invalidate_q11},
+ {DEFINE_VFP_D_IDX(23), g_contained_q11, g_invalidate_q11},
+ {DEFINE_VFP_D_IDX(24), g_contained_q12, g_invalidate_q12},
+ {DEFINE_VFP_D_IDX(25), g_contained_q12, g_invalidate_q12},
+ {DEFINE_VFP_D_IDX(26), g_contained_q13, g_invalidate_q13},
+ {DEFINE_VFP_D_IDX(27), g_contained_q13, g_invalidate_q13},
+ {DEFINE_VFP_D_IDX(28), g_contained_q14, g_invalidate_q14},
+ {DEFINE_VFP_D_IDX(29), g_contained_q14, g_invalidate_q14},
+ {DEFINE_VFP_D_IDX(30), g_contained_q15, g_invalidate_q15},
+ {DEFINE_VFP_D_IDX(31), g_contained_q15, g_invalidate_q15},
+
+ {DEFINE_VFP_Q_IDX(0), NULL, g_invalidate_q0},
+ {DEFINE_VFP_Q_IDX(1), NULL, g_invalidate_q1},
+ {DEFINE_VFP_Q_IDX(2), NULL, g_invalidate_q2},
+ {DEFINE_VFP_Q_IDX(3), NULL, g_invalidate_q3},
+ {DEFINE_VFP_Q_IDX(4), NULL, g_invalidate_q4},
+ {DEFINE_VFP_Q_IDX(5), NULL, g_invalidate_q5},
+ {DEFINE_VFP_Q_IDX(6), NULL, g_invalidate_q6},
+ {DEFINE_VFP_Q_IDX(7), NULL, g_invalidate_q7},
+ {DEFINE_VFP_Q_IDX(8), NULL, g_invalidate_q8},
+ {DEFINE_VFP_Q_IDX(9), NULL, g_invalidate_q9},
+ {DEFINE_VFP_Q_IDX(10), NULL, g_invalidate_q10},
+ {DEFINE_VFP_Q_IDX(11), NULL, g_invalidate_q11},
+ {DEFINE_VFP_Q_IDX(12), NULL, g_invalidate_q12},
+ {DEFINE_VFP_Q_IDX(13), NULL, g_invalidate_q13},
+ {DEFINE_VFP_Q_IDX(14), NULL, g_invalidate_q14},
+ {DEFINE_VFP_Q_IDX(15), NULL, g_invalidate_q15},
+
+#if defined(__arm64__) || defined(__aarch64__)
+ {e_regSetVFP, vfp_fpsr, "fpsr", NULL, Uint, Hex, 4, VFP_OFFSET_NAME(fpsr),
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetVFP, vfp_fpcr, "fpcr", NULL, Uint, Hex, 4, VFP_OFFSET_NAME(fpcr),
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, NULL, NULL}
+#else
+ {e_regSetVFP, vfp_fpscr, "fpscr", NULL, Uint, Hex, 4,
+ VFP_OFFSET_NAME(fpscr), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL}
+#endif
+};
+
+// Exception registers
+
+const DNBRegisterInfo DNBArchMachARM::g_exc_registers[] = {
+ {e_regSetVFP, exc_exception, "exception", NULL, Uint, Hex, 4,
+ EXC_OFFSET(exception), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM},
+ {e_regSetVFP, exc_fsr, "fsr", NULL, Uint, Hex, 4, EXC_OFFSET(fsr),
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM},
+ {e_regSetVFP, exc_far, "far", NULL, Uint, Hex, 4, EXC_OFFSET(far),
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM}};
+
+// Number of registers in each register set
+const size_t DNBArchMachARM::k_num_gpr_registers =
+ sizeof(g_gpr_registers) / sizeof(DNBRegisterInfo);
+const size_t DNBArchMachARM::k_num_vfp_registers =
+ sizeof(g_vfp_registers) / sizeof(DNBRegisterInfo);
+const size_t DNBArchMachARM::k_num_exc_registers =
+ sizeof(g_exc_registers) / sizeof(DNBRegisterInfo);
+const size_t DNBArchMachARM::k_num_all_registers =
+ k_num_gpr_registers + k_num_vfp_registers + k_num_exc_registers;
+
+// Register set definitions. The first definitions at register set index
+// of zero is for all registers, followed by other registers sets. The
+// register information for the all register set need not be filled in.
+const DNBRegisterSetInfo DNBArchMachARM::g_reg_sets[] = {
+ {"ARM Registers", NULL, k_num_all_registers},
+ {"General Purpose Registers", g_gpr_registers, k_num_gpr_registers},
+ {"Floating Point Registers", g_vfp_registers, k_num_vfp_registers},
+ {"Exception State Registers", g_exc_registers, k_num_exc_registers}};
+// Total number of register sets for this architecture
+const size_t DNBArchMachARM::k_num_register_sets =
+ sizeof(g_reg_sets) / sizeof(DNBRegisterSetInfo);
+
+const DNBRegisterSetInfo *
+DNBArchMachARM::GetRegisterSetInfo(nub_size_t *num_reg_sets) {
+ *num_reg_sets = k_num_register_sets;
+ return g_reg_sets;
+}
+
+bool DNBArchMachARM::GetRegisterValue(uint32_t set, uint32_t reg,
+ DNBRegisterValue *value) {
+ if (set == REGISTER_SET_GENERIC) {
+ switch (reg) {
+ case GENERIC_REGNUM_PC: // Program Counter
+ set = e_regSetGPR;
+ reg = gpr_pc;
+ break;
+
+ case GENERIC_REGNUM_SP: // Stack Pointer
+ set = e_regSetGPR;
+ reg = gpr_sp;
+ break;
+
+ case GENERIC_REGNUM_FP: // Frame Pointer
+ set = e_regSetGPR;
+ reg = gpr_r7; // is this the right reg?
+ break;
+
+ case GENERIC_REGNUM_RA: // Return Address
+ set = e_regSetGPR;
+ reg = gpr_lr;
+ break;
+
+ case GENERIC_REGNUM_FLAGS: // Processor flags register
+ set = e_regSetGPR;
+ reg = gpr_cpsr;
+ break;
+
+ default:
+ return false;
+ }
+ }
+
+ if (GetRegisterState(set, false) != KERN_SUCCESS)
+ return false;
+
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo) {
+ value->info = *regInfo;
+ switch (set) {
+ case e_regSetGPR:
+ if (reg < k_num_gpr_registers) {
+ value->value.uint32 = m_state.context.gpr.__r[reg];
+ return true;
+ }
+ break;
+
+ case e_regSetVFP:
+ // "reg" is an index into the floating point register set at this point.
+ // We need to translate it up so entry 0 in the fp reg set is the same as
+ // vfp_s0
+ // in the enumerated values for case statement below.
+ if (reg >= vfp_s0 && reg <= vfp_s31) {
+#if defined(__arm64__) || defined(__aarch64__)
+ uint32_t *s_reg =
+ ((uint32_t *)&m_state.context.vfp.__v[0]) + (reg - vfp_s0);
+ memcpy(&value->value.v_uint8, s_reg, 4);
+#else
+ value->value.uint32 = m_state.context.vfp.__r[reg];
+#endif
+ return true;
+ } else if (reg >= vfp_d0 && reg <= vfp_d31) {
+#if defined(__arm64__) || defined(__aarch64__)
+ uint64_t *d_reg =
+ ((uint64_t *)&m_state.context.vfp.__v[0]) + (reg - vfp_d0);
+ memcpy(&value->value.v_uint8, d_reg, 8);
+#else
+ uint32_t d_reg_idx = reg - vfp_d0;
+ uint32_t s_reg_idx = d_reg_idx * 2;
+ value->value.v_sint32[0] = m_state.context.vfp.__r[s_reg_idx + 0];
+ value->value.v_sint32[1] = m_state.context.vfp.__r[s_reg_idx + 1];
+#endif
+ return true;
+ } else if (reg >= vfp_q0 && reg <= vfp_q15) {
+#if defined(__arm64__) || defined(__aarch64__)
+ memcpy(&value->value.v_uint8,
+ (uint8_t *)&m_state.context.vfp.__v[reg - vfp_q0], 16);
+#else
+ uint32_t s_reg_idx = (reg - vfp_q0) * 4;
+ memcpy(&value->value.v_uint8,
+ (uint8_t *)&m_state.context.vfp.__r[s_reg_idx], 16);
+#endif
+ return true;
+ }
+#if defined(__arm64__) || defined(__aarch64__)
+ else if (reg == vfp_fpsr) {
+ value->value.uint32 = m_state.context.vfp.__fpsr;
+ return true;
+ } else if (reg == vfp_fpcr) {
+ value->value.uint32 = m_state.context.vfp.__fpcr;
+ return true;
+ }
+#else
+ else if (reg == vfp_fpscr) {
+ value->value.uint32 = m_state.context.vfp.__fpscr;
+ return true;
+ }
+#endif
+ break;
+
+ case e_regSetEXC:
+ if (reg < k_num_exc_registers) {
+ value->value.uint32 = (&m_state.context.exc.__exception)[reg];
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+bool DNBArchMachARM::SetRegisterValue(uint32_t set, uint32_t reg,
+ const DNBRegisterValue *value) {
+ if (set == REGISTER_SET_GENERIC) {
+ switch (reg) {
+ case GENERIC_REGNUM_PC: // Program Counter
+ set = e_regSetGPR;
+ reg = gpr_pc;
+ break;
+
+ case GENERIC_REGNUM_SP: // Stack Pointer
+ set = e_regSetGPR;
+ reg = gpr_sp;
+ break;
+
+ case GENERIC_REGNUM_FP: // Frame Pointer
+ set = e_regSetGPR;
+ reg = gpr_r7;
+ break;
+
+ case GENERIC_REGNUM_RA: // Return Address
+ set = e_regSetGPR;
+ reg = gpr_lr;
+ break;
+
+ case GENERIC_REGNUM_FLAGS: // Processor flags register
+ set = e_regSetGPR;
+ reg = gpr_cpsr;
+ break;
+
+ default:
+ return false;
+ }
+ }
+
+ if (GetRegisterState(set, false) != KERN_SUCCESS)
+ return false;
+
+ bool success = false;
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo) {
+ switch (set) {
+ case e_regSetGPR:
+ if (reg < k_num_gpr_registers) {
+ m_state.context.gpr.__r[reg] = value->value.uint32;
+ success = true;
+ }
+ break;
+
+ case e_regSetVFP:
+ // "reg" is an index into the floating point register set at this point.
+ // We need to translate it up so entry 0 in the fp reg set is the same as
+ // vfp_s0
+ // in the enumerated values for case statement below.
+ if (reg >= vfp_s0 && reg <= vfp_s31) {
+#if defined(__arm64__) || defined(__aarch64__)
+ uint32_t *s_reg =
+ ((uint32_t *)&m_state.context.vfp.__v[0]) + (reg - vfp_s0);
+ memcpy(s_reg, &value->value.v_uint8, 4);
+#else
+ m_state.context.vfp.__r[reg] = value->value.uint32;
+#endif
+ success = true;
+ } else if (reg >= vfp_d0 && reg <= vfp_d31) {
+#if defined(__arm64__) || defined(__aarch64__)
+ uint64_t *d_reg =
+ ((uint64_t *)&m_state.context.vfp.__v[0]) + (reg - vfp_d0);
+ memcpy(d_reg, &value->value.v_uint8, 8);
+#else
+ uint32_t d_reg_idx = reg - vfp_d0;
+ uint32_t s_reg_idx = d_reg_idx * 2;
+ m_state.context.vfp.__r[s_reg_idx + 0] = value->value.v_sint32[0];
+ m_state.context.vfp.__r[s_reg_idx + 1] = value->value.v_sint32[1];
+#endif
+ success = true;
+ } else if (reg >= vfp_q0 && reg <= vfp_q15) {
+#if defined(__arm64__) || defined(__aarch64__)
+ memcpy((uint8_t *)&m_state.context.vfp.__v[reg - vfp_q0],
+ &value->value.v_uint8, 16);
+#else
+ uint32_t s_reg_idx = (reg - vfp_q0) * 4;
+ memcpy((uint8_t *)&m_state.context.vfp.__r[s_reg_idx],
+ &value->value.v_uint8, 16);
+#endif
+ success = true;
+ }
+#if defined(__arm64__) || defined(__aarch64__)
+ else if (reg == vfp_fpsr) {
+ m_state.context.vfp.__fpsr = value->value.uint32;
+ success = true;
+ } else if (reg == vfp_fpcr) {
+ m_state.context.vfp.__fpcr = value->value.uint32;
+ success = true;
+ }
+#else
+ else if (reg == vfp_fpscr) {
+ m_state.context.vfp.__fpscr = value->value.uint32;
+ success = true;
+ }
+#endif
+ break;
+
+ case e_regSetEXC:
+ if (reg < k_num_exc_registers) {
+ (&m_state.context.exc.__exception)[reg] = value->value.uint32;
+ success = true;
+ }
+ break;
+ }
+ }
+ if (success)
+ return SetRegisterState(set) == KERN_SUCCESS;
+ return false;
+}
+
+kern_return_t DNBArchMachARM::GetRegisterState(int set, bool force) {
+ switch (set) {
+ case e_regSetALL:
+ return GetGPRState(force) | GetVFPState(force) | GetEXCState(force) |
+ GetDBGState(force);
+ case e_regSetGPR:
+ return GetGPRState(force);
+ case e_regSetVFP:
+ return GetVFPState(force);
+ case e_regSetEXC:
+ return GetEXCState(force);
+ case e_regSetDBG:
+ return GetDBGState(force);
+ default:
+ break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+kern_return_t DNBArchMachARM::SetRegisterState(int set) {
+ // Make sure we have a valid context to set.
+ kern_return_t err = GetRegisterState(set, false);
+ if (err != KERN_SUCCESS)
+ return err;
+
+ switch (set) {
+ case e_regSetALL:
+ return SetGPRState() | SetVFPState() | SetEXCState() | SetDBGState(false);
+ case e_regSetGPR:
+ return SetGPRState();
+ case e_regSetVFP:
+ return SetVFPState();
+ case e_regSetEXC:
+ return SetEXCState();
+ case e_regSetDBG:
+ return SetDBGState(false);
+ default:
+ break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+bool DNBArchMachARM::RegisterSetStateIsValid(int set) const {
+ return m_state.RegsAreValid(set);
+}
+
+nub_size_t DNBArchMachARM::GetRegisterContext(void *buf, nub_size_t buf_len) {
+ nub_size_t size = sizeof(m_state.context.gpr) + sizeof(m_state.context.vfp) +
+ sizeof(m_state.context.exc);
+
+ if (buf && buf_len) {
+ if (size > buf_len)
+ size = buf_len;
+
+ bool force = false;
+ if (GetGPRState(force) | GetVFPState(force) | GetEXCState(force))
+ return 0;
+
+ // Copy each struct individually to avoid any padding that might be between
+ // the structs in m_state.context
+ uint8_t *p = (uint8_t *)buf;
+ ::memcpy(p, &m_state.context.gpr, sizeof(m_state.context.gpr));
+ p += sizeof(m_state.context.gpr);
+ ::memcpy(p, &m_state.context.vfp, sizeof(m_state.context.vfp));
+ p += sizeof(m_state.context.vfp);
+ ::memcpy(p, &m_state.context.exc, sizeof(m_state.context.exc));
+ p += sizeof(m_state.context.exc);
+
+ size_t bytes_written = p - (uint8_t *)buf;
+ UNUSED_IF_ASSERT_DISABLED(bytes_written);
+ assert(bytes_written == size);
+ }
+ DNBLogThreadedIf(
+ LOG_THREAD,
+ "DNBArchMachARM::GetRegisterContext (buf = %p, len = %llu) => %llu", buf,
+ (uint64_t)buf_len, (uint64_t)size);
+ // Return the size of the register context even if NULL was passed in
+ return size;
+}
+
+nub_size_t DNBArchMachARM::SetRegisterContext(const void *buf,
+ nub_size_t buf_len) {
+ nub_size_t size = sizeof(m_state.context.gpr) + sizeof(m_state.context.vfp) +
+ sizeof(m_state.context.exc);
+
+ if (buf == NULL || buf_len == 0)
+ size = 0;
+
+ if (size) {
+ if (size > buf_len)
+ size = buf_len;
+
+ // Copy each struct individually to avoid any padding that might be between
+ // the structs in m_state.context
+ uint8_t *p = (uint8_t *)buf;
+ ::memcpy(&m_state.context.gpr, p, sizeof(m_state.context.gpr));
+ p += sizeof(m_state.context.gpr);
+ ::memcpy(&m_state.context.vfp, p, sizeof(m_state.context.vfp));
+ p += sizeof(m_state.context.vfp);
+ ::memcpy(&m_state.context.exc, p, sizeof(m_state.context.exc));
+ p += sizeof(m_state.context.exc);
+
+ size_t bytes_written = p - (uint8_t *)buf;
+ UNUSED_IF_ASSERT_DISABLED(bytes_written);
+ assert(bytes_written == size);
+
+ if (SetGPRState() | SetVFPState() | SetEXCState())
+ return 0;
+ }
+ DNBLogThreadedIf(
+ LOG_THREAD,
+ "DNBArchMachARM::SetRegisterContext (buf = %p, len = %llu) => %llu", buf,
+ (uint64_t)buf_len, (uint64_t)size);
+ return size;
+}
+
+uint32_t DNBArchMachARM::SaveRegisterState() {
+ kern_return_t kret = ::thread_abort_safely(m_thread->MachPortNumber());
+ DNBLogThreadedIf(
+ LOG_THREAD, "thread = 0x%4.4x calling thread_abort_safely (tid) => %u "
+ "(SetGPRState() for stop_count = %u)",
+ m_thread->MachPortNumber(), kret, m_thread->Process()->StopCount());
+
+ // Always re-read the registers because above we call thread_abort_safely();
+ bool force = true;
+
+ if ((kret = GetGPRState(force)) != KERN_SUCCESS) {
+ DNBLogThreadedIf(LOG_THREAD, "DNBArchMachARM::SaveRegisterState () error: "
+ "GPR regs failed to read: %u ",
+ kret);
+ } else if ((kret = GetVFPState(force)) != KERN_SUCCESS) {
+ DNBLogThreadedIf(LOG_THREAD, "DNBArchMachARM::SaveRegisterState () error: "
+ "%s regs failed to read: %u",
+ "VFP", kret);
+ } else {
+ const uint32_t save_id = GetNextRegisterStateSaveID();
+ m_saved_register_states[save_id] = m_state.context;
+ return save_id;
+ }
+ return UINT32_MAX;
+}
+
+bool DNBArchMachARM::RestoreRegisterState(uint32_t save_id) {
+ SaveRegisterStates::iterator pos = m_saved_register_states.find(save_id);
+ if (pos != m_saved_register_states.end()) {
+ m_state.context.gpr = pos->second.gpr;
+ m_state.context.vfp = pos->second.vfp;
+ kern_return_t kret;
+ bool success = true;
+ if ((kret = SetGPRState()) != KERN_SUCCESS) {
+ DNBLogThreadedIf(LOG_THREAD, "DNBArchMachARM::RestoreRegisterState "
+ "(save_id = %u) error: GPR regs failed to "
+ "write: %u",
+ save_id, kret);
+ success = false;
+ } else if ((kret = SetVFPState()) != KERN_SUCCESS) {
+ DNBLogThreadedIf(LOG_THREAD, "DNBArchMachARM::RestoreRegisterState "
+ "(save_id = %u) error: %s regs failed to "
+ "write: %u",
+ save_id, "VFP", kret);
+ success = false;
+ }
+ m_saved_register_states.erase(pos);
+ return success;
+ }
+ return false;
+}
+
+#endif // #if defined (__arm__)
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h
new file mode 100644
index 00000000000..4bb899b4503
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm/DNBArchImpl.h
@@ -0,0 +1,274 @@
+//===-- DNBArchImpl.h -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DebugNubArchMachARM_h__
+#define __DebugNubArchMachARM_h__
+
+#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
+
+#include "DNBArch.h"
+
+#include <map>
+
+class MachThread;
+
+class DNBArchMachARM : public DNBArchProtocol {
+public:
+ enum { kMaxNumThumbITBreakpoints = 4 };
+
+ DNBArchMachARM(MachThread *thread)
+ : m_thread(thread), m_state(), m_disabled_watchpoints(),
+ m_hw_single_chained_step_addr(INVALID_NUB_ADDRESS),
+ m_last_decode_pc(INVALID_NUB_ADDRESS), m_watchpoint_hw_index(-1),
+ m_watchpoint_did_occur(false),
+ m_watchpoint_resume_single_step_enabled(false),
+ m_saved_register_states() {
+ m_disabled_watchpoints.resize(16);
+ memset(&m_dbg_save, 0, sizeof(m_dbg_save));
+#if defined(USE_ARM_DISASSEMBLER_FRAMEWORK)
+ ThumbStaticsInit(&m_last_decode_thumb);
+#endif
+ }
+
+ virtual ~DNBArchMachARM() {}
+
+ static void Initialize();
+ static const DNBRegisterSetInfo *GetRegisterSetInfo(nub_size_t *num_reg_sets);
+
+ virtual bool GetRegisterValue(uint32_t set, uint32_t reg,
+ DNBRegisterValue *value);
+ virtual bool SetRegisterValue(uint32_t set, uint32_t reg,
+ const DNBRegisterValue *value);
+ virtual nub_size_t GetRegisterContext(void *buf, nub_size_t buf_len);
+ virtual nub_size_t SetRegisterContext(const void *buf, nub_size_t buf_len);
+ virtual uint32_t SaveRegisterState();
+ virtual bool RestoreRegisterState(uint32_t save_id);
+
+ virtual kern_return_t GetRegisterState(int set, bool force);
+ virtual kern_return_t SetRegisterState(int set);
+ virtual bool RegisterSetStateIsValid(int set) const;
+
+ virtual uint64_t GetPC(uint64_t failValue); // Get program counter
+ virtual kern_return_t SetPC(uint64_t value);
+ virtual uint64_t GetSP(uint64_t failValue); // Get stack pointer
+ virtual void ThreadWillResume();
+ virtual bool ThreadDidStop();
+ virtual bool NotifyException(MachException::Data &exc);
+
+ static DNBArchProtocol *Create(MachThread *thread);
+ static const uint8_t *SoftwareBreakpointOpcode(nub_size_t byte_size);
+ static uint32_t GetCPUType();
+
+ virtual uint32_t NumSupportedHardwareBreakpoints();
+ virtual uint32_t NumSupportedHardwareWatchpoints();
+ virtual uint32_t EnableHardwareBreakpoint(nub_addr_t addr, nub_size_t size);
+ virtual bool DisableHardwareBreakpoint(uint32_t hw_break_index);
+
+ virtual uint32_t EnableHardwareWatchpoint(nub_addr_t addr, nub_size_t size,
+ bool read, bool write,
+ bool also_set_on_task);
+ virtual bool DisableHardwareWatchpoint(uint32_t hw_break_index,
+ bool also_set_on_task);
+ virtual bool DisableHardwareWatchpoint_helper(uint32_t hw_break_index,
+ bool also_set_on_task);
+ virtual bool ReenableHardwareWatchpoint(uint32_t hw_break_index);
+ virtual bool ReenableHardwareWatchpoint_helper(uint32_t hw_break_index);
+
+ virtual bool StepNotComplete();
+ virtual uint32_t GetHardwareWatchpointHit(nub_addr_t &addr);
+
+#if defined(ARM_DEBUG_STATE32) && (defined(__arm64__) || defined(__aarch64__))
+ typedef arm_debug_state32_t DBG;
+#else
+ typedef arm_debug_state_t DBG;
+#endif
+
+protected:
+ kern_return_t EnableHardwareSingleStep(bool enable);
+ kern_return_t SetSingleStepSoftwareBreakpoints();
+
+ bool ConditionPassed(uint8_t condition, uint32_t cpsr);
+#if defined(USE_ARM_DISASSEMBLER_FRAMEWORK)
+ bool ComputeNextPC(nub_addr_t currentPC,
+ arm_decoded_instruction_t decodedInstruction,
+ bool currentPCIsThumb, nub_addr_t *targetPC);
+ arm_error_t DecodeInstructionUsingDisassembler(
+ nub_addr_t curr_pc, uint32_t curr_cpsr,
+ arm_decoded_instruction_t *decodedInstruction,
+ thumb_static_data_t *thumbStaticData, nub_addr_t *next_pc);
+ void DecodeITBlockInstructions(nub_addr_t curr_pc);
+#endif
+ void EvaluateNextInstructionForSoftwareBreakpointSetup(nub_addr_t currentPC,
+ uint32_t cpsr,
+ bool currentPCIsThumb,
+ nub_addr_t *nextPC,
+ bool *nextPCIsThumb);
+
+ enum RegisterSet {
+ e_regSetALL = REGISTER_SET_ALL,
+ e_regSetGPR, // ARM_THREAD_STATE
+ e_regSetVFP, // ARM_VFP_STATE (ARM_NEON_STATE if defined __arm64__)
+ e_regSetEXC, // ARM_EXCEPTION_STATE
+ e_regSetDBG, // ARM_DEBUG_STATE (ARM_DEBUG_STATE32 if defined __arm64__)
+ kNumRegisterSets
+ };
+
+ enum { Read = 0, Write = 1, kNumErrors = 2 };
+
+ typedef arm_thread_state_t GPR;
+#if defined(__arm64__) || defined(__aarch64__)
+ typedef arm_neon_state_t FPU;
+#else
+ typedef arm_vfp_state_t FPU;
+#endif
+ typedef arm_exception_state_t EXC;
+
+ static const DNBRegisterInfo g_gpr_registers[];
+ static const DNBRegisterInfo g_vfp_registers[];
+ static const DNBRegisterInfo g_exc_registers[];
+ static const DNBRegisterSetInfo g_reg_sets[];
+
+ static const size_t k_num_gpr_registers;
+ static const size_t k_num_vfp_registers;
+ static const size_t k_num_exc_registers;
+ static const size_t k_num_all_registers;
+ static const size_t k_num_register_sets;
+
+ struct Context {
+ GPR gpr;
+ FPU vfp;
+ EXC exc;
+ };
+
+ struct State {
+ Context context;
+ DBG dbg;
+ kern_return_t gpr_errs[2]; // Read/Write errors
+ kern_return_t vfp_errs[2]; // Read/Write errors
+ kern_return_t exc_errs[2]; // Read/Write errors
+ kern_return_t dbg_errs[2]; // Read/Write errors
+ State() {
+ uint32_t i;
+ for (i = 0; i < kNumErrors; i++) {
+ gpr_errs[i] = -1;
+ vfp_errs[i] = -1;
+ exc_errs[i] = -1;
+ dbg_errs[i] = -1;
+ }
+ }
+ void InvalidateRegisterSetState(int set) { SetError(set, Read, -1); }
+ kern_return_t GetError(int set, uint32_t err_idx) const {
+ if (err_idx < kNumErrors) {
+ switch (set) {
+ // When getting all errors, just OR all values together to see if
+ // we got any kind of error.
+ case e_regSetALL:
+ return gpr_errs[err_idx] | vfp_errs[err_idx] | exc_errs[err_idx] |
+ dbg_errs[err_idx];
+ case e_regSetGPR:
+ return gpr_errs[err_idx];
+ case e_regSetVFP:
+ return vfp_errs[err_idx];
+ case e_regSetEXC:
+ return exc_errs[err_idx];
+ case e_regSetDBG:
+ return dbg_errs[err_idx];
+ default:
+ break;
+ }
+ }
+ return -1;
+ }
+ bool SetError(int set, uint32_t err_idx, kern_return_t err) {
+ if (err_idx < kNumErrors) {
+ switch (set) {
+ case e_regSetALL:
+ gpr_errs[err_idx] = err;
+ vfp_errs[err_idx] = err;
+ dbg_errs[err_idx] = err;
+ exc_errs[err_idx] = err;
+ return true;
+
+ case e_regSetGPR:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case e_regSetVFP:
+ vfp_errs[err_idx] = err;
+ return true;
+
+ case e_regSetEXC:
+ exc_errs[err_idx] = err;
+ return true;
+
+ case e_regSetDBG:
+ dbg_errs[err_idx] = err;
+ return true;
+ default:
+ break;
+ }
+ }
+ return false;
+ }
+ bool RegsAreValid(int set) const {
+ return GetError(set, Read) == KERN_SUCCESS;
+ }
+ };
+
+ kern_return_t GetGPRState(bool force);
+ kern_return_t GetVFPState(bool force);
+ kern_return_t GetEXCState(bool force);
+ kern_return_t GetDBGState(bool force);
+
+ kern_return_t SetGPRState();
+ kern_return_t SetVFPState();
+ kern_return_t SetEXCState();
+ kern_return_t SetDBGState(bool also_set_on_task);
+
+ bool IsWatchpointEnabled(const DBG &debug_state, uint32_t hw_index);
+ nub_addr_t GetWatchpointAddressByIndex(uint32_t hw_index);
+ nub_addr_t GetWatchAddress(const DBG &debug_state, uint32_t hw_index);
+
+ class disabled_watchpoint {
+ public:
+ disabled_watchpoint() {
+ addr = 0;
+ control = 0;
+ }
+ nub_addr_t addr;
+ uint32_t control;
+ };
+
+protected:
+ MachThread *m_thread;
+ State m_state;
+ DBG m_dbg_save;
+
+ // armv8 doesn't keep the disabled watchpoint values in the debug register
+ // context like armv7;
+ // we need to save them aside when we disable them temporarily.
+ std::vector<disabled_watchpoint> m_disabled_watchpoints;
+
+ nub_addr_t m_hw_single_chained_step_addr;
+ nub_addr_t m_last_decode_pc;
+
+ // The following member variables should be updated atomically.
+ int32_t m_watchpoint_hw_index;
+ bool m_watchpoint_did_occur;
+ bool m_watchpoint_resume_single_step_enabled;
+
+ typedef std::map<uint32_t, Context> SaveRegisterStates;
+ SaveRegisterStates m_saved_register_states;
+};
+
+#endif // #if defined (__arm__)
+#endif // #ifndef __DebugNubArchMachARM_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp
new file mode 100644
index 00000000000..f99dbc48b12
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp
@@ -0,0 +1,2201 @@
+//===-- DNBArchImplARM64.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
+
+#include "MacOSX/arm64/DNBArchImplARM64.h"
+
+#if defined(ARM_THREAD_STATE64_COUNT)
+
+#include "DNB.h"
+#include "DNBBreakpoint.h"
+#include "DNBLog.h"
+#include "DNBRegisterInfo.h"
+#include "MacOSX/MachProcess.h"
+#include "MacOSX/MachThread.h"
+
+#include <inttypes.h>
+#include <sys/sysctl.h>
+
+#if __has_feature(ptrauth_calls)
+#include <ptrauth.h>
+#endif
+
+// Break only in privileged or user mode
+// (PAC bits in the DBGWVRn_EL1 watchpoint control register)
+#define S_USER ((uint32_t)(2u << 1))
+
+#define BCR_ENABLE ((uint32_t)(1u))
+#define WCR_ENABLE ((uint32_t)(1u))
+
+// Watchpoint load/store
+// (LSC bits in the DBGWVRn_EL1 watchpoint control register)
+#define WCR_LOAD ((uint32_t)(1u << 3))
+#define WCR_STORE ((uint32_t)(1u << 4))
+
+// Enable breakpoint, watchpoint, and vector catch debug exceptions.
+// (MDE bit in the MDSCR_EL1 register. Equivalent to the MDBGen bit in
+// DBGDSCRext in Aarch32)
+#define MDE_ENABLE ((uint32_t)(1u << 15))
+
+// Single instruction step
+// (SS bit in the MDSCR_EL1 register)
+#define SS_ENABLE ((uint32_t)(1u))
+
+static const uint8_t g_arm64_breakpoint_opcode[] = {
+ 0x00, 0x00, 0x20, 0xD4}; // "brk #0", 0xd4200000 in BE byte order
+
+// If we need to set one logical watchpoint by using
+// two hardware watchpoint registers, the watchpoint
+// will be split into a "high" and "low" watchpoint.
+// Record both of them in the LoHi array.
+
+// It's safe to initialize to all 0's since
+// hi > lo and therefore LoHi[i] cannot be 0.
+static uint32_t LoHi[16] = {0};
+
+void DNBArchMachARM64::Initialize() {
+ DNBArchPluginInfo arch_plugin_info = {
+ CPU_TYPE_ARM64, DNBArchMachARM64::Create,
+ DNBArchMachARM64::GetRegisterSetInfo,
+ DNBArchMachARM64::SoftwareBreakpointOpcode};
+
+ // Register this arch plug-in with the main protocol class
+ DNBArchProtocol::RegisterArchPlugin(arch_plugin_info);
+
+ DNBArchPluginInfo arch_plugin_info_32 = {
+ CPU_TYPE_ARM64_32, DNBArchMachARM64::Create,
+ DNBArchMachARM64::GetRegisterSetInfo,
+ DNBArchMachARM64::SoftwareBreakpointOpcode};
+
+ // Register this arch plug-in with the main protocol class
+ DNBArchProtocol::RegisterArchPlugin(arch_plugin_info_32);
+}
+
+DNBArchProtocol *DNBArchMachARM64::Create(MachThread *thread) {
+ DNBArchMachARM64 *obj = new DNBArchMachARM64(thread);
+
+ return obj;
+}
+
+const uint8_t *
+DNBArchMachARM64::SoftwareBreakpointOpcode(nub_size_t byte_size) {
+ return g_arm64_breakpoint_opcode;
+}
+
+uint32_t DNBArchMachARM64::GetCPUType() { return CPU_TYPE_ARM64; }
+
+uint64_t DNBArchMachARM64::GetPC(uint64_t failValue) {
+ // Get program counter
+ if (GetGPRState(false) == KERN_SUCCESS)
+#if defined(__LP64__)
+ return arm_thread_state64_get_pc(m_state.context.gpr);
+#else
+ return m_state.context.gpr.__pc;
+#endif
+ return failValue;
+}
+
+kern_return_t DNBArchMachARM64::SetPC(uint64_t value) {
+ // Get program counter
+ kern_return_t err = GetGPRState(false);
+ if (err == KERN_SUCCESS) {
+#if defined(__LP64__)
+#if __has_feature(ptrauth_calls)
+ // The incoming value could be garbage. Strip it to avoid
+ // trapping when it gets resigned in the thread state.
+ value = (uint64_t) ptrauth_strip((void*) value, ptrauth_key_function_pointer);
+ value = (uint64_t) ptrauth_sign_unauthenticated((void*) value, ptrauth_key_function_pointer, 0);
+#endif
+ arm_thread_state64_set_pc_fptr (m_state.context.gpr, (void*) value);
+#else
+ m_state.context.gpr.__pc = value;
+#endif
+ err = SetGPRState();
+ }
+ return err == KERN_SUCCESS;
+}
+
+uint64_t DNBArchMachARM64::GetSP(uint64_t failValue) {
+ // Get stack pointer
+ if (GetGPRState(false) == KERN_SUCCESS)
+#if defined(__LP64__)
+ return arm_thread_state64_get_sp(m_state.context.gpr);
+#else
+ return m_state.context.gpr.__sp;
+#endif
+ return failValue;
+}
+
+kern_return_t DNBArchMachARM64::GetGPRState(bool force) {
+ int set = e_regSetGPR;
+ // Check if we have valid cached registers
+ if (!force && m_state.GetError(set, Read) == KERN_SUCCESS)
+ return KERN_SUCCESS;
+
+ // Read the registers from our thread
+ mach_msg_type_number_t count = e_regSetGPRCount;
+ kern_return_t kret =
+ ::thread_get_state(m_thread->MachPortNumber(), ARM_THREAD_STATE64,
+ (thread_state_t)&m_state.context.gpr, &count);
+ if (DNBLogEnabledForAny(LOG_THREAD)) {
+ uint64_t *x = &m_state.context.gpr.__x[0];
+ DNBLogThreaded(
+ "thread_get_state(0x%4.4x, %u, &gpr, %u) => 0x%8.8x (count = %u) regs"
+ "\n x0=%16.16llx"
+ "\n x1=%16.16llx"
+ "\n x2=%16.16llx"
+ "\n x3=%16.16llx"
+ "\n x4=%16.16llx"
+ "\n x5=%16.16llx"
+ "\n x6=%16.16llx"
+ "\n x7=%16.16llx"
+ "\n x8=%16.16llx"
+ "\n x9=%16.16llx"
+ "\n x10=%16.16llx"
+ "\n x11=%16.16llx"
+ "\n x12=%16.16llx"
+ "\n x13=%16.16llx"
+ "\n x14=%16.16llx"
+ "\n x15=%16.16llx"
+ "\n x16=%16.16llx"
+ "\n x17=%16.16llx"
+ "\n x18=%16.16llx"
+ "\n x19=%16.16llx"
+ "\n x20=%16.16llx"
+ "\n x21=%16.16llx"
+ "\n x22=%16.16llx"
+ "\n x23=%16.16llx"
+ "\n x24=%16.16llx"
+ "\n x25=%16.16llx"
+ "\n x26=%16.16llx"
+ "\n x27=%16.16llx"
+ "\n x28=%16.16llx"
+ "\n fp=%16.16llx"
+ "\n lr=%16.16llx"
+ "\n sp=%16.16llx"
+ "\n pc=%16.16llx"
+ "\n cpsr=%8.8x",
+ m_thread->MachPortNumber(), e_regSetGPR, e_regSetGPRCount, kret, count,
+ x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7], x[8], x[9], x[0], x[11],
+ x[12], x[13], x[14], x[15], x[16], x[17], x[18], x[19], x[20], x[21],
+ x[22], x[23], x[24], x[25], x[26], x[27], x[28],
+#if defined(__LP64__)
+ (uint64_t) arm_thread_state64_get_fp (m_state.context.gpr),
+ (uint64_t) arm_thread_state64_get_lr (m_state.context.gpr),
+ (uint64_t) arm_thread_state64_get_sp (m_state.context.gpr),
+ (uint64_t) arm_thread_state64_get_pc (m_state.context.gpr),
+#else
+ m_state.context.gpr.__fp, m_state.context.gpr.__lr,
+ m_state.context.gpr.__sp, m_state.context.gpr.__pc,
+#endif
+ m_state.context.gpr.__cpsr);
+ }
+ m_state.SetError(set, Read, kret);
+ return kret;
+}
+
+kern_return_t DNBArchMachARM64::GetVFPState(bool force) {
+ int set = e_regSetVFP;
+ // Check if we have valid cached registers
+ if (!force && m_state.GetError(set, Read) == KERN_SUCCESS)
+ return KERN_SUCCESS;
+
+ // Read the registers from our thread
+ mach_msg_type_number_t count = e_regSetVFPCount;
+ kern_return_t kret =
+ ::thread_get_state(m_thread->MachPortNumber(), ARM_NEON_STATE64,
+ (thread_state_t)&m_state.context.vfp, &count);
+ if (DNBLogEnabledForAny(LOG_THREAD)) {
+#if defined(__arm64__) || defined(__aarch64__)
+ DNBLogThreaded(
+ "thread_get_state(0x%4.4x, %u, &vfp, %u) => 0x%8.8x (count = %u) regs"
+ "\n q0 = 0x%16.16llx%16.16llx"
+ "\n q1 = 0x%16.16llx%16.16llx"
+ "\n q2 = 0x%16.16llx%16.16llx"
+ "\n q3 = 0x%16.16llx%16.16llx"
+ "\n q4 = 0x%16.16llx%16.16llx"
+ "\n q5 = 0x%16.16llx%16.16llx"
+ "\n q6 = 0x%16.16llx%16.16llx"
+ "\n q7 = 0x%16.16llx%16.16llx"
+ "\n q8 = 0x%16.16llx%16.16llx"
+ "\n q9 = 0x%16.16llx%16.16llx"
+ "\n q10 = 0x%16.16llx%16.16llx"
+ "\n q11 = 0x%16.16llx%16.16llx"
+ "\n q12 = 0x%16.16llx%16.16llx"
+ "\n q13 = 0x%16.16llx%16.16llx"
+ "\n q14 = 0x%16.16llx%16.16llx"
+ "\n q15 = 0x%16.16llx%16.16llx"
+ "\n q16 = 0x%16.16llx%16.16llx"
+ "\n q17 = 0x%16.16llx%16.16llx"
+ "\n q18 = 0x%16.16llx%16.16llx"
+ "\n q19 = 0x%16.16llx%16.16llx"
+ "\n q20 = 0x%16.16llx%16.16llx"
+ "\n q21 = 0x%16.16llx%16.16llx"
+ "\n q22 = 0x%16.16llx%16.16llx"
+ "\n q23 = 0x%16.16llx%16.16llx"
+ "\n q24 = 0x%16.16llx%16.16llx"
+ "\n q25 = 0x%16.16llx%16.16llx"
+ "\n q26 = 0x%16.16llx%16.16llx"
+ "\n q27 = 0x%16.16llx%16.16llx"
+ "\n q28 = 0x%16.16llx%16.16llx"
+ "\n q29 = 0x%16.16llx%16.16llx"
+ "\n q30 = 0x%16.16llx%16.16llx"
+ "\n q31 = 0x%16.16llx%16.16llx"
+ "\n fpsr = 0x%8.8x"
+ "\n fpcr = 0x%8.8x\n\n",
+ m_thread->MachPortNumber(), e_regSetVFP, e_regSetVFPCount, kret, count,
+ ((uint64_t *)&m_state.context.vfp.__v[0])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[0])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[1])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[1])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[2])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[2])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[3])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[3])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[4])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[4])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[5])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[5])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[6])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[6])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[7])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[7])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[8])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[8])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[9])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[9])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[10])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[10])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[11])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[11])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[12])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[12])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[13])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[13])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[14])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[14])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[15])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[15])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[16])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[16])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[17])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[17])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[18])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[18])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[19])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[19])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[20])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[20])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[21])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[21])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[22])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[22])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[23])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[23])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[24])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[24])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[25])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[25])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[26])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[26])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[27])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[27])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[28])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[28])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[29])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[29])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[30])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[30])[1],
+ ((uint64_t *)&m_state.context.vfp.__v[31])[0],
+ ((uint64_t *)&m_state.context.vfp.__v[31])[1],
+ m_state.context.vfp.__fpsr, m_state.context.vfp.__fpcr);
+#endif
+ }
+ m_state.SetError(set, Read, kret);
+ return kret;
+}
+
+kern_return_t DNBArchMachARM64::GetEXCState(bool force) {
+ int set = e_regSetEXC;
+ // Check if we have valid cached registers
+ if (!force && m_state.GetError(set, Read) == KERN_SUCCESS)
+ return KERN_SUCCESS;
+
+ // Read the registers from our thread
+ mach_msg_type_number_t count = e_regSetEXCCount;
+ kern_return_t kret =
+ ::thread_get_state(m_thread->MachPortNumber(), ARM_EXCEPTION_STATE64,
+ (thread_state_t)&m_state.context.exc, &count);
+ m_state.SetError(set, Read, kret);
+ return kret;
+}
+
+static void DumpDBGState(const arm_debug_state_t &dbg) {
+ uint32_t i = 0;
+ for (i = 0; i < 16; i++)
+ DNBLogThreadedIf(LOG_STEP, "BVR%-2u/BCR%-2u = { 0x%8.8x, 0x%8.8x } "
+ "WVR%-2u/WCR%-2u = { 0x%8.8x, 0x%8.8x }",
+ i, i, dbg.__bvr[i], dbg.__bcr[i], i, i, dbg.__wvr[i],
+ dbg.__wcr[i]);
+}
+
+kern_return_t DNBArchMachARM64::GetDBGState(bool force) {
+ int set = e_regSetDBG;
+
+ // Check if we have valid cached registers
+ if (!force && m_state.GetError(set, Read) == KERN_SUCCESS)
+ return KERN_SUCCESS;
+
+ // Read the registers from our thread
+ mach_msg_type_number_t count = e_regSetDBGCount;
+ kern_return_t kret =
+ ::thread_get_state(m_thread->MachPortNumber(), ARM_DEBUG_STATE64,
+ (thread_state_t)&m_state.dbg, &count);
+ m_state.SetError(set, Read, kret);
+
+ return kret;
+}
+
+kern_return_t DNBArchMachARM64::SetGPRState() {
+ int set = e_regSetGPR;
+ kern_return_t kret = ::thread_set_state(
+ m_thread->MachPortNumber(), ARM_THREAD_STATE64,
+ (thread_state_t)&m_state.context.gpr, e_regSetGPRCount);
+ m_state.SetError(set, Write,
+ kret); // Set the current write error for this register set
+ m_state.InvalidateRegisterSetState(set); // Invalidate the current register
+ // state in case registers are read
+ // back differently
+ return kret; // Return the error code
+}
+
+kern_return_t DNBArchMachARM64::SetVFPState() {
+ int set = e_regSetVFP;
+ kern_return_t kret = ::thread_set_state(
+ m_thread->MachPortNumber(), ARM_NEON_STATE64,
+ (thread_state_t)&m_state.context.vfp, e_regSetVFPCount);
+ m_state.SetError(set, Write,
+ kret); // Set the current write error for this register set
+ m_state.InvalidateRegisterSetState(set); // Invalidate the current register
+ // state in case registers are read
+ // back differently
+ return kret; // Return the error code
+}
+
+kern_return_t DNBArchMachARM64::SetEXCState() {
+ int set = e_regSetEXC;
+ kern_return_t kret = ::thread_set_state(
+ m_thread->MachPortNumber(), ARM_EXCEPTION_STATE64,
+ (thread_state_t)&m_state.context.exc, e_regSetEXCCount);
+ m_state.SetError(set, Write,
+ kret); // Set the current write error for this register set
+ m_state.InvalidateRegisterSetState(set); // Invalidate the current register
+ // state in case registers are read
+ // back differently
+ return kret; // Return the error code
+}
+
+kern_return_t DNBArchMachARM64::SetDBGState(bool also_set_on_task) {
+ int set = e_regSetDBG;
+ kern_return_t kret =
+ ::thread_set_state(m_thread->MachPortNumber(), ARM_DEBUG_STATE64,
+ (thread_state_t)&m_state.dbg, e_regSetDBGCount);
+ if (also_set_on_task) {
+ kern_return_t task_kret = task_set_state(
+ m_thread->Process()->Task().TaskPort(), ARM_DEBUG_STATE64,
+ (thread_state_t)&m_state.dbg, e_regSetDBGCount);
+ if (task_kret != KERN_SUCCESS)
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM64::SetDBGState failed "
+ "to set debug control register state: "
+ "0x%8.8x.",
+ task_kret);
+ }
+ m_state.SetError(set, Write,
+ kret); // Set the current write error for this register set
+ m_state.InvalidateRegisterSetState(set); // Invalidate the current register
+ // state in case registers are read
+ // back differently
+
+ return kret; // Return the error code
+}
+
+void DNBArchMachARM64::ThreadWillResume() {
+ // Do we need to step this thread? If so, let the mach thread tell us so.
+ if (m_thread->IsStepping()) {
+ EnableHardwareSingleStep(true);
+ }
+
+ // Disable the triggered watchpoint temporarily before we resume.
+ // Plus, we try to enable hardware single step to execute past the instruction
+ // which triggered our watchpoint.
+ if (m_watchpoint_did_occur) {
+ if (m_watchpoint_hw_index >= 0) {
+ kern_return_t kret = GetDBGState(false);
+ if (kret == KERN_SUCCESS &&
+ !IsWatchpointEnabled(m_state.dbg, m_watchpoint_hw_index)) {
+ // The watchpoint might have been disabled by the user. We don't need
+ // to do anything at all
+ // to enable hardware single stepping.
+ m_watchpoint_did_occur = false;
+ m_watchpoint_hw_index = -1;
+ return;
+ }
+
+ DisableHardwareWatchpoint(m_watchpoint_hw_index, false);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::ThreadWillResume() "
+ "DisableHardwareWatchpoint(%d) called",
+ m_watchpoint_hw_index);
+
+ // Enable hardware single step to move past the watchpoint-triggering
+ // instruction.
+ m_watchpoint_resume_single_step_enabled =
+ (EnableHardwareSingleStep(true) == KERN_SUCCESS);
+
+ // If we are not able to enable single step to move past the
+ // watchpoint-triggering instruction,
+ // at least we should reset the two watchpoint member variables so that
+ // the next time around
+ // this callback function is invoked, the enclosing logical branch is
+ // skipped.
+ if (!m_watchpoint_resume_single_step_enabled) {
+ // Reset the two watchpoint member variables.
+ m_watchpoint_did_occur = false;
+ m_watchpoint_hw_index = -1;
+ DNBLogThreadedIf(
+ LOG_WATCHPOINTS,
+ "DNBArchMachARM::ThreadWillResume() failed to enable single step");
+ } else
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::ThreadWillResume() "
+ "succeeded to enable single step");
+ }
+ }
+}
+
+bool DNBArchMachARM64::NotifyException(MachException::Data &exc) {
+
+ switch (exc.exc_type) {
+ default:
+ break;
+ case EXC_BREAKPOINT:
+ if (exc.exc_data.size() == 2 && exc.exc_data[0] == EXC_ARM_DA_DEBUG) {
+ // The data break address is passed as exc_data[1].
+ nub_addr_t addr = exc.exc_data[1];
+ // Find the hardware index with the side effect of possibly massaging the
+ // addr to return the starting address as seen from the debugger side.
+ uint32_t hw_index = GetHardwareWatchpointHit(addr);
+
+ // One logical watchpoint was split into two watchpoint locations because
+ // it was too big. If the watchpoint exception is indicating the 2nd half
+ // of the two-parter, find the address of the 1st half and report that --
+ // that's what lldb is going to expect to see.
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::NotifyException "
+ "watchpoint %d was hit on address "
+ "0x%llx",
+ hw_index, (uint64_t)addr);
+ const int num_watchpoints = NumSupportedHardwareWatchpoints();
+ for (int i = 0; i < num_watchpoints; i++) {
+ if (LoHi[i] != 0 && LoHi[i] == hw_index && LoHi[i] != i &&
+ GetWatchpointAddressByIndex(i) != INVALID_NUB_ADDRESS) {
+ addr = GetWatchpointAddressByIndex(i);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM::NotifyException "
+ "It is a linked watchpoint; "
+ "rewritten to index %d addr 0x%llx",
+ LoHi[i], (uint64_t)addr);
+ }
+ }
+
+ if (hw_index != INVALID_NUB_HW_INDEX) {
+ m_watchpoint_did_occur = true;
+ m_watchpoint_hw_index = hw_index;
+ exc.exc_data[1] = addr;
+ // Piggyback the hw_index in the exc.data.
+ exc.exc_data.push_back(hw_index);
+ }
+
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+bool DNBArchMachARM64::ThreadDidStop() {
+ bool success = true;
+
+ m_state.InvalidateAllRegisterStates();
+
+ if (m_watchpoint_resume_single_step_enabled) {
+ // Great! We now disable the hardware single step as well as re-enable the
+ // hardware watchpoint.
+ // See also ThreadWillResume().
+ if (EnableHardwareSingleStep(false) == KERN_SUCCESS) {
+ if (m_watchpoint_did_occur && m_watchpoint_hw_index >= 0) {
+ ReenableHardwareWatchpoint(m_watchpoint_hw_index);
+ m_watchpoint_resume_single_step_enabled = false;
+ m_watchpoint_did_occur = false;
+ m_watchpoint_hw_index = -1;
+ } else {
+ DNBLogError("internal error detected: m_watchpoint_resume_step_enabled "
+ "is true but (m_watchpoint_did_occur && "
+ "m_watchpoint_hw_index >= 0) does not hold!");
+ }
+ } else {
+ DNBLogError("internal error detected: m_watchpoint_resume_step_enabled "
+ "is true but unable to disable single step!");
+ }
+ }
+
+ // Are we stepping a single instruction?
+ if (GetGPRState(true) == KERN_SUCCESS) {
+ // We are single stepping, was this the primary thread?
+ if (m_thread->IsStepping()) {
+ // This was the primary thread, we need to clear the trace
+ // bit if so.
+ success = EnableHardwareSingleStep(false) == KERN_SUCCESS;
+ } else {
+ // The MachThread will automatically restore the suspend count
+ // in ThreadDidStop(), so we don't need to do anything here if
+ // we weren't the primary thread the last time
+ }
+ }
+ return success;
+}
+
+// Set the single step bit in the processor status register.
+kern_return_t DNBArchMachARM64::EnableHardwareSingleStep(bool enable) {
+ DNBError err;
+ DNBLogThreadedIf(LOG_STEP, "%s( enable = %d )", __FUNCTION__, enable);
+
+ err = GetGPRState(false);
+
+ if (err.Fail()) {
+ err.LogThreaded("%s: failed to read the GPR registers", __FUNCTION__);
+ return err.Status();
+ }
+
+ err = GetDBGState(false);
+
+ if (err.Fail()) {
+ err.LogThreaded("%s: failed to read the DBG registers", __FUNCTION__);
+ return err.Status();
+ }
+
+ if (enable) {
+ DNBLogThreadedIf(LOG_STEP,
+ "%s: Setting MDSCR_EL1 Single Step bit at pc 0x%llx",
+#if defined(__LP64__)
+ __FUNCTION__, (uint64_t)arm_thread_state64_get_pc (m_state.context.gpr));
+#else
+ __FUNCTION__, (uint64_t)m_state.context.gpr.__pc);
+#endif
+ m_state.dbg.__mdscr_el1 |= SS_ENABLE;
+ } else {
+ DNBLogThreadedIf(LOG_STEP,
+ "%s: Clearing MDSCR_EL1 Single Step bit at pc 0x%llx",
+#if defined(__LP64__)
+ __FUNCTION__, (uint64_t)arm_thread_state64_get_pc (m_state.context.gpr));
+#else
+ __FUNCTION__, (uint64_t)m_state.context.gpr.__pc);
+#endif
+ m_state.dbg.__mdscr_el1 &= ~(SS_ENABLE);
+ }
+
+ return SetDBGState(false);
+}
+
+// return 1 if bit "BIT" is set in "value"
+static inline uint32_t bit(uint32_t value, uint32_t bit) {
+ return (value >> bit) & 1u;
+}
+
+// return the bitfield "value[msbit:lsbit]".
+static inline uint64_t bits(uint64_t value, uint32_t msbit, uint32_t lsbit) {
+ assert(msbit >= lsbit);
+ uint64_t shift_left = sizeof(value) * 8 - 1 - msbit;
+ value <<=
+ shift_left; // shift anything above the msbit off of the unsigned edge
+ value >>= shift_left + lsbit; // shift it back again down to the lsbit
+ // (including undoing any shift from above)
+ return value; // return our result
+}
+
+uint32_t DNBArchMachARM64::NumSupportedHardwareWatchpoints() {
+ // Set the init value to something that will let us know that we need to
+ // autodetect how many watchpoints are supported dynamically...
+ static uint32_t g_num_supported_hw_watchpoints = UINT_MAX;
+ if (g_num_supported_hw_watchpoints == UINT_MAX) {
+ // Set this to zero in case we can't tell if there are any HW breakpoints
+ g_num_supported_hw_watchpoints = 0;
+
+ size_t len;
+ uint32_t n = 0;
+ len = sizeof(n);
+ if (::sysctlbyname("hw.optional.watchpoint", &n, &len, NULL, 0) == 0) {
+ g_num_supported_hw_watchpoints = n;
+ DNBLogThreadedIf(LOG_THREAD, "hw.optional.watchpoint=%u", n);
+ } else {
+// For AArch64 we would need to look at ID_AA64DFR0_EL1 but debugserver runs in
+// EL0 so it can't
+// access that reg. The kernel should have filled in the sysctls based on it
+// though.
+#if defined(__arm__)
+ uint32_t register_DBGDIDR;
+
+ asm("mrc p14, 0, %0, c0, c0, 0" : "=r"(register_DBGDIDR));
+ uint32_t numWRPs = bits(register_DBGDIDR, 31, 28);
+ // Zero is reserved for the WRP count, so don't increment it if it is zero
+ if (numWRPs > 0)
+ numWRPs++;
+ g_num_supported_hw_watchpoints = numWRPs;
+ DNBLogThreadedIf(LOG_THREAD,
+ "Number of supported hw watchpoints via asm(): %d",
+ g_num_supported_hw_watchpoints);
+#endif
+ }
+ }
+ return g_num_supported_hw_watchpoints;
+}
+
+uint32_t DNBArchMachARM64::EnableHardwareWatchpoint(nub_addr_t addr,
+ nub_size_t size, bool read,
+ bool write,
+ bool also_set_on_task) {
+ DNBLogThreadedIf(LOG_WATCHPOINTS,
+ "DNBArchMachARM64::EnableHardwareWatchpoint(addr = "
+ "0x%8.8llx, size = %zu, read = %u, write = %u)",
+ (uint64_t)addr, size, read, write);
+
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+
+ // Can't watch zero bytes
+ if (size == 0)
+ return INVALID_NUB_HW_INDEX;
+
+ // We must watch for either read or write
+ if (read == false && write == false)
+ return INVALID_NUB_HW_INDEX;
+
+ // Otherwise, can't watch more than 8 bytes per WVR/WCR pair
+ if (size > 8)
+ return INVALID_NUB_HW_INDEX;
+
+ // Aarch64 watchpoints are in one of two forms: (1) 1-8 bytes, aligned to
+ // an 8 byte address, or (2) a power-of-two size region of memory; minimum
+ // 8 bytes, maximum 2GB; the starting address must be aligned to that power
+ // of two.
+ //
+ // For (1), 1-8 byte watchpoints, using the Byte Address Selector field in
+ // DBGWCR<n>.BAS. Any of the bytes may be watched, but if multiple bytes
+ // are watched, the bytes selected must be contiguous. The start address
+ // watched must be doubleword (8-byte) aligned; if the start address is
+ // word (4-byte) aligned, only 4 bytes can be watched.
+ //
+ // For (2), the MASK field in DBGWCR<n>.MASK is used.
+ //
+ // See the ARM ARM, section "Watchpoint exceptions", and more specifically,
+ // "Watchpoint data address comparisons".
+ //
+ // debugserver today only supports (1) - the Byte Address Selector 1-8 byte
+ // watchpoints that are 8-byte aligned. To support larger watchpoints,
+ // debugserver would need to interpret the mach exception when the watched
+ // region was hit, see if the address accessed lies within the subset
+ // of the power-of-two region that lldb asked us to watch (v. ARM ARM,
+ // "Determining the memory location that caused a Watchpoint exception"),
+ // and silently resume the inferior (disable watchpoint, stepi, re-enable
+ // watchpoint) if the address lies outside the region that lldb asked us
+ // to watch.
+ //
+ // Alternatively, lldb would need to be prepared for a larger region
+ // being watched than it requested, and silently resume the inferior if
+ // the accessed address is outside the region lldb wants to watch.
+
+ nub_addr_t aligned_wp_address = addr & ~0x7;
+ uint32_t addr_dword_offset = addr & 0x7;
+
+ // Do we need to split up this logical watchpoint into two hardware watchpoint
+ // registers?
+ // e.g. a watchpoint of length 4 on address 6. We need do this with
+ // one watchpoint on address 0 with bytes 6 & 7 being monitored
+ // one watchpoint on address 8 with bytes 0, 1, 2, 3 being monitored
+
+ if (addr_dword_offset + size > 8) {
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM64::"
+ "EnableHardwareWatchpoint(addr = "
+ "0x%8.8llx, size = %zu) needs two "
+ "hardware watchpoints slots to monitor",
+ (uint64_t)addr, size);
+ int low_watchpoint_size = 8 - addr_dword_offset;
+ int high_watchpoint_size = addr_dword_offset + size - 8;
+
+ uint32_t lo = EnableHardwareWatchpoint(addr, low_watchpoint_size, read,
+ write, also_set_on_task);
+ if (lo == INVALID_NUB_HW_INDEX)
+ return INVALID_NUB_HW_INDEX;
+ uint32_t hi =
+ EnableHardwareWatchpoint(aligned_wp_address + 8, high_watchpoint_size,
+ read, write, also_set_on_task);
+ if (hi == INVALID_NUB_HW_INDEX) {
+ DisableHardwareWatchpoint(lo, also_set_on_task);
+ return INVALID_NUB_HW_INDEX;
+ }
+ // Tag this lo->hi mapping in our database.
+ LoHi[lo] = hi;
+ return lo;
+ }
+
+ // At this point
+ // 1 aligned_wp_address is the requested address rounded down to 8-byte
+ // alignment
+ // 2 addr_dword_offset is the offset into that double word (8-byte) region
+ // that we are watching
+ // 3 size is the number of bytes within that 8-byte region that we are
+ // watching
+
+ // Set the Byte Address Selects bits DBGWCRn_EL1 bits [12:5] based on the
+ // above.
+ // The bit shift and negation operation will give us 0b11 for 2, 0b1111 for 4,
+ // etc, up to 0b11111111 for 8.
+ // then we shift those bits left by the offset into this dword that we are
+ // interested in.
+ // e.g. if we are watching bytes 4,5,6,7 in a dword we want a BAS of
+ // 0b11110000.
+ uint32_t byte_address_select = ((1 << size) - 1) << addr_dword_offset;
+
+ // Read the debug state
+ kern_return_t kret = GetDBGState(false);
+
+ if (kret == KERN_SUCCESS) {
+ // Check to make sure we have the needed hardware support
+ uint32_t i = 0;
+
+ for (i = 0; i < num_hw_watchpoints; ++i) {
+ if ((m_state.dbg.__wcr[i] & WCR_ENABLE) == 0)
+ break; // We found an available hw watchpoint slot (in i)
+ }
+
+ // See if we found an available hw watchpoint slot above
+ if (i < num_hw_watchpoints) {
+ // DumpDBGState(m_state.dbg);
+
+ // Clear any previous LoHi joined-watchpoint that may have been in use
+ LoHi[i] = 0;
+
+ // shift our Byte Address Select bits up to the correct bit range for the
+ // DBGWCRn_EL1
+ byte_address_select = byte_address_select << 5;
+
+ // Make sure bits 1:0 are clear in our address
+ m_state.dbg.__wvr[i] = aligned_wp_address; // DVA (Data Virtual Address)
+ m_state.dbg.__wcr[i] = byte_address_select | // Which bytes that follow
+ // the DVA that we will watch
+ S_USER | // Stop only in user mode
+ (read ? WCR_LOAD : 0) | // Stop on read access?
+ (write ? WCR_STORE : 0) | // Stop on write access?
+ WCR_ENABLE; // Enable this watchpoint;
+
+ DNBLogThreadedIf(
+ LOG_WATCHPOINTS, "DNBArchMachARM64::EnableHardwareWatchpoint() "
+ "adding watchpoint on address 0x%llx with control "
+ "register value 0x%x",
+ (uint64_t)m_state.dbg.__wvr[i], (uint32_t)m_state.dbg.__wcr[i]);
+
+ // The kernel will set the MDE_ENABLE bit in the MDSCR_EL1 for us
+ // automatically, don't need to do it here.
+
+ kret = SetDBGState(also_set_on_task);
+ // DumpDBGState(m_state.dbg);
+
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM64::"
+ "EnableHardwareWatchpoint() "
+ "SetDBGState() => 0x%8.8x.",
+ kret);
+
+ if (kret == KERN_SUCCESS)
+ return i;
+ } else {
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM64::"
+ "EnableHardwareWatchpoint(): All "
+ "hardware resources (%u) are in use.",
+ num_hw_watchpoints);
+ }
+ }
+ return INVALID_NUB_HW_INDEX;
+}
+
+bool DNBArchMachARM64::ReenableHardwareWatchpoint(uint32_t hw_index) {
+ // If this logical watchpoint # is actually implemented using
+ // two hardware watchpoint registers, re-enable both of them.
+
+ if (hw_index < NumSupportedHardwareWatchpoints() && LoHi[hw_index]) {
+ return ReenableHardwareWatchpoint_helper(hw_index) &&
+ ReenableHardwareWatchpoint_helper(LoHi[hw_index]);
+ } else {
+ return ReenableHardwareWatchpoint_helper(hw_index);
+ }
+}
+
+bool DNBArchMachARM64::ReenableHardwareWatchpoint_helper(uint32_t hw_index) {
+ kern_return_t kret = GetDBGState(false);
+ if (kret != KERN_SUCCESS)
+ return false;
+
+ const uint32_t num_hw_points = NumSupportedHardwareWatchpoints();
+ if (hw_index >= num_hw_points)
+ return false;
+
+ m_state.dbg.__wvr[hw_index] = m_disabled_watchpoints[hw_index].addr;
+ m_state.dbg.__wcr[hw_index] = m_disabled_watchpoints[hw_index].control;
+
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM64::"
+ "EnableHardwareWatchpoint( %u ) - WVR%u = "
+ "0x%8.8llx WCR%u = 0x%8.8llx",
+ hw_index, hw_index, (uint64_t)m_state.dbg.__wvr[hw_index],
+ hw_index, (uint64_t)m_state.dbg.__wcr[hw_index]);
+
+ // The kernel will set the MDE_ENABLE bit in the MDSCR_EL1 for us
+ // automatically, don't need to do it here.
+
+ kret = SetDBGState(false);
+
+ return (kret == KERN_SUCCESS);
+}
+
+bool DNBArchMachARM64::DisableHardwareWatchpoint(uint32_t hw_index,
+ bool also_set_on_task) {
+ if (hw_index < NumSupportedHardwareWatchpoints() && LoHi[hw_index]) {
+ return DisableHardwareWatchpoint_helper(hw_index, also_set_on_task) &&
+ DisableHardwareWatchpoint_helper(LoHi[hw_index], also_set_on_task);
+ } else {
+ return DisableHardwareWatchpoint_helper(hw_index, also_set_on_task);
+ }
+}
+
+bool DNBArchMachARM64::DisableHardwareWatchpoint_helper(uint32_t hw_index,
+ bool also_set_on_task) {
+ kern_return_t kret = GetDBGState(false);
+ if (kret != KERN_SUCCESS)
+ return false;
+
+ const uint32_t num_hw_points = NumSupportedHardwareWatchpoints();
+ if (hw_index >= num_hw_points)
+ return false;
+
+ m_disabled_watchpoints[hw_index].addr = m_state.dbg.__wvr[hw_index];
+ m_disabled_watchpoints[hw_index].control = m_state.dbg.__wcr[hw_index];
+
+ m_state.dbg.__wcr[hw_index] &= ~((nub_addr_t)WCR_ENABLE);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM64::"
+ "DisableHardwareWatchpoint( %u ) - WVR%u = "
+ "0x%8.8llx WCR%u = 0x%8.8llx",
+ hw_index, hw_index, (uint64_t)m_state.dbg.__wvr[hw_index],
+ hw_index, (uint64_t)m_state.dbg.__wcr[hw_index]);
+
+ kret = SetDBGState(also_set_on_task);
+
+ return (kret == KERN_SUCCESS);
+}
+
+// This is for checking the Byte Address Select bits in the DBRWCRn_EL1 control
+// register.
+// Returns -1 if the trailing bit patterns are not one of:
+// { 0b???????1, 0b??????10, 0b?????100, 0b????1000, 0b???10000, 0b??100000,
+// 0b?1000000, 0b10000000 }.
+static inline int32_t LowestBitSet(uint32_t val) {
+ for (unsigned i = 0; i < 8; ++i) {
+ if (bit(val, i))
+ return i;
+ }
+ return -1;
+}
+
+// Iterate through the debug registers; return the index of the first watchpoint
+// whose address matches.
+// As a side effect, the starting address as understood by the debugger is
+// returned which could be
+// different from 'addr' passed as an in/out argument.
+uint32_t DNBArchMachARM64::GetHardwareWatchpointHit(nub_addr_t &addr) {
+ // Read the debug state
+ kern_return_t kret = GetDBGState(true);
+ // DumpDBGState(m_state.dbg);
+ DNBLogThreadedIf(
+ LOG_WATCHPOINTS,
+ "DNBArchMachARM64::GetHardwareWatchpointHit() GetDBGState() => 0x%8.8x.",
+ kret);
+ DNBLogThreadedIf(LOG_WATCHPOINTS,
+ "DNBArchMachARM64::GetHardwareWatchpointHit() addr = 0x%llx",
+ (uint64_t)addr);
+
+ // This is the watchpoint value to match against, i.e., word address.
+ nub_addr_t wp_val = addr & ~((nub_addr_t)3);
+ if (kret == KERN_SUCCESS) {
+ DBG &debug_state = m_state.dbg;
+ uint32_t i, num = NumSupportedHardwareWatchpoints();
+ for (i = 0; i < num; ++i) {
+ nub_addr_t wp_addr = GetWatchAddress(debug_state, i);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchMachARM64::"
+ "GetHardwareWatchpointHit() slot: %u "
+ "(addr = 0x%llx).",
+ i, (uint64_t)wp_addr);
+ if (wp_val == wp_addr) {
+ uint32_t byte_mask = bits(debug_state.__wcr[i], 12, 5);
+
+ // Sanity check the byte_mask, first.
+ if (LowestBitSet(byte_mask) < 0)
+ continue;
+
+ // Check that the watchpoint is enabled.
+ if (!IsWatchpointEnabled(debug_state, i))
+ continue;
+
+ // Compute the starting address (from the point of view of the
+ // debugger).
+ addr = wp_addr + LowestBitSet(byte_mask);
+ return i;
+ }
+ }
+ }
+ return INVALID_NUB_HW_INDEX;
+}
+
+nub_addr_t DNBArchMachARM64::GetWatchpointAddressByIndex(uint32_t hw_index) {
+ kern_return_t kret = GetDBGState(true);
+ if (kret != KERN_SUCCESS)
+ return INVALID_NUB_ADDRESS;
+ const uint32_t num = NumSupportedHardwareWatchpoints();
+ if (hw_index >= num)
+ return INVALID_NUB_ADDRESS;
+ if (IsWatchpointEnabled(m_state.dbg, hw_index))
+ return GetWatchAddress(m_state.dbg, hw_index);
+ return INVALID_NUB_ADDRESS;
+}
+
+bool DNBArchMachARM64::IsWatchpointEnabled(const DBG &debug_state,
+ uint32_t hw_index) {
+ // Watchpoint Control Registers, bitfield definitions
+ // ...
+ // Bits Value Description
+ // [0] 0 Watchpoint disabled
+ // 1 Watchpoint enabled.
+ return (debug_state.__wcr[hw_index] & 1u);
+}
+
+nub_addr_t DNBArchMachARM64::GetWatchAddress(const DBG &debug_state,
+ uint32_t hw_index) {
+ // Watchpoint Value Registers, bitfield definitions
+ // Bits Description
+ // [31:2] Watchpoint value (word address, i.e., 4-byte aligned)
+ // [1:0] RAZ/SBZP
+ return bits(debug_state.__wvr[hw_index], 63, 0);
+}
+
+// Register information definitions for 64 bit ARMv8.
+enum gpr_regnums {
+ gpr_x0 = 0,
+ gpr_x1,
+ gpr_x2,
+ gpr_x3,
+ gpr_x4,
+ gpr_x5,
+ gpr_x6,
+ gpr_x7,
+ gpr_x8,
+ gpr_x9,
+ gpr_x10,
+ gpr_x11,
+ gpr_x12,
+ gpr_x13,
+ gpr_x14,
+ gpr_x15,
+ gpr_x16,
+ gpr_x17,
+ gpr_x18,
+ gpr_x19,
+ gpr_x20,
+ gpr_x21,
+ gpr_x22,
+ gpr_x23,
+ gpr_x24,
+ gpr_x25,
+ gpr_x26,
+ gpr_x27,
+ gpr_x28,
+ gpr_fp,
+ gpr_x29 = gpr_fp,
+ gpr_lr,
+ gpr_x30 = gpr_lr,
+ gpr_sp,
+ gpr_x31 = gpr_sp,
+ gpr_pc,
+ gpr_cpsr,
+ gpr_w0,
+ gpr_w1,
+ gpr_w2,
+ gpr_w3,
+ gpr_w4,
+ gpr_w5,
+ gpr_w6,
+ gpr_w7,
+ gpr_w8,
+ gpr_w9,
+ gpr_w10,
+ gpr_w11,
+ gpr_w12,
+ gpr_w13,
+ gpr_w14,
+ gpr_w15,
+ gpr_w16,
+ gpr_w17,
+ gpr_w18,
+ gpr_w19,
+ gpr_w20,
+ gpr_w21,
+ gpr_w22,
+ gpr_w23,
+ gpr_w24,
+ gpr_w25,
+ gpr_w26,
+ gpr_w27,
+ gpr_w28
+
+};
+
+enum {
+ vfp_v0 = 0,
+ vfp_v1,
+ vfp_v2,
+ vfp_v3,
+ vfp_v4,
+ vfp_v5,
+ vfp_v6,
+ vfp_v7,
+ vfp_v8,
+ vfp_v9,
+ vfp_v10,
+ vfp_v11,
+ vfp_v12,
+ vfp_v13,
+ vfp_v14,
+ vfp_v15,
+ vfp_v16,
+ vfp_v17,
+ vfp_v18,
+ vfp_v19,
+ vfp_v20,
+ vfp_v21,
+ vfp_v22,
+ vfp_v23,
+ vfp_v24,
+ vfp_v25,
+ vfp_v26,
+ vfp_v27,
+ vfp_v28,
+ vfp_v29,
+ vfp_v30,
+ vfp_v31,
+ vfp_fpsr,
+ vfp_fpcr,
+
+ // lower 32 bits of the corresponding vfp_v<n> reg.
+ vfp_s0,
+ vfp_s1,
+ vfp_s2,
+ vfp_s3,
+ vfp_s4,
+ vfp_s5,
+ vfp_s6,
+ vfp_s7,
+ vfp_s8,
+ vfp_s9,
+ vfp_s10,
+ vfp_s11,
+ vfp_s12,
+ vfp_s13,
+ vfp_s14,
+ vfp_s15,
+ vfp_s16,
+ vfp_s17,
+ vfp_s18,
+ vfp_s19,
+ vfp_s20,
+ vfp_s21,
+ vfp_s22,
+ vfp_s23,
+ vfp_s24,
+ vfp_s25,
+ vfp_s26,
+ vfp_s27,
+ vfp_s28,
+ vfp_s29,
+ vfp_s30,
+ vfp_s31,
+
+ // lower 64 bits of the corresponding vfp_v<n> reg.
+ vfp_d0,
+ vfp_d1,
+ vfp_d2,
+ vfp_d3,
+ vfp_d4,
+ vfp_d5,
+ vfp_d6,
+ vfp_d7,
+ vfp_d8,
+ vfp_d9,
+ vfp_d10,
+ vfp_d11,
+ vfp_d12,
+ vfp_d13,
+ vfp_d14,
+ vfp_d15,
+ vfp_d16,
+ vfp_d17,
+ vfp_d18,
+ vfp_d19,
+ vfp_d20,
+ vfp_d21,
+ vfp_d22,
+ vfp_d23,
+ vfp_d24,
+ vfp_d25,
+ vfp_d26,
+ vfp_d27,
+ vfp_d28,
+ vfp_d29,
+ vfp_d30,
+ vfp_d31
+};
+
+enum { exc_far = 0, exc_esr, exc_exception };
+
+// These numbers from the "DWARF for the ARM 64-bit Architecture (AArch64)"
+// document.
+
+enum {
+ dwarf_x0 = 0,
+ dwarf_x1,
+ dwarf_x2,
+ dwarf_x3,
+ dwarf_x4,
+ dwarf_x5,
+ dwarf_x6,
+ dwarf_x7,
+ dwarf_x8,
+ dwarf_x9,
+ dwarf_x10,
+ dwarf_x11,
+ dwarf_x12,
+ dwarf_x13,
+ dwarf_x14,
+ dwarf_x15,
+ dwarf_x16,
+ dwarf_x17,
+ dwarf_x18,
+ dwarf_x19,
+ dwarf_x20,
+ dwarf_x21,
+ dwarf_x22,
+ dwarf_x23,
+ dwarf_x24,
+ dwarf_x25,
+ dwarf_x26,
+ dwarf_x27,
+ dwarf_x28,
+ dwarf_x29,
+ dwarf_x30,
+ dwarf_x31,
+ dwarf_pc = 32,
+ dwarf_elr_mode = 33,
+ dwarf_fp = dwarf_x29,
+ dwarf_lr = dwarf_x30,
+ dwarf_sp = dwarf_x31,
+ // 34-63 reserved
+
+ // V0-V31 (128 bit vector registers)
+ dwarf_v0 = 64,
+ dwarf_v1,
+ dwarf_v2,
+ dwarf_v3,
+ dwarf_v4,
+ dwarf_v5,
+ dwarf_v6,
+ dwarf_v7,
+ dwarf_v8,
+ dwarf_v9,
+ dwarf_v10,
+ dwarf_v11,
+ dwarf_v12,
+ dwarf_v13,
+ dwarf_v14,
+ dwarf_v15,
+ dwarf_v16,
+ dwarf_v17,
+ dwarf_v18,
+ dwarf_v19,
+ dwarf_v20,
+ dwarf_v21,
+ dwarf_v22,
+ dwarf_v23,
+ dwarf_v24,
+ dwarf_v25,
+ dwarf_v26,
+ dwarf_v27,
+ dwarf_v28,
+ dwarf_v29,
+ dwarf_v30,
+ dwarf_v31
+
+ // 96-127 reserved
+};
+
+enum {
+ debugserver_gpr_x0 = 0,
+ debugserver_gpr_x1,
+ debugserver_gpr_x2,
+ debugserver_gpr_x3,
+ debugserver_gpr_x4,
+ debugserver_gpr_x5,
+ debugserver_gpr_x6,
+ debugserver_gpr_x7,
+ debugserver_gpr_x8,
+ debugserver_gpr_x9,
+ debugserver_gpr_x10,
+ debugserver_gpr_x11,
+ debugserver_gpr_x12,
+ debugserver_gpr_x13,
+ debugserver_gpr_x14,
+ debugserver_gpr_x15,
+ debugserver_gpr_x16,
+ debugserver_gpr_x17,
+ debugserver_gpr_x18,
+ debugserver_gpr_x19,
+ debugserver_gpr_x20,
+ debugserver_gpr_x21,
+ debugserver_gpr_x22,
+ debugserver_gpr_x23,
+ debugserver_gpr_x24,
+ debugserver_gpr_x25,
+ debugserver_gpr_x26,
+ debugserver_gpr_x27,
+ debugserver_gpr_x28,
+ debugserver_gpr_fp, // x29
+ debugserver_gpr_lr, // x30
+ debugserver_gpr_sp, // sp aka xsp
+ debugserver_gpr_pc,
+ debugserver_gpr_cpsr,
+ debugserver_vfp_v0,
+ debugserver_vfp_v1,
+ debugserver_vfp_v2,
+ debugserver_vfp_v3,
+ debugserver_vfp_v4,
+ debugserver_vfp_v5,
+ debugserver_vfp_v6,
+ debugserver_vfp_v7,
+ debugserver_vfp_v8,
+ debugserver_vfp_v9,
+ debugserver_vfp_v10,
+ debugserver_vfp_v11,
+ debugserver_vfp_v12,
+ debugserver_vfp_v13,
+ debugserver_vfp_v14,
+ debugserver_vfp_v15,
+ debugserver_vfp_v16,
+ debugserver_vfp_v17,
+ debugserver_vfp_v18,
+ debugserver_vfp_v19,
+ debugserver_vfp_v20,
+ debugserver_vfp_v21,
+ debugserver_vfp_v22,
+ debugserver_vfp_v23,
+ debugserver_vfp_v24,
+ debugserver_vfp_v25,
+ debugserver_vfp_v26,
+ debugserver_vfp_v27,
+ debugserver_vfp_v28,
+ debugserver_vfp_v29,
+ debugserver_vfp_v30,
+ debugserver_vfp_v31,
+ debugserver_vfp_fpsr,
+ debugserver_vfp_fpcr
+};
+
+const char *g_contained_x0[]{"x0", NULL};
+const char *g_contained_x1[]{"x1", NULL};
+const char *g_contained_x2[]{"x2", NULL};
+const char *g_contained_x3[]{"x3", NULL};
+const char *g_contained_x4[]{"x4", NULL};
+const char *g_contained_x5[]{"x5", NULL};
+const char *g_contained_x6[]{"x6", NULL};
+const char *g_contained_x7[]{"x7", NULL};
+const char *g_contained_x8[]{"x8", NULL};
+const char *g_contained_x9[]{"x9", NULL};
+const char *g_contained_x10[]{"x10", NULL};
+const char *g_contained_x11[]{"x11", NULL};
+const char *g_contained_x12[]{"x12", NULL};
+const char *g_contained_x13[]{"x13", NULL};
+const char *g_contained_x14[]{"x14", NULL};
+const char *g_contained_x15[]{"x15", NULL};
+const char *g_contained_x16[]{"x16", NULL};
+const char *g_contained_x17[]{"x17", NULL};
+const char *g_contained_x18[]{"x18", NULL};
+const char *g_contained_x19[]{"x19", NULL};
+const char *g_contained_x20[]{"x20", NULL};
+const char *g_contained_x21[]{"x21", NULL};
+const char *g_contained_x22[]{"x22", NULL};
+const char *g_contained_x23[]{"x23", NULL};
+const char *g_contained_x24[]{"x24", NULL};
+const char *g_contained_x25[]{"x25", NULL};
+const char *g_contained_x26[]{"x26", NULL};
+const char *g_contained_x27[]{"x27", NULL};
+const char *g_contained_x28[]{"x28", NULL};
+
+const char *g_invalidate_x0[]{"x0", "w0", NULL};
+const char *g_invalidate_x1[]{"x1", "w1", NULL};
+const char *g_invalidate_x2[]{"x2", "w2", NULL};
+const char *g_invalidate_x3[]{"x3", "w3", NULL};
+const char *g_invalidate_x4[]{"x4", "w4", NULL};
+const char *g_invalidate_x5[]{"x5", "w5", NULL};
+const char *g_invalidate_x6[]{"x6", "w6", NULL};
+const char *g_invalidate_x7[]{"x7", "w7", NULL};
+const char *g_invalidate_x8[]{"x8", "w8", NULL};
+const char *g_invalidate_x9[]{"x9", "w9", NULL};
+const char *g_invalidate_x10[]{"x10", "w10", NULL};
+const char *g_invalidate_x11[]{"x11", "w11", NULL};
+const char *g_invalidate_x12[]{"x12", "w12", NULL};
+const char *g_invalidate_x13[]{"x13", "w13", NULL};
+const char *g_invalidate_x14[]{"x14", "w14", NULL};
+const char *g_invalidate_x15[]{"x15", "w15", NULL};
+const char *g_invalidate_x16[]{"x16", "w16", NULL};
+const char *g_invalidate_x17[]{"x17", "w17", NULL};
+const char *g_invalidate_x18[]{"x18", "w18", NULL};
+const char *g_invalidate_x19[]{"x19", "w19", NULL};
+const char *g_invalidate_x20[]{"x20", "w20", NULL};
+const char *g_invalidate_x21[]{"x21", "w21", NULL};
+const char *g_invalidate_x22[]{"x22", "w22", NULL};
+const char *g_invalidate_x23[]{"x23", "w23", NULL};
+const char *g_invalidate_x24[]{"x24", "w24", NULL};
+const char *g_invalidate_x25[]{"x25", "w25", NULL};
+const char *g_invalidate_x26[]{"x26", "w26", NULL};
+const char *g_invalidate_x27[]{"x27", "w27", NULL};
+const char *g_invalidate_x28[]{"x28", "w28", NULL};
+
+#define GPR_OFFSET_IDX(idx) (offsetof(DNBArchMachARM64::GPR, __x[idx]))
+
+#define GPR_OFFSET_NAME(reg) (offsetof(DNBArchMachARM64::GPR, __##reg))
+
+// These macros will auto define the register name, alt name, register size,
+// register offset, encoding, format and native register. This ensures that
+// the register state structures are defined correctly and have the correct
+// sizes and offsets.
+#define DEFINE_GPR_IDX(idx, reg, alt, gen) \
+ { \
+ e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, 8, GPR_OFFSET_IDX(idx), \
+ dwarf_##reg, dwarf_##reg, gen, debugserver_gpr_##reg, NULL, \
+ g_invalidate_x##idx \
+ }
+#define DEFINE_GPR_NAME(reg, alt, gen) \
+ { \
+ e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, 8, GPR_OFFSET_NAME(reg), \
+ dwarf_##reg, dwarf_##reg, gen, debugserver_gpr_##reg, NULL, NULL \
+ }
+#define DEFINE_PSEUDO_GPR_IDX(idx, reg) \
+ { \
+ e_regSetGPR, gpr_##reg, #reg, NULL, Uint, Hex, 4, 0, INVALID_NUB_REGNUM, \
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, \
+ g_contained_x##idx, g_invalidate_x##idx \
+ }
+
+//_STRUCT_ARM_THREAD_STATE64
+//{
+// uint64_t x[29]; /* General purpose registers x0-x28 */
+// uint64_t fp; /* Frame pointer x29 */
+// uint64_t lr; /* Link register x30 */
+// uint64_t sp; /* Stack pointer x31 */
+// uint64_t pc; /* Program counter */
+// uint32_t cpsr; /* Current program status register */
+//};
+
+// General purpose registers
+const DNBRegisterInfo DNBArchMachARM64::g_gpr_registers[] = {
+ DEFINE_GPR_IDX(0, x0, "arg1", GENERIC_REGNUM_ARG1),
+ DEFINE_GPR_IDX(1, x1, "arg2", GENERIC_REGNUM_ARG2),
+ DEFINE_GPR_IDX(2, x2, "arg3", GENERIC_REGNUM_ARG3),
+ DEFINE_GPR_IDX(3, x3, "arg4", GENERIC_REGNUM_ARG4),
+ DEFINE_GPR_IDX(4, x4, "arg5", GENERIC_REGNUM_ARG5),
+ DEFINE_GPR_IDX(5, x5, "arg6", GENERIC_REGNUM_ARG6),
+ DEFINE_GPR_IDX(6, x6, "arg7", GENERIC_REGNUM_ARG7),
+ DEFINE_GPR_IDX(7, x7, "arg8", GENERIC_REGNUM_ARG8),
+ DEFINE_GPR_IDX(8, x8, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(9, x9, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(10, x10, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(11, x11, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(12, x12, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(13, x13, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(14, x14, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(15, x15, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(16, x16, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(17, x17, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(18, x18, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(19, x19, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(20, x20, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(21, x21, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(22, x22, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(23, x23, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(24, x24, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(25, x25, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(26, x26, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(27, x27, NULL, INVALID_NUB_REGNUM),
+ DEFINE_GPR_IDX(28, x28, NULL, INVALID_NUB_REGNUM),
+ // For the G/g packet we want to show where the offset into the regctx
+ // is for fp/lr/sp/pc, but we cannot directly access them on arm64e
+ // devices (and therefore can't offsetof() them)) - add the offset based
+ // on the last accessible register by hand for advertising the location
+ // in the regctx to lldb. We'll go through the accessor functions when
+ // we read/write them here.
+ {
+ e_regSetGPR, gpr_fp, "fp", "x29", Uint, Hex, 8, GPR_OFFSET_IDX(28) + 8,
+ dwarf_fp, dwarf_fp, GENERIC_REGNUM_FP, debugserver_gpr_fp, NULL, NULL
+ },
+ {
+ e_regSetGPR, gpr_lr, "lr", "x30", Uint, Hex, 8, GPR_OFFSET_IDX(28) + 16,
+ dwarf_lr, dwarf_lr, GENERIC_REGNUM_RA, debugserver_gpr_lr, NULL, NULL
+ },
+ {
+ e_regSetGPR, gpr_sp, "sp", "xsp", Uint, Hex, 8, GPR_OFFSET_IDX(28) + 24,
+ dwarf_sp, dwarf_sp, GENERIC_REGNUM_SP, debugserver_gpr_sp, NULL, NULL
+ },
+ {
+ e_regSetGPR, gpr_pc, "pc", NULL, Uint, Hex, 8, GPR_OFFSET_IDX(28) + 32,
+ dwarf_pc, dwarf_pc, GENERIC_REGNUM_PC, debugserver_gpr_pc, NULL, NULL
+ },
+
+ // in armv7 we specify that writing to the CPSR should invalidate r8-12, sp,
+ // lr.
+ // this should be specified for arm64 too even though debugserver is only
+ // used for
+ // userland debugging.
+ {e_regSetGPR, gpr_cpsr, "cpsr", "flags", Uint, Hex, 4,
+ GPR_OFFSET_NAME(cpsr), dwarf_elr_mode, dwarf_elr_mode, INVALID_NUB_REGNUM,
+ debugserver_gpr_cpsr, NULL, NULL},
+
+ DEFINE_PSEUDO_GPR_IDX(0, w0),
+ DEFINE_PSEUDO_GPR_IDX(1, w1),
+ DEFINE_PSEUDO_GPR_IDX(2, w2),
+ DEFINE_PSEUDO_GPR_IDX(3, w3),
+ DEFINE_PSEUDO_GPR_IDX(4, w4),
+ DEFINE_PSEUDO_GPR_IDX(5, w5),
+ DEFINE_PSEUDO_GPR_IDX(6, w6),
+ DEFINE_PSEUDO_GPR_IDX(7, w7),
+ DEFINE_PSEUDO_GPR_IDX(8, w8),
+ DEFINE_PSEUDO_GPR_IDX(9, w9),
+ DEFINE_PSEUDO_GPR_IDX(10, w10),
+ DEFINE_PSEUDO_GPR_IDX(11, w11),
+ DEFINE_PSEUDO_GPR_IDX(12, w12),
+ DEFINE_PSEUDO_GPR_IDX(13, w13),
+ DEFINE_PSEUDO_GPR_IDX(14, w14),
+ DEFINE_PSEUDO_GPR_IDX(15, w15),
+ DEFINE_PSEUDO_GPR_IDX(16, w16),
+ DEFINE_PSEUDO_GPR_IDX(17, w17),
+ DEFINE_PSEUDO_GPR_IDX(18, w18),
+ DEFINE_PSEUDO_GPR_IDX(19, w19),
+ DEFINE_PSEUDO_GPR_IDX(20, w20),
+ DEFINE_PSEUDO_GPR_IDX(21, w21),
+ DEFINE_PSEUDO_GPR_IDX(22, w22),
+ DEFINE_PSEUDO_GPR_IDX(23, w23),
+ DEFINE_PSEUDO_GPR_IDX(24, w24),
+ DEFINE_PSEUDO_GPR_IDX(25, w25),
+ DEFINE_PSEUDO_GPR_IDX(26, w26),
+ DEFINE_PSEUDO_GPR_IDX(27, w27),
+ DEFINE_PSEUDO_GPR_IDX(28, w28)};
+
+const char *g_contained_v0[]{"v0", NULL};
+const char *g_contained_v1[]{"v1", NULL};
+const char *g_contained_v2[]{"v2", NULL};
+const char *g_contained_v3[]{"v3", NULL};
+const char *g_contained_v4[]{"v4", NULL};
+const char *g_contained_v5[]{"v5", NULL};
+const char *g_contained_v6[]{"v6", NULL};
+const char *g_contained_v7[]{"v7", NULL};
+const char *g_contained_v8[]{"v8", NULL};
+const char *g_contained_v9[]{"v9", NULL};
+const char *g_contained_v10[]{"v10", NULL};
+const char *g_contained_v11[]{"v11", NULL};
+const char *g_contained_v12[]{"v12", NULL};
+const char *g_contained_v13[]{"v13", NULL};
+const char *g_contained_v14[]{"v14", NULL};
+const char *g_contained_v15[]{"v15", NULL};
+const char *g_contained_v16[]{"v16", NULL};
+const char *g_contained_v17[]{"v17", NULL};
+const char *g_contained_v18[]{"v18", NULL};
+const char *g_contained_v19[]{"v19", NULL};
+const char *g_contained_v20[]{"v20", NULL};
+const char *g_contained_v21[]{"v21", NULL};
+const char *g_contained_v22[]{"v22", NULL};
+const char *g_contained_v23[]{"v23", NULL};
+const char *g_contained_v24[]{"v24", NULL};
+const char *g_contained_v25[]{"v25", NULL};
+const char *g_contained_v26[]{"v26", NULL};
+const char *g_contained_v27[]{"v27", NULL};
+const char *g_contained_v28[]{"v28", NULL};
+const char *g_contained_v29[]{"v29", NULL};
+const char *g_contained_v30[]{"v30", NULL};
+const char *g_contained_v31[]{"v31", NULL};
+
+const char *g_invalidate_v0[]{"v0", "d0", "s0", NULL};
+const char *g_invalidate_v1[]{"v1", "d1", "s1", NULL};
+const char *g_invalidate_v2[]{"v2", "d2", "s2", NULL};
+const char *g_invalidate_v3[]{"v3", "d3", "s3", NULL};
+const char *g_invalidate_v4[]{"v4", "d4", "s4", NULL};
+const char *g_invalidate_v5[]{"v5", "d5", "s5", NULL};
+const char *g_invalidate_v6[]{"v6", "d6", "s6", NULL};
+const char *g_invalidate_v7[]{"v7", "d7", "s7", NULL};
+const char *g_invalidate_v8[]{"v8", "d8", "s8", NULL};
+const char *g_invalidate_v9[]{"v9", "d9", "s9", NULL};
+const char *g_invalidate_v10[]{"v10", "d10", "s10", NULL};
+const char *g_invalidate_v11[]{"v11", "d11", "s11", NULL};
+const char *g_invalidate_v12[]{"v12", "d12", "s12", NULL};
+const char *g_invalidate_v13[]{"v13", "d13", "s13", NULL};
+const char *g_invalidate_v14[]{"v14", "d14", "s14", NULL};
+const char *g_invalidate_v15[]{"v15", "d15", "s15", NULL};
+const char *g_invalidate_v16[]{"v16", "d16", "s16", NULL};
+const char *g_invalidate_v17[]{"v17", "d17", "s17", NULL};
+const char *g_invalidate_v18[]{"v18", "d18", "s18", NULL};
+const char *g_invalidate_v19[]{"v19", "d19", "s19", NULL};
+const char *g_invalidate_v20[]{"v20", "d20", "s20", NULL};
+const char *g_invalidate_v21[]{"v21", "d21", "s21", NULL};
+const char *g_invalidate_v22[]{"v22", "d22", "s22", NULL};
+const char *g_invalidate_v23[]{"v23", "d23", "s23", NULL};
+const char *g_invalidate_v24[]{"v24", "d24", "s24", NULL};
+const char *g_invalidate_v25[]{"v25", "d25", "s25", NULL};
+const char *g_invalidate_v26[]{"v26", "d26", "s26", NULL};
+const char *g_invalidate_v27[]{"v27", "d27", "s27", NULL};
+const char *g_invalidate_v28[]{"v28", "d28", "s28", NULL};
+const char *g_invalidate_v29[]{"v29", "d29", "s29", NULL};
+const char *g_invalidate_v30[]{"v30", "d30", "s30", NULL};
+const char *g_invalidate_v31[]{"v31", "d31", "s31", NULL};
+
+#if defined(__arm64__) || defined(__aarch64__)
+#define VFP_V_OFFSET_IDX(idx) \
+ (offsetof(DNBArchMachARM64::FPU, __v) + (idx * 16) + \
+ offsetof(DNBArchMachARM64::Context, vfp))
+#else
+#define VFP_V_OFFSET_IDX(idx) \
+ (offsetof(DNBArchMachARM64::FPU, opaque) + (idx * 16) + \
+ offsetof(DNBArchMachARM64::Context, vfp))
+#endif
+#define VFP_OFFSET_NAME(reg) \
+ (offsetof(DNBArchMachARM64::FPU, reg) + \
+ offsetof(DNBArchMachARM64::Context, vfp))
+#define EXC_OFFSET(reg) \
+ (offsetof(DNBArchMachARM64::EXC, reg) + \
+ offsetof(DNBArchMachARM64::Context, exc))
+
+//#define FLOAT_FORMAT Float
+#define DEFINE_VFP_V_IDX(idx) \
+ { \
+ e_regSetVFP, vfp_v##idx, "v" #idx, "q" #idx, Vector, VectorOfUInt8, 16, \
+ VFP_V_OFFSET_IDX(idx), INVALID_NUB_REGNUM, dwarf_v##idx, \
+ INVALID_NUB_REGNUM, debugserver_vfp_v##idx, NULL, g_invalidate_v##idx \
+ }
+#define DEFINE_PSEUDO_VFP_S_IDX(idx) \
+ { \
+ e_regSetVFP, vfp_s##idx, "s" #idx, NULL, IEEE754, Float, 4, 0, \
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, \
+ INVALID_NUB_REGNUM, g_contained_v##idx, g_invalidate_v##idx \
+ }
+#define DEFINE_PSEUDO_VFP_D_IDX(idx) \
+ { \
+ e_regSetVFP, vfp_d##idx, "d" #idx, NULL, IEEE754, Float, 8, 0, \
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, \
+ INVALID_NUB_REGNUM, g_contained_v##idx, g_invalidate_v##idx \
+ }
+
+// Floating point registers
+const DNBRegisterInfo DNBArchMachARM64::g_vfp_registers[] = {
+ DEFINE_VFP_V_IDX(0),
+ DEFINE_VFP_V_IDX(1),
+ DEFINE_VFP_V_IDX(2),
+ DEFINE_VFP_V_IDX(3),
+ DEFINE_VFP_V_IDX(4),
+ DEFINE_VFP_V_IDX(5),
+ DEFINE_VFP_V_IDX(6),
+ DEFINE_VFP_V_IDX(7),
+ DEFINE_VFP_V_IDX(8),
+ DEFINE_VFP_V_IDX(9),
+ DEFINE_VFP_V_IDX(10),
+ DEFINE_VFP_V_IDX(11),
+ DEFINE_VFP_V_IDX(12),
+ DEFINE_VFP_V_IDX(13),
+ DEFINE_VFP_V_IDX(14),
+ DEFINE_VFP_V_IDX(15),
+ DEFINE_VFP_V_IDX(16),
+ DEFINE_VFP_V_IDX(17),
+ DEFINE_VFP_V_IDX(18),
+ DEFINE_VFP_V_IDX(19),
+ DEFINE_VFP_V_IDX(20),
+ DEFINE_VFP_V_IDX(21),
+ DEFINE_VFP_V_IDX(22),
+ DEFINE_VFP_V_IDX(23),
+ DEFINE_VFP_V_IDX(24),
+ DEFINE_VFP_V_IDX(25),
+ DEFINE_VFP_V_IDX(26),
+ DEFINE_VFP_V_IDX(27),
+ DEFINE_VFP_V_IDX(28),
+ DEFINE_VFP_V_IDX(29),
+ DEFINE_VFP_V_IDX(30),
+ DEFINE_VFP_V_IDX(31),
+ {e_regSetVFP, vfp_fpsr, "fpsr", NULL, Uint, Hex, 4,
+ VFP_V_OFFSET_IDX(32) + 0, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetVFP, vfp_fpcr, "fpcr", NULL, Uint, Hex, 4,
+ VFP_V_OFFSET_IDX(32) + 4, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL},
+
+ DEFINE_PSEUDO_VFP_S_IDX(0),
+ DEFINE_PSEUDO_VFP_S_IDX(1),
+ DEFINE_PSEUDO_VFP_S_IDX(2),
+ DEFINE_PSEUDO_VFP_S_IDX(3),
+ DEFINE_PSEUDO_VFP_S_IDX(4),
+ DEFINE_PSEUDO_VFP_S_IDX(5),
+ DEFINE_PSEUDO_VFP_S_IDX(6),
+ DEFINE_PSEUDO_VFP_S_IDX(7),
+ DEFINE_PSEUDO_VFP_S_IDX(8),
+ DEFINE_PSEUDO_VFP_S_IDX(9),
+ DEFINE_PSEUDO_VFP_S_IDX(10),
+ DEFINE_PSEUDO_VFP_S_IDX(11),
+ DEFINE_PSEUDO_VFP_S_IDX(12),
+ DEFINE_PSEUDO_VFP_S_IDX(13),
+ DEFINE_PSEUDO_VFP_S_IDX(14),
+ DEFINE_PSEUDO_VFP_S_IDX(15),
+ DEFINE_PSEUDO_VFP_S_IDX(16),
+ DEFINE_PSEUDO_VFP_S_IDX(17),
+ DEFINE_PSEUDO_VFP_S_IDX(18),
+ DEFINE_PSEUDO_VFP_S_IDX(19),
+ DEFINE_PSEUDO_VFP_S_IDX(20),
+ DEFINE_PSEUDO_VFP_S_IDX(21),
+ DEFINE_PSEUDO_VFP_S_IDX(22),
+ DEFINE_PSEUDO_VFP_S_IDX(23),
+ DEFINE_PSEUDO_VFP_S_IDX(24),
+ DEFINE_PSEUDO_VFP_S_IDX(25),
+ DEFINE_PSEUDO_VFP_S_IDX(26),
+ DEFINE_PSEUDO_VFP_S_IDX(27),
+ DEFINE_PSEUDO_VFP_S_IDX(28),
+ DEFINE_PSEUDO_VFP_S_IDX(29),
+ DEFINE_PSEUDO_VFP_S_IDX(30),
+ DEFINE_PSEUDO_VFP_S_IDX(31),
+
+ DEFINE_PSEUDO_VFP_D_IDX(0),
+ DEFINE_PSEUDO_VFP_D_IDX(1),
+ DEFINE_PSEUDO_VFP_D_IDX(2),
+ DEFINE_PSEUDO_VFP_D_IDX(3),
+ DEFINE_PSEUDO_VFP_D_IDX(4),
+ DEFINE_PSEUDO_VFP_D_IDX(5),
+ DEFINE_PSEUDO_VFP_D_IDX(6),
+ DEFINE_PSEUDO_VFP_D_IDX(7),
+ DEFINE_PSEUDO_VFP_D_IDX(8),
+ DEFINE_PSEUDO_VFP_D_IDX(9),
+ DEFINE_PSEUDO_VFP_D_IDX(10),
+ DEFINE_PSEUDO_VFP_D_IDX(11),
+ DEFINE_PSEUDO_VFP_D_IDX(12),
+ DEFINE_PSEUDO_VFP_D_IDX(13),
+ DEFINE_PSEUDO_VFP_D_IDX(14),
+ DEFINE_PSEUDO_VFP_D_IDX(15),
+ DEFINE_PSEUDO_VFP_D_IDX(16),
+ DEFINE_PSEUDO_VFP_D_IDX(17),
+ DEFINE_PSEUDO_VFP_D_IDX(18),
+ DEFINE_PSEUDO_VFP_D_IDX(19),
+ DEFINE_PSEUDO_VFP_D_IDX(20),
+ DEFINE_PSEUDO_VFP_D_IDX(21),
+ DEFINE_PSEUDO_VFP_D_IDX(22),
+ DEFINE_PSEUDO_VFP_D_IDX(23),
+ DEFINE_PSEUDO_VFP_D_IDX(24),
+ DEFINE_PSEUDO_VFP_D_IDX(25),
+ DEFINE_PSEUDO_VFP_D_IDX(26),
+ DEFINE_PSEUDO_VFP_D_IDX(27),
+ DEFINE_PSEUDO_VFP_D_IDX(28),
+ DEFINE_PSEUDO_VFP_D_IDX(29),
+ DEFINE_PSEUDO_VFP_D_IDX(30),
+ DEFINE_PSEUDO_VFP_D_IDX(31)
+
+};
+
+//_STRUCT_ARM_EXCEPTION_STATE64
+//{
+// uint64_t far; /* Virtual Fault Address */
+// uint32_t esr; /* Exception syndrome */
+// uint32_t exception; /* number of arm exception taken */
+//};
+
+// Exception registers
+const DNBRegisterInfo DNBArchMachARM64::g_exc_registers[] = {
+ {e_regSetEXC, exc_far, "far", NULL, Uint, Hex, 8, EXC_OFFSET(__far),
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetEXC, exc_esr, "esr", NULL, Uint, Hex, 4, EXC_OFFSET(__esr),
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetEXC, exc_exception, "exception", NULL, Uint, Hex, 4,
+ EXC_OFFSET(__exception), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL}};
+
+// Number of registers in each register set
+const size_t DNBArchMachARM64::k_num_gpr_registers =
+ sizeof(g_gpr_registers) / sizeof(DNBRegisterInfo);
+const size_t DNBArchMachARM64::k_num_vfp_registers =
+ sizeof(g_vfp_registers) / sizeof(DNBRegisterInfo);
+const size_t DNBArchMachARM64::k_num_exc_registers =
+ sizeof(g_exc_registers) / sizeof(DNBRegisterInfo);
+const size_t DNBArchMachARM64::k_num_all_registers =
+ k_num_gpr_registers + k_num_vfp_registers + k_num_exc_registers;
+
+// Register set definitions. The first definitions at register set index
+// of zero is for all registers, followed by other registers sets. The
+// register information for the all register set need not be filled in.
+const DNBRegisterSetInfo DNBArchMachARM64::g_reg_sets[] = {
+ {"ARM64 Registers", NULL, k_num_all_registers},
+ {"General Purpose Registers", g_gpr_registers, k_num_gpr_registers},
+ {"Floating Point Registers", g_vfp_registers, k_num_vfp_registers},
+ {"Exception State Registers", g_exc_registers, k_num_exc_registers}};
+// Total number of register sets for this architecture
+const size_t DNBArchMachARM64::k_num_register_sets =
+ sizeof(g_reg_sets) / sizeof(DNBRegisterSetInfo);
+
+const DNBRegisterSetInfo *
+DNBArchMachARM64::GetRegisterSetInfo(nub_size_t *num_reg_sets) {
+ *num_reg_sets = k_num_register_sets;
+ return g_reg_sets;
+}
+
+bool DNBArchMachARM64::FixGenericRegisterNumber(uint32_t &set, uint32_t &reg) {
+ if (set == REGISTER_SET_GENERIC) {
+ switch (reg) {
+ case GENERIC_REGNUM_PC: // Program Counter
+ set = e_regSetGPR;
+ reg = gpr_pc;
+ break;
+
+ case GENERIC_REGNUM_SP: // Stack Pointer
+ set = e_regSetGPR;
+ reg = gpr_sp;
+ break;
+
+ case GENERIC_REGNUM_FP: // Frame Pointer
+ set = e_regSetGPR;
+ reg = gpr_fp;
+ break;
+
+ case GENERIC_REGNUM_RA: // Return Address
+ set = e_regSetGPR;
+ reg = gpr_lr;
+ break;
+
+ case GENERIC_REGNUM_FLAGS: // Processor flags register
+ set = e_regSetGPR;
+ reg = gpr_cpsr;
+ break;
+
+ case GENERIC_REGNUM_ARG1:
+ case GENERIC_REGNUM_ARG2:
+ case GENERIC_REGNUM_ARG3:
+ case GENERIC_REGNUM_ARG4:
+ case GENERIC_REGNUM_ARG5:
+ case GENERIC_REGNUM_ARG6:
+ set = e_regSetGPR;
+ reg = gpr_x0 + reg - GENERIC_REGNUM_ARG1;
+ break;
+
+ default:
+ return false;
+ }
+ }
+ return true;
+}
+bool DNBArchMachARM64::GetRegisterValue(uint32_t set, uint32_t reg,
+ DNBRegisterValue *value) {
+ if (!FixGenericRegisterNumber(set, reg))
+ return false;
+
+ if (GetRegisterState(set, false) != KERN_SUCCESS)
+ return false;
+
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo) {
+ value->info = *regInfo;
+ switch (set) {
+ case e_regSetGPR:
+ if (reg <= gpr_pc) {
+#if defined(__LP64__)
+ if (reg == gpr_pc)
+ value->value.uint64 = arm_thread_state64_get_pc (m_state.context.gpr);
+ else if (reg == gpr_lr)
+ value->value.uint64 = arm_thread_state64_get_lr (m_state.context.gpr);
+ else if (reg == gpr_sp)
+ value->value.uint64 = arm_thread_state64_get_sp (m_state.context.gpr);
+ else if (reg == gpr_fp)
+ value->value.uint64 = arm_thread_state64_get_fp (m_state.context.gpr);
+ else
+ value->value.uint64 = m_state.context.gpr.__x[reg];
+#else
+ value->value.uint64 = m_state.context.gpr.__x[reg];
+#endif
+ return true;
+ } else if (reg == gpr_cpsr) {
+ value->value.uint32 = m_state.context.gpr.__cpsr;
+ return true;
+ }
+ break;
+
+ case e_regSetVFP:
+
+ if (reg >= vfp_v0 && reg <= vfp_v31) {
+#if defined(__arm64__) || defined(__aarch64__)
+ memcpy(&value->value.v_uint8, &m_state.context.vfp.__v[reg - vfp_v0],
+ 16);
+#else
+ memcpy(&value->value.v_uint8,
+ ((uint8_t *)&m_state.context.vfp.opaque) + ((reg - vfp_v0) * 16),
+ 16);
+#endif
+ return true;
+ } else if (reg == vfp_fpsr) {
+#if defined(__arm64__) || defined(__aarch64__)
+ memcpy(&value->value.uint32, &m_state.context.vfp.__fpsr, 4);
+#else
+ memcpy(&value->value.uint32,
+ ((uint8_t *)&m_state.context.vfp.opaque) + (32 * 16) + 0, 4);
+#endif
+ return true;
+ } else if (reg == vfp_fpcr) {
+#if defined(__arm64__) || defined(__aarch64__)
+ memcpy(&value->value.uint32, &m_state.context.vfp.__fpcr, 4);
+#else
+ memcpy(&value->value.uint32,
+ ((uint8_t *)&m_state.context.vfp.opaque) + (32 * 16) + 4, 4);
+#endif
+ return true;
+ } else if (reg >= vfp_s0 && reg <= vfp_s31) {
+#if defined(__arm64__) || defined(__aarch64__)
+ memcpy(&value->value.v_uint8, &m_state.context.vfp.__v[reg - vfp_s0],
+ 4);
+#else
+ memcpy(&value->value.v_uint8,
+ ((uint8_t *)&m_state.context.vfp.opaque) + ((reg - vfp_s0) * 16),
+ 4);
+#endif
+ return true;
+ } else if (reg >= vfp_d0 && reg <= vfp_d31) {
+#if defined(__arm64__) || defined(__aarch64__)
+ memcpy(&value->value.v_uint8, &m_state.context.vfp.__v[reg - vfp_d0],
+ 8);
+#else
+ memcpy(&value->value.v_uint8,
+ ((uint8_t *)&m_state.context.vfp.opaque) + ((reg - vfp_d0) * 16),
+ 8);
+#endif
+ return true;
+ }
+ break;
+
+ case e_regSetEXC:
+ if (reg == exc_far) {
+ value->value.uint64 = m_state.context.exc.__far;
+ return true;
+ } else if (reg == exc_esr) {
+ value->value.uint32 = m_state.context.exc.__esr;
+ return true;
+ } else if (reg == exc_exception) {
+ value->value.uint32 = m_state.context.exc.__exception;
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+bool DNBArchMachARM64::SetRegisterValue(uint32_t set, uint32_t reg,
+ const DNBRegisterValue *value) {
+ if (!FixGenericRegisterNumber(set, reg))
+ return false;
+
+ if (GetRegisterState(set, false) != KERN_SUCCESS)
+ return false;
+
+ bool success = false;
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo) {
+ switch (set) {
+ case e_regSetGPR:
+ if (reg <= gpr_pc) {
+#if defined(__LP64__)
+ uint64_t signed_value = value->value.uint64;
+#if __has_feature(ptrauth_calls)
+ // The incoming value could be garbage. Strip it to avoid
+ // trapping when it gets resigned in the thread state.
+ signed_value = (uint64_t) ptrauth_strip((void*) signed_value, ptrauth_key_function_pointer);
+ signed_value = (uint64_t) ptrauth_sign_unauthenticated((void*) signed_value, ptrauth_key_function_pointer, 0);
+#endif
+ if (reg == gpr_pc)
+ arm_thread_state64_set_pc_fptr (m_state.context.gpr, (void*) signed_value);
+ else if (reg == gpr_lr)
+ arm_thread_state64_set_lr_fptr (m_state.context.gpr, (void*) signed_value);
+ else if (reg == gpr_sp)
+ arm_thread_state64_set_sp (m_state.context.gpr, value->value.uint64);
+ else if (reg == gpr_fp)
+ arm_thread_state64_set_fp (m_state.context.gpr, value->value.uint64);
+ else
+ m_state.context.gpr.__x[reg] = value->value.uint64;
+#else
+ m_state.context.gpr.__x[reg] = value->value.uint64;
+#endif
+ success = true;
+ } else if (reg == gpr_cpsr) {
+ m_state.context.gpr.__cpsr = value->value.uint32;
+ success = true;
+ }
+ break;
+
+ case e_regSetVFP:
+ if (reg >= vfp_v0 && reg <= vfp_v31) {
+#if defined(__arm64__) || defined(__aarch64__)
+ memcpy(&m_state.context.vfp.__v[reg - vfp_v0], &value->value.v_uint8,
+ 16);
+#else
+ memcpy(((uint8_t *)&m_state.context.vfp.opaque) + ((reg - vfp_v0) * 16),
+ &value->value.v_uint8, 16);
+#endif
+ success = true;
+ } else if (reg == vfp_fpsr) {
+#if defined(__arm64__) || defined(__aarch64__)
+ memcpy(&m_state.context.vfp.__fpsr, &value->value.uint32, 4);
+#else
+ memcpy(((uint8_t *)&m_state.context.vfp.opaque) + (32 * 16) + 0,
+ &value->value.uint32, 4);
+#endif
+ success = true;
+ } else if (reg == vfp_fpcr) {
+#if defined(__arm64__) || defined(__aarch64__)
+ memcpy(&m_state.context.vfp.__fpcr, &value->value.uint32, 4);
+#else
+ memcpy(((uint8_t *)m_state.context.vfp.opaque) + (32 * 16) + 4,
+ &value->value.uint32, 4);
+#endif
+ success = true;
+ } else if (reg >= vfp_s0 && reg <= vfp_s31) {
+#if defined(__arm64__) || defined(__aarch64__)
+ memcpy(&m_state.context.vfp.__v[reg - vfp_s0], &value->value.v_uint8,
+ 4);
+#else
+ memcpy(((uint8_t *)&m_state.context.vfp.opaque) + ((reg - vfp_s0) * 16),
+ &value->value.v_uint8, 4);
+#endif
+ success = true;
+ } else if (reg >= vfp_d0 && reg <= vfp_d31) {
+#if defined(__arm64__) || defined(__aarch64__)
+ memcpy(&m_state.context.vfp.__v[reg - vfp_d0], &value->value.v_uint8,
+ 8);
+#else
+ memcpy(((uint8_t *)&m_state.context.vfp.opaque) + ((reg - vfp_d0) * 16),
+ &value->value.v_uint8, 8);
+#endif
+ success = true;
+ }
+ break;
+
+ case e_regSetEXC:
+ if (reg == exc_far) {
+ m_state.context.exc.__far = value->value.uint64;
+ success = true;
+ } else if (reg == exc_esr) {
+ m_state.context.exc.__esr = value->value.uint32;
+ success = true;
+ } else if (reg == exc_exception) {
+ m_state.context.exc.__exception = value->value.uint32;
+ success = true;
+ }
+ break;
+ }
+ }
+ if (success)
+ return SetRegisterState(set) == KERN_SUCCESS;
+ return false;
+}
+
+kern_return_t DNBArchMachARM64::GetRegisterState(int set, bool force) {
+ switch (set) {
+ case e_regSetALL:
+ return GetGPRState(force) | GetVFPState(force) | GetEXCState(force) |
+ GetDBGState(force);
+ case e_regSetGPR:
+ return GetGPRState(force);
+ case e_regSetVFP:
+ return GetVFPState(force);
+ case e_regSetEXC:
+ return GetEXCState(force);
+ case e_regSetDBG:
+ return GetDBGState(force);
+ default:
+ break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+kern_return_t DNBArchMachARM64::SetRegisterState(int set) {
+ // Make sure we have a valid context to set.
+ kern_return_t err = GetRegisterState(set, false);
+ if (err != KERN_SUCCESS)
+ return err;
+
+ switch (set) {
+ case e_regSetALL:
+ return SetGPRState() | SetVFPState() | SetEXCState() | SetDBGState(false);
+ case e_regSetGPR:
+ return SetGPRState();
+ case e_regSetVFP:
+ return SetVFPState();
+ case e_regSetEXC:
+ return SetEXCState();
+ case e_regSetDBG:
+ return SetDBGState(false);
+ default:
+ break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+bool DNBArchMachARM64::RegisterSetStateIsValid(int set) const {
+ return m_state.RegsAreValid(set);
+}
+
+nub_size_t DNBArchMachARM64::GetRegisterContext(void *buf, nub_size_t buf_len) {
+ nub_size_t size = sizeof(m_state.context.gpr) + sizeof(m_state.context.vfp) +
+ sizeof(m_state.context.exc);
+
+ if (buf && buf_len) {
+ if (size > buf_len)
+ size = buf_len;
+
+ bool force = false;
+ if (GetGPRState(force) | GetVFPState(force) | GetEXCState(force))
+ return 0;
+
+ // Copy each struct individually to avoid any padding that might be between
+ // the structs in m_state.context
+ uint8_t *p = (uint8_t *)buf;
+ ::memcpy(p, &m_state.context.gpr, sizeof(m_state.context.gpr));
+ p += sizeof(m_state.context.gpr);
+ ::memcpy(p, &m_state.context.vfp, sizeof(m_state.context.vfp));
+ p += sizeof(m_state.context.vfp);
+ ::memcpy(p, &m_state.context.exc, sizeof(m_state.context.exc));
+ p += sizeof(m_state.context.exc);
+
+ size_t bytes_written = p - (uint8_t *)buf;
+ UNUSED_IF_ASSERT_DISABLED(bytes_written);
+ assert(bytes_written == size);
+ }
+ DNBLogThreadedIf(
+ LOG_THREAD,
+ "DNBArchMachARM64::GetRegisterContext (buf = %p, len = %zu) => %zu", buf,
+ buf_len, size);
+ // Return the size of the register context even if NULL was passed in
+ return size;
+}
+
+nub_size_t DNBArchMachARM64::SetRegisterContext(const void *buf,
+ nub_size_t buf_len) {
+ nub_size_t size = sizeof(m_state.context.gpr) + sizeof(m_state.context.vfp) +
+ sizeof(m_state.context.exc);
+
+ if (buf == NULL || buf_len == 0)
+ size = 0;
+
+ if (size) {
+ if (size > buf_len)
+ size = buf_len;
+
+ // Copy each struct individually to avoid any padding that might be between
+ // the structs in m_state.context
+ uint8_t *p = (uint8_t *)buf;
+ ::memcpy(&m_state.context.gpr, p, sizeof(m_state.context.gpr));
+ p += sizeof(m_state.context.gpr);
+ ::memcpy(&m_state.context.vfp, p, sizeof(m_state.context.vfp));
+ p += sizeof(m_state.context.vfp);
+ ::memcpy(&m_state.context.exc, p, sizeof(m_state.context.exc));
+ p += sizeof(m_state.context.exc);
+
+ size_t bytes_written = p - (uint8_t *)buf;
+ UNUSED_IF_ASSERT_DISABLED(bytes_written);
+ assert(bytes_written == size);
+ SetGPRState();
+ SetVFPState();
+ SetEXCState();
+ }
+ DNBLogThreadedIf(
+ LOG_THREAD,
+ "DNBArchMachARM64::SetRegisterContext (buf = %p, len = %zu) => %zu", buf,
+ buf_len, size);
+ return size;
+}
+
+uint32_t DNBArchMachARM64::SaveRegisterState() {
+ kern_return_t kret = ::thread_abort_safely(m_thread->MachPortNumber());
+ DNBLogThreadedIf(
+ LOG_THREAD, "thread = 0x%4.4x calling thread_abort_safely (tid) => %u "
+ "(SetGPRState() for stop_count = %u)",
+ m_thread->MachPortNumber(), kret, m_thread->Process()->StopCount());
+
+ // Always re-read the registers because above we call thread_abort_safely();
+ bool force = true;
+
+ if ((kret = GetGPRState(force)) != KERN_SUCCESS) {
+ DNBLogThreadedIf(LOG_THREAD, "DNBArchMachARM64::SaveRegisterState () "
+ "error: GPR regs failed to read: %u ",
+ kret);
+ } else if ((kret = GetVFPState(force)) != KERN_SUCCESS) {
+ DNBLogThreadedIf(LOG_THREAD, "DNBArchMachARM64::SaveRegisterState () "
+ "error: %s regs failed to read: %u",
+ "VFP", kret);
+ } else {
+ const uint32_t save_id = GetNextRegisterStateSaveID();
+ m_saved_register_states[save_id] = m_state.context;
+ return save_id;
+ }
+ return UINT32_MAX;
+}
+
+bool DNBArchMachARM64::RestoreRegisterState(uint32_t save_id) {
+ SaveRegisterStates::iterator pos = m_saved_register_states.find(save_id);
+ if (pos != m_saved_register_states.end()) {
+ m_state.context.gpr = pos->second.gpr;
+ m_state.context.vfp = pos->second.vfp;
+ kern_return_t kret;
+ bool success = true;
+ if ((kret = SetGPRState()) != KERN_SUCCESS) {
+ DNBLogThreadedIf(LOG_THREAD, "DNBArchMachARM64::RestoreRegisterState "
+ "(save_id = %u) error: GPR regs failed to "
+ "write: %u",
+ save_id, kret);
+ success = false;
+ } else if ((kret = SetVFPState()) != KERN_SUCCESS) {
+ DNBLogThreadedIf(LOG_THREAD, "DNBArchMachARM64::RestoreRegisterState "
+ "(save_id = %u) error: %s regs failed to "
+ "write: %u",
+ save_id, "VFP", kret);
+ success = false;
+ }
+ m_saved_register_states.erase(pos);
+ return success;
+ }
+ return false;
+}
+
+#endif // #if defined (ARM_THREAD_STATE64_COUNT)
+#endif // #if defined (__arm__) || defined (__arm64__) || defined (__aarch64__)
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.h
new file mode 100644
index 00000000000..2c59a0ceb5d
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.h
@@ -0,0 +1,248 @@
+//===-- DNBArchImplARM64.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNBArchImplARM64_h__
+#define __DNBArchImplARM64_h__
+
+#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
+
+#include <mach/thread_status.h>
+#include <map>
+
+#if defined(ARM_THREAD_STATE64_COUNT)
+
+#include "DNBArch.h"
+
+class MachThread;
+
+class DNBArchMachARM64 : public DNBArchProtocol {
+public:
+ enum { kMaxNumThumbITBreakpoints = 4 };
+
+ DNBArchMachARM64(MachThread *thread)
+ : m_thread(thread), m_state(), m_disabled_watchpoints(),
+ m_watchpoint_hw_index(-1), m_watchpoint_did_occur(false),
+ m_watchpoint_resume_single_step_enabled(false),
+ m_saved_register_states() {
+ m_disabled_watchpoints.resize(16);
+ memset(&m_dbg_save, 0, sizeof(m_dbg_save));
+ }
+
+ virtual ~DNBArchMachARM64() {}
+
+ static void Initialize();
+ static const DNBRegisterSetInfo *GetRegisterSetInfo(nub_size_t *num_reg_sets);
+
+ virtual bool GetRegisterValue(uint32_t set, uint32_t reg,
+ DNBRegisterValue *value);
+ virtual bool SetRegisterValue(uint32_t set, uint32_t reg,
+ const DNBRegisterValue *value);
+ virtual nub_size_t GetRegisterContext(void *buf, nub_size_t buf_len);
+ virtual nub_size_t SetRegisterContext(const void *buf, nub_size_t buf_len);
+ virtual uint32_t SaveRegisterState();
+ virtual bool RestoreRegisterState(uint32_t save_id);
+
+ virtual kern_return_t GetRegisterState(int set, bool force);
+ virtual kern_return_t SetRegisterState(int set);
+ virtual bool RegisterSetStateIsValid(int set) const;
+
+ virtual uint64_t GetPC(uint64_t failValue); // Get program counter
+ virtual kern_return_t SetPC(uint64_t value);
+ virtual uint64_t GetSP(uint64_t failValue); // Get stack pointer
+ virtual void ThreadWillResume();
+ virtual bool ThreadDidStop();
+ virtual bool NotifyException(MachException::Data &exc);
+
+ static DNBArchProtocol *Create(MachThread *thread);
+ static const uint8_t *SoftwareBreakpointOpcode(nub_size_t byte_size);
+ static uint32_t GetCPUType();
+
+ virtual uint32_t NumSupportedHardwareWatchpoints();
+ virtual uint32_t EnableHardwareWatchpoint(nub_addr_t addr, nub_size_t size,
+ bool read, bool write,
+ bool also_set_on_task);
+ virtual bool DisableHardwareWatchpoint(uint32_t hw_break_index,
+ bool also_set_on_task);
+ virtual bool DisableHardwareWatchpoint_helper(uint32_t hw_break_index,
+ bool also_set_on_task);
+
+protected:
+ kern_return_t EnableHardwareSingleStep(bool enable);
+ static bool FixGenericRegisterNumber(uint32_t &set, uint32_t &reg);
+
+ enum RegisterSet {
+ e_regSetALL = REGISTER_SET_ALL,
+ e_regSetGPR, // ARM_THREAD_STATE64,
+ e_regSetVFP, // ARM_NEON_STATE64,
+ e_regSetEXC, // ARM_EXCEPTION_STATE64,
+ e_regSetDBG, // ARM_DEBUG_STATE64,
+ kNumRegisterSets
+ };
+
+ enum {
+ e_regSetGPRCount = ARM_THREAD_STATE64_COUNT,
+ e_regSetVFPCount = ARM_NEON_STATE64_COUNT,
+ e_regSetEXCCount = ARM_EXCEPTION_STATE64_COUNT,
+ e_regSetDBGCount = ARM_DEBUG_STATE64_COUNT,
+ };
+
+ enum { Read = 0, Write = 1, kNumErrors = 2 };
+
+ typedef arm_thread_state64_t GPR;
+ typedef arm_neon_state64_t FPU;
+ typedef arm_exception_state64_t EXC;
+
+ static const DNBRegisterInfo g_gpr_registers[];
+ static const DNBRegisterInfo g_vfp_registers[];
+ static const DNBRegisterInfo g_exc_registers[];
+ static const DNBRegisterSetInfo g_reg_sets[];
+
+ static const size_t k_num_gpr_registers;
+ static const size_t k_num_vfp_registers;
+ static const size_t k_num_exc_registers;
+ static const size_t k_num_all_registers;
+ static const size_t k_num_register_sets;
+
+ struct Context {
+ GPR gpr;
+ FPU vfp;
+ EXC exc;
+ };
+
+ struct State {
+ Context context;
+ arm_debug_state64_t dbg;
+ kern_return_t gpr_errs[2]; // Read/Write errors
+ kern_return_t vfp_errs[2]; // Read/Write errors
+ kern_return_t exc_errs[2]; // Read/Write errors
+ kern_return_t dbg_errs[2]; // Read/Write errors
+ State() {
+ uint32_t i;
+ for (i = 0; i < kNumErrors; i++) {
+ gpr_errs[i] = -1;
+ vfp_errs[i] = -1;
+ exc_errs[i] = -1;
+ dbg_errs[i] = -1;
+ }
+ }
+ void InvalidateRegisterSetState(int set) { SetError(set, Read, -1); }
+
+ void InvalidateAllRegisterStates() { SetError(e_regSetALL, Read, -1); }
+
+ kern_return_t GetError(int set, uint32_t err_idx) const {
+ if (err_idx < kNumErrors) {
+ switch (set) {
+ // When getting all errors, just OR all values together to see if
+ // we got any kind of error.
+ case e_regSetALL:
+ return gpr_errs[err_idx] | vfp_errs[err_idx] | exc_errs[err_idx] |
+ dbg_errs[err_idx];
+ case e_regSetGPR:
+ return gpr_errs[err_idx];
+ case e_regSetVFP:
+ return vfp_errs[err_idx];
+ case e_regSetEXC:
+ return exc_errs[err_idx];
+ // case e_regSetDBG: return dbg_errs[err_idx];
+ default:
+ break;
+ }
+ }
+ return -1;
+ }
+ bool SetError(int set, uint32_t err_idx, kern_return_t err) {
+ if (err_idx < kNumErrors) {
+ switch (set) {
+ case e_regSetALL:
+ gpr_errs[err_idx] = err;
+ vfp_errs[err_idx] = err;
+ dbg_errs[err_idx] = err;
+ exc_errs[err_idx] = err;
+ return true;
+
+ case e_regSetGPR:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case e_regSetVFP:
+ vfp_errs[err_idx] = err;
+ return true;
+
+ case e_regSetEXC:
+ exc_errs[err_idx] = err;
+ return true;
+
+ // case e_regSetDBG:
+ // dbg_errs[err_idx] = err;
+ // return true;
+ default:
+ break;
+ }
+ }
+ return false;
+ }
+ bool RegsAreValid(int set) const {
+ return GetError(set, Read) == KERN_SUCCESS;
+ }
+ };
+
+ kern_return_t GetGPRState(bool force);
+ kern_return_t GetVFPState(bool force);
+ kern_return_t GetEXCState(bool force);
+ kern_return_t GetDBGState(bool force);
+
+ kern_return_t SetGPRState();
+ kern_return_t SetVFPState();
+ kern_return_t SetEXCState();
+ kern_return_t SetDBGState(bool also_set_on_task);
+
+ // Helper functions for watchpoint implementaions.
+
+ typedef arm_debug_state64_t DBG;
+
+ void ClearWatchpointOccurred();
+ bool HasWatchpointOccurred();
+ bool IsWatchpointEnabled(const DBG &debug_state, uint32_t hw_index);
+ nub_addr_t GetWatchpointAddressByIndex(uint32_t hw_index);
+ nub_addr_t GetWatchAddress(const DBG &debug_state, uint32_t hw_index);
+ virtual bool ReenableHardwareWatchpoint(uint32_t hw_break_index);
+ virtual bool ReenableHardwareWatchpoint_helper(uint32_t hw_break_index);
+ virtual uint32_t GetHardwareWatchpointHit(nub_addr_t &addr);
+
+ class disabled_watchpoint {
+ public:
+ disabled_watchpoint() {
+ addr = 0;
+ control = 0;
+ }
+ nub_addr_t addr;
+ uint32_t control;
+ };
+
+protected:
+ MachThread *m_thread;
+ State m_state;
+ arm_debug_state64_t m_dbg_save;
+
+ // arm64 doesn't keep the disabled watchpoint values in the debug register
+ // context like armv7;
+ // we need to save them aside when we disable them temporarily.
+ std::vector<disabled_watchpoint> m_disabled_watchpoints;
+
+ // The following member variables should be updated atomically.
+ int32_t m_watchpoint_hw_index;
+ bool m_watchpoint_did_occur;
+ bool m_watchpoint_resume_single_step_enabled;
+
+ typedef std::map<uint32_t, Context> SaveRegisterStates;
+ SaveRegisterStates m_saved_register_states;
+};
+
+#endif // #if defined (ARM_THREAD_STATE64_COUNT)
+#endif // #if defined (__arm__)
+#endif // #ifndef __DNBArchImplARM64_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/dbgnub-mig.defs b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/dbgnub-mig.defs
new file mode 100644
index 00000000000..cd5be170070
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/dbgnub-mig.defs
@@ -0,0 +1,5 @@
+/*
+ * nub.defs
+ */
+
+#import <mach/mach_exc.defs>
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp
new file mode 100644
index 00000000000..7d2d0c2ef1b
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp
@@ -0,0 +1,2382 @@
+//===-- DNBArchImplI386.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include <sys/cdefs.h>
+
+#include "DNBLog.h"
+#include "MacOSX/i386/DNBArchImplI386.h"
+#include "MachProcess.h"
+#include "MachThread.h"
+
+extern "C" bool CPUHasAVX(); // Defined over in DNBArchImplX86_64.cpp
+extern "C" bool CPUHasAVX512f(); // Defined over in DNBArchImplX86_64.cpp
+#if defined(LLDB_DEBUGSERVER_RELEASE) || defined(LLDB_DEBUGSERVER_DEBUG)
+enum debugState { debugStateUnknown, debugStateOff, debugStateOn };
+
+static debugState sFPUDebugState = debugStateUnknown;
+static debugState sAVXForceState = debugStateUnknown;
+
+static bool DebugFPURegs() {
+ if (sFPUDebugState == debugStateUnknown) {
+ if (getenv("DNB_DEBUG_FPU_REGS"))
+ sFPUDebugState = debugStateOn;
+ else
+ sFPUDebugState = debugStateOff;
+ }
+
+ return (sFPUDebugState == debugStateOn);
+}
+
+static bool ForceAVXRegs() {
+ if (sFPUDebugState == debugStateUnknown) {
+ if (getenv("DNB_DEBUG_X86_FORCE_AVX_REGS"))
+ sAVXForceState = debugStateOn;
+ else
+ sAVXForceState = debugStateOff;
+ }
+
+ return (sAVXForceState == debugStateOn);
+}
+
+#define DEBUG_FPU_REGS (DebugFPURegs())
+#define FORCE_AVX_REGS (ForceAVXRegs())
+#else
+#define DEBUG_FPU_REGS (0)
+#define FORCE_AVX_REGS (0)
+#endif
+
+enum {
+ gpr_eax = 0,
+ gpr_ebx = 1,
+ gpr_ecx = 2,
+ gpr_edx = 3,
+ gpr_edi = 4,
+ gpr_esi = 5,
+ gpr_ebp = 6,
+ gpr_esp = 7,
+ gpr_ss = 8,
+ gpr_eflags = 9,
+ gpr_eip = 10,
+ gpr_cs = 11,
+ gpr_ds = 12,
+ gpr_es = 13,
+ gpr_fs = 14,
+ gpr_gs = 15,
+ gpr_ax,
+ gpr_bx,
+ gpr_cx,
+ gpr_dx,
+ gpr_di,
+ gpr_si,
+ gpr_bp,
+ gpr_sp,
+ gpr_ah,
+ gpr_bh,
+ gpr_ch,
+ gpr_dh,
+ gpr_al,
+ gpr_bl,
+ gpr_cl,
+ gpr_dl,
+ gpr_dil,
+ gpr_sil,
+ gpr_bpl,
+ gpr_spl,
+ k_num_gpr_regs
+};
+
+enum {
+ fpu_fcw,
+ fpu_fsw,
+ fpu_ftw,
+ fpu_fop,
+ fpu_ip,
+ fpu_cs,
+ fpu_dp,
+ fpu_ds,
+ fpu_mxcsr,
+ fpu_mxcsrmask,
+ fpu_stmm0,
+ fpu_stmm1,
+ fpu_stmm2,
+ fpu_stmm3,
+ fpu_stmm4,
+ fpu_stmm5,
+ fpu_stmm6,
+ fpu_stmm7,
+ fpu_xmm0,
+ fpu_xmm1,
+ fpu_xmm2,
+ fpu_xmm3,
+ fpu_xmm4,
+ fpu_xmm5,
+ fpu_xmm6,
+ fpu_xmm7,
+ fpu_ymm0,
+ fpu_ymm1,
+ fpu_ymm2,
+ fpu_ymm3,
+ fpu_ymm4,
+ fpu_ymm5,
+ fpu_ymm6,
+ fpu_ymm7,
+ fpu_k0,
+ fpu_k1,
+ fpu_k2,
+ fpu_k3,
+ fpu_k4,
+ fpu_k5,
+ fpu_k6,
+ fpu_k7,
+ fpu_zmm0,
+ fpu_zmm1,
+ fpu_zmm2,
+ fpu_zmm3,
+ fpu_zmm4,
+ fpu_zmm5,
+ fpu_zmm6,
+ fpu_zmm7,
+ k_num_fpu_regs,
+
+ // Aliases
+ fpu_fctrl = fpu_fcw,
+ fpu_fstat = fpu_fsw,
+ fpu_ftag = fpu_ftw,
+ fpu_fiseg = fpu_cs,
+ fpu_fioff = fpu_ip,
+ fpu_foseg = fpu_ds,
+ fpu_fooff = fpu_dp
+};
+
+enum {
+ exc_trapno,
+ exc_err,
+ exc_faultvaddr,
+ k_num_exc_regs,
+};
+
+enum {
+ ehframe_eax = 0,
+ ehframe_ecx,
+ ehframe_edx,
+ ehframe_ebx,
+
+ // On i386 Darwin the eh_frame register numbers for ebp and esp are reversed
+ // from DWARF.
+ // It's due to an ancient compiler bug in the output of the eh_frame.
+ // Specifically, on i386 darwin eh_frame, 4 is ebp, 5 is esp.
+ // On i386 darwin debug_frame (and debug_info), 4 is esp, 5 is ebp.
+ ehframe_ebp,
+ ehframe_esp,
+ ehframe_esi,
+ ehframe_edi,
+ ehframe_eip,
+ ehframe_eflags
+};
+
+enum {
+ dwarf_eax = 0,
+ dwarf_ecx,
+ dwarf_edx,
+ dwarf_ebx,
+ dwarf_esp,
+ dwarf_ebp,
+ dwarf_esi,
+ dwarf_edi,
+ dwarf_eip,
+ dwarf_eflags,
+ dwarf_stmm0 = 11,
+ dwarf_stmm1,
+ dwarf_stmm2,
+ dwarf_stmm3,
+ dwarf_stmm4,
+ dwarf_stmm5,
+ dwarf_stmm6,
+ dwarf_stmm7,
+ dwarf_xmm0 = 21,
+ dwarf_xmm1,
+ dwarf_xmm2,
+ dwarf_xmm3,
+ dwarf_xmm4,
+ dwarf_xmm5,
+ dwarf_xmm6,
+ dwarf_xmm7,
+ dwarf_ymm0 = dwarf_xmm0,
+ dwarf_ymm1 = dwarf_xmm1,
+ dwarf_ymm2 = dwarf_xmm2,
+ dwarf_ymm3 = dwarf_xmm3,
+ dwarf_ymm4 = dwarf_xmm4,
+ dwarf_ymm5 = dwarf_xmm5,
+ dwarf_ymm6 = dwarf_xmm6,
+ dwarf_ymm7 = dwarf_xmm7,
+ dwarf_zmm0 = dwarf_xmm0,
+ dwarf_zmm1 = dwarf_xmm1,
+ dwarf_zmm2 = dwarf_xmm2,
+ dwarf_zmm3 = dwarf_xmm3,
+ dwarf_zmm4 = dwarf_xmm4,
+ dwarf_zmm5 = dwarf_xmm5,
+ dwarf_zmm6 = dwarf_xmm6,
+ dwarf_zmm7 = dwarf_xmm7,
+ dwarf_k0 = 118,
+ dwarf_k1,
+ dwarf_k2,
+ dwarf_k3,
+ dwarf_k4,
+ dwarf_k5,
+ dwarf_k6,
+ dwarf_k7,
+};
+
+enum {
+ debugserver_eax = 0,
+ debugserver_ecx = 1,
+ debugserver_edx = 2,
+ debugserver_ebx = 3,
+ debugserver_esp = 4,
+ debugserver_ebp = 5,
+ debugserver_esi = 6,
+ debugserver_edi = 7,
+ debugserver_eip = 8,
+ debugserver_eflags = 9,
+ debugserver_cs = 10,
+ debugserver_ss = 11,
+ debugserver_ds = 12,
+ debugserver_es = 13,
+ debugserver_fs = 14,
+ debugserver_gs = 15,
+ debugserver_stmm0 = 16,
+ debugserver_stmm1 = 17,
+ debugserver_stmm2 = 18,
+ debugserver_stmm3 = 19,
+ debugserver_stmm4 = 20,
+ debugserver_stmm5 = 21,
+ debugserver_stmm6 = 22,
+ debugserver_stmm7 = 23,
+ debugserver_fctrl = 24,
+ debugserver_fcw = debugserver_fctrl,
+ debugserver_fstat = 25,
+ debugserver_fsw = debugserver_fstat,
+ debugserver_ftag = 26,
+ debugserver_ftw = debugserver_ftag,
+ debugserver_fiseg = 27,
+ debugserver_fpu_cs = debugserver_fiseg,
+ debugserver_fioff = 28,
+ debugserver_ip = debugserver_fioff,
+ debugserver_foseg = 29,
+ debugserver_fpu_ds = debugserver_foseg,
+ debugserver_fooff = 30,
+ debugserver_dp = debugserver_fooff,
+ debugserver_fop = 31,
+ debugserver_xmm0 = 32,
+ debugserver_xmm1 = 33,
+ debugserver_xmm2 = 34,
+ debugserver_xmm3 = 35,
+ debugserver_xmm4 = 36,
+ debugserver_xmm5 = 37,
+ debugserver_xmm6 = 38,
+ debugserver_xmm7 = 39,
+ debugserver_mxcsr = 40,
+ debugserver_mm0 = 41,
+ debugserver_mm1 = 42,
+ debugserver_mm2 = 43,
+ debugserver_mm3 = 44,
+ debugserver_mm4 = 45,
+ debugserver_mm5 = 46,
+ debugserver_mm6 = 47,
+ debugserver_mm7 = 48,
+ debugserver_ymm0 = debugserver_xmm0,
+ debugserver_ymm1 = debugserver_xmm1,
+ debugserver_ymm2 = debugserver_xmm2,
+ debugserver_ymm3 = debugserver_xmm3,
+ debugserver_ymm4 = debugserver_xmm4,
+ debugserver_ymm5 = debugserver_xmm5,
+ debugserver_ymm6 = debugserver_xmm6,
+ debugserver_ymm7 = debugserver_xmm7,
+ debugserver_zmm0 = debugserver_xmm0,
+ debugserver_zmm1 = debugserver_xmm1,
+ debugserver_zmm2 = debugserver_xmm2,
+ debugserver_zmm3 = debugserver_xmm3,
+ debugserver_zmm4 = debugserver_xmm4,
+ debugserver_zmm5 = debugserver_xmm5,
+ debugserver_zmm6 = debugserver_xmm6,
+ debugserver_zmm7 = debugserver_xmm7,
+ debugserver_k0 = 118,
+ debugserver_k1 = 119,
+ debugserver_k2 = 120,
+ debugserver_k3 = 121,
+ debugserver_k4 = 122,
+ debugserver_k5 = 123,
+ debugserver_k6 = 124,
+ debugserver_k7 = 125,
+};
+
+uint64_t DNBArchImplI386::GetPC(uint64_t failValue) {
+ // Get program counter
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.context.gpr.__eip;
+ return failValue;
+}
+
+kern_return_t DNBArchImplI386::SetPC(uint64_t value) {
+ // Get program counter
+ kern_return_t err = GetGPRState(false);
+ if (err == KERN_SUCCESS) {
+ m_state.context.gpr.__eip = static_cast<uint32_t>(value);
+ err = SetGPRState();
+ }
+ return err == KERN_SUCCESS;
+}
+
+uint64_t DNBArchImplI386::GetSP(uint64_t failValue) {
+ // Get stack pointer
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.context.gpr.__esp;
+ return failValue;
+}
+
+// Uncomment the value below to verify the values in the debugger.
+//#define DEBUG_GPR_VALUES 1 // DO NOT CHECK IN WITH THIS DEFINE ENABLED
+//#define SET_GPR(reg) m_state.context.gpr.__##reg = gpr_##reg
+
+kern_return_t DNBArchImplI386::GetGPRState(bool force) {
+ if (force || m_state.GetError(e_regSetGPR, Read)) {
+#if DEBUG_GPR_VALUES
+ SET_GPR(eax);
+ SET_GPR(ebx);
+ SET_GPR(ecx);
+ SET_GPR(edx);
+ SET_GPR(edi);
+ SET_GPR(esi);
+ SET_GPR(ebp);
+ SET_GPR(esp);
+ SET_GPR(ss);
+ SET_GPR(eflags);
+ SET_GPR(eip);
+ SET_GPR(cs);
+ SET_GPR(ds);
+ SET_GPR(es);
+ SET_GPR(fs);
+ SET_GPR(gs);
+ m_state.SetError(e_regSetGPR, Read, 0);
+#else
+ mach_msg_type_number_t count = e_regSetWordSizeGPR;
+ m_state.SetError(
+ e_regSetGPR, Read,
+ ::thread_get_state(m_thread->MachPortNumber(), __i386_THREAD_STATE,
+ (thread_state_t)&m_state.context.gpr, &count));
+#endif
+ }
+ return m_state.GetError(e_regSetGPR, Read);
+}
+
+// Uncomment the value below to verify the values in the debugger.
+//#define DEBUG_FPU_VALUES 1 // DO NOT CHECK IN WITH THIS DEFINE ENABLED
+
+kern_return_t DNBArchImplI386::GetFPUState(bool force) {
+ if (force || m_state.GetError(e_regSetFPU, Read)) {
+ if (DEBUG_FPU_REGS) {
+
+ m_state.context.fpu.no_avx.__fpu_reserved[0] = -1;
+ m_state.context.fpu.no_avx.__fpu_reserved[1] = -1;
+ *(uint16_t *)&(m_state.context.fpu.no_avx.__fpu_fcw) = 0x1234;
+ *(uint16_t *)&(m_state.context.fpu.no_avx.__fpu_fsw) = 0x5678;
+ m_state.context.fpu.no_avx.__fpu_ftw = 1;
+ m_state.context.fpu.no_avx.__fpu_rsrv1 = UINT8_MAX;
+ m_state.context.fpu.no_avx.__fpu_fop = 2;
+ m_state.context.fpu.no_avx.__fpu_ip = 3;
+ m_state.context.fpu.no_avx.__fpu_cs = 4;
+ m_state.context.fpu.no_avx.__fpu_rsrv2 = 5;
+ m_state.context.fpu.no_avx.__fpu_dp = 6;
+ m_state.context.fpu.no_avx.__fpu_ds = 7;
+ m_state.context.fpu.no_avx.__fpu_rsrv3 = UINT16_MAX;
+ m_state.context.fpu.no_avx.__fpu_mxcsr = 8;
+ m_state.context.fpu.no_avx.__fpu_mxcsrmask = 9;
+ for (int i = 0; i < 16; ++i) {
+ if (i < 10) {
+ m_state.context.fpu.no_avx.__fpu_stmm0.__mmst_reg[i] = 'a';
+ m_state.context.fpu.no_avx.__fpu_stmm1.__mmst_reg[i] = 'b';
+ m_state.context.fpu.no_avx.__fpu_stmm2.__mmst_reg[i] = 'c';
+ m_state.context.fpu.no_avx.__fpu_stmm3.__mmst_reg[i] = 'd';
+ m_state.context.fpu.no_avx.__fpu_stmm4.__mmst_reg[i] = 'e';
+ m_state.context.fpu.no_avx.__fpu_stmm5.__mmst_reg[i] = 'f';
+ m_state.context.fpu.no_avx.__fpu_stmm6.__mmst_reg[i] = 'g';
+ m_state.context.fpu.no_avx.__fpu_stmm7.__mmst_reg[i] = 'h';
+ } else {
+ m_state.context.fpu.no_avx.__fpu_stmm0.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm1.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm2.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm3.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm4.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm5.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm6.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm7.__mmst_reg[i] = INT8_MIN;
+ }
+
+ m_state.context.fpu.no_avx.__fpu_xmm0.__xmm_reg[i] = '0';
+ m_state.context.fpu.no_avx.__fpu_xmm1.__xmm_reg[i] = '1';
+ m_state.context.fpu.no_avx.__fpu_xmm2.__xmm_reg[i] = '2';
+ m_state.context.fpu.no_avx.__fpu_xmm3.__xmm_reg[i] = '3';
+ m_state.context.fpu.no_avx.__fpu_xmm4.__xmm_reg[i] = '4';
+ m_state.context.fpu.no_avx.__fpu_xmm5.__xmm_reg[i] = '5';
+ m_state.context.fpu.no_avx.__fpu_xmm6.__xmm_reg[i] = '6';
+ m_state.context.fpu.no_avx.__fpu_xmm7.__xmm_reg[i] = '7';
+ }
+ for (int i = 0; i < sizeof(m_state.context.fpu.no_avx.__fpu_rsrv4); ++i)
+ m_state.context.fpu.no_avx.__fpu_rsrv4[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_reserved1 = -1;
+
+ if (CPUHasAVX() || FORCE_AVX_REGS) {
+ for (int i = 0; i < sizeof(m_state.context.fpu.avx.__avx_reserved1);
+ ++i)
+ m_state.context.fpu.avx.__avx_reserved1[i] = INT8_MIN;
+
+ for (int i = 0; i < 16; ++i) {
+ m_state.context.fpu.avx.__fpu_ymmh0.__xmm_reg[i] = '0';
+ m_state.context.fpu.avx.__fpu_ymmh1.__xmm_reg[i] = '1';
+ m_state.context.fpu.avx.__fpu_ymmh2.__xmm_reg[i] = '2';
+ m_state.context.fpu.avx.__fpu_ymmh3.__xmm_reg[i] = '3';
+ m_state.context.fpu.avx.__fpu_ymmh4.__xmm_reg[i] = '4';
+ m_state.context.fpu.avx.__fpu_ymmh5.__xmm_reg[i] = '5';
+ m_state.context.fpu.avx.__fpu_ymmh6.__xmm_reg[i] = '6';
+ m_state.context.fpu.avx.__fpu_ymmh7.__xmm_reg[i] = '7';
+ }
+ }
+ if (CPUHasAVX512f() || FORCE_AVX_REGS) {
+ for (int i = 0; i < 8; ++i) {
+ m_state.context.fpu.avx512f.__fpu_k0.__opmask_reg[i] = '0';
+ m_state.context.fpu.avx512f.__fpu_k1.__opmask_reg[i] = '1';
+ m_state.context.fpu.avx512f.__fpu_k2.__opmask_reg[i] = '2';
+ m_state.context.fpu.avx512f.__fpu_k3.__opmask_reg[i] = '3';
+ m_state.context.fpu.avx512f.__fpu_k4.__opmask_reg[i] = '4';
+ m_state.context.fpu.avx512f.__fpu_k5.__opmask_reg[i] = '5';
+ m_state.context.fpu.avx512f.__fpu_k6.__opmask_reg[i] = '6';
+ m_state.context.fpu.avx512f.__fpu_k7.__opmask_reg[i] = '7';
+ }
+
+ for (int i = 0; i < 32; ++i) {
+ m_state.context.fpu.avx512f.__fpu_zmmh0.__ymm_reg[i] = '0';
+ m_state.context.fpu.avx512f.__fpu_zmmh1.__ymm_reg[i] = '1';
+ m_state.context.fpu.avx512f.__fpu_zmmh2.__ymm_reg[i] = '2';
+ m_state.context.fpu.avx512f.__fpu_zmmh3.__ymm_reg[i] = '3';
+ m_state.context.fpu.avx512f.__fpu_zmmh4.__ymm_reg[i] = '4';
+ m_state.context.fpu.avx512f.__fpu_zmmh5.__ymm_reg[i] = '5';
+ m_state.context.fpu.avx512f.__fpu_zmmh6.__ymm_reg[i] = '6';
+ m_state.context.fpu.avx512f.__fpu_zmmh7.__ymm_reg[i] = '7';
+ }
+ }
+ m_state.SetError(e_regSetFPU, Read, 0);
+ } else {
+ mach_msg_type_number_t count = e_regSetWordSizeFPU;
+ int flavor = __i386_FLOAT_STATE;
+
+ // On a machine with the AVX512 register set, a process only gets a
+ // full AVX512 register context after it uses the AVX512 registers;
+ // if the process has not yet triggered this change, trying to fetch
+ // the AVX512 registers will fail. Fall through to fetching the AVX
+ // registers.
+ if (CPUHasAVX512f() || FORCE_AVX_REGS) {
+ count = e_regSetWordSizeAVX512f;
+ flavor = __i386_AVX512F_STATE;
+ m_state.SetError(e_regSetFPU, Read,
+ ::thread_get_state(m_thread->MachPortNumber(), flavor,
+ (thread_state_t)&m_state.context.fpu,
+ &count));
+ DNBLogThreadedIf(LOG_THREAD,
+ "::thread_get_state (0x%4.4x, %u, &fpu, %u => 0x%8.8x",
+ m_thread->MachPortNumber(), flavor, (uint32_t)count,
+ m_state.GetError(e_regSetFPU, Read));
+ if (m_state.GetError(e_regSetFPU, Read) == KERN_SUCCESS)
+ return m_state.GetError(e_regSetFPU, Read);
+ }
+ if (CPUHasAVX()) {
+ count = e_regSetWordSizeAVX;
+ flavor = __i386_AVX_STATE;
+ }
+ m_state.SetError(e_regSetFPU, Read,
+ ::thread_get_state(m_thread->MachPortNumber(), flavor,
+ (thread_state_t)&m_state.context.fpu,
+ &count));
+ DNBLogThreadedIf(LOG_THREAD,
+ "::thread_get_state (0x%4.4x, %u, &fpu, %u => 0x%8.8x",
+ m_thread->MachPortNumber(), flavor, (uint32_t)count,
+ m_state.GetError(e_regSetFPU, Read));
+ }
+ }
+ return m_state.GetError(e_regSetFPU, Read);
+}
+
+kern_return_t DNBArchImplI386::GetEXCState(bool force) {
+ if (force || m_state.GetError(e_regSetEXC, Read)) {
+ mach_msg_type_number_t count = e_regSetWordSizeEXC;
+ m_state.SetError(
+ e_regSetEXC, Read,
+ ::thread_get_state(m_thread->MachPortNumber(), __i386_EXCEPTION_STATE,
+ (thread_state_t)&m_state.context.exc, &count));
+ }
+ return m_state.GetError(e_regSetEXC, Read);
+}
+
+kern_return_t DNBArchImplI386::SetGPRState() {
+ kern_return_t kret = ::thread_abort_safely(m_thread->MachPortNumber());
+ DNBLogThreadedIf(
+ LOG_THREAD, "thread = 0x%4.4x calling thread_abort_safely (tid) => %u "
+ "(SetGPRState() for stop_count = %u)",
+ m_thread->MachPortNumber(), kret, m_thread->Process()->StopCount());
+
+ m_state.SetError(e_regSetGPR, Write,
+ ::thread_set_state(m_thread->MachPortNumber(),
+ __i386_THREAD_STATE,
+ (thread_state_t)&m_state.context.gpr,
+ e_regSetWordSizeGPR));
+ return m_state.GetError(e_regSetGPR, Write);
+}
+
+kern_return_t DNBArchImplI386::SetFPUState() {
+ if (DEBUG_FPU_REGS) {
+ m_state.SetError(e_regSetFPU, Write, 0);
+ return m_state.GetError(e_regSetFPU, Write);
+ } else {
+ int flavor = __i386_FLOAT_STATE;
+ mach_msg_type_number_t count = e_regSetWordSizeFPU;
+ if (CPUHasAVX512f() || FORCE_AVX_REGS) {
+ flavor = __i386_AVX512F_STATE;
+ count = e_regSetWordSizeAVX512f;
+ } else
+ if (CPUHasAVX()) {
+ flavor = __i386_AVX_STATE;
+ count = e_regSetWordSizeAVX;
+ }
+
+ m_state.SetError(e_regSetFPU, Write,
+ ::thread_set_state(m_thread->MachPortNumber(), flavor,
+ (thread_state_t)&m_state.context.fpu,
+ count));
+ return m_state.GetError(e_regSetFPU, Write);
+ }
+}
+
+kern_return_t DNBArchImplI386::SetEXCState() {
+ m_state.SetError(e_regSetEXC, Write,
+ ::thread_set_state(m_thread->MachPortNumber(),
+ __i386_EXCEPTION_STATE,
+ (thread_state_t)&m_state.context.exc,
+ e_regSetWordSizeEXC));
+ return m_state.GetError(e_regSetEXC, Write);
+}
+
+kern_return_t DNBArchImplI386::GetDBGState(bool force) {
+ if (force || m_state.GetError(e_regSetDBG, Read)) {
+ mach_msg_type_number_t count = e_regSetWordSizeDBG;
+ m_state.SetError(
+ e_regSetDBG, Read,
+ ::thread_get_state(m_thread->MachPortNumber(), __i386_DEBUG_STATE,
+ (thread_state_t)&m_state.context.dbg, &count));
+ }
+ return m_state.GetError(e_regSetDBG, Read);
+}
+
+kern_return_t DNBArchImplI386::SetDBGState(bool also_set_on_task) {
+ m_state.SetError(e_regSetDBG, Write,
+ ::thread_set_state(m_thread->MachPortNumber(),
+ __i386_DEBUG_STATE,
+ (thread_state_t)&m_state.context.dbg,
+ e_regSetWordSizeDBG));
+ if (also_set_on_task) {
+ kern_return_t kret = ::task_set_state(
+ m_thread->Process()->Task().TaskPort(), __i386_DEBUG_STATE,
+ (thread_state_t)&m_state.context.dbg, e_regSetWordSizeDBG);
+ if (kret != KERN_SUCCESS)
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplI386::SetDBGState failed "
+ "to set debug control register state: "
+ "0x%8.8x.",
+ kret);
+ }
+ return m_state.GetError(e_regSetDBG, Write);
+}
+
+void DNBArchImplI386::ThreadWillResume() {
+ // Do we need to step this thread? If so, let the mach thread tell us so.
+ if (m_thread->IsStepping()) {
+ // This is the primary thread, let the arch do anything it needs
+ EnableHardwareSingleStep(true);
+ }
+
+ // Reset the debug status register, if necessary, before we resume.
+ kern_return_t kret = GetDBGState(false);
+ DNBLogThreadedIf(
+ LOG_WATCHPOINTS,
+ "DNBArchImplI386::ThreadWillResume() GetDBGState() => 0x%8.8x.", kret);
+ if (kret != KERN_SUCCESS)
+ return;
+
+ DBG &debug_state = m_state.context.dbg;
+ bool need_reset = false;
+ uint32_t i, num = NumSupportedHardwareWatchpoints();
+ for (i = 0; i < num; ++i)
+ if (IsWatchpointHit(debug_state, i))
+ need_reset = true;
+
+ if (need_reset) {
+ ClearWatchpointHits(debug_state);
+ kret = SetDBGState(false);
+ DNBLogThreadedIf(
+ LOG_WATCHPOINTS,
+ "DNBArchImplI386::ThreadWillResume() SetDBGState() => 0x%8.8x.", kret);
+ }
+}
+
+bool DNBArchImplI386::ThreadDidStop() {
+ bool success = true;
+
+ m_state.InvalidateAllRegisterStates();
+
+ // Are we stepping a single instruction?
+ if (GetGPRState(true) == KERN_SUCCESS) {
+ // We are single stepping, was this the primary thread?
+ if (m_thread->IsStepping()) {
+ // This was the primary thread, we need to clear the trace
+ // bit if so.
+ success = EnableHardwareSingleStep(false) == KERN_SUCCESS;
+ } else {
+ // The MachThread will automatically restore the suspend count
+ // in ThreadDidStop(), so we don't need to do anything here if
+ // we weren't the primary thread the last time
+ }
+ }
+ return success;
+}
+
+bool DNBArchImplI386::NotifyException(MachException::Data &exc) {
+ switch (exc.exc_type) {
+ case EXC_BAD_ACCESS:
+ break;
+ case EXC_BAD_INSTRUCTION:
+ break;
+ case EXC_ARITHMETIC:
+ break;
+ case EXC_EMULATION:
+ break;
+ case EXC_SOFTWARE:
+ break;
+ case EXC_BREAKPOINT:
+ if (exc.exc_data.size() >= 2 && exc.exc_data[0] == 2) {
+ // exc_code = EXC_I386_BPT
+ //
+ nub_addr_t pc = GetPC(INVALID_NUB_ADDRESS);
+ if (pc != INVALID_NUB_ADDRESS && pc > 0) {
+ pc -= 1;
+ // Check for a breakpoint at one byte prior to the current PC value
+ // since the PC will be just past the trap.
+
+ DNBBreakpoint *bp =
+ m_thread->Process()->Breakpoints().FindByAddress(pc);
+ if (bp) {
+ // Backup the PC for i386 since the trap was taken and the PC
+ // is at the address following the single byte trap instruction.
+ if (m_state.context.gpr.__eip > 0) {
+ m_state.context.gpr.__eip = static_cast<uint32_t>(pc);
+ // Write the new PC back out
+ SetGPRState();
+ }
+ }
+ return true;
+ }
+ } else if (exc.exc_data.size() >= 2 && exc.exc_data[0] == 1) {
+ // exc_code = EXC_I386_SGL
+ //
+ // Check whether this corresponds to a watchpoint hit event.
+ // If yes, set the exc_sub_code to the data break address.
+ nub_addr_t addr = 0;
+ uint32_t hw_index = GetHardwareWatchpointHit(addr);
+ if (hw_index != INVALID_NUB_HW_INDEX) {
+ exc.exc_data[1] = addr;
+ // Piggyback the hw_index in the exc.data.
+ exc.exc_data.push_back(hw_index);
+ }
+
+ return true;
+ }
+ break;
+ case EXC_SYSCALL:
+ break;
+ case EXC_MACH_SYSCALL:
+ break;
+ case EXC_RPC_ALERT:
+ break;
+ }
+ return false;
+}
+
+uint32_t DNBArchImplI386::NumSupportedHardwareWatchpoints() {
+ // Available debug address registers: dr0, dr1, dr2, dr3.
+ return 4;
+}
+
+static uint32_t size_and_rw_bits(nub_size_t size, bool read, bool write) {
+ uint32_t rw;
+ if (read) {
+ rw = 0x3; // READ or READ/WRITE
+ } else if (write) {
+ rw = 0x1; // WRITE
+ } else {
+ assert(0 && "read and write cannot both be false");
+ }
+
+ switch (size) {
+ case 1:
+ return rw;
+ case 2:
+ return (0x1 << 2) | rw;
+ case 4:
+ return (0x3 << 2) | rw;
+ case 8:
+ return (0x2 << 2) | rw;
+ }
+ assert(0 && "invalid size, must be one of 1, 2, 4, or 8");
+ return 0;
+}
+
+void DNBArchImplI386::SetWatchpoint(DBG &debug_state, uint32_t hw_index,
+ nub_addr_t addr, nub_size_t size, bool read,
+ bool write) {
+ // Set both dr7 (debug control register) and dri (debug address register).
+
+ // dr7{7-0} encodes the local/gloabl enable bits:
+ // global enable --. .-- local enable
+ // | |
+ // v v
+ // dr0 -> bits{1-0}
+ // dr1 -> bits{3-2}
+ // dr2 -> bits{5-4}
+ // dr3 -> bits{7-6}
+ //
+ // dr7{31-16} encodes the rw/len bits:
+ // b_x+3, b_x+2, b_x+1, b_x
+ // where bits{x+1, x} => rw
+ // 0b00: execute, 0b01: write, 0b11: read-or-write, 0b10: io
+ // read-or-write (unused)
+ // and bits{x+3, x+2} => len
+ // 0b00: 1-byte, 0b01: 2-byte, 0b11: 4-byte, 0b10: 8-byte
+ //
+ // dr0 -> bits{19-16}
+ // dr1 -> bits{23-20}
+ // dr2 -> bits{27-24}
+ // dr3 -> bits{31-28}
+ debug_state.__dr7 |=
+ (1 << (2 * hw_index) |
+ size_and_rw_bits(size, read, write) << (16 + 4 * hw_index));
+ uint32_t addr_32 = addr & 0xffffffff;
+ switch (hw_index) {
+ case 0:
+ debug_state.__dr0 = addr_32;
+ break;
+ case 1:
+ debug_state.__dr1 = addr_32;
+ break;
+ case 2:
+ debug_state.__dr2 = addr_32;
+ break;
+ case 3:
+ debug_state.__dr3 = addr_32;
+ break;
+ default:
+ assert(0 &&
+ "invalid hardware register index, must be one of 0, 1, 2, or 3");
+ }
+ return;
+}
+
+void DNBArchImplI386::ClearWatchpoint(DBG &debug_state, uint32_t hw_index) {
+ debug_state.__dr7 &= ~(3 << (2 * hw_index));
+ switch (hw_index) {
+ case 0:
+ debug_state.__dr0 = 0;
+ break;
+ case 1:
+ debug_state.__dr1 = 0;
+ break;
+ case 2:
+ debug_state.__dr2 = 0;
+ break;
+ case 3:
+ debug_state.__dr3 = 0;
+ break;
+ default:
+ assert(0 &&
+ "invalid hardware register index, must be one of 0, 1, 2, or 3");
+ }
+ return;
+}
+
+bool DNBArchImplI386::IsWatchpointVacant(const DBG &debug_state,
+ uint32_t hw_index) {
+ // Check dr7 (debug control register) for local/global enable bits:
+ // global enable --. .-- local enable
+ // | |
+ // v v
+ // dr0 -> bits{1-0}
+ // dr1 -> bits{3-2}
+ // dr2 -> bits{5-4}
+ // dr3 -> bits{7-6}
+ return (debug_state.__dr7 & (3 << (2 * hw_index))) == 0;
+}
+
+// Resets local copy of debug status register to wait for the next debug
+// exception.
+void DNBArchImplI386::ClearWatchpointHits(DBG &debug_state) {
+ // See also IsWatchpointHit().
+ debug_state.__dr6 = 0;
+ return;
+}
+
+bool DNBArchImplI386::IsWatchpointHit(const DBG &debug_state,
+ uint32_t hw_index) {
+ // Check dr6 (debug status register) whether a watchpoint hits:
+ // is watchpoint hit?
+ // |
+ // v
+ // dr0 -> bits{0}
+ // dr1 -> bits{1}
+ // dr2 -> bits{2}
+ // dr3 -> bits{3}
+ return (debug_state.__dr6 & (1 << hw_index));
+}
+
+nub_addr_t DNBArchImplI386::GetWatchAddress(const DBG &debug_state,
+ uint32_t hw_index) {
+ switch (hw_index) {
+ case 0:
+ return debug_state.__dr0;
+ case 1:
+ return debug_state.__dr1;
+ case 2:
+ return debug_state.__dr2;
+ case 3:
+ return debug_state.__dr3;
+ }
+ assert(0 && "invalid hardware register index, must be one of 0, 1, 2, or 3");
+ return 0;
+}
+
+bool DNBArchImplI386::StartTransForHWP() {
+ if (m_2pc_trans_state != Trans_Done && m_2pc_trans_state != Trans_Rolled_Back)
+ DNBLogError("%s inconsistent state detected, expected %d or %d, got: %d",
+ __FUNCTION__, Trans_Done, Trans_Rolled_Back, m_2pc_trans_state);
+ m_2pc_dbg_checkpoint = m_state.context.dbg;
+ m_2pc_trans_state = Trans_Pending;
+ return true;
+}
+bool DNBArchImplI386::RollbackTransForHWP() {
+ m_state.context.dbg = m_2pc_dbg_checkpoint;
+ if (m_2pc_trans_state != Trans_Pending)
+ DNBLogError("%s inconsistent state detected, expected %d, got: %d",
+ __FUNCTION__, Trans_Pending, m_2pc_trans_state);
+ m_2pc_trans_state = Trans_Rolled_Back;
+ kern_return_t kret = SetDBGState(false);
+ DNBLogThreadedIf(
+ LOG_WATCHPOINTS,
+ "DNBArchImplI386::RollbackTransForHWP() SetDBGState() => 0x%8.8x.", kret);
+
+ return kret == KERN_SUCCESS;
+}
+bool DNBArchImplI386::FinishTransForHWP() {
+ m_2pc_trans_state = Trans_Done;
+ return true;
+}
+DNBArchImplI386::DBG DNBArchImplI386::GetDBGCheckpoint() {
+ return m_2pc_dbg_checkpoint;
+}
+
+uint32_t DNBArchImplI386::EnableHardwareWatchpoint(nub_addr_t addr,
+ nub_size_t size, bool read,
+ bool write,
+ bool also_set_on_task) {
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplI386::EnableHardwareWatchpoint("
+ "addr = 0x%llx, size = %llu, read = %u, "
+ "write = %u)",
+ (uint64_t)addr, (uint64_t)size, read, write);
+
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+
+ // Can only watch 1, 2, 4, or 8 bytes.
+ if (!(size == 1 || size == 2 || size == 4 || size == 8))
+ return INVALID_NUB_HW_INDEX;
+
+ // We must watch for either read or write
+ if (!read && !write)
+ return INVALID_NUB_HW_INDEX;
+
+ // Read the debug state
+ kern_return_t kret = GetDBGState(false);
+
+ if (kret == KERN_SUCCESS) {
+ // Check to make sure we have the needed hardware support
+ uint32_t i = 0;
+
+ DBG &debug_state = m_state.context.dbg;
+ for (i = 0; i < num_hw_watchpoints; ++i) {
+ if (IsWatchpointVacant(debug_state, i))
+ break;
+ }
+
+ // See if we found an available hw breakpoint slot above
+ if (i < num_hw_watchpoints) {
+ StartTransForHWP();
+
+ // Modify our local copy of the debug state, first.
+ SetWatchpoint(debug_state, i, addr, size, read, write);
+ // Now set the watch point in the inferior.
+ kret = SetDBGState(also_set_on_task);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplI386::"
+ "EnableHardwareWatchpoint() "
+ "SetDBGState() => 0x%8.8x.",
+ kret);
+
+ if (kret == KERN_SUCCESS)
+ return i;
+ else // Revert to the previous debug state voluntarily. The transaction
+ // coordinator knows that we have failed.
+ m_state.context.dbg = GetDBGCheckpoint();
+ } else {
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplI386::"
+ "EnableHardwareWatchpoint(): All "
+ "hardware resources (%u) are in use.",
+ num_hw_watchpoints);
+ }
+ }
+ return INVALID_NUB_HW_INDEX;
+}
+
+bool DNBArchImplI386::DisableHardwareWatchpoint(uint32_t hw_index,
+ bool also_set_on_task) {
+ kern_return_t kret = GetDBGState(false);
+
+ const uint32_t num_hw_points = NumSupportedHardwareWatchpoints();
+ if (kret == KERN_SUCCESS) {
+ DBG &debug_state = m_state.context.dbg;
+ if (hw_index < num_hw_points &&
+ !IsWatchpointVacant(debug_state, hw_index)) {
+ StartTransForHWP();
+
+ // Modify our local copy of the debug state, first.
+ ClearWatchpoint(debug_state, hw_index);
+ // Now disable the watch point in the inferior.
+ kret = SetDBGState(also_set_on_task);
+ DNBLogThreadedIf(LOG_WATCHPOINTS,
+ "DNBArchImplI386::DisableHardwareWatchpoint( %u )",
+ hw_index);
+
+ if (kret == KERN_SUCCESS)
+ return true;
+ else // Revert to the previous debug state voluntarily. The transaction
+ // coordinator knows that we have failed.
+ m_state.context.dbg = GetDBGCheckpoint();
+ }
+ }
+ return false;
+}
+
+// Iterate through the debug status register; return the index of the first hit.
+uint32_t DNBArchImplI386::GetHardwareWatchpointHit(nub_addr_t &addr) {
+ // Read the debug state
+ kern_return_t kret = GetDBGState(true);
+ DNBLogThreadedIf(
+ LOG_WATCHPOINTS,
+ "DNBArchImplI386::GetHardwareWatchpointHit() GetDBGState() => 0x%8.8x.",
+ kret);
+ if (kret == KERN_SUCCESS) {
+ DBG &debug_state = m_state.context.dbg;
+ uint32_t i, num = NumSupportedHardwareWatchpoints();
+ for (i = 0; i < num; ++i) {
+ if (IsWatchpointHit(debug_state, i)) {
+ addr = GetWatchAddress(debug_state, i);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplI386::"
+ "GetHardwareWatchpointHit() found => "
+ "%u (addr = 0x%llx).",
+ i, (uint64_t)addr);
+ return i;
+ }
+ }
+ }
+ return INVALID_NUB_HW_INDEX;
+}
+
+// Set the single step bit in the processor status register.
+kern_return_t DNBArchImplI386::EnableHardwareSingleStep(bool enable) {
+ if (GetGPRState(false) == KERN_SUCCESS) {
+ const uint32_t trace_bit = 0x100u;
+ if (enable)
+ m_state.context.gpr.__eflags |= trace_bit;
+ else
+ m_state.context.gpr.__eflags &= ~trace_bit;
+ return SetGPRState();
+ }
+ return m_state.GetError(e_regSetGPR, Read);
+}
+
+// Register information definitions
+
+#define DEFINE_GPR_PSEUDO_16(reg16, reg32) \
+ { \
+ e_regSetGPR, gpr_##reg16, #reg16, NULL, Uint, Hex, 2, 0, \
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, \
+ INVALID_NUB_REGNUM, g_contained_##reg32, g_invalidate_##reg32 \
+ }
+#define DEFINE_GPR_PSEUDO_8H(reg8, reg32) \
+ { \
+ e_regSetGPR, gpr_##reg8, #reg8, NULL, Uint, Hex, 1, 1, INVALID_NUB_REGNUM, \
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, \
+ g_contained_##reg32, g_invalidate_##reg32 \
+ }
+#define DEFINE_GPR_PSEUDO_8L(reg8, reg32) \
+ { \
+ e_regSetGPR, gpr_##reg8, #reg8, NULL, Uint, Hex, 1, 0, INVALID_NUB_REGNUM, \
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, \
+ g_contained_##reg32, g_invalidate_##reg32 \
+ }
+
+#define GPR_OFFSET(reg) (offsetof(DNBArchImplI386::GPR, __##reg))
+#define FPU_OFFSET(reg) \
+ (offsetof(DNBArchImplI386::FPU, __fpu_##reg) + \
+ offsetof(DNBArchImplI386::Context, fpu.no_avx))
+#define AVX_OFFSET(reg) \
+ (offsetof(DNBArchImplI386::AVX, __fpu_##reg) + \
+ offsetof(DNBArchImplI386::Context, fpu.avx))
+#define AVX512F_OFFSET(reg) \
+ (offsetof(DNBArchImplI386::AVX512F, __fpu_##reg) + \
+ offsetof(DNBArchImplI386::Context, fpu.avx512f))
+#define EXC_OFFSET(reg) \
+ (offsetof(DNBArchImplI386::EXC, __##reg) + \
+ offsetof(DNBArchImplI386::Context, exc))
+
+#define GPR_SIZE(reg) (sizeof(((DNBArchImplI386::GPR *)NULL)->__##reg))
+#define FPU_SIZE_UINT(reg) (sizeof(((DNBArchImplI386::FPU *)NULL)->__fpu_##reg))
+#define FPU_SIZE_MMST(reg) \
+ (sizeof(((DNBArchImplI386::FPU *)NULL)->__fpu_##reg.__mmst_reg))
+#define FPU_SIZE_XMM(reg) \
+ (sizeof(((DNBArchImplI386::FPU *)NULL)->__fpu_##reg.__xmm_reg))
+#define FPU_SIZE_YMM(reg) (32)
+#define FPU_SIZE_ZMM(reg) (64)
+#define EXC_SIZE(reg) (sizeof(((DNBArchImplI386::EXC *)NULL)->__##reg))
+
+// This does not accurately identify the location of ymm0...7 in
+// Context.fpu.avx. That is because there is a bunch of padding
+// in Context.fpu.avx that we don't need. Offset macros lay out
+// the register state that Debugserver transmits to the debugger
+// -- not to interpret the thread_get_state info.
+#define AVX_OFFSET_YMM(n) (AVX_OFFSET(xmm7) + FPU_SIZE_XMM(xmm7) + (32 * n))
+
+// TODO: Test this and come back.
+#define AVX512F_OFFSET_ZMM(n) (AVX_OFFSET_YMM(7) + FPU_SIZE_XMM(xmm7) + (64 * n))
+
+// These macros will auto define the register name, alt name, register size,
+// register offset, encoding, format and native register. This ensures that
+// the register state structures are defined correctly and have the correct
+// sizes and offsets.
+
+const char *g_contained_eax[] = {"eax", NULL};
+const char *g_contained_ebx[] = {"ebx", NULL};
+const char *g_contained_ecx[] = {"ecx", NULL};
+const char *g_contained_edx[] = {"edx", NULL};
+const char *g_contained_edi[] = {"edi", NULL};
+const char *g_contained_esi[] = {"esi", NULL};
+const char *g_contained_ebp[] = {"ebp", NULL};
+const char *g_contained_esp[] = {"esp", NULL};
+
+const char *g_invalidate_eax[] = {"eax", "ax", "ah", "al", NULL};
+const char *g_invalidate_ebx[] = {"ebx", "bx", "bh", "bl", NULL};
+const char *g_invalidate_ecx[] = {"ecx", "cx", "ch", "cl", NULL};
+const char *g_invalidate_edx[] = {"edx", "dx", "dh", "dl", NULL};
+const char *g_invalidate_edi[] = {"edi", "di", "dil", NULL};
+const char *g_invalidate_esi[] = {"esi", "si", "sil", NULL};
+const char *g_invalidate_ebp[] = {"ebp", "bp", "bpl", NULL};
+const char *g_invalidate_esp[] = {"esp", "sp", "spl", NULL};
+
+// General purpose registers for 64 bit
+const DNBRegisterInfo DNBArchImplI386::g_gpr_registers[] = {
+ {e_regSetGPR, gpr_eax, "eax", NULL, Uint, Hex, GPR_SIZE(eax),
+ GPR_OFFSET(eax), ehframe_eax, dwarf_eax, INVALID_NUB_REGNUM,
+ debugserver_eax, NULL, g_invalidate_eax},
+ {e_regSetGPR, gpr_ebx, "ebx", NULL, Uint, Hex, GPR_SIZE(ebx),
+ GPR_OFFSET(ebx), ehframe_ebx, dwarf_ebx, INVALID_NUB_REGNUM,
+ debugserver_ebx, NULL, g_invalidate_ebx},
+ {e_regSetGPR, gpr_ecx, "ecx", NULL, Uint, Hex, GPR_SIZE(ecx),
+ GPR_OFFSET(ecx), ehframe_ecx, dwarf_ecx, INVALID_NUB_REGNUM,
+ debugserver_ecx, NULL, g_invalidate_ecx},
+ {e_regSetGPR, gpr_edx, "edx", NULL, Uint, Hex, GPR_SIZE(edx),
+ GPR_OFFSET(edx), ehframe_edx, dwarf_edx, INVALID_NUB_REGNUM,
+ debugserver_edx, NULL, g_invalidate_edx},
+ {e_regSetGPR, gpr_edi, "edi", NULL, Uint, Hex, GPR_SIZE(edi),
+ GPR_OFFSET(edi), ehframe_edi, dwarf_edi, INVALID_NUB_REGNUM,
+ debugserver_edi, NULL, g_invalidate_edi},
+ {e_regSetGPR, gpr_esi, "esi", NULL, Uint, Hex, GPR_SIZE(esi),
+ GPR_OFFSET(esi), ehframe_esi, dwarf_esi, INVALID_NUB_REGNUM,
+ debugserver_esi, NULL, g_invalidate_esi},
+ {e_regSetGPR, gpr_ebp, "ebp", "fp", Uint, Hex, GPR_SIZE(ebp),
+ GPR_OFFSET(ebp), ehframe_ebp, dwarf_ebp, GENERIC_REGNUM_FP,
+ debugserver_ebp, NULL, g_invalidate_ebp},
+ {e_regSetGPR, gpr_esp, "esp", "sp", Uint, Hex, GPR_SIZE(esp),
+ GPR_OFFSET(esp), ehframe_esp, dwarf_esp, GENERIC_REGNUM_SP,
+ debugserver_esp, NULL, g_invalidate_esp},
+ {e_regSetGPR, gpr_ss, "ss", NULL, Uint, Hex, GPR_SIZE(ss), GPR_OFFSET(ss),
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, debugserver_ss,
+ NULL, NULL},
+ {e_regSetGPR, gpr_eflags, "eflags", "flags", Uint, Hex, GPR_SIZE(eflags),
+ GPR_OFFSET(eflags), ehframe_eflags, dwarf_eflags, GENERIC_REGNUM_FLAGS,
+ debugserver_eflags, NULL, NULL},
+ {e_regSetGPR, gpr_eip, "eip", "pc", Uint, Hex, GPR_SIZE(eip),
+ GPR_OFFSET(eip), ehframe_eip, dwarf_eip, GENERIC_REGNUM_PC,
+ debugserver_eip, NULL, NULL},
+ {e_regSetGPR, gpr_cs, "cs", NULL, Uint, Hex, GPR_SIZE(cs), GPR_OFFSET(cs),
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, debugserver_cs,
+ NULL, NULL},
+ {e_regSetGPR, gpr_ds, "ds", NULL, Uint, Hex, GPR_SIZE(ds), GPR_OFFSET(ds),
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, debugserver_ds,
+ NULL, NULL},
+ {e_regSetGPR, gpr_es, "es", NULL, Uint, Hex, GPR_SIZE(es), GPR_OFFSET(es),
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, debugserver_es,
+ NULL, NULL},
+ {e_regSetGPR, gpr_fs, "fs", NULL, Uint, Hex, GPR_SIZE(fs), GPR_OFFSET(fs),
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, debugserver_fs,
+ NULL, NULL},
+ {e_regSetGPR, gpr_gs, "gs", NULL, Uint, Hex, GPR_SIZE(gs), GPR_OFFSET(gs),
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, debugserver_gs,
+ NULL, NULL},
+ DEFINE_GPR_PSEUDO_16(ax, eax),
+ DEFINE_GPR_PSEUDO_16(bx, ebx),
+ DEFINE_GPR_PSEUDO_16(cx, ecx),
+ DEFINE_GPR_PSEUDO_16(dx, edx),
+ DEFINE_GPR_PSEUDO_16(di, edi),
+ DEFINE_GPR_PSEUDO_16(si, esi),
+ DEFINE_GPR_PSEUDO_16(bp, ebp),
+ DEFINE_GPR_PSEUDO_16(sp, esp),
+ DEFINE_GPR_PSEUDO_8H(ah, eax),
+ DEFINE_GPR_PSEUDO_8H(bh, ebx),
+ DEFINE_GPR_PSEUDO_8H(ch, ecx),
+ DEFINE_GPR_PSEUDO_8H(dh, edx),
+ DEFINE_GPR_PSEUDO_8L(al, eax),
+ DEFINE_GPR_PSEUDO_8L(bl, ebx),
+ DEFINE_GPR_PSEUDO_8L(cl, ecx),
+ DEFINE_GPR_PSEUDO_8L(dl, edx),
+ DEFINE_GPR_PSEUDO_8L(dil, edi),
+ DEFINE_GPR_PSEUDO_8L(sil, esi),
+ DEFINE_GPR_PSEUDO_8L(bpl, ebp),
+ DEFINE_GPR_PSEUDO_8L(spl, esp)};
+
+const DNBRegisterInfo DNBArchImplI386::g_fpu_registers_no_avx[] = {
+ {e_regSetFPU, fpu_fcw, "fctrl", NULL, Uint, Hex, FPU_SIZE_UINT(fcw),
+ FPU_OFFSET(fcw), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_fsw, "fstat", NULL, Uint, Hex, FPU_SIZE_UINT(fsw),
+ FPU_OFFSET(fsw), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_ftw, "ftag", NULL, Uint, Hex, 2 /* sizeof __fpu_ftw + sizeof __fpu_rsrv1 */,
+ FPU_OFFSET(ftw), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_fop, "fop", NULL, Uint, Hex, FPU_SIZE_UINT(fop),
+ FPU_OFFSET(fop), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_ip, "fioff", NULL, Uint, Hex, FPU_SIZE_UINT(ip),
+ FPU_OFFSET(ip), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_cs, "fiseg", NULL, Uint, Hex, FPU_SIZE_UINT(cs),
+ FPU_OFFSET(cs), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_dp, "fooff", NULL, Uint, Hex, FPU_SIZE_UINT(dp),
+ FPU_OFFSET(dp), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_ds, "foseg", NULL, Uint, Hex, FPU_SIZE_UINT(ds),
+ FPU_OFFSET(ds), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_mxcsr, "mxcsr", NULL, Uint, Hex, FPU_SIZE_UINT(mxcsr),
+ FPU_OFFSET(mxcsr), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_mxcsrmask, "mxcsrmask", NULL, Uint, Hex,
+ FPU_SIZE_UINT(mxcsrmask), FPU_OFFSET(mxcsrmask), INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL},
+
+ {e_regSetFPU, fpu_stmm0, "stmm0", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm0), FPU_OFFSET(stmm0), INVALID_NUB_REGNUM, dwarf_stmm0,
+ INVALID_NUB_REGNUM, debugserver_stmm0, NULL, NULL},
+ {e_regSetFPU, fpu_stmm1, "stmm1", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm1), FPU_OFFSET(stmm1), INVALID_NUB_REGNUM, dwarf_stmm1,
+ INVALID_NUB_REGNUM, debugserver_stmm1, NULL, NULL},
+ {e_regSetFPU, fpu_stmm2, "stmm2", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm2), FPU_OFFSET(stmm2), INVALID_NUB_REGNUM, dwarf_stmm2,
+ INVALID_NUB_REGNUM, debugserver_stmm2, NULL, NULL},
+ {e_regSetFPU, fpu_stmm3, "stmm3", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm3), FPU_OFFSET(stmm3), INVALID_NUB_REGNUM, dwarf_stmm3,
+ INVALID_NUB_REGNUM, debugserver_stmm3, NULL, NULL},
+ {e_regSetFPU, fpu_stmm4, "stmm4", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm4), FPU_OFFSET(stmm4), INVALID_NUB_REGNUM, dwarf_stmm4,
+ INVALID_NUB_REGNUM, debugserver_stmm4, NULL, NULL},
+ {e_regSetFPU, fpu_stmm5, "stmm5", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm5), FPU_OFFSET(stmm5), INVALID_NUB_REGNUM, dwarf_stmm5,
+ INVALID_NUB_REGNUM, debugserver_stmm5, NULL, NULL},
+ {e_regSetFPU, fpu_stmm6, "stmm6", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm6), FPU_OFFSET(stmm6), INVALID_NUB_REGNUM, dwarf_stmm6,
+ INVALID_NUB_REGNUM, debugserver_stmm6, NULL, NULL},
+ {e_regSetFPU, fpu_stmm7, "stmm7", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm7), FPU_OFFSET(stmm7), INVALID_NUB_REGNUM, dwarf_stmm7,
+ INVALID_NUB_REGNUM, debugserver_stmm7, NULL, NULL},
+
+ {e_regSetFPU, fpu_xmm0, "xmm0", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm0), FPU_OFFSET(xmm0), INVALID_NUB_REGNUM, dwarf_xmm0,
+ INVALID_NUB_REGNUM, debugserver_xmm0, NULL, NULL},
+ {e_regSetFPU, fpu_xmm1, "xmm1", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm1), FPU_OFFSET(xmm1), INVALID_NUB_REGNUM, dwarf_xmm1,
+ INVALID_NUB_REGNUM, debugserver_xmm1, NULL, NULL},
+ {e_regSetFPU, fpu_xmm2, "xmm2", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm2), FPU_OFFSET(xmm2), INVALID_NUB_REGNUM, dwarf_xmm2,
+ INVALID_NUB_REGNUM, debugserver_xmm2, NULL, NULL},
+ {e_regSetFPU, fpu_xmm3, "xmm3", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm3), FPU_OFFSET(xmm3), INVALID_NUB_REGNUM, dwarf_xmm3,
+ INVALID_NUB_REGNUM, debugserver_xmm3, NULL, NULL},
+ {e_regSetFPU, fpu_xmm4, "xmm4", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm4), FPU_OFFSET(xmm4), INVALID_NUB_REGNUM, dwarf_xmm4,
+ INVALID_NUB_REGNUM, debugserver_xmm4, NULL, NULL},
+ {e_regSetFPU, fpu_xmm5, "xmm5", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm5), FPU_OFFSET(xmm5), INVALID_NUB_REGNUM, dwarf_xmm5,
+ INVALID_NUB_REGNUM, debugserver_xmm5, NULL, NULL},
+ {e_regSetFPU, fpu_xmm6, "xmm6", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm6), FPU_OFFSET(xmm6), INVALID_NUB_REGNUM, dwarf_xmm6,
+ INVALID_NUB_REGNUM, debugserver_xmm6, NULL, NULL},
+ {e_regSetFPU, fpu_xmm7, "xmm7", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm7), FPU_OFFSET(xmm7), INVALID_NUB_REGNUM, dwarf_xmm7,
+ INVALID_NUB_REGNUM, debugserver_xmm7, NULL, NULL}};
+
+static const char *g_contained_ymm0[] = {"ymm0", NULL};
+static const char *g_contained_ymm1[] = {"ymm1", NULL};
+static const char *g_contained_ymm2[] = {"ymm2", NULL};
+static const char *g_contained_ymm3[] = {"ymm3", NULL};
+static const char *g_contained_ymm4[] = {"ymm4", NULL};
+static const char *g_contained_ymm5[] = {"ymm5", NULL};
+static const char *g_contained_ymm6[] = {"ymm6", NULL};
+static const char *g_contained_ymm7[] = {"ymm7", NULL};
+
+const DNBRegisterInfo DNBArchImplI386::g_fpu_registers_avx[] = {
+ {e_regSetFPU, fpu_fcw, "fctrl", NULL, Uint, Hex, FPU_SIZE_UINT(fcw),
+ AVX_OFFSET(fcw), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_fsw, "fstat", NULL, Uint, Hex, FPU_SIZE_UINT(fsw),
+ AVX_OFFSET(fsw), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_ftw, "ftag", NULL, Uint, Hex, 2 /* sizeof __fpu_ftw + sizeof __fpu_rsrv1 */,
+ AVX_OFFSET(ftw), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_fop, "fop", NULL, Uint, Hex, FPU_SIZE_UINT(fop),
+ AVX_OFFSET(fop), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_ip, "fioff", NULL, Uint, Hex, FPU_SIZE_UINT(ip),
+ AVX_OFFSET(ip), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_cs, "fiseg", NULL, Uint, Hex, FPU_SIZE_UINT(cs),
+ AVX_OFFSET(cs), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_dp, "fooff", NULL, Uint, Hex, FPU_SIZE_UINT(dp),
+ AVX_OFFSET(dp), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_ds, "foseg", NULL, Uint, Hex, FPU_SIZE_UINT(ds),
+ AVX_OFFSET(ds), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_mxcsr, "mxcsr", NULL, Uint, Hex, FPU_SIZE_UINT(mxcsr),
+ AVX_OFFSET(mxcsr), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_mxcsrmask, "mxcsrmask", NULL, Uint, Hex,
+ FPU_SIZE_UINT(mxcsrmask), AVX_OFFSET(mxcsrmask), INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL},
+
+ {e_regSetFPU, fpu_stmm0, "stmm0", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm0), AVX_OFFSET(stmm0), INVALID_NUB_REGNUM, dwarf_stmm0,
+ INVALID_NUB_REGNUM, debugserver_stmm0, NULL, NULL},
+ {e_regSetFPU, fpu_stmm1, "stmm1", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm1), AVX_OFFSET(stmm1), INVALID_NUB_REGNUM, dwarf_stmm1,
+ INVALID_NUB_REGNUM, debugserver_stmm1, NULL, NULL},
+ {e_regSetFPU, fpu_stmm2, "stmm2", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm2), AVX_OFFSET(stmm2), INVALID_NUB_REGNUM, dwarf_stmm2,
+ INVALID_NUB_REGNUM, debugserver_stmm2, NULL, NULL},
+ {e_regSetFPU, fpu_stmm3, "stmm3", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm3), AVX_OFFSET(stmm3), INVALID_NUB_REGNUM, dwarf_stmm3,
+ INVALID_NUB_REGNUM, debugserver_stmm3, NULL, NULL},
+ {e_regSetFPU, fpu_stmm4, "stmm4", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm4), AVX_OFFSET(stmm4), INVALID_NUB_REGNUM, dwarf_stmm4,
+ INVALID_NUB_REGNUM, debugserver_stmm4, NULL, NULL},
+ {e_regSetFPU, fpu_stmm5, "stmm5", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm5), AVX_OFFSET(stmm5), INVALID_NUB_REGNUM, dwarf_stmm5,
+ INVALID_NUB_REGNUM, debugserver_stmm5, NULL, NULL},
+ {e_regSetFPU, fpu_stmm6, "stmm6", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm6), AVX_OFFSET(stmm6), INVALID_NUB_REGNUM, dwarf_stmm6,
+ INVALID_NUB_REGNUM, debugserver_stmm6, NULL, NULL},
+ {e_regSetFPU, fpu_stmm7, "stmm7", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm7), AVX_OFFSET(stmm7), INVALID_NUB_REGNUM, dwarf_stmm7,
+ INVALID_NUB_REGNUM, debugserver_stmm7, NULL, NULL},
+
+ {e_regSetFPU, fpu_ymm0, "ymm0", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_YMM(ymm0), AVX_OFFSET_YMM(0), INVALID_NUB_REGNUM, dwarf_ymm0,
+ INVALID_NUB_REGNUM, debugserver_ymm0, NULL, NULL},
+ {e_regSetFPU, fpu_ymm1, "ymm1", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_YMM(ymm1), AVX_OFFSET_YMM(1), INVALID_NUB_REGNUM, dwarf_ymm1,
+ INVALID_NUB_REGNUM, debugserver_ymm1, NULL, NULL},
+ {e_regSetFPU, fpu_ymm2, "ymm2", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_YMM(ymm2), AVX_OFFSET_YMM(2), INVALID_NUB_REGNUM, dwarf_ymm2,
+ INVALID_NUB_REGNUM, debugserver_ymm2, NULL, NULL},
+ {e_regSetFPU, fpu_ymm3, "ymm3", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_YMM(ymm3), AVX_OFFSET_YMM(3), INVALID_NUB_REGNUM, dwarf_ymm3,
+ INVALID_NUB_REGNUM, debugserver_ymm3, NULL, NULL},
+ {e_regSetFPU, fpu_ymm4, "ymm4", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_YMM(ymm4), AVX_OFFSET_YMM(4), INVALID_NUB_REGNUM, dwarf_ymm4,
+ INVALID_NUB_REGNUM, debugserver_ymm4, NULL, NULL},
+ {e_regSetFPU, fpu_ymm5, "ymm5", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_YMM(ymm5), AVX_OFFSET_YMM(5), INVALID_NUB_REGNUM, dwarf_ymm5,
+ INVALID_NUB_REGNUM, debugserver_ymm5, NULL, NULL},
+ {e_regSetFPU, fpu_ymm6, "ymm6", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_YMM(ymm6), AVX_OFFSET_YMM(6), INVALID_NUB_REGNUM, dwarf_ymm6,
+ INVALID_NUB_REGNUM, debugserver_ymm6, NULL, NULL},
+ {e_regSetFPU, fpu_ymm7, "ymm7", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_YMM(ymm7), AVX_OFFSET_YMM(7), INVALID_NUB_REGNUM, dwarf_ymm7,
+ INVALID_NUB_REGNUM, debugserver_ymm7, NULL, NULL},
+
+ {e_regSetFPU, fpu_xmm0, "xmm0", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm0), 0, INVALID_NUB_REGNUM, dwarf_xmm0, INVALID_NUB_REGNUM,
+ debugserver_xmm0, g_contained_ymm0, NULL},
+ {e_regSetFPU, fpu_xmm1, "xmm1", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm1), 0, INVALID_NUB_REGNUM, dwarf_xmm1, INVALID_NUB_REGNUM,
+ debugserver_xmm1, g_contained_ymm1, NULL},
+ {e_regSetFPU, fpu_xmm2, "xmm2", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm2), 0, INVALID_NUB_REGNUM, dwarf_xmm2, INVALID_NUB_REGNUM,
+ debugserver_xmm2, g_contained_ymm2, NULL},
+ {e_regSetFPU, fpu_xmm3, "xmm3", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm3), 0, INVALID_NUB_REGNUM, dwarf_xmm3, INVALID_NUB_REGNUM,
+ debugserver_xmm3, g_contained_ymm3, NULL},
+ {e_regSetFPU, fpu_xmm4, "xmm4", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm4), 0, INVALID_NUB_REGNUM, dwarf_xmm4, INVALID_NUB_REGNUM,
+ debugserver_xmm4, g_contained_ymm4, NULL},
+ {e_regSetFPU, fpu_xmm5, "xmm5", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm5), 0, INVALID_NUB_REGNUM, dwarf_xmm5, INVALID_NUB_REGNUM,
+ debugserver_xmm5, g_contained_ymm5, NULL},
+ {e_regSetFPU, fpu_xmm6, "xmm6", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm6), 0, INVALID_NUB_REGNUM, dwarf_xmm6, INVALID_NUB_REGNUM,
+ debugserver_xmm6, g_contained_ymm6, NULL},
+ {e_regSetFPU, fpu_xmm7, "xmm7", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm7), 0, INVALID_NUB_REGNUM, dwarf_xmm7, INVALID_NUB_REGNUM,
+ debugserver_xmm7, g_contained_ymm7, NULL},
+
+};
+
+
+#define STR(s) #s
+
+#define ZMM_REG_DEF(reg) \
+ { \
+ e_regSetFPU, fpu_zmm##reg, STR(zmm##reg), NULL, Vector, VectorOfUInt8, \
+ FPU_SIZE_ZMM(zmm##reg), AVX512F_OFFSET_ZMM(reg), INVALID_NUB_REGNUM, \
+ dwarf_zmm##reg, INVALID_NUB_REGNUM, debugserver_zmm##reg, NULL, NULL \
+ }
+
+#define YMM_REG_ALIAS(reg) \
+ { \
+ e_regSetFPU, fpu_ymm##reg, STR(ymm##reg), NULL, Vector, VectorOfUInt8, \
+ FPU_SIZE_YMM(ymm##reg), 0, INVALID_NUB_REGNUM, dwarf_ymm##reg, \
+ INVALID_NUB_REGNUM, debugserver_ymm##reg, g_contained_zmm##reg, NULL \
+ }
+
+#define XMM_REG_ALIAS(reg) \
+ { \
+ e_regSetFPU, fpu_xmm##reg, STR(xmm##reg), NULL, Vector, VectorOfUInt8, \
+ FPU_SIZE_XMM(xmm##reg), 0, INVALID_NUB_REGNUM, dwarf_xmm##reg, \
+ INVALID_NUB_REGNUM, debugserver_xmm##reg, g_contained_zmm##reg, NULL \
+ }
+
+#define AVX512_K_REG_DEF(reg) \
+ { \
+ e_regSetFPU, fpu_k##reg, STR(k##reg), NULL, Vector, VectorOfUInt8, 8, \
+ AVX512F_OFFSET(k##reg), dwarf_k##reg, dwarf_k##reg, -1U, \
+ debugserver_k##reg, NULL, NULL \
+ }
+
+static const char *g_contained_zmm0[] = {"zmm0", NULL};
+static const char *g_contained_zmm1[] = {"zmm1", NULL};
+static const char *g_contained_zmm2[] = {"zmm2", NULL};
+static const char *g_contained_zmm3[] = {"zmm3", NULL};
+static const char *g_contained_zmm4[] = {"zmm4", NULL};
+static const char *g_contained_zmm5[] = {"zmm5", NULL};
+static const char *g_contained_zmm6[] = {"zmm6", NULL};
+static const char *g_contained_zmm7[] = {"zmm7", NULL};
+
+const DNBRegisterInfo DNBArchImplI386::g_fpu_registers_avx512f[] = {
+ {e_regSetFPU, fpu_fcw, "fctrl", NULL, Uint, Hex, FPU_SIZE_UINT(fcw),
+ AVX_OFFSET(fcw), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_fsw, "fstat", NULL, Uint, Hex, FPU_SIZE_UINT(fsw),
+ AVX_OFFSET(fsw), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_ftw, "ftag", NULL, Uint, Hex, 2 /* sizeof __fpu_ftw + sizeof __fpu_rsrv1 */,
+ FPU_OFFSET(ftw), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_fop, "fop", NULL, Uint, Hex, FPU_SIZE_UINT(fop),
+ AVX_OFFSET(fop), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_ip, "fioff", NULL, Uint, Hex, FPU_SIZE_UINT(ip),
+ AVX_OFFSET(ip), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_cs, "fiseg", NULL, Uint, Hex, FPU_SIZE_UINT(cs),
+ AVX_OFFSET(cs), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_dp, "fooff", NULL, Uint, Hex, FPU_SIZE_UINT(dp),
+ AVX_OFFSET(dp), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_ds, "foseg", NULL, Uint, Hex, FPU_SIZE_UINT(ds),
+ AVX_OFFSET(ds), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_mxcsr, "mxcsr", NULL, Uint, Hex, FPU_SIZE_UINT(mxcsr),
+ AVX_OFFSET(mxcsr), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetFPU, fpu_mxcsrmask, "mxcsrmask", NULL, Uint, Hex,
+ FPU_SIZE_UINT(mxcsrmask), AVX_OFFSET(mxcsrmask), INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL},
+
+ {e_regSetFPU, fpu_stmm0, "stmm0", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm0), AVX_OFFSET(stmm0), INVALID_NUB_REGNUM, dwarf_stmm0,
+ INVALID_NUB_REGNUM, debugserver_stmm0, NULL, NULL},
+ {e_regSetFPU, fpu_stmm1, "stmm1", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm1), AVX_OFFSET(stmm1), INVALID_NUB_REGNUM, dwarf_stmm1,
+ INVALID_NUB_REGNUM, debugserver_stmm1, NULL, NULL},
+ {e_regSetFPU, fpu_stmm2, "stmm2", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm2), AVX_OFFSET(stmm2), INVALID_NUB_REGNUM, dwarf_stmm2,
+ INVALID_NUB_REGNUM, debugserver_stmm2, NULL, NULL},
+ {e_regSetFPU, fpu_stmm3, "stmm3", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm3), AVX_OFFSET(stmm3), INVALID_NUB_REGNUM, dwarf_stmm3,
+ INVALID_NUB_REGNUM, debugserver_stmm3, NULL, NULL},
+ {e_regSetFPU, fpu_stmm4, "stmm4", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm4), AVX_OFFSET(stmm4), INVALID_NUB_REGNUM, dwarf_stmm4,
+ INVALID_NUB_REGNUM, debugserver_stmm4, NULL, NULL},
+ {e_regSetFPU, fpu_stmm5, "stmm5", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm5), AVX_OFFSET(stmm5), INVALID_NUB_REGNUM, dwarf_stmm5,
+ INVALID_NUB_REGNUM, debugserver_stmm5, NULL, NULL},
+ {e_regSetFPU, fpu_stmm6, "stmm6", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm6), AVX_OFFSET(stmm6), INVALID_NUB_REGNUM, dwarf_stmm6,
+ INVALID_NUB_REGNUM, debugserver_stmm6, NULL, NULL},
+ {e_regSetFPU, fpu_stmm7, "stmm7", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm7), AVX_OFFSET(stmm7), INVALID_NUB_REGNUM, dwarf_stmm7,
+ INVALID_NUB_REGNUM, debugserver_stmm7, NULL, NULL},
+
+ AVX512_K_REG_DEF(0),
+ AVX512_K_REG_DEF(1),
+ AVX512_K_REG_DEF(2),
+ AVX512_K_REG_DEF(3),
+ AVX512_K_REG_DEF(4),
+ AVX512_K_REG_DEF(5),
+ AVX512_K_REG_DEF(6),
+ AVX512_K_REG_DEF(7),
+
+ ZMM_REG_DEF(0),
+ ZMM_REG_DEF(1),
+ ZMM_REG_DEF(2),
+ ZMM_REG_DEF(3),
+ ZMM_REG_DEF(4),
+ ZMM_REG_DEF(5),
+ ZMM_REG_DEF(6),
+ ZMM_REG_DEF(7),
+
+ YMM_REG_ALIAS(0),
+ YMM_REG_ALIAS(1),
+ YMM_REG_ALIAS(2),
+ YMM_REG_ALIAS(3),
+ YMM_REG_ALIAS(4),
+ YMM_REG_ALIAS(5),
+ YMM_REG_ALIAS(6),
+ YMM_REG_ALIAS(7),
+
+ XMM_REG_ALIAS(0),
+ XMM_REG_ALIAS(1),
+ XMM_REG_ALIAS(2),
+ XMM_REG_ALIAS(3),
+ XMM_REG_ALIAS(4),
+ XMM_REG_ALIAS(5),
+ XMM_REG_ALIAS(6),
+ XMM_REG_ALIAS(7)
+
+};
+
+const DNBRegisterInfo DNBArchImplI386::g_exc_registers[] = {
+ {e_regSetEXC, exc_trapno, "trapno", NULL, Uint, Hex, EXC_SIZE(trapno),
+ EXC_OFFSET(trapno), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetEXC, exc_err, "err", NULL, Uint, Hex, EXC_SIZE(err),
+ EXC_OFFSET(err), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL},
+ {e_regSetEXC, exc_faultvaddr, "faultvaddr", NULL, Uint, Hex,
+ EXC_SIZE(faultvaddr), EXC_OFFSET(faultvaddr), INVALID_NUB_REGNUM,
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, NULL, NULL}};
+
+// Number of registers in each register set
+const size_t DNBArchImplI386::k_num_gpr_registers =
+ sizeof(g_gpr_registers) / sizeof(DNBRegisterInfo);
+const size_t DNBArchImplI386::k_num_fpu_registers_no_avx =
+ sizeof(g_fpu_registers_no_avx) / sizeof(DNBRegisterInfo);
+const size_t DNBArchImplI386::k_num_fpu_registers_avx =
+ sizeof(g_fpu_registers_avx) / sizeof(DNBRegisterInfo);
+const size_t DNBArchImplI386::k_num_fpu_registers_avx512f =
+ sizeof(g_fpu_registers_avx512f) / sizeof(DNBRegisterInfo);
+const size_t DNBArchImplI386::k_num_exc_registers =
+ sizeof(g_exc_registers) / sizeof(DNBRegisterInfo);
+const size_t DNBArchImplI386::k_num_all_registers_no_avx =
+ k_num_gpr_registers + k_num_fpu_registers_no_avx + k_num_exc_registers;
+const size_t DNBArchImplI386::k_num_all_registers_avx =
+ k_num_gpr_registers + k_num_fpu_registers_avx + k_num_exc_registers;
+const size_t DNBArchImplI386::k_num_all_registers_avx512f =
+ k_num_gpr_registers + k_num_fpu_registers_avx512f + k_num_exc_registers;
+
+// Register set definitions. The first definitions at register set index
+// of zero is for all registers, followed by other registers sets. The
+// register information for the all register set need not be filled in.
+const DNBRegisterSetInfo DNBArchImplI386::g_reg_sets_no_avx[] = {
+ {"i386 Registers", NULL, k_num_all_registers_no_avx},
+ {"General Purpose Registers", g_gpr_registers, k_num_gpr_registers},
+ {"Floating Point Registers", g_fpu_registers_no_avx,
+ k_num_fpu_registers_no_avx},
+ {"Exception State Registers", g_exc_registers, k_num_exc_registers}};
+
+const DNBRegisterSetInfo DNBArchImplI386::g_reg_sets_avx[] = {
+ {"i386 Registers", NULL, k_num_all_registers_avx},
+ {"General Purpose Registers", g_gpr_registers, k_num_gpr_registers},
+ {"Floating Point Registers", g_fpu_registers_avx, k_num_fpu_registers_avx},
+ {"Exception State Registers", g_exc_registers, k_num_exc_registers}};
+
+const DNBRegisterSetInfo DNBArchImplI386::g_reg_sets_avx512f[] = {
+ {"i386 Registers", NULL, k_num_all_registers_avx512f},
+ {"General Purpose Registers", g_gpr_registers, k_num_gpr_registers},
+ {"Floating Point Registers", g_fpu_registers_avx512f,
+ k_num_fpu_registers_avx512f},
+ {"Exception State Registers", g_exc_registers, k_num_exc_registers}};
+
+// Total number of register sets for this architecture
+const size_t DNBArchImplI386::k_num_register_sets =
+ sizeof(g_reg_sets_avx) / sizeof(DNBRegisterSetInfo);
+
+DNBArchProtocol *DNBArchImplI386::Create(MachThread *thread) {
+ DNBArchImplI386 *obj = new DNBArchImplI386(thread);
+ return obj;
+}
+
+const uint8_t *DNBArchImplI386::SoftwareBreakpointOpcode(nub_size_t byte_size) {
+ static const uint8_t g_breakpoint_opcode[] = {0xCC};
+ if (byte_size == 1)
+ return g_breakpoint_opcode;
+ return NULL;
+}
+
+const DNBRegisterSetInfo *
+DNBArchImplI386::GetRegisterSetInfo(nub_size_t *num_reg_sets) {
+ *num_reg_sets = k_num_register_sets;
+ if (CPUHasAVX512f() || FORCE_AVX_REGS)
+ return g_reg_sets_avx512f;
+ if (CPUHasAVX())
+ return g_reg_sets_avx;
+ else
+ return g_reg_sets_no_avx;
+}
+
+void DNBArchImplI386::Initialize() {
+ DNBArchPluginInfo arch_plugin_info = {
+ CPU_TYPE_I386, DNBArchImplI386::Create,
+ DNBArchImplI386::GetRegisterSetInfo,
+ DNBArchImplI386::SoftwareBreakpointOpcode};
+
+ // Register this arch plug-in with the main protocol class
+ DNBArchProtocol::RegisterArchPlugin(arch_plugin_info);
+}
+
+bool DNBArchImplI386::GetRegisterValue(uint32_t set, uint32_t reg,
+ DNBRegisterValue *value) {
+ if (set == REGISTER_SET_GENERIC) {
+ switch (reg) {
+ case GENERIC_REGNUM_PC: // Program Counter
+ set = e_regSetGPR;
+ reg = gpr_eip;
+ break;
+
+ case GENERIC_REGNUM_SP: // Stack Pointer
+ set = e_regSetGPR;
+ reg = gpr_esp;
+ break;
+
+ case GENERIC_REGNUM_FP: // Frame Pointer
+ set = e_regSetGPR;
+ reg = gpr_ebp;
+ break;
+
+ case GENERIC_REGNUM_FLAGS: // Processor flags register
+ set = e_regSetGPR;
+ reg = gpr_eflags;
+ break;
+
+ case GENERIC_REGNUM_RA: // Return Address
+ default:
+ return false;
+ }
+ }
+
+ if (GetRegisterState(set, false) != KERN_SUCCESS)
+ return false;
+
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo) {
+ value->info = *regInfo;
+ switch (set) {
+ case e_regSetGPR:
+ if (reg < k_num_gpr_registers) {
+ value->value.uint32 = ((uint32_t *)(&m_state.context.gpr))[reg];
+ return true;
+ }
+ break;
+
+ case e_regSetFPU:
+ if (reg > fpu_xmm7 && !(CPUHasAVX() || FORCE_AVX_REGS))
+ return false;
+ if (reg > fpu_ymm7 && !(CPUHasAVX512f() || FORCE_AVX_REGS))
+ return false;
+ switch (reg) {
+ case fpu_fcw:
+ value->value.uint16 =
+ *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fcw));
+ return true;
+ case fpu_fsw:
+ value->value.uint16 =
+ *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fsw));
+ return true;
+ case fpu_ftw:
+ memcpy (&value->value.uint16, &m_state.context.fpu.no_avx.__fpu_ftw, 2);
+ return true;
+ case fpu_fop:
+ value->value.uint16 = m_state.context.fpu.no_avx.__fpu_fop;
+ return true;
+ case fpu_ip:
+ value->value.uint32 = m_state.context.fpu.no_avx.__fpu_ip;
+ return true;
+ case fpu_cs:
+ value->value.uint16 = m_state.context.fpu.no_avx.__fpu_cs;
+ return true;
+ case fpu_dp:
+ value->value.uint32 = m_state.context.fpu.no_avx.__fpu_dp;
+ return true;
+ case fpu_ds:
+ value->value.uint16 = m_state.context.fpu.no_avx.__fpu_ds;
+ return true;
+ case fpu_mxcsr:
+ value->value.uint32 = m_state.context.fpu.no_avx.__fpu_mxcsr;
+ return true;
+ case fpu_mxcsrmask:
+ value->value.uint32 = m_state.context.fpu.no_avx.__fpu_mxcsrmask;
+ return true;
+
+ case fpu_stmm0:
+ memcpy(&value->value.uint8,
+ m_state.context.fpu.no_avx.__fpu_stmm0.__mmst_reg, 10);
+ return true;
+ case fpu_stmm1:
+ memcpy(&value->value.uint8,
+ m_state.context.fpu.no_avx.__fpu_stmm1.__mmst_reg, 10);
+ return true;
+ case fpu_stmm2:
+ memcpy(&value->value.uint8,
+ m_state.context.fpu.no_avx.__fpu_stmm2.__mmst_reg, 10);
+ return true;
+ case fpu_stmm3:
+ memcpy(&value->value.uint8,
+ m_state.context.fpu.no_avx.__fpu_stmm3.__mmst_reg, 10);
+ return true;
+ case fpu_stmm4:
+ memcpy(&value->value.uint8,
+ m_state.context.fpu.no_avx.__fpu_stmm4.__mmst_reg, 10);
+ return true;
+ case fpu_stmm5:
+ memcpy(&value->value.uint8,
+ m_state.context.fpu.no_avx.__fpu_stmm5.__mmst_reg, 10);
+ return true;
+ case fpu_stmm6:
+ memcpy(&value->value.uint8,
+ m_state.context.fpu.no_avx.__fpu_stmm6.__mmst_reg, 10);
+ return true;
+ case fpu_stmm7:
+ memcpy(&value->value.uint8,
+ m_state.context.fpu.no_avx.__fpu_stmm7.__mmst_reg, 10);
+ return true;
+
+ case fpu_xmm0:
+ memcpy(&value->value.uint8,
+ m_state.context.fpu.no_avx.__fpu_xmm0.__xmm_reg, 16);
+ return true;
+ case fpu_xmm1:
+ memcpy(&value->value.uint8,
+ m_state.context.fpu.no_avx.__fpu_xmm1.__xmm_reg, 16);
+ return true;
+ case fpu_xmm2:
+ memcpy(&value->value.uint8,
+ m_state.context.fpu.no_avx.__fpu_xmm2.__xmm_reg, 16);
+ return true;
+ case fpu_xmm3:
+ memcpy(&value->value.uint8,
+ m_state.context.fpu.no_avx.__fpu_xmm3.__xmm_reg, 16);
+ return true;
+ case fpu_xmm4:
+ memcpy(&value->value.uint8,
+ m_state.context.fpu.no_avx.__fpu_xmm4.__xmm_reg, 16);
+ return true;
+ case fpu_xmm5:
+ memcpy(&value->value.uint8,
+ m_state.context.fpu.no_avx.__fpu_xmm5.__xmm_reg, 16);
+ return true;
+ case fpu_xmm6:
+ memcpy(&value->value.uint8,
+ m_state.context.fpu.no_avx.__fpu_xmm6.__xmm_reg, 16);
+ return true;
+ case fpu_xmm7:
+ memcpy(&value->value.uint8,
+ m_state.context.fpu.no_avx.__fpu_xmm7.__xmm_reg, 16);
+ return true;
+
+#define MEMCPY_YMM(n) \
+ memcpy(&value->value.uint8, m_state.context.fpu.avx.__fpu_xmm##n.__xmm_reg, \
+ 16); \
+ memcpy((&value->value.uint8) + 16, \
+ m_state.context.fpu.avx.__fpu_ymmh##n.__xmm_reg, 16);
+ case fpu_ymm0:
+ MEMCPY_YMM(0);
+ return true;
+ case fpu_ymm1:
+ MEMCPY_YMM(1);
+ return true;
+ case fpu_ymm2:
+ MEMCPY_YMM(2);
+ return true;
+ case fpu_ymm3:
+ MEMCPY_YMM(3);
+ return true;
+ case fpu_ymm4:
+ MEMCPY_YMM(4);
+ return true;
+ case fpu_ymm5:
+ MEMCPY_YMM(5);
+ return true;
+ case fpu_ymm6:
+ MEMCPY_YMM(6);
+ return true;
+ case fpu_ymm7:
+ MEMCPY_YMM(7);
+ return true;
+#undef MEMCPY_YMM
+
+ case fpu_k0:
+ case fpu_k1:
+ case fpu_k2:
+ case fpu_k3:
+ case fpu_k4:
+ case fpu_k5:
+ case fpu_k6:
+ case fpu_k7:
+ memcpy((&value->value.uint8),
+ &m_state.context.fpu.avx512f.__fpu_k0 + (reg - fpu_k0), 8);
+ return true;
+ case fpu_zmm0:
+ case fpu_zmm1:
+ case fpu_zmm2:
+ case fpu_zmm3:
+ case fpu_zmm4:
+ case fpu_zmm5:
+ case fpu_zmm6:
+ case fpu_zmm7:
+ memcpy(&value->value.uint8,
+ &m_state.context.fpu.avx512f.__fpu_xmm0 + (reg - fpu_zmm0), 16);
+ memcpy(&value->value.uint8 + 16,
+ &m_state.context.fpu.avx512f.__fpu_ymmh0 + (reg - fpu_zmm0), 16);
+ memcpy(&value->value.uint8 + 32,
+ &m_state.context.fpu.avx512f.__fpu_zmmh0 + (reg - fpu_zmm0), 32);
+ return true;
+ }
+ break;
+
+ case e_regSetEXC:
+ if (reg < k_num_exc_registers) {
+ value->value.uint32 = (&m_state.context.exc.__trapno)[reg];
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+bool DNBArchImplI386::SetRegisterValue(uint32_t set, uint32_t reg,
+ const DNBRegisterValue *value) {
+ if (set == REGISTER_SET_GENERIC) {
+ switch (reg) {
+ case GENERIC_REGNUM_PC: // Program Counter
+ set = e_regSetGPR;
+ reg = gpr_eip;
+ break;
+
+ case GENERIC_REGNUM_SP: // Stack Pointer
+ set = e_regSetGPR;
+ reg = gpr_esp;
+ break;
+
+ case GENERIC_REGNUM_FP: // Frame Pointer
+ set = e_regSetGPR;
+ reg = gpr_ebp;
+ break;
+
+ case GENERIC_REGNUM_FLAGS: // Processor flags register
+ set = e_regSetGPR;
+ reg = gpr_eflags;
+ break;
+
+ case GENERIC_REGNUM_RA: // Return Address
+ default:
+ return false;
+ }
+ }
+
+ if (GetRegisterState(set, false) != KERN_SUCCESS)
+ return false;
+
+ bool success = false;
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo) {
+ switch (set) {
+ case e_regSetGPR:
+ if (reg < k_num_gpr_registers) {
+ ((uint32_t *)(&m_state.context.gpr))[reg] = value->value.uint32;
+ success = true;
+ }
+ break;
+
+ case e_regSetFPU:
+ if (reg > fpu_xmm7 && !(CPUHasAVX() || FORCE_AVX_REGS))
+ return false;
+ if (reg > fpu_ymm7 && !(CPUHasAVX512f() || FORCE_AVX_REGS))
+ return false;
+ switch (reg) {
+ case fpu_fcw:
+ *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fcw)) =
+ value->value.uint16;
+ success = true;
+ break;
+ case fpu_fsw:
+ *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fsw)) =
+ value->value.uint16;
+ success = true;
+ break;
+ case fpu_ftw:
+ memcpy (&m_state.context.fpu.no_avx.__fpu_ftw, &value->value.uint16, 2);
+ success = true;
+ break;
+ case fpu_fop:
+ m_state.context.fpu.no_avx.__fpu_fop = value->value.uint16;
+ success = true;
+ break;
+ case fpu_ip:
+ m_state.context.fpu.no_avx.__fpu_ip = value->value.uint32;
+ success = true;
+ break;
+ case fpu_cs:
+ m_state.context.fpu.no_avx.__fpu_cs = value->value.uint16;
+ success = true;
+ break;
+ case fpu_dp:
+ m_state.context.fpu.no_avx.__fpu_dp = value->value.uint32;
+ success = true;
+ break;
+ case fpu_ds:
+ m_state.context.fpu.no_avx.__fpu_ds = value->value.uint16;
+ success = true;
+ break;
+ case fpu_mxcsr:
+ m_state.context.fpu.no_avx.__fpu_mxcsr = value->value.uint32;
+ success = true;
+ break;
+ case fpu_mxcsrmask:
+ m_state.context.fpu.no_avx.__fpu_mxcsrmask = value->value.uint32;
+ success = true;
+ break;
+
+ case fpu_stmm0:
+ memcpy(m_state.context.fpu.no_avx.__fpu_stmm0.__mmst_reg,
+ &value->value.uint8, 10);
+ success = true;
+ break;
+ case fpu_stmm1:
+ memcpy(m_state.context.fpu.no_avx.__fpu_stmm1.__mmst_reg,
+ &value->value.uint8, 10);
+ success = true;
+ break;
+ case fpu_stmm2:
+ memcpy(m_state.context.fpu.no_avx.__fpu_stmm2.__mmst_reg,
+ &value->value.uint8, 10);
+ success = true;
+ break;
+ case fpu_stmm3:
+ memcpy(m_state.context.fpu.no_avx.__fpu_stmm3.__mmst_reg,
+ &value->value.uint8, 10);
+ success = true;
+ break;
+ case fpu_stmm4:
+ memcpy(m_state.context.fpu.no_avx.__fpu_stmm4.__mmst_reg,
+ &value->value.uint8, 10);
+ success = true;
+ break;
+ case fpu_stmm5:
+ memcpy(m_state.context.fpu.no_avx.__fpu_stmm5.__mmst_reg,
+ &value->value.uint8, 10);
+ success = true;
+ break;
+ case fpu_stmm6:
+ memcpy(m_state.context.fpu.no_avx.__fpu_stmm6.__mmst_reg,
+ &value->value.uint8, 10);
+ success = true;
+ break;
+ case fpu_stmm7:
+ memcpy(m_state.context.fpu.no_avx.__fpu_stmm7.__mmst_reg,
+ &value->value.uint8, 10);
+ success = true;
+ break;
+
+ case fpu_xmm0:
+ memcpy(m_state.context.fpu.no_avx.__fpu_xmm0.__xmm_reg,
+ &value->value.uint8, 16);
+ success = true;
+ break;
+ case fpu_xmm1:
+ memcpy(m_state.context.fpu.no_avx.__fpu_xmm1.__xmm_reg,
+ &value->value.uint8, 16);
+ success = true;
+ break;
+ case fpu_xmm2:
+ memcpy(m_state.context.fpu.no_avx.__fpu_xmm2.__xmm_reg,
+ &value->value.uint8, 16);
+ success = true;
+ break;
+ case fpu_xmm3:
+ memcpy(m_state.context.fpu.no_avx.__fpu_xmm3.__xmm_reg,
+ &value->value.uint8, 16);
+ success = true;
+ break;
+ case fpu_xmm4:
+ memcpy(m_state.context.fpu.no_avx.__fpu_xmm4.__xmm_reg,
+ &value->value.uint8, 16);
+ success = true;
+ break;
+ case fpu_xmm5:
+ memcpy(m_state.context.fpu.no_avx.__fpu_xmm5.__xmm_reg,
+ &value->value.uint8, 16);
+ success = true;
+ break;
+ case fpu_xmm6:
+ memcpy(m_state.context.fpu.no_avx.__fpu_xmm6.__xmm_reg,
+ &value->value.uint8, 16);
+ success = true;
+ break;
+ case fpu_xmm7:
+ memcpy(m_state.context.fpu.no_avx.__fpu_xmm7.__xmm_reg,
+ &value->value.uint8, 16);
+ success = true;
+ break;
+
+#define MEMCPY_YMM(n) \
+ memcpy(m_state.context.fpu.avx.__fpu_xmm##n.__xmm_reg, &value->value.uint8, \
+ 16); \
+ memcpy(m_state.context.fpu.avx.__fpu_ymmh##n.__xmm_reg, \
+ (&value->value.uint8) + 16, 16);
+ case fpu_ymm0:
+ MEMCPY_YMM(0);
+ return true;
+ case fpu_ymm1:
+ MEMCPY_YMM(1);
+ return true;
+ case fpu_ymm2:
+ MEMCPY_YMM(2);
+ return true;
+ case fpu_ymm3:
+ MEMCPY_YMM(3);
+ return true;
+ case fpu_ymm4:
+ MEMCPY_YMM(4);
+ return true;
+ case fpu_ymm5:
+ MEMCPY_YMM(5);
+ return true;
+ case fpu_ymm6:
+ MEMCPY_YMM(6);
+ return true;
+ case fpu_ymm7:
+ MEMCPY_YMM(7);
+ return true;
+#undef MEMCPY_YMM
+
+ case fpu_k0:
+ case fpu_k1:
+ case fpu_k2:
+ case fpu_k3:
+ case fpu_k4:
+ case fpu_k5:
+ case fpu_k6:
+ case fpu_k7:
+ memcpy(&m_state.context.fpu.avx512f.__fpu_k0 + (reg - fpu_k0),
+ &value->value.uint8, 8);
+ return true;
+ case fpu_zmm0:
+ case fpu_zmm1:
+ case fpu_zmm2:
+ case fpu_zmm3:
+ case fpu_zmm4:
+ case fpu_zmm5:
+ case fpu_zmm6:
+ case fpu_zmm7:
+ memcpy(&m_state.context.fpu.avx512f.__fpu_xmm0 + (reg - fpu_zmm0),
+ &value->value.uint8, 16);
+ memcpy(&m_state.context.fpu.avx512f.__fpu_ymmh0 + (reg - fpu_zmm0),
+ &value->value.uint8 + 16, 16);
+ memcpy(&m_state.context.fpu.avx512f.__fpu_zmmh0 + (reg - fpu_zmm0),
+ &value->value.uint8 + 32, 32);
+ return true;
+ }
+ break;
+
+ case e_regSetEXC:
+ if (reg < k_num_exc_registers) {
+ (&m_state.context.exc.__trapno)[reg] = value->value.uint32;
+ success = true;
+ }
+ break;
+ }
+ }
+
+ if (success)
+ return SetRegisterState(set) == KERN_SUCCESS;
+ return false;
+}
+
+uint32_t DNBArchImplI386::GetRegisterContextSize() {
+ static uint32_t g_cached_size = 0;
+ if (g_cached_size == 0) {
+ if(CPUHasAVX512f() || FORCE_AVX_REGS) {
+ for (size_t i = 0; i < k_num_fpu_registers_avx512f; ++i) {
+ if (g_fpu_registers_avx512f[i].value_regs == NULL)
+ g_cached_size += g_fpu_registers_avx512f[i].size;
+ }
+ } else
+ if (CPUHasAVX()) {
+ for (size_t i = 0; i < k_num_fpu_registers_avx; ++i) {
+ if (g_fpu_registers_avx[i].value_regs == NULL)
+ g_cached_size += g_fpu_registers_avx[i].size;
+ }
+ } else {
+ for (size_t i = 0; i < k_num_fpu_registers_no_avx; ++i) {
+ if (g_fpu_registers_no_avx[i].value_regs == NULL)
+ g_cached_size += g_fpu_registers_no_avx[i].size;
+ }
+ }
+ DNBLogThreaded("DNBArchImplX86_64::GetRegisterContextSize() - GPR = %zu, "
+ "FPU = %u, EXC = %zu",
+ sizeof(GPR), g_cached_size, sizeof(EXC));
+ g_cached_size += sizeof(GPR);
+ g_cached_size += sizeof(EXC);
+ DNBLogThreaded(
+ "DNBArchImplX86_64::GetRegisterContextSize() - GPR + FPU + EXC = %u",
+ g_cached_size);
+ }
+ return g_cached_size;
+}
+
+nub_size_t DNBArchImplI386::GetRegisterContext(void *buf, nub_size_t buf_len) {
+ uint32_t size = GetRegisterContextSize();
+
+ if (buf && buf_len) {
+ if (size > buf_len)
+ size = static_cast<uint32_t>(buf_len);
+
+ bool force = false;
+ kern_return_t kret;
+ if ((kret = GetGPRState(force)) != KERN_SUCCESS) {
+ DNBLogThreadedIf(LOG_THREAD, "DNBArchImplI386::GetRegisterContext (buf = "
+ "%p, len = %llu) error: GPR regs failed to "
+ "read: %u ",
+ buf, (uint64_t)buf_len, kret);
+ size = 0;
+ } else if ((kret = GetFPUState(force)) != KERN_SUCCESS) {
+ DNBLogThreadedIf(
+ LOG_THREAD, "DNBArchImplI386::GetRegisterContext (buf = %p, len = "
+ "%llu) error: %s regs failed to read: %u",
+ buf, (uint64_t)buf_len, CPUHasAVX() ? "AVX" : "FPU", kret);
+ size = 0;
+ } else if ((kret = GetEXCState(force)) != KERN_SUCCESS) {
+ DNBLogThreadedIf(LOG_THREAD, "DNBArchImplI386::GetRegisterContext (buf = "
+ "%p, len = %llu) error: EXC regs failed to "
+ "read: %u",
+ buf, (uint64_t)buf_len, kret);
+ size = 0;
+ } else {
+ uint8_t *p = (uint8_t *)buf;
+ // Copy the GPR registers
+ memcpy(p, &m_state.context.gpr, sizeof(GPR));
+ p += sizeof(GPR);
+
+ // Walk around the gaps in the FPU regs
+ memcpy(p, &m_state.context.fpu.no_avx.__fpu_fcw, 5);
+ p += 5;
+ memcpy(p, &m_state.context.fpu.no_avx.__fpu_fop, 8);
+ p += 8;
+ memcpy(p, &m_state.context.fpu.no_avx.__fpu_dp, 6);
+ p += 6;
+ memcpy(p, &m_state.context.fpu.no_avx.__fpu_mxcsr, 8);
+ p += 8;
+
+ // Work around the padding between the stmm registers as they are 16
+ // byte structs with 10 bytes of the value in each
+ for (size_t i = 0; i < 8; ++i) {
+ memcpy(p, &m_state.context.fpu.no_avx.__fpu_stmm0 + i, 10);
+ p += 10;
+ }
+
+ if (CPUHasAVX512f() || FORCE_AVX_REGS) {
+ for (size_t i = 0; i < 8; ++i) {
+ memcpy(p, &m_state.context.fpu.avx512f.__fpu_k0 + i, 8);
+ p += 8;
+ }
+ }
+
+ if (CPUHasAVX() || FORCE_AVX_REGS) {
+ // Interleave the XMM and YMMH registers to make the YMM registers
+ for (size_t i = 0; i < 8; ++i) {
+ memcpy(p, &m_state.context.fpu.avx.__fpu_xmm0 + i, 16);
+ p += 16;
+ memcpy(p, &m_state.context.fpu.avx.__fpu_ymmh0 + i, 16);
+ p += 16;
+ }
+ if(CPUHasAVX512f() || FORCE_AVX_REGS) {
+ for (size_t i = 0; i < 8; ++i) {
+ memcpy(p, &m_state.context.fpu.avx512f.__fpu_zmmh0 + i, 32);
+ p += 32;
+ }
+ }
+ } else {
+ // Copy the XMM registers in a single block
+ memcpy(p, &m_state.context.fpu.no_avx.__fpu_xmm0, 8 * 16);
+ p += 8 * 16;
+ }
+
+ // Copy the exception registers
+ memcpy(p, &m_state.context.exc, sizeof(EXC));
+ p += sizeof(EXC);
+
+ // make sure we end up with exactly what we think we should have
+ size_t bytes_written = p - (uint8_t *)buf;
+ UNUSED_IF_ASSERT_DISABLED(bytes_written);
+ assert(bytes_written == size);
+ }
+ }
+ DNBLogThreadedIf(
+ LOG_THREAD,
+ "DNBArchImplI386::GetRegisterContext (buf = %p, len = %llu) => %llu", buf,
+ (uint64_t)buf_len, (uint64_t)size);
+ // Return the size of the register context even if NULL was passed in
+ return size;
+}
+
+nub_size_t DNBArchImplI386::SetRegisterContext(const void *buf,
+ nub_size_t buf_len) {
+ nub_size_t size = sizeof(m_state.context);
+ if (buf == NULL || buf_len == 0)
+ size = 0;
+
+ if (size) {
+ if (size > buf_len)
+ size = buf_len;
+
+ const uint8_t *p = (const uint8_t *)buf;
+ // Copy the GPR registers
+ memcpy(&m_state.context.gpr, p, sizeof(GPR));
+ p += sizeof(GPR);
+
+ // Copy fcw through mxcsrmask as there is no padding
+ memcpy(&m_state.context.fpu.no_avx.__fpu_fcw, p, 5);
+ p += 5;
+ memcpy(&m_state.context.fpu.no_avx.__fpu_fop, p, 8);
+ p += 8;
+ memcpy(&m_state.context.fpu.no_avx.__fpu_dp, p, 6);
+ p += 6;
+ memcpy(&m_state.context.fpu.no_avx.__fpu_mxcsr, p, 8);
+ p += 8;
+
+ // Work around the padding between the stmm registers as they are 16
+ // byte structs with 10 bytes of the value in each
+ for (size_t i = 0; i < 8; ++i) {
+ memcpy(&m_state.context.fpu.no_avx.__fpu_stmm0 + i, p, 10);
+ p += 10;
+ }
+
+ if(CPUHasAVX512f() || FORCE_AVX_REGS) {
+ for (size_t i = 0; i < 8; ++i) {
+ memcpy(&m_state.context.fpu.avx512f.__fpu_k0 + i, p, 8);
+ p += 8;
+ }
+ }
+
+ if (CPUHasAVX() || FORCE_AVX_REGS) {
+ // Interleave the XMM and YMMH registers to make the YMM registers
+ for (size_t i = 0; i < 8; ++i) {
+ memcpy(&m_state.context.fpu.avx.__fpu_xmm0 + i, p, 16);
+ p += 16;
+ memcpy(&m_state.context.fpu.avx.__fpu_ymmh0 + i, p, 16);
+ p += 16;
+ }
+
+ if(CPUHasAVX512f() || FORCE_AVX_REGS) {
+ for (size_t i = 0; i < 8; ++i) {
+ memcpy(&m_state.context.fpu.avx512f.__fpu_zmmh0 + i, p, 32);
+ p += 32;
+ }
+ }
+ } else {
+ // Copy the XMM registers in a single block
+ memcpy(&m_state.context.fpu.no_avx.__fpu_xmm0, p, 8 * 16);
+ p += 8 * 16;
+ }
+
+ // Copy the exception registers
+ memcpy(&m_state.context.exc, p, sizeof(EXC));
+ p += sizeof(EXC);
+
+ // make sure we end up with exactly what we think we should have
+ size_t bytes_written = p - (const uint8_t *)buf;
+ UNUSED_IF_ASSERT_DISABLED(bytes_written);
+ assert(bytes_written == size);
+ kern_return_t kret;
+ if ((kret = SetGPRState()) != KERN_SUCCESS)
+ DNBLogThreadedIf(LOG_THREAD, "DNBArchImplI386::SetRegisterContext (buf = "
+ "%p, len = %llu) error: GPR regs failed to "
+ "write: %u",
+ buf, (uint64_t)buf_len, kret);
+ if ((kret = SetFPUState()) != KERN_SUCCESS)
+ DNBLogThreadedIf(
+ LOG_THREAD, "DNBArchImplI386::SetRegisterContext (buf = %p, len = "
+ "%llu) error: %s regs failed to write: %u",
+ buf, (uint64_t)buf_len, CPUHasAVX() ? "AVX" : "FPU", kret);
+ if ((kret = SetEXCState()) != KERN_SUCCESS)
+ DNBLogThreadedIf(LOG_THREAD, "DNBArchImplI386::SetRegisterContext (buf = "
+ "%p, len = %llu) error: EXP regs failed to "
+ "write: %u",
+ buf, (uint64_t)buf_len, kret);
+ }
+ DNBLogThreadedIf(
+ LOG_THREAD,
+ "DNBArchImplI386::SetRegisterContext (buf = %p, len = %llu) => %llu", buf,
+ (uint64_t)buf_len, (uint64_t)size);
+ return size;
+}
+
+uint32_t DNBArchImplI386::SaveRegisterState() {
+ kern_return_t kret = ::thread_abort_safely(m_thread->MachPortNumber());
+ DNBLogThreadedIf(
+ LOG_THREAD, "thread = 0x%4.4x calling thread_abort_safely (tid) => %u "
+ "(SetGPRState() for stop_count = %u)",
+ m_thread->MachPortNumber(), kret, m_thread->Process()->StopCount());
+
+ bool force = true;
+
+ if ((kret = GetGPRState(force)) != KERN_SUCCESS) {
+ DNBLogThreadedIf(LOG_THREAD, "DNBArchImplI386::SaveRegisterState () error: "
+ "GPR regs failed to read: %u ",
+ kret);
+ } else if ((kret = GetFPUState(force)) != KERN_SUCCESS) {
+ DNBLogThreadedIf(LOG_THREAD, "DNBArchImplI386::SaveRegisterState () error: "
+ "%s regs failed to read: %u",
+ CPUHasAVX() ? "AVX" : "FPU", kret);
+ } else {
+ const uint32_t save_id = GetNextRegisterStateSaveID();
+ m_saved_register_states[save_id] = m_state.context;
+ return save_id;
+ }
+ return 0;
+}
+bool DNBArchImplI386::RestoreRegisterState(uint32_t save_id) {
+ SaveRegisterStates::iterator pos = m_saved_register_states.find(save_id);
+ if (pos != m_saved_register_states.end()) {
+ m_state.context.gpr = pos->second.gpr;
+ m_state.context.fpu = pos->second.fpu;
+ m_state.context.exc = pos->second.exc;
+ m_state.SetError(e_regSetGPR, Read, 0);
+ m_state.SetError(e_regSetFPU, Read, 0);
+ m_state.SetError(e_regSetEXC, Read, 0);
+ kern_return_t kret;
+ bool success = true;
+ if ((kret = SetGPRState()) != KERN_SUCCESS) {
+ DNBLogThreadedIf(LOG_THREAD, "DNBArchImplI386::RestoreRegisterState "
+ "(save_id = %u) error: GPR regs failed to "
+ "write: %u",
+ save_id, kret);
+ success = false;
+ } else if ((kret = SetFPUState()) != KERN_SUCCESS) {
+ DNBLogThreadedIf(LOG_THREAD, "DNBArchImplI386::RestoreRegisterState "
+ "(save_id = %u) error: %s regs failed to "
+ "write: %u",
+ save_id, CPUHasAVX() ? "AVX" : "FPU", kret);
+ success = false;
+ }
+ m_saved_register_states.erase(pos);
+ return success;
+ }
+ return false;
+}
+
+kern_return_t DNBArchImplI386::GetRegisterState(int set, bool force) {
+ switch (set) {
+ case e_regSetALL:
+ return GetGPRState(force) | GetFPUState(force) | GetEXCState(force);
+ case e_regSetGPR:
+ return GetGPRState(force);
+ case e_regSetFPU:
+ return GetFPUState(force);
+ case e_regSetEXC:
+ return GetEXCState(force);
+ default:
+ break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+kern_return_t DNBArchImplI386::SetRegisterState(int set) {
+ // Make sure we have a valid context to set.
+ if (RegisterSetStateIsValid(set)) {
+ switch (set) {
+ case e_regSetALL:
+ return SetGPRState() | SetFPUState() | SetEXCState();
+ case e_regSetGPR:
+ return SetGPRState();
+ case e_regSetFPU:
+ return SetFPUState();
+ case e_regSetEXC:
+ return SetEXCState();
+ default:
+ break;
+ }
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+bool DNBArchImplI386::RegisterSetStateIsValid(int set) const {
+ return m_state.RegsAreValid(set);
+}
+
+#endif // #if defined (__i386__)
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h
new file mode 100644
index 00000000000..12b515a2957
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.h
@@ -0,0 +1,238 @@
+//===-- DNBArchImplI386.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNBArchImplI386_h__
+#define __DNBArchImplI386_h__
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include "DNBArch.h"
+#include "MachRegisterStatesI386.h"
+
+#include <map>
+
+class MachThread;
+
+class DNBArchImplI386 : public DNBArchProtocol {
+public:
+ DNBArchImplI386(MachThread *thread)
+ : DNBArchProtocol(), m_thread(thread), m_state(), m_2pc_dbg_checkpoint(),
+ m_2pc_trans_state(Trans_Done), m_saved_register_states() {}
+ virtual ~DNBArchImplI386() {}
+
+ static void Initialize();
+
+ virtual bool GetRegisterValue(uint32_t set, uint32_t reg,
+ DNBRegisterValue *value);
+ virtual bool SetRegisterValue(uint32_t set, uint32_t reg,
+ const DNBRegisterValue *value);
+ virtual nub_size_t GetRegisterContext(void *buf, nub_size_t buf_len);
+ virtual nub_size_t SetRegisterContext(const void *buf, nub_size_t buf_len);
+ virtual uint32_t SaveRegisterState();
+ virtual bool RestoreRegisterState(uint32_t save_id);
+
+ virtual kern_return_t GetRegisterState(int set, bool force);
+ virtual kern_return_t SetRegisterState(int set);
+ virtual bool RegisterSetStateIsValid(int set) const;
+
+ virtual uint64_t GetPC(uint64_t failValue); // Get program counter
+ virtual kern_return_t SetPC(uint64_t value);
+ virtual uint64_t GetSP(uint64_t failValue); // Get stack pointer
+ virtual void ThreadWillResume();
+ virtual bool ThreadDidStop();
+ virtual bool NotifyException(MachException::Data &exc);
+
+ virtual uint32_t NumSupportedHardwareWatchpoints();
+ virtual uint32_t EnableHardwareWatchpoint(nub_addr_t addr, nub_size_t size,
+ bool read, bool write,
+ bool also_set_on_task);
+ virtual bool DisableHardwareWatchpoint(uint32_t hw_break_index,
+ bool also_set_on_task);
+ virtual uint32_t GetHardwareWatchpointHit(nub_addr_t &addr);
+
+protected:
+ kern_return_t EnableHardwareSingleStep(bool enable);
+
+ typedef __i386_thread_state_t GPR;
+ typedef __i386_float_state_t FPU;
+ typedef __i386_exception_state_t EXC;
+ typedef __i386_avx_state_t AVX;
+ typedef __i386_debug_state_t DBG;
+
+ static const DNBRegisterInfo g_gpr_registers[];
+ static const DNBRegisterInfo g_fpu_registers_no_avx[];
+ static const DNBRegisterInfo g_fpu_registers_avx[];
+ static const DNBRegisterInfo g_exc_registers[];
+ static const DNBRegisterSetInfo g_reg_sets_no_avx[];
+ static const DNBRegisterSetInfo g_reg_sets_avx[];
+ static const size_t k_num_gpr_registers;
+ static const size_t k_num_fpu_registers_no_avx;
+ static const size_t k_num_fpu_registers_avx;
+ static const size_t k_num_exc_registers;
+ static const size_t k_num_all_registers_no_avx;
+ static const size_t k_num_all_registers_avx;
+ static const size_t k_num_register_sets;
+
+ typedef __i386_avx512f_state_t AVX512F;
+ static const DNBRegisterInfo g_fpu_registers_avx512f[];
+ static const DNBRegisterSetInfo g_reg_sets_avx512f[];
+ static const size_t k_num_fpu_registers_avx512f;
+ static const size_t k_num_all_registers_avx512f;
+
+ enum RegisterSet {
+ e_regSetALL = REGISTER_SET_ALL,
+ e_regSetGPR,
+ e_regSetFPU,
+ e_regSetEXC,
+ e_regSetDBG,
+ kNumRegisterSets
+ };
+
+ enum RegisterSetWordSize {
+ e_regSetWordSizeGPR = sizeof(GPR) / sizeof(int),
+ e_regSetWordSizeFPU = sizeof(FPU) / sizeof(int),
+ e_regSetWordSizeEXC = sizeof(EXC) / sizeof(int),
+ e_regSetWordSizeAVX = sizeof(AVX) / sizeof(int),
+ e_regSetWordSizeAVX512f = sizeof(AVX512F) / sizeof(int),
+ e_regSetWordSizeDBG = sizeof(DBG) / sizeof(int)
+ };
+
+ enum { Read = 0, Write = 1, kNumErrors = 2 };
+
+ struct Context {
+ GPR gpr;
+ union {
+ FPU no_avx;
+ AVX avx;
+ AVX512F avx512f;
+ } fpu;
+ EXC exc;
+ DBG dbg;
+ };
+
+ struct State {
+ Context context;
+ kern_return_t gpr_errs[2]; // Read/Write errors
+ kern_return_t fpu_errs[2]; // Read/Write errors
+ kern_return_t exc_errs[2]; // Read/Write errors
+ kern_return_t dbg_errs[2]; // Read/Write errors
+
+ State() {
+ uint32_t i;
+ for (i = 0; i < kNumErrors; i++) {
+ gpr_errs[i] = -1;
+ fpu_errs[i] = -1;
+ exc_errs[i] = -1;
+ dbg_errs[i] = -1;
+ }
+ }
+ void InvalidateAllRegisterStates() { SetError(e_regSetALL, Read, -1); }
+ kern_return_t GetError(int flavor, uint32_t err_idx) const {
+ if (err_idx < kNumErrors) {
+ switch (flavor) {
+ // When getting all errors, just OR all values together to see if
+ // we got any kind of error.
+ case e_regSetALL:
+ return gpr_errs[err_idx] | fpu_errs[err_idx] | exc_errs[err_idx];
+ case e_regSetGPR:
+ return gpr_errs[err_idx];
+ case e_regSetFPU:
+ return fpu_errs[err_idx];
+ case e_regSetEXC:
+ return exc_errs[err_idx];
+ case e_regSetDBG:
+ return dbg_errs[err_idx];
+ default:
+ break;
+ }
+ }
+ return -1;
+ }
+ bool SetError(int flavor, uint32_t err_idx, kern_return_t err) {
+ if (err_idx < kNumErrors) {
+ switch (flavor) {
+ case e_regSetALL:
+ gpr_errs[err_idx] = fpu_errs[err_idx] = exc_errs[err_idx] =
+ dbg_errs[err_idx] = err;
+ return true;
+
+ case e_regSetGPR:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case e_regSetFPU:
+ fpu_errs[err_idx] = err;
+ return true;
+
+ case e_regSetEXC:
+ exc_errs[err_idx] = err;
+ return true;
+
+ case e_regSetDBG:
+ dbg_errs[err_idx] = err;
+ return true;
+
+ default:
+ break;
+ }
+ }
+ return false;
+ }
+ bool RegsAreValid(int flavor) const {
+ return GetError(flavor, Read) == KERN_SUCCESS;
+ }
+ };
+
+ kern_return_t GetGPRState(bool force);
+ kern_return_t GetFPUState(bool force);
+ kern_return_t GetEXCState(bool force);
+ kern_return_t GetDBGState(bool force);
+
+ kern_return_t SetGPRState();
+ kern_return_t SetFPUState();
+ kern_return_t SetEXCState();
+ kern_return_t SetDBGState(bool also_set_on_task);
+
+ static DNBArchProtocol *Create(MachThread *thread);
+
+ static const uint8_t *SoftwareBreakpointOpcode(nub_size_t byte_size);
+
+ static const DNBRegisterSetInfo *GetRegisterSetInfo(nub_size_t *num_reg_sets);
+
+ static uint32_t GetRegisterContextSize();
+
+ // Helper functions for watchpoint manipulations.
+ static void SetWatchpoint(DBG &debug_state, uint32_t hw_index,
+ nub_addr_t addr, nub_size_t size, bool read,
+ bool write);
+ static void ClearWatchpoint(DBG &debug_state, uint32_t hw_index);
+ static bool IsWatchpointVacant(const DBG &debug_state, uint32_t hw_index);
+ static void ClearWatchpointHits(DBG &debug_state);
+ static bool IsWatchpointHit(const DBG &debug_state, uint32_t hw_index);
+ static nub_addr_t GetWatchAddress(const DBG &debug_state, uint32_t hw_index);
+
+ virtual bool StartTransForHWP();
+ virtual bool RollbackTransForHWP();
+ virtual bool FinishTransForHWP();
+ DBG GetDBGCheckpoint();
+
+ MachThread *m_thread;
+ State m_state;
+ DBG m_2pc_dbg_checkpoint;
+ uint32_t m_2pc_trans_state; // Is transaction of DBG state change: Pedning
+ // (0), Done (1), or Rolled Back (2)?
+ typedef std::map<uint32_t, Context> SaveRegisterStates;
+ SaveRegisterStates m_saved_register_states;
+};
+
+#endif // #if defined (__i386__) || defined (__x86_64__)
+#endif // #ifndef __DNBArchImplI386_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/i386/MachRegisterStatesI386.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/i386/MachRegisterStatesI386.h
new file mode 100644
index 00000000000..6eab0ad7a5a
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/i386/MachRegisterStatesI386.h
@@ -0,0 +1,241 @@
+//===-- MachRegisterStatesI386.h --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Sean Callanan on 3/16/11.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachRegisterStatesI386_h__
+#define __MachRegisterStatesI386_h__
+
+#include <inttypes.h>
+
+#define __i386_THREAD_STATE 1
+#define __i386_FLOAT_STATE 2
+#define __i386_EXCEPTION_STATE 3
+#define __i386_DEBUG_STATE 10
+#define __i386_AVX_STATE 16
+#define __i386_AVX512F_STATE 19
+
+typedef struct {
+ uint32_t __eax;
+ uint32_t __ebx;
+ uint32_t __ecx;
+ uint32_t __edx;
+ uint32_t __edi;
+ uint32_t __esi;
+ uint32_t __ebp;
+ uint32_t __esp;
+ uint32_t __ss;
+ uint32_t __eflags;
+ uint32_t __eip;
+ uint32_t __cs;
+ uint32_t __ds;
+ uint32_t __es;
+ uint32_t __fs;
+ uint32_t __gs;
+} __i386_thread_state_t;
+
+typedef struct {
+ uint16_t __invalid : 1;
+ uint16_t __denorm : 1;
+ uint16_t __zdiv : 1;
+ uint16_t __ovrfl : 1;
+ uint16_t __undfl : 1;
+ uint16_t __precis : 1;
+ uint16_t __PAD1 : 2;
+ uint16_t __pc : 2;
+ uint16_t __rc : 2;
+ uint16_t __PAD2 : 1;
+ uint16_t __PAD3 : 3;
+} __i386_fp_control_t;
+
+typedef struct {
+ uint16_t __invalid : 1;
+ uint16_t __denorm : 1;
+ uint16_t __zdiv : 1;
+ uint16_t __ovrfl : 1;
+ uint16_t __undfl : 1;
+ uint16_t __precis : 1;
+ uint16_t __stkflt : 1;
+ uint16_t __errsumm : 1;
+ uint16_t __c0 : 1;
+ uint16_t __c1 : 1;
+ uint16_t __c2 : 1;
+ uint16_t __tos : 3;
+ uint16_t __c3 : 1;
+ uint16_t __busy : 1;
+} __i386_fp_status_t;
+
+typedef struct {
+ uint8_t __mmst_reg[10];
+ uint8_t __mmst_rsrv[6];
+} __i386_mmst_reg;
+
+typedef struct { uint8_t __xmm_reg[16]; } __i386_xmm_reg;
+
+typedef struct {
+ uint32_t __fpu_reserved[2];
+ __i386_fp_control_t __fpu_fcw;
+ __i386_fp_status_t __fpu_fsw;
+ uint8_t __fpu_ftw;
+ uint8_t __fpu_rsrv1;
+ uint16_t __fpu_fop;
+ uint32_t __fpu_ip;
+ uint16_t __fpu_cs;
+ uint16_t __fpu_rsrv2;
+ uint32_t __fpu_dp;
+ uint16_t __fpu_ds;
+ uint16_t __fpu_rsrv3;
+ uint32_t __fpu_mxcsr;
+ uint32_t __fpu_mxcsrmask;
+ __i386_mmst_reg __fpu_stmm0;
+ __i386_mmst_reg __fpu_stmm1;
+ __i386_mmst_reg __fpu_stmm2;
+ __i386_mmst_reg __fpu_stmm3;
+ __i386_mmst_reg __fpu_stmm4;
+ __i386_mmst_reg __fpu_stmm5;
+ __i386_mmst_reg __fpu_stmm6;
+ __i386_mmst_reg __fpu_stmm7;
+ __i386_xmm_reg __fpu_xmm0;
+ __i386_xmm_reg __fpu_xmm1;
+ __i386_xmm_reg __fpu_xmm2;
+ __i386_xmm_reg __fpu_xmm3;
+ __i386_xmm_reg __fpu_xmm4;
+ __i386_xmm_reg __fpu_xmm5;
+ __i386_xmm_reg __fpu_xmm6;
+ __i386_xmm_reg __fpu_xmm7;
+ uint8_t __fpu_rsrv4[14 * 16];
+ uint32_t __fpu_reserved1;
+} __i386_float_state_t;
+
+typedef struct {
+ uint32_t __fpu_reserved[2];
+ __i386_fp_control_t __fpu_fcw;
+ __i386_fp_status_t __fpu_fsw;
+ uint8_t __fpu_ftw;
+ uint8_t __fpu_rsrv1;
+ uint16_t __fpu_fop;
+ uint32_t __fpu_ip;
+ uint16_t __fpu_cs;
+ uint16_t __fpu_rsrv2;
+ uint32_t __fpu_dp;
+ uint16_t __fpu_ds;
+ uint16_t __fpu_rsrv3;
+ uint32_t __fpu_mxcsr;
+ uint32_t __fpu_mxcsrmask;
+ __i386_mmst_reg __fpu_stmm0;
+ __i386_mmst_reg __fpu_stmm1;
+ __i386_mmst_reg __fpu_stmm2;
+ __i386_mmst_reg __fpu_stmm3;
+ __i386_mmst_reg __fpu_stmm4;
+ __i386_mmst_reg __fpu_stmm5;
+ __i386_mmst_reg __fpu_stmm6;
+ __i386_mmst_reg __fpu_stmm7;
+ __i386_xmm_reg __fpu_xmm0;
+ __i386_xmm_reg __fpu_xmm1;
+ __i386_xmm_reg __fpu_xmm2;
+ __i386_xmm_reg __fpu_xmm3;
+ __i386_xmm_reg __fpu_xmm4;
+ __i386_xmm_reg __fpu_xmm5;
+ __i386_xmm_reg __fpu_xmm6;
+ __i386_xmm_reg __fpu_xmm7;
+ uint8_t __fpu_rsrv4[14 * 16];
+ uint32_t __fpu_reserved1;
+ uint8_t __avx_reserved1[64];
+ __i386_xmm_reg __fpu_ymmh0;
+ __i386_xmm_reg __fpu_ymmh1;
+ __i386_xmm_reg __fpu_ymmh2;
+ __i386_xmm_reg __fpu_ymmh3;
+ __i386_xmm_reg __fpu_ymmh4;
+ __i386_xmm_reg __fpu_ymmh5;
+ __i386_xmm_reg __fpu_ymmh6;
+ __i386_xmm_reg __fpu_ymmh7;
+} __i386_avx_state_t;
+
+typedef struct { uint8_t __ymm_reg[32]; } __i386_ymm_reg;
+typedef struct { uint8_t __opmask_reg[8]; } __i386_opmask_reg;
+
+typedef struct {
+ uint32_t __fpu_reserved[2];
+ __i386_fp_control_t __fpu_fcw;
+ __i386_fp_status_t __fpu_fsw;
+ uint8_t __fpu_ftw;
+ uint8_t __fpu_rsrv1;
+ uint16_t __fpu_fop;
+ uint32_t __fpu_ip;
+ uint16_t __fpu_cs;
+ uint16_t __fpu_rsrv2;
+ uint32_t __fpu_dp;
+ uint16_t __fpu_ds;
+ uint16_t __fpu_rsrv3;
+ uint32_t __fpu_mxcsr;
+ uint32_t __fpu_mxcsrmask;
+ __i386_mmst_reg __fpu_stmm0;
+ __i386_mmst_reg __fpu_stmm1;
+ __i386_mmst_reg __fpu_stmm2;
+ __i386_mmst_reg __fpu_stmm3;
+ __i386_mmst_reg __fpu_stmm4;
+ __i386_mmst_reg __fpu_stmm5;
+ __i386_mmst_reg __fpu_stmm6;
+ __i386_mmst_reg __fpu_stmm7;
+ __i386_xmm_reg __fpu_xmm0;
+ __i386_xmm_reg __fpu_xmm1;
+ __i386_xmm_reg __fpu_xmm2;
+ __i386_xmm_reg __fpu_xmm3;
+ __i386_xmm_reg __fpu_xmm4;
+ __i386_xmm_reg __fpu_xmm5;
+ __i386_xmm_reg __fpu_xmm6;
+ __i386_xmm_reg __fpu_xmm7;
+ uint8_t __fpu_rsrv4[14 * 16];
+ uint32_t __fpu_reserved1;
+ uint8_t __avx_reserved1[64];
+ __i386_xmm_reg __fpu_ymmh0;
+ __i386_xmm_reg __fpu_ymmh1;
+ __i386_xmm_reg __fpu_ymmh2;
+ __i386_xmm_reg __fpu_ymmh3;
+ __i386_xmm_reg __fpu_ymmh4;
+ __i386_xmm_reg __fpu_ymmh5;
+ __i386_xmm_reg __fpu_ymmh6;
+ __i386_xmm_reg __fpu_ymmh7;
+ __i386_opmask_reg __fpu_k0;
+ __i386_opmask_reg __fpu_k1;
+ __i386_opmask_reg __fpu_k2;
+ __i386_opmask_reg __fpu_k3;
+ __i386_opmask_reg __fpu_k4;
+ __i386_opmask_reg __fpu_k5;
+ __i386_opmask_reg __fpu_k6;
+ __i386_opmask_reg __fpu_k7;
+ __i386_ymm_reg __fpu_zmmh0;
+ __i386_ymm_reg __fpu_zmmh1;
+ __i386_ymm_reg __fpu_zmmh2;
+ __i386_ymm_reg __fpu_zmmh3;
+ __i386_ymm_reg __fpu_zmmh4;
+ __i386_ymm_reg __fpu_zmmh5;
+ __i386_ymm_reg __fpu_zmmh6;
+ __i386_ymm_reg __fpu_zmmh7;
+} __i386_avx512f_state_t;
+
+typedef struct {
+ uint32_t __trapno;
+ uint32_t __err;
+ uint32_t __faultvaddr;
+} __i386_exception_state_t;
+
+typedef struct {
+ uint32_t __dr0;
+ uint32_t __dr1;
+ uint32_t __dr2;
+ uint32_t __dr3;
+ uint32_t __dr4;
+ uint32_t __dr5;
+ uint32_t __dr6;
+ uint32_t __dr7;
+} __i386_debug_state_t;
+
+#endif
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.cpp
new file mode 100644
index 00000000000..c6671b5066a
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.cpp
@@ -0,0 +1,487 @@
+//===-- DNBArchImpl.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__powerpc__) || defined(__ppc__) || defined(__ppc64__)
+
+#if __DARWIN_UNIX03
+#define PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(reg) __##reg
+#else
+#define PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(reg) reg
+#endif
+
+#include "MacOSX/ppc/DNBArchImpl.h"
+#include "DNBBreakpoint.h"
+#include "DNBLog.h"
+#include "DNBRegisterInfo.h"
+#include "MacOSX/MachThread.h"
+
+static const uint8_t g_breakpoint_opcode[] = {0x7F, 0xC0, 0x00, 0x08};
+
+const uint8_t *DNBArchMachPPC::SoftwareBreakpointOpcode(nub_size_t size) {
+ if (size == 4)
+ return g_breakpoint_opcode;
+ return NULL;
+}
+
+uint32_t DNBArchMachPPC::GetCPUType() { return CPU_TYPE_POWERPC; }
+
+uint64_t DNBArchMachPPC::GetPC(uint64_t failValue) {
+ // Get program counter
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.gpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(srr0);
+ return failValue;
+}
+
+kern_return_t DNBArchMachPPC::SetPC(uint64_t value) {
+ // Get program counter
+ kern_return_t err = GetGPRState(false);
+ if (err == KERN_SUCCESS) {
+ m_state.gpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(srr0) = value;
+ err = SetGPRState();
+ }
+ return err == KERN_SUCCESS;
+}
+
+uint64_t DNBArchMachPPC::GetSP(uint64_t failValue) {
+ // Get stack pointer
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.gpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(r1);
+ return failValue;
+}
+
+kern_return_t DNBArchMachPPC::GetGPRState(bool force) {
+ if (force || m_state.GetError(e_regSetGPR, Read)) {
+ mach_msg_type_number_t count = e_regSetWordSizeGPR;
+ m_state.SetError(e_regSetGPR, Read,
+ ::thread_get_state(m_thread->MachPortNumber(), e_regSetGPR,
+ (thread_state_t)&m_state.gpr, &count));
+ }
+ return m_state.GetError(e_regSetGPR, Read);
+}
+
+kern_return_t DNBArchMachPPC::GetFPRState(bool force) {
+ if (force || m_state.GetError(e_regSetFPR, Read)) {
+ mach_msg_type_number_t count = e_regSetWordSizeFPR;
+ m_state.SetError(e_regSetFPR, Read,
+ ::thread_get_state(m_thread->MachPortNumber(), e_regSetFPR,
+ (thread_state_t)&m_state.fpr, &count));
+ }
+ return m_state.GetError(e_regSetFPR, Read);
+}
+
+kern_return_t DNBArchMachPPC::GetEXCState(bool force) {
+ if (force || m_state.GetError(e_regSetEXC, Read)) {
+ mach_msg_type_number_t count = e_regSetWordSizeEXC;
+ m_state.SetError(e_regSetEXC, Read,
+ ::thread_get_state(m_thread->MachPortNumber(), e_regSetEXC,
+ (thread_state_t)&m_state.exc, &count));
+ }
+ return m_state.GetError(e_regSetEXC, Read);
+}
+
+kern_return_t DNBArchMachPPC::GetVECState(bool force) {
+ if (force || m_state.GetError(e_regSetVEC, Read)) {
+ mach_msg_type_number_t count = e_regSetWordSizeVEC;
+ m_state.SetError(e_regSetVEC, Read,
+ ::thread_get_state(m_thread->MachPortNumber(), e_regSetVEC,
+ (thread_state_t)&m_state.vec, &count));
+ }
+ return m_state.GetError(e_regSetVEC, Read);
+}
+
+kern_return_t DNBArchMachPPC::SetGPRState() {
+ m_state.SetError(e_regSetGPR, Write,
+ ::thread_set_state(m_thread->MachPortNumber(), e_regSetGPR,
+ (thread_state_t)&m_state.gpr,
+ e_regSetWordSizeGPR));
+ return m_state.GetError(e_regSetGPR, Write);
+}
+
+kern_return_t DNBArchMachPPC::SetFPRState() {
+ m_state.SetError(e_regSetFPR, Write,
+ ::thread_set_state(m_thread->MachPortNumber(), e_regSetFPR,
+ (thread_state_t)&m_state.fpr,
+ e_regSetWordSizeFPR));
+ return m_state.GetError(e_regSetFPR, Write);
+}
+
+kern_return_t DNBArchMachPPC::SetEXCState() {
+ m_state.SetError(e_regSetEXC, Write,
+ ::thread_set_state(m_thread->MachPortNumber(), e_regSetEXC,
+ (thread_state_t)&m_state.exc,
+ e_regSetWordSizeEXC));
+ return m_state.GetError(e_regSetEXC, Write);
+}
+
+kern_return_t DNBArchMachPPC::SetVECState() {
+ m_state.SetError(e_regSetVEC, Write,
+ ::thread_set_state(m_thread->MachPortNumber(), e_regSetVEC,
+ (thread_state_t)&m_state.vec,
+ e_regSetWordSizeVEC));
+ return m_state.GetError(e_regSetVEC, Write);
+}
+
+bool DNBArchMachPPC::ThreadWillResume() {
+ bool success = true;
+
+ // Do we need to step this thread? If so, let the mach thread tell us so.
+ if (m_thread->IsStepping()) {
+ // This is the primary thread, let the arch do anything it needs
+ success = EnableHardwareSingleStep(true) == KERN_SUCCESS;
+ }
+ return success;
+}
+
+bool DNBArchMachPPC::ThreadDidStop() {
+ bool success = true;
+
+ m_state.InvalidateAllRegisterStates();
+
+ // Are we stepping a single instruction?
+ if (GetGPRState(true) == KERN_SUCCESS) {
+ // We are single stepping, was this the primary thread?
+ if (m_thread->IsStepping()) {
+ // This was the primary thread, we need to clear the trace
+ // bit if so.
+ success = EnableHardwareSingleStep(false) == KERN_SUCCESS;
+ } else {
+ // The MachThread will automatically restore the suspend count
+ // in ThreadDidStop(), so we don't need to do anything here if
+ // we weren't the primary thread the last time
+ }
+ }
+ return success;
+}
+
+// Set the single step bit in the processor status register.
+kern_return_t DNBArchMachPPC::EnableHardwareSingleStep(bool enable) {
+ DNBLogThreadedIf(LOG_STEP,
+ "DNBArchMachPPC::EnableHardwareSingleStep( enable = %d )",
+ enable);
+ if (GetGPRState(false) == KERN_SUCCESS) {
+ const uint32_t trace_bit = 0x400;
+ if (enable)
+ m_state.gpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(srr1) |= trace_bit;
+ else
+ m_state.gpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(srr1) &= ~trace_bit;
+ return SetGPRState();
+ }
+ return m_state.GetError(e_regSetGPR, Read);
+}
+
+// Register information definitions for 32 bit PowerPC.
+
+enum gpr_regnums {
+ e_regNumGPR_srr0,
+ e_regNumGPR_srr1,
+ e_regNumGPR_r0,
+ e_regNumGPR_r1,
+ e_regNumGPR_r2,
+ e_regNumGPR_r3,
+ e_regNumGPR_r4,
+ e_regNumGPR_r5,
+ e_regNumGPR_r6,
+ e_regNumGPR_r7,
+ e_regNumGPR_r8,
+ e_regNumGPR_r9,
+ e_regNumGPR_r10,
+ e_regNumGPR_r11,
+ e_regNumGPR_r12,
+ e_regNumGPR_r13,
+ e_regNumGPR_r14,
+ e_regNumGPR_r15,
+ e_regNumGPR_r16,
+ e_regNumGPR_r17,
+ e_regNumGPR_r18,
+ e_regNumGPR_r19,
+ e_regNumGPR_r20,
+ e_regNumGPR_r21,
+ e_regNumGPR_r22,
+ e_regNumGPR_r23,
+ e_regNumGPR_r24,
+ e_regNumGPR_r25,
+ e_regNumGPR_r26,
+ e_regNumGPR_r27,
+ e_regNumGPR_r28,
+ e_regNumGPR_r29,
+ e_regNumGPR_r30,
+ e_regNumGPR_r31,
+ e_regNumGPR_cr,
+ e_regNumGPR_xer,
+ e_regNumGPR_lr,
+ e_regNumGPR_ctr,
+ e_regNumGPR_mq,
+ e_regNumGPR_vrsave
+};
+
+// General purpose registers
+static DNBRegisterInfo g_gpr_registers[] = {
+ {"srr0", Uint, 4, Hex}, {"srr1", Uint, 4, Hex}, {"r0", Uint, 4, Hex},
+ {"r1", Uint, 4, Hex}, {"r2", Uint, 4, Hex}, {"r3", Uint, 4, Hex},
+ {"r4", Uint, 4, Hex}, {"r5", Uint, 4, Hex}, {"r6", Uint, 4, Hex},
+ {"r7", Uint, 4, Hex}, {"r8", Uint, 4, Hex}, {"r9", Uint, 4, Hex},
+ {"r10", Uint, 4, Hex}, {"r11", Uint, 4, Hex}, {"r12", Uint, 4, Hex},
+ {"r13", Uint, 4, Hex}, {"r14", Uint, 4, Hex}, {"r15", Uint, 4, Hex},
+ {"r16", Uint, 4, Hex}, {"r17", Uint, 4, Hex}, {"r18", Uint, 4, Hex},
+ {"r19", Uint, 4, Hex}, {"r20", Uint, 4, Hex}, {"r21", Uint, 4, Hex},
+ {"r22", Uint, 4, Hex}, {"r23", Uint, 4, Hex}, {"r24", Uint, 4, Hex},
+ {"r25", Uint, 4, Hex}, {"r26", Uint, 4, Hex}, {"r27", Uint, 4, Hex},
+ {"r28", Uint, 4, Hex}, {"r29", Uint, 4, Hex}, {"r30", Uint, 4, Hex},
+ {"r31", Uint, 4, Hex}, {"cr", Uint, 4, Hex}, {"xer", Uint, 4, Hex},
+ {"lr", Uint, 4, Hex}, {"ctr", Uint, 4, Hex}, {"mq", Uint, 4, Hex},
+ {"vrsave", Uint, 4, Hex},
+};
+
+// Floating point registers
+static DNBRegisterInfo g_fpr_registers[] = {
+ {"fp0", IEEE754, 8, Float}, {"fp1", IEEE754, 8, Float},
+ {"fp2", IEEE754, 8, Float}, {"fp3", IEEE754, 8, Float},
+ {"fp4", IEEE754, 8, Float}, {"fp5", IEEE754, 8, Float},
+ {"fp6", IEEE754, 8, Float}, {"fp7", IEEE754, 8, Float},
+ {"fp8", IEEE754, 8, Float}, {"fp9", IEEE754, 8, Float},
+ {"fp10", IEEE754, 8, Float}, {"fp11", IEEE754, 8, Float},
+ {"fp12", IEEE754, 8, Float}, {"fp13", IEEE754, 8, Float},
+ {"fp14", IEEE754, 8, Float}, {"fp15", IEEE754, 8, Float},
+ {"fp16", IEEE754, 8, Float}, {"fp17", IEEE754, 8, Float},
+ {"fp18", IEEE754, 8, Float}, {"fp19", IEEE754, 8, Float},
+ {"fp20", IEEE754, 8, Float}, {"fp21", IEEE754, 8, Float},
+ {"fp22", IEEE754, 8, Float}, {"fp23", IEEE754, 8, Float},
+ {"fp24", IEEE754, 8, Float}, {"fp25", IEEE754, 8, Float},
+ {"fp26", IEEE754, 8, Float}, {"fp27", IEEE754, 8, Float},
+ {"fp28", IEEE754, 8, Float}, {"fp29", IEEE754, 8, Float},
+ {"fp30", IEEE754, 8, Float}, {"fp31", IEEE754, 8, Float},
+ {"fpscr", Uint, 4, Hex}};
+
+// Exception registers
+
+static DNBRegisterInfo g_exc_registers[] = {{"dar", Uint, 4, Hex},
+ {"dsisr", Uint, 4, Hex},
+ {"exception", Uint, 4, Hex}};
+
+// Altivec registers
+static DNBRegisterInfo g_vec_registers[] = {
+ {"vr0", Vector, 16, VectorOfFloat32},
+ {"vr1", Vector, 16, VectorOfFloat32},
+ {"vr2", Vector, 16, VectorOfFloat32},
+ {"vr3", Vector, 16, VectorOfFloat32},
+ {"vr4", Vector, 16, VectorOfFloat32},
+ {"vr5", Vector, 16, VectorOfFloat32},
+ {"vr6", Vector, 16, VectorOfFloat32},
+ {"vr7", Vector, 16, VectorOfFloat32},
+ {"vr8", Vector, 16, VectorOfFloat32},
+ {"vr9", Vector, 16, VectorOfFloat32},
+ {"vr10", Vector, 16, VectorOfFloat32},
+ {"vr11", Vector, 16, VectorOfFloat32},
+ {"vr12", Vector, 16, VectorOfFloat32},
+ {"vr13", Vector, 16, VectorOfFloat32},
+ {"vr14", Vector, 16, VectorOfFloat32},
+ {"vr15", Vector, 16, VectorOfFloat32},
+ {"vr16", Vector, 16, VectorOfFloat32},
+ {"vr17", Vector, 16, VectorOfFloat32},
+ {"vr18", Vector, 16, VectorOfFloat32},
+ {"vr19", Vector, 16, VectorOfFloat32},
+ {"vr20", Vector, 16, VectorOfFloat32},
+ {"vr21", Vector, 16, VectorOfFloat32},
+ {"vr22", Vector, 16, VectorOfFloat32},
+ {"vr23", Vector, 16, VectorOfFloat32},
+ {"vr24", Vector, 16, VectorOfFloat32},
+ {"vr25", Vector, 16, VectorOfFloat32},
+ {"vr26", Vector, 16, VectorOfFloat32},
+ {"vr27", Vector, 16, VectorOfFloat32},
+ {"vr28", Vector, 16, VectorOfFloat32},
+ {"vr29", Vector, 16, VectorOfFloat32},
+ {"vr30", Vector, 16, VectorOfFloat32},
+ {"vr31", Vector, 16, VectorOfFloat32},
+ {"vscr", Uint, 16, Hex},
+ {"vrvalid", Uint, 4, Hex}};
+
+// Number of registers in each register set
+const size_t k_num_gpr_registers =
+ sizeof(g_gpr_registers) / sizeof(DNBRegisterInfo);
+const size_t k_num_fpr_registers =
+ sizeof(g_fpr_registers) / sizeof(DNBRegisterInfo);
+const size_t k_num_exc_registers =
+ sizeof(g_exc_registers) / sizeof(DNBRegisterInfo);
+const size_t k_num_vec_registers =
+ sizeof(g_vec_registers) / sizeof(DNBRegisterInfo);
+// Total number of registers for this architecture
+const size_t k_num_ppc_registers = k_num_gpr_registers + k_num_fpr_registers +
+ k_num_exc_registers + k_num_vec_registers;
+
+// Register set definitions. The first definitions at register set index
+// of zero is for all registers, followed by other registers sets. The
+// register information for the all register set need not be filled in.
+static const DNBRegisterSetInfo g_reg_sets[] = {
+ {"PowerPC Registers", NULL, k_num_ppc_registers},
+ {"General Purpose Registers", g_gpr_registers, k_num_gpr_registers},
+ {"Floating Point Registers", g_fpr_registers, k_num_fpr_registers},
+ {"Exception State Registers", g_exc_registers, k_num_exc_registers},
+ {"Altivec Registers", g_vec_registers, k_num_vec_registers}};
+// Total number of register sets for this architecture
+const size_t k_num_register_sets =
+ sizeof(g_reg_sets) / sizeof(DNBRegisterSetInfo);
+
+const DNBRegisterSetInfo *
+DNBArchMachPPC::GetRegisterSetInfo(nub_size_t *num_reg_sets) const {
+ *num_reg_sets = k_num_register_sets;
+ return g_reg_sets;
+}
+
+bool DNBArchMachPPC::GetRegisterValue(uint32_t set, uint32_t reg,
+ DNBRegisterValue *value) const {
+ if (set == REGISTER_SET_GENERIC) {
+ switch (reg) {
+ case GENERIC_REGNUM_PC: // Program Counter
+ set = e_regSetGPR;
+ reg = e_regNumGPR_srr0;
+ break;
+
+ case GENERIC_REGNUM_SP: // Stack Pointer
+ set = e_regSetGPR;
+ reg = e_regNumGPR_r1;
+ break;
+
+ case GENERIC_REGNUM_FP: // Frame Pointer
+ // Return false for now instead of returning r30 as gcc 3.x would
+ // use a variety of registers for the FP and it takes inspecting
+ // the stack to make sure there is a frame pointer before we can
+ // determine the FP.
+ return false;
+
+ case GENERIC_REGNUM_RA: // Return Address
+ set = e_regSetGPR;
+ reg = e_regNumGPR_lr;
+ break;
+
+ case GENERIC_REGNUM_FLAGS: // Processor flags register
+ set = e_regSetGPR;
+ reg = e_regNumGPR_srr1;
+ break;
+
+ default:
+ return false;
+ }
+ }
+
+ if (!m_state.RegsAreValid(set))
+ return false;
+
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo) {
+ value->info = *regInfo;
+ switch (set) {
+ case e_regSetGPR:
+ if (reg < k_num_gpr_registers) {
+ value->value.uint32 =
+ (&m_state.gpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(srr0))[reg];
+ return true;
+ }
+ break;
+
+ case e_regSetFPR:
+ if (reg < 32) {
+ value->value.float64 =
+ m_state.fpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(fpregs)[reg];
+ return true;
+ } else if (reg == 32) {
+ value->value.uint32 =
+ m_state.fpr.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(fpscr);
+ return true;
+ }
+ break;
+
+ case e_regSetEXC:
+ if (reg < k_num_exc_registers) {
+ value->value.uint32 =
+ (&m_state.exc.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(dar))[reg];
+ return true;
+ }
+ break;
+
+ case e_regSetVEC:
+ if (reg < k_num_vec_registers) {
+ if (reg < 33) // FP0 - FP31 and VSCR
+ {
+ // Copy all 4 uint32 values for this vector register
+ value->value.v_uint32[0] =
+ m_state.vec.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(save_vr)[reg]
+ [0];
+ value->value.v_uint32[1] =
+ m_state.vec.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(save_vr)[reg]
+ [1];
+ value->value.v_uint32[2] =
+ m_state.vec.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(save_vr)[reg]
+ [2];
+ value->value.v_uint32[3] =
+ m_state.vec.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(save_vr)[reg]
+ [3];
+ return true;
+ } else if (reg == 34) // VRVALID
+ {
+ value->value.uint32 =
+ m_state.vec.PREFIX_DOUBLE_UNDERSCORE_DARWIN_UNIX03(save_vrvalid);
+ return true;
+ }
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+kern_return_t DNBArchMachPPC::GetRegisterState(int set, bool force) {
+ switch (set) {
+ case e_regSetALL:
+ return GetGPRState(force) | GetFPRState(force) | GetEXCState(force) |
+ GetVECState(force);
+ case e_regSetGPR:
+ return GetGPRState(force);
+ case e_regSetFPR:
+ return GetFPRState(force);
+ case e_regSetEXC:
+ return GetEXCState(force);
+ case e_regSetVEC:
+ return GetVECState(force);
+ default:
+ break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+kern_return_t DNBArchMachPPC::SetRegisterState(int set) {
+ // Make sure we have a valid context to set.
+ kern_return_t err = GetRegisterState(set, false);
+ if (err != KERN_SUCCESS)
+ return err;
+
+ switch (set) {
+ case e_regSetALL:
+ return SetGPRState() | SetFPRState() | SetEXCState() | SetVECState();
+ case e_regSetGPR:
+ return SetGPRState();
+ case e_regSetFPR:
+ return SetFPRState();
+ case e_regSetEXC:
+ return SetEXCState();
+ case e_regSetVEC:
+ return SetVECState();
+ default:
+ break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+bool DNBArchMachPPC::RegisterSetStateIsValid(int set) const {
+ return m_state.RegsAreValid(set);
+}
+
+#endif // #if defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__)
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.h
new file mode 100644
index 00000000000..7d20f18d027
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/ppc/DNBArchImpl.h
@@ -0,0 +1,159 @@
+//===-- DNBArchImpl.h -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DebugNubArchMachPPC_h__
+#define __DebugNubArchMachPPC_h__
+
+#if defined(__powerpc__) || defined(__ppc__) || defined(__ppc64__)
+
+#include "DNBArch.h"
+
+class MachThread;
+
+class DNBArchMachPPC : public DNBArchProtocol {
+public:
+ DNBArchMachPPC(MachThread *thread) : m_thread(thread), m_state() {}
+
+ virtual ~DNBArchMachPPC() {}
+
+ virtual const DNBRegisterSetInfo *
+ GetRegisterSetInfo(nub_size_t *num_reg_sets) const;
+ virtual bool GetRegisterValue(uint32_t set, uint32_t reg,
+ DNBRegisterValue *value) const;
+ virtual kern_return_t GetRegisterState(int set, bool force);
+ virtual kern_return_t SetRegisterState(int set);
+ virtual bool RegisterSetStateIsValid(int set) const;
+
+ virtual uint64_t GetPC(uint64_t failValue); // Get program counter
+ virtual kern_return_t SetPC(uint64_t value);
+ virtual uint64_t GetSP(uint64_t failValue); // Get stack pointer
+ virtual bool ThreadWillResume();
+ virtual bool ThreadDidStop();
+
+ static const uint8_t *SoftwareBreakpointOpcode(nub_size_t byte_size);
+ static uint32_t GetCPUType();
+
+protected:
+ kern_return_t EnableHardwareSingleStep(bool enable);
+
+ enum RegisterSet {
+ e_regSetALL = REGISTER_SET_ALL,
+ e_regSetGPR,
+ e_regSetFPR,
+ e_regSetEXC,
+ e_regSetVEC,
+ kNumRegisterSets
+ };
+
+ typedef enum RegisterSetWordSizeTag {
+ e_regSetWordSizeGPR = PPC_THREAD_STATE_COUNT,
+ e_regSetWordSizeFPR = PPC_FLOAT_STATE_COUNT,
+ e_regSetWordSizeEXC = PPC_EXCEPTION_STATE_COUNT,
+ e_regSetWordSizeVEC = PPC_VECTOR_STATE_COUNT
+ } RegisterSetWordSize;
+
+ enum { Read = 0, Write = 1, kNumErrors = 2 };
+
+ struct State {
+ ppc_thread_state_t gpr;
+ ppc_float_state_t fpr;
+ ppc_exception_state_t exc;
+ ppc_vector_state_t vec;
+ kern_return_t gpr_errs[2]; // Read/Write errors
+ kern_return_t fpr_errs[2]; // Read/Write errors
+ kern_return_t exc_errs[2]; // Read/Write errors
+ kern_return_t vec_errs[2]; // Read/Write errors
+
+ State() {
+ uint32_t i;
+ for (i = 0; i < kNumErrors; i++) {
+ gpr_errs[i] = -1;
+ fpr_errs[i] = -1;
+ exc_errs[i] = -1;
+ vec_errs[i] = -1;
+ }
+ }
+ void InvalidateAllRegisterStates() { SetError(e_regSetALL, Read, -1); }
+ kern_return_t GetError(int set, uint32_t err_idx) const {
+ if (err_idx < kNumErrors) {
+ switch (set) {
+ // When getting all errors, just OR all values together to see if
+ // we got any kind of error.
+ case e_regSetALL:
+ return gpr_errs[err_idx] | fpr_errs[err_idx] | exc_errs[err_idx] |
+ vec_errs[err_idx];
+ case e_regSetGPR:
+ return gpr_errs[err_idx];
+ case e_regSetFPR:
+ return fpr_errs[err_idx];
+ case e_regSetEXC:
+ return exc_errs[err_idx];
+ case e_regSetVEC:
+ return vec_errs[err_idx];
+ default:
+ break;
+ }
+ }
+ return -1;
+ }
+ bool SetError(int set, uint32_t err_idx, kern_return_t err) {
+ if (err_idx < kNumErrors) {
+ switch (set) {
+ case e_regSetALL:
+ gpr_errs[err_idx] = fpr_errs[err_idx] = exc_errs[err_idx] =
+ vec_errs[err_idx] = err;
+ return true;
+
+ case e_regSetGPR:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case e_regSetFPR:
+ fpr_errs[err_idx] = err;
+ return true;
+
+ case e_regSetEXC:
+ exc_errs[err_idx] = err;
+ return true;
+
+ case e_regSetVEC:
+ vec_errs[err_idx] = err;
+ return true;
+
+ default:
+ break;
+ }
+ }
+ return false;
+ }
+ bool RegsAreValid(int set) const {
+ return GetError(set, Read) == KERN_SUCCESS;
+ }
+ };
+
+ kern_return_t GetGPRState(bool force);
+ kern_return_t GetFPRState(bool force);
+ kern_return_t GetEXCState(bool force);
+ kern_return_t GetVECState(bool force);
+
+ kern_return_t SetGPRState();
+ kern_return_t SetFPRState();
+ kern_return_t SetEXCState();
+ kern_return_t SetVECState();
+
+protected:
+ MachThread *m_thread;
+ State m_state;
+};
+
+#endif // #if defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__)
+#endif // #ifndef __DebugNubArchMachPPC_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/stack_logging.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/stack_logging.h
new file mode 100644
index 00000000000..5209e38a08e
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/stack_logging.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 1999-2007 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef malloc_history_test_stack_logging_h
+#define malloc_history_test_stack_logging_h
+
+#import <malloc/malloc.h>
+
+#define stack_logging_type_free 0
+#define stack_logging_type_generic \
+ 1 /* anything that is not allocation/deallocation */
+#define stack_logging_type_alloc 2 /* malloc, realloc, etc... */
+#define stack_logging_type_dealloc 4 /* free, realloc, etc... */
+
+// Following flags are absorbed by stack_logging_log_stack()
+#define stack_logging_flag_zone 8 /* NSZoneMalloc, etc... */
+#define stack_logging_flag_calloc 16 /* multiply arguments to get the size */
+#define stack_logging_flag_object \
+ 32 /* NSAllocateObject(Class, extraBytes, zone) */
+#define stack_logging_flag_cleared 64 /* for NewEmptyHandle */
+#define stack_logging_flag_handle 128 /* for Handle (de-)allocation routines \
+ */
+#define stack_logging_flag_set_handle_size \
+ 256 /* (Handle, newSize) treated specially */
+
+/* Macro used to disguise addresses so that leak finding can work */
+#define STACK_LOGGING_DISGUISE(address) \
+ ((address) ^ 0x00005555) /* nicely idempotent */
+
+extern "C" int
+ stack_logging_enable_logging; /* when clear, no logging takes place */
+extern "C" int stack_logging_dontcompact; /* default is to compact; when set
+ does not compact alloc/free logs;
+ useful for tracing history */
+
+extern "C" void stack_logging_log_stack(unsigned type, unsigned arg1,
+ unsigned arg2, unsigned arg3,
+ unsigned result,
+ unsigned num_hot_to_skip);
+/* This is the old log-to-memory logger, which is now deprecated. It remains
+ * for compatibility with performance tools that haven't been updated to
+ * disk_stack_logging_log_stack() yet. */
+
+extern "C" void
+__disk_stack_logging_log_stack(uint32_t type_flags, uintptr_t zone_ptr,
+ uintptr_t size, uintptr_t ptr_arg,
+ uintptr_t return_val, uint32_t num_hot_to_skip);
+/* Fits as the malloc_logger; logs malloc/free/realloc events and can log custom
+ * events if called directly */
+
+/* 64-bit-aware stack log access. */
+typedef struct {
+ uint32_t type_flags;
+ uint64_t stack_identifier;
+ uint64_t argument;
+ mach_vm_address_t address;
+} mach_stack_logging_record_t;
+
+extern "C" kern_return_t
+__mach_stack_logging_get_frames(task_t task, mach_vm_address_t address,
+ mach_vm_address_t *stack_frames_buffer,
+ uint32_t max_stack_frames, uint32_t *count);
+/* Gets the last allocation record (malloc, realloc, or free) about address */
+
+extern "C" kern_return_t __mach_stack_logging_enumerate_records(
+ task_t task, mach_vm_address_t address,
+ void enumerator(mach_stack_logging_record_t, void *), void *context);
+/* Applies enumerator to all records involving address sending context as
+ * enumerator's second parameter; if !address, applies enumerator to all records
+ */
+
+extern "C" kern_return_t __mach_stack_logging_frames_for_uniqued_stack(
+ task_t task, uint64_t stack_identifier,
+ mach_vm_address_t *stack_frames_buffer, uint32_t max_stack_frames,
+ uint32_t *count);
+/* Given a uniqued_stack fills stack_frames_buffer */
+
+#pragma mark -
+#pragma mark Legacy
+
+/* The following is the old 32-bit-only, in-process-memory stack logging. This
+ * is deprecated and clients should move to the above 64-bit-aware disk stack
+ * logging SPI. */
+
+typedef struct {
+ unsigned type;
+ unsigned uniqued_stack;
+ unsigned argument;
+ unsigned address; /* disguised, to avoid confusing leaks */
+} stack_logging_record_t;
+
+typedef struct {
+ unsigned overall_num_bytes;
+ unsigned num_records;
+ unsigned lock; /* 0 means OK to lock; used for inter-process locking */
+ unsigned *uniquing_table; /* allocated using vm_allocate() */
+ /* hashtable organized as (PC, uniqued parent)
+ Only the second half of the table is active
+ To enable us to grow dynamically */
+ unsigned uniquing_table_num_pages; /* number of pages of the table */
+ unsigned extra_retain_count; /* not used by stack_logging_log_stack */
+ unsigned filler[2]; /* align to cache lines for better performance */
+ stack_logging_record_t records[0]; /* records follow here */
+} stack_logging_record_list_t;
+
+extern "C" stack_logging_record_list_t *stack_logging_the_record_list;
+/* This is the global variable containing all logs */
+
+extern "C" kern_return_t
+stack_logging_get_frames(task_t task, memory_reader_t reader,
+ vm_address_t address,
+ vm_address_t *stack_frames_buffer,
+ unsigned max_stack_frames, unsigned *num_frames);
+/* Gets the last record in stack_logging_the_record_list about address */
+
+#define STACK_LOGGING_ENUMERATION_PROVIDED \
+ 1 // temporary to avoid dependencies between projects
+
+extern "C" kern_return_t stack_logging_enumerate_records(
+ task_t task, memory_reader_t reader, vm_address_t address,
+ void enumerator(stack_logging_record_t, void *), void *context);
+/* Gets all the records about address;
+ If !address, gets all records */
+
+extern "C" kern_return_t stack_logging_frames_for_uniqued_stack(
+ task_t task, memory_reader_t reader, unsigned uniqued_stack,
+ vm_address_t *stack_frames_buffer, unsigned max_stack_frames,
+ unsigned *num_frames);
+/* Given a uniqued_stack fills stack_frames_buffer */
+
+extern "C" void thread_stack_pcs(vm_address_t *buffer, unsigned max,
+ unsigned *num);
+/* Convenience to fill buffer with the PCs of the frames, starting with the hot
+ frames;
+ num: returned number of frames
+ */
+
+#endif
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp
new file mode 100644
index 00000000000..319215e8a7f
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp
@@ -0,0 +1,2885 @@
+//===-- DNBArchImplX86_64.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include <sys/cdefs.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include "DNBLog.h"
+#include "MacOSX/x86_64/DNBArchImplX86_64.h"
+#include "MachProcess.h"
+#include "MachThread.h"
+#include <mach/mach.h>
+#include <stdlib.h>
+
+#if defined(LLDB_DEBUGSERVER_RELEASE) || defined(LLDB_DEBUGSERVER_DEBUG)
+enum debugState { debugStateUnknown, debugStateOff, debugStateOn };
+
+static debugState sFPUDebugState = debugStateUnknown;
+static debugState sAVXForceState = debugStateUnknown;
+
+static bool DebugFPURegs() {
+ if (sFPUDebugState == debugStateUnknown) {
+ if (getenv("DNB_DEBUG_FPU_REGS"))
+ sFPUDebugState = debugStateOn;
+ else
+ sFPUDebugState = debugStateOff;
+ }
+
+ return (sFPUDebugState == debugStateOn);
+}
+
+static bool ForceAVXRegs() {
+ if (sFPUDebugState == debugStateUnknown) {
+ if (getenv("DNB_DEBUG_X86_FORCE_AVX_REGS"))
+ sAVXForceState = debugStateOn;
+ else
+ sAVXForceState = debugStateOff;
+ }
+
+ return (sAVXForceState == debugStateOn);
+}
+
+#define DEBUG_FPU_REGS (DebugFPURegs())
+#define FORCE_AVX_REGS (ForceAVXRegs())
+#else
+#define DEBUG_FPU_REGS (0)
+#define FORCE_AVX_REGS (0)
+#endif
+
+bool DetectHardwareFeature(const char *feature) {
+ int answer = 0;
+ size_t answer_size = sizeof(answer);
+ int error = ::sysctlbyname(feature, &answer, &answer_size, NULL, 0);
+ return error == 0 && answer != 0;
+}
+
+enum AVXPresence { eAVXUnknown = -1, eAVXNotPresent = 0, eAVXPresent = 1 };
+
+bool LogAVXAndReturn(AVXPresence has_avx, int err, const char * os_ver) {
+ DNBLogThreadedIf(LOG_THREAD,
+ "CPUHasAVX(): g_has_avx = %i (err = %i, os_ver = %s)",
+ has_avx, err, os_ver);
+ return (has_avx == eAVXPresent);
+}
+
+extern "C" bool CPUHasAVX() {
+ static AVXPresence g_has_avx = eAVXUnknown;
+ if (g_has_avx != eAVXUnknown)
+ return LogAVXAndReturn(g_has_avx, 0, "");
+
+ g_has_avx = eAVXNotPresent;
+
+ // OS X 10.7.3 and earlier have a bug in thread_get_state that truncated the
+ // size of the return. To work around this we have to disable AVX debugging
+ // on hosts prior to 10.7.3 (<rdar://problem/10122874>).
+ int mib[2];
+ char buffer[1024];
+ size_t length = sizeof(buffer);
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_OSVERSION;
+
+ // KERN_OSVERSION returns the build number which is a number signifying the
+ // major version, a capitol letter signifying the minor version, and numbers
+ // signifying the build (ex: on 10.12.3, the returned value is 16D32).
+ int err = ::sysctl(mib, 2, &buffer, &length, NULL, 0);
+ if (err != 0)
+ return LogAVXAndReturn(g_has_avx, err, "");
+
+ size_t first_letter = 0;
+ for (; first_letter < length; ++first_letter) {
+ // This is looking for the first uppercase letter
+ if (isupper(buffer[first_letter]))
+ break;
+ }
+ char letter = buffer[first_letter];
+ buffer[first_letter] = '\0';
+ auto major_ver = strtoull(buffer, NULL, 0);
+ buffer[first_letter] = letter;
+
+ // In this check we're looking to see that our major and minor version numer
+ // was >= 11E, which is the 10.7.4 release.
+ if (major_ver < 11 || (major_ver == 11 && letter < 'E'))
+ return LogAVXAndReturn(g_has_avx, err, buffer);
+ if (DetectHardwareFeature("hw.optional.avx1_0"))
+ g_has_avx = eAVXPresent;
+
+ return LogAVXAndReturn(g_has_avx, err, buffer);
+}
+
+extern "C" bool CPUHasAVX512f() {
+ static AVXPresence g_has_avx512f = eAVXUnknown;
+ if (g_has_avx512f != eAVXUnknown)
+ return g_has_avx512f == eAVXPresent;
+
+ g_has_avx512f = DetectHardwareFeature("hw.optional.avx512f") ? eAVXPresent
+ : eAVXNotPresent;
+
+ return (g_has_avx512f == eAVXPresent);
+}
+
+uint64_t DNBArchImplX86_64::GetPC(uint64_t failValue) {
+ // Get program counter
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.context.gpr.__rip;
+ return failValue;
+}
+
+kern_return_t DNBArchImplX86_64::SetPC(uint64_t value) {
+ // Get program counter
+ kern_return_t err = GetGPRState(false);
+ if (err == KERN_SUCCESS) {
+ m_state.context.gpr.__rip = value;
+ err = SetGPRState();
+ }
+ return err == KERN_SUCCESS;
+}
+
+uint64_t DNBArchImplX86_64::GetSP(uint64_t failValue) {
+ // Get stack pointer
+ if (GetGPRState(false) == KERN_SUCCESS)
+ return m_state.context.gpr.__rsp;
+ return failValue;
+}
+
+// Uncomment the value below to verify the values in the debugger.
+//#define DEBUG_GPR_VALUES 1 // DO NOT CHECK IN WITH THIS DEFINE ENABLED
+
+kern_return_t DNBArchImplX86_64::GetGPRState(bool force) {
+ if (force || m_state.GetError(e_regSetGPR, Read)) {
+#if DEBUG_GPR_VALUES
+ m_state.context.gpr.__rax = ('a' << 8) + 'x';
+ m_state.context.gpr.__rbx = ('b' << 8) + 'x';
+ m_state.context.gpr.__rcx = ('c' << 8) + 'x';
+ m_state.context.gpr.__rdx = ('d' << 8) + 'x';
+ m_state.context.gpr.__rdi = ('d' << 8) + 'i';
+ m_state.context.gpr.__rsi = ('s' << 8) + 'i';
+ m_state.context.gpr.__rbp = ('b' << 8) + 'p';
+ m_state.context.gpr.__rsp = ('s' << 8) + 'p';
+ m_state.context.gpr.__r8 = ('r' << 8) + '8';
+ m_state.context.gpr.__r9 = ('r' << 8) + '9';
+ m_state.context.gpr.__r10 = ('r' << 8) + 'a';
+ m_state.context.gpr.__r11 = ('r' << 8) + 'b';
+ m_state.context.gpr.__r12 = ('r' << 8) + 'c';
+ m_state.context.gpr.__r13 = ('r' << 8) + 'd';
+ m_state.context.gpr.__r14 = ('r' << 8) + 'e';
+ m_state.context.gpr.__r15 = ('r' << 8) + 'f';
+ m_state.context.gpr.__rip = ('i' << 8) + 'p';
+ m_state.context.gpr.__rflags = ('f' << 8) + 'l';
+ m_state.context.gpr.__cs = ('c' << 8) + 's';
+ m_state.context.gpr.__fs = ('f' << 8) + 's';
+ m_state.context.gpr.__gs = ('g' << 8) + 's';
+ m_state.SetError(e_regSetGPR, Read, 0);
+#else
+ mach_msg_type_number_t count = e_regSetWordSizeGPR;
+ m_state.SetError(
+ e_regSetGPR, Read,
+ ::thread_get_state(m_thread->MachPortNumber(), __x86_64_THREAD_STATE,
+ (thread_state_t)&m_state.context.gpr, &count));
+ DNBLogThreadedIf(
+ LOG_THREAD,
+ "::thread_get_state (0x%4.4x, %u, &gpr, %u) => 0x%8.8x"
+ "\n\trax = %16.16llx rbx = %16.16llx rcx = %16.16llx rdx = %16.16llx"
+ "\n\trdi = %16.16llx rsi = %16.16llx rbp = %16.16llx rsp = %16.16llx"
+ "\n\t r8 = %16.16llx r9 = %16.16llx r10 = %16.16llx r11 = %16.16llx"
+ "\n\tr12 = %16.16llx r13 = %16.16llx r14 = %16.16llx r15 = %16.16llx"
+ "\n\trip = %16.16llx"
+ "\n\tflg = %16.16llx cs = %16.16llx fs = %16.16llx gs = %16.16llx",
+ m_thread->MachPortNumber(), x86_THREAD_STATE64,
+ x86_THREAD_STATE64_COUNT, m_state.GetError(e_regSetGPR, Read),
+ m_state.context.gpr.__rax, m_state.context.gpr.__rbx,
+ m_state.context.gpr.__rcx, m_state.context.gpr.__rdx,
+ m_state.context.gpr.__rdi, m_state.context.gpr.__rsi,
+ m_state.context.gpr.__rbp, m_state.context.gpr.__rsp,
+ m_state.context.gpr.__r8, m_state.context.gpr.__r9,
+ m_state.context.gpr.__r10, m_state.context.gpr.__r11,
+ m_state.context.gpr.__r12, m_state.context.gpr.__r13,
+ m_state.context.gpr.__r14, m_state.context.gpr.__r15,
+ m_state.context.gpr.__rip, m_state.context.gpr.__rflags,
+ m_state.context.gpr.__cs, m_state.context.gpr.__fs,
+ m_state.context.gpr.__gs);
+
+// DNBLogThreadedIf (LOG_THREAD, "thread_get_state(0x%4.4x, %u, &gpr, %u)
+// => 0x%8.8x"
+// "\n\trax = %16.16llx"
+// "\n\trbx = %16.16llx"
+// "\n\trcx = %16.16llx"
+// "\n\trdx = %16.16llx"
+// "\n\trdi = %16.16llx"
+// "\n\trsi = %16.16llx"
+// "\n\trbp = %16.16llx"
+// "\n\trsp = %16.16llx"
+// "\n\t r8 = %16.16llx"
+// "\n\t r9 = %16.16llx"
+// "\n\tr10 = %16.16llx"
+// "\n\tr11 = %16.16llx"
+// "\n\tr12 = %16.16llx"
+// "\n\tr13 = %16.16llx"
+// "\n\tr14 = %16.16llx"
+// "\n\tr15 = %16.16llx"
+// "\n\trip = %16.16llx"
+// "\n\tflg = %16.16llx"
+// "\n\t cs = %16.16llx"
+// "\n\t fs = %16.16llx"
+// "\n\t gs = %16.16llx",
+// m_thread->MachPortNumber(),
+// x86_THREAD_STATE64,
+// x86_THREAD_STATE64_COUNT,
+// m_state.GetError(e_regSetGPR, Read),
+// m_state.context.gpr.__rax,
+// m_state.context.gpr.__rbx,
+// m_state.context.gpr.__rcx,
+// m_state.context.gpr.__rdx,
+// m_state.context.gpr.__rdi,
+// m_state.context.gpr.__rsi,
+// m_state.context.gpr.__rbp,
+// m_state.context.gpr.__rsp,
+// m_state.context.gpr.__r8,
+// m_state.context.gpr.__r9,
+// m_state.context.gpr.__r10,
+// m_state.context.gpr.__r11,
+// m_state.context.gpr.__r12,
+// m_state.context.gpr.__r13,
+// m_state.context.gpr.__r14,
+// m_state.context.gpr.__r15,
+// m_state.context.gpr.__rip,
+// m_state.context.gpr.__rflags,
+// m_state.context.gpr.__cs,
+// m_state.context.gpr.__fs,
+// m_state.context.gpr.__gs);
+#endif
+ }
+ return m_state.GetError(e_regSetGPR, Read);
+}
+
+// Uncomment the value below to verify the values in the debugger.
+//#define DEBUG_FPU_REGS 1 // DO NOT CHECK IN WITH THIS DEFINE ENABLED
+
+kern_return_t DNBArchImplX86_64::GetFPUState(bool force) {
+ if (force || m_state.GetError(e_regSetFPU, Read)) {
+ if (DEBUG_FPU_REGS) {
+ m_state.context.fpu.no_avx.__fpu_reserved[0] = -1;
+ m_state.context.fpu.no_avx.__fpu_reserved[1] = -1;
+ *(uint16_t *)&(m_state.context.fpu.no_avx.__fpu_fcw) = 0x1234;
+ *(uint16_t *)&(m_state.context.fpu.no_avx.__fpu_fsw) = 0x5678;
+ m_state.context.fpu.no_avx.__fpu_ftw = 1;
+ m_state.context.fpu.no_avx.__fpu_rsrv1 = UINT8_MAX;
+ m_state.context.fpu.no_avx.__fpu_fop = 2;
+ m_state.context.fpu.no_avx.__fpu_ip = 3;
+ m_state.context.fpu.no_avx.__fpu_cs = 4;
+ m_state.context.fpu.no_avx.__fpu_rsrv2 = 5;
+ m_state.context.fpu.no_avx.__fpu_dp = 6;
+ m_state.context.fpu.no_avx.__fpu_ds = 7;
+ m_state.context.fpu.no_avx.__fpu_rsrv3 = UINT16_MAX;
+ m_state.context.fpu.no_avx.__fpu_mxcsr = 8;
+ m_state.context.fpu.no_avx.__fpu_mxcsrmask = 9;
+ for (int i = 0; i < 16; ++i) {
+ if (i < 10) {
+ m_state.context.fpu.no_avx.__fpu_stmm0.__mmst_reg[i] = 'a';
+ m_state.context.fpu.no_avx.__fpu_stmm1.__mmst_reg[i] = 'b';
+ m_state.context.fpu.no_avx.__fpu_stmm2.__mmst_reg[i] = 'c';
+ m_state.context.fpu.no_avx.__fpu_stmm3.__mmst_reg[i] = 'd';
+ m_state.context.fpu.no_avx.__fpu_stmm4.__mmst_reg[i] = 'e';
+ m_state.context.fpu.no_avx.__fpu_stmm5.__mmst_reg[i] = 'f';
+ m_state.context.fpu.no_avx.__fpu_stmm6.__mmst_reg[i] = 'g';
+ m_state.context.fpu.no_avx.__fpu_stmm7.__mmst_reg[i] = 'h';
+ } else {
+ m_state.context.fpu.no_avx.__fpu_stmm0.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm1.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm2.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm3.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm4.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm5.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm6.__mmst_reg[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_stmm7.__mmst_reg[i] = INT8_MIN;
+ }
+
+ m_state.context.fpu.no_avx.__fpu_xmm0.__xmm_reg[i] = '0';
+ m_state.context.fpu.no_avx.__fpu_xmm1.__xmm_reg[i] = '1';
+ m_state.context.fpu.no_avx.__fpu_xmm2.__xmm_reg[i] = '2';
+ m_state.context.fpu.no_avx.__fpu_xmm3.__xmm_reg[i] = '3';
+ m_state.context.fpu.no_avx.__fpu_xmm4.__xmm_reg[i] = '4';
+ m_state.context.fpu.no_avx.__fpu_xmm5.__xmm_reg[i] = '5';
+ m_state.context.fpu.no_avx.__fpu_xmm6.__xmm_reg[i] = '6';
+ m_state.context.fpu.no_avx.__fpu_xmm7.__xmm_reg[i] = '7';
+ m_state.context.fpu.no_avx.__fpu_xmm8.__xmm_reg[i] = '8';
+ m_state.context.fpu.no_avx.__fpu_xmm9.__xmm_reg[i] = '9';
+ m_state.context.fpu.no_avx.__fpu_xmm10.__xmm_reg[i] = 'A';
+ m_state.context.fpu.no_avx.__fpu_xmm11.__xmm_reg[i] = 'B';
+ m_state.context.fpu.no_avx.__fpu_xmm12.__xmm_reg[i] = 'C';
+ m_state.context.fpu.no_avx.__fpu_xmm13.__xmm_reg[i] = 'D';
+ m_state.context.fpu.no_avx.__fpu_xmm14.__xmm_reg[i] = 'E';
+ m_state.context.fpu.no_avx.__fpu_xmm15.__xmm_reg[i] = 'F';
+ }
+ for (int i = 0; i < sizeof(m_state.context.fpu.no_avx.__fpu_rsrv4); ++i)
+ m_state.context.fpu.no_avx.__fpu_rsrv4[i] = INT8_MIN;
+ m_state.context.fpu.no_avx.__fpu_reserved1 = -1;
+
+ if (CPUHasAVX() || FORCE_AVX_REGS) {
+ for (int i = 0; i < 16; ++i) {
+ m_state.context.fpu.avx.__fpu_ymmh0.__xmm_reg[i] = '0' + i;
+ m_state.context.fpu.avx.__fpu_ymmh1.__xmm_reg[i] = '1' + i;
+ m_state.context.fpu.avx.__fpu_ymmh2.__xmm_reg[i] = '2' + i;
+ m_state.context.fpu.avx.__fpu_ymmh3.__xmm_reg[i] = '3' + i;
+ m_state.context.fpu.avx.__fpu_ymmh4.__xmm_reg[i] = '4' + i;
+ m_state.context.fpu.avx.__fpu_ymmh5.__xmm_reg[i] = '5' + i;
+ m_state.context.fpu.avx.__fpu_ymmh6.__xmm_reg[i] = '6' + i;
+ m_state.context.fpu.avx.__fpu_ymmh7.__xmm_reg[i] = '7' + i;
+ m_state.context.fpu.avx.__fpu_ymmh8.__xmm_reg[i] = '8' + i;
+ m_state.context.fpu.avx.__fpu_ymmh9.__xmm_reg[i] = '9' + i;
+ m_state.context.fpu.avx.__fpu_ymmh10.__xmm_reg[i] = 'A' + i;
+ m_state.context.fpu.avx.__fpu_ymmh11.__xmm_reg[i] = 'B' + i;
+ m_state.context.fpu.avx.__fpu_ymmh12.__xmm_reg[i] = 'C' + i;
+ m_state.context.fpu.avx.__fpu_ymmh13.__xmm_reg[i] = 'D' + i;
+ m_state.context.fpu.avx.__fpu_ymmh14.__xmm_reg[i] = 'E' + i;
+ m_state.context.fpu.avx.__fpu_ymmh15.__xmm_reg[i] = 'F' + i;
+ }
+ for (int i = 0; i < sizeof(m_state.context.fpu.avx.__avx_reserved1); ++i)
+ m_state.context.fpu.avx.__avx_reserved1[i] = INT8_MIN;
+ }
+ if (CPUHasAVX512f() || FORCE_AVX_REGS) {
+ for (int i = 0; i < 8; ++i) {
+ m_state.context.fpu.avx512f.__fpu_k0.__opmask_reg[i] = '0';
+ m_state.context.fpu.avx512f.__fpu_k1.__opmask_reg[i] = '1';
+ m_state.context.fpu.avx512f.__fpu_k2.__opmask_reg[i] = '2';
+ m_state.context.fpu.avx512f.__fpu_k3.__opmask_reg[i] = '3';
+ m_state.context.fpu.avx512f.__fpu_k4.__opmask_reg[i] = '4';
+ m_state.context.fpu.avx512f.__fpu_k5.__opmask_reg[i] = '5';
+ m_state.context.fpu.avx512f.__fpu_k6.__opmask_reg[i] = '6';
+ m_state.context.fpu.avx512f.__fpu_k7.__opmask_reg[i] = '7';
+ }
+
+ for (int i = 0; i < 32; ++i) {
+ m_state.context.fpu.avx512f.__fpu_zmmh0.__ymm_reg[i] = '0';
+ m_state.context.fpu.avx512f.__fpu_zmmh1.__ymm_reg[i] = '1';
+ m_state.context.fpu.avx512f.__fpu_zmmh2.__ymm_reg[i] = '2';
+ m_state.context.fpu.avx512f.__fpu_zmmh3.__ymm_reg[i] = '3';
+ m_state.context.fpu.avx512f.__fpu_zmmh4.__ymm_reg[i] = '4';
+ m_state.context.fpu.avx512f.__fpu_zmmh5.__ymm_reg[i] = '5';
+ m_state.context.fpu.avx512f.__fpu_zmmh6.__ymm_reg[i] = '6';
+ m_state.context.fpu.avx512f.__fpu_zmmh7.__ymm_reg[i] = '7';
+ m_state.context.fpu.avx512f.__fpu_zmmh8.__ymm_reg[i] = '8';
+ m_state.context.fpu.avx512f.__fpu_zmmh9.__ymm_reg[i] = '9';
+ m_state.context.fpu.avx512f.__fpu_zmmh10.__ymm_reg[i] = 'A';
+ m_state.context.fpu.avx512f.__fpu_zmmh11.__ymm_reg[i] = 'B';
+ m_state.context.fpu.avx512f.__fpu_zmmh12.__ymm_reg[i] = 'C';
+ m_state.context.fpu.avx512f.__fpu_zmmh13.__ymm_reg[i] = 'D';
+ m_state.context.fpu.avx512f.__fpu_zmmh14.__ymm_reg[i] = 'E';
+ m_state.context.fpu.avx512f.__fpu_zmmh15.__ymm_reg[i] = 'F';
+ }
+ for (int i = 0; i < 64; ++i) {
+ m_state.context.fpu.avx512f.__fpu_zmm16.__zmm_reg[i] = 'G';
+ m_state.context.fpu.avx512f.__fpu_zmm17.__zmm_reg[i] = 'H';
+ m_state.context.fpu.avx512f.__fpu_zmm18.__zmm_reg[i] = 'I';
+ m_state.context.fpu.avx512f.__fpu_zmm19.__zmm_reg[i] = 'J';
+ m_state.context.fpu.avx512f.__fpu_zmm20.__zmm_reg[i] = 'K';
+ m_state.context.fpu.avx512f.__fpu_zmm21.__zmm_reg[i] = 'L';
+ m_state.context.fpu.avx512f.__fpu_zmm22.__zmm_reg[i] = 'M';
+ m_state.context.fpu.avx512f.__fpu_zmm23.__zmm_reg[i] = 'N';
+ m_state.context.fpu.avx512f.__fpu_zmm24.__zmm_reg[i] = 'O';
+ m_state.context.fpu.avx512f.__fpu_zmm25.__zmm_reg[i] = 'P';
+ m_state.context.fpu.avx512f.__fpu_zmm26.__zmm_reg[i] = 'Q';
+ m_state.context.fpu.avx512f.__fpu_zmm27.__zmm_reg[i] = 'R';
+ m_state.context.fpu.avx512f.__fpu_zmm28.__zmm_reg[i] = 'S';
+ m_state.context.fpu.avx512f.__fpu_zmm29.__zmm_reg[i] = 'T';
+ m_state.context.fpu.avx512f.__fpu_zmm30.__zmm_reg[i] = 'U';
+ m_state.context.fpu.avx512f.__fpu_zmm31.__zmm_reg[i] = 'V';
+ }
+ }
+ m_state.SetError(e_regSetFPU, Read, 0);
+ } else {
+ mach_msg_type_number_t count = e_regSetWordSizeFPU;
+ int flavor = __x86_64_FLOAT_STATE;
+ // On a machine with the AVX512 register set, a process only gets a
+ // full AVX512 register context after it uses the AVX512 registers;
+ // if the process has not yet triggered this change, trying to fetch
+ // the AVX512 registers will fail. Fall through to fetching the AVX
+ // registers.
+ if (CPUHasAVX512f() || FORCE_AVX_REGS) {
+ count = e_regSetWordSizeAVX512f;
+ flavor = __x86_64_AVX512F_STATE;
+ m_state.SetError(e_regSetFPU, Read,
+ ::thread_get_state(m_thread->MachPortNumber(), flavor,
+ (thread_state_t)&m_state.context.fpu,
+ &count));
+ DNBLogThreadedIf(LOG_THREAD,
+ "::thread_get_state (0x%4.4x, %u, &fpu, %u => 0x%8.8x",
+ m_thread->MachPortNumber(), flavor, (uint32_t)count,
+ m_state.GetError(e_regSetFPU, Read));
+
+ if (m_state.GetError(e_regSetFPU, Read) == KERN_SUCCESS)
+ return m_state.GetError(e_regSetFPU, Read);
+ else
+ DNBLogThreadedIf(LOG_THREAD,
+ "::thread_get_state attempted fetch of avx512 fpu regctx failed, will try fetching avx");
+ }
+ if (CPUHasAVX() || FORCE_AVX_REGS) {
+ count = e_regSetWordSizeAVX;
+ flavor = __x86_64_AVX_STATE;
+ }
+ m_state.SetError(e_regSetFPU, Read,
+ ::thread_get_state(m_thread->MachPortNumber(), flavor,
+ (thread_state_t)&m_state.context.fpu,
+ &count));
+ DNBLogThreadedIf(LOG_THREAD,
+ "::thread_get_state (0x%4.4x, %u, &fpu, %u => 0x%8.8x",
+ m_thread->MachPortNumber(), flavor, (uint32_t)count,
+ m_state.GetError(e_regSetFPU, Read));
+ }
+ }
+ return m_state.GetError(e_regSetFPU, Read);
+}
+
+kern_return_t DNBArchImplX86_64::GetEXCState(bool force) {
+ if (force || m_state.GetError(e_regSetEXC, Read)) {
+ mach_msg_type_number_t count = e_regSetWordSizeEXC;
+ m_state.SetError(
+ e_regSetEXC, Read,
+ ::thread_get_state(m_thread->MachPortNumber(), __x86_64_EXCEPTION_STATE,
+ (thread_state_t)&m_state.context.exc, &count));
+ }
+ return m_state.GetError(e_regSetEXC, Read);
+}
+
+kern_return_t DNBArchImplX86_64::SetGPRState() {
+ kern_return_t kret = ::thread_abort_safely(m_thread->MachPortNumber());
+ DNBLogThreadedIf(
+ LOG_THREAD, "thread = 0x%4.4x calling thread_abort_safely (tid) => %u "
+ "(SetGPRState() for stop_count = %u)",
+ m_thread->MachPortNumber(), kret, m_thread->Process()->StopCount());
+
+ m_state.SetError(e_regSetGPR, Write,
+ ::thread_set_state(m_thread->MachPortNumber(),
+ __x86_64_THREAD_STATE,
+ (thread_state_t)&m_state.context.gpr,
+ e_regSetWordSizeGPR));
+ DNBLogThreadedIf(
+ LOG_THREAD,
+ "::thread_set_state (0x%4.4x, %u, &gpr, %u) => 0x%8.8x"
+ "\n\trax = %16.16llx rbx = %16.16llx rcx = %16.16llx rdx = %16.16llx"
+ "\n\trdi = %16.16llx rsi = %16.16llx rbp = %16.16llx rsp = %16.16llx"
+ "\n\t r8 = %16.16llx r9 = %16.16llx r10 = %16.16llx r11 = %16.16llx"
+ "\n\tr12 = %16.16llx r13 = %16.16llx r14 = %16.16llx r15 = %16.16llx"
+ "\n\trip = %16.16llx"
+ "\n\tflg = %16.16llx cs = %16.16llx fs = %16.16llx gs = %16.16llx",
+ m_thread->MachPortNumber(), __x86_64_THREAD_STATE, e_regSetWordSizeGPR,
+ m_state.GetError(e_regSetGPR, Write), m_state.context.gpr.__rax,
+ m_state.context.gpr.__rbx, m_state.context.gpr.__rcx,
+ m_state.context.gpr.__rdx, m_state.context.gpr.__rdi,
+ m_state.context.gpr.__rsi, m_state.context.gpr.__rbp,
+ m_state.context.gpr.__rsp, m_state.context.gpr.__r8,
+ m_state.context.gpr.__r9, m_state.context.gpr.__r10,
+ m_state.context.gpr.__r11, m_state.context.gpr.__r12,
+ m_state.context.gpr.__r13, m_state.context.gpr.__r14,
+ m_state.context.gpr.__r15, m_state.context.gpr.__rip,
+ m_state.context.gpr.__rflags, m_state.context.gpr.__cs,
+ m_state.context.gpr.__fs, m_state.context.gpr.__gs);
+ return m_state.GetError(e_regSetGPR, Write);
+}
+
+kern_return_t DNBArchImplX86_64::SetFPUState() {
+ if (DEBUG_FPU_REGS) {
+ m_state.SetError(e_regSetFPU, Write, 0);
+ return m_state.GetError(e_regSetFPU, Write);
+ } else {
+ int flavor = __x86_64_FLOAT_STATE;
+ mach_msg_type_number_t count = e_regSetWordSizeFPU;
+ if (CPUHasAVX512f() || FORCE_AVX_REGS) {
+ count = e_regSetWordSizeAVX512f;
+ flavor = __x86_64_AVX512F_STATE;
+ m_state.SetError(
+ e_regSetFPU, Write,
+ ::thread_set_state(m_thread->MachPortNumber(), flavor,
+ (thread_state_t)&m_state.context.fpu, count));
+ if (m_state.GetError(e_regSetFPU, Write) == KERN_SUCCESS)
+ return m_state.GetError(e_regSetFPU, Write);
+ else
+ DNBLogThreadedIf(LOG_THREAD,
+ "::thread_get_state attempted save of avx512 fpu regctx failed, will try saving avx regctx");
+ }
+
+ if (CPUHasAVX() || FORCE_AVX_REGS) {
+ flavor = __x86_64_AVX_STATE;
+ count = e_regSetWordSizeAVX;
+ }
+ m_state.SetError(
+ e_regSetFPU, Write,
+ ::thread_set_state(m_thread->MachPortNumber(), flavor,
+ (thread_state_t)&m_state.context.fpu, count));
+ return m_state.GetError(e_regSetFPU, Write);
+ }
+}
+
+kern_return_t DNBArchImplX86_64::SetEXCState() {
+ m_state.SetError(e_regSetEXC, Write,
+ ::thread_set_state(m_thread->MachPortNumber(),
+ __x86_64_EXCEPTION_STATE,
+ (thread_state_t)&m_state.context.exc,
+ e_regSetWordSizeEXC));
+ return m_state.GetError(e_regSetEXC, Write);
+}
+
+kern_return_t DNBArchImplX86_64::GetDBGState(bool force) {
+ if (force || m_state.GetError(e_regSetDBG, Read)) {
+ mach_msg_type_number_t count = e_regSetWordSizeDBG;
+ m_state.SetError(
+ e_regSetDBG, Read,
+ ::thread_get_state(m_thread->MachPortNumber(), __x86_64_DEBUG_STATE,
+ (thread_state_t)&m_state.context.dbg, &count));
+ }
+ return m_state.GetError(e_regSetDBG, Read);
+}
+
+kern_return_t DNBArchImplX86_64::SetDBGState(bool also_set_on_task) {
+ m_state.SetError(e_regSetDBG, Write,
+ ::thread_set_state(m_thread->MachPortNumber(),
+ __x86_64_DEBUG_STATE,
+ (thread_state_t)&m_state.context.dbg,
+ e_regSetWordSizeDBG));
+ if (also_set_on_task) {
+ kern_return_t kret = ::task_set_state(
+ m_thread->Process()->Task().TaskPort(), __x86_64_DEBUG_STATE,
+ (thread_state_t)&m_state.context.dbg, e_regSetWordSizeDBG);
+ if (kret != KERN_SUCCESS)
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplX86_64::SetDBGState failed "
+ "to set debug control register state: "
+ "0x%8.8x.",
+ kret);
+ }
+ return m_state.GetError(e_regSetDBG, Write);
+}
+
+void DNBArchImplX86_64::ThreadWillResume() {
+ // Do we need to step this thread? If so, let the mach thread tell us so.
+ if (m_thread->IsStepping()) {
+ // This is the primary thread, let the arch do anything it needs
+ EnableHardwareSingleStep(true);
+ }
+
+ // Reset the debug status register, if necessary, before we resume.
+ kern_return_t kret = GetDBGState(false);
+ DNBLogThreadedIf(
+ LOG_WATCHPOINTS,
+ "DNBArchImplX86_64::ThreadWillResume() GetDBGState() => 0x%8.8x.", kret);
+ if (kret != KERN_SUCCESS)
+ return;
+
+ DBG &debug_state = m_state.context.dbg;
+ bool need_reset = false;
+ uint32_t i, num = NumSupportedHardwareWatchpoints();
+ for (i = 0; i < num; ++i)
+ if (IsWatchpointHit(debug_state, i))
+ need_reset = true;
+
+ if (need_reset) {
+ ClearWatchpointHits(debug_state);
+ kret = SetDBGState(false);
+ DNBLogThreadedIf(
+ LOG_WATCHPOINTS,
+ "DNBArchImplX86_64::ThreadWillResume() SetDBGState() => 0x%8.8x.",
+ kret);
+ }
+}
+
+bool DNBArchImplX86_64::ThreadDidStop() {
+ bool success = true;
+
+ m_state.InvalidateAllRegisterStates();
+
+ // Are we stepping a single instruction?
+ if (GetGPRState(true) == KERN_SUCCESS) {
+ // We are single stepping, was this the primary thread?
+ if (m_thread->IsStepping()) {
+ // This was the primary thread, we need to clear the trace
+ // bit if so.
+ success = EnableHardwareSingleStep(false) == KERN_SUCCESS;
+ } else {
+ // The MachThread will automatically restore the suspend count
+ // in ThreadDidStop(), so we don't need to do anything here if
+ // we weren't the primary thread the last time
+ }
+ }
+ return success;
+}
+
+bool DNBArchImplX86_64::NotifyException(MachException::Data &exc) {
+ switch (exc.exc_type) {
+ case EXC_BAD_ACCESS:
+ break;
+ case EXC_BAD_INSTRUCTION:
+ break;
+ case EXC_ARITHMETIC:
+ break;
+ case EXC_EMULATION:
+ break;
+ case EXC_SOFTWARE:
+ break;
+ case EXC_BREAKPOINT:
+ if (exc.exc_data.size() >= 2 && exc.exc_data[0] == 2) {
+ // exc_code = EXC_I386_BPT
+ //
+ nub_addr_t pc = GetPC(INVALID_NUB_ADDRESS);
+ if (pc != INVALID_NUB_ADDRESS && pc > 0) {
+ pc -= 1;
+ // Check for a breakpoint at one byte prior to the current PC value
+ // since the PC will be just past the trap.
+
+ DNBBreakpoint *bp =
+ m_thread->Process()->Breakpoints().FindByAddress(pc);
+ if (bp) {
+ // Backup the PC for i386 since the trap was taken and the PC
+ // is at the address following the single byte trap instruction.
+ if (m_state.context.gpr.__rip > 0) {
+ m_state.context.gpr.__rip = pc;
+ // Write the new PC back out
+ SetGPRState();
+ }
+ }
+ return true;
+ }
+ } else if (exc.exc_data.size() >= 2 && exc.exc_data[0] == 1) {
+ // exc_code = EXC_I386_SGL
+ //
+ // Check whether this corresponds to a watchpoint hit event.
+ // If yes, set the exc_sub_code to the data break address.
+ nub_addr_t addr = 0;
+ uint32_t hw_index = GetHardwareWatchpointHit(addr);
+ if (hw_index != INVALID_NUB_HW_INDEX) {
+ exc.exc_data[1] = addr;
+ // Piggyback the hw_index in the exc.data.
+ exc.exc_data.push_back(hw_index);
+ }
+
+ return true;
+ }
+ break;
+ case EXC_SYSCALL:
+ break;
+ case EXC_MACH_SYSCALL:
+ break;
+ case EXC_RPC_ALERT:
+ break;
+ }
+ return false;
+}
+
+uint32_t DNBArchImplX86_64::NumSupportedHardwareWatchpoints() {
+ // Available debug address registers: dr0, dr1, dr2, dr3.
+ return 4;
+}
+
+static uint32_t size_and_rw_bits(nub_size_t size, bool read, bool write) {
+ uint32_t rw;
+ if (read) {
+ rw = 0x3; // READ or READ/WRITE
+ } else if (write) {
+ rw = 0x1; // WRITE
+ } else {
+ assert(0 && "read and write cannot both be false");
+ }
+
+ switch (size) {
+ case 1:
+ return rw;
+ case 2:
+ return (0x1 << 2) | rw;
+ case 4:
+ return (0x3 << 2) | rw;
+ case 8:
+ return (0x2 << 2) | rw;
+ }
+ assert(0 && "invalid size, must be one of 1, 2, 4, or 8");
+ return 0;
+}
+void DNBArchImplX86_64::SetWatchpoint(DBG &debug_state, uint32_t hw_index,
+ nub_addr_t addr, nub_size_t size,
+ bool read, bool write) {
+ // Set both dr7 (debug control register) and dri (debug address register).
+
+ // dr7{7-0} encodes the local/gloabl enable bits:
+ // global enable --. .-- local enable
+ // | |
+ // v v
+ // dr0 -> bits{1-0}
+ // dr1 -> bits{3-2}
+ // dr2 -> bits{5-4}
+ // dr3 -> bits{7-6}
+ //
+ // dr7{31-16} encodes the rw/len bits:
+ // b_x+3, b_x+2, b_x+1, b_x
+ // where bits{x+1, x} => rw
+ // 0b00: execute, 0b01: write, 0b11: read-or-write, 0b10: io
+ // read-or-write (unused)
+ // and bits{x+3, x+2} => len
+ // 0b00: 1-byte, 0b01: 2-byte, 0b11: 4-byte, 0b10: 8-byte
+ //
+ // dr0 -> bits{19-16}
+ // dr1 -> bits{23-20}
+ // dr2 -> bits{27-24}
+ // dr3 -> bits{31-28}
+ debug_state.__dr7 |=
+ (1 << (2 * hw_index) |
+ size_and_rw_bits(size, read, write) << (16 + 4 * hw_index));
+ switch (hw_index) {
+ case 0:
+ debug_state.__dr0 = addr;
+ break;
+ case 1:
+ debug_state.__dr1 = addr;
+ break;
+ case 2:
+ debug_state.__dr2 = addr;
+ break;
+ case 3:
+ debug_state.__dr3 = addr;
+ break;
+ default:
+ assert(0 &&
+ "invalid hardware register index, must be one of 0, 1, 2, or 3");
+ }
+ return;
+}
+
+void DNBArchImplX86_64::ClearWatchpoint(DBG &debug_state, uint32_t hw_index) {
+ debug_state.__dr7 &= ~(3 << (2 * hw_index));
+ switch (hw_index) {
+ case 0:
+ debug_state.__dr0 = 0;
+ break;
+ case 1:
+ debug_state.__dr1 = 0;
+ break;
+ case 2:
+ debug_state.__dr2 = 0;
+ break;
+ case 3:
+ debug_state.__dr3 = 0;
+ break;
+ default:
+ assert(0 &&
+ "invalid hardware register index, must be one of 0, 1, 2, or 3");
+ }
+ return;
+}
+
+bool DNBArchImplX86_64::IsWatchpointVacant(const DBG &debug_state,
+ uint32_t hw_index) {
+ // Check dr7 (debug control register) for local/global enable bits:
+ // global enable --. .-- local enable
+ // | |
+ // v v
+ // dr0 -> bits{1-0}
+ // dr1 -> bits{3-2}
+ // dr2 -> bits{5-4}
+ // dr3 -> bits{7-6}
+ return (debug_state.__dr7 & (3 << (2 * hw_index))) == 0;
+}
+
+// Resets local copy of debug status register to wait for the next debug
+// exception.
+void DNBArchImplX86_64::ClearWatchpointHits(DBG &debug_state) {
+ // See also IsWatchpointHit().
+ debug_state.__dr6 = 0;
+ return;
+}
+
+bool DNBArchImplX86_64::IsWatchpointHit(const DBG &debug_state,
+ uint32_t hw_index) {
+ // Check dr6 (debug status register) whether a watchpoint hits:
+ // is watchpoint hit?
+ // |
+ // v
+ // dr0 -> bits{0}
+ // dr1 -> bits{1}
+ // dr2 -> bits{2}
+ // dr3 -> bits{3}
+ return (debug_state.__dr6 & (1 << hw_index));
+}
+
+nub_addr_t DNBArchImplX86_64::GetWatchAddress(const DBG &debug_state,
+ uint32_t hw_index) {
+ switch (hw_index) {
+ case 0:
+ return debug_state.__dr0;
+ case 1:
+ return debug_state.__dr1;
+ case 2:
+ return debug_state.__dr2;
+ case 3:
+ return debug_state.__dr3;
+ }
+ assert(0 && "invalid hardware register index, must be one of 0, 1, 2, or 3");
+ return 0;
+}
+
+bool DNBArchImplX86_64::StartTransForHWP() {
+ if (m_2pc_trans_state != Trans_Done && m_2pc_trans_state != Trans_Rolled_Back)
+ DNBLogError("%s inconsistent state detected, expected %d or %d, got: %d",
+ __FUNCTION__, Trans_Done, Trans_Rolled_Back, m_2pc_trans_state);
+ m_2pc_dbg_checkpoint = m_state.context.dbg;
+ m_2pc_trans_state = Trans_Pending;
+ return true;
+}
+bool DNBArchImplX86_64::RollbackTransForHWP() {
+ m_state.context.dbg = m_2pc_dbg_checkpoint;
+ if (m_2pc_trans_state != Trans_Pending)
+ DNBLogError("%s inconsistent state detected, expected %d, got: %d",
+ __FUNCTION__, Trans_Pending, m_2pc_trans_state);
+ m_2pc_trans_state = Trans_Rolled_Back;
+ kern_return_t kret = SetDBGState(false);
+ DNBLogThreadedIf(
+ LOG_WATCHPOINTS,
+ "DNBArchImplX86_64::RollbackTransForHWP() SetDBGState() => 0x%8.8x.",
+ kret);
+
+ return kret == KERN_SUCCESS;
+}
+bool DNBArchImplX86_64::FinishTransForHWP() {
+ m_2pc_trans_state = Trans_Done;
+ return true;
+}
+DNBArchImplX86_64::DBG DNBArchImplX86_64::GetDBGCheckpoint() {
+ return m_2pc_dbg_checkpoint;
+}
+
+uint32_t DNBArchImplX86_64::EnableHardwareWatchpoint(nub_addr_t addr,
+ nub_size_t size, bool read,
+ bool write,
+ bool also_set_on_task) {
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplX86_64::"
+ "EnableHardwareWatchpoint(addr = 0x%llx, "
+ "size = %llu, read = %u, write = %u)",
+ (uint64_t)addr, (uint64_t)size, read, write);
+
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+
+ // Can only watch 1, 2, 4, or 8 bytes.
+ if (!(size == 1 || size == 2 || size == 4 || size == 8))
+ return INVALID_NUB_HW_INDEX;
+
+ // We must watch for either read or write
+ if (!read && !write)
+ return INVALID_NUB_HW_INDEX;
+
+ // Read the debug state
+ kern_return_t kret = GetDBGState(false);
+
+ if (kret == KERN_SUCCESS) {
+ // Check to make sure we have the needed hardware support
+ uint32_t i = 0;
+
+ DBG &debug_state = m_state.context.dbg;
+ for (i = 0; i < num_hw_watchpoints; ++i) {
+ if (IsWatchpointVacant(debug_state, i))
+ break;
+ }
+
+ // See if we found an available hw breakpoint slot above
+ if (i < num_hw_watchpoints) {
+ StartTransForHWP();
+
+ // Modify our local copy of the debug state, first.
+ SetWatchpoint(debug_state, i, addr, size, read, write);
+ // Now set the watch point in the inferior.
+ kret = SetDBGState(also_set_on_task);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplX86_64::"
+ "EnableHardwareWatchpoint() "
+ "SetDBGState() => 0x%8.8x.",
+ kret);
+
+ if (kret == KERN_SUCCESS)
+ return i;
+ else // Revert to the previous debug state voluntarily. The transaction
+ // coordinator knows that we have failed.
+ m_state.context.dbg = GetDBGCheckpoint();
+ } else {
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplX86_64::"
+ "EnableHardwareWatchpoint(): All "
+ "hardware resources (%u) are in use.",
+ num_hw_watchpoints);
+ }
+ }
+ return INVALID_NUB_HW_INDEX;
+}
+
+bool DNBArchImplX86_64::DisableHardwareWatchpoint(uint32_t hw_index,
+ bool also_set_on_task) {
+ kern_return_t kret = GetDBGState(false);
+
+ const uint32_t num_hw_points = NumSupportedHardwareWatchpoints();
+ if (kret == KERN_SUCCESS) {
+ DBG &debug_state = m_state.context.dbg;
+ if (hw_index < num_hw_points &&
+ !IsWatchpointVacant(debug_state, hw_index)) {
+ StartTransForHWP();
+
+ // Modify our local copy of the debug state, first.
+ ClearWatchpoint(debug_state, hw_index);
+ // Now disable the watch point in the inferior.
+ kret = SetDBGState(also_set_on_task);
+ DNBLogThreadedIf(LOG_WATCHPOINTS,
+ "DNBArchImplX86_64::DisableHardwareWatchpoint( %u )",
+ hw_index);
+
+ if (kret == KERN_SUCCESS)
+ return true;
+ else // Revert to the previous debug state voluntarily. The transaction
+ // coordinator knows that we have failed.
+ m_state.context.dbg = GetDBGCheckpoint();
+ }
+ }
+ return false;
+}
+
+// Iterate through the debug status register; return the index of the first hit.
+uint32_t DNBArchImplX86_64::GetHardwareWatchpointHit(nub_addr_t &addr) {
+ // Read the debug state
+ kern_return_t kret = GetDBGState(true);
+ DNBLogThreadedIf(
+ LOG_WATCHPOINTS,
+ "DNBArchImplX86_64::GetHardwareWatchpointHit() GetDBGState() => 0x%8.8x.",
+ kret);
+ if (kret == KERN_SUCCESS) {
+ DBG &debug_state = m_state.context.dbg;
+ uint32_t i, num = NumSupportedHardwareWatchpoints();
+ for (i = 0; i < num; ++i) {
+ if (IsWatchpointHit(debug_state, i)) {
+ addr = GetWatchAddress(debug_state, i);
+ DNBLogThreadedIf(LOG_WATCHPOINTS, "DNBArchImplX86_64::"
+ "GetHardwareWatchpointHit() found => "
+ "%u (addr = 0x%llx).",
+ i, (uint64_t)addr);
+ return i;
+ }
+ }
+ }
+ return INVALID_NUB_HW_INDEX;
+}
+
+// Set the single step bit in the processor status register.
+kern_return_t DNBArchImplX86_64::EnableHardwareSingleStep(bool enable) {
+ if (GetGPRState(false) == KERN_SUCCESS) {
+ const uint32_t trace_bit = 0x100u;
+ if (enable)
+ m_state.context.gpr.__rflags |= trace_bit;
+ else
+ m_state.context.gpr.__rflags &= ~trace_bit;
+ return SetGPRState();
+ }
+ return m_state.GetError(e_regSetGPR, Read);
+}
+
+// Register information definitions
+
+enum {
+ gpr_rax = 0,
+ gpr_rbx,
+ gpr_rcx,
+ gpr_rdx,
+ gpr_rdi,
+ gpr_rsi,
+ gpr_rbp,
+ gpr_rsp,
+ gpr_r8,
+ gpr_r9,
+ gpr_r10,
+ gpr_r11,
+ gpr_r12,
+ gpr_r13,
+ gpr_r14,
+ gpr_r15,
+ gpr_rip,
+ gpr_rflags,
+ gpr_cs,
+ gpr_fs,
+ gpr_gs,
+ gpr_eax,
+ gpr_ebx,
+ gpr_ecx,
+ gpr_edx,
+ gpr_edi,
+ gpr_esi,
+ gpr_ebp,
+ gpr_esp,
+ gpr_r8d, // Low 32 bits or r8
+ gpr_r9d, // Low 32 bits or r9
+ gpr_r10d, // Low 32 bits or r10
+ gpr_r11d, // Low 32 bits or r11
+ gpr_r12d, // Low 32 bits or r12
+ gpr_r13d, // Low 32 bits or r13
+ gpr_r14d, // Low 32 bits or r14
+ gpr_r15d, // Low 32 bits or r15
+ gpr_ax,
+ gpr_bx,
+ gpr_cx,
+ gpr_dx,
+ gpr_di,
+ gpr_si,
+ gpr_bp,
+ gpr_sp,
+ gpr_r8w, // Low 16 bits or r8
+ gpr_r9w, // Low 16 bits or r9
+ gpr_r10w, // Low 16 bits or r10
+ gpr_r11w, // Low 16 bits or r11
+ gpr_r12w, // Low 16 bits or r12
+ gpr_r13w, // Low 16 bits or r13
+ gpr_r14w, // Low 16 bits or r14
+ gpr_r15w, // Low 16 bits or r15
+ gpr_ah,
+ gpr_bh,
+ gpr_ch,
+ gpr_dh,
+ gpr_al,
+ gpr_bl,
+ gpr_cl,
+ gpr_dl,
+ gpr_dil,
+ gpr_sil,
+ gpr_bpl,
+ gpr_spl,
+ gpr_r8l, // Low 8 bits or r8
+ gpr_r9l, // Low 8 bits or r9
+ gpr_r10l, // Low 8 bits or r10
+ gpr_r11l, // Low 8 bits or r11
+ gpr_r12l, // Low 8 bits or r12
+ gpr_r13l, // Low 8 bits or r13
+ gpr_r14l, // Low 8 bits or r14
+ gpr_r15l, // Low 8 bits or r15
+ k_num_gpr_regs
+};
+
+enum {
+ fpu_fcw,
+ fpu_fsw,
+ fpu_ftw,
+ fpu_fop,
+ fpu_ip,
+ fpu_cs,
+ fpu_dp,
+ fpu_ds,
+ fpu_mxcsr,
+ fpu_mxcsrmask,
+ fpu_stmm0,
+ fpu_stmm1,
+ fpu_stmm2,
+ fpu_stmm3,
+ fpu_stmm4,
+ fpu_stmm5,
+ fpu_stmm6,
+ fpu_stmm7,
+ fpu_xmm0,
+ fpu_xmm1,
+ fpu_xmm2,
+ fpu_xmm3,
+ fpu_xmm4,
+ fpu_xmm5,
+ fpu_xmm6,
+ fpu_xmm7,
+ fpu_xmm8,
+ fpu_xmm9,
+ fpu_xmm10,
+ fpu_xmm11,
+ fpu_xmm12,
+ fpu_xmm13,
+ fpu_xmm14,
+ fpu_xmm15,
+ fpu_ymm0,
+ fpu_ymm1,
+ fpu_ymm2,
+ fpu_ymm3,
+ fpu_ymm4,
+ fpu_ymm5,
+ fpu_ymm6,
+ fpu_ymm7,
+ fpu_ymm8,
+ fpu_ymm9,
+ fpu_ymm10,
+ fpu_ymm11,
+ fpu_ymm12,
+ fpu_ymm13,
+ fpu_ymm14,
+ fpu_ymm15,
+ fpu_k0,
+ fpu_k1,
+ fpu_k2,
+ fpu_k3,
+ fpu_k4,
+ fpu_k5,
+ fpu_k6,
+ fpu_k7,
+ fpu_zmm0,
+ fpu_zmm1,
+ fpu_zmm2,
+ fpu_zmm3,
+ fpu_zmm4,
+ fpu_zmm5,
+ fpu_zmm6,
+ fpu_zmm7,
+ fpu_zmm8,
+ fpu_zmm9,
+ fpu_zmm10,
+ fpu_zmm11,
+ fpu_zmm12,
+ fpu_zmm13,
+ fpu_zmm14,
+ fpu_zmm15,
+ fpu_zmm16,
+ fpu_zmm17,
+ fpu_zmm18,
+ fpu_zmm19,
+ fpu_zmm20,
+ fpu_zmm21,
+ fpu_zmm22,
+ fpu_zmm23,
+ fpu_zmm24,
+ fpu_zmm25,
+ fpu_zmm26,
+ fpu_zmm27,
+ fpu_zmm28,
+ fpu_zmm29,
+ fpu_zmm30,
+ fpu_zmm31,
+ k_num_fpu_regs,
+
+ // Aliases
+ fpu_fctrl = fpu_fcw,
+ fpu_fstat = fpu_fsw,
+ fpu_ftag = fpu_ftw,
+ fpu_fiseg = fpu_cs,
+ fpu_fioff = fpu_ip,
+ fpu_foseg = fpu_ds,
+ fpu_fooff = fpu_dp
+};
+
+enum {
+ exc_trapno,
+ exc_err,
+ exc_faultvaddr,
+ k_num_exc_regs,
+};
+
+enum ehframe_dwarf_regnums {
+ ehframe_dwarf_rax = 0,
+ ehframe_dwarf_rdx = 1,
+ ehframe_dwarf_rcx = 2,
+ ehframe_dwarf_rbx = 3,
+ ehframe_dwarf_rsi = 4,
+ ehframe_dwarf_rdi = 5,
+ ehframe_dwarf_rbp = 6,
+ ehframe_dwarf_rsp = 7,
+ ehframe_dwarf_r8,
+ ehframe_dwarf_r9,
+ ehframe_dwarf_r10,
+ ehframe_dwarf_r11,
+ ehframe_dwarf_r12,
+ ehframe_dwarf_r13,
+ ehframe_dwarf_r14,
+ ehframe_dwarf_r15,
+ ehframe_dwarf_rip,
+ ehframe_dwarf_xmm0,
+ ehframe_dwarf_xmm1,
+ ehframe_dwarf_xmm2,
+ ehframe_dwarf_xmm3,
+ ehframe_dwarf_xmm4,
+ ehframe_dwarf_xmm5,
+ ehframe_dwarf_xmm6,
+ ehframe_dwarf_xmm7,
+ ehframe_dwarf_xmm8,
+ ehframe_dwarf_xmm9,
+ ehframe_dwarf_xmm10,
+ ehframe_dwarf_xmm11,
+ ehframe_dwarf_xmm12,
+ ehframe_dwarf_xmm13,
+ ehframe_dwarf_xmm14,
+ ehframe_dwarf_xmm15,
+ ehframe_dwarf_stmm0,
+ ehframe_dwarf_stmm1,
+ ehframe_dwarf_stmm2,
+ ehframe_dwarf_stmm3,
+ ehframe_dwarf_stmm4,
+ ehframe_dwarf_stmm5,
+ ehframe_dwarf_stmm6,
+ ehframe_dwarf_stmm7,
+ ehframe_dwarf_ymm0 = ehframe_dwarf_xmm0,
+ ehframe_dwarf_ymm1 = ehframe_dwarf_xmm1,
+ ehframe_dwarf_ymm2 = ehframe_dwarf_xmm2,
+ ehframe_dwarf_ymm3 = ehframe_dwarf_xmm3,
+ ehframe_dwarf_ymm4 = ehframe_dwarf_xmm4,
+ ehframe_dwarf_ymm5 = ehframe_dwarf_xmm5,
+ ehframe_dwarf_ymm6 = ehframe_dwarf_xmm6,
+ ehframe_dwarf_ymm7 = ehframe_dwarf_xmm7,
+ ehframe_dwarf_ymm8 = ehframe_dwarf_xmm8,
+ ehframe_dwarf_ymm9 = ehframe_dwarf_xmm9,
+ ehframe_dwarf_ymm10 = ehframe_dwarf_xmm10,
+ ehframe_dwarf_ymm11 = ehframe_dwarf_xmm11,
+ ehframe_dwarf_ymm12 = ehframe_dwarf_xmm12,
+ ehframe_dwarf_ymm13 = ehframe_dwarf_xmm13,
+ ehframe_dwarf_ymm14 = ehframe_dwarf_xmm14,
+ ehframe_dwarf_ymm15 = ehframe_dwarf_xmm15,
+ ehframe_dwarf_zmm0 = ehframe_dwarf_xmm0,
+ ehframe_dwarf_zmm1 = ehframe_dwarf_xmm1,
+ ehframe_dwarf_zmm2 = ehframe_dwarf_xmm2,
+ ehframe_dwarf_zmm3 = ehframe_dwarf_xmm3,
+ ehframe_dwarf_zmm4 = ehframe_dwarf_xmm4,
+ ehframe_dwarf_zmm5 = ehframe_dwarf_xmm5,
+ ehframe_dwarf_zmm6 = ehframe_dwarf_xmm6,
+ ehframe_dwarf_zmm7 = ehframe_dwarf_xmm7,
+ ehframe_dwarf_zmm8 = ehframe_dwarf_xmm8,
+ ehframe_dwarf_zmm9 = ehframe_dwarf_xmm9,
+ ehframe_dwarf_zmm10 = ehframe_dwarf_xmm10,
+ ehframe_dwarf_zmm11 = ehframe_dwarf_xmm11,
+ ehframe_dwarf_zmm12 = ehframe_dwarf_xmm12,
+ ehframe_dwarf_zmm13 = ehframe_dwarf_xmm13,
+ ehframe_dwarf_zmm14 = ehframe_dwarf_xmm14,
+ ehframe_dwarf_zmm15 = ehframe_dwarf_xmm15,
+ ehframe_dwarf_zmm16 = 67,
+ ehframe_dwarf_zmm17,
+ ehframe_dwarf_zmm18,
+ ehframe_dwarf_zmm19,
+ ehframe_dwarf_zmm20,
+ ehframe_dwarf_zmm21,
+ ehframe_dwarf_zmm22,
+ ehframe_dwarf_zmm23,
+ ehframe_dwarf_zmm24,
+ ehframe_dwarf_zmm25,
+ ehframe_dwarf_zmm26,
+ ehframe_dwarf_zmm27,
+ ehframe_dwarf_zmm28,
+ ehframe_dwarf_zmm29,
+ ehframe_dwarf_zmm30,
+ ehframe_dwarf_zmm31,
+ ehframe_dwarf_k0 = 118,
+ ehframe_dwarf_k1,
+ ehframe_dwarf_k2,
+ ehframe_dwarf_k3,
+ ehframe_dwarf_k4,
+ ehframe_dwarf_k5,
+ ehframe_dwarf_k6,
+ ehframe_dwarf_k7,
+};
+
+enum debugserver_regnums {
+ debugserver_rax = 0,
+ debugserver_rbx = 1,
+ debugserver_rcx = 2,
+ debugserver_rdx = 3,
+ debugserver_rsi = 4,
+ debugserver_rdi = 5,
+ debugserver_rbp = 6,
+ debugserver_rsp = 7,
+ debugserver_r8 = 8,
+ debugserver_r9 = 9,
+ debugserver_r10 = 10,
+ debugserver_r11 = 11,
+ debugserver_r12 = 12,
+ debugserver_r13 = 13,
+ debugserver_r14 = 14,
+ debugserver_r15 = 15,
+ debugserver_rip = 16,
+ debugserver_rflags = 17,
+ debugserver_cs = 18,
+ debugserver_ss = 19,
+ debugserver_ds = 20,
+ debugserver_es = 21,
+ debugserver_fs = 22,
+ debugserver_gs = 23,
+ debugserver_stmm0 = 24,
+ debugserver_stmm1 = 25,
+ debugserver_stmm2 = 26,
+ debugserver_stmm3 = 27,
+ debugserver_stmm4 = 28,
+ debugserver_stmm5 = 29,
+ debugserver_stmm6 = 30,
+ debugserver_stmm7 = 31,
+ debugserver_fctrl = 32,
+ debugserver_fcw = debugserver_fctrl,
+ debugserver_fstat = 33,
+ debugserver_fsw = debugserver_fstat,
+ debugserver_ftag = 34,
+ debugserver_ftw = debugserver_ftag,
+ debugserver_fiseg = 35,
+ debugserver_fpu_cs = debugserver_fiseg,
+ debugserver_fioff = 36,
+ debugserver_ip = debugserver_fioff,
+ debugserver_foseg = 37,
+ debugserver_fpu_ds = debugserver_foseg,
+ debugserver_fooff = 38,
+ debugserver_dp = debugserver_fooff,
+ debugserver_fop = 39,
+ debugserver_xmm0 = 40,
+ debugserver_xmm1 = 41,
+ debugserver_xmm2 = 42,
+ debugserver_xmm3 = 43,
+ debugserver_xmm4 = 44,
+ debugserver_xmm5 = 45,
+ debugserver_xmm6 = 46,
+ debugserver_xmm7 = 47,
+ debugserver_xmm8 = 48,
+ debugserver_xmm9 = 49,
+ debugserver_xmm10 = 50,
+ debugserver_xmm11 = 51,
+ debugserver_xmm12 = 52,
+ debugserver_xmm13 = 53,
+ debugserver_xmm14 = 54,
+ debugserver_xmm15 = 55,
+ debugserver_mxcsr = 56,
+ debugserver_ymm0 = debugserver_xmm0,
+ debugserver_ymm1 = debugserver_xmm1,
+ debugserver_ymm2 = debugserver_xmm2,
+ debugserver_ymm3 = debugserver_xmm3,
+ debugserver_ymm4 = debugserver_xmm4,
+ debugserver_ymm5 = debugserver_xmm5,
+ debugserver_ymm6 = debugserver_xmm6,
+ debugserver_ymm7 = debugserver_xmm7,
+ debugserver_ymm8 = debugserver_xmm8,
+ debugserver_ymm9 = debugserver_xmm9,
+ debugserver_ymm10 = debugserver_xmm10,
+ debugserver_ymm11 = debugserver_xmm11,
+ debugserver_ymm12 = debugserver_xmm12,
+ debugserver_ymm13 = debugserver_xmm13,
+ debugserver_ymm14 = debugserver_xmm14,
+ debugserver_ymm15 = debugserver_xmm15,
+ debugserver_zmm0 = debugserver_xmm0,
+ debugserver_zmm1 = debugserver_xmm1,
+ debugserver_zmm2 = debugserver_xmm2,
+ debugserver_zmm3 = debugserver_xmm3,
+ debugserver_zmm4 = debugserver_xmm4,
+ debugserver_zmm5 = debugserver_xmm5,
+ debugserver_zmm6 = debugserver_xmm6,
+ debugserver_zmm7 = debugserver_xmm7,
+ debugserver_zmm8 = debugserver_xmm8,
+ debugserver_zmm9 = debugserver_xmm9,
+ debugserver_zmm10 = debugserver_xmm10,
+ debugserver_zmm11 = debugserver_xmm11,
+ debugserver_zmm12 = debugserver_xmm12,
+ debugserver_zmm13 = debugserver_xmm13,
+ debugserver_zmm14 = debugserver_xmm14,
+ debugserver_zmm15 = debugserver_xmm15,
+ debugserver_zmm16 = 67,
+ debugserver_zmm17 = 68,
+ debugserver_zmm18 = 69,
+ debugserver_zmm19 = 70,
+ debugserver_zmm20 = 71,
+ debugserver_zmm21 = 72,
+ debugserver_zmm22 = 73,
+ debugserver_zmm23 = 74,
+ debugserver_zmm24 = 75,
+ debugserver_zmm25 = 76,
+ debugserver_zmm26 = 77,
+ debugserver_zmm27 = 78,
+ debugserver_zmm28 = 79,
+ debugserver_zmm29 = 80,
+ debugserver_zmm30 = 81,
+ debugserver_zmm31 = 82,
+ debugserver_k0 = 118,
+ debugserver_k1 = 119,
+ debugserver_k2 = 120,
+ debugserver_k3 = 121,
+ debugserver_k4 = 122,
+ debugserver_k5 = 123,
+ debugserver_k6 = 124,
+ debugserver_k7 = 125,
+};
+
+#define GPR_OFFSET(reg) (offsetof(DNBArchImplX86_64::GPR, __##reg))
+#define FPU_OFFSET(reg) \
+ (offsetof(DNBArchImplX86_64::FPU, __fpu_##reg) + \
+ offsetof(DNBArchImplX86_64::Context, fpu.no_avx))
+#define AVX_OFFSET(reg) \
+ (offsetof(DNBArchImplX86_64::AVX, __fpu_##reg) + \
+ offsetof(DNBArchImplX86_64::Context, fpu.avx))
+#define AVX512F_OFFSET(reg) \
+ (offsetof(DNBArchImplX86_64::AVX512F, __fpu_##reg) + \
+ offsetof(DNBArchImplX86_64::Context, fpu.avx512f))
+#define EXC_OFFSET(reg) \
+ (offsetof(DNBArchImplX86_64::EXC, __##reg) + \
+ offsetof(DNBArchImplX86_64::Context, exc))
+#define AVX_OFFSET_YMM(n) (AVX_OFFSET(ymmh0) + (32 * n))
+#define AVX512F_OFFSET_ZMM(n) (AVX512F_OFFSET(zmmh0) + (64 * n))
+
+#define GPR_SIZE(reg) (sizeof(((DNBArchImplX86_64::GPR *)NULL)->__##reg))
+#define FPU_SIZE_UINT(reg) \
+ (sizeof(((DNBArchImplX86_64::FPU *)NULL)->__fpu_##reg))
+#define FPU_SIZE_MMST(reg) \
+ (sizeof(((DNBArchImplX86_64::FPU *)NULL)->__fpu_##reg.__mmst_reg))
+#define FPU_SIZE_XMM(reg) \
+ (sizeof(((DNBArchImplX86_64::FPU *)NULL)->__fpu_##reg.__xmm_reg))
+#define FPU_SIZE_YMM(reg) (32)
+#define FPU_SIZE_ZMM(reg) (64)
+#define EXC_SIZE(reg) (sizeof(((DNBArchImplX86_64::EXC *)NULL)->__##reg))
+
+// These macros will auto define the register name, alt name, register size,
+// register offset, encoding, format and native register. This ensures that
+// the register state structures are defined correctly and have the correct
+// sizes and offsets.
+#define DEFINE_GPR(reg) \
+ { \
+ e_regSetGPR, gpr_##reg, #reg, NULL, Uint, Hex, GPR_SIZE(reg), \
+ GPR_OFFSET(reg), ehframe_dwarf_##reg, ehframe_dwarf_##reg, \
+ INVALID_NUB_REGNUM, debugserver_##reg, NULL, g_invalidate_##reg \
+ }
+#define DEFINE_GPR_ALT(reg, alt, gen) \
+ { \
+ e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, GPR_SIZE(reg), \
+ GPR_OFFSET(reg), ehframe_dwarf_##reg, ehframe_dwarf_##reg, gen, \
+ debugserver_##reg, NULL, g_invalidate_##reg \
+ }
+#define DEFINE_GPR_ALT2(reg, alt) \
+ { \
+ e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, GPR_SIZE(reg), \
+ GPR_OFFSET(reg), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, \
+ INVALID_NUB_REGNUM, debugserver_##reg, NULL, NULL \
+ }
+#define DEFINE_GPR_ALT3(reg, alt, gen) \
+ { \
+ e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, GPR_SIZE(reg), \
+ GPR_OFFSET(reg), INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, gen, \
+ debugserver_##reg, NULL, NULL \
+ }
+#define DEFINE_GPR_ALT4(reg, alt, gen) \
+ { \
+ e_regSetGPR, gpr_##reg, #reg, alt, Uint, Hex, GPR_SIZE(reg), \
+ GPR_OFFSET(reg), ehframe_dwarf_##reg, ehframe_dwarf_##reg, gen, \
+ debugserver_##reg, NULL, NULL \
+ }
+
+#define DEFINE_GPR_PSEUDO_32(reg32, reg64) \
+ { \
+ e_regSetGPR, gpr_##reg32, #reg32, NULL, Uint, Hex, 4, 0, \
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, \
+ INVALID_NUB_REGNUM, g_contained_##reg64, g_invalidate_##reg64 \
+ }
+#define DEFINE_GPR_PSEUDO_16(reg16, reg64) \
+ { \
+ e_regSetGPR, gpr_##reg16, #reg16, NULL, Uint, Hex, 2, 0, \
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, \
+ INVALID_NUB_REGNUM, g_contained_##reg64, g_invalidate_##reg64 \
+ }
+#define DEFINE_GPR_PSEUDO_8H(reg8, reg64) \
+ { \
+ e_regSetGPR, gpr_##reg8, #reg8, NULL, Uint, Hex, 1, 1, INVALID_NUB_REGNUM, \
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, \
+ g_contained_##reg64, g_invalidate_##reg64 \
+ }
+#define DEFINE_GPR_PSEUDO_8L(reg8, reg64) \
+ { \
+ e_regSetGPR, gpr_##reg8, #reg8, NULL, Uint, Hex, 1, 0, INVALID_NUB_REGNUM, \
+ INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, INVALID_NUB_REGNUM, \
+ g_contained_##reg64, g_invalidate_##reg64 \
+ }
+
+// General purpose registers for 64 bit
+
+const char *g_contained_rax[] = {"rax", NULL};
+const char *g_contained_rbx[] = {"rbx", NULL};
+const char *g_contained_rcx[] = {"rcx", NULL};
+const char *g_contained_rdx[] = {"rdx", NULL};
+const char *g_contained_rdi[] = {"rdi", NULL};
+const char *g_contained_rsi[] = {"rsi", NULL};
+const char *g_contained_rbp[] = {"rbp", NULL};
+const char *g_contained_rsp[] = {"rsp", NULL};
+const char *g_contained_r8[] = {"r8", NULL};
+const char *g_contained_r9[] = {"r9", NULL};
+const char *g_contained_r10[] = {"r10", NULL};
+const char *g_contained_r11[] = {"r11", NULL};
+const char *g_contained_r12[] = {"r12", NULL};
+const char *g_contained_r13[] = {"r13", NULL};
+const char *g_contained_r14[] = {"r14", NULL};
+const char *g_contained_r15[] = {"r15", NULL};
+
+const char *g_invalidate_rax[] = {"rax", "eax", "ax", "ah", "al", NULL};
+const char *g_invalidate_rbx[] = {"rbx", "ebx", "bx", "bh", "bl", NULL};
+const char *g_invalidate_rcx[] = {"rcx", "ecx", "cx", "ch", "cl", NULL};
+const char *g_invalidate_rdx[] = {"rdx", "edx", "dx", "dh", "dl", NULL};
+const char *g_invalidate_rdi[] = {"rdi", "edi", "di", "dil", NULL};
+const char *g_invalidate_rsi[] = {"rsi", "esi", "si", "sil", NULL};
+const char *g_invalidate_rbp[] = {"rbp", "ebp", "bp", "bpl", NULL};
+const char *g_invalidate_rsp[] = {"rsp", "esp", "sp", "spl", NULL};
+const char *g_invalidate_r8[] = {"r8", "r8d", "r8w", "r8l", NULL};
+const char *g_invalidate_r9[] = {"r9", "r9d", "r9w", "r9l", NULL};
+const char *g_invalidate_r10[] = {"r10", "r10d", "r10w", "r10l", NULL};
+const char *g_invalidate_r11[] = {"r11", "r11d", "r11w", "r11l", NULL};
+const char *g_invalidate_r12[] = {"r12", "r12d", "r12w", "r12l", NULL};
+const char *g_invalidate_r13[] = {"r13", "r13d", "r13w", "r13l", NULL};
+const char *g_invalidate_r14[] = {"r14", "r14d", "r14w", "r14l", NULL};
+const char *g_invalidate_r15[] = {"r15", "r15d", "r15w", "r15l", NULL};
+
+const DNBRegisterInfo DNBArchImplX86_64::g_gpr_registers[] = {
+ DEFINE_GPR(rax),
+ DEFINE_GPR(rbx),
+ DEFINE_GPR_ALT(rcx, "arg4", GENERIC_REGNUM_ARG4),
+ DEFINE_GPR_ALT(rdx, "arg3", GENERIC_REGNUM_ARG3),
+ DEFINE_GPR_ALT(rdi, "arg1", GENERIC_REGNUM_ARG1),
+ DEFINE_GPR_ALT(rsi, "arg2", GENERIC_REGNUM_ARG2),
+ DEFINE_GPR_ALT(rbp, "fp", GENERIC_REGNUM_FP),
+ DEFINE_GPR_ALT(rsp, "sp", GENERIC_REGNUM_SP),
+ DEFINE_GPR_ALT(r8, "arg5", GENERIC_REGNUM_ARG5),
+ DEFINE_GPR_ALT(r9, "arg6", GENERIC_REGNUM_ARG6),
+ DEFINE_GPR(r10),
+ DEFINE_GPR(r11),
+ DEFINE_GPR(r12),
+ DEFINE_GPR(r13),
+ DEFINE_GPR(r14),
+ DEFINE_GPR(r15),
+ DEFINE_GPR_ALT4(rip, "pc", GENERIC_REGNUM_PC),
+ DEFINE_GPR_ALT3(rflags, "flags", GENERIC_REGNUM_FLAGS),
+ DEFINE_GPR_ALT2(cs, NULL),
+ DEFINE_GPR_ALT2(fs, NULL),
+ DEFINE_GPR_ALT2(gs, NULL),
+ DEFINE_GPR_PSEUDO_32(eax, rax),
+ DEFINE_GPR_PSEUDO_32(ebx, rbx),
+ DEFINE_GPR_PSEUDO_32(ecx, rcx),
+ DEFINE_GPR_PSEUDO_32(edx, rdx),
+ DEFINE_GPR_PSEUDO_32(edi, rdi),
+ DEFINE_GPR_PSEUDO_32(esi, rsi),
+ DEFINE_GPR_PSEUDO_32(ebp, rbp),
+ DEFINE_GPR_PSEUDO_32(esp, rsp),
+ DEFINE_GPR_PSEUDO_32(r8d, r8),
+ DEFINE_GPR_PSEUDO_32(r9d, r9),
+ DEFINE_GPR_PSEUDO_32(r10d, r10),
+ DEFINE_GPR_PSEUDO_32(r11d, r11),
+ DEFINE_GPR_PSEUDO_32(r12d, r12),
+ DEFINE_GPR_PSEUDO_32(r13d, r13),
+ DEFINE_GPR_PSEUDO_32(r14d, r14),
+ DEFINE_GPR_PSEUDO_32(r15d, r15),
+ DEFINE_GPR_PSEUDO_16(ax, rax),
+ DEFINE_GPR_PSEUDO_16(bx, rbx),
+ DEFINE_GPR_PSEUDO_16(cx, rcx),
+ DEFINE_GPR_PSEUDO_16(dx, rdx),
+ DEFINE_GPR_PSEUDO_16(di, rdi),
+ DEFINE_GPR_PSEUDO_16(si, rsi),
+ DEFINE_GPR_PSEUDO_16(bp, rbp),
+ DEFINE_GPR_PSEUDO_16(sp, rsp),
+ DEFINE_GPR_PSEUDO_16(r8w, r8),
+ DEFINE_GPR_PSEUDO_16(r9w, r9),
+ DEFINE_GPR_PSEUDO_16(r10w, r10),
+ DEFINE_GPR_PSEUDO_16(r11w, r11),
+ DEFINE_GPR_PSEUDO_16(r12w, r12),
+ DEFINE_GPR_PSEUDO_16(r13w, r13),
+ DEFINE_GPR_PSEUDO_16(r14w, r14),
+ DEFINE_GPR_PSEUDO_16(r15w, r15),
+ DEFINE_GPR_PSEUDO_8H(ah, rax),
+ DEFINE_GPR_PSEUDO_8H(bh, rbx),
+ DEFINE_GPR_PSEUDO_8H(ch, rcx),
+ DEFINE_GPR_PSEUDO_8H(dh, rdx),
+ DEFINE_GPR_PSEUDO_8L(al, rax),
+ DEFINE_GPR_PSEUDO_8L(bl, rbx),
+ DEFINE_GPR_PSEUDO_8L(cl, rcx),
+ DEFINE_GPR_PSEUDO_8L(dl, rdx),
+ DEFINE_GPR_PSEUDO_8L(dil, rdi),
+ DEFINE_GPR_PSEUDO_8L(sil, rsi),
+ DEFINE_GPR_PSEUDO_8L(bpl, rbp),
+ DEFINE_GPR_PSEUDO_8L(spl, rsp),
+ DEFINE_GPR_PSEUDO_8L(r8l, r8),
+ DEFINE_GPR_PSEUDO_8L(r9l, r9),
+ DEFINE_GPR_PSEUDO_8L(r10l, r10),
+ DEFINE_GPR_PSEUDO_8L(r11l, r11),
+ DEFINE_GPR_PSEUDO_8L(r12l, r12),
+ DEFINE_GPR_PSEUDO_8L(r13l, r13),
+ DEFINE_GPR_PSEUDO_8L(r14l, r14),
+ DEFINE_GPR_PSEUDO_8L(r15l, r15)};
+
+// Floating point registers 64 bit
+const DNBRegisterInfo DNBArchImplX86_64::g_fpu_registers_no_avx[] = {
+ {e_regSetFPU, fpu_fcw, "fctrl", NULL, Uint, Hex, FPU_SIZE_UINT(fcw),
+ FPU_OFFSET(fcw), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_fsw, "fstat", NULL, Uint, Hex, FPU_SIZE_UINT(fsw),
+ FPU_OFFSET(fsw), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_ftw, "ftag", NULL, Uint, Hex, 2 /* sizeof __fpu_ftw + sizeof __fpu_rsrv1 */,
+ FPU_OFFSET(ftw), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_fop, "fop", NULL, Uint, Hex, FPU_SIZE_UINT(fop),
+ FPU_OFFSET(fop), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_ip, "fioff", NULL, Uint, Hex, FPU_SIZE_UINT(ip),
+ FPU_OFFSET(ip), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_cs, "fiseg", NULL, Uint, Hex, FPU_SIZE_UINT(cs),
+ FPU_OFFSET(cs), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_dp, "fooff", NULL, Uint, Hex, FPU_SIZE_UINT(dp),
+ FPU_OFFSET(dp), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_ds, "foseg", NULL, Uint, Hex, FPU_SIZE_UINT(ds),
+ FPU_OFFSET(ds), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_mxcsr, "mxcsr", NULL, Uint, Hex, FPU_SIZE_UINT(mxcsr),
+ FPU_OFFSET(mxcsr), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_mxcsrmask, "mxcsrmask", NULL, Uint, Hex,
+ FPU_SIZE_UINT(mxcsrmask), FPU_OFFSET(mxcsrmask), -1U, -1U, -1U, -1U, NULL,
+ NULL},
+
+ {e_regSetFPU, fpu_stmm0, "stmm0", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm0), FPU_OFFSET(stmm0), ehframe_dwarf_stmm0,
+ ehframe_dwarf_stmm0, -1U, debugserver_stmm0, NULL, NULL},
+ {e_regSetFPU, fpu_stmm1, "stmm1", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm1), FPU_OFFSET(stmm1), ehframe_dwarf_stmm1,
+ ehframe_dwarf_stmm1, -1U, debugserver_stmm1, NULL, NULL},
+ {e_regSetFPU, fpu_stmm2, "stmm2", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm2), FPU_OFFSET(stmm2), ehframe_dwarf_stmm2,
+ ehframe_dwarf_stmm2, -1U, debugserver_stmm2, NULL, NULL},
+ {e_regSetFPU, fpu_stmm3, "stmm3", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm3), FPU_OFFSET(stmm3), ehframe_dwarf_stmm3,
+ ehframe_dwarf_stmm3, -1U, debugserver_stmm3, NULL, NULL},
+ {e_regSetFPU, fpu_stmm4, "stmm4", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm4), FPU_OFFSET(stmm4), ehframe_dwarf_stmm4,
+ ehframe_dwarf_stmm4, -1U, debugserver_stmm4, NULL, NULL},
+ {e_regSetFPU, fpu_stmm5, "stmm5", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm5), FPU_OFFSET(stmm5), ehframe_dwarf_stmm5,
+ ehframe_dwarf_stmm5, -1U, debugserver_stmm5, NULL, NULL},
+ {e_regSetFPU, fpu_stmm6, "stmm6", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm6), FPU_OFFSET(stmm6), ehframe_dwarf_stmm6,
+ ehframe_dwarf_stmm6, -1U, debugserver_stmm6, NULL, NULL},
+ {e_regSetFPU, fpu_stmm7, "stmm7", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm7), FPU_OFFSET(stmm7), ehframe_dwarf_stmm7,
+ ehframe_dwarf_stmm7, -1U, debugserver_stmm7, NULL, NULL},
+
+ {e_regSetFPU, fpu_xmm0, "xmm0", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm0), FPU_OFFSET(xmm0), ehframe_dwarf_xmm0,
+ ehframe_dwarf_xmm0, -1U, debugserver_xmm0, NULL, NULL},
+ {e_regSetFPU, fpu_xmm1, "xmm1", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm1), FPU_OFFSET(xmm1), ehframe_dwarf_xmm1,
+ ehframe_dwarf_xmm1, -1U, debugserver_xmm1, NULL, NULL},
+ {e_regSetFPU, fpu_xmm2, "xmm2", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm2), FPU_OFFSET(xmm2), ehframe_dwarf_xmm2,
+ ehframe_dwarf_xmm2, -1U, debugserver_xmm2, NULL, NULL},
+ {e_regSetFPU, fpu_xmm3, "xmm3", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm3), FPU_OFFSET(xmm3), ehframe_dwarf_xmm3,
+ ehframe_dwarf_xmm3, -1U, debugserver_xmm3, NULL, NULL},
+ {e_regSetFPU, fpu_xmm4, "xmm4", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm4), FPU_OFFSET(xmm4), ehframe_dwarf_xmm4,
+ ehframe_dwarf_xmm4, -1U, debugserver_xmm4, NULL, NULL},
+ {e_regSetFPU, fpu_xmm5, "xmm5", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm5), FPU_OFFSET(xmm5), ehframe_dwarf_xmm5,
+ ehframe_dwarf_xmm5, -1U, debugserver_xmm5, NULL, NULL},
+ {e_regSetFPU, fpu_xmm6, "xmm6", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm6), FPU_OFFSET(xmm6), ehframe_dwarf_xmm6,
+ ehframe_dwarf_xmm6, -1U, debugserver_xmm6, NULL, NULL},
+ {e_regSetFPU, fpu_xmm7, "xmm7", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm7), FPU_OFFSET(xmm7), ehframe_dwarf_xmm7,
+ ehframe_dwarf_xmm7, -1U, debugserver_xmm7, NULL, NULL},
+ {e_regSetFPU, fpu_xmm8, "xmm8", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm8), FPU_OFFSET(xmm8), ehframe_dwarf_xmm8,
+ ehframe_dwarf_xmm8, -1U, debugserver_xmm8, NULL, NULL},
+ {e_regSetFPU, fpu_xmm9, "xmm9", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm9), FPU_OFFSET(xmm9), ehframe_dwarf_xmm9,
+ ehframe_dwarf_xmm9, -1U, debugserver_xmm9, NULL, NULL},
+ {e_regSetFPU, fpu_xmm10, "xmm10", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm10), FPU_OFFSET(xmm10), ehframe_dwarf_xmm10,
+ ehframe_dwarf_xmm10, -1U, debugserver_xmm10, NULL, NULL},
+ {e_regSetFPU, fpu_xmm11, "xmm11", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm11), FPU_OFFSET(xmm11), ehframe_dwarf_xmm11,
+ ehframe_dwarf_xmm11, -1U, debugserver_xmm11, NULL, NULL},
+ {e_regSetFPU, fpu_xmm12, "xmm12", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm12), FPU_OFFSET(xmm12), ehframe_dwarf_xmm12,
+ ehframe_dwarf_xmm12, -1U, debugserver_xmm12, NULL, NULL},
+ {e_regSetFPU, fpu_xmm13, "xmm13", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm13), FPU_OFFSET(xmm13), ehframe_dwarf_xmm13,
+ ehframe_dwarf_xmm13, -1U, debugserver_xmm13, NULL, NULL},
+ {e_regSetFPU, fpu_xmm14, "xmm14", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm14), FPU_OFFSET(xmm14), ehframe_dwarf_xmm14,
+ ehframe_dwarf_xmm14, -1U, debugserver_xmm14, NULL, NULL},
+ {e_regSetFPU, fpu_xmm15, "xmm15", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm15), FPU_OFFSET(xmm15), ehframe_dwarf_xmm15,
+ ehframe_dwarf_xmm15, -1U, debugserver_xmm15, NULL, NULL},
+};
+
+static const char *g_contained_ymm0[] = {"ymm0", NULL};
+static const char *g_contained_ymm1[] = {"ymm1", NULL};
+static const char *g_contained_ymm2[] = {"ymm2", NULL};
+static const char *g_contained_ymm3[] = {"ymm3", NULL};
+static const char *g_contained_ymm4[] = {"ymm4", NULL};
+static const char *g_contained_ymm5[] = {"ymm5", NULL};
+static const char *g_contained_ymm6[] = {"ymm6", NULL};
+static const char *g_contained_ymm7[] = {"ymm7", NULL};
+static const char *g_contained_ymm8[] = {"ymm8", NULL};
+static const char *g_contained_ymm9[] = {"ymm9", NULL};
+static const char *g_contained_ymm10[] = {"ymm10", NULL};
+static const char *g_contained_ymm11[] = {"ymm11", NULL};
+static const char *g_contained_ymm12[] = {"ymm12", NULL};
+static const char *g_contained_ymm13[] = {"ymm13", NULL};
+static const char *g_contained_ymm14[] = {"ymm14", NULL};
+static const char *g_contained_ymm15[] = {"ymm15", NULL};
+
+const DNBRegisterInfo DNBArchImplX86_64::g_fpu_registers_avx[] = {
+ {e_regSetFPU, fpu_fcw, "fctrl", NULL, Uint, Hex, FPU_SIZE_UINT(fcw),
+ AVX_OFFSET(fcw), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_fsw, "fstat", NULL, Uint, Hex, FPU_SIZE_UINT(fsw),
+ AVX_OFFSET(fsw), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_ftw, "ftag", NULL, Uint, Hex, 2 /* sizeof __fpu_ftw + sizeof __fpu_rsrv1 */,
+ AVX_OFFSET(ftw), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_fop, "fop", NULL, Uint, Hex, FPU_SIZE_UINT(fop),
+ AVX_OFFSET(fop), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_ip, "fioff", NULL, Uint, Hex, FPU_SIZE_UINT(ip),
+ AVX_OFFSET(ip), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_cs, "fiseg", NULL, Uint, Hex, FPU_SIZE_UINT(cs),
+ AVX_OFFSET(cs), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_dp, "fooff", NULL, Uint, Hex, FPU_SIZE_UINT(dp),
+ AVX_OFFSET(dp), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_ds, "foseg", NULL, Uint, Hex, FPU_SIZE_UINT(ds),
+ AVX_OFFSET(ds), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_mxcsr, "mxcsr", NULL, Uint, Hex, FPU_SIZE_UINT(mxcsr),
+ AVX_OFFSET(mxcsr), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_mxcsrmask, "mxcsrmask", NULL, Uint, Hex,
+ FPU_SIZE_UINT(mxcsrmask), AVX_OFFSET(mxcsrmask), -1U, -1U, -1U, -1U, NULL,
+ NULL},
+
+ {e_regSetFPU, fpu_stmm0, "stmm0", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm0), AVX_OFFSET(stmm0), ehframe_dwarf_stmm0,
+ ehframe_dwarf_stmm0, -1U, debugserver_stmm0, NULL, NULL},
+ {e_regSetFPU, fpu_stmm1, "stmm1", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm1), AVX_OFFSET(stmm1), ehframe_dwarf_stmm1,
+ ehframe_dwarf_stmm1, -1U, debugserver_stmm1, NULL, NULL},
+ {e_regSetFPU, fpu_stmm2, "stmm2", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm2), AVX_OFFSET(stmm2), ehframe_dwarf_stmm2,
+ ehframe_dwarf_stmm2, -1U, debugserver_stmm2, NULL, NULL},
+ {e_regSetFPU, fpu_stmm3, "stmm3", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm3), AVX_OFFSET(stmm3), ehframe_dwarf_stmm3,
+ ehframe_dwarf_stmm3, -1U, debugserver_stmm3, NULL, NULL},
+ {e_regSetFPU, fpu_stmm4, "stmm4", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm4), AVX_OFFSET(stmm4), ehframe_dwarf_stmm4,
+ ehframe_dwarf_stmm4, -1U, debugserver_stmm4, NULL, NULL},
+ {e_regSetFPU, fpu_stmm5, "stmm5", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm5), AVX_OFFSET(stmm5), ehframe_dwarf_stmm5,
+ ehframe_dwarf_stmm5, -1U, debugserver_stmm5, NULL, NULL},
+ {e_regSetFPU, fpu_stmm6, "stmm6", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm6), AVX_OFFSET(stmm6), ehframe_dwarf_stmm6,
+ ehframe_dwarf_stmm6, -1U, debugserver_stmm6, NULL, NULL},
+ {e_regSetFPU, fpu_stmm7, "stmm7", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm7), AVX_OFFSET(stmm7), ehframe_dwarf_stmm7,
+ ehframe_dwarf_stmm7, -1U, debugserver_stmm7, NULL, NULL},
+
+ {e_regSetFPU, fpu_ymm0, "ymm0", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_YMM(ymm0), AVX_OFFSET_YMM(0), ehframe_dwarf_ymm0,
+ ehframe_dwarf_ymm0, -1U, debugserver_ymm0, NULL, NULL},
+ {e_regSetFPU, fpu_ymm1, "ymm1", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_YMM(ymm1), AVX_OFFSET_YMM(1), ehframe_dwarf_ymm1,
+ ehframe_dwarf_ymm1, -1U, debugserver_ymm1, NULL, NULL},
+ {e_regSetFPU, fpu_ymm2, "ymm2", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_YMM(ymm2), AVX_OFFSET_YMM(2), ehframe_dwarf_ymm2,
+ ehframe_dwarf_ymm2, -1U, debugserver_ymm2, NULL, NULL},
+ {e_regSetFPU, fpu_ymm3, "ymm3", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_YMM(ymm3), AVX_OFFSET_YMM(3), ehframe_dwarf_ymm3,
+ ehframe_dwarf_ymm3, -1U, debugserver_ymm3, NULL, NULL},
+ {e_regSetFPU, fpu_ymm4, "ymm4", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_YMM(ymm4), AVX_OFFSET_YMM(4), ehframe_dwarf_ymm4,
+ ehframe_dwarf_ymm4, -1U, debugserver_ymm4, NULL, NULL},
+ {e_regSetFPU, fpu_ymm5, "ymm5", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_YMM(ymm5), AVX_OFFSET_YMM(5), ehframe_dwarf_ymm5,
+ ehframe_dwarf_ymm5, -1U, debugserver_ymm5, NULL, NULL},
+ {e_regSetFPU, fpu_ymm6, "ymm6", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_YMM(ymm6), AVX_OFFSET_YMM(6), ehframe_dwarf_ymm6,
+ ehframe_dwarf_ymm6, -1U, debugserver_ymm6, NULL, NULL},
+ {e_regSetFPU, fpu_ymm7, "ymm7", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_YMM(ymm7), AVX_OFFSET_YMM(7), ehframe_dwarf_ymm7,
+ ehframe_dwarf_ymm7, -1U, debugserver_ymm7, NULL, NULL},
+ {e_regSetFPU, fpu_ymm8, "ymm8", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_YMM(ymm8), AVX_OFFSET_YMM(8), ehframe_dwarf_ymm8,
+ ehframe_dwarf_ymm8, -1U, debugserver_ymm8, NULL, NULL},
+ {e_regSetFPU, fpu_ymm9, "ymm9", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_YMM(ymm9), AVX_OFFSET_YMM(9), ehframe_dwarf_ymm9,
+ ehframe_dwarf_ymm9, -1U, debugserver_ymm9, NULL, NULL},
+ {e_regSetFPU, fpu_ymm10, "ymm10", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_YMM(ymm10), AVX_OFFSET_YMM(10), ehframe_dwarf_ymm10,
+ ehframe_dwarf_ymm10, -1U, debugserver_ymm10, NULL, NULL},
+ {e_regSetFPU, fpu_ymm11, "ymm11", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_YMM(ymm11), AVX_OFFSET_YMM(11), ehframe_dwarf_ymm11,
+ ehframe_dwarf_ymm11, -1U, debugserver_ymm11, NULL, NULL},
+ {e_regSetFPU, fpu_ymm12, "ymm12", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_YMM(ymm12), AVX_OFFSET_YMM(12), ehframe_dwarf_ymm12,
+ ehframe_dwarf_ymm12, -1U, debugserver_ymm12, NULL, NULL},
+ {e_regSetFPU, fpu_ymm13, "ymm13", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_YMM(ymm13), AVX_OFFSET_YMM(13), ehframe_dwarf_ymm13,
+ ehframe_dwarf_ymm13, -1U, debugserver_ymm13, NULL, NULL},
+ {e_regSetFPU, fpu_ymm14, "ymm14", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_YMM(ymm14), AVX_OFFSET_YMM(14), ehframe_dwarf_ymm14,
+ ehframe_dwarf_ymm14, -1U, debugserver_ymm14, NULL, NULL},
+ {e_regSetFPU, fpu_ymm15, "ymm15", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_YMM(ymm15), AVX_OFFSET_YMM(15), ehframe_dwarf_ymm15,
+ ehframe_dwarf_ymm15, -1U, debugserver_ymm15, NULL, NULL},
+
+ {e_regSetFPU, fpu_xmm0, "xmm0", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm0), 0, ehframe_dwarf_xmm0, ehframe_dwarf_xmm0, -1U,
+ debugserver_xmm0, g_contained_ymm0, NULL},
+ {e_regSetFPU, fpu_xmm1, "xmm1", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm1), 0, ehframe_dwarf_xmm1, ehframe_dwarf_xmm1, -1U,
+ debugserver_xmm1, g_contained_ymm1, NULL},
+ {e_regSetFPU, fpu_xmm2, "xmm2", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm2), 0, ehframe_dwarf_xmm2, ehframe_dwarf_xmm2, -1U,
+ debugserver_xmm2, g_contained_ymm2, NULL},
+ {e_regSetFPU, fpu_xmm3, "xmm3", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm3), 0, ehframe_dwarf_xmm3, ehframe_dwarf_xmm3, -1U,
+ debugserver_xmm3, g_contained_ymm3, NULL},
+ {e_regSetFPU, fpu_xmm4, "xmm4", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm4), 0, ehframe_dwarf_xmm4, ehframe_dwarf_xmm4, -1U,
+ debugserver_xmm4, g_contained_ymm4, NULL},
+ {e_regSetFPU, fpu_xmm5, "xmm5", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm5), 0, ehframe_dwarf_xmm5, ehframe_dwarf_xmm5, -1U,
+ debugserver_xmm5, g_contained_ymm5, NULL},
+ {e_regSetFPU, fpu_xmm6, "xmm6", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm6), 0, ehframe_dwarf_xmm6, ehframe_dwarf_xmm6, -1U,
+ debugserver_xmm6, g_contained_ymm6, NULL},
+ {e_regSetFPU, fpu_xmm7, "xmm7", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm7), 0, ehframe_dwarf_xmm7, ehframe_dwarf_xmm7, -1U,
+ debugserver_xmm7, g_contained_ymm7, NULL},
+ {e_regSetFPU, fpu_xmm8, "xmm8", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm8), 0, ehframe_dwarf_xmm8, ehframe_dwarf_xmm8, -1U,
+ debugserver_xmm8, g_contained_ymm8, NULL},
+ {e_regSetFPU, fpu_xmm9, "xmm9", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm9), 0, ehframe_dwarf_xmm9, ehframe_dwarf_xmm9, -1U,
+ debugserver_xmm9, g_contained_ymm9, NULL},
+ {e_regSetFPU, fpu_xmm10, "xmm10", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm10), 0, ehframe_dwarf_xmm10, ehframe_dwarf_xmm10, -1U,
+ debugserver_xmm10, g_contained_ymm10, NULL},
+ {e_regSetFPU, fpu_xmm11, "xmm11", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm11), 0, ehframe_dwarf_xmm11, ehframe_dwarf_xmm11, -1U,
+ debugserver_xmm11, g_contained_ymm11, NULL},
+ {e_regSetFPU, fpu_xmm12, "xmm12", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm12), 0, ehframe_dwarf_xmm12, ehframe_dwarf_xmm12, -1U,
+ debugserver_xmm12, g_contained_ymm12, NULL},
+ {e_regSetFPU, fpu_xmm13, "xmm13", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm13), 0, ehframe_dwarf_xmm13, ehframe_dwarf_xmm13, -1U,
+ debugserver_xmm13, g_contained_ymm13, NULL},
+ {e_regSetFPU, fpu_xmm14, "xmm14", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm14), 0, ehframe_dwarf_xmm14, ehframe_dwarf_xmm14, -1U,
+ debugserver_xmm14, g_contained_ymm14, NULL},
+ {e_regSetFPU, fpu_xmm15, "xmm15", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_XMM(xmm15), 0, ehframe_dwarf_xmm15, ehframe_dwarf_xmm15, -1U,
+ debugserver_xmm15, g_contained_ymm15, NULL}
+
+};
+
+static const char *g_contained_zmm0[] = {"zmm0", NULL};
+static const char *g_contained_zmm1[] = {"zmm1", NULL};
+static const char *g_contained_zmm2[] = {"zmm2", NULL};
+static const char *g_contained_zmm3[] = {"zmm3", NULL};
+static const char *g_contained_zmm4[] = {"zmm4", NULL};
+static const char *g_contained_zmm5[] = {"zmm5", NULL};
+static const char *g_contained_zmm6[] = {"zmm6", NULL};
+static const char *g_contained_zmm7[] = {"zmm7", NULL};
+static const char *g_contained_zmm8[] = {"zmm8", NULL};
+static const char *g_contained_zmm9[] = {"zmm9", NULL};
+static const char *g_contained_zmm10[] = {"zmm10", NULL};
+static const char *g_contained_zmm11[] = {"zmm11", NULL};
+static const char *g_contained_zmm12[] = {"zmm12", NULL};
+static const char *g_contained_zmm13[] = {"zmm13", NULL};
+static const char *g_contained_zmm14[] = {"zmm14", NULL};
+static const char *g_contained_zmm15[] = {"zmm15", NULL};
+
+#define STR(s) #s
+
+#define ZMM_REG_DEF(reg) \
+ { \
+ e_regSetFPU, fpu_zmm##reg, STR(zmm##reg), NULL, Vector, VectorOfUInt8, \
+ FPU_SIZE_ZMM(zmm##reg), AVX512F_OFFSET_ZMM(reg), \
+ ehframe_dwarf_zmm##reg, ehframe_dwarf_zmm##reg, -1U, \
+ debugserver_zmm##reg, NULL, NULL \
+ }
+
+#define YMM_REG_ALIAS(reg) \
+ { \
+ e_regSetFPU, fpu_ymm##reg, STR(ymm##reg), NULL, Vector, VectorOfUInt8, \
+ FPU_SIZE_YMM(ymm##reg), 0, ehframe_dwarf_ymm##reg, \
+ ehframe_dwarf_ymm##reg, -1U, debugserver_ymm##reg, \
+ g_contained_zmm##reg, NULL \
+ }
+
+#define XMM_REG_ALIAS(reg) \
+ { \
+ e_regSetFPU, fpu_xmm##reg, STR(xmm##reg), NULL, Vector, VectorOfUInt8, \
+ FPU_SIZE_XMM(xmm##reg), 0, ehframe_dwarf_xmm##reg, \
+ ehframe_dwarf_xmm##reg, -1U, debugserver_xmm##reg, \
+ g_contained_zmm##reg, NULL \
+ }
+
+#define AVX512_K_REG_DEF(reg) \
+ { \
+ e_regSetFPU, fpu_k##reg, STR(k##reg), NULL, Vector, VectorOfUInt8, 8, \
+ AVX512F_OFFSET(k##reg), ehframe_dwarf_k##reg, ehframe_dwarf_k##reg, \
+ -1U, debugserver_k##reg, NULL, NULL \
+ }
+
+const DNBRegisterInfo DNBArchImplX86_64::g_fpu_registers_avx512f[] = {
+ {e_regSetFPU, fpu_fcw, "fctrl", NULL, Uint, Hex, FPU_SIZE_UINT(fcw),
+ AVX_OFFSET(fcw), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_fsw, "fstat", NULL, Uint, Hex, FPU_SIZE_UINT(fsw),
+ AVX_OFFSET(fsw), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_ftw, "ftag", NULL, Uint, Hex, 2 /* sizeof __fpu_ftw + sizeof __fpu_rsrv1 */,
+ AVX_OFFSET(ftw), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_fop, "fop", NULL, Uint, Hex, FPU_SIZE_UINT(fop),
+ AVX_OFFSET(fop), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_ip, "fioff", NULL, Uint, Hex, FPU_SIZE_UINT(ip),
+ AVX_OFFSET(ip), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_cs, "fiseg", NULL, Uint, Hex, FPU_SIZE_UINT(cs),
+ AVX_OFFSET(cs), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_dp, "fooff", NULL, Uint, Hex, FPU_SIZE_UINT(dp),
+ AVX_OFFSET(dp), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_ds, "foseg", NULL, Uint, Hex, FPU_SIZE_UINT(ds),
+ AVX_OFFSET(ds), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_mxcsr, "mxcsr", NULL, Uint, Hex, FPU_SIZE_UINT(mxcsr),
+ AVX_OFFSET(mxcsr), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetFPU, fpu_mxcsrmask, "mxcsrmask", NULL, Uint, Hex,
+ FPU_SIZE_UINT(mxcsrmask), AVX_OFFSET(mxcsrmask), -1U, -1U, -1U, -1U, NULL,
+ NULL},
+
+ {e_regSetFPU, fpu_stmm0, "stmm0", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm0), AVX_OFFSET(stmm0), ehframe_dwarf_stmm0,
+ ehframe_dwarf_stmm0, -1U, debugserver_stmm0, NULL, NULL},
+ {e_regSetFPU, fpu_stmm1, "stmm1", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm1), AVX_OFFSET(stmm1), ehframe_dwarf_stmm1,
+ ehframe_dwarf_stmm1, -1U, debugserver_stmm1, NULL, NULL},
+ {e_regSetFPU, fpu_stmm2, "stmm2", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm2), AVX_OFFSET(stmm2), ehframe_dwarf_stmm2,
+ ehframe_dwarf_stmm2, -1U, debugserver_stmm2, NULL, NULL},
+ {e_regSetFPU, fpu_stmm3, "stmm3", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm3), AVX_OFFSET(stmm3), ehframe_dwarf_stmm3,
+ ehframe_dwarf_stmm3, -1U, debugserver_stmm3, NULL, NULL},
+ {e_regSetFPU, fpu_stmm4, "stmm4", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm4), AVX_OFFSET(stmm4), ehframe_dwarf_stmm4,
+ ehframe_dwarf_stmm4, -1U, debugserver_stmm4, NULL, NULL},
+ {e_regSetFPU, fpu_stmm5, "stmm5", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm5), AVX_OFFSET(stmm5), ehframe_dwarf_stmm5,
+ ehframe_dwarf_stmm5, -1U, debugserver_stmm5, NULL, NULL},
+ {e_regSetFPU, fpu_stmm6, "stmm6", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm6), AVX_OFFSET(stmm6), ehframe_dwarf_stmm6,
+ ehframe_dwarf_stmm6, -1U, debugserver_stmm6, NULL, NULL},
+ {e_regSetFPU, fpu_stmm7, "stmm7", NULL, Vector, VectorOfUInt8,
+ FPU_SIZE_MMST(stmm7), AVX_OFFSET(stmm7), ehframe_dwarf_stmm7,
+ ehframe_dwarf_stmm7, -1U, debugserver_stmm7, NULL, NULL},
+
+ AVX512_K_REG_DEF(0),
+ AVX512_K_REG_DEF(1),
+ AVX512_K_REG_DEF(2),
+ AVX512_K_REG_DEF(3),
+ AVX512_K_REG_DEF(4),
+ AVX512_K_REG_DEF(5),
+ AVX512_K_REG_DEF(6),
+ AVX512_K_REG_DEF(7),
+
+ ZMM_REG_DEF(0),
+ ZMM_REG_DEF(1),
+ ZMM_REG_DEF(2),
+ ZMM_REG_DEF(3),
+ ZMM_REG_DEF(4),
+ ZMM_REG_DEF(5),
+ ZMM_REG_DEF(6),
+ ZMM_REG_DEF(7),
+ ZMM_REG_DEF(8),
+ ZMM_REG_DEF(9),
+ ZMM_REG_DEF(10),
+ ZMM_REG_DEF(11),
+ ZMM_REG_DEF(12),
+ ZMM_REG_DEF(13),
+ ZMM_REG_DEF(14),
+ ZMM_REG_DEF(15),
+ ZMM_REG_DEF(16),
+ ZMM_REG_DEF(17),
+ ZMM_REG_DEF(18),
+ ZMM_REG_DEF(19),
+ ZMM_REG_DEF(20),
+ ZMM_REG_DEF(21),
+ ZMM_REG_DEF(22),
+ ZMM_REG_DEF(23),
+ ZMM_REG_DEF(24),
+ ZMM_REG_DEF(25),
+ ZMM_REG_DEF(26),
+ ZMM_REG_DEF(27),
+ ZMM_REG_DEF(28),
+ ZMM_REG_DEF(29),
+ ZMM_REG_DEF(30),
+ ZMM_REG_DEF(31),
+
+ YMM_REG_ALIAS(0),
+ YMM_REG_ALIAS(1),
+ YMM_REG_ALIAS(2),
+ YMM_REG_ALIAS(3),
+ YMM_REG_ALIAS(4),
+ YMM_REG_ALIAS(5),
+ YMM_REG_ALIAS(6),
+ YMM_REG_ALIAS(7),
+ YMM_REG_ALIAS(8),
+ YMM_REG_ALIAS(9),
+ YMM_REG_ALIAS(10),
+ YMM_REG_ALIAS(11),
+ YMM_REG_ALIAS(12),
+ YMM_REG_ALIAS(13),
+ YMM_REG_ALIAS(14),
+ YMM_REG_ALIAS(15),
+
+ XMM_REG_ALIAS(0),
+ XMM_REG_ALIAS(1),
+ XMM_REG_ALIAS(2),
+ XMM_REG_ALIAS(3),
+ XMM_REG_ALIAS(4),
+ XMM_REG_ALIAS(5),
+ XMM_REG_ALIAS(6),
+ XMM_REG_ALIAS(7),
+ XMM_REG_ALIAS(8),
+ XMM_REG_ALIAS(9),
+ XMM_REG_ALIAS(10),
+ XMM_REG_ALIAS(11),
+ XMM_REG_ALIAS(12),
+ XMM_REG_ALIAS(13),
+ XMM_REG_ALIAS(14),
+ XMM_REG_ALIAS(15),
+
+};
+
+
+// Exception registers
+
+const DNBRegisterInfo DNBArchImplX86_64::g_exc_registers[] = {
+ {e_regSetEXC, exc_trapno, "trapno", NULL, Uint, Hex, EXC_SIZE(trapno),
+ EXC_OFFSET(trapno), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetEXC, exc_err, "err", NULL, Uint, Hex, EXC_SIZE(err),
+ EXC_OFFSET(err), -1U, -1U, -1U, -1U, NULL, NULL},
+ {e_regSetEXC, exc_faultvaddr, "faultvaddr", NULL, Uint, Hex,
+ EXC_SIZE(faultvaddr), EXC_OFFSET(faultvaddr), -1U, -1U, -1U, -1U, NULL,
+ NULL}};
+
+// Number of registers in each register set
+const size_t DNBArchImplX86_64::k_num_gpr_registers =
+ sizeof(g_gpr_registers) / sizeof(DNBRegisterInfo);
+const size_t DNBArchImplX86_64::k_num_fpu_registers_no_avx =
+ sizeof(g_fpu_registers_no_avx) / sizeof(DNBRegisterInfo);
+const size_t DNBArchImplX86_64::k_num_fpu_registers_avx =
+ sizeof(g_fpu_registers_avx) / sizeof(DNBRegisterInfo);
+const size_t DNBArchImplX86_64::k_num_exc_registers =
+ sizeof(g_exc_registers) / sizeof(DNBRegisterInfo);
+const size_t DNBArchImplX86_64::k_num_all_registers_no_avx =
+ k_num_gpr_registers + k_num_fpu_registers_no_avx + k_num_exc_registers;
+const size_t DNBArchImplX86_64::k_num_all_registers_avx =
+ k_num_gpr_registers + k_num_fpu_registers_avx + k_num_exc_registers;
+const size_t DNBArchImplX86_64::k_num_fpu_registers_avx512f =
+ sizeof(g_fpu_registers_avx512f) / sizeof(DNBRegisterInfo);
+const size_t DNBArchImplX86_64::k_num_all_registers_avx512f =
+ k_num_gpr_registers + k_num_fpu_registers_avx512f + k_num_exc_registers;
+
+// Register set definitions. The first definitions at register set index
+// of zero is for all registers, followed by other registers sets. The
+// register information for the all register set need not be filled in.
+const DNBRegisterSetInfo DNBArchImplX86_64::g_reg_sets_no_avx[] = {
+ {"x86_64 Registers", NULL, k_num_all_registers_no_avx},
+ {"General Purpose Registers", g_gpr_registers, k_num_gpr_registers},
+ {"Floating Point Registers", g_fpu_registers_no_avx,
+ k_num_fpu_registers_no_avx},
+ {"Exception State Registers", g_exc_registers, k_num_exc_registers}};
+
+const DNBRegisterSetInfo DNBArchImplX86_64::g_reg_sets_avx[] = {
+ {"x86_64 Registers", NULL, k_num_all_registers_avx},
+ {"General Purpose Registers", g_gpr_registers, k_num_gpr_registers},
+ {"Floating Point Registers", g_fpu_registers_avx, k_num_fpu_registers_avx},
+ {"Exception State Registers", g_exc_registers, k_num_exc_registers}};
+
+const DNBRegisterSetInfo DNBArchImplX86_64::g_reg_sets_avx512f[] = {
+ {"x86_64 Registers", NULL, k_num_all_registers_avx},
+ {"General Purpose Registers", g_gpr_registers, k_num_gpr_registers},
+ {"Floating Point Registers", g_fpu_registers_avx512f,
+ k_num_fpu_registers_avx512f},
+ {"Exception State Registers", g_exc_registers, k_num_exc_registers}};
+
+// Total number of register sets for this architecture
+const size_t DNBArchImplX86_64::k_num_register_sets =
+ sizeof(g_reg_sets_avx) / sizeof(DNBRegisterSetInfo);
+
+DNBArchProtocol *DNBArchImplX86_64::Create(MachThread *thread) {
+ DNBArchImplX86_64 *obj = new DNBArchImplX86_64(thread);
+ return obj;
+}
+
+const uint8_t *
+DNBArchImplX86_64::SoftwareBreakpointOpcode(nub_size_t byte_size) {
+ static const uint8_t g_breakpoint_opcode[] = {0xCC};
+ if (byte_size == 1)
+ return g_breakpoint_opcode;
+ return NULL;
+}
+
+const DNBRegisterSetInfo *
+DNBArchImplX86_64::GetRegisterSetInfo(nub_size_t *num_reg_sets) {
+ *num_reg_sets = k_num_register_sets;
+
+ if (CPUHasAVX512f() || FORCE_AVX_REGS)
+ return g_reg_sets_avx512f;
+ if (CPUHasAVX() || FORCE_AVX_REGS)
+ return g_reg_sets_avx;
+ else
+ return g_reg_sets_no_avx;
+}
+
+void DNBArchImplX86_64::Initialize() {
+ DNBArchPluginInfo arch_plugin_info = {
+ CPU_TYPE_X86_64, DNBArchImplX86_64::Create,
+ DNBArchImplX86_64::GetRegisterSetInfo,
+ DNBArchImplX86_64::SoftwareBreakpointOpcode};
+
+ // Register this arch plug-in with the main protocol class
+ DNBArchProtocol::RegisterArchPlugin(arch_plugin_info);
+}
+
+bool DNBArchImplX86_64::GetRegisterValue(uint32_t set, uint32_t reg,
+ DNBRegisterValue *value) {
+ if (set == REGISTER_SET_GENERIC) {
+ switch (reg) {
+ case GENERIC_REGNUM_PC: // Program Counter
+ set = e_regSetGPR;
+ reg = gpr_rip;
+ break;
+
+ case GENERIC_REGNUM_SP: // Stack Pointer
+ set = e_regSetGPR;
+ reg = gpr_rsp;
+ break;
+
+ case GENERIC_REGNUM_FP: // Frame Pointer
+ set = e_regSetGPR;
+ reg = gpr_rbp;
+ break;
+
+ case GENERIC_REGNUM_FLAGS: // Processor flags register
+ set = e_regSetGPR;
+ reg = gpr_rflags;
+ break;
+
+ case GENERIC_REGNUM_RA: // Return Address
+ default:
+ return false;
+ }
+ }
+
+ if (GetRegisterState(set, false) != KERN_SUCCESS)
+ return false;
+
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo) {
+ value->info = *regInfo;
+ switch (set) {
+ case e_regSetGPR:
+ if (reg < k_num_gpr_registers) {
+ value->value.uint64 = ((uint64_t *)(&m_state.context.gpr))[reg];
+ return true;
+ }
+ break;
+
+ case e_regSetFPU:
+ if (reg > fpu_xmm15 && !(CPUHasAVX() || FORCE_AVX_REGS))
+ return false;
+ if (reg > fpu_ymm15 && !(CPUHasAVX512f() || FORCE_AVX_REGS))
+ return false;
+ switch (reg) {
+
+ case fpu_fcw:
+ value->value.uint16 =
+ *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fcw));
+ return true;
+ case fpu_fsw:
+ value->value.uint16 =
+ *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fsw));
+ return true;
+ case fpu_ftw:
+ memcpy (&value->value.uint16, &m_state.context.fpu.no_avx.__fpu_ftw, 2);
+ return true;
+ case fpu_fop:
+ value->value.uint16 = m_state.context.fpu.no_avx.__fpu_fop;
+ return true;
+ case fpu_ip:
+ value->value.uint32 = m_state.context.fpu.no_avx.__fpu_ip;
+ return true;
+ case fpu_cs:
+ value->value.uint16 = m_state.context.fpu.no_avx.__fpu_cs;
+ return true;
+ case fpu_dp:
+ value->value.uint32 = m_state.context.fpu.no_avx.__fpu_dp;
+ return true;
+ case fpu_ds:
+ value->value.uint16 = m_state.context.fpu.no_avx.__fpu_ds;
+ return true;
+ case fpu_mxcsr:
+ value->value.uint32 = m_state.context.fpu.no_avx.__fpu_mxcsr;
+ return true;
+ case fpu_mxcsrmask:
+ value->value.uint32 = m_state.context.fpu.no_avx.__fpu_mxcsrmask;
+ return true;
+
+ case fpu_stmm0:
+ case fpu_stmm1:
+ case fpu_stmm2:
+ case fpu_stmm3:
+ case fpu_stmm4:
+ case fpu_stmm5:
+ case fpu_stmm6:
+ case fpu_stmm7:
+ memcpy(&value->value.uint8,
+ &m_state.context.fpu.no_avx.__fpu_stmm0 + (reg - fpu_stmm0), 10);
+ return true;
+
+ case fpu_xmm0:
+ case fpu_xmm1:
+ case fpu_xmm2:
+ case fpu_xmm3:
+ case fpu_xmm4:
+ case fpu_xmm5:
+ case fpu_xmm6:
+ case fpu_xmm7:
+ case fpu_xmm8:
+ case fpu_xmm9:
+ case fpu_xmm10:
+ case fpu_xmm11:
+ case fpu_xmm12:
+ case fpu_xmm13:
+ case fpu_xmm14:
+ case fpu_xmm15:
+ memcpy(&value->value.uint8,
+ &m_state.context.fpu.no_avx.__fpu_xmm0 + (reg - fpu_xmm0), 16);
+ return true;
+
+ case fpu_ymm0:
+ case fpu_ymm1:
+ case fpu_ymm2:
+ case fpu_ymm3:
+ case fpu_ymm4:
+ case fpu_ymm5:
+ case fpu_ymm6:
+ case fpu_ymm7:
+ case fpu_ymm8:
+ case fpu_ymm9:
+ case fpu_ymm10:
+ case fpu_ymm11:
+ case fpu_ymm12:
+ case fpu_ymm13:
+ case fpu_ymm14:
+ case fpu_ymm15:
+ memcpy(&value->value.uint8,
+ &m_state.context.fpu.avx.__fpu_xmm0 + (reg - fpu_ymm0), 16);
+ memcpy((&value->value.uint8) + 16,
+ &m_state.context.fpu.avx.__fpu_ymmh0 + (reg - fpu_ymm0), 16);
+ return true;
+ case fpu_k0:
+ case fpu_k1:
+ case fpu_k2:
+ case fpu_k3:
+ case fpu_k4:
+ case fpu_k5:
+ case fpu_k6:
+ case fpu_k7:
+ memcpy((&value->value.uint8),
+ &m_state.context.fpu.avx512f.__fpu_k0 + (reg - fpu_k0), 8);
+ return true;
+ case fpu_zmm0:
+ case fpu_zmm1:
+ case fpu_zmm2:
+ case fpu_zmm3:
+ case fpu_zmm4:
+ case fpu_zmm5:
+ case fpu_zmm6:
+ case fpu_zmm7:
+ case fpu_zmm8:
+ case fpu_zmm9:
+ case fpu_zmm10:
+ case fpu_zmm11:
+ case fpu_zmm12:
+ case fpu_zmm13:
+ case fpu_zmm14:
+ case fpu_zmm15:
+ memcpy(&value->value.uint8,
+ &m_state.context.fpu.avx512f.__fpu_xmm0 + (reg - fpu_zmm0), 16);
+ memcpy((&value->value.uint8) + 16,
+ &m_state.context.fpu.avx512f.__fpu_ymmh0 + (reg - fpu_zmm0), 16);
+ memcpy((&value->value.uint8) + 32,
+ &m_state.context.fpu.avx512f.__fpu_zmmh0 + (reg - fpu_zmm0), 32);
+ return true;
+ case fpu_zmm16:
+ case fpu_zmm17:
+ case fpu_zmm18:
+ case fpu_zmm19:
+ case fpu_zmm20:
+ case fpu_zmm21:
+ case fpu_zmm22:
+ case fpu_zmm23:
+ case fpu_zmm24:
+ case fpu_zmm25:
+ case fpu_zmm26:
+ case fpu_zmm27:
+ case fpu_zmm28:
+ case fpu_zmm29:
+ case fpu_zmm30:
+ case fpu_zmm31:
+ memcpy(&value->value.uint8,
+ &m_state.context.fpu.avx512f.__fpu_zmm16 + (reg - fpu_zmm16), 64);
+ return true;
+ }
+ break;
+
+ case e_regSetEXC:
+ switch (reg) {
+ case exc_trapno:
+ value->value.uint32 = m_state.context.exc.__trapno;
+ return true;
+ case exc_err:
+ value->value.uint32 = m_state.context.exc.__err;
+ return true;
+ case exc_faultvaddr:
+ value->value.uint64 = m_state.context.exc.__faultvaddr;
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+bool DNBArchImplX86_64::SetRegisterValue(uint32_t set, uint32_t reg,
+ const DNBRegisterValue *value) {
+ if (set == REGISTER_SET_GENERIC) {
+ switch (reg) {
+ case GENERIC_REGNUM_PC: // Program Counter
+ set = e_regSetGPR;
+ reg = gpr_rip;
+ break;
+
+ case GENERIC_REGNUM_SP: // Stack Pointer
+ set = e_regSetGPR;
+ reg = gpr_rsp;
+ break;
+
+ case GENERIC_REGNUM_FP: // Frame Pointer
+ set = e_regSetGPR;
+ reg = gpr_rbp;
+ break;
+
+ case GENERIC_REGNUM_FLAGS: // Processor flags register
+ set = e_regSetGPR;
+ reg = gpr_rflags;
+ break;
+
+ case GENERIC_REGNUM_RA: // Return Address
+ default:
+ return false;
+ }
+ }
+
+ if (GetRegisterState(set, false) != KERN_SUCCESS)
+ return false;
+
+ bool success = false;
+ const DNBRegisterInfo *regInfo = m_thread->GetRegisterInfo(set, reg);
+ if (regInfo) {
+ switch (set) {
+ case e_regSetGPR:
+ if (reg < k_num_gpr_registers) {
+ ((uint64_t *)(&m_state.context.gpr))[reg] = value->value.uint64;
+ success = true;
+ }
+ break;
+ if (reg > fpu_xmm15 && !(CPUHasAVX() || FORCE_AVX_REGS))
+ return false;
+ if (reg > fpu_ymm15 && !(CPUHasAVX512f() || FORCE_AVX_REGS))
+ return false;
+ case e_regSetFPU:
+ switch (reg) {
+ case fpu_fcw:
+ *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fcw)) =
+ value->value.uint16;
+ success = true;
+ break;
+ case fpu_fsw:
+ *((uint16_t *)(&m_state.context.fpu.no_avx.__fpu_fsw)) =
+ value->value.uint16;
+ success = true;
+ break;
+ case fpu_ftw:
+ memcpy (&m_state.context.fpu.no_avx.__fpu_ftw, &value->value.uint8, 2);
+ success = true;
+ break;
+ case fpu_fop:
+ m_state.context.fpu.no_avx.__fpu_fop = value->value.uint16;
+ success = true;
+ break;
+ case fpu_ip:
+ m_state.context.fpu.no_avx.__fpu_ip = value->value.uint32;
+ success = true;
+ break;
+ case fpu_cs:
+ m_state.context.fpu.no_avx.__fpu_cs = value->value.uint16;
+ success = true;
+ break;
+ case fpu_dp:
+ m_state.context.fpu.no_avx.__fpu_dp = value->value.uint32;
+ success = true;
+ break;
+ case fpu_ds:
+ m_state.context.fpu.no_avx.__fpu_ds = value->value.uint16;
+ success = true;
+ break;
+ case fpu_mxcsr:
+ m_state.context.fpu.no_avx.__fpu_mxcsr = value->value.uint32;
+ success = true;
+ break;
+ case fpu_mxcsrmask:
+ m_state.context.fpu.no_avx.__fpu_mxcsrmask = value->value.uint32;
+ success = true;
+ break;
+
+ case fpu_stmm0:
+ case fpu_stmm1:
+ case fpu_stmm2:
+ case fpu_stmm3:
+ case fpu_stmm4:
+ case fpu_stmm5:
+ case fpu_stmm6:
+ case fpu_stmm7:
+ memcpy(&m_state.context.fpu.no_avx.__fpu_stmm0 + (reg - fpu_stmm0),
+ &value->value.uint8, 10);
+ success = true;
+ break;
+
+ case fpu_xmm0:
+ case fpu_xmm1:
+ case fpu_xmm2:
+ case fpu_xmm3:
+ case fpu_xmm4:
+ case fpu_xmm5:
+ case fpu_xmm6:
+ case fpu_xmm7:
+ case fpu_xmm8:
+ case fpu_xmm9:
+ case fpu_xmm10:
+ case fpu_xmm11:
+ case fpu_xmm12:
+ case fpu_xmm13:
+ case fpu_xmm14:
+ case fpu_xmm15:
+ memcpy(&m_state.context.fpu.no_avx.__fpu_xmm0 + (reg - fpu_xmm0),
+ &value->value.uint8, 16);
+ success = true;
+ break;
+
+ case fpu_ymm0:
+ case fpu_ymm1:
+ case fpu_ymm2:
+ case fpu_ymm3:
+ case fpu_ymm4:
+ case fpu_ymm5:
+ case fpu_ymm6:
+ case fpu_ymm7:
+ case fpu_ymm8:
+ case fpu_ymm9:
+ case fpu_ymm10:
+ case fpu_ymm11:
+ case fpu_ymm12:
+ case fpu_ymm13:
+ case fpu_ymm14:
+ case fpu_ymm15:
+ memcpy(&m_state.context.fpu.avx.__fpu_xmm0 + (reg - fpu_ymm0),
+ &value->value.uint8, 16);
+ memcpy(&m_state.context.fpu.avx.__fpu_ymmh0 + (reg - fpu_ymm0),
+ (&value->value.uint8) + 16, 16);
+ return true;
+ case fpu_k0:
+ case fpu_k1:
+ case fpu_k2:
+ case fpu_k3:
+ case fpu_k4:
+ case fpu_k5:
+ case fpu_k6:
+ case fpu_k7:
+ memcpy(&m_state.context.fpu.avx512f.__fpu_k0 + (reg - fpu_k0),
+ &value->value.uint8, 8);
+ return true;
+ case fpu_zmm0:
+ case fpu_zmm1:
+ case fpu_zmm2:
+ case fpu_zmm3:
+ case fpu_zmm4:
+ case fpu_zmm5:
+ case fpu_zmm6:
+ case fpu_zmm7:
+ case fpu_zmm8:
+ case fpu_zmm9:
+ case fpu_zmm10:
+ case fpu_zmm11:
+ case fpu_zmm12:
+ case fpu_zmm13:
+ case fpu_zmm14:
+ case fpu_zmm15:
+ memcpy(&m_state.context.fpu.avx512f.__fpu_xmm0 + (reg - fpu_zmm0),
+ &value->value.uint8, 16);
+ memcpy(&m_state.context.fpu.avx512f.__fpu_ymmh0 + (reg - fpu_zmm0),
+ &value->value.uint8 + 16, 16);
+ memcpy(&m_state.context.fpu.avx512f.__fpu_zmmh0 + (reg - fpu_zmm0),
+ &value->value.uint8 + 32, 32);
+ return true;
+ case fpu_zmm16:
+ case fpu_zmm17:
+ case fpu_zmm18:
+ case fpu_zmm19:
+ case fpu_zmm20:
+ case fpu_zmm21:
+ case fpu_zmm22:
+ case fpu_zmm23:
+ case fpu_zmm24:
+ case fpu_zmm25:
+ case fpu_zmm26:
+ case fpu_zmm27:
+ case fpu_zmm28:
+ case fpu_zmm29:
+ case fpu_zmm30:
+ case fpu_zmm31:
+ memcpy(&m_state.context.fpu.avx512f.__fpu_zmm16 + (reg - fpu_zmm16),
+ &value->value.uint8, 64);
+ return true;
+ }
+ break;
+
+ case e_regSetEXC:
+ switch (reg) {
+ case exc_trapno:
+ m_state.context.exc.__trapno = value->value.uint32;
+ success = true;
+ break;
+ case exc_err:
+ m_state.context.exc.__err = value->value.uint32;
+ success = true;
+ break;
+ case exc_faultvaddr:
+ m_state.context.exc.__faultvaddr = value->value.uint64;
+ success = true;
+ break;
+ }
+ break;
+ }
+ }
+
+ if (success)
+ return SetRegisterState(set) == KERN_SUCCESS;
+ return false;
+}
+
+uint32_t DNBArchImplX86_64::GetRegisterContextSize() {
+ static uint32_t g_cached_size = 0;
+ if (g_cached_size == 0) {
+ if (CPUHasAVX512f() || FORCE_AVX_REGS) {
+ for (size_t i = 0; i < k_num_fpu_registers_avx512f; ++i) {
+ if (g_fpu_registers_avx512f[i].value_regs == NULL)
+ g_cached_size += g_fpu_registers_avx512f[i].size;
+ }
+ } else if (CPUHasAVX() || FORCE_AVX_REGS) {
+ for (size_t i = 0; i < k_num_fpu_registers_avx; ++i) {
+ if (g_fpu_registers_avx[i].value_regs == NULL)
+ g_cached_size += g_fpu_registers_avx[i].size;
+ }
+ } else {
+ for (size_t i = 0; i < k_num_fpu_registers_no_avx; ++i) {
+ if (g_fpu_registers_no_avx[i].value_regs == NULL)
+ g_cached_size += g_fpu_registers_no_avx[i].size;
+ }
+ }
+ DNBLogThreaded("DNBArchImplX86_64::GetRegisterContextSize() - GPR = %zu, "
+ "FPU = %u, EXC = %zu",
+ sizeof(GPR), g_cached_size, sizeof(EXC));
+ g_cached_size += sizeof(GPR);
+ g_cached_size += sizeof(EXC);
+ DNBLogThreaded(
+ "DNBArchImplX86_64::GetRegisterContextSize() - GPR + FPU + EXC = %u",
+ g_cached_size);
+ }
+ return g_cached_size;
+}
+
+nub_size_t DNBArchImplX86_64::GetRegisterContext(void *buf,
+ nub_size_t buf_len) {
+ uint32_t size = GetRegisterContextSize();
+
+ if (buf && buf_len) {
+ bool force = false;
+ kern_return_t kret;
+
+ if ((kret = GetGPRState(force)) != KERN_SUCCESS) {
+ DNBLogThreadedIf(LOG_THREAD, "DNBArchImplX86_64::GetRegisterContext (buf "
+ "= %p, len = %llu) error: GPR regs failed "
+ "to read: %u ",
+ buf, (uint64_t)buf_len, kret);
+ size = 0;
+ } else if ((kret = GetFPUState(force)) != KERN_SUCCESS) {
+ DNBLogThreadedIf(
+ LOG_THREAD, "DNBArchImplX86_64::GetRegisterContext (buf = %p, len = "
+ "%llu) error: %s regs failed to read: %u",
+ buf, (uint64_t)buf_len, CPUHasAVX() ? "AVX" : "FPU", kret);
+ size = 0;
+ } else if ((kret = GetEXCState(force)) != KERN_SUCCESS) {
+ DNBLogThreadedIf(LOG_THREAD, "DNBArchImplX86_64::GetRegisterContext (buf "
+ "= %p, len = %llu) error: EXC regs failed "
+ "to read: %u",
+ buf, (uint64_t)buf_len, kret);
+ size = 0;
+ } else {
+ uint8_t *p = (uint8_t *)buf;
+ // Copy the GPR registers
+ memcpy(p, &m_state.context.gpr, sizeof(GPR));
+ p += sizeof(GPR);
+
+ // Walk around the gaps in the FPU regs
+ memcpy(p, &m_state.context.fpu.no_avx.__fpu_fcw, 5);
+ // We read 5 bytes, but we skip 6 to account for __fpu_rsrv1
+ // to match the g_fpu_registers_* tables.
+ p += 6;
+ memcpy(p, &m_state.context.fpu.no_avx.__fpu_fop, 8);
+ p += 8;
+ memcpy(p, &m_state.context.fpu.no_avx.__fpu_dp, 6);
+ p += 6;
+ memcpy(p, &m_state.context.fpu.no_avx.__fpu_mxcsr, 8);
+ p += 8;
+
+ // Work around the padding between the stmm registers as they are 16
+ // byte structs with 10 bytes of the value in each
+ for (size_t i = 0; i < 8; ++i) {
+ memcpy(p, &m_state.context.fpu.no_avx.__fpu_stmm0 + i, 10);
+ p += 10;
+ }
+
+ if(CPUHasAVX512f() || FORCE_AVX_REGS) {
+ for (size_t i = 0; i < 8; ++i) {
+ memcpy(p, &m_state.context.fpu.avx512f.__fpu_k0 + i, 8);
+ p += 8;
+ }
+ }
+
+ if (CPUHasAVX() || FORCE_AVX_REGS) {
+ // Interleave the XMM and YMMH registers to make the YMM registers
+ for (size_t i = 0; i < 16; ++i) {
+ memcpy(p, &m_state.context.fpu.avx.__fpu_xmm0 + i, 16);
+ p += 16;
+ memcpy(p, &m_state.context.fpu.avx.__fpu_ymmh0 + i, 16);
+ p += 16;
+ }
+ if(CPUHasAVX512f() || FORCE_AVX_REGS) {
+ for (size_t i = 0; i < 16; ++i) {
+ memcpy(p, &m_state.context.fpu.avx512f.__fpu_zmmh0 + i, 32);
+ p += 32;
+ }
+ for (size_t i = 0; i < 16; ++i) {
+ memcpy(p, &m_state.context.fpu.avx512f.__fpu_zmm16 + i, 64);
+ p += 64;
+ }
+ }
+ } else {
+ // Copy the XMM registers in a single block
+ memcpy(p, &m_state.context.fpu.no_avx.__fpu_xmm0, 16 * 16);
+ p += 16 * 16;
+ }
+
+ // Copy the exception registers
+ memcpy(p, &m_state.context.exc, sizeof(EXC));
+ p += sizeof(EXC);
+
+ // make sure we end up with exactly what we think we should have
+ size_t bytes_written = p - (uint8_t *)buf;
+ UNUSED_IF_ASSERT_DISABLED(bytes_written);
+ assert(bytes_written == size);
+ }
+ }
+
+ DNBLogThreadedIf(
+ LOG_THREAD,
+ "DNBArchImplX86_64::GetRegisterContext (buf = %p, len = %llu) => %u", buf,
+ (uint64_t)buf_len, size);
+ // Return the size of the register context even if NULL was passed in
+ return size;
+}
+
+nub_size_t DNBArchImplX86_64::SetRegisterContext(const void *buf,
+ nub_size_t buf_len) {
+ uint32_t size = GetRegisterContextSize();
+ if (buf == NULL || buf_len == 0)
+ size = 0;
+
+ if (size) {
+ if (size > buf_len)
+ size = static_cast<uint32_t>(buf_len);
+
+ const uint8_t *p = (const uint8_t *)buf;
+ // Copy the GPR registers
+ memcpy(&m_state.context.gpr, p, sizeof(GPR));
+ p += sizeof(GPR);
+
+ // Copy fcw through mxcsrmask as there is no padding
+ memcpy(&m_state.context.fpu.no_avx.__fpu_fcw, p, 5);
+ // We wrote 5 bytes, but we skip 6 to account for __fpu_rsrv1
+ // to match the g_fpu_registers_* tables.
+ p += 6;
+ memcpy(&m_state.context.fpu.no_avx.__fpu_fop, p, 8);
+ p += 8;
+ memcpy(&m_state.context.fpu.no_avx.__fpu_dp, p, 6);
+ p += 6;
+ memcpy(&m_state.context.fpu.no_avx.__fpu_mxcsr, p, 8);
+ p += 8;
+
+ // Work around the padding between the stmm registers as they are 16
+ // byte structs with 10 bytes of the value in each
+ for (size_t i = 0; i < 8; ++i) {
+ memcpy(&m_state.context.fpu.no_avx.__fpu_stmm0 + i, p, 10);
+ p += 10;
+ }
+
+ if(CPUHasAVX512f() || FORCE_AVX_REGS) {
+ for (size_t i = 0; i < 8; ++i) {
+ memcpy(&m_state.context.fpu.avx512f.__fpu_k0 + i, p, 8);
+ p += 8;
+ }
+ }
+
+ if (CPUHasAVX() || FORCE_AVX_REGS) {
+ // Interleave the XMM and YMMH registers to make the YMM registers
+ for (size_t i = 0; i < 16; ++i) {
+ memcpy(&m_state.context.fpu.avx.__fpu_xmm0 + i, p, 16);
+ p += 16;
+ memcpy(&m_state.context.fpu.avx.__fpu_ymmh0 + i, p, 16);
+ p += 16;
+ }
+ if(CPUHasAVX512f() || FORCE_AVX_REGS) {
+ for (size_t i = 0; i < 16; ++i) {
+ memcpy(&m_state.context.fpu.avx512f.__fpu_zmmh0 + i, p, 32);
+ p += 32;
+ }
+ for (size_t i = 0; i < 16; ++i) {
+ memcpy(&m_state.context.fpu.avx512f.__fpu_zmm16 + i, p, 64);
+ p += 64;
+ }
+ }
+ } else {
+ // Copy the XMM registers in a single block
+ memcpy(&m_state.context.fpu.no_avx.__fpu_xmm0, p, 16 * 16);
+ p += 16 * 16;
+ }
+
+ // Copy the exception registers
+ memcpy(&m_state.context.exc, p, sizeof(EXC));
+ p += sizeof(EXC);
+
+ // make sure we end up with exactly what we think we should have
+ size_t bytes_written = p - (const uint8_t *)buf;
+ UNUSED_IF_ASSERT_DISABLED(bytes_written);
+ assert(bytes_written == size);
+
+ kern_return_t kret;
+ if ((kret = SetGPRState()) != KERN_SUCCESS)
+ DNBLogThreadedIf(LOG_THREAD, "DNBArchImplX86_64::SetRegisterContext (buf "
+ "= %p, len = %llu) error: GPR regs failed "
+ "to write: %u",
+ buf, (uint64_t)buf_len, kret);
+ if ((kret = SetFPUState()) != KERN_SUCCESS)
+ DNBLogThreadedIf(
+ LOG_THREAD, "DNBArchImplX86_64::SetRegisterContext (buf = %p, len = "
+ "%llu) error: %s regs failed to write: %u",
+ buf, (uint64_t)buf_len, CPUHasAVX() ? "AVX" : "FPU", kret);
+ if ((kret = SetEXCState()) != KERN_SUCCESS)
+ DNBLogThreadedIf(LOG_THREAD, "DNBArchImplX86_64::SetRegisterContext (buf "
+ "= %p, len = %llu) error: EXP regs failed "
+ "to write: %u",
+ buf, (uint64_t)buf_len, kret);
+ }
+ DNBLogThreadedIf(
+ LOG_THREAD,
+ "DNBArchImplX86_64::SetRegisterContext (buf = %p, len = %llu) => %llu",
+ buf, (uint64_t)buf_len, (uint64_t)size);
+ return size;
+}
+
+uint32_t DNBArchImplX86_64::SaveRegisterState() {
+ kern_return_t kret = ::thread_abort_safely(m_thread->MachPortNumber());
+ DNBLogThreadedIf(
+ LOG_THREAD, "thread = 0x%4.4x calling thread_abort_safely (tid) => %u "
+ "(SetGPRState() for stop_count = %u)",
+ m_thread->MachPortNumber(), kret, m_thread->Process()->StopCount());
+
+ // Always re-read the registers because above we call thread_abort_safely();
+ bool force = true;
+
+ if ((kret = GetGPRState(force)) != KERN_SUCCESS) {
+ DNBLogThreadedIf(LOG_THREAD, "DNBArchImplX86_64::SaveRegisterState () "
+ "error: GPR regs failed to read: %u ",
+ kret);
+ } else if ((kret = GetFPUState(force)) != KERN_SUCCESS) {
+ DNBLogThreadedIf(LOG_THREAD, "DNBArchImplX86_64::SaveRegisterState () "
+ "error: %s regs failed to read: %u",
+ CPUHasAVX() ? "AVX" : "FPU", kret);
+ } else {
+ const uint32_t save_id = GetNextRegisterStateSaveID();
+ m_saved_register_states[save_id] = m_state.context;
+ return save_id;
+ }
+ return 0;
+}
+bool DNBArchImplX86_64::RestoreRegisterState(uint32_t save_id) {
+ SaveRegisterStates::iterator pos = m_saved_register_states.find(save_id);
+ if (pos != m_saved_register_states.end()) {
+ m_state.context.gpr = pos->second.gpr;
+ m_state.context.fpu = pos->second.fpu;
+ m_state.SetError(e_regSetGPR, Read, 0);
+ m_state.SetError(e_regSetFPU, Read, 0);
+ kern_return_t kret;
+ bool success = true;
+ if ((kret = SetGPRState()) != KERN_SUCCESS) {
+ DNBLogThreadedIf(LOG_THREAD, "DNBArchImplX86_64::RestoreRegisterState "
+ "(save_id = %u) error: GPR regs failed to "
+ "write: %u",
+ save_id, kret);
+ success = false;
+ } else if ((kret = SetFPUState()) != KERN_SUCCESS) {
+ DNBLogThreadedIf(LOG_THREAD, "DNBArchImplX86_64::RestoreRegisterState "
+ "(save_id = %u) error: %s regs failed to "
+ "write: %u",
+ save_id, CPUHasAVX() ? "AVX" : "FPU", kret);
+ success = false;
+ }
+ m_saved_register_states.erase(pos);
+ return success;
+ }
+ return false;
+}
+
+kern_return_t DNBArchImplX86_64::GetRegisterState(int set, bool force) {
+ switch (set) {
+ case e_regSetALL:
+ return GetGPRState(force) | GetFPUState(force) | GetEXCState(force);
+ case e_regSetGPR:
+ return GetGPRState(force);
+ case e_regSetFPU:
+ return GetFPUState(force);
+ case e_regSetEXC:
+ return GetEXCState(force);
+ default:
+ break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+kern_return_t DNBArchImplX86_64::SetRegisterState(int set) {
+ // Make sure we have a valid context to set.
+ if (RegisterSetStateIsValid(set)) {
+ switch (set) {
+ case e_regSetALL:
+ return SetGPRState() | SetFPUState() | SetEXCState();
+ case e_regSetGPR:
+ return SetGPRState();
+ case e_regSetFPU:
+ return SetFPUState();
+ case e_regSetEXC:
+ return SetEXCState();
+ default:
+ break;
+ }
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+bool DNBArchImplX86_64::RegisterSetStateIsValid(int set) const {
+ return m_state.RegsAreValid(set);
+}
+
+#endif // #if defined (__i386__) || defined (__x86_64__)
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h
new file mode 100644
index 00000000000..62ce37d4c04
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.h
@@ -0,0 +1,241 @@
+//===-- DNBArchImplX86_64.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/25/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __DNBArchImplX86_64_h__
+#define __DNBArchImplX86_64_h__
+
+#if defined(__i386__) || defined(__x86_64__)
+#include "DNBArch.h"
+#include "MachRegisterStatesX86_64.h"
+
+#include <map>
+
+class MachThread;
+
+class DNBArchImplX86_64 : public DNBArchProtocol {
+public:
+ DNBArchImplX86_64(MachThread *thread)
+ : DNBArchProtocol(), m_thread(thread), m_state(), m_2pc_dbg_checkpoint(),
+ m_2pc_trans_state(Trans_Done), m_saved_register_states() {}
+ virtual ~DNBArchImplX86_64() {}
+
+ static void Initialize();
+
+ virtual bool GetRegisterValue(uint32_t set, uint32_t reg,
+ DNBRegisterValue *value);
+ virtual bool SetRegisterValue(uint32_t set, uint32_t reg,
+ const DNBRegisterValue *value);
+ virtual nub_size_t GetRegisterContext(void *buf, nub_size_t buf_len);
+ virtual nub_size_t SetRegisterContext(const void *buf, nub_size_t buf_len);
+ virtual uint32_t SaveRegisterState();
+ virtual bool RestoreRegisterState(uint32_t save_id);
+
+ virtual kern_return_t GetRegisterState(int set, bool force);
+ virtual kern_return_t SetRegisterState(int set);
+ virtual bool RegisterSetStateIsValid(int set) const;
+
+ virtual uint64_t GetPC(uint64_t failValue); // Get program counter
+ virtual kern_return_t SetPC(uint64_t value);
+ virtual uint64_t GetSP(uint64_t failValue); // Get stack pointer
+ virtual void ThreadWillResume();
+ virtual bool ThreadDidStop();
+ virtual bool NotifyException(MachException::Data &exc);
+
+ virtual uint32_t NumSupportedHardwareWatchpoints();
+ virtual uint32_t EnableHardwareWatchpoint(nub_addr_t addr, nub_size_t size,
+ bool read, bool write,
+ bool also_set_on_task);
+ virtual bool DisableHardwareWatchpoint(uint32_t hw_break_index,
+ bool also_set_on_task);
+ virtual uint32_t GetHardwareWatchpointHit(nub_addr_t &addr);
+
+protected:
+ kern_return_t EnableHardwareSingleStep(bool enable);
+
+ typedef __x86_64_thread_state_t GPR;
+ typedef __x86_64_float_state_t FPU;
+ typedef __x86_64_exception_state_t EXC;
+ typedef __x86_64_avx_state_t AVX;
+ typedef __x86_64_debug_state_t DBG;
+
+ static const DNBRegisterInfo g_gpr_registers[];
+ static const DNBRegisterInfo g_fpu_registers_no_avx[];
+ static const DNBRegisterInfo g_fpu_registers_avx[];
+ static const DNBRegisterInfo g_exc_registers[];
+ static const DNBRegisterSetInfo g_reg_sets_no_avx[];
+ static const DNBRegisterSetInfo g_reg_sets_avx[];
+ static const size_t k_num_gpr_registers;
+ static const size_t k_num_fpu_registers_no_avx;
+ static const size_t k_num_fpu_registers_avx;
+ static const size_t k_num_exc_registers;
+ static const size_t k_num_all_registers_no_avx;
+ static const size_t k_num_all_registers_avx;
+ static const size_t k_num_register_sets;
+
+ typedef __x86_64_avx512f_state_t AVX512F;
+ static const DNBRegisterInfo g_fpu_registers_avx512f[];
+ static const DNBRegisterSetInfo g_reg_sets_avx512f[];
+ static const size_t k_num_fpu_registers_avx512f;
+ static const size_t k_num_all_registers_avx512f;
+
+ enum RegisterSet {
+ e_regSetALL = REGISTER_SET_ALL,
+ e_regSetGPR,
+ e_regSetFPU,
+ e_regSetEXC,
+ e_regSetDBG,
+ kNumRegisterSets
+ };
+
+ enum RegisterSetWordSize {
+ e_regSetWordSizeGPR = sizeof(GPR) / sizeof(int),
+ e_regSetWordSizeFPU = sizeof(FPU) / sizeof(int),
+ e_regSetWordSizeEXC = sizeof(EXC) / sizeof(int),
+ e_regSetWordSizeAVX = sizeof(AVX) / sizeof(int),
+ e_regSetWordSizeAVX512f = sizeof(AVX512F) / sizeof(int),
+ e_regSetWordSizeDBG = sizeof(DBG) / sizeof(int)
+ };
+
+ enum { Read = 0, Write = 1, kNumErrors = 2 };
+
+ struct Context {
+ GPR gpr;
+ union {
+ FPU no_avx;
+ AVX avx;
+ AVX512F avx512f;
+ } fpu;
+ EXC exc;
+ DBG dbg;
+ };
+
+ struct State {
+ Context context;
+ kern_return_t gpr_errs[2]; // Read/Write errors
+ kern_return_t fpu_errs[2]; // Read/Write errors
+ kern_return_t exc_errs[2]; // Read/Write errors
+ kern_return_t dbg_errs[2]; // Read/Write errors
+
+ State() {
+ uint32_t i;
+ for (i = 0; i < kNumErrors; i++) {
+ gpr_errs[i] = -1;
+ fpu_errs[i] = -1;
+ exc_errs[i] = -1;
+ dbg_errs[i] = -1;
+ }
+ }
+
+ void InvalidateAllRegisterStates() { SetError(e_regSetALL, Read, -1); }
+
+ kern_return_t GetError(int flavor, uint32_t err_idx) const {
+ if (err_idx < kNumErrors) {
+ switch (flavor) {
+ // When getting all errors, just OR all values together to see if
+ // we got any kind of error.
+ case e_regSetALL:
+ return gpr_errs[err_idx] | fpu_errs[err_idx] | exc_errs[err_idx];
+ case e_regSetGPR:
+ return gpr_errs[err_idx];
+ case e_regSetFPU:
+ return fpu_errs[err_idx];
+ case e_regSetEXC:
+ return exc_errs[err_idx];
+ case e_regSetDBG:
+ return dbg_errs[err_idx];
+ default:
+ break;
+ }
+ }
+ return -1;
+ }
+
+ bool SetError(int flavor, uint32_t err_idx, kern_return_t err) {
+ if (err_idx < kNumErrors) {
+ switch (flavor) {
+ case e_regSetALL:
+ gpr_errs[err_idx] = fpu_errs[err_idx] = exc_errs[err_idx] =
+ dbg_errs[err_idx] = err;
+ return true;
+
+ case e_regSetGPR:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case e_regSetFPU:
+ fpu_errs[err_idx] = err;
+ return true;
+
+ case e_regSetEXC:
+ exc_errs[err_idx] = err;
+ return true;
+
+ case e_regSetDBG:
+ dbg_errs[err_idx] = err;
+ return true;
+
+ default:
+ break;
+ }
+ }
+ return false;
+ }
+
+ bool RegsAreValid(int flavor) const {
+ return GetError(flavor, Read) == KERN_SUCCESS;
+ }
+ };
+
+ kern_return_t GetGPRState(bool force);
+ kern_return_t GetFPUState(bool force);
+ kern_return_t GetEXCState(bool force);
+ kern_return_t GetDBGState(bool force);
+
+ kern_return_t SetGPRState();
+ kern_return_t SetFPUState();
+ kern_return_t SetEXCState();
+ kern_return_t SetDBGState(bool also_set_on_task);
+
+ static DNBArchProtocol *Create(MachThread *thread);
+
+ static const uint8_t *SoftwareBreakpointOpcode(nub_size_t byte_size);
+
+ static const DNBRegisterSetInfo *GetRegisterSetInfo(nub_size_t *num_reg_sets);
+
+ static uint32_t GetRegisterContextSize();
+
+ // Helper functions for watchpoint manipulations.
+ static void SetWatchpoint(DBG &debug_state, uint32_t hw_index,
+ nub_addr_t addr, nub_size_t size, bool read,
+ bool write);
+ static void ClearWatchpoint(DBG &debug_state, uint32_t hw_index);
+ static bool IsWatchpointVacant(const DBG &debug_state, uint32_t hw_index);
+ static void ClearWatchpointHits(DBG &debug_state);
+ static bool IsWatchpointHit(const DBG &debug_state, uint32_t hw_index);
+ static nub_addr_t GetWatchAddress(const DBG &debug_state, uint32_t hw_index);
+
+ virtual bool StartTransForHWP();
+ virtual bool RollbackTransForHWP();
+ virtual bool FinishTransForHWP();
+ DBG GetDBGCheckpoint();
+
+ MachThread *m_thread;
+ State m_state;
+ DBG m_2pc_dbg_checkpoint;
+ uint32_t m_2pc_trans_state; // Is transaction of DBG state change: Pedning
+ // (0), Done (1), or Rolled Back (2)?
+ typedef std::map<uint32_t, Context> SaveRegisterStates;
+ SaveRegisterStates m_saved_register_states;
+};
+
+#endif // #if defined (__i386__) || defined (__x86_64__)
+#endif // #ifndef __DNBArchImplX86_64_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/MacOSX/x86_64/MachRegisterStatesX86_64.h b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/x86_64/MachRegisterStatesX86_64.h
new file mode 100644
index 00000000000..81839666837
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/MacOSX/x86_64/MachRegisterStatesX86_64.h
@@ -0,0 +1,313 @@
+//===-- MachRegisterStatesX86_64.h --------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Sean Callanan on 3/16/11.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachRegisterStatesX86_64_h__
+#define __MachRegisterStatesX86_64_h__
+
+#include <inttypes.h>
+
+#define __x86_64_THREAD_STATE 4
+#define __x86_64_FLOAT_STATE 5
+#define __x86_64_EXCEPTION_STATE 6
+#define __x86_64_DEBUG_STATE 11
+#define __x86_64_AVX_STATE 17
+#define __x86_64_AVX512F_STATE 20
+
+typedef struct {
+ uint64_t __rax;
+ uint64_t __rbx;
+ uint64_t __rcx;
+ uint64_t __rdx;
+ uint64_t __rdi;
+ uint64_t __rsi;
+ uint64_t __rbp;
+ uint64_t __rsp;
+ uint64_t __r8;
+ uint64_t __r9;
+ uint64_t __r10;
+ uint64_t __r11;
+ uint64_t __r12;
+ uint64_t __r13;
+ uint64_t __r14;
+ uint64_t __r15;
+ uint64_t __rip;
+ uint64_t __rflags;
+ uint64_t __cs;
+ uint64_t __fs;
+ uint64_t __gs;
+} __x86_64_thread_state_t;
+
+typedef struct {
+ uint16_t __invalid : 1;
+ uint16_t __denorm : 1;
+ uint16_t __zdiv : 1;
+ uint16_t __ovrfl : 1;
+ uint16_t __undfl : 1;
+ uint16_t __precis : 1;
+ uint16_t __PAD1 : 2;
+ uint16_t __pc : 2;
+ uint16_t __rc : 2;
+ uint16_t __PAD2 : 1;
+ uint16_t __PAD3 : 3;
+} __x86_64_fp_control_t;
+
+typedef struct {
+ uint16_t __invalid : 1;
+ uint16_t __denorm : 1;
+ uint16_t __zdiv : 1;
+ uint16_t __ovrfl : 1;
+ uint16_t __undfl : 1;
+ uint16_t __precis : 1;
+ uint16_t __stkflt : 1;
+ uint16_t __errsumm : 1;
+ uint16_t __c0 : 1;
+ uint16_t __c1 : 1;
+ uint16_t __c2 : 1;
+ uint16_t __tos : 3;
+ uint16_t __c3 : 1;
+ uint16_t __busy : 1;
+} __x86_64_fp_status_t;
+
+typedef struct {
+ uint8_t __mmst_reg[10];
+ uint8_t __mmst_rsrv[6];
+} __x86_64_mmst_reg;
+
+typedef struct { uint8_t __xmm_reg[16]; } __x86_64_xmm_reg;
+
+typedef struct {
+ uint32_t __fpu_reserved[2];
+ __x86_64_fp_control_t __fpu_fcw;
+ __x86_64_fp_status_t __fpu_fsw;
+ uint8_t __fpu_ftw;
+ uint8_t __fpu_rsrv1;
+ uint16_t __fpu_fop;
+ uint32_t __fpu_ip;
+ uint16_t __fpu_cs;
+ uint16_t __fpu_rsrv2;
+ uint32_t __fpu_dp;
+ uint16_t __fpu_ds;
+ uint16_t __fpu_rsrv3;
+ uint32_t __fpu_mxcsr;
+ uint32_t __fpu_mxcsrmask;
+ __x86_64_mmst_reg __fpu_stmm0;
+ __x86_64_mmst_reg __fpu_stmm1;
+ __x86_64_mmst_reg __fpu_stmm2;
+ __x86_64_mmst_reg __fpu_stmm3;
+ __x86_64_mmst_reg __fpu_stmm4;
+ __x86_64_mmst_reg __fpu_stmm5;
+ __x86_64_mmst_reg __fpu_stmm6;
+ __x86_64_mmst_reg __fpu_stmm7;
+ __x86_64_xmm_reg __fpu_xmm0;
+ __x86_64_xmm_reg __fpu_xmm1;
+ __x86_64_xmm_reg __fpu_xmm2;
+ __x86_64_xmm_reg __fpu_xmm3;
+ __x86_64_xmm_reg __fpu_xmm4;
+ __x86_64_xmm_reg __fpu_xmm5;
+ __x86_64_xmm_reg __fpu_xmm6;
+ __x86_64_xmm_reg __fpu_xmm7;
+ __x86_64_xmm_reg __fpu_xmm8;
+ __x86_64_xmm_reg __fpu_xmm9;
+ __x86_64_xmm_reg __fpu_xmm10;
+ __x86_64_xmm_reg __fpu_xmm11;
+ __x86_64_xmm_reg __fpu_xmm12;
+ __x86_64_xmm_reg __fpu_xmm13;
+ __x86_64_xmm_reg __fpu_xmm14;
+ __x86_64_xmm_reg __fpu_xmm15;
+ uint8_t __fpu_rsrv4[6 * 16];
+ uint32_t __fpu_reserved1;
+} __x86_64_float_state_t;
+
+typedef struct {
+ uint32_t __fpu_reserved[2];
+ __x86_64_fp_control_t __fpu_fcw;
+ __x86_64_fp_status_t __fpu_fsw;
+ uint8_t __fpu_ftw;
+ uint8_t __fpu_rsrv1;
+ uint16_t __fpu_fop;
+ uint32_t __fpu_ip;
+ uint16_t __fpu_cs;
+ uint16_t __fpu_rsrv2;
+ uint32_t __fpu_dp;
+ uint16_t __fpu_ds;
+ uint16_t __fpu_rsrv3;
+ uint32_t __fpu_mxcsr;
+ uint32_t __fpu_mxcsrmask;
+ __x86_64_mmst_reg __fpu_stmm0;
+ __x86_64_mmst_reg __fpu_stmm1;
+ __x86_64_mmst_reg __fpu_stmm2;
+ __x86_64_mmst_reg __fpu_stmm3;
+ __x86_64_mmst_reg __fpu_stmm4;
+ __x86_64_mmst_reg __fpu_stmm5;
+ __x86_64_mmst_reg __fpu_stmm6;
+ __x86_64_mmst_reg __fpu_stmm7;
+ __x86_64_xmm_reg __fpu_xmm0;
+ __x86_64_xmm_reg __fpu_xmm1;
+ __x86_64_xmm_reg __fpu_xmm2;
+ __x86_64_xmm_reg __fpu_xmm3;
+ __x86_64_xmm_reg __fpu_xmm4;
+ __x86_64_xmm_reg __fpu_xmm5;
+ __x86_64_xmm_reg __fpu_xmm6;
+ __x86_64_xmm_reg __fpu_xmm7;
+ __x86_64_xmm_reg __fpu_xmm8;
+ __x86_64_xmm_reg __fpu_xmm9;
+ __x86_64_xmm_reg __fpu_xmm10;
+ __x86_64_xmm_reg __fpu_xmm11;
+ __x86_64_xmm_reg __fpu_xmm12;
+ __x86_64_xmm_reg __fpu_xmm13;
+ __x86_64_xmm_reg __fpu_xmm14;
+ __x86_64_xmm_reg __fpu_xmm15;
+ uint8_t __fpu_rsrv4[6 * 16];
+ uint32_t __fpu_reserved1;
+ uint8_t __avx_reserved1[64];
+ __x86_64_xmm_reg __fpu_ymmh0;
+ __x86_64_xmm_reg __fpu_ymmh1;
+ __x86_64_xmm_reg __fpu_ymmh2;
+ __x86_64_xmm_reg __fpu_ymmh3;
+ __x86_64_xmm_reg __fpu_ymmh4;
+ __x86_64_xmm_reg __fpu_ymmh5;
+ __x86_64_xmm_reg __fpu_ymmh6;
+ __x86_64_xmm_reg __fpu_ymmh7;
+ __x86_64_xmm_reg __fpu_ymmh8;
+ __x86_64_xmm_reg __fpu_ymmh9;
+ __x86_64_xmm_reg __fpu_ymmh10;
+ __x86_64_xmm_reg __fpu_ymmh11;
+ __x86_64_xmm_reg __fpu_ymmh12;
+ __x86_64_xmm_reg __fpu_ymmh13;
+ __x86_64_xmm_reg __fpu_ymmh14;
+ __x86_64_xmm_reg __fpu_ymmh15;
+} __x86_64_avx_state_t;
+
+typedef struct { uint8_t __ymm_reg[32]; } __x86_64_ymm_reg;
+typedef struct { uint8_t __zmm_reg[64]; } __x86_64_zmm_reg;
+typedef struct { uint8_t __opmask_reg[8]; } __x86_64_opmask_reg;
+
+typedef struct {
+ uint32_t __fpu_reserved[2];
+ __x86_64_fp_control_t __fpu_fcw;
+ __x86_64_fp_status_t __fpu_fsw;
+ uint8_t __fpu_ftw;
+ uint8_t __fpu_rsrv1;
+ uint16_t __fpu_fop;
+ uint32_t __fpu_ip;
+ uint16_t __fpu_cs;
+ uint16_t __fpu_rsrv2;
+ uint32_t __fpu_dp;
+ uint16_t __fpu_ds;
+ uint16_t __fpu_rsrv3;
+ uint32_t __fpu_mxcsr;
+ uint32_t __fpu_mxcsrmask;
+ __x86_64_mmst_reg __fpu_stmm0;
+ __x86_64_mmst_reg __fpu_stmm1;
+ __x86_64_mmst_reg __fpu_stmm2;
+ __x86_64_mmst_reg __fpu_stmm3;
+ __x86_64_mmst_reg __fpu_stmm4;
+ __x86_64_mmst_reg __fpu_stmm5;
+ __x86_64_mmst_reg __fpu_stmm6;
+ __x86_64_mmst_reg __fpu_stmm7;
+ __x86_64_xmm_reg __fpu_xmm0;
+ __x86_64_xmm_reg __fpu_xmm1;
+ __x86_64_xmm_reg __fpu_xmm2;
+ __x86_64_xmm_reg __fpu_xmm3;
+ __x86_64_xmm_reg __fpu_xmm4;
+ __x86_64_xmm_reg __fpu_xmm5;
+ __x86_64_xmm_reg __fpu_xmm6;
+ __x86_64_xmm_reg __fpu_xmm7;
+ __x86_64_xmm_reg __fpu_xmm8;
+ __x86_64_xmm_reg __fpu_xmm9;
+ __x86_64_xmm_reg __fpu_xmm10;
+ __x86_64_xmm_reg __fpu_xmm11;
+ __x86_64_xmm_reg __fpu_xmm12;
+ __x86_64_xmm_reg __fpu_xmm13;
+ __x86_64_xmm_reg __fpu_xmm14;
+ __x86_64_xmm_reg __fpu_xmm15;
+ uint8_t __fpu_rsrv4[6 * 16];
+ uint32_t __fpu_reserved1;
+ uint8_t __avx_reserved1[64];
+ __x86_64_xmm_reg __fpu_ymmh0;
+ __x86_64_xmm_reg __fpu_ymmh1;
+ __x86_64_xmm_reg __fpu_ymmh2;
+ __x86_64_xmm_reg __fpu_ymmh3;
+ __x86_64_xmm_reg __fpu_ymmh4;
+ __x86_64_xmm_reg __fpu_ymmh5;
+ __x86_64_xmm_reg __fpu_ymmh6;
+ __x86_64_xmm_reg __fpu_ymmh7;
+ __x86_64_xmm_reg __fpu_ymmh8;
+ __x86_64_xmm_reg __fpu_ymmh9;
+ __x86_64_xmm_reg __fpu_ymmh10;
+ __x86_64_xmm_reg __fpu_ymmh11;
+ __x86_64_xmm_reg __fpu_ymmh12;
+ __x86_64_xmm_reg __fpu_ymmh13;
+ __x86_64_xmm_reg __fpu_ymmh14;
+ __x86_64_xmm_reg __fpu_ymmh15;
+ __x86_64_opmask_reg __fpu_k0;
+ __x86_64_opmask_reg __fpu_k1;
+ __x86_64_opmask_reg __fpu_k2;
+ __x86_64_opmask_reg __fpu_k3;
+ __x86_64_opmask_reg __fpu_k4;
+ __x86_64_opmask_reg __fpu_k5;
+ __x86_64_opmask_reg __fpu_k6;
+ __x86_64_opmask_reg __fpu_k7;
+ __x86_64_ymm_reg __fpu_zmmh0;
+ __x86_64_ymm_reg __fpu_zmmh1;
+ __x86_64_ymm_reg __fpu_zmmh2;
+ __x86_64_ymm_reg __fpu_zmmh3;
+ __x86_64_ymm_reg __fpu_zmmh4;
+ __x86_64_ymm_reg __fpu_zmmh5;
+ __x86_64_ymm_reg __fpu_zmmh6;
+ __x86_64_ymm_reg __fpu_zmmh7;
+ __x86_64_ymm_reg __fpu_zmmh8;
+ __x86_64_ymm_reg __fpu_zmmh9;
+ __x86_64_ymm_reg __fpu_zmmh10;
+ __x86_64_ymm_reg __fpu_zmmh11;
+ __x86_64_ymm_reg __fpu_zmmh12;
+ __x86_64_ymm_reg __fpu_zmmh13;
+ __x86_64_ymm_reg __fpu_zmmh14;
+ __x86_64_ymm_reg __fpu_zmmh15;
+ __x86_64_zmm_reg __fpu_zmm16;
+ __x86_64_zmm_reg __fpu_zmm17;
+ __x86_64_zmm_reg __fpu_zmm18;
+ __x86_64_zmm_reg __fpu_zmm19;
+ __x86_64_zmm_reg __fpu_zmm20;
+ __x86_64_zmm_reg __fpu_zmm21;
+ __x86_64_zmm_reg __fpu_zmm22;
+ __x86_64_zmm_reg __fpu_zmm23;
+ __x86_64_zmm_reg __fpu_zmm24;
+ __x86_64_zmm_reg __fpu_zmm25;
+ __x86_64_zmm_reg __fpu_zmm26;
+ __x86_64_zmm_reg __fpu_zmm27;
+ __x86_64_zmm_reg __fpu_zmm28;
+ __x86_64_zmm_reg __fpu_zmm29;
+ __x86_64_zmm_reg __fpu_zmm30;
+ __x86_64_zmm_reg __fpu_zmm31;
+
+} __x86_64_avx512f_state_t;
+
+typedef struct {
+ uint32_t __trapno;
+ uint32_t __err;
+ uint64_t __faultvaddr;
+} __x86_64_exception_state_t;
+
+typedef struct {
+ uint64_t __dr0;
+ uint64_t __dr1;
+ uint64_t __dr2;
+ uint64_t __dr3;
+ uint64_t __dr4;
+ uint64_t __dr5;
+ uint64_t __dr6;
+ uint64_t __dr7;
+} __x86_64_debug_state_t;
+
+#endif
diff --git a/gnu/llvm/lldb/tools/debugserver/source/PThreadCondition.h b/gnu/llvm/lldb/tools/debugserver/source/PThreadCondition.h
new file mode 100644
index 00000000000..2f9060da028
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/PThreadCondition.h
@@ -0,0 +1,34 @@
+//===-- PThreadCondition.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/16/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __PThreadCondition_h__
+#define __PThreadCondition_h__
+
+#include <pthread.h>
+
+class PThreadCondition {
+public:
+ PThreadCondition() { ::pthread_cond_init(&m_condition, NULL); }
+
+ ~PThreadCondition() { ::pthread_cond_destroy(&m_condition); }
+
+ pthread_cond_t *Condition() { return &m_condition; }
+
+ int Broadcast() { return ::pthread_cond_broadcast(&m_condition); }
+
+ int Signal() { return ::pthread_cond_signal(&m_condition); }
+
+protected:
+ pthread_cond_t m_condition;
+};
+
+#endif
diff --git a/gnu/llvm/lldb/tools/debugserver/source/PThreadEvent.cpp b/gnu/llvm/lldb/tools/debugserver/source/PThreadEvent.cpp
new file mode 100644
index 00000000000..82d1d227867
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/PThreadEvent.cpp
@@ -0,0 +1,195 @@
+//===-- PThreadEvent.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/16/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "PThreadEvent.h"
+#include "DNBLog.h"
+#include "errno.h"
+
+PThreadEvent::PThreadEvent(uint32_t bits, uint32_t validBits)
+ : m_mutex(), m_set_condition(), m_reset_condition(), m_bits(bits),
+ m_validBits(validBits), m_reset_ack_mask(0) {
+ // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, 0x%8.8x)",
+ // this, __FUNCTION__, bits, validBits);
+}
+
+PThreadEvent::~PThreadEvent() {
+ // DNBLogThreadedIf(LOG_EVENTS, "%p %s", this, LLVM_PRETTY_FUNCTION);
+}
+
+uint32_t PThreadEvent::NewEventBit() {
+ // DNBLogThreadedIf(LOG_EVENTS, "%p %s", this, LLVM_PRETTY_FUNCTION);
+ PTHREAD_MUTEX_LOCKER(locker, m_mutex);
+ uint32_t mask = 1;
+ while (mask & m_validBits)
+ mask <<= 1;
+ m_validBits |= mask;
+ return mask;
+}
+
+void PThreadEvent::FreeEventBits(const uint32_t mask) {
+ // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this,
+ // __FUNCTION__, mask);
+ if (mask) {
+ PTHREAD_MUTEX_LOCKER(locker, m_mutex);
+ m_bits &= ~mask;
+ m_validBits &= ~mask;
+ }
+}
+
+uint32_t PThreadEvent::GetEventBits() const {
+ // DNBLogThreadedIf(LOG_EVENTS, "%p %s", this, LLVM_PRETTY_FUNCTION);
+ PTHREAD_MUTEX_LOCKER(locker, m_mutex);
+ uint32_t bits = m_bits;
+ return bits;
+}
+
+// Replace the event bits with a new bitmask value
+void PThreadEvent::ReplaceEventBits(const uint32_t bits) {
+ // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this,
+ // __FUNCTION__, bits);
+ PTHREAD_MUTEX_LOCKER(locker, m_mutex);
+ // Make sure we have some bits and that they aren't already set...
+ if (m_bits != bits) {
+ // Figure out which bits are changing
+ uint32_t changed_bits = m_bits ^ bits;
+ // Set the new bit values
+ m_bits = bits;
+ // If any new bits are set, then broadcast
+ if (changed_bits & m_bits)
+ m_set_condition.Broadcast();
+ }
+}
+
+// Set one or more event bits and broadcast if any new event bits get set
+// that weren't already set.
+
+void PThreadEvent::SetEvents(const uint32_t mask) {
+ // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this,
+ // __FUNCTION__, mask);
+ // Make sure we have some bits to set
+ if (mask) {
+ PTHREAD_MUTEX_LOCKER(locker, m_mutex);
+ // Save the old event bit state so we can tell if things change
+ uint32_t old = m_bits;
+ // Set the all event bits that are set in 'mask'
+ m_bits |= mask;
+ // Broadcast only if any extra bits got set.
+ if (old != m_bits)
+ m_set_condition.Broadcast();
+ }
+}
+
+// Reset one or more event bits
+void PThreadEvent::ResetEvents(const uint32_t mask) {
+ // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this,
+ // __FUNCTION__, mask);
+ if (mask) {
+ PTHREAD_MUTEX_LOCKER(locker, m_mutex);
+
+ // Save the old event bit state so we can tell if things change
+ uint32_t old = m_bits;
+ // Clear the all event bits that are set in 'mask'
+ m_bits &= ~mask;
+ // Broadcast only if any extra bits got reset.
+ if (old != m_bits)
+ m_reset_condition.Broadcast();
+ }
+}
+
+// Wait until 'timeout_abstime' for any events that are set in
+// 'mask'. If 'timeout_abstime' is NULL, then wait forever.
+uint32_t
+PThreadEvent::WaitForSetEvents(const uint32_t mask,
+ const struct timespec *timeout_abstime) const {
+ // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this,
+ // __FUNCTION__, mask, timeout_abstime);
+ int err = 0;
+ // pthread_cond_timedwait() or pthread_cond_wait() will atomically
+ // unlock the mutex and wait for the condition to be set. When either
+ // function returns, they will re-lock the mutex. We use an auto lock/unlock
+ // class (PThreadMutex::Locker) to allow us to return at any point in this
+ // function and not have to worry about unlocking the mutex.
+ PTHREAD_MUTEX_LOCKER(locker, m_mutex);
+ do {
+ // Check our predicate (event bits) in case any are already set
+ if (mask & m_bits) {
+ uint32_t bits_set = mask & m_bits;
+ // Our PThreadMutex::Locker will automatically unlock our mutex
+ return bits_set;
+ }
+ if (timeout_abstime) {
+ // Wait for condition to get broadcast, or for a timeout. If we get
+ // a timeout we will drop out of the do loop and return false which
+ // is what we want.
+ err = ::pthread_cond_timedwait(m_set_condition.Condition(),
+ m_mutex.Mutex(), timeout_abstime);
+ // Retest our predicate in case of a race condition right at the end
+ // of the timeout.
+ if (err == ETIMEDOUT) {
+ uint32_t bits_set = mask & m_bits;
+ return bits_set;
+ }
+ } else {
+ // Wait for condition to get broadcast. The only error this function
+ // should return is if
+ err = ::pthread_cond_wait(m_set_condition.Condition(), m_mutex.Mutex());
+ }
+ } while (err == 0);
+ return 0;
+}
+
+// Wait until 'timeout_abstime' for any events in 'mask' to reset.
+// If 'timeout_abstime' is NULL, then wait forever.
+uint32_t PThreadEvent::WaitForEventsToReset(
+ const uint32_t mask, const struct timespec *timeout_abstime) const {
+ // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this,
+ // __FUNCTION__, mask, timeout_abstime);
+ int err = 0;
+ // pthread_cond_timedwait() or pthread_cond_wait() will atomically
+ // unlock the mutex and wait for the condition to be set. When either
+ // function returns, they will re-lock the mutex. We use an auto lock/unlock
+ // class (PThreadMutex::Locker) to allow us to return at any point in this
+ // function and not have to worry about unlocking the mutex.
+ PTHREAD_MUTEX_LOCKER(locker, m_mutex);
+ do {
+ // Check our predicate (event bits) each time through this do loop
+ if ((mask & m_bits) == 0) {
+ // All the bits requested have been reset, return zero indicating
+ // which bits from the mask were still set (none of them)
+ return 0;
+ }
+ if (timeout_abstime) {
+ // Wait for condition to get broadcast, or for a timeout. If we get
+ // a timeout we will drop out of the do loop and return false which
+ // is what we want.
+ err = ::pthread_cond_timedwait(m_reset_condition.Condition(),
+ m_mutex.Mutex(), timeout_abstime);
+ } else {
+ // Wait for condition to get broadcast. The only error this function
+ // should return is if
+ err = ::pthread_cond_wait(m_reset_condition.Condition(), m_mutex.Mutex());
+ }
+ } while (err == 0);
+ // Return a mask indicating which bits (if any) were still set
+ return mask & m_bits;
+}
+
+uint32_t
+PThreadEvent::WaitForResetAck(const uint32_t mask,
+ const struct timespec *timeout_abstime) const {
+ if (mask & m_reset_ack_mask) {
+ // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this,
+ // __FUNCTION__, mask, timeout_abstime);
+ return WaitForEventsToReset(mask & m_reset_ack_mask, timeout_abstime);
+ }
+ return 0;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/PThreadEvent.h b/gnu/llvm/lldb/tools/debugserver/source/PThreadEvent.h
new file mode 100644
index 00000000000..f81798de5c7
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/PThreadEvent.h
@@ -0,0 +1,61 @@
+//===-- PThreadEvent.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/16/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __PThreadEvent_h__
+#define __PThreadEvent_h__
+#include "PThreadCondition.h"
+#include "PThreadMutex.h"
+#include <stdint.h>
+#include <time.h>
+
+class PThreadEvent {
+public:
+ PThreadEvent(uint32_t bits = 0, uint32_t validBits = 0);
+ ~PThreadEvent();
+
+ uint32_t NewEventBit();
+ void FreeEventBits(const uint32_t mask);
+
+ void ReplaceEventBits(const uint32_t bits);
+ uint32_t GetEventBits() const;
+ void SetEvents(const uint32_t mask);
+ void ResetEvents(const uint32_t mask);
+ // Wait for events to be set or reset. These functions take an optional
+ // timeout value. If timeout is NULL an infinite timeout will be used.
+ uint32_t
+ WaitForSetEvents(const uint32_t mask,
+ const struct timespec *timeout_abstime = NULL) const;
+ uint32_t
+ WaitForEventsToReset(const uint32_t mask,
+ const struct timespec *timeout_abstime = NULL) const;
+
+ uint32_t GetResetAckMask() const { return m_reset_ack_mask; }
+ uint32_t SetResetAckMask(uint32_t mask) { return m_reset_ack_mask = mask; }
+ uint32_t WaitForResetAck(const uint32_t mask,
+ const struct timespec *timeout_abstime = NULL) const;
+
+protected:
+ // pthread condition and mutex variable to control access and allow
+ // blocking between the main thread and the spotlight index thread.
+ mutable PThreadMutex m_mutex;
+ mutable PThreadCondition m_set_condition;
+ mutable PThreadCondition m_reset_condition;
+ uint32_t m_bits;
+ uint32_t m_validBits;
+ uint32_t m_reset_ack_mask;
+
+private:
+ PThreadEvent(const PThreadEvent &) = delete;
+ PThreadEvent &operator=(const PThreadEvent &rhs) = delete;
+};
+
+#endif // #ifndef __PThreadEvent_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/PThreadMutex.cpp b/gnu/llvm/lldb/tools/debugserver/source/PThreadMutex.cpp
new file mode 100644
index 00000000000..118921aee5a
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/PThreadMutex.cpp
@@ -0,0 +1,66 @@
+//===-- PThreadMutex.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 12/9/08.
+//
+//===----------------------------------------------------------------------===//
+
+#include "PThreadMutex.h"
+
+#include "DNBTimer.h"
+
+#if defined(DEBUG_PTHREAD_MUTEX_DEADLOCKS)
+
+PThreadMutex::Locker::Locker(PThreadMutex &m, const char *function,
+ const char *file, const int line)
+ : m_pMutex(m.Mutex()), m_function(function), m_file(file), m_line(line),
+ m_lock_time(0) {
+ Lock();
+}
+
+PThreadMutex::Locker::Locker(PThreadMutex *m, const char *function,
+ const char *file, const int line)
+ : m_pMutex(m ? m->Mutex() : NULL), m_function(function), m_file(file),
+ m_line(line), m_lock_time(0) {
+ Lock();
+}
+
+PThreadMutex::Locker::Locker(pthread_mutex_t *mutex, const char *function,
+ const char *file, const int line)
+ : m_pMutex(mutex), m_function(function), m_file(file), m_line(line),
+ m_lock_time(0) {
+ Lock();
+}
+
+PThreadMutex::Locker::~Locker() { Unlock(); }
+
+void PThreadMutex::Locker::Lock() {
+ if (m_pMutex) {
+ m_lock_time = DNBTimer::GetTimeOfDay();
+ if (::pthread_mutex_trylock(m_pMutex) != 0) {
+ fprintf(stdout, "::pthread_mutex_trylock (%8.8p) mutex is locked "
+ "(function %s in %s:%i), waiting...\n",
+ m_pMutex, m_function, m_file, m_line);
+ ::pthread_mutex_lock(m_pMutex);
+ fprintf(stdout, "::pthread_mutex_lock (%8.8p) succeeded after %6llu "
+ "usecs (function %s in %s:%i)\n",
+ m_pMutex, DNBTimer::GetTimeOfDay() - m_lock_time, m_function,
+ m_file, m_line);
+ }
+ }
+}
+
+void PThreadMutex::Locker::Unlock() {
+ fprintf(stdout, "::pthread_mutex_unlock (%8.8p) had lock for %6llu usecs in "
+ "%s in %s:%i\n",
+ m_pMutex, DNBTimer::GetTimeOfDay() - m_lock_time, m_function, m_file,
+ m_line);
+ ::pthread_mutex_unlock(m_pMutex);
+}
+
+#endif
diff --git a/gnu/llvm/lldb/tools/debugserver/source/PThreadMutex.h b/gnu/llvm/lldb/tools/debugserver/source/PThreadMutex.h
new file mode 100644
index 00000000000..075fdc9114f
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/PThreadMutex.h
@@ -0,0 +1,119 @@
+//===-- PThreadMutex.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/16/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __PThreadMutex_h__
+#define __PThreadMutex_h__
+
+#include <assert.h>
+#include <pthread.h>
+#include <stdint.h>
+
+//#define DEBUG_PTHREAD_MUTEX_DEADLOCKS 1
+
+#if defined(DEBUG_PTHREAD_MUTEX_DEADLOCKS)
+#define PTHREAD_MUTEX_LOCKER(var, mutex) \
+ PThreadMutex::Locker var(mutex, __FUNCTION__, __FILE__, __LINE__)
+
+#else
+#define PTHREAD_MUTEX_LOCKER(var, mutex) PThreadMutex::Locker var(mutex)
+#endif
+
+class PThreadMutex {
+public:
+ class Locker {
+ public:
+#if defined(DEBUG_PTHREAD_MUTEX_DEADLOCKS)
+
+ Locker(PThreadMutex &m, const char *function, const char *file, int line);
+ Locker(PThreadMutex *m, const char *function, const char *file, int line);
+ Locker(pthread_mutex_t *mutex, const char *function, const char *file,
+ int line);
+ ~Locker();
+ void Lock();
+ void Unlock();
+
+#else
+ Locker(PThreadMutex &m) : m_pMutex(m.Mutex()) { Lock(); }
+
+ Locker(PThreadMutex *m) : m_pMutex(m ? m->Mutex() : NULL) { Lock(); }
+
+ Locker(pthread_mutex_t *mutex) : m_pMutex(mutex) { Lock(); }
+
+ void Lock() {
+ if (m_pMutex)
+ ::pthread_mutex_lock(m_pMutex);
+ }
+
+ void Unlock() {
+ if (m_pMutex)
+ ::pthread_mutex_unlock(m_pMutex);
+ }
+
+ ~Locker() { Unlock(); }
+
+#endif
+
+ // unlock any the current mutex and lock the new one if it is valid
+ void Reset(pthread_mutex_t *pMutex = NULL) {
+ Unlock();
+ m_pMutex = pMutex;
+ Lock();
+ }
+ pthread_mutex_t *m_pMutex;
+#if defined(DEBUG_PTHREAD_MUTEX_DEADLOCKS)
+ const char *m_function;
+ const char *m_file;
+ int m_line;
+ uint64_t m_lock_time;
+#endif
+ };
+
+ PThreadMutex() {
+ int err;
+ err = ::pthread_mutex_init(&m_mutex, NULL);
+ assert(err == 0);
+ }
+
+ PThreadMutex(int type) {
+ int err;
+ ::pthread_mutexattr_t attr;
+ err = ::pthread_mutexattr_init(&attr);
+ assert(err == 0);
+ err = ::pthread_mutexattr_settype(&attr, type);
+ assert(err == 0);
+ err = ::pthread_mutex_init(&m_mutex, &attr);
+ assert(err == 0);
+ err = ::pthread_mutexattr_destroy(&attr);
+ assert(err == 0);
+ }
+
+ ~PThreadMutex() {
+ int err;
+ err = ::pthread_mutex_destroy(&m_mutex);
+ if (err != 0) {
+ err = Unlock();
+ if (err == 0)
+ ::pthread_mutex_destroy(&m_mutex);
+ }
+ }
+
+ pthread_mutex_t *Mutex() { return &m_mutex; }
+
+ int Lock() { return ::pthread_mutex_lock(&m_mutex); }
+
+ int Unlock() { return ::pthread_mutex_unlock(&m_mutex); }
+
+protected:
+ pthread_mutex_t m_mutex;
+};
+
+#endif
diff --git a/gnu/llvm/lldb/tools/debugserver/source/PseudoTerminal.cpp b/gnu/llvm/lldb/tools/debugserver/source/PseudoTerminal.cpp
new file mode 100644
index 00000000000..cac8bda6248
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/PseudoTerminal.cpp
@@ -0,0 +1,179 @@
+//===-- PseudoTerminal.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/8/08.
+//
+//===----------------------------------------------------------------------===//
+
+#include "PseudoTerminal.h"
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+// PseudoTerminal constructor
+PseudoTerminal::PseudoTerminal()
+ : m_master_fd(invalid_fd), m_slave_fd(invalid_fd) {}
+
+// Destructor
+// The master and slave file descriptors will get closed if they are
+// valid. Call the ReleaseMasterFD()/ReleaseSlaveFD() member functions
+// to release any file descriptors that are needed beyond the lifespan
+// of this object.
+PseudoTerminal::~PseudoTerminal() {
+ CloseMaster();
+ CloseSlave();
+}
+
+// Close the master file descriptor if it is valid.
+void PseudoTerminal::CloseMaster() {
+ if (m_master_fd > 0) {
+ ::close(m_master_fd);
+ m_master_fd = invalid_fd;
+ }
+}
+
+// Close the slave file descriptor if it is valid.
+void PseudoTerminal::CloseSlave() {
+ if (m_slave_fd > 0) {
+ ::close(m_slave_fd);
+ m_slave_fd = invalid_fd;
+ }
+}
+
+// Open the first available pseudo terminal with OFLAG as the
+// permissions. The file descriptor is store in the m_master_fd member
+// variable and can be accessed via the MasterFD() or ReleaseMasterFD()
+// accessors.
+//
+// Suggested value for oflag is O_RDWR|O_NOCTTY
+//
+// RETURNS:
+// Zero when successful, non-zero indicating an error occurred.
+PseudoTerminal::Status PseudoTerminal::OpenFirstAvailableMaster(int oflag) {
+ // Open the master side of a pseudo terminal
+ m_master_fd = ::posix_openpt(oflag);
+ if (m_master_fd < 0) {
+ return err_posix_openpt_failed;
+ }
+
+ // Grant access to the slave pseudo terminal
+ if (::grantpt(m_master_fd) < 0) {
+ CloseMaster();
+ return err_grantpt_failed;
+ }
+
+ // Clear the lock flag on the slave pseudo terminal
+ if (::unlockpt(m_master_fd) < 0) {
+ CloseMaster();
+ return err_unlockpt_failed;
+ }
+
+ return success;
+}
+
+// Open the slave pseudo terminal for the current master pseudo
+// terminal. A master pseudo terminal should already be valid prior to
+// calling this function (see PseudoTerminal::OpenFirstAvailableMaster()).
+// The file descriptor is stored in the m_slave_fd member variable and
+// can be accessed via the SlaveFD() or ReleaseSlaveFD() accessors.
+//
+// RETURNS:
+// Zero when successful, non-zero indicating an error occurred.
+PseudoTerminal::Status PseudoTerminal::OpenSlave(int oflag) {
+ CloseSlave();
+
+ // Open the master side of a pseudo terminal
+ const char *slave_name = SlaveName();
+
+ if (slave_name == NULL)
+ return err_ptsname_failed;
+
+ m_slave_fd = ::open(slave_name, oflag);
+
+ if (m_slave_fd < 0)
+ return err_open_slave_failed;
+
+ return success;
+}
+
+// Get the name of the slave pseudo terminal. A master pseudo terminal
+// should already be valid prior to calling this function (see
+// PseudoTerminal::OpenFirstAvailableMaster()).
+//
+// RETURNS:
+// NULL if no valid master pseudo terminal or if ptsname() fails.
+// The name of the slave pseudo terminal as a NULL terminated C string
+// that comes from static memory, so a copy of the string should be
+// made as subsequent calls can change this value.
+const char *PseudoTerminal::SlaveName() const {
+ if (m_master_fd < 0)
+ return NULL;
+ return ::ptsname(m_master_fd);
+}
+
+// Fork a child process that and have its stdio routed to a pseudo
+// terminal.
+//
+// In the parent process when a valid pid is returned, the master file
+// descriptor can be used as a read/write access to stdio of the
+// child process.
+//
+// In the child process the stdin/stdout/stderr will already be routed
+// to the slave pseudo terminal and the master file descriptor will be
+// closed as it is no longer needed by the child process.
+//
+// This class will close the file descriptors for the master/slave
+// when the destructor is called, so be sure to call ReleaseMasterFD()
+// or ReleaseSlaveFD() if any file descriptors are going to be used
+// past the lifespan of this object.
+//
+// RETURNS:
+// in the parent process: the pid of the child, or -1 if fork fails
+// in the child process: zero
+
+pid_t PseudoTerminal::Fork(PseudoTerminal::Status &error) {
+ pid_t pid = invalid_pid;
+ error = OpenFirstAvailableMaster(O_RDWR | O_NOCTTY);
+
+ if (error == 0) {
+ // Successfully opened our master pseudo terminal
+
+ pid = ::fork();
+ if (pid < 0) {
+ // Fork failed
+ error = err_fork_failed;
+ } else if (pid == 0) {
+ // Child Process
+ ::setsid();
+
+ error = OpenSlave(O_RDWR);
+ if (error == 0) {
+ // Successfully opened slave
+ // We are done with the master in the child process so lets close it
+ CloseMaster();
+
+#if defined(TIOCSCTTY)
+ // Acquire the controlling terminal
+ if (::ioctl(m_slave_fd, TIOCSCTTY, (char *)0) < 0)
+ error = err_failed_to_acquire_controlling_terminal;
+#endif
+ // Duplicate all stdio file descriptors to the slave pseudo terminal
+ if (::dup2(m_slave_fd, STDIN_FILENO) != STDIN_FILENO)
+ error = error ? error : err_dup2_failed_on_stdin;
+ if (::dup2(m_slave_fd, STDOUT_FILENO) != STDOUT_FILENO)
+ error = error ? error : err_dup2_failed_on_stdout;
+ if (::dup2(m_slave_fd, STDERR_FILENO) != STDERR_FILENO)
+ error = error ? error : err_dup2_failed_on_stderr;
+ }
+ } else {
+ // Parent Process
+ // Do nothing and let the pid get returned!
+ }
+ }
+ return pid;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/PseudoTerminal.h b/gnu/llvm/lldb/tools/debugserver/source/PseudoTerminal.h
new file mode 100644
index 00000000000..e3324c1c358
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/PseudoTerminal.h
@@ -0,0 +1,79 @@
+//===-- PseudoTerminal.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 1/8/08.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __PseudoTerminal_h__
+#define __PseudoTerminal_h__
+
+#include <fcntl.h>
+#include <string>
+#include <termios.h>
+
+class PseudoTerminal {
+public:
+ enum { invalid_fd = -1, invalid_pid = -1 };
+
+ enum Status {
+ success = 0,
+ err_posix_openpt_failed = -2,
+ err_grantpt_failed = -3,
+ err_unlockpt_failed = -4,
+ err_ptsname_failed = -5,
+ err_open_slave_failed = -6,
+ err_fork_failed = -7,
+ err_setsid_failed = -8,
+ err_failed_to_acquire_controlling_terminal = -9,
+ err_dup2_failed_on_stdin = -10,
+ err_dup2_failed_on_stdout = -11,
+ err_dup2_failed_on_stderr = -12
+ };
+ // Constructors and Destructors
+ PseudoTerminal();
+ ~PseudoTerminal();
+
+ void CloseMaster();
+ void CloseSlave();
+ Status OpenFirstAvailableMaster(int oflag);
+ Status OpenSlave(int oflag);
+ int MasterFD() const { return m_master_fd; }
+ int SlaveFD() const { return m_slave_fd; }
+ int ReleaseMasterFD() {
+ // Release ownership of the master pseudo terminal file
+ // descriptor without closing it. (the destructor for this
+ // class will close it otherwise!)
+ int fd = m_master_fd;
+ m_master_fd = invalid_fd;
+ return fd;
+ }
+ int ReleaseSlaveFD() {
+ // Release ownership of the slave pseudo terminal file
+ // descriptor without closing it (the destructor for this
+ // class will close it otherwise!)
+ int fd = m_slave_fd;
+ m_slave_fd = invalid_fd;
+ return fd;
+ }
+
+ const char *SlaveName() const;
+
+ pid_t Fork(Status &error);
+
+protected:
+ // Classes that inherit from PseudoTerminal can see and modify these
+ int m_master_fd;
+ int m_slave_fd;
+
+private:
+ PseudoTerminal(const PseudoTerminal &rhs) = delete;
+ PseudoTerminal &operator=(const PseudoTerminal &rhs) = delete;
+};
+
+#endif // #ifndef __PseudoTerminal_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/RNBContext.cpp b/gnu/llvm/lldb/tools/debugserver/source/RNBContext.cpp
new file mode 100644
index 00000000000..3f1a37a1175
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/RNBContext.cpp
@@ -0,0 +1,290 @@
+//===-- RNBContext.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 12/12/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RNBContext.h"
+
+#include <sstream>
+#include <sys/stat.h>
+
+#if defined(__APPLE__)
+#include <pthread.h>
+#include <sched.h>
+#endif
+
+#include "CFString.h"
+#include "DNB.h"
+#include "DNBLog.h"
+#include "RNBRemote.h"
+
+// Destructor
+RNBContext::~RNBContext() { SetProcessID(INVALID_NUB_PROCESS); }
+
+// RNBContext constructor
+
+const char *RNBContext::EnvironmentAtIndex(size_t index) {
+ if (index < m_env_vec.size())
+ return m_env_vec[index].c_str();
+ else
+ return NULL;
+}
+
+static std::string GetEnvironmentKey(const std::string &env) {
+ std::string key = env.substr(0, env.find('='));
+ if (!key.empty() && key.back() == '=')
+ key.pop_back();
+ return key;
+}
+
+void RNBContext::PushEnvironmentIfNeeded(const char *arg) {
+ if (!arg)
+ return;
+ std::string arg_key = GetEnvironmentKey(arg);
+
+ for (const std::string &entry: m_env_vec) {
+ if (arg_key == GetEnvironmentKey(entry))
+ return;
+ }
+ m_env_vec.push_back(arg);
+}
+
+const char *RNBContext::ArgumentAtIndex(size_t index) {
+ if (index < m_arg_vec.size())
+ return m_arg_vec[index].c_str();
+ else
+ return NULL;
+}
+
+bool RNBContext::SetWorkingDirectory(const char *path) {
+ struct stat working_directory_stat;
+ if (::stat(path, &working_directory_stat) != 0) {
+ m_working_directory.clear();
+ return false;
+ }
+ m_working_directory.assign(path);
+ return true;
+}
+
+void RNBContext::SetProcessID(nub_process_t pid) {
+ // Delete and events we created
+ if (m_pid != INVALID_NUB_PROCESS) {
+ StopProcessStatusThread();
+ // Unregister this context as a client of the process's events.
+ }
+ // Assign our new process ID
+ m_pid = pid;
+
+ if (pid != INVALID_NUB_PROCESS) {
+ StartProcessStatusThread();
+ }
+}
+
+void RNBContext::StartProcessStatusThread() {
+ DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s called", __FUNCTION__);
+ if ((m_events.GetEventBits() & event_proc_thread_running) == 0) {
+ int err = ::pthread_create(&m_pid_pthread, NULL,
+ ThreadFunctionProcessStatus, this);
+ if (err == 0) {
+ // Our thread was successfully kicked off, wait for it to
+ // set the started event so we can safely continue
+ m_events.WaitForSetEvents(event_proc_thread_running);
+ DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s thread got started!",
+ __FUNCTION__);
+ } else {
+ DNBLogThreadedIf(LOG_RNB_PROC,
+ "RNBContext::%s thread failed to start: err = %i",
+ __FUNCTION__, err);
+ m_events.ResetEvents(event_proc_thread_running);
+ m_events.SetEvents(event_proc_thread_exiting);
+ }
+ }
+}
+
+void RNBContext::StopProcessStatusThread() {
+ DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s called", __FUNCTION__);
+ if ((m_events.GetEventBits() & event_proc_thread_running) ==
+ event_proc_thread_running) {
+ struct timespec timeout_abstime;
+ DNBTimer::OffsetTimeOfDay(&timeout_abstime, 2, 0);
+ // Wait for 2 seconds for the rx thread to exit
+ if (m_events.WaitForSetEvents(RNBContext::event_proc_thread_exiting,
+ &timeout_abstime) ==
+ RNBContext::event_proc_thread_exiting) {
+ DNBLogThreadedIf(LOG_RNB_PROC,
+ "RNBContext::%s thread stopped as requeseted",
+ __FUNCTION__);
+ } else {
+ DNBLogThreadedIf(LOG_RNB_PROC,
+ "RNBContext::%s thread did not stop in 2 seconds...",
+ __FUNCTION__);
+ // Kill the RX thread???
+ }
+ }
+}
+
+// This thread's sole purpose is to watch for any status changes in the
+// child process.
+void *RNBContext::ThreadFunctionProcessStatus(void *arg) {
+ RNBRemoteSP remoteSP(g_remoteSP);
+ RNBRemote *remote = remoteSP.get();
+ if (remote == NULL)
+ return NULL;
+ RNBContext &ctx = remote->Context();
+
+ nub_process_t pid = ctx.ProcessID();
+ DNBLogThreadedIf(LOG_RNB_PROC,
+ "RNBContext::%s (arg=%p, pid=%4.4x): thread starting...",
+ __FUNCTION__, arg, pid);
+ ctx.Events().SetEvents(RNBContext::event_proc_thread_running);
+
+#if defined(__APPLE__)
+ pthread_setname_np("child process status watcher thread");
+#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
+ struct sched_param thread_param;
+ int thread_sched_policy;
+ if (pthread_getschedparam(pthread_self(), &thread_sched_policy,
+ &thread_param) == 0) {
+ thread_param.sched_priority = 47;
+ pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);
+ }
+#endif
+#endif
+
+ bool done = false;
+ while (!done) {
+ DNBLogThreadedIf(LOG_RNB_PROC,
+ "RNBContext::%s calling DNBProcessWaitForEvent(pid, "
+ "eEventProcessRunningStateChanged | "
+ "eEventProcessStoppedStateChanged | eEventStdioAvailable "
+ "| eEventProfileDataAvailable, true)...",
+ __FUNCTION__);
+ nub_event_t pid_status_event = DNBProcessWaitForEvents(
+ pid,
+ eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged |
+ eEventStdioAvailable | eEventProfileDataAvailable,
+ true, NULL);
+ DNBLogThreadedIf(LOG_RNB_PROC,
+ "RNBContext::%s calling DNBProcessWaitForEvent(pid, "
+ "eEventProcessRunningStateChanged | "
+ "eEventProcessStoppedStateChanged | eEventStdioAvailable "
+ "| eEventProfileDataAvailable, true) => 0x%8.8x",
+ __FUNCTION__, pid_status_event);
+
+ if (pid_status_event == 0) {
+ DNBLogThreadedIf(LOG_RNB_PROC, "RNBContext::%s (pid=%4.4x) got ZERO back "
+ "from DNBProcessWaitForEvent....",
+ __FUNCTION__, pid);
+ // done = true;
+ } else {
+ if (pid_status_event & eEventStdioAvailable) {
+ DNBLogThreadedIf(
+ LOG_RNB_PROC,
+ "RNBContext::%s (pid=%4.4x) got stdio available event....",
+ __FUNCTION__, pid);
+ ctx.Events().SetEvents(RNBContext::event_proc_stdio_available);
+ // Wait for the main thread to consume this notification if it requested
+ // we wait for it
+ ctx.Events().WaitForResetAck(RNBContext::event_proc_stdio_available);
+ }
+
+ if (pid_status_event & eEventProfileDataAvailable) {
+ DNBLogThreadedIf(
+ LOG_RNB_PROC,
+ "RNBContext::%s (pid=%4.4x) got profile data event....",
+ __FUNCTION__, pid);
+ ctx.Events().SetEvents(RNBContext::event_proc_profile_data);
+ // Wait for the main thread to consume this notification if it requested
+ // we wait for it
+ ctx.Events().WaitForResetAck(RNBContext::event_proc_profile_data);
+ }
+
+ if (pid_status_event & (eEventProcessRunningStateChanged |
+ eEventProcessStoppedStateChanged)) {
+ nub_state_t pid_state = DNBProcessGetState(pid);
+ DNBLogThreadedIf(
+ LOG_RNB_PROC,
+ "RNBContext::%s (pid=%4.4x) got process state change: %s",
+ __FUNCTION__, pid, DNBStateAsString(pid_state));
+
+ // Let the main thread know there is a process state change to see
+ ctx.Events().SetEvents(RNBContext::event_proc_state_changed);
+ // Wait for the main thread to consume this notification if it requested
+ // we wait for it
+ ctx.Events().WaitForResetAck(RNBContext::event_proc_state_changed);
+
+ switch (pid_state) {
+ case eStateStopped:
+ break;
+
+ case eStateInvalid:
+ case eStateExited:
+ case eStateDetached:
+ done = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Reset any events that we consumed.
+ DNBProcessResetEvents(pid, pid_status_event);
+ }
+ }
+ DNBLogThreadedIf(LOG_RNB_PROC,
+ "RNBContext::%s (arg=%p, pid=%4.4x): thread exiting...",
+ __FUNCTION__, arg, pid);
+ ctx.Events().ResetEvents(event_proc_thread_running);
+ ctx.Events().SetEvents(event_proc_thread_exiting);
+ return NULL;
+}
+
+const char *RNBContext::EventsAsString(nub_event_t events, std::string &s) {
+ s.clear();
+ if (events & event_proc_state_changed)
+ s += "proc_state_changed ";
+ if (events & event_proc_thread_running)
+ s += "proc_thread_running ";
+ if (events & event_proc_thread_exiting)
+ s += "proc_thread_exiting ";
+ if (events & event_proc_stdio_available)
+ s += "proc_stdio_available ";
+ if (events & event_proc_profile_data)
+ s += "proc_profile_data ";
+ if (events & event_darwin_log_data_available)
+ s += "darwin_log_data_available ";
+ if (events & event_read_packet_available)
+ s += "read_packet_available ";
+ if (events & event_read_thread_running)
+ s += "read_thread_running ";
+ if (events & event_read_thread_running)
+ s += "read_thread_running ";
+ return s.c_str();
+}
+
+const char *RNBContext::LaunchStatusAsString(std::string &s) {
+ s.clear();
+
+ const char *err_str = m_launch_status.AsString();
+ if (err_str)
+ s = err_str;
+ else {
+ char error_num_str[64];
+ snprintf(error_num_str, sizeof(error_num_str), "%u",
+ m_launch_status.Status());
+ s = error_num_str;
+ }
+ return s.c_str();
+}
+
+bool RNBContext::ProcessStateRunning() const {
+ nub_state_t pid_state = DNBProcessGetState(m_pid);
+ return pid_state == eStateRunning || pid_state == eStateStepping;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/RNBContext.h b/gnu/llvm/lldb/tools/debugserver/source/RNBContext.h
new file mode 100644
index 00000000000..946dfb2eb10
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/RNBContext.h
@@ -0,0 +1,156 @@
+//===-- RNBContext.h --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 12/12/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __RNBContext_h__
+#define __RNBContext_h__
+
+#include "DNBError.h"
+#include "PThreadEvent.h"
+#include "RNBDefs.h"
+#include <string>
+#include <vector>
+
+class RNBContext {
+public:
+ enum {
+ event_proc_state_changed = 0x001,
+ event_proc_thread_running = 0x002, // Sticky
+ event_proc_thread_exiting = 0x004,
+ event_proc_stdio_available = 0x008,
+ event_proc_profile_data = 0x010,
+ event_read_packet_available = 0x020,
+ event_read_thread_running = 0x040, // Sticky
+ event_read_thread_exiting = 0x080,
+ event_darwin_log_data_available = 0x100,
+
+ normal_event_bits = event_proc_state_changed | event_proc_thread_exiting |
+ event_proc_stdio_available | event_proc_profile_data |
+ event_read_packet_available |
+ event_read_thread_exiting |
+ event_darwin_log_data_available,
+
+ sticky_event_bits = event_proc_thread_running | event_read_thread_running,
+
+ all_event_bits = sticky_event_bits | normal_event_bits
+ } event_t;
+ // Constructors and Destructors
+ RNBContext()
+ : m_pid(INVALID_NUB_PROCESS), m_pid_stop_count(0),
+ m_events(0, all_event_bits), m_pid_pthread(), m_launch_status(),
+ m_arg_vec(), m_env_vec(), m_detach_on_error(false) {}
+
+ virtual ~RNBContext();
+
+ nub_process_t ProcessID() const { return m_pid; }
+ bool HasValidProcessID() const { return m_pid != INVALID_NUB_PROCESS; }
+ void SetProcessID(nub_process_t pid);
+ nub_size_t GetProcessStopCount() const { return m_pid_stop_count; }
+ bool SetProcessStopCount(nub_size_t count) {
+ // Returns true if this class' notion of the PID state changed
+ if (m_pid_stop_count == count)
+ return false; // Didn't change
+ m_pid_stop_count = count;
+ return true; // The stop count has changed.
+ }
+
+ bool ProcessStateRunning() const;
+ PThreadEvent &Events() { return m_events; }
+ nub_event_t AllEventBits() const { return all_event_bits; }
+ nub_event_t NormalEventBits() const { return normal_event_bits; }
+ nub_event_t StickyEventBits() const { return sticky_event_bits; }
+ const char *EventsAsString(nub_event_t events, std::string &s);
+
+ size_t ArgumentCount() const { return m_arg_vec.size(); }
+ const char *ArgumentAtIndex(size_t index);
+ void PushArgument(const char *arg) {
+ if (arg)
+ m_arg_vec.push_back(arg);
+ }
+ void ClearArgv() { m_arg_vec.erase(m_arg_vec.begin(), m_arg_vec.end()); }
+
+ size_t EnvironmentCount() const { return m_env_vec.size(); }
+ const char *EnvironmentAtIndex(size_t index);
+ void PushEnvironment(const char *arg) {
+ if (arg)
+ m_env_vec.push_back(arg);
+ }
+ void PushEnvironmentIfNeeded(const char *arg);
+ void ClearEnvironment() {
+ m_env_vec.erase(m_env_vec.begin(), m_env_vec.end());
+ }
+ DNBError &LaunchStatus() { return m_launch_status; }
+ const char *LaunchStatusAsString(std::string &s);
+ nub_launch_flavor_t LaunchFlavor() const { return m_launch_flavor; }
+ void SetLaunchFlavor(nub_launch_flavor_t flavor) { m_launch_flavor = flavor; }
+
+ const char *GetWorkingDirectory() const {
+ if (!m_working_directory.empty())
+ return m_working_directory.c_str();
+ return NULL;
+ }
+
+ bool SetWorkingDirectory(const char *path);
+
+ std::string &GetSTDIN() { return m_stdin; }
+ std::string &GetSTDOUT() { return m_stdout; }
+ std::string &GetSTDERR() { return m_stderr; }
+ std::string &GetWorkingDir() { return m_working_dir; }
+
+ const char *GetSTDINPath() {
+ return m_stdin.empty() ? NULL : m_stdin.c_str();
+ }
+ const char *GetSTDOUTPath() {
+ return m_stdout.empty() ? NULL : m_stdout.c_str();
+ }
+ const char *GetSTDERRPath() {
+ return m_stderr.empty() ? NULL : m_stderr.c_str();
+ }
+ const char *GetWorkingDirPath() {
+ return m_working_dir.empty() ? NULL : m_working_dir.c_str();
+ }
+
+ void PushProcessEvent(const char *p) { m_process_event.assign(p); }
+ const char *GetProcessEvent() { return m_process_event.c_str(); }
+
+ void SetDetachOnError(bool detach) { m_detach_on_error = detach; }
+ bool GetDetachOnError() { return m_detach_on_error; }
+
+protected:
+ // Classes that inherit from RNBContext can see and modify these
+ nub_process_t m_pid;
+ std::string m_stdin;
+ std::string m_stdout;
+ std::string m_stderr;
+ std::string m_working_dir;
+ nub_size_t m_pid_stop_count;
+ PThreadEvent m_events; // Threaded events that we can wait for
+ pthread_t m_pid_pthread;
+ nub_launch_flavor_t m_launch_flavor; // How to launch our inferior process
+ DNBError
+ m_launch_status; // This holds the status from the last launch attempt.
+ std::vector<std::string> m_arg_vec;
+ std::vector<std::string>
+ m_env_vec; // This will be unparsed - entries FOO=value
+ std::string m_working_directory;
+ std::string m_process_event;
+ bool m_detach_on_error;
+
+ void StartProcessStatusThread();
+ void StopProcessStatusThread();
+ static void *ThreadFunctionProcessStatus(void *arg);
+
+private:
+ RNBContext(const RNBContext &rhs) = delete;
+ RNBContext &operator=(const RNBContext &rhs) = delete;
+};
+
+#endif // #ifndef __RNBContext_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/RNBDefs.h b/gnu/llvm/lldb/tools/debugserver/source/RNBDefs.h
new file mode 100644
index 00000000000..4cc7c220b7f
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/RNBDefs.h
@@ -0,0 +1,98 @@
+//===-- RNBDefs.h -----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 12/14/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __RNBDefs_h__
+#define __RNBDefs_h__
+
+#include "DNBDefs.h"
+#include <memory>
+
+#define CONCAT2(a, b) a##b
+#define CONCAT(a, b) CONCAT2(a, b)
+#define STRINGIZE2(x) #x
+#define STRINGIZE(x) STRINGIZE2(x)
+
+#if !defined(DEBUGSERVER_PROGRAM_SYMBOL)
+#define DEBUGSERVER_PROGRAM_SYMBOL debugserver
+#endif
+
+#if !defined(DEBUGSERVER_PROGRAM_NAME)
+#define DEBUGSERVER_PROGRAM_NAME STRINGIZE(DEBUGSERVER_PROGRAM_SYMBOL)
+#endif
+
+#ifndef DEBUGSERVER_VERSION_NUM
+extern "C" const unsigned char CONCAT(DEBUGSERVER_PROGRAM_SYMBOL,
+ VersionString)[];
+#define DEBUGSERVER_VERSION_NUM \
+ CONCAT(DEBUGSERVER_PROGRAM_SYMBOL, VersionNumber)
+#endif
+
+#ifndef DEBUGSERVER_VERSION_STR
+extern "C" const double CONCAT(DEBUGSERVER_PROGRAM_SYMBOL, VersionNumber);
+#define DEBUGSERVER_VERSION_STR \
+ CONCAT(DEBUGSERVER_PROGRAM_SYMBOL, VersionString)
+#endif
+
+#if defined(__i386__)
+
+#define RNB_ARCH "i386"
+
+#elif defined(__x86_64__)
+
+#define RNB_ARCH "x86_64"
+
+#elif defined(__ppc64__)
+
+#define RNB_ARCH "ppc64"
+
+#elif defined(__powerpc__) || defined(__ppc__)
+
+#define RNB_ARCH "ppc"
+
+#elif defined(__arm64__) || defined(__aarch64__)
+
+#define RNB_ARCH "arm64"
+
+#elif defined(__arm__)
+
+#define RNB_ARCH "armv7"
+
+#else
+
+#error undefined architecture
+
+#endif
+
+class RNBRemote;
+typedef std::shared_ptr<RNBRemote> RNBRemoteSP;
+
+enum rnb_err_t { rnb_success = 0, rnb_err = 1, rnb_not_connected = 2 };
+
+// Log bits
+// reserve low bits for DNB
+#define LOG_RNB_MINIMAL \
+ ((LOG_LO_USER) << 0) // Minimal logging (min verbosity)
+#define LOG_RNB_MEDIUM \
+ ((LOG_LO_USER) << 1) // Medium logging (med verbosity)
+#define LOG_RNB_MAX ((LOG_LO_USER) << 2) // Max logging (max verbosity)
+#define LOG_RNB_COMM ((LOG_LO_USER) << 3) // Log communications (RNBSocket)
+#define LOG_RNB_REMOTE ((LOG_LO_USER) << 4) // Log remote (RNBRemote)
+#define LOG_RNB_EVENTS \
+ ((LOG_LO_USER) << 5) // Log events (PThreadEvents)
+#define LOG_RNB_PROC ((LOG_LO_USER) << 6) // Log process state (Process thread)
+#define LOG_RNB_PACKETS ((LOG_LO_USER) << 7) // Log gdb remote packets
+#define LOG_RNB_ALL (~((LOG_LO_USER)-1))
+#define LOG_RNB_DEFAULT (LOG_RNB_ALL)
+
+extern RNBRemoteSP g_remoteSP;
+
+#endif // #ifndef __RNBDefs_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/RNBRemote.cpp b/gnu/llvm/lldb/tools/debugserver/source/RNBRemote.cpp
new file mode 100644
index 00000000000..64e3bc49abc
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/RNBRemote.cpp
@@ -0,0 +1,6313 @@
+//===-- RNBRemote.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 12/12/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RNBRemote.h"
+
+#include <errno.h>
+#include <mach-o/loader.h>
+#include <mach/exception_types.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <unistd.h>
+
+#if defined(__APPLE__)
+#include <pthread.h>
+#include <sched.h>
+#endif
+
+#include "DNB.h"
+#include "DNBDataRef.h"
+#include "DNBLog.h"
+#include "DNBThreadResumeActions.h"
+#include "DarwinLogCollector.h"
+#include "DarwinLogEvent.h"
+#include "JSON.h"
+#include "JSONGenerator.h"
+#include "JSONGenerator.h"
+#include "MacOSX/Genealogy.h"
+#include "OsLogger.h"
+#include "RNBContext.h"
+#include "RNBServices.h"
+#include "RNBSocket.h"
+#include "StdStringExtractor.h"
+
+#include <compression.h>
+
+#include <TargetConditionals.h>
+#include <iomanip>
+#include <memory>
+#include <sstream>
+#include <unordered_set>
+
+// constants
+
+static const std::string OS_LOG_EVENTS_KEY_NAME("events");
+static const std::string JSON_ASYNC_TYPE_KEY_NAME("type");
+static const DarwinLogEventVector::size_type DARWIN_LOG_MAX_EVENTS_PER_PACKET =
+ 10;
+
+// std::iostream formatting macros
+#define RAW_HEXBASE std::setfill('0') << std::hex << std::right
+#define HEXBASE '0' << 'x' << RAW_HEXBASE
+#define RAWHEX8(x) RAW_HEXBASE << std::setw(2) << ((uint32_t)((uint8_t)x))
+#define RAWHEX16 RAW_HEXBASE << std::setw(4)
+#define RAWHEX32 RAW_HEXBASE << std::setw(8)
+#define RAWHEX64 RAW_HEXBASE << std::setw(16)
+#define HEX8(x) HEXBASE << std::setw(2) << ((uint32_t)(x))
+#define HEX16 HEXBASE << std::setw(4)
+#define HEX32 HEXBASE << std::setw(8)
+#define HEX64 HEXBASE << std::setw(16)
+#define RAW_HEX(x) RAW_HEXBASE << std::setw(sizeof(x) * 2) << (x)
+#define HEX(x) HEXBASE << std::setw(sizeof(x) * 2) << (x)
+#define RAWHEX_SIZE(x, sz) RAW_HEXBASE << std::setw((sz)) << (x)
+#define HEX_SIZE(x, sz) HEXBASE << std::setw((sz)) << (x)
+#define STRING_WIDTH(w) std::setfill(' ') << std::setw(w)
+#define LEFT_STRING_WIDTH(s, w) \
+ std::left << std::setfill(' ') << std::setw(w) << (s) << std::right
+#define DECIMAL std::dec << std::setfill(' ')
+#define DECIMAL_WIDTH(w) DECIMAL << std::setw(w)
+#define FLOAT(n, d) \
+ std::setfill(' ') << std::setw((n) + (d) + 1) << std::setprecision(d) \
+ << std::showpoint << std::fixed
+#define INDENT_WITH_SPACES(iword_idx) \
+ std::setfill(' ') << std::setw((iword_idx)) << ""
+#define INDENT_WITH_TABS(iword_idx) \
+ std::setfill('\t') << std::setw((iword_idx)) << ""
+// Class to handle communications via gdb remote protocol.
+
+// Prototypes
+
+static std::string binary_encode_string(const std::string &s);
+
+// Decode a single hex character and return the hex value as a number or
+// -1 if "ch" is not a hex character.
+static inline int xdigit_to_sint(char ch) {
+ if (ch >= 'a' && ch <= 'f')
+ return 10 + ch - 'a';
+ if (ch >= 'A' && ch <= 'F')
+ return 10 + ch - 'A';
+ if (ch >= '0' && ch <= '9')
+ return ch - '0';
+ return -1;
+}
+
+// Decode a single hex ASCII byte. Return -1 on failure, a value 0-255
+// on success.
+static inline int decoded_hex_ascii_char(const char *p) {
+ const int hi_nibble = xdigit_to_sint(p[0]);
+ if (hi_nibble == -1)
+ return -1;
+ const int lo_nibble = xdigit_to_sint(p[1]);
+ if (lo_nibble == -1)
+ return -1;
+ return (uint8_t)((hi_nibble << 4) + lo_nibble);
+}
+
+// Decode a hex ASCII string back into a string
+static std::string decode_hex_ascii_string(const char *p,
+ uint32_t max_length = UINT32_MAX) {
+ std::string arg;
+ if (p) {
+ for (const char *c = p; ((c - p) / 2) < max_length; c += 2) {
+ int ch = decoded_hex_ascii_char(c);
+ if (ch == -1)
+ break;
+ else
+ arg.push_back(ch);
+ }
+ }
+ return arg;
+}
+
+uint64_t decode_uint64(const char *p, int base, char **end = nullptr,
+ uint64_t fail_value = 0) {
+ nub_addr_t addr = strtoull(p, end, 16);
+ if (addr == 0 && errno != 0)
+ return fail_value;
+ return addr;
+}
+
+extern void ASLLogCallback(void *baton, uint32_t flags, const char *format,
+ va_list args);
+
+#if defined(__APPLE__) && \
+ (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101000)
+// from System.framework/Versions/B/PrivateHeaders/sys/codesign.h
+extern "C" {
+#define CS_OPS_STATUS 0 /* return status */
+#define CS_RESTRICT 0x0000800 /* tell dyld to treat restricted */
+int csops(pid_t pid, unsigned int ops, void *useraddr, size_t usersize);
+
+// from rootless.h
+bool rootless_allows_task_for_pid(pid_t pid);
+
+// from sys/csr.h
+typedef uint32_t csr_config_t;
+#define CSR_ALLOW_TASK_FOR_PID (1 << 2)
+int csr_check(csr_config_t mask);
+}
+#endif
+
+RNBRemote::RNBRemote()
+ : m_ctx(), m_comm(), m_arch(), m_continue_thread(-1), m_thread(-1),
+ m_mutex(), m_dispatch_queue_offsets(),
+ m_dispatch_queue_offsets_addr(INVALID_NUB_ADDRESS),
+ m_qSymbol_index(UINT32_MAX), m_packets_recvd(0), m_packets(),
+ m_rx_packets(), m_rx_partial_data(), m_rx_pthread(0),
+ m_max_payload_size(DEFAULT_GDB_REMOTE_PROTOCOL_BUFSIZE - 4),
+ m_extended_mode(false), m_noack_mode(false),
+ m_thread_suffix_supported(false), m_list_threads_in_stop_reply(false),
+ m_compression_minsize(384), m_enable_compression_next_send_packet(false),
+ m_compression_mode(compression_types::none) {
+ DNBLogThreadedIf(LOG_RNB_REMOTE, "%s", __PRETTY_FUNCTION__);
+ CreatePacketTable();
+}
+
+RNBRemote::~RNBRemote() {
+ DNBLogThreadedIf(LOG_RNB_REMOTE, "%s", __PRETTY_FUNCTION__);
+ StopReadRemoteDataThread();
+}
+
+void RNBRemote::CreatePacketTable() {
+ // Step required to add new packets:
+ // 1 - Add new enumeration to RNBRemote::PacketEnum
+ // 2 - Create the RNBRemote::HandlePacket_ function if a new function is
+ // needed
+ // 3 - Register the Packet definition with any needed callbacks in this
+ // function
+ // - If no response is needed for a command, then use NULL for the
+ // normal callback
+ // - If the packet is not supported while the target is running, use
+ // NULL for the async callback
+ // 4 - If the packet is a standard packet (starts with a '$' character
+ // followed by the payload and then '#' and checksum, then you are done
+ // else go on to step 5
+ // 5 - if the packet is a fixed length packet:
+ // - modify the switch statement for the first character in the payload
+ // in RNBRemote::CommDataReceived so it doesn't reject the new packet
+ // type as invalid
+ // - modify the switch statement for the first character in the payload
+ // in RNBRemote::GetPacketPayload and make sure the payload of the
+ // packet
+ // is returned correctly
+
+ std::vector<Packet> &t = m_packets;
+ t.push_back(Packet(ack, NULL, NULL, "+", "ACK"));
+ t.push_back(Packet(nack, NULL, NULL, "-", "!ACK"));
+ t.push_back(Packet(read_memory, &RNBRemote::HandlePacket_m, NULL, "m",
+ "Read memory"));
+ t.push_back(Packet(read_register, &RNBRemote::HandlePacket_p, NULL, "p",
+ "Read one register"));
+ t.push_back(Packet(read_general_regs, &RNBRemote::HandlePacket_g, NULL, "g",
+ "Read registers"));
+ t.push_back(Packet(write_memory, &RNBRemote::HandlePacket_M, NULL, "M",
+ "Write memory"));
+ t.push_back(Packet(write_register, &RNBRemote::HandlePacket_P, NULL, "P",
+ "Write one register"));
+ t.push_back(Packet(write_general_regs, &RNBRemote::HandlePacket_G, NULL, "G",
+ "Write registers"));
+ t.push_back(Packet(insert_mem_bp, &RNBRemote::HandlePacket_z, NULL, "Z0",
+ "Insert memory breakpoint"));
+ t.push_back(Packet(remove_mem_bp, &RNBRemote::HandlePacket_z, NULL, "z0",
+ "Remove memory breakpoint"));
+ t.push_back(Packet(single_step, &RNBRemote::HandlePacket_s, NULL, "s",
+ "Single step"));
+ t.push_back(Packet(cont, &RNBRemote::HandlePacket_c, NULL, "c", "continue"));
+ t.push_back(Packet(single_step_with_sig, &RNBRemote::HandlePacket_S, NULL,
+ "S", "Single step with signal"));
+ t.push_back(
+ Packet(set_thread, &RNBRemote::HandlePacket_H, NULL, "H", "Set thread"));
+ t.push_back(Packet(halt, &RNBRemote::HandlePacket_last_signal,
+ &RNBRemote::HandlePacket_stop_process, "\x03", "^C"));
+ // t.push_back (Packet (use_extended_mode,
+ // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "!", "Use extended mode"));
+ t.push_back(Packet(why_halted, &RNBRemote::HandlePacket_last_signal, NULL,
+ "?", "Why did target halt"));
+ t.push_back(
+ Packet(set_argv, &RNBRemote::HandlePacket_A, NULL, "A", "Set argv"));
+ // t.push_back (Packet (set_bp,
+ // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "B", "Set/clear
+ // breakpoint"));
+ t.push_back(Packet(continue_with_sig, &RNBRemote::HandlePacket_C, NULL, "C",
+ "Continue with signal"));
+ t.push_back(Packet(detach, &RNBRemote::HandlePacket_D, NULL, "D",
+ "Detach gdb from remote system"));
+ // t.push_back (Packet (step_inferior_one_cycle,
+ // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "i", "Step inferior by one
+ // clock cycle"));
+ // t.push_back (Packet (signal_and_step_inf_one_cycle,
+ // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "I", "Signal inferior, then
+ // step one clock cycle"));
+ t.push_back(Packet(kill, &RNBRemote::HandlePacket_k, NULL, "k", "Kill"));
+ // t.push_back (Packet (restart,
+ // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "R", "Restart inferior"));
+ // t.push_back (Packet (search_mem_backwards,
+ // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "t", "Search memory
+ // backwards"));
+ t.push_back(Packet(thread_alive_p, &RNBRemote::HandlePacket_T, NULL, "T",
+ "Is thread alive"));
+ t.push_back(Packet(query_supported_features,
+ &RNBRemote::HandlePacket_qSupported, NULL, "qSupported",
+ "Query about supported features"));
+ t.push_back(Packet(vattach, &RNBRemote::HandlePacket_v, NULL, "vAttach",
+ "Attach to a new process"));
+ t.push_back(Packet(vattachwait, &RNBRemote::HandlePacket_v, NULL,
+ "vAttachWait",
+ "Wait for a process to start up then attach to it"));
+ t.push_back(Packet(vattachorwait, &RNBRemote::HandlePacket_v, NULL,
+ "vAttachOrWait", "Attach to the process or if it doesn't "
+ "exist, wait for the process to start up "
+ "then attach to it"));
+ t.push_back(Packet(vattachname, &RNBRemote::HandlePacket_v, NULL,
+ "vAttachName", "Attach to an existing process by name"));
+ t.push_back(Packet(vcont_list_actions, &RNBRemote::HandlePacket_v, NULL,
+ "vCont;", "Verbose resume with thread actions"));
+ t.push_back(Packet(vcont_list_actions, &RNBRemote::HandlePacket_v, NULL,
+ "vCont?",
+ "List valid continue-with-thread-actions actions"));
+ t.push_back(Packet(read_data_from_memory, &RNBRemote::HandlePacket_x, NULL,
+ "x", "Read data from memory"));
+ t.push_back(Packet(write_data_to_memory, &RNBRemote::HandlePacket_X, NULL,
+ "X", "Write data to memory"));
+ // t.push_back (Packet (insert_hardware_bp,
+ // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "Z1", "Insert hardware
+ // breakpoint"));
+ // t.push_back (Packet (remove_hardware_bp,
+ // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "z1", "Remove hardware
+ // breakpoint"));
+ t.push_back(Packet(insert_write_watch_bp, &RNBRemote::HandlePacket_z, NULL,
+ "Z2", "Insert write watchpoint"));
+ t.push_back(Packet(remove_write_watch_bp, &RNBRemote::HandlePacket_z, NULL,
+ "z2", "Remove write watchpoint"));
+ t.push_back(Packet(insert_read_watch_bp, &RNBRemote::HandlePacket_z, NULL,
+ "Z3", "Insert read watchpoint"));
+ t.push_back(Packet(remove_read_watch_bp, &RNBRemote::HandlePacket_z, NULL,
+ "z3", "Remove read watchpoint"));
+ t.push_back(Packet(insert_access_watch_bp, &RNBRemote::HandlePacket_z, NULL,
+ "Z4", "Insert access watchpoint"));
+ t.push_back(Packet(remove_access_watch_bp, &RNBRemote::HandlePacket_z, NULL,
+ "z4", "Remove access watchpoint"));
+ t.push_back(Packet(query_monitor, &RNBRemote::HandlePacket_qRcmd, NULL,
+ "qRcmd", "Monitor command"));
+ t.push_back(Packet(query_current_thread_id, &RNBRemote::HandlePacket_qC, NULL,
+ "qC", "Query current thread ID"));
+ t.push_back(Packet(query_echo, &RNBRemote::HandlePacket_qEcho, NULL, "qEcho:",
+ "Echo the packet back to allow the debugger to sync up "
+ "with this server"));
+ t.push_back(Packet(query_get_pid, &RNBRemote::HandlePacket_qGetPid, NULL,
+ "qGetPid", "Query process id"));
+ t.push_back(Packet(query_thread_ids_first,
+ &RNBRemote::HandlePacket_qThreadInfo, NULL, "qfThreadInfo",
+ "Get list of active threads (first req)"));
+ t.push_back(Packet(query_thread_ids_subsequent,
+ &RNBRemote::HandlePacket_qThreadInfo, NULL, "qsThreadInfo",
+ "Get list of active threads (subsequent req)"));
+ // APPLE LOCAL: qThreadStopInfo
+ // syntax: qThreadStopInfoTTTT
+ // TTTT is hex thread ID
+ t.push_back(Packet(query_thread_stop_info,
+ &RNBRemote::HandlePacket_qThreadStopInfo, NULL,
+ "qThreadStopInfo",
+ "Get detailed info on why the specified thread stopped"));
+ t.push_back(Packet(query_thread_extra_info,
+ &RNBRemote::HandlePacket_qThreadExtraInfo, NULL,
+ "qThreadExtraInfo", "Get printable status of a thread"));
+ // t.push_back (Packet (query_image_offsets,
+ // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "qOffsets", "Report offset
+ // of loaded program"));
+ t.push_back(Packet(
+ query_launch_success, &RNBRemote::HandlePacket_qLaunchSuccess, NULL,
+ "qLaunchSuccess", "Report the success or failure of the launch attempt"));
+ t.push_back(
+ Packet(query_register_info, &RNBRemote::HandlePacket_qRegisterInfo, NULL,
+ "qRegisterInfo",
+ "Dynamically discover remote register context information."));
+ t.push_back(Packet(
+ query_shlib_notify_info_addr, &RNBRemote::HandlePacket_qShlibInfoAddr,
+ NULL, "qShlibInfoAddr", "Returns the address that contains info needed "
+ "for getting shared library notifications"));
+ t.push_back(Packet(query_step_packet_supported,
+ &RNBRemote::HandlePacket_qStepPacketSupported, NULL,
+ "qStepPacketSupported",
+ "Replys with OK if the 's' packet is supported."));
+ t.push_back(
+ Packet(query_vattachorwait_supported,
+ &RNBRemote::HandlePacket_qVAttachOrWaitSupported, NULL,
+ "qVAttachOrWaitSupported",
+ "Replys with OK if the 'vAttachOrWait' packet is supported."));
+ t.push_back(
+ Packet(query_sync_thread_state_supported,
+ &RNBRemote::HandlePacket_qSyncThreadStateSupported, NULL,
+ "qSyncThreadStateSupported",
+ "Replys with OK if the 'QSyncThreadState:' packet is supported."));
+ t.push_back(Packet(
+ query_host_info, &RNBRemote::HandlePacket_qHostInfo, NULL, "qHostInfo",
+ "Replies with multiple 'key:value;' tuples appended to each other."));
+ t.push_back(Packet(
+ query_gdb_server_version, &RNBRemote::HandlePacket_qGDBServerVersion,
+ NULL, "qGDBServerVersion",
+ "Replies with multiple 'key:value;' tuples appended to each other."));
+ t.push_back(Packet(
+ query_process_info, &RNBRemote::HandlePacket_qProcessInfo, NULL,
+ "qProcessInfo",
+ "Replies with multiple 'key:value;' tuples appended to each other."));
+ t.push_back(Packet(
+ query_symbol_lookup, &RNBRemote::HandlePacket_qSymbol, NULL, "qSymbol:",
+ "Notify that host debugger is ready to do symbol lookups"));
+ t.push_back(Packet(json_query_thread_extended_info,
+ &RNBRemote::HandlePacket_jThreadExtendedInfo, NULL,
+ "jThreadExtendedInfo",
+ "Replies with JSON data of thread extended information."));
+ t.push_back(Packet(json_query_get_loaded_dynamic_libraries_infos,
+ &RNBRemote::HandlePacket_jGetLoadedDynamicLibrariesInfos,
+ NULL, "jGetLoadedDynamicLibrariesInfos",
+ "Replies with JSON data of all the shared libraries "
+ "loaded in this process."));
+ t.push_back(
+ Packet(json_query_threads_info, &RNBRemote::HandlePacket_jThreadsInfo,
+ NULL, "jThreadsInfo",
+ "Replies with JSON data with information about all threads."));
+ t.push_back(Packet(json_query_get_shared_cache_info,
+ &RNBRemote::HandlePacket_jGetSharedCacheInfo, NULL,
+ "jGetSharedCacheInfo", "Replies with JSON data about the "
+ "location and uuid of the shared "
+ "cache in the inferior process."));
+ t.push_back(Packet(start_noack_mode, &RNBRemote::HandlePacket_QStartNoAckMode,
+ NULL, "QStartNoAckMode",
+ "Request that " DEBUGSERVER_PROGRAM_NAME
+ " stop acking remote protocol packets"));
+ t.push_back(Packet(prefix_reg_packets_with_tid,
+ &RNBRemote::HandlePacket_QThreadSuffixSupported, NULL,
+ "QThreadSuffixSupported",
+ "Check if thread specific packets (register packets 'g', "
+ "'G', 'p', and 'P') support having the thread ID appended "
+ "to the end of the command"));
+ t.push_back(Packet(set_logging_mode, &RNBRemote::HandlePacket_QSetLogging,
+ NULL, "QSetLogging:", "Check if register packets ('g', "
+ "'G', 'p', and 'P' support having "
+ "the thread ID prefix"));
+ t.push_back(Packet(
+ set_max_packet_size, &RNBRemote::HandlePacket_QSetMaxPacketSize, NULL,
+ "QSetMaxPacketSize:",
+ "Tell " DEBUGSERVER_PROGRAM_NAME " the max sized packet gdb can handle"));
+ t.push_back(Packet(
+ set_max_payload_size, &RNBRemote::HandlePacket_QSetMaxPayloadSize, NULL,
+ "QSetMaxPayloadSize:", "Tell " DEBUGSERVER_PROGRAM_NAME
+ " the max sized payload gdb can handle"));
+ t.push_back(
+ Packet(set_environment_variable, &RNBRemote::HandlePacket_QEnvironment,
+ NULL, "QEnvironment:",
+ "Add an environment variable to the inferior's environment"));
+ t.push_back(
+ Packet(set_environment_variable_hex,
+ &RNBRemote::HandlePacket_QEnvironmentHexEncoded, NULL,
+ "QEnvironmentHexEncoded:",
+ "Add an environment variable to the inferior's environment"));
+ t.push_back(Packet(set_launch_arch, &RNBRemote::HandlePacket_QLaunchArch,
+ NULL, "QLaunchArch:", "Set the architecture to use when "
+ "launching a process for hosts that "
+ "can run multiple architecture "
+ "slices from universal files."));
+ t.push_back(Packet(set_disable_aslr, &RNBRemote::HandlePacket_QSetDisableASLR,
+ NULL, "QSetDisableASLR:",
+ "Set whether to disable ASLR when launching the process "
+ "with the set argv ('A') packet"));
+ t.push_back(Packet(set_stdin, &RNBRemote::HandlePacket_QSetSTDIO, NULL,
+ "QSetSTDIN:", "Set the standard input for a process to be "
+ "launched with the 'A' packet"));
+ t.push_back(Packet(set_stdout, &RNBRemote::HandlePacket_QSetSTDIO, NULL,
+ "QSetSTDOUT:", "Set the standard output for a process to "
+ "be launched with the 'A' packet"));
+ t.push_back(Packet(set_stderr, &RNBRemote::HandlePacket_QSetSTDIO, NULL,
+ "QSetSTDERR:", "Set the standard error for a process to "
+ "be launched with the 'A' packet"));
+ t.push_back(Packet(set_working_dir, &RNBRemote::HandlePacket_QSetWorkingDir,
+ NULL, "QSetWorkingDir:", "Set the working directory for a "
+ "process to be launched with the "
+ "'A' packet"));
+ t.push_back(Packet(set_list_threads_in_stop_reply,
+ &RNBRemote::HandlePacket_QListThreadsInStopReply, NULL,
+ "QListThreadsInStopReply",
+ "Set if the 'threads' key should be added to the stop "
+ "reply packets with a list of all thread IDs."));
+ t.push_back(Packet(
+ sync_thread_state, &RNBRemote::HandlePacket_QSyncThreadState, NULL,
+ "QSyncThreadState:", "Do whatever is necessary to make sure 'thread' is "
+ "in a safe state to call functions on."));
+ // t.push_back (Packet (pass_signals_to_inferior,
+ // &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "QPassSignals:", "Specify
+ // which signals are passed to the inferior"));
+ t.push_back(Packet(allocate_memory, &RNBRemote::HandlePacket_AllocateMemory,
+ NULL, "_M", "Allocate memory in the inferior process."));
+ t.push_back(Packet(deallocate_memory,
+ &RNBRemote::HandlePacket_DeallocateMemory, NULL, "_m",
+ "Deallocate memory in the inferior process."));
+ t.push_back(Packet(
+ save_register_state, &RNBRemote::HandlePacket_SaveRegisterState, NULL,
+ "QSaveRegisterState", "Save the register state for the current thread "
+ "and return a decimal save ID."));
+ t.push_back(Packet(restore_register_state,
+ &RNBRemote::HandlePacket_RestoreRegisterState, NULL,
+ "QRestoreRegisterState:",
+ "Restore the register state given a save ID previously "
+ "returned from a call to QSaveRegisterState."));
+ t.push_back(Packet(
+ memory_region_info, &RNBRemote::HandlePacket_MemoryRegionInfo, NULL,
+ "qMemoryRegionInfo", "Return size and attributes of a memory region that "
+ "contains the given address"));
+ t.push_back(Packet(get_profile_data, &RNBRemote::HandlePacket_GetProfileData,
+ NULL, "qGetProfileData",
+ "Return profiling data of the current target."));
+ t.push_back(Packet(set_enable_profiling,
+ &RNBRemote::HandlePacket_SetEnableAsyncProfiling, NULL,
+ "QSetEnableAsyncProfiling",
+ "Enable or disable the profiling of current target."));
+ t.push_back(Packet(enable_compression,
+ &RNBRemote::HandlePacket_QEnableCompression, NULL,
+ "QEnableCompression:",
+ "Enable compression for the remainder of the connection"));
+ t.push_back(Packet(watchpoint_support_info,
+ &RNBRemote::HandlePacket_WatchpointSupportInfo, NULL,
+ "qWatchpointSupportInfo",
+ "Return the number of supported hardware watchpoints"));
+ t.push_back(Packet(set_process_event,
+ &RNBRemote::HandlePacket_QSetProcessEvent, NULL,
+ "QSetProcessEvent:", "Set a process event, to be passed "
+ "to the process, can be set before "
+ "the process is started, or after."));
+ t.push_back(
+ Packet(set_detach_on_error, &RNBRemote::HandlePacket_QSetDetachOnError,
+ NULL, "QSetDetachOnError:",
+ "Set whether debugserver will detach (1) or kill (0) from the "
+ "process it is controlling if it loses connection to lldb."));
+ t.push_back(Packet(
+ speed_test, &RNBRemote::HandlePacket_qSpeedTest, NULL, "qSpeedTest:",
+ "Test the maximum speed at which packet can be sent/received."));
+ t.push_back(Packet(query_transfer, &RNBRemote::HandlePacket_qXfer, NULL,
+ "qXfer:", "Support the qXfer packet."));
+ t.push_back(
+ Packet(query_supported_async_json_packets,
+ &RNBRemote::HandlePacket_qStructuredDataPlugins, NULL,
+ "qStructuredDataPlugins",
+ "Query for the structured data plugins supported by the remote."));
+ t.push_back(
+ Packet(configure_darwin_log, &RNBRemote::HandlePacket_QConfigureDarwinLog,
+ NULL, "QConfigureDarwinLog:",
+ "Configure the DarwinLog structured data plugin support."));
+}
+
+void RNBRemote::FlushSTDIO() {
+ if (m_ctx.HasValidProcessID()) {
+ nub_process_t pid = m_ctx.ProcessID();
+ char buf[256];
+ nub_size_t count;
+ do {
+ count = DNBProcessGetAvailableSTDOUT(pid, buf, sizeof(buf));
+ if (count > 0) {
+ SendSTDOUTPacket(buf, count);
+ }
+ } while (count > 0);
+
+ do {
+ count = DNBProcessGetAvailableSTDERR(pid, buf, sizeof(buf));
+ if (count > 0) {
+ SendSTDERRPacket(buf, count);
+ }
+ } while (count > 0);
+ }
+}
+
+void RNBRemote::SendAsyncProfileData() {
+ if (m_ctx.HasValidProcessID()) {
+ nub_process_t pid = m_ctx.ProcessID();
+ char buf[1024];
+ nub_size_t count;
+ do {
+ count = DNBProcessGetAvailableProfileData(pid, buf, sizeof(buf));
+ if (count > 0) {
+ SendAsyncProfileDataPacket(buf, count);
+ }
+ } while (count > 0);
+ }
+}
+
+void RNBRemote::SendAsyncDarwinLogData() {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): enter", __FUNCTION__);
+
+ if (!m_ctx.HasValidProcessID()) {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): ignoring due to"
+ "invalid process id",
+ __FUNCTION__);
+ return;
+ }
+
+ nub_process_t pid = m_ctx.ProcessID();
+ DarwinLogEventVector::size_type entry_count = 0;
+
+ // NOTE: the current looping structure here does nothing
+ // to guarantee that we can send off async packets faster
+ // than we generate them. It will keep sending as long
+ // as there's data to send.
+ do {
+ DarwinLogEventVector events = DNBProcessGetAvailableDarwinLogEvents(pid);
+ entry_count = events.size();
+
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): outer loop enter",
+ __FUNCTION__);
+
+ for (DarwinLogEventVector::size_type base_entry = 0;
+ base_entry < entry_count;
+ base_entry += DARWIN_LOG_MAX_EVENTS_PER_PACKET) {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): inner loop enter",
+ __FUNCTION__);
+
+ // We limit the total number of entries we pack
+ // into a single JSON async packet just so it
+ // doesn't get too large.
+ JSONGenerator::Dictionary async_dictionary;
+
+ // Specify the type of the JSON async data we're sending.
+ async_dictionary.AddStringItem(JSON_ASYNC_TYPE_KEY_NAME, "DarwinLog");
+
+ // Create an array entry in the dictionary to hold all
+ // the events going in this packet.
+ JSONGenerator::ArraySP events_array(new JSONGenerator::Array());
+ async_dictionary.AddItem(OS_LOG_EVENTS_KEY_NAME, events_array);
+
+ // We bundle up to DARWIN_LOG_MAX_EVENTS_PER_PACKET events in
+ // a single packet.
+ const auto inner_loop_bound =
+ std::min(base_entry + DARWIN_LOG_MAX_EVENTS_PER_PACKET, entry_count);
+ for (DarwinLogEventVector::size_type i = base_entry; i < inner_loop_bound;
+ ++i) {
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): adding "
+ "entry index %lu to the JSON packet",
+ __FUNCTION__, i);
+ events_array->AddItem(events[i]);
+ }
+
+ // Send off the packet.
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): sending JSON "
+ "packet, %lu entries remain",
+ __FUNCTION__, entry_count - inner_loop_bound);
+ SendAsyncJSONPacket(async_dictionary);
+ }
+
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): outer loop exit",
+ __FUNCTION__);
+
+ } while (entry_count > 0);
+
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "RNBRemote::%s(): exit",
+ __PRETTY_FUNCTION__);
+}
+
+rnb_err_t RNBRemote::SendHexEncodedBytePacket(const char *header,
+ const void *buf, size_t buf_len,
+ const char *footer) {
+ std::ostringstream packet_sstrm;
+ // Append the header cstr if there was one
+ if (header && header[0])
+ packet_sstrm << header;
+ nub_size_t i;
+ const uint8_t *ubuf8 = (const uint8_t *)buf;
+ for (i = 0; i < buf_len; i++) {
+ packet_sstrm << RAWHEX8(ubuf8[i]);
+ }
+ // Append the footer cstr if there was one
+ if (footer && footer[0])
+ packet_sstrm << footer;
+
+ return SendPacket(packet_sstrm.str());
+}
+
+rnb_err_t RNBRemote::SendSTDOUTPacket(char *buf, nub_size_t buf_size) {
+ if (buf_size == 0)
+ return rnb_success;
+ return SendHexEncodedBytePacket("O", buf, buf_size, NULL);
+}
+
+rnb_err_t RNBRemote::SendSTDERRPacket(char *buf, nub_size_t buf_size) {
+ if (buf_size == 0)
+ return rnb_success;
+ return SendHexEncodedBytePacket("O", buf, buf_size, NULL);
+}
+
+// This makes use of asynchronous bit 'A' in the gdb remote protocol.
+rnb_err_t RNBRemote::SendAsyncProfileDataPacket(char *buf,
+ nub_size_t buf_size) {
+ if (buf_size == 0)
+ return rnb_success;
+
+ std::string packet("A");
+ packet.append(buf, buf_size);
+ return SendPacket(packet);
+}
+
+rnb_err_t
+RNBRemote::SendAsyncJSONPacket(const JSONGenerator::Dictionary &dictionary) {
+ std::ostringstream stream;
+ // We're choosing something that is easy to spot if we somehow get one
+ // of these coming out at the wrong time (i.e. when the remote side
+ // is not waiting for a process control completion response).
+ stream << "JSON-async:";
+ dictionary.Dump(stream);
+ const std::string payload = binary_encode_string(stream.str());
+ return SendPacket(payload);
+}
+
+// Given a std::string packet contents to send, possibly encode/compress it.
+// If compression is enabled, the returned std::string will be in one of two
+// forms:
+//
+// N<original packet contents uncompressed>
+// C<size of original decompressed packet>:<packet compressed with the
+// requested compression scheme>
+//
+// If compression is not requested, the original packet contents are returned
+
+std::string RNBRemote::CompressString(const std::string &orig) {
+ std::string compressed;
+ compression_types compression_type = GetCompressionType();
+ if (compression_type != compression_types::none) {
+ bool compress_this_packet = false;
+
+ if (orig.size() > m_compression_minsize) {
+ compress_this_packet = true;
+ }
+
+ if (compress_this_packet) {
+ const size_t encoded_data_buf_size = orig.size() + 128;
+ std::vector<uint8_t> encoded_data(encoded_data_buf_size);
+ size_t compressed_size = 0;
+
+ // Allocate a scratch buffer for libcompression the first
+ // time we see a different compression type; reuse it in
+ // all compression_encode_buffer calls so it doesn't need
+ // to allocate / free its own scratch buffer each time.
+ // This buffer will only be freed when compression type
+ // changes; otherwise it will persist until debugserver
+ // exit.
+
+ static compression_types g_libcompress_scratchbuf_type = compression_types::none;
+ static void *g_libcompress_scratchbuf = nullptr;
+
+ if (g_libcompress_scratchbuf_type != compression_type) {
+ if (g_libcompress_scratchbuf) {
+ free (g_libcompress_scratchbuf);
+ g_libcompress_scratchbuf = nullptr;
+ }
+ size_t scratchbuf_size = 0;
+ switch (compression_type) {
+ case compression_types::lz4:
+ scratchbuf_size = compression_encode_scratch_buffer_size (COMPRESSION_LZ4_RAW);
+ break;
+ case compression_types::zlib_deflate:
+ scratchbuf_size = compression_encode_scratch_buffer_size (COMPRESSION_ZLIB);
+ break;
+ case compression_types::lzma:
+ scratchbuf_size = compression_encode_scratch_buffer_size (COMPRESSION_LZMA);
+ break;
+ case compression_types::lzfse:
+ scratchbuf_size = compression_encode_scratch_buffer_size (COMPRESSION_LZFSE);
+ break;
+ default:
+ break;
+ }
+ if (scratchbuf_size > 0) {
+ g_libcompress_scratchbuf = (void*) malloc (scratchbuf_size);
+ g_libcompress_scratchbuf_type = compression_type;
+ }
+ }
+
+ if (compression_type == compression_types::lz4) {
+ compressed_size = compression_encode_buffer(
+ encoded_data.data(), encoded_data_buf_size,
+ (const uint8_t *)orig.c_str(), orig.size(),
+ g_libcompress_scratchbuf,
+ COMPRESSION_LZ4_RAW);
+ }
+ if (compression_type == compression_types::zlib_deflate) {
+ compressed_size = compression_encode_buffer(
+ encoded_data.data(), encoded_data_buf_size,
+ (const uint8_t *)orig.c_str(), orig.size(),
+ g_libcompress_scratchbuf,
+ COMPRESSION_ZLIB);
+ }
+ if (compression_type == compression_types::lzma) {
+ compressed_size = compression_encode_buffer(
+ encoded_data.data(), encoded_data_buf_size,
+ (const uint8_t *)orig.c_str(), orig.size(),
+ g_libcompress_scratchbuf,
+ COMPRESSION_LZMA);
+ }
+ if (compression_type == compression_types::lzfse) {
+ compressed_size = compression_encode_buffer(
+ encoded_data.data(), encoded_data_buf_size,
+ (const uint8_t *)orig.c_str(), orig.size(),
+ g_libcompress_scratchbuf,
+ COMPRESSION_LZFSE);
+ }
+
+ if (compressed_size > 0) {
+ compressed.clear();
+ compressed.reserve(compressed_size);
+ compressed = "C";
+ char numbuf[16];
+ snprintf(numbuf, sizeof(numbuf), "%zu:", orig.size());
+ numbuf[sizeof(numbuf) - 1] = '\0';
+ compressed.append(numbuf);
+
+ for (size_t i = 0; i < compressed_size; i++) {
+ uint8_t byte = encoded_data[i];
+ if (byte == '#' || byte == '$' || byte == '}' || byte == '*' ||
+ byte == '\0') {
+ compressed.push_back(0x7d);
+ compressed.push_back(byte ^ 0x20);
+ } else {
+ compressed.push_back(byte);
+ }
+ }
+ } else {
+ compressed = "N" + orig;
+ }
+ } else {
+ compressed = "N" + orig;
+ }
+ } else {
+ compressed = orig;
+ }
+
+ return compressed;
+}
+
+rnb_err_t RNBRemote::SendPacket(const std::string &s) {
+ DNBLogThreadedIf(LOG_RNB_MAX, "%8d RNBRemote::%s (%s) called",
+ (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
+ __FUNCTION__, s.c_str());
+
+ std::string s_compressed = CompressString(s);
+
+ std::string sendpacket = "$" + s_compressed + "#";
+ int cksum = 0;
+ char hexbuf[5];
+
+ if (m_noack_mode) {
+ sendpacket += "00";
+ } else {
+ for (size_t i = 0; i != s_compressed.size(); ++i)
+ cksum += s_compressed[i];
+ snprintf(hexbuf, sizeof hexbuf, "%02x", cksum & 0xff);
+ sendpacket += hexbuf;
+ }
+
+ rnb_err_t err = m_comm.Write(sendpacket.c_str(), sendpacket.size());
+ if (err != rnb_success)
+ return err;
+
+ if (m_noack_mode)
+ return rnb_success;
+
+ std::string reply;
+ RNBRemote::Packet packet;
+ err = GetPacket(reply, packet, true);
+
+ if (err != rnb_success) {
+ DNBLogThreadedIf(LOG_RNB_REMOTE,
+ "%8d RNBRemote::%s (%s) got error trying to get reply...",
+ (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
+ __FUNCTION__, sendpacket.c_str());
+ return err;
+ }
+
+ DNBLogThreadedIf(LOG_RNB_MAX, "%8d RNBRemote::%s (%s) got reply: '%s'",
+ (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
+ __FUNCTION__, sendpacket.c_str(), reply.c_str());
+
+ if (packet.type == ack)
+ return rnb_success;
+
+ // Should we try to resend the packet at this layer?
+ // if (packet.command == nack)
+ return rnb_err;
+}
+
+/* Get a packet via gdb remote protocol.
+ Strip off the prefix/suffix, verify the checksum to make sure
+ a valid packet was received, send an ACK if they match. */
+
+rnb_err_t RNBRemote::GetPacketPayload(std::string &return_packet) {
+ // DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s called",
+ // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
+
+ PThreadMutex::Locker locker(m_mutex);
+ if (m_rx_packets.empty()) {
+ // Only reset the remote command available event if we have no more packets
+ m_ctx.Events().ResetEvents(RNBContext::event_read_packet_available);
+ // DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s error: no packets
+ // available...", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
+ // __FUNCTION__);
+ return rnb_err;
+ }
+
+ // DNBLogThreadedIf (LOG_RNB_MAX, "%8u RNBRemote::%s has %u queued packets",
+ // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__,
+ // m_rx_packets.size());
+ return_packet.swap(m_rx_packets.front());
+ m_rx_packets.pop_front();
+ locker.Reset(); // Release our lock on the mutex
+
+ if (m_rx_packets.empty()) {
+ // Reset the remote command available event if we have no more packets
+ m_ctx.Events().ResetEvents(RNBContext::event_read_packet_available);
+ }
+
+ // DNBLogThreadedIf (LOG_RNB_MEDIUM, "%8u RNBRemote::%s: '%s'",
+ // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__,
+ // return_packet.c_str());
+
+ switch (return_packet[0]) {
+ case '+':
+ case '-':
+ case '\x03':
+ break;
+
+ case '$': {
+ long packet_checksum = 0;
+ if (!m_noack_mode) {
+ for (size_t i = return_packet.size() - 2; i < return_packet.size(); ++i) {
+ char checksum_char = tolower(return_packet[i]);
+ if (!isxdigit(checksum_char)) {
+ m_comm.Write("-", 1);
+ DNBLogThreadedIf(LOG_RNB_REMOTE, "%8u RNBRemote::%s error: packet "
+ "with invalid checksum characters: "
+ "%s",
+ (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
+ __FUNCTION__, return_packet.c_str());
+ return rnb_err;
+ }
+ }
+ packet_checksum =
+ strtol(&return_packet[return_packet.size() - 2], NULL, 16);
+ }
+
+ return_packet.erase(0, 1); // Strip the leading '$'
+ return_packet.erase(return_packet.size() - 3); // Strip the #XX checksum
+
+ if (!m_noack_mode) {
+ // Compute the checksum
+ int computed_checksum = 0;
+ for (std::string::iterator it = return_packet.begin();
+ it != return_packet.end(); ++it) {
+ computed_checksum += *it;
+ }
+
+ if (packet_checksum == (computed_checksum & 0xff)) {
+ // DNBLogThreadedIf (LOG_RNB_MEDIUM, "%8u RNBRemote::%s sending ACK for
+ // '%s'", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
+ // __FUNCTION__, return_packet.c_str());
+ m_comm.Write("+", 1);
+ } else {
+ DNBLogThreadedIf(
+ LOG_RNB_MEDIUM, "%8u RNBRemote::%s sending ACK for '%s' (error: "
+ "packet checksum mismatch (0x%2.2lx != 0x%2.2x))",
+ (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__,
+ return_packet.c_str(), packet_checksum, computed_checksum);
+ m_comm.Write("-", 1);
+ return rnb_err;
+ }
+ }
+ } break;
+
+ default:
+ DNBLogThreadedIf(LOG_RNB_REMOTE,
+ "%8u RNBRemote::%s tossing unexpected packet???? %s",
+ (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
+ __FUNCTION__, return_packet.c_str());
+ if (!m_noack_mode)
+ m_comm.Write("-", 1);
+ return rnb_err;
+ }
+
+ return rnb_success;
+}
+
+rnb_err_t RNBRemote::HandlePacket_UNIMPLEMENTED(const char *p) {
+ DNBLogThreadedIf(LOG_RNB_MAX, "%8u RNBRemote::%s(\"%s\")",
+ (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
+ __FUNCTION__, p ? p : "NULL");
+ return SendPacket("");
+}
+
+rnb_err_t RNBRemote::HandlePacket_ILLFORMED(const char *file, int line,
+ const char *p,
+ const char *description) {
+ DNBLogThreadedIf(LOG_RNB_PACKETS, "%8u %s:%i ILLFORMED: '%s' (%s)",
+ (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), file,
+ line, __FUNCTION__, p);
+ return SendPacket("E03");
+}
+
+rnb_err_t RNBRemote::GetPacket(std::string &packet_payload,
+ RNBRemote::Packet &packet_info, bool wait) {
+ std::string payload;
+ rnb_err_t err = GetPacketPayload(payload);
+ if (err != rnb_success) {
+ PThreadEvent &events = m_ctx.Events();
+ nub_event_t set_events = events.GetEventBits();
+ // TODO: add timeout version of GetPacket?? We would then need to pass
+ // that timeout value along to DNBProcessTimedWaitForEvent.
+ if (!wait || ((set_events & RNBContext::event_read_thread_running) == 0))
+ return err;
+
+ const nub_event_t events_to_wait_for =
+ RNBContext::event_read_packet_available |
+ RNBContext::event_read_thread_exiting;
+
+ while ((set_events = events.WaitForSetEvents(events_to_wait_for)) != 0) {
+ if (set_events & RNBContext::event_read_packet_available) {
+ // Try the queue again now that we got an event
+ err = GetPacketPayload(payload);
+ if (err == rnb_success)
+ break;
+ }
+
+ if (set_events & RNBContext::event_read_thread_exiting)
+ err = rnb_not_connected;
+
+ if (err == rnb_not_connected)
+ return err;
+ }
+ while (err == rnb_err)
+ ;
+
+ if (set_events == 0)
+ err = rnb_not_connected;
+ }
+
+ if (err == rnb_success) {
+ Packet::iterator it;
+ for (it = m_packets.begin(); it != m_packets.end(); ++it) {
+ if (payload.compare(0, it->abbrev.size(), it->abbrev) == 0)
+ break;
+ }
+
+ // A packet we don't have an entry for. This can happen when we
+ // get a packet that we don't know about or support. We just reply
+ // accordingly and go on.
+ if (it == m_packets.end()) {
+ DNBLogThreadedIf(LOG_RNB_PACKETS, "unimplemented packet: '%s'",
+ payload.c_str());
+ HandlePacket_UNIMPLEMENTED(payload.c_str());
+ return rnb_err;
+ } else {
+ packet_info = *it;
+ packet_payload = payload;
+ }
+ }
+ return err;
+}
+
+rnb_err_t RNBRemote::HandleAsyncPacket(PacketEnum *type) {
+ DNBLogThreadedIf(LOG_RNB_REMOTE, "%8u RNBRemote::%s",
+ (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
+ __FUNCTION__);
+ static DNBTimer g_packetTimer(true);
+ rnb_err_t err = rnb_err;
+ std::string packet_data;
+ RNBRemote::Packet packet_info;
+ err = GetPacket(packet_data, packet_info, false);
+
+ if (err == rnb_success) {
+ if (!packet_data.empty() && isprint(packet_data[0]))
+ DNBLogThreadedIf(LOG_RNB_REMOTE | LOG_RNB_PACKETS,
+ "HandleAsyncPacket (\"%s\");", packet_data.c_str());
+ else
+ DNBLogThreadedIf(LOG_RNB_REMOTE | LOG_RNB_PACKETS,
+ "HandleAsyncPacket (%s);",
+ packet_info.printable_name.c_str());
+
+ HandlePacketCallback packet_callback = packet_info.async;
+ if (packet_callback != NULL) {
+ if (type != NULL)
+ *type = packet_info.type;
+ return (this->*packet_callback)(packet_data.c_str());
+ }
+ }
+
+ return err;
+}
+
+rnb_err_t RNBRemote::HandleReceivedPacket(PacketEnum *type) {
+ static DNBTimer g_packetTimer(true);
+
+ // DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s",
+ // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
+ rnb_err_t err = rnb_err;
+ std::string packet_data;
+ RNBRemote::Packet packet_info;
+ err = GetPacket(packet_data, packet_info, false);
+
+ if (err == rnb_success) {
+ DNBLogThreadedIf(LOG_RNB_REMOTE, "HandleReceivedPacket (\"%s\");",
+ packet_data.c_str());
+ HandlePacketCallback packet_callback = packet_info.normal;
+ if (packet_callback != NULL) {
+ if (type != NULL)
+ *type = packet_info.type;
+ return (this->*packet_callback)(packet_data.c_str());
+ } else {
+ // Do not fall through to end of this function, if we have valid
+ // packet_info and it has a NULL callback, then we need to respect
+ // that it may not want any response or anything to be done.
+ return err;
+ }
+ }
+ return rnb_err;
+}
+
+void RNBRemote::CommDataReceived(const std::string &new_data) {
+ // DNBLogThreadedIf (LOG_RNB_REMOTE, "%8d RNBRemote::%s called",
+ // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
+ {
+ // Put the packet data into the buffer in a thread safe fashion
+ PThreadMutex::Locker locker(m_mutex);
+
+ std::string data;
+ // See if we have any left over data from a previous call to this
+ // function?
+ if (!m_rx_partial_data.empty()) {
+ // We do, so lets start with that data
+ data.swap(m_rx_partial_data);
+ }
+ // Append the new incoming data
+ data += new_data;
+
+ // Parse up the packets into gdb remote packets
+ size_t idx = 0;
+ const size_t data_size = data.size();
+
+ while (idx < data_size) {
+ // end_idx must be one past the last valid packet byte. Start
+ // it off with an invalid value that is the same as the current
+ // index.
+ size_t end_idx = idx;
+
+ switch (data[idx]) {
+ case '+': // Look for ack
+ case '-': // Look for cancel
+ case '\x03': // ^C to halt target
+ end_idx = idx + 1; // The command is one byte long...
+ break;
+
+ case '$':
+ // Look for a standard gdb packet?
+ end_idx = data.find('#', idx + 1);
+ if (end_idx == std::string::npos || end_idx + 3 > data_size) {
+ end_idx = std::string::npos;
+ } else {
+ // Add two for the checksum bytes and 1 to point to the
+ // byte just past the end of this packet
+ end_idx += 3;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (end_idx == std::string::npos) {
+ // Not all data may be here for the packet yet, save it for
+ // next time through this function.
+ m_rx_partial_data += data.substr(idx);
+ // DNBLogThreadedIf (LOG_RNB_MAX, "%8d RNBRemote::%s saving data for
+ // later[%u, npos):
+ // '%s'",(uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
+ // __FUNCTION__, idx, m_rx_partial_data.c_str());
+ idx = end_idx;
+ } else if (idx < end_idx) {
+ m_packets_recvd++;
+ // Hack to get rid of initial '+' ACK???
+ if (m_packets_recvd == 1 && (end_idx == idx + 1) && data[idx] == '+') {
+ // DNBLogThreadedIf (LOG_RNB_REMOTE, "%8d RNBRemote::%s throwing first
+ // ACK away....[%u, npos):
+ // '+'",(uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
+ // __FUNCTION__, idx);
+ } else {
+ // We have a valid packet...
+ m_rx_packets.push_back(data.substr(idx, end_idx - idx));
+ DNBLogThreadedIf(LOG_RNB_PACKETS, "getpkt: %s",
+ m_rx_packets.back().c_str());
+ }
+ idx = end_idx;
+ } else {
+ DNBLogThreadedIf(LOG_RNB_MAX,
+ "%8d RNBRemote::%s tossing junk byte at %c",
+ (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
+ __FUNCTION__, data[idx]);
+ idx = idx + 1;
+ }
+ }
+ }
+
+ if (!m_rx_packets.empty()) {
+ // Let the main thread know we have received a packet
+
+ // DNBLogThreadedIf (LOG_RNB_EVENTS, "%8d RNBRemote::%s called
+ // events.SetEvent(RNBContext::event_read_packet_available)",
+ // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
+ PThreadEvent &events = m_ctx.Events();
+ events.SetEvents(RNBContext::event_read_packet_available);
+ }
+}
+
+rnb_err_t RNBRemote::GetCommData() {
+ // DNBLogThreadedIf (LOG_RNB_REMOTE, "%8d RNBRemote::%s called",
+ // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
+ std::string comm_data;
+ rnb_err_t err = m_comm.Read(comm_data);
+ if (err == rnb_success) {
+ if (!comm_data.empty())
+ CommDataReceived(comm_data);
+ }
+ return err;
+}
+
+void RNBRemote::StartReadRemoteDataThread() {
+ DNBLogThreadedIf(LOG_RNB_REMOTE, "%8u RNBRemote::%s called",
+ (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
+ __FUNCTION__);
+ PThreadEvent &events = m_ctx.Events();
+ if ((events.GetEventBits() & RNBContext::event_read_thread_running) == 0) {
+ events.ResetEvents(RNBContext::event_read_thread_exiting);
+ int err = ::pthread_create(&m_rx_pthread, NULL,
+ ThreadFunctionReadRemoteData, this);
+ if (err == 0) {
+ // Our thread was successfully kicked off, wait for it to
+ // set the started event so we can safely continue
+ events.WaitForSetEvents(RNBContext::event_read_thread_running);
+ } else {
+ events.ResetEvents(RNBContext::event_read_thread_running);
+ events.SetEvents(RNBContext::event_read_thread_exiting);
+ }
+ }
+}
+
+void RNBRemote::StopReadRemoteDataThread() {
+ DNBLogThreadedIf(LOG_RNB_REMOTE, "%8u RNBRemote::%s called",
+ (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
+ __FUNCTION__);
+ PThreadEvent &events = m_ctx.Events();
+ if ((events.GetEventBits() & RNBContext::event_read_thread_running) ==
+ RNBContext::event_read_thread_running) {
+ m_comm.Disconnect(true);
+ struct timespec timeout_abstime;
+ DNBTimer::OffsetTimeOfDay(&timeout_abstime, 2, 0);
+
+ // Wait for 2 seconds for the remote data thread to exit
+ if (events.WaitForSetEvents(RNBContext::event_read_thread_exiting,
+ &timeout_abstime) == 0) {
+ // Kill the remote data thread???
+ }
+ }
+}
+
+void *RNBRemote::ThreadFunctionReadRemoteData(void *arg) {
+ // Keep a shared pointer reference so this doesn't go away on us before the
+ // thread is killed.
+ DNBLogThreadedIf(LOG_RNB_REMOTE, "RNBRemote::%s (%p): thread starting...",
+ __FUNCTION__, arg);
+ RNBRemoteSP remoteSP(g_remoteSP);
+ if (remoteSP.get() != NULL) {
+
+#if defined(__APPLE__)
+ pthread_setname_np("read gdb-remote packets thread");
+#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
+ struct sched_param thread_param;
+ int thread_sched_policy;
+ if (pthread_getschedparam(pthread_self(), &thread_sched_policy,
+ &thread_param) == 0) {
+ thread_param.sched_priority = 47;
+ pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);
+ }
+#endif
+#endif
+
+ RNBRemote *remote = remoteSP.get();
+ PThreadEvent &events = remote->Context().Events();
+ events.SetEvents(RNBContext::event_read_thread_running);
+ // START: main receive remote command thread loop
+ bool done = false;
+ while (!done) {
+ rnb_err_t err = remote->GetCommData();
+
+ switch (err) {
+ case rnb_success:
+ break;
+
+ case rnb_err:
+ DNBLogThreadedIf(LOG_RNB_REMOTE,
+ "RNBSocket::GetCommData returned error %u", err);
+ done = true;
+ break;
+
+ case rnb_not_connected:
+ DNBLogThreadedIf(LOG_RNB_REMOTE,
+ "RNBSocket::GetCommData returned not connected...");
+ done = true;
+ break;
+ }
+ }
+ // START: main receive remote command thread loop
+ events.ResetEvents(RNBContext::event_read_thread_running);
+ events.SetEvents(RNBContext::event_read_thread_exiting);
+ }
+ DNBLogThreadedIf(LOG_RNB_REMOTE, "RNBRemote::%s (%p): thread exiting...",
+ __FUNCTION__, arg);
+ return NULL;
+}
+
+// If we fail to get back a valid CPU type for the remote process,
+// make a best guess for the CPU type based on the currently running
+// debugserver binary -- the debugger may not handle the case of an
+// un-specified process CPU type correctly.
+
+static cpu_type_t best_guess_cpu_type() {
+#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
+ if (sizeof(char *) == 8) {
+ return CPU_TYPE_ARM64;
+ } else {
+#if defined (__ARM64_ARCH_8_32__)
+ return CPU_TYPE_ARM64_32;
+#endif
+ return CPU_TYPE_ARM;
+ }
+#elif defined(__i386__) || defined(__x86_64__)
+ if (sizeof(char *) == 8) {
+ return CPU_TYPE_X86_64;
+ } else {
+ return CPU_TYPE_I386;
+ }
+#endif
+ return 0;
+}
+
+/* Read the bytes in STR which are GDB Remote Protocol binary encoded bytes
+ (8-bit bytes).
+ This encoding uses 0x7d ('}') as an escape character for
+ 0x7d ('}'), 0x23 ('#'), 0x24 ('$'), 0x2a ('*').
+ LEN is the number of bytes to be processed. If a character is escaped,
+ it is 2 characters for LEN. A LEN of -1 means decode-until-nul-byte
+ (end of string). */
+
+std::vector<uint8_t> decode_binary_data(const char *str, size_t len) {
+ std::vector<uint8_t> bytes;
+ if (len == 0) {
+ return bytes;
+ }
+ if (len == (size_t)-1)
+ len = strlen(str);
+
+ while (len--) {
+ unsigned char c = *str++;
+ if (c == 0x7d && len > 0) {
+ len--;
+ c = *str++ ^ 0x20;
+ }
+ bytes.push_back(c);
+ }
+ return bytes;
+}
+
+// Quote any meta characters in a std::string as per the binary
+// packet convention in the gdb-remote protocol.
+
+static std::string binary_encode_string(const std::string &s) {
+ std::string output;
+ const size_t s_size = s.size();
+ const char *s_chars = s.c_str();
+
+ for (size_t i = 0; i < s_size; i++) {
+ unsigned char ch = *(s_chars + i);
+ if (ch == '#' || ch == '$' || ch == '}' || ch == '*') {
+ output.push_back('}'); // 0x7d
+ output.push_back(ch ^ 0x20);
+ } else {
+ output.push_back(ch);
+ }
+ }
+ return output;
+}
+
+// If the value side of a key-value pair in JSON is a string,
+// and that string has a " character in it, the " character must
+// be escaped.
+
+std::string json_string_quote_metachars(const std::string &s) {
+ if (s.find('"') == std::string::npos)
+ return s;
+
+ std::string output;
+ const size_t s_size = s.size();
+ const char *s_chars = s.c_str();
+ for (size_t i = 0; i < s_size; i++) {
+ unsigned char ch = *(s_chars + i);
+ if (ch == '"') {
+ output.push_back('\\');
+ }
+ output.push_back(ch);
+ }
+ return output;
+}
+
+typedef struct register_map_entry {
+ uint32_t debugserver_regnum; // debugserver register number
+ uint32_t offset; // Offset in bytes into the register context data with no
+ // padding between register values
+ DNBRegisterInfo nub_info; // debugnub register info
+ std::vector<uint32_t> value_regnums;
+ std::vector<uint32_t> invalidate_regnums;
+} register_map_entry_t;
+
+// If the notion of registers differs from what is handed out by the
+// architecture, then flavors can be defined here.
+
+static std::vector<register_map_entry_t> g_dynamic_register_map;
+static register_map_entry_t *g_reg_entries = NULL;
+static size_t g_num_reg_entries = 0;
+
+void RNBRemote::Initialize() { DNBInitialize(); }
+
+bool RNBRemote::InitializeRegisters(bool force) {
+ pid_t pid = m_ctx.ProcessID();
+ if (pid == INVALID_NUB_PROCESS)
+ return false;
+
+ DNBLogThreadedIf(
+ LOG_RNB_PROC,
+ "RNBRemote::%s() getting native registers from DNB interface",
+ __FUNCTION__);
+ // Discover the registers by querying the DNB interface and letting it
+ // state the registers that it would like to export. This allows the
+ // registers to be discovered using multiple qRegisterInfo calls to get
+ // all register information after the architecture for the process is
+ // determined.
+ if (force) {
+ g_dynamic_register_map.clear();
+ g_reg_entries = NULL;
+ g_num_reg_entries = 0;
+ }
+
+ if (g_dynamic_register_map.empty()) {
+ nub_size_t num_reg_sets = 0;
+ const DNBRegisterSetInfo *reg_sets = DNBGetRegisterSetInfo(&num_reg_sets);
+
+ assert(num_reg_sets > 0 && reg_sets != NULL);
+
+ uint32_t regnum = 0;
+ uint32_t reg_data_offset = 0;
+ typedef std::map<std::string, uint32_t> NameToRegNum;
+ NameToRegNum name_to_regnum;
+ for (nub_size_t set = 0; set < num_reg_sets; ++set) {
+ if (reg_sets[set].registers == NULL)
+ continue;
+
+ for (uint32_t reg = 0; reg < reg_sets[set].num_registers; ++reg) {
+ register_map_entry_t reg_entry = {
+ regnum++, // register number starts at zero and goes up with no gaps
+ reg_data_offset, // Offset into register context data, no gaps
+ // between registers
+ reg_sets[set].registers[reg], // DNBRegisterInfo
+ {},
+ {},
+ };
+
+ name_to_regnum[reg_entry.nub_info.name] = reg_entry.debugserver_regnum;
+
+ if (reg_entry.nub_info.value_regs == NULL) {
+ reg_data_offset += reg_entry.nub_info.size;
+ }
+
+ g_dynamic_register_map.push_back(reg_entry);
+ }
+ }
+
+ // Now we must find any registers whose values are in other registers and
+ // fix up
+ // the offsets since we removed all gaps...
+ for (auto &reg_entry : g_dynamic_register_map) {
+ if (reg_entry.nub_info.value_regs) {
+ uint32_t new_offset = UINT32_MAX;
+ for (size_t i = 0; reg_entry.nub_info.value_regs[i] != NULL; ++i) {
+ const char *name = reg_entry.nub_info.value_regs[i];
+ auto pos = name_to_regnum.find(name);
+ if (pos != name_to_regnum.end()) {
+ regnum = pos->second;
+ reg_entry.value_regnums.push_back(regnum);
+ if (regnum < g_dynamic_register_map.size()) {
+ // The offset for value_regs registers is the offset within the
+ // register with the lowest offset
+ const uint32_t reg_offset =
+ g_dynamic_register_map[regnum].offset +
+ reg_entry.nub_info.offset;
+ if (new_offset > reg_offset)
+ new_offset = reg_offset;
+ }
+ }
+ }
+
+ if (new_offset != UINT32_MAX) {
+ reg_entry.offset = new_offset;
+ } else {
+ DNBLogThreaded("no offset was calculated entry for register %s",
+ reg_entry.nub_info.name);
+ reg_entry.offset = UINT32_MAX;
+ }
+ }
+
+ if (reg_entry.nub_info.update_regs) {
+ for (size_t i = 0; reg_entry.nub_info.update_regs[i] != NULL; ++i) {
+ const char *name = reg_entry.nub_info.update_regs[i];
+ auto pos = name_to_regnum.find(name);
+ if (pos != name_to_regnum.end()) {
+ regnum = pos->second;
+ reg_entry.invalidate_regnums.push_back(regnum);
+ }
+ }
+ }
+ }
+
+ // for (auto &reg_entry: g_dynamic_register_map)
+ // {
+ // DNBLogThreaded("%4i: size = %3u, pseudo = %i, name = %s",
+ // reg_entry.offset,
+ // reg_entry.nub_info.size,
+ // reg_entry.nub_info.value_regs != NULL,
+ // reg_entry.nub_info.name);
+ // }
+
+ g_reg_entries = g_dynamic_register_map.data();
+ g_num_reg_entries = g_dynamic_register_map.size();
+ }
+ return true;
+}
+
+/* The inferior has stopped executing; send a packet
+ to gdb to let it know. */
+
+void RNBRemote::NotifyThatProcessStopped(void) {
+ RNBRemote::HandlePacket_last_signal(NULL);
+ return;
+}
+
+/* 'A arglen,argnum,arg,...'
+ Update the inferior context CTX with the program name and arg
+ list.
+ The documentation for this packet is underwhelming but my best reading
+ of this is that it is a series of (len, position #, arg)'s, one for
+ each argument with "arg" hex encoded (two 0-9a-f chars?).
+ Why we need BOTH a "len" and a hex encoded "arg" is beyond me - either
+ is sufficient to get around the "," position separator escape issue.
+
+ e.g. our best guess for a valid 'A' packet for "gdb -q a.out" is
+
+ 6,0,676462,4,1,2d71,10,2,612e6f7574
+
+ Note that "argnum" and "arglen" are numbers in base 10. Again, that's
+ not documented either way but I'm assuming it's so. */
+
+rnb_err_t RNBRemote::HandlePacket_A(const char *p) {
+ if (p == NULL || *p == '\0') {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Null packet for 'A' pkt");
+ }
+ p++;
+ if (*p == '\0' || !isdigit(*p)) {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "arglen not specified on 'A' pkt");
+ }
+
+ /* I promise I don't modify it anywhere in this function. strtoul()'s
+ 2nd arg has to be non-const which makes it problematic to step
+ through the string easily. */
+ char *buf = const_cast<char *>(p);
+
+ RNBContext &ctx = Context();
+
+ while (*buf != '\0') {
+ unsigned long arglen, argnum;
+ std::string arg;
+ char *c;
+
+ errno = 0;
+ arglen = strtoul(buf, &c, 10);
+ if (errno != 0 && arglen == 0) {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "arglen not a number on 'A' pkt");
+ }
+ if (*c != ',') {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "arglen not followed by comma on 'A' pkt");
+ }
+ buf = c + 1;
+
+ errno = 0;
+ argnum = strtoul(buf, &c, 10);
+ if (errno != 0 && argnum == 0) {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "argnum not a number on 'A' pkt");
+ }
+ if (*c != ',') {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "arglen not followed by comma on 'A' pkt");
+ }
+ buf = c + 1;
+
+ c = buf;
+ buf = buf + arglen;
+ while (c < buf && *c != '\0' && c + 1 < buf && *(c + 1) != '\0') {
+ char smallbuf[3];
+ smallbuf[0] = *c;
+ smallbuf[1] = *(c + 1);
+ smallbuf[2] = '\0';
+
+ errno = 0;
+ int ch = static_cast<int>(strtoul(smallbuf, NULL, 16));
+ if (errno != 0 && ch == 0) {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "non-hex char in arg on 'A' pkt");
+ }
+
+ arg.push_back(ch);
+ c += 2;
+ }
+
+ ctx.PushArgument(arg.c_str());
+ if (*buf == ',')
+ buf++;
+ }
+ SendPacket("OK");
+
+ return rnb_success;
+}
+
+/* 'H c t'
+ Set the thread for subsequent actions; 'c' for step/continue ops,
+ 'g' for other ops. -1 means all threads, 0 means any thread. */
+
+rnb_err_t RNBRemote::HandlePacket_H(const char *p) {
+ p++; // skip 'H'
+ if (*p != 'c' && *p != 'g') {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Missing 'c' or 'g' type in H packet");
+ }
+
+ if (!m_ctx.HasValidProcessID()) {
+ // We allow gdb to connect to a server that hasn't started running
+ // the target yet. gdb still wants to ask questions about it and
+ // freaks out if it gets an error. So just return OK here.
+ }
+
+ errno = 0;
+ nub_thread_t tid = strtoul(p + 1, NULL, 16);
+ if (errno != 0 && tid == 0) {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid thread number in H packet");
+ }
+ if (*p == 'c')
+ SetContinueThread(tid);
+ if (*p == 'g')
+ SetCurrentThread(tid);
+
+ return SendPacket("OK");
+}
+
+rnb_err_t RNBRemote::HandlePacket_qLaunchSuccess(const char *p) {
+ if (m_ctx.HasValidProcessID() || m_ctx.LaunchStatus().Status() == 0)
+ return SendPacket("OK");
+ std::ostringstream ret_str;
+ std::string status_str;
+ ret_str << "E" << m_ctx.LaunchStatusAsString(status_str);
+
+ return SendPacket(ret_str.str());
+}
+
+rnb_err_t RNBRemote::HandlePacket_qShlibInfoAddr(const char *p) {
+ if (m_ctx.HasValidProcessID()) {
+ nub_addr_t shlib_info_addr =
+ DNBProcessGetSharedLibraryInfoAddress(m_ctx.ProcessID());
+ if (shlib_info_addr != INVALID_NUB_ADDRESS) {
+ std::ostringstream ostrm;
+ ostrm << RAW_HEXBASE << shlib_info_addr;
+ return SendPacket(ostrm.str());
+ }
+ }
+ return SendPacket("E44");
+}
+
+rnb_err_t RNBRemote::HandlePacket_qStepPacketSupported(const char *p) {
+ // Normally the "s" packet is mandatory, yet in gdb when using ARM, they
+ // get around the need for this packet by implementing software single
+ // stepping from gdb. Current versions of debugserver do support the "s"
+ // packet, yet some older versions do not. We need a way to tell if this
+ // packet is supported so we can disable software single stepping in gdb
+ // for remote targets (so the "s" packet will get used).
+ return SendPacket("OK");
+}
+
+rnb_err_t RNBRemote::HandlePacket_qSyncThreadStateSupported(const char *p) {
+ // We support attachOrWait meaning attach if the process exists, otherwise
+ // wait to attach.
+ return SendPacket("OK");
+}
+
+rnb_err_t RNBRemote::HandlePacket_qVAttachOrWaitSupported(const char *p) {
+ // We support attachOrWait meaning attach if the process exists, otherwise
+ // wait to attach.
+ return SendPacket("OK");
+}
+
+rnb_err_t RNBRemote::HandlePacket_qThreadStopInfo(const char *p) {
+ p += strlen("qThreadStopInfo");
+ nub_thread_t tid = strtoul(p, 0, 16);
+ return SendStopReplyPacketForThread(tid);
+}
+
+rnb_err_t RNBRemote::HandlePacket_qThreadInfo(const char *p) {
+ // We allow gdb to connect to a server that hasn't started running
+ // the target yet. gdb still wants to ask questions about it and
+ // freaks out if it gets an error. So just return OK here.
+ nub_process_t pid = m_ctx.ProcessID();
+ if (pid == INVALID_NUB_PROCESS)
+ return SendPacket("OK");
+
+ // Only "qfThreadInfo" and "qsThreadInfo" get into this function so
+ // we only need to check the second byte to tell which is which
+ if (p[1] == 'f') {
+ nub_size_t numthreads = DNBProcessGetNumThreads(pid);
+ std::ostringstream ostrm;
+ ostrm << "m";
+ bool first = true;
+ for (nub_size_t i = 0; i < numthreads; ++i) {
+ if (first)
+ first = false;
+ else
+ ostrm << ",";
+ nub_thread_t th = DNBProcessGetThreadAtIndex(pid, i);
+ ostrm << std::hex << th;
+ }
+ return SendPacket(ostrm.str());
+ } else {
+ return SendPacket("l");
+ }
+}
+
+rnb_err_t RNBRemote::HandlePacket_qThreadExtraInfo(const char *p) {
+ // We allow gdb to connect to a server that hasn't started running
+ // the target yet. gdb still wants to ask questions about it and
+ // freaks out if it gets an error. So just return OK here.
+ nub_process_t pid = m_ctx.ProcessID();
+ if (pid == INVALID_NUB_PROCESS)
+ return SendPacket("OK");
+
+ /* This is supposed to return a string like 'Runnable' or
+ 'Blocked on Mutex'.
+ The returned string is formatted like the "A" packet - a
+ sequence of letters encoded in as 2-hex-chars-per-letter. */
+ p += strlen("qThreadExtraInfo");
+ if (*p++ != ',')
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Illformed qThreadExtraInfo packet");
+ errno = 0;
+ nub_thread_t tid = strtoul(p, NULL, 16);
+ if (errno != 0 && tid == 0) {
+ return HandlePacket_ILLFORMED(
+ __FILE__, __LINE__, p,
+ "Invalid thread number in qThreadExtraInfo packet");
+ }
+
+ const char *threadInfo = DNBThreadGetInfo(pid, tid);
+ if (threadInfo != NULL && threadInfo[0]) {
+ return SendHexEncodedBytePacket(NULL, threadInfo, strlen(threadInfo), NULL);
+ } else {
+ // "OK" == 4f6b
+ // Return "OK" as a ASCII hex byte stream if things go wrong
+ return SendPacket("4f6b");
+ }
+
+ return SendPacket("");
+}
+
+const char *k_space_delimiters = " \t";
+static void skip_spaces(std::string &line) {
+ if (!line.empty()) {
+ size_t space_pos = line.find_first_not_of(k_space_delimiters);
+ if (space_pos > 0)
+ line.erase(0, space_pos);
+ }
+}
+
+static std::string get_identifier(std::string &line) {
+ std::string word;
+ skip_spaces(line);
+ const size_t line_size = line.size();
+ size_t end_pos;
+ for (end_pos = 0; end_pos < line_size; ++end_pos) {
+ if (end_pos == 0) {
+ if (isalpha(line[end_pos]) || line[end_pos] == '_')
+ continue;
+ } else if (isalnum(line[end_pos]) || line[end_pos] == '_')
+ continue;
+ break;
+ }
+ word.assign(line, 0, end_pos);
+ line.erase(0, end_pos);
+ return word;
+}
+
+static std::string get_operator(std::string &line) {
+ std::string op;
+ skip_spaces(line);
+ if (!line.empty()) {
+ if (line[0] == '=') {
+ op = '=';
+ line.erase(0, 1);
+ }
+ }
+ return op;
+}
+
+static std::string get_value(std::string &line) {
+ std::string value;
+ skip_spaces(line);
+ if (!line.empty()) {
+ value.swap(line);
+ }
+ return value;
+}
+
+extern void FileLogCallback(void *baton, uint32_t flags, const char *format,
+ va_list args);
+extern void ASLLogCallback(void *baton, uint32_t flags, const char *format,
+ va_list args);
+
+rnb_err_t RNBRemote::HandlePacket_qRcmd(const char *p) {
+ const char *c = p + strlen("qRcmd,");
+ std::string line;
+ while (c[0] && c[1]) {
+ char smallbuf[3] = {c[0], c[1], '\0'};
+ errno = 0;
+ int ch = static_cast<int>(strtoul(smallbuf, NULL, 16));
+ if (errno != 0 && ch == 0)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "non-hex char in payload of qRcmd packet");
+ line.push_back(ch);
+ c += 2;
+ }
+ if (*c == '\0') {
+ std::string command = get_identifier(line);
+ if (command == "set") {
+ std::string variable = get_identifier(line);
+ std::string op = get_operator(line);
+ std::string value = get_value(line);
+ if (variable == "logfile") {
+ FILE *log_file = fopen(value.c_str(), "w");
+ if (log_file) {
+ DNBLogSetLogCallback(FileLogCallback, log_file);
+ return SendPacket("OK");
+ }
+ return SendPacket("E71");
+ } else if (variable == "logmask") {
+ char *end;
+ errno = 0;
+ uint32_t logmask =
+ static_cast<uint32_t>(strtoul(value.c_str(), &end, 0));
+ if (errno == 0 && end && *end == '\0') {
+ DNBLogSetLogMask(logmask);
+ if (!DNBLogGetLogCallback())
+ DNBLogSetLogCallback(ASLLogCallback, NULL);
+ return SendPacket("OK");
+ }
+ errno = 0;
+ logmask = static_cast<uint32_t>(strtoul(value.c_str(), &end, 16));
+ if (errno == 0 && end && *end == '\0') {
+ DNBLogSetLogMask(logmask);
+ return SendPacket("OK");
+ }
+ return SendPacket("E72");
+ }
+ return SendPacket("E70");
+ }
+ return SendPacket("E69");
+ }
+ return SendPacket("E73");
+}
+
+rnb_err_t RNBRemote::HandlePacket_qC(const char *p) {
+ nub_thread_t tid;
+ std::ostringstream rep;
+ // If we haven't run the process yet, we tell the debugger the
+ // pid is 0. That way it can know to tell use to run later on.
+ if (!m_ctx.HasValidProcessID())
+ tid = 0;
+ else {
+ // Grab the current thread.
+ tid = DNBProcessGetCurrentThread(m_ctx.ProcessID());
+ // Make sure we set the current thread so g and p packets return
+ // the data the gdb will expect.
+ SetCurrentThread(tid);
+ }
+ rep << "QC" << std::hex << tid;
+ return SendPacket(rep.str());
+}
+
+rnb_err_t RNBRemote::HandlePacket_qEcho(const char *p) {
+ // Just send the exact same packet back that we received to
+ // synchronize the response packets after a previous packet
+ // timed out. This allows the debugger to get back on track
+ // with responses after a packet timeout.
+ return SendPacket(p);
+}
+
+rnb_err_t RNBRemote::HandlePacket_qGetPid(const char *p) {
+ nub_process_t pid;
+ std::ostringstream rep;
+ // If we haven't run the process yet, we tell the debugger the
+ // pid is 0. That way it can know to tell use to run later on.
+ if (m_ctx.HasValidProcessID())
+ pid = m_ctx.ProcessID();
+ else
+ pid = 0;
+ rep << std::hex << pid;
+ return SendPacket(rep.str());
+}
+
+rnb_err_t RNBRemote::HandlePacket_qRegisterInfo(const char *p) {
+ if (g_num_reg_entries == 0)
+ InitializeRegisters();
+
+ p += strlen("qRegisterInfo");
+
+ nub_size_t num_reg_sets = 0;
+ const DNBRegisterSetInfo *reg_set_info = DNBGetRegisterSetInfo(&num_reg_sets);
+ uint32_t reg_num = static_cast<uint32_t>(strtoul(p, 0, 16));
+
+ if (reg_num < g_num_reg_entries) {
+ const register_map_entry_t *reg_entry = &g_reg_entries[reg_num];
+ std::ostringstream ostrm;
+ if (reg_entry->nub_info.name)
+ ostrm << "name:" << reg_entry->nub_info.name << ';';
+ if (reg_entry->nub_info.alt)
+ ostrm << "alt-name:" << reg_entry->nub_info.alt << ';';
+
+ ostrm << "bitsize:" << std::dec << reg_entry->nub_info.size * 8 << ';';
+ ostrm << "offset:" << std::dec << reg_entry->offset << ';';
+
+ switch (reg_entry->nub_info.type) {
+ case Uint:
+ ostrm << "encoding:uint;";
+ break;
+ case Sint:
+ ostrm << "encoding:sint;";
+ break;
+ case IEEE754:
+ ostrm << "encoding:ieee754;";
+ break;
+ case Vector:
+ ostrm << "encoding:vector;";
+ break;
+ }
+
+ switch (reg_entry->nub_info.format) {
+ case Binary:
+ ostrm << "format:binary;";
+ break;
+ case Decimal:
+ ostrm << "format:decimal;";
+ break;
+ case Hex:
+ ostrm << "format:hex;";
+ break;
+ case Float:
+ ostrm << "format:float;";
+ break;
+ case VectorOfSInt8:
+ ostrm << "format:vector-sint8;";
+ break;
+ case VectorOfUInt8:
+ ostrm << "format:vector-uint8;";
+ break;
+ case VectorOfSInt16:
+ ostrm << "format:vector-sint16;";
+ break;
+ case VectorOfUInt16:
+ ostrm << "format:vector-uint16;";
+ break;
+ case VectorOfSInt32:
+ ostrm << "format:vector-sint32;";
+ break;
+ case VectorOfUInt32:
+ ostrm << "format:vector-uint32;";
+ break;
+ case VectorOfFloat32:
+ ostrm << "format:vector-float32;";
+ break;
+ case VectorOfUInt128:
+ ostrm << "format:vector-uint128;";
+ break;
+ };
+
+ if (reg_set_info && reg_entry->nub_info.set < num_reg_sets)
+ ostrm << "set:" << reg_set_info[reg_entry->nub_info.set].name << ';';
+
+ if (reg_entry->nub_info.reg_ehframe != INVALID_NUB_REGNUM)
+ ostrm << "ehframe:" << std::dec << reg_entry->nub_info.reg_ehframe << ';';
+
+ if (reg_entry->nub_info.reg_dwarf != INVALID_NUB_REGNUM)
+ ostrm << "dwarf:" << std::dec << reg_entry->nub_info.reg_dwarf << ';';
+
+ switch (reg_entry->nub_info.reg_generic) {
+ case GENERIC_REGNUM_FP:
+ ostrm << "generic:fp;";
+ break;
+ case GENERIC_REGNUM_PC:
+ ostrm << "generic:pc;";
+ break;
+ case GENERIC_REGNUM_SP:
+ ostrm << "generic:sp;";
+ break;
+ case GENERIC_REGNUM_RA:
+ ostrm << "generic:ra;";
+ break;
+ case GENERIC_REGNUM_FLAGS:
+ ostrm << "generic:flags;";
+ break;
+ case GENERIC_REGNUM_ARG1:
+ ostrm << "generic:arg1;";
+ break;
+ case GENERIC_REGNUM_ARG2:
+ ostrm << "generic:arg2;";
+ break;
+ case GENERIC_REGNUM_ARG3:
+ ostrm << "generic:arg3;";
+ break;
+ case GENERIC_REGNUM_ARG4:
+ ostrm << "generic:arg4;";
+ break;
+ case GENERIC_REGNUM_ARG5:
+ ostrm << "generic:arg5;";
+ break;
+ case GENERIC_REGNUM_ARG6:
+ ostrm << "generic:arg6;";
+ break;
+ case GENERIC_REGNUM_ARG7:
+ ostrm << "generic:arg7;";
+ break;
+ case GENERIC_REGNUM_ARG8:
+ ostrm << "generic:arg8;";
+ break;
+ default:
+ break;
+ }
+
+ if (!reg_entry->value_regnums.empty()) {
+ ostrm << "container-regs:";
+ for (size_t i = 0, n = reg_entry->value_regnums.size(); i < n; ++i) {
+ if (i > 0)
+ ostrm << ',';
+ ostrm << RAW_HEXBASE << reg_entry->value_regnums[i];
+ }
+ ostrm << ';';
+ }
+
+ if (!reg_entry->invalidate_regnums.empty()) {
+ ostrm << "invalidate-regs:";
+ for (size_t i = 0, n = reg_entry->invalidate_regnums.size(); i < n; ++i) {
+ if (i > 0)
+ ostrm << ',';
+ ostrm << RAW_HEXBASE << reg_entry->invalidate_regnums[i];
+ }
+ ostrm << ';';
+ }
+
+ return SendPacket(ostrm.str());
+ }
+ return SendPacket("E45");
+}
+
+/* This expects a packet formatted like
+
+ QSetLogging:bitmask=LOG_ALL|LOG_RNB_REMOTE;
+
+ with the "QSetLogging:" already removed from the start. Maybe in the
+ future this packet will include other keyvalue pairs like
+
+ QSetLogging:bitmask=LOG_ALL;mode=asl;
+ */
+
+rnb_err_t set_logging(const char *p) {
+ int bitmask = 0;
+ while (p && *p != '\0') {
+ if (strncmp(p, "bitmask=", sizeof("bitmask=") - 1) == 0) {
+ p += sizeof("bitmask=") - 1;
+ while (p && *p != '\0' && *p != ';') {
+ if (*p == '|')
+ p++;
+
+ // to regenerate the LOG_ entries (not including the LOG_RNB entries)
+ // $ for logname in `grep '^#define LOG_' DNBDefs.h | egrep -v
+ // 'LOG_HI|LOG_LO' | awk '{print $2}'`
+ // do
+ // echo " else if (strncmp (p, \"$logname\", sizeof
+ // (\"$logname\") - 1) == 0)"
+ // echo " {"
+ // echo " p += sizeof (\"$logname\") - 1;"
+ // echo " bitmask |= $logname;"
+ // echo " }"
+ // done
+ if (strncmp(p, "LOG_VERBOSE", sizeof("LOG_VERBOSE") - 1) == 0) {
+ p += sizeof("LOG_VERBOSE") - 1;
+ bitmask |= LOG_VERBOSE;
+ } else if (strncmp(p, "LOG_PROCESS", sizeof("LOG_PROCESS") - 1) == 0) {
+ p += sizeof("LOG_PROCESS") - 1;
+ bitmask |= LOG_PROCESS;
+ } else if (strncmp(p, "LOG_THREAD", sizeof("LOG_THREAD") - 1) == 0) {
+ p += sizeof("LOG_THREAD") - 1;
+ bitmask |= LOG_THREAD;
+ } else if (strncmp(p, "LOG_EXCEPTIONS", sizeof("LOG_EXCEPTIONS") - 1) ==
+ 0) {
+ p += sizeof("LOG_EXCEPTIONS") - 1;
+ bitmask |= LOG_EXCEPTIONS;
+ } else if (strncmp(p, "LOG_SHLIB", sizeof("LOG_SHLIB") - 1) == 0) {
+ p += sizeof("LOG_SHLIB") - 1;
+ bitmask |= LOG_SHLIB;
+ } else if (strncmp(p, "LOG_MEMORY_DATA_SHORT",
+ sizeof("LOG_MEMORY_DATA_SHORT") - 1) == 0) {
+ p += sizeof("LOG_MEMORY_DATA_SHORT") - 1;
+ bitmask |= LOG_MEMORY_DATA_SHORT;
+ } else if (strncmp(p, "LOG_MEMORY_DATA_LONG",
+ sizeof("LOG_MEMORY_DATA_LONG") - 1) == 0) {
+ p += sizeof("LOG_MEMORY_DATA_LONG") - 1;
+ bitmask |= LOG_MEMORY_DATA_LONG;
+ } else if (strncmp(p, "LOG_MEMORY_PROTECTIONS",
+ sizeof("LOG_MEMORY_PROTECTIONS") - 1) == 0) {
+ p += sizeof("LOG_MEMORY_PROTECTIONS") - 1;
+ bitmask |= LOG_MEMORY_PROTECTIONS;
+ } else if (strncmp(p, "LOG_MEMORY", sizeof("LOG_MEMORY") - 1) == 0) {
+ p += sizeof("LOG_MEMORY") - 1;
+ bitmask |= LOG_MEMORY;
+ } else if (strncmp(p, "LOG_BREAKPOINTS",
+ sizeof("LOG_BREAKPOINTS") - 1) == 0) {
+ p += sizeof("LOG_BREAKPOINTS") - 1;
+ bitmask |= LOG_BREAKPOINTS;
+ } else if (strncmp(p, "LOG_EVENTS", sizeof("LOG_EVENTS") - 1) == 0) {
+ p += sizeof("LOG_EVENTS") - 1;
+ bitmask |= LOG_EVENTS;
+ } else if (strncmp(p, "LOG_WATCHPOINTS",
+ sizeof("LOG_WATCHPOINTS") - 1) == 0) {
+ p += sizeof("LOG_WATCHPOINTS") - 1;
+ bitmask |= LOG_WATCHPOINTS;
+ } else if (strncmp(p, "LOG_STEP", sizeof("LOG_STEP") - 1) == 0) {
+ p += sizeof("LOG_STEP") - 1;
+ bitmask |= LOG_STEP;
+ } else if (strncmp(p, "LOG_TASK", sizeof("LOG_TASK") - 1) == 0) {
+ p += sizeof("LOG_TASK") - 1;
+ bitmask |= LOG_TASK;
+ } else if (strncmp(p, "LOG_ALL", sizeof("LOG_ALL") - 1) == 0) {
+ p += sizeof("LOG_ALL") - 1;
+ bitmask |= LOG_ALL;
+ } else if (strncmp(p, "LOG_DEFAULT", sizeof("LOG_DEFAULT") - 1) == 0) {
+ p += sizeof("LOG_DEFAULT") - 1;
+ bitmask |= LOG_DEFAULT;
+ }
+ // end of auto-generated entries
+
+ else if (strncmp(p, "LOG_NONE", sizeof("LOG_NONE") - 1) == 0) {
+ p += sizeof("LOG_NONE") - 1;
+ bitmask = 0;
+ } else if (strncmp(p, "LOG_RNB_MINIMAL",
+ sizeof("LOG_RNB_MINIMAL") - 1) == 0) {
+ p += sizeof("LOG_RNB_MINIMAL") - 1;
+ bitmask |= LOG_RNB_MINIMAL;
+ } else if (strncmp(p, "LOG_RNB_MEDIUM", sizeof("LOG_RNB_MEDIUM") - 1) ==
+ 0) {
+ p += sizeof("LOG_RNB_MEDIUM") - 1;
+ bitmask |= LOG_RNB_MEDIUM;
+ } else if (strncmp(p, "LOG_RNB_MAX", sizeof("LOG_RNB_MAX") - 1) == 0) {
+ p += sizeof("LOG_RNB_MAX") - 1;
+ bitmask |= LOG_RNB_MAX;
+ } else if (strncmp(p, "LOG_RNB_COMM", sizeof("LOG_RNB_COMM") - 1) ==
+ 0) {
+ p += sizeof("LOG_RNB_COMM") - 1;
+ bitmask |= LOG_RNB_COMM;
+ } else if (strncmp(p, "LOG_RNB_REMOTE", sizeof("LOG_RNB_REMOTE") - 1) ==
+ 0) {
+ p += sizeof("LOG_RNB_REMOTE") - 1;
+ bitmask |= LOG_RNB_REMOTE;
+ } else if (strncmp(p, "LOG_RNB_EVENTS", sizeof("LOG_RNB_EVENTS") - 1) ==
+ 0) {
+ p += sizeof("LOG_RNB_EVENTS") - 1;
+ bitmask |= LOG_RNB_EVENTS;
+ } else if (strncmp(p, "LOG_RNB_PROC", sizeof("LOG_RNB_PROC") - 1) ==
+ 0) {
+ p += sizeof("LOG_RNB_PROC") - 1;
+ bitmask |= LOG_RNB_PROC;
+ } else if (strncmp(p, "LOG_RNB_PACKETS",
+ sizeof("LOG_RNB_PACKETS") - 1) == 0) {
+ p += sizeof("LOG_RNB_PACKETS") - 1;
+ bitmask |= LOG_RNB_PACKETS;
+ } else if (strncmp(p, "LOG_RNB_ALL", sizeof("LOG_RNB_ALL") - 1) == 0) {
+ p += sizeof("LOG_RNB_ALL") - 1;
+ bitmask |= LOG_RNB_ALL;
+ } else if (strncmp(p, "LOG_RNB_DEFAULT",
+ sizeof("LOG_RNB_DEFAULT") - 1) == 0) {
+ p += sizeof("LOG_RNB_DEFAULT") - 1;
+ bitmask |= LOG_RNB_DEFAULT;
+ } else if (strncmp(p, "LOG_DARWIN_LOG", sizeof("LOG_DARWIN_LOG") - 1) ==
+ 0) {
+ p += sizeof("LOG_DARWIN_LOG") - 1;
+ bitmask |= LOG_DARWIN_LOG;
+ } else if (strncmp(p, "LOG_RNB_NONE", sizeof("LOG_RNB_NONE") - 1) ==
+ 0) {
+ p += sizeof("LOG_RNB_NONE") - 1;
+ bitmask = 0;
+ } else {
+ /* Unrecognized logging bit; ignore it. */
+ const char *c = strchr(p, '|');
+ if (c) {
+ p = c;
+ } else {
+ c = strchr(p, ';');
+ if (c) {
+ p = c;
+ } else {
+ // Improperly terminated word; just go to end of str
+ p = strchr(p, '\0');
+ }
+ }
+ }
+ }
+ // Did we get a properly formatted logging bitmask?
+ if (p && *p == ';') {
+ // Enable DNB logging.
+ // Use the existing log callback if one was already configured.
+ if (!DNBLogGetLogCallback()) {
+ // Use the os_log()-based logger if available; otherwise,
+ // fallback to ASL.
+ auto log_callback = OsLogger::GetLogFunction();
+ if (log_callback)
+ DNBLogSetLogCallback(log_callback, nullptr);
+ else
+ DNBLogSetLogCallback(ASLLogCallback, nullptr);
+ }
+
+ // Update logging to use the configured log channel bitmask.
+ DNBLogSetLogMask(bitmask);
+ p++;
+ }
+ }
+// We're not going to support logging to a file for now. All logging
+// goes through ASL or the previously arranged log callback.
+#if 0
+ else if (strncmp (p, "mode=", sizeof ("mode=") - 1) == 0)
+ {
+ p += sizeof ("mode=") - 1;
+ if (strncmp (p, "asl;", sizeof ("asl;") - 1) == 0)
+ {
+ DNBLogToASL ();
+ p += sizeof ("asl;") - 1;
+ }
+ else if (strncmp (p, "file;", sizeof ("file;") - 1) == 0)
+ {
+ DNBLogToFile ();
+ p += sizeof ("file;") - 1;
+ }
+ else
+ {
+ // Ignore unknown argument
+ const char *c = strchr (p, ';');
+ if (c)
+ p = c + 1;
+ else
+ p = strchr (p, '\0');
+ }
+ }
+ else if (strncmp (p, "filename=", sizeof ("filename=") - 1) == 0)
+ {
+ p += sizeof ("filename=") - 1;
+ const char *c = strchr (p, ';');
+ if (c == NULL)
+ {
+ c = strchr (p, '\0');
+ continue;
+ }
+ char *fn = (char *) alloca (c - p + 1);
+ strlcpy (fn, p, c - p);
+ fn[c - p] = '\0';
+
+ // A file name of "asl" is special and is another way to indicate
+ // that logging should be done via ASL, not by file.
+ if (strcmp (fn, "asl") == 0)
+ {
+ DNBLogToASL ();
+ }
+ else
+ {
+ FILE *f = fopen (fn, "w");
+ if (f)
+ {
+ DNBLogSetLogFile (f);
+ DNBEnableLogging (f, DNBLogGetLogMask ());
+ DNBLogToFile ();
+ }
+ }
+ p = c + 1;
+ }
+#endif /* #if 0 to enforce ASL logging only. */
+ else {
+ // Ignore unknown argument
+ const char *c = strchr(p, ';');
+ if (c)
+ p = c + 1;
+ else
+ p = strchr(p, '\0');
+ }
+ }
+
+ return rnb_success;
+}
+
+rnb_err_t RNBRemote::HandlePacket_QThreadSuffixSupported(const char *p) {
+ m_thread_suffix_supported = true;
+ return SendPacket("OK");
+}
+
+rnb_err_t RNBRemote::HandlePacket_QStartNoAckMode(const char *p) {
+ // Send the OK packet first so the correct checksum is appended...
+ rnb_err_t result = SendPacket("OK");
+ m_noack_mode = true;
+ return result;
+}
+
+rnb_err_t RNBRemote::HandlePacket_QSetLogging(const char *p) {
+ p += sizeof("QSetLogging:") - 1;
+ rnb_err_t result = set_logging(p);
+ if (result == rnb_success)
+ return SendPacket("OK");
+ else
+ return SendPacket("E35");
+}
+
+rnb_err_t RNBRemote::HandlePacket_QSetDisableASLR(const char *p) {
+ extern int g_disable_aslr;
+ p += sizeof("QSetDisableASLR:") - 1;
+ switch (*p) {
+ case '0':
+ g_disable_aslr = 0;
+ break;
+ case '1':
+ g_disable_aslr = 1;
+ break;
+ default:
+ return SendPacket("E56");
+ }
+ return SendPacket("OK");
+}
+
+rnb_err_t RNBRemote::HandlePacket_QSetSTDIO(const char *p) {
+ // Only set stdin/out/err if we don't already have a process
+ if (!m_ctx.HasValidProcessID()) {
+ bool success = false;
+ // Check the seventh character since the packet will be one of:
+ // QSetSTDIN
+ // QSetSTDOUT
+ // QSetSTDERR
+ StdStringExtractor packet(p);
+ packet.SetFilePos(7);
+ char ch = packet.GetChar();
+ while (packet.GetChar() != ':')
+ /* Do nothing. */;
+
+ switch (ch) {
+ case 'I': // STDIN
+ packet.GetHexByteString(m_ctx.GetSTDIN());
+ success = !m_ctx.GetSTDIN().empty();
+ break;
+
+ case 'O': // STDOUT
+ packet.GetHexByteString(m_ctx.GetSTDOUT());
+ success = !m_ctx.GetSTDOUT().empty();
+ break;
+
+ case 'E': // STDERR
+ packet.GetHexByteString(m_ctx.GetSTDERR());
+ success = !m_ctx.GetSTDERR().empty();
+ break;
+
+ default:
+ break;
+ }
+ if (success)
+ return SendPacket("OK");
+ return SendPacket("E57");
+ }
+ return SendPacket("E58");
+}
+
+rnb_err_t RNBRemote::HandlePacket_QSetWorkingDir(const char *p) {
+ // Only set the working directory if we don't already have a process
+ if (!m_ctx.HasValidProcessID()) {
+ StdStringExtractor packet(p += sizeof("QSetWorkingDir:") - 1);
+ if (packet.GetHexByteString(m_ctx.GetWorkingDir())) {
+ struct stat working_dir_stat;
+ if (::stat(m_ctx.GetWorkingDirPath(), &working_dir_stat) == -1) {
+ m_ctx.GetWorkingDir().clear();
+ return SendPacket("E61"); // Working directory doesn't exist...
+ } else if ((working_dir_stat.st_mode & S_IFMT) == S_IFDIR) {
+ return SendPacket("OK");
+ } else {
+ m_ctx.GetWorkingDir().clear();
+ return SendPacket("E62"); // Working directory isn't a directory...
+ }
+ }
+ return SendPacket("E59"); // Invalid path
+ }
+ return SendPacket(
+ "E60"); // Already had a process, too late to set working dir
+}
+
+rnb_err_t RNBRemote::HandlePacket_QSyncThreadState(const char *p) {
+ if (!m_ctx.HasValidProcessID()) {
+ // We allow gdb to connect to a server that hasn't started running
+ // the target yet. gdb still wants to ask questions about it and
+ // freaks out if it gets an error. So just return OK here.
+ return SendPacket("OK");
+ }
+
+ errno = 0;
+ p += strlen("QSyncThreadState:");
+ nub_thread_t tid = strtoul(p, NULL, 16);
+ if (errno != 0 && tid == 0) {
+ return HandlePacket_ILLFORMED(
+ __FILE__, __LINE__, p,
+ "Invalid thread number in QSyncThreadState packet");
+ }
+ if (DNBProcessSyncThreadState(m_ctx.ProcessID(), tid))
+ return SendPacket("OK");
+ else
+ return SendPacket("E61");
+}
+
+rnb_err_t RNBRemote::HandlePacket_QSetDetachOnError(const char *p) {
+ p += sizeof("QSetDetachOnError:") - 1;
+ bool should_detach = true;
+ switch (*p) {
+ case '0':
+ should_detach = false;
+ break;
+ case '1':
+ should_detach = true;
+ break;
+ default:
+ return HandlePacket_ILLFORMED(
+ __FILE__, __LINE__, p,
+ "Invalid value for QSetDetachOnError - should be 0 or 1");
+ break;
+ }
+
+ m_ctx.SetDetachOnError(should_detach);
+ return SendPacket("OK");
+}
+
+rnb_err_t RNBRemote::HandlePacket_qStructuredDataPlugins(const char *p) {
+ // We'll return a JSON array of supported packet types.
+ // The type is significant. For each of the supported
+ // packet types that have been enabled, there will be a
+ // 'J' async packet sent to the client with payload data.
+ // This payload data will be a JSON dictionary, and the
+ // top level dictionary will contain a string field with
+ // its value set to the relevant packet type from this list.
+ JSONGenerator::Array supported_json_packets;
+
+ // Check for DarwinLog (libtrace os_log/activity support).
+ if (DarwinLogCollector::IsSupported())
+ supported_json_packets.AddItem(
+ JSONGenerator::StringSP(new JSONGenerator::String("DarwinLog")));
+
+ // Send back the array.
+ std::ostringstream stream;
+ supported_json_packets.Dump(stream);
+ return SendPacket(stream.str());
+}
+
+rnb_err_t RNBRemote::HandlePacket_QConfigureDarwinLog(const char *p) {
+ if (!DarwinLogCollector::IsSupported()) {
+ // We should never have been given this request.
+ return SendPacket("E89");
+ }
+
+ // Ensure we have a process. We expect a separate configure request for
+ // each process launched/attached.
+ const nub_process_t pid = m_ctx.ProcessID();
+ if (pid == INVALID_NUB_PROCESS)
+ return SendPacket("E94");
+
+ // Get the configuration dictionary.
+ p += strlen("QConfigureDarwinLog:");
+
+ // The configuration dictionary is binary encoded.
+ std::vector<uint8_t> unescaped_config_data = decode_binary_data(p, -1);
+ std::string unescaped_config_string((const char *)&unescaped_config_data[0],
+ unescaped_config_data.size());
+ DNBLogThreadedIf(LOG_DARWIN_LOG, "DarwinLog: received config data: \"%s\"",
+ unescaped_config_string.c_str());
+ auto configuration_sp =
+ JSONParser(unescaped_config_string.c_str()).ParseJSONValue();
+ if (!configuration_sp) {
+ // Malformed request - we require configuration data
+ // indicating whether we're enabling or disabling.
+ return SendPacket("E90");
+ }
+
+ if (!JSONObject::classof(configuration_sp.get())) {
+ // Configuration data is not of the right type.
+ return SendPacket("E91");
+ }
+ JSONObject &config_dict = *static_cast<JSONObject *>(configuration_sp.get());
+
+ // Check if we're enabling or disabling.
+ auto enabled_sp = config_dict.GetObject("enabled");
+ if (!enabled_sp) {
+ // Missing required "enabled" field.
+ return SendPacket("E92");
+ }
+ if (!JSONTrue::classof(enabled_sp.get()) &&
+ !JSONFalse::classof(enabled_sp.get())) {
+ // Should be a boolean type, but wasn't.
+ return SendPacket("E93");
+ }
+ const bool enabling = JSONTrue::classof(enabled_sp.get());
+
+ // TODO - handle other configuration parameters here.
+
+ // Shut down any active activity stream for the process.
+ DarwinLogCollector::CancelStreamForProcess(pid);
+
+ if (enabling) {
+ // Look up the procecess.
+ if (!DarwinLogCollector::StartCollectingForProcess(pid, config_dict))
+ return SendPacket("E95");
+ }
+
+ return SendPacket("OK");
+}
+
+rnb_err_t RNBRemote::HandlePacket_QListThreadsInStopReply(const char *p) {
+ // If this packet is received, it allows us to send an extra key/value
+ // pair in the stop reply packets where we will list all of the thread IDs
+ // separated by commas:
+ //
+ // "threads:10a,10b,10c;"
+ //
+ // This will get included in the stop reply packet as something like:
+ //
+ // "T11thread:10a;00:00000000;01:00010203:threads:10a,10b,10c;"
+ //
+ // This can save two packets on each stop: qfThreadInfo/qsThreadInfo and
+ // speed things up a bit.
+ //
+ // Send the OK packet first so the correct checksum is appended...
+ rnb_err_t result = SendPacket("OK");
+ m_list_threads_in_stop_reply = true;
+
+ return result;
+}
+
+rnb_err_t RNBRemote::HandlePacket_QSetMaxPayloadSize(const char *p) {
+ /* The number of characters in a packet payload that gdb is
+ prepared to accept. The packet-start char, packet-end char,
+ 2 checksum chars and terminating null character are not included
+ in this size. */
+ p += sizeof("QSetMaxPayloadSize:") - 1;
+ errno = 0;
+ uint32_t size = static_cast<uint32_t>(strtoul(p, NULL, 16));
+ if (errno != 0 && size == 0) {
+ return HandlePacket_ILLFORMED(
+ __FILE__, __LINE__, p, "Invalid length in QSetMaxPayloadSize packet");
+ }
+ m_max_payload_size = size;
+ return SendPacket("OK");
+}
+
+rnb_err_t RNBRemote::HandlePacket_QSetMaxPacketSize(const char *p) {
+ /* This tells us the largest packet that gdb can handle.
+ i.e. the size of gdb's packet-reading buffer.
+ QSetMaxPayloadSize is preferred because it is less ambiguous. */
+ p += sizeof("QSetMaxPacketSize:") - 1;
+ errno = 0;
+ uint32_t size = static_cast<uint32_t>(strtoul(p, NULL, 16));
+ if (errno != 0 && size == 0) {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid length in QSetMaxPacketSize packet");
+ }
+ m_max_payload_size = size - 5;
+ return SendPacket("OK");
+}
+
+rnb_err_t RNBRemote::HandlePacket_QEnvironment(const char *p) {
+ /* This sets the environment for the target program. The packet is of the
+ form:
+
+ QEnvironment:VARIABLE=VALUE
+
+ */
+
+ DNBLogThreadedIf(
+ LOG_RNB_REMOTE, "%8u RNBRemote::%s Handling QEnvironment: \"%s\"",
+ (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, p);
+
+ p += sizeof("QEnvironment:") - 1;
+ RNBContext &ctx = Context();
+
+ ctx.PushEnvironment(p);
+ return SendPacket("OK");
+}
+
+rnb_err_t RNBRemote::HandlePacket_QEnvironmentHexEncoded(const char *p) {
+ /* This sets the environment for the target program. The packet is of the
+ form:
+
+ QEnvironmentHexEncoded:VARIABLE=VALUE
+
+ The VARIABLE=VALUE part is sent hex-encoded so characters like '#' with
+ special
+ meaning in the remote protocol won't break it.
+ */
+
+ DNBLogThreadedIf(LOG_RNB_REMOTE,
+ "%8u RNBRemote::%s Handling QEnvironmentHexEncoded: \"%s\"",
+ (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true),
+ __FUNCTION__, p);
+
+ p += sizeof("QEnvironmentHexEncoded:") - 1;
+
+ std::string arg;
+ const char *c;
+ c = p;
+ while (*c != '\0') {
+ if (*(c + 1) == '\0') {
+ return HandlePacket_ILLFORMED(
+ __FILE__, __LINE__, p,
+ "non-hex char in arg on 'QEnvironmentHexEncoded' pkt");
+ }
+ char smallbuf[3];
+ smallbuf[0] = *c;
+ smallbuf[1] = *(c + 1);
+ smallbuf[2] = '\0';
+ errno = 0;
+ int ch = static_cast<int>(strtoul(smallbuf, NULL, 16));
+ if (errno != 0 && ch == 0) {
+ return HandlePacket_ILLFORMED(
+ __FILE__, __LINE__, p,
+ "non-hex char in arg on 'QEnvironmentHexEncoded' pkt");
+ }
+ arg.push_back(ch);
+ c += 2;
+ }
+
+ RNBContext &ctx = Context();
+ if (arg.length() > 0)
+ ctx.PushEnvironment(arg.c_str());
+
+ return SendPacket("OK");
+}
+
+rnb_err_t RNBRemote::HandlePacket_QLaunchArch(const char *p) {
+ p += sizeof("QLaunchArch:") - 1;
+ if (DNBSetArchitecture(p))
+ return SendPacket("OK");
+ return SendPacket("E63");
+}
+
+rnb_err_t RNBRemote::HandlePacket_QSetProcessEvent(const char *p) {
+ p += sizeof("QSetProcessEvent:") - 1;
+ // If the process is running, then send the event to the process, otherwise
+ // store it in the context.
+ if (Context().HasValidProcessID()) {
+ if (DNBProcessSendEvent(Context().ProcessID(), p))
+ return SendPacket("OK");
+ else
+ return SendPacket("E80");
+ } else {
+ Context().PushProcessEvent(p);
+ }
+ return SendPacket("OK");
+}
+
+void append_hex_value(std::ostream &ostrm, const void *buf, size_t buf_size,
+ bool swap) {
+ int i;
+ const uint8_t *p = (const uint8_t *)buf;
+ if (swap) {
+ for (i = static_cast<int>(buf_size) - 1; i >= 0; i--)
+ ostrm << RAWHEX8(p[i]);
+ } else {
+ for (size_t i = 0; i < buf_size; i++)
+ ostrm << RAWHEX8(p[i]);
+ }
+}
+
+std::string cstring_to_asciihex_string(const char *str) {
+ std::string hex_str;
+ hex_str.reserve (strlen (str) * 2);
+ while (str && *str) {
+ char hexbuf[5];
+ snprintf (hexbuf, sizeof(hexbuf), "%02x", *str++);
+ hex_str += hexbuf;
+ }
+ return hex_str;
+}
+
+void append_hexified_string(std::ostream &ostrm, const std::string &string) {
+ size_t string_size = string.size();
+ const char *string_buf = string.c_str();
+ for (size_t i = 0; i < string_size; i++) {
+ ostrm << RAWHEX8(*(string_buf + i));
+ }
+}
+
+void register_value_in_hex_fixed_width(std::ostream &ostrm, nub_process_t pid,
+ nub_thread_t tid,
+ const register_map_entry_t *reg,
+ const DNBRegisterValue *reg_value_ptr) {
+ if (reg != NULL) {
+ DNBRegisterValue reg_value;
+ if (reg_value_ptr == NULL) {
+ if (DNBThreadGetRegisterValueByID(pid, tid, reg->nub_info.set,
+ reg->nub_info.reg, &reg_value))
+ reg_value_ptr = &reg_value;
+ }
+
+ if (reg_value_ptr) {
+ append_hex_value(ostrm, reg_value_ptr->value.v_uint8, reg->nub_info.size,
+ false);
+ } else {
+ // If we fail to read a register value, check if it has a default
+ // fail value. If it does, return this instead in case some of
+ // the registers are not available on the current system.
+ if (reg->nub_info.size > 0) {
+ std::basic_string<uint8_t> zeros(reg->nub_info.size, '\0');
+ append_hex_value(ostrm, zeros.data(), zeros.size(), false);
+ }
+ }
+ }
+}
+
+void debugserver_regnum_with_fixed_width_hex_register_value(
+ std::ostream &ostrm, nub_process_t pid, nub_thread_t tid,
+ const register_map_entry_t *reg, const DNBRegisterValue *reg_value_ptr) {
+ // Output the register number as 'NN:VVVVVVVV;' where NN is a 2 bytes HEX
+ // gdb register number, and VVVVVVVV is the correct number of hex bytes
+ // as ASCII for the register value.
+ if (reg != NULL) {
+ ostrm << RAWHEX8(reg->debugserver_regnum) << ':';
+ register_value_in_hex_fixed_width(ostrm, pid, tid, reg, reg_value_ptr);
+ ostrm << ';';
+ }
+}
+
+void RNBRemote::DispatchQueueOffsets::GetThreadQueueInfo(
+ nub_process_t pid, nub_addr_t dispatch_qaddr, nub_addr_t &dispatch_queue_t,
+ std::string &queue_name, uint64_t &queue_width,
+ uint64_t &queue_serialnum) const {
+ queue_name.clear();
+ queue_width = 0;
+ queue_serialnum = 0;
+
+ if (IsValid() && dispatch_qaddr != INVALID_NUB_ADDRESS &&
+ dispatch_qaddr != 0) {
+ dispatch_queue_t = DNBProcessMemoryReadPointer(pid, dispatch_qaddr);
+ if (dispatch_queue_t) {
+ queue_width = DNBProcessMemoryReadInteger(
+ pid, dispatch_queue_t + dqo_width, dqo_width_size, 0);
+ queue_serialnum = DNBProcessMemoryReadInteger(
+ pid, dispatch_queue_t + dqo_serialnum, dqo_serialnum_size, 0);
+
+ if (dqo_version >= 4) {
+ // libdispatch versions 4+, pointer to dispatch name is in the
+ // queue structure.
+ nub_addr_t pointer_to_label_address = dispatch_queue_t + dqo_label;
+ nub_addr_t label_addr =
+ DNBProcessMemoryReadPointer(pid, pointer_to_label_address);
+ if (label_addr)
+ queue_name = DNBProcessMemoryReadCString(pid, label_addr);
+ } else {
+ // libdispatch versions 1-3, dispatch name is a fixed width char array
+ // in the queue structure.
+ queue_name = DNBProcessMemoryReadCStringFixed(
+ pid, dispatch_queue_t + dqo_label, dqo_label_size);
+ }
+ }
+ }
+}
+
+struct StackMemory {
+ uint8_t bytes[2 * sizeof(nub_addr_t)];
+ nub_size_t length;
+};
+typedef std::map<nub_addr_t, StackMemory> StackMemoryMap;
+
+static void ReadStackMemory(nub_process_t pid, nub_thread_t tid,
+ StackMemoryMap &stack_mmap,
+ uint32_t backtrace_limit = 256) {
+ DNBRegisterValue reg_value;
+ if (DNBThreadGetRegisterValueByID(pid, tid, REGISTER_SET_GENERIC,
+ GENERIC_REGNUM_FP, &reg_value)) {
+ uint32_t frame_count = 0;
+ uint64_t fp = 0;
+ if (reg_value.info.size == 4)
+ fp = reg_value.value.uint32;
+ else
+ fp = reg_value.value.uint64;
+ while (fp != 0) {
+ // Make sure we never recurse more than 256 times so we don't recurse too
+ // far or
+ // store up too much memory in the expedited cache
+ if (++frame_count > backtrace_limit)
+ break;
+
+ const nub_size_t read_size = reg_value.info.size * 2;
+ StackMemory stack_memory;
+ stack_memory.length = read_size;
+ if (DNBProcessMemoryRead(pid, fp, read_size, stack_memory.bytes) !=
+ read_size)
+ break;
+ // Make sure we don't try to put the same stack memory in more than once
+ if (stack_mmap.find(fp) != stack_mmap.end())
+ break;
+ // Put the entry into the cache
+ stack_mmap[fp] = stack_memory;
+ // Dereference the frame pointer to get to the previous frame pointer
+ if (reg_value.info.size == 4)
+ fp = ((uint32_t *)stack_memory.bytes)[0];
+ else
+ fp = ((uint64_t *)stack_memory.bytes)[0];
+ }
+ }
+}
+
+rnb_err_t RNBRemote::SendStopReplyPacketForThread(nub_thread_t tid) {
+ const nub_process_t pid = m_ctx.ProcessID();
+ if (pid == INVALID_NUB_PROCESS)
+ return SendPacket("E50");
+
+ struct DNBThreadStopInfo tid_stop_info;
+
+ /* Fill the remaining space in this packet with as many registers
+ as we can stuff in there. */
+
+ if (DNBThreadGetStopReason(pid, tid, &tid_stop_info)) {
+ const bool did_exec = tid_stop_info.reason == eStopTypeExec;
+ if (did_exec) {
+ RNBRemote::InitializeRegisters(true);
+
+ // Reset any symbols that need resetting when we exec
+ m_dispatch_queue_offsets_addr = INVALID_NUB_ADDRESS;
+ m_dispatch_queue_offsets.Clear();
+ }
+
+ std::ostringstream ostrm;
+ // Output the T packet with the thread
+ ostrm << 'T';
+ int signum = tid_stop_info.details.signal.signo;
+ DNBLogThreadedIf(
+ LOG_RNB_PROC, "%8d %s got signal signo = %u, exc_type = %u",
+ (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__,
+ signum, tid_stop_info.details.exception.type);
+
+ // Translate any mach exceptions to gdb versions, unless they are
+ // common exceptions like a breakpoint or a soft signal.
+ switch (tid_stop_info.details.exception.type) {
+ default:
+ signum = 0;
+ break;
+ case EXC_BREAKPOINT:
+ signum = SIGTRAP;
+ break;
+ case EXC_BAD_ACCESS:
+ signum = TARGET_EXC_BAD_ACCESS;
+ break;
+ case EXC_BAD_INSTRUCTION:
+ signum = TARGET_EXC_BAD_INSTRUCTION;
+ break;
+ case EXC_ARITHMETIC:
+ signum = TARGET_EXC_ARITHMETIC;
+ break;
+ case EXC_EMULATION:
+ signum = TARGET_EXC_EMULATION;
+ break;
+ case EXC_SOFTWARE:
+ if (tid_stop_info.details.exception.data_count == 2 &&
+ tid_stop_info.details.exception.data[0] == EXC_SOFT_SIGNAL)
+ signum = static_cast<int>(tid_stop_info.details.exception.data[1]);
+ else
+ signum = TARGET_EXC_SOFTWARE;
+ break;
+ }
+
+ ostrm << RAWHEX8(signum & 0xff);
+
+ ostrm << std::hex << "thread:" << tid << ';';
+
+ const char *thread_name = DNBThreadGetName(pid, tid);
+ if (thread_name && thread_name[0]) {
+ size_t thread_name_len = strlen(thread_name);
+
+ if (::strcspn(thread_name, "$#+-;:") == thread_name_len)
+ ostrm << std::hex << "name:" << thread_name << ';';
+ else {
+ // the thread name contains special chars, send as hex bytes
+ ostrm << std::hex << "hexname:";
+ const uint8_t *u_thread_name = (const uint8_t *)thread_name;
+ for (size_t i = 0; i < thread_name_len; i++)
+ ostrm << RAWHEX8(u_thread_name[i]);
+ ostrm << ';';
+ }
+ }
+
+ // If a 'QListThreadsInStopReply' was sent to enable this feature, we
+ // will send all thread IDs back in the "threads" key whose value is
+ // a list of hex thread IDs separated by commas:
+ // "threads:10a,10b,10c;"
+ // This will save the debugger from having to send a pair of qfThreadInfo
+ // and qsThreadInfo packets, but it also might take a lot of room in the
+ // stop reply packet, so it must be enabled only on systems where there
+ // are no limits on packet lengths.
+ if (m_list_threads_in_stop_reply) {
+ const nub_size_t numthreads = DNBProcessGetNumThreads(pid);
+ if (numthreads > 0) {
+ std::vector<uint64_t> pc_values;
+ ostrm << std::hex << "threads:";
+ for (nub_size_t i = 0; i < numthreads; ++i) {
+ nub_thread_t th = DNBProcessGetThreadAtIndex(pid, i);
+ if (i > 0)
+ ostrm << ',';
+ ostrm << std::hex << th;
+ DNBRegisterValue pc_regval;
+ if (DNBThreadGetRegisterValueByID(pid, th, REGISTER_SET_GENERIC,
+ GENERIC_REGNUM_PC, &pc_regval)) {
+ uint64_t pc = INVALID_NUB_ADDRESS;
+ if (pc_regval.value.uint64 != INVALID_NUB_ADDRESS) {
+ if (pc_regval.info.size == 4) {
+ pc = pc_regval.value.uint32;
+ } else if (pc_regval.info.size == 8) {
+ pc = pc_regval.value.uint64;
+ }
+ if (pc != INVALID_NUB_ADDRESS) {
+ pc_values.push_back(pc);
+ }
+ }
+ }
+ }
+ ostrm << ';';
+
+ // If we failed to get any of the thread pc values, the size of our
+ // vector will not
+ // be the same as the # of threads. Don't provide any expedited thread
+ // pc values in
+ // that case. This should not happen.
+ if (pc_values.size() == numthreads) {
+ ostrm << std::hex << "thread-pcs:";
+ for (nub_size_t i = 0; i < numthreads; ++i) {
+ if (i > 0)
+ ostrm << ',';
+ ostrm << std::hex << pc_values[i];
+ }
+ ostrm << ';';
+ }
+ }
+
+ // Include JSON info that describes the stop reason for any threads
+ // that actually have stop reasons. We use the new "jstopinfo" key
+ // whose values is hex ascii JSON that contains the thread IDs
+ // thread stop info only for threads that have stop reasons. Only send
+ // this if we have more than one thread otherwise this packet has all
+ // the info it needs.
+ if (numthreads > 1) {
+ const bool threads_with_valid_stop_info_only = true;
+ JSONGenerator::ObjectSP threads_info_sp =
+ GetJSONThreadsInfo(threads_with_valid_stop_info_only);
+ if (threads_info_sp) {
+ ostrm << std::hex << "jstopinfo:";
+ std::ostringstream json_strm;
+ threads_info_sp->Dump(json_strm);
+ append_hexified_string(ostrm, json_strm.str());
+ ostrm << ';';
+ }
+ }
+ }
+
+ if (g_num_reg_entries == 0)
+ InitializeRegisters();
+
+ if (g_reg_entries != NULL) {
+ DNBRegisterValue reg_value;
+ for (uint32_t reg = 0; reg < g_num_reg_entries; reg++) {
+ // Expedite all registers in the first register set that aren't
+ // contained in other registers
+ if (g_reg_entries[reg].nub_info.set == 1 &&
+ g_reg_entries[reg].nub_info.value_regs == NULL) {
+ if (!DNBThreadGetRegisterValueByID(
+ pid, tid, g_reg_entries[reg].nub_info.set,
+ g_reg_entries[reg].nub_info.reg, &reg_value))
+ continue;
+
+ debugserver_regnum_with_fixed_width_hex_register_value(
+ ostrm, pid, tid, &g_reg_entries[reg], &reg_value);
+ }
+ }
+ }
+
+ if (did_exec) {
+ ostrm << "reason:exec;";
+ } else if (tid_stop_info.details.exception.type) {
+ ostrm << "metype:" << std::hex << tid_stop_info.details.exception.type
+ << ';';
+ ostrm << "mecount:" << std::hex
+ << tid_stop_info.details.exception.data_count << ';';
+ for (nub_size_t i = 0; i < tid_stop_info.details.exception.data_count;
+ ++i)
+ ostrm << "medata:" << std::hex
+ << tid_stop_info.details.exception.data[i] << ';';
+ }
+
+ // Add expedited stack memory so stack backtracing doesn't need to read
+ // anything from the
+ // frame pointer chain.
+ StackMemoryMap stack_mmap;
+ ReadStackMemory(pid, tid, stack_mmap, 2);
+ if (!stack_mmap.empty()) {
+ for (const auto &stack_memory : stack_mmap) {
+ ostrm << "memory:" << HEXBASE << stack_memory.first << '=';
+ append_hex_value(ostrm, stack_memory.second.bytes,
+ stack_memory.second.length, false);
+ ostrm << ';';
+ }
+ }
+
+ return SendPacket(ostrm.str());
+ }
+ return SendPacket("E51");
+}
+
+/* '?'
+ The stop reply packet - tell gdb what the status of the inferior is.
+ Often called the questionmark_packet. */
+
+rnb_err_t RNBRemote::HandlePacket_last_signal(const char *unused) {
+ if (!m_ctx.HasValidProcessID()) {
+ // Inferior is not yet specified/running
+ return SendPacket("E02");
+ }
+
+ nub_process_t pid = m_ctx.ProcessID();
+ nub_state_t pid_state = DNBProcessGetState(pid);
+
+ switch (pid_state) {
+ case eStateAttaching:
+ case eStateLaunching:
+ case eStateRunning:
+ case eStateStepping:
+ case eStateDetached:
+ return rnb_success; // Ignore
+
+ case eStateSuspended:
+ case eStateStopped:
+ case eStateCrashed: {
+ nub_thread_t tid = DNBProcessGetCurrentThread(pid);
+ // Make sure we set the current thread so g and p packets return
+ // the data the gdb will expect.
+ SetCurrentThread(tid);
+
+ SendStopReplyPacketForThread(tid);
+ } break;
+
+ case eStateInvalid:
+ case eStateUnloaded:
+ case eStateExited: {
+ char pid_exited_packet[16] = "";
+ int pid_status = 0;
+ // Process exited with exit status
+ if (!DNBProcessGetExitStatus(pid, &pid_status))
+ pid_status = 0;
+
+ if (pid_status) {
+ if (WIFEXITED(pid_status))
+ snprintf(pid_exited_packet, sizeof(pid_exited_packet), "W%02x",
+ WEXITSTATUS(pid_status));
+ else if (WIFSIGNALED(pid_status))
+ snprintf(pid_exited_packet, sizeof(pid_exited_packet), "X%02x",
+ WEXITSTATUS(pid_status));
+ else if (WIFSTOPPED(pid_status))
+ snprintf(pid_exited_packet, sizeof(pid_exited_packet), "S%02x",
+ WSTOPSIG(pid_status));
+ }
+
+ // If we have an empty exit packet, lets fill one in to be safe.
+ if (!pid_exited_packet[0]) {
+ strlcpy(pid_exited_packet, "W00", sizeof(pid_exited_packet) - 1);
+ pid_exited_packet[sizeof(pid_exited_packet) - 1] = '\0';
+ }
+
+ const char *exit_info = DNBProcessGetExitInfo(pid);
+ if (exit_info != NULL && *exit_info != '\0') {
+ std::ostringstream exit_packet;
+ exit_packet << pid_exited_packet;
+ exit_packet << ';';
+ exit_packet << RAW_HEXBASE << "description";
+ exit_packet << ':';
+ for (size_t i = 0; exit_info[i] != '\0'; i++)
+ exit_packet << RAWHEX8(exit_info[i]);
+ exit_packet << ';';
+ return SendPacket(exit_packet.str());
+ } else
+ return SendPacket(pid_exited_packet);
+ } break;
+ }
+ return rnb_success;
+}
+
+rnb_err_t RNBRemote::HandlePacket_M(const char *p) {
+ if (p == NULL || p[0] == '\0' || strlen(p) < 3) {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Too short M packet");
+ }
+
+ char *c;
+ p++;
+ errno = 0;
+ nub_addr_t addr = strtoull(p, &c, 16);
+ if (errno != 0 && addr == 0) {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid address in M packet");
+ }
+ if (*c != ',') {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Comma sep missing in M packet");
+ }
+
+ /* Advance 'p' to the length part of the packet. */
+ p += (c - p) + 1;
+
+ errno = 0;
+ unsigned long length = strtoul(p, &c, 16);
+ if (errno != 0 && length == 0) {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid length in M packet");
+ }
+ if (length == 0) {
+ return SendPacket("OK");
+ }
+
+ if (*c != ':') {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Missing colon in M packet");
+ }
+ /* Advance 'p' to the data part of the packet. */
+ p += (c - p) + 1;
+
+ size_t datalen = strlen(p);
+ if (datalen & 0x1) {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Uneven # of hex chars for data in M packet");
+ }
+ if (datalen == 0) {
+ return SendPacket("OK");
+ }
+
+ uint8_t *buf = (uint8_t *)alloca(datalen / 2);
+ uint8_t *i = buf;
+
+ while (*p != '\0' && *(p + 1) != '\0') {
+ char hexbuf[3];
+ hexbuf[0] = *p;
+ hexbuf[1] = *(p + 1);
+ hexbuf[2] = '\0';
+ errno = 0;
+ uint8_t byte = strtoul(hexbuf, NULL, 16);
+ if (errno != 0 && byte == 0) {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid hex byte in M packet");
+ }
+ *i++ = byte;
+ p += 2;
+ }
+
+ nub_size_t wrote =
+ DNBProcessMemoryWrite(m_ctx.ProcessID(), addr, length, buf);
+ if (wrote != length)
+ return SendPacket("E09");
+ else
+ return SendPacket("OK");
+}
+
+rnb_err_t RNBRemote::HandlePacket_m(const char *p) {
+ if (p == NULL || p[0] == '\0' || strlen(p) < 3) {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Too short m packet");
+ }
+
+ char *c;
+ p++;
+ errno = 0;
+ nub_addr_t addr = strtoull(p, &c, 16);
+ if (errno != 0 && addr == 0) {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid address in m packet");
+ }
+ if (*c != ',') {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Comma sep missing in m packet");
+ }
+
+ /* Advance 'p' to the length part of the packet. */
+ p += (c - p) + 1;
+
+ errno = 0;
+ auto length = strtoul(p, NULL, 16);
+ if (errno != 0 && length == 0) {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid length in m packet");
+ }
+ if (length == 0) {
+ return SendPacket("");
+ }
+
+ std::string buf(length, '\0');
+ if (buf.empty()) {
+ return SendPacket("E78");
+ }
+ nub_size_t bytes_read =
+ DNBProcessMemoryRead(m_ctx.ProcessID(), addr, buf.size(), &buf[0]);
+ if (bytes_read == 0) {
+ return SendPacket("E08");
+ }
+
+ // "The reply may contain fewer bytes than requested if the server was able
+ // to read only part of the region of memory."
+ length = bytes_read;
+
+ std::ostringstream ostrm;
+ for (unsigned long i = 0; i < length; i++)
+ ostrm << RAWHEX8(buf[i]);
+ return SendPacket(ostrm.str());
+}
+
+// Read memory, sent it up as binary data.
+// Usage: xADDR,LEN
+// ADDR and LEN are both base 16.
+
+// Responds with 'OK' for zero-length request
+// or
+//
+// DATA
+//
+// where DATA is the binary data payload.
+
+rnb_err_t RNBRemote::HandlePacket_x(const char *p) {
+ if (p == NULL || p[0] == '\0' || strlen(p) < 3) {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Too short X packet");
+ }
+
+ char *c;
+ p++;
+ errno = 0;
+ nub_addr_t addr = strtoull(p, &c, 16);
+ if (errno != 0) {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid address in X packet");
+ }
+ if (*c != ',') {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Comma sep missing in X packet");
+ }
+
+ /* Advance 'p' to the number of bytes to be read. */
+ p += (c - p) + 1;
+
+ errno = 0;
+ auto length = strtoul(p, NULL, 16);
+ if (errno != 0) {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid length in x packet");
+ }
+
+ // zero length read means this is a test of whether that packet is implemented
+ // or not.
+ if (length == 0) {
+ return SendPacket("OK");
+ }
+
+ std::vector<uint8_t> buf(length);
+
+ if (buf.capacity() != length) {
+ return SendPacket("E79");
+ }
+ nub_size_t bytes_read =
+ DNBProcessMemoryRead(m_ctx.ProcessID(), addr, buf.size(), &buf[0]);
+ if (bytes_read == 0) {
+ return SendPacket("E80");
+ }
+
+ std::vector<uint8_t> buf_quoted;
+ buf_quoted.reserve(bytes_read + 30);
+ for (nub_size_t i = 0; i < bytes_read; i++) {
+ if (buf[i] == '#' || buf[i] == '$' || buf[i] == '}' || buf[i] == '*') {
+ buf_quoted.push_back(0x7d);
+ buf_quoted.push_back(buf[i] ^ 0x20);
+ } else {
+ buf_quoted.push_back(buf[i]);
+ }
+ }
+ length = buf_quoted.size();
+
+ std::ostringstream ostrm;
+ for (unsigned long i = 0; i < length; i++)
+ ostrm << buf_quoted[i];
+
+ return SendPacket(ostrm.str());
+}
+
+rnb_err_t RNBRemote::HandlePacket_X(const char *p) {
+ if (p == NULL || p[0] == '\0' || strlen(p) < 3) {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Too short X packet");
+ }
+
+ char *c;
+ p++;
+ errno = 0;
+ nub_addr_t addr = strtoull(p, &c, 16);
+ if (errno != 0 && addr == 0) {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid address in X packet");
+ }
+ if (*c != ',') {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Comma sep missing in X packet");
+ }
+
+ /* Advance 'p' to the length part of the packet. NB this is the length of the
+ packet
+ including any escaped chars. The data payload may be a little bit smaller
+ after
+ decoding. */
+ p += (c - p) + 1;
+
+ errno = 0;
+ auto length = strtoul(p, NULL, 16);
+ if (errno != 0 && length == 0) {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid length in X packet");
+ }
+
+ // I think gdb sends a zero length write request to test whether this
+ // packet is accepted.
+ if (length == 0) {
+ return SendPacket("OK");
+ }
+
+ std::vector<uint8_t> data = decode_binary_data(c, -1);
+ std::vector<uint8_t>::const_iterator it;
+ uint8_t *buf = (uint8_t *)alloca(data.size());
+ uint8_t *i = buf;
+ for (it = data.begin(); it != data.end(); ++it) {
+ *i++ = *it;
+ }
+
+ nub_size_t wrote =
+ DNBProcessMemoryWrite(m_ctx.ProcessID(), addr, data.size(), buf);
+ if (wrote != data.size())
+ return SendPacket("E08");
+ return SendPacket("OK");
+}
+
+/* 'g' -- read registers
+ Get the contents of the registers for the current thread,
+ send them to gdb.
+ Should the setting of the Hg packet determine which thread's registers
+ are returned? */
+
+rnb_err_t RNBRemote::HandlePacket_g(const char *p) {
+ std::ostringstream ostrm;
+ if (!m_ctx.HasValidProcessID()) {
+ return SendPacket("E11");
+ }
+
+ if (g_num_reg_entries == 0)
+ InitializeRegisters();
+
+ nub_process_t pid = m_ctx.ProcessID();
+ nub_thread_t tid = ExtractThreadIDFromThreadSuffix(p + 1);
+ if (tid == INVALID_NUB_THREAD)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "No thread specified in p packet");
+
+ // Get the register context size first by calling with NULL buffer
+ nub_size_t reg_ctx_size = DNBThreadGetRegisterContext(pid, tid, NULL, 0);
+ if (reg_ctx_size) {
+ // Now allocate enough space for the entire register context
+ std::vector<uint8_t> reg_ctx;
+ reg_ctx.resize(reg_ctx_size);
+ // Now read the register context
+ reg_ctx_size =
+ DNBThreadGetRegisterContext(pid, tid, &reg_ctx[0], reg_ctx.size());
+ if (reg_ctx_size) {
+ append_hex_value(ostrm, reg_ctx.data(), reg_ctx.size(), false);
+ return SendPacket(ostrm.str());
+ }
+ }
+ return SendPacket("E74");
+}
+
+/* 'G XXX...' -- write registers
+ How is the thread for these specified, beyond "the current thread"?
+ Does gdb actually use the Hg packet to set this? */
+
+rnb_err_t RNBRemote::HandlePacket_G(const char *p) {
+ if (!m_ctx.HasValidProcessID()) {
+ return SendPacket("E11");
+ }
+
+ if (g_num_reg_entries == 0)
+ InitializeRegisters();
+
+ StdStringExtractor packet(p);
+ packet.SetFilePos(1); // Skip the 'G'
+
+ nub_process_t pid = m_ctx.ProcessID();
+ nub_thread_t tid = ExtractThreadIDFromThreadSuffix(p);
+ if (tid == INVALID_NUB_THREAD)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "No thread specified in p packet");
+
+ // Get the register context size first by calling with NULL buffer
+ nub_size_t reg_ctx_size = DNBThreadGetRegisterContext(pid, tid, NULL, 0);
+ if (reg_ctx_size) {
+ // Now allocate enough space for the entire register context
+ std::vector<uint8_t> reg_ctx;
+ reg_ctx.resize(reg_ctx_size);
+
+ const nub_size_t bytes_extracted =
+ packet.GetHexBytes(&reg_ctx[0], reg_ctx.size(), 0xcc);
+ if (bytes_extracted == reg_ctx.size()) {
+ // Now write the register context
+ reg_ctx_size =
+ DNBThreadSetRegisterContext(pid, tid, reg_ctx.data(), reg_ctx.size());
+ if (reg_ctx_size == reg_ctx.size())
+ return SendPacket("OK");
+ else
+ return SendPacket("E55");
+ } else {
+ DNBLogError("RNBRemote::HandlePacket_G(%s): extracted %llu of %llu "
+ "bytes, size mismatch\n",
+ p, (uint64_t)bytes_extracted, (uint64_t)reg_ctx_size);
+ return SendPacket("E64");
+ }
+ }
+ return SendPacket("E65");
+}
+
+static bool RNBRemoteShouldCancelCallback(void *not_used) {
+ RNBRemoteSP remoteSP(g_remoteSP);
+ if (remoteSP.get() != NULL) {
+ RNBRemote *remote = remoteSP.get();
+ return !remote->Comm().IsConnected();
+ }
+ return true;
+}
+
+// FORMAT: _MXXXXXX,PPP
+// XXXXXX: big endian hex chars
+// PPP: permissions can be any combo of r w x chars
+//
+// RESPONSE: XXXXXX
+// XXXXXX: hex address of the newly allocated memory
+// EXX: error code
+//
+// EXAMPLES:
+// _M123000,rw
+// _M123000,rwx
+// _M123000,xw
+
+rnb_err_t RNBRemote::HandlePacket_AllocateMemory(const char *p) {
+ StdStringExtractor packet(p);
+ packet.SetFilePos(2); // Skip the "_M"
+
+ nub_addr_t size = packet.GetHexMaxU64(StdStringExtractor::BigEndian, 0);
+ if (size != 0) {
+ if (packet.GetChar() == ',') {
+ uint32_t permissions = 0;
+ char ch;
+ bool success = true;
+ while (success && (ch = packet.GetChar()) != '\0') {
+ switch (ch) {
+ case 'r':
+ permissions |= eMemoryPermissionsReadable;
+ break;
+ case 'w':
+ permissions |= eMemoryPermissionsWritable;
+ break;
+ case 'x':
+ permissions |= eMemoryPermissionsExecutable;
+ break;
+ default:
+ success = false;
+ break;
+ }
+ }
+
+ if (success) {
+ nub_addr_t addr =
+ DNBProcessMemoryAllocate(m_ctx.ProcessID(), size, permissions);
+ if (addr != INVALID_NUB_ADDRESS) {
+ std::ostringstream ostrm;
+ ostrm << RAW_HEXBASE << addr;
+ return SendPacket(ostrm.str());
+ }
+ }
+ }
+ }
+ return SendPacket("E53");
+}
+
+// FORMAT: _mXXXXXX
+// XXXXXX: address that was previously allocated
+//
+// RESPONSE: XXXXXX
+// OK: address was deallocated
+// EXX: error code
+//
+// EXAMPLES:
+// _m123000
+
+rnb_err_t RNBRemote::HandlePacket_DeallocateMemory(const char *p) {
+ StdStringExtractor packet(p);
+ packet.SetFilePos(2); // Skip the "_m"
+ nub_addr_t addr =
+ packet.GetHexMaxU64(StdStringExtractor::BigEndian, INVALID_NUB_ADDRESS);
+
+ if (addr != INVALID_NUB_ADDRESS) {
+ if (DNBProcessMemoryDeallocate(m_ctx.ProcessID(), addr))
+ return SendPacket("OK");
+ }
+ return SendPacket("E54");
+}
+
+// FORMAT: QSaveRegisterState;thread:TTTT; (when thread suffix is supported)
+// FORMAT: QSaveRegisterState (when thread suffix is NOT
+// supported)
+// TTTT: thread ID in hex
+//
+// RESPONSE:
+// SAVEID: Where SAVEID is a decimal number that represents the save ID
+// that can be passed back into a "QRestoreRegisterState" packet
+// EXX: error code
+//
+// EXAMPLES:
+// QSaveRegisterState;thread:1E34; (when thread suffix is supported)
+// QSaveRegisterState (when thread suffix is NOT
+// supported)
+
+rnb_err_t RNBRemote::HandlePacket_SaveRegisterState(const char *p) {
+ nub_process_t pid = m_ctx.ProcessID();
+ nub_thread_t tid = ExtractThreadIDFromThreadSuffix(p);
+ if (tid == INVALID_NUB_THREAD) {
+ if (m_thread_suffix_supported)
+ return HandlePacket_ILLFORMED(
+ __FILE__, __LINE__, p,
+ "No thread specified in QSaveRegisterState packet");
+ else
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "No thread was is set with the Hg packet");
+ }
+
+ // Get the register context size first by calling with NULL buffer
+ const uint32_t save_id = DNBThreadSaveRegisterState(pid, tid);
+ if (save_id != 0) {
+ char response[64];
+ snprintf(response, sizeof(response), "%u", save_id);
+ return SendPacket(response);
+ } else {
+ return SendPacket("E75");
+ }
+}
+// FORMAT: QRestoreRegisterState:SAVEID;thread:TTTT; (when thread suffix is
+// supported)
+// FORMAT: QRestoreRegisterState:SAVEID (when thread suffix is NOT
+// supported)
+// TTTT: thread ID in hex
+// SAVEID: a decimal number that represents the save ID that was
+// returned from a call to "QSaveRegisterState"
+//
+// RESPONSE:
+// OK: successfully restored registers for the specified thread
+// EXX: error code
+//
+// EXAMPLES:
+// QRestoreRegisterState:1;thread:1E34; (when thread suffix is
+// supported)
+// QRestoreRegisterState:1 (when thread suffix is NOT
+// supported)
+
+rnb_err_t RNBRemote::HandlePacket_RestoreRegisterState(const char *p) {
+ nub_process_t pid = m_ctx.ProcessID();
+ nub_thread_t tid = ExtractThreadIDFromThreadSuffix(p);
+ if (tid == INVALID_NUB_THREAD) {
+ if (m_thread_suffix_supported)
+ return HandlePacket_ILLFORMED(
+ __FILE__, __LINE__, p,
+ "No thread specified in QSaveRegisterState packet");
+ else
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "No thread was is set with the Hg packet");
+ }
+
+ StdStringExtractor packet(p);
+ packet.SetFilePos(
+ strlen("QRestoreRegisterState:")); // Skip the "QRestoreRegisterState:"
+ const uint32_t save_id = packet.GetU32(0);
+
+ if (save_id != 0) {
+ // Get the register context size first by calling with NULL buffer
+ if (DNBThreadRestoreRegisterState(pid, tid, save_id))
+ return SendPacket("OK");
+ else
+ return SendPacket("E77");
+ }
+ return SendPacket("E76");
+}
+
+static bool GetProcessNameFrom_vAttach(const char *&p,
+ std::string &attach_name) {
+ bool return_val = true;
+ while (*p != '\0') {
+ char smallbuf[3];
+ smallbuf[0] = *p;
+ smallbuf[1] = *(p + 1);
+ smallbuf[2] = '\0';
+
+ errno = 0;
+ int ch = static_cast<int>(strtoul(smallbuf, NULL, 16));
+ if (errno != 0 && ch == 0) {
+ return_val = false;
+ break;
+ }
+
+ attach_name.push_back(ch);
+ p += 2;
+ }
+ return return_val;
+}
+
+rnb_err_t RNBRemote::HandlePacket_qSupported(const char *p) {
+ uint32_t max_packet_size = 128 * 1024; // 128KBytes is a reasonable max packet
+ // size--debugger can always use less
+ char buf[256];
+ snprintf(buf, sizeof(buf), "qXfer:features:read+;PacketSize=%x;qEcho+",
+ max_packet_size);
+
+ bool enable_compression = false;
+ (void)enable_compression;
+
+#if (defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1) \
+ || (defined (TARGET_OS_IOS) && TARGET_OS_IOS == 1) \
+ || (defined (TARGET_OS_TV) && TARGET_OS_TV == 1) \
+ || (defined (TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1)
+ enable_compression = true;
+#endif
+
+ if (enable_compression) {
+ strcat(buf, ";SupportedCompressions=lzfse,zlib-deflate,lz4,lzma;"
+ "DefaultCompressionMinSize=");
+ char numbuf[16];
+ snprintf(numbuf, sizeof(numbuf), "%zu", m_compression_minsize);
+ numbuf[sizeof(numbuf) - 1] = '\0';
+ strcat(buf, numbuf);
+ }
+
+ return SendPacket(buf);
+}
+
+/*
+ vAttach;pid
+
+ Attach to a new process with the specified process ID. pid is a hexadecimal
+ integer
+ identifying the process. If the stub is currently controlling a process, it is
+ killed. The attached process is stopped.This packet is only available in
+ extended
+ mode (see extended mode).
+
+ Reply:
+ "ENN" for an error
+ "Any Stop Reply Packet" for success
+ */
+
+rnb_err_t RNBRemote::HandlePacket_v(const char *p) {
+ if (strcmp(p, "vCont;c") == 0) {
+ // Simple continue
+ return RNBRemote::HandlePacket_c("c");
+ } else if (strcmp(p, "vCont;s") == 0) {
+ // Simple step
+ return RNBRemote::HandlePacket_s("s");
+ } else if (strstr(p, "vCont") == p) {
+ DNBThreadResumeActions thread_actions;
+ char *c = const_cast<char *>(p += strlen("vCont"));
+ char *c_end = c + strlen(c);
+ if (*c == '?')
+ return SendPacket("vCont;c;C;s;S");
+
+ while (c < c_end && *c == ';') {
+ ++c; // Skip the semi-colon
+ DNBThreadResumeAction thread_action;
+ thread_action.tid = INVALID_NUB_THREAD;
+ thread_action.state = eStateInvalid;
+ thread_action.signal = 0;
+ thread_action.addr = INVALID_NUB_ADDRESS;
+
+ char action = *c++;
+
+ switch (action) {
+ case 'C':
+ errno = 0;
+ thread_action.signal = static_cast<int>(strtoul(c, &c, 16));
+ if (errno != 0)
+ return HandlePacket_ILLFORMED(
+ __FILE__, __LINE__, p, "Could not parse signal in vCont packet");
+ // Fall through to next case...
+ [[clang::fallthrough]];
+ case 'c':
+ // Continue
+ thread_action.state = eStateRunning;
+ break;
+
+ case 'S':
+ errno = 0;
+ thread_action.signal = static_cast<int>(strtoul(c, &c, 16));
+ if (errno != 0)
+ return HandlePacket_ILLFORMED(
+ __FILE__, __LINE__, p, "Could not parse signal in vCont packet");
+ // Fall through to next case...
+ [[clang::fallthrough]];
+ case 's':
+ // Step
+ thread_action.state = eStateStepping;
+ break;
+
+ default:
+ HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Unsupported action in vCont packet");
+ break;
+ }
+ if (*c == ':') {
+ errno = 0;
+ thread_action.tid = strtoul(++c, &c, 16);
+ if (errno != 0)
+ return HandlePacket_ILLFORMED(
+ __FILE__, __LINE__, p,
+ "Could not parse thread number in vCont packet");
+ }
+
+ thread_actions.Append(thread_action);
+ }
+
+ // If a default action for all other threads wasn't mentioned
+ // then we should stop the threads
+ thread_actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0);
+ DNBProcessResume(m_ctx.ProcessID(), thread_actions.GetFirst(),
+ thread_actions.GetSize());
+ return rnb_success;
+ } else if (strstr(p, "vAttach") == p) {
+ nub_process_t attach_pid =
+ INVALID_NUB_PROCESS; // attach_pid will be set to 0 if the attach fails
+ nub_process_t pid_attaching_to =
+ INVALID_NUB_PROCESS; // pid_attaching_to is the original pid specified
+ char err_str[1024] = {'\0'};
+ std::string attach_name;
+
+ if (strstr(p, "vAttachWait;") == p) {
+ p += strlen("vAttachWait;");
+ if (!GetProcessNameFrom_vAttach(p, attach_name)) {
+ return HandlePacket_ILLFORMED(
+ __FILE__, __LINE__, p, "non-hex char in arg on 'vAttachWait' pkt");
+ }
+ const bool ignore_existing = true;
+ attach_pid = DNBProcessAttachWait(
+ attach_name.c_str(), m_ctx.LaunchFlavor(), ignore_existing, NULL,
+ 1000, err_str, sizeof(err_str), RNBRemoteShouldCancelCallback);
+
+ } else if (strstr(p, "vAttachOrWait;") == p) {
+ p += strlen("vAttachOrWait;");
+ if (!GetProcessNameFrom_vAttach(p, attach_name)) {
+ return HandlePacket_ILLFORMED(
+ __FILE__, __LINE__, p,
+ "non-hex char in arg on 'vAttachOrWait' pkt");
+ }
+ const bool ignore_existing = false;
+ attach_pid = DNBProcessAttachWait(
+ attach_name.c_str(), m_ctx.LaunchFlavor(), ignore_existing, NULL,
+ 1000, err_str, sizeof(err_str), RNBRemoteShouldCancelCallback);
+ } else if (strstr(p, "vAttachName;") == p) {
+ p += strlen("vAttachName;");
+ if (!GetProcessNameFrom_vAttach(p, attach_name)) {
+ return HandlePacket_ILLFORMED(
+ __FILE__, __LINE__, p, "non-hex char in arg on 'vAttachName' pkt");
+ }
+
+ attach_pid = DNBProcessAttachByName(attach_name.c_str(), NULL, err_str,
+ sizeof(err_str));
+
+ } else if (strstr(p, "vAttach;") == p) {
+ p += strlen("vAttach;");
+ char *end = NULL;
+ pid_attaching_to = static_cast<int>(
+ strtoul(p, &end, 16)); // PID will be in hex, so use base 16 to decode
+ if (p != end && *end == '\0') {
+ // Wait at most 30 second for attach
+ struct timespec attach_timeout_abstime;
+ DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, 30, 0);
+ attach_pid = DNBProcessAttach(pid_attaching_to, &attach_timeout_abstime,
+ err_str, sizeof(err_str));
+ }
+ } else {
+ return HandlePacket_UNIMPLEMENTED(p);
+ }
+
+ if (attach_pid != INVALID_NUB_PROCESS) {
+ if (m_ctx.ProcessID() != attach_pid)
+ m_ctx.SetProcessID(attach_pid);
+ // Send a stop reply packet to indicate we successfully attached!
+ NotifyThatProcessStopped();
+ return rnb_success;
+ } else {
+ m_ctx.LaunchStatus().SetError(-1, DNBError::Generic);
+ if (err_str[0])
+ m_ctx.LaunchStatus().SetErrorString(err_str);
+ else
+ m_ctx.LaunchStatus().SetErrorString("attach failed");
+
+#if defined(__APPLE__) && \
+ (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101000)
+ if (pid_attaching_to == INVALID_NUB_PROCESS && !attach_name.empty()) {
+ pid_attaching_to = DNBProcessGetPIDByName(attach_name.c_str());
+ }
+ if (pid_attaching_to != INVALID_NUB_PROCESS &&
+ strcmp(err_str, "No such process") != 0) {
+ // csr_check(CSR_ALLOW_TASK_FOR_PID) will be nonzero if System Integrity
+ // Protection is in effect.
+ if (csr_check(CSR_ALLOW_TASK_FOR_PID) != 0) {
+ bool attach_failed_due_to_sip = false;
+
+ if (rootless_allows_task_for_pid(pid_attaching_to) == 0) {
+ attach_failed_due_to_sip = true;
+ }
+
+ if (!attach_failed_due_to_sip) {
+ int csops_flags = 0;
+ int retval = ::csops(pid_attaching_to, CS_OPS_STATUS, &csops_flags,
+ sizeof(csops_flags));
+ if (retval != -1 && (csops_flags & CS_RESTRICT)) {
+ attach_failed_due_to_sip = true;
+ }
+ }
+ if (attach_failed_due_to_sip) {
+ std::string return_message = "E96;";
+ return_message += cstring_to_asciihex_string(
+ "Process attach denied, possibly because "
+ "System Integrity Protection is enabled and "
+ "process does not allow attaching.");
+
+ SendPacket(return_message.c_str());
+ DNBLogError("Attach failed because process does not allow "
+ "attaching: \"%s\".",
+ err_str);
+ return rnb_err;
+ }
+ }
+ }
+
+#endif
+
+ SendPacket("E01"); // E01 is our magic error value for attach failed.
+ DNBLogError("Attach failed: \"%s\".", err_str);
+ return rnb_err;
+ }
+ }
+
+ // All other failures come through here
+ return HandlePacket_UNIMPLEMENTED(p);
+}
+
+/* 'T XX' -- status of thread
+ Check if the specified thread is alive.
+ The thread number is in hex? */
+
+rnb_err_t RNBRemote::HandlePacket_T(const char *p) {
+ p++;
+ if (p == NULL || *p == '\0') {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "No thread specified in T packet");
+ }
+ if (!m_ctx.HasValidProcessID()) {
+ return SendPacket("E15");
+ }
+ errno = 0;
+ nub_thread_t tid = strtoul(p, NULL, 16);
+ if (errno != 0 && tid == 0) {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Could not parse thread number in T packet");
+ }
+
+ nub_state_t state = DNBThreadGetState(m_ctx.ProcessID(), tid);
+ if (state == eStateInvalid || state == eStateExited ||
+ state == eStateCrashed) {
+ return SendPacket("E16");
+ }
+
+ return SendPacket("OK");
+}
+
+rnb_err_t RNBRemote::HandlePacket_z(const char *p) {
+ if (p == NULL || *p == '\0')
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "No thread specified in z packet");
+
+ if (!m_ctx.HasValidProcessID())
+ return SendPacket("E15");
+
+ char packet_cmd = *p++;
+ char break_type = *p++;
+
+ if (*p++ != ',')
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Comma separator missing in z packet");
+
+ char *c = NULL;
+ nub_process_t pid = m_ctx.ProcessID();
+ errno = 0;
+ nub_addr_t addr = strtoull(p, &c, 16);
+ if (errno != 0 && addr == 0)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid address in z packet");
+ p = c;
+ if (*p++ != ',')
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Comma separator missing in z packet");
+
+ errno = 0;
+ auto byte_size = strtoul(p, &c, 16);
+ if (errno != 0 && byte_size == 0)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid length in z packet");
+
+ if (packet_cmd == 'Z') {
+ // set
+ switch (break_type) {
+ case '0': // set software breakpoint
+ case '1': // set hardware breakpoint
+ {
+ // gdb can send multiple Z packets for the same address and
+ // these calls must be ref counted.
+ bool hardware = (break_type == '1');
+
+ if (DNBBreakpointSet(pid, addr, byte_size, hardware)) {
+ // We successfully created a breakpoint, now lets full out
+ // a ref count structure with the breakID and add it to our
+ // map.
+ return SendPacket("OK");
+ } else {
+ // We failed to set the software breakpoint
+ return SendPacket("E09");
+ }
+ } break;
+
+ case '2': // set write watchpoint
+ case '3': // set read watchpoint
+ case '4': // set access watchpoint
+ {
+ bool hardware = true;
+ uint32_t watch_flags = 0;
+ if (break_type == '2')
+ watch_flags = WATCH_TYPE_WRITE;
+ else if (break_type == '3')
+ watch_flags = WATCH_TYPE_READ;
+ else
+ watch_flags = WATCH_TYPE_READ | WATCH_TYPE_WRITE;
+
+ if (DNBWatchpointSet(pid, addr, byte_size, watch_flags, hardware)) {
+ return SendPacket("OK");
+ } else {
+ // We failed to set the watchpoint
+ return SendPacket("E09");
+ }
+ } break;
+
+ default:
+ break;
+ }
+ } else if (packet_cmd == 'z') {
+ // remove
+ switch (break_type) {
+ case '0': // remove software breakpoint
+ case '1': // remove hardware breakpoint
+ if (DNBBreakpointClear(pid, addr)) {
+ return SendPacket("OK");
+ } else {
+ return SendPacket("E08");
+ }
+ break;
+
+ case '2': // remove write watchpoint
+ case '3': // remove read watchpoint
+ case '4': // remove access watchpoint
+ if (DNBWatchpointClear(pid, addr)) {
+ return SendPacket("OK");
+ } else {
+ return SendPacket("E08");
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ return HandlePacket_UNIMPLEMENTED(p);
+}
+
+// Extract the thread number from the thread suffix that might be appended to
+// thread specific packets. This will only be enabled if
+// m_thread_suffix_supported
+// is true.
+nub_thread_t RNBRemote::ExtractThreadIDFromThreadSuffix(const char *p) {
+ if (m_thread_suffix_supported) {
+ nub_thread_t tid = INVALID_NUB_THREAD;
+ if (p) {
+ const char *tid_cstr = strstr(p, "thread:");
+ if (tid_cstr) {
+ tid_cstr += strlen("thread:");
+ tid = strtoul(tid_cstr, NULL, 16);
+ }
+ }
+ return tid;
+ }
+ return GetCurrentThread();
+}
+
+/* 'p XX'
+ print the contents of register X */
+
+rnb_err_t RNBRemote::HandlePacket_p(const char *p) {
+ if (g_num_reg_entries == 0)
+ InitializeRegisters();
+
+ if (p == NULL || *p == '\0') {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "No thread specified in p packet");
+ }
+ if (!m_ctx.HasValidProcessID()) {
+ return SendPacket("E15");
+ }
+ nub_process_t pid = m_ctx.ProcessID();
+ errno = 0;
+ char *tid_cstr = NULL;
+ uint32_t reg = static_cast<uint32_t>(strtoul(p + 1, &tid_cstr, 16));
+ if (errno != 0 && reg == 0) {
+ return HandlePacket_ILLFORMED(
+ __FILE__, __LINE__, p, "Could not parse register number in p packet");
+ }
+
+ nub_thread_t tid = ExtractThreadIDFromThreadSuffix(tid_cstr);
+ if (tid == INVALID_NUB_THREAD)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "No thread specified in p packet");
+
+ const register_map_entry_t *reg_entry;
+
+ if (reg < g_num_reg_entries)
+ reg_entry = &g_reg_entries[reg];
+ else
+ reg_entry = NULL;
+
+ std::ostringstream ostrm;
+ if (reg_entry == NULL) {
+ DNBLogError(
+ "RNBRemote::HandlePacket_p(%s): unknown register number %u requested\n",
+ p, reg);
+ ostrm << "00000000";
+ } else if (reg_entry->nub_info.reg == (uint32_t)-1) {
+ if (reg_entry->nub_info.size > 0) {
+ std::basic_string<uint8_t> zeros(reg_entry->nub_info.size, '\0');
+ append_hex_value(ostrm, zeros.data(), zeros.size(), false);
+ }
+ } else {
+ register_value_in_hex_fixed_width(ostrm, pid, tid, reg_entry, NULL);
+ }
+ return SendPacket(ostrm.str());
+}
+
+/* 'Pnn=rrrrr'
+ Set register number n to value r.
+ n and r are hex strings. */
+
+rnb_err_t RNBRemote::HandlePacket_P(const char *p) {
+ if (g_num_reg_entries == 0)
+ InitializeRegisters();
+
+ if (p == NULL || *p == '\0') {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p, "Empty P packet");
+ }
+ if (!m_ctx.HasValidProcessID()) {
+ return SendPacket("E28");
+ }
+
+ nub_process_t pid = m_ctx.ProcessID();
+
+ StdStringExtractor packet(p);
+
+ const char cmd_char = packet.GetChar();
+ // Register ID is always in big endian
+ const uint32_t reg = packet.GetHexMaxU32(false, UINT32_MAX);
+ const char equal_char = packet.GetChar();
+
+ if (cmd_char != 'P')
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Improperly formed P packet");
+
+ if (reg == UINT32_MAX)
+ return SendPacket("E29");
+
+ if (equal_char != '=')
+ return SendPacket("E30");
+
+ const register_map_entry_t *reg_entry;
+
+ if (reg >= g_num_reg_entries)
+ return SendPacket("E47");
+
+ reg_entry = &g_reg_entries[reg];
+
+ if (reg_entry->nub_info.set == (uint32_t)-1 &&
+ reg_entry->nub_info.reg == (uint32_t)-1) {
+ DNBLogError(
+ "RNBRemote::HandlePacket_P(%s): unknown register number %u requested\n",
+ p, reg);
+ return SendPacket("E48");
+ }
+
+ DNBRegisterValue reg_value;
+ reg_value.info = reg_entry->nub_info;
+ packet.GetHexBytes(reg_value.value.v_sint8, reg_entry->nub_info.size, 0xcc);
+
+ nub_thread_t tid = ExtractThreadIDFromThreadSuffix(p);
+ if (tid == INVALID_NUB_THREAD)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "No thread specified in p packet");
+
+ if (!DNBThreadSetRegisterValueByID(pid, tid, reg_entry->nub_info.set,
+ reg_entry->nub_info.reg, &reg_value)) {
+ return SendPacket("E32");
+ }
+ return SendPacket("OK");
+}
+
+/* 'c [addr]'
+ Continue, optionally from a specified address. */
+
+rnb_err_t RNBRemote::HandlePacket_c(const char *p) {
+ const nub_process_t pid = m_ctx.ProcessID();
+
+ if (pid == INVALID_NUB_PROCESS)
+ return SendPacket("E23");
+
+ DNBThreadResumeAction action = {INVALID_NUB_THREAD, eStateRunning, 0,
+ INVALID_NUB_ADDRESS};
+
+ if (*(p + 1) != '\0') {
+ action.tid = GetContinueThread();
+ errno = 0;
+ action.addr = strtoull(p + 1, NULL, 16);
+ if (errno != 0 && action.addr == 0)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Could not parse address in c packet");
+ }
+
+ DNBThreadResumeActions thread_actions;
+ thread_actions.Append(action);
+ thread_actions.SetDefaultThreadActionIfNeeded(eStateRunning, 0);
+ if (!DNBProcessResume(pid, thread_actions.GetFirst(),
+ thread_actions.GetSize()))
+ return SendPacket("E25");
+ // Don't send an "OK" packet; response is the stopped/exited message.
+ return rnb_success;
+}
+
+rnb_err_t RNBRemote::HandlePacket_MemoryRegionInfo(const char *p) {
+ /* This packet will find memory attributes (e.g. readable, writable,
+ executable, stack, jitted code)
+ for the memory region containing a given address and return that
+ information.
+
+ Users of this packet must be prepared for three results:
+
+ Region information is returned
+ Region information is unavailable for this address because the address
+ is in unmapped memory
+ Region lookup cannot be performed on this platform or process is not
+ yet launched
+ This packet isn't implemented
+
+ Examples of use:
+ qMemoryRegionInfo:3a55140
+ start:3a50000,size:100000,permissions:rwx
+
+ qMemoryRegionInfo:0
+ error:address in unmapped region
+
+ qMemoryRegionInfo:3a551140 (on a different platform)
+ error:region lookup cannot be performed
+
+ qMemoryRegionInfo
+ OK // this packet is implemented by the remote nub
+ */
+
+ p += sizeof("qMemoryRegionInfo") - 1;
+ if (*p == '\0')
+ return SendPacket("OK");
+ if (*p++ != ':')
+ return SendPacket("E67");
+ if (*p == '0' && (*(p + 1) == 'x' || *(p + 1) == 'X'))
+ p += 2;
+
+ errno = 0;
+ uint64_t address = strtoul(p, NULL, 16);
+ if (errno != 0 && address == 0) {
+ return HandlePacket_ILLFORMED(
+ __FILE__, __LINE__, p, "Invalid address in qMemoryRegionInfo packet");
+ }
+
+ DNBRegionInfo region_info = {0, 0, 0};
+ DNBProcessMemoryRegionInfo(m_ctx.ProcessID(), address, &region_info);
+ std::ostringstream ostrm;
+
+ // start:3a50000,size:100000,permissions:rwx
+ ostrm << "start:" << std::hex << region_info.addr << ';';
+
+ if (region_info.size > 0)
+ ostrm << "size:" << std::hex << region_info.size << ';';
+
+ if (region_info.permissions) {
+ ostrm << "permissions:";
+
+ if (region_info.permissions & eMemoryPermissionsReadable)
+ ostrm << 'r';
+ if (region_info.permissions & eMemoryPermissionsWritable)
+ ostrm << 'w';
+ if (region_info.permissions & eMemoryPermissionsExecutable)
+ ostrm << 'x';
+ ostrm << ';';
+ }
+ return SendPacket(ostrm.str());
+}
+
+// qGetProfileData;scan_type:0xYYYYYYY
+rnb_err_t RNBRemote::HandlePacket_GetProfileData(const char *p) {
+ nub_process_t pid = m_ctx.ProcessID();
+ if (pid == INVALID_NUB_PROCESS)
+ return SendPacket("OK");
+
+ StdStringExtractor packet(p += sizeof("qGetProfileData"));
+ DNBProfileDataScanType scan_type = eProfileAll;
+ std::string name;
+ std::string value;
+ while (packet.GetNameColonValue(name, value)) {
+ if (name == "scan_type") {
+ std::istringstream iss(value);
+ uint32_t int_value = 0;
+ if (iss >> std::hex >> int_value) {
+ scan_type = (DNBProfileDataScanType)int_value;
+ }
+ }
+ }
+
+ std::string data = DNBProcessGetProfileData(pid, scan_type);
+ if (!data.empty()) {
+ return SendPacket(data.c_str());
+ } else {
+ return SendPacket("OK");
+ }
+}
+
+// QSetEnableAsyncProfiling;enable:[0|1]:interval_usec:XXXXXX;scan_type:0xYYYYYYY
+rnb_err_t RNBRemote::HandlePacket_SetEnableAsyncProfiling(const char *p) {
+ nub_process_t pid = m_ctx.ProcessID();
+ if (pid == INVALID_NUB_PROCESS)
+ return SendPacket("OK");
+
+ StdStringExtractor packet(p += sizeof("QSetEnableAsyncProfiling"));
+ bool enable = false;
+ uint64_t interval_usec = 0;
+ DNBProfileDataScanType scan_type = eProfileAll;
+ std::string name;
+ std::string value;
+ while (packet.GetNameColonValue(name, value)) {
+ if (name == "enable") {
+ enable = strtoul(value.c_str(), NULL, 10) > 0;
+ } else if (name == "interval_usec") {
+ interval_usec = strtoul(value.c_str(), NULL, 10);
+ } else if (name == "scan_type") {
+ std::istringstream iss(value);
+ uint32_t int_value = 0;
+ if (iss >> std::hex >> int_value) {
+ scan_type = (DNBProfileDataScanType)int_value;
+ }
+ }
+ }
+
+ if (interval_usec == 0) {
+ enable = false;
+ }
+
+ DNBProcessSetEnableAsyncProfiling(pid, enable, interval_usec, scan_type);
+ return SendPacket("OK");
+}
+
+// QEnableCompression:type:<COMPRESSION-TYPE>;minsize:<MINIMUM PACKET SIZE TO
+// COMPRESS>;
+//
+// type: must be a type previously reported by the qXfer:features:
+// SupportedCompressions list
+//
+// minsize: is optional; by default the qXfer:features:
+// DefaultCompressionMinSize value is used
+// debugserver may have a better idea of what a good minimum packet size to
+// compress is than lldb.
+
+rnb_err_t RNBRemote::HandlePacket_QEnableCompression(const char *p) {
+ p += sizeof("QEnableCompression:") - 1;
+
+ size_t new_compression_minsize = m_compression_minsize;
+ const char *new_compression_minsize_str = strstr(p, "minsize:");
+ if (new_compression_minsize_str) {
+ new_compression_minsize_str += strlen("minsize:");
+ errno = 0;
+ new_compression_minsize = strtoul(new_compression_minsize_str, NULL, 10);
+ if (errno != 0 || new_compression_minsize == ULONG_MAX) {
+ new_compression_minsize = m_compression_minsize;
+ }
+ }
+
+ if (strstr(p, "type:zlib-deflate;") != nullptr) {
+ EnableCompressionNextSendPacket(compression_types::zlib_deflate);
+ m_compression_minsize = new_compression_minsize;
+ return SendPacket("OK");
+ } else if (strstr(p, "type:lz4;") != nullptr) {
+ EnableCompressionNextSendPacket(compression_types::lz4);
+ m_compression_minsize = new_compression_minsize;
+ return SendPacket("OK");
+ } else if (strstr(p, "type:lzma;") != nullptr) {
+ EnableCompressionNextSendPacket(compression_types::lzma);
+ m_compression_minsize = new_compression_minsize;
+ return SendPacket("OK");
+ } else if (strstr(p, "type:lzfse;") != nullptr) {
+ EnableCompressionNextSendPacket(compression_types::lzfse);
+ m_compression_minsize = new_compression_minsize;
+ return SendPacket("OK");
+ }
+
+ return SendPacket("E88");
+}
+
+rnb_err_t RNBRemote::HandlePacket_qSpeedTest(const char *p) {
+ p += strlen("qSpeedTest:response_size:");
+ char *end = NULL;
+ errno = 0;
+ uint64_t response_size = ::strtoul(p, &end, 16);
+ if (errno != 0)
+ return HandlePacket_ILLFORMED(
+ __FILE__, __LINE__, p,
+ "Didn't find response_size value at right offset");
+ else if (*end == ';') {
+ static char g_data[4 * 1024 * 1024 + 16] = "data:";
+ memset(g_data + 5, 'a', response_size);
+ g_data[response_size + 5] = '\0';
+ return SendPacket(g_data);
+ } else {
+ return SendPacket("E79");
+ }
+}
+
+rnb_err_t RNBRemote::HandlePacket_WatchpointSupportInfo(const char *p) {
+ /* This packet simply returns the number of supported hardware watchpoints.
+
+ Examples of use:
+ qWatchpointSupportInfo:
+ num:4
+
+ qWatchpointSupportInfo
+ OK // this packet is implemented by the remote nub
+ */
+
+ p += sizeof("qWatchpointSupportInfo") - 1;
+ if (*p == '\0')
+ return SendPacket("OK");
+ if (*p++ != ':')
+ return SendPacket("E67");
+
+ errno = 0;
+ uint32_t num = DNBWatchpointGetNumSupportedHWP(m_ctx.ProcessID());
+ std::ostringstream ostrm;
+
+ // size:4
+ ostrm << "num:" << std::dec << num << ';';
+ return SendPacket(ostrm.str());
+}
+
+/* 'C sig [;addr]'
+ Resume with signal sig, optionally at address addr. */
+
+rnb_err_t RNBRemote::HandlePacket_C(const char *p) {
+ const nub_process_t pid = m_ctx.ProcessID();
+
+ if (pid == INVALID_NUB_PROCESS)
+ return SendPacket("E36");
+
+ DNBThreadResumeAction action = {INVALID_NUB_THREAD, eStateRunning, 0,
+ INVALID_NUB_ADDRESS};
+ int process_signo = -1;
+ if (*(p + 1) != '\0') {
+ action.tid = GetContinueThread();
+ char *end = NULL;
+ errno = 0;
+ process_signo = static_cast<int>(strtoul(p + 1, &end, 16));
+ if (errno != 0)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Could not parse signal in C packet");
+ else if (*end == ';') {
+ errno = 0;
+ action.addr = strtoull(end + 1, NULL, 16);
+ if (errno != 0 && action.addr == 0)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Could not parse address in C packet");
+ }
+ }
+
+ DNBThreadResumeActions thread_actions;
+ thread_actions.Append(action);
+ thread_actions.SetDefaultThreadActionIfNeeded(eStateRunning, action.signal);
+ if (!DNBProcessSignal(pid, process_signo))
+ return SendPacket("E52");
+ if (!DNBProcessResume(pid, thread_actions.GetFirst(),
+ thread_actions.GetSize()))
+ return SendPacket("E38");
+ /* Don't send an "OK" packet; response is the stopped/exited message. */
+ return rnb_success;
+}
+
+// 'D' packet
+// Detach from gdb.
+rnb_err_t RNBRemote::HandlePacket_D(const char *p) {
+ if (m_ctx.HasValidProcessID()) {
+ if (DNBProcessDetach(m_ctx.ProcessID()))
+ SendPacket("OK");
+ else
+ SendPacket("E");
+ } else {
+ SendPacket("E");
+ }
+ return rnb_success;
+}
+
+/* 'k'
+ Kill the inferior process. */
+
+rnb_err_t RNBRemote::HandlePacket_k(const char *p) {
+ DNBLog("Got a 'k' packet, killing the inferior process.");
+ // No response to should be sent to the kill packet
+ if (m_ctx.HasValidProcessID())
+ DNBProcessKill(m_ctx.ProcessID());
+ SendPacket("X09");
+ return rnb_success;
+}
+
+rnb_err_t RNBRemote::HandlePacket_stop_process(const char *p) {
+//#define TEST_EXIT_ON_INTERRUPT // This should only be uncommented to test
+//exiting on interrupt
+#if defined(TEST_EXIT_ON_INTERRUPT)
+ rnb_err_t err = HandlePacket_k(p);
+ m_comm.Disconnect(true);
+ return err;
+#else
+ if (!DNBProcessInterrupt(m_ctx.ProcessID())) {
+ // If we failed to interrupt the process, then send a stop
+ // reply packet as the process was probably already stopped
+ DNBLogThreaded("RNBRemote::HandlePacket_stop_process() sending extra stop "
+ "reply because DNBProcessInterrupt returned false");
+ HandlePacket_last_signal(NULL);
+ }
+ return rnb_success;
+#endif
+}
+
+/* 's'
+ Step the inferior process. */
+
+rnb_err_t RNBRemote::HandlePacket_s(const char *p) {
+ const nub_process_t pid = m_ctx.ProcessID();
+ if (pid == INVALID_NUB_PROCESS)
+ return SendPacket("E32");
+
+ // Hardware supported stepping not supported on arm
+ nub_thread_t tid = GetContinueThread();
+ if (tid == 0 || tid == (nub_thread_t)-1)
+ tid = GetCurrentThread();
+
+ if (tid == INVALID_NUB_THREAD)
+ return SendPacket("E33");
+
+ DNBThreadResumeActions thread_actions;
+ thread_actions.AppendAction(tid, eStateStepping);
+
+ // Make all other threads stop when we are stepping
+ thread_actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0);
+ if (!DNBProcessResume(pid, thread_actions.GetFirst(),
+ thread_actions.GetSize()))
+ return SendPacket("E49");
+ // Don't send an "OK" packet; response is the stopped/exited message.
+ return rnb_success;
+}
+
+/* 'S sig [;addr]'
+ Step with signal sig, optionally at address addr. */
+
+rnb_err_t RNBRemote::HandlePacket_S(const char *p) {
+ const nub_process_t pid = m_ctx.ProcessID();
+ if (pid == INVALID_NUB_PROCESS)
+ return SendPacket("E36");
+
+ DNBThreadResumeAction action = {INVALID_NUB_THREAD, eStateStepping, 0,
+ INVALID_NUB_ADDRESS};
+
+ if (*(p + 1) != '\0') {
+ char *end = NULL;
+ errno = 0;
+ action.signal = static_cast<int>(strtoul(p + 1, &end, 16));
+ if (errno != 0)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Could not parse signal in S packet");
+ else if (*end == ';') {
+ errno = 0;
+ action.addr = strtoull(end + 1, NULL, 16);
+ if (errno != 0 && action.addr == 0) {
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Could not parse address in S packet");
+ }
+ }
+ }
+
+ action.tid = GetContinueThread();
+ if (action.tid == 0 || action.tid == (nub_thread_t)-1)
+ return SendPacket("E40");
+
+ nub_state_t tstate = DNBThreadGetState(pid, action.tid);
+ if (tstate == eStateInvalid || tstate == eStateExited)
+ return SendPacket("E37");
+
+ DNBThreadResumeActions thread_actions;
+ thread_actions.Append(action);
+
+ // Make all other threads stop when we are stepping
+ thread_actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0);
+ if (!DNBProcessResume(pid, thread_actions.GetFirst(),
+ thread_actions.GetSize()))
+ return SendPacket("E39");
+
+ // Don't send an "OK" packet; response is the stopped/exited message.
+ return rnb_success;
+}
+
+static const char *GetArchName(const uint32_t cputype,
+ const uint32_t cpusubtype) {
+ switch (cputype) {
+ case CPU_TYPE_ARM:
+ switch (cpusubtype) {
+ case 5:
+ return "armv4";
+ case 6:
+ return "armv6";
+ case 7:
+ return "armv5t";
+ case 8:
+ return "xscale";
+ case 9:
+ return "armv7";
+ case 10:
+ return "armv7f";
+ case 11:
+ return "armv7s";
+ case 12:
+ return "armv7k";
+ case 14:
+ return "armv6m";
+ case 15:
+ return "armv7m";
+ case 16:
+ return "armv7em";
+ default:
+ return "arm";
+ }
+ break;
+ case CPU_TYPE_ARM64:
+ return "arm64";
+ case CPU_TYPE_ARM64_32:
+ return "arm64_32";
+ case CPU_TYPE_I386:
+ return "i386";
+ case CPU_TYPE_X86_64:
+ switch (cpusubtype) {
+ default:
+ return "x86_64";
+ case 8:
+ return "x86_64h";
+ }
+ break;
+ }
+ return NULL;
+}
+
+static bool GetHostCPUType(uint32_t &cputype, uint32_t &cpusubtype,
+ uint32_t &is_64_bit_capable, bool &promoted_to_64) {
+ static uint32_t g_host_cputype = 0;
+ static uint32_t g_host_cpusubtype = 0;
+ static uint32_t g_is_64_bit_capable = 0;
+ static bool g_promoted_to_64 = false;
+
+ if (g_host_cputype == 0) {
+ g_promoted_to_64 = false;
+ size_t len = sizeof(uint32_t);
+ if (::sysctlbyname("hw.cputype", &g_host_cputype, &len, NULL, 0) == 0) {
+ len = sizeof(uint32_t);
+ if (::sysctlbyname("hw.cpu64bit_capable", &g_is_64_bit_capable, &len,
+ NULL, 0) == 0) {
+ if (g_is_64_bit_capable && ((g_host_cputype & CPU_ARCH_ABI64) == 0)) {
+ g_promoted_to_64 = true;
+ g_host_cputype |= CPU_ARCH_ABI64;
+ }
+ }
+#if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
+ if (g_host_cputype == CPU_TYPE_ARM64 && sizeof (void*) == 4)
+ g_host_cputype = CPU_TYPE_ARM64_32;
+#endif
+ }
+
+ len = sizeof(uint32_t);
+ if (::sysctlbyname("hw.cpusubtype", &g_host_cpusubtype, &len, NULL, 0) ==
+ 0) {
+ if (g_promoted_to_64 && g_host_cputype == CPU_TYPE_X86_64 &&
+ g_host_cpusubtype == CPU_SUBTYPE_486)
+ g_host_cpusubtype = CPU_SUBTYPE_X86_64_ALL;
+ }
+#if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
+ // on arm64_32 devices, the machine's native cpu type is
+ // CPU_TYPE_ARM64 and subtype is 2 indicating arm64e.
+ // But we change the cputype to CPU_TYPE_ARM64_32 because
+ // the user processes are all ILP32 processes today.
+ // We also need to rewrite the cpusubtype so we vend
+ // a valid cputype + cpusubtype combination.
+ if (g_host_cputype == CPU_TYPE_ARM64_32)
+ g_host_cpusubtype = CPU_SUBTYPE_ARM64_32_V8;
+#endif
+ }
+
+ cputype = g_host_cputype;
+ cpusubtype = g_host_cpusubtype;
+ is_64_bit_capable = g_is_64_bit_capable;
+ promoted_to_64 = g_promoted_to_64;
+ return g_host_cputype != 0;
+}
+
+static bool GetAddressingBits(uint32_t &addressing_bits) {
+ static uint32_t g_addressing_bits = 0;
+ static bool g_tried_addressing_bits_syscall = false;
+ if (g_tried_addressing_bits_syscall == false) {
+ size_t len = sizeof (uint32_t);
+ if (::sysctlbyname("machdep.virtual_address_size",
+ &g_addressing_bits, &len, NULL, 0) != 0) {
+ g_addressing_bits = 0;
+ }
+ }
+ g_tried_addressing_bits_syscall = true;
+ addressing_bits = g_addressing_bits;
+ if (addressing_bits > 0)
+ return true;
+ else
+ return false;
+}
+
+rnb_err_t RNBRemote::HandlePacket_qHostInfo(const char *p) {
+ std::ostringstream strm;
+
+ uint32_t cputype = 0;
+ uint32_t cpusubtype = 0;
+ uint32_t is_64_bit_capable = 0;
+ bool promoted_to_64 = false;
+ if (GetHostCPUType(cputype, cpusubtype, is_64_bit_capable, promoted_to_64)) {
+ strm << "cputype:" << std::dec << cputype << ';';
+ strm << "cpusubtype:" << std::dec << cpusubtype << ';';
+ }
+
+ uint32_t addressing_bits = 0;
+ if (GetAddressingBits(addressing_bits)) {
+ strm << "addressing_bits:" << std::dec << addressing_bits << ';';
+ }
+
+ // The OS in the triple should be "ios" or "macosx" which doesn't match our
+ // "Darwin" which gets returned from "kern.ostype", so we need to hardcode
+ // this for now.
+ if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64
+ || cputype == CPU_TYPE_ARM64_32) {
+#if defined(TARGET_OS_TV) && TARGET_OS_TV == 1
+ strm << "ostype:tvos;";
+#elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
+ strm << "ostype:watchos;";
+#elif defined(TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1
+ strm << "ostype:bridgeos;";
+#else
+ strm << "ostype:ios;";
+#endif
+
+ // On armv7 we use "synchronous" watchpoints which means the exception is
+ // delivered before the instruction executes.
+ strm << "watchpoint_exceptions_received:before;";
+ } else {
+ strm << "ostype:macosx;";
+ strm << "watchpoint_exceptions_received:after;";
+ }
+ // char ostype[64];
+ // len = sizeof(ostype);
+ // if (::sysctlbyname("kern.ostype", &ostype, &len, NULL, 0) == 0)
+ // {
+ // len = strlen(ostype);
+ // std::transform (ostype, ostype + len, ostype, tolower);
+ // strm << "ostype:" << std::dec << ostype << ';';
+ // }
+
+ strm << "vendor:apple;";
+
+ uint64_t major, minor, patch;
+ if (DNBGetOSVersionNumbers(&major, &minor, &patch)) {
+ strm << "os_version:" << major << "." << minor;
+ if (patch != UINT64_MAX)
+ strm << "." << patch;
+ strm << ";";
+ }
+
+ std::string maccatalyst_version = DNBGetMacCatalystVersionString();
+ if (!maccatalyst_version.empty() &&
+ std::all_of(maccatalyst_version.begin(), maccatalyst_version.end(),
+ [](char c) { return (c >= '0' && c <= '9') || c == '.'; }))
+ strm << "maccatalyst_version:" << maccatalyst_version << ";";
+
+#if defined(__LITTLE_ENDIAN__)
+ strm << "endian:little;";
+#elif defined(__BIG_ENDIAN__)
+ strm << "endian:big;";
+#elif defined(__PDP_ENDIAN__)
+ strm << "endian:pdp;";
+#endif
+
+ if (promoted_to_64)
+ strm << "ptrsize:8;";
+ else
+ strm << "ptrsize:" << std::dec << sizeof(void *) << ';';
+
+#if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
+ strm << "default_packet_timeout:10;";
+#endif
+
+ return SendPacket(strm.str());
+}
+
+void XMLElementStart(std::ostringstream &s, uint32_t indent, const char *name,
+ bool has_attributes) {
+ if (indent)
+ s << INDENT_WITH_SPACES(indent);
+ s << '<' << name;
+ if (!has_attributes)
+ s << '>' << std::endl;
+}
+
+void XMLElementStartEndAttributes(std::ostringstream &s, bool empty) {
+ if (empty)
+ s << '/';
+ s << '>' << std::endl;
+}
+
+void XMLElementEnd(std::ostringstream &s, uint32_t indent, const char *name) {
+ if (indent)
+ s << INDENT_WITH_SPACES(indent);
+ s << '<' << '/' << name << '>' << std::endl;
+}
+
+void XMLElementWithStringValue(std::ostringstream &s, uint32_t indent,
+ const char *name, const char *value,
+ bool close = true) {
+ if (value) {
+ if (indent)
+ s << INDENT_WITH_SPACES(indent);
+ s << '<' << name << '>' << value;
+ if (close)
+ XMLElementEnd(s, 0, name);
+ }
+}
+
+void XMLElementWithUnsignedValue(std::ostringstream &s, uint32_t indent,
+ const char *name, uint64_t value,
+ bool close = true) {
+ if (indent)
+ s << INDENT_WITH_SPACES(indent);
+
+ s << '<' << name << '>' << DECIMAL << value;
+ if (close)
+ XMLElementEnd(s, 0, name);
+}
+
+void XMLAttributeString(std::ostringstream &s, const char *name,
+ const char *value, const char *default_value = NULL) {
+ if (value) {
+ if (default_value && strcmp(value, default_value) == 0)
+ return; // No need to emit the attribute because it matches the default
+ // value
+ s << ' ' << name << "=\"" << value << "\"";
+ }
+}
+
+void XMLAttributeUnsignedDecimal(std::ostringstream &s, const char *name,
+ uint64_t value) {
+ s << ' ' << name << "=\"" << DECIMAL << value << "\"";
+}
+
+void GenerateTargetXMLRegister(std::ostringstream &s, const uint32_t reg_num,
+ nub_size_t num_reg_sets,
+ const DNBRegisterSetInfo *reg_set_info,
+ const register_map_entry_t &reg) {
+ const char *default_lldb_encoding = "uint";
+ const char *lldb_encoding = default_lldb_encoding;
+ const char *gdb_group = "general";
+ const char *default_gdb_type = "int";
+ const char *gdb_type = default_gdb_type;
+ const char *default_lldb_format = "hex";
+ const char *lldb_format = default_lldb_format;
+ const char *lldb_set = NULL;
+
+ switch (reg.nub_info.type) {
+ case Uint:
+ lldb_encoding = "uint";
+ break;
+ case Sint:
+ lldb_encoding = "sint";
+ break;
+ case IEEE754:
+ lldb_encoding = "ieee754";
+ if (reg.nub_info.set > 0)
+ gdb_group = "float";
+ break;
+ case Vector:
+ lldb_encoding = "vector";
+ if (reg.nub_info.set > 0)
+ gdb_group = "vector";
+ break;
+ }
+
+ switch (reg.nub_info.format) {
+ case Binary:
+ lldb_format = "binary";
+ break;
+ case Decimal:
+ lldb_format = "decimal";
+ break;
+ case Hex:
+ lldb_format = "hex";
+ break;
+ case Float:
+ gdb_type = "float";
+ lldb_format = "float";
+ break;
+ case VectorOfSInt8:
+ gdb_type = "float";
+ lldb_format = "vector-sint8";
+ break;
+ case VectorOfUInt8:
+ gdb_type = "float";
+ lldb_format = "vector-uint8";
+ break;
+ case VectorOfSInt16:
+ gdb_type = "float";
+ lldb_format = "vector-sint16";
+ break;
+ case VectorOfUInt16:
+ gdb_type = "float";
+ lldb_format = "vector-uint16";
+ break;
+ case VectorOfSInt32:
+ gdb_type = "float";
+ lldb_format = "vector-sint32";
+ break;
+ case VectorOfUInt32:
+ gdb_type = "float";
+ lldb_format = "vector-uint32";
+ break;
+ case VectorOfFloat32:
+ gdb_type = "float";
+ lldb_format = "vector-float32";
+ break;
+ case VectorOfUInt128:
+ gdb_type = "float";
+ lldb_format = "vector-uint128";
+ break;
+ };
+ if (reg_set_info && reg.nub_info.set < num_reg_sets)
+ lldb_set = reg_set_info[reg.nub_info.set].name;
+
+ uint32_t indent = 2;
+
+ XMLElementStart(s, indent, "reg", true);
+ XMLAttributeString(s, "name", reg.nub_info.name);
+ XMLAttributeUnsignedDecimal(s, "regnum", reg_num);
+ XMLAttributeUnsignedDecimal(s, "offset", reg.offset);
+ XMLAttributeUnsignedDecimal(s, "bitsize", reg.nub_info.size * 8);
+ XMLAttributeString(s, "group", gdb_group);
+ XMLAttributeString(s, "type", gdb_type, default_gdb_type);
+ XMLAttributeString(s, "altname", reg.nub_info.alt);
+ XMLAttributeString(s, "encoding", lldb_encoding, default_lldb_encoding);
+ XMLAttributeString(s, "format", lldb_format, default_lldb_format);
+ XMLAttributeUnsignedDecimal(s, "group_id", reg.nub_info.set);
+ if (reg.nub_info.reg_ehframe != INVALID_NUB_REGNUM)
+ XMLAttributeUnsignedDecimal(s, "ehframe_regnum", reg.nub_info.reg_ehframe);
+ if (reg.nub_info.reg_dwarf != INVALID_NUB_REGNUM)
+ XMLAttributeUnsignedDecimal(s, "dwarf_regnum", reg.nub_info.reg_dwarf);
+
+ const char *lldb_generic = NULL;
+ switch (reg.nub_info.reg_generic) {
+ case GENERIC_REGNUM_FP:
+ lldb_generic = "fp";
+ break;
+ case GENERIC_REGNUM_PC:
+ lldb_generic = "pc";
+ break;
+ case GENERIC_REGNUM_SP:
+ lldb_generic = "sp";
+ break;
+ case GENERIC_REGNUM_RA:
+ lldb_generic = "ra";
+ break;
+ case GENERIC_REGNUM_FLAGS:
+ lldb_generic = "flags";
+ break;
+ case GENERIC_REGNUM_ARG1:
+ lldb_generic = "arg1";
+ break;
+ case GENERIC_REGNUM_ARG2:
+ lldb_generic = "arg2";
+ break;
+ case GENERIC_REGNUM_ARG3:
+ lldb_generic = "arg3";
+ break;
+ case GENERIC_REGNUM_ARG4:
+ lldb_generic = "arg4";
+ break;
+ case GENERIC_REGNUM_ARG5:
+ lldb_generic = "arg5";
+ break;
+ case GENERIC_REGNUM_ARG6:
+ lldb_generic = "arg6";
+ break;
+ case GENERIC_REGNUM_ARG7:
+ lldb_generic = "arg7";
+ break;
+ case GENERIC_REGNUM_ARG8:
+ lldb_generic = "arg8";
+ break;
+ default:
+ break;
+ }
+ XMLAttributeString(s, "generic", lldb_generic);
+
+ bool empty = reg.value_regnums.empty() && reg.invalidate_regnums.empty();
+ if (!empty) {
+ if (!reg.value_regnums.empty()) {
+ std::ostringstream regnums;
+ bool first = true;
+ regnums << DECIMAL;
+ for (auto regnum : reg.value_regnums) {
+ if (!first)
+ regnums << ',';
+ regnums << regnum;
+ first = false;
+ }
+ XMLAttributeString(s, "value_regnums", regnums.str().c_str());
+ }
+
+ if (!reg.invalidate_regnums.empty()) {
+ std::ostringstream regnums;
+ bool first = true;
+ regnums << DECIMAL;
+ for (auto regnum : reg.invalidate_regnums) {
+ if (!first)
+ regnums << ',';
+ regnums << regnum;
+ first = false;
+ }
+ XMLAttributeString(s, "invalidate_regnums", regnums.str().c_str());
+ }
+ }
+ XMLElementStartEndAttributes(s, true);
+}
+
+void GenerateTargetXMLRegisters(std::ostringstream &s) {
+ nub_size_t num_reg_sets = 0;
+ const DNBRegisterSetInfo *reg_sets = DNBGetRegisterSetInfo(&num_reg_sets);
+
+ uint32_t cputype = DNBGetRegisterCPUType();
+ if (cputype) {
+ XMLElementStart(s, 0, "feature", true);
+ std::ostringstream name_strm;
+ name_strm << "com.apple.debugserver." << GetArchName(cputype, 0);
+ XMLAttributeString(s, "name", name_strm.str().c_str());
+ XMLElementStartEndAttributes(s, false);
+ for (uint32_t reg_num = 0; reg_num < g_num_reg_entries; ++reg_num)
+ // for (const auto &reg: g_dynamic_register_map)
+ {
+ GenerateTargetXMLRegister(s, reg_num, num_reg_sets, reg_sets,
+ g_reg_entries[reg_num]);
+ }
+ XMLElementEnd(s, 0, "feature");
+
+ if (num_reg_sets > 0) {
+ XMLElementStart(s, 0, "groups", false);
+ for (uint32_t set = 1; set < num_reg_sets; ++set) {
+ XMLElementStart(s, 2, "group", true);
+ XMLAttributeUnsignedDecimal(s, "id", set);
+ XMLAttributeString(s, "name", reg_sets[set].name);
+ XMLElementStartEndAttributes(s, true);
+ }
+ XMLElementEnd(s, 0, "groups");
+ }
+ }
+}
+
+static const char *g_target_xml_header = R"(<?xml version="1.0"?>
+<target version="1.0">)";
+
+static const char *g_target_xml_footer = "</target>";
+
+static std::string g_target_xml;
+
+void UpdateTargetXML() {
+ std::ostringstream s;
+ s << g_target_xml_header << std::endl;
+
+ // Set the architecture
+ //
+ // On raw targets (no OS, vendor info), I've seen replies like
+ // <architecture>i386:x86-64</architecture> (for x86_64 systems - from vmware)
+ // <architecture>arm</architecture> (for an unspecified arm device - from a Segger JLink)
+ // For good interop, I'm not sure what's expected here. e.g. will anyone understand
+ // <architecture>x86_64</architecture> ? Or is i386:x86_64 the expected phrasing?
+ //
+ // s << "<architecture>" << arch "</architecture>" << std::endl;
+
+ // Set the OSABI
+ // s << "<osabi>abi-name</osabi>"
+
+ GenerateTargetXMLRegisters(s);
+
+ s << g_target_xml_footer << std::endl;
+
+ // Save the XML output in case it gets retrieved in chunks
+ g_target_xml = s.str();
+}
+
+rnb_err_t RNBRemote::HandlePacket_qXfer(const char *command) {
+ const char *p = command;
+ p += strlen("qXfer:");
+ const char *sep = strchr(p, ':');
+ if (sep) {
+ std::string object(p, sep - p); // "auxv", "backtrace", "features", etc
+ p = sep + 1;
+ sep = strchr(p, ':');
+ if (sep) {
+ std::string rw(p, sep - p); // "read" or "write"
+ p = sep + 1;
+ sep = strchr(p, ':');
+ if (sep) {
+ std::string annex(p, sep - p); // "read" or "write"
+
+ p = sep + 1;
+ sep = strchr(p, ',');
+ if (sep) {
+ std::string offset_str(p, sep - p); // read the length as a string
+ p = sep + 1;
+ std::string length_str(p); // read the offset as a string
+ char *end = nullptr;
+ const uint64_t offset = strtoul(offset_str.c_str(), &end,
+ 16); // convert offset_str to a offset
+ if (*end == '\0') {
+ const uint64_t length = strtoul(
+ length_str.c_str(), &end, 16); // convert length_str to a length
+ if (*end == '\0') {
+ if (object == "features" && rw == "read" &&
+ annex == "target.xml") {
+ std::ostringstream xml_out;
+
+ if (offset == 0) {
+ InitializeRegisters(true);
+
+ UpdateTargetXML();
+ if (g_target_xml.empty())
+ return SendPacket("E83");
+
+ if (length > g_target_xml.size()) {
+ xml_out << 'l'; // No more data
+ xml_out << binary_encode_string(g_target_xml);
+ } else {
+ xml_out << 'm'; // More data needs to be read with a
+ // subsequent call
+ xml_out << binary_encode_string(
+ std::string(g_target_xml, offset, length));
+ }
+ } else {
+ // Retrieving target XML in chunks
+ if (offset < g_target_xml.size()) {
+ std::string chunk(g_target_xml, offset, length);
+ if (chunk.size() < length)
+ xml_out << 'l'; // No more data
+ else
+ xml_out << 'm'; // More data needs to be read with a
+ // subsequent call
+ xml_out << binary_encode_string(chunk.data());
+ }
+ }
+ return SendPacket(xml_out.str());
+ }
+ // Well formed, put not supported
+ return HandlePacket_UNIMPLEMENTED(command);
+ }
+ }
+ }
+ } else {
+ SendPacket("E85");
+ }
+ } else {
+ SendPacket("E86");
+ }
+ }
+ return SendPacket("E82");
+}
+
+rnb_err_t RNBRemote::HandlePacket_qGDBServerVersion(const char *p) {
+ std::ostringstream strm;
+
+#if defined(DEBUGSERVER_PROGRAM_NAME)
+ strm << "name:" DEBUGSERVER_PROGRAM_NAME ";";
+#else
+ strm << "name:debugserver;";
+#endif
+ strm << "version:" << DEBUGSERVER_VERSION_NUM << ";";
+
+ return SendPacket(strm.str());
+}
+
+// A helper function that retrieves a single integer value from
+// a one-level-deep JSON dictionary of key-value pairs. e.g.
+// jThreadExtendedInfo:{"plo_pthread_tsd_base_address_offset":0,"plo_pthread_tsd_base_offset":224,"plo_pthread_tsd_entry_size":8,"thread":144305}]
+//
+uint64_t get_integer_value_for_key_name_from_json(const char *key,
+ const char *json_string) {
+ uint64_t retval = INVALID_NUB_ADDRESS;
+ std::string key_with_quotes = "\"";
+ key_with_quotes += key;
+ key_with_quotes += "\"";
+ const char *c = strstr(json_string, key_with_quotes.c_str());
+ if (c) {
+ c += key_with_quotes.size();
+
+ while (*c != '\0' && (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r'))
+ c++;
+
+ if (*c == ':') {
+ c++;
+
+ while (*c != '\0' &&
+ (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r'))
+ c++;
+
+ errno = 0;
+ retval = strtoul(c, NULL, 10);
+ if (errno != 0) {
+ retval = INVALID_NUB_ADDRESS;
+ }
+ }
+ }
+ return retval;
+}
+
+// A helper function that retrieves a boolean value from
+// a one-level-deep JSON dictionary of key-value pairs. e.g.
+// jGetLoadedDynamicLibrariesInfos:{"fetch_all_solibs":true}]
+
+// Returns true if it was able to find the key name, and sets the 'value'
+// argument to the value found.
+
+bool get_boolean_value_for_key_name_from_json(const char *key,
+ const char *json_string,
+ bool &value) {
+ std::string key_with_quotes = "\"";
+ key_with_quotes += key;
+ key_with_quotes += "\"";
+ const char *c = strstr(json_string, key_with_quotes.c_str());
+ if (c) {
+ c += key_with_quotes.size();
+
+ while (*c != '\0' && (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r'))
+ c++;
+
+ if (*c == ':') {
+ c++;
+
+ while (*c != '\0' &&
+ (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r'))
+ c++;
+
+ if (strncmp(c, "true", 4) == 0) {
+ value = true;
+ return true;
+ } else if (strncmp(c, "false", 5) == 0) {
+ value = false;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// A helper function that reads an array of uint64_t's from
+// a one-level-deep JSON dictionary of key-value pairs. e.g.
+// jGetLoadedDynamicLibrariesInfos:{"solib_addrs":[31345823,7768020384,7310483024]}]
+
+// Returns true if it was able to find the key name, false if it did not.
+// "ints" will have all integers found in the array appended to it.
+
+bool get_array_of_ints_value_for_key_name_from_json(
+ const char *key, const char *json_string, std::vector<uint64_t> &ints) {
+ std::string key_with_quotes = "\"";
+ key_with_quotes += key;
+ key_with_quotes += "\"";
+ const char *c = strstr(json_string, key_with_quotes.c_str());
+ if (c) {
+ c += key_with_quotes.size();
+
+ while (*c != '\0' && (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r'))
+ c++;
+
+ if (*c == ':') {
+ c++;
+
+ while (*c != '\0' &&
+ (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r'))
+ c++;
+
+ if (*c == '[') {
+ c++;
+ while (*c != '\0' &&
+ (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r'))
+ c++;
+ while (true) {
+ if (!isdigit(*c)) {
+ return true;
+ }
+
+ errno = 0;
+ char *endptr;
+ uint64_t value = strtoul(c, &endptr, 10);
+ if (errno == 0) {
+ ints.push_back(value);
+ } else {
+ break;
+ }
+ if (endptr == c || endptr == nullptr || *endptr == '\0') {
+ break;
+ }
+ c = endptr;
+
+ while (*c != '\0' &&
+ (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r'))
+ c++;
+ if (*c == ',')
+ c++;
+ while (*c != '\0' &&
+ (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r'))
+ c++;
+ if (*c == ']') {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+JSONGenerator::ObjectSP
+RNBRemote::GetJSONThreadsInfo(bool threads_with_valid_stop_info_only) {
+ JSONGenerator::ArraySP threads_array_sp;
+ if (m_ctx.HasValidProcessID()) {
+ threads_array_sp = std::make_shared<JSONGenerator::Array>();
+
+ nub_process_t pid = m_ctx.ProcessID();
+
+ nub_size_t numthreads = DNBProcessGetNumThreads(pid);
+ for (nub_size_t i = 0; i < numthreads; ++i) {
+ nub_thread_t tid = DNBProcessGetThreadAtIndex(pid, i);
+
+ struct DNBThreadStopInfo tid_stop_info;
+
+ const bool stop_info_valid =
+ DNBThreadGetStopReason(pid, tid, &tid_stop_info);
+
+ // If we are doing stop info only, then we only show threads that have a
+ // valid stop reason
+ if (threads_with_valid_stop_info_only) {
+ if (!stop_info_valid || tid_stop_info.reason == eStopTypeInvalid)
+ continue;
+ }
+
+ JSONGenerator::DictionarySP thread_dict_sp(
+ new JSONGenerator::Dictionary());
+ thread_dict_sp->AddIntegerItem("tid", tid);
+
+ std::string reason_value("none");
+
+ if (stop_info_valid) {
+ switch (tid_stop_info.reason) {
+ case eStopTypeInvalid:
+ break;
+
+ case eStopTypeSignal:
+ if (tid_stop_info.details.signal.signo != 0) {
+ thread_dict_sp->AddIntegerItem("signal",
+ tid_stop_info.details.signal.signo);
+ reason_value = "signal";
+ }
+ break;
+
+ case eStopTypeException:
+ if (tid_stop_info.details.exception.type != 0) {
+ reason_value = "exception";
+ thread_dict_sp->AddIntegerItem(
+ "metype", tid_stop_info.details.exception.type);
+ JSONGenerator::ArraySP medata_array_sp(new JSONGenerator::Array());
+ for (nub_size_t i = 0;
+ i < tid_stop_info.details.exception.data_count; ++i) {
+ medata_array_sp->AddItem(
+ JSONGenerator::IntegerSP(new JSONGenerator::Integer(
+ tid_stop_info.details.exception.data[i])));
+ }
+ thread_dict_sp->AddItem("medata", medata_array_sp);
+ }
+ break;
+
+ case eStopTypeExec:
+ reason_value = "exec";
+ break;
+ }
+ }
+
+ thread_dict_sp->AddStringItem("reason", reason_value);
+
+ if (!threads_with_valid_stop_info_only) {
+ const char *thread_name = DNBThreadGetName(pid, tid);
+ if (thread_name && thread_name[0])
+ thread_dict_sp->AddStringItem("name", thread_name);
+
+ thread_identifier_info_data_t thread_ident_info;
+ if (DNBThreadGetIdentifierInfo(pid, tid, &thread_ident_info)) {
+ if (thread_ident_info.dispatch_qaddr != 0) {
+ thread_dict_sp->AddIntegerItem("qaddr",
+ thread_ident_info.dispatch_qaddr);
+
+ const DispatchQueueOffsets *dispatch_queue_offsets =
+ GetDispatchQueueOffsets();
+ if (dispatch_queue_offsets) {
+ std::string queue_name;
+ uint64_t queue_width = 0;
+ uint64_t queue_serialnum = 0;
+ nub_addr_t dispatch_queue_t = INVALID_NUB_ADDRESS;
+ dispatch_queue_offsets->GetThreadQueueInfo(
+ pid, thread_ident_info.dispatch_qaddr, dispatch_queue_t,
+ queue_name, queue_width, queue_serialnum);
+ if (dispatch_queue_t == 0 && queue_name.empty() &&
+ queue_serialnum == 0) {
+ thread_dict_sp->AddBooleanItem("associated_with_dispatch_queue",
+ false);
+ } else {
+ thread_dict_sp->AddBooleanItem("associated_with_dispatch_queue",
+ true);
+ }
+ if (dispatch_queue_t != INVALID_NUB_ADDRESS &&
+ dispatch_queue_t != 0)
+ thread_dict_sp->AddIntegerItem("dispatch_queue_t",
+ dispatch_queue_t);
+ if (!queue_name.empty())
+ thread_dict_sp->AddStringItem("qname", queue_name);
+ if (queue_width == 1)
+ thread_dict_sp->AddStringItem("qkind", "serial");
+ else if (queue_width > 1)
+ thread_dict_sp->AddStringItem("qkind", "concurrent");
+ if (queue_serialnum > 0)
+ thread_dict_sp->AddIntegerItem("qserialnum", queue_serialnum);
+ }
+ }
+ }
+
+ DNBRegisterValue reg_value;
+
+ if (g_reg_entries != NULL) {
+ JSONGenerator::DictionarySP registers_dict_sp(
+ new JSONGenerator::Dictionary());
+
+ for (uint32_t reg = 0; reg < g_num_reg_entries; reg++) {
+ // Expedite all registers in the first register set that aren't
+ // contained in other registers
+ if (g_reg_entries[reg].nub_info.set == 1 &&
+ g_reg_entries[reg].nub_info.value_regs == NULL) {
+ if (!DNBThreadGetRegisterValueByID(
+ pid, tid, g_reg_entries[reg].nub_info.set,
+ g_reg_entries[reg].nub_info.reg, &reg_value))
+ continue;
+
+ std::ostringstream reg_num;
+ reg_num << std::dec << g_reg_entries[reg].debugserver_regnum;
+ // Encode native byte ordered bytes as hex ascii
+ registers_dict_sp->AddBytesAsHexASCIIString(
+ reg_num.str(), reg_value.value.v_uint8,
+ g_reg_entries[reg].nub_info.size);
+ }
+ }
+ thread_dict_sp->AddItem("registers", registers_dict_sp);
+ }
+
+ // Add expedited stack memory so stack backtracing doesn't need to read
+ // anything from the
+ // frame pointer chain.
+ StackMemoryMap stack_mmap;
+ ReadStackMemory(pid, tid, stack_mmap);
+ if (!stack_mmap.empty()) {
+ JSONGenerator::ArraySP memory_array_sp(new JSONGenerator::Array());
+
+ for (const auto &stack_memory : stack_mmap) {
+ JSONGenerator::DictionarySP stack_memory_sp(
+ new JSONGenerator::Dictionary());
+ stack_memory_sp->AddIntegerItem("address", stack_memory.first);
+ stack_memory_sp->AddBytesAsHexASCIIString(
+ "bytes", stack_memory.second.bytes, stack_memory.second.length);
+ memory_array_sp->AddItem(stack_memory_sp);
+ }
+ thread_dict_sp->AddItem("memory", memory_array_sp);
+ }
+ }
+
+ threads_array_sp->AddItem(thread_dict_sp);
+ }
+ }
+ return threads_array_sp;
+}
+
+rnb_err_t RNBRemote::HandlePacket_jThreadsInfo(const char *p) {
+ JSONGenerator::ObjectSP threads_info_sp;
+ std::ostringstream json;
+ std::ostringstream reply_strm;
+ // If we haven't run the process yet, return an error.
+ if (m_ctx.HasValidProcessID()) {
+ const bool threads_with_valid_stop_info_only = false;
+ JSONGenerator::ObjectSP threads_info_sp =
+ GetJSONThreadsInfo(threads_with_valid_stop_info_only);
+
+ if (threads_info_sp) {
+ std::ostringstream strm;
+ threads_info_sp->Dump(strm);
+ std::string binary_packet = binary_encode_string(strm.str());
+ if (!binary_packet.empty())
+ return SendPacket(binary_packet.c_str());
+ }
+ }
+ return SendPacket("E85");
+}
+
+rnb_err_t RNBRemote::HandlePacket_jThreadExtendedInfo(const char *p) {
+ nub_process_t pid;
+ std::ostringstream json;
+ // If we haven't run the process yet, return an error.
+ if (!m_ctx.HasValidProcessID()) {
+ return SendPacket("E81");
+ }
+
+ pid = m_ctx.ProcessID();
+
+ const char thread_extended_info_str[] = {"jThreadExtendedInfo:{"};
+ if (strncmp(p, thread_extended_info_str,
+ sizeof(thread_extended_info_str) - 1) == 0) {
+ p += strlen(thread_extended_info_str);
+
+ uint64_t tid = get_integer_value_for_key_name_from_json("thread", p);
+ uint64_t plo_pthread_tsd_base_address_offset =
+ get_integer_value_for_key_name_from_json(
+ "plo_pthread_tsd_base_address_offset", p);
+ uint64_t plo_pthread_tsd_base_offset =
+ get_integer_value_for_key_name_from_json("plo_pthread_tsd_base_offset",
+ p);
+ uint64_t plo_pthread_tsd_entry_size =
+ get_integer_value_for_key_name_from_json("plo_pthread_tsd_entry_size",
+ p);
+ uint64_t dti_qos_class_index =
+ get_integer_value_for_key_name_from_json("dti_qos_class_index", p);
+
+ if (tid != INVALID_NUB_ADDRESS) {
+ nub_addr_t pthread_t_value = DNBGetPThreadT(pid, tid);
+
+ uint64_t tsd_address = INVALID_NUB_ADDRESS;
+ if (plo_pthread_tsd_entry_size != INVALID_NUB_ADDRESS &&
+ plo_pthread_tsd_base_offset != INVALID_NUB_ADDRESS &&
+ plo_pthread_tsd_entry_size != INVALID_NUB_ADDRESS) {
+ tsd_address = DNBGetTSDAddressForThread(
+ pid, tid, plo_pthread_tsd_base_address_offset,
+ plo_pthread_tsd_base_offset, plo_pthread_tsd_entry_size);
+ }
+
+ bool timed_out = false;
+ Genealogy::ThreadActivitySP thread_activity_sp;
+
+ // If the pthread_t value is invalid, or if we were able to fetch the
+ // thread's TSD base
+ // and got an invalid value back, then we have a thread in early startup
+ // or shutdown and
+ // it's possible that gathering the genealogy information for this thread
+ // go badly.
+ // Ideally fetching this info for a thread in these odd states shouldn't
+ // matter - but
+ // we've seen some problems with these new SPI and threads in edge-casey
+ // states.
+
+ double genealogy_fetch_time = 0;
+ if (pthread_t_value != INVALID_NUB_ADDRESS &&
+ tsd_address != INVALID_NUB_ADDRESS) {
+ DNBTimer timer(false);
+ thread_activity_sp = DNBGetGenealogyInfoForThread(pid, tid, timed_out);
+ genealogy_fetch_time = timer.ElapsedMicroSeconds(false) / 1000000.0;
+ }
+
+ std::unordered_set<uint32_t>
+ process_info_indexes; // an array of the process info #'s seen
+
+ json << "{";
+
+ bool need_to_print_comma = false;
+
+ if (thread_activity_sp && !timed_out) {
+ const Genealogy::Activity *activity =
+ &thread_activity_sp->current_activity;
+ bool need_vouchers_comma_sep = false;
+ json << "\"activity_query_timed_out\":false,";
+ if (genealogy_fetch_time != 0) {
+ // If we append the floating point value with << we'll get it in
+ // scientific
+ // notation.
+ char floating_point_ascii_buffer[64];
+ floating_point_ascii_buffer[0] = '\0';
+ snprintf(floating_point_ascii_buffer,
+ sizeof(floating_point_ascii_buffer), "%f",
+ genealogy_fetch_time);
+ if (strlen(floating_point_ascii_buffer) > 0) {
+ if (need_to_print_comma)
+ json << ",";
+ need_to_print_comma = true;
+ json << "\"activity_query_duration\":"
+ << floating_point_ascii_buffer;
+ }
+ }
+ if (activity->activity_id != 0) {
+ if (need_to_print_comma)
+ json << ",";
+ need_to_print_comma = true;
+ need_vouchers_comma_sep = true;
+ json << "\"activity\":{";
+ json << "\"start\":" << activity->activity_start << ",";
+ json << "\"id\":" << activity->activity_id << ",";
+ json << "\"parent_id\":" << activity->parent_id << ",";
+ json << "\"name\":\""
+ << json_string_quote_metachars(activity->activity_name) << "\",";
+ json << "\"reason\":\""
+ << json_string_quote_metachars(activity->reason) << "\"";
+ json << "}";
+ }
+ if (thread_activity_sp->messages.size() > 0) {
+ need_to_print_comma = true;
+ if (need_vouchers_comma_sep)
+ json << ",";
+ need_vouchers_comma_sep = true;
+ json << "\"trace_messages\":[";
+ bool printed_one_message = false;
+ for (auto iter = thread_activity_sp->messages.begin();
+ iter != thread_activity_sp->messages.end(); ++iter) {
+ if (printed_one_message)
+ json << ",";
+ else
+ printed_one_message = true;
+ json << "{";
+ json << "\"timestamp\":" << iter->timestamp << ",";
+ json << "\"activity_id\":" << iter->activity_id << ",";
+ json << "\"trace_id\":" << iter->trace_id << ",";
+ json << "\"thread\":" << iter->thread << ",";
+ json << "\"type\":" << (int)iter->type << ",";
+ json << "\"process_info_index\":" << iter->process_info_index
+ << ",";
+ process_info_indexes.insert(iter->process_info_index);
+ json << "\"message\":\""
+ << json_string_quote_metachars(iter->message) << "\"";
+ json << "}";
+ }
+ json << "]";
+ }
+ if (thread_activity_sp->breadcrumbs.size() == 1) {
+ need_to_print_comma = true;
+ if (need_vouchers_comma_sep)
+ json << ",";
+ need_vouchers_comma_sep = true;
+ json << "\"breadcrumb\":{";
+ for (auto iter = thread_activity_sp->breadcrumbs.begin();
+ iter != thread_activity_sp->breadcrumbs.end(); ++iter) {
+ json << "\"breadcrumb_id\":" << iter->breadcrumb_id << ",";
+ json << "\"activity_id\":" << iter->activity_id << ",";
+ json << "\"timestamp\":" << iter->timestamp << ",";
+ json << "\"name\":\"" << json_string_quote_metachars(iter->name)
+ << "\"";
+ }
+ json << "}";
+ }
+ if (process_info_indexes.size() > 0) {
+ need_to_print_comma = true;
+ if (need_vouchers_comma_sep)
+ json << ",";
+ need_vouchers_comma_sep = true;
+ bool printed_one_process_info = false;
+ for (auto iter = process_info_indexes.begin();
+ iter != process_info_indexes.end(); ++iter) {
+ if (printed_one_process_info)
+ json << ",";
+ Genealogy::ProcessExecutableInfoSP image_info_sp;
+ uint32_t idx = *iter;
+ image_info_sp = DNBGetGenealogyImageInfo(pid, idx);
+ if (image_info_sp) {
+ if (!printed_one_process_info) {
+ json << "\"process_infos\":[";
+ printed_one_process_info = true;
+ }
+
+ json << "{";
+ char uuid_buf[37];
+ uuid_unparse_upper(image_info_sp->image_uuid, uuid_buf);
+ json << "\"process_info_index\":" << idx << ",";
+ json << "\"image_path\":\""
+ << json_string_quote_metachars(image_info_sp->image_path)
+ << "\",";
+ json << "\"image_uuid\":\"" << uuid_buf << "\"";
+ json << "}";
+ }
+ }
+ if (printed_one_process_info)
+ json << "]";
+ }
+ } else {
+ if (timed_out) {
+ if (need_to_print_comma)
+ json << ",";
+ need_to_print_comma = true;
+ json << "\"activity_query_timed_out\":true";
+ if (genealogy_fetch_time != 0) {
+ // If we append the floating point value with << we'll get it in
+ // scientific
+ // notation.
+ char floating_point_ascii_buffer[64];
+ floating_point_ascii_buffer[0] = '\0';
+ snprintf(floating_point_ascii_buffer,
+ sizeof(floating_point_ascii_buffer), "%f",
+ genealogy_fetch_time);
+ if (strlen(floating_point_ascii_buffer) > 0) {
+ json << ",";
+ json << "\"activity_query_duration\":"
+ << floating_point_ascii_buffer;
+ }
+ }
+ }
+ }
+
+ if (tsd_address != INVALID_NUB_ADDRESS) {
+ if (need_to_print_comma)
+ json << ",";
+ need_to_print_comma = true;
+ json << "\"tsd_address\":" << tsd_address;
+
+ if (dti_qos_class_index != 0 && dti_qos_class_index != UINT64_MAX) {
+ ThreadInfo::QoS requested_qos = DNBGetRequestedQoSForThread(
+ pid, tid, tsd_address, dti_qos_class_index);
+ if (requested_qos.IsValid()) {
+ if (need_to_print_comma)
+ json << ",";
+ need_to_print_comma = true;
+ json << "\"requested_qos\":{";
+ json << "\"enum_value\":" << requested_qos.enum_value << ",";
+ json << "\"constant_name\":\""
+ << json_string_quote_metachars(requested_qos.constant_name)
+ << "\",";
+ json << "\"printable_name\":\""
+ << json_string_quote_metachars(requested_qos.printable_name)
+ << "\"";
+ json << "}";
+ }
+ }
+ }
+
+ if (pthread_t_value != INVALID_NUB_ADDRESS) {
+ if (need_to_print_comma)
+ json << ",";
+ need_to_print_comma = true;
+ json << "\"pthread_t\":" << pthread_t_value;
+ }
+
+ nub_addr_t dispatch_queue_t_value = DNBGetDispatchQueueT(pid, tid);
+ if (dispatch_queue_t_value != INVALID_NUB_ADDRESS) {
+ if (need_to_print_comma)
+ json << ",";
+ need_to_print_comma = true;
+ json << "\"dispatch_queue_t\":" << dispatch_queue_t_value;
+ }
+
+ json << "}";
+ std::string json_quoted = binary_encode_string(json.str());
+ return SendPacket(json_quoted);
+ }
+ }
+ return SendPacket("OK");
+}
+
+// This packet may be called in one of three ways:
+//
+// jGetLoadedDynamicLibrariesInfos:{"image_count":40,"image_list_address":4295244704}
+// Look for an array of the old dyld_all_image_infos style of binary infos
+// at the image_list_address.
+// This an array of {void* load_addr, void* mod_date, void* pathname}
+//
+// jGetLoadedDynamicLibrariesInfos:{"fetch_all_solibs":true}
+// Use the new style (macOS 10.12, tvOS 10, iOS 10, watchOS 3) dyld SPI to
+// get a list of all the
+// libraries loaded
+//
+// jGetLoadedDynamicLibrariesInfos:{"solib_addresses":[8382824135,3258302053,830202858503]}
+// Use the new style (macOS 10.12, tvOS 10, iOS 10, watchOS 3) dyld SPI to
+// get the information
+// about the libraries loaded at these addresses.
+//
+rnb_err_t
+RNBRemote::HandlePacket_jGetLoadedDynamicLibrariesInfos(const char *p) {
+ nub_process_t pid;
+ // If we haven't run the process yet, return an error.
+ if (!m_ctx.HasValidProcessID()) {
+ return SendPacket("E83");
+ }
+
+ pid = m_ctx.ProcessID();
+
+ const char get_loaded_dynamic_libraries_infos_str[] = {
+ "jGetLoadedDynamicLibrariesInfos:{"};
+ if (strncmp(p, get_loaded_dynamic_libraries_infos_str,
+ sizeof(get_loaded_dynamic_libraries_infos_str) - 1) == 0) {
+ p += strlen(get_loaded_dynamic_libraries_infos_str);
+
+ JSONGenerator::ObjectSP json_sp;
+
+ std::vector<uint64_t> macho_addresses;
+ bool fetch_all_solibs = false;
+ if (get_boolean_value_for_key_name_from_json("fetch_all_solibs", p,
+ fetch_all_solibs) &&
+ fetch_all_solibs) {
+ json_sp = DNBGetAllLoadedLibrariesInfos(pid);
+ } else if (get_array_of_ints_value_for_key_name_from_json(
+ "solib_addresses", p, macho_addresses)) {
+ json_sp = DNBGetLibrariesInfoForAddresses(pid, macho_addresses);
+ } else {
+ nub_addr_t image_list_address =
+ get_integer_value_for_key_name_from_json("image_list_address", p);
+ nub_addr_t image_count =
+ get_integer_value_for_key_name_from_json("image_count", p);
+
+ if (image_list_address != INVALID_NUB_ADDRESS &&
+ image_count != INVALID_NUB_ADDRESS) {
+ json_sp = DNBGetLoadedDynamicLibrariesInfos(pid, image_list_address,
+ image_count);
+ }
+ }
+
+ if (json_sp.get()) {
+ std::ostringstream json_str;
+ json_sp->Dump(json_str);
+ if (json_str.str().size() > 0) {
+ std::string json_str_quoted = binary_encode_string(json_str.str());
+ return SendPacket(json_str_quoted.c_str());
+ } else {
+ SendPacket("E84");
+ }
+ }
+ }
+ return SendPacket("OK");
+}
+
+// This packet does not currently take any arguments. So the behavior is
+// jGetSharedCacheInfo:{}
+// send information about the inferior's shared cache
+// jGetSharedCacheInfo:
+// send "OK" to indicate that this packet is supported
+rnb_err_t RNBRemote::HandlePacket_jGetSharedCacheInfo(const char *p) {
+ nub_process_t pid;
+ // If we haven't run the process yet, return an error.
+ if (!m_ctx.HasValidProcessID()) {
+ return SendPacket("E85");
+ }
+
+ pid = m_ctx.ProcessID();
+
+ const char get_shared_cache_info_str[] = {"jGetSharedCacheInfo:{"};
+ if (strncmp(p, get_shared_cache_info_str,
+ sizeof(get_shared_cache_info_str) - 1) == 0) {
+ JSONGenerator::ObjectSP json_sp = DNBGetSharedCacheInfo(pid);
+
+ if (json_sp.get()) {
+ std::ostringstream json_str;
+ json_sp->Dump(json_str);
+ if (json_str.str().size() > 0) {
+ std::string json_str_quoted = binary_encode_string(json_str.str());
+ return SendPacket(json_str_quoted.c_str());
+ } else {
+ SendPacket("E86");
+ }
+ }
+ }
+ return SendPacket("OK");
+}
+
+static bool MachHeaderIsMainExecutable(nub_process_t pid, uint32_t addr_size,
+ nub_addr_t mach_header_addr,
+ mach_header &mh) {
+ DNBLogThreadedIf(LOG_RNB_PROC, "GetMachHeaderForMainExecutable(pid = %u, "
+ "addr_size = %u, mach_header_addr = "
+ "0x%16.16llx)",
+ pid, addr_size, mach_header_addr);
+ const nub_size_t bytes_read =
+ DNBProcessMemoryRead(pid, mach_header_addr, sizeof(mh), &mh);
+ if (bytes_read == sizeof(mh)) {
+ DNBLogThreadedIf(
+ LOG_RNB_PROC, "GetMachHeaderForMainExecutable(pid = %u, addr_size = "
+ "%u, mach_header_addr = 0x%16.16llx): mh = {\n magic = "
+ "0x%8.8x\n cpu = 0x%8.8x\n sub = 0x%8.8x\n filetype = "
+ "%u\n ncmds = %u\n sizeofcmds = 0x%8.8x\n flags = "
+ "0x%8.8x }",
+ pid, addr_size, mach_header_addr, mh.magic, mh.cputype, mh.cpusubtype,
+ mh.filetype, mh.ncmds, mh.sizeofcmds, mh.flags);
+ if ((addr_size == 4 && mh.magic == MH_MAGIC) ||
+ (addr_size == 8 && mh.magic == MH_MAGIC_64)) {
+ if (mh.filetype == MH_EXECUTE) {
+ DNBLogThreadedIf(LOG_RNB_PROC, "GetMachHeaderForMainExecutable(pid = "
+ "%u, addr_size = %u, mach_header_addr = "
+ "0x%16.16llx) -> this is the "
+ "executable!!!",
+ pid, addr_size, mach_header_addr);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static nub_addr_t GetMachHeaderForMainExecutable(const nub_process_t pid,
+ const uint32_t addr_size,
+ mach_header &mh) {
+ struct AllImageInfos {
+ uint32_t version;
+ uint32_t dylib_info_count;
+ uint64_t dylib_info_addr;
+ };
+
+ uint64_t mach_header_addr = 0;
+
+ const nub_addr_t shlib_addr = DNBProcessGetSharedLibraryInfoAddress(pid);
+ uint8_t bytes[256];
+ nub_size_t bytes_read = 0;
+ DNBDataRef data(bytes, sizeof(bytes), false);
+ DNBDataRef::offset_t offset = 0;
+ data.SetPointerSize(addr_size);
+
+ // When we are sitting at __dyld_start, the kernel has placed the
+ // address of the mach header of the main executable on the stack. If we
+ // read the SP and dereference a pointer, we might find the mach header
+ // for the executable. We also just make sure there is only 1 thread
+ // since if we are at __dyld_start we shouldn't have multiple threads.
+ if (DNBProcessGetNumThreads(pid) == 1) {
+ nub_thread_t tid = DNBProcessGetThreadAtIndex(pid, 0);
+ if (tid != INVALID_NUB_THREAD) {
+ DNBRegisterValue sp_value;
+ if (DNBThreadGetRegisterValueByID(pid, tid, REGISTER_SET_GENERIC,
+ GENERIC_REGNUM_SP, &sp_value)) {
+ uint64_t sp =
+ addr_size == 8 ? sp_value.value.uint64 : sp_value.value.uint32;
+ bytes_read = DNBProcessMemoryRead(pid, sp, addr_size, bytes);
+ if (bytes_read == addr_size) {
+ offset = 0;
+ mach_header_addr = data.GetPointer(&offset);
+ if (MachHeaderIsMainExecutable(pid, addr_size, mach_header_addr, mh))
+ return mach_header_addr;
+ }
+ }
+ }
+ }
+
+ // Check the dyld_all_image_info structure for a list of mach header
+ // since it is a very easy thing to check
+ if (shlib_addr != INVALID_NUB_ADDRESS) {
+ bytes_read =
+ DNBProcessMemoryRead(pid, shlib_addr, sizeof(AllImageInfos), bytes);
+ if (bytes_read > 0) {
+ AllImageInfos aii;
+ offset = 0;
+ aii.version = data.Get32(&offset);
+ aii.dylib_info_count = data.Get32(&offset);
+ if (aii.dylib_info_count > 0) {
+ aii.dylib_info_addr = data.GetPointer(&offset);
+ if (aii.dylib_info_addr != 0) {
+ const size_t image_info_byte_size = 3 * addr_size;
+ for (uint32_t i = 0; i < aii.dylib_info_count; ++i) {
+ bytes_read = DNBProcessMemoryRead(pid, aii.dylib_info_addr +
+ i * image_info_byte_size,
+ image_info_byte_size, bytes);
+ if (bytes_read != image_info_byte_size)
+ break;
+ offset = 0;
+ mach_header_addr = data.GetPointer(&offset);
+ if (MachHeaderIsMainExecutable(pid, addr_size, mach_header_addr,
+ mh))
+ return mach_header_addr;
+ }
+ }
+ }
+ }
+ }
+
+ // We failed to find the executable's mach header from the all image
+ // infos and by dereferencing the stack pointer. Now we fall back to
+ // enumerating the memory regions and looking for regions that are
+ // executable.
+ DNBRegionInfo region_info;
+ mach_header_addr = 0;
+ while (DNBProcessMemoryRegionInfo(pid, mach_header_addr, &region_info)) {
+ if (region_info.size == 0)
+ break;
+
+ if (region_info.permissions & eMemoryPermissionsExecutable) {
+ DNBLogThreadedIf(
+ LOG_RNB_PROC, "[0x%16.16llx - 0x%16.16llx) permissions = %c%c%c: "
+ "checking region for executable mach header",
+ region_info.addr, region_info.addr + region_info.size,
+ (region_info.permissions & eMemoryPermissionsReadable) ? 'r' : '-',
+ (region_info.permissions & eMemoryPermissionsWritable) ? 'w' : '-',
+ (region_info.permissions & eMemoryPermissionsExecutable) ? 'x' : '-');
+ if (MachHeaderIsMainExecutable(pid, addr_size, mach_header_addr, mh))
+ return mach_header_addr;
+ } else {
+ DNBLogThreadedIf(
+ LOG_RNB_PROC,
+ "[0x%16.16llx - 0x%16.16llx): permissions = %c%c%c: skipping region",
+ region_info.addr, region_info.addr + region_info.size,
+ (region_info.permissions & eMemoryPermissionsReadable) ? 'r' : '-',
+ (region_info.permissions & eMemoryPermissionsWritable) ? 'w' : '-',
+ (region_info.permissions & eMemoryPermissionsExecutable) ? 'x' : '-');
+ }
+ // Set the address to the next mapped region
+ mach_header_addr = region_info.addr + region_info.size;
+ }
+ bzero(&mh, sizeof(mh));
+ return INVALID_NUB_ADDRESS;
+}
+
+rnb_err_t RNBRemote::HandlePacket_qSymbol(const char *command) {
+ const char *p = command;
+ p += strlen("qSymbol:");
+ const char *sep = strchr(p, ':');
+
+ std::string symbol_name;
+ std::string symbol_value_str;
+ // Extract the symbol value if there is one
+ if (sep > p)
+ symbol_value_str.assign(p, sep - p);
+ p = sep + 1;
+
+ if (*p) {
+ // We have a symbol name
+ symbol_name = decode_hex_ascii_string(p);
+ if (!symbol_value_str.empty()) {
+ nub_addr_t symbol_value = decode_uint64(symbol_value_str.c_str(), 16);
+ if (symbol_name == "dispatch_queue_offsets")
+ m_dispatch_queue_offsets_addr = symbol_value;
+ }
+ ++m_qSymbol_index;
+ } else {
+ // No symbol name, set our symbol index to zero so we can
+ // read any symbols that we need
+ m_qSymbol_index = 0;
+ }
+
+ symbol_name.clear();
+
+ if (m_qSymbol_index == 0) {
+ if (m_dispatch_queue_offsets_addr == INVALID_NUB_ADDRESS)
+ symbol_name = "dispatch_queue_offsets";
+ else
+ ++m_qSymbol_index;
+ }
+
+ // // Lookup next symbol when we have one...
+ // if (m_qSymbol_index == 1)
+ // {
+ // }
+
+ if (symbol_name.empty()) {
+ // Done with symbol lookups
+ return SendPacket("OK");
+ } else {
+ std::ostringstream reply;
+ reply << "qSymbol:";
+ for (size_t i = 0; i < symbol_name.size(); ++i)
+ reply << RAWHEX8(symbol_name[i]);
+ return SendPacket(reply.str().c_str());
+ }
+}
+
+// Note that all numeric values returned by qProcessInfo are hex encoded,
+// including the pid and the cpu type.
+
+rnb_err_t RNBRemote::HandlePacket_qProcessInfo(const char *p) {
+ nub_process_t pid;
+ std::ostringstream rep;
+
+ // If we haven't run the process yet, return an error.
+ if (!m_ctx.HasValidProcessID())
+ return SendPacket("E68");
+
+ pid = m_ctx.ProcessID();
+
+ rep << "pid:" << std::hex << pid << ';';
+
+ int procpid_mib[4];
+ procpid_mib[0] = CTL_KERN;
+ procpid_mib[1] = KERN_PROC;
+ procpid_mib[2] = KERN_PROC_PID;
+ procpid_mib[3] = pid;
+ struct kinfo_proc proc_kinfo;
+ size_t proc_kinfo_size = sizeof(struct kinfo_proc);
+
+ if (::sysctl(procpid_mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0) {
+ if (proc_kinfo_size > 0) {
+ rep << "parent-pid:" << std::hex << proc_kinfo.kp_eproc.e_ppid << ';';
+ rep << "real-uid:" << std::hex << proc_kinfo.kp_eproc.e_pcred.p_ruid
+ << ';';
+ rep << "real-gid:" << std::hex << proc_kinfo.kp_eproc.e_pcred.p_rgid
+ << ';';
+ rep << "effective-uid:" << std::hex << proc_kinfo.kp_eproc.e_ucred.cr_uid
+ << ';';
+ if (proc_kinfo.kp_eproc.e_ucred.cr_ngroups > 0)
+ rep << "effective-gid:" << std::hex
+ << proc_kinfo.kp_eproc.e_ucred.cr_groups[0] << ';';
+ }
+ }
+
+ cpu_type_t cputype = DNBProcessGetCPUType(pid);
+ if (cputype == 0) {
+ DNBLog("Unable to get the process cpu_type, making a best guess.");
+ cputype = best_guess_cpu_type();
+ }
+
+ uint32_t addr_size = 0;
+ if (cputype != 0) {
+ rep << "cputype:" << std::hex << cputype << ";";
+ if (cputype & CPU_ARCH_ABI64)
+ addr_size = 8;
+ else
+ addr_size = 4;
+ }
+
+ bool host_cpu_is_64bit = false;
+ 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 = is64bit_capable != 0;
+
+ uint32_t cpusubtype;
+ size_t cpusubtype_len = sizeof(cpusubtype);
+ if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &cpusubtype_len, NULL, 0) ==
+ 0) {
+ // If a process is CPU_TYPE_X86, then ignore the cpusubtype that we detected
+ // from the host and use CPU_SUBTYPE_I386_ALL because we don't want the
+ // CPU_SUBTYPE_X86_ARCH1 or CPU_SUBTYPE_X86_64_H to be used as the cpu
+ // subtype
+ // for i386...
+ if (host_cpu_is_64bit) {
+ if (cputype == CPU_TYPE_X86) {
+ cpusubtype = 3; // CPU_SUBTYPE_I386_ALL
+ } else if (cputype == CPU_TYPE_ARM) {
+ // We can query a process' cputype but we cannot query a process'
+ // cpusubtype.
+ // If the process has cputype CPU_TYPE_ARM, then it is an armv7 (32-bit
+ // process) and we
+ // need to override the host cpusubtype (which is in the
+ // CPU_SUBTYPE_ARM64 subtype namespace)
+ // with a reasonable CPU_SUBTYPE_ARMV7 subtype.
+ cpusubtype = 12; // CPU_SUBTYPE_ARM_V7K
+ }
+ }
+#if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
+ // on arm64_32 devices, the machine's native cpu type is
+ // CPU_TYPE_ARM64 and subtype is 2 indicating arm64e.
+ // But we change the cputype to CPU_TYPE_ARM64_32 because
+ // the user processes are all ILP32 processes today.
+ // We also need to rewrite the cpusubtype so we vend
+ // a valid cputype + cpusubtype combination.
+ if (cputype == CPU_TYPE_ARM64_32 && cpusubtype == 2)
+ cpusubtype = CPU_SUBTYPE_ARM64_32_V8;
+#endif
+
+ rep << "cpusubtype:" << std::hex << cpusubtype << ';';
+ }
+
+ bool os_handled = false;
+ if (addr_size > 0) {
+ rep << "ptrsize:" << std::dec << addr_size << ';';
+
+#if (defined(__x86_64__) || defined(__i386__))
+ // Try and get the OS type by looking at the load commands in the main
+ // executable and looking for a LC_VERSION_MIN load command. This is the
+ // most reliable way to determine the "ostype" value when on desktop.
+
+ mach_header mh;
+ nub_addr_t exe_mach_header_addr =
+ GetMachHeaderForMainExecutable(pid, addr_size, mh);
+ if (exe_mach_header_addr != INVALID_NUB_ADDRESS) {
+ uint64_t load_command_addr =
+ exe_mach_header_addr +
+ ((addr_size == 8) ? sizeof(mach_header_64) : sizeof(mach_header));
+ load_command lc;
+ for (uint32_t i = 0; i < mh.ncmds && !os_handled; ++i) {
+ const nub_size_t bytes_read =
+ DNBProcessMemoryRead(pid, load_command_addr, sizeof(lc), &lc);
+ (void)bytes_read;
+
+ uint32_t major_version, minor_version, patch_version;
+ auto *platform = DNBGetDeploymentInfo(pid, lc, load_command_addr,
+ major_version, minor_version,
+ patch_version);
+ if (platform) {
+ os_handled = true;
+ rep << "ostype:" << platform << ";";
+ break;
+ }
+ load_command_addr = load_command_addr + lc.cmdsize;
+ }
+ }
+#endif // when compiling this on x86 targets
+ }
+
+ // If we weren't able to find the OS in a LC_VERSION_MIN load command, try
+ // to set it correctly by using the cpu type and other tricks
+ if (!os_handled) {
+ // The OS in the triple should be "ios" or "macosx" which doesn't match our
+ // "Darwin" which gets returned from "kern.ostype", so we need to hardcode
+ // this for now.
+ if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64
+ || cputype == CPU_TYPE_ARM64_32) {
+#if defined(TARGET_OS_TV) && TARGET_OS_TV == 1
+ rep << "ostype:tvos;";
+#elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
+ rep << "ostype:watchos;";
+#elif defined(TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1
+ rep << "ostype:bridgeos;";
+#else
+ rep << "ostype:ios;";
+#endif
+ } else {
+ bool is_ios_simulator = false;
+ if (cputype == CPU_TYPE_X86 || cputype == CPU_TYPE_X86_64) {
+ // Check for iOS simulator binaries by getting the process argument
+ // and environment and checking for SIMULATOR_UDID in the environment
+ int proc_args_mib[3] = {CTL_KERN, KERN_PROCARGS2, (int)pid};
+
+ uint8_t arg_data[8192];
+ size_t arg_data_size = sizeof(arg_data);
+ if (::sysctl(proc_args_mib, 3, arg_data, &arg_data_size, NULL, 0) ==
+ 0) {
+ DNBDataRef data(arg_data, arg_data_size, false);
+ DNBDataRef::offset_t offset = 0;
+ uint32_t argc = data.Get32(&offset);
+ const char *cstr;
+
+ cstr = data.GetCStr(&offset);
+ if (cstr) {
+ // Skip NULLs
+ while (true) {
+ const char *p = data.PeekCStr(offset);
+ if ((p == NULL) || (*p != '\0'))
+ break;
+ ++offset;
+ }
+ // Now skip all arguments
+ for (uint32_t i = 0; i < argc; ++i) {
+ data.GetCStr(&offset);
+ }
+
+ // Now iterate across all environment variables
+ while ((cstr = data.GetCStr(&offset))) {
+ if (strncmp(cstr, "SIMULATOR_UDID=", strlen("SIMULATOR_UDID=")) ==
+ 0) {
+ is_ios_simulator = true;
+ break;
+ }
+ if (cstr[0] == '\0')
+ break;
+ }
+ }
+ }
+ }
+ if (is_ios_simulator) {
+#if defined(TARGET_OS_TV) && TARGET_OS_TV == 1
+ rep << "ostype:tvos;";
+#elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
+ rep << "ostype:watchos;";
+#elif defined(TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1
+ rep << "ostype:bridgeos;";
+#else
+ rep << "ostype:ios;";
+#endif
+ } else {
+ rep << "ostype:macosx;";
+ }
+ }
+ }
+
+ rep << "vendor:apple;";
+
+#if defined(__LITTLE_ENDIAN__)
+ rep << "endian:little;";
+#elif defined(__BIG_ENDIAN__)
+ rep << "endian:big;";
+#elif defined(__PDP_ENDIAN__)
+ rep << "endian:pdp;";
+#endif
+
+ if (addr_size == 0) {
+#if (defined(__x86_64__) || defined(__i386__)) && defined(x86_THREAD_STATE)
+ nub_thread_t thread = DNBProcessGetCurrentThreadMachPort(pid);
+ kern_return_t kr;
+ x86_thread_state_t gp_regs;
+ mach_msg_type_number_t gp_count = x86_THREAD_STATE_COUNT;
+ kr = thread_get_state(static_cast<thread_act_t>(thread), x86_THREAD_STATE,
+ (thread_state_t)&gp_regs, &gp_count);
+ if (kr == KERN_SUCCESS) {
+ if (gp_regs.tsh.flavor == x86_THREAD_STATE64)
+ rep << "ptrsize:8;";
+ else
+ rep << "ptrsize:4;";
+ }
+#elif defined(__arm__)
+ rep << "ptrsize:4;";
+#elif (defined(__arm64__) || defined(__aarch64__)) && \
+ defined(ARM_UNIFIED_THREAD_STATE)
+ nub_thread_t thread = DNBProcessGetCurrentThreadMachPort(pid);
+ kern_return_t kr;
+ arm_unified_thread_state_t gp_regs;
+ mach_msg_type_number_t gp_count = ARM_UNIFIED_THREAD_STATE_COUNT;
+ kr = thread_get_state(thread, ARM_UNIFIED_THREAD_STATE,
+ (thread_state_t)&gp_regs, &gp_count);
+ if (kr == KERN_SUCCESS) {
+ if (gp_regs.ash.flavor == ARM_THREAD_STATE64)
+ rep << "ptrsize:8;";
+ else
+ rep << "ptrsize:4;";
+ }
+#endif
+ }
+
+ return SendPacket(rep.str());
+}
+
+const RNBRemote::DispatchQueueOffsets *RNBRemote::GetDispatchQueueOffsets() {
+ if (!m_dispatch_queue_offsets.IsValid() &&
+ m_dispatch_queue_offsets_addr != INVALID_NUB_ADDRESS &&
+ m_ctx.HasValidProcessID()) {
+ nub_process_t pid = m_ctx.ProcessID();
+ nub_size_t bytes_read = DNBProcessMemoryRead(
+ pid, m_dispatch_queue_offsets_addr, sizeof(m_dispatch_queue_offsets),
+ &m_dispatch_queue_offsets);
+ if (bytes_read != sizeof(m_dispatch_queue_offsets))
+ m_dispatch_queue_offsets.Clear();
+ }
+
+ if (m_dispatch_queue_offsets.IsValid())
+ return &m_dispatch_queue_offsets;
+ else
+ return nullptr;
+}
+
+void RNBRemote::EnableCompressionNextSendPacket(compression_types type) {
+ m_compression_mode = type;
+ m_enable_compression_next_send_packet = true;
+}
+
+compression_types RNBRemote::GetCompressionType() {
+ // The first packet we send back to the debugger after a QEnableCompression
+ // request
+ // should be uncompressed -- so we can indicate whether the compression was
+ // enabled
+ // or not via OK / Enn returns. After that, all packets sent will be using
+ // the
+ // compression protocol.
+
+ if (m_enable_compression_next_send_packet) {
+ // One time, we send back "None" as our compression type
+ m_enable_compression_next_send_packet = false;
+ return compression_types::none;
+ }
+ return m_compression_mode;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/RNBRemote.h b/gnu/llvm/lldb/tools/debugserver/source/RNBRemote.h
new file mode 100644
index 00000000000..88d82091130
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/RNBRemote.h
@@ -0,0 +1,431 @@
+
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 12/12/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __RNBRemote_h__
+#define __RNBRemote_h__
+
+#include "DNB.h"
+#include "PThreadMutex.h"
+#include "RNBContext.h"
+#include "RNBDefs.h"
+#include "RNBSocket.h"
+#include <deque>
+#include <map>
+#include <string>
+#include <vector>
+
+class RNBSocket;
+class RNBContext;
+class PThreadEvents;
+
+enum event_loop_mode { debug_nub, gdb_remote_protocol, done };
+
+enum class compression_types { zlib_deflate, lz4, lzma, lzfse, none };
+
+class RNBRemote {
+public:
+ enum PacketEnum {
+ invalid_packet = 0,
+ ack, // '+'
+ nack, // '-'
+ halt, // ^C (async halt)
+ use_extended_mode, // '!'
+ why_halted, // '?'
+ set_argv, // 'A'
+ set_bp, // 'B'
+ cont, // 'c'
+ continue_with_sig, // 'C'
+ detach, // 'D'
+ read_general_regs, // 'g'
+ write_general_regs, // 'G'
+ set_thread, // 'H'
+ step_inferior_one_cycle, // 'i'
+ signal_and_step_inf_one_cycle, // 'I'
+ kill, // 'k'
+ read_memory, // 'm'
+ write_memory, // 'M'
+ read_register, // 'p'
+ write_register, // 'P'
+ restart, // 'R'
+ single_step, // 's'
+ single_step_with_sig, // 'S'
+ search_mem_backwards, // 't'
+ thread_alive_p, // 'T'
+ vattach, // 'vAttach;pid'
+ vattachwait, // 'vAttachWait:XX...' where XX is one or more hex encoded
+ // process name ASCII bytes
+ vattachorwait, // 'vAttachOrWait:XX...' where XX is one or more hex encoded
+ // process name ASCII bytes
+ vattachname, // 'vAttachName:XX...' where XX is one or more hex encoded
+ // process name ASCII bytes
+ vcont, // 'vCont'
+ vcont_list_actions, // 'vCont?'
+ read_data_from_memory, // 'x'
+ write_data_to_memory, // 'X'
+ insert_mem_bp, // 'Z0'
+ remove_mem_bp, // 'z0'
+ insert_hardware_bp, // 'Z1'
+ remove_hardware_bp, // 'z1'
+ insert_write_watch_bp, // 'Z2'
+ remove_write_watch_bp, // 'z2'
+ insert_read_watch_bp, // 'Z3'
+ remove_read_watch_bp, // 'z3'
+ insert_access_watch_bp, // 'Z4'
+ remove_access_watch_bp, // 'z4'
+
+ query_monitor, // 'qRcmd'
+ query_current_thread_id, // 'qC'
+ query_get_pid, // 'qGetPid'
+ query_echo, // 'qEcho'
+ query_thread_ids_first, // 'qfThreadInfo'
+ query_thread_ids_subsequent, // 'qsThreadInfo'
+ query_thread_extra_info, // 'qThreadExtraInfo'
+ query_thread_stop_info, // 'qThreadStopInfo'
+ query_image_offsets, // 'qOffsets'
+ query_symbol_lookup, // 'qSymbol'
+ query_launch_success, // 'qLaunchSuccess'
+ query_register_info, // 'qRegisterInfo'
+ query_shlib_notify_info_addr, // 'qShlibInfoAddr'
+ query_step_packet_supported, // 'qStepPacketSupported'
+ query_supported_features, // 'qSupported'
+ query_vattachorwait_supported, // 'qVAttachOrWaitSupported'
+ query_sync_thread_state_supported, // 'QSyncThreadState'
+ query_host_info, // 'qHostInfo'
+ query_gdb_server_version, // 'qGDBServerVersion'
+ query_process_info, // 'qProcessInfo'
+ json_query_thread_extended_info, // 'jThreadExtendedInfo'
+ json_query_get_loaded_dynamic_libraries_infos, // 'jGetLoadedDynamicLibrariesInfos'
+ json_query_threads_info, // 'jThreadsInfo'
+ json_query_get_shared_cache_info, // 'jGetSharedCacheInfo'
+ pass_signals_to_inferior, // 'QPassSignals'
+ start_noack_mode, // 'QStartNoAckMode'
+ prefix_reg_packets_with_tid, // 'QPrefixRegisterPacketsWithThreadID
+ set_logging_mode, // 'QSetLogging:'
+ set_max_packet_size, // 'QSetMaxPacketSize:'
+ set_max_payload_size, // 'QSetMaxPayloadSize:'
+ set_environment_variable, // 'QEnvironment:'
+ set_environment_variable_hex, // 'QEnvironmentHexEncoded:'
+ set_launch_arch, // 'QLaunchArch:'
+ set_disable_aslr, // 'QSetDisableASLR:'
+ set_stdin, // 'QSetSTDIN:'
+ set_stdout, // 'QSetSTDOUT:'
+ set_stderr, // 'QSetSTDERR:'
+ set_working_dir, // 'QSetWorkingDir:'
+ set_list_threads_in_stop_reply, // 'QListThreadsInStopReply:'
+ sync_thread_state, // 'QSyncThreadState:'
+ memory_region_info, // 'qMemoryRegionInfo:'
+ get_profile_data, // 'qGetProfileData'
+ set_enable_profiling, // 'QSetEnableAsyncProfiling'
+ enable_compression, // 'QEnableCompression:'
+ watchpoint_support_info, // 'qWatchpointSupportInfo:'
+ allocate_memory, // '_M'
+ deallocate_memory, // '_m'
+ set_process_event, // 'QSetProcessEvent:'
+ save_register_state, // '_g'
+ restore_register_state, // '_G'
+ speed_test, // 'qSpeedTest:'
+ set_detach_on_error, // 'QSetDetachOnError:'
+ query_transfer, // 'qXfer:'
+ query_supported_async_json_packets, // 'QSupportedAsyncJSONPackets'
+ configure_darwin_log, // 'ConfigureDarwinLog:'
+ unknown_type
+ };
+
+ typedef rnb_err_t (RNBRemote::*HandlePacketCallback)(const char *p);
+
+ RNBRemote();
+ ~RNBRemote();
+
+ void Initialize();
+
+ bool InitializeRegisters(bool force = false);
+
+ rnb_err_t HandleAsyncPacket(PacketEnum *type = NULL);
+ rnb_err_t HandleReceivedPacket(PacketEnum *type = NULL);
+
+ nub_thread_t GetContinueThread() const { return m_continue_thread; }
+
+ void SetContinueThread(nub_thread_t tid) { m_continue_thread = tid; }
+
+ nub_thread_t GetCurrentThread() const {
+ if (m_thread == 0 || m_thread == (nub_thread_t)-1)
+ return DNBProcessGetCurrentThread(m_ctx.ProcessID());
+ return m_thread;
+ }
+
+ void SetCurrentThread(nub_thread_t tid) {
+ DNBProcessSetCurrentThread(m_ctx.ProcessID(), tid);
+ m_thread = tid;
+ }
+
+ static void *ThreadFunctionReadRemoteData(void *arg);
+ void StartReadRemoteDataThread();
+ void StopReadRemoteDataThread();
+
+ void NotifyThatProcessStopped(void);
+
+ rnb_err_t HandlePacket_A(const char *p);
+ rnb_err_t HandlePacket_H(const char *p);
+ rnb_err_t HandlePacket_qC(const char *p);
+ rnb_err_t HandlePacket_qRcmd(const char *p);
+ rnb_err_t HandlePacket_qGetPid(const char *p);
+ rnb_err_t HandlePacket_qEcho(const char *p);
+ rnb_err_t HandlePacket_qLaunchSuccess(const char *p);
+ rnb_err_t HandlePacket_qRegisterInfo(const char *p);
+ rnb_err_t HandlePacket_qShlibInfoAddr(const char *p);
+ rnb_err_t HandlePacket_qStepPacketSupported(const char *p);
+ rnb_err_t HandlePacket_qVAttachOrWaitSupported(const char *p);
+ rnb_err_t HandlePacket_qSyncThreadStateSupported(const char *p);
+ rnb_err_t HandlePacket_qThreadInfo(const char *p);
+ rnb_err_t HandlePacket_jThreadExtendedInfo(const char *p);
+ rnb_err_t HandlePacket_jGetLoadedDynamicLibrariesInfos(const char *p);
+ rnb_err_t HandlePacket_jThreadsInfo(const char *p);
+ rnb_err_t HandlePacket_jGetSharedCacheInfo(const char *p);
+ rnb_err_t HandlePacket_qThreadExtraInfo(const char *p);
+ rnb_err_t HandlePacket_qThreadStopInfo(const char *p);
+ rnb_err_t HandlePacket_qHostInfo(const char *p);
+ rnb_err_t HandlePacket_qGDBServerVersion(const char *p);
+ rnb_err_t HandlePacket_qProcessInfo(const char *p);
+ rnb_err_t HandlePacket_qSymbol(const char *p);
+ rnb_err_t HandlePacket_QStartNoAckMode(const char *p);
+ rnb_err_t HandlePacket_QThreadSuffixSupported(const char *p);
+ rnb_err_t HandlePacket_QSetLogging(const char *p);
+ rnb_err_t HandlePacket_QSetDisableASLR(const char *p);
+ rnb_err_t HandlePacket_QSetSTDIO(const char *p);
+ rnb_err_t HandlePacket_QSetWorkingDir(const char *p);
+ rnb_err_t HandlePacket_QSetMaxPayloadSize(const char *p);
+ rnb_err_t HandlePacket_QSetMaxPacketSize(const char *p);
+ rnb_err_t HandlePacket_QEnvironment(const char *p);
+ rnb_err_t HandlePacket_QEnvironmentHexEncoded(const char *p);
+ rnb_err_t HandlePacket_QLaunchArch(const char *p);
+ rnb_err_t HandlePacket_QListThreadsInStopReply(const char *p);
+ rnb_err_t HandlePacket_QSyncThreadState(const char *p);
+ rnb_err_t HandlePacket_QPrefixRegisterPacketsWithThreadID(const char *p);
+ rnb_err_t HandlePacket_QSetProcessEvent(const char *p);
+ rnb_err_t HandlePacket_last_signal(const char *p);
+ rnb_err_t HandlePacket_m(const char *p);
+ rnb_err_t HandlePacket_M(const char *p);
+ rnb_err_t HandlePacket_x(const char *p);
+ rnb_err_t HandlePacket_X(const char *p);
+ rnb_err_t HandlePacket_g(const char *p);
+ rnb_err_t HandlePacket_G(const char *p);
+ rnb_err_t HandlePacket_z(const char *p);
+ rnb_err_t HandlePacket_T(const char *p);
+ rnb_err_t HandlePacket_p(const char *p);
+ rnb_err_t HandlePacket_P(const char *p);
+ rnb_err_t HandlePacket_c(const char *p);
+ rnb_err_t HandlePacket_C(const char *p);
+ rnb_err_t HandlePacket_D(const char *p);
+ rnb_err_t HandlePacket_k(const char *p);
+ rnb_err_t HandlePacket_s(const char *p);
+ rnb_err_t HandlePacket_S(const char *p);
+ rnb_err_t HandlePacket_qSupported(const char *p);
+ rnb_err_t HandlePacket_v(const char *p);
+ rnb_err_t HandlePacket_UNIMPLEMENTED(const char *p);
+ rnb_err_t HandlePacket_ILLFORMED(const char *file, int line, const char *p,
+ const char *description);
+ rnb_err_t HandlePacket_AllocateMemory(const char *p);
+ rnb_err_t HandlePacket_DeallocateMemory(const char *p);
+ rnb_err_t HandlePacket_SaveRegisterState(const char *p);
+ rnb_err_t HandlePacket_RestoreRegisterState(const char *p);
+ rnb_err_t HandlePacket_MemoryRegionInfo(const char *p);
+ rnb_err_t HandlePacket_GetProfileData(const char *p);
+ rnb_err_t HandlePacket_SetEnableAsyncProfiling(const char *p);
+ rnb_err_t HandlePacket_QEnableCompression(const char *p);
+ rnb_err_t HandlePacket_WatchpointSupportInfo(const char *p);
+ rnb_err_t HandlePacket_qSpeedTest(const char *p);
+ rnb_err_t HandlePacket_qXfer(const char *p);
+ rnb_err_t HandlePacket_stop_process(const char *p);
+ rnb_err_t HandlePacket_QSetDetachOnError(const char *p);
+ rnb_err_t HandlePacket_qStructuredDataPlugins(const char *p);
+ rnb_err_t HandlePacket_QConfigureDarwinLog(const char *p);
+
+ rnb_err_t SendStopReplyPacketForThread(nub_thread_t tid);
+ rnb_err_t SendHexEncodedBytePacket(const char *header, const void *buf,
+ size_t buf_len, const char *footer);
+ rnb_err_t SendSTDOUTPacket(char *buf, nub_size_t buf_size);
+ rnb_err_t SendSTDERRPacket(char *buf, nub_size_t buf_size);
+ void FlushSTDIO();
+ void SendAsyncProfileData();
+ rnb_err_t SendAsyncProfileDataPacket(char *buf, nub_size_t buf_size);
+ void SendAsyncDarwinLogData();
+ rnb_err_t SendAsyncJSONPacket(const JSONGenerator::Dictionary &dictionary);
+
+ RNBContext &Context() { return m_ctx; }
+ RNBSocket &Comm() { return m_comm; }
+
+private:
+ RNBRemote(const RNBRemote &) = delete;
+
+protected:
+ rnb_err_t GetCommData();
+ void CommDataReceived(const std::string &data);
+ struct Packet {
+ typedef std::vector<Packet> collection;
+ typedef collection::iterator iterator;
+ typedef collection::const_iterator const_iterator;
+ PacketEnum type;
+ HandlePacketCallback normal; // Function to call when inferior is halted
+ HandlePacketCallback async; // Function to call when inferior is running
+ std::string abbrev;
+ std::string printable_name;
+
+ bool IsPlatformPacket() const {
+ switch (type) {
+ case set_logging_mode:
+ case query_host_info:
+ return true;
+ default:
+ break;
+ }
+ return false;
+ }
+ Packet()
+ : type(invalid_packet), normal(NULL), async(NULL), abbrev(),
+ printable_name() {}
+
+ Packet(PacketEnum in_type, HandlePacketCallback in_normal,
+ HandlePacketCallback in_async, const char *in_abbrev,
+ const char *in_printable_name)
+ : type(in_type), normal(in_normal), async(in_async), abbrev(in_abbrev),
+ printable_name(in_printable_name) {}
+ };
+
+ struct DispatchQueueOffsets {
+ 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
+
+ DispatchQueueOffsets() { Clear(); }
+
+ void Clear() {
+ dqo_version = UINT16_MAX;
+ dqo_label = UINT16_MAX;
+ dqo_label_size = UINT16_MAX;
+ dqo_flags = UINT16_MAX;
+ dqo_flags_size = UINT16_MAX;
+ dqo_serialnum = UINT16_MAX;
+ dqo_serialnum_size = UINT16_MAX;
+ dqo_width = UINT16_MAX;
+ dqo_width_size = UINT16_MAX;
+ dqo_running = UINT16_MAX;
+ dqo_running_size = UINT16_MAX;
+ dqo_suspend_cnt = UINT16_MAX;
+ dqo_suspend_cnt_size = UINT16_MAX;
+ dqo_target_queue = UINT16_MAX;
+ dqo_target_queue_size = UINT16_MAX;
+ dqo_priority = UINT16_MAX;
+ dqo_priority_size = UINT16_MAX;
+ }
+
+ bool IsValid() const { return dqo_version != UINT16_MAX; }
+
+ void GetThreadQueueInfo(nub_process_t pid, nub_addr_t dispatch_qaddr,
+ nub_addr_t &dispatch_queue_t,
+ std::string &queue_name, uint64_t &queue_width,
+ uint64_t &queue_serialnum) const;
+ };
+
+ rnb_err_t GetPacket(std::string &packet_data, RNBRemote::Packet &packet_info,
+ bool wait);
+ rnb_err_t SendPacket(const std::string &);
+ std::string CompressString(const std::string &);
+
+ void CreatePacketTable();
+ rnb_err_t GetPacketPayload(std::string &);
+
+ nub_thread_t ExtractThreadIDFromThreadSuffix(const char *p);
+
+ void EnableCompressionNextSendPacket(compression_types);
+
+ compression_types GetCompressionType();
+
+ const DispatchQueueOffsets *GetDispatchQueueOffsets();
+
+ JSONGenerator::ObjectSP
+ GetJSONThreadsInfo(bool threads_with_valid_stop_info_only);
+
+ RNBContext m_ctx; // process context
+ RNBSocket m_comm; // communication port
+ std::string m_arch;
+ nub_thread_t m_continue_thread; // thread to continue; 0 for any, -1 for all
+ nub_thread_t m_thread; // thread for other ops; 0 for any, -1 for all
+ PThreadMutex m_mutex; // Mutex that protects
+ DispatchQueueOffsets m_dispatch_queue_offsets;
+ nub_addr_t m_dispatch_queue_offsets_addr;
+ uint32_t m_qSymbol_index;
+ uint32_t m_packets_recvd;
+ Packet::collection m_packets;
+ std::deque<std::string> m_rx_packets;
+ std::string m_rx_partial_data; // For packets that may come in more than one
+ // batch, anything left over can be left here
+ pthread_t m_rx_pthread;
+ uint32_t
+ m_max_payload_size; // the maximum sized payload we should send to gdb
+ bool m_extended_mode; // are we in extended mode?
+ bool m_noack_mode; // are we in no-ack mode?
+ bool m_thread_suffix_supported; // Set to true if the 'p', 'P', 'g', and 'G'
+ // packets should be prefixed with the thread
+ // ID and colon:
+ // "$pRR;thread:TTTT;" instead of "$pRR"
+ // "$PRR=VVVVVVVV;thread:TTTT;" instead of "$PRR=VVVVVVVV"
+ // "$g;thread:TTTT" instead of "$g"
+ // "$GVVVVVVVVVVVVVV;thread:TTTT;#00 instead of "$GVVVVVVVVVVVVVV"
+ bool m_list_threads_in_stop_reply;
+
+ size_t m_compression_minsize; // only packets larger than this size will be
+ // compressed
+ bool m_enable_compression_next_send_packet;
+
+ compression_types m_compression_mode;
+};
+
+/* We translate the /usr/include/mach/exception_types.h exception types
+ (e.g. EXC_BAD_ACCESS) to the fake BSD signal numbers that gdb uses
+ in include/gdb/signals.h (e.g. TARGET_EXC_BAD_ACCESS). These hard
+ coded values for TARGET_EXC_BAD_ACCESS et al must match the gdb
+ values in its include/gdb/signals.h. */
+
+#define TARGET_EXC_BAD_ACCESS 0x91
+#define TARGET_EXC_BAD_INSTRUCTION 0x92
+#define TARGET_EXC_ARITHMETIC 0x93
+#define TARGET_EXC_EMULATION 0x94
+#define TARGET_EXC_SOFTWARE 0x95
+#define TARGET_EXC_BREAKPOINT 0x96
+
+/* Generally speaking, you can't assume gdb can receive more than 399 bytes
+ at a time with a random gdb. This bufsize constant is only specifying
+ how many bytes gdb can *receive* from debugserver -- it tells us nothing
+ about how many bytes gdb might try to send in a single packet. */
+#define DEFAULT_GDB_REMOTE_PROTOCOL_BUFSIZE 399
+
+#endif // #ifndef __RNBRemote_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/RNBServices.cpp b/gnu/llvm/lldb/tools/debugserver/source/RNBServices.cpp
new file mode 100644
index 00000000000..085aaddfaf1
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/RNBServices.cpp
@@ -0,0 +1,234 @@
+//===-- RNBServices.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Christopher Friesen on 3/21/08.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RNBServices.h"
+
+#include "CFString.h"
+#include "DNBLog.h"
+#include "MacOSX/CFUtils.h"
+#include <CoreFoundation/CoreFoundation.h>
+#include <libproc.h>
+#include <sys/sysctl.h>
+#include <unistd.h>
+#include <vector>
+
+// For now only SpringBoard has a notion of "Applications" that it can list for
+// us.
+// So we have to use the SpringBoard API's here.
+#if defined(WITH_SPRINGBOARD) || defined(WITH_BKS)
+#include <SpringBoardServices/SpringBoardServices.h>
+#endif
+
+// From DNB.cpp
+size_t GetAllInfos(std::vector<struct kinfo_proc> &proc_infos);
+
+int GetProcesses(CFMutableArrayRef plistMutableArray, bool all_users) {
+ if (plistMutableArray == NULL)
+ return -1;
+
+ // Running as root, get all processes
+ std::vector<struct kinfo_proc> proc_infos;
+ const size_t num_proc_infos = GetAllInfos(proc_infos);
+ if (num_proc_infos > 0) {
+ const pid_t our_pid = getpid();
+ const uid_t our_uid = getuid();
+ uint32_t i;
+ CFAllocatorRef alloc = kCFAllocatorDefault;
+
+ for (i = 0; i < num_proc_infos; i++) {
+ struct kinfo_proc &proc_info = proc_infos[i];
+
+ bool kinfo_user_matches;
+ // Special case, if lldb is being run as root we can attach to anything.
+ if (all_users)
+ kinfo_user_matches = true;
+ else
+ kinfo_user_matches = proc_info.kp_eproc.e_pcred.p_ruid == our_uid;
+
+ const pid_t pid = proc_info.kp_proc.p_pid;
+ // Skip zombie processes and processes with unset status
+ if (!kinfo_user_matches || // User is acceptable
+ pid == our_pid || // Skip this process
+ pid == 0 || // Skip kernel (kernel pid is zero)
+ proc_info.kp_proc.p_stat ==
+ SZOMB || // Zombies are bad, they like brains...
+ proc_info.kp_proc.p_flag & P_TRACED || // Being debugged?
+ proc_info.kp_proc.p_flag & P_WEXIT || // Working on exiting?
+ proc_info.kp_proc.p_flag &
+ P_TRANSLATED) // Skip translated ppc (Rosetta)
+ continue;
+
+ // Create a new mutable dictionary for each application
+ CFReleaser<CFMutableDictionaryRef> appInfoDict(
+ ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+
+ // Get the process id for the app (if there is one)
+ const int32_t pid_int32 = pid;
+ CFReleaser<CFNumberRef> pidCFNumber(
+ ::CFNumberCreate(alloc, kCFNumberSInt32Type, &pid_int32));
+ ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PID_KEY,
+ pidCFNumber.get());
+
+ // Set a boolean to indicate if this is the front most
+ ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY,
+ kCFBooleanFalse);
+
+ const char *pid_basename = proc_info.kp_proc.p_comm;
+ char proc_path_buf[PATH_MAX];
+
+ int return_val = proc_pidpath(pid, proc_path_buf, PATH_MAX);
+ if (return_val > 0) {
+ // Okay, now search backwards from that to see if there is a
+ // slash in the name. Note, even though we got all the args we don't
+ // care
+ // because the list data is just a bunch of concatenated null terminated
+ // strings
+ // so strrchr will start from the end of argv0.
+
+ pid_basename = strrchr(proc_path_buf, '/');
+ if (pid_basename) {
+ // Skip the '/'
+ ++pid_basename;
+ } else {
+ // We didn't find a directory delimiter in the process argv[0], just
+ // use what was in there
+ pid_basename = proc_path_buf;
+ }
+ CFString cf_pid_path(proc_path_buf);
+ if (cf_pid_path.get())
+ ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PATH_KEY,
+ cf_pid_path.get());
+ }
+
+ if (pid_basename && pid_basename[0]) {
+ CFString pid_name(pid_basename);
+ ::CFDictionarySetValue(appInfoDict.get(),
+ DTSERVICES_APP_DISPLAY_NAME_KEY, pid_name.get());
+ }
+
+ // Append the application info to the plist array
+ ::CFArrayAppendValue(plistMutableArray, appInfoDict.get());
+ }
+ }
+ return 0;
+}
+int ListApplications(std::string &plist, bool opt_runningApps,
+ bool opt_debuggable) {
+ int result = -1;
+
+ CFAllocatorRef alloc = kCFAllocatorDefault;
+
+ // Create a mutable array that we can populate. Specify zero so it can be of
+ // any size.
+ CFReleaser<CFMutableArrayRef> plistMutableArray(
+ ::CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks));
+
+ const uid_t our_uid = getuid();
+
+#if defined(WITH_SPRINGBOARD) || defined(WITH_BKS)
+
+ if (our_uid == 0) {
+ bool all_users = true;
+ result = GetProcesses(plistMutableArray.get(), all_users);
+ } else {
+ CFReleaser<CFStringRef> sbsFrontAppID(
+ ::SBSCopyFrontmostApplicationDisplayIdentifier());
+ CFReleaser<CFArrayRef> sbsAppIDs(::SBSCopyApplicationDisplayIdentifiers(
+ opt_runningApps, opt_debuggable));
+
+ // Need to check the return value from SBSCopyApplicationDisplayIdentifiers.
+ CFIndex count = sbsAppIDs.get() ? ::CFArrayGetCount(sbsAppIDs.get()) : 0;
+ CFIndex i = 0;
+ for (i = 0; i < count; i++) {
+ CFStringRef displayIdentifier =
+ (CFStringRef)::CFArrayGetValueAtIndex(sbsAppIDs.get(), i);
+
+ // Create a new mutable dictionary for each application
+ CFReleaser<CFMutableDictionaryRef> appInfoDict(
+ ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+
+ // Get the process id for the app (if there is one)
+ pid_t pid = INVALID_NUB_PROCESS;
+ if (::SBSProcessIDForDisplayIdentifier((CFStringRef)displayIdentifier,
+ &pid) == true) {
+ CFReleaser<CFNumberRef> pidCFNumber(
+ ::CFNumberCreate(alloc, kCFNumberSInt32Type, &pid));
+ ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PID_KEY,
+ pidCFNumber.get());
+ }
+
+ // Set a boolean to indicate if this is the front most
+ if (sbsFrontAppID.get() && displayIdentifier &&
+ (::CFStringCompare(sbsFrontAppID.get(), displayIdentifier, 0) ==
+ kCFCompareEqualTo))
+ ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY,
+ kCFBooleanTrue);
+ else
+ ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY,
+ kCFBooleanFalse);
+
+ CFReleaser<CFStringRef> executablePath(
+ ::SBSCopyExecutablePathForDisplayIdentifier(displayIdentifier));
+ if (executablePath.get() != NULL) {
+ ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PATH_KEY,
+ executablePath.get());
+ }
+
+ CFReleaser<CFStringRef> iconImagePath(
+ ::SBSCopyIconImagePathForDisplayIdentifier(displayIdentifier));
+ if (iconImagePath.get() != NULL) {
+ ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_ICON_PATH_KEY,
+ iconImagePath.get());
+ }
+
+ CFReleaser<CFStringRef> localizedDisplayName(
+ ::SBSCopyLocalizedApplicationNameForDisplayIdentifier(
+ displayIdentifier));
+ if (localizedDisplayName.get() != NULL) {
+ ::CFDictionarySetValue(appInfoDict.get(),
+ DTSERVICES_APP_DISPLAY_NAME_KEY,
+ localizedDisplayName.get());
+ }
+
+ // Append the application info to the plist array
+ ::CFArrayAppendValue(plistMutableArray.get(), appInfoDict.get());
+ }
+ }
+#else // #if defined (WITH_SPRINGBOARD) || defined (WITH_BKS)
+ // When root, show all processes
+ bool all_users = (our_uid == 0);
+ GetProcesses(plistMutableArray.get(), all_users);
+#endif
+
+ CFReleaser<CFDataRef> plistData(
+ ::CFPropertyListCreateXMLData(alloc, plistMutableArray.get()));
+
+ // write plist to service port
+ if (plistData.get() != NULL) {
+ CFIndex size = ::CFDataGetLength(plistData.get());
+ const UInt8 *bytes = ::CFDataGetBytePtr(plistData.get());
+ if (bytes != NULL && size > 0) {
+ plist.assign((const char *)bytes, size);
+ return 0; // Success
+ } else {
+ DNBLogError("empty application property list.");
+ result = -2;
+ }
+ } else {
+ DNBLogError("serializing task list.");
+ result = -3;
+ }
+
+ return result;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/RNBServices.h b/gnu/llvm/lldb/tools/debugserver/source/RNBServices.h
new file mode 100644
index 00000000000..b7514f31b21
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/RNBServices.h
@@ -0,0 +1,28 @@
+//===-- RNBServices.h -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Christopher Friesen on 3/21/08.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __RNBServices_h__
+#define __RNBServices_h__
+
+#include "RNBDefs.h"
+#include <string>
+
+#define DTSERVICES_APP_FRONTMOST_KEY CFSTR("isFrontApp")
+#define DTSERVICES_APP_PATH_KEY CFSTR("executablePath")
+#define DTSERVICES_APP_ICON_PATH_KEY CFSTR("iconPath")
+#define DTSERVICES_APP_DISPLAY_NAME_KEY CFSTR("displayName")
+#define DTSERVICES_APP_PID_KEY CFSTR("pid")
+
+int ListApplications(std::string &plist, bool opt_runningApps,
+ bool opt_debuggable);
+
+#endif // __RNBServices_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/RNBSocket.cpp b/gnu/llvm/lldb/tools/debugserver/source/RNBSocket.cpp
new file mode 100644
index 00000000000..80b55b5de3b
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/RNBSocket.cpp
@@ -0,0 +1,391 @@
+//===-- RNBSocket.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 12/12/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RNBSocket.h"
+#include "DNBError.h"
+#include "DNBLog.h"
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <map>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/event.h>
+#include <termios.h>
+#include <vector>
+
+#include "lldb/Host/SocketAddress.h"
+
+#ifdef WITH_LOCKDOWN
+#include "lockdown.h"
+#endif
+
+/* Once we have a RNBSocket object with a port # specified,
+ this function is called to wait for an incoming connection.
+ This function blocks while waiting for that connection. */
+
+bool ResolveIPV4HostName(const char *hostname, in_addr_t &addr) {
+ if (hostname == NULL || hostname[0] == '\0' ||
+ strcmp(hostname, "localhost") == 0 ||
+ strcmp(hostname, "127.0.0.1") == 0) {
+ addr = htonl(INADDR_LOOPBACK);
+ return true;
+ } else if (strcmp(hostname, "*") == 0) {
+ addr = htonl(INADDR_ANY);
+ return true;
+ } else {
+ // See if an IP address was specified as numbers
+ int inet_pton_result = ::inet_pton(AF_INET, hostname, &addr);
+
+ if (inet_pton_result == 1)
+ return true;
+
+ struct hostent *host_entry = gethostbyname(hostname);
+ if (host_entry) {
+ std::string ip_str(
+ ::inet_ntoa(*(struct in_addr *)*host_entry->h_addr_list));
+ inet_pton_result = ::inet_pton(AF_INET, ip_str.c_str(), &addr);
+ if (inet_pton_result == 1)
+ return true;
+ }
+ }
+ return false;
+}
+
+rnb_err_t RNBSocket::Listen(const char *listen_host, uint16_t port,
+ PortBoundCallback callback,
+ const void *callback_baton) {
+ // DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s called",
+ // (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__);
+ // Disconnect without saving errno
+ Disconnect(false);
+
+ DNBError err;
+ int queue_id = kqueue();
+ if (queue_id < 0) {
+ err.SetError(errno, DNBError::MachKernel);
+ err.LogThreaded("error: failed to create kqueue.");
+ return rnb_err;
+ }
+
+ bool any_addr = (strcmp(listen_host, "*") == 0);
+
+ // If the user wants to allow connections from any address we should create
+ // sockets on all families that can resolve localhost. This will allow us to
+ // listen for IPv6 and IPv4 connections from all addresses if those interfaces
+ // are available.
+ const char *local_addr = any_addr ? "localhost" : listen_host;
+
+ std::map<int, lldb_private::SocketAddress> sockets;
+ auto addresses = lldb_private::SocketAddress::GetAddressInfo(
+ local_addr, NULL, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP);
+
+ for (auto address : addresses) {
+ int sock_fd = ::socket(address.GetFamily(), SOCK_STREAM, IPPROTO_TCP);
+ if (sock_fd == -1)
+ continue;
+
+ SetSocketOption(sock_fd, SOL_SOCKET, SO_REUSEADDR, 1);
+
+ lldb_private::SocketAddress bind_address = address;
+
+ if(any_addr || !bind_address.IsLocalhost())
+ bind_address.SetToAnyAddress(bind_address.GetFamily(), port);
+ else
+ bind_address.SetPort(port);
+
+ int error =
+ ::bind(sock_fd, &bind_address.sockaddr(), bind_address.GetLength());
+ if (error == -1) {
+ ClosePort(sock_fd, false);
+ continue;
+ }
+
+ error = ::listen(sock_fd, 5);
+ if (error == -1) {
+ ClosePort(sock_fd, false);
+ continue;
+ }
+
+ // We were asked to listen on port zero which means we must now read the
+ // actual port that was given to us as port zero is a special code for "find
+ // an open port for me". This will only execute on the first socket created,
+ // subesquent sockets will reuse this port number.
+ if (port == 0) {
+ socklen_t sa_len = address.GetLength();
+ if (getsockname(sock_fd, &address.sockaddr(), &sa_len) == 0)
+ port = address.GetPort();
+ }
+
+ sockets[sock_fd] = address;
+ }
+
+ if (sockets.size() == 0) {
+ err.SetError(errno, DNBError::POSIX);
+ err.LogThreaded("::listen or ::bind failed");
+ return rnb_err;
+ }
+
+ if (callback)
+ callback(callback_baton, port);
+
+ std::vector<struct kevent> events;
+ events.resize(sockets.size());
+ int i = 0;
+ for (auto socket : sockets) {
+ EV_SET(&events[i++], socket.first, EVFILT_READ, EV_ADD, 0, 0, 0);
+ }
+
+ bool accept_connection = false;
+
+ // Loop until we are happy with our connection
+ while (!accept_connection) {
+
+ struct kevent event_list[4];
+ int num_events =
+ kevent(queue_id, events.data(), events.size(), event_list, 4, NULL);
+
+ if (num_events < 0) {
+ err.SetError(errno, DNBError::MachKernel);
+ err.LogThreaded("error: kevent() failed.");
+ }
+
+ for (int i = 0; i < num_events; ++i) {
+ auto sock_fd = event_list[i].ident;
+ auto socket_pair = sockets.find(sock_fd);
+ if (socket_pair == sockets.end())
+ continue;
+
+ lldb_private::SocketAddress &addr_in = socket_pair->second;
+ lldb_private::SocketAddress accept_addr;
+ socklen_t sa_len = accept_addr.GetMaxLength();
+ m_fd = ::accept(sock_fd, &accept_addr.sockaddr(), &sa_len);
+
+ if (m_fd == -1) {
+ err.SetError(errno, DNBError::POSIX);
+ err.LogThreaded("error: Socket accept failed.");
+ }
+
+ if (addr_in.IsAnyAddr())
+ accept_connection = true;
+ else {
+ if (accept_addr == addr_in)
+ accept_connection = true;
+ else {
+ ::close(m_fd);
+ m_fd = -1;
+ ::fprintf(
+ stderr,
+ "error: rejecting incoming connection from %s (expecting %s)\n",
+ accept_addr.GetIPAddress().c_str(),
+ addr_in.GetIPAddress().c_str());
+ DNBLogThreaded("error: rejecting connection from %s (expecting %s)\n",
+ accept_addr.GetIPAddress().c_str(),
+ addr_in.GetIPAddress().c_str());
+ err.Clear();
+ }
+ }
+ }
+ if (err.Fail())
+ break;
+ }
+ for (auto socket : sockets) {
+ int ListenFd = socket.first;
+ ClosePort(ListenFd, false);
+ }
+
+ if (err.Fail())
+ return rnb_err;
+
+ // Keep our TCP packets coming without any delays.
+ SetSocketOption(m_fd, IPPROTO_TCP, TCP_NODELAY, 1);
+
+ return rnb_success;
+}
+
+rnb_err_t RNBSocket::Connect(const char *host, uint16_t port) {
+ auto result = rnb_err;
+ Disconnect(false);
+
+ auto addresses = lldb_private::SocketAddress::GetAddressInfo(
+ host, NULL, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP);
+
+ for (auto address : addresses) {
+ m_fd = ::socket(address.GetFamily(), SOCK_STREAM, IPPROTO_TCP);
+ if (m_fd == -1)
+ continue;
+
+ // Enable local address reuse
+ SetSocketOption(m_fd, SOL_SOCKET, SO_REUSEADDR, 1);
+
+ address.SetPort(port);
+
+ if (-1 == ::connect(m_fd, &address.sockaddr(), address.GetLength())) {
+ Disconnect(false);
+ continue;
+ }
+ SetSocketOption(m_fd, IPPROTO_TCP, TCP_NODELAY, 1);
+
+ result = rnb_success;
+ break;
+ }
+ return result;
+}
+
+rnb_err_t RNBSocket::useFD(int fd) {
+ if (fd < 0) {
+ DNBLogThreadedIf(LOG_RNB_COMM, "Bad file descriptor passed in.");
+ return rnb_err;
+ }
+
+ m_fd = fd;
+ return rnb_success;
+}
+
+#ifdef WITH_LOCKDOWN
+rnb_err_t RNBSocket::ConnectToService() {
+ DNBLog("Connecting to com.apple.%s service...", DEBUGSERVER_PROGRAM_NAME);
+ // Disconnect from any previous connections
+ Disconnect(false);
+ if (::secure_lockdown_checkin(&m_ld_conn, NULL, NULL) != kLDESuccess) {
+ DNBLogThreadedIf(LOG_RNB_COMM,
+ "::secure_lockdown_checkin(&m_fd, NULL, NULL) failed");
+ m_fd = -1;
+ return rnb_not_connected;
+ }
+ m_fd = ::lockdown_get_socket(m_ld_conn);
+ if (m_fd == -1) {
+ DNBLogThreadedIf(LOG_RNB_COMM, "::lockdown_get_socket() failed");
+ return rnb_not_connected;
+ }
+ m_fd_from_lockdown = true;
+ return rnb_success;
+}
+#endif
+
+rnb_err_t RNBSocket::OpenFile(const char *path) {
+ DNBError err;
+ m_fd = open(path, O_RDWR);
+ if (m_fd == -1) {
+ err.SetError(errno, DNBError::POSIX);
+ err.LogThreaded("can't open file '%s'", path);
+ return rnb_not_connected;
+ } else {
+ struct termios stdin_termios;
+
+ if (::tcgetattr(m_fd, &stdin_termios) == 0) {
+ stdin_termios.c_lflag &= ~ECHO; // Turn off echoing
+ stdin_termios.c_lflag &= ~ICANON; // Get one char at a time
+ ::tcsetattr(m_fd, TCSANOW, &stdin_termios);
+ }
+ }
+ return rnb_success;
+}
+
+int RNBSocket::SetSocketOption(int fd, int level, int option_name,
+ int option_value) {
+ return ::setsockopt(fd, level, option_name, &option_value,
+ sizeof(option_value));
+}
+
+rnb_err_t RNBSocket::Disconnect(bool save_errno) {
+#ifdef WITH_LOCKDOWN
+ if (m_fd_from_lockdown) {
+ m_fd_from_lockdown = false;
+ m_fd = -1;
+ lockdown_disconnect(m_ld_conn);
+ return rnb_success;
+ }
+#endif
+ return ClosePort(m_fd, save_errno);
+}
+
+rnb_err_t RNBSocket::Read(std::string &p) {
+ char buf[1024];
+ p.clear();
+
+ // Note that BUF is on the stack so we must be careful to keep any
+ // writes to BUF from overflowing or we'll have security issues.
+
+ if (m_fd == -1)
+ return rnb_err;
+
+ // DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s calling read()",
+ // (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__);
+ DNBError err;
+ ssize_t bytesread = read(m_fd, buf, sizeof(buf));
+ if (bytesread <= 0)
+ err.SetError(errno, DNBError::POSIX);
+ else
+ p.append(buf, bytesread);
+
+ if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
+ err.LogThreaded("::read ( %i, %p, %llu ) => %i", m_fd, buf, sizeof(buf),
+ (uint64_t)bytesread);
+
+ // Our port went away - we have to mark this so IsConnected will return the
+ // truth.
+ if (bytesread == 0) {
+ m_fd = -1;
+ return rnb_not_connected;
+ } else if (bytesread == -1) {
+ m_fd = -1;
+ return rnb_err;
+ }
+ // Strip spaces from the end of the buffer
+ while (!p.empty() && isspace(p[p.size() - 1]))
+ p.erase(p.size() - 1);
+
+ // Most data in the debugserver packets valid printable characters...
+ DNBLogThreadedIf(LOG_RNB_COMM, "read: %s", p.c_str());
+ return rnb_success;
+}
+
+rnb_err_t RNBSocket::Write(const void *buffer, size_t length) {
+ if (m_fd == -1)
+ return rnb_err;
+
+ DNBError err;
+ ssize_t bytessent = write(m_fd, buffer, length);
+ if (bytessent < 0)
+ err.SetError(errno, DNBError::POSIX);
+
+ if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
+ err.LogThreaded("::write ( socket = %i, buffer = %p, length = %llu) => %i",
+ m_fd, buffer, length, (uint64_t)bytessent);
+
+ if (bytessent < 0)
+ return rnb_err;
+
+ if ((size_t)bytessent != length)
+ return rnb_err;
+
+ DNBLogThreadedIf(
+ LOG_RNB_PACKETS, "putpkt: %*s", (int)length,
+ (const char *)
+ buffer); // All data is string based in debugserver, so this is safe
+ DNBLogThreadedIf(LOG_RNB_COMM, "sent: %*s", (int)length,
+ (const char *)buffer);
+
+ return rnb_success;
+}
+
+rnb_err_t RNBSocket::ClosePort(int &fd, bool save_errno) {
+ int close_err = 0;
+ if (fd > 0) {
+ errno = 0;
+ close_err = close(fd);
+ fd = -1;
+ }
+ return close_err != 0 ? rnb_err : rnb_success;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/RNBSocket.h b/gnu/llvm/lldb/tools/debugserver/source/RNBSocket.h
new file mode 100644
index 00000000000..2f68eb00f37
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/RNBSocket.h
@@ -0,0 +1,77 @@
+//===-- RNBSocket.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 12/12/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __RNBSocket_h__
+#define __RNBSocket_h__
+
+#include "DNBTimer.h"
+#include "RNBDefs.h"
+#include <string>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#ifdef WITH_LOCKDOWN
+#include "lockdown.h"
+#endif
+
+class RNBSocket {
+public:
+ typedef void (*PortBoundCallback)(const void *baton, uint16_t port);
+
+ RNBSocket()
+ : m_fd(-1),
+#ifdef WITH_LOCKDOWN
+ m_fd_from_lockdown(false), m_ld_conn(),
+#endif
+ m_timer(true) // Make a thread safe timer
+ {
+ }
+ ~RNBSocket(void) { Disconnect(false); }
+
+ rnb_err_t Listen(const char *listen_host, uint16_t port,
+ PortBoundCallback callback, const void *callback_baton);
+ rnb_err_t Connect(const char *host, uint16_t port);
+
+ rnb_err_t useFD(int fd);
+
+#ifdef WITH_LOCKDOWN
+ rnb_err_t ConnectToService();
+#endif
+ rnb_err_t OpenFile(const char *path);
+ rnb_err_t Disconnect(bool save_errno);
+ rnb_err_t Read(std::string &p);
+ rnb_err_t Write(const void *buffer, size_t length);
+
+ bool IsConnected() const { return m_fd != -1; }
+ void SaveErrno(int curr_errno);
+ DNBTimer &Timer() { return m_timer; }
+
+ static int SetSocketOption(int fd, int level, int option_name,
+ int option_value);
+
+private:
+ RNBSocket(const RNBSocket &) = delete;
+
+protected:
+ rnb_err_t ClosePort(int &fd, bool save_errno);
+
+ int m_fd; // Socket we use to communicate once conn established
+
+#ifdef WITH_LOCKDOWN
+ bool m_fd_from_lockdown;
+ lockdown_connection m_ld_conn;
+#endif
+
+ DNBTimer m_timer;
+};
+
+#endif // #ifndef __RNBSocket_h__
diff --git a/gnu/llvm/lldb/tools/debugserver/source/StdStringExtractor.cpp b/gnu/llvm/lldb/tools/debugserver/source/StdStringExtractor.cpp
new file mode 100644
index 00000000000..a4cad2d4bbf
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/StdStringExtractor.cpp
@@ -0,0 +1,344 @@
+//===-- StdStringExtractor.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "StdStringExtractor.h"
+
+#include <stdlib.h>
+
+
+static inline int xdigit_to_sint(char ch) {
+ if (ch >= 'a' && ch <= 'f')
+ return 10 + ch - 'a';
+ if (ch >= 'A' && ch <= 'F')
+ return 10 + ch - 'A';
+ if (ch >= '0' && ch <= '9')
+ return ch - '0';
+ return -1;
+}
+
+// StdStringExtractor constructor
+StdStringExtractor::StdStringExtractor() : m_packet(), m_index(0) {}
+
+StdStringExtractor::StdStringExtractor(const char *packet_cstr)
+ : m_packet(), m_index(0) {
+ if (packet_cstr)
+ m_packet.assign(packet_cstr);
+}
+
+// Destructor
+StdStringExtractor::~StdStringExtractor() {}
+
+char StdStringExtractor::GetChar(char fail_value) {
+ if (m_index < m_packet.size()) {
+ char ch = m_packet[m_index];
+ ++m_index;
+ return ch;
+ }
+ m_index = UINT64_MAX;
+ return fail_value;
+}
+
+// If a pair of valid hex digits exist at the head of the
+// StdStringExtractor they are decoded into an unsigned byte and returned
+// by this function
+//
+// If there is not a pair of valid hex digits at the head of the
+// StdStringExtractor, it is left unchanged and -1 is returned
+int StdStringExtractor::DecodeHexU8() {
+ SkipSpaces();
+ if (GetBytesLeft() < 2) {
+ return -1;
+ }
+ const int hi_nibble = xdigit_to_sint(m_packet[m_index]);
+ const int lo_nibble = xdigit_to_sint(m_packet[m_index + 1]);
+ if (hi_nibble == -1 || lo_nibble == -1) {
+ return -1;
+ }
+ m_index += 2;
+ return (uint8_t)((hi_nibble << 4) + lo_nibble);
+}
+
+// Extract an unsigned character from two hex ASCII chars in the packet
+// string, or return fail_value on failure
+uint8_t StdStringExtractor::GetHexU8(uint8_t fail_value, bool set_eof_on_fail) {
+ // On success, fail_value will be overwritten with the next
+ // character in the stream
+ GetHexU8Ex(fail_value, set_eof_on_fail);
+ return fail_value;
+}
+
+bool StdStringExtractor::GetHexU8Ex(uint8_t &ch, bool set_eof_on_fail) {
+ int byte = DecodeHexU8();
+ if (byte == -1) {
+ if (set_eof_on_fail || m_index >= m_packet.size())
+ m_index = UINT64_MAX;
+ // ch should not be changed in case of failure
+ return false;
+ }
+ ch = (uint8_t)byte;
+ return true;
+}
+
+uint32_t StdStringExtractor::GetU32(uint32_t fail_value, int base) {
+ if (m_index < m_packet.size()) {
+ char *end = nullptr;
+ const char *start = m_packet.c_str();
+ const char *cstr = start + m_index;
+ uint32_t result = static_cast<uint32_t>(::strtoul(cstr, &end, base));
+
+ if (end && end != cstr) {
+ m_index = end - start;
+ return result;
+ }
+ }
+ return fail_value;
+}
+
+int32_t StdStringExtractor::GetS32(int32_t fail_value, int base) {
+ if (m_index < m_packet.size()) {
+ char *end = nullptr;
+ const char *start = m_packet.c_str();
+ const char *cstr = start + m_index;
+ int32_t result = static_cast<int32_t>(::strtol(cstr, &end, base));
+
+ if (end && end != cstr) {
+ m_index = end - start;
+ return result;
+ }
+ }
+ return fail_value;
+}
+
+uint64_t StdStringExtractor::GetU64(uint64_t fail_value, int base) {
+ if (m_index < m_packet.size()) {
+ char *end = nullptr;
+ const char *start = m_packet.c_str();
+ const char *cstr = start + m_index;
+ uint64_t result = ::strtoull(cstr, &end, base);
+
+ if (end && end != cstr) {
+ m_index = end - start;
+ return result;
+ }
+ }
+ return fail_value;
+}
+
+int64_t StdStringExtractor::GetS64(int64_t fail_value, int base) {
+ if (m_index < m_packet.size()) {
+ char *end = nullptr;
+ const char *start = m_packet.c_str();
+ const char *cstr = start + m_index;
+ int64_t result = ::strtoll(cstr, &end, base);
+
+ if (end && end != cstr) {
+ m_index = end - start;
+ return result;
+ }
+ }
+ return fail_value;
+}
+
+uint32_t StdStringExtractor::GetHexMaxU32(bool little_endian,
+ uint32_t fail_value) {
+ uint32_t result = 0;
+ uint32_t nibble_count = 0;
+
+ SkipSpaces();
+ if (little_endian) {
+ uint32_t shift_amount = 0;
+ while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) {
+ // Make sure we don't exceed the size of a uint32_t...
+ if (nibble_count >= (sizeof(uint32_t) * 2)) {
+ m_index = UINT64_MAX;
+ return fail_value;
+ }
+
+ uint8_t nibble_lo;
+ uint8_t nibble_hi = xdigit_to_sint(m_packet[m_index]);
+ ++m_index;
+ if (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) {
+ nibble_lo = xdigit_to_sint(m_packet[m_index]);
+ ++m_index;
+ result |= ((uint32_t)nibble_hi << (shift_amount + 4));
+ result |= ((uint32_t)nibble_lo << shift_amount);
+ nibble_count += 2;
+ shift_amount += 8;
+ } else {
+ result |= ((uint32_t)nibble_hi << shift_amount);
+ nibble_count += 1;
+ shift_amount += 4;
+ }
+ }
+ } else {
+ while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) {
+ // Make sure we don't exceed the size of a uint32_t...
+ if (nibble_count >= (sizeof(uint32_t) * 2)) {
+ m_index = UINT64_MAX;
+ return fail_value;
+ }
+
+ uint8_t nibble = xdigit_to_sint(m_packet[m_index]);
+ // Big Endian
+ result <<= 4;
+ result |= nibble;
+
+ ++m_index;
+ ++nibble_count;
+ }
+ }
+ return result;
+}
+
+uint64_t StdStringExtractor::GetHexMaxU64(bool little_endian,
+ uint64_t fail_value) {
+ uint64_t result = 0;
+ uint32_t nibble_count = 0;
+
+ SkipSpaces();
+ if (little_endian) {
+ uint32_t shift_amount = 0;
+ while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) {
+ // Make sure we don't exceed the size of a uint64_t...
+ if (nibble_count >= (sizeof(uint64_t) * 2)) {
+ m_index = UINT64_MAX;
+ return fail_value;
+ }
+
+ uint8_t nibble_lo;
+ uint8_t nibble_hi = xdigit_to_sint(m_packet[m_index]);
+ ++m_index;
+ if (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) {
+ nibble_lo = xdigit_to_sint(m_packet[m_index]);
+ ++m_index;
+ result |= ((uint64_t)nibble_hi << (shift_amount + 4));
+ result |= ((uint64_t)nibble_lo << shift_amount);
+ nibble_count += 2;
+ shift_amount += 8;
+ } else {
+ result |= ((uint64_t)nibble_hi << shift_amount);
+ nibble_count += 1;
+ shift_amount += 4;
+ }
+ }
+ } else {
+ while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) {
+ // Make sure we don't exceed the size of a uint64_t...
+ if (nibble_count >= (sizeof(uint64_t) * 2)) {
+ m_index = UINT64_MAX;
+ return fail_value;
+ }
+
+ uint8_t nibble = xdigit_to_sint(m_packet[m_index]);
+ // Big Endian
+ result <<= 4;
+ result |= nibble;
+
+ ++m_index;
+ ++nibble_count;
+ }
+ }
+ return result;
+}
+
+size_t StdStringExtractor::GetHexBytes(void *dst_void, size_t dst_len,
+ uint8_t fail_fill_value) {
+ uint8_t *dst = (uint8_t *)dst_void;
+ size_t bytes_extracted = 0;
+ while (bytes_extracted < dst_len && GetBytesLeft()) {
+ dst[bytes_extracted] = GetHexU8(fail_fill_value);
+ if (IsGood())
+ ++bytes_extracted;
+ else
+ break;
+ }
+
+ for (size_t i = bytes_extracted; i < dst_len; ++i)
+ dst[i] = fail_fill_value;
+
+ return bytes_extracted;
+}
+
+// Decodes all valid hex encoded bytes at the head of the
+// StdStringExtractor, limited by dst_len.
+//
+// Returns the number of bytes successfully decoded
+size_t StdStringExtractor::GetHexBytesAvail(void *dst_void, size_t dst_len) {
+ uint8_t *dst = (uint8_t *)dst_void;
+ size_t bytes_extracted = 0;
+ while (bytes_extracted < dst_len) {
+ int decode = DecodeHexU8();
+ if (decode == -1) {
+ break;
+ }
+ dst[bytes_extracted++] = (uint8_t)decode;
+ }
+ return bytes_extracted;
+}
+
+size_t StdStringExtractor::GetHexByteString(std::string &str) {
+ str.clear();
+ str.reserve(GetBytesLeft() / 2);
+ char ch;
+ while ((ch = GetHexU8()) != '\0')
+ str.append(1, ch);
+ return str.size();
+}
+
+size_t StdStringExtractor::GetHexByteStringFixedLength(std::string &str,
+ uint32_t nibble_length) {
+ str.clear();
+
+ uint32_t nibble_count = 0;
+ for (const char *pch = Peek();
+ (nibble_count < nibble_length) && (pch != nullptr);
+ str.append(1, GetHexU8(0, false)), pch = Peek(), nibble_count += 2) {
+ }
+
+ return str.size();
+}
+
+size_t StdStringExtractor::GetHexByteStringTerminatedBy(std::string &str,
+ char terminator) {
+ str.clear();
+ char ch;
+ while ((ch = GetHexU8(0, false)) != '\0')
+ str.append(1, ch);
+ if (Peek() && *Peek() == terminator)
+ return str.size();
+
+ str.clear();
+ return str.size();
+}
+
+bool StdStringExtractor::GetNameColonValue(std::string &name,
+ std::string &value) {
+ // Read something in the form of NNNN:VVVV; where NNNN is any character
+ // that is not a colon, followed by a ':' character, then a value (one or
+ // more ';' chars), followed by a ';'
+ if (m_index < m_packet.size()) {
+ const size_t colon_idx = m_packet.find(':', m_index);
+ if (colon_idx != std::string::npos) {
+ const size_t semicolon_idx = m_packet.find(';', colon_idx);
+ if (semicolon_idx != std::string::npos) {
+ name.assign(m_packet, m_index, colon_idx - m_index);
+ value.assign(m_packet, colon_idx + 1, semicolon_idx - (colon_idx + 1));
+ m_index = semicolon_idx + 1;
+ return true;
+ }
+ }
+ }
+ m_index = UINT64_MAX;
+ return false;
+}
+
+void StdStringExtractor::SkipSpaces() {
+ const size_t n = m_packet.size();
+ while (m_index < n && isspace(m_packet[m_index]))
+ ++m_index;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/StdStringExtractor.h b/gnu/llvm/lldb/tools/debugserver/source/StdStringExtractor.h
new file mode 100644
index 00000000000..5165240bd20
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/StdStringExtractor.h
@@ -0,0 +1,105 @@
+//===-- StdStringExtractor.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef utility_StdStringExtractor_h_
+#define utility_StdStringExtractor_h_
+
+#include <stdint.h>
+#include <string>
+
+
+// Based on StringExtractor, with the added limitation that this file should not
+// take a dependency on LLVM, as it is used from debugserver.
+class StdStringExtractor {
+public:
+ enum { BigEndian = 0, LittleEndian = 1 };
+ // Constructors and Destructors
+ StdStringExtractor();
+ StdStringExtractor(const char *packet_cstr);
+ virtual ~StdStringExtractor();
+
+ // Returns true if the file position is still valid for the data
+ // contained in this string extractor object.
+ bool IsGood() const { return m_index != UINT64_MAX; }
+
+ uint64_t GetFilePos() const { return m_index; }
+
+ void SetFilePos(uint32_t idx) { m_index = idx; }
+
+ void Clear() {
+ m_packet.clear();
+ m_index = 0;
+ }
+
+ void SkipSpaces();
+
+ const std::string &GetStringRef() const { return m_packet; }
+
+ bool Empty() { return m_packet.empty(); }
+
+ size_t GetBytesLeft() {
+ if (m_index < m_packet.size())
+ return m_packet.size() - m_index;
+ return 0;
+ }
+
+ char GetChar(char fail_value = '\0');
+
+ char PeekChar(char fail_value = '\0') {
+ const char *cstr = Peek();
+ if (cstr)
+ return cstr[0];
+ return fail_value;
+ }
+
+ int DecodeHexU8();
+
+ uint8_t GetHexU8(uint8_t fail_value = 0, bool set_eof_on_fail = true);
+
+ bool GetHexU8Ex(uint8_t &ch, bool set_eof_on_fail = true);
+
+ bool GetNameColonValue(std::string &name, std::string &value);
+
+ int32_t GetS32(int32_t fail_value, int base = 0);
+
+ uint32_t GetU32(uint32_t fail_value, int base = 0);
+
+ int64_t GetS64(int64_t fail_value, int base = 0);
+
+ uint64_t GetU64(uint64_t fail_value, int base = 0);
+
+ uint32_t GetHexMaxU32(bool little_endian, uint32_t fail_value);
+
+ uint64_t GetHexMaxU64(bool little_endian, uint64_t fail_value);
+
+ size_t GetHexBytes(void *dst, size_t dst_len, uint8_t fail_fill_value);
+
+ size_t GetHexBytesAvail(void *dst, size_t dst_len);
+
+ size_t GetHexByteString(std::string &str);
+
+ size_t GetHexByteStringFixedLength(std::string &str, uint32_t nibble_length);
+
+ size_t GetHexByteStringTerminatedBy(std::string &str, char terminator);
+
+ const char *Peek() {
+ if (m_index < m_packet.size())
+ return m_packet.c_str() + m_index;
+ return nullptr;
+ }
+
+protected:
+ // For StdStringExtractor only
+ std::string m_packet; // The string in which to extract data.
+ uint64_t m_index; // When extracting data from a packet, this index
+ // will march along as things get extracted. If set
+ // to UINT64_MAX the end of the packet data was
+ // reached when decoding information
+};
+
+#endif // utility_StringExtractor_h_
diff --git a/gnu/llvm/lldb/tools/debugserver/source/SysSignal.cpp b/gnu/llvm/lldb/tools/debugserver/source/SysSignal.cpp
new file mode 100644
index 00000000000..9afbbec0987
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/SysSignal.cpp
@@ -0,0 +1,94 @@
+//===-- SysSignal.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/18/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SysSignal.h"
+#include <signal.h>
+#include <stddef.h>
+
+const char *SysSignal::Name(int signal) {
+ switch (signal) {
+ case SIGHUP:
+ return "SIGHUP"; // 1 hangup
+ case SIGINT:
+ return "SIGINT"; // 2 interrupt
+ case SIGQUIT:
+ return "SIGQUIT"; // 3 quit
+ case SIGILL:
+ return "SIGILL"; // 4 illegal instruction (not reset when caught)
+ case SIGTRAP:
+ return "SIGTRAP"; // 5 trace trap (not reset when caught)
+ case SIGABRT:
+ return "SIGABRT"; // 6 abort()
+#if defined(_POSIX_C_SOURCE)
+ case SIGPOLL:
+ return "SIGPOLL"; // 7 pollable event ([XSR] generated, not supported)
+#else // !_POSIX_C_SOURCE
+ case SIGEMT:
+ return "SIGEMT"; // 7 EMT instruction
+#endif // !_POSIX_C_SOURCE
+ case SIGFPE:
+ return "SIGFPE"; // 8 floating point exception
+ case SIGKILL:
+ return "SIGKILL"; // 9 kill (cannot be caught or ignored)
+ case SIGBUS:
+ return "SIGBUS"; // 10 bus error
+ case SIGSEGV:
+ return "SIGSEGV"; // 11 segmentation violation
+ case SIGSYS:
+ return "SIGSYS"; // 12 bad argument to system call
+ case SIGPIPE:
+ return "SIGPIPE"; // 13 write on a pipe with no one to read it
+ case SIGALRM:
+ return "SIGALRM"; // 14 alarm clock
+ case SIGTERM:
+ return "SIGTERM"; // 15 software termination signal from kill
+ case SIGURG:
+ return "SIGURG"; // 16 urgent condition on IO channel
+ case SIGSTOP:
+ return "SIGSTOP"; // 17 sendable stop signal not from tty
+ case SIGTSTP:
+ return "SIGTSTP"; // 18 stop signal from tty
+ case SIGCONT:
+ return "SIGCONT"; // 19 continue a stopped process
+ case SIGCHLD:
+ return "SIGCHLD"; // 20 to parent on child stop or exit
+ case SIGTTIN:
+ return "SIGTTIN"; // 21 to readers pgrp upon background tty read
+ case SIGTTOU:
+ return "SIGTTOU"; // 22 like TTIN for output if (tp->t_local&LTOSTOP)
+#if !defined(_POSIX_C_SOURCE)
+ case SIGIO:
+ return "SIGIO"; // 23 input/output possible signal
+#endif
+ case SIGXCPU:
+ return "SIGXCPU"; // 24 exceeded CPU time limit
+ case SIGXFSZ:
+ return "SIGXFSZ"; // 25 exceeded file size limit
+ case SIGVTALRM:
+ return "SIGVTALRM"; // 26 virtual time alarm
+ case SIGPROF:
+ return "SIGPROF"; // 27 profiling time alarm
+#if !defined(_POSIX_C_SOURCE)
+ case SIGWINCH:
+ return "SIGWINCH"; // 28 window size changes
+ case SIGINFO:
+ return "SIGINFO"; // 29 information request
+#endif
+ case SIGUSR1:
+ return "SIGUSR1"; // 30 user defined signal 1
+ case SIGUSR2:
+ return "SIGUSR2"; // 31 user defined signal 2
+ default:
+ break;
+ }
+ return NULL;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/SysSignal.h b/gnu/llvm/lldb/tools/debugserver/source/SysSignal.h
new file mode 100644
index 00000000000..b6fc67f9be7
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/SysSignal.h
@@ -0,0 +1,21 @@
+//===-- SysSignal.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 6/18/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __SysSignal_h__
+#define __SysSignal_h__
+
+class SysSignal {
+public:
+ static const char *Name(int signal);
+};
+
+#endif
diff --git a/gnu/llvm/lldb/tools/debugserver/source/TTYState.cpp b/gnu/llvm/lldb/tools/debugserver/source/TTYState.cpp
new file mode 100644
index 00000000000..96699a36049
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/TTYState.cpp
@@ -0,0 +1,93 @@
+//===-- TTYState.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 3/26/07.
+//
+//===----------------------------------------------------------------------===//
+
+#include "TTYState.h"
+#include <fcntl.h>
+#include <sys/signal.h>
+#include <unistd.h>
+
+TTYState::TTYState()
+ : m_fd(-1), m_tflags(-1), m_ttystateErr(-1), m_processGroup(-1) {}
+
+TTYState::~TTYState() {}
+
+bool TTYState::GetTTYState(int fd, bool saveProcessGroup) {
+ if (fd >= 0 && ::isatty(fd)) {
+ m_fd = fd;
+ m_tflags = fcntl(fd, F_GETFL, 0);
+ m_ttystateErr = tcgetattr(fd, &m_ttystate);
+ if (saveProcessGroup)
+ m_processGroup = tcgetpgrp(0);
+ else
+ m_processGroup = -1;
+ } else {
+ m_fd = -1;
+ m_tflags = -1;
+ m_ttystateErr = -1;
+ m_processGroup = -1;
+ }
+ return m_ttystateErr == 0;
+}
+
+bool TTYState::SetTTYState() const {
+ int result = 0;
+ if (IsValid()) {
+ if (TFlagsValid())
+ result = fcntl(m_fd, F_SETFL, m_tflags);
+
+ if (TTYStateValid())
+ result = tcsetattr(m_fd, TCSANOW, &m_ttystate);
+
+ if (ProcessGroupValid()) {
+ // Save the original signal handler.
+ void (*saved_sigttou_callback)(int) = NULL;
+ saved_sigttou_callback = (void (*)(int))signal(SIGTTOU, SIG_IGN);
+ // Set the process group
+ result = tcsetpgrp(m_fd, m_processGroup);
+ // Restore the original signal handler.
+ signal(SIGTTOU, saved_sigttou_callback);
+ }
+ return true;
+ }
+ return false;
+}
+
+TTYStateSwitcher::TTYStateSwitcher() : m_currentState(~0) {}
+
+TTYStateSwitcher::~TTYStateSwitcher() {}
+
+bool TTYStateSwitcher::GetState(uint32_t idx, int fd, bool saveProcessGroup) {
+ if (ValidStateIndex(idx))
+ return m_ttystates[idx].GetTTYState(fd, saveProcessGroup);
+ return false;
+}
+
+bool TTYStateSwitcher::SetState(uint32_t idx) const {
+ if (!ValidStateIndex(idx))
+ return false;
+
+ // See if we already are in this state?
+ if (ValidStateIndex(m_currentState) && (idx == m_currentState) &&
+ m_ttystates[idx].IsValid())
+ return true;
+
+ // Set the state to match the index passed in and only update the
+ // current state if there are no errors.
+ if (m_ttystates[idx].SetTTYState()) {
+ m_currentState = idx;
+ return true;
+ }
+
+ // We failed to set the state. The tty state was invalid or not
+ // initialized.
+ return false;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/TTYState.h b/gnu/llvm/lldb/tools/debugserver/source/TTYState.h
new file mode 100644
index 00000000000..01ef579cc6c
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/TTYState.h
@@ -0,0 +1,58 @@
+//===-- TTYState.h ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Created by Greg Clayton on 3/26/07.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __TTYState_h__
+#define __TTYState_h__
+
+#include <stdint.h>
+#include <termios.h>
+
+class TTYState {
+public:
+ TTYState();
+ ~TTYState();
+
+ bool GetTTYState(int fd, bool saveProcessGroup);
+ bool SetTTYState() const;
+
+ bool IsValid() const {
+ return FileDescriptorValid() && TFlagsValid() && TTYStateValid();
+ }
+ bool FileDescriptorValid() const { return m_fd >= 0; }
+ bool TFlagsValid() const { return m_tflags != -1; }
+ bool TTYStateValid() const { return m_ttystateErr == 0; }
+ bool ProcessGroupValid() const { return m_processGroup != -1; }
+
+protected:
+ int m_fd; // File descriptor
+ int m_tflags;
+ int m_ttystateErr;
+ struct termios m_ttystate;
+ pid_t m_processGroup;
+};
+
+class TTYStateSwitcher {
+public:
+ TTYStateSwitcher();
+ ~TTYStateSwitcher();
+
+ bool GetState(uint32_t idx, int fd, bool saveProcessGroup);
+ bool SetState(uint32_t idx) const;
+ uint32_t NumStates() const { return sizeof(m_ttystates) / sizeof(TTYState); }
+ bool ValidStateIndex(uint32_t idx) const { return idx < NumStates(); }
+
+protected:
+ mutable uint32_t m_currentState;
+ TTYState m_ttystates[2];
+};
+
+#endif
diff --git a/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.applist.internal.plist b/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.applist.internal.plist
new file mode 100644
index 00000000000..e9a74bd0bf7
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.applist.internal.plist
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>com.apple.debugserver.applist.internal</string>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/Developer/usr/bin/debugserver</string>
+ <string>--lockdown</string>
+ <string>--applist</string>
+ </array>
+ <key>AllowByProxy</key>
+ <true/>
+</dict>
+</plist>
diff --git a/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.applist.plist b/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.applist.plist
new file mode 100644
index 00000000000..002e90d98d1
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.applist.plist
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>com.apple.debugserver.applist</string>
+ <key>UserName</key>
+ <string>mobile</string>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/Developer/usr/bin/debugserver</string>
+ <string>--lockdown</string>
+ <string>--applist</string>
+ <string>--launch=frontboard</string>
+ </array>
+ <key>AllowByProxy</key>
+ <true/>
+</dict>
+</plist>
diff --git a/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.internal.plist b/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.internal.plist
new file mode 100644
index 00000000000..b9f57f73123
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.internal.plist
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>com.apple.debugserver.internal</string>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/Developer/usr/bin/debugserver</string>
+ <string>--lockdown</string>
+ </array>
+ <key>AllowByProxy</key>
+ <true/>
+</dict>
+</plist>
diff --git a/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.plist b/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.plist
new file mode 100644
index 00000000000..c07466e27cd
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.plist
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>com.apple.debugserver</string>
+ <key>UserName</key>
+ <string>mobile</string>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/Developer/usr/bin/debugserver</string>
+ <string>--lockdown</string>
+ <string>--launch=frontboard</string>
+ </array>
+ <key>AllowByProxy</key>
+ <true/>
+</dict>
+</plist>
diff --git a/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.posix.plist b/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.posix.plist
new file mode 100644
index 00000000000..4083f8a75c6
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/com.apple.debugserver.posix.plist
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>com.apple.debugserver.posix</string>
+ <key>UserName</key>
+ <string>mobile</string>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/Developer/usr/bin/debugserver</string>
+ <string>--lockdown</string>
+ <string>--launch=posix</string>
+ </array>
+ <key>AllowByProxy</key>
+ <true/>
+</dict>
+</plist>
diff --git a/gnu/llvm/lldb/tools/debugserver/source/com.apple.internal.xpc.remote.debugserver.plist b/gnu/llvm/lldb/tools/debugserver/source/com.apple.internal.xpc.remote.debugserver.plist
new file mode 100644
index 00000000000..837ebe7b59a
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/com.apple.internal.xpc.remote.debugserver.plist
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>com.apple.internal.xpc.remote.debugserver</string>
+ <key>RemoteServices</key>
+ <dict>
+ <key>com.apple.internal.debugserver</key>
+ <dict>
+ <key>RequireEntitlement</key>
+ <string>AppleInternal</string>
+ <key>ExposedToUntrustedDevices</key>
+ <true/>
+ </dict>
+ </dict>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/usr/libexec/remotectl</string>
+ <string>trampoline</string>
+ <string>-2</string>
+ <string>42</string>
+ <string>com.apple.internal.debugserver</string>
+ <string>/usr/local/bin/debugserver-nonui</string>
+ <string>--fd</string>
+ <string>42</string>
+ </array>
+ <key>POSIXSpawnType</key>
+ <string>Interactive</string>
+ <key>EnableTransactions</key>
+ <true/>
+ <key>EnablePressuredExit</key>
+ <true/>
+</dict>
+</plist>
diff --git a/gnu/llvm/lldb/tools/debugserver/source/debugserver-entitlements.plist b/gnu/llvm/lldb/tools/debugserver/source/debugserver-entitlements.plist
new file mode 100644
index 00000000000..1798610305d
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/debugserver-entitlements.plist
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.springboard.debugapplications</key>
+ <true/>
+ <key>com.apple.backboardd.launchapplications</key>
+ <true/>
+ <key>com.apple.backboardd.debugapplications</key>
+ <true/>
+ <key>com.apple.frontboard.launchapplications</key>
+ <true/>
+ <key>com.apple.frontboard.debugapplications</key>
+ <true/>
+ <key>seatbelt-profiles</key>
+ <array>
+ <string>debugserver</string>
+ </array>
+ <key>com.apple.private.logging.diagnostic</key>
+ <true/>
+ <key>com.apple.security.network.server</key>
+ <true/>
+ <key>com.apple.security.network.client</key>
+ <true/>
+ <key>com.apple.private.memorystatus</key>
+ <true/>
+ <key>com.apple.private.cs.debugger</key>
+ <true/>
+</dict>
+</plist>
diff --git a/gnu/llvm/lldb/tools/debugserver/source/debugserver-macosx-entitlements.plist b/gnu/llvm/lldb/tools/debugserver/source/debugserver-macosx-entitlements.plist
new file mode 100644
index 00000000000..d09bd924bf2
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/debugserver-macosx-entitlements.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.private.logging.diagnostic</key>
+ <true/>
+ <key>com.apple.private.cs.debugger</key>
+ <true/>
+</dict>
+</plist>
diff --git a/gnu/llvm/lldb/tools/debugserver/source/debugserver.cpp b/gnu/llvm/lldb/tools/debugserver/source/debugserver.cpp
new file mode 100644
index 00000000000..42205dedf4b
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/debugserver.cpp
@@ -0,0 +1,1678 @@
+//===-- debugserver.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <arpa/inet.h>
+#include <asl.h>
+#include <crt_externs.h>
+#include <errno.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <string>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <memory>
+#include <vector>
+
+#if defined(__APPLE__)
+#include <sched.h>
+extern "C" int proc_set_wakemon_params(pid_t, int,
+ int); // <libproc_internal.h> SPI
+#endif
+
+#include "CFString.h"
+#include "DNB.h"
+#include "DNBLog.h"
+#include "DNBTimer.h"
+#include "OsLogger.h"
+#include "PseudoTerminal.h"
+#include "RNBContext.h"
+#include "RNBRemote.h"
+#include "RNBServices.h"
+#include "RNBSocket.h"
+#include "SysSignal.h"
+
+// Global PID in case we get a signal and need to stop the process...
+nub_process_t g_pid = INVALID_NUB_PROCESS;
+
+// Run loop modes which determine which run loop function will be called
+enum RNBRunLoopMode {
+ eRNBRunLoopModeInvalid = 0,
+ eRNBRunLoopModeGetStartModeFromRemoteProtocol,
+ eRNBRunLoopModeInferiorAttaching,
+ eRNBRunLoopModeInferiorLaunching,
+ eRNBRunLoopModeInferiorExecuting,
+ eRNBRunLoopModePlatformMode,
+ eRNBRunLoopModeExit
+};
+
+// Global Variables
+RNBRemoteSP g_remoteSP;
+static int g_lockdown_opt = 0;
+static int g_applist_opt = 0;
+static nub_launch_flavor_t g_launch_flavor = eLaunchFlavorDefault;
+int g_disable_aslr = 0;
+
+int g_isatty = 0;
+bool g_detach_on_error = true;
+
+#define RNBLogSTDOUT(fmt, ...) \
+ do { \
+ if (g_isatty) { \
+ fprintf(stdout, fmt, ##__VA_ARGS__); \
+ } else { \
+ _DNBLog(0, fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+#define RNBLogSTDERR(fmt, ...) \
+ do { \
+ if (g_isatty) { \
+ fprintf(stderr, fmt, ##__VA_ARGS__); \
+ } else { \
+ _DNBLog(0, fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+// Get our program path and arguments from the remote connection.
+// We will need to start up the remote connection without a PID, get the
+// arguments, wait for the new process to finish launching and hit its
+// entry point, and then return the run loop mode that should come next.
+RNBRunLoopMode RNBRunLoopGetStartModeFromRemote(RNBRemote *remote) {
+ std::string packet;
+
+ if (remote) {
+ RNBContext &ctx = remote->Context();
+ uint32_t event_mask = RNBContext::event_read_packet_available |
+ RNBContext::event_read_thread_exiting;
+
+ // Spin waiting to get the A packet.
+ while (true) {
+ DNBLogThreadedIf(LOG_RNB_MAX,
+ "%s ctx.Events().WaitForSetEvents( 0x%08x ) ...",
+ __FUNCTION__, event_mask);
+ nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
+ DNBLogThreadedIf(LOG_RNB_MAX,
+ "%s ctx.Events().WaitForSetEvents( 0x%08x ) => 0x%08x",
+ __FUNCTION__, event_mask, set_events);
+
+ if (set_events & RNBContext::event_read_thread_exiting) {
+ RNBLogSTDERR("error: packet read thread exited.\n");
+ return eRNBRunLoopModeExit;
+ }
+
+ if (set_events & RNBContext::event_read_packet_available) {
+ rnb_err_t err = rnb_err;
+ RNBRemote::PacketEnum type;
+
+ err = remote->HandleReceivedPacket(&type);
+
+ // check if we tried to attach to a process
+ if (type == RNBRemote::vattach || type == RNBRemote::vattachwait ||
+ type == RNBRemote::vattachorwait) {
+ if (err == rnb_success) {
+ RNBLogSTDOUT("Attach succeeded, ready to debug.\n");
+ return eRNBRunLoopModeInferiorExecuting;
+ } else {
+ RNBLogSTDERR("error: attach failed.\n");
+ return eRNBRunLoopModeExit;
+ }
+ }
+
+ if (err == rnb_success) {
+ // If we got our arguments we are ready to launch using the arguments
+ // and any environment variables we received.
+ if (type == RNBRemote::set_argv) {
+ return eRNBRunLoopModeInferiorLaunching;
+ }
+ } else if (err == rnb_not_connected) {
+ RNBLogSTDERR("error: connection lost.\n");
+ return eRNBRunLoopModeExit;
+ } else {
+ // a catch all for any other gdb remote packets that failed
+ DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s Error getting packet.",
+ __FUNCTION__);
+ continue;
+ }
+
+ DNBLogThreadedIf(LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);
+ } else {
+ DNBLogThreadedIf(LOG_RNB_MINIMAL,
+ "%s Connection closed before getting \"A\" packet.",
+ __FUNCTION__);
+ return eRNBRunLoopModeExit;
+ }
+ }
+ }
+ return eRNBRunLoopModeExit;
+}
+
+// This run loop mode will wait for the process to launch and hit its
+// entry point. It will currently ignore all events except for the
+// process state changed event, where it watches for the process stopped
+// or crash process state.
+RNBRunLoopMode RNBRunLoopLaunchInferior(RNBRemote *remote,
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ bool no_stdio) {
+ RNBContext &ctx = remote->Context();
+
+ // The Process stuff takes a c array, the RNBContext has a vector...
+ // So make up a c array.
+
+ DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s Launching '%s'...", __FUNCTION__,
+ ctx.ArgumentAtIndex(0));
+
+ size_t inferior_argc = ctx.ArgumentCount();
+ // Initialize inferior_argv with inferior_argc + 1 NULLs
+ std::vector<const char *> inferior_argv(inferior_argc + 1, NULL);
+
+ size_t i;
+ for (i = 0; i < inferior_argc; i++)
+ inferior_argv[i] = ctx.ArgumentAtIndex(i);
+
+ // Pass the environment array the same way:
+
+ size_t inferior_envc = ctx.EnvironmentCount();
+ // Initialize inferior_argv with inferior_argc + 1 NULLs
+ std::vector<const char *> inferior_envp(inferior_envc + 1, NULL);
+
+ for (i = 0; i < inferior_envc; i++)
+ inferior_envp[i] = ctx.EnvironmentAtIndex(i);
+
+ // Our launch type hasn't been set to anything concrete, so we need to
+ // figure our how we are going to launch automatically.
+
+ nub_launch_flavor_t launch_flavor = g_launch_flavor;
+ if (launch_flavor == eLaunchFlavorDefault) {
+ // Our default launch method is posix spawn
+ launch_flavor = eLaunchFlavorPosixSpawn;
+
+#if defined WITH_FBS
+ // Check if we have an app bundle, if so launch using BackBoard Services.
+ if (strstr(inferior_argv[0], ".app")) {
+ launch_flavor = eLaunchFlavorFBS;
+ }
+#elif defined WITH_BKS
+ // Check if we have an app bundle, if so launch using BackBoard Services.
+ if (strstr(inferior_argv[0], ".app")) {
+ launch_flavor = eLaunchFlavorBKS;
+ }
+#elif defined WITH_SPRINGBOARD
+ // Check if we have an app bundle, if so launch using SpringBoard.
+ if (strstr(inferior_argv[0], ".app")) {
+ launch_flavor = eLaunchFlavorSpringBoard;
+ }
+#endif
+ }
+
+ ctx.SetLaunchFlavor(launch_flavor);
+ char resolved_path[PATH_MAX];
+
+ // If we fail to resolve the path to our executable, then just use what we
+ // were given and hope for the best
+ if (!DNBResolveExecutablePath(inferior_argv[0], resolved_path,
+ sizeof(resolved_path)))
+ ::strlcpy(resolved_path, inferior_argv[0], sizeof(resolved_path));
+
+ char launch_err_str[PATH_MAX];
+ launch_err_str[0] = '\0';
+ const char *cwd =
+ (ctx.GetWorkingDirPath() != NULL ? ctx.GetWorkingDirPath()
+ : ctx.GetWorkingDirectory());
+ const char *process_event = ctx.GetProcessEvent();
+ nub_process_t pid = DNBProcessLaunch(
+ resolved_path, &inferior_argv[0], &inferior_envp[0], cwd, stdin_path,
+ stdout_path, stderr_path, no_stdio, launch_flavor, g_disable_aslr,
+ process_event, launch_err_str, sizeof(launch_err_str));
+
+ g_pid = pid;
+
+ if (pid == INVALID_NUB_PROCESS && strlen(launch_err_str) > 0) {
+ DNBLogThreaded("%s DNBProcessLaunch() returned error: '%s'", __FUNCTION__,
+ launch_err_str);
+ ctx.LaunchStatus().SetError(-1, DNBError::Generic);
+ ctx.LaunchStatus().SetErrorString(launch_err_str);
+ } else if (pid == INVALID_NUB_PROCESS) {
+ DNBLogThreaded(
+ "%s DNBProcessLaunch() failed to launch process, unknown failure",
+ __FUNCTION__);
+ ctx.LaunchStatus().SetError(-1, DNBError::Generic);
+ ctx.LaunchStatus().SetErrorString("<unknown failure>");
+ } else {
+ ctx.LaunchStatus().Clear();
+ }
+
+ if (remote->Comm().IsConnected()) {
+ // It we are connected already, the next thing gdb will do is ask
+ // whether the launch succeeded, and if not, whether there is an
+ // error code. So we need to fetch one packet from gdb before we wait
+ // on the stop from the target.
+
+ uint32_t event_mask = RNBContext::event_read_packet_available;
+ nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
+
+ if (set_events & RNBContext::event_read_packet_available) {
+ rnb_err_t err = rnb_err;
+ RNBRemote::PacketEnum type;
+
+ err = remote->HandleReceivedPacket(&type);
+
+ if (err != rnb_success) {
+ DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s Error getting packet.",
+ __FUNCTION__);
+ return eRNBRunLoopModeExit;
+ }
+ if (type != RNBRemote::query_launch_success) {
+ DNBLogThreadedIf(LOG_RNB_MINIMAL,
+ "%s Didn't get the expected qLaunchSuccess packet.",
+ __FUNCTION__);
+ }
+ }
+ }
+
+ while (pid != INVALID_NUB_PROCESS) {
+ // Wait for process to start up and hit entry point
+ DNBLogThreadedIf(LOG_RNB_EVENTS, "%s DNBProcessWaitForEvent (%4.4x, "
+ "eEventProcessRunningStateChanged | "
+ "eEventProcessStoppedStateChanged, true, "
+ "INFINITE)...",
+ __FUNCTION__, pid);
+ nub_event_t set_events =
+ DNBProcessWaitForEvents(pid, eEventProcessRunningStateChanged |
+ eEventProcessStoppedStateChanged,
+ true, NULL);
+ DNBLogThreadedIf(LOG_RNB_EVENTS, "%s DNBProcessWaitForEvent (%4.4x, "
+ "eEventProcessRunningStateChanged | "
+ "eEventProcessStoppedStateChanged, true, "
+ "INFINITE) => 0x%8.8x",
+ __FUNCTION__, pid, set_events);
+
+ if (set_events == 0) {
+ pid = INVALID_NUB_PROCESS;
+ g_pid = pid;
+ } else {
+ if (set_events & (eEventProcessRunningStateChanged |
+ eEventProcessStoppedStateChanged)) {
+ nub_state_t pid_state = DNBProcessGetState(pid);
+ DNBLogThreadedIf(
+ LOG_RNB_EVENTS,
+ "%s process %4.4x state changed (eEventProcessStateChanged): %s",
+ __FUNCTION__, pid, DNBStateAsString(pid_state));
+
+ switch (pid_state) {
+ case eStateInvalid:
+ case eStateUnloaded:
+ case eStateAttaching:
+ case eStateLaunching:
+ case eStateSuspended:
+ break; // Ignore
+
+ case eStateRunning:
+ case eStateStepping:
+ // Still waiting to stop at entry point...
+ break;
+
+ case eStateStopped:
+ case eStateCrashed:
+ ctx.SetProcessID(pid);
+ return eRNBRunLoopModeInferiorExecuting;
+
+ case eStateDetached:
+ case eStateExited:
+ pid = INVALID_NUB_PROCESS;
+ g_pid = pid;
+ return eRNBRunLoopModeExit;
+ }
+ }
+
+ DNBProcessResetEvents(pid, set_events);
+ }
+ }
+
+ return eRNBRunLoopModeExit;
+}
+
+// This run loop mode will wait for the process to launch and hit its
+// entry point. It will currently ignore all events except for the
+// process state changed event, where it watches for the process stopped
+// or crash process state.
+RNBRunLoopMode RNBRunLoopLaunchAttaching(RNBRemote *remote,
+ nub_process_t attach_pid,
+ nub_process_t &pid) {
+ RNBContext &ctx = remote->Context();
+
+ DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s Attaching to pid %i...", __FUNCTION__,
+ attach_pid);
+ char err_str[1024];
+ pid = DNBProcessAttach(attach_pid, NULL, err_str, sizeof(err_str));
+ g_pid = pid;
+
+ if (pid == INVALID_NUB_PROCESS) {
+ ctx.LaunchStatus().SetError(-1, DNBError::Generic);
+ if (err_str[0])
+ ctx.LaunchStatus().SetErrorString(err_str);
+ return eRNBRunLoopModeExit;
+ } else {
+ ctx.SetProcessID(pid);
+ return eRNBRunLoopModeInferiorExecuting;
+ }
+}
+
+// Watch for signals:
+// SIGINT: so we can halt our inferior. (disabled for now)
+// SIGPIPE: in case our child process dies
+int g_sigint_received = 0;
+int g_sigpipe_received = 0;
+void signal_handler(int signo) {
+ DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s (%s)", __FUNCTION__,
+ SysSignal::Name(signo));
+
+ switch (signo) {
+ case SIGINT:
+ g_sigint_received++;
+ if (g_pid != INVALID_NUB_PROCESS) {
+ // Only send a SIGINT once...
+ if (g_sigint_received == 1) {
+ switch (DNBProcessGetState(g_pid)) {
+ case eStateRunning:
+ case eStateStepping:
+ DNBProcessSignal(g_pid, SIGSTOP);
+ return;
+ default:
+ break;
+ }
+ }
+ }
+ exit(SIGINT);
+ break;
+
+ case SIGPIPE:
+ g_sigpipe_received = 1;
+ break;
+ }
+}
+
+// Return the new run loop mode based off of the current process state
+RNBRunLoopMode HandleProcessStateChange(RNBRemote *remote, bool initialize) {
+ RNBContext &ctx = remote->Context();
+ nub_process_t pid = ctx.ProcessID();
+
+ if (pid == INVALID_NUB_PROCESS) {
+ DNBLogThreadedIf(LOG_RNB_MINIMAL, "#### %s error: pid invalid, exiting...",
+ __FUNCTION__);
+ return eRNBRunLoopModeExit;
+ }
+ nub_state_t pid_state = DNBProcessGetState(pid);
+
+ DNBLogThreadedIf(LOG_RNB_MINIMAL,
+ "%s (&remote, initialize=%i) pid_state = %s", __FUNCTION__,
+ (int)initialize, DNBStateAsString(pid_state));
+
+ switch (pid_state) {
+ case eStateInvalid:
+ case eStateUnloaded:
+ // Something bad happened
+ return eRNBRunLoopModeExit;
+ break;
+
+ case eStateAttaching:
+ case eStateLaunching:
+ return eRNBRunLoopModeInferiorExecuting;
+
+ case eStateSuspended:
+ case eStateCrashed:
+ case eStateStopped:
+ // If we stop due to a signal, so clear the fact that we got a SIGINT
+ // so we can stop ourselves again (but only while our inferior
+ // process is running..)
+ g_sigint_received = 0;
+ if (initialize == false) {
+ // Compare the last stop count to our current notion of a stop count
+ // to make sure we don't notify more than once for a given stop.
+ nub_size_t prev_pid_stop_count = ctx.GetProcessStopCount();
+ bool pid_stop_count_changed =
+ ctx.SetProcessStopCount(DNBProcessGetStopCount(pid));
+ if (pid_stop_count_changed) {
+ remote->FlushSTDIO();
+
+ if (ctx.GetProcessStopCount() == 1) {
+ DNBLogThreadedIf(
+ LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s "
+ "pid_stop_count %llu (old %llu)) Notify??? no, "
+ "first stop...",
+ __FUNCTION__, (int)initialize, DNBStateAsString(pid_state),
+ (uint64_t)ctx.GetProcessStopCount(),
+ (uint64_t)prev_pid_stop_count);
+ } else {
+
+ DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) "
+ "pid_state = %s pid_stop_count "
+ "%llu (old %llu)) Notify??? YES!!!",
+ __FUNCTION__, (int)initialize,
+ DNBStateAsString(pid_state),
+ (uint64_t)ctx.GetProcessStopCount(),
+ (uint64_t)prev_pid_stop_count);
+ remote->NotifyThatProcessStopped();
+ }
+ } else {
+ DNBLogThreadedIf(
+ LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s "
+ "pid_stop_count %llu (old %llu)) Notify??? "
+ "skipping...",
+ __FUNCTION__, (int)initialize, DNBStateAsString(pid_state),
+ (uint64_t)ctx.GetProcessStopCount(), (uint64_t)prev_pid_stop_count);
+ }
+ }
+ return eRNBRunLoopModeInferiorExecuting;
+
+ case eStateStepping:
+ case eStateRunning:
+ return eRNBRunLoopModeInferiorExecuting;
+
+ case eStateExited:
+ remote->HandlePacket_last_signal(NULL);
+ return eRNBRunLoopModeExit;
+ case eStateDetached:
+ return eRNBRunLoopModeExit;
+ }
+
+ // Catch all...
+ return eRNBRunLoopModeExit;
+}
+
+// This function handles the case where our inferior program is stopped and
+// we are waiting for gdb remote protocol packets. When a packet occurs that
+// makes the inferior run, we need to leave this function with a new state
+// as the return code.
+RNBRunLoopMode RNBRunLoopInferiorExecuting(RNBRemote *remote) {
+ DNBLogThreadedIf(LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);
+ RNBContext &ctx = remote->Context();
+
+ // Init our mode and set 'is_running' based on the current process state
+ RNBRunLoopMode mode = HandleProcessStateChange(remote, true);
+
+ while (ctx.ProcessID() != INVALID_NUB_PROCESS) {
+
+ std::string set_events_str;
+ uint32_t event_mask = ctx.NormalEventBits();
+
+ if (!ctx.ProcessStateRunning()) {
+ // Clear some bits if we are not running so we don't send any async
+ // packets
+ event_mask &= ~RNBContext::event_proc_stdio_available;
+ event_mask &= ~RNBContext::event_proc_profile_data;
+ // When we enable async structured data packets over another logical
+ // channel,
+ // this can be relaxed.
+ event_mask &= ~RNBContext::event_darwin_log_data_available;
+ }
+
+ // We want to make sure we consume all process state changes and have
+ // whomever is notifying us to wait for us to reset the event bit before
+ // continuing.
+ // ctx.Events().SetResetAckMask (RNBContext::event_proc_state_changed);
+
+ DNBLogThreadedIf(LOG_RNB_EVENTS,
+ "%s ctx.Events().WaitForSetEvents(0x%08x) ...",
+ __FUNCTION__, event_mask);
+ nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
+ DNBLogThreadedIf(LOG_RNB_EVENTS,
+ "%s ctx.Events().WaitForSetEvents(0x%08x) => 0x%08x (%s)",
+ __FUNCTION__, event_mask, set_events,
+ ctx.EventsAsString(set_events, set_events_str));
+
+ if (set_events) {
+ if ((set_events & RNBContext::event_proc_thread_exiting) ||
+ (set_events & RNBContext::event_proc_stdio_available)) {
+ remote->FlushSTDIO();
+ }
+
+ if (set_events & RNBContext::event_proc_profile_data) {
+ remote->SendAsyncProfileData();
+ }
+
+ if (set_events & RNBContext::event_darwin_log_data_available) {
+ remote->SendAsyncDarwinLogData();
+ }
+
+ if (set_events & RNBContext::event_read_packet_available) {
+ // handleReceivedPacket will take care of resetting the
+ // event_read_packet_available events when there are no more...
+ set_events ^= RNBContext::event_read_packet_available;
+
+ if (ctx.ProcessStateRunning()) {
+ if (remote->HandleAsyncPacket() == rnb_not_connected) {
+ // TODO: connect again? Exit?
+ }
+ } else {
+ if (remote->HandleReceivedPacket() == rnb_not_connected) {
+ // TODO: connect again? Exit?
+ }
+ }
+ }
+
+ if (set_events & RNBContext::event_proc_state_changed) {
+ mode = HandleProcessStateChange(remote, false);
+ ctx.Events().ResetEvents(RNBContext::event_proc_state_changed);
+ set_events ^= RNBContext::event_proc_state_changed;
+ }
+
+ if (set_events & RNBContext::event_proc_thread_exiting) {
+ mode = eRNBRunLoopModeExit;
+ }
+
+ if (set_events & RNBContext::event_read_thread_exiting) {
+ // Out remote packet receiving thread exited, exit for now.
+ if (ctx.HasValidProcessID()) {
+ // TODO: We should add code that will leave the current process
+ // in its current state and listen for another connection...
+ if (ctx.ProcessStateRunning()) {
+ if (ctx.GetDetachOnError()) {
+ DNBLog("debugserver's event read thread is exiting, detaching "
+ "from the inferior process.");
+ DNBProcessDetach(ctx.ProcessID());
+ } else {
+ DNBLog("debugserver's event read thread is exiting, killing the "
+ "inferior process.");
+ DNBProcessKill(ctx.ProcessID());
+ }
+ } else {
+ if (ctx.GetDetachOnError()) {
+ DNBLog("debugserver's event read thread is exiting, detaching "
+ "from the inferior process.");
+ DNBProcessDetach(ctx.ProcessID());
+ }
+ }
+ }
+ mode = eRNBRunLoopModeExit;
+ }
+ }
+
+ // Reset all event bits that weren't reset for now...
+ if (set_events != 0)
+ ctx.Events().ResetEvents(set_events);
+
+ if (mode != eRNBRunLoopModeInferiorExecuting)
+ break;
+ }
+
+ return mode;
+}
+
+RNBRunLoopMode RNBRunLoopPlatform(RNBRemote *remote) {
+ RNBRunLoopMode mode = eRNBRunLoopModePlatformMode;
+ RNBContext &ctx = remote->Context();
+
+ while (mode == eRNBRunLoopModePlatformMode) {
+ std::string set_events_str;
+ const uint32_t event_mask = RNBContext::event_read_packet_available |
+ RNBContext::event_read_thread_exiting;
+
+ DNBLogThreadedIf(LOG_RNB_EVENTS,
+ "%s ctx.Events().WaitForSetEvents(0x%08x) ...",
+ __FUNCTION__, event_mask);
+ nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
+ DNBLogThreadedIf(LOG_RNB_EVENTS,
+ "%s ctx.Events().WaitForSetEvents(0x%08x) => 0x%08x (%s)",
+ __FUNCTION__, event_mask, set_events,
+ ctx.EventsAsString(set_events, set_events_str));
+
+ if (set_events) {
+ if (set_events & RNBContext::event_read_packet_available) {
+ if (remote->HandleReceivedPacket() == rnb_not_connected)
+ mode = eRNBRunLoopModeExit;
+ }
+
+ if (set_events & RNBContext::event_read_thread_exiting) {
+ mode = eRNBRunLoopModeExit;
+ }
+ ctx.Events().ResetEvents(set_events);
+ }
+ }
+ return eRNBRunLoopModeExit;
+}
+
+// Convenience function to set up the remote listening port
+// Returns 1 for success 0 for failure.
+
+static void PortWasBoundCallbackUnixSocket(const void *baton, in_port_t port) {
+ //::printf ("PortWasBoundCallbackUnixSocket (baton = %p, port = %u)\n", baton,
+ //port);
+
+ const char *unix_socket_name = (const char *)baton;
+
+ if (unix_socket_name && unix_socket_name[0]) {
+ // We were given a unix socket name to use to communicate the port
+ // that we ended up binding to back to our parent process
+ struct sockaddr_un saddr_un;
+ int s = ::socket(AF_UNIX, SOCK_STREAM, 0);
+ if (s < 0) {
+ perror("error: socket (AF_UNIX, SOCK_STREAM, 0)");
+ exit(1);
+ }
+
+ saddr_un.sun_family = AF_UNIX;
+ ::strlcpy(saddr_un.sun_path, unix_socket_name,
+ sizeof(saddr_un.sun_path) - 1);
+ saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0';
+ saddr_un.sun_len = SUN_LEN(&saddr_un);
+
+ if (::connect(s, (struct sockaddr *)&saddr_un,
+ static_cast<socklen_t>(SUN_LEN(&saddr_un))) < 0) {
+ perror("error: connect (socket, &saddr_un, saddr_un_len)");
+ exit(1);
+ }
+
+ //::printf ("connect () sucess!!\n");
+
+ // We were able to connect to the socket, now write our PID so whomever
+ // launched us will know this process's ID
+ RNBLogSTDOUT("Listening to port %i...\n", port);
+
+ char pid_str[64];
+ const int pid_str_len = ::snprintf(pid_str, sizeof(pid_str), "%u", port);
+ const ssize_t bytes_sent = ::send(s, pid_str, pid_str_len, 0);
+
+ if (pid_str_len != bytes_sent) {
+ perror("error: send (s, pid_str, pid_str_len, 0)");
+ exit(1);
+ }
+
+ //::printf ("send () sucess!!\n");
+
+ // We are done with the socket
+ close(s);
+ }
+}
+
+static void PortWasBoundCallbackNamedPipe(const void *baton, uint16_t port) {
+ const char *named_pipe = (const char *)baton;
+ if (named_pipe && named_pipe[0]) {
+ int fd = ::open(named_pipe, O_WRONLY);
+ if (fd > -1) {
+ char port_str[64];
+ const ssize_t port_str_len =
+ ::snprintf(port_str, sizeof(port_str), "%u", port);
+ // Write the port number as a C string with the NULL terminator
+ ::write(fd, port_str, port_str_len + 1);
+ close(fd);
+ }
+ }
+}
+
+static int ConnectRemote(RNBRemote *remote, const char *host, int port,
+ bool reverse_connect, const char *named_pipe_path,
+ const char *unix_socket_name) {
+ if (!remote->Comm().IsConnected()) {
+ if (reverse_connect) {
+ if (port == 0) {
+ DNBLogThreaded(
+ "error: invalid port supplied for reverse connection: %i.\n", port);
+ return 0;
+ }
+ if (remote->Comm().Connect(host, port) != rnb_success) {
+ DNBLogThreaded("Failed to reverse connect to %s:%i.\n", host, port);
+ return 0;
+ }
+ } else {
+ if (port != 0)
+ RNBLogSTDOUT("Listening to port %i for a connection from %s...\n", port,
+ host ? host : "127.0.0.1");
+ if (unix_socket_name && unix_socket_name[0]) {
+ if (remote->Comm().Listen(host, port, PortWasBoundCallbackUnixSocket,
+ unix_socket_name) != rnb_success) {
+ RNBLogSTDERR("Failed to get connection from a remote gdb process.\n");
+ return 0;
+ }
+ } else {
+ if (remote->Comm().Listen(host, port, PortWasBoundCallbackNamedPipe,
+ named_pipe_path) != rnb_success) {
+ RNBLogSTDERR("Failed to get connection from a remote gdb process.\n");
+ return 0;
+ }
+ }
+ }
+ remote->StartReadRemoteDataThread();
+ }
+ return 1;
+}
+
+// ASL Logging callback that can be registered with DNBLogSetLogCallback
+void ASLLogCallback(void *baton, uint32_t flags, const char *format,
+ va_list args) {
+ if (format == NULL)
+ return;
+ 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.%s-%s",
+ DEBUGSERVER_PROGRAM_NAME, DEBUGSERVER_VERSION_STR);
+ ::asl_set(g_aslmsg, ASL_KEY_SENDER, asl_key_sender);
+ }
+
+ int asl_level;
+ if (flags & DNBLOG_FLAG_FATAL)
+ asl_level = ASL_LEVEL_CRIT;
+ else if (flags & DNBLOG_FLAG_ERROR)
+ asl_level = ASL_LEVEL_ERR;
+ else if (flags & DNBLOG_FLAG_WARNING)
+ asl_level = ASL_LEVEL_WARNING;
+ else if (flags & DNBLOG_FLAG_VERBOSE)
+ asl_level = ASL_LEVEL_WARNING; // ASL_LEVEL_INFO;
+ else
+ asl_level = ASL_LEVEL_WARNING; // ASL_LEVEL_DEBUG;
+
+ ::asl_vlog(NULL, g_aslmsg, asl_level, format, args);
+}
+
+// FILE based Logging callback that can be registered with
+// DNBLogSetLogCallback
+void FileLogCallback(void *baton, uint32_t flags, const char *format,
+ va_list args) {
+ if (baton == NULL || format == NULL)
+ return;
+
+ ::vfprintf((FILE *)baton, format, args);
+ ::fprintf((FILE *)baton, "\n");
+ ::fflush((FILE *)baton);
+}
+
+void show_version_and_exit(int exit_code) {
+ printf("%s-%s for %s.\n", DEBUGSERVER_PROGRAM_NAME, DEBUGSERVER_VERSION_STR,
+ RNB_ARCH);
+ exit(exit_code);
+}
+
+void show_usage_and_exit(int exit_code) {
+ RNBLogSTDERR(
+ "Usage:\n %s host:port [program-name program-arg1 program-arg2 ...]\n",
+ DEBUGSERVER_PROGRAM_NAME);
+ RNBLogSTDERR(" %s /path/file [program-name program-arg1 program-arg2 ...]\n",
+ DEBUGSERVER_PROGRAM_NAME);
+ RNBLogSTDERR(" %s host:port --attach=<pid>\n", DEBUGSERVER_PROGRAM_NAME);
+ RNBLogSTDERR(" %s /path/file --attach=<pid>\n", DEBUGSERVER_PROGRAM_NAME);
+ RNBLogSTDERR(" %s host:port --attach=<process_name>\n",
+ DEBUGSERVER_PROGRAM_NAME);
+ RNBLogSTDERR(" %s /path/file --attach=<process_name>\n",
+ DEBUGSERVER_PROGRAM_NAME);
+ exit(exit_code);
+}
+
+// option descriptors for getopt_long_only()
+static struct option g_long_options[] = {
+ {"attach", required_argument, NULL, 'a'},
+ {"arch", required_argument, NULL, 'A'},
+ {"debug", no_argument, NULL, 'g'},
+ {"kill-on-error", no_argument, NULL, 'K'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"version", no_argument, NULL, 'V'},
+ {"lockdown", no_argument, &g_lockdown_opt, 1}, // short option "-k"
+ {"applist", no_argument, &g_applist_opt, 1}, // short option "-t"
+ {"log-file", required_argument, NULL, 'l'},
+ {"log-flags", required_argument, NULL, 'f'},
+ {"launch", required_argument, NULL, 'x'}, // Valid values are "auto",
+ // "posix-spawn", "fork-exec",
+ // "springboard" (arm only)
+ {"waitfor", required_argument, NULL,
+ 'w'}, // Wait for a process whose name starts with ARG
+ {"waitfor-interval", required_argument, NULL,
+ 'i'}, // Time in usecs to wait between sampling the pid list when waiting
+ // for a process by name
+ {"waitfor-duration", required_argument, NULL,
+ 'd'}, // The time in seconds to wait for a process to show up by name
+ {"native-regs", no_argument, NULL, 'r'}, // Specify to use the native
+ // registers instead of the gdb
+ // defaults for the architecture.
+ {"stdio-path", required_argument, NULL,
+ 's'}, // Set the STDIO path to be used when launching applications (STDIN,
+ // STDOUT and STDERR) (only if debugserver launches the process)
+ {"stdin-path", required_argument, NULL,
+ 'I'}, // Set the STDIN path to be used when launching applications (only if
+ // debugserver launches the process)
+ {"stdout-path", required_argument, NULL,
+ 'O'}, // Set the STDOUT path to be used when launching applications (only
+ // if debugserver launches the process)
+ {"stderr-path", required_argument, NULL,
+ 'E'}, // Set the STDERR path to be used when launching applications (only
+ // if debugserver launches the process)
+ {"no-stdio", no_argument, NULL,
+ 'n'}, // Do not set up any stdio (perhaps the program is a GUI program)
+ // (only if debugserver launches the process)
+ {"setsid", no_argument, NULL,
+ 'S'}, // call setsid() to make debugserver run in its own session
+ {"disable-aslr", no_argument, NULL, 'D'}, // Use _POSIX_SPAWN_DISABLE_ASLR
+ // to avoid shared library
+ // randomization
+ {"working-dir", required_argument, NULL,
+ 'W'}, // The working directory that the inferior process should have (only
+ // if debugserver launches the process)
+ {"platform", required_argument, NULL,
+ 'p'}, // Put this executable into a remote platform mode
+ {"unix-socket", required_argument, NULL,
+ 'u'}, // If we need to handshake with our parent process, an option will be
+ // passed down that specifies a unix socket name to use
+ {"fd", required_argument, NULL,
+ '2'}, // A file descriptor was passed to this process when spawned that
+ // is already open and ready for communication
+ {"named-pipe", required_argument, NULL, 'P'},
+ {"reverse-connect", no_argument, NULL, 'R'},
+ {"env", required_argument, NULL,
+ 'e'}, // When debugserver launches the process, set a single environment
+ // entry as specified by the option value ("./debugserver -e FOO=1 -e
+ // BAR=2 localhost:1234 -- /bin/ls")
+ {"forward-env", no_argument, NULL,
+ 'F'}, // When debugserver launches the process, forward debugserver's
+ // current environment variables to the child process ("./debugserver
+ // -F localhost:1234 -- /bin/ls"
+ {NULL, 0, NULL, 0}};
+
+// main
+int main(int argc, char *argv[]) {
+ // If debugserver is launched with DYLD_INSERT_LIBRARIES, unset it so we
+ // don't spawn child processes with this enabled.
+ unsetenv("DYLD_INSERT_LIBRARIES");
+
+ const char *argv_sub_zero =
+ argv[0]; // save a copy of argv[0] for error reporting post-launch
+
+#if defined(__APPLE__)
+ pthread_setname_np("main thread");
+#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
+ struct sched_param thread_param;
+ int thread_sched_policy;
+ if (pthread_getschedparam(pthread_self(), &thread_sched_policy,
+ &thread_param) == 0) {
+ thread_param.sched_priority = 47;
+ pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);
+ }
+
+ ::proc_set_wakemon_params(
+ getpid(), 500,
+ 0); // Allow up to 500 wakeups/sec to avoid EXC_RESOURCE for normal use.
+#endif
+#endif
+
+ g_isatty = ::isatty(STDIN_FILENO);
+
+ // ::printf ("uid=%u euid=%u gid=%u egid=%u\n",
+ // getuid(),
+ // geteuid(),
+ // getgid(),
+ // getegid());
+
+ // signal (SIGINT, signal_handler);
+ signal(SIGPIPE, signal_handler);
+ signal(SIGHUP, signal_handler);
+
+ // We're always sitting in waitpid or kevent waiting on our target process'
+ // death,
+ // we don't need no stinking SIGCHLD's...
+
+ sigset_t sigset;
+ sigemptyset(&sigset);
+ sigaddset(&sigset, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &sigset, NULL);
+
+ g_remoteSP = std::make_shared<RNBRemote>();
+
+ RNBRemote *remote = g_remoteSP.get();
+ if (remote == NULL) {
+ RNBLogSTDERR("error: failed to create a remote connection class\n");
+ return -1;
+ }
+
+ RNBContext &ctx = remote->Context();
+
+ int i;
+ int attach_pid = INVALID_NUB_PROCESS;
+
+ FILE *log_file = NULL;
+ uint32_t log_flags = 0;
+ // Parse our options
+ int ch;
+ int long_option_index = 0;
+ int debug = 0;
+ int communication_fd = -1;
+ std::string compile_options;
+ std::string waitfor_pid_name; // Wait for a process that starts with this name
+ std::string attach_pid_name;
+ std::string arch_name;
+ std::string working_dir; // The new working directory to use for the inferior
+ std::string unix_socket_name; // If we need to handshake with our parent
+ // process, an option will be passed down that
+ // specifies a unix socket name to use
+ std::string named_pipe_path; // If we need to handshake with our parent
+ // process, an option will be passed down that
+ // specifies a named pipe to use
+ useconds_t waitfor_interval = 1000; // Time in usecs between process lists
+ // polls when waiting for a process by
+ // name, default 1 msec.
+ useconds_t waitfor_duration =
+ 0; // Time in seconds to wait for a process by name, 0 means wait forever.
+ bool no_stdio = false;
+ bool reverse_connect = false; // Set to true by an option to indicate we
+ // should reverse connect to the host:port
+ // supplied as the first debugserver argument
+
+#if !defined(DNBLOG_ENABLED)
+ compile_options += "(no-logging) ";
+#endif
+
+ RNBRunLoopMode start_mode = eRNBRunLoopModeExit;
+
+ char short_options[512];
+ uint32_t short_options_idx = 0;
+
+ // Handle the two case that don't have short options in g_long_options
+ short_options[short_options_idx++] = 'k';
+ short_options[short_options_idx++] = 't';
+
+ for (i = 0; g_long_options[i].name != NULL; ++i) {
+ if (isalpha(g_long_options[i].val)) {
+ short_options[short_options_idx++] = g_long_options[i].val;
+ switch (g_long_options[i].has_arg) {
+ default:
+ case no_argument:
+ break;
+
+ case optional_argument:
+ short_options[short_options_idx++] = ':';
+ short_options[short_options_idx++] = ':';
+ break;
+ case required_argument:
+ short_options[short_options_idx++] = ':';
+ break;
+ }
+ }
+ }
+ // NULL terminate the short option string.
+ short_options[short_options_idx++] = '\0';
+
+#if __GLIBC__
+ optind = 0;
+#else
+ optreset = 1;
+ optind = 1;
+#endif
+
+ bool forward_env = false;
+ while ((ch = getopt_long_only(argc, argv, short_options, g_long_options,
+ &long_option_index)) != -1) {
+ DNBLogDebug("option: ch == %c (0x%2.2x) --%s%c%s\n", ch, (uint8_t)ch,
+ g_long_options[long_option_index].name,
+ g_long_options[long_option_index].has_arg ? '=' : ' ',
+ optarg ? optarg : "");
+ switch (ch) {
+ case 0: // Any optional that auto set themselves will return 0
+ break;
+
+ case 'A':
+ if (optarg && optarg[0])
+ arch_name.assign(optarg);
+ break;
+
+ case 'a':
+ if (optarg && optarg[0]) {
+ if (isdigit(optarg[0])) {
+ char *end = NULL;
+ attach_pid = static_cast<int>(strtoul(optarg, &end, 0));
+ if (end == NULL || *end != '\0') {
+ RNBLogSTDERR("error: invalid pid option '%s'\n", optarg);
+ exit(4);
+ }
+ } else {
+ attach_pid_name = optarg;
+ }
+ start_mode = eRNBRunLoopModeInferiorAttaching;
+ }
+ break;
+
+ // --waitfor=NAME
+ case 'w':
+ if (optarg && optarg[0]) {
+ waitfor_pid_name = optarg;
+ start_mode = eRNBRunLoopModeInferiorAttaching;
+ }
+ break;
+
+ // --waitfor-interval=USEC
+ case 'i':
+ if (optarg && optarg[0]) {
+ char *end = NULL;
+ waitfor_interval = static_cast<useconds_t>(strtoul(optarg, &end, 0));
+ if (end == NULL || *end != '\0') {
+ RNBLogSTDERR("error: invalid waitfor-interval option value '%s'.\n",
+ optarg);
+ exit(6);
+ }
+ }
+ break;
+
+ // --waitfor-duration=SEC
+ case 'd':
+ if (optarg && optarg[0]) {
+ char *end = NULL;
+ waitfor_duration = static_cast<useconds_t>(strtoul(optarg, &end, 0));
+ if (end == NULL || *end != '\0') {
+ RNBLogSTDERR("error: invalid waitfor-duration option value '%s'.\n",
+ optarg);
+ exit(7);
+ }
+ }
+ break;
+
+ case 'K':
+ g_detach_on_error = false;
+ break;
+ case 'W':
+ if (optarg && optarg[0])
+ working_dir.assign(optarg);
+ break;
+
+ case 'x':
+ if (optarg && optarg[0]) {
+ if (strcasecmp(optarg, "auto") == 0)
+ g_launch_flavor = eLaunchFlavorDefault;
+ else if (strcasestr(optarg, "posix") == optarg)
+ g_launch_flavor = eLaunchFlavorPosixSpawn;
+ else if (strcasestr(optarg, "fork") == optarg)
+ g_launch_flavor = eLaunchFlavorForkExec;
+#ifdef WITH_SPRINGBOARD
+ else if (strcasestr(optarg, "spring") == optarg)
+ g_launch_flavor = eLaunchFlavorSpringBoard;
+#endif
+#ifdef WITH_BKS
+ else if (strcasestr(optarg, "backboard") == optarg)
+ g_launch_flavor = eLaunchFlavorBKS;
+#endif
+#ifdef WITH_FBS
+ else if (strcasestr(optarg, "frontboard") == optarg)
+ g_launch_flavor = eLaunchFlavorFBS;
+#endif
+
+ else {
+ RNBLogSTDERR("error: invalid TYPE for the --launch=TYPE (-x TYPE) "
+ "option: '%s'\n",
+ optarg);
+ RNBLogSTDERR("Valid values TYPE are:\n");
+ RNBLogSTDERR(
+ " auto Auto-detect the best launch method to use.\n");
+ RNBLogSTDERR(
+ " posix Launch the executable using posix_spawn.\n");
+ RNBLogSTDERR(
+ " fork Launch the executable using fork and exec.\n");
+#ifdef WITH_SPRINGBOARD
+ RNBLogSTDERR(
+ " spring Launch the executable through Springboard.\n");
+#endif
+#ifdef WITH_BKS
+ RNBLogSTDERR(" backboard Launch the executable through BackBoard "
+ "Services.\n");
+#endif
+#ifdef WITH_FBS
+ RNBLogSTDERR(" frontboard Launch the executable through FrontBoard "
+ "Services.\n");
+#endif
+ exit(5);
+ }
+ }
+ break;
+
+ case 'l': // Set Log File
+ if (optarg && optarg[0]) {
+ if (strcasecmp(optarg, "stdout") == 0)
+ log_file = stdout;
+ else if (strcasecmp(optarg, "stderr") == 0)
+ log_file = stderr;
+ else {
+ log_file = fopen(optarg, "w");
+ if (log_file != NULL)
+ setlinebuf(log_file);
+ }
+
+ if (log_file == NULL) {
+ const char *errno_str = strerror(errno);
+ RNBLogSTDERR(
+ "Failed to open log file '%s' for writing: errno = %i (%s)",
+ optarg, errno, errno_str ? errno_str : "unknown error");
+ }
+ }
+ break;
+
+ case 'f': // Log Flags
+ if (optarg && optarg[0])
+ log_flags = static_cast<uint32_t>(strtoul(optarg, NULL, 0));
+ break;
+
+ case 'g':
+ debug = 1;
+ DNBLogSetDebug(debug);
+ break;
+
+ case 't':
+ g_applist_opt = 1;
+ break;
+
+ case 'k':
+ g_lockdown_opt = 1;
+ break;
+
+ case 'r':
+ // Do nothing, native regs is the default these days
+ break;
+
+ case 'R':
+ reverse_connect = true;
+ break;
+ case 'v':
+ DNBLogSetVerbose(1);
+ break;
+
+ case 'V':
+ show_version_and_exit(0);
+ break;
+
+ case 's':
+ ctx.GetSTDIN().assign(optarg);
+ ctx.GetSTDOUT().assign(optarg);
+ ctx.GetSTDERR().assign(optarg);
+ break;
+
+ case 'I':
+ ctx.GetSTDIN().assign(optarg);
+ break;
+
+ case 'O':
+ ctx.GetSTDOUT().assign(optarg);
+ break;
+
+ case 'E':
+ ctx.GetSTDERR().assign(optarg);
+ break;
+
+ case 'n':
+ no_stdio = true;
+ break;
+
+ case 'S':
+ // Put debugserver into a new session. Terminals group processes
+ // into sessions and when a special terminal key sequences
+ // (like control+c) are typed they can cause signals to go out to
+ // all processes in a session. Using this --setsid (-S) option
+ // will cause debugserver to run in its own sessions and be free
+ // from such issues.
+ //
+ // This is useful when debugserver is spawned from a command
+ // line application that uses debugserver to do the debugging,
+ // yet that application doesn't want debugserver receiving the
+ // signals sent to the session (i.e. dying when anyone hits ^C).
+ setsid();
+ break;
+ case 'D':
+ g_disable_aslr = 1;
+ break;
+
+ case 'p':
+ start_mode = eRNBRunLoopModePlatformMode;
+ break;
+
+ case 'u':
+ unix_socket_name.assign(optarg);
+ break;
+
+ case 'P':
+ named_pipe_path.assign(optarg);
+ break;
+
+ case 'e':
+ // Pass a single specified environment variable down to the process that
+ // gets launched
+ remote->Context().PushEnvironment(optarg);
+ break;
+
+ case 'F':
+ forward_env = true;
+ break;
+
+ case '2':
+ // File descriptor passed to this process during fork/exec and is already
+ // open and ready for communication.
+ communication_fd = atoi(optarg);
+ break;
+ }
+ }
+
+ if (arch_name.empty()) {
+#if defined(__arm__)
+ arch_name.assign("arm");
+#endif
+ } else {
+ DNBSetArchitecture(arch_name.c_str());
+ }
+
+ // if (arch_name.empty())
+ // {
+ // fprintf(stderr, "error: no architecture was specified\n");
+ // exit (8);
+ // }
+ // Skip any options we consumed with getopt_long_only
+ argc -= optind;
+ argv += optind;
+
+ if (!working_dir.empty()) {
+ if (remote->Context().SetWorkingDirectory(working_dir.c_str()) == false) {
+ RNBLogSTDERR("error: working directory doesn't exist '%s'.\n",
+ working_dir.c_str());
+ exit(8);
+ }
+ }
+
+ remote->Context().SetDetachOnError(g_detach_on_error);
+
+ remote->Initialize();
+
+ // It is ok for us to set NULL as the logfile (this will disable any logging)
+
+ if (log_file != NULL) {
+ DNBLogSetLogCallback(FileLogCallback, log_file);
+ // If our log file was set, yet we have no log flags, log everything!
+ if (log_flags == 0)
+ log_flags = LOG_ALL | LOG_RNB_ALL;
+
+ DNBLogSetLogMask(log_flags);
+ } else {
+ // Enable DNB logging
+
+ // if os_log() support is available, log through that.
+ auto log_callback = OsLogger::GetLogFunction();
+ if (log_callback) {
+ DNBLogSetLogCallback(log_callback, nullptr);
+ DNBLog("debugserver will use os_log for internal logging.");
+ } else {
+ // Fall back to ASL support.
+ DNBLogSetLogCallback(ASLLogCallback, NULL);
+ DNBLog("debugserver will use ASL for internal logging.");
+ }
+ DNBLogSetLogMask(log_flags);
+ }
+
+ if (DNBLogEnabled()) {
+ for (i = 0; i < argc; i++)
+ DNBLogDebug("argv[%i] = %s", i, argv[i]);
+ }
+
+ // as long as we're dropping remotenub in as a replacement for gdbserver,
+ // explicitly note that this is not gdbserver.
+
+ RNBLogSTDOUT("%s-%s %sfor %s.\n", DEBUGSERVER_PROGRAM_NAME,
+ DEBUGSERVER_VERSION_STR, compile_options.c_str(), RNB_ARCH);
+
+ std::string host;
+ int port = INT32_MAX;
+ char str[PATH_MAX];
+ str[0] = '\0';
+
+ if (g_lockdown_opt == 0 && g_applist_opt == 0 && communication_fd == -1) {
+ // Make sure we at least have port
+ if (argc < 1) {
+ show_usage_and_exit(1);
+ }
+ // accept 'localhost:' prefix on port number
+ std::string host_specifier = argv[0];
+ auto colon_location = host_specifier.rfind(':');
+ if (colon_location != std::string::npos) {
+ host = host_specifier.substr(0, colon_location);
+ std::string port_str =
+ host_specifier.substr(colon_location + 1, std::string::npos);
+ char *end_ptr;
+ port = strtoul(port_str.c_str(), &end_ptr, 0);
+ if (end_ptr < port_str.c_str() + port_str.size())
+ show_usage_and_exit(2);
+ if (host.front() == '[' && host.back() == ']')
+ host = host.substr(1, host.size() - 2);
+ DNBLogDebug("host = '%s' port = %i", host.c_str(), port);
+ } else {
+ // No hostname means "localhost"
+ int items_scanned = ::sscanf(argv[0], "%i", &port);
+ if (items_scanned == 1) {
+ host = "127.0.0.1";
+ DNBLogDebug("host = '%s' port = %i", host.c_str(), port);
+ } else if (argv[0][0] == '/') {
+ port = INT32_MAX;
+ strlcpy(str, argv[0], sizeof(str));
+ } else {
+ show_usage_and_exit(2);
+ }
+ }
+
+ // We just used the 'host:port' or the '/path/file' arg...
+ argc--;
+ argv++;
+ }
+
+ // If we know we're waiting to attach, we don't need any of this other info.
+ if (start_mode != eRNBRunLoopModeInferiorAttaching &&
+ start_mode != eRNBRunLoopModePlatformMode) {
+ if (argc == 0 || g_lockdown_opt) {
+ if (g_lockdown_opt != 0) {
+ // Work around for SIGPIPE crashes due to posix_spawn issue.
+ // We have to close STDOUT and STDERR, else the first time we
+ // try and do any, we get SIGPIPE and die as posix_spawn is
+ // doing bad things with our file descriptors at the moment.
+ int null = open("/dev/null", O_RDWR);
+ dup2(null, STDOUT_FILENO);
+ dup2(null, STDERR_FILENO);
+ } else if (g_applist_opt != 0) {
+ // List all applications we are able to see
+ std::string applist_plist;
+ int err = ListApplications(applist_plist, false, false);
+ if (err == 0) {
+ fputs(applist_plist.c_str(), stdout);
+ } else {
+ RNBLogSTDERR("error: ListApplications returned error %i\n", err);
+ }
+ // Exit with appropriate error if we were asked to list the applications
+ // with no other args were given (and we weren't trying to do this over
+ // lockdown)
+ return err;
+ }
+
+ DNBLogDebug("Get args from remote protocol...");
+ start_mode = eRNBRunLoopModeGetStartModeFromRemoteProtocol;
+ } else {
+ start_mode = eRNBRunLoopModeInferiorLaunching;
+ // Fill in the argv array in the context from the rest of our args.
+ // Skip the name of this executable and the port number
+ for (int i = 0; i < argc; i++) {
+ DNBLogDebug("inferior_argv[%i] = '%s'", i, argv[i]);
+ ctx.PushArgument(argv[i]);
+ }
+ }
+ }
+
+ if (start_mode == eRNBRunLoopModeExit)
+ return -1;
+
+ if (forward_env || start_mode == eRNBRunLoopModeInferiorLaunching) {
+ // Pass the current environment down to the process that gets launched
+ // This happens automatically in the "launching" mode. For the rest, we
+ // only do that if the user explicitly requested this via --forward-env
+ // argument.
+ char **host_env = *_NSGetEnviron();
+ char *env_entry;
+ size_t i;
+ for (i = 0; (env_entry = host_env[i]) != NULL; ++i)
+ remote->Context().PushEnvironmentIfNeeded(env_entry);
+ }
+
+ RNBRunLoopMode mode = start_mode;
+ char err_str[1024] = {'\0'};
+
+ while (mode != eRNBRunLoopModeExit) {
+ switch (mode) {
+ case eRNBRunLoopModeGetStartModeFromRemoteProtocol:
+#ifdef WITH_LOCKDOWN
+ if (g_lockdown_opt) {
+ if (!remote->Comm().IsConnected()) {
+ if (remote->Comm().ConnectToService() != rnb_success) {
+ RNBLogSTDERR(
+ "Failed to get connection from a remote gdb process.\n");
+ mode = eRNBRunLoopModeExit;
+ } else if (g_applist_opt != 0) {
+ // List all applications we are able to see
+ std::string applist_plist;
+ if (ListApplications(applist_plist, false, false) == 0) {
+ DNBLogDebug("Task list: %s", applist_plist.c_str());
+
+ remote->Comm().Write(applist_plist.c_str(), applist_plist.size());
+ // Issue a read that will never yield any data until the other
+ // side
+ // closes the socket so this process doesn't just exit and cause
+ // the
+ // socket to close prematurely on the other end and cause data
+ // loss.
+ std::string buf;
+ remote->Comm().Read(buf);
+ }
+ remote->Comm().Disconnect(false);
+ mode = eRNBRunLoopModeExit;
+ break;
+ } else {
+ // Start watching for remote packets
+ remote->StartReadRemoteDataThread();
+ }
+ }
+ } else
+#endif
+ if (port != INT32_MAX) {
+ if (!ConnectRemote(remote, host.c_str(), port, reverse_connect,
+ named_pipe_path.c_str(), unix_socket_name.c_str()))
+ mode = eRNBRunLoopModeExit;
+ } else if (str[0] == '/') {
+ if (remote->Comm().OpenFile(str))
+ mode = eRNBRunLoopModeExit;
+ } else if (communication_fd >= 0) {
+ // We were passed a file descriptor to use during fork/exec that is
+ // already open
+ // in our process, so lets just use it!
+ if (remote->Comm().useFD(communication_fd))
+ mode = eRNBRunLoopModeExit;
+ else
+ remote->StartReadRemoteDataThread();
+ }
+
+ if (mode != eRNBRunLoopModeExit) {
+ RNBLogSTDOUT("Got a connection, waiting for process information for "
+ "launching or attaching.\n");
+
+ mode = RNBRunLoopGetStartModeFromRemote(remote);
+ }
+ break;
+
+ case eRNBRunLoopModeInferiorAttaching:
+ if (!waitfor_pid_name.empty()) {
+ // Set our end wait time if we are using a waitfor-duration
+ // option that may have been specified
+ struct timespec attach_timeout_abstime, *timeout_ptr = NULL;
+ if (waitfor_duration != 0) {
+ DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, waitfor_duration,
+ 0);
+ timeout_ptr = &attach_timeout_abstime;
+ }
+ nub_launch_flavor_t launch_flavor = g_launch_flavor;
+ if (launch_flavor == eLaunchFlavorDefault) {
+ // Our default launch method is posix spawn
+ launch_flavor = eLaunchFlavorPosixSpawn;
+
+#if defined WITH_FBS
+ // Check if we have an app bundle, if so launch using SpringBoard.
+ if (waitfor_pid_name.find(".app") != std::string::npos) {
+ launch_flavor = eLaunchFlavorFBS;
+ }
+#elif defined WITH_BKS
+ // Check if we have an app bundle, if so launch using SpringBoard.
+ if (waitfor_pid_name.find(".app") != std::string::npos) {
+ launch_flavor = eLaunchFlavorBKS;
+ }
+#elif defined WITH_SPRINGBOARD
+ // Check if we have an app bundle, if so launch using SpringBoard.
+ if (waitfor_pid_name.find(".app") != std::string::npos) {
+ launch_flavor = eLaunchFlavorSpringBoard;
+ }
+#endif
+ }
+
+ ctx.SetLaunchFlavor(launch_flavor);
+ bool ignore_existing = false;
+ RNBLogSTDOUT("Waiting to attach to process %s...\n",
+ waitfor_pid_name.c_str());
+ nub_process_t pid = DNBProcessAttachWait(
+ waitfor_pid_name.c_str(), launch_flavor, ignore_existing,
+ timeout_ptr, waitfor_interval, err_str, sizeof(err_str));
+ g_pid = pid;
+
+ if (pid == INVALID_NUB_PROCESS) {
+ ctx.LaunchStatus().SetError(-1, DNBError::Generic);
+ if (err_str[0])
+ ctx.LaunchStatus().SetErrorString(err_str);
+ RNBLogSTDERR("error: failed to attach to process named: \"%s\" %s\n",
+ waitfor_pid_name.c_str(), err_str);
+ mode = eRNBRunLoopModeExit;
+ } else {
+ ctx.SetProcessID(pid);
+ mode = eRNBRunLoopModeInferiorExecuting;
+ }
+ } else if (attach_pid != INVALID_NUB_PROCESS) {
+
+ RNBLogSTDOUT("Attaching to process %i...\n", attach_pid);
+ nub_process_t attached_pid;
+ mode = RNBRunLoopLaunchAttaching(remote, attach_pid, attached_pid);
+ if (mode != eRNBRunLoopModeInferiorExecuting) {
+ const char *error_str = remote->Context().LaunchStatus().AsString();
+ RNBLogSTDERR("error: failed to attach process %i: %s\n", attach_pid,
+ error_str ? error_str : "unknown error.");
+ mode = eRNBRunLoopModeExit;
+ }
+ } else if (!attach_pid_name.empty()) {
+ struct timespec attach_timeout_abstime, *timeout_ptr = NULL;
+ if (waitfor_duration != 0) {
+ DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, waitfor_duration,
+ 0);
+ timeout_ptr = &attach_timeout_abstime;
+ }
+
+ RNBLogSTDOUT("Attaching to process %s...\n", attach_pid_name.c_str());
+ nub_process_t pid = DNBProcessAttachByName(
+ attach_pid_name.c_str(), timeout_ptr, err_str, sizeof(err_str));
+ g_pid = pid;
+ if (pid == INVALID_NUB_PROCESS) {
+ ctx.LaunchStatus().SetError(-1, DNBError::Generic);
+ if (err_str[0])
+ ctx.LaunchStatus().SetErrorString(err_str);
+ RNBLogSTDERR("error: failed to attach to process named: \"%s\" %s\n",
+ waitfor_pid_name.c_str(), err_str);
+ mode = eRNBRunLoopModeExit;
+ } else {
+ ctx.SetProcessID(pid);
+ mode = eRNBRunLoopModeInferiorExecuting;
+ }
+
+ } else {
+ RNBLogSTDERR(
+ "error: asked to attach with empty name and invalid PID.\n");
+ mode = eRNBRunLoopModeExit;
+ }
+
+ if (mode != eRNBRunLoopModeExit) {
+ if (port != INT32_MAX) {
+ if (!ConnectRemote(remote, host.c_str(), port, reverse_connect,
+ named_pipe_path.c_str(), unix_socket_name.c_str()))
+ mode = eRNBRunLoopModeExit;
+ } else if (str[0] == '/') {
+ if (remote->Comm().OpenFile(str))
+ mode = eRNBRunLoopModeExit;
+ } else if (communication_fd >= 0) {
+ // We were passed a file descriptor to use during fork/exec that is
+ // already open
+ // in our process, so lets just use it!
+ if (remote->Comm().useFD(communication_fd))
+ mode = eRNBRunLoopModeExit;
+ else
+ remote->StartReadRemoteDataThread();
+ }
+
+ if (mode != eRNBRunLoopModeExit)
+ RNBLogSTDOUT("Waiting for debugger instructions for process %d.\n",
+ attach_pid);
+ }
+ break;
+
+ case eRNBRunLoopModeInferiorLaunching: {
+ mode = RNBRunLoopLaunchInferior(remote, ctx.GetSTDINPath(),
+ ctx.GetSTDOUTPath(), ctx.GetSTDERRPath(),
+ no_stdio);
+
+ if (mode == eRNBRunLoopModeInferiorExecuting) {
+ if (port != INT32_MAX) {
+ if (!ConnectRemote(remote, host.c_str(), port, reverse_connect,
+ named_pipe_path.c_str(), unix_socket_name.c_str()))
+ mode = eRNBRunLoopModeExit;
+ } else if (str[0] == '/') {
+ if (remote->Comm().OpenFile(str))
+ mode = eRNBRunLoopModeExit;
+ } else if (communication_fd >= 0) {
+ // We were passed a file descriptor to use during fork/exec that is
+ // already open
+ // in our process, so lets just use it!
+ if (remote->Comm().useFD(communication_fd))
+ mode = eRNBRunLoopModeExit;
+ else
+ remote->StartReadRemoteDataThread();
+ }
+
+ if (mode != eRNBRunLoopModeExit) {
+ const char *proc_name = "<unknown>";
+ if (ctx.ArgumentCount() > 0)
+ proc_name = ctx.ArgumentAtIndex(0);
+ RNBLogSTDOUT("Got a connection, launched process %s (pid = %d).\n",
+ proc_name, ctx.ProcessID());
+ }
+ } else {
+ const char *error_str = remote->Context().LaunchStatus().AsString();
+ RNBLogSTDERR("error: failed to launch process %s: %s\n", argv_sub_zero,
+ error_str ? error_str : "unknown error.");
+ }
+ } break;
+
+ case eRNBRunLoopModeInferiorExecuting:
+ mode = RNBRunLoopInferiorExecuting(remote);
+ break;
+
+ case eRNBRunLoopModePlatformMode:
+ if (port != INT32_MAX) {
+ if (!ConnectRemote(remote, host.c_str(), port, reverse_connect,
+ named_pipe_path.c_str(), unix_socket_name.c_str()))
+ mode = eRNBRunLoopModeExit;
+ } else if (str[0] == '/') {
+ if (remote->Comm().OpenFile(str))
+ mode = eRNBRunLoopModeExit;
+ } else if (communication_fd >= 0) {
+ // We were passed a file descriptor to use during fork/exec that is
+ // already open
+ // in our process, so lets just use it!
+ if (remote->Comm().useFD(communication_fd))
+ mode = eRNBRunLoopModeExit;
+ else
+ remote->StartReadRemoteDataThread();
+ }
+
+ if (mode != eRNBRunLoopModeExit)
+ mode = RNBRunLoopPlatform(remote);
+ break;
+
+ default:
+ mode = eRNBRunLoopModeExit;
+ break;
+ case eRNBRunLoopModeExit:
+ break;
+ }
+ }
+
+ remote->StopReadRemoteDataThread();
+ remote->Context().SetProcessID(INVALID_NUB_PROCESS);
+ RNBLogSTDOUT("Exiting.\n");
+
+ return 0;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/debugserver_vers.c.in b/gnu/llvm/lldb/tools/debugserver/source/debugserver_vers.c.in
new file mode 100644
index 00000000000..00e34c29b07
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/debugserver_vers.c.in
@@ -0,0 +1,2 @@
+const unsigned char debugserverVersionString[] __attribute__ ((used)) = "@(#)PROGRAM:LLDB PROJECT:lldb-@LLDB_VERSION_MAJOR@.@LLDB_VERSION_MINOR@.@LLDB_VERSION_PATCH@" "\n";
+const double debugserverVersionNumber __attribute__ ((used)) = (double)@LLDB_VERSION_MAJOR@.@LLDB_VERSION_MINOR@;
diff --git a/gnu/llvm/lldb/tools/debugserver/source/libdebugserver.cpp b/gnu/llvm/lldb/tools/debugserver/source/libdebugserver.cpp
new file mode 100644
index 00000000000..0c53fa4039c
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/libdebugserver.cpp
@@ -0,0 +1,376 @@
+//===-- libdebugserver.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <errno.h>
+#include <getopt.h>
+#include <netinet/in.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include <memory>
+
+#include "DNB.h"
+#include "DNBLog.h"
+#include "DNBTimer.h"
+#include "PseudoTerminal.h"
+#include "RNBContext.h"
+#include "RNBRemote.h"
+#include "RNBServices.h"
+#include "RNBSocket.h"
+#include "SysSignal.h"
+
+// Run loop modes which determine which run loop function will be called
+enum RNBRunLoopMode {
+ eRNBRunLoopModeInvalid = 0,
+ eRNBRunLoopModeGetStartModeFromRemoteProtocol,
+ eRNBRunLoopModeInferiorExecuting,
+ eRNBRunLoopModeExit
+};
+
+// Global Variables
+RNBRemoteSP g_remoteSP;
+int g_disable_aslr = 0;
+int g_isatty = 0;
+
+#define RNBLogSTDOUT(fmt, ...) \
+ do { \
+ if (g_isatty) { \
+ fprintf(stdout, fmt, ##__VA_ARGS__); \
+ } else { \
+ _DNBLog(0, fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+#define RNBLogSTDERR(fmt, ...) \
+ do { \
+ if (g_isatty) { \
+ fprintf(stderr, fmt, ##__VA_ARGS__); \
+ } else { \
+ _DNBLog(0, fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+// Get our program path and arguments from the remote connection.
+// We will need to start up the remote connection without a PID, get the
+// arguments, wait for the new process to finish launching and hit its
+// entry point, and then return the run loop mode that should come next.
+RNBRunLoopMode RNBRunLoopGetStartModeFromRemote(RNBRemoteSP &remoteSP) {
+ std::string packet;
+
+ if (remoteSP.get() != NULL) {
+ RNBRemote *remote = remoteSP.get();
+ RNBContext &ctx = remote->Context();
+ uint32_t event_mask = RNBContext::event_read_packet_available;
+
+ // Spin waiting to get the A packet.
+ while (true) {
+ DNBLogThreadedIf(LOG_RNB_MAX,
+ "%s ctx.Events().WaitForSetEvents( 0x%08x ) ...",
+ __FUNCTION__, event_mask);
+ nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
+ DNBLogThreadedIf(LOG_RNB_MAX,
+ "%s ctx.Events().WaitForSetEvents( 0x%08x ) => 0x%08x",
+ __FUNCTION__, event_mask, set_events);
+
+ if (set_events & RNBContext::event_read_packet_available) {
+ rnb_err_t err = rnb_err;
+ RNBRemote::PacketEnum type;
+
+ err = remote->HandleReceivedPacket(&type);
+
+ // check if we tried to attach to a process
+ if (type == RNBRemote::vattach || type == RNBRemote::vattachwait) {
+ if (err == rnb_success)
+ return eRNBRunLoopModeInferiorExecuting;
+ else {
+ RNBLogSTDERR("error: attach failed.");
+ return eRNBRunLoopModeExit;
+ }
+ }
+
+ if (err == rnb_success) {
+ DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s Got success...", __FUNCTION__);
+ continue;
+ } else if (err == rnb_not_connected) {
+ RNBLogSTDERR("error: connection lost.");
+ return eRNBRunLoopModeExit;
+ } else {
+ // a catch all for any other gdb remote packets that failed
+ DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s Error getting packet.",
+ __FUNCTION__);
+ continue;
+ }
+
+ DNBLogThreadedIf(LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);
+ } else {
+ DNBLogThreadedIf(LOG_RNB_MINIMAL,
+ "%s Connection closed before getting \"A\" packet.",
+ __FUNCTION__);
+ return eRNBRunLoopModeExit;
+ }
+ }
+ }
+ return eRNBRunLoopModeExit;
+}
+
+// Watch for signals:
+// SIGINT: so we can halt our inferior. (disabled for now)
+// SIGPIPE: in case our child process dies
+nub_process_t g_pid;
+int g_sigpipe_received = 0;
+void signal_handler(int signo) {
+ DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s (%s)", __FUNCTION__,
+ SysSignal::Name(signo));
+
+ switch (signo) {
+ // case SIGINT:
+ // DNBProcessKill (g_pid, signo);
+ // break;
+
+ case SIGPIPE:
+ g_sigpipe_received = 1;
+ break;
+ }
+}
+
+// Return the new run loop mode based off of the current process state
+RNBRunLoopMode HandleProcessStateChange(RNBRemoteSP &remote, bool initialize) {
+ RNBContext &ctx = remote->Context();
+ nub_process_t pid = ctx.ProcessID();
+
+ if (pid == INVALID_NUB_PROCESS) {
+ DNBLogThreadedIf(LOG_RNB_MINIMAL, "#### %s error: pid invalid, exiting...",
+ __FUNCTION__);
+ return eRNBRunLoopModeExit;
+ }
+ nub_state_t pid_state = DNBProcessGetState(pid);
+
+ DNBLogThreadedIf(LOG_RNB_MINIMAL,
+ "%s (&remote, initialize=%i) pid_state = %s", __FUNCTION__,
+ (int)initialize, DNBStateAsString(pid_state));
+
+ switch (pid_state) {
+ case eStateInvalid:
+ case eStateUnloaded:
+ // Something bad happened
+ return eRNBRunLoopModeExit;
+ break;
+
+ case eStateAttaching:
+ case eStateLaunching:
+ return eRNBRunLoopModeInferiorExecuting;
+
+ case eStateSuspended:
+ case eStateCrashed:
+ case eStateStopped:
+ if (!initialize) {
+ // Compare the last stop count to our current notion of a stop count
+ // to make sure we don't notify more than once for a given stop.
+ nub_size_t prev_pid_stop_count = ctx.GetProcessStopCount();
+ bool pid_stop_count_changed =
+ ctx.SetProcessStopCount(DNBProcessGetStopCount(pid));
+ if (pid_stop_count_changed) {
+ remote->FlushSTDIO();
+
+ if (ctx.GetProcessStopCount() == 1) {
+ DNBLogThreadedIf(
+ LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s "
+ "pid_stop_count %zu (old %zu)) Notify??? no, "
+ "first stop...",
+ __FUNCTION__, (int)initialize, DNBStateAsString(pid_state),
+ ctx.GetProcessStopCount(), prev_pid_stop_count);
+ } else {
+
+ DNBLogThreadedIf(
+ LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s "
+ "pid_stop_count %zu (old %zu)) Notify??? YES!!!",
+ __FUNCTION__, (int)initialize, DNBStateAsString(pid_state),
+ ctx.GetProcessStopCount(), prev_pid_stop_count);
+ remote->NotifyThatProcessStopped();
+ }
+ } else {
+ DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) "
+ "pid_state = %s pid_stop_count %zu "
+ "(old %zu)) Notify??? skipping...",
+ __FUNCTION__, (int)initialize,
+ DNBStateAsString(pid_state), ctx.GetProcessStopCount(),
+ prev_pid_stop_count);
+ }
+ }
+ return eRNBRunLoopModeInferiorExecuting;
+
+ case eStateStepping:
+ case eStateRunning:
+ return eRNBRunLoopModeInferiorExecuting;
+
+ case eStateExited:
+ remote->HandlePacket_last_signal(NULL);
+ return eRNBRunLoopModeExit;
+ case eStateDetached:
+ return eRNBRunLoopModeExit;
+ }
+
+ // Catch all...
+ return eRNBRunLoopModeExit;
+}
+// This function handles the case where our inferior program is stopped and
+// we are waiting for gdb remote protocol packets. When a packet occurs that
+// makes the inferior run, we need to leave this function with a new state
+// as the return code.
+RNBRunLoopMode RNBRunLoopInferiorExecuting(RNBRemoteSP &remote) {
+ DNBLogThreadedIf(LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);
+ RNBContext &ctx = remote->Context();
+
+ // Init our mode and set 'is_running' based on the current process state
+ RNBRunLoopMode mode = HandleProcessStateChange(remote, true);
+
+ while (ctx.ProcessID() != INVALID_NUB_PROCESS) {
+
+ std::string set_events_str;
+ uint32_t event_mask = ctx.NormalEventBits();
+
+ if (!ctx.ProcessStateRunning()) {
+ // Clear the stdio bits if we are not running so we don't send any async
+ // packets
+ event_mask &= ~RNBContext::event_proc_stdio_available;
+ }
+
+ // We want to make sure we consume all process state changes and have
+ // whomever is notifying us to wait for us to reset the event bit before
+ // continuing.
+ // ctx.Events().SetResetAckMask (RNBContext::event_proc_state_changed);
+
+ DNBLogThreadedIf(LOG_RNB_EVENTS,
+ "%s ctx.Events().WaitForSetEvents(0x%08x) ...",
+ __FUNCTION__, event_mask);
+ nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
+ DNBLogThreadedIf(LOG_RNB_EVENTS,
+ "%s ctx.Events().WaitForSetEvents(0x%08x) => 0x%08x (%s)",
+ __FUNCTION__, event_mask, set_events,
+ ctx.EventsAsString(set_events, set_events_str));
+
+ if (set_events) {
+ if ((set_events & RNBContext::event_proc_thread_exiting) ||
+ (set_events & RNBContext::event_proc_stdio_available)) {
+ remote->FlushSTDIO();
+ }
+
+ if (set_events & RNBContext::event_read_packet_available) {
+ // handleReceivedPacket will take care of resetting the
+ // event_read_packet_available events when there are no more...
+ set_events ^= RNBContext::event_read_packet_available;
+
+ if (ctx.ProcessStateRunning()) {
+ if (remote->HandleAsyncPacket() == rnb_not_connected) {
+ // TODO: connect again? Exit?
+ }
+ } else {
+ if (remote->HandleReceivedPacket() == rnb_not_connected) {
+ // TODO: connect again? Exit?
+ }
+ }
+ }
+
+ if (set_events & RNBContext::event_proc_state_changed) {
+ mode = HandleProcessStateChange(remote, false);
+ ctx.Events().ResetEvents(RNBContext::event_proc_state_changed);
+ set_events ^= RNBContext::event_proc_state_changed;
+ }
+
+ if (set_events & RNBContext::event_proc_thread_exiting) {
+ mode = eRNBRunLoopModeExit;
+ }
+
+ if (set_events & RNBContext::event_read_thread_exiting) {
+ // Out remote packet receiving thread exited, exit for now.
+ if (ctx.HasValidProcessID()) {
+ // TODO: We should add code that will leave the current process
+ // in its current state and listen for another connection...
+ if (ctx.ProcessStateRunning()) {
+ DNBProcessKill(ctx.ProcessID());
+ }
+ }
+ mode = eRNBRunLoopModeExit;
+ }
+ }
+
+ // Reset all event bits that weren't reset for now...
+ if (set_events != 0)
+ ctx.Events().ResetEvents(set_events);
+
+ if (mode != eRNBRunLoopModeInferiorExecuting)
+ break;
+ }
+
+ return mode;
+}
+
+void ASLLogCallback(void *baton, uint32_t flags, const char *format,
+ va_list args) {
+#if 0
+ vprintf(format, args);
+#endif
+}
+
+extern "C" int debug_server_main(int fd) {
+#if 1
+ g_isatty = 0;
+#else
+ g_isatty = ::isatty(STDIN_FILENO);
+
+ DNBLogSetDebug(1);
+ DNBLogSetVerbose(1);
+ DNBLogSetLogMask(-1);
+ DNBLogSetLogCallback(ASLLogCallback, NULL);
+#endif
+
+ signal(SIGPIPE, signal_handler);
+
+ g_remoteSP = std::make_shared<RNBRemote>();
+
+ RNBRemote *remote = g_remoteSP.get();
+ if (remote == NULL) {
+ RNBLogSTDERR("error: failed to create a remote connection class\n");
+ return -1;
+ }
+
+ RNBRunLoopMode mode = eRNBRunLoopModeGetStartModeFromRemoteProtocol;
+
+ while (mode != eRNBRunLoopModeExit) {
+ switch (mode) {
+ case eRNBRunLoopModeGetStartModeFromRemoteProtocol:
+ if (g_remoteSP->Comm().useFD(fd) == rnb_success) {
+ RNBLogSTDOUT("Starting remote data thread.\n");
+ g_remoteSP->StartReadRemoteDataThread();
+
+ RNBLogSTDOUT("Waiting for start mode from remote.\n");
+ mode = RNBRunLoopGetStartModeFromRemote(g_remoteSP);
+ } else {
+ mode = eRNBRunLoopModeExit;
+ }
+ break;
+
+ case eRNBRunLoopModeInferiorExecuting:
+ mode = RNBRunLoopInferiorExecuting(g_remoteSP);
+ break;
+
+ default:
+ mode = eRNBRunLoopModeExit;
+ break;
+
+ case eRNBRunLoopModeExit:
+ break;
+ }
+ }
+
+ g_remoteSP->StopReadRemoteDataThread();
+ g_remoteSP->Context().SetProcessID(INVALID_NUB_PROCESS);
+
+ return 0;
+}
diff --git a/gnu/llvm/lldb/tools/debugserver/source/libdebugserver.h b/gnu/llvm/lldb/tools/debugserver/source/libdebugserver.h
new file mode 100644
index 00000000000..17b048bcbbc
--- /dev/null
+++ b/gnu/llvm/lldb/tools/debugserver/source/libdebugserver.h
@@ -0,0 +1,14 @@
+//===-- libdebugserver.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef debugserver_libdebugserver_h
+#define debugserver_libdebugserver_h
+
+int debug_server_main(int fd);
+
+#endif
diff --git a/gnu/llvm/lldb/tools/driver/CMakeLists.txt b/gnu/llvm/lldb/tools/driver/CMakeLists.txt
new file mode 100644
index 00000000000..c31863b205c
--- /dev/null
+++ b/gnu/llvm/lldb/tools/driver/CMakeLists.txt
@@ -0,0 +1,48 @@
+set(LLVM_TARGET_DEFINITIONS Options.td)
+tablegen(LLVM Options.inc -gen-opt-parser-defs)
+add_public_tablegen_target(LLDBOptionsTableGen)
+
+if(APPLE)
+ configure_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lldb-Info.plist.in
+ ${CMAKE_CURRENT_BINARY_DIR}/lldb-Info.plist
+ )
+ # Inline info plist in binary (use target_link_options for this as soon as CMake 3.13 is available)
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-sectcreate,__TEXT,__info_plist,${CMAKE_CURRENT_BINARY_DIR}/lldb-Info.plist")
+endif()
+
+add_lldb_tool(lldb
+ Driver.cpp
+ Platform.cpp
+
+ LINK_LIBS
+ liblldb
+
+ LINK_COMPONENTS
+ Option
+ Support
+ )
+
+if ( CMAKE_SYSTEM_NAME MATCHES "Windows" )
+ add_definitions( -DIMPORT_LIBLLDB )
+endif()
+
+add_dependencies(lldb
+ LLDBOptionsTableGen
+ ${tablegen_deps}
+)
+
+set_target_properties(LLDBOptionsTableGen PROPERTIES FOLDER "lldb misc")
+
+if(LLDB_BUILD_FRAMEWORK)
+ # In the build-tree, we know the exact path to the framework directory.
+ # The installed framework can be in different locations.
+ lldb_setup_rpaths(lldb
+ BUILD_RPATH
+ "${LLDB_FRAMEWORK_ABSOLUTE_BUILD_DIR}"
+ INSTALL_RPATH
+ "@loader_path/../../../SharedFrameworks"
+ "@loader_path/../../System/Library/PrivateFrameworks"
+ "@loader_path/../../Library/PrivateFrameworks"
+ )
+endif()
diff --git a/gnu/llvm/lldb/tools/driver/Driver.cpp b/gnu/llvm/lldb/tools/driver/Driver.cpp
new file mode 100644
index 00000000000..73874389aa1
--- /dev/null
+++ b/gnu/llvm/lldb/tools/driver/Driver.cpp
@@ -0,0 +1,896 @@
+//===-- Driver.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Driver.h"
+
+#include "lldb/API/SBCommandInterpreter.h"
+#include "lldb/API/SBCommandReturnObject.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBFile.h"
+#include "lldb/API/SBHostOS.h"
+#include "lldb/API/SBLanguageRuntime.h"
+#include "lldb/API/SBReproducer.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBStringList.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <algorithm>
+#include <atomic>
+#include <bitset>
+#include <csignal>
+#include <string>
+#include <thread>
+#include <utility>
+
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// Includes for pipe()
+#if defined(_WIN32)
+#include <fcntl.h>
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+#if !defined(__APPLE__)
+#include "llvm/Support/DataTypes.h"
+#endif
+
+using namespace lldb;
+using namespace llvm;
+
+namespace {
+enum ID {
+ OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OPT_##ID,
+#include "Options.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Options.inc"
+#undef PREFIX
+
+const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ { \
+ PREFIX, NAME, HELPTEXT, \
+ METAVAR, OPT_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OPT_##GROUP, \
+ OPT_##ALIAS, ALIASARGS, VALUES},
+#include "Options.inc"
+#undef OPTION
+};
+
+class LLDBOptTable : public opt::OptTable {
+public:
+ LLDBOptTable() : OptTable(InfoTable) {}
+};
+} // namespace
+
+static void reset_stdin_termios();
+static bool g_old_stdin_termios_is_valid = false;
+static struct termios g_old_stdin_termios;
+
+static Driver *g_driver = nullptr;
+
+// In the Driver::MainLoop, we change the terminal settings. This function is
+// added as an atexit handler to make sure we clean them up.
+static void reset_stdin_termios() {
+ if (g_old_stdin_termios_is_valid) {
+ g_old_stdin_termios_is_valid = false;
+ ::tcsetattr(STDIN_FILENO, TCSANOW, &g_old_stdin_termios);
+ }
+}
+
+Driver::Driver()
+ : SBBroadcaster("Driver"), m_debugger(SBDebugger::Create(false)) {
+ // We want to be able to handle CTRL+D in the terminal to have it terminate
+ // certain input
+ m_debugger.SetCloseInputOnEOF(false);
+ g_driver = this;
+}
+
+Driver::~Driver() { g_driver = nullptr; }
+
+void Driver::OptionData::AddInitialCommand(std::string command,
+ CommandPlacement placement,
+ bool is_file, SBError &error) {
+ std::vector<InitialCmdEntry> *command_set;
+ switch (placement) {
+ case eCommandPlacementBeforeFile:
+ command_set = &(m_initial_commands);
+ break;
+ case eCommandPlacementAfterFile:
+ command_set = &(m_after_file_commands);
+ break;
+ case eCommandPlacementAfterCrash:
+ command_set = &(m_after_crash_commands);
+ break;
+ }
+
+ if (is_file) {
+ SBFileSpec file(command.c_str());
+ if (file.Exists())
+ command_set->push_back(InitialCmdEntry(command, is_file));
+ else if (file.ResolveExecutableLocation()) {
+ char final_path[PATH_MAX];
+ file.GetPath(final_path, sizeof(final_path));
+ command_set->push_back(InitialCmdEntry(final_path, is_file));
+ } else
+ error.SetErrorStringWithFormat(
+ "file specified in --source (-s) option doesn't exist: '%s'",
+ command.c_str());
+ } else
+ command_set->push_back(InitialCmdEntry(command, is_file));
+}
+
+void Driver::WriteCommandsForSourcing(CommandPlacement placement,
+ SBStream &strm) {
+ std::vector<OptionData::InitialCmdEntry> *command_set;
+ switch (placement) {
+ case eCommandPlacementBeforeFile:
+ command_set = &m_option_data.m_initial_commands;
+ break;
+ case eCommandPlacementAfterFile:
+ command_set = &m_option_data.m_after_file_commands;
+ break;
+ case eCommandPlacementAfterCrash:
+ command_set = &m_option_data.m_after_crash_commands;
+ break;
+ }
+
+ for (const auto &command_entry : *command_set) {
+ const char *command = command_entry.contents.c_str();
+ if (command_entry.is_file) {
+ bool source_quietly =
+ m_option_data.m_source_quietly || command_entry.source_quietly;
+ strm.Printf("command source -s %i '%s'\n",
+ static_cast<int>(source_quietly), command);
+ } else
+ strm.Printf("%s\n", command);
+ }
+}
+
+// Check the arguments that were passed to this program to make sure they are
+// valid and to get their argument values (if any). Return a boolean value
+// indicating whether or not to start up the full debugger (i.e. the Command
+// Interpreter) or not. Return FALSE if the arguments were invalid OR if the
+// user only wanted help or version information.
+SBError Driver::ProcessArgs(const opt::InputArgList &args, bool &exiting) {
+ SBError error;
+
+ // This is kind of a pain, but since we make the debugger in the Driver's
+ // constructor, we can't know at that point whether we should read in init
+ // files yet. So we don't read them in in the Driver constructor, then set
+ // the flags back to "read them in" here, and then if we see the "-n" flag,
+ // we'll turn it off again. Finally we have to read them in by hand later in
+ // the main loop.
+ m_debugger.SkipLLDBInitFiles(false);
+ m_debugger.SkipAppInitFiles(false);
+
+ if (args.hasArg(OPT_version)) {
+ m_option_data.m_print_version = true;
+ }
+
+ if (args.hasArg(OPT_python_path)) {
+ m_option_data.m_print_python_path = true;
+ }
+
+ if (args.hasArg(OPT_batch)) {
+ m_option_data.m_batch = true;
+ }
+
+ if (auto *arg = args.getLastArg(OPT_core)) {
+ auto arg_value = arg->getValue();
+ SBFileSpec file(arg_value);
+ if (!file.Exists()) {
+ error.SetErrorStringWithFormat(
+ "file specified in --core (-c) option doesn't exist: '%s'",
+ arg_value);
+ return error;
+ }
+ m_option_data.m_core_file = arg_value;
+ }
+
+ if (args.hasArg(OPT_editor)) {
+ m_option_data.m_use_external_editor = true;
+ }
+
+ if (args.hasArg(OPT_no_lldbinit)) {
+ m_debugger.SkipLLDBInitFiles(true);
+ m_debugger.SkipAppInitFiles(true);
+ }
+
+ if (args.hasArg(OPT_local_lldbinit)) {
+ lldb::SBDebugger::SetInternalVariable("target.load-cwd-lldbinit", "true",
+ m_debugger.GetInstanceName());
+ }
+
+ if (args.hasArg(OPT_no_use_colors)) {
+ m_debugger.SetUseColor(false);
+ m_option_data.m_debug_mode = true;
+ }
+
+ if (auto *arg = args.getLastArg(OPT_file)) {
+ auto arg_value = arg->getValue();
+ SBFileSpec file(arg_value);
+ if (file.Exists()) {
+ m_option_data.m_args.emplace_back(arg_value);
+ } else if (file.ResolveExecutableLocation()) {
+ char path[PATH_MAX];
+ file.GetPath(path, sizeof(path));
+ m_option_data.m_args.emplace_back(path);
+ } else {
+ error.SetErrorStringWithFormat(
+ "file specified in --file (-f) option doesn't exist: '%s'",
+ arg_value);
+ return error;
+ }
+ }
+
+ if (auto *arg = args.getLastArg(OPT_arch)) {
+ auto arg_value = arg->getValue();
+ if (!lldb::SBDebugger::SetDefaultArchitecture(arg_value)) {
+ error.SetErrorStringWithFormat(
+ "invalid architecture in the -a or --arch option: '%s'", arg_value);
+ return error;
+ }
+ }
+
+ if (auto *arg = args.getLastArg(OPT_script_language)) {
+ auto arg_value = arg->getValue();
+ m_debugger.SetScriptLanguage(m_debugger.GetScriptingLanguage(arg_value));
+ }
+
+ if (args.hasArg(OPT_source_quietly)) {
+ m_option_data.m_source_quietly = true;
+ }
+
+ if (auto *arg = args.getLastArg(OPT_attach_name)) {
+ auto arg_value = arg->getValue();
+ m_option_data.m_process_name = arg_value;
+ }
+
+ if (args.hasArg(OPT_wait_for)) {
+ m_option_data.m_wait_for = true;
+ }
+
+ if (auto *arg = args.getLastArg(OPT_attach_pid)) {
+ auto arg_value = arg->getValue();
+ char *remainder;
+ m_option_data.m_process_pid = strtol(arg_value, &remainder, 0);
+ if (remainder == arg_value || *remainder != '\0') {
+ error.SetErrorStringWithFormat(
+ "Could not convert process PID: \"%s\" into a pid.", arg_value);
+ return error;
+ }
+ }
+
+ if (auto *arg = args.getLastArg(OPT_repl_language)) {
+ auto arg_value = arg->getValue();
+ m_option_data.m_repl_lang =
+ SBLanguageRuntime::GetLanguageTypeFromString(arg_value);
+ if (m_option_data.m_repl_lang == eLanguageTypeUnknown) {
+ error.SetErrorStringWithFormat("Unrecognized language name: \"%s\"",
+ arg_value);
+ return error;
+ }
+ }
+
+ if (args.hasArg(OPT_repl)) {
+ m_option_data.m_repl = true;
+ }
+
+ if (auto *arg = args.getLastArg(OPT_repl_)) {
+ m_option_data.m_repl = true;
+ if (auto arg_value = arg->getValue())
+ m_option_data.m_repl_options = arg_value;
+ }
+
+ // We need to process the options below together as their relative order
+ // matters.
+ for (auto *arg : args.filtered(OPT_source_on_crash, OPT_one_line_on_crash,
+ OPT_source, OPT_source_before_file,
+ OPT_one_line, OPT_one_line_before_file)) {
+ auto arg_value = arg->getValue();
+ if (arg->getOption().matches(OPT_source_on_crash)) {
+ m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterCrash,
+ true, error);
+ if (error.Fail())
+ return error;
+ }
+
+ if (arg->getOption().matches(OPT_one_line_on_crash)) {
+ m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterCrash,
+ false, error);
+ if (error.Fail())
+ return error;
+ }
+
+ if (arg->getOption().matches(OPT_source)) {
+ m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterFile,
+ true, error);
+ if (error.Fail())
+ return error;
+ }
+
+ if (arg->getOption().matches(OPT_source_before_file)) {
+ m_option_data.AddInitialCommand(arg_value, eCommandPlacementBeforeFile,
+ true, error);
+ if (error.Fail())
+ return error;
+ }
+
+ if (arg->getOption().matches(OPT_one_line)) {
+ m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterFile,
+ false, error);
+ if (error.Fail())
+ return error;
+ }
+
+ if (arg->getOption().matches(OPT_one_line_before_file)) {
+ m_option_data.AddInitialCommand(arg_value, eCommandPlacementBeforeFile,
+ false, error);
+ if (error.Fail())
+ return error;
+ }
+ }
+
+ if (m_option_data.m_process_name.empty() &&
+ m_option_data.m_process_pid == LLDB_INVALID_PROCESS_ID) {
+
+ // If the option data args array is empty that means the file was not
+ // specified with -f and we need to get it from the input args.
+ if (m_option_data.m_args.empty()) {
+ if (auto *arg = args.getLastArgNoClaim(OPT_INPUT)) {
+ m_option_data.m_args.push_back(arg->getAsString((args)));
+ }
+ }
+
+ // Any argument following -- is an argument for the inferior.
+ if (auto *arg = args.getLastArgNoClaim(OPT_REM)) {
+ for (auto value : arg->getValues())
+ m_option_data.m_args.emplace_back(value);
+ }
+ } else if (args.getLastArgNoClaim() != nullptr) {
+ WithColor::warning() << "program arguments are ignored when attaching.\n";
+ }
+
+ if (m_option_data.m_print_version) {
+ llvm::outs() << lldb::SBDebugger::GetVersionString() << '\n';
+ exiting = true;
+ return error;
+ }
+
+ if (m_option_data.m_print_python_path) {
+ SBFileSpec python_file_spec = SBHostOS::GetLLDBPythonPath();
+ if (python_file_spec.IsValid()) {
+ char python_path[PATH_MAX];
+ size_t num_chars = python_file_spec.GetPath(python_path, PATH_MAX);
+ if (num_chars < PATH_MAX) {
+ llvm::outs() << python_path << '\n';
+ } else
+ llvm::outs() << "<PATH TOO LONG>\n";
+ } else
+ llvm::outs() << "<COULD NOT FIND PATH>\n";
+ exiting = true;
+ return error;
+ }
+
+ return error;
+}
+
+static inline int OpenPipe(int fds[2], std::size_t size) {
+#ifdef _WIN32
+ return _pipe(fds, size, O_BINARY);
+#else
+ (void)size;
+ return pipe(fds);
+#endif
+}
+
+static ::FILE *PrepareCommandsForSourcing(const char *commands_data,
+ size_t commands_size) {
+ enum PIPES { READ, WRITE }; // Indexes for the read and write fds
+ int fds[2] = {-1, -1};
+
+ if (OpenPipe(fds, commands_size) != 0) {
+ WithColor::error()
+ << "can't create pipe file descriptors for LLDB commands\n";
+ return nullptr;
+ }
+
+ ssize_t nrwr = write(fds[WRITE], commands_data, commands_size);
+ if (size_t(nrwr) != commands_size) {
+ WithColor::error()
+ << format(
+ "write(%i, %p, %" PRIu64
+ ") failed (errno = %i) when trying to open LLDB commands pipe",
+ fds[WRITE], static_cast<const void *>(commands_data),
+ static_cast<uint64_t>(commands_size), errno)
+ << '\n';
+ llvm::sys::Process::SafelyCloseFileDescriptor(fds[READ]);
+ llvm::sys::Process::SafelyCloseFileDescriptor(fds[WRITE]);
+ return nullptr;
+ }
+
+ // Close the write end of the pipe, so that the command interpreter will exit
+ // when it consumes all the data.
+ llvm::sys::Process::SafelyCloseFileDescriptor(fds[WRITE]);
+
+ // Open the read file descriptor as a FILE * that we can return as an input
+ // handle.
+ ::FILE *commands_file = fdopen(fds[READ], "rb");
+ if (commands_file == nullptr) {
+ WithColor::error() << format("fdopen(%i, \"rb\") failed (errno = %i) "
+ "when trying to open LLDB commands pipe",
+ fds[READ], errno)
+ << '\n';
+ llvm::sys::Process::SafelyCloseFileDescriptor(fds[READ]);
+ return nullptr;
+ }
+
+ // 'commands_file' now owns the read descriptor.
+ return commands_file;
+}
+
+std::string EscapeString(std::string arg) {
+ std::string::size_type pos = 0;
+ while ((pos = arg.find_first_of("\"\\", pos)) != std::string::npos) {
+ arg.insert(pos, 1, '\\');
+ pos += 2;
+ }
+ return '"' + arg + '"';
+}
+
+int Driver::MainLoop() {
+ if (::tcgetattr(STDIN_FILENO, &g_old_stdin_termios) == 0) {
+ g_old_stdin_termios_is_valid = true;
+ atexit(reset_stdin_termios);
+ }
+
+#ifndef _MSC_VER
+ // Disabling stdin buffering with MSVC's 2015 CRT exposes a bug in fgets
+ // which causes it to miss newlines depending on whether there have been an
+ // odd or even number of characters. Bug has been reported to MS via Connect.
+ ::setbuf(stdin, nullptr);
+#endif
+ ::setbuf(stdout, nullptr);
+
+ m_debugger.SetErrorFileHandle(stderr, false);
+ m_debugger.SetOutputFileHandle(stdout, false);
+ // Don't take ownership of STDIN yet...
+ m_debugger.SetInputFileHandle(stdin, false);
+
+ m_debugger.SetUseExternalEditor(m_option_data.m_use_external_editor);
+
+ struct winsize window_size;
+ if ((isatty(STDIN_FILENO) != 0) &&
+ ::ioctl(STDIN_FILENO, TIOCGWINSZ, &window_size) == 0) {
+ if (window_size.ws_col > 0)
+ m_debugger.SetTerminalWidth(window_size.ws_col);
+ }
+
+ SBCommandInterpreter sb_interpreter = m_debugger.GetCommandInterpreter();
+
+ // Before we handle any options from the command line, we parse the
+ // .lldbinit file in the user's home directory.
+ SBCommandReturnObject result;
+ sb_interpreter.SourceInitFileInHomeDirectory(result);
+ if (m_option_data.m_debug_mode) {
+ result.PutError(m_debugger.GetErrorFile());
+ result.PutOutput(m_debugger.GetOutputFile());
+ }
+
+ // Source the local .lldbinit file if it exists and we're allowed to source.
+ // Here we want to always print the return object because it contains the
+ // warning and instructions to load local lldbinit files.
+ sb_interpreter.SourceInitFileInCurrentWorkingDirectory(result);
+ result.PutError(m_debugger.GetErrorFile());
+ result.PutOutput(m_debugger.GetOutputFile());
+
+ // We allow the user to specify an exit code when calling quit which we will
+ // return when exiting.
+ m_debugger.GetCommandInterpreter().AllowExitCodeOnQuit(true);
+
+ // Now we handle options we got from the command line
+ SBStream commands_stream;
+
+ // First source in the commands specified to be run before the file arguments
+ // are processed.
+ WriteCommandsForSourcing(eCommandPlacementBeforeFile, commands_stream);
+
+ // If we're not in --repl mode, add the commands to process the file
+ // arguments, and the commands specified to run afterwards.
+ if (!m_option_data.m_repl) {
+ const size_t num_args = m_option_data.m_args.size();
+ if (num_args > 0) {
+ char arch_name[64];
+ if (lldb::SBDebugger::GetDefaultArchitecture(arch_name,
+ sizeof(arch_name)))
+ commands_stream.Printf("target create --arch=%s %s", arch_name,
+ EscapeString(m_option_data.m_args[0]).c_str());
+ else
+ commands_stream.Printf("target create %s",
+ EscapeString(m_option_data.m_args[0]).c_str());
+
+ if (!m_option_data.m_core_file.empty()) {
+ commands_stream.Printf(" --core %s",
+ EscapeString(m_option_data.m_core_file).c_str());
+ }
+ commands_stream.Printf("\n");
+
+ if (num_args > 1) {
+ commands_stream.Printf("settings set -- target.run-args ");
+ for (size_t arg_idx = 1; arg_idx < num_args; ++arg_idx)
+ commands_stream.Printf(
+ " %s", EscapeString(m_option_data.m_args[arg_idx]).c_str());
+ commands_stream.Printf("\n");
+ }
+ } else if (!m_option_data.m_core_file.empty()) {
+ commands_stream.Printf("target create --core %s\n",
+ EscapeString(m_option_data.m_core_file).c_str());
+ } else if (!m_option_data.m_process_name.empty()) {
+ commands_stream.Printf(
+ "process attach --name %s",
+ EscapeString(m_option_data.m_process_name).c_str());
+
+ if (m_option_data.m_wait_for)
+ commands_stream.Printf(" --waitfor");
+
+ commands_stream.Printf("\n");
+
+ } else if (LLDB_INVALID_PROCESS_ID != m_option_data.m_process_pid) {
+ commands_stream.Printf("process attach --pid %" PRIu64 "\n",
+ m_option_data.m_process_pid);
+ }
+
+ WriteCommandsForSourcing(eCommandPlacementAfterFile, commands_stream);
+ } else if (!m_option_data.m_after_file_commands.empty()) {
+ // We're in repl mode and after-file-load commands were specified.
+ WithColor::warning() << "commands specified to run after file load (via -o "
+ "or -s) are ignored in REPL mode.\n";
+ }
+
+ if (m_option_data.m_debug_mode) {
+ result.PutError(m_debugger.GetErrorFile());
+ result.PutOutput(m_debugger.GetOutputFile());
+ }
+
+ const bool handle_events = true;
+ const bool spawn_thread = false;
+
+ // Check if we have any data in the commands stream, and if so, save it to a
+ // temp file
+ // so we can then run the command interpreter using the file contents.
+ const char *commands_data = commands_stream.GetData();
+ const size_t commands_size = commands_stream.GetSize();
+
+ // The command file might have requested that we quit, this variable will
+ // track that.
+ bool quit_requested = false;
+ bool stopped_for_crash = false;
+ if ((commands_data != nullptr) && (commands_size != 0u)) {
+ bool success = true;
+ FILE *commands_file =
+ PrepareCommandsForSourcing(commands_data, commands_size);
+ if (commands_file != nullptr) {
+ m_debugger.SetInputFileHandle(commands_file, true);
+
+ // Set the debugger into Sync mode when running the command file.
+ // Otherwise command files
+ // that run the target won't run in a sensible way.
+ bool old_async = m_debugger.GetAsync();
+ m_debugger.SetAsync(false);
+ int num_errors = 0;
+
+ SBCommandInterpreterRunOptions options;
+ options.SetStopOnError(true);
+ if (m_option_data.m_batch)
+ options.SetStopOnCrash(true);
+
+ m_debugger.RunCommandInterpreter(handle_events, spawn_thread, options,
+ num_errors, quit_requested,
+ stopped_for_crash);
+
+ if (m_option_data.m_batch && stopped_for_crash &&
+ !m_option_data.m_after_crash_commands.empty()) {
+ SBStream crash_commands_stream;
+ WriteCommandsForSourcing(eCommandPlacementAfterCrash,
+ crash_commands_stream);
+ const char *crash_commands_data = crash_commands_stream.GetData();
+ const size_t crash_commands_size = crash_commands_stream.GetSize();
+ commands_file = PrepareCommandsForSourcing(crash_commands_data,
+ crash_commands_size);
+ if (commands_file != nullptr) {
+ bool local_quit_requested;
+ bool local_stopped_for_crash;
+ m_debugger.SetInputFileHandle(commands_file, true);
+
+ m_debugger.RunCommandInterpreter(handle_events, spawn_thread, options,
+ num_errors, local_quit_requested,
+ local_stopped_for_crash);
+ if (local_quit_requested)
+ quit_requested = true;
+ }
+ }
+ m_debugger.SetAsync(old_async);
+ } else
+ success = false;
+
+ // Something went wrong with command pipe
+ if (!success) {
+ exit(1);
+ }
+ }
+
+ // Now set the input file handle to STDIN and run the command
+ // interpreter again in interactive mode or repl mode and let the debugger
+ // take ownership of stdin
+
+ bool go_interactive = true;
+ if (quit_requested)
+ go_interactive = false;
+ else if (m_option_data.m_batch && !stopped_for_crash)
+ go_interactive = false;
+
+ if (go_interactive) {
+ m_debugger.SetInputFileHandle(stdin, true);
+
+ if (m_option_data.m_repl) {
+ const char *repl_options = nullptr;
+ if (!m_option_data.m_repl_options.empty())
+ repl_options = m_option_data.m_repl_options.c_str();
+ SBError error(
+ m_debugger.RunREPL(m_option_data.m_repl_lang, repl_options));
+ if (error.Fail()) {
+ const char *error_cstr = error.GetCString();
+ if ((error_cstr != nullptr) && (error_cstr[0] != 0))
+ WithColor::error() << error_cstr << '\n';
+ else
+ WithColor::error() << error.GetError() << '\n';
+ }
+ } else {
+ m_debugger.RunCommandInterpreter(handle_events, spawn_thread);
+ }
+ }
+
+ reset_stdin_termios();
+ fclose(stdin);
+
+ int exit_code = sb_interpreter.GetQuitStatus();
+ SBDebugger::Destroy(m_debugger);
+ return exit_code;
+}
+
+void Driver::ResizeWindow(unsigned short col) {
+ GetDebugger().SetTerminalWidth(col);
+}
+
+void sigwinch_handler(int signo) {
+ struct winsize window_size;
+ if ((isatty(STDIN_FILENO) != 0) &&
+ ::ioctl(STDIN_FILENO, TIOCGWINSZ, &window_size) == 0) {
+ if ((window_size.ws_col > 0) && g_driver != nullptr) {
+ g_driver->ResizeWindow(window_size.ws_col);
+ }
+ }
+}
+
+void sigint_handler(int signo) {
+#ifdef _WIN32 // Restore handler as it is not persistent on Windows
+ signal(SIGINT, sigint_handler);
+#endif
+ static std::atomic_flag g_interrupt_sent = ATOMIC_FLAG_INIT;
+ if (g_driver != nullptr) {
+ if (!g_interrupt_sent.test_and_set()) {
+ g_driver->GetDebugger().DispatchInputInterrupt();
+ g_interrupt_sent.clear();
+ return;
+ }
+ }
+
+ _exit(signo);
+}
+
+void sigtstp_handler(int signo) {
+ if (g_driver != nullptr)
+ g_driver->GetDebugger().SaveInputTerminalState();
+
+ signal(signo, SIG_DFL);
+ kill(getpid(), signo);
+ signal(signo, sigtstp_handler);
+}
+
+void sigcont_handler(int signo) {
+ if (g_driver != nullptr)
+ g_driver->GetDebugger().RestoreInputTerminalState();
+
+ signal(signo, SIG_DFL);
+ kill(getpid(), signo);
+ signal(signo, sigcont_handler);
+}
+
+void reproducer_handler(void *argv0) {
+ if (SBReproducer::Generate()) {
+ auto exe = static_cast<const char *>(argv0);
+ llvm::outs() << "********************\n";
+ llvm::outs() << "Crash reproducer for ";
+ llvm::outs() << lldb::SBDebugger::GetVersionString() << '\n';
+ llvm::outs() << '\n';
+ llvm::outs() << "Reproducer written to '" << SBReproducer::GetPath()
+ << "'\n";
+ llvm::outs() << '\n';
+ llvm::outs() << "Before attaching the reproducer to a bug report:\n";
+ llvm::outs() << " - Look at the directory to ensure you're willing to "
+ "share its content.\n";
+ llvm::outs()
+ << " - Make sure the reproducer works by replaying the reproducer.\n";
+ llvm::outs() << '\n';
+ llvm::outs() << "Replay the reproducer with the following command:\n";
+ llvm::outs() << exe << " -replay " << SBReproducer::GetPath() << "\n";
+ llvm::outs() << "********************\n";
+ }
+}
+
+static void printHelp(LLDBOptTable &table, llvm::StringRef tool_name) {
+ std::string usage_str = tool_name.str() + " [options]";
+ table.PrintHelp(llvm::outs(), usage_str.c_str(), "LLDB", false);
+
+ std::string examples = R"___(
+EXAMPLES:
+ The debugger can be started in several modes.
+
+ Passing an executable as a positional argument prepares lldb to debug the
+ given executable. Arguments passed after -- are considered arguments to the
+ debugged executable.
+
+ lldb --arch x86_64 /path/to/program -- --arch arvm7
+
+ Passing one of the attach options causes lldb to immediately attach to the
+ given process.
+
+ lldb -p <pid>
+ lldb -n <process-name>
+
+ Passing --repl starts lldb in REPL mode.
+
+ lldb -r
+
+ Passing --core causes lldb to debug the core file.
+
+ lldb -c /path/to/core
+
+ Command options can be combined with these modes and cause lldb to run the
+ specified commands before or after events, like loading the file or crashing,
+ in the order provided on the command line.
+
+ lldb -O 'settings set stop-disassembly-count 20' -o 'run' -o 'bt'
+ lldb -S /source/before/file -s /source/after/file
+ lldb -K /source/before/crash -k /source/after/crash
+
+ Note: In REPL mode no file is loaded, so commands specified to run after
+ loading the file (via -o or -s) will be ignored.)___";
+ llvm::outs() << examples << '\n';
+}
+
+llvm::Optional<int> InitializeReproducer(opt::InputArgList &input_args) {
+ if (auto *replay_path = input_args.getLastArg(OPT_replay)) {
+ const bool skip_version_check = input_args.hasArg(OPT_skip_version_check);
+ if (const char *error =
+ SBReproducer::Replay(replay_path->getValue(), skip_version_check)) {
+ WithColor::error() << "reproducer replay failed: " << error << '\n';
+ return 1;
+ }
+ return 0;
+ }
+
+ bool capture = input_args.hasArg(OPT_capture);
+ auto *capture_path = input_args.getLastArg(OPT_capture_path);
+
+ if (capture || capture_path) {
+ if (capture_path) {
+ if (!capture)
+ WithColor::warning() << "-capture-path specified without -capture\n";
+ if (const char *error = SBReproducer::Capture(capture_path->getValue())) {
+ WithColor::error() << "reproducer capture failed: " << error << '\n';
+ return 1;
+ }
+ } else {
+ const char *error = SBReproducer::Capture();
+ if (error) {
+ WithColor::error() << "reproducer capture failed: " << error << '\n';
+ return 1;
+ }
+ }
+ }
+
+ return llvm::None;
+}
+
+int main(int argc, char const *argv[]) {
+ // Setup LLVM signal handlers and make sure we call llvm_shutdown() on
+ // destruction.
+ llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false);
+
+ // Parse arguments.
+ LLDBOptTable T;
+ unsigned MAI;
+ unsigned MAC;
+ ArrayRef<const char *> arg_arr = makeArrayRef(argv + 1, argc - 1);
+ opt::InputArgList input_args = T.ParseArgs(arg_arr, MAI, MAC);
+
+ if (input_args.hasArg(OPT_help)) {
+ printHelp(T, llvm::sys::path::filename(argv[0]));
+ return 0;
+ }
+
+ for (auto *arg : input_args.filtered(OPT_UNKNOWN)) {
+ WithColor::warning() << "ignoring unknown option: " << arg->getSpelling()
+ << '\n';
+ }
+
+ if (auto exit_code = InitializeReproducer(input_args)) {
+ return *exit_code;
+ }
+
+ // Register the reproducer signal handler.
+ llvm::sys::AddSignalHandler(reproducer_handler, const_cast<char *>(argv[0]));
+
+ SBError error = SBDebugger::InitializeWithErrorHandling();
+ if (error.Fail()) {
+ WithColor::error() << "initialization failed: " << error.GetCString()
+ << '\n';
+ return 1;
+ }
+ SBHostOS::ThreadCreated("<lldb.driver.main-thread>");
+
+ signal(SIGINT, sigint_handler);
+#if !defined(_MSC_VER)
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGWINCH, sigwinch_handler);
+ signal(SIGTSTP, sigtstp_handler);
+ signal(SIGCONT, sigcont_handler);
+#endif
+
+ int exit_code = 0;
+ // Create a scope for driver so that the driver object will destroy itself
+ // before SBDebugger::Terminate() is called.
+ {
+ Driver driver;
+
+ bool exiting = false;
+ SBError error(driver.ProcessArgs(input_args, exiting));
+ if (error.Fail()) {
+ exit_code = 1;
+ if (const char *error_cstr = error.GetCString())
+ WithColor::error() << error_cstr << '\n';
+ } else if (!exiting) {
+ exit_code = driver.MainLoop();
+ }
+ }
+
+ SBDebugger::Terminate();
+ return exit_code;
+}
diff --git a/gnu/llvm/lldb/tools/driver/Driver.h b/gnu/llvm/lldb/tools/driver/Driver.h
new file mode 100644
index 00000000000..f442458ae40
--- /dev/null
+++ b/gnu/llvm/lldb/tools/driver/Driver.h
@@ -0,0 +1,102 @@
+//===-- Driver.h ------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_Driver_h_
+#define lldb_Driver_h_
+
+#include "Platform.h"
+
+#include "lldb/API/SBBroadcaster.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBDefines.h"
+#include "lldb/API/SBError.h"
+
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+
+#include <set>
+#include <string>
+#include <vector>
+
+class Driver : public lldb::SBBroadcaster {
+public:
+ enum CommandPlacement {
+ eCommandPlacementBeforeFile,
+ eCommandPlacementAfterFile,
+ eCommandPlacementAfterCrash,
+ };
+
+ Driver();
+
+ virtual ~Driver();
+
+ /// Runs the main loop.
+ ///
+ /// \return The exit code that the process should return.
+ int MainLoop();
+
+ lldb::SBError ProcessArgs(const llvm::opt::InputArgList &args, bool &exiting);
+
+ void WriteCommandsForSourcing(CommandPlacement placement,
+ lldb::SBStream &strm);
+
+ struct OptionData {
+ void AddInitialCommand(std::string command, CommandPlacement placement,
+ bool is_file, lldb::SBError &error);
+
+ struct InitialCmdEntry {
+ InitialCmdEntry(std::string contents, bool in_is_file,
+ bool in_quiet = false)
+ : contents(std::move(contents)), is_file(in_is_file),
+ source_quietly(in_quiet) {}
+
+ std::string contents;
+ bool is_file;
+ bool source_quietly;
+ };
+
+ std::vector<std::string> m_args;
+
+ lldb::LanguageType m_repl_lang = lldb::eLanguageTypeUnknown;
+ lldb::pid_t m_process_pid = LLDB_INVALID_PROCESS_ID;
+
+ std::string m_core_file;
+ std::string m_crash_log;
+ std::string m_repl_options;
+ std::string m_process_name;
+
+ std::vector<InitialCmdEntry> m_initial_commands;
+ std::vector<InitialCmdEntry> m_after_file_commands;
+ std::vector<InitialCmdEntry> m_after_crash_commands;
+
+ bool m_debug_mode = false;
+ bool m_source_quietly = false;
+ bool m_print_version = false;
+ bool m_print_python_path = false;
+ bool m_wait_for = false;
+ bool m_repl = false;
+ bool m_batch = false;
+
+ // FIXME: When we have set/show variables we can remove this from here.
+ bool m_use_external_editor = false;
+
+ using OptionSet = std::set<char>;
+ OptionSet m_seen_options;
+ };
+
+ lldb::SBDebugger &GetDebugger() { return m_debugger; }
+
+ void ResizeWindow(unsigned short col);
+
+private:
+ lldb::SBDebugger m_debugger;
+ OptionData m_option_data;
+};
+
+#endif // lldb_Driver_h_
diff --git a/gnu/llvm/lldb/tools/driver/Options.td b/gnu/llvm/lldb/tools/driver/Options.td
new file mode 100644
index 00000000000..c237f568f64
--- /dev/null
+++ b/gnu/llvm/lldb/tools/driver/Options.td
@@ -0,0 +1,238 @@
+include "llvm/Option/OptParser.td"
+
+class F<string name>: Flag<["--", "-"], name>;
+class S<string name>: Separate<["--", "-"], name>;
+class R<list<string> prefixes, string name>
+ : Option<prefixes, name, KIND_REMAINING_ARGS>;
+
+// Please keep this in sync with the man page in docs/man/lldb.rst
+
+// Attaching options.
+def grp_attach : OptionGroup<"attaching">, HelpText<"ATTACHING">;
+
+def attach_name: Separate<["--", "-"], "attach-name">,
+ MetaVarName<"<name>">,
+ HelpText<"Tells the debugger to attach to a process with the given name.">,
+ Group<grp_attach>;
+def: Separate<["-"], "n">,
+ Alias<attach_name>,
+ HelpText<"Alias for --attach-name">,
+ Group<grp_attach>;
+
+def wait_for: F<"wait-for">,
+ HelpText<"Tells the debugger to wait for a process with the given pid or name to launch before attaching.">,
+ Group<grp_attach>;
+def: Flag<["-"], "w">,
+ Alias<wait_for>,
+ HelpText<"Alias for --wait-for">,
+ Group<grp_attach>;
+
+def attach_pid: Separate<["--", "-"], "attach-pid">,
+ MetaVarName<"<pid>">,
+ HelpText<"Tells the debugger to attach to a process with the given pid.">,
+ Group<grp_attach>;
+def: Separate<["-"], "p">,
+ Alias<attach_pid>,
+ HelpText<"Alias for --attach-pid">,
+ Group<grp_attach>;
+
+
+// Scripting options.
+def grp_scripting : OptionGroup<"scripting">, HelpText<"SCRIPTING">;
+
+def python_path: F<"python-path">,
+ HelpText<"Prints out the path to the lldb.py file for this version of lldb.">,
+ Group<grp_scripting>;
+def: Flag<["-"], "P">,
+ Alias<python_path>,
+ HelpText<"Alias for --python-path">,
+ Group<grp_scripting>;
+
+def script_language: Separate<["--", "-"], "script-language">,
+ MetaVarName<"<language>">,
+ HelpText<"Tells the debugger to use the specified scripting language for user-defined scripts.">,
+ Group<grp_scripting>;
+def: Separate<["-"], "l">,
+ Alias<script_language>,
+ HelpText<"Alias for --script-language">,
+ Group<grp_scripting>;
+
+// Repl options.
+def grp_repl : OptionGroup<"repl">, HelpText<"REPL">;
+
+def repl: Flag<["--", "-"], "repl">,
+ HelpText<"Runs lldb in REPL mode with a stub process.">,
+ Group<grp_repl>;
+def: Flag<["-"], "r">,
+ Alias<repl>,
+ HelpText<"Alias for --repl">,
+ Group<grp_repl>;
+def repl_: Joined<["--", "-"], "repl=">,
+ MetaVarName<"<flags>">,
+ HelpText<"Runs lldb in REPL mode with a stub process with the given flags.">,
+ Group<grp_repl>;
+def: Joined<["-"], "r=">,
+ MetaVarName<"<flags>">,
+ Alias<repl_>,
+ HelpText<"Alias for --repl=<flags>">,
+ Group<grp_repl>;
+
+def repl_language: Separate<["--", "-"], "repl-language">,
+ MetaVarName<"<language>">,
+ HelpText<"Chooses the language for the REPL.">,
+ Group<grp_repl>;
+def: Separate<["-"], "R">,
+ Alias<repl_language>,
+ HelpText<"Alias for --repl-language">,
+ Group<grp_repl>;
+
+
+// Command options.
+def grp_command : OptionGroup<"command">, HelpText<"COMMANDS">;
+
+def no_lldbinit: F<"no-lldbinit">,
+ HelpText<"Do not automatically parse any '.lldbinit' files.">,
+ Group<grp_command>;
+def: Flag<["-"], "x">,
+ Alias<no_lldbinit>,
+ HelpText<"Alias for --no-lldbinit">,
+ Group<grp_command>;
+def local_lldbinit: F<"local-lldbinit">,
+ HelpText<"Allow the debugger to parse the .lldbinit files in the current working directory, unless --no-lldbinit is passed.">,
+ Group<grp_command>;
+
+def batch: F<"batch">,
+ HelpText<"Tells the debugger to run the commands from -s, -S, -o & -O, and then quit.">,
+ Group<grp_command>;
+def: Flag<["-"], "b">,
+ Alias<batch>,
+ HelpText<"Alias for --batch">,
+ Group<grp_command>;
+
+def source_quietly: F<"source-quietly">,
+ HelpText<"Tells the debugger to execute this one-line lldb command before any file has been loaded.">,
+ Group<grp_command>;
+def: Flag<["-"], "Q">,
+ Alias<source_quietly>,
+ HelpText<"Alias for --source-quietly">,
+ Group<grp_command>;
+
+def one_line_on_crash: Separate<["--", "-"], "one-line-on-crash">,
+ MetaVarName<"<command>">,
+ HelpText<"When in batch mode, tells the debugger to run this one-line lldb command if the target crashes.">,
+ Group<grp_command>;
+def: Separate<["-"], "k">,
+ Alias<one_line_on_crash>,
+ HelpText<"Alias for --one-line-on-crash">,
+ Group<grp_command>;
+
+def source_on_crash: Separate<["--", "-"], "source-on-crash">,
+ MetaVarName<"<file>">,
+ HelpText<"When in batch mode, tells the debugger to source this file of lldb commands if the target crashes.">,
+ Group<grp_command>;
+def: Separate<["-"], "K">,
+ Alias<source_on_crash>,
+ HelpText<"Alias for --source-on-crash">,
+ Group<grp_command>;
+
+def source: Separate<["--", "-"], "source">,
+ MetaVarName<"<file>">,
+ HelpText<"Tells the debugger to read in and execute the lldb commands in the given file, after any file has been loaded.">,
+ Group<grp_command>;
+def: Separate<["-"], "s">,
+ Alias<source>,
+ HelpText<"Alias for --source">,
+ Group<grp_command>;
+
+def source_before_file: Separate<["--", "-"], "source-before-file">,
+ MetaVarName<"<file>">,
+ HelpText<"Tells the debugger to read in and execute the lldb commands in the given file, before any file has been loaded.">,
+ Group<grp_command>;
+def: Separate<["-"], "S">,
+ Alias<source_before_file>,
+ HelpText<"Alias for --source-before-file">,
+ Group<grp_command>;
+
+def one_line: Separate<["--", "-"], "one-line">,
+ MetaVarName<"<command>">,
+ HelpText<"Tells the debugger to execute this one-line lldb command after any file provided on the command line has been loaded.">,
+ Group<grp_command>;
+def: Separate<["-"], "o">,
+ Alias<one_line>,
+ HelpText<"Alias for --one-line">,
+ Group<grp_command>;
+
+def one_line_before_file: Separate<["--", "-"], "one-line-before-file">,
+ MetaVarName<"<command>">,
+ HelpText<"Tells the debugger to execute this one-line lldb command before any file provided on the command line has been loaded.">,
+ Group<grp_command>;
+def: Separate<["-"], "O">,
+ Alias<one_line_before_file>,
+ HelpText<"Alias for --one-line-before-file">,
+ Group<grp_command>;
+
+
+// General options.
+def version: F<"version">,
+ HelpText<"Prints out the current version number of the LLDB debugger.">;
+def: Flag<["-"], "v">,
+ Alias<version>,
+ HelpText<"Alias for --version">;
+
+def help: F<"help">,
+ HelpText<"Prints out the usage information for the LLDB debugger.">;
+def: Flag<["-"], "h">,
+ Alias<help>,
+ HelpText<"Alias for --help">;
+
+def core: Separate<["--", "-"], "core">,
+ MetaVarName<"<filename>">,
+ HelpText<"Tells the debugger to use the full path to <filename> as the core file.">;
+def: Separate<["-"], "c">,
+ Alias<core>,
+ HelpText<"Alias for --core">;
+
+def editor: F<"editor">,
+ HelpText<"Tells the debugger to open source files using the host's \"external editor\" mechanism.">;
+def: Flag<["-"], "e">,
+ Alias<editor>,
+ HelpText<"Alias for --editor">;
+
+def no_use_colors: F<"no-use-colors">,
+ HelpText<"Do not use colors.">;
+def: Flag<["-"], "X">,
+ Alias<no_use_colors>,
+ HelpText<"Alias for --no-use-color">;
+
+def file: Separate<["--", "-"], "file">,
+ MetaVarName<"<filename>">,
+ HelpText<"Tells the debugger to use the file <filename> as the program to be debugged.">;
+def: Separate<["-"], "f">,
+ Alias<file>,
+ HelpText<"Alias for --file">;
+
+def arch: Separate<["--", "-"], "arch">,
+ MetaVarName<"<architecture>">,
+ HelpText<"Tells the debugger to use the specified architecture when starting and running the program.">;
+def: Separate<["-"], "a">,
+ Alias<arch>,
+ HelpText<"Alias for --arch">;
+
+def debug: F<"debug">,
+ HelpText<"Tells the debugger to print out extra information for debugging itself.">;
+def: Flag<["-"], "d">,
+ Alias<debug>,
+ HelpText<"Alias for --debug">;
+
+def capture: F<"capture">,
+ HelpText<"Tells the debugger to capture a reproducer.">;
+def capture_path: Separate<["--", "-"], "capture-path">,
+ MetaVarName<"<filename>">,
+ HelpText<"Tells the debugger to use the given filename for the reproducer.">;
+def replay: Separate<["--", "-"], "replay">,
+ MetaVarName<"<filename>">,
+ HelpText<"Tells the debugger to replay a reproducer from <filename>.">;
+def skip_version_check: F<"reproducer-skip-version-check">,
+ HelpText<"Skip the reproducer version check.">;
+
+def REM : R<["--"], "">;
diff --git a/gnu/llvm/lldb/tools/driver/Platform.cpp b/gnu/llvm/lldb/tools/driver/Platform.cpp
new file mode 100644
index 00000000000..f3a71eb79de
--- /dev/null
+++ b/gnu/llvm/lldb/tools/driver/Platform.cpp
@@ -0,0 +1,59 @@
+//===-- Platform.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// this file is only relevant for Visual C++
+#if defined(_WIN32)
+
+#include <assert.h>
+#include <process.h>
+#include <stdlib.h>
+
+#include "Platform.h"
+#include "llvm/Support/ErrorHandling.h"
+
+int ioctl(int d, int request, ...) {
+ switch (request) {
+ // request the console windows size
+ case (TIOCGWINSZ): {
+ va_list vl;
+ va_start(vl, request);
+ // locate the window size structure on stack
+ winsize *ws = va_arg(vl, winsize *);
+ // get screen buffer information
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info) ==
+ TRUE)
+ // fill in the columns
+ ws->ws_col = info.dwMaximumWindowSize.X;
+ va_end(vl);
+ return 0;
+ } break;
+ default:
+ llvm_unreachable("Not implemented!");
+ }
+}
+
+int kill(pid_t pid, int sig) {
+ // is the app trying to kill itself
+ if (pid == getpid())
+ exit(sig);
+ //
+ llvm_unreachable("Not implemented!");
+}
+
+int tcsetattr(int fd, int optional_actions, const struct termios *termios_p) {
+ llvm_unreachable("Not implemented!");
+}
+
+int tcgetattr(int fildes, struct termios *termios_p) {
+ // assert( !"Not implemented!" );
+ // error return value (0=success)
+ return -1;
+}
+
+#endif
diff --git a/gnu/llvm/lldb/tools/driver/Platform.h b/gnu/llvm/lldb/tools/driver/Platform.h
new file mode 100644
index 00000000000..cf6c4ec8e14
--- /dev/null
+++ b/gnu/llvm/lldb/tools/driver/Platform.h
@@ -0,0 +1,90 @@
+//===-- Platform.h ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_Platform_h_
+#define lldb_Platform_h_
+
+#include "lldb/Host/Config.h"
+
+#if defined(_WIN32)
+
+#include <io.h>
+#if defined(_MSC_VER)
+#include <signal.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include "lldb/Host/windows/windows.h"
+#include <inttypes.h>
+
+struct winsize {
+ long ws_col;
+};
+
+typedef unsigned char cc_t;
+typedef unsigned int speed_t;
+typedef unsigned int tcflag_t;
+
+// fcntl.h
+#define O_NOCTTY 0400
+
+// ioctls.h
+#define TIOCGWINSZ 0x5413
+
+// signal.h
+#define SIGPIPE 13
+#define SIGCONT 18
+#define SIGTSTP 20
+#define SIGWINCH 28
+
+// tcsetattr arguments
+#define TCSANOW 0
+
+#define NCCS 32
+struct termios {
+ tcflag_t c_iflag; // input mode flags
+ tcflag_t c_oflag; // output mode flags
+ tcflag_t c_cflag; // control mode flags
+ tcflag_t c_lflag; // local mode flags
+ cc_t c_line; // line discipline
+ cc_t c_cc[NCCS]; // control characters
+ speed_t c_ispeed; // input speed
+ speed_t c_ospeed; // output speed
+};
+
+#ifdef _MSC_VER
+struct timeval {
+ long tv_sec;
+ long tv_usec;
+};
+typedef long pid_t;
+#define PATH_MAX MAX_PATH
+#endif
+
+#define STDIN_FILENO 0
+
+extern int ioctl(int d, int request, ...);
+extern int kill(pid_t pid, int sig);
+extern int tcsetattr(int fd, int optional_actions,
+ const struct termios *termios_p);
+extern int tcgetattr(int fildes, struct termios *termios_p);
+
+#else
+#include <inttypes.h>
+
+#include <libgen.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <pthread.h>
+#include <sys/time.h>
+#endif
+
+#endif // lldb_Platform_h_
diff --git a/gnu/llvm/lldb/tools/driver/lldb-Info.plist.in b/gnu/llvm/lldb/tools/driver/lldb-Info.plist.in
new file mode 100644
index 00000000000..a875129ef29
--- /dev/null
+++ b/gnu/llvm/lldb/tools/driver/lldb-Info.plist.in
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.apple.lldb</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>lldb</string>
+ <key>CFBundleVersion</key>
+ <string>${LLDB_VERSION}</string>
+ <key>SecTaskAccess</key>
+ <array>
+ <string>allowed</string>
+ <string>debug</string>
+ </array>
+</dict>
+</plist>
diff --git a/gnu/llvm/lldb/tools/intel-features/CMakeLists.txt b/gnu/llvm/lldb/tools/intel-features/CMakeLists.txt
new file mode 100644
index 00000000000..aff75d7db33
--- /dev/null
+++ b/gnu/llvm/lldb/tools/intel-features/CMakeLists.txt
@@ -0,0 +1,67 @@
+# Flags to control each individual feature
+option(LLDB_BUILD_INTEL_MPX "Enable Building of Intel(R) Memory Protection Extensions" ON)
+option(LLDB_BUILD_INTEL_PT "Enable Building of Intel(R) Processor Trace Tool" OFF)
+
+# Return if all features are OFF
+if (NOT LLDB_BUILD_INTEL_MPX AND NOT LLDB_BUILD_INTEL_PT)
+ return()
+endif()
+
+LIST (APPEND FEATURE_LIBS "")
+
+# Add feature specific subdirectories based on flags
+if (LLDB_BUILD_INTEL_MPX AND CMAKE_SYSTEM_NAME MATCHES "Linux")
+ add_subdirectory(intel-mpx)
+ LIST (APPEND FEATURE_LIBS ${FEATURE_LIBS} lldbIntelMPX)
+ SET (CLI_WRAPPER_PREPROCESSORS "${CLI_WRAPPER_PREPROCESSORS} -DBUILD_INTEL_MPX")
+endif()
+
+if (LLDB_BUILD_INTEL_PT)
+ add_subdirectory(intel-pt)
+ LIST (APPEND FEATURE_LIBS ${FEATURE_LIBS} lldbIntelPT)
+ SET (CLI_WRAPPER_PREPROCESSORS "${CLI_WRAPPER_PREPROCESSORS} -DBUILD_INTEL_PT")
+endif()
+
+# Add python wrapper if python not disabled
+if (LLDB_ENABLE_PYTHON AND LLDB_BUILD_INTEL_PT)
+ set(LLDB_INTEL_FEATURES_PYTHON_WRAP
+ ${LLDB_BINARY_DIR}/tools/intel-features/scripts/IntelFeaturesPythonWrap.cpp)
+ set_source_files_properties(${LLDB_INTEL_FEATURES_PYTHON_WRAP}
+ PROPERTIES GENERATED 1)
+
+ if (CLANG_CL)
+ set_source_files_properties(${LLDB_INTEL_FEATURES_PYTHON_WRAP}
+ PROPERTIES COMPILE_FLAGS -Wno-unused-function)
+ endif()
+
+ if (LLVM_COMPILER_IS_GCC_COMPATIBLE AND
+ NOT "${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")
+ set_property(SOURCE ${LLDB_INTEL_FEATURES_PYTHON_WRAP}
+ APPEND_STRING PROPERTY COMPILE_FLAGS
+ " -Wno-sequence-point -Wno-cast-qual")
+ endif ()
+ add_subdirectory(scripts)
+endif()
+
+if (NOT CLI_WRAPPER_PREPROCESSORS)
+ return()
+endif()
+
+set_source_files_properties(cli-wrapper.cpp PROPERTIES
+ COMPILE_FLAGS ${CLI_WRAPPER_PREPROCESSORS})
+
+add_lldb_library(lldbIntelFeatures SHARED
+ cli-wrapper.cpp
+ ${LLDB_INTEL_FEATURES_PYTHON_WRAP}
+
+ LINK_LIBS
+ ${FEATURE_LIBS}
+ )
+
+# Add link dependencies for python wrapper
+if (LLDB_ENABLE_PYTHON AND LLDB_BUILD_INTEL_PT)
+ add_dependencies(lldbIntelFeatures intel-features-swig_wrapper)
+endif()
+
+install(TARGETS lldbIntelFeatures
+ LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX})
diff --git a/gnu/llvm/lldb/tools/intel-features/README.txt b/gnu/llvm/lldb/tools/intel-features/README.txt
new file mode 100644
index 00000000000..d46bf1392dc
--- /dev/null
+++ b/gnu/llvm/lldb/tools/intel-features/README.txt
@@ -0,0 +1,73 @@
+****************************************************************************
+* README *
+* *
+* This file provides all the information regarding new CLI commands that *
+* enable using various hardware features of Intel(R) architecture based *
+* processors from LLDB's CLI. *
+****************************************************************************
+
+
+============
+Introduction
+============
+A shared library has been developed to use various hardware features of
+Intel(R) architecture based processors through LLDB's command line. The library
+currently comprises of hardware features namely Intel(R) Processor Trace and
+Intel(R) Memory Protection Extensions.
+
+
+============
+Details
+============
+A C++ based cli wrapper (cli-wrapper.cpp) has been developed here that
+agglomerates all cli commands for various hardware features. This wrapper is
+build to generate a shared library (lldbIntelFeatures) to provide all these
+commands.
+
+For each hardware feature, separate cli commands have been developed that are
+provided by wrappers (cli-wrapper-pt.cpp and cli-wrapper-mpxtable.cpp) residing
+in feature specific folders ("intel-pt" and "intel-mpx" respectively).
+
+For details regarding cli commands of each feature, please refer to these
+feature specific wrappers.
+
+
+
+============
+How to Build
+============
+The shared library (lldbIntelFeatures) has a cmake based build and can be built
+while building LLDB with cmake. "cli-wrapper.cpp" file is compiled along with all
+the feature specific source files (residing in feature specific folders).
+
+Furthermore, flexibility is provided to the user to include/exclude a particular
+feature while building lldbIntelFeatures library. This is done by flags described
+below:
+
+ - LLDB_BUILD_INTEL_PT - The flag enables building of Intel(R) Processor Trace
+ feature (inside intel-pt folder). This flag defaults to "OFF" meaning the
+ feature is excluded while building lldbIntelFeatures library. Set it to "ON"
+ in order to include it.
+
+ - LLDB_BUILD_INTEL_MPX - Enables building Intel(R) Memory Protection Extensions
+ feature (inside intel-mpx folder). This flag defaults to "ON" meaning
+ the feature is excluded while building lldbIntelFeatures library.
+
+Please refer to README files in feature specific folders to know about additional
+flags that need to be set in order to build that feature successfully.
+
+
+============
+How to Use
+============
+All CLI commands provided by this shared library can be used through the LLDB's
+CLI by executing "plugin load <shared_lib_name>" on LLDB CLI. shared_lib_name here
+is lldbIntelFeatures
+
+
+
+============
+Description
+============
+Please refer to README_CLI file of each feature to know about details of CLI
+commands.
diff --git a/gnu/llvm/lldb/tools/intel-features/cli-wrapper.cpp b/gnu/llvm/lldb/tools/intel-features/cli-wrapper.cpp
new file mode 100644
index 00000000000..f04e39a3be9
--- /dev/null
+++ b/gnu/llvm/lldb/tools/intel-features/cli-wrapper.cpp
@@ -0,0 +1,42 @@
+//===-- cli-wrapper.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// CLI Wrapper for hardware features of Intel(R) architecture based processors
+// to enable them to be used through LLDB's CLI. For details, please refer to
+// cli wrappers of each individual feature, residing in their respective
+// folders.
+//
+// Compile this into a shared lib and load by placing at appropriate locations
+// on disk or by using "plugin load" command at the LLDB command line.
+//
+//===----------------------------------------------------------------------===//
+
+#ifdef BUILD_INTEL_MPX
+#include "intel-mpx/cli-wrapper-mpxtable.h"
+#endif
+
+#ifdef BUILD_INTEL_PT
+#include "intel-pt/cli-wrapper-pt.h"
+#endif
+
+#include "lldb/API/SBDebugger.h"
+
+namespace lldb {
+bool PluginInitialize(lldb::SBDebugger debugger);
+}
+
+bool lldb::PluginInitialize(lldb::SBDebugger debugger) {
+
+#ifdef BUILD_INTEL_PT
+ PTPluginInitialize(debugger);
+#endif
+
+#ifdef BUILD_INTEL_MPX
+ MPXPluginInitialize(debugger);
+#endif
+
+ return true;
+}
diff --git a/gnu/llvm/lldb/tools/intel-features/intel-mpx/CMakeLists.txt b/gnu/llvm/lldb/tools/intel-features/intel-mpx/CMakeLists.txt
new file mode 100644
index 00000000000..319f712b82b
--- /dev/null
+++ b/gnu/llvm/lldb/tools/intel-features/intel-mpx/CMakeLists.txt
@@ -0,0 +1,9 @@
+add_lldb_library(lldbIntelMPX
+ cli-wrapper-mpxtable.cpp
+
+ LINK_LIBS
+ liblldb
+
+ LINK_COMPONENTS
+ Support
+ )
diff --git a/gnu/llvm/lldb/tools/intel-features/intel-mpx/cli-wrapper-mpxtable.cpp b/gnu/llvm/lldb/tools/intel-features/intel-mpx/cli-wrapper-mpxtable.cpp
new file mode 100644
index 00000000000..5bffd27409e
--- /dev/null
+++ b/gnu/llvm/lldb/tools/intel-features/intel-mpx/cli-wrapper-mpxtable.cpp
@@ -0,0 +1,422 @@
+//===-- cli-wrapper-mpxtable.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// C++ includes
+#include <cerrno>
+#include <string>
+
+#include "cli-wrapper-mpxtable.h"
+#include "lldb/API/SBCommandInterpreter.h"
+#include "lldb/API/SBCommandReturnObject.h"
+#include "lldb/API/SBMemoryRegionInfo.h"
+#include "lldb/API/SBProcess.h"
+#include "lldb/API/SBTarget.h"
+#include "lldb/API/SBThread.h"
+
+#include "llvm/ADT/Triple.h"
+
+static bool GetPtr(char *cptr, uint64_t &ptr, lldb::SBFrame &frame,
+ lldb::SBCommandReturnObject &result) {
+ if (!cptr) {
+ result.SetError("Bad argument.");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+
+ lldb::SBValue ptr_addr = frame.GetValueForVariablePath(cptr);
+ if (!ptr_addr.IsValid()) {
+ result.SetError("Invalid pointer.");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+ ptr = ptr_addr.GetLoadAddress();
+ return true;
+}
+
+enum {
+ mpx_base_mask_64 = ~(uint64_t)0xFFFULL,
+ mpx_bd_mask_64 = 0xFFFFFFF00000ULL,
+ bd_r_shift_64 = 20,
+ bd_l_shift_64 = 3,
+ bt_r_shift_64 = 3,
+ bt_l_shift_64 = 5,
+ bt_mask_64 = 0x0000000FFFF8ULL,
+
+ mpx_base_mask_32 = 0xFFFFFFFFFFFFF000ULL,
+ mpx_bd_mask_32 = 0xFFFFF000ULL,
+ bd_r_shift_32 = 12,
+ bd_l_shift_32 = 2,
+ bt_r_shift_32 = 2,
+ bt_l_shift_32 = 4,
+ bt_mask_32 = 0x00000FFCULL,
+};
+
+static void PrintBTEntry(lldb::addr_t lbound, lldb::addr_t ubound,
+ uint64_t value, uint64_t meta,
+ lldb::SBCommandReturnObject &result) {
+ const lldb::addr_t one_cmpl64 = ~((lldb::addr_t)0);
+ const lldb::addr_t one_cmpl32 = ~((uint32_t)0);
+
+ if ((lbound == one_cmpl64 || one_cmpl32) && ubound == 0) {
+ result.Printf("Null bounds on map: pointer value = 0x%lx\n", value);
+ } else {
+ result.Printf(" lbound = 0x%lx,", lbound);
+ result.Printf(" ubound = 0x%lx", ubound);
+ result.Printf(" (pointer value = 0x%lx,", value);
+ result.Printf(" metadata = 0x%lx)\n", meta);
+ }
+}
+
+static bool GetBTEntryAddr(uint64_t bndcfgu, uint64_t ptr,
+ lldb::SBTarget &target, llvm::Triple::ArchType arch,
+ size_t &size, lldb::addr_t &bt_entry_addr,
+ lldb::SBCommandReturnObject &result,
+ lldb::SBError &error) {
+ lldb::addr_t mpx_base_mask;
+ lldb::addr_t mpx_bd_mask;
+ lldb::addr_t bd_r_shift;
+ lldb::addr_t bd_l_shift;
+ lldb::addr_t bt_r_shift;
+ lldb::addr_t bt_l_shift;
+ lldb::addr_t bt_mask;
+
+ if (arch == llvm::Triple::ArchType::x86_64) {
+ mpx_base_mask = mpx_base_mask_64;
+ mpx_bd_mask = mpx_bd_mask_64;
+ bd_r_shift = bd_r_shift_64;
+ bd_l_shift = bd_l_shift_64;
+ bt_r_shift = bt_r_shift_64;
+ bt_l_shift = bt_l_shift_64;
+ bt_mask = bt_mask_64;
+ } else if (arch == llvm::Triple::ArchType::x86) {
+ mpx_base_mask = mpx_base_mask_32;
+ mpx_bd_mask = mpx_bd_mask_32;
+ bd_r_shift = bd_r_shift_32;
+ bd_l_shift = bd_l_shift_32;
+ bt_r_shift = bt_r_shift_32;
+ bt_l_shift = bt_l_shift_32;
+ bt_mask = bt_mask_32;
+ } else {
+ result.SetError("Invalid arch.");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+
+ size = target.GetAddressByteSize();
+ lldb::addr_t mpx_bd_base = bndcfgu & mpx_base_mask;
+ lldb::addr_t bd_entry_offset = ((ptr & mpx_bd_mask) >> bd_r_shift)
+ << bd_l_shift;
+ lldb::addr_t bd_entry_addr = mpx_bd_base + bd_entry_offset;
+
+ std::vector<uint8_t> bd_entry_v(size);
+ size_t ret = target.GetProcess().ReadMemory(
+ bd_entry_addr, static_cast<void *>(bd_entry_v.data()), size, error);
+ if (ret != size || !error.Success()) {
+ result.SetError("Failed access to BD entry.");
+ return false;
+ }
+
+ lldb::SBData data;
+ data.SetData(error, bd_entry_v.data(), bd_entry_v.size(),
+ target.GetByteOrder(), size);
+ lldb::addr_t bd_entry = data.GetAddress(error, 0);
+
+ if (!error.Success()) {
+ result.SetError("Failed access to BD entry.");
+ return false;
+ }
+
+ if ((bd_entry & 0x01) == 0) {
+ result.SetError("Invalid bound directory.");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+
+ // Clear status bit.
+ //
+ bd_entry--;
+
+ lldb::addr_t bt_addr = bd_entry & ~bt_r_shift;
+ lldb::addr_t bt_entry_offset = ((ptr & bt_mask) >> bt_r_shift) << bt_l_shift;
+ bt_entry_addr = bt_addr + bt_entry_offset;
+
+ return true;
+}
+
+static bool GetBTEntry(uint64_t bndcfgu, uint64_t ptr, lldb::SBTarget &target,
+ llvm::Triple::ArchType arch,
+ lldb::SBCommandReturnObject &result,
+ lldb::SBError &error) {
+ lldb::addr_t bt_entry_addr;
+ size_t size;
+ if (!GetBTEntryAddr(bndcfgu, ptr, target, arch, size, bt_entry_addr, result,
+ error))
+ return false;
+
+ // bt_entry_v must have space to store the 4 elements of the BT entry (lower
+ // boundary,
+ // upper boundary, pointer value and meta data), which all have the same size
+ // 'size'.
+ //
+ std::vector<uint8_t> bt_entry_v(size * 4);
+ size_t ret = target.GetProcess().ReadMemory(
+ bt_entry_addr, static_cast<void *>(bt_entry_v.data()), size * 4, error);
+
+ if ((ret != (size * 4)) || !error.Success()) {
+ result.SetError("Unsuccessful. Failed access to BT entry.");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+
+ lldb::addr_t lbound;
+ lldb::addr_t ubound;
+ uint64_t value;
+ uint64_t meta;
+ lldb::SBData data;
+ data.SetData(error, bt_entry_v.data(), bt_entry_v.size(),
+ target.GetByteOrder(), size);
+ lbound = data.GetAddress(error, size * 0);
+ ubound = data.GetAddress(error, size * 1);
+ value = data.GetAddress(error, size * 2);
+ meta = data.GetAddress(error, size * 3);
+ // ubound is stored as one's complement.
+ if (arch == llvm::Triple::ArchType::x86) {
+ ubound = (~ubound) & 0x00000000FFFFFFFF;
+ } else {
+ ubound = ~ubound;
+ }
+
+ if (!error.Success()) {
+ result.SetError("Failed access to BT entry.");
+ return false;
+ }
+
+ PrintBTEntry(lbound, ubound, value, meta, result);
+
+ result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
+ return true;
+}
+
+static std::vector<uint8_t> uIntToU8(uint64_t input, size_t size) {
+ std::vector<uint8_t> output;
+ for (size_t i = 0; i < size; i++)
+ output.push_back(
+ static_cast<uint8_t>((input & (0xFFULL << (i * 8))) >> (i * 8)));
+
+ return output;
+}
+
+static bool SetBTEntry(uint64_t bndcfgu, uint64_t ptr, lldb::addr_t lbound,
+ lldb::addr_t ubound, lldb::SBTarget &target,
+ llvm::Triple::ArchType arch,
+ lldb::SBCommandReturnObject &result,
+ lldb::SBError &error) {
+ lldb::addr_t bt_entry_addr;
+ size_t size;
+
+ if (!GetBTEntryAddr(bndcfgu, ptr, target, arch, size, bt_entry_addr, result,
+ error))
+ return false;
+
+ // bt_entry_v must have space to store only 2 elements of the BT Entry, the
+ // lower boundary and the upper boundary, which both have size 'size'.
+ //
+ std::vector<uint8_t> bt_entry_v(size * 2);
+
+ std::vector<uint8_t> lbound_v = uIntToU8(lbound, size);
+ bt_entry_v.insert(bt_entry_v.begin(), lbound_v.begin(), lbound_v.end());
+ std::vector<uint8_t> ubound_v = uIntToU8(~ubound, size);
+ bt_entry_v.insert(bt_entry_v.begin() + size, ubound_v.begin(),
+ ubound_v.end());
+
+ size_t ret = target.GetProcess().WriteMemory(
+ bt_entry_addr, (void *)(bt_entry_v.data()), size * 2, error);
+ if ((ret != (size * 2)) || !error.Success()) {
+ result.SetError("Failed access to BT entry.");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+
+ result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
+ return true;
+}
+
+static bool GetInitInfo(lldb::SBDebugger debugger, lldb::SBTarget &target,
+ llvm::Triple::ArchType &arch, uint64_t &bndcfgu,
+ char *arg, uint64_t &ptr,
+ lldb::SBCommandReturnObject &result,
+ lldb::SBError &error) {
+ target = debugger.GetSelectedTarget();
+ if (!target.IsValid()) {
+ result.SetError("Invalid target.");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+
+ const std::string triple_s(target.GetTriple());
+ const llvm::Triple triple(triple_s);
+
+ arch = triple.getArch();
+
+ if ((arch != llvm::Triple::ArchType::x86) &&
+ (arch != llvm::Triple::ArchType::x86_64)) {
+ result.SetError("Platform not supported.");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+
+ lldb::SBFrame frame =
+ target.GetProcess().GetSelectedThread().GetSelectedFrame();
+ if (!frame.IsValid()) {
+ result.SetError("No valid process, thread or frame.");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+
+ lldb::SBValue bndcfgu_val = frame.FindRegister("bndcfgu");
+ if (!bndcfgu_val.IsValid()) {
+ result.SetError("Cannot access register BNDCFGU. Does the target support "
+ "Intel(R) Memory Protection Extensions (Intel(R) MPX)?");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+
+ lldb::SBData bndcfgu_data = bndcfgu_val.GetData();
+ bndcfgu = bndcfgu_data.GetUnsignedInt64(error, 0);
+ if (!error.Success()) {
+ result.SetError(error, "Invalid read of register BNDCFGU.");
+ return false;
+ }
+
+ if (!GetPtr(arg, ptr, frame, result))
+ return false;
+
+ return true;
+}
+
+class MPXTableShow : public lldb::SBCommandPluginInterface {
+public:
+ bool DoExecute(lldb::SBDebugger debugger, char **command,
+ lldb::SBCommandReturnObject &result) override {
+
+ if (command) {
+ int arg_c = 0;
+ char *arg;
+
+ while (*command) {
+ if (arg_c >= 1) {
+ result.SetError("Too many arguments. See help.");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+ arg_c++;
+ arg = *command;
+ command++;
+ }
+
+ if (!debugger.IsValid()) {
+ result.SetError("Invalid debugger.");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+
+ lldb::SBTarget target;
+ llvm::Triple::ArchType arch;
+ lldb::SBError error;
+ uint64_t bndcfgu;
+ uint64_t ptr;
+
+ if (!GetInitInfo(debugger, target, arch, bndcfgu, arg, ptr, result,
+ error))
+ return false;
+
+ return GetBTEntry(bndcfgu, ptr, target, arch, result, error);
+ }
+
+ result.SetError("Too few arguments. See help.");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+};
+
+class MPXTableSet : public lldb::SBCommandPluginInterface {
+public:
+ bool DoExecute(lldb::SBDebugger debugger, char **command,
+ lldb::SBCommandReturnObject &result) override {
+
+ if (command) {
+ int arg_c = 0;
+ char *arg[3];
+
+ while (*command) {
+ arg[arg_c] = *command;
+ command++;
+ arg_c++;
+ }
+
+ if (arg_c != 3) {
+ result.SetError("Wrong arguments. See help.");
+ return false;
+ }
+
+ if (!debugger.IsValid()) {
+ result.SetError("Invalid debugger.");
+ return false;
+ }
+
+ lldb::SBTarget target;
+ llvm::Triple::ArchType arch;
+ lldb::SBError error;
+ uint64_t bndcfgu;
+ uint64_t ptr;
+
+ if (!GetInitInfo(debugger, target, arch, bndcfgu, arg[0], ptr, result,
+ error))
+ return false;
+
+ char *endptr;
+ errno = 0;
+ uint64_t lbound = std::strtoul(arg[1], &endptr, 16);
+ if (endptr == arg[1] || errno == ERANGE) {
+ result.SetError("Lower Bound: bad argument format.");
+ errno = 0;
+ return false;
+ }
+
+ uint64_t ubound = std::strtoul(arg[2], &endptr, 16);
+ if (endptr == arg[1] || errno == ERANGE) {
+ result.SetError("Upper Bound: bad argument format.");
+ errno = 0;
+ return false;
+ }
+
+ return SetBTEntry(bndcfgu, ptr, lbound, ubound, target, arch, result,
+ error);
+ }
+
+ result.SetError("Too few arguments. See help.");
+ return false;
+ }
+};
+
+bool MPXPluginInitialize(lldb::SBDebugger &debugger) {
+ lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter();
+ lldb::SBCommand mpxTable = interpreter.AddMultiwordCommand(
+ "mpx-table", "A utility to access the Intel(R) MPX table entries.");
+
+ const char *mpx_show_help = "Show the Intel(R) MPX table entry of a pointer."
+ "\nmpx-table show <pointer>";
+ mpxTable.AddCommand("show", new MPXTableShow(), mpx_show_help);
+
+ const char *mpx_set_help =
+ "Set the Intel(R) MPX table entry of a pointer.\n"
+ "mpx-table set <pointer> <lower bound> <upper bound>";
+ mpxTable.AddCommand("set", new MPXTableSet(), mpx_set_help);
+
+ return true;
+}
diff --git a/gnu/llvm/lldb/tools/intel-features/intel-mpx/cli-wrapper-mpxtable.h b/gnu/llvm/lldb/tools/intel-features/intel-mpx/cli-wrapper-mpxtable.h
new file mode 100644
index 00000000000..335c673fef8
--- /dev/null
+++ b/gnu/llvm/lldb/tools/intel-features/intel-mpx/cli-wrapper-mpxtable.h
@@ -0,0 +1,11 @@
+//===-- cli-wrapper-mpxtable.h----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBDebugger.h"
+
+bool MPXPluginInitialize(lldb::SBDebugger &debugger);
diff --git a/gnu/llvm/lldb/tools/intel-features/intel-mpx/test/Makefile b/gnu/llvm/lldb/tools/intel-features/intel-mpx/test/Makefile
new file mode 100644
index 00000000000..b18044407a7
--- /dev/null
+++ b/gnu/llvm/lldb/tools/intel-features/intel-mpx/test/Makefile
@@ -0,0 +1,7 @@
+LEVEL = ../../../../make
+
+CXX_SOURCES := main.cpp
+
+CFLAGS_EXTRAS += -mmpx -fcheck-pointer-bounds -lmpxwrappers -lmpx -fuse-ld=bfd
+
+include $(LEVEL)/Makefile.rules
diff --git a/gnu/llvm/lldb/tools/intel-features/intel-mpx/test/README.txt b/gnu/llvm/lldb/tools/intel-features/intel-mpx/test/README.txt
new file mode 100644
index 00000000000..6797eff30a7
--- /dev/null
+++ b/gnu/llvm/lldb/tools/intel-features/intel-mpx/test/README.txt
@@ -0,0 +1,6 @@
+In order to run this test, create the following directory:
+
+ packages/Python/lldbsuite/test/functionalities/plugins/commands/mpxtablecmd
+
+and copy into it the contents of this directory.
+
diff --git a/gnu/llvm/lldb/tools/intel-features/intel-mpx/test/TestMPXTable.py b/gnu/llvm/lldb/tools/intel-features/intel-mpx/test/TestMPXTable.py
new file mode 100644
index 00000000000..f571252e26f
--- /dev/null
+++ b/gnu/llvm/lldb/tools/intel-features/intel-mpx/test/TestMPXTable.py
@@ -0,0 +1,169 @@
+"""
+Test mpx-table command.
+"""
+
+from __future__ import print_function
+
+
+import os
+import time
+import re
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestMPXTable(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ def setUp(self):
+ TestBase.setUp(self)
+
+ @skipIf(compiler="clang")
+ @skipIf(oslist=no_match(['linux']))
+ @skipIf(archs=no_match(['i386', 'x86_64']))
+ @skipIf(compiler="gcc", compiler_version=["<", "5"]) #GCC version >= 5 supports
+ #Intel(R) Memory Protection Extensions (Intel(R) MPX).
+ def test_show_command(self):
+ """Test 'mpx-table show' command"""
+ self.build()
+
+ lldb_exec_dir = os.environ["LLDB_IMPLIB_DIR"]
+ lldb_lib_dir = os.path.join(lldb_exec_dir, os.pardir, "lib")
+ plugin_file = os.path.join(lldb_lib_dir, "liblldbIntelFeatures.so")
+ if not os.path.isfile(plugin_file):
+ self.skipTest("features plugin missing.")
+ plugin_command = " "
+ seq = ("plugin", "load", plugin_file)
+ plugin_command = plugin_command.join(seq)
+ self.runCmd(plugin_command)
+
+ exe = os.path.join(os.getcwd(), "a.out")
+ self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+
+ self.b1 = line_number('main.cpp', '// Break 1.')
+ self.b2 = line_number('main.cpp', '// Break 2.')
+ self.b3 = line_number('main.cpp', '// Break 3.')
+ self.b4 = line_number('main.cpp', '// Break 4.')
+ lldbutil.run_break_set_by_file_and_line(self, "main.cpp", self.b1, num_expected_locations=1)
+ lldbutil.run_break_set_by_file_and_line(self, "main.cpp", self.b2, num_expected_locations=1)
+ lldbutil.run_break_set_by_file_and_line(self, "main.cpp", self.b3, num_expected_locations=1)
+ lldbutil.run_break_set_by_file_and_line(self, "main.cpp", self.b4, num_expected_locations=1)
+ self.runCmd("run", RUN_SUCCEEDED)
+
+ target = self.dbg.GetSelectedTarget()
+ process = target.GetProcess()
+
+ if (process.GetState() == lldb.eStateExited):
+ self.skipTest("Intel(R) MPX is not supported.")
+ else:
+ self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT,
+ substrs = ["stop reason = breakpoint 1."])
+
+ self.expect("mpx-table show a",
+ substrs = ['lbound = 0x',
+ ', ubound = 0x',
+ '(pointer value = 0x',
+ ', metadata = 0x',
+ ')'],
+ error = False)
+
+ self.expect("continue", STOPPED_DUE_TO_BREAKPOINT,
+ substrs = ["stop reason = breakpoint 2."])
+
+ # Check that out of scope pointer cannot be reached.
+ #
+ self.expect("mpx-table show a",
+ substrs = ['Invalid pointer.'],
+ error = True)
+
+ self.expect("mpx-table show tmp",
+ substrs = ['lbound = 0x',
+ ', ubound = 0x',
+ '(pointer value = 0x',
+ ', metadata = 0x',
+ ')'],
+ error = False)
+
+ self.expect("continue", STOPPED_DUE_TO_BREAKPOINT,
+ substrs = ["stop reason = breakpoint 3."])
+
+ # Check that the pointer value is correctly updated.
+ #
+ self.expect("mpx-table show tmp",
+ substrs = ['lbound = 0x',
+ ', ubound = 0x',
+ '(pointer value = 0x2',
+ ', metadata = 0x',
+ ')'],
+ error = False)
+
+ self.expect("continue", STOPPED_DUE_TO_BREAKPOINT,
+ substrs = ["stop reason = breakpoint 4."])
+
+ # After going back to main(), check that out of scope pointer cannot be
+ # reached.
+ #
+ self.expect("mpx-table show tmp",
+ substrs = ['Invalid pointer.'],
+ error = True)
+
+ self.expect("mpx-table show a",
+ substrs = ['lbound = 0x',
+ ', ubound = 0x',
+ '(pointer value = 0x',
+ ', metadata = 0x',
+ ')'],
+ error = False)
+
+ def test_set_command(self):
+ """Test 'mpx-table set' command"""
+ self.build()
+
+ lldb_exec_dir = os.environ["LLDB_IMPLIB_DIR"]
+ lldb_lib_dir = os.path.join(lldb_exec_dir, os.pardir, "lib")
+ plugin_file = os.path.join(lldb_lib_dir, "liblldbIntelFeatures.so")
+ if not os.path.isfile(plugin_file):
+ self.skipTest("features plugin missing.")
+ plugin_command = " "
+ seq = ("plugin", "load", plugin_file)
+ plugin_command = plugin_command.join(seq)
+ self.runCmd(plugin_command)
+
+ exe = os.path.join(os.getcwd(), "a.out")
+ self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+
+ self.b1 = line_number('main.cpp', '// Break 1.')
+ lldbutil.run_break_set_by_file_and_line(self, "main.cpp", self.b1, num_expected_locations=1)
+ self.runCmd("run", RUN_SUCCEEDED)
+
+ target = self.dbg.GetSelectedTarget()
+ process = target.GetProcess()
+
+ if (process.GetState() == lldb.eStateExited):
+ self.skipTest("Intel(R) MPX is not supported.")
+ else:
+ self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT,
+ substrs = ["stop reason = breakpoint 1."])
+
+ # Check that the BT Entry doesn't already contain the test values.
+ #
+ self.expect("mpx-table show a", matching=False,
+ substrs = ['lbound = 0xcafecafe',
+ ', ubound = 0xbeefbeef'])
+
+ # Set the test values.
+ #
+ self.expect("mpx-table set a 0xcafecafe 0xbeefbeef", error = False)
+
+ # Verify that the test values have been correctly written in the BT
+ # entry.
+ #
+ self.expect("mpx-table show a",
+ substrs = ['lbound = 0xcafecafe',
+ ', ubound = 0xbeefbeef'],
+ error = False)
+
+
diff --git a/gnu/llvm/lldb/tools/intel-features/intel-mpx/test/main.cpp b/gnu/llvm/lldb/tools/intel-features/intel-mpx/test/main.cpp
new file mode 100644
index 00000000000..2f5253ed860
--- /dev/null
+++ b/gnu/llvm/lldb/tools/intel-features/intel-mpx/test/main.cpp
@@ -0,0 +1,48 @@
+//===-- main.cpp ------------------------------------------------*- C++ -*-===//
+////
+//// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+//// See https://llvm.org/LICENSE.txt for license information.
+//// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+////
+////===----------------------------------------------------------------------===//
+//
+
+const int size = 5;
+
+#include <cstddef>
+#include <cstdlib>
+#include <sys/prctl.h>
+
+void func(int *ptr) {
+ int *tmp;
+
+#if defined __GNUC__ && !defined __INTEL_COMPILER
+ __builtin___bnd_store_ptr_bounds ((void**)&ptr, ptr);
+#endif
+ tmp = ptr + size - 1;
+#if defined __GNUC__ && !defined __INTEL_COMPILER
+ __builtin___bnd_store_ptr_bounds ((void**)&tmp, tmp);
+#endif
+ tmp = (int*)0x2; // Break 2.
+
+ return; // Break 3.
+}
+
+int
+main(int argc, char const *argv[])
+{
+ // This call returns 0 only if the CPU and the kernel support
+ // Intel(R) Memory Protection Extensions (Intel(R) MPX).
+ if (prctl(PR_MPX_ENABLE_MANAGEMENT, 0, 0, 0, 0) != 0)
+ return -1;
+
+ int* a = (int *) calloc(size, sizeof(int));
+#if defined __GNUC__ && !defined __INTEL_COMPILER
+ __builtin___bnd_store_ptr_bounds ((void**)&a, a);
+#endif
+ func(a); // Break 1.
+
+ free(a); // Break 4.
+
+ return 0;
+}
diff --git a/gnu/llvm/lldb/tools/intel-features/intel-pt/CMakeLists.txt b/gnu/llvm/lldb/tools/intel-features/intel-pt/CMakeLists.txt
new file mode 100644
index 00000000000..9750ed83ce2
--- /dev/null
+++ b/gnu/llvm/lldb/tools/intel-features/intel-pt/CMakeLists.txt
@@ -0,0 +1,31 @@
+if (NOT LIBIPT_INCLUDE_PATH)
+ message (FATAL_ERROR "libipt include path not provided")
+endif()
+
+if (NOT EXISTS "${LIBIPT_INCLUDE_PATH}")
+ message (FATAL_ERROR "invalid libipt include path provided")
+endif()
+include_directories(${LIBIPT_INCLUDE_PATH})
+
+if (NOT LIBIPT_LIBRARY_PATH)
+ find_library(LIBIPT_LIBRARY ipt)
+else()
+ if (NOT EXISTS "${LIBIPT_LIBRARY_PATH}")
+ message (FATAL_ERROR "invalid libipt library path provided")
+ endif()
+ find_library(LIBIPT_LIBRARY ipt PATHS ${LIBIPT_LIBRARY_PATH})
+endif()
+
+if (NOT LIBIPT_LIBRARY)
+ message (FATAL_ERROR "libipt library not found")
+endif()
+
+add_lldb_library(lldbIntelPT
+ PTDecoder.cpp
+ Decoder.cpp
+ cli-wrapper-pt.cpp
+
+ LINK_LIBS
+ ${LIBIPT_LIBRARY}
+ liblldb
+ )
diff --git a/gnu/llvm/lldb/tools/intel-features/intel-pt/Decoder.cpp b/gnu/llvm/lldb/tools/intel-features/intel-pt/Decoder.cpp
new file mode 100644
index 00000000000..2ce5c985c54
--- /dev/null
+++ b/gnu/llvm/lldb/tools/intel-features/intel-pt/Decoder.cpp
@@ -0,0 +1,901 @@
+//===-- Decoder.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Decoder.h"
+
+// C/C++ Includes
+#include <cinttypes>
+#include <cstring>
+
+#include "lldb/API/SBModule.h"
+#include "lldb/API/SBProcess.h"
+#include "lldb/API/SBThread.h"
+
+using namespace ptdecoder_private;
+
+// This function removes entries of all the processes/threads which were once
+// registered in the class but are not alive anymore because they died or
+// finished executing.
+void Decoder::RemoveDeadProcessesAndThreads(lldb::SBProcess &sbprocess) {
+ lldb::SBTarget sbtarget = sbprocess.GetTarget();
+ lldb::SBDebugger sbdebugger = sbtarget.GetDebugger();
+ uint32_t num_targets = sbdebugger.GetNumTargets();
+
+ auto itr_process = m_mapProcessUID_mapThreadID_TraceInfo.begin();
+ while (itr_process != m_mapProcessUID_mapThreadID_TraceInfo.end()) {
+ bool process_found = false;
+ lldb::SBTarget target;
+ lldb::SBProcess process;
+ for (uint32_t i = 0; i < num_targets; i++) {
+ target = sbdebugger.GetTargetAtIndex(i);
+ process = target.GetProcess();
+ if (process.GetUniqueID() == itr_process->first) {
+ process_found = true;
+ break;
+ }
+ }
+
+ // Remove the process's entry if it was not found in SBDebugger
+ if (!process_found) {
+ itr_process = m_mapProcessUID_mapThreadID_TraceInfo.erase(itr_process);
+ continue;
+ }
+
+ // If the state of the process is exited or detached then remove process's
+ // entry. If not then remove entry for all those registered threads of this
+ // process that are not alive anymore.
+ lldb::StateType state = process.GetState();
+ if ((state == lldb::StateType::eStateDetached) ||
+ (state == lldb::StateType::eStateExited))
+ itr_process = m_mapProcessUID_mapThreadID_TraceInfo.erase(itr_process);
+ else {
+ auto itr_thread = itr_process->second.begin();
+ while (itr_thread != itr_process->second.end()) {
+ if (itr_thread->first == LLDB_INVALID_THREAD_ID) {
+ ++itr_thread;
+ continue;
+ }
+
+ lldb::SBThread thread = process.GetThreadByID(itr_thread->first);
+ if (!thread.IsValid())
+ itr_thread = itr_process->second.erase(itr_thread);
+ else
+ ++itr_thread;
+ }
+ ++itr_process;
+ }
+ }
+}
+
+void Decoder::StartProcessorTrace(lldb::SBProcess &sbprocess,
+ lldb::SBTraceOptions &sbtraceoptions,
+ lldb::SBError &sberror) {
+ sberror.Clear();
+ CheckDebuggerID(sbprocess, sberror);
+ if (!sberror.Success())
+ return;
+
+ std::lock_guard<std::mutex> guard(
+ m_mapProcessUID_mapThreadID_TraceInfo_mutex);
+ RemoveDeadProcessesAndThreads(sbprocess);
+
+ if (sbtraceoptions.getType() != lldb::TraceType::eTraceTypeProcessorTrace) {
+ sberror.SetErrorStringWithFormat("SBTraceOptions::TraceType not set to "
+ "eTraceTypeProcessorTrace; ProcessID = "
+ "%" PRIu64,
+ sbprocess.GetProcessID());
+ return;
+ }
+ lldb::SBStructuredData sbstructdata = sbtraceoptions.getTraceParams(sberror);
+ if (!sberror.Success())
+ return;
+
+ const char *trace_tech_key = "trace-tech";
+ std::string trace_tech_value("intel-pt");
+ lldb::SBStructuredData value = sbstructdata.GetValueForKey(trace_tech_key);
+ if (!value.IsValid()) {
+ sberror.SetErrorStringWithFormat(
+ "key \"%s\" not set in custom trace parameters", trace_tech_key);
+ return;
+ }
+
+ char string_value[9];
+ size_t bytes_written = value.GetStringValue(
+ string_value, sizeof(string_value) / sizeof(*string_value));
+ if (!bytes_written ||
+ (bytes_written > (sizeof(string_value) / sizeof(*string_value)))) {
+ sberror.SetErrorStringWithFormat(
+ "key \"%s\" not set in custom trace parameters", trace_tech_key);
+ return;
+ }
+
+ std::size_t pos =
+ trace_tech_value.find((const char *)string_value, 0, bytes_written);
+ if ((pos == std::string::npos)) {
+ sberror.SetErrorStringWithFormat(
+ "key \"%s\" not set to \"%s\" in custom trace parameters",
+ trace_tech_key, trace_tech_value.c_str());
+ return;
+ }
+
+ // Start Tracing
+ lldb::SBError error;
+ uint32_t unique_id = sbprocess.GetUniqueID();
+ lldb::tid_t tid = sbtraceoptions.getThreadID();
+ lldb::SBTrace trace = sbprocess.StartTrace(sbtraceoptions, error);
+ if (!error.Success()) {
+ if (tid == LLDB_INVALID_THREAD_ID)
+ sberror.SetErrorStringWithFormat("%s; ProcessID = %" PRIu64,
+ error.GetCString(),
+ sbprocess.GetProcessID());
+ else
+ sberror.SetErrorStringWithFormat(
+ "%s; thread_id = %" PRIu64 ", ProcessID = %" PRIu64,
+ error.GetCString(), tid, sbprocess.GetProcessID());
+ return;
+ }
+
+ MapThreadID_TraceInfo &mapThreadID_TraceInfo =
+ m_mapProcessUID_mapThreadID_TraceInfo[unique_id];
+ ThreadTraceInfo &trace_info = mapThreadID_TraceInfo[tid];
+ trace_info.SetUniqueTraceInstance(trace);
+ trace_info.SetStopID(sbprocess.GetStopID());
+}
+
+void Decoder::StopProcessorTrace(lldb::SBProcess &sbprocess,
+ lldb::SBError &sberror, lldb::tid_t tid) {
+ sberror.Clear();
+ CheckDebuggerID(sbprocess, sberror);
+ if (!sberror.Success()) {
+ return;
+ }
+
+ std::lock_guard<std::mutex> guard(
+ m_mapProcessUID_mapThreadID_TraceInfo_mutex);
+ RemoveDeadProcessesAndThreads(sbprocess);
+
+ uint32_t unique_id = sbprocess.GetUniqueID();
+ auto itr_process = m_mapProcessUID_mapThreadID_TraceInfo.find(unique_id);
+ if (itr_process == m_mapProcessUID_mapThreadID_TraceInfo.end()) {
+ sberror.SetErrorStringWithFormat(
+ "tracing not active for this process; ProcessID = %" PRIu64,
+ sbprocess.GetProcessID());
+ return;
+ }
+
+ lldb::SBError error;
+ if (tid == LLDB_INVALID_THREAD_ID) {
+ // This implies to stop tracing on the whole process
+ lldb::user_id_t id_to_be_ignored = LLDB_INVALID_UID;
+ auto itr_thread = itr_process->second.begin();
+ while (itr_thread != itr_process->second.end()) {
+ // In the case when user started trace on the entire process and then
+ // registered newly spawned threads of this process in the class later,
+ // these newly spawned threads will have same trace id. If we stopped
+ // trace on the entire process then tracing stops automatically for these
+ // newly spawned registered threads. Stopping trace on them again will
+ // return error and therefore we need to skip stopping trace on them
+ // again.
+ lldb::SBTrace &trace = itr_thread->second.GetUniqueTraceInstance();
+ lldb::user_id_t lldb_pt_user_id = trace.GetTraceUID();
+ if (lldb_pt_user_id != id_to_be_ignored) {
+ trace.StopTrace(error, itr_thread->first);
+ if (!error.Success()) {
+ std::string error_string(error.GetCString());
+ if ((error_string.find("tracing not active for this process") ==
+ std::string::npos) &&
+ (error_string.find("tracing not active for this thread") ==
+ std::string::npos)) {
+ sberror.SetErrorStringWithFormat(
+ "%s; thread id=%" PRIu64 ", ProcessID = %" PRIu64,
+ error_string.c_str(), itr_thread->first,
+ sbprocess.GetProcessID());
+ return;
+ }
+ }
+
+ if (itr_thread->first == LLDB_INVALID_THREAD_ID)
+ id_to_be_ignored = lldb_pt_user_id;
+ }
+ itr_thread = itr_process->second.erase(itr_thread);
+ }
+ m_mapProcessUID_mapThreadID_TraceInfo.erase(itr_process);
+ } else {
+ // This implies to stop tracing on a single thread.
+ // if 'tid' is registered in the class then get the trace id and stop trace
+ // on it. If it is not then check if tracing was ever started on the entire
+ // process (because there is a possibility that trace is still running for
+ // 'tid' but it was not registered in the class because user had started
+ // trace on the whole process and 'tid' spawned later). In that case, get
+ // the trace id of the process trace instance and stop trace on this thread.
+ // If tracing was never started on the entire process then return error
+ // because there is no way tracing is active on 'tid'.
+ MapThreadID_TraceInfo &mapThreadID_TraceInfo = itr_process->second;
+ lldb::SBTrace trace;
+ auto itr = mapThreadID_TraceInfo.find(tid);
+ if (itr != mapThreadID_TraceInfo.end()) {
+ trace = itr->second.GetUniqueTraceInstance();
+ } else {
+ auto itr = mapThreadID_TraceInfo.find(LLDB_INVALID_THREAD_ID);
+ if (itr != mapThreadID_TraceInfo.end()) {
+ trace = itr->second.GetUniqueTraceInstance();
+ } else {
+ sberror.SetErrorStringWithFormat(
+ "tracing not active for this thread; thread id=%" PRIu64
+ ", ProcessID = %" PRIu64,
+ tid, sbprocess.GetProcessID());
+ return;
+ }
+ }
+
+ // Stop Tracing
+ trace.StopTrace(error, tid);
+ if (!error.Success()) {
+ std::string error_string(error.GetCString());
+ sberror.SetErrorStringWithFormat(
+ "%s; thread id=%" PRIu64 ", ProcessID = %" PRIu64,
+ error_string.c_str(), tid, sbprocess.GetProcessID());
+ if (error_string.find("tracing not active") == std::string::npos)
+ return;
+ }
+ // Delete the entry of 'tid' from this class (if any)
+ mapThreadID_TraceInfo.erase(tid);
+ }
+}
+
+void Decoder::ReadTraceDataAndImageInfo(lldb::SBProcess &sbprocess,
+ lldb::tid_t tid, lldb::SBError &sberror,
+ ThreadTraceInfo &threadTraceInfo) {
+ // Allocate trace data buffer and parse cpu info for 'tid' if it is registered
+ // for the first time in class
+ lldb::SBTrace &trace = threadTraceInfo.GetUniqueTraceInstance();
+ Buffer &pt_buffer = threadTraceInfo.GetPTBuffer();
+ lldb::SBError error;
+ if (pt_buffer.size() == 0) {
+ lldb::SBTraceOptions traceoptions;
+ traceoptions.setThreadID(tid);
+ trace.GetTraceConfig(traceoptions, error);
+ if (!error.Success()) {
+ sberror.SetErrorStringWithFormat("%s; ProcessID = %" PRIu64,
+ error.GetCString(),
+ sbprocess.GetProcessID());
+ return;
+ }
+ if (traceoptions.getType() != lldb::TraceType::eTraceTypeProcessorTrace) {
+ sberror.SetErrorStringWithFormat("invalid TraceType received from LLDB "
+ "for this thread; thread id=%" PRIu64
+ ", ProcessID = %" PRIu64,
+ tid, sbprocess.GetProcessID());
+ return;
+ }
+
+ threadTraceInfo.AllocatePTBuffer(traceoptions.getTraceBufferSize());
+ lldb::SBStructuredData sbstructdata = traceoptions.getTraceParams(sberror);
+ if (!sberror.Success())
+ return;
+ CPUInfo &pt_cpu = threadTraceInfo.GetCPUInfo();
+ ParseCPUInfo(pt_cpu, sbstructdata, sberror);
+ if (!sberror.Success())
+ return;
+ }
+
+ // Call LLDB API to get raw trace data for this thread
+ size_t bytes_written = trace.GetTraceData(error, (void *)pt_buffer.data(),
+ pt_buffer.size(), 0, tid);
+ if (!error.Success()) {
+ sberror.SetErrorStringWithFormat(
+ "%s; thread_id = %" PRIu64 ", ProcessID = %" PRIu64,
+ error.GetCString(), tid, sbprocess.GetProcessID());
+ return;
+ }
+ std::fill(pt_buffer.begin() + bytes_written, pt_buffer.end(), 0);
+
+ // Get information of all the modules of the inferior
+ lldb::SBTarget sbtarget = sbprocess.GetTarget();
+ ReadExecuteSectionInfos &readExecuteSectionInfos =
+ threadTraceInfo.GetReadExecuteSectionInfos();
+ GetTargetModulesInfo(sbtarget, readExecuteSectionInfos, sberror);
+ if (!sberror.Success())
+ return;
+}
+
+void Decoder::DecodeProcessorTrace(lldb::SBProcess &sbprocess, lldb::tid_t tid,
+ lldb::SBError &sberror,
+ ThreadTraceInfo &threadTraceInfo) {
+ // Initialize instruction decoder
+ struct pt_insn_decoder *decoder = nullptr;
+ struct pt_config config;
+ Buffer &pt_buffer = threadTraceInfo.GetPTBuffer();
+ CPUInfo &pt_cpu = threadTraceInfo.GetCPUInfo();
+ ReadExecuteSectionInfos &readExecuteSectionInfos =
+ threadTraceInfo.GetReadExecuteSectionInfos();
+
+ InitializePTInstDecoder(&decoder, &config, pt_cpu, pt_buffer,
+ readExecuteSectionInfos, sberror);
+ if (!sberror.Success())
+ return;
+
+ // Start raw trace decoding
+ Instructions &instruction_list = threadTraceInfo.GetInstructionLog();
+ instruction_list.clear();
+ DecodeTrace(decoder, instruction_list, sberror);
+}
+
+// Raw trace decoding requires information of Read & Execute sections of each
+// module of the inferior. This function updates internal state of the class to
+// store this information.
+void Decoder::GetTargetModulesInfo(
+ lldb::SBTarget &sbtarget, ReadExecuteSectionInfos &readExecuteSectionInfos,
+ lldb::SBError &sberror) {
+ if (!sbtarget.IsValid()) {
+ sberror.SetErrorStringWithFormat("Can't get target's modules info from "
+ "LLDB; process has an invalid target");
+ return;
+ }
+
+ lldb::SBFileSpec target_file_spec = sbtarget.GetExecutable();
+ if (!target_file_spec.IsValid()) {
+ sberror.SetErrorStringWithFormat("Target has an invalid file spec");
+ return;
+ }
+
+ uint32_t num_modules = sbtarget.GetNumModules();
+ readExecuteSectionInfos.clear();
+
+ // Store information of all RX sections of each module of inferior
+ for (uint32_t i = 0; i < num_modules; i++) {
+ lldb::SBModule module = sbtarget.GetModuleAtIndex(i);
+ if (!module.IsValid()) {
+ sberror.SetErrorStringWithFormat(
+ "Can't get module info [ %" PRIu32
+ " ] of target \"%s\" from LLDB, invalid module",
+ i, target_file_spec.GetFilename());
+ return;
+ }
+
+ lldb::SBFileSpec module_file_spec = module.GetPlatformFileSpec();
+ if (!module_file_spec.IsValid()) {
+ sberror.SetErrorStringWithFormat(
+ "Can't get module info [ %" PRIu32
+ " ] of target \"%s\" from LLDB, invalid file spec",
+ i, target_file_spec.GetFilename());
+ return;
+ }
+
+ const char *image(module_file_spec.GetFilename());
+ lldb::SBError error;
+ char image_complete_path[1024];
+ uint32_t path_length = module_file_spec.GetPath(
+ image_complete_path, sizeof(image_complete_path));
+ size_t num_sections = module.GetNumSections();
+
+ // Store information of only RX sections
+ for (size_t idx = 0; idx < num_sections; idx++) {
+ lldb::SBSection section = module.GetSectionAtIndex(idx);
+ uint32_t section_permission = section.GetPermissions();
+ if ((section_permission & lldb::Permissions::ePermissionsReadable) &&
+ (section_permission & lldb::Permissions::ePermissionsExecutable)) {
+ lldb::SBData section_data = section.GetSectionData();
+ if (!section_data.IsValid()) {
+ sberror.SetErrorStringWithFormat(
+ "Can't get module info [ %" PRIu32 " ] \"%s\" of target "
+ "\"%s\" from LLDB, invalid "
+ "data in \"%s\" section",
+ i, image, target_file_spec.GetFilename(), section.GetName());
+ return;
+ }
+
+ // In case section has no data, skip it.
+ if (section_data.GetByteSize() == 0)
+ continue;
+
+ if (!path_length) {
+ sberror.SetErrorStringWithFormat(
+ "Can't get module info [ %" PRIu32 " ] \"%s\" of target "
+ "\"%s\" from LLDB, module "
+ "has an invalid path length",
+ i, image, target_file_spec.GetFilename());
+ return;
+ }
+
+ std::string image_path(image_complete_path, path_length);
+ readExecuteSectionInfos.emplace_back(
+ section.GetLoadAddress(sbtarget), section.GetFileOffset(),
+ section_data.GetByteSize(), image_path);
+ }
+ }
+ }
+}
+
+// Raw trace decoding requires information of the target cpu on which inferior
+// is running. This function gets the Trace Configuration from LLDB, parses it
+// for cpu model, family, stepping and vendor id info and updates the internal
+// state of the class to store this information.
+void Decoder::ParseCPUInfo(CPUInfo &pt_cpu, lldb::SBStructuredData &s,
+ lldb::SBError &sberror) {
+ lldb::SBStructuredData custom_trace_params = s.GetValueForKey("intel-pt");
+ if (!custom_trace_params.IsValid()) {
+ sberror.SetErrorStringWithFormat("lldb couldn't provide cpuinfo");
+ return;
+ }
+
+ uint64_t family = 0, model = 0, stepping = 0;
+ char vendor[32];
+ const char *key_family = "cpu_family";
+ const char *key_model = "cpu_model";
+ const char *key_stepping = "cpu_stepping";
+ const char *key_vendor = "cpu_vendor";
+
+ // parse family
+ lldb::SBStructuredData struct_family =
+ custom_trace_params.GetValueForKey(key_family);
+ if (!struct_family.IsValid()) {
+ sberror.SetErrorStringWithFormat(
+ "%s info missing in custom trace parameters", key_family);
+ return;
+ }
+ family = struct_family.GetIntegerValue(0x10000);
+ if (family > UINT16_MAX) {
+ sberror.SetErrorStringWithFormat(
+ "invalid CPU family value extracted from custom trace parameters");
+ return;
+ }
+ pt_cpu.family = (uint16_t)family;
+
+ // parse model
+ lldb::SBStructuredData struct_model =
+ custom_trace_params.GetValueForKey(key_model);
+ if (!struct_model.IsValid()) {
+ sberror.SetErrorStringWithFormat(
+ "%s info missing in custom trace parameters; family=%" PRIu16,
+ key_model, pt_cpu.family);
+ return;
+ }
+ model = struct_model.GetIntegerValue(0x100);
+ if (model > UINT8_MAX) {
+ sberror.SetErrorStringWithFormat("invalid CPU model value extracted from "
+ "custom trace parameters; family=%" PRIu16,
+ pt_cpu.family);
+ return;
+ }
+ pt_cpu.model = (uint8_t)model;
+
+ // parse stepping
+ lldb::SBStructuredData struct_stepping =
+ custom_trace_params.GetValueForKey(key_stepping);
+ if (!struct_stepping.IsValid()) {
+ sberror.SetErrorStringWithFormat(
+ "%s info missing in custom trace parameters; family=%" PRIu16
+ ", model=%" PRIu8,
+ key_stepping, pt_cpu.family, pt_cpu.model);
+ return;
+ }
+ stepping = struct_stepping.GetIntegerValue(0x100);
+ if (stepping > UINT8_MAX) {
+ sberror.SetErrorStringWithFormat("invalid CPU stepping value extracted "
+ "from custom trace parameters; "
+ "family=%" PRIu16 ", model=%" PRIu8,
+ pt_cpu.family, pt_cpu.model);
+ return;
+ }
+ pt_cpu.stepping = (uint8_t)stepping;
+
+ // parse vendor info
+ pt_cpu.vendor = pcv_unknown;
+ lldb::SBStructuredData struct_vendor =
+ custom_trace_params.GetValueForKey(key_vendor);
+ if (!struct_vendor.IsValid()) {
+ sberror.SetErrorStringWithFormat(
+ "%s info missing in custom trace parameters; family=%" PRIu16
+ ", model=%" PRIu8 ", stepping=%" PRIu8,
+ key_vendor, pt_cpu.family, pt_cpu.model, pt_cpu.stepping);
+ return;
+ }
+ auto length = struct_vendor.GetStringValue(vendor, sizeof(vendor));
+ if (length && strstr(vendor, "GenuineIntel"))
+ pt_cpu.vendor = pcv_intel;
+}
+
+// Initialize trace decoder with pt_config structure and populate its image
+// structure with inferior's memory image information. pt_config structure is
+// initialized with trace buffer and cpu info of the inferior before storing it
+// in trace decoder.
+void Decoder::InitializePTInstDecoder(
+ struct pt_insn_decoder **decoder, struct pt_config *config,
+ const CPUInfo &pt_cpu, Buffer &pt_buffer,
+ const ReadExecuteSectionInfos &readExecuteSectionInfos,
+ lldb::SBError &sberror) const {
+ if (!decoder || !config) {
+ sberror.SetErrorStringWithFormat("internal error");
+ return;
+ }
+
+ // Load cpu info of inferior's target in pt_config struct
+ pt_config_init(config);
+ config->cpu = pt_cpu;
+ int errcode = pt_cpu_errata(&(config->errata), &(config->cpu));
+ if (errcode < 0) {
+ sberror.SetErrorStringWithFormat("processor trace decoding library: "
+ "pt_cpu_errata() failed with error: "
+ "\"%s\"",
+ pt_errstr(pt_errcode(errcode)));
+ return;
+ }
+
+ // Load trace buffer's starting and end address in pt_config struct
+ config->begin = pt_buffer.data();
+ config->end = pt_buffer.data() + pt_buffer.size();
+
+ // Fill trace decoder with pt_config struct
+ *decoder = pt_insn_alloc_decoder(config);
+ if (*decoder == nullptr) {
+ sberror.SetErrorStringWithFormat("processor trace decoding library: "
+ "pt_insn_alloc_decoder() returned null "
+ "pointer");
+ return;
+ }
+
+ // Fill trace decoder's image with inferior's memory image information
+ struct pt_image *image = pt_insn_get_image(*decoder);
+ if (!image) {
+ sberror.SetErrorStringWithFormat("processor trace decoding library: "
+ "pt_insn_get_image() returned null "
+ "pointer");
+ pt_insn_free_decoder(*decoder);
+ return;
+ }
+
+ for (auto &itr : readExecuteSectionInfos) {
+ errcode = pt_image_add_file(image, itr.image_path.c_str(), itr.file_offset,
+ itr.size, nullptr, itr.load_address);
+ if (errcode < 0) {
+ sberror.SetErrorStringWithFormat("processor trace decoding library: "
+ "pt_image_add_file() failed with error: "
+ "\"%s\"",
+ pt_errstr(pt_errcode(errcode)));
+ pt_insn_free_decoder(*decoder);
+ return;
+ }
+ }
+}
+
+// Start actual decoding of raw trace
+void Decoder::DecodeTrace(struct pt_insn_decoder *decoder,
+ Instructions &instruction_list,
+ lldb::SBError &sberror) {
+ uint64_t decoder_offset = 0;
+
+ while (1) {
+ struct pt_insn insn;
+
+ // Try to sync the decoder. If it fails then get the decoder_offset and try
+ // to sync again. If the new_decoder_offset is same as decoder_offset then
+ // we will not succeed in syncing for any number of pt_insn_sync_forward()
+ // operations. Return in that case. Else keep resyncing until either end of
+ // trace stream is reached or pt_insn_sync_forward() passes.
+ int errcode = pt_insn_sync_forward(decoder);
+ if (errcode < 0) {
+ if (errcode == -pte_eos)
+ return;
+
+ int errcode_off = pt_insn_get_offset(decoder, &decoder_offset);
+ if (errcode_off < 0) {
+ sberror.SetErrorStringWithFormat(
+ "processor trace decoding library: \"%s\"",
+ pt_errstr(pt_errcode(errcode)));
+ instruction_list.emplace_back(sberror.GetCString());
+ return;
+ }
+
+ sberror.SetErrorStringWithFormat(
+ "processor trace decoding library: \"%s\" [decoder_offset] => "
+ "[0x%" PRIu64 "]",
+ pt_errstr(pt_errcode(errcode)), decoder_offset);
+ instruction_list.emplace_back(sberror.GetCString());
+ while (1) {
+ errcode = pt_insn_sync_forward(decoder);
+ if (errcode >= 0)
+ break;
+
+ if (errcode == -pte_eos)
+ return;
+
+ uint64_t new_decoder_offset = 0;
+ errcode_off = pt_insn_get_offset(decoder, &new_decoder_offset);
+ if (errcode_off < 0) {
+ sberror.SetErrorStringWithFormat(
+ "processor trace decoding library: \"%s\"",
+ pt_errstr(pt_errcode(errcode)));
+ instruction_list.emplace_back(sberror.GetCString());
+ return;
+ } else if (new_decoder_offset <= decoder_offset) {
+ // We tried resyncing the decoder and decoder didn't make any
+ // progress because the offset didn't change. We will not make any
+ // progress further. Hence, returning in this situation.
+ return;
+ }
+ sberror.SetErrorStringWithFormat(
+ "processor trace decoding library: \"%s\" [decoder_offset] => "
+ "[0x%" PRIu64 "]",
+ pt_errstr(pt_errcode(errcode)), new_decoder_offset);
+ instruction_list.emplace_back(sberror.GetCString());
+ decoder_offset = new_decoder_offset;
+ }
+ }
+
+ while (1) {
+ errcode = pt_insn_next(decoder, &insn, sizeof(insn));
+ if (errcode < 0) {
+ if (insn.iclass == ptic_error)
+ break;
+
+ instruction_list.emplace_back(insn);
+
+ if (errcode == -pte_eos)
+ return;
+
+ Diagnose(decoder, errcode, sberror, &insn);
+ instruction_list.emplace_back(sberror.GetCString());
+ break;
+ }
+ instruction_list.emplace_back(insn);
+ if (errcode & pts_eos)
+ return;
+ }
+ }
+}
+
+// Function to diagnose and indicate errors during raw trace decoding
+void Decoder::Diagnose(struct pt_insn_decoder *decoder, int decode_error,
+ lldb::SBError &sberror, const struct pt_insn *insn) {
+ int errcode;
+ uint64_t offset;
+
+ errcode = pt_insn_get_offset(decoder, &offset);
+ if (insn) {
+ if (errcode < 0)
+ sberror.SetErrorStringWithFormat(
+ "processor trace decoding library: \"%s\" [decoder_offset, "
+ "last_successful_decoded_ip] => [?, 0x%" PRIu64 "]",
+ pt_errstr(pt_errcode(decode_error)), insn->ip);
+ else
+ sberror.SetErrorStringWithFormat(
+ "processor trace decoding library: \"%s\" [decoder_offset, "
+ "last_successful_decoded_ip] => [0x%" PRIu64 ", 0x%" PRIu64 "]",
+ pt_errstr(pt_errcode(decode_error)), offset, insn->ip);
+ } else {
+ if (errcode < 0)
+ sberror.SetErrorStringWithFormat(
+ "processor trace decoding library: \"%s\"",
+ pt_errstr(pt_errcode(decode_error)));
+ else
+ sberror.SetErrorStringWithFormat(
+ "processor trace decoding library: \"%s\" [decoder_offset] => "
+ "[0x%" PRIu64 "]",
+ pt_errstr(pt_errcode(decode_error)), offset);
+ }
+}
+
+void Decoder::GetInstructionLogAtOffset(lldb::SBProcess &sbprocess,
+ lldb::tid_t tid, uint32_t offset,
+ uint32_t count,
+ InstructionList &result_list,
+ lldb::SBError &sberror) {
+ sberror.Clear();
+ CheckDebuggerID(sbprocess, sberror);
+ if (!sberror.Success()) {
+ return;
+ }
+
+ std::lock_guard<std::mutex> guard(
+ m_mapProcessUID_mapThreadID_TraceInfo_mutex);
+ RemoveDeadProcessesAndThreads(sbprocess);
+
+ ThreadTraceInfo *threadTraceInfo = nullptr;
+ FetchAndDecode(sbprocess, tid, sberror, &threadTraceInfo);
+ if (!sberror.Success()) {
+ return;
+ }
+ if (threadTraceInfo == nullptr) {
+ sberror.SetErrorStringWithFormat("internal error");
+ return;
+ }
+
+ // Return instruction log by populating 'result_list'
+ Instructions &insn_list = threadTraceInfo->GetInstructionLog();
+ uint64_t sum = (uint64_t)offset + 1;
+ if (((insn_list.size() <= offset) && (count <= sum) &&
+ ((sum - count) >= insn_list.size())) ||
+ (count < 1)) {
+ sberror.SetErrorStringWithFormat(
+ "Instruction Log not available for offset=%" PRIu32
+ " and count=%" PRIu32 ", ProcessID = %" PRIu64,
+ offset, count, sbprocess.GetProcessID());
+ return;
+ }
+
+ Instructions::iterator itr_first =
+ (insn_list.size() <= offset) ? insn_list.begin()
+ : insn_list.begin() + insn_list.size() - sum;
+ Instructions::iterator itr_last =
+ (count <= sum) ? insn_list.begin() + insn_list.size() - (sum - count)
+ : insn_list.end();
+ Instructions::iterator itr = itr_first;
+ while (itr != itr_last) {
+ result_list.AppendInstruction(*itr);
+ ++itr;
+ }
+}
+
+void Decoder::GetProcessorTraceInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid,
+ TraceOptions &options,
+ lldb::SBError &sberror) {
+ sberror.Clear();
+ CheckDebuggerID(sbprocess, sberror);
+ if (!sberror.Success()) {
+ return;
+ }
+
+ std::lock_guard<std::mutex> guard(
+ m_mapProcessUID_mapThreadID_TraceInfo_mutex);
+ RemoveDeadProcessesAndThreads(sbprocess);
+
+ ThreadTraceInfo *threadTraceInfo = nullptr;
+ FetchAndDecode(sbprocess, tid, sberror, &threadTraceInfo);
+ if (!sberror.Success()) {
+ return;
+ }
+ if (threadTraceInfo == nullptr) {
+ sberror.SetErrorStringWithFormat("internal error");
+ return;
+ }
+
+ // Get SBTraceOptions from LLDB for 'tid', populate 'traceoptions' with it
+ lldb::SBTrace &trace = threadTraceInfo->GetUniqueTraceInstance();
+ lldb::SBTraceOptions traceoptions;
+ lldb::SBError error;
+ traceoptions.setThreadID(tid);
+ trace.GetTraceConfig(traceoptions, error);
+ if (!error.Success()) {
+ std::string error_string(error.GetCString());
+ if (error_string.find("tracing not active") != std::string::npos) {
+ uint32_t unique_id = sbprocess.GetUniqueID();
+ auto itr_process = m_mapProcessUID_mapThreadID_TraceInfo.find(unique_id);
+ if (itr_process == m_mapProcessUID_mapThreadID_TraceInfo.end())
+ return;
+ itr_process->second.erase(tid);
+ }
+ sberror.SetErrorStringWithFormat("%s; ProcessID = %" PRIu64,
+ error_string.c_str(),
+ sbprocess.GetProcessID());
+ return;
+ }
+ if (traceoptions.getType() != lldb::TraceType::eTraceTypeProcessorTrace) {
+ sberror.SetErrorStringWithFormat("invalid TraceType received from LLDB "
+ "for this thread; thread id=%" PRIu64
+ ", ProcessID = %" PRIu64,
+ tid, sbprocess.GetProcessID());
+ return;
+ }
+ options.setType(traceoptions.getType());
+ options.setTraceBufferSize(traceoptions.getTraceBufferSize());
+ options.setMetaDataBufferSize(traceoptions.getMetaDataBufferSize());
+ lldb::SBStructuredData sbstructdata = traceoptions.getTraceParams(sberror);
+ if (!sberror.Success())
+ return;
+ options.setTraceParams(sbstructdata);
+ options.setInstructionLogSize(threadTraceInfo->GetInstructionLog().size());
+}
+
+void Decoder::FetchAndDecode(lldb::SBProcess &sbprocess, lldb::tid_t tid,
+ lldb::SBError &sberror,
+ ThreadTraceInfo **threadTraceInfo) {
+ // Return with error if 'sbprocess' is not registered in the class
+ uint32_t unique_id = sbprocess.GetUniqueID();
+ auto itr_process = m_mapProcessUID_mapThreadID_TraceInfo.find(unique_id);
+ if (itr_process == m_mapProcessUID_mapThreadID_TraceInfo.end()) {
+ sberror.SetErrorStringWithFormat(
+ "tracing not active for this process; ProcessID = %" PRIu64,
+ sbprocess.GetProcessID());
+ return;
+ }
+
+ if (tid == LLDB_INVALID_THREAD_ID) {
+ sberror.SetErrorStringWithFormat(
+ "invalid thread id provided; thread_id = %" PRIu64
+ ", ProcessID = %" PRIu64,
+ tid, sbprocess.GetProcessID());
+ return;
+ }
+
+ // Check whether 'tid' thread is registered in the class. If it is then in
+ // case StopID didn't change then return without doing anything (no need to
+ // read and decode trace data then). Otherwise, save new StopID and proceed
+ // with reading and decoding trace.
+ if (threadTraceInfo == nullptr) {
+ sberror.SetErrorStringWithFormat("internal error");
+ return;
+ }
+
+ MapThreadID_TraceInfo &mapThreadID_TraceInfo = itr_process->second;
+ auto itr_thread = mapThreadID_TraceInfo.find(tid);
+ if (itr_thread != mapThreadID_TraceInfo.end()) {
+ if (itr_thread->second.GetStopID() == sbprocess.GetStopID()) {
+ *threadTraceInfo = &(itr_thread->second);
+ return;
+ }
+ itr_thread->second.SetStopID(sbprocess.GetStopID());
+ } else {
+ // Implies 'tid' is not registered in the class. If tracing was never
+ // started on the entire process then return an error. Else try to register
+ // this thread and proceed with reading and decoding trace.
+ lldb::SBError error;
+ itr_thread = mapThreadID_TraceInfo.find(LLDB_INVALID_THREAD_ID);
+ if (itr_thread == mapThreadID_TraceInfo.end()) {
+ sberror.SetErrorStringWithFormat(
+ "tracing not active for this thread; ProcessID = %" PRIu64,
+ sbprocess.GetProcessID());
+ return;
+ }
+
+ lldb::SBTrace &trace = itr_thread->second.GetUniqueTraceInstance();
+ ThreadTraceInfo &trace_info = mapThreadID_TraceInfo[tid];
+ trace_info.SetUniqueTraceInstance(trace);
+ trace_info.SetStopID(sbprocess.GetStopID());
+ itr_thread = mapThreadID_TraceInfo.find(tid);
+ }
+
+ // Get raw trace data and inferior image from LLDB for the registered thread
+ ReadTraceDataAndImageInfo(sbprocess, tid, sberror, itr_thread->second);
+ if (!sberror.Success()) {
+ std::string error_string(sberror.GetCString());
+ if (error_string.find("tracing not active") != std::string::npos)
+ mapThreadID_TraceInfo.erase(itr_thread);
+ return;
+ }
+ // Decode raw trace data
+ DecodeProcessorTrace(sbprocess, tid, sberror, itr_thread->second);
+ if (!sberror.Success()) {
+ return;
+ }
+ *threadTraceInfo = &(itr_thread->second);
+}
+
+// This function checks whether the provided SBProcess instance belongs to same
+// SBDebugger with which this tool instance is associated.
+void Decoder::CheckDebuggerID(lldb::SBProcess &sbprocess,
+ lldb::SBError &sberror) {
+ if (!sbprocess.IsValid()) {
+ sberror.SetErrorStringWithFormat("invalid process instance");
+ return;
+ }
+
+ lldb::SBTarget sbtarget = sbprocess.GetTarget();
+ if (!sbtarget.IsValid()) {
+ sberror.SetErrorStringWithFormat(
+ "process contains an invalid target; ProcessID = %" PRIu64,
+ sbprocess.GetProcessID());
+ return;
+ }
+
+ lldb::SBDebugger sbdebugger = sbtarget.GetDebugger();
+ if (!sbdebugger.IsValid()) {
+ sberror.SetErrorStringWithFormat("process's target contains an invalid "
+ "debugger instance; ProcessID = %" PRIu64,
+ sbprocess.GetProcessID());
+ return;
+ }
+
+ if (sbdebugger.GetID() != m_debugger_user_id) {
+ sberror.SetErrorStringWithFormat(
+ "process belongs to a different SBDebugger instance than the one for "
+ "which the tool is instantiated; ProcessID = %" PRIu64,
+ sbprocess.GetProcessID());
+ return;
+ }
+}
diff --git a/gnu/llvm/lldb/tools/intel-features/intel-pt/Decoder.h b/gnu/llvm/lldb/tools/intel-features/intel-pt/Decoder.h
new file mode 100644
index 00000000000..2ae89653df5
--- /dev/null
+++ b/gnu/llvm/lldb/tools/intel-features/intel-pt/Decoder.h
@@ -0,0 +1,309 @@
+//===-- Decoder.h -----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef Decoder_h_
+#define Decoder_h_
+
+// C/C++ Includes
+#include <map>
+#include <mutex>
+#include <string>
+#include <vector>
+
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBError.h"
+#include "lldb/API/SBProcess.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBStructuredData.h"
+#include "lldb/API/SBTarget.h"
+#include "lldb/API/SBTrace.h"
+#include "lldb/API/SBTraceOptions.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-types.h"
+
+#include "intel-pt.h"
+
+namespace ptdecoder_private {
+/// \class Instruction
+/// Represents an assembly instruction containing raw
+/// instruction bytes, instruction address along with information
+/// regarding execution flow context and Intel(R) Processor Trace
+/// context.
+class Instruction {
+public:
+ Instruction() : ip(0), data(), error(), iclass(ptic_error), speculative(0) {}
+
+ Instruction(const Instruction &insn) = default;
+
+ Instruction(const struct pt_insn &insn)
+ : ip(insn.ip), data(), error(insn.size == 0 ? "invalid instruction" : ""),
+ iclass(insn.iclass), speculative(insn.speculative) {
+ if (insn.size != 0)
+ data.assign(insn.raw, insn.raw + insn.size);
+ }
+
+ Instruction(const char *err)
+ : ip(0), data(), error(err ? err : "unknown error"), iclass(ptic_error),
+ speculative(0) {}
+
+ ~Instruction() {}
+
+ uint64_t GetInsnAddress() const { return ip; }
+
+ size_t GetRawBytes(void *buf, size_t size) const {
+ if ((buf == nullptr) || (size == 0))
+ return data.size();
+
+ size_t bytes_to_read = ((size <= data.size()) ? size : data.size());
+ ::memcpy(buf, data.data(), bytes_to_read);
+ return bytes_to_read;
+ }
+
+ const std::string &GetError() const { return error; }
+
+ bool GetSpeculative() const { return speculative; }
+
+private:
+ uint64_t ip; // instruction address in inferior's memory image
+ std::vector<uint8_t> data; // raw bytes
+ std::string error; // Error string if instruction is invalid
+ enum pt_insn_class iclass; // classification of the instruction
+ // A collection of flags giving additional information about instruction
+ uint32_t speculative : 1; // Instruction was executed speculatively or not
+};
+
+/// \class InstructionList
+/// Represents a list of assembly instructions. Each instruction is of
+/// type Instruction.
+class InstructionList {
+public:
+ InstructionList() : m_insn_vec() {}
+
+ InstructionList(const InstructionList &insn_list)
+ : m_insn_vec(insn_list.m_insn_vec) {}
+
+ ~InstructionList() {}
+
+ // Get number of instructions in the list
+ size_t GetSize() const { return m_insn_vec.size(); }
+
+ // Get instruction at index
+ Instruction GetInstructionAtIndex(uint32_t idx) {
+ return (idx < m_insn_vec.size() ? m_insn_vec[idx]
+ : Instruction("invalid instruction"));
+ }
+
+ // Append intruction at the end of the list
+ void AppendInstruction(Instruction inst) { m_insn_vec.push_back(inst); }
+
+private:
+ std::vector<Instruction> m_insn_vec;
+};
+
+/// \class TraceOptions
+/// Provides Intel(R) Processor Trace specific configuration options and
+/// other information obtained by decoding and post-processing the trace
+/// data. Currently, this information comprises of the total number of
+/// assembly instructions executed for an inferior.
+class TraceOptions : public lldb::SBTraceOptions {
+public:
+ TraceOptions() : lldb::SBTraceOptions(), m_insn_log_size(0) {}
+
+ ~TraceOptions() {}
+
+ /// Get total number of assembly instructions obtained after decoding the
+ /// complete Intel(R) Processor Trace data obtained from LLDB.
+ ///
+ /// \return
+ /// Total number of instructions.
+ uint32_t getInstructionLogSize() const { return m_insn_log_size; }
+
+ /// Set total number of assembly instructions.
+ ///
+ /// \param[in] size
+ /// Value to be set.
+ void setInstructionLogSize(uint32_t size) { m_insn_log_size = size; }
+
+private:
+ uint32_t m_insn_log_size;
+};
+
+/// \class Decoder
+/// This class makes use of Intel(R) Processor Trace hardware feature
+/// (implememted inside LLDB) to gather trace data for an inferior (being
+/// debugged with LLDB) to provide meaningful information out of it.
+///
+/// Currently the meaningful information comprises of the execution flow
+/// of the inferior (in terms of assembly instructions executed). The class
+/// enables user to:
+/// - start the trace with configuration options for a thread/process,
+/// - stop the trace for a thread/process,
+/// - get the execution flow (assembly instructions) for a thread and
+/// - get trace specific information for a thread
+class Decoder {
+public:
+ typedef std::vector<Instruction> Instructions;
+
+ Decoder(lldb::SBDebugger &sbdebugger)
+ : m_mapProcessUID_mapThreadID_TraceInfo_mutex(),
+ m_mapProcessUID_mapThreadID_TraceInfo(),
+ m_debugger_user_id(sbdebugger.GetID()) {}
+
+ ~Decoder() {}
+
+ void StartProcessorTrace(lldb::SBProcess &sbprocess,
+ lldb::SBTraceOptions &sbtraceoptions,
+ lldb::SBError &sberror);
+
+ void StopProcessorTrace(lldb::SBProcess &sbprocess, lldb::SBError &sberror,
+ lldb::tid_t tid = LLDB_INVALID_THREAD_ID);
+
+ void GetInstructionLogAtOffset(lldb::SBProcess &sbprocess, lldb::tid_t tid,
+ uint32_t offset, uint32_t count,
+ InstructionList &result_list,
+ lldb::SBError &sberror);
+
+ void GetProcessorTraceInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid,
+ TraceOptions &traceinfo, lldb::SBError &sberror);
+
+private:
+ class ThreadTraceInfo;
+ typedef std::vector<uint8_t> Buffer;
+
+ // internal class to manage inferior's read-execute section information
+ class ReadExecuteSectionInfo {
+ public:
+ uint64_t load_address;
+ uint64_t file_offset;
+ uint64_t size;
+ std::string image_path;
+
+ ReadExecuteSectionInfo(const uint64_t addr, const uint64_t offset,
+ const uint64_t sz, const std::string &path)
+ : load_address(addr), file_offset(offset), size(sz), image_path(path) {}
+
+ ReadExecuteSectionInfo(const ReadExecuteSectionInfo &rxsection) = default;
+ };
+
+ typedef struct pt_cpu CPUInfo;
+ typedef std::vector<ReadExecuteSectionInfo> ReadExecuteSectionInfos;
+
+ // Check whether the provided SBProcess belongs to the same SBDebugger with
+ // which Decoder class instance was constructed.
+ void CheckDebuggerID(lldb::SBProcess &sbprocess, lldb::SBError &sberror);
+
+ // Function to remove entries of finished processes/threads in the class
+ void RemoveDeadProcessesAndThreads(lldb::SBProcess &sbprocess);
+
+ // Parse cpu information from trace configuration received from LLDB
+ void ParseCPUInfo(CPUInfo &pt_cpu, lldb::SBStructuredData &s,
+ lldb::SBError &sberror);
+
+ /// Function performs following tasks for a given process and thread:
+ /// - Checks if the given thread is registered in the class or not. If not
+ /// then tries to register it if trace was ever started on the entire
+ /// process. Else returns error.
+ /// - fetches trace and other necessary information from LLDB (using
+ /// ReadTraceDataAndImageInfo()) and decodes the trace (using
+ /// DecodeProcessorTrace())
+ void FetchAndDecode(lldb::SBProcess &sbprocess, lldb::tid_t tid,
+ lldb::SBError &sberror,
+ ThreadTraceInfo **threadTraceInfo);
+
+ // Helper function of FetchAndDecode() to get raw trace data and memory image
+ // info of inferior from LLDB
+ void ReadTraceDataAndImageInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid,
+ lldb::SBError &sberror,
+ ThreadTraceInfo &threadTraceInfo);
+
+ // Helper function of FetchAndDecode() to initialize raw trace decoder and
+ // start trace decoding
+ void DecodeProcessorTrace(lldb::SBProcess &sbprocess, lldb::tid_t tid,
+ lldb::SBError &sberror,
+ ThreadTraceInfo &threadTraceInfo);
+
+ // Helper function of ReadTraceDataAndImageInfo() function for gathering
+ // inferior's memory image info along with all dynamic libraries linked with
+ // it
+ void GetTargetModulesInfo(lldb::SBTarget &sbtarget,
+ ReadExecuteSectionInfos &readExecuteSectionInfos,
+ lldb::SBError &sberror);
+
+ /// Helper functions of DecodeProcessorTrace() function for:
+ /// - initializing raw trace decoder (provided by Intel(R) Processor Trace
+ /// Decoding library)
+ /// - start trace decoding
+ void InitializePTInstDecoder(
+ struct pt_insn_decoder **decoder, struct pt_config *config,
+ const CPUInfo &pt_cpu, Buffer &pt_buffer,
+ const ReadExecuteSectionInfos &readExecuteSectionInfos,
+ lldb::SBError &sberror) const;
+ void DecodeTrace(struct pt_insn_decoder *decoder,
+ Instructions &instruction_list, lldb::SBError &sberror);
+
+ // Function to diagnose and indicate errors during raw trace decoding
+ void Diagnose(struct pt_insn_decoder *decoder, int errcode,
+ lldb::SBError &sberror, const struct pt_insn *insn = nullptr);
+
+ class ThreadTraceInfo {
+ public:
+ ThreadTraceInfo()
+ : m_pt_buffer(), m_readExecuteSectionInfos(), m_thread_stop_id(0),
+ m_trace(), m_pt_cpu(), m_instruction_log() {}
+
+ ThreadTraceInfo(const ThreadTraceInfo &trace_info) = default;
+
+ ~ThreadTraceInfo() {}
+
+ Buffer &GetPTBuffer() { return m_pt_buffer; }
+
+ void AllocatePTBuffer(uint64_t size) { m_pt_buffer.assign(size, 0); }
+
+ ReadExecuteSectionInfos &GetReadExecuteSectionInfos() {
+ return m_readExecuteSectionInfos;
+ }
+
+ CPUInfo &GetCPUInfo() { return m_pt_cpu; }
+
+ Instructions &GetInstructionLog() { return m_instruction_log; }
+
+ uint32_t GetStopID() const { return m_thread_stop_id; }
+
+ void SetStopID(uint32_t stop_id) { m_thread_stop_id = stop_id; }
+
+ lldb::SBTrace &GetUniqueTraceInstance() { return m_trace; }
+
+ void SetUniqueTraceInstance(lldb::SBTrace &trace) { m_trace = trace; }
+
+ friend class Decoder;
+
+ private:
+ Buffer m_pt_buffer; // raw trace buffer
+ ReadExecuteSectionInfos
+ m_readExecuteSectionInfos; // inferior's memory image info
+ uint32_t m_thread_stop_id; // stop id for thread
+ lldb::SBTrace m_trace; // unique tracing instance of a thread/process
+ CPUInfo m_pt_cpu; // cpu info of the target on which inferior is running
+ Instructions m_instruction_log; // complete instruction log
+ };
+
+ typedef std::map<lldb::user_id_t, ThreadTraceInfo> MapThreadID_TraceInfo;
+ typedef std::map<uint32_t, MapThreadID_TraceInfo>
+ MapProcessUID_MapThreadID_TraceInfo;
+
+ std::mutex m_mapProcessUID_mapThreadID_TraceInfo_mutex;
+ MapProcessUID_MapThreadID_TraceInfo
+ m_mapProcessUID_mapThreadID_TraceInfo; // to store trace information for
+ // each process and its associated
+ // threads
+ lldb::user_id_t m_debugger_user_id; // SBDebugger instance which is associated
+ // to this Decoder instance
+};
+
+} // namespace ptdecoder_private
+#endif // Decoder_h_
diff --git a/gnu/llvm/lldb/tools/intel-features/intel-pt/PTDecoder.cpp b/gnu/llvm/lldb/tools/intel-features/intel-pt/PTDecoder.cpp
new file mode 100644
index 00000000000..f09307d0ffe
--- /dev/null
+++ b/gnu/llvm/lldb/tools/intel-features/intel-pt/PTDecoder.cpp
@@ -0,0 +1,149 @@
+//===-- PTDecoder.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PTDecoder.h"
+#include "Decoder.h"
+
+using namespace ptdecoder;
+using namespace ptdecoder_private;
+
+// PTInstruction class member functions definitions
+PTInstruction::PTInstruction(
+ const std::shared_ptr<ptdecoder_private::Instruction> &ptr)
+ : m_opaque_sp(ptr) {}
+
+PTInstruction::~PTInstruction() {}
+
+uint64_t PTInstruction::GetInsnAddress() const {
+ return (m_opaque_sp ? m_opaque_sp->GetInsnAddress() : 0);
+}
+
+size_t PTInstruction::GetRawBytes(void *buf, size_t size) const {
+ return (m_opaque_sp ? m_opaque_sp->GetRawBytes(buf, size) : 0);
+}
+
+std::string PTInstruction::GetError() const {
+ return (m_opaque_sp ? m_opaque_sp->GetError() : "null pointer");
+}
+
+bool PTInstruction::GetSpeculative() const {
+ return (m_opaque_sp ? m_opaque_sp->GetSpeculative() : 0);
+}
+
+// PTInstructionList class member functions definitions
+size_t PTInstructionList::GetSize() const {
+ return (m_opaque_sp ? m_opaque_sp->GetSize() : 0);
+}
+
+PTInstruction PTInstructionList::GetInstructionAtIndex(uint32_t idx) {
+ if (m_opaque_sp)
+ return PTInstruction(std::shared_ptr<ptdecoder_private::Instruction>(
+ new Instruction(m_opaque_sp->GetInstructionAtIndex(idx))));
+
+ return PTInstruction(std::shared_ptr<ptdecoder_private::Instruction>(
+ new Instruction("invalid instruction")));
+}
+
+void PTInstructionList::SetSP(
+ const std::shared_ptr<ptdecoder_private::InstructionList> &ptr) {
+ m_opaque_sp = ptr;
+}
+void PTInstructionList::Clear() {
+ if (!m_opaque_sp)
+ return;
+ m_opaque_sp.reset();
+}
+
+// PTTraceOptions class member functions definitions
+lldb::TraceType PTTraceOptions::GetType() const {
+ return (m_opaque_sp ? m_opaque_sp->getType()
+ : lldb::TraceType::eTraceTypeNone);
+}
+
+uint64_t PTTraceOptions::GetTraceBufferSize() const {
+ return (m_opaque_sp ? m_opaque_sp->getTraceBufferSize() : 0);
+}
+
+uint64_t PTTraceOptions::GetMetaDataBufferSize() const {
+ return (m_opaque_sp ? m_opaque_sp->getMetaDataBufferSize() : 0);
+}
+
+lldb::SBStructuredData PTTraceOptions::GetTraceParams(lldb::SBError &error) {
+ if (!m_opaque_sp)
+ error.SetErrorString("null pointer");
+ return (m_opaque_sp ? m_opaque_sp->getTraceParams(error)
+ : lldb::SBStructuredData());
+}
+
+void PTTraceOptions::SetSP(
+ const std::shared_ptr<ptdecoder_private::TraceOptions> &ptr) {
+ m_opaque_sp = ptr;
+}
+
+// PTDecoder class member functions definitions
+PTDecoder::PTDecoder(lldb::SBDebugger &sbdebugger)
+ : m_opaque_sp(new ptdecoder_private::Decoder(sbdebugger)) {}
+
+void PTDecoder::StartProcessorTrace(lldb::SBProcess &sbprocess,
+ lldb::SBTraceOptions &sbtraceoptions,
+ lldb::SBError &sberror) {
+ if (m_opaque_sp == nullptr) {
+ sberror.SetErrorStringWithFormat("invalid PTDecoder instance");
+ return;
+ }
+
+ m_opaque_sp->StartProcessorTrace(sbprocess, sbtraceoptions, sberror);
+}
+
+void PTDecoder::StopProcessorTrace(lldb::SBProcess &sbprocess,
+ lldb::SBError &sberror, lldb::tid_t tid) {
+ if (m_opaque_sp == nullptr) {
+ sberror.SetErrorStringWithFormat("invalid PTDecoder instance");
+ return;
+ }
+
+ m_opaque_sp->StopProcessorTrace(sbprocess, sberror, tid);
+}
+
+void PTDecoder::GetInstructionLogAtOffset(lldb::SBProcess &sbprocess,
+ lldb::tid_t tid, uint32_t offset,
+ uint32_t count,
+ PTInstructionList &result_list,
+ lldb::SBError &sberror) {
+ if (m_opaque_sp == nullptr) {
+ sberror.SetErrorStringWithFormat("invalid PTDecoder instance");
+ return;
+ }
+
+ std::shared_ptr<ptdecoder_private::InstructionList> insn_list_ptr(
+ new InstructionList());
+ m_opaque_sp->GetInstructionLogAtOffset(sbprocess, tid, offset, count,
+ *insn_list_ptr, sberror);
+ if (!sberror.Success())
+ return;
+
+ result_list.SetSP(insn_list_ptr);
+}
+
+void PTDecoder::GetProcessorTraceInfo(lldb::SBProcess &sbprocess,
+ lldb::tid_t tid, PTTraceOptions &options,
+ lldb::SBError &sberror) {
+ if (m_opaque_sp == nullptr) {
+ sberror.SetErrorStringWithFormat("invalid PTDecoder instance");
+ return;
+ }
+
+ std::shared_ptr<ptdecoder_private::TraceOptions> trace_options_ptr(
+ new TraceOptions());
+ m_opaque_sp->GetProcessorTraceInfo(sbprocess, tid, *trace_options_ptr,
+ sberror);
+ if (!sberror.Success())
+ return;
+
+ options.SetSP(trace_options_ptr);
+}
diff --git a/gnu/llvm/lldb/tools/intel-features/intel-pt/PTDecoder.h b/gnu/llvm/lldb/tools/intel-features/intel-pt/PTDecoder.h
new file mode 100644
index 00000000000..f44c152747b
--- /dev/null
+++ b/gnu/llvm/lldb/tools/intel-features/intel-pt/PTDecoder.h
@@ -0,0 +1,270 @@
+//===-- PTDecoder.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef PTDecoder_h_
+#define PTDecoder_h_
+
+// C/C++ Includes
+#include <vector>
+
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBError.h"
+#include "lldb/API/SBProcess.h"
+#include "lldb/API/SBStructuredData.h"
+#include "lldb/API/SBTraceOptions.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-types.h"
+
+namespace ptdecoder_private {
+class Instruction;
+class InstructionList;
+class TraceOptions;
+class Decoder;
+} // namespace ptdecoder_private
+
+namespace ptdecoder {
+
+/// \class PTInstruction
+/// Represents an assembly instruction containing raw
+/// instruction bytes, instruction address along with information
+/// regarding execution flow context and Intel(R) Processor Trace
+/// context.
+class PTInstruction {
+public:
+ PTInstruction() = default;
+
+ PTInstruction(const std::shared_ptr<ptdecoder_private::Instruction> &ptr);
+
+ ~PTInstruction();
+
+ // Get instruction address in inferior's memory image
+ uint64_t GetInsnAddress() const;
+
+ /// Get raw bytes of the instruction in the buffer.
+ ///
+ /// \param[out] buf
+ /// The buffer where the raw bytes will be written. This buffer should be
+ /// allocated by the caller of this API. Providing an unallocated buffer
+ /// is an error. In case of errors, the content of the buffer is not
+ /// valid.
+ ///
+ /// \param[in] size
+ /// Number of raw bytes to be written to @buf. Atleast @size bytes of
+ /// memory should be allocated to @buf otherwise the behaviour of the API
+ /// is undefined. Providing 0 for this argument is an error.
+ ///
+ /// \return
+ /// Number of bytes of the instruction actually written to @buf if API
+ /// succeeds. In case of errors, total number of raw bytes of the
+ /// instruction is returned.
+ size_t GetRawBytes(void *buf, size_t size) const;
+
+ // Get error string if it represents an invalid instruction. For a valid
+ // instruction, an empty string is returned
+ std::string GetError() const;
+
+ // Instruction was executed speculatively or not
+ bool GetSpeculative() const;
+
+private:
+ std::shared_ptr<ptdecoder_private::Instruction> m_opaque_sp;
+};
+
+/// \class PTInstructionList
+/// Represents a list of assembly instructions. Each instruction is of
+/// type PTInstruction.
+class PTInstructionList {
+public:
+ // Get number of instructions in the list
+ size_t GetSize() const;
+
+ // Get instruction at index
+ PTInstruction GetInstructionAtIndex(uint32_t idx);
+
+ void Clear();
+
+private:
+ friend class PTDecoder;
+
+ void SetSP(const std::shared_ptr<ptdecoder_private::InstructionList> &ptr);
+
+ std::shared_ptr<ptdecoder_private::InstructionList> m_opaque_sp;
+};
+
+/// \class PTTraceOptions
+/// Provides configuration options like trace type, trace buffer size,
+/// meta data buffer size along with other Intel(R) Processor Trace
+/// specific options.
+class PTTraceOptions {
+public:
+ lldb::TraceType GetType() const;
+
+ uint64_t GetTraceBufferSize() const;
+
+ uint64_t GetMetaDataBufferSize() const;
+
+ /// Get Intel(R) Processor Trace specific configuration options (apart from
+ /// trace buffer size, meta data buffer size and TraceType) formatted as
+ /// json text i.e. {"Name":Value,"Name":Value} pairs, where "Value" is a
+ /// 64-bit unsigned integer in hex format. For "Name", please refer to
+ /// SBProcess::StartTrace API description for setting SBTraceOptions.
+ ///
+ /// \return
+ /// A string formatted as json text {"Name":Value,"Name":Value}
+ lldb::SBStructuredData GetTraceParams(lldb::SBError &error);
+
+private:
+ friend class PTDecoder;
+
+ void SetSP(const std::shared_ptr<ptdecoder_private::TraceOptions> &ptr);
+
+ std::shared_ptr<ptdecoder_private::TraceOptions> m_opaque_sp;
+};
+
+/// \class PTDecoder
+/// This class makes use of Intel(R) Processor Trace hardware feature
+/// (implememted inside LLDB) to gather trace data for an inferior (being
+/// debugged with LLDB) to provide meaningful information out of it.
+///
+/// Currently the meaningful information comprises of the execution flow
+/// of the inferior (in terms of assembly instructions executed). The class
+/// enables user to:
+/// - start the trace with configuration options for a thread/process,
+/// - stop the trace for a thread/process,
+/// - get the execution flow (assembly instructions) for a thread and
+/// - get trace specific information for a thread
+class PTDecoder {
+public:
+ PTDecoder(lldb::SBDebugger &sbdebugger);
+
+ /// Start Intel(R) Processor Trace on a thread or complete process with
+ /// Intel(R) Processor Trace specific configuration options
+ ///
+ /// \param[in] sbprocess
+ /// A valid process on which this operation will be performed. An error is
+ /// returned in case of an invalid process.
+ ///
+ /// \param[in] sbtraceoptions
+ /// Contains thread id information and configuration options:
+ ///
+ /// For tracing a single thread, provide a valid thread id. If sbprocess
+ /// doesn't contain this thread id, error will be returned. For tracing
+ /// complete process, set it to lldb::LLDB_INVALID_THREAD_ID
+ /// Configuration options comprises of:
+ /// a) trace buffer size, meta data buffer size, TraceType and
+ /// b) All other possible Intel(R) Processor Trace specific configuration
+ /// options (hereafter collectively referred as CUSTOM_OPTIONS), formatted
+ /// as json text i.e. {"Name":Value,"Name":Value,..} inside
+ /// sbtraceoptions, where "Value" should be a 64-bit unsigned integer in
+ /// hex format. For information regarding what all configuration options
+ /// are currently supported by LLDB and detailed information about
+ /// CUSTOM_OPTIONS usage, please refer to SBProcess::StartTrace() API
+ /// description. To know about all possible configuration options of
+ /// Intel(R) Processor Trace, please refer to Intel(R) 64 and IA-32
+ /// Architectures Software Developer's Manual.
+ ///
+ /// TraceType should be set to lldb::TraceType::eTraceTypeProcessorTrace,
+ /// else error is returned. To find out any other requirement to start
+ /// tracing successfully, please refer to SBProcess::StartTrace() API
+ /// description. LLDB's current implementation of Intel(R) Processor Trace
+ /// feature may round off invalid values for configuration options.
+ /// Therefore, the configuration options with which the trace was actually
+ /// started, might be different to the ones with which trace was asked to
+ /// be started by user. The actual used configuration options can be
+ /// obtained from GetProcessorTraceInfo() API.
+ ///
+ /// \param[out] sberror
+ /// An error with the failure reason if API fails. Else success.
+ void StartProcessorTrace(lldb::SBProcess &sbprocess,
+ lldb::SBTraceOptions &sbtraceoptions,
+ lldb::SBError &sberror);
+
+ /// Stop Intel(R) Processor Trace on a thread or complete process.
+ ///
+ /// \param[in] sbprocess
+ /// A valid process on which this operation will be performed. An error is
+ /// returned in case of an invalid process.
+ ///
+ /// \param[in] tid
+ /// Case 1: To stop tracing a single thread, provide a valid thread id. If
+ /// sbprocess doesn't contain the thread tid, error will be returned.
+ /// Case 2: To stop tracing complete process, use
+ /// lldb::LLDB_INVALID_THREAD_ID.
+ ///
+ /// \param[out] sberror
+ /// An error with the failure reason if API fails. Else success.
+ void StopProcessorTrace(lldb::SBProcess &sbprocess, lldb::SBError &sberror,
+ lldb::tid_t tid = LLDB_INVALID_THREAD_ID);
+
+ /// Get instruction log containing the execution flow for a thread of a
+ /// process in terms of assembly instructions executed.
+ ///
+ /// \param[in] sbprocess
+ /// A valid process on which this operation will be performed. An error is
+ /// returned in case of an invalid process.
+ ///
+ /// \param[in] tid
+ /// A valid thread id of the thread for which instruction log is desired.
+ /// If sbprocess doesn't contain the thread tid, error will be returned.
+ ///
+ /// \param[in] count
+ /// The number of instructions requested by the user to be returned from
+ /// the complete instruction log. Complete instruction log refers to all
+ /// the assembly instructions obtained after decoding the complete raw
+ /// trace data obtained from LLDB. The length of the complete instruction
+ /// log is dependent on the trace buffer size with which processor tracing
+ /// was started for this thread.
+ /// The number of instructions actually returned are dependent on 'count'
+ /// and 'offset' parameters of this API.
+ ///
+ /// \param[in] offset
+ /// The offset in the complete instruction log from where 'count' number
+ /// of instructions are requested by the user. offset is counted from the
+ /// end of of this complete instruction log (which means the last executed
+ /// instruction is at offset 0 (zero)).
+ ///
+ /// \param[out] result_list
+ /// Depending upon 'count' and 'offset' values, list will be overwritten
+ /// with the new instructions.
+ ///
+ /// \param[out] sberror
+ /// An error with the failure reason if API fails. Else success.
+ void GetInstructionLogAtOffset(lldb::SBProcess &sbprocess, lldb::tid_t tid,
+ uint32_t offset, uint32_t count,
+ PTInstructionList &result_list,
+ lldb::SBError &sberror);
+
+ /// Get Intel(R) Processor Trace specific information for a thread of a
+ /// process. The information contains the actual configuration options with
+ /// which the trace was started for this thread.
+ ///
+ /// \param[in] sbprocess
+ /// A valid process on which this operation will be performed. An error is
+ /// returned in case of an invalid process.
+ ///
+ /// \param[in] tid
+ /// A valid thread id of the thread for which the trace specific
+ /// information is required. If sbprocess doesn't contain the thread tid,
+ /// an error will be returned.
+ ///
+ /// \param[out] options
+ /// Contains actual configuration options (they may be different to the
+ /// ones with which tracing was asked to be started for this thread during
+ /// StartProcessorTrace() API call).
+ ///
+ /// \param[out] sberror
+ /// An error with the failure reason if API fails. Else success.
+ void GetProcessorTraceInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid,
+ PTTraceOptions &options, lldb::SBError &sberror);
+
+private:
+ std::shared_ptr<ptdecoder_private::Decoder> m_opaque_sp;
+};
+
+} // namespace ptdecoder
+#endif // PTDecoder_h_
diff --git a/gnu/llvm/lldb/tools/intel-features/intel-pt/README_CLI.txt b/gnu/llvm/lldb/tools/intel-features/intel-pt/README_CLI.txt
new file mode 100644
index 00000000000..2a497c4b3ef
--- /dev/null
+++ b/gnu/llvm/lldb/tools/intel-features/intel-pt/README_CLI.txt
@@ -0,0 +1,123 @@
+****************************************************************************
+* README *
+* *
+* This file provides all the information regarding 4 new CLI commands that *
+* enable using Intel(R) Processor Trace Tool from LLDB's CLI. *
+****************************************************************************
+
+
+============
+Introduction
+============
+A C++ based cli wrapper has been developed to use Intel(R) Processor Trace Tool
+through LLDB's command line. This also provides an idea to all developers on how
+to integrate the Tool into various IDEs providing LLDB as a debugger.
+
+
+
+============
+How to Build
+============
+The wrapper cli-wrapper-pt.cpp needs to be compiled and linked with the shared
+library of the Intel(R) Processor Trace Tool in order to be used through LLDB's
+CLI. The procedure to build shared library of the Intel(R) Processor Trace Tool
+is given in README_TOOL.txt file.
+
+
+
+============
+How to Use
+============
+All these commands are available via shared library (lldbIntelFeatures)
+obtained after building intel-features folder from top. Please refer to
+cli-wrapper.cpp and README files of "intel-features" folder for this purpose.
+
+
+
+============
+Description
+============
+4 CLI commands have been designed keeping the LLDB's existing CLI command syntax
+in mind.
+
+ 1) processor-trace start [-b <buffer-size>] [<thread-index>]
+
+ Start Intel(R) Processor Trace on a specific thread or on the whole process
+
+ Syntax: processor-trace start <cmd-options>
+
+ cmd-options Usage:
+ processor-trace start [-b <buffer-size>] [<thread-index>]
+
+ -b <buffer-size>
+ size of the trace buffer to store the trace data. If not specified
+ then a default value (=4KB) will be taken
+
+ <thread-index>
+ thread index of the thread. If no threads are specified, currently
+ selected thread is taken. Use the thread-index 'all' to start
+ tracing the whole process
+
+
+
+ 2) processor-trace stop [<thread-index>]
+
+ Stop Intel(R) Processor Trace on a specific thread or on the whole process
+
+ Syntax: processor-trace stop <cmd-options>
+
+ cmd-options Usage:
+ processor-trace stop [<thread-index>]
+
+ <thread-index>
+ thread index of the thread. If no threads are specified, currently
+ selected thread is taken. Use the thread-index 'all' to stop
+ tracing the whole process
+
+
+
+ 3) processor-trace show-trace-options [<thread-index>]
+
+ Display all the information regarding Intel(R) Processor Trace for a specific
+ thread or for the whole process. The information contains trace buffer
+ size and configuration options of Intel(R) Processor Trace.
+
+ Syntax: processor-trace show-trace-options <cmd-options>
+
+ cmd-options Usage:
+ processor-trace show-trace-options [<thread-index>]
+
+ <thread-index>
+ thread index of the thread. If no threads are specified, currently
+ selected thread is taken. Use the thread-index 'all' to display
+ information for all threads of the process
+
+
+
+ 4) processor-trace show-instr-log [-o <offset>] [-c <count>] [<thread-index>]
+
+ Display a log of assembly instructions executed for a specific thread or
+ for the whole process. The length of the log to be displayed and the
+ offset in the whole instruction log from where the log needs to be
+ displayed can also be provided. The offset is counted from the end of this
+ whole instruction log which means the last executed instruction is at
+ offset 0 (zero).
+
+ Syntax: processor-trace show-instr-log <cmd-options>
+
+ cmd-options Usage:
+ processor-trace show-instr-log [-o <offset>] [-c <count>] [<thread-index>]
+
+ -c <count>
+ number of instructions to be displayed. If not specified then a
+ default value (=10) will be taken
+
+ -o <offset>
+ offset in the whole instruction log from where the log will be
+ displayed. If not specified then default value is calculated as
+ offset = count -1
+
+ <thread-index>
+ thread index of the thread. If no threads are specified, currently
+ selected thread is taken. Use the thread-index 'all' to show
+ instruction log for all the threads of the process
diff --git a/gnu/llvm/lldb/tools/intel-features/intel-pt/README_TOOL.txt b/gnu/llvm/lldb/tools/intel-features/intel-pt/README_TOOL.txt
new file mode 100644
index 00000000000..d1ec1caf73c
--- /dev/null
+++ b/gnu/llvm/lldb/tools/intel-features/intel-pt/README_TOOL.txt
@@ -0,0 +1,311 @@
+*******************************************************************************
+* README *
+* *
+* This file provides all the information regarding Intel(R) Processor Trace *
+* Tool. It consists explanation about how Tool internally works, its hardware *
+* and software dependencies, build procedure and usage of the API. *
+*******************************************************************************
+
+
+
+============
+Introduction
+============
+The Intel(R) Processor Trace Tool is developed on top of LLDB and provides its
+its users execution trace of the debugged applications. Tool makes use of
+Intel(R) Processor Trace hardware feature implementation inside LLDB for this
+purpose. This hardware feature generates a set of trace packets that
+encapsulates program flow information. These trace packets along with the binary
+of the application can be decoded with the help of a software decoder to
+construct the execution trace of the application.
+
+More information about Intel(R) Processor Trace feature can be obtained from
+website: https://software.intel.com/en-us/blogs/2013/09/18/processor-tracing
+
+
+
+
+=========
+Details
+=========
+The functionality of the Tool consists three parts:
+
+1. Raw Trace Collection from LLDB
+ With the help of API of this Tool (given below), Intel(R) Processor Trace
+ can be started on the application being debugged with LLDB. The generated
+ trace of the application is gathered inside LLDB and is collected by the
+ Tool from LLDB through LLDB's public API.
+
+2. Raw Trace Decoding
+ For decoding the raw trace data, the Tool makes use of "libipt", an
+ Intel(R) Processor Trace Decoder Library. The library needs binary of
+ the application and information about the cpu on which the application is
+ running in order to decode the raw trace. The Tool gathers this
+ information from LLDB public API and provide it to "libipt". More
+ information about "libipt" can be found at:
+ https://software.intel.com/en-us/blogs/2013/09/18/processor-tracing and
+ https://github.com/01org/processor-trace
+
+3. Decoded Trace Post-processing
+ The decoded trace is post-processed to reconstruct the execution flow of
+ the application. The execution flow contains the list of assembly
+ instructions (called instruction log hereafter).
+
+
+
+
+=============
+Dependencies
+=============
+The Tool has following hardware and software dependencies:
+
+ - Hardware dependency: The Tool makes use of this hardware feature to capture
+ raw trace of an application from LLDB. This hardware feature may not be
+ present in all processors. The hardware feature is supported on Broadwell
+ and other succeeding CPUs such as Skylake etc. In order for Tool to provide
+ something meaningful, the target machine on which the application is running
+ should have this feature.
+
+ - Software dependency: The Tool has an indirect dependency on the Operating
+ System level software support for Intel(R) Processor Trace on the target
+ machine where the application is running and being debugged by LLDB. This
+ support is required to enable raw trace generation on the target machine.
+ Currently, the Tool works for applications running on Linux OS as till now
+ the Operating System level support for the feature is present only in Linux
+ (more specifically starting from the 4.1 kernel). In Linux, this feature is
+ implemented in perf_events subsystem and is usable through perf_event_open
+ system call. In the User space level, the Tool has a direct dependency on
+ "libipt" to decode the captured raw trace. This library might be
+ pre-installed on host systems. If not then the library can be built from
+ its sources (available at): https://github.com/01org/processor-trace
+
+
+
+
+============
+How to Build
+============
+The Tool has a cmake based build and can be built by specifying some extra flags
+while building LLDB with cmake. The following cmake flags need to be provided to
+build the Tool:
+
+ - LIBIPT_INCLUDE_PATH - The flag specifies the directory where the header
+ file of "libipt" resides. If the library is not pre-installed on the host
+ system and is built directly from "libipt" project sources then this file
+ may either come as a part of the sources itself or will be generated in
+ build folder while building library.
+
+ - LIBIPT_LIBRARY_PATH - The flag points to the location of "libipt" shared
+ library.
+
+The Tool currently works successfully with following versions of this library:
+ - v1.4, v1.5, v1.6
+
+
+
+============
+How to Use
+============
+The Tool's API are exposed as a C++ object oriented interface (file PTDecoder.h)
+in a shared library. The main class that implements the whole functionality is
+PTDecoder. This class makes use of 3 other classes,
+ - PTInstruction to represent an assembly instruction
+ - PTInstructionList to return instruction log
+ - PTTraceOptions to return trace specific information
+The users can use these API to develop their own products. All API are also
+available as python functions through a script bridging interface, allowing
+them to be used directly from python either interactively or to build python
+apps.
+
+Currently, cli wrapper has been developed on top of the Tool to use it through
+LLDB's command line. Please refer to README_CLI.txt file for command line usage.
+
+
+A brief introduction about the classes and their API are given below.
+
+ class PTDecoder
+ ===============
+ This class makes use of Intel(R) Processor Trace hardware feature
+ (implemented inside LLDB) to gather trace data for an inferior (being
+ debugged with LLDB) to provide meaningful information out of it. Currently
+ the meaningful information comprises of the execution flow of the inferior
+ (in terms of assembly instructions executed). The class enables user to:
+
+ - start the trace with configuration options for a thread/process,
+ - stop the trace for a thread/process,
+ - get the execution flow (assembly instructions) for a thread and
+ - get trace specific information for a thread
+
+ Corresponding API are explained below:
+ a) void StartProcessorTrace(lldb::SBProcess &sbprocess,
+ lldb::SBTraceOptions &sbtraceoptions,
+ lldb::SBError &sberror)
+ ------------------------------------------------------------------------
+ This API allows the user to start trace on a particular thread or on
+ the whole process with Intel(R) Processor Trace specific
+ configuration options.
+
+ @param[in] sbprocess : A valid process on which this operation
+ will be performed. An error is returned in case of an invalid
+ process.
+
+ @param[out] sberror : An error with the failure reason if API
+ fails. Else success.
+
+ @param[in] sbtraceoptions : Contains thread id information and
+ configuration options:
+ For tracing a single thread, provide a valid thread id. If
+ sbprocess doesn't contain this thread id, error will be returned.
+ For tracing complete process, set to lldb::LLDB_INVALID_THREAD_ID
+ Configuration options comprises of:
+ - trace buffer size, meta data buffer size, TraceType and
+ - All other possible Intel(R) Processor Trace specific
+ configuration options (hereafter collectively referred as
+ CUSTOM_OPTIONS)
+
+ Trace buffer, meant to store the trace data read from target
+ machine, inside LLDB is configured as a cyclic buffer. Hence,
+ depending upon the trace buffer size provided here, buffer
+ overwrites may happen while LLDB writes trace data into it.
+ CUSTOM_OPTIONS are formatted as json text i.e. {"Name":Value,
+ "Name":Value,...} inside sbtraceoptions, where "Value" should be
+ a 64-bit unsigned integer in hex format. For information
+ regarding what all configuration options are currently supported
+ by LLDB and detailed information about CUSTOM_OPTIONS usage,
+ please refer to SBProcess::StartTrace() API description. An
+ overview of some of the various CUSTOM_OPTIONS are briefly given
+ below. Please refer to "Intel(R) 64 and IA-32 Architectures
+ Software Developer's Manual" for more details about them.
+ - CYCEn Enable/Disable Cycle Count Packet (CYC) Packet
+ - OS Packet generation enabled/disabled if
+ Current Privilege Level (CPL)=0
+ - User Packet generation enabled/disabled if CPL>0
+ - CR3Filter Enable/Disable CR3 Filtering
+ - MTCEn Enable/disable MTC packets
+ - TSCEn Enable/disable TSC packets
+ - DisRETC Enable/disable RET Compression
+ - BranchEn Enable/disable COFI-based packets
+ - MTCFreq Defines MTC Packet Frequency
+ - CycThresh CYC Packet threshold
+ - PSBFreq Frequency of PSB Packets
+
+ TraceType should be set to
+ lldb::TraceType::eTraceTypeProcessorTrace, else error is
+ returned. To find out any other requirement to start tracing
+ successfully, refer to SBProcess::StartTrace() API description.
+ LLDB's current implementation of Intel(R) Processor Trace
+ feature may round off invalid values for configuration options.
+ Therefore, the configuration options with which the trace was
+ actually started, might be different to the ones with which
+ trace was asked to be started by user. The actual used
+ configuration options can be obtained from
+ GetProcessorTraceInfo() API.
+
+
+
+ b) void StopProcessorTrace(lldb::SBProcess &sbprocess,
+ lldb::SBError &sberror,
+ lldb::tid_t tid = LLDB_INVALID_THREAD_ID)
+ ------------------------------------------------------------------------
+ This API allows the user to Stop trace on a particular thread or on
+ the whole process.
+
+ @param[in] sbprocess : A valid process on which this operation will
+ be performed. An error is returned in case of an invalid process.
+
+ @param[in] tid : To stop tracing a single thread, provide a
+ valid thread id. If sbprocess doesn't contain the thread tid,
+ error will be returned. To stop tracing complete process, use
+ lldb::LLDB_INVALID_THREAD_ID
+
+ @param[out] sberror : An error with the failure reason if API fails.
+ Else success
+
+
+
+ c) void GetInstructionLogAtOffset(lldb::SBProcess &sbprocess, lldb::tid_t tid,
+ uint32_t offset, uint32_t count,
+ PTInstructionList &result_list,
+ lldb::SBError &sberror)
+ ------------------------------------------------------------------------
+ This API provides instruction log that contains the execution flow
+ for a thread of a process in terms of assembly instruction executed.
+ The API works on only 1 thread at a time. To gather this information
+ for whole process, this API needs to be called for each thread.
+
+ @param[in] sbprocess : A valid process on which this operation
+ will be performed. An error is returned in case of an invalid
+ process.
+
+ @param[in] tid : A valid thread id of the thread for which
+ instruction log is desired. If sbprocess doesn't contain the
+ thread tid, error will be returned.
+
+ @param[in] count : Number of instructions requested by the
+ user to be returned from the complete instruction log. Complete
+ instruction log refers to all the assembly instructions obtained
+ after decoding the complete raw trace data obtained from LLDB.
+ The length of the complete instruction log is dependent on the
+ trace buffer size with which processor tracing was started for
+ this thread.
+ The number of instructions actually returned are dependent on
+ 'count' and 'offset' parameters of this API.
+
+ @param[in] offset : The offset in the complete instruction log
+ from where 'count' number of instructions are requested by the
+ user. offset is counted from the end of of this complete
+ instruction log (which means the last executed instruction
+ is at offset 0 (zero)).
+
+ @param[out] result_list : Depending upon 'count' and 'offset' values,
+ list will be overwritten with the instructions.
+
+ @param[out] sberror : An error with the failure reason if API
+ fails. Else success
+
+
+
+ d) void GetProcessorTraceInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid,
+ PTTraceOptions &options, lldb::SBError &sberror)
+ ------------------------------------------------------------------------
+ This API provides Intel(R) Processor Trace specific information for
+ a thread of a process. The API works on only 1 thread at a time. To
+ gather this information for whole process, this API needs to be
+ called for each thread. The information contains the actual
+ configuration options with which the trace was started for this
+ thread.
+
+ @param[in] sbprocess : The valid process on which this operation
+ will be performed. An error is returned in case of an invalid
+ process.
+
+ @param[in] tid : A valid thread id of the thread for which the
+ trace specific information is required. If sbprocess doesn't
+ contain the thread tid, an error will be returned.
+
+ @param[out] options : Contains actual configuration options (they
+ may be different to the ones with which tracing was asked to be
+ started for this thread during StartProcessorTrace() API call).
+
+ @param[out] sberror : An error with the failure reason if API
+ fails. Else success
+
+
+ class PTInstruction
+ ===================
+ This class represents an assembly instruction containing raw instruction
+ bytes, instruction address along with execution flow context and
+ Intel(R) Processor Trace context. For more details, please refer to
+ PTDecoder.h file.
+
+ class PTInstructionList
+ =======================
+ This class represents a list of assembly instructions. Each assembly
+ instruction is of type PTInstruction.
+
+ class PTTraceOptions
+ ====================
+ This class provides Intel(R) Processor Trace specific configuration
+ options like trace type, trace buffer size, meta data buffer size along
+ with other trace specific options. For more details, please refer to
+ PTDecoder.h file.
diff --git a/gnu/llvm/lldb/tools/intel-features/intel-pt/cli-wrapper-pt.cpp b/gnu/llvm/lldb/tools/intel-features/intel-pt/cli-wrapper-pt.cpp
new file mode 100644
index 00000000000..8db1c0f82d6
--- /dev/null
+++ b/gnu/llvm/lldb/tools/intel-features/intel-pt/cli-wrapper-pt.cpp
@@ -0,0 +1,582 @@
+//===-- cli-wrapper-pt.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// CLI Wrapper of PTDecoder Tool to enable it to be used through LLDB's CLI. The
+// wrapper provides a new command called processor-trace with 4 child
+// subcommands as follows:
+// processor-trace start
+// processor-trace stop
+// processor-trace show-trace-options
+// processor-trace show-instr-log
+//
+//===----------------------------------------------------------------------===//
+
+#include <cerrno>
+#include <cinttypes>
+#include <cstring>
+#include <string>
+#include <vector>
+
+#include "PTDecoder.h"
+#include "cli-wrapper-pt.h"
+#include "lldb/API/SBCommandInterpreter.h"
+#include "lldb/API/SBCommandReturnObject.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBProcess.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBStructuredData.h"
+#include "lldb/API/SBTarget.h"
+#include "lldb/API/SBThread.h"
+
+static bool GetProcess(lldb::SBDebugger &debugger,
+ lldb::SBCommandReturnObject &result,
+ lldb::SBProcess &process) {
+ if (!debugger.IsValid()) {
+ result.Printf("error: invalid debugger\n");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+
+ lldb::SBTarget target = debugger.GetSelectedTarget();
+ if (!target.IsValid()) {
+ result.Printf("error: invalid target inside debugger\n");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+
+ process = target.GetProcess();
+ if (!process.IsValid() ||
+ (process.GetState() == lldb::StateType::eStateDetached) ||
+ (process.GetState() == lldb::StateType::eStateExited) ||
+ (process.GetState() == lldb::StateType::eStateInvalid)) {
+ result.Printf("error: invalid process inside debugger's target\n");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+
+ return true;
+}
+
+static bool ParseCommandOption(char **command,
+ lldb::SBCommandReturnObject &result,
+ uint32_t &index, const std::string &arg,
+ uint32_t &parsed_result) {
+ char *endptr;
+ if (!command[++index]) {
+ result.Printf("error: option \"%s\" requires an argument\n", arg.c_str());
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+
+ errno = 0;
+ unsigned long output = strtoul(command[index], &endptr, 0);
+ if ((errno != 0) || (*endptr != '\0')) {
+ result.Printf("error: invalid value \"%s\" provided for option \"%s\"\n",
+ command[index], arg.c_str());
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+ if (output > UINT32_MAX) {
+ result.Printf("error: value \"%s\" for option \"%s\" exceeds UINT32_MAX\n",
+ command[index], arg.c_str());
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+ parsed_result = (uint32_t)output;
+ return true;
+}
+
+static bool ParseCommandArgThread(char **command,
+ lldb::SBCommandReturnObject &result,
+ lldb::SBProcess &process, uint32_t &index,
+ lldb::tid_t &thread_id) {
+ char *endptr;
+ if (!strcmp(command[index], "all"))
+ thread_id = LLDB_INVALID_THREAD_ID;
+ else {
+ uint32_t thread_index_id;
+ errno = 0;
+ unsigned long output = strtoul(command[index], &endptr, 0);
+ if ((errno != 0) || (*endptr != '\0') || (output > UINT32_MAX)) {
+ result.Printf("error: invalid thread specification: \"%s\"\n",
+ command[index]);
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+ thread_index_id = (uint32_t)output;
+
+ lldb::SBThread thread = process.GetThreadByIndexID(thread_index_id);
+ if (!thread.IsValid()) {
+ result.Printf(
+ "error: process has no thread with thread specification: \"%s\"\n",
+ command[index]);
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+ thread_id = thread.GetThreadID();
+ }
+ return true;
+}
+
+class ProcessorTraceStart : public lldb::SBCommandPluginInterface {
+public:
+ ProcessorTraceStart(std::shared_ptr<ptdecoder::PTDecoder> &pt_decoder)
+ : SBCommandPluginInterface(), pt_decoder_sp(pt_decoder) {}
+
+ ~ProcessorTraceStart() {}
+
+ virtual bool DoExecute(lldb::SBDebugger debugger, char **command,
+ lldb::SBCommandReturnObject &result) {
+ lldb::SBProcess process;
+ lldb::SBThread thread;
+ if (!GetProcess(debugger, result, process))
+ return false;
+
+ // Default initialize API's arguments
+ lldb::SBTraceOptions lldb_SBTraceOptions;
+ uint32_t trace_buffer_size = m_default_trace_buff_size;
+ lldb::tid_t thread_id;
+
+ // Parse Command line options
+ bool thread_argument_provided = false;
+ if (command) {
+ for (uint32_t i = 0; command[i]; i++) {
+ if (!strcmp(command[i], "-b")) {
+ if (!ParseCommandOption(command, result, i, "-b", trace_buffer_size))
+ return false;
+ } else {
+ thread_argument_provided = true;
+ if (!ParseCommandArgThread(command, result, process, i, thread_id))
+ return false;
+ }
+ }
+ }
+
+ if (!thread_argument_provided) {
+ thread = process.GetSelectedThread();
+ if (!thread.IsValid()) {
+ result.Printf("error: invalid current selected thread\n");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+ thread_id = thread.GetThreadID();
+ }
+
+ if (trace_buffer_size > m_max_trace_buff_size)
+ trace_buffer_size = m_max_trace_buff_size;
+
+ // Set API's arguments with parsed values
+ lldb_SBTraceOptions.setType(lldb::TraceType::eTraceTypeProcessorTrace);
+ lldb_SBTraceOptions.setTraceBufferSize(trace_buffer_size);
+ lldb_SBTraceOptions.setMetaDataBufferSize(0);
+ lldb_SBTraceOptions.setThreadID(thread_id);
+ lldb::SBStream sb_stream;
+ sb_stream.Printf("{\"trace-tech\":\"intel-pt\"}");
+ lldb::SBStructuredData custom_params;
+ lldb::SBError error = custom_params.SetFromJSON(sb_stream);
+ if (!error.Success()) {
+ result.Printf("error: %s\n", error.GetCString());
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+ lldb_SBTraceOptions.setTraceParams(custom_params);
+
+ // Start trace
+ pt_decoder_sp->StartProcessorTrace(process, lldb_SBTraceOptions, error);
+ if (!error.Success()) {
+ result.Printf("error: %s\n", error.GetCString());
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+ return true;
+ }
+
+private:
+ std::shared_ptr<ptdecoder::PTDecoder> pt_decoder_sp;
+ const uint32_t m_max_trace_buff_size = 0x3fff;
+ const uint32_t m_default_trace_buff_size = 4096;
+};
+
+class ProcessorTraceInfo : public lldb::SBCommandPluginInterface {
+public:
+ ProcessorTraceInfo(std::shared_ptr<ptdecoder::PTDecoder> &pt_decoder)
+ : SBCommandPluginInterface(), pt_decoder_sp(pt_decoder) {}
+
+ ~ProcessorTraceInfo() {}
+
+ virtual bool DoExecute(lldb::SBDebugger debugger, char **command,
+ lldb::SBCommandReturnObject &result) {
+ lldb::SBProcess process;
+ lldb::SBThread thread;
+ if (!GetProcess(debugger, result, process))
+ return false;
+
+ lldb::tid_t thread_id;
+
+ // Parse command line options
+ bool thread_argument_provided = false;
+ if (command) {
+ for (uint32_t i = 0; command[i]; i++) {
+ thread_argument_provided = true;
+ if (!ParseCommandArgThread(command, result, process, i, thread_id))
+ return false;
+ }
+ }
+
+ if (!thread_argument_provided) {
+ thread = process.GetSelectedThread();
+ if (!thread.IsValid()) {
+ result.Printf("error: invalid current selected thread\n");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+ thread_id = thread.GetThreadID();
+ }
+
+ size_t loop_count = 1;
+ bool entire_process_tracing = false;
+ if (thread_id == LLDB_INVALID_THREAD_ID) {
+ entire_process_tracing = true;
+ loop_count = process.GetNumThreads();
+ }
+
+ // Get trace information
+ lldb::SBError error;
+ lldb::SBCommandReturnObject res;
+ for (size_t i = 0; i < loop_count; i++) {
+ error.Clear();
+ res.Clear();
+
+ if (entire_process_tracing)
+ thread = process.GetThreadAtIndex(i);
+ else
+ thread = process.GetThreadByID(thread_id);
+ thread_id = thread.GetThreadID();
+
+ ptdecoder::PTTraceOptions options;
+ pt_decoder_sp->GetProcessorTraceInfo(process, thread_id, options, error);
+ if (!error.Success()) {
+ res.Printf("thread #%" PRIu32 ": tid=%" PRIu64 ", error: %s",
+ thread.GetIndexID(), thread_id, error.GetCString());
+ result.AppendMessage(res.GetOutput());
+ continue;
+ }
+
+ lldb::SBStructuredData data = options.GetTraceParams(error);
+ if (!error.Success()) {
+ res.Printf("thread #%" PRIu32 ": tid=%" PRIu64 ", error: %s",
+ thread.GetIndexID(), thread_id, error.GetCString());
+ result.AppendMessage(res.GetOutput());
+ continue;
+ }
+
+ lldb::SBStream s;
+ error = data.GetAsJSON(s);
+ if (!error.Success()) {
+ res.Printf("thread #%" PRIu32 ": tid=%" PRIu64 ", error: %s",
+ thread.GetIndexID(), thread_id, error.GetCString());
+ result.AppendMessage(res.GetOutput());
+ continue;
+ }
+
+ res.Printf("thread #%" PRIu32 ": tid=%" PRIu64
+ ", trace buffer size=%" PRIu64 ", meta buffer size=%" PRIu64
+ ", trace type=%" PRIu32 ", custom trace params=%s",
+ thread.GetIndexID(), thread_id, options.GetTraceBufferSize(),
+ options.GetMetaDataBufferSize(), options.GetType(),
+ s.GetData());
+ result.AppendMessage(res.GetOutput());
+ }
+ return true;
+ }
+
+private:
+ std::shared_ptr<ptdecoder::PTDecoder> pt_decoder_sp;
+};
+
+class ProcessorTraceShowInstrLog : public lldb::SBCommandPluginInterface {
+public:
+ ProcessorTraceShowInstrLog(std::shared_ptr<ptdecoder::PTDecoder> &pt_decoder)
+ : SBCommandPluginInterface(), pt_decoder_sp(pt_decoder) {}
+
+ ~ProcessorTraceShowInstrLog() {}
+
+ virtual bool DoExecute(lldb::SBDebugger debugger, char **command,
+ lldb::SBCommandReturnObject &result) {
+ lldb::SBProcess process;
+ lldb::SBThread thread;
+ if (!GetProcess(debugger, result, process))
+ return false;
+
+ // Default initialize API's arguments
+ uint32_t offset;
+ bool offset_provided = false;
+ uint32_t count = m_default_count;
+ lldb::tid_t thread_id;
+
+ // Parse command line options
+ bool thread_argument_provided = false;
+ if (command) {
+ for (uint32_t i = 0; command[i]; i++) {
+ if (!strcmp(command[i], "-o")) {
+ if (!ParseCommandOption(command, result, i, "-o", offset))
+ return false;
+ offset_provided = true;
+ } else if (!strcmp(command[i], "-c")) {
+ if (!ParseCommandOption(command, result, i, "-c", count))
+ return false;
+ } else {
+ thread_argument_provided = true;
+ if (!ParseCommandArgThread(command, result, process, i, thread_id))
+ return false;
+ }
+ }
+ }
+
+ if (!thread_argument_provided) {
+ thread = process.GetSelectedThread();
+ if (!thread.IsValid()) {
+ result.Printf("error: invalid current selected thread\n");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+ thread_id = thread.GetThreadID();
+ }
+
+ size_t loop_count = 1;
+ bool entire_process_tracing = false;
+ if (thread_id == LLDB_INVALID_THREAD_ID) {
+ entire_process_tracing = true;
+ loop_count = process.GetNumThreads();
+ }
+
+ // Get instruction log and disassemble it
+ lldb::SBError error;
+ lldb::SBCommandReturnObject res;
+ for (size_t i = 0; i < loop_count; i++) {
+ error.Clear();
+ res.Clear();
+
+ if (entire_process_tracing)
+ thread = process.GetThreadAtIndex(i);
+ else
+ thread = process.GetThreadByID(thread_id);
+ thread_id = thread.GetThreadID();
+
+ // If offset is not provided then calculate a default offset (to display
+ // last 'count' number of instructions)
+ if (!offset_provided)
+ offset = count - 1;
+
+ // Get the instruction log
+ ptdecoder::PTInstructionList insn_list;
+ pt_decoder_sp->GetInstructionLogAtOffset(process, thread_id, offset,
+ count, insn_list, error);
+ if (!error.Success()) {
+ res.Printf("thread #%" PRIu32 ": tid=%" PRIu64 ", error: %s",
+ thread.GetIndexID(), thread_id, error.GetCString());
+ result.AppendMessage(res.GetOutput());
+ continue;
+ }
+
+ // Disassemble the instruction log
+ std::string disassembler_command("dis -c 1 -s ");
+ res.Printf("thread #%" PRIu32 ": tid=%" PRIu64 "\n", thread.GetIndexID(),
+ thread_id);
+ lldb::SBCommandInterpreter sb_cmnd_interpreter(
+ debugger.GetCommandInterpreter());
+ lldb::SBCommandReturnObject result_obj;
+ for (size_t i = 0; i < insn_list.GetSize(); i++) {
+ ptdecoder::PTInstruction insn = insn_list.GetInstructionAtIndex(i);
+ uint64_t addr = insn.GetInsnAddress();
+ std::string error = insn.GetError();
+ if (!error.empty()) {
+ res.AppendMessage(error.c_str());
+ continue;
+ }
+
+ result_obj.Clear();
+ std::string complete_disassembler_command =
+ disassembler_command + std::to_string(addr);
+ sb_cmnd_interpreter.HandleCommand(complete_disassembler_command.c_str(),
+ result_obj, false);
+ std::string result_str(result_obj.GetOutput());
+ if (result_str.empty()) {
+ lldb::SBCommandReturnObject output;
+ output.Printf(" Disassembly not found for address: %" PRIu64, addr);
+ res.AppendMessage(output.GetOutput());
+ continue;
+ }
+
+ // LLDB's disassemble command displays assembly instructions along with
+ // the names of the functions they belong to. Parse this result to
+ // display only the assembly instructions and not the function names
+ // in an instruction log
+ std::size_t first_new_line_index = result_str.find_first_of('\n');
+ std::size_t last_new_line_index = result_str.find_last_of('\n');
+ if (first_new_line_index != last_new_line_index)
+ res.AppendMessage((result_str.substr(first_new_line_index + 1,
+ last_new_line_index -
+ first_new_line_index - 1))
+ .c_str());
+ else
+ res.AppendMessage(
+ (result_str.substr(0, result_str.length() - 1)).c_str());
+ }
+ result.AppendMessage(res.GetOutput());
+ }
+ return true;
+ }
+
+private:
+ std::shared_ptr<ptdecoder::PTDecoder> pt_decoder_sp;
+ const uint32_t m_default_count = 10;
+};
+
+class ProcessorTraceStop : public lldb::SBCommandPluginInterface {
+public:
+ ProcessorTraceStop(std::shared_ptr<ptdecoder::PTDecoder> &pt_decoder)
+ : SBCommandPluginInterface(), pt_decoder_sp(pt_decoder) {}
+
+ ~ProcessorTraceStop() {}
+
+ virtual bool DoExecute(lldb::SBDebugger debugger, char **command,
+ lldb::SBCommandReturnObject &result) {
+ lldb::SBProcess process;
+ lldb::SBThread thread;
+ if (!GetProcess(debugger, result, process))
+ return false;
+
+ lldb::tid_t thread_id;
+
+ // Parse command line options
+ bool thread_argument_provided = false;
+ if (command) {
+ for (uint32_t i = 0; command[i]; i++) {
+ thread_argument_provided = true;
+ if (!ParseCommandArgThread(command, result, process, i, thread_id))
+ return false;
+ }
+ }
+
+ if (!thread_argument_provided) {
+ thread = process.GetSelectedThread();
+ if (!thread.IsValid()) {
+ result.Printf("error: invalid current selected thread\n");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+ thread_id = thread.GetThreadID();
+ }
+
+ // Stop trace
+ lldb::SBError error;
+ pt_decoder_sp->StopProcessorTrace(process, error, thread_id);
+ if (!error.Success()) {
+ result.Printf("error: %s\n", error.GetCString());
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+ return true;
+ }
+
+private:
+ std::shared_ptr<ptdecoder::PTDecoder> pt_decoder_sp;
+};
+
+bool PTPluginInitialize(lldb::SBDebugger &debugger) {
+ lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter();
+ lldb::SBCommand proc_trace = interpreter.AddMultiwordCommand(
+ "processor-trace", "Intel(R) Processor Trace for thread/process");
+
+ std::shared_ptr<ptdecoder::PTDecoder> PTDecoderSP(
+ new ptdecoder::PTDecoder(debugger));
+
+ lldb::SBCommandPluginInterface *proc_trace_start =
+ new ProcessorTraceStart(PTDecoderSP);
+ const char *help_proc_trace_start = "start Intel(R) Processor Trace on a "
+ "specific thread or on the whole process";
+ const char *syntax_proc_trace_start =
+ "processor-trace start <cmd-options>\n\n"
+ "\rcmd-options Usage:\n"
+ "\r processor-trace start [-b <buffer-size>] [<thread-index>]\n\n"
+ "\t\b-b <buffer-size>\n"
+ "\t size of the trace buffer to store the trace data. If not "
+ "specified then a default value will be taken\n\n"
+ "\t\b<thread-index>\n"
+ "\t thread index of the thread. If no threads are specified, "
+ "currently selected thread is taken.\n"
+ "\t Use the thread-index 'all' to start tracing the whole process\n";
+ proc_trace.AddCommand("start", proc_trace_start, help_proc_trace_start,
+ syntax_proc_trace_start);
+
+ lldb::SBCommandPluginInterface *proc_trace_stop =
+ new ProcessorTraceStop(PTDecoderSP);
+ const char *help_proc_trace_stop =
+ "stop Intel(R) Processor Trace on a specific thread or on whole process";
+ const char *syntax_proc_trace_stop =
+ "processor-trace stop <cmd-options>\n\n"
+ "\rcmd-options Usage:\n"
+ "\r processor-trace stop [<thread-index>]\n\n"
+ "\t\b<thread-index>\n"
+ "\t thread index of the thread. If no threads are specified, "
+ "currently selected thread is taken.\n"
+ "\t Use the thread-index 'all' to stop tracing the whole process\n";
+ proc_trace.AddCommand("stop", proc_trace_stop, help_proc_trace_stop,
+ syntax_proc_trace_stop);
+
+ lldb::SBCommandPluginInterface *proc_trace_show_instr_log =
+ new ProcessorTraceShowInstrLog(PTDecoderSP);
+ const char *help_proc_trace_show_instr_log =
+ "display a log of assembly instructions executed for a specific thread "
+ "or for the whole process.\n"
+ "The length of the log to be displayed and the offset in the whole "
+ "instruction log from where the log needs to be displayed can also be "
+ "provided. The offset is counted from the end of this whole "
+ "instruction log which means the last executed instruction is at offset "
+ "0 (zero)";
+ const char *syntax_proc_trace_show_instr_log =
+ "processor-trace show-instr-log <cmd-options>\n\n"
+ "\rcmd-options Usage:\n"
+ "\r processor-trace show-instr-log [-o <offset>] [-c <count>] "
+ "[<thread-index>]\n\n"
+ "\t\b-o <offset>\n"
+ "\t offset in the whole instruction log from where the log will be "
+ "displayed. If not specified then a default value will be taken\n\n"
+ "\t\b-c <count>\n"
+ "\t number of instructions to be displayed. If not specified then a "
+ "default value will be taken\n\n"
+ "\t\b<thread-index>\n"
+ "\t thread index of the thread. If no threads are specified, "
+ "currently selected thread is taken.\n"
+ "\t Use the thread-index 'all' to show instruction log for all the "
+ "threads of the process\n";
+ proc_trace.AddCommand("show-instr-log", proc_trace_show_instr_log,
+ help_proc_trace_show_instr_log,
+ syntax_proc_trace_show_instr_log);
+
+ lldb::SBCommandPluginInterface *proc_trace_options =
+ new ProcessorTraceInfo(PTDecoderSP);
+ const char *help_proc_trace_show_options =
+ "display all the information regarding Intel(R) Processor Trace for a "
+ "specific thread or for the whole process.\n"
+ "The information contains trace buffer size and configuration options"
+ " of Intel(R) Processor Trace.";
+ const char *syntax_proc_trace_show_options =
+ "processor-trace show-options <cmd-options>\n\n"
+ "\rcmd-options Usage:\n"
+ "\r processor-trace show-options [<thread-index>]\n\n"
+ "\t\b<thread-index>\n"
+ "\t thread index of the thread. If no threads are specified, "
+ "currently selected thread is taken.\n"
+ "\t Use the thread-index 'all' to display information for all threads "
+ "of the process\n";
+ proc_trace.AddCommand("show-trace-options", proc_trace_options,
+ help_proc_trace_show_options,
+ syntax_proc_trace_show_options);
+
+ return true;
+}
diff --git a/gnu/llvm/lldb/tools/intel-features/intel-pt/cli-wrapper-pt.h b/gnu/llvm/lldb/tools/intel-features/intel-pt/cli-wrapper-pt.h
new file mode 100644
index 00000000000..3d2fe69b015
--- /dev/null
+++ b/gnu/llvm/lldb/tools/intel-features/intel-pt/cli-wrapper-pt.h
@@ -0,0 +1,12 @@
+//===-- cli-wrapper-pt.h----------------------------------*- C++ -*-==========//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// CLI Wrapper of PTDecoder Tool to enable it to be used through LLDB's CLI.
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBDebugger.h"
+
+bool PTPluginInitialize(lldb::SBDebugger &debugger);
diff --git a/gnu/llvm/lldb/tools/intel-features/intel-pt/interface/PTDecoder.i b/gnu/llvm/lldb/tools/intel-features/intel-pt/interface/PTDecoder.i
new file mode 100644
index 00000000000..f662b8ff359
--- /dev/null
+++ b/gnu/llvm/lldb/tools/intel-features/intel-pt/interface/PTDecoder.i
@@ -0,0 +1,10 @@
+%include "stdint.i"
+
+%include "lldb/lldb-defines.h"
+%include "lldb/lldb-enumerations.h"
+%include "lldb/lldb-forward.h"
+%include "lldb/lldb-types.h"
+
+%include "lldb/API/SBDefines.h"
+
+%include "../PTDecoder.h"
diff --git a/gnu/llvm/lldb/tools/intel-features/scripts/CMakeLists.txt b/gnu/llvm/lldb/tools/intel-features/scripts/CMakeLists.txt
new file mode 100644
index 00000000000..6df97a4b006
--- /dev/null
+++ b/gnu/llvm/lldb/tools/intel-features/scripts/CMakeLists.txt
@@ -0,0 +1,37 @@
+file(GLOB_RECURSE SWIG_SOURCES *.swig)
+
+set(FLAGS
+ -c++
+ -shadow
+ -python
+ -D__STDC_LIMIT_MACROS
+ -D__STDC_CONSTANT_MACROS
+ )
+
+set(INCLUDES
+ -I${LLDB_SOURCE_DIR}/include
+ -I${LLDB_SOURCE_DIR}/tools/intel-features/intel-pt
+ )
+
+set(OUTPUT_PYTHON_WRAPPER
+ ${CMAKE_CURRENT_BINARY_DIR}/IntelFeaturesPythonWrap.cpp
+ )
+
+set(OUTPUT_PYTHON_SCRIPT_DIR
+ ${CMAKE_CURRENT_BINARY_DIR}
+ )
+
+find_package(SWIG REQUIRED)
+add_custom_command(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/IntelFeaturesPythonWrap.cpp
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lldbIntelFeatures.py
+ DEPENDS ${SWIG_SOURCES}
+ COMMAND ${SWIG_EXECUTABLE} ${FLAGS} ${INCLUDES} -o ${OUTPUT_PYTHON_WRAPPER} -outdir ${OUTPUT_PYTHON_SCRIPT_DIR} ${SWIG_SOURCES}
+ COMMENT "Generating python wrapper for features library")
+
+set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/IntelFeaturesPythonWrap.cpp PROPERTIES GENERATED 1)
+set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/lldbIntelFeatures.py PROPERTIES GENERATED 1)
+
+add_custom_target(intel-features-swig_wrapper ALL
+ DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/IntelFeaturesPythonWrap.cpp
+ )
diff --git a/gnu/llvm/lldb/tools/intel-features/scripts/lldb-intel-features.swig b/gnu/llvm/lldb/tools/intel-features/scripts/lldb-intel-features.swig
new file mode 100644
index 00000000000..c035fb6132d
--- /dev/null
+++ b/gnu/llvm/lldb/tools/intel-features/scripts/lldb-intel-features.swig
@@ -0,0 +1,16 @@
+%module lldbIntelFeatures
+
+%{
+#include "lldb/lldb-public.h"
+#include "intel-pt/PTDecoder.h"
+using namespace ptdecoder;
+%}
+
+/* Undefine GCC keyword to make Swig happy when processing glibc's stdint.h */
+#define __extension__
+
+/* Combined python typemap for all features */
+%include "python-typemaps.txt"
+
+/* Feature specific python interface files*/
+%include "../intel-pt/interface/PTDecoder.i"
diff --git a/gnu/llvm/lldb/tools/intel-features/scripts/python-typemaps.txt b/gnu/llvm/lldb/tools/intel-features/scripts/python-typemaps.txt
new file mode 100644
index 00000000000..888d5321393
--- /dev/null
+++ b/gnu/llvm/lldb/tools/intel-features/scripts/python-typemaps.txt
@@ -0,0 +1,31 @@
+/* Typemap definitions to allow SWIG to properly handle some data types */
+
+// typemap for an incoming buffer
+%typemap(in) (void *buf, size_t size) {
+ if (PyInt_Check($input)) {
+ $2 = PyInt_AsLong($input);
+ } else if (PyLong_Check($input)) {
+ $2 = PyLong_AsLong($input);
+ } else {
+ PyErr_SetString(PyExc_ValueError, "Expecting an integer or long object");
+ return NULL;
+ }
+ if ($2 <= 0) {
+ PyErr_SetString(PyExc_ValueError, "Positive integer expected");
+ return NULL;
+ }
+ $1 = (void *) malloc($2);
+}
+
+// Return the buffer. Discarding any previous return result
+%typemap(argout) (void *buf, size_t size) {
+ Py_XDECREF($result); /* Blow away any previous result */
+ if (result == 0) {
+ $result = Py_None;
+ Py_INCREF($result);
+ } else {
+ PyObject *py_bytes = PyBytes_FromStringAndSize(reinterpret_cast<const char*>($1), result);
+ $result = py_bytes;
+ }
+ free($1);
+}
diff --git a/gnu/llvm/lldb/tools/lldb-instr/CMakeLists.txt b/gnu/llvm/lldb/tools/lldb-instr/CMakeLists.txt
new file mode 100644
index 00000000000..8da453b2894
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-instr/CMakeLists.txt
@@ -0,0 +1,16 @@
+add_lldb_tool(lldb-instr
+ Instrument.cpp
+
+ CLANG_LIBS
+ clangAST
+ clangBasic
+ clangCodeGen
+ clangFrontend
+ clangLex
+ clangRewrite
+ clangSerialization
+ clangTooling
+
+ LINK_COMPONENTS
+ Support
+ )
diff --git a/gnu/llvm/lldb/tools/lldb-instr/Instrument.cpp b/gnu/llvm/lldb/tools/lldb-instr/Instrument.cpp
new file mode 100644
index 00000000000..9b2970030cb
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-instr/Instrument.cpp
@@ -0,0 +1,356 @@
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
+#include "clang/Frontend/ASTConsumers.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Tooling.h"
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <sstream>
+#include <string>
+
+using namespace clang;
+using namespace clang::driver;
+using namespace clang::tooling;
+
+static llvm::cl::OptionCategory InstrCategory("LLDB Instrumentation Generator");
+
+/// Get the macro name for recording method calls.
+///
+/// LLDB_RECORD_METHOD
+/// LLDB_RECORD_METHOD_CONST
+/// LLDB_RECORD_METHOD_NO_ARGS
+/// LLDB_RECORD_METHOD_CONST_NO_ARGS
+/// LLDB_RECORD_STATIC_METHOD
+/// LLDB_RECORD_STATIC_METHOD_NO_ARGS
+static std::string GetRecordMethodMacroName(bool Static, bool Const,
+ bool NoArgs) {
+ std::string Macro;
+ llvm::raw_string_ostream OS(Macro);
+
+ OS << "LLDB_RECORD";
+ if (Static)
+ OS << "_STATIC";
+ OS << "_METHOD";
+ if (Const)
+ OS << "_CONST";
+ if (NoArgs)
+ OS << "_NO_ARGS";
+
+ return OS.str();
+}
+
+/// Get the macro name for register methods.
+///
+/// LLDB_REGISTER_CONSTRUCTOR
+/// LLDB_REGISTER_METHOD
+/// LLDB_REGISTER_METHOD_CONST
+/// LLDB_REGISTER_STATIC_METHOD
+static std::string GetRegisterMethodMacroName(bool Static, bool Const) {
+ std::string Macro;
+ llvm::raw_string_ostream OS(Macro);
+
+ OS << "LLDB_REGISTER";
+ if (Static)
+ OS << "_STATIC";
+ OS << "_METHOD";
+ if (Const)
+ OS << "_CONST";
+
+ return OS.str();
+}
+
+static std::string GetRecordMethodMacro(StringRef Result, StringRef Class,
+ StringRef Method, StringRef Signature,
+ StringRef Values, bool Static,
+ bool Const) {
+ std::string Macro;
+ llvm::raw_string_ostream OS(Macro);
+
+ OS << GetRecordMethodMacroName(Static, Const, Values.empty());
+ OS << "(" << Result << ", " << Class << ", " << Method;
+
+ if (!Values.empty()) {
+ OS << ", (" << Signature << "), " << Values << ");\n\n";
+ } else {
+ OS << ");\n\n";
+ }
+
+ return OS.str();
+}
+
+static std::string GetRecordConstructorMacro(StringRef Class,
+ StringRef Signature,
+ StringRef Values) {
+ std::string Macro;
+ llvm::raw_string_ostream OS(Macro);
+ if (!Values.empty()) {
+ OS << "LLDB_RECORD_CONSTRUCTOR(" << Class << ", (" << Signature << "), "
+ << Values << ");\n\n";
+ } else {
+ OS << "LLDB_RECORD_CONSTRUCTOR_NO_ARGS(" << Class << ");\n\n";
+ }
+ return OS.str();
+}
+
+static std::string GetRecordDummyMacro(StringRef Result, StringRef Class,
+ StringRef Method, StringRef Signature,
+ StringRef Values) {
+ assert(!Values.empty());
+ std::string Macro;
+ llvm::raw_string_ostream OS(Macro);
+
+ OS << "LLDB_RECORD_DUMMY(" << Result << ", " << Class << ", " << Method;
+ OS << ", (" << Signature << "), " << Values << ");\n\n";
+
+ return OS.str();
+}
+
+static std::string GetRegisterConstructorMacro(StringRef Class,
+ StringRef Signature) {
+ std::string Macro;
+ llvm::raw_string_ostream OS(Macro);
+ OS << "LLDB_REGISTER_CONSTRUCTOR(" << Class << ", (" << Signature << "));\n";
+ return OS.str();
+}
+
+static std::string GetRegisterMethodMacro(StringRef Result, StringRef Class,
+ StringRef Method, StringRef Signature,
+ bool Static, bool Const) {
+ std::string Macro;
+ llvm::raw_string_ostream OS(Macro);
+ OS << GetRegisterMethodMacroName(Static, Const);
+ OS << "(" << Result << ", " << Class << ", " << Method << ", (" << Signature
+ << "));\n";
+ return OS.str();
+}
+
+class SBReturnVisitor : public RecursiveASTVisitor<SBReturnVisitor> {
+public:
+ SBReturnVisitor(Rewriter &R) : MyRewriter(R) {}
+
+ bool VisitReturnStmt(ReturnStmt *Stmt) {
+ Expr *E = Stmt->getRetValue();
+
+ if (E->getBeginLoc().isMacroID())
+ return false;
+
+ SourceRange R(E->getBeginLoc(), E->getEndLoc());
+
+ StringRef WrittenExpr = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(R), MyRewriter.getSourceMgr(),
+ MyRewriter.getLangOpts());
+
+ std::string ReplacementText =
+ "LLDB_RECORD_RESULT(" + WrittenExpr.str() + ")";
+ MyRewriter.ReplaceText(R, ReplacementText);
+
+ return true;
+ }
+
+private:
+ Rewriter &MyRewriter;
+};
+
+class SBVisitor : public RecursiveASTVisitor<SBVisitor> {
+public:
+ SBVisitor(Rewriter &R, ASTContext &Context)
+ : MyRewriter(R), Context(Context) {}
+
+ bool VisitCXXMethodDecl(CXXMethodDecl *Decl) {
+ // Not all decls should be registered. Please refer to that method's
+ // comment for details.
+ if (ShouldSkip(Decl))
+ return false;
+
+ // Skip CXXMethodDecls that already starts with a macro. This should make
+ // it easier to rerun the tool to find missing macros.
+ Stmt *Body = Decl->getBody();
+ for (auto &C : Body->children()) {
+ if (C->getBeginLoc().isMacroID())
+ return false;
+ break;
+ }
+
+ // Print 'bool' instead of '_Bool'.
+ PrintingPolicy Policy(Context.getLangOpts());
+ Policy.Bool = true;
+
+ // Unsupported signatures get a dummy macro.
+ bool ShouldInsertDummy = false;
+
+ // Collect the functions parameter types and names.
+ std::vector<std::string> ParamTypes;
+ std::vector<std::string> ParamNames;
+ for (auto *P : Decl->parameters()) {
+ QualType T = P->getType();
+ ParamTypes.push_back(T.getAsString(Policy));
+ ParamNames.push_back(P->getNameAsString());
+
+ // Currently we don't support functions that have void pointers or
+ // function pointers as an argument, in which case we insert a dummy
+ // macro.
+ ShouldInsertDummy |= T->isFunctionPointerType() || T->isVoidPointerType();
+ }
+
+ // Convert the two lists to string for the macros.
+ std::string ParamTypesStr = llvm::join(ParamTypes, ", ");
+ std::string ParamNamesStr = llvm::join(ParamNames, ", ");
+
+ CXXRecordDecl *Record = Decl->getParent();
+ QualType ReturnType = Decl->getReturnType();
+
+ // Construct the macros.
+ std::string Macro;
+ if (ShouldInsertDummy) {
+ // Don't insert a register call for dummy macros.
+ Macro = GetRecordDummyMacro(
+ ReturnType.getAsString(Policy), Record->getNameAsString(),
+ Decl->getNameAsString(), ParamTypesStr, ParamNamesStr);
+
+ } else if (isa<CXXConstructorDecl>(Decl)) {
+ llvm::outs() << GetRegisterConstructorMacro(Record->getNameAsString(),
+ ParamTypesStr);
+
+ Macro = GetRecordConstructorMacro(Record->getNameAsString(),
+ ParamTypesStr, ParamNamesStr);
+ } else {
+ llvm::outs() << GetRegisterMethodMacro(
+ ReturnType.getAsString(Policy), Record->getNameAsString(),
+ Decl->getNameAsString(), ParamTypesStr, Decl->isStatic(),
+ Decl->isConst());
+
+ Macro = GetRecordMethodMacro(
+ ReturnType.getAsString(Policy), Record->getNameAsString(),
+ Decl->getNameAsString(), ParamTypesStr, ParamNamesStr,
+ Decl->isStatic(), Decl->isConst());
+ }
+
+ // Insert the macro at the beginning of the function. We don't attempt to
+ // fix the formatting and instead rely on clang-format to fix it after the
+ // tool has run. This is also the reason that the macros end with two
+ // newlines, counting on clang-format to normalize this in case the macro
+ // got inserted before an existing newline.
+ SourceLocation InsertLoc = Lexer::getLocForEndOfToken(
+ Body->getBeginLoc(), 0, MyRewriter.getSourceMgr(),
+ MyRewriter.getLangOpts());
+ MyRewriter.InsertTextAfter(InsertLoc, Macro);
+
+ // If the function returns a class or struct, we need to wrap its return
+ // statement(s).
+ bool ShouldRecordResult = ReturnType->isStructureOrClassType() ||
+ ReturnType->getPointeeCXXRecordDecl();
+ if (!ShouldInsertDummy && ShouldRecordResult) {
+ SBReturnVisitor Visitor(MyRewriter);
+ Visitor.TraverseDecl(Decl);
+ }
+
+ return true;
+ }
+
+private:
+ /// Determine whether we need to consider the given CXXMethodDecl.
+ ///
+ /// Currently we skip the following cases:
+ /// 1. Decls outside the main source file,
+ /// 2. Decls that are only present in the source file,
+ /// 3. Decls that are not definitions,
+ /// 4. Non-public methods,
+ /// 5. Variadic methods.
+ /// 6. Destructors.
+ bool ShouldSkip(CXXMethodDecl *Decl) {
+ // Skip anything outside the main file.
+ if (!MyRewriter.getSourceMgr().isInMainFile(Decl->getBeginLoc()))
+ return true;
+
+ // Skip if the canonical decl in the current decl. It means that the method
+ // is declared in the implementation and is therefore not exposed as part
+ // of the API.
+ if (Decl == Decl->getCanonicalDecl())
+ return true;
+
+ // Skip decls that have no body, i.e. are just declarations.
+ Stmt *Body = Decl->getBody();
+ if (!Body)
+ return true;
+
+ // Skip non-public methods.
+ AccessSpecifier AS = Decl->getAccess();
+ if (AS != AccessSpecifier::AS_public)
+ return true;
+
+ // Skip variadic methods.
+ if (Decl->isVariadic())
+ return true;
+
+ // Skip destructors.
+ if (isa<CXXDestructorDecl>(Decl))
+ return true;
+
+ return false;
+ }
+
+ Rewriter &MyRewriter;
+ ASTContext &Context;
+};
+
+class SBConsumer : public ASTConsumer {
+public:
+ SBConsumer(Rewriter &R, ASTContext &Context) : Visitor(R, Context) {}
+
+ // Override the method that gets called for each parsed top-level
+ // declaration.
+ bool HandleTopLevelDecl(DeclGroupRef DR) override {
+ for (DeclGroupRef::iterator b = DR.begin(), e = DR.end(); b != e; ++b) {
+ Visitor.TraverseDecl(*b);
+ }
+ return true;
+ }
+
+private:
+ SBVisitor Visitor;
+};
+
+class SBAction : public ASTFrontendAction {
+public:
+ SBAction() = default;
+
+ bool BeginSourceFileAction(CompilerInstance &CI) override {
+ llvm::outs() << "{\n";
+ return true;
+ }
+
+ void EndSourceFileAction() override {
+ llvm::outs() << "}\n";
+ MyRewriter.overwriteChangedFiles();
+ }
+
+ std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+ StringRef File) override {
+ MyRewriter.setSourceMgr(CI.getSourceManager(), CI.getLangOpts());
+ return std::make_unique<SBConsumer>(MyRewriter, CI.getASTContext());
+ }
+
+private:
+ Rewriter MyRewriter;
+};
+
+int main(int argc, const char **argv) {
+ CommonOptionsParser OP(argc, argv, InstrCategory,
+ "Utility for generating the macros for LLDB's "
+ "instrumentation framework.");
+
+ auto PCHOpts = std::make_shared<PCHContainerOperations>();
+ PCHOpts->registerWriter(std::make_unique<ObjectFilePCHContainerWriter>());
+ PCHOpts->registerReader(std::make_unique<ObjectFilePCHContainerReader>());
+
+ ClangTool T(OP.getCompilations(), OP.getSourcePathList(), PCHOpts);
+ return T.run(newFrontendActionFactory<SBAction>().get());
+}
diff --git a/gnu/llvm/lldb/tools/lldb-mi/lldb-mi.exports b/gnu/llvm/lldb/tools/lldb-mi/lldb-mi.exports
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-mi/lldb-mi.exports
diff --git a/gnu/llvm/lldb/tools/lldb-perf/darwin/sketch/foobar.sketch2 b/gnu/llvm/lldb/tools/lldb-perf/darwin/sketch/foobar.sketch2
new file mode 100644
index 00000000000..553c698b180
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-perf/darwin/sketch/foobar.sketch2
Binary files differ
diff --git a/gnu/llvm/lldb/tools/lldb-server/Acceptor.cpp b/gnu/llvm/lldb/tools/lldb-server/Acceptor.cpp
new file mode 100644
index 00000000000..2cfb34d215c
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-server/Acceptor.cpp
@@ -0,0 +1,135 @@
+//===-- Acceptor.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Acceptor.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ScopedPrinter.h"
+
+#include "lldb/Host/ConnectionFileDescriptor.h"
+#include "lldb/Host/common/TCPSocket.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/UriParser.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::lldb_server;
+using namespace llvm;
+
+namespace {
+
+struct SocketScheme {
+ const char *m_scheme;
+ const Socket::SocketProtocol m_protocol;
+};
+
+SocketScheme socket_schemes[] = {
+ {"tcp", Socket::ProtocolTcp},
+ {"udp", Socket::ProtocolUdp},
+ {"unix", Socket::ProtocolUnixDomain},
+ {"unix-abstract", Socket::ProtocolUnixAbstract},
+};
+
+bool FindProtocolByScheme(const char *scheme,
+ Socket::SocketProtocol &protocol) {
+ for (auto s : socket_schemes) {
+ if (!strcmp(s.m_scheme, scheme)) {
+ protocol = s.m_protocol;
+ return true;
+ }
+ }
+ return false;
+}
+
+const char *FindSchemeByProtocol(const Socket::SocketProtocol protocol) {
+ for (auto s : socket_schemes) {
+ if (s.m_protocol == protocol)
+ return s.m_scheme;
+ }
+ return nullptr;
+}
+}
+
+Status Acceptor::Listen(int backlog) {
+ return m_listener_socket_up->Listen(StringRef(m_name), backlog);
+}
+
+Status Acceptor::Accept(const bool child_processes_inherit, Connection *&conn) {
+ Socket *conn_socket = nullptr;
+ auto error = m_listener_socket_up->Accept(conn_socket);
+ if (error.Success())
+ conn = new ConnectionFileDescriptor(conn_socket);
+
+ return error;
+}
+
+Socket::SocketProtocol Acceptor::GetSocketProtocol() const {
+ return m_listener_socket_up->GetSocketProtocol();
+}
+
+const char *Acceptor::GetSocketScheme() const {
+ return FindSchemeByProtocol(GetSocketProtocol());
+}
+
+std::string Acceptor::GetLocalSocketId() const { return m_local_socket_id(); }
+
+std::unique_ptr<Acceptor> Acceptor::Create(StringRef name,
+ const bool child_processes_inherit,
+ Status &error) {
+ error.Clear();
+
+ Socket::SocketProtocol socket_protocol = Socket::ProtocolUnixDomain;
+ int port;
+ StringRef scheme, host, path;
+ // Try to match socket name as URL - e.g., tcp://localhost:5555
+ if (UriParser::Parse(name, scheme, host, port, path)) {
+ if (!FindProtocolByScheme(scheme.str().c_str(), socket_protocol))
+ error.SetErrorStringWithFormat("Unknown protocol scheme \"%s\"",
+ scheme.str().c_str());
+ else
+ name = name.drop_front(scheme.size() + strlen("://"));
+ } else {
+ std::string host_str;
+ std::string port_str;
+ int32_t port = INT32_MIN;
+ // Try to match socket name as $host:port - e.g., localhost:5555
+ if (Socket::DecodeHostAndPort(name, host_str, port_str, port, nullptr))
+ socket_protocol = Socket::ProtocolTcp;
+ }
+
+ if (error.Fail())
+ return std::unique_ptr<Acceptor>();
+
+ std::unique_ptr<Socket> listener_socket_up =
+ Socket::Create(socket_protocol, child_processes_inherit, error);
+
+ LocalSocketIdFunc local_socket_id;
+ if (error.Success()) {
+ if (listener_socket_up->GetSocketProtocol() == Socket::ProtocolTcp) {
+ TCPSocket *tcp_socket =
+ static_cast<TCPSocket *>(listener_socket_up.get());
+ local_socket_id = [tcp_socket]() {
+ auto local_port = tcp_socket->GetLocalPortNumber();
+ return (local_port != 0) ? llvm::to_string(local_port) : "";
+ };
+ } else {
+ const std::string socket_name = name;
+ local_socket_id = [socket_name]() { return socket_name; };
+ }
+
+ return std::unique_ptr<Acceptor>(
+ new Acceptor(std::move(listener_socket_up), name, local_socket_id));
+ }
+
+ return std::unique_ptr<Acceptor>();
+}
+
+Acceptor::Acceptor(std::unique_ptr<Socket> &&listener_socket, StringRef name,
+ const LocalSocketIdFunc &local_socket_id)
+ : m_listener_socket_up(std::move(listener_socket)), m_name(name.str()),
+ m_local_socket_id(local_socket_id) {}
diff --git a/gnu/llvm/lldb/tools/lldb-server/Acceptor.h b/gnu/llvm/lldb/tools/lldb-server/Acceptor.h
new file mode 100644
index 00000000000..1e7337f1411
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-server/Acceptor.h
@@ -0,0 +1,60 @@
+//===-- Acceptor.h ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#ifndef lldb_server_Acceptor_h_
+#define lldb_server_Acceptor_h_
+
+#include "lldb/Host/Socket.h"
+#include "lldb/Utility/Connection.h"
+#include "lldb/Utility/Status.h"
+
+#include <functional>
+#include <memory>
+#include <string>
+
+namespace llvm {
+class StringRef;
+}
+
+namespace lldb_private {
+namespace lldb_server {
+
+class Acceptor {
+public:
+ virtual ~Acceptor() = default;
+
+ Status Listen(int backlog);
+
+ Status Accept(const bool child_processes_inherit, Connection *&conn);
+
+ static std::unique_ptr<Acceptor> Create(llvm::StringRef name,
+ const bool child_processes_inherit,
+ Status &error);
+
+ Socket::SocketProtocol GetSocketProtocol() const;
+
+ const char *GetSocketScheme() const;
+
+ // Returns either TCP port number as string or domain socket path.
+ // Empty string is returned in case of error.
+ std::string GetLocalSocketId() const;
+
+private:
+ typedef std::function<std::string()> LocalSocketIdFunc;
+
+ Acceptor(std::unique_ptr<Socket> &&listener_socket, llvm::StringRef name,
+ const LocalSocketIdFunc &local_socket_id);
+
+ const std::unique_ptr<Socket> m_listener_socket_up;
+ const std::string m_name;
+ const LocalSocketIdFunc m_local_socket_id;
+};
+
+} // namespace lldb_server
+} // namespace lldb_private
+
+#endif // lldb_server_Acceptor_h_
diff --git a/gnu/llvm/lldb/tools/lldb-server/CMakeLists.txt b/gnu/llvm/lldb/tools/lldb-server/CMakeLists.txt
new file mode 100644
index 00000000000..739bdc3c422
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-server/CMakeLists.txt
@@ -0,0 +1,58 @@
+set(LLDB_PLUGINS)
+
+if(CMAKE_SYSTEM_NAME MATCHES "Linux|Android")
+ list(APPEND LLDB_PLUGINS lldbPluginProcessLinux)
+endif()
+
+if(CMAKE_SYSTEM_NAME MATCHES "NetBSD")
+ list(APPEND LLDB_PLUGINS lldbPluginProcessNetBSD)
+endif()
+
+if(CMAKE_SYSTEM_NAME MATCHES "OpenBSD")
+ list(APPEND LLDB_PLUGINS lldbPluginProcessOpenBSD)
+endif()
+
+if(CMAKE_SYSTEM_NAME MATCHES "Darwin")
+ list(APPEND LLDB_PLUGINS lldbPluginObjectFileMachO)
+elseif(CMAKE_SYSTEM_NAME MATCHES "Windows")
+ list(APPEND LLDB_PLUGINS lldbPluginObjectFilePECOFF)
+else()
+ list(APPEND LLDB_PLUGINS lldbPluginObjectFileELF)
+endif()
+
+if(IOS)
+ if(LLDB_CODESIGN_IDENTITY)
+ # Use explicit LLDB identity
+ set(LLVM_CODESIGNING_IDENTITY ${LLDB_CODESIGN_IDENTITY})
+ else()
+ # Use explicit LLVM identity or default to lldb_codesign if empty
+ if(NOT LLVM_CODESIGNING_IDENTITY)
+ set(LLVM_CODESIGNING_IDENTITY lldb_codesign)
+ endif()
+ endif()
+endif()
+
+add_lldb_tool(lldb-server
+ Acceptor.cpp
+ lldb-gdbserver.cpp
+ lldb-platform.cpp
+ lldb-server.cpp
+ LLDBServerUtilities.cpp
+ SystemInitializerLLGS.cpp
+
+ LINK_LIBS
+ lldbBase
+ lldbHost
+ lldbInitialization
+ ${LLDB_PLUGINS}
+ lldbPluginInstructionARM
+ lldbPluginInstructionMIPS
+ lldbPluginInstructionMIPS64
+ ${LLDB_SYSTEM_LIBS}
+
+ LINK_COMPONENTS
+ Support
+)
+
+target_include_directories(lldb-server PRIVATE "${LLDB_SOURCE_DIR}/source")
+target_link_libraries(lldb-server PRIVATE ${LLDB_SYSTEM_LIBS})
diff --git a/gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-Info.plist b/gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-Info.plist
new file mode 100644
index 00000000000..58a34ca5e43
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-Info.plist
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.apple.lldb-server</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>lldb-server</string>
+ <key>CFBundleVersion</key>
+ <string>2</string>
+ <key>SecTaskAccess</key>
+ <array>
+ <string>allowed</string>
+ <string>debug</string>
+ </array>
+</dict>
+</plist>
diff --git a/gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-entitlements.plist b/gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-entitlements.plist
new file mode 100644
index 00000000000..e05a9baa5d0
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-entitlements.plist
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.springboard.debugapplications</key>
+ <true/>
+ <key>com.apple.backboardd.launchapplications</key>
+ <true/>
+ <key>com.apple.backboardd.debugapplications</key>
+ <true/>
+ <key>com.apple.frontboard.launchapplications</key>
+ <true/>
+ <key>com.apple.frontboard.debugapplications</key>
+ <true/>
+ <key>run-unsigned-code</key>
+ <true/>
+ <key>seatbelt-profiles</key>
+ <array>
+ <string>debugserver</string>
+ </array>
+ <key>com.apple.private.logging.diagnostic</key>
+ <true/>
+ <key>com.apple.security.network.server</key>
+ <true/>
+ <key>com.apple.security.network.client</key>
+ <true/>
+</dict>
+</plist>
diff --git a/gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-macos-entitlements.plist b/gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-macos-entitlements.plist
new file mode 100644
index 00000000000..edf79b3b3ee
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-macos-entitlements.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.private.logging.diagnostic</key>
+ <true/>
+</dict>
+</plist>
diff --git a/gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-mig.defs b/gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-mig.defs
new file mode 100644
index 00000000000..cd5be170070
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-server/Darwin/resources/lldb-server-mig.defs
@@ -0,0 +1,5 @@
+/*
+ * nub.defs
+ */
+
+#import <mach/mach_exc.defs>
diff --git a/gnu/llvm/lldb/tools/lldb-server/LLDBServerUtilities.cpp b/gnu/llvm/lldb/tools/lldb-server/LLDBServerUtilities.cpp
new file mode 100644
index 00000000000..7f9271e36b6
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-server/LLDBServerUtilities.cpp
@@ -0,0 +1,63 @@
+//===-- LLDBServerUtilities.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LLDBServerUtilities.h"
+
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/FileSystem.h"
+
+using namespace lldb;
+using namespace lldb_private::lldb_server;
+using namespace llvm;
+
+static std::shared_ptr<raw_ostream> GetLogStream(StringRef log_file) {
+ if (!log_file.empty()) {
+ std::error_code EC;
+ std::shared_ptr<raw_ostream> stream_sp = std::make_shared<raw_fd_ostream>(
+ log_file, EC, sys::fs::OF_Text | sys::fs::OF_Append);
+ if (!EC)
+ return stream_sp;
+ errs() << llvm::formatv(
+ "Failed to open log file `{0}`: {1}\nWill log to stderr instead.\n",
+ log_file, EC.message());
+ }
+ // No need to delete the stderr stream.
+ return std::shared_ptr<raw_ostream>(&errs(), [](raw_ostream *) {});
+}
+
+bool LLDBServerUtilities::SetupLogging(const std::string &log_file,
+ const StringRef &log_channels,
+ uint32_t log_options) {
+
+ auto log_stream_sp = GetLogStream(log_file);
+
+ SmallVector<StringRef, 32> channel_array;
+ log_channels.split(channel_array, ":", /*MaxSplit*/ -1, /*KeepEmpty*/ false);
+ for (auto channel_with_categories : channel_array) {
+ std::string error;
+ llvm::raw_string_ostream error_stream(error);
+ Args channel_then_categories(channel_with_categories);
+ std::string channel(channel_then_categories.GetArgumentAtIndex(0));
+ channel_then_categories.Shift(); // Shift off the channel
+
+ bool success = Log::EnableLogChannel(
+ log_stream_sp, log_options, channel,
+ channel_then_categories.GetArgumentArrayRef(), error_stream);
+ if (!success) {
+ errs() << formatv("Unable to setup logging for channel \"{0}\": {1}",
+ channel, error_stream.str());
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/gnu/llvm/lldb/tools/lldb-server/LLDBServerUtilities.h b/gnu/llvm/lldb/tools/lldb-server/LLDBServerUtilities.h
new file mode 100644
index 00000000000..3ade1f9f5b8
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-server/LLDBServerUtilities.h
@@ -0,0 +1,23 @@
+//===-- LLDBServerUtilities.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/StringRef.h"
+
+#include <string>
+
+namespace lldb_private {
+namespace lldb_server {
+
+class LLDBServerUtilities {
+public:
+ static bool SetupLogging(const std::string &log_file,
+ const llvm::StringRef &log_channels,
+ uint32_t log_options);
+};
+}
+}
diff --git a/gnu/llvm/lldb/tools/lldb-server/SystemInitializerLLGS.cpp b/gnu/llvm/lldb/tools/lldb-server/SystemInitializerLLGS.cpp
new file mode 100644
index 00000000000..b93e6b40dcd
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-server/SystemInitializerLLGS.cpp
@@ -0,0 +1,79 @@
+//===-- SystemInitializerLLGS.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SystemInitializerLLGS.h"
+
+#if defined(__APPLE__)
+#include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h"
+using HostObjectFile = ObjectFileMachO;
+#elif defined(_WIN32)
+#include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h"
+using HostObjectFile = ObjectFilePECOFF;
+#else
+#include "Plugins/ObjectFile/ELF/ObjectFileELF.h"
+using HostObjectFile = ObjectFileELF;
+#endif
+
+#if defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64)
+#define LLDB_TARGET_ARM64
+#endif
+
+#if defined(__arm__) || defined(__arm) || defined(_ARM) || defined(_M_ARM) || \
+ defined(LLDB_TARGET_ARM64)
+#define LLDB_TARGET_ARM
+#include "Plugins/Instruction/ARM/EmulateInstructionARM.h"
+#endif
+
+#if defined(__mips64__) || defined(mips64) || defined(__mips64) || \
+ defined(__MIPS64__) || defined(_M_MIPS64)
+#define LLDB_TARGET_MIPS64
+#include "Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.h"
+#endif
+
+#if defined(__mips__) || defined(mips) || defined(__mips) || \
+ defined(__MIPS__) || defined(_M_MIPS) || defined(LLDB_TARGET_MIPS64)
+#define LLDB_TARGET_MIPS
+#include "Plugins/Instruction/MIPS/EmulateInstructionMIPS.h"
+#endif
+
+using namespace lldb_private;
+
+llvm::Error SystemInitializerLLGS::Initialize() {
+ if (auto e = SystemInitializerCommon::Initialize())
+ return e;
+
+ HostObjectFile::Initialize();
+
+#if defined(LLDB_TARGET_ARM) || defined(LLDB_TARGET_ARM64)
+ EmulateInstructionARM::Initialize();
+#endif
+#if defined(LLDB_TARGET_MIPS) || defined(LLDB_TARGET_MIPS64)
+ EmulateInstructionMIPS::Initialize();
+#endif
+#if defined(LLDB_TARGET_MIPS64)
+ EmulateInstructionMIPS64::Initialize();
+#endif
+
+ return llvm::Error::success();
+}
+
+void SystemInitializerLLGS::Terminate() {
+ HostObjectFile::Terminate();
+
+#if defined(LLDB_TARGET_ARM) || defined(LLDB_TARGET_ARM64)
+ EmulateInstructionARM::Terminate();
+#endif
+#if defined(LLDB_TARGET_MIPS) || defined(LLDB_TARGET_MIPS64)
+ EmulateInstructionMIPS::Terminate();
+#endif
+#if defined(LLDB_TARGET_MIPS64)
+ EmulateInstructionMIPS64::Terminate();
+#endif
+
+ SystemInitializerCommon::Terminate();
+}
diff --git a/gnu/llvm/lldb/tools/lldb-server/SystemInitializerLLGS.h b/gnu/llvm/lldb/tools/lldb-server/SystemInitializerLLGS.h
new file mode 100644
index 00000000000..59a1fa14e81
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-server/SystemInitializerLLGS.h
@@ -0,0 +1,21 @@
+//===-- SystemInitializerLLGS.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SYSTEMINITIALIZERLLGS_H
+#define LLDB_SYSTEMINITIALIZERLLGS_H
+
+#include "lldb/Initialization/SystemInitializer.h"
+#include "lldb/Initialization/SystemInitializerCommon.h"
+
+class SystemInitializerLLGS : public lldb_private::SystemInitializerCommon {
+public:
+ llvm::Error Initialize() override;
+ void Terminate() override;
+};
+
+#endif // LLDB_SYSTEMINITIALIZERLLGS_H
diff --git a/gnu/llvm/lldb/tools/lldb-server/lldb-gdbserver.cpp b/gnu/llvm/lldb/tools/lldb-server/lldb-gdbserver.cpp
new file mode 100644
index 00000000000..9648a6a4426
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-server/lldb-gdbserver.cpp
@@ -0,0 +1,547 @@
+//===-- lldb-gdbserver.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef _WIN32
+#include <signal.h>
+#include <unistd.h>
+#endif
+
+
+#include "Acceptor.h"
+#include "LLDBServerUtilities.h"
+#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h"
+#include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
+#include "lldb/Host/Config.h"
+#include "lldb/Host/ConnectionFileDescriptor.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostGetOpt.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Host/Pipe.h"
+#include "lldb/Host/Socket.h"
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Host/common/NativeProcessProtocol.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/Status.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Errno.h"
+
+#if defined(__linux__)
+#include "Plugins/Process/Linux/NativeProcessLinux.h"
+#elif defined(__NetBSD__)
+#include "Plugins/Process/NetBSD/NativeProcessNetBSD.h"
+#elif defined(__OpenBSD__)
+#include "Plugins/Process/OpenBSD/NativeProcessOpenBSD.h"
+#elif defined(_WIN32)
+#include "Plugins/Process/Windows/Common/NativeProcessWindows.h"
+#endif
+
+#ifndef LLGS_PROGRAM_NAME
+#define LLGS_PROGRAM_NAME "lldb-server"
+#endif
+
+#ifndef LLGS_VERSION_STR
+#define LLGS_VERSION_STR "local_build"
+#endif
+
+using namespace llvm;
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::lldb_server;
+using namespace lldb_private::process_gdb_remote;
+
+namespace {
+#if defined(__linux__)
+typedef process_linux::NativeProcessLinux::Factory NativeProcessFactory;
+#elif defined(__NetBSD__)
+typedef process_netbsd::NativeProcessNetBSD::Factory NativeProcessFactory;
+#elif defined(__OpenBSD__)
+typedef process_openbsd::NativeProcessOpenBSD::Factory NativeProcessFactory;
+#elif defined(_WIN32)
+typedef NativeProcessWindows::Factory NativeProcessFactory;
+#else
+// Dummy implementation to make sure the code compiles
+class NativeProcessFactory : public NativeProcessProtocol::Factory {
+public:
+ llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+ Launch(ProcessLaunchInfo &launch_info,
+ NativeProcessProtocol::NativeDelegate &delegate,
+ MainLoop &mainloop) const override {
+ llvm_unreachable("Not implemented");
+ }
+ llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+ Attach(lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &delegate,
+ MainLoop &mainloop) const override {
+ llvm_unreachable("Not implemented");
+ }
+};
+#endif
+}
+
+// option descriptors for getopt_long_only()
+
+static int g_debug = 0;
+static int g_verbose = 0;
+
+static struct option g_long_options[] = {
+ {"debug", no_argument, &g_debug, 1},
+ {"verbose", no_argument, &g_verbose, 1},
+ {"log-file", required_argument, nullptr, 'l'},
+ {"log-channels", required_argument, nullptr, 'c'},
+ {"attach", required_argument, nullptr, 'a'},
+ {"named-pipe", required_argument, nullptr, 'N'},
+ {"pipe", required_argument, nullptr, 'U'},
+ {"native-regs", no_argument, nullptr,
+ 'r'}, // Specify to use the native registers instead of the gdb defaults
+ // for the architecture. NOTE: this is a do-nothing arg as it's
+ // behavior is default now. FIXME remove call from lldb-platform.
+ {"reverse-connect", no_argument, nullptr,
+ 'R'}, // Specifies that llgs attaches to the client address:port rather
+ // than llgs listening for a connection from address on port.
+ {"setsid", no_argument, nullptr,
+ 'S'}, // Call setsid() to make llgs run in its own session.
+ {"fd", required_argument, nullptr, 'F'},
+ {nullptr, 0, nullptr, 0}};
+
+#ifndef _WIN32
+// Watch for signals
+static int g_sighup_received_count = 0;
+
+static void sighup_handler(MainLoopBase &mainloop) {
+ ++g_sighup_received_count;
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ LLDB_LOGF(log, "lldb-server:%s swallowing SIGHUP (receive count=%d)",
+ __FUNCTION__, g_sighup_received_count);
+
+ if (g_sighup_received_count >= 2)
+ mainloop.RequestTermination();
+}
+#endif // #ifndef _WIN32
+
+static void display_usage(const char *progname, const char *subcommand) {
+ fprintf(stderr, "Usage:\n %s %s "
+ "[--log-file log-file-name] "
+ "[--log-channels log-channel-list] "
+ "[--setsid] "
+ "[--fd file-descriptor]"
+ "[--named-pipe named-pipe-path] "
+ "[--native-regs] "
+ "[--attach pid] "
+ "[[HOST]:PORT] "
+ "[-- PROGRAM ARG1 ARG2 ...]\n",
+ progname, subcommand);
+}
+
+void handle_attach_to_pid(GDBRemoteCommunicationServerLLGS &gdb_server,
+ lldb::pid_t pid) {
+ Status error = gdb_server.AttachToProcess(pid);
+ if (error.Fail()) {
+ fprintf(stderr, "error: failed to attach to pid %" PRIu64 ": %s\n", pid,
+ error.AsCString());
+ exit(1);
+ }
+}
+
+void handle_attach_to_process_name(GDBRemoteCommunicationServerLLGS &gdb_server,
+ const std::string &process_name) {
+ // FIXME implement.
+}
+
+void handle_attach(GDBRemoteCommunicationServerLLGS &gdb_server,
+ const std::string &attach_target) {
+ assert(!attach_target.empty() && "attach_target cannot be empty");
+
+ // First check if the attach_target is convertible to a long. If so, we'll use
+ // it as a pid.
+ char *end_p = nullptr;
+ const long int pid = strtol(attach_target.c_str(), &end_p, 10);
+
+ // We'll call it a match if the entire argument is consumed.
+ if (end_p &&
+ static_cast<size_t>(end_p - attach_target.c_str()) ==
+ attach_target.size())
+ handle_attach_to_pid(gdb_server, static_cast<lldb::pid_t>(pid));
+ else
+ handle_attach_to_process_name(gdb_server, attach_target);
+}
+
+void handle_launch(GDBRemoteCommunicationServerLLGS &gdb_server, int argc,
+ const char *const argv[]) {
+ ProcessLaunchInfo info;
+ info.GetFlags().Set(eLaunchFlagStopAtEntry | eLaunchFlagDebug |
+ eLaunchFlagDisableASLR);
+ info.SetArguments(const_cast<const char **>(argv), true);
+
+ llvm::SmallString<64> cwd;
+ if (std::error_code ec = llvm::sys::fs::current_path(cwd)) {
+ llvm::errs() << "Error getting current directory: " << ec.message() << "\n";
+ exit(1);
+ }
+ FileSpec cwd_spec(cwd);
+ FileSystem::Instance().Resolve(cwd_spec);
+ info.SetWorkingDirectory(cwd_spec);
+ info.GetEnvironment() = Host::GetEnvironment();
+
+ gdb_server.SetLaunchInfo(info);
+
+ Status error = gdb_server.LaunchProcess();
+ if (error.Fail()) {
+ llvm::errs() << llvm::formatv("error: failed to launch '{0}': {1}\n",
+ argv[0], error);
+ exit(1);
+ }
+}
+
+Status writeSocketIdToPipe(Pipe &port_pipe, const std::string &socket_id) {
+ size_t bytes_written = 0;
+ // Write the port number as a C string with the NULL terminator.
+ return port_pipe.Write(socket_id.c_str(), socket_id.size() + 1,
+ bytes_written);
+}
+
+Status writeSocketIdToPipe(const char *const named_pipe_path,
+ const std::string &socket_id) {
+ Pipe port_name_pipe;
+ // Wait for 10 seconds for pipe to be opened.
+ auto error = port_name_pipe.OpenAsWriterWithTimeout(named_pipe_path, false,
+ std::chrono::seconds{10});
+ if (error.Fail())
+ return error;
+ return writeSocketIdToPipe(port_name_pipe, socket_id);
+}
+
+Status writeSocketIdToPipe(lldb::pipe_t unnamed_pipe,
+ const std::string &socket_id) {
+ Pipe port_pipe{LLDB_INVALID_PIPE, unnamed_pipe};
+ return writeSocketIdToPipe(port_pipe, socket_id);
+}
+
+void ConnectToRemote(MainLoop &mainloop,
+ GDBRemoteCommunicationServerLLGS &gdb_server,
+ bool reverse_connect, const char *const host_and_port,
+ const char *const progname, const char *const subcommand,
+ const char *const named_pipe_path, pipe_t unnamed_pipe,
+ int connection_fd) {
+ Status error;
+
+ std::unique_ptr<Connection> connection_up;
+ if (connection_fd != -1) {
+ // Build the connection string.
+ char connection_url[512];
+ snprintf(connection_url, sizeof(connection_url), "fd://%d", connection_fd);
+
+ // Create the connection.
+#if LLDB_ENABLE_POSIX && !defined _WIN32
+ ::fcntl(connection_fd, F_SETFD, FD_CLOEXEC);
+#endif
+ connection_up.reset(new ConnectionFileDescriptor);
+ auto connection_result = connection_up->Connect(connection_url, &error);
+ if (connection_result != eConnectionStatusSuccess) {
+ fprintf(stderr, "error: failed to connect to client at '%s' "
+ "(connection status: %d)\n",
+ connection_url, static_cast<int>(connection_result));
+ exit(-1);
+ }
+ if (error.Fail()) {
+ fprintf(stderr, "error: failed to connect to client at '%s': %s\n",
+ connection_url, error.AsCString());
+ exit(-1);
+ }
+ } else if (host_and_port && host_and_port[0]) {
+ // Parse out host and port.
+ std::string final_host_and_port;
+ std::string connection_host;
+ std::string connection_port;
+ uint32_t connection_portno = 0;
+
+ // If host_and_port starts with ':', default the host to be "localhost" and
+ // expect the remainder to be the port.
+ if (host_and_port[0] == ':')
+ final_host_and_port.append("localhost");
+ final_host_and_port.append(host_and_port);
+
+ const std::string::size_type colon_pos = final_host_and_port.find(':');
+ if (colon_pos != std::string::npos) {
+ connection_host = final_host_and_port.substr(0, colon_pos);
+ connection_port = final_host_and_port.substr(colon_pos + 1);
+ connection_portno = StringConvert::ToUInt32(connection_port.c_str(), 0);
+ }
+
+
+ if (reverse_connect) {
+ // llgs will connect to the gdb-remote client.
+
+ // Ensure we have a port number for the connection.
+ if (connection_portno == 0) {
+ fprintf(stderr, "error: port number must be specified on when using "
+ "reverse connect\n");
+ exit(1);
+ }
+
+ // Build the connection string.
+ char connection_url[512];
+ snprintf(connection_url, sizeof(connection_url), "connect://%s",
+ final_host_and_port.c_str());
+
+ // Create the connection.
+ connection_up.reset(new ConnectionFileDescriptor);
+ auto connection_result = connection_up->Connect(connection_url, &error);
+ if (connection_result != eConnectionStatusSuccess) {
+ fprintf(stderr, "error: failed to connect to client at '%s' "
+ "(connection status: %d)\n",
+ connection_url, static_cast<int>(connection_result));
+ exit(-1);
+ }
+ if (error.Fail()) {
+ fprintf(stderr, "error: failed to connect to client at '%s': %s\n",
+ connection_url, error.AsCString());
+ exit(-1);
+ }
+ } else {
+ std::unique_ptr<Acceptor> acceptor_up(
+ Acceptor::Create(final_host_and_port, false, error));
+ if (error.Fail()) {
+ fprintf(stderr, "failed to create acceptor: %s\n", error.AsCString());
+ exit(1);
+ }
+ error = acceptor_up->Listen(1);
+ if (error.Fail()) {
+ fprintf(stderr, "failed to listen: %s\n", error.AsCString());
+ exit(1);
+ }
+ const std::string socket_id = acceptor_up->GetLocalSocketId();
+ if (!socket_id.empty()) {
+ // If we have a named pipe to write the socket id back to, do that now.
+ if (named_pipe_path && named_pipe_path[0]) {
+ error = writeSocketIdToPipe(named_pipe_path, socket_id);
+ if (error.Fail())
+ fprintf(stderr, "failed to write to the named pipe \'%s\': %s\n",
+ named_pipe_path, error.AsCString());
+ }
+ // If we have an unnamed pipe to write the socket id back to, do that
+ // now.
+ else if (unnamed_pipe != LLDB_INVALID_PIPE) {
+ error = writeSocketIdToPipe(unnamed_pipe, socket_id);
+ if (error.Fail())
+ fprintf(stderr, "failed to write to the unnamed pipe: %s\n",
+ error.AsCString());
+ }
+ } else {
+ fprintf(stderr,
+ "unable to get the socket id for the listening connection\n");
+ }
+
+ Connection *conn = nullptr;
+ error = acceptor_up->Accept(false, conn);
+ if (error.Fail()) {
+ printf("failed to accept new connection: %s\n", error.AsCString());
+ exit(1);
+ }
+ connection_up.reset(conn);
+ }
+ }
+ error = gdb_server.InitializeConnection(std::move(connection_up));
+ if (error.Fail()) {
+ fprintf(stderr, "Failed to initialize connection: %s\n",
+ error.AsCString());
+ exit(-1);
+ }
+ printf("Connection established.\n");
+}
+
+// main
+int main_gdbserver(int argc, char *argv[]) {
+ Status error;
+ MainLoop mainloop;
+#ifndef _WIN32
+ // Setup signal handlers first thing.
+ signal(SIGPIPE, SIG_IGN);
+ MainLoop::SignalHandleUP sighup_handle =
+ mainloop.RegisterSignal(SIGHUP, sighup_handler, error);
+#endif
+
+ const char *progname = argv[0];
+ const char *subcommand = argv[1];
+ argc--;
+ argv++;
+ int long_option_index = 0;
+ int ch;
+ std::string attach_target;
+ std::string named_pipe_path;
+ std::string log_file;
+ StringRef
+ log_channels; // e.g. "lldb process threads:gdb-remote default:linux all"
+ lldb::pipe_t unnamed_pipe = LLDB_INVALID_PIPE;
+ bool reverse_connect = false;
+ int connection_fd = -1;
+
+ // ProcessLaunchInfo launch_info;
+ ProcessAttachInfo attach_info;
+
+ bool show_usage = false;
+ int option_error = 0;
+#if __GLIBC__
+ optind = 0;
+#else
+ optreset = 1;
+ optind = 1;
+#endif
+
+ std::string short_options(OptionParser::GetShortOptionString(g_long_options));
+
+ while ((ch = getopt_long_only(argc, argv, short_options.c_str(),
+ g_long_options, &long_option_index)) != -1) {
+ switch (ch) {
+ case 0: // Any optional that auto set themselves will return 0
+ break;
+
+ case 'l': // Set Log File
+ if (optarg && optarg[0])
+ log_file.assign(optarg);
+ break;
+
+ case 'c': // Log Channels
+ if (optarg && optarg[0])
+ log_channels = StringRef(optarg);
+ break;
+
+ case 'N': // named pipe
+ if (optarg && optarg[0])
+ named_pipe_path = optarg;
+ break;
+
+ case 'U': // unnamed pipe
+ if (optarg && optarg[0])
+ unnamed_pipe = (pipe_t)StringConvert::ToUInt64(optarg, -1);
+ break;
+
+ case 'r':
+ // Do nothing, native regs is the default these days
+ break;
+
+ case 'R':
+ reverse_connect = true;
+ break;
+
+ case 'F':
+ connection_fd = StringConvert::ToUInt32(optarg, -1);
+ break;
+
+#ifndef _WIN32
+ case 'S':
+ // Put llgs into a new session. Terminals group processes
+ // into sessions and when a special terminal key sequences
+ // (like control+c) are typed they can cause signals to go out to
+ // all processes in a session. Using this --setsid (-S) option
+ // will cause debugserver to run in its own sessions and be free
+ // from such issues.
+ //
+ // This is useful when llgs is spawned from a command
+ // line application that uses llgs to do the debugging,
+ // yet that application doesn't want llgs receiving the
+ // signals sent to the session (i.e. dying when anyone hits ^C).
+ {
+ const ::pid_t new_sid = setsid();
+ if (new_sid == -1) {
+ llvm::errs() << llvm::formatv(
+ "failed to set new session id for {0} ({1})\n", LLGS_PROGRAM_NAME,
+ llvm::sys::StrError());
+ }
+ }
+ break;
+#endif
+
+ case 'a': // attach {pid|process_name}
+ if (optarg && optarg[0])
+ attach_target = optarg;
+ break;
+
+ case 'h': /* fall-through is intentional */
+ case '?':
+ show_usage = true;
+ break;
+ }
+ }
+
+ if (show_usage || option_error) {
+ display_usage(progname, subcommand);
+ exit(option_error);
+ }
+
+ if (!LLDBServerUtilities::SetupLogging(
+ log_file, log_channels,
+ LLDB_LOG_OPTION_PREPEND_TIMESTAMP |
+ LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION))
+ return -1;
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(GDBR_LOG_PROCESS));
+ if (log) {
+ LLDB_LOGF(log, "lldb-server launch");
+ for (int i = 0; i < argc; i++) {
+ LLDB_LOGF(log, "argv[%i] = '%s'", i, argv[i]);
+ }
+ }
+
+ // Skip any options we consumed with getopt_long_only.
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0 && connection_fd == -1) {
+ fputs("No arguments\n", stderr);
+ display_usage(progname, subcommand);
+ exit(255);
+ }
+
+ NativeProcessFactory factory;
+ GDBRemoteCommunicationServerLLGS gdb_server(mainloop, factory);
+
+ const char *const host_and_port = argv[0];
+ argc -= 1;
+ argv += 1;
+
+ // Any arguments left over are for the program that we need to launch. If
+ // there
+ // are no arguments, then the GDB server will start up and wait for an 'A'
+ // packet
+ // to launch a program, or a vAttach packet to attach to an existing process,
+ // unless
+ // explicitly asked to attach with the --attach={pid|program_name} form.
+ if (!attach_target.empty())
+ handle_attach(gdb_server, attach_target);
+ else if (argc > 0)
+ handle_launch(gdb_server, argc, argv);
+
+ // Print version info.
+ printf("%s-%s\n", LLGS_PROGRAM_NAME, LLGS_VERSION_STR);
+
+ ConnectToRemote(mainloop, gdb_server, reverse_connect, host_and_port,
+ progname, subcommand, named_pipe_path.c_str(),
+ unnamed_pipe, connection_fd);
+
+ if (!gdb_server.IsConnected()) {
+ fprintf(stderr, "no connection information provided, unable to run\n");
+ display_usage(progname, subcommand);
+ return 1;
+ }
+
+ Status ret = mainloop.Run();
+ if (ret.Fail()) {
+ fprintf(stderr, "lldb-server terminating due to error: %s\n",
+ ret.AsCString());
+ return 1;
+ }
+ fprintf(stderr, "lldb-server exiting...\n");
+
+ return 0;
+}
diff --git a/gnu/llvm/lldb/tools/lldb-server/lldb-platform.cpp b/gnu/llvm/lldb/tools/lldb-server/lldb-platform.cpp
new file mode 100644
index 00000000000..a6fb5639d64
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-server/lldb-platform.cpp
@@ -0,0 +1,385 @@
+//===-- lldb-platform.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <errno.h>
+#if defined(__APPLE__)
+#include <netinet/in.h>
+#endif
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if !defined(_WIN32)
+#include <sys/wait.h>
+#endif
+#include <fstream>
+
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include "Acceptor.h"
+#include "LLDBServerUtilities.h"
+#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h"
+#include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
+#include "lldb/Host/ConnectionFileDescriptor.h"
+#include "lldb/Host/HostGetOpt.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Host/common/TCPSocket.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::lldb_server;
+using namespace lldb_private::process_gdb_remote;
+using namespace llvm;
+
+// option descriptors for getopt_long_only()
+
+static int g_debug = 0;
+static int g_verbose = 0;
+static int g_server = 0;
+
+static struct option g_long_options[] = {
+ {"debug", no_argument, &g_debug, 1},
+ {"verbose", no_argument, &g_verbose, 1},
+ {"log-file", required_argument, nullptr, 'l'},
+ {"log-channels", required_argument, nullptr, 'c'},
+ {"listen", required_argument, nullptr, 'L'},
+ {"port-offset", required_argument, nullptr, 'p'},
+ {"gdbserver-port", required_argument, nullptr, 'P'},
+ {"min-gdbserver-port", required_argument, nullptr, 'm'},
+ {"max-gdbserver-port", required_argument, nullptr, 'M'},
+ {"socket-file", required_argument, nullptr, 'f'},
+ {"server", no_argument, &g_server, 1},
+ {nullptr, 0, nullptr, 0}};
+
+#if defined(__APPLE__)
+#define LOW_PORT (IPPORT_RESERVED)
+#define HIGH_PORT (IPPORT_HIFIRSTAUTO)
+#else
+#define LOW_PORT (1024u)
+#define HIGH_PORT (49151u)
+#endif
+
+#if !defined(_WIN32)
+// Watch for signals
+static void signal_handler(int signo) {
+ switch (signo) {
+ case SIGHUP:
+ // Use SIGINT first, if that does not work, use SIGHUP as a last resort.
+ // And we should not call exit() here because it results in the global
+ // destructors
+ // to be invoked and wreaking havoc on the threads still running.
+ Host::SystemLog(Host::eSystemLogWarning,
+ "SIGHUP received, exiting lldb-server...\n");
+ abort();
+ break;
+ }
+}
+#endif
+
+static void display_usage(const char *progname, const char *subcommand) {
+ fprintf(stderr, "Usage:\n %s %s [--log-file log-file-name] [--log-channels "
+ "log-channel-list] [--port-file port-file-path] --server "
+ "--listen port\n",
+ progname, subcommand);
+ exit(0);
+}
+
+static Status save_socket_id_to_file(const std::string &socket_id,
+ const FileSpec &file_spec) {
+ FileSpec temp_file_spec(file_spec.GetDirectory().AsCString());
+ Status error(llvm::sys::fs::create_directory(temp_file_spec.GetPath()));
+ if (error.Fail())
+ return Status("Failed to create directory %s: %s",
+ temp_file_spec.GetCString(), error.AsCString());
+
+ llvm::SmallString<64> temp_file_path;
+ temp_file_spec.AppendPathComponent("port-file.%%%%%%");
+
+ Status status;
+ if (auto Err =
+ handleErrors(llvm::writeFileAtomically(
+ temp_file_path, temp_file_spec.GetPath(), socket_id),
+ [&status, &file_spec](const AtomicFileWriteError &E) {
+ std::string ErrorMsgBuffer;
+ llvm::raw_string_ostream S(ErrorMsgBuffer);
+ E.log(S);
+
+ switch (E.Error) {
+ case atomic_write_error::failed_to_create_uniq_file:
+ status = Status("Failed to create temp file: %s",
+ ErrorMsgBuffer.c_str());
+ break;
+ case atomic_write_error::output_stream_error:
+ status = Status("Failed to write to port file.");
+ break;
+ case atomic_write_error::failed_to_rename_temp_file:
+ status = Status("Failed to rename file %s to %s: %s",
+ ErrorMsgBuffer.c_str(),
+ file_spec.GetPath().c_str(),
+ ErrorMsgBuffer.c_str());
+ break;
+ }
+ })) {
+ return Status("Failed to atomically write file %s",
+ file_spec.GetPath().c_str());
+ }
+ return status;
+}
+
+// main
+int main_platform(int argc, char *argv[]) {
+ const char *progname = argv[0];
+ const char *subcommand = argv[1];
+ argc--;
+ argv++;
+#if !defined(_WIN32)
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGHUP, signal_handler);
+#endif
+ int long_option_index = 0;
+ Status error;
+ std::string listen_host_port;
+ int ch;
+
+ std::string log_file;
+ StringRef
+ log_channels; // e.g. "lldb process threads:gdb-remote default:linux all"
+
+ GDBRemoteCommunicationServerPlatform::PortMap gdbserver_portmap;
+ int min_gdbserver_port = 0;
+ int max_gdbserver_port = 0;
+ uint16_t port_offset = 0;
+
+ FileSpec socket_file;
+ bool show_usage = false;
+ int option_error = 0;
+ int socket_error = -1;
+
+ std::string short_options(OptionParser::GetShortOptionString(g_long_options));
+
+#if __GLIBC__
+ optind = 0;
+#else
+ optreset = 1;
+ optind = 1;
+#endif
+
+ while ((ch = getopt_long_only(argc, argv, short_options.c_str(),
+ g_long_options, &long_option_index)) != -1) {
+ switch (ch) {
+ case 0: // Any optional that auto set themselves will return 0
+ break;
+
+ case 'L':
+ listen_host_port.append(optarg);
+ break;
+
+ case 'l': // Set Log File
+ if (optarg && optarg[0])
+ log_file.assign(optarg);
+ break;
+
+ case 'c': // Log Channels
+ if (optarg && optarg[0])
+ log_channels = StringRef(optarg);
+ break;
+
+ case 'f': // Socket file
+ if (optarg && optarg[0])
+ socket_file.SetFile(optarg, FileSpec::Style::native);
+ break;
+
+ case 'p': {
+ if (!llvm::to_integer(optarg, port_offset)) {
+ llvm::errs() << "error: invalid port offset string " << optarg << "\n";
+ option_error = 4;
+ break;
+ }
+ if (port_offset < LOW_PORT || port_offset > HIGH_PORT) {
+ llvm::errs() << llvm::formatv("error: port offset {0} is not in the "
+ "valid user port range of {1} - {2}\n",
+ port_offset, LOW_PORT, HIGH_PORT);
+ option_error = 5;
+ }
+ } break;
+
+ case 'P':
+ case 'm':
+ case 'M': {
+ uint16_t portnum;
+ if (!llvm::to_integer(optarg, portnum)) {
+ llvm::errs() << "error: invalid port number string " << optarg << "\n";
+ option_error = 2;
+ break;
+ }
+ if (portnum < LOW_PORT || portnum > HIGH_PORT) {
+ llvm::errs() << llvm::formatv("error: port number {0} is not in the "
+ "valid user port range of {1} - {2}\n",
+ portnum, LOW_PORT, HIGH_PORT);
+ option_error = 1;
+ break;
+ }
+ if (ch == 'P')
+ gdbserver_portmap[portnum] = LLDB_INVALID_PROCESS_ID;
+ else if (ch == 'm')
+ min_gdbserver_port = portnum;
+ else
+ max_gdbserver_port = portnum;
+ } break;
+
+ case 'h': /* fall-through is intentional */
+ case '?':
+ show_usage = true;
+ break;
+ }
+ }
+
+ if (!LLDBServerUtilities::SetupLogging(log_file, log_channels, 0))
+ return -1;
+
+ // Make a port map for a port range that was specified.
+ if (min_gdbserver_port && min_gdbserver_port < max_gdbserver_port) {
+ for (uint16_t port = min_gdbserver_port; port < max_gdbserver_port; ++port)
+ gdbserver_portmap[port] = LLDB_INVALID_PROCESS_ID;
+ } else if (min_gdbserver_port || max_gdbserver_port) {
+ fprintf(stderr, "error: --min-gdbserver-port (%u) is not lower than "
+ "--max-gdbserver-port (%u)\n",
+ min_gdbserver_port, max_gdbserver_port);
+ option_error = 3;
+ }
+
+ // Print usage and exit if no listening port is specified.
+ if (listen_host_port.empty())
+ show_usage = true;
+
+ if (show_usage || option_error) {
+ display_usage(progname, subcommand);
+ exit(option_error);
+ }
+
+ // Skip any options we consumed with getopt_long_only.
+ argc -= optind;
+ argv += optind;
+ lldb_private::Args inferior_arguments;
+ inferior_arguments.SetArguments(argc, const_cast<const char **>(argv));
+
+ const bool children_inherit_listen_socket = false;
+ // the test suite makes many connections in parallel, let's not miss any.
+ // The highest this should get reasonably is a function of the number
+ // of target CPUs. For now, let's just use 100.
+ const int backlog = 100;
+
+ std::unique_ptr<Acceptor> acceptor_up(Acceptor::Create(
+ listen_host_port, children_inherit_listen_socket, error));
+ if (error.Fail()) {
+ fprintf(stderr, "failed to create acceptor: %s", error.AsCString());
+ exit(socket_error);
+ }
+
+ error = acceptor_up->Listen(backlog);
+ if (error.Fail()) {
+ printf("failed to listen: %s\n", error.AsCString());
+ exit(socket_error);
+ }
+ if (socket_file) {
+ error =
+ save_socket_id_to_file(acceptor_up->GetLocalSocketId(), socket_file);
+ if (error.Fail()) {
+ fprintf(stderr, "failed to write socket id to %s: %s\n",
+ socket_file.GetPath().c_str(), error.AsCString());
+ return 1;
+ }
+ }
+
+ do {
+ GDBRemoteCommunicationServerPlatform platform(
+ acceptor_up->GetSocketProtocol(), acceptor_up->GetSocketScheme());
+
+ if (port_offset > 0)
+ platform.SetPortOffset(port_offset);
+
+ if (!gdbserver_portmap.empty()) {
+ platform.SetPortMap(std::move(gdbserver_portmap));
+ }
+
+ const bool children_inherit_accept_socket = true;
+ Connection *conn = nullptr;
+ error = acceptor_up->Accept(children_inherit_accept_socket, conn);
+ if (error.Fail()) {
+ printf("error: %s\n", error.AsCString());
+ exit(socket_error);
+ }
+ printf("Connection established.\n");
+ if (g_server) {
+ // Collect child zombie processes.
+#if !defined(_WIN32)
+ while (waitpid(-1, nullptr, WNOHANG) > 0)
+ ;
+#endif
+ if (fork()) {
+ // Parent doesn't need a connection to the lldb client
+ delete conn;
+
+ // Parent will continue to listen for new connections.
+ continue;
+ } else {
+ // Child process will handle the connection and exit.
+ g_server = 0;
+ // Listening socket is owned by parent process.
+ acceptor_up.release();
+ }
+ } else {
+ // If not running as a server, this process will not accept
+ // connections while a connection is active.
+ acceptor_up.reset();
+ }
+ platform.SetConnection(conn);
+
+ if (platform.IsConnected()) {
+ if (inferior_arguments.GetArgumentCount() > 0) {
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+ uint16_t port = 0;
+ std::string socket_name;
+ Status error = platform.LaunchGDBServer(inferior_arguments,
+ "", // hostname
+ pid, port, socket_name);
+ if (error.Success())
+ platform.SetPendingGdbServer(pid, port, socket_name);
+ else
+ fprintf(stderr, "failed to start gdbserver: %s\n", error.AsCString());
+ }
+
+ // After we connected, we need to get an initial ack from...
+ if (platform.HandshakeWithClient()) {
+ bool interrupt = false;
+ bool done = false;
+ while (!interrupt && !done) {
+ if (platform.GetPacketAndSendResponse(llvm::None, error, interrupt,
+ done) !=
+ GDBRemoteCommunication::PacketResult::Success)
+ break;
+ }
+
+ if (error.Fail()) {
+ fprintf(stderr, "error: %s\n", error.AsCString());
+ }
+ } else {
+ fprintf(stderr, "error: handshake with client failed\n");
+ }
+ }
+ } while (g_server);
+
+ fprintf(stderr, "lldb-server exiting...\n");
+
+ return 0;
+}
diff --git a/gnu/llvm/lldb/tools/lldb-server/lldb-server.cpp b/gnu/llvm/lldb/tools/lldb-server/lldb-server.cpp
new file mode 100644
index 00000000000..749a381ebca
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-server/lldb-server.cpp
@@ -0,0 +1,82 @@
+//===-- lldb-server.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SystemInitializerLLGS.h"
+#include "lldb/Initialization/SystemLifetimeManager.h"
+#include "lldb/lldb-private.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Signals.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static llvm::ManagedStatic<lldb_private::SystemLifetimeManager>
+ g_debugger_lifetime;
+
+static void display_usage(const char *progname) {
+ fprintf(stderr, "Usage:\n"
+ " %s v[ersion]\n"
+ " %s g[dbserver] [options]\n"
+ " %s p[latform] [options]\n"
+ "Invoke subcommand for additional help\n",
+ progname, progname, progname);
+ exit(0);
+}
+
+// Forward declarations of subcommand main methods.
+int main_gdbserver(int argc, char *argv[]);
+int main_platform(int argc, char *argv[]);
+
+namespace llgs {
+static void initialize() {
+ if (auto e = g_debugger_lifetime->Initialize(
+ std::make_unique<SystemInitializerLLGS>(), nullptr))
+ llvm::consumeError(std::move(e));
+}
+
+static void terminate_debugger() { g_debugger_lifetime->Terminate(); }
+} // namespace llgs
+
+// main
+int main(int argc, char *argv[]) {
+ llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false);
+ llvm::StringRef ToolName = argv[0];
+ llvm::sys::PrintStackTraceOnErrorSignal(ToolName);
+ llvm::PrettyStackTraceProgram X(argc, argv);
+
+ int option_error = 0;
+ const char *progname = argv[0];
+ if (argc < 2) {
+ display_usage(progname);
+ exit(option_error);
+ }
+
+ switch (argv[1][0]) {
+ case 'g':
+ llgs::initialize();
+ main_gdbserver(argc, argv);
+ llgs::terminate_debugger();
+ break;
+ case 'p':
+ llgs::initialize();
+ main_platform(argc, argv);
+ llgs::terminate_debugger();
+ break;
+ case 'v':
+ fprintf(stderr, "%s\n", lldb_private::GetVersion());
+ break;
+ default:
+ display_usage(progname);
+ exit(option_error);
+ }
+}
diff --git a/gnu/llvm/lldb/tools/lldb-server/lldb-server.exports b/gnu/llvm/lldb/tools/lldb-server/lldb-server.exports
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-server/lldb-server.exports
diff --git a/gnu/llvm/lldb/tools/lldb-test/CMakeLists.txt b/gnu/llvm/lldb/tools/lldb-test/CMakeLists.txt
new file mode 100644
index 00000000000..2ab1ceacdcd
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-test/CMakeLists.txt
@@ -0,0 +1,27 @@
+get_property(LLDB_ALL_PLUGINS GLOBAL PROPERTY LLDB_PLUGINS)
+
+add_lldb_tool(lldb-test
+ FormatUtil.cpp
+ lldb-test.cpp
+ SystemInitializerTest.cpp
+
+ LINK_LIBS
+ lldbBase
+ lldbBreakpoint
+ lldbCore
+ lldbDataFormatters
+ lldbExpression
+ lldbHost
+ lldbInitialization
+ lldbInterpreter
+ lldbSymbol
+ lldbTarget
+ lldbUtility
+ ${LLDB_ALL_PLUGINS}
+ ${host_lib}
+
+ LINK_COMPONENTS
+ Support
+ )
+
+include_directories(${LLDB_SOURCE_DIR}/source)
diff --git a/gnu/llvm/lldb/tools/lldb-test/FormatUtil.cpp b/gnu/llvm/lldb/tools/lldb-test/FormatUtil.cpp
new file mode 100644
index 00000000000..1e5e1ee0874
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-test/FormatUtil.cpp
@@ -0,0 +1,65 @@
+//===- FormatUtil.cpp ----------------------------------------- *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "FormatUtil.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/FormatVariadic.h"
+
+using namespace lldb_private;
+using namespace llvm;
+
+LinePrinter::Line::~Line() {
+ if (P)
+ P->NewLine();
+}
+
+LinePrinter::LinePrinter(int Indent, llvm::raw_ostream &Stream)
+ : OS(Stream), IndentSpaces(Indent), CurrentIndent(0) {}
+
+void LinePrinter::Indent(uint32_t Amount) {
+ if (Amount == 0)
+ Amount = IndentSpaces;
+ CurrentIndent += Amount;
+}
+
+void LinePrinter::Unindent(uint32_t Amount) {
+ if (Amount == 0)
+ Amount = IndentSpaces;
+ CurrentIndent = std::max<int>(0, CurrentIndent - Amount);
+}
+
+void LinePrinter::NewLine() {
+ OS << "\n";
+}
+
+void LinePrinter::formatBinary(StringRef Label, ArrayRef<uint8_t> Data,
+ uint32_t StartOffset) {
+ if (Data.empty()) {
+ line() << Label << " ()";
+ return;
+ }
+ line() << Label << " (";
+ OS << format_bytes_with_ascii(Data, StartOffset, 32, 4,
+ CurrentIndent + IndentSpaces, true);
+ NewLine();
+ line() << ")";
+}
+
+void LinePrinter::formatBinary(StringRef Label, ArrayRef<uint8_t> Data,
+ uint64_t Base, uint32_t StartOffset) {
+ if (Data.empty()) {
+ line() << Label << " ()";
+ return;
+ }
+ line() << Label << " (";
+ Base += StartOffset;
+ OS << format_bytes_with_ascii(Data, Base, 32, 4, CurrentIndent + IndentSpaces,
+ true);
+ NewLine();
+ line() << ")";
+}
diff --git a/gnu/llvm/lldb/tools/lldb-test/FormatUtil.h b/gnu/llvm/lldb/tools/lldb-test/FormatUtil.h
new file mode 100644
index 00000000000..91b3c085b60
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-test/FormatUtil.h
@@ -0,0 +1,77 @@
+//===- FormatUtil.h ------------------------------------------- *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLDBTEST_FORMATUTIL_H
+#define LLVM_TOOLS_LLDBTEST_FORMATUTIL_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <list>
+
+namespace lldb_private {
+
+class LinePrinter {
+ llvm::raw_ostream &OS;
+ int IndentSpaces;
+ int CurrentIndent;
+
+public:
+ class Line {
+ LinePrinter *P;
+
+ public:
+ Line(LinePrinter &P) : P(&P) { P.OS.indent(P.CurrentIndent); }
+ ~Line();
+
+ Line(Line &&RHS) : P(RHS.P) { RHS.P = nullptr; }
+ void operator=(Line &&) = delete;
+
+ operator llvm::raw_ostream &() { return P->OS; }
+ };
+
+ LinePrinter(int Indent, llvm::raw_ostream &Stream);
+
+ void Indent(uint32_t Amount = 0);
+ void Unindent(uint32_t Amount = 0);
+ void NewLine();
+
+ void printLine(const llvm::Twine &T) { line() << T; }
+ template <typename... Ts> void formatLine(const char *Fmt, Ts &&... Items) {
+ printLine(llvm::formatv(Fmt, std::forward<Ts>(Items)...));
+ }
+
+ void formatBinary(llvm::StringRef Label, llvm::ArrayRef<uint8_t> Data,
+ uint32_t StartOffset);
+ void formatBinary(llvm::StringRef Label, llvm::ArrayRef<uint8_t> Data,
+ uint64_t BaseAddr, uint32_t StartOffset);
+
+ Line line() { return Line(*this); }
+ int getIndentLevel() const { return CurrentIndent; }
+};
+
+struct AutoIndent {
+ explicit AutoIndent(LinePrinter &L, uint32_t Amount = 0)
+ : L(&L), Amount(Amount) {
+ L.Indent(Amount);
+ }
+ ~AutoIndent() {
+ if (L)
+ L->Unindent(Amount);
+ }
+
+ LinePrinter *L = nullptr;
+ uint32_t Amount = 0;
+};
+
+} // namespace lldb_private
+
+#endif
diff --git a/gnu/llvm/lldb/tools/lldb-test/SystemInitializerTest.cpp b/gnu/llvm/lldb/tools/lldb-test/SystemInitializerTest.cpp
new file mode 100644
index 00000000000..8841e4d6583
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-test/SystemInitializerTest.cpp
@@ -0,0 +1,351 @@
+//===-- SystemInitializerTest.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SystemInitializerTest.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Initialization/SystemInitializerCommon.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Utility/Timer.h"
+
+#include "Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h"
+#include "Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.h"
+#include "Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h"
+#include "Plugins/ABI/SysV-arm/ABISysV_arm.h"
+#include "Plugins/ABI/SysV-arm64/ABISysV_arm64.h"
+#include "Plugins/ABI/SysV-hexagon/ABISysV_hexagon.h"
+#include "Plugins/ABI/SysV-i386/ABISysV_i386.h"
+#include "Plugins/ABI/SysV-mips/ABISysV_mips.h"
+#include "Plugins/ABI/SysV-mips64/ABISysV_mips64.h"
+#include "Plugins/ABI/SysV-ppc/ABISysV_ppc.h"
+#include "Plugins/ABI/SysV-ppc64/ABISysV_ppc64.h"
+#include "Plugins/ABI/SysV-s390x/ABISysV_s390x.h"
+#include "Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h"
+#include "Plugins/ABI/Windows-x86_64/ABIWindows_x86_64.h"
+#include "Plugins/Architecture/Arm/ArchitectureArm.h"
+#include "Plugins/Architecture/PPC64/ArchitecturePPC64.h"
+#include "Plugins/Disassembler/llvm/DisassemblerLLVMC.h"
+#include "Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.h"
+#include "Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h"
+#include "Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h"
+#include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h"
+#include "Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h"
+#include "Plugins/Instruction/ARM64/EmulateInstructionARM64.h"
+#include "Plugins/Instruction/PPC64/EmulateInstructionPPC64.h"
+#include "Plugins/InstrumentationRuntime/ASan/ASanRuntime.h"
+#include "Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.h"
+#include "Plugins/InstrumentationRuntime/TSan/TSanRuntime.h"
+#include "Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.h"
+#include "Plugins/JITLoader/GDB/JITLoaderGDB.h"
+#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
+#include "Plugins/Language/ObjC/ObjCLanguage.h"
+#include "Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h"
+#include "Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h"
+#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h"
+#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h"
+#include "Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h"
+#include "Plugins/MemoryHistory/asan/MemoryHistoryASan.h"
+#include "Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h"
+#include "Plugins/ObjectFile/ELF/ObjectFileELF.h"
+#include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h"
+#include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h"
+#include "Plugins/Platform/Android/PlatformAndroid.h"
+#include "Plugins/Platform/FreeBSD/PlatformFreeBSD.h"
+#include "Plugins/Platform/Linux/PlatformLinux.h"
+#include "Plugins/Platform/MacOSX/PlatformMacOSX.h"
+#include "Plugins/Platform/MacOSX/PlatformRemoteiOS.h"
+#include "Plugins/Platform/NetBSD/PlatformNetBSD.h"
+#include "Plugins/Platform/OpenBSD/PlatformOpenBSD.h"
+#include "Plugins/Platform/Windows/PlatformWindows.h"
+#include "Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h"
+#include "Plugins/Process/elf-core/ProcessElfCore.h"
+#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h"
+#include "Plugins/Process/minidump/ProcessMinidump.h"
+#include "Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h"
+#include "Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h"
+#include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h"
+#include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h"
+#include "Plugins/SymbolFile/PDB/SymbolFilePDB.h"
+#include "Plugins/SymbolFile/Symtab/SymbolFileSymtab.h"
+#include "Plugins/SymbolVendor/ELF/SymbolVendorELF.h"
+#include "Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.h"
+#include "Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h"
+#include "Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h"
+
+#if defined(__APPLE__)
+#include "Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h"
+#include "Plugins/Platform/MacOSX/PlatformAppleTVSimulator.h"
+#include "Plugins/Platform/MacOSX/PlatformAppleWatchSimulator.h"
+#include "Plugins/Platform/MacOSX/PlatformDarwinKernel.h"
+#include "Plugins/Platform/MacOSX/PlatformRemoteAppleTV.h"
+#include "Plugins/Platform/MacOSX/PlatformRemoteAppleWatch.h"
+#include "Plugins/Platform/MacOSX/PlatformiOSSimulator.h"
+#include "Plugins/Process/MacOSX-Kernel/ProcessKDP.h"
+#include "Plugins/Process/mach-core/ProcessMachCore.h"
+#include "Plugins/SymbolVendor/MacOSX/SymbolVendorMacOSX.h"
+#endif
+#include "Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.h"
+
+#if defined(__FreeBSD__)
+#include "Plugins/Process/FreeBSD/ProcessFreeBSD.h"
+#endif
+
+#if defined(_WIN32)
+#include "Plugins/Process/Windows/Common/ProcessWindows.h"
+#include "lldb/Host/windows/windows.h"
+#endif
+
+#include "llvm/Support/TargetSelect.h"
+
+#include <string>
+
+using namespace lldb_private;
+
+SystemInitializerTest::SystemInitializerTest() {}
+
+SystemInitializerTest::~SystemInitializerTest() {}
+
+#define LLDB_PROCESS_AArch64(op) \
+ ABIMacOSX_arm64::op(); \
+ ABISysV_arm64::op();
+#define LLDB_PROCESS_ARM(op) \
+ ABIMacOSX_arm::op(); \
+ ABISysV_arm::op();
+#define LLDB_PROCESS_Hexagon(op) ABISysV_hexagon::op();
+#define LLDB_PROCESS_Mips(op) \
+ ABISysV_mips::op(); \
+ ABISysV_mips64::op();
+#define LLDB_PROCESS_PowerPC(op) \
+ ABISysV_ppc::op(); \
+ ABISysV_ppc64::op();
+#define LLDB_PROCESS_SystemZ(op) ABISysV_s390x::op();
+#define LLDB_PROCESS_X86(op) \
+ ABIMacOSX_i386::op(); \
+ ABISysV_i386::op(); \
+ ABISysV_x86_64::op(); \
+ ABIWindows_x86_64::op();
+
+#define LLDB_PROCESS_AMDGPU(op)
+#define LLDB_PROCESS_ARC(op)
+#define LLDB_PROCESS_AVR(op)
+#define LLDB_PROCESS_BPF(op)
+#define LLDB_PROCESS_Lanai(op)
+#define LLDB_PROCESS_MSP430(op)
+#define LLDB_PROCESS_NVPTX(op)
+#define LLDB_PROCESS_RISCV(op)
+#define LLDB_PROCESS_Sparc(op)
+#define LLDB_PROCESS_WebAssembly(op)
+#define LLDB_PROCESS_XCore(op)
+
+llvm::Error SystemInitializerTest::Initialize() {
+ if (auto e = SystemInitializerCommon::Initialize())
+ return e;
+
+ breakpad::ObjectFileBreakpad::Initialize();
+ ObjectFileELF::Initialize();
+ ObjectFileMachO::Initialize();
+ ObjectFilePECOFF::Initialize();
+
+ ScriptInterpreterNone::Initialize();
+
+
+ platform_freebsd::PlatformFreeBSD::Initialize();
+ platform_linux::PlatformLinux::Initialize();
+ platform_netbsd::PlatformNetBSD::Initialize();
+ platform_openbsd::PlatformOpenBSD::Initialize();
+ PlatformWindows::Initialize();
+ platform_android::PlatformAndroid::Initialize();
+ PlatformRemoteiOS::Initialize();
+ PlatformMacOSX::Initialize();
+#if defined(__APPLE__)
+ PlatformiOSSimulator::Initialize();
+ PlatformDarwinKernel::Initialize();
+#endif
+
+ // Initialize LLVM and Clang
+ llvm::InitializeAllTargets();
+ llvm::InitializeAllAsmPrinters();
+ llvm::InitializeAllTargetMCs();
+ llvm::InitializeAllDisassemblers();
+
+ ClangASTContext::Initialize();
+
+#define LLVM_TARGET(t) LLDB_PROCESS_ ## t(Initialize)
+#include "llvm/Config/Targets.def"
+
+ ArchitectureArm::Initialize();
+ ArchitecturePPC64::Initialize();
+
+ DisassemblerLLVMC::Initialize();
+
+ JITLoaderGDB::Initialize();
+ ProcessElfCore::Initialize();
+ minidump::ProcessMinidump::Initialize();
+ MemoryHistoryASan::Initialize();
+ AddressSanitizerRuntime::Initialize();
+ ThreadSanitizerRuntime::Initialize();
+ UndefinedBehaviorSanitizerRuntime::Initialize();
+ MainThreadCheckerRuntime::Initialize();
+
+ SymbolVendorELF::Initialize();
+ breakpad::SymbolFileBreakpad::Initialize();
+ SymbolFileDWARF::Initialize();
+ SymbolFilePDB::Initialize();
+ SymbolFileSymtab::Initialize();
+ UnwindAssemblyInstEmulation::Initialize();
+ UnwindAssembly_x86::Initialize();
+ EmulateInstructionARM64::Initialize();
+ EmulateInstructionPPC64::Initialize();
+ SymbolFileDWARFDebugMap::Initialize();
+ ItaniumABILanguageRuntime::Initialize();
+ AppleObjCRuntimeV2::Initialize();
+ AppleObjCRuntimeV1::Initialize();
+ SystemRuntimeMacOSX::Initialize();
+ RenderScriptRuntime::Initialize();
+
+ CPlusPlusLanguage::Initialize();
+ ObjCLanguage::Initialize();
+ ObjCPlusPlusLanguage::Initialize();
+
+#if defined(_WIN32)
+ ProcessWindows::Initialize();
+#endif
+#if defined(__FreeBSD__)
+ ProcessFreeBSD::Initialize();
+#endif
+#if defined(__APPLE__)
+ SymbolVendorMacOSX::Initialize();
+ ProcessKDP::Initialize();
+ ProcessMachCore::Initialize();
+ PlatformAppleTVSimulator::Initialize();
+ PlatformAppleWatchSimulator::Initialize();
+ PlatformRemoteAppleTV::Initialize();
+ PlatformRemoteAppleWatch::Initialize();
+ DynamicLoaderDarwinKernel::Initialize();
+#endif
+
+ // This plugin is valid on any host that talks to a Darwin remote.
+ // It shouldn't be limited to __APPLE__.
+ StructuredDataDarwinLog::Initialize();
+
+ // Platform agnostic plugins
+ platform_gdb_server::PlatformRemoteGDBServer::Initialize();
+
+ process_gdb_remote::ProcessGDBRemote::Initialize();
+ DynamicLoaderMacOSXDYLD::Initialize();
+ DynamicLoaderMacOS::Initialize();
+ DynamicLoaderPOSIXDYLD::Initialize();
+ DynamicLoaderStatic::Initialize();
+ DynamicLoaderWindowsDYLD::Initialize();
+
+ // Scan for any system or user LLDB plug-ins
+ PluginManager::Initialize();
+
+ // The process settings need to know about installed plug-ins, so the Settings
+ // must be initialized
+ // AFTER PluginManager::Initialize is called.
+
+ Debugger::SettingsInitialize();
+
+ return llvm::Error::success();
+}
+
+void SystemInitializerTest::Terminate() {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION);
+
+ Debugger::SettingsTerminate();
+
+ // Terminate and unload and loaded system or user LLDB plug-ins
+ PluginManager::Terminate();
+
+ ClangASTContext::Terminate();
+
+#define LLVM_TARGET(t) LLDB_PROCESS_ ## t(Terminate)
+#include "llvm/Config/Targets.def"
+
+ DisassemblerLLVMC::Terminate();
+
+ JITLoaderGDB::Terminate();
+ ProcessElfCore::Terminate();
+ minidump::ProcessMinidump::Terminate();
+ MemoryHistoryASan::Terminate();
+ AddressSanitizerRuntime::Terminate();
+ ThreadSanitizerRuntime::Terminate();
+ UndefinedBehaviorSanitizerRuntime::Terminate();
+ MainThreadCheckerRuntime::Terminate();
+ SymbolVendorELF::Terminate();
+ breakpad::SymbolFileBreakpad::Terminate();
+ SymbolFileDWARF::Terminate();
+ SymbolFilePDB::Terminate();
+ SymbolFileSymtab::Terminate();
+ UnwindAssembly_x86::Terminate();
+ UnwindAssemblyInstEmulation::Terminate();
+ EmulateInstructionARM64::Terminate();
+ EmulateInstructionPPC64::Terminate();
+ SymbolFileDWARFDebugMap::Terminate();
+ ItaniumABILanguageRuntime::Terminate();
+ AppleObjCRuntimeV2::Terminate();
+ AppleObjCRuntimeV1::Terminate();
+ SystemRuntimeMacOSX::Terminate();
+ RenderScriptRuntime::Terminate();
+
+ CPlusPlusLanguage::Terminate();
+ ObjCLanguage::Terminate();
+ ObjCPlusPlusLanguage::Terminate();
+
+#if defined(__APPLE__)
+ DynamicLoaderDarwinKernel::Terminate();
+ ProcessMachCore::Terminate();
+ ProcessKDP::Terminate();
+ SymbolVendorMacOSX::Terminate();
+ PlatformAppleTVSimulator::Terminate();
+ PlatformAppleWatchSimulator::Terminate();
+ PlatformRemoteAppleTV::Terminate();
+ PlatformRemoteAppleWatch::Terminate();
+#endif
+
+#if defined(__FreeBSD__)
+ ProcessFreeBSD::Terminate();
+#endif
+ Debugger::SettingsTerminate();
+
+ platform_gdb_server::PlatformRemoteGDBServer::Terminate();
+ process_gdb_remote::ProcessGDBRemote::Terminate();
+ StructuredDataDarwinLog::Terminate();
+
+ DynamicLoaderMacOSXDYLD::Terminate();
+ DynamicLoaderMacOS::Terminate();
+ DynamicLoaderPOSIXDYLD::Terminate();
+ DynamicLoaderStatic::Terminate();
+ DynamicLoaderWindowsDYLD::Terminate();
+
+
+ platform_freebsd::PlatformFreeBSD::Terminate();
+ platform_linux::PlatformLinux::Terminate();
+ platform_netbsd::PlatformNetBSD::Terminate();
+ platform_openbsd::PlatformOpenBSD::Terminate();
+ PlatformWindows::Terminate();
+ platform_android::PlatformAndroid::Terminate();
+ PlatformMacOSX::Terminate();
+ PlatformRemoteiOS::Terminate();
+#if defined(__APPLE__)
+ PlatformiOSSimulator::Terminate();
+ PlatformDarwinKernel::Terminate();
+#endif
+
+ breakpad::ObjectFileBreakpad::Terminate();
+ ObjectFileELF::Terminate();
+ ObjectFileMachO::Terminate();
+ ObjectFilePECOFF::Terminate();
+
+ // Now shutdown the common parts, in reverse order.
+ SystemInitializerCommon::Terminate();
+}
diff --git a/gnu/llvm/lldb/tools/lldb-test/SystemInitializerTest.h b/gnu/llvm/lldb/tools/lldb-test/SystemInitializerTest.h
new file mode 100644
index 00000000000..4f58d2a64a6
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-test/SystemInitializerTest.h
@@ -0,0 +1,32 @@
+//===-- SystemInitializerTest.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_API_SYSTEM_INITIALIZER_TEST_H
+#define LLDB_API_SYSTEM_INITIALIZER_TEST_H
+
+#include "lldb/Initialization/SystemInitializerCommon.h"
+
+namespace lldb_private {
+/// Initializes lldb.
+///
+/// This class is responsible for initializing all of lldb system
+/// services needed to use the full LLDB application. This class is
+/// not intended to be used externally, but is instead used
+/// internally by SBDebugger to initialize the system.
+class SystemInitializerTest : public SystemInitializerCommon {
+public:
+ SystemInitializerTest();
+ ~SystemInitializerTest() override;
+
+ llvm::Error Initialize() override;
+ void Terminate() override;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_API_SYSTEM_INITIALIZER_FULL_H
diff --git a/gnu/llvm/lldb/tools/lldb-test/lldb-test.cpp b/gnu/llvm/lldb/tools/lldb-test/lldb-test.cpp
new file mode 100644
index 00000000000..66c9a87301e
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-test/lldb-test.cpp
@@ -0,0 +1,1103 @@
+//===- lldb-test.cpp ------------------------------------------ *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "FormatUtil.h"
+#include "SystemInitializerTest.h"
+
+#include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Expression/IRMemoryMap.h"
+#include "lldb/Initialization/SystemLifetimeManager.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Symbol/TypeMap.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "llvm/ADT/IntervalMap.h"
+#include "llvm/ADT/ScopeExit.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/WithColor.h"
+
+#include <cstdio>
+#include <thread>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm;
+
+namespace opts {
+static cl::SubCommand BreakpointSubcommand("breakpoints",
+ "Test breakpoint resolution");
+cl::SubCommand ObjectFileSubcommand("object-file",
+ "Display LLDB object file information");
+cl::SubCommand SymbolsSubcommand("symbols", "Dump symbols for an object file");
+cl::SubCommand IRMemoryMapSubcommand("ir-memory-map", "Test IRMemoryMap");
+
+cl::opt<std::string> Log("log", cl::desc("Path to a log file"), cl::init(""),
+ cl::sub(BreakpointSubcommand),
+ cl::sub(ObjectFileSubcommand),
+ cl::sub(SymbolsSubcommand),
+ cl::sub(IRMemoryMapSubcommand));
+
+/// Create a target using the file pointed to by \p Filename, or abort.
+TargetSP createTarget(Debugger &Dbg, const std::string &Filename);
+
+/// Read \p Filename into a null-terminated buffer, or abort.
+std::unique_ptr<MemoryBuffer> openFile(const std::string &Filename);
+
+namespace breakpoint {
+static cl::opt<std::string> Target(cl::Positional, cl::desc("<target>"),
+ cl::Required, cl::sub(BreakpointSubcommand));
+static cl::opt<std::string> CommandFile(cl::Positional,
+ cl::desc("<command-file>"),
+ cl::init("-"),
+ cl::sub(BreakpointSubcommand));
+static cl::opt<bool> Persistent(
+ "persistent",
+ cl::desc("Don't automatically remove all breakpoints before each command"),
+ cl::sub(BreakpointSubcommand));
+
+static llvm::StringRef plural(uintmax_t value) { return value == 1 ? "" : "s"; }
+static void dumpState(const BreakpointList &List, LinePrinter &P);
+static std::string substitute(StringRef Cmd);
+static int evaluateBreakpoints(Debugger &Dbg);
+} // namespace breakpoint
+
+namespace object {
+cl::opt<bool> SectionContents("contents",
+ cl::desc("Dump each section's contents"),
+ cl::sub(ObjectFileSubcommand));
+cl::opt<bool> SectionDependentModules("dep-modules",
+ cl::desc("Dump each dependent module"),
+ cl::sub(ObjectFileSubcommand));
+cl::list<std::string> InputFilenames(cl::Positional, cl::desc("<input files>"),
+ cl::OneOrMore,
+ cl::sub(ObjectFileSubcommand));
+} // namespace object
+
+namespace symbols {
+static cl::opt<std::string> InputFile(cl::Positional, cl::desc("<input file>"),
+ cl::Required, cl::sub(SymbolsSubcommand));
+
+static cl::opt<std::string>
+ SymbolPath("symbol-file",
+ cl::desc("The file from which to fetch symbol information."),
+ cl::value_desc("file"), cl::sub(SymbolsSubcommand));
+
+enum class FindType {
+ None,
+ Function,
+ Block,
+ Namespace,
+ Type,
+ Variable,
+};
+static cl::opt<FindType> Find(
+ "find", cl::desc("Choose search type:"),
+ cl::values(
+ clEnumValN(FindType::None, "none", "No search, just dump the module."),
+ clEnumValN(FindType::Function, "function", "Find functions."),
+ clEnumValN(FindType::Block, "block", "Find blocks."),
+ clEnumValN(FindType::Namespace, "namespace", "Find namespaces."),
+ clEnumValN(FindType::Type, "type", "Find types."),
+ clEnumValN(FindType::Variable, "variable", "Find global variables.")),
+ cl::sub(SymbolsSubcommand));
+
+static cl::opt<std::string> Name("name", cl::desc("Name to find."),
+ cl::sub(SymbolsSubcommand));
+static cl::opt<bool>
+ Regex("regex",
+ cl::desc("Search using regular expressions (avaliable for variables "
+ "and functions only)."),
+ cl::sub(SymbolsSubcommand));
+static cl::opt<std::string>
+ Context("context",
+ cl::desc("Restrict search to the context of the given variable."),
+ cl::value_desc("variable"), cl::sub(SymbolsSubcommand));
+
+static cl::opt<std::string> CompilerContext(
+ "compiler-context",
+ cl::desc("Specify a compiler context as \"kind:name,...\"."),
+ cl::value_desc("context"), cl::sub(SymbolsSubcommand));
+
+static cl::opt<std::string>
+ Language("language", cl::desc("Specify a language type, like C99."),
+ cl::value_desc("language"), cl::sub(SymbolsSubcommand));
+
+static cl::list<FunctionNameType> FunctionNameFlags(
+ "function-flags", cl::desc("Function search flags:"),
+ cl::values(clEnumValN(eFunctionNameTypeAuto, "auto",
+ "Automatically deduce flags based on name."),
+ clEnumValN(eFunctionNameTypeFull, "full", "Full function name."),
+ clEnumValN(eFunctionNameTypeBase, "base", "Base name."),
+ clEnumValN(eFunctionNameTypeMethod, "method", "Method name."),
+ clEnumValN(eFunctionNameTypeSelector, "selector",
+ "Selector name.")),
+ cl::sub(SymbolsSubcommand));
+static FunctionNameType getFunctionNameFlags() {
+ FunctionNameType Result = FunctionNameType(0);
+ for (FunctionNameType Flag : FunctionNameFlags)
+ Result = FunctionNameType(Result | Flag);
+ return Result;
+}
+
+static cl::opt<bool> DumpAST("dump-ast",
+ cl::desc("Dump AST restored from symbols."),
+ cl::sub(SymbolsSubcommand));
+static cl::opt<bool>
+ DumpClangAST("dump-clang-ast",
+ cl::desc("Dump clang AST restored from symbols."),
+ cl::sub(SymbolsSubcommand));
+
+static cl::opt<bool> Verify("verify", cl::desc("Verify symbol information."),
+ cl::sub(SymbolsSubcommand));
+
+static cl::opt<std::string> File("file",
+ cl::desc("File (compile unit) to search."),
+ cl::sub(SymbolsSubcommand));
+static cl::opt<int> Line("line", cl::desc("Line to search."),
+ cl::sub(SymbolsSubcommand));
+
+static Expected<CompilerDeclContext> getDeclContext(SymbolFile &Symfile);
+
+static Error findFunctions(lldb_private::Module &Module);
+static Error findBlocks(lldb_private::Module &Module);
+static Error findNamespaces(lldb_private::Module &Module);
+static Error findTypes(lldb_private::Module &Module);
+static Error findVariables(lldb_private::Module &Module);
+static Error dumpModule(lldb_private::Module &Module);
+static Error dumpAST(lldb_private::Module &Module);
+static Error dumpClangAST(lldb_private::Module &Module);
+static Error verify(lldb_private::Module &Module);
+
+static Expected<Error (*)(lldb_private::Module &)> getAction();
+static int dumpSymbols(Debugger &Dbg);
+} // namespace symbols
+
+namespace irmemorymap {
+static cl::opt<std::string> Target(cl::Positional, cl::desc("<target>"),
+ cl::Required,
+ cl::sub(IRMemoryMapSubcommand));
+static cl::opt<std::string> CommandFile(cl::Positional,
+ cl::desc("<command-file>"),
+ cl::init("-"),
+ cl::sub(IRMemoryMapSubcommand));
+static cl::opt<bool> UseHostOnlyAllocationPolicy(
+ "host-only", cl::desc("Use the host-only allocation policy"),
+ cl::init(false), cl::sub(IRMemoryMapSubcommand));
+
+using AllocationT = std::pair<addr_t, addr_t>;
+using AddrIntervalMap =
+ IntervalMap<addr_t, unsigned, 8, IntervalMapHalfOpenInfo<addr_t>>;
+
+struct IRMemoryMapTestState {
+ TargetSP Target;
+ IRMemoryMap Map;
+
+ AddrIntervalMap::Allocator IntervalMapAllocator;
+ AddrIntervalMap Allocations;
+
+ StringMap<addr_t> Label2AddrMap;
+
+ IRMemoryMapTestState(TargetSP Target)
+ : Target(Target), Map(Target), Allocations(IntervalMapAllocator) {}
+};
+
+bool evalMalloc(StringRef Line, IRMemoryMapTestState &State);
+bool evalFree(StringRef Line, IRMemoryMapTestState &State);
+int evaluateMemoryMapCommands(Debugger &Dbg);
+} // namespace irmemorymap
+
+} // namespace opts
+
+std::vector<CompilerContext> parseCompilerContext() {
+ std::vector<CompilerContext> result;
+ if (opts::symbols::CompilerContext.empty())
+ return result;
+
+ StringRef str{opts::symbols::CompilerContext};
+ SmallVector<StringRef, 8> entries_str;
+ str.split(entries_str, ',', /*maxSplit*/-1, /*keepEmpty=*/false);
+ for (auto entry_str : entries_str) {
+ StringRef key, value;
+ std::tie(key, value) = entry_str.split(':');
+ auto kind =
+ StringSwitch<CompilerContextKind>(key)
+ .Case("TranslationUnit", CompilerContextKind::TranslationUnit)
+ .Case("Module", CompilerContextKind::Module)
+ .Case("Namespace", CompilerContextKind::Namespace)
+ .Case("Class", CompilerContextKind::Class)
+ .Case("Struct", CompilerContextKind::Struct)
+ .Case("Union", CompilerContextKind::Union)
+ .Case("Function", CompilerContextKind::Function)
+ .Case("Variable", CompilerContextKind::Variable)
+ .Case("Enum", CompilerContextKind::Enum)
+ .Case("Typedef", CompilerContextKind::Typedef)
+ .Case("AnyModule", CompilerContextKind::AnyModule)
+ .Case("AnyType", CompilerContextKind::AnyType)
+ .Default(CompilerContextKind::Invalid);
+ if (value.empty()) {
+ WithColor::error() << "compiler context entry has no \"name\"\n";
+ exit(1);
+ }
+ result.push_back({kind, ConstString{value}});
+ }
+ outs() << "Search context: {\n";
+ for (auto entry: result)
+ entry.Dump();
+ outs() << "}\n";
+
+ return result;
+}
+
+template <typename... Args>
+static Error make_string_error(const char *Format, Args &&... args) {
+ return llvm::make_error<llvm::StringError>(
+ llvm::formatv(Format, std::forward<Args>(args)...).str(),
+ llvm::inconvertibleErrorCode());
+}
+
+TargetSP opts::createTarget(Debugger &Dbg, const std::string &Filename) {
+ TargetSP Target;
+ Status ST = Dbg.GetTargetList().CreateTarget(
+ Dbg, Filename, /*triple*/ "", eLoadDependentsNo,
+ /*platform_options*/ nullptr, Target);
+ if (ST.Fail()) {
+ errs() << formatv("Failed to create target '{0}: {1}\n", Filename, ST);
+ exit(1);
+ }
+ return Target;
+}
+
+std::unique_ptr<MemoryBuffer> opts::openFile(const std::string &Filename) {
+ auto MB = MemoryBuffer::getFileOrSTDIN(Filename);
+ if (!MB) {
+ errs() << formatv("Could not open file '{0}: {1}\n", Filename,
+ MB.getError().message());
+ exit(1);
+ }
+ return std::move(*MB);
+}
+
+void opts::breakpoint::dumpState(const BreakpointList &List, LinePrinter &P) {
+ P.formatLine("{0} breakpoint{1}", List.GetSize(), plural(List.GetSize()));
+ if (List.GetSize() > 0)
+ P.formatLine("At least one breakpoint.");
+ for (size_t i = 0, e = List.GetSize(); i < e; ++i) {
+ BreakpointSP BP = List.GetBreakpointAtIndex(i);
+ P.formatLine("Breakpoint ID {0}:", BP->GetID());
+ AutoIndent Indent(P, 2);
+ P.formatLine("{0} location{1}.", BP->GetNumLocations(),
+ plural(BP->GetNumLocations()));
+ if (BP->GetNumLocations() > 0)
+ P.formatLine("At least one location.");
+ P.formatLine("{0} resolved location{1}.", BP->GetNumResolvedLocations(),
+ plural(BP->GetNumResolvedLocations()));
+ if (BP->GetNumResolvedLocations() > 0)
+ P.formatLine("At least one resolved location.");
+ for (size_t l = 0, le = BP->GetNumLocations(); l < le; ++l) {
+ BreakpointLocationSP Loc = BP->GetLocationAtIndex(l);
+ P.formatLine("Location ID {0}:", Loc->GetID());
+ AutoIndent Indent(P, 2);
+ P.formatLine("Enabled: {0}", Loc->IsEnabled());
+ P.formatLine("Resolved: {0}", Loc->IsResolved());
+ SymbolContext sc;
+ Loc->GetAddress().CalculateSymbolContext(&sc);
+ lldb_private::StreamString S;
+ sc.DumpStopContext(&S, BP->GetTarget().GetProcessSP().get(),
+ Loc->GetAddress(), false, true, false, true, true);
+ P.formatLine("Address: {0}", S.GetString());
+ }
+ }
+ P.NewLine();
+}
+
+std::string opts::breakpoint::substitute(StringRef Cmd) {
+ std::string Result;
+ raw_string_ostream OS(Result);
+ while (!Cmd.empty()) {
+ switch (Cmd[0]) {
+ case '%':
+ if (Cmd.consume_front("%p") && (Cmd.empty() || !isalnum(Cmd[0]))) {
+ OS << sys::path::parent_path(breakpoint::CommandFile);
+ break;
+ }
+ LLVM_FALLTHROUGH;
+ default:
+ size_t pos = Cmd.find('%');
+ OS << Cmd.substr(0, pos);
+ Cmd = Cmd.substr(pos);
+ break;
+ }
+ }
+ return std::move(OS.str());
+}
+
+int opts::breakpoint::evaluateBreakpoints(Debugger &Dbg) {
+ TargetSP Target = opts::createTarget(Dbg, breakpoint::Target);
+ std::unique_ptr<MemoryBuffer> MB = opts::openFile(breakpoint::CommandFile);
+
+ LinePrinter P(4, outs());
+ StringRef Rest = MB->getBuffer();
+ int HadErrors = 0;
+ while (!Rest.empty()) {
+ StringRef Line;
+ std::tie(Line, Rest) = Rest.split('\n');
+ Line = Line.ltrim().rtrim();
+ if (Line.empty() || Line[0] == '#')
+ continue;
+
+ if (!Persistent)
+ Target->RemoveAllBreakpoints(/*internal_also*/ true);
+
+ std::string Command = substitute(Line);
+ P.formatLine("Command: {0}", Command);
+ CommandReturnObject Result;
+ if (!Dbg.GetCommandInterpreter().HandleCommand(
+ Command.c_str(), /*add_to_history*/ eLazyBoolNo, Result)) {
+ P.formatLine("Failed: {0}", Result.GetErrorData());
+ HadErrors = 1;
+ continue;
+ }
+
+ dumpState(Target->GetBreakpointList(/*internal*/ false), P);
+ }
+ return HadErrors;
+}
+
+Expected<CompilerDeclContext>
+opts::symbols::getDeclContext(SymbolFile &Symfile) {
+ if (Context.empty())
+ return CompilerDeclContext();
+ VariableList List;
+ Symfile.FindGlobalVariables(ConstString(Context), nullptr, UINT32_MAX, List);
+ if (List.Empty())
+ return make_string_error("Context search didn't find a match.");
+ if (List.GetSize() > 1)
+ return make_string_error("Context search found multiple matches.");
+ return List.GetVariableAtIndex(0)->GetDeclContext();
+}
+
+Error opts::symbols::findFunctions(lldb_private::Module &Module) {
+ SymbolFile &Symfile = *Module.GetSymbolFile();
+ SymbolContextList List;
+ if (!File.empty()) {
+ assert(Line != 0);
+
+ FileSpec src_file(File);
+ size_t cu_count = Module.GetNumCompileUnits();
+ for (size_t i = 0; i < cu_count; i++) {
+ lldb::CompUnitSP cu_sp = Module.GetCompileUnitAtIndex(i);
+ if (!cu_sp)
+ continue;
+
+ LineEntry le;
+ cu_sp->FindLineEntry(0, Line, &src_file, false, &le);
+ if (!le.IsValid())
+ continue;
+ const bool include_inlined_functions = false;
+ auto addr =
+ le.GetSameLineContiguousAddressRange(include_inlined_functions)
+ .GetBaseAddress();
+ if (!addr.IsValid())
+ continue;
+
+ SymbolContext sc;
+ uint32_t resolved =
+ addr.CalculateSymbolContext(&sc, eSymbolContextFunction);
+ if (resolved & eSymbolContextFunction)
+ List.Append(sc);
+ }
+ } else if (Regex) {
+ RegularExpression RE(Name);
+ assert(RE.IsValid());
+ List.Clear();
+ Symfile.FindFunctions(RE, true, List);
+ } else {
+ Expected<CompilerDeclContext> ContextOr = getDeclContext(Symfile);
+ if (!ContextOr)
+ return ContextOr.takeError();
+ CompilerDeclContext *ContextPtr =
+ ContextOr->IsValid() ? &*ContextOr : nullptr;
+
+ List.Clear();
+ Symfile.FindFunctions(ConstString(Name), ContextPtr, getFunctionNameFlags(),
+ true, List);
+ }
+ outs() << formatv("Found {0} functions:\n", List.GetSize());
+ StreamString Stream;
+ List.Dump(&Stream, nullptr);
+ outs() << Stream.GetData() << "\n";
+ return Error::success();
+}
+
+Error opts::symbols::findBlocks(lldb_private::Module &Module) {
+ assert(!Regex);
+ assert(!File.empty());
+ assert(Line != 0);
+
+ SymbolContextList List;
+
+ FileSpec src_file(File);
+ size_t cu_count = Module.GetNumCompileUnits();
+ for (size_t i = 0; i < cu_count; i++) {
+ lldb::CompUnitSP cu_sp = Module.GetCompileUnitAtIndex(i);
+ if (!cu_sp)
+ continue;
+
+ LineEntry le;
+ cu_sp->FindLineEntry(0, Line, &src_file, false, &le);
+ if (!le.IsValid())
+ continue;
+ const bool include_inlined_functions = false;
+ auto addr = le.GetSameLineContiguousAddressRange(include_inlined_functions)
+ .GetBaseAddress();
+ if (!addr.IsValid())
+ continue;
+
+ SymbolContext sc;
+ uint32_t resolved = addr.CalculateSymbolContext(&sc, eSymbolContextBlock);
+ if (resolved & eSymbolContextBlock)
+ List.Append(sc);
+ }
+
+ outs() << formatv("Found {0} blocks:\n", List.GetSize());
+ StreamString Stream;
+ List.Dump(&Stream, nullptr);
+ outs() << Stream.GetData() << "\n";
+ return Error::success();
+}
+
+Error opts::symbols::findNamespaces(lldb_private::Module &Module) {
+ SymbolFile &Symfile = *Module.GetSymbolFile();
+ Expected<CompilerDeclContext> ContextOr = getDeclContext(Symfile);
+ if (!ContextOr)
+ return ContextOr.takeError();
+ CompilerDeclContext *ContextPtr =
+ ContextOr->IsValid() ? &*ContextOr : nullptr;
+
+ CompilerDeclContext Result =
+ Symfile.FindNamespace(ConstString(Name), ContextPtr);
+ if (Result)
+ outs() << "Found namespace: "
+ << Result.GetScopeQualifiedName().GetStringRef() << "\n";
+ else
+ outs() << "Namespace not found.\n";
+ return Error::success();
+}
+
+Error opts::symbols::findTypes(lldb_private::Module &Module) {
+ SymbolFile &Symfile = *Module.GetSymbolFile();
+ Expected<CompilerDeclContext> ContextOr = getDeclContext(Symfile);
+ if (!ContextOr)
+ return ContextOr.takeError();
+ CompilerDeclContext *ContextPtr =
+ ContextOr->IsValid() ? &*ContextOr : nullptr;
+
+ LanguageSet languages;
+ if (!Language.empty())
+ languages.Insert(Language::GetLanguageTypeFromString(Language));
+
+ DenseSet<SymbolFile *> SearchedFiles;
+ TypeMap Map;
+ if (!Name.empty())
+ Symfile.FindTypes(ConstString(Name), ContextPtr, UINT32_MAX, SearchedFiles,
+ Map);
+ else
+ Module.FindTypes(parseCompilerContext(), languages, SearchedFiles, Map);
+
+ outs() << formatv("Found {0} types:\n", Map.GetSize());
+ StreamString Stream;
+ Map.Dump(&Stream, false);
+ outs() << Stream.GetData() << "\n";
+ return Error::success();
+}
+
+Error opts::symbols::findVariables(lldb_private::Module &Module) {
+ SymbolFile &Symfile = *Module.GetSymbolFile();
+ VariableList List;
+ if (Regex) {
+ RegularExpression RE(Name);
+ assert(RE.IsValid());
+ Symfile.FindGlobalVariables(RE, UINT32_MAX, List);
+ } else if (!File.empty()) {
+ CompUnitSP CU;
+ for (size_t Ind = 0; !CU && Ind < Module.GetNumCompileUnits(); ++Ind) {
+ CompUnitSP Candidate = Module.GetCompileUnitAtIndex(Ind);
+ if (!Candidate ||
+ Candidate->GetPrimaryFile().GetFilename().GetStringRef() != File)
+ continue;
+ if (CU)
+ return make_string_error("Multiple compile units for file `{0}` found.",
+ File);
+ CU = std::move(Candidate);
+ }
+
+ if (!CU)
+ return make_string_error("Compile unit `{0}` not found.", File);
+
+ List.AddVariables(CU->GetVariableList(true).get());
+ } else {
+ Expected<CompilerDeclContext> ContextOr = getDeclContext(Symfile);
+ if (!ContextOr)
+ return ContextOr.takeError();
+ CompilerDeclContext *ContextPtr =
+ ContextOr->IsValid() ? &*ContextOr : nullptr;
+
+ Symfile.FindGlobalVariables(ConstString(Name), ContextPtr, UINT32_MAX, List);
+ }
+ outs() << formatv("Found {0} variables:\n", List.GetSize());
+ StreamString Stream;
+ List.Dump(&Stream, false);
+ outs() << Stream.GetData() << "\n";
+ return Error::success();
+}
+
+Error opts::symbols::dumpModule(lldb_private::Module &Module) {
+ StreamString Stream;
+ Module.ParseAllDebugSymbols();
+ Module.Dump(&Stream);
+ outs() << Stream.GetData() << "\n";
+ return Error::success();
+}
+
+Error opts::symbols::dumpAST(lldb_private::Module &Module) {
+ Module.ParseAllDebugSymbols();
+
+ SymbolFile *symfile = Module.GetSymbolFile();
+ if (!symfile)
+ return make_string_error("Module has no symbol file.");
+
+ llvm::Expected<TypeSystem &> type_system_or_err =
+ symfile->GetTypeSystemForLanguage(eLanguageTypeC_plus_plus);
+ if (!type_system_or_err)
+ return make_string_error("Can't retrieve ClangASTContext");
+
+ auto *clang_ast_ctx =
+ llvm::dyn_cast_or_null<ClangASTContext>(&type_system_or_err.get());
+ if (!clang_ast_ctx)
+ return make_string_error("Retrieved TypeSystem was not a ClangASTContext");
+
+ clang::ASTContext &ast_ctx = clang_ast_ctx->getASTContext();
+
+ clang::TranslationUnitDecl *tu = ast_ctx.getTranslationUnitDecl();
+ if (!tu)
+ return make_string_error("Can't retrieve translation unit declaration.");
+
+ tu->print(outs());
+
+ return Error::success();
+}
+
+Error opts::symbols::dumpClangAST(lldb_private::Module &Module) {
+ Module.ParseAllDebugSymbols();
+
+ SymbolFile *symfile = Module.GetSymbolFile();
+ if (!symfile)
+ return make_string_error("Module has no symbol file.");
+
+ llvm::Expected<TypeSystem &> type_system_or_err =
+ symfile->GetTypeSystemForLanguage(eLanguageTypeObjC_plus_plus);
+ if (!type_system_or_err)
+ return make_string_error("Can't retrieve ClangASTContext");
+
+ auto *clang_ast_ctx =
+ llvm::dyn_cast_or_null<ClangASTContext>(&type_system_or_err.get());
+ if (!clang_ast_ctx)
+ return make_string_error("Retrieved TypeSystem was not a ClangASTContext");
+
+ StreamString Stream;
+ clang_ast_ctx->DumpFromSymbolFile(Stream, Name);
+ outs() << Stream.GetData() << "\n";
+
+ return Error::success();
+}
+
+Error opts::symbols::verify(lldb_private::Module &Module) {
+ SymbolFile *symfile = Module.GetSymbolFile();
+ if (!symfile)
+ return make_string_error("Module has no symbol file.");
+
+ uint32_t comp_units_count = symfile->GetNumCompileUnits();
+
+ outs() << "Found " << comp_units_count << " compile units.\n";
+
+ for (uint32_t i = 0; i < comp_units_count; i++) {
+ lldb::CompUnitSP comp_unit = symfile->GetCompileUnitAtIndex(i);
+ if (!comp_unit)
+ return make_string_error("Connot parse compile unit {0}.", i);
+
+ outs() << "Processing '"
+ << comp_unit->GetPrimaryFile().GetFilename().AsCString()
+ << "' compile unit.\n";
+
+ LineTable *lt = comp_unit->GetLineTable();
+ if (!lt)
+ return make_string_error("Can't get a line table of a compile unit.");
+
+ uint32_t count = lt->GetSize();
+
+ outs() << "The line table contains " << count << " entries.\n";
+
+ if (count == 0)
+ continue;
+
+ LineEntry le;
+ if (!lt->GetLineEntryAtIndex(0, le))
+ return make_string_error("Can't get a line entry of a compile unit.");
+
+ for (uint32_t i = 1; i < count; i++) {
+ lldb::addr_t curr_end =
+ le.range.GetBaseAddress().GetFileAddress() + le.range.GetByteSize();
+
+ if (!lt->GetLineEntryAtIndex(i, le))
+ return make_string_error("Can't get a line entry of a compile unit");
+
+ if (curr_end > le.range.GetBaseAddress().GetFileAddress())
+ return make_string_error(
+ "Line table of a compile unit is inconsistent.");
+ }
+ }
+
+ outs() << "The symbol information is verified.\n";
+
+ return Error::success();
+}
+
+Expected<Error (*)(lldb_private::Module &)> opts::symbols::getAction() {
+ if (Verify && DumpAST)
+ return make_string_error(
+ "Cannot both verify symbol information and dump AST.");
+
+ if (Verify) {
+ if (Find != FindType::None)
+ return make_string_error(
+ "Cannot both search and verify symbol information.");
+ if (Regex || !Context.empty() || !Name.empty() || !File.empty() ||
+ Line != 0)
+ return make_string_error(
+ "-regex, -context, -name, -file and -line options are not "
+ "applicable for symbol verification.");
+ return verify;
+ }
+
+ if (DumpAST) {
+ if (Find != FindType::None)
+ return make_string_error("Cannot both search and dump AST.");
+ if (Regex || !Context.empty() || !Name.empty() || !File.empty() ||
+ Line != 0)
+ return make_string_error(
+ "-regex, -context, -name, -file and -line options are not "
+ "applicable for dumping AST.");
+ return dumpAST;
+ }
+
+ if (DumpClangAST) {
+ if (Find != FindType::None)
+ return make_string_error("Cannot both search and dump clang AST.");
+ if (Regex || !Context.empty() || !File.empty() || Line != 0)
+ return make_string_error(
+ "-regex, -context, -name, -file and -line options are not "
+ "applicable for dumping clang AST.");
+ return dumpClangAST;
+ }
+
+ if (Regex && !Context.empty())
+ return make_string_error(
+ "Cannot search using both regular expressions and context.");
+
+ if (Regex && !RegularExpression(Name).IsValid())
+ return make_string_error("`{0}` is not a valid regular expression.", Name);
+
+ if (Regex + !Context.empty() + !File.empty() >= 2)
+ return make_string_error(
+ "Only one of -regex, -context and -file may be used simultaneously.");
+ if (Regex && Name.empty())
+ return make_string_error("-regex used without a -name");
+
+ switch (Find) {
+ case FindType::None:
+ if (!Context.empty() || !Name.empty() || !File.empty() || Line != 0)
+ return make_string_error(
+ "Specify search type (-find) to use search options.");
+ return dumpModule;
+
+ case FindType::Function:
+ if (!File.empty() + (Line != 0) == 1)
+ return make_string_error("Both file name and line number must be "
+ "specified when searching a function "
+ "by file position.");
+ if (Regex + (getFunctionNameFlags() != 0) + !File.empty() >= 2)
+ return make_string_error("Only one of regular expression, function-flags "
+ "and file position may be used simultaneously "
+ "when searching a function.");
+ return findFunctions;
+
+ case FindType::Block:
+ if (File.empty() || Line == 0)
+ return make_string_error("Both file name and line number must be "
+ "specified when searching a block.");
+ if (Regex || getFunctionNameFlags() != 0)
+ return make_string_error("Cannot use regular expression or "
+ "function-flags for searching a block.");
+ return findBlocks;
+
+ case FindType::Namespace:
+ if (Regex || !File.empty() || Line != 0)
+ return make_string_error("Cannot search for namespaces using regular "
+ "expressions, file names or line numbers.");
+ return findNamespaces;
+
+ case FindType::Type:
+ if (Regex || !File.empty() || Line != 0)
+ return make_string_error("Cannot search for types using regular "
+ "expressions, file names or line numbers.");
+ if (!Name.empty() && !CompilerContext.empty())
+ return make_string_error("Name is ignored if compiler context present.");
+
+ return findTypes;
+
+ case FindType::Variable:
+ if (Line != 0)
+ return make_string_error("Cannot search for variables "
+ "using line numbers.");
+ return findVariables;
+ }
+
+ llvm_unreachable("Unsupported symbol action.");
+}
+
+int opts::symbols::dumpSymbols(Debugger &Dbg) {
+ auto ActionOr = getAction();
+ if (!ActionOr) {
+ logAllUnhandledErrors(ActionOr.takeError(), WithColor::error(), "");
+ return 1;
+ }
+ auto Action = *ActionOr;
+
+ outs() << "Module: " << InputFile << "\n";
+ ModuleSpec Spec{FileSpec(InputFile)};
+ StringRef Symbols = SymbolPath.empty() ? InputFile : SymbolPath;
+ Spec.GetSymbolFileSpec().SetFile(Symbols, FileSpec::Style::native);
+
+ auto ModulePtr = std::make_shared<lldb_private::Module>(Spec);
+ SymbolFile *Symfile = ModulePtr->GetSymbolFile();
+ if (!Symfile) {
+ WithColor::error() << "Module has no symbol vendor.\n";
+ return 1;
+ }
+
+ if (Error E = Action(*ModulePtr)) {
+ WithColor::error() << toString(std::move(E)) << "\n";
+ return 1;
+ }
+
+ return 0;
+}
+
+static void dumpSectionList(LinePrinter &Printer, const SectionList &List, bool is_subsection) {
+ size_t Count = List.GetNumSections(0);
+ if (Count == 0) {
+ Printer.formatLine("There are no {0}sections", is_subsection ? "sub" : "");
+ return;
+ }
+ Printer.formatLine("Showing {0} {1}sections", Count,
+ is_subsection ? "sub" : "");
+ for (size_t I = 0; I < Count; ++I) {
+ auto S = List.GetSectionAtIndex(I);
+ assert(S);
+ AutoIndent Indent(Printer, 2);
+ Printer.formatLine("Index: {0}", I);
+ Printer.formatLine("ID: {0:x}", S->GetID());
+ Printer.formatLine("Name: {0}", S->GetName().GetStringRef());
+ Printer.formatLine("Type: {0}", S->GetTypeAsCString());
+ Printer.formatLine("Permissions: {0}", GetPermissionsAsCString(S->GetPermissions()));
+ Printer.formatLine("Thread specific: {0:y}", S->IsThreadSpecific());
+ Printer.formatLine("VM address: {0:x}", S->GetFileAddress());
+ Printer.formatLine("VM size: {0}", S->GetByteSize());
+ Printer.formatLine("File size: {0}", S->GetFileSize());
+
+ if (opts::object::SectionContents) {
+ lldb_private::DataExtractor Data;
+ S->GetSectionData(Data);
+ ArrayRef<uint8_t> Bytes = {Data.GetDataStart(), Data.GetDataEnd()};
+ Printer.formatBinary("Data: ", Bytes, 0);
+ }
+
+ if (S->GetType() == eSectionTypeContainer)
+ dumpSectionList(Printer, S->GetChildren(), true);
+ Printer.NewLine();
+ }
+}
+
+static int dumpObjectFiles(Debugger &Dbg) {
+ LinePrinter Printer(4, llvm::outs());
+
+ int HadErrors = 0;
+ for (const auto &File : opts::object::InputFilenames) {
+ ModuleSpec Spec{FileSpec(File)};
+
+ auto ModulePtr = std::make_shared<lldb_private::Module>(Spec);
+
+ ObjectFile *ObjectPtr = ModulePtr->GetObjectFile();
+ if (!ObjectPtr) {
+ WithColor::error() << File << " not recognised as an object file\n";
+ HadErrors = 1;
+ continue;
+ }
+
+ // Fetch symbol vendor before we get the section list to give the symbol
+ // vendor a chance to populate it.
+ ModulePtr->GetSymbolFile();
+ SectionList *Sections = ModulePtr->GetSectionList();
+ if (!Sections) {
+ llvm::errs() << "Could not load sections for module " << File << "\n";
+ HadErrors = 1;
+ continue;
+ }
+
+ Printer.formatLine("Plugin name: {0}", ObjectPtr->GetPluginName());
+ Printer.formatLine("Architecture: {0}",
+ ModulePtr->GetArchitecture().GetTriple().getTriple());
+ Printer.formatLine("UUID: {0}", ModulePtr->GetUUID().GetAsString());
+ Printer.formatLine("Executable: {0}", ObjectPtr->IsExecutable());
+ Printer.formatLine("Stripped: {0}", ObjectPtr->IsStripped());
+ Printer.formatLine("Type: {0}", ObjectPtr->GetType());
+ Printer.formatLine("Strata: {0}", ObjectPtr->GetStrata());
+ Printer.formatLine("Base VM address: {0:x}",
+ ObjectPtr->GetBaseAddress().GetFileAddress());
+
+ dumpSectionList(Printer, *Sections, /*is_subsection*/ false);
+
+ if (opts::object::SectionDependentModules) {
+ // A non-empty section list ensures a valid object file.
+ auto Obj = ModulePtr->GetObjectFile();
+ FileSpecList Files;
+ auto Count = Obj->GetDependentModules(Files);
+ Printer.formatLine("Showing {0} dependent module(s)", Count);
+ for (size_t I = 0; I < Files.GetSize(); ++I) {
+ AutoIndent Indent(Printer, 2);
+ Printer.formatLine("Name: {0}",
+ Files.GetFileSpecAtIndex(I).GetCString());
+ }
+ Printer.NewLine();
+ }
+ }
+ return HadErrors;
+}
+
+bool opts::irmemorymap::evalMalloc(StringRef Line,
+ IRMemoryMapTestState &State) {
+ // ::= <label> = malloc <size> <alignment>
+ StringRef Label;
+ std::tie(Label, Line) = Line.split('=');
+ if (Line.empty())
+ return false;
+ Label = Label.trim();
+ Line = Line.trim();
+ size_t Size;
+ uint8_t Alignment;
+ int Matches = sscanf(Line.data(), "malloc %zu %hhu", &Size, &Alignment);
+ if (Matches != 2)
+ return false;
+
+ outs() << formatv("Command: {0} = malloc(size={1}, alignment={2})\n", Label,
+ Size, Alignment);
+ if (!isPowerOf2_32(Alignment)) {
+ outs() << "Malloc error: alignment is not a power of 2\n";
+ exit(1);
+ }
+
+ IRMemoryMap::AllocationPolicy AP =
+ UseHostOnlyAllocationPolicy ? IRMemoryMap::eAllocationPolicyHostOnly
+ : IRMemoryMap::eAllocationPolicyProcessOnly;
+
+ // Issue the malloc in the target process with "-rw" permissions.
+ const uint32_t Permissions = 0x3;
+ const bool ZeroMemory = false;
+ Status ST;
+ addr_t Addr =
+ State.Map.Malloc(Size, Alignment, Permissions, AP, ZeroMemory, ST);
+ if (ST.Fail()) {
+ outs() << formatv("Malloc error: {0}\n", ST);
+ return true;
+ }
+
+ // Print the result of the allocation before checking its validity.
+ outs() << formatv("Malloc: address = {0:x}\n", Addr);
+
+ // Check that the allocation is aligned.
+ if (!Addr || Addr % Alignment != 0) {
+ outs() << "Malloc error: zero or unaligned allocation detected\n";
+ exit(1);
+ }
+
+ // In case of Size == 0, we still expect the returned address to be unique and
+ // non-overlapping.
+ addr_t EndOfRegion = Addr + std::max<size_t>(Size, 1);
+ if (State.Allocations.overlaps(Addr, EndOfRegion)) {
+ auto I = State.Allocations.find(Addr);
+ outs() << "Malloc error: overlapping allocation detected"
+ << formatv(", previous allocation at [{0:x}, {1:x})\n", I.start(),
+ I.stop());
+ exit(1);
+ }
+
+ // Insert the new allocation into the interval map. Use unique allocation
+ // IDs to inhibit interval coalescing.
+ static unsigned AllocationID = 0;
+ State.Allocations.insert(Addr, EndOfRegion, AllocationID++);
+
+ // Store the label -> address mapping.
+ State.Label2AddrMap[Label] = Addr;
+
+ return true;
+}
+
+bool opts::irmemorymap::evalFree(StringRef Line, IRMemoryMapTestState &State) {
+ // ::= free <label>
+ if (!Line.consume_front("free"))
+ return false;
+ StringRef Label = Line.trim();
+
+ outs() << formatv("Command: free({0})\n", Label);
+ auto LabelIt = State.Label2AddrMap.find(Label);
+ if (LabelIt == State.Label2AddrMap.end()) {
+ outs() << "Free error: Invalid allocation label\n";
+ exit(1);
+ }
+
+ Status ST;
+ addr_t Addr = LabelIt->getValue();
+ State.Map.Free(Addr, ST);
+ if (ST.Fail()) {
+ outs() << formatv("Free error: {0}\n", ST);
+ exit(1);
+ }
+
+ // Erase the allocation from the live interval map.
+ auto Interval = State.Allocations.find(Addr);
+ if (Interval != State.Allocations.end()) {
+ outs() << formatv("Free: [{0:x}, {1:x})\n", Interval.start(),
+ Interval.stop());
+ Interval.erase();
+ }
+
+ return true;
+}
+
+int opts::irmemorymap::evaluateMemoryMapCommands(Debugger &Dbg) {
+ // Set up a Target.
+ TargetSP Target = opts::createTarget(Dbg, irmemorymap::Target);
+
+ // Set up a Process. In order to allocate memory within a target, this
+ // process must be alive and must support JIT'ing.
+ CommandReturnObject Result;
+ Dbg.SetAsyncExecution(false);
+ CommandInterpreter &CI = Dbg.GetCommandInterpreter();
+ auto IssueCmd = [&](const char *Cmd) -> bool {
+ return CI.HandleCommand(Cmd, eLazyBoolNo, Result);
+ };
+ if (!IssueCmd("b main") || !IssueCmd("run")) {
+ outs() << formatv("Failed: {0}\n", Result.GetErrorData());
+ exit(1);
+ }
+
+ ProcessSP Process = Target->GetProcessSP();
+ if (!Process || !Process->IsAlive() || !Process->CanJIT()) {
+ outs() << "Cannot use process to test IRMemoryMap\n";
+ exit(1);
+ }
+
+ // Set up an IRMemoryMap and associated testing state.
+ IRMemoryMapTestState State(Target);
+
+ // Parse and apply commands from the command file.
+ std::unique_ptr<MemoryBuffer> MB = opts::openFile(irmemorymap::CommandFile);
+ StringRef Rest = MB->getBuffer();
+ while (!Rest.empty()) {
+ StringRef Line;
+ std::tie(Line, Rest) = Rest.split('\n');
+ Line = Line.ltrim().rtrim();
+
+ if (Line.empty() || Line[0] == '#')
+ continue;
+
+ if (evalMalloc(Line, State))
+ continue;
+
+ if (evalFree(Line, State))
+ continue;
+
+ errs() << "Could not parse line: " << Line << "\n";
+ exit(1);
+ }
+ return 0;
+}
+
+int main(int argc, const char *argv[]) {
+ StringRef ToolName = argv[0];
+ sys::PrintStackTraceOnErrorSignal(ToolName);
+ PrettyStackTraceProgram X(argc, argv);
+ llvm_shutdown_obj Y;
+
+ cl::ParseCommandLineOptions(argc, argv, "LLDB Testing Utility\n");
+
+ SystemLifetimeManager DebuggerLifetime;
+ if (auto e = DebuggerLifetime.Initialize(
+ std::make_unique<SystemInitializerTest>(), nullptr)) {
+ WithColor::error() << "initialization failed: " << toString(std::move(e))
+ << '\n';
+ return 1;
+ }
+
+ auto TerminateDebugger =
+ llvm::make_scope_exit([&] { DebuggerLifetime.Terminate(); });
+
+ auto Dbg = lldb_private::Debugger::CreateInstance();
+ ModuleList::GetGlobalModuleListProperties().SetEnableExternalLookup(false);
+ CommandReturnObject Result;
+ Dbg->GetCommandInterpreter().HandleCommand(
+ "settings set plugin.process.gdb-remote.packet-timeout 60",
+ /*add_to_history*/ eLazyBoolNo, Result);
+
+ if (!opts::Log.empty())
+ Dbg->EnableLog("lldb", {"all"}, opts::Log, 0, errs());
+
+ if (opts::BreakpointSubcommand)
+ return opts::breakpoint::evaluateBreakpoints(*Dbg);
+ if (opts::ObjectFileSubcommand)
+ return dumpObjectFiles(*Dbg);
+ if (opts::SymbolsSubcommand)
+ return opts::symbols::dumpSymbols(*Dbg);
+ if (opts::IRMemoryMapSubcommand)
+ return opts::irmemorymap::evaluateMemoryMapCommands(*Dbg);
+
+ WithColor::error() << "No command specified.\n";
+ return 1;
+}
diff --git a/gnu/llvm/lldb/tools/lldb-vscode/BreakpointBase.cpp b/gnu/llvm/lldb/tools/lldb-vscode/BreakpointBase.cpp
new file mode 100644
index 00000000000..af775250cb9
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-vscode/BreakpointBase.cpp
@@ -0,0 +1,36 @@
+//===-- BreakpointBase.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "BreakpointBase.h"
+#include "llvm/ADT/StringExtras.h"
+
+using namespace lldb_vscode;
+
+BreakpointBase::BreakpointBase(const llvm::json::Object &obj)
+ : condition(GetString(obj, "condition")),
+ hitCondition(GetString(obj, "hitCondition")),
+ logMessage(GetString(obj, "logMessage")) {}
+
+void BreakpointBase::SetCondition() { bp.SetCondition(condition.c_str()); }
+
+void BreakpointBase::SetHitCondition() {
+ uint64_t hitCount = 0;
+ if (llvm::to_integer(hitCondition, hitCount))
+ bp.SetIgnoreCount(hitCount - 1);
+}
+
+void BreakpointBase::UpdateBreakpoint(const BreakpointBase &request_bp) {
+ if (condition != request_bp.condition) {
+ condition = request_bp.condition;
+ SetCondition();
+ }
+ if (hitCondition != request_bp.hitCondition) {
+ hitCondition = request_bp.hitCondition;
+ SetHitCondition();
+ }
+}
diff --git a/gnu/llvm/lldb/tools/lldb-vscode/BreakpointBase.h b/gnu/llvm/lldb/tools/lldb-vscode/BreakpointBase.h
new file mode 100644
index 00000000000..ee241a9713a
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-vscode/BreakpointBase.h
@@ -0,0 +1,43 @@
+//===-- BreakpointBase.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDBVSCODE_BREAKPOINTBASE_H_
+#define LLDBVSCODE_BREAKPOINTBASE_H_
+
+#include "JSONUtils.h"
+#include "lldb/API/SBBreakpoint.h"
+#include "llvm/Support/JSON.h"
+#include <string>
+
+namespace lldb_vscode {
+
+struct BreakpointBase {
+
+ // An optional expression for conditional breakpoints.
+ std::string condition;
+ // An optional expression that controls how many hits of the breakpoint are
+ // ignored. The backend is expected to interpret the expression as needed
+ std::string hitCondition;
+ // If this attribute exists and is non-empty, the backend must not 'break'
+ // (stop) but log the message instead. Expressions within {} are
+ // interpolated.
+ std::string logMessage;
+ // The LLDB breakpoint associated wit this source breakpoint
+ lldb::SBBreakpoint bp;
+
+ BreakpointBase() = default;
+ BreakpointBase(const llvm::json::Object &obj);
+
+ void SetCondition();
+ void SetHitCondition();
+ void UpdateBreakpoint(const BreakpointBase &request_bp);
+};
+
+} // namespace lldb_vscode
+
+#endif
diff --git a/gnu/llvm/lldb/tools/lldb-vscode/CMakeLists.txt b/gnu/llvm/lldb/tools/lldb-vscode/CMakeLists.txt
new file mode 100644
index 00000000000..b527addb6ba
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-vscode/CMakeLists.txt
@@ -0,0 +1,54 @@
+if ( CMAKE_SYSTEM_NAME MATCHES "Windows" OR CMAKE_SYSTEM_NAME MATCHES "NetBSD" )
+ add_definitions( -DIMPORT_LIBLLDB )
+ list(APPEND extra_libs lldbHost)
+endif ()
+
+if (HAVE_LIBPTHREAD)
+ list(APPEND extra_libs pthread)
+endif ()
+
+
+if(APPLE)
+ configure_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lldb-vscode-Info.plist.in
+ ${CMAKE_CURRENT_BINARY_DIR}/lldb-vscode-Info.plist
+ )
+ # Inline info plist in binary (use target_link_options for this as soon as CMake 3.13 is available)
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-sectcreate,__TEXT,__info_plist,${CMAKE_CURRENT_BINARY_DIR}/lldb-vscode-Info.plist")
+endif()
+
+# We need to include the llvm components we depend on manually, as liblldb does
+# not re-export those.
+set(LLVM_LINK_COMPONENTS Support)
+add_lldb_tool(lldb-vscode
+ lldb-vscode.cpp
+ BreakpointBase.cpp
+ ExceptionBreakpoint.cpp
+ FunctionBreakpoint.cpp
+ IOStream.cpp
+ JSONUtils.cpp
+ LLDBUtils.cpp
+ SourceBreakpoint.cpp
+ VSCode.cpp
+
+ LINK_LIBS
+ liblldb
+ ${host_lib}
+ ${extra_libs}
+
+ LINK_COMPONENTS
+ Support
+ )
+
+if(LLDB_BUILD_FRAMEWORK)
+ # In the build-tree, we know the exact path to the framework directory.
+ # The installed framework can be in different locations.
+ lldb_setup_rpaths(lldb-vscode
+ BUILD_RPATH
+ "${LLDB_FRAMEWORK_ABSOLUTE_BUILD_DIR}"
+ INSTALL_RPATH
+ "@loader_path/../../../SharedFrameworks"
+ "@loader_path/../../System/Library/PrivateFrameworks"
+ "@loader_path/../../Library/PrivateFrameworks"
+ )
+endif()
diff --git a/gnu/llvm/lldb/tools/lldb-vscode/ExceptionBreakpoint.cpp b/gnu/llvm/lldb/tools/lldb-vscode/ExceptionBreakpoint.cpp
new file mode 100644
index 00000000000..090463ff60c
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-vscode/ExceptionBreakpoint.cpp
@@ -0,0 +1,31 @@
+//===-- ExceptionBreakpoint.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ExceptionBreakpoint.h"
+#include "VSCode.h"
+
+namespace lldb_vscode {
+
+void ExceptionBreakpoint::SetBreakpoint() {
+ if (bp.IsValid())
+ return;
+ bool catch_value = filter.find("_catch") != std::string::npos;
+ bool throw_value = filter.find("_throw") != std::string::npos;
+ bp = g_vsc.target.BreakpointCreateForException(language, catch_value,
+ throw_value);
+}
+
+void ExceptionBreakpoint::ClearBreakpoint() {
+ if (!bp.IsValid())
+ return;
+ g_vsc.target.BreakpointDelete(bp.GetID());
+ bp = lldb::SBBreakpoint();
+}
+
+} // namespace lldb_vscode
+
diff --git a/gnu/llvm/lldb/tools/lldb-vscode/ExceptionBreakpoint.h b/gnu/llvm/lldb/tools/lldb-vscode/ExceptionBreakpoint.h
new file mode 100644
index 00000000000..fd9f8efe237
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-vscode/ExceptionBreakpoint.h
@@ -0,0 +1,37 @@
+//===-- ExceptionBreakpoint.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDBVSCODE_EXCEPTIONBREAKPOINT_H_
+#define LLDBVSCODE_EXCEPTIONBREAKPOINT_H_
+
+#include <string>
+
+#include "lldb/API/SBBreakpoint.h"
+
+namespace lldb_vscode {
+
+struct ExceptionBreakpoint {
+ std::string filter;
+ std::string label;
+ lldb::LanguageType language;
+ bool default_value;
+ lldb::SBBreakpoint bp;
+ ExceptionBreakpoint(std::string f, std::string l, lldb::LanguageType lang) :
+ filter(std::move(f)),
+ label(std::move(l)),
+ language(lang),
+ default_value(false),
+ bp() {}
+
+ void SetBreakpoint();
+ void ClearBreakpoint();
+};
+
+} // namespace lldb_vscode
+
+#endif
diff --git a/gnu/llvm/lldb/tools/lldb-vscode/FunctionBreakpoint.cpp b/gnu/llvm/lldb/tools/lldb-vscode/FunctionBreakpoint.cpp
new file mode 100644
index 00000000000..1ac2e856f16
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-vscode/FunctionBreakpoint.cpp
@@ -0,0 +1,27 @@
+//===-- FunctionBreakpoint.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "FunctionBreakpoint.h"
+#include "VSCode.h"
+
+namespace lldb_vscode {
+
+FunctionBreakpoint::FunctionBreakpoint(const llvm::json::Object &obj)
+ : BreakpointBase(obj), functionName(GetString(obj, "name")) {}
+
+void FunctionBreakpoint::SetBreakpoint() {
+ if (functionName.empty())
+ return;
+ bp = g_vsc.target.BreakpointCreateByName(functionName.c_str());
+ if (!condition.empty())
+ SetCondition();
+ if (!hitCondition.empty())
+ SetHitCondition();
+}
+
+}
diff --git a/gnu/llvm/lldb/tools/lldb-vscode/FunctionBreakpoint.h b/gnu/llvm/lldb/tools/lldb-vscode/FunctionBreakpoint.h
new file mode 100644
index 00000000000..dbc184914f0
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-vscode/FunctionBreakpoint.h
@@ -0,0 +1,28 @@
+//===-- FunctionBreakpoint.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDBVSCODE_FUNCTIONBREAKPOINT_H_
+#define LLDBVSCODE_FUNCTIONBREAKPOINT_H_
+
+#include "BreakpointBase.h"
+
+namespace lldb_vscode {
+
+struct FunctionBreakpoint : public BreakpointBase {
+ std::string functionName;
+
+ FunctionBreakpoint() = default;
+ FunctionBreakpoint(const llvm::json::Object &obj);
+
+ // Set this breakpoint in LLDB as a new breakpoint
+ void SetBreakpoint();
+};
+
+} // namespace lldb_vscode
+
+#endif
diff --git a/gnu/llvm/lldb/tools/lldb-vscode/IOStream.cpp b/gnu/llvm/lldb/tools/lldb-vscode/IOStream.cpp
new file mode 100644
index 00000000000..4b11b90b4c2
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-vscode/IOStream.cpp
@@ -0,0 +1,158 @@
+//===-- IOStream.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "IOStream.h"
+
+#if defined(_WIN32)
+#include <io.h>
+#else
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#endif
+
+#include <fstream>
+#include <string>
+#include <vector>
+
+using namespace lldb_vscode;
+
+StreamDescriptor::StreamDescriptor() {}
+
+StreamDescriptor::StreamDescriptor(StreamDescriptor &&other) {
+ *this = std::move(other);
+}
+
+StreamDescriptor::~StreamDescriptor() {
+ if (!m_close)
+ return;
+
+ if (m_is_socket)
+#if defined(_WIN32)
+ ::closesocket(m_socket);
+#else
+ ::close(m_socket);
+#endif
+ else
+ ::close(m_fd);
+}
+
+StreamDescriptor &StreamDescriptor::operator=(StreamDescriptor &&other) {
+ m_close = other.m_close;
+ other.m_close = false;
+ m_is_socket = other.m_is_socket;
+ if (m_is_socket)
+ m_socket = other.m_socket;
+ else
+ m_fd = other.m_fd;
+ return *this;
+}
+
+StreamDescriptor StreamDescriptor::from_socket(SOCKET s, bool close) {
+ StreamDescriptor sd;
+ sd.m_is_socket = true;
+ sd.m_socket = s;
+ sd.m_close = close;
+ return sd;
+}
+
+StreamDescriptor StreamDescriptor::from_file(int fd, bool close) {
+ StreamDescriptor sd;
+ sd.m_is_socket = false;
+ sd.m_fd = fd;
+ sd.m_close = close;
+ return sd;
+}
+
+bool OutputStream::write_full(llvm::StringRef str) {
+ while (!str.empty()) {
+ int bytes_written = 0;
+ if (descriptor.m_is_socket)
+ bytes_written = ::send(descriptor.m_socket, str.data(), str.size(), 0);
+ else
+ bytes_written = ::write(descriptor.m_fd, str.data(), str.size());
+
+ if (bytes_written < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return false;
+ }
+ str = str.drop_front(bytes_written);
+ }
+
+ return true;
+}
+
+bool InputStream::read_full(std::ofstream *log, size_t length,
+ std::string &text) {
+ std::string data;
+ data.resize(length);
+
+ char *ptr = &data[0];
+ while (length != 0) {
+ int bytes_read = 0;
+ if (descriptor.m_is_socket)
+ bytes_read = ::recv(descriptor.m_socket, ptr, length, 0);
+ else
+ bytes_read = ::read(descriptor.m_fd, ptr, length);
+
+ if (bytes_read == 0) {
+ if (log)
+ *log << "End of file (EOF) reading from input file.\n";
+ return false;
+ }
+ if (bytes_read < 0) {
+ int reason = 0;
+#if defined(_WIN32)
+ if (descriptor.m_is_socket)
+ reason = WSAGetLastError();
+ else
+ reason = errno;
+#else
+ reason = errno;
+ if (reason == EINTR || reason == EAGAIN)
+ continue;
+#endif
+
+ if (log)
+ *log << "Error " << reason << " reading from input file.\n";
+ return false;
+ }
+
+ assert(bytes_read >= 0 && (size_t)bytes_read <= length);
+ ptr += bytes_read;
+ length -= bytes_read;
+ }
+ text += data;
+ return true;
+}
+
+bool InputStream::read_line(std::ofstream *log, std::string &line) {
+ line.clear();
+ while (true) {
+ if (!read_full(log, 1, line))
+ return false;
+
+ if (llvm::StringRef(line).endswith("\r\n"))
+ break;
+ }
+ line.erase(line.size() - 2);
+ return true;
+}
+
+bool InputStream::read_expected(std::ofstream *log, llvm::StringRef expected) {
+ std::string result;
+ if (!read_full(log, expected.size(), result))
+ return false;
+ if (expected != result) {
+ if (log)
+ *log << "Warning: Expected '" << expected.str() << "', got '" << result
+ << "\n";
+ }
+ return true;
+}
diff --git a/gnu/llvm/lldb/tools/lldb-vscode/IOStream.h b/gnu/llvm/lldb/tools/lldb-vscode/IOStream.h
new file mode 100644
index 00000000000..8414c09e9fe
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-vscode/IOStream.h
@@ -0,0 +1,69 @@
+//===-- IOStream.h ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDBVSCODE_IOSTREAM_H_
+#define LLDBVSCODE_IOSTREAM_H_
+
+#if defined(_WIN32)
+// We need to #define NOMINMAX in order to skip `min()` and `max()` macro
+// definitions that conflict with other system headers.
+// We also need to #undef GetObject (which is defined to GetObjectW) because
+// the JSON code we use also has methods named `GetObject()` and we conflict
+// against these.
+#define NOMINMAX
+#include <windows.h>
+#else
+typedef int SOCKET;
+#endif
+
+#include "llvm/ADT/StringRef.h"
+
+#include <fstream>
+#include <string>
+
+// Windows requires different system calls for dealing with sockets and other
+// types of files, so we can't simply have one code path that just uses read
+// and write everywhere. So we need an abstraction in order to allow us to
+// treat them identically.
+namespace lldb_vscode {
+struct StreamDescriptor {
+ StreamDescriptor();
+ ~StreamDescriptor();
+ StreamDescriptor(StreamDescriptor &&other);
+
+ StreamDescriptor &operator=(StreamDescriptor &&other);
+
+ static StreamDescriptor from_socket(SOCKET s, bool close);
+ static StreamDescriptor from_file(int fd, bool close);
+
+ bool m_is_socket = false;
+ bool m_close = false;
+ union {
+ int m_fd;
+ SOCKET m_socket;
+ };
+};
+
+struct InputStream {
+ StreamDescriptor descriptor;
+
+ bool read_full(std::ofstream *log, size_t length, std::string &text);
+
+ bool read_line(std::ofstream *log, std::string &line);
+
+ bool read_expected(std::ofstream *log, llvm::StringRef expected);
+};
+
+struct OutputStream {
+ StreamDescriptor descriptor;
+
+ bool write_full(llvm::StringRef str);
+};
+} // namespace lldb_vscode
+
+#endif
diff --git a/gnu/llvm/lldb/tools/lldb-vscode/JSONUtils.cpp b/gnu/llvm/lldb/tools/lldb-vscode/JSONUtils.cpp
new file mode 100644
index 00000000000..b83f56445e2
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-vscode/JSONUtils.cpp
@@ -0,0 +1,873 @@
+//===-- JSONUtils.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <algorithm>
+
+#include "llvm/Support/FormatAdapters.h"
+
+#include "lldb/API/SBBreakpoint.h"
+#include "lldb/API/SBBreakpointLocation.h"
+#include "lldb/API/SBValue.h"
+#include "lldb/Host/PosixApi.h"
+
+#include "ExceptionBreakpoint.h"
+#include "JSONUtils.h"
+#include "LLDBUtils.h"
+#include "VSCode.h"
+
+namespace lldb_vscode {
+
+void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key,
+ llvm::StringRef str) {
+ if (LLVM_LIKELY(llvm::json::isUTF8(str)))
+ obj.try_emplace(key, str.str());
+ else
+ obj.try_emplace(key, llvm::json::fixUTF8(str));
+}
+
+llvm::StringRef GetAsString(const llvm::json::Value &value) {
+ if (auto s = value.getAsString())
+ return *s;
+ return llvm::StringRef();
+}
+
+// Gets a string from a JSON object using the key, or returns an empty string.
+llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key) {
+ if (auto value = obj.getString(key))
+ return GetAsString(*value);
+ return llvm::StringRef();
+}
+
+llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key) {
+ if (obj == nullptr)
+ return llvm::StringRef();
+ return GetString(*obj, key);
+}
+
+// Gets an unsigned integer from a JSON object using the key, or returns the
+// specified fail value.
+uint64_t GetUnsigned(const llvm::json::Object &obj, llvm::StringRef key,
+ uint64_t fail_value) {
+ if (auto value = obj.getInteger(key))
+ return (uint64_t)*value;
+ return fail_value;
+}
+
+uint64_t GetUnsigned(const llvm::json::Object *obj, llvm::StringRef key,
+ uint64_t fail_value) {
+ if (obj == nullptr)
+ return fail_value;
+ return GetUnsigned(*obj, key, fail_value);
+}
+
+bool GetBoolean(const llvm::json::Object &obj, llvm::StringRef key,
+ bool fail_value) {
+ if (auto value = obj.getBoolean(key))
+ return *value;
+ if (auto value = obj.getInteger(key))
+ return *value != 0;
+ return fail_value;
+}
+
+bool GetBoolean(const llvm::json::Object *obj, llvm::StringRef key,
+ bool fail_value) {
+ if (obj == nullptr)
+ return fail_value;
+ return GetBoolean(*obj, key, fail_value);
+}
+
+int64_t GetSigned(const llvm::json::Object &obj, llvm::StringRef key,
+ int64_t fail_value) {
+ if (auto value = obj.getInteger(key))
+ return *value;
+ return fail_value;
+}
+
+int64_t GetSigned(const llvm::json::Object *obj, llvm::StringRef key,
+ int64_t fail_value) {
+ if (obj == nullptr)
+ return fail_value;
+ return GetSigned(*obj, key, fail_value);
+}
+
+bool ObjectContainsKey(const llvm::json::Object &obj, llvm::StringRef key) {
+ return obj.find(key) != obj.end();
+}
+
+std::vector<std::string> GetStrings(const llvm::json::Object *obj,
+ llvm::StringRef key) {
+ std::vector<std::string> strs;
+ auto json_array = obj->getArray(key);
+ if (!json_array)
+ return strs;
+ for (const auto &value : *json_array) {
+ switch (value.kind()) {
+ case llvm::json::Value::String:
+ strs.push_back(value.getAsString()->str());
+ break;
+ case llvm::json::Value::Number:
+ case llvm::json::Value::Boolean: {
+ std::string s;
+ llvm::raw_string_ostream strm(s);
+ strm << value;
+ strs.push_back(strm.str());
+ break;
+ }
+ case llvm::json::Value::Null:
+ case llvm::json::Value::Object:
+ case llvm::json::Value::Array:
+ break;
+ }
+ }
+ return strs;
+}
+
+void SetValueForKey(lldb::SBValue &v, llvm::json::Object &object,
+ llvm::StringRef key) {
+
+ llvm::StringRef value = v.GetValue();
+ llvm::StringRef summary = v.GetSummary();
+ llvm::StringRef type_name = v.GetType().GetDisplayTypeName();
+
+ std::string result;
+ llvm::raw_string_ostream strm(result);
+ if (!value.empty()) {
+ strm << value;
+ if (!summary.empty())
+ strm << ' ' << summary;
+ } else if (!summary.empty()) {
+ strm << ' ' << summary;
+ } else if (!type_name.empty()) {
+ strm << type_name;
+ lldb::addr_t address = v.GetLoadAddress();
+ if (address != LLDB_INVALID_ADDRESS)
+ strm << " @ " << llvm::format_hex(address, 0);
+ }
+ strm.flush();
+ EmplaceSafeString(object, key, result);
+}
+
+void FillResponse(const llvm::json::Object &request,
+ llvm::json::Object &response) {
+ // Fill in all of the needed response fields to a "request" and set "success"
+ // to true by default.
+ response.try_emplace("type", "response");
+ response.try_emplace("seq", (int64_t)0);
+ EmplaceSafeString(response, "command", GetString(request, "command"));
+ const int64_t seq = GetSigned(request, "seq", 0);
+ response.try_emplace("request_seq", seq);
+ response.try_emplace("success", true);
+}
+
+// "Scope": {
+// "type": "object",
+// "description": "A Scope is a named container for variables. Optionally
+// a scope can map to a source or a range within a source.",
+// "properties": {
+// "name": {
+// "type": "string",
+// "description": "Name of the scope such as 'Arguments', 'Locals'."
+// },
+// "variablesReference": {
+// "type": "integer",
+// "description": "The variables of this scope can be retrieved by
+// passing the value of variablesReference to the
+// VariablesRequest."
+// },
+// "namedVariables": {
+// "type": "integer",
+// "description": "The number of named variables in this scope. The
+// client can use this optional information to present
+// the variables in a paged UI and fetch them in chunks."
+// },
+// "indexedVariables": {
+// "type": "integer",
+// "description": "The number of indexed variables in this scope. The
+// client can use this optional information to present
+// the variables in a paged UI and fetch them in chunks."
+// },
+// "expensive": {
+// "type": "boolean",
+// "description": "If true, the number of variables in this scope is
+// large or expensive to retrieve."
+// },
+// "source": {
+// "$ref": "#/definitions/Source",
+// "description": "Optional source for this scope."
+// },
+// "line": {
+// "type": "integer",
+// "description": "Optional start line of the range covered by this
+// scope."
+// },
+// "column": {
+// "type": "integer",
+// "description": "Optional start column of the range covered by this
+// scope."
+// },
+// "endLine": {
+// "type": "integer",
+// "description": "Optional end line of the range covered by this scope."
+// },
+// "endColumn": {
+// "type": "integer",
+// "description": "Optional end column of the range covered by this
+// scope."
+// }
+// },
+// "required": [ "name", "variablesReference", "expensive" ]
+// }
+llvm::json::Value CreateScope(const llvm::StringRef name,
+ int64_t variablesReference,
+ int64_t namedVariables, bool expensive) {
+ llvm::json::Object object;
+ EmplaceSafeString(object, "name", name.str());
+ object.try_emplace("variablesReference", variablesReference);
+ object.try_emplace("expensive", expensive);
+ object.try_emplace("namedVariables", namedVariables);
+ return llvm::json::Value(std::move(object));
+}
+
+// "Breakpoint": {
+// "type": "object",
+// "description": "Information about a Breakpoint created in setBreakpoints
+// or setFunctionBreakpoints.",
+// "properties": {
+// "id": {
+// "type": "integer",
+// "description": "An optional unique identifier for the breakpoint."
+// },
+// "verified": {
+// "type": "boolean",
+// "description": "If true breakpoint could be set (but not necessarily
+// at the desired location)."
+// },
+// "message": {
+// "type": "string",
+// "description": "An optional message about the state of the breakpoint.
+// This is shown to the user and can be used to explain
+// why a breakpoint could not be verified."
+// },
+// "source": {
+// "$ref": "#/definitions/Source",
+// "description": "The source where the breakpoint is located."
+// },
+// "line": {
+// "type": "integer",
+// "description": "The start line of the actual range covered by the
+// breakpoint."
+// },
+// "column": {
+// "type": "integer",
+// "description": "An optional start column of the actual range covered
+// by the breakpoint."
+// },
+// "endLine": {
+// "type": "integer",
+// "description": "An optional end line of the actual range covered by
+// the breakpoint."
+// },
+// "endColumn": {
+// "type": "integer",
+// "description": "An optional end column of the actual range covered by
+// the breakpoint. If no end line is given, then the end
+// column is assumed to be in the start line."
+// }
+// },
+// "required": [ "verified" ]
+// }
+llvm::json::Value CreateBreakpoint(lldb::SBBreakpointLocation &bp_loc) {
+ // Each breakpoint location is treated as a separate breakpoint for VS code.
+ // They don't have the notion of a single breakpoint with multiple locations.
+ llvm::json::Object object;
+ if (!bp_loc.IsValid())
+ return llvm::json::Value(std::move(object));
+
+ object.try_emplace("verified", true);
+ const auto vs_id = MakeVSCodeBreakpointID(bp_loc);
+ object.try_emplace("id", vs_id);
+ auto bp_addr = bp_loc.GetAddress();
+ if (bp_addr.IsValid()) {
+ auto line_entry = bp_addr.GetLineEntry();
+ const auto line = line_entry.GetLine();
+ if (line != UINT32_MAX)
+ object.try_emplace("line", line);
+ object.try_emplace("source", CreateSource(line_entry));
+ }
+ return llvm::json::Value(std::move(object));
+}
+
+void AppendBreakpoint(lldb::SBBreakpoint &bp, llvm::json::Array &breakpoints) {
+ if (!bp.IsValid())
+ return;
+ const auto num_locations = bp.GetNumLocations();
+ if (num_locations == 0)
+ return;
+ for (size_t i = 0; i < num_locations; ++i) {
+ auto bp_loc = bp.GetLocationAtIndex(i);
+ breakpoints.emplace_back(CreateBreakpoint(bp_loc));
+ }
+}
+
+// "Event": {
+// "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
+// "type": "object",
+// "description": "Server-initiated event.",
+// "properties": {
+// "type": {
+// "type": "string",
+// "enum": [ "event" ]
+// },
+// "event": {
+// "type": "string",
+// "description": "Type of event."
+// },
+// "body": {
+// "type": [ "array", "boolean", "integer", "null", "number" ,
+// "object", "string" ],
+// "description": "Event-specific information."
+// }
+// },
+// "required": [ "type", "event" ]
+// }]
+// },
+// "ProtocolMessage": {
+// "type": "object",
+// "description": "Base class of requests, responses, and events.",
+// "properties": {
+// "seq": {
+// "type": "integer",
+// "description": "Sequence number."
+// },
+// "type": {
+// "type": "string",
+// "description": "Message type.",
+// "_enum": [ "request", "response", "event" ]
+// }
+// },
+// "required": [ "seq", "type" ]
+// }
+llvm::json::Object CreateEventObject(const llvm::StringRef event_name) {
+ llvm::json::Object event;
+ event.try_emplace("seq", 0);
+ event.try_emplace("type", "event");
+ EmplaceSafeString(event, "event", event_name);
+ return event;
+}
+
+// "ExceptionBreakpointsFilter": {
+// "type": "object",
+// "description": "An ExceptionBreakpointsFilter is shown in the UI as an
+// option for configuring how exceptions are dealt with.",
+// "properties": {
+// "filter": {
+// "type": "string",
+// "description": "The internal ID of the filter. This value is passed
+// to the setExceptionBreakpoints request."
+// },
+// "label": {
+// "type": "string",
+// "description": "The name of the filter. This will be shown in the UI."
+// },
+// "default": {
+// "type": "boolean",
+// "description": "Initial value of the filter. If not specified a value
+// 'false' is assumed."
+// }
+// },
+// "required": [ "filter", "label" ]
+// }
+llvm::json::Value
+CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) {
+ llvm::json::Object object;
+ EmplaceSafeString(object, "filter", bp.filter);
+ EmplaceSafeString(object, "label", bp.label);
+ object.try_emplace("default", bp.default_value);
+ return llvm::json::Value(std::move(object));
+}
+
+// "Source": {
+// "type": "object",
+// "description": "A Source is a descriptor for source code. It is returned
+// from the debug adapter as part of a StackFrame and it is
+// used by clients when specifying breakpoints.",
+// "properties": {
+// "name": {
+// "type": "string",
+// "description": "The short name of the source. Every source returned
+// from the debug adapter has a name. When sending a
+// source to the debug adapter this name is optional."
+// },
+// "path": {
+// "type": "string",
+// "description": "The path of the source to be shown in the UI. It is
+// only used to locate and load the content of the
+// source if no sourceReference is specified (or its
+// value is 0)."
+// },
+// "sourceReference": {
+// "type": "number",
+// "description": "If sourceReference > 0 the contents of the source must
+// be retrieved through the SourceRequest (even if a path
+// is specified). A sourceReference is only valid for a
+// session, so it must not be used to persist a source."
+// },
+// "presentationHint": {
+// "type": "string",
+// "description": "An optional hint for how to present the source in the
+// UI. A value of 'deemphasize' can be used to indicate
+// that the source is not available or that it is
+// skipped on stepping.",
+// "enum": [ "normal", "emphasize", "deemphasize" ]
+// },
+// "origin": {
+// "type": "string",
+// "description": "The (optional) origin of this source: possible values
+// 'internal module', 'inlined content from source map',
+// etc."
+// },
+// "sources": {
+// "type": "array",
+// "items": {
+// "$ref": "#/definitions/Source"
+// },
+// "description": "An optional list of sources that are related to this
+// source. These may be the source that generated this
+// source."
+// },
+// "adapterData": {
+// "type":["array","boolean","integer","null","number","object","string"],
+// "description": "Optional data that a debug adapter might want to loop
+// through the client. The client should leave the data
+// intact and persist it across sessions. The client
+// should not interpret the data."
+// },
+// "checksums": {
+// "type": "array",
+// "items": {
+// "$ref": "#/definitions/Checksum"
+// },
+// "description": "The checksums associated with this file."
+// }
+// }
+// }
+llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry) {
+ llvm::json::Object object;
+ lldb::SBFileSpec file = line_entry.GetFileSpec();
+ if (file.IsValid()) {
+ const char *name = file.GetFilename();
+ if (name)
+ EmplaceSafeString(object, "name", name);
+ char path[PATH_MAX] = "";
+ file.GetPath(path, sizeof(path));
+ if (path[0]) {
+ EmplaceSafeString(object, "path", std::string(path));
+ }
+ }
+ return llvm::json::Value(std::move(object));
+}
+
+llvm::json::Value CreateSource(lldb::SBFrame &frame, int64_t &disasm_line) {
+ disasm_line = 0;
+ auto line_entry = frame.GetLineEntry();
+ if (line_entry.GetFileSpec().IsValid())
+ return CreateSource(line_entry);
+
+ llvm::json::Object object;
+ const auto pc = frame.GetPC();
+
+ lldb::SBInstructionList insts;
+ lldb::SBFunction function = frame.GetFunction();
+ lldb::addr_t low_pc = LLDB_INVALID_ADDRESS;
+ if (function.IsValid()) {
+ low_pc = function.GetStartAddress().GetLoadAddress(g_vsc.target);
+ auto addr_srcref = g_vsc.addr_to_source_ref.find(low_pc);
+ if (addr_srcref != g_vsc.addr_to_source_ref.end()) {
+ // We have this disassembly cached already, return the existing
+ // sourceReference
+ object.try_emplace("sourceReference", addr_srcref->second);
+ disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc);
+ } else {
+ insts = function.GetInstructions(g_vsc.target);
+ }
+ } else {
+ lldb::SBSymbol symbol = frame.GetSymbol();
+ if (symbol.IsValid()) {
+ low_pc = symbol.GetStartAddress().GetLoadAddress(g_vsc.target);
+ auto addr_srcref = g_vsc.addr_to_source_ref.find(low_pc);
+ if (addr_srcref != g_vsc.addr_to_source_ref.end()) {
+ // We have this disassembly cached already, return the existing
+ // sourceReference
+ object.try_emplace("sourceReference", addr_srcref->second);
+ disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc);
+ } else {
+ insts = symbol.GetInstructions(g_vsc.target);
+ }
+ }
+ }
+ const auto num_insts = insts.GetSize();
+ if (low_pc != LLDB_INVALID_ADDRESS && num_insts > 0) {
+ EmplaceSafeString(object, "name", frame.GetFunctionName());
+ SourceReference source;
+ llvm::raw_string_ostream src_strm(source.content);
+ std::string line;
+ for (size_t i = 0; i < num_insts; ++i) {
+ lldb::SBInstruction inst = insts.GetInstructionAtIndex(i);
+ const auto inst_addr = inst.GetAddress().GetLoadAddress(g_vsc.target);
+ const char *m = inst.GetMnemonic(g_vsc.target);
+ const char *o = inst.GetOperands(g_vsc.target);
+ const char *c = inst.GetComment(g_vsc.target);
+ if (pc == inst_addr)
+ disasm_line = i + 1;
+ const auto inst_offset = inst_addr - low_pc;
+ int spaces = 0;
+ if (inst_offset < 10)
+ spaces = 3;
+ else if (inst_offset < 100)
+ spaces = 2;
+ else if (inst_offset < 1000)
+ spaces = 1;
+ line.clear();
+ llvm::raw_string_ostream line_strm(line);
+ line_strm << llvm::formatv("{0:X+}: <{1}> {2} {3,12} {4}", inst_addr,
+ inst_offset, llvm::fmt_repeat(' ', spaces), m,
+ o);
+
+ // If there is a comment append it starting at column 60 or after one
+ // space past the last char
+ const uint32_t comment_row = std::max(line_strm.str().size(), (size_t)60);
+ if (c && c[0]) {
+ if (line.size() < comment_row)
+ line_strm.indent(comment_row - line_strm.str().size());
+ line_strm << " # " << c;
+ }
+ src_strm << line_strm.str() << "\n";
+ source.addr_to_line[inst_addr] = i + 1;
+ }
+ // Flush the source stream
+ src_strm.str();
+ auto sourceReference = VSCode::GetNextSourceReference();
+ g_vsc.source_map[sourceReference] = std::move(source);
+ g_vsc.addr_to_source_ref[low_pc] = sourceReference;
+ object.try_emplace("sourceReference", sourceReference);
+ }
+ return llvm::json::Value(std::move(object));
+}
+
+// "StackFrame": {
+// "type": "object",
+// "description": "A Stackframe contains the source location.",
+// "properties": {
+// "id": {
+// "type": "integer",
+// "description": "An identifier for the stack frame. It must be unique
+// across all threads. This id can be used to retrieve
+// the scopes of the frame with the 'scopesRequest' or
+// to restart the execution of a stackframe."
+// },
+// "name": {
+// "type": "string",
+// "description": "The name of the stack frame, typically a method name."
+// },
+// "source": {
+// "$ref": "#/definitions/Source",
+// "description": "The optional source of the frame."
+// },
+// "line": {
+// "type": "integer",
+// "description": "The line within the file of the frame. If source is
+// null or doesn't exist, line is 0 and must be ignored."
+// },
+// "column": {
+// "type": "integer",
+// "description": "The column within the line. If source is null or
+// doesn't exist, column is 0 and must be ignored."
+// },
+// "endLine": {
+// "type": "integer",
+// "description": "An optional end line of the range covered by the
+// stack frame."
+// },
+// "endColumn": {
+// "type": "integer",
+// "description": "An optional end column of the range covered by the
+// stack frame."
+// },
+// "moduleId": {
+// "type": ["integer", "string"],
+// "description": "The module associated with this frame, if any."
+// },
+// "presentationHint": {
+// "type": "string",
+// "enum": [ "normal", "label", "subtle" ],
+// "description": "An optional hint for how to present this frame in
+// the UI. A value of 'label' can be used to indicate
+// that the frame is an artificial frame that is used
+// as a visual label or separator. A value of 'subtle'
+// can be used to change the appearance of a frame in
+// a 'subtle' way."
+// }
+// },
+// "required": [ "id", "name", "line", "column" ]
+// }
+llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) {
+ llvm::json::Object object;
+ int64_t frame_id = MakeVSCodeFrameID(frame);
+ object.try_emplace("id", frame_id);
+ EmplaceSafeString(object, "name", frame.GetFunctionName());
+ int64_t disasm_line = 0;
+ object.try_emplace("source", CreateSource(frame, disasm_line));
+
+ auto line_entry = frame.GetLineEntry();
+ if (disasm_line > 0) {
+ object.try_emplace("line", disasm_line);
+ } else {
+ auto line = line_entry.GetLine();
+ if (line == UINT32_MAX)
+ line = 0;
+ object.try_emplace("line", line);
+ }
+ object.try_emplace("column", line_entry.GetColumn());
+ return llvm::json::Value(std::move(object));
+}
+
+// "Thread": {
+// "type": "object",
+// "description": "A Thread",
+// "properties": {
+// "id": {
+// "type": "integer",
+// "description": "Unique identifier for the thread."
+// },
+// "name": {
+// "type": "string",
+// "description": "A name of the thread."
+// }
+// },
+// "required": [ "id", "name" ]
+// }
+llvm::json::Value CreateThread(lldb::SBThread &thread) {
+ llvm::json::Object object;
+ object.try_emplace("id", (int64_t)thread.GetThreadID());
+ char thread_str[64];
+ snprintf(thread_str, sizeof(thread_str), "Thread #%u", thread.GetIndexID());
+ const char *name = thread.GetName();
+ if (name) {
+ std::string thread_with_name(thread_str);
+ thread_with_name += ' ';
+ thread_with_name += name;
+ EmplaceSafeString(object, "name", thread_with_name);
+ } else {
+ EmplaceSafeString(object, "name", std::string(thread_str));
+ }
+ return llvm::json::Value(std::move(object));
+}
+
+// "StoppedEvent": {
+// "allOf": [ { "$ref": "#/definitions/Event" }, {
+// "type": "object",
+// "description": "Event message for 'stopped' event type. The event
+// indicates that the execution of the debuggee has stopped
+// due to some condition. This can be caused by a break
+// point previously set, a stepping action has completed,
+// by executing a debugger statement etc.",
+// "properties": {
+// "event": {
+// "type": "string",
+// "enum": [ "stopped" ]
+// },
+// "body": {
+// "type": "object",
+// "properties": {
+// "reason": {
+// "type": "string",
+// "description": "The reason for the event. For backward
+// compatibility this string is shown in the UI if
+// the 'description' attribute is missing (but it
+// must not be translated).",
+// "_enum": [ "step", "breakpoint", "exception", "pause", "entry" ]
+// },
+// "description": {
+// "type": "string",
+// "description": "The full reason for the event, e.g. 'Paused
+// on exception'. This string is shown in the UI
+// as is."
+// },
+// "threadId": {
+// "type": "integer",
+// "description": "The thread which was stopped."
+// },
+// "text": {
+// "type": "string",
+// "description": "Additional information. E.g. if reason is
+// 'exception', text contains the exception name.
+// This string is shown in the UI."
+// },
+// "allThreadsStopped": {
+// "type": "boolean",
+// "description": "If allThreadsStopped is true, a debug adapter
+// can announce that all threads have stopped.
+// The client should use this information to
+// enable that all threads can be expanded to
+// access their stacktraces. If the attribute
+// is missing or false, only the thread with the
+// given threadId can be expanded."
+// }
+// },
+// "required": [ "reason" ]
+// }
+// },
+// "required": [ "event", "body" ]
+// }]
+// }
+llvm::json::Value CreateThreadStopped(lldb::SBThread &thread,
+ uint32_t stop_id) {
+ llvm::json::Object event(CreateEventObject("stopped"));
+ llvm::json::Object body;
+ switch (thread.GetStopReason()) {
+ case lldb::eStopReasonTrace:
+ case lldb::eStopReasonPlanComplete:
+ body.try_emplace("reason", "step");
+ break;
+ case lldb::eStopReasonBreakpoint: {
+ ExceptionBreakpoint *exc_bp = g_vsc.GetExceptionBPFromStopReason(thread);
+ if (exc_bp) {
+ body.try_emplace("reason", "exception");
+ EmplaceSafeString(body, "description", exc_bp->label);
+ } else {
+ body.try_emplace("reason", "breakpoint");
+ }
+ } break;
+ case lldb::eStopReasonWatchpoint:
+ case lldb::eStopReasonInstrumentation:
+ body.try_emplace("reason", "breakpoint");
+ break;
+ case lldb::eStopReasonSignal:
+ body.try_emplace("reason", "exception");
+ break;
+ case lldb::eStopReasonException:
+ body.try_emplace("reason", "exception");
+ break;
+ case lldb::eStopReasonExec:
+ body.try_emplace("reason", "entry");
+ break;
+ case lldb::eStopReasonThreadExiting:
+ case lldb::eStopReasonInvalid:
+ case lldb::eStopReasonNone:
+ break;
+ }
+ if (stop_id == 0)
+ body.try_emplace("reason", "entry");
+ const lldb::tid_t tid = thread.GetThreadID();
+ body.try_emplace("threadId", (int64_t)tid);
+ // If no description has been set, then set it to the default thread stopped
+ // description. If we have breakpoints that get hit and shouldn't be reported
+ // as breakpoints, then they will set the description above.
+ if (ObjectContainsKey(body, "description")) {
+ char description[1024];
+ if (thread.GetStopDescription(description, sizeof(description))) {
+ EmplaceSafeString(body, "description", std::string(description));
+ }
+ }
+ if (tid == g_vsc.focus_tid) {
+ body.try_emplace("threadCausedFocus", true);
+ }
+ body.try_emplace("preserveFocusHint", tid != g_vsc.focus_tid);
+ body.try_emplace("allThreadsStopped", true);
+ event.try_emplace("body", std::move(body));
+ return llvm::json::Value(std::move(event));
+}
+
+// "Variable": {
+// "type": "object",
+// "description": "A Variable is a name/value pair. Optionally a variable
+// can have a 'type' that is shown if space permits or when
+// hovering over the variable's name. An optional 'kind' is
+// used to render additional properties of the variable,
+// e.g. different icons can be used to indicate that a
+// variable is public or private. If the value is
+// structured (has children), a handle is provided to
+// retrieve the children with the VariablesRequest. If
+// the number of named or indexed children is large, the
+// numbers should be returned via the optional
+// 'namedVariables' and 'indexedVariables' attributes. The
+// client can use this optional information to present the
+// children in a paged UI and fetch them in chunks.",
+// "properties": {
+// "name": {
+// "type": "string",
+// "description": "The variable's name."
+// },
+// "value": {
+// "type": "string",
+// "description": "The variable's value. This can be a multi-line text,
+// e.g. for a function the body of a function."
+// },
+// "type": {
+// "type": "string",
+// "description": "The type of the variable's value. Typically shown in
+// the UI when hovering over the value."
+// },
+// "presentationHint": {
+// "$ref": "#/definitions/VariablePresentationHint",
+// "description": "Properties of a variable that can be used to determine
+// how to render the variable in the UI."
+// },
+// "evaluateName": {
+// "type": "string",
+// "description": "Optional evaluatable name of this variable which can
+// be passed to the 'EvaluateRequest' to fetch the
+// variable's value."
+// },
+// "variablesReference": {
+// "type": "integer",
+// "description": "If variablesReference is > 0, the variable is
+// structured and its children can be retrieved by
+// passing variablesReference to the VariablesRequest."
+// },
+// "namedVariables": {
+// "type": "integer",
+// "description": "The number of named child variables. The client can
+// use this optional information to present the children
+// in a paged UI and fetch them in chunks."
+// },
+// "indexedVariables": {
+// "type": "integer",
+// "description": "The number of indexed child variables. The client
+// can use this optional information to present the
+// children in a paged UI and fetch them in chunks."
+// }
+// },
+// "required": [ "name", "value", "variablesReference" ]
+// }
+llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
+ int64_t varID, bool format_hex) {
+ llvm::json::Object object;
+ auto name = v.GetName();
+ EmplaceSafeString(object, "name", name ? name : "<null>");
+ if (format_hex)
+ v.SetFormat(lldb::eFormatHex);
+ SetValueForKey(v, object, "value");
+ auto type_cstr = v.GetType().GetDisplayTypeName();
+ EmplaceSafeString(object, "type", type_cstr ? type_cstr : NO_TYPENAME);
+ if (varID != INT64_MAX)
+ object.try_emplace("id", varID);
+ if (v.MightHaveChildren())
+ object.try_emplace("variablesReference", variablesReference);
+ else
+ object.try_emplace("variablesReference", (int64_t)0);
+ lldb::SBStream evaluateStream;
+ v.GetExpressionPath(evaluateStream);
+ const char *evaluateName = evaluateStream.GetData();
+ if (evaluateName && evaluateName[0])
+ EmplaceSafeString(object, "evaluateName", std::string(evaluateName));
+ return llvm::json::Value(std::move(object));
+}
+
+} // namespace lldb_vscode
+
diff --git a/gnu/llvm/lldb/tools/lldb-vscode/JSONUtils.h b/gnu/llvm/lldb/tools/lldb-vscode/JSONUtils.h
new file mode 100644
index 00000000000..2391ac32b5c
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-vscode/JSONUtils.h
@@ -0,0 +1,395 @@
+//===-- JSONUtils.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDBVSCODE_JSONUTILS_H_
+#define LLDBVSCODE_JSONUTILS_H_
+
+#include <stdint.h>
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/JSON.h"
+#include "VSCodeForward.h"
+
+namespace lldb_vscode {
+
+/// Emplace a StringRef in a json::Object after enusring that the
+/// string is valid UTF8. If not, first call llvm::json::fixUTF8
+/// before emplacing.
+///
+/// \param[in] obj
+/// A JSON object that we will attempt to emplace the value in
+///
+/// \param[in] key
+/// The key to use when emplacing the value
+///
+/// \param[in] str
+/// The string to emplace
+void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key,
+ llvm::StringRef str);
+
+/// Extract simple values as a string.
+///
+/// \param[in] value
+/// A JSON value to extract the string from.
+///
+/// \return
+/// A llvm::StringRef that contains the string value, or an empty
+/// string if \a value isn't a string.
+llvm::StringRef GetAsString(const llvm::json::Value &value);
+
+/// Extract the string value for the specified key from the
+/// specified object.
+///
+/// \param[in] obj
+/// A JSON object that we will attempt to extract the value from
+///
+/// \param[in] key
+/// The key to use when extracting the value
+///
+/// \return
+/// A llvm::StringRef that contains the string value for the
+/// specified \a key, or an empty string if there is no key that
+/// matches or if the value is not a string.
+llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key);
+llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key);
+
+/// Extract the unsigned integer value for the specified key from
+/// the specified object.
+///
+/// \param[in] obj
+/// A JSON object that we will attempt to extract the value from
+///
+/// \param[in] key
+/// The key to use when extracting the value
+///
+/// \return
+/// The unsigned integer value for the specified \a key, or
+/// \a fail_value if there is no key that matches or if the
+/// value is not an integer.
+uint64_t GetUnsigned(const llvm::json::Object &obj, llvm::StringRef key,
+ uint64_t fail_value);
+uint64_t GetUnsigned(const llvm::json::Object *obj, llvm::StringRef key,
+ uint64_t fail_value);
+
+/// Extract the boolean value for the specified key from the
+/// specified object.
+///
+/// \param[in] obj
+/// A JSON object that we will attempt to extract the value from
+///
+/// \param[in] key
+/// The key to use when extracting the value
+///
+/// \return
+/// The boolean value for the specified \a key, or \a fail_value
+/// if there is no key that matches or if the value is not a
+/// boolean value of an integer.
+bool GetBoolean(const llvm::json::Object &obj, llvm::StringRef key,
+ bool fail_value);
+bool GetBoolean(const llvm::json::Object *obj, llvm::StringRef key,
+ bool fail_value);
+
+/// Extract the signed integer for the specified key from the
+/// specified object.
+///
+/// \param[in] obj
+/// A JSON object that we will attempt to extract the value from
+///
+/// \param[in] key
+/// The key to use when extracting the value
+///
+/// \return
+/// The signed integer value for the specified \a key, or
+/// \a fail_value if there is no key that matches or if the
+/// value is not an integer.
+int64_t GetSigned(const llvm::json::Object &obj, llvm::StringRef key,
+ int64_t fail_value);
+int64_t GetSigned(const llvm::json::Object *obj, llvm::StringRef key,
+ int64_t fail_value);
+
+/// Check if the specified key exists in the specified object.
+///
+/// \param[in] obj
+/// A JSON object that we will attempt to extract the value from
+///
+/// \param[in] key
+/// The key to check for
+///
+/// \return
+/// \b True if the key exists in the \a obj, \b False otherwise.
+bool ObjectContainsKey(const llvm::json::Object &obj, llvm::StringRef key);
+
+/// Extract an array of strings for the specified key from an object.
+///
+/// String values in the array will be extracted without any quotes
+/// around them. Numbers and Booleans will be converted into
+/// strings. Any NULL, array or objects values in the array will be
+/// ignored.
+///
+/// \param[in] obj
+/// A JSON object that we will attempt to extract the array from
+///
+/// \param[in] key
+/// The key to use when extracting the value
+///
+/// \return
+/// An array of string values for the specified \a key, or
+/// \a fail_value if there is no key that matches or if the
+/// value is not an array or all items in the array are not
+/// strings, numbers or booleans.
+std::vector<std::string> GetStrings(const llvm::json::Object *obj,
+ llvm::StringRef key);
+
+/// Fill a response object given the request object.
+///
+/// The \a response object will get its "type" set to "response",
+/// the "seq" set to zero, "response_seq" set to the "seq" value from
+/// \a request, "command" set to the "command" from \a request,
+/// and "success" set to true.
+///
+/// \param[in] request
+/// The request object received from a call to VSCode::ReadJSON().
+///
+/// \param[in,out] response
+/// An empty llvm::json::Object object that will be filled
+/// in as noted in description.
+void FillResponse(const llvm::json::Object &request,
+ llvm::json::Object &response);
+
+/// Emplace the string value from an SBValue into the supplied object
+/// using \a key as the key that will contain the value.
+///
+/// The value is what we will display in VS Code. Some SBValue objects
+/// can have a value and/or a summary. If a value has both, we
+/// combine the value and the summary into one string. If we only have a
+/// value or summary, then that is considered the value. If there is
+/// no value and no summary then the value is the type name followed by
+/// the address of the type if it has an address.
+///
+///
+/// \param[in] v
+/// A lldb::SBValue object to extract the string value from
+///
+///
+/// \param[in] object
+/// The object to place the value object into
+///
+///
+/// \param[in] key
+/// The key name to use when inserting the value object we create
+void SetValueForKey(lldb::SBValue &v, llvm::json::Object &object,
+ llvm::StringRef key);
+
+/// Converts \a bp to a JSON value and appends all locations to the
+/// \a breakpoints array.
+///
+/// \param[in] bp
+/// A LLDB breakpoint object which will get all locations extracted
+/// and converted into a JSON objects in the \a breakpoints array
+///
+/// \param[in] breakpoints
+/// A JSON array that will get a llvm::json::Value for \a bp
+/// appended to it.
+void AppendBreakpoint(lldb::SBBreakpoint &bp, llvm::json::Array &breakpoints);
+
+/// Converts breakpoint location to a Visual Studio Code "Breakpoint"
+/// JSON object and appends it to the \a breakpoints array.
+///
+/// \param[in] bp_loc
+/// A LLDB breakpoint location object to convert into a JSON value
+///
+/// \return
+/// A "Breakpoint" JSON object with that follows the formal JSON
+/// definition outlined by Microsoft.
+llvm::json::Value CreateBreakpoint(lldb::SBBreakpointLocation &bp_loc);
+
+/// Create a "Event" JSON object using \a event_name as the event name
+///
+/// \param[in] event_name
+/// The string value to use for the "event" key in the JSON object.
+///
+/// \return
+/// A "Event" JSON object with that follows the formal JSON
+/// definition outlined by Microsoft.
+llvm::json::Object CreateEventObject(const llvm::StringRef event_name);
+
+/// Create a "ExceptionBreakpointsFilter" JSON object as described in
+/// the Visual Studio Code debug adaptor definition.
+///
+/// \param[in] bp
+/// The exception breakppoint object to use
+///
+/// \return
+/// A "ExceptionBreakpointsFilter" JSON object with that follows
+/// the formal JSON definition outlined by Microsoft.
+llvm::json::Value
+CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp);
+
+/// Create a "Scope" JSON object as described in the Visual Studio Code
+/// debug adaptor definition.
+///
+/// \param[in] name
+/// The value to place into the "name" key
+//
+/// \param[in] variablesReference
+/// The value to place into the "variablesReference" key
+//
+/// \param[in] namedVariables
+/// The value to place into the "namedVariables" key
+//
+/// \param[in] expensive
+/// The value to place into the "expensive" key
+///
+/// \return
+/// A "Scope" JSON object with that follows the formal JSON
+/// definition outlined by Microsoft.
+llvm::json::Value CreateScope(const llvm::StringRef name,
+ int64_t variablesReference,
+ int64_t namedVariables, bool expensive);
+
+/// Create a "Source" JSON object as described in the Visual Studio Code
+/// debug adaptor definition.
+///
+/// \param[in] line_entry
+/// The LLDB line table to use when populating out the "Source"
+/// object
+///
+/// \return
+/// A "Source" JSON object with that follows the formal JSON
+/// definition outlined by Microsoft.
+llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry);
+
+/// Create a "Source" object for a given frame.
+///
+/// When there is no source file information for a stack frame, we will
+/// create disassembly for a function and store a permanent
+/// "sourceReference" that contains the textual disassembly for a
+/// function along with address to line information. The "Source" object
+/// that is created will contain a "sourceReference" that the VSCode
+/// protocol can later fetch as text in order to display disassembly.
+/// The PC will be extracted from the frame and the disassembly line
+/// within the source referred to by "sourceReference" will be filled
+/// in.
+///
+/// \param[in] frame
+/// The LLDB stack frame to use when populating out the "Source"
+/// object.
+///
+/// \param[out] disasm_line
+/// The line within the "sourceReference" file that the PC from
+/// \a frame matches.
+///
+/// \return
+/// A "Source" JSON object with that follows the formal JSON
+/// definition outlined by Microsoft.
+llvm::json::Value CreateSource(lldb::SBFrame &frame, int64_t &disasm_line);
+
+/// Create a "StackFrame" object for a LLDB frame object.
+///
+/// This function will fill in the following keys in the returned
+/// object:
+/// "id" - the stack frame ID as an integer
+/// "name" - the function name as a string
+/// "source" - source file information as a "Source" VSCode object
+/// "line" - the source file line number as an integer
+/// "column" - the source file column number as an integer
+///
+/// \param[in] frame
+/// The LLDB stack frame to use when populating out the "StackFrame"
+/// object.
+///
+/// \return
+/// A "StackFrame" JSON object with that follows the formal JSON
+/// definition outlined by Microsoft.
+llvm::json::Value CreateStackFrame(lldb::SBFrame &frame);
+
+/// Create a "Thread" object for a LLDB thread object.
+///
+/// This function will fill in the following keys in the returned
+/// object:
+/// "id" - the thread ID as an integer
+/// "name" - the thread name as a string which combines the LLDB
+/// thread index ID along with the string name of the thread
+/// from the OS if it has a name.
+///
+/// \param[in] thread
+/// The LLDB thread to use when populating out the "Thread"
+/// object.
+///
+/// \return
+/// A "Thread" JSON object with that follows the formal JSON
+/// definition outlined by Microsoft.
+llvm::json::Value CreateThread(lldb::SBThread &thread);
+
+/// Create a "StoppedEvent" object for a LLDB thread object.
+///
+/// This function will fill in the following keys in the returned
+/// object's "body" object:
+/// "reason" - With a valid stop reason enumeration string value
+/// that Microsoft specifies
+/// "threadId" - The thread ID as an integer
+/// "description" - a stop description (like "breakpoint 12.3") as a
+/// string
+/// "preserveFocusHint" - a boolean value that states if this thread
+/// should keep the focus in the GUI.
+/// "allThreadsStopped" - set to True to indicate that all threads
+/// stop when any thread stops.
+///
+/// \param[in] thread
+/// The LLDB thread to use when populating out the "StoppedEvent"
+/// object.
+///
+/// \return
+/// A "StoppedEvent" JSON object with that follows the formal JSON
+/// definition outlined by Microsoft.
+llvm::json::Value CreateThreadStopped(lldb::SBThread &thread, uint32_t stop_id);
+
+/// Create a "Variable" object for a LLDB thread object.
+///
+/// This function will fill in the following keys in the returned
+/// object:
+/// "name" - the name of the variable
+/// "value" - the value of the variable as a string
+/// "type" - the typename of the variable as a string
+/// "id" - a unique identifier for a value in case there are multiple
+/// variables with the same name. Other parts of the VSCode
+/// protocol refer to values by name so this can help
+/// disambiguate such cases if a IDE passes this "id" value
+/// back down.
+/// "variablesReference" - Zero if the variable has no children,
+/// non-zero integer otherwise which can be used to expand
+/// the variable.
+/// "evaluateName" - The name of the variable to use in expressions
+/// as a string.
+///
+/// \param[in] v
+/// The LLDB value to use when populating out the "Variable"
+/// object.
+///
+/// \param[in] variablesReference
+/// The variable reference. Zero if this value isn't structured
+/// and has no children, non-zero if it does have children and
+/// might be asked to expand itself.
+///
+/// \param[in] varID
+/// A unique variable identifier to help in properly identifying
+/// variables with the same name. This is an extension to the
+/// VS protocol.
+///
+/// \param[in] format_hex
+/// It set to true the variable will be formatted as hex in
+/// the "value" key value pair for the value of the variable.
+///
+/// \return
+/// A "Variable" JSON object with that follows the formal JSON
+/// definition outlined by Microsoft.
+llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
+ int64_t varID, bool format_hex);
+
+} // namespace lldb_vscode
+
+#endif
diff --git a/gnu/llvm/lldb/tools/lldb-vscode/LLDBUtils.cpp b/gnu/llvm/lldb/tools/lldb-vscode/LLDBUtils.cpp
new file mode 100644
index 00000000000..5be293dab4e
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-vscode/LLDBUtils.cpp
@@ -0,0 +1,97 @@
+//===-- LLDBUtils.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LLDBUtils.h"
+#include "VSCode.h"
+
+namespace lldb_vscode {
+
+void RunLLDBCommands(llvm::StringRef prefix,
+ const llvm::ArrayRef<std::string> &commands,
+ llvm::raw_ostream &strm) {
+ if (commands.empty())
+ return;
+ lldb::SBCommandInterpreter interp = g_vsc.debugger.GetCommandInterpreter();
+ if (!prefix.empty())
+ strm << prefix << "\n";
+ for (const auto &command : commands) {
+ lldb::SBCommandReturnObject result;
+ strm << "(lldb) " << command << "\n";
+ interp.HandleCommand(command.c_str(), result);
+ auto output_len = result.GetOutputSize();
+ if (output_len) {
+ const char *output = result.GetOutput();
+ strm << output;
+ }
+ auto error_len = result.GetErrorSize();
+ if (error_len) {
+ const char *error = result.GetError();
+ strm << error;
+ }
+ }
+}
+
+std::string RunLLDBCommands(llvm::StringRef prefix,
+ const llvm::ArrayRef<std::string> &commands) {
+ std::string s;
+ llvm::raw_string_ostream strm(s);
+ RunLLDBCommands(prefix, commands, strm);
+ strm.flush();
+ return s;
+}
+
+bool ThreadHasStopReason(lldb::SBThread &thread) {
+ switch (thread.GetStopReason()) {
+ case lldb::eStopReasonTrace:
+ case lldb::eStopReasonPlanComplete:
+ case lldb::eStopReasonBreakpoint:
+ case lldb::eStopReasonWatchpoint:
+ case lldb::eStopReasonInstrumentation:
+ case lldb::eStopReasonSignal:
+ case lldb::eStopReasonException:
+ case lldb::eStopReasonExec:
+ return true;
+ case lldb::eStopReasonThreadExiting:
+ case lldb::eStopReasonInvalid:
+ case lldb::eStopReasonNone:
+ break;
+ }
+ return false;
+}
+
+static uint32_t constexpr THREAD_INDEX_SHIFT = 19;
+
+uint32_t GetLLDBThreadIndexID(uint64_t dap_frame_id) {
+ return dap_frame_id >> THREAD_INDEX_SHIFT;
+}
+
+uint32_t GetLLDBFrameID(uint64_t dap_frame_id) {
+ return dap_frame_id & ((1u << THREAD_INDEX_SHIFT) - 1);
+}
+
+int64_t MakeVSCodeFrameID(lldb::SBFrame &frame) {
+ return (int64_t)(frame.GetThread().GetIndexID() << THREAD_INDEX_SHIFT |
+ frame.GetFrameID());
+}
+
+static uint32_t constexpr BREAKPOINT_ID_SHIFT = 22;
+
+uint32_t GetLLDBBreakpointID(uint64_t dap_breakpoint_id) {
+ return dap_breakpoint_id >> BREAKPOINT_ID_SHIFT;
+}
+
+uint32_t GetLLDBBreakpointLocationID(uint64_t dap_breakpoint_id) {
+ return dap_breakpoint_id & ((1u << BREAKPOINT_ID_SHIFT) - 1);
+}
+
+int64_t MakeVSCodeBreakpointID(lldb::SBBreakpointLocation &bp_loc) {
+ return (int64_t)(bp_loc.GetBreakpoint().GetID() << BREAKPOINT_ID_SHIFT |
+ bp_loc.GetID());
+}
+
+} // namespace lldb_vscode
diff --git a/gnu/llvm/lldb/tools/lldb-vscode/LLDBUtils.h b/gnu/llvm/lldb/tools/lldb-vscode/LLDBUtils.h
new file mode 100644
index 00000000000..82c17eb3cdc
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-vscode/LLDBUtils.h
@@ -0,0 +1,151 @@
+//===-- LLDBUtils.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDBVSCODE_LLDBUTILS_H_
+#define LLDBVSCODE_LLDBUTILS_H_
+
+#include "VSCodeForward.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/raw_ostream.h"
+#include <string>
+#include <vector>
+
+namespace lldb_vscode {
+
+/// Run a list of LLDB commands in the LLDB command interpreter.
+///
+/// All output from every command, including the prompt + the command
+/// is placed into the "strm" argument.
+///
+/// \param[in] prefix
+/// A string that will be printed into \a strm prior to emitting
+/// the prompt + command and command output. Can be NULL.
+///
+/// \param[in] commands
+/// An array of LLDB commands to execute.
+///
+/// \param[in] strm
+/// The stream that will receive the prefix, prompt + command and
+/// all command output.
+void RunLLDBCommands(llvm::StringRef prefix,
+ const llvm::ArrayRef<std::string> &commands,
+ llvm::raw_ostream &strm);
+
+/// Run a list of LLDB commands in the LLDB command interpreter.
+///
+/// All output from every command, including the prompt + the command
+/// is returned in the std::string return value.
+///
+/// \param[in] prefix
+/// A string that will be printed into \a strm prior to emitting
+/// the prompt + command and command output. Can be NULL.
+///
+/// \param[in] commands
+/// An array of LLDB commands to execute.
+///
+/// \return
+/// A std::string that contains the prefix and all commands and
+/// command output
+std::string RunLLDBCommands(llvm::StringRef prefix,
+ const llvm::ArrayRef<std::string> &commands);
+
+/// Check if a thread has a stop reason.
+///
+/// \param[in] thread
+/// The LLDB thread object to check
+///
+/// \return
+/// \b True if the thread has a valid stop reason, \b false
+/// otherwise.
+bool ThreadHasStopReason(lldb::SBThread &thread);
+
+/// Given a LLDB frame, make a frame ID that is unique to a specific
+/// thread and frame.
+///
+/// VSCode requires a Stackframe "id" to be unique, so we use the frame
+/// index in the lower 32 bits and the thread index ID in the upper 32
+/// bits.
+///
+/// \param[in] frame
+/// The LLDB stack frame object generate the ID for
+///
+/// \return
+/// A unique integer that allows us to easily find the right
+/// stack frame within a thread on subsequent VS code requests.
+int64_t MakeVSCodeFrameID(lldb::SBFrame &frame);
+
+/// Given a VSCode frame ID, convert to a LLDB thread index id.
+///
+/// VSCode requires a Stackframe "id" to be unique, so we use the frame
+/// index in the lower THREAD_INDEX_SHIFT bits and the thread index ID in
+/// the upper 32 - THREAD_INDEX_SHIFT bits.
+///
+/// \param[in] dap_frame_id
+/// The VSCode frame ID to convert to a thread index ID.
+///
+/// \return
+/// The LLDB thread index ID.
+uint32_t GetLLDBThreadIndexID(uint64_t dap_frame_id);
+
+/// Given a VSCode frame ID, convert to a LLDB frame ID.
+///
+/// VSCode requires a Stackframe "id" to be unique, so we use the frame
+/// index in the lower THREAD_INDEX_SHIFT bits and the thread index ID in
+/// the upper 32 - THREAD_INDEX_SHIFT bits.
+///
+/// \param[in] dap_frame_id
+/// The VSCode frame ID to convert to a frame ID.
+///
+/// \return
+/// The LLDB frame index ID.
+uint32_t GetLLDBFrameID(uint64_t dap_frame_id);
+
+/// Given a LLDB breakpoint, make a breakpoint ID that is unique to a
+/// specific breakpoint and breakpoint location.
+///
+/// VSCode requires a Breakpoint "id" to be unique, so we use the
+/// breakpoint ID in the lower BREAKPOINT_ID_SHIFT bits and the
+/// breakpoint location ID in the upper BREAKPOINT_ID_SHIFT bits.
+///
+/// \param[in] bp_loc
+/// The LLDB break point location.
+///
+/// \return
+/// A unique integer that allows us to easily find the right
+/// stack frame within a thread on subsequent VS code requests.
+int64_t MakeVSCodeBreakpointID(lldb::SBBreakpointLocation &bp_loc);
+
+/// Given a VSCode breakpoint ID, convert to a LLDB breakpoint ID.
+///
+/// VSCode requires a Breakpoint "id" to be unique, so we use the
+/// breakpoint ID in the lower BREAKPOINT_ID_SHIFT bits and the
+/// breakpoint location ID in the upper BREAKPOINT_ID_SHIFT bits.
+///
+/// \param[in] dap_breakpoint_id
+/// The VSCode breakpoint ID to convert to an LLDB breakpoint ID.
+///
+/// \return
+/// The LLDB breakpoint ID.
+uint32_t GetLLDBBreakpointID(uint64_t dap_breakpoint_id);
+
+/// Given a VSCode breakpoint ID, convert to a LLDB breakpoint location ID.
+///
+/// VSCode requires a Breakpoint "id" to be unique, so we use the
+/// breakpoint ID in the lower BREAKPOINT_ID_SHIFT bits and the
+/// breakpoint location ID in the upper BREAKPOINT_ID_SHIFT bits.
+///
+/// \param[in] dap_breakpoint_id
+/// The VSCode frame ID to convert to a breakpoint location ID.
+///
+/// \return
+/// The LLDB breakpoint location ID.
+uint32_t GetLLDBBreakpointLocationID(uint64_t dap_breakpoint_id);
+} // namespace lldb_vscode
+
+#endif
diff --git a/gnu/llvm/lldb/tools/lldb-vscode/README.md b/gnu/llvm/lldb/tools/lldb-vscode/README.md
new file mode 100644
index 00000000000..2294659fc29
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-vscode/README.md
@@ -0,0 +1,195 @@
+
+# Table of Contents
+
+- [Introduction](#Introduction)
+- [Installation](#Installation-Visual-Studio-Code)
+- [Configurations](#configurations)
+ - [Launch Configuration Settings](#launch-configuration-settings)
+ - [Attach Configuration Settings](#attach-configuration-settings)
+ - [Example configurations](#example-configurations)
+ - [Launching](#launching)
+ - [Attach to process using process ID](#attach-using-pid)
+ - [Attach to process by name](#attach-by-name)
+ - [Loading a core file](#loading-a-core-file)
+
+# Introduction
+
+The `lldb-vscode` tool creates a command line tool that implements the [Visual
+Studio Code Debug API](https://code.visualstudio.com/docs/extensionAPI/api-debugging).
+It can be installed as an extension for the Visual Studio Code and Nuclide IDE.
+The protocol is easy to run remotely and also can allow other tools and IDEs to
+get a full featured debugger with a well defined protocol.
+
+# Installation for Visual Studio Code
+
+Installing the plug-in involves creating a directory in the `~/.vscode/extensions` folder and copying the package.json file that is in the same directory as this
+documentation into it, and copying to symlinking a lldb-vscode binary into
+the `bin` directory inside the plug-in directory.
+
+If you want to make a stand alone plug-in that you can send to others on unix systems:
+
+```
+$ mkdir -p ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0/bin
+$ cp package.json ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0
+$ cd ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0/bin
+$ cp /path/to/a/built/lldb-vscode .
+$ cp /path/to/a/built/liblldb.so .
+```
+
+
+If you want to make a stand alone plug-in that you can send to others on macOS systems:
+
+```
+$ mkdir -p ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0/bin
+$ cp package.json ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0
+$ cd ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0/bin
+$ cp /path/to/a/built/lldb-vscode .
+$ rsync -av /path/to/a/built/LLDB.framework LLDB.framework
+```
+
+You might need to create additional directories for the `liblldb.so` or `LLDB.framework` inside or next to the `bin` folder depending on how the [rpath](https://en.wikipedia.org/wiki/Rpath) is set in your `lldb-vscode` binary. By default the `Debug` builds of LLDB usually includes
+the current executable directory in the rpath, so these steps should work for most people.
+
+To create a plug-in that symlinks into your `lldb-vscode` in your build directory:
+
+```
+$ mkdir -p ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0/bin
+$ cp package.json ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0
+$ cd ~/.vscode/extensions/llvm-org.lldb-vscode-0.1.0/bin
+$ ln -s /path/to/a/built/lldb-vscode
+```
+
+This is handy if you want to debug and develope the `lldb-vscode` executable when adding features or fixing bugs.
+
+# Configurations
+
+Launching to attaching require you to create a [launch configuration](https://code.visualstudio.com/Docs/editor/debugging#_launch-configurations). This file
+defines arguments that get passed to `lldb-vscode` and the configuration settings
+control how the launch or attach happens.
+
+## Launch Configuration Settings
+
+When you launch a program with Visual Studio Code you will need to create a [launch.json](https://code.visualstudio.com/Docs/editor/debugging#_launch-configurations)
+file that defines how your program will be run. The JSON configuration file can contain the following `lldb-vscode` specific launch key/value pairs:
+
+|parameter |type|req | |
+|-------------------|----|:--:|---------|
+|**name** |string|Y| A configuration name that will be displayed in the IDE.
+|**type** |string|Y| Must be "lldb-vscode".
+|**request** |string|Y| Must be "launch".
+|**program** |string|Y| Path to the executable to launch.
+|**args** |[string]|| An array of command line argument strings to be passed to the program being launched.
+|**cwd** |string| | The program working directory.
+|**env** |dictionary| | Environment variables to set when launching the program. The format of each environment variable string is "VAR=VALUE" for environment variables with values or just "VAR" for environment variables with no values.
+|**stopOnEntry** |boolean| | Whether to stop program immediately after launching.
+|**initCommands** |[string]| | LLDB commands executed upon debugger startup prior to creating the LLDB target. Commands and command output will be sent to the debugger console when they are executed.
+|**preRunCommands** |[string]| | LLDB commands executed just before launching after the LLDB target has been created. Commands and command output will be sent to the debugger console when they are executed.
+|**stopCommands** |[string]| | LLDB commands executed just after each stop. Commands and command output will be sent to the debugger console when they are executed.
+|**exitCommands** |[string]| | LLDB commands executed when the program exits. Commands and command output will be sent to the debugger console when they are executed.
+|**sourceMap** |[string[2]]| | Specify an array of path re-mappings. Each element in the array must be a two element array containing a source and destination pathname.
+|**debuggerRoot** | string| |Specify a working directory to use when launching lldb-vscode. If the debug information in your executable contains relative paths, this option can be used so that `lldb-vscode` can find source files and object files that have relative paths.
+
+## Attaching Settings
+
+When attaching to a process using LLDB you can attach in a few ways
+
+1. Attach to an existing process using the process ID
+2. Attach to an existing process by name
+3. Attach by name by waiting for the next instance of a process to launch
+
+The JSON configuration file can contain the following `lldb-vscode` specific launch key/value pairs:
+
+|parameter |type |req | |
+|-------------------|--------|:--:|---------|
+|**name** |string |Y| A configuration name that will be displayed in the IDE.
+|**type** |string |Y| Must be "lldb-vscode".
+|**request** |string |Y| Must be "attach".
+|**program** |string | | Path to the executable to attach to. This value is optional but can help to resolve breakpoints prior the attaching to the program.
+|**pid** |number | | The process id of the process you wish to attach to. If **pid** is omitted, the debugger will attempt to attach to the program by finding a process whose file name matches the file name from **porgram**. Setting this value to `${command:pickMyProcess}` will allow interactive process selection in the IDE.
+|**stopOnEntry** |boolean| | Whether to stop program immediately after launching.
+|**waitFor** |boolean | | Wait for the process to launch.
+|**initCommands** |[string]| | LLDB commands executed upon debugger startup prior to creating the LLDB target. Commands and command output will be sent to the debugger console when they are executed.
+|**preRunCommands** |[string]| | LLDB commands executed just before launching after the LLDB target has been created. Commands and command output will be sent to the debugger console when they are executed.
+|**stopCommands** |[string]| | LLDB commands executed just after each stop. Commands and command output will be sent to the debugger console when they are executed.
+|**exitCommands** |[string]| | LLDB commands executed when the program exits. Commands and command output will be sent to the debugger console when they are executed.
+|**attachCommands** |[string]| | LLDB commands that will be executed after **preRunCommands** which take place of the code that normally does the attach. The commands can create a new target and attach or launch it however desired. This allows custom launch and attach configurations. Core files can use `target create --core /path/to/core` to attach to core files.
+
+
+## Example configurations
+
+### Launching
+
+This will launch `/tmp/a.out` with arguments `one`, `two`, and `three` and
+adds `FOO=1` and `bar` to the environment:
+
+```javascript
+{
+ "type": "lldb-vscode",
+ "request": "launch",
+ "name": "Debug",
+ "program": "/tmp/a.out",
+ "args": [ "one", "two", "three" ],
+ "env": [ "FOO=1", "BAR" ],
+}
+```
+
+### Attach using PID
+
+This will attach to a process `a.out` whose process ID is 123:
+
+```javascript
+{
+ "type": "lldb-vscode",
+ "request": "attach",
+ "name": "Attach to PID",
+ "program": "/tmp/a.out",
+ "pid": 123
+}
+```
+
+### Attach by Name
+
+This will attach to an existing process whose base
+name matches `a.out`. All we have to do is leave the `pid` value out of the
+above configuration:
+
+```javascript
+{
+ "name": "Attach to Name",
+ "type": "lldb-vscode",
+ "request": "attach",
+ "program": "/tmp/a.out",
+}
+```
+
+If you want to ignore any existing a.out processes and wait for the next instance
+to be launched you can add the "waitFor" key value pair:
+
+```javascript
+{
+ "name": "Attach to Name (wait)",
+ "type": "lldb-vscode",
+ "request": "attach",
+ "program": "/tmp/a.out",
+ "waitFor": true
+}
+```
+
+This will work as long as the architecture, vendor and OS supports waiting
+for processes. Currently MacOS is the only platform that supports this.
+
+
+### Loading a Core File
+
+Loading a core file can use the `"attach"` request along with the
+`"attachCommands"` to implement a custom attach:
+
+```javascript
+{
+ "name": "Attach to Name (wait)",
+ "type": "lldb-vscode",
+ "request": "attach",
+ "attachCommands": ["target create -c /path/to/123.core /path/to/executable"],
+ "stopOnEntry": false
+}
+```
diff --git a/gnu/llvm/lldb/tools/lldb-vscode/SourceBreakpoint.cpp b/gnu/llvm/lldb/tools/lldb-vscode/SourceBreakpoint.cpp
new file mode 100644
index 00000000000..91d1ad70ecd
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-vscode/SourceBreakpoint.cpp
@@ -0,0 +1,26 @@
+//===-- SourceBreakpoint.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SourceBreakpoint.h"
+#include "VSCode.h"
+
+namespace lldb_vscode {
+
+SourceBreakpoint::SourceBreakpoint(const llvm::json::Object &obj)
+ : BreakpointBase(obj), line(GetUnsigned(obj, "line", 0)),
+ column(GetUnsigned(obj, "column", 0)) {}
+
+void SourceBreakpoint::SetBreakpoint(const llvm::StringRef source_path) {
+ bp = g_vsc.target.BreakpointCreateByLocation(source_path.str().c_str(), line);
+ if (!condition.empty())
+ SetCondition();
+ if (!hitCondition.empty())
+ SetHitCondition();
+}
+
+} // namespace lldb_vscode
diff --git a/gnu/llvm/lldb/tools/lldb-vscode/SourceBreakpoint.h b/gnu/llvm/lldb/tools/lldb-vscode/SourceBreakpoint.h
new file mode 100644
index 00000000000..f17557ef1f8
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-vscode/SourceBreakpoint.h
@@ -0,0 +1,38 @@
+//===-- SourceBreakpoint.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDBVSCODE_SOURCEBREAKPOINT_H_
+#define LLDBVSCODE_SOURCEBREAKPOINT_H_
+
+#include "llvm/ADT/StringRef.h"
+#include "BreakpointBase.h"
+
+namespace lldb_vscode {
+
+struct SourceBreakpoint : public BreakpointBase {
+
+ uint32_t line; ///< The source line of the breakpoint or logpoint
+ uint32_t column; ///< An optional source column of the breakpoint
+
+ SourceBreakpoint() : BreakpointBase(), line(0), column(0) {}
+ SourceBreakpoint(const llvm::json::Object &obj);
+
+ // Set this breakpoint in LLDB as a new breakpoint
+ void SetBreakpoint(const llvm::StringRef source_path);
+};
+
+inline bool operator<(const SourceBreakpoint &lhs,
+ const SourceBreakpoint &rhs) {
+ if (lhs.line == rhs.line)
+ return lhs.column < rhs.column;
+ return lhs.line < rhs.line;
+}
+
+} // namespace lldb_vscode
+
+#endif
diff --git a/gnu/llvm/lldb/tools/lldb-vscode/SourceReference.h b/gnu/llvm/lldb/tools/lldb-vscode/SourceReference.h
new file mode 100644
index 00000000000..55f5b9b9c40
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-vscode/SourceReference.h
@@ -0,0 +1,32 @@
+//===-- SourceReference.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDBVSCODE_SOURCEREFERENCE_H_
+#define LLDBVSCODE_SOURCEREFERENCE_H_
+
+#include "lldb/lldb-types.h"
+#include "llvm/ADT/DenseMap.h"
+#include <string>
+
+namespace lldb_vscode {
+
+struct SourceReference {
+ std::string content;
+ llvm::DenseMap<lldb::addr_t, int64_t> addr_to_line;
+
+ int64_t GetLineForPC(lldb::addr_t pc) const {
+ auto addr_line = addr_to_line.find(pc);
+ if (addr_line != addr_to_line.end())
+ return addr_line->second;
+ return 0;
+ }
+};
+
+} // namespace lldb_vscode
+
+#endif
diff --git a/gnu/llvm/lldb/tools/lldb-vscode/VSCode.cpp b/gnu/llvm/lldb/tools/lldb-vscode/VSCode.cpp
new file mode 100644
index 00000000000..2f85627da4b
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-vscode/VSCode.cpp
@@ -0,0 +1,306 @@
+//===-- VSCode.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdarg.h>
+#include <fstream>
+#include <mutex>
+
+#include "LLDBUtils.h"
+#include "VSCode.h"
+#include "llvm/Support/FormatVariadic.h"
+
+#if defined(_WIN32)
+#define NOMINMAX
+#include <windows.h>
+#include <fcntl.h>
+#include <io.h>
+#endif
+
+using namespace lldb_vscode;
+
+namespace lldb_vscode {
+
+VSCode g_vsc;
+
+VSCode::VSCode()
+ : launch_info(nullptr), variables(), broadcaster("lldb-vscode"),
+ num_regs(0), num_locals(0), num_globals(0), log(),
+ exception_breakpoints(
+ {{"cpp_catch", "C++ Catch", lldb::eLanguageTypeC_plus_plus},
+ {"cpp_throw", "C++ Throw", lldb::eLanguageTypeC_plus_plus},
+ {"objc_catch", "Objective C Catch", lldb::eLanguageTypeObjC},
+ {"objc_throw", "Objective C Throw", lldb::eLanguageTypeObjC},
+ {"swift_catch", "Swift Catch", lldb::eLanguageTypeSwift},
+ {"swift_throw", "Swift Throw", lldb::eLanguageTypeSwift}}),
+ focus_tid(LLDB_INVALID_THREAD_ID), sent_terminated_event(false),
+ stop_at_entry(false) {
+ const char *log_file_path = getenv("LLDBVSCODE_LOG");
+#if defined(_WIN32)
+// Windows opens stdout and stdin in text mode which converts \n to 13,10
+// while the value is just 10 on Darwin/Linux. Setting the file mode to binary
+// fixes this.
+ int result = _setmode(fileno(stdout), _O_BINARY);
+ assert(result);
+ result = _setmode(fileno(stdin), _O_BINARY);
+ (void)result;
+ assert(result);
+#endif
+ if (log_file_path)
+ log.reset(new std::ofstream(log_file_path));
+}
+
+VSCode::~VSCode() {
+}
+
+int64_t VSCode::GetLineForPC(int64_t sourceReference, lldb::addr_t pc) const {
+ auto pos = source_map.find(sourceReference);
+ if (pos != source_map.end())
+ return pos->second.GetLineForPC(pc);
+ return 0;
+}
+
+ExceptionBreakpoint *VSCode::GetExceptionBreakpoint(const std::string &filter) {
+ for (auto &bp : exception_breakpoints) {
+ if (bp.filter == filter)
+ return &bp;
+ }
+ return nullptr;
+}
+
+ExceptionBreakpoint *
+VSCode::GetExceptionBreakpoint(const lldb::break_id_t bp_id) {
+ for (auto &bp : exception_breakpoints) {
+ if (bp.bp.GetID() == bp_id)
+ return &bp;
+ }
+ return nullptr;
+}
+
+// Send the JSON in "json_str" to the "out" stream. Correctly send the
+// "Content-Length:" field followed by the length, followed by the raw
+// JSON bytes.
+void VSCode::SendJSON(const std::string &json_str) {
+ output.write_full("Content-Length: ");
+ output.write_full(llvm::utostr(json_str.size()));
+ output.write_full("\r\n\r\n");
+ output.write_full(json_str);
+
+ if (log) {
+ *log << "<-- " << std::endl
+ << "Content-Length: " << json_str.size() << "\r\n\r\n"
+ << json_str << std::endl;
+ }
+}
+
+// Serialize the JSON value into a string and send the JSON packet to
+// the "out" stream.
+void VSCode::SendJSON(const llvm::json::Value &json) {
+ std::string s;
+ llvm::raw_string_ostream strm(s);
+ strm << json;
+ static std::mutex mutex;
+ std::lock_guard<std::mutex> locker(mutex);
+ SendJSON(strm.str());
+}
+
+// Read a JSON packet from the "in" stream.
+std::string VSCode::ReadJSON() {
+ std::string length_str;
+ std::string json_str;
+ int length;
+
+ if (!input.read_expected(log.get(), "Content-Length: "))
+ return json_str;
+
+ if (!input.read_line(log.get(), length_str))
+ return json_str;
+
+ if (!llvm::to_integer(length_str, length))
+ return json_str;
+
+ if (!input.read_expected(log.get(), "\r\n"))
+ return json_str;
+
+ if (!input.read_full(log.get(), length, json_str))
+ return json_str;
+
+ return json_str;
+}
+
+// "OutputEvent": {
+// "allOf": [ { "$ref": "#/definitions/Event" }, {
+// "type": "object",
+// "description": "Event message for 'output' event type. The event
+// indicates that the target has produced some output.",
+// "properties": {
+// "event": {
+// "type": "string",
+// "enum": [ "output" ]
+// },
+// "body": {
+// "type": "object",
+// "properties": {
+// "category": {
+// "type": "string",
+// "description": "The output category. If not specified,
+// 'console' is assumed.",
+// "_enum": [ "console", "stdout", "stderr", "telemetry" ]
+// },
+// "output": {
+// "type": "string",
+// "description": "The output to report."
+// },
+// "variablesReference": {
+// "type": "number",
+// "description": "If an attribute 'variablesReference' exists
+// and its value is > 0, the output contains
+// objects which can be retrieved by passing
+// variablesReference to the VariablesRequest."
+// },
+// "source": {
+// "$ref": "#/definitions/Source",
+// "description": "An optional source location where the output
+// was produced."
+// },
+// "line": {
+// "type": "integer",
+// "description": "An optional source location line where the
+// output was produced."
+// },
+// "column": {
+// "type": "integer",
+// "description": "An optional source location column where the
+// output was produced."
+// },
+// "data": {
+// "type":["array","boolean","integer","null","number","object",
+// "string"],
+// "description": "Optional data to report. For the 'telemetry'
+// category the data will be sent to telemetry, for
+// the other categories the data is shown in JSON
+// format."
+// }
+// },
+// "required": ["output"]
+// }
+// },
+// "required": [ "event", "body" ]
+// }]
+// }
+void VSCode::SendOutput(OutputType o, const llvm::StringRef output) {
+ if (output.empty())
+ return;
+
+ llvm::json::Object event(CreateEventObject("output"));
+ llvm::json::Object body;
+ const char *category = nullptr;
+ switch (o) {
+ case OutputType::Console:
+ category = "console";
+ break;
+ case OutputType::Stdout:
+ category = "stdout";
+ break;
+ case OutputType::Stderr:
+ category = "stderr";
+ break;
+ case OutputType::Telemetry:
+ category = "telemetry";
+ break;
+ }
+ body.try_emplace("category", category);
+ EmplaceSafeString(body, "output", output.str());
+ event.try_emplace("body", std::move(body));
+ SendJSON(llvm::json::Value(std::move(event)));
+}
+
+void __attribute__((format(printf, 3, 4)))
+VSCode::SendFormattedOutput(OutputType o, const char *format, ...) {
+ char buffer[1024];
+ va_list args;
+ va_start(args, format);
+ int actual_length = vsnprintf(buffer, sizeof(buffer), format, args);
+ va_end(args);
+ SendOutput(o, llvm::StringRef(buffer,
+ std::min<int>(actual_length, sizeof(buffer))));
+}
+
+int64_t VSCode::GetNextSourceReference() {
+ static int64_t ref = 0;
+ return ++ref;
+}
+
+ExceptionBreakpoint *
+VSCode::GetExceptionBPFromStopReason(lldb::SBThread &thread) {
+ const auto num = thread.GetStopReasonDataCount();
+ // Check to see if have hit an exception breakpoint and change the
+ // reason to "exception", but only do so if all breakpoints that were
+ // hit are exception breakpoints.
+ ExceptionBreakpoint *exc_bp = nullptr;
+ for (size_t i = 0; i < num; i += 2) {
+ // thread.GetStopReasonDataAtIndex(i) will return the bp ID and
+ // thread.GetStopReasonDataAtIndex(i+1) will return the location
+ // within that breakpoint. We only care about the bp ID so we can
+ // see if this is an exception breakpoint that is getting hit.
+ lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(i);
+ exc_bp = GetExceptionBreakpoint(bp_id);
+ // If any breakpoint is not an exception breakpoint, then stop and
+ // report this as a normal breakpoint
+ if (exc_bp == nullptr)
+ return nullptr;
+ }
+ return exc_bp;
+}
+
+lldb::SBThread VSCode::GetLLDBThread(const llvm::json::Object &arguments) {
+ auto tid = GetSigned(arguments, "threadId", LLDB_INVALID_THREAD_ID);
+ return target.GetProcess().GetThreadByID(tid);
+}
+
+lldb::SBFrame VSCode::GetLLDBFrame(const llvm::json::Object &arguments) {
+ const uint64_t frame_id = GetUnsigned(arguments, "frameId", UINT64_MAX);
+ lldb::SBProcess process = target.GetProcess();
+ // Upper 32 bits is the thread index ID
+ lldb::SBThread thread =
+ process.GetThreadByIndexID(GetLLDBThreadIndexID(frame_id));
+ // Lower 32 bits is the frame index
+ return thread.GetFrameAtIndex(GetLLDBFrameID(frame_id));
+}
+
+llvm::json::Value VSCode::CreateTopLevelScopes() {
+ llvm::json::Array scopes;
+ scopes.emplace_back(CreateScope("Locals", VARREF_LOCALS, num_locals, false));
+ scopes.emplace_back(
+ CreateScope("Globals", VARREF_GLOBALS, num_globals, false));
+ scopes.emplace_back(CreateScope("Registers", VARREF_REGS, num_regs, false));
+ return llvm::json::Value(std::move(scopes));
+}
+
+void VSCode::RunLLDBCommands(llvm::StringRef prefix,
+ const std::vector<std::string> &commands) {
+ SendOutput(OutputType::Console,
+ llvm::StringRef(::RunLLDBCommands(prefix, commands)));
+}
+
+void VSCode::RunInitCommands() {
+ RunLLDBCommands("Running initCommands:", init_commands);
+}
+
+void VSCode::RunPreRunCommands() {
+ RunLLDBCommands("Running preRunCommands:", pre_run_commands);
+}
+
+void VSCode::RunStopCommands() {
+ RunLLDBCommands("Running stopCommands:", stop_commands);
+}
+
+void VSCode::RunExitCommands() {
+ RunLLDBCommands("Running exitCommands:", exit_commands);
+}
+
+} // namespace lldb_vscode
diff --git a/gnu/llvm/lldb/tools/lldb-vscode/VSCode.h b/gnu/llvm/lldb/tools/lldb-vscode/VSCode.h
new file mode 100644
index 00000000000..be8b22806a4
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-vscode/VSCode.h
@@ -0,0 +1,141 @@
+//===-- VSCode.h ------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDBVSCODE_VSCODE_H_
+#define LLDBVSCODE_VSCODE_H_
+
+#include <iosfwd>
+#include <map>
+#include <set>
+#include <stdio.h>
+#include <thread>
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include "lldb/API/SBAttachInfo.h"
+#include "lldb/API/SBBreakpoint.h"
+#include "lldb/API/SBBreakpointLocation.h"
+#include "lldb/API/SBCommandInterpreter.h"
+#include "lldb/API/SBCommandReturnObject.h"
+#include "lldb/API/SBCommunication.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBEvent.h"
+#include "lldb/API/SBHostOS.h"
+#include "lldb/API/SBInstruction.h"
+#include "lldb/API/SBInstructionList.h"
+#include "lldb/API/SBLanguageRuntime.h"
+#include "lldb/API/SBLaunchInfo.h"
+#include "lldb/API/SBLineEntry.h"
+#include "lldb/API/SBListener.h"
+#include "lldb/API/SBProcess.h"
+#include "lldb/API/SBStream.h"
+#include "lldb/API/SBStringList.h"
+#include "lldb/API/SBTarget.h"
+#include "lldb/API/SBThread.h"
+
+#include "ExceptionBreakpoint.h"
+#include "FunctionBreakpoint.h"
+#include "IOStream.h"
+#include "SourceBreakpoint.h"
+#include "SourceReference.h"
+
+#define VARREF_LOCALS (int64_t)1
+#define VARREF_GLOBALS (int64_t)2
+#define VARREF_REGS (int64_t)3
+#define VARREF_FIRST_VAR_IDX (int64_t)4
+#define VARREF_IS_SCOPE(v) (VARREF_LOCALS <= 1 && v < VARREF_FIRST_VAR_IDX)
+#define VARIDX_TO_VARREF(i) ((i) + VARREF_FIRST_VAR_IDX)
+#define VARREF_TO_VARIDX(v) ((v)-VARREF_FIRST_VAR_IDX)
+#define NO_TYPENAME "<no-type>"
+
+namespace lldb_vscode {
+
+typedef llvm::DenseMap<uint32_t, SourceBreakpoint> SourceBreakpointMap;
+typedef llvm::StringMap<FunctionBreakpoint> FunctionBreakpointMap;
+enum class OutputType { Console, Stdout, Stderr, Telemetry };
+
+struct VSCode {
+ InputStream input;
+ OutputStream output;
+ lldb::SBDebugger debugger;
+ lldb::SBTarget target;
+ lldb::SBAttachInfo attach_info;
+ lldb::SBLaunchInfo launch_info;
+ lldb::SBValueList variables;
+ lldb::SBBroadcaster broadcaster;
+ int64_t num_regs;
+ int64_t num_locals;
+ int64_t num_globals;
+ std::thread event_thread;
+ std::unique_ptr<std::ofstream> log;
+ llvm::DenseMap<lldb::addr_t, int64_t> addr_to_source_ref;
+ llvm::DenseMap<int64_t, SourceReference> source_map;
+ llvm::StringMap<SourceBreakpointMap> source_breakpoints;
+ FunctionBreakpointMap function_breakpoints;
+ std::vector<ExceptionBreakpoint> exception_breakpoints;
+ std::vector<std::string> init_commands;
+ std::vector<std::string> pre_run_commands;
+ std::vector<std::string> exit_commands;
+ std::vector<std::string> stop_commands;
+ lldb::tid_t focus_tid;
+ bool sent_terminated_event;
+ bool stop_at_entry;
+ // Keep track of the last stop thread index IDs as threads won't go away
+ // unless we send a "thread" event to indicate the thread exited.
+ llvm::DenseSet<lldb::tid_t> thread_ids;
+ VSCode();
+ ~VSCode();
+ VSCode(const VSCode &rhs) = delete;
+ void operator=(const VSCode &rhs) = delete;
+ int64_t GetLineForPC(int64_t sourceReference, lldb::addr_t pc) const;
+ ExceptionBreakpoint *GetExceptionBreakpoint(const std::string &filter);
+ ExceptionBreakpoint *GetExceptionBreakpoint(const lldb::break_id_t bp_id);
+ // Send the JSON in "json_str" to the "out" stream. Correctly send the
+ // "Content-Length:" field followed by the length, followed by the raw
+ // JSON bytes.
+ void SendJSON(const std::string &json_str);
+
+ // Serialize the JSON value into a string and send the JSON packet to
+ // the "out" stream.
+ void SendJSON(const llvm::json::Value &json);
+
+ std::string ReadJSON();
+
+ void SendOutput(OutputType o, const llvm::StringRef output);
+
+ void __attribute__((format(printf, 3, 4)))
+ SendFormattedOutput(OutputType o, const char *format, ...);
+
+ static int64_t GetNextSourceReference();
+
+ ExceptionBreakpoint *GetExceptionBPFromStopReason(lldb::SBThread &thread);
+
+ lldb::SBThread GetLLDBThread(const llvm::json::Object &arguments);
+
+ lldb::SBFrame GetLLDBFrame(const llvm::json::Object &arguments);
+
+ llvm::json::Value CreateTopLevelScopes();
+
+ void RunLLDBCommands(llvm::StringRef prefix,
+ const std::vector<std::string> &commands);
+
+ void RunInitCommands();
+ void RunPreRunCommands();
+ void RunStopCommands();
+ void RunExitCommands();
+};
+
+extern VSCode g_vsc;
+
+} // namespace lldb_vscode
+
+#endif
diff --git a/gnu/llvm/lldb/tools/lldb-vscode/VSCodeForward.h b/gnu/llvm/lldb/tools/lldb-vscode/VSCodeForward.h
new file mode 100644
index 00000000000..4a89120d1ec
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-vscode/VSCodeForward.h
@@ -0,0 +1,46 @@
+//===-- VSCodeForward.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDBVSCODE_VSCODEFORWARD_H_
+#define LLDBVSCODE_VSCODEFORWARD_H_
+
+
+namespace lldb_vscode {
+struct BreakpointBase;
+struct ExceptionBreakpoint;
+struct FunctionBreakpoint;
+struct SourceBreakpoint;
+struct SourceReference;
+} // namespace lldb_vscode
+
+namespace lldb {
+class SBAttachInfo;
+class SBBreakpoint;
+class SBBreakpointLocation;
+class SBCommandInterpreter;
+class SBCommandReturnObject;
+class SBCommunication;
+class SBDebugger;
+class SBEvent;
+class SBFrame;
+class SBHostOS;
+class SBInstruction;
+class SBInstructionList;
+class SBLanguageRuntime;
+class SBLaunchInfo;
+class SBLineEntry;
+class SBListener;
+class SBProcess;
+class SBStream;
+class SBStringList;
+class SBTarget;
+class SBThread;
+class SBValue;
+} // namespace lldb
+
+#endif
diff --git a/gnu/llvm/lldb/tools/lldb-vscode/lldb-vscode-Info.plist.in b/gnu/llvm/lldb/tools/lldb-vscode/lldb-vscode-Info.plist.in
new file mode 100644
index 00000000000..2098e190d6b
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-vscode/lldb-vscode-Info.plist.in
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.apple.lldb-vscode</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>lldb-vscode</string>
+ <key>CFBundleVersion</key>
+ <string>${LLDB_VERSION}</string>
+ <key>SecTaskAccess</key>
+ <array>
+ <string>allowed</string>
+ <string>debug</string>
+ </array>
+</dict>
+</plist>
diff --git a/gnu/llvm/lldb/tools/lldb-vscode/lldb-vscode.cpp b/gnu/llvm/lldb/tools/lldb-vscode/lldb-vscode.cpp
new file mode 100644
index 00000000000..6d638a6a8f8
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-vscode/lldb-vscode.cpp
@@ -0,0 +1,2817 @@
+//===-- lldb-vscode.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <assert.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#if defined(_WIN32)
+// We need to #define NOMINMAX in order to skip `min()` and `max()` macro
+// definitions that conflict with other system headers.
+// We also need to #undef GetObject (which is defined to GetObjectW) because
+// the JSON code we use also has methods named `GetObject()` and we conflict
+// against these.
+#define NOMINMAX
+#include <windows.h>
+#undef GetObject
+#include <io.h>
+#else
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#endif
+
+#include <algorithm>
+#include <chrono>
+#include <fstream>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <set>
+#include <sstream>
+#include <thread>
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Support/Errno.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include "JSONUtils.h"
+#include "LLDBUtils.h"
+#include "VSCode.h"
+
+#if defined(_WIN32)
+#ifndef PATH_MAX
+#define PATH_MAX MAX_PATH
+#endif
+typedef int socklen_t;
+constexpr const char *dev_null_path = "nul";
+
+#else
+constexpr const char *dev_null_path = "/dev/null";
+
+#endif
+
+using namespace lldb_vscode;
+
+namespace {
+
+typedef void (*RequestCallback)(const llvm::json::Object &command);
+
+enum LaunchMethod { Launch, Attach, AttachForSuspendedLaunch };
+
+enum VSCodeBroadcasterBits { eBroadcastBitStopEventThread = 1u << 0 };
+
+SOCKET AcceptConnection(int portno) {
+ // Accept a socket connection from any host on "portno".
+ SOCKET newsockfd = -1;
+ struct sockaddr_in serv_addr, cli_addr;
+ SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sockfd < 0) {
+ if (g_vsc.log)
+ *g_vsc.log << "error: opening socket (" << strerror(errno) << ")"
+ << std::endl;
+ } else {
+ memset((char *)&serv_addr, 0, sizeof(serv_addr));
+ serv_addr.sin_family = AF_INET;
+ // serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ serv_addr.sin_port = htons(portno);
+ if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
+ if (g_vsc.log)
+ *g_vsc.log << "error: binding socket (" << strerror(errno) << ")"
+ << std::endl;
+ } else {
+ listen(sockfd, 5);
+ socklen_t clilen = sizeof(cli_addr);
+ newsockfd =
+ llvm::sys::RetryAfterSignal(static_cast<SOCKET>(-1), accept, sockfd,
+ (struct sockaddr *)&cli_addr, &clilen);
+ if (newsockfd < 0)
+ if (g_vsc.log)
+ *g_vsc.log << "error: accept (" << strerror(errno) << ")"
+ << std::endl;
+ }
+#if defined(_WIN32)
+ closesocket(sockfd);
+#else
+ close(sockfd);
+#endif
+ }
+ return newsockfd;
+}
+
+std::vector<const char *> MakeArgv(const llvm::ArrayRef<std::string> &strs) {
+ // Create and return an array of "const char *", one for each C string in
+ // "strs" and terminate the list with a NULL. This can be used for argument
+ // vectors (argv) or environment vectors (envp) like those passed to the
+ // "main" function in C programs.
+ std::vector<const char *> argv;
+ for (const auto &s : strs)
+ argv.push_back(s.c_str());
+ argv.push_back(nullptr);
+ return argv;
+}
+
+// Send a "exited" event to indicate the process has exited.
+void SendProcessExitedEvent(lldb::SBProcess &process) {
+ llvm::json::Object event(CreateEventObject("exited"));
+ llvm::json::Object body;
+ body.try_emplace("exitCode", (int64_t)process.GetExitStatus());
+ event.try_emplace("body", std::move(body));
+ g_vsc.SendJSON(llvm::json::Value(std::move(event)));
+}
+
+void SendThreadExitedEvent(lldb::tid_t tid) {
+ llvm::json::Object event(CreateEventObject("thread"));
+ llvm::json::Object body;
+ body.try_emplace("reason", "exited");
+ body.try_emplace("threadId", (int64_t)tid);
+ event.try_emplace("body", std::move(body));
+ g_vsc.SendJSON(llvm::json::Value(std::move(event)));
+}
+
+// Send a "terminated" event to indicate the process is done being
+// debugged.
+void SendTerminatedEvent() {
+ if (!g_vsc.sent_terminated_event) {
+ g_vsc.sent_terminated_event = true;
+ // Send a "terminated" event
+ llvm::json::Object event(CreateEventObject("terminated"));
+ g_vsc.SendJSON(llvm::json::Value(std::move(event)));
+ }
+}
+
+// Send a thread stopped event for all threads as long as the process
+// is stopped.
+void SendThreadStoppedEvent() {
+ lldb::SBProcess process = g_vsc.target.GetProcess();
+ if (process.IsValid()) {
+ auto state = process.GetState();
+ if (state == lldb::eStateStopped) {
+ llvm::DenseSet<lldb::tid_t> old_thread_ids;
+ old_thread_ids.swap(g_vsc.thread_ids);
+ uint32_t stop_id = process.GetStopID();
+ const uint32_t num_threads = process.GetNumThreads();
+
+ // First make a pass through the threads to see if the focused thread
+ // has a stop reason. In case the focus thread doesn't have a stop
+ // reason, remember the first thread that has a stop reason so we can
+ // set it as the focus thread if below if needed.
+ lldb::tid_t first_tid_with_reason = LLDB_INVALID_THREAD_ID;
+ uint32_t num_threads_with_reason = 0;
+ for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
+ lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
+ const lldb::tid_t tid = thread.GetThreadID();
+ const bool has_reason = ThreadHasStopReason(thread);
+ // If the focus thread doesn't have a stop reason, clear the thread ID
+ if (tid == g_vsc.focus_tid && !has_reason)
+ g_vsc.focus_tid = LLDB_INVALID_THREAD_ID;
+ if (has_reason) {
+ ++num_threads_with_reason;
+ if (first_tid_with_reason == LLDB_INVALID_THREAD_ID)
+ first_tid_with_reason = tid;
+ }
+ }
+
+ // We will have cleared g_vsc.focus_tid if he focus thread doesn't
+ // have a stop reason, so if it was cleared, or wasn't set, then set the
+ // focus thread to the first thread with a stop reason.
+ if (g_vsc.focus_tid == LLDB_INVALID_THREAD_ID)
+ g_vsc.focus_tid = first_tid_with_reason;
+
+ // If no threads stopped with a reason, then report the first one so
+ // we at least let the UI know we stopped.
+ if (num_threads_with_reason == 0) {
+ lldb::SBThread thread = process.GetThreadAtIndex(0);
+ g_vsc.SendJSON(CreateThreadStopped(thread, stop_id));
+ } else {
+ for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
+ lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
+ g_vsc.thread_ids.insert(thread.GetThreadID());
+ if (ThreadHasStopReason(thread)) {
+ g_vsc.SendJSON(CreateThreadStopped(thread, stop_id));
+ }
+ }
+ }
+
+ for (auto tid : old_thread_ids) {
+ auto end = g_vsc.thread_ids.end();
+ auto pos = g_vsc.thread_ids.find(tid);
+ if (pos == end)
+ SendThreadExitedEvent(tid);
+ }
+ } else {
+ if (g_vsc.log)
+ *g_vsc.log << "error: SendThreadStoppedEvent() when process"
+ " isn't stopped ("
+ << lldb::SBDebugger::StateAsCString(state) << ')'
+ << std::endl;
+ }
+ } else {
+ if (g_vsc.log)
+ *g_vsc.log << "error: SendThreadStoppedEvent() invalid process"
+ << std::endl;
+ }
+ g_vsc.RunStopCommands();
+}
+
+// "ProcessEvent": {
+// "allOf": [
+// { "$ref": "#/definitions/Event" },
+// {
+// "type": "object",
+// "description": "Event message for 'process' event type. The event
+// indicates that the debugger has begun debugging a
+// new process. Either one that it has launched, or one
+// that it has attached to.",
+// "properties": {
+// "event": {
+// "type": "string",
+// "enum": [ "process" ]
+// },
+// "body": {
+// "type": "object",
+// "properties": {
+// "name": {
+// "type": "string",
+// "description": "The logical name of the process. This is
+// usually the full path to process's executable
+// file. Example: /home/myproj/program.js."
+// },
+// "systemProcessId": {
+// "type": "integer",
+// "description": "The system process id of the debugged process.
+// This property will be missing for non-system
+// processes."
+// },
+// "isLocalProcess": {
+// "type": "boolean",
+// "description": "If true, the process is running on the same
+// computer as the debug adapter."
+// },
+// "startMethod": {
+// "type": "string",
+// "enum": [ "launch", "attach", "attachForSuspendedLaunch" ],
+// "description": "Describes how the debug engine started
+// debugging this process.",
+// "enumDescriptions": [
+// "Process was launched under the debugger.",
+// "Debugger attached to an existing process.",
+// "A project launcher component has launched a new process in
+// a suspended state and then asked the debugger to attach."
+// ]
+// }
+// },
+// "required": [ "name" ]
+// }
+// },
+// "required": [ "event", "body" ]
+// }
+// ]
+// }
+void SendProcessEvent(LaunchMethod launch_method) {
+ lldb::SBFileSpec exe_fspec = g_vsc.target.GetExecutable();
+ char exe_path[PATH_MAX];
+ exe_fspec.GetPath(exe_path, sizeof(exe_path));
+ llvm::json::Object event(CreateEventObject("process"));
+ llvm::json::Object body;
+ EmplaceSafeString(body, "name", std::string(exe_path));
+ const auto pid = g_vsc.target.GetProcess().GetProcessID();
+ body.try_emplace("systemProcessId", (int64_t)pid);
+ body.try_emplace("isLocalProcess", true);
+ const char *startMethod = nullptr;
+ switch (launch_method) {
+ case Launch:
+ startMethod = "launch";
+ break;
+ case Attach:
+ startMethod = "attach";
+ break;
+ case AttachForSuspendedLaunch:
+ startMethod = "attachForSuspendedLaunch";
+ break;
+ }
+ body.try_emplace("startMethod", startMethod);
+ event.try_emplace("body", std::move(body));
+ g_vsc.SendJSON(llvm::json::Value(std::move(event)));
+}
+
+// Grab any STDOUT and STDERR from the process and send it up to VS Code
+// via an "output" event to the "stdout" and "stderr" categories.
+void SendStdOutStdErr(lldb::SBProcess &process) {
+ char buffer[1024];
+ size_t count;
+ while ((count = process.GetSTDOUT(buffer, sizeof(buffer))) > 0)
+ g_vsc.SendOutput(OutputType::Stdout, llvm::StringRef(buffer, count));
+ while ((count = process.GetSTDERR(buffer, sizeof(buffer))) > 0)
+ g_vsc.SendOutput(OutputType::Stderr, llvm::StringRef(buffer, count));
+}
+
+// All events from the debugger, target, process, thread and frames are
+// received in this function that runs in its own thread. We are using a
+// "FILE *" to output packets back to VS Code and they have mutexes in them
+// them prevent multiple threads from writing simultaneously so no locking
+// is required.
+void EventThreadFunction() {
+ lldb::SBEvent event;
+ lldb::SBListener listener = g_vsc.debugger.GetListener();
+ bool done = false;
+ while (!done) {
+ if (listener.WaitForEvent(1, event)) {
+ const auto event_mask = event.GetType();
+ if (lldb::SBProcess::EventIsProcessEvent(event)) {
+ lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event);
+ if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) {
+ auto state = lldb::SBProcess::GetStateFromEvent(event);
+ switch (state) {
+ case lldb::eStateInvalid:
+ // Not a state event
+ break;
+ case lldb::eStateUnloaded:
+ break;
+ case lldb::eStateConnected:
+ break;
+ case lldb::eStateAttaching:
+ break;
+ case lldb::eStateLaunching:
+ break;
+ case lldb::eStateStepping:
+ break;
+ case lldb::eStateCrashed:
+ break;
+ case lldb::eStateDetached:
+ break;
+ case lldb::eStateSuspended:
+ break;
+ case lldb::eStateStopped:
+ // Only report a stopped event if the process was not restarted.
+ if (!lldb::SBProcess::GetRestartedFromEvent(event)) {
+ SendStdOutStdErr(process);
+ SendThreadStoppedEvent();
+ }
+ break;
+ case lldb::eStateRunning:
+ break;
+ case lldb::eStateExited: {
+ // Run any exit LLDB commands the user specified in the
+ // launch.json
+ g_vsc.RunExitCommands();
+ SendProcessExitedEvent(process);
+ SendTerminatedEvent();
+ done = true;
+ } break;
+ }
+ } else if ((event_mask & lldb::SBProcess::eBroadcastBitSTDOUT) ||
+ (event_mask & lldb::SBProcess::eBroadcastBitSTDERR)) {
+ SendStdOutStdErr(process);
+ }
+ } else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) {
+ if (event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged) {
+ auto event_type =
+ lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event);
+ const auto num_locs =
+ lldb::SBBreakpoint::GetNumBreakpointLocationsFromEvent(event);
+ auto bp = lldb::SBBreakpoint::GetBreakpointFromEvent(event);
+ bool added = event_type & lldb::eBreakpointEventTypeLocationsAdded;
+ bool removed =
+ event_type & lldb::eBreakpointEventTypeLocationsRemoved;
+ if (added || removed) {
+ for (size_t i = 0; i < num_locs; ++i) {
+ auto bp_loc =
+ lldb::SBBreakpoint::GetBreakpointLocationAtIndexFromEvent(
+ event, i);
+ auto bp_event = CreateEventObject("breakpoint");
+ llvm::json::Object body;
+ body.try_emplace("breakpoint", CreateBreakpoint(bp_loc));
+ if (added)
+ body.try_emplace("reason", "new");
+ else
+ body.try_emplace("reason", "removed");
+ bp_event.try_emplace("body", std::move(body));
+ g_vsc.SendJSON(llvm::json::Value(std::move(bp_event)));
+ }
+ }
+ }
+ } else if (event.BroadcasterMatchesRef(g_vsc.broadcaster)) {
+ if (event_mask & eBroadcastBitStopEventThread) {
+ done = true;
+ }
+ }
+ }
+ }
+}
+
+// Both attach and launch take a either a sourcePath or sourceMap
+// argument (or neither), from which we need to set the target.source-map.
+void SetSourceMapFromArguments(const llvm::json::Object &arguments) {
+ const char *sourceMapHelp =
+ "source must be be an array of two-element arrays, "
+ "each containing a source and replacement path string.\n";
+
+ std::string sourceMapCommand;
+ llvm::raw_string_ostream strm(sourceMapCommand);
+ strm << "settings set target.source-map ";
+ auto sourcePath = GetString(arguments, "sourcePath");
+
+ // sourceMap is the new, more general form of sourcePath and overrides it.
+ auto sourceMap = arguments.getArray("sourceMap");
+ if (sourceMap) {
+ for (const auto &value : *sourceMap) {
+ auto mapping = value.getAsArray();
+ if (mapping == nullptr || mapping->size() != 2 ||
+ (*mapping)[0].kind() != llvm::json::Value::String ||
+ (*mapping)[1].kind() != llvm::json::Value::String) {
+ g_vsc.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp));
+ return;
+ }
+ auto mapFrom = GetAsString((*mapping)[0]);
+ auto mapTo = GetAsString((*mapping)[1]);
+ strm << "\"" << mapFrom << "\" \"" << mapTo << "\" ";
+ }
+ } else {
+ if (ObjectContainsKey(arguments, "sourceMap")) {
+ g_vsc.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp));
+ return;
+ }
+ if (sourcePath.empty())
+ return;
+ // Do any source remapping needed before we create our targets
+ strm << "\".\" \"" << sourcePath << "\"";
+ }
+ strm.flush();
+ if (!sourceMapCommand.empty()) {
+ g_vsc.RunLLDBCommands("Setting source map:", {sourceMapCommand});
+ }
+}
+
+// "AttachRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "Attach request; value of command field is 'attach'.",
+// "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "attach" ]
+// },
+// "arguments": {
+// "$ref": "#/definitions/AttachRequestArguments"
+// }
+// },
+// "required": [ "command", "arguments" ]
+// }]
+// },
+// "AttachRequestArguments": {
+// "type": "object",
+// "description": "Arguments for 'attach' request.\nThe attach request has no
+// standardized attributes."
+// },
+// "AttachResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'attach' request. This is just an
+// acknowledgement, so no body field is required."
+// }]
+// }
+void request_attach(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ lldb::SBError error;
+ FillResponse(request, response);
+ auto arguments = request.getObject("arguments");
+ const lldb::pid_t pid =
+ GetUnsigned(arguments, "pid", LLDB_INVALID_PROCESS_ID);
+ if (pid != LLDB_INVALID_PROCESS_ID)
+ g_vsc.attach_info.SetProcessID(pid);
+ const auto wait_for = GetBoolean(arguments, "waitFor", false);
+ g_vsc.attach_info.SetWaitForLaunch(wait_for, false /*async*/);
+ g_vsc.init_commands = GetStrings(arguments, "initCommands");
+ g_vsc.pre_run_commands = GetStrings(arguments, "preRunCommands");
+ g_vsc.stop_commands = GetStrings(arguments, "stopCommands");
+ g_vsc.exit_commands = GetStrings(arguments, "exitCommands");
+ auto attachCommands = GetStrings(arguments, "attachCommands");
+ g_vsc.stop_at_entry = GetBoolean(arguments, "stopOnEntry", false);
+ const auto debuggerRoot = GetString(arguments, "debuggerRoot");
+
+ // This is a hack for loading DWARF in .o files on Mac where the .o files
+ // in the debug map of the main executable have relative paths which require
+ // the lldb-vscode binary to have its working directory set to that relative
+ // root for the .o files in order to be able to load debug info.
+ if (!debuggerRoot.empty()) {
+ llvm::sys::fs::set_current_path(debuggerRoot.data());
+ }
+
+ // Run any initialize LLDB commands the user specified in the launch.json
+ g_vsc.RunInitCommands();
+
+ // Grab the name of the program we need to debug and set it as the first
+ // argument that will be passed to the program we will debug.
+ const auto program = GetString(arguments, "program");
+ if (!program.empty()) {
+ lldb::SBFileSpec program_fspec(program.data(), true /*resolve_path*/);
+
+ g_vsc.launch_info.SetExecutableFile(program_fspec,
+ false /*add_as_first_arg*/);
+ const char *target_triple = nullptr;
+ const char *uuid_cstr = nullptr;
+ // Stand alone debug info file if different from executable
+ const char *symfile = nullptr;
+ g_vsc.target.AddModule(program.data(), target_triple, uuid_cstr, symfile);
+ if (error.Fail()) {
+ response["success"] = llvm::json::Value(false);
+ EmplaceSafeString(response, "message", std::string(error.GetCString()));
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+ return;
+ }
+ }
+
+ const bool detatchOnError = GetBoolean(arguments, "detachOnError", false);
+ g_vsc.launch_info.SetDetachOnError(detatchOnError);
+
+ // Run any pre run LLDB commands the user specified in the launch.json
+ g_vsc.RunPreRunCommands();
+
+ if (pid == LLDB_INVALID_PROCESS_ID && wait_for) {
+ char attach_info[256];
+ auto attach_info_len =
+ snprintf(attach_info, sizeof(attach_info),
+ "Waiting to attach to \"%s\"...", program.data());
+ g_vsc.SendOutput(OutputType::Console, llvm::StringRef(attach_info,
+ attach_info_len));
+ }
+ if (attachCommands.empty()) {
+ // No "attachCommands", just attach normally.
+ // Disable async events so the attach will be successful when we return from
+ // the launch call and the launch will happen synchronously
+ g_vsc.debugger.SetAsync(false);
+ g_vsc.target.Attach(g_vsc.attach_info, error);
+ // Reenable async events
+ g_vsc.debugger.SetAsync(true);
+ } else {
+ // We have "attachCommands" that are a set of commands that are expected
+ // to execute the commands after which a process should be created. If there
+ // is no valid process after running these commands, we have failed.
+ g_vsc.RunLLDBCommands("Running attachCommands:", attachCommands);
+ // The custom commands might have created a new target so we should use the
+ // selected target after these commands are run.
+ g_vsc.target = g_vsc.debugger.GetSelectedTarget();
+ }
+
+ SetSourceMapFromArguments(*arguments);
+
+ if (error.Success()) {
+ auto attached_pid = g_vsc.target.GetProcess().GetProcessID();
+ if (attached_pid == LLDB_INVALID_PROCESS_ID) {
+ if (attachCommands.empty())
+ error.SetErrorString("failed to attach to a process");
+ else
+ error.SetErrorString("attachCommands failed to attach to a process");
+ }
+ }
+
+ if (error.Fail()) {
+ response["success"] = llvm::json::Value(false);
+ EmplaceSafeString(response, "message", std::string(error.GetCString()));
+ }
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+ if (error.Success()) {
+ SendProcessEvent(Attach);
+ g_vsc.SendJSON(CreateEventObject("initialized"));
+ // SendThreadStoppedEvent();
+ }
+}
+
+// "ContinueRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "Continue request; value of command field is 'continue'.
+// The request starts the debuggee to run again.",
+// "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "continue" ]
+// },
+// "arguments": {
+// "$ref": "#/definitions/ContinueArguments"
+// }
+// },
+// "required": [ "command", "arguments" ]
+// }]
+// },
+// "ContinueArguments": {
+// "type": "object",
+// "description": "Arguments for 'continue' request.",
+// "properties": {
+// "threadId": {
+// "type": "integer",
+// "description": "Continue execution for the specified thread (if
+// possible). If the backend cannot continue on a single
+// thread but will continue on all threads, it should
+// set the allThreadsContinued attribute in the response
+// to true."
+// }
+// },
+// "required": [ "threadId" ]
+// },
+// "ContinueResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'continue' request.",
+// "properties": {
+// "body": {
+// "type": "object",
+// "properties": {
+// "allThreadsContinued": {
+// "type": "boolean",
+// "description": "If true, the continue request has ignored the
+// specified thread and continued all threads
+// instead. If this attribute is missing a value
+// of 'true' is assumed for backward
+// compatibility."
+// }
+// }
+// }
+// },
+// "required": [ "body" ]
+// }]
+// }
+void request_continue(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ FillResponse(request, response);
+ lldb::SBProcess process = g_vsc.target.GetProcess();
+ auto arguments = request.getObject("arguments");
+ // Remember the thread ID that caused the resume so we can set the
+ // "threadCausedFocus" boolean value in the "stopped" events.
+ g_vsc.focus_tid = GetUnsigned(arguments, "threadId", LLDB_INVALID_THREAD_ID);
+ lldb::SBError error = process.Continue();
+ llvm::json::Object body;
+ body.try_emplace("allThreadsContinued", true);
+ response.try_emplace("body", std::move(body));
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+// "ConfigurationDoneRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "ConfigurationDone request; value of command field
+// is 'configurationDone'.\nThe client of the debug protocol must
+// send this request at the end of the sequence of configuration
+// requests (which was started by the InitializedEvent).",
+// "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "configurationDone" ]
+// },
+// "arguments": {
+// "$ref": "#/definitions/ConfigurationDoneArguments"
+// }
+// },
+// "required": [ "command" ]
+// }]
+// },
+// "ConfigurationDoneArguments": {
+// "type": "object",
+// "description": "Arguments for 'configurationDone' request.\nThe
+// configurationDone request has no standardized attributes."
+// },
+// "ConfigurationDoneResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'configurationDone' request. This is
+// just an acknowledgement, so no body field is required."
+// }]
+// },
+void request_configurationDone(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ FillResponse(request, response);
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+ if (g_vsc.stop_at_entry)
+ SendThreadStoppedEvent();
+ else
+ g_vsc.target.GetProcess().Continue();
+}
+
+// "DisconnectRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "Disconnect request; value of command field is
+// 'disconnect'.",
+// "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "disconnect" ]
+// },
+// "arguments": {
+// "$ref": "#/definitions/DisconnectArguments"
+// }
+// },
+// "required": [ "command" ]
+// }]
+// },
+// "DisconnectArguments": {
+// "type": "object",
+// "description": "Arguments for 'disconnect' request.",
+// "properties": {
+// "terminateDebuggee": {
+// "type": "boolean",
+// "description": "Indicates whether the debuggee should be terminated
+// when the debugger is disconnected. If unspecified,
+// the debug adapter is free to do whatever it thinks
+// is best. A client can only rely on this attribute
+// being properly honored if a debug adapter returns
+// true for the 'supportTerminateDebuggee' capability."
+// },
+// "restart": {
+// "type": "boolean",
+// "description": "Indicates whether the debuggee should be restart
+// the process."
+// }
+// }
+// },
+// "DisconnectResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'disconnect' request. This is just an
+// acknowledgement, so no body field is required."
+// }]
+// }
+void request_disconnect(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ FillResponse(request, response);
+ auto arguments = request.getObject("arguments");
+
+ bool terminateDebuggee = GetBoolean(arguments, "terminateDebuggee", false);
+ lldb::SBProcess process = g_vsc.target.GetProcess();
+ auto state = process.GetState();
+
+ switch (state) {
+ case lldb::eStateInvalid:
+ case lldb::eStateUnloaded:
+ case lldb::eStateDetached:
+ case lldb::eStateExited:
+ break;
+ case lldb::eStateConnected:
+ case lldb::eStateAttaching:
+ case lldb::eStateLaunching:
+ case lldb::eStateStepping:
+ case lldb::eStateCrashed:
+ case lldb::eStateSuspended:
+ case lldb::eStateStopped:
+ case lldb::eStateRunning:
+ g_vsc.debugger.SetAsync(false);
+ if (terminateDebuggee)
+ process.Kill();
+ else
+ process.Detach();
+ g_vsc.debugger.SetAsync(true);
+ break;
+ }
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+ SendTerminatedEvent();
+ if (g_vsc.event_thread.joinable()) {
+ g_vsc.broadcaster.BroadcastEventByType(eBroadcastBitStopEventThread);
+ g_vsc.event_thread.join();
+ }
+}
+
+void request_exceptionInfo(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ FillResponse(request, response);
+ auto arguments = request.getObject("arguments");
+ llvm::json::Object body;
+ lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments);
+ if (thread.IsValid()) {
+ auto stopReason = thread.GetStopReason();
+ if (stopReason == lldb::eStopReasonSignal)
+ body.try_emplace("exceptionId", "signal");
+ else if (stopReason == lldb::eStopReasonBreakpoint) {
+ ExceptionBreakpoint *exc_bp = g_vsc.GetExceptionBPFromStopReason(thread);
+ if (exc_bp) {
+ EmplaceSafeString(body, "exceptionId", exc_bp->filter);
+ EmplaceSafeString(body, "description", exc_bp->label);
+ } else {
+ body.try_emplace("exceptionId", "exception");
+ }
+ } else {
+ body.try_emplace("exceptionId", "exception");
+ }
+ if (!ObjectContainsKey(body, "description")) {
+ char description[1024];
+ if (thread.GetStopDescription(description, sizeof(description))) {
+ EmplaceSafeString(body, "description", std::string(description));
+ }
+ }
+ body.try_emplace("breakMode", "always");
+ // auto excInfoCount = thread.GetStopReasonDataCount();
+ // for (auto i=0; i<excInfoCount; ++i) {
+ // uint64_t exc_data = thread.GetStopReasonDataAtIndex(i);
+ // }
+ } else {
+ response["success"] = llvm::json::Value(false);
+ }
+ response.try_emplace("body", std::move(body));
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+// "CompletionsRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "Returns a list of possible completions for a given caret position and text.\nThe CompletionsRequest may only be called if the 'supportsCompletionsRequest' capability exists and is true.",
+// "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "completions" ]
+// },
+// "arguments": {
+// "$ref": "#/definitions/CompletionsArguments"
+// }
+// },
+// "required": [ "command", "arguments" ]
+// }]
+// },
+// "CompletionsArguments": {
+// "type": "object",
+// "description": "Arguments for 'completions' request.",
+// "properties": {
+// "frameId": {
+// "type": "integer",
+// "description": "Returns completions in the scope of this stack frame. If not specified, the completions are returned for the global scope."
+// },
+// "text": {
+// "type": "string",
+// "description": "One or more source lines. Typically this is the text a user has typed into the debug console before he asked for completion."
+// },
+// "column": {
+// "type": "integer",
+// "description": "The character position for which to determine the completion proposals."
+// },
+// "line": {
+// "type": "integer",
+// "description": "An optional line for which to determine the completion proposals. If missing the first line of the text is assumed."
+// }
+// },
+// "required": [ "text", "column" ]
+// },
+// "CompletionsResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'completions' request.",
+// "properties": {
+// "body": {
+// "type": "object",
+// "properties": {
+// "targets": {
+// "type": "array",
+// "items": {
+// "$ref": "#/definitions/CompletionItem"
+// },
+// "description": "The possible completions for ."
+// }
+// },
+// "required": [ "targets" ]
+// }
+// },
+// "required": [ "body" ]
+// }]
+// },
+// "CompletionItem": {
+// "type": "object",
+// "description": "CompletionItems are the suggestions returned from the CompletionsRequest.",
+// "properties": {
+// "label": {
+// "type": "string",
+// "description": "The label of this completion item. By default this is also the text that is inserted when selecting this completion."
+// },
+// "text": {
+// "type": "string",
+// "description": "If text is not falsy then it is inserted instead of the label."
+// },
+// "sortText": {
+// "type": "string",
+// "description": "A string that should be used when comparing this item with other items. When `falsy` the label is used."
+// },
+// "type": {
+// "$ref": "#/definitions/CompletionItemType",
+// "description": "The item's type. Typically the client uses this information to render the item in the UI with an icon."
+// },
+// "start": {
+// "type": "integer",
+// "description": "This value determines the location (in the CompletionsRequest's 'text' attribute) where the completion text is added.\nIf missing the text is added at the location specified by the CompletionsRequest's 'column' attribute."
+// },
+// "length": {
+// "type": "integer",
+// "description": "This value determines how many characters are overwritten by the completion text.\nIf missing the value 0 is assumed which results in the completion text being inserted."
+// }
+// },
+// "required": [ "label" ]
+// },
+// "CompletionItemType": {
+// "type": "string",
+// "description": "Some predefined types for the CompletionItem. Please note that not all clients have specific icons for all of them.",
+// "enum": [ "method", "function", "constructor", "field", "variable", "class", "interface", "module", "property", "unit", "value", "enum", "keyword", "snippet", "text", "color", "file", "reference", "customcolor" ]
+// }
+void request_completions(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ FillResponse(request, response);
+ llvm::json::Object body;
+ auto arguments = request.getObject("arguments");
+ std::string text = GetString(arguments, "text");
+ auto original_column = GetSigned(arguments, "column", text.size());
+ auto actual_column = original_column - 1;
+ llvm::json::Array targets;
+ // NOTE: the 'line' argument is not needed, as multiline expressions
+ // work well already
+ // TODO: support frameID. Currently
+ // g_vsc.debugger.GetCommandInterpreter().HandleCompletionWithDescriptions
+ // is frame-unaware.
+
+ if (!text.empty() && text[0] == '`') {
+ text = text.substr(1);
+ actual_column--;
+ } else {
+ text = "p " + text;
+ actual_column += 2;
+ }
+ lldb::SBStringList matches;
+ lldb::SBStringList descriptions;
+ g_vsc.debugger.GetCommandInterpreter().HandleCompletionWithDescriptions(
+ text.c_str(),
+ actual_column,
+ 0, -1, matches, descriptions);
+ size_t count = std::min((uint32_t)50, matches.GetSize());
+ targets.reserve(count);
+ for (size_t i = 0; i < count; i++) {
+ std::string match = matches.GetStringAtIndex(i);
+ std::string description = descriptions.GetStringAtIndex(i);
+
+ llvm::json::Object item;
+ EmplaceSafeString(item, "text", match);
+ if (description.empty())
+ EmplaceSafeString(item, "label", match);
+ else
+ EmplaceSafeString(item, "label", match + " -- " + description);
+
+ targets.emplace_back(std::move(item));
+ }
+
+ body.try_emplace("targets", std::move(targets));
+ response.try_emplace("body", std::move(body));
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+// "EvaluateRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "Evaluate request; value of command field is 'evaluate'.
+// Evaluates the given expression in the context of the
+// top most stack frame. The expression has access to any
+// variables and arguments that are in scope.",
+// "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "evaluate" ]
+// },
+// "arguments": {
+// "$ref": "#/definitions/EvaluateArguments"
+// }
+// },
+// "required": [ "command", "arguments" ]
+// }]
+// },
+// "EvaluateArguments": {
+// "type": "object",
+// "description": "Arguments for 'evaluate' request.",
+// "properties": {
+// "expression": {
+// "type": "string",
+// "description": "The expression to evaluate."
+// },
+// "frameId": {
+// "type": "integer",
+// "description": "Evaluate the expression in the scope of this stack
+// frame. If not specified, the expression is evaluated
+// in the global scope."
+// },
+// "context": {
+// "type": "string",
+// "_enum": [ "watch", "repl", "hover" ],
+// "enumDescriptions": [
+// "evaluate is run in a watch.",
+// "evaluate is run from REPL console.",
+// "evaluate is run from a data hover."
+// ],
+// "description": "The context in which the evaluate request is run."
+// },
+// "format": {
+// "$ref": "#/definitions/ValueFormat",
+// "description": "Specifies details on how to format the Evaluate
+// result."
+// }
+// },
+// "required": [ "expression" ]
+// },
+// "EvaluateResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'evaluate' request.",
+// "properties": {
+// "body": {
+// "type": "object",
+// "properties": {
+// "result": {
+// "type": "string",
+// "description": "The result of the evaluate request."
+// },
+// "type": {
+// "type": "string",
+// "description": "The optional type of the evaluate result."
+// },
+// "presentationHint": {
+// "$ref": "#/definitions/VariablePresentationHint",
+// "description": "Properties of a evaluate result that can be
+// used to determine how to render the result in
+// the UI."
+// },
+// "variablesReference": {
+// "type": "number",
+// "description": "If variablesReference is > 0, the evaluate
+// result is structured and its children can be
+// retrieved by passing variablesReference to the
+// VariablesRequest."
+// },
+// "namedVariables": {
+// "type": "number",
+// "description": "The number of named child variables. The
+// client can use this optional information to
+// present the variables in a paged UI and fetch
+// them in chunks."
+// },
+// "indexedVariables": {
+// "type": "number",
+// "description": "The number of indexed child variables. The
+// client can use this optional information to
+// present the variables in a paged UI and fetch
+// them in chunks."
+// }
+// },
+// "required": [ "result", "variablesReference" ]
+// }
+// },
+// "required": [ "body" ]
+// }]
+// }
+void request_evaluate(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ FillResponse(request, response);
+ llvm::json::Object body;
+ auto arguments = request.getObject("arguments");
+ lldb::SBFrame frame = g_vsc.GetLLDBFrame(*arguments);
+ const auto expression = GetString(arguments, "expression");
+
+ if (!expression.empty() && expression[0] == '`') {
+ auto result = RunLLDBCommands(llvm::StringRef(),
+ {expression.substr(1)});
+ EmplaceSafeString(body, "result", result);
+ body.try_emplace("variablesReference", (int64_t)0);
+ } else {
+ // Always try to get the answer from the local variables if possible. If
+ // this fails, then actually evaluate an expression using the expression
+ // parser. "frame variable" is more reliable than the expression parser in
+ // many cases and it is faster.
+ lldb::SBValue value = frame.GetValueForVariablePath(
+ expression.data(), lldb::eDynamicDontRunTarget);
+ if (value.GetError().Fail())
+ value = frame.EvaluateExpression(expression.data());
+ if (value.GetError().Fail()) {
+ response["success"] = llvm::json::Value(false);
+ // This error object must live until we're done with the pointer returned
+ // by GetCString().
+ lldb::SBError error = value.GetError();
+ const char *error_cstr = error.GetCString();
+ if (error_cstr && error_cstr[0])
+ EmplaceSafeString(response, "message", std::string(error_cstr));
+ else
+ EmplaceSafeString(response, "message", "evaluate failed");
+ } else {
+ SetValueForKey(value, body, "result");
+ auto value_typename = value.GetType().GetDisplayTypeName();
+ EmplaceSafeString(body, "type", value_typename ? value_typename : NO_TYPENAME);
+ if (value.MightHaveChildren()) {
+ auto variablesReference = VARIDX_TO_VARREF(g_vsc.variables.GetSize());
+ g_vsc.variables.Append(value);
+ body.try_emplace("variablesReference", variablesReference);
+ } else {
+ body.try_emplace("variablesReference", (int64_t)0);
+ }
+ }
+ }
+ response.try_emplace("body", std::move(body));
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+// "InitializeRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "Initialize request; value of command field is
+// 'initialize'.",
+// "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "initialize" ]
+// },
+// "arguments": {
+// "$ref": "#/definitions/InitializeRequestArguments"
+// }
+// },
+// "required": [ "command", "arguments" ]
+// }]
+// },
+// "InitializeRequestArguments": {
+// "type": "object",
+// "description": "Arguments for 'initialize' request.",
+// "properties": {
+// "clientID": {
+// "type": "string",
+// "description": "The ID of the (frontend) client using this adapter."
+// },
+// "adapterID": {
+// "type": "string",
+// "description": "The ID of the debug adapter."
+// },
+// "locale": {
+// "type": "string",
+// "description": "The ISO-639 locale of the (frontend) client using
+// this adapter, e.g. en-US or de-CH."
+// },
+// "linesStartAt1": {
+// "type": "boolean",
+// "description": "If true all line numbers are 1-based (default)."
+// },
+// "columnsStartAt1": {
+// "type": "boolean",
+// "description": "If true all column numbers are 1-based (default)."
+// },
+// "pathFormat": {
+// "type": "string",
+// "_enum": [ "path", "uri" ],
+// "description": "Determines in what format paths are specified. The
+// default is 'path', which is the native format."
+// },
+// "supportsVariableType": {
+// "type": "boolean",
+// "description": "Client supports the optional type attribute for
+// variables."
+// },
+// "supportsVariablePaging": {
+// "type": "boolean",
+// "description": "Client supports the paging of variables."
+// },
+// "supportsRunInTerminalRequest": {
+// "type": "boolean",
+// "description": "Client supports the runInTerminal request."
+// }
+// },
+// "required": [ "adapterID" ]
+// },
+// "InitializeResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'initialize' request.",
+// "properties": {
+// "body": {
+// "$ref": "#/definitions/Capabilities",
+// "description": "The capabilities of this debug adapter."
+// }
+// }
+// }]
+// }
+void request_initialize(const llvm::json::Object &request) {
+ g_vsc.debugger = lldb::SBDebugger::Create(true /*source_init_files*/);
+ // Create an empty target right away since we might get breakpoint requests
+ // before we are given an executable to launch in a "launch" request, or a
+ // executable when attaching to a process by process ID in a "attach"
+ // request.
+ FILE *out = llvm::sys::RetryAfterSignal(nullptr, fopen, dev_null_path, "w");
+ if (out) {
+ // Set the output and error file handles to redirect into nothing otherwise
+ // if any code in LLDB prints to the debugger file handles, the output and
+ // error file handles are initialized to STDOUT and STDERR and any output
+ // will kill our debug session.
+ g_vsc.debugger.SetOutputFileHandle(out, true);
+ g_vsc.debugger.SetErrorFileHandle(out, false);
+ }
+
+ g_vsc.target = g_vsc.debugger.CreateTarget(nullptr);
+ lldb::SBListener listener = g_vsc.debugger.GetListener();
+ listener.StartListeningForEvents(
+ g_vsc.target.GetBroadcaster(),
+ lldb::SBTarget::eBroadcastBitBreakpointChanged);
+ listener.StartListeningForEvents(g_vsc.broadcaster,
+ eBroadcastBitStopEventThread);
+ // Start our event thread so we can receive events from the debugger, target,
+ // process and more.
+ g_vsc.event_thread = std::thread(EventThreadFunction);
+
+ llvm::json::Object response;
+ FillResponse(request, response);
+ llvm::json::Object body;
+ // The debug adapter supports the configurationDoneRequest.
+ body.try_emplace("supportsConfigurationDoneRequest", true);
+ // The debug adapter supports function breakpoints.
+ body.try_emplace("supportsFunctionBreakpoints", true);
+ // The debug adapter supports conditional breakpoints.
+ body.try_emplace("supportsConditionalBreakpoints", true);
+ // The debug adapter supports breakpoints that break execution after a
+ // specified number of hits.
+ body.try_emplace("supportsHitConditionalBreakpoints", true);
+ // The debug adapter supports a (side effect free) evaluate request for
+ // data hovers.
+ body.try_emplace("supportsEvaluateForHovers", true);
+ // Available filters or options for the setExceptionBreakpoints request.
+ llvm::json::Array filters;
+ for (const auto &exc_bp : g_vsc.exception_breakpoints) {
+ filters.emplace_back(CreateExceptionBreakpointFilter(exc_bp));
+ }
+ body.try_emplace("exceptionBreakpointFilters", std::move(filters));
+ // The debug adapter supports stepping back via the stepBack and
+ // reverseContinue requests.
+ body.try_emplace("supportsStepBack", false);
+ // The debug adapter supports setting a variable to a value.
+ body.try_emplace("supportsSetVariable", true);
+ // The debug adapter supports restarting a frame.
+ body.try_emplace("supportsRestartFrame", false);
+ // The debug adapter supports the gotoTargetsRequest.
+ body.try_emplace("supportsGotoTargetsRequest", false);
+ // The debug adapter supports the stepInTargetsRequest.
+ body.try_emplace("supportsStepInTargetsRequest", false);
+ // The debug adapter supports the completionsRequest.
+ body.try_emplace("supportsCompletionsRequest", true);
+ // The debug adapter supports the modules request.
+ body.try_emplace("supportsModulesRequest", false);
+ // The set of additional module information exposed by the debug adapter.
+ // body.try_emplace("additionalModuleColumns"] = ColumnDescriptor
+ // Checksum algorithms supported by the debug adapter.
+ // body.try_emplace("supportedChecksumAlgorithms"] = ChecksumAlgorithm
+ // The debug adapter supports the RestartRequest. In this case a client
+ // should not implement 'restart' by terminating and relaunching the adapter
+ // but by calling the RestartRequest.
+ body.try_emplace("supportsRestartRequest", false);
+ // The debug adapter supports 'exceptionOptions' on the
+ // setExceptionBreakpoints request.
+ body.try_emplace("supportsExceptionOptions", true);
+ // The debug adapter supports a 'format' attribute on the stackTraceRequest,
+ // variablesRequest, and evaluateRequest.
+ body.try_emplace("supportsValueFormattingOptions", true);
+ // The debug adapter supports the exceptionInfo request.
+ body.try_emplace("supportsExceptionInfoRequest", true);
+ // The debug adapter supports the 'terminateDebuggee' attribute on the
+ // 'disconnect' request.
+ body.try_emplace("supportTerminateDebuggee", true);
+ // The debug adapter supports the delayed loading of parts of the stack,
+ // which requires that both the 'startFrame' and 'levels' arguments and the
+ // 'totalFrames' result of the 'StackTrace' request are supported.
+ body.try_emplace("supportsDelayedStackTraceLoading", true);
+ // The debug adapter supports the 'loadedSources' request.
+ body.try_emplace("supportsLoadedSourcesRequest", false);
+
+ response.try_emplace("body", std::move(body));
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+// "LaunchRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "Launch request; value of command field is 'launch'.",
+// "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "launch" ]
+// },
+// "arguments": {
+// "$ref": "#/definitions/LaunchRequestArguments"
+// }
+// },
+// "required": [ "command", "arguments" ]
+// }]
+// },
+// "LaunchRequestArguments": {
+// "type": "object",
+// "description": "Arguments for 'launch' request.",
+// "properties": {
+// "noDebug": {
+// "type": "boolean",
+// "description": "If noDebug is true the launch request should launch
+// the program without enabling debugging."
+// }
+// }
+// },
+// "LaunchResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'launch' request. This is just an
+// acknowledgement, so no body field is required."
+// }]
+// }
+void request_launch(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ lldb::SBError error;
+ FillResponse(request, response);
+ auto arguments = request.getObject("arguments");
+ g_vsc.init_commands = GetStrings(arguments, "initCommands");
+ g_vsc.pre_run_commands = GetStrings(arguments, "preRunCommands");
+ g_vsc.stop_commands = GetStrings(arguments, "stopCommands");
+ g_vsc.exit_commands = GetStrings(arguments, "exitCommands");
+ auto launchCommands = GetStrings(arguments, "launchCommands");
+ g_vsc.stop_at_entry = GetBoolean(arguments, "stopOnEntry", false);
+ const auto debuggerRoot = GetString(arguments, "debuggerRoot");
+
+ // This is a hack for loading DWARF in .o files on Mac where the .o files
+ // in the debug map of the main executable have relative paths which require
+ // the lldb-vscode binary to have its working directory set to that relative
+ // root for the .o files in order to be able to load debug info.
+ if (!debuggerRoot.empty()) {
+ llvm::sys::fs::set_current_path(debuggerRoot.data());
+ }
+
+ SetSourceMapFromArguments(*arguments);
+
+ // Run any initialize LLDB commands the user specified in the launch.json
+ g_vsc.RunInitCommands();
+
+ // Grab the current working directory if there is one and set it in the
+ // launch info.
+ const auto cwd = GetString(arguments, "cwd");
+ if (!cwd.empty())
+ g_vsc.launch_info.SetWorkingDirectory(cwd.data());
+
+ // Grab the name of the program we need to debug and set it as the first
+ // argument that will be passed to the program we will debug.
+ llvm::StringRef program = GetString(arguments, "program");
+ if (!program.empty()) {
+ lldb::SBFileSpec program_fspec(program.data(), true /*resolve_path*/);
+ g_vsc.launch_info.SetExecutableFile(program_fspec,
+ true /*add_as_first_arg*/);
+ const char *target_triple = nullptr;
+ const char *uuid_cstr = nullptr;
+ // Stand alone debug info file if different from executable
+ const char *symfile = nullptr;
+ lldb::SBModule module = g_vsc.target.AddModule(
+ program.data(), target_triple, uuid_cstr, symfile);
+ if (!module.IsValid()) {
+ response["success"] = llvm::json::Value(false);
+
+ EmplaceSafeString(
+ response, "message",
+ llvm::formatv("Could not load program '{0}'.", program).str());
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+ return;
+ }
+ }
+
+ // Extract any extra arguments and append them to our program arguments for
+ // when we launch
+ auto args = GetStrings(arguments, "args");
+ if (!args.empty())
+ g_vsc.launch_info.SetArguments(MakeArgv(args).data(), true);
+
+ // Pass any environment variables along that the user specified.
+ auto envs = GetStrings(arguments, "env");
+ if (!envs.empty())
+ g_vsc.launch_info.SetEnvironmentEntries(MakeArgv(envs).data(), true);
+
+ auto flags = g_vsc.launch_info.GetLaunchFlags();
+
+ if (GetBoolean(arguments, "disableASLR", true))
+ flags |= lldb::eLaunchFlagDisableASLR;
+ if (GetBoolean(arguments, "disableSTDIO", false))
+ flags |= lldb::eLaunchFlagDisableSTDIO;
+ if (GetBoolean(arguments, "shellExpandArguments", false))
+ flags |= lldb::eLaunchFlagShellExpandArguments;
+ const bool detatchOnError = GetBoolean(arguments, "detachOnError", false);
+ g_vsc.launch_info.SetDetachOnError(detatchOnError);
+ g_vsc.launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug |
+ lldb::eLaunchFlagStopAtEntry);
+
+ // Run any pre run LLDB commands the user specified in the launch.json
+ g_vsc.RunPreRunCommands();
+ if (launchCommands.empty()) {
+ // Disable async events so the launch will be successful when we return from
+ // the launch call and the launch will happen synchronously
+ g_vsc.debugger.SetAsync(false);
+ g_vsc.target.Launch(g_vsc.launch_info, error);
+ g_vsc.debugger.SetAsync(true);
+ } else {
+ g_vsc.RunLLDBCommands("Running launchCommands:", launchCommands);
+ // The custom commands might have created a new target so we should use the
+ // selected target after these commands are run.
+ g_vsc.target = g_vsc.debugger.GetSelectedTarget();
+ }
+
+ if (error.Fail()) {
+ response["success"] = llvm::json::Value(false);
+ EmplaceSafeString(response, "message", std::string(error.GetCString()));
+ }
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+
+ SendProcessEvent(Launch);
+ g_vsc.SendJSON(llvm::json::Value(CreateEventObject("initialized")));
+ // Reenable async events and start the event thread to catch async events.
+ // g_vsc.debugger.SetAsync(true);
+}
+
+// "NextRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "Next request; value of command field is 'next'. The
+// request starts the debuggee to run again for one step.
+// The debug adapter first sends the NextResponse and then
+// a StoppedEvent (event type 'step') after the step has
+// completed.",
+// "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "next" ]
+// },
+// "arguments": {
+// "$ref": "#/definitions/NextArguments"
+// }
+// },
+// "required": [ "command", "arguments" ]
+// }]
+// },
+// "NextArguments": {
+// "type": "object",
+// "description": "Arguments for 'next' request.",
+// "properties": {
+// "threadId": {
+// "type": "integer",
+// "description": "Execute 'next' for this thread."
+// }
+// },
+// "required": [ "threadId" ]
+// },
+// "NextResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'next' request. This is just an
+// acknowledgement, so no body field is required."
+// }]
+// }
+void request_next(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ FillResponse(request, response);
+ auto arguments = request.getObject("arguments");
+ lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments);
+ if (thread.IsValid()) {
+ // Remember the thread ID that caused the resume so we can set the
+ // "threadCausedFocus" boolean value in the "stopped" events.
+ g_vsc.focus_tid = thread.GetThreadID();
+ thread.StepOver();
+ } else {
+ response["success"] = llvm::json::Value(false);
+ }
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+// "PauseRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "Pause request; value of command field is 'pause'. The
+// request suspenses the debuggee. The debug adapter first sends the
+// PauseResponse and then a StoppedEvent (event type 'pause') after the
+// thread has been paused successfully.", "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "pause" ]
+// },
+// "arguments": {
+// "$ref": "#/definitions/PauseArguments"
+// }
+// },
+// "required": [ "command", "arguments" ]
+// }]
+// },
+// "PauseArguments": {
+// "type": "object",
+// "description": "Arguments for 'pause' request.",
+// "properties": {
+// "threadId": {
+// "type": "integer",
+// "description": "Pause execution for this thread."
+// }
+// },
+// "required": [ "threadId" ]
+// },
+// "PauseResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'pause' request. This is just an
+// acknowledgement, so no body field is required."
+// }]
+// }
+void request_pause(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ FillResponse(request, response);
+ lldb::SBProcess process = g_vsc.target.GetProcess();
+ lldb::SBError error = process.Stop();
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+// "ScopesRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "Scopes request; value of command field is 'scopes'. The
+// request returns the variable scopes for a given stackframe ID.",
+// "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "scopes" ]
+// },
+// "arguments": {
+// "$ref": "#/definitions/ScopesArguments"
+// }
+// },
+// "required": [ "command", "arguments" ]
+// }]
+// },
+// "ScopesArguments": {
+// "type": "object",
+// "description": "Arguments for 'scopes' request.",
+// "properties": {
+// "frameId": {
+// "type": "integer",
+// "description": "Retrieve the scopes for this stackframe."
+// }
+// },
+// "required": [ "frameId" ]
+// },
+// "ScopesResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'scopes' request.",
+// "properties": {
+// "body": {
+// "type": "object",
+// "properties": {
+// "scopes": {
+// "type": "array",
+// "items": {
+// "$ref": "#/definitions/Scope"
+// },
+// "description": "The scopes of the stackframe. If the array has
+// length zero, there are no scopes available."
+// }
+// },
+// "required": [ "scopes" ]
+// }
+// },
+// "required": [ "body" ]
+// }]
+// }
+void request_scopes(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ FillResponse(request, response);
+ llvm::json::Object body;
+ auto arguments = request.getObject("arguments");
+ lldb::SBFrame frame = g_vsc.GetLLDBFrame(*arguments);
+ g_vsc.variables.Clear();
+ g_vsc.variables.Append(frame.GetVariables(true, // arguments
+ true, // locals
+ false, // statics
+ true)); // in_scope_only
+ g_vsc.num_locals = g_vsc.variables.GetSize();
+ g_vsc.variables.Append(frame.GetVariables(false, // arguments
+ false, // locals
+ true, // statics
+ true)); // in_scope_only
+ g_vsc.num_globals = g_vsc.variables.GetSize() - (g_vsc.num_locals);
+ g_vsc.variables.Append(frame.GetRegisters());
+ g_vsc.num_regs =
+ g_vsc.variables.GetSize() - (g_vsc.num_locals + g_vsc.num_globals);
+ body.try_emplace("scopes", g_vsc.CreateTopLevelScopes());
+ response.try_emplace("body", std::move(body));
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+// "SetBreakpointsRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "SetBreakpoints request; value of command field is
+// 'setBreakpoints'. Sets multiple breakpoints for a single source and
+// clears all previous breakpoints in that source. To clear all breakpoint
+// for a source, specify an empty array. When a breakpoint is hit, a
+// StoppedEvent (event type 'breakpoint') is generated.", "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "setBreakpoints" ]
+// },
+// "arguments": {
+// "$ref": "#/definitions/SetBreakpointsArguments"
+// }
+// },
+// "required": [ "command", "arguments" ]
+// }]
+// },
+// "SetBreakpointsArguments": {
+// "type": "object",
+// "description": "Arguments for 'setBreakpoints' request.",
+// "properties": {
+// "source": {
+// "$ref": "#/definitions/Source",
+// "description": "The source location of the breakpoints; either
+// source.path or source.reference must be specified."
+// },
+// "breakpoints": {
+// "type": "array",
+// "items": {
+// "$ref": "#/definitions/SourceBreakpoint"
+// },
+// "description": "The code locations of the breakpoints."
+// },
+// "lines": {
+// "type": "array",
+// "items": {
+// "type": "integer"
+// },
+// "description": "Deprecated: The code locations of the breakpoints."
+// },
+// "sourceModified": {
+// "type": "boolean",
+// "description": "A value of true indicates that the underlying source
+// has been modified which results in new breakpoint locations."
+// }
+// },
+// "required": [ "source" ]
+// },
+// "SetBreakpointsResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'setBreakpoints' request. Returned is
+// information about each breakpoint created by this request. This includes
+// the actual code location and whether the breakpoint could be verified.
+// The breakpoints returned are in the same order as the elements of the
+// 'breakpoints' (or the deprecated 'lines') in the
+// SetBreakpointsArguments.", "properties": {
+// "body": {
+// "type": "object",
+// "properties": {
+// "breakpoints": {
+// "type": "array",
+// "items": {
+// "$ref": "#/definitions/Breakpoint"
+// },
+// "description": "Information about the breakpoints. The array
+// elements are in the same order as the elements of the
+// 'breakpoints' (or the deprecated 'lines') in the
+// SetBreakpointsArguments."
+// }
+// },
+// "required": [ "breakpoints" ]
+// }
+// },
+// "required": [ "body" ]
+// }]
+// },
+// "SourceBreakpoint": {
+// "type": "object",
+// "description": "Properties of a breakpoint or logpoint passed to the
+// setBreakpoints request.", "properties": {
+// "line": {
+// "type": "integer",
+// "description": "The source line of the breakpoint or logpoint."
+// },
+// "column": {
+// "type": "integer",
+// "description": "An optional source column of the breakpoint."
+// },
+// "condition": {
+// "type": "string",
+// "description": "An optional expression for conditional breakpoints."
+// },
+// "hitCondition": {
+// "type": "string",
+// "description": "An optional expression that controls how many hits of
+// the breakpoint are ignored. The backend is expected to interpret the
+// expression as needed."
+// },
+// "logMessage": {
+// "type": "string",
+// "description": "If this attribute exists and is non-empty, the backend
+// must not 'break' (stop) but log the message instead. Expressions within
+// {} are interpolated."
+// }
+// },
+// "required": [ "line" ]
+// }
+void request_setBreakpoints(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ lldb::SBError error;
+ FillResponse(request, response);
+ auto arguments = request.getObject("arguments");
+ auto source = arguments->getObject("source");
+ const auto path = GetString(source, "path");
+ auto breakpoints = arguments->getArray("breakpoints");
+ llvm::json::Array response_breakpoints;
+ // Decode the source breakpoint infos for this "setBreakpoints" request
+ SourceBreakpointMap request_bps;
+ for (const auto &bp : *breakpoints) {
+ auto bp_obj = bp.getAsObject();
+ if (bp_obj) {
+ SourceBreakpoint src_bp(*bp_obj);
+ request_bps[src_bp.line] = std::move(src_bp);
+ }
+ }
+
+ // See if we already have breakpoints set for this source file from a
+ // previous "setBreakpoints" request
+ auto old_src_bp_pos = g_vsc.source_breakpoints.find(path);
+ if (old_src_bp_pos != g_vsc.source_breakpoints.end()) {
+
+ // We have already set breakpoints in this source file and they are giving
+ // use a new list of lines to set breakpoints on. Some breakpoints might
+ // already be set, and some might not. We need to remove any breakpoints
+ // whose lines are not contained in the any breakpoints lines in in the
+ // "breakpoints" array.
+
+ // Delete any breakpoints in this source file that aren't in the
+ // request_bps set. There is no call to remove breakpoints other than
+ // calling this function with a smaller or empty "breakpoints" list.
+ std::vector<uint32_t> remove_lines;
+ for (auto &pair: old_src_bp_pos->second) {
+ auto request_pos = request_bps.find(pair.first);
+ if (request_pos == request_bps.end()) {
+ // This breakpoint no longer exists in this source file, delete it
+ g_vsc.target.BreakpointDelete(pair.second.bp.GetID());
+ remove_lines.push_back(pair.first);
+ } else {
+ pair.second.UpdateBreakpoint(request_pos->second);
+ // Remove this breakpoint from the request breakpoints since we have
+ // handled it here and we don't need to set a new breakpoint below.
+ request_bps.erase(request_pos);
+ // Add this breakpoint info to the response
+ AppendBreakpoint(pair.second.bp, response_breakpoints);
+ }
+ }
+ // Remove any lines from this existing source breakpoint map
+ for (auto line: remove_lines)
+ old_src_bp_pos->second.erase(line);
+
+ // Now add any breakpoint infos left over in request_bps are the
+ // breakpoints that weren't set in this source file yet. We need to update
+ // thread source breakpoint info for the source file in the variable
+ // "old_src_bp_pos->second" so the info for this source file is up to date.
+ for (auto &pair : request_bps) {
+ pair.second.SetBreakpoint(path.data());
+ // Add this breakpoint info to the response
+ AppendBreakpoint(pair.second.bp, response_breakpoints);
+ old_src_bp_pos->second[pair.first] = std::move(pair.second);
+ }
+ } else {
+ // No breakpoints were set for this source file yet. Set all breakpoints
+ // for each line and add them to the response and create an entry in
+ // g_vsc.source_breakpoints for this source file.
+ for (auto &pair : request_bps) {
+ pair.second.SetBreakpoint(path.data());
+ // Add this breakpoint info to the response
+ AppendBreakpoint(pair.second.bp, response_breakpoints);
+ }
+ g_vsc.source_breakpoints[path] = std::move(request_bps);
+ }
+
+ llvm::json::Object body;
+ body.try_emplace("breakpoints", std::move(response_breakpoints));
+ response.try_emplace("body", std::move(body));
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+// "SetExceptionBreakpointsRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "SetExceptionBreakpoints request; value of command field
+// is 'setExceptionBreakpoints'. The request configures the debuggers
+// response to thrown exceptions. If an exception is configured to break, a
+// StoppedEvent is fired (event type 'exception').", "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "setExceptionBreakpoints" ]
+// },
+// "arguments": {
+// "$ref": "#/definitions/SetExceptionBreakpointsArguments"
+// }
+// },
+// "required": [ "command", "arguments" ]
+// }]
+// },
+// "SetExceptionBreakpointsArguments": {
+// "type": "object",
+// "description": "Arguments for 'setExceptionBreakpoints' request.",
+// "properties": {
+// "filters": {
+// "type": "array",
+// "items": {
+// "type": "string"
+// },
+// "description": "IDs of checked exception options. The set of IDs is
+// returned via the 'exceptionBreakpointFilters' capability."
+// },
+// "exceptionOptions": {
+// "type": "array",
+// "items": {
+// "$ref": "#/definitions/ExceptionOptions"
+// },
+// "description": "Configuration options for selected exceptions."
+// }
+// },
+// "required": [ "filters" ]
+// },
+// "SetExceptionBreakpointsResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'setExceptionBreakpoints' request. This is
+// just an acknowledgement, so no body field is required."
+// }]
+// }
+void request_setExceptionBreakpoints(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ lldb::SBError error;
+ FillResponse(request, response);
+ auto arguments = request.getObject("arguments");
+ auto filters = arguments->getArray("filters");
+ // Keep a list of any exception breakpoint filter names that weren't set
+ // so we can clear any exception breakpoints if needed.
+ std::set<std::string> unset_filters;
+ for (const auto &bp : g_vsc.exception_breakpoints)
+ unset_filters.insert(bp.filter);
+
+ for (const auto &value : *filters) {
+ const auto filter = GetAsString(value);
+ auto exc_bp = g_vsc.GetExceptionBreakpoint(filter);
+ if (exc_bp) {
+ exc_bp->SetBreakpoint();
+ unset_filters.erase(filter);
+ }
+ }
+ for (const auto &filter : unset_filters) {
+ auto exc_bp = g_vsc.GetExceptionBreakpoint(filter);
+ if (exc_bp)
+ exc_bp->ClearBreakpoint();
+ }
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+// "SetFunctionBreakpointsRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "SetFunctionBreakpoints request; value of command field is
+// 'setFunctionBreakpoints'. Sets multiple function breakpoints and clears
+// all previous function breakpoints. To clear all function breakpoint,
+// specify an empty array. When a function breakpoint is hit, a StoppedEvent
+// (event type 'function breakpoint') is generated.", "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "setFunctionBreakpoints" ]
+// },
+// "arguments": {
+// "$ref": "#/definitions/SetFunctionBreakpointsArguments"
+// }
+// },
+// "required": [ "command", "arguments" ]
+// }]
+// },
+// "SetFunctionBreakpointsArguments": {
+// "type": "object",
+// "description": "Arguments for 'setFunctionBreakpoints' request.",
+// "properties": {
+// "breakpoints": {
+// "type": "array",
+// "items": {
+// "$ref": "#/definitions/FunctionBreakpoint"
+// },
+// "description": "The function names of the breakpoints."
+// }
+// },
+// "required": [ "breakpoints" ]
+// },
+// "FunctionBreakpoint": {
+// "type": "object",
+// "description": "Properties of a breakpoint passed to the
+// setFunctionBreakpoints request.", "properties": {
+// "name": {
+// "type": "string",
+// "description": "The name of the function."
+// },
+// "condition": {
+// "type": "string",
+// "description": "An optional expression for conditional breakpoints."
+// },
+// "hitCondition": {
+// "type": "string",
+// "description": "An optional expression that controls how many hits of
+// the breakpoint are ignored. The backend is expected to interpret the
+// expression as needed."
+// }
+// },
+// "required": [ "name" ]
+// },
+// "SetFunctionBreakpointsResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'setFunctionBreakpoints' request. Returned is
+// information about each breakpoint created by this request.",
+// "properties": {
+// "body": {
+// "type": "object",
+// "properties": {
+// "breakpoints": {
+// "type": "array",
+// "items": {
+// "$ref": "#/definitions/Breakpoint"
+// },
+// "description": "Information about the breakpoints. The array
+// elements correspond to the elements of the 'breakpoints' array."
+// }
+// },
+// "required": [ "breakpoints" ]
+// }
+// },
+// "required": [ "body" ]
+// }]
+// }
+void request_setFunctionBreakpoints(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ lldb::SBError error;
+ FillResponse(request, response);
+ auto arguments = request.getObject("arguments");
+ auto breakpoints = arguments->getArray("breakpoints");
+ FunctionBreakpointMap request_bps;
+ llvm::json::Array response_breakpoints;
+ for (const auto &value : *breakpoints) {
+ auto bp_obj = value.getAsObject();
+ if (bp_obj == nullptr)
+ continue;
+ FunctionBreakpoint func_bp(*bp_obj);
+ request_bps[func_bp.functionName] = std::move(func_bp);
+ }
+
+ std::vector<llvm::StringRef> remove_names;
+ // Disable any function breakpoints that aren't in the request_bps.
+ // There is no call to remove function breakpoints other than calling this
+ // function with a smaller or empty "breakpoints" list.
+ for (auto &pair: g_vsc.function_breakpoints) {
+ auto request_pos = request_bps.find(pair.first());
+ if (request_pos == request_bps.end()) {
+ // This function breakpoint no longer exists delete it from LLDB
+ g_vsc.target.BreakpointDelete(pair.second.bp.GetID());
+ remove_names.push_back(pair.first());
+ } else {
+ // Update the existing breakpoint as any setting withing the function
+ // breakpoint might have changed.
+ pair.second.UpdateBreakpoint(request_pos->second);
+ // Remove this breakpoint from the request breakpoints since we have
+ // handled it here and we don't need to set a new breakpoint below.
+ request_bps.erase(request_pos);
+ // Add this breakpoint info to the response
+ AppendBreakpoint(pair.second.bp, response_breakpoints);
+ }
+ }
+ // Remove any breakpoints that are no longer in our list
+ for (const auto &name: remove_names)
+ g_vsc.function_breakpoints.erase(name);
+
+ // Any breakpoints that are left in "request_bps" are breakpoints that
+ // need to be set.
+ for (auto &pair : request_bps) {
+ pair.second.SetBreakpoint();
+ // Add this breakpoint info to the response
+ AppendBreakpoint(pair.second.bp, response_breakpoints);
+ g_vsc.function_breakpoints[pair.first()] = std::move(pair.second);
+ }
+
+ llvm::json::Object body;
+ body.try_emplace("breakpoints", std::move(response_breakpoints));
+ response.try_emplace("body", std::move(body));
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+// "SourceRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "Source request; value of command field is 'source'. The
+// request retrieves the source code for a given source reference.",
+// "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "source" ]
+// },
+// "arguments": {
+// "$ref": "#/definitions/SourceArguments"
+// }
+// },
+// "required": [ "command", "arguments" ]
+// }]
+// },
+// "SourceArguments": {
+// "type": "object",
+// "description": "Arguments for 'source' request.",
+// "properties": {
+// "source": {
+// "$ref": "#/definitions/Source",
+// "description": "Specifies the source content to load. Either
+// source.path or source.sourceReference must be specified."
+// },
+// "sourceReference": {
+// "type": "integer",
+// "description": "The reference to the source. This is the same as
+// source.sourceReference. This is provided for backward compatibility
+// since old backends do not understand the 'source' attribute."
+// }
+// },
+// "required": [ "sourceReference" ]
+// },
+// "SourceResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'source' request.",
+// "properties": {
+// "body": {
+// "type": "object",
+// "properties": {
+// "content": {
+// "type": "string",
+// "description": "Content of the source reference."
+// },
+// "mimeType": {
+// "type": "string",
+// "description": "Optional content type (mime type) of the source."
+// }
+// },
+// "required": [ "content" ]
+// }
+// },
+// "required": [ "body" ]
+// }]
+// }
+void request_source(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ FillResponse(request, response);
+ llvm::json::Object body;
+
+ auto arguments = request.getObject("arguments");
+ auto source = arguments->getObject("source");
+ auto sourceReference = GetSigned(source, "sourceReference", -1);
+ auto pos = g_vsc.source_map.find((lldb::addr_t)sourceReference);
+ if (pos != g_vsc.source_map.end()) {
+ EmplaceSafeString(body, "content", pos->second.content);
+ } else {
+ response["success"] = llvm::json::Value(false);
+ }
+ response.try_emplace("body", std::move(body));
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+// "StackTraceRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "StackTrace request; value of command field is
+// 'stackTrace'. The request returns a stacktrace from the current execution
+// state.", "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "stackTrace" ]
+// },
+// "arguments": {
+// "$ref": "#/definitions/StackTraceArguments"
+// }
+// },
+// "required": [ "command", "arguments" ]
+// }]
+// },
+// "StackTraceArguments": {
+// "type": "object",
+// "description": "Arguments for 'stackTrace' request.",
+// "properties": {
+// "threadId": {
+// "type": "integer",
+// "description": "Retrieve the stacktrace for this thread."
+// },
+// "startFrame": {
+// "type": "integer",
+// "description": "The index of the first frame to return; if omitted
+// frames start at 0."
+// },
+// "levels": {
+// "type": "integer",
+// "description": "The maximum number of frames to return. If levels is
+// not specified or 0, all frames are returned."
+// },
+// "format": {
+// "$ref": "#/definitions/StackFrameFormat",
+// "description": "Specifies details on how to format the stack frames."
+// }
+// },
+// "required": [ "threadId" ]
+// },
+// "StackTraceResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'stackTrace' request.",
+// "properties": {
+// "body": {
+// "type": "object",
+// "properties": {
+// "stackFrames": {
+// "type": "array",
+// "items": {
+// "$ref": "#/definitions/StackFrame"
+// },
+// "description": "The frames of the stackframe. If the array has
+// length zero, there are no stackframes available. This means that
+// there is no location information available."
+// },
+// "totalFrames": {
+// "type": "integer",
+// "description": "The total number of frames available."
+// }
+// },
+// "required": [ "stackFrames" ]
+// }
+// },
+// "required": [ "body" ]
+// }]
+// }
+void request_stackTrace(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ FillResponse(request, response);
+ lldb::SBError error;
+ auto arguments = request.getObject("arguments");
+ lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments);
+ llvm::json::Array stackFrames;
+ llvm::json::Object body;
+
+ if (thread.IsValid()) {
+ const auto startFrame = GetUnsigned(arguments, "startFrame", 0);
+ const auto levels = GetUnsigned(arguments, "levels", 0);
+ const auto endFrame = (levels == 0) ? INT64_MAX : (startFrame + levels);
+ for (uint32_t i = startFrame; i < endFrame; ++i) {
+ auto frame = thread.GetFrameAtIndex(i);
+ if (!frame.IsValid())
+ break;
+ stackFrames.emplace_back(CreateStackFrame(frame));
+ }
+ const auto totalFrames = thread.GetNumFrames();
+ body.try_emplace("totalFrames", totalFrames);
+ }
+ body.try_emplace("stackFrames", std::move(stackFrames));
+ response.try_emplace("body", std::move(body));
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+// "StepInRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "StepIn request; value of command field is 'stepIn'. The
+// request starts the debuggee to step into a function/method if possible.
+// If it cannot step into a target, 'stepIn' behaves like 'next'. The debug
+// adapter first sends the StepInResponse and then a StoppedEvent (event
+// type 'step') after the step has completed. If there are multiple
+// function/method calls (or other targets) on the source line, the optional
+// argument 'targetId' can be used to control into which target the 'stepIn'
+// should occur. The list of possible targets for a given source line can be
+// retrieved via the 'stepInTargets' request.", "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "stepIn" ]
+// },
+// "arguments": {
+// "$ref": "#/definitions/StepInArguments"
+// }
+// },
+// "required": [ "command", "arguments" ]
+// }]
+// },
+// "StepInArguments": {
+// "type": "object",
+// "description": "Arguments for 'stepIn' request.",
+// "properties": {
+// "threadId": {
+// "type": "integer",
+// "description": "Execute 'stepIn' for this thread."
+// },
+// "targetId": {
+// "type": "integer",
+// "description": "Optional id of the target to step into."
+// }
+// },
+// "required": [ "threadId" ]
+// },
+// "StepInResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'stepIn' request. This is just an
+// acknowledgement, so no body field is required."
+// }]
+// }
+void request_stepIn(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ FillResponse(request, response);
+ auto arguments = request.getObject("arguments");
+ lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments);
+ if (thread.IsValid()) {
+ // Remember the thread ID that caused the resume so we can set the
+ // "threadCausedFocus" boolean value in the "stopped" events.
+ g_vsc.focus_tid = thread.GetThreadID();
+ thread.StepInto();
+ } else {
+ response["success"] = llvm::json::Value(false);
+ }
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+// "StepOutRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "StepOut request; value of command field is 'stepOut'. The
+// request starts the debuggee to run again for one step. The debug adapter
+// first sends the StepOutResponse and then a StoppedEvent (event type
+// 'step') after the step has completed.", "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "stepOut" ]
+// },
+// "arguments": {
+// "$ref": "#/definitions/StepOutArguments"
+// }
+// },
+// "required": [ "command", "arguments" ]
+// }]
+// },
+// "StepOutArguments": {
+// "type": "object",
+// "description": "Arguments for 'stepOut' request.",
+// "properties": {
+// "threadId": {
+// "type": "integer",
+// "description": "Execute 'stepOut' for this thread."
+// }
+// },
+// "required": [ "threadId" ]
+// },
+// "StepOutResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'stepOut' request. This is just an
+// acknowledgement, so no body field is required."
+// }]
+// }
+void request_stepOut(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ FillResponse(request, response);
+ auto arguments = request.getObject("arguments");
+ lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments);
+ if (thread.IsValid()) {
+ // Remember the thread ID that caused the resume so we can set the
+ // "threadCausedFocus" boolean value in the "stopped" events.
+ g_vsc.focus_tid = thread.GetThreadID();
+ thread.StepOut();
+ } else {
+ response["success"] = llvm::json::Value(false);
+ }
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+// "ThreadsRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "Thread request; value of command field is 'threads'. The
+// request retrieves a list of all threads.", "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "threads" ]
+// }
+// },
+// "required": [ "command" ]
+// }]
+// },
+// "ThreadsResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'threads' request.",
+// "properties": {
+// "body": {
+// "type": "object",
+// "properties": {
+// "threads": {
+// "type": "array",
+// "items": {
+// "$ref": "#/definitions/Thread"
+// },
+// "description": "All threads."
+// }
+// },
+// "required": [ "threads" ]
+// }
+// },
+// "required": [ "body" ]
+// }]
+// }
+void request_threads(const llvm::json::Object &request) {
+
+ lldb::SBProcess process = g_vsc.target.GetProcess();
+ llvm::json::Object response;
+ FillResponse(request, response);
+
+ const uint32_t num_threads = process.GetNumThreads();
+ llvm::json::Array threads;
+ for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
+ lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
+ threads.emplace_back(CreateThread(thread));
+ }
+ if (threads.size() == 0) {
+ response["success"] = llvm::json::Value(false);
+ }
+ llvm::json::Object body;
+ body.try_emplace("threads", std::move(threads));
+ response.try_emplace("body", std::move(body));
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+// "SetVariableRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "setVariable request; value of command field is
+// 'setVariable'. Set the variable with the given name in the variable
+// container to a new value.", "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "setVariable" ]
+// },
+// "arguments": {
+// "$ref": "#/definitions/SetVariableArguments"
+// }
+// },
+// "required": [ "command", "arguments" ]
+// }]
+// },
+// "SetVariableArguments": {
+// "type": "object",
+// "description": "Arguments for 'setVariable' request.",
+// "properties": {
+// "variablesReference": {
+// "type": "integer",
+// "description": "The reference of the variable container."
+// },
+// "name": {
+// "type": "string",
+// "description": "The name of the variable."
+// },
+// "value": {
+// "type": "string",
+// "description": "The value of the variable."
+// },
+// "format": {
+// "$ref": "#/definitions/ValueFormat",
+// "description": "Specifies details on how to format the response value."
+// }
+// },
+// "required": [ "variablesReference", "name", "value" ]
+// },
+// "SetVariableResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'setVariable' request.",
+// "properties": {
+// "body": {
+// "type": "object",
+// "properties": {
+// "value": {
+// "type": "string",
+// "description": "The new value of the variable."
+// },
+// "type": {
+// "type": "string",
+// "description": "The type of the new value. Typically shown in the
+// UI when hovering over the value."
+// },
+// "variablesReference": {
+// "type": "number",
+// "description": "If variablesReference is > 0, the new value is
+// structured and its children can be retrieved by passing
+// variablesReference to the VariablesRequest."
+// },
+// "namedVariables": {
+// "type": "number",
+// "description": "The number of named child variables. The client
+// can use this optional information to present the variables in a
+// paged UI and fetch them in chunks."
+// },
+// "indexedVariables": {
+// "type": "number",
+// "description": "The number of indexed child variables. The client
+// can use this optional information to present the variables in a
+// paged UI and fetch them in chunks."
+// }
+// },
+// "required": [ "value" ]
+// }
+// },
+// "required": [ "body" ]
+// }]
+// }
+void request_setVariable(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ FillResponse(request, response);
+ llvm::json::Array variables;
+ llvm::json::Object body;
+ auto arguments = request.getObject("arguments");
+ // This is a reference to the containing variable/scope
+ const auto variablesReference =
+ GetUnsigned(arguments, "variablesReference", 0);
+ const auto name = GetString(arguments, "name");
+ const auto value = GetString(arguments, "value");
+ // Set success to false just in case we don't find the variable by name
+ response.try_emplace("success", false);
+
+ lldb::SBValue variable;
+ int64_t newVariablesReference = 0;
+
+ // The "id" is the unique integer ID that is unique within the enclosing
+ // variablesReference. It is optionally added to any "interface Variable"
+ // objects to uniquely identify a variable within an enclosing
+ // variablesReference. It helps to disambiguate between two variables that
+ // have the same name within the same scope since the "setVariables" request
+ // only specifies the variable reference of the enclosing scope/variable, and
+ // the name of the variable. We could have two shadowed variables with the
+ // same name in "Locals" or "Globals". In our case the "id" absolute index
+ // of the variable within the g_vsc.variables list.
+ const auto id_value = GetUnsigned(arguments, "id", UINT64_MAX);
+ if (id_value != UINT64_MAX) {
+ variable = g_vsc.variables.GetValueAtIndex(id_value);
+ } else if (VARREF_IS_SCOPE(variablesReference)) {
+ // variablesReference is one of our scopes, not an actual variable it is
+ // asking for a variable in locals or globals or registers
+ int64_t start_idx = 0;
+ int64_t end_idx = 0;
+ switch (variablesReference) {
+ case VARREF_LOCALS:
+ start_idx = 0;
+ end_idx = start_idx + g_vsc.num_locals;
+ break;
+ case VARREF_GLOBALS:
+ start_idx = g_vsc.num_locals;
+ end_idx = start_idx + g_vsc.num_globals;
+ break;
+ case VARREF_REGS:
+ start_idx = g_vsc.num_locals + g_vsc.num_globals;
+ end_idx = start_idx + g_vsc.num_regs;
+ break;
+ default:
+ break;
+ }
+
+ // Find the variable by name in the correct scope and hope we don't have
+ // multiple variables with the same name. We search backwards because
+ // the list of variables has the top most variables first and variables
+ // in deeper scopes are last. This means we will catch the deepest
+ // variable whose name matches which is probably what the user wants.
+ for (int64_t i = end_idx - 1; i >= start_idx; --i) {
+ auto curr_variable = g_vsc.variables.GetValueAtIndex(i);
+ llvm::StringRef variable_name(curr_variable.GetName());
+ if (variable_name == name) {
+ variable = curr_variable;
+ if (curr_variable.MightHaveChildren())
+ newVariablesReference = i;
+ break;
+ }
+ }
+ } else {
+ // We have a named item within an actual variable so we need to find it
+ // withing the container variable by name.
+ const int64_t var_idx = VARREF_TO_VARIDX(variablesReference);
+ lldb::SBValue container = g_vsc.variables.GetValueAtIndex(var_idx);
+ variable = container.GetChildMemberWithName(name.data());
+ if (!variable.IsValid()) {
+ if (name.startswith("[")) {
+ llvm::StringRef index_str(name.drop_front(1));
+ uint64_t index = 0;
+ if (!index_str.consumeInteger(0, index)) {
+ if (index_str == "]")
+ variable = container.GetChildAtIndex(index);
+ }
+ }
+ }
+
+ // We don't know the index of the variable in our g_vsc.variables
+ if (variable.IsValid()) {
+ if (variable.MightHaveChildren()) {
+ newVariablesReference = VARIDX_TO_VARREF(g_vsc.variables.GetSize());
+ g_vsc.variables.Append(variable);
+ }
+ }
+ }
+
+ if (variable.IsValid()) {
+ lldb::SBError error;
+ bool success = variable.SetValueFromCString(value.data(), error);
+ if (success) {
+ SetValueForKey(variable, body, "value");
+ EmplaceSafeString(body, "type", variable.GetType().GetDisplayTypeName());
+ body.try_emplace("variablesReference", newVariablesReference);
+ } else {
+ EmplaceSafeString(body, "message", std::string(error.GetCString()));
+ }
+ response["success"] = llvm::json::Value(success);
+ }
+
+ response.try_emplace("body", std::move(body));
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+// "VariablesRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "Variables request; value of command field is 'variables'.
+// Retrieves all child variables for the given variable reference. An
+// optional filter can be used to limit the fetched children to either named
+// or indexed children.", "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "variables" ]
+// },
+// "arguments": {
+// "$ref": "#/definitions/VariablesArguments"
+// }
+// },
+// "required": [ "command", "arguments" ]
+// }]
+// },
+// "VariablesArguments": {
+// "type": "object",
+// "description": "Arguments for 'variables' request.",
+// "properties": {
+// "variablesReference": {
+// "type": "integer",
+// "description": "The Variable reference."
+// },
+// "filter": {
+// "type": "string",
+// "enum": [ "indexed", "named" ],
+// "description": "Optional filter to limit the child variables to either
+// named or indexed. If ommited, both types are fetched."
+// },
+// "start": {
+// "type": "integer",
+// "description": "The index of the first variable to return; if omitted
+// children start at 0."
+// },
+// "count": {
+// "type": "integer",
+// "description": "The number of variables to return. If count is missing
+// or 0, all variables are returned."
+// },
+// "format": {
+// "$ref": "#/definitions/ValueFormat",
+// "description": "Specifies details on how to format the Variable
+// values."
+// }
+// },
+// "required": [ "variablesReference" ]
+// },
+// "VariablesResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'variables' request.",
+// "properties": {
+// "body": {
+// "type": "object",
+// "properties": {
+// "variables": {
+// "type": "array",
+// "items": {
+// "$ref": "#/definitions/Variable"
+// },
+// "description": "All (or a range) of variables for the given
+// variable reference."
+// }
+// },
+// "required": [ "variables" ]
+// }
+// },
+// "required": [ "body" ]
+// }]
+// }
+void request_variables(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ FillResponse(request, response);
+ llvm::json::Array variables;
+ auto arguments = request.getObject("arguments");
+ const auto variablesReference =
+ GetUnsigned(arguments, "variablesReference", 0);
+ const int64_t start = GetSigned(arguments, "start", 0);
+ const int64_t count = GetSigned(arguments, "count", 0);
+ bool hex = false;
+ auto format = arguments->getObject("format");
+ if (format)
+ hex = GetBoolean(format, "hex", false);
+
+ if (VARREF_IS_SCOPE(variablesReference)) {
+ // variablesReference is one of our scopes, not an actual variable it is
+ // asking for the list of args, locals or globals.
+ int64_t start_idx = 0;
+ int64_t num_children = 0;
+ switch (variablesReference) {
+ case VARREF_LOCALS:
+ start_idx = start;
+ num_children = g_vsc.num_locals;
+ break;
+ case VARREF_GLOBALS:
+ start_idx = start + g_vsc.num_locals + start;
+ num_children = g_vsc.num_globals;
+ break;
+ case VARREF_REGS:
+ start_idx = start + g_vsc.num_locals + g_vsc.num_globals;
+ num_children = g_vsc.num_regs;
+ break;
+ default:
+ break;
+ }
+ const int64_t end_idx = start_idx + ((count == 0) ? num_children : count);
+ for (auto i = start_idx; i < end_idx; ++i) {
+ lldb::SBValue variable = g_vsc.variables.GetValueAtIndex(i);
+ if (!variable.IsValid())
+ break;
+ variables.emplace_back(
+ CreateVariable(variable, VARIDX_TO_VARREF(i), i, hex));
+ }
+ } else {
+ // We are expanding a variable that has children, so we will return its
+ // children.
+ const int64_t var_idx = VARREF_TO_VARIDX(variablesReference);
+ lldb::SBValue variable = g_vsc.variables.GetValueAtIndex(var_idx);
+ if (variable.IsValid()) {
+ const auto num_children = variable.GetNumChildren();
+ const int64_t end_idx = start + ((count == 0) ? num_children : count);
+ for (auto i = start; i < end_idx; ++i) {
+ lldb::SBValue child = variable.GetChildAtIndex(i);
+ if (!child.IsValid())
+ break;
+ if (child.MightHaveChildren()) {
+ const int64_t var_idx = g_vsc.variables.GetSize();
+ auto childVariablesReferences = VARIDX_TO_VARREF(var_idx);
+ variables.emplace_back(
+ CreateVariable(child, childVariablesReferences, var_idx, hex));
+ g_vsc.variables.Append(child);
+ } else {
+ variables.emplace_back(CreateVariable(child, 0, INT64_MAX, hex));
+ }
+ }
+ }
+ }
+ llvm::json::Object body;
+ body.try_emplace("variables", std::move(variables));
+ response.try_emplace("body", std::move(body));
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+// A request used in testing to get the details on all breakpoints that are
+// currently set in the target. This helps us to test "setBreakpoints" and
+// "setFunctionBreakpoints" requests to verify we have the correct set of
+// breakpoints currently set in LLDB.
+void request__testGetTargetBreakpoints(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ FillResponse(request, response);
+ llvm::json::Array response_breakpoints;
+ for (uint32_t i = 0; g_vsc.target.GetBreakpointAtIndex(i).IsValid(); ++i) {
+ auto bp = g_vsc.target.GetBreakpointAtIndex(i);
+ AppendBreakpoint(bp, response_breakpoints);
+ }
+ llvm::json::Object body;
+ body.try_emplace("breakpoints", std::move(response_breakpoints));
+ response.try_emplace("body", std::move(body));
+ g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+const std::map<std::string, RequestCallback> &GetRequestHandlers() {
+#define REQUEST_CALLBACK(name) \
+ { #name, request_##name }
+ static std::map<std::string, RequestCallback> g_request_handlers = {
+ // VSCode Debug Adaptor requests
+ REQUEST_CALLBACK(attach),
+ REQUEST_CALLBACK(completions),
+ REQUEST_CALLBACK(continue),
+ REQUEST_CALLBACK(configurationDone),
+ REQUEST_CALLBACK(disconnect),
+ REQUEST_CALLBACK(evaluate),
+ REQUEST_CALLBACK(exceptionInfo),
+ REQUEST_CALLBACK(initialize),
+ REQUEST_CALLBACK(launch),
+ REQUEST_CALLBACK(next),
+ REQUEST_CALLBACK(pause),
+ REQUEST_CALLBACK(scopes),
+ REQUEST_CALLBACK(setBreakpoints),
+ REQUEST_CALLBACK(setExceptionBreakpoints),
+ REQUEST_CALLBACK(setFunctionBreakpoints),
+ REQUEST_CALLBACK(setVariable),
+ REQUEST_CALLBACK(source),
+ REQUEST_CALLBACK(stackTrace),
+ REQUEST_CALLBACK(stepIn),
+ REQUEST_CALLBACK(stepOut),
+ REQUEST_CALLBACK(threads),
+ REQUEST_CALLBACK(variables),
+ // Testing requests
+ REQUEST_CALLBACK(_testGetTargetBreakpoints),
+ };
+#undef REQUEST_CALLBACK
+ return g_request_handlers;
+}
+
+} // anonymous namespace
+
+int main(int argc, char *argv[]) {
+
+ // Initialize LLDB first before we do anything.
+ lldb::SBDebugger::Initialize();
+
+ if (argc == 2) {
+ const char *arg = argv[1];
+#if !defined(_WIN32)
+ if (strcmp(arg, "-g") == 0) {
+ printf("Paused waiting for debugger to attach (pid = %i)...\n", getpid());
+ pause();
+ } else {
+#else
+ {
+#endif
+ int portno = atoi(arg);
+ printf("Listening on port %i...\n", portno);
+ SOCKET socket_fd = AcceptConnection(portno);
+ if (socket_fd >= 0) {
+ g_vsc.input.descriptor = StreamDescriptor::from_socket(socket_fd, true);
+ g_vsc.output.descriptor =
+ StreamDescriptor::from_socket(socket_fd, false);
+ } else {
+ exit(1);
+ }
+ }
+ } else {
+ g_vsc.input.descriptor = StreamDescriptor::from_file(fileno(stdin), false);
+ g_vsc.output.descriptor =
+ StreamDescriptor::from_file(fileno(stdout), false);
+ }
+ auto request_handlers = GetRequestHandlers();
+ uint32_t packet_idx = 0;
+ while (true) {
+ std::string json = g_vsc.ReadJSON();
+ if (json.empty())
+ break;
+
+ llvm::StringRef json_sref(json);
+ llvm::Expected<llvm::json::Value> json_value = llvm::json::parse(json_sref);
+ if (!json_value) {
+ auto error = json_value.takeError();
+ if (g_vsc.log) {
+ std::string error_str;
+ llvm::raw_string_ostream strm(error_str);
+ strm << error;
+ strm.flush();
+
+ *g_vsc.log << "error: failed to parse JSON: " << error_str << std::endl
+ << json << std::endl;
+ }
+ return 1;
+ }
+
+ auto object = json_value->getAsObject();
+ if (!object) {
+ if (g_vsc.log)
+ *g_vsc.log << "error: json packet isn't a object" << std::endl;
+ return 1;
+ }
+
+ const auto packet_type = GetString(object, "type");
+ if (packet_type == "request") {
+ const auto command = GetString(object, "command");
+ auto handler_pos = request_handlers.find(command);
+ if (handler_pos != request_handlers.end()) {
+ handler_pos->second(*object);
+ } else {
+ if (g_vsc.log)
+ *g_vsc.log << "error: unhandled command \"" << command.data() << std::endl;
+ return 1;
+ }
+ }
+ ++packet_idx;
+ }
+
+ // We must terminate the debugger in a thread before the C++ destructor
+ // chain messes everything up.
+ lldb::SBDebugger::Terminate();
+ return 0;
+}
diff --git a/gnu/llvm/lldb/tools/lldb-vscode/package.json b/gnu/llvm/lldb/tools/lldb-vscode/package.json
new file mode 100644
index 00000000000..847a219fbb5
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-vscode/package.json
@@ -0,0 +1,245 @@
+{
+ "name": "lldb-vscode",
+ "displayName": "LLDB native Debug stub",
+ "version": "0.1.0",
+ "publisher": "llvm.org",
+ "description": "Debug adapter for LLDB which uses a C++ tool to interface directly with LLDB.",
+ "author": {
+ "name": "Greg Clayton",
+ "email": "clayborg@gmail.com"
+ },
+ "license": "LLVM",
+ "keywords": [
+ "multi-root ready"
+ ],
+ "engines": {
+ "vscode": "^1.18.0",
+ "node": "^7.9.0"
+ },
+ "icon": "images/lldb.png",
+ "categories": [
+ "Debuggers"
+ ],
+ "private": true,
+ "devDependencies": {
+ "@types/node": "7.0.43",
+ "@types/mocha": "2.2.45",
+ "typescript": "2.6.2",
+ "mocha": "4.0.1",
+ "vscode": "1.1.10",
+ "vscode-debugadapter-testsupport": "1.25.0",
+ "tslint": "5.8.0",
+ "vsce": "1.35.0"
+ },
+ "contributes": {
+ "debuggers": [
+ {
+ "type": "lldb-vscode",
+ "label": "Native LLDB Debugger",
+ "enableBreakpointsFor": {
+ "languageIds": [
+ "ada",
+ "arm",
+ "asm",
+ "c",
+ "cpp",
+ "crystal",
+ "d",
+ "fortan",
+ "fortran-modern",
+ "nim",
+ "objective-c",
+ "objectpascal",
+ "pascal",
+ "rust",
+ "swift"
+ ]
+ },
+ "program": "./bin/lldb-vscode",
+ "windows": {
+ "program": "./bin/lldb-vscode.exe"
+ },
+ "configurationAttributes": {
+ "launch": {
+ "required": [
+ "program"
+ ],
+ "properties": {
+ "program": {
+ "type": "string",
+ "description": "Path to the program to debug."
+ },
+ "args": {
+ "type": [ "array", "string" ],
+ "description": "Program arguments.",
+ "default": []
+ },
+ "cwd": {
+ "type": "string",
+ "description": "Program working directory.",
+ "default": "${workspaceRoot}"
+ },
+ "env": {
+ "type": "array",
+ "description": "Additional environment variables.",
+ "default": []
+ },
+ "stopOnEntry": {
+ "type": "boolean",
+ "description": "Automatically stop after launch.",
+ "default": false
+ },
+ "disableASLR": {
+ "type": "boolean",
+ "description": "Enable or disable Address space layout randomization if the debugger supports it.",
+ "default": true
+ },
+ "disableSTDIO": {
+ "type": "boolean",
+ "description": "Don't retrieve STDIN, STDOUT and STDERR as the program is running.",
+ "default": false
+ },
+ "shellExpandArguments": {
+ "type": "boolean",
+ "description": "Expand program arguments as a shell would without actually launching the program in a shell.",
+ "default": false
+ },
+ "detachOnError": {
+ "type": "boolean",
+ "description": "Detach from the program.",
+ "default": false
+ },
+ "trace": {
+ "type": "boolean",
+ "description": "Enable logging of the Debug Adapter Protocol.",
+ "default": true
+ },
+ "sourcePath": {
+ "type": "string",
+ "description": "Specify a source path to remap \"./\" to allow full paths to be used when setting breakpoints in binaries that have relative source paths."
+ },
+ "sourceMap": {
+ "type": "array",
+ "description": "Specify an array of path remappings; each element must itself be a two element array containing a source and desination pathname. Overrides sourcePath.",
+ "default": []
+ },
+ "debuggerRoot": {
+ "type": "string",
+ "description": "Specify a working directory to set the debug adaptor to so relative object files can be located."
+ },
+ "initCommands": {
+ "type": "array",
+ "description": "Initialization commands executed upon debugger startup.",
+ "default": []
+ },
+ "preRunCommands": {
+ "type": "array",
+ "description": "Commands executed just before the program is launched.",
+ "default": []
+ },
+ "stopCommands": {
+ "type": "array",
+ "description": "Commands executed each time the program stops.",
+ "default": []
+ },
+ "exitCommands": {
+ "type": "array",
+ "description": "Commands executed at the end of debugging session.",
+ "default": []
+ }
+ }
+ },
+ "attach": {
+ "properties": {
+ "program": {
+ "type": "string",
+ "description": "Path to the program to attach to."
+ },
+ "pid": {
+ "type": [
+ "number",
+ "string"
+ ],
+ "description": "System process ID to attach to."
+ },
+ "waitFor": {
+ "type": "boolean",
+ "description": "If set to true, then wait for the process to launch by looking for a process with a basename that matches `program`. No process ID needs to be specified when using this flag.",
+ "default": true
+ },
+ "trace": {
+ "type": "boolean",
+ "description": "Enable logging of the Debug Adapter Protocol.",
+ "default": true
+ },
+ "sourcePath": {
+ "type": "string",
+ "description": "Specify a source path to remap \"./\" to allow full paths to be used when setting breakpoints in binaries that have relative source paths."
+ },
+ "sourceMap": {
+ "type": "array",
+ "description": "Specify an array of path remappings; each element must itself be a two element array containing a source and desination pathname. Overrides sourcePath.",
+ "default": []
+ },
+ "debuggerRoot": {
+ "type": "string",
+ "description": "Specify a working directory to set the debug adaptor to so relative object files can be located."
+ },
+ "attachCommands": {
+ "type": "array",
+ "description": "Custom commands that are executed instead of attaching to a process ID or to a process by name. These commands may optionally create a new target and must perform an attach. A valid process must exist after these commands complete or the \"attach\" will fail.",
+ "default": []
+ },
+ "initCommands": {
+ "type": "array",
+ "description": "Initialization commands executed upon debugger startup.",
+ "default": []
+ },
+ "preRunCommands": {
+ "type": "array",
+ "description": "Commands executed just before the program is attached to.",
+ "default": []
+ },
+ "stopCommands": {
+ "type": "array",
+ "description": "Commands executed each time the program stops.",
+ "default": []
+ },
+ "exitCommands": {
+ "type": "array",
+ "description": "Commands executed at the end of debugging session.",
+ "default": []
+ }
+ }
+ }
+ },
+ "initialConfigurations": [
+ {
+ "type": "lldb-vscode",
+ "request": "launch",
+ "name": "Debug",
+ "program": "${workspaceRoot}/<your program>",
+ "args": [],
+ "env": [],
+ "cwd": "${workspaceRoot}"
+ }
+ ],
+ "configurationSnippets": [
+ {
+ "label": "LLDB: Launch",
+ "description": "",
+ "body": {
+ "type": "lldb-vscode",
+ "request": "launch",
+ "name": "${2:Launch}",
+ "program": "^\"\\${workspaceRoot}/${1:<your program>}\"",
+ "args": [],
+ "env": [],
+ "cwd": "^\"\\${workspaceRoot}\""
+ }
+ }
+ ]
+ }
+ ]
+ }
+}