aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/source/Plugins
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins')
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.cpp2052
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h93
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.cpp2447
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.h101
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp1130
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h100
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm/ABISysV_arm.cpp2156
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm/ABISysV_arm.h93
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.cpp2417
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.h100
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-hexagon/ABISysV_hexagon.cpp1353
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-hexagon/ABISysV_hexagon.h105
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-i386/ABISysV_i386.cpp849
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-i386/ABISysV_i386.h108
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips/ABISysV_mips.cpp1069
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips/ABISysV_mips.h95
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips64/ABISysV_mips64.cpp1217
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips64/ABISysV_mips64.h108
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc/ABISysV_ppc.cpp982
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc/ABISysV_ppc.h104
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc64/ABISysV_ppc64.cpp1091
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc64/ABISysV_ppc64.h106
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-s390x/ABISysV_s390x.cpp749
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-s390x/ABISysV_s390x.h96
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp1086
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h106
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/Windows-x86_64/ABIWindows_x86_64.cpp1805
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ABI/Windows-x86_64/ABIWindows_x86_64.h99
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.cpp157
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.h40
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Architecture/Mips/ArchitectureMips.cpp240
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Architecture/Mips/ArchitectureMips.h51
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Architecture/PPC64/ArchitecturePPC64.cpp67
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Architecture/PPC64/ArchitecturePPC64.h41
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp1433
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h85
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.cpp618
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.h138
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/HexagonDYLDRendezvous.cpp367
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/HexagonDYLDRendezvous.h246
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp597
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h253
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp773
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h165
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp154
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.h59
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.cpp225
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h54
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTDumper.cpp108
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTDumper.h40
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp516
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.h173
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTStructExtractor.cpp185
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTStructExtractor.h134
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.cpp26
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h579
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp2237
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h528
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangDiagnostic.h49
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp2254
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h564
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionHelper.h60
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp1383
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h184
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp497
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h71
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.cpp66
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.h207
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.cpp215
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.h153
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.cpp168
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.h23
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp716
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h111
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.cpp94
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.h84
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp903
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h226
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp161
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.h107
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.cpp595
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.h131
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp2270
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.h567
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ModuleDependencyCollector.h38
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp14512
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.h786
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulationStateARM.cpp356
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulationStateARM.h79
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp1195
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.h192
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp3050
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.h221
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp2361
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.h182
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.cpp397
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h92
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/ASan/ASanRuntime.cpp325
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/ASan/ASanRuntime.h66
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.cpp274
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.h67
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/TSan/TSanRuntime.cpp1066
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/TSan/TSanRuntime.h81
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.cpp341
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.h68
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp474
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.h80
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.cpp205
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.h25
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp1071
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h136
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp660
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h177
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp228
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.h43
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp681
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h159
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.cpp159
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.h33
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxBitset.cpp113
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp122
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp444
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp463
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxOptional.cpp84
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxQueue.cpp60
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp82
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp221
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp255
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.h30
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp296
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp426
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h59
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp106
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp169
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.cpp98
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h50
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.cpp238
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.h37
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CF.cpp295
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CF.h32
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/Cocoa.cpp1123
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/Cocoa.h116
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CoreMedia.cpp95
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CoreMedia.h25
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSArray.cpp870
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp1049
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.h94
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSError.cpp211
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSException.cpp205
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp313
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSSet.cpp675
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSSet.h39
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.cpp396
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.h42
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp1094
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h163
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.cpp56
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h51
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp353
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h90
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp617
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h117
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp541
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h331
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp662
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.h51
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp583
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h131
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp443
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h156
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp2736
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h342
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.cpp1151
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.h158
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeEncodingParser.cpp381
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeEncodingParser.h83
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp204
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.h76
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp436
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h429
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptExpressionOpts.cpp191
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptExpressionOpts.h52
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp5055
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h587
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.cpp160
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.h17
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.cpp281
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.h22
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp200
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.h48
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp514
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h177
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp411
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h163
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp168
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h101
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.cpp435
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.h394
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp3380
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h381
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.cpp258
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.h100
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp419
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.h85
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp321
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h71
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Platform/NetBSD/PlatformNetBSD.cpp364
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Platform/NetBSD/PlatformNetBSD.h69
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.cpp215
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.h63
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp1062
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.h111
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp858
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h201
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFBundle.cpp68
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFBundle.h35
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFString.cpp153
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFString.h40
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFUtils.h75
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/DarwinProcessLauncher.cpp642
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/DarwinProcessLauncher.h48
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/LaunchFlavor.h32
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/MachException.cpp504
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/MachException.h139
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.cpp1541
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.h337
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadDarwin.cpp281
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadDarwin.h165
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadListDarwin.cpp702
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadListDarwin.h138
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp632
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.h113
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/POSIXStopInfo.cpp44
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/POSIXStopInfo.h66
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp1081
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h220
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp1437
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.h279
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIX.h63
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm.cpp259
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm.h76
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.cpp264
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.h77
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_mips64.cpp262
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_mips64.h80
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_powerpc.cpp274
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_powerpc.h84
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_x86.cpp613
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_x86.h81
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp798
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h118
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.cpp38
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h43
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.cpp933
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.h97
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.cpp205
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.h74
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/POSIX/CrashReason.cpp337
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/POSIX/CrashReason.h59
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/POSIX/NativeProcessELF.cpp110
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/POSIX/NativeProcessELF.h46
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessMessage.cpp68
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessMessage.h168
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp31
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.h39
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/ARMDefines.h191
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/ARMUtils.h374
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/AuxVector.cpp96
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/AuxVector.h73
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp758
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.h93
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/FreeBSDSignals.cpp86
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/FreeBSDSignals.h27
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.cpp18
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.h29
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryThread.cpp84
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryThread.h91
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryUnwind.cpp66
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryUnwind.h41
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp237
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.h38
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/InstructionUtils.h116
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.cpp112
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.h27
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxSignals.cpp92
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxSignals.h27
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/MipsLinuxSignals.cpp93
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/MipsLinuxSignals.h28
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextRegisterInfo.cpp42
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h40
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/NetBSDSignals.cpp53
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/NetBSDSignals.h27
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwinConstants.h25
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm.cpp1757
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm.h264
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm64.cpp1043
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm64.h231
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_i386.cpp969
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_i386.h208
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.cpp1078
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.h213
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDummy.cpp120
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDummy.h64
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.cpp80
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.h25
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.cpp117
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h30
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.cpp236
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h52
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.cpp143
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h30
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextHistory.cpp122
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextHistory.h65
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp2105
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.h253
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_i386.cpp133
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_i386.h33
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips.cpp149
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips.h36
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips64.cpp207
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips64.h39
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_s390x.cpp74
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_s390x.h36
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.cpp192
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.h36
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp160
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h56
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_arm.cpp74
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_arm.h40
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_i386.cpp60
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_i386.h35
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_x86_64.cpp63
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_x86_64.h36
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMemory.cpp139
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMemory.h73
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.cpp123
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.h30
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_i386.cpp77
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_i386.h25
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.cpp104
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.h30
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.cpp212
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.h106
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.cpp232
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.h106
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.cpp184
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.h84
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.cpp193
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h202
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.cpp211
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.h77
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.cpp191
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.h79
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.cpp528
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.h176
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextThreadMemory.cpp216
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextThreadMemory.h100
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_mips.h374
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_powerpc.h123
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_s390x.h90
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_x86.h374
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoInterface.h70
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp91
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.h61
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp96
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h69
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.cpp63
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.h31
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h1619
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h712
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_i386.h305
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_mips.h305
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_mips64.h435
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_powerpc.h228
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_ppc64.h329
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_ppc64le.h474
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_s390x.h124
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_x86_64.h466
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp624
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/StopInfoMachException.h51
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/ThreadMemory.cpp103
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/ThreadMemory.h106
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindLLDB.cpp505
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindLLDB.h157
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp247
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h53
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-arm-register-enums.h199
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-arm64-register-enums.h264
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-mips-freebsd-register-enums.h65
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-mips-linux-register-enums.h360
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-ppc64-register-enums.h136
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-ppc64le-register-enums.h207
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-s390x-register-enums.h90
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-x86-register-enums.h318
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp915
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h168
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp72
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h53
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp73
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h53
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.cpp91
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.h55
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.cpp111
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.h60
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_ppc64le.cpp133
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_ppc64le.h48
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.cpp96
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.h55
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.cpp99
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.h49
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterUtilities.cpp38
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h147
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp442
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ThreadElfCore.h176
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp394
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h148
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp1398
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h238
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp4005
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h602
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp142
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h155
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp282
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.h82
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp158
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h95
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp1285
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h155
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp3285
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h229
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp550
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h109
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp958
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h132
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp5486
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h459
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp43
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h46
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp343
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h119
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp615
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.h115
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp115
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.h236
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/NtStructures.h36
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp822
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.h119
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.cpp533
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.h92
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.cpp833
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.h82
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.cpp96
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.h135
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp110
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h180
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp118
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.h45
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/None/ScriptInterpreterNone.cpp71
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h47
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp1052
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h479
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.cpp169
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.h56
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp3259
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h57
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h473
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/lldb-python.h45
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp1924
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.h118
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp653
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h222
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp175
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h62
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DIERef.cpp18
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DIERef.h63
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.h59
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp3885
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h151
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp89
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.h65
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.cpp58
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.h85
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.cpp149
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.h127
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp115
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h35
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFContext.cpp138
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFContext.h74
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp483
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h107
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.cpp30
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.h37
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.cpp146
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.h81
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.cpp140
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.h62
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp95
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h55
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp197
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h88
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp1246
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h185
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp1038
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h227
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacro.cpp126
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacro.h61
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp290
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h76
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp87
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h88
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp402
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.h77
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp739
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h89
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp66
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h66
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.cpp23
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.h37
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp878
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h306
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp272
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h81
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.cpp583
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.h193
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.cpp33
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.h35
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp476
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h74
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp78
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.h53
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp3762
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h492
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp1440
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h362
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp153
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h79
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwoDwp.cpp35
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwoDwp.h29
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwp.cpp138
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwp.h50
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.cpp73
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.h103
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.cpp457
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.h24
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp225
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h94
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp252
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h44
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp1366
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h144
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.cpp137
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.h28
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp196
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.h162
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.cpp160
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.h125
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp877
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h158
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp1590
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h241
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp236
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h82
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp1367
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.h116
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.cpp181
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.h47
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp1999
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h255
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp267
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h101
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.cpp162
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.h44
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp656
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h154
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp269
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h65
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp1614
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h199
578 files changed, 224689 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.cpp
new file mode 100644
index 000000000000..362a80be4b0d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.cpp
@@ -0,0 +1,2052 @@
+//===-- ABIMacOSX_arm.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 "ABIMacOSX_arm.h"
+
+#include <vector>
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+
+#include "Plugins/Process/Utility/ARMDefines.h"
+#include "Utility/ARM_DWARF_Registers.h"
+#include "Utility/ARM_ehframe_Registers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static RegisterInfo g_register_infos[] = {
+ // NAME ALT SZ OFF ENCODING FORMAT EH_FRAME
+ // DWARF GENERIC PROCESS PLUGIN
+ // LLDB NATIVE
+ // ========== ======= == === ============= ============
+ // ======================= =================== ===========================
+ // ======================= ======================
+ {"r0",
+ "arg1",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r0, dwarf_r0, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r1",
+ "arg2",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r1, dwarf_r1, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r2",
+ "arg3",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r2, dwarf_r2, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r3",
+ "arg4",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r3, dwarf_r3, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r4",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r4, dwarf_r4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r5",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r5, dwarf_r5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r6",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r6, dwarf_r6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r7",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r8",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r8, dwarf_r8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r9",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r9, dwarf_r9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r10",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r10, dwarf_r10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r11",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r11, dwarf_r11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r12",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r12, dwarf_r12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"sp",
+ "r13",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lr",
+ "r14",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"pc",
+ "r15",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cpsr",
+ "psr",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s0",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s1",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s2",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s3",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s4",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s5",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s6",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s7",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s8",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s9",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s10",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s11",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s12",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s13",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s14",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s15",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s16",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s17",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s18",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s19",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s20",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s21",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s22",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s23",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s24",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s25",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s26",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s27",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s28",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s29",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s30",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s31",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fpscr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d0",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d1",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d2",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d3",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d4",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d5",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d6",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d7",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d8",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d9",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d10",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d11",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d12",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d13",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d14",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d15",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d16",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d17",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d18",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d19",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d20",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d21",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d22",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d23",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d24",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d25",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d26",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d27",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d28",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d29",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d29, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d30",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d30, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d31",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d31, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r8_usr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r8_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r9_usr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r9_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r10_usr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r10_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r11_usr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r11_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r12_usr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r12_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13_usr",
+ "sp_usr",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r13_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14_usr",
+ "lr_usr",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r14_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r8_fiq",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r8_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r9_fiq",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r9_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r10_fiq",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r10_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r11_fiq",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r11_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r12_fiq",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r12_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13_fiq",
+ "sp_fiq",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r13_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14_fiq",
+ "lr_fiq",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r14_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13_irq",
+ "sp_irq",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r13_irq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14_irq",
+ "lr_irq",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r14_irq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13_abt",
+ "sp_abt",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r13_abt, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14_abt",
+ "lr_abt",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r14_abt, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13_und",
+ "sp_und",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r13_und, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14_und",
+ "lr_und",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r14_und, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13_svc",
+ "sp_svc",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r13_svc, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14_svc",
+ "lr_svc",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r14_svc, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0}};
+
+static const uint32_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos);
+static bool g_register_info_names_constified = false;
+
+const lldb_private::RegisterInfo *
+ABIMacOSX_arm::GetRegisterInfoArray(uint32_t &count) {
+ // Make the C-string names and alt_names for the register infos into const
+ // C-string values by having the ConstString unique the names in the global
+ // constant C-string pool.
+ if (!g_register_info_names_constified) {
+ g_register_info_names_constified = true;
+ for (uint32_t i = 0; i < k_num_register_infos; ++i) {
+ if (g_register_infos[i].name)
+ g_register_infos[i].name =
+ ConstString(g_register_infos[i].name).GetCString();
+ if (g_register_infos[i].alt_name)
+ g_register_infos[i].alt_name =
+ ConstString(g_register_infos[i].alt_name).GetCString();
+ }
+ }
+ count = k_num_register_infos;
+ return g_register_infos;
+}
+
+size_t ABIMacOSX_arm::GetRedZoneSize() const { return 0; }
+
+// Static Functions
+
+ABISP
+ABIMacOSX_arm::CreateInstance(ProcessSP process_sp, const ArchSpec &arch) {
+ const llvm::Triple::ArchType arch_type = arch.GetTriple().getArch();
+ const llvm::Triple::VendorType vendor_type = arch.GetTriple().getVendor();
+
+ if (vendor_type == llvm::Triple::Apple) {
+ if ((arch_type == llvm::Triple::arm) ||
+ (arch_type == llvm::Triple::thumb)) {
+ return ABISP(new ABIMacOSX_arm(process_sp));
+ }
+ }
+
+ return ABISP();
+}
+
+bool ABIMacOSX_arm::PrepareTrivialCall(Thread &thread, addr_t sp,
+ addr_t function_addr, addr_t return_addr,
+ llvm::ArrayRef<addr_t> args) const {
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+
+ const uint32_t pc_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ const uint32_t sp_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+ const uint32_t ra_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA);
+
+ RegisterValue reg_value;
+
+ const char *reg_names[] = {"r0", "r1", "r2", "r3"};
+
+ llvm::ArrayRef<addr_t>::iterator ai = args.begin(), ae = args.end();
+
+ for (size_t i = 0; i < llvm::array_lengthof(reg_names); ++i) {
+ if (ai == ae)
+ break;
+
+ reg_value.SetUInt32(*ai);
+ if (!reg_ctx->WriteRegister(reg_ctx->GetRegisterInfoByName(reg_names[i]),
+ reg_value))
+ return false;
+
+ ++ai;
+ }
+
+ if (ai != ae) {
+ // Spill onto the stack
+ size_t num_stack_regs = ae - ai;
+
+ sp -= (num_stack_regs * 4);
+ // Keep the stack 16 byte aligned
+ sp &= ~(16ull - 1ull);
+
+ // just using arg1 to get the right size
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1);
+
+ addr_t arg_pos = sp;
+
+ for (; ai != ae; ++ai) {
+ reg_value.SetUInt32(*ai);
+ if (reg_ctx
+ ->WriteRegisterValueToMemory(reg_info, arg_pos,
+ reg_info->byte_size, reg_value)
+ .Fail())
+ return false;
+ arg_pos += reg_info->byte_size;
+ }
+ }
+
+ TargetSP target_sp(thread.CalculateTarget());
+ Address so_addr;
+
+ // Figure out if our return address is ARM or Thumb by using the
+ // Address::GetCallableLoadAddress(Target*) which will figure out the ARM
+ // thumb-ness and set the correct address bits for us.
+ so_addr.SetLoadAddress(return_addr, target_sp.get());
+ return_addr = so_addr.GetCallableLoadAddress(target_sp.get());
+
+ // Set "lr" to the return address
+ if (!reg_ctx->WriteRegisterFromUnsigned(ra_reg_num, return_addr))
+ return false;
+
+ // If bit zero or 1 is set, this must be a thumb function, no need to figure
+ // this out from the symbols.
+ so_addr.SetLoadAddress(function_addr, target_sp.get());
+ function_addr = so_addr.GetCallableLoadAddress(target_sp.get());
+
+ const RegisterInfo *cpsr_reg_info = reg_ctx->GetRegisterInfoByName("cpsr");
+ const uint32_t curr_cpsr = reg_ctx->ReadRegisterAsUnsigned(cpsr_reg_info, 0);
+
+ // Make a new CPSR and mask out any Thumb IT (if/then) bits
+ uint32_t new_cpsr = curr_cpsr & ~MASK_CPSR_IT_MASK;
+ // If bit zero or 1 is set, this must be thumb...
+ if (function_addr & 1ull)
+ new_cpsr |= MASK_CPSR_T; // Set T bit in CPSR
+ else
+ new_cpsr &= ~MASK_CPSR_T; // Clear T bit in CPSR
+
+ if (new_cpsr != curr_cpsr) {
+ if (!reg_ctx->WriteRegisterFromUnsigned(cpsr_reg_info, new_cpsr))
+ return false;
+ }
+
+ function_addr &=
+ ~1ull; // clear bit zero since the CPSR will take care of the mode for us
+
+ // Update the sp - stack pointer - to be aligned to 16-bytes
+ sp &= ~(0xfull);
+ if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_num, sp))
+ return false;
+
+ // Set "pc" to the address requested
+ if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_num, function_addr))
+ return false;
+
+ return true;
+}
+
+bool ABIMacOSX_arm::GetArgumentValues(Thread &thread, ValueList &values) const {
+ uint32_t num_values = values.GetSize();
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ // For now, assume that the types in the AST values come from the Target's
+ // scratch AST.
+
+ // Extract the register context so we can read arguments from registers
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+
+ if (!reg_ctx)
+ return false;
+
+ addr_t sp = 0;
+
+ for (uint32_t value_idx = 0; value_idx < num_values; ++value_idx) {
+ // We currently only support extracting values with Clang QualTypes. Do we
+ // care about others?
+ Value *value = values.GetValueAtIndex(value_idx);
+
+ if (!value)
+ return false;
+
+ CompilerType compiler_type = value->GetCompilerType();
+ if (compiler_type) {
+ bool is_signed = false;
+ size_t bit_width = 0;
+ llvm::Optional<uint64_t> bit_size = compiler_type.GetBitSize(&thread);
+ if (!bit_size)
+ return false;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed))
+ bit_width = *bit_size;
+ else if (compiler_type.IsPointerOrReferenceType())
+ bit_width = *bit_size;
+ else
+ // We only handle integer, pointer and reference types currently...
+ return false;
+
+ if (bit_width <= (exe_ctx.GetProcessRef().GetAddressByteSize() * 8)) {
+ if (value_idx < 4) {
+ // Arguments 1-4 are in r0-r3...
+ const RegisterInfo *arg_reg_info = nullptr;
+ // Search by generic ID first, then fall back to by name
+ uint32_t arg_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + value_idx);
+ if (arg_reg_num != LLDB_INVALID_REGNUM) {
+ arg_reg_info = reg_ctx->GetRegisterInfoAtIndex(arg_reg_num);
+ } else {
+ switch (value_idx) {
+ case 0:
+ arg_reg_info = reg_ctx->GetRegisterInfoByName("r0");
+ break;
+ case 1:
+ arg_reg_info = reg_ctx->GetRegisterInfoByName("r1");
+ break;
+ case 2:
+ arg_reg_info = reg_ctx->GetRegisterInfoByName("r2");
+ break;
+ case 3:
+ arg_reg_info = reg_ctx->GetRegisterInfoByName("r3");
+ break;
+ }
+ }
+
+ if (arg_reg_info) {
+ RegisterValue reg_value;
+
+ if (reg_ctx->ReadRegister(arg_reg_info, reg_value)) {
+ if (is_signed)
+ reg_value.SignExtend(bit_width);
+ if (!reg_value.GetScalarValue(value->GetScalar()))
+ return false;
+ continue;
+ }
+ }
+ return false;
+ } else {
+ if (sp == 0) {
+ // Read the stack pointer if it already hasn't been read
+ sp = reg_ctx->GetSP(0);
+ if (sp == 0)
+ return false;
+ }
+
+ // Arguments 5 on up are on the stack
+ const uint32_t arg_byte_size = (bit_width + (8 - 1)) / 8;
+ Status error;
+ if (!exe_ctx.GetProcessRef().ReadScalarIntegerFromMemory(
+ sp, arg_byte_size, is_signed, value->GetScalar(), error))
+ return false;
+
+ sp += arg_byte_size;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+bool ABIMacOSX_arm::IsArmv7kProcess() const {
+ bool is_armv7k = false;
+ ProcessSP process_sp(GetProcessSP());
+ if (process_sp) {
+ const ArchSpec &arch(process_sp->GetTarget().GetArchitecture());
+ const ArchSpec::Core system_core = arch.GetCore();
+ if (system_core == ArchSpec::eCore_arm_armv7k) {
+ is_armv7k = true;
+ }
+ }
+ return is_armv7k;
+}
+
+ValueObjectSP ABIMacOSX_arm::GetReturnValueObjectImpl(
+ Thread &thread, lldb_private::CompilerType &compiler_type) const {
+ Value value;
+ ValueObjectSP return_valobj_sp;
+
+ if (!compiler_type)
+ return return_valobj_sp;
+
+ value.SetCompilerType(compiler_type);
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return return_valobj_sp;
+
+ bool is_signed;
+
+ // Get the pointer to the first stack argument so we have a place to start
+ // when reading data
+
+ const RegisterInfo *r0_reg_info = reg_ctx->GetRegisterInfoByName("r0", 0);
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed)) {
+ llvm::Optional<uint64_t> bit_width = compiler_type.GetBitSize(&thread);
+ if (!bit_width)
+ return return_valobj_sp;
+
+ switch (*bit_width) {
+ default:
+ return return_valobj_sp;
+ case 128:
+ if (IsArmv7kProcess()) {
+ // "A composite type not larger than 16 bytes is returned in r0-r3. The
+ // format is as if the result had been stored in memory at a word-
+ // aligned address and then loaded into r0-r3 with an ldm instruction"
+ {
+ const RegisterInfo *r1_reg_info =
+ reg_ctx->GetRegisterInfoByName("r1", 0);
+ const RegisterInfo *r2_reg_info =
+ reg_ctx->GetRegisterInfoByName("r2", 0);
+ const RegisterInfo *r3_reg_info =
+ reg_ctx->GetRegisterInfoByName("r3", 0);
+ if (r1_reg_info && r2_reg_info && r3_reg_info) {
+ llvm::Optional<uint64_t> byte_size =
+ compiler_type.GetByteSize(&thread);
+ if (!byte_size)
+ return return_valobj_sp;
+ ProcessSP process_sp(thread.GetProcess());
+ if (*byte_size <= r0_reg_info->byte_size + r1_reg_info->byte_size +
+ r2_reg_info->byte_size +
+ r3_reg_info->byte_size &&
+ process_sp) {
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order = process_sp->GetByteOrder();
+ RegisterValue r0_reg_value;
+ RegisterValue r1_reg_value;
+ RegisterValue r2_reg_value;
+ RegisterValue r3_reg_value;
+ if (reg_ctx->ReadRegister(r0_reg_info, r0_reg_value) &&
+ reg_ctx->ReadRegister(r1_reg_info, r1_reg_value) &&
+ reg_ctx->ReadRegister(r2_reg_info, r2_reg_value) &&
+ reg_ctx->ReadRegister(r3_reg_info, r3_reg_value)) {
+ Status error;
+ if (r0_reg_value.GetAsMemoryData(r0_reg_info,
+ heap_data_up->GetBytes() + 0,
+ 4, byte_order, error) &&
+ r1_reg_value.GetAsMemoryData(r1_reg_info,
+ heap_data_up->GetBytes() + 4,
+ 4, byte_order, error) &&
+ r2_reg_value.GetAsMemoryData(r2_reg_info,
+ heap_data_up->GetBytes() + 8,
+ 4, byte_order, error) &&
+ r3_reg_value.GetAsMemoryData(r3_reg_info,
+ heap_data_up->GetBytes() + 12,
+ 4, byte_order, error)) {
+ DataExtractor data(DataBufferSP(heap_data_up.release()),
+ byte_order,
+ process_sp->GetAddressByteSize());
+
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, compiler_type, ConstString(""), data);
+ return return_valobj_sp;
+ }
+ }
+ }
+ }
+ }
+ } else {
+ return return_valobj_sp;
+ }
+ break;
+ case 64: {
+ const RegisterInfo *r1_reg_info = reg_ctx->GetRegisterInfoByName("r1", 0);
+ uint64_t raw_value;
+ raw_value = reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX;
+ raw_value |= ((uint64_t)(reg_ctx->ReadRegisterAsUnsigned(r1_reg_info, 0) &
+ UINT32_MAX))
+ << 32;
+ if (is_signed)
+ value.GetScalar() = (int64_t)raw_value;
+ else
+ value.GetScalar() = (uint64_t)raw_value;
+ } break;
+ case 32:
+ if (is_signed)
+ value.GetScalar() = (int32_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX);
+ else
+ value.GetScalar() = (uint32_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX);
+ break;
+ case 16:
+ if (is_signed)
+ value.GetScalar() = (int16_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT16_MAX);
+ else
+ value.GetScalar() = (uint16_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT16_MAX);
+ break;
+ case 8:
+ if (is_signed)
+ value.GetScalar() = (int8_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT8_MAX);
+ else
+ value.GetScalar() = (uint8_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT8_MAX);
+ break;
+ }
+ } else if (compiler_type.IsPointerType()) {
+ uint32_t ptr =
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(r0_reg_info, 0) &
+ UINT32_MAX;
+ value.GetScalar() = ptr;
+ } else {
+ // not handled yet
+ return return_valobj_sp;
+ }
+
+ // If we get here, we have a valid Value, so make our ValueObject out of it:
+
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ return return_valobj_sp;
+}
+
+Status ABIMacOSX_arm::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType compiler_type = new_value_sp->GetCompilerType();
+ if (!compiler_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+
+ bool is_signed;
+ uint32_t count;
+ bool is_complex;
+
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+
+ bool set_it_simple = false;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ compiler_type.IsPointerType()) {
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+ lldb::offset_t offset = 0;
+ if (num_bytes <= 8) {
+ const RegisterInfo *r0_info = reg_ctx->GetRegisterInfoByName("r0", 0);
+ if (num_bytes <= 4) {
+ uint32_t raw_value = data.GetMaxU32(&offset, num_bytes);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(r0_info, raw_value))
+ set_it_simple = true;
+ } else {
+ uint32_t raw_value = data.GetMaxU32(&offset, 4);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(r0_info, raw_value)) {
+ const RegisterInfo *r1_info = reg_ctx->GetRegisterInfoByName("r1", 0);
+ uint32_t raw_value = data.GetMaxU32(&offset, num_bytes - offset);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(r1_info, raw_value))
+ set_it_simple = true;
+ }
+ }
+ } else if (num_bytes <= 16 && IsArmv7kProcess()) {
+ // "A composite type not larger than 16 bytes is returned in r0-r3. The
+ // format is as if the result had been stored in memory at a word-aligned
+ // address and then loaded into r0-r3 with an ldm instruction"
+
+ const RegisterInfo *r0_info = reg_ctx->GetRegisterInfoByName("r0", 0);
+ const RegisterInfo *r1_info = reg_ctx->GetRegisterInfoByName("r1", 0);
+ const RegisterInfo *r2_info = reg_ctx->GetRegisterInfoByName("r2", 0);
+ const RegisterInfo *r3_info = reg_ctx->GetRegisterInfoByName("r3", 0);
+ lldb::offset_t offset = 0;
+ uint32_t bytes_written = 4;
+ uint32_t raw_value = data.GetMaxU64(&offset, 4);
+ if (reg_ctx->WriteRegisterFromUnsigned(r0_info, raw_value) &&
+ bytes_written <= num_bytes) {
+ bytes_written += 4;
+ raw_value = data.GetMaxU64(&offset, 4);
+ if (bytes_written <= num_bytes &&
+ reg_ctx->WriteRegisterFromUnsigned(r1_info, raw_value)) {
+ bytes_written += 4;
+ raw_value = data.GetMaxU64(&offset, 4);
+ if (bytes_written <= num_bytes &&
+ reg_ctx->WriteRegisterFromUnsigned(r2_info, raw_value)) {
+ bytes_written += 4;
+ raw_value = data.GetMaxU64(&offset, 4);
+ if (bytes_written <= num_bytes &&
+ reg_ctx->WriteRegisterFromUnsigned(r3_info, raw_value)) {
+ set_it_simple = true;
+ }
+ }
+ }
+ }
+ } else {
+ error.SetErrorString("We don't support returning longer than 64 bit "
+ "integer values at present.");
+ }
+ } else if (compiler_type.IsFloatingPointType(count, is_complex)) {
+ if (is_complex)
+ error.SetErrorString(
+ "We don't support returning complex values at present");
+ else
+ error.SetErrorString(
+ "We don't support returning float values at present");
+ }
+
+ if (!set_it_simple)
+ error.SetErrorString(
+ "We only support setting simple integer return types at present.");
+
+ return error;
+}
+
+bool ABIMacOSX_arm::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t lr_reg_num = dwarf_lr;
+ uint32_t sp_reg_num = dwarf_sp;
+ uint32_t pc_reg_num = dwarf_pc;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our Call Frame Address is the stack pointer value
+ row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 0);
+
+ // The previous PC is in the LR
+ row->SetRegisterLocationToRegister(pc_reg_num, lr_reg_num, true);
+ unwind_plan.AppendRow(row);
+
+ // All other registers are the same.
+
+ unwind_plan.SetSourceName("arm at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+
+ return true;
+}
+
+bool ABIMacOSX_arm::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t fp_reg_num =
+ dwarf_r7; // apple uses r7 for all frames. Normal arm uses r11
+ uint32_t pc_reg_num = dwarf_pc;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ const int32_t ptr_size = 4;
+
+ row->GetCFAValue().SetIsRegisterPlusOffset(fp_reg_num, 2 * ptr_size);
+ row->SetOffset(0);
+
+ row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("arm-apple-ios default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+
+ return true;
+}
+
+// cf. "ARMv6 Function Calling Conventions"
+// https://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARMv6FunctionCallingConventions.html
+// and "ARMv7 Function Calling Conventions"
+// https://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARMv7FunctionCallingConventions.html
+
+// ARMv7 on iOS general purpose reg rules:
+// r0-r3 not preserved (used for argument passing)
+// r4-r6 preserved
+// r7 preserved (frame pointer)
+// r8 preserved
+// r9 not preserved (usable as volatile scratch register with iOS 3.x and
+// later)
+// r10-r11 preserved
+// r12 not presrved
+// r13 preserved (stack pointer)
+// r14 not preserved (link register)
+// r15 preserved (pc)
+// cpsr not preserved (different rules for different bits)
+
+// ARMv7 on iOS floating point rules:
+// d0-d7 not preserved (aka s0-s15, q0-q3)
+// d8-d15 preserved (aka s16-s31, q4-q7)
+// d16-d31 not preserved (aka q8-q15)
+
+bool ABIMacOSX_arm::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ if (reg_info) {
+ // Volatile registers are: r0, r1, r2, r3, r9, r12, r13 (aka sp)
+ const char *name = reg_info->name;
+ if (name[0] == 'r') {
+ switch (name[1]) {
+ case '0':
+ return name[2] == '\0'; // r0
+ case '1':
+ switch (name[2]) {
+ case '\0':
+ return true; // r1
+ case '2':
+ case '3':
+ return name[3] == '\0'; // r12, r13 (sp)
+ default:
+ break;
+ }
+ break;
+
+ case '2':
+ return name[2] == '\0'; // r2
+ case '3':
+ return name[2] == '\0'; // r3
+ case '9':
+ return name[2] == '\0'; // r9 (apple-ios only...)
+
+ break;
+ }
+ } else if (name[0] == 'd') {
+ switch (name[1]) {
+ case '0':
+ return name[2] == '\0'; // d0 is volatile
+
+ case '1':
+ switch (name[2]) {
+ case '\0':
+ return true; // d1 is volatile
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return name[3] == '\0'; // d16 - d19 are volatile
+ default:
+ break;
+ }
+ break;
+
+ case '2':
+ switch (name[2]) {
+ case '\0':
+ return true; // d2 is volatile
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return name[3] == '\0'; // d20 - d29 are volatile
+ default:
+ break;
+ }
+ break;
+
+ case '3':
+ switch (name[2]) {
+ case '\0':
+ return true; // d3 is volatile
+ case '0':
+ case '1':
+ return name[3] == '\0'; // d30 - d31 are volatile
+ default:
+ break;
+ }
+ break;
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ return name[2] == '\0'; // d4 - d7 are volatile
+
+ default:
+ break;
+ }
+ } else if (name[0] == 's') {
+ switch (name[1]) {
+ case '0':
+ return name[2] == '\0'; // s0 is volatile
+
+ case '1':
+ switch (name[2]) {
+ case '\0':
+ return true; // s1 is volatile
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ return name[3] == '\0'; // s10 - s15 are volatile
+ default:
+ break;
+ }
+ break;
+
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return name[2] == '\0'; // s2 - s9 are volatile
+
+ default:
+ break;
+ }
+ } else if (name[0] == 'q') {
+ switch (name[1]) {
+ case '1':
+ switch (name[2]) {
+ case '\0':
+ return true; // q1 is volatile
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ return true; // q10-q15 are volatile
+ default:
+ break;
+ };
+ break;
+ case '0':
+ case '2':
+ case '3':
+ return name[2] == '\0'; // q0-q3 are volatile
+ case '8':
+ case '9':
+ return name[2] == '\0'; // q8-q9 are volatile
+ default:
+ break;
+ }
+ } else if (name[0] == 's' && name[1] == 'p' && name[2] == '\0')
+ return true;
+ }
+ return false;
+}
+
+void ABIMacOSX_arm::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ "Mac OS X ABI for arm targets", CreateInstance);
+}
+
+void ABIMacOSX_arm::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ABIMacOSX_arm::GetPluginNameStatic() {
+ static ConstString g_name("macosx-arm");
+ return g_name;
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString ABIMacOSX_arm::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ABIMacOSX_arm::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h b/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h
new file mode 100644
index 000000000000..ac9ba00b9d91
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h
@@ -0,0 +1,93 @@
+//===-- ABIMacOSX_arm.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 liblldb_ABIMacOSX_arm_h_
+#define liblldb_ABIMacOSX_arm_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABIMacOSX_arm : public lldb_private::ABI {
+public:
+ ~ABIMacOSX_arm() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t func_addr, lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are are 4 byte aligned
+ if (cfa & (4ull - 1ull))
+ return false; // Not 4 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ // Just make sure the address is a valid 32 bit address. Bit zero
+ // might be set due to Thumb function calls, so don't enforce 2 byte
+ // alignment
+ return pc <= UINT32_MAX;
+ }
+
+ lldb::addr_t FixCodeAddress(lldb::addr_t pc) override {
+ // ARM uses bit zero to signify a code address is thumb, so we must
+ // strip bit zero in any code addresses.
+ return pc & ~(lldb::addr_t)1;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ bool IsArmv7kProcess() const;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ // PluginInterface protocol
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const override;
+
+private:
+ ABIMacOSX_arm(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABIMacOSX_arm_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.cpp
new file mode 100644
index 000000000000..368e37213249
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.cpp
@@ -0,0 +1,2447 @@
+//===-- ABIMacOSX_arm64.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 "ABIMacOSX_arm64.h"
+
+#include <vector>
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+
+#include "Utility/ARM64_DWARF_Registers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static const char *pluginDesc = "Mac OS X ABI for arm64 targets";
+
+static RegisterInfo g_register_infos[] = {
+ // NAME ALT SZ OFF ENCODING FORMAT
+ // EH_FRAME DWARF GENERIC
+ // PROCESS PLUGIN LLDB NATIVE
+ // ========== ======= == === ============= ===================
+ // =================== ====================== ===========================
+ // ======================= ======================
+ {"x0",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x0, LLDB_REGNUM_GENERIC_ARG1,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x1",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x1, LLDB_REGNUM_GENERIC_ARG2,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x2",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x2, LLDB_REGNUM_GENERIC_ARG3,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x3",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x3, LLDB_REGNUM_GENERIC_ARG4,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x4",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x4, LLDB_REGNUM_GENERIC_ARG5,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x5",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x5, LLDB_REGNUM_GENERIC_ARG6,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x6",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x6, LLDB_REGNUM_GENERIC_ARG7,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x7",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x7, LLDB_REGNUM_GENERIC_ARG8,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x8",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x8, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x9",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x9, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x10",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x10, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x11",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x11, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x12",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x12, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x13",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x13, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x14",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x14, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x15",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x15, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x16",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x16, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x17",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x17, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x18",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x18, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x19",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x19, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x20",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x20, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x21",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x21, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x22",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x22, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x23",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x23, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x24",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x24, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x25",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x25, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x26",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x26, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x27",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x27, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x28",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x28, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fp",
+ "x29",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x29, LLDB_REGNUM_GENERIC_FP,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lr",
+ "x30",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x30, LLDB_REGNUM_GENERIC_RA,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"sp",
+ "x31",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x31, LLDB_REGNUM_GENERIC_SP,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"pc",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::pc, LLDB_REGNUM_GENERIC_PC,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cpsr",
+ "psr",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::cpsr, LLDB_REGNUM_GENERIC_FLAGS,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"v0",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v0, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v1",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v1, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v2",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v2, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v3",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v3, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v4",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v4, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v5",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v5, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v6",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v6, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v7",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v7, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v8",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v8, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v9",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v9, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v10",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v10, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v11",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v11, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v12",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v12, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v13",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v13, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v14",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v14, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v15",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v15, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v16",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v16, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v17",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v17, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v18",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v18, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v19",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v19, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v20",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v20, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v21",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v21, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v22",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v22, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v23",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v23, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v24",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v24, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v25",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v25, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v26",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v26, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v27",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v27, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v28",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v28, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v29",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v29, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v30",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v30, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v31",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v31, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"fpsr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fpcr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"s0",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s1",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s2",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s3",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s4",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s5",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s6",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s7",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s8",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s9",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s10",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s11",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s12",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s13",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s14",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s15",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s16",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s17",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s18",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s19",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s20",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s21",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s22",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s23",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s24",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s25",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s26",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s27",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s28",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s29",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s30",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s31",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"d0",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d1",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d2",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d3",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d4",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d5",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d6",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d7",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d8",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d9",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d10",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d11",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d12",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d13",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d14",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d15",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d16",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d17",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d18",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d19",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d20",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d21",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d22",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d23",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d24",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d25",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d26",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d27",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d28",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d29",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d30",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d31",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0}};
+
+static const uint32_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos);
+static bool g_register_info_names_constified = false;
+
+const lldb_private::RegisterInfo *
+ABIMacOSX_arm64::GetRegisterInfoArray(uint32_t &count) {
+ // Make the C-string names and alt_names for the register infos into const
+ // C-string values by having the ConstString unique the names in the global
+ // constant C-string pool.
+ if (!g_register_info_names_constified) {
+ g_register_info_names_constified = true;
+ for (uint32_t i = 0; i < k_num_register_infos; ++i) {
+ if (g_register_infos[i].name)
+ g_register_infos[i].name =
+ ConstString(g_register_infos[i].name).GetCString();
+ if (g_register_infos[i].alt_name)
+ g_register_infos[i].alt_name =
+ ConstString(g_register_infos[i].alt_name).GetCString();
+ }
+ }
+ count = k_num_register_infos;
+ return g_register_infos;
+}
+
+size_t ABIMacOSX_arm64::GetRedZoneSize() const { return 128; }
+
+// Static Functions
+
+ABISP
+ABIMacOSX_arm64::CreateInstance(ProcessSP process_sp, const ArchSpec &arch) {
+ const llvm::Triple::ArchType arch_type = arch.GetTriple().getArch();
+ const llvm::Triple::VendorType vendor_type = arch.GetTriple().getVendor();
+
+ if (vendor_type == llvm::Triple::Apple) {
+ if (arch_type == llvm::Triple::aarch64) {
+ return ABISP(new ABIMacOSX_arm64(process_sp));
+ }
+ }
+
+ return ABISP();
+}
+
+bool ABIMacOSX_arm64::PrepareTrivialCall(
+ Thread &thread, lldb::addr_t sp, lldb::addr_t func_addr,
+ lldb::addr_t return_addr, llvm::ArrayRef<lldb::addr_t> args) const {
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ StreamString s;
+ s.Printf("ABISysV_x86_64::PrepareTrivialCall (tid = 0x%" PRIx64
+ ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64
+ ", return_addr = 0x%" PRIx64,
+ thread.GetID(), (uint64_t)sp, (uint64_t)func_addr,
+ (uint64_t)return_addr);
+
+ for (size_t i = 0; i < args.size(); ++i)
+ s.Printf(", arg%d = 0x%" PRIx64, static_cast<int>(i + 1), args[i]);
+ s.PutCString(")");
+ log->PutString(s.GetString());
+ }
+
+ const uint32_t pc_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ const uint32_t sp_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+ const uint32_t ra_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA);
+
+ // x0 - x7 contain first 8 simple args
+ if (args.size() > 8) // TODO handle more than 6 arguments
+ return false;
+
+ for (size_t i = 0; i < args.size(); ++i) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + i);
+ if (log)
+ log->Printf("About to write arg%d (0x%" PRIx64 ") into %s",
+ static_cast<int>(i + 1), args[i], reg_info->name);
+ if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i]))
+ return false;
+ }
+
+ // Set "lr" to the return address
+ if (!reg_ctx->WriteRegisterFromUnsigned(
+ reg_ctx->GetRegisterInfoAtIndex(ra_reg_num), return_addr))
+ return false;
+
+ // Set "sp" to the requested value
+ if (!reg_ctx->WriteRegisterFromUnsigned(
+ reg_ctx->GetRegisterInfoAtIndex(sp_reg_num), sp))
+ return false;
+
+ // Set "pc" to the address requested
+ if (!reg_ctx->WriteRegisterFromUnsigned(
+ reg_ctx->GetRegisterInfoAtIndex(pc_reg_num), func_addr))
+ return false;
+
+ return true;
+}
+
+bool ABIMacOSX_arm64::GetArgumentValues(Thread &thread,
+ ValueList &values) const {
+ uint32_t num_values = values.GetSize();
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+
+ // Extract the register context so we can read arguments from registers
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+
+ if (!reg_ctx)
+ return false;
+
+ addr_t sp = 0;
+
+ for (uint32_t value_idx = 0; value_idx < num_values; ++value_idx) {
+ // We currently only support extracting values with Clang QualTypes. Do we
+ // care about others?
+ Value *value = values.GetValueAtIndex(value_idx);
+
+ if (!value)
+ return false;
+
+ CompilerType value_type = value->GetCompilerType();
+ llvm::Optional<uint64_t> bit_size = value_type.GetBitSize(&thread);
+ if (!bit_size)
+ return false;
+
+ bool is_signed = false;
+ size_t bit_width = 0;
+ if (value_type.IsIntegerOrEnumerationType(is_signed)) {
+ bit_width = *bit_size;
+ } else if (value_type.IsPointerOrReferenceType()) {
+ bit_width = *bit_size;
+ } else {
+ // We only handle integer, pointer and reference types currently...
+ return false;
+ }
+
+ if (bit_width <= (exe_ctx.GetProcessRef().GetAddressByteSize() * 8)) {
+ if (value_idx < 8) {
+ // Arguments 1-6 are in x0-x5...
+ const RegisterInfo *reg_info = nullptr;
+ // Search by generic ID first, then fall back to by name
+ uint32_t arg_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + value_idx);
+ if (arg_reg_num != LLDB_INVALID_REGNUM) {
+ reg_info = reg_ctx->GetRegisterInfoAtIndex(arg_reg_num);
+ } else {
+ switch (value_idx) {
+ case 0:
+ reg_info = reg_ctx->GetRegisterInfoByName("x0");
+ break;
+ case 1:
+ reg_info = reg_ctx->GetRegisterInfoByName("x1");
+ break;
+ case 2:
+ reg_info = reg_ctx->GetRegisterInfoByName("x2");
+ break;
+ case 3:
+ reg_info = reg_ctx->GetRegisterInfoByName("x3");
+ break;
+ case 4:
+ reg_info = reg_ctx->GetRegisterInfoByName("x4");
+ break;
+ case 5:
+ reg_info = reg_ctx->GetRegisterInfoByName("x5");
+ break;
+ case 6:
+ reg_info = reg_ctx->GetRegisterInfoByName("x6");
+ break;
+ case 7:
+ reg_info = reg_ctx->GetRegisterInfoByName("x7");
+ break;
+ }
+ }
+
+ if (reg_info) {
+ RegisterValue reg_value;
+
+ if (reg_ctx->ReadRegister(reg_info, reg_value)) {
+ if (is_signed)
+ reg_value.SignExtend(bit_width);
+ if (!reg_value.GetScalarValue(value->GetScalar()))
+ return false;
+ continue;
+ }
+ }
+ return false;
+ } else {
+ if (sp == 0) {
+ // Read the stack pointer if we already haven't read it
+ sp = reg_ctx->GetSP(0);
+ if (sp == 0)
+ return false;
+ }
+
+ // Arguments 5 on up are on the stack
+ const uint32_t arg_byte_size = (bit_width + (8 - 1)) / 8;
+ Status error;
+ if (!exe_ctx.GetProcessRef().ReadScalarIntegerFromMemory(
+ sp, arg_byte_size, is_signed, value->GetScalar(), error))
+ return false;
+
+ sp += arg_byte_size;
+ // Align up to the next 8 byte boundary if needed
+ if (sp % 8) {
+ sp >>= 3;
+ sp += 1;
+ sp <<= 3;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+Status
+ABIMacOSX_arm64::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType return_value_type = new_value_sp->GetCompilerType();
+ if (!return_value_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+
+ if (reg_ctx) {
+ DataExtractor data;
+ Status data_error;
+ const uint64_t byte_size = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+
+ const uint32_t type_flags = return_value_type.GetTypeInfo(nullptr);
+ if (type_flags & eTypeIsScalar || type_flags & eTypeIsPointer) {
+ if (type_flags & eTypeIsInteger || type_flags & eTypeIsPointer) {
+ // Extract the register context so we can read arguments from registers
+ lldb::offset_t offset = 0;
+ if (byte_size <= 16) {
+ const RegisterInfo *x0_info = reg_ctx->GetRegisterInfoByName("x0", 0);
+ if (byte_size <= 8) {
+ uint64_t raw_value = data.GetMaxU64(&offset, byte_size);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(x0_info, raw_value))
+ error.SetErrorString("failed to write register x0");
+ } else {
+ uint64_t raw_value = data.GetMaxU64(&offset, 8);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(x0_info, raw_value)) {
+ const RegisterInfo *x1_info =
+ reg_ctx->GetRegisterInfoByName("x1", 0);
+ raw_value = data.GetMaxU64(&offset, byte_size - offset);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(x1_info, raw_value))
+ error.SetErrorString("failed to write register x1");
+ }
+ }
+ } else {
+ error.SetErrorString("We don't support returning longer than 128 bit "
+ "integer values at present.");
+ }
+ } else if (type_flags & eTypeIsFloat) {
+ if (type_flags & eTypeIsComplex) {
+ // Don't handle complex yet.
+ error.SetErrorString(
+ "returning complex float values are not supported");
+ } else {
+ const RegisterInfo *v0_info = reg_ctx->GetRegisterInfoByName("v0", 0);
+
+ if (v0_info) {
+ if (byte_size <= 16) {
+ if (byte_size <= RegisterValue::GetMaxByteSize()) {
+ RegisterValue reg_value;
+ error = reg_value.SetValueFromData(v0_info, data, 0, true);
+ if (error.Success()) {
+ if (!reg_ctx->WriteRegister(v0_info, reg_value))
+ error.SetErrorString("failed to write register v0");
+ }
+ } else {
+ error.SetErrorStringWithFormat(
+ "returning float values with a byte size of %" PRIu64
+ " are not supported",
+ byte_size);
+ }
+ } else {
+ error.SetErrorString("returning float values longer than 128 "
+ "bits are not supported");
+ }
+ } else {
+ error.SetErrorString("v0 register is not available on this target");
+ }
+ }
+ }
+ } else if (type_flags & eTypeIsVector) {
+ if (byte_size > 0) {
+ const RegisterInfo *v0_info = reg_ctx->GetRegisterInfoByName("v0", 0);
+
+ if (v0_info) {
+ if (byte_size <= v0_info->byte_size) {
+ RegisterValue reg_value;
+ error = reg_value.SetValueFromData(v0_info, data, 0, true);
+ if (error.Success()) {
+ if (!reg_ctx->WriteRegister(v0_info, reg_value))
+ error.SetErrorString("failed to write register v0");
+ }
+ }
+ }
+ }
+ }
+ } else {
+ error.SetErrorString("no registers are available");
+ }
+
+ return error;
+}
+
+bool ABIMacOSX_arm64::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t lr_reg_num = arm64_dwarf::lr;
+ uint32_t sp_reg_num = arm64_dwarf::sp;
+ uint32_t pc_reg_num = arm64_dwarf::pc;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our previous Call Frame Address is the stack pointer
+ row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 0);
+
+ // Our previous PC is in the LR
+ row->SetRegisterLocationToRegister(pc_reg_num, lr_reg_num, true);
+
+ unwind_plan.AppendRow(row);
+
+ // All other registers are the same.
+
+ unwind_plan.SetSourceName("arm64 at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+
+ return true;
+}
+
+bool ABIMacOSX_arm64::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t fp_reg_num = arm64_dwarf::fp;
+ uint32_t pc_reg_num = arm64_dwarf::pc;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ const int32_t ptr_size = 8;
+
+ row->GetCFAValue().SetIsRegisterPlusOffset(fp_reg_num, 2 * ptr_size);
+ row->SetOffset(0);
+
+ row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("arm64-apple-darwin default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ return true;
+}
+
+// AAPCS64 (Procedure Call Standard for the ARM 64-bit Architecture) says
+// registers x19 through x28 and sp are callee preserved. v8-v15 are non-
+// volatile (and specifically only the lower 8 bytes of these regs), the rest
+// of the fp/SIMD registers are volatile.
+
+// We treat x29 as callee preserved also, else the unwinder won't try to
+// retrieve fp saves.
+
+bool ABIMacOSX_arm64::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ if (reg_info) {
+ const char *name = reg_info->name;
+
+ // Sometimes we'll be called with the "alternate" name for these registers;
+ // recognize them as non-volatile.
+
+ if (name[0] == 'p' && name[1] == 'c') // pc
+ return false;
+ if (name[0] == 'f' && name[1] == 'p') // fp
+ return false;
+ if (name[0] == 's' && name[1] == 'p') // sp
+ return false;
+ if (name[0] == 'l' && name[1] == 'r') // lr
+ return false;
+
+ if (name[0] == 'x') {
+ // Volatile registers: x0-x18, x30 (lr)
+ // Return false for the non-volatile gpr regs, true for everything else
+ switch (name[1]) {
+ case '1':
+ switch (name[2]) {
+ case '9':
+ return false; // x19 is non-volatile
+ default:
+ return true;
+ }
+ break;
+ case '2':
+ switch (name[2]) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ return false; // x20 - 28 are non-volatile
+ case '9':
+ return false; // x29 aka fp treat as non-volatile on Darwin
+ default:
+ return true;
+ }
+ case '3': // x30 aka lr treat as non-volatile
+ if (name[2] == '0')
+ return false;
+ break;
+ default:
+ return true;
+ }
+ } else if (name[0] == 'v' || name[0] == 's' || name[0] == 'd') {
+ // Volatile registers: v0-7, v16-v31
+ // Return false for non-volatile fp/SIMD regs, true for everything else
+ switch (name[1]) {
+ case '8':
+ case '9':
+ return false; // v8-v9 are non-volatile
+ case '1':
+ switch (name[2]) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ return false; // v10-v15 are non-volatile
+ default:
+ return true;
+ }
+ default:
+ return true;
+ }
+ }
+ }
+ return true;
+}
+
+static bool LoadValueFromConsecutiveGPRRegisters(
+ ExecutionContext &exe_ctx, RegisterContext *reg_ctx,
+ const CompilerType &value_type,
+ bool is_return_value, // false => parameter, true => return value
+ uint32_t &NGRN, // NGRN (see ABI documentation)
+ uint32_t &NSRN, // NSRN (see ABI documentation)
+ DataExtractor &data) {
+ llvm::Optional<uint64_t> byte_size = value_type.GetByteSize(nullptr);
+ if (!byte_size || *byte_size == 0)
+ return false;
+
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order = exe_ctx.GetProcessRef().GetByteOrder();
+ Status error;
+
+ CompilerType base_type;
+ const uint32_t homogeneous_count =
+ value_type.IsHomogeneousAggregate(&base_type);
+ if (homogeneous_count > 0 && homogeneous_count <= 8) {
+ // Make sure we have enough registers
+ if (NSRN < 8 && (8 - NSRN) >= homogeneous_count) {
+ if (!base_type)
+ return false;
+ llvm::Optional<uint64_t> base_byte_size = base_type.GetByteSize(nullptr);
+ if (!base_byte_size)
+ return false;
+ uint32_t data_offset = 0;
+
+ for (uint32_t i = 0; i < homogeneous_count; ++i) {
+ char v_name[8];
+ ::snprintf(v_name, sizeof(v_name), "v%u", NSRN);
+ const RegisterInfo *reg_info =
+ reg_ctx->GetRegisterInfoByName(v_name, 0);
+ if (reg_info == nullptr)
+ return false;
+
+ if (*base_byte_size > reg_info->byte_size)
+ return false;
+
+ RegisterValue reg_value;
+
+ if (!reg_ctx->ReadRegister(reg_info, reg_value))
+ return false;
+
+ // Make sure we have enough room in "heap_data_up"
+ if ((data_offset + *base_byte_size) <= heap_data_up->GetByteSize()) {
+ const size_t bytes_copied = reg_value.GetAsMemoryData(
+ reg_info, heap_data_up->GetBytes() + data_offset, *base_byte_size,
+ byte_order, error);
+ if (bytes_copied != *base_byte_size)
+ return false;
+ data_offset += bytes_copied;
+ ++NSRN;
+ } else
+ return false;
+ }
+ data.SetByteOrder(byte_order);
+ data.SetAddressByteSize(exe_ctx.GetProcessRef().GetAddressByteSize());
+ data.SetData(DataBufferSP(heap_data_up.release()));
+ return true;
+ }
+ }
+
+ const size_t max_reg_byte_size = 16;
+ if (*byte_size <= max_reg_byte_size) {
+ size_t bytes_left = *byte_size;
+ uint32_t data_offset = 0;
+ while (data_offset < *byte_size) {
+ if (NGRN >= 8)
+ return false;
+
+ uint32_t reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + NGRN);
+ if (reg_num == LLDB_INVALID_REGNUM)
+ return false;
+
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_num);
+ if (reg_info == nullptr)
+ return false;
+
+ RegisterValue reg_value;
+
+ if (!reg_ctx->ReadRegister(reg_info, reg_value))
+ return false;
+
+ const size_t curr_byte_size = std::min<size_t>(8, bytes_left);
+ const size_t bytes_copied = reg_value.GetAsMemoryData(
+ reg_info, heap_data_up->GetBytes() + data_offset, curr_byte_size,
+ byte_order, error);
+ if (bytes_copied == 0)
+ return false;
+ if (bytes_copied >= bytes_left)
+ break;
+ data_offset += bytes_copied;
+ bytes_left -= bytes_copied;
+ ++NGRN;
+ }
+ } else {
+ const RegisterInfo *reg_info = nullptr;
+ if (is_return_value) {
+ // We are assuming we are decoding this immediately after returning from
+ // a function call and that the address of the structure is in x8
+ reg_info = reg_ctx->GetRegisterInfoByName("x8", 0);
+ } else {
+ // We are assuming we are stopped at the first instruction in a function
+ // and that the ABI is being respected so all parameters appear where
+ // they should be (functions with no external linkage can legally violate
+ // the ABI).
+ if (NGRN >= 8)
+ return false;
+
+ uint32_t reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + NGRN);
+ if (reg_num == LLDB_INVALID_REGNUM)
+ return false;
+ reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_num);
+ if (reg_info == nullptr)
+ return false;
+ ++NGRN;
+ }
+
+ if (reg_info == nullptr)
+ return false;
+
+ const lldb::addr_t value_addr =
+ reg_ctx->ReadRegisterAsUnsigned(reg_info, LLDB_INVALID_ADDRESS);
+
+ if (value_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ if (exe_ctx.GetProcessRef().ReadMemory(
+ value_addr, heap_data_up->GetBytes(), heap_data_up->GetByteSize(),
+ error) != heap_data_up->GetByteSize()) {
+ return false;
+ }
+ }
+
+ data.SetByteOrder(byte_order);
+ data.SetAddressByteSize(exe_ctx.GetProcessRef().GetAddressByteSize());
+ data.SetData(DataBufferSP(heap_data_up.release()));
+ return true;
+}
+
+ValueObjectSP ABIMacOSX_arm64::GetReturnValueObjectImpl(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ Value value;
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ if (exe_ctx.GetTargetPtr() == nullptr || exe_ctx.GetProcessPtr() == nullptr)
+ return return_valobj_sp;
+
+ // value.SetContext (Value::eContextTypeClangType, return_compiler_type);
+ value.SetCompilerType(return_compiler_type);
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return return_valobj_sp;
+
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (!byte_size)
+ return return_valobj_sp;
+
+ const uint32_t type_flags = return_compiler_type.GetTypeInfo(nullptr);
+ if (type_flags & eTypeIsScalar || type_flags & eTypeIsPointer) {
+ value.SetValueType(Value::eValueTypeScalar);
+
+ bool success = false;
+ if (type_flags & eTypeIsInteger || type_flags & eTypeIsPointer) {
+ // Extract the register context so we can read arguments from registers
+ if (*byte_size <= 8) {
+ const RegisterInfo *x0_reg_info =
+ reg_ctx->GetRegisterInfoByName("x0", 0);
+ if (x0_reg_info) {
+ uint64_t raw_value =
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(x0_reg_info,
+ 0);
+ const bool is_signed = (type_flags & eTypeIsSigned) != 0;
+ switch (*byte_size) {
+ default:
+ break;
+ case 16: // uint128_t
+ // In register x0 and x1
+ {
+ const RegisterInfo *x1_reg_info =
+ reg_ctx->GetRegisterInfoByName("x1", 0);
+
+ if (x1_reg_info) {
+ if (*byte_size <=
+ x0_reg_info->byte_size + x1_reg_info->byte_size) {
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order =
+ exe_ctx.GetProcessRef().GetByteOrder();
+ RegisterValue x0_reg_value;
+ RegisterValue x1_reg_value;
+ if (reg_ctx->ReadRegister(x0_reg_info, x0_reg_value) &&
+ reg_ctx->ReadRegister(x1_reg_info, x1_reg_value)) {
+ Status error;
+ if (x0_reg_value.GetAsMemoryData(
+ x0_reg_info, heap_data_up->GetBytes() + 0, 8,
+ byte_order, error) &&
+ x1_reg_value.GetAsMemoryData(
+ x1_reg_info, heap_data_up->GetBytes() + 8, 8,
+ byte_order, error)) {
+ DataExtractor data(
+ DataBufferSP(heap_data_up.release()), byte_order,
+ exe_ctx.GetProcessRef().GetAddressByteSize());
+
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), data);
+ return return_valobj_sp;
+ }
+ }
+ }
+ }
+ }
+ break;
+ case sizeof(uint64_t):
+ if (is_signed)
+ value.GetScalar() = (int64_t)(raw_value);
+ else
+ value.GetScalar() = (uint64_t)(raw_value);
+ success = true;
+ break;
+
+ case sizeof(uint32_t):
+ if (is_signed)
+ value.GetScalar() = (int32_t)(raw_value & UINT32_MAX);
+ else
+ value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint16_t):
+ if (is_signed)
+ value.GetScalar() = (int16_t)(raw_value & UINT16_MAX);
+ else
+ value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint8_t):
+ if (is_signed)
+ value.GetScalar() = (int8_t)(raw_value & UINT8_MAX);
+ else
+ value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX);
+ success = true;
+ break;
+ }
+ }
+ }
+ } else if (type_flags & eTypeIsFloat) {
+ if (type_flags & eTypeIsComplex) {
+ // Don't handle complex yet.
+ } else {
+ if (*byte_size <= sizeof(long double)) {
+ const RegisterInfo *v0_reg_info =
+ reg_ctx->GetRegisterInfoByName("v0", 0);
+ RegisterValue v0_value;
+ if (reg_ctx->ReadRegister(v0_reg_info, v0_value)) {
+ DataExtractor data;
+ if (v0_value.GetData(data)) {
+ lldb::offset_t offset = 0;
+ if (*byte_size == sizeof(float)) {
+ value.GetScalar() = data.GetFloat(&offset);
+ success = true;
+ } else if (*byte_size == sizeof(double)) {
+ value.GetScalar() = data.GetDouble(&offset);
+ success = true;
+ } else if (*byte_size == sizeof(long double)) {
+ value.GetScalar() = data.GetLongDouble(&offset);
+ success = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (success)
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if (type_flags & eTypeIsVector) {
+ if (*byte_size > 0) {
+
+ const RegisterInfo *v0_info = reg_ctx->GetRegisterInfoByName("v0", 0);
+
+ if (v0_info) {
+ if (*byte_size <= v0_info->byte_size) {
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order = exe_ctx.GetProcessRef().GetByteOrder();
+ RegisterValue reg_value;
+ if (reg_ctx->ReadRegister(v0_info, reg_value)) {
+ Status error;
+ if (reg_value.GetAsMemoryData(v0_info, heap_data_up->GetBytes(),
+ heap_data_up->GetByteSize(),
+ byte_order, error)) {
+ DataExtractor data(DataBufferSP(heap_data_up.release()),
+ byte_order,
+ exe_ctx.GetProcessRef().GetAddressByteSize());
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), data);
+ }
+ }
+ }
+ }
+ }
+ } else if (type_flags & eTypeIsStructUnion || type_flags & eTypeIsClass) {
+ DataExtractor data;
+
+ uint32_t NGRN = 0; // Search ABI docs for NGRN
+ uint32_t NSRN = 0; // Search ABI docs for NSRN
+ const bool is_return_value = true;
+ if (LoadValueFromConsecutiveGPRRegisters(
+ exe_ctx, reg_ctx, return_compiler_type, is_return_value, NGRN, NSRN,
+ data)) {
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), data);
+ }
+ }
+ return return_valobj_sp;
+}
+
+void ABIMacOSX_arm64::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(), pluginDesc,
+ CreateInstance);
+}
+
+void ABIMacOSX_arm64::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+// PluginInterface protocol
+
+ConstString ABIMacOSX_arm64::GetPluginNameStatic() {
+ static ConstString g_plugin_name("ABIMacOSX_arm64");
+ return g_plugin_name;
+}
+
+uint32_t ABIMacOSX_arm64::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.h b/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.h
new file mode 100644
index 000000000000..bfacbcd54a94
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.h
@@ -0,0 +1,101 @@
+//===-- ABIMacOSX_arm64.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 liblldb_ABIMacOSX_arm64_h_
+#define liblldb_ABIMacOSX_arm64_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/lldb-private.h"
+
+class ABIMacOSX_arm64 : public lldb_private::ABI {
+public:
+ ~ABIMacOSX_arm64() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress,
+ lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ // The arm64 ABI requires that stack frames be 16 byte aligned.
+ // When there is a trap handler on the stack, e.g. _sigtramp in userland
+ // code, we've seen that the stack pointer is often not aligned properly
+ // before the handler is invoked. This means that lldb will stop the unwind
+ // early -- before the function which caused the trap.
+ //
+ // To work around this, we relax that alignment to be just word-size
+ // (8-bytes).
+ // Whitelisting the trap handlers for user space would be easy (_sigtramp) but
+ // in other environments there can be a large number of different functions
+ // involved in async traps.
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are are 8 byte aligned
+ if (cfa & (8ull - 1ull))
+ return false; // Not 8 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ if (pc & (4ull - 1ull))
+ return false; // Not 4 byte aligned
+
+ // Anything else if fair game..
+ return true;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ // PluginInterface protocol
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ lldb_private::ConstString GetPluginName() override {
+ return GetPluginNameStatic();
+ }
+
+ uint32_t GetPluginVersion() override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+protected:
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const override;
+
+private:
+ ABIMacOSX_arm64(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABIMacOSX_arm64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp
new file mode 100644
index 000000000000..67371b432ff8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp
@@ -0,0 +1,1130 @@
+//===-- ABIMacOSX_i386.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 "ABIMacOSX_i386.h"
+
+#include <vector>
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum {
+ ehframe_eax = 0,
+ ehframe_ecx,
+ ehframe_edx,
+ ehframe_ebx,
+ ehframe_ebp, // Different from DWARF the regnums - eh_frame esp/ebp had their
+ // regnums switched on i386 darwin
+ ehframe_esp, // Different from DWARF the regnums - eh_frame esp/ebp had their
+ // regnums switched on i386 darwin
+ 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
+};
+
+static RegisterInfo g_register_infos[] = {
+ // NAME ALT SZ OFF ENCODING FORMAT
+ // EH_FRAME DWARF GENERIC
+ // PROCESS PLUGIN LLDB NATIVE
+ // ====== ======= == === ============= ============
+ // ===================== ===================== ============================
+ // ==================== ======================
+ {"eax",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_eax, dwarf_eax, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ebx",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_ebx, dwarf_ebx, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ecx",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_ecx, dwarf_ecx, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"edx",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_edx, dwarf_edx, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"esi",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_esi, dwarf_esi, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"edi",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_edi, dwarf_edi, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ebp",
+ "fp",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_ebp, dwarf_ebp, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"esp",
+ "sp",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_esp, dwarf_esp, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"eip",
+ "pc",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_eip, dwarf_eip, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"eflags",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_FLAGS,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cs",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ss",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ds",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"es",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fs",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"gs",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm0",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_stmm0, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm1",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_stmm1, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm2",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_stmm2, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm3",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_stmm3, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm4",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_stmm4, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm5",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_stmm5, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm6",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_stmm6, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm7",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_stmm7, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fctrl",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fstat",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ftag",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fiseg",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fioff",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"foseg",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fooff",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fop",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm0",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_xmm0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm1",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_xmm1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm2",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_xmm2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm3",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_xmm3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm4",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_xmm4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm5",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_xmm5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm6",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_xmm6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm7",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_xmm7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"mxcsr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm0",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_ymm0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm1",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_ymm1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm2",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_ymm2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm3",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_ymm3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm4",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_ymm4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm5",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_ymm5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm6",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_ymm6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm7",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_ymm7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0}};
+
+static const uint32_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos);
+static bool g_register_info_names_constified = false;
+
+const lldb_private::RegisterInfo *
+ABIMacOSX_i386::GetRegisterInfoArray(uint32_t &count) {
+ // Make the C-string names and alt_names for the register infos into const
+ // C-string values by having the ConstString unique the names in the global
+ // constant C-string pool.
+ if (!g_register_info_names_constified) {
+ g_register_info_names_constified = true;
+ for (uint32_t i = 0; i < k_num_register_infos; ++i) {
+ if (g_register_infos[i].name)
+ g_register_infos[i].name =
+ ConstString(g_register_infos[i].name).GetCString();
+ if (g_register_infos[i].alt_name)
+ g_register_infos[i].alt_name =
+ ConstString(g_register_infos[i].alt_name).GetCString();
+ }
+ }
+ count = k_num_register_infos;
+ return g_register_infos;
+}
+
+size_t ABIMacOSX_i386::GetRedZoneSize() const { return 0; }
+
+// Static Functions
+
+ABISP
+ABIMacOSX_i386::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) {
+ if ((arch.GetTriple().getArch() == llvm::Triple::x86) &&
+ (arch.GetTriple().isMacOSX() || arch.GetTriple().isiOS() ||
+ arch.GetTriple().isWatchOS())) {
+ return ABISP(new ABIMacOSX_i386(process_sp));
+ }
+ return ABISP();
+}
+
+bool ABIMacOSX_i386::PrepareTrivialCall(Thread &thread, addr_t sp,
+ addr_t func_addr, addr_t return_addr,
+ llvm::ArrayRef<addr_t> args) const {
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+ uint32_t pc_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ uint32_t sp_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+
+ // When writing a register value down to memory, the register info used to
+ // write memory just needs to have the correct size of a 32 bit register, the
+ // actual register it pertains to is not important, just the size needs to be
+ // correct. Here we use "eax"...
+ const RegisterInfo *reg_info_32 = reg_ctx->GetRegisterInfoByName("eax");
+ if (!reg_info_32)
+ return false; // TODO this should actually never happen
+
+ // Make room for the argument(s) on the stack
+
+ Status error;
+ RegisterValue reg_value;
+
+ // Write any arguments onto the stack
+ sp -= 4 * args.size();
+
+ // Align the SP
+ sp &= ~(16ull - 1ull); // 16-byte alignment
+
+ addr_t arg_pos = sp;
+
+ for (addr_t arg : args) {
+ reg_value.SetUInt32(arg);
+ error = reg_ctx->WriteRegisterValueToMemory(
+ reg_info_32, arg_pos, reg_info_32->byte_size, reg_value);
+ if (error.Fail())
+ return false;
+ arg_pos += 4;
+ }
+
+ // The return address is pushed onto the stack (yes after we just set the
+ // alignment above!).
+ sp -= 4;
+ reg_value.SetUInt32(return_addr);
+ error = reg_ctx->WriteRegisterValueToMemory(
+ reg_info_32, sp, reg_info_32->byte_size, reg_value);
+ if (error.Fail())
+ return false;
+
+ // %esp is set to the actual stack value.
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_num, sp))
+ return false;
+
+ // %eip is set to the address of the called function.
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_num, func_addr))
+ return false;
+
+ return true;
+}
+
+static bool ReadIntegerArgument(Scalar &scalar, unsigned int bit_width,
+ bool is_signed, Process *process,
+ addr_t &current_stack_argument) {
+
+ uint32_t byte_size = (bit_width + (8 - 1)) / 8;
+ Status error;
+ if (process->ReadScalarIntegerFromMemory(current_stack_argument, byte_size,
+ is_signed, scalar, error)) {
+ current_stack_argument += byte_size;
+ return true;
+ }
+ return false;
+}
+
+bool ABIMacOSX_i386::GetArgumentValues(Thread &thread,
+ ValueList &values) const {
+ unsigned int num_values = values.GetSize();
+ unsigned int value_index;
+
+ // Get the pointer to the first stack argument so we have a place to start
+ // when reading data
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+
+ if (!reg_ctx)
+ return false;
+
+ addr_t sp = reg_ctx->GetSP(0);
+
+ if (!sp)
+ return false;
+
+ addr_t current_stack_argument = sp + 4; // jump over return address
+
+ for (value_index = 0; value_index < num_values; ++value_index) {
+ Value *value = values.GetValueAtIndex(value_index);
+
+ if (!value)
+ return false;
+
+ // We currently only support extracting values with Clang QualTypes. Do we
+ // care about others?
+ CompilerType compiler_type(value->GetCompilerType());
+ llvm::Optional<uint64_t> bit_size = compiler_type.GetBitSize(&thread);
+ if (bit_size) {
+ bool is_signed;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed))
+ ReadIntegerArgument(value->GetScalar(), *bit_size, is_signed,
+ thread.GetProcess().get(), current_stack_argument);
+ else if (compiler_type.IsPointerType())
+ ReadIntegerArgument(value->GetScalar(), *bit_size, false,
+ thread.GetProcess().get(), current_stack_argument);
+ }
+ }
+
+ return true;
+}
+
+Status ABIMacOSX_i386::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType compiler_type = new_value_sp->GetCompilerType();
+ if (!compiler_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+
+ bool is_signed;
+ uint32_t count;
+ bool is_complex;
+
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+
+ bool set_it_simple = false;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ compiler_type.IsPointerType()) {
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+ lldb::offset_t offset = 0;
+ if (num_bytes <= 8) {
+ const RegisterInfo *eax_info = reg_ctx->GetRegisterInfoByName("eax", 0);
+ if (num_bytes <= 4) {
+ uint32_t raw_value = data.GetMaxU32(&offset, num_bytes);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(eax_info, raw_value))
+ set_it_simple = true;
+ } else {
+ uint32_t raw_value = data.GetMaxU32(&offset, 4);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(eax_info, raw_value)) {
+ const RegisterInfo *edx_info =
+ reg_ctx->GetRegisterInfoByName("edx", 0);
+ uint32_t raw_value = data.GetMaxU32(&offset, num_bytes - offset);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(edx_info, raw_value))
+ set_it_simple = true;
+ }
+ }
+ } else {
+ error.SetErrorString("We don't support returning longer than 64 bit "
+ "integer values at present.");
+ }
+ } else if (compiler_type.IsFloatingPointType(count, is_complex)) {
+ if (is_complex)
+ error.SetErrorString(
+ "We don't support returning complex values at present");
+ else
+ error.SetErrorString(
+ "We don't support returning float values at present");
+ }
+
+ if (!set_it_simple)
+ error.SetErrorString(
+ "We only support setting simple integer return types at present.");
+
+ return error;
+}
+
+ValueObjectSP
+ABIMacOSX_i386::GetReturnValueObjectImpl(Thread &thread,
+ CompilerType &compiler_type) const {
+ Value value;
+ ValueObjectSP return_valobj_sp;
+
+ if (!compiler_type)
+ return return_valobj_sp;
+
+ // value.SetContext (Value::eContextTypeClangType,
+ // compiler_type.GetOpaqueQualType());
+ value.SetCompilerType(compiler_type);
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return return_valobj_sp;
+
+ bool is_signed;
+
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed)) {
+ llvm::Optional<uint64_t> bit_width = compiler_type.GetBitSize(&thread);
+ if (!bit_width)
+ return return_valobj_sp;
+ unsigned eax_id =
+ reg_ctx->GetRegisterInfoByName("eax", 0)->kinds[eRegisterKindLLDB];
+ unsigned edx_id =
+ reg_ctx->GetRegisterInfoByName("edx", 0)->kinds[eRegisterKindLLDB];
+
+ switch (*bit_width) {
+ default:
+ case 128:
+ // Scalar can't hold 128-bit literals, so we don't handle this
+ return return_valobj_sp;
+ case 64:
+ uint64_t raw_value;
+ raw_value =
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xffffffff;
+ raw_value |=
+ (thread.GetRegisterContext()->ReadRegisterAsUnsigned(edx_id, 0) &
+ 0xffffffff)
+ << 32;
+ if (is_signed)
+ value.GetScalar() = (int64_t)raw_value;
+ else
+ value.GetScalar() = (uint64_t)raw_value;
+ break;
+ case 32:
+ if (is_signed)
+ value.GetScalar() = (int32_t)(
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xffffffff);
+ else
+ value.GetScalar() = (uint32_t)(
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xffffffff);
+ break;
+ case 16:
+ if (is_signed)
+ value.GetScalar() = (int16_t)(
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xffff);
+ else
+ value.GetScalar() = (uint16_t)(
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xffff);
+ break;
+ case 8:
+ if (is_signed)
+ value.GetScalar() = (int8_t)(
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xff);
+ else
+ value.GetScalar() = (uint8_t)(
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xff);
+ break;
+ }
+ } else if (compiler_type.IsPointerType()) {
+ unsigned eax_id =
+ reg_ctx->GetRegisterInfoByName("eax", 0)->kinds[eRegisterKindLLDB];
+ uint32_t ptr =
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xffffffff;
+ value.GetScalar() = ptr;
+ } else {
+ // not handled yet
+ return return_valobj_sp;
+ }
+
+ // If we get here, we have a valid Value, so make our ValueObject out of it:
+
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ return return_valobj_sp;
+}
+
+// This defines the CFA as esp+4
+// the saved pc is at CFA-4 (i.e. esp+0)
+// The saved esp is CFA+0
+
+bool ABIMacOSX_i386::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t sp_reg_num = dwarf_esp;
+ uint32_t pc_reg_num = dwarf_eip;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 4);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -4, false);
+ row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("i386 at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ return true;
+}
+
+// This defines the CFA as ebp+8
+// The saved pc is at CFA-4 (i.e. ebp+4)
+// The saved ebp is at CFA-8 (i.e. ebp+0)
+// The saved esp is CFA+0
+
+bool ABIMacOSX_i386::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t fp_reg_num = dwarf_ebp;
+ uint32_t sp_reg_num = dwarf_esp;
+ uint32_t pc_reg_num = dwarf_eip;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ const int32_t ptr_size = 4;
+
+ row->GetCFAValue().SetIsRegisterPlusOffset(fp_reg_num, 2 * ptr_size);
+ row->SetOffset(0);
+
+ row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true);
+ row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("i386 default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ return true;
+}
+
+bool ABIMacOSX_i386::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ return !RegisterIsCalleeSaved(reg_info);
+}
+
+// v.
+// http://developer.apple.com/library/mac/#documentation/developertools/Conceptual/LowLevelABI/130
+// -IA-
+// 32_Function_Calling_Conventions/IA32.html#//apple_ref/doc/uid/TP40002492-SW4
+//
+// This document ("OS X ABI Function Call Guide", chapter "IA-32 Function
+// Calling Conventions") says that the following registers on i386 are
+// preserved aka non-volatile aka callee-saved:
+//
+// ebx, ebp, esi, edi, esp
+
+bool ABIMacOSX_i386::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
+ if (reg_info) {
+ // Saved registers are ebx, ebp, esi, edi, esp, eip
+ const char *name = reg_info->name;
+ if (name[0] == 'e') {
+ switch (name[1]) {
+ case 'b':
+ if (name[2] == 'x' || name[2] == 'p')
+ return name[3] == '\0';
+ break;
+ case 'd':
+ if (name[2] == 'i')
+ return name[3] == '\0';
+ break;
+ case 'i':
+ if (name[2] == 'p')
+ return name[3] == '\0';
+ break;
+ case 's':
+ if (name[2] == 'i' || name[2] == 'p')
+ return name[3] == '\0';
+ break;
+ }
+ }
+ if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') // sp
+ return true;
+ if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0') // fp
+ return true;
+ if (name[0] == 'p' && name[1] == 'c' && name[2] == '\0') // pc
+ return true;
+ }
+ return false;
+}
+
+void ABIMacOSX_i386::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "Mac OS X ABI for i386 targets", CreateInstance);
+}
+
+void ABIMacOSX_i386::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ABIMacOSX_i386::GetPluginNameStatic() {
+ static ConstString g_short_name("abi.macosx-i386");
+ return g_short_name;
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString ABIMacOSX_i386::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ABIMacOSX_i386::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h b/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h
new file mode 100644
index 000000000000..57def683283f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h
@@ -0,0 +1,100 @@
+//===-- ABIMacOSX_i386.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 liblldb_ABIMacOSX_i386_h_
+#define liblldb_ABIMacOSX_i386_h_
+
+#include "lldb/Core/Value.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABIMacOSX_i386 : public lldb_private::ABI {
+public:
+ ~ABIMacOSX_i386() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t func_addr, lldb::addr_t return_addr,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ // The Darwin i386 ABI requires that stack frames be 16 byte aligned.
+ // When there is a trap handler on the stack, e.g. _sigtramp in userland
+ // code, we've seen that the stack pointer is often not aligned properly
+ // before the handler is invoked. This means that lldb will stop the unwind
+ // early -- before the function which caused the trap.
+ //
+ // To work around this, we relax that alignment to be just word-size
+ // (4-bytes).
+ // Whitelisting the trap handlers for user space would be easy (_sigtramp) but
+ // in other environments there can be a large number of different functions
+ // involved in async traps.
+ //
+ // If we were to enforce 16-byte alignment, we also need to relax to 4-byte
+ // alignment for non-darwin i386 targets.
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are are 4 byte aligned
+ if (cfa & (4ull - 1ull))
+ return false; // Not 4 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ // Just make sure the address is a valid 32 bit address.
+ return pc <= UINT32_MAX;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ // PluginInterface protocol
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const override;
+
+ bool RegisterIsCalleeSaved(const lldb_private::RegisterInfo *reg_info);
+
+private:
+ ABIMacOSX_i386(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABIMacOSX_i386_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm/ABISysV_arm.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm/ABISysV_arm.cpp
new file mode 100644
index 000000000000..dd47ac7cbe3c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm/ABISysV_arm.cpp
@@ -0,0 +1,2156 @@
+//===-- ABISysV_arm.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 "ABISysV_arm.h"
+
+#include <vector>
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+
+#include "Plugins/Process/Utility/ARMDefines.h"
+#include "Utility/ARM_DWARF_Registers.h"
+#include "Utility/ARM_ehframe_Registers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static RegisterInfo g_register_infos[] = {
+ // NAME ALT SZ OFF ENCODING FORMAT EH_FRAME
+ // DWARF GENERIC PROCESS PLUGIN
+ // LLDB NATIVE VALUE REGS INVALIDATE REGS
+ // ========== ======= == === ============= ============
+ // ======================= =================== ===========================
+ // ======================= ====================== ==========
+ // ===============
+ {"r0",
+ "arg1",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r0, dwarf_r0, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r1",
+ "arg2",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r1, dwarf_r1, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r2",
+ "arg3",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r2, dwarf_r2, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r3",
+ "arg4",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r3, dwarf_r3, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r4",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r4, dwarf_r4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r5",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r5, dwarf_r5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r6",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r6, dwarf_r6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r7",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r8",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r8, dwarf_r8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r9",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r9, dwarf_r9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r10",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r10, dwarf_r10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r11",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r11, dwarf_r11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r12",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r12, dwarf_r12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"sp",
+ "r13",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lr",
+ "r14",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"pc",
+ "r15",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cpsr",
+ "psr",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s0",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s1",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s2",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s3",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s4",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s5",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s6",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s7",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s8",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s9",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s10",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s11",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s12",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s13",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s14",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s15",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s16",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s17",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s18",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s19",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s20",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s21",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s22",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s23",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s24",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s25",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s26",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s27",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s28",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s29",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s30",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s31",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fpscr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d0",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d1",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d2",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d3",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d4",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d5",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d6",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d7",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d8",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d9",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d10",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d11",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d12",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d13",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d14",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d15",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d16",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d17",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d18",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d19",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d20",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d21",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d22",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d23",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d24",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d25",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d26",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d27",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d28",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d29",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d29, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d30",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d30, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d31",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d31, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r8_usr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r8_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r9_usr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r9_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r10_usr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r10_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r11_usr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r11_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r12_usr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r12_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13_usr",
+ "sp_usr",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r13_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14_usr",
+ "lr_usr",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r14_usr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r8_fiq",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r8_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r9_fiq",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r9_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r10_fiq",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r10_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r11_fiq",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r11_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r12_fiq",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r12_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13_fiq",
+ "sp_fiq",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r13_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14_fiq",
+ "lr_fiq",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r14_fiq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13_irq",
+ "sp_irq",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r13_irq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14_irq",
+ "lr_irq",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r14_irq, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13_abt",
+ "sp_abt",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r13_abt, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14_abt",
+ "lr_abt",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r14_abt, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13_und",
+ "sp_und",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r13_und, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14_und",
+ "lr_und",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r14_und, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13_svc",
+ "sp_svc",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r13_svc, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14_svc",
+ "lr_svc",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, dwarf_r14_svc, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0}};
+
+static const uint32_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos);
+static bool g_register_info_names_constified = false;
+
+const lldb_private::RegisterInfo *
+ABISysV_arm::GetRegisterInfoArray(uint32_t &count) {
+ // Make the C-string names and alt_names for the register infos into const
+ // C-string values by having the ConstString unique the names in the global
+ // constant C-string pool.
+ if (!g_register_info_names_constified) {
+ g_register_info_names_constified = true;
+ for (uint32_t i = 0; i < k_num_register_infos; ++i) {
+ if (g_register_infos[i].name)
+ g_register_infos[i].name =
+ ConstString(g_register_infos[i].name).GetCString();
+ if (g_register_infos[i].alt_name)
+ g_register_infos[i].alt_name =
+ ConstString(g_register_infos[i].alt_name).GetCString();
+ }
+ }
+ count = k_num_register_infos;
+ return g_register_infos;
+}
+
+size_t ABISysV_arm::GetRedZoneSize() const { return 0; }
+
+// Static Functions
+
+ABISP
+ABISysV_arm::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) {
+ const llvm::Triple::ArchType arch_type = arch.GetTriple().getArch();
+ const llvm::Triple::VendorType vendor_type = arch.GetTriple().getVendor();
+
+ if (vendor_type != llvm::Triple::Apple) {
+ if ((arch_type == llvm::Triple::arm) ||
+ (arch_type == llvm::Triple::thumb)) {
+ return ABISP(new ABISysV_arm(process_sp));
+ }
+ }
+
+ return ABISP();
+}
+
+bool ABISysV_arm::PrepareTrivialCall(Thread &thread, addr_t sp,
+ addr_t function_addr, addr_t return_addr,
+ llvm::ArrayRef<addr_t> args) const {
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+
+ const uint32_t pc_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ const uint32_t sp_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+ const uint32_t ra_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA);
+
+ RegisterValue reg_value;
+
+ const uint8_t reg_names[] = {
+ LLDB_REGNUM_GENERIC_ARG1, LLDB_REGNUM_GENERIC_ARG2,
+ LLDB_REGNUM_GENERIC_ARG3, LLDB_REGNUM_GENERIC_ARG4};
+
+ llvm::ArrayRef<addr_t>::iterator ai = args.begin(), ae = args.end();
+
+ for (size_t i = 0; i < llvm::array_lengthof(reg_names); ++i) {
+ if (ai == ae)
+ break;
+
+ reg_value.SetUInt32(*ai);
+ if (!reg_ctx->WriteRegister(
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, reg_names[i]),
+ reg_value))
+ return false;
+
+ ++ai;
+ }
+
+ if (ai != ae) {
+ // Spill onto the stack
+ size_t num_stack_regs = ae - ai;
+
+ sp -= (num_stack_regs * 4);
+ // Keep the stack 8 byte aligned, not that we need to
+ sp &= ~(8ull - 1ull);
+
+ // just using arg1 to get the right size
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1);
+
+ addr_t arg_pos = sp;
+
+ for (; ai != ae; ++ai) {
+ reg_value.SetUInt32(*ai);
+ if (reg_ctx
+ ->WriteRegisterValueToMemory(reg_info, arg_pos,
+ reg_info->byte_size, reg_value)
+ .Fail())
+ return false;
+ arg_pos += reg_info->byte_size;
+ }
+ }
+
+ TargetSP target_sp(thread.CalculateTarget());
+ Address so_addr;
+
+ // Figure out if our return address is ARM or Thumb by using the
+ // Address::GetCallableLoadAddress(Target*) which will figure out the ARM
+ // thumb-ness and set the correct address bits for us.
+ so_addr.SetLoadAddress(return_addr, target_sp.get());
+ return_addr = so_addr.GetCallableLoadAddress(target_sp.get());
+
+ // Set "lr" to the return address
+ if (!reg_ctx->WriteRegisterFromUnsigned(ra_reg_num, return_addr))
+ return false;
+
+ // Set "sp" to the requested value
+ if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_num, sp))
+ return false;
+
+ // If bit zero or 1 is set, this must be a thumb function, no need to figure
+ // this out from the symbols.
+ so_addr.SetLoadAddress(function_addr, target_sp.get());
+ function_addr = so_addr.GetCallableLoadAddress(target_sp.get());
+
+ const RegisterInfo *cpsr_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS);
+ const uint32_t curr_cpsr = reg_ctx->ReadRegisterAsUnsigned(cpsr_reg_info, 0);
+
+ // Make a new CPSR and mask out any Thumb IT (if/then) bits
+ uint32_t new_cpsr = curr_cpsr & ~MASK_CPSR_IT_MASK;
+ // If bit zero or 1 is set, this must be thumb...
+ if (function_addr & 1ull)
+ new_cpsr |= MASK_CPSR_T; // Set T bit in CPSR
+ else
+ new_cpsr &= ~MASK_CPSR_T; // Clear T bit in CPSR
+
+ if (new_cpsr != curr_cpsr) {
+ if (!reg_ctx->WriteRegisterFromUnsigned(cpsr_reg_info, new_cpsr))
+ return false;
+ }
+
+ function_addr &=
+ ~1ull; // clear bit zero since the CPSR will take care of the mode for us
+
+ // Set "pc" to the address requested
+ return reg_ctx->WriteRegisterFromUnsigned(pc_reg_num, function_addr);
+}
+
+bool ABISysV_arm::GetArgumentValues(Thread &thread, ValueList &values) const {
+ uint32_t num_values = values.GetSize();
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ // For now, assume that the types in the AST values come from the Target's
+ // scratch AST.
+
+ // Extract the register context so we can read arguments from registers
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+
+ if (!reg_ctx)
+ return false;
+
+ addr_t sp = 0;
+
+ for (uint32_t value_idx = 0; value_idx < num_values; ++value_idx) {
+ // We currently only support extracting values with Clang QualTypes. Do we
+ // care about others?
+ Value *value = values.GetValueAtIndex(value_idx);
+
+ if (!value)
+ return false;
+
+ CompilerType compiler_type = value->GetCompilerType();
+ if (compiler_type) {
+ bool is_signed = false;
+ size_t bit_width = 0;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ compiler_type.IsPointerOrReferenceType()) {
+ if (llvm::Optional<uint64_t> size = compiler_type.GetBitSize(&thread))
+ bit_width = *size;
+ } else {
+ // We only handle integer, pointer and reference types currently...
+ return false;
+ }
+
+ if (bit_width <= (exe_ctx.GetProcessRef().GetAddressByteSize() * 8)) {
+ if (value_idx < 4) {
+ // Arguments 1-4 are in r0-r3...
+ const RegisterInfo *arg_reg_info = nullptr;
+ arg_reg_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + value_idx);
+ if (arg_reg_info) {
+ RegisterValue reg_value;
+
+ if (reg_ctx->ReadRegister(arg_reg_info, reg_value)) {
+ if (is_signed)
+ reg_value.SignExtend(bit_width);
+ if (!reg_value.GetScalarValue(value->GetScalar()))
+ return false;
+ continue;
+ }
+ }
+ return false;
+ } else {
+ if (sp == 0) {
+ // Read the stack pointer if it already hasn't been read
+ sp = reg_ctx->GetSP(0);
+ if (sp == 0)
+ return false;
+ }
+
+ // Arguments 5 on up are on the stack
+ const uint32_t arg_byte_size = (bit_width + (8 - 1)) / 8;
+ Status error;
+ if (!exe_ctx.GetProcessRef().ReadScalarIntegerFromMemory(
+ sp, arg_byte_size, is_signed, value->GetScalar(), error))
+ return false;
+
+ sp += arg_byte_size;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+static bool GetReturnValuePassedInMemory(Thread &thread,
+ RegisterContext *reg_ctx,
+ size_t byte_size, Value &value) {
+ Status error;
+ DataBufferHeap buffer(byte_size, 0);
+
+ const RegisterInfo *r0_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1);
+ uint32_t address =
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX;
+ thread.GetProcess()->ReadMemory(address, buffer.GetBytes(),
+ buffer.GetByteSize(), error);
+
+ if (error.Fail())
+ return false;
+
+ value.SetBytes(buffer.GetBytes(), buffer.GetByteSize());
+ return true;
+}
+
+bool ABISysV_arm::IsArmHardFloat(Thread &thread) const {
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp) {
+ const ArchSpec &arch(process_sp->GetTarget().GetArchitecture());
+
+ return (arch.GetFlags() & ArchSpec::eARM_abi_hard_float) != 0;
+ }
+
+ return false;
+}
+
+ValueObjectSP ABISysV_arm::GetReturnValueObjectImpl(
+ Thread &thread, lldb_private::CompilerType &compiler_type) const {
+ Value value;
+ ValueObjectSP return_valobj_sp;
+
+ if (!compiler_type)
+ return return_valobj_sp;
+
+ // value.SetContext (Value::eContextTypeClangType,
+ // compiler_type.GetOpaqueQualType());
+ value.SetCompilerType(compiler_type);
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return return_valobj_sp;
+
+ bool is_signed;
+ bool is_complex;
+ uint32_t float_count;
+ bool is_vfp_candidate = false;
+ uint8_t vfp_count = 0;
+ uint8_t vfp_byte_size = 0;
+
+ // Get the pointer to the first stack argument so we have a place to start
+ // when reading data
+
+ const RegisterInfo *r0_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1);
+ llvm::Optional<uint64_t> bit_width = compiler_type.GetBitSize(&thread);
+ llvm::Optional<uint64_t> byte_size = compiler_type.GetByteSize(&thread);
+ if (!bit_width || !byte_size)
+ return return_valobj_sp;
+
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed)) {
+ switch (*bit_width) {
+ default:
+ return return_valobj_sp;
+ case 64: {
+ const RegisterInfo *r1_reg_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2);
+ uint64_t raw_value;
+ raw_value = reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX;
+ raw_value |= ((uint64_t)(reg_ctx->ReadRegisterAsUnsigned(r1_reg_info, 0) &
+ UINT32_MAX))
+ << 32;
+ if (is_signed)
+ value.GetScalar() = (int64_t)raw_value;
+ else
+ value.GetScalar() = (uint64_t)raw_value;
+ } break;
+ case 32:
+ if (is_signed)
+ value.GetScalar() = (int32_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX);
+ else
+ value.GetScalar() = (uint32_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX);
+ break;
+ case 16:
+ if (is_signed)
+ value.GetScalar() = (int16_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT16_MAX);
+ else
+ value.GetScalar() = (uint16_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT16_MAX);
+ break;
+ case 8:
+ if (is_signed)
+ value.GetScalar() = (int8_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT8_MAX);
+ else
+ value.GetScalar() = (uint8_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT8_MAX);
+ break;
+ }
+ } else if (compiler_type.IsPointerType()) {
+ uint32_t ptr =
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(r0_reg_info, 0) &
+ UINT32_MAX;
+ value.GetScalar() = ptr;
+ } else if (compiler_type.IsVectorType(nullptr, nullptr)) {
+ if (IsArmHardFloat(thread) && (*byte_size == 8 || *byte_size == 16)) {
+ is_vfp_candidate = true;
+ vfp_byte_size = 8;
+ vfp_count = (*byte_size == 8 ? 1 : 2);
+ } else if (*byte_size <= 16) {
+ DataBufferHeap buffer(16, 0);
+ uint32_t *buffer_ptr = (uint32_t *)buffer.GetBytes();
+
+ for (uint32_t i = 0; 4 * i < *byte_size; ++i) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + i);
+ buffer_ptr[i] =
+ reg_ctx->ReadRegisterAsUnsigned(reg_info, 0) & UINT32_MAX;
+ }
+ value.SetBytes(buffer.GetBytes(), *byte_size);
+ } else {
+ if (!GetReturnValuePassedInMemory(thread, reg_ctx, *byte_size, value))
+ return return_valobj_sp;
+ }
+ } else if (compiler_type.IsFloatingPointType(float_count, is_complex)) {
+ if (float_count == 1 && !is_complex) {
+ switch (*bit_width) {
+ default:
+ return return_valobj_sp;
+ case 64: {
+ static_assert(sizeof(double) == sizeof(uint64_t), "");
+
+ if (IsArmHardFloat(thread)) {
+ RegisterValue reg_value;
+ const RegisterInfo *d0_reg_info =
+ reg_ctx->GetRegisterInfoByName("d0", 0);
+ reg_ctx->ReadRegister(d0_reg_info, reg_value);
+ value.GetScalar() = reg_value.GetAsDouble();
+ } else {
+ uint64_t raw_value;
+ const RegisterInfo *r1_reg_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2);
+ raw_value =
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX;
+ raw_value |=
+ ((uint64_t)(reg_ctx->ReadRegisterAsUnsigned(r1_reg_info, 0) &
+ UINT32_MAX))
+ << 32;
+ value.GetScalar() = *reinterpret_cast<double *>(&raw_value);
+ }
+ break;
+ }
+ case 16: // Half precision returned after a conversion to single precision
+ case 32: {
+ static_assert(sizeof(float) == sizeof(uint32_t), "");
+
+ if (IsArmHardFloat(thread)) {
+ RegisterValue reg_value;
+ const RegisterInfo *s0_reg_info =
+ reg_ctx->GetRegisterInfoByName("s0", 0);
+ reg_ctx->ReadRegister(s0_reg_info, reg_value);
+ value.GetScalar() = reg_value.GetAsFloat();
+ } else {
+ uint32_t raw_value;
+ raw_value =
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX;
+ value.GetScalar() = *reinterpret_cast<float *>(&raw_value);
+ }
+ break;
+ }
+ }
+ } else if (is_complex && float_count == 2) {
+ if (IsArmHardFloat(thread)) {
+ is_vfp_candidate = true;
+ vfp_byte_size = *byte_size / 2;
+ vfp_count = 2;
+ } else if (!GetReturnValuePassedInMemory(thread, reg_ctx, *bit_width / 8,
+ value))
+ return return_valobj_sp;
+ } else
+ // not handled yet
+ return return_valobj_sp;
+ } else if (compiler_type.IsAggregateType()) {
+ if (IsArmHardFloat(thread)) {
+ CompilerType base_type;
+ const uint32_t homogeneous_count =
+ compiler_type.IsHomogeneousAggregate(&base_type);
+
+ if (homogeneous_count > 0 && homogeneous_count <= 4) {
+ llvm::Optional<uint64_t> base_byte_size =
+ base_type.GetByteSize(nullptr);
+ if (base_type.IsVectorType(nullptr, nullptr)) {
+ if (base_byte_size &&
+ (*base_byte_size == 8 || *base_byte_size == 16)) {
+ is_vfp_candidate = true;
+ vfp_byte_size = 8;
+ vfp_count = (*base_byte_size == 8 ? homogeneous_count
+ : homogeneous_count * 2);
+ }
+ } else if (base_type.IsFloatingPointType(float_count, is_complex)) {
+ if (float_count == 1 && !is_complex) {
+ is_vfp_candidate = true;
+ if (base_byte_size)
+ vfp_byte_size = *base_byte_size;
+ vfp_count = homogeneous_count;
+ }
+ }
+ } else if (homogeneous_count == 0) {
+ const uint32_t num_children = compiler_type.GetNumFields();
+
+ if (num_children > 0 && num_children <= 2) {
+ uint32_t index = 0;
+ for (index = 0; index < num_children; index++) {
+ std::string name;
+ base_type = compiler_type.GetFieldAtIndex(index, name, nullptr,
+ nullptr, nullptr);
+
+ if (base_type.IsFloatingPointType(float_count, is_complex)) {
+ llvm::Optional<uint64_t> base_byte_size =
+ base_type.GetByteSize(nullptr);
+ if (float_count == 2 && is_complex) {
+ if (index != 0 && base_byte_size &&
+ vfp_byte_size != *base_byte_size)
+ break;
+ else if (base_byte_size)
+ vfp_byte_size = *base_byte_size;
+ } else
+ break;
+ } else
+ break;
+ }
+
+ if (index == num_children) {
+ is_vfp_candidate = true;
+ vfp_byte_size = (vfp_byte_size >> 1);
+ vfp_count = (num_children << 1);
+ }
+ }
+ }
+ }
+
+ if (*byte_size <= 4) {
+ RegisterValue r0_reg_value;
+ uint32_t raw_value =
+ reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX;
+ value.SetBytes(&raw_value, *byte_size);
+ } else if (!is_vfp_candidate) {
+ if (!GetReturnValuePassedInMemory(thread, reg_ctx, *byte_size, value))
+ return return_valobj_sp;
+ }
+ } else {
+ // not handled yet
+ return return_valobj_sp;
+ }
+
+ if (is_vfp_candidate) {
+ ProcessSP process_sp(thread.GetProcess());
+ ByteOrder byte_order = process_sp->GetByteOrder();
+
+ DataBufferSP data_sp(new DataBufferHeap(*byte_size, 0));
+ uint32_t data_offset = 0;
+
+ for (uint32_t reg_index = 0; reg_index < vfp_count; reg_index++) {
+ uint32_t regnum = 0;
+
+ if (vfp_byte_size == 4)
+ regnum = dwarf_s0 + reg_index;
+ else if (vfp_byte_size == 8)
+ regnum = dwarf_d0 + reg_index;
+ else
+ break;
+
+ const RegisterInfo *reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindDWARF, regnum);
+ if (reg_info == nullptr)
+ break;
+
+ RegisterValue reg_value;
+ if (!reg_ctx->ReadRegister(reg_info, reg_value))
+ break;
+
+ // Make sure we have enough room in "data_sp"
+ if ((data_offset + vfp_byte_size) <= data_sp->GetByteSize()) {
+ Status error;
+ const size_t bytes_copied = reg_value.GetAsMemoryData(
+ reg_info, data_sp->GetBytes() + data_offset, vfp_byte_size,
+ byte_order, error);
+ if (bytes_copied != vfp_byte_size)
+ break;
+
+ data_offset += bytes_copied;
+ }
+ }
+
+ if (data_offset == *byte_size) {
+ DataExtractor data;
+ data.SetByteOrder(byte_order);
+ data.SetAddressByteSize(process_sp->GetAddressByteSize());
+ data.SetData(data_sp);
+
+ return ValueObjectConstResult::Create(&thread, compiler_type,
+ ConstString(""), data);
+ } else { // Some error occurred while getting values from registers
+ return return_valobj_sp;
+ }
+ }
+
+ // If we get here, we have a valid Value, so make our ValueObject out of it:
+
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ return return_valobj_sp;
+}
+
+Status ABISysV_arm::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType compiler_type = new_value_sp->GetCompilerType();
+ if (!compiler_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+
+ bool is_signed;
+ uint32_t count;
+ bool is_complex;
+
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+
+ bool set_it_simple = false;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ compiler_type.IsPointerType()) {
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+ lldb::offset_t offset = 0;
+ if (num_bytes <= 8) {
+ const RegisterInfo *r0_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1);
+ if (num_bytes <= 4) {
+ uint32_t raw_value = data.GetMaxU32(&offset, num_bytes);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(r0_info, raw_value))
+ set_it_simple = true;
+ } else {
+ uint32_t raw_value = data.GetMaxU32(&offset, 4);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(r0_info, raw_value)) {
+ const RegisterInfo *r1_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2);
+ uint32_t raw_value = data.GetMaxU32(&offset, num_bytes - offset);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(r1_info, raw_value))
+ set_it_simple = true;
+ }
+ }
+ } else {
+ error.SetErrorString("We don't support returning longer than 64 bit "
+ "integer values at present.");
+ }
+ } else if (compiler_type.IsFloatingPointType(count, is_complex)) {
+ if (is_complex)
+ error.SetErrorString(
+ "We don't support returning complex values at present");
+ else
+ error.SetErrorString(
+ "We don't support returning float values at present");
+ }
+
+ if (!set_it_simple)
+ error.SetErrorString(
+ "We only support setting simple integer return types at present.");
+
+ return error;
+}
+
+bool ABISysV_arm::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t lr_reg_num = dwarf_lr;
+ uint32_t sp_reg_num = dwarf_sp;
+ uint32_t pc_reg_num = dwarf_pc;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our Call Frame Address is the stack pointer value
+ row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 0);
+
+ // The previous PC is in the LR
+ row->SetRegisterLocationToRegister(pc_reg_num, lr_reg_num, true);
+ unwind_plan.AppendRow(row);
+
+ // All other registers are the same.
+
+ unwind_plan.SetSourceName("arm at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+
+ return true;
+}
+
+bool ABISysV_arm::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ // TODO: Handle thumb
+ uint32_t fp_reg_num = dwarf_r11;
+ uint32_t pc_reg_num = dwarf_pc;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ const int32_t ptr_size = 4;
+
+ row->GetCFAValue().SetIsRegisterPlusOffset(fp_reg_num, 2 * ptr_size);
+ row->SetOffset(0);
+
+ row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("arm default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+
+ return true;
+}
+
+// cf. "ARMv6 Function Calling Conventions"
+
+// ARMv7 on GNU/Linux general purpose reg rules:
+// r0-r3 not preserved (used for argument passing)
+// r4-r11 preserved (v1-v8)
+// r12 not presrved
+// r13 preserved (stack pointer)
+// r14 preserved (link register)
+// r15 preserved (pc)
+// cpsr not preserved (different rules for different bits)
+
+// ARMv7 VFP register rules:
+// d0-d7 not preserved (aka s0-s15, q0-q3)
+// d8-d15 preserved (aka s16-s31, q4-q7)
+// d16-d31 not preserved (aka q8-q15)
+
+bool ABISysV_arm::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ if (reg_info) {
+ // Volatile registers are: r0, r1, r2, r3, r9, r12, r13 (aka sp)
+ const char *name = reg_info->name;
+ if (name[0] == 'r') {
+ switch (name[1]) {
+ case '0':
+ return name[2] == '\0'; // r0
+ case '1':
+ switch (name[2]) {
+ case '\0':
+ return true; // r1
+ case '2':
+ return name[3] == '\0'; // r12
+ default:
+ break;
+ }
+ break;
+
+ case '2':
+ return name[2] == '\0'; // r2
+ case '3':
+ return name[2] == '\0'; // r3
+ default:
+ break;
+ }
+ } else if (name[0] == 'd') {
+ switch (name[1]) {
+ case '0':
+ return name[2] == '\0'; // d0 is volatile
+
+ case '1':
+ switch (name[2]) {
+ case '\0':
+ return true; // d1 is volatile
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return name[3] == '\0'; // d16 - d19 are volatile
+ default:
+ break;
+ }
+ break;
+
+ case '2':
+ switch (name[2]) {
+ case '\0':
+ return true; // d2 is volatile
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return name[3] == '\0'; // d20 - d29 are volatile
+ default:
+ break;
+ }
+ break;
+
+ case '3':
+ switch (name[2]) {
+ case '\0':
+ return true; // d3 is volatile
+ case '0':
+ case '1':
+ return name[3] == '\0'; // d30 - d31 are volatile
+ default:
+ break;
+ }
+ break;
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ return name[2] == '\0'; // d4 - d7 are volatile
+
+ default:
+ break;
+ }
+ } else if (name[0] == 's') {
+ switch (name[1]) {
+ case '0':
+ return name[2] == '\0'; // s0 is volatile
+
+ case '1':
+ switch (name[2]) {
+ case '\0':
+ return true; // s1 is volatile
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ return name[3] == '\0'; // s10 - s15 are volatile
+ default:
+ break;
+ }
+ break;
+
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return name[2] == '\0'; // s2 - s9 are volatile
+
+ default:
+ break;
+ }
+ } else if (name[0] == 'q') {
+ switch (name[1]) {
+ case '1':
+ switch (name[2]) {
+ case '\0':
+ return true; // q1 is volatile
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ return true; // q10-q15 are volatile
+ default:
+ return false;
+ }
+ break;
+
+ case '0':
+ case '2':
+ case '3':
+ return name[2] == '\0'; // q0-q3 are volatile
+ case '8':
+ case '9':
+ return name[2] == '\0'; // q8-q9 are volatile
+ default:
+ break;
+ }
+ } else if (name[0] == 's' && name[1] == 'p' && name[2] == '\0')
+ return true;
+ }
+ return false;
+}
+
+void ABISysV_arm::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ "SysV ABI for arm targets", CreateInstance);
+}
+
+void ABISysV_arm::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ABISysV_arm::GetPluginNameStatic() {
+ static ConstString g_name("SysV-arm");
+ return g_name;
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString ABISysV_arm::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ABISysV_arm::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm/ABISysV_arm.h b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm/ABISysV_arm.h
new file mode 100644
index 000000000000..a0f00c8f227d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm/ABISysV_arm.h
@@ -0,0 +1,93 @@
+//===-- ABISysV_arm.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 liblldb_ABISysV_arm_h_
+#define liblldb_ABISysV_arm_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABISysV_arm : public lldb_private::ABI {
+public:
+ ~ABISysV_arm() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t func_addr, lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are are 4 byte aligned
+ if (cfa & (4ull - 1ull))
+ return false; // Not 4 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ // Just make sure the address is a valid 32 bit address. Bit zero
+ // might be set due to Thumb function calls, so don't enforce 2 byte
+ // alignment
+ return pc <= UINT32_MAX;
+ }
+
+ lldb::addr_t FixCodeAddress(lldb::addr_t pc) override {
+ // ARM uses bit zero to signify a code address is thumb, so we must
+ // strip bit zero in any code addresses.
+ return pc & ~(lldb::addr_t)1;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ bool IsArmHardFloat(lldb_private::Thread &thread) const;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ // PluginInterface protocol
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const override;
+
+private:
+ ABISysV_arm(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABISysV_arm_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.cpp
new file mode 100644
index 000000000000..1d547121e231
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.cpp
@@ -0,0 +1,2417 @@
+//===-- ABISysV_arm64.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 "ABISysV_arm64.h"
+
+#include <vector>
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+
+#include "Utility/ARM64_DWARF_Registers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static RegisterInfo g_register_infos[] = {
+ // NAME ALT SZ OFF ENCODING FORMAT
+ // EH_FRAME DWARF GENERIC
+ // PROCESS PLUGIN LLDB NATIVE
+ // ========== ======= == === ============= ===================
+ // =================== ====================== ===========================
+ // ======================= ======================
+ {"x0",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x0, LLDB_REGNUM_GENERIC_ARG1,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x1",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x1, LLDB_REGNUM_GENERIC_ARG2,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x2",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x2, LLDB_REGNUM_GENERIC_ARG3,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x3",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x3, LLDB_REGNUM_GENERIC_ARG4,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x4",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x4, LLDB_REGNUM_GENERIC_ARG5,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x5",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x5, LLDB_REGNUM_GENERIC_ARG6,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x6",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x6, LLDB_REGNUM_GENERIC_ARG7,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x7",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x7, LLDB_REGNUM_GENERIC_ARG8,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x8",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x8, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x9",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x9, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x10",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x10, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x11",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x11, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x12",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x12, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x13",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x13, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x14",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x14, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x15",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x15, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x16",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x16, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x17",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x17, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x18",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x18, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x19",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x19, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x20",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x20, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x21",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x21, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x22",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x22, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x23",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x23, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x24",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x24, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x25",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x25, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x26",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x26, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x27",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x27, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"x28",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x28, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fp",
+ "x29",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x29, LLDB_REGNUM_GENERIC_FP,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lr",
+ "x30",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x30, LLDB_REGNUM_GENERIC_RA,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"sp",
+ "x31",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::x31, LLDB_REGNUM_GENERIC_SP,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"pc",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::pc, LLDB_REGNUM_GENERIC_PC,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cpsr",
+ "psr",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::cpsr, LLDB_REGNUM_GENERIC_FLAGS,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"v0",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v0, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v1",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v1, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v2",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v2, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v3",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v3, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v4",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v4, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v5",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v5, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v6",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v6, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v7",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v7, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v8",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v8, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v9",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v9, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v10",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v10, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v11",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v11, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v12",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v12, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v13",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v13, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v14",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v14, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v15",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v15, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v16",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v16, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v17",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v17, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v18",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v18, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v19",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v19, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v20",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v20, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v21",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v21, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v22",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v22, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v23",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v23, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v24",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v24, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v25",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v25, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v26",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v26, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v27",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v27, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v28",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v28, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v29",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v29, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v30",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v30, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"v31",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, arm64_dwarf::v31, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"fpsr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fpcr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"s0",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s1",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s2",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s3",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s4",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s5",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s6",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s7",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s8",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s9",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s10",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s11",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s12",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s13",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s14",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s15",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s16",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s17",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s18",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s19",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s20",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s21",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s22",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s23",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s24",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s25",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s26",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s27",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s28",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s29",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s30",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s31",
+ nullptr,
+ 4,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"d0",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d1",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d2",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d3",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d4",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d5",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d6",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d7",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d8",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d9",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d10",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d11",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d12",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d13",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d14",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d15",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d16",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d17",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d18",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d19",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d20",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d21",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d22",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d23",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d24",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d25",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d26",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d27",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d28",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d29",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d30",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"d31",
+ nullptr,
+ 8,
+ 0,
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0}};
+
+static const uint32_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos);
+static bool g_register_info_names_constified = false;
+
+const lldb_private::RegisterInfo *
+ABISysV_arm64::GetRegisterInfoArray(uint32_t &count) {
+ // Make the C-string names and alt_names for the register infos into const
+ // C-string values by having the ConstString unique the names in the global
+ // constant C-string pool.
+ if (!g_register_info_names_constified) {
+ g_register_info_names_constified = true;
+ for (uint32_t i = 0; i < k_num_register_infos; ++i) {
+ if (g_register_infos[i].name)
+ g_register_infos[i].name =
+ ConstString(g_register_infos[i].name).GetCString();
+ if (g_register_infos[i].alt_name)
+ g_register_infos[i].alt_name =
+ ConstString(g_register_infos[i].alt_name).GetCString();
+ }
+ }
+ count = k_num_register_infos;
+ return g_register_infos;
+}
+
+bool ABISysV_arm64::GetPointerReturnRegister(const char *&name) {
+ name = "x0";
+ return true;
+}
+
+size_t ABISysV_arm64::GetRedZoneSize() const { return 128; }
+
+// Static Functions
+
+ABISP
+ABISysV_arm64::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) {
+ const llvm::Triple::ArchType arch_type = arch.GetTriple().getArch();
+ const llvm::Triple::VendorType vendor_type = arch.GetTriple().getVendor();
+
+ if (vendor_type != llvm::Triple::Apple) {
+ if (arch_type == llvm::Triple::aarch64) {
+ return ABISP(new ABISysV_arm64(process_sp));
+ }
+ }
+
+ return ABISP();
+}
+
+bool ABISysV_arm64::PrepareTrivialCall(Thread &thread, addr_t sp,
+ addr_t func_addr, addr_t return_addr,
+ llvm::ArrayRef<addr_t> args) const {
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ StreamString s;
+ s.Printf("ABISysV_arm64::PrepareTrivialCall (tid = 0x%" PRIx64
+ ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64
+ ", return_addr = 0x%" PRIx64,
+ thread.GetID(), (uint64_t)sp, (uint64_t)func_addr,
+ (uint64_t)return_addr);
+
+ for (size_t i = 0; i < args.size(); ++i)
+ s.Printf(", arg%d = 0x%" PRIx64, static_cast<int>(i + 1), args[i]);
+ s.PutCString(")");
+ log->PutString(s.GetString());
+ }
+
+ // x0 - x7 contain first 8 simple args
+ if (args.size() > 8)
+ return false;
+
+ for (size_t i = 0; i < args.size(); ++i) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + i);
+ if (log)
+ log->Printf("About to write arg%d (0x%" PRIx64 ") into %s",
+ static_cast<int>(i + 1), args[i], reg_info->name);
+ if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i]))
+ return false;
+ }
+
+ // Set "lr" to the return address
+ if (!reg_ctx->WriteRegisterFromUnsigned(
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_RA),
+ return_addr))
+ return false;
+
+ // Set "sp" to the requested value
+ if (!reg_ctx->WriteRegisterFromUnsigned(
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_SP),
+ sp))
+ return false;
+
+ // Set "pc" to the address requested
+ if (!reg_ctx->WriteRegisterFromUnsigned(
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC),
+ func_addr))
+ return false;
+
+ return true;
+}
+
+// TODO: We dont support fp/SIMD arguments in v0-v7
+bool ABISysV_arm64::GetArgumentValues(Thread &thread, ValueList &values) const {
+ uint32_t num_values = values.GetSize();
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+
+ // Extract the register context so we can read arguments from registers
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+
+ if (!reg_ctx)
+ return false;
+
+ addr_t sp = 0;
+
+ for (uint32_t value_idx = 0; value_idx < num_values; ++value_idx) {
+ // We currently only support extracting values with Clang QualTypes. Do we
+ // care about others?
+ Value *value = values.GetValueAtIndex(value_idx);
+
+ if (!value)
+ return false;
+
+ CompilerType value_type = value->GetCompilerType();
+ if (value_type) {
+ bool is_signed = false;
+ size_t bit_width = 0;
+ llvm::Optional<uint64_t> bit_size = value_type.GetBitSize(&thread);
+ if (!bit_size)
+ return false;
+ if (value_type.IsIntegerOrEnumerationType(is_signed)) {
+ bit_width = *bit_size;
+ } else if (value_type.IsPointerOrReferenceType()) {
+ bit_width = *bit_size;
+ } else {
+ // We only handle integer, pointer and reference types currently...
+ return false;
+ }
+
+ if (bit_width <= (exe_ctx.GetProcessRef().GetAddressByteSize() * 8)) {
+ if (value_idx < 8) {
+ // Arguments 1-8 are in x0-x7...
+ const RegisterInfo *reg_info = nullptr;
+ reg_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + value_idx);
+
+ if (reg_info) {
+ RegisterValue reg_value;
+
+ if (reg_ctx->ReadRegister(reg_info, reg_value)) {
+ if (is_signed)
+ reg_value.SignExtend(bit_width);
+ if (!reg_value.GetScalarValue(value->GetScalar()))
+ return false;
+ continue;
+ }
+ }
+ return false;
+ } else {
+ // TODO: Verify for stack layout for SysV
+ if (sp == 0) {
+ // Read the stack pointer if we already haven't read it
+ sp = reg_ctx->GetSP(0);
+ if (sp == 0)
+ return false;
+ }
+
+ // Arguments 5 on up are on the stack
+ const uint32_t arg_byte_size = (bit_width + (8 - 1)) / 8;
+ Status error;
+ if (!exe_ctx.GetProcessRef().ReadScalarIntegerFromMemory(
+ sp, arg_byte_size, is_signed, value->GetScalar(), error))
+ return false;
+
+ sp += arg_byte_size;
+ // Align up to the next 8 byte boundary if needed
+ if (sp % 8) {
+ sp >>= 3;
+ sp += 1;
+ sp <<= 3;
+ }
+ }
+ }
+ }
+ }
+ return true;
+}
+
+Status ABISysV_arm64::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType return_value_type = new_value_sp->GetCompilerType();
+ if (!return_value_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+
+ if (reg_ctx) {
+ DataExtractor data;
+ Status data_error;
+ const uint64_t byte_size = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+
+ const uint32_t type_flags = return_value_type.GetTypeInfo(nullptr);
+ if (type_flags & eTypeIsScalar || type_flags & eTypeIsPointer) {
+ if (type_flags & eTypeIsInteger || type_flags & eTypeIsPointer) {
+ // Extract the register context so we can read arguments from registers
+ lldb::offset_t offset = 0;
+ if (byte_size <= 16) {
+ const RegisterInfo *x0_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1);
+ if (byte_size <= 8) {
+ uint64_t raw_value = data.GetMaxU64(&offset, byte_size);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(x0_info, raw_value))
+ error.SetErrorString("failed to write register x0");
+ } else {
+ uint64_t raw_value = data.GetMaxU64(&offset, 8);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(x0_info, raw_value)) {
+ const RegisterInfo *x1_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2);
+ raw_value = data.GetMaxU64(&offset, byte_size - offset);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(x1_info, raw_value))
+ error.SetErrorString("failed to write register x1");
+ }
+ }
+ } else {
+ error.SetErrorString("We don't support returning longer than 128 bit "
+ "integer values at present.");
+ }
+ } else if (type_flags & eTypeIsFloat) {
+ if (type_flags & eTypeIsComplex) {
+ // Don't handle complex yet.
+ error.SetErrorString(
+ "returning complex float values are not supported");
+ } else {
+ const RegisterInfo *v0_info = reg_ctx->GetRegisterInfoByName("v0", 0);
+
+ if (v0_info) {
+ if (byte_size <= 16) {
+ if (byte_size <= RegisterValue::GetMaxByteSize()) {
+ RegisterValue reg_value;
+ error = reg_value.SetValueFromData(v0_info, data, 0, true);
+ if (error.Success()) {
+ if (!reg_ctx->WriteRegister(v0_info, reg_value))
+ error.SetErrorString("failed to write register v0");
+ }
+ } else {
+ error.SetErrorStringWithFormat(
+ "returning float values with a byte size of %" PRIu64
+ " are not supported",
+ byte_size);
+ }
+ } else {
+ error.SetErrorString("returning float values longer than 128 "
+ "bits are not supported");
+ }
+ } else {
+ error.SetErrorString("v0 register is not available on this target");
+ }
+ }
+ }
+ } else if (type_flags & eTypeIsVector) {
+ if (byte_size > 0) {
+ const RegisterInfo *v0_info = reg_ctx->GetRegisterInfoByName("v0", 0);
+
+ if (v0_info) {
+ if (byte_size <= v0_info->byte_size) {
+ RegisterValue reg_value;
+ error = reg_value.SetValueFromData(v0_info, data, 0, true);
+ if (error.Success()) {
+ if (!reg_ctx->WriteRegister(v0_info, reg_value))
+ error.SetErrorString("failed to write register v0");
+ }
+ }
+ }
+ }
+ }
+ } else {
+ error.SetErrorString("no registers are available");
+ }
+
+ return error;
+}
+
+bool ABISysV_arm64::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t lr_reg_num = arm64_dwarf::lr;
+ uint32_t sp_reg_num = arm64_dwarf::sp;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our previous Call Frame Address is the stack pointer
+ row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 0);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetReturnAddressRegister(lr_reg_num);
+
+ // All other registers are the same.
+
+ unwind_plan.SetSourceName("arm64 at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+
+ return true;
+}
+
+bool ABISysV_arm64::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t fp_reg_num = arm64_dwarf::fp;
+ uint32_t pc_reg_num = arm64_dwarf::pc;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ const int32_t ptr_size = 8;
+
+ row->GetCFAValue().SetIsRegisterPlusOffset(fp_reg_num, 2 * ptr_size);
+ row->SetOffset(0);
+
+ row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("arm64 default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+
+ return true;
+}
+
+// AAPCS64 (Procedure Call Standard for the ARM 64-bit Architecture) says
+// registers x19 through x28 and sp are callee preserved. v8-v15 are non-
+// volatile (and specifically only the lower 8 bytes of these regs), the rest
+// of the fp/SIMD registers are volatile.
+
+// We treat x29 as callee preserved also, else the unwinder won't try to
+// retrieve fp saves.
+
+bool ABISysV_arm64::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ if (reg_info) {
+ const char *name = reg_info->name;
+
+ // Sometimes we'll be called with the "alternate" name for these registers;
+ // recognize them as non-volatile.
+
+ if (name[0] == 'p' && name[1] == 'c') // pc
+ return false;
+ if (name[0] == 'f' && name[1] == 'p') // fp
+ return false;
+ if (name[0] == 's' && name[1] == 'p') // sp
+ return false;
+ if (name[0] == 'l' && name[1] == 'r') // lr
+ return false;
+
+ if (name[0] == 'x' || name[0] == 'r') {
+ // Volatile registers: x0-x18
+ // Although documentation says only x19-28 + sp are callee saved We ll
+ // also have to treat x30 as non-volatile. Each dwarf frame has its own
+ // value of lr. Return false for the non-volatile gpr regs, true for
+ // everything else
+ switch (name[1]) {
+ case '1':
+ switch (name[2]) {
+ case '9':
+ return false; // x19 is non-volatile
+ default:
+ return true;
+ }
+ break;
+ case '2':
+ switch (name[2]) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ return false; // x20 - 28 are non-volatile
+ case '9':
+ return false; // x29 aka fp treat as non-volatile
+ default:
+ return true;
+ }
+ case '3': // x30 (lr) and x31 (sp) treat as non-volatile
+ if (name[2] == '0' || name[2] == '1')
+ return false;
+ break;
+ default:
+ return true; // all volatile cases not handled above fall here.
+ }
+ } else if (name[0] == 'v' || name[0] == 's' || name[0] == 'd') {
+ // Volatile registers: v0-7, v16-v31
+ // Return false for non-volatile fp/SIMD regs, true for everything else
+ switch (name[1]) {
+ case '8':
+ case '9':
+ return false; // v8-v9 are non-volatile
+ case '1':
+ switch (name[2]) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ return false; // v10-v15 are non-volatile
+ default:
+ return true;
+ }
+ default:
+ return true;
+ }
+ }
+ }
+ return true;
+}
+
+static bool LoadValueFromConsecutiveGPRRegisters(
+ ExecutionContext &exe_ctx, RegisterContext *reg_ctx,
+ const CompilerType &value_type,
+ bool is_return_value, // false => parameter, true => return value
+ uint32_t &NGRN, // NGRN (see ABI documentation)
+ uint32_t &NSRN, // NSRN (see ABI documentation)
+ DataExtractor &data) {
+ llvm::Optional<uint64_t> byte_size = value_type.GetByteSize(nullptr);
+
+ if (byte_size || *byte_size == 0)
+ return false;
+
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order = exe_ctx.GetProcessRef().GetByteOrder();
+ Status error;
+
+ CompilerType base_type;
+ const uint32_t homogeneous_count =
+ value_type.IsHomogeneousAggregate(&base_type);
+ if (homogeneous_count > 0 && homogeneous_count <= 8) {
+ // Make sure we have enough registers
+ if (NSRN < 8 && (8 - NSRN) >= homogeneous_count) {
+ if (!base_type)
+ return false;
+ llvm::Optional<uint64_t> base_byte_size = base_type.GetByteSize(nullptr);
+ if (!base_byte_size)
+ return false;
+ uint32_t data_offset = 0;
+
+ for (uint32_t i = 0; i < homogeneous_count; ++i) {
+ char v_name[8];
+ ::snprintf(v_name, sizeof(v_name), "v%u", NSRN);
+ const RegisterInfo *reg_info =
+ reg_ctx->GetRegisterInfoByName(v_name, 0);
+ if (reg_info == nullptr)
+ return false;
+
+ if (*base_byte_size > reg_info->byte_size)
+ return false;
+
+ RegisterValue reg_value;
+
+ if (!reg_ctx->ReadRegister(reg_info, reg_value))
+ return false;
+
+ // Make sure we have enough room in "heap_data_up"
+ if ((data_offset + *base_byte_size) <= heap_data_up->GetByteSize()) {
+ const size_t bytes_copied = reg_value.GetAsMemoryData(
+ reg_info, heap_data_up->GetBytes() + data_offset, *base_byte_size,
+ byte_order, error);
+ if (bytes_copied != *base_byte_size)
+ return false;
+ data_offset += bytes_copied;
+ ++NSRN;
+ } else
+ return false;
+ }
+ data.SetByteOrder(byte_order);
+ data.SetAddressByteSize(exe_ctx.GetProcessRef().GetAddressByteSize());
+ data.SetData(DataBufferSP(heap_data_up.release()));
+ return true;
+ }
+ }
+
+ const size_t max_reg_byte_size = 16;
+ if (*byte_size <= max_reg_byte_size) {
+ size_t bytes_left = *byte_size;
+ uint32_t data_offset = 0;
+ while (data_offset < *byte_size) {
+ if (NGRN >= 8)
+ return false;
+
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + NGRN);
+ if (reg_info == nullptr)
+ return false;
+
+ RegisterValue reg_value;
+
+ if (!reg_ctx->ReadRegister(reg_info, reg_value))
+ return false;
+
+ const size_t curr_byte_size = std::min<size_t>(8, bytes_left);
+ const size_t bytes_copied = reg_value.GetAsMemoryData(
+ reg_info, heap_data_up->GetBytes() + data_offset, curr_byte_size,
+ byte_order, error);
+ if (bytes_copied == 0)
+ return false;
+ if (bytes_copied >= bytes_left)
+ break;
+ data_offset += bytes_copied;
+ bytes_left -= bytes_copied;
+ ++NGRN;
+ }
+ } else {
+ const RegisterInfo *reg_info = nullptr;
+ if (is_return_value) {
+ // We are assuming we are decoding this immediately after returning from
+ // a function call and that the address of the structure is in x8
+ reg_info = reg_ctx->GetRegisterInfoByName("x8", 0);
+ } else {
+ // We are assuming we are stopped at the first instruction in a function
+ // and that the ABI is being respected so all parameters appear where
+ // they should be (functions with no external linkage can legally violate
+ // the ABI).
+ if (NGRN >= 8)
+ return false;
+
+ reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_ARG1 + NGRN);
+ if (reg_info == nullptr)
+ return false;
+ ++NGRN;
+ }
+
+ if (reg_info == nullptr)
+ return false;
+
+ const lldb::addr_t value_addr =
+ reg_ctx->ReadRegisterAsUnsigned(reg_info, LLDB_INVALID_ADDRESS);
+
+ if (value_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ if (exe_ctx.GetProcessRef().ReadMemory(
+ value_addr, heap_data_up->GetBytes(), heap_data_up->GetByteSize(),
+ error) != heap_data_up->GetByteSize()) {
+ return false;
+ }
+ }
+
+ data.SetByteOrder(byte_order);
+ data.SetAddressByteSize(exe_ctx.GetProcessRef().GetAddressByteSize());
+ data.SetData(DataBufferSP(heap_data_up.release()));
+ return true;
+}
+
+ValueObjectSP ABISysV_arm64::GetReturnValueObjectImpl(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ Value value;
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ if (exe_ctx.GetTargetPtr() == nullptr || exe_ctx.GetProcessPtr() == nullptr)
+ return return_valobj_sp;
+
+ // value.SetContext (Value::eContextTypeClangType, return_compiler_type);
+ value.SetCompilerType(return_compiler_type);
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return return_valobj_sp;
+
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (!byte_size)
+ return return_valobj_sp;
+
+ const uint32_t type_flags = return_compiler_type.GetTypeInfo(nullptr);
+ if (type_flags & eTypeIsScalar || type_flags & eTypeIsPointer) {
+ value.SetValueType(Value::eValueTypeScalar);
+
+ bool success = false;
+ if (type_flags & eTypeIsInteger || type_flags & eTypeIsPointer) {
+ // Extract the register context so we can read arguments from registers
+ if (*byte_size <= 8) {
+ const RegisterInfo *x0_reg_info = nullptr;
+ x0_reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_ARG1);
+ if (x0_reg_info) {
+ uint64_t raw_value =
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(x0_reg_info,
+ 0);
+ const bool is_signed = (type_flags & eTypeIsSigned) != 0;
+ switch (*byte_size) {
+ default:
+ break;
+ case 16: // uint128_t
+ // In register x0 and x1
+ {
+ const RegisterInfo *x1_reg_info = nullptr;
+ x1_reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_ARG2);
+
+ if (x1_reg_info) {
+ if (*byte_size <=
+ x0_reg_info->byte_size + x1_reg_info->byte_size) {
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order =
+ exe_ctx.GetProcessRef().GetByteOrder();
+ RegisterValue x0_reg_value;
+ RegisterValue x1_reg_value;
+ if (reg_ctx->ReadRegister(x0_reg_info, x0_reg_value) &&
+ reg_ctx->ReadRegister(x1_reg_info, x1_reg_value)) {
+ Status error;
+ if (x0_reg_value.GetAsMemoryData(
+ x0_reg_info, heap_data_up->GetBytes() + 0, 8,
+ byte_order, error) &&
+ x1_reg_value.GetAsMemoryData(
+ x1_reg_info, heap_data_up->GetBytes() + 8, 8,
+ byte_order, error)) {
+ DataExtractor data(
+ DataBufferSP(heap_data_up.release()), byte_order,
+ exe_ctx.GetProcessRef().GetAddressByteSize());
+
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), data);
+ return return_valobj_sp;
+ }
+ }
+ }
+ }
+ }
+ break;
+ case sizeof(uint64_t):
+ if (is_signed)
+ value.GetScalar() = (int64_t)(raw_value);
+ else
+ value.GetScalar() = (uint64_t)(raw_value);
+ success = true;
+ break;
+
+ case sizeof(uint32_t):
+ if (is_signed)
+ value.GetScalar() = (int32_t)(raw_value & UINT32_MAX);
+ else
+ value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint16_t):
+ if (is_signed)
+ value.GetScalar() = (int16_t)(raw_value & UINT16_MAX);
+ else
+ value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint8_t):
+ if (is_signed)
+ value.GetScalar() = (int8_t)(raw_value & UINT8_MAX);
+ else
+ value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX);
+ success = true;
+ break;
+ }
+ }
+ }
+ } else if (type_flags & eTypeIsFloat) {
+ if (type_flags & eTypeIsComplex) {
+ // Don't handle complex yet.
+ } else {
+ if (*byte_size <= sizeof(long double)) {
+ const RegisterInfo *v0_reg_info =
+ reg_ctx->GetRegisterInfoByName("v0", 0);
+ RegisterValue v0_value;
+ if (reg_ctx->ReadRegister(v0_reg_info, v0_value)) {
+ DataExtractor data;
+ if (v0_value.GetData(data)) {
+ lldb::offset_t offset = 0;
+ if (*byte_size == sizeof(float)) {
+ value.GetScalar() = data.GetFloat(&offset);
+ success = true;
+ } else if (*byte_size == sizeof(double)) {
+ value.GetScalar() = data.GetDouble(&offset);
+ success = true;
+ } else if (*byte_size == sizeof(long double)) {
+ value.GetScalar() = data.GetLongDouble(&offset);
+ success = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (success)
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if (type_flags & eTypeIsVector && *byte_size <= 16) {
+ if (*byte_size > 0) {
+ const RegisterInfo *v0_info = reg_ctx->GetRegisterInfoByName("v0", 0);
+
+ if (v0_info) {
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order = exe_ctx.GetProcessRef().GetByteOrder();
+ RegisterValue reg_value;
+ if (reg_ctx->ReadRegister(v0_info, reg_value)) {
+ Status error;
+ if (reg_value.GetAsMemoryData(v0_info, heap_data_up->GetBytes(),
+ heap_data_up->GetByteSize(), byte_order,
+ error)) {
+ DataExtractor data(DataBufferSP(heap_data_up.release()), byte_order,
+ exe_ctx.GetProcessRef().GetAddressByteSize());
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), data);
+ }
+ }
+ }
+ }
+ } else if (type_flags & eTypeIsStructUnion || type_flags & eTypeIsClass ||
+ (type_flags & eTypeIsVector && *byte_size > 16)) {
+ DataExtractor data;
+
+ uint32_t NGRN = 0; // Search ABI docs for NGRN
+ uint32_t NSRN = 0; // Search ABI docs for NSRN
+ const bool is_return_value = true;
+ if (LoadValueFromConsecutiveGPRRegisters(
+ exe_ctx, reg_ctx, return_compiler_type, is_return_value, NGRN, NSRN,
+ data)) {
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), data);
+ }
+ }
+ return return_valobj_sp;
+}
+
+void ABISysV_arm64::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ "SysV ABI for AArch64 targets", CreateInstance);
+}
+
+void ABISysV_arm64::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ABISysV_arm64::GetPluginNameStatic() {
+ static ConstString g_name("SysV-arm64");
+ return g_name;
+}
+
+// PluginInterface protocol
+
+ConstString ABISysV_arm64::GetPluginName() { return GetPluginNameStatic(); }
+
+uint32_t ABISysV_arm64::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.h b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.h
new file mode 100644
index 000000000000..1fbdc793ed6e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-arm64/ABISysV_arm64.h
@@ -0,0 +1,100 @@
+//===-- ABISysV_arm64.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 liblldb_ABISysV_arm64_h_
+#define liblldb_ABISysV_arm64_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABISysV_arm64 : public lldb_private::ABI {
+public:
+ ~ABISysV_arm64() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress,
+ lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ // The arm64 ABI requires that stack frames be 16 byte aligned.
+ // When there is a trap handler on the stack, e.g. _sigtramp in userland
+ // code, we've seen that the stack pointer is often not aligned properly
+ // before the handler is invoked. This means that lldb will stop the unwind
+ // early -- before the function which caused the trap.
+ //
+ // To work around this, we relax that alignment to be just word-size
+ // (8-bytes).
+ // Whitelisting the trap handlers for user space would be easy (_sigtramp) but
+ // in other environments there can be a large number of different functions
+ // involved in async traps.
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are are 8 byte aligned
+ if (cfa & (8ull - 1ull))
+ return false; // Not 8 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ if (pc & (4ull - 1ull))
+ return false; // Not 4 byte aligned
+
+ // Anything else if fair game..
+ return true;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ bool GetPointerReturnRegister(const char *&name) override;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ // PluginInterface protocol
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const override;
+
+private:
+ ABISysV_arm64(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABISysV_arm64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-hexagon/ABISysV_hexagon.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-hexagon/ABISysV_hexagon.cpp
new file mode 100644
index 000000000000..93647564fe25
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-hexagon/ABISysV_hexagon.cpp
@@ -0,0 +1,1353 @@
+//===-- ABISysV_hexagon.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 "ABISysV_hexagon.h"
+
+#include "llvm/ADT/Triple.h"
+#include "llvm/IR/DerivedTypes.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Core/ValueObjectRegister.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static RegisterInfo g_register_infos[] = {
+ // hexagon-core.xml
+ {"r00",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {0, 0, LLDB_INVALID_REGNUM, 0, 0},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r01",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {1, 1, LLDB_INVALID_REGNUM, 1, 1},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r02",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {2, 2, LLDB_INVALID_REGNUM, 2, 2},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r03",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {3, 3, LLDB_INVALID_REGNUM, 3, 3},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r04",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {4, 4, LLDB_INVALID_REGNUM, 4, 4},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r05",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {5, 5, LLDB_INVALID_REGNUM, 5, 5},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r06",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {6, 6, LLDB_INVALID_REGNUM, 6, 6},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r07",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {7, 7, LLDB_INVALID_REGNUM, 7, 7},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r08",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {8, 8, LLDB_INVALID_REGNUM, 8, 8},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r09",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {9, 9, LLDB_INVALID_REGNUM, 9, 9},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r10",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {10, 10, LLDB_INVALID_REGNUM, 10, 10},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r11",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {11, 11, LLDB_INVALID_REGNUM, 11, 11},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r12",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {12, 12, LLDB_INVALID_REGNUM, 12, 12},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {13, 13, LLDB_INVALID_REGNUM, 13, 13},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {14, 14, LLDB_INVALID_REGNUM, 14, 14},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r15",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {15, 15, LLDB_INVALID_REGNUM, 15, 15},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r16",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {16, 16, LLDB_INVALID_REGNUM, 16, 16},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r17",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {17, 17, LLDB_INVALID_REGNUM, 17, 17},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r18",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {18, 18, LLDB_INVALID_REGNUM, 18, 18},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r19",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {19, 19, LLDB_INVALID_REGNUM, 19, 19},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r20",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {20, 20, LLDB_INVALID_REGNUM, 20, 20},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r21",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {21, 21, LLDB_INVALID_REGNUM, 21, 21},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r22",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {22, 22, LLDB_INVALID_REGNUM, 22, 22},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r23",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {23, 23, LLDB_INVALID_REGNUM, 23, 23},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r24",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {24, 24, LLDB_INVALID_REGNUM, 24, 24},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r25",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {25, 25, LLDB_INVALID_REGNUM, 25, 25},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r26",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {26, 26, LLDB_INVALID_REGNUM, 26, 26},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r27",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {27, 27, LLDB_INVALID_REGNUM, 27, 27},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r28",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {28, 28, LLDB_INVALID_REGNUM, 28, 28},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"sp",
+ "r29",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {29, 29, LLDB_REGNUM_GENERIC_SP, 29, 29},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fp",
+ "r30",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {30, 30, LLDB_REGNUM_GENERIC_FP, 30, 30},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lr",
+ "r31",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {31, 31, LLDB_REGNUM_GENERIC_RA, 31, 31},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"sa0",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {32, 32, LLDB_INVALID_REGNUM, 32, 32},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lc0",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {33, 33, LLDB_INVALID_REGNUM, 33, 33},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"sa1",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {34, 34, LLDB_INVALID_REGNUM, 34, 34},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lc1",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {35, 35, LLDB_INVALID_REGNUM, 35, 35},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // --> hexagon-v4/5/55/56-sim.xml
+ {"p3_0",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {36, 36, LLDB_INVALID_REGNUM, 36, 36},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // PADDING {
+ {"p00",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {37, 37, LLDB_INVALID_REGNUM, 37, 37},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // }
+ {"m0",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {38, 38, LLDB_INVALID_REGNUM, 38, 38},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"m1",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {39, 39, LLDB_INVALID_REGNUM, 39, 39},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"usr",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {40, 40, LLDB_INVALID_REGNUM, 40, 40},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"pc",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {41, 41, LLDB_REGNUM_GENERIC_PC, 41, 41},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ugp",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {42, 42, LLDB_INVALID_REGNUM, 42, 42},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"gp",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {43, 43, LLDB_INVALID_REGNUM, 43, 43},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cs0",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {44, 44, LLDB_INVALID_REGNUM, 44, 44},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cs1",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {45, 45, LLDB_INVALID_REGNUM, 45, 45},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // PADDING {
+ {"p01",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {46, 46, LLDB_INVALID_REGNUM, 46, 46},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p02",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {47, 47, LLDB_INVALID_REGNUM, 47, 47},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p03",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {48, 48, LLDB_INVALID_REGNUM, 48, 48},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p04",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {49, 49, LLDB_INVALID_REGNUM, 49, 49},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p05",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {50, 50, LLDB_INVALID_REGNUM, 50, 50},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p06",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {51, 51, LLDB_INVALID_REGNUM, 51, 51},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p07",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {52, 52, LLDB_INVALID_REGNUM, 52, 52},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p08",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {53, 53, LLDB_INVALID_REGNUM, 53, 53},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p09",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {54, 54, LLDB_INVALID_REGNUM, 54, 54},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p10",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {55, 55, LLDB_INVALID_REGNUM, 55, 55},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p11",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {56, 56, LLDB_INVALID_REGNUM, 56, 56},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p12",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {57, 57, LLDB_INVALID_REGNUM, 57, 57},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p13",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {58, 58, LLDB_INVALID_REGNUM, 58, 58},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p14",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {59, 59, LLDB_INVALID_REGNUM, 59, 59},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p15",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {60, 60, LLDB_INVALID_REGNUM, 60, 60},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p16",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {61, 61, LLDB_INVALID_REGNUM, 61, 61},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p17",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {62, 62, LLDB_INVALID_REGNUM, 62, 62},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p18",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {63, 63, LLDB_INVALID_REGNUM, 63, 63},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // }
+ {"sgp0",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {64, 64, LLDB_INVALID_REGNUM, 64, 64},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // PADDING {
+ {"p19",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {65, 65, LLDB_INVALID_REGNUM, 65, 65},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // }
+ {"stid",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {66, 66, LLDB_INVALID_REGNUM, 66, 66},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"elr",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {67, 67, LLDB_INVALID_REGNUM, 67, 67},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"badva0",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {68, 68, LLDB_INVALID_REGNUM, 68, 68},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"badva1",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {69, 69, LLDB_INVALID_REGNUM, 69, 69},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ssr",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {70, 70, LLDB_INVALID_REGNUM, 70, 70},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ccr",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {71, 71, LLDB_INVALID_REGNUM, 71, 71},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"htid",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {72, 72, LLDB_INVALID_REGNUM, 72, 72},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // PADDING {
+ {"p20",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {73, 73, LLDB_INVALID_REGNUM, 73, 73},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // }
+ {"imask",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {74, 74, LLDB_INVALID_REGNUM, 74, 74},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // PADDING {
+ {"p21",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {75, 75, LLDB_INVALID_REGNUM, 75, 75},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p22",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {76, 76, LLDB_INVALID_REGNUM, 76, 76},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p23",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {77, 77, LLDB_INVALID_REGNUM, 77, 77},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p24",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {78, 78, LLDB_INVALID_REGNUM, 78, 78},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"p25",
+ "",
+ 4,
+ 0,
+ eEncodingInvalid,
+ eFormatInvalid,
+ {79, 79, LLDB_INVALID_REGNUM, 79, 79},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // }
+ {"g0",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {80, 80, LLDB_INVALID_REGNUM, 80, 80},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"g1",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {81, 81, LLDB_INVALID_REGNUM, 81, 81},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"g2",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {82, 82, LLDB_INVALID_REGNUM, 82, 82},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"g3",
+ "",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatAddressInfo,
+ {83, 83, LLDB_INVALID_REGNUM, 83, 83},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0}};
+
+static const uint32_t k_num_register_infos =
+ sizeof(g_register_infos) / sizeof(RegisterInfo);
+static bool g_register_info_names_constified = false;
+
+const lldb_private::RegisterInfo *
+ABISysV_hexagon::GetRegisterInfoArray(uint32_t &count) {
+ // Make the C-string names and alt_names for the register infos into const
+ // C-string values by having the ConstString unique the names in the global
+ // constant C-string pool.
+ if (!g_register_info_names_constified) {
+ g_register_info_names_constified = true;
+ for (uint32_t i = 0; i < k_num_register_infos; ++i) {
+ if (g_register_infos[i].name)
+ g_register_infos[i].name =
+ ConstString(g_register_infos[i].name).GetCString();
+ if (g_register_infos[i].alt_name)
+ g_register_infos[i].alt_name =
+ ConstString(g_register_infos[i].alt_name).GetCString();
+ }
+ }
+ count = k_num_register_infos;
+ return g_register_infos;
+}
+
+/*
+ http://en.wikipedia.org/wiki/Red_zone_%28computing%29
+
+ In computing, a red zone is a fixed size area in memory beyond the stack
+ pointer that has not been
+ "allocated". This region of memory is not to be modified by
+ interrupt/exception/signal handlers.
+ This allows the space to be used for temporary data without the extra
+ overhead of modifying the
+ stack pointer. The x86-64 ABI mandates a 128 byte red zone.[1] The OpenRISC
+ toolchain assumes a
+ 128 byte red zone though it is not documented.
+*/
+size_t ABISysV_hexagon::GetRedZoneSize() const { return 0; }
+
+// Static Functions
+
+ABISP
+ABISysV_hexagon::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) {
+ if (arch.GetTriple().getArch() == llvm::Triple::hexagon) {
+ return ABISP(new ABISysV_hexagon(process_sp));
+ }
+ return ABISP();
+}
+
+bool ABISysV_hexagon::PrepareTrivialCall(Thread &thread, lldb::addr_t sp,
+ lldb::addr_t pc, lldb::addr_t ra,
+ llvm::ArrayRef<addr_t> args) const {
+ // we don't use the traditional trivial call specialized for jit
+ return false;
+}
+
+/*
+
+// AD:
+// . safeguard the current stack
+// . how can we know that the called function will create its own frame
+properly?
+// . we could manually make a new stack first:
+// 2. push RA
+// 3. push FP
+// 4. FP = SP
+// 5. SP = SP ( since no locals in our temp frame )
+
+// AD 6/05/2014
+// . variable argument list parameters are not passed via registers, they are
+passed on
+// the stack. This presents us with a problem, since we need to know when
+the valist
+// starts. Currently I can find out if a function is varg, but not how many
+// real parameters it takes. Thus I don't know when to start spilling the
+vargs. For
+// the time being, to progress, I will assume that it takes on real parameter
+before
+// the vargs list starts.
+
+// AD 06/05/2014
+// . how do we adhere to the stack alignment requirements
+
+// AD 06/05/2014
+// . handle 64bit values and their register / stack requirements
+
+*/
+#define HEX_ABI_DEBUG 0
+bool ABISysV_hexagon::PrepareTrivialCall(
+ Thread &thread, lldb::addr_t sp, lldb::addr_t pc, lldb::addr_t ra,
+ llvm::Type &prototype, llvm::ArrayRef<ABI::CallArgument> args) const {
+ // default number of register passed arguments for varg functions
+ const int nVArgRegParams = 1;
+ Status error;
+
+ // grab the process so we have access to the memory for spilling
+ lldb::ProcessSP proc = thread.GetProcess();
+
+ // get the register context for modifying all of the registers
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+
+ uint32_t pc_reg = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ if (pc_reg == LLDB_INVALID_REGNUM)
+ return false;
+
+ uint32_t ra_reg = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA);
+ if (ra_reg == LLDB_INVALID_REGNUM)
+ return false;
+
+ uint32_t sp_reg = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+ if (sp_reg == LLDB_INVALID_REGNUM)
+ return false;
+
+ // push host data onto target
+ for (size_t i = 0; i < args.size(); i++) {
+ const ABI::CallArgument &arg = args[i];
+ // skip over target values
+ if (arg.type == ABI::CallArgument::TargetValue)
+ continue;
+ // round up to 8 byte multiple
+ size_t argSize = (arg.size | 0x7) + 1;
+
+ // create space on the stack for this data
+ sp -= argSize;
+
+ // write this argument onto the stack of the host process
+ proc->WriteMemory(sp, arg.data_up.get(), arg.size, error);
+ if (error.Fail())
+ return false;
+
+ // update the argument with the target pointer
+ // XXX: This is a gross hack for getting around the const
+ *const_cast<lldb::addr_t *>(&arg.value) = sp;
+ }
+
+#if HEX_ABI_DEBUG
+ // print the original stack pointer
+ printf("sp : %04" PRIx64 " \n", sp);
+#endif
+
+ // make sure number of parameters matches prototype
+ assert(prototype.getFunctionNumParams() == args.size());
+
+ // check if this is a variable argument function
+ bool isVArg = prototype.isFunctionVarArg();
+
+ // number of arguments passed by register
+ int nRegArgs = nVArgRegParams;
+ if (!isVArg) {
+ // number of arguments is limited by [R0 : R5] space
+ nRegArgs = args.size();
+ if (nRegArgs > 6)
+ nRegArgs = 6;
+ }
+
+ // pass arguments that are passed via registers
+ for (int i = 0; i < nRegArgs; i++) {
+ // get the parameter as a u32
+ uint32_t param = (uint32_t)args[i].value;
+ // write argument into register
+ if (!reg_ctx->WriteRegisterFromUnsigned(i, param))
+ return false;
+ }
+
+ // number of arguments to spill onto stack
+ int nSpillArgs = args.size() - nRegArgs;
+ // make space on the stack for arguments
+ sp -= 4 * nSpillArgs;
+ // align stack on an 8 byte boundary
+ if (sp & 7)
+ sp -= 4;
+
+ // arguments that are passed on the stack
+ for (size_t i = nRegArgs, offs = 0; i < args.size(); i++) {
+ // get the parameter as a u32
+ uint32_t param = (uint32_t)args[i].value;
+ // write argument to stack
+ proc->WriteMemory(sp + offs, (void *)&param, sizeof(param), error);
+ if (!error.Success())
+ return false;
+ //
+ offs += 4;
+ }
+
+ // update registers with current function call state
+ reg_ctx->WriteRegisterFromUnsigned(pc_reg, pc);
+ reg_ctx->WriteRegisterFromUnsigned(ra_reg, ra);
+ reg_ctx->WriteRegisterFromUnsigned(sp_reg, sp);
+
+#if HEX_ABI_DEBUG
+ // quick and dirty stack dumper for debugging
+ for (int i = -8; i < 8; i++) {
+ uint32_t data = 0;
+ lldb::addr_t addr = sp + i * 4;
+ proc->ReadMemory(addr, (void *)&data, sizeof(data), error);
+ printf("\n0x%04" PRIx64 " 0x%08x ", addr, data);
+ if (i == 0)
+ printf("<<-- sp");
+ }
+ printf("\n");
+#endif
+
+ return true;
+}
+
+bool ABISysV_hexagon::GetArgumentValues(Thread &thread,
+ ValueList &values) const {
+ return false;
+}
+
+Status
+ABISysV_hexagon::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ return error;
+}
+
+ValueObjectSP ABISysV_hexagon::GetReturnValueObjectSimple(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ return return_valobj_sp;
+}
+
+ValueObjectSP ABISysV_hexagon::GetReturnValueObjectImpl(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ return return_valobj_sp;
+}
+
+// called when we are on the first instruction of a new function for hexagon
+// the return address is in RA (R31)
+bool ABISysV_hexagon::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindGeneric);
+ unwind_plan.SetReturnAddressRegister(LLDB_REGNUM_GENERIC_RA);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our Call Frame Address is the stack pointer value
+ row->GetCFAValue().SetIsRegisterPlusOffset(LLDB_REGNUM_GENERIC_SP, 4);
+ row->SetOffset(0);
+
+ // The previous PC is in the LR
+ row->SetRegisterLocationToRegister(LLDB_REGNUM_GENERIC_PC,
+ LLDB_REGNUM_GENERIC_RA, true);
+ unwind_plan.AppendRow(row);
+
+ unwind_plan.SetSourceName("hexagon at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ return true;
+}
+
+bool ABISysV_hexagon::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindGeneric);
+
+ uint32_t fp_reg_num = LLDB_REGNUM_GENERIC_FP;
+ uint32_t sp_reg_num = LLDB_REGNUM_GENERIC_SP;
+ uint32_t pc_reg_num = LLDB_REGNUM_GENERIC_PC;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ row->GetCFAValue().SetIsRegisterPlusOffset(LLDB_REGNUM_GENERIC_FP, 8);
+
+ row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, -8, true);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -4, true);
+ row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("hexagon default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ return true;
+}
+
+/*
+ Register Usage Saved By
+
+ R0 - R5 parameters(a) -
+ R6 - R15 Scratch(b) Caller
+ R16 - R27 Scratch Callee
+ R28 Scratch(b) Caller
+ R29 - R31 Stack Frames Callee(c)
+ P3:0 Processor State Caller
+
+ a = the caller can change parameter values
+ b = R14 - R15 and R28 are used by the procedure linkage table
+ c = R29 - R31 are saved and restored by allocframe() and deallocframe()
+*/
+bool ABISysV_hexagon::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ return !RegisterIsCalleeSaved(reg_info);
+}
+
+bool ABISysV_hexagon::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
+ int reg = ((reg_info->byte_offset) / 4);
+
+ bool save = (reg >= 16) && (reg <= 27);
+ save |= (reg >= 29) && (reg <= 32);
+
+ return save;
+}
+
+void ABISysV_hexagon::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ "System V ABI for hexagon targets",
+ CreateInstance);
+}
+
+void ABISysV_hexagon::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ABISysV_hexagon::GetPluginNameStatic() {
+ static ConstString g_name("sysv-hexagon");
+ return g_name;
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString ABISysV_hexagon::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ABISysV_hexagon::GetPluginVersion() { return 1; }
+
+// get value object specialized to work with llvm IR types
+lldb::ValueObjectSP
+ABISysV_hexagon::GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ llvm::Type &retType) const {
+ Value value;
+ ValueObjectSP vObjSP;
+
+ // get the current register context
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return vObjSP;
+
+ // for now just pop R0 to find the return value
+ const lldb_private::RegisterInfo *r0_info =
+ reg_ctx->GetRegisterInfoAtIndex(0);
+ if (r0_info == nullptr)
+ return vObjSP;
+
+ // void return type
+ if (retType.isVoidTy()) {
+ value.GetScalar() = 0;
+ }
+ // integer / pointer return type
+ else if (retType.isIntegerTy() || retType.isPointerTy()) {
+ // read r0 register value
+ lldb_private::RegisterValue r0_value;
+ if (!reg_ctx->ReadRegister(r0_info, r0_value))
+ return vObjSP;
+
+ // push r0 into value
+ uint32_t r0_u32 = r0_value.GetAsUInt32();
+
+ // account for integer size
+ if (retType.isIntegerTy() && retType.isSized()) {
+ uint64_t size = retType.getScalarSizeInBits();
+ uint64_t mask = (1ull << size) - 1;
+ // mask out higher order bits then the type we expect
+ r0_u32 &= mask;
+ }
+
+ value.GetScalar() = r0_u32;
+ }
+ // unsupported return type
+ else
+ return vObjSP;
+
+ // pack the value into a ValueObjectSP
+ vObjSP = ValueObjectConstResult::Create(thread.GetStackFrameAtIndex(0).get(),
+ value, ConstString(""));
+ return vObjSP;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-hexagon/ABISysV_hexagon.h b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-hexagon/ABISysV_hexagon.h
new file mode 100644
index 000000000000..459b6315dba2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-hexagon/ABISysV_hexagon.h
@@ -0,0 +1,105 @@
+//===-- ABISysV_hexagon.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 liblldb_ABISysV_hexagon_h_
+#define liblldb_ABISysV_hexagon_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABISysV_hexagon : public lldb_private::ABI {
+public:
+ ~ABISysV_hexagon() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress,
+ lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ // special thread plan for GDB style non-jit function calls
+ bool
+ PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress, lldb::addr_t returnAddress,
+ llvm::Type &prototype,
+ llvm::ArrayRef<ABI::CallArgument> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &type) const override;
+
+ // specialized to work with llvm IR types
+ lldb::ValueObjectSP GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ llvm::Type &type) const override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are 8 byte aligned
+ if (cfa & 0x07)
+ return false; // Not 8 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ // We have a 64 bit address space, so anything is valid as opcodes
+ // aren't fixed width...
+ return true;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ // PluginInterface protocol
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ void CreateRegisterMapIfNeeded();
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectSimple(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const;
+
+ bool RegisterIsCalleeSaved(const lldb_private::RegisterInfo *reg_info);
+
+private:
+ ABISysV_hexagon(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABISysV_hexagon_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-i386/ABISysV_i386.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-i386/ABISysV_i386.cpp
new file mode 100644
index 000000000000..05f5dba90687
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-i386/ABISysV_i386.cpp
@@ -0,0 +1,849 @@
+//===----------------------- ABISysV_i386.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 "ABISysV_i386.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Core/ValueObjectRegister.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// This source file uses the following document as a reference:
+//====================================================================
+// System V Application Binary Interface
+// Intel386 Architecture Processor Supplement, Version 1.0
+// Edited by
+// H.J. Lu, David L Kreitzer, Milind Girkar, Zia Ansari
+//
+// (Based on
+// System V Application Binary Interface,
+// AMD64 Architecture Processor Supplement,
+// Edited by
+// H.J. Lu, Michael Matz, Milind Girkar, Jan Hubicka,
+// Andreas Jaeger, Mark Mitchell)
+//
+// February 3, 2015
+//====================================================================
+
+// DWARF Register Number Mapping
+// See Table 2.14 of the reference document (specified on top of this file)
+// Comment: Table 2.14 is followed till 'mm' entries. After that, all entries
+// are ignored here.
+
+enum dwarf_regnums {
+ dwarf_eax = 0,
+ dwarf_ecx,
+ dwarf_edx,
+ dwarf_ebx,
+ dwarf_esp,
+ dwarf_ebp,
+ dwarf_esi,
+ dwarf_edi,
+ dwarf_eip,
+ dwarf_eflags,
+
+ dwarf_st0 = 11,
+ dwarf_st1,
+ dwarf_st2,
+ dwarf_st3,
+ dwarf_st4,
+ dwarf_st5,
+ dwarf_st6,
+ dwarf_st7,
+
+ 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_mm0 = 29,
+ dwarf_mm1,
+ dwarf_mm2,
+ dwarf_mm3,
+ dwarf_mm4,
+ dwarf_mm5,
+ dwarf_mm6,
+ dwarf_mm7,
+
+ dwarf_bnd0 = 101,
+ dwarf_bnd1,
+ dwarf_bnd2,
+ dwarf_bnd3
+};
+
+static RegisterInfo g_register_infos[] = {
+ // clang-format off
+ //NAME ALT SZ OFF ENCODING FORMAT EH_FRAME DWARF GENERIC PROCESS PLUGIN LLDB NATIVE VALUE INVAL DYN EXPR SZ
+ //========== ======= == === ============= ==================== =================== =================== ========================= =================== =================== ======= ======= ======== ==
+ {"eax", nullptr, 4, 0, eEncodingUint, eFormatHex, {dwarf_eax, dwarf_eax, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ebx", nullptr, 4, 0, eEncodingUint, eFormatHex, {dwarf_ebx, dwarf_ebx, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ecx", nullptr, 4, 0, eEncodingUint, eFormatHex, {dwarf_ecx, dwarf_ecx, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"edx", nullptr, 4, 0, eEncodingUint, eFormatHex, {dwarf_edx, dwarf_edx, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"esi", nullptr, 4, 0, eEncodingUint, eFormatHex, {dwarf_esi, dwarf_esi, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"edi", nullptr, 4, 0, eEncodingUint, eFormatHex, {dwarf_edi, dwarf_edi, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ebp", "fp", 4, 0, eEncodingUint, eFormatHex, {dwarf_ebp, dwarf_ebp, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"esp", "sp", 4, 0, eEncodingUint, eFormatHex, {dwarf_esp, dwarf_esp, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"eip", "pc", 4, 0, eEncodingUint, eFormatHex, {dwarf_eip, dwarf_eip, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"eflags", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_FLAGS,LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"cs", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ss", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ds", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"es", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fs", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"gs", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"st0", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_st0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"st1", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_st1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"st2", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_st2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"st3", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_st3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"st4", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_st4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"st5", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_st5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"st6", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_st6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"st7", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_st7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fctrl", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fstat", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ftag", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fiseg", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fioff", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"foseg", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fooff", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fop", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm0", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_xmm0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm1", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_xmm1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm2", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_xmm2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm3", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_xmm3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm4", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_xmm4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm5", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_xmm5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm6", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_xmm6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm7", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_xmm7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"mxcsr", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm0", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_ymm0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm1", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_ymm1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm2", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_ymm2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm3", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_ymm3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm4", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_ymm4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm5", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_ymm5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm6", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_ymm6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm7", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_ymm7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"bnd0", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt64,{dwarf_bnd0, dwarf_bnd0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"bnd1", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt64,{dwarf_bnd1, dwarf_bnd1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"bnd2", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt64,{dwarf_bnd2, dwarf_bnd2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"bnd3", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt64,{dwarf_bnd3, dwarf_bnd3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"bndcfgu", nullptr, 8, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"bndstatus",nullptr, 8, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}
+ // clang-format on
+};
+
+static const uint32_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos);
+static bool g_register_info_names_constified = false;
+
+const lldb_private::RegisterInfo *
+ABISysV_i386::GetRegisterInfoArray(uint32_t &count) {
+ // Make the C-string names and alt_names for the register infos into const
+ // C-string values by having the ConstString unique the names in the global
+ // constant C-string pool.
+ if (!g_register_info_names_constified) {
+ g_register_info_names_constified = true;
+ for (uint32_t i = 0; i < k_num_register_infos; ++i) {
+ if (g_register_infos[i].name)
+ g_register_infos[i].name =
+ ConstString(g_register_infos[i].name).GetCString();
+ if (g_register_infos[i].alt_name)
+ g_register_infos[i].alt_name =
+ ConstString(g_register_infos[i].alt_name).GetCString();
+ }
+ }
+ count = k_num_register_infos;
+ return g_register_infos;
+}
+
+// Static Functions
+
+ABISP
+ABISysV_i386::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) {
+ if (arch.GetTriple().getVendor() != llvm::Triple::Apple) {
+ if (arch.GetTriple().getArch() == llvm::Triple::x86) {
+ return ABISP(new ABISysV_i386(process_sp));
+ }
+ }
+ return ABISP();
+}
+
+bool ABISysV_i386::PrepareTrivialCall(Thread &thread, addr_t sp,
+ addr_t func_addr, addr_t return_addr,
+ llvm::ArrayRef<addr_t> args) const {
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+
+ if (!reg_ctx)
+ return false;
+
+ uint32_t pc_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ uint32_t sp_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+
+ // While using register info to write a register value to memory, the
+ // register info just needs to have the correct size of a 32 bit register,
+ // the actual register it pertains to is not important, just the size needs
+ // to be correct. "eax" is used here for this purpose.
+ const RegisterInfo *reg_info_32 = reg_ctx->GetRegisterInfoByName("eax");
+ if (!reg_info_32)
+ return false; // TODO this should actually never happen
+
+ Status error;
+ RegisterValue reg_value;
+
+ // Make room for the argument(s) on the stack
+ sp -= 4 * args.size();
+
+ // SP Alignment
+ sp &= ~(16ull - 1ull); // 16-byte alignment
+
+ // Write arguments onto the stack
+ addr_t arg_pos = sp;
+ for (addr_t arg : args) {
+ reg_value.SetUInt32(arg);
+ error = reg_ctx->WriteRegisterValueToMemory(
+ reg_info_32, arg_pos, reg_info_32->byte_size, reg_value);
+ if (error.Fail())
+ return false;
+ arg_pos += 4;
+ }
+
+ // The return address is pushed onto the stack
+ sp -= 4;
+ reg_value.SetUInt32(return_addr);
+ error = reg_ctx->WriteRegisterValueToMemory(
+ reg_info_32, sp, reg_info_32->byte_size, reg_value);
+ if (error.Fail())
+ return false;
+
+ // Setting %esp to the actual stack value.
+ if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_num, sp))
+ return false;
+
+ // Setting %eip to the address of the called function.
+ if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_num, func_addr))
+ return false;
+
+ return true;
+}
+
+static bool ReadIntegerArgument(Scalar &scalar, unsigned int bit_width,
+ bool is_signed, Process *process,
+ addr_t &current_stack_argument) {
+ uint32_t byte_size = (bit_width + (8 - 1)) / 8;
+ Status error;
+
+ if (!process)
+ return false;
+
+ if (process->ReadScalarIntegerFromMemory(current_stack_argument, byte_size,
+ is_signed, scalar, error)) {
+ current_stack_argument += byte_size;
+ return true;
+ }
+ return false;
+}
+
+bool ABISysV_i386::GetArgumentValues(Thread &thread, ValueList &values) const {
+ unsigned int num_values = values.GetSize();
+ unsigned int value_index;
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+
+ if (!reg_ctx)
+ return false;
+
+ // Get pointer to the first stack argument
+ addr_t sp = reg_ctx->GetSP(0);
+ if (!sp)
+ return false;
+
+ addr_t current_stack_argument = sp + 4; // jump over return address
+
+ for (value_index = 0; value_index < num_values; ++value_index) {
+ Value *value = values.GetValueAtIndex(value_index);
+
+ if (!value)
+ return false;
+
+ // Currently: Support for extracting values with Clang QualTypes only.
+ CompilerType compiler_type(value->GetCompilerType());
+ llvm::Optional<uint64_t> bit_size = compiler_type.GetBitSize(&thread);
+ if (bit_size) {
+ bool is_signed;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed)) {
+ ReadIntegerArgument(value->GetScalar(), *bit_size, is_signed,
+ thread.GetProcess().get(), current_stack_argument);
+ } else if (compiler_type.IsPointerType()) {
+ ReadIntegerArgument(value->GetScalar(), *bit_size, false,
+ thread.GetProcess().get(), current_stack_argument);
+ }
+ }
+ }
+ return true;
+}
+
+Status ABISysV_i386::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType compiler_type = new_value_sp->GetCompilerType();
+ if (!compiler_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ const uint32_t type_flags = compiler_type.GetTypeInfo();
+ Thread *thread = frame_sp->GetThread().get();
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ bool register_write_successful = true;
+
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+
+ // Following "IF ELSE" block categorizes various 'Fundamental Data Types'.
+ // The terminology 'Fundamental Data Types' used here is adopted from Table
+ // 2.1 of the reference document (specified on top of this file)
+
+ if (type_flags & eTypeIsPointer) // 'Pointer'
+ {
+ if (num_bytes != sizeof(uint32_t)) {
+ error.SetErrorString("Pointer to be returned is not 4 bytes wide");
+ return error;
+ }
+ lldb::offset_t offset = 0;
+ const RegisterInfo *eax_info = reg_ctx->GetRegisterInfoByName("eax", 0);
+ uint32_t raw_value = data.GetMaxU32(&offset, num_bytes);
+ register_write_successful =
+ reg_ctx->WriteRegisterFromUnsigned(eax_info, raw_value);
+ } else if ((type_flags & eTypeIsScalar) ||
+ (type_flags & eTypeIsEnumeration)) //'Integral' + 'Floating Point'
+ {
+ lldb::offset_t offset = 0;
+ const RegisterInfo *eax_info = reg_ctx->GetRegisterInfoByName("eax", 0);
+
+ if (type_flags & eTypeIsInteger) // 'Integral' except enum
+ {
+ switch (num_bytes) {
+ default:
+ break;
+ case 16:
+ // For clang::BuiltinType::UInt128 & Int128 ToDo: Need to decide how to
+ // handle it
+ break;
+ case 8: {
+ uint32_t raw_value_low = data.GetMaxU32(&offset, 4);
+ const RegisterInfo *edx_info = reg_ctx->GetRegisterInfoByName("edx", 0);
+ uint32_t raw_value_high = data.GetMaxU32(&offset, num_bytes - offset);
+ register_write_successful =
+ (reg_ctx->WriteRegisterFromUnsigned(eax_info, raw_value_low) &&
+ reg_ctx->WriteRegisterFromUnsigned(edx_info, raw_value_high));
+ break;
+ }
+ case 4:
+ case 2:
+ case 1: {
+ uint32_t raw_value = data.GetMaxU32(&offset, num_bytes);
+ register_write_successful =
+ reg_ctx->WriteRegisterFromUnsigned(eax_info, raw_value);
+ break;
+ }
+ }
+ } else if (type_flags & eTypeIsEnumeration) // handles enum
+ {
+ uint32_t raw_value = data.GetMaxU32(&offset, num_bytes);
+ register_write_successful =
+ reg_ctx->WriteRegisterFromUnsigned(eax_info, raw_value);
+ } else if (type_flags & eTypeIsFloat) // 'Floating Point'
+ {
+ RegisterValue st0_value, fstat_value, ftag_value;
+ const RegisterInfo *st0_info = reg_ctx->GetRegisterInfoByName("st0", 0);
+ const RegisterInfo *fstat_info =
+ reg_ctx->GetRegisterInfoByName("fstat", 0);
+ const RegisterInfo *ftag_info = reg_ctx->GetRegisterInfoByName("ftag", 0);
+
+ /* According to Page 3-12 of document
+ System V Application Binary Interface, Intel386 Architecture Processor
+ Supplement, Fourth Edition
+ To return Floating Point values, all st% registers except st0 should be
+ empty after exiting from
+ a function. This requires setting fstat and ftag registers to specific
+ values.
+ fstat: The TOP field of fstat should be set to a value [0,7]. ABI doesn't
+ specify the specific
+ value of TOP in case of function return. Hence, we set the TOP field to 7
+ by our choice. */
+ uint32_t value_fstat_u32 = 0x00003800;
+
+ /* ftag: Implication of setting TOP to 7 and indicating all st% registers
+ empty except st0 is to set
+ 7th bit of 4th byte of FXSAVE area to 1 and all other bits of this byte to
+ 0. This is in accordance
+ with the document Intel 64 and IA-32 Architectures Software Developer's
+ Manual, January 2015 */
+ uint32_t value_ftag_u32 = 0x00000080;
+
+ if (num_bytes <= 12) // handles float, double, long double, __float80
+ {
+ long double value_long_dbl = 0.0;
+ if (num_bytes == 4)
+ value_long_dbl = data.GetFloat(&offset);
+ else if (num_bytes == 8)
+ value_long_dbl = data.GetDouble(&offset);
+ else if (num_bytes == 12)
+ value_long_dbl = data.GetLongDouble(&offset);
+ else {
+ error.SetErrorString("Invalid number of bytes for this return type");
+ return error;
+ }
+ st0_value.SetLongDouble(value_long_dbl);
+ fstat_value.SetUInt32(value_fstat_u32);
+ ftag_value.SetUInt32(value_ftag_u32);
+ register_write_successful =
+ reg_ctx->WriteRegister(st0_info, st0_value) &&
+ reg_ctx->WriteRegister(fstat_info, fstat_value) &&
+ reg_ctx->WriteRegister(ftag_info, ftag_value);
+ } else if (num_bytes == 16) // handles __float128
+ {
+ error.SetErrorString("Implementation is missing for this clang type.");
+ }
+ } else {
+ // Neither 'Integral' nor 'Floating Point'. If flow reaches here then
+ // check type_flags. This type_flags is not a valid type.
+ error.SetErrorString("Invalid clang type");
+ }
+ } else {
+ /* 'Complex Floating Point', 'Packed', 'Decimal Floating Point' and
+ 'Aggregate' data types
+ are yet to be implemented */
+ error.SetErrorString("Currently only Integral and Floating Point clang "
+ "types are supported.");
+ }
+ if (!register_write_successful)
+ error.SetErrorString("Register writing failed");
+ return error;
+}
+
+ValueObjectSP ABISysV_i386::GetReturnValueObjectSimple(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ Value value;
+
+ if (!return_compiler_type)
+ return return_valobj_sp;
+
+ value.SetCompilerType(return_compiler_type);
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return return_valobj_sp;
+
+ const uint32_t type_flags = return_compiler_type.GetTypeInfo();
+
+ unsigned eax_id =
+ reg_ctx->GetRegisterInfoByName("eax", 0)->kinds[eRegisterKindLLDB];
+ unsigned edx_id =
+ reg_ctx->GetRegisterInfoByName("edx", 0)->kinds[eRegisterKindLLDB];
+
+ // Following "IF ELSE" block categorizes various 'Fundamental Data Types'.
+ // The terminology 'Fundamental Data Types' used here is adopted from Table
+ // 2.1 of the reference document (specified on top of this file)
+
+ if (type_flags & eTypeIsPointer) // 'Pointer'
+ {
+ uint32_t ptr =
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xffffffff;
+ value.SetValueType(Value::eValueTypeScalar);
+ value.GetScalar() = ptr;
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if ((type_flags & eTypeIsScalar) ||
+ (type_flags & eTypeIsEnumeration)) //'Integral' + 'Floating Point'
+ {
+ value.SetValueType(Value::eValueTypeScalar);
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (!byte_size)
+ return return_valobj_sp;
+ bool success = false;
+
+ if (type_flags & eTypeIsInteger) // 'Integral' except enum
+ {
+ const bool is_signed = ((type_flags & eTypeIsSigned) != 0);
+ uint64_t raw_value =
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xffffffff;
+ raw_value |=
+ (thread.GetRegisterContext()->ReadRegisterAsUnsigned(edx_id, 0) &
+ 0xffffffff)
+ << 32;
+
+ switch (*byte_size) {
+ default:
+ break;
+
+ case 16:
+ // For clang::BuiltinType::UInt128 & Int128 ToDo: Need to decide how to
+ // handle it
+ break;
+
+ case 8:
+ if (is_signed)
+ value.GetScalar() = (int64_t)(raw_value);
+ else
+ value.GetScalar() = (uint64_t)(raw_value);
+ success = true;
+ break;
+
+ case 4:
+ if (is_signed)
+ value.GetScalar() = (int32_t)(raw_value & UINT32_MAX);
+ else
+ value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX);
+ success = true;
+ break;
+
+ case 2:
+ if (is_signed)
+ value.GetScalar() = (int16_t)(raw_value & UINT16_MAX);
+ else
+ value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX);
+ success = true;
+ break;
+
+ case 1:
+ if (is_signed)
+ value.GetScalar() = (int8_t)(raw_value & UINT8_MAX);
+ else
+ value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX);
+ success = true;
+ break;
+ }
+
+ if (success)
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if (type_flags & eTypeIsEnumeration) // handles enum
+ {
+ uint32_t enm =
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xffffffff;
+ value.SetValueType(Value::eValueTypeScalar);
+ value.GetScalar() = enm;
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if (type_flags & eTypeIsFloat) // 'Floating Point'
+ {
+ if (*byte_size <= 12) // handles float, double, long double, __float80
+ {
+ const RegisterInfo *st0_info = reg_ctx->GetRegisterInfoByName("st0", 0);
+ RegisterValue st0_value;
+
+ if (reg_ctx->ReadRegister(st0_info, st0_value)) {
+ DataExtractor data;
+ if (st0_value.GetData(data)) {
+ lldb::offset_t offset = 0;
+ long double value_long_double = data.GetLongDouble(&offset);
+
+ // float is 4 bytes.
+ if (*byte_size == 4) {
+ float value_float = (float)value_long_double;
+ value.GetScalar() = value_float;
+ success = true;
+ } else if (*byte_size == 8) {
+ // double is 8 bytes
+ // On Android Platform: long double is also 8 bytes It will be
+ // handled here only.
+ double value_double = (double)value_long_double;
+ value.GetScalar() = value_double;
+ success = true;
+ } else if (*byte_size == 12) {
+ // long double and __float80 are 12 bytes on i386.
+ value.GetScalar() = value_long_double;
+ success = true;
+ }
+ }
+ }
+
+ if (success)
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if (*byte_size == 16) // handles __float128
+ {
+ lldb::addr_t storage_addr = (uint32_t)(
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xffffffff);
+ return_valobj_sp = ValueObjectMemory::Create(
+ &thread, "", Address(storage_addr, nullptr), return_compiler_type);
+ }
+ } else // Neither 'Integral' nor 'Floating Point'
+ {
+ // If flow reaches here then check type_flags This type_flags is
+ // unhandled
+ }
+ } else if (type_flags & eTypeIsComplex) // 'Complex Floating Point'
+ {
+ // ToDo: Yet to be implemented
+ } else if (type_flags & eTypeIsVector) // 'Packed'
+ {
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (byte_size && *byte_size > 0) {
+ const RegisterInfo *vec_reg = reg_ctx->GetRegisterInfoByName("xmm0", 0);
+ if (vec_reg == nullptr)
+ vec_reg = reg_ctx->GetRegisterInfoByName("mm0", 0);
+
+ if (vec_reg) {
+ if (*byte_size <= vec_reg->byte_size) {
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp) {
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order = process_sp->GetByteOrder();
+ RegisterValue reg_value;
+ if (reg_ctx->ReadRegister(vec_reg, reg_value)) {
+ Status error;
+ if (reg_value.GetAsMemoryData(vec_reg, heap_data_up->GetBytes(),
+ heap_data_up->GetByteSize(),
+ byte_order, error)) {
+ DataExtractor data(DataBufferSP(heap_data_up.release()),
+ byte_order,
+ process_sp->GetTarget()
+ .GetArchitecture()
+ .GetAddressByteSize());
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), data);
+ }
+ }
+ }
+ } else if (*byte_size <= vec_reg->byte_size * 2) {
+ const RegisterInfo *vec_reg2 =
+ reg_ctx->GetRegisterInfoByName("xmm1", 0);
+ if (vec_reg2) {
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp) {
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order = process_sp->GetByteOrder();
+ RegisterValue reg_value;
+ RegisterValue reg_value2;
+ if (reg_ctx->ReadRegister(vec_reg, reg_value) &&
+ reg_ctx->ReadRegister(vec_reg2, reg_value2)) {
+
+ Status error;
+ if (reg_value.GetAsMemoryData(vec_reg, heap_data_up->GetBytes(),
+ vec_reg->byte_size, byte_order,
+ error) &&
+ reg_value2.GetAsMemoryData(
+ vec_reg2, heap_data_up->GetBytes() + vec_reg->byte_size,
+ heap_data_up->GetByteSize() - vec_reg->byte_size,
+ byte_order, error)) {
+ DataExtractor data(DataBufferSP(heap_data_up.release()),
+ byte_order,
+ process_sp->GetTarget()
+ .GetArchitecture()
+ .GetAddressByteSize());
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), data);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } else // 'Decimal Floating Point'
+ {
+ // ToDo: Yet to be implemented
+ }
+ return return_valobj_sp;
+}
+
+ValueObjectSP ABISysV_i386::GetReturnValueObjectImpl(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+
+ if (!return_compiler_type)
+ return return_valobj_sp;
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ return_valobj_sp = GetReturnValueObjectSimple(thread, return_compiler_type);
+ if (return_valobj_sp)
+ return return_valobj_sp;
+
+ RegisterContextSP reg_ctx_sp = thread.GetRegisterContext();
+ if (!reg_ctx_sp)
+ return return_valobj_sp;
+
+ if (return_compiler_type.IsAggregateType()) {
+ unsigned eax_id =
+ reg_ctx_sp->GetRegisterInfoByName("eax", 0)->kinds[eRegisterKindLLDB];
+ lldb::addr_t storage_addr = (uint32_t)(
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &
+ 0xffffffff);
+ return_valobj_sp = ValueObjectMemory::Create(
+ &thread, "", Address(storage_addr, nullptr), return_compiler_type);
+ }
+
+ return return_valobj_sp;
+}
+
+// This defines CFA as esp+4
+// The saved pc is at CFA-4 (i.e. esp+0)
+// The saved esp is CFA+0
+
+bool ABISysV_i386::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t sp_reg_num = dwarf_esp;
+ uint32_t pc_reg_num = dwarf_eip;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 4);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -4, false);
+ row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("i386 at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ return true;
+}
+
+// This defines CFA as ebp+8
+// The saved pc is at CFA-4 (i.e. ebp+4)
+// The saved ebp is at CFA-8 (i.e. ebp+0)
+// The saved esp is CFA+0
+
+bool ABISysV_i386::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t fp_reg_num = dwarf_ebp;
+ uint32_t sp_reg_num = dwarf_esp;
+ uint32_t pc_reg_num = dwarf_eip;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ const int32_t ptr_size = 4;
+
+ row->GetCFAValue().SetIsRegisterPlusOffset(fp_reg_num, 2 * ptr_size);
+ row->SetOffset(0);
+
+ row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true);
+ row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("i386 default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ return true;
+}
+
+// According to "Register Usage" in reference document (specified on top of
+// this source file) ebx, ebp, esi, edi and esp registers are preserved i.e.
+// non-volatile i.e. callee-saved on i386
+bool ABISysV_i386::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
+ if (!reg_info)
+ return false;
+
+ // Saved registers are ebx, ebp, esi, edi, esp, eip
+ const char *name = reg_info->name;
+ if (name[0] == 'e') {
+ switch (name[1]) {
+ case 'b':
+ if (name[2] == 'x' || name[2] == 'p')
+ return name[3] == '\0';
+ break;
+ case 'd':
+ if (name[2] == 'i')
+ return name[3] == '\0';
+ break;
+ case 'i':
+ if (name[2] == 'p')
+ return name[3] == '\0';
+ break;
+ case 's':
+ if (name[2] == 'i' || name[2] == 'p')
+ return name[3] == '\0';
+ break;
+ }
+ }
+
+ if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') // sp
+ return true;
+ if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0') // fp
+ return true;
+ if (name[0] == 'p' && name[1] == 'c' && name[2] == '\0') // pc
+ return true;
+
+ return false;
+}
+
+void ABISysV_i386::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "System V ABI for i386 targets", CreateInstance);
+}
+
+void ABISysV_i386::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString ABISysV_i386::GetPluginNameStatic() {
+ static ConstString g_name("sysv-i386");
+ return g_name;
+}
+
+lldb_private::ConstString ABISysV_i386::GetPluginName() {
+ return GetPluginNameStatic();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-i386/ABISysV_i386.h b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-i386/ABISysV_i386.h
new file mode 100644
index 000000000000..982bdd676b74
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-i386/ABISysV_i386.h
@@ -0,0 +1,108 @@
+//===------------------- ABISysV_i386.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 liblldb_ABISysV_i386_h_
+#define liblldb_ABISysV_i386_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABISysV_i386 : public lldb_private::ABI {
+public:
+ ~ABISysV_i386() override = default;
+
+ size_t GetRedZoneSize() const override {
+ return 0; // There is no red zone for i386 Architecture
+ }
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress,
+ lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &type) const override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override {
+ return !RegisterIsCalleeSaved(reg_info);
+ }
+
+ // The SysV i386 ABI requires that stack frames be 16 byte aligned.
+ // When there is a trap handler on the stack, e.g. _sigtramp in userland
+ // code, we've seen that the stack pointer is often not aligned properly
+ // before the handler is invoked. This means that lldb will stop the unwind
+ // early -- before the function which caused the trap.
+ //
+ // To work around this, we relax that alignment to be just word-size
+ // (4-bytes).
+ // Whitelisting the trap handlers for user space would be easy (_sigtramp) but
+ // in other environments there can be a large number of different functions
+ // involved in async traps.
+
+ // ToDo: When __m256 arguments are passed then stack frames should be
+ // 32 byte aligned. Decide what to do for 32 byte alignment checking
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are 4 byte aligned
+ if (cfa & (4ull - 1ull))
+ return false; // Not 4 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ // Check whether the address is a valid 32 bit address
+ return (pc <= UINT32_MAX);
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ // PluginInterface protocol
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+protected:
+ lldb::ValueObjectSP
+ GetReturnValueObjectSimple(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const;
+
+ bool RegisterIsCalleeSaved(const lldb_private::RegisterInfo *reg_info);
+
+private:
+ ABISysV_i386(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABISysV_i386_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips/ABISysV_mips.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips/ABISysV_mips.cpp
new file mode 100644
index 000000000000..121c7300b968
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips/ABISysV_mips.cpp
@@ -0,0 +1,1069 @@
+//===-- ABISysV_mips.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 "ABISysV_mips.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Core/ValueObjectRegister.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum dwarf_regnums {
+ 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_r13,
+ dwarf_r14,
+ dwarf_r15,
+ dwarf_r16,
+ dwarf_r17,
+ dwarf_r18,
+ dwarf_r19,
+ dwarf_r20,
+ dwarf_r21,
+ dwarf_r22,
+ dwarf_r23,
+ dwarf_r24,
+ dwarf_r25,
+ dwarf_r26,
+ dwarf_r27,
+ dwarf_r28,
+ dwarf_r29,
+ dwarf_r30,
+ dwarf_r31,
+ dwarf_sr,
+ dwarf_lo,
+ dwarf_hi,
+ dwarf_bad,
+ dwarf_cause,
+ dwarf_pc
+};
+
+static const RegisterInfo g_register_infos[] = {
+ // NAME ALT SZ OFF ENCODING FORMAT EH_FRAME
+ // DWARF GENERIC PROCESS PLUGINS
+ // LLDB NATIVE VALUE REGS INVALIDATE REGS
+ // ======== ====== == === ============= =========== ============
+ // ============== ============ =================
+ // =================== ========== =================
+ {"r0",
+ "zero",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r0, dwarf_r0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r1",
+ "AT",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r1, dwarf_r1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r2",
+ "v0",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r2, dwarf_r2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r3",
+ "v1",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r3, dwarf_r3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r4",
+ "arg1",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r4, dwarf_r4, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r5",
+ "arg2",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r5, dwarf_r5, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r6",
+ "arg3",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r6, dwarf_r6, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r7",
+ "arg4",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r7, dwarf_r7, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r8",
+ "arg5",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r8, dwarf_r8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r9",
+ "arg6",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r9, dwarf_r9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r10",
+ "arg7",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r10, dwarf_r10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r11",
+ "arg8",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r11, dwarf_r11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r12",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r12, dwarf_r12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r13, dwarf_r13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r14, dwarf_r14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r15",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r15, dwarf_r15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r16",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r16, dwarf_r16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r17",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r17, dwarf_r17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r18",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r18, dwarf_r18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r19",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r19, dwarf_r19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r20",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r20, dwarf_r20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r21",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r21, dwarf_r21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r22",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r22, dwarf_r22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r23",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r23, dwarf_r23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r24",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r24, dwarf_r24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r25",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r25, dwarf_r25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r26",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r26, dwarf_r26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r27",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r27, dwarf_r27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r28",
+ "gp",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r28, dwarf_r28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r29",
+ "sp",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r29, dwarf_r29, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r30",
+ "fp",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r30, dwarf_r30, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r31",
+ "ra",
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r31, dwarf_r31, LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"sr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_sr, dwarf_sr, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lo",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_lo, dwarf_lo, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"hi",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_hi, dwarf_hi, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"bad",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_bad, dwarf_bad, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cause",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_cause, dwarf_cause, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"pc",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+};
+
+static const uint32_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos);
+
+const lldb_private::RegisterInfo *
+ABISysV_mips::GetRegisterInfoArray(uint32_t &count) {
+ count = k_num_register_infos;
+ return g_register_infos;
+}
+
+size_t ABISysV_mips::GetRedZoneSize() const { return 0; }
+
+// Static Functions
+
+ABISP
+ABISysV_mips::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) {
+ const llvm::Triple::ArchType arch_type = arch.GetTriple().getArch();
+ if ((arch_type == llvm::Triple::mips) ||
+ (arch_type == llvm::Triple::mipsel)) {
+ return ABISP(new ABISysV_mips(process_sp));
+ }
+ return ABISP();
+}
+
+bool ABISysV_mips::PrepareTrivialCall(Thread &thread, addr_t sp,
+ addr_t func_addr, addr_t return_addr,
+ llvm::ArrayRef<addr_t> args) const {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ StreamString s;
+ s.Printf("ABISysV_mips::PrepareTrivialCall (tid = 0x%" PRIx64
+ ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64
+ ", return_addr = 0x%" PRIx64,
+ thread.GetID(), (uint64_t)sp, (uint64_t)func_addr,
+ (uint64_t)return_addr);
+
+ for (size_t i = 0; i < args.size(); ++i)
+ s.Printf(", arg%zd = 0x%" PRIx64, i + 1, args[i]);
+ s.PutCString(")");
+ log->PutString(s.GetString());
+ }
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+
+ const RegisterInfo *reg_info = nullptr;
+
+ RegisterValue reg_value;
+
+ // Argument registers
+ const char *reg_names[] = {"r4", "r5", "r6", "r7"};
+
+ llvm::ArrayRef<addr_t>::iterator ai = args.begin(), ae = args.end();
+
+ // Write arguments to registers
+ for (size_t i = 0; i < llvm::array_lengthof(reg_names); ++i) {
+ if (ai == ae)
+ break;
+
+ reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_ARG1 + i);
+ if (log)
+ log->Printf("About to write arg%zd (0x%" PRIx64 ") into %s", i + 1,
+ args[i], reg_info->name);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i]))
+ return false;
+
+ ++ai;
+ }
+
+ // If we have more than 4 arguments --Spill onto the stack
+ if (ai != ae) {
+ // No of arguments to go on stack
+ size_t num_stack_regs = args.size();
+
+ // Allocate needed space for args on the stack
+ sp -= (num_stack_regs * 4);
+
+ // Keep the stack 8 byte aligned
+ sp &= ~(8ull - 1ull);
+
+ // just using arg1 to get the right size
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1);
+
+ addr_t arg_pos = sp + 16;
+
+ size_t i = 4;
+ for (; ai != ae; ++ai) {
+ reg_value.SetUInt32(*ai);
+ if (log)
+ log->Printf("About to write arg%zd (0x%" PRIx64 ") at 0x%" PRIx64 "",
+ i + 1, args[i], arg_pos);
+
+ if (reg_ctx
+ ->WriteRegisterValueToMemory(reg_info, arg_pos,
+ reg_info->byte_size, reg_value)
+ .Fail())
+ return false;
+ arg_pos += reg_info->byte_size;
+ i++;
+ }
+ }
+
+ Status error;
+ const RegisterInfo *pc_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ const RegisterInfo *sp_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+ const RegisterInfo *ra_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA);
+ const RegisterInfo *r25_info = reg_ctx->GetRegisterInfoByName("r25", 0);
+ const RegisterInfo *r0_info = reg_ctx->GetRegisterInfoByName("zero", 0);
+
+ if (log)
+ log->Printf("Writing R0: 0x%" PRIx64, (uint64_t)0);
+
+ /* Write r0 with 0, in case we are stopped in syscall,
+ * such setting prevents automatic decrement of the PC.
+ * This clears the bug 23659 for MIPS.
+ */
+ if (!reg_ctx->WriteRegisterFromUnsigned(r0_info, (uint64_t)0))
+ return false;
+
+ if (log)
+ log->Printf("Writing SP: 0x%" PRIx64, (uint64_t)sp);
+
+ // Set "sp" to the requested value
+ if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_info, sp))
+ return false;
+
+ if (log)
+ log->Printf("Writing RA: 0x%" PRIx64, (uint64_t)return_addr);
+
+ // Set "ra" to the return address
+ if (!reg_ctx->WriteRegisterFromUnsigned(ra_reg_info, return_addr))
+ return false;
+
+ if (log)
+ log->Printf("Writing PC: 0x%" PRIx64, (uint64_t)func_addr);
+
+ // Set pc to the address of the called function.
+ if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_info, func_addr))
+ return false;
+
+ if (log)
+ log->Printf("Writing r25: 0x%" PRIx64, (uint64_t)func_addr);
+
+ // All callers of position independent functions must place the address of
+ // the called function in t9 (r25)
+ if (!reg_ctx->WriteRegisterFromUnsigned(r25_info, func_addr))
+ return false;
+
+ return true;
+}
+
+bool ABISysV_mips::GetArgumentValues(Thread &thread, ValueList &values) const {
+ return false;
+}
+
+Status ABISysV_mips::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType compiler_type = new_value_sp->GetCompilerType();
+ if (!compiler_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+
+ bool is_signed;
+ uint32_t count;
+ bool is_complex;
+
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+
+ bool set_it_simple = false;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ compiler_type.IsPointerType()) {
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+
+ lldb::offset_t offset = 0;
+ if (num_bytes <= 8) {
+ const RegisterInfo *r2_info = reg_ctx->GetRegisterInfoByName("r2", 0);
+ if (num_bytes <= 4) {
+ uint32_t raw_value = data.GetMaxU32(&offset, num_bytes);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(r2_info, raw_value))
+ set_it_simple = true;
+ } else {
+ uint32_t raw_value = data.GetMaxU32(&offset, 4);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(r2_info, raw_value)) {
+ const RegisterInfo *r3_info = reg_ctx->GetRegisterInfoByName("r3", 0);
+ uint32_t raw_value = data.GetMaxU32(&offset, num_bytes - offset);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(r3_info, raw_value))
+ set_it_simple = true;
+ }
+ }
+ } else {
+ error.SetErrorString("We don't support returning longer than 64 bit "
+ "integer values at present.");
+ }
+ } else if (compiler_type.IsFloatingPointType(count, is_complex)) {
+ if (is_complex)
+ error.SetErrorString(
+ "We don't support returning complex values at present");
+ else
+ error.SetErrorString(
+ "We don't support returning float values at present");
+ }
+
+ if (!set_it_simple)
+ error.SetErrorString(
+ "We only support setting simple integer return types at present.");
+
+ return error;
+}
+
+ValueObjectSP ABISysV_mips::GetReturnValueObjectSimple(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ return return_valobj_sp;
+}
+
+ValueObjectSP ABISysV_mips::GetReturnValueObjectImpl(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ Value value;
+
+ if (!return_compiler_type)
+ return return_valobj_sp;
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ if (exe_ctx.GetTargetPtr() == nullptr || exe_ctx.GetProcessPtr() == nullptr)
+ return return_valobj_sp;
+
+ Target *target = exe_ctx.GetTargetPtr();
+ const ArchSpec target_arch = target->GetArchitecture();
+ ByteOrder target_byte_order = target_arch.GetByteOrder();
+ value.SetCompilerType(return_compiler_type);
+ uint32_t fp_flag =
+ target_arch.GetFlags() & lldb_private::ArchSpec::eMIPS_ABI_FP_mask;
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return return_valobj_sp;
+
+ bool is_signed = false;
+ bool is_complex = false;
+ uint32_t count = 0;
+
+ // In MIPS register "r2" (v0) holds the integer function return values
+ const RegisterInfo *r2_reg_info = reg_ctx->GetRegisterInfoByName("r2", 0);
+ llvm::Optional<uint64_t> bit_width = return_compiler_type.GetBitSize(&thread);
+ if (!bit_width)
+ return return_valobj_sp;
+ if (return_compiler_type.IsIntegerOrEnumerationType(is_signed)) {
+ switch (*bit_width) {
+ default:
+ return return_valobj_sp;
+ case 64: {
+ const RegisterInfo *r3_reg_info = reg_ctx->GetRegisterInfoByName("r3", 0);
+ uint64_t raw_value;
+ raw_value = reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT32_MAX;
+ raw_value |= ((uint64_t)(reg_ctx->ReadRegisterAsUnsigned(r3_reg_info, 0) &
+ UINT32_MAX))
+ << 32;
+ if (is_signed)
+ value.GetScalar() = (int64_t)raw_value;
+ else
+ value.GetScalar() = (uint64_t)raw_value;
+ } break;
+ case 32:
+ if (is_signed)
+ value.GetScalar() = (int32_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT32_MAX);
+ else
+ value.GetScalar() = (uint32_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT32_MAX);
+ break;
+ case 16:
+ if (is_signed)
+ value.GetScalar() = (int16_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT16_MAX);
+ else
+ value.GetScalar() = (uint16_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT16_MAX);
+ break;
+ case 8:
+ if (is_signed)
+ value.GetScalar() = (int8_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT8_MAX);
+ else
+ value.GetScalar() = (uint8_t)(
+ reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT8_MAX);
+ break;
+ }
+ } else if (return_compiler_type.IsPointerType()) {
+ uint32_t ptr =
+ thread.GetRegisterContext()->ReadRegisterAsUnsigned(r2_reg_info, 0) &
+ UINT32_MAX;
+ value.GetScalar() = ptr;
+ } else if (return_compiler_type.IsAggregateType()) {
+ // Structure/Vector is always passed in memory and pointer to that memory
+ // is passed in r2.
+ uint64_t mem_address = reg_ctx->ReadRegisterAsUnsigned(
+ reg_ctx->GetRegisterInfoByName("r2", 0), 0);
+ // We have got the address. Create a memory object out of it
+ return_valobj_sp = ValueObjectMemory::Create(
+ &thread, "", Address(mem_address, nullptr), return_compiler_type);
+ return return_valobj_sp;
+ } else if (return_compiler_type.IsFloatingPointType(count, is_complex)) {
+ if (IsSoftFloat(fp_flag)) {
+ uint64_t raw_value = reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0);
+ if (count != 1 && is_complex)
+ return return_valobj_sp;
+ switch (*bit_width) {
+ default:
+ return return_valobj_sp;
+ case 32:
+ static_assert(sizeof(float) == sizeof(uint32_t), "");
+ value.GetScalar() = *((float *)(&raw_value));
+ break;
+ case 64:
+ static_assert(sizeof(double) == sizeof(uint64_t), "");
+ const RegisterInfo *r3_reg_info =
+ reg_ctx->GetRegisterInfoByName("r3", 0);
+ if (target_byte_order == eByteOrderLittle)
+ raw_value =
+ ((reg_ctx->ReadRegisterAsUnsigned(r3_reg_info, 0)) << 32) |
+ raw_value;
+ else
+ raw_value = (raw_value << 32) |
+ reg_ctx->ReadRegisterAsUnsigned(r3_reg_info, 0);
+ value.GetScalar() = *((double *)(&raw_value));
+ break;
+ }
+ }
+
+ else {
+ const RegisterInfo *f0_info = reg_ctx->GetRegisterInfoByName("f0", 0);
+ RegisterValue f0_value;
+ DataExtractor f0_data;
+ reg_ctx->ReadRegister(f0_info, f0_value);
+ f0_value.GetData(f0_data);
+ lldb::offset_t offset = 0;
+
+ if (count == 1 && !is_complex) {
+ switch (*bit_width) {
+ default:
+ return return_valobj_sp;
+ case 64: {
+ static_assert(sizeof(double) == sizeof(uint64_t), "");
+ const RegisterInfo *f1_info = reg_ctx->GetRegisterInfoByName("f1", 0);
+ RegisterValue f1_value;
+ DataExtractor f1_data;
+ reg_ctx->ReadRegister(f1_info, f1_value);
+ DataExtractor *copy_from_extractor = nullptr;
+ DataBufferSP data_sp(new DataBufferHeap(8, 0));
+ DataExtractor return_ext(
+ data_sp, target_byte_order,
+ target->GetArchitecture().GetAddressByteSize());
+
+ if (target_byte_order == eByteOrderLittle) {
+ copy_from_extractor = &f0_data;
+ copy_from_extractor->CopyByteOrderedData(
+ offset, 4, data_sp->GetBytes(), 4, target_byte_order);
+ f1_value.GetData(f1_data);
+ copy_from_extractor = &f1_data;
+ copy_from_extractor->CopyByteOrderedData(
+ offset, 4, data_sp->GetBytes() + 4, 4, target_byte_order);
+ } else {
+ copy_from_extractor = &f0_data;
+ copy_from_extractor->CopyByteOrderedData(
+ offset, 4, data_sp->GetBytes() + 4, 4, target_byte_order);
+ f1_value.GetData(f1_data);
+ copy_from_extractor = &f1_data;
+ copy_from_extractor->CopyByteOrderedData(
+ offset, 4, data_sp->GetBytes(), 4, target_byte_order);
+ }
+ value.GetScalar() = (double)return_ext.GetDouble(&offset);
+ break;
+ }
+ case 32: {
+ static_assert(sizeof(float) == sizeof(uint32_t), "");
+ value.GetScalar() = (float)f0_data.GetFloat(&offset);
+ break;
+ }
+ }
+ } else {
+ // not handled yet
+ return return_valobj_sp;
+ }
+ }
+ } else {
+ // not handled yet
+ return return_valobj_sp;
+ }
+
+ // If we get here, we have a valid Value, so make our ValueObject out of it:
+
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ return return_valobj_sp;
+}
+
+bool ABISysV_mips::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our Call Frame Address is the stack pointer value
+ row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_r29, 0);
+
+ // The previous PC is in the RA
+ row->SetRegisterLocationToRegister(dwarf_pc, dwarf_r31, true);
+ unwind_plan.AppendRow(row);
+
+ // All other registers are the same.
+
+ unwind_plan.SetSourceName("mips at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetReturnAddressRegister(dwarf_r31);
+ return true;
+}
+
+bool ABISysV_mips::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_r29, 0);
+
+ row->SetRegisterLocationToRegister(dwarf_pc, dwarf_r31, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("mips default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ return true;
+}
+
+bool ABISysV_mips::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ return !RegisterIsCalleeSaved(reg_info);
+}
+
+bool ABISysV_mips::IsSoftFloat(uint32_t fp_flags) const {
+ return (fp_flags == lldb_private::ArchSpec::eMIPS_ABI_FP_SOFT);
+}
+
+bool ABISysV_mips::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
+ if (reg_info) {
+ // Preserved registers are :
+ // r16-r23, r28, r29, r30, r31
+ const char *name = reg_info->name;
+
+ if (name[0] == 'r') {
+ switch (name[1]) {
+ case '1':
+ if (name[2] == '6' || name[2] == '7' || name[2] == '8' ||
+ name[2] == '9') // r16-r19
+ return name[3] == '\0';
+ break;
+ case '2':
+ if (name[2] == '0' || name[2] == '1' || name[2] == '2' ||
+ name[2] == '3' // r20-r23
+ || name[2] == '8' || name[2] == '9') // r28 and r29
+ return name[3] == '\0';
+ break;
+ case '3':
+ if (name[2] == '0' || name[2] == '1') // r30 and r31
+ return name[3] == '\0';
+ break;
+ }
+
+ if (name[0] == 'g' && name[1] == 'p' && name[2] == '\0') // gp (r28)
+ return true;
+ if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') // sp (r29)
+ return true;
+ if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0') // fp (r30)
+ return true;
+ if (name[0] == 'r' && name[1] == 'a' && name[2] == '\0') // ra (r31)
+ return true;
+ }
+ }
+ return false;
+}
+
+void ABISysV_mips::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "System V ABI for mips targets", CreateInstance);
+}
+
+void ABISysV_mips::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ABISysV_mips::GetPluginNameStatic() {
+ static ConstString g_name("sysv-mips");
+ return g_name;
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString ABISysV_mips::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ABISysV_mips::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips/ABISysV_mips.h b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips/ABISysV_mips.h
new file mode 100644
index 000000000000..6cd9c19c22ac
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips/ABISysV_mips.h
@@ -0,0 +1,95 @@
+//===-- ABISysV_mips.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 liblldb_ABISysV_mips_h_
+#define liblldb_ABISysV_mips_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABISysV_mips : public lldb_private::ABI {
+public:
+ ~ABISysV_mips() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress,
+ lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &type) const override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ bool IsSoftFloat(uint32_t fp_flag) const;
+
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are 8 byte aligned
+ if (cfa & (8ull - 1ull))
+ return false; // Not 8 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ // Just make sure the address is a valid 32 bit address. Bit zero
+ // might be set due to MicroMIPS function calls, so don't enforce alignment.
+ return (pc <= UINT32_MAX);
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ // PluginInterface protocol
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ void CreateRegisterMapIfNeeded();
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectSimple(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const;
+
+ bool RegisterIsCalleeSaved(const lldb_private::RegisterInfo *reg_info);
+
+private:
+ ABISysV_mips(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABISysV_mips_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips64/ABISysV_mips64.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips64/ABISysV_mips64.cpp
new file mode 100644
index 000000000000..18011cfb6b9e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips64/ABISysV_mips64.cpp
@@ -0,0 +1,1217 @@
+//===-- ABISysV_mips64.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 "ABISysV_mips64.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Core/ValueObjectRegister.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum dwarf_regnums {
+ 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_r13,
+ dwarf_r14,
+ dwarf_r15,
+ dwarf_r16,
+ dwarf_r17,
+ dwarf_r18,
+ dwarf_r19,
+ dwarf_r20,
+ dwarf_r21,
+ dwarf_r22,
+ dwarf_r23,
+ dwarf_r24,
+ dwarf_r25,
+ dwarf_r26,
+ dwarf_r27,
+ dwarf_r28,
+ dwarf_r29,
+ dwarf_r30,
+ dwarf_r31,
+ dwarf_sr,
+ dwarf_lo,
+ dwarf_hi,
+ dwarf_bad,
+ dwarf_cause,
+ dwarf_pc
+};
+
+static const RegisterInfo g_register_infos_mips64[] = {
+ // NAME ALT SZ OFF ENCODING FORMAT EH_FRAME
+ // DWARF GENERIC PROCESS PLUGIN
+ // LLDB NATIVE
+ // ======== ====== == === ============= ========== =============
+ // ================= ==================== =================
+ // ====================
+ {"r0",
+ "zero",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r0, dwarf_r0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r1",
+ "AT",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r1, dwarf_r1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r2",
+ "v0",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r2, dwarf_r2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r3",
+ "v1",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r3, dwarf_r3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r4",
+ "arg1",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r4, dwarf_r4, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r5",
+ "arg2",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r5, dwarf_r5, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r6",
+ "arg3",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r6, dwarf_r6, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r7",
+ "arg4",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r7, dwarf_r7, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r8",
+ "arg5",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r8, dwarf_r8, LLDB_REGNUM_GENERIC_ARG5, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r9",
+ "arg6",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r9, dwarf_r9, LLDB_REGNUM_GENERIC_ARG6, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r10",
+ "arg7",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r10, dwarf_r10, LLDB_REGNUM_GENERIC_ARG7, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r11",
+ "arg8",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r11, dwarf_r11, LLDB_REGNUM_GENERIC_ARG8, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r12",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r12, dwarf_r12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r13, dwarf_r13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r14, dwarf_r14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r15",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r15, dwarf_r15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r16",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r16, dwarf_r16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r17",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r17, dwarf_r17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r18",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r18, dwarf_r18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r19",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r19, dwarf_r19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r20",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r20, dwarf_r20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r21",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r21, dwarf_r21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r22",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r22, dwarf_r22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r23",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r23, dwarf_r23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r24",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r24, dwarf_r24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r25",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r25, dwarf_r25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r26",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r26, dwarf_r26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r27",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r27, dwarf_r27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r28",
+ "gp",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r28, dwarf_r28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r29",
+ "sp",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r29, dwarf_r29, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r30",
+ "fp",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r30, dwarf_r30, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r31",
+ "ra",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r31, dwarf_r31, LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"sr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_sr, dwarf_sr, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lo",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_lo, dwarf_lo, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"hi",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_hi, dwarf_hi, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"bad",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_bad, dwarf_bad, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cause",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_cause, dwarf_cause, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"pc",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+};
+
+static const uint32_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos_mips64);
+
+const lldb_private::RegisterInfo *
+ABISysV_mips64::GetRegisterInfoArray(uint32_t &count) {
+ count = k_num_register_infos;
+ return g_register_infos_mips64;
+}
+
+size_t ABISysV_mips64::GetRedZoneSize() const { return 0; }
+
+// Static Functions
+
+ABISP
+ABISysV_mips64::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) {
+ if (arch.GetTriple().isMIPS64())
+ return ABISP(new ABISysV_mips64(process_sp));
+ return ABISP();
+}
+
+bool ABISysV_mips64::PrepareTrivialCall(Thread &thread, addr_t sp,
+ addr_t func_addr, addr_t return_addr,
+ llvm::ArrayRef<addr_t> args) const {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ StreamString s;
+ s.Printf("ABISysV_mips64::PrepareTrivialCall (tid = 0x%" PRIx64
+ ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64
+ ", return_addr = 0x%" PRIx64,
+ thread.GetID(), (uint64_t)sp, (uint64_t)func_addr,
+ (uint64_t)return_addr);
+
+ for (size_t i = 0; i < args.size(); ++i)
+ s.Printf(", arg%zd = 0x%" PRIx64, i + 1, args[i]);
+ s.PutCString(")");
+ log->PutString(s.GetString());
+ }
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+
+ const RegisterInfo *reg_info = nullptr;
+
+ if (args.size() > 8) // TODO handle more than 8 arguments
+ return false;
+
+ for (size_t i = 0; i < args.size(); ++i) {
+ reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_ARG1 + i);
+ if (log)
+ log->Printf("About to write arg%zd (0x%" PRIx64 ") into %s", i + 1,
+ args[i], reg_info->name);
+ if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i]))
+ return false;
+ }
+
+ // First, align the SP
+
+ if (log)
+ log->Printf("16-byte aligning SP: 0x%" PRIx64 " to 0x%" PRIx64,
+ (uint64_t)sp, (uint64_t)(sp & ~0xfull));
+
+ sp &= ~(0xfull); // 16-byte alignment
+
+ Status error;
+ const RegisterInfo *pc_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ const RegisterInfo *sp_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+ const RegisterInfo *ra_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA);
+ const RegisterInfo *r25_info = reg_ctx->GetRegisterInfoByName("r25", 0);
+ const RegisterInfo *r0_info = reg_ctx->GetRegisterInfoByName("zero", 0);
+
+ if (log)
+ log->Printf("Writing R0: 0x%" PRIx64, (uint64_t)0);
+
+ /* Write r0 with 0, in case we are stopped in syscall,
+ * such setting prevents automatic decrement of the PC.
+ * This clears the bug 23659 for MIPS.
+ */
+ if (!reg_ctx->WriteRegisterFromUnsigned(r0_info, (uint64_t)0))
+ return false;
+
+ if (log)
+ log->Printf("Writing SP: 0x%" PRIx64, (uint64_t)sp);
+
+ // Set "sp" to the requested value
+ if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_info, sp))
+ return false;
+
+ if (log)
+ log->Printf("Writing RA: 0x%" PRIx64, (uint64_t)return_addr);
+
+ // Set "ra" to the return address
+ if (!reg_ctx->WriteRegisterFromUnsigned(ra_reg_info, return_addr))
+ return false;
+
+ if (log)
+ log->Printf("Writing PC: 0x%" PRIx64, (uint64_t)func_addr);
+
+ // Set pc to the address of the called function.
+ if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_info, func_addr))
+ return false;
+
+ if (log)
+ log->Printf("Writing r25: 0x%" PRIx64, (uint64_t)func_addr);
+
+ // All callers of position independent functions must place the address of
+ // the called function in t9 (r25)
+ if (!reg_ctx->WriteRegisterFromUnsigned(r25_info, func_addr))
+ return false;
+
+ return true;
+}
+
+bool ABISysV_mips64::GetArgumentValues(Thread &thread,
+ ValueList &values) const {
+ return false;
+}
+
+Status ABISysV_mips64::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType compiler_type = new_value_sp->GetCompilerType();
+ if (!compiler_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+
+ if (!reg_ctx)
+ error.SetErrorString("no registers are available");
+
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+
+ const uint32_t type_flags = compiler_type.GetTypeInfo(nullptr);
+
+ if (type_flags & eTypeIsScalar || type_flags & eTypeIsPointer) {
+ if (type_flags & eTypeIsInteger || type_flags & eTypeIsPointer) {
+ lldb::offset_t offset = 0;
+
+ if (num_bytes <= 16) {
+ const RegisterInfo *r2_info = reg_ctx->GetRegisterInfoByName("r2", 0);
+ if (num_bytes <= 8) {
+ uint64_t raw_value = data.GetMaxU64(&offset, num_bytes);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(r2_info, raw_value))
+ error.SetErrorString("failed to write register r2");
+ } else {
+ uint64_t raw_value = data.GetMaxU64(&offset, 8);
+ if (reg_ctx->WriteRegisterFromUnsigned(r2_info, raw_value)) {
+ const RegisterInfo *r3_info =
+ reg_ctx->GetRegisterInfoByName("r3", 0);
+ raw_value = data.GetMaxU64(&offset, num_bytes - offset);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(r3_info, raw_value))
+ error.SetErrorString("failed to write register r3");
+ } else
+ error.SetErrorString("failed to write register r2");
+ }
+ } else {
+ error.SetErrorString("We don't support returning longer than 128 bit "
+ "integer values at present.");
+ }
+ } else if (type_flags & eTypeIsFloat) {
+ error.SetErrorString("TODO: Handle Float Types.");
+ }
+ } else if (type_flags & eTypeIsVector) {
+ error.SetErrorString("returning vector values are not supported");
+ }
+
+ return error;
+}
+
+ValueObjectSP ABISysV_mips64::GetReturnValueObjectSimple(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ return return_valobj_sp;
+}
+
+ValueObjectSP ABISysV_mips64::GetReturnValueObjectImpl(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ Value value;
+ Status error;
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ if (exe_ctx.GetTargetPtr() == nullptr || exe_ctx.GetProcessPtr() == nullptr)
+ return return_valobj_sp;
+
+ value.SetCompilerType(return_compiler_type);
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return return_valobj_sp;
+
+ Target *target = exe_ctx.GetTargetPtr();
+ const ArchSpec target_arch = target->GetArchitecture();
+ ByteOrder target_byte_order = target_arch.GetByteOrder();
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (!byte_size)
+ return return_valobj_sp;
+ const uint32_t type_flags = return_compiler_type.GetTypeInfo(nullptr);
+ uint32_t fp_flag =
+ target_arch.GetFlags() & lldb_private::ArchSpec::eMIPS_ABI_FP_mask;
+
+ const RegisterInfo *r2_info = reg_ctx->GetRegisterInfoByName("r2", 0);
+ const RegisterInfo *r3_info = reg_ctx->GetRegisterInfoByName("r3", 0);
+
+ if (type_flags & eTypeIsScalar || type_flags & eTypeIsPointer) {
+ value.SetValueType(Value::eValueTypeScalar);
+
+ bool success = false;
+ if (type_flags & eTypeIsInteger || type_flags & eTypeIsPointer) {
+ // Extract the register context so we can read arguments from registers
+ // In MIPS register "r2" (v0) holds the integer function return values
+
+ uint64_t raw_value = reg_ctx->ReadRegisterAsUnsigned(r2_info, 0);
+
+ const bool is_signed = (type_flags & eTypeIsSigned) != 0;
+ switch (*byte_size) {
+ default:
+ break;
+
+ case sizeof(uint64_t):
+ if (is_signed)
+ value.GetScalar() = (int64_t)(raw_value);
+ else
+ value.GetScalar() = (uint64_t)(raw_value);
+ success = true;
+ break;
+
+ case sizeof(uint32_t):
+ if (is_signed)
+ value.GetScalar() = (int32_t)(raw_value & UINT32_MAX);
+ else
+ value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint16_t):
+ if (is_signed)
+ value.GetScalar() = (int16_t)(raw_value & UINT16_MAX);
+ else
+ value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint8_t):
+ if (is_signed)
+ value.GetScalar() = (int8_t)(raw_value & UINT8_MAX);
+ else
+ value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX);
+ success = true;
+ break;
+ }
+ } else if (type_flags & eTypeIsFloat) {
+ if (type_flags & eTypeIsComplex) {
+ // Don't handle complex yet.
+ } else if (IsSoftFloat(fp_flag)) {
+ uint64_t raw_value = reg_ctx->ReadRegisterAsUnsigned(r2_info, 0);
+ switch (*byte_size) {
+ case 4:
+ value.GetScalar() = *((float *)(&raw_value));
+ success = true;
+ break;
+ case 8:
+ value.GetScalar() = *((double *)(&raw_value));
+ success = true;
+ break;
+ case 16:
+ uint64_t result[2];
+ if (target_byte_order == eByteOrderLittle) {
+ result[0] = raw_value;
+ result[1] = reg_ctx->ReadRegisterAsUnsigned(r3_info, 0);
+ value.GetScalar() = *((long double *)(result));
+ } else {
+ result[0] = reg_ctx->ReadRegisterAsUnsigned(r3_info, 0);
+ result[1] = raw_value;
+ value.GetScalar() = *((long double *)(result));
+ }
+ success = true;
+ break;
+ }
+
+ } else {
+ if (*byte_size <= sizeof(long double)) {
+ const RegisterInfo *f0_info = reg_ctx->GetRegisterInfoByName("f0", 0);
+
+ RegisterValue f0_value;
+ DataExtractor f0_data;
+
+ reg_ctx->ReadRegister(f0_info, f0_value);
+
+ f0_value.GetData(f0_data);
+
+ lldb::offset_t offset = 0;
+ if (*byte_size == sizeof(float)) {
+ value.GetScalar() = (float)f0_data.GetFloat(&offset);
+ success = true;
+ } else if (*byte_size == sizeof(double)) {
+ value.GetScalar() = (double)f0_data.GetDouble(&offset);
+ success = true;
+ } else if (*byte_size == sizeof(long double)) {
+ const RegisterInfo *f2_info =
+ reg_ctx->GetRegisterInfoByName("f2", 0);
+ RegisterValue f2_value;
+ DataExtractor f2_data;
+ reg_ctx->ReadRegister(f2_info, f2_value);
+ DataExtractor *copy_from_extractor = nullptr;
+ DataBufferSP data_sp(new DataBufferHeap(16, 0));
+ DataExtractor return_ext(
+ data_sp, target_byte_order,
+ target->GetArchitecture().GetAddressByteSize());
+
+ if (target_byte_order == eByteOrderLittle) {
+ copy_from_extractor = &f0_data;
+ copy_from_extractor->CopyByteOrderedData(
+ 0, 8, data_sp->GetBytes(), *byte_size - 8, target_byte_order);
+ f2_value.GetData(f2_data);
+ copy_from_extractor = &f2_data;
+ copy_from_extractor->CopyByteOrderedData(
+ 0, 8, data_sp->GetBytes() + 8, *byte_size - 8,
+ target_byte_order);
+ } else {
+ copy_from_extractor = &f0_data;
+ copy_from_extractor->CopyByteOrderedData(
+ 0, 8, data_sp->GetBytes() + 8, *byte_size - 8,
+ target_byte_order);
+ f2_value.GetData(f2_data);
+ copy_from_extractor = &f2_data;
+ copy_from_extractor->CopyByteOrderedData(
+ 0, 8, data_sp->GetBytes(), *byte_size - 8, target_byte_order);
+ }
+
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), return_ext);
+ return return_valobj_sp;
+ }
+ }
+ }
+ }
+
+ if (success)
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if (type_flags & eTypeIsStructUnion || type_flags & eTypeIsClass ||
+ type_flags & eTypeIsVector) {
+ // Any structure of up to 16 bytes in size is returned in the registers.
+ if (*byte_size <= 16) {
+ DataBufferSP data_sp(new DataBufferHeap(16, 0));
+ DataExtractor return_ext(data_sp, target_byte_order,
+ target->GetArchitecture().GetAddressByteSize());
+
+ RegisterValue r2_value, r3_value, f0_value, f1_value, f2_value;
+ // Tracks how much bytes of r2 and r3 registers we've consumed so far
+ uint32_t integer_bytes = 0;
+
+ // True if return values are in FP return registers.
+ bool use_fp_regs = false;
+ // True if we found any non floating point field in structure.
+ bool found_non_fp_field = false;
+ // True if return values are in r2 register.
+ bool use_r2 = false;
+ // True if return values are in r3 register.
+ bool use_r3 = false;
+ // True if the result is copied into our data buffer
+ bool sucess = false;
+ std::string name;
+ bool is_complex;
+ uint32_t count;
+ const uint32_t num_children = return_compiler_type.GetNumFields();
+
+ // A structure consisting of one or two FP values (and nothing else) will
+ // be returned in the two FP return-value registers i.e fp0 and fp2.
+ if (num_children <= 2) {
+ uint64_t field_bit_offset = 0;
+
+ // Check if this structure contains only floating point fields
+ for (uint32_t idx = 0; idx < num_children; idx++) {
+ CompilerType field_compiler_type =
+ return_compiler_type.GetFieldAtIndex(idx, name, &field_bit_offset,
+ nullptr, nullptr);
+
+ if (field_compiler_type.IsFloatingPointType(count, is_complex))
+ use_fp_regs = true;
+ else
+ found_non_fp_field = true;
+ }
+
+ if (use_fp_regs && !found_non_fp_field) {
+ // We have one or two FP-only values in this structure. Get it from
+ // f0/f2 registers.
+ DataExtractor f0_data, f1_data, f2_data;
+ const RegisterInfo *f0_info = reg_ctx->GetRegisterInfoByName("f0", 0);
+ const RegisterInfo *f1_info = reg_ctx->GetRegisterInfoByName("f1", 0);
+ const RegisterInfo *f2_info = reg_ctx->GetRegisterInfoByName("f2", 0);
+
+ reg_ctx->ReadRegister(f0_info, f0_value);
+ reg_ctx->ReadRegister(f2_info, f2_value);
+
+ f0_value.GetData(f0_data);
+
+ for (uint32_t idx = 0; idx < num_children; idx++) {
+ CompilerType field_compiler_type =
+ return_compiler_type.GetFieldAtIndex(
+ idx, name, &field_bit_offset, nullptr, nullptr);
+ llvm::Optional<uint64_t> field_byte_width =
+ field_compiler_type.GetByteSize(nullptr);
+ if (!field_byte_width)
+ return return_valobj_sp;
+
+ DataExtractor *copy_from_extractor = nullptr;
+ uint64_t return_value[2];
+ offset_t offset = 0;
+
+ if (idx == 0) {
+ // This case is for long double type.
+ if (*field_byte_width == 16) {
+
+ // If structure contains long double type, then it is returned
+ // in fp0/fp1 registers.
+ if (target_byte_order == eByteOrderLittle) {
+ return_value[0] = f0_data.GetU64(&offset);
+ reg_ctx->ReadRegister(f1_info, f1_value);
+ f1_value.GetData(f1_data);
+ offset = 0;
+ return_value[1] = f1_data.GetU64(&offset);
+ } else {
+ return_value[1] = f0_data.GetU64(&offset);
+ reg_ctx->ReadRegister(f1_info, f1_value);
+ f1_value.GetData(f1_data);
+ offset = 0;
+ return_value[0] = f1_data.GetU64(&offset);
+ }
+
+ f0_data.SetData(return_value, *field_byte_width,
+ target_byte_order);
+ }
+ copy_from_extractor = &f0_data; // This is in f0, copy from
+ // register to our result
+ // structure
+ } else {
+ f2_value.GetData(f2_data);
+ // This is in f2, copy from register to our result structure
+ copy_from_extractor = &f2_data;
+ }
+
+ // Sanity check to avoid crash
+ if (!copy_from_extractor ||
+ *field_byte_width > copy_from_extractor->GetByteSize())
+ return return_valobj_sp;
+
+ // copy the register contents into our data buffer
+ copy_from_extractor->CopyByteOrderedData(
+ 0, *field_byte_width,
+ data_sp->GetBytes() + (field_bit_offset / 8), *field_byte_width,
+ target_byte_order);
+ }
+
+ // The result is in our data buffer. Create a variable object out of
+ // it
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), return_ext);
+
+ return return_valobj_sp;
+ }
+ }
+
+ // If we reach here, it means this structure either contains more than
+ // two fields or it contains at least one non floating point type. In
+ // that case, all fields are returned in GP return registers.
+ for (uint32_t idx = 0; idx < num_children; idx++) {
+ uint64_t field_bit_offset = 0;
+ bool is_signed;
+ uint32_t padding;
+
+ CompilerType field_compiler_type = return_compiler_type.GetFieldAtIndex(
+ idx, name, &field_bit_offset, nullptr, nullptr);
+ llvm::Optional<uint64_t> field_byte_width =
+ field_compiler_type.GetByteSize(nullptr);
+
+ // if we don't know the size of the field (e.g. invalid type), just
+ // bail out
+ if (!field_byte_width || *field_byte_width == 0)
+ break;
+
+ uint32_t field_byte_offset = field_bit_offset / 8;
+
+ if (field_compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ field_compiler_type.IsPointerType() ||
+ field_compiler_type.IsFloatingPointType(count, is_complex)) {
+ padding = field_byte_offset - integer_bytes;
+
+ if (integer_bytes < 8) {
+ // We have not yet consumed r2 completely.
+ if (integer_bytes + *field_byte_width + padding <= 8) {
+ // This field fits in r2, copy its value from r2 to our result
+ // structure
+ integer_bytes = integer_bytes + *field_byte_width +
+ padding; // Increase the consumed bytes.
+ use_r2 = true;
+ } else {
+ // There isn't enough space left in r2 for this field, so this
+ // will be in r3.
+ integer_bytes = integer_bytes + *field_byte_width +
+ padding; // Increase the consumed bytes.
+ use_r3 = true;
+ }
+ }
+ // We already have consumed at-least 8 bytes that means r2 is done,
+ // and this field will be in r3. Check if this field can fit in r3.
+ else if (integer_bytes + *field_byte_width + padding <= 16) {
+ integer_bytes = integer_bytes + *field_byte_width + padding;
+ use_r3 = true;
+ } else {
+ // There isn't any space left for this field, this should not
+ // happen as we have already checked the overall size is not
+ // greater than 16 bytes. For now, return a nullptr return value
+ // object.
+ return return_valobj_sp;
+ }
+ }
+ }
+ // Vector types up to 16 bytes are returned in GP return registers
+ if (type_flags & eTypeIsVector) {
+ if (*byte_size <= 8)
+ use_r2 = true;
+ else {
+ use_r2 = true;
+ use_r3 = true;
+ }
+ }
+
+ if (use_r2) {
+ reg_ctx->ReadRegister(r2_info, r2_value);
+
+ const size_t bytes_copied = r2_value.GetAsMemoryData(
+ r2_info, data_sp->GetBytes(), r2_info->byte_size, target_byte_order,
+ error);
+ if (bytes_copied != r2_info->byte_size)
+ return return_valobj_sp;
+ sucess = true;
+ }
+ if (use_r3) {
+ reg_ctx->ReadRegister(r3_info, r3_value);
+ const size_t bytes_copied = r3_value.GetAsMemoryData(
+ r3_info, data_sp->GetBytes() + r2_info->byte_size,
+ r3_info->byte_size, target_byte_order, error);
+
+ if (bytes_copied != r3_info->byte_size)
+ return return_valobj_sp;
+ sucess = true;
+ }
+ if (sucess) {
+ // The result is in our data buffer. Create a variable object out of
+ // it
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), return_ext);
+ }
+ return return_valobj_sp;
+ }
+
+ // Any structure/vector greater than 16 bytes in size is returned in
+ // memory. The pointer to that memory is returned in r2.
+ uint64_t mem_address = reg_ctx->ReadRegisterAsUnsigned(
+ reg_ctx->GetRegisterInfoByName("r2", 0), 0);
+
+ // We have got the address. Create a memory object out of it
+ return_valobj_sp = ValueObjectMemory::Create(
+ &thread, "", Address(mem_address, nullptr), return_compiler_type);
+ }
+ return return_valobj_sp;
+}
+
+bool ABISysV_mips64::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our Call Frame Address is the stack pointer value
+ row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_r29, 0);
+
+ // The previous PC is in the RA
+ row->SetRegisterLocationToRegister(dwarf_pc, dwarf_r31, true);
+ unwind_plan.AppendRow(row);
+
+ // All other registers are the same.
+
+ unwind_plan.SetSourceName("mips64 at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetReturnAddressRegister(dwarf_r31);
+ return true;
+}
+
+bool ABISysV_mips64::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_r29, 0);
+
+ row->SetRegisterLocationToRegister(dwarf_pc, dwarf_r31, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("mips64 default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ return true;
+}
+
+bool ABISysV_mips64::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ return !RegisterIsCalleeSaved(reg_info);
+}
+
+bool ABISysV_mips64::IsSoftFloat(uint32_t fp_flag) const {
+ return (fp_flag == lldb_private::ArchSpec::eMIPS_ABI_FP_SOFT);
+}
+
+bool ABISysV_mips64::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
+ if (reg_info) {
+ // Preserved registers are :
+ // r16-r23, r28, r29, r30, r31
+
+ int reg = ((reg_info->byte_offset) / 8);
+
+ bool save = (reg >= 16) && (reg <= 23);
+ save |= (reg >= 28) && (reg <= 31);
+
+ return save;
+ }
+ return false;
+}
+
+void ABISysV_mips64::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "System V ABI for mips64 targets", CreateInstance);
+}
+
+void ABISysV_mips64::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ABISysV_mips64::GetPluginNameStatic() {
+ static ConstString g_name("sysv-mips64");
+ return g_name;
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString ABISysV_mips64::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ABISysV_mips64::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips64/ABISysV_mips64.h b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips64/ABISysV_mips64.h
new file mode 100644
index 000000000000..7da71b36b4b7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-mips64/ABISysV_mips64.h
@@ -0,0 +1,108 @@
+//===-- ABISysV_mips64.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 liblldb_ABISysV_mips64_h_
+#define liblldb_ABISysV_mips64_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABISysV_mips64 : public lldb_private::ABI {
+public:
+ ~ABISysV_mips64() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress,
+ lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &type) const override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ bool IsSoftFloat(uint32_t fp_flag) const;
+
+ // The SysV mips ABI requires that stack frames be 16 byte aligned.
+ // When there is a trap handler on the stack, e.g. _sigtramp in userland
+ // code, we've seen that the stack pointer is often not aligned properly
+ // before the handler is invoked. This means that lldb will stop the unwind
+ // early -- before the function which caused the trap.
+ //
+ // To work around this, we relax that alignment to be just word-size
+ // (8-bytes).
+ // Whitelisting the trap handlers for user space would be easy (_sigtramp) but
+ // in other environments there can be a large number of different functions
+ // involved in async traps.
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are 8 byte aligned
+ if (cfa & (8ull - 1ull))
+ return false; // Not 8 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ if (pc & (4ull - 1ull))
+ return false; // Not 4 byte aligned
+
+ // Anything else if fair game..
+ return true;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ // PluginInterface protocol
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ void CreateRegisterMapIfNeeded();
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectSimple(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const;
+
+ bool RegisterIsCalleeSaved(const lldb_private::RegisterInfo *reg_info);
+
+private:
+ ABISysV_mips64(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABISysV_mips64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc/ABISysV_ppc.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc/ABISysV_ppc.cpp
new file mode 100644
index 000000000000..faa995033ac2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc/ABISysV_ppc.cpp
@@ -0,0 +1,982 @@
+//===-- ABISysV_ppc.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 "ABISysV_ppc.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Core/ValueObjectRegister.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum dwarf_regnums {
+ 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_r13,
+ dwarf_r14,
+ dwarf_r15,
+ dwarf_r16,
+ dwarf_r17,
+ dwarf_r18,
+ dwarf_r19,
+ dwarf_r20,
+ dwarf_r21,
+ dwarf_r22,
+ dwarf_r23,
+ dwarf_r24,
+ dwarf_r25,
+ dwarf_r26,
+ dwarf_r27,
+ dwarf_r28,
+ dwarf_r29,
+ dwarf_r30,
+ dwarf_r31,
+ dwarf_f0,
+ dwarf_f1,
+ dwarf_f2,
+ dwarf_f3,
+ dwarf_f4,
+ dwarf_f5,
+ dwarf_f6,
+ dwarf_f7,
+ dwarf_f8,
+ dwarf_f9,
+ dwarf_f10,
+ dwarf_f11,
+ dwarf_f12,
+ dwarf_f13,
+ dwarf_f14,
+ dwarf_f15,
+ dwarf_f16,
+ dwarf_f17,
+ dwarf_f18,
+ dwarf_f19,
+ dwarf_f20,
+ dwarf_f21,
+ dwarf_f22,
+ dwarf_f23,
+ dwarf_f24,
+ dwarf_f25,
+ dwarf_f26,
+ dwarf_f27,
+ dwarf_f28,
+ dwarf_f29,
+ dwarf_f30,
+ dwarf_f31,
+ dwarf_cr,
+ dwarf_fpscr,
+ dwarf_xer = 101,
+ dwarf_lr = 108,
+ dwarf_ctr,
+ dwarf_pc,
+ dwarf_cfa,
+};
+
+// Note that the size and offset will be updated by platform-specific classes.
+#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \
+ { \
+ #reg, alt, 8, 0, eEncodingUint, eFormatHex, {kind1, kind2, kind3, kind4 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+static const RegisterInfo g_register_infos[] = {
+ // General purpose registers. eh_frame, DWARF,
+ // Generic, Process Plugin
+ DEFINE_GPR(r0, nullptr, dwarf_r0, dwarf_r0, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r1, "sp", dwarf_r1, dwarf_r1, LLDB_REGNUM_GENERIC_SP,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r2, nullptr, dwarf_r2, dwarf_r2, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r3, "arg1", dwarf_r3, dwarf_r3, LLDB_REGNUM_GENERIC_ARG1,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r4, "arg2", dwarf_r4, dwarf_r4, LLDB_REGNUM_GENERIC_ARG2,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r5, "arg3", dwarf_r5, dwarf_r5, LLDB_REGNUM_GENERIC_ARG3,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r6, "arg4", dwarf_r6, dwarf_r6, LLDB_REGNUM_GENERIC_ARG4,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r7, "arg5", dwarf_r7, dwarf_r7, LLDB_REGNUM_GENERIC_ARG5,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r8, "arg6", dwarf_r8, dwarf_r8, LLDB_REGNUM_GENERIC_ARG6,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r9, "arg7", dwarf_r9, dwarf_r9, LLDB_REGNUM_GENERIC_ARG7,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r10, "arg8", dwarf_r10, dwarf_r10, LLDB_REGNUM_GENERIC_ARG8,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r11, nullptr, dwarf_r11, dwarf_r11, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r12, nullptr, dwarf_r12, dwarf_r12, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r13, nullptr, dwarf_r13, dwarf_r13, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r14, nullptr, dwarf_r14, dwarf_r14, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r15, nullptr, dwarf_r15, dwarf_r15, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r16, nullptr, dwarf_r16, dwarf_r16, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r17, nullptr, dwarf_r17, dwarf_r17, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r18, nullptr, dwarf_r18, dwarf_r18, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r19, nullptr, dwarf_r19, dwarf_r19, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r20, nullptr, dwarf_r20, dwarf_r20, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r21, nullptr, dwarf_r21, dwarf_r21, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r22, nullptr, dwarf_r22, dwarf_r22, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r23, nullptr, dwarf_r23, dwarf_r23, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r24, nullptr, dwarf_r24, dwarf_r24, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r25, nullptr, dwarf_r25, dwarf_r25, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r26, nullptr, dwarf_r26, dwarf_r26, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r27, nullptr, dwarf_r27, dwarf_r27, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r28, nullptr, dwarf_r28, dwarf_r28, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r29, nullptr, dwarf_r29, dwarf_r29, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r30, nullptr, dwarf_r30, dwarf_r30, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r31, nullptr, dwarf_r31, dwarf_r31, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(lr, "lr", dwarf_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(cr, "cr", dwarf_cr, dwarf_cr, LLDB_REGNUM_GENERIC_FLAGS,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(xer, "xer", dwarf_xer, dwarf_xer, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(ctr, "ctr", dwarf_ctr, dwarf_ctr, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(pc, "pc", dwarf_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC,
+ LLDB_INVALID_REGNUM),
+ {nullptr,
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_cfa, dwarf_cfa, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0}};
+
+static const uint32_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos);
+
+const lldb_private::RegisterInfo *
+ABISysV_ppc::GetRegisterInfoArray(uint32_t &count) {
+ count = k_num_register_infos;
+ return g_register_infos;
+}
+
+size_t ABISysV_ppc::GetRedZoneSize() const { return 224; }
+
+// Static Functions
+
+ABISP
+ABISysV_ppc::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) {
+ if (arch.GetTriple().getArch() == llvm::Triple::ppc) {
+ return ABISP(new ABISysV_ppc(process_sp));
+ }
+ return ABISP();
+}
+
+bool ABISysV_ppc::PrepareTrivialCall(Thread &thread, addr_t sp,
+ addr_t func_addr, addr_t return_addr,
+ llvm::ArrayRef<addr_t> args) const {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ StreamString s;
+ s.Printf("ABISysV_ppc::PrepareTrivialCall (tid = 0x%" PRIx64
+ ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64
+ ", return_addr = 0x%" PRIx64,
+ thread.GetID(), (uint64_t)sp, (uint64_t)func_addr,
+ (uint64_t)return_addr);
+
+ for (size_t i = 0; i < args.size(); ++i)
+ s.Printf(", arg%" PRIu64 " = 0x%" PRIx64, static_cast<uint64_t>(i + 1),
+ args[i]);
+ s.PutCString(")");
+ log->PutString(s.GetString());
+ }
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+
+ const RegisterInfo *reg_info = nullptr;
+
+ if (args.size() > 8) // TODO handle more than 8 arguments
+ return false;
+
+ for (size_t i = 0; i < args.size(); ++i) {
+ reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_ARG1 + i);
+ if (log)
+ log->Printf("About to write arg%" PRIu64 " (0x%" PRIx64 ") into %s",
+ static_cast<uint64_t>(i + 1), args[i], reg_info->name);
+ if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i]))
+ return false;
+ }
+
+ // First, align the SP
+
+ if (log)
+ log->Printf("16-byte aligning SP: 0x%" PRIx64 " to 0x%" PRIx64,
+ (uint64_t)sp, (uint64_t)(sp & ~0xfull));
+
+ sp &= ~(0xfull); // 16-byte alignment
+
+ sp -= 8;
+
+ Status error;
+ const RegisterInfo *pc_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ const RegisterInfo *sp_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+ ProcessSP process_sp(thread.GetProcess());
+
+ RegisterValue reg_value;
+
+ if (log)
+ log->Printf("Pushing the return address onto the stack: 0x%" PRIx64
+ ": 0x%" PRIx64,
+ (uint64_t)sp, (uint64_t)return_addr);
+
+ // Save return address onto the stack
+ if (!process_sp->WritePointerToMemory(sp, return_addr, error))
+ return false;
+
+ // %r1 is set to the actual stack value.
+
+ if (log)
+ log->Printf("Writing SP: 0x%" PRIx64, (uint64_t)sp);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_info, sp))
+ return false;
+
+ // %pc is set to the address of the called function.
+
+ if (log)
+ log->Printf("Writing IP: 0x%" PRIx64, (uint64_t)func_addr);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_info, func_addr))
+ return false;
+
+ return true;
+}
+
+static bool ReadIntegerArgument(Scalar &scalar, unsigned int bit_width,
+ bool is_signed, Thread &thread,
+ uint32_t *argument_register_ids,
+ unsigned int &current_argument_register,
+ addr_t &current_stack_argument) {
+ if (bit_width > 64)
+ return false; // Scalar can't hold large integer arguments
+
+ if (current_argument_register < 6) {
+ scalar = thread.GetRegisterContext()->ReadRegisterAsUnsigned(
+ argument_register_ids[current_argument_register], 0);
+ current_argument_register++;
+ if (is_signed)
+ scalar.SignExtend(bit_width);
+ } else {
+ uint32_t byte_size = (bit_width + (8 - 1)) / 8;
+ Status error;
+ if (thread.GetProcess()->ReadScalarIntegerFromMemory(
+ current_stack_argument, byte_size, is_signed, scalar, error)) {
+ current_stack_argument += byte_size;
+ return true;
+ }
+ return false;
+ }
+ return true;
+}
+
+bool ABISysV_ppc::GetArgumentValues(Thread &thread, ValueList &values) const {
+ unsigned int num_values = values.GetSize();
+ unsigned int value_index;
+
+ // Extract the register context so we can read arguments from registers
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+
+ if (!reg_ctx)
+ return false;
+
+ // Get the pointer to the first stack argument so we have a place to start
+ // when reading data
+
+ addr_t sp = reg_ctx->GetSP(0);
+
+ if (!sp)
+ return false;
+
+ addr_t current_stack_argument = sp + 48; // jump over return address
+
+ uint32_t argument_register_ids[8];
+
+ argument_register_ids[0] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[1] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[2] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG3)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[3] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG4)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[4] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG5)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[5] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG6)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[6] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG7)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[7] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG8)
+ ->kinds[eRegisterKindLLDB];
+
+ unsigned int current_argument_register = 0;
+
+ for (value_index = 0; value_index < num_values; ++value_index) {
+ Value *value = values.GetValueAtIndex(value_index);
+
+ if (!value)
+ return false;
+
+ // We currently only support extracting values with Clang QualTypes. Do we
+ // care about others?
+ CompilerType compiler_type = value->GetCompilerType();
+ llvm::Optional<uint64_t> bit_size = compiler_type.GetBitSize(&thread);
+ if (!bit_size)
+ return false;
+ bool is_signed;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed))
+ ReadIntegerArgument(value->GetScalar(), *bit_size, is_signed, thread,
+ argument_register_ids, current_argument_register,
+ current_stack_argument);
+ else if (compiler_type.IsPointerType())
+ ReadIntegerArgument(value->GetScalar(), *bit_size, false, thread,
+ argument_register_ids, current_argument_register,
+ current_stack_argument);
+ }
+
+ return true;
+}
+
+Status ABISysV_ppc::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType compiler_type = new_value_sp->GetCompilerType();
+ if (!compiler_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+
+ bool is_signed;
+ uint32_t count;
+ bool is_complex;
+
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+
+ bool set_it_simple = false;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ compiler_type.IsPointerType()) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName("r3", 0);
+
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+ lldb::offset_t offset = 0;
+ if (num_bytes <= 8) {
+ uint64_t raw_value = data.GetMaxU64(&offset, num_bytes);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(reg_info, raw_value))
+ set_it_simple = true;
+ } else {
+ error.SetErrorString("We don't support returning longer than 64 bit "
+ "integer values at present.");
+ }
+ } else if (compiler_type.IsFloatingPointType(count, is_complex)) {
+ if (is_complex)
+ error.SetErrorString(
+ "We don't support returning complex values at present");
+ else {
+ llvm::Optional<uint64_t> bit_width =
+ compiler_type.GetBitSize(frame_sp.get());
+ if (!bit_width) {
+ error.SetErrorString("can't get type size");
+ return error;
+ }
+ if (*bit_width <= 64) {
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+
+ unsigned char buffer[16];
+ ByteOrder byte_order = data.GetByteOrder();
+
+ data.CopyByteOrderedData(0, num_bytes, buffer, 16, byte_order);
+ set_it_simple = true;
+ } else {
+ // FIXME - don't know how to do 80 bit long doubles yet.
+ error.SetErrorString(
+ "We don't support returning float values > 64 bits at present");
+ }
+ }
+ }
+
+ if (!set_it_simple) {
+ // Okay we've got a structure or something that doesn't fit in a simple
+ // register. We should figure out where it really goes, but we don't
+ // support this yet.
+ error.SetErrorString("We only support setting simple integer and float "
+ "return types at present.");
+ }
+
+ return error;
+}
+
+ValueObjectSP ABISysV_ppc::GetReturnValueObjectSimple(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ Value value;
+
+ if (!return_compiler_type)
+ return return_valobj_sp;
+
+ // value.SetContext (Value::eContextTypeClangType, return_value_type);
+ value.SetCompilerType(return_compiler_type);
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return return_valobj_sp;
+
+ const uint32_t type_flags = return_compiler_type.GetTypeInfo();
+ if (type_flags & eTypeIsScalar) {
+ value.SetValueType(Value::eValueTypeScalar);
+
+ bool success = false;
+ if (type_flags & eTypeIsInteger) {
+ // Extract the register context so we can read arguments from registers
+
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (!byte_size)
+ return return_valobj_sp;
+ uint64_t raw_value = thread.GetRegisterContext()->ReadRegisterAsUnsigned(
+ reg_ctx->GetRegisterInfoByName("r3", 0), 0);
+ const bool is_signed = (type_flags & eTypeIsSigned) != 0;
+ switch (*byte_size) {
+ default:
+ break;
+
+ case sizeof(uint64_t):
+ if (is_signed)
+ value.GetScalar() = (int64_t)(raw_value);
+ else
+ value.GetScalar() = (uint64_t)(raw_value);
+ success = true;
+ break;
+
+ case sizeof(uint32_t):
+ if (is_signed)
+ value.GetScalar() = (int32_t)(raw_value & UINT32_MAX);
+ else
+ value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint16_t):
+ if (is_signed)
+ value.GetScalar() = (int16_t)(raw_value & UINT16_MAX);
+ else
+ value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint8_t):
+ if (is_signed)
+ value.GetScalar() = (int8_t)(raw_value & UINT8_MAX);
+ else
+ value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX);
+ success = true;
+ break;
+ }
+ } else if (type_flags & eTypeIsFloat) {
+ if (type_flags & eTypeIsComplex) {
+ // Don't handle complex yet.
+ } else {
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (byte_size && *byte_size <= sizeof(long double)) {
+ const RegisterInfo *f1_info = reg_ctx->GetRegisterInfoByName("f1", 0);
+ RegisterValue f1_value;
+ if (reg_ctx->ReadRegister(f1_info, f1_value)) {
+ DataExtractor data;
+ if (f1_value.GetData(data)) {
+ lldb::offset_t offset = 0;
+ if (*byte_size == sizeof(float)) {
+ value.GetScalar() = (float)data.GetFloat(&offset);
+ success = true;
+ } else if (*byte_size == sizeof(double)) {
+ value.GetScalar() = (double)data.GetDouble(&offset);
+ success = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (success)
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if (type_flags & eTypeIsPointer) {
+ unsigned r3_id =
+ reg_ctx->GetRegisterInfoByName("r3", 0)->kinds[eRegisterKindLLDB];
+ value.GetScalar() =
+ (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(r3_id, 0);
+ value.SetValueType(Value::eValueTypeScalar);
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if (type_flags & eTypeIsVector) {
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (byte_size && *byte_size > 0) {
+ const RegisterInfo *altivec_reg = reg_ctx->GetRegisterInfoByName("v2", 0);
+ if (altivec_reg) {
+ if (*byte_size <= altivec_reg->byte_size) {
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp) {
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order = process_sp->GetByteOrder();
+ RegisterValue reg_value;
+ if (reg_ctx->ReadRegister(altivec_reg, reg_value)) {
+ Status error;
+ if (reg_value.GetAsMemoryData(
+ altivec_reg, heap_data_up->GetBytes(),
+ heap_data_up->GetByteSize(), byte_order, error)) {
+ DataExtractor data(DataBufferSP(heap_data_up.release()),
+ byte_order,
+ process_sp->GetTarget()
+ .GetArchitecture()
+ .GetAddressByteSize());
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), data);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return return_valobj_sp;
+}
+
+ValueObjectSP ABISysV_ppc::GetReturnValueObjectImpl(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+
+ if (!return_compiler_type)
+ return return_valobj_sp;
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ return_valobj_sp = GetReturnValueObjectSimple(thread, return_compiler_type);
+ if (return_valobj_sp)
+ return return_valobj_sp;
+
+ RegisterContextSP reg_ctx_sp = thread.GetRegisterContext();
+ if (!reg_ctx_sp)
+ return return_valobj_sp;
+
+ llvm::Optional<uint64_t> bit_width = return_compiler_type.GetBitSize(&thread);
+ if (!bit_width)
+ return return_valobj_sp;
+ if (return_compiler_type.IsAggregateType()) {
+ Target *target = exe_ctx.GetTargetPtr();
+ bool is_memory = true;
+ if (*bit_width <= 128) {
+ ByteOrder target_byte_order = target->GetArchitecture().GetByteOrder();
+ DataBufferSP data_sp(new DataBufferHeap(16, 0));
+ DataExtractor return_ext(data_sp, target_byte_order,
+ target->GetArchitecture().GetAddressByteSize());
+
+ const RegisterInfo *r3_info = reg_ctx_sp->GetRegisterInfoByName("r3", 0);
+ const RegisterInfo *rdx_info =
+ reg_ctx_sp->GetRegisterInfoByName("rdx", 0);
+
+ RegisterValue r3_value, rdx_value;
+ reg_ctx_sp->ReadRegister(r3_info, r3_value);
+ reg_ctx_sp->ReadRegister(rdx_info, rdx_value);
+
+ DataExtractor r3_data, rdx_data;
+
+ r3_value.GetData(r3_data);
+ rdx_value.GetData(rdx_data);
+
+ uint32_t fp_bytes =
+ 0; // Tracks how much of the xmm registers we've consumed so far
+ uint32_t integer_bytes =
+ 0; // Tracks how much of the r3/rds registers we've consumed so far
+
+ const uint32_t num_children = return_compiler_type.GetNumFields();
+
+ // Since we are in the small struct regime, assume we are not in memory.
+ is_memory = false;
+
+ for (uint32_t idx = 0; idx < num_children; idx++) {
+ std::string name;
+ uint64_t field_bit_offset = 0;
+ bool is_signed;
+ bool is_complex;
+ uint32_t count;
+
+ CompilerType field_compiler_type = return_compiler_type.GetFieldAtIndex(
+ idx, name, &field_bit_offset, nullptr, nullptr);
+ llvm::Optional<uint64_t> field_bit_width =
+ field_compiler_type.GetBitSize(&thread);
+ if (!field_bit_width)
+ return return_valobj_sp;
+
+ // If there are any unaligned fields, this is stored in memory.
+ if (field_bit_offset % *field_bit_width != 0) {
+ is_memory = true;
+ break;
+ }
+
+ uint32_t field_byte_width = *field_bit_width / 8;
+ uint32_t field_byte_offset = field_bit_offset / 8;
+
+ DataExtractor *copy_from_extractor = nullptr;
+ uint32_t copy_from_offset = 0;
+
+ if (field_compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ field_compiler_type.IsPointerType()) {
+ if (integer_bytes < 8) {
+ if (integer_bytes + field_byte_width <= 8) {
+ // This is in RAX, copy from register to our result structure:
+ copy_from_extractor = &r3_data;
+ copy_from_offset = integer_bytes;
+ integer_bytes += field_byte_width;
+ } else {
+ // The next field wouldn't fit in the remaining space, so we
+ // pushed it to rdx.
+ copy_from_extractor = &rdx_data;
+ copy_from_offset = 0;
+ integer_bytes = 8 + field_byte_width;
+ }
+ } else if (integer_bytes + field_byte_width <= 16) {
+ copy_from_extractor = &rdx_data;
+ copy_from_offset = integer_bytes - 8;
+ integer_bytes += field_byte_width;
+ } else {
+ // The last field didn't fit. I can't see how that would happen
+ // w/o the overall size being greater than 16 bytes. For now,
+ // return a nullptr return value object.
+ return return_valobj_sp;
+ }
+ } else if (field_compiler_type.IsFloatingPointType(count, is_complex)) {
+ // Structs with long doubles are always passed in memory.
+ if (*field_bit_width == 128) {
+ is_memory = true;
+ break;
+ } else if (*field_bit_width == 64) {
+ copy_from_offset = 0;
+ fp_bytes += field_byte_width;
+ } else if (*field_bit_width == 32) {
+ // This one is kind of complicated. If we are in an "eightbyte"
+ // with another float, we'll be stuffed into an xmm register with
+ // it. If we are in an "eightbyte" with one or more ints, then we
+ // will be stuffed into the appropriate GPR with them.
+ bool in_gpr;
+ if (field_byte_offset % 8 == 0) {
+ // We are at the beginning of one of the eightbytes, so check the
+ // next element (if any)
+ if (idx == num_children - 1)
+ in_gpr = false;
+ else {
+ uint64_t next_field_bit_offset = 0;
+ CompilerType next_field_compiler_type =
+ return_compiler_type.GetFieldAtIndex(idx + 1, name,
+ &next_field_bit_offset,
+ nullptr, nullptr);
+ if (next_field_compiler_type.IsIntegerOrEnumerationType(
+ is_signed))
+ in_gpr = true;
+ else {
+ copy_from_offset = 0;
+ in_gpr = false;
+ }
+ }
+ } else if (field_byte_offset % 4 == 0) {
+ // We are inside of an eightbyte, so see if the field before us
+ // is floating point: This could happen if somebody put padding
+ // in the structure.
+ if (idx == 0)
+ in_gpr = false;
+ else {
+ uint64_t prev_field_bit_offset = 0;
+ CompilerType prev_field_compiler_type =
+ return_compiler_type.GetFieldAtIndex(idx - 1, name,
+ &prev_field_bit_offset,
+ nullptr, nullptr);
+ if (prev_field_compiler_type.IsIntegerOrEnumerationType(
+ is_signed))
+ in_gpr = true;
+ else {
+ copy_from_offset = 4;
+ in_gpr = false;
+ }
+ }
+ } else {
+ is_memory = true;
+ continue;
+ }
+
+ // Okay, we've figured out whether we are in GPR or XMM, now figure
+ // out which one.
+ if (in_gpr) {
+ if (integer_bytes < 8) {
+ // This is in RAX, copy from register to our result structure:
+ copy_from_extractor = &r3_data;
+ copy_from_offset = integer_bytes;
+ integer_bytes += field_byte_width;
+ } else {
+ copy_from_extractor = &rdx_data;
+ copy_from_offset = integer_bytes - 8;
+ integer_bytes += field_byte_width;
+ }
+ } else {
+ fp_bytes += field_byte_width;
+ }
+ }
+ }
+
+ // These two tests are just sanity checks. If I somehow get the type
+ // calculation wrong above it is better to just return nothing than to
+ // assert or crash.
+ if (!copy_from_extractor)
+ return return_valobj_sp;
+ if (copy_from_offset + field_byte_width >
+ copy_from_extractor->GetByteSize())
+ return return_valobj_sp;
+
+ copy_from_extractor->CopyByteOrderedData(
+ copy_from_offset, field_byte_width,
+ data_sp->GetBytes() + field_byte_offset, field_byte_width,
+ target_byte_order);
+ }
+
+ if (!is_memory) {
+ // The result is in our data buffer. Let's make a variable object out
+ // of it:
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), return_ext);
+ }
+ }
+
+ // FIXME: This is just taking a guess, r3 may very well no longer hold the
+ // return storage location.
+ // If we are going to do this right, when we make a new frame we should
+ // check to see if it uses a memory return, and if we are at the first
+ // instruction and if so stash away the return location. Then we would
+ // only return the memory return value if we know it is valid.
+
+ if (is_memory) {
+ unsigned r3_id =
+ reg_ctx_sp->GetRegisterInfoByName("r3", 0)->kinds[eRegisterKindLLDB];
+ lldb::addr_t storage_addr =
+ (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(r3_id,
+ 0);
+ return_valobj_sp = ValueObjectMemory::Create(
+ &thread, "", Address(storage_addr, nullptr), return_compiler_type);
+ }
+ }
+
+ return return_valobj_sp;
+}
+
+bool ABISysV_ppc::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t lr_reg_num = dwarf_lr;
+ uint32_t sp_reg_num = dwarf_r1;
+ uint32_t pc_reg_num = dwarf_pc;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our Call Frame Address is the stack pointer value
+ row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 0);
+
+ // The previous PC is in the LR
+ row->SetRegisterLocationToRegister(pc_reg_num, lr_reg_num, true);
+ unwind_plan.AppendRow(row);
+
+ // All other registers are the same.
+
+ unwind_plan.SetSourceName("ppc at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+
+ return true;
+}
+
+bool ABISysV_ppc::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t sp_reg_num = dwarf_r1;
+ uint32_t pc_reg_num = dwarf_lr;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ const int32_t ptr_size = 4;
+ row->GetCFAValue().SetIsRegisterDereferenced(sp_reg_num);
+
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * 1, true);
+ row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("ppc default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ unwind_plan.SetReturnAddressRegister(dwarf_lr);
+ return true;
+}
+
+bool ABISysV_ppc::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ return !RegisterIsCalleeSaved(reg_info);
+}
+
+// See "Register Usage" in the
+// "System V Application Binary Interface"
+// "64-bit PowerPC ELF Application Binary Interface Supplement" current version
+// is 1.9 released 2004 at http://refspecs.linuxfoundation.org/ELF/ppc/PPC-
+// elf64abi-1.9.pdf
+
+bool ABISysV_ppc::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
+ if (reg_info) {
+ // Preserved registers are :
+ // r1,r2,r13-r31
+ // f14-f31 (not yet)
+ // v20-v31 (not yet)
+ // vrsave (not yet)
+
+ const char *name = reg_info->name;
+ if (name[0] == 'r') {
+ if ((name[1] == '1' || name[1] == '2') && name[2] == '\0')
+ return true;
+ if (name[1] == '1' && name[2] > '2')
+ return true;
+ if ((name[1] == '2' || name[1] == '3') && name[2] != '\0')
+ return true;
+ }
+
+ if (name[0] == 'f' && name[1] >= '0' && name[1] <= '9') {
+ if (name[3] == '1' && name[4] >= '4')
+ return true;
+ if ((name[3] == '2' || name[3] == '3') && name[4] != '\0')
+ return true;
+ }
+
+ if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') // sp
+ return true;
+ if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0') // fp
+ return true;
+ if (name[0] == 'p' && name[1] == 'c' && name[2] == '\0') // pc
+ return true;
+ }
+ return false;
+}
+
+void ABISysV_ppc::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ "System V ABI for ppc targets", CreateInstance);
+}
+
+void ABISysV_ppc::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ABISysV_ppc::GetPluginNameStatic() {
+ static ConstString g_name("sysv-ppc");
+ return g_name;
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString ABISysV_ppc::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ABISysV_ppc::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc/ABISysV_ppc.h b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc/ABISysV_ppc.h
new file mode 100644
index 000000000000..3b199852c30d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc/ABISysV_ppc.h
@@ -0,0 +1,104 @@
+//===-- ABISysV_ppc.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 liblldb_ABISysV_ppc_h_
+#define liblldb_ABISysV_ppc_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABISysV_ppc : public lldb_private::ABI {
+public:
+ ~ABISysV_ppc() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress,
+ lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &type) const override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ // The SysV ppc ABI requires that stack frames be 16 byte aligned.
+ // When there is a trap handler on the stack, e.g. _sigtramp in userland
+ // code, we've seen that the stack pointer is often not aligned properly
+ // before the handler is invoked. This means that lldb will stop the unwind
+ // early -- before the function which caused the trap.
+ //
+ // To work around this, we relax that alignment to be just word-size
+ // (8-bytes).
+ // Whitelisting the trap handlers for user space would be easy (_sigtramp) but
+ // in other environments there can be a large number of different functions
+ // involved in async traps.
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are 8 byte aligned
+ if (cfa & (8ull - 1ull))
+ return false; // Not 8 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ // We have a 64 bit address space, so anything is valid as opcodes
+ // aren't fixed width...
+ return true;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ // PluginInterface protocol
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ void CreateRegisterMapIfNeeded();
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectSimple(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const;
+
+ bool RegisterIsCalleeSaved(const lldb_private::RegisterInfo *reg_info);
+
+private:
+ ABISysV_ppc(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABISysV_ppc_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc64/ABISysV_ppc64.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc64/ABISysV_ppc64.cpp
new file mode 100644
index 000000000000..aa7907550f29
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc64/ABISysV_ppc64.cpp
@@ -0,0 +1,1091 @@
+//===-- ABISysV_ppc64.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 "ABISysV_ppc64.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+
+#include "Utility/PPC64LE_DWARF_Registers.h"
+#include "Utility/PPC64_DWARF_Registers.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Core/ValueObjectRegister.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/Decl.h"
+
+#define DECLARE_REGISTER_INFOS_PPC64_STRUCT
+#include "Plugins/Process/Utility/RegisterInfos_ppc64.h"
+#undef DECLARE_REGISTER_INFOS_PPC64_STRUCT
+
+#define DECLARE_REGISTER_INFOS_PPC64LE_STRUCT
+#include "Plugins/Process/Utility/RegisterInfos_ppc64le.h"
+#undef DECLARE_REGISTER_INFOS_PPC64LE_STRUCT
+
+using namespace lldb;
+using namespace lldb_private;
+
+const lldb_private::RegisterInfo *
+ABISysV_ppc64::GetRegisterInfoArray(uint32_t &count) {
+ if (GetByteOrder() == lldb::eByteOrderLittle) {
+ count = llvm::array_lengthof(g_register_infos_ppc64le);
+ return g_register_infos_ppc64le;
+ } else {
+ count = llvm::array_lengthof(g_register_infos_ppc64);
+ return g_register_infos_ppc64;
+ }
+}
+
+size_t ABISysV_ppc64::GetRedZoneSize() const { return 224; }
+
+lldb::ByteOrder ABISysV_ppc64::GetByteOrder() const {
+ return GetProcessSP()->GetByteOrder();
+}
+
+// Static Functions
+
+ABISP
+ABISysV_ppc64::CreateInstance(lldb::ProcessSP process_sp,
+ const ArchSpec &arch) {
+ if (arch.GetTriple().isPPC64())
+ return ABISP(new ABISysV_ppc64(process_sp));
+ return ABISP();
+}
+
+bool ABISysV_ppc64::PrepareTrivialCall(Thread &thread, addr_t sp,
+ addr_t func_addr, addr_t return_addr,
+ llvm::ArrayRef<addr_t> args) const {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ StreamString s;
+ s.Printf("ABISysV_ppc64::PrepareTrivialCall (tid = 0x%" PRIx64
+ ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64
+ ", return_addr = 0x%" PRIx64,
+ thread.GetID(), (uint64_t)sp, (uint64_t)func_addr,
+ (uint64_t)return_addr);
+
+ for (size_t i = 0; i < args.size(); ++i)
+ s.Printf(", arg%" PRIu64 " = 0x%" PRIx64, static_cast<uint64_t>(i + 1),
+ args[i]);
+ s.PutCString(")");
+ log->PutString(s.GetString());
+ }
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+
+ const RegisterInfo *reg_info = nullptr;
+
+ if (args.size() > 8) // TODO handle more than 8 arguments
+ return false;
+
+ for (size_t i = 0; i < args.size(); ++i) {
+ reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_ARG1 + i);
+ if (log)
+ log->Printf("About to write arg%" PRIu64 " (0x%" PRIx64 ") into %s",
+ static_cast<uint64_t>(i + 1), args[i], reg_info->name);
+ if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i]))
+ return false;
+ }
+
+ // First, align the SP
+
+ if (log)
+ log->Printf("16-byte aligning SP: 0x%" PRIx64 " to 0x%" PRIx64,
+ (uint64_t)sp, (uint64_t)(sp & ~0xfull));
+
+ sp &= ~(0xfull); // 16-byte alignment
+
+ sp -= 544; // allocate frame to save TOC, RA and SP.
+
+ Status error;
+ uint64_t reg_value;
+ const RegisterInfo *pc_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ const RegisterInfo *sp_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+ ProcessSP process_sp(thread.GetProcess());
+ const RegisterInfo *lr_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA);
+ const RegisterInfo *r2_reg_info = reg_ctx->GetRegisterInfoAtIndex(2);
+ const RegisterInfo *r12_reg_info = reg_ctx->GetRegisterInfoAtIndex(12);
+
+ // Save return address onto the stack.
+ if (log)
+ log->Printf("Pushing the return address onto the stack: 0x%" PRIx64
+ "(+16): 0x%" PRIx64,
+ (uint64_t)sp, (uint64_t)return_addr);
+ if (!process_sp->WritePointerToMemory(sp + 16, return_addr, error))
+ return false;
+
+ // Write the return address to link register.
+ if (log)
+ log->Printf("Writing LR: 0x%" PRIx64, (uint64_t)return_addr);
+ if (!reg_ctx->WriteRegisterFromUnsigned(lr_reg_info, return_addr))
+ return false;
+
+ // Write target address to %r12 register.
+ if (log)
+ log->Printf("Writing R12: 0x%" PRIx64, (uint64_t)func_addr);
+ if (!reg_ctx->WriteRegisterFromUnsigned(r12_reg_info, func_addr))
+ return false;
+
+ // Read TOC pointer value.
+ reg_value = reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0);
+
+ // Write TOC pointer onto the stack.
+ uint64_t stack_offset;
+ if (GetByteOrder() == lldb::eByteOrderLittle)
+ stack_offset = 24;
+ else
+ stack_offset = 40;
+
+ if (log)
+ log->Printf("Writing R2 (TOC) at SP(0x%" PRIx64 ")+%d: 0x%" PRIx64,
+ (uint64_t)(sp + stack_offset), (int)stack_offset,
+ (uint64_t)reg_value);
+ if (!process_sp->WritePointerToMemory(sp + stack_offset, reg_value, error))
+ return false;
+
+ // Read the current SP value.
+ reg_value = reg_ctx->ReadRegisterAsUnsigned(sp_reg_info, 0);
+
+ // Save current SP onto the stack.
+ if (log)
+ log->Printf("Writing SP at SP(0x%" PRIx64 ")+0: 0x%" PRIx64, (uint64_t)sp,
+ (uint64_t)reg_value);
+ if (!process_sp->WritePointerToMemory(sp, reg_value, error))
+ return false;
+
+ // %r1 is set to the actual stack value.
+ if (log)
+ log->Printf("Writing SP: 0x%" PRIx64, (uint64_t)sp);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_info, sp))
+ return false;
+
+ // %pc is set to the address of the called function.
+
+ if (log)
+ log->Printf("Writing IP: 0x%" PRIx64, (uint64_t)func_addr);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_info, func_addr))
+ return false;
+
+ return true;
+}
+
+static bool ReadIntegerArgument(Scalar &scalar, unsigned int bit_width,
+ bool is_signed, Thread &thread,
+ uint32_t *argument_register_ids,
+ unsigned int &current_argument_register,
+ addr_t &current_stack_argument) {
+ if (bit_width > 64)
+ return false; // Scalar can't hold large integer arguments
+
+ if (current_argument_register < 6) {
+ scalar = thread.GetRegisterContext()->ReadRegisterAsUnsigned(
+ argument_register_ids[current_argument_register], 0);
+ current_argument_register++;
+ if (is_signed)
+ scalar.SignExtend(bit_width);
+ } else {
+ uint32_t byte_size = (bit_width + (8 - 1)) / 8;
+ Status error;
+ if (thread.GetProcess()->ReadScalarIntegerFromMemory(
+ current_stack_argument, byte_size, is_signed, scalar, error)) {
+ current_stack_argument += byte_size;
+ return true;
+ }
+ return false;
+ }
+ return true;
+}
+
+bool ABISysV_ppc64::GetArgumentValues(Thread &thread, ValueList &values) const {
+ unsigned int num_values = values.GetSize();
+ unsigned int value_index;
+
+ // Extract the register context so we can read arguments from registers
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+
+ if (!reg_ctx)
+ return false;
+
+ // Get the pointer to the first stack argument so we have a place to start
+ // when reading data
+
+ addr_t sp = reg_ctx->GetSP(0);
+
+ if (!sp)
+ return false;
+
+ uint64_t stack_offset;
+ if (GetByteOrder() == lldb::eByteOrderLittle)
+ stack_offset = 32;
+ else
+ stack_offset = 48;
+
+ // jump over return address.
+ addr_t current_stack_argument = sp + stack_offset;
+ uint32_t argument_register_ids[8];
+
+ for (size_t i = 0; i < 8; ++i) {
+ argument_register_ids[i] =
+ reg_ctx
+ ->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_ARG1 + i)
+ ->kinds[eRegisterKindLLDB];
+ }
+
+ unsigned int current_argument_register = 0;
+
+ for (value_index = 0; value_index < num_values; ++value_index) {
+ Value *value = values.GetValueAtIndex(value_index);
+
+ if (!value)
+ return false;
+
+ // We currently only support extracting values with Clang QualTypes. Do we
+ // care about others?
+ CompilerType compiler_type = value->GetCompilerType();
+ llvm::Optional<uint64_t> bit_size = compiler_type.GetBitSize(&thread);
+ if (!bit_size)
+ return false;
+ bool is_signed;
+
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed)) {
+ ReadIntegerArgument(value->GetScalar(), *bit_size, is_signed, thread,
+ argument_register_ids, current_argument_register,
+ current_stack_argument);
+ } else if (compiler_type.IsPointerType()) {
+ ReadIntegerArgument(value->GetScalar(), *bit_size, false, thread,
+ argument_register_ids, current_argument_register,
+ current_stack_argument);
+ }
+ }
+
+ return true;
+}
+
+Status ABISysV_ppc64::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType compiler_type = new_value_sp->GetCompilerType();
+ if (!compiler_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+
+ bool is_signed;
+ uint32_t count;
+ bool is_complex;
+
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+
+ bool set_it_simple = false;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ compiler_type.IsPointerType()) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName("r3", 0);
+
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+ lldb::offset_t offset = 0;
+ if (num_bytes <= 8) {
+ uint64_t raw_value = data.GetMaxU64(&offset, num_bytes);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(reg_info, raw_value))
+ set_it_simple = true;
+ } else {
+ error.SetErrorString("We don't support returning longer than 64 bit "
+ "integer values at present.");
+ }
+ } else if (compiler_type.IsFloatingPointType(count, is_complex)) {
+ if (is_complex)
+ error.SetErrorString(
+ "We don't support returning complex values at present");
+ else {
+ llvm::Optional<uint64_t> bit_width =
+ compiler_type.GetBitSize(frame_sp.get());
+ if (!bit_width) {
+ error.SetErrorString("can't get size of type");
+ return error;
+ }
+ if (*bit_width <= 64) {
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+
+ unsigned char buffer[16];
+ ByteOrder byte_order = data.GetByteOrder();
+
+ data.CopyByteOrderedData(0, num_bytes, buffer, 16, byte_order);
+ set_it_simple = true;
+ } else {
+ // FIXME - don't know how to do 80 bit long doubles yet.
+ error.SetErrorString(
+ "We don't support returning float values > 64 bits at present");
+ }
+ }
+ }
+
+ if (!set_it_simple) {
+ // Okay we've got a structure or something that doesn't fit in a simple
+ // register. We should figure out where it really goes, but we don't
+ // support this yet.
+ error.SetErrorString("We only support setting simple integer and float "
+ "return types at present.");
+ }
+
+ return error;
+}
+
+//
+// ReturnValueExtractor
+//
+
+namespace {
+
+#define LOG_PREFIX "ReturnValueExtractor: "
+
+class ReturnValueExtractor {
+ // This class represents a register, from which data may be extracted.
+ //
+ // It may be constructed by directly specifying its index (where 0 is the
+ // first register used to return values) or by specifying the offset of a
+ // given struct field, in which case the appropriated register index will be
+ // calculated.
+ class Register {
+ public:
+ enum Type {
+ GPR, // General Purpose Register
+ FPR // Floating Point Register
+ };
+
+ // main constructor
+ //
+ // offs - field offset in struct
+ Register(Type ty, uint32_t index, uint32_t offs, RegisterContext *reg_ctx,
+ ByteOrder byte_order)
+ : m_index(index), m_offs(offs % sizeof(uint64_t)),
+ m_avail(sizeof(uint64_t) - m_offs), m_type(ty), m_reg_ctx(reg_ctx),
+ m_byte_order(byte_order) {}
+
+ // explicit index, no offset
+ Register(Type ty, uint32_t index, RegisterContext *reg_ctx,
+ ByteOrder byte_order)
+ : Register(ty, index, 0, reg_ctx, byte_order) {}
+
+ // GPR, calculate index from offs
+ Register(uint32_t offs, RegisterContext *reg_ctx, ByteOrder byte_order)
+ : Register(GPR, offs / sizeof(uint64_t), offs, reg_ctx, byte_order) {}
+
+ uint32_t Index() const { return m_index; }
+
+ // register offset where data is located
+ uint32_t Offs() const { return m_offs; }
+
+ // available bytes in this register
+ uint32_t Avail() const { return m_avail; }
+
+ bool IsValid() const {
+ if (m_index > 7) {
+ LLDB_LOG(m_log, LOG_PREFIX
+ "No more than 8 registers should be used to return values");
+ return false;
+ }
+ return true;
+ }
+
+ std::string GetName() const {
+ if (m_type == GPR)
+ return ("r" + llvm::Twine(m_index + 3)).str();
+ else
+ return ("f" + llvm::Twine(m_index + 1)).str();
+ }
+
+ // get raw register data
+ bool GetRawData(uint64_t &raw_data) {
+ const RegisterInfo *reg_info =
+ m_reg_ctx->GetRegisterInfoByName(GetName());
+ if (!reg_info) {
+ LLDB_LOG(m_log, LOG_PREFIX "Failed to get RegisterInfo");
+ return false;
+ }
+
+ RegisterValue reg_val;
+ if (!m_reg_ctx->ReadRegister(reg_info, reg_val)) {
+ LLDB_LOG(m_log, LOG_PREFIX "ReadRegister() failed");
+ return false;
+ }
+
+ Status error;
+ uint32_t rc = reg_val.GetAsMemoryData(
+ reg_info, &raw_data, sizeof(raw_data), m_byte_order, error);
+ if (rc != sizeof(raw_data)) {
+ LLDB_LOG(m_log, LOG_PREFIX "GetAsMemoryData() failed");
+ return false;
+ }
+
+ return true;
+ }
+
+ private:
+ uint32_t m_index;
+ uint32_t m_offs;
+ uint32_t m_avail;
+ Type m_type;
+ RegisterContext *m_reg_ctx;
+ ByteOrder m_byte_order;
+ Log *m_log =
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS);
+ };
+
+ Register GetGPR(uint32_t index) const {
+ return Register(Register::GPR, index, m_reg_ctx, m_byte_order);
+ }
+
+ Register GetFPR(uint32_t index) const {
+ return Register(Register::FPR, index, m_reg_ctx, m_byte_order);
+ }
+
+ Register GetGPRByOffs(uint32_t offs) const {
+ return Register(offs, m_reg_ctx, m_byte_order);
+ }
+
+public:
+ // factory
+ static llvm::Expected<ReturnValueExtractor> Create(Thread &thread,
+ CompilerType &type) {
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return llvm::make_error<llvm::StringError>(
+ LOG_PREFIX "Failed to get RegisterContext",
+ llvm::inconvertibleErrorCode());
+
+ ProcessSP process_sp = thread.GetProcess();
+ if (!process_sp)
+ return llvm::make_error<llvm::StringError>(
+ LOG_PREFIX "GetProcess() failed", llvm::inconvertibleErrorCode());
+
+ return ReturnValueExtractor(thread, type, reg_ctx, process_sp);
+ }
+
+ // main method: get value of the type specified at construction time
+ ValueObjectSP GetValue() {
+ const uint32_t type_flags = m_type.GetTypeInfo();
+
+ // call the appropriate type handler
+ ValueSP value_sp;
+ ValueObjectSP valobj_sp;
+ if (type_flags & eTypeIsScalar) {
+ if (type_flags & eTypeIsInteger) {
+ value_sp = GetIntegerValue(0);
+ } else if (type_flags & eTypeIsFloat) {
+ if (type_flags & eTypeIsComplex) {
+ LLDB_LOG(m_log, LOG_PREFIX "Complex numbers are not supported yet");
+ return ValueObjectSP();
+ } else {
+ value_sp = GetFloatValue(m_type, 0);
+ }
+ }
+ } else if (type_flags & eTypeIsPointer) {
+ value_sp = GetPointerValue(0);
+ }
+
+ if (value_sp) {
+ valobj_sp = ValueObjectConstResult::Create(
+ m_thread.GetStackFrameAtIndex(0).get(), *value_sp, ConstString(""));
+ } else if (type_flags & eTypeIsVector) {
+ valobj_sp = GetVectorValueObject();
+ } else if (type_flags & eTypeIsStructUnion || type_flags & eTypeIsClass) {
+ valobj_sp = GetStructValueObject();
+ }
+
+ return valobj_sp;
+ }
+
+private:
+ // data
+ Thread &m_thread;
+ CompilerType &m_type;
+ uint64_t m_byte_size;
+ std::unique_ptr<DataBufferHeap> m_data_up;
+ int32_t m_src_offs = 0;
+ int32_t m_dst_offs = 0;
+ bool m_packed = false;
+ Log *m_log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS);
+ RegisterContext *m_reg_ctx;
+ ProcessSP m_process_sp;
+ ByteOrder m_byte_order;
+ uint32_t m_addr_size;
+
+ // methods
+
+ // constructor
+ ReturnValueExtractor(Thread &thread, CompilerType &type,
+ RegisterContext *reg_ctx, ProcessSP process_sp)
+ : m_thread(thread), m_type(type),
+ m_byte_size(m_type.GetByteSize(nullptr).getValueOr(0)),
+ m_data_up(new DataBufferHeap(m_byte_size, 0)), m_reg_ctx(reg_ctx),
+ m_process_sp(process_sp), m_byte_order(process_sp->GetByteOrder()),
+ m_addr_size(
+ process_sp->GetTarget().GetArchitecture().GetAddressByteSize()) {}
+
+ // build a new scalar value
+ ValueSP NewScalarValue(CompilerType &type) {
+ ValueSP value_sp(new Value);
+ value_sp->SetCompilerType(type);
+ value_sp->SetValueType(Value::eValueTypeScalar);
+ return value_sp;
+ }
+
+ // get an integer value in the specified register
+ ValueSP GetIntegerValue(uint32_t reg_index) {
+ uint64_t raw_value;
+ auto reg = GetGPR(reg_index);
+ if (!reg.GetRawData(raw_value))
+ return ValueSP();
+
+ // build value from data
+ ValueSP value_sp(NewScalarValue(m_type));
+
+ uint32_t type_flags = m_type.GetTypeInfo();
+ bool is_signed = (type_flags & eTypeIsSigned) != 0;
+
+ switch (m_byte_size) {
+ case sizeof(uint64_t):
+ if (is_signed)
+ value_sp->GetScalar() = (int64_t)(raw_value);
+ else
+ value_sp->GetScalar() = (uint64_t)(raw_value);
+ break;
+
+ case sizeof(uint32_t):
+ if (is_signed)
+ value_sp->GetScalar() = (int32_t)(raw_value & UINT32_MAX);
+ else
+ value_sp->GetScalar() = (uint32_t)(raw_value & UINT32_MAX);
+ break;
+
+ case sizeof(uint16_t):
+ if (is_signed)
+ value_sp->GetScalar() = (int16_t)(raw_value & UINT16_MAX);
+ else
+ value_sp->GetScalar() = (uint16_t)(raw_value & UINT16_MAX);
+ break;
+
+ case sizeof(uint8_t):
+ if (is_signed)
+ value_sp->GetScalar() = (int8_t)(raw_value & UINT8_MAX);
+ else
+ value_sp->GetScalar() = (uint8_t)(raw_value & UINT8_MAX);
+ break;
+
+ default:
+ llvm_unreachable("Invalid integer size");
+ }
+
+ return value_sp;
+ }
+
+ // get a floating point value on the specified register
+ ValueSP GetFloatValue(CompilerType &type, uint32_t reg_index) {
+ uint64_t raw_data;
+ auto reg = GetFPR(reg_index);
+ if (!reg.GetRawData(raw_data))
+ return {};
+
+ // build value from data
+ ValueSP value_sp(NewScalarValue(type));
+
+ DataExtractor de(&raw_data, sizeof(raw_data), m_byte_order, m_addr_size);
+
+ offset_t offset = 0;
+ llvm::Optional<uint64_t> byte_size = type.GetByteSize(nullptr);
+ if (!byte_size)
+ return {};
+ switch (*byte_size) {
+ case sizeof(float):
+ value_sp->GetScalar() = (float)de.GetDouble(&offset);
+ break;
+
+ case sizeof(double):
+ value_sp->GetScalar() = de.GetDouble(&offset);
+ break;
+
+ default:
+ llvm_unreachable("Invalid floating point size");
+ }
+
+ return value_sp;
+ }
+
+ // get pointer value from register
+ ValueSP GetPointerValue(uint32_t reg_index) {
+ uint64_t raw_data;
+ auto reg = GetGPR(reg_index);
+ if (!reg.GetRawData(raw_data))
+ return ValueSP();
+
+ // build value from raw data
+ ValueSP value_sp(NewScalarValue(m_type));
+ value_sp->GetScalar() = raw_data;
+ return value_sp;
+ }
+
+ // build the ValueObject from our data buffer
+ ValueObjectSP BuildValueObject() {
+ DataExtractor de(DataBufferSP(m_data_up.release()), m_byte_order,
+ m_addr_size);
+ return ValueObjectConstResult::Create(&m_thread, m_type, ConstString(""),
+ de);
+ }
+
+ // get a vector return value
+ ValueObjectSP GetVectorValueObject() {
+ const uint32_t MAX_VRS = 2;
+
+ // get first V register used to return values
+ const RegisterInfo *vr[MAX_VRS];
+ vr[0] = m_reg_ctx->GetRegisterInfoByName("vr2");
+ if (!vr[0]) {
+ LLDB_LOG(m_log, LOG_PREFIX "Failed to get vr2 RegisterInfo");
+ return ValueObjectSP();
+ }
+
+ const uint32_t vr_size = vr[0]->byte_size;
+ size_t vrs = 1;
+ if (m_byte_size > 2 * vr_size) {
+ LLDB_LOG(
+ m_log, LOG_PREFIX
+ "Returning vectors that don't fit in 2 VR regs is not supported");
+ return ValueObjectSP();
+ }
+
+ // load vr3, if needed
+ if (m_byte_size > vr_size) {
+ vrs++;
+ vr[1] = m_reg_ctx->GetRegisterInfoByName("vr3");
+ if (!vr[1]) {
+ LLDB_LOG(m_log, LOG_PREFIX "Failed to get vr3 RegisterInfo");
+ return ValueObjectSP();
+ }
+ }
+
+ // Get the whole contents of vector registers and let the logic here
+ // arrange the data properly.
+
+ RegisterValue vr_val[MAX_VRS];
+ Status error;
+ std::unique_ptr<DataBufferHeap> vr_data(
+ new DataBufferHeap(vrs * vr_size, 0));
+
+ for (uint32_t i = 0; i < vrs; i++) {
+ if (!m_reg_ctx->ReadRegister(vr[i], vr_val[i])) {
+ LLDB_LOG(m_log, LOG_PREFIX "Failed to read vector register contents");
+ return ValueObjectSP();
+ }
+ if (!vr_val[i].GetAsMemoryData(vr[i], vr_data->GetBytes() + i * vr_size,
+ vr_size, m_byte_order, error)) {
+ LLDB_LOG(m_log, LOG_PREFIX "Failed to extract vector register bytes");
+ return ValueObjectSP();
+ }
+ }
+
+ // The compiler generated code seems to always put the vector elements at
+ // the end of the vector register, in case they don't occupy all of it.
+ // This offset variable handles this.
+ uint32_t offs = 0;
+ if (m_byte_size < vr_size)
+ offs = vr_size - m_byte_size;
+
+ // copy extracted data to our buffer
+ memcpy(m_data_up->GetBytes(), vr_data->GetBytes() + offs, m_byte_size);
+ return BuildValueObject();
+ }
+
+ // get a struct return value
+ ValueObjectSP GetStructValueObject() {
+ // case 1: get from stack
+ if (m_byte_size > 2 * sizeof(uint64_t)) {
+ uint64_t addr;
+ auto reg = GetGPR(0);
+ if (!reg.GetRawData(addr))
+ return {};
+
+ Status error;
+ size_t rc = m_process_sp->ReadMemory(addr, m_data_up->GetBytes(),
+ m_byte_size, error);
+ if (rc != m_byte_size) {
+ LLDB_LOG(m_log, LOG_PREFIX "Failed to read memory pointed by r3");
+ return ValueObjectSP();
+ }
+ return BuildValueObject();
+ }
+
+ // get number of children
+ const bool omit_empty_base_classes = true;
+ uint32_t n = m_type.GetNumChildren(omit_empty_base_classes, nullptr);
+ if (!n) {
+ LLDB_LOG(m_log, LOG_PREFIX "No children found in struct");
+ return {};
+ }
+
+ // case 2: homogeneous double or float aggregate
+ CompilerType elem_type;
+ if (m_type.IsHomogeneousAggregate(&elem_type)) {
+ uint32_t type_flags = elem_type.GetTypeInfo();
+ llvm::Optional<uint64_t> elem_size = elem_type.GetByteSize(nullptr);
+ if (!elem_size)
+ return {};
+ if (type_flags & eTypeIsComplex || !(type_flags & eTypeIsFloat)) {
+ LLDB_LOG(m_log,
+ LOG_PREFIX "Unexpected type found in homogeneous aggregate");
+ return {};
+ }
+
+ for (uint32_t i = 0; i < n; i++) {
+ ValueSP val_sp = GetFloatValue(elem_type, i);
+ if (!val_sp)
+ return {};
+
+ // copy to buffer
+ Status error;
+ size_t rc = val_sp->GetScalar().GetAsMemoryData(
+ m_data_up->GetBytes() + m_dst_offs, *elem_size, m_byte_order,
+ error);
+ if (rc != *elem_size) {
+ LLDB_LOG(m_log, LOG_PREFIX "Failed to get float data");
+ return {};
+ }
+ m_dst_offs += *elem_size;
+ }
+ return BuildValueObject();
+ }
+
+ // case 3: get from GPRs
+
+ // first, check if this is a packed struct or not
+ ClangASTContext *ast =
+ llvm::dyn_cast<ClangASTContext>(m_type.GetTypeSystem());
+ if (ast) {
+ clang::RecordDecl *record_decl = ClangASTContext::GetAsRecordDecl(m_type);
+
+ if (record_decl) {
+ auto attrs = record_decl->attrs();
+ for (const auto &attr : attrs) {
+ if (attr->getKind() == clang::attr::Packed) {
+ m_packed = true;
+ break;
+ }
+ }
+ }
+ }
+
+ LLDB_LOG(m_log, LOG_PREFIX "{0} struct",
+ m_packed ? "packed" : "not packed");
+
+ for (uint32_t i = 0; i < n; i++) {
+ std::string name;
+ uint32_t size;
+ GetChildType(i, name, size);
+ // NOTE: the offset returned by GetChildCompilerTypeAtIndex()
+ // can't be used because it never considers alignment bytes
+ // between struct fields.
+ LLDB_LOG(m_log, LOG_PREFIX "field={0}, size={1}", name, size);
+ if (!ExtractField(size))
+ return ValueObjectSP();
+ }
+
+ return BuildValueObject();
+ }
+
+ // extract 'size' bytes at 'offs' from GPRs
+ bool ExtractFromRegs(int32_t offs, uint32_t size, void *buf) {
+ while (size) {
+ auto reg = GetGPRByOffs(offs);
+ if (!reg.IsValid())
+ return false;
+
+ uint32_t n = std::min(reg.Avail(), size);
+ uint64_t raw_data;
+
+ if (!reg.GetRawData(raw_data))
+ return false;
+
+ memcpy(buf, (char *)&raw_data + reg.Offs(), n);
+ offs += n;
+ size -= n;
+ buf = (char *)buf + n;
+ }
+ return true;
+ }
+
+ // extract one field from GPRs and put it in our buffer
+ bool ExtractField(uint32_t size) {
+ auto reg = GetGPRByOffs(m_src_offs);
+ if (!reg.IsValid())
+ return false;
+
+ // handle padding
+ if (!m_packed) {
+ uint32_t n = m_src_offs % size;
+
+ // not 'size' bytes aligned
+ if (n) {
+ LLDB_LOG(m_log,
+ LOG_PREFIX "Extracting {0} alignment bytes at offset {1}", n,
+ m_src_offs);
+ // get alignment bytes
+ if (!ExtractFromRegs(m_src_offs, n, m_data_up->GetBytes() + m_dst_offs))
+ return false;
+ m_src_offs += n;
+ m_dst_offs += n;
+ }
+ }
+
+ // get field
+ LLDB_LOG(m_log, LOG_PREFIX "Extracting {0} field bytes at offset {1}", size,
+ m_src_offs);
+ if (!ExtractFromRegs(m_src_offs, size, m_data_up->GetBytes() + m_dst_offs))
+ return false;
+ m_src_offs += size;
+ m_dst_offs += size;
+ return true;
+ }
+
+ // get child
+ CompilerType GetChildType(uint32_t i, std::string &name, uint32_t &size) {
+ // GetChild constant inputs
+ const bool transparent_pointers = false;
+ const bool omit_empty_base_classes = true;
+ const bool ignore_array_bounds = false;
+ // GetChild output params
+ int32_t child_offs;
+ uint32_t child_bitfield_bit_size;
+ uint32_t child_bitfield_bit_offset;
+ bool child_is_base_class;
+ bool child_is_deref_of_parent;
+ ValueObject *valobj = nullptr;
+ uint64_t language_flags;
+ ExecutionContext exe_ctx;
+ m_thread.CalculateExecutionContext(exe_ctx);
+
+ return m_type.GetChildCompilerTypeAtIndex(
+ &exe_ctx, i, transparent_pointers, omit_empty_base_classes,
+ ignore_array_bounds, name, size, child_offs, child_bitfield_bit_size,
+ child_bitfield_bit_offset, child_is_base_class,
+ child_is_deref_of_parent, valobj, language_flags);
+ }
+};
+
+#undef LOG_PREFIX
+
+} // anonymous namespace
+
+ValueObjectSP
+ABISysV_ppc64::GetReturnValueObjectSimple(Thread &thread,
+ CompilerType &type) const {
+ if (!type)
+ return ValueObjectSP();
+
+ auto exp_extractor = ReturnValueExtractor::Create(thread, type);
+ if (!exp_extractor) {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS);
+ LLDB_LOG_ERROR(log, exp_extractor.takeError(),
+ "Extracting return value failed: {0}");
+ return ValueObjectSP();
+ }
+
+ return exp_extractor.get().GetValue();
+}
+
+ValueObjectSP ABISysV_ppc64::GetReturnValueObjectImpl(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ return GetReturnValueObjectSimple(thread, return_compiler_type);
+}
+
+bool ABISysV_ppc64::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t lr_reg_num;
+ uint32_t sp_reg_num;
+ uint32_t pc_reg_num;
+
+ if (GetByteOrder() == lldb::eByteOrderLittle) {
+ lr_reg_num = ppc64le_dwarf::dwarf_lr_ppc64le;
+ sp_reg_num = ppc64le_dwarf::dwarf_r1_ppc64le;
+ pc_reg_num = ppc64le_dwarf::dwarf_pc_ppc64le;
+ } else {
+ lr_reg_num = ppc64_dwarf::dwarf_lr_ppc64;
+ sp_reg_num = ppc64_dwarf::dwarf_r1_ppc64;
+ pc_reg_num = ppc64_dwarf::dwarf_pc_ppc64;
+ }
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our Call Frame Address is the stack pointer value
+ row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 0);
+
+ // The previous PC is in the LR
+ row->SetRegisterLocationToRegister(pc_reg_num, lr_reg_num, true);
+ unwind_plan.AppendRow(row);
+
+ // All other registers are the same.
+
+ unwind_plan.SetSourceName("ppc64 at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+
+ return true;
+}
+
+bool ABISysV_ppc64::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t sp_reg_num;
+ uint32_t pc_reg_num;
+ uint32_t cr_reg_num;
+
+ if (GetByteOrder() == lldb::eByteOrderLittle) {
+ sp_reg_num = ppc64le_dwarf::dwarf_r1_ppc64le;
+ pc_reg_num = ppc64le_dwarf::dwarf_lr_ppc64le;
+ cr_reg_num = ppc64le_dwarf::dwarf_cr_ppc64le;
+ } else {
+ sp_reg_num = ppc64_dwarf::dwarf_r1_ppc64;
+ pc_reg_num = ppc64_dwarf::dwarf_lr_ppc64;
+ cr_reg_num = ppc64_dwarf::dwarf_cr_ppc64;
+ }
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ const int32_t ptr_size = 8;
+ row->GetCFAValue().SetIsRegisterDereferenced(sp_reg_num);
+
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * 2, true);
+ row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
+ row->SetRegisterLocationToAtCFAPlusOffset(cr_reg_num, ptr_size, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("ppc64 default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ unwind_plan.SetReturnAddressRegister(pc_reg_num);
+ return true;
+}
+
+bool ABISysV_ppc64::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ return !RegisterIsCalleeSaved(reg_info);
+}
+
+// See "Register Usage" in the
+// "System V Application Binary Interface"
+// "64-bit PowerPC ELF Application Binary Interface Supplement" current version
+// is 2 released 2015 at
+// https://members.openpowerfoundation.org/document/dl/576
+bool ABISysV_ppc64::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
+ if (reg_info) {
+ // Preserved registers are :
+ // r1,r2,r13-r31
+ // cr2-cr4 (partially preserved)
+ // f14-f31 (not yet)
+ // v20-v31 (not yet)
+ // vrsave (not yet)
+
+ const char *name = reg_info->name;
+ if (name[0] == 'r') {
+ if ((name[1] == '1' || name[1] == '2') && name[2] == '\0')
+ return true;
+ if (name[1] == '1' && name[2] > '2')
+ return true;
+ if ((name[1] == '2' || name[1] == '3') && name[2] != '\0')
+ return true;
+ }
+
+ if (name[0] == 'f' && name[1] >= '0' && name[2] <= '9') {
+ if (name[2] == '\0')
+ return false;
+ if (name[1] == '1' && name[2] >= '4')
+ return true;
+ if ((name[1] == '2' || name[1] == '3') && name[2] != '\0')
+ return true;
+ }
+
+ if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') // sp
+ return true;
+ if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0') // fp
+ return false;
+ if (name[0] == 'p' && name[1] == 'c' && name[2] == '\0') // pc
+ return true;
+ }
+ return false;
+}
+
+void ABISysV_ppc64::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "System V ABI for ppc64 targets", CreateInstance);
+}
+
+void ABISysV_ppc64::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ABISysV_ppc64::GetPluginNameStatic() {
+ static ConstString g_name("sysv-ppc64");
+ return g_name;
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString ABISysV_ppc64::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ABISysV_ppc64::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc64/ABISysV_ppc64.h b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc64/ABISysV_ppc64.h
new file mode 100644
index 000000000000..d5fb09eec0d0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-ppc64/ABISysV_ppc64.h
@@ -0,0 +1,106 @@
+//===-- ABISysV_ppc64.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 liblldb_ABISysV_ppc64_h_
+#define liblldb_ABISysV_ppc64_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABISysV_ppc64 : public lldb_private::ABI {
+public:
+ ~ABISysV_ppc64() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress,
+ lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &type) const override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ // The SysV ppc64 ABI requires that stack frames be 16 byte aligned.
+ // When there is a trap handler on the stack, e.g. _sigtramp in userland
+ // code, we've seen that the stack pointer is often not aligned properly
+ // before the handler is invoked. This means that lldb will stop the unwind
+ // early -- before the function which caused the trap.
+ //
+ // To work around this, we relax that alignment to be just word-size
+ // (8-bytes).
+ // Whitelisting the trap handlers for user space would be easy (_sigtramp) but
+ // in other environments there can be a large number of different functions
+ // involved in async traps.
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are 8 byte aligned
+ if (cfa & (8ull - 1ull))
+ return false; // Not 8 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ // We have a 64 bit address space, so anything is valid as opcodes
+ // aren't fixed width...
+ return true;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ // PluginInterface protocol
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ void CreateRegisterMapIfNeeded();
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectSimple(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const;
+
+ bool RegisterIsCalleeSaved(const lldb_private::RegisterInfo *reg_info);
+
+private:
+ ABISysV_ppc64(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+
+ lldb::ByteOrder GetByteOrder() const;
+};
+
+#endif // liblldb_ABISysV_ppc64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-s390x/ABISysV_s390x.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-s390x/ABISysV_s390x.cpp
new file mode 100644
index 000000000000..abe847b386a8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-s390x/ABISysV_s390x.cpp
@@ -0,0 +1,749 @@
+//===-- ABISysV_s390x.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 "ABISysV_s390x.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Core/ValueObjectRegister.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum dwarf_regnums {
+ // General Purpose Registers
+ dwarf_r0_s390x = 0,
+ dwarf_r1_s390x,
+ dwarf_r2_s390x,
+ dwarf_r3_s390x,
+ dwarf_r4_s390x,
+ dwarf_r5_s390x,
+ dwarf_r6_s390x,
+ dwarf_r7_s390x,
+ dwarf_r8_s390x,
+ dwarf_r9_s390x,
+ dwarf_r10_s390x,
+ dwarf_r11_s390x,
+ dwarf_r12_s390x,
+ dwarf_r13_s390x,
+ dwarf_r14_s390x,
+ dwarf_r15_s390x,
+ // Floating Point Registers / Vector Registers 0-15
+ dwarf_f0_s390x = 16,
+ dwarf_f2_s390x,
+ dwarf_f4_s390x,
+ dwarf_f6_s390x,
+ dwarf_f1_s390x,
+ dwarf_f3_s390x,
+ dwarf_f5_s390x,
+ dwarf_f7_s390x,
+ dwarf_f8_s390x,
+ dwarf_f10_s390x,
+ dwarf_f12_s390x,
+ dwarf_f14_s390x,
+ dwarf_f9_s390x,
+ dwarf_f11_s390x,
+ dwarf_f13_s390x,
+ dwarf_f15_s390x,
+ // Access Registers
+ dwarf_acr0_s390x = 48,
+ dwarf_acr1_s390x,
+ dwarf_acr2_s390x,
+ dwarf_acr3_s390x,
+ dwarf_acr4_s390x,
+ dwarf_acr5_s390x,
+ dwarf_acr6_s390x,
+ dwarf_acr7_s390x,
+ dwarf_acr8_s390x,
+ dwarf_acr9_s390x,
+ dwarf_acr10_s390x,
+ dwarf_acr11_s390x,
+ dwarf_acr12_s390x,
+ dwarf_acr13_s390x,
+ dwarf_acr14_s390x,
+ dwarf_acr15_s390x,
+ // Program Status Word
+ dwarf_pswm_s390x = 64,
+ dwarf_pswa_s390x,
+ // Vector Registers 16-31
+ dwarf_v16_s390x = 68,
+ dwarf_v18_s390x,
+ dwarf_v20_s390x,
+ dwarf_v22_s390x,
+ dwarf_v17_s390x,
+ dwarf_v19_s390x,
+ dwarf_v21_s390x,
+ dwarf_v23_s390x,
+ dwarf_v24_s390x,
+ dwarf_v26_s390x,
+ dwarf_v28_s390x,
+ dwarf_v30_s390x,
+ dwarf_v25_s390x,
+ dwarf_v27_s390x,
+ dwarf_v29_s390x,
+ dwarf_v31_s390x,
+};
+
+// RegisterKind: EHFrame, DWARF, Generic, Process Plugin, LLDB
+
+#define DEFINE_REG(name, size, alt, generic) \
+ { \
+ #name, alt, size, 0, eEncodingUint, eFormatHex, \
+ {dwarf_##name##_s390x, dwarf_##name##_s390x, generic, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+static RegisterInfo g_register_infos[] = {
+ DEFINE_REG(r0, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(r1, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(r2, 8, "arg1", LLDB_REGNUM_GENERIC_ARG1),
+ DEFINE_REG(r3, 8, "arg2", LLDB_REGNUM_GENERIC_ARG2),
+ DEFINE_REG(r4, 8, "arg3", LLDB_REGNUM_GENERIC_ARG3),
+ DEFINE_REG(r5, 8, "arg4", LLDB_REGNUM_GENERIC_ARG4),
+ DEFINE_REG(r6, 8, "arg5", LLDB_REGNUM_GENERIC_ARG5),
+ DEFINE_REG(r7, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(r8, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(r9, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(r10, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(r11, 8, "fp", LLDB_REGNUM_GENERIC_FP),
+ DEFINE_REG(r12, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(r13, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(r14, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(r15, 8, "sp", LLDB_REGNUM_GENERIC_SP),
+ DEFINE_REG(acr0, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr1, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr2, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr3, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr4, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr5, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr6, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr7, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr8, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr9, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr10, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr11, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr12, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr13, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr14, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(acr15, 4, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(pswm, 8, "flags", LLDB_REGNUM_GENERIC_FLAGS),
+ DEFINE_REG(pswa, 8, "pc", LLDB_REGNUM_GENERIC_PC),
+ DEFINE_REG(f0, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f1, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f2, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f3, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f4, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f5, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f6, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f7, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f8, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f9, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f10, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f11, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f12, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f13, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f14, 8, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_REG(f15, 8, nullptr, LLDB_INVALID_REGNUM),
+};
+
+static const uint32_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos);
+static bool g_register_info_names_constified = false;
+
+const lldb_private::RegisterInfo *
+ABISysV_s390x::GetRegisterInfoArray(uint32_t &count) {
+ // Make the C-string names and alt_names for the register infos into const
+ // C-string values by having the ConstString unique the names in the global
+ // constant C-string pool.
+ if (!g_register_info_names_constified) {
+ g_register_info_names_constified = true;
+ for (uint32_t i = 0; i < k_num_register_infos; ++i) {
+ if (g_register_infos[i].name)
+ g_register_infos[i].name =
+ ConstString(g_register_infos[i].name).GetCString();
+ if (g_register_infos[i].alt_name)
+ g_register_infos[i].alt_name =
+ ConstString(g_register_infos[i].alt_name).GetCString();
+ }
+ }
+ count = k_num_register_infos;
+ return g_register_infos;
+}
+
+size_t ABISysV_s390x::GetRedZoneSize() const { return 0; }
+
+// Static Functions
+
+ABISP
+ABISysV_s390x::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) {
+ if (arch.GetTriple().getArch() == llvm::Triple::systemz) {
+ return ABISP(new ABISysV_s390x(process_sp));
+ }
+ return ABISP();
+}
+
+bool ABISysV_s390x::PrepareTrivialCall(Thread &thread, addr_t sp,
+ addr_t func_addr, addr_t return_addr,
+ llvm::ArrayRef<addr_t> args) const {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ StreamString s;
+ s.Printf("ABISysV_s390x::PrepareTrivialCall (tid = 0x%" PRIx64
+ ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64
+ ", return_addr = 0x%" PRIx64,
+ thread.GetID(), (uint64_t)sp, (uint64_t)func_addr,
+ (uint64_t)return_addr);
+
+ for (size_t i = 0; i < args.size(); ++i)
+ s.Printf(", arg%" PRIu64 " = 0x%" PRIx64, static_cast<uint64_t>(i + 1),
+ args[i]);
+ s.PutCString(")");
+ log->PutString(s.GetString());
+ }
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+
+ const RegisterInfo *pc_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ const RegisterInfo *sp_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+ const RegisterInfo *ra_reg_info = reg_ctx->GetRegisterInfoByName("r14", 0);
+ ProcessSP process_sp(thread.GetProcess());
+
+ // Allocate a new stack frame and space for stack arguments if necessary
+
+ addr_t arg_pos = 0;
+ if (args.size() > 5) {
+ sp -= 8 * (args.size() - 5);
+ arg_pos = sp;
+ }
+
+ sp -= 160;
+
+ // Process arguments
+
+ for (size_t i = 0; i < args.size(); ++i) {
+ if (i < 5) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + i);
+ if (log)
+ log->Printf("About to write arg%" PRIu64 " (0x%" PRIx64 ") into %s",
+ static_cast<uint64_t>(i + 1), args[i], reg_info->name);
+ if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i]))
+ return false;
+ } else {
+ Status error;
+ if (log)
+ log->Printf("About to write arg%" PRIu64 " (0x%" PRIx64 ") onto stack",
+ static_cast<uint64_t>(i + 1), args[i]);
+ if (!process_sp->WritePointerToMemory(arg_pos, args[i], error))
+ return false;
+ arg_pos += 8;
+ }
+ }
+
+ // %r14 is set to the return address
+
+ if (log)
+ log->Printf("Writing RA: 0x%" PRIx64, (uint64_t)return_addr);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(ra_reg_info, return_addr))
+ return false;
+
+ // %r15 is set to the actual stack value.
+
+ if (log)
+ log->Printf("Writing SP: 0x%" PRIx64, (uint64_t)sp);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_info, sp))
+ return false;
+
+ // %pc is set to the address of the called function.
+
+ if (log)
+ log->Printf("Writing PC: 0x%" PRIx64, (uint64_t)func_addr);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_info, func_addr))
+ return false;
+
+ return true;
+}
+
+static bool ReadIntegerArgument(Scalar &scalar, unsigned int bit_width,
+ bool is_signed, Thread &thread,
+ uint32_t *argument_register_ids,
+ unsigned int &current_argument_register,
+ addr_t &current_stack_argument) {
+ if (bit_width > 64)
+ return false; // Scalar can't hold large integer arguments
+
+ if (current_argument_register < 5) {
+ scalar = thread.GetRegisterContext()->ReadRegisterAsUnsigned(
+ argument_register_ids[current_argument_register], 0);
+ current_argument_register++;
+ if (is_signed)
+ scalar.SignExtend(bit_width);
+ } else {
+ uint32_t byte_size = (bit_width + (8 - 1)) / 8;
+ Status error;
+ if (thread.GetProcess()->ReadScalarIntegerFromMemory(
+ current_stack_argument + 8 - byte_size, byte_size, is_signed,
+ scalar, error)) {
+ current_stack_argument += 8;
+ return true;
+ }
+ return false;
+ }
+ return true;
+}
+
+bool ABISysV_s390x::GetArgumentValues(Thread &thread, ValueList &values) const {
+ unsigned int num_values = values.GetSize();
+ unsigned int value_index;
+
+ // Extract the register context so we can read arguments from registers
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+
+ if (!reg_ctx)
+ return false;
+
+ // Get the pointer to the first stack argument so we have a place to start
+ // when reading data
+
+ addr_t sp = reg_ctx->GetSP(0);
+
+ if (!sp)
+ return false;
+
+ addr_t current_stack_argument = sp + 160;
+
+ uint32_t argument_register_ids[5];
+
+ argument_register_ids[0] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[1] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[2] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG3)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[3] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG4)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[4] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG5)
+ ->kinds[eRegisterKindLLDB];
+
+ unsigned int current_argument_register = 0;
+
+ for (value_index = 0; value_index < num_values; ++value_index) {
+ Value *value = values.GetValueAtIndex(value_index);
+
+ if (!value)
+ return false;
+
+ // We currently only support extracting values with Clang QualTypes. Do we
+ // care about others?
+ CompilerType compiler_type = value->GetCompilerType();
+ llvm::Optional<uint64_t> bit_size = compiler_type.GetBitSize(&thread);
+ if (!bit_size)
+ return false;
+ bool is_signed;
+
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed)) {
+ ReadIntegerArgument(value->GetScalar(), *bit_size, is_signed, thread,
+ argument_register_ids, current_argument_register,
+ current_stack_argument);
+ } else if (compiler_type.IsPointerType()) {
+ ReadIntegerArgument(value->GetScalar(), *bit_size, false, thread,
+ argument_register_ids, current_argument_register,
+ current_stack_argument);
+ }
+ }
+
+ return true;
+}
+
+Status ABISysV_s390x::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType compiler_type = new_value_sp->GetCompilerType();
+ if (!compiler_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+
+ bool is_signed;
+ uint32_t count;
+ bool is_complex;
+
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+
+ bool set_it_simple = false;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ compiler_type.IsPointerType()) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName("r2", 0);
+
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+ lldb::offset_t offset = 0;
+ if (num_bytes <= 8) {
+ uint64_t raw_value = data.GetMaxU64(&offset, num_bytes);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(reg_info, raw_value))
+ set_it_simple = true;
+ } else {
+ error.SetErrorString("We don't support returning longer than 64 bit "
+ "integer values at present.");
+ }
+ } else if (compiler_type.IsFloatingPointType(count, is_complex)) {
+ if (is_complex)
+ error.SetErrorString(
+ "We don't support returning complex values at present");
+ else {
+ llvm::Optional<uint64_t> bit_width =
+ compiler_type.GetBitSize(frame_sp.get());
+ if (!bit_width) {
+ error.SetErrorString("can't get type size");
+ return error;
+ }
+ if (*bit_width <= 64) {
+ const RegisterInfo *f0_info = reg_ctx->GetRegisterInfoByName("f0", 0);
+ RegisterValue f0_value;
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+
+ unsigned char buffer[8];
+ ByteOrder byte_order = data.GetByteOrder();
+
+ data.CopyByteOrderedData(0, num_bytes, buffer, 8, byte_order);
+ f0_value.SetBytes(buffer, 8, byte_order);
+ reg_ctx->WriteRegister(f0_info, f0_value);
+ set_it_simple = true;
+ } else {
+ // FIXME - don't know how to do long doubles yet.
+ error.SetErrorString(
+ "We don't support returning float values > 64 bits at present");
+ }
+ }
+ }
+
+ if (!set_it_simple) {
+ // Okay we've got a structure or something that doesn't fit in a simple
+ // register. We should figure out where it really goes, but we don't
+ // support this yet.
+ error.SetErrorString("We only support setting simple integer and float "
+ "return types at present.");
+ }
+
+ return error;
+}
+
+ValueObjectSP ABISysV_s390x::GetReturnValueObjectSimple(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ Value value;
+
+ if (!return_compiler_type)
+ return return_valobj_sp;
+
+ // value.SetContext (Value::eContextTypeClangType, return_value_type);
+ value.SetCompilerType(return_compiler_type);
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return return_valobj_sp;
+
+ const uint32_t type_flags = return_compiler_type.GetTypeInfo();
+ if (type_flags & eTypeIsScalar) {
+ value.SetValueType(Value::eValueTypeScalar);
+
+ bool success = false;
+ if (type_flags & eTypeIsInteger) {
+ // Extract the register context so we can read arguments from registers.
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (!byte_size)
+ return return_valobj_sp;
+ uint64_t raw_value = thread.GetRegisterContext()->ReadRegisterAsUnsigned(
+ reg_ctx->GetRegisterInfoByName("r2", 0), 0);
+ const bool is_signed = (type_flags & eTypeIsSigned) != 0;
+ switch (*byte_size) {
+ default:
+ break;
+
+ case sizeof(uint64_t):
+ if (is_signed)
+ value.GetScalar() = (int64_t)(raw_value);
+ else
+ value.GetScalar() = (uint64_t)(raw_value);
+ success = true;
+ break;
+
+ case sizeof(uint32_t):
+ if (is_signed)
+ value.GetScalar() = (int32_t)(raw_value & UINT32_MAX);
+ else
+ value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint16_t):
+ if (is_signed)
+ value.GetScalar() = (int16_t)(raw_value & UINT16_MAX);
+ else
+ value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint8_t):
+ if (is_signed)
+ value.GetScalar() = (int8_t)(raw_value & UINT8_MAX);
+ else
+ value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX);
+ success = true;
+ break;
+ }
+ } else if (type_flags & eTypeIsFloat) {
+ if (type_flags & eTypeIsComplex) {
+ // Don't handle complex yet.
+ } else {
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (byte_size && *byte_size <= sizeof(long double)) {
+ const RegisterInfo *f0_info = reg_ctx->GetRegisterInfoByName("f0", 0);
+ RegisterValue f0_value;
+ if (reg_ctx->ReadRegister(f0_info, f0_value)) {
+ DataExtractor data;
+ if (f0_value.GetData(data)) {
+ lldb::offset_t offset = 0;
+ if (*byte_size == sizeof(float)) {
+ value.GetScalar() = (float)data.GetFloat(&offset);
+ success = true;
+ } else if (*byte_size == sizeof(double)) {
+ value.GetScalar() = (double)data.GetDouble(&offset);
+ success = true;
+ } else if (*byte_size == sizeof(long double)) {
+ // Don't handle long double yet.
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (success)
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if (type_flags & eTypeIsPointer) {
+ unsigned r2_id =
+ reg_ctx->GetRegisterInfoByName("r2", 0)->kinds[eRegisterKindLLDB];
+ value.GetScalar() =
+ (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(r2_id, 0);
+ value.SetValueType(Value::eValueTypeScalar);
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ }
+
+ return return_valobj_sp;
+}
+
+ValueObjectSP ABISysV_s390x::GetReturnValueObjectImpl(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+
+ if (!return_compiler_type)
+ return return_valobj_sp;
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ return_valobj_sp = GetReturnValueObjectSimple(thread, return_compiler_type);
+ if (return_valobj_sp)
+ return return_valobj_sp;
+
+ RegisterContextSP reg_ctx_sp = thread.GetRegisterContext();
+ if (!reg_ctx_sp)
+ return return_valobj_sp;
+
+ if (return_compiler_type.IsAggregateType()) {
+ // FIXME: This is just taking a guess, r2 may very well no longer hold the
+ // return storage location.
+ // If we are going to do this right, when we make a new frame we should
+ // check to see if it uses a memory return, and if we are at the first
+ // instruction and if so stash away the return location. Then we would
+ // only return the memory return value if we know it is valid.
+
+ unsigned r2_id =
+ reg_ctx_sp->GetRegisterInfoByName("r2", 0)->kinds[eRegisterKindLLDB];
+ lldb::addr_t storage_addr =
+ (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(r2_id, 0);
+ return_valobj_sp = ValueObjectMemory::Create(
+ &thread, "", Address(storage_addr, nullptr), return_compiler_type);
+ }
+
+ return return_valobj_sp;
+}
+
+bool ABISysV_s390x::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our Call Frame Address is the stack pointer value + 160
+ row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_r15_s390x, 160);
+
+ // The previous PC is in r14
+ row->SetRegisterLocationToRegister(dwarf_pswa_s390x, dwarf_r14_s390x, true);
+
+ // All other registers are the same.
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("s390x at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ return true;
+}
+
+bool ABISysV_s390x::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ // There's really no default way to unwind on s390x. Trust the .eh_frame CFI,
+ // which should always be good.
+ return false;
+}
+
+bool ABISysV_s390x::GetFallbackRegisterLocation(
+ const RegisterInfo *reg_info,
+ UnwindPlan::Row::RegisterLocation &unwind_regloc) {
+ // If a volatile register is being requested, we don't want to forward the
+ // next frame's register contents up the stack -- the register is not
+ // retrievable at this frame.
+ if (RegisterIsVolatile(reg_info)) {
+ unwind_regloc.SetUndefined();
+ return true;
+ }
+
+ return false;
+}
+
+bool ABISysV_s390x::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ return !RegisterIsCalleeSaved(reg_info);
+}
+
+bool ABISysV_s390x::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
+ if (reg_info) {
+ // Preserved registers are :
+ // r6-r13, r15
+ // f8-f15
+
+ const char *name = reg_info->name;
+ if (name[0] == 'r') {
+ switch (name[1]) {
+ case '6': // r6
+ case '7': // r7
+ case '8': // r8
+ case '9': // r9
+ return name[2] == '\0';
+
+ case '1': // r10, r11, r12, r13, r15
+ if ((name[2] >= '0' && name[2] <= '3') || name[2] == '5')
+ return name[3] == '\0';
+ break;
+
+ default:
+ break;
+ }
+ }
+ if (name[0] == 'f') {
+ switch (name[1]) {
+ case '8': // r8
+ case '9': // r9
+ return name[2] == '\0';
+
+ case '1': // r10, r11, r12, r13, r14, r15
+ if (name[2] >= '0' && name[2] <= '5')
+ return name[3] == '\0';
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // Accept shorter-variant versions
+ if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') // sp
+ return true;
+ if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0') // fp
+ return true;
+ if (name[0] == 'p' && name[1] == 'c' && name[2] == '\0') // pc
+ return true;
+ }
+ return false;
+}
+
+void ABISysV_s390x::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "System V ABI for s390x targets", CreateInstance);
+}
+
+void ABISysV_s390x::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ABISysV_s390x::GetPluginNameStatic() {
+ static ConstString g_name("sysv-s390x");
+ return g_name;
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString ABISysV_s390x::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ABISysV_s390x::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-s390x/ABISysV_s390x.h b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-s390x/ABISysV_s390x.h
new file mode 100644
index 000000000000..13df477e84bc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-s390x/ABISysV_s390x.h
@@ -0,0 +1,96 @@
+//===-- ABISysV_s390x.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 liblldb_ABISysV_s390x_h_
+#define liblldb_ABISysV_s390x_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABISysV_s390x : public lldb_private::ABI {
+public:
+ ~ABISysV_s390x() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress,
+ lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &type) const override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ bool GetFallbackRegisterLocation(
+ const lldb_private::RegisterInfo *reg_info,
+ lldb_private::UnwindPlan::Row::RegisterLocation &unwind_regloc) override;
+
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are 8 byte aligned
+ if (cfa & (8ull - 1ull))
+ return false; // Not 8 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ // Code addressed must be 2 byte aligned
+ return (pc & 1ull) == 0;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ // PluginInterface protocol
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ void CreateRegisterMapIfNeeded();
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectSimple(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const;
+
+ bool RegisterIsCalleeSaved(const lldb_private::RegisterInfo *reg_info);
+
+private:
+ ABISysV_s390x(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABISysV_s390x_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp
new file mode 100644
index 000000000000..6c7b45f63399
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp
@@ -0,0 +1,1086 @@
+//===-- ABISysV_x86_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
+//
+//===----------------------------------------------------------------------===//
+
+#include "ABISysV_x86_64.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/Triple.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Core/ValueObjectRegister.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum dwarf_regnums {
+ dwarf_rax = 0,
+ dwarf_rdx,
+ dwarf_rcx,
+ dwarf_rbx,
+ dwarf_rsi,
+ dwarf_rdi,
+ dwarf_rbp,
+ dwarf_rsp,
+ dwarf_r8,
+ dwarf_r9,
+ dwarf_r10,
+ dwarf_r11,
+ dwarf_r12,
+ dwarf_r13,
+ dwarf_r14,
+ dwarf_r15,
+ dwarf_rip,
+ dwarf_xmm0,
+ dwarf_xmm1,
+ dwarf_xmm2,
+ dwarf_xmm3,
+ dwarf_xmm4,
+ dwarf_xmm5,
+ dwarf_xmm6,
+ dwarf_xmm7,
+ dwarf_xmm8,
+ dwarf_xmm9,
+ dwarf_xmm10,
+ dwarf_xmm11,
+ dwarf_xmm12,
+ dwarf_xmm13,
+ dwarf_xmm14,
+ dwarf_xmm15,
+ dwarf_stmm0,
+ dwarf_stmm1,
+ dwarf_stmm2,
+ dwarf_stmm3,
+ dwarf_stmm4,
+ dwarf_stmm5,
+ dwarf_stmm6,
+ dwarf_stmm7,
+ dwarf_ymm0,
+ dwarf_ymm1,
+ dwarf_ymm2,
+ dwarf_ymm3,
+ dwarf_ymm4,
+ dwarf_ymm5,
+ dwarf_ymm6,
+ dwarf_ymm7,
+ dwarf_ymm8,
+ dwarf_ymm9,
+ dwarf_ymm10,
+ dwarf_ymm11,
+ dwarf_ymm12,
+ dwarf_ymm13,
+ dwarf_ymm14,
+ dwarf_ymm15,
+ dwarf_bnd0 = 126,
+ dwarf_bnd1,
+ dwarf_bnd2,
+ dwarf_bnd3
+};
+
+static RegisterInfo g_register_infos[] = {
+ // clang-format off
+ // NAME ALT SZ OFF ENCODING FORMAT EH_FRAME DWARF GENERIC LLDB NATIVE
+ // ======== ======= == === ============= =================== ======================= ===================== =========================== ===================== ======================
+ {"rax", nullptr, 8, 0, eEncodingUint, eFormatHex, {dwarf_rax, dwarf_rax, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"rbx", nullptr, 8, 0, eEncodingUint, eFormatHex, {dwarf_rbx, dwarf_rbx, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"rcx", "arg4", 8, 0, eEncodingUint, eFormatHex, {dwarf_rcx, dwarf_rcx, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"rdx", "arg3", 8, 0, eEncodingUint, eFormatHex, {dwarf_rdx, dwarf_rdx, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"rsi", "arg2", 8, 0, eEncodingUint, eFormatHex, {dwarf_rsi, dwarf_rsi, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"rdi", "arg1", 8, 0, eEncodingUint, eFormatHex, {dwarf_rdi, dwarf_rdi, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"rbp", "fp", 8, 0, eEncodingUint, eFormatHex, {dwarf_rbp, dwarf_rbp, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"rsp", "sp", 8, 0, eEncodingUint, eFormatHex, {dwarf_rsp, dwarf_rsp, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"r8", "arg5", 8, 0, eEncodingUint, eFormatHex, {dwarf_r8, dwarf_r8, LLDB_REGNUM_GENERIC_ARG5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"r9", "arg6", 8, 0, eEncodingUint, eFormatHex, {dwarf_r9, dwarf_r9, LLDB_REGNUM_GENERIC_ARG6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"r10", nullptr, 8, 0, eEncodingUint, eFormatHex, {dwarf_r10, dwarf_r10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"r11", nullptr, 8, 0, eEncodingUint, eFormatHex, {dwarf_r11, dwarf_r11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"r12", nullptr, 8, 0, eEncodingUint, eFormatHex, {dwarf_r12, dwarf_r12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"r13", nullptr, 8, 0, eEncodingUint, eFormatHex, {dwarf_r13, dwarf_r13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"r14", nullptr, 8, 0, eEncodingUint, eFormatHex, {dwarf_r14, dwarf_r14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"r15", nullptr, 8, 0, eEncodingUint, eFormatHex, {dwarf_r15, dwarf_r15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"rip", "pc", 8, 0, eEncodingUint, eFormatHex, {dwarf_rip, dwarf_rip, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"rflags", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"cs", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ss", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ds", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"es", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fs", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"gs", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"stmm0", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_stmm0, dwarf_stmm0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"stmm1", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_stmm1, dwarf_stmm1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"stmm2", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_stmm2, dwarf_stmm2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"stmm3", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_stmm3, dwarf_stmm3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"stmm4", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_stmm4, dwarf_stmm4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"stmm5", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_stmm5, dwarf_stmm5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"stmm6", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_stmm6, dwarf_stmm6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"stmm7", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_stmm7, dwarf_stmm7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fctrl", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fstat", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ftag", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fiseg", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fioff", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"foseg", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fooff", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"fop", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm0", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm0, dwarf_xmm0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm1", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm1, dwarf_xmm1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm2", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm2, dwarf_xmm2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm3", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm3, dwarf_xmm3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm4", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm4, dwarf_xmm4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm5", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm5, dwarf_xmm5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm6", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm6, dwarf_xmm6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm7", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm7, dwarf_xmm7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm8", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm8, dwarf_xmm8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm9", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm9, dwarf_xmm9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm10", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm10, dwarf_xmm10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm11", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm11, dwarf_xmm11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm12", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm12, dwarf_xmm12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm13", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm13, dwarf_xmm13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm14", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm14, dwarf_xmm14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"xmm15", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_xmm15, dwarf_xmm15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"mxcsr", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm0", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm0, dwarf_ymm0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm1", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm1, dwarf_ymm1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm2", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm2, dwarf_ymm2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm3", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm3, dwarf_ymm3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm4", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm4, dwarf_ymm4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm5", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm5, dwarf_ymm5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm6", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm6, dwarf_ymm6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm7", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm7, dwarf_ymm7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm8", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm8, dwarf_ymm8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm9", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm9, dwarf_ymm9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm10", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm10, dwarf_ymm10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm11", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm11, dwarf_ymm11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm12", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm12, dwarf_ymm12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm13", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm13, dwarf_ymm13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm14", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm14, dwarf_ymm14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"ymm15", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {dwarf_ymm15, dwarf_ymm15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"bnd0", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt64, {dwarf_bnd0, dwarf_bnd0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"bnd1", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt64, {dwarf_bnd1, dwarf_bnd1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"bnd2", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt64, {dwarf_bnd2, dwarf_bnd2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"bnd3", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt64, {dwarf_bnd3, dwarf_bnd3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"bndcfgu", nullptr, 8, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ {"bndstatus",nullptr, 8, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0},
+ // clang-format on
+};
+
+static const uint32_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos);
+static bool g_register_info_names_constified = false;
+
+const lldb_private::RegisterInfo *
+ABISysV_x86_64::GetRegisterInfoArray(uint32_t &count) {
+ // Make the C-string names and alt_names for the register infos into const
+ // C-string values by having the ConstString unique the names in the global
+ // constant C-string pool.
+ if (!g_register_info_names_constified) {
+ g_register_info_names_constified = true;
+ for (uint32_t i = 0; i < k_num_register_infos; ++i) {
+ if (g_register_infos[i].name)
+ g_register_infos[i].name =
+ ConstString(g_register_infos[i].name).GetCString();
+ if (g_register_infos[i].alt_name)
+ g_register_infos[i].alt_name =
+ ConstString(g_register_infos[i].alt_name).GetCString();
+ }
+ }
+ count = k_num_register_infos;
+ return g_register_infos;
+}
+
+bool ABISysV_x86_64::GetPointerReturnRegister(const char *&name) {
+ name = "rax";
+ return true;
+}
+
+size_t ABISysV_x86_64::GetRedZoneSize() const { return 128; }
+
+// Static Functions
+
+ABISP
+ABISysV_x86_64::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) {
+ const llvm::Triple::ArchType arch_type = arch.GetTriple().getArch();
+ const llvm::Triple::OSType os_type = arch.GetTriple().getOS();
+ if (arch_type == llvm::Triple::x86_64) {
+ switch(os_type) {
+ case llvm::Triple::OSType::MacOSX:
+ case llvm::Triple::OSType::Linux:
+ case llvm::Triple::OSType::FreeBSD:
+ case llvm::Triple::OSType::NetBSD:
+ case llvm::Triple::OSType::Solaris:
+ case llvm::Triple::OSType::UnknownOS:
+ return ABISP(new ABISysV_x86_64(process_sp));
+ default:
+ return ABISP();
+ }
+ }
+ return ABISP();
+}
+
+bool ABISysV_x86_64::PrepareTrivialCall(Thread &thread, addr_t sp,
+ addr_t func_addr, addr_t return_addr,
+ llvm::ArrayRef<addr_t> args) const {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ StreamString s;
+ s.Printf("ABISysV_x86_64::PrepareTrivialCall (tid = 0x%" PRIx64
+ ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64
+ ", return_addr = 0x%" PRIx64,
+ thread.GetID(), (uint64_t)sp, (uint64_t)func_addr,
+ (uint64_t)return_addr);
+
+ for (size_t i = 0; i < args.size(); ++i)
+ s.Printf(", arg%" PRIu64 " = 0x%" PRIx64, static_cast<uint64_t>(i + 1),
+ args[i]);
+ s.PutCString(")");
+ log->PutString(s.GetString());
+ }
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+
+ const RegisterInfo *reg_info = nullptr;
+
+ if (args.size() > 6) // TODO handle more than 6 arguments
+ return false;
+
+ for (size_t i = 0; i < args.size(); ++i) {
+ reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_ARG1 + i);
+ if (log)
+ log->Printf("About to write arg%" PRIu64 " (0x%" PRIx64 ") into %s",
+ static_cast<uint64_t>(i + 1), args[i], reg_info->name);
+ if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i]))
+ return false;
+ }
+
+ // First, align the SP
+
+ if (log)
+ log->Printf("16-byte aligning SP: 0x%" PRIx64 " to 0x%" PRIx64,
+ (uint64_t)sp, (uint64_t)(sp & ~0xfull));
+
+ sp &= ~(0xfull); // 16-byte alignment
+
+ sp -= 8;
+
+ Status error;
+ const RegisterInfo *pc_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ const RegisterInfo *sp_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+ ProcessSP process_sp(thread.GetProcess());
+
+ RegisterValue reg_value;
+ if (log)
+ log->Printf("Pushing the return address onto the stack: 0x%" PRIx64
+ ": 0x%" PRIx64,
+ (uint64_t)sp, (uint64_t)return_addr);
+
+ // Save return address onto the stack
+ if (!process_sp->WritePointerToMemory(sp, return_addr, error))
+ return false;
+
+ // %rsp is set to the actual stack value.
+
+ if (log)
+ log->Printf("Writing SP: 0x%" PRIx64, (uint64_t)sp);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_info, sp))
+ return false;
+
+ // %rip is set to the address of the called function.
+
+ if (log)
+ log->Printf("Writing IP: 0x%" PRIx64, (uint64_t)func_addr);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_info, func_addr))
+ return false;
+
+ return true;
+}
+
+static bool ReadIntegerArgument(Scalar &scalar, unsigned int bit_width,
+ bool is_signed, Thread &thread,
+ uint32_t *argument_register_ids,
+ unsigned int &current_argument_register,
+ addr_t &current_stack_argument) {
+ if (bit_width > 64)
+ return false; // Scalar can't hold large integer arguments
+
+ if (current_argument_register < 6) {
+ scalar = thread.GetRegisterContext()->ReadRegisterAsUnsigned(
+ argument_register_ids[current_argument_register], 0);
+ current_argument_register++;
+ if (is_signed)
+ scalar.SignExtend(bit_width);
+ } else {
+ uint32_t byte_size = (bit_width + (8 - 1)) / 8;
+ Status error;
+ if (thread.GetProcess()->ReadScalarIntegerFromMemory(
+ current_stack_argument, byte_size, is_signed, scalar, error)) {
+ current_stack_argument += byte_size;
+ return true;
+ }
+ return false;
+ }
+ return true;
+}
+
+bool ABISysV_x86_64::GetArgumentValues(Thread &thread,
+ ValueList &values) const {
+ unsigned int num_values = values.GetSize();
+ unsigned int value_index;
+
+ // Extract the register context so we can read arguments from registers
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+
+ if (!reg_ctx)
+ return false;
+
+ // Get the pointer to the first stack argument so we have a place to start
+ // when reading data
+
+ addr_t sp = reg_ctx->GetSP(0);
+
+ if (!sp)
+ return false;
+
+ addr_t current_stack_argument = sp + 8; // jump over return address
+
+ uint32_t argument_register_ids[6];
+
+ argument_register_ids[0] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[1] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[2] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG3)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[3] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG4)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[4] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG5)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[5] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG6)
+ ->kinds[eRegisterKindLLDB];
+
+ unsigned int current_argument_register = 0;
+
+ for (value_index = 0; value_index < num_values; ++value_index) {
+ Value *value = values.GetValueAtIndex(value_index);
+
+ if (!value)
+ return false;
+
+ // We currently only support extracting values with Clang QualTypes. Do we
+ // care about others?
+ CompilerType compiler_type = value->GetCompilerType();
+ llvm::Optional<uint64_t> bit_size = compiler_type.GetBitSize(&thread);
+ if (!bit_size)
+ return false;
+ bool is_signed;
+
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed)) {
+ ReadIntegerArgument(value->GetScalar(), *bit_size, is_signed, thread,
+ argument_register_ids, current_argument_register,
+ current_stack_argument);
+ } else if (compiler_type.IsPointerType()) {
+ ReadIntegerArgument(value->GetScalar(), *bit_size, false, thread,
+ argument_register_ids, current_argument_register,
+ current_stack_argument);
+ }
+ }
+
+ return true;
+}
+
+Status ABISysV_x86_64::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType compiler_type = new_value_sp->GetCompilerType();
+ if (!compiler_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+
+ bool is_signed;
+ uint32_t count;
+ bool is_complex;
+
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+
+ bool set_it_simple = false;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ compiler_type.IsPointerType()) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName("rax", 0);
+
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+ lldb::offset_t offset = 0;
+ if (num_bytes <= 8) {
+ uint64_t raw_value = data.GetMaxU64(&offset, num_bytes);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(reg_info, raw_value))
+ set_it_simple = true;
+ } else {
+ error.SetErrorString("We don't support returning longer than 64 bit "
+ "integer values at present.");
+ }
+ } else if (compiler_type.IsFloatingPointType(count, is_complex)) {
+ if (is_complex)
+ error.SetErrorString(
+ "We don't support returning complex values at present");
+ else {
+ llvm::Optional<uint64_t> bit_width =
+ compiler_type.GetBitSize(frame_sp.get());
+ if (!bit_width) {
+ error.SetErrorString("can't get type size");
+ return error;
+ }
+ if (*bit_width <= 64) {
+ const RegisterInfo *xmm0_info =
+ reg_ctx->GetRegisterInfoByName("xmm0", 0);
+ RegisterValue xmm0_value;
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+
+ unsigned char buffer[16];
+ ByteOrder byte_order = data.GetByteOrder();
+
+ data.CopyByteOrderedData(0, num_bytes, buffer, 16, byte_order);
+ xmm0_value.SetBytes(buffer, 16, byte_order);
+ reg_ctx->WriteRegister(xmm0_info, xmm0_value);
+ set_it_simple = true;
+ } else {
+ // FIXME - don't know how to do 80 bit long doubles yet.
+ error.SetErrorString(
+ "We don't support returning float values > 64 bits at present");
+ }
+ }
+ }
+
+ if (!set_it_simple) {
+ // Okay we've got a structure or something that doesn't fit in a simple
+ // register. We should figure out where it really goes, but we don't
+ // support this yet.
+ error.SetErrorString("We only support setting simple integer and float "
+ "return types at present.");
+ }
+
+ return error;
+}
+
+ValueObjectSP ABISysV_x86_64::GetReturnValueObjectSimple(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ Value value;
+
+ if (!return_compiler_type)
+ return return_valobj_sp;
+
+ // value.SetContext (Value::eContextTypeClangType, return_value_type);
+ value.SetCompilerType(return_compiler_type);
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return return_valobj_sp;
+
+ const uint32_t type_flags = return_compiler_type.GetTypeInfo();
+ if (type_flags & eTypeIsScalar) {
+ value.SetValueType(Value::eValueTypeScalar);
+
+ bool success = false;
+ if (type_flags & eTypeIsInteger) {
+ // Extract the register context so we can read arguments from registers
+
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (!byte_size)
+ return return_valobj_sp;
+ uint64_t raw_value = thread.GetRegisterContext()->ReadRegisterAsUnsigned(
+ reg_ctx->GetRegisterInfoByName("rax", 0), 0);
+ const bool is_signed = (type_flags & eTypeIsSigned) != 0;
+ switch (*byte_size) {
+ default:
+ break;
+
+ case sizeof(uint64_t):
+ if (is_signed)
+ value.GetScalar() = (int64_t)(raw_value);
+ else
+ value.GetScalar() = (uint64_t)(raw_value);
+ success = true;
+ break;
+
+ case sizeof(uint32_t):
+ if (is_signed)
+ value.GetScalar() = (int32_t)(raw_value & UINT32_MAX);
+ else
+ value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint16_t):
+ if (is_signed)
+ value.GetScalar() = (int16_t)(raw_value & UINT16_MAX);
+ else
+ value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint8_t):
+ if (is_signed)
+ value.GetScalar() = (int8_t)(raw_value & UINT8_MAX);
+ else
+ value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX);
+ success = true;
+ break;
+ }
+ } else if (type_flags & eTypeIsFloat) {
+ if (type_flags & eTypeIsComplex) {
+ // Don't handle complex yet.
+ } else {
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (byte_size && *byte_size <= sizeof(long double)) {
+ const RegisterInfo *xmm0_info =
+ reg_ctx->GetRegisterInfoByName("xmm0", 0);
+ RegisterValue xmm0_value;
+ if (reg_ctx->ReadRegister(xmm0_info, xmm0_value)) {
+ DataExtractor data;
+ if (xmm0_value.GetData(data)) {
+ lldb::offset_t offset = 0;
+ if (*byte_size == sizeof(float)) {
+ value.GetScalar() = (float)data.GetFloat(&offset);
+ success = true;
+ } else if (*byte_size == sizeof(double)) {
+ value.GetScalar() = (double)data.GetDouble(&offset);
+ success = true;
+ } else if (*byte_size == sizeof(long double)) {
+ // Don't handle long double since that can be encoded as 80 bit
+ // floats...
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (success)
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if (type_flags & eTypeIsPointer) {
+ unsigned rax_id =
+ reg_ctx->GetRegisterInfoByName("rax", 0)->kinds[eRegisterKindLLDB];
+ value.GetScalar() =
+ (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id,
+ 0);
+ value.SetValueType(Value::eValueTypeScalar);
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if (type_flags & eTypeIsVector) {
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (byte_size && *byte_size > 0) {
+ const RegisterInfo *altivec_reg =
+ reg_ctx->GetRegisterInfoByName("xmm0", 0);
+ if (altivec_reg == nullptr)
+ altivec_reg = reg_ctx->GetRegisterInfoByName("mm0", 0);
+
+ if (altivec_reg) {
+ if (*byte_size <= altivec_reg->byte_size) {
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp) {
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order = process_sp->GetByteOrder();
+ RegisterValue reg_value;
+ if (reg_ctx->ReadRegister(altivec_reg, reg_value)) {
+ Status error;
+ if (reg_value.GetAsMemoryData(
+ altivec_reg, heap_data_up->GetBytes(),
+ heap_data_up->GetByteSize(), byte_order, error)) {
+ DataExtractor data(DataBufferSP(heap_data_up.release()),
+ byte_order,
+ process_sp->GetTarget()
+ .GetArchitecture()
+ .GetAddressByteSize());
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), data);
+ }
+ }
+ }
+ } else if (*byte_size <= altivec_reg->byte_size * 2) {
+ const RegisterInfo *altivec_reg2 =
+ reg_ctx->GetRegisterInfoByName("xmm1", 0);
+ if (altivec_reg2) {
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp) {
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order = process_sp->GetByteOrder();
+ RegisterValue reg_value;
+ RegisterValue reg_value2;
+ if (reg_ctx->ReadRegister(altivec_reg, reg_value) &&
+ reg_ctx->ReadRegister(altivec_reg2, reg_value2)) {
+
+ Status error;
+ if (reg_value.GetAsMemoryData(
+ altivec_reg, heap_data_up->GetBytes(),
+ altivec_reg->byte_size, byte_order, error) &&
+ reg_value2.GetAsMemoryData(
+ altivec_reg2,
+ heap_data_up->GetBytes() + altivec_reg->byte_size,
+ heap_data_up->GetByteSize() - altivec_reg->byte_size,
+ byte_order, error)) {
+ DataExtractor data(DataBufferSP(heap_data_up.release()),
+ byte_order,
+ process_sp->GetTarget()
+ .GetArchitecture()
+ .GetAddressByteSize());
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), data);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return return_valobj_sp;
+}
+
+// The compiler will flatten the nested aggregate type into single
+// layer and push the value to stack
+// This helper function will flatten an aggregate type
+// and return true if it can be returned in register(s) by value
+// return false if the aggregate is in memory
+static bool FlattenAggregateType(
+ Thread &thread, ExecutionContext &exe_ctx,
+ CompilerType &return_compiler_type,
+ uint32_t data_byte_offset,
+ std::vector<uint32_t> &aggregate_field_offsets,
+ std::vector<CompilerType> &aggregate_compiler_types) {
+
+ const uint32_t num_children = return_compiler_type.GetNumFields();
+ for (uint32_t idx = 0; idx < num_children; ++idx) {
+ std::string name;
+ bool is_signed;
+ uint32_t count;
+ bool is_complex;
+
+ uint64_t field_bit_offset = 0;
+ CompilerType field_compiler_type = return_compiler_type.GetFieldAtIndex(
+ idx, name, &field_bit_offset, nullptr, nullptr);
+ llvm::Optional<uint64_t> field_bit_width =
+ field_compiler_type.GetBitSize(&thread);
+
+ // if we don't know the size of the field (e.g. invalid type), exit
+ if (!field_bit_width || *field_bit_width == 0) {
+ return false;
+ }
+
+ uint32_t field_byte_offset = field_bit_offset / 8 + data_byte_offset;
+
+ const uint32_t field_type_flags = field_compiler_type.GetTypeInfo();
+ if (field_compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ field_compiler_type.IsPointerType() ||
+ field_compiler_type.IsFloatingPointType(count, is_complex)) {
+ aggregate_field_offsets.push_back(field_byte_offset);
+ aggregate_compiler_types.push_back(field_compiler_type);
+ } else if (field_type_flags & eTypeHasChildren) {
+ if (!FlattenAggregateType(thread, exe_ctx, field_compiler_type,
+ field_byte_offset, aggregate_field_offsets,
+ aggregate_compiler_types)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+ValueObjectSP ABISysV_x86_64::GetReturnValueObjectImpl(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+
+ if (!return_compiler_type)
+ return return_valobj_sp;
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ return_valobj_sp = GetReturnValueObjectSimple(thread, return_compiler_type);
+ if (return_valobj_sp)
+ return return_valobj_sp;
+
+ RegisterContextSP reg_ctx_sp = thread.GetRegisterContext();
+ if (!reg_ctx_sp)
+ return return_valobj_sp;
+
+ llvm::Optional<uint64_t> bit_width = return_compiler_type.GetBitSize(&thread);
+ if (!bit_width)
+ return return_valobj_sp;
+ if (return_compiler_type.IsAggregateType()) {
+ Target *target = exe_ctx.GetTargetPtr();
+ bool is_memory = true;
+ std::vector<uint32_t> aggregate_field_offsets;
+ std::vector<CompilerType> aggregate_compiler_types;
+ if (return_compiler_type.GetTypeSystem()->CanPassInRegisters(
+ return_compiler_type) &&
+ *bit_width <= 128 &&
+ FlattenAggregateType(thread, exe_ctx, return_compiler_type,
+ 0, aggregate_field_offsets,
+ aggregate_compiler_types)) {
+ ByteOrder byte_order = target->GetArchitecture().GetByteOrder();
+ DataBufferSP data_sp(new DataBufferHeap(16, 0));
+ DataExtractor return_ext(data_sp, byte_order,
+ target->GetArchitecture().GetAddressByteSize());
+
+ const RegisterInfo *rax_info =
+ reg_ctx_sp->GetRegisterInfoByName("rax", 0);
+ const RegisterInfo *rdx_info =
+ reg_ctx_sp->GetRegisterInfoByName("rdx", 0);
+ const RegisterInfo *xmm0_info =
+ reg_ctx_sp->GetRegisterInfoByName("xmm0", 0);
+ const RegisterInfo *xmm1_info =
+ reg_ctx_sp->GetRegisterInfoByName("xmm1", 0);
+
+ RegisterValue rax_value, rdx_value, xmm0_value, xmm1_value;
+ reg_ctx_sp->ReadRegister(rax_info, rax_value);
+ reg_ctx_sp->ReadRegister(rdx_info, rdx_value);
+ reg_ctx_sp->ReadRegister(xmm0_info, xmm0_value);
+ reg_ctx_sp->ReadRegister(xmm1_info, xmm1_value);
+
+ DataExtractor rax_data, rdx_data, xmm0_data, xmm1_data;
+
+ rax_value.GetData(rax_data);
+ rdx_value.GetData(rdx_data);
+ xmm0_value.GetData(xmm0_data);
+ xmm1_value.GetData(xmm1_data);
+
+ uint32_t fp_bytes =
+ 0; // Tracks how much of the xmm registers we've consumed so far
+ uint32_t integer_bytes =
+ 0; // Tracks how much of the rax/rds registers we've consumed so far
+
+ // in case of the returned type is a subclass of non-abstract-base class
+ // it will have a padding to skip the base content
+ if (aggregate_field_offsets.size()) {
+ fp_bytes = aggregate_field_offsets[0];
+ integer_bytes = aggregate_field_offsets[0];
+ }
+
+ const uint32_t num_children = aggregate_compiler_types.size();
+
+ // Since we are in the small struct regime, assume we are not in memory.
+ is_memory = false;
+ for (uint32_t idx = 0; idx < num_children; idx++) {
+ bool is_signed;
+ uint32_t count;
+ bool is_complex;
+
+ CompilerType field_compiler_type = aggregate_compiler_types[idx];
+ uint32_t field_byte_width = (uint32_t) (*field_compiler_type.GetByteSize(&thread));
+ uint32_t field_byte_offset = aggregate_field_offsets[idx];
+
+ uint32_t field_bit_width = field_byte_width * 8;
+
+ DataExtractor *copy_from_extractor = nullptr;
+ uint32_t copy_from_offset = 0;
+
+ if (field_compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ field_compiler_type.IsPointerType()) {
+ if (integer_bytes < 8) {
+ if (integer_bytes + field_byte_width <= 8) {
+ // This is in RAX, copy from register to our result structure:
+ copy_from_extractor = &rax_data;
+ copy_from_offset = integer_bytes;
+ integer_bytes += field_byte_width;
+ } else {
+ // The next field wouldn't fit in the remaining space, so we
+ // pushed it to rdx.
+ copy_from_extractor = &rdx_data;
+ copy_from_offset = 0;
+ integer_bytes = 8 + field_byte_width;
+ }
+ } else if (integer_bytes + field_byte_width <= 16) {
+ copy_from_extractor = &rdx_data;
+ copy_from_offset = integer_bytes - 8;
+ integer_bytes += field_byte_width;
+ } else {
+ // The last field didn't fit. I can't see how that would happen
+ // w/o the overall size being greater than 16 bytes. For now,
+ // return a nullptr return value object.
+ return return_valobj_sp;
+ }
+ } else if (field_compiler_type.IsFloatingPointType(count, is_complex)) {
+ // Structs with long doubles are always passed in memory.
+ if (field_bit_width == 128) {
+ is_memory = true;
+ break;
+ } else if (field_bit_width == 64) {
+ // These have to be in a single xmm register.
+ if (fp_bytes == 0)
+ copy_from_extractor = &xmm0_data;
+ else
+ copy_from_extractor = &xmm1_data;
+
+ copy_from_offset = 0;
+ fp_bytes += field_byte_width;
+ } else if (field_bit_width == 32) {
+ // This one is kind of complicated. If we are in an "eightbyte"
+ // with another float, we'll be stuffed into an xmm register with
+ // it. If we are in an "eightbyte" with one or more ints, then we
+ // will be stuffed into the appropriate GPR with them.
+ bool in_gpr;
+ if (field_byte_offset % 8 == 0) {
+ // We are at the beginning of one of the eightbytes, so check the
+ // next element (if any)
+ if (idx == num_children - 1) {
+ in_gpr = false;
+ } else {
+ CompilerType next_field_compiler_type =
+ aggregate_compiler_types[idx + 1];
+ if (next_field_compiler_type.IsIntegerOrEnumerationType(
+ is_signed)) {
+ in_gpr = true;
+ } else {
+ copy_from_offset = 0;
+ in_gpr = false;
+ }
+ }
+ } else if (field_byte_offset % 4 == 0) {
+ // We are inside of an eightbyte, so see if the field before us
+ // is floating point: This could happen if somebody put padding
+ // in the structure.
+ if (idx == 0) {
+ in_gpr = false;
+ } else {
+ CompilerType prev_field_compiler_type =
+ aggregate_compiler_types[idx - 1];
+ if (prev_field_compiler_type.IsIntegerOrEnumerationType(
+ is_signed)) {
+ in_gpr = true;
+ } else {
+ copy_from_offset = 4;
+ in_gpr = false;
+ }
+ }
+ } else {
+ is_memory = true;
+ continue;
+ }
+
+ // Okay, we've figured out whether we are in GPR or XMM, now figure
+ // out which one.
+ if (in_gpr) {
+ if (integer_bytes < 8) {
+ // This is in RAX, copy from register to our result structure:
+ copy_from_extractor = &rax_data;
+ copy_from_offset = integer_bytes;
+ integer_bytes += field_byte_width;
+ } else {
+ copy_from_extractor = &rdx_data;
+ copy_from_offset = integer_bytes - 8;
+ integer_bytes += field_byte_width;
+ }
+ } else {
+ if (fp_bytes < 8)
+ copy_from_extractor = &xmm0_data;
+ else
+ copy_from_extractor = &xmm1_data;
+
+ fp_bytes += field_byte_width;
+ }
+ }
+ }
+ // These two tests are just sanity checks. If I somehow get the type
+ // calculation wrong above it is better to just return nothing than to
+ // assert or crash.
+ if (!copy_from_extractor)
+ return return_valobj_sp;
+ if (copy_from_offset + field_byte_width >
+ copy_from_extractor->GetByteSize())
+ return return_valobj_sp;
+ copy_from_extractor->CopyByteOrderedData(
+ copy_from_offset, field_byte_width,
+ data_sp->GetBytes() + field_byte_offset, field_byte_width,
+ byte_order);
+ }
+ if (!is_memory) {
+ // The result is in our data buffer. Let's make a variable object out
+ // of it:
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), return_ext);
+ }
+ }
+
+ // FIXME: This is just taking a guess, rax may very well no longer hold the
+ // return storage location.
+ // If we are going to do this right, when we make a new frame we should
+ // check to see if it uses a memory return, and if we are at the first
+ // instruction and if so stash away the return location. Then we would
+ // only return the memory return value if we know it is valid.
+
+ if (is_memory) {
+ unsigned rax_id =
+ reg_ctx_sp->GetRegisterInfoByName("rax", 0)->kinds[eRegisterKindLLDB];
+ lldb::addr_t storage_addr =
+ (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id,
+ 0);
+ return_valobj_sp = ValueObjectMemory::Create(
+ &thread, "", Address(storage_addr, nullptr), return_compiler_type);
+ }
+ }
+
+ return return_valobj_sp;
+}
+
+// This defines the CFA as rsp+8
+// the saved pc is at CFA-8 (i.e. rsp+0)
+// The saved rsp is CFA+0
+
+bool ABISysV_x86_64::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t sp_reg_num = dwarf_rsp;
+ uint32_t pc_reg_num = dwarf_rip;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 8);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -8, false);
+ row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("x86_64 at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ return true;
+}
+
+// This defines the CFA as rbp+16
+// The saved pc is at CFA-8 (i.e. rbp+8)
+// The saved rbp is at CFA-16 (i.e. rbp+0)
+// The saved rsp is CFA+0
+
+bool ABISysV_x86_64::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t fp_reg_num = dwarf_rbp;
+ uint32_t sp_reg_num = dwarf_rsp;
+ uint32_t pc_reg_num = dwarf_rip;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ const int32_t ptr_size = 8;
+ row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_rbp, 2 * ptr_size);
+ row->SetOffset(0);
+
+ row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true);
+ row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("x86_64 default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ return true;
+}
+
+bool ABISysV_x86_64::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ return !RegisterIsCalleeSaved(reg_info);
+}
+
+// See "Register Usage" in the
+// "System V Application Binary Interface"
+// "AMD64 Architecture Processor Supplement" (or "x86-64(tm) Architecture
+// Processor Supplement" in earlier revisions) (this doc is also commonly
+// referred to as the x86-64/AMD64 psABI) Edited by Michael Matz, Jan Hubicka,
+// Andreas Jaeger, and Mark Mitchell current version is 0.99.6 released
+// 2012-07-02 at http://refspecs.linuxfoundation.org/elf/x86-64-abi-0.99.pdf
+// It's being revised & updated at https://github.com/hjl-tools/x86-psABI/
+
+bool ABISysV_x86_64::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
+ if (!reg_info)
+ return false;
+ assert(reg_info->name != nullptr && "unnamed register?");
+ std::string Name = std::string(reg_info->name);
+ bool IsCalleeSaved =
+ llvm::StringSwitch<bool>(Name)
+ .Cases("r12", "r13", "r14", "r15", "rbp", "ebp", "rbx", "ebx", true)
+ .Cases("rip", "eip", "rsp", "esp", "sp", "fp", "pc", true)
+ .Default(false);
+ return IsCalleeSaved;
+}
+
+void ABISysV_x86_64::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "System V ABI for x86_64 targets", CreateInstance);
+}
+
+void ABISysV_x86_64::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ABISysV_x86_64::GetPluginNameStatic() {
+ static ConstString g_name("sysv-x86_64");
+ return g_name;
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString ABISysV_x86_64::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ABISysV_x86_64::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h
new file mode 100644
index 000000000000..f6704aff348c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h
@@ -0,0 +1,106 @@
+//===-- ABISysV_x86_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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ABISysV_x86_64_h_
+#define liblldb_ABISysV_x86_64_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABISysV_x86_64 : public lldb_private::ABI {
+public:
+ ~ABISysV_x86_64() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress,
+ lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &type) const override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ // The SysV x86_64 ABI requires that stack frames be 16 byte aligned.
+ // When there is a trap handler on the stack, e.g. _sigtramp in userland
+ // code, we've seen that the stack pointer is often not aligned properly
+ // before the handler is invoked. This means that lldb will stop the unwind
+ // early -- before the function which caused the trap.
+ //
+ // To work around this, we relax that alignment to be just word-size
+ // (8-bytes).
+ // Whitelisting the trap handlers for user space would be easy (_sigtramp) but
+ // in other environments there can be a large number of different functions
+ // involved in async traps.
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ // Make sure the stack call frame addresses are 8 byte aligned
+ if (cfa & (8ull - 1ull))
+ return false; // Not 8 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ // We have a 64 bit address space, so anything is valid as opcodes
+ // aren't fixed width...
+ return true;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ bool GetPointerReturnRegister(const char *&name) override;
+
+ // Static Functions
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ // PluginInterface protocol
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ void CreateRegisterMapIfNeeded();
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectSimple(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const;
+
+ bool RegisterIsCalleeSaved(const lldb_private::RegisterInfo *reg_info);
+
+private:
+ ABISysV_x86_64(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABISysV_x86_64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/Windows-x86_64/ABIWindows_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/ABI/Windows-x86_64/ABIWindows_x86_64.cpp
new file mode 100644
index 000000000000..5dc7717d865d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/Windows-x86_64/ABIWindows_x86_64.cpp
@@ -0,0 +1,1805 @@
+//===-- ABIWindows_x86_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
+//
+//===----------------------------------------------------------------------===//
+
+#include "ABIWindows_x86_64.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/Triple.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Core/ValueObjectRegister.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum dwarf_regnums {
+ dwarf_rax = 0,
+ dwarf_rdx,
+ dwarf_rcx,
+ dwarf_rbx,
+ dwarf_rsi,
+ dwarf_rdi,
+ dwarf_rbp,
+ dwarf_rsp,
+ dwarf_r8,
+ dwarf_r9,
+ dwarf_r10,
+ dwarf_r11,
+ dwarf_r12,
+ dwarf_r13,
+ dwarf_r14,
+ dwarf_r15,
+ dwarf_rip,
+ dwarf_xmm0,
+ dwarf_xmm1,
+ dwarf_xmm2,
+ dwarf_xmm3,
+ dwarf_xmm4,
+ dwarf_xmm5,
+ dwarf_xmm6,
+ dwarf_xmm7,
+ dwarf_xmm8,
+ dwarf_xmm9,
+ dwarf_xmm10,
+ dwarf_xmm11,
+ dwarf_xmm12,
+ dwarf_xmm13,
+ dwarf_xmm14,
+ dwarf_xmm15,
+ dwarf_stmm0,
+ dwarf_stmm1,
+ dwarf_stmm2,
+ dwarf_stmm3,
+ dwarf_stmm4,
+ dwarf_stmm5,
+ dwarf_stmm6,
+ dwarf_stmm7,
+ dwarf_ymm0,
+ dwarf_ymm1,
+ dwarf_ymm2,
+ dwarf_ymm3,
+ dwarf_ymm4,
+ dwarf_ymm5,
+ dwarf_ymm6,
+ dwarf_ymm7,
+ dwarf_ymm8,
+ dwarf_ymm9,
+ dwarf_ymm10,
+ dwarf_ymm11,
+ dwarf_ymm12,
+ dwarf_ymm13,
+ dwarf_ymm14,
+ dwarf_ymm15,
+ dwarf_bnd0 = 126,
+ dwarf_bnd1,
+ dwarf_bnd2,
+ dwarf_bnd3
+};
+
+static RegisterInfo g_register_infos[] = {
+ // NAME ALT SZ OFF ENCODING FORMAT EH_FRAME
+ // DWARF GENERIC PROCESS PLUGIN
+ // LLDB NATIVE
+ // ======== ======= == === ============= ===================
+ // ======================= =====================
+ // =========================== ===================== ======================
+ {"rax",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_rax, dwarf_rax, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"rbx",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_rbx, dwarf_rbx, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"rcx",
+ "arg1",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_rcx, dwarf_rcx, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"rdx",
+ "arg2",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_rdx, dwarf_rdx, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"rsi",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_rsi, dwarf_rsi, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"rdi",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_rdi, dwarf_rdi, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"rbp",
+ "fp",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_rbp, dwarf_rbp, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"rsp",
+ "sp",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_rsp, dwarf_rsp, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r8",
+ "arg3",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r8, dwarf_r8, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r9",
+ "arg4",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r9, dwarf_r9, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r10",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r10, dwarf_r10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r11",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r11, dwarf_r11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r12",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r12, dwarf_r12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r13",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r13, dwarf_r13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r14",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r14, dwarf_r14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r15",
+ nullptr,
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_r15, dwarf_r15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"rip",
+ "pc",
+ 8,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {dwarf_rip, dwarf_rip, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"rflags",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_FLAGS,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cs",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ss",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ds",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"es",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fs",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"gs",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm0",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_stmm0, dwarf_stmm0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm1",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_stmm1, dwarf_stmm1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm2",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_stmm2, dwarf_stmm2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm3",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_stmm3, dwarf_stmm3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm4",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_stmm4, dwarf_stmm4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm5",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_stmm5, dwarf_stmm5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm6",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_stmm6, dwarf_stmm6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"stmm7",
+ nullptr,
+ 10,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_stmm7, dwarf_stmm7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fctrl",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fstat",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ftag",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fiseg",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fioff",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"foseg",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fooff",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fop",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm0",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm0, dwarf_xmm0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm1",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm1, dwarf_xmm1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm2",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm2, dwarf_xmm2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm3",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm3, dwarf_xmm3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm4",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm4, dwarf_xmm4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm5",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm5, dwarf_xmm5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm6",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm6, dwarf_xmm6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm7",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm7, dwarf_xmm7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm8",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm8, dwarf_xmm8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm9",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm9, dwarf_xmm9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm10",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm10, dwarf_xmm10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm11",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm11, dwarf_xmm11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm12",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm12, dwarf_xmm12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm13",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm13, dwarf_xmm13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm14",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm14, dwarf_xmm14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"xmm15",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_xmm15, dwarf_xmm15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"mxcsr",
+ nullptr,
+ 4,
+ 0,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm0",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm0, dwarf_ymm0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm1",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm1, dwarf_ymm1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm2",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm2, dwarf_ymm2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm3",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm3, dwarf_ymm3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm4",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm4, dwarf_ymm4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm5",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm5, dwarf_ymm5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm6",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm6, dwarf_ymm6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm7",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm7, dwarf_ymm7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm8",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm8, dwarf_ymm8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm9",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm9, dwarf_ymm9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm10",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm10, dwarf_ymm10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm11",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm11, dwarf_ymm11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm12",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm12, dwarf_ymm12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm13",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm13, dwarf_ymm13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm14",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm14, dwarf_ymm14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"ymm15",
+ nullptr,
+ 32,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {dwarf_ymm15, dwarf_ymm15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"bnd0",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt64,
+ {dwarf_bnd0, dwarf_bnd0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"bnd1",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt64,
+ {dwarf_bnd1, dwarf_bnd1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"bnd2",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt64,
+ {dwarf_bnd2, dwarf_bnd2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"bnd3",
+ nullptr,
+ 16,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt64,
+ {dwarf_bnd3, dwarf_bnd3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"bndcfgu",
+ nullptr,
+ 8,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"bndstatus",
+ nullptr,
+ 8,
+ 0,
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0}};
+
+static const uint32_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos);
+static bool g_register_info_names_constified = false;
+
+const lldb_private::RegisterInfo *
+ABIWindows_x86_64::GetRegisterInfoArray(uint32_t &count) {
+ // Make the C-string names and alt_names for the register infos into const
+ // C-string values by having the ConstString unique the names in the global
+ // constant C-string pool.
+ if (!g_register_info_names_constified) {
+ g_register_info_names_constified = true;
+ for (uint32_t i = 0; i < k_num_register_infos; ++i) {
+ if (g_register_infos[i].name)
+ g_register_infos[i].name =
+ ConstString(g_register_infos[i].name).GetCString();
+ if (g_register_infos[i].alt_name)
+ g_register_infos[i].alt_name =
+ ConstString(g_register_infos[i].alt_name).GetCString();
+ }
+ }
+ count = k_num_register_infos;
+ return g_register_infos;
+}
+
+bool ABIWindows_x86_64::GetPointerReturnRegister(const char *&name) {
+ name = "rax";
+ return true;
+}
+
+size_t ABIWindows_x86_64::GetRedZoneSize() const { return 0; }
+
+//------------------------------------------------------------------
+// Static Functions
+//------------------------------------------------------------------
+
+ABISP
+ABIWindows_x86_64::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) {
+ if (arch.GetTriple().getArch() == llvm::Triple::x86_64 &&
+ arch.GetTriple().isOSWindows()) {
+ return ABISP(new ABIWindows_x86_64(process_sp));
+ }
+ return ABISP();
+}
+
+bool ABIWindows_x86_64::PrepareTrivialCall(Thread &thread, addr_t sp,
+ addr_t func_addr, addr_t return_addr,
+ llvm::ArrayRef<addr_t> args) const {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ StreamString s;
+ s.Printf("ABIWindows_x86_64::PrepareTrivialCall (tid = 0x%" PRIx64
+ ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64
+ ", return_addr = 0x%" PRIx64,
+ thread.GetID(), (uint64_t)sp, (uint64_t)func_addr,
+ (uint64_t)return_addr);
+
+ for (size_t i = 0; i < args.size(); ++i)
+ s.Printf(", arg%" PRIu64 " = 0x%" PRIx64, static_cast<uint64_t>(i + 1),
+ args[i]);
+ s.PutCString(")");
+ log->PutString(s.GetString());
+ }
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return false;
+
+ const RegisterInfo *reg_info = nullptr;
+
+ if (args.size() > 4) // Windows x64 only put first 4 arguments into registers
+ return false;
+
+ for (size_t i = 0; i < args.size(); ++i) {
+ reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_ARG1 + i);
+ if (log)
+ log->Printf("About to write arg%" PRIu64 " (0x%" PRIx64 ") into %s",
+ static_cast<uint64_t>(i + 1), args[i], reg_info->name);
+ if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i]))
+ return false;
+ }
+
+ // First, align the SP
+
+ if (log)
+ log->Printf("16-byte aligning SP: 0x%" PRIx64 " to 0x%" PRIx64,
+ (uint64_t)sp, (uint64_t)(sp & ~0xfull));
+
+ sp &= ~(0xfull); // 16-byte alignment
+
+ sp -= 8; // return address
+
+ Status error;
+ const RegisterInfo *pc_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ const RegisterInfo *sp_reg_info =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+ ProcessSP process_sp(thread.GetProcess());
+
+ RegisterValue reg_value;
+ if (log)
+ log->Printf("Pushing the return address onto the stack: 0x%" PRIx64
+ ": 0x%" PRIx64,
+ (uint64_t)sp, (uint64_t)return_addr);
+
+ // Save return address onto the stack
+ if (!process_sp->WritePointerToMemory(sp, return_addr, error))
+ return false;
+
+ // %rsp is set to the actual stack value.
+
+ if (log)
+ log->Printf("Writing SP: 0x%" PRIx64, (uint64_t)sp);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_info, sp))
+ return false;
+
+ // %rip is set to the address of the called function.
+
+ if (log)
+ log->Printf("Writing IP: 0x%" PRIx64, (uint64_t)func_addr);
+
+ if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_info, func_addr))
+ return false;
+
+ return true;
+}
+
+static bool ReadIntegerArgument(Scalar &scalar, unsigned int bit_width,
+ bool is_signed, Thread &thread,
+ uint32_t *argument_register_ids,
+ unsigned int &current_argument_register,
+ addr_t &current_stack_argument) {
+ if (bit_width > 64)
+ return false; // Scalar can't hold large integer arguments
+
+ if (current_argument_register < 4) { // Windows pass first 4 arguments to register
+ scalar = thread.GetRegisterContext()->ReadRegisterAsUnsigned(
+ argument_register_ids[current_argument_register], 0);
+ current_argument_register++;
+ if (is_signed)
+ scalar.SignExtend(bit_width);
+ return true;
+ }
+ uint32_t byte_size = (bit_width + (CHAR_BIT - 1)) / CHAR_BIT;
+ Status error;
+ if (thread.GetProcess()->ReadScalarIntegerFromMemory(
+ current_stack_argument, byte_size, is_signed, scalar, error)) {
+ current_stack_argument += byte_size;
+ return true;
+ }
+ return false;
+}
+
+bool ABIWindows_x86_64::GetArgumentValues(Thread &thread,
+ ValueList &values) const {
+ unsigned int num_values = values.GetSize();
+ unsigned int value_index;
+
+ // Extract the register context so we can read arguments from registers
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+
+ if (!reg_ctx)
+ return false;
+
+ // Get the pointer to the first stack argument so we have a place to start
+ // when reading data
+
+ addr_t sp = reg_ctx->GetSP(0);
+
+ if (!sp)
+ return false;
+
+ addr_t current_stack_argument = sp + 8; // jump over return address
+
+ uint32_t argument_register_ids[4];
+
+ argument_register_ids[0] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[1] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[2] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG3)
+ ->kinds[eRegisterKindLLDB];
+ argument_register_ids[3] =
+ reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG4)
+ ->kinds[eRegisterKindLLDB];
+
+ unsigned int current_argument_register = 0;
+
+ for (value_index = 0; value_index < num_values; ++value_index) {
+ Value *value = values.GetValueAtIndex(value_index);
+
+ if (!value)
+ return false;
+
+ CompilerType compiler_type = value->GetCompilerType();
+ llvm::Optional<uint64_t> bit_size = compiler_type.GetBitSize(&thread);
+ if (!bit_size)
+ return false;
+ bool is_signed;
+
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed)) {
+ ReadIntegerArgument(value->GetScalar(), *bit_size, is_signed, thread,
+ argument_register_ids, current_argument_register,
+ current_stack_argument);
+ } else if (compiler_type.IsPointerType()) {
+ ReadIntegerArgument(value->GetScalar(), *bit_size, false, thread,
+ argument_register_ids, current_argument_register,
+ current_stack_argument);
+ }
+ }
+
+ return true;
+}
+
+Status ABIWindows_x86_64::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value_sp) {
+ Status error;
+ if (!new_value_sp) {
+ error.SetErrorString("Empty value object for return value.");
+ return error;
+ }
+
+ CompilerType compiler_type = new_value_sp->GetCompilerType();
+ if (!compiler_type) {
+ error.SetErrorString("Null clang type for return value.");
+ return error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+
+ bool is_signed;
+ uint32_t count;
+ bool is_complex;
+
+ RegisterContext *reg_ctx = thread->GetRegisterContext().get();
+
+ bool set_it_simple = false;
+ if (compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ compiler_type.IsPointerType()) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName("rax", 0);
+
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+ lldb::offset_t offset = 0;
+ if (num_bytes <= 8) {
+ uint64_t raw_value = data.GetMaxU64(&offset, num_bytes);
+
+ if (reg_ctx->WriteRegisterFromUnsigned(reg_info, raw_value))
+ set_it_simple = true;
+ } else {
+ error.SetErrorString("We don't support returning longer than 64 bit "
+ "integer values at present.");
+ }
+ } else if (compiler_type.IsFloatingPointType(count, is_complex)) {
+ if (is_complex)
+ error.SetErrorString(
+ "We don't support returning complex values at present");
+ else {
+ llvm::Optional<uint64_t> bit_width =
+ compiler_type.GetBitSize(frame_sp.get());
+ if (!bit_width) {
+ error.SetErrorString("can't get type size");
+ return error;
+ }
+ if (*bit_width <= 64) {
+ const RegisterInfo *xmm0_info =
+ reg_ctx->GetRegisterInfoByName("xmm0", 0);
+ RegisterValue xmm0_value;
+ DataExtractor data;
+ Status data_error;
+ size_t num_bytes = new_value_sp->GetData(data, data_error);
+ if (data_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Couldn't convert return value to raw data: %s",
+ data_error.AsCString());
+ return error;
+ }
+
+ unsigned char buffer[16];
+ ByteOrder byte_order = data.GetByteOrder();
+
+ data.CopyByteOrderedData(0, num_bytes, buffer, 16, byte_order);
+ xmm0_value.SetBytes(buffer, 16, byte_order);
+ reg_ctx->WriteRegister(xmm0_info, xmm0_value);
+ set_it_simple = true;
+ } else {
+ // Windows doesn't support 80 bit FP
+ error.SetErrorString(
+ "Windows-x86_64 doesn't allow FP larger than 64 bits.");
+ }
+ }
+ }
+
+ if (!set_it_simple) {
+ // Okay we've got a structure or something that doesn't fit in a simple
+ // register.
+ // TODO(wanyi): On Windows, if the return type is a struct:
+ // 1) smaller that 64 bits and return by value -> RAX
+ // 2) bigger than 64 bits, the caller will allocate memory for that struct
+ // and pass the struct pointer in RCX then return the pointer in RAX
+ error.SetErrorString("We only support setting simple integer and float "
+ "return types at present.");
+ }
+
+ return error;
+}
+
+ValueObjectSP ABIWindows_x86_64::GetReturnValueObjectSimple(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+ Value value;
+
+ if (!return_compiler_type)
+ return return_valobj_sp;
+
+ value.SetCompilerType(return_compiler_type);
+
+ RegisterContext *reg_ctx = thread.GetRegisterContext().get();
+ if (!reg_ctx)
+ return return_valobj_sp;
+
+ const uint32_t type_flags = return_compiler_type.GetTypeInfo();
+ if (type_flags & eTypeIsScalar) {
+ value.SetValueType(Value::eValueTypeScalar);
+
+ bool success = false;
+ if (type_flags & eTypeIsInteger) {
+ // Extract the register context so we can read arguments from registers
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (!byte_size)
+ return return_valobj_sp;
+ uint64_t raw_value = thread.GetRegisterContext()->ReadRegisterAsUnsigned(
+ reg_ctx->GetRegisterInfoByName("rax", 0), 0);
+ const bool is_signed = (type_flags & eTypeIsSigned) != 0;
+ switch (*byte_size) {
+ default:
+ break;
+
+ case sizeof(uint64_t):
+ if (is_signed)
+ value.GetScalar() = (int64_t)(raw_value);
+ else
+ value.GetScalar() = (uint64_t)(raw_value);
+ success = true;
+ break;
+
+ case sizeof(uint32_t):
+ if (is_signed)
+ value.GetScalar() = (int32_t)(raw_value & UINT32_MAX);
+ else
+ value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint16_t):
+ if (is_signed)
+ value.GetScalar() = (int16_t)(raw_value & UINT16_MAX);
+ else
+ value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX);
+ success = true;
+ break;
+
+ case sizeof(uint8_t):
+ if (is_signed)
+ value.GetScalar() = (int8_t)(raw_value & UINT8_MAX);
+ else
+ value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX);
+ success = true;
+ break;
+ }
+ } else if (type_flags & eTypeIsFloat) {
+ if (type_flags & eTypeIsComplex) {
+ // Don't handle complex yet.
+ } else {
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (byte_size && *byte_size <= sizeof(long double)) {
+ const RegisterInfo *xmm0_info =
+ reg_ctx->GetRegisterInfoByName("xmm0", 0);
+ RegisterValue xmm0_value;
+ if (reg_ctx->ReadRegister(xmm0_info, xmm0_value)) {
+ DataExtractor data;
+ if (xmm0_value.GetData(data)) {
+ lldb::offset_t offset = 0;
+ if (*byte_size == sizeof(float)) {
+ value.GetScalar() = (float)data.GetFloat(&offset);
+ success = true;
+ } else if (*byte_size == sizeof(double)) {
+ // double and long double are the same on windows
+ value.GetScalar() = (double)data.GetDouble(&offset);
+ success = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (success)
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if ((type_flags & eTypeIsPointer) ||
+ (type_flags & eTypeInstanceIsPointer)) {
+ unsigned rax_id =
+ reg_ctx->GetRegisterInfoByName("rax", 0)->kinds[eRegisterKindLLDB];
+ value.GetScalar() =
+ (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id,
+ 0);
+ value.SetValueType(Value::eValueTypeScalar);
+ return_valobj_sp = ValueObjectConstResult::Create(
+ thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));
+ } else if (type_flags & eTypeIsVector) {
+ llvm::Optional<uint64_t> byte_size =
+ return_compiler_type.GetByteSize(nullptr);
+ if (byte_size && *byte_size > 0) {
+ const RegisterInfo *xmm_reg =
+ reg_ctx->GetRegisterInfoByName("xmm0", 0);
+ if (xmm_reg == nullptr)
+ xmm_reg = reg_ctx->GetRegisterInfoByName("mm0", 0);
+
+ if (xmm_reg) {
+ if (*byte_size <= xmm_reg->byte_size) {
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp) {
+ std::unique_ptr<DataBufferHeap> heap_data_up(
+ new DataBufferHeap(*byte_size, 0));
+ const ByteOrder byte_order = process_sp->GetByteOrder();
+ RegisterValue reg_value;
+ if (reg_ctx->ReadRegister(xmm_reg, reg_value)) {
+ Status error;
+ if (reg_value.GetAsMemoryData(
+ xmm_reg, heap_data_up->GetBytes(),
+ heap_data_up->GetByteSize(), byte_order, error)) {
+ DataExtractor data(DataBufferSP(heap_data_up.release()),
+ byte_order,
+ process_sp->GetTarget()
+ .GetArchitecture()
+ .GetAddressByteSize());
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), data);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return return_valobj_sp;
+}
+
+// The compiler will flatten the nested aggregate type into single
+// layer and push the value to stack
+// This helper function will flatten an aggregate type
+// and return true if it can be returned in register(s) by value
+// return false if the aggregate is in memory
+static bool FlattenAggregateType(
+ Thread &thread, ExecutionContext &exe_ctx,
+ CompilerType &return_compiler_type,
+ uint32_t data_byte_offset,
+ std::vector<uint32_t> &aggregate_field_offsets,
+ std::vector<CompilerType> &aggregate_compiler_types) {
+
+ const uint32_t num_children = return_compiler_type.GetNumFields();
+ for (uint32_t idx = 0; idx < num_children; ++idx) {
+ std::string name;
+ bool is_signed;
+ uint32_t count;
+ bool is_complex;
+
+ uint64_t field_bit_offset = 0;
+ CompilerType field_compiler_type = return_compiler_type.GetFieldAtIndex(
+ idx, name, &field_bit_offset, nullptr, nullptr);
+ llvm::Optional<uint64_t> field_bit_width =
+ field_compiler_type.GetBitSize(&thread);
+
+ // if we don't know the size of the field (e.g. invalid type), exit
+ if (!field_bit_width || *field_bit_width == 0) {
+ return false;
+ }
+ // If there are any unaligned fields, this is stored in memory.
+ if (field_bit_offset % *field_bit_width != 0) {
+ return false;
+ }
+
+ // add overall offset
+ uint32_t field_byte_offset = field_bit_offset / 8 + data_byte_offset;
+
+ const uint32_t field_type_flags = field_compiler_type.GetTypeInfo();
+ if (field_compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ field_compiler_type.IsPointerType() ||
+ field_compiler_type.IsFloatingPointType(count, is_complex)) {
+ aggregate_field_offsets.push_back(field_byte_offset);
+ aggregate_compiler_types.push_back(field_compiler_type);
+ } else if (field_type_flags & eTypeHasChildren) {
+ if (!FlattenAggregateType(thread, exe_ctx, field_compiler_type,
+ field_byte_offset, aggregate_field_offsets,
+ aggregate_compiler_types)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+ValueObjectSP ABIWindows_x86_64::GetReturnValueObjectImpl(
+ Thread &thread, CompilerType &return_compiler_type) const {
+ ValueObjectSP return_valobj_sp;
+
+ if (!return_compiler_type) {
+ return return_valobj_sp;
+ }
+
+ // try extract value as if it's a simple type
+ return_valobj_sp = GetReturnValueObjectSimple(thread, return_compiler_type);
+ if (return_valobj_sp) {
+ return return_valobj_sp;
+ }
+
+ RegisterContextSP reg_ctx_sp = thread.GetRegisterContext();
+ if (!reg_ctx_sp) {
+ return return_valobj_sp;
+ }
+
+ llvm::Optional<uint64_t> bit_width = return_compiler_type.GetBitSize(&thread);
+ if (!bit_width) {
+ return return_valobj_sp;
+ }
+
+ // if it's not simple or aggregate type, then we don't know how to handle it
+ if (!return_compiler_type.IsAggregateType()) {
+ return return_valobj_sp;
+ }
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ Target *target = exe_ctx.GetTargetPtr();
+ uint32_t max_register_value_bit_width = 64;
+
+ // The scenario here is to have a struct/class which is POD
+ // if the return struct/class size is larger than 64 bits,
+ // the caller will allocate memory for it and pass the return addr in RCX
+ // then return the address in RAX
+
+ // if the struct is returned by value in register (RAX)
+ // its size has to be: 1, 2, 4, 8, 16, 32, or 64 bits (aligned)
+ // for floating point, the return value will be copied over to RAX
+ bool is_memory = *bit_width > max_register_value_bit_width ||
+ *bit_width & (*bit_width - 1);
+ std::vector<uint32_t> aggregate_field_offsets;
+ std::vector<CompilerType> aggregate_compiler_types;
+ if (!is_memory &&
+ FlattenAggregateType(thread, exe_ctx, return_compiler_type,
+ 0, aggregate_field_offsets,
+ aggregate_compiler_types)) {
+ ByteOrder byte_order = target->GetArchitecture().GetByteOrder();
+ DataBufferSP data_sp(
+ new DataBufferHeap(max_register_value_bit_width / 8, 0));
+ DataExtractor return_ext(data_sp, byte_order,
+ target->GetArchitecture().GetAddressByteSize());
+
+ // The only register used to return struct/class by value
+ const RegisterInfo *rax_info =
+ reg_ctx_sp->GetRegisterInfoByName("rax", 0);
+ RegisterValue rax_value;
+ reg_ctx_sp->ReadRegister(rax_info, rax_value);
+ DataExtractor rax_data;
+ rax_value.GetData(rax_data);
+
+ uint32_t used_bytes =
+ 0; // Tracks how much of the rax registers we've consumed so far
+
+ // in case of the returned type is a subclass of non-abstract-base class
+ // it will have a padding to skip the base content
+ if (aggregate_field_offsets.size())
+ used_bytes = aggregate_field_offsets[0];
+
+ const uint32_t num_children = aggregate_compiler_types.size();
+ for (uint32_t idx = 0; idx < num_children; idx++) {
+ bool is_signed;
+ bool is_complex;
+ uint32_t count;
+
+ CompilerType field_compiler_type = aggregate_compiler_types[idx];
+ uint32_t field_byte_width = (uint32_t) (*field_compiler_type.GetByteSize(&thread));
+ uint32_t field_byte_offset = aggregate_field_offsets[idx];
+
+ // this is unlikely w/o the overall size being greater than 8 bytes
+ // For now, return a nullptr return value object.
+ if (used_bytes >= 8 || used_bytes + field_byte_width > 8) {
+ return return_valobj_sp;
+ }
+
+ DataExtractor *copy_from_extractor = nullptr;
+ uint32_t copy_from_offset = 0;
+ if (field_compiler_type.IsIntegerOrEnumerationType(is_signed) ||
+ field_compiler_type.IsPointerType() ||
+ field_compiler_type.IsFloatingPointType(count, is_complex)) {
+ copy_from_extractor = &rax_data;
+ copy_from_offset = used_bytes;
+ used_bytes += field_byte_width;
+ }
+ // These two tests are just sanity checks. If I somehow get the type
+ // calculation wrong above it is better to just return nothing than to
+ // assert or crash.
+ if (!copy_from_extractor) {
+ return return_valobj_sp;
+ }
+ if (copy_from_offset + field_byte_width >
+ copy_from_extractor->GetByteSize()) {
+ return return_valobj_sp;
+ }
+ copy_from_extractor->CopyByteOrderedData(copy_from_offset,
+ field_byte_width, data_sp->GetBytes() + field_byte_offset,
+ field_byte_width, byte_order);
+ }
+ if (!is_memory) {
+ // The result is in our data buffer. Let's make a variable object out
+ // of it:
+ return_valobj_sp = ValueObjectConstResult::Create(
+ &thread, return_compiler_type, ConstString(""), return_ext);
+ }
+ }
+
+ // The Windows x86_64 ABI specifies that the return address for MEMORY
+ // objects be placed in rax on exit from the function.
+
+ // FIXME: This is just taking a guess, rax may very well no longer hold the
+ // return storage location.
+ // If we are going to do this right, when we make a new frame we should
+ // check to see if it uses a memory return, and if we are at the first
+ // instruction and if so stash away the return location. Then we would
+ // only return the memory return value if we know it is valid.
+ if (is_memory) {
+ unsigned rax_id =
+ reg_ctx_sp->GetRegisterInfoByName("rax", 0)->kinds[eRegisterKindLLDB];
+ lldb::addr_t storage_addr =
+ (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id,
+ 0);
+ return_valobj_sp = ValueObjectMemory::Create(
+ &thread, "", Address(storage_addr, nullptr), return_compiler_type);
+ }
+ return return_valobj_sp;
+}
+
+// This defines the CFA as rsp+8
+// the saved pc is at CFA-8 (i.e. rsp+0)
+// The saved rsp is CFA+0
+
+bool ABIWindows_x86_64::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t sp_reg_num = dwarf_rsp;
+ uint32_t pc_reg_num = dwarf_rip;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 8);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -8, false);
+ row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("x86_64 at-func-entry default");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ return true;
+}
+
+// Windows-x86_64 doesn't use %rbp
+// No available Unwind information for Windows-x86_64 (section .pdata)
+// Let's use SysV-x86_64 one for now
+bool ABIWindows_x86_64::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ uint32_t fp_reg_num = dwarf_rbp;
+ uint32_t sp_reg_num = dwarf_rsp;
+ uint32_t pc_reg_num = dwarf_rip;
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ const int32_t ptr_size = 8;
+ row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_rbp, 2 * ptr_size);
+ row->SetOffset(0);
+
+ row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true);
+ row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true);
+ row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("x86_64 default unwind plan");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+
+ return true;
+}
+
+bool ABIWindows_x86_64::RegisterIsVolatile(const RegisterInfo *reg_info) {
+ return !RegisterIsCalleeSaved(reg_info);
+}
+
+bool ABIWindows_x86_64::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
+ if (!reg_info)
+ return false;
+ assert(reg_info->name != nullptr && "unnamed register?");
+ std::string Name = std::string(reg_info->name);
+ bool IsCalleeSaved =
+ llvm::StringSwitch<bool>(Name)
+ .Cases("rbx", "ebx", "rbp", "ebp", "rdi", "edi", "rsi", "esi", true)
+ .Cases("rsp", "esp", "r12", "r13", "r14", "r15", "sp", "fp", true)
+ .Cases("xmm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11", "xmm12",
+ "xmm13", "xmm14", "xmm15", true)
+ .Default(false);
+ return IsCalleeSaved;
+}
+
+void ABIWindows_x86_64::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "Windows ABI for x86_64 targets", CreateInstance);
+}
+
+void ABIWindows_x86_64::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ABIWindows_x86_64::GetPluginNameStatic() {
+ static ConstString g_name("windows-x86_64");
+ return g_name;
+}
+
+//------------------------------------------------------------------
+// PluginInterface protocol
+//------------------------------------------------------------------
+
+lldb_private::ConstString ABIWindows_x86_64::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ABIWindows_x86_64::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ABI/Windows-x86_64/ABIWindows_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/ABI/Windows-x86_64/ABIWindows_x86_64.h
new file mode 100644
index 000000000000..9f6b2ceef299
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ABI/Windows-x86_64/ABIWindows_x86_64.h
@@ -0,0 +1,99 @@
+//===-- ABIWindows_x86_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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ABIWindows_x86_64_h_
+#define liblldb_ABIWindows_x86_64_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/lldb-private.h"
+
+class ABIWindows_x86_64 : public lldb_private::ABI {
+public:
+ ~ABIWindows_x86_64() override = default;
+
+ size_t GetRedZoneSize() const override;
+
+ bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp,
+ lldb::addr_t functionAddress,
+ lldb::addr_t returnAddress,
+ llvm::ArrayRef<lldb::addr_t> args) const override;
+
+ bool GetArgumentValues(lldb_private::Thread &thread,
+ lldb_private::ValueList &values) const override;
+
+ lldb_private::Status
+ SetReturnValueObject(lldb::StackFrameSP &frame_sp,
+ lldb::ValueObjectSP &new_value) override;
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectImpl(lldb_private::Thread &thread,
+ lldb_private::CompilerType &type) const override;
+
+ bool
+ CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override;
+
+ // In Windows_x86_64 ABI, stack will always be maintained 16-byte aligned
+ bool CallFrameAddressIsValid(lldb::addr_t cfa) override {
+ if (cfa & (16ull - 1ull))
+ return false; // Not 16 byte aligned
+ if (cfa == 0)
+ return false; // Zero is not a valid stack address
+ return true;
+ }
+
+ bool CodeAddressIsValid(lldb::addr_t pc) override {
+ // We have a 64 bit address space, so anything is valid as opcodes
+ // aren't fixed width...
+ return true;
+ }
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfoArray(uint32_t &count) override;
+
+ bool GetPointerReturnRegister(const char *&name) override;
+
+ //------------------------------------------------------------------
+ // Static Functions
+ //------------------------------------------------------------------
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, const lldb_private::ArchSpec &arch);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ //------------------------------------------------------------------
+ // PluginInterface protocol
+ //------------------------------------------------------------------
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ void CreateRegisterMapIfNeeded();
+
+ lldb::ValueObjectSP
+ GetReturnValueObjectSimple(lldb_private::Thread &thread,
+ lldb_private::CompilerType &ast_type) const;
+
+ bool RegisterIsCalleeSaved(const lldb_private::RegisterInfo *reg_info);
+
+private:
+ ABIWindows_x86_64(lldb::ProcessSP process_sp) : lldb_private::ABI(process_sp) {
+ // Call CreateInstance instead.
+ }
+};
+
+#endif // liblldb_ABISysV_x86_64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.cpp b/contrib/llvm-project/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.cpp
new file mode 100644
index 000000000000..5b86df6c5273
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.cpp
@@ -0,0 +1,157 @@
+//===-- ArchitectureArm.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 "Plugins/Architecture/Arm/ArchitectureArm.h"
+#include "Plugins/Process/Utility/ARMDefines.h"
+#include "Plugins/Process/Utility/InstructionUtils.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ArchSpec.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+ConstString ArchitectureArm::GetPluginNameStatic() {
+ return ConstString("arm");
+}
+
+void ArchitectureArm::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ "Arm-specific algorithms",
+ &ArchitectureArm::Create);
+}
+
+void ArchitectureArm::Terminate() {
+ PluginManager::UnregisterPlugin(&ArchitectureArm::Create);
+}
+
+std::unique_ptr<Architecture> ArchitectureArm::Create(const ArchSpec &arch) {
+ if (arch.GetMachine() != llvm::Triple::arm)
+ return nullptr;
+ return std::unique_ptr<Architecture>(new ArchitectureArm());
+}
+
+ConstString ArchitectureArm::GetPluginName() { return GetPluginNameStatic(); }
+uint32_t ArchitectureArm::GetPluginVersion() { return 1; }
+
+void ArchitectureArm::OverrideStopInfo(Thread &thread) const {
+ // We need to check if we are stopped in Thumb mode in a IT instruction and
+ // detect if the condition doesn't pass. If this is the case it means we
+ // won't actually execute this instruction. If this happens we need to clear
+ // the stop reason to no thread plans think we are stopped for a reason and
+ // the plans should keep going.
+ //
+ // We do this because when single stepping many ARM processes, debuggers
+ // often use the BVR/BCR registers that says "stop when the PC is not equal
+ // to its current value". This method of stepping means we can end up
+ // stopping on instructions inside an if/then block that wouldn't get
+ // executed. By fixing this we can stop the debugger from seeming like you
+ // stepped through both the "if" _and_ the "else" clause when source level
+ // stepping because the debugger stops regardless due to the BVR/BCR
+ // triggering a stop.
+ //
+ // It also means we can set breakpoints on instructions inside an an if/then
+ // block and correctly skip them if we use the BKPT instruction. The ARM and
+ // Thumb BKPT instructions are unconditional even when executed in a Thumb IT
+ // block.
+ //
+ // If your debugger inserts software traps in ARM/Thumb code, it will need to
+ // use 16 and 32 bit instruction for 16 and 32 bit thumb instructions
+ // respectively. If your debugger inserts a 16 bit thumb trap on top of a 32
+ // bit thumb instruction for an opcode that is inside an if/then, it will
+ // change the it/then to conditionally execute your
+ // 16 bit trap and then cause your program to crash if it executes the
+ // trailing 16 bits (the second half of the 32 bit thumb instruction you
+ // partially overwrote).
+
+ RegisterContextSP reg_ctx_sp(thread.GetRegisterContext());
+ if (!reg_ctx_sp)
+ return;
+
+ const uint32_t cpsr = reg_ctx_sp->GetFlags(0);
+ if (cpsr == 0)
+ return;
+
+ // Read the J and T bits to get the ISETSTATE
+ const uint32_t J = Bit32(cpsr, 24);
+ const uint32_t T = Bit32(cpsr, 5);
+ const uint32_t ISETSTATE = J << 1 | T;
+ if (ISETSTATE == 0) {
+// NOTE: I am pretty sure we want to enable the code below
+// that detects when we stop on an instruction in ARM mode that is conditional
+// and the condition doesn't pass. This can happen if you set a breakpoint on
+// an instruction that is conditional. We currently will _always_ stop on the
+// instruction which is bad. You can also run into this while single stepping
+// and you could appear to run code in the "if" and in the "else" clause
+// because it would stop at all of the conditional instructions in both. In
+// such cases, we really don't want to stop at this location.
+// I will check with the lldb-dev list first before I enable this.
+#if 0
+ // ARM mode: check for condition on instruction
+ const addr_t pc = reg_ctx_sp->GetPC();
+ Status error;
+ // If we fail to read the opcode we will get UINT64_MAX as the result in
+ // "opcode" which we can use to detect if we read a valid opcode.
+ const uint64_t opcode = thread.GetProcess()->ReadUnsignedIntegerFromMemory(pc, 4, UINT64_MAX, error);
+ if (opcode <= UINT32_MAX)
+ {
+ const uint32_t condition = Bits32((uint32_t)opcode, 31, 28);
+ if (!ARMConditionPassed(condition, cpsr))
+ {
+ // We ARE stopped on an ARM instruction whose condition doesn't
+ // pass so this instruction won't get executed. Regardless of why
+ // it stopped, we need to clear the stop info
+ thread.SetStopInfo (StopInfoSP());
+ }
+ }
+#endif
+ } else if (ISETSTATE == 1) {
+ // Thumb mode
+ const uint32_t ITSTATE = Bits32(cpsr, 15, 10) << 2 | Bits32(cpsr, 26, 25);
+ if (ITSTATE != 0) {
+ const uint32_t condition = Bits32(ITSTATE, 7, 4);
+ if (!ARMConditionPassed(condition, cpsr)) {
+ // We ARE stopped in a Thumb IT instruction on an instruction whose
+ // condition doesn't pass so this instruction won't get executed.
+ // Regardless of why it stopped, we need to clear the stop info
+ thread.SetStopInfo(StopInfoSP());
+ }
+ }
+ }
+}
+
+addr_t ArchitectureArm::GetCallableLoadAddress(addr_t code_addr,
+ AddressClass addr_class) const {
+ bool is_alternate_isa = false;
+
+ switch (addr_class) {
+ case AddressClass::eData:
+ case AddressClass::eDebug:
+ return LLDB_INVALID_ADDRESS;
+ case AddressClass::eCodeAlternateISA:
+ is_alternate_isa = true;
+ break;
+ default: break;
+ }
+
+ if ((code_addr & 2u) || is_alternate_isa)
+ return code_addr | 1u;
+ return code_addr;
+}
+
+addr_t ArchitectureArm::GetOpcodeLoadAddress(addr_t opcode_addr,
+ AddressClass addr_class) const {
+ switch (addr_class) {
+ case AddressClass::eData:
+ case AddressClass::eDebug:
+ return LLDB_INVALID_ADDRESS;
+ default: break;
+ }
+ return opcode_addr & ~(1ull);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.h b/contrib/llvm-project/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.h
new file mode 100644
index 000000000000..03e79ce524a7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.h
@@ -0,0 +1,40 @@
+//===-- ArchitectureArm.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_PLUGIN_ARCHITECTURE_ARM_H
+#define LLDB_PLUGIN_ARCHITECTURE_ARM_H
+
+#include "lldb/Core/Architecture.h"
+
+namespace lldb_private {
+
+class ArchitectureArm : public Architecture {
+public:
+ static ConstString GetPluginNameStatic();
+ static void Initialize();
+ static void Terminate();
+
+ ConstString GetPluginName() override;
+ uint32_t GetPluginVersion() override;
+
+ void OverrideStopInfo(Thread &thread) const override;
+
+ lldb::addr_t GetCallableLoadAddress(lldb::addr_t load_addr,
+ AddressClass addr_class) const override;
+
+ lldb::addr_t GetOpcodeLoadAddress(lldb::addr_t load_addr,
+ AddressClass addr_class) const override;
+
+private:
+ static std::unique_ptr<Architecture> Create(const ArchSpec &arch);
+ ArchitectureArm() = default;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_PLUGIN_ARCHITECTURE_ARM_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Architecture/Mips/ArchitectureMips.cpp b/contrib/llvm-project/lldb/source/Plugins/Architecture/Mips/ArchitectureMips.cpp
new file mode 100644
index 000000000000..60f1a2eb7572
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Architecture/Mips/ArchitectureMips.cpp
@@ -0,0 +1,240 @@
+//===-- ArchitectureMips.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 "Plugins/Architecture/Mips/ArchitectureMips.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/Log.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+ConstString ArchitectureMips::GetPluginNameStatic() {
+ return ConstString("mips");
+}
+
+void ArchitectureMips::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ "Mips-specific algorithms",
+ &ArchitectureMips::Create);
+}
+
+void ArchitectureMips::Terminate() {
+ PluginManager::UnregisterPlugin(&ArchitectureMips::Create);
+}
+
+std::unique_ptr<Architecture> ArchitectureMips::Create(const ArchSpec &arch) {
+ return arch.IsMIPS() ?
+ std::unique_ptr<Architecture>(new ArchitectureMips(arch)) : nullptr;
+}
+
+ConstString ArchitectureMips::GetPluginName() { return GetPluginNameStatic(); }
+uint32_t ArchitectureMips::GetPluginVersion() { return 1; }
+
+addr_t ArchitectureMips::GetCallableLoadAddress(addr_t code_addr,
+ AddressClass addr_class) const {
+ bool is_alternate_isa = false;
+
+ switch (addr_class) {
+ case AddressClass::eData:
+ case AddressClass::eDebug:
+ return LLDB_INVALID_ADDRESS;
+ case AddressClass::eCodeAlternateISA:
+ is_alternate_isa = true;
+ break;
+ default: break;
+ }
+
+ if ((code_addr & 2ull) || is_alternate_isa)
+ return code_addr | 1u;
+ return code_addr;
+}
+
+addr_t ArchitectureMips::GetOpcodeLoadAddress(addr_t opcode_addr,
+ AddressClass addr_class) const {
+ switch (addr_class) {
+ case AddressClass::eData:
+ case AddressClass::eDebug:
+ return LLDB_INVALID_ADDRESS;
+ default: break;
+ }
+ return opcode_addr & ~(1ull);
+}
+
+lldb::addr_t ArchitectureMips::GetBreakableLoadAddress(lldb::addr_t addr,
+ Target &target) const {
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+
+ Address resolved_addr;
+
+ SectionLoadList &section_load_list = target.GetSectionLoadList();
+ if (section_load_list.IsEmpty())
+ // No sections are loaded, so we must assume we are not running yet and
+ // need to operate only on file address.
+ target.ResolveFileAddress(addr, resolved_addr);
+ else
+ target.ResolveLoadAddress(addr, resolved_addr);
+
+ addr_t current_offset = 0;
+
+ // Get the function boundaries to make sure we don't scan back before the
+ // beginning of the current function.
+ ModuleSP temp_addr_module_sp(resolved_addr.GetModule());
+ if (temp_addr_module_sp) {
+ SymbolContext sc;
+ SymbolContextItem resolve_scope =
+ eSymbolContextFunction | eSymbolContextSymbol;
+ temp_addr_module_sp->ResolveSymbolContextForAddress(resolved_addr,
+ resolve_scope, sc);
+ Address sym_addr;
+ if (sc.function)
+ sym_addr = sc.function->GetAddressRange().GetBaseAddress();
+ else if (sc.symbol)
+ sym_addr = sc.symbol->GetAddress();
+
+ addr_t function_start = sym_addr.GetLoadAddress(&target);
+ if (function_start == LLDB_INVALID_ADDRESS)
+ function_start = sym_addr.GetFileAddress();
+
+ if (function_start)
+ current_offset = addr - function_start;
+ }
+
+ // If breakpoint address is start of function then we dont have to do
+ // anything.
+ if (current_offset == 0)
+ return addr;
+
+ ExecutionContext ctx;
+ target.CalculateExecutionContext(ctx);
+ auto insn = GetInstructionAtAddress(ctx, current_offset, addr);
+
+ if (nullptr == insn || !insn->HasDelaySlot())
+ return addr;
+
+ // Adjust the breakable address
+ uint64_t breakable_addr = addr - insn->GetOpcode().GetByteSize();
+ if (log)
+ log->Printf("Target::%s Breakpoint at 0x%8.8" PRIx64
+ " is adjusted to 0x%8.8" PRIx64 " due to delay slot\n",
+ __FUNCTION__, addr, breakable_addr);
+
+ return breakable_addr;
+}
+
+Instruction *ArchitectureMips::GetInstructionAtAddress(
+ const ExecutionContext &exe_ctx, const Address &resolved_addr,
+ addr_t symbol_offset) const {
+
+ auto loop_count = symbol_offset / 2;
+
+ uint32_t arch_flags = m_arch.GetFlags();
+ bool IsMips16 = arch_flags & ArchSpec::eMIPSAse_mips16;
+ bool IsMicromips = arch_flags & ArchSpec::eMIPSAse_micromips;
+
+ if (loop_count > 3) {
+ // Scan previous 6 bytes
+ if (IsMips16 | IsMicromips)
+ loop_count = 3;
+ // For mips-only, instructions are always 4 bytes, so scan previous 4
+ // bytes only.
+ else
+ loop_count = 2;
+ }
+
+ // Create Disassembler Instance
+ lldb::DisassemblerSP disasm_sp(
+ Disassembler::FindPlugin(m_arch, nullptr, nullptr));
+
+ InstructionList instruction_list;
+ InstructionSP prev_insn;
+ bool prefer_file_cache = true; // Read from file
+ uint32_t inst_to_choose = 0;
+
+ Address addr = resolved_addr;
+
+ for (uint32_t i = 1; i <= loop_count; i++) {
+ // Adjust the address to read from.
+ addr.Slide(-2);
+ AddressRange range(addr, i * 2);
+ uint32_t insn_size = 0;
+
+ disasm_sp->ParseInstructions(&exe_ctx, range, nullptr, prefer_file_cache);
+
+ uint32_t num_insns = disasm_sp->GetInstructionList().GetSize();
+ if (num_insns) {
+ prev_insn = disasm_sp->GetInstructionList().GetInstructionAtIndex(0);
+ insn_size = prev_insn->GetOpcode().GetByteSize();
+ if (i == 1 && insn_size == 2) {
+ // This looks like a valid 2-byte instruction (but it could be a part
+ // of upper 4 byte instruction).
+ instruction_list.Append(prev_insn);
+ inst_to_choose = 1;
+ }
+ else if (i == 2) {
+ // Here we may get one 4-byte instruction or two 2-byte instructions.
+ if (num_insns == 2) {
+ // Looks like there are two 2-byte instructions above our
+ // breakpoint target address. Now the upper 2-byte instruction is
+ // either a valid 2-byte instruction or could be a part of it's
+ // upper 4-byte instruction. In both cases we don't care because in
+ // this case lower 2-byte instruction is definitely a valid
+ // instruction and whatever i=1 iteration has found out is true.
+ inst_to_choose = 1;
+ break;
+ }
+ else if (insn_size == 4) {
+ // This instruction claims its a valid 4-byte instruction. But it
+ // could be a part of it's upper 4-byte instruction. Lets try
+ // scanning upper 2 bytes to verify this.
+ instruction_list.Append(prev_insn);
+ inst_to_choose = 2;
+ }
+ }
+ else if (i == 3) {
+ if (insn_size == 4)
+ // FIXME: We reached here that means instruction at [target - 4] has
+ // already claimed to be a 4-byte instruction, and now instruction
+ // at [target - 6] is also claiming that it's a 4-byte instruction.
+ // This can not be true. In this case we can not decide the valid
+ // previous instruction so we let lldb set the breakpoint at the
+ // address given by user.
+ inst_to_choose = 0;
+ else
+ // This is straight-forward
+ inst_to_choose = 2;
+ break;
+ }
+ }
+ else {
+ // Decode failed, bytes do not form a valid instruction. So whatever
+ // previous iteration has found out is true.
+ if (i > 1) {
+ inst_to_choose = i - 1;
+ break;
+ }
+ }
+ }
+
+ // Check if we are able to find any valid instruction.
+ if (inst_to_choose) {
+ if (inst_to_choose > instruction_list.GetSize())
+ inst_to_choose--;
+ return instruction_list.GetInstructionAtIndex(inst_to_choose - 1).get();
+ }
+
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Architecture/Mips/ArchitectureMips.h b/contrib/llvm-project/lldb/source/Plugins/Architecture/Mips/ArchitectureMips.h
new file mode 100644
index 000000000000..a15991ff9ebf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Architecture/Mips/ArchitectureMips.h
@@ -0,0 +1,51 @@
+//===-- ArchitectureMips.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_PLUGIN_ARCHITECTURE_MIPS_H
+#define LLDB_PLUGIN_ARCHITECTURE_MIPS_H
+
+#include "lldb/Core/Architecture.h"
+#include "lldb/Utility/ArchSpec.h"
+
+namespace lldb_private {
+
+class ArchitectureMips : public Architecture {
+public:
+ static ConstString GetPluginNameStatic();
+ static void Initialize();
+ static void Terminate();
+
+ ConstString GetPluginName() override;
+ uint32_t GetPluginVersion() override;
+
+ void OverrideStopInfo(Thread &thread) const override {}
+
+ lldb::addr_t GetBreakableLoadAddress(lldb::addr_t addr,
+ Target &) const override;
+
+ lldb::addr_t GetCallableLoadAddress(lldb::addr_t load_addr,
+ AddressClass addr_class) const override;
+
+ lldb::addr_t GetOpcodeLoadAddress(lldb::addr_t load_addr,
+ AddressClass addr_class) const override;
+
+private:
+ Instruction *GetInstructionAtAddress(const ExecutionContext &exe_ctx,
+ const Address &resolved_addr,
+ lldb::addr_t symbol_offset) const;
+
+
+ static std::unique_ptr<Architecture> Create(const ArchSpec &arch);
+ ArchitectureMips(const ArchSpec &arch) : m_arch(arch) {}
+
+ ArchSpec m_arch;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_PLUGIN_ARCHITECTURE_MIPS_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Architecture/PPC64/ArchitecturePPC64.cpp b/contrib/llvm-project/lldb/source/Plugins/Architecture/PPC64/ArchitecturePPC64.cpp
new file mode 100644
index 000000000000..76eaa44546eb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Architecture/PPC64/ArchitecturePPC64.cpp
@@ -0,0 +1,67 @@
+//===-- ArchitecturePPC64.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 "Plugins/Architecture/PPC64/ArchitecturePPC64.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ArchSpec.h"
+
+#include "llvm/BinaryFormat/ELF.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+ConstString ArchitecturePPC64::GetPluginNameStatic() {
+ return ConstString("ppc64");
+}
+
+void ArchitecturePPC64::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ "PPC64-specific algorithms",
+ &ArchitecturePPC64::Create);
+}
+
+void ArchitecturePPC64::Terminate() {
+ PluginManager::UnregisterPlugin(&ArchitecturePPC64::Create);
+}
+
+std::unique_ptr<Architecture> ArchitecturePPC64::Create(const ArchSpec &arch) {
+ if (arch.GetTriple().isPPC64() &&
+ arch.GetTriple().getObjectFormat() == llvm::Triple::ObjectFormatType::ELF)
+ return std::unique_ptr<Architecture>(new ArchitecturePPC64());
+ return nullptr;
+}
+
+ConstString ArchitecturePPC64::GetPluginName() { return GetPluginNameStatic(); }
+uint32_t ArchitecturePPC64::GetPluginVersion() { return 1; }
+
+static int32_t GetLocalEntryOffset(const Symbol &sym) {
+ unsigned char other = sym.GetFlags() >> 8 & 0xFF;
+ return llvm::ELF::decodePPC64LocalEntryOffset(other);
+}
+
+size_t ArchitecturePPC64::GetBytesToSkip(Symbol &func,
+ const Address &curr_addr) const {
+ if (curr_addr.GetFileAddress() ==
+ func.GetFileAddress() + GetLocalEntryOffset(func))
+ return func.GetPrologueByteSize();
+ return 0;
+}
+
+void ArchitecturePPC64::AdjustBreakpointAddress(const Symbol &func,
+ Address &addr) const {
+ int32_t loffs = GetLocalEntryOffset(func);
+ if (!loffs)
+ return;
+
+ addr.SetOffset(addr.GetOffset() + loffs);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Architecture/PPC64/ArchitecturePPC64.h b/contrib/llvm-project/lldb/source/Plugins/Architecture/PPC64/ArchitecturePPC64.h
new file mode 100644
index 000000000000..dc663b849c4a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Architecture/PPC64/ArchitecturePPC64.h
@@ -0,0 +1,41 @@
+//===-- ArchitecturePPC64.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_PLUGIN_ARCHITECTURE_PPC64_H
+#define LLDB_PLUGIN_ARCHITECTURE_PPC64_H
+
+#include "lldb/Core/Architecture.h"
+
+namespace lldb_private {
+
+class ArchitecturePPC64 : public Architecture {
+public:
+ static ConstString GetPluginNameStatic();
+ static void Initialize();
+ static void Terminate();
+
+ ConstString GetPluginName() override;
+ uint32_t GetPluginVersion() override;
+
+ void OverrideStopInfo(Thread &thread) const override {}
+
+ /// This method compares current address with current function's
+ /// local entry point, returning the bytes to skip if they match.
+ size_t GetBytesToSkip(Symbol &func, const Address &curr_addr) const override;
+
+ void AdjustBreakpointAddress(const Symbol &func,
+ Address &addr) const override;
+
+private:
+ static std::unique_ptr<Architecture> Create(const ArchSpec &arch);
+ ArchitecturePPC64() = default;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_PLUGIN_ARCHITECTURE_PPC64_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp b/contrib/llvm-project/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp
new file mode 100644
index 000000000000..44c75fc953c8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp
@@ -0,0 +1,1433 @@
+//===-- DisassemblerLLVMC.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 "DisassemblerLLVMC.h"
+
+#include "llvm-c/Disassembler.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
+#include "llvm/MC/MCDisassembler/MCExternalSymbolizer.h"
+#include "llvm/MC/MCDisassembler/MCRelocationInfo.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+class DisassemblerLLVMC::MCDisasmInstance {
+public:
+ static std::unique_ptr<MCDisasmInstance>
+ Create(const char *triple, const char *cpu, const char *features_str,
+ unsigned flavor, DisassemblerLLVMC &owner);
+
+ ~MCDisasmInstance() = default;
+
+ uint64_t GetMCInst(const uint8_t *opcode_data, size_t opcode_data_len,
+ lldb::addr_t pc, llvm::MCInst &mc_inst) const;
+ void PrintMCInst(llvm::MCInst &mc_inst, std::string &inst_string,
+ std::string &comments_string);
+ void SetStyle(bool use_hex_immed, HexImmediateStyle hex_style);
+ bool CanBranch(llvm::MCInst &mc_inst) const;
+ bool HasDelaySlot(llvm::MCInst &mc_inst) const;
+ bool IsCall(llvm::MCInst &mc_inst) const;
+
+private:
+ MCDisasmInstance(std::unique_ptr<llvm::MCInstrInfo> &&instr_info_up,
+ std::unique_ptr<llvm::MCRegisterInfo> &&reg_info_up,
+ std::unique_ptr<llvm::MCSubtargetInfo> &&subtarget_info_up,
+ std::unique_ptr<llvm::MCAsmInfo> &&asm_info_up,
+ std::unique_ptr<llvm::MCContext> &&context_up,
+ std::unique_ptr<llvm::MCDisassembler> &&disasm_up,
+ std::unique_ptr<llvm::MCInstPrinter> &&instr_printer_up);
+
+ std::unique_ptr<llvm::MCInstrInfo> m_instr_info_up;
+ std::unique_ptr<llvm::MCRegisterInfo> m_reg_info_up;
+ std::unique_ptr<llvm::MCSubtargetInfo> m_subtarget_info_up;
+ std::unique_ptr<llvm::MCAsmInfo> m_asm_info_up;
+ std::unique_ptr<llvm::MCContext> m_context_up;
+ std::unique_ptr<llvm::MCDisassembler> m_disasm_up;
+ std::unique_ptr<llvm::MCInstPrinter> m_instr_printer_up;
+};
+
+class InstructionLLVMC : public lldb_private::Instruction {
+public:
+ InstructionLLVMC(DisassemblerLLVMC &disasm,
+ const lldb_private::Address &address,
+ AddressClass addr_class)
+ : Instruction(address, addr_class),
+ m_disasm_wp(std::static_pointer_cast<DisassemblerLLVMC>(
+ disasm.shared_from_this())),
+ m_does_branch(eLazyBoolCalculate), m_has_delay_slot(eLazyBoolCalculate),
+ m_is_call(eLazyBoolCalculate), m_is_valid(false),
+ m_using_file_addr(false) {}
+
+ ~InstructionLLVMC() override = default;
+
+ bool DoesBranch() override {
+ if (m_does_branch == eLazyBoolCalculate) {
+ DisassemblerScope disasm(*this);
+ if (disasm) {
+ DataExtractor data;
+ if (m_opcode.GetData(data)) {
+ bool is_alternate_isa;
+ lldb::addr_t pc = m_address.GetFileAddress();
+
+ DisassemblerLLVMC::MCDisasmInstance *mc_disasm_ptr =
+ GetDisasmToUse(is_alternate_isa, disasm);
+ const uint8_t *opcode_data = data.GetDataStart();
+ const size_t opcode_data_len = data.GetByteSize();
+ llvm::MCInst inst;
+ const size_t inst_size =
+ mc_disasm_ptr->GetMCInst(opcode_data, opcode_data_len, pc, inst);
+ // Be conservative, if we didn't understand the instruction, say it
+ // might branch...
+ if (inst_size == 0)
+ m_does_branch = eLazyBoolYes;
+ else {
+ const bool can_branch = mc_disasm_ptr->CanBranch(inst);
+ if (can_branch)
+ m_does_branch = eLazyBoolYes;
+ else
+ m_does_branch = eLazyBoolNo;
+ }
+ }
+ }
+ }
+ return m_does_branch == eLazyBoolYes;
+ }
+
+ bool HasDelaySlot() override {
+ if (m_has_delay_slot == eLazyBoolCalculate) {
+ DisassemblerScope disasm(*this);
+ if (disasm) {
+ DataExtractor data;
+ if (m_opcode.GetData(data)) {
+ bool is_alternate_isa;
+ lldb::addr_t pc = m_address.GetFileAddress();
+
+ DisassemblerLLVMC::MCDisasmInstance *mc_disasm_ptr =
+ GetDisasmToUse(is_alternate_isa, disasm);
+ const uint8_t *opcode_data = data.GetDataStart();
+ const size_t opcode_data_len = data.GetByteSize();
+ llvm::MCInst inst;
+ const size_t inst_size =
+ mc_disasm_ptr->GetMCInst(opcode_data, opcode_data_len, pc, inst);
+ // if we didn't understand the instruction, say it doesn't have a
+ // delay slot...
+ if (inst_size == 0)
+ m_has_delay_slot = eLazyBoolNo;
+ else {
+ const bool has_delay_slot = mc_disasm_ptr->HasDelaySlot(inst);
+ if (has_delay_slot)
+ m_has_delay_slot = eLazyBoolYes;
+ else
+ m_has_delay_slot = eLazyBoolNo;
+ }
+ }
+ }
+ }
+ return m_has_delay_slot == eLazyBoolYes;
+ }
+
+ DisassemblerLLVMC::MCDisasmInstance *GetDisasmToUse(bool &is_alternate_isa) {
+ DisassemblerScope disasm(*this);
+ return GetDisasmToUse(is_alternate_isa, disasm);
+ }
+
+ size_t Decode(const lldb_private::Disassembler &disassembler,
+ const lldb_private::DataExtractor &data,
+ lldb::offset_t data_offset) override {
+ // All we have to do is read the opcode which can be easy for some
+ // architectures
+ bool got_op = false;
+ DisassemblerScope disasm(*this);
+ if (disasm) {
+ const ArchSpec &arch = disasm->GetArchitecture();
+ const lldb::ByteOrder byte_order = data.GetByteOrder();
+
+ const uint32_t min_op_byte_size = arch.GetMinimumOpcodeByteSize();
+ const uint32_t max_op_byte_size = arch.GetMaximumOpcodeByteSize();
+ if (min_op_byte_size == max_op_byte_size) {
+ // Fixed size instructions, just read that amount of data.
+ if (!data.ValidOffsetForDataOfSize(data_offset, min_op_byte_size))
+ return false;
+
+ switch (min_op_byte_size) {
+ case 1:
+ m_opcode.SetOpcode8(data.GetU8(&data_offset), byte_order);
+ got_op = true;
+ break;
+
+ case 2:
+ m_opcode.SetOpcode16(data.GetU16(&data_offset), byte_order);
+ got_op = true;
+ break;
+
+ case 4:
+ m_opcode.SetOpcode32(data.GetU32(&data_offset), byte_order);
+ got_op = true;
+ break;
+
+ case 8:
+ m_opcode.SetOpcode64(data.GetU64(&data_offset), byte_order);
+ got_op = true;
+ break;
+
+ default:
+ m_opcode.SetOpcodeBytes(data.PeekData(data_offset, min_op_byte_size),
+ min_op_byte_size);
+ got_op = true;
+ break;
+ }
+ }
+ if (!got_op) {
+ bool is_alternate_isa = false;
+ DisassemblerLLVMC::MCDisasmInstance *mc_disasm_ptr =
+ GetDisasmToUse(is_alternate_isa, disasm);
+
+ const llvm::Triple::ArchType machine = arch.GetMachine();
+ if (machine == llvm::Triple::arm || machine == llvm::Triple::thumb) {
+ if (machine == llvm::Triple::thumb || is_alternate_isa) {
+ uint32_t thumb_opcode = data.GetU16(&data_offset);
+ if ((thumb_opcode & 0xe000) != 0xe000 ||
+ ((thumb_opcode & 0x1800u) == 0)) {
+ m_opcode.SetOpcode16(thumb_opcode, byte_order);
+ m_is_valid = true;
+ } else {
+ thumb_opcode <<= 16;
+ thumb_opcode |= data.GetU16(&data_offset);
+ m_opcode.SetOpcode16_2(thumb_opcode, byte_order);
+ m_is_valid = true;
+ }
+ } else {
+ m_opcode.SetOpcode32(data.GetU32(&data_offset), byte_order);
+ m_is_valid = true;
+ }
+ } else {
+ // The opcode isn't evenly sized, so we need to actually use the llvm
+ // disassembler to parse it and get the size.
+ uint8_t *opcode_data =
+ const_cast<uint8_t *>(data.PeekData(data_offset, 1));
+ const size_t opcode_data_len = data.BytesLeft(data_offset);
+ const addr_t pc = m_address.GetFileAddress();
+ llvm::MCInst inst;
+
+ const size_t inst_size =
+ mc_disasm_ptr->GetMCInst(opcode_data, opcode_data_len, pc, inst);
+ if (inst_size == 0)
+ m_opcode.Clear();
+ else {
+ m_opcode.SetOpcodeBytes(opcode_data, inst_size);
+ m_is_valid = true;
+ }
+ }
+ }
+ return m_opcode.GetByteSize();
+ }
+ return 0;
+ }
+
+ void AppendComment(std::string &description) {
+ if (m_comment.empty())
+ m_comment.swap(description);
+ else {
+ m_comment.append(", ");
+ m_comment.append(description);
+ }
+ }
+
+ void CalculateMnemonicOperandsAndComment(
+ const lldb_private::ExecutionContext *exe_ctx) override {
+ DataExtractor data;
+ const AddressClass address_class = GetAddressClass();
+
+ if (m_opcode.GetData(data)) {
+ std::string out_string;
+ std::string comment_string;
+
+ DisassemblerScope disasm(*this, exe_ctx);
+ if (disasm) {
+ DisassemblerLLVMC::MCDisasmInstance *mc_disasm_ptr;
+
+ if (address_class == AddressClass::eCodeAlternateISA)
+ mc_disasm_ptr = disasm->m_alternate_disasm_up.get();
+ else
+ mc_disasm_ptr = disasm->m_disasm_up.get();
+
+ lldb::addr_t pc = m_address.GetFileAddress();
+ m_using_file_addr = true;
+
+ const bool data_from_file = disasm->m_data_from_file;
+ bool use_hex_immediates = true;
+ Disassembler::HexImmediateStyle hex_style = Disassembler::eHexStyleC;
+
+ if (exe_ctx) {
+ Target *target = exe_ctx->GetTargetPtr();
+ if (target) {
+ use_hex_immediates = target->GetUseHexImmediates();
+ hex_style = target->GetHexImmediateStyle();
+
+ if (!data_from_file) {
+ const lldb::addr_t load_addr = m_address.GetLoadAddress(target);
+ if (load_addr != LLDB_INVALID_ADDRESS) {
+ pc = load_addr;
+ m_using_file_addr = false;
+ }
+ }
+ }
+ }
+
+ const uint8_t *opcode_data = data.GetDataStart();
+ const size_t opcode_data_len = data.GetByteSize();
+ llvm::MCInst inst;
+ size_t inst_size =
+ mc_disasm_ptr->GetMCInst(opcode_data, opcode_data_len, pc, inst);
+
+ if (inst_size > 0) {
+ mc_disasm_ptr->SetStyle(use_hex_immediates, hex_style);
+ mc_disasm_ptr->PrintMCInst(inst, out_string, comment_string);
+
+ if (!comment_string.empty()) {
+ AppendComment(comment_string);
+ }
+ }
+
+ if (inst_size == 0) {
+ m_comment.assign("unknown opcode");
+ inst_size = m_opcode.GetByteSize();
+ StreamString mnemonic_strm;
+ lldb::offset_t offset = 0;
+ lldb::ByteOrder byte_order = data.GetByteOrder();
+ switch (inst_size) {
+ case 1: {
+ const uint8_t uval8 = data.GetU8(&offset);
+ m_opcode.SetOpcode8(uval8, byte_order);
+ m_opcode_name.assign(".byte");
+ mnemonic_strm.Printf("0x%2.2x", uval8);
+ } break;
+ case 2: {
+ const uint16_t uval16 = data.GetU16(&offset);
+ m_opcode.SetOpcode16(uval16, byte_order);
+ m_opcode_name.assign(".short");
+ mnemonic_strm.Printf("0x%4.4x", uval16);
+ } break;
+ case 4: {
+ const uint32_t uval32 = data.GetU32(&offset);
+ m_opcode.SetOpcode32(uval32, byte_order);
+ m_opcode_name.assign(".long");
+ mnemonic_strm.Printf("0x%8.8x", uval32);
+ } break;
+ case 8: {
+ const uint64_t uval64 = data.GetU64(&offset);
+ m_opcode.SetOpcode64(uval64, byte_order);
+ m_opcode_name.assign(".quad");
+ mnemonic_strm.Printf("0x%16.16" PRIx64, uval64);
+ } break;
+ default:
+ if (inst_size == 0)
+ return;
+ else {
+ const uint8_t *bytes = data.PeekData(offset, inst_size);
+ if (bytes == nullptr)
+ return;
+ m_opcode_name.assign(".byte");
+ m_opcode.SetOpcodeBytes(bytes, inst_size);
+ mnemonic_strm.Printf("0x%2.2x", bytes[0]);
+ for (uint32_t i = 1; i < inst_size; ++i)
+ mnemonic_strm.Printf(" 0x%2.2x", bytes[i]);
+ }
+ break;
+ }
+ m_mnemonics = mnemonic_strm.GetString();
+ return;
+ } else {
+ if (m_does_branch == eLazyBoolCalculate) {
+ const bool can_branch = mc_disasm_ptr->CanBranch(inst);
+ if (can_branch)
+ m_does_branch = eLazyBoolYes;
+ else
+ m_does_branch = eLazyBoolNo;
+ }
+ }
+
+ static RegularExpression s_regex(
+ llvm::StringRef("[ \t]*([^ ^\t]+)[ \t]*([^ ^\t].*)?"));
+
+ RegularExpression::Match matches(3);
+
+ if (s_regex.Execute(out_string, &matches)) {
+ matches.GetMatchAtIndex(out_string.c_str(), 1, m_opcode_name);
+ matches.GetMatchAtIndex(out_string.c_str(), 2, m_mnemonics);
+ }
+ }
+ }
+ }
+
+ bool IsValid() const { return m_is_valid; }
+
+ bool UsingFileAddress() const { return m_using_file_addr; }
+ size_t GetByteSize() const { return m_opcode.GetByteSize(); }
+
+ /// Grants exclusive access to the disassembler and initializes it with the
+ /// given InstructionLLVMC and an optional ExecutionContext.
+ class DisassemblerScope {
+ std::shared_ptr<DisassemblerLLVMC> m_disasm;
+
+ public:
+ explicit DisassemblerScope(
+ InstructionLLVMC &i,
+ const lldb_private::ExecutionContext *exe_ctx = nullptr)
+ : m_disasm(i.m_disasm_wp.lock()) {
+ m_disasm->m_mutex.lock();
+ m_disasm->m_inst = &i;
+ m_disasm->m_exe_ctx = exe_ctx;
+ }
+ ~DisassemblerScope() { m_disasm->m_mutex.unlock(); }
+
+ /// Evaluates to true if this scope contains a valid disassembler.
+ operator bool() const { return static_cast<bool>(m_disasm); }
+
+ std::shared_ptr<DisassemblerLLVMC> operator->() { return m_disasm; }
+ };
+
+ static llvm::StringRef::const_iterator
+ ConsumeWhitespace(llvm::StringRef::const_iterator osi,
+ llvm::StringRef::const_iterator ose) {
+ while (osi != ose) {
+ switch (*osi) {
+ default:
+ return osi;
+ case ' ':
+ case '\t':
+ break;
+ }
+ ++osi;
+ }
+
+ return osi;
+ }
+
+ static std::pair<bool, llvm::StringRef::const_iterator>
+ ConsumeChar(llvm::StringRef::const_iterator osi, const char c,
+ llvm::StringRef::const_iterator ose) {
+ bool found = false;
+
+ osi = ConsumeWhitespace(osi, ose);
+ if (osi != ose && *osi == c) {
+ found = true;
+ ++osi;
+ }
+
+ return std::make_pair(found, osi);
+ }
+
+ static std::pair<Operand, llvm::StringRef::const_iterator>
+ ParseRegisterName(llvm::StringRef::const_iterator osi,
+ llvm::StringRef::const_iterator ose) {
+ Operand ret;
+ ret.m_type = Operand::Type::Register;
+ std::string str;
+
+ osi = ConsumeWhitespace(osi, ose);
+
+ while (osi != ose) {
+ if (*osi >= '0' && *osi <= '9') {
+ if (str.empty()) {
+ return std::make_pair(Operand(), osi);
+ } else {
+ str.push_back(*osi);
+ }
+ } else if (*osi >= 'a' && *osi <= 'z') {
+ str.push_back(*osi);
+ } else {
+ switch (*osi) {
+ default:
+ if (str.empty()) {
+ return std::make_pair(Operand(), osi);
+ } else {
+ ret.m_register = ConstString(str);
+ return std::make_pair(ret, osi);
+ }
+ case '%':
+ if (!str.empty()) {
+ return std::make_pair(Operand(), osi);
+ }
+ break;
+ }
+ }
+ ++osi;
+ }
+
+ ret.m_register = ConstString(str);
+ return std::make_pair(ret, osi);
+ }
+
+ static std::pair<Operand, llvm::StringRef::const_iterator>
+ ParseImmediate(llvm::StringRef::const_iterator osi,
+ llvm::StringRef::const_iterator ose) {
+ Operand ret;
+ ret.m_type = Operand::Type::Immediate;
+ std::string str;
+ bool is_hex = false;
+
+ osi = ConsumeWhitespace(osi, ose);
+
+ while (osi != ose) {
+ if (*osi >= '0' && *osi <= '9') {
+ str.push_back(*osi);
+ } else if (*osi >= 'a' && *osi <= 'f') {
+ if (is_hex) {
+ str.push_back(*osi);
+ } else {
+ return std::make_pair(Operand(), osi);
+ }
+ } else {
+ switch (*osi) {
+ default:
+ if (str.empty()) {
+ return std::make_pair(Operand(), osi);
+ } else {
+ ret.m_immediate = strtoull(str.c_str(), nullptr, 0);
+ return std::make_pair(ret, osi);
+ }
+ case 'x':
+ if (!str.compare("0")) {
+ is_hex = true;
+ str.push_back(*osi);
+ } else {
+ return std::make_pair(Operand(), osi);
+ }
+ break;
+ case '#':
+ case '$':
+ if (!str.empty()) {
+ return std::make_pair(Operand(), osi);
+ }
+ break;
+ case '-':
+ if (str.empty()) {
+ ret.m_negative = true;
+ } else {
+ return std::make_pair(Operand(), osi);
+ }
+ }
+ }
+ ++osi;
+ }
+
+ ret.m_immediate = strtoull(str.c_str(), nullptr, 0);
+ return std::make_pair(ret, osi);
+ }
+
+ // -0x5(%rax,%rax,2)
+ static std::pair<Operand, llvm::StringRef::const_iterator>
+ ParseIntelIndexedAccess(llvm::StringRef::const_iterator osi,
+ llvm::StringRef::const_iterator ose) {
+ std::pair<Operand, llvm::StringRef::const_iterator> offset_and_iterator =
+ ParseImmediate(osi, ose);
+ if (offset_and_iterator.first.IsValid()) {
+ osi = offset_and_iterator.second;
+ }
+
+ bool found = false;
+ std::tie(found, osi) = ConsumeChar(osi, '(', ose);
+ if (!found) {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::pair<Operand, llvm::StringRef::const_iterator> base_and_iterator =
+ ParseRegisterName(osi, ose);
+ if (base_and_iterator.first.IsValid()) {
+ osi = base_and_iterator.second;
+ } else {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::tie(found, osi) = ConsumeChar(osi, ',', ose);
+ if (!found) {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::pair<Operand, llvm::StringRef::const_iterator> index_and_iterator =
+ ParseRegisterName(osi, ose);
+ if (index_and_iterator.first.IsValid()) {
+ osi = index_and_iterator.second;
+ } else {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::tie(found, osi) = ConsumeChar(osi, ',', ose);
+ if (!found) {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::pair<Operand, llvm::StringRef::const_iterator>
+ multiplier_and_iterator = ParseImmediate(osi, ose);
+ if (index_and_iterator.first.IsValid()) {
+ osi = index_and_iterator.second;
+ } else {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::tie(found, osi) = ConsumeChar(osi, ')', ose);
+ if (!found) {
+ return std::make_pair(Operand(), osi);
+ }
+
+ Operand product;
+ product.m_type = Operand::Type::Product;
+ product.m_children.push_back(index_and_iterator.first);
+ product.m_children.push_back(multiplier_and_iterator.first);
+
+ Operand index;
+ index.m_type = Operand::Type::Sum;
+ index.m_children.push_back(base_and_iterator.first);
+ index.m_children.push_back(product);
+
+ if (offset_and_iterator.first.IsValid()) {
+ Operand offset;
+ offset.m_type = Operand::Type::Sum;
+ offset.m_children.push_back(offset_and_iterator.first);
+ offset.m_children.push_back(index);
+
+ Operand deref;
+ deref.m_type = Operand::Type::Dereference;
+ deref.m_children.push_back(offset);
+ return std::make_pair(deref, osi);
+ } else {
+ Operand deref;
+ deref.m_type = Operand::Type::Dereference;
+ deref.m_children.push_back(index);
+ return std::make_pair(deref, osi);
+ }
+ }
+
+ // -0x10(%rbp)
+ static std::pair<Operand, llvm::StringRef::const_iterator>
+ ParseIntelDerefAccess(llvm::StringRef::const_iterator osi,
+ llvm::StringRef::const_iterator ose) {
+ std::pair<Operand, llvm::StringRef::const_iterator> offset_and_iterator =
+ ParseImmediate(osi, ose);
+ if (offset_and_iterator.first.IsValid()) {
+ osi = offset_and_iterator.second;
+ }
+
+ bool found = false;
+ std::tie(found, osi) = ConsumeChar(osi, '(', ose);
+ if (!found) {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::pair<Operand, llvm::StringRef::const_iterator> base_and_iterator =
+ ParseRegisterName(osi, ose);
+ if (base_and_iterator.first.IsValid()) {
+ osi = base_and_iterator.second;
+ } else {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::tie(found, osi) = ConsumeChar(osi, ')', ose);
+ if (!found) {
+ return std::make_pair(Operand(), osi);
+ }
+
+ if (offset_and_iterator.first.IsValid()) {
+ Operand offset;
+ offset.m_type = Operand::Type::Sum;
+ offset.m_children.push_back(offset_and_iterator.first);
+ offset.m_children.push_back(base_and_iterator.first);
+
+ Operand deref;
+ deref.m_type = Operand::Type::Dereference;
+ deref.m_children.push_back(offset);
+ return std::make_pair(deref, osi);
+ } else {
+ Operand deref;
+ deref.m_type = Operand::Type::Dereference;
+ deref.m_children.push_back(base_and_iterator.first);
+ return std::make_pair(deref, osi);
+ }
+ }
+
+ // [sp, #8]!
+ static std::pair<Operand, llvm::StringRef::const_iterator>
+ ParseARMOffsetAccess(llvm::StringRef::const_iterator osi,
+ llvm::StringRef::const_iterator ose) {
+ bool found = false;
+ std::tie(found, osi) = ConsumeChar(osi, '[', ose);
+ if (!found) {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::pair<Operand, llvm::StringRef::const_iterator> base_and_iterator =
+ ParseRegisterName(osi, ose);
+ if (base_and_iterator.first.IsValid()) {
+ osi = base_and_iterator.second;
+ } else {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::tie(found, osi) = ConsumeChar(osi, ',', ose);
+ if (!found) {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::pair<Operand, llvm::StringRef::const_iterator> offset_and_iterator =
+ ParseImmediate(osi, ose);
+ if (offset_and_iterator.first.IsValid()) {
+ osi = offset_and_iterator.second;
+ }
+
+ std::tie(found, osi) = ConsumeChar(osi, ']', ose);
+ if (!found) {
+ return std::make_pair(Operand(), osi);
+ }
+
+ Operand offset;
+ offset.m_type = Operand::Type::Sum;
+ offset.m_children.push_back(offset_and_iterator.first);
+ offset.m_children.push_back(base_and_iterator.first);
+
+ Operand deref;
+ deref.m_type = Operand::Type::Dereference;
+ deref.m_children.push_back(offset);
+ return std::make_pair(deref, osi);
+ }
+
+ // [sp]
+ static std::pair<Operand, llvm::StringRef::const_iterator>
+ ParseARMDerefAccess(llvm::StringRef::const_iterator osi,
+ llvm::StringRef::const_iterator ose) {
+ bool found = false;
+ std::tie(found, osi) = ConsumeChar(osi, '[', ose);
+ if (!found) {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::pair<Operand, llvm::StringRef::const_iterator> base_and_iterator =
+ ParseRegisterName(osi, ose);
+ if (base_and_iterator.first.IsValid()) {
+ osi = base_and_iterator.second;
+ } else {
+ return std::make_pair(Operand(), osi);
+ }
+
+ std::tie(found, osi) = ConsumeChar(osi, ']', ose);
+ if (!found) {
+ return std::make_pair(Operand(), osi);
+ }
+
+ Operand deref;
+ deref.m_type = Operand::Type::Dereference;
+ deref.m_children.push_back(base_and_iterator.first);
+ return std::make_pair(deref, osi);
+ }
+
+ static void DumpOperand(const Operand &op, Stream &s) {
+ switch (op.m_type) {
+ case Operand::Type::Dereference:
+ s.PutCString("*");
+ DumpOperand(op.m_children[0], s);
+ break;
+ case Operand::Type::Immediate:
+ if (op.m_negative) {
+ s.PutCString("-");
+ }
+ s.PutCString(llvm::to_string(op.m_immediate));
+ break;
+ case Operand::Type::Invalid:
+ s.PutCString("Invalid");
+ break;
+ case Operand::Type::Product:
+ s.PutCString("(");
+ DumpOperand(op.m_children[0], s);
+ s.PutCString("*");
+ DumpOperand(op.m_children[1], s);
+ s.PutCString(")");
+ break;
+ case Operand::Type::Register:
+ s.PutCString(op.m_register.AsCString());
+ break;
+ case Operand::Type::Sum:
+ s.PutCString("(");
+ DumpOperand(op.m_children[0], s);
+ s.PutCString("+");
+ DumpOperand(op.m_children[1], s);
+ s.PutCString(")");
+ break;
+ }
+ }
+
+ bool ParseOperands(
+ llvm::SmallVectorImpl<Instruction::Operand> &operands) override {
+ const char *operands_string = GetOperands(nullptr);
+
+ if (!operands_string) {
+ return false;
+ }
+
+ llvm::StringRef operands_ref(operands_string);
+
+ llvm::StringRef::const_iterator osi = operands_ref.begin();
+ llvm::StringRef::const_iterator ose = operands_ref.end();
+
+ while (osi != ose) {
+ Operand operand;
+ llvm::StringRef::const_iterator iter;
+
+ if ((std::tie(operand, iter) = ParseIntelIndexedAccess(osi, ose),
+ operand.IsValid()) ||
+ (std::tie(operand, iter) = ParseIntelDerefAccess(osi, ose),
+ operand.IsValid()) ||
+ (std::tie(operand, iter) = ParseARMOffsetAccess(osi, ose),
+ operand.IsValid()) ||
+ (std::tie(operand, iter) = ParseARMDerefAccess(osi, ose),
+ operand.IsValid()) ||
+ (std::tie(operand, iter) = ParseRegisterName(osi, ose),
+ operand.IsValid()) ||
+ (std::tie(operand, iter) = ParseImmediate(osi, ose),
+ operand.IsValid())) {
+ osi = iter;
+ operands.push_back(operand);
+ } else {
+ return false;
+ }
+
+ std::pair<bool, llvm::StringRef::const_iterator> found_and_iter =
+ ConsumeChar(osi, ',', ose);
+ if (found_and_iter.first) {
+ osi = found_and_iter.second;
+ }
+
+ osi = ConsumeWhitespace(osi, ose);
+ }
+
+ DisassemblerSP disasm_sp = m_disasm_wp.lock();
+
+ if (disasm_sp && operands.size() > 1) {
+ // TODO tie this into the MC Disassembler's notion of clobbers.
+ switch (disasm_sp->GetArchitecture().GetMachine()) {
+ default:
+ break;
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ operands[operands.size() - 1].m_clobbered = true;
+ break;
+ case llvm::Triple::arm:
+ operands[0].m_clobbered = true;
+ break;
+ }
+ }
+
+ if (Log *log =
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)) {
+ StreamString ss;
+
+ ss.Printf("[%s] expands to %zu operands:\n", operands_string,
+ operands.size());
+ for (const Operand &operand : operands) {
+ ss.PutCString(" ");
+ DumpOperand(operand, ss);
+ ss.PutCString("\n");
+ }
+
+ log->PutString(ss.GetString());
+ }
+
+ return true;
+ }
+
+ bool IsCall() override {
+ if (m_is_call == eLazyBoolCalculate) {
+ DisassemblerScope disasm(*this);
+ if (disasm) {
+ DataExtractor data;
+ if (m_opcode.GetData(data)) {
+ bool is_alternate_isa;
+ lldb::addr_t pc = m_address.GetFileAddress();
+
+ DisassemblerLLVMC::MCDisasmInstance *mc_disasm_ptr =
+ GetDisasmToUse(is_alternate_isa, disasm);
+ const uint8_t *opcode_data = data.GetDataStart();
+ const size_t opcode_data_len = data.GetByteSize();
+ llvm::MCInst inst;
+ const size_t inst_size =
+ mc_disasm_ptr->GetMCInst(opcode_data, opcode_data_len, pc, inst);
+ if (inst_size == 0) {
+ m_is_call = eLazyBoolNo;
+ } else {
+ if (mc_disasm_ptr->IsCall(inst))
+ m_is_call = eLazyBoolYes;
+ else
+ m_is_call = eLazyBoolNo;
+ }
+ }
+ }
+ }
+ return m_is_call == eLazyBoolYes;
+ }
+
+protected:
+ std::weak_ptr<DisassemblerLLVMC> m_disasm_wp;
+ LazyBool m_does_branch;
+ LazyBool m_has_delay_slot;
+ LazyBool m_is_call;
+ bool m_is_valid;
+ bool m_using_file_addr;
+
+private:
+ DisassemblerLLVMC::MCDisasmInstance *
+ GetDisasmToUse(bool &is_alternate_isa, DisassemblerScope &disasm) {
+ is_alternate_isa = false;
+ if (disasm) {
+ if (disasm->m_alternate_disasm_up) {
+ const AddressClass address_class = GetAddressClass();
+
+ if (address_class == AddressClass::eCodeAlternateISA) {
+ is_alternate_isa = true;
+ return disasm->m_alternate_disasm_up.get();
+ }
+ }
+ return disasm->m_disasm_up.get();
+ }
+ return nullptr;
+ }
+};
+
+std::unique_ptr<DisassemblerLLVMC::MCDisasmInstance>
+DisassemblerLLVMC::MCDisasmInstance::Create(const char *triple, const char *cpu,
+ const char *features_str,
+ unsigned flavor,
+ DisassemblerLLVMC &owner) {
+ using Instance = std::unique_ptr<DisassemblerLLVMC::MCDisasmInstance>;
+
+ std::string Status;
+ const llvm::Target *curr_target =
+ llvm::TargetRegistry::lookupTarget(triple, Status);
+ if (!curr_target)
+ return Instance();
+
+ std::unique_ptr<llvm::MCInstrInfo> instr_info_up(
+ curr_target->createMCInstrInfo());
+ if (!instr_info_up)
+ return Instance();
+
+ std::unique_ptr<llvm::MCRegisterInfo> reg_info_up(
+ curr_target->createMCRegInfo(triple));
+ if (!reg_info_up)
+ return Instance();
+
+ std::unique_ptr<llvm::MCSubtargetInfo> subtarget_info_up(
+ curr_target->createMCSubtargetInfo(triple, cpu, features_str));
+ if (!subtarget_info_up)
+ return Instance();
+
+ std::unique_ptr<llvm::MCAsmInfo> asm_info_up(
+ curr_target->createMCAsmInfo(*reg_info_up, triple));
+ if (!asm_info_up)
+ return Instance();
+
+ std::unique_ptr<llvm::MCContext> context_up(
+ new llvm::MCContext(asm_info_up.get(), reg_info_up.get(), nullptr));
+ if (!context_up)
+ return Instance();
+
+ std::unique_ptr<llvm::MCDisassembler> disasm_up(
+ curr_target->createMCDisassembler(*subtarget_info_up, *context_up));
+ if (!disasm_up)
+ return Instance();
+
+ std::unique_ptr<llvm::MCRelocationInfo> rel_info_up(
+ curr_target->createMCRelocationInfo(triple, *context_up));
+ if (!rel_info_up)
+ return Instance();
+
+ std::unique_ptr<llvm::MCSymbolizer> symbolizer_up(
+ curr_target->createMCSymbolizer(
+ triple, nullptr, DisassemblerLLVMC::SymbolLookupCallback, &owner,
+ context_up.get(), std::move(rel_info_up)));
+ disasm_up->setSymbolizer(std::move(symbolizer_up));
+
+ unsigned asm_printer_variant =
+ flavor == ~0U ? asm_info_up->getAssemblerDialect() : flavor;
+
+ std::unique_ptr<llvm::MCInstPrinter> instr_printer_up(
+ curr_target->createMCInstPrinter(llvm::Triple{triple},
+ asm_printer_variant, *asm_info_up,
+ *instr_info_up, *reg_info_up));
+ if (!instr_printer_up)
+ return Instance();
+
+ return Instance(
+ new MCDisasmInstance(std::move(instr_info_up), std::move(reg_info_up),
+ std::move(subtarget_info_up), std::move(asm_info_up),
+ std::move(context_up), std::move(disasm_up),
+ std::move(instr_printer_up)));
+}
+
+DisassemblerLLVMC::MCDisasmInstance::MCDisasmInstance(
+ std::unique_ptr<llvm::MCInstrInfo> &&instr_info_up,
+ std::unique_ptr<llvm::MCRegisterInfo> &&reg_info_up,
+ std::unique_ptr<llvm::MCSubtargetInfo> &&subtarget_info_up,
+ std::unique_ptr<llvm::MCAsmInfo> &&asm_info_up,
+ std::unique_ptr<llvm::MCContext> &&context_up,
+ std::unique_ptr<llvm::MCDisassembler> &&disasm_up,
+ std::unique_ptr<llvm::MCInstPrinter> &&instr_printer_up)
+ : m_instr_info_up(std::move(instr_info_up)),
+ m_reg_info_up(std::move(reg_info_up)),
+ m_subtarget_info_up(std::move(subtarget_info_up)),
+ m_asm_info_up(std::move(asm_info_up)),
+ m_context_up(std::move(context_up)), m_disasm_up(std::move(disasm_up)),
+ m_instr_printer_up(std::move(instr_printer_up)) {
+ assert(m_instr_info_up && m_reg_info_up && m_subtarget_info_up &&
+ m_asm_info_up && m_context_up && m_disasm_up && m_instr_printer_up);
+}
+
+uint64_t DisassemblerLLVMC::MCDisasmInstance::GetMCInst(
+ const uint8_t *opcode_data, size_t opcode_data_len, lldb::addr_t pc,
+ llvm::MCInst &mc_inst) const {
+ llvm::ArrayRef<uint8_t> data(opcode_data, opcode_data_len);
+ llvm::MCDisassembler::DecodeStatus status;
+
+ uint64_t new_inst_size;
+ status = m_disasm_up->getInstruction(mc_inst, new_inst_size, data, pc,
+ llvm::nulls(), llvm::nulls());
+ if (status == llvm::MCDisassembler::Success)
+ return new_inst_size;
+ else
+ return 0;
+}
+
+void DisassemblerLLVMC::MCDisasmInstance::PrintMCInst(
+ llvm::MCInst &mc_inst, std::string &inst_string,
+ std::string &comments_string) {
+ llvm::raw_string_ostream inst_stream(inst_string);
+ llvm::raw_string_ostream comments_stream(comments_string);
+
+ m_instr_printer_up->setCommentStream(comments_stream);
+ m_instr_printer_up->printInst(&mc_inst, inst_stream, llvm::StringRef(),
+ *m_subtarget_info_up);
+ m_instr_printer_up->setCommentStream(llvm::nulls());
+ comments_stream.flush();
+
+ static std::string g_newlines("\r\n");
+
+ for (size_t newline_pos = 0;
+ (newline_pos = comments_string.find_first_of(g_newlines, newline_pos)) !=
+ comments_string.npos;
+ /**/) {
+ comments_string.replace(comments_string.begin() + newline_pos,
+ comments_string.begin() + newline_pos + 1, 1, ' ');
+ }
+}
+
+void DisassemblerLLVMC::MCDisasmInstance::SetStyle(
+ bool use_hex_immed, HexImmediateStyle hex_style) {
+ m_instr_printer_up->setPrintImmHex(use_hex_immed);
+ switch (hex_style) {
+ case eHexStyleC:
+ m_instr_printer_up->setPrintHexStyle(llvm::HexStyle::C);
+ break;
+ case eHexStyleAsm:
+ m_instr_printer_up->setPrintHexStyle(llvm::HexStyle::Asm);
+ break;
+ }
+}
+
+bool DisassemblerLLVMC::MCDisasmInstance::CanBranch(
+ llvm::MCInst &mc_inst) const {
+ return m_instr_info_up->get(mc_inst.getOpcode())
+ .mayAffectControlFlow(mc_inst, *m_reg_info_up);
+}
+
+bool DisassemblerLLVMC::MCDisasmInstance::HasDelaySlot(
+ llvm::MCInst &mc_inst) const {
+ return m_instr_info_up->get(mc_inst.getOpcode()).hasDelaySlot();
+}
+
+bool DisassemblerLLVMC::MCDisasmInstance::IsCall(llvm::MCInst &mc_inst) const {
+ return m_instr_info_up->get(mc_inst.getOpcode()).isCall();
+}
+
+DisassemblerLLVMC::DisassemblerLLVMC(const ArchSpec &arch,
+ const char *flavor_string)
+ : Disassembler(arch, flavor_string), m_exe_ctx(nullptr), m_inst(nullptr),
+ m_data_from_file(false) {
+ if (!FlavorValidForArchSpec(arch, m_flavor.c_str())) {
+ m_flavor.assign("default");
+ }
+
+ unsigned flavor = ~0U;
+ llvm::Triple triple = arch.GetTriple();
+
+ // So far the only supported flavor is "intel" on x86. The base class will
+ // set this correctly coming in.
+ if (triple.getArch() == llvm::Triple::x86 ||
+ triple.getArch() == llvm::Triple::x86_64) {
+ if (m_flavor == "intel") {
+ flavor = 1;
+ } else if (m_flavor == "att") {
+ flavor = 0;
+ }
+ }
+
+ ArchSpec thumb_arch(arch);
+ if (triple.getArch() == llvm::Triple::arm) {
+ std::string thumb_arch_name(thumb_arch.GetTriple().getArchName().str());
+ // Replace "arm" with "thumb" so we get all thumb variants correct
+ if (thumb_arch_name.size() > 3) {
+ thumb_arch_name.erase(0, 3);
+ thumb_arch_name.insert(0, "thumb");
+ } else {
+ thumb_arch_name = "thumbv8.2a";
+ }
+ thumb_arch.GetTriple().setArchName(llvm::StringRef(thumb_arch_name));
+ }
+
+ // If no sub architecture specified then use the most recent arm architecture
+ // so the disassembler will return all instruction. Without it we will see a
+ // lot of unknow opcode in case the code uses instructions which are not
+ // available in the oldest arm version (used when no sub architecture is
+ // specified)
+ if (triple.getArch() == llvm::Triple::arm &&
+ triple.getSubArch() == llvm::Triple::NoSubArch)
+ triple.setArchName("armv8.2a");
+
+ std::string features_str = "";
+ const char *triple_str = triple.getTriple().c_str();
+
+ // ARM Cortex M0-M7 devices only execute thumb instructions
+ if (arch.IsAlwaysThumbInstructions()) {
+ triple_str = thumb_arch.GetTriple().getTriple().c_str();
+ features_str += "+fp-armv8,";
+ }
+
+ const char *cpu = "";
+
+ switch (arch.GetCore()) {
+ case ArchSpec::eCore_mips32:
+ case ArchSpec::eCore_mips32el:
+ cpu = "mips32";
+ break;
+ case ArchSpec::eCore_mips32r2:
+ case ArchSpec::eCore_mips32r2el:
+ cpu = "mips32r2";
+ break;
+ case ArchSpec::eCore_mips32r3:
+ case ArchSpec::eCore_mips32r3el:
+ cpu = "mips32r3";
+ break;
+ case ArchSpec::eCore_mips32r5:
+ case ArchSpec::eCore_mips32r5el:
+ cpu = "mips32r5";
+ break;
+ case ArchSpec::eCore_mips32r6:
+ case ArchSpec::eCore_mips32r6el:
+ cpu = "mips32r6";
+ break;
+ case ArchSpec::eCore_mips64:
+ case ArchSpec::eCore_mips64el:
+ cpu = "mips64";
+ break;
+ case ArchSpec::eCore_mips64r2:
+ case ArchSpec::eCore_mips64r2el:
+ cpu = "mips64r2";
+ break;
+ case ArchSpec::eCore_mips64r3:
+ case ArchSpec::eCore_mips64r3el:
+ cpu = "mips64r3";
+ break;
+ case ArchSpec::eCore_mips64r5:
+ case ArchSpec::eCore_mips64r5el:
+ cpu = "mips64r5";
+ break;
+ case ArchSpec::eCore_mips64r6:
+ case ArchSpec::eCore_mips64r6el:
+ cpu = "mips64r6";
+ break;
+ default:
+ cpu = "";
+ break;
+ }
+
+ if (arch.IsMIPS()) {
+ uint32_t arch_flags = arch.GetFlags();
+ if (arch_flags & ArchSpec::eMIPSAse_msa)
+ features_str += "+msa,";
+ if (arch_flags & ArchSpec::eMIPSAse_dsp)
+ features_str += "+dsp,";
+ if (arch_flags & ArchSpec::eMIPSAse_dspr2)
+ features_str += "+dspr2,";
+ }
+
+ // If any AArch64 variant, enable the ARMv8.5 ISA with SVE extensions so we
+ // can disassemble newer instructions.
+ if (triple.getArch() == llvm::Triple::aarch64)
+ features_str += "+v8.5a,+sve2";
+
+ if (triple.getArch() == llvm::Triple::aarch64
+ && triple.getVendor() == llvm::Triple::Apple) {
+ cpu = "apple-latest";
+ }
+
+ // We use m_disasm_up.get() to tell whether we are valid or not, so if this
+ // isn't good for some reason, we won't be valid and FindPlugin will fail and
+ // we won't get used.
+ m_disasm_up = MCDisasmInstance::Create(triple_str, cpu, features_str.c_str(),
+ flavor, *this);
+
+ llvm::Triple::ArchType llvm_arch = triple.getArch();
+
+ // For arm CPUs that can execute arm or thumb instructions, also create a
+ // thumb instruction disassembler.
+ if (llvm_arch == llvm::Triple::arm) {
+ std::string thumb_triple(thumb_arch.GetTriple().getTriple());
+ m_alternate_disasm_up =
+ MCDisasmInstance::Create(thumb_triple.c_str(), "", features_str.c_str(),
+ flavor, *this);
+ if (!m_alternate_disasm_up)
+ m_disasm_up.reset();
+
+ } else if (arch.IsMIPS()) {
+ /* Create alternate disassembler for MIPS16 and microMIPS */
+ uint32_t arch_flags = arch.GetFlags();
+ if (arch_flags & ArchSpec::eMIPSAse_mips16)
+ features_str += "+mips16,";
+ else if (arch_flags & ArchSpec::eMIPSAse_micromips)
+ features_str += "+micromips,";
+
+ m_alternate_disasm_up = MCDisasmInstance::Create(
+ triple_str, cpu, features_str.c_str(), flavor, *this);
+ if (!m_alternate_disasm_up)
+ m_disasm_up.reset();
+ }
+}
+
+DisassemblerLLVMC::~DisassemblerLLVMC() = default;
+
+Disassembler *DisassemblerLLVMC::CreateInstance(const ArchSpec &arch,
+ const char *flavor) {
+ if (arch.GetTriple().getArch() != llvm::Triple::UnknownArch) {
+ std::unique_ptr<DisassemblerLLVMC> disasm_up(
+ new DisassemblerLLVMC(arch, flavor));
+
+ if (disasm_up.get() && disasm_up->IsValid())
+ return disasm_up.release();
+ }
+ return nullptr;
+}
+
+size_t DisassemblerLLVMC::DecodeInstructions(const Address &base_addr,
+ const DataExtractor &data,
+ lldb::offset_t data_offset,
+ size_t num_instructions,
+ bool append, bool data_from_file) {
+ if (!append)
+ m_instruction_list.Clear();
+
+ if (!IsValid())
+ return 0;
+
+ m_data_from_file = data_from_file;
+ uint32_t data_cursor = data_offset;
+ const size_t data_byte_size = data.GetByteSize();
+ uint32_t instructions_parsed = 0;
+ Address inst_addr(base_addr);
+
+ while (data_cursor < data_byte_size &&
+ instructions_parsed < num_instructions) {
+
+ AddressClass address_class = AddressClass::eCode;
+
+ if (m_alternate_disasm_up)
+ address_class = inst_addr.GetAddressClass();
+
+ InstructionSP inst_sp(
+ new InstructionLLVMC(*this, inst_addr, address_class));
+
+ if (!inst_sp)
+ break;
+
+ uint32_t inst_size = inst_sp->Decode(*this, data, data_cursor);
+
+ if (inst_size == 0)
+ break;
+
+ m_instruction_list.Append(inst_sp);
+ data_cursor += inst_size;
+ inst_addr.Slide(inst_size);
+ instructions_parsed++;
+ }
+
+ return data_cursor - data_offset;
+}
+
+void DisassemblerLLVMC::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ "Disassembler that uses LLVM MC to disassemble "
+ "i386, x86_64, ARM, and ARM64.",
+ CreateInstance);
+
+ llvm::InitializeAllTargetInfos();
+ llvm::InitializeAllTargetMCs();
+ llvm::InitializeAllAsmParsers();
+ llvm::InitializeAllDisassemblers();
+}
+
+void DisassemblerLLVMC::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+ConstString DisassemblerLLVMC::GetPluginNameStatic() {
+ static ConstString g_name("llvm-mc");
+ return g_name;
+}
+
+int DisassemblerLLVMC::OpInfoCallback(void *disassembler, uint64_t pc,
+ uint64_t offset, uint64_t size,
+ int tag_type, void *tag_bug) {
+ return static_cast<DisassemblerLLVMC *>(disassembler)
+ ->OpInfo(pc, offset, size, tag_type, tag_bug);
+}
+
+const char *DisassemblerLLVMC::SymbolLookupCallback(void *disassembler,
+ uint64_t value,
+ uint64_t *type, uint64_t pc,
+ const char **name) {
+ return static_cast<DisassemblerLLVMC *>(disassembler)
+ ->SymbolLookup(value, type, pc, name);
+}
+
+bool DisassemblerLLVMC::FlavorValidForArchSpec(
+ const lldb_private::ArchSpec &arch, const char *flavor) {
+ llvm::Triple triple = arch.GetTriple();
+ if (flavor == nullptr || strcmp(flavor, "default") == 0)
+ return true;
+
+ if (triple.getArch() == llvm::Triple::x86 ||
+ triple.getArch() == llvm::Triple::x86_64) {
+ return strcmp(flavor, "intel") == 0 || strcmp(flavor, "att") == 0;
+ } else
+ return false;
+}
+
+bool DisassemblerLLVMC::IsValid() const { return m_disasm_up.operator bool(); }
+
+int DisassemblerLLVMC::OpInfo(uint64_t PC, uint64_t Offset, uint64_t Size,
+ int tag_type, void *tag_bug) {
+ switch (tag_type) {
+ default:
+ break;
+ case 1:
+ memset(tag_bug, 0, sizeof(::LLVMOpInfo1));
+ break;
+ }
+ return 0;
+}
+
+const char *DisassemblerLLVMC::SymbolLookup(uint64_t value, uint64_t *type_ptr,
+ uint64_t pc, const char **name) {
+ if (*type_ptr) {
+ if (m_exe_ctx && m_inst) {
+ // std::string remove_this_prior_to_checkin;
+ Target *target = m_exe_ctx ? m_exe_ctx->GetTargetPtr() : nullptr;
+ Address value_so_addr;
+ Address pc_so_addr;
+ if (m_inst->UsingFileAddress()) {
+ ModuleSP module_sp(m_inst->GetAddress().GetModule());
+ if (module_sp) {
+ module_sp->ResolveFileAddress(value, value_so_addr);
+ module_sp->ResolveFileAddress(pc, pc_so_addr);
+ }
+ } else if (target && !target->GetSectionLoadList().IsEmpty()) {
+ target->GetSectionLoadList().ResolveLoadAddress(value, value_so_addr);
+ target->GetSectionLoadList().ResolveLoadAddress(pc, pc_so_addr);
+ }
+
+ SymbolContext sym_ctx;
+ const SymbolContextItem resolve_scope =
+ eSymbolContextFunction | eSymbolContextSymbol;
+ if (pc_so_addr.IsValid() && pc_so_addr.GetModule()) {
+ pc_so_addr.GetModule()->ResolveSymbolContextForAddress(
+ pc_so_addr, resolve_scope, sym_ctx);
+ }
+
+ if (value_so_addr.IsValid() && value_so_addr.GetSection()) {
+ StreamString ss;
+
+ bool format_omitting_current_func_name = false;
+ if (sym_ctx.symbol || sym_ctx.function) {
+ AddressRange range;
+ if (sym_ctx.GetAddressRange(resolve_scope, 0, false, range) &&
+ range.GetBaseAddress().IsValid() &&
+ range.ContainsLoadAddress(value_so_addr, target)) {
+ format_omitting_current_func_name = true;
+ }
+ }
+
+ // If the "value" address (the target address we're symbolicating) is
+ // inside the same SymbolContext as the current instruction pc
+ // (pc_so_addr), don't print the full function name - just print it
+ // with DumpStyleNoFunctionName style, e.g. "<+36>".
+ if (format_omitting_current_func_name) {
+ value_so_addr.Dump(&ss, target, Address::DumpStyleNoFunctionName,
+ Address::DumpStyleSectionNameOffset);
+ } else {
+ value_so_addr.Dump(
+ &ss, target,
+ Address::DumpStyleResolvedDescriptionNoFunctionArguments,
+ Address::DumpStyleSectionNameOffset);
+ }
+
+ if (!ss.GetString().empty()) {
+ // If Address::Dump returned a multi-line description, most commonly
+ // seen when we have multiple levels of inlined functions at an
+ // address, only show the first line.
+ std::string str = ss.GetString();
+ size_t first_eol_char = str.find_first_of("\r\n");
+ if (first_eol_char != std::string::npos) {
+ str.erase(first_eol_char);
+ }
+ m_inst->AppendComment(str);
+ }
+ }
+ }
+ }
+
+ *type_ptr = LLVMDisassembler_ReferenceType_InOut_None;
+ *name = nullptr;
+ return nullptr;
+}
+
+// PluginInterface protocol
+ConstString DisassemblerLLVMC::GetPluginName() { return GetPluginNameStatic(); }
+
+uint32_t DisassemblerLLVMC::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h b/contrib/llvm-project/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h
new file mode 100644
index 000000000000..fd5775056d33
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h
@@ -0,0 +1,85 @@
+//===-- DisassemblerLLVMC.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 liblldb_DisassemblerLLVMC_h_
+#define liblldb_DisassemblerLLVMC_h_
+
+#include <memory>
+#include <mutex>
+#include <string>
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/PluginManager.h"
+
+class InstructionLLVMC;
+
+class DisassemblerLLVMC : public lldb_private::Disassembler {
+public:
+ DisassemblerLLVMC(const lldb_private::ArchSpec &arch,
+ const char *flavor /* = NULL */);
+
+ ~DisassemblerLLVMC() override;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static lldb_private::Disassembler *
+ CreateInstance(const lldb_private::ArchSpec &arch, const char *flavor);
+
+ size_t DecodeInstructions(const lldb_private::Address &base_addr,
+ const lldb_private::DataExtractor &data,
+ lldb::offset_t data_offset, size_t num_instructions,
+ bool append, bool data_from_file) override;
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ friend class InstructionLLVMC;
+
+ bool FlavorValidForArchSpec(const lldb_private::ArchSpec &arch,
+ const char *flavor) override;
+
+ bool IsValid() const;
+
+ int OpInfo(uint64_t PC, uint64_t Offset, uint64_t Size, int TagType,
+ void *TagBug);
+
+ const char *SymbolLookup(uint64_t ReferenceValue, uint64_t *ReferenceType,
+ uint64_t ReferencePC, const char **ReferenceName);
+
+ static int OpInfoCallback(void *DisInfo, uint64_t PC, uint64_t Offset,
+ uint64_t Size, int TagType, void *TagBug);
+
+ static const char *SymbolLookupCallback(void *DisInfo,
+ uint64_t ReferenceValue,
+ uint64_t *ReferenceType,
+ uint64_t ReferencePC,
+ const char **ReferenceName);
+
+ const lldb_private::ExecutionContext *m_exe_ctx;
+ InstructionLLVMC *m_inst;
+ std::mutex m_mutex;
+ bool m_data_from_file;
+
+ // Since we need to make two actual MC Disassemblers for ARM (ARM & THUMB),
+ // and there's a bit of goo to set up and own in the MC disassembler world,
+ // this class was added to manage the actual disassemblers.
+ class MCDisasmInstance;
+ std::unique_ptr<MCDisasmInstance> m_disasm_up;
+ std::unique_ptr<MCDisasmInstance> m_alternate_disasm_up;
+};
+
+#endif // liblldb_DisassemblerLLVM_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.cpp b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.cpp
new file mode 100644
index 000000000000..23c8416f4986
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.cpp
@@ -0,0 +1,618 @@
+//===-- DynamicLoaderHexagonDYLD.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 "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlanRunToAddress.h"
+#include "lldb/Utility/Log.h"
+
+#include "DynamicLoaderHexagonDYLD.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Aidan 21/05/2014
+//
+// Notes about hexagon dynamic loading:
+//
+// When we connect to a target we find the dyld breakpoint address. We put
+// a
+// breakpoint there with a callback 'RendezvousBreakpointHit()'.
+//
+// It is possible to find the dyld structure address from the ELF symbol
+// table,
+// but in the case of the simulator it has not been initialized before the
+// target calls dlinit().
+//
+// We can only safely parse the dyld structure after we hit the dyld
+// breakpoint
+// since at that time we know dlinit() must have been called.
+//
+
+// Find the load address of a symbol
+static lldb::addr_t findSymbolAddress(Process *proc, ConstString findName) {
+ assert(proc != nullptr);
+
+ ModuleSP module = proc->GetTarget().GetExecutableModule();
+ assert(module.get() != nullptr);
+
+ ObjectFile *exe = module->GetObjectFile();
+ assert(exe != nullptr);
+
+ lldb_private::Symtab *symtab = exe->GetSymtab();
+ assert(symtab != nullptr);
+
+ for (size_t i = 0; i < symtab->GetNumSymbols(); i++) {
+ const Symbol *sym = symtab->SymbolAtIndex(i);
+ assert(sym != nullptr);
+ ConstString symName = sym->GetName();
+
+ if (ConstString::Compare(findName, symName) == 0) {
+ Address addr = sym->GetAddress();
+ return addr.GetLoadAddress(&proc->GetTarget());
+ }
+ }
+ return LLDB_INVALID_ADDRESS;
+}
+
+void DynamicLoaderHexagonDYLD::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void DynamicLoaderHexagonDYLD::Terminate() {}
+
+lldb_private::ConstString DynamicLoaderHexagonDYLD::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+lldb_private::ConstString DynamicLoaderHexagonDYLD::GetPluginNameStatic() {
+ static ConstString g_name("hexagon-dyld");
+ return g_name;
+}
+
+const char *DynamicLoaderHexagonDYLD::GetPluginDescriptionStatic() {
+ return "Dynamic loader plug-in that watches for shared library "
+ "loads/unloads in Hexagon processes.";
+}
+
+uint32_t DynamicLoaderHexagonDYLD::GetPluginVersion() { return 1; }
+
+DynamicLoader *DynamicLoaderHexagonDYLD::CreateInstance(Process *process,
+ bool force) {
+ bool create = force;
+ if (!create) {
+ const llvm::Triple &triple_ref =
+ process->GetTarget().GetArchitecture().GetTriple();
+ if (triple_ref.getArch() == llvm::Triple::hexagon)
+ create = true;
+ }
+
+ if (create)
+ return new DynamicLoaderHexagonDYLD(process);
+ return nullptr;
+}
+
+DynamicLoaderHexagonDYLD::DynamicLoaderHexagonDYLD(Process *process)
+ : DynamicLoader(process), m_rendezvous(process),
+ m_load_offset(LLDB_INVALID_ADDRESS), m_entry_point(LLDB_INVALID_ADDRESS),
+ m_dyld_bid(LLDB_INVALID_BREAK_ID) {}
+
+DynamicLoaderHexagonDYLD::~DynamicLoaderHexagonDYLD() {
+ if (m_dyld_bid != LLDB_INVALID_BREAK_ID) {
+ m_process->GetTarget().RemoveBreakpointByID(m_dyld_bid);
+ m_dyld_bid = LLDB_INVALID_BREAK_ID;
+ }
+}
+
+void DynamicLoaderHexagonDYLD::DidAttach() {
+ ModuleSP executable;
+ addr_t load_offset;
+
+ executable = GetTargetExecutable();
+
+ // Find the difference between the desired load address in the elf file and
+ // the real load address in memory
+ load_offset = ComputeLoadOffset();
+
+ // Check that there is a valid executable
+ if (executable.get() == nullptr)
+ return;
+
+ // Disable JIT for hexagon targets because its not supported
+ m_process->SetCanJIT(false);
+
+ // Enable Interpreting of function call expressions
+ m_process->SetCanInterpretFunctionCalls(true);
+
+ // Add the current executable to the module list
+ ModuleList module_list;
+ module_list.Append(executable);
+
+ // Map the loaded sections of this executable
+ if (load_offset != LLDB_INVALID_ADDRESS)
+ UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_offset, true);
+
+ // AD: confirm this?
+ // Load into LLDB all of the currently loaded executables in the stub
+ LoadAllCurrentModules();
+
+ // AD: confirm this?
+ // Callback for the target to give it the loaded module list
+ m_process->GetTarget().ModulesDidLoad(module_list);
+
+ // Try to set a breakpoint at the rendezvous breakpoint. DidLaunch uses
+ // ProbeEntry() instead. That sets a breakpoint, at the dyld breakpoint
+ // address, with a callback so that when hit, the dyld structure can be
+ // parsed.
+ if (!SetRendezvousBreakpoint()) {
+ // fail
+ }
+}
+
+void DynamicLoaderHexagonDYLD::DidLaunch() {}
+
+/// Checks to see if the target module has changed, updates the target
+/// accordingly and returns the target executable module.
+ModuleSP DynamicLoaderHexagonDYLD::GetTargetExecutable() {
+ Target &target = m_process->GetTarget();
+ ModuleSP executable = target.GetExecutableModule();
+
+ // There is no executable
+ if (!executable.get())
+ return executable;
+
+ // The target executable file does not exits
+ if (!FileSystem::Instance().Exists(executable->GetFileSpec()))
+ return executable;
+
+ // Prep module for loading
+ ModuleSpec module_spec(executable->GetFileSpec(),
+ executable->GetArchitecture());
+ ModuleSP module_sp(new Module(module_spec));
+
+ // Check if the executable has changed and set it to the target executable if
+ // they differ.
+ if (module_sp.get() && module_sp->GetUUID().IsValid() &&
+ executable->GetUUID().IsValid()) {
+ // if the executable has changed ??
+ if (module_sp->GetUUID() != executable->GetUUID())
+ executable.reset();
+ } else if (executable->FileHasChanged())
+ executable.reset();
+
+ if (executable.get())
+ return executable;
+
+ // TODO: What case is this code used?
+ executable = target.GetOrCreateModule(module_spec, true /* notify */);
+ if (executable.get() != target.GetExecutableModulePointer()) {
+ // Don't load dependent images since we are in dyld where we will know and
+ // find out about all images that are loaded
+ target.SetExecutableModule(executable, eLoadDependentsNo);
+ }
+
+ return executable;
+}
+
+// AD: Needs to be updated?
+Status DynamicLoaderHexagonDYLD::CanLoadImage() { return Status(); }
+
+void DynamicLoaderHexagonDYLD::UpdateLoadedSections(ModuleSP module,
+ addr_t link_map_addr,
+ addr_t base_addr,
+ bool base_addr_is_offset) {
+ Target &target = m_process->GetTarget();
+ const SectionList *sections = GetSectionListFromModule(module);
+
+ assert(sections && "SectionList missing from loaded module.");
+
+ m_loaded_modules[module] = link_map_addr;
+
+ const size_t num_sections = sections->GetSize();
+
+ for (unsigned i = 0; i < num_sections; ++i) {
+ SectionSP section_sp(sections->GetSectionAtIndex(i));
+ lldb::addr_t new_load_addr = section_sp->GetFileAddress() + base_addr;
+
+ // AD: 02/05/14
+ // since our memory map starts from address 0, we must not ignore
+ // sections that load to address 0. This violates the reference
+ // ELF spec, however is used for Hexagon.
+
+ // If the file address of the section is zero then this is not an
+ // allocatable/loadable section (property of ELF sh_addr). Skip it.
+ // if (new_load_addr == base_addr)
+ // continue;
+
+ target.SetSectionLoadAddress(section_sp, new_load_addr);
+ }
+}
+
+/// Removes the loaded sections from the target in \p module.
+///
+/// \param module The module to traverse.
+void DynamicLoaderHexagonDYLD::UnloadSections(const ModuleSP module) {
+ Target &target = m_process->GetTarget();
+ const SectionList *sections = GetSectionListFromModule(module);
+
+ assert(sections && "SectionList missing from unloaded module.");
+
+ m_loaded_modules.erase(module);
+
+ const size_t num_sections = sections->GetSize();
+ for (size_t i = 0; i < num_sections; ++i) {
+ SectionSP section_sp(sections->GetSectionAtIndex(i));
+ target.SetSectionUnloaded(section_sp);
+ }
+}
+
+// Place a breakpoint on <_rtld_debug_state>
+bool DynamicLoaderHexagonDYLD::SetRendezvousBreakpoint() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+
+ // This is the original code, which want to look in the rendezvous structure
+ // to find the breakpoint address. Its backwards for us, since we can easily
+ // find the breakpoint address, since it is exported in our executable. We
+ // however know that we cant read the Rendezvous structure until we have hit
+ // the breakpoint once.
+ const ConstString dyldBpName("_rtld_debug_state");
+ addr_t break_addr = findSymbolAddress(m_process, dyldBpName);
+
+ Target &target = m_process->GetTarget();
+
+ // Do not try to set the breakpoint if we don't know where to put it
+ if (break_addr == LLDB_INVALID_ADDRESS) {
+ if (log)
+ log->Printf("Unable to locate _rtld_debug_state breakpoint address");
+
+ return false;
+ }
+
+ // Save the address of the rendezvous structure
+ m_rendezvous.SetBreakAddress(break_addr);
+
+ // If we haven't set the breakpoint before then set it
+ if (m_dyld_bid == LLDB_INVALID_BREAK_ID) {
+ Breakpoint *dyld_break =
+ target.CreateBreakpoint(break_addr, true, false).get();
+ dyld_break->SetCallback(RendezvousBreakpointHit, this, true);
+ dyld_break->SetBreakpointKind("shared-library-event");
+ m_dyld_bid = dyld_break->GetID();
+
+ // Make sure our breakpoint is at the right address.
+ assert(target.GetBreakpointByID(m_dyld_bid)
+ ->FindLocationByAddress(break_addr)
+ ->GetBreakpoint()
+ .GetID() == m_dyld_bid);
+
+ if (log && dyld_break == nullptr)
+ log->Printf("Failed to create _rtld_debug_state breakpoint");
+
+ // check we have successfully set bp
+ return (dyld_break != nullptr);
+ } else
+ // rendezvous already set
+ return true;
+}
+
+// We have just hit our breakpoint at <_rtld_debug_state>
+bool DynamicLoaderHexagonDYLD::RendezvousBreakpointHit(
+ void *baton, StoppointCallbackContext *context, user_id_t break_id,
+ user_id_t break_loc_id) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+
+ if (log)
+ log->Printf("Rendezvous breakpoint hit!");
+
+ DynamicLoaderHexagonDYLD *dyld_instance = nullptr;
+ dyld_instance = static_cast<DynamicLoaderHexagonDYLD *>(baton);
+
+ // if the dyld_instance is still not valid then try to locate it on the
+ // symbol table
+ if (!dyld_instance->m_rendezvous.IsValid()) {
+ Process *proc = dyld_instance->m_process;
+
+ const ConstString dyldStructName("_rtld_debug");
+ addr_t structAddr = findSymbolAddress(proc, dyldStructName);
+
+ if (structAddr != LLDB_INVALID_ADDRESS) {
+ dyld_instance->m_rendezvous.SetRendezvousAddress(structAddr);
+
+ if (log)
+ log->Printf("Found _rtld_debug structure @ 0x%08" PRIx64, structAddr);
+ } else {
+ if (log)
+ log->Printf("Unable to resolve the _rtld_debug structure");
+ }
+ }
+
+ dyld_instance->RefreshModules();
+
+ // Return true to stop the target, false to just let the target run.
+ return dyld_instance->GetStopWhenImagesChange();
+}
+
+/// Helper method for RendezvousBreakpointHit. Updates LLDB's current set
+/// of loaded modules.
+void DynamicLoaderHexagonDYLD::RefreshModules() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+
+ if (!m_rendezvous.Resolve())
+ return;
+
+ HexagonDYLDRendezvous::iterator I;
+ HexagonDYLDRendezvous::iterator E;
+
+ ModuleList &loaded_modules = m_process->GetTarget().GetImages();
+
+ if (m_rendezvous.ModulesDidLoad()) {
+ ModuleList new_modules;
+
+ E = m_rendezvous.loaded_end();
+ for (I = m_rendezvous.loaded_begin(); I != E; ++I) {
+ FileSpec file(I->path);
+ FileSystem::Instance().Resolve(file);
+ ModuleSP module_sp =
+ LoadModuleAtAddress(file, I->link_addr, I->base_addr, true);
+ if (module_sp.get()) {
+ loaded_modules.AppendIfNeeded(module_sp);
+ new_modules.Append(module_sp);
+ }
+
+ if (log) {
+ log->Printf("Target is loading '%s'", I->path.c_str());
+ if (!module_sp.get())
+ log->Printf("LLDB failed to load '%s'", I->path.c_str());
+ else
+ log->Printf("LLDB successfully loaded '%s'", I->path.c_str());
+ }
+ }
+ m_process->GetTarget().ModulesDidLoad(new_modules);
+ }
+
+ if (m_rendezvous.ModulesDidUnload()) {
+ ModuleList old_modules;
+
+ E = m_rendezvous.unloaded_end();
+ for (I = m_rendezvous.unloaded_begin(); I != E; ++I) {
+ FileSpec file(I->path);
+ FileSystem::Instance().Resolve(file);
+ ModuleSpec module_spec(file);
+ ModuleSP module_sp = loaded_modules.FindFirstModule(module_spec);
+
+ if (module_sp.get()) {
+ old_modules.Append(module_sp);
+ UnloadSections(module_sp);
+ }
+
+ if (log)
+ log->Printf("Target is unloading '%s'", I->path.c_str());
+ }
+ loaded_modules.Remove(old_modules);
+ m_process->GetTarget().ModulesDidUnload(old_modules, false);
+ }
+}
+
+// AD: This is very different to the Static Loader code.
+// It may be wise to look over this and its relation to stack
+// unwinding.
+ThreadPlanSP
+DynamicLoaderHexagonDYLD::GetStepThroughTrampolinePlan(Thread &thread,
+ bool stop) {
+ ThreadPlanSP thread_plan_sp;
+
+ StackFrame *frame = thread.GetStackFrameAtIndex(0).get();
+ const SymbolContext &context = frame->GetSymbolContext(eSymbolContextSymbol);
+ Symbol *sym = context.symbol;
+
+ if (sym == nullptr || !sym->IsTrampoline())
+ return thread_plan_sp;
+
+ const ConstString sym_name = sym->GetMangled().GetName(
+ lldb::eLanguageTypeUnknown, Mangled::ePreferMangled);
+ if (!sym_name)
+ return thread_plan_sp;
+
+ SymbolContextList target_symbols;
+ Target &target = thread.GetProcess()->GetTarget();
+ const ModuleList &images = target.GetImages();
+
+ images.FindSymbolsWithNameAndType(sym_name, eSymbolTypeCode, target_symbols);
+ size_t num_targets = target_symbols.GetSize();
+ if (!num_targets)
+ return thread_plan_sp;
+
+ typedef std::vector<lldb::addr_t> AddressVector;
+ AddressVector addrs;
+ for (size_t i = 0; i < num_targets; ++i) {
+ SymbolContext context;
+ AddressRange range;
+ if (target_symbols.GetContextAtIndex(i, context)) {
+ context.GetAddressRange(eSymbolContextEverything, 0, false, range);
+ lldb::addr_t addr = range.GetBaseAddress().GetLoadAddress(&target);
+ if (addr != LLDB_INVALID_ADDRESS)
+ addrs.push_back(addr);
+ }
+ }
+
+ if (addrs.size() > 0) {
+ AddressVector::iterator start = addrs.begin();
+ AddressVector::iterator end = addrs.end();
+
+ llvm::sort(start, end);
+ addrs.erase(std::unique(start, end), end);
+ thread_plan_sp =
+ std::make_shared<ThreadPlanRunToAddress>(thread, addrs, stop);
+ }
+
+ return thread_plan_sp;
+}
+
+/// Helper for the entry breakpoint callback. Resolves the load addresses
+/// of all dependent modules.
+void DynamicLoaderHexagonDYLD::LoadAllCurrentModules() {
+ HexagonDYLDRendezvous::iterator I;
+ HexagonDYLDRendezvous::iterator E;
+ ModuleList module_list;
+
+ if (!m_rendezvous.Resolve()) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ if (log)
+ log->Printf(
+ "DynamicLoaderHexagonDYLD::%s unable to resolve rendezvous address",
+ __FUNCTION__);
+ return;
+ }
+
+ // The rendezvous class doesn't enumerate the main module, so track that
+ // ourselves here.
+ ModuleSP executable = GetTargetExecutable();
+ m_loaded_modules[executable] = m_rendezvous.GetLinkMapAddress();
+
+ for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I) {
+ const char *module_path = I->path.c_str();
+ FileSpec file(module_path);
+ ModuleSP module_sp =
+ LoadModuleAtAddress(file, I->link_addr, I->base_addr, true);
+ if (module_sp.get()) {
+ module_list.Append(module_sp);
+ } else {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ if (log)
+ log->Printf("DynamicLoaderHexagonDYLD::%s failed loading module %s at "
+ "0x%" PRIx64,
+ __FUNCTION__, module_path, I->base_addr);
+ }
+ }
+
+ m_process->GetTarget().ModulesDidLoad(module_list);
+}
+
+/// Computes a value for m_load_offset returning the computed address on
+/// success and LLDB_INVALID_ADDRESS on failure.
+addr_t DynamicLoaderHexagonDYLD::ComputeLoadOffset() {
+ // Here we could send a GDB packet to know the load offset
+ //
+ // send: $qOffsets#4b
+ // get: Text=0;Data=0;Bss=0
+ //
+ // Currently qOffsets is not supported by pluginProcessGDBRemote
+ //
+ return 0;
+}
+
+// Here we must try to read the entry point directly from the elf header. This
+// is possible if the process is not relocatable or dynamically linked.
+//
+// an alternative is to look at the PC if we can be sure that we have connected
+// when the process is at the entry point.
+// I dont think that is reliable for us.
+addr_t DynamicLoaderHexagonDYLD::GetEntryPoint() {
+ if (m_entry_point != LLDB_INVALID_ADDRESS)
+ return m_entry_point;
+ // check we have a valid process
+ if (m_process == nullptr)
+ return LLDB_INVALID_ADDRESS;
+ // Get the current executable module
+ Module &module = *(m_process->GetTarget().GetExecutableModule().get());
+ // Get the object file (elf file) for this module
+ lldb_private::ObjectFile &object = *(module.GetObjectFile());
+ // Check if the file is executable (ie, not shared object or relocatable)
+ if (object.IsExecutable()) {
+ // Get the entry point address for this object
+ lldb_private::Address entry = object.GetEntryPointAddress();
+ // Return the entry point address
+ return entry.GetFileAddress();
+ }
+ // No idea so back out
+ return LLDB_INVALID_ADDRESS;
+}
+
+const SectionList *DynamicLoaderHexagonDYLD::GetSectionListFromModule(
+ const ModuleSP module) const {
+ SectionList *sections = nullptr;
+ if (module.get()) {
+ ObjectFile *obj_file = module->GetObjectFile();
+ if (obj_file) {
+ sections = obj_file->GetSectionList();
+ }
+ }
+ return sections;
+}
+
+static int ReadInt(Process *process, addr_t addr) {
+ Status error;
+ int value = (int)process->ReadUnsignedIntegerFromMemory(
+ addr, sizeof(uint32_t), 0, error);
+ if (error.Fail())
+ return -1;
+ else
+ return value;
+}
+
+lldb::addr_t
+DynamicLoaderHexagonDYLD::GetThreadLocalData(const lldb::ModuleSP module,
+ const lldb::ThreadSP thread,
+ lldb::addr_t tls_file_addr) {
+ auto it = m_loaded_modules.find(module);
+ if (it == m_loaded_modules.end())
+ return LLDB_INVALID_ADDRESS;
+
+ addr_t link_map = it->second;
+ if (link_map == LLDB_INVALID_ADDRESS)
+ return LLDB_INVALID_ADDRESS;
+
+ const HexagonDYLDRendezvous::ThreadInfo &metadata =
+ m_rendezvous.GetThreadInfo();
+ if (!metadata.valid)
+ return LLDB_INVALID_ADDRESS;
+
+ // Get the thread pointer.
+ addr_t tp = thread->GetThreadPointer();
+ if (tp == LLDB_INVALID_ADDRESS)
+ return LLDB_INVALID_ADDRESS;
+
+ // Find the module's modid.
+ int modid = ReadInt(m_process, link_map + metadata.modid_offset);
+ if (modid == -1)
+ return LLDB_INVALID_ADDRESS;
+
+ // Lookup the DTV structure for this thread.
+ addr_t dtv_ptr = tp + metadata.dtv_offset;
+ addr_t dtv = ReadPointer(dtv_ptr);
+ if (dtv == LLDB_INVALID_ADDRESS)
+ return LLDB_INVALID_ADDRESS;
+
+ // Find the TLS block for this module.
+ addr_t dtv_slot = dtv + metadata.dtv_slot_size * modid;
+ addr_t tls_block = ReadPointer(dtv_slot + metadata.tls_offset);
+
+ Module *mod = module.get();
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ if (log)
+ log->Printf("DynamicLoaderHexagonDYLD::Performed TLS lookup: "
+ "module=%s, link_map=0x%" PRIx64 ", tp=0x%" PRIx64
+ ", modid=%i, tls_block=0x%" PRIx64,
+ mod->GetObjectName().AsCString(""), link_map, tp, modid,
+ tls_block);
+
+ if (tls_block == LLDB_INVALID_ADDRESS)
+ return LLDB_INVALID_ADDRESS;
+ else
+ return tls_block + tls_file_addr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.h b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.h
new file mode 100644
index 000000000000..c171513c5499
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.h
@@ -0,0 +1,138 @@
+//===-- DynamicLoaderHexagonDYLD.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 liblldb_DynamicLoaderHexagonDYLD_h_
+#define liblldb_DynamicLoaderHexagonDYLD_h_
+
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Target/DynamicLoader.h"
+
+#include "HexagonDYLDRendezvous.h"
+
+class DynamicLoaderHexagonDYLD : public lldb_private::DynamicLoader {
+public:
+ DynamicLoaderHexagonDYLD(lldb_private::Process *process);
+
+ ~DynamicLoaderHexagonDYLD() override;
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::DynamicLoader *
+ CreateInstance(lldb_private::Process *process, bool force);
+
+ // DynamicLoader protocol
+
+ void DidAttach() override;
+
+ void DidLaunch() override;
+
+ lldb::ThreadPlanSP GetStepThroughTrampolinePlan(lldb_private::Thread &thread,
+ bool stop_others) override;
+
+ lldb_private::Status CanLoadImage() override;
+
+ lldb::addr_t GetThreadLocalData(const lldb::ModuleSP module,
+ const lldb::ThreadSP thread,
+ lldb::addr_t tls_file_addr) override;
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ /// Runtime linker rendezvous structure.
+ HexagonDYLDRendezvous m_rendezvous;
+
+ /// Virtual load address of the inferior process.
+ lldb::addr_t m_load_offset;
+
+ /// Virtual entry address of the inferior process.
+ lldb::addr_t m_entry_point;
+
+ /// Rendezvous breakpoint.
+ lldb::break_id_t m_dyld_bid;
+
+ /// Loaded module list. (link map for each module)
+ std::map<lldb::ModuleWP, lldb::addr_t, std::owner_less<lldb::ModuleWP>>
+ m_loaded_modules;
+
+ /// Enables a breakpoint on a function called by the runtime
+ /// linker each time a module is loaded or unloaded.
+ bool SetRendezvousBreakpoint();
+
+ /// Callback routine which updates the current list of loaded modules based
+ /// on the information supplied by the runtime linker.
+ static bool RendezvousBreakpointHit(
+ void *baton, lldb_private::StoppointCallbackContext *context,
+ lldb::user_id_t break_id, lldb::user_id_t break_loc_id);
+
+ /// Helper method for RendezvousBreakpointHit. Updates LLDB's current set
+ /// of loaded modules.
+ void RefreshModules();
+
+ /// Updates the load address of every allocatable section in \p module.
+ ///
+ /// \param module The module to traverse.
+ ///
+ /// \param link_map_addr The virtual address of the link map for the @p
+ /// module.
+ ///
+ /// \param base_addr The virtual base address \p module is loaded at.
+ void UpdateLoadedSections(lldb::ModuleSP module, lldb::addr_t link_map_addr,
+ lldb::addr_t base_addr,
+ bool base_addr_is_offset) override;
+
+ /// Removes the loaded sections from the target in \p module.
+ ///
+ /// \param module The module to traverse.
+ void UnloadSections(const lldb::ModuleSP module) override;
+
+ /// Callback routine invoked when we hit the breakpoint on process entry.
+ ///
+ /// This routine is responsible for resolving the load addresses of all
+ /// dependent modules required by the inferior and setting up the rendezvous
+ /// breakpoint.
+ static bool
+ EntryBreakpointHit(void *baton,
+ lldb_private::StoppointCallbackContext *context,
+ lldb::user_id_t break_id, lldb::user_id_t break_loc_id);
+
+ /// Helper for the entry breakpoint callback. Resolves the load addresses
+ /// of all dependent modules.
+ void LoadAllCurrentModules();
+
+ /// Computes a value for m_load_offset returning the computed address on
+ /// success and LLDB_INVALID_ADDRESS on failure.
+ lldb::addr_t ComputeLoadOffset();
+
+ /// Computes a value for m_entry_point returning the computed address on
+ /// success and LLDB_INVALID_ADDRESS on failure.
+ lldb::addr_t GetEntryPoint();
+
+ /// Checks to see if the target module has changed, updates the target
+ /// accordingly and returns the target executable module.
+ lldb::ModuleSP GetTargetExecutable();
+
+ /// return the address of the Rendezvous breakpoint
+ lldb::addr_t FindRendezvousBreakpointAddress();
+
+private:
+ const lldb_private::SectionList *
+ GetSectionListFromModule(const lldb::ModuleSP module) const;
+
+ DISALLOW_COPY_AND_ASSIGN(DynamicLoaderHexagonDYLD);
+};
+
+#endif // liblldb_DynamicLoaderHexagonDYLD_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/HexagonDYLDRendezvous.cpp b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/HexagonDYLDRendezvous.cpp
new file mode 100644
index 000000000000..844a06c2b37d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/HexagonDYLDRendezvous.cpp
@@ -0,0 +1,367 @@
+//===-- HexagonDYLDRendezvous.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 "lldb/Core/Module.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Status.h"
+
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+
+#include "HexagonDYLDRendezvous.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+/// Locates the address of the rendezvous structure. Returns the address on
+/// success and LLDB_INVALID_ADDRESS on failure.
+static addr_t ResolveRendezvousAddress(Process *process) {
+ addr_t info_location;
+ addr_t info_addr;
+ Status error;
+
+ info_location = process->GetImageInfoAddress();
+
+ if (info_location == LLDB_INVALID_ADDRESS)
+ return LLDB_INVALID_ADDRESS;
+
+ info_addr = process->ReadPointerFromMemory(info_location, error);
+ if (error.Fail())
+ return LLDB_INVALID_ADDRESS;
+
+ if (info_addr == 0)
+ return LLDB_INVALID_ADDRESS;
+
+ return info_addr;
+}
+
+HexagonDYLDRendezvous::HexagonDYLDRendezvous(Process *process)
+ : m_process(process), m_rendezvous_addr(LLDB_INVALID_ADDRESS), m_current(),
+ m_previous(), m_soentries(), m_added_soentries(), m_removed_soentries() {
+ m_thread_info.valid = false;
+
+ // Cache a copy of the executable path
+ if (m_process) {
+ Module *exe_mod = m_process->GetTarget().GetExecutableModulePointer();
+ if (exe_mod)
+ exe_mod->GetFileSpec().GetPath(m_exe_path, PATH_MAX);
+ }
+}
+
+bool HexagonDYLDRendezvous::Resolve() {
+ const size_t word_size = 4;
+ Rendezvous info;
+ size_t address_size;
+ size_t padding;
+ addr_t info_addr;
+ addr_t cursor;
+
+ address_size = m_process->GetAddressByteSize();
+ padding = address_size - word_size;
+
+ if (m_rendezvous_addr == LLDB_INVALID_ADDRESS)
+ cursor = info_addr = ResolveRendezvousAddress(m_process);
+ else
+ cursor = info_addr = m_rendezvous_addr;
+
+ if (cursor == LLDB_INVALID_ADDRESS)
+ return false;
+
+ if (!(cursor = ReadWord(cursor, &info.version, word_size)))
+ return false;
+
+ if (!(cursor = ReadPointer(cursor + padding, &info.map_addr)))
+ return false;
+
+ if (!(cursor = ReadPointer(cursor, &info.brk)))
+ return false;
+
+ if (!(cursor = ReadWord(cursor, &info.state, word_size)))
+ return false;
+
+ if (!(cursor = ReadPointer(cursor + padding, &info.ldbase)))
+ return false;
+
+ // The rendezvous was successfully read. Update our internal state.
+ m_rendezvous_addr = info_addr;
+ m_previous = m_current;
+ m_current = info;
+
+ return UpdateSOEntries();
+}
+
+void HexagonDYLDRendezvous::SetRendezvousAddress(lldb::addr_t addr) {
+ m_rendezvous_addr = addr;
+}
+
+bool HexagonDYLDRendezvous::IsValid() {
+ return m_rendezvous_addr != LLDB_INVALID_ADDRESS;
+}
+
+bool HexagonDYLDRendezvous::UpdateSOEntries() {
+ SOEntry entry;
+
+ if (m_current.map_addr == 0)
+ return false;
+
+ // When the previous and current states are consistent this is the first time
+ // we have been asked to update. Just take a snapshot of the currently
+ // loaded modules.
+ if (m_previous.state == eConsistent && m_current.state == eConsistent)
+ return TakeSnapshot(m_soentries);
+
+ // If we are about to add or remove a shared object clear out the current
+ // state and take a snapshot of the currently loaded images.
+ if (m_current.state == eAdd || m_current.state == eDelete) {
+ // this is a fudge so that we can clear the assert below.
+ m_previous.state = eConsistent;
+ // We hit this assert on the 2nd run of this function after running the
+ // calc example
+ assert(m_previous.state == eConsistent);
+ m_soentries.clear();
+ m_added_soentries.clear();
+ m_removed_soentries.clear();
+ return TakeSnapshot(m_soentries);
+ }
+ assert(m_current.state == eConsistent);
+
+ // Otherwise check the previous state to determine what to expect and update
+ // accordingly.
+ if (m_previous.state == eAdd)
+ return UpdateSOEntriesForAddition();
+ else if (m_previous.state == eDelete)
+ return UpdateSOEntriesForDeletion();
+
+ return false;
+}
+
+bool HexagonDYLDRendezvous::UpdateSOEntriesForAddition() {
+ SOEntry entry;
+ iterator pos;
+
+ assert(m_previous.state == eAdd);
+
+ if (m_current.map_addr == 0)
+ return false;
+
+ for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) {
+ if (!ReadSOEntryFromMemory(cursor, entry))
+ return false;
+
+ // Only add shared libraries and not the executable. On Linux this is
+ // indicated by an empty path in the entry. On FreeBSD it is the name of
+ // the executable.
+ if (entry.path.empty() || ::strcmp(entry.path.c_str(), m_exe_path) == 0)
+ continue;
+
+ pos = std::find(m_soentries.begin(), m_soentries.end(), entry);
+ if (pos == m_soentries.end()) {
+ m_soentries.push_back(entry);
+ m_added_soentries.push_back(entry);
+ }
+ }
+
+ return true;
+}
+
+bool HexagonDYLDRendezvous::UpdateSOEntriesForDeletion() {
+ SOEntryList entry_list;
+ iterator pos;
+
+ assert(m_previous.state == eDelete);
+
+ if (!TakeSnapshot(entry_list))
+ return false;
+
+ for (iterator I = begin(); I != end(); ++I) {
+ pos = std::find(entry_list.begin(), entry_list.end(), *I);
+ if (pos == entry_list.end())
+ m_removed_soentries.push_back(*I);
+ }
+
+ m_soentries = entry_list;
+ return true;
+}
+
+bool HexagonDYLDRendezvous::TakeSnapshot(SOEntryList &entry_list) {
+ SOEntry entry;
+
+ if (m_current.map_addr == 0)
+ return false;
+
+ for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) {
+ if (!ReadSOEntryFromMemory(cursor, entry))
+ return false;
+
+ // Only add shared libraries and not the executable. On Linux this is
+ // indicated by an empty path in the entry. On FreeBSD it is the name of
+ // the executable.
+ if (entry.path.empty() || ::strcmp(entry.path.c_str(), m_exe_path) == 0)
+ continue;
+
+ entry_list.push_back(entry);
+ }
+
+ return true;
+}
+
+addr_t HexagonDYLDRendezvous::ReadWord(addr_t addr, uint64_t *dst,
+ size_t size) {
+ Status error;
+
+ *dst = m_process->ReadUnsignedIntegerFromMemory(addr, size, 0, error);
+ if (error.Fail())
+ return 0;
+
+ return addr + size;
+}
+
+addr_t HexagonDYLDRendezvous::ReadPointer(addr_t addr, addr_t *dst) {
+ Status error;
+
+ *dst = m_process->ReadPointerFromMemory(addr, error);
+ if (error.Fail())
+ return 0;
+
+ return addr + m_process->GetAddressByteSize();
+}
+
+std::string HexagonDYLDRendezvous::ReadStringFromMemory(addr_t addr) {
+ std::string str;
+ Status error;
+ size_t size;
+ char c;
+
+ if (addr == LLDB_INVALID_ADDRESS)
+ return std::string();
+
+ for (;;) {
+ size = m_process->DoReadMemory(addr, &c, 1, error);
+ if (size != 1 || error.Fail())
+ return std::string();
+ if (c == 0)
+ break;
+ else {
+ str.push_back(c);
+ addr++;
+ }
+ }
+
+ return str;
+}
+
+bool HexagonDYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr,
+ SOEntry &entry) {
+ entry.clear();
+ entry.link_addr = addr;
+
+ if (!(addr = ReadPointer(addr, &entry.base_addr)))
+ return false;
+
+ if (!(addr = ReadPointer(addr, &entry.path_addr)))
+ return false;
+
+ if (!(addr = ReadPointer(addr, &entry.dyn_addr)))
+ return false;
+
+ if (!(addr = ReadPointer(addr, &entry.next)))
+ return false;
+
+ if (!(addr = ReadPointer(addr, &entry.prev)))
+ return false;
+
+ entry.path = ReadStringFromMemory(entry.path_addr);
+
+ return true;
+}
+
+bool HexagonDYLDRendezvous::FindMetadata(const char *name, PThreadField field,
+ uint32_t &value) {
+ Target &target = m_process->GetTarget();
+
+ SymbolContextList list;
+ if (!target.GetImages().FindSymbolsWithNameAndType(ConstString(name),
+ eSymbolTypeAny, list))
+ return false;
+
+ Address address = list[0].symbol->GetAddress();
+ addr_t addr = address.GetLoadAddress(&target);
+ if (addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ Status error;
+ value = (uint32_t)m_process->ReadUnsignedIntegerFromMemory(
+ addr + field * sizeof(uint32_t), sizeof(uint32_t), 0, error);
+ if (error.Fail())
+ return false;
+
+ if (field == eSize)
+ value /= 8; // convert bits to bytes
+
+ return true;
+}
+
+const HexagonDYLDRendezvous::ThreadInfo &
+HexagonDYLDRendezvous::GetThreadInfo() {
+ if (!m_thread_info.valid) {
+ bool ok = true;
+
+ ok &= FindMetadata("_thread_db_pthread_dtvp", eOffset,
+ m_thread_info.dtv_offset);
+ ok &=
+ FindMetadata("_thread_db_dtv_dtv", eSize, m_thread_info.dtv_slot_size);
+ ok &= FindMetadata("_thread_db_link_map_l_tls_modid", eOffset,
+ m_thread_info.modid_offset);
+ ok &= FindMetadata("_thread_db_dtv_t_pointer_val", eOffset,
+ m_thread_info.tls_offset);
+
+ if (ok)
+ m_thread_info.valid = true;
+ }
+
+ return m_thread_info;
+}
+
+void HexagonDYLDRendezvous::DumpToLog(Log *log) const {
+ int state = GetState();
+
+ if (!log)
+ return;
+
+ log->PutCString("HexagonDYLDRendezvous:");
+ log->Printf(" Address: %" PRIx64, GetRendezvousAddress());
+ log->Printf(" Version: %" PRIu64, GetVersion());
+ log->Printf(" Link : %" PRIx64, GetLinkMapAddress());
+ log->Printf(" Break : %" PRIx64, GetBreakAddress());
+ log->Printf(" LDBase : %" PRIx64, GetLDBase());
+ log->Printf(" State : %s",
+ (state == eConsistent)
+ ? "consistent"
+ : (state == eAdd) ? "add" : (state == eDelete) ? "delete"
+ : "unknown");
+
+ iterator I = begin();
+ iterator E = end();
+
+ if (I != E)
+ log->PutCString("HexagonDYLDRendezvous SOEntries:");
+
+ for (int i = 1; I != E; ++I, ++i) {
+ log->Printf("\n SOEntry [%d] %s", i, I->path.c_str());
+ log->Printf(" Base : %" PRIx64, I->base_addr);
+ log->Printf(" Path : %" PRIx64, I->path_addr);
+ log->Printf(" Dyn : %" PRIx64, I->dyn_addr);
+ log->Printf(" Next : %" PRIx64, I->next);
+ log->Printf(" Prev : %" PRIx64, I->prev);
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/HexagonDYLDRendezvous.h b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/HexagonDYLDRendezvous.h
new file mode 100644
index 000000000000..70fc12b7fab7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/HexagonDYLDRendezvous.h
@@ -0,0 +1,246 @@
+//===-- HexagonDYLDRendezvous.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 liblldb_HexagonDYLDRendezvous_H_
+#define liblldb_HexagonDYLDRendezvous_H_
+
+#include <limits.h>
+#include <list>
+#include <map>
+#include <string>
+
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+class Process;
+}
+
+/// \class HexagonDYLDRendezvous
+/// Interface to the runtime linker.
+///
+/// A structure is present in a processes memory space which is updated by the
+/// runtime liker each time a module is loaded or unloaded. This class
+/// provides an interface to this structure and maintains a consistent
+/// snapshot of the currently loaded modules.
+class HexagonDYLDRendezvous {
+
+ // This structure is used to hold the contents of the debug rendezvous
+ // information (struct r_debug) as found in the inferiors memory. Note that
+ // the layout of this struct is not binary compatible, it is simply large
+ // enough to hold the information on both 32 and 64 bit platforms.
+ struct Rendezvous {
+ uint64_t version;
+ lldb::addr_t map_addr;
+ lldb::addr_t brk;
+ uint64_t state;
+ lldb::addr_t ldbase;
+
+ Rendezvous()
+ : version(0), map_addr(LLDB_INVALID_ADDRESS), brk(LLDB_INVALID_ADDRESS),
+ state(0), ldbase(0) {}
+ };
+
+public:
+ // Various metadata supplied by the inferior's threading library to describe
+ // the per-thread state.
+ struct ThreadInfo {
+ bool valid; // whether we read valid metadata
+ uint32_t dtv_offset; // offset of DTV pointer within pthread
+ uint32_t dtv_slot_size; // size of one DTV slot
+ uint32_t modid_offset; // offset of module ID within link_map
+ uint32_t tls_offset; // offset of TLS pointer within DTV slot
+ };
+
+ HexagonDYLDRendezvous(lldb_private::Process *process);
+
+ /// Update the internal snapshot of runtime linker rendezvous and recompute
+ /// the currently loaded modules.
+ ///
+ /// This method should be called once one start up, then once each time the
+ /// runtime linker enters the function given by GetBreakAddress().
+ ///
+ /// \returns true on success and false on failure.
+ ///
+ /// \see GetBreakAddress().
+ bool Resolve();
+
+ /// \returns true if this rendezvous has been located in the inferiors
+ /// address space and false otherwise.
+ bool IsValid();
+
+ /// \returns the address of the rendezvous structure in the inferiors
+ /// address space.
+ lldb::addr_t GetRendezvousAddress() const { return m_rendezvous_addr; }
+
+ /// Provide the dyld structure address
+ void SetRendezvousAddress(lldb::addr_t);
+
+ /// \returns the version of the rendezvous protocol being used.
+ uint64_t GetVersion() const { return m_current.version; }
+
+ /// \returns address in the inferiors address space containing the linked
+ /// list of shared object descriptors.
+ lldb::addr_t GetLinkMapAddress() const { return m_current.map_addr; }
+
+ /// A breakpoint should be set at this address and Resolve called on each
+ /// hit.
+ ///
+ /// \returns the address of a function called by the runtime linker each
+ /// time a module is loaded/unloaded, or about to be loaded/unloaded.
+ ///
+ /// \see Resolve()
+ lldb::addr_t GetBreakAddress() const { return m_current.brk; }
+
+ /// In hexagon it is possible that we can know the dyld breakpoint without
+ /// having to find it from the rendezvous structure
+ ///
+ void SetBreakAddress(lldb::addr_t addr) { m_current.brk = addr; }
+
+ /// Returns the current state of the rendezvous structure.
+ uint64_t GetState() const { return m_current.state; }
+
+ /// \returns the base address of the runtime linker in the inferiors address
+ /// space.
+ lldb::addr_t GetLDBase() const { return m_current.ldbase; }
+
+ /// \returns the thread layout metadata from the inferiors thread library.
+ const ThreadInfo &GetThreadInfo();
+
+ /// \returns true if modules have been loaded into the inferior since the
+ /// last call to Resolve().
+ bool ModulesDidLoad() const { return !m_added_soentries.empty(); }
+
+ /// \returns true if modules have been unloaded from the inferior since the
+ /// last call to Resolve().
+ bool ModulesDidUnload() const { return !m_removed_soentries.empty(); }
+
+ void DumpToLog(lldb_private::Log *log) const;
+
+ /// Constants describing the state of the rendezvous.
+ ///
+ /// \see GetState().
+ enum RendezvousState {
+ eConsistent = 0,
+ eAdd,
+ eDelete,
+ };
+
+ /// Structure representing the shared objects currently loaded into the
+ /// inferior process.
+ ///
+ /// This object is a rough analogue to the struct link_map object which
+ /// actually lives in the inferiors memory.
+ struct SOEntry {
+ lldb::addr_t link_addr; ///< Address of this link_map.
+ lldb::addr_t base_addr; ///< Base address of the loaded object.
+ lldb::addr_t path_addr; ///< String naming the shared object.
+ lldb::addr_t dyn_addr; ///< Dynamic section of shared object.
+ lldb::addr_t next; ///< Address of next so_entry.
+ lldb::addr_t prev; ///< Address of previous so_entry.
+ std::string path; ///< File name of shared object.
+
+ SOEntry() { clear(); }
+
+ bool operator==(const SOEntry &entry) { return this->path == entry.path; }
+
+ void clear() {
+ link_addr = 0;
+ base_addr = 0;
+ path_addr = 0;
+ dyn_addr = 0;
+ next = 0;
+ prev = 0;
+ path.clear();
+ }
+ };
+
+protected:
+ typedef std::list<SOEntry> SOEntryList;
+
+public:
+ typedef SOEntryList::const_iterator iterator;
+
+ /// Iterators over all currently loaded modules.
+ iterator begin() const { return m_soentries.begin(); }
+ iterator end() const { return m_soentries.end(); }
+
+ /// Iterators over all modules loaded into the inferior since the last call
+ /// to Resolve().
+ iterator loaded_begin() const { return m_added_soentries.begin(); }
+ iterator loaded_end() const { return m_added_soentries.end(); }
+
+ /// Iterators over all modules unloaded from the inferior since the last
+ /// call to Resolve().
+ iterator unloaded_begin() const { return m_removed_soentries.begin(); }
+ iterator unloaded_end() const { return m_removed_soentries.end(); }
+
+protected:
+ lldb_private::Process *m_process;
+
+ // Cached copy of executable pathname
+ char m_exe_path[PATH_MAX];
+
+ /// Location of the r_debug structure in the inferiors address space.
+ lldb::addr_t m_rendezvous_addr;
+
+ /// Current and previous snapshots of the rendezvous structure.
+ Rendezvous m_current;
+ Rendezvous m_previous;
+
+ /// List of SOEntry objects corresponding to the current link map state.
+ SOEntryList m_soentries;
+
+ /// List of SOEntry's added to the link map since the last call to
+ /// Resolve().
+ SOEntryList m_added_soentries;
+
+ /// List of SOEntry's removed from the link map since the last call to
+ /// Resolve().
+ SOEntryList m_removed_soentries;
+
+ /// Threading metadata read from the inferior.
+ ThreadInfo m_thread_info;
+
+ /// Reads an unsigned integer of \p size bytes from the inferior's address
+ /// space starting at \p addr.
+ ///
+ /// \returns addr + size if the read was successful and false otherwise.
+ lldb::addr_t ReadWord(lldb::addr_t addr, uint64_t *dst, size_t size);
+
+ /// Reads an address from the inferior's address space starting at \p addr.
+ ///
+ /// \returns addr + target address size if the read was successful and
+ /// 0 otherwise.
+ lldb::addr_t ReadPointer(lldb::addr_t addr, lldb::addr_t *dst);
+
+ /// Reads a null-terminated C string from the memory location starting at @p
+ /// addr.
+ std::string ReadStringFromMemory(lldb::addr_t addr);
+
+ /// Reads an SOEntry starting at \p addr.
+ bool ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry);
+
+ /// Updates the current set of SOEntries, the set of added entries, and the
+ /// set of removed entries.
+ bool UpdateSOEntries();
+
+ bool UpdateSOEntriesForAddition();
+
+ bool UpdateSOEntriesForDeletion();
+
+ /// Reads the current list of shared objects according to the link map
+ /// supplied by the runtime linker.
+ bool TakeSnapshot(SOEntryList &entry_list);
+
+ enum PThreadField { eSize, eNElem, eOffset };
+
+ bool FindMetadata(const char *name, PThreadField field, uint32_t &value);
+};
+
+#endif // liblldb_HexagonDYLDRendezvous_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp
new file mode 100644
index 000000000000..0d736738ebb5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp
@@ -0,0 +1,597 @@
+//===-- DYLDRendezvous.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 "lldb/Core/Module.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Status.h"
+
+#include "llvm/Support/Path.h"
+
+#include "DYLDRendezvous.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+/// Locates the address of the rendezvous structure. Returns the address on
+/// success and LLDB_INVALID_ADDRESS on failure.
+static addr_t ResolveRendezvousAddress(Process *process) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ addr_t info_location;
+ addr_t info_addr;
+ Status error;
+
+ if (!process) {
+ if (log)
+ log->Printf("%s null process provided", __FUNCTION__);
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ // Try to get it from our process. This might be a remote process and might
+ // grab it via some remote-specific mechanism.
+ info_location = process->GetImageInfoAddress();
+ if (log)
+ log->Printf("%s info_location = 0x%" PRIx64, __FUNCTION__, info_location);
+
+ // If the process fails to return an address, fall back to seeing if the
+ // local object file can help us find it.
+ if (info_location == LLDB_INVALID_ADDRESS) {
+ Target *target = &process->GetTarget();
+ if (target) {
+ ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile();
+ Address addr = obj_file->GetImageInfoAddress(target);
+
+ if (addr.IsValid()) {
+ info_location = addr.GetLoadAddress(target);
+ if (log)
+ log->Printf(
+ "%s resolved via direct object file approach to 0x%" PRIx64,
+ __FUNCTION__, info_location);
+ } else {
+ if (log)
+ log->Printf("%s FAILED - direct object file approach did not yield a "
+ "valid address",
+ __FUNCTION__);
+ }
+ }
+ }
+
+ if (info_location == LLDB_INVALID_ADDRESS) {
+ if (log)
+ log->Printf("%s FAILED - invalid info address", __FUNCTION__);
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ if (log)
+ log->Printf("%s reading pointer (%" PRIu32 " bytes) from 0x%" PRIx64,
+ __FUNCTION__, process->GetAddressByteSize(), info_location);
+
+ info_addr = process->ReadPointerFromMemory(info_location, error);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("%s FAILED - could not read from the info location: %s",
+ __FUNCTION__, error.AsCString());
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ if (info_addr == 0) {
+ if (log)
+ log->Printf("%s FAILED - the rendezvous address contained at 0x%" PRIx64
+ " returned a null value",
+ __FUNCTION__, info_location);
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ return info_addr;
+}
+
+DYLDRendezvous::DYLDRendezvous(Process *process)
+ : m_process(process), m_rendezvous_addr(LLDB_INVALID_ADDRESS), m_current(),
+ m_previous(), m_loaded_modules(), m_soentries(), m_added_soentries(),
+ m_removed_soentries() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+
+ m_thread_info.valid = false;
+
+ // Cache a copy of the executable path
+ if (m_process) {
+ Module *exe_mod = m_process->GetTarget().GetExecutableModulePointer();
+ if (exe_mod) {
+ m_exe_file_spec = exe_mod->GetPlatformFileSpec();
+ if (log)
+ log->Printf("DYLDRendezvous::%s exe module executable path set: '%s'",
+ __FUNCTION__, m_exe_file_spec.GetCString());
+ } else {
+ if (log)
+ log->Printf("DYLDRendezvous::%s cannot cache exe module path: null "
+ "executable module pointer",
+ __FUNCTION__);
+ }
+ }
+}
+
+bool DYLDRendezvous::Resolve() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+
+ const size_t word_size = 4;
+ Rendezvous info;
+ size_t address_size;
+ size_t padding;
+ addr_t info_addr;
+ addr_t cursor;
+
+ address_size = m_process->GetAddressByteSize();
+ padding = address_size - word_size;
+ if (log)
+ log->Printf("DYLDRendezvous::%s address size: %" PRIu64
+ ", padding %" PRIu64,
+ __FUNCTION__, uint64_t(address_size), uint64_t(padding));
+
+ if (m_rendezvous_addr == LLDB_INVALID_ADDRESS)
+ cursor = info_addr = ResolveRendezvousAddress(m_process);
+ else
+ cursor = info_addr = m_rendezvous_addr;
+ if (log)
+ log->Printf("DYLDRendezvous::%s cursor = 0x%" PRIx64, __FUNCTION__, cursor);
+
+ if (cursor == LLDB_INVALID_ADDRESS)
+ return false;
+
+ if (!(cursor = ReadWord(cursor, &info.version, word_size)))
+ return false;
+
+ if (!(cursor = ReadPointer(cursor + padding, &info.map_addr)))
+ return false;
+
+ if (!(cursor = ReadPointer(cursor, &info.brk)))
+ return false;
+
+ if (!(cursor = ReadWord(cursor, &info.state, word_size)))
+ return false;
+
+ if (!(cursor = ReadPointer(cursor + padding, &info.ldbase)))
+ return false;
+
+ // The rendezvous was successfully read. Update our internal state.
+ m_rendezvous_addr = info_addr;
+ m_previous = m_current;
+ m_current = info;
+
+ if (UpdateSOEntries(true))
+ return true;
+
+ return UpdateSOEntries();
+}
+
+bool DYLDRendezvous::IsValid() {
+ return m_rendezvous_addr != LLDB_INVALID_ADDRESS;
+}
+
+bool DYLDRendezvous::UpdateSOEntries(bool fromRemote) {
+ SOEntry entry;
+ LoadedModuleInfoList module_list;
+
+ // If we can't get the SO info from the remote, return failure.
+ if (fromRemote && m_process->LoadModules(module_list) == 0)
+ return false;
+
+ if (!fromRemote && m_current.map_addr == 0)
+ return false;
+
+ // When the previous and current states are consistent this is the first time
+ // we have been asked to update. Just take a snapshot of the currently
+ // loaded modules.
+ if (m_previous.state == eConsistent && m_current.state == eConsistent)
+ return fromRemote ? SaveSOEntriesFromRemote(module_list)
+ : TakeSnapshot(m_soentries);
+
+ // If we are about to add or remove a shared object clear out the current
+ // state and take a snapshot of the currently loaded images.
+ if (m_current.state == eAdd || m_current.state == eDelete) {
+ // Some versions of the android dynamic linker might send two notifications
+ // with state == eAdd back to back. Ignore them until we get an eConsistent
+ // notification.
+ if (!(m_previous.state == eConsistent ||
+ (m_previous.state == eAdd && m_current.state == eDelete)))
+ return false;
+
+ m_soentries.clear();
+ if (fromRemote)
+ return SaveSOEntriesFromRemote(module_list);
+
+ m_added_soentries.clear();
+ m_removed_soentries.clear();
+ return TakeSnapshot(m_soentries);
+ }
+ assert(m_current.state == eConsistent);
+
+ // Otherwise check the previous state to determine what to expect and update
+ // accordingly.
+ if (m_previous.state == eAdd)
+ return fromRemote ? AddSOEntriesFromRemote(module_list) : AddSOEntries();
+ else if (m_previous.state == eDelete)
+ return fromRemote ? RemoveSOEntriesFromRemote(module_list)
+ : RemoveSOEntries();
+
+ return false;
+}
+
+bool DYLDRendezvous::FillSOEntryFromModuleInfo(
+ LoadedModuleInfoList::LoadedModuleInfo const &modInfo, SOEntry &entry) {
+ addr_t link_map_addr;
+ addr_t base_addr;
+ addr_t dyn_addr;
+ std::string name;
+
+ if (!modInfo.get_link_map(link_map_addr) || !modInfo.get_base(base_addr) ||
+ !modInfo.get_dynamic(dyn_addr) || !modInfo.get_name(name))
+ return false;
+
+ entry.link_addr = link_map_addr;
+ entry.base_addr = base_addr;
+ entry.dyn_addr = dyn_addr;
+
+ entry.file_spec.SetFile(name, FileSpec::Style::native);
+
+ UpdateBaseAddrIfNecessary(entry, name);
+
+ // not needed if we're using ModuleInfos
+ entry.next = 0;
+ entry.prev = 0;
+ entry.path_addr = 0;
+
+ return true;
+}
+
+bool DYLDRendezvous::SaveSOEntriesFromRemote(
+ LoadedModuleInfoList &module_list) {
+ for (auto const &modInfo : module_list.m_list) {
+ SOEntry entry;
+ if (!FillSOEntryFromModuleInfo(modInfo, entry))
+ return false;
+
+ // Only add shared libraries and not the executable.
+ if (!SOEntryIsMainExecutable(entry))
+ m_soentries.push_back(entry);
+ }
+
+ m_loaded_modules = module_list;
+ return true;
+}
+
+bool DYLDRendezvous::AddSOEntriesFromRemote(LoadedModuleInfoList &module_list) {
+ for (auto const &modInfo : module_list.m_list) {
+ bool found = false;
+ for (auto const &existing : m_loaded_modules.m_list) {
+ if (modInfo == existing) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found)
+ continue;
+
+ SOEntry entry;
+ if (!FillSOEntryFromModuleInfo(modInfo, entry))
+ return false;
+
+ // Only add shared libraries and not the executable.
+ if (!SOEntryIsMainExecutable(entry))
+ m_soentries.push_back(entry);
+ }
+
+ m_loaded_modules = module_list;
+ return true;
+}
+
+bool DYLDRendezvous::RemoveSOEntriesFromRemote(
+ LoadedModuleInfoList &module_list) {
+ for (auto const &existing : m_loaded_modules.m_list) {
+ bool found = false;
+ for (auto const &modInfo : module_list.m_list) {
+ if (modInfo == existing) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found)
+ continue;
+
+ SOEntry entry;
+ if (!FillSOEntryFromModuleInfo(existing, entry))
+ return false;
+
+ // Only add shared libraries and not the executable.
+ if (!SOEntryIsMainExecutable(entry)) {
+ auto pos = std::find(m_soentries.begin(), m_soentries.end(), entry);
+ if (pos == m_soentries.end())
+ return false;
+
+ m_soentries.erase(pos);
+ }
+ }
+
+ m_loaded_modules = module_list;
+ return true;
+}
+
+bool DYLDRendezvous::AddSOEntries() {
+ SOEntry entry;
+ iterator pos;
+
+ assert(m_previous.state == eAdd);
+
+ if (m_current.map_addr == 0)
+ return false;
+
+ for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) {
+ if (!ReadSOEntryFromMemory(cursor, entry))
+ return false;
+
+ // Only add shared libraries and not the executable.
+ if (SOEntryIsMainExecutable(entry))
+ continue;
+
+ pos = std::find(m_soentries.begin(), m_soentries.end(), entry);
+ if (pos == m_soentries.end()) {
+ m_soentries.push_back(entry);
+ m_added_soentries.push_back(entry);
+ }
+ }
+
+ return true;
+}
+
+bool DYLDRendezvous::RemoveSOEntries() {
+ SOEntryList entry_list;
+ iterator pos;
+
+ assert(m_previous.state == eDelete);
+
+ if (!TakeSnapshot(entry_list))
+ return false;
+
+ for (iterator I = begin(); I != end(); ++I) {
+ pos = std::find(entry_list.begin(), entry_list.end(), *I);
+ if (pos == entry_list.end())
+ m_removed_soentries.push_back(*I);
+ }
+
+ m_soentries = entry_list;
+ return true;
+}
+
+bool DYLDRendezvous::SOEntryIsMainExecutable(const SOEntry &entry) {
+ // On some systes the executable is indicated by an empty path in the entry.
+ // On others it is the full path to the executable.
+
+ auto triple = m_process->GetTarget().GetArchitecture().GetTriple();
+ switch (triple.getOS()) {
+ case llvm::Triple::FreeBSD:
+ case llvm::Triple::NetBSD:
+ return entry.file_spec == m_exe_file_spec;
+ case llvm::Triple::Linux:
+ if (triple.isAndroid())
+ return entry.file_spec == m_exe_file_spec;
+ return !entry.file_spec;
+ default:
+ return false;
+ }
+}
+
+bool DYLDRendezvous::TakeSnapshot(SOEntryList &entry_list) {
+ SOEntry entry;
+
+ if (m_current.map_addr == 0)
+ return false;
+
+ // Clear previous entries since we are about to obtain an up to date list.
+ entry_list.clear();
+
+ for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) {
+ if (!ReadSOEntryFromMemory(cursor, entry))
+ return false;
+
+ // Only add shared libraries and not the executable.
+ if (SOEntryIsMainExecutable(entry))
+ continue;
+
+ entry_list.push_back(entry);
+ }
+
+ return true;
+}
+
+addr_t DYLDRendezvous::ReadWord(addr_t addr, uint64_t *dst, size_t size) {
+ Status error;
+
+ *dst = m_process->ReadUnsignedIntegerFromMemory(addr, size, 0, error);
+ if (error.Fail())
+ return 0;
+
+ return addr + size;
+}
+
+addr_t DYLDRendezvous::ReadPointer(addr_t addr, addr_t *dst) {
+ Status error;
+
+ *dst = m_process->ReadPointerFromMemory(addr, error);
+ if (error.Fail())
+ return 0;
+
+ return addr + m_process->GetAddressByteSize();
+}
+
+std::string DYLDRendezvous::ReadStringFromMemory(addr_t addr) {
+ std::string str;
+ Status error;
+
+ if (addr == LLDB_INVALID_ADDRESS)
+ return std::string();
+
+ m_process->ReadCStringFromMemory(addr, str, error);
+
+ return str;
+}
+
+// Returns true if the load bias reported by the linker is incorrect for the
+// given entry. This function is used to handle cases where we want to work
+// around a bug in the system linker.
+static bool isLoadBiasIncorrect(Target &target, const std::string &file_path) {
+ // On Android L (API 21, 22) the load address of the "/system/bin/linker"
+ // isn't filled in correctly.
+ unsigned os_major = target.GetPlatform()->GetOSVersion().getMajor();
+ return target.GetArchitecture().GetTriple().isAndroid() &&
+ (os_major == 21 || os_major == 22) &&
+ (file_path == "/system/bin/linker" ||
+ file_path == "/system/bin/linker64");
+}
+
+void DYLDRendezvous::UpdateBaseAddrIfNecessary(SOEntry &entry,
+ std::string const &file_path) {
+ // If the load bias reported by the linker is incorrect then fetch the load
+ // address of the file from the proc file system.
+ if (isLoadBiasIncorrect(m_process->GetTarget(), file_path)) {
+ lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
+ bool is_loaded = false;
+ Status error =
+ m_process->GetFileLoadAddress(entry.file_spec, is_loaded, load_addr);
+ if (error.Success() && is_loaded)
+ entry.base_addr = load_addr;
+ }
+}
+
+bool DYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry) {
+ entry.clear();
+
+ entry.link_addr = addr;
+
+ if (!(addr = ReadPointer(addr, &entry.base_addr)))
+ return false;
+
+ // mips adds an extra load offset field to the link map struct on FreeBSD and
+ // NetBSD (need to validate other OSes).
+ // http://svnweb.freebsd.org/base/head/sys/sys/link_elf.h?revision=217153&view=markup#l57
+ const ArchSpec &arch = m_process->GetTarget().GetArchitecture();
+ if ((arch.GetTriple().getOS() == llvm::Triple::FreeBSD ||
+ arch.GetTriple().getOS() == llvm::Triple::NetBSD) &&
+ arch.IsMIPS()) {
+ addr_t mips_l_offs;
+ if (!(addr = ReadPointer(addr, &mips_l_offs)))
+ return false;
+ if (mips_l_offs != 0 && mips_l_offs != entry.base_addr)
+ return false;
+ }
+
+ if (!(addr = ReadPointer(addr, &entry.path_addr)))
+ return false;
+
+ if (!(addr = ReadPointer(addr, &entry.dyn_addr)))
+ return false;
+
+ if (!(addr = ReadPointer(addr, &entry.next)))
+ return false;
+
+ if (!(addr = ReadPointer(addr, &entry.prev)))
+ return false;
+
+ std::string file_path = ReadStringFromMemory(entry.path_addr);
+ entry.file_spec.SetFile(file_path, FileSpec::Style::native);
+
+ UpdateBaseAddrIfNecessary(entry, file_path);
+
+ return true;
+}
+
+bool DYLDRendezvous::FindMetadata(const char *name, PThreadField field,
+ uint32_t &value) {
+ Target &target = m_process->GetTarget();
+
+ SymbolContextList list;
+ if (!target.GetImages().FindSymbolsWithNameAndType(ConstString(name),
+ eSymbolTypeAny, list))
+ return false;
+
+ Address address = list[0].symbol->GetAddress();
+ addr_t addr = address.GetLoadAddress(&target);
+ if (addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ Status error;
+ value = (uint32_t)m_process->ReadUnsignedIntegerFromMemory(
+ addr + field * sizeof(uint32_t), sizeof(uint32_t), 0, error);
+ if (error.Fail())
+ return false;
+
+ if (field == eSize)
+ value /= 8; // convert bits to bytes
+
+ return true;
+}
+
+const DYLDRendezvous::ThreadInfo &DYLDRendezvous::GetThreadInfo() {
+ if (!m_thread_info.valid) {
+ bool ok = true;
+
+ ok &= FindMetadata("_thread_db_pthread_dtvp", eOffset,
+ m_thread_info.dtv_offset);
+ ok &=
+ FindMetadata("_thread_db_dtv_dtv", eSize, m_thread_info.dtv_slot_size);
+ ok &= FindMetadata("_thread_db_link_map_l_tls_modid", eOffset,
+ m_thread_info.modid_offset);
+ ok &= FindMetadata("_thread_db_dtv_t_pointer_val", eOffset,
+ m_thread_info.tls_offset);
+
+ if (ok)
+ m_thread_info.valid = true;
+ }
+
+ return m_thread_info;
+}
+
+void DYLDRendezvous::DumpToLog(Log *log) const {
+ int state = GetState();
+
+ if (!log)
+ return;
+
+ log->PutCString("DYLDRendezvous:");
+ log->Printf(" Address: %" PRIx64, GetRendezvousAddress());
+ log->Printf(" Version: %" PRIu64, GetVersion());
+ log->Printf(" Link : %" PRIx64, GetLinkMapAddress());
+ log->Printf(" Break : %" PRIx64, GetBreakAddress());
+ log->Printf(" LDBase : %" PRIx64, GetLDBase());
+ log->Printf(" State : %s",
+ (state == eConsistent)
+ ? "consistent"
+ : (state == eAdd) ? "add" : (state == eDelete) ? "delete"
+ : "unknown");
+
+ iterator I = begin();
+ iterator E = end();
+
+ if (I != E)
+ log->PutCString("DYLDRendezvous SOEntries:");
+
+ for (int i = 1; I != E; ++I, ++i) {
+ log->Printf("\n SOEntry [%d] %s", i, I->file_spec.GetCString());
+ log->Printf(" Base : %" PRIx64, I->base_addr);
+ log->Printf(" Path : %" PRIx64, I->path_addr);
+ log->Printf(" Dyn : %" PRIx64, I->dyn_addr);
+ log->Printf(" Next : %" PRIx64, I->next);
+ log->Printf(" Prev : %" PRIx64, I->prev);
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h
new file mode 100644
index 000000000000..993e62f5e9f9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h
@@ -0,0 +1,253 @@
+//===-- DYLDRendezvous.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 liblldb_Rendezvous_H_
+#define liblldb_Rendezvous_H_
+
+#include <list>
+#include <string>
+
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-types.h"
+
+#include "lldb/Core/LoadedModuleInfoList.h"
+
+using lldb_private::LoadedModuleInfoList;
+
+namespace lldb_private {
+class Process;
+}
+
+/// \class DYLDRendezvous
+/// Interface to the runtime linker.
+///
+/// A structure is present in a processes memory space which is updated by the
+/// runtime liker each time a module is loaded or unloaded. This class
+/// provides an interface to this structure and maintains a consistent
+/// snapshot of the currently loaded modules.
+class DYLDRendezvous {
+
+ // This structure is used to hold the contents of the debug rendezvous
+ // information (struct r_debug) as found in the inferiors memory. Note that
+ // the layout of this struct is not binary compatible, it is simply large
+ // enough to hold the information on both 32 and 64 bit platforms.
+ struct Rendezvous {
+ uint64_t version;
+ lldb::addr_t map_addr;
+ lldb::addr_t brk;
+ uint64_t state;
+ lldb::addr_t ldbase;
+
+ Rendezvous() : version(0), map_addr(0), brk(0), state(0), ldbase(0) {}
+ };
+
+public:
+ // Various metadata supplied by the inferior's threading library to describe
+ // the per-thread state.
+ struct ThreadInfo {
+ bool valid; // whether we read valid metadata
+ uint32_t dtv_offset; // offset of DTV pointer within pthread
+ uint32_t dtv_slot_size; // size of one DTV slot
+ uint32_t modid_offset; // offset of module ID within link_map
+ uint32_t tls_offset; // offset of TLS pointer within DTV slot
+ };
+
+ DYLDRendezvous(lldb_private::Process *process);
+
+ /// Update the internal snapshot of runtime linker rendezvous and recompute
+ /// the currently loaded modules.
+ ///
+ /// This method should be called once one start up, then once each time the
+ /// runtime linker enters the function given by GetBreakAddress().
+ ///
+ /// \returns true on success and false on failure.
+ ///
+ /// \see GetBreakAddress().
+ bool Resolve();
+
+ /// \returns true if this rendezvous has been located in the inferiors
+ /// address space and false otherwise.
+ bool IsValid();
+
+ /// \returns the address of the rendezvous structure in the inferiors
+ /// address space.
+ lldb::addr_t GetRendezvousAddress() const { return m_rendezvous_addr; }
+
+ /// \returns the version of the rendezvous protocol being used.
+ uint64_t GetVersion() const { return m_current.version; }
+
+ /// \returns address in the inferiors address space containing the linked
+ /// list of shared object descriptors.
+ lldb::addr_t GetLinkMapAddress() const { return m_current.map_addr; }
+
+ /// A breakpoint should be set at this address and Resolve called on each
+ /// hit.
+ ///
+ /// \returns the address of a function called by the runtime linker each
+ /// time a module is loaded/unloaded, or about to be loaded/unloaded.
+ ///
+ /// \see Resolve()
+ lldb::addr_t GetBreakAddress() const { return m_current.brk; }
+
+ /// Returns the current state of the rendezvous structure.
+ uint64_t GetState() const { return m_current.state; }
+
+ /// \returns the base address of the runtime linker in the inferiors address
+ /// space.
+ lldb::addr_t GetLDBase() const { return m_current.ldbase; }
+
+ /// \returns the thread layout metadata from the inferiors thread library.
+ const ThreadInfo &GetThreadInfo();
+
+ /// \returns true if modules have been loaded into the inferior since the
+ /// last call to Resolve().
+ bool ModulesDidLoad() const { return !m_added_soentries.empty(); }
+
+ /// \returns true if modules have been unloaded from the inferior since the
+ /// last call to Resolve().
+ bool ModulesDidUnload() const { return !m_removed_soentries.empty(); }
+
+ void DumpToLog(lldb_private::Log *log) const;
+
+ /// Constants describing the state of the rendezvous.
+ ///
+ /// \see GetState().
+ enum RendezvousState { eConsistent, eAdd, eDelete };
+
+ /// Structure representing the shared objects currently loaded into the
+ /// inferior process.
+ ///
+ /// This object is a rough analogue to the struct link_map object which
+ /// actually lives in the inferiors memory.
+ struct SOEntry {
+ lldb::addr_t link_addr; ///< Address of this link_map.
+ lldb::addr_t base_addr; ///< Base address of the loaded object.
+ lldb::addr_t path_addr; ///< String naming the shared object.
+ lldb::addr_t dyn_addr; ///< Dynamic section of shared object.
+ lldb::addr_t next; ///< Address of next so_entry.
+ lldb::addr_t prev; ///< Address of previous so_entry.
+ lldb_private::FileSpec file_spec; ///< File spec of shared object.
+
+ SOEntry() { clear(); }
+
+ bool operator==(const SOEntry &entry) {
+ return file_spec == entry.file_spec;
+ }
+
+ void clear() {
+ link_addr = 0;
+ base_addr = 0;
+ path_addr = 0;
+ dyn_addr = 0;
+ next = 0;
+ prev = 0;
+ file_spec.Clear();
+ }
+ };
+
+protected:
+ typedef std::list<SOEntry> SOEntryList;
+
+public:
+ typedef SOEntryList::const_iterator iterator;
+
+ /// Iterators over all currently loaded modules.
+ iterator begin() const { return m_soentries.begin(); }
+ iterator end() const { return m_soentries.end(); }
+
+ /// Iterators over all modules loaded into the inferior since the last call
+ /// to Resolve().
+ iterator loaded_begin() const { return m_added_soentries.begin(); }
+ iterator loaded_end() const { return m_added_soentries.end(); }
+
+ /// Iterators over all modules unloaded from the inferior since the last
+ /// call to Resolve().
+ iterator unloaded_begin() const { return m_removed_soentries.begin(); }
+ iterator unloaded_end() const { return m_removed_soentries.end(); }
+
+protected:
+ lldb_private::Process *m_process;
+
+ // Cached copy of executable file spec
+ lldb_private::FileSpec m_exe_file_spec;
+
+ /// Location of the r_debug structure in the inferiors address space.
+ lldb::addr_t m_rendezvous_addr;
+
+ /// Current and previous snapshots of the rendezvous structure.
+ Rendezvous m_current;
+ Rendezvous m_previous;
+
+ /// List of currently loaded SO modules
+ LoadedModuleInfoList m_loaded_modules;
+
+ /// List of SOEntry objects corresponding to the current link map state.
+ SOEntryList m_soentries;
+
+ /// List of SOEntry's added to the link map since the last call to
+ /// Resolve().
+ SOEntryList m_added_soentries;
+
+ /// List of SOEntry's removed from the link map since the last call to
+ /// Resolve().
+ SOEntryList m_removed_soentries;
+
+ /// Threading metadata read from the inferior.
+ ThreadInfo m_thread_info;
+
+ /// Reads an unsigned integer of \p size bytes from the inferior's address
+ /// space starting at \p addr.
+ ///
+ /// \returns addr + size if the read was successful and false otherwise.
+ lldb::addr_t ReadWord(lldb::addr_t addr, uint64_t *dst, size_t size);
+
+ /// Reads an address from the inferior's address space starting at \p addr.
+ ///
+ /// \returns addr + target address size if the read was successful and
+ /// 0 otherwise.
+ lldb::addr_t ReadPointer(lldb::addr_t addr, lldb::addr_t *dst);
+
+ /// Reads a null-terminated C string from the memory location starting at @p
+ /// addr.
+ std::string ReadStringFromMemory(lldb::addr_t addr);
+
+ /// Reads an SOEntry starting at \p addr.
+ bool ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry);
+
+ /// Updates the current set of SOEntries, the set of added entries, and the
+ /// set of removed entries.
+ bool UpdateSOEntries(bool fromRemote = false);
+
+ bool FillSOEntryFromModuleInfo(
+ LoadedModuleInfoList::LoadedModuleInfo const &modInfo, SOEntry &entry);
+
+ bool SaveSOEntriesFromRemote(LoadedModuleInfoList &module_list);
+
+ bool AddSOEntriesFromRemote(LoadedModuleInfoList &module_list);
+
+ bool RemoveSOEntriesFromRemote(LoadedModuleInfoList &module_list);
+
+ bool AddSOEntries();
+
+ bool RemoveSOEntries();
+
+ void UpdateBaseAddrIfNecessary(SOEntry &entry, std::string const &file_path);
+
+ bool SOEntryIsMainExecutable(const SOEntry &entry);
+
+ /// Reads the current list of shared objects according to the link map
+ /// supplied by the runtime linker.
+ bool TakeSnapshot(SOEntryList &entry_list);
+
+ enum PThreadField { eSize, eNElem, eOffset };
+
+ bool FindMetadata(const char *name, PThreadField field, uint32_t &value);
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
new file mode 100644
index 000000000000..b55660899d0d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
@@ -0,0 +1,773 @@
+//===-- DynamicLoaderPOSIXDYLD.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
+//
+//===----------------------------------------------------------------------===//
+
+// Main header include
+#include "DynamicLoaderPOSIXDYLD.h"
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlanRunToAddress.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/ProcessInfo.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+void DynamicLoaderPOSIXDYLD::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void DynamicLoaderPOSIXDYLD::Terminate() {}
+
+lldb_private::ConstString DynamicLoaderPOSIXDYLD::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+lldb_private::ConstString DynamicLoaderPOSIXDYLD::GetPluginNameStatic() {
+ static ConstString g_name("linux-dyld");
+ return g_name;
+}
+
+const char *DynamicLoaderPOSIXDYLD::GetPluginDescriptionStatic() {
+ return "Dynamic loader plug-in that watches for shared library "
+ "loads/unloads in POSIX processes.";
+}
+
+uint32_t DynamicLoaderPOSIXDYLD::GetPluginVersion() { return 1; }
+
+DynamicLoader *DynamicLoaderPOSIXDYLD::CreateInstance(Process *process,
+ bool force) {
+ bool create = force;
+ if (!create) {
+ const llvm::Triple &triple_ref =
+ process->GetTarget().GetArchitecture().GetTriple();
+ if (triple_ref.getOS() == llvm::Triple::FreeBSD ||
+ triple_ref.getOS() == llvm::Triple::Linux ||
+ triple_ref.getOS() == llvm::Triple::NetBSD)
+ create = true;
+ }
+
+ if (create)
+ return new DynamicLoaderPOSIXDYLD(process);
+ return nullptr;
+}
+
+DynamicLoaderPOSIXDYLD::DynamicLoaderPOSIXDYLD(Process *process)
+ : DynamicLoader(process), m_rendezvous(process),
+ m_load_offset(LLDB_INVALID_ADDRESS), m_entry_point(LLDB_INVALID_ADDRESS),
+ m_auxv(), m_dyld_bid(LLDB_INVALID_BREAK_ID),
+ m_vdso_base(LLDB_INVALID_ADDRESS),
+ m_interpreter_base(LLDB_INVALID_ADDRESS) {}
+
+DynamicLoaderPOSIXDYLD::~DynamicLoaderPOSIXDYLD() {
+ if (m_dyld_bid != LLDB_INVALID_BREAK_ID) {
+ m_process->GetTarget().RemoveBreakpointByID(m_dyld_bid);
+ m_dyld_bid = LLDB_INVALID_BREAK_ID;
+ }
+}
+
+void DynamicLoaderPOSIXDYLD::DidAttach() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s() pid %" PRIu64, __FUNCTION__,
+ m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID);
+ m_auxv = llvm::make_unique<AuxVector>(m_process->GetAuxvData());
+
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64 " reloaded auxv data",
+ __FUNCTION__,
+ m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID);
+
+ // ask the process if it can load any of its own modules
+ m_process->LoadModules();
+
+ ModuleSP executable_sp = GetTargetExecutable();
+ ResolveExecutableModule(executable_sp);
+
+ // find the main process load offset
+ addr_t load_offset = ComputeLoadOffset();
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64
+ " executable '%s', load_offset 0x%" PRIx64,
+ __FUNCTION__,
+ m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID,
+ executable_sp ? executable_sp->GetFileSpec().GetPath().c_str()
+ : "<null executable>",
+ load_offset);
+
+ EvalSpecialModulesStatus();
+
+ // if we dont have a load address we cant re-base
+ bool rebase_exec = load_offset != LLDB_INVALID_ADDRESS;
+
+ // if we have a valid executable
+ if (executable_sp.get()) {
+ lldb_private::ObjectFile *obj = executable_sp->GetObjectFile();
+ if (obj) {
+ // don't rebase if the module already has a load address
+ Target &target = m_process->GetTarget();
+ Address addr = obj->GetImageInfoAddress(&target);
+ if (addr.GetLoadAddress(&target) != LLDB_INVALID_ADDRESS)
+ rebase_exec = false;
+ }
+ } else {
+ // no executable, nothing to re-base
+ rebase_exec = false;
+ }
+
+ // if the target executable should be re-based
+ if (rebase_exec) {
+ ModuleList module_list;
+
+ module_list.Append(executable_sp);
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64
+ " added executable '%s' to module load list",
+ __FUNCTION__,
+ m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID,
+ executable_sp->GetFileSpec().GetPath().c_str());
+
+ UpdateLoadedSections(executable_sp, LLDB_INVALID_ADDRESS, load_offset,
+ true);
+
+ LoadAllCurrentModules();
+
+ m_process->GetTarget().ModulesDidLoad(module_list);
+ if (log) {
+ log->Printf("DynamicLoaderPOSIXDYLD::%s told the target about the "
+ "modules that loaded:",
+ __FUNCTION__);
+ for (auto module_sp : module_list.Modules()) {
+ log->Printf("-- [module] %s (pid %" PRIu64 ")",
+ module_sp ? module_sp->GetFileSpec().GetPath().c_str()
+ : "<null>",
+ m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID);
+ }
+ }
+ }
+
+ if (executable_sp.get()) {
+ if (!SetRendezvousBreakpoint()) {
+ // If we cannot establish rendezvous breakpoint right now we'll try again
+ // at entry point.
+ ProbeEntry();
+ }
+ }
+}
+
+void DynamicLoaderPOSIXDYLD::DidLaunch() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s()", __FUNCTION__);
+
+ ModuleSP executable;
+ addr_t load_offset;
+
+ m_auxv = llvm::make_unique<AuxVector>(m_process->GetAuxvData());
+
+ executable = GetTargetExecutable();
+ load_offset = ComputeLoadOffset();
+ EvalSpecialModulesStatus();
+
+ if (executable.get() && load_offset != LLDB_INVALID_ADDRESS) {
+ ModuleList module_list;
+ module_list.Append(executable);
+ UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_offset, true);
+
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s about to call ProbeEntry()",
+ __FUNCTION__);
+
+ if (!SetRendezvousBreakpoint()) {
+ // If we cannot establish rendezvous breakpoint right now we'll try again
+ // at entry point.
+ ProbeEntry();
+ }
+
+ LoadVDSO();
+ m_process->GetTarget().ModulesDidLoad(module_list);
+ }
+}
+
+Status DynamicLoaderPOSIXDYLD::CanLoadImage() { return Status(); }
+
+void DynamicLoaderPOSIXDYLD::UpdateLoadedSections(ModuleSP module,
+ addr_t link_map_addr,
+ addr_t base_addr,
+ bool base_addr_is_offset) {
+ m_loaded_modules[module] = link_map_addr;
+ UpdateLoadedSectionsCommon(module, base_addr, base_addr_is_offset);
+}
+
+void DynamicLoaderPOSIXDYLD::UnloadSections(const ModuleSP module) {
+ m_loaded_modules.erase(module);
+
+ UnloadSectionsCommon(module);
+}
+
+void DynamicLoaderPOSIXDYLD::ProbeEntry() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+
+ const addr_t entry = GetEntryPoint();
+ if (entry == LLDB_INVALID_ADDRESS) {
+ if (log)
+ log->Printf(
+ "DynamicLoaderPOSIXDYLD::%s pid %" PRIu64
+ " GetEntryPoint() returned no address, not setting entry breakpoint",
+ __FUNCTION__,
+ m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID);
+ return;
+ }
+
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64
+ " GetEntryPoint() returned address 0x%" PRIx64
+ ", setting entry breakpoint",
+ __FUNCTION__,
+ m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID,
+ entry);
+
+ if (m_process) {
+ Breakpoint *const entry_break =
+ m_process->GetTarget().CreateBreakpoint(entry, true, false).get();
+ entry_break->SetCallback(EntryBreakpointHit, this, true);
+ entry_break->SetBreakpointKind("shared-library-event");
+
+ // Shoudn't hit this more than once.
+ entry_break->SetOneShot(true);
+ }
+}
+
+// The runtime linker has run and initialized the rendezvous structure once the
+// process has hit its entry point. When we hit the corresponding breakpoint
+// we interrogate the rendezvous structure to get the load addresses of all
+// dependent modules for the process. Similarly, we can discover the runtime
+// linker function and setup a breakpoint to notify us of any dynamically
+// loaded modules (via dlopen).
+bool DynamicLoaderPOSIXDYLD::EntryBreakpointHit(
+ void *baton, StoppointCallbackContext *context, user_id_t break_id,
+ user_id_t break_loc_id) {
+ assert(baton && "null baton");
+ if (!baton)
+ return false;
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ DynamicLoaderPOSIXDYLD *const dyld_instance =
+ static_cast<DynamicLoaderPOSIXDYLD *>(baton);
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s called for pid %" PRIu64,
+ __FUNCTION__,
+ dyld_instance->m_process ? dyld_instance->m_process->GetID()
+ : LLDB_INVALID_PROCESS_ID);
+
+ // Disable the breakpoint --- if a stop happens right after this, which we've
+ // seen on occasion, we don't want the breakpoint stepping thread-plan logic
+ // to show a breakpoint instruction at the disassembled entry point to the
+ // program. Disabling it prevents it. (One-shot is not enough - one-shot
+ // removal logic only happens after the breakpoint goes public, which wasn't
+ // happening in our scenario).
+ if (dyld_instance->m_process) {
+ BreakpointSP breakpoint_sp =
+ dyld_instance->m_process->GetTarget().GetBreakpointByID(break_id);
+ if (breakpoint_sp) {
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64
+ " disabling breakpoint id %" PRIu64,
+ __FUNCTION__, dyld_instance->m_process->GetID(), break_id);
+ breakpoint_sp->SetEnabled(false);
+ } else {
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64
+ " failed to find breakpoint for breakpoint id %" PRIu64,
+ __FUNCTION__, dyld_instance->m_process->GetID(), break_id);
+ }
+ } else {
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s breakpoint id %" PRIu64
+ " no Process instance! Cannot disable breakpoint",
+ __FUNCTION__, break_id);
+ }
+
+ dyld_instance->LoadAllCurrentModules();
+ dyld_instance->SetRendezvousBreakpoint();
+ return false; // Continue running.
+}
+
+bool DynamicLoaderPOSIXDYLD::SetRendezvousBreakpoint() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ if (m_dyld_bid != LLDB_INVALID_BREAK_ID) {
+ LLDB_LOG(log,
+ "Rendezvous breakpoint breakpoint id {0} for pid {1}"
+ "is already set.",
+ m_dyld_bid,
+ m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID);
+ return true;
+ }
+
+ addr_t break_addr;
+ Target &target = m_process->GetTarget();
+ BreakpointSP dyld_break;
+ if (m_rendezvous.IsValid()) {
+ break_addr = m_rendezvous.GetBreakAddress();
+ LLDB_LOG(log, "Setting rendezvous break address for pid {0} at {1:x}",
+ m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID,
+ break_addr);
+ dyld_break = target.CreateBreakpoint(break_addr, true, false);
+ } else {
+ LLDB_LOG(log, "Rendezvous structure is not set up yet. "
+ "Trying to locate rendezvous breakpoint in the interpreter "
+ "by symbol name.");
+ ModuleSP interpreter = LoadInterpreterModule();
+ if (!interpreter) {
+ LLDB_LOG(log, "Can't find interpreter, rendezvous breakpoint isn't set.");
+ return false;
+ }
+
+ // Function names from different dynamic loaders that are known to be used
+ // as rendezvous between the loader and debuggers.
+ static std::vector<std::string> DebugStateCandidates{
+ "_dl_debug_state", "rtld_db_dlactivity", "__dl_rtld_db_dlactivity",
+ "r_debug_state", "_r_debug_state", "_rtld_debug_state",
+ };
+
+ FileSpecList containingModules;
+ containingModules.Append(interpreter->GetFileSpec());
+ dyld_break = target.CreateBreakpoint(
+ &containingModules, nullptr /* containingSourceFiles */,
+ DebugStateCandidates, eFunctionNameTypeFull, eLanguageTypeC,
+ 0, /* offset */
+ eLazyBoolNo, /* skip_prologue */
+ true, /* internal */
+ false /* request_hardware */);
+ }
+
+ if (dyld_break->GetNumResolvedLocations() != 1) {
+ LLDB_LOG(
+ log,
+ "Rendezvous breakpoint has abnormal number of"
+ " resolved locations ({0}) in pid {1}. It's supposed to be exactly 1.",
+ dyld_break->GetNumResolvedLocations(),
+ m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID);
+
+ target.RemoveBreakpointByID(dyld_break->GetID());
+ return false;
+ }
+
+ BreakpointLocationSP location = dyld_break->GetLocationAtIndex(0);
+ LLDB_LOG(log,
+ "Successfully set rendezvous breakpoint at address {0:x} "
+ "for pid {1}",
+ location->GetLoadAddress(),
+ m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID);
+
+ dyld_break->SetCallback(RendezvousBreakpointHit, this, true);
+ dyld_break->SetBreakpointKind("shared-library-event");
+ m_dyld_bid = dyld_break->GetID();
+ return true;
+}
+
+bool DynamicLoaderPOSIXDYLD::RendezvousBreakpointHit(
+ void *baton, StoppointCallbackContext *context, user_id_t break_id,
+ user_id_t break_loc_id) {
+ assert(baton && "null baton");
+ if (!baton)
+ return false;
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ DynamicLoaderPOSIXDYLD *const dyld_instance =
+ static_cast<DynamicLoaderPOSIXDYLD *>(baton);
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s called for pid %" PRIu64,
+ __FUNCTION__,
+ dyld_instance->m_process ? dyld_instance->m_process->GetID()
+ : LLDB_INVALID_PROCESS_ID);
+
+ dyld_instance->RefreshModules();
+
+ // Return true to stop the target, false to just let the target run.
+ const bool stop_when_images_change = dyld_instance->GetStopWhenImagesChange();
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64
+ " stop_when_images_change=%s",
+ __FUNCTION__,
+ dyld_instance->m_process ? dyld_instance->m_process->GetID()
+ : LLDB_INVALID_PROCESS_ID,
+ stop_when_images_change ? "true" : "false");
+ return stop_when_images_change;
+}
+
+void DynamicLoaderPOSIXDYLD::RefreshModules() {
+ if (!m_rendezvous.Resolve())
+ return;
+
+ DYLDRendezvous::iterator I;
+ DYLDRendezvous::iterator E;
+
+ ModuleList &loaded_modules = m_process->GetTarget().GetImages();
+
+ if (m_rendezvous.ModulesDidLoad()) {
+ ModuleList new_modules;
+
+ E = m_rendezvous.loaded_end();
+ for (I = m_rendezvous.loaded_begin(); I != E; ++I) {
+ ModuleSP module_sp =
+ LoadModuleAtAddress(I->file_spec, I->link_addr, I->base_addr, true);
+ if (module_sp.get()) {
+ loaded_modules.AppendIfNeeded(module_sp);
+ new_modules.Append(module_sp);
+ }
+ }
+ m_process->GetTarget().ModulesDidLoad(new_modules);
+ }
+
+ if (m_rendezvous.ModulesDidUnload()) {
+ ModuleList old_modules;
+
+ E = m_rendezvous.unloaded_end();
+ for (I = m_rendezvous.unloaded_begin(); I != E; ++I) {
+ ModuleSpec module_spec{I->file_spec};
+ ModuleSP module_sp = loaded_modules.FindFirstModule(module_spec);
+
+ if (module_sp.get()) {
+ old_modules.Append(module_sp);
+ UnloadSections(module_sp);
+ }
+ }
+ loaded_modules.Remove(old_modules);
+ m_process->GetTarget().ModulesDidUnload(old_modules, false);
+ }
+}
+
+ThreadPlanSP
+DynamicLoaderPOSIXDYLD::GetStepThroughTrampolinePlan(Thread &thread,
+ bool stop) {
+ ThreadPlanSP thread_plan_sp;
+
+ StackFrame *frame = thread.GetStackFrameAtIndex(0).get();
+ const SymbolContext &context = frame->GetSymbolContext(eSymbolContextSymbol);
+ Symbol *sym = context.symbol;
+
+ if (sym == nullptr || !sym->IsTrampoline())
+ return thread_plan_sp;
+
+ ConstString sym_name = sym->GetName();
+ if (!sym_name)
+ return thread_plan_sp;
+
+ SymbolContextList target_symbols;
+ Target &target = thread.GetProcess()->GetTarget();
+ const ModuleList &images = target.GetImages();
+
+ images.FindSymbolsWithNameAndType(sym_name, eSymbolTypeCode, target_symbols);
+ size_t num_targets = target_symbols.GetSize();
+ if (!num_targets)
+ return thread_plan_sp;
+
+ typedef std::vector<lldb::addr_t> AddressVector;
+ AddressVector addrs;
+ for (size_t i = 0; i < num_targets; ++i) {
+ SymbolContext context;
+ AddressRange range;
+ if (target_symbols.GetContextAtIndex(i, context)) {
+ context.GetAddressRange(eSymbolContextEverything, 0, false, range);
+ lldb::addr_t addr = range.GetBaseAddress().GetLoadAddress(&target);
+ if (addr != LLDB_INVALID_ADDRESS)
+ addrs.push_back(addr);
+ }
+ }
+
+ if (addrs.size() > 0) {
+ AddressVector::iterator start = addrs.begin();
+ AddressVector::iterator end = addrs.end();
+
+ llvm::sort(start, end);
+ addrs.erase(std::unique(start, end), end);
+ thread_plan_sp =
+ std::make_shared<ThreadPlanRunToAddress>(thread, addrs, stop);
+ }
+
+ return thread_plan_sp;
+}
+
+void DynamicLoaderPOSIXDYLD::LoadVDSO() {
+ if (m_vdso_base == LLDB_INVALID_ADDRESS)
+ return;
+
+ FileSpec file("[vdso]");
+
+ MemoryRegionInfo info;
+ Status status = m_process->GetMemoryRegionInfo(m_vdso_base, info);
+ if (status.Fail()) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ LLDB_LOG(log, "Failed to get vdso region info: {0}", status);
+ return;
+ }
+
+ if (ModuleSP module_sp = m_process->ReadModuleFromMemory(
+ file, m_vdso_base, info.GetRange().GetByteSize())) {
+ UpdateLoadedSections(module_sp, LLDB_INVALID_ADDRESS, m_vdso_base, false);
+ m_process->GetTarget().GetImages().AppendIfNeeded(module_sp);
+ }
+}
+
+ModuleSP DynamicLoaderPOSIXDYLD::LoadInterpreterModule() {
+ if (m_interpreter_base == LLDB_INVALID_ADDRESS)
+ return nullptr;
+
+ MemoryRegionInfo info;
+ Target &target = m_process->GetTarget();
+ Status status = m_process->GetMemoryRegionInfo(m_interpreter_base, info);
+ if (status.Fail() || info.GetMapped() != MemoryRegionInfo::eYes ||
+ info.GetName().IsEmpty()) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ LLDB_LOG(log, "Failed to get interpreter region info: {0}", status);
+ return nullptr;
+ }
+
+ FileSpec file(info.GetName().GetCString());
+ ModuleSpec module_spec(file, target.GetArchitecture());
+
+ if (ModuleSP module_sp = target.GetOrCreateModule(module_spec,
+ true /* notify */)) {
+ UpdateLoadedSections(module_sp, LLDB_INVALID_ADDRESS, m_interpreter_base,
+ false);
+ return module_sp;
+ }
+ return nullptr;
+}
+
+void DynamicLoaderPOSIXDYLD::LoadAllCurrentModules() {
+ DYLDRendezvous::iterator I;
+ DYLDRendezvous::iterator E;
+ ModuleList module_list;
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+
+ LoadVDSO();
+
+ if (!m_rendezvous.Resolve()) {
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s unable to resolve POSIX DYLD "
+ "rendezvous address",
+ __FUNCTION__);
+ return;
+ }
+
+ // The rendezvous class doesn't enumerate the main module, so track that
+ // ourselves here.
+ ModuleSP executable = GetTargetExecutable();
+ m_loaded_modules[executable] = m_rendezvous.GetLinkMapAddress();
+
+ std::vector<FileSpec> module_names;
+ for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I)
+ module_names.push_back(I->file_spec);
+ m_process->PrefetchModuleSpecs(
+ module_names, m_process->GetTarget().GetArchitecture().GetTriple());
+
+ for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I) {
+ ModuleSP module_sp =
+ LoadModuleAtAddress(I->file_spec, I->link_addr, I->base_addr, true);
+ if (module_sp.get()) {
+ LLDB_LOG(log, "LoadAllCurrentModules loading module: {0}",
+ I->file_spec.GetFilename());
+ module_list.Append(module_sp);
+ } else {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ if (log)
+ log->Printf(
+ "DynamicLoaderPOSIXDYLD::%s failed loading module %s at 0x%" PRIx64,
+ __FUNCTION__, I->file_spec.GetCString(), I->base_addr);
+ }
+ }
+
+ m_process->GetTarget().ModulesDidLoad(module_list);
+}
+
+addr_t DynamicLoaderPOSIXDYLD::ComputeLoadOffset() {
+ addr_t virt_entry;
+
+ if (m_load_offset != LLDB_INVALID_ADDRESS)
+ return m_load_offset;
+
+ if ((virt_entry = GetEntryPoint()) == LLDB_INVALID_ADDRESS)
+ return LLDB_INVALID_ADDRESS;
+
+ ModuleSP module = m_process->GetTarget().GetExecutableModule();
+ if (!module)
+ return LLDB_INVALID_ADDRESS;
+
+ ObjectFile *exe = module->GetObjectFile();
+ if (!exe)
+ return LLDB_INVALID_ADDRESS;
+
+ Address file_entry = exe->GetEntryPointAddress();
+
+ if (!file_entry.IsValid())
+ return LLDB_INVALID_ADDRESS;
+
+ m_load_offset = virt_entry - file_entry.GetFileAddress();
+ return m_load_offset;
+}
+
+void DynamicLoaderPOSIXDYLD::EvalSpecialModulesStatus() {
+ if (llvm::Optional<uint64_t> vdso_base =
+ m_auxv->GetAuxValue(AuxVector::AUXV_AT_SYSINFO_EHDR))
+ m_vdso_base = *vdso_base;
+
+ if (llvm::Optional<uint64_t> interpreter_base =
+ m_auxv->GetAuxValue(AuxVector::AUXV_AT_BASE))
+ m_interpreter_base = *interpreter_base;
+}
+
+addr_t DynamicLoaderPOSIXDYLD::GetEntryPoint() {
+ if (m_entry_point != LLDB_INVALID_ADDRESS)
+ return m_entry_point;
+
+ if (m_auxv == nullptr)
+ return LLDB_INVALID_ADDRESS;
+
+ llvm::Optional<uint64_t> entry_point =
+ m_auxv->GetAuxValue(AuxVector::AUXV_AT_ENTRY);
+ if (!entry_point)
+ return LLDB_INVALID_ADDRESS;
+
+ m_entry_point = static_cast<addr_t>(*entry_point);
+
+ const ArchSpec &arch = m_process->GetTarget().GetArchitecture();
+
+ // On ppc64, the entry point is actually a descriptor. Dereference it.
+ if (arch.GetMachine() == llvm::Triple::ppc64)
+ m_entry_point = ReadUnsignedIntWithSizeInBytes(m_entry_point, 8);
+
+ return m_entry_point;
+}
+
+lldb::addr_t
+DynamicLoaderPOSIXDYLD::GetThreadLocalData(const lldb::ModuleSP module_sp,
+ const lldb::ThreadSP thread,
+ lldb::addr_t tls_file_addr) {
+ auto it = m_loaded_modules.find(module_sp);
+ if (it == m_loaded_modules.end())
+ return LLDB_INVALID_ADDRESS;
+
+ addr_t link_map = it->second;
+ if (link_map == LLDB_INVALID_ADDRESS)
+ return LLDB_INVALID_ADDRESS;
+
+ const DYLDRendezvous::ThreadInfo &metadata = m_rendezvous.GetThreadInfo();
+ if (!metadata.valid)
+ return LLDB_INVALID_ADDRESS;
+
+ // Get the thread pointer.
+ addr_t tp = thread->GetThreadPointer();
+ if (tp == LLDB_INVALID_ADDRESS)
+ return LLDB_INVALID_ADDRESS;
+
+ // Find the module's modid.
+ int modid_size = 4; // FIXME(spucci): This isn't right for big-endian 64-bit
+ int64_t modid = ReadUnsignedIntWithSizeInBytes(
+ link_map + metadata.modid_offset, modid_size);
+ if (modid == -1)
+ return LLDB_INVALID_ADDRESS;
+
+ // Lookup the DTV structure for this thread.
+ addr_t dtv_ptr = tp + metadata.dtv_offset;
+ addr_t dtv = ReadPointer(dtv_ptr);
+ if (dtv == LLDB_INVALID_ADDRESS)
+ return LLDB_INVALID_ADDRESS;
+
+ // Find the TLS block for this module.
+ addr_t dtv_slot = dtv + metadata.dtv_slot_size * modid;
+ addr_t tls_block = ReadPointer(dtv_slot + metadata.tls_offset);
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::Performed TLS lookup: "
+ "module=%s, link_map=0x%" PRIx64 ", tp=0x%" PRIx64
+ ", modid=%" PRId64 ", tls_block=0x%" PRIx64 "\n",
+ module_sp->GetObjectName().AsCString(""), link_map, tp,
+ (int64_t)modid, tls_block);
+
+ if (tls_block == LLDB_INVALID_ADDRESS)
+ return LLDB_INVALID_ADDRESS;
+ else
+ return tls_block + tls_file_addr;
+}
+
+void DynamicLoaderPOSIXDYLD::ResolveExecutableModule(
+ lldb::ModuleSP &module_sp) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+
+ if (m_process == nullptr)
+ return;
+
+ auto &target = m_process->GetTarget();
+ const auto platform_sp = target.GetPlatform();
+
+ ProcessInstanceInfo process_info;
+ if (!m_process->GetProcessInfo(process_info)) {
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s - failed to get process info for "
+ "pid %" PRIu64,
+ __FUNCTION__, m_process->GetID());
+ return;
+ }
+
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s - got executable by pid %" PRIu64
+ ": %s",
+ __FUNCTION__, m_process->GetID(),
+ process_info.GetExecutableFile().GetPath().c_str());
+
+ ModuleSpec module_spec(process_info.GetExecutableFile(),
+ process_info.GetArchitecture());
+ if (module_sp && module_sp->MatchesModuleSpec(module_spec))
+ return;
+
+ const auto executable_search_paths(Target::GetDefaultExecutableSearchPaths());
+ auto error = platform_sp->ResolveExecutable(
+ module_spec, module_sp,
+ !executable_search_paths.IsEmpty() ? &executable_search_paths : nullptr);
+ if (error.Fail()) {
+ StreamString stream;
+ module_spec.Dump(stream);
+
+ if (log)
+ log->Printf("DynamicLoaderPOSIXDYLD::%s - failed to resolve executable "
+ "with module spec \"%s\": %s",
+ __FUNCTION__, stream.GetData(), error.AsCString());
+ return;
+ }
+
+ target.SetExecutableModule(module_sp, eLoadDependentsNo);
+}
+
+bool DynamicLoaderPOSIXDYLD::AlwaysRelyOnEHUnwindInfo(
+ lldb_private::SymbolContext &sym_ctx) {
+ ModuleSP module_sp;
+ if (sym_ctx.symbol)
+ module_sp = sym_ctx.symbol->GetAddressRef().GetModule();
+ if (!module_sp && sym_ctx.function)
+ module_sp =
+ sym_ctx.function->GetAddressRange().GetBaseAddress().GetModule();
+ if (!module_sp)
+ return false;
+
+ return module_sp->GetFileSpec().GetPath() == "[vdso]";
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h
new file mode 100644
index 000000000000..0630d1eb11d1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h
@@ -0,0 +1,165 @@
+//===-- DynamicLoaderPOSIXDYLD.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 liblldb_DynamicLoaderPOSIXDYLD_h_
+#define liblldb_DynamicLoaderPOSIXDYLD_h_
+
+#include <map>
+#include <memory>
+
+#include "DYLDRendezvous.h"
+#include "Plugins/Process/Utility/AuxVector.h"
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Target/DynamicLoader.h"
+
+class AuxVector;
+
+class DynamicLoaderPOSIXDYLD : public lldb_private::DynamicLoader {
+public:
+ DynamicLoaderPOSIXDYLD(lldb_private::Process *process);
+
+ ~DynamicLoaderPOSIXDYLD() override;
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::DynamicLoader *
+ CreateInstance(lldb_private::Process *process, bool force);
+
+ // DynamicLoader protocol
+
+ void DidAttach() override;
+
+ void DidLaunch() override;
+
+ lldb::ThreadPlanSP GetStepThroughTrampolinePlan(lldb_private::Thread &thread,
+ bool stop_others) override;
+
+ lldb_private::Status CanLoadImage() override;
+
+ lldb::addr_t GetThreadLocalData(const lldb::ModuleSP module,
+ const lldb::ThreadSP thread,
+ lldb::addr_t tls_file_addr) override;
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ /// Runtime linker rendezvous structure.
+ DYLDRendezvous m_rendezvous;
+
+ /// Virtual load address of the inferior process.
+ lldb::addr_t m_load_offset;
+
+ /// Virtual entry address of the inferior process.
+ lldb::addr_t m_entry_point;
+
+ /// Auxiliary vector of the inferior process.
+ std::unique_ptr<AuxVector> m_auxv;
+
+ /// Rendezvous breakpoint.
+ lldb::break_id_t m_dyld_bid;
+
+ /// Contains AT_SYSINFO_EHDR, which means a vDSO has been
+ /// mapped to the address space
+ lldb::addr_t m_vdso_base;
+
+ /// Contains AT_BASE, which means a dynamic loader has been
+ /// mapped to the address space
+ lldb::addr_t m_interpreter_base;
+
+ /// Loaded module list. (link map for each module)
+ std::map<lldb::ModuleWP, lldb::addr_t, std::owner_less<lldb::ModuleWP>>
+ m_loaded_modules;
+
+ /// If possible sets a breakpoint on a function called by the runtime
+ /// linker each time a module is loaded or unloaded.
+ bool SetRendezvousBreakpoint();
+
+ /// Callback routine which updates the current list of loaded modules based
+ /// on the information supplied by the runtime linker.
+ static bool RendezvousBreakpointHit(
+ void *baton, lldb_private::StoppointCallbackContext *context,
+ lldb::user_id_t break_id, lldb::user_id_t break_loc_id);
+
+ /// Helper method for RendezvousBreakpointHit. Updates LLDB's current set
+ /// of loaded modules.
+ void RefreshModules();
+
+ /// Updates the load address of every allocatable section in \p module.
+ ///
+ /// \param module The module to traverse.
+ ///
+ /// \param link_map_addr The virtual address of the link map for the @p
+ /// module.
+ ///
+ /// \param base_addr The virtual base address \p module is loaded at.
+ void UpdateLoadedSections(lldb::ModuleSP module, lldb::addr_t link_map_addr,
+ lldb::addr_t base_addr,
+ bool base_addr_is_offset) override;
+
+ /// Removes the loaded sections from the target in \p module.
+ ///
+ /// \param module The module to traverse.
+ void UnloadSections(const lldb::ModuleSP module) override;
+
+ /// Resolves the entry point for the current inferior process and sets a
+ /// breakpoint at that address.
+ void ProbeEntry();
+
+ /// Callback routine invoked when we hit the breakpoint on process entry.
+ ///
+ /// This routine is responsible for resolving the load addresses of all
+ /// dependent modules required by the inferior and setting up the rendezvous
+ /// breakpoint.
+ static bool
+ EntryBreakpointHit(void *baton,
+ lldb_private::StoppointCallbackContext *context,
+ lldb::user_id_t break_id, lldb::user_id_t break_loc_id);
+
+ /// Helper for the entry breakpoint callback. Resolves the load addresses
+ /// of all dependent modules.
+ virtual void LoadAllCurrentModules();
+
+ void LoadVDSO();
+
+ // Loading an interpreter module (if present) assumming m_interpreter_base
+ // already points to its base address.
+ lldb::ModuleSP LoadInterpreterModule();
+
+ /// Computes a value for m_load_offset returning the computed address on
+ /// success and LLDB_INVALID_ADDRESS on failure.
+ lldb::addr_t ComputeLoadOffset();
+
+ /// Computes a value for m_entry_point returning the computed address on
+ /// success and LLDB_INVALID_ADDRESS on failure.
+ lldb::addr_t GetEntryPoint();
+
+ /// Evaluate if Aux vectors contain vDSO and LD information
+ /// in case they do, read and assign the address to m_vdso_base
+ /// and m_interpreter_base.
+ void EvalSpecialModulesStatus();
+
+ /// Loads Module from inferior process.
+ void ResolveExecutableModule(lldb::ModuleSP &module_sp);
+
+ bool AlwaysRelyOnEHUnwindInfo(lldb_private::SymbolContext &sym_ctx) override;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(DynamicLoaderPOSIXDYLD);
+};
+
+#endif // liblldb_DynamicLoaderPOSIXDYLD_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp
new file mode 100644
index 000000000000..6bc951c4d35b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp
@@ -0,0 +1,154 @@
+//===-- DynamicLoaderStatic.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 "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/Target.h"
+
+#include "DynamicLoaderStatic.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Create an instance of this class. This function is filled into the plugin
+// info class that gets handed out by the plugin factory and allows the lldb to
+// instantiate an instance of this class.
+DynamicLoader *DynamicLoaderStatic::CreateInstance(Process *process,
+ bool force) {
+ bool create = force;
+ if (!create) {
+ const llvm::Triple &triple_ref =
+ process->GetTarget().GetArchitecture().GetTriple();
+ const llvm::Triple::OSType os_type = triple_ref.getOS();
+ if ((os_type == llvm::Triple::UnknownOS))
+ create = true;
+ }
+
+ if (!create) {
+ Module *exe_module = process->GetTarget().GetExecutableModulePointer();
+ if (exe_module) {
+ ObjectFile *object_file = exe_module->GetObjectFile();
+ if (object_file) {
+ create = (object_file->GetStrata() == ObjectFile::eStrataRawImage);
+ }
+ }
+ }
+
+ if (create)
+ return new DynamicLoaderStatic(process);
+ return nullptr;
+}
+
+// Constructor
+DynamicLoaderStatic::DynamicLoaderStatic(Process *process)
+ : DynamicLoader(process) {}
+
+// Destructor
+DynamicLoaderStatic::~DynamicLoaderStatic() {}
+
+/// Called after attaching a process.
+///
+/// Allow DynamicLoader plug-ins to execute some code after
+/// attaching to a process.
+void DynamicLoaderStatic::DidAttach() { LoadAllImagesAtFileAddresses(); }
+
+/// Called after attaching a process.
+///
+/// Allow DynamicLoader plug-ins to execute some code after
+/// attaching to a process.
+void DynamicLoaderStatic::DidLaunch() { LoadAllImagesAtFileAddresses(); }
+
+void DynamicLoaderStatic::LoadAllImagesAtFileAddresses() {
+ const ModuleList &module_list = m_process->GetTarget().GetImages();
+
+ ModuleList loaded_module_list;
+
+ // Disable JIT for static dynamic loader targets
+ m_process->SetCanJIT(false);
+
+ std::lock_guard<std::recursive_mutex> guard(module_list.GetMutex());
+
+ const size_t num_modules = module_list.GetSize();
+ for (uint32_t idx = 0; idx < num_modules; ++idx) {
+ ModuleSP module_sp(module_list.GetModuleAtIndexUnlocked(idx));
+ if (module_sp) {
+ bool changed = false;
+ ObjectFile *image_object_file = module_sp->GetObjectFile();
+ if (image_object_file) {
+ SectionList *section_list = image_object_file->GetSectionList();
+ if (section_list) {
+ // All sections listed in the dyld image info structure will all
+ // either be fixed up already, or they will all be off by a single
+ // slide amount that is determined by finding the first segment that
+ // is at file offset zero which also has bytes (a file size that is
+ // greater than zero) in the object file.
+
+ // Determine the slide amount (if any)
+ const size_t num_sections = section_list->GetSize();
+ size_t sect_idx = 0;
+ for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) {
+ // Iterate through the object file sections to find the first
+ // section that starts of file offset zero and that has bytes in
+ // the file...
+ SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx));
+ if (section_sp) {
+ if (m_process->GetTarget().SetSectionLoadAddress(
+ section_sp, section_sp->GetFileAddress()))
+ changed = true;
+ }
+ }
+ }
+ }
+
+ if (changed)
+ loaded_module_list.AppendIfNeeded(module_sp);
+ }
+ }
+
+ m_process->GetTarget().ModulesDidLoad(loaded_module_list);
+}
+
+ThreadPlanSP
+DynamicLoaderStatic::GetStepThroughTrampolinePlan(Thread &thread,
+ bool stop_others) {
+ return ThreadPlanSP();
+}
+
+Status DynamicLoaderStatic::CanLoadImage() {
+ Status error;
+ error.SetErrorString("can't load images on with a static debug session");
+ return error;
+}
+
+void DynamicLoaderStatic::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void DynamicLoaderStatic::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString DynamicLoaderStatic::GetPluginNameStatic() {
+ static ConstString g_name("static");
+ return g_name;
+}
+
+const char *DynamicLoaderStatic::GetPluginDescriptionStatic() {
+ return "Dynamic loader plug-in that will load any images at the static "
+ "addresses contained in each image.";
+}
+
+// PluginInterface protocol
+lldb_private::ConstString DynamicLoaderStatic::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t DynamicLoaderStatic::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.h b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.h
new file mode 100644
index 000000000000..fa9aded7286c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.h
@@ -0,0 +1,59 @@
+//===-- DynamicLoaderStatic.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 liblldb_DynamicLoaderStatic_h_
+#define liblldb_DynamicLoaderStatic_h_
+
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/UUID.h"
+
+class DynamicLoaderStatic : public lldb_private::DynamicLoader {
+public:
+ DynamicLoaderStatic(lldb_private::Process *process);
+
+ ~DynamicLoaderStatic() override;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::DynamicLoader *
+ CreateInstance(lldb_private::Process *process, bool force);
+
+ /// Called after attaching a process.
+ ///
+ /// Allow DynamicLoader plug-ins to execute some code after
+ /// attaching to a process.
+ void DidAttach() override;
+
+ void DidLaunch() override;
+
+ lldb::ThreadPlanSP GetStepThroughTrampolinePlan(lldb_private::Thread &thread,
+ bool stop_others) override;
+
+ lldb_private::Status CanLoadImage() override;
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+private:
+ void LoadAllImagesAtFileAddresses();
+
+ DISALLOW_COPY_AND_ASSIGN(DynamicLoaderStatic);
+};
+
+#endif // liblldb_DynamicLoaderStatic_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.cpp b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.cpp
new file mode 100644
index 000000000000..fa3fbe0d9fa6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.cpp
@@ -0,0 +1,225 @@
+//===-- DynamicLoaderWindowsDYLD.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 "DynamicLoaderWindowsDYLD.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadPlanStepInstruction.h"
+#include "lldb/Utility/Log.h"
+
+#include "llvm/ADT/Triple.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+DynamicLoaderWindowsDYLD::DynamicLoaderWindowsDYLD(Process *process)
+ : DynamicLoader(process) {}
+
+DynamicLoaderWindowsDYLD::~DynamicLoaderWindowsDYLD() {}
+
+void DynamicLoaderWindowsDYLD::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void DynamicLoaderWindowsDYLD::Terminate() {}
+
+ConstString DynamicLoaderWindowsDYLD::GetPluginNameStatic() {
+ static ConstString g_plugin_name("windows-dyld");
+ return g_plugin_name;
+}
+
+const char *DynamicLoaderWindowsDYLD::GetPluginDescriptionStatic() {
+ return "Dynamic loader plug-in that watches for shared library "
+ "loads/unloads in Windows processes.";
+}
+
+DynamicLoader *DynamicLoaderWindowsDYLD::CreateInstance(Process *process,
+ bool force) {
+ bool should_create = force;
+ if (!should_create) {
+ const llvm::Triple &triple_ref =
+ process->GetTarget().GetArchitecture().GetTriple();
+ if (triple_ref.getOS() == llvm::Triple::Win32)
+ should_create = true;
+ }
+
+ if (should_create)
+ return new DynamicLoaderWindowsDYLD(process);
+
+ return nullptr;
+}
+
+void DynamicLoaderWindowsDYLD::OnLoadModule(lldb::ModuleSP module_sp,
+ const ModuleSpec module_spec,
+ lldb::addr_t module_addr) {
+
+ // Resolve the module unless we already have one.
+ if (!module_sp) {
+ Status error;
+ module_sp = m_process->GetTarget().GetOrCreateModule(module_spec,
+ true /* notify */, &error);
+ if (error.Fail())
+ return;
+ }
+
+ m_loaded_modules[module_sp] = module_addr;
+ UpdateLoadedSectionsCommon(module_sp, module_addr, false);
+ ModuleList module_list;
+ module_list.Append(module_sp);
+ m_process->GetTarget().ModulesDidLoad(module_list);
+}
+
+void DynamicLoaderWindowsDYLD::OnUnloadModule(lldb::addr_t module_addr) {
+ Address resolved_addr;
+ if (!m_process->GetTarget().ResolveLoadAddress(module_addr, resolved_addr))
+ return;
+
+ ModuleSP module_sp = resolved_addr.GetModule();
+ if (module_sp) {
+ m_loaded_modules.erase(module_sp);
+ UnloadSectionsCommon(module_sp);
+ ModuleList module_list;
+ module_list.Append(module_sp);
+ m_process->GetTarget().ModulesDidUnload(module_list, false);
+ }
+}
+
+lldb::addr_t DynamicLoaderWindowsDYLD::GetLoadAddress(ModuleSP executable) {
+ // First, see if the load address is already cached.
+ auto it = m_loaded_modules.find(executable);
+ if (it != m_loaded_modules.end() && it->second != LLDB_INVALID_ADDRESS)
+ return it->second;
+
+ lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
+
+ // Second, try to get it through the process plugins. For a remote process,
+ // the remote platform will be responsible for providing it.
+ FileSpec file_spec(executable->GetPlatformFileSpec());
+ bool is_loaded = false;
+ Status status =
+ m_process->GetFileLoadAddress(file_spec, is_loaded, load_addr);
+ // Servers other than lldb server could respond with a bogus address.
+ if (status.Success() && is_loaded && load_addr != LLDB_INVALID_ADDRESS) {
+ m_loaded_modules[executable] = load_addr;
+ return load_addr;
+ }
+
+ return LLDB_INVALID_ADDRESS;
+}
+
+void DynamicLoaderWindowsDYLD::DidAttach() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ if (log)
+ log->Printf("DynamicLoaderWindowsDYLD::%s()", __FUNCTION__);
+
+ ModuleSP executable = GetTargetExecutable();
+
+ if (!executable.get())
+ return;
+
+ // Try to fetch the load address of the file from the process, since there
+ // could be randomization of the load address.
+ lldb::addr_t load_addr = GetLoadAddress(executable);
+ if (load_addr == LLDB_INVALID_ADDRESS)
+ return;
+
+ // Request the process base address.
+ lldb::addr_t image_base = m_process->GetImageInfoAddress();
+ if (image_base == load_addr)
+ return;
+
+ // Rebase the process's modules if there is a mismatch.
+ UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_addr, false);
+
+ ModuleList module_list;
+ module_list.Append(executable);
+ m_process->GetTarget().ModulesDidLoad(module_list);
+ m_process->LoadModules();
+}
+
+void DynamicLoaderWindowsDYLD::DidLaunch() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ if (log)
+ log->Printf("DynamicLoaderWindowsDYLD::%s()", __FUNCTION__);
+
+ ModuleSP executable = GetTargetExecutable();
+ if (!executable.get())
+ return;
+
+ lldb::addr_t load_addr = GetLoadAddress(executable);
+ if (load_addr != LLDB_INVALID_ADDRESS) {
+ // Update the loaded sections so that the breakpoints can be resolved.
+ UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_addr, false);
+
+ ModuleList module_list;
+ module_list.Append(executable);
+ m_process->GetTarget().ModulesDidLoad(module_list);
+ m_process->LoadModules();
+ }
+}
+
+Status DynamicLoaderWindowsDYLD::CanLoadImage() { return Status(); }
+
+ConstString DynamicLoaderWindowsDYLD::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t DynamicLoaderWindowsDYLD::GetPluginVersion() { return 1; }
+
+ThreadPlanSP
+DynamicLoaderWindowsDYLD::GetStepThroughTrampolinePlan(Thread &thread,
+ bool stop) {
+ auto arch = m_process->GetTarget().GetArchitecture();
+ if (arch.GetMachine() != llvm::Triple::x86) {
+ return ThreadPlanSP();
+ }
+
+ uint64_t pc = thread.GetRegisterContext()->GetPC();
+ // Max size of an instruction in x86 is 15 bytes.
+ AddressRange range(pc, 2 * 15);
+
+ ExecutionContext exe_ctx(m_process->GetTarget());
+ DisassemblerSP disassembler_sp = Disassembler::DisassembleRange(
+ arch, nullptr, nullptr, exe_ctx, range, true);
+ if (!disassembler_sp) {
+ return ThreadPlanSP();
+ }
+
+ InstructionList *insn_list = &disassembler_sp->GetInstructionList();
+ if (insn_list == nullptr) {
+ return ThreadPlanSP();
+ }
+
+ // First instruction in a x86 Windows trampoline is going to be an indirect
+ // jump through the IAT and the next one will be a nop (usually there for
+ // alignment purposes). e.g.:
+ // 0x70ff4cfc <+956>: jmpl *0x7100c2a8
+ // 0x70ff4d02 <+962>: nop
+
+ auto first_insn = insn_list->GetInstructionAtIndex(0);
+ auto second_insn = insn_list->GetInstructionAtIndex(1);
+
+ if (first_insn == nullptr || second_insn == nullptr ||
+ strcmp(first_insn->GetMnemonic(&exe_ctx), "jmpl") != 0 ||
+ strcmp(second_insn->GetMnemonic(&exe_ctx), "nop") != 0) {
+ return ThreadPlanSP();
+ }
+
+ assert(first_insn->DoesBranch() && !second_insn->DoesBranch());
+
+ return ThreadPlanSP(new ThreadPlanStepInstruction(
+ thread, false, false, eVoteNoOpinion, eVoteNoOpinion));
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h
new file mode 100644
index 000000000000..100689a63128
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h
@@ -0,0 +1,54 @@
+//===-- DynamicLoaderWindowsDYLD.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 liblldb_Plugins_Process_Windows_DynamicLoaderWindowsDYLD_h_
+#define liblldb_Plugins_Process_Windows_DynamicLoaderWindowsDYLD_h_
+
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/lldb-forward.h"
+
+#include <map>
+
+namespace lldb_private {
+
+class DynamicLoaderWindowsDYLD : public DynamicLoader {
+public:
+ DynamicLoaderWindowsDYLD(Process *process);
+
+ ~DynamicLoaderWindowsDYLD() override;
+
+ static void Initialize();
+ static void Terminate();
+ static ConstString GetPluginNameStatic();
+ static const char *GetPluginDescriptionStatic();
+
+ static DynamicLoader *CreateInstance(Process *process, bool force);
+
+ void OnLoadModule(lldb::ModuleSP module_sp, const ModuleSpec module_spec,
+ lldb::addr_t module_addr);
+ void OnUnloadModule(lldb::addr_t module_addr);
+
+ void DidAttach() override;
+ void DidLaunch() override;
+ Status CanLoadImage() override;
+ lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
+ bool stop) override;
+
+ ConstString GetPluginName() override;
+ uint32_t GetPluginVersion() override;
+
+protected:
+ lldb::addr_t GetLoadAddress(lldb::ModuleSP executable);
+
+private:
+ std::map<lldb::ModuleSP, lldb::addr_t> m_loaded_modules;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_Plugins_Process_Windows_DynamicLoaderWindowsDYLD_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTDumper.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTDumper.cpp
new file mode 100644
index 000000000000..369f88327dd9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTDumper.cpp
@@ -0,0 +1,108 @@
+//===-- ASTDumper.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 "ASTDumper.h"
+
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangUtil.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Utility/Log.h"
+
+#include "llvm/Support/raw_ostream.h"
+
+using namespace lldb_private;
+
+ASTDumper::ASTDumper(clang::Decl *decl) {
+ clang::DeclContext *decl_ctx = llvm::dyn_cast<clang::DeclContext>(decl);
+
+ bool has_external_lexical_storage;
+ bool has_external_visible_storage;
+
+ if (decl_ctx) {
+ has_external_lexical_storage = decl_ctx->hasExternalLexicalStorage();
+ has_external_visible_storage = decl_ctx->hasExternalVisibleStorage();
+ decl_ctx->setHasExternalLexicalStorage(false);
+ decl_ctx->setHasExternalVisibleStorage(false);
+ }
+
+ llvm::raw_string_ostream os(m_dump);
+ decl->print(os);
+ os.flush();
+
+ if (decl_ctx) {
+ decl_ctx->setHasExternalLexicalStorage(has_external_lexical_storage);
+ decl_ctx->setHasExternalVisibleStorage(has_external_visible_storage);
+ }
+}
+
+ASTDumper::ASTDumper(clang::DeclContext *decl_ctx) {
+ bool has_external_lexical_storage = decl_ctx->hasExternalLexicalStorage();
+ bool has_external_visible_storage = decl_ctx->hasExternalVisibleStorage();
+
+ decl_ctx->setHasExternalLexicalStorage(false);
+ decl_ctx->setHasExternalVisibleStorage(false);
+
+ if (clang::Decl *decl = llvm::dyn_cast<clang::Decl>(decl_ctx)) {
+ llvm::raw_string_ostream os(m_dump);
+ decl->print(os);
+ os.flush();
+ } else {
+ m_dump.assign("<DeclContext is not a Decl>");
+ }
+
+ decl_ctx->setHasExternalLexicalStorage(has_external_lexical_storage);
+ decl_ctx->setHasExternalVisibleStorage(has_external_visible_storage);
+}
+
+ASTDumper::ASTDumper(const clang::Type *type) {
+ m_dump = clang::QualType(type, 0).getAsString();
+}
+
+ASTDumper::ASTDumper(clang::QualType type) { m_dump = type.getAsString(); }
+
+ASTDumper::ASTDumper(lldb::opaque_compiler_type_t type) {
+ m_dump = clang::QualType::getFromOpaquePtr(type).getAsString();
+}
+
+ASTDumper::ASTDumper(const CompilerType &compiler_type) {
+ m_dump = ClangUtil::GetQualType(compiler_type).getAsString();
+}
+
+const char *ASTDumper::GetCString() { return m_dump.c_str(); }
+
+void ASTDumper::ToSTDERR() { fprintf(stderr, "%s\n", m_dump.c_str()); }
+
+void ASTDumper::ToLog(Log *log, const char *prefix) {
+ size_t len = m_dump.length() + 1;
+
+ char *alloc = (char *)malloc(len);
+ char *str = alloc;
+
+ memcpy(str, m_dump.c_str(), len);
+
+ char *end = nullptr;
+
+ end = strchr(str, '\n');
+
+ while (end) {
+ *end = '\0';
+
+ log->Printf("%s%s", prefix, str);
+
+ *end = '\n';
+
+ str = end + 1;
+ end = strchr(str, '\n');
+ }
+
+ log->Printf("%s%s", prefix, str);
+
+ free(alloc);
+}
+
+void ASTDumper::ToStream(lldb::StreamSP &stream) { stream->PutCString(m_dump); }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTDumper.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTDumper.h
new file mode 100644
index 000000000000..ddf055d9c0c3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTDumper.h
@@ -0,0 +1,40 @@
+//===-- ASTDumper.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 liblldb_ASTDumper_h_
+#define liblldb_ASTDumper_h_
+
+#include "clang/AST/DeclVisitor.h"
+#include "clang/AST/TypeVisitor.h"
+
+#include "lldb/Utility/Stream.h"
+#include "llvm/ADT/DenseSet.h"
+
+namespace lldb_private {
+
+class ASTDumper {
+public:
+ ASTDumper(clang::Decl *decl);
+ ASTDumper(clang::DeclContext *decl_ctx);
+ ASTDumper(const clang::Type *type);
+ ASTDumper(clang::QualType type);
+ ASTDumper(lldb::opaque_compiler_type_t type);
+ ASTDumper(const CompilerType &compiler_type);
+
+ const char *GetCString();
+ void ToSTDERR();
+ void ToLog(Log *log, const char *prefix);
+ void ToStream(lldb::StreamSP &stream);
+
+private:
+ std::string m_dump;
+};
+
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp
new file mode 100644
index 000000000000..526ef90782ef
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp
@@ -0,0 +1,516 @@
+//===-- ASTResultSynthesizer.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 "ASTResultSynthesizer.h"
+
+#include "ClangPersistentVariables.h"
+
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangASTImporter.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "stdlib.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclGroup.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Parse/Parser.h"
+#include "clang/Sema/SemaDiagnostic.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace clang;
+using namespace lldb_private;
+
+ASTResultSynthesizer::ASTResultSynthesizer(ASTConsumer *passthrough,
+ bool top_level, Target &target)
+ : m_ast_context(nullptr), m_passthrough(passthrough),
+ m_passthrough_sema(nullptr), m_target(target), m_sema(nullptr),
+ m_top_level(top_level) {
+ if (!m_passthrough)
+ return;
+
+ m_passthrough_sema = dyn_cast<SemaConsumer>(passthrough);
+}
+
+ASTResultSynthesizer::~ASTResultSynthesizer() {}
+
+void ASTResultSynthesizer::Initialize(ASTContext &Context) {
+ m_ast_context = &Context;
+
+ if (m_passthrough)
+ m_passthrough->Initialize(Context);
+}
+
+void ASTResultSynthesizer::TransformTopLevelDecl(Decl *D) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (NamedDecl *named_decl = dyn_cast<NamedDecl>(D)) {
+ if (log && log->GetVerbose()) {
+ if (named_decl->getIdentifier())
+ log->Printf("TransformTopLevelDecl(%s)",
+ named_decl->getIdentifier()->getNameStart());
+ else if (ObjCMethodDecl *method_decl = dyn_cast<ObjCMethodDecl>(D))
+ log->Printf("TransformTopLevelDecl(%s)",
+ method_decl->getSelector().getAsString().c_str());
+ else
+ log->Printf("TransformTopLevelDecl(<complex>)");
+ }
+
+ if (m_top_level) {
+ RecordPersistentDecl(named_decl);
+ }
+ }
+
+ if (LinkageSpecDecl *linkage_spec_decl = dyn_cast<LinkageSpecDecl>(D)) {
+ RecordDecl::decl_iterator decl_iterator;
+
+ for (decl_iterator = linkage_spec_decl->decls_begin();
+ decl_iterator != linkage_spec_decl->decls_end(); ++decl_iterator) {
+ TransformTopLevelDecl(*decl_iterator);
+ }
+ } else if (!m_top_level) {
+ if (ObjCMethodDecl *method_decl = dyn_cast<ObjCMethodDecl>(D)) {
+ if (m_ast_context &&
+ !method_decl->getSelector().getAsString().compare("$__lldb_expr:")) {
+ RecordPersistentTypes(method_decl);
+ SynthesizeObjCMethodResult(method_decl);
+ }
+ } else if (FunctionDecl *function_decl = dyn_cast<FunctionDecl>(D)) {
+ // When completing user input the body of the function may be a nullptr.
+ if (m_ast_context && function_decl->hasBody() &&
+ !function_decl->getNameInfo().getAsString().compare("$__lldb_expr")) {
+ RecordPersistentTypes(function_decl);
+ SynthesizeFunctionResult(function_decl);
+ }
+ }
+ }
+}
+
+bool ASTResultSynthesizer::HandleTopLevelDecl(DeclGroupRef D) {
+ DeclGroupRef::iterator decl_iterator;
+
+ for (decl_iterator = D.begin(); decl_iterator != D.end(); ++decl_iterator) {
+ Decl *decl = *decl_iterator;
+
+ TransformTopLevelDecl(decl);
+ }
+
+ if (m_passthrough)
+ return m_passthrough->HandleTopLevelDecl(D);
+ return true;
+}
+
+bool ASTResultSynthesizer::SynthesizeFunctionResult(FunctionDecl *FunDecl) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (!m_sema)
+ return false;
+
+ FunctionDecl *function_decl = FunDecl;
+
+ if (!function_decl)
+ return false;
+
+ if (log && log->GetVerbose()) {
+ std::string s;
+ raw_string_ostream os(s);
+
+ function_decl->print(os);
+
+ os.flush();
+
+ log->Printf("Untransformed function AST:\n%s", s.c_str());
+ }
+
+ Stmt *function_body = function_decl->getBody();
+ CompoundStmt *compound_stmt = dyn_cast<CompoundStmt>(function_body);
+
+ bool ret = SynthesizeBodyResult(compound_stmt, function_decl);
+
+ if (log && log->GetVerbose()) {
+ std::string s;
+ raw_string_ostream os(s);
+
+ function_decl->print(os);
+
+ os.flush();
+
+ log->Printf("Transformed function AST:\n%s", s.c_str());
+ }
+
+ return ret;
+}
+
+bool ASTResultSynthesizer::SynthesizeObjCMethodResult(
+ ObjCMethodDecl *MethodDecl) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (!m_sema)
+ return false;
+
+ if (!MethodDecl)
+ return false;
+
+ if (log && log->GetVerbose()) {
+ std::string s;
+ raw_string_ostream os(s);
+
+ MethodDecl->print(os);
+
+ os.flush();
+
+ log->Printf("Untransformed method AST:\n%s", s.c_str());
+ }
+
+ Stmt *method_body = MethodDecl->getBody();
+
+ if (!method_body)
+ return false;
+
+ CompoundStmt *compound_stmt = dyn_cast<CompoundStmt>(method_body);
+
+ bool ret = SynthesizeBodyResult(compound_stmt, MethodDecl);
+
+ if (log && log->GetVerbose()) {
+ std::string s;
+ raw_string_ostream os(s);
+
+ MethodDecl->print(os);
+
+ os.flush();
+
+ log->Printf("Transformed method AST:\n%s", s.c_str());
+ }
+
+ return ret;
+}
+
+bool ASTResultSynthesizer::SynthesizeBodyResult(CompoundStmt *Body,
+ DeclContext *DC) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ ASTContext &Ctx(*m_ast_context);
+
+ if (!Body)
+ return false;
+
+ if (Body->body_empty())
+ return false;
+
+ Stmt **last_stmt_ptr = Body->body_end() - 1;
+ Stmt *last_stmt = *last_stmt_ptr;
+
+ while (dyn_cast<NullStmt>(last_stmt)) {
+ if (last_stmt_ptr != Body->body_begin()) {
+ last_stmt_ptr--;
+ last_stmt = *last_stmt_ptr;
+ } else {
+ return false;
+ }
+ }
+
+ Expr *last_expr = dyn_cast<Expr>(last_stmt);
+
+ if (!last_expr)
+ // No auxiliary variable necessary; expression returns void
+ return true;
+
+ // In C++11, last_expr can be a LValueToRvalue implicit cast. Strip that off
+ // if that's the case.
+
+ do {
+ ImplicitCastExpr *implicit_cast = dyn_cast<ImplicitCastExpr>(last_expr);
+
+ if (!implicit_cast)
+ break;
+
+ if (implicit_cast->getCastKind() != CK_LValueToRValue)
+ break;
+
+ last_expr = implicit_cast->getSubExpr();
+ } while (false);
+
+ // is_lvalue is used to record whether the expression returns an assignable
+ // Lvalue or an Rvalue. This is relevant because they are handled
+ // differently.
+ //
+ // For Lvalues
+ //
+ // - In AST result synthesis (here!) the expression E is transformed into an
+ // initialization
+ // T *$__lldb_expr_result_ptr = &E.
+ //
+ // - In structure allocation, a pointer-sized slot is allocated in the
+ // struct that is to be
+ // passed into the expression.
+ //
+ // - In IR transformations, reads and writes to $__lldb_expr_result_ptr are
+ // redirected at
+ // an entry in the struct ($__lldb_arg) passed into the expression.
+ // (Other persistent
+ // variables are treated similarly, having been materialized as
+ // references, but in those
+ // cases the value of the reference itself is never modified.)
+ //
+ // - During materialization, $0 (the result persistent variable) is ignored.
+ //
+ // - During dematerialization, $0 is marked up as a load address with value
+ // equal to the
+ // contents of the structure entry.
+ //
+ // For Rvalues
+ //
+ // - In AST result synthesis the expression E is transformed into an
+ // initialization
+ // static T $__lldb_expr_result = E.
+ //
+ // - In structure allocation, a pointer-sized slot is allocated in the
+ // struct that is to be
+ // passed into the expression.
+ //
+ // - In IR transformations, an instruction is inserted at the beginning of
+ // the function to
+ // dereference the pointer resident in the slot. Reads and writes to
+ // $__lldb_expr_result
+ // are redirected at that dereferenced version. Guard variables for the
+ // static variable
+ // are excised.
+ //
+ // - During materialization, $0 (the result persistent variable) is
+ // populated with the location
+ // of a newly-allocated area of memory.
+ //
+ // - During dematerialization, $0 is ignored.
+
+ bool is_lvalue = last_expr->getValueKind() == VK_LValue &&
+ last_expr->getObjectKind() == OK_Ordinary;
+
+ QualType expr_qual_type = last_expr->getType();
+ const clang::Type *expr_type = expr_qual_type.getTypePtr();
+
+ if (!expr_type)
+ return false;
+
+ if (expr_type->isVoidType())
+ return true;
+
+ if (log) {
+ std::string s = expr_qual_type.getAsString();
+
+ log->Printf("Last statement is an %s with type: %s",
+ (is_lvalue ? "lvalue" : "rvalue"), s.c_str());
+ }
+
+ clang::VarDecl *result_decl = nullptr;
+
+ if (is_lvalue) {
+ IdentifierInfo *result_ptr_id;
+
+ if (expr_type->isFunctionType())
+ result_ptr_id =
+ &Ctx.Idents.get("$__lldb_expr_result"); // functions actually should
+ // be treated like function
+ // pointers
+ else
+ result_ptr_id = &Ctx.Idents.get("$__lldb_expr_result_ptr");
+
+ m_sema->RequireCompleteType(SourceLocation(), expr_qual_type,
+ clang::diag::err_incomplete_type);
+
+ QualType ptr_qual_type;
+
+ if (expr_qual_type->getAs<ObjCObjectType>() != nullptr)
+ ptr_qual_type = Ctx.getObjCObjectPointerType(expr_qual_type);
+ else
+ ptr_qual_type = Ctx.getPointerType(expr_qual_type);
+
+ result_decl =
+ VarDecl::Create(Ctx, DC, SourceLocation(), SourceLocation(),
+ result_ptr_id, ptr_qual_type, nullptr, SC_Static);
+
+ if (!result_decl)
+ return false;
+
+ ExprResult address_of_expr =
+ m_sema->CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, last_expr);
+ if (address_of_expr.get())
+ m_sema->AddInitializerToDecl(result_decl, address_of_expr.get(), true);
+ else
+ return false;
+ } else {
+ IdentifierInfo &result_id = Ctx.Idents.get("$__lldb_expr_result");
+
+ result_decl =
+ VarDecl::Create(Ctx, DC, SourceLocation(), SourceLocation(), &result_id,
+ expr_qual_type, nullptr, SC_Static);
+
+ if (!result_decl)
+ return false;
+
+ m_sema->AddInitializerToDecl(result_decl, last_expr, true);
+ }
+
+ DC->addDecl(result_decl);
+
+ ///////////////////////////////
+ // call AddInitializerToDecl
+ //
+
+ // m_sema->AddInitializerToDecl(result_decl, last_expr);
+
+ /////////////////////////////////
+ // call ConvertDeclToDeclGroup
+ //
+
+ Sema::DeclGroupPtrTy result_decl_group_ptr;
+
+ result_decl_group_ptr = m_sema->ConvertDeclToDeclGroup(result_decl);
+
+ ////////////////////////
+ // call ActOnDeclStmt
+ //
+
+ StmtResult result_initialization_stmt_result(m_sema->ActOnDeclStmt(
+ result_decl_group_ptr, SourceLocation(), SourceLocation()));
+
+ ////////////////////////////////////////////////
+ // replace the old statement with the new one
+ //
+
+ *last_stmt_ptr =
+ reinterpret_cast<Stmt *>(result_initialization_stmt_result.get());
+
+ return true;
+}
+
+void ASTResultSynthesizer::HandleTranslationUnit(ASTContext &Ctx) {
+ if (m_passthrough)
+ m_passthrough->HandleTranslationUnit(Ctx);
+}
+
+void ASTResultSynthesizer::RecordPersistentTypes(DeclContext *FunDeclCtx) {
+ typedef DeclContext::specific_decl_iterator<TypeDecl> TypeDeclIterator;
+
+ for (TypeDeclIterator i = TypeDeclIterator(FunDeclCtx->decls_begin()),
+ e = TypeDeclIterator(FunDeclCtx->decls_end());
+ i != e; ++i) {
+ MaybeRecordPersistentType(*i);
+ }
+}
+
+void ASTResultSynthesizer::MaybeRecordPersistentType(TypeDecl *D) {
+ if (!D->getIdentifier())
+ return;
+
+ StringRef name = D->getName();
+
+ if (name.size() == 0 || name[0] != '$')
+ return;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ ConstString name_cs(name.str().c_str());
+
+ if (log)
+ log->Printf("Recording persistent type %s\n", name_cs.GetCString());
+
+ m_decls.push_back(D);
+}
+
+void ASTResultSynthesizer::RecordPersistentDecl(NamedDecl *D) {
+ lldbassert(m_top_level);
+
+ if (!D->getIdentifier())
+ return;
+
+ StringRef name = D->getName();
+
+ if (name.size() == 0)
+ return;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ ConstString name_cs(name.str().c_str());
+
+ if (log)
+ log->Printf("Recording persistent decl %s\n", name_cs.GetCString());
+
+ m_decls.push_back(D);
+}
+
+void ASTResultSynthesizer::CommitPersistentDecls() {
+ for (clang::NamedDecl *decl : m_decls) {
+ StringRef name = decl->getName();
+ ConstString name_cs(name.str().c_str());
+
+ Decl *D_scratch = m_target.GetClangASTImporter()->DeportDecl(
+ m_target.GetScratchClangASTContext()->getASTContext(), m_ast_context,
+ decl);
+
+ if (!D_scratch) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ std::string s;
+ llvm::raw_string_ostream ss(s);
+ decl->dump(ss);
+ ss.flush();
+
+ log->Printf("Couldn't commit persistent decl: %s\n", s.c_str());
+ }
+
+ continue;
+ }
+
+ if (NamedDecl *NamedDecl_scratch = dyn_cast<NamedDecl>(D_scratch))
+ llvm::cast<ClangPersistentVariables>(
+ m_target.GetPersistentExpressionStateForLanguage(
+ lldb::eLanguageTypeC))
+ ->RegisterPersistentDecl(name_cs, NamedDecl_scratch);
+ }
+}
+
+void ASTResultSynthesizer::HandleTagDeclDefinition(TagDecl *D) {
+ if (m_passthrough)
+ m_passthrough->HandleTagDeclDefinition(D);
+}
+
+void ASTResultSynthesizer::CompleteTentativeDefinition(VarDecl *D) {
+ if (m_passthrough)
+ m_passthrough->CompleteTentativeDefinition(D);
+}
+
+void ASTResultSynthesizer::HandleVTable(CXXRecordDecl *RD) {
+ if (m_passthrough)
+ m_passthrough->HandleVTable(RD);
+}
+
+void ASTResultSynthesizer::PrintStats() {
+ if (m_passthrough)
+ m_passthrough->PrintStats();
+}
+
+void ASTResultSynthesizer::InitializeSema(Sema &S) {
+ m_sema = &S;
+
+ if (m_passthrough_sema)
+ m_passthrough_sema->InitializeSema(S);
+}
+
+void ASTResultSynthesizer::ForgetSema() {
+ m_sema = nullptr;
+
+ if (m_passthrough_sema)
+ m_passthrough_sema->ForgetSema();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.h
new file mode 100644
index 000000000000..670ba6dce72e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.h
@@ -0,0 +1,173 @@
+//===-- ASTResultSynthesizer.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 liblldb_ASTResultSynthesizer_h_
+#define liblldb_ASTResultSynthesizer_h_
+
+#include "lldb/Core/ClangForward.h"
+#include "lldb/Target/Target.h"
+#include "clang/Sema/SemaConsumer.h"
+
+namespace lldb_private {
+
+/// \class ASTResultSynthesizer ASTResultSynthesizer.h
+/// "lldb/Expression/ASTResultSynthesizer.h" Adds a result variable
+/// declaration to the ASTs for an expression.
+///
+/// Users expect the expression "i + 3" to return a result, even if a result
+/// variable wasn't specifically declared. To fulfil this requirement, LLDB
+/// adds a result variable to the expression, transforming it to "int
+/// $__lldb_expr_result = i + 3." The IR transformers ensure that the
+/// resulting variable is mapped to the right piece of memory.
+/// ASTResultSynthesizer's job is to add the variable and its initialization
+/// to the ASTs for the expression, and it does so by acting as a SemaConsumer
+/// for Clang.
+class ASTResultSynthesizer : public clang::SemaConsumer {
+public:
+ /// Constructor
+ ///
+ /// \param[in] passthrough
+ /// Since the ASTs must typically go through to the Clang code generator
+ /// in order to produce LLVM IR, this SemaConsumer must allow them to
+ /// pass to the next step in the chain after processing. Passthrough is
+ /// the next ASTConsumer, or NULL if none is required.
+ ///
+ /// \param[in] top_level
+ /// If true, register all top-level Decls and don't try to handle the
+ /// main function.
+ ///
+ /// \param[in] target
+ /// The target, which contains the persistent variable store and the
+ /// AST importer.
+ ASTResultSynthesizer(clang::ASTConsumer *passthrough, bool top_level,
+ Target &target);
+
+ /// Destructor
+ ~ASTResultSynthesizer() override;
+
+ /// Link this consumer with a particular AST context
+ ///
+ /// \param[in] Context
+ /// This AST context will be used for types and identifiers, and also
+ /// forwarded to the passthrough consumer, if one exists.
+ void Initialize(clang::ASTContext &Context) override;
+
+ /// Examine a list of Decls to find the function $__lldb_expr and transform
+ /// its code
+ ///
+ /// \param[in] D
+ /// The list of Decls to search. These may contain LinkageSpecDecls,
+ /// which need to be searched recursively. That job falls to
+ /// TransformTopLevelDecl.
+ bool HandleTopLevelDecl(clang::DeclGroupRef D) override;
+
+ /// Passthrough stub
+ void HandleTranslationUnit(clang::ASTContext &Ctx) override;
+
+ /// Passthrough stub
+ void HandleTagDeclDefinition(clang::TagDecl *D) override;
+
+ /// Passthrough stub
+ void CompleteTentativeDefinition(clang::VarDecl *D) override;
+
+ /// Passthrough stub
+ void HandleVTable(clang::CXXRecordDecl *RD) override;
+
+ /// Passthrough stub
+ void PrintStats() override;
+
+ /// Set the Sema object to use when performing transforms, and pass it on
+ ///
+ /// \param[in] S
+ /// The Sema to use. Because Sema isn't externally visible, this class
+ /// casts it to an Action for actual use.
+ void InitializeSema(clang::Sema &S) override;
+
+ /// Reset the Sema to NULL now that transformations are done
+ void ForgetSema() override;
+
+ /// The parse has succeeded, so record its persistent decls
+ void CommitPersistentDecls();
+
+private:
+ /// Hunt the given Decl for FunctionDecls named $__lldb_expr, recursing as
+ /// necessary through LinkageSpecDecls, and calling SynthesizeResult on
+ /// anything that was found
+ ///
+ /// \param[in] D
+ /// The Decl to hunt.
+ void TransformTopLevelDecl(clang::Decl *D);
+
+ /// Process an Objective-C method and produce the result variable and
+ /// initialization
+ ///
+ /// \param[in] MethodDecl
+ /// The method to process.
+ bool SynthesizeObjCMethodResult(clang::ObjCMethodDecl *MethodDecl);
+
+ /// Process a function and produce the result variable and initialization
+ ///
+ /// \param[in] FunDecl
+ /// The function to process.
+ bool SynthesizeFunctionResult(clang::FunctionDecl *FunDecl);
+
+ /// Process a function body and produce the result variable and
+ /// initialization
+ ///
+ /// \param[in] Body
+ /// The body of the function.
+ ///
+ /// \param[in] DC
+ /// The DeclContext of the function, into which the result variable
+ /// is inserted.
+ bool SynthesizeBodyResult(clang::CompoundStmt *Body, clang::DeclContext *DC);
+
+ /// Given a DeclContext for a function or method, find all types declared in
+ /// the context and record any persistent types found.
+ ///
+ /// \param[in] FunDeclCtx
+ /// The context for the function to process.
+ void RecordPersistentTypes(clang::DeclContext *FunDeclCtx);
+
+ /// Given a TypeDecl, if it declares a type whose name starts with a dollar
+ /// sign, register it as a pointer type in the target's scratch
+ /// AST context.
+ ///
+ /// \param[in] Body
+ /// The body of the function.
+ void MaybeRecordPersistentType(clang::TypeDecl *D);
+
+ /// Given a NamedDecl, register it as a pointer type in the target's scratch
+ /// AST context.
+ ///
+ /// \param[in] Body
+ /// The body of the function.
+ void RecordPersistentDecl(clang::NamedDecl *D);
+
+ clang::ASTContext
+ *m_ast_context; ///< The AST context to use for identifiers and types.
+ clang::ASTConsumer *m_passthrough; ///< The ASTConsumer down the chain, for
+ ///passthrough. NULL if it's a
+ ///SemaConsumer.
+ clang::SemaConsumer *m_passthrough_sema; ///< The SemaConsumer down the chain,
+ ///for passthrough. NULL if it's an
+ ///ASTConsumer.
+
+ std::vector<clang::NamedDecl *> m_decls; ///< Persistent declarations to
+ ///register assuming the expression
+ ///succeeds.
+
+ Target &m_target; ///< The target, which contains the persistent variable
+ ///store and the
+ clang::Sema *m_sema; ///< The Sema to use.
+ bool m_top_level;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ASTResultSynthesizer_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTStructExtractor.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTStructExtractor.cpp
new file mode 100644
index 000000000000..190eacaa2b62
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTStructExtractor.cpp
@@ -0,0 +1,185 @@
+//===-- ASTStructExtractor.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 "ASTStructExtractor.h"
+
+#include "lldb/Utility/Log.h"
+#include "stdlib.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclGroup.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/RecordLayout.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Parse/Parser.h"
+#include "clang/Sema/Sema.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace clang;
+using namespace lldb_private;
+
+ASTStructExtractor::ASTStructExtractor(ASTConsumer *passthrough,
+ const char *struct_name,
+ ClangFunctionCaller &function)
+ : m_ast_context(nullptr), m_passthrough(passthrough),
+ m_passthrough_sema(nullptr), m_sema(nullptr), m_action(nullptr),
+ m_function(function), m_struct_name(struct_name) {
+ if (!m_passthrough)
+ return;
+
+ m_passthrough_sema = dyn_cast<SemaConsumer>(passthrough);
+}
+
+ASTStructExtractor::~ASTStructExtractor() {}
+
+void ASTStructExtractor::Initialize(ASTContext &Context) {
+ m_ast_context = &Context;
+
+ if (m_passthrough)
+ m_passthrough->Initialize(Context);
+}
+
+void ASTStructExtractor::ExtractFromFunctionDecl(FunctionDecl *F) {
+ if (!F->hasBody())
+ return;
+
+ Stmt *body_stmt = F->getBody();
+ CompoundStmt *body_compound_stmt = dyn_cast<CompoundStmt>(body_stmt);
+
+ if (!body_compound_stmt)
+ return; // do we have to handle this?
+
+ RecordDecl *struct_decl = nullptr;
+
+ StringRef desired_name(m_struct_name);
+
+ for (CompoundStmt::const_body_iterator bi = body_compound_stmt->body_begin(),
+ be = body_compound_stmt->body_end();
+ bi != be; ++bi) {
+ Stmt *curr_stmt = *bi;
+ DeclStmt *curr_decl_stmt = dyn_cast<DeclStmt>(curr_stmt);
+ if (!curr_decl_stmt)
+ continue;
+ DeclGroupRef decl_group = curr_decl_stmt->getDeclGroup();
+ for (Decl *candidate_decl : decl_group) {
+ RecordDecl *candidate_record_decl = dyn_cast<RecordDecl>(candidate_decl);
+ if (!candidate_record_decl)
+ continue;
+ if (candidate_record_decl->getName() == desired_name) {
+ struct_decl = candidate_record_decl;
+ break;
+ }
+ }
+ if (struct_decl)
+ break;
+ }
+
+ if (!struct_decl)
+ return;
+
+ const ASTRecordLayout *struct_layout(
+ &m_ast_context->getASTRecordLayout(struct_decl));
+
+ if (!struct_layout)
+ return;
+
+ m_function.m_struct_size =
+ struct_layout->getSize()
+ .getQuantity(); // TODO Store m_struct_size as CharUnits
+ m_function.m_return_offset =
+ struct_layout->getFieldOffset(struct_layout->getFieldCount() - 1) / 8;
+ m_function.m_return_size =
+ struct_layout->getDataSize().getQuantity() - m_function.m_return_offset;
+
+ for (unsigned field_index = 0, num_fields = struct_layout->getFieldCount();
+ field_index < num_fields; ++field_index) {
+ m_function.m_member_offsets.push_back(
+ struct_layout->getFieldOffset(field_index) / 8);
+ }
+
+ m_function.m_struct_valid = true;
+}
+
+void ASTStructExtractor::ExtractFromTopLevelDecl(Decl *D) {
+ LinkageSpecDecl *linkage_spec_decl = dyn_cast<LinkageSpecDecl>(D);
+
+ if (linkage_spec_decl) {
+ RecordDecl::decl_iterator decl_iterator;
+
+ for (decl_iterator = linkage_spec_decl->decls_begin();
+ decl_iterator != linkage_spec_decl->decls_end(); ++decl_iterator) {
+ ExtractFromTopLevelDecl(*decl_iterator);
+ }
+ }
+
+ FunctionDecl *function_decl = dyn_cast<FunctionDecl>(D);
+
+ if (m_ast_context && function_decl &&
+ !m_function.m_wrapper_function_name.compare(
+ function_decl->getNameAsString())) {
+ ExtractFromFunctionDecl(function_decl);
+ }
+}
+
+bool ASTStructExtractor::HandleTopLevelDecl(DeclGroupRef D) {
+ DeclGroupRef::iterator decl_iterator;
+
+ for (decl_iterator = D.begin(); decl_iterator != D.end(); ++decl_iterator) {
+ Decl *decl = *decl_iterator;
+
+ ExtractFromTopLevelDecl(decl);
+ }
+
+ if (m_passthrough)
+ return m_passthrough->HandleTopLevelDecl(D);
+ return true;
+}
+
+void ASTStructExtractor::HandleTranslationUnit(ASTContext &Ctx) {
+ if (m_passthrough)
+ m_passthrough->HandleTranslationUnit(Ctx);
+}
+
+void ASTStructExtractor::HandleTagDeclDefinition(TagDecl *D) {
+ if (m_passthrough)
+ m_passthrough->HandleTagDeclDefinition(D);
+}
+
+void ASTStructExtractor::CompleteTentativeDefinition(VarDecl *D) {
+ if (m_passthrough)
+ m_passthrough->CompleteTentativeDefinition(D);
+}
+
+void ASTStructExtractor::HandleVTable(CXXRecordDecl *RD) {
+ if (m_passthrough)
+ m_passthrough->HandleVTable(RD);
+}
+
+void ASTStructExtractor::PrintStats() {
+ if (m_passthrough)
+ m_passthrough->PrintStats();
+}
+
+void ASTStructExtractor::InitializeSema(Sema &S) {
+ m_sema = &S;
+ m_action = reinterpret_cast<Action *>(m_sema);
+
+ if (m_passthrough_sema)
+ m_passthrough_sema->InitializeSema(S);
+}
+
+void ASTStructExtractor::ForgetSema() {
+ m_sema = nullptr;
+ m_action = nullptr;
+
+ if (m_passthrough_sema)
+ m_passthrough_sema->ForgetSema();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTStructExtractor.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTStructExtractor.h
new file mode 100644
index 000000000000..7aef2e254e1f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTStructExtractor.h
@@ -0,0 +1,134 @@
+//===-- ASTStructExtractor.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 liblldb_ASTStructExtractor_h_
+#define liblldb_ASTStructExtractor_h_
+
+#include "ClangExpressionVariable.h"
+#include "ClangFunctionCaller.h"
+
+#include "lldb/Core/ClangForward.h"
+#include "clang/Sema/SemaConsumer.h"
+
+namespace lldb_private {
+
+/// \class ASTStructExtractor ASTStructExtractor.h
+/// "lldb/Expression/ASTStructExtractor.h" Extracts and describes the argument
+/// structure for a wrapped function.
+///
+/// This pass integrates with ClangFunctionCaller, which calls functions with
+/// custom sets of arguments. To avoid having to implement the full calling
+/// convention for the target's architecture, ClangFunctionCaller writes a
+/// simple wrapper function that takes a pointer to an argument structure that
+/// contains room for the address of the function to be called, the values of
+/// all its arguments, and room for the function's return value.
+///
+/// The definition of this struct is itself in the body of the wrapper
+/// function, so Clang does the structure layout itself. ASTStructExtractor
+/// reads through the AST for the wrapper function and finds the struct.
+class ASTStructExtractor : public clang::SemaConsumer {
+public:
+ /// Constructor
+ ///
+ /// \param[in] passthrough
+ /// Since the ASTs must typically go through to the Clang code generator
+ /// in order to produce LLVM IR, this SemaConsumer must allow them to
+ /// pass to the next step in the chain after processing. Passthrough is
+ /// the next ASTConsumer, or NULL if none is required.
+ ///
+ /// \param[in] struct_name
+ /// The name of the structure to extract from the wrapper function.
+ ///
+ /// \param[in] function
+ /// The caller object whose members should be populated with information
+ /// about the argument struct. ClangFunctionCaller friends
+ /// ASTStructExtractor
+ /// for this purpose.
+ ASTStructExtractor(clang::ASTConsumer *passthrough, const char *struct_name,
+ ClangFunctionCaller &function);
+
+ /// Destructor
+ ~ASTStructExtractor() override;
+
+ /// Link this consumer with a particular AST context
+ ///
+ /// \param[in] Context
+ /// This AST context will be used for types and identifiers, and also
+ /// forwarded to the passthrough consumer, if one exists.
+ void Initialize(clang::ASTContext &Context) override;
+
+ /// Examine a list of Decls to find the function $__lldb_expr and transform
+ /// its code
+ ///
+ /// \param[in] D
+ /// The list of Decls to search. These may contain LinkageSpecDecls,
+ /// which need to be searched recursively. That job falls to
+ /// TransformTopLevelDecl.
+ bool HandleTopLevelDecl(clang::DeclGroupRef D) override;
+
+ /// Passthrough stub
+ void HandleTranslationUnit(clang::ASTContext &Ctx) override;
+
+ /// Passthrough stub
+ void HandleTagDeclDefinition(clang::TagDecl *D) override;
+
+ /// Passthrough stub
+ void CompleteTentativeDefinition(clang::VarDecl *D) override;
+
+ /// Passthrough stub
+ void HandleVTable(clang::CXXRecordDecl *RD) override;
+
+ /// Passthrough stub
+ void PrintStats() override;
+
+ /// Set the Sema object to use when performing transforms, and pass it on
+ ///
+ /// \param[in] S
+ /// The Sema to use. Because Sema isn't externally visible, this class
+ /// casts it to an Action for actual use.
+ void InitializeSema(clang::Sema &S) override;
+
+ /// Reset the Sema to NULL now that transformations are done
+ void ForgetSema() override;
+
+private:
+ /// Hunt the given FunctionDecl for the argument struct and place
+ /// information about it into m_function
+ ///
+ /// \param[in] F
+ /// The FunctionDecl to hunt.
+ void ExtractFromFunctionDecl(clang::FunctionDecl *F);
+
+ /// Hunt the given Decl for FunctionDecls named the same as the wrapper
+ /// function name, recursing as necessary through LinkageSpecDecls, and
+ /// calling ExtractFromFunctionDecl on anything that was found
+ ///
+ /// \param[in] D
+ /// The Decl to hunt.
+ void ExtractFromTopLevelDecl(clang::Decl *D);
+
+ clang::ASTContext
+ *m_ast_context; ///< The AST context to use for identifiers and types.
+ clang::ASTConsumer *m_passthrough; ///< The ASTConsumer down the chain, for
+ ///passthrough. NULL if it's a
+ ///SemaConsumer.
+ clang::SemaConsumer *m_passthrough_sema; ///< The SemaConsumer down the chain,
+ ///for passthrough. NULL if it's an
+ ///ASTConsumer.
+ clang::Sema *m_sema; ///< The Sema to use.
+ clang::Action
+ *m_action; ///< The Sema to use, cast to an Action so it's usable.
+
+ ClangFunctionCaller &m_function; ///< The function to populate with
+ ///information about the argument structure.
+ std::string m_struct_name; ///< The name of the structure to extract.
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ASTStructExtractor_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.cpp
new file mode 100644
index 000000000000..bbdf4e31c5a4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.cpp
@@ -0,0 +1,26 @@
+//===-- ASTUtils.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 "ASTUtils.h"
+
+lldb_private::ExternalASTSourceWrapper::~ExternalASTSourceWrapper() {}
+
+void lldb_private::ExternalASTSourceWrapper::PrintStats() {
+ m_Source->PrintStats();
+}
+
+lldb_private::ASTConsumerForwarder::~ASTConsumerForwarder() {}
+
+void lldb_private::ASTConsumerForwarder::PrintStats() { m_c->PrintStats(); }
+
+lldb_private::SemaSourceWithPriorities::~SemaSourceWithPriorities() {}
+
+void lldb_private::SemaSourceWithPriorities::PrintStats() {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ Sources[i]->PrintStats();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h
new file mode 100644
index 000000000000..d429e8c3855f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ASTUtils.h
@@ -0,0 +1,579 @@
+//===-- ASTUtils.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 liblldb_ASTUtils_h_
+#define liblldb_ASTUtils_h_
+
+#include "clang/Sema/Lookup.h"
+#include "clang/Sema/MultiplexExternalSemaSource.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaConsumer.h"
+
+namespace lldb_private {
+
+/// Wraps an ExternalASTSource into an ExternalSemaSource. Doesn't take
+/// ownership of the provided source.
+class ExternalASTSourceWrapper : public clang::ExternalSemaSource {
+ ExternalASTSource *m_Source;
+
+public:
+ ExternalASTSourceWrapper(ExternalASTSource *Source) : m_Source(Source) {
+ assert(m_Source && "Can't wrap nullptr ExternalASTSource");
+ }
+
+ ~ExternalASTSourceWrapper() override;
+
+ clang::Decl *GetExternalDecl(uint32_t ID) override {
+ return m_Source->GetExternalDecl(ID);
+ }
+
+ clang::Selector GetExternalSelector(uint32_t ID) override {
+ return m_Source->GetExternalSelector(ID);
+ }
+
+ uint32_t GetNumExternalSelectors() override {
+ return m_Source->GetNumExternalSelectors();
+ }
+
+ clang::Stmt *GetExternalDeclStmt(uint64_t Offset) override {
+ return m_Source->GetExternalDeclStmt(Offset);
+ }
+
+ clang::CXXCtorInitializer **
+ GetExternalCXXCtorInitializers(uint64_t Offset) override {
+ return m_Source->GetExternalCXXCtorInitializers(Offset);
+ }
+
+ clang::CXXBaseSpecifier *
+ GetExternalCXXBaseSpecifiers(uint64_t Offset) override {
+ return m_Source->GetExternalCXXBaseSpecifiers(Offset);
+ }
+
+ void updateOutOfDateIdentifier(clang::IdentifierInfo &II) override {
+ m_Source->updateOutOfDateIdentifier(II);
+ }
+
+ bool FindExternalVisibleDeclsByName(const clang::DeclContext *DC,
+ clang::DeclarationName Name) override {
+ return m_Source->FindExternalVisibleDeclsByName(DC, Name);
+ }
+
+ void completeVisibleDeclsMap(const clang::DeclContext *DC) override {
+ m_Source->completeVisibleDeclsMap(DC);
+ }
+
+ clang::Module *getModule(unsigned ID) override {
+ return m_Source->getModule(ID);
+ }
+
+ llvm::Optional<ASTSourceDescriptor>
+ getSourceDescriptor(unsigned ID) override {
+ return m_Source->getSourceDescriptor(ID);
+ }
+
+ ExtKind hasExternalDefinitions(const clang::Decl *D) override {
+ return m_Source->hasExternalDefinitions(D);
+ }
+
+ void FindExternalLexicalDecls(
+ const clang::DeclContext *DC,
+ llvm::function_ref<bool(clang::Decl::Kind)> IsKindWeWant,
+ llvm::SmallVectorImpl<clang::Decl *> &Result) override {
+ m_Source->FindExternalLexicalDecls(DC, IsKindWeWant, Result);
+ }
+
+ void
+ FindFileRegionDecls(clang::FileID File, unsigned Offset, unsigned Length,
+ llvm::SmallVectorImpl<clang::Decl *> &Decls) override {
+ m_Source->FindFileRegionDecls(File, Offset, Length, Decls);
+ }
+
+ void CompleteRedeclChain(const clang::Decl *D) override {
+ m_Source->CompleteRedeclChain(D);
+ }
+
+ void CompleteType(clang::TagDecl *Tag) override {
+ m_Source->CompleteType(Tag);
+ }
+
+ void CompleteType(clang::ObjCInterfaceDecl *Class) override {
+ m_Source->CompleteType(Class);
+ }
+
+ void ReadComments() override { m_Source->ReadComments(); }
+
+ void StartedDeserializing() override { m_Source->StartedDeserializing(); }
+
+ void FinishedDeserializing() override { m_Source->FinishedDeserializing(); }
+
+ void StartTranslationUnit(clang::ASTConsumer *Consumer) override {
+ m_Source->StartTranslationUnit(Consumer);
+ }
+
+ void PrintStats() override;
+
+ bool layoutRecordType(
+ const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment,
+ llvm::DenseMap<const clang::FieldDecl *, uint64_t> &FieldOffsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &BaseOffsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &VirtualBaseOffsets) override {
+ return m_Source->layoutRecordType(Record, Size, Alignment, FieldOffsets,
+ BaseOffsets, VirtualBaseOffsets);
+ }
+};
+
+/// Wraps an ASTConsumer into an SemaConsumer. Doesn't take ownership of the
+/// provided consumer. If the provided ASTConsumer is also a SemaConsumer,
+/// the wrapper will also forward SemaConsumer functions.
+class ASTConsumerForwarder : public clang::SemaConsumer {
+ clang::ASTConsumer *m_c;
+ clang::SemaConsumer *m_sc;
+
+public:
+ ASTConsumerForwarder(clang::ASTConsumer *c) : m_c(c) {
+ m_sc = llvm::dyn_cast<clang::SemaConsumer>(m_c);
+ }
+
+ ~ASTConsumerForwarder() override;
+
+ void Initialize(clang::ASTContext &Context) override {
+ m_c->Initialize(Context);
+ }
+
+ bool HandleTopLevelDecl(clang::DeclGroupRef D) override {
+ return m_c->HandleTopLevelDecl(D);
+ }
+
+ void HandleInlineFunctionDefinition(clang::FunctionDecl *D) override {
+ m_c->HandleInlineFunctionDefinition(D);
+ }
+
+ void HandleInterestingDecl(clang::DeclGroupRef D) override {
+ m_c->HandleInterestingDecl(D);
+ }
+
+ void HandleTranslationUnit(clang::ASTContext &Ctx) override {
+ m_c->HandleTranslationUnit(Ctx);
+ }
+
+ void HandleTagDeclDefinition(clang::TagDecl *D) override {
+ m_c->HandleTagDeclDefinition(D);
+ }
+
+ void HandleTagDeclRequiredDefinition(const clang::TagDecl *D) override {
+ m_c->HandleTagDeclRequiredDefinition(D);
+ }
+
+ void HandleCXXImplicitFunctionInstantiation(clang::FunctionDecl *D) override {
+ m_c->HandleCXXImplicitFunctionInstantiation(D);
+ }
+
+ void HandleTopLevelDeclInObjCContainer(clang::DeclGroupRef D) override {
+ m_c->HandleTopLevelDeclInObjCContainer(D);
+ }
+
+ void HandleImplicitImportDecl(clang::ImportDecl *D) override {
+ m_c->HandleImplicitImportDecl(D);
+ }
+
+ void CompleteTentativeDefinition(clang::VarDecl *D) override {
+ m_c->CompleteTentativeDefinition(D);
+ }
+
+ void AssignInheritanceModel(clang::CXXRecordDecl *RD) override {
+ m_c->AssignInheritanceModel(RD);
+ }
+
+ void HandleCXXStaticMemberVarInstantiation(clang::VarDecl *D) override {
+ m_c->HandleCXXStaticMemberVarInstantiation(D);
+ }
+
+ void HandleVTable(clang::CXXRecordDecl *RD) override {
+ m_c->HandleVTable(RD);
+ }
+
+ clang::ASTMutationListener *GetASTMutationListener() override {
+ return m_c->GetASTMutationListener();
+ }
+
+ clang::ASTDeserializationListener *GetASTDeserializationListener() override {
+ return m_c->GetASTDeserializationListener();
+ }
+
+ void PrintStats() override;
+
+ void InitializeSema(clang::Sema &S) override {
+ if (m_sc)
+ m_sc->InitializeSema(S);
+ }
+
+ /// Inform the semantic consumer that Sema is no longer available.
+ void ForgetSema() override {
+ if (m_sc)
+ m_sc->ForgetSema();
+ }
+
+ bool shouldSkipFunctionBody(clang::Decl *D) override {
+ return m_c->shouldSkipFunctionBody(D);
+ }
+};
+
+/// A ExternalSemaSource multiplexer that prioritizes its sources.
+///
+/// This ExternalSemaSource will forward all requests to its attached sources.
+/// However, unlike a normal multiplexer it will not forward a request to all
+/// sources, but instead give priority to certain sources. If a source with a
+/// higher priority can fulfill a request, all sources with a lower priority
+/// will not receive the request.
+///
+/// This class is mostly use to multiplex between sources of different
+/// 'quality', e.g. a C++ modules and debug information. The C++ module will
+/// provide more accurate replies to the requests, but might not be able to
+/// answer all requests. The debug information will be used as a fallback then
+/// to provide information that is not in the C++ module.
+class SemaSourceWithPriorities : public clang::ExternalSemaSource {
+
+private:
+ /// The sources ordered in decreasing priority.
+ llvm::SmallVector<clang::ExternalSemaSource *, 2> Sources;
+
+public:
+ /// Construct a SemaSourceWithPriorities with a 'high quality' source that
+ /// has the higher priority and a 'low quality' source that will be used
+ /// as a fallback.
+ SemaSourceWithPriorities(clang::ExternalSemaSource &high_quality_source,
+ clang::ExternalSemaSource &low_quality_source) {
+ Sources.push_back(&high_quality_source);
+ Sources.push_back(&low_quality_source);
+ }
+
+ ~SemaSourceWithPriorities() override;
+
+ void addSource(clang::ExternalSemaSource &source) {
+ Sources.push_back(&source);
+ }
+
+ //===--------------------------------------------------------------------===//
+ // ExternalASTSource.
+ //===--------------------------------------------------------------------===//
+
+ clang::Decl *GetExternalDecl(uint32_t ID) override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ if (clang::Decl *Result = Sources[i]->GetExternalDecl(ID))
+ return Result;
+ return nullptr;
+ }
+
+ void CompleteRedeclChain(const clang::Decl *D) override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ Sources[i]->CompleteRedeclChain(D);
+ }
+
+ clang::Selector GetExternalSelector(uint32_t ID) override {
+ clang::Selector Sel;
+ for (size_t i = 0; i < Sources.size(); ++i) {
+ Sel = Sources[i]->GetExternalSelector(ID);
+ if (!Sel.isNull())
+ return Sel;
+ }
+ return Sel;
+ }
+
+ uint32_t GetNumExternalSelectors() override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ if (uint32_t total = Sources[i]->GetNumExternalSelectors())
+ return total;
+ return 0;
+ }
+
+ clang::Stmt *GetExternalDeclStmt(uint64_t Offset) override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ if (clang::Stmt *Result = Sources[i]->GetExternalDeclStmt(Offset))
+ return Result;
+ return nullptr;
+ }
+
+ clang::CXXBaseSpecifier *
+ GetExternalCXXBaseSpecifiers(uint64_t Offset) override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ if (clang::CXXBaseSpecifier *R =
+ Sources[i]->GetExternalCXXBaseSpecifiers(Offset))
+ return R;
+ return nullptr;
+ }
+
+ clang::CXXCtorInitializer **
+ GetExternalCXXCtorInitializers(uint64_t Offset) override {
+ for (auto *S : Sources)
+ if (auto *R = S->GetExternalCXXCtorInitializers(Offset))
+ return R;
+ return nullptr;
+ }
+
+ ExtKind hasExternalDefinitions(const clang::Decl *D) override {
+ for (const auto &S : Sources)
+ if (auto EK = S->hasExternalDefinitions(D))
+ if (EK != EK_ReplyHazy)
+ return EK;
+ return EK_ReplyHazy;
+ }
+
+ bool FindExternalVisibleDeclsByName(const clang::DeclContext *DC,
+ clang::DeclarationName Name) override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ if (Sources[i]->FindExternalVisibleDeclsByName(DC, Name))
+ return true;
+ return false;
+ }
+
+ void completeVisibleDeclsMap(const clang::DeclContext *DC) override {
+ // FIXME: Only one source should be able to complete the decls map.
+ for (size_t i = 0; i < Sources.size(); ++i)
+ Sources[i]->completeVisibleDeclsMap(DC);
+ }
+
+ void FindExternalLexicalDecls(
+ const clang::DeclContext *DC,
+ llvm::function_ref<bool(clang::Decl::Kind)> IsKindWeWant,
+ llvm::SmallVectorImpl<clang::Decl *> &Result) override {
+ for (size_t i = 0; i < Sources.size(); ++i) {
+ Sources[i]->FindExternalLexicalDecls(DC, IsKindWeWant, Result);
+ if (!Result.empty())
+ return;
+ }
+ }
+
+ void
+ FindFileRegionDecls(clang::FileID File, unsigned Offset, unsigned Length,
+ llvm::SmallVectorImpl<clang::Decl *> &Decls) override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ Sources[i]->FindFileRegionDecls(File, Offset, Length, Decls);
+ }
+
+ void CompleteType(clang::TagDecl *Tag) override {
+ while (!Tag->isCompleteDefinition())
+ for (size_t i = 0; i < Sources.size(); ++i) {
+ // FIXME: We are technically supposed to loop here too until
+ // Tag->isCompleteDefinition() is true, but if our low quality source
+ // is failing to complete the tag this code will deadlock.
+ Sources[i]->CompleteType(Tag);
+ if (Tag->isCompleteDefinition())
+ break;
+ }
+ }
+
+ void CompleteType(clang::ObjCInterfaceDecl *Class) override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ Sources[i]->CompleteType(Class);
+ }
+
+ void ReadComments() override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ Sources[i]->ReadComments();
+ }
+
+ void StartedDeserializing() override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ Sources[i]->StartedDeserializing();
+ }
+
+ void FinishedDeserializing() override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ Sources[i]->FinishedDeserializing();
+ }
+
+ void StartTranslationUnit(clang::ASTConsumer *Consumer) override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ Sources[i]->StartTranslationUnit(Consumer);
+ }
+
+ void PrintStats() override;
+
+ clang::Module *getModule(unsigned ID) override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ if (auto M = Sources[i]->getModule(ID))
+ return M;
+ return nullptr;
+ }
+
+ bool DeclIsFromPCHWithObjectFile(const clang::Decl *D) override {
+ for (auto *S : Sources)
+ if (S->DeclIsFromPCHWithObjectFile(D))
+ return true;
+ return false;
+ }
+
+ bool layoutRecordType(
+ const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment,
+ llvm::DenseMap<const clang::FieldDecl *, uint64_t> &FieldOffsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &BaseOffsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &VirtualBaseOffsets) override {
+ for (size_t i = 0; i < Sources.size(); ++i)
+ if (Sources[i]->layoutRecordType(Record, Size, Alignment, FieldOffsets,
+ BaseOffsets, VirtualBaseOffsets))
+ return true;
+ return false;
+ }
+
+ void getMemoryBufferSizes(MemoryBufferSizes &sizes) const override {
+ for (auto &Source : Sources)
+ Source->getMemoryBufferSizes(sizes);
+ }
+
+ //===--------------------------------------------------------------------===//
+ // ExternalSemaSource.
+ //===--------------------------------------------------------------------===//
+
+ void InitializeSema(clang::Sema &S) override {
+ for (auto &Source : Sources)
+ Source->InitializeSema(S);
+ }
+
+ void ForgetSema() override {
+ for (auto &Source : Sources)
+ Source->ForgetSema();
+ }
+
+ void ReadMethodPool(clang::Selector Sel) override {
+ for (auto &Source : Sources)
+ Source->ReadMethodPool(Sel);
+ }
+
+ void updateOutOfDateSelector(clang::Selector Sel) override {
+ for (auto &Source : Sources)
+ Source->updateOutOfDateSelector(Sel);
+ }
+
+ void ReadKnownNamespaces(
+ llvm::SmallVectorImpl<clang::NamespaceDecl *> &Namespaces) override {
+ for (auto &Source : Sources)
+ Source->ReadKnownNamespaces(Namespaces);
+ }
+
+ void ReadUndefinedButUsed(
+ llvm::MapVector<clang::NamedDecl *, clang::SourceLocation> &Undefined)
+ override {
+ for (auto &Source : Sources)
+ Source->ReadUndefinedButUsed(Undefined);
+ }
+
+ void ReadMismatchingDeleteExpressions(
+ llvm::MapVector<clang::FieldDecl *,
+ llvm::SmallVector<std::pair<clang::SourceLocation, bool>,
+ 4>> &Exprs) override {
+ for (auto &Source : Sources)
+ Source->ReadMismatchingDeleteExpressions(Exprs);
+ }
+
+ bool LookupUnqualified(clang::LookupResult &R, clang::Scope *S) override {
+ for (auto &Source : Sources) {
+ Source->LookupUnqualified(R, S);
+ if (!R.empty())
+ break;
+ }
+
+ return !R.empty();
+ }
+
+ void ReadTentativeDefinitions(
+ llvm::SmallVectorImpl<clang::VarDecl *> &Defs) override {
+ for (auto &Source : Sources)
+ Source->ReadTentativeDefinitions(Defs);
+ }
+
+ void ReadUnusedFileScopedDecls(
+ llvm::SmallVectorImpl<const clang::DeclaratorDecl *> &Decls) override {
+ for (auto &Source : Sources)
+ Source->ReadUnusedFileScopedDecls(Decls);
+ }
+
+ void ReadDelegatingConstructors(
+ llvm::SmallVectorImpl<clang::CXXConstructorDecl *> &Decls) override {
+ for (auto &Source : Sources)
+ Source->ReadDelegatingConstructors(Decls);
+ }
+
+ void ReadExtVectorDecls(
+ llvm::SmallVectorImpl<clang::TypedefNameDecl *> &Decls) override {
+ for (auto &Source : Sources)
+ Source->ReadExtVectorDecls(Decls);
+ }
+
+ void ReadUnusedLocalTypedefNameCandidates(
+ llvm::SmallSetVector<const clang::TypedefNameDecl *, 4> &Decls) override {
+ for (auto &Source : Sources)
+ Source->ReadUnusedLocalTypedefNameCandidates(Decls);
+ }
+
+ void ReadReferencedSelectors(
+ llvm::SmallVectorImpl<std::pair<clang::Selector, clang::SourceLocation>>
+ &Sels) override {
+ for (auto &Source : Sources)
+ Source->ReadReferencedSelectors(Sels);
+ }
+
+ void ReadWeakUndeclaredIdentifiers(
+ llvm::SmallVectorImpl<std::pair<clang::IdentifierInfo *, clang::WeakInfo>>
+ &WI) override {
+ for (auto &Source : Sources)
+ Source->ReadWeakUndeclaredIdentifiers(WI);
+ }
+
+ void ReadUsedVTables(
+ llvm::SmallVectorImpl<clang::ExternalVTableUse> &VTables) override {
+ for (auto &Source : Sources)
+ Source->ReadUsedVTables(VTables);
+ }
+
+ void ReadPendingInstantiations(
+ llvm::SmallVectorImpl<
+ std::pair<clang::ValueDecl *, clang::SourceLocation>> &Pending)
+ override {
+ for (auto &Source : Sources)
+ Source->ReadPendingInstantiations(Pending);
+ }
+
+ void ReadLateParsedTemplates(
+ llvm::MapVector<const clang::FunctionDecl *,
+ std::unique_ptr<clang::LateParsedTemplate>> &LPTMap)
+ override {
+ for (auto &Source : Sources)
+ Source->ReadLateParsedTemplates(LPTMap);
+ }
+
+ clang::TypoCorrection
+ CorrectTypo(const clang::DeclarationNameInfo &Typo, int LookupKind,
+ clang::Scope *S, clang::CXXScopeSpec *SS,
+ clang::CorrectionCandidateCallback &CCC,
+ clang::DeclContext *MemberContext, bool EnteringContext,
+ const clang::ObjCObjectPointerType *OPT) override {
+ for (auto &Source : Sources) {
+ if (clang::TypoCorrection C =
+ Source->CorrectTypo(Typo, LookupKind, S, SS, CCC,
+ MemberContext, EnteringContext, OPT))
+ return C;
+ }
+ return clang::TypoCorrection();
+ }
+
+ bool MaybeDiagnoseMissingCompleteType(clang::SourceLocation Loc,
+ clang::QualType T) override {
+ for (auto &Source : Sources) {
+ if (Source->MaybeDiagnoseMissingCompleteType(Loc, T))
+ return true;
+ }
+ return false;
+ }
+};
+
+} // namespace lldb_private
+#endif // liblldb_ASTUtils_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp
new file mode 100644
index 000000000000..c5778f86bb62
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp
@@ -0,0 +1,2237 @@
+//===-- ClangASTSource.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 "ClangASTSource.h"
+
+#include "ASTDumper.h"
+#include "ClangModulesDeclVendor.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangUtil.h"
+#include "lldb/Symbol/CompilerDeclContext.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/TaggedASTType.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Log.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecordLayout.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+#include <memory>
+#include <vector>
+
+using namespace clang;
+using namespace lldb_private;
+
+// Scoped class that will remove an active lexical decl from the set when it
+// goes out of scope.
+namespace {
+class ScopedLexicalDeclEraser {
+public:
+ ScopedLexicalDeclEraser(std::set<const clang::Decl *> &decls,
+ const clang::Decl *decl)
+ : m_active_lexical_decls(decls), m_decl(decl) {}
+
+ ~ScopedLexicalDeclEraser() { m_active_lexical_decls.erase(m_decl); }
+
+private:
+ std::set<const clang::Decl *> &m_active_lexical_decls;
+ const clang::Decl *m_decl;
+};
+}
+
+ClangASTSource::ClangASTSource(const lldb::TargetSP &target)
+ : m_import_in_progress(false), m_lookups_enabled(false), m_target(target),
+ m_ast_context(nullptr), m_active_lexical_decls(), m_active_lookups() {
+ if (!target->GetUseModernTypeLookup()) {
+ m_ast_importer_sp = m_target->GetClangASTImporter();
+ }
+}
+
+void ClangASTSource::InstallASTContext(clang::ASTContext &ast_context,
+ clang::FileManager &file_manager,
+ bool is_shared_context) {
+ m_ast_context = &ast_context;
+ m_file_manager = &file_manager;
+ if (m_target->GetUseModernTypeLookup()) {
+ // Configure the ExternalASTMerger. The merger needs to be able to import
+ // types from any source that we would do lookups in, which includes the
+ // persistent AST context as well as the modules and Objective-C runtime
+ // AST contexts.
+
+ lldbassert(!m_merger_up);
+ clang::ExternalASTMerger::ImporterTarget target = {ast_context,
+ file_manager};
+ std::vector<clang::ExternalASTMerger::ImporterSource> sources;
+ for (lldb::ModuleSP module_sp : m_target->GetImages().Modules()) {
+ if (auto *module_ast_ctx = llvm::cast_or_null<ClangASTContext>(
+ module_sp->GetTypeSystemForLanguage(lldb::eLanguageTypeC))) {
+ lldbassert(module_ast_ctx->getASTContext());
+ lldbassert(module_ast_ctx->getFileManager());
+ sources.push_back({*module_ast_ctx->getASTContext(),
+ *module_ast_ctx->getFileManager(),
+ module_ast_ctx->GetOriginMap()
+ });
+ }
+ }
+
+ do {
+ lldb::ProcessSP process(m_target->GetProcessSP());
+
+ if (!process)
+ break;
+
+ ObjCLanguageRuntime *language_runtime(ObjCLanguageRuntime::Get(*process));
+
+ if (!language_runtime)
+ break;
+
+ DeclVendor *runtime_decl_vendor = language_runtime->GetDeclVendor();
+
+ if (!runtime_decl_vendor)
+ break;
+
+ sources.push_back(runtime_decl_vendor->GetImporterSource());
+ } while (false);
+
+ do {
+ DeclVendor *modules_decl_vendor =
+ m_target->GetClangModulesDeclVendor();
+
+ if (!modules_decl_vendor)
+ break;
+
+ sources.push_back(modules_decl_vendor->GetImporterSource());
+ } while (false);
+
+ if (!is_shared_context) {
+ // Update the scratch AST context's merger to reflect any new sources we
+ // might have come across since the last time an expression was parsed.
+
+ auto scratch_ast_context = static_cast<ClangASTContextForExpressions*>(
+ m_target->GetScratchClangASTContext());
+
+ scratch_ast_context->GetMergerUnchecked().AddSources(sources);
+
+ sources.push_back({*scratch_ast_context->getASTContext(),
+ *scratch_ast_context->getFileManager(),
+ scratch_ast_context->GetOriginMap()});
+ }
+ while (false)
+ ;
+
+ m_merger_up =
+ llvm::make_unique<clang::ExternalASTMerger>(target, sources);
+ } else {
+ m_ast_importer_sp->InstallMapCompleter(&ast_context, *this);
+ }
+}
+
+ClangASTSource::~ClangASTSource() {
+ if (m_ast_importer_sp)
+ m_ast_importer_sp->ForgetDestination(m_ast_context);
+
+ // We are in the process of destruction, don't create clang ast context on
+ // demand by passing false to
+ // Target::GetScratchClangASTContext(create_on_demand).
+ ClangASTContext *scratch_clang_ast_context =
+ m_target->GetScratchClangASTContext(false);
+
+ if (!scratch_clang_ast_context)
+ return;
+
+ clang::ASTContext *scratch_ast_context =
+ scratch_clang_ast_context->getASTContext();
+
+ if (!scratch_ast_context)
+ return;
+
+ if (m_ast_context != scratch_ast_context && m_ast_importer_sp)
+ m_ast_importer_sp->ForgetSource(scratch_ast_context, m_ast_context);
+}
+
+void ClangASTSource::StartTranslationUnit(ASTConsumer *Consumer) {
+ if (!m_ast_context)
+ return;
+
+ m_ast_context->getTranslationUnitDecl()->setHasExternalVisibleStorage();
+ m_ast_context->getTranslationUnitDecl()->setHasExternalLexicalStorage();
+}
+
+// The core lookup interface.
+bool ClangASTSource::FindExternalVisibleDeclsByName(
+ const DeclContext *decl_ctx, DeclarationName clang_decl_name) {
+ if (!m_ast_context) {
+ SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name);
+ return false;
+ }
+
+ if (GetImportInProgress()) {
+ SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name);
+ return false;
+ }
+
+ std::string decl_name(clang_decl_name.getAsString());
+
+ // if (m_decl_map.DoingASTImport ())
+ // return DeclContext::lookup_result();
+ //
+ switch (clang_decl_name.getNameKind()) {
+ // Normal identifiers.
+ case DeclarationName::Identifier: {
+ clang::IdentifierInfo *identifier_info =
+ clang_decl_name.getAsIdentifierInfo();
+
+ if (!identifier_info || identifier_info->getBuiltinID() != 0) {
+ SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name);
+ return false;
+ }
+ } break;
+
+ // Operator names.
+ case DeclarationName::CXXOperatorName:
+ case DeclarationName::CXXLiteralOperatorName:
+ break;
+
+ // Using directives found in this context.
+ // Tell Sema we didn't find any or we'll end up getting asked a *lot*.
+ case DeclarationName::CXXUsingDirective:
+ SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name);
+ return false;
+
+ case DeclarationName::ObjCZeroArgSelector:
+ case DeclarationName::ObjCOneArgSelector:
+ case DeclarationName::ObjCMultiArgSelector: {
+ llvm::SmallVector<NamedDecl *, 1> method_decls;
+
+ NameSearchContext method_search_context(*this, method_decls,
+ clang_decl_name, decl_ctx);
+
+ FindObjCMethodDecls(method_search_context);
+
+ SetExternalVisibleDeclsForName(decl_ctx, clang_decl_name, method_decls);
+ return (method_decls.size() > 0);
+ }
+ // These aren't possible in the global context.
+ case DeclarationName::CXXConstructorName:
+ case DeclarationName::CXXDestructorName:
+ case DeclarationName::CXXConversionFunctionName:
+ case DeclarationName::CXXDeductionGuideName:
+ SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name);
+ return false;
+ }
+
+ if (!GetLookupsEnabled()) {
+ // Wait until we see a '$' at the start of a name before we start doing any
+ // lookups so we can avoid lookup up all of the builtin types.
+ if (!decl_name.empty() && decl_name[0] == '$') {
+ SetLookupsEnabled(true);
+ } else {
+ SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name);
+ return false;
+ }
+ }
+
+ ConstString const_decl_name(decl_name.c_str());
+
+ const char *uniqued_const_decl_name = const_decl_name.GetCString();
+ if (m_active_lookups.find(uniqued_const_decl_name) !=
+ m_active_lookups.end()) {
+ // We are currently looking up this name...
+ SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name);
+ return false;
+ }
+ m_active_lookups.insert(uniqued_const_decl_name);
+ // static uint32_t g_depth = 0;
+ // ++g_depth;
+ // printf("[%5u] FindExternalVisibleDeclsByName() \"%s\"\n", g_depth,
+ // uniqued_const_decl_name);
+ llvm::SmallVector<NamedDecl *, 4> name_decls;
+ NameSearchContext name_search_context(*this, name_decls, clang_decl_name,
+ decl_ctx);
+ FindExternalVisibleDecls(name_search_context);
+ SetExternalVisibleDeclsForName(decl_ctx, clang_decl_name, name_decls);
+ // --g_depth;
+ m_active_lookups.erase(uniqued_const_decl_name);
+ return (name_decls.size() != 0);
+}
+
+void ClangASTSource::CompleteType(TagDecl *tag_decl) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ static unsigned int invocation_id = 0;
+ unsigned int current_id = invocation_id++;
+
+ if (log) {
+ log->Printf(" CompleteTagDecl[%u] on (ASTContext*)%p Completing "
+ "(TagDecl*)%p named %s",
+ current_id, static_cast<void *>(m_ast_context),
+ static_cast<void *>(tag_decl),
+ tag_decl->getName().str().c_str());
+
+ log->Printf(" CTD[%u] Before:", current_id);
+ ASTDumper dumper((Decl *)tag_decl);
+ dumper.ToLog(log, " [CTD] ");
+ }
+
+ auto iter = m_active_lexical_decls.find(tag_decl);
+ if (iter != m_active_lexical_decls.end())
+ return;
+ m_active_lexical_decls.insert(tag_decl);
+ ScopedLexicalDeclEraser eraser(m_active_lexical_decls, tag_decl);
+
+ if (!m_ast_importer_sp) {
+ if (HasMerger()) {
+ GetMergerUnchecked().CompleteType(tag_decl);
+ }
+ return;
+ }
+
+ if (!m_ast_importer_sp->CompleteTagDecl(tag_decl)) {
+ // We couldn't complete the type. Maybe there's a definition somewhere
+ // else that can be completed.
+
+ if (log)
+ log->Printf(" CTD[%u] Type could not be completed in the module in "
+ "which it was first found.",
+ current_id);
+
+ bool found = false;
+
+ DeclContext *decl_ctx = tag_decl->getDeclContext();
+
+ if (const NamespaceDecl *namespace_context =
+ dyn_cast<NamespaceDecl>(decl_ctx)) {
+ ClangASTImporter::NamespaceMapSP namespace_map =
+ m_ast_importer_sp->GetNamespaceMap(namespace_context);
+
+ if (log && log->GetVerbose())
+ log->Printf(" CTD[%u] Inspecting namespace map %p (%d entries)",
+ current_id, static_cast<void *>(namespace_map.get()),
+ static_cast<int>(namespace_map->size()));
+
+ if (!namespace_map)
+ return;
+
+ for (ClangASTImporter::NamespaceMap::iterator i = namespace_map->begin(),
+ e = namespace_map->end();
+ i != e && !found; ++i) {
+ if (log)
+ log->Printf(" CTD[%u] Searching namespace %s in module %s",
+ current_id, i->second.GetName().AsCString(),
+ i->first->GetFileSpec().GetFilename().GetCString());
+
+ TypeList types;
+
+ ConstString name(tag_decl->getName().str().c_str());
+
+ i->first->FindTypesInNamespace(name, &i->second, UINT32_MAX, types);
+
+ for (uint32_t ti = 0, te = types.GetSize(); ti != te && !found; ++ti) {
+ lldb::TypeSP type = types.GetTypeAtIndex(ti);
+
+ if (!type)
+ continue;
+
+ CompilerType clang_type(type->GetFullCompilerType());
+
+ if (!ClangUtil::IsClangType(clang_type))
+ continue;
+
+ const TagType *tag_type =
+ ClangUtil::GetQualType(clang_type)->getAs<TagType>();
+
+ if (!tag_type)
+ continue;
+
+ TagDecl *candidate_tag_decl =
+ const_cast<TagDecl *>(tag_type->getDecl());
+
+ if (m_ast_importer_sp->CompleteTagDeclWithOrigin(tag_decl,
+ candidate_tag_decl))
+ found = true;
+ }
+ }
+ } else {
+ TypeList types;
+
+ ConstString name(tag_decl->getName().str().c_str());
+ CompilerDeclContext namespace_decl;
+
+ const ModuleList &module_list = m_target->GetImages();
+
+ bool exact_match = false;
+ llvm::DenseSet<SymbolFile *> searched_symbol_files;
+ module_list.FindTypes(nullptr, name, exact_match, UINT32_MAX,
+ searched_symbol_files, types);
+
+ for (uint32_t ti = 0, te = types.GetSize(); ti != te && !found; ++ti) {
+ lldb::TypeSP type = types.GetTypeAtIndex(ti);
+
+ if (!type)
+ continue;
+
+ CompilerType clang_type(type->GetFullCompilerType());
+
+ if (!ClangUtil::IsClangType(clang_type))
+ continue;
+
+ const TagType *tag_type =
+ ClangUtil::GetQualType(clang_type)->getAs<TagType>();
+
+ if (!tag_type)
+ continue;
+
+ TagDecl *candidate_tag_decl =
+ const_cast<TagDecl *>(tag_type->getDecl());
+
+ // We have found a type by basename and we need to make sure the decl
+ // contexts are the same before we can try to complete this type with
+ // another
+ if (!ClangASTContext::DeclsAreEquivalent(tag_decl, candidate_tag_decl))
+ continue;
+
+ if (m_ast_importer_sp->CompleteTagDeclWithOrigin(tag_decl,
+ candidate_tag_decl))
+ found = true;
+ }
+ }
+ }
+
+ if (log) {
+ log->Printf(" [CTD] After:");
+ ASTDumper dumper((Decl *)tag_decl);
+ dumper.ToLog(log, " [CTD] ");
+ }
+}
+
+void ClangASTSource::CompleteType(clang::ObjCInterfaceDecl *interface_decl) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ log->Printf(" [CompleteObjCInterfaceDecl] on (ASTContext*)%p Completing "
+ "an ObjCInterfaceDecl named %s",
+ static_cast<void *>(m_ast_context),
+ interface_decl->getName().str().c_str());
+ log->Printf(" [COID] Before:");
+ ASTDumper dumper((Decl *)interface_decl);
+ dumper.ToLog(log, " [COID] ");
+ }
+
+ if (!m_ast_importer_sp) {
+ if (HasMerger()) {
+ ObjCInterfaceDecl *complete_iface_decl =
+ GetCompleteObjCInterface(interface_decl);
+
+ if (complete_iface_decl && (complete_iface_decl != interface_decl)) {
+ m_merger_up->ForceRecordOrigin(interface_decl, {complete_iface_decl, &complete_iface_decl->getASTContext()});
+ }
+
+ GetMergerUnchecked().CompleteType(interface_decl);
+ } else {
+ lldbassert(0 && "No mechanism for completing a type!");
+ }
+ return;
+ }
+
+ Decl *original_decl = nullptr;
+ ASTContext *original_ctx = nullptr;
+
+ if (m_ast_importer_sp->ResolveDeclOrigin(interface_decl, &original_decl,
+ &original_ctx)) {
+ if (ObjCInterfaceDecl *original_iface_decl =
+ dyn_cast<ObjCInterfaceDecl>(original_decl)) {
+ ObjCInterfaceDecl *complete_iface_decl =
+ GetCompleteObjCInterface(original_iface_decl);
+
+ if (complete_iface_decl && (complete_iface_decl != original_iface_decl)) {
+ m_ast_importer_sp->SetDeclOrigin(interface_decl, complete_iface_decl);
+ }
+ }
+ }
+
+ m_ast_importer_sp->CompleteObjCInterfaceDecl(interface_decl);
+
+ if (interface_decl->getSuperClass() &&
+ interface_decl->getSuperClass() != interface_decl)
+ CompleteType(interface_decl->getSuperClass());
+
+ if (log) {
+ log->Printf(" [COID] After:");
+ ASTDumper dumper((Decl *)interface_decl);
+ dumper.ToLog(log, " [COID] ");
+ }
+}
+
+clang::ObjCInterfaceDecl *ClangASTSource::GetCompleteObjCInterface(
+ const clang::ObjCInterfaceDecl *interface_decl) {
+ lldb::ProcessSP process(m_target->GetProcessSP());
+
+ if (!process)
+ return nullptr;
+
+ ObjCLanguageRuntime *language_runtime(ObjCLanguageRuntime::Get(*process));
+
+ if (!language_runtime)
+ return nullptr;
+
+ ConstString class_name(interface_decl->getNameAsString().c_str());
+
+ lldb::TypeSP complete_type_sp(
+ language_runtime->LookupInCompleteClassCache(class_name));
+
+ if (!complete_type_sp)
+ return nullptr;
+
+ TypeFromUser complete_type =
+ TypeFromUser(complete_type_sp->GetFullCompilerType());
+ lldb::opaque_compiler_type_t complete_opaque_type =
+ complete_type.GetOpaqueQualType();
+
+ if (!complete_opaque_type)
+ return nullptr;
+
+ const clang::Type *complete_clang_type =
+ QualType::getFromOpaquePtr(complete_opaque_type).getTypePtr();
+ const ObjCInterfaceType *complete_interface_type =
+ dyn_cast<ObjCInterfaceType>(complete_clang_type);
+
+ if (!complete_interface_type)
+ return nullptr;
+
+ ObjCInterfaceDecl *complete_iface_decl(complete_interface_type->getDecl());
+
+ return complete_iface_decl;
+}
+
+void ClangASTSource::FindExternalLexicalDecls(
+ const DeclContext *decl_context,
+ llvm::function_ref<bool(Decl::Kind)> predicate,
+ llvm::SmallVectorImpl<Decl *> &decls) {
+
+ if (HasMerger()) {
+ if (auto *interface_decl = dyn_cast<ObjCInterfaceDecl>(decl_context)) {
+ ObjCInterfaceDecl *complete_iface_decl =
+ GetCompleteObjCInterface(interface_decl);
+
+ if (complete_iface_decl && (complete_iface_decl != interface_decl)) {
+ m_merger_up->ForceRecordOrigin(interface_decl, {complete_iface_decl, &complete_iface_decl->getASTContext()});
+ }
+ }
+ return GetMergerUnchecked().FindExternalLexicalDecls(decl_context,
+ predicate,
+ decls);
+ } else if (!m_ast_importer_sp)
+ return;
+
+ ClangASTMetrics::RegisterLexicalQuery();
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ const Decl *context_decl = dyn_cast<Decl>(decl_context);
+
+ if (!context_decl)
+ return;
+
+ auto iter = m_active_lexical_decls.find(context_decl);
+ if (iter != m_active_lexical_decls.end())
+ return;
+ m_active_lexical_decls.insert(context_decl);
+ ScopedLexicalDeclEraser eraser(m_active_lexical_decls, context_decl);
+
+ static unsigned int invocation_id = 0;
+ unsigned int current_id = invocation_id++;
+
+ if (log) {
+ if (const NamedDecl *context_named_decl = dyn_cast<NamedDecl>(context_decl))
+ log->Printf(
+ "FindExternalLexicalDecls[%u] on (ASTContext*)%p in '%s' (%sDecl*)%p",
+ current_id, static_cast<void *>(m_ast_context),
+ context_named_decl->getNameAsString().c_str(),
+ context_decl->getDeclKindName(),
+ static_cast<const void *>(context_decl));
+ else if (context_decl)
+ log->Printf(
+ "FindExternalLexicalDecls[%u] on (ASTContext*)%p in (%sDecl*)%p",
+ current_id, static_cast<void *>(m_ast_context),
+ context_decl->getDeclKindName(),
+ static_cast<const void *>(context_decl));
+ else
+ log->Printf(
+ "FindExternalLexicalDecls[%u] on (ASTContext*)%p in a NULL context",
+ current_id, static_cast<const void *>(m_ast_context));
+ }
+
+ Decl *original_decl = nullptr;
+ ASTContext *original_ctx = nullptr;
+
+ if (!m_ast_importer_sp->ResolveDeclOrigin(context_decl, &original_decl,
+ &original_ctx))
+ return;
+
+ if (log) {
+ log->Printf(" FELD[%u] Original decl (ASTContext*)%p (Decl*)%p:",
+ current_id, static_cast<void *>(original_ctx),
+ static_cast<void *>(original_decl));
+ ASTDumper(original_decl).ToLog(log, " ");
+ }
+
+ if (ObjCInterfaceDecl *original_iface_decl =
+ dyn_cast<ObjCInterfaceDecl>(original_decl)) {
+ ObjCInterfaceDecl *complete_iface_decl =
+ GetCompleteObjCInterface(original_iface_decl);
+
+ if (complete_iface_decl && (complete_iface_decl != original_iface_decl)) {
+ original_decl = complete_iface_decl;
+ original_ctx = &complete_iface_decl->getASTContext();
+
+ m_ast_importer_sp->SetDeclOrigin(context_decl, complete_iface_decl);
+ }
+ }
+
+ if (TagDecl *original_tag_decl = dyn_cast<TagDecl>(original_decl)) {
+ ExternalASTSource *external_source = original_ctx->getExternalSource();
+
+ if (external_source)
+ external_source->CompleteType(original_tag_decl);
+ }
+
+ const DeclContext *original_decl_context =
+ dyn_cast<DeclContext>(original_decl);
+
+ if (!original_decl_context)
+ return;
+
+ // Indicates whether we skipped any Decls of the original DeclContext.
+ bool SkippedDecls = false;
+ for (TagDecl::decl_iterator iter = original_decl_context->decls_begin();
+ iter != original_decl_context->decls_end(); ++iter) {
+ Decl *decl = *iter;
+
+ // The predicate function returns true if the passed declaration kind is
+ // the one we are looking for.
+ // See clang::ExternalASTSource::FindExternalLexicalDecls()
+ if (predicate(decl->getKind())) {
+ if (log) {
+ ASTDumper ast_dumper(decl);
+ if (const NamedDecl *context_named_decl =
+ dyn_cast<NamedDecl>(context_decl))
+ log->Printf(" FELD[%d] Adding [to %sDecl %s] lexical %sDecl %s",
+ current_id, context_named_decl->getDeclKindName(),
+ context_named_decl->getNameAsString().c_str(),
+ decl->getDeclKindName(), ast_dumper.GetCString());
+ else
+ log->Printf(" FELD[%d] Adding lexical %sDecl %s", current_id,
+ decl->getDeclKindName(), ast_dumper.GetCString());
+ }
+
+ Decl *copied_decl = CopyDecl(decl);
+
+ if (!copied_decl)
+ continue;
+
+ if (FieldDecl *copied_field = dyn_cast<FieldDecl>(copied_decl)) {
+ QualType copied_field_type = copied_field->getType();
+
+ m_ast_importer_sp->RequireCompleteType(copied_field_type);
+ }
+ } else {
+ SkippedDecls = true;
+ }
+ }
+
+ // CopyDecl may build a lookup table which may set up ExternalLexicalStorage
+ // to false. However, since we skipped some of the external Decls we must
+ // set it back!
+ if (SkippedDecls) {
+ decl_context->setHasExternalLexicalStorage(true);
+ // This sets HasLazyExternalLexicalLookups to true. By setting this bit we
+ // ensure that the lookup table is rebuilt, which means the external source
+ // is consulted again when a clang::DeclContext::lookup is called.
+ const_cast<DeclContext *>(decl_context)->setMustBuildLookupTable();
+ }
+
+ return;
+}
+
+void ClangASTSource::FindExternalVisibleDecls(NameSearchContext &context) {
+ assert(m_ast_context);
+
+ ClangASTMetrics::RegisterVisibleQuery();
+
+ const ConstString name(context.m_decl_name.getAsString().c_str());
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ static unsigned int invocation_id = 0;
+ unsigned int current_id = invocation_id++;
+
+ if (log) {
+ if (!context.m_decl_context)
+ log->Printf("ClangASTSource::FindExternalVisibleDecls[%u] on "
+ "(ASTContext*)%p for '%s' in a NULL DeclContext",
+ current_id, static_cast<void *>(m_ast_context),
+ name.GetCString());
+ else if (const NamedDecl *context_named_decl =
+ dyn_cast<NamedDecl>(context.m_decl_context))
+ log->Printf("ClangASTSource::FindExternalVisibleDecls[%u] on "
+ "(ASTContext*)%p for '%s' in '%s'",
+ current_id, static_cast<void *>(m_ast_context),
+ name.GetCString(),
+ context_named_decl->getNameAsString().c_str());
+ else
+ log->Printf("ClangASTSource::FindExternalVisibleDecls[%u] on "
+ "(ASTContext*)%p for '%s' in a '%s'",
+ current_id, static_cast<void *>(m_ast_context),
+ name.GetCString(), context.m_decl_context->getDeclKindName());
+ }
+
+ if (HasMerger() && !isa<TranslationUnitDecl>(context.m_decl_context)
+ /* possibly handle NamespaceDecls here? */) {
+ if (auto *interface_decl =
+ dyn_cast<ObjCInterfaceDecl>(context.m_decl_context)) {
+ ObjCInterfaceDecl *complete_iface_decl =
+ GetCompleteObjCInterface(interface_decl);
+
+ if (complete_iface_decl && (complete_iface_decl != interface_decl)) {
+ GetMergerUnchecked().ForceRecordOrigin(
+ interface_decl,
+ {complete_iface_decl, &complete_iface_decl->getASTContext()});
+ }
+ }
+
+ GetMergerUnchecked().FindExternalVisibleDeclsByName(context.m_decl_context,
+ context.m_decl_name);
+ return; // otherwise we may need to fall back
+ }
+
+ context.m_namespace_map = std::make_shared<ClangASTImporter::NamespaceMap>();
+
+ if (const NamespaceDecl *namespace_context =
+ dyn_cast<NamespaceDecl>(context.m_decl_context)) {
+ ClangASTImporter::NamespaceMapSP namespace_map = m_ast_importer_sp ?
+ m_ast_importer_sp->GetNamespaceMap(namespace_context) : nullptr;
+
+ if (log && log->GetVerbose())
+ log->Printf(" CAS::FEVD[%u] Inspecting namespace map %p (%d entries)",
+ current_id, static_cast<void *>(namespace_map.get()),
+ static_cast<int>(namespace_map->size()));
+
+ if (!namespace_map)
+ return;
+
+ for (ClangASTImporter::NamespaceMap::iterator i = namespace_map->begin(),
+ e = namespace_map->end();
+ i != e; ++i) {
+ if (log)
+ log->Printf(" CAS::FEVD[%u] Searching namespace %s in module %s",
+ current_id, i->second.GetName().AsCString(),
+ i->first->GetFileSpec().GetFilename().GetCString());
+
+ FindExternalVisibleDecls(context, i->first, i->second, current_id);
+ }
+ } else if (isa<ObjCInterfaceDecl>(context.m_decl_context) && !HasMerger()) {
+ FindObjCPropertyAndIvarDecls(context);
+ } else if (!isa<TranslationUnitDecl>(context.m_decl_context)) {
+ // we shouldn't be getting FindExternalVisibleDecls calls for these
+ return;
+ } else {
+ CompilerDeclContext namespace_decl;
+
+ if (log)
+ log->Printf(" CAS::FEVD[%u] Searching the root namespace", current_id);
+
+ FindExternalVisibleDecls(context, lldb::ModuleSP(), namespace_decl,
+ current_id);
+ }
+
+ if (!context.m_namespace_map->empty()) {
+ if (log && log->GetVerbose())
+ log->Printf(" CAS::FEVD[%u] Registering namespace map %p (%d entries)",
+ current_id,
+ static_cast<void *>(context.m_namespace_map.get()),
+ static_cast<int>(context.m_namespace_map->size()));
+
+ NamespaceDecl *clang_namespace_decl =
+ AddNamespace(context, context.m_namespace_map);
+
+ if (clang_namespace_decl)
+ clang_namespace_decl->setHasExternalVisibleStorage();
+ }
+}
+
+clang::Sema *ClangASTSource::getSema() {
+ return ClangASTContext::GetASTContext(m_ast_context)->getSema();
+}
+
+bool ClangASTSource::IgnoreName(const ConstString name,
+ bool ignore_all_dollar_names) {
+ static const ConstString id_name("id");
+ static const ConstString Class_name("Class");
+
+ if (m_ast_context->getLangOpts().ObjC)
+ if (name == id_name || name == Class_name)
+ return true;
+
+ StringRef name_string_ref = name.GetStringRef();
+
+ // The ClangASTSource is not responsible for finding $-names.
+ return name_string_ref.empty() ||
+ (ignore_all_dollar_names && name_string_ref.startswith("$")) ||
+ name_string_ref.startswith("_$");
+}
+
+void ClangASTSource::FindExternalVisibleDecls(
+ NameSearchContext &context, lldb::ModuleSP module_sp,
+ CompilerDeclContext &namespace_decl, unsigned int current_id) {
+ assert(m_ast_context);
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ SymbolContextList sc_list;
+
+ const ConstString name(context.m_decl_name.getAsString().c_str());
+ if (IgnoreName(name, true))
+ return;
+
+ if (module_sp && namespace_decl) {
+ CompilerDeclContext found_namespace_decl;
+
+ SymbolVendor *symbol_vendor = module_sp->GetSymbolVendor();
+
+ if (symbol_vendor) {
+ found_namespace_decl =
+ symbol_vendor->FindNamespace(name, &namespace_decl);
+
+ if (found_namespace_decl) {
+ context.m_namespace_map->push_back(
+ std::pair<lldb::ModuleSP, CompilerDeclContext>(
+ module_sp, found_namespace_decl));
+
+ if (log)
+ log->Printf(" CAS::FEVD[%u] Found namespace %s in module %s",
+ current_id, name.GetCString(),
+ module_sp->GetFileSpec().GetFilename().GetCString());
+ }
+ }
+ } else if (!HasMerger()) {
+ const ModuleList &target_images = m_target->GetImages();
+ std::lock_guard<std::recursive_mutex> guard(target_images.GetMutex());
+
+ for (size_t i = 0, e = target_images.GetSize(); i < e; ++i) {
+ lldb::ModuleSP image = target_images.GetModuleAtIndexUnlocked(i);
+
+ if (!image)
+ continue;
+
+ CompilerDeclContext found_namespace_decl;
+
+ SymbolVendor *symbol_vendor = image->GetSymbolVendor();
+
+ if (!symbol_vendor)
+ continue;
+
+ found_namespace_decl =
+ symbol_vendor->FindNamespace(name, &namespace_decl);
+
+ if (found_namespace_decl) {
+ context.m_namespace_map->push_back(
+ std::pair<lldb::ModuleSP, CompilerDeclContext>(
+ image, found_namespace_decl));
+
+ if (log)
+ log->Printf(" CAS::FEVD[%u] Found namespace %s in module %s",
+ current_id, name.GetCString(),
+ image->GetFileSpec().GetFilename().GetCString());
+ }
+ }
+ }
+
+ do {
+ if (context.m_found.type)
+ break;
+
+ TypeList types;
+ const bool exact_match = true;
+ llvm::DenseSet<lldb_private::SymbolFile *> searched_symbol_files;
+ if (module_sp && namespace_decl)
+ module_sp->FindTypesInNamespace(name, &namespace_decl, 1, types);
+ else {
+ m_target->GetImages().FindTypes(module_sp.get(), name, exact_match, 1,
+ searched_symbol_files, types);
+ }
+
+ if (size_t num_types = types.GetSize()) {
+ for (size_t ti = 0; ti < num_types; ++ti) {
+ lldb::TypeSP type_sp = types.GetTypeAtIndex(ti);
+
+ if (log) {
+ const char *name_string = type_sp->GetName().GetCString();
+
+ log->Printf(" CAS::FEVD[%u] Matching type found for \"%s\": %s",
+ current_id, name.GetCString(),
+ (name_string ? name_string : "<anonymous>"));
+ }
+
+ CompilerType full_type = type_sp->GetFullCompilerType();
+
+ CompilerType copied_clang_type(GuardedCopyType(full_type));
+
+ if (!copied_clang_type) {
+ if (log)
+ log->Printf(" CAS::FEVD[%u] - Couldn't export a type", current_id);
+
+ continue;
+ }
+
+ context.AddTypeDecl(copied_clang_type);
+
+ context.m_found.type = true;
+ break;
+ }
+ }
+
+ if (!context.m_found.type) {
+ // Try the modules next.
+
+ do {
+ if (ClangModulesDeclVendor *modules_decl_vendor =
+ m_target->GetClangModulesDeclVendor()) {
+ bool append = false;
+ uint32_t max_matches = 1;
+ std::vector<clang::NamedDecl *> decls;
+
+ if (!modules_decl_vendor->FindDecls(name, append, max_matches, decls))
+ break;
+
+ if (log) {
+ log->Printf(" CAS::FEVD[%u] Matching entity found for \"%s\" in "
+ "the modules",
+ current_id, name.GetCString());
+ }
+
+ clang::NamedDecl *const decl_from_modules = decls[0];
+
+ if (llvm::isa<clang::TypeDecl>(decl_from_modules) ||
+ llvm::isa<clang::ObjCContainerDecl>(decl_from_modules) ||
+ llvm::isa<clang::EnumConstantDecl>(decl_from_modules)) {
+ clang::Decl *copied_decl = CopyDecl(decl_from_modules);
+ clang::NamedDecl *copied_named_decl =
+ copied_decl ? dyn_cast<clang::NamedDecl>(copied_decl) : nullptr;
+
+ if (!copied_named_decl) {
+ if (log)
+ log->Printf(
+ " CAS::FEVD[%u] - Couldn't export a type from the modules",
+ current_id);
+
+ break;
+ }
+
+ context.AddNamedDecl(copied_named_decl);
+
+ context.m_found.type = true;
+ }
+ }
+ } while (false);
+ }
+
+ if (!context.m_found.type) {
+ do {
+ // Couldn't find any types elsewhere. Try the Objective-C runtime if
+ // one exists.
+
+ lldb::ProcessSP process(m_target->GetProcessSP());
+
+ if (!process)
+ break;
+
+ ObjCLanguageRuntime *language_runtime(
+ ObjCLanguageRuntime::Get(*process));
+
+ if (!language_runtime)
+ break;
+
+ DeclVendor *decl_vendor = language_runtime->GetDeclVendor();
+
+ if (!decl_vendor)
+ break;
+
+ bool append = false;
+ uint32_t max_matches = 1;
+ std::vector<clang::NamedDecl *> decls;
+
+ if (!decl_vendor->FindDecls(name, append, max_matches, decls))
+ break;
+
+ if (log) {
+ log->Printf(
+ " CAS::FEVD[%u] Matching type found for \"%s\" in the runtime",
+ current_id, name.GetCString());
+ }
+
+ clang::Decl *copied_decl = CopyDecl(decls[0]);
+ clang::NamedDecl *copied_named_decl =
+ copied_decl ? dyn_cast<clang::NamedDecl>(copied_decl) : nullptr;
+
+ if (!copied_named_decl) {
+ if (log)
+ log->Printf(
+ " CAS::FEVD[%u] - Couldn't export a type from the runtime",
+ current_id);
+
+ break;
+ }
+
+ context.AddNamedDecl(copied_named_decl);
+ } while (false);
+ }
+
+ } while (false);
+}
+
+template <class D> class TaggedASTDecl {
+public:
+ TaggedASTDecl() : decl(nullptr) {}
+ TaggedASTDecl(D *_decl) : decl(_decl) {}
+ bool IsValid() const { return (decl != nullptr); }
+ bool IsInvalid() const { return !IsValid(); }
+ D *operator->() const { return decl; }
+ D *decl;
+};
+
+template <class D2, template <class D> class TD, class D1>
+TD<D2> DynCast(TD<D1> source) {
+ return TD<D2>(dyn_cast<D2>(source.decl));
+}
+
+template <class D = Decl> class DeclFromParser;
+template <class D = Decl> class DeclFromUser;
+
+template <class D> class DeclFromParser : public TaggedASTDecl<D> {
+public:
+ DeclFromParser() : TaggedASTDecl<D>() {}
+ DeclFromParser(D *_decl) : TaggedASTDecl<D>(_decl) {}
+
+ DeclFromUser<D> GetOrigin(ClangASTSource &source);
+};
+
+template <class D> class DeclFromUser : public TaggedASTDecl<D> {
+public:
+ DeclFromUser() : TaggedASTDecl<D>() {}
+ DeclFromUser(D *_decl) : TaggedASTDecl<D>(_decl) {}
+
+ DeclFromParser<D> Import(ClangASTSource &source);
+};
+
+template <class D>
+DeclFromUser<D> DeclFromParser<D>::GetOrigin(ClangASTSource &source) {
+ DeclFromUser<> origin_decl;
+ source.ResolveDeclOrigin(this->decl, &origin_decl.decl, nullptr);
+ if (origin_decl.IsInvalid())
+ return DeclFromUser<D>();
+ return DeclFromUser<D>(dyn_cast<D>(origin_decl.decl));
+}
+
+template <class D>
+DeclFromParser<D> DeclFromUser<D>::Import(ClangASTSource &source) {
+ DeclFromParser<> parser_generic_decl(source.CopyDecl(this->decl));
+ if (parser_generic_decl.IsInvalid())
+ return DeclFromParser<D>();
+ return DeclFromParser<D>(dyn_cast<D>(parser_generic_decl.decl));
+}
+
+bool ClangASTSource::FindObjCMethodDeclsWithOrigin(
+ unsigned int current_id, NameSearchContext &context,
+ ObjCInterfaceDecl *original_interface_decl, const char *log_info) {
+ const DeclarationName &decl_name(context.m_decl_name);
+ clang::ASTContext *original_ctx = &original_interface_decl->getASTContext();
+
+ Selector original_selector;
+
+ if (decl_name.isObjCZeroArgSelector()) {
+ IdentifierInfo *ident = &original_ctx->Idents.get(decl_name.getAsString());
+ original_selector = original_ctx->Selectors.getSelector(0, &ident);
+ } else if (decl_name.isObjCOneArgSelector()) {
+ const std::string &decl_name_string = decl_name.getAsString();
+ std::string decl_name_string_without_colon(decl_name_string.c_str(),
+ decl_name_string.length() - 1);
+ IdentifierInfo *ident =
+ &original_ctx->Idents.get(decl_name_string_without_colon);
+ original_selector = original_ctx->Selectors.getSelector(1, &ident);
+ } else {
+ SmallVector<IdentifierInfo *, 4> idents;
+
+ clang::Selector sel = decl_name.getObjCSelector();
+
+ unsigned num_args = sel.getNumArgs();
+
+ for (unsigned i = 0; i != num_args; ++i) {
+ idents.push_back(&original_ctx->Idents.get(sel.getNameForSlot(i)));
+ }
+
+ original_selector =
+ original_ctx->Selectors.getSelector(num_args, idents.data());
+ }
+
+ DeclarationName original_decl_name(original_selector);
+
+ llvm::SmallVector<NamedDecl *, 1> methods;
+
+ ClangASTContext::GetCompleteDecl(original_ctx, original_interface_decl);
+
+ if (ObjCMethodDecl *instance_method_decl =
+ original_interface_decl->lookupInstanceMethod(original_selector)) {
+ methods.push_back(instance_method_decl);
+ } else if (ObjCMethodDecl *class_method_decl =
+ original_interface_decl->lookupClassMethod(
+ original_selector)) {
+ methods.push_back(class_method_decl);
+ }
+
+ if (methods.empty()) {
+ return false;
+ }
+
+ for (NamedDecl *named_decl : methods) {
+ if (!named_decl)
+ continue;
+
+ ObjCMethodDecl *result_method = dyn_cast<ObjCMethodDecl>(named_decl);
+
+ if (!result_method)
+ continue;
+
+ Decl *copied_decl = CopyDecl(result_method);
+
+ if (!copied_decl)
+ continue;
+
+ ObjCMethodDecl *copied_method_decl = dyn_cast<ObjCMethodDecl>(copied_decl);
+
+ if (!copied_method_decl)
+ continue;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ ASTDumper dumper((Decl *)copied_method_decl);
+ log->Printf(" CAS::FOMD[%d] found (%s) %s", current_id, log_info,
+ dumper.GetCString());
+ }
+
+ context.AddNamedDecl(copied_method_decl);
+ }
+
+ return true;
+}
+
+void ClangASTSource::FindObjCMethodDecls(NameSearchContext &context) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (HasMerger()) {
+ if (auto *interface_decl = dyn_cast<ObjCInterfaceDecl>(context.m_decl_context)) {
+ ObjCInterfaceDecl *complete_iface_decl =
+ GetCompleteObjCInterface(interface_decl);
+
+ if (complete_iface_decl && (complete_iface_decl != context.m_decl_context)) {
+ m_merger_up->ForceRecordOrigin(interface_decl, {complete_iface_decl, &complete_iface_decl->getASTContext()});
+ }
+ }
+
+ GetMergerUnchecked().FindExternalVisibleDeclsByName(context.m_decl_context,
+ context.m_decl_name);
+ return;
+ }
+
+ static unsigned int invocation_id = 0;
+ unsigned int current_id = invocation_id++;
+
+ const DeclarationName &decl_name(context.m_decl_name);
+ const DeclContext *decl_ctx(context.m_decl_context);
+
+ const ObjCInterfaceDecl *interface_decl =
+ dyn_cast<ObjCInterfaceDecl>(decl_ctx);
+
+ if (!interface_decl)
+ return;
+
+ do {
+ Decl *original_decl = nullptr;
+ ASTContext *original_ctx = nullptr;
+
+ m_ast_importer_sp->ResolveDeclOrigin(interface_decl, &original_decl,
+ &original_ctx);
+
+ if (!original_decl)
+ break;
+
+ ObjCInterfaceDecl *original_interface_decl =
+ dyn_cast<ObjCInterfaceDecl>(original_decl);
+
+ if (FindObjCMethodDeclsWithOrigin(current_id, context,
+ original_interface_decl, "at origin"))
+ return; // found it, no need to look any further
+ } while (false);
+
+ StreamString ss;
+
+ if (decl_name.isObjCZeroArgSelector()) {
+ ss.Printf("%s", decl_name.getAsString().c_str());
+ } else if (decl_name.isObjCOneArgSelector()) {
+ ss.Printf("%s", decl_name.getAsString().c_str());
+ } else {
+ clang::Selector sel = decl_name.getObjCSelector();
+
+ for (unsigned i = 0, e = sel.getNumArgs(); i != e; ++i) {
+ llvm::StringRef r = sel.getNameForSlot(i);
+ ss.Printf("%s:", r.str().c_str());
+ }
+ }
+ ss.Flush();
+
+ if (ss.GetString().contains("$__lldb"))
+ return; // we don't need any results
+
+ ConstString selector_name(ss.GetString());
+
+ if (log)
+ log->Printf("ClangASTSource::FindObjCMethodDecls[%d] on (ASTContext*)%p "
+ "for selector [%s %s]",
+ current_id, static_cast<void *>(m_ast_context),
+ interface_decl->getNameAsString().c_str(),
+ selector_name.AsCString());
+ SymbolContextList sc_list;
+
+ const bool include_symbols = false;
+ const bool include_inlines = false;
+ const bool append = false;
+
+ std::string interface_name = interface_decl->getNameAsString();
+
+ do {
+ StreamString ms;
+ ms.Printf("-[%s %s]", interface_name.c_str(), selector_name.AsCString());
+ ms.Flush();
+ ConstString instance_method_name(ms.GetString());
+
+ m_target->GetImages().FindFunctions(
+ instance_method_name, lldb::eFunctionNameTypeFull, include_symbols,
+ include_inlines, append, sc_list);
+
+ if (sc_list.GetSize())
+ break;
+
+ ms.Clear();
+ ms.Printf("+[%s %s]", interface_name.c_str(), selector_name.AsCString());
+ ms.Flush();
+ ConstString class_method_name(ms.GetString());
+
+ m_target->GetImages().FindFunctions(
+ class_method_name, lldb::eFunctionNameTypeFull, include_symbols,
+ include_inlines, append, sc_list);
+
+ if (sc_list.GetSize())
+ break;
+
+ // Fall back and check for methods in categories. If we find methods this
+ // way, we need to check that they're actually in categories on the desired
+ // class.
+
+ SymbolContextList candidate_sc_list;
+
+ m_target->GetImages().FindFunctions(
+ selector_name, lldb::eFunctionNameTypeSelector, include_symbols,
+ include_inlines, append, candidate_sc_list);
+
+ for (uint32_t ci = 0, ce = candidate_sc_list.GetSize(); ci != ce; ++ci) {
+ SymbolContext candidate_sc;
+
+ if (!candidate_sc_list.GetContextAtIndex(ci, candidate_sc))
+ continue;
+
+ if (!candidate_sc.function)
+ continue;
+
+ const char *candidate_name = candidate_sc.function->GetName().AsCString();
+
+ const char *cursor = candidate_name;
+
+ if (*cursor != '+' && *cursor != '-')
+ continue;
+
+ ++cursor;
+
+ if (*cursor != '[')
+ continue;
+
+ ++cursor;
+
+ size_t interface_len = interface_name.length();
+
+ if (strncmp(cursor, interface_name.c_str(), interface_len))
+ continue;
+
+ cursor += interface_len;
+
+ if (*cursor == ' ' || *cursor == '(')
+ sc_list.Append(candidate_sc);
+ }
+ } while (false);
+
+ if (sc_list.GetSize()) {
+ // We found a good function symbol. Use that.
+
+ for (uint32_t i = 0, e = sc_list.GetSize(); i != e; ++i) {
+ SymbolContext sc;
+
+ if (!sc_list.GetContextAtIndex(i, sc))
+ continue;
+
+ if (!sc.function)
+ continue;
+
+ CompilerDeclContext function_decl_ctx = sc.function->GetDeclContext();
+ if (!function_decl_ctx)
+ continue;
+
+ ObjCMethodDecl *method_decl =
+ ClangASTContext::DeclContextGetAsObjCMethodDecl(function_decl_ctx);
+
+ if (!method_decl)
+ continue;
+
+ ObjCInterfaceDecl *found_interface_decl =
+ method_decl->getClassInterface();
+
+ if (!found_interface_decl)
+ continue;
+
+ if (found_interface_decl->getName() == interface_decl->getName()) {
+ Decl *copied_decl = CopyDecl(method_decl);
+
+ if (!copied_decl)
+ continue;
+
+ ObjCMethodDecl *copied_method_decl =
+ dyn_cast<ObjCMethodDecl>(copied_decl);
+
+ if (!copied_method_decl)
+ continue;
+
+ if (log) {
+ ASTDumper dumper((Decl *)copied_method_decl);
+ log->Printf(" CAS::FOMD[%d] found (in symbols) %s", current_id,
+ dumper.GetCString());
+ }
+
+ context.AddNamedDecl(copied_method_decl);
+ }
+ }
+
+ return;
+ }
+
+ // Try the debug information.
+
+ do {
+ ObjCInterfaceDecl *complete_interface_decl = GetCompleteObjCInterface(
+ const_cast<ObjCInterfaceDecl *>(interface_decl));
+
+ if (!complete_interface_decl)
+ break;
+
+ // We found the complete interface. The runtime never needs to be queried
+ // in this scenario.
+
+ DeclFromUser<const ObjCInterfaceDecl> complete_iface_decl(
+ complete_interface_decl);
+
+ if (complete_interface_decl == interface_decl)
+ break; // already checked this one
+
+ if (log)
+ log->Printf("CAS::FOPD[%d] trying origin "
+ "(ObjCInterfaceDecl*)%p/(ASTContext*)%p...",
+ current_id, static_cast<void *>(complete_interface_decl),
+ static_cast<void *>(&complete_iface_decl->getASTContext()));
+
+ FindObjCMethodDeclsWithOrigin(current_id, context, complete_interface_decl,
+ "in debug info");
+
+ return;
+ } while (false);
+
+ do {
+ // Check the modules only if the debug information didn't have a complete
+ // interface.
+
+ if (ClangModulesDeclVendor *modules_decl_vendor =
+ m_target->GetClangModulesDeclVendor()) {
+ ConstString interface_name(interface_decl->getNameAsString().c_str());
+ bool append = false;
+ uint32_t max_matches = 1;
+ std::vector<clang::NamedDecl *> decls;
+
+ if (!modules_decl_vendor->FindDecls(interface_name, append, max_matches,
+ decls))
+ break;
+
+ ObjCInterfaceDecl *interface_decl_from_modules =
+ dyn_cast<ObjCInterfaceDecl>(decls[0]);
+
+ if (!interface_decl_from_modules)
+ break;
+
+ if (FindObjCMethodDeclsWithOrigin(
+ current_id, context, interface_decl_from_modules, "in modules"))
+ return;
+ }
+ } while (false);
+
+ do {
+ // Check the runtime only if the debug information didn't have a complete
+ // interface and the modules don't get us anywhere.
+
+ lldb::ProcessSP process(m_target->GetProcessSP());
+
+ if (!process)
+ break;
+
+ ObjCLanguageRuntime *language_runtime(ObjCLanguageRuntime::Get(*process));
+
+ if (!language_runtime)
+ break;
+
+ DeclVendor *decl_vendor = language_runtime->GetDeclVendor();
+
+ if (!decl_vendor)
+ break;
+
+ ConstString interface_name(interface_decl->getNameAsString().c_str());
+ bool append = false;
+ uint32_t max_matches = 1;
+ std::vector<clang::NamedDecl *> decls;
+
+ if (!decl_vendor->FindDecls(interface_name, append, max_matches, decls))
+ break;
+
+ ObjCInterfaceDecl *runtime_interface_decl =
+ dyn_cast<ObjCInterfaceDecl>(decls[0]);
+
+ if (!runtime_interface_decl)
+ break;
+
+ FindObjCMethodDeclsWithOrigin(current_id, context, runtime_interface_decl,
+ "in runtime");
+ } while (false);
+}
+
+static bool FindObjCPropertyAndIvarDeclsWithOrigin(
+ unsigned int current_id, NameSearchContext &context, ClangASTSource &source,
+ DeclFromUser<const ObjCInterfaceDecl> &origin_iface_decl) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (origin_iface_decl.IsInvalid())
+ return false;
+
+ std::string name_str = context.m_decl_name.getAsString();
+ StringRef name(name_str);
+ IdentifierInfo &name_identifier(
+ origin_iface_decl->getASTContext().Idents.get(name));
+
+ DeclFromUser<ObjCPropertyDecl> origin_property_decl(
+ origin_iface_decl->FindPropertyDeclaration(
+ &name_identifier, ObjCPropertyQueryKind::OBJC_PR_query_instance));
+
+ bool found = false;
+
+ if (origin_property_decl.IsValid()) {
+ DeclFromParser<ObjCPropertyDecl> parser_property_decl(
+ origin_property_decl.Import(source));
+ if (parser_property_decl.IsValid()) {
+ if (log) {
+ ASTDumper dumper((Decl *)parser_property_decl.decl);
+ log->Printf(" CAS::FOPD[%d] found %s", current_id,
+ dumper.GetCString());
+ }
+
+ context.AddNamedDecl(parser_property_decl.decl);
+ found = true;
+ }
+ }
+
+ DeclFromUser<ObjCIvarDecl> origin_ivar_decl(
+ origin_iface_decl->getIvarDecl(&name_identifier));
+
+ if (origin_ivar_decl.IsValid()) {
+ DeclFromParser<ObjCIvarDecl> parser_ivar_decl(
+ origin_ivar_decl.Import(source));
+ if (parser_ivar_decl.IsValid()) {
+ if (log) {
+ ASTDumper dumper((Decl *)parser_ivar_decl.decl);
+ log->Printf(" CAS::FOPD[%d] found %s", current_id,
+ dumper.GetCString());
+ }
+
+ context.AddNamedDecl(parser_ivar_decl.decl);
+ found = true;
+ }
+ }
+
+ return found;
+}
+
+void ClangASTSource::FindObjCPropertyAndIvarDecls(NameSearchContext &context) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ static unsigned int invocation_id = 0;
+ unsigned int current_id = invocation_id++;
+
+ DeclFromParser<const ObjCInterfaceDecl> parser_iface_decl(
+ cast<ObjCInterfaceDecl>(context.m_decl_context));
+ DeclFromUser<const ObjCInterfaceDecl> origin_iface_decl(
+ parser_iface_decl.GetOrigin(*this));
+
+ ConstString class_name(parser_iface_decl->getNameAsString().c_str());
+
+ if (log)
+ log->Printf("ClangASTSource::FindObjCPropertyAndIvarDecls[%d] on "
+ "(ASTContext*)%p for '%s.%s'",
+ current_id, static_cast<void *>(m_ast_context),
+ parser_iface_decl->getNameAsString().c_str(),
+ context.m_decl_name.getAsString().c_str());
+
+ if (FindObjCPropertyAndIvarDeclsWithOrigin(
+ current_id, context, *this, origin_iface_decl))
+ return;
+
+ if (log)
+ log->Printf("CAS::FOPD[%d] couldn't find the property on origin "
+ "(ObjCInterfaceDecl*)%p/(ASTContext*)%p, searching "
+ "elsewhere...",
+ current_id, static_cast<const void *>(origin_iface_decl.decl),
+ static_cast<void *>(&origin_iface_decl->getASTContext()));
+
+ SymbolContext null_sc;
+ TypeList type_list;
+
+ do {
+ ObjCInterfaceDecl *complete_interface_decl = GetCompleteObjCInterface(
+ const_cast<ObjCInterfaceDecl *>(parser_iface_decl.decl));
+
+ if (!complete_interface_decl)
+ break;
+
+ // We found the complete interface. The runtime never needs to be queried
+ // in this scenario.
+
+ DeclFromUser<const ObjCInterfaceDecl> complete_iface_decl(
+ complete_interface_decl);
+
+ if (complete_iface_decl.decl == origin_iface_decl.decl)
+ break; // already checked this one
+
+ if (log)
+ log->Printf("CAS::FOPD[%d] trying origin "
+ "(ObjCInterfaceDecl*)%p/(ASTContext*)%p...",
+ current_id,
+ static_cast<const void *>(complete_iface_decl.decl),
+ static_cast<void *>(&complete_iface_decl->getASTContext()));
+
+ FindObjCPropertyAndIvarDeclsWithOrigin(current_id, context, *this,
+ complete_iface_decl);
+
+ return;
+ } while (false);
+
+ do {
+ // Check the modules only if the debug information didn't have a complete
+ // interface.
+
+ ClangModulesDeclVendor *modules_decl_vendor =
+ m_target->GetClangModulesDeclVendor();
+
+ if (!modules_decl_vendor)
+ break;
+
+ bool append = false;
+ uint32_t max_matches = 1;
+ std::vector<clang::NamedDecl *> decls;
+
+ if (!modules_decl_vendor->FindDecls(class_name, append, max_matches, decls))
+ break;
+
+ DeclFromUser<const ObjCInterfaceDecl> interface_decl_from_modules(
+ dyn_cast<ObjCInterfaceDecl>(decls[0]));
+
+ if (!interface_decl_from_modules.IsValid())
+ break;
+
+ if (log)
+ log->Printf(
+ "CAS::FOPD[%d] trying module "
+ "(ObjCInterfaceDecl*)%p/(ASTContext*)%p...",
+ current_id,
+ static_cast<const void *>(interface_decl_from_modules.decl),
+ static_cast<void *>(&interface_decl_from_modules->getASTContext()));
+
+ if (FindObjCPropertyAndIvarDeclsWithOrigin(current_id, context, *this,
+ interface_decl_from_modules))
+ return;
+ } while (false);
+
+ do {
+ // Check the runtime only if the debug information didn't have a complete
+ // interface and nothing was in the modules.
+
+ lldb::ProcessSP process(m_target->GetProcessSP());
+
+ if (!process)
+ return;
+
+ ObjCLanguageRuntime *language_runtime(ObjCLanguageRuntime::Get(*process));
+
+ if (!language_runtime)
+ return;
+
+ DeclVendor *decl_vendor = language_runtime->GetDeclVendor();
+
+ if (!decl_vendor)
+ break;
+
+ bool append = false;
+ uint32_t max_matches = 1;
+ std::vector<clang::NamedDecl *> decls;
+
+ if (!decl_vendor->FindDecls(class_name, append, max_matches, decls))
+ break;
+
+ DeclFromUser<const ObjCInterfaceDecl> interface_decl_from_runtime(
+ dyn_cast<ObjCInterfaceDecl>(decls[0]));
+
+ if (!interface_decl_from_runtime.IsValid())
+ break;
+
+ if (log)
+ log->Printf(
+ "CAS::FOPD[%d] trying runtime "
+ "(ObjCInterfaceDecl*)%p/(ASTContext*)%p...",
+ current_id,
+ static_cast<const void *>(interface_decl_from_runtime.decl),
+ static_cast<void *>(&interface_decl_from_runtime->getASTContext()));
+
+ if (FindObjCPropertyAndIvarDeclsWithOrigin(
+ current_id, context, *this, interface_decl_from_runtime))
+ return;
+ } while (false);
+}
+
+typedef llvm::DenseMap<const FieldDecl *, uint64_t> FieldOffsetMap;
+typedef llvm::DenseMap<const CXXRecordDecl *, CharUnits> BaseOffsetMap;
+
+template <class D, class O>
+static bool ImportOffsetMap(llvm::DenseMap<const D *, O> &destination_map,
+ llvm::DenseMap<const D *, O> &source_map,
+ ClangASTSource &source) {
+ // When importing fields into a new record, clang has a hard requirement that
+ // fields be imported in field offset order. Since they are stored in a
+ // DenseMap with a pointer as the key type, this means we cannot simply
+ // iterate over the map, as the order will be non-deterministic. Instead we
+ // have to sort by the offset and then insert in sorted order.
+ typedef llvm::DenseMap<const D *, O> MapType;
+ typedef typename MapType::value_type PairType;
+ std::vector<PairType> sorted_items;
+ sorted_items.reserve(source_map.size());
+ sorted_items.assign(source_map.begin(), source_map.end());
+ llvm::sort(sorted_items.begin(), sorted_items.end(),
+ [](const PairType &lhs, const PairType &rhs) {
+ return lhs.second < rhs.second;
+ });
+
+ for (const auto &item : sorted_items) {
+ DeclFromUser<D> user_decl(const_cast<D *>(item.first));
+ DeclFromParser<D> parser_decl(user_decl.Import(source));
+ if (parser_decl.IsInvalid())
+ return false;
+ destination_map.insert(
+ std::pair<const D *, O>(parser_decl.decl, item.second));
+ }
+
+ return true;
+}
+
+template <bool IsVirtual>
+bool ExtractBaseOffsets(const ASTRecordLayout &record_layout,
+ DeclFromUser<const CXXRecordDecl> &record,
+ BaseOffsetMap &base_offsets) {
+ for (CXXRecordDecl::base_class_const_iterator
+ bi = (IsVirtual ? record->vbases_begin() : record->bases_begin()),
+ be = (IsVirtual ? record->vbases_end() : record->bases_end());
+ bi != be; ++bi) {
+ if (!IsVirtual && bi->isVirtual())
+ continue;
+
+ const clang::Type *origin_base_type = bi->getType().getTypePtr();
+ const clang::RecordType *origin_base_record_type =
+ origin_base_type->getAs<RecordType>();
+
+ if (!origin_base_record_type)
+ return false;
+
+ DeclFromUser<RecordDecl> origin_base_record(
+ origin_base_record_type->getDecl());
+
+ if (origin_base_record.IsInvalid())
+ return false;
+
+ DeclFromUser<CXXRecordDecl> origin_base_cxx_record(
+ DynCast<CXXRecordDecl>(origin_base_record));
+
+ if (origin_base_cxx_record.IsInvalid())
+ return false;
+
+ CharUnits base_offset;
+
+ if (IsVirtual)
+ base_offset =
+ record_layout.getVBaseClassOffset(origin_base_cxx_record.decl);
+ else
+ base_offset =
+ record_layout.getBaseClassOffset(origin_base_cxx_record.decl);
+
+ base_offsets.insert(std::pair<const CXXRecordDecl *, CharUnits>(
+ origin_base_cxx_record.decl, base_offset));
+ }
+
+ return true;
+}
+
+bool ClangASTSource::layoutRecordType(const RecordDecl *record, uint64_t &size,
+ uint64_t &alignment,
+ FieldOffsetMap &field_offsets,
+ BaseOffsetMap &base_offsets,
+ BaseOffsetMap &virtual_base_offsets) {
+ ClangASTMetrics::RegisterRecordLayout();
+
+ static unsigned int invocation_id = 0;
+ unsigned int current_id = invocation_id++;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log)
+ log->Printf("LayoutRecordType[%u] on (ASTContext*)%p for (RecordDecl*)%p "
+ "[name = '%s']",
+ current_id, static_cast<void *>(m_ast_context),
+ static_cast<const void *>(record),
+ record->getNameAsString().c_str());
+
+ DeclFromParser<const RecordDecl> parser_record(record);
+ DeclFromUser<const RecordDecl> origin_record(
+ parser_record.GetOrigin(*this));
+
+ if (origin_record.IsInvalid())
+ return false;
+
+ FieldOffsetMap origin_field_offsets;
+ BaseOffsetMap origin_base_offsets;
+ BaseOffsetMap origin_virtual_base_offsets;
+
+ ClangASTContext::GetCompleteDecl(
+ &origin_record->getASTContext(),
+ const_cast<RecordDecl *>(origin_record.decl));
+
+ clang::RecordDecl *definition = origin_record.decl->getDefinition();
+ if (!definition || !definition->isCompleteDefinition())
+ return false;
+
+ const ASTRecordLayout &record_layout(
+ origin_record->getASTContext().getASTRecordLayout(origin_record.decl));
+
+ int field_idx = 0, field_count = record_layout.getFieldCount();
+
+ for (RecordDecl::field_iterator fi = origin_record->field_begin(),
+ fe = origin_record->field_end();
+ fi != fe; ++fi) {
+ if (field_idx >= field_count)
+ return false; // Layout didn't go well. Bail out.
+
+ uint64_t field_offset = record_layout.getFieldOffset(field_idx);
+
+ origin_field_offsets.insert(
+ std::pair<const FieldDecl *, uint64_t>(*fi, field_offset));
+
+ field_idx++;
+ }
+
+ lldbassert(&record->getASTContext() == m_ast_context);
+
+ DeclFromUser<const CXXRecordDecl> origin_cxx_record(
+ DynCast<const CXXRecordDecl>(origin_record));
+
+ if (origin_cxx_record.IsValid()) {
+ if (!ExtractBaseOffsets<false>(record_layout, origin_cxx_record,
+ origin_base_offsets) ||
+ !ExtractBaseOffsets<true>(record_layout, origin_cxx_record,
+ origin_virtual_base_offsets))
+ return false;
+ }
+
+ if (!ImportOffsetMap(field_offsets, origin_field_offsets, *this) ||
+ !ImportOffsetMap(base_offsets, origin_base_offsets, *this) ||
+ !ImportOffsetMap(virtual_base_offsets, origin_virtual_base_offsets,
+ *this))
+ return false;
+
+ size = record_layout.getSize().getQuantity() * m_ast_context->getCharWidth();
+ alignment = record_layout.getAlignment().getQuantity() *
+ m_ast_context->getCharWidth();
+
+ if (log) {
+ log->Printf("LRT[%u] returned:", current_id);
+ log->Printf("LRT[%u] Original = (RecordDecl*)%p", current_id,
+ static_cast<const void *>(origin_record.decl));
+ log->Printf("LRT[%u] Size = %" PRId64, current_id, size);
+ log->Printf("LRT[%u] Alignment = %" PRId64, current_id, alignment);
+ log->Printf("LRT[%u] Fields:", current_id);
+ for (RecordDecl::field_iterator fi = record->field_begin(),
+ fe = record->field_end();
+ fi != fe; ++fi) {
+ log->Printf("LRT[%u] (FieldDecl*)%p, Name = '%s', Offset = %" PRId64
+ " bits",
+ current_id, static_cast<void *>(*fi),
+ fi->getNameAsString().c_str(), field_offsets[*fi]);
+ }
+ DeclFromParser<const CXXRecordDecl> parser_cxx_record =
+ DynCast<const CXXRecordDecl>(parser_record);
+ if (parser_cxx_record.IsValid()) {
+ log->Printf("LRT[%u] Bases:", current_id);
+ for (CXXRecordDecl::base_class_const_iterator
+ bi = parser_cxx_record->bases_begin(),
+ be = parser_cxx_record->bases_end();
+ bi != be; ++bi) {
+ bool is_virtual = bi->isVirtual();
+
+ QualType base_type = bi->getType();
+ const RecordType *base_record_type = base_type->getAs<RecordType>();
+ DeclFromParser<RecordDecl> base_record(base_record_type->getDecl());
+ DeclFromParser<CXXRecordDecl> base_cxx_record =
+ DynCast<CXXRecordDecl>(base_record);
+
+ log->Printf(
+ "LRT[%u] %s(CXXRecordDecl*)%p, Name = '%s', Offset = %" PRId64
+ " chars",
+ current_id, (is_virtual ? "Virtual " : ""),
+ static_cast<void *>(base_cxx_record.decl),
+ base_cxx_record.decl->getNameAsString().c_str(),
+ (is_virtual
+ ? virtual_base_offsets[base_cxx_record.decl].getQuantity()
+ : base_offsets[base_cxx_record.decl].getQuantity()));
+ }
+ } else {
+ log->Printf("LRD[%u] Not a CXXRecord, so no bases", current_id);
+ }
+ }
+
+ return true;
+}
+
+void ClangASTSource::CompleteNamespaceMap(
+ ClangASTImporter::NamespaceMapSP &namespace_map, ConstString name,
+ ClangASTImporter::NamespaceMapSP &parent_map) const {
+ static unsigned int invocation_id = 0;
+ unsigned int current_id = invocation_id++;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log) {
+ if (parent_map && parent_map->size())
+ log->Printf("CompleteNamespaceMap[%u] on (ASTContext*)%p Searching for "
+ "namespace %s in namespace %s",
+ current_id, static_cast<void *>(m_ast_context),
+ name.GetCString(),
+ parent_map->begin()->second.GetName().AsCString());
+ else
+ log->Printf("CompleteNamespaceMap[%u] on (ASTContext*)%p Searching for "
+ "namespace %s",
+ current_id, static_cast<void *>(m_ast_context),
+ name.GetCString());
+ }
+
+ if (parent_map) {
+ for (ClangASTImporter::NamespaceMap::iterator i = parent_map->begin(),
+ e = parent_map->end();
+ i != e; ++i) {
+ CompilerDeclContext found_namespace_decl;
+
+ lldb::ModuleSP module_sp = i->first;
+ CompilerDeclContext module_parent_namespace_decl = i->second;
+
+ SymbolVendor *symbol_vendor = module_sp->GetSymbolVendor();
+
+ if (!symbol_vendor)
+ continue;
+
+ found_namespace_decl =
+ symbol_vendor->FindNamespace(name, &module_parent_namespace_decl);
+
+ if (!found_namespace_decl)
+ continue;
+
+ namespace_map->push_back(std::pair<lldb::ModuleSP, CompilerDeclContext>(
+ module_sp, found_namespace_decl));
+
+ if (log)
+ log->Printf(" CMN[%u] Found namespace %s in module %s", current_id,
+ name.GetCString(),
+ module_sp->GetFileSpec().GetFilename().GetCString());
+ }
+ } else {
+ const ModuleList &target_images = m_target->GetImages();
+ std::lock_guard<std::recursive_mutex> guard(target_images.GetMutex());
+
+ CompilerDeclContext null_namespace_decl;
+
+ for (size_t i = 0, e = target_images.GetSize(); i < e; ++i) {
+ lldb::ModuleSP image = target_images.GetModuleAtIndexUnlocked(i);
+
+ if (!image)
+ continue;
+
+ CompilerDeclContext found_namespace_decl;
+
+ SymbolVendor *symbol_vendor = image->GetSymbolVendor();
+
+ if (!symbol_vendor)
+ continue;
+
+ found_namespace_decl =
+ symbol_vendor->FindNamespace(name, &null_namespace_decl);
+
+ if (!found_namespace_decl)
+ continue;
+
+ namespace_map->push_back(std::pair<lldb::ModuleSP, CompilerDeclContext>(
+ image, found_namespace_decl));
+
+ if (log)
+ log->Printf(" CMN[%u] Found namespace %s in module %s", current_id,
+ name.GetCString(),
+ image->GetFileSpec().GetFilename().GetCString());
+ }
+ }
+}
+
+NamespaceDecl *ClangASTSource::AddNamespace(
+ NameSearchContext &context,
+ ClangASTImporter::NamespaceMapSP &namespace_decls) {
+ if (!namespace_decls)
+ return nullptr;
+
+ const CompilerDeclContext &namespace_decl = namespace_decls->begin()->second;
+
+ clang::ASTContext *src_ast =
+ ClangASTContext::DeclContextGetClangASTContext(namespace_decl);
+ if (!src_ast)
+ return nullptr;
+ clang::NamespaceDecl *src_namespace_decl =
+ ClangASTContext::DeclContextGetAsNamespaceDecl(namespace_decl);
+
+ if (!src_namespace_decl)
+ return nullptr;
+
+ Decl *copied_decl = CopyDecl(src_namespace_decl);
+
+ if (!copied_decl)
+ return nullptr;
+
+ NamespaceDecl *copied_namespace_decl = dyn_cast<NamespaceDecl>(copied_decl);
+
+ if (!copied_namespace_decl)
+ return nullptr;
+
+ context.m_decls.push_back(copied_namespace_decl);
+
+ m_ast_importer_sp->RegisterNamespaceMap(copied_namespace_decl,
+ namespace_decls);
+
+ return dyn_cast<NamespaceDecl>(copied_decl);
+}
+
+clang::QualType ClangASTSource::CopyTypeWithMerger(
+ clang::ASTContext &from_context,
+ clang::ExternalASTMerger &merger,
+ clang::QualType type) {
+ if (!merger.HasImporterForOrigin(from_context)) {
+ lldbassert(0 && "Couldn't find the importer for a source context!");
+ return QualType();
+ }
+
+ if (llvm::Expected<QualType> type_or_error =
+ merger.ImporterForOrigin(from_context).Import(type)) {
+ return *type_or_error;
+ } else {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS);
+ LLDB_LOG_ERROR(log, type_or_error.takeError(), "Couldn't import type: {0}");
+ return QualType();
+ }
+}
+
+clang::Decl *ClangASTSource::CopyDecl(Decl *src_decl) {
+ clang::ASTContext &from_context = src_decl->getASTContext();
+ if (m_ast_importer_sp) {
+ return m_ast_importer_sp->CopyDecl(m_ast_context, &from_context, src_decl);
+ } else if (m_merger_up) {
+ if (!m_merger_up->HasImporterForOrigin(from_context)) {
+ lldbassert(0 && "Couldn't find the importer for a source context!");
+ return nullptr;
+ }
+
+ if (llvm::Expected<Decl *> decl_or_error =
+ m_merger_up->ImporterForOrigin(from_context).Import(src_decl)) {
+ return *decl_or_error;
+ } else {
+ Log *log =
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS);
+ LLDB_LOG_ERROR(log, decl_or_error.takeError(),
+ "Couldn't import decl: {0}");
+ return nullptr;
+ }
+ } else {
+ lldbassert(0 && "No mechanism for copying a decl!");
+ return nullptr;
+ }
+}
+
+bool ClangASTSource::ResolveDeclOrigin(const clang::Decl *decl,
+ clang::Decl **original_decl,
+ clang::ASTContext **original_ctx) {
+ if (m_ast_importer_sp) {
+ return m_ast_importer_sp->ResolveDeclOrigin(decl, original_decl,
+ original_ctx);
+ } else if (m_merger_up) {
+ return false; // Implement this correctly in ExternalASTMerger
+ } else {
+ // this can happen early enough that no ExternalASTSource is installed.
+ return false;
+ }
+}
+
+clang::ExternalASTMerger &ClangASTSource::GetMergerUnchecked() {
+ lldbassert(m_merger_up != nullptr);
+ return *m_merger_up;
+}
+
+CompilerType ClangASTSource::GuardedCopyType(const CompilerType &src_type) {
+ ClangASTContext *src_ast =
+ llvm::dyn_cast_or_null<ClangASTContext>(src_type.GetTypeSystem());
+ if (src_ast == nullptr)
+ return CompilerType();
+
+ ClangASTMetrics::RegisterLLDBImport();
+
+ SetImportInProgress(true);
+
+ QualType copied_qual_type;
+
+ if (m_ast_importer_sp) {
+ copied_qual_type =
+ m_ast_importer_sp->CopyType(m_ast_context, src_ast->getASTContext(),
+ ClangUtil::GetQualType(src_type));
+ } else if (m_merger_up) {
+ copied_qual_type =
+ CopyTypeWithMerger(*src_ast->getASTContext(), *m_merger_up,
+ ClangUtil::GetQualType(src_type));
+ } else {
+ lldbassert(0 && "No mechanism for copying a type!");
+ return CompilerType();
+ }
+
+ SetImportInProgress(false);
+
+ if (copied_qual_type.getAsOpaquePtr() &&
+ copied_qual_type->getCanonicalTypeInternal().isNull())
+ // this shouldn't happen, but we're hardening because the AST importer
+ // seems to be generating bad types on occasion.
+ return CompilerType();
+
+ return CompilerType(m_ast_context, copied_qual_type);
+}
+
+clang::NamedDecl *NameSearchContext::AddVarDecl(const CompilerType &type) {
+ assert(type && "Type for variable must be valid!");
+
+ if (!type.IsValid())
+ return nullptr;
+
+ ClangASTContext *lldb_ast =
+ llvm::dyn_cast<ClangASTContext>(type.GetTypeSystem());
+ if (!lldb_ast)
+ return nullptr;
+
+ IdentifierInfo *ii = m_decl_name.getAsIdentifierInfo();
+
+ clang::ASTContext *ast = lldb_ast->getASTContext();
+
+ clang::NamedDecl *Decl = VarDecl::Create(
+ *ast, const_cast<DeclContext *>(m_decl_context), SourceLocation(),
+ SourceLocation(), ii, ClangUtil::GetQualType(type), nullptr, SC_Static);
+ m_decls.push_back(Decl);
+
+ return Decl;
+}
+
+clang::NamedDecl *NameSearchContext::AddFunDecl(const CompilerType &type,
+ bool extern_c) {
+ assert(type && "Type for variable must be valid!");
+
+ if (!type.IsValid())
+ return nullptr;
+
+ if (m_function_types.count(type))
+ return nullptr;
+
+ ClangASTContext *lldb_ast =
+ llvm::dyn_cast<ClangASTContext>(type.GetTypeSystem());
+ if (!lldb_ast)
+ return nullptr;
+
+ m_function_types.insert(type);
+
+ QualType qual_type(ClangUtil::GetQualType(type));
+
+ clang::ASTContext *ast = lldb_ast->getASTContext();
+
+ const bool isInlineSpecified = false;
+ const bool hasWrittenPrototype = true;
+ const bool isConstexprSpecified = false;
+
+ clang::DeclContext *context = const_cast<DeclContext *>(m_decl_context);
+
+ if (extern_c) {
+ context = LinkageSpecDecl::Create(
+ *ast, context, SourceLocation(), SourceLocation(),
+ clang::LinkageSpecDecl::LanguageIDs::lang_c, false);
+ }
+
+ // Pass the identifier info for functions the decl_name is needed for
+ // operators
+ clang::DeclarationName decl_name =
+ m_decl_name.getNameKind() == DeclarationName::Identifier
+ ? m_decl_name.getAsIdentifierInfo()
+ : m_decl_name;
+
+ clang::FunctionDecl *func_decl = FunctionDecl::Create(
+ *ast, context, SourceLocation(), SourceLocation(), decl_name, qual_type,
+ nullptr, SC_Extern, isInlineSpecified, hasWrittenPrototype,
+ isConstexprSpecified ? CSK_constexpr : CSK_unspecified);
+
+ // We have to do more than just synthesize the FunctionDecl. We have to
+ // synthesize ParmVarDecls for all of the FunctionDecl's arguments. To do
+ // this, we raid the function's FunctionProtoType for types.
+
+ const FunctionProtoType *func_proto_type =
+ qual_type.getTypePtr()->getAs<FunctionProtoType>();
+
+ if (func_proto_type) {
+ unsigned NumArgs = func_proto_type->getNumParams();
+ unsigned ArgIndex;
+
+ SmallVector<ParmVarDecl *, 5> parm_var_decls;
+
+ for (ArgIndex = 0; ArgIndex < NumArgs; ++ArgIndex) {
+ QualType arg_qual_type(func_proto_type->getParamType(ArgIndex));
+
+ parm_var_decls.push_back(
+ ParmVarDecl::Create(*ast, const_cast<DeclContext *>(context),
+ SourceLocation(), SourceLocation(), nullptr,
+ arg_qual_type, nullptr, SC_Static, nullptr));
+ }
+
+ func_decl->setParams(ArrayRef<ParmVarDecl *>(parm_var_decls));
+ } else {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log)
+ log->Printf("Function type wasn't a FunctionProtoType");
+ }
+
+ // If this is an operator (e.g. operator new or operator==), only insert the
+ // declaration we inferred from the symbol if we can provide the correct
+ // number of arguments. We shouldn't really inject random decl(s) for
+ // functions that are analyzed semantically in a special way, otherwise we
+ // will crash in clang.
+ clang::OverloadedOperatorKind op_kind = clang::NUM_OVERLOADED_OPERATORS;
+ if (func_proto_type &&
+ ClangASTContext::IsOperator(decl_name.getAsString().c_str(), op_kind)) {
+ if (!ClangASTContext::CheckOverloadedOperatorKindParameterCount(
+ false, op_kind, func_proto_type->getNumParams()))
+ return nullptr;
+ }
+ m_decls.push_back(func_decl);
+
+ return func_decl;
+}
+
+clang::NamedDecl *NameSearchContext::AddGenericFunDecl() {
+ FunctionProtoType::ExtProtoInfo proto_info;
+
+ proto_info.Variadic = true;
+
+ QualType generic_function_type(m_ast_source.m_ast_context->getFunctionType(
+ m_ast_source.m_ast_context->UnknownAnyTy, // result
+ ArrayRef<QualType>(), // argument types
+ proto_info));
+
+ return AddFunDecl(
+ CompilerType(m_ast_source.m_ast_context, generic_function_type), true);
+}
+
+clang::NamedDecl *
+NameSearchContext::AddTypeDecl(const CompilerType &clang_type) {
+ if (ClangUtil::IsClangType(clang_type)) {
+ QualType qual_type = ClangUtil::GetQualType(clang_type);
+
+ if (const TypedefType *typedef_type =
+ llvm::dyn_cast<TypedefType>(qual_type)) {
+ TypedefNameDecl *typedef_name_decl = typedef_type->getDecl();
+
+ m_decls.push_back(typedef_name_decl);
+
+ return (NamedDecl *)typedef_name_decl;
+ } else if (const TagType *tag_type = qual_type->getAs<TagType>()) {
+ TagDecl *tag_decl = tag_type->getDecl();
+
+ m_decls.push_back(tag_decl);
+
+ return tag_decl;
+ } else if (const ObjCObjectType *objc_object_type =
+ qual_type->getAs<ObjCObjectType>()) {
+ ObjCInterfaceDecl *interface_decl = objc_object_type->getInterface();
+
+ m_decls.push_back((NamedDecl *)interface_decl);
+
+ return (NamedDecl *)interface_decl;
+ }
+ }
+ return nullptr;
+}
+
+void NameSearchContext::AddLookupResult(clang::DeclContextLookupResult result) {
+ for (clang::NamedDecl *decl : result)
+ m_decls.push_back(decl);
+}
+
+void NameSearchContext::AddNamedDecl(clang::NamedDecl *decl) {
+ m_decls.push_back(decl);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h
new file mode 100644
index 000000000000..7a8bacf48a8f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h
@@ -0,0 +1,528 @@
+//===-- ClangASTSource.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 liblldb_ClangASTSource_h_
+#define liblldb_ClangASTSource_h_
+
+#include <set>
+
+#include "lldb/Symbol/ClangASTImporter.h"
+#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Target/Target.h"
+#include "clang/AST/ExternalASTMerger.h"
+#include "clang/Basic/IdentifierTable.h"
+
+#include "llvm/ADT/SmallSet.h"
+
+namespace lldb_private {
+
+/// \class ClangASTSource ClangASTSource.h "lldb/Expression/ClangASTSource.h"
+/// Provider for named objects defined in the debug info for Clang
+///
+/// As Clang parses an expression, it may encounter names that are not defined
+/// inside the expression, including variables, functions, and types. Clang
+/// knows the name it is looking for, but nothing else. The ExternalSemaSource
+/// class provides Decls (VarDecl, FunDecl, TypeDecl) to Clang for these
+/// names, consulting the ClangExpressionDeclMap to do the actual lookups.
+class ClangASTSource : public ClangExternalASTSourceCommon,
+ public ClangASTImporter::MapCompleter {
+public:
+ /// Constructor
+ ///
+ /// Initializes class variables.
+ ///
+ /// \param[in] target
+ /// A reference to the target containing debug information to use.
+ ClangASTSource(const lldb::TargetSP &target);
+
+ /// Destructor
+ ~ClangASTSource() override;
+
+ /// Interface stubs.
+ clang::Decl *GetExternalDecl(uint32_t) override { return nullptr; }
+ clang::Stmt *GetExternalDeclStmt(uint64_t) override { return nullptr; }
+ clang::Selector GetExternalSelector(uint32_t) override {
+ return clang::Selector();
+ }
+ uint32_t GetNumExternalSelectors() override { return 0; }
+ clang::CXXBaseSpecifier *
+ GetExternalCXXBaseSpecifiers(uint64_t Offset) override {
+ return nullptr;
+ }
+ void MaterializeVisibleDecls(const clang::DeclContext *DC) { return; }
+
+ void InstallASTContext(clang::ASTContext &ast_context,
+ clang::FileManager &file_manager,
+ bool is_shared_context = false);
+
+ //
+ // APIs for ExternalASTSource
+ //
+
+ /// Look up all Decls that match a particular name. Only handles
+ /// Identifiers and DeclContexts that are either NamespaceDecls or
+ /// TranslationUnitDecls. Calls SetExternalVisibleDeclsForName with the
+ /// result.
+ ///
+ /// The work for this function is done by
+ /// void FindExternalVisibleDecls (NameSearchContext &);
+ ///
+ /// \param[in] DC
+ /// The DeclContext to register the found Decls in.
+ ///
+ /// \param[in] Name
+ /// The name to find entries for.
+ ///
+ /// \return
+ /// Whatever SetExternalVisibleDeclsForName returns.
+ bool FindExternalVisibleDeclsByName(const clang::DeclContext *DC,
+ clang::DeclarationName Name) override;
+
+ /// Enumerate all Decls in a given lexical context.
+ ///
+ /// \param[in] DC
+ /// The DeclContext being searched.
+ ///
+ /// \param[in] isKindWeWant
+ /// A callback function that returns true given the
+ /// DeclKinds of desired Decls, and false otherwise.
+ ///
+ /// \param[in] Decls
+ /// A vector that is filled in with matching Decls.
+ void FindExternalLexicalDecls(
+ const clang::DeclContext *DC,
+ llvm::function_ref<bool(clang::Decl::Kind)> IsKindWeWant,
+ llvm::SmallVectorImpl<clang::Decl *> &Decls) override;
+
+ /// Specify the layout of the contents of a RecordDecl.
+ ///
+ /// \param[in] Record
+ /// The record (in the parser's AST context) that needs to be
+ /// laid out.
+ ///
+ /// \param[out] Size
+ /// The total size of the record in bits.
+ ///
+ /// \param[out] Alignment
+ /// The alignment of the record in bits.
+ ///
+ /// \param[in] FieldOffsets
+ /// A map that must be populated with pairs of the record's
+ /// fields (in the parser's AST context) and their offsets
+ /// (measured in bits).
+ ///
+ /// \param[in] BaseOffsets
+ /// A map that must be populated with pairs of the record's
+ /// C++ concrete base classes (in the parser's AST context,
+ /// and only if the record is a CXXRecordDecl and has base
+ /// classes) and their offsets (measured in bytes).
+ ///
+ /// \param[in] VirtualBaseOffsets
+ /// A map that must be populated with pairs of the record's
+ /// C++ virtual base classes (in the parser's AST context,
+ /// and only if the record is a CXXRecordDecl and has base
+ /// classes) and their offsets (measured in bytes).
+ ///
+ /// \return
+ /// True <=> the layout is valid.
+ bool layoutRecordType(
+ const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment,
+ llvm::DenseMap<const clang::FieldDecl *, uint64_t> &FieldOffsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &BaseOffsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &VirtualBaseOffsets) override;
+
+ /// Complete a TagDecl.
+ ///
+ /// \param[in] Tag
+ /// The Decl to be completed in place.
+ void CompleteType(clang::TagDecl *Tag) override;
+
+ /// Complete an ObjCInterfaceDecl.
+ ///
+ /// \param[in] Class
+ /// The Decl to be completed in place.
+ void CompleteType(clang::ObjCInterfaceDecl *Class) override;
+
+ /// Called on entering a translation unit. Tells Clang by calling
+ /// setHasExternalVisibleStorage() and setHasExternalLexicalStorage() that
+ /// this object has something to say about undefined names.
+ ///
+ /// \param[in] ASTConsumer
+ /// Unused.
+ void StartTranslationUnit(clang::ASTConsumer *Consumer) override;
+
+ //
+ // APIs for NamespaceMapCompleter
+ //
+
+ /// Look up the modules containing a given namespace and put the appropriate
+ /// entries in the namespace map.
+ ///
+ /// \param[in] namespace_map
+ /// The map to be completed.
+ ///
+ /// \param[in] name
+ /// The name of the namespace to be found.
+ ///
+ /// \param[in] parent_map
+ /// The map for the namespace's parent namespace, if there is
+ /// one.
+ void CompleteNamespaceMap(
+ ClangASTImporter::NamespaceMapSP &namespace_map, ConstString name,
+ ClangASTImporter::NamespaceMapSP &parent_map) const override;
+
+ //
+ // Helper APIs
+ //
+
+ clang::NamespaceDecl *
+ AddNamespace(NameSearchContext &context,
+ ClangASTImporter::NamespaceMapSP &namespace_decls);
+
+ /// The worker function for FindExternalVisibleDeclsByName.
+ ///
+ /// \param[in] context
+ /// The NameSearchContext to use when filing results.
+ virtual void FindExternalVisibleDecls(NameSearchContext &context);
+
+ clang::Sema *getSema();
+
+ void SetImportInProgress(bool import_in_progress) {
+ m_import_in_progress = import_in_progress;
+ }
+ bool GetImportInProgress() { return m_import_in_progress; }
+
+ void SetLookupsEnabled(bool lookups_enabled) {
+ m_lookups_enabled = lookups_enabled;
+ }
+ bool GetLookupsEnabled() { return m_lookups_enabled; }
+
+ /// \class ClangASTSourceProxy ClangASTSource.h
+ /// "lldb/Expression/ClangASTSource.h" Proxy for ClangASTSource
+ ///
+ /// Clang AST contexts like to own their AST sources, so this is a state-
+ /// free proxy object.
+ class ClangASTSourceProxy : public ClangExternalASTSourceCommon {
+ public:
+ ClangASTSourceProxy(ClangASTSource &original) : m_original(original) {}
+
+ bool FindExternalVisibleDeclsByName(const clang::DeclContext *DC,
+ clang::DeclarationName Name) override {
+ return m_original.FindExternalVisibleDeclsByName(DC, Name);
+ }
+
+ void FindExternalLexicalDecls(
+ const clang::DeclContext *DC,
+ llvm::function_ref<bool(clang::Decl::Kind)> IsKindWeWant,
+ llvm::SmallVectorImpl<clang::Decl *> &Decls) override {
+ return m_original.FindExternalLexicalDecls(DC, IsKindWeWant, Decls);
+ }
+
+ void CompleteType(clang::TagDecl *Tag) override {
+ return m_original.CompleteType(Tag);
+ }
+
+ void CompleteType(clang::ObjCInterfaceDecl *Class) override {
+ return m_original.CompleteType(Class);
+ }
+
+ bool layoutRecordType(
+ const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment,
+ llvm::DenseMap<const clang::FieldDecl *, uint64_t> &FieldOffsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &BaseOffsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &VirtualBaseOffsets) override {
+ return m_original.layoutRecordType(Record, Size, Alignment, FieldOffsets,
+ BaseOffsets, VirtualBaseOffsets);
+ }
+
+ void StartTranslationUnit(clang::ASTConsumer *Consumer) override {
+ return m_original.StartTranslationUnit(Consumer);
+ }
+
+ ClangASTMetadata *GetMetadata(const void *object) {
+ return m_original.GetMetadata(object);
+ }
+
+ void SetMetadata(const void *object, ClangASTMetadata &metadata) {
+ return m_original.SetMetadata(object, metadata);
+ }
+
+ bool HasMetadata(const void *object) {
+ return m_original.HasMetadata(object);
+ }
+
+ private:
+ ClangASTSource &m_original;
+ };
+
+ clang::ExternalASTSource *CreateProxy() {
+ return new ClangASTSourceProxy(*this);
+ }
+
+protected:
+ /// Look for the complete version of an Objective-C interface, and return it
+ /// if found.
+ ///
+ /// \param[in] interface_decl
+ /// An ObjCInterfaceDecl that may not be the complete one.
+ ///
+ /// \return
+ /// NULL if the complete interface couldn't be found;
+ /// the complete interface otherwise.
+ clang::ObjCInterfaceDecl *
+ GetCompleteObjCInterface(const clang::ObjCInterfaceDecl *interface_decl);
+
+ /// Find all entities matching a given name in a given module, using a
+ /// NameSearchContext to make Decls for them.
+ ///
+ /// \param[in] context
+ /// The NameSearchContext that can construct Decls for this name.
+ ///
+ /// \param[in] module
+ /// If non-NULL, the module to query.
+ ///
+ /// \param[in] namespace_decl
+ /// If valid and module is non-NULL, the parent namespace.
+ ///
+ /// \param[in] current_id
+ /// The ID for the current FindExternalVisibleDecls invocation,
+ /// for logging purposes.
+ void FindExternalVisibleDecls(NameSearchContext &context,
+ lldb::ModuleSP module,
+ CompilerDeclContext &namespace_decl,
+ unsigned int current_id);
+
+ /// Find all Objective-C methods matching a given selector.
+ ///
+ /// \param[in] context
+ /// The NameSearchContext that can construct Decls for this name.
+ /// Its m_decl_name contains the selector and its m_decl_context
+ /// is the containing object.
+ void FindObjCMethodDecls(NameSearchContext &context);
+
+ /// Find all Objective-C properties and ivars with a given name.
+ ///
+ /// \param[in] context
+ /// The NameSearchContext that can construct Decls for this name.
+ /// Its m_decl_name contains the name and its m_decl_context
+ /// is the containing object.
+ void FindObjCPropertyAndIvarDecls(NameSearchContext &context);
+
+ /// A wrapper for ClangASTContext::CopyType that sets a flag that
+ /// indicates that we should not respond to queries during import.
+ ///
+ /// \param[in] dest_context
+ /// The target AST context, typically the parser's AST context.
+ ///
+ /// \param[in] source_context
+ /// The source AST context, typically the AST context of whatever
+ /// symbol file the type was found in.
+ ///
+ /// \param[in] src_type
+ /// The source type.
+ ///
+ /// \return
+ /// The imported type.
+ CompilerType GuardedCopyType(const CompilerType &src_type);
+
+public:
+ /// Returns true if a name should be ignored by name lookup.
+ ///
+ /// \param[in] name
+ /// The name to be considered.
+ ///
+ /// \param[in] ignore_all_dollar_nmmes
+ /// True if $-names of all sorts should be ignored.
+ ///
+ /// \return
+ /// True if the name is one of a class of names that are ignored by
+ /// global lookup for performance reasons.
+ bool IgnoreName(const ConstString name, bool ignore_all_dollar_names);
+
+public:
+ /// Copies a single Decl into the parser's AST context.
+ ///
+ /// \param[in] src_decl
+ /// The Decl to copy.
+ ///
+ /// \return
+ /// A copy of the Decl in m_ast_context, or NULL if the copy failed.
+ clang::Decl *CopyDecl(clang::Decl *src_decl);
+
+ /// Copies a single Type to the target of the given ExternalASTMerger.
+ ///
+ /// \param[in] src_context
+ /// The ASTContext containing the type.
+ ///
+ /// \param[in] merger
+ /// The merger to use. This isn't just *m_merger_up because it might be
+ /// the persistent AST context's merger.
+ ///
+ /// \param[in] type
+ /// The type to copy.
+ ///
+ /// \return
+ /// A copy of the Type in the merger's target context.
+ clang::QualType CopyTypeWithMerger(clang::ASTContext &src_context,
+ clang::ExternalASTMerger &merger,
+ clang::QualType type);
+
+ /// Determined the origin of a single Decl, if it can be found.
+ ///
+ /// \param[in] decl
+ /// The Decl whose origin is to be found.
+ ///
+ /// \param[out] original_decl
+ /// A pointer whose target is filled in with the original Decl.
+ ///
+ /// \param[in] original_ctx
+ /// A pointer whose target is filled in with the original's ASTContext.
+ ///
+ /// \return
+ /// True if lookup succeeded; false otherwise.
+ bool ResolveDeclOrigin(const clang::Decl *decl, clang::Decl **original_decl,
+ clang::ASTContext **original_ctx);
+
+ /// Returns m_merger_up. Only call this if the target is configured to use
+ /// modern lookup,
+ clang::ExternalASTMerger &GetMergerUnchecked();
+
+ /// Returns true if there is a merger. This only occurs if the target is
+ /// using modern lookup.
+ bool HasMerger() { return (bool)m_merger_up; }
+
+protected:
+ bool FindObjCMethodDeclsWithOrigin(
+ unsigned int current_id, NameSearchContext &context,
+ clang::ObjCInterfaceDecl *original_interface_decl, const char *log_info);
+
+ friend struct NameSearchContext;
+
+ bool m_import_in_progress;
+ bool m_lookups_enabled;
+
+ const lldb::TargetSP
+ m_target; ///< The target to use in finding variables and types.
+ clang::ASTContext
+ *m_ast_context; ///< The AST context requests are coming in for.
+ clang::FileManager
+ *m_file_manager; ///< The file manager paired with the AST context.
+ lldb::ClangASTImporterSP m_ast_importer_sp; ///< The target's AST importer.
+ std::unique_ptr<clang::ExternalASTMerger> m_merger_up;
+ ///< The ExternalASTMerger for this parse.
+ std::set<const clang::Decl *> m_active_lexical_decls;
+ std::set<const char *> m_active_lookups;
+};
+
+/// \class NameSearchContext ClangASTSource.h
+/// "lldb/Expression/ClangASTSource.h" Container for all objects relevant to a
+/// single name lookup
+///
+/// LLDB needs to create Decls for entities it finds. This class communicates
+/// what name is being searched for and provides helper functions to construct
+/// Decls given appropriate type information.
+struct NameSearchContext {
+ ClangASTSource &m_ast_source; ///< The AST source making the request
+ llvm::SmallVectorImpl<clang::NamedDecl *>
+ &m_decls; ///< The list of declarations already constructed
+ ClangASTImporter::NamespaceMapSP m_namespace_map; ///< The mapping of all
+ ///namespaces found for this
+ ///request back to their
+ ///modules
+ const clang::DeclarationName &m_decl_name; ///< The name being looked for
+ const clang::DeclContext
+ *m_decl_context; ///< The DeclContext to put declarations into
+ llvm::SmallSet<CompilerType, 5> m_function_types; ///< All the types of
+ ///functions that have been
+ ///reported, so we don't
+ ///report conflicts
+
+ struct {
+ bool variable : 1;
+ bool function_with_type_info : 1;
+ bool function : 1;
+ bool local_vars_nsp : 1;
+ bool type : 1;
+ } m_found;
+
+ /// Constructor
+ ///
+ /// Initializes class variables.
+ ///
+ /// \param[in] astSource
+ /// A reference to the AST source making a request.
+ ///
+ /// \param[in] decls
+ /// A reference to a list into which new Decls will be placed. This
+ /// list is typically empty when the function is called.
+ ///
+ /// \param[in] name
+ /// The name being searched for (always an Identifier).
+ ///
+ /// \param[in] dc
+ /// The DeclContext to register Decls in.
+ NameSearchContext(ClangASTSource &astSource,
+ llvm::SmallVectorImpl<clang::NamedDecl *> &decls,
+ clang::DeclarationName &name, const clang::DeclContext *dc)
+ : m_ast_source(astSource), m_decls(decls), m_decl_name(name),
+ m_decl_context(dc) {
+ memset(&m_found, 0, sizeof(m_found));
+ }
+
+ /// Create a VarDecl with the name being searched for and the provided type
+ /// and register it in the right places.
+ ///
+ /// \param[in] type
+ /// The opaque QualType for the VarDecl being registered.
+ clang::NamedDecl *AddVarDecl(const CompilerType &type);
+
+ /// Create a FunDecl with the name being searched for and the provided type
+ /// and register it in the right places.
+ ///
+ /// \param[in] type
+ /// The opaque QualType for the FunDecl being registered.
+ ///
+ /// \param[in] extern_c
+ /// If true, build an extern "C" linkage specification for this.
+ clang::NamedDecl *AddFunDecl(const CompilerType &type, bool extern_c = false);
+
+ /// Create a FunDecl with the name being searched for and generic type (i.e.
+ /// intptr_t NAME_GOES_HERE(...)) and register it in the right places.
+ clang::NamedDecl *AddGenericFunDecl();
+
+ /// Create a TypeDecl with the name being searched for and the provided type
+ /// and register it in the right places.
+ ///
+ /// \param[in] compiler_type
+ /// The opaque QualType for the TypeDecl being registered.
+ clang::NamedDecl *AddTypeDecl(const CompilerType &compiler_type);
+
+ /// Add Decls from the provided DeclContextLookupResult to the list of
+ /// results.
+ ///
+ /// \param[in] result
+ /// The DeclContextLookupResult, usually returned as the result
+ /// of querying a DeclContext.
+ void AddLookupResult(clang::DeclContextLookupResult result);
+
+ /// Add a NamedDecl to the list of results.
+ ///
+ /// \param[in] decl
+ /// The NamedDecl, usually returned as the result
+ /// of querying a DeclContext.
+ void AddNamedDecl(clang::NamedDecl *decl);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ClangASTSource_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangDiagnostic.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangDiagnostic.h
new file mode 100644
index 000000000000..db50c2aa3e90
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangDiagnostic.h
@@ -0,0 +1,49 @@
+//===-- ClangDiagnostic.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_ClangDiagnostic_h
+#define lldb_ClangDiagnostic_h
+
+#include <vector>
+
+#include "clang/Basic/Diagnostic.h"
+
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-types.h"
+
+#include "lldb/Expression/DiagnosticManager.h"
+
+namespace lldb_private {
+
+class ClangDiagnostic : public Diagnostic {
+public:
+ typedef std::vector<clang::FixItHint> FixItList;
+
+ static inline bool classof(const ClangDiagnostic *) { return true; }
+ static inline bool classof(const Diagnostic *diag) {
+ return diag->getKind() == eDiagnosticOriginClang;
+ }
+
+ ClangDiagnostic(const char *message, DiagnosticSeverity severity,
+ uint32_t compiler_id)
+ : Diagnostic(message, severity, eDiagnosticOriginClang, compiler_id) {}
+
+ ~ClangDiagnostic() override = default;
+
+ bool HasFixIts() const override { return !m_fixit_vec.empty(); }
+
+ void AddFixitHint(const clang::FixItHint &fixit) {
+ m_fixit_vec.push_back(fixit);
+ }
+
+ const FixItList &FixIts() const { return m_fixit_vec; }
+ FixItList m_fixit_vec;
+};
+
+} // namespace lldb_private
+#endif /* lldb_ClangDiagnostic_h */
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
new file mode 100644
index 000000000000..a49a7029e0d2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
@@ -0,0 +1,2254 @@
+//===-- ClangExpressionDeclMap.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 "ClangExpressionDeclMap.h"
+
+#include "ASTDumper.h"
+#include "ClangASTSource.h"
+#include "ClangModulesDeclVendor.h"
+#include "ClangPersistentVariables.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/Expression/Materializer.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/CompilerDecl.h"
+#include "lldb/Symbol/CompilerDeclContext.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-private.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTImporter.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclarationName.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+
+#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
+#include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h"
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace clang;
+
+namespace {
+const char *g_lldb_local_vars_namespace_cstr = "$__lldb_local_vars";
+} // anonymous namespace
+
+ClangExpressionDeclMap::ClangExpressionDeclMap(
+ bool keep_result_in_memory,
+ Materializer::PersistentVariableDelegate *result_delegate,
+ ExecutionContext &exe_ctx, ValueObject *ctx_obj)
+ : ClangASTSource(exe_ctx.GetTargetSP()), m_found_entities(),
+ m_struct_members(), m_keep_result_in_memory(keep_result_in_memory),
+ m_result_delegate(result_delegate), m_ctx_obj(ctx_obj), m_parser_vars(),
+ m_struct_vars() {
+ EnableStructVars();
+}
+
+ClangExpressionDeclMap::~ClangExpressionDeclMap() {
+ // Note: The model is now that the parser's AST context and all associated
+ // data does not vanish until the expression has been executed. This means
+ // that valuable lookup data (like namespaces) doesn't vanish, but
+
+ DidParse();
+ DisableStructVars();
+}
+
+bool ClangExpressionDeclMap::WillParse(ExecutionContext &exe_ctx,
+ Materializer *materializer) {
+ ClangASTMetrics::ClearLocalCounters();
+
+ EnableParserVars();
+ m_parser_vars->m_exe_ctx = exe_ctx;
+
+ Target *target = exe_ctx.GetTargetPtr();
+ if (exe_ctx.GetFramePtr())
+ m_parser_vars->m_sym_ctx =
+ exe_ctx.GetFramePtr()->GetSymbolContext(lldb::eSymbolContextEverything);
+ else if (exe_ctx.GetThreadPtr() &&
+ exe_ctx.GetThreadPtr()->GetStackFrameAtIndex(0))
+ m_parser_vars->m_sym_ctx =
+ exe_ctx.GetThreadPtr()->GetStackFrameAtIndex(0)->GetSymbolContext(
+ lldb::eSymbolContextEverything);
+ else if (exe_ctx.GetProcessPtr()) {
+ m_parser_vars->m_sym_ctx.Clear(true);
+ m_parser_vars->m_sym_ctx.target_sp = exe_ctx.GetTargetSP();
+ } else if (target) {
+ m_parser_vars->m_sym_ctx.Clear(true);
+ m_parser_vars->m_sym_ctx.target_sp = exe_ctx.GetTargetSP();
+ }
+
+ if (target) {
+ m_parser_vars->m_persistent_vars = llvm::cast<ClangPersistentVariables>(
+ target->GetPersistentExpressionStateForLanguage(eLanguageTypeC));
+
+ if (!target->GetScratchClangASTContext())
+ return false;
+ }
+
+ m_parser_vars->m_target_info = GetTargetInfo();
+ m_parser_vars->m_materializer = materializer;
+
+ return true;
+}
+
+void ClangExpressionDeclMap::InstallCodeGenerator(
+ clang::ASTConsumer *code_gen) {
+ assert(m_parser_vars);
+ m_parser_vars->m_code_gen = code_gen;
+}
+
+void ClangExpressionDeclMap::DidParse() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log)
+ ClangASTMetrics::DumpCounters(log);
+
+ if (m_parser_vars) {
+ for (size_t entity_index = 0, num_entities = m_found_entities.GetSize();
+ entity_index < num_entities; ++entity_index) {
+ ExpressionVariableSP var_sp(
+ m_found_entities.GetVariableAtIndex(entity_index));
+ if (var_sp)
+ llvm::cast<ClangExpressionVariable>(var_sp.get())
+ ->DisableParserVars(GetParserID());
+ }
+
+ for (size_t pvar_index = 0,
+ num_pvars = m_parser_vars->m_persistent_vars->GetSize();
+ pvar_index < num_pvars; ++pvar_index) {
+ ExpressionVariableSP pvar_sp(
+ m_parser_vars->m_persistent_vars->GetVariableAtIndex(pvar_index));
+ if (ClangExpressionVariable *clang_var =
+ llvm::dyn_cast<ClangExpressionVariable>(pvar_sp.get()))
+ clang_var->DisableParserVars(GetParserID());
+ }
+
+ DisableParserVars();
+ }
+}
+
+// Interface for IRForTarget
+
+ClangExpressionDeclMap::TargetInfo ClangExpressionDeclMap::GetTargetInfo() {
+ assert(m_parser_vars.get());
+
+ TargetInfo ret;
+
+ ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx;
+
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process) {
+ ret.byte_order = process->GetByteOrder();
+ ret.address_byte_size = process->GetAddressByteSize();
+ } else {
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ ret.byte_order = target->GetArchitecture().GetByteOrder();
+ ret.address_byte_size = target->GetArchitecture().GetAddressByteSize();
+ }
+ }
+
+ return ret;
+}
+
+namespace {
+/// This class walks an AST and ensures that all DeclContexts defined inside the
+/// current source file are properly complete.
+///
+/// This is used to ensure that persistent types defined in the current source
+/// file migrate completely to the persistent AST context before they are
+/// reused. If that didn't happen, it would be impoossible to complete them
+/// because their origin would be gone.
+///
+/// The stragtegy used by this class is to check the SourceLocation (to be
+/// specific, the FileID) and see if it's the FileID for the current expression.
+/// Alternate strategies could include checking whether an ExternalASTMerger,
+/// set up to not have the current context as a source, can find an original for
+/// the type.
+class Completer : public clang::RecursiveASTVisitor<Completer> {
+private:
+ clang::ASTImporter &m_exporter; /// Used to import Decl contents
+ clang::FileID m_file; /// The file that's going away
+ llvm::DenseSet<clang::Decl *> m_completed; /// Visited Decls, to avoid cycles
+
+ bool ImportAndCheckCompletable(clang::Decl *decl) {
+ (void)m_exporter.Import(decl);
+ if (m_completed.count(decl))
+ return false;
+ if (!llvm::isa<DeclContext>(decl))
+ return false;
+ const clang::SourceLocation loc = decl->getLocation();
+ if (!loc.isValid())
+ return false;
+ const clang::FileID file =
+ m_exporter.getFromContext().getSourceManager().getFileID(loc);
+ if (file != m_file)
+ return false;
+ // We are assuming the Decl was parsed in this very expression, so it
+ // should not have external storage.
+ lldbassert(!llvm::cast<DeclContext>(decl)->hasExternalLexicalStorage());
+ return true;
+ }
+
+ void Complete(clang::Decl *decl) {
+ m_completed.insert(decl);
+ auto *decl_context = llvm::cast<DeclContext>(decl);
+ (void)m_exporter.Import(decl);
+ m_exporter.CompleteDecl(decl);
+ for (Decl *child : decl_context->decls())
+ if (ImportAndCheckCompletable(child))
+ Complete(child);
+ }
+
+ void MaybeComplete(clang::Decl *decl) {
+ if (ImportAndCheckCompletable(decl))
+ Complete(decl);
+ }
+
+public:
+ Completer(clang::ASTImporter &exporter, clang::FileID file)
+ : m_exporter(exporter), m_file(file) {}
+
+ // Implements the RecursiveASTVisitor's core API. It is called on each Decl
+ // that the RecursiveASTVisitor encounters, and returns true if the traversal
+ // should continue.
+ bool VisitDecl(clang::Decl *decl) {
+ MaybeComplete(decl);
+ return true;
+ }
+};
+}
+
+static void CompleteAllDeclContexts(clang::ASTImporter &exporter,
+ clang::FileID file,
+ clang::QualType root) {
+ clang::QualType canonical_type = root.getCanonicalType();
+ if (clang::TagDecl *tag_decl = canonical_type->getAsTagDecl()) {
+ Completer(exporter, file).TraverseDecl(tag_decl);
+ } else if (auto interface_type = llvm::dyn_cast<ObjCInterfaceType>(
+ canonical_type.getTypePtr())) {
+ Completer(exporter, file).TraverseDecl(interface_type->getDecl());
+ } else {
+ Completer(exporter, file).TraverseType(canonical_type);
+ }
+}
+
+static clang::QualType ExportAllDeclaredTypes(
+ clang::ExternalASTMerger &merger,
+ clang::ASTContext &source, clang::FileManager &source_file_manager,
+ const clang::ExternalASTMerger::OriginMap &source_origin_map,
+ clang::FileID file, clang::QualType root) {
+ clang::ExternalASTMerger::ImporterSource importer_source =
+ { source, source_file_manager, source_origin_map };
+ merger.AddSources(importer_source);
+ clang::ASTImporter &exporter = merger.ImporterForOrigin(source);
+ CompleteAllDeclContexts(exporter, file, root);
+ llvm::Expected<clang::QualType> ret_or_error = exporter.Import(root);
+ merger.RemoveSources(importer_source);
+ if (ret_or_error) {
+ return *ret_or_error;
+ } else {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS);
+ LLDB_LOG_ERROR(log, ret_or_error.takeError(), "Couldn't import type: {0}");
+ return clang::QualType();
+ }
+}
+
+TypeFromUser ClangExpressionDeclMap::DeportType(ClangASTContext &target,
+ ClangASTContext &source,
+ TypeFromParser parser_type) {
+ assert (&target == m_target->GetScratchClangASTContext());
+ assert ((TypeSystem*)&source == parser_type.GetTypeSystem());
+ assert (source.getASTContext() == m_ast_context);
+
+ if (m_ast_importer_sp) {
+ return TypeFromUser(m_ast_importer_sp->DeportType(
+ target.getASTContext(), source.getASTContext(),
+ parser_type.GetOpaqueQualType()),
+ &target);
+ } else if (m_merger_up) {
+ clang::FileID source_file =
+ source.getASTContext()->getSourceManager().getFileID(
+ source.getASTContext()->getTranslationUnitDecl()->getLocation());
+ auto scratch_ast_context = static_cast<ClangASTContextForExpressions*>(
+ m_target->GetScratchClangASTContext());
+ clang::QualType exported_type = ExportAllDeclaredTypes(
+ scratch_ast_context->GetMergerUnchecked(),
+ *source.getASTContext(), *source.getFileManager(),
+ m_merger_up->GetOrigins(),
+ source_file,
+ clang::QualType::getFromOpaquePtr(parser_type.GetOpaqueQualType()));
+ return TypeFromUser(exported_type.getAsOpaquePtr(), &target);
+ } else {
+ lldbassert(0 && "No mechanism for deporting a type!");
+ return TypeFromUser();
+ }
+}
+
+bool ClangExpressionDeclMap::AddPersistentVariable(const NamedDecl *decl,
+ ConstString name,
+ TypeFromParser parser_type,
+ bool is_result,
+ bool is_lvalue) {
+ assert(m_parser_vars.get());
+
+ ClangASTContext *ast =
+ llvm::dyn_cast_or_null<ClangASTContext>(parser_type.GetTypeSystem());
+ if (ast == nullptr)
+ return false;
+
+ if (m_parser_vars->m_materializer && is_result) {
+ Status err;
+
+ ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx;
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target == nullptr)
+ return false;
+
+ TypeFromUser user_type =
+ DeportType(*target->GetScratchClangASTContext(), *ast, parser_type);
+
+ uint32_t offset = m_parser_vars->m_materializer->AddResultVariable(
+ user_type, is_lvalue, m_keep_result_in_memory, m_result_delegate, err);
+
+ ClangExpressionVariable *var = new ClangExpressionVariable(
+ exe_ctx.GetBestExecutionContextScope(), name, user_type,
+ m_parser_vars->m_target_info.byte_order,
+ m_parser_vars->m_target_info.address_byte_size);
+
+ m_found_entities.AddNewlyConstructedVariable(var);
+
+ var->EnableParserVars(GetParserID());
+
+ ClangExpressionVariable::ParserVars *parser_vars =
+ var->GetParserVars(GetParserID());
+
+ parser_vars->m_named_decl = decl;
+ parser_vars->m_parser_type = parser_type;
+
+ var->EnableJITVars(GetParserID());
+
+ ClangExpressionVariable::JITVars *jit_vars = var->GetJITVars(GetParserID());
+
+ jit_vars->m_offset = offset;
+
+ return true;
+ }
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+ ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx;
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target == nullptr)
+ return false;
+
+ ClangASTContext *context(target->GetScratchClangASTContext());
+
+ TypeFromUser user_type = DeportType(*context, *ast, parser_type);
+
+ if (!user_type.GetOpaqueQualType()) {
+ if (log)
+ log->Printf("Persistent variable's type wasn't copied successfully");
+ return false;
+ }
+
+ if (!m_parser_vars->m_target_info.IsValid())
+ return false;
+
+ ClangExpressionVariable *var = llvm::cast<ClangExpressionVariable>(
+ m_parser_vars->m_persistent_vars
+ ->CreatePersistentVariable(
+ exe_ctx.GetBestExecutionContextScope(), name, user_type,
+ m_parser_vars->m_target_info.byte_order,
+ m_parser_vars->m_target_info.address_byte_size)
+ .get());
+
+ if (!var)
+ return false;
+
+ var->m_frozen_sp->SetHasCompleteType();
+
+ if (is_result)
+ var->m_flags |= ClangExpressionVariable::EVNeedsFreezeDry;
+ else
+ var->m_flags |=
+ ClangExpressionVariable::EVKeepInTarget; // explicitly-declared
+ // persistent variables should
+ // persist
+
+ if (is_lvalue) {
+ var->m_flags |= ClangExpressionVariable::EVIsProgramReference;
+ } else {
+ var->m_flags |= ClangExpressionVariable::EVIsLLDBAllocated;
+ var->m_flags |= ClangExpressionVariable::EVNeedsAllocation;
+ }
+
+ if (m_keep_result_in_memory) {
+ var->m_flags |= ClangExpressionVariable::EVKeepInTarget;
+ }
+
+ if (log)
+ log->Printf("Created persistent variable with flags 0x%hx", var->m_flags);
+
+ var->EnableParserVars(GetParserID());
+
+ ClangExpressionVariable::ParserVars *parser_vars =
+ var->GetParserVars(GetParserID());
+
+ parser_vars->m_named_decl = decl;
+ parser_vars->m_parser_type = parser_type;
+
+ return true;
+}
+
+bool ClangExpressionDeclMap::AddValueToStruct(const NamedDecl *decl,
+ ConstString name,
+ llvm::Value *value, size_t size,
+ lldb::offset_t alignment) {
+ assert(m_struct_vars.get());
+ assert(m_parser_vars.get());
+
+ bool is_persistent_variable = false;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ m_struct_vars->m_struct_laid_out = false;
+
+ if (ClangExpressionVariable::FindVariableInList(m_struct_members, decl,
+ GetParserID()))
+ return true;
+
+ ClangExpressionVariable *var(ClangExpressionVariable::FindVariableInList(
+ m_found_entities, decl, GetParserID()));
+
+ if (!var) {
+ var = ClangExpressionVariable::FindVariableInList(
+ *m_parser_vars->m_persistent_vars, decl, GetParserID());
+ is_persistent_variable = true;
+ }
+
+ if (!var)
+ return false;
+
+ if (log)
+ log->Printf("Adding value for (NamedDecl*)%p [%s - %s] to the structure",
+ static_cast<const void *>(decl), name.GetCString(),
+ var->GetName().GetCString());
+
+ // We know entity->m_parser_vars is valid because we used a parser variable
+ // to find it
+
+ ClangExpressionVariable::ParserVars *parser_vars =
+ llvm::cast<ClangExpressionVariable>(var)->GetParserVars(GetParserID());
+
+ parser_vars->m_llvm_value = value;
+
+ if (ClangExpressionVariable::JITVars *jit_vars =
+ llvm::cast<ClangExpressionVariable>(var)->GetJITVars(GetParserID())) {
+ // We already laid this out; do not touch
+
+ if (log)
+ log->Printf("Already placed at 0x%llx",
+ (unsigned long long)jit_vars->m_offset);
+ }
+
+ llvm::cast<ClangExpressionVariable>(var)->EnableJITVars(GetParserID());
+
+ ClangExpressionVariable::JITVars *jit_vars =
+ llvm::cast<ClangExpressionVariable>(var)->GetJITVars(GetParserID());
+
+ jit_vars->m_alignment = alignment;
+ jit_vars->m_size = size;
+
+ m_struct_members.AddVariable(var->shared_from_this());
+
+ if (m_parser_vars->m_materializer) {
+ uint32_t offset = 0;
+
+ Status err;
+
+ if (is_persistent_variable) {
+ ExpressionVariableSP var_sp(var->shared_from_this());
+ offset = m_parser_vars->m_materializer->AddPersistentVariable(
+ var_sp, nullptr, err);
+ } else {
+ if (const lldb_private::Symbol *sym = parser_vars->m_lldb_sym)
+ offset = m_parser_vars->m_materializer->AddSymbol(*sym, err);
+ else if (const RegisterInfo *reg_info = var->GetRegisterInfo())
+ offset = m_parser_vars->m_materializer->AddRegister(*reg_info, err);
+ else if (parser_vars->m_lldb_var)
+ offset = m_parser_vars->m_materializer->AddVariable(
+ parser_vars->m_lldb_var, err);
+ }
+
+ if (!err.Success())
+ return false;
+
+ if (log)
+ log->Printf("Placed at 0x%llx", (unsigned long long)offset);
+
+ jit_vars->m_offset =
+ offset; // TODO DoStructLayout() should not change this.
+ }
+
+ return true;
+}
+
+bool ClangExpressionDeclMap::DoStructLayout() {
+ assert(m_struct_vars.get());
+
+ if (m_struct_vars->m_struct_laid_out)
+ return true;
+
+ if (!m_parser_vars->m_materializer)
+ return false;
+
+ m_struct_vars->m_struct_alignment =
+ m_parser_vars->m_materializer->GetStructAlignment();
+ m_struct_vars->m_struct_size =
+ m_parser_vars->m_materializer->GetStructByteSize();
+ m_struct_vars->m_struct_laid_out = true;
+ return true;
+}
+
+bool ClangExpressionDeclMap::GetStructInfo(uint32_t &num_elements, size_t &size,
+ lldb::offset_t &alignment) {
+ assert(m_struct_vars.get());
+
+ if (!m_struct_vars->m_struct_laid_out)
+ return false;
+
+ num_elements = m_struct_members.GetSize();
+ size = m_struct_vars->m_struct_size;
+ alignment = m_struct_vars->m_struct_alignment;
+
+ return true;
+}
+
+bool ClangExpressionDeclMap::GetStructElement(const NamedDecl *&decl,
+ llvm::Value *&value,
+ lldb::offset_t &offset,
+ ConstString &name,
+ uint32_t index) {
+ assert(m_struct_vars.get());
+
+ if (!m_struct_vars->m_struct_laid_out)
+ return false;
+
+ if (index >= m_struct_members.GetSize())
+ return false;
+
+ ExpressionVariableSP member_sp(m_struct_members.GetVariableAtIndex(index));
+
+ if (!member_sp)
+ return false;
+
+ ClangExpressionVariable::ParserVars *parser_vars =
+ llvm::cast<ClangExpressionVariable>(member_sp.get())
+ ->GetParserVars(GetParserID());
+ ClangExpressionVariable::JITVars *jit_vars =
+ llvm::cast<ClangExpressionVariable>(member_sp.get())
+ ->GetJITVars(GetParserID());
+
+ if (!parser_vars || !jit_vars || !member_sp->GetValueObject())
+ return false;
+
+ decl = parser_vars->m_named_decl;
+ value = parser_vars->m_llvm_value;
+ offset = jit_vars->m_offset;
+ name = member_sp->GetName();
+
+ return true;
+}
+
+bool ClangExpressionDeclMap::GetFunctionInfo(const NamedDecl *decl,
+ uint64_t &ptr) {
+ ClangExpressionVariable *entity(ClangExpressionVariable::FindVariableInList(
+ m_found_entities, decl, GetParserID()));
+
+ if (!entity)
+ return false;
+
+ // We know m_parser_vars is valid since we searched for the variable by its
+ // NamedDecl
+
+ ClangExpressionVariable::ParserVars *parser_vars =
+ entity->GetParserVars(GetParserID());
+
+ ptr = parser_vars->m_lldb_value.GetScalar().ULongLong();
+
+ return true;
+}
+
+addr_t ClangExpressionDeclMap::GetSymbolAddress(Target &target,
+ Process *process,
+ ConstString name,
+ lldb::SymbolType symbol_type,
+ lldb_private::Module *module) {
+ SymbolContextList sc_list;
+
+ if (module)
+ module->FindSymbolsWithNameAndType(name, symbol_type, sc_list);
+ else
+ target.GetImages().FindSymbolsWithNameAndType(name, symbol_type, sc_list);
+
+ const uint32_t num_matches = sc_list.GetSize();
+ addr_t symbol_load_addr = LLDB_INVALID_ADDRESS;
+
+ for (uint32_t i = 0;
+ i < num_matches &&
+ (symbol_load_addr == 0 || symbol_load_addr == LLDB_INVALID_ADDRESS);
+ i++) {
+ SymbolContext sym_ctx;
+ sc_list.GetContextAtIndex(i, sym_ctx);
+
+ const Address sym_address = sym_ctx.symbol->GetAddress();
+
+ if (!sym_address.IsValid())
+ continue;
+
+ switch (sym_ctx.symbol->GetType()) {
+ case eSymbolTypeCode:
+ case eSymbolTypeTrampoline:
+ symbol_load_addr = sym_address.GetCallableLoadAddress(&target);
+ break;
+
+ case eSymbolTypeResolver:
+ symbol_load_addr = sym_address.GetCallableLoadAddress(&target, true);
+ break;
+
+ case eSymbolTypeReExported: {
+ ConstString reexport_name = sym_ctx.symbol->GetReExportedSymbolName();
+ if (reexport_name) {
+ ModuleSP reexport_module_sp;
+ ModuleSpec reexport_module_spec;
+ reexport_module_spec.GetPlatformFileSpec() =
+ sym_ctx.symbol->GetReExportedSymbolSharedLibrary();
+ if (reexport_module_spec.GetPlatformFileSpec()) {
+ reexport_module_sp =
+ target.GetImages().FindFirstModule(reexport_module_spec);
+ if (!reexport_module_sp) {
+ reexport_module_spec.GetPlatformFileSpec().GetDirectory().Clear();
+ reexport_module_sp =
+ target.GetImages().FindFirstModule(reexport_module_spec);
+ }
+ }
+ symbol_load_addr = GetSymbolAddress(
+ target, process, sym_ctx.symbol->GetReExportedSymbolName(),
+ symbol_type, reexport_module_sp.get());
+ }
+ } break;
+
+ case eSymbolTypeData:
+ case eSymbolTypeRuntime:
+ case eSymbolTypeVariable:
+ case eSymbolTypeLocal:
+ case eSymbolTypeParam:
+ case eSymbolTypeInvalid:
+ case eSymbolTypeAbsolute:
+ case eSymbolTypeException:
+ case eSymbolTypeSourceFile:
+ case eSymbolTypeHeaderFile:
+ case eSymbolTypeObjectFile:
+ case eSymbolTypeCommonBlock:
+ case eSymbolTypeBlock:
+ case eSymbolTypeVariableType:
+ case eSymbolTypeLineEntry:
+ case eSymbolTypeLineHeader:
+ case eSymbolTypeScopeBegin:
+ case eSymbolTypeScopeEnd:
+ case eSymbolTypeAdditional:
+ case eSymbolTypeCompiler:
+ case eSymbolTypeInstrumentation:
+ case eSymbolTypeUndefined:
+ case eSymbolTypeObjCClass:
+ case eSymbolTypeObjCMetaClass:
+ case eSymbolTypeObjCIVar:
+ symbol_load_addr = sym_address.GetLoadAddress(&target);
+ break;
+ }
+ }
+
+ if (symbol_load_addr == LLDB_INVALID_ADDRESS && process) {
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process);
+
+ if (runtime) {
+ symbol_load_addr = runtime->LookupRuntimeSymbol(name);
+ }
+ }
+
+ return symbol_load_addr;
+}
+
+addr_t ClangExpressionDeclMap::GetSymbolAddress(ConstString name,
+ lldb::SymbolType symbol_type) {
+ assert(m_parser_vars.get());
+
+ if (!m_parser_vars->m_exe_ctx.GetTargetPtr())
+ return false;
+
+ return GetSymbolAddress(m_parser_vars->m_exe_ctx.GetTargetRef(),
+ m_parser_vars->m_exe_ctx.GetProcessPtr(), name,
+ symbol_type);
+}
+
+lldb::VariableSP ClangExpressionDeclMap::FindGlobalVariable(
+ Target &target, ModuleSP &module, ConstString name,
+ CompilerDeclContext *namespace_decl, TypeFromUser *type) {
+ VariableList vars;
+
+ if (module && namespace_decl)
+ module->FindGlobalVariables(name, namespace_decl, -1, vars);
+ else
+ target.GetImages().FindGlobalVariables(name, -1, vars);
+
+ if (vars.GetSize()) {
+ if (type) {
+ for (size_t i = 0; i < vars.GetSize(); ++i) {
+ VariableSP var_sp = vars.GetVariableAtIndex(i);
+
+ if (ClangASTContext::AreTypesSame(
+ *type, var_sp->GetType()->GetFullCompilerType()))
+ return var_sp;
+ }
+ } else {
+ return vars.GetVariableAtIndex(0);
+ }
+ }
+
+ return VariableSP();
+}
+
+ClangASTContext *ClangExpressionDeclMap::GetClangASTContext() {
+ StackFrame *frame = m_parser_vars->m_exe_ctx.GetFramePtr();
+ if (frame == nullptr)
+ return nullptr;
+
+ SymbolContext sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction |
+ lldb::eSymbolContextBlock);
+ if (sym_ctx.block == nullptr)
+ return nullptr;
+
+ CompilerDeclContext frame_decl_context = sym_ctx.block->GetDeclContext();
+ if (!frame_decl_context)
+ return nullptr;
+
+ return llvm::dyn_cast_or_null<ClangASTContext>(
+ frame_decl_context.GetTypeSystem());
+}
+
+// Interface for ClangASTSource
+
+void ClangExpressionDeclMap::FindExternalVisibleDecls(
+ NameSearchContext &context) {
+ assert(m_ast_context);
+
+ ClangASTMetrics::RegisterVisibleQuery();
+
+ const ConstString name(context.m_decl_name.getAsString().c_str());
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (GetImportInProgress()) {
+ if (log && log->GetVerbose())
+ log->Printf("Ignoring a query during an import");
+ return;
+ }
+
+ static unsigned int invocation_id = 0;
+ unsigned int current_id = invocation_id++;
+
+ if (log) {
+ if (!context.m_decl_context)
+ log->Printf("ClangExpressionDeclMap::FindExternalVisibleDecls[%u] for "
+ "'%s' in a NULL DeclContext",
+ current_id, name.GetCString());
+ else if (const NamedDecl *context_named_decl =
+ dyn_cast<NamedDecl>(context.m_decl_context))
+ log->Printf("ClangExpressionDeclMap::FindExternalVisibleDecls[%u] for "
+ "'%s' in '%s'",
+ current_id, name.GetCString(),
+ context_named_decl->getNameAsString().c_str());
+ else
+ log->Printf("ClangExpressionDeclMap::FindExternalVisibleDecls[%u] for "
+ "'%s' in a '%s'",
+ current_id, name.GetCString(),
+ context.m_decl_context->getDeclKindName());
+ }
+
+ if (const NamespaceDecl *namespace_context =
+ dyn_cast<NamespaceDecl>(context.m_decl_context)) {
+ if (namespace_context->getName().str() ==
+ std::string(g_lldb_local_vars_namespace_cstr)) {
+ CompilerDeclContext compiler_decl_ctx(
+ GetClangASTContext(), const_cast<void *>(static_cast<const void *>(
+ context.m_decl_context)));
+ FindExternalVisibleDecls(context, lldb::ModuleSP(), compiler_decl_ctx,
+ current_id);
+ return;
+ }
+
+ ClangASTImporter::NamespaceMapSP namespace_map =
+ m_ast_importer_sp
+ ? m_ast_importer_sp->GetNamespaceMap(namespace_context)
+ : ClangASTImporter::NamespaceMapSP();
+
+ if (!namespace_map)
+ return;
+
+ if (log && log->GetVerbose())
+ log->Printf(" CEDM::FEVD[%u] Inspecting (NamespaceMap*)%p (%d entries)",
+ current_id, static_cast<void *>(namespace_map.get()),
+ (int)namespace_map->size());
+
+ for (ClangASTImporter::NamespaceMap::iterator i = namespace_map->begin(),
+ e = namespace_map->end();
+ i != e; ++i) {
+ if (log)
+ log->Printf(" CEDM::FEVD[%u] Searching namespace %s in module %s",
+ current_id, i->second.GetName().AsCString(),
+ i->first->GetFileSpec().GetFilename().GetCString());
+
+ FindExternalVisibleDecls(context, i->first, i->second, current_id);
+ }
+ } else if (isa<TranslationUnitDecl>(context.m_decl_context)) {
+ CompilerDeclContext namespace_decl;
+
+ if (log)
+ log->Printf(" CEDM::FEVD[%u] Searching the root namespace", current_id);
+
+ FindExternalVisibleDecls(context, lldb::ModuleSP(), namespace_decl,
+ current_id);
+ }
+
+ ClangASTSource::FindExternalVisibleDecls(context);
+}
+
+void ClangExpressionDeclMap::FindExternalVisibleDecls(
+ NameSearchContext &context, lldb::ModuleSP module_sp,
+ CompilerDeclContext &namespace_decl, unsigned int current_id) {
+ assert(m_ast_context);
+
+ std::function<void(clang::FunctionDecl *)> MaybeRegisterFunctionBody =
+ [this](clang::FunctionDecl *copied_function_decl) {
+ if (copied_function_decl->getBody() && m_parser_vars->m_code_gen) {
+ DeclGroupRef decl_group_ref(copied_function_decl);
+ m_parser_vars->m_code_gen->HandleTopLevelDecl(decl_group_ref);
+ }
+ };
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ SymbolContextList sc_list;
+
+ const ConstString name(context.m_decl_name.getAsString().c_str());
+ if (IgnoreName(name, false))
+ return;
+
+ // Only look for functions by name out in our symbols if the function doesn't
+ // start with our phony prefix of '$'
+ Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr();
+ StackFrame *frame = m_parser_vars->m_exe_ctx.GetFramePtr();
+ SymbolContext sym_ctx;
+ if (frame != nullptr)
+ sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction |
+ lldb::eSymbolContextBlock);
+
+ // Try the persistent decls, which take precedence over all else.
+ if (!namespace_decl) {
+ do {
+ if (!target)
+ break;
+
+ ClangASTContext *scratch_clang_ast_context =
+ target->GetScratchClangASTContext();
+
+ if (!scratch_clang_ast_context)
+ break;
+
+ ASTContext *scratch_ast_context =
+ scratch_clang_ast_context->getASTContext();
+
+ if (!scratch_ast_context)
+ break;
+
+ NamedDecl *persistent_decl =
+ m_parser_vars->m_persistent_vars->GetPersistentDecl(name);
+
+ if (!persistent_decl)
+ break;
+
+ Decl *parser_persistent_decl = CopyDecl(persistent_decl);
+
+ if (!parser_persistent_decl)
+ break;
+
+ NamedDecl *parser_named_decl =
+ dyn_cast<NamedDecl>(parser_persistent_decl);
+
+ if (!parser_named_decl)
+ break;
+
+ if (clang::FunctionDecl *parser_function_decl =
+ llvm::dyn_cast<clang::FunctionDecl>(parser_named_decl)) {
+ MaybeRegisterFunctionBody(parser_function_decl);
+ }
+
+ if (log)
+ log->Printf(" CEDM::FEVD[%u] Found persistent decl %s", current_id,
+ name.GetCString());
+
+ context.AddNamedDecl(parser_named_decl);
+ } while (false);
+ }
+
+ if (name.GetCString()[0] == '$' && !namespace_decl) {
+ static ConstString g_lldb_class_name("$__lldb_class");
+
+ if (name == g_lldb_class_name) {
+ if (m_ctx_obj) {
+ Status status;
+ lldb::ValueObjectSP ctx_obj_ptr = m_ctx_obj->AddressOf(status);
+ if (!ctx_obj_ptr || status.Fail())
+ return;
+
+ AddThisType(context, TypeFromUser(m_ctx_obj->GetCompilerType()),
+ current_id);
+
+ m_struct_vars->m_object_pointer_type =
+ TypeFromUser(ctx_obj_ptr->GetCompilerType());
+
+ return;
+ }
+
+ // Clang is looking for the type of "this"
+
+ if (frame == nullptr)
+ return;
+
+ // Find the block that defines the function represented by "sym_ctx"
+ Block *function_block = sym_ctx.GetFunctionBlock();
+
+ if (!function_block)
+ return;
+
+ CompilerDeclContext function_decl_ctx = function_block->GetDeclContext();
+
+ if (!function_decl_ctx)
+ return;
+
+ clang::CXXMethodDecl *method_decl =
+ ClangASTContext::DeclContextGetAsCXXMethodDecl(function_decl_ctx);
+
+ if (method_decl) {
+ clang::CXXRecordDecl *class_decl = method_decl->getParent();
+
+ QualType class_qual_type(class_decl->getTypeForDecl(), 0);
+
+ TypeFromUser class_user_type(
+ class_qual_type.getAsOpaquePtr(),
+ ClangASTContext::GetASTContext(&class_decl->getASTContext()));
+
+ if (log) {
+ ASTDumper ast_dumper(class_qual_type);
+ log->Printf(" CEDM::FEVD[%u] Adding type for $__lldb_class: %s",
+ current_id, ast_dumper.GetCString());
+ }
+
+ AddThisType(context, class_user_type, current_id);
+
+ if (method_decl->isInstance()) {
+ // self is a pointer to the object
+
+ QualType class_pointer_type =
+ method_decl->getASTContext().getPointerType(class_qual_type);
+
+ TypeFromUser self_user_type(
+ class_pointer_type.getAsOpaquePtr(),
+ ClangASTContext::GetASTContext(&method_decl->getASTContext()));
+
+ m_struct_vars->m_object_pointer_type = self_user_type;
+ }
+ } else {
+ // This branch will get hit if we are executing code in the context of
+ // a function that claims to have an object pointer (through
+ // DW_AT_object_pointer?) but is not formally a method of the class.
+ // In that case, just look up the "this" variable in the current scope
+ // and use its type.
+ // FIXME: This code is formally correct, but clang doesn't currently
+ // emit DW_AT_object_pointer
+ // for C++ so it hasn't actually been tested.
+
+ VariableList *vars = frame->GetVariableList(false);
+
+ lldb::VariableSP this_var = vars->FindVariable(ConstString("this"));
+
+ if (this_var && this_var->IsInScope(frame) &&
+ this_var->LocationIsValidForFrame(frame)) {
+ Type *this_type = this_var->GetType();
+
+ if (!this_type)
+ return;
+
+ TypeFromUser pointee_type =
+ this_type->GetForwardCompilerType().GetPointeeType();
+
+ if (pointee_type.IsValid()) {
+ if (log) {
+ ASTDumper ast_dumper(pointee_type);
+ log->Printf(" FEVD[%u] Adding type for $__lldb_class: %s",
+ current_id, ast_dumper.GetCString());
+ }
+
+ AddThisType(context, pointee_type, current_id);
+ TypeFromUser this_user_type(this_type->GetFullCompilerType());
+ m_struct_vars->m_object_pointer_type = this_user_type;
+ return;
+ }
+ }
+ }
+
+ return;
+ }
+
+ static ConstString g_lldb_objc_class_name("$__lldb_objc_class");
+ if (name == g_lldb_objc_class_name) {
+ if (m_ctx_obj) {
+ Status status;
+ lldb::ValueObjectSP ctx_obj_ptr = m_ctx_obj->AddressOf(status);
+ if (!ctx_obj_ptr || status.Fail())
+ return;
+
+ AddOneType(context, TypeFromUser(m_ctx_obj->GetCompilerType()),
+ current_id);
+
+ m_struct_vars->m_object_pointer_type =
+ TypeFromUser(ctx_obj_ptr->GetCompilerType());
+
+ return;
+ }
+
+ // Clang is looking for the type of "*self"
+
+ if (!frame)
+ return;
+
+ SymbolContext sym_ctx = frame->GetSymbolContext(
+ lldb::eSymbolContextFunction | lldb::eSymbolContextBlock);
+
+ // Find the block that defines the function represented by "sym_ctx"
+ Block *function_block = sym_ctx.GetFunctionBlock();
+
+ if (!function_block)
+ return;
+
+ CompilerDeclContext function_decl_ctx = function_block->GetDeclContext();
+
+ if (!function_decl_ctx)
+ return;
+
+ clang::ObjCMethodDecl *method_decl =
+ ClangASTContext::DeclContextGetAsObjCMethodDecl(function_decl_ctx);
+
+ if (method_decl) {
+ ObjCInterfaceDecl *self_interface = method_decl->getClassInterface();
+
+ if (!self_interface)
+ return;
+
+ const clang::Type *interface_type = self_interface->getTypeForDecl();
+
+ if (!interface_type)
+ return; // This is unlikely, but we have seen crashes where this
+ // occurred
+
+ TypeFromUser class_user_type(
+ QualType(interface_type, 0).getAsOpaquePtr(),
+ ClangASTContext::GetASTContext(&method_decl->getASTContext()));
+
+ if (log) {
+ ASTDumper ast_dumper(interface_type);
+ log->Printf(" FEVD[%u] Adding type for $__lldb_objc_class: %s",
+ current_id, ast_dumper.GetCString());
+ }
+
+ AddOneType(context, class_user_type, current_id);
+
+ if (method_decl->isInstanceMethod()) {
+ // self is a pointer to the object
+
+ QualType class_pointer_type =
+ method_decl->getASTContext().getObjCObjectPointerType(
+ QualType(interface_type, 0));
+
+ TypeFromUser self_user_type(
+ class_pointer_type.getAsOpaquePtr(),
+ ClangASTContext::GetASTContext(&method_decl->getASTContext()));
+
+ m_struct_vars->m_object_pointer_type = self_user_type;
+ } else {
+ // self is a Class pointer
+ QualType class_type = method_decl->getASTContext().getObjCClassType();
+
+ TypeFromUser self_user_type(
+ class_type.getAsOpaquePtr(),
+ ClangASTContext::GetASTContext(&method_decl->getASTContext()));
+
+ m_struct_vars->m_object_pointer_type = self_user_type;
+ }
+
+ return;
+ } else {
+ // This branch will get hit if we are executing code in the context of
+ // a function that claims to have an object pointer (through
+ // DW_AT_object_pointer?) but is not formally a method of the class.
+ // In that case, just look up the "self" variable in the current scope
+ // and use its type.
+
+ VariableList *vars = frame->GetVariableList(false);
+
+ lldb::VariableSP self_var = vars->FindVariable(ConstString("self"));
+
+ if (self_var && self_var->IsInScope(frame) &&
+ self_var->LocationIsValidForFrame(frame)) {
+ Type *self_type = self_var->GetType();
+
+ if (!self_type)
+ return;
+
+ CompilerType self_clang_type = self_type->GetFullCompilerType();
+
+ if (ClangASTContext::IsObjCClassType(self_clang_type)) {
+ return;
+ } else if (ClangASTContext::IsObjCObjectPointerType(
+ self_clang_type)) {
+ self_clang_type = self_clang_type.GetPointeeType();
+
+ if (!self_clang_type)
+ return;
+
+ if (log) {
+ ASTDumper ast_dumper(self_type->GetFullCompilerType());
+ log->Printf(" FEVD[%u] Adding type for $__lldb_objc_class: %s",
+ current_id, ast_dumper.GetCString());
+ }
+
+ TypeFromUser class_user_type(self_clang_type);
+
+ AddOneType(context, class_user_type, current_id);
+
+ TypeFromUser self_user_type(self_type->GetFullCompilerType());
+
+ m_struct_vars->m_object_pointer_type = self_user_type;
+ return;
+ }
+ }
+ }
+
+ return;
+ }
+
+ if (name == ConstString(g_lldb_local_vars_namespace_cstr)) {
+ CompilerDeclContext frame_decl_context =
+ sym_ctx.block != nullptr ? sym_ctx.block->GetDeclContext()
+ : CompilerDeclContext();
+
+ if (frame_decl_context) {
+ ClangASTContext *ast = llvm::dyn_cast_or_null<ClangASTContext>(
+ frame_decl_context.GetTypeSystem());
+
+ if (ast) {
+ clang::NamespaceDecl *namespace_decl =
+ ClangASTContext::GetUniqueNamespaceDeclaration(
+ m_ast_context, name.GetCString(), nullptr);
+ if (namespace_decl) {
+ context.AddNamedDecl(namespace_decl);
+ clang::DeclContext *clang_decl_ctx =
+ clang::Decl::castToDeclContext(namespace_decl);
+ clang_decl_ctx->setHasExternalVisibleStorage(true);
+ context.m_found.local_vars_nsp = true;
+ }
+ }
+ }
+
+ return;
+ }
+
+ // any other $__lldb names should be weeded out now
+ if (name.GetStringRef().startswith("$__lldb"))
+ return;
+
+ ExpressionVariableSP pvar_sp(
+ m_parser_vars->m_persistent_vars->GetVariable(name));
+
+ if (pvar_sp) {
+ AddOneVariable(context, pvar_sp, current_id);
+ return;
+ }
+
+ const char *reg_name(&name.GetCString()[1]);
+
+ if (m_parser_vars->m_exe_ctx.GetRegisterContext()) {
+ const RegisterInfo *reg_info(
+ m_parser_vars->m_exe_ctx.GetRegisterContext()->GetRegisterInfoByName(
+ reg_name));
+
+ if (reg_info) {
+ if (log)
+ log->Printf(" CEDM::FEVD[%u] Found register %s", current_id,
+ reg_info->name);
+
+ AddOneRegister(context, reg_info, current_id);
+ }
+ }
+ } else {
+ ValueObjectSP valobj;
+ VariableSP var;
+
+ bool local_var_lookup =
+ !namespace_decl || (namespace_decl.GetName() ==
+ ConstString(g_lldb_local_vars_namespace_cstr));
+ if (frame && local_var_lookup) {
+ CompilerDeclContext compiler_decl_context =
+ sym_ctx.block != nullptr ? sym_ctx.block->GetDeclContext()
+ : CompilerDeclContext();
+
+ if (compiler_decl_context) {
+ // Make sure that the variables are parsed so that we have the
+ // declarations.
+ VariableListSP vars = frame->GetInScopeVariableList(true);
+ for (size_t i = 0; i < vars->GetSize(); i++)
+ vars->GetVariableAtIndex(i)->GetDecl();
+
+ // Search for declarations matching the name. Do not include imported
+ // decls in the search if we are looking for decls in the artificial
+ // namespace $__lldb_local_vars.
+ std::vector<CompilerDecl> found_decls =
+ compiler_decl_context.FindDeclByName(name,
+ namespace_decl.IsValid());
+
+ bool variable_found = false;
+ for (CompilerDecl decl : found_decls) {
+ for (size_t vi = 0, ve = vars->GetSize(); vi != ve; ++vi) {
+ VariableSP candidate_var = vars->GetVariableAtIndex(vi);
+ if (candidate_var->GetDecl() == decl) {
+ var = candidate_var;
+ break;
+ }
+ }
+
+ if (var && !variable_found) {
+ variable_found = true;
+ valobj = ValueObjectVariable::Create(frame, var);
+ AddOneVariable(context, var, valobj, current_id);
+ context.m_found.variable = true;
+ }
+ }
+ if (variable_found)
+ return;
+ }
+ }
+ if (target) {
+ var = FindGlobalVariable(*target, module_sp, name, &namespace_decl,
+ nullptr);
+
+ if (var) {
+ valobj = ValueObjectVariable::Create(target, var);
+ AddOneVariable(context, var, valobj, current_id);
+ context.m_found.variable = true;
+ return;
+ }
+ }
+
+ std::vector<clang::NamedDecl *> decls_from_modules;
+
+ if (target) {
+ if (ClangModulesDeclVendor *decl_vendor =
+ target->GetClangModulesDeclVendor()) {
+ decl_vendor->FindDecls(name, false, UINT32_MAX, decls_from_modules);
+ }
+ }
+
+ const bool include_inlines = false;
+ const bool append = false;
+
+ if (namespace_decl && module_sp) {
+ const bool include_symbols = false;
+
+ module_sp->FindFunctions(name, &namespace_decl, eFunctionNameTypeBase,
+ include_symbols, include_inlines, append,
+ sc_list);
+ } else if (target && !namespace_decl) {
+ const bool include_symbols = true;
+
+ // TODO Fix FindFunctions so that it doesn't return
+ // instance methods for eFunctionNameTypeBase.
+
+ target->GetImages().FindFunctions(name, eFunctionNameTypeFull,
+ include_symbols, include_inlines,
+ append, sc_list);
+ }
+
+ // If we found more than one function, see if we can use the frame's decl
+ // context to remove functions that are shadowed by other functions which
+ // match in type but are nearer in scope.
+ //
+ // AddOneFunction will not add a function whose type has already been
+ // added, so if there's another function in the list with a matching type,
+ // check to see if their decl context is a parent of the current frame's or
+ // was imported via a and using statement, and pick the best match
+ // according to lookup rules.
+ if (sc_list.GetSize() > 1) {
+ // Collect some info about our frame's context.
+ StackFrame *frame = m_parser_vars->m_exe_ctx.GetFramePtr();
+ SymbolContext frame_sym_ctx;
+ if (frame != nullptr)
+ frame_sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction |
+ lldb::eSymbolContextBlock);
+ CompilerDeclContext frame_decl_context =
+ frame_sym_ctx.block != nullptr ? frame_sym_ctx.block->GetDeclContext()
+ : CompilerDeclContext();
+
+ // We can't do this without a compiler decl context for our frame.
+ if (frame_decl_context) {
+ clang::DeclContext *frame_decl_ctx =
+ (clang::DeclContext *)frame_decl_context.GetOpaqueDeclContext();
+ ClangASTContext *ast = llvm::dyn_cast_or_null<ClangASTContext>(
+ frame_decl_context.GetTypeSystem());
+
+ // Structure to hold the info needed when comparing function
+ // declarations.
+ struct FuncDeclInfo {
+ ConstString m_name;
+ CompilerType m_copied_type;
+ uint32_t m_decl_lvl;
+ SymbolContext m_sym_ctx;
+ };
+
+ // First, symplify things by looping through the symbol contexts to
+ // remove unwanted functions and separate out the functions we want to
+ // compare and prune into a separate list. Cache the info needed about
+ // the function declarations in a vector for efficiency.
+ SymbolContextList sc_sym_list;
+ uint32_t num_indices = sc_list.GetSize();
+ std::vector<FuncDeclInfo> fdi_cache;
+ fdi_cache.reserve(num_indices);
+ for (uint32_t index = 0; index < num_indices; ++index) {
+ FuncDeclInfo fdi;
+ SymbolContext sym_ctx;
+ sc_list.GetContextAtIndex(index, sym_ctx);
+
+ // We don't know enough about symbols to compare them, but we should
+ // keep them in the list.
+ Function *function = sym_ctx.function;
+ if (!function) {
+ sc_sym_list.Append(sym_ctx);
+ continue;
+ }
+ // Filter out functions without declaration contexts, as well as
+ // class/instance methods, since they'll be skipped in the code that
+ // follows anyway.
+ CompilerDeclContext func_decl_context = function->GetDeclContext();
+ if (!func_decl_context ||
+ func_decl_context.IsClassMethod(nullptr, nullptr, nullptr))
+ continue;
+ // We can only prune functions for which we can copy the type.
+ CompilerType func_clang_type =
+ function->GetType()->GetFullCompilerType();
+ CompilerType copied_func_type = GuardedCopyType(func_clang_type);
+ if (!copied_func_type) {
+ sc_sym_list.Append(sym_ctx);
+ continue;
+ }
+
+ fdi.m_sym_ctx = sym_ctx;
+ fdi.m_name = function->GetName();
+ fdi.m_copied_type = copied_func_type;
+ fdi.m_decl_lvl = LLDB_INVALID_DECL_LEVEL;
+ if (fdi.m_copied_type && func_decl_context) {
+ // Call CountDeclLevels to get the number of parent scopes we have
+ // to look through before we find the function declaration. When
+ // comparing functions of the same type, the one with a lower count
+ // will be closer to us in the lookup scope and shadows the other.
+ clang::DeclContext *func_decl_ctx =
+ (clang::DeclContext *)func_decl_context.GetOpaqueDeclContext();
+ fdi.m_decl_lvl = ast->CountDeclLevels(
+ frame_decl_ctx, func_decl_ctx, &fdi.m_name, &fdi.m_copied_type);
+ }
+ fdi_cache.emplace_back(fdi);
+ }
+
+ // Loop through the functions in our cache looking for matching types,
+ // then compare their scope levels to see which is closer.
+ std::multimap<CompilerType, const FuncDeclInfo *> matches;
+ for (const FuncDeclInfo &fdi : fdi_cache) {
+ const CompilerType t = fdi.m_copied_type;
+ auto q = matches.find(t);
+ if (q != matches.end()) {
+ if (q->second->m_decl_lvl > fdi.m_decl_lvl)
+ // This function is closer; remove the old set.
+ matches.erase(t);
+ else if (q->second->m_decl_lvl < fdi.m_decl_lvl)
+ // The functions in our set are closer - skip this one.
+ continue;
+ }
+ matches.insert(std::make_pair(t, &fdi));
+ }
+
+ // Loop through our matches and add their symbol contexts to our list.
+ SymbolContextList sc_func_list;
+ for (const auto &q : matches)
+ sc_func_list.Append(q.second->m_sym_ctx);
+
+ // Rejoin the lists with the functions in front.
+ sc_list = sc_func_list;
+ sc_list.Append(sc_sym_list);
+ }
+ }
+
+ if (sc_list.GetSize()) {
+ Symbol *extern_symbol = nullptr;
+ Symbol *non_extern_symbol = nullptr;
+
+ for (uint32_t index = 0, num_indices = sc_list.GetSize();
+ index < num_indices; ++index) {
+ SymbolContext sym_ctx;
+ sc_list.GetContextAtIndex(index, sym_ctx);
+
+ if (sym_ctx.function) {
+ CompilerDeclContext decl_ctx = sym_ctx.function->GetDeclContext();
+
+ if (!decl_ctx)
+ continue;
+
+ // Filter out class/instance methods.
+ if (decl_ctx.IsClassMethod(nullptr, nullptr, nullptr))
+ continue;
+
+ AddOneFunction(context, sym_ctx.function, nullptr, current_id);
+ context.m_found.function_with_type_info = true;
+ context.m_found.function = true;
+ } else if (sym_ctx.symbol) {
+ if (sym_ctx.symbol->GetType() == eSymbolTypeReExported && target) {
+ sym_ctx.symbol = sym_ctx.symbol->ResolveReExportedSymbol(*target);
+ if (sym_ctx.symbol == nullptr)
+ continue;
+ }
+
+ if (sym_ctx.symbol->IsExternal())
+ extern_symbol = sym_ctx.symbol;
+ else
+ non_extern_symbol = sym_ctx.symbol;
+ }
+ }
+
+ if (!context.m_found.function_with_type_info) {
+ for (clang::NamedDecl *decl : decls_from_modules) {
+ if (llvm::isa<clang::FunctionDecl>(decl)) {
+ clang::NamedDecl *copied_decl =
+ llvm::cast_or_null<FunctionDecl>(CopyDecl(decl));
+ if (copied_decl) {
+ context.AddNamedDecl(copied_decl);
+ context.m_found.function_with_type_info = true;
+ }
+ }
+ }
+ }
+
+ if (!context.m_found.function_with_type_info) {
+ if (extern_symbol) {
+ AddOneFunction(context, nullptr, extern_symbol, current_id);
+ context.m_found.function = true;
+ } else if (non_extern_symbol) {
+ AddOneFunction(context, nullptr, non_extern_symbol, current_id);
+ context.m_found.function = true;
+ }
+ }
+ }
+
+ if (!context.m_found.function_with_type_info) {
+ // Try the modules next.
+
+ do {
+ if (ClangModulesDeclVendor *modules_decl_vendor =
+ m_target->GetClangModulesDeclVendor()) {
+ bool append = false;
+ uint32_t max_matches = 1;
+ std::vector<clang::NamedDecl *> decls;
+
+ if (!modules_decl_vendor->FindDecls(name, append, max_matches, decls))
+ break;
+
+ clang::NamedDecl *const decl_from_modules = decls[0];
+
+ if (llvm::isa<clang::FunctionDecl>(decl_from_modules)) {
+ if (log) {
+ log->Printf(" CAS::FEVD[%u] Matching function found for "
+ "\"%s\" in the modules",
+ current_id, name.GetCString());
+ }
+
+ clang::Decl *copied_decl = CopyDecl(decl_from_modules);
+ clang::FunctionDecl *copied_function_decl =
+ copied_decl ? dyn_cast<clang::FunctionDecl>(copied_decl)
+ : nullptr;
+
+ if (!copied_function_decl) {
+ if (log)
+ log->Printf(" CAS::FEVD[%u] - Couldn't export a function "
+ "declaration from the modules",
+ current_id);
+
+ break;
+ }
+
+ MaybeRegisterFunctionBody(copied_function_decl);
+
+ context.AddNamedDecl(copied_function_decl);
+
+ context.m_found.function_with_type_info = true;
+ context.m_found.function = true;
+ } else if (llvm::isa<clang::VarDecl>(decl_from_modules)) {
+ if (log) {
+ log->Printf(" CAS::FEVD[%u] Matching variable found for "
+ "\"%s\" in the modules",
+ current_id, name.GetCString());
+ }
+
+ clang::Decl *copied_decl = CopyDecl(decl_from_modules);
+ clang::VarDecl *copied_var_decl =
+ copied_decl ? dyn_cast_or_null<clang::VarDecl>(copied_decl)
+ : nullptr;
+
+ if (!copied_var_decl) {
+ if (log)
+ log->Printf(" CAS::FEVD[%u] - Couldn't export a variable "
+ "declaration from the modules",
+ current_id);
+
+ break;
+ }
+
+ context.AddNamedDecl(copied_var_decl);
+
+ context.m_found.variable = true;
+ }
+ }
+ } while (false);
+ }
+
+ if (target && !context.m_found.variable && !namespace_decl) {
+ // We couldn't find a non-symbol variable for this. Now we'll hunt for a
+ // generic data symbol, and -- if it is found -- treat it as a variable.
+ Status error;
+
+ const Symbol *data_symbol =
+ m_parser_vars->m_sym_ctx.FindBestGlobalDataSymbol(name, error);
+
+ if (!error.Success()) {
+ const unsigned diag_id =
+ m_ast_context->getDiagnostics().getCustomDiagID(
+ clang::DiagnosticsEngine::Level::Error, "%0");
+ m_ast_context->getDiagnostics().Report(diag_id) << error.AsCString();
+ }
+
+ if (data_symbol) {
+ std::string warning("got name from symbols: ");
+ warning.append(name.AsCString());
+ const unsigned diag_id =
+ m_ast_context->getDiagnostics().getCustomDiagID(
+ clang::DiagnosticsEngine::Level::Warning, "%0");
+ m_ast_context->getDiagnostics().Report(diag_id) << warning.c_str();
+ AddOneGenericVariable(context, *data_symbol, current_id);
+ context.m_found.variable = true;
+ }
+ }
+ }
+}
+
+bool ClangExpressionDeclMap::GetVariableValue(VariableSP &var,
+ lldb_private::Value &var_location,
+ TypeFromUser *user_type,
+ TypeFromParser *parser_type) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ Type *var_type = var->GetType();
+
+ if (!var_type) {
+ if (log)
+ log->PutCString("Skipped a definition because it has no type");
+ return false;
+ }
+
+ CompilerType var_clang_type = var_type->GetFullCompilerType();
+
+ if (!var_clang_type) {
+ if (log)
+ log->PutCString("Skipped a definition because it has no Clang type");
+ return false;
+ }
+
+ ClangASTContext *clang_ast = llvm::dyn_cast_or_null<ClangASTContext>(
+ var_type->GetForwardCompilerType().GetTypeSystem());
+
+ if (!clang_ast) {
+ if (log)
+ log->PutCString("Skipped a definition because it has no Clang AST");
+ return false;
+ }
+
+ ASTContext *ast = clang_ast->getASTContext();
+
+ if (!ast) {
+ if (log)
+ log->PutCString(
+ "There is no AST context for the current execution context");
+ return false;
+ }
+
+ DWARFExpression &var_location_expr = var->LocationExpression();
+
+ Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr();
+ Status err;
+
+ if (var->GetLocationIsConstantValueData()) {
+ DataExtractor const_value_extractor;
+
+ if (var_location_expr.GetExpressionData(const_value_extractor)) {
+ var_location = Value(const_value_extractor.GetDataStart(),
+ const_value_extractor.GetByteSize());
+ var_location.SetValueType(Value::eValueTypeHostAddress);
+ } else {
+ if (log)
+ log->Printf("Error evaluating constant variable: %s", err.AsCString());
+ return false;
+ }
+ }
+
+ CompilerType type_to_use = GuardedCopyType(var_clang_type);
+
+ if (!type_to_use) {
+ if (log)
+ log->Printf(
+ "Couldn't copy a variable's type into the parser's AST context");
+
+ return false;
+ }
+
+ if (parser_type)
+ *parser_type = TypeFromParser(type_to_use);
+
+ if (var_location.GetContextType() == Value::eContextTypeInvalid)
+ var_location.SetCompilerType(type_to_use);
+
+ if (var_location.GetValueType() == Value::eValueTypeFileAddress) {
+ SymbolContext var_sc;
+ var->CalculateSymbolContext(&var_sc);
+
+ if (!var_sc.module_sp)
+ return false;
+
+ Address so_addr(var_location.GetScalar().ULongLong(),
+ var_sc.module_sp->GetSectionList());
+
+ lldb::addr_t load_addr = so_addr.GetLoadAddress(target);
+
+ if (load_addr != LLDB_INVALID_ADDRESS) {
+ var_location.GetScalar() = load_addr;
+ var_location.SetValueType(Value::eValueTypeLoadAddress);
+ }
+ }
+
+ if (user_type)
+ *user_type = TypeFromUser(var_clang_type);
+
+ return true;
+}
+
+void ClangExpressionDeclMap::AddOneVariable(NameSearchContext &context,
+ VariableSP var,
+ ValueObjectSP valobj,
+ unsigned int current_id) {
+ assert(m_parser_vars.get());
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ TypeFromUser ut;
+ TypeFromParser pt;
+ Value var_location;
+
+ if (!GetVariableValue(var, var_location, &ut, &pt))
+ return;
+
+ clang::QualType parser_opaque_type =
+ QualType::getFromOpaquePtr(pt.GetOpaqueQualType());
+
+ if (parser_opaque_type.isNull())
+ return;
+
+ if (const clang::Type *parser_type = parser_opaque_type.getTypePtr()) {
+ if (const TagType *tag_type = dyn_cast<TagType>(parser_type))
+ CompleteType(tag_type->getDecl());
+ if (const ObjCObjectPointerType *objc_object_ptr_type =
+ dyn_cast<ObjCObjectPointerType>(parser_type))
+ CompleteType(objc_object_ptr_type->getInterfaceDecl());
+ }
+
+ bool is_reference = pt.IsReferenceType();
+
+ NamedDecl *var_decl = nullptr;
+ if (is_reference)
+ var_decl = context.AddVarDecl(pt);
+ else
+ var_decl = context.AddVarDecl(pt.GetLValueReferenceType());
+
+ std::string decl_name(context.m_decl_name.getAsString());
+ ConstString entity_name(decl_name.c_str());
+ ClangExpressionVariable *entity(new ClangExpressionVariable(valobj));
+ m_found_entities.AddNewlyConstructedVariable(entity);
+
+ assert(entity);
+ entity->EnableParserVars(GetParserID());
+ ClangExpressionVariable::ParserVars *parser_vars =
+ entity->GetParserVars(GetParserID());
+ parser_vars->m_parser_type = pt;
+ parser_vars->m_named_decl = var_decl;
+ parser_vars->m_llvm_value = nullptr;
+ parser_vars->m_lldb_value = var_location;
+ parser_vars->m_lldb_var = var;
+
+ if (is_reference)
+ entity->m_flags |= ClangExpressionVariable::EVTypeIsReference;
+
+ if (log) {
+ ASTDumper orig_dumper(ut.GetOpaqueQualType());
+ ASTDumper ast_dumper(var_decl);
+ log->Printf(" CEDM::FEVD[%u] Found variable %s, returned %s (original %s)",
+ current_id, decl_name.c_str(), ast_dumper.GetCString(),
+ orig_dumper.GetCString());
+ }
+}
+
+void ClangExpressionDeclMap::AddOneVariable(NameSearchContext &context,
+ ExpressionVariableSP &pvar_sp,
+ unsigned int current_id) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ TypeFromUser user_type(
+ llvm::cast<ClangExpressionVariable>(pvar_sp.get())->GetTypeFromUser());
+
+ TypeFromParser parser_type(GuardedCopyType(user_type));
+
+ if (!parser_type.GetOpaqueQualType()) {
+ if (log)
+ log->Printf(" CEDM::FEVD[%u] Couldn't import type for pvar %s",
+ current_id, pvar_sp->GetName().GetCString());
+ return;
+ }
+
+ NamedDecl *var_decl =
+ context.AddVarDecl(parser_type.GetLValueReferenceType());
+
+ llvm::cast<ClangExpressionVariable>(pvar_sp.get())
+ ->EnableParserVars(GetParserID());
+ ClangExpressionVariable::ParserVars *parser_vars =
+ llvm::cast<ClangExpressionVariable>(pvar_sp.get())
+ ->GetParserVars(GetParserID());
+ parser_vars->m_parser_type = parser_type;
+ parser_vars->m_named_decl = var_decl;
+ parser_vars->m_llvm_value = nullptr;
+ parser_vars->m_lldb_value.Clear();
+
+ if (log) {
+ ASTDumper ast_dumper(var_decl);
+ log->Printf(" CEDM::FEVD[%u] Added pvar %s, returned %s", current_id,
+ pvar_sp->GetName().GetCString(), ast_dumper.GetCString());
+ }
+}
+
+void ClangExpressionDeclMap::AddOneGenericVariable(NameSearchContext &context,
+ const Symbol &symbol,
+ unsigned int current_id) {
+ assert(m_parser_vars.get());
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr();
+
+ if (target == nullptr)
+ return;
+
+ ASTContext *scratch_ast_context =
+ target->GetScratchClangASTContext()->getASTContext();
+
+ TypeFromUser user_type(
+ ClangASTContext::GetBasicType(scratch_ast_context, eBasicTypeVoid)
+ .GetPointerType()
+ .GetLValueReferenceType());
+ TypeFromParser parser_type(
+ ClangASTContext::GetBasicType(m_ast_context, eBasicTypeVoid)
+ .GetPointerType()
+ .GetLValueReferenceType());
+ NamedDecl *var_decl = context.AddVarDecl(parser_type);
+
+ std::string decl_name(context.m_decl_name.getAsString());
+ ConstString entity_name(decl_name.c_str());
+ ClangExpressionVariable *entity(new ClangExpressionVariable(
+ m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(), entity_name,
+ user_type, m_parser_vars->m_target_info.byte_order,
+ m_parser_vars->m_target_info.address_byte_size));
+ m_found_entities.AddNewlyConstructedVariable(entity);
+
+ entity->EnableParserVars(GetParserID());
+ ClangExpressionVariable::ParserVars *parser_vars =
+ entity->GetParserVars(GetParserID());
+
+ const Address symbol_address = symbol.GetAddress();
+ lldb::addr_t symbol_load_addr = symbol_address.GetLoadAddress(target);
+
+ // parser_vars->m_lldb_value.SetContext(Value::eContextTypeClangType,
+ // user_type.GetOpaqueQualType());
+ parser_vars->m_lldb_value.SetCompilerType(user_type);
+ parser_vars->m_lldb_value.GetScalar() = symbol_load_addr;
+ parser_vars->m_lldb_value.SetValueType(Value::eValueTypeLoadAddress);
+
+ parser_vars->m_parser_type = parser_type;
+ parser_vars->m_named_decl = var_decl;
+ parser_vars->m_llvm_value = nullptr;
+ parser_vars->m_lldb_sym = &symbol;
+
+ if (log) {
+ ASTDumper ast_dumper(var_decl);
+
+ log->Printf(" CEDM::FEVD[%u] Found variable %s, returned %s", current_id,
+ decl_name.c_str(), ast_dumper.GetCString());
+ }
+}
+
+bool ClangExpressionDeclMap::ResolveUnknownTypes() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+ Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr();
+
+ ClangASTContextForExpressions *scratch_ast_context =
+ static_cast<ClangASTContextForExpressions*>(
+ target->GetScratchClangASTContext());
+
+ for (size_t index = 0, num_entities = m_found_entities.GetSize();
+ index < num_entities; ++index) {
+ ExpressionVariableSP entity = m_found_entities.GetVariableAtIndex(index);
+
+ ClangExpressionVariable::ParserVars *parser_vars =
+ llvm::cast<ClangExpressionVariable>(entity.get())
+ ->GetParserVars(GetParserID());
+
+ if (entity->m_flags & ClangExpressionVariable::EVUnknownType) {
+ const NamedDecl *named_decl = parser_vars->m_named_decl;
+ const VarDecl *var_decl = dyn_cast<VarDecl>(named_decl);
+
+ if (!var_decl) {
+ if (log)
+ log->Printf("Entity of unknown type does not have a VarDecl");
+ return false;
+ }
+
+ if (log) {
+ ASTDumper ast_dumper(const_cast<VarDecl *>(var_decl));
+ log->Printf("Variable of unknown type now has Decl %s",
+ ast_dumper.GetCString());
+ }
+
+ QualType var_type = var_decl->getType();
+ TypeFromParser parser_type(
+ var_type.getAsOpaquePtr(),
+ ClangASTContext::GetASTContext(&var_decl->getASTContext()));
+
+ lldb::opaque_compiler_type_t copied_type = nullptr;
+ if (m_ast_importer_sp) {
+ copied_type = m_ast_importer_sp->CopyType(
+ scratch_ast_context->getASTContext(), &var_decl->getASTContext(),
+ var_type.getAsOpaquePtr());
+ } else if (HasMerger()) {
+ copied_type = CopyTypeWithMerger(
+ var_decl->getASTContext(),
+ scratch_ast_context->GetMergerUnchecked(),
+ var_type).getAsOpaquePtr();
+ } else {
+ lldbassert(0 && "No mechanism to copy a resolved unknown type!");
+ return false;
+ }
+
+ if (!copied_type) {
+ if (log)
+ log->Printf("ClangExpressionDeclMap::ResolveUnknownType - Couldn't "
+ "import the type for a variable");
+
+ return (bool)lldb::ExpressionVariableSP();
+ }
+
+ TypeFromUser user_type(copied_type, scratch_ast_context);
+
+ // parser_vars->m_lldb_value.SetContext(Value::eContextTypeClangType,
+ // user_type.GetOpaqueQualType());
+ parser_vars->m_lldb_value.SetCompilerType(user_type);
+ parser_vars->m_parser_type = parser_type;
+
+ entity->SetCompilerType(user_type);
+
+ entity->m_flags &= ~(ClangExpressionVariable::EVUnknownType);
+ }
+ }
+
+ return true;
+}
+
+void ClangExpressionDeclMap::AddOneRegister(NameSearchContext &context,
+ const RegisterInfo *reg_info,
+ unsigned int current_id) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ CompilerType clang_type =
+ ClangASTContext::GetBuiltinTypeForEncodingAndBitSize(
+ m_ast_context, reg_info->encoding, reg_info->byte_size * 8);
+
+ if (!clang_type) {
+ if (log)
+ log->Printf(" Tried to add a type for %s, but couldn't get one",
+ context.m_decl_name.getAsString().c_str());
+ return;
+ }
+
+ TypeFromParser parser_clang_type(clang_type);
+
+ NamedDecl *var_decl = context.AddVarDecl(parser_clang_type);
+
+ ClangExpressionVariable *entity(new ClangExpressionVariable(
+ m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(),
+ m_parser_vars->m_target_info.byte_order,
+ m_parser_vars->m_target_info.address_byte_size));
+ m_found_entities.AddNewlyConstructedVariable(entity);
+
+ std::string decl_name(context.m_decl_name.getAsString());
+ entity->SetName(ConstString(decl_name.c_str()));
+ entity->SetRegisterInfo(reg_info);
+ entity->EnableParserVars(GetParserID());
+ ClangExpressionVariable::ParserVars *parser_vars =
+ entity->GetParserVars(GetParserID());
+ parser_vars->m_parser_type = parser_clang_type;
+ parser_vars->m_named_decl = var_decl;
+ parser_vars->m_llvm_value = nullptr;
+ parser_vars->m_lldb_value.Clear();
+ entity->m_flags |= ClangExpressionVariable::EVBareRegister;
+
+ if (log) {
+ ASTDumper ast_dumper(var_decl);
+ log->Printf(" CEDM::FEVD[%d] Added register %s, returned %s", current_id,
+ context.m_decl_name.getAsString().c_str(),
+ ast_dumper.GetCString());
+ }
+}
+
+void ClangExpressionDeclMap::AddOneFunction(NameSearchContext &context,
+ Function *function, Symbol *symbol,
+ unsigned int current_id) {
+ assert(m_parser_vars.get());
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ NamedDecl *function_decl = nullptr;
+ Address fun_address;
+ CompilerType function_clang_type;
+
+ bool is_indirect_function = false;
+
+ if (function) {
+ Type *function_type = function->GetType();
+
+ const auto lang = function->GetCompileUnit()->GetLanguage();
+ const auto name = function->GetMangled().GetMangledName().AsCString();
+ const bool extern_c = (Language::LanguageIsC(lang) &&
+ !CPlusPlusLanguage::IsCPPMangledName(name)) ||
+ (Language::LanguageIsObjC(lang) &&
+ !Language::LanguageIsCPlusPlus(lang));
+
+ if (!extern_c) {
+ TypeSystem *type_system = function->GetDeclContext().GetTypeSystem();
+ if (llvm::isa<ClangASTContext>(type_system)) {
+ clang::DeclContext *src_decl_context =
+ (clang::DeclContext *)function->GetDeclContext()
+ .GetOpaqueDeclContext();
+ clang::FunctionDecl *src_function_decl =
+ llvm::dyn_cast_or_null<clang::FunctionDecl>(src_decl_context);
+ if (src_function_decl &&
+ src_function_decl->getTemplateSpecializationInfo()) {
+ clang::FunctionTemplateDecl *function_template =
+ src_function_decl->getTemplateSpecializationInfo()->getTemplate();
+ clang::FunctionTemplateDecl *copied_function_template =
+ llvm::dyn_cast_or_null<clang::FunctionTemplateDecl>(
+ CopyDecl(function_template));
+ if (copied_function_template) {
+ if (log) {
+ ASTDumper ast_dumper((clang::Decl *)copied_function_template);
+
+ StreamString ss;
+
+ function->DumpSymbolContext(&ss);
+
+ log->Printf(" CEDM::FEVD[%u] Imported decl for function template"
+ " %s (description %s), returned %s",
+ current_id,
+ copied_function_template->getNameAsString().c_str(),
+ ss.GetData(), ast_dumper.GetCString());
+ }
+
+ context.AddNamedDecl(copied_function_template);
+ }
+ } else if (src_function_decl) {
+ if (clang::FunctionDecl *copied_function_decl =
+ llvm::dyn_cast_or_null<clang::FunctionDecl>(
+ CopyDecl(src_function_decl))) {
+ if (log) {
+ ASTDumper ast_dumper((clang::Decl *)copied_function_decl);
+
+ StreamString ss;
+
+ function->DumpSymbolContext(&ss);
+
+ log->Printf(" CEDM::FEVD[%u] Imported decl for function %s "
+ "(description %s), returned %s",
+ current_id,
+ copied_function_decl->getNameAsString().c_str(),
+ ss.GetData(), ast_dumper.GetCString());
+ }
+
+ context.AddNamedDecl(copied_function_decl);
+ return;
+ } else {
+ if (log) {
+ log->Printf(" Failed to import the function decl for '%s'",
+ src_function_decl->getName().str().c_str());
+ }
+ }
+ }
+ }
+ }
+
+ if (!function_type) {
+ if (log)
+ log->PutCString(" Skipped a function because it has no type");
+ return;
+ }
+
+ function_clang_type = function_type->GetFullCompilerType();
+
+ if (!function_clang_type) {
+ if (log)
+ log->PutCString(" Skipped a function because it has no Clang type");
+ return;
+ }
+
+ fun_address = function->GetAddressRange().GetBaseAddress();
+
+ CompilerType copied_function_type = GuardedCopyType(function_clang_type);
+ if (copied_function_type) {
+ function_decl = context.AddFunDecl(copied_function_type, extern_c);
+
+ if (!function_decl) {
+ if (log) {
+ log->Printf(
+ " Failed to create a function decl for '%s' {0x%8.8" PRIx64 "}",
+ function_type->GetName().GetCString(), function_type->GetID());
+ }
+
+ return;
+ }
+ } else {
+ // We failed to copy the type we found
+ if (log) {
+ log->Printf(" Failed to import the function type '%s' {0x%8.8" PRIx64
+ "} into the expression parser AST contenxt",
+ function_type->GetName().GetCString(),
+ function_type->GetID());
+ }
+
+ return;
+ }
+ } else if (symbol) {
+ fun_address = symbol->GetAddress();
+ function_decl = context.AddGenericFunDecl();
+ is_indirect_function = symbol->IsIndirect();
+ } else {
+ if (log)
+ log->PutCString(" AddOneFunction called with no function and no symbol");
+ return;
+ }
+
+ Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr();
+
+ lldb::addr_t load_addr =
+ fun_address.GetCallableLoadAddress(target, is_indirect_function);
+
+ ClangExpressionVariable *entity(new ClangExpressionVariable(
+ m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(),
+ m_parser_vars->m_target_info.byte_order,
+ m_parser_vars->m_target_info.address_byte_size));
+ m_found_entities.AddNewlyConstructedVariable(entity);
+
+ std::string decl_name(context.m_decl_name.getAsString());
+ entity->SetName(ConstString(decl_name.c_str()));
+ entity->SetCompilerType(function_clang_type);
+ entity->EnableParserVars(GetParserID());
+
+ ClangExpressionVariable::ParserVars *parser_vars =
+ entity->GetParserVars(GetParserID());
+
+ if (load_addr != LLDB_INVALID_ADDRESS) {
+ parser_vars->m_lldb_value.SetValueType(Value::eValueTypeLoadAddress);
+ parser_vars->m_lldb_value.GetScalar() = load_addr;
+ } else {
+ // We have to try finding a file address.
+
+ lldb::addr_t file_addr = fun_address.GetFileAddress();
+
+ parser_vars->m_lldb_value.SetValueType(Value::eValueTypeFileAddress);
+ parser_vars->m_lldb_value.GetScalar() = file_addr;
+ }
+
+ parser_vars->m_named_decl = function_decl;
+ parser_vars->m_llvm_value = nullptr;
+
+ if (log) {
+ std::string function_str =
+ function_decl ? ASTDumper(function_decl).GetCString() : "nullptr";
+
+ StreamString ss;
+
+ fun_address.Dump(&ss,
+ m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(),
+ Address::DumpStyleResolvedDescription);
+
+ log->Printf(
+ " CEDM::FEVD[%u] Found %s function %s (description %s), returned %s",
+ current_id, (function ? "specific" : "generic"), decl_name.c_str(),
+ ss.GetData(), function_str.c_str());
+ }
+}
+
+void ClangExpressionDeclMap::AddThisType(NameSearchContext &context,
+ const TypeFromUser &ut,
+ unsigned int current_id) {
+ CompilerType copied_clang_type = GuardedCopyType(ut);
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (!copied_clang_type) {
+ if (log)
+ log->Printf(
+ "ClangExpressionDeclMap::AddThisType - Couldn't import the type");
+
+ return;
+ }
+
+ if (copied_clang_type.IsAggregateType() &&
+ copied_clang_type.GetCompleteType()) {
+ CompilerType void_clang_type =
+ ClangASTContext::GetBasicType(m_ast_context, eBasicTypeVoid);
+ CompilerType void_ptr_clang_type = void_clang_type.GetPointerType();
+
+ CompilerType method_type = ClangASTContext::CreateFunctionType(
+ m_ast_context, void_clang_type, &void_ptr_clang_type, 1, false, 0);
+
+ const bool is_virtual = false;
+ const bool is_static = false;
+ const bool is_inline = false;
+ const bool is_explicit = false;
+ const bool is_attr_used = true;
+ const bool is_artificial = false;
+
+ CXXMethodDecl *method_decl =
+ ClangASTContext::GetASTContext(m_ast_context)
+ ->AddMethodToCXXRecordType(
+ copied_clang_type.GetOpaqueQualType(), "$__lldb_expr", nullptr,
+ method_type, lldb::eAccessPublic, is_virtual, is_static,
+ is_inline, is_explicit, is_attr_used, is_artificial);
+
+ if (log) {
+ ASTDumper method_ast_dumper((clang::Decl *)method_decl);
+ ASTDumper type_ast_dumper(copied_clang_type);
+
+ log->Printf(" CEDM::AddThisType Added function $__lldb_expr "
+ "(description %s) for this type %s",
+ method_ast_dumper.GetCString(), type_ast_dumper.GetCString());
+ }
+ }
+
+ if (!copied_clang_type.IsValid())
+ return;
+
+ TypeSourceInfo *type_source_info = m_ast_context->getTrivialTypeSourceInfo(
+ QualType::getFromOpaquePtr(copied_clang_type.GetOpaqueQualType()));
+
+ if (!type_source_info)
+ return;
+
+ // Construct a typedef type because if "*this" is a templated type we can't
+ // just return ClassTemplateSpecializationDecls in response to name queries.
+ // Using a typedef makes this much more robust.
+
+ TypedefDecl *typedef_decl = TypedefDecl::Create(
+ *m_ast_context, m_ast_context->getTranslationUnitDecl(), SourceLocation(),
+ SourceLocation(), context.m_decl_name.getAsIdentifierInfo(),
+ type_source_info);
+
+ if (!typedef_decl)
+ return;
+
+ context.AddNamedDecl(typedef_decl);
+
+ return;
+}
+
+void ClangExpressionDeclMap::AddOneType(NameSearchContext &context,
+ const TypeFromUser &ut,
+ unsigned int current_id) {
+ CompilerType copied_clang_type = GuardedCopyType(ut);
+
+ if (!copied_clang_type) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log)
+ log->Printf(
+ "ClangExpressionDeclMap::AddOneType - Couldn't import the type");
+
+ return;
+ }
+
+ context.AddTypeDecl(copied_clang_type);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h
new file mode 100644
index 000000000000..03b73e6be391
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h
@@ -0,0 +1,564 @@
+//===-- ClangExpressionDeclMap.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 liblldb_ClangExpressionDeclMap_h_
+#define liblldb_ClangExpressionDeclMap_h_
+
+#include <signal.h>
+#include <stdint.h>
+
+#include <vector>
+
+#include "ClangASTSource.h"
+#include "ClangExpressionVariable.h"
+
+#include "lldb/Core/ClangForward.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Expression/Materializer.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/TaggedASTType.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/lldb-public.h"
+#include "clang/AST/Decl.h"
+#include "llvm/ADT/DenseMap.h"
+
+namespace lldb_private {
+
+/// \class ClangExpressionDeclMap ClangExpressionDeclMap.h
+/// "lldb/Expression/ClangExpressionDeclMap.h" Manages named entities that are
+/// defined in LLDB's debug information.
+///
+/// The Clang parser uses the ClangASTSource as an interface to request named
+/// entities from outside an expression. The ClangASTSource reports back,
+/// listing all possible objects corresponding to a particular name. But it
+/// in turn relies on ClangExpressionDeclMap, which performs several important
+/// functions.
+///
+/// First, it records what variables and functions were looked up and what
+/// Decls were returned for them.
+///
+/// Second, it constructs a struct on behalf of IRForTarget, recording which
+/// variables should be placed where and relaying this information back so
+/// that IRForTarget can generate context-independent code.
+///
+/// Third, it "materializes" this struct on behalf of the expression command,
+/// finding the current values of each variable and placing them into the
+/// struct so that it can be passed to the JITted version of the IR.
+///
+/// Fourth and finally, it "dematerializes" the struct after the JITted code
+/// has has executed, placing the new values back where it found the old ones.
+class ClangExpressionDeclMap : public ClangASTSource {
+public:
+ /// Constructor
+ ///
+ /// Initializes class variables.
+ ///
+ /// \param[in] keep_result_in_memory
+ /// If true, inhibits the normal deallocation of the memory for
+ /// the result persistent variable, and instead marks the variable
+ /// as persisting.
+ ///
+ /// \param[in] delegate
+ /// If non-NULL, use this delegate to report result values. This
+ /// allows the client ClangUserExpression to report a result.
+ ///
+ /// \param[in] exe_ctx
+ /// The execution context to use when parsing.
+ ///
+ /// \param[in] ctx_obj
+ /// If not empty, then expression is evaluated in context of this object.
+ /// See the comment to `UserExpression::Evaluate` for details.
+ ClangExpressionDeclMap(
+ bool keep_result_in_memory,
+ Materializer::PersistentVariableDelegate *result_delegate,
+ ExecutionContext &exe_ctx,
+ ValueObject *ctx_obj);
+
+ /// Destructor
+ ~ClangExpressionDeclMap() override;
+
+ /// Enable the state needed for parsing and IR transformation.
+ ///
+ /// \param[in] exe_ctx
+ /// The execution context to use when finding types for variables.
+ /// Also used to find a "scratch" AST context to store result types.
+ ///
+ /// \param[in] materializer
+ /// If non-NULL, the materializer to populate with information about
+ /// the variables to use
+ ///
+ /// \return
+ /// True if parsing is possible; false if it is unsafe to continue.
+ bool WillParse(ExecutionContext &exe_ctx, Materializer *materializer);
+
+ void InstallCodeGenerator(clang::ASTConsumer *code_gen);
+
+ /// [Used by ClangExpressionParser] For each variable that had an unknown
+ /// type at the beginning of parsing, determine its final type now.
+ ///
+ /// \return
+ /// True on success; false otherwise.
+ bool ResolveUnknownTypes();
+
+ /// Disable the state needed for parsing and IR transformation.
+ void DidParse();
+
+ /// [Used by IRForTarget] Add a variable to the list of persistent
+ /// variables for the process.
+ ///
+ /// \param[in] decl
+ /// The Clang declaration for the persistent variable, used for
+ /// lookup during parsing.
+ ///
+ /// \param[in] name
+ /// The name of the persistent variable, usually $something.
+ ///
+ /// \param[in] type
+ /// The type of the variable, in the Clang parser's context.
+ ///
+ /// \return
+ /// True on success; false otherwise.
+ bool AddPersistentVariable(const clang::NamedDecl *decl,
+ ConstString name, TypeFromParser type,
+ bool is_result, bool is_lvalue);
+
+ /// [Used by IRForTarget] Add a variable to the struct that needs to
+ /// be materialized each time the expression runs.
+ ///
+ /// \param[in] decl
+ /// The Clang declaration for the variable.
+ ///
+ /// \param[in] name
+ /// The name of the variable.
+ ///
+ /// \param[in] value
+ /// The LLVM IR value for this variable.
+ ///
+ /// \param[in] size
+ /// The size of the variable in bytes.
+ ///
+ /// \param[in] alignment
+ /// The required alignment of the variable in bytes.
+ ///
+ /// \return
+ /// True on success; false otherwise.
+ bool AddValueToStruct(const clang::NamedDecl *decl, ConstString name,
+ llvm::Value *value, size_t size,
+ lldb::offset_t alignment);
+
+ /// [Used by IRForTarget] Finalize the struct, laying out the position of
+ /// each object in it.
+ ///
+ /// \return
+ /// True on success; false otherwise.
+ bool DoStructLayout();
+
+ /// [Used by IRForTarget] Get general information about the laid-out struct
+ /// after DoStructLayout() has been called.
+ ///
+ /// \param[out] num_elements
+ /// The number of elements in the struct.
+ ///
+ /// \param[out] size
+ /// The size of the struct, in bytes.
+ ///
+ /// \param[out] alignment
+ /// The alignment of the struct, in bytes.
+ ///
+ /// \return
+ /// True if the information could be retrieved; false otherwise.
+ bool GetStructInfo(uint32_t &num_elements, size_t &size,
+ lldb::offset_t &alignment);
+
+ /// [Used by IRForTarget] Get specific information about one field of the
+ /// laid-out struct after DoStructLayout() has been called.
+ ///
+ /// \param[out] decl
+ /// The parsed Decl for the field, as generated by ClangASTSource
+ /// on ClangExpressionDeclMap's behalf. In the case of the result
+ /// value, this will have the name $__lldb_result even if the
+ /// result value ends up having the name $1. This is an
+ /// implementation detail of IRForTarget.
+ ///
+ /// \param[out] value
+ /// The IR value for the field (usually a GlobalVariable). In
+ /// the case of the result value, this will have the correct
+ /// name ($1, for instance). This is an implementation detail
+ /// of IRForTarget.
+ ///
+ /// \param[out] offset
+ /// The offset of the field from the beginning of the struct.
+ /// As long as the struct is aligned according to its required
+ /// alignment, this offset will align the field correctly.
+ ///
+ /// \param[out] name
+ /// The name of the field as used in materialization.
+ ///
+ /// \param[in] index
+ /// The index of the field about which information is requested.
+ ///
+ /// \return
+ /// True if the information could be retrieved; false otherwise.
+ bool GetStructElement(const clang::NamedDecl *&decl, llvm::Value *&value,
+ lldb::offset_t &offset, ConstString &name,
+ uint32_t index);
+
+ /// [Used by IRForTarget] Get information about a function given its Decl.
+ ///
+ /// \param[in] decl
+ /// The parsed Decl for the Function, as generated by ClangASTSource
+ /// on ClangExpressionDeclMap's behalf.
+ ///
+ /// \param[out] ptr
+ /// The absolute address of the function in the target.
+ ///
+ /// \return
+ /// True if the information could be retrieved; false otherwise.
+ bool GetFunctionInfo(const clang::NamedDecl *decl, uint64_t &ptr);
+
+ /// [Used by IRForTarget] Get the address of a symbol given nothing but its
+ /// name.
+ ///
+ /// \param[in] target
+ /// The target to find the symbol in. If not provided,
+ /// then the current parsing context's Target.
+ ///
+ /// \param[in] process
+ /// The process to use. For Objective-C symbols, the process's
+ /// Objective-C language runtime may be queried if the process
+ /// is non-NULL.
+ ///
+ /// \param[in] name
+ /// The name of the symbol.
+ ///
+ /// \param[in] module
+ /// The module to limit the search to. This can be NULL
+ ///
+ /// \return
+ /// Valid load address for the symbol
+ lldb::addr_t GetSymbolAddress(Target &target, Process *process,
+ ConstString name, lldb::SymbolType symbol_type,
+ Module *module = nullptr);
+
+ lldb::addr_t GetSymbolAddress(ConstString name,
+ lldb::SymbolType symbol_type);
+
+ /// [Used by IRInterpreter] Get basic target information.
+ ///
+ /// \param[out] byte_order
+ /// The byte order of the target.
+ ///
+ /// \param[out] address_byte_size
+ /// The size of a pointer in bytes.
+ ///
+ /// \return
+ /// True if the information could be determined; false
+ /// otherwise.
+ struct TargetInfo {
+ lldb::ByteOrder byte_order;
+ size_t address_byte_size;
+
+ TargetInfo() : byte_order(lldb::eByteOrderInvalid), address_byte_size(0) {}
+
+ bool IsValid() {
+ return (byte_order != lldb::eByteOrderInvalid && address_byte_size != 0);
+ }
+ };
+ TargetInfo GetTargetInfo();
+
+ /// [Used by ClangASTSource] Find all entities matching a given name, using
+ /// a NameSearchContext to make Decls for them.
+ ///
+ /// \param[in] context
+ /// The NameSearchContext that can construct Decls for this name.
+ ///
+ /// \return
+ /// True on success; false otherwise.
+ void FindExternalVisibleDecls(NameSearchContext &context) override;
+
+ /// Find all entities matching a given name in a given module/namespace,
+ /// using a NameSearchContext to make Decls for them.
+ ///
+ /// \param[in] context
+ /// The NameSearchContext that can construct Decls for this name.
+ ///
+ /// \param[in] module
+ /// If non-NULL, the module to query.
+ ///
+ /// \param[in] namespace_decl
+ /// If valid and module is non-NULL, the parent namespace.
+ ///
+ /// \param[in] current_id
+ /// The ID for the current FindExternalVisibleDecls invocation,
+ /// for logging purposes.
+ ///
+ /// \return
+ /// True on success; false otherwise.
+ void FindExternalVisibleDecls(NameSearchContext &context,
+ lldb::ModuleSP module,
+ CompilerDeclContext &namespace_decl,
+ unsigned int current_id);
+
+private:
+ ExpressionVariableList
+ m_found_entities; ///< All entities that were looked up for the parser.
+ ExpressionVariableList
+ m_struct_members; ///< All entities that need to be placed in the struct.
+ bool m_keep_result_in_memory; ///< True if result persistent variables
+ ///generated by this expression should stay in
+ ///memory.
+ Materializer::PersistentVariableDelegate
+ *m_result_delegate; ///< If non-NULL, used to report expression results to
+ ///ClangUserExpression.
+ ValueObject *m_ctx_obj; ///< If not empty, then expression is
+ ///evaluated in context of this object.
+ ///For details see the comment to
+ ///`UserExpression::Evaluate`.
+
+ /// The following values should not live beyond parsing
+ class ParserVars {
+ public:
+ ParserVars() {}
+
+ Target *GetTarget() {
+ if (m_exe_ctx.GetTargetPtr())
+ return m_exe_ctx.GetTargetPtr();
+ else if (m_sym_ctx.target_sp)
+ m_sym_ctx.target_sp.get();
+ return nullptr;
+ }
+
+ ExecutionContext m_exe_ctx; ///< The execution context to use when parsing.
+ SymbolContext m_sym_ctx; ///< The symbol context to use in finding variables
+ ///and types.
+ ClangPersistentVariables *m_persistent_vars =
+ nullptr; ///< The persistent variables for the process.
+ bool m_enable_lookups = false; ///< Set to true during parsing if we have
+ ///found the first "$__lldb" name.
+ TargetInfo m_target_info; ///< Basic information about the target.
+ Materializer *m_materializer = nullptr; ///< If non-NULL, the materializer
+ ///to use when reporting used
+ ///variables.
+ clang::ASTConsumer *m_code_gen = nullptr; ///< If non-NULL, a code generator
+ ///that receives new top-level
+ ///functions.
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ParserVars);
+ };
+
+ std::unique_ptr<ParserVars> m_parser_vars;
+
+ /// Activate parser-specific variables
+ void EnableParserVars() {
+ if (!m_parser_vars.get())
+ m_parser_vars = llvm::make_unique<ParserVars>();
+ }
+
+ /// Deallocate parser-specific variables
+ void DisableParserVars() { m_parser_vars.reset(); }
+
+ /// The following values contain layout information for the materialized
+ /// struct, but are not specific to a single materialization
+ struct StructVars {
+ StructVars()
+ : m_struct_alignment(0), m_struct_size(0), m_struct_laid_out(false),
+ m_result_name(), m_object_pointer_type(nullptr, nullptr) {}
+
+ lldb::offset_t
+ m_struct_alignment; ///< The alignment of the struct in bytes.
+ size_t m_struct_size; ///< The size of the struct in bytes.
+ bool m_struct_laid_out; ///< True if the struct has been laid out and the
+ ///layout is valid (that is, no new fields have been
+ ///added since).
+ ConstString
+ m_result_name; ///< The name of the result variable ($1, for example)
+ TypeFromUser m_object_pointer_type; ///< The type of the "this" variable, if
+ ///one exists
+ };
+
+ std::unique_ptr<StructVars> m_struct_vars;
+
+ /// Activate struct variables
+ void EnableStructVars() {
+ if (!m_struct_vars.get())
+ m_struct_vars.reset(new struct StructVars);
+ }
+
+ /// Deallocate struct variables
+ void DisableStructVars() { m_struct_vars.reset(); }
+
+ /// Get this parser's ID for use in extracting parser- and JIT-specific data
+ /// from persistent variables.
+ uint64_t GetParserID() { return (uint64_t) this; }
+
+ /// Given a target, find a variable that matches the given name and type.
+ ///
+ /// \param[in] target
+ /// The target to use as a basis for finding the variable.
+ ///
+ /// \param[in] module
+ /// If non-NULL, the module to search.
+ ///
+ /// \param[in] name
+ /// The name as a plain C string.
+ ///
+ /// \param[in] namespace_decl
+ /// If non-NULL and module is non-NULL, the parent namespace.
+ ///
+ /// \param[in] type
+ /// The required type for the variable. This function may be called
+ /// during parsing, in which case we don't know its type; hence the
+ /// default.
+ ///
+ /// \return
+ /// The LLDB Variable found, or NULL if none was found.
+ lldb::VariableSP FindGlobalVariable(Target &target, lldb::ModuleSP &module,
+ ConstString name,
+ CompilerDeclContext *namespace_decl,
+ TypeFromUser *type = nullptr);
+
+ /// Get the value of a variable in a given execution context and return the
+ /// associated Types if needed.
+ ///
+ /// \param[in] var
+ /// The variable to evaluate.
+ ///
+ /// \param[out] var_location
+ /// The variable location value to fill in
+ ///
+ /// \param[out] found_type
+ /// The type of the found value, as it was found in the user process.
+ /// This is only useful when the variable is being inspected on behalf
+ /// of the parser, hence the default.
+ ///
+ /// \param[out] parser_type
+ /// The type of the found value, as it was copied into the parser's
+ /// AST context. This is only useful when the variable is being
+ /// inspected on behalf of the parser, hence the default.
+ ///
+ /// \param[in] decl
+ /// The Decl to be looked up.
+ ///
+ /// \return
+ /// Return true if the value was successfully filled in.
+ bool GetVariableValue(lldb::VariableSP &var,
+ lldb_private::Value &var_location,
+ TypeFromUser *found_type = nullptr,
+ TypeFromParser *parser_type = nullptr);
+
+ /// Use the NameSearchContext to generate a Decl for the given LLDB
+ /// Variable, and put it in the Tuple list.
+ ///
+ /// \param[in] context
+ /// The NameSearchContext to use when constructing the Decl.
+ ///
+ /// \param[in] var
+ /// The LLDB Variable that needs a Decl.
+ ///
+ /// \param[in] valobj
+ /// The LLDB ValueObject for that variable.
+ void AddOneVariable(NameSearchContext &context, lldb::VariableSP var,
+ lldb::ValueObjectSP valobj, unsigned int current_id);
+
+ /// Use the NameSearchContext to generate a Decl for the given persistent
+ /// variable, and put it in the list of found entities.
+ ///
+ /// \param[in] context
+ /// The NameSearchContext to use when constructing the Decl.
+ ///
+ /// \param[in] pvar
+ /// The persistent variable that needs a Decl.
+ ///
+ /// \param[in] current_id
+ /// The ID of the current invocation of FindExternalVisibleDecls
+ /// for logging purposes.
+ void AddOneVariable(NameSearchContext &context,
+ lldb::ExpressionVariableSP &pvar_sp,
+ unsigned int current_id);
+
+ /// Use the NameSearchContext to generate a Decl for the given LLDB symbol
+ /// (treated as a variable), and put it in the list of found entities.
+ ///
+ /// \param[in] context
+ /// The NameSearchContext to use when constructing the Decl.
+ ///
+ /// \param[in] var
+ /// The LLDB Variable that needs a Decl.
+ void AddOneGenericVariable(NameSearchContext &context, const Symbol &symbol,
+ unsigned int current_id);
+
+ /// Use the NameSearchContext to generate a Decl for the given function.
+ /// (Functions are not placed in the Tuple list.) Can handle both fully
+ /// typed functions and generic functions.
+ ///
+ /// \param[in] context
+ /// The NameSearchContext to use when constructing the Decl.
+ ///
+ /// \param[in] fun
+ /// The Function that needs to be created. If non-NULL, this is
+ /// a fully-typed function.
+ ///
+ /// \param[in] sym
+ /// The Symbol that corresponds to a function that needs to be
+ /// created with generic type (unitptr_t foo(...)).
+ void AddOneFunction(NameSearchContext &context, Function *fun, Symbol *sym,
+ unsigned int current_id);
+
+ /// Use the NameSearchContext to generate a Decl for the given register.
+ ///
+ /// \param[in] context
+ /// The NameSearchContext to use when constructing the Decl.
+ ///
+ /// \param[in] reg_info
+ /// The information corresponding to that register.
+ void AddOneRegister(NameSearchContext &context, const RegisterInfo *reg_info,
+ unsigned int current_id);
+
+ /// Use the NameSearchContext to generate a Decl for the given type. (Types
+ /// are not placed in the Tuple list.)
+ ///
+ /// \param[in] context
+ /// The NameSearchContext to use when constructing the Decl.
+ ///
+ /// \param[in] type
+ /// The type that needs to be created.
+ void AddOneType(NameSearchContext &context, const TypeFromUser &type,
+ unsigned int current_id);
+
+ /// Generate a Decl for "*this" and add a member function declaration to it
+ /// for the expression, then report it.
+ ///
+ /// \param[in] context
+ /// The NameSearchContext to use when constructing the Decl.
+ ///
+ /// \param[in] type
+ /// The type for *this.
+ void AddThisType(NameSearchContext &context, const TypeFromUser &type,
+ unsigned int current_id);
+
+ /// Move a type out of the current ASTContext into another, but make sure to
+ /// export all components of the type also.
+ ///
+ /// \param[in] target
+ /// The ClangASTContext to move to.
+ /// \param[in] source
+ /// The ClangASTContext to move from. This is assumed to be going away.
+ /// \param[in] parser_type
+ /// The type as it appears in the source context.
+ ///
+ /// \return
+ /// Returns the moved type, or an empty type if there was a problem.
+ TypeFromUser DeportType(ClangASTContext &target, ClangASTContext &source,
+ TypeFromParser parser_type);
+
+ ClangASTContext *GetClangASTContext();
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ClangExpressionDeclMap_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionHelper.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionHelper.h
new file mode 100644
index 000000000000..48da5abb9126
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionHelper.h
@@ -0,0 +1,60 @@
+//===-- ClangExpressionHelper.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 liblldb_ClangExpression_h_
+#define liblldb_ClangExpression_h_
+
+#include <map>
+#include <string>
+#include <vector>
+
+
+#include "lldb/Core/ClangForward.h"
+#include "lldb/Expression/ExpressionTypeSystemHelper.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class RecordingMemoryManager;
+
+// ClangExpressionHelper
+class ClangExpressionHelper : public ExpressionTypeSystemHelper {
+public:
+ static bool classof(const ExpressionTypeSystemHelper *ts) {
+ return ts->getKind() == eKindClangHelper;
+ }
+
+ ClangExpressionHelper()
+ : ExpressionTypeSystemHelper(
+ ExpressionTypeSystemHelper::LLVMCastKind::eKindClangHelper) {}
+
+ /// Destructor
+ virtual ~ClangExpressionHelper() {}
+
+ /// Return the object that the parser should use when resolving external
+ /// values. May be NULL if everything should be self-contained.
+ virtual ClangExpressionDeclMap *DeclMap() = 0;
+
+ /// Return the object that the parser should allow to access ASTs.
+ /// May be NULL if the ASTs do not need to be transformed.
+ ///
+ /// \param[in] passthrough
+ /// The ASTConsumer that the returned transformer should send
+ /// the ASTs to after transformation.
+ virtual clang::ASTConsumer *
+ ASTTransformer(clang::ASTConsumer *passthrough) = 0;
+
+ virtual void CommitPersistentDecls() {}
+
+protected:
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ClangExpression_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
new file mode 100644
index 000000000000..7d13891ded8d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
@@ -0,0 +1,1383 @@
+//===-- ClangExpressionParser.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 "clang/AST/ASTContext.h"
+#include "clang/AST/ASTDiagnostic.h"
+#include "clang/AST/ExternalASTSource.h"
+#include "clang/AST/PrettyPrinter.h"
+#include "clang/Basic/DiagnosticIDs.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Basic/Version.h"
+#include "clang/CodeGen/CodeGenAction.h"
+#include "clang/CodeGen/ModuleBuilder.h"
+#include "clang/Edit/Commit.h"
+#include "clang/Edit/EditedSource.h"
+#include "clang/Edit/EditsReceiver.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Frontend/FrontendDiagnostic.h"
+#include "clang/Frontend/FrontendPluginRegistry.h"
+#include "clang/Frontend/TextDiagnosticBuffer.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Parse/ParseAST.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Rewrite/Frontend/FrontendActions.h"
+#include "clang/Sema/CodeCompleteConsumer.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaConsumer.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ExecutionEngine/ExecutionEngine.h"
+#include "llvm/Support/CrashRecoveryContext.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/TargetSelect.h"
+
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Signals.h"
+
+#include "ClangDiagnostic.h"
+#include "ClangExpressionParser.h"
+#include "ClangUserExpression.h"
+
+#include "ASTUtils.h"
+#include "ClangASTSource.h"
+#include "ClangDiagnostic.h"
+#include "ClangExpressionDeclMap.h"
+#include "ClangExpressionHelper.h"
+#include "ClangExpressionParser.h"
+#include "ClangHost.h"
+#include "ClangModulesDeclVendor.h"
+#include "ClangPersistentVariables.h"
+#include "IRDynamicChecks.h"
+#include "IRForTarget.h"
+#include "ModuleDependencyCollector.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Expression/IRExecutionUnit.h"
+#include "lldb/Expression/IRInterpreter.h"
+#include "lldb/Host/File.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadPlanCallFunction.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Reproducer.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StringList.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+#include <cctype>
+#include <memory>
+
+using namespace clang;
+using namespace llvm;
+using namespace lldb_private;
+
+//===----------------------------------------------------------------------===//
+// Utility Methods for Clang
+//===----------------------------------------------------------------------===//
+
+class ClangExpressionParser::LLDBPreprocessorCallbacks : public PPCallbacks {
+ ClangModulesDeclVendor &m_decl_vendor;
+ ClangPersistentVariables &m_persistent_vars;
+ StreamString m_error_stream;
+ bool m_has_errors = false;
+
+public:
+ LLDBPreprocessorCallbacks(ClangModulesDeclVendor &decl_vendor,
+ ClangPersistentVariables &persistent_vars)
+ : m_decl_vendor(decl_vendor), m_persistent_vars(persistent_vars) {}
+
+ void moduleImport(SourceLocation import_location, clang::ModuleIdPath path,
+ const clang::Module * /*null*/) override {
+ SourceModule module;
+
+ for (const std::pair<IdentifierInfo *, SourceLocation> &component : path)
+ module.path.push_back(ConstString(component.first->getName()));
+
+ StreamString error_stream;
+
+ ClangModulesDeclVendor::ModuleVector exported_modules;
+ if (!m_decl_vendor.AddModule(module, &exported_modules, m_error_stream))
+ m_has_errors = true;
+
+ for (ClangModulesDeclVendor::ModuleID module : exported_modules)
+ m_persistent_vars.AddHandLoadedClangModule(module);
+ }
+
+ bool hasErrors() { return m_has_errors; }
+
+ llvm::StringRef getErrorString() { return m_error_stream.GetString(); }
+};
+
+class ClangDiagnosticManagerAdapter : public clang::DiagnosticConsumer {
+public:
+ ClangDiagnosticManagerAdapter()
+ : m_passthrough(new clang::TextDiagnosticBuffer) {}
+
+ ClangDiagnosticManagerAdapter(
+ const std::shared_ptr<clang::TextDiagnosticBuffer> &passthrough)
+ : m_passthrough(passthrough) {}
+
+ void ResetManager(DiagnosticManager *manager = nullptr) {
+ m_manager = manager;
+ }
+
+ void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
+ const clang::Diagnostic &Info) override {
+ if (m_manager) {
+ llvm::SmallVector<char, 32> diag_str;
+ Info.FormatDiagnostic(diag_str);
+ diag_str.push_back('\0');
+ const char *data = diag_str.data();
+
+ lldb_private::DiagnosticSeverity severity;
+ bool make_new_diagnostic = true;
+
+ switch (DiagLevel) {
+ case DiagnosticsEngine::Level::Fatal:
+ case DiagnosticsEngine::Level::Error:
+ severity = eDiagnosticSeverityError;
+ break;
+ case DiagnosticsEngine::Level::Warning:
+ severity = eDiagnosticSeverityWarning;
+ break;
+ case DiagnosticsEngine::Level::Remark:
+ case DiagnosticsEngine::Level::Ignored:
+ severity = eDiagnosticSeverityRemark;
+ break;
+ case DiagnosticsEngine::Level::Note:
+ m_manager->AppendMessageToDiagnostic(data);
+ make_new_diagnostic = false;
+ }
+ if (make_new_diagnostic) {
+ ClangDiagnostic *new_diagnostic =
+ new ClangDiagnostic(data, severity, Info.getID());
+ m_manager->AddDiagnostic(new_diagnostic);
+
+ // Don't store away warning fixits, since the compiler doesn't have
+ // enough context in an expression for the warning to be useful.
+ // FIXME: Should we try to filter out FixIts that apply to our generated
+ // code, and not the user's expression?
+ if (severity == eDiagnosticSeverityError) {
+ size_t num_fixit_hints = Info.getNumFixItHints();
+ for (size_t i = 0; i < num_fixit_hints; i++) {
+ const clang::FixItHint &fixit = Info.getFixItHint(i);
+ if (!fixit.isNull())
+ new_diagnostic->AddFixitHint(fixit);
+ }
+ }
+ }
+ }
+
+ m_passthrough->HandleDiagnostic(DiagLevel, Info);
+ }
+
+ void FlushDiagnostics(DiagnosticsEngine &Diags) {
+ m_passthrough->FlushDiagnostics(Diags);
+ }
+
+ DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const {
+ return new ClangDiagnosticManagerAdapter(m_passthrough);
+ }
+
+ clang::TextDiagnosticBuffer *GetPassthrough() { return m_passthrough.get(); }
+
+private:
+ DiagnosticManager *m_manager = nullptr;
+ std::shared_ptr<clang::TextDiagnosticBuffer> m_passthrough;
+};
+
+static void
+SetupModuleHeaderPaths(CompilerInstance *compiler,
+ std::vector<ConstString> include_directories,
+ lldb::TargetSP target_sp) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ HeaderSearchOptions &search_opts = compiler->getHeaderSearchOpts();
+
+ for (ConstString dir : include_directories) {
+ search_opts.AddPath(dir.AsCString(), frontend::System, false, true);
+ LLDB_LOG(log, "Added user include dir: {0}", dir);
+ }
+
+ llvm::SmallString<128> module_cache;
+ auto props = ModuleList::GetGlobalModuleListProperties();
+ props.GetClangModulesCachePath().GetPath(module_cache);
+ search_opts.ModuleCachePath = module_cache.str();
+ LLDB_LOG(log, "Using module cache path: {0}", module_cache.c_str());
+
+ FileSpec clang_resource_dir = GetClangResourceDir();
+ std::string resource_dir = clang_resource_dir.GetPath();
+ if (FileSystem::Instance().IsDirectory(resource_dir)) {
+ search_opts.ResourceDir = resource_dir;
+ std::string resource_include = resource_dir + "/include";
+ search_opts.AddPath(resource_include, frontend::System, false, true);
+
+ LLDB_LOG(log, "Added resource include dir: {0}", resource_include);
+ }
+
+ search_opts.ImplicitModuleMaps = true;
+
+ std::vector<std::string> system_include_directories =
+ target_sp->GetPlatform()->GetSystemIncludeDirectories(
+ lldb::eLanguageTypeC_plus_plus);
+
+ for (const std::string &include_dir : system_include_directories) {
+ search_opts.AddPath(include_dir, frontend::System, false, true);
+
+ LLDB_LOG(log, "Added system include dir: {0}", include_dir);
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Implementation of ClangExpressionParser
+//===----------------------------------------------------------------------===//
+
+ClangExpressionParser::ClangExpressionParser(
+ ExecutionContextScope *exe_scope, Expression &expr,
+ bool generate_debug_info, std::vector<ConstString> include_directories)
+ : ExpressionParser(exe_scope, expr, generate_debug_info), m_compiler(),
+ m_pp_callbacks(nullptr),
+ m_include_directories(std::move(include_directories)) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ // We can't compile expressions without a target. So if the exe_scope is
+ // null or doesn't have a target, then we just need to get out of here. I'll
+ // lldb_assert and not make any of the compiler objects since
+ // I can't return errors directly from the constructor. Further calls will
+ // check if the compiler was made and
+ // bag out if it wasn't.
+
+ if (!exe_scope) {
+ lldb_assert(exe_scope, "Can't make an expression parser with a null scope.",
+ __FUNCTION__, __FILE__, __LINE__);
+ return;
+ }
+
+ lldb::TargetSP target_sp;
+ target_sp = exe_scope->CalculateTarget();
+ if (!target_sp) {
+ lldb_assert(target_sp.get(),
+ "Can't make an expression parser with a null target.",
+ __FUNCTION__, __FILE__, __LINE__);
+ return;
+ }
+
+ // 1. Create a new compiler instance.
+ m_compiler.reset(new CompilerInstance());
+
+ // When capturing a reproducer, hook up the file collector with clang to
+ // collector modules and headers.
+ if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) {
+ repro::FileProvider &fp = g->GetOrCreate<repro::FileProvider>();
+ m_compiler->setModuleDepCollector(
+ std::make_shared<ModuleDependencyCollectorAdaptor>(
+ fp.GetFileCollector()));
+ DependencyOutputOptions &opts = m_compiler->getDependencyOutputOpts();
+ opts.IncludeSystemHeaders = true;
+ opts.IncludeModuleFiles = true;
+ }
+
+ // Make sure clang uses the same VFS as LLDB.
+ m_compiler->createFileManager(FileSystem::Instance().GetVirtualFileSystem());
+
+ lldb::LanguageType frame_lang =
+ expr.Language(); // defaults to lldb::eLanguageTypeUnknown
+ bool overridden_target_opts = false;
+ lldb_private::LanguageRuntime *lang_rt = nullptr;
+
+ std::string abi;
+ ArchSpec target_arch;
+ target_arch = target_sp->GetArchitecture();
+
+ const auto target_machine = target_arch.GetMachine();
+
+ // If the expression is being evaluated in the context of an existing stack
+ // frame, we introspect to see if the language runtime is available.
+
+ lldb::StackFrameSP frame_sp = exe_scope->CalculateStackFrame();
+ lldb::ProcessSP process_sp = exe_scope->CalculateProcess();
+
+ // Make sure the user hasn't provided a preferred execution language with
+ // `expression --language X -- ...`
+ if (frame_sp && frame_lang == lldb::eLanguageTypeUnknown)
+ frame_lang = frame_sp->GetLanguage();
+
+ if (process_sp && frame_lang != lldb::eLanguageTypeUnknown) {
+ lang_rt = process_sp->GetLanguageRuntime(frame_lang);
+ if (log)
+ log->Printf("Frame has language of type %s",
+ Language::GetNameForLanguageType(frame_lang));
+ }
+
+ // 2. Configure the compiler with a set of default options that are
+ // appropriate for most situations.
+ if (target_arch.IsValid()) {
+ std::string triple = target_arch.GetTriple().str();
+ m_compiler->getTargetOpts().Triple = triple;
+ if (log)
+ log->Printf("Using %s as the target triple",
+ m_compiler->getTargetOpts().Triple.c_str());
+ } else {
+ // If we get here we don't have a valid target and just have to guess.
+ // Sometimes this will be ok to just use the host target triple (when we
+ // evaluate say "2+3", but other expressions like breakpoint conditions and
+ // other things that _are_ target specific really shouldn't just be using
+ // the host triple. In such a case the language runtime should expose an
+ // overridden options set (3), below.
+ m_compiler->getTargetOpts().Triple = llvm::sys::getDefaultTargetTriple();
+ if (log)
+ log->Printf("Using default target triple of %s",
+ m_compiler->getTargetOpts().Triple.c_str());
+ }
+ // Now add some special fixes for known architectures: Any arm32 iOS
+ // environment, but not on arm64
+ if (m_compiler->getTargetOpts().Triple.find("arm64") == std::string::npos &&
+ m_compiler->getTargetOpts().Triple.find("arm") != std::string::npos &&
+ m_compiler->getTargetOpts().Triple.find("ios") != std::string::npos) {
+ m_compiler->getTargetOpts().ABI = "apcs-gnu";
+ }
+ // Supported subsets of x86
+ if (target_machine == llvm::Triple::x86 ||
+ target_machine == llvm::Triple::x86_64) {
+ m_compiler->getTargetOpts().Features.push_back("+sse");
+ m_compiler->getTargetOpts().Features.push_back("+sse2");
+ }
+
+ // Set the target CPU to generate code for. This will be empty for any CPU
+ // that doesn't really need to make a special
+ // CPU string.
+ m_compiler->getTargetOpts().CPU = target_arch.GetClangTargetCPU();
+
+ // Set the target ABI
+ abi = GetClangTargetABI(target_arch);
+ if (!abi.empty())
+ m_compiler->getTargetOpts().ABI = abi;
+
+ // 3. Now allow the runtime to provide custom configuration options for the
+ // target. In this case, a specialized language runtime is available and we
+ // can query it for extra options. For 99% of use cases, this will not be
+ // needed and should be provided when basic platform detection is not enough.
+ if (lang_rt)
+ overridden_target_opts =
+ lang_rt->GetOverrideExprOptions(m_compiler->getTargetOpts());
+
+ if (overridden_target_opts)
+ if (log && log->GetVerbose()) {
+ LLDB_LOGV(
+ log, "Using overridden target options for the expression evaluation");
+
+ auto opts = m_compiler->getTargetOpts();
+ LLDB_LOGV(log, "Triple: '{0}'", opts.Triple);
+ LLDB_LOGV(log, "CPU: '{0}'", opts.CPU);
+ LLDB_LOGV(log, "FPMath: '{0}'", opts.FPMath);
+ LLDB_LOGV(log, "ABI: '{0}'", opts.ABI);
+ LLDB_LOGV(log, "LinkerVersion: '{0}'", opts.LinkerVersion);
+ StringList::LogDump(log, opts.FeaturesAsWritten, "FeaturesAsWritten");
+ StringList::LogDump(log, opts.Features, "Features");
+ }
+
+ // 4. Create and install the target on the compiler.
+ m_compiler->createDiagnostics();
+ auto target_info = TargetInfo::CreateTargetInfo(
+ m_compiler->getDiagnostics(), m_compiler->getInvocation().TargetOpts);
+ if (log) {
+ log->Printf("Using SIMD alignment: %d", target_info->getSimdDefaultAlign());
+ log->Printf("Target datalayout string: '%s'",
+ target_info->getDataLayout().getStringRepresentation().c_str());
+ log->Printf("Target ABI: '%s'", target_info->getABI().str().c_str());
+ log->Printf("Target vector alignment: %d",
+ target_info->getMaxVectorAlign());
+ }
+ m_compiler->setTarget(target_info);
+
+ assert(m_compiler->hasTarget());
+
+ // 5. Set language options.
+ lldb::LanguageType language = expr.Language();
+ LangOptions &lang_opts = m_compiler->getLangOpts();
+
+ switch (language) {
+ case lldb::eLanguageTypeC:
+ case lldb::eLanguageTypeC89:
+ case lldb::eLanguageTypeC99:
+ case lldb::eLanguageTypeC11:
+ // FIXME: the following language option is a temporary workaround,
+ // to "ask for C, get C++."
+ // For now, the expression parser must use C++ anytime the language is a C
+ // family language, because the expression parser uses features of C++ to
+ // capture values.
+ lang_opts.CPlusPlus = true;
+ break;
+ case lldb::eLanguageTypeObjC:
+ lang_opts.ObjC = true;
+ // FIXME: the following language option is a temporary workaround,
+ // to "ask for ObjC, get ObjC++" (see comment above).
+ lang_opts.CPlusPlus = true;
+
+ // Clang now sets as default C++14 as the default standard (with
+ // GNU extensions), so we do the same here to avoid mismatches that
+ // cause compiler error when evaluating expressions (e.g. nullptr not found
+ // as it's a C++11 feature). Currently lldb evaluates C++14 as C++11 (see
+ // two lines below) so we decide to be consistent with that, but this could
+ // be re-evaluated in the future.
+ lang_opts.CPlusPlus11 = true;
+ break;
+ case lldb::eLanguageTypeC_plus_plus:
+ case lldb::eLanguageTypeC_plus_plus_11:
+ case lldb::eLanguageTypeC_plus_plus_14:
+ lang_opts.CPlusPlus11 = true;
+ m_compiler->getHeaderSearchOpts().UseLibcxx = true;
+ LLVM_FALLTHROUGH;
+ case lldb::eLanguageTypeC_plus_plus_03:
+ lang_opts.CPlusPlus = true;
+ if (process_sp)
+ lang_opts.ObjC =
+ process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC) != nullptr;
+ break;
+ case lldb::eLanguageTypeObjC_plus_plus:
+ case lldb::eLanguageTypeUnknown:
+ default:
+ lang_opts.ObjC = true;
+ lang_opts.CPlusPlus = true;
+ lang_opts.CPlusPlus11 = true;
+ m_compiler->getHeaderSearchOpts().UseLibcxx = true;
+ break;
+ }
+
+ lang_opts.Bool = true;
+ lang_opts.WChar = true;
+ lang_opts.Blocks = true;
+ lang_opts.DebuggerSupport =
+ true; // Features specifically for debugger clients
+ if (expr.DesiredResultType() == Expression::eResultTypeId)
+ lang_opts.DebuggerCastResultToId = true;
+
+ lang_opts.CharIsSigned = ArchSpec(m_compiler->getTargetOpts().Triple.c_str())
+ .CharIsSignedByDefault();
+
+ // Spell checking is a nice feature, but it ends up completing a lot of types
+ // that we didn't strictly speaking need to complete. As a result, we spend a
+ // long time parsing and importing debug information.
+ lang_opts.SpellChecking = false;
+
+ auto *clang_expr = dyn_cast<ClangUserExpression>(&m_expr);
+ if (clang_expr && clang_expr->DidImportCxxModules()) {
+ LLDB_LOG(log, "Adding lang options for importing C++ modules");
+
+ lang_opts.Modules = true;
+ // We want to implicitly build modules.
+ lang_opts.ImplicitModules = true;
+ // To automatically import all submodules when we import 'std'.
+ lang_opts.ModulesLocalVisibility = false;
+
+ // We use the @import statements, so we need this:
+ // FIXME: We could use the modules-ts, but that currently doesn't work.
+ lang_opts.ObjC = true;
+
+ // Options we need to parse libc++ code successfully.
+ // FIXME: We should ask the driver for the appropriate default flags.
+ lang_opts.GNUMode = true;
+ lang_opts.GNUKeywords = true;
+ lang_opts.DoubleSquareBracketAttributes = true;
+ lang_opts.CPlusPlus11 = true;
+
+ SetupModuleHeaderPaths(m_compiler.get(), m_include_directories,
+ target_sp);
+ }
+
+ if (process_sp && lang_opts.ObjC) {
+ if (auto *runtime = ObjCLanguageRuntime::Get(*process_sp)) {
+ if (runtime->GetRuntimeVersion() ==
+ ObjCLanguageRuntime::ObjCRuntimeVersions::eAppleObjC_V2)
+ lang_opts.ObjCRuntime.set(ObjCRuntime::MacOSX, VersionTuple(10, 7));
+ else
+ lang_opts.ObjCRuntime.set(ObjCRuntime::FragileMacOSX,
+ VersionTuple(10, 7));
+
+ if (runtime->HasNewLiteralsAndIndexing())
+ lang_opts.DebuggerObjCLiteral = true;
+ }
+ }
+
+ lang_opts.ThreadsafeStatics = false;
+ lang_opts.AccessControl = false; // Debuggers get universal access
+ lang_opts.DollarIdents = true; // $ indicates a persistent variable name
+ // We enable all builtin functions beside the builtins from libc/libm (e.g.
+ // 'fopen'). Those libc functions are already correctly handled by LLDB, and
+ // additionally enabling them as expandable builtins is breaking Clang.
+ lang_opts.NoBuiltin = true;
+
+ // Set CodeGen options
+ m_compiler->getCodeGenOpts().EmitDeclMetadata = true;
+ m_compiler->getCodeGenOpts().InstrumentFunctions = false;
+ m_compiler->getCodeGenOpts().DisableFPElim = true;
+ m_compiler->getCodeGenOpts().OmitLeafFramePointer = false;
+ if (generate_debug_info)
+ m_compiler->getCodeGenOpts().setDebugInfo(codegenoptions::FullDebugInfo);
+ else
+ m_compiler->getCodeGenOpts().setDebugInfo(codegenoptions::NoDebugInfo);
+
+ // Disable some warnings.
+ m_compiler->getDiagnostics().setSeverityForGroup(
+ clang::diag::Flavor::WarningOrError, "unused-value",
+ clang::diag::Severity::Ignored, SourceLocation());
+ m_compiler->getDiagnostics().setSeverityForGroup(
+ clang::diag::Flavor::WarningOrError, "odr",
+ clang::diag::Severity::Ignored, SourceLocation());
+
+ // Inform the target of the language options
+ //
+ // FIXME: We shouldn't need to do this, the target should be immutable once
+ // created. This complexity should be lifted elsewhere.
+ m_compiler->getTarget().adjust(m_compiler->getLangOpts());
+
+ // 6. Set up the diagnostic buffer for reporting errors
+
+ m_compiler->getDiagnostics().setClient(new ClangDiagnosticManagerAdapter);
+
+ // 7. Set up the source management objects inside the compiler
+ m_compiler->createFileManager();
+ if (!m_compiler->hasSourceManager())
+ m_compiler->createSourceManager(m_compiler->getFileManager());
+ m_compiler->createPreprocessor(TU_Complete);
+
+ if (ClangModulesDeclVendor *decl_vendor =
+ target_sp->GetClangModulesDeclVendor()) {
+ ClangPersistentVariables *clang_persistent_vars =
+ llvm::cast<ClangPersistentVariables>(
+ target_sp->GetPersistentExpressionStateForLanguage(
+ lldb::eLanguageTypeC));
+ std::unique_ptr<PPCallbacks> pp_callbacks(
+ new LLDBPreprocessorCallbacks(*decl_vendor, *clang_persistent_vars));
+ m_pp_callbacks =
+ static_cast<LLDBPreprocessorCallbacks *>(pp_callbacks.get());
+ m_compiler->getPreprocessor().addPPCallbacks(std::move(pp_callbacks));
+ }
+
+ // 8. Most of this we get from the CompilerInstance, but we also want to give
+ // the context an ExternalASTSource.
+
+ auto &PP = m_compiler->getPreprocessor();
+ auto &builtin_context = PP.getBuiltinInfo();
+ builtin_context.initializeBuiltins(PP.getIdentifierTable(),
+ m_compiler->getLangOpts());
+
+ m_compiler->createASTContext();
+ clang::ASTContext &ast_context = m_compiler->getASTContext();
+
+ m_ast_context.reset(
+ new ClangASTContext(m_compiler->getTargetOpts().Triple.c_str()));
+ m_ast_context->setASTContext(&ast_context);
+
+ std::string module_name("$__lldb_module");
+
+ m_llvm_context.reset(new LLVMContext());
+ m_code_generator.reset(CreateLLVMCodeGen(
+ m_compiler->getDiagnostics(), module_name,
+ m_compiler->getHeaderSearchOpts(), m_compiler->getPreprocessorOpts(),
+ m_compiler->getCodeGenOpts(), *m_llvm_context));
+}
+
+ClangExpressionParser::~ClangExpressionParser() {}
+
+namespace {
+
+/// \class CodeComplete
+///
+/// A code completion consumer for the clang Sema that is responsible for
+/// creating the completion suggestions when a user requests completion
+/// of an incomplete `expr` invocation.
+class CodeComplete : public CodeCompleteConsumer {
+ CodeCompletionTUInfo m_info;
+
+ std::string m_expr;
+ unsigned m_position = 0;
+ CompletionRequest &m_request;
+ /// The printing policy we use when printing declarations for our completion
+ /// descriptions.
+ clang::PrintingPolicy m_desc_policy;
+
+ /// Returns true if the given character can be used in an identifier.
+ /// This also returns true for numbers because for completion we usually
+ /// just iterate backwards over iterators.
+ ///
+ /// Note: lldb uses '$' in its internal identifiers, so we also allow this.
+ static bool IsIdChar(char c) {
+ return c == '_' || std::isalnum(c) || c == '$';
+ }
+
+ /// Returns true if the given character is used to separate arguments
+ /// in the command line of lldb.
+ static bool IsTokenSeparator(char c) { return c == ' ' || c == '\t'; }
+
+ /// Drops all tokens in front of the expression that are unrelated for
+ /// the completion of the cmd line. 'unrelated' means here that the token
+ /// is not interested for the lldb completion API result.
+ StringRef dropUnrelatedFrontTokens(StringRef cmd) {
+ if (cmd.empty())
+ return cmd;
+
+ // If we are at the start of a word, then all tokens are unrelated to
+ // the current completion logic.
+ if (IsTokenSeparator(cmd.back()))
+ return StringRef();
+
+ // Remove all previous tokens from the string as they are unrelated
+ // to completing the current token.
+ StringRef to_remove = cmd;
+ while (!to_remove.empty() && !IsTokenSeparator(to_remove.back())) {
+ to_remove = to_remove.drop_back();
+ }
+ cmd = cmd.drop_front(to_remove.size());
+
+ return cmd;
+ }
+
+ /// Removes the last identifier token from the given cmd line.
+ StringRef removeLastToken(StringRef cmd) {
+ while (!cmd.empty() && IsIdChar(cmd.back())) {
+ cmd = cmd.drop_back();
+ }
+ return cmd;
+ }
+
+ /// Attemps to merge the given completion from the given position into the
+ /// existing command. Returns the completion string that can be returned to
+ /// the lldb completion API.
+ std::string mergeCompletion(StringRef existing, unsigned pos,
+ StringRef completion) {
+ StringRef existing_command = existing.substr(0, pos);
+ // We rewrite the last token with the completion, so let's drop that
+ // token from the command.
+ existing_command = removeLastToken(existing_command);
+ // We also should remove all previous tokens from the command as they
+ // would otherwise be added to the completion that already has the
+ // completion.
+ existing_command = dropUnrelatedFrontTokens(existing_command);
+ return existing_command.str() + completion.str();
+ }
+
+public:
+ /// Constructs a CodeComplete consumer that can be attached to a Sema.
+ /// \param[out] matches
+ /// The list of matches that the lldb completion API expects as a result.
+ /// This may already contain matches, so it's only allowed to append
+ /// to this variable.
+ /// \param[out] expr
+ /// The whole expression string that we are currently parsing. This
+ /// string needs to be equal to the input the user typed, and NOT the
+ /// final code that Clang is parsing.
+ /// \param[out] position
+ /// The character position of the user cursor in the `expr` parameter.
+ ///
+ CodeComplete(CompletionRequest &request, clang::LangOptions ops,
+ std::string expr, unsigned position)
+ : CodeCompleteConsumer(CodeCompleteOptions()),
+ m_info(std::make_shared<GlobalCodeCompletionAllocator>()), m_expr(expr),
+ m_position(position), m_request(request), m_desc_policy(ops) {
+
+ // Ensure that the printing policy is producing a description that is as
+ // short as possible.
+ m_desc_policy.SuppressScope = true;
+ m_desc_policy.SuppressTagKeyword = true;
+ m_desc_policy.FullyQualifiedName = false;
+ m_desc_policy.TerseOutput = true;
+ m_desc_policy.IncludeNewlines = false;
+ m_desc_policy.UseVoidForZeroParams = false;
+ m_desc_policy.Bool = true;
+ }
+
+ /// Deregisters and destroys this code-completion consumer.
+ ~CodeComplete() override {}
+
+ /// \name Code-completion filtering
+ /// Check if the result should be filtered out.
+ bool isResultFilteredOut(StringRef Filter,
+ CodeCompletionResult Result) override {
+ // This code is mostly copied from CodeCompleteConsumer.
+ switch (Result.Kind) {
+ case CodeCompletionResult::RK_Declaration:
+ return !(
+ Result.Declaration->getIdentifier() &&
+ Result.Declaration->getIdentifier()->getName().startswith(Filter));
+ case CodeCompletionResult::RK_Keyword:
+ return !StringRef(Result.Keyword).startswith(Filter);
+ case CodeCompletionResult::RK_Macro:
+ return !Result.Macro->getName().startswith(Filter);
+ case CodeCompletionResult::RK_Pattern:
+ return !StringRef(Result.Pattern->getAsString()).startswith(Filter);
+ }
+ // If we trigger this assert or the above switch yields a warning, then
+ // CodeCompletionResult has been enhanced with more kinds of completion
+ // results. Expand the switch above in this case.
+ assert(false && "Unknown completion result type?");
+ // If we reach this, then we should just ignore whatever kind of unknown
+ // result we got back. We probably can't turn it into any kind of useful
+ // completion suggestion with the existing code.
+ return true;
+ }
+
+ /// \name Code-completion callbacks
+ /// Process the finalized code-completion results.
+ void ProcessCodeCompleteResults(Sema &SemaRef, CodeCompletionContext Context,
+ CodeCompletionResult *Results,
+ unsigned NumResults) override {
+
+ // The Sema put the incomplete token we try to complete in here during
+ // lexing, so we need to retrieve it here to know what we are completing.
+ StringRef Filter = SemaRef.getPreprocessor().getCodeCompletionFilter();
+
+ // Iterate over all the results. Filter out results we don't want and
+ // process the rest.
+ for (unsigned I = 0; I != NumResults; ++I) {
+ // Filter the results with the information from the Sema.
+ if (!Filter.empty() && isResultFilteredOut(Filter, Results[I]))
+ continue;
+
+ CodeCompletionResult &R = Results[I];
+ std::string ToInsert;
+ std::string Description;
+ // Handle the different completion kinds that come from the Sema.
+ switch (R.Kind) {
+ case CodeCompletionResult::RK_Declaration: {
+ const NamedDecl *D = R.Declaration;
+ ToInsert = R.Declaration->getNameAsString();
+ // If we have a function decl that has no arguments we want to
+ // complete the empty parantheses for the user. If the function has
+ // arguments, we at least complete the opening bracket.
+ if (const FunctionDecl *F = dyn_cast<FunctionDecl>(D)) {
+ if (F->getNumParams() == 0)
+ ToInsert += "()";
+ else
+ ToInsert += "(";
+ raw_string_ostream OS(Description);
+ F->print(OS, m_desc_policy, false);
+ OS.flush();
+ } else if (const VarDecl *V = dyn_cast<VarDecl>(D)) {
+ Description = V->getType().getAsString(m_desc_policy);
+ } else if (const FieldDecl *F = dyn_cast<FieldDecl>(D)) {
+ Description = F->getType().getAsString(m_desc_policy);
+ } else if (const NamespaceDecl *N = dyn_cast<NamespaceDecl>(D)) {
+ // If we try to complete a namespace, then we can directly append
+ // the '::'.
+ if (!N->isAnonymousNamespace())
+ ToInsert += "::";
+ }
+ break;
+ }
+ case CodeCompletionResult::RK_Keyword:
+ ToInsert = R.Keyword;
+ break;
+ case CodeCompletionResult::RK_Macro:
+ ToInsert = R.Macro->getName().str();
+ break;
+ case CodeCompletionResult::RK_Pattern:
+ ToInsert = R.Pattern->getTypedText();
+ break;
+ }
+ // At this point all information is in the ToInsert string.
+
+ // We also filter some internal lldb identifiers here. The user
+ // shouldn't see these.
+ if (StringRef(ToInsert).startswith("$__lldb_"))
+ continue;
+ if (!ToInsert.empty()) {
+ // Merge the suggested Token into the existing command line to comply
+ // with the kind of result the lldb API expects.
+ std::string CompletionSuggestion =
+ mergeCompletion(m_expr, m_position, ToInsert);
+ m_request.AddCompletion(CompletionSuggestion, Description);
+ }
+ }
+ }
+
+ /// \param S the semantic-analyzer object for which code-completion is being
+ /// done.
+ ///
+ /// \param CurrentArg the index of the current argument.
+ ///
+ /// \param Candidates an array of overload candidates.
+ ///
+ /// \param NumCandidates the number of overload candidates
+ void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg,
+ OverloadCandidate *Candidates,
+ unsigned NumCandidates,
+ SourceLocation OpenParLoc) override {
+ // At the moment we don't filter out any overloaded candidates.
+ }
+
+ CodeCompletionAllocator &getAllocator() override {
+ return m_info.getAllocator();
+ }
+
+ CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return m_info; }
+};
+} // namespace
+
+bool ClangExpressionParser::Complete(CompletionRequest &request, unsigned line,
+ unsigned pos, unsigned typed_pos) {
+ DiagnosticManager mgr;
+ // We need the raw user expression here because that's what the CodeComplete
+ // class uses to provide completion suggestions.
+ // However, the `Text` method only gives us the transformed expression here.
+ // To actually get the raw user input here, we have to cast our expression to
+ // the LLVMUserExpression which exposes the right API. This should never fail
+ // as we always have a ClangUserExpression whenever we call this.
+ ClangUserExpression *llvm_expr = cast<ClangUserExpression>(&m_expr);
+ CodeComplete CC(request, m_compiler->getLangOpts(), llvm_expr->GetUserText(),
+ typed_pos);
+ // We don't need a code generator for parsing.
+ m_code_generator.reset();
+ // Start parsing the expression with our custom code completion consumer.
+ ParseInternal(mgr, &CC, line, pos);
+ return true;
+}
+
+unsigned ClangExpressionParser::Parse(DiagnosticManager &diagnostic_manager) {
+ return ParseInternal(diagnostic_manager);
+}
+
+unsigned
+ClangExpressionParser::ParseInternal(DiagnosticManager &diagnostic_manager,
+ CodeCompleteConsumer *completion_consumer,
+ unsigned completion_line,
+ unsigned completion_column) {
+ ClangDiagnosticManagerAdapter *adapter =
+ static_cast<ClangDiagnosticManagerAdapter *>(
+ m_compiler->getDiagnostics().getClient());
+ clang::TextDiagnosticBuffer *diag_buf = adapter->GetPassthrough();
+ diag_buf->FlushDiagnostics(m_compiler->getDiagnostics());
+
+ adapter->ResetManager(&diagnostic_manager);
+
+ const char *expr_text = m_expr.Text();
+
+ clang::SourceManager &source_mgr = m_compiler->getSourceManager();
+ bool created_main_file = false;
+
+ // Clang wants to do completion on a real file known by Clang's file manager,
+ // so we have to create one to make this work.
+ // TODO: We probably could also simulate to Clang's file manager that there
+ // is a real file that contains our code.
+ bool should_create_file = completion_consumer != nullptr;
+
+ // We also want a real file on disk if we generate full debug info.
+ should_create_file |= m_compiler->getCodeGenOpts().getDebugInfo() ==
+ codegenoptions::FullDebugInfo;
+
+ if (should_create_file) {
+ int temp_fd = -1;
+ llvm::SmallString<128> result_path;
+ if (FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir()) {
+ tmpdir_file_spec.AppendPathComponent("lldb-%%%%%%.expr");
+ std::string temp_source_path = tmpdir_file_spec.GetPath();
+ llvm::sys::fs::createUniqueFile(temp_source_path, temp_fd, result_path);
+ } else {
+ llvm::sys::fs::createTemporaryFile("lldb", "expr", temp_fd, result_path);
+ }
+
+ if (temp_fd != -1) {
+ lldb_private::File file(temp_fd, true);
+ const size_t expr_text_len = strlen(expr_text);
+ size_t bytes_written = expr_text_len;
+ if (file.Write(expr_text, bytes_written).Success()) {
+ if (bytes_written == expr_text_len) {
+ file.Close();
+ source_mgr.setMainFileID(source_mgr.createFileID(
+ m_compiler->getFileManager().getFile(result_path),
+ SourceLocation(), SrcMgr::C_User));
+ created_main_file = true;
+ }
+ }
+ }
+ }
+
+ if (!created_main_file) {
+ std::unique_ptr<MemoryBuffer> memory_buffer =
+ MemoryBuffer::getMemBufferCopy(expr_text, "<lldb-expr>");
+ source_mgr.setMainFileID(source_mgr.createFileID(std::move(memory_buffer)));
+ }
+
+ diag_buf->BeginSourceFile(m_compiler->getLangOpts(),
+ &m_compiler->getPreprocessor());
+
+ ClangExpressionHelper *type_system_helper =
+ dyn_cast<ClangExpressionHelper>(m_expr.GetTypeSystemHelper());
+
+ // If we want to parse for code completion, we need to attach our code
+ // completion consumer to the Sema and specify a completion position.
+ // While parsing the Sema will call this consumer with the provided
+ // completion suggestions.
+ if (completion_consumer) {
+ auto main_file = source_mgr.getFileEntryForID(source_mgr.getMainFileID());
+ auto &PP = m_compiler->getPreprocessor();
+ // Lines and columns start at 1 in Clang, but code completion positions are
+ // indexed from 0, so we need to add 1 to the line and column here.
+ ++completion_line;
+ ++completion_column;
+ PP.SetCodeCompletionPoint(main_file, completion_line, completion_column);
+ }
+
+ ASTConsumer *ast_transformer =
+ type_system_helper->ASTTransformer(m_code_generator.get());
+
+ std::unique_ptr<clang::ASTConsumer> Consumer;
+ if (ast_transformer) {
+ Consumer.reset(new ASTConsumerForwarder(ast_transformer));
+ } else if (m_code_generator) {
+ Consumer.reset(new ASTConsumerForwarder(m_code_generator.get()));
+ } else {
+ Consumer.reset(new ASTConsumer());
+ }
+
+ clang::ASTContext &ast_context = m_compiler->getASTContext();
+
+ m_compiler->setSema(new Sema(m_compiler->getPreprocessor(), ast_context,
+ *Consumer, TU_Complete, completion_consumer));
+ m_compiler->setASTConsumer(std::move(Consumer));
+
+ if (ast_context.getLangOpts().Modules) {
+ m_compiler->createModuleManager();
+ m_ast_context->setSema(&m_compiler->getSema());
+ }
+
+ ClangExpressionDeclMap *decl_map = type_system_helper->DeclMap();
+ if (decl_map) {
+ decl_map->InstallCodeGenerator(&m_compiler->getASTConsumer());
+
+ clang::ExternalASTSource *ast_source = decl_map->CreateProxy();
+
+ if (ast_context.getExternalSource()) {
+ auto module_wrapper =
+ new ExternalASTSourceWrapper(ast_context.getExternalSource());
+
+ auto ast_source_wrapper = new ExternalASTSourceWrapper(ast_source);
+
+ auto multiplexer =
+ new SemaSourceWithPriorities(*module_wrapper, *ast_source_wrapper);
+ IntrusiveRefCntPtr<ExternalASTSource> Source(multiplexer);
+ ast_context.setExternalSource(Source);
+ } else {
+ ast_context.setExternalSource(ast_source);
+ }
+ decl_map->InstallASTContext(ast_context, m_compiler->getFileManager());
+ }
+
+ // Check that the ASTReader is properly attached to ASTContext and Sema.
+ if (ast_context.getLangOpts().Modules) {
+ assert(m_compiler->getASTContext().getExternalSource() &&
+ "ASTContext doesn't know about the ASTReader?");
+ assert(m_compiler->getSema().getExternalSource() &&
+ "Sema doesn't know about the ASTReader?");
+ }
+
+ {
+ llvm::CrashRecoveryContextCleanupRegistrar<Sema> CleanupSema(
+ &m_compiler->getSema());
+ ParseAST(m_compiler->getSema(), false, false);
+ }
+
+ // Make sure we have no pointer to the Sema we are about to destroy.
+ if (ast_context.getLangOpts().Modules)
+ m_ast_context->setSema(nullptr);
+ // Destroy the Sema. This is necessary because we want to emulate the
+ // original behavior of ParseAST (which also destroys the Sema after parsing).
+ m_compiler->setSema(nullptr);
+
+ diag_buf->EndSourceFile();
+
+ unsigned num_errors = diag_buf->getNumErrors();
+
+ if (m_pp_callbacks && m_pp_callbacks->hasErrors()) {
+ num_errors++;
+ diagnostic_manager.PutString(eDiagnosticSeverityError,
+ "while importing modules:");
+ diagnostic_manager.AppendMessageToDiagnostic(
+ m_pp_callbacks->getErrorString());
+ }
+
+ if (!num_errors) {
+ if (type_system_helper->DeclMap() &&
+ !type_system_helper->DeclMap()->ResolveUnknownTypes()) {
+ diagnostic_manager.Printf(eDiagnosticSeverityError,
+ "Couldn't infer the type of a variable");
+ num_errors++;
+ }
+ }
+
+ if (!num_errors) {
+ type_system_helper->CommitPersistentDecls();
+ }
+
+ adapter->ResetManager();
+
+ return num_errors;
+}
+
+std::string
+ClangExpressionParser::GetClangTargetABI(const ArchSpec &target_arch) {
+ std::string abi;
+
+ if (target_arch.IsMIPS()) {
+ switch (target_arch.GetFlags() & ArchSpec::eMIPSABI_mask) {
+ case ArchSpec::eMIPSABI_N64:
+ abi = "n64";
+ break;
+ case ArchSpec::eMIPSABI_N32:
+ abi = "n32";
+ break;
+ case ArchSpec::eMIPSABI_O32:
+ abi = "o32";
+ break;
+ default:
+ break;
+ }
+ }
+ return abi;
+}
+
+bool ClangExpressionParser::RewriteExpression(
+ DiagnosticManager &diagnostic_manager) {
+ clang::SourceManager &source_manager = m_compiler->getSourceManager();
+ clang::edit::EditedSource editor(source_manager, m_compiler->getLangOpts(),
+ nullptr);
+ clang::edit::Commit commit(editor);
+ clang::Rewriter rewriter(source_manager, m_compiler->getLangOpts());
+
+ class RewritesReceiver : public edit::EditsReceiver {
+ Rewriter &rewrite;
+
+ public:
+ RewritesReceiver(Rewriter &in_rewrite) : rewrite(in_rewrite) {}
+
+ void insert(SourceLocation loc, StringRef text) override {
+ rewrite.InsertText(loc, text);
+ }
+ void replace(CharSourceRange range, StringRef text) override {
+ rewrite.ReplaceText(range.getBegin(), rewrite.getRangeSize(range), text);
+ }
+ };
+
+ RewritesReceiver rewrites_receiver(rewriter);
+
+ const DiagnosticList &diagnostics = diagnostic_manager.Diagnostics();
+ size_t num_diags = diagnostics.size();
+ if (num_diags == 0)
+ return false;
+
+ for (const Diagnostic *diag : diagnostic_manager.Diagnostics()) {
+ const ClangDiagnostic *diagnostic = llvm::dyn_cast<ClangDiagnostic>(diag);
+ if (diagnostic && diagnostic->HasFixIts()) {
+ for (const FixItHint &fixit : diagnostic->FixIts()) {
+ // This is cobbed from clang::Rewrite::FixItRewriter.
+ if (fixit.CodeToInsert.empty()) {
+ if (fixit.InsertFromRange.isValid()) {
+ commit.insertFromRange(fixit.RemoveRange.getBegin(),
+ fixit.InsertFromRange, /*afterToken=*/false,
+ fixit.BeforePreviousInsertions);
+ } else
+ commit.remove(fixit.RemoveRange);
+ } else {
+ if (fixit.RemoveRange.isTokenRange() ||
+ fixit.RemoveRange.getBegin() != fixit.RemoveRange.getEnd())
+ commit.replace(fixit.RemoveRange, fixit.CodeToInsert);
+ else
+ commit.insert(fixit.RemoveRange.getBegin(), fixit.CodeToInsert,
+ /*afterToken=*/false, fixit.BeforePreviousInsertions);
+ }
+ }
+ }
+ }
+
+ // FIXME - do we want to try to propagate specific errors here?
+ if (!commit.isCommitable())
+ return false;
+ else if (!editor.commit(commit))
+ return false;
+
+ // Now play all the edits, and stash the result in the diagnostic manager.
+ editor.applyRewrites(rewrites_receiver);
+ RewriteBuffer &main_file_buffer =
+ rewriter.getEditBuffer(source_manager.getMainFileID());
+
+ std::string fixed_expression;
+ llvm::raw_string_ostream out_stream(fixed_expression);
+
+ main_file_buffer.write(out_stream);
+ out_stream.flush();
+ diagnostic_manager.SetFixedExpression(fixed_expression);
+
+ return true;
+}
+
+static bool FindFunctionInModule(ConstString &mangled_name,
+ llvm::Module *module, const char *orig_name) {
+ for (const auto &func : module->getFunctionList()) {
+ const StringRef &name = func.getName();
+ if (name.find(orig_name) != StringRef::npos) {
+ mangled_name.SetString(name);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+lldb_private::Status ClangExpressionParser::PrepareForExecution(
+ lldb::addr_t &func_addr, lldb::addr_t &func_end,
+ lldb::IRExecutionUnitSP &execution_unit_sp, ExecutionContext &exe_ctx,
+ bool &can_interpret, ExecutionPolicy execution_policy) {
+ func_addr = LLDB_INVALID_ADDRESS;
+ func_end = LLDB_INVALID_ADDRESS;
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ lldb_private::Status err;
+
+ std::unique_ptr<llvm::Module> llvm_module_up(
+ m_code_generator->ReleaseModule());
+
+ if (!llvm_module_up) {
+ err.SetErrorToGenericError();
+ err.SetErrorString("IR doesn't contain a module");
+ return err;
+ }
+
+ ConstString function_name;
+
+ if (execution_policy != eExecutionPolicyTopLevel) {
+ // Find the actual name of the function (it's often mangled somehow)
+
+ if (!FindFunctionInModule(function_name, llvm_module_up.get(),
+ m_expr.FunctionName())) {
+ err.SetErrorToGenericError();
+ err.SetErrorStringWithFormat("Couldn't find %s() in the module",
+ m_expr.FunctionName());
+ return err;
+ } else {
+ if (log)
+ log->Printf("Found function %s for %s", function_name.AsCString(),
+ m_expr.FunctionName());
+ }
+ }
+
+ SymbolContext sc;
+
+ if (lldb::StackFrameSP frame_sp = exe_ctx.GetFrameSP()) {
+ sc = frame_sp->GetSymbolContext(lldb::eSymbolContextEverything);
+ } else if (lldb::TargetSP target_sp = exe_ctx.GetTargetSP()) {
+ sc.target_sp = target_sp;
+ }
+
+ LLVMUserExpression::IRPasses custom_passes;
+ {
+ auto lang = m_expr.Language();
+ if (log)
+ log->Printf("%s - Current expression language is %s\n", __FUNCTION__,
+ Language::GetNameForLanguageType(lang));
+ lldb::ProcessSP process_sp = exe_ctx.GetProcessSP();
+ if (process_sp && lang != lldb::eLanguageTypeUnknown) {
+ auto runtime = process_sp->GetLanguageRuntime(lang);
+ if (runtime)
+ runtime->GetIRPasses(custom_passes);
+ }
+ }
+
+ if (custom_passes.EarlyPasses) {
+ if (log)
+ log->Printf("%s - Running Early IR Passes from LanguageRuntime on "
+ "expression module '%s'",
+ __FUNCTION__, m_expr.FunctionName());
+
+ custom_passes.EarlyPasses->run(*llvm_module_up);
+ }
+
+ execution_unit_sp = std::make_shared<IRExecutionUnit>(
+ m_llvm_context, // handed off here
+ llvm_module_up, // handed off here
+ function_name, exe_ctx.GetTargetSP(), sc,
+ m_compiler->getTargetOpts().Features);
+
+ ClangExpressionHelper *type_system_helper =
+ dyn_cast<ClangExpressionHelper>(m_expr.GetTypeSystemHelper());
+ ClangExpressionDeclMap *decl_map =
+ type_system_helper->DeclMap(); // result can be NULL
+
+ if (decl_map) {
+ Stream *error_stream = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ error_stream = target->GetDebugger().GetErrorFile().get();
+
+ IRForTarget ir_for_target(decl_map, m_expr.NeedsVariableResolution(),
+ *execution_unit_sp, *error_stream,
+ function_name.AsCString());
+
+ bool ir_can_run =
+ ir_for_target.runOnModule(*execution_unit_sp->GetModule());
+
+ if (!ir_can_run) {
+ err.SetErrorString(
+ "The expression could not be prepared to run in the target");
+ return err;
+ }
+
+ Process *process = exe_ctx.GetProcessPtr();
+
+ if (execution_policy != eExecutionPolicyAlways &&
+ execution_policy != eExecutionPolicyTopLevel) {
+ lldb_private::Status interpret_error;
+
+ bool interpret_function_calls =
+ !process ? false : process->CanInterpretFunctionCalls();
+ can_interpret = IRInterpreter::CanInterpret(
+ *execution_unit_sp->GetModule(), *execution_unit_sp->GetFunction(),
+ interpret_error, interpret_function_calls);
+
+ if (!can_interpret && execution_policy == eExecutionPolicyNever) {
+ err.SetErrorStringWithFormat("Can't run the expression locally: %s",
+ interpret_error.AsCString());
+ return err;
+ }
+ }
+
+ if (!process && execution_policy == eExecutionPolicyAlways) {
+ err.SetErrorString("Expression needed to run in the target, but the "
+ "target can't be run");
+ return err;
+ }
+
+ if (!process && execution_policy == eExecutionPolicyTopLevel) {
+ err.SetErrorString("Top-level code needs to be inserted into a runnable "
+ "target, but the target can't be run");
+ return err;
+ }
+
+ if (execution_policy == eExecutionPolicyAlways ||
+ (execution_policy != eExecutionPolicyTopLevel && !can_interpret)) {
+ if (m_expr.NeedsValidation() && process) {
+ if (!process->GetDynamicCheckers()) {
+ ClangDynamicCheckerFunctions *dynamic_checkers =
+ new ClangDynamicCheckerFunctions();
+
+ DiagnosticManager install_diagnostics;
+
+ if (!dynamic_checkers->Install(install_diagnostics, exe_ctx)) {
+ if (install_diagnostics.Diagnostics().size())
+ err.SetErrorString(install_diagnostics.GetString().c_str());
+ else
+ err.SetErrorString("couldn't install checkers, unknown error");
+
+ return err;
+ }
+
+ process->SetDynamicCheckers(dynamic_checkers);
+
+ if (log)
+ log->Printf("== [ClangExpressionParser::PrepareForExecution] "
+ "Finished installing dynamic checkers ==");
+ }
+
+ if (auto *checker_funcs = llvm::dyn_cast<ClangDynamicCheckerFunctions>(
+ process->GetDynamicCheckers())) {
+ IRDynamicChecks ir_dynamic_checks(*checker_funcs,
+ function_name.AsCString());
+
+ llvm::Module *module = execution_unit_sp->GetModule();
+ if (!module || !ir_dynamic_checks.runOnModule(*module)) {
+ err.SetErrorToGenericError();
+ err.SetErrorString("Couldn't add dynamic checks to the expression");
+ return err;
+ }
+
+ if (custom_passes.LatePasses) {
+ if (log)
+ log->Printf("%s - Running Late IR Passes from LanguageRuntime on "
+ "expression module '%s'",
+ __FUNCTION__, m_expr.FunctionName());
+
+ custom_passes.LatePasses->run(*module);
+ }
+ }
+ }
+ }
+
+ if (execution_policy == eExecutionPolicyAlways ||
+ execution_policy == eExecutionPolicyTopLevel || !can_interpret) {
+ execution_unit_sp->GetRunnableInfo(err, func_addr, func_end);
+ }
+ } else {
+ execution_unit_sp->GetRunnableInfo(err, func_addr, func_end);
+ }
+
+ return err;
+}
+
+lldb_private::Status ClangExpressionParser::RunStaticInitializers(
+ lldb::IRExecutionUnitSP &execution_unit_sp, ExecutionContext &exe_ctx) {
+ lldb_private::Status err;
+
+ lldbassert(execution_unit_sp.get());
+ lldbassert(exe_ctx.HasThreadScope());
+
+ if (!execution_unit_sp.get()) {
+ err.SetErrorString(
+ "can't run static initializers for a NULL execution unit");
+ return err;
+ }
+
+ if (!exe_ctx.HasThreadScope()) {
+ err.SetErrorString("can't run static initializers without a thread");
+ return err;
+ }
+
+ std::vector<lldb::addr_t> static_initializers;
+
+ execution_unit_sp->GetStaticInitializers(static_initializers);
+
+ for (lldb::addr_t static_initializer : static_initializers) {
+ EvaluateExpressionOptions options;
+
+ lldb::ThreadPlanSP call_static_initializer(new ThreadPlanCallFunction(
+ exe_ctx.GetThreadRef(), Address(static_initializer), CompilerType(),
+ llvm::ArrayRef<lldb::addr_t>(), options));
+
+ DiagnosticManager execution_errors;
+ lldb::ExpressionResults results =
+ exe_ctx.GetThreadRef().GetProcess()->RunThreadPlan(
+ exe_ctx, call_static_initializer, options, execution_errors);
+
+ if (results != lldb::eExpressionCompleted) {
+ err.SetErrorStringWithFormat("couldn't run static initializer: %s",
+ execution_errors.GetString().c_str());
+ return err;
+ }
+ }
+
+ return err;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h
new file mode 100644
index 000000000000..a42c2190ffb8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h
@@ -0,0 +1,184 @@
+//===-- ClangExpressionParser.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 liblldb_ClangExpressionParser_h_
+#define liblldb_ClangExpressionParser_h_
+
+#include "lldb/Core/ClangForward.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/ExpressionParser.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-public.h"
+
+#include <string>
+#include <vector>
+
+namespace clang {
+class CodeCompleteConsumer;
+}
+
+namespace lldb_private {
+
+class IRExecutionUnit;
+
+/// \class ClangExpressionParser ClangExpressionParser.h
+/// "lldb/Expression/ClangExpressionParser.h" Encapsulates an instance of
+/// Clang that can parse expressions.
+///
+/// ClangExpressionParser is responsible for preparing an instance of
+/// ClangExpression for execution. ClangExpressionParser uses ClangExpression
+/// as a glorified parameter list, performing the required parsing and
+/// conversion to formats (DWARF bytecode, or JIT compiled machine code) that
+/// can be executed.
+class ClangExpressionParser : public ExpressionParser {
+public:
+ /// Constructor
+ ///
+ /// Initializes class variables.
+ ///
+ /// \param[in] exe_scope,
+ /// If non-NULL, an execution context scope that can help to
+ /// correctly create an expression with a valid process for
+ /// optional tuning Objective-C runtime support. Can be NULL.
+ ///
+ /// \param[in] expr
+ /// The expression to be parsed.
+ ///
+ /// @param[in] include_directories
+ /// List of include directories that should be used when parsing the
+ /// expression.
+ ClangExpressionParser(ExecutionContextScope *exe_scope, Expression &expr,
+ bool generate_debug_info,
+ std::vector<ConstString> include_directories = {});
+
+ /// Destructor
+ ~ClangExpressionParser() override;
+
+ bool Complete(CompletionRequest &request, unsigned line, unsigned pos,
+ unsigned typed_pos) override;
+
+ /// Parse a single expression and convert it to IR using Clang. Don't wrap
+ /// the expression in anything at all.
+ ///
+ /// \param[in] diagnostic_manager
+ /// The diagnostic manager to report errors to.
+ ///
+ /// \return
+ /// The number of errors encountered during parsing. 0 means
+ /// success.
+ unsigned Parse(DiagnosticManager &diagnostic_manager) override;
+
+ bool RewriteExpression(DiagnosticManager &diagnostic_manager) override;
+
+ /// Ready an already-parsed expression for execution, possibly evaluating it
+ /// statically.
+ ///
+ /// \param[out] func_addr
+ /// The address to which the function has been written.
+ ///
+ /// \param[out] func_end
+ /// The end of the function's allocated memory region. (func_addr
+ /// and func_end do not delimit an allocated region; the allocated
+ /// region may begin before func_addr.)
+ ///
+ /// \param[in] execution_unit_sp
+ /// After parsing, ownership of the execution unit for
+ /// for the expression is handed to this shared pointer.
+ ///
+ /// \param[in] exe_ctx
+ /// The execution context to write the function into.
+ ///
+ /// \param[out] evaluated_statically
+ /// Set to true if the expression could be interpreted statically;
+ /// untouched otherwise.
+ ///
+ /// \param[out] const_result
+ /// If the result of the expression is constant, and the
+ /// expression has no side effects, this is set to the result of the
+ /// expression.
+ ///
+ /// \param[in] execution_policy
+ /// Determines whether the expression must be JIT-compiled, must be
+ /// evaluated statically, or whether this decision may be made
+ /// opportunistically.
+ ///
+ /// \return
+ /// An error code indicating the success or failure of the operation.
+ /// Test with Success().
+ Status
+ PrepareForExecution(lldb::addr_t &func_addr, lldb::addr_t &func_end,
+ lldb::IRExecutionUnitSP &execution_unit_sp,
+ ExecutionContext &exe_ctx, bool &can_interpret,
+ lldb_private::ExecutionPolicy execution_policy) override;
+
+ /// Run all static initializers for an execution unit.
+ ///
+ /// \param[in] execution_unit_sp
+ /// The execution unit.
+ ///
+ /// \param[in] exe_ctx
+ /// The execution context to use when running them. Thread can't be null.
+ ///
+ /// \return
+ /// The error code indicating the
+ Status RunStaticInitializers(lldb::IRExecutionUnitSP &execution_unit_sp,
+ ExecutionContext &exe_ctx);
+
+ /// Returns a string representing current ABI.
+ ///
+ /// \param[in] target_arch
+ /// The target architecture.
+ ///
+ /// \return
+ /// A string representing target ABI for the current architecture.
+ std::string GetClangTargetABI(const ArchSpec &target_arch);
+
+private:
+ /// Parses the expression.
+ ///
+ /// \param[in] diagnostic_manager
+ /// The diagnostic manager that should receive the diagnostics
+ /// from the parsing process.
+ ///
+ /// \param[in] completion
+ /// The completion consumer that should be used during parsing
+ /// (or a nullptr if no consumer should be attached).
+ ///
+ /// \param[in] completion_line
+ /// The line in which the completion marker should be placed.
+ /// The first line is represented by the value 0.
+ ///
+ /// \param[in] completion_column
+ /// The column in which the completion marker should be placed.
+ /// The first column is represented by the value 0.
+ ///
+ /// \return
+ /// The number of parsing errors.
+ unsigned ParseInternal(DiagnosticManager &diagnostic_manager,
+ clang::CodeCompleteConsumer *completion = nullptr,
+ unsigned completion_line = 0,
+ unsigned completion_column = 0);
+
+ std::unique_ptr<llvm::LLVMContext>
+ m_llvm_context; ///< The LLVM context to generate IR into
+ std::unique_ptr<clang::CompilerInstance>
+ m_compiler; ///< The Clang compiler used to parse expressions into IR
+ std::unique_ptr<clang::CodeGenerator>
+ m_code_generator; ///< The Clang object that generates IR
+
+ class LLDBPreprocessorCallbacks;
+ LLDBPreprocessorCallbacks *m_pp_callbacks; ///< Called when the preprocessor
+ ///encounters module imports
+ std::unique_ptr<ClangASTContext> m_ast_context;
+
+ std::vector<ConstString> m_include_directories;
+};
+}
+
+#endif // liblldb_ClangExpressionParser_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp
new file mode 100644
index 000000000000..f513b1eea360
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp
@@ -0,0 +1,497 @@
+//===-- ClangExpressionSourceCode.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 "ClangExpressionSourceCode.h"
+
+#include "clang/Basic/CharInfo.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/StringRef.h"
+
+#include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h"
+#include "Plugins/ExpressionParser/Clang/ClangPersistentVariables.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/DebugMacros.h"
+#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb_private;
+
+const char *ClangExpressionSourceCode::g_expression_prefix = R"(
+#ifndef NULL
+#define NULL (__null)
+#endif
+#ifndef Nil
+#define Nil (__null)
+#endif
+#ifndef nil
+#define nil (__null)
+#endif
+#ifndef YES
+#define YES ((BOOL)1)
+#endif
+#ifndef NO
+#define NO ((BOOL)0)
+#endif
+typedef __INT8_TYPE__ int8_t;
+typedef __UINT8_TYPE__ uint8_t;
+typedef __INT16_TYPE__ int16_t;
+typedef __UINT16_TYPE__ uint16_t;
+typedef __INT32_TYPE__ int32_t;
+typedef __UINT32_TYPE__ uint32_t;
+typedef __INT64_TYPE__ int64_t;
+typedef __UINT64_TYPE__ uint64_t;
+typedef __INTPTR_TYPE__ intptr_t;
+typedef __UINTPTR_TYPE__ uintptr_t;
+typedef __SIZE_TYPE__ size_t;
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+typedef unsigned short unichar;
+extern "C"
+{
+ int printf(const char * __restrict, ...);
+}
+)";
+
+static const char *c_start_marker = " /*LLDB_BODY_START*/\n ";
+static const char *c_end_marker = ";\n /*LLDB_BODY_END*/\n";
+
+namespace {
+
+class AddMacroState {
+ enum State {
+ CURRENT_FILE_NOT_YET_PUSHED,
+ CURRENT_FILE_PUSHED,
+ CURRENT_FILE_POPPED
+ };
+
+public:
+ AddMacroState(const FileSpec &current_file, const uint32_t current_file_line)
+ : m_state(CURRENT_FILE_NOT_YET_PUSHED), m_current_file(current_file),
+ m_current_file_line(current_file_line) {}
+
+ void StartFile(const FileSpec &file) {
+ m_file_stack.push_back(file);
+ if (file == m_current_file)
+ m_state = CURRENT_FILE_PUSHED;
+ }
+
+ void EndFile() {
+ if (m_file_stack.size() == 0)
+ return;
+
+ FileSpec old_top = m_file_stack.back();
+ m_file_stack.pop_back();
+ if (old_top == m_current_file)
+ m_state = CURRENT_FILE_POPPED;
+ }
+
+ // An entry is valid if it occurs before the current line in the current
+ // file.
+ bool IsValidEntry(uint32_t line) {
+ switch (m_state) {
+ case CURRENT_FILE_NOT_YET_PUSHED:
+ return true;
+ case CURRENT_FILE_PUSHED:
+ // If we are in file included in the current file, the entry should be
+ // added.
+ if (m_file_stack.back() != m_current_file)
+ return true;
+
+ return line < m_current_file_line;
+ default:
+ return false;
+ }
+ }
+
+private:
+ std::vector<FileSpec> m_file_stack;
+ State m_state;
+ FileSpec m_current_file;
+ uint32_t m_current_file_line;
+};
+
+} // anonymous namespace
+
+static void AddMacros(const DebugMacros *dm, CompileUnit *comp_unit,
+ AddMacroState &state, StreamString &stream) {
+ if (dm == nullptr)
+ return;
+
+ for (size_t i = 0; i < dm->GetNumMacroEntries(); i++) {
+ const DebugMacroEntry &entry = dm->GetMacroEntryAtIndex(i);
+ uint32_t line;
+
+ switch (entry.GetType()) {
+ case DebugMacroEntry::DEFINE:
+ if (state.IsValidEntry(entry.GetLineNumber()))
+ stream.Printf("#define %s\n", entry.GetMacroString().AsCString());
+ else
+ return;
+ break;
+ case DebugMacroEntry::UNDEF:
+ if (state.IsValidEntry(entry.GetLineNumber()))
+ stream.Printf("#undef %s\n", entry.GetMacroString().AsCString());
+ else
+ return;
+ break;
+ case DebugMacroEntry::START_FILE:
+ line = entry.GetLineNumber();
+ if (state.IsValidEntry(line))
+ state.StartFile(entry.GetFileSpec(comp_unit));
+ else
+ return;
+ break;
+ case DebugMacroEntry::END_FILE:
+ state.EndFile();
+ break;
+ case DebugMacroEntry::INDIRECT:
+ AddMacros(entry.GetIndirectDebugMacros(), comp_unit, state, stream);
+ break;
+ default:
+ // This is an unknown/invalid entry. Ignore.
+ break;
+ }
+ }
+}
+
+namespace {
+/// Allows checking if a token is contained in a given expression.
+class TokenVerifier {
+ /// The tokens we found in the expression.
+ llvm::StringSet<> m_tokens;
+
+public:
+ TokenVerifier(std::string body);
+ /// Returns true iff the given expression body contained a token with the
+ /// given content.
+ bool hasToken(llvm::StringRef token) const {
+ return m_tokens.find(token) != m_tokens.end();
+ }
+};
+} // namespace
+
+TokenVerifier::TokenVerifier(std::string body) {
+ using namespace clang;
+
+ // We only care about tokens and not their original source locations. If we
+ // move the whole expression to only be in one line we can simplify the
+ // following code that extracts the token contents.
+ std::replace(body.begin(), body.end(), '\n', ' ');
+ std::replace(body.begin(), body.end(), '\r', ' ');
+
+ FileSystemOptions file_opts;
+ FileManager file_mgr(file_opts,
+ FileSystem::Instance().GetVirtualFileSystem());
+
+ // Let's build the actual source code Clang needs and setup some utility
+ // objects.
+ llvm::IntrusiveRefCntPtr<DiagnosticIDs> diag_ids(new DiagnosticIDs());
+ llvm::IntrusiveRefCntPtr<DiagnosticOptions> diags_opts(
+ new DiagnosticOptions());
+ DiagnosticsEngine diags(diag_ids, diags_opts);
+ clang::SourceManager SM(diags, file_mgr);
+ auto buf = llvm::MemoryBuffer::getMemBuffer(body);
+
+ FileID FID = SM.createFileID(clang::SourceManager::Unowned, buf.get());
+
+ // Let's just enable the latest ObjC and C++ which should get most tokens
+ // right.
+ LangOptions Opts;
+ Opts.ObjC = true;
+ Opts.DollarIdents = true;
+ Opts.CPlusPlus17 = true;
+ Opts.LineComment = true;
+
+ Lexer lex(FID, buf.get(), SM, Opts);
+
+ Token token;
+ bool exit = false;
+ while (!exit) {
+ // Returns true if this is the last token we get from the lexer.
+ exit = lex.LexFromRawLexer(token);
+
+ // Extract the column number which we need to extract the token content.
+ // Our expression is just one line, so we don't need to handle any line
+ // numbers here.
+ bool invalid = false;
+ unsigned start = SM.getSpellingColumnNumber(token.getLocation(), &invalid);
+ if (invalid)
+ continue;
+ // Column numbers start at 1, but indexes in our string start at 0.
+ --start;
+
+ // Annotations don't have a length, so let's skip them.
+ if (token.isAnnotation())
+ continue;
+
+ // Extract the token string from our source code and store it.
+ std::string token_str = body.substr(start, token.getLength());
+ if (token_str.empty())
+ continue;
+ m_tokens.insert(token_str);
+ }
+}
+
+static void AddLocalVariableDecls(const lldb::VariableListSP &var_list_sp,
+ StreamString &stream,
+ const std::string &expr,
+ lldb::LanguageType wrapping_language) {
+ TokenVerifier tokens(expr);
+
+ for (size_t i = 0; i < var_list_sp->GetSize(); i++) {
+ lldb::VariableSP var_sp = var_list_sp->GetVariableAtIndex(i);
+
+ ConstString var_name = var_sp->GetName();
+
+
+ // We can check for .block_descriptor w/o checking for langauge since this
+ // is not a valid identifier in either C or C++.
+ if (!var_name || var_name == ".block_descriptor")
+ continue;
+
+ if (!expr.empty() && !tokens.hasToken(var_name.GetStringRef()))
+ continue;
+
+ if ((var_name == "self" || var_name == "_cmd") &&
+ (wrapping_language == lldb::eLanguageTypeObjC ||
+ wrapping_language == lldb::eLanguageTypeObjC_plus_plus))
+ continue;
+
+ if (var_name == "this" &&
+ wrapping_language == lldb::eLanguageTypeC_plus_plus)
+ continue;
+
+ stream.Printf("using $__lldb_local_vars::%s;\n", var_name.AsCString());
+ }
+}
+
+bool ClangExpressionSourceCode::GetText(
+ std::string &text, lldb::LanguageType wrapping_language, bool static_method,
+ ExecutionContext &exe_ctx, bool add_locals, bool force_add_all_locals,
+ llvm::ArrayRef<std::string> modules) const {
+ const char *target_specific_defines = "typedef signed char BOOL;\n";
+ std::string module_macros;
+
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ if (target->GetArchitecture().GetMachine() == llvm::Triple::aarch64) {
+ target_specific_defines = "typedef bool BOOL;\n";
+ }
+ if (target->GetArchitecture().GetMachine() == llvm::Triple::x86_64) {
+ if (lldb::PlatformSP platform_sp = target->GetPlatform()) {
+ static ConstString g_platform_ios_simulator("ios-simulator");
+ if (platform_sp->GetPluginName() == g_platform_ios_simulator) {
+ target_specific_defines = "typedef bool BOOL;\n";
+ }
+ }
+ }
+
+ if (ClangModulesDeclVendor *decl_vendor =
+ target->GetClangModulesDeclVendor()) {
+ ClangPersistentVariables *persistent_vars =
+ llvm::cast<ClangPersistentVariables>(
+ target->GetPersistentExpressionStateForLanguage(
+ lldb::eLanguageTypeC));
+ const ClangModulesDeclVendor::ModuleVector &hand_imported_modules =
+ persistent_vars->GetHandLoadedClangModules();
+ ClangModulesDeclVendor::ModuleVector modules_for_macros;
+
+ for (ClangModulesDeclVendor::ModuleID module : hand_imported_modules) {
+ modules_for_macros.push_back(module);
+ }
+
+ if (target->GetEnableAutoImportClangModules()) {
+ if (StackFrame *frame = exe_ctx.GetFramePtr()) {
+ if (Block *block = frame->GetFrameBlock()) {
+ SymbolContext sc;
+
+ block->CalculateSymbolContext(&sc);
+
+ if (sc.comp_unit) {
+ StreamString error_stream;
+
+ decl_vendor->AddModulesForCompileUnit(
+ *sc.comp_unit, modules_for_macros, error_stream);
+ }
+ }
+ }
+ }
+
+ decl_vendor->ForEachMacro(
+ modules_for_macros,
+ [&module_macros](const std::string &expansion) -> bool {
+ module_macros.append(expansion);
+ module_macros.append("\n");
+ return false;
+ });
+ }
+ }
+
+ StreamString debug_macros_stream;
+ StreamString lldb_local_var_decls;
+ if (StackFrame *frame = exe_ctx.GetFramePtr()) {
+ const SymbolContext &sc = frame->GetSymbolContext(
+ lldb::eSymbolContextCompUnit | lldb::eSymbolContextLineEntry);
+
+ if (sc.comp_unit && sc.line_entry.IsValid()) {
+ DebugMacros *dm = sc.comp_unit->GetDebugMacros();
+ if (dm) {
+ AddMacroState state(sc.line_entry.file, sc.line_entry.line);
+ AddMacros(dm, sc.comp_unit, state, debug_macros_stream);
+ }
+ }
+
+ if (add_locals)
+ if (target->GetInjectLocalVariables(&exe_ctx)) {
+ lldb::VariableListSP var_list_sp =
+ frame->GetInScopeVariableList(false, true);
+ AddLocalVariableDecls(var_list_sp, lldb_local_var_decls,
+ force_add_all_locals ? "" : m_body,
+ wrapping_language);
+ }
+ }
+
+ if (m_wrap) {
+ switch (wrapping_language) {
+ default:
+ return false;
+ case lldb::eLanguageTypeC:
+ case lldb::eLanguageTypeC_plus_plus:
+ case lldb::eLanguageTypeObjC:
+ break;
+ }
+
+ // Generate a list of @import statements that will import the specified
+ // module into our expression.
+ std::string module_imports;
+ for (const std::string &module : modules) {
+ module_imports.append("@import ");
+ module_imports.append(module);
+ module_imports.append(";\n");
+ }
+
+ StreamString wrap_stream;
+
+ wrap_stream.Printf("%s\n%s\n%s\n%s\n%s\n", module_macros.c_str(),
+ debug_macros_stream.GetData(), g_expression_prefix,
+ target_specific_defines, m_prefix.c_str());
+
+ // First construct a tagged form of the user expression so we can find it
+ // later:
+ std::string tagged_body;
+ switch (wrapping_language) {
+ default:
+ tagged_body = m_body;
+ break;
+ case lldb::eLanguageTypeC:
+ case lldb::eLanguageTypeC_plus_plus:
+ case lldb::eLanguageTypeObjC:
+ tagged_body.append(c_start_marker);
+ tagged_body.append(m_body);
+ tagged_body.append(c_end_marker);
+ break;
+ }
+ switch (wrapping_language) {
+ default:
+ break;
+ case lldb::eLanguageTypeC:
+ wrap_stream.Printf("%s"
+ "void \n"
+ "%s(void *$__lldb_arg) \n"
+ "{ \n"
+ " %s; \n"
+ "%s"
+ "} \n",
+ module_imports.c_str(), m_name.c_str(),
+ lldb_local_var_decls.GetData(), tagged_body.c_str());
+ break;
+ case lldb::eLanguageTypeC_plus_plus:
+ wrap_stream.Printf("%s"
+ "void \n"
+ "$__lldb_class::%s(void *$__lldb_arg) \n"
+ "{ \n"
+ " %s; \n"
+ "%s"
+ "} \n",
+ module_imports.c_str(), m_name.c_str(),
+ lldb_local_var_decls.GetData(), tagged_body.c_str());
+ break;
+ case lldb::eLanguageTypeObjC:
+ if (static_method) {
+ wrap_stream.Printf(
+ "%s"
+ "@interface $__lldb_objc_class ($__lldb_category) \n"
+ "+(void)%s:(void *)$__lldb_arg; \n"
+ "@end \n"
+ "@implementation $__lldb_objc_class ($__lldb_category) \n"
+ "+(void)%s:(void *)$__lldb_arg \n"
+ "{ \n"
+ " %s; \n"
+ "%s"
+ "} \n"
+ "@end \n",
+ module_imports.c_str(), m_name.c_str(), m_name.c_str(),
+ lldb_local_var_decls.GetData(), tagged_body.c_str());
+ } else {
+ wrap_stream.Printf(
+ "%s"
+ "@interface $__lldb_objc_class ($__lldb_category) \n"
+ "-(void)%s:(void *)$__lldb_arg; \n"
+ "@end \n"
+ "@implementation $__lldb_objc_class ($__lldb_category) \n"
+ "-(void)%s:(void *)$__lldb_arg \n"
+ "{ \n"
+ " %s; \n"
+ "%s"
+ "} \n"
+ "@end \n",
+ module_imports.c_str(), m_name.c_str(), m_name.c_str(),
+ lldb_local_var_decls.GetData(), tagged_body.c_str());
+ }
+ break;
+ }
+
+ text = wrap_stream.GetString();
+ } else {
+ text.append(m_body);
+ }
+
+ return true;
+}
+
+bool ClangExpressionSourceCode::GetOriginalBodyBounds(
+ std::string transformed_text, lldb::LanguageType wrapping_language,
+ size_t &start_loc, size_t &end_loc) {
+ const char *start_marker;
+ const char *end_marker;
+
+ switch (wrapping_language) {
+ default:
+ return false;
+ case lldb::eLanguageTypeC:
+ case lldb::eLanguageTypeC_plus_plus:
+ case lldb::eLanguageTypeObjC:
+ start_marker = c_start_marker;
+ end_marker = c_end_marker;
+ break;
+ }
+
+ start_loc = transformed_text.find(start_marker);
+ if (start_loc == std::string::npos)
+ return false;
+ start_loc += strlen(start_marker);
+ end_loc = transformed_text.find(end_marker);
+ return end_loc != std::string::npos;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h
new file mode 100644
index 000000000000..894290295837
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h
@@ -0,0 +1,71 @@
+//===-- ClangExpressionSourceCode.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 liblldb_ClangExpressionSourceCode_h
+#define liblldb_ClangExpressionSourceCode_h
+
+#include "lldb/Expression/Expression.h"
+#include "lldb/Expression/ExpressionSourceCode.h"
+#include "lldb/lldb-enumerations.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+
+#include <string>
+
+namespace lldb_private {
+
+class ExecutionContext;
+
+class ClangExpressionSourceCode : public ExpressionSourceCode {
+public:
+ static const char *g_expression_prefix;
+
+ static ClangExpressionSourceCode *CreateWrapped(const char *prefix,
+ const char *body) {
+ return new ClangExpressionSourceCode("$__lldb_expr", prefix, body, true);
+ }
+
+ uint32_t GetNumBodyLines();
+
+ /// Generates the source code that will evaluate the expression.
+ ///
+ /// \param text output parameter containing the source code string.
+ /// \param wrapping_language If the expression is supossed to be wrapped,
+ /// then this is the language that should be used for that.
+ /// \param static_method True iff the expression is valuated inside a static
+ /// Objective-C method.
+ /// \param exe_ctx The execution context in which the expression will be
+ /// evaluated.
+ /// \param add_locals True iff local variables should be injected into the
+ /// expression source code.
+ /// \param force_add_all_locals True iff all local variables should be
+ /// injected even if they are not used in the expression.
+ /// \param modules A list of (C++) modules that the expression should import.
+ ///
+ /// \return true iff the source code was successfully generated.
+ bool GetText(std::string &text, lldb::LanguageType wrapping_language,
+ bool static_method, ExecutionContext &exe_ctx, bool add_locals,
+ bool force_add_all_locals,
+ llvm::ArrayRef<std::string> modules) const;
+
+ // Given a string returned by GetText, find the beginning and end of the body
+ // passed to CreateWrapped. Return true if the bounds could be found. This
+ // will also work on text with FixItHints applied.
+ static bool GetOriginalBodyBounds(std::string transformed_text,
+ lldb::LanguageType wrapping_language,
+ size_t &start_loc, size_t &end_loc);
+
+protected:
+ ClangExpressionSourceCode(const char *name, const char *prefix, const char *body,
+ bool wrap) :
+ ExpressionSourceCode(name, prefix, body, wrap) {}
+};
+
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.cpp
new file mode 100644
index 000000000000..b5a2c80b5349
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.cpp
@@ -0,0 +1,66 @@
+//===-- ClangExpressionVariable.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 "ClangExpressionVariable.h"
+
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Stream.h"
+#include "clang/AST/ASTContext.h"
+
+using namespace lldb_private;
+using namespace clang;
+
+ClangExpressionVariable::ClangExpressionVariable(
+ ExecutionContextScope *exe_scope, lldb::ByteOrder byte_order,
+ uint32_t addr_byte_size)
+ : ExpressionVariable(LLVMCastKind::eKindClang), m_parser_vars(),
+ m_jit_vars() {
+ m_flags = EVNone;
+ m_frozen_sp =
+ ValueObjectConstResult::Create(exe_scope, byte_order, addr_byte_size);
+}
+
+ClangExpressionVariable::ClangExpressionVariable(
+ ExecutionContextScope *exe_scope, Value &value, ConstString name,
+ uint16_t flags)
+ : ExpressionVariable(LLVMCastKind::eKindClang), m_parser_vars(),
+ m_jit_vars() {
+ m_flags = flags;
+ m_frozen_sp = ValueObjectConstResult::Create(exe_scope, value, name);
+}
+
+ClangExpressionVariable::ClangExpressionVariable(
+ const lldb::ValueObjectSP &valobj_sp)
+ : ExpressionVariable(LLVMCastKind::eKindClang), m_parser_vars(),
+ m_jit_vars() {
+ m_flags = EVNone;
+ m_frozen_sp = valobj_sp;
+}
+
+ClangExpressionVariable::ClangExpressionVariable(
+ ExecutionContextScope *exe_scope, ConstString name,
+ const TypeFromUser &user_type, lldb::ByteOrder byte_order,
+ uint32_t addr_byte_size)
+ : ExpressionVariable(LLVMCastKind::eKindClang), m_parser_vars(),
+ m_jit_vars() {
+ m_flags = EVNone;
+ m_frozen_sp =
+ ValueObjectConstResult::Create(exe_scope, byte_order, addr_byte_size);
+ SetName(name);
+ SetCompilerType(user_type);
+}
+
+TypeFromUser ClangExpressionVariable::GetTypeFromUser() {
+ TypeFromUser tfu(m_frozen_sp->GetCompilerType());
+ return tfu;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.h
new file mode 100644
index 000000000000..eb7f74f20a20
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.h
@@ -0,0 +1,207 @@
+//===-- ClangExpressionVariable.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 liblldb_ClangExpressionVariable_h_
+#define liblldb_ClangExpressionVariable_h_
+
+#include <signal.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "llvm/Support/Casting.h"
+
+#include "lldb/Core/ClangForward.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Expression/ExpressionVariable.h"
+#include "lldb/Symbol/TaggedASTType.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/lldb-public.h"
+
+namespace llvm {
+class Value;
+}
+
+namespace lldb_private {
+
+class ValueObjectConstResult;
+
+/// \class ClangExpressionVariable ClangExpressionVariable.h
+/// "lldb/Expression/ClangExpressionVariable.h" Encapsulates one variable for
+/// the expression parser.
+///
+/// The expression parser uses variables in three different contexts:
+///
+/// First, it stores persistent variables along with the process for use in
+/// expressions. These persistent variables contain their own data and are
+/// typed.
+///
+/// Second, in an interpreted expression, it stores the local variables for
+/// the expression along with the expression. These variables contain their
+/// own data and are typed.
+///
+/// Third, in a JIT-compiled expression, it stores the variables that the
+/// expression needs to have materialized and dematerialized at each
+/// execution. These do not contain their own data but are named and typed.
+///
+/// This class supports all of these use cases using simple type polymorphism,
+/// and provides necessary support methods. Its interface is RTTI-neutral.
+class ClangExpressionVariable : public ExpressionVariable {
+public:
+ ClangExpressionVariable(ExecutionContextScope *exe_scope,
+ lldb::ByteOrder byte_order, uint32_t addr_byte_size);
+
+ ClangExpressionVariable(ExecutionContextScope *exe_scope, Value &value,
+ ConstString name, uint16_t flags = EVNone);
+
+ ClangExpressionVariable(const lldb::ValueObjectSP &valobj_sp);
+
+ ClangExpressionVariable(ExecutionContextScope *exe_scope,
+ ConstString name,
+ const TypeFromUser &user_type,
+ lldb::ByteOrder byte_order, uint32_t addr_byte_size);
+
+ /// Utility functions for dealing with ExpressionVariableLists in Clang-
+ /// specific ways
+
+ /// Finds a variable by NamedDecl in the list.
+ ///
+ /// \param[in] name
+ /// The name of the requested variable.
+ ///
+ /// \return
+ /// The variable requested, or NULL if that variable is not in the list.
+ static ClangExpressionVariable *
+ FindVariableInList(ExpressionVariableList &list, const clang::NamedDecl *decl,
+ uint64_t parser_id) {
+ lldb::ExpressionVariableSP var_sp;
+ for (size_t index = 0, size = list.GetSize(); index < size; ++index) {
+ var_sp = list.GetVariableAtIndex(index);
+
+ if (ClangExpressionVariable *clang_var =
+ llvm::dyn_cast<ClangExpressionVariable>(var_sp.get())) {
+ ClangExpressionVariable::ParserVars *parser_vars =
+ clang_var->GetParserVars(parser_id);
+
+ if (parser_vars && parser_vars->m_named_decl == decl)
+ return clang_var;
+ }
+ }
+ return nullptr;
+ }
+
+ /// If the variable contains its own data, make a Value point at it. If \a
+ /// exe_ctx in not NULL, the value will be resolved in with that execution
+ /// context.
+ ///
+ /// \param[in] value
+ /// The value to point at the data.
+ ///
+ /// \param[in] exe_ctx
+ /// The execution context to use to resolve \a value.
+ ///
+ /// \return
+ /// True on success; false otherwise (in particular, if this variable
+ /// does not contain its own data).
+ bool PointValueAtData(Value &value, ExecutionContext *exe_ctx);
+
+ /// The following values should not live beyond parsing
+ class ParserVars {
+ public:
+ ParserVars()
+ : m_parser_type(), m_named_decl(nullptr), m_llvm_value(nullptr),
+ m_lldb_value(), m_lldb_var(), m_lldb_sym(nullptr) {}
+
+ TypeFromParser
+ m_parser_type; ///< The type of the variable according to the parser
+ const clang::NamedDecl
+ *m_named_decl; ///< The Decl corresponding to this variable
+ llvm::Value *m_llvm_value; ///< The IR value corresponding to this variable;
+ ///usually a GlobalValue
+ lldb_private::Value
+ m_lldb_value; ///< The value found in LLDB for this variable
+ lldb::VariableSP m_lldb_var; ///< The original variable for this variable
+ const lldb_private::Symbol *m_lldb_sym; ///< The original symbol for this
+ ///variable, if it was a symbol
+ };
+
+private:
+ typedef std::map<uint64_t, ParserVars> ParserVarMap;
+ ParserVarMap m_parser_vars;
+
+public:
+ /// Make this variable usable by the parser by allocating space for parser-
+ /// specific variables
+ void EnableParserVars(uint64_t parser_id) {
+ m_parser_vars.insert(std::make_pair(parser_id, ParserVars()));
+ }
+
+ /// Deallocate parser-specific variables
+ void DisableParserVars(uint64_t parser_id) { m_parser_vars.erase(parser_id); }
+
+ /// Access parser-specific variables
+ ParserVars *GetParserVars(uint64_t parser_id) {
+ ParserVarMap::iterator i = m_parser_vars.find(parser_id);
+
+ if (i == m_parser_vars.end())
+ return nullptr;
+ else
+ return &i->second;
+ }
+
+ /// The following values are valid if the variable is used by JIT code
+ struct JITVars {
+ JITVars() : m_alignment(0), m_size(0), m_offset(0) {}
+
+ lldb::offset_t
+ m_alignment; ///< The required alignment of the variable, in bytes
+ size_t m_size; ///< The space required for the variable, in bytes
+ lldb::offset_t
+ m_offset; ///< The offset of the variable in the struct, in bytes
+ };
+
+private:
+ typedef std::map<uint64_t, JITVars> JITVarMap;
+ JITVarMap m_jit_vars;
+
+public:
+ /// Make this variable usable for materializing for the JIT by allocating
+ /// space for JIT-specific variables
+ void EnableJITVars(uint64_t parser_id) {
+ m_jit_vars.insert(std::make_pair(parser_id, JITVars()));
+ }
+
+ /// Deallocate JIT-specific variables
+ void DisableJITVars(uint64_t parser_id) { m_jit_vars.erase(parser_id); }
+
+ JITVars *GetJITVars(uint64_t parser_id) {
+ JITVarMap::iterator i = m_jit_vars.find(parser_id);
+
+ if (i == m_jit_vars.end())
+ return nullptr;
+ else
+ return &i->second;
+ }
+
+ TypeFromUser GetTypeFromUser();
+
+ // llvm casting support
+ static bool classof(const ExpressionVariable *ev) {
+ return ev->getKind() == ExpressionVariable::eKindClang;
+ }
+
+ /// Members
+ DISALLOW_COPY_AND_ASSIGN(ClangExpressionVariable);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ClangExpressionVariable_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.cpp
new file mode 100644
index 000000000000..eabc96aa8e51
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.cpp
@@ -0,0 +1,215 @@
+//===-- ClangFunctionCaller.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 "ClangFunctionCaller.h"
+
+#include "ASTStructExtractor.h"
+#include "ClangExpressionParser.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecordLayout.h"
+#include "clang/CodeGen/CodeGenAction.h"
+#include "clang/CodeGen/ModuleBuilder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/ExecutionEngine/ExecutionEngine.h"
+#include "llvm/IR/Module.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectList.h"
+#include "lldb/Expression/IRExecutionUnit.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/ThreadPlanCallFunction.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+
+using namespace lldb_private;
+
+// ClangFunctionCaller constructor
+ClangFunctionCaller::ClangFunctionCaller(ExecutionContextScope &exe_scope,
+ const CompilerType &return_type,
+ const Address &functionAddress,
+ const ValueList &arg_value_list,
+ const char *name)
+ : FunctionCaller(exe_scope, return_type, functionAddress, arg_value_list,
+ name),
+ m_type_system_helper(*this) {
+ m_jit_process_wp = lldb::ProcessWP(exe_scope.CalculateProcess());
+ // Can't make a ClangFunctionCaller without a process.
+ assert(m_jit_process_wp.lock());
+}
+
+// Destructor
+ClangFunctionCaller::~ClangFunctionCaller() {}
+
+unsigned
+
+ClangFunctionCaller::CompileFunction(lldb::ThreadSP thread_to_use_sp,
+ DiagnosticManager &diagnostic_manager) {
+ if (m_compiled)
+ return 0;
+
+ // Compilation might call code, make sure to keep on the thread the caller
+ // indicated.
+ ThreadList::ExpressionExecutionThreadPusher execution_thread_pusher(
+ thread_to_use_sp);
+
+ // FIXME: How does clang tell us there's no return value? We need to handle
+ // that case.
+ unsigned num_errors = 0;
+
+ std::string return_type_str(
+ m_function_return_type.GetTypeName().AsCString(""));
+
+ // Cons up the function we're going to wrap our call in, then compile it...
+ // We declare the function "extern "C"" because the compiler might be in C++
+ // mode which would mangle the name and then we couldn't find it again...
+ m_wrapper_function_text.clear();
+ m_wrapper_function_text.append("extern \"C\" void ");
+ m_wrapper_function_text.append(m_wrapper_function_name);
+ m_wrapper_function_text.append(" (void *input)\n{\n struct ");
+ m_wrapper_function_text.append(m_wrapper_struct_name);
+ m_wrapper_function_text.append(" \n {\n");
+ m_wrapper_function_text.append(" ");
+ m_wrapper_function_text.append(return_type_str);
+ m_wrapper_function_text.append(" (*fn_ptr) (");
+
+ // Get the number of arguments. If we have a function type and it is
+ // prototyped, trust that, otherwise use the values we were given.
+
+ // FIXME: This will need to be extended to handle Variadic functions. We'll
+ // need
+ // to pull the defined arguments out of the function, then add the types from
+ // the arguments list for the variable arguments.
+
+ uint32_t num_args = UINT32_MAX;
+ bool trust_function = false;
+ // GetArgumentCount returns -1 for an unprototyped function.
+ CompilerType function_clang_type;
+ if (m_function_ptr) {
+ function_clang_type = m_function_ptr->GetCompilerType();
+ if (function_clang_type) {
+ int num_func_args = function_clang_type.GetFunctionArgumentCount();
+ if (num_func_args >= 0) {
+ trust_function = true;
+ num_args = num_func_args;
+ }
+ }
+ }
+
+ if (num_args == UINT32_MAX)
+ num_args = m_arg_values.GetSize();
+
+ std::string args_buffer; // This one stores the definition of all the args in
+ // "struct caller".
+ std::string args_list_buffer; // This one stores the argument list called from
+ // the structure.
+ for (size_t i = 0; i < num_args; i++) {
+ std::string type_name;
+
+ if (trust_function) {
+ type_name = function_clang_type.GetFunctionArgumentTypeAtIndex(i)
+ .GetTypeName()
+ .AsCString("");
+ } else {
+ CompilerType clang_qual_type =
+ m_arg_values.GetValueAtIndex(i)->GetCompilerType();
+ if (clang_qual_type) {
+ type_name = clang_qual_type.GetTypeName().AsCString("");
+ } else {
+ diagnostic_manager.Printf(
+ eDiagnosticSeverityError,
+ "Could not determine type of input value %" PRIu64 ".",
+ (uint64_t)i);
+ return 1;
+ }
+ }
+
+ m_wrapper_function_text.append(type_name);
+ if (i < num_args - 1)
+ m_wrapper_function_text.append(", ");
+
+ char arg_buf[32];
+ args_buffer.append(" ");
+ args_buffer.append(type_name);
+ snprintf(arg_buf, 31, "arg_%" PRIu64, (uint64_t)i);
+ args_buffer.push_back(' ');
+ args_buffer.append(arg_buf);
+ args_buffer.append(";\n");
+
+ args_list_buffer.append("__lldb_fn_data->");
+ args_list_buffer.append(arg_buf);
+ if (i < num_args - 1)
+ args_list_buffer.append(", ");
+ }
+ m_wrapper_function_text.append(
+ ");\n"); // Close off the function calling prototype.
+
+ m_wrapper_function_text.append(args_buffer);
+
+ m_wrapper_function_text.append(" ");
+ m_wrapper_function_text.append(return_type_str);
+ m_wrapper_function_text.append(" return_value;");
+ m_wrapper_function_text.append("\n };\n struct ");
+ m_wrapper_function_text.append(m_wrapper_struct_name);
+ m_wrapper_function_text.append("* __lldb_fn_data = (struct ");
+ m_wrapper_function_text.append(m_wrapper_struct_name);
+ m_wrapper_function_text.append(" *) input;\n");
+
+ m_wrapper_function_text.append(
+ " __lldb_fn_data->return_value = __lldb_fn_data->fn_ptr (");
+ m_wrapper_function_text.append(args_list_buffer);
+ m_wrapper_function_text.append(");\n}\n");
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+ if (log)
+ log->Printf("Expression: \n\n%s\n\n", m_wrapper_function_text.c_str());
+
+ // Okay, now compile this expression
+
+ lldb::ProcessSP jit_process_sp(m_jit_process_wp.lock());
+ if (jit_process_sp) {
+ const bool generate_debug_info = true;
+ m_parser.reset(new ClangExpressionParser(jit_process_sp.get(), *this,
+ generate_debug_info));
+
+ num_errors = m_parser->Parse(diagnostic_manager);
+ } else {
+ diagnostic_manager.PutString(eDiagnosticSeverityError,
+ "no process - unable to inject function");
+ num_errors = 1;
+ }
+
+ m_compiled = (num_errors == 0);
+
+ if (!m_compiled)
+ return num_errors;
+
+ return num_errors;
+}
+
+clang::ASTConsumer *
+ClangFunctionCaller::ClangFunctionCallerHelper::ASTTransformer(
+ clang::ASTConsumer *passthrough) {
+ m_struct_extractor.reset(new ASTStructExtractor(
+ passthrough, m_owner.GetWrapperStructName(), m_owner));
+
+ return m_struct_extractor.get();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.h
new file mode 100644
index 000000000000..24f6f2eb91b3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.h
@@ -0,0 +1,153 @@
+//===-- ClangFunctionCaller.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 liblldb_ClangFunctionCaller_h_
+#define liblldb_ClangFunctionCaller_h_
+
+#include "ClangExpressionHelper.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/ClangForward.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectList.h"
+#include "lldb/Expression/FunctionCaller.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Target/Process.h"
+
+namespace lldb_private {
+
+class ASTStructExtractor;
+class ClangExpressionParser;
+
+/// \class ClangFunctionCaller ClangFunctionCaller.h
+/// "lldb/Expression/ClangFunctionCaller.h" Encapsulates a function that can
+/// be called.
+///
+/// A given ClangFunctionCaller object can handle a single function signature.
+/// Once constructed, it can set up any number of concurrent calls to
+/// functions with that signature.
+///
+/// It performs the call by synthesizing a structure that contains the pointer
+/// to the function and the arguments that should be passed to that function,
+/// and producing a special-purpose JIT-compiled function that accepts a void*
+/// pointing to this struct as its only argument and calls the function in the
+/// struct with the written arguments. This method lets Clang handle the
+/// vagaries of function calling conventions.
+///
+/// The simplest use of the ClangFunctionCaller is to construct it with a
+/// function representative of the signature you want to use, then call
+/// ExecuteFunction(ExecutionContext &, Stream &, Value &).
+///
+/// If you need to reuse the arguments for several calls, you can call
+/// InsertFunction() followed by WriteFunctionArguments(), which will return
+/// the location of the args struct for the wrapper function in args_addr_ref.
+///
+/// If you need to call the function on the thread plan stack, you can also
+/// call InsertFunction() followed by GetThreadPlanToCallFunction().
+///
+/// Any of the methods that take arg_addr_ptr or arg_addr_ref can be passed a
+/// pointer set to LLDB_INVALID_ADDRESS and new structure will be allocated
+/// and its address returned in that variable.
+///
+/// Any of the methods that take arg_addr_ptr can be passed NULL, and the
+/// argument space will be managed for you.
+class ClangFunctionCaller : public FunctionCaller {
+ friend class ASTStructExtractor;
+
+ /// LLVM-style RTTI support.
+ static bool classof(const Expression *E) {
+ return E->getKind() == eKindClangFunctionCaller;
+ }
+
+ class ClangFunctionCallerHelper : public ClangExpressionHelper {
+ public:
+ ClangFunctionCallerHelper(ClangFunctionCaller &owner) : m_owner(owner) {}
+
+ ~ClangFunctionCallerHelper() override = default;
+
+ /// Return the object that the parser should use when resolving external
+ /// values. May be NULL if everything should be self-contained.
+ ClangExpressionDeclMap *DeclMap() override { return nullptr; }
+
+ /// Return the object that the parser should allow to access ASTs. May be
+ /// NULL if the ASTs do not need to be transformed.
+ ///
+ /// \param[in] passthrough
+ /// The ASTConsumer that the returned transformer should send
+ /// the ASTs to after transformation.
+ clang::ASTConsumer *
+ ASTTransformer(clang::ASTConsumer *passthrough) override;
+
+ private:
+ ClangFunctionCaller &m_owner;
+ std::unique_ptr<ASTStructExtractor> m_struct_extractor; ///< The class that
+ ///generates the
+ ///argument struct
+ ///layout.
+ };
+
+public:
+ /// Constructor
+ ///
+ /// \param[in] exe_scope
+ /// An execution context scope that gets us at least a target and
+ /// process.
+ ///
+ /// \param[in] ast_context
+ /// The AST context to evaluate argument types in.
+ ///
+ /// \param[in] return_qualtype
+ /// An opaque Clang QualType for the function result. Should be
+ /// defined in ast_context.
+ ///
+ /// \param[in] function_address
+ /// The address of the function to call.
+ ///
+ /// \param[in] arg_value_list
+ /// The default values to use when calling this function. Can
+ /// be overridden using WriteFunctionArguments().
+ ClangFunctionCaller(ExecutionContextScope &exe_scope,
+ const CompilerType &return_type,
+ const Address &function_address,
+ const ValueList &arg_value_list, const char *name);
+
+ ~ClangFunctionCaller() override;
+
+ /// Compile the wrapper function
+ ///
+ /// \param[in] thread_to_use_sp
+ /// Compilation might end up calling functions. Pass in the thread you
+ /// want the compilation to use. If you pass in an empty ThreadSP it will
+ /// use the currently selected thread.
+ ///
+ /// \param[in] diagnostic_manager
+ /// The diagnostic manager to report parser errors to.
+ ///
+ /// \return
+ /// The number of errors.
+ unsigned CompileFunction(lldb::ThreadSP thread_to_use_sp,
+ DiagnosticManager &diagnostic_manager) override;
+
+ ExpressionTypeSystemHelper *GetTypeSystemHelper() override {
+ return &m_type_system_helper;
+ }
+
+protected:
+ const char *GetWrapperStructName() { return m_wrapper_struct_name.c_str(); }
+
+private:
+ // For ClangFunctionCaller only
+
+ // Note: the parser needs to be destructed before the execution unit, so
+ // declare the execution unit first.
+ ClangFunctionCallerHelper m_type_system_helper;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ClangFunctionCaller_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.cpp
new file mode 100644
index 000000000000..65c547391831
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.cpp
@@ -0,0 +1,168 @@
+//===-- ClangHost.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 "ClangHost.h"
+
+#include "clang/Basic/Version.h"
+#include "clang/Config/config.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Threading.h"
+
+#include "lldb/Host/Config.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+
+#include <string>
+
+using namespace lldb_private;
+
+static bool VerifyClangPath(const llvm::Twine &clang_path) {
+ if (FileSystem::Instance().IsDirectory(clang_path))
+ return true;
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+ if (log)
+ log->Printf("VerifyClangPath(): "
+ "failed to stat clang resource directory at \"%s\"",
+ clang_path.str().c_str());
+ return false;
+}
+
+///
+/// This will compute the clang resource directory assuming that clang was
+/// installed with the same prefix as lldb.
+///
+/// If verify is true, the first candidate resource directory will be returned.
+/// This mode is only used for testing.
+///
+static bool DefaultComputeClangResourceDirectory(FileSpec &lldb_shlib_spec,
+ FileSpec &file_spec,
+ bool verify) {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+ std::string raw_path = lldb_shlib_spec.GetPath();
+ llvm::StringRef parent_dir = llvm::sys::path::parent_path(raw_path);
+
+ static const llvm::StringRef kResourceDirSuffixes[] = {
+ // LLVM.org's build of LLDB uses the clang resource directory placed
+ // in $install_dir/lib{,64}/clang/$clang_version.
+ "lib" CLANG_LIBDIR_SUFFIX "/clang/" CLANG_VERSION_STRING,
+ // swift-lldb uses the clang resource directory copied from swift, which
+ // by default is placed in $install_dir/lib{,64}/lldb/clang. LLDB places
+ // it there, so we use LLDB_LIBDIR_SUFFIX.
+ "lib" LLDB_LIBDIR_SUFFIX "/lldb/clang",
+ };
+
+ for (const auto &Suffix : kResourceDirSuffixes) {
+ llvm::SmallString<256> clang_dir(parent_dir);
+ llvm::SmallString<32> relative_path(Suffix);
+ llvm::sys::path::native(relative_path);
+ llvm::sys::path::append(clang_dir, relative_path);
+ if (!verify || VerifyClangPath(clang_dir)) {
+ if (log)
+ log->Printf("DefaultComputeClangResourceDir: Setting ClangResourceDir "
+ "to \"%s\", verify = %s",
+ clang_dir.str().str().c_str(), verify ? "true" : "false");
+ file_spec.GetDirectory().SetString(clang_dir);
+ FileSystem::Instance().Resolve(file_spec);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool lldb_private::ComputeClangResourceDirectory(FileSpec &lldb_shlib_spec,
+ FileSpec &file_spec, bool verify) {
+#if !defined(__APPLE__)
+ return DefaultComputeClangResourceDirectory(lldb_shlib_spec, file_spec,
+ verify);
+#else
+ std::string raw_path = lldb_shlib_spec.GetPath();
+
+ auto rev_it = llvm::sys::path::rbegin(raw_path);
+ auto r_end = llvm::sys::path::rend(raw_path);
+
+ // Check for a Posix-style build of LLDB.
+ while (rev_it != r_end) {
+ if (*rev_it == "LLDB.framework")
+ break;
+ ++rev_it;
+ }
+
+ // We found a non-framework build of LLDB
+ if (rev_it == r_end)
+ return DefaultComputeClangResourceDirectory(lldb_shlib_spec, file_spec,
+ verify);
+
+ // Inside Xcode and in Xcode toolchains LLDB is always in lockstep
+ // with the Swift compiler, so it can reuse its Clang resource
+ // directory. This allows LLDB and the Swift compiler to share the
+ // same Clang module cache.
+ llvm::SmallString<256> clang_path;
+ const char *swift_clang_resource_dir = "usr/lib/swift/clang";
+ auto parent = std::next(rev_it);
+ if (parent != r_end && *parent == "SharedFrameworks") {
+ // This is the top-level LLDB in the Xcode.app bundle.
+ // E.g., "Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/A"
+ raw_path.resize(parent - r_end);
+ llvm::sys::path::append(clang_path, raw_path,
+ "Developer/Toolchains/XcodeDefault.xctoolchain",
+ swift_clang_resource_dir);
+ if (!verify || VerifyClangPath(clang_path)) {
+ file_spec.GetDirectory().SetString(clang_path.c_str());
+ FileSystem::Instance().Resolve(file_spec);
+ return true;
+ }
+ } else if (parent != r_end && *parent == "PrivateFrameworks" &&
+ std::distance(parent, r_end) > 2) {
+ ++parent;
+ ++parent;
+ if (*parent == "System") {
+ // This is LLDB inside an Xcode toolchain.
+ // E.g., "Xcode.app/Contents/Developer/Toolchains/" \
+ // "My.xctoolchain/System/Library/PrivateFrameworks/LLDB.framework"
+ raw_path.resize(parent - r_end);
+ llvm::sys::path::append(clang_path, raw_path, swift_clang_resource_dir);
+ if (!verify || VerifyClangPath(clang_path)) {
+ file_spec.GetDirectory().SetString(clang_path.c_str());
+ FileSystem::Instance().Resolve(file_spec);
+ return true;
+ }
+ raw_path = lldb_shlib_spec.GetPath();
+ }
+ raw_path.resize(rev_it - r_end);
+ } else {
+ raw_path.resize(rev_it - r_end);
+ }
+
+ // Fall back to the Clang resource directory inside the framework.
+ raw_path.append("LLDB.framework/Resources/Clang");
+ file_spec.GetDirectory().SetString(raw_path.c_str());
+ FileSystem::Instance().Resolve(file_spec);
+ return true;
+#endif // __APPLE__
+}
+
+FileSpec lldb_private::GetClangResourceDir() {
+ static FileSpec g_cached_resource_dir;
+ static llvm::once_flag g_once_flag;
+ llvm::call_once(g_once_flag, []() {
+ if (FileSpec lldb_file_spec = HostInfo::GetShlibDir())
+ ComputeClangResourceDirectory(lldb_file_spec, g_cached_resource_dir,
+ true);
+ Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+ if (log)
+ log->Printf("GetClangResourceDir() => '%s'",
+ g_cached_resource_dir.GetPath().c_str());
+ });
+ return g_cached_resource_dir;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.h
new file mode 100644
index 000000000000..9d49188178cd
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.h
@@ -0,0 +1,23 @@
+//===-- ClangHost.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_PLUGINS_EXPRESSIONPARSER_CLANG_CLANGHOST_H
+#define LLDB_PLUGINS_EXPRESSIONPARSER_CLANG_CLANGHOST_H
+
+namespace lldb_private {
+
+class FileSpec;
+
+bool ComputeClangResourceDirectory(FileSpec &lldb_shlib_spec,
+ FileSpec &file_spec, bool verify);
+
+FileSpec GetClangResourceDir();
+
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp
new file mode 100644
index 000000000000..4a220790e50d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp
@@ -0,0 +1,716 @@
+//===-- ClangModulesDeclVendor.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 <mutex>
+
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Lex/PreprocessorOptions.h"
+#include "clang/Parse/Parser.h"
+#include "clang/Sema/Lookup.h"
+#include "clang/Serialization/ASTReader.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Threading.h"
+
+#include "ClangHost.h"
+#include "ClangModulesDeclVendor.h"
+#include "ModuleDependencyCollector.h"
+
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/SourceModule.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Reproducer.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb_private;
+
+namespace {
+// Any Clang compiler requires a consumer for diagnostics. This one stores
+// them as strings so we can provide them to the user in case a module failed
+// to load.
+class StoringDiagnosticConsumer : public clang::DiagnosticConsumer {
+public:
+ StoringDiagnosticConsumer();
+
+ void HandleDiagnostic(clang::DiagnosticsEngine::Level DiagLevel,
+ const clang::Diagnostic &info) override;
+
+ void ClearDiagnostics();
+
+ void DumpDiagnostics(Stream &error_stream);
+
+private:
+ typedef std::pair<clang::DiagnosticsEngine::Level, std::string>
+ IDAndDiagnostic;
+ std::vector<IDAndDiagnostic> m_diagnostics;
+ Log *m_log;
+};
+
+// The private implementation of our ClangModulesDeclVendor. Contains all the
+// Clang state required to load modules.
+class ClangModulesDeclVendorImpl : public ClangModulesDeclVendor {
+public:
+ ClangModulesDeclVendorImpl(
+ llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diagnostics_engine,
+ std::shared_ptr<clang::CompilerInvocation> compiler_invocation,
+ std::unique_ptr<clang::CompilerInstance> compiler_instance,
+ std::unique_ptr<clang::Parser> parser);
+
+ ~ClangModulesDeclVendorImpl() override = default;
+
+ bool AddModule(const SourceModule &module, ModuleVector *exported_modules,
+ Stream &error_stream) override;
+
+ bool AddModulesForCompileUnit(CompileUnit &cu, ModuleVector &exported_modules,
+ Stream &error_stream) override;
+
+ uint32_t FindDecls(ConstString name, bool append, uint32_t max_matches,
+ std::vector<clang::NamedDecl *> &decls) override;
+
+ void ForEachMacro(const ModuleVector &modules,
+ std::function<bool(const std::string &)> handler) override;
+
+ clang::ExternalASTMerger::ImporterSource GetImporterSource() override;
+private:
+ void
+ ReportModuleExportsHelper(std::set<ClangModulesDeclVendor::ModuleID> &exports,
+ clang::Module *module);
+
+ void ReportModuleExports(ModuleVector &exports, clang::Module *module);
+
+ clang::ModuleLoadResult DoGetModule(clang::ModuleIdPath path,
+ bool make_visible);
+
+ bool m_enabled = false;
+
+ llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> m_diagnostics_engine;
+ std::shared_ptr<clang::CompilerInvocation> m_compiler_invocation;
+ std::unique_ptr<clang::CompilerInstance> m_compiler_instance;
+ std::unique_ptr<clang::Parser> m_parser;
+ size_t m_source_location_index =
+ 0; // used to give name components fake SourceLocations
+
+ typedef std::vector<ConstString> ImportedModule;
+ typedef std::map<ImportedModule, clang::Module *> ImportedModuleMap;
+ typedef std::set<ModuleID> ImportedModuleSet;
+ ImportedModuleMap m_imported_modules;
+ ImportedModuleSet m_user_imported_modules;
+ const clang::ExternalASTMerger::OriginMap m_origin_map;
+};
+} // anonymous namespace
+
+StoringDiagnosticConsumer::StoringDiagnosticConsumer() {
+ m_log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS);
+}
+
+void StoringDiagnosticConsumer::HandleDiagnostic(
+ clang::DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &info) {
+ llvm::SmallVector<char, 256> diagnostic_string;
+
+ info.FormatDiagnostic(diagnostic_string);
+
+ m_diagnostics.push_back(
+ IDAndDiagnostic(DiagLevel, std::string(diagnostic_string.data(),
+ diagnostic_string.size())));
+}
+
+void StoringDiagnosticConsumer::ClearDiagnostics() { m_diagnostics.clear(); }
+
+void StoringDiagnosticConsumer::DumpDiagnostics(Stream &error_stream) {
+ for (IDAndDiagnostic &diag : m_diagnostics) {
+ switch (diag.first) {
+ default:
+ error_stream.PutCString(diag.second);
+ error_stream.PutChar('\n');
+ break;
+ case clang::DiagnosticsEngine::Level::Ignored:
+ break;
+ }
+ }
+}
+
+ClangModulesDeclVendor::ClangModulesDeclVendor() {}
+
+ClangModulesDeclVendor::~ClangModulesDeclVendor() {}
+
+ClangModulesDeclVendorImpl::ClangModulesDeclVendorImpl(
+ llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diagnostics_engine,
+ std::shared_ptr<clang::CompilerInvocation> compiler_invocation,
+ std::unique_ptr<clang::CompilerInstance> compiler_instance,
+ std::unique_ptr<clang::Parser> parser)
+ : m_diagnostics_engine(std::move(diagnostics_engine)),
+ m_compiler_invocation(std::move(compiler_invocation)),
+ m_compiler_instance(std::move(compiler_instance)),
+ m_parser(std::move(parser)), m_origin_map() {}
+
+void ClangModulesDeclVendorImpl::ReportModuleExportsHelper(
+ std::set<ClangModulesDeclVendor::ModuleID> &exports,
+ clang::Module *module) {
+ if (exports.count(reinterpret_cast<ClangModulesDeclVendor::ModuleID>(module)))
+ return;
+
+ exports.insert(reinterpret_cast<ClangModulesDeclVendor::ModuleID>(module));
+
+ llvm::SmallVector<clang::Module *, 2> sub_exports;
+
+ module->getExportedModules(sub_exports);
+
+ for (clang::Module *module : sub_exports) {
+ ReportModuleExportsHelper(exports, module);
+ }
+}
+
+void ClangModulesDeclVendorImpl::ReportModuleExports(
+ ClangModulesDeclVendor::ModuleVector &exports, clang::Module *module) {
+ std::set<ClangModulesDeclVendor::ModuleID> exports_set;
+
+ ReportModuleExportsHelper(exports_set, module);
+
+ for (ModuleID module : exports_set) {
+ exports.push_back(module);
+ }
+}
+
+bool ClangModulesDeclVendorImpl::AddModule(const SourceModule &module,
+ ModuleVector *exported_modules,
+ Stream &error_stream) {
+ // Fail early.
+
+ if (m_compiler_instance->hadModuleLoaderFatalFailure()) {
+ error_stream.PutCString("error: Couldn't load a module because the module "
+ "loader is in a fatal state.\n");
+ return false;
+ }
+
+ // Check if we've already imported this module.
+
+ std::vector<ConstString> imported_module;
+
+ for (ConstString path_component : module.path) {
+ imported_module.push_back(path_component);
+ }
+
+ {
+ ImportedModuleMap::iterator mi = m_imported_modules.find(imported_module);
+
+ if (mi != m_imported_modules.end()) {
+ if (exported_modules) {
+ ReportModuleExports(*exported_modules, mi->second);
+ }
+ return true;
+ }
+ }
+
+ clang::HeaderSearch &HS =
+ m_compiler_instance->getPreprocessor().getHeaderSearchInfo();
+
+ if (module.search_path) {
+ auto path_begin = llvm::sys::path::begin(module.search_path.GetStringRef());
+ auto path_end = llvm::sys::path::end(module.search_path.GetStringRef());
+ auto sysroot_begin = llvm::sys::path::begin(module.sysroot.GetStringRef());
+ auto sysroot_end = llvm::sys::path::end(module.sysroot.GetStringRef());
+ // FIXME: Use C++14 std::equal(it, it, it, it) variant once it's available.
+ bool is_system_module = (std::distance(path_begin, path_end) >=
+ std::distance(sysroot_begin, sysroot_end)) &&
+ std::equal(sysroot_begin, sysroot_end, path_begin);
+ // No need to inject search paths to modules in the sysroot.
+ if (!is_system_module) {
+ auto error = [&]() {
+ error_stream.Printf("error: No module map file in %s\n",
+ module.search_path.AsCString());
+ return false;
+ };
+
+ bool is_system = true;
+ bool is_framework = false;
+ auto *dir =
+ HS.getFileMgr().getDirectory(module.search_path.GetStringRef());
+ if (!dir)
+ return error();
+ auto *file = HS.lookupModuleMapFile(dir, is_framework);
+ if (!file)
+ return error();
+ if (!HS.loadModuleMapFile(file, is_system))
+ return error();
+ }
+ }
+ if (!HS.lookupModule(module.path.front().GetStringRef())) {
+ error_stream.Printf("error: Header search couldn't locate module %s\n",
+ module.path.front().AsCString());
+ return false;
+ }
+
+ llvm::SmallVector<std::pair<clang::IdentifierInfo *, clang::SourceLocation>,
+ 4>
+ clang_path;
+
+ {
+ clang::SourceManager &source_manager =
+ m_compiler_instance->getASTContext().getSourceManager();
+
+ for (ConstString path_component : module.path) {
+ clang_path.push_back(std::make_pair(
+ &m_compiler_instance->getASTContext().Idents.get(
+ path_component.GetStringRef()),
+ source_manager.getLocForStartOfFile(source_manager.getMainFileID())
+ .getLocWithOffset(m_source_location_index++)));
+ }
+ }
+
+ StoringDiagnosticConsumer *diagnostic_consumer =
+ static_cast<StoringDiagnosticConsumer *>(
+ m_compiler_instance->getDiagnostics().getClient());
+
+ diagnostic_consumer->ClearDiagnostics();
+
+ clang::Module *top_level_module = DoGetModule(clang_path.front(), false);
+
+ if (!top_level_module) {
+ diagnostic_consumer->DumpDiagnostics(error_stream);
+ error_stream.Printf("error: Couldn't load top-level module %s\n",
+ module.path.front().AsCString());
+ return false;
+ }
+
+ clang::Module *submodule = top_level_module;
+
+ for (auto &component : llvm::ArrayRef<ConstString>(module.path).drop_front()) {
+ submodule = submodule->findSubmodule(component.GetStringRef());
+ if (!submodule) {
+ diagnostic_consumer->DumpDiagnostics(error_stream);
+ error_stream.Printf("error: Couldn't load submodule %s\n",
+ component.GetCString());
+ return false;
+ }
+ }
+
+ clang::Module *requested_module = DoGetModule(clang_path, true);
+
+ if (requested_module != nullptr) {
+ if (exported_modules) {
+ ReportModuleExports(*exported_modules, requested_module);
+ }
+
+ m_imported_modules[imported_module] = requested_module;
+
+ m_enabled = true;
+
+ return true;
+ }
+
+ return false;
+}
+
+bool ClangModulesDeclVendor::LanguageSupportsClangModules(
+ lldb::LanguageType language) {
+ switch (language) {
+ default:
+ return false;
+ case lldb::LanguageType::eLanguageTypeC:
+ case lldb::LanguageType::eLanguageTypeC11:
+ case lldb::LanguageType::eLanguageTypeC89:
+ case lldb::LanguageType::eLanguageTypeC99:
+ case lldb::LanguageType::eLanguageTypeC_plus_plus:
+ case lldb::LanguageType::eLanguageTypeC_plus_plus_03:
+ case lldb::LanguageType::eLanguageTypeC_plus_plus_11:
+ case lldb::LanguageType::eLanguageTypeC_plus_plus_14:
+ case lldb::LanguageType::eLanguageTypeObjC:
+ case lldb::LanguageType::eLanguageTypeObjC_plus_plus:
+ return true;
+ }
+}
+
+bool ClangModulesDeclVendorImpl::AddModulesForCompileUnit(
+ CompileUnit &cu, ClangModulesDeclVendor::ModuleVector &exported_modules,
+ Stream &error_stream) {
+ if (LanguageSupportsClangModules(cu.GetLanguage())) {
+ for (auto &imported_module : cu.GetImportedModules())
+ if (!AddModule(imported_module, &exported_modules, error_stream))
+ return false;
+ }
+ return true;
+}
+
+// ClangImporter::lookupValue
+
+uint32_t
+ClangModulesDeclVendorImpl::FindDecls(ConstString name, bool append,
+ uint32_t max_matches,
+ std::vector<clang::NamedDecl *> &decls) {
+ if (!m_enabled) {
+ return 0;
+ }
+
+ if (!append)
+ decls.clear();
+
+ clang::IdentifierInfo &ident =
+ m_compiler_instance->getASTContext().Idents.get(name.GetStringRef());
+
+ clang::LookupResult lookup_result(
+ m_compiler_instance->getSema(), clang::DeclarationName(&ident),
+ clang::SourceLocation(), clang::Sema::LookupOrdinaryName);
+
+ m_compiler_instance->getSema().LookupName(
+ lookup_result,
+ m_compiler_instance->getSema().getScopeForContext(
+ m_compiler_instance->getASTContext().getTranslationUnitDecl()));
+
+ uint32_t num_matches = 0;
+
+ for (clang::NamedDecl *named_decl : lookup_result) {
+ if (num_matches >= max_matches)
+ return num_matches;
+
+ decls.push_back(named_decl);
+ ++num_matches;
+ }
+
+ return num_matches;
+}
+
+void ClangModulesDeclVendorImpl::ForEachMacro(
+ const ClangModulesDeclVendor::ModuleVector &modules,
+ std::function<bool(const std::string &)> handler) {
+ if (!m_enabled) {
+ return;
+ }
+
+ typedef std::map<ModuleID, ssize_t> ModulePriorityMap;
+ ModulePriorityMap module_priorities;
+
+ ssize_t priority = 0;
+
+ for (ModuleID module : modules) {
+ module_priorities[module] = priority++;
+ }
+
+ if (m_compiler_instance->getPreprocessor().getExternalSource()) {
+ m_compiler_instance->getPreprocessor()
+ .getExternalSource()
+ ->ReadDefinedMacros();
+ }
+
+ for (clang::Preprocessor::macro_iterator
+ mi = m_compiler_instance->getPreprocessor().macro_begin(),
+ me = m_compiler_instance->getPreprocessor().macro_end();
+ mi != me; ++mi) {
+ const clang::IdentifierInfo *ii = nullptr;
+
+ {
+ if (clang::IdentifierInfoLookup *lookup =
+ m_compiler_instance->getPreprocessor()
+ .getIdentifierTable()
+ .getExternalIdentifierLookup()) {
+ lookup->get(mi->first->getName());
+ }
+ if (!ii) {
+ ii = mi->first;
+ }
+ }
+
+ ssize_t found_priority = -1;
+ clang::MacroInfo *macro_info = nullptr;
+
+ for (clang::ModuleMacro *module_macro :
+ m_compiler_instance->getPreprocessor().getLeafModuleMacros(ii)) {
+ clang::Module *module = module_macro->getOwningModule();
+
+ {
+ ModulePriorityMap::iterator pi =
+ module_priorities.find(reinterpret_cast<ModuleID>(module));
+
+ if (pi != module_priorities.end() && pi->second > found_priority) {
+ macro_info = module_macro->getMacroInfo();
+ found_priority = pi->second;
+ }
+ }
+
+ clang::Module *top_level_module = module->getTopLevelModule();
+
+ if (top_level_module != module) {
+ ModulePriorityMap::iterator pi = module_priorities.find(
+ reinterpret_cast<ModuleID>(top_level_module));
+
+ if ((pi != module_priorities.end()) && pi->second > found_priority) {
+ macro_info = module_macro->getMacroInfo();
+ found_priority = pi->second;
+ }
+ }
+ }
+
+ if (macro_info) {
+ std::string macro_expansion = "#define ";
+ macro_expansion.append(mi->first->getName().str());
+
+ {
+ if (macro_info->isFunctionLike()) {
+ macro_expansion.append("(");
+
+ bool first_arg = true;
+
+ for (auto pi = macro_info->param_begin(),
+ pe = macro_info->param_end();
+ pi != pe; ++pi) {
+ if (!first_arg) {
+ macro_expansion.append(", ");
+ } else {
+ first_arg = false;
+ }
+
+ macro_expansion.append((*pi)->getName().str());
+ }
+
+ if (macro_info->isC99Varargs()) {
+ if (first_arg) {
+ macro_expansion.append("...");
+ } else {
+ macro_expansion.append(", ...");
+ }
+ } else if (macro_info->isGNUVarargs()) {
+ macro_expansion.append("...");
+ }
+
+ macro_expansion.append(")");
+ }
+
+ macro_expansion.append(" ");
+
+ bool first_token = true;
+
+ for (clang::MacroInfo::tokens_iterator ti = macro_info->tokens_begin(),
+ te = macro_info->tokens_end();
+ ti != te; ++ti) {
+ if (!first_token) {
+ macro_expansion.append(" ");
+ } else {
+ first_token = false;
+ }
+
+ if (ti->isLiteral()) {
+ if (const char *literal_data = ti->getLiteralData()) {
+ std::string token_str(literal_data, ti->getLength());
+ macro_expansion.append(token_str);
+ } else {
+ bool invalid = false;
+ const char *literal_source =
+ m_compiler_instance->getSourceManager().getCharacterData(
+ ti->getLocation(), &invalid);
+
+ if (invalid) {
+ lldbassert(0 && "Unhandled token kind");
+ macro_expansion.append("<unknown literal value>");
+ } else {
+ macro_expansion.append(
+ std::string(literal_source, ti->getLength()));
+ }
+ }
+ } else if (const char *punctuator_spelling =
+ clang::tok::getPunctuatorSpelling(ti->getKind())) {
+ macro_expansion.append(punctuator_spelling);
+ } else if (const char *keyword_spelling =
+ clang::tok::getKeywordSpelling(ti->getKind())) {
+ macro_expansion.append(keyword_spelling);
+ } else {
+ switch (ti->getKind()) {
+ case clang::tok::TokenKind::identifier:
+ macro_expansion.append(ti->getIdentifierInfo()->getName().str());
+ break;
+ case clang::tok::TokenKind::raw_identifier:
+ macro_expansion.append(ti->getRawIdentifier().str());
+ break;
+ default:
+ macro_expansion.append(ti->getName());
+ break;
+ }
+ }
+ }
+
+ if (handler(macro_expansion)) {
+ return;
+ }
+ }
+ }
+ }
+}
+
+clang::ModuleLoadResult
+ClangModulesDeclVendorImpl::DoGetModule(clang::ModuleIdPath path,
+ bool make_visible) {
+ clang::Module::NameVisibilityKind visibility =
+ make_visible ? clang::Module::AllVisible : clang::Module::Hidden;
+
+ const bool is_inclusion_directive = false;
+
+ return m_compiler_instance->loadModule(path.front().second, path, visibility,
+ is_inclusion_directive);
+}
+
+clang::ExternalASTMerger::ImporterSource
+ClangModulesDeclVendorImpl::GetImporterSource() {
+ return {m_compiler_instance->getASTContext(),
+ m_compiler_instance->getFileManager(), m_origin_map};
+}
+
+static const char *ModuleImportBufferName = "LLDBModulesMemoryBuffer";
+
+lldb_private::ClangModulesDeclVendor *
+ClangModulesDeclVendor::Create(Target &target) {
+ // FIXME we should insure programmatically that the expression parser's
+ // compiler and the modules runtime's
+ // compiler are both initialized in the same way – preferably by the same
+ // code.
+
+ if (!target.GetPlatform()->SupportsModules())
+ return nullptr;
+
+ const ArchSpec &arch = target.GetArchitecture();
+
+ std::vector<std::string> compiler_invocation_arguments = {
+ "clang",
+ "-fmodules",
+ "-fimplicit-module-maps",
+ "-fcxx-modules",
+ "-fsyntax-only",
+ "-femit-all-decls",
+ "-target",
+ arch.GetTriple().str(),
+ "-fmodules-validate-system-headers",
+ "-Werror=non-modular-include-in-framework-module"};
+
+ target.GetPlatform()->AddClangModuleCompilationOptions(
+ &target, compiler_invocation_arguments);
+
+ compiler_invocation_arguments.push_back(ModuleImportBufferName);
+
+ // Add additional search paths with { "-I", path } or { "-F", path } here.
+
+ {
+ llvm::SmallString<128> path;
+ auto props = ModuleList::GetGlobalModuleListProperties();
+ props.GetClangModulesCachePath().GetPath(path);
+ std::string module_cache_argument("-fmodules-cache-path=");
+ module_cache_argument.append(path.str());
+ compiler_invocation_arguments.push_back(module_cache_argument);
+ }
+
+ FileSpecList module_search_paths = target.GetClangModuleSearchPaths();
+
+ for (size_t spi = 0, spe = module_search_paths.GetSize(); spi < spe; ++spi) {
+ const FileSpec &search_path = module_search_paths.GetFileSpecAtIndex(spi);
+
+ std::string search_path_argument = "-I";
+ search_path_argument.append(search_path.GetPath());
+
+ compiler_invocation_arguments.push_back(search_path_argument);
+ }
+
+ {
+ FileSpec clang_resource_dir = GetClangResourceDir();
+
+ if (FileSystem::Instance().IsDirectory(clang_resource_dir.GetPath())) {
+ compiler_invocation_arguments.push_back("-resource-dir");
+ compiler_invocation_arguments.push_back(clang_resource_dir.GetPath());
+ }
+ }
+
+ llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diagnostics_engine =
+ clang::CompilerInstance::createDiagnostics(new clang::DiagnosticOptions,
+ new StoringDiagnosticConsumer);
+
+ std::vector<const char *> compiler_invocation_argument_cstrs;
+ compiler_invocation_argument_cstrs.reserve(
+ compiler_invocation_arguments.size());
+ for (const std::string &arg : compiler_invocation_arguments)
+ compiler_invocation_argument_cstrs.push_back(arg.c_str());
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+ LLDB_LOG(log, "ClangModulesDeclVendor's compiler flags {0:$[ ]}",
+ llvm::make_range(compiler_invocation_arguments.begin(),
+ compiler_invocation_arguments.end()));
+
+ std::shared_ptr<clang::CompilerInvocation> invocation =
+ clang::createInvocationFromCommandLine(compiler_invocation_argument_cstrs,
+ diagnostics_engine);
+
+ if (!invocation)
+ return nullptr;
+
+ std::unique_ptr<llvm::MemoryBuffer> source_buffer =
+ llvm::MemoryBuffer::getMemBuffer(
+ "extern int __lldb __attribute__((unavailable));",
+ ModuleImportBufferName);
+
+ invocation->getPreprocessorOpts().addRemappedFile(ModuleImportBufferName,
+ source_buffer.release());
+
+ std::unique_ptr<clang::CompilerInstance> instance(
+ new clang::CompilerInstance);
+
+ // When capturing a reproducer, hook up the file collector with clang to
+ // collector modules and headers.
+ if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) {
+ repro::FileProvider &fp = g->GetOrCreate<repro::FileProvider>();
+ instance->setModuleDepCollector(
+ std::make_shared<ModuleDependencyCollectorAdaptor>(
+ fp.GetFileCollector()));
+ clang::DependencyOutputOptions &opts = instance->getDependencyOutputOpts();
+ opts.IncludeSystemHeaders = true;
+ opts.IncludeModuleFiles = true;
+ }
+
+ // Make sure clang uses the same VFS as LLDB.
+ instance->createFileManager(FileSystem::Instance().GetVirtualFileSystem());
+ instance->setDiagnostics(diagnostics_engine.get());
+ instance->setInvocation(invocation);
+
+ std::unique_ptr<clang::FrontendAction> action(new clang::SyntaxOnlyAction);
+
+ instance->setTarget(clang::TargetInfo::CreateTargetInfo(
+ *diagnostics_engine, instance->getInvocation().TargetOpts));
+
+ if (!instance->hasTarget())
+ return nullptr;
+
+ instance->getTarget().adjust(instance->getLangOpts());
+
+ if (!action->BeginSourceFile(*instance,
+ instance->getFrontendOpts().Inputs[0]))
+ return nullptr;
+
+ instance->getPreprocessor().enableIncrementalProcessing();
+
+ instance->createModuleManager();
+
+ instance->createSema(action->getTranslationUnitKind(), nullptr);
+
+ const bool skipFunctionBodies = false;
+ std::unique_ptr<clang::Parser> parser(new clang::Parser(
+ instance->getPreprocessor(), instance->getSema(), skipFunctionBodies));
+
+ instance->getPreprocessor().EnterMainSourceFile();
+ parser->Initialize();
+
+ clang::Parser::DeclGroupPtrTy parsed;
+
+ while (!parser->ParseTopLevelDecl(parsed))
+ ;
+
+ return new ClangModulesDeclVendorImpl(std::move(diagnostics_engine),
+ std::move(invocation),
+ std::move(instance), std::move(parser));
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h
new file mode 100644
index 000000000000..d5c8757bdcd0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h
@@ -0,0 +1,111 @@
+//===-- ClangModulesDeclVendor.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 liblldb_ClangModulesDeclVendor_h
+#define liblldb_ClangModulesDeclVendor_h
+
+#include "lldb/Core/ClangForward.h"
+#include "lldb/Symbol/DeclVendor.h"
+#include "lldb/Symbol/SourceModule.h"
+#include "lldb/Target/Platform.h"
+
+#include <set>
+#include <vector>
+
+namespace lldb_private {
+
+class ClangModulesDeclVendor : public DeclVendor {
+public:
+ // Constructors and Destructors
+ ClangModulesDeclVendor();
+
+ ~ClangModulesDeclVendor() override;
+
+ static ClangModulesDeclVendor *Create(Target &target);
+
+ typedef std::vector<ConstString> ModulePath;
+ typedef uintptr_t ModuleID;
+ typedef std::vector<ModuleID> ModuleVector;
+
+ /// Add a module to the list of modules to search.
+ ///
+ /// \param[in] module
+ /// The path to the exact module to be loaded. E.g., if the desired
+ /// module is std.io, then this should be { "std", "io" }.
+ ///
+ /// \param[in] exported_modules
+ /// If non-NULL, a pointer to a vector to populate with the ID of every
+ /// module that is re-exported by the specified module.
+ ///
+ /// \param[in] error_stream
+ /// A stream to populate with the output of the Clang parser when
+ /// it tries to load the module.
+ ///
+ /// \return
+ /// True if the module could be loaded; false if not. If the
+ /// compiler encountered a fatal error during a previous module
+ /// load, then this will always return false for this ModuleImporter.
+ virtual bool AddModule(const SourceModule &module,
+ ModuleVector *exported_modules,
+ Stream &error_stream) = 0;
+
+ /// Add all modules referred to in a given compilation unit to the list
+ /// of modules to search.
+ ///
+ /// \param[in] cu
+ /// The compilation unit to scan for imported modules.
+ ///
+ /// \param[in] exported_modules
+ /// A vector to populate with the ID of each module loaded (directly
+ /// and via re-exports) in this way.
+ ///
+ /// \param[in] error_stream
+ /// A stream to populate with the output of the Clang parser when
+ /// it tries to load the modules.
+ ///
+ /// \return
+ /// True if all modules referred to by the compilation unit could be
+ /// loaded; false if one could not be loaded. If the compiler
+ /// encountered a fatal error during a previous module
+ /// load, then this will always return false for this ModuleImporter.
+ virtual bool AddModulesForCompileUnit(CompileUnit &cu,
+ ModuleVector &exported_modules,
+ Stream &error_stream) = 0;
+
+ /// Enumerate all the macros that are defined by a given set of modules
+ /// that are already imported.
+ ///
+ /// \param[in] modules
+ /// The unique IDs for all modules to query. Later modules have higher
+ /// priority, just as if you @imported them in that order. This matters
+ /// if module A #defines a macro and module B #undefs it.
+ ///
+ /// \param[in] handler
+ /// A function to call with the text of each #define (including the
+ /// #define directive). #undef directives are not included; we simply
+ /// elide any corresponding #define. If this function returns true,
+ /// we stop the iteration immediately.
+ virtual void
+ ForEachMacro(const ModuleVector &modules,
+ std::function<bool(const std::string &)> handler) = 0;
+
+ /// Query whether Clang supports modules for a particular language.
+ /// LLDB uses this to decide whether to try to find the modules loaded
+ /// by a given compile unit.
+ ///
+ /// \param[in] language
+ /// The language to query for.
+ ///
+ /// \return
+ /// True if Clang has modules for the given language.
+ static bool LanguageSupportsClangModules(lldb::LanguageType language);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ClangModulesDeclVendor_h
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.cpp
new file mode 100644
index 000000000000..742a14992dc9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.cpp
@@ -0,0 +1,94 @@
+//===-- ClangPersistentVariables.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 "ClangPersistentVariables.h"
+
+#include "lldb/Core/Value.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "clang/AST/Decl.h"
+
+#include "llvm/ADT/StringMap.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+ClangPersistentVariables::ClangPersistentVariables()
+ : lldb_private::PersistentExpressionState(LLVMCastKind::eKindClang),
+ m_next_persistent_variable_id(0) {}
+
+ExpressionVariableSP ClangPersistentVariables::CreatePersistentVariable(
+ const lldb::ValueObjectSP &valobj_sp) {
+ return AddNewlyConstructedVariable(new ClangExpressionVariable(valobj_sp));
+}
+
+ExpressionVariableSP ClangPersistentVariables::CreatePersistentVariable(
+ ExecutionContextScope *exe_scope, ConstString name,
+ const CompilerType &compiler_type, lldb::ByteOrder byte_order,
+ uint32_t addr_byte_size) {
+ return AddNewlyConstructedVariable(new ClangExpressionVariable(
+ exe_scope, name, compiler_type, byte_order, addr_byte_size));
+}
+
+void ClangPersistentVariables::RemovePersistentVariable(
+ lldb::ExpressionVariableSP variable) {
+ RemoveVariable(variable);
+
+ const char *name = variable->GetName().AsCString();
+
+ if (*name != '$')
+ return;
+ name++;
+
+ if (strtoul(name, nullptr, 0) == m_next_persistent_variable_id - 1)
+ m_next_persistent_variable_id--;
+}
+
+llvm::Optional<CompilerType>
+ClangPersistentVariables::GetCompilerTypeFromPersistentDecl(
+ ConstString type_name) {
+ CompilerType compiler_type;
+ if (clang::TypeDecl *tdecl = llvm::dyn_cast_or_null<clang::TypeDecl>(
+ GetPersistentDecl(type_name))) {
+ compiler_type.SetCompilerType(
+ ClangASTContext::GetASTContext(&tdecl->getASTContext()),
+ reinterpret_cast<lldb::opaque_compiler_type_t>(
+ const_cast<clang::Type *>(tdecl->getTypeForDecl())));
+ return compiler_type;
+ }
+ return llvm::None;
+}
+
+void ClangPersistentVariables::RegisterPersistentDecl(ConstString name,
+ clang::NamedDecl *decl) {
+ m_persistent_decls.insert(
+ std::pair<const char *, clang::NamedDecl *>(name.GetCString(), decl));
+
+ if (clang::EnumDecl *enum_decl = llvm::dyn_cast<clang::EnumDecl>(decl)) {
+ for (clang::EnumConstantDecl *enumerator_decl : enum_decl->enumerators()) {
+ m_persistent_decls.insert(std::pair<const char *, clang::NamedDecl *>(
+ ConstString(enumerator_decl->getNameAsString()).GetCString(),
+ enumerator_decl));
+ }
+ }
+}
+
+clang::NamedDecl *
+ClangPersistentVariables::GetPersistentDecl(ConstString name) {
+ PersistentDeclMap::const_iterator i =
+ m_persistent_decls.find(name.GetCString());
+
+ if (i == m_persistent_decls.end())
+ return nullptr;
+ else
+ return i->second;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.h
new file mode 100644
index 000000000000..b39f89ad7eef
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.h
@@ -0,0 +1,84 @@
+//===-- ClangPersistentVariables.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 liblldb_ClangPersistentVariables_h_
+#define liblldb_ClangPersistentVariables_h_
+
+#include "llvm/ADT/DenseMap.h"
+
+#include "ClangExpressionVariable.h"
+#include "ClangModulesDeclVendor.h"
+
+#include "lldb/Expression/ExpressionVariable.h"
+
+namespace lldb_private {
+
+/// \class ClangPersistentVariables ClangPersistentVariables.h
+/// "lldb/Expression/ClangPersistentVariables.h" Manages persistent values
+/// that need to be preserved between expression invocations.
+///
+/// A list of variables that can be accessed and updated by any expression. See
+/// ClangPersistentVariable for more discussion. Also provides an increasing,
+/// 0-based counter for naming result variables.
+class ClangPersistentVariables : public PersistentExpressionState {
+public:
+ ClangPersistentVariables();
+
+ ~ClangPersistentVariables() override = default;
+
+ // llvm casting support
+ static bool classof(const PersistentExpressionState *pv) {
+ return pv->getKind() == PersistentExpressionState::eKindClang;
+ }
+
+ lldb::ExpressionVariableSP
+ CreatePersistentVariable(const lldb::ValueObjectSP &valobj_sp) override;
+
+ lldb::ExpressionVariableSP CreatePersistentVariable(
+ ExecutionContextScope *exe_scope, ConstString name,
+ const CompilerType &compiler_type, lldb::ByteOrder byte_order,
+ uint32_t addr_byte_size) override;
+
+ void RemovePersistentVariable(lldb::ExpressionVariableSP variable) override;
+ llvm::StringRef
+ GetPersistentVariablePrefix(bool is_error) const override {
+ return "$";
+ }
+
+ llvm::Optional<CompilerType>
+ GetCompilerTypeFromPersistentDecl(ConstString type_name) override;
+
+ void RegisterPersistentDecl(ConstString name, clang::NamedDecl *decl);
+
+ clang::NamedDecl *GetPersistentDecl(ConstString name);
+
+ void AddHandLoadedClangModule(ClangModulesDeclVendor::ModuleID module) {
+ m_hand_loaded_clang_modules.push_back(module);
+ }
+
+ const ClangModulesDeclVendor::ModuleVector &GetHandLoadedClangModules() {
+ return m_hand_loaded_clang_modules;
+ }
+
+private:
+ uint32_t m_next_persistent_variable_id; ///< The counter used by
+ ///GetNextResultName().
+
+ typedef llvm::DenseMap<const char *, clang::NamedDecl *> PersistentDeclMap;
+ PersistentDeclMap
+ m_persistent_decls; ///< Persistent entities declared by the user.
+
+ ClangModulesDeclVendor::ModuleVector
+ m_hand_loaded_clang_modules; ///< These are Clang modules we hand-loaded;
+ ///these are the highest-
+ ///< priority source for macros.
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ClangPersistentVariables_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
new file mode 100644
index 000000000000..2dae5b7022f3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
@@ -0,0 +1,903 @@
+//===-- ClangUserExpression.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 <stdio.h>
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <cstdlib>
+#include <map>
+#include <string>
+
+#include "ClangUserExpression.h"
+
+#include "ASTResultSynthesizer.h"
+#include "ClangDiagnostic.h"
+#include "ClangExpressionDeclMap.h"
+#include "ClangExpressionParser.h"
+#include "ClangExpressionSourceCode.h"
+#include "ClangModulesDeclVendor.h"
+#include "ClangPersistentVariables.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Expression/ExpressionSourceCode.h"
+#include "lldb/Expression/IRExecutionUnit.h"
+#include "lldb/Expression/IRInterpreter.h"
+#include "lldb/Expression/Materializer.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/ThreadPlanCallUserExpression.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
+
+#include "llvm/ADT/ScopeExit.h"
+
+using namespace lldb_private;
+
+ClangUserExpression::ClangUserExpression(
+ ExecutionContextScope &exe_scope, llvm::StringRef expr,
+ llvm::StringRef prefix, lldb::LanguageType language,
+ ResultType desired_type, const EvaluateExpressionOptions &options,
+ ValueObject *ctx_obj)
+ : LLVMUserExpression(exe_scope, expr, prefix, language, desired_type,
+ options, eKindClangUserExpression),
+ m_type_system_helper(*m_target_wp.lock(), options.GetExecutionPolicy() ==
+ eExecutionPolicyTopLevel),
+ m_result_delegate(exe_scope.CalculateTarget()), m_ctx_obj(ctx_obj) {
+ switch (m_language) {
+ case lldb::eLanguageTypeC_plus_plus:
+ m_allow_cxx = true;
+ break;
+ case lldb::eLanguageTypeObjC:
+ m_allow_objc = true;
+ break;
+ case lldb::eLanguageTypeObjC_plus_plus:
+ default:
+ m_allow_cxx = true;
+ m_allow_objc = true;
+ break;
+ }
+}
+
+ClangUserExpression::~ClangUserExpression() {}
+
+void ClangUserExpression::ScanContext(ExecutionContext &exe_ctx, Status &err) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log)
+ log->Printf("ClangUserExpression::ScanContext()");
+
+ m_target = exe_ctx.GetTargetPtr();
+
+ if (!(m_allow_cxx || m_allow_objc)) {
+ if (log)
+ log->Printf(" [CUE::SC] Settings inhibit C++ and Objective-C");
+ return;
+ }
+
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ if (frame == nullptr) {
+ if (log)
+ log->Printf(" [CUE::SC] Null stack frame");
+ return;
+ }
+
+ SymbolContext sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction |
+ lldb::eSymbolContextBlock);
+
+ if (!sym_ctx.function) {
+ if (log)
+ log->Printf(" [CUE::SC] Null function");
+ return;
+ }
+
+ // Find the block that defines the function represented by "sym_ctx"
+ Block *function_block = sym_ctx.GetFunctionBlock();
+
+ if (!function_block) {
+ if (log)
+ log->Printf(" [CUE::SC] Null function block");
+ return;
+ }
+
+ CompilerDeclContext decl_context = function_block->GetDeclContext();
+
+ if (!decl_context) {
+ if (log)
+ log->Printf(" [CUE::SC] Null decl context");
+ return;
+ }
+
+ if (m_ctx_obj) {
+ switch (m_ctx_obj->GetObjectRuntimeLanguage()) {
+ case lldb::eLanguageTypeC:
+ case lldb::eLanguageTypeC89:
+ case lldb::eLanguageTypeC99:
+ case lldb::eLanguageTypeC11:
+ case lldb::eLanguageTypeC_plus_plus:
+ case lldb::eLanguageTypeC_plus_plus_03:
+ case lldb::eLanguageTypeC_plus_plus_11:
+ case lldb::eLanguageTypeC_plus_plus_14:
+ m_in_cplusplus_method = true;
+ break;
+ case lldb::eLanguageTypeObjC:
+ case lldb::eLanguageTypeObjC_plus_plus:
+ m_in_objectivec_method = true;
+ break;
+ default:
+ break;
+ }
+ m_needs_object_ptr = true;
+ } else if (clang::CXXMethodDecl *method_decl =
+ ClangASTContext::DeclContextGetAsCXXMethodDecl(decl_context)) {
+ if (m_allow_cxx && method_decl->isInstance()) {
+ if (m_enforce_valid_object) {
+ lldb::VariableListSP variable_list_sp(
+ function_block->GetBlockVariableList(true));
+
+ const char *thisErrorString = "Stopped in a C++ method, but 'this' "
+ "isn't available; pretending we are in a "
+ "generic context";
+
+ if (!variable_list_sp) {
+ err.SetErrorString(thisErrorString);
+ return;
+ }
+
+ lldb::VariableSP this_var_sp(
+ variable_list_sp->FindVariable(ConstString("this")));
+
+ if (!this_var_sp || !this_var_sp->IsInScope(frame) ||
+ !this_var_sp->LocationIsValidForFrame(frame)) {
+ err.SetErrorString(thisErrorString);
+ return;
+ }
+ }
+
+ m_in_cplusplus_method = true;
+ m_needs_object_ptr = true;
+ }
+ } else if (clang::ObjCMethodDecl *method_decl =
+ ClangASTContext::DeclContextGetAsObjCMethodDecl(
+ decl_context)) {
+ if (m_allow_objc) {
+ if (m_enforce_valid_object) {
+ lldb::VariableListSP variable_list_sp(
+ function_block->GetBlockVariableList(true));
+
+ const char *selfErrorString = "Stopped in an Objective-C method, but "
+ "'self' isn't available; pretending we "
+ "are in a generic context";
+
+ if (!variable_list_sp) {
+ err.SetErrorString(selfErrorString);
+ return;
+ }
+
+ lldb::VariableSP self_variable_sp =
+ variable_list_sp->FindVariable(ConstString("self"));
+
+ if (!self_variable_sp || !self_variable_sp->IsInScope(frame) ||
+ !self_variable_sp->LocationIsValidForFrame(frame)) {
+ err.SetErrorString(selfErrorString);
+ return;
+ }
+ }
+
+ m_in_objectivec_method = true;
+ m_needs_object_ptr = true;
+
+ if (!method_decl->isInstanceMethod())
+ m_in_static_method = true;
+ }
+ } else if (clang::FunctionDecl *function_decl =
+ ClangASTContext::DeclContextGetAsFunctionDecl(decl_context)) {
+ // We might also have a function that said in the debug information that it
+ // captured an object pointer. The best way to deal with getting to the
+ // ivars at present is by pretending that this is a method of a class in
+ // whatever runtime the debug info says the object pointer belongs to. Do
+ // that here.
+
+ ClangASTMetadata *metadata =
+ ClangASTContext::DeclContextGetMetaData(decl_context, function_decl);
+ if (metadata && metadata->HasObjectPtr()) {
+ lldb::LanguageType language = metadata->GetObjectPtrLanguage();
+ if (language == lldb::eLanguageTypeC_plus_plus) {
+ if (m_enforce_valid_object) {
+ lldb::VariableListSP variable_list_sp(
+ function_block->GetBlockVariableList(true));
+
+ const char *thisErrorString = "Stopped in a context claiming to "
+ "capture a C++ object pointer, but "
+ "'this' isn't available; pretending we "
+ "are in a generic context";
+
+ if (!variable_list_sp) {
+ err.SetErrorString(thisErrorString);
+ return;
+ }
+
+ lldb::VariableSP this_var_sp(
+ variable_list_sp->FindVariable(ConstString("this")));
+
+ if (!this_var_sp || !this_var_sp->IsInScope(frame) ||
+ !this_var_sp->LocationIsValidForFrame(frame)) {
+ err.SetErrorString(thisErrorString);
+ return;
+ }
+ }
+
+ m_in_cplusplus_method = true;
+ m_needs_object_ptr = true;
+ } else if (language == lldb::eLanguageTypeObjC) {
+ if (m_enforce_valid_object) {
+ lldb::VariableListSP variable_list_sp(
+ function_block->GetBlockVariableList(true));
+
+ const char *selfErrorString =
+ "Stopped in a context claiming to capture an Objective-C object "
+ "pointer, but 'self' isn't available; pretending we are in a "
+ "generic context";
+
+ if (!variable_list_sp) {
+ err.SetErrorString(selfErrorString);
+ return;
+ }
+
+ lldb::VariableSP self_variable_sp =
+ variable_list_sp->FindVariable(ConstString("self"));
+
+ if (!self_variable_sp || !self_variable_sp->IsInScope(frame) ||
+ !self_variable_sp->LocationIsValidForFrame(frame)) {
+ err.SetErrorString(selfErrorString);
+ return;
+ }
+
+ Type *self_type = self_variable_sp->GetType();
+
+ if (!self_type) {
+ err.SetErrorString(selfErrorString);
+ return;
+ }
+
+ CompilerType self_clang_type = self_type->GetForwardCompilerType();
+
+ if (!self_clang_type) {
+ err.SetErrorString(selfErrorString);
+ return;
+ }
+
+ if (ClangASTContext::IsObjCClassType(self_clang_type)) {
+ return;
+ } else if (ClangASTContext::IsObjCObjectPointerType(
+ self_clang_type)) {
+ m_in_objectivec_method = true;
+ m_needs_object_ptr = true;
+ } else {
+ err.SetErrorString(selfErrorString);
+ return;
+ }
+ } else {
+ m_in_objectivec_method = true;
+ m_needs_object_ptr = true;
+ }
+ }
+ }
+ }
+}
+
+// This is a really nasty hack, meant to fix Objective-C expressions of the
+// form (int)[myArray count]. Right now, because the type information for
+// count is not available, [myArray count] returns id, which can't be directly
+// cast to int without causing a clang error.
+static void ApplyObjcCastHack(std::string &expr) {
+#define OBJC_CAST_HACK_FROM "(int)["
+#define OBJC_CAST_HACK_TO "(int)(long long)["
+
+ size_t from_offset;
+
+ while ((from_offset = expr.find(OBJC_CAST_HACK_FROM)) != expr.npos)
+ expr.replace(from_offset, sizeof(OBJC_CAST_HACK_FROM) - 1,
+ OBJC_CAST_HACK_TO);
+
+#undef OBJC_CAST_HACK_TO
+#undef OBJC_CAST_HACK_FROM
+}
+
+bool ClangUserExpression::SetupPersistentState(DiagnosticManager &diagnostic_manager,
+ ExecutionContext &exe_ctx) {
+ if (Target *target = exe_ctx.GetTargetPtr()) {
+ if (PersistentExpressionState *persistent_state =
+ target->GetPersistentExpressionStateForLanguage(
+ lldb::eLanguageTypeC)) {
+ m_result_delegate.RegisterPersistentState(persistent_state);
+ } else {
+ diagnostic_manager.PutString(
+ eDiagnosticSeverityError,
+ "couldn't start parsing (no persistent data)");
+ return false;
+ }
+ } else {
+ diagnostic_manager.PutString(eDiagnosticSeverityError,
+ "error: couldn't start parsing (no target)");
+ return false;
+ }
+ return true;
+}
+
+static void SetupDeclVendor(ExecutionContext &exe_ctx, Target *target) {
+ if (ClangModulesDeclVendor *decl_vendor =
+ target->GetClangModulesDeclVendor()) {
+ const ClangModulesDeclVendor::ModuleVector &hand_imported_modules =
+ llvm::cast<ClangPersistentVariables>(
+ target->GetPersistentExpressionStateForLanguage(
+ lldb::eLanguageTypeC))
+ ->GetHandLoadedClangModules();
+ ClangModulesDeclVendor::ModuleVector modules_for_macros;
+
+ for (ClangModulesDeclVendor::ModuleID module : hand_imported_modules) {
+ modules_for_macros.push_back(module);
+ }
+
+ if (target->GetEnableAutoImportClangModules()) {
+ if (StackFrame *frame = exe_ctx.GetFramePtr()) {
+ if (Block *block = frame->GetFrameBlock()) {
+ SymbolContext sc;
+
+ block->CalculateSymbolContext(&sc);
+
+ if (sc.comp_unit) {
+ StreamString error_stream;
+
+ decl_vendor->AddModulesForCompileUnit(
+ *sc.comp_unit, modules_for_macros, error_stream);
+ }
+ }
+ }
+ }
+ }
+}
+
+void ClangUserExpression::UpdateLanguageForExpr(
+ DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx,
+ std::vector<std::string> modules_to_import, bool for_completion) {
+ m_expr_lang = lldb::LanguageType::eLanguageTypeUnknown;
+
+ std::string prefix = m_expr_prefix;
+
+ if (m_options.GetExecutionPolicy() == eExecutionPolicyTopLevel) {
+ m_transformed_text = m_expr_text;
+ } else {
+ std::unique_ptr<ClangExpressionSourceCode> source_code(
+ ClangExpressionSourceCode::CreateWrapped(prefix.c_str(),
+ m_expr_text.c_str()));
+
+ if (m_in_cplusplus_method)
+ m_expr_lang = lldb::eLanguageTypeC_plus_plus;
+ else if (m_in_objectivec_method)
+ m_expr_lang = lldb::eLanguageTypeObjC;
+ else
+ m_expr_lang = lldb::eLanguageTypeC;
+
+ if (!source_code->GetText(m_transformed_text, m_expr_lang,
+ m_in_static_method, exe_ctx, !m_ctx_obj,
+ for_completion, modules_to_import)) {
+ diagnostic_manager.PutString(eDiagnosticSeverityError,
+ "couldn't construct expression body");
+ return;
+ }
+
+ // Find and store the start position of the original code inside the
+ // transformed code. We need this later for the code completion.
+ std::size_t original_start;
+ std::size_t original_end;
+ bool found_bounds = source_code->GetOriginalBodyBounds(
+ m_transformed_text, m_expr_lang, original_start, original_end);
+ if (found_bounds)
+ m_user_expression_start_pos = original_start;
+ }
+}
+
+static bool SupportsCxxModuleImport(lldb::LanguageType language) {
+ switch (language) {
+ case lldb::eLanguageTypeC_plus_plus:
+ case lldb::eLanguageTypeC_plus_plus_03:
+ case lldb::eLanguageTypeC_plus_plus_11:
+ case lldb::eLanguageTypeC_plus_plus_14:
+ case lldb::eLanguageTypeObjC_plus_plus:
+ return true;
+ default:
+ return false;
+ }
+}
+
+std::vector<std::string>
+ClangUserExpression::GetModulesToImport(ExecutionContext &exe_ctx) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (!SupportsCxxModuleImport(Language()))
+ return {};
+
+ Target *target = exe_ctx.GetTargetPtr();
+ if (!target || !target->GetEnableImportStdModule())
+ return {};
+
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ if (!frame)
+ return {};
+
+ Block *block = frame->GetFrameBlock();
+ if (!block)
+ return {};
+
+ SymbolContext sc;
+ block->CalculateSymbolContext(&sc);
+ if (!sc.comp_unit)
+ return {};
+
+ if (log) {
+ for (const SourceModule &m : sc.comp_unit->GetImportedModules()) {
+ LLDB_LOG(log, "Found module in compile unit: {0:$[.]} - include dir: {1}",
+ llvm::make_range(m.path.begin(), m.path.end()), m.search_path);
+ }
+ }
+
+ for (const SourceModule &m : sc.comp_unit->GetImportedModules())
+ m_include_directories.push_back(m.search_path);
+
+ // Check if we imported 'std' or any of its submodules.
+ // We currently don't support importing any other modules in the expression
+ // parser.
+ for (const SourceModule &m : sc.comp_unit->GetImportedModules())
+ if (!m.path.empty() && m.path.front() == "std")
+ return {"std"};
+
+ return {};
+}
+
+bool ClangUserExpression::PrepareForParsing(
+ DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx,
+ bool for_completion) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ InstallContext(exe_ctx);
+
+ if (!SetupPersistentState(diagnostic_manager, exe_ctx))
+ return false;
+
+ Status err;
+ ScanContext(exe_ctx, err);
+
+ if (!err.Success()) {
+ diagnostic_manager.PutString(eDiagnosticSeverityWarning, err.AsCString());
+ }
+
+ ////////////////////////////////////
+ // Generate the expression
+ //
+
+ ApplyObjcCastHack(m_expr_text);
+
+ SetupDeclVendor(exe_ctx, m_target);
+
+ std::vector<std::string> used_modules = GetModulesToImport(exe_ctx);
+ m_imported_cpp_modules = !used_modules.empty();
+
+ LLDB_LOG(log, "List of imported modules in expression: {0}",
+ llvm::make_range(used_modules.begin(), used_modules.end()));
+
+ UpdateLanguageForExpr(diagnostic_manager, exe_ctx, used_modules,
+ for_completion);
+ return true;
+}
+
+bool ClangUserExpression::Parse(DiagnosticManager &diagnostic_manager,
+ ExecutionContext &exe_ctx,
+ lldb_private::ExecutionPolicy execution_policy,
+ bool keep_result_in_memory,
+ bool generate_debug_info) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (!PrepareForParsing(diagnostic_manager, exe_ctx, /*for_completion*/ false))
+ return false;
+
+ if (log)
+ log->Printf("Parsing the following code:\n%s", m_transformed_text.c_str());
+
+ ////////////////////////////////////
+ // Set up the target and compiler
+ //
+
+ Target *target = exe_ctx.GetTargetPtr();
+
+ if (!target) {
+ diagnostic_manager.PutString(eDiagnosticSeverityError, "invalid target");
+ return false;
+ }
+
+ //////////////////////////
+ // Parse the expression
+ //
+
+ m_materializer_up.reset(new Materializer());
+
+ ResetDeclMap(exe_ctx, m_result_delegate, keep_result_in_memory);
+
+ auto on_exit = llvm::make_scope_exit([this]() { ResetDeclMap(); });
+
+ if (!DeclMap()->WillParse(exe_ctx, m_materializer_up.get())) {
+ diagnostic_manager.PutString(
+ eDiagnosticSeverityError,
+ "current process state is unsuitable for expression parsing");
+ return false;
+ }
+
+ if (m_options.GetExecutionPolicy() == eExecutionPolicyTopLevel) {
+ DeclMap()->SetLookupsEnabled(true);
+ }
+
+ Process *process = exe_ctx.GetProcessPtr();
+ ExecutionContextScope *exe_scope = process;
+
+ if (!exe_scope)
+ exe_scope = exe_ctx.GetTargetPtr();
+
+ // We use a shared pointer here so we can use the original parser - if it
+ // succeeds or the rewrite parser we might make if it fails. But the
+ // parser_sp will never be empty.
+
+ ClangExpressionParser parser(exe_scope, *this, generate_debug_info,
+ m_include_directories);
+
+ unsigned num_errors = parser.Parse(diagnostic_manager);
+
+ // Check here for FixItHints. If there are any try to apply the fixits and
+ // set the fixed text in m_fixed_text before returning an error.
+ if (num_errors) {
+ if (diagnostic_manager.HasFixIts()) {
+ if (parser.RewriteExpression(diagnostic_manager)) {
+ size_t fixed_start;
+ size_t fixed_end;
+ const std::string &fixed_expression =
+ diagnostic_manager.GetFixedExpression();
+ if (ClangExpressionSourceCode::GetOriginalBodyBounds(
+ fixed_expression, m_expr_lang, fixed_start, fixed_end))
+ m_fixed_text =
+ fixed_expression.substr(fixed_start, fixed_end - fixed_start);
+ }
+ }
+ return false;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ // Prepare the output of the parser for execution, evaluating it statically
+ // if possible
+ //
+
+ {
+ Status jit_error = parser.PrepareForExecution(
+ m_jit_start_addr, m_jit_end_addr, m_execution_unit_sp, exe_ctx,
+ m_can_interpret, execution_policy);
+
+ if (!jit_error.Success()) {
+ const char *error_cstr = jit_error.AsCString();
+ if (error_cstr && error_cstr[0])
+ diagnostic_manager.PutString(eDiagnosticSeverityError, error_cstr);
+ else
+ diagnostic_manager.PutString(eDiagnosticSeverityError,
+ "expression can't be interpreted or run");
+ return false;
+ }
+ }
+
+ if (exe_ctx.GetProcessPtr() && execution_policy == eExecutionPolicyTopLevel) {
+ Status static_init_error =
+ parser.RunStaticInitializers(m_execution_unit_sp, exe_ctx);
+
+ if (!static_init_error.Success()) {
+ const char *error_cstr = static_init_error.AsCString();
+ if (error_cstr && error_cstr[0])
+ diagnostic_manager.Printf(eDiagnosticSeverityError,
+ "couldn't run static initializers: %s\n",
+ error_cstr);
+ else
+ diagnostic_manager.PutString(eDiagnosticSeverityError,
+ "couldn't run static initializers\n");
+ return false;
+ }
+ }
+
+ if (m_execution_unit_sp) {
+ bool register_execution_unit = false;
+
+ if (m_options.GetExecutionPolicy() == eExecutionPolicyTopLevel) {
+ register_execution_unit = true;
+ }
+
+ // If there is more than one external function in the execution unit, it
+ // needs to keep living even if it's not top level, because the result
+ // could refer to that function.
+
+ if (m_execution_unit_sp->GetJittedFunctions().size() > 1) {
+ register_execution_unit = true;
+ }
+
+ if (register_execution_unit) {
+ llvm::cast<PersistentExpressionState>(
+ exe_ctx.GetTargetPtr()->GetPersistentExpressionStateForLanguage(
+ m_language))
+ ->RegisterExecutionUnit(m_execution_unit_sp);
+ }
+ }
+
+ if (generate_debug_info) {
+ lldb::ModuleSP jit_module_sp(m_execution_unit_sp->GetJITModule());
+
+ if (jit_module_sp) {
+ ConstString const_func_name(FunctionName());
+ FileSpec jit_file;
+ jit_file.GetFilename() = const_func_name;
+ jit_module_sp->SetFileSpecAndObjectName(jit_file, ConstString());
+ m_jit_module_wp = jit_module_sp;
+ target->GetImages().Append(jit_module_sp);
+ }
+ }
+
+ if (process && m_jit_start_addr != LLDB_INVALID_ADDRESS)
+ m_jit_process_wp = lldb::ProcessWP(process->shared_from_this());
+ return true;
+}
+
+/// Converts an absolute position inside a given code string into
+/// a column/line pair.
+///
+/// \param[in] abs_pos
+/// A absolute position in the code string that we want to convert
+/// to a column/line pair.
+///
+/// \param[in] code
+/// A multi-line string usually representing source code.
+///
+/// \param[out] line
+/// The line in the code that contains the given absolute position.
+/// The first line in the string is indexed as 1.
+///
+/// \param[out] column
+/// The column in the line that contains the absolute position.
+/// The first character in a line is indexed as 0.
+static void AbsPosToLineColumnPos(size_t abs_pos, llvm::StringRef code,
+ unsigned &line, unsigned &column) {
+ // Reset to code position to beginning of the file.
+ line = 0;
+ column = 0;
+
+ assert(abs_pos <= code.size() && "Absolute position outside code string?");
+
+ // We have to walk up to the position and count lines/columns.
+ for (std::size_t i = 0; i < abs_pos; ++i) {
+ // If we hit a line break, we go back to column 0 and enter a new line.
+ // We only handle \n because that's what we internally use to make new
+ // lines for our temporary code strings.
+ if (code[i] == '\n') {
+ ++line;
+ column = 0;
+ continue;
+ }
+ ++column;
+ }
+}
+
+bool ClangUserExpression::Complete(ExecutionContext &exe_ctx,
+ CompletionRequest &request,
+ unsigned complete_pos) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ // We don't want any visible feedback when completing an expression. Mostly
+ // because the results we get from an incomplete invocation are probably not
+ // correct.
+ DiagnosticManager diagnostic_manager;
+
+ if (!PrepareForParsing(diagnostic_manager, exe_ctx, /*for_completion*/ true))
+ return false;
+
+ if (log)
+ log->Printf("Parsing the following code:\n%s", m_transformed_text.c_str());
+
+ //////////////////////////
+ // Parse the expression
+ //
+
+ m_materializer_up.reset(new Materializer());
+
+ ResetDeclMap(exe_ctx, m_result_delegate, /*keep result in memory*/ true);
+
+ auto on_exit = llvm::make_scope_exit([this]() { ResetDeclMap(); });
+
+ if (!DeclMap()->WillParse(exe_ctx, m_materializer_up.get())) {
+ diagnostic_manager.PutString(
+ eDiagnosticSeverityError,
+ "current process state is unsuitable for expression parsing");
+
+ return false;
+ }
+
+ if (m_options.GetExecutionPolicy() == eExecutionPolicyTopLevel) {
+ DeclMap()->SetLookupsEnabled(true);
+ }
+
+ Process *process = exe_ctx.GetProcessPtr();
+ ExecutionContextScope *exe_scope = process;
+
+ if (!exe_scope)
+ exe_scope = exe_ctx.GetTargetPtr();
+
+ ClangExpressionParser parser(exe_scope, *this, false);
+
+ // We have to find the source code location where the user text is inside
+ // the transformed expression code. When creating the transformed text, we
+ // already stored the absolute position in the m_transformed_text string. The
+ // only thing left to do is to transform it into the line:column format that
+ // Clang expects.
+
+ // The line and column of the user expression inside the transformed source
+ // code.
+ unsigned user_expr_line, user_expr_column;
+ if (m_user_expression_start_pos.hasValue())
+ AbsPosToLineColumnPos(*m_user_expression_start_pos, m_transformed_text,
+ user_expr_line, user_expr_column);
+ else
+ return false;
+
+ // The actual column where we have to complete is the start column of the
+ // user expression + the offset inside the user code that we were given.
+ const unsigned completion_column = user_expr_column + complete_pos;
+ parser.Complete(request, user_expr_line, completion_column, complete_pos);
+
+ return true;
+}
+
+bool ClangUserExpression::AddArguments(ExecutionContext &exe_ctx,
+ std::vector<lldb::addr_t> &args,
+ lldb::addr_t struct_address,
+ DiagnosticManager &diagnostic_manager) {
+ lldb::addr_t object_ptr = LLDB_INVALID_ADDRESS;
+ lldb::addr_t cmd_ptr = LLDB_INVALID_ADDRESS;
+
+ if (m_needs_object_ptr) {
+ lldb::StackFrameSP frame_sp = exe_ctx.GetFrameSP();
+ if (!frame_sp)
+ return true;
+
+ ConstString object_name;
+
+ if (m_in_cplusplus_method) {
+ object_name.SetCString("this");
+ } else if (m_in_objectivec_method) {
+ object_name.SetCString("self");
+ } else {
+ diagnostic_manager.PutString(
+ eDiagnosticSeverityError,
+ "need object pointer but don't know the language");
+ return false;
+ }
+
+ Status object_ptr_error;
+
+ if (m_ctx_obj) {
+ AddressType address_type;
+ object_ptr = m_ctx_obj->GetAddressOf(false, &address_type);
+ if (object_ptr == LLDB_INVALID_ADDRESS ||
+ address_type != eAddressTypeLoad)
+ object_ptr_error.SetErrorString("Can't get context object's "
+ "debuggee address");
+ } else
+ object_ptr = GetObjectPointer(frame_sp, object_name, object_ptr_error);
+
+ if (!object_ptr_error.Success()) {
+ exe_ctx.GetTargetRef().GetDebugger().GetAsyncOutputStream()->Printf(
+ "warning: `%s' is not accessible (substituting 0)\n",
+ object_name.AsCString());
+ object_ptr = 0;
+ }
+
+ if (m_in_objectivec_method) {
+ ConstString cmd_name("_cmd");
+
+ cmd_ptr = GetObjectPointer(frame_sp, cmd_name, object_ptr_error);
+
+ if (!object_ptr_error.Success()) {
+ diagnostic_manager.Printf(
+ eDiagnosticSeverityWarning,
+ "couldn't get cmd pointer (substituting NULL): %s",
+ object_ptr_error.AsCString());
+ cmd_ptr = 0;
+ }
+ }
+
+ args.push_back(object_ptr);
+
+ if (m_in_objectivec_method)
+ args.push_back(cmd_ptr);
+
+ args.push_back(struct_address);
+ } else {
+ args.push_back(struct_address);
+ }
+ return true;
+}
+
+lldb::ExpressionVariableSP ClangUserExpression::GetResultAfterDematerialization(
+ ExecutionContextScope *exe_scope) {
+ return m_result_delegate.GetVariable();
+}
+
+void ClangUserExpression::ClangUserExpressionHelper::ResetDeclMap(
+ ExecutionContext &exe_ctx,
+ Materializer::PersistentVariableDelegate &delegate,
+ bool keep_result_in_memory,
+ ValueObject *ctx_obj) {
+ m_expr_decl_map_up.reset(
+ new ClangExpressionDeclMap(keep_result_in_memory, &delegate, exe_ctx,
+ ctx_obj));
+}
+
+clang::ASTConsumer *
+ClangUserExpression::ClangUserExpressionHelper::ASTTransformer(
+ clang::ASTConsumer *passthrough) {
+ m_result_synthesizer_up.reset(
+ new ASTResultSynthesizer(passthrough, m_top_level, m_target));
+
+ return m_result_synthesizer_up.get();
+}
+
+void ClangUserExpression::ClangUserExpressionHelper::CommitPersistentDecls() {
+ if (m_result_synthesizer_up) {
+ m_result_synthesizer_up->CommitPersistentDecls();
+ }
+}
+
+ConstString ClangUserExpression::ResultDelegate::GetName() {
+ auto prefix = m_persistent_state->GetPersistentVariablePrefix();
+ return m_persistent_state->GetNextPersistentVariableName(*m_target_sp,
+ prefix);
+}
+
+void ClangUserExpression::ResultDelegate::DidDematerialize(
+ lldb::ExpressionVariableSP &variable) {
+ m_variable = variable;
+}
+
+void ClangUserExpression::ResultDelegate::RegisterPersistentState(
+ PersistentExpressionState *persistent_state) {
+ m_persistent_state = persistent_state;
+}
+
+lldb::ExpressionVariableSP &ClangUserExpression::ResultDelegate::GetVariable() {
+ return m_variable;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h
new file mode 100644
index 000000000000..24c152bdb45d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h
@@ -0,0 +1,226 @@
+//===-- ClangUserExpression.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 liblldb_ClangUserExpression_h_
+#define liblldb_ClangUserExpression_h_
+
+#include <vector>
+
+#include "ASTResultSynthesizer.h"
+#include "ASTStructExtractor.h"
+#include "ClangExpressionDeclMap.h"
+#include "ClangExpressionHelper.h"
+#include "ClangExpressionVariable.h"
+#include "IRForTarget.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/ClangForward.h"
+#include "lldb/Expression/LLVMUserExpression.h"
+#include "lldb/Expression/Materializer.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+/// \class ClangUserExpression ClangUserExpression.h
+/// "lldb/Expression/ClangUserExpression.h" Encapsulates a single expression
+/// for use with Clang
+///
+/// LLDB uses expressions for various purposes, notably to call functions
+/// and as a backend for the expr command. ClangUserExpression encapsulates
+/// the objects needed to parse and interpret or JIT an expression. It uses
+/// the Clang parser to produce LLVM IR from the expression.
+class ClangUserExpression : public LLVMUserExpression {
+public:
+ /// LLVM-style RTTI support.
+ static bool classof(const Expression *E) {
+ return E->getKind() == eKindClangUserExpression;
+ }
+
+ enum { kDefaultTimeout = 500000u };
+
+ class ClangUserExpressionHelper : public ClangExpressionHelper {
+ public:
+ ClangUserExpressionHelper(Target &target, bool top_level)
+ : m_target(target), m_top_level(top_level) {}
+
+ ~ClangUserExpressionHelper() override = default;
+
+ /// Return the object that the parser should use when resolving external
+ /// values. May be NULL if everything should be self-contained.
+ ClangExpressionDeclMap *DeclMap() override {
+ return m_expr_decl_map_up.get();
+ }
+
+ void ResetDeclMap() { m_expr_decl_map_up.reset(); }
+
+ void ResetDeclMap(ExecutionContext &exe_ctx,
+ Materializer::PersistentVariableDelegate &result_delegate,
+ bool keep_result_in_memory,
+ ValueObject *ctx_obj);
+
+ /// Return the object that the parser should allow to access ASTs. May be
+ /// NULL if the ASTs do not need to be transformed.
+ ///
+ /// \param[in] passthrough
+ /// The ASTConsumer that the returned transformer should send
+ /// the ASTs to after transformation.
+ clang::ASTConsumer *
+ ASTTransformer(clang::ASTConsumer *passthrough) override;
+
+ void CommitPersistentDecls() override;
+
+ private:
+ Target &m_target;
+ std::unique_ptr<ClangExpressionDeclMap> m_expr_decl_map_up;
+ std::unique_ptr<ASTStructExtractor> m_struct_extractor_up; ///< The class
+ ///that generates
+ ///the argument
+ ///struct layout.
+ std::unique_ptr<ASTResultSynthesizer> m_result_synthesizer_up;
+ bool m_top_level;
+ };
+
+ /// Constructor
+ ///
+ /// \param[in] expr
+ /// The expression to parse.
+ ///
+ /// \param[in] expr_prefix
+ /// If non-NULL, a C string containing translation-unit level
+ /// definitions to be included when the expression is parsed.
+ ///
+ /// \param[in] language
+ /// If not eLanguageTypeUnknown, a language to use when parsing
+ /// the expression. Currently restricted to those languages
+ /// supported by Clang.
+ ///
+ /// \param[in] desired_type
+ /// If not eResultTypeAny, the type to use for the expression
+ /// result.
+ ///
+ /// \param[in] ctx_obj
+ /// The object (if any) in which context the expression
+ /// must be evaluated. For details see the comment to
+ /// `UserExpression::Evaluate`.
+ ClangUserExpression(ExecutionContextScope &exe_scope, llvm::StringRef expr,
+ llvm::StringRef prefix, lldb::LanguageType language,
+ ResultType desired_type,
+ const EvaluateExpressionOptions &options,
+ ValueObject *ctx_obj);
+
+ ~ClangUserExpression() override;
+
+ /// Parse the expression
+ ///
+ /// \param[in] diagnostic_manager
+ /// A diagnostic manager to report parse errors and warnings to.
+ ///
+ /// \param[in] exe_ctx
+ /// The execution context to use when looking up entities that
+ /// are needed for parsing (locations of functions, types of
+ /// variables, persistent variables, etc.)
+ ///
+ /// \param[in] execution_policy
+ /// Determines whether interpretation is possible or mandatory.
+ ///
+ /// \param[in] keep_result_in_memory
+ /// True if the resulting persistent variable should reside in
+ /// target memory, if applicable.
+ ///
+ /// \return
+ /// True on success (no errors); false otherwise.
+ bool Parse(DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx,
+ lldb_private::ExecutionPolicy execution_policy,
+ bool keep_result_in_memory, bool generate_debug_info) override;
+
+ bool Complete(ExecutionContext &exe_ctx, CompletionRequest &request,
+ unsigned complete_pos) override;
+
+ ExpressionTypeSystemHelper *GetTypeSystemHelper() override {
+ return &m_type_system_helper;
+ }
+
+ ClangExpressionDeclMap *DeclMap() { return m_type_system_helper.DeclMap(); }
+
+ void ResetDeclMap() { m_type_system_helper.ResetDeclMap(); }
+
+ void ResetDeclMap(ExecutionContext &exe_ctx,
+ Materializer::PersistentVariableDelegate &result_delegate,
+ bool keep_result_in_memory) {
+ m_type_system_helper.ResetDeclMap(exe_ctx, result_delegate,
+ keep_result_in_memory,
+ m_ctx_obj);
+ }
+
+ lldb::ExpressionVariableSP
+ GetResultAfterDematerialization(ExecutionContextScope *exe_scope) override;
+
+ bool DidImportCxxModules() const { return m_imported_cpp_modules; }
+
+private:
+ /// Populate m_in_cplusplus_method and m_in_objectivec_method based on the
+ /// environment.
+
+ void ScanContext(ExecutionContext &exe_ctx,
+ lldb_private::Status &err) override;
+
+ bool AddArguments(ExecutionContext &exe_ctx, std::vector<lldb::addr_t> &args,
+ lldb::addr_t struct_address,
+ DiagnosticManager &diagnostic_manager) override;
+
+ std::vector<std::string> GetModulesToImport(ExecutionContext &exe_ctx);
+ void UpdateLanguageForExpr(DiagnosticManager &diagnostic_manager,
+ ExecutionContext &exe_ctx,
+ std::vector<std::string> modules_to_import,
+ bool for_completion);
+ bool SetupPersistentState(DiagnosticManager &diagnostic_manager,
+ ExecutionContext &exe_ctx);
+ bool PrepareForParsing(DiagnosticManager &diagnostic_manager,
+ ExecutionContext &exe_ctx, bool for_completion);
+
+ ClangUserExpressionHelper m_type_system_helper;
+
+ class ResultDelegate : public Materializer::PersistentVariableDelegate {
+ public:
+ ResultDelegate(lldb::TargetSP target) : m_target_sp(target) {}
+ ConstString GetName() override;
+ void DidDematerialize(lldb::ExpressionVariableSP &variable) override;
+
+ void RegisterPersistentState(PersistentExpressionState *persistent_state);
+ lldb::ExpressionVariableSP &GetVariable();
+
+ private:
+ PersistentExpressionState *m_persistent_state;
+ lldb::ExpressionVariableSP m_variable;
+ lldb::TargetSP m_target_sp;
+ };
+
+ /// The language type of the current expression.
+ lldb::LanguageType m_expr_lang = lldb::eLanguageTypeUnknown;
+ /// The include directories that should be used when parsing the expression.
+ std::vector<ConstString> m_include_directories;
+
+ /// The absolute character position in the transformed source code where the
+ /// user code (as typed by the user) starts. If the variable is empty, then we
+ /// were not able to calculate this position.
+ llvm::Optional<size_t> m_user_expression_start_pos;
+ ResultDelegate m_result_delegate;
+
+ /// The object (if any) in which context the expression is evaluated.
+ /// See the comment to `UserExpression::Evaluate` for details.
+ ValueObject *m_ctx_obj;
+
+ /// True iff this expression explicitly imported C++ modules.
+ bool m_imported_cpp_modules = false;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ClangUserExpression_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp
new file mode 100644
index 000000000000..5eec224477fc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp
@@ -0,0 +1,161 @@
+//===-- ClangUtilityFunction.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 "ClangUtilityFunction.h"
+#include "ClangExpressionDeclMap.h"
+#include "ClangExpressionParser.h"
+#include "ClangExpressionSourceCode.h"
+
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Expression/IRExecutionUnit.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb_private;
+
+/// Constructor
+///
+/// \param[in] text
+/// The text of the function. Must be a full translation unit.
+///
+/// \param[in] name
+/// The name of the function, as used in the text.
+ClangUtilityFunction::ClangUtilityFunction(ExecutionContextScope &exe_scope,
+ const char *text, const char *name)
+ : UtilityFunction(exe_scope, text, name, eKindClangUtilityFunction) {
+ m_function_text.assign(ClangExpressionSourceCode::g_expression_prefix);
+ if (text && text[0])
+ m_function_text.append(text);
+}
+
+ClangUtilityFunction::~ClangUtilityFunction() {}
+
+/// Install the utility function into a process
+///
+/// \param[in] diagnostic_manager
+/// A diagnostic manager to report errors and warnings to.
+///
+/// \param[in] exe_ctx
+/// The execution context to install the utility function to.
+///
+/// \return
+/// True on success (no errors); false otherwise.
+bool ClangUtilityFunction::Install(DiagnosticManager &diagnostic_manager,
+ ExecutionContext &exe_ctx) {
+ if (m_jit_start_addr != LLDB_INVALID_ADDRESS) {
+ diagnostic_manager.PutString(eDiagnosticSeverityWarning,
+ "already installed");
+ return false;
+ }
+
+ ////////////////////////////////////
+ // Set up the target and compiler
+ //
+
+ Target *target = exe_ctx.GetTargetPtr();
+
+ if (!target) {
+ diagnostic_manager.PutString(eDiagnosticSeverityError, "invalid target");
+ return false;
+ }
+
+ Process *process = exe_ctx.GetProcessPtr();
+
+ if (!process) {
+ diagnostic_manager.PutString(eDiagnosticSeverityError, "invalid process");
+ return false;
+ }
+
+ //////////////////////////
+ // Parse the expression
+ //
+
+ bool keep_result_in_memory = false;
+
+ ResetDeclMap(exe_ctx, keep_result_in_memory);
+
+ if (!DeclMap()->WillParse(exe_ctx, nullptr)) {
+ diagnostic_manager.PutString(
+ eDiagnosticSeverityError,
+ "current process state is unsuitable for expression parsing");
+ return false;
+ }
+
+ const bool generate_debug_info = true;
+ ClangExpressionParser parser(exe_ctx.GetBestExecutionContextScope(), *this,
+ generate_debug_info);
+
+ unsigned num_errors = parser.Parse(diagnostic_manager);
+
+ if (num_errors) {
+ ResetDeclMap();
+
+ return false;
+ }
+
+ //////////////////////////////////
+ // JIT the output of the parser
+ //
+
+ bool can_interpret = false; // should stay that way
+
+ Status jit_error = parser.PrepareForExecution(
+ m_jit_start_addr, m_jit_end_addr, m_execution_unit_sp, exe_ctx,
+ can_interpret, eExecutionPolicyAlways);
+
+ if (m_jit_start_addr != LLDB_INVALID_ADDRESS) {
+ m_jit_process_wp = process->shared_from_this();
+ if (parser.GetGenerateDebugInfo()) {
+ lldb::ModuleSP jit_module_sp(m_execution_unit_sp->GetJITModule());
+
+ if (jit_module_sp) {
+ ConstString const_func_name(FunctionName());
+ FileSpec jit_file;
+ jit_file.GetFilename() = const_func_name;
+ jit_module_sp->SetFileSpecAndObjectName(jit_file, ConstString());
+ m_jit_module_wp = jit_module_sp;
+ target->GetImages().Append(jit_module_sp);
+ }
+ }
+ }
+
+ DeclMap()->DidParse();
+
+ ResetDeclMap();
+
+ if (jit_error.Success()) {
+ return true;
+ } else {
+ const char *error_cstr = jit_error.AsCString();
+ if (error_cstr && error_cstr[0]) {
+ diagnostic_manager.Printf(eDiagnosticSeverityError, "%s", error_cstr);
+ } else {
+ diagnostic_manager.PutString(eDiagnosticSeverityError,
+ "expression can't be interpreted or run");
+ }
+ return false;
+ }
+}
+
+void ClangUtilityFunction::ClangUtilityFunctionHelper::ResetDeclMap(
+ ExecutionContext &exe_ctx, bool keep_result_in_memory) {
+ m_expr_decl_map_up.reset(
+ new ClangExpressionDeclMap(keep_result_in_memory, nullptr, exe_ctx,
+ nullptr));
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.h
new file mode 100644
index 000000000000..70ebb2f3ad8a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.h
@@ -0,0 +1,107 @@
+//===-- ClangUtilityFunction.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 liblldb_ClangUtilityFunction_h_
+#define liblldb_ClangUtilityFunction_h_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "ClangExpressionHelper.h"
+
+#include "lldb/Core/ClangForward.h"
+#include "lldb/Expression/UtilityFunction.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+/// \class ClangUtilityFunction ClangUtilityFunction.h
+/// "lldb/Expression/ClangUtilityFunction.h" Encapsulates a single expression
+/// for use with Clang
+///
+/// LLDB uses expressions for various purposes, notably to call functions
+/// and as a backend for the expr command. ClangUtilityFunction encapsulates
+/// a self-contained function meant to be used from other code. Utility
+/// functions can perform error-checking for ClangUserExpressions, or can
+/// simply provide a way to push a function into the target for the debugger
+/// to call later on.
+class ClangUtilityFunction : public UtilityFunction {
+public:
+ /// LLVM-style RTTI support.
+ static bool classof(const Expression *E) {
+ return E->getKind() == eKindClangUtilityFunction;
+ }
+
+ class ClangUtilityFunctionHelper : public ClangExpressionHelper {
+ public:
+ ClangUtilityFunctionHelper() {}
+
+ ~ClangUtilityFunctionHelper() override {}
+
+ /// Return the object that the parser should use when resolving external
+ /// values. May be NULL if everything should be self-contained.
+ ClangExpressionDeclMap *DeclMap() override {
+ return m_expr_decl_map_up.get();
+ }
+
+ void ResetDeclMap() { m_expr_decl_map_up.reset(); }
+
+ void ResetDeclMap(ExecutionContext &exe_ctx, bool keep_result_in_memory);
+
+ /// Return the object that the parser should allow to access ASTs. May be
+ /// NULL if the ASTs do not need to be transformed.
+ ///
+ /// \param[in] passthrough
+ /// The ASTConsumer that the returned transformer should send
+ /// the ASTs to after transformation.
+ clang::ASTConsumer *
+ ASTTransformer(clang::ASTConsumer *passthrough) override {
+ return nullptr;
+ }
+
+ private:
+ std::unique_ptr<ClangExpressionDeclMap> m_expr_decl_map_up;
+ };
+ /// Constructor
+ ///
+ /// \param[in] text
+ /// The text of the function. Must be a full translation unit.
+ ///
+ /// \param[in] name
+ /// The name of the function, as used in the text.
+ ClangUtilityFunction(ExecutionContextScope &exe_scope, const char *text,
+ const char *name);
+
+ ~ClangUtilityFunction() override;
+
+ ExpressionTypeSystemHelper *GetTypeSystemHelper() override {
+ return &m_type_system_helper;
+ }
+
+ ClangExpressionDeclMap *DeclMap() { return m_type_system_helper.DeclMap(); }
+
+ void ResetDeclMap() { m_type_system_helper.ResetDeclMap(); }
+
+ void ResetDeclMap(ExecutionContext &exe_ctx, bool keep_result_in_memory) {
+ m_type_system_helper.ResetDeclMap(exe_ctx, keep_result_in_memory);
+ }
+
+ bool Install(DiagnosticManager &diagnostic_manager,
+ ExecutionContext &exe_ctx) override;
+
+private:
+ ClangUtilityFunctionHelper m_type_system_helper; ///< The map to use when
+ ///parsing and materializing
+ ///the expression.
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ClangUtilityFunction_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.cpp
new file mode 100644
index 000000000000..f8e004fe7d4a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.cpp
@@ -0,0 +1,595 @@
+//===-- IRDynamicChecks.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/IR/Constants.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Value.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include "IRDynamicChecks.h"
+
+#include "lldb/Expression/UtilityFunction.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+using namespace llvm;
+using namespace lldb_private;
+
+static char ID;
+
+#define VALID_POINTER_CHECK_NAME "_$__lldb_valid_pointer_check"
+#define VALID_OBJC_OBJECT_CHECK_NAME "$__lldb_objc_object_check"
+
+static const char g_valid_pointer_check_text[] =
+ "extern \"C\" void\n"
+ "_$__lldb_valid_pointer_check (unsigned char *$__lldb_arg_ptr)\n"
+ "{\n"
+ " unsigned char $__lldb_local_val = *$__lldb_arg_ptr;\n"
+ "}";
+
+ClangDynamicCheckerFunctions::ClangDynamicCheckerFunctions()
+ : DynamicCheckerFunctions(DCF_Clang) {}
+
+ClangDynamicCheckerFunctions::~ClangDynamicCheckerFunctions() = default;
+
+bool ClangDynamicCheckerFunctions::Install(
+ DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx) {
+ Status error;
+ m_valid_pointer_check.reset(
+ exe_ctx.GetTargetRef().GetUtilityFunctionForLanguage(
+ g_valid_pointer_check_text, lldb::eLanguageTypeC,
+ VALID_POINTER_CHECK_NAME, error));
+ if (error.Fail())
+ return false;
+
+ if (!m_valid_pointer_check->Install(diagnostic_manager, exe_ctx))
+ return false;
+
+ Process *process = exe_ctx.GetProcessPtr();
+
+ if (process) {
+ ObjCLanguageRuntime *objc_language_runtime =
+ ObjCLanguageRuntime::Get(*process);
+
+ if (objc_language_runtime) {
+ m_objc_object_check.reset(objc_language_runtime->CreateObjectChecker(
+ VALID_OBJC_OBJECT_CHECK_NAME));
+
+ if (!m_objc_object_check->Install(diagnostic_manager, exe_ctx))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ClangDynamicCheckerFunctions::DoCheckersExplainStop(lldb::addr_t addr,
+ Stream &message) {
+ // FIXME: We have to get the checkers to know why they scotched the call in
+ // more detail,
+ // so we can print a better message here.
+ if (m_valid_pointer_check && m_valid_pointer_check->ContainsAddress(addr)) {
+ message.Printf("Attempted to dereference an invalid pointer.");
+ return true;
+ } else if (m_objc_object_check &&
+ m_objc_object_check->ContainsAddress(addr)) {
+ message.Printf("Attempted to dereference an invalid ObjC Object or send it "
+ "an unrecognized selector");
+ return true;
+ }
+ return false;
+}
+
+static std::string PrintValue(llvm::Value *V, bool truncate = false) {
+ std::string s;
+ raw_string_ostream rso(s);
+ V->print(rso);
+ rso.flush();
+ if (truncate)
+ s.resize(s.length() - 1);
+ return s;
+}
+
+/// \class Instrumenter IRDynamicChecks.cpp
+/// Finds and instruments individual LLVM IR instructions
+///
+/// When instrumenting LLVM IR, it is frequently desirable to first search for
+/// instructions, and then later modify them. This way iterators remain
+/// intact, and multiple passes can look at the same code base without
+/// treading on each other's toes.
+///
+/// The Instrumenter class implements this functionality. A client first
+/// calls Inspect on a function, which populates a list of instructions to be
+/// instrumented. Then, later, when all passes' Inspect functions have been
+/// called, the client calls Instrument, which adds the desired
+/// instrumentation.
+///
+/// A subclass of Instrumenter must override InstrumentInstruction, which
+/// is responsible for adding whatever instrumentation is necessary.
+///
+/// A subclass of Instrumenter may override:
+///
+/// - InspectInstruction [default: does nothing]
+///
+/// - InspectBasicBlock [default: iterates through the instructions in a
+/// basic block calling InspectInstruction]
+///
+/// - InspectFunction [default: iterates through the basic blocks in a
+/// function calling InspectBasicBlock]
+class Instrumenter {
+public:
+ /// Constructor
+ ///
+ /// \param[in] module
+ /// The module being instrumented.
+ Instrumenter(llvm::Module &module,
+ std::shared_ptr<UtilityFunction> checker_function)
+ : m_module(module), m_checker_function(checker_function),
+ m_i8ptr_ty(nullptr), m_intptr_ty(nullptr) {}
+
+ virtual ~Instrumenter() = default;
+
+ /// Inspect a function to find instructions to instrument
+ ///
+ /// \param[in] function
+ /// The function to inspect.
+ ///
+ /// \return
+ /// True on success; false on error.
+ bool Inspect(llvm::Function &function) { return InspectFunction(function); }
+
+ /// Instrument all the instructions found by Inspect()
+ ///
+ /// \return
+ /// True on success; false on error.
+ bool Instrument() {
+ for (InstIterator ii = m_to_instrument.begin(),
+ last_ii = m_to_instrument.end();
+ ii != last_ii; ++ii) {
+ if (!InstrumentInstruction(*ii))
+ return false;
+ }
+
+ return true;
+ }
+
+protected:
+ /// Add instrumentation to a single instruction
+ ///
+ /// \param[in] inst
+ /// The instruction to be instrumented.
+ ///
+ /// \return
+ /// True on success; false otherwise.
+ virtual bool InstrumentInstruction(llvm::Instruction *inst) = 0;
+
+ /// Register a single instruction to be instrumented
+ ///
+ /// \param[in] inst
+ /// The instruction to be instrumented.
+ void RegisterInstruction(llvm::Instruction &i) {
+ m_to_instrument.push_back(&i);
+ }
+
+ /// Determine whether a single instruction is interesting to instrument,
+ /// and, if so, call RegisterInstruction
+ ///
+ /// \param[in] i
+ /// The instruction to be inspected.
+ ///
+ /// \return
+ /// False if there was an error scanning; true otherwise.
+ virtual bool InspectInstruction(llvm::Instruction &i) { return true; }
+
+ /// Scan a basic block to see if any instructions are interesting
+ ///
+ /// \param[in] bb
+ /// The basic block to be inspected.
+ ///
+ /// \return
+ /// False if there was an error scanning; true otherwise.
+ virtual bool InspectBasicBlock(llvm::BasicBlock &bb) {
+ for (llvm::BasicBlock::iterator ii = bb.begin(), last_ii = bb.end();
+ ii != last_ii; ++ii) {
+ if (!InspectInstruction(*ii))
+ return false;
+ }
+
+ return true;
+ }
+
+ /// Scan a function to see if any instructions are interesting
+ ///
+ /// \param[in] f
+ /// The function to be inspected.
+ ///
+ /// \return
+ /// False if there was an error scanning; true otherwise.
+ virtual bool InspectFunction(llvm::Function &f) {
+ for (llvm::Function::iterator bbi = f.begin(), last_bbi = f.end();
+ bbi != last_bbi; ++bbi) {
+ if (!InspectBasicBlock(*bbi))
+ return false;
+ }
+
+ return true;
+ }
+
+ /// Build a function pointer for a function with signature void
+ /// (*)(uint8_t*) with a given address
+ ///
+ /// \param[in] start_address
+ /// The address of the function.
+ ///
+ /// \return
+ /// The function pointer, for use in a CallInst.
+ llvm::FunctionCallee BuildPointerValidatorFunc(lldb::addr_t start_address) {
+ llvm::Type *param_array[1];
+
+ param_array[0] = const_cast<llvm::PointerType *>(GetI8PtrTy());
+
+ ArrayRef<llvm::Type *> params(param_array, 1);
+
+ FunctionType *fun_ty = FunctionType::get(
+ llvm::Type::getVoidTy(m_module.getContext()), params, true);
+ PointerType *fun_ptr_ty = PointerType::getUnqual(fun_ty);
+ Constant *fun_addr_int =
+ ConstantInt::get(GetIntptrTy(), start_address, false);
+ return {fun_ty, ConstantExpr::getIntToPtr(fun_addr_int, fun_ptr_ty)};
+ }
+
+ /// Build a function pointer for a function with signature void
+ /// (*)(uint8_t*, uint8_t*) with a given address
+ ///
+ /// \param[in] start_address
+ /// The address of the function.
+ ///
+ /// \return
+ /// The function pointer, for use in a CallInst.
+ llvm::FunctionCallee BuildObjectCheckerFunc(lldb::addr_t start_address) {
+ llvm::Type *param_array[2];
+
+ param_array[0] = const_cast<llvm::PointerType *>(GetI8PtrTy());
+ param_array[1] = const_cast<llvm::PointerType *>(GetI8PtrTy());
+
+ ArrayRef<llvm::Type *> params(param_array, 2);
+
+ FunctionType *fun_ty = FunctionType::get(
+ llvm::Type::getVoidTy(m_module.getContext()), params, true);
+ PointerType *fun_ptr_ty = PointerType::getUnqual(fun_ty);
+ Constant *fun_addr_int =
+ ConstantInt::get(GetIntptrTy(), start_address, false);
+ return {fun_ty, ConstantExpr::getIntToPtr(fun_addr_int, fun_ptr_ty)};
+ }
+
+ PointerType *GetI8PtrTy() {
+ if (!m_i8ptr_ty)
+ m_i8ptr_ty = llvm::Type::getInt8PtrTy(m_module.getContext());
+
+ return m_i8ptr_ty;
+ }
+
+ IntegerType *GetIntptrTy() {
+ if (!m_intptr_ty) {
+ llvm::DataLayout data_layout(&m_module);
+
+ m_intptr_ty = llvm::Type::getIntNTy(m_module.getContext(),
+ data_layout.getPointerSizeInBits());
+ }
+
+ return m_intptr_ty;
+ }
+
+ typedef std::vector<llvm::Instruction *> InstVector;
+ typedef InstVector::iterator InstIterator;
+
+ InstVector m_to_instrument; ///< List of instructions the inspector found
+ llvm::Module &m_module; ///< The module which is being instrumented
+ std::shared_ptr<UtilityFunction>
+ m_checker_function; ///< The dynamic checker function for the process
+
+private:
+ PointerType *m_i8ptr_ty;
+ IntegerType *m_intptr_ty;
+};
+
+class ValidPointerChecker : public Instrumenter {
+public:
+ ValidPointerChecker(llvm::Module &module,
+ std::shared_ptr<UtilityFunction> checker_function)
+ : Instrumenter(module, checker_function),
+ m_valid_pointer_check_func(nullptr) {}
+
+ ~ValidPointerChecker() override = default;
+
+protected:
+ bool InstrumentInstruction(llvm::Instruction *inst) override {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log)
+ log->Printf("Instrumenting load/store instruction: %s\n",
+ PrintValue(inst).c_str());
+
+ if (!m_valid_pointer_check_func)
+ m_valid_pointer_check_func =
+ BuildPointerValidatorFunc(m_checker_function->StartAddress());
+
+ llvm::Value *dereferenced_ptr = nullptr;
+
+ if (llvm::LoadInst *li = dyn_cast<llvm::LoadInst>(inst))
+ dereferenced_ptr = li->getPointerOperand();
+ else if (llvm::StoreInst *si = dyn_cast<llvm::StoreInst>(inst))
+ dereferenced_ptr = si->getPointerOperand();
+ else
+ return false;
+
+ // Insert an instruction to cast the loaded value to int8_t*
+
+ BitCastInst *bit_cast =
+ new BitCastInst(dereferenced_ptr, GetI8PtrTy(), "", inst);
+
+ // Insert an instruction to call the helper with the result
+
+ llvm::Value *arg_array[1];
+
+ arg_array[0] = bit_cast;
+
+ llvm::ArrayRef<llvm::Value *> args(arg_array, 1);
+
+ CallInst::Create(m_valid_pointer_check_func, args, "", inst);
+
+ return true;
+ }
+
+ bool InspectInstruction(llvm::Instruction &i) override {
+ if (dyn_cast<llvm::LoadInst>(&i) || dyn_cast<llvm::StoreInst>(&i))
+ RegisterInstruction(i);
+
+ return true;
+ }
+
+private:
+ llvm::FunctionCallee m_valid_pointer_check_func;
+};
+
+class ObjcObjectChecker : public Instrumenter {
+public:
+ ObjcObjectChecker(llvm::Module &module,
+ std::shared_ptr<UtilityFunction> checker_function)
+ : Instrumenter(module, checker_function),
+ m_objc_object_check_func(nullptr) {}
+
+ ~ObjcObjectChecker() override = default;
+
+ enum msgSend_type {
+ eMsgSend = 0,
+ eMsgSendSuper,
+ eMsgSendSuper_stret,
+ eMsgSend_fpret,
+ eMsgSend_stret
+ };
+
+ std::map<llvm::Instruction *, msgSend_type> msgSend_types;
+
+protected:
+ bool InstrumentInstruction(llvm::Instruction *inst) override {
+ CallInst *call_inst = dyn_cast<CallInst>(inst);
+
+ if (!call_inst)
+ return false; // call_inst really shouldn't be nullptr, because otherwise
+ // InspectInstruction wouldn't have registered it
+
+ if (!m_objc_object_check_func)
+ m_objc_object_check_func =
+ BuildObjectCheckerFunc(m_checker_function->StartAddress());
+
+ // id objc_msgSend(id theReceiver, SEL theSelector, ...)
+
+ llvm::Value *target_object;
+ llvm::Value *selector;
+
+ switch (msgSend_types[inst]) {
+ case eMsgSend:
+ case eMsgSend_fpret:
+ // On arm64, clang uses objc_msgSend for scalar and struct return
+ // calls. The call instruction will record which was used.
+ if (call_inst->hasStructRetAttr()) {
+ target_object = call_inst->getArgOperand(1);
+ selector = call_inst->getArgOperand(2);
+ } else {
+ target_object = call_inst->getArgOperand(0);
+ selector = call_inst->getArgOperand(1);
+ }
+ break;
+ case eMsgSend_stret:
+ target_object = call_inst->getArgOperand(1);
+ selector = call_inst->getArgOperand(2);
+ break;
+ case eMsgSendSuper:
+ case eMsgSendSuper_stret:
+ return true;
+ }
+
+ // These objects should always be valid according to Sean Calannan
+ assert(target_object);
+ assert(selector);
+
+ // Insert an instruction to cast the receiver id to int8_t*
+
+ BitCastInst *bit_cast =
+ new BitCastInst(target_object, GetI8PtrTy(), "", inst);
+
+ // Insert an instruction to call the helper with the result
+
+ llvm::Value *arg_array[2];
+
+ arg_array[0] = bit_cast;
+ arg_array[1] = selector;
+
+ ArrayRef<llvm::Value *> args(arg_array, 2);
+
+ CallInst::Create(m_objc_object_check_func, args, "", inst);
+
+ return true;
+ }
+
+ static llvm::Function *GetFunction(llvm::Value *value) {
+ if (llvm::Function *function = llvm::dyn_cast<llvm::Function>(value)) {
+ return function;
+ }
+
+ if (llvm::ConstantExpr *const_expr =
+ llvm::dyn_cast<llvm::ConstantExpr>(value)) {
+ switch (const_expr->getOpcode()) {
+ default:
+ return nullptr;
+ case llvm::Instruction::BitCast:
+ return GetFunction(const_expr->getOperand(0));
+ }
+ }
+
+ return nullptr;
+ }
+
+ static llvm::Function *GetCalledFunction(llvm::CallInst *inst) {
+ return GetFunction(inst->getCalledValue());
+ }
+
+ bool InspectInstruction(llvm::Instruction &i) override {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ CallInst *call_inst = dyn_cast<CallInst>(&i);
+
+ if (call_inst) {
+ const llvm::Function *called_function = GetCalledFunction(call_inst);
+
+ if (!called_function)
+ return true;
+
+ std::string name_str = called_function->getName().str();
+ const char *name_cstr = name_str.c_str();
+
+ if (log)
+ log->Printf("Found call to %s: %s\n", name_cstr,
+ PrintValue(call_inst).c_str());
+
+ if (name_str.find("objc_msgSend") == std::string::npos)
+ return true;
+
+ if (!strcmp(name_cstr, "objc_msgSend")) {
+ RegisterInstruction(i);
+ msgSend_types[&i] = eMsgSend;
+ return true;
+ }
+
+ if (!strcmp(name_cstr, "objc_msgSend_stret")) {
+ RegisterInstruction(i);
+ msgSend_types[&i] = eMsgSend_stret;
+ return true;
+ }
+
+ if (!strcmp(name_cstr, "objc_msgSend_fpret")) {
+ RegisterInstruction(i);
+ msgSend_types[&i] = eMsgSend_fpret;
+ return true;
+ }
+
+ if (!strcmp(name_cstr, "objc_msgSendSuper")) {
+ RegisterInstruction(i);
+ msgSend_types[&i] = eMsgSendSuper;
+ return true;
+ }
+
+ if (!strcmp(name_cstr, "objc_msgSendSuper_stret")) {
+ RegisterInstruction(i);
+ msgSend_types[&i] = eMsgSendSuper_stret;
+ return true;
+ }
+
+ if (log)
+ log->Printf(
+ "Function name '%s' contains 'objc_msgSend' but is not handled",
+ name_str.c_str());
+
+ return true;
+ }
+
+ return true;
+ }
+
+private:
+ llvm::FunctionCallee m_objc_object_check_func;
+};
+
+IRDynamicChecks::IRDynamicChecks(
+ ClangDynamicCheckerFunctions &checker_functions, const char *func_name)
+ : ModulePass(ID), m_func_name(func_name),
+ m_checker_functions(checker_functions) {}
+
+IRDynamicChecks::~IRDynamicChecks() = default;
+
+bool IRDynamicChecks::runOnModule(llvm::Module &M) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ llvm::Function *function = M.getFunction(StringRef(m_func_name));
+
+ if (!function) {
+ if (log)
+ log->Printf("Couldn't find %s() in the module", m_func_name.c_str());
+
+ return false;
+ }
+
+ if (m_checker_functions.m_valid_pointer_check) {
+ ValidPointerChecker vpc(M, m_checker_functions.m_valid_pointer_check);
+
+ if (!vpc.Inspect(*function))
+ return false;
+
+ if (!vpc.Instrument())
+ return false;
+ }
+
+ if (m_checker_functions.m_objc_object_check) {
+ ObjcObjectChecker ooc(M, m_checker_functions.m_objc_object_check);
+
+ if (!ooc.Inspect(*function))
+ return false;
+
+ if (!ooc.Instrument())
+ return false;
+ }
+
+ if (log && log->GetVerbose()) {
+ std::string s;
+ raw_string_ostream oss(s);
+
+ M.print(oss, nullptr);
+
+ oss.flush();
+
+ log->Printf("Module after dynamic checks: \n%s", s.c_str());
+ }
+
+ return true;
+}
+
+void IRDynamicChecks::assignPassManager(PMStack &PMS, PassManagerType T) {}
+
+PassManagerType IRDynamicChecks::getPotentialPassManagerType() const {
+ return PMT_ModulePassManager;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.h
new file mode 100644
index 000000000000..60c0691b21c1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.h
@@ -0,0 +1,131 @@
+//===-- IRDynamicChecks.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 liblldb_IRDynamicChecks_h_
+#define liblldb_IRDynamicChecks_h_
+
+#include "lldb/Expression/DynamicCheckerFunctions.h"
+#include "lldb/lldb-types.h"
+#include "llvm/Pass.h"
+
+namespace llvm {
+class BasicBlock;
+class Module;
+}
+
+namespace lldb_private {
+
+class ExecutionContext;
+class Stream;
+
+class ClangDynamicCheckerFunctions
+ : public lldb_private::DynamicCheckerFunctions {
+public:
+ /// Constructor
+ ClangDynamicCheckerFunctions();
+
+ /// Destructor
+ virtual ~ClangDynamicCheckerFunctions();
+
+ static bool classof(const DynamicCheckerFunctions *checker_funcs) {
+ return checker_funcs->GetKind() == DCF_Clang;
+ }
+
+ /// Install the utility functions into a process. This binds the instance
+ /// of DynamicCheckerFunctions to that process.
+ ///
+ /// \param[in] diagnostic_manager
+ /// A diagnostic manager to report errors to.
+ ///
+ /// \param[in] exe_ctx
+ /// The execution context to install the functions into.
+ ///
+ /// \return
+ /// True on success; false on failure, or if the functions have
+ /// already been installed.
+ bool Install(DiagnosticManager &diagnostic_manager,
+ ExecutionContext &exe_ctx) override;
+
+ bool DoCheckersExplainStop(lldb::addr_t addr, Stream &message) override;
+
+ std::shared_ptr<UtilityFunction> m_valid_pointer_check;
+ std::shared_ptr<UtilityFunction> m_objc_object_check;
+};
+
+/// \class IRDynamicChecks IRDynamicChecks.h
+/// "lldb/Expression/IRDynamicChecks.h" Adds dynamic checks to a user-entered
+/// expression to reduce its likelihood of crashing
+///
+/// When an IR function is executed in the target process, it may cause
+/// crashes or hangs by dereferencing NULL pointers, trying to call
+/// Objective-C methods on objects that do not respond to them, and so forth.
+///
+/// IRDynamicChecks adds calls to the functions in DynamicCheckerFunctions to
+/// appropriate locations in an expression's IR.
+class IRDynamicChecks : public llvm::ModulePass {
+public:
+ /// Constructor
+ ///
+ /// \param[in] checker_functions
+ /// The checker functions for the target process.
+ ///
+ /// \param[in] func_name
+ /// The name of the function to prepare for execution in the target.
+ ///
+ /// \param[in] decl_map
+ /// The mapping used to look up entities in the target process. In
+ /// this case, used to find objc_msgSend
+ IRDynamicChecks(ClangDynamicCheckerFunctions &checker_functions,
+ const char *func_name = "$__lldb_expr");
+
+ /// Destructor
+ ~IRDynamicChecks() override;
+
+ /// Run this IR transformer on a single module
+ ///
+ /// \param[in] M
+ /// The module to run on. This module is searched for the function
+ /// $__lldb_expr, and that function is passed to the passes one by
+ /// one.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool runOnModule(llvm::Module &M) override;
+
+ /// Interface stub
+ void assignPassManager(
+ llvm::PMStack &PMS,
+ llvm::PassManagerType T = llvm::PMT_ModulePassManager) override;
+
+ /// Returns PMT_ModulePassManager
+ llvm::PassManagerType getPotentialPassManagerType() const override;
+
+private:
+ /// A basic block-level pass to find all pointer dereferences and
+ /// validate them before use.
+
+ /// The top-level pass implementation
+ ///
+ /// \param[in] M
+ /// The module currently being processed.
+ ///
+ /// \param[in] BB
+ /// The basic block currently being processed.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool FindDataLoads(llvm::Module &M, llvm::BasicBlock &BB);
+
+ std::string m_func_name; ///< The name of the function to add checks to
+ ClangDynamicCheckerFunctions
+ &m_checker_functions; ///< The checker functions for the process
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_IRDynamicChecks_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp
new file mode 100644
index 000000000000..07acb2e1030f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp
@@ -0,0 +1,2270 @@
+//===-- IRForTarget.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 "IRForTarget.h"
+
+#include "ClangExpressionDeclMap.h"
+
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/InstrTypes.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/ValueSymbolTable.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Transforms/IPO.h"
+
+#include "clang/AST/ASTContext.h"
+
+#include "lldb/Core/dwarf.h"
+#include "lldb/Expression/IRExecutionUnit.h"
+#include "lldb/Expression/IRInterpreter.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangUtil.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/StreamString.h"
+
+#include <map>
+
+using namespace llvm;
+
+static char ID;
+
+IRForTarget::FunctionValueCache::FunctionValueCache(Maker const &maker)
+ : m_maker(maker), m_values() {}
+
+IRForTarget::FunctionValueCache::~FunctionValueCache() {}
+
+llvm::Value *
+IRForTarget::FunctionValueCache::GetValue(llvm::Function *function) {
+ if (!m_values.count(function)) {
+ llvm::Value *ret = m_maker(function);
+ m_values[function] = ret;
+ return ret;
+ }
+ return m_values[function];
+}
+
+static llvm::Value *FindEntryInstruction(llvm::Function *function) {
+ if (function->empty())
+ return nullptr;
+
+ return function->getEntryBlock().getFirstNonPHIOrDbg();
+}
+
+IRForTarget::IRForTarget(lldb_private::ClangExpressionDeclMap *decl_map,
+ bool resolve_vars,
+ lldb_private::IRExecutionUnit &execution_unit,
+ lldb_private::Stream &error_stream,
+ const char *func_name)
+ : ModulePass(ID), m_resolve_vars(resolve_vars), m_func_name(func_name),
+ m_module(nullptr), m_decl_map(decl_map),
+ m_CFStringCreateWithBytes(nullptr), m_sel_registerName(nullptr),
+ m_objc_getClass(nullptr), m_intptr_ty(nullptr),
+ m_error_stream(error_stream), m_execution_unit(execution_unit),
+ m_result_store(nullptr), m_result_is_pointer(false),
+ m_reloc_placeholder(nullptr),
+ m_entry_instruction_finder(FindEntryInstruction) {}
+
+/* Handy utility functions used at several places in the code */
+
+static std::string PrintValue(const Value *value, bool truncate = false) {
+ std::string s;
+ if (value) {
+ raw_string_ostream rso(s);
+ value->print(rso);
+ rso.flush();
+ if (truncate)
+ s.resize(s.length() - 1);
+ }
+ return s;
+}
+
+static std::string PrintType(const llvm::Type *type, bool truncate = false) {
+ std::string s;
+ raw_string_ostream rso(s);
+ type->print(rso);
+ rso.flush();
+ if (truncate)
+ s.resize(s.length() - 1);
+ return s;
+}
+
+IRForTarget::~IRForTarget() {}
+
+bool IRForTarget::FixFunctionLinkage(llvm::Function &llvm_function) {
+ llvm_function.setLinkage(GlobalValue::ExternalLinkage);
+
+ return true;
+}
+
+clang::NamedDecl *IRForTarget::DeclForGlobal(const GlobalValue *global_val,
+ Module *module) {
+ NamedMDNode *named_metadata =
+ module->getNamedMetadata("clang.global.decl.ptrs");
+
+ if (!named_metadata)
+ return nullptr;
+
+ unsigned num_nodes = named_metadata->getNumOperands();
+ unsigned node_index;
+
+ for (node_index = 0; node_index < num_nodes; ++node_index) {
+ llvm::MDNode *metadata_node =
+ dyn_cast<llvm::MDNode>(named_metadata->getOperand(node_index));
+ if (!metadata_node)
+ return nullptr;
+
+ if (metadata_node->getNumOperands() != 2)
+ continue;
+
+ if (mdconst::dyn_extract_or_null<GlobalValue>(
+ metadata_node->getOperand(0)) != global_val)
+ continue;
+
+ ConstantInt *constant_int =
+ mdconst::dyn_extract<ConstantInt>(metadata_node->getOperand(1));
+
+ if (!constant_int)
+ return nullptr;
+
+ uintptr_t ptr = constant_int->getZExtValue();
+
+ return reinterpret_cast<clang::NamedDecl *>(ptr);
+ }
+
+ return nullptr;
+}
+
+clang::NamedDecl *IRForTarget::DeclForGlobal(GlobalValue *global_val) {
+ return DeclForGlobal(global_val, m_module);
+}
+
+bool IRForTarget::CreateResultVariable(llvm::Function &llvm_function) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (!m_resolve_vars)
+ return true;
+
+ // Find the result variable. If it doesn't exist, we can give up right here.
+
+ ValueSymbolTable &value_symbol_table = m_module->getValueSymbolTable();
+
+ std::string result_name_str;
+ const char *result_name = nullptr;
+
+ for (ValueSymbolTable::iterator vi = value_symbol_table.begin(),
+ ve = value_symbol_table.end();
+ vi != ve; ++vi) {
+ result_name_str = vi->first().str();
+ const char *value_name = result_name_str.c_str();
+
+ if (strstr(value_name, "$__lldb_expr_result_ptr") &&
+ strncmp(value_name, "_ZGV", 4)) {
+ result_name = value_name;
+ m_result_is_pointer = true;
+ break;
+ }
+
+ if (strstr(value_name, "$__lldb_expr_result") &&
+ strncmp(value_name, "_ZGV", 4)) {
+ result_name = value_name;
+ m_result_is_pointer = false;
+ break;
+ }
+ }
+
+ if (!result_name) {
+ if (log)
+ log->PutCString("Couldn't find result variable");
+
+ return true;
+ }
+
+ if (log)
+ log->Printf("Result name: \"%s\"", result_name);
+
+ Value *result_value = m_module->getNamedValue(result_name);
+
+ if (!result_value) {
+ if (log)
+ log->PutCString("Result variable had no data");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: Result variable's "
+ "name (%s) exists, but not its definition\n",
+ result_name);
+
+ return false;
+ }
+
+ if (log)
+ log->Printf("Found result in the IR: \"%s\"",
+ PrintValue(result_value, false).c_str());
+
+ GlobalVariable *result_global = dyn_cast<GlobalVariable>(result_value);
+
+ if (!result_global) {
+ if (log)
+ log->PutCString("Result variable isn't a GlobalVariable");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: Result variable (%s) "
+ "is defined, but is not a global variable\n",
+ result_name);
+
+ return false;
+ }
+
+ clang::NamedDecl *result_decl = DeclForGlobal(result_global);
+ if (!result_decl) {
+ if (log)
+ log->PutCString("Result variable doesn't have a corresponding Decl");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: Result variable (%s) "
+ "does not have a corresponding Clang entity\n",
+ result_name);
+
+ return false;
+ }
+
+ if (log) {
+ std::string decl_desc_str;
+ raw_string_ostream decl_desc_stream(decl_desc_str);
+ result_decl->print(decl_desc_stream);
+ decl_desc_stream.flush();
+
+ log->Printf("Found result decl: \"%s\"", decl_desc_str.c_str());
+ }
+
+ clang::VarDecl *result_var = dyn_cast<clang::VarDecl>(result_decl);
+ if (!result_var) {
+ if (log)
+ log->PutCString("Result variable Decl isn't a VarDecl");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: Result variable "
+ "(%s)'s corresponding Clang entity isn't a "
+ "variable\n",
+ result_name);
+
+ return false;
+ }
+
+ // Get the next available result name from m_decl_map and create the
+ // persistent variable for it
+
+ // If the result is an Lvalue, it is emitted as a pointer; see
+ // ASTResultSynthesizer::SynthesizeBodyResult.
+ if (m_result_is_pointer) {
+ clang::QualType pointer_qual_type = result_var->getType();
+ const clang::Type *pointer_type = pointer_qual_type.getTypePtr();
+
+ const clang::PointerType *pointer_pointertype =
+ pointer_type->getAs<clang::PointerType>();
+ const clang::ObjCObjectPointerType *pointer_objcobjpointertype =
+ pointer_type->getAs<clang::ObjCObjectPointerType>();
+
+ if (pointer_pointertype) {
+ clang::QualType element_qual_type = pointer_pointertype->getPointeeType();
+
+ m_result_type = lldb_private::TypeFromParser(
+ element_qual_type.getAsOpaquePtr(),
+ lldb_private::ClangASTContext::GetASTContext(
+ &result_decl->getASTContext()));
+ } else if (pointer_objcobjpointertype) {
+ clang::QualType element_qual_type =
+ clang::QualType(pointer_objcobjpointertype->getObjectType(), 0);
+
+ m_result_type = lldb_private::TypeFromParser(
+ element_qual_type.getAsOpaquePtr(),
+ lldb_private::ClangASTContext::GetASTContext(
+ &result_decl->getASTContext()));
+ } else {
+ if (log)
+ log->PutCString("Expected result to have pointer type, but it did not");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: Lvalue result (%s) "
+ "is not a pointer variable\n",
+ result_name);
+
+ return false;
+ }
+ } else {
+ m_result_type = lldb_private::TypeFromParser(
+ result_var->getType().getAsOpaquePtr(),
+ lldb_private::ClangASTContext::GetASTContext(
+ &result_decl->getASTContext()));
+ }
+
+ lldb::TargetSP target_sp(m_execution_unit.GetTarget());
+ lldb_private::ExecutionContext exe_ctx(target_sp, true);
+ llvm::Optional<uint64_t> bit_size =
+ m_result_type.GetBitSize(exe_ctx.GetBestExecutionContextScope());
+ if (!bit_size) {
+ lldb_private::StreamString type_desc_stream;
+ m_result_type.DumpTypeDescription(&type_desc_stream);
+
+ if (log)
+ log->Printf("Result type has unknown size");
+
+ m_error_stream.Printf("Error [IRForTarget]: Size of result type '%s' "
+ "couldn't be determined\n",
+ type_desc_stream.GetData());
+ return false;
+ }
+
+ if (log) {
+ lldb_private::StreamString type_desc_stream;
+ m_result_type.DumpTypeDescription(&type_desc_stream);
+
+ log->Printf("Result decl type: \"%s\"", type_desc_stream.GetData());
+ }
+
+ m_result_name = lldb_private::ConstString("$RESULT_NAME");
+
+ if (log)
+ log->Printf("Creating a new result global: \"%s\" with size 0x%" PRIx64,
+ m_result_name.GetCString(),
+ m_result_type.GetByteSize(nullptr).getValueOr(0));
+
+ // Construct a new result global and set up its metadata
+
+ GlobalVariable *new_result_global = new GlobalVariable(
+ (*m_module), result_global->getType()->getElementType(),
+ false, /* not constant */
+ GlobalValue::ExternalLinkage, nullptr, /* no initializer */
+ m_result_name.GetCString());
+
+ // It's too late in compilation to create a new VarDecl for this, but we
+ // don't need to. We point the metadata at the old VarDecl. This creates an
+ // odd anomaly: a variable with a Value whose name is something like $0 and a
+ // Decl whose name is $__lldb_expr_result. This condition is handled in
+ // ClangExpressionDeclMap::DoMaterialize, and the name of the variable is
+ // fixed up.
+
+ ConstantInt *new_constant_int =
+ ConstantInt::get(llvm::Type::getInt64Ty(m_module->getContext()),
+ reinterpret_cast<uint64_t>(result_decl), false);
+
+ llvm::Metadata *values[2];
+ values[0] = ConstantAsMetadata::get(new_result_global);
+ values[1] = ConstantAsMetadata::get(new_constant_int);
+
+ ArrayRef<Metadata *> value_ref(values, 2);
+
+ MDNode *persistent_global_md = MDNode::get(m_module->getContext(), value_ref);
+ NamedMDNode *named_metadata =
+ m_module->getNamedMetadata("clang.global.decl.ptrs");
+ named_metadata->addOperand(persistent_global_md);
+
+ if (log)
+ log->Printf("Replacing \"%s\" with \"%s\"",
+ PrintValue(result_global).c_str(),
+ PrintValue(new_result_global).c_str());
+
+ if (result_global->use_empty()) {
+ // We need to synthesize a store for this variable, because otherwise
+ // there's nothing to put into its equivalent persistent variable.
+
+ BasicBlock &entry_block(llvm_function.getEntryBlock());
+ Instruction *first_entry_instruction(entry_block.getFirstNonPHIOrDbg());
+
+ if (!first_entry_instruction)
+ return false;
+
+ if (!result_global->hasInitializer()) {
+ if (log)
+ log->Printf("Couldn't find initializer for unused variable");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: Result variable "
+ "(%s) has no writes and no initializer\n",
+ result_name);
+
+ return false;
+ }
+
+ Constant *initializer = result_global->getInitializer();
+
+ StoreInst *synthesized_store =
+ new StoreInst(initializer, new_result_global, first_entry_instruction);
+
+ if (log)
+ log->Printf("Synthesized result store \"%s\"\n",
+ PrintValue(synthesized_store).c_str());
+ } else {
+ result_global->replaceAllUsesWith(new_result_global);
+ }
+
+ if (!m_decl_map->AddPersistentVariable(
+ result_decl, m_result_name, m_result_type, true, m_result_is_pointer))
+ return false;
+
+ result_global->eraseFromParent();
+
+ return true;
+}
+
+bool IRForTarget::RewriteObjCConstString(llvm::GlobalVariable *ns_str,
+ llvm::GlobalVariable *cstr) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ Type *ns_str_ty = ns_str->getType();
+
+ Type *i8_ptr_ty = Type::getInt8PtrTy(m_module->getContext());
+ Type *i32_ty = Type::getInt32Ty(m_module->getContext());
+ Type *i8_ty = Type::getInt8Ty(m_module->getContext());
+
+ if (!m_CFStringCreateWithBytes) {
+ lldb::addr_t CFStringCreateWithBytes_addr;
+
+ static lldb_private::ConstString g_CFStringCreateWithBytes_str(
+ "CFStringCreateWithBytes");
+
+ bool missing_weak = false;
+ CFStringCreateWithBytes_addr =
+ m_execution_unit.FindSymbol(g_CFStringCreateWithBytes_str,
+ missing_weak);
+ if (CFStringCreateWithBytes_addr == LLDB_INVALID_ADDRESS || missing_weak) {
+ if (log)
+ log->PutCString("Couldn't find CFStringCreateWithBytes in the target");
+
+ m_error_stream.Printf("Error [IRForTarget]: Rewriting an Objective-C "
+ "constant string requires "
+ "CFStringCreateWithBytes\n");
+
+ return false;
+ }
+
+ if (log)
+ log->Printf("Found CFStringCreateWithBytes at 0x%" PRIx64,
+ CFStringCreateWithBytes_addr);
+
+ // Build the function type:
+ //
+ // CFStringRef CFStringCreateWithBytes (
+ // CFAllocatorRef alloc,
+ // const UInt8 *bytes,
+ // CFIndex numBytes,
+ // CFStringEncoding encoding,
+ // Boolean isExternalRepresentation
+ // );
+ //
+ // We make the following substitutions:
+ //
+ // CFStringRef -> i8*
+ // CFAllocatorRef -> i8*
+ // UInt8 * -> i8*
+ // CFIndex -> long (i32 or i64, as appropriate; we ask the module for its
+ // pointer size for now) CFStringEncoding -> i32 Boolean -> i8
+
+ Type *arg_type_array[5];
+
+ arg_type_array[0] = i8_ptr_ty;
+ arg_type_array[1] = i8_ptr_ty;
+ arg_type_array[2] = m_intptr_ty;
+ arg_type_array[3] = i32_ty;
+ arg_type_array[4] = i8_ty;
+
+ ArrayRef<Type *> CFSCWB_arg_types(arg_type_array, 5);
+
+ llvm::FunctionType *CFSCWB_ty =
+ FunctionType::get(ns_str_ty, CFSCWB_arg_types, false);
+
+ // Build the constant containing the pointer to the function
+ PointerType *CFSCWB_ptr_ty = PointerType::getUnqual(CFSCWB_ty);
+ Constant *CFSCWB_addr_int =
+ ConstantInt::get(m_intptr_ty, CFStringCreateWithBytes_addr, false);
+ m_CFStringCreateWithBytes = {
+ CFSCWB_ty, ConstantExpr::getIntToPtr(CFSCWB_addr_int, CFSCWB_ptr_ty)};
+ }
+
+ ConstantDataSequential *string_array = nullptr;
+
+ if (cstr)
+ string_array = dyn_cast<ConstantDataSequential>(cstr->getInitializer());
+
+ Constant *alloc_arg = Constant::getNullValue(i8_ptr_ty);
+ Constant *bytes_arg = cstr ? ConstantExpr::getBitCast(cstr, i8_ptr_ty)
+ : Constant::getNullValue(i8_ptr_ty);
+ Constant *numBytes_arg = ConstantInt::get(
+ m_intptr_ty, cstr ? (string_array->getNumElements() - 1) * string_array->getElementByteSize() : 0, false);
+ int encoding_flags = 0;
+ switch (cstr ? string_array->getElementByteSize() : 1) {
+ case 1:
+ encoding_flags = 0x08000100; /* 0x08000100 is kCFStringEncodingUTF8 */
+ break;
+ case 2:
+ encoding_flags = 0x0100; /* 0x0100 is kCFStringEncodingUTF16 */
+ break;
+ case 4:
+ encoding_flags = 0x0c000100; /* 0x0c000100 is kCFStringEncodingUTF32 */
+ break;
+ default:
+ encoding_flags = 0x0600; /* fall back to 0x0600, kCFStringEncodingASCII */
+ LLDB_LOG(log, "Encountered an Objective-C constant string with unusual "
+ "element size {0}",
+ string_array->getElementByteSize());
+ }
+ Constant *encoding_arg = ConstantInt::get(i32_ty, encoding_flags, false);
+ Constant *isExternal_arg =
+ ConstantInt::get(i8_ty, 0x0, false); /* 0x0 is false */
+
+ Value *argument_array[5];
+
+ argument_array[0] = alloc_arg;
+ argument_array[1] = bytes_arg;
+ argument_array[2] = numBytes_arg;
+ argument_array[3] = encoding_arg;
+ argument_array[4] = isExternal_arg;
+
+ ArrayRef<Value *> CFSCWB_arguments(argument_array, 5);
+
+ FunctionValueCache CFSCWB_Caller(
+ [this, &CFSCWB_arguments](llvm::Function *function) -> llvm::Value * {
+ return CallInst::Create(
+ m_CFStringCreateWithBytes, CFSCWB_arguments,
+ "CFStringCreateWithBytes",
+ llvm::cast<Instruction>(
+ m_entry_instruction_finder.GetValue(function)));
+ });
+
+ if (!UnfoldConstant(ns_str, nullptr, CFSCWB_Caller, m_entry_instruction_finder,
+ m_error_stream)) {
+ if (log)
+ log->PutCString(
+ "Couldn't replace the NSString with the result of the call");
+
+ m_error_stream.Printf("error [IRForTarget internal]: Couldn't replace an "
+ "Objective-C constant string with a dynamic "
+ "string\n");
+
+ return false;
+ }
+
+ ns_str->eraseFromParent();
+
+ return true;
+}
+
+bool IRForTarget::RewriteObjCConstStrings() {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ ValueSymbolTable &value_symbol_table = m_module->getValueSymbolTable();
+
+ for (ValueSymbolTable::iterator vi = value_symbol_table.begin(),
+ ve = value_symbol_table.end();
+ vi != ve; ++vi) {
+ std::string value_name = vi->first().str();
+ const char *value_name_cstr = value_name.c_str();
+
+ if (strstr(value_name_cstr, "_unnamed_cfstring_")) {
+ Value *nsstring_value = vi->second;
+
+ GlobalVariable *nsstring_global =
+ dyn_cast<GlobalVariable>(nsstring_value);
+
+ if (!nsstring_global) {
+ if (log)
+ log->PutCString("NSString variable is not a GlobalVariable");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C "
+ "constant string is not a global variable\n");
+
+ return false;
+ }
+
+ if (!nsstring_global->hasInitializer()) {
+ if (log)
+ log->PutCString("NSString variable does not have an initializer");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C "
+ "constant string does not have an initializer\n");
+
+ return false;
+ }
+
+ ConstantStruct *nsstring_struct =
+ dyn_cast<ConstantStruct>(nsstring_global->getInitializer());
+
+ if (!nsstring_struct) {
+ if (log)
+ log->PutCString(
+ "NSString variable's initializer is not a ConstantStruct");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C "
+ "constant string is not a structure constant\n");
+
+ return false;
+ }
+
+ // We expect the following structure:
+ //
+ // struct {
+ // int *isa;
+ // int flags;
+ // char *str;
+ // long length;
+ // };
+
+ if (nsstring_struct->getNumOperands() != 4) {
+ if (log)
+ log->Printf("NSString variable's initializer structure has an "
+ "unexpected number of members. Should be 4, is %d",
+ nsstring_struct->getNumOperands());
+
+ m_error_stream.Printf("Internal error [IRForTarget]: The struct for an "
+ "Objective-C constant string is not as "
+ "expected\n");
+
+ return false;
+ }
+
+ Constant *nsstring_member = nsstring_struct->getOperand(2);
+
+ if (!nsstring_member) {
+ if (log)
+ log->PutCString("NSString initializer's str element was empty");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C "
+ "constant string does not have a string "
+ "initializer\n");
+
+ return false;
+ }
+
+ ConstantExpr *nsstring_expr = dyn_cast<ConstantExpr>(nsstring_member);
+
+ if (!nsstring_expr) {
+ if (log)
+ log->PutCString(
+ "NSString initializer's str element is not a ConstantExpr");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C "
+ "constant string's string initializer is not "
+ "constant\n");
+
+ return false;
+ }
+
+ GlobalVariable *cstr_global = nullptr;
+
+ if (nsstring_expr->getOpcode() == Instruction::GetElementPtr) {
+ Constant *nsstring_cstr = nsstring_expr->getOperand(0);
+ cstr_global = dyn_cast<GlobalVariable>(nsstring_cstr);
+ } else if (nsstring_expr->getOpcode() == Instruction::BitCast) {
+ Constant *nsstring_cstr = nsstring_expr->getOperand(0);
+ cstr_global = dyn_cast<GlobalVariable>(nsstring_cstr);
+ }
+
+ if (!cstr_global) {
+ if (log)
+ log->PutCString(
+ "NSString initializer's str element is not a GlobalVariable");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: Unhandled"
+ "constant string initializer\n");
+
+ return false;
+ }
+
+ if (!cstr_global->hasInitializer()) {
+ if (log)
+ log->PutCString("NSString initializer's str element does not have an "
+ "initializer");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C "
+ "constant string's string initializer doesn't "
+ "point to initialized data\n");
+
+ return false;
+ }
+
+ /*
+ if (!cstr_array)
+ {
+ if (log)
+ log->PutCString("NSString initializer's str element is not a
+ ConstantArray");
+
+ if (m_error_stream)
+ m_error_stream.Printf("Internal error [IRForTarget]: An
+ Objective-C constant string's string initializer doesn't point to an
+ array\n");
+
+ return false;
+ }
+
+ if (!cstr_array->isCString())
+ {
+ if (log)
+ log->PutCString("NSString initializer's str element is not a C
+ string array");
+
+ if (m_error_stream)
+ m_error_stream.Printf("Internal error [IRForTarget]: An
+ Objective-C constant string's string initializer doesn't point to a C
+ string\n");
+
+ return false;
+ }
+ */
+
+ ConstantDataArray *cstr_array =
+ dyn_cast<ConstantDataArray>(cstr_global->getInitializer());
+
+ if (log) {
+ if (cstr_array)
+ log->Printf("Found NSString constant %s, which contains \"%s\"",
+ value_name_cstr, cstr_array->getAsString().str().c_str());
+ else
+ log->Printf("Found NSString constant %s, which contains \"\"",
+ value_name_cstr);
+ }
+
+ if (!cstr_array)
+ cstr_global = nullptr;
+
+ if (!RewriteObjCConstString(nsstring_global, cstr_global)) {
+ if (log)
+ log->PutCString("Error rewriting the constant string");
+
+ // We don't print an error message here because RewriteObjCConstString
+ // has done so for us.
+
+ return false;
+ }
+ }
+ }
+
+ for (ValueSymbolTable::iterator vi = value_symbol_table.begin(),
+ ve = value_symbol_table.end();
+ vi != ve; ++vi) {
+ std::string value_name = vi->first().str();
+ const char *value_name_cstr = value_name.c_str();
+
+ if (!strcmp(value_name_cstr, "__CFConstantStringClassReference")) {
+ GlobalVariable *gv = dyn_cast<GlobalVariable>(vi->second);
+
+ if (!gv) {
+ if (log)
+ log->PutCString(
+ "__CFConstantStringClassReference is not a global variable");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: Found a "
+ "CFConstantStringClassReference, but it is not a "
+ "global object\n");
+
+ return false;
+ }
+
+ gv->eraseFromParent();
+
+ break;
+ }
+ }
+
+ return true;
+}
+
+static bool IsObjCSelectorRef(Value *value) {
+ GlobalVariable *global_variable = dyn_cast<GlobalVariable>(value);
+
+ return !(!global_variable || !global_variable->hasName() ||
+ !global_variable->getName().startswith("OBJC_SELECTOR_REFERENCES_"));
+}
+
+// This function does not report errors; its callers are responsible.
+bool IRForTarget::RewriteObjCSelector(Instruction *selector_load) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ LoadInst *load = dyn_cast<LoadInst>(selector_load);
+
+ if (!load)
+ return false;
+
+ // Unpack the message name from the selector. In LLVM IR, an objc_msgSend
+ // gets represented as
+ //
+ // %tmp = load i8** @"OBJC_SELECTOR_REFERENCES_" ; <i8*> %call = call
+ // i8* (i8*, i8*, ...)* @objc_msgSend(i8* %obj, i8* %tmp, ...) ; <i8*>
+ //
+ // where %obj is the object pointer and %tmp is the selector.
+ //
+ // @"OBJC_SELECTOR_REFERENCES_" is a pointer to a character array called
+ // @"\01L_OBJC_llvm_moduleETH_VAR_NAllvm_moduleE_".
+ // @"\01L_OBJC_llvm_moduleETH_VAR_NAllvm_moduleE_" contains the string.
+
+ // Find the pointer's initializer (a ConstantExpr with opcode GetElementPtr)
+ // and get the string from its target
+
+ GlobalVariable *_objc_selector_references_ =
+ dyn_cast<GlobalVariable>(load->getPointerOperand());
+
+ if (!_objc_selector_references_ ||
+ !_objc_selector_references_->hasInitializer())
+ return false;
+
+ Constant *osr_initializer = _objc_selector_references_->getInitializer();
+
+ ConstantExpr *osr_initializer_expr = dyn_cast<ConstantExpr>(osr_initializer);
+
+ if (!osr_initializer_expr ||
+ osr_initializer_expr->getOpcode() != Instruction::GetElementPtr)
+ return false;
+
+ Value *osr_initializer_base = osr_initializer_expr->getOperand(0);
+
+ if (!osr_initializer_base)
+ return false;
+
+ // Find the string's initializer (a ConstantArray) and get the string from it
+
+ GlobalVariable *_objc_meth_var_name_ =
+ dyn_cast<GlobalVariable>(osr_initializer_base);
+
+ if (!_objc_meth_var_name_ || !_objc_meth_var_name_->hasInitializer())
+ return false;
+
+ Constant *omvn_initializer = _objc_meth_var_name_->getInitializer();
+
+ ConstantDataArray *omvn_initializer_array =
+ dyn_cast<ConstantDataArray>(omvn_initializer);
+
+ if (!omvn_initializer_array->isString())
+ return false;
+
+ std::string omvn_initializer_string = omvn_initializer_array->getAsString();
+
+ if (log)
+ log->Printf("Found Objective-C selector reference \"%s\"",
+ omvn_initializer_string.c_str());
+
+ // Construct a call to sel_registerName
+
+ if (!m_sel_registerName) {
+ lldb::addr_t sel_registerName_addr;
+
+ bool missing_weak = false;
+ static lldb_private::ConstString g_sel_registerName_str("sel_registerName");
+ sel_registerName_addr = m_execution_unit.FindSymbol(g_sel_registerName_str,
+ missing_weak);
+ if (sel_registerName_addr == LLDB_INVALID_ADDRESS || missing_weak)
+ return false;
+
+ if (log)
+ log->Printf("Found sel_registerName at 0x%" PRIx64,
+ sel_registerName_addr);
+
+ // Build the function type: struct objc_selector
+ // *sel_registerName(uint8_t*)
+
+ // The below code would be "more correct," but in actuality what's required
+ // is uint8_t*
+ // Type *sel_type = StructType::get(m_module->getContext());
+ // Type *sel_ptr_type = PointerType::getUnqual(sel_type);
+ Type *sel_ptr_type = Type::getInt8PtrTy(m_module->getContext());
+
+ Type *type_array[1];
+
+ type_array[0] = llvm::Type::getInt8PtrTy(m_module->getContext());
+
+ ArrayRef<Type *> srN_arg_types(type_array, 1);
+
+ llvm::FunctionType *srN_type =
+ FunctionType::get(sel_ptr_type, srN_arg_types, false);
+
+ // Build the constant containing the pointer to the function
+ PointerType *srN_ptr_ty = PointerType::getUnqual(srN_type);
+ Constant *srN_addr_int =
+ ConstantInt::get(m_intptr_ty, sel_registerName_addr, false);
+ m_sel_registerName = {srN_type,
+ ConstantExpr::getIntToPtr(srN_addr_int, srN_ptr_ty)};
+ }
+
+ Value *argument_array[1];
+
+ Constant *omvn_pointer = ConstantExpr::getBitCast(
+ _objc_meth_var_name_, Type::getInt8PtrTy(m_module->getContext()));
+
+ argument_array[0] = omvn_pointer;
+
+ ArrayRef<Value *> srN_arguments(argument_array, 1);
+
+ CallInst *srN_call = CallInst::Create(m_sel_registerName, srN_arguments,
+ "sel_registerName", selector_load);
+
+ // Replace the load with the call in all users
+
+ selector_load->replaceAllUsesWith(srN_call);
+
+ selector_load->eraseFromParent();
+
+ return true;
+}
+
+bool IRForTarget::RewriteObjCSelectors(BasicBlock &basic_block) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ BasicBlock::iterator ii;
+
+ typedef SmallVector<Instruction *, 2> InstrList;
+ typedef InstrList::iterator InstrIterator;
+
+ InstrList selector_loads;
+
+ for (ii = basic_block.begin(); ii != basic_block.end(); ++ii) {
+ Instruction &inst = *ii;
+
+ if (LoadInst *load = dyn_cast<LoadInst>(&inst))
+ if (IsObjCSelectorRef(load->getPointerOperand()))
+ selector_loads.push_back(&inst);
+ }
+
+ InstrIterator iter;
+
+ for (iter = selector_loads.begin(); iter != selector_loads.end(); ++iter) {
+ if (!RewriteObjCSelector(*iter)) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Couldn't change a "
+ "static reference to an Objective-C selector to a "
+ "dynamic reference\n");
+
+ if (log)
+ log->PutCString(
+ "Couldn't rewrite a reference to an Objective-C selector");
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool IsObjCClassReference(Value *value) {
+ GlobalVariable *global_variable = dyn_cast<GlobalVariable>(value);
+
+ return !(!global_variable || !global_variable->hasName() ||
+ !global_variable->getName().startswith("OBJC_CLASS_REFERENCES_"));
+}
+
+// This function does not report errors; its callers are responsible.
+bool IRForTarget::RewriteObjCClassReference(Instruction *class_load) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ LoadInst *load = dyn_cast<LoadInst>(class_load);
+
+ if (!load)
+ return false;
+
+ // Unpack the class name from the reference. In LLVM IR, a reference to an
+ // Objective-C class gets represented as
+ //
+ // %tmp = load %struct._objc_class*,
+ // %struct._objc_class** @OBJC_CLASS_REFERENCES_, align 4
+ //
+ // @"OBJC_CLASS_REFERENCES_ is a bitcast of a character array called
+ // @OBJC_CLASS_NAME_. @OBJC_CLASS_NAME contains the string.
+
+ // Find the pointer's initializer (a ConstantExpr with opcode BitCast) and
+ // get the string from its target
+
+ GlobalVariable *_objc_class_references_ =
+ dyn_cast<GlobalVariable>(load->getPointerOperand());
+
+ if (!_objc_class_references_ ||
+ !_objc_class_references_->hasInitializer())
+ return false;
+
+ Constant *ocr_initializer = _objc_class_references_->getInitializer();
+
+ ConstantExpr *ocr_initializer_expr = dyn_cast<ConstantExpr>(ocr_initializer);
+
+ if (!ocr_initializer_expr ||
+ ocr_initializer_expr->getOpcode() != Instruction::BitCast)
+ return false;
+
+ Value *ocr_initializer_base = ocr_initializer_expr->getOperand(0);
+
+ if (!ocr_initializer_base)
+ return false;
+
+ // Find the string's initializer (a ConstantArray) and get the string from it
+
+ GlobalVariable *_objc_class_name_ =
+ dyn_cast<GlobalVariable>(ocr_initializer_base);
+
+ if (!_objc_class_name_ || !_objc_class_name_->hasInitializer())
+ return false;
+
+ Constant *ocn_initializer = _objc_class_name_->getInitializer();
+
+ ConstantDataArray *ocn_initializer_array =
+ dyn_cast<ConstantDataArray>(ocn_initializer);
+
+ if (!ocn_initializer_array->isString())
+ return false;
+
+ std::string ocn_initializer_string = ocn_initializer_array->getAsString();
+
+ if (log)
+ log->Printf("Found Objective-C class reference \"%s\"",
+ ocn_initializer_string.c_str());
+
+ // Construct a call to objc_getClass
+
+ if (!m_objc_getClass) {
+ lldb::addr_t objc_getClass_addr;
+
+ bool missing_weak = false;
+ static lldb_private::ConstString g_objc_getClass_str("objc_getClass");
+ objc_getClass_addr = m_execution_unit.FindSymbol(g_objc_getClass_str,
+ missing_weak);
+ if (objc_getClass_addr == LLDB_INVALID_ADDRESS || missing_weak)
+ return false;
+
+ if (log)
+ log->Printf("Found objc_getClass at 0x%" PRIx64,
+ objc_getClass_addr);
+
+ // Build the function type: %struct._objc_class *objc_getClass(i8*)
+
+ Type *class_type = load->getType();
+ Type *type_array[1];
+ type_array[0] = llvm::Type::getInt8PtrTy(m_module->getContext());
+
+ ArrayRef<Type *> ogC_arg_types(type_array, 1);
+
+ llvm::FunctionType *ogC_type =
+ FunctionType::get(class_type, ogC_arg_types, false);
+
+ // Build the constant containing the pointer to the function
+ PointerType *ogC_ptr_ty = PointerType::getUnqual(ogC_type);
+ Constant *ogC_addr_int =
+ ConstantInt::get(m_intptr_ty, objc_getClass_addr, false);
+ m_objc_getClass = {ogC_type,
+ ConstantExpr::getIntToPtr(ogC_addr_int, ogC_ptr_ty)};
+ }
+
+ Value *argument_array[1];
+
+ Constant *ocn_pointer = ConstantExpr::getBitCast(
+ _objc_class_name_, Type::getInt8PtrTy(m_module->getContext()));
+
+ argument_array[0] = ocn_pointer;
+
+ ArrayRef<Value *> ogC_arguments(argument_array, 1);
+
+ CallInst *ogC_call = CallInst::Create(m_objc_getClass, ogC_arguments,
+ "objc_getClass", class_load);
+
+ // Replace the load with the call in all users
+
+ class_load->replaceAllUsesWith(ogC_call);
+
+ class_load->eraseFromParent();
+
+ return true;
+}
+
+bool IRForTarget::RewriteObjCClassReferences(BasicBlock &basic_block) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ BasicBlock::iterator ii;
+
+ typedef SmallVector<Instruction *, 2> InstrList;
+ typedef InstrList::iterator InstrIterator;
+
+ InstrList class_loads;
+
+ for (ii = basic_block.begin(); ii != basic_block.end(); ++ii) {
+ Instruction &inst = *ii;
+
+ if (LoadInst *load = dyn_cast<LoadInst>(&inst))
+ if (IsObjCClassReference(load->getPointerOperand()))
+ class_loads.push_back(&inst);
+ }
+
+ InstrIterator iter;
+
+ for (iter = class_loads.begin(); iter != class_loads.end(); ++iter) {
+ if (!RewriteObjCClassReference(*iter)) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Couldn't change a "
+ "static reference to an Objective-C class to a "
+ "dynamic reference\n");
+
+ if (log)
+ log->PutCString(
+ "Couldn't rewrite a reference to an Objective-C class");
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// This function does not report errors; its callers are responsible.
+bool IRForTarget::RewritePersistentAlloc(llvm::Instruction *persistent_alloc) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ AllocaInst *alloc = dyn_cast<AllocaInst>(persistent_alloc);
+
+ MDNode *alloc_md = alloc->getMetadata("clang.decl.ptr");
+
+ if (!alloc_md || !alloc_md->getNumOperands())
+ return false;
+
+ ConstantInt *constant_int =
+ mdconst::dyn_extract<ConstantInt>(alloc_md->getOperand(0));
+
+ if (!constant_int)
+ return false;
+
+ // We attempt to register this as a new persistent variable with the DeclMap.
+
+ uintptr_t ptr = constant_int->getZExtValue();
+
+ clang::VarDecl *decl = reinterpret_cast<clang::VarDecl *>(ptr);
+
+ lldb_private::TypeFromParser result_decl_type(
+ decl->getType().getAsOpaquePtr(),
+ lldb_private::ClangASTContext::GetASTContext(&decl->getASTContext()));
+
+ StringRef decl_name(decl->getName());
+ lldb_private::ConstString persistent_variable_name(decl_name.data(),
+ decl_name.size());
+ if (!m_decl_map->AddPersistentVariable(decl, persistent_variable_name,
+ result_decl_type, false, false))
+ return false;
+
+ GlobalVariable *persistent_global = new GlobalVariable(
+ (*m_module), alloc->getType(), false, /* not constant */
+ GlobalValue::ExternalLinkage, nullptr, /* no initializer */
+ alloc->getName().str());
+
+ // What we're going to do here is make believe this was a regular old
+ // external variable. That means we need to make the metadata valid.
+
+ NamedMDNode *named_metadata =
+ m_module->getOrInsertNamedMetadata("clang.global.decl.ptrs");
+
+ llvm::Metadata *values[2];
+ values[0] = ConstantAsMetadata::get(persistent_global);
+ values[1] = ConstantAsMetadata::get(constant_int);
+
+ ArrayRef<llvm::Metadata *> value_ref(values, 2);
+
+ MDNode *persistent_global_md = MDNode::get(m_module->getContext(), value_ref);
+ named_metadata->addOperand(persistent_global_md);
+
+ // Now, since the variable is a pointer variable, we will drop in a load of
+ // that pointer variable.
+
+ LoadInst *persistent_load = new LoadInst(persistent_global, "", alloc);
+
+ if (log)
+ log->Printf("Replacing \"%s\" with \"%s\"", PrintValue(alloc).c_str(),
+ PrintValue(persistent_load).c_str());
+
+ alloc->replaceAllUsesWith(persistent_load);
+ alloc->eraseFromParent();
+
+ return true;
+}
+
+bool IRForTarget::RewritePersistentAllocs(llvm::BasicBlock &basic_block) {
+ if (!m_resolve_vars)
+ return true;
+
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ BasicBlock::iterator ii;
+
+ typedef SmallVector<Instruction *, 2> InstrList;
+ typedef InstrList::iterator InstrIterator;
+
+ InstrList pvar_allocs;
+
+ for (ii = basic_block.begin(); ii != basic_block.end(); ++ii) {
+ Instruction &inst = *ii;
+
+ if (AllocaInst *alloc = dyn_cast<AllocaInst>(&inst)) {
+ llvm::StringRef alloc_name = alloc->getName();
+
+ if (alloc_name.startswith("$") && !alloc_name.startswith("$__lldb")) {
+ if (alloc_name.find_first_of("0123456789") == 1) {
+ if (log)
+ log->Printf("Rejecting a numeric persistent variable.");
+
+ m_error_stream.Printf("Error [IRForTarget]: Names starting with $0, "
+ "$1, ... are reserved for use as result "
+ "names\n");
+
+ return false;
+ }
+
+ pvar_allocs.push_back(alloc);
+ }
+ }
+ }
+
+ InstrIterator iter;
+
+ for (iter = pvar_allocs.begin(); iter != pvar_allocs.end(); ++iter) {
+ if (!RewritePersistentAlloc(*iter)) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Couldn't rewrite "
+ "the creation of a persistent variable\n");
+
+ if (log)
+ log->PutCString(
+ "Couldn't rewrite the creation of a persistent variable");
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool IRForTarget::MaterializeInitializer(uint8_t *data, Constant *initializer) {
+ if (!initializer)
+ return true;
+
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log && log->GetVerbose())
+ log->Printf(" MaterializeInitializer(%p, %s)", (void *)data,
+ PrintValue(initializer).c_str());
+
+ Type *initializer_type = initializer->getType();
+
+ if (ConstantInt *int_initializer = dyn_cast<ConstantInt>(initializer)) {
+ size_t constant_size = m_target_data->getTypeStoreSize(initializer_type);
+ lldb_private::Scalar scalar = int_initializer->getValue().zextOrTrunc(
+ llvm::NextPowerOf2(constant_size) * 8);
+
+ lldb_private::Status get_data_error;
+ return scalar.GetAsMemoryData(data, constant_size,
+ lldb_private::endian::InlHostByteOrder(),
+ get_data_error) != 0;
+ } else if (ConstantDataArray *array_initializer =
+ dyn_cast<ConstantDataArray>(initializer)) {
+ if (array_initializer->isString()) {
+ std::string array_initializer_string = array_initializer->getAsString();
+ memcpy(data, array_initializer_string.c_str(),
+ m_target_data->getTypeStoreSize(initializer_type));
+ } else {
+ ArrayType *array_initializer_type = array_initializer->getType();
+ Type *array_element_type = array_initializer_type->getElementType();
+
+ size_t element_size = m_target_data->getTypeAllocSize(array_element_type);
+
+ for (unsigned i = 0; i < array_initializer->getNumOperands(); ++i) {
+ Value *operand_value = array_initializer->getOperand(i);
+ Constant *operand_constant = dyn_cast<Constant>(operand_value);
+
+ if (!operand_constant)
+ return false;
+
+ if (!MaterializeInitializer(data + (i * element_size),
+ operand_constant))
+ return false;
+ }
+ }
+ return true;
+ } else if (ConstantStruct *struct_initializer =
+ dyn_cast<ConstantStruct>(initializer)) {
+ StructType *struct_initializer_type = struct_initializer->getType();
+ const StructLayout *struct_layout =
+ m_target_data->getStructLayout(struct_initializer_type);
+
+ for (unsigned i = 0; i < struct_initializer->getNumOperands(); ++i) {
+ if (!MaterializeInitializer(data + struct_layout->getElementOffset(i),
+ struct_initializer->getOperand(i)))
+ return false;
+ }
+ return true;
+ } else if (isa<ConstantAggregateZero>(initializer)) {
+ memset(data, 0, m_target_data->getTypeStoreSize(initializer_type));
+ return true;
+ }
+ return false;
+}
+
+// This function does not report errors; its callers are responsible.
+bool IRForTarget::MaybeHandleVariable(Value *llvm_value_ptr) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log)
+ log->Printf("MaybeHandleVariable (%s)", PrintValue(llvm_value_ptr).c_str());
+
+ if (ConstantExpr *constant_expr = dyn_cast<ConstantExpr>(llvm_value_ptr)) {
+ switch (constant_expr->getOpcode()) {
+ default:
+ break;
+ case Instruction::GetElementPtr:
+ case Instruction::BitCast:
+ Value *s = constant_expr->getOperand(0);
+ if (!MaybeHandleVariable(s))
+ return false;
+ }
+ } else if (GlobalVariable *global_variable =
+ dyn_cast<GlobalVariable>(llvm_value_ptr)) {
+ if (!GlobalValue::isExternalLinkage(global_variable->getLinkage()))
+ return true;
+
+ clang::NamedDecl *named_decl = DeclForGlobal(global_variable);
+
+ if (!named_decl) {
+ if (IsObjCSelectorRef(llvm_value_ptr))
+ return true;
+
+ if (!global_variable->hasExternalLinkage())
+ return true;
+
+ if (log)
+ log->Printf("Found global variable \"%s\" without metadata",
+ global_variable->getName().str().c_str());
+
+ return false;
+ }
+
+ std::string name(named_decl->getName().str());
+
+ clang::ValueDecl *value_decl = dyn_cast<clang::ValueDecl>(named_decl);
+ if (value_decl == nullptr)
+ return false;
+
+ lldb_private::CompilerType compiler_type(&value_decl->getASTContext(),
+ value_decl->getType());
+
+ const Type *value_type = nullptr;
+
+ if (name[0] == '$') {
+ // The $__lldb_expr_result name indicates the return value has allocated
+ // as a static variable. Per the comment at
+ // ASTResultSynthesizer::SynthesizeBodyResult, accesses to this static
+ // variable need to be redirected to the result of dereferencing a
+ // pointer that is passed in as one of the arguments.
+ //
+ // Consequently, when reporting the size of the type, we report a pointer
+ // type pointing to the type of $__lldb_expr_result, not the type itself.
+ //
+ // We also do this for any user-declared persistent variables.
+ compiler_type = compiler_type.GetPointerType();
+ value_type = PointerType::get(global_variable->getType(), 0);
+ } else {
+ value_type = global_variable->getType();
+ }
+
+ llvm::Optional<uint64_t> value_size = compiler_type.GetByteSize(nullptr);
+ if (!value_size)
+ return false;
+ lldb::offset_t value_alignment =
+ (compiler_type.GetTypeBitAlign() + 7ull) / 8ull;
+
+ if (log) {
+ log->Printf("Type of \"%s\" is [clang \"%s\", llvm \"%s\"] [size %" PRIu64
+ ", align %" PRIu64 "]",
+ name.c_str(),
+ lldb_private::ClangUtil::GetQualType(compiler_type)
+ .getAsString()
+ .c_str(),
+ PrintType(value_type).c_str(), *value_size, value_alignment);
+ }
+
+ if (named_decl &&
+ !m_decl_map->AddValueToStruct(
+ named_decl, lldb_private::ConstString(name.c_str()), llvm_value_ptr,
+ *value_size, value_alignment)) {
+ if (!global_variable->hasExternalLinkage())
+ return true;
+ else
+ return true;
+ }
+ } else if (dyn_cast<llvm::Function>(llvm_value_ptr)) {
+ if (log)
+ log->Printf("Function pointers aren't handled right now");
+
+ return false;
+ }
+
+ return true;
+}
+
+// This function does not report errors; its callers are responsible.
+bool IRForTarget::HandleSymbol(Value *symbol) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ lldb_private::ConstString name(symbol->getName().str().c_str());
+
+ lldb::addr_t symbol_addr =
+ m_decl_map->GetSymbolAddress(name, lldb::eSymbolTypeAny);
+
+ if (symbol_addr == LLDB_INVALID_ADDRESS) {
+ if (log)
+ log->Printf("Symbol \"%s\" had no address", name.GetCString());
+
+ return false;
+ }
+
+ if (log)
+ log->Printf("Found \"%s\" at 0x%" PRIx64, name.GetCString(), symbol_addr);
+
+ Type *symbol_type = symbol->getType();
+
+ Constant *symbol_addr_int = ConstantInt::get(m_intptr_ty, symbol_addr, false);
+
+ Value *symbol_addr_ptr =
+ ConstantExpr::getIntToPtr(symbol_addr_int, symbol_type);
+
+ if (log)
+ log->Printf("Replacing %s with %s", PrintValue(symbol).c_str(),
+ PrintValue(symbol_addr_ptr).c_str());
+
+ symbol->replaceAllUsesWith(symbol_addr_ptr);
+
+ return true;
+}
+
+bool IRForTarget::MaybeHandleCallArguments(CallInst *Old) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ if (log)
+ log->Printf("MaybeHandleCallArguments(%s)", PrintValue(Old).c_str());
+
+ for (unsigned op_index = 0, num_ops = Old->getNumArgOperands();
+ op_index < num_ops; ++op_index)
+ if (!MaybeHandleVariable(Old->getArgOperand(
+ op_index))) // conservatively believe that this is a store
+ {
+ m_error_stream.Printf("Internal error [IRForTarget]: Couldn't rewrite "
+ "one of the arguments of a function call.\n");
+
+ return false;
+ }
+
+ return true;
+}
+
+bool IRForTarget::HandleObjCClass(Value *classlist_reference) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ GlobalVariable *global_variable =
+ dyn_cast<GlobalVariable>(classlist_reference);
+
+ if (!global_variable)
+ return false;
+
+ Constant *initializer = global_variable->getInitializer();
+
+ if (!initializer)
+ return false;
+
+ if (!initializer->hasName())
+ return false;
+
+ StringRef name(initializer->getName());
+ lldb_private::ConstString name_cstr(name.str().c_str());
+ lldb::addr_t class_ptr =
+ m_decl_map->GetSymbolAddress(name_cstr, lldb::eSymbolTypeObjCClass);
+
+ if (log)
+ log->Printf("Found reference to Objective-C class %s (0x%llx)",
+ name_cstr.AsCString(), (unsigned long long)class_ptr);
+
+ if (class_ptr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ if (global_variable->use_empty())
+ return false;
+
+ SmallVector<LoadInst *, 2> load_instructions;
+
+ for (llvm::User *u : global_variable->users()) {
+ if (LoadInst *load_instruction = dyn_cast<LoadInst>(u))
+ load_instructions.push_back(load_instruction);
+ }
+
+ if (load_instructions.empty())
+ return false;
+
+ Constant *class_addr = ConstantInt::get(m_intptr_ty, (uint64_t)class_ptr);
+
+ for (LoadInst *load_instruction : load_instructions) {
+ Constant *class_bitcast =
+ ConstantExpr::getIntToPtr(class_addr, load_instruction->getType());
+
+ load_instruction->replaceAllUsesWith(class_bitcast);
+
+ load_instruction->eraseFromParent();
+ }
+
+ return true;
+}
+
+bool IRForTarget::RemoveCXAAtExit(BasicBlock &basic_block) {
+ BasicBlock::iterator ii;
+
+ std::vector<CallInst *> calls_to_remove;
+
+ for (ii = basic_block.begin(); ii != basic_block.end(); ++ii) {
+ Instruction &inst = *ii;
+
+ CallInst *call = dyn_cast<CallInst>(&inst);
+
+ // MaybeHandleCallArguments handles error reporting; we are silent here
+ if (!call)
+ continue;
+
+ bool remove = false;
+
+ llvm::Function *func = call->getCalledFunction();
+
+ if (func && func->getName() == "__cxa_atexit")
+ remove = true;
+
+ llvm::Value *val = call->getCalledValue();
+
+ if (val && val->getName() == "__cxa_atexit")
+ remove = true;
+
+ if (remove)
+ calls_to_remove.push_back(call);
+ }
+
+ for (std::vector<CallInst *>::iterator ci = calls_to_remove.begin(),
+ ce = calls_to_remove.end();
+ ci != ce; ++ci) {
+ (*ci)->eraseFromParent();
+ }
+
+ return true;
+}
+
+bool IRForTarget::ResolveCalls(BasicBlock &basic_block) {
+ /////////////////////////////////////////////////////////////////////////
+ // Prepare the current basic block for execution in the remote process
+ //
+
+ BasicBlock::iterator ii;
+
+ for (ii = basic_block.begin(); ii != basic_block.end(); ++ii) {
+ Instruction &inst = *ii;
+
+ CallInst *call = dyn_cast<CallInst>(&inst);
+
+ // MaybeHandleCallArguments handles error reporting; we are silent here
+ if (call && !MaybeHandleCallArguments(call))
+ return false;
+ }
+
+ return true;
+}
+
+bool IRForTarget::ResolveExternals(Function &llvm_function) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ for (GlobalVariable &global_var : m_module->globals()) {
+ std::string global_name = global_var.getName().str();
+
+ if (log)
+ log->Printf("Examining %s, DeclForGlobalValue returns %p",
+ global_name.c_str(),
+ static_cast<void *>(DeclForGlobal(&global_var)));
+
+ if (global_name.find("OBJC_IVAR") == 0) {
+ if (!HandleSymbol(&global_var)) {
+ m_error_stream.Printf("Error [IRForTarget]: Couldn't find Objective-C "
+ "indirect ivar symbol %s\n",
+ global_name.c_str());
+
+ return false;
+ }
+ } else if (global_name.find("OBJC_CLASSLIST_REFERENCES_$") !=
+ global_name.npos) {
+ if (!HandleObjCClass(&global_var)) {
+ m_error_stream.Printf("Error [IRForTarget]: Couldn't resolve the class "
+ "for an Objective-C static method call\n");
+
+ return false;
+ }
+ } else if (global_name.find("OBJC_CLASSLIST_SUP_REFS_$") !=
+ global_name.npos) {
+ if (!HandleObjCClass(&global_var)) {
+ m_error_stream.Printf("Error [IRForTarget]: Couldn't resolve the class "
+ "for an Objective-C static method call\n");
+
+ return false;
+ }
+ } else if (DeclForGlobal(&global_var)) {
+ if (!MaybeHandleVariable(&global_var)) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Couldn't rewrite "
+ "external variable %s\n",
+ global_name.c_str());
+
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool isGuardVariableRef(Value *V) {
+ Constant *Old = nullptr;
+
+ if (!(Old = dyn_cast<Constant>(V)))
+ return false;
+
+ ConstantExpr *CE = nullptr;
+
+ if ((CE = dyn_cast<ConstantExpr>(V))) {
+ if (CE->getOpcode() != Instruction::BitCast)
+ return false;
+
+ Old = CE->getOperand(0);
+ }
+
+ GlobalVariable *GV = dyn_cast<GlobalVariable>(Old);
+
+ if (!GV || !GV->hasName() ||
+ (!GV->getName().startswith("_ZGV") && // Itanium ABI guard variable
+ !GV->getName().endswith("@4IA"))) // Microsoft ABI guard variable
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void IRForTarget::TurnGuardLoadIntoZero(llvm::Instruction *guard_load) {
+ Constant *zero(Constant::getNullValue(guard_load->getType()));
+ guard_load->replaceAllUsesWith(zero);
+ guard_load->eraseFromParent();
+}
+
+static void ExciseGuardStore(Instruction *guard_store) {
+ guard_store->eraseFromParent();
+}
+
+bool IRForTarget::RemoveGuards(BasicBlock &basic_block) {
+ ///////////////////////////////////////////////////////
+ // Eliminate any reference to guard variables found.
+ //
+
+ BasicBlock::iterator ii;
+
+ typedef SmallVector<Instruction *, 2> InstrList;
+ typedef InstrList::iterator InstrIterator;
+
+ InstrList guard_loads;
+ InstrList guard_stores;
+
+ for (ii = basic_block.begin(); ii != basic_block.end(); ++ii) {
+ Instruction &inst = *ii;
+
+ if (LoadInst *load = dyn_cast<LoadInst>(&inst))
+ if (isGuardVariableRef(load->getPointerOperand()))
+ guard_loads.push_back(&inst);
+
+ if (StoreInst *store = dyn_cast<StoreInst>(&inst))
+ if (isGuardVariableRef(store->getPointerOperand()))
+ guard_stores.push_back(&inst);
+ }
+
+ InstrIterator iter;
+
+ for (iter = guard_loads.begin(); iter != guard_loads.end(); ++iter)
+ TurnGuardLoadIntoZero(*iter);
+
+ for (iter = guard_stores.begin(); iter != guard_stores.end(); ++iter)
+ ExciseGuardStore(*iter);
+
+ return true;
+}
+
+// This function does not report errors; its callers are responsible.
+bool IRForTarget::UnfoldConstant(Constant *old_constant,
+ llvm::Function *llvm_function,
+ FunctionValueCache &value_maker,
+ FunctionValueCache &entry_instruction_finder,
+ lldb_private::Stream &error_stream) {
+ SmallVector<User *, 16> users;
+
+ // We do this because the use list might change, invalidating our iterator.
+ // Much better to keep a work list ourselves.
+ for (llvm::User *u : old_constant->users())
+ users.push_back(u);
+
+ for (size_t i = 0; i < users.size(); ++i) {
+ User *user = users[i];
+
+ if (Constant *constant = dyn_cast<Constant>(user)) {
+ // synthesize a new non-constant equivalent of the constant
+
+ if (ConstantExpr *constant_expr = dyn_cast<ConstantExpr>(constant)) {
+ switch (constant_expr->getOpcode()) {
+ default:
+ error_stream.Printf("error [IRForTarget internal]: Unhandled "
+ "constant expression type: \"%s\"",
+ PrintValue(constant_expr).c_str());
+ return false;
+ case Instruction::BitCast: {
+ FunctionValueCache bit_cast_maker(
+ [&value_maker, &entry_instruction_finder, old_constant,
+ constant_expr](llvm::Function *function) -> llvm::Value * {
+ // UnaryExpr
+ // OperandList[0] is value
+
+ if (constant_expr->getOperand(0) != old_constant)
+ return constant_expr;
+
+ return new BitCastInst(
+ value_maker.GetValue(function), constant_expr->getType(),
+ "", llvm::cast<Instruction>(
+ entry_instruction_finder.GetValue(function)));
+ });
+
+ if (!UnfoldConstant(constant_expr, llvm_function, bit_cast_maker,
+ entry_instruction_finder, error_stream))
+ return false;
+ } break;
+ case Instruction::GetElementPtr: {
+ // GetElementPtrConstantExpr
+ // OperandList[0] is base
+ // OperandList[1]... are indices
+
+ FunctionValueCache get_element_pointer_maker(
+ [&value_maker, &entry_instruction_finder, old_constant,
+ constant_expr](llvm::Function *function) -> llvm::Value * {
+ Value *ptr = constant_expr->getOperand(0);
+
+ if (ptr == old_constant)
+ ptr = value_maker.GetValue(function);
+
+ std::vector<Value *> index_vector;
+
+ unsigned operand_index;
+ unsigned num_operands = constant_expr->getNumOperands();
+
+ for (operand_index = 1; operand_index < num_operands;
+ ++operand_index) {
+ Value *operand = constant_expr->getOperand(operand_index);
+
+ if (operand == old_constant)
+ operand = value_maker.GetValue(function);
+
+ index_vector.push_back(operand);
+ }
+
+ ArrayRef<Value *> indices(index_vector);
+
+ return GetElementPtrInst::Create(
+ nullptr, ptr, indices, "",
+ llvm::cast<Instruction>(
+ entry_instruction_finder.GetValue(function)));
+ });
+
+ if (!UnfoldConstant(constant_expr, llvm_function,
+ get_element_pointer_maker,
+ entry_instruction_finder, error_stream))
+ return false;
+ } break;
+ }
+ } else {
+ error_stream.Printf(
+ "error [IRForTarget internal]: Unhandled constant type: \"%s\"",
+ PrintValue(constant).c_str());
+ return false;
+ }
+ } else {
+ if (Instruction *inst = llvm::dyn_cast<Instruction>(user)) {
+ if (llvm_function && inst->getParent()->getParent() != llvm_function) {
+ error_stream.PutCString("error: Capturing non-local variables in "
+ "expressions is unsupported.\n");
+ return false;
+ }
+ inst->replaceUsesOfWith(
+ old_constant, value_maker.GetValue(inst->getParent()->getParent()));
+ } else {
+ error_stream.Printf(
+ "error [IRForTarget internal]: Unhandled non-constant type: \"%s\"",
+ PrintValue(user).c_str());
+ return false;
+ }
+ }
+ }
+
+ if (!isa<GlobalValue>(old_constant)) {
+ old_constant->destroyConstant();
+ }
+
+ return true;
+}
+
+bool IRForTarget::ReplaceVariables(Function &llvm_function) {
+ if (!m_resolve_vars)
+ return true;
+
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ m_decl_map->DoStructLayout();
+
+ if (log)
+ log->Printf("Element arrangement:");
+
+ uint32_t num_elements;
+ uint32_t element_index;
+
+ size_t size;
+ lldb::offset_t alignment;
+
+ if (!m_decl_map->GetStructInfo(num_elements, size, alignment))
+ return false;
+
+ Function::arg_iterator iter(llvm_function.arg_begin());
+
+ if (iter == llvm_function.arg_end()) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Wrapper takes no "
+ "arguments (should take at least a struct pointer)");
+
+ return false;
+ }
+
+ Argument *argument = &*iter;
+
+ if (argument->getName().equals("this")) {
+ ++iter;
+
+ if (iter == llvm_function.arg_end()) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Wrapper takes only "
+ "'this' argument (should take a struct pointer "
+ "too)");
+
+ return false;
+ }
+
+ argument = &*iter;
+ } else if (argument->getName().equals("self")) {
+ ++iter;
+
+ if (iter == llvm_function.arg_end()) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Wrapper takes only "
+ "'self' argument (should take '_cmd' and a struct "
+ "pointer too)");
+
+ return false;
+ }
+
+ if (!iter->getName().equals("_cmd")) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Wrapper takes '%s' "
+ "after 'self' argument (should take '_cmd')",
+ iter->getName().str().c_str());
+
+ return false;
+ }
+
+ ++iter;
+
+ if (iter == llvm_function.arg_end()) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Wrapper takes only "
+ "'self' and '_cmd' arguments (should take a struct "
+ "pointer too)");
+
+ return false;
+ }
+
+ argument = &*iter;
+ }
+
+ if (!argument->getName().equals("$__lldb_arg")) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Wrapper takes an "
+ "argument named '%s' instead of the struct pointer",
+ argument->getName().str().c_str());
+
+ return false;
+ }
+
+ if (log)
+ log->Printf("Arg: \"%s\"", PrintValue(argument).c_str());
+
+ BasicBlock &entry_block(llvm_function.getEntryBlock());
+ Instruction *FirstEntryInstruction(entry_block.getFirstNonPHIOrDbg());
+
+ if (!FirstEntryInstruction) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Couldn't find the "
+ "first instruction in the wrapper for use in "
+ "rewriting");
+
+ return false;
+ }
+
+ LLVMContext &context(m_module->getContext());
+ IntegerType *offset_type(Type::getInt32Ty(context));
+
+ if (!offset_type) {
+ m_error_stream.Printf(
+ "Internal error [IRForTarget]: Couldn't produce an offset type");
+
+ return false;
+ }
+
+ for (element_index = 0; element_index < num_elements; ++element_index) {
+ const clang::NamedDecl *decl = nullptr;
+ Value *value = nullptr;
+ lldb::offset_t offset;
+ lldb_private::ConstString name;
+
+ if (!m_decl_map->GetStructElement(decl, value, offset, name,
+ element_index)) {
+ m_error_stream.Printf(
+ "Internal error [IRForTarget]: Structure information is incomplete");
+
+ return false;
+ }
+
+ if (log)
+ log->Printf(" \"%s\" (\"%s\") placed at %" PRIu64, name.GetCString(),
+ decl->getNameAsString().c_str(), offset);
+
+ if (value) {
+ if (log)
+ log->Printf(" Replacing [%s]", PrintValue(value).c_str());
+
+ FunctionValueCache body_result_maker(
+ [this, name, offset_type, offset, argument,
+ value](llvm::Function *function) -> llvm::Value * {
+ // Per the comment at ASTResultSynthesizer::SynthesizeBodyResult,
+ // in cases where the result variable is an rvalue, we have to
+ // synthesize a dereference of the appropriate structure entry in
+ // order to produce the static variable that the AST thinks it is
+ // accessing.
+
+ llvm::Instruction *entry_instruction = llvm::cast<Instruction>(
+ m_entry_instruction_finder.GetValue(function));
+
+ ConstantInt *offset_int(
+ ConstantInt::get(offset_type, offset, true));
+ GetElementPtrInst *get_element_ptr = GetElementPtrInst::Create(
+ nullptr, argument, offset_int, "", entry_instruction);
+
+ if (name == m_result_name && !m_result_is_pointer) {
+ BitCastInst *bit_cast = new BitCastInst(
+ get_element_ptr, value->getType()->getPointerTo(), "",
+ entry_instruction);
+
+ LoadInst *load = new LoadInst(bit_cast, "", entry_instruction);
+
+ return load;
+ } else {
+ BitCastInst *bit_cast = new BitCastInst(
+ get_element_ptr, value->getType(), "", entry_instruction);
+
+ return bit_cast;
+ }
+ });
+
+ if (Constant *constant = dyn_cast<Constant>(value)) {
+ if (!UnfoldConstant(constant, &llvm_function, body_result_maker,
+ m_entry_instruction_finder, m_error_stream)) {
+ return false;
+ }
+ } else if (Instruction *instruction = dyn_cast<Instruction>(value)) {
+ if (instruction->getParent()->getParent() != &llvm_function) {
+ m_error_stream.PutCString("error: Capturing non-local variables in "
+ "expressions is unsupported.\n");
+ return false;
+ }
+ value->replaceAllUsesWith(
+ body_result_maker.GetValue(instruction->getParent()->getParent()));
+ } else {
+ if (log)
+ log->Printf("Unhandled non-constant type: \"%s\"",
+ PrintValue(value).c_str());
+ return false;
+ }
+
+ if (GlobalVariable *var = dyn_cast<GlobalVariable>(value))
+ var->eraseFromParent();
+ }
+ }
+
+ if (log)
+ log->Printf("Total structure [align %" PRId64 ", size %" PRIu64 "]",
+ (int64_t)alignment, (uint64_t)size);
+
+ return true;
+}
+
+llvm::Constant *IRForTarget::BuildRelocation(llvm::Type *type,
+ uint64_t offset) {
+ llvm::Constant *offset_int = ConstantInt::get(m_intptr_ty, offset);
+
+ llvm::Constant *offset_array[1];
+
+ offset_array[0] = offset_int;
+
+ llvm::ArrayRef<llvm::Constant *> offsets(offset_array, 1);
+ llvm::Type *char_type = llvm::Type::getInt8Ty(m_module->getContext());
+ llvm::Type *char_pointer_type = char_type->getPointerTo();
+
+ llvm::Constant *reloc_placeholder_bitcast =
+ ConstantExpr::getBitCast(m_reloc_placeholder, char_pointer_type);
+ llvm::Constant *reloc_getelementptr = ConstantExpr::getGetElementPtr(
+ char_type, reloc_placeholder_bitcast, offsets);
+ llvm::Constant *reloc_bitcast =
+ ConstantExpr::getBitCast(reloc_getelementptr, type);
+
+ return reloc_bitcast;
+}
+
+bool IRForTarget::runOnModule(Module &llvm_module) {
+ lldb_private::Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+ m_module = &llvm_module;
+ m_target_data.reset(new DataLayout(m_module));
+ m_intptr_ty = llvm::Type::getIntNTy(m_module->getContext(),
+ m_target_data->getPointerSizeInBits());
+
+ if (log) {
+ std::string s;
+ raw_string_ostream oss(s);
+
+ m_module->print(oss, nullptr);
+
+ oss.flush();
+
+ log->Printf("Module as passed in to IRForTarget: \n\"%s\"", s.c_str());
+ }
+
+ Function *const main_function =
+ m_func_name.IsEmpty() ? nullptr
+ : m_module->getFunction(m_func_name.GetStringRef());
+
+ if (!m_func_name.IsEmpty() && !main_function) {
+ if (log)
+ log->Printf("Couldn't find \"%s()\" in the module",
+ m_func_name.AsCString());
+
+ m_error_stream.Printf("Internal error [IRForTarget]: Couldn't find wrapper "
+ "'%s' in the module",
+ m_func_name.AsCString());
+
+ return false;
+ }
+
+ if (main_function) {
+ if (!FixFunctionLinkage(*main_function)) {
+ if (log)
+ log->Printf("Couldn't fix the linkage for the function");
+
+ return false;
+ }
+ }
+
+ llvm::Type *int8_ty = Type::getInt8Ty(m_module->getContext());
+
+ m_reloc_placeholder = new llvm::GlobalVariable(
+ (*m_module), int8_ty, false /* IsConstant */,
+ GlobalVariable::InternalLinkage, Constant::getNullValue(int8_ty),
+ "reloc_placeholder", nullptr /* InsertBefore */,
+ GlobalVariable::NotThreadLocal /* ThreadLocal */, 0 /* AddressSpace */);
+
+ ////////////////////////////////////////////////////////////
+ // Replace $__lldb_expr_result with a persistent variable
+ //
+
+ if (main_function) {
+ if (!CreateResultVariable(*main_function)) {
+ if (log)
+ log->Printf("CreateResultVariable() failed");
+
+ // CreateResultVariable() reports its own errors, so we don't do so here
+
+ return false;
+ }
+ }
+
+ if (log && log->GetVerbose()) {
+ std::string s;
+ raw_string_ostream oss(s);
+
+ m_module->print(oss, nullptr);
+
+ oss.flush();
+
+ log->Printf("Module after creating the result variable: \n\"%s\"",
+ s.c_str());
+ }
+
+ for (Module::iterator fi = m_module->begin(), fe = m_module->end(); fi != fe;
+ ++fi) {
+ llvm::Function *function = &*fi;
+
+ if (function->begin() == function->end())
+ continue;
+
+ Function::iterator bbi;
+
+ for (bbi = function->begin(); bbi != function->end(); ++bbi) {
+ if (!RemoveGuards(*bbi)) {
+ if (log)
+ log->Printf("RemoveGuards() failed");
+
+ // RemoveGuards() reports its own errors, so we don't do so here
+
+ return false;
+ }
+
+ if (!RewritePersistentAllocs(*bbi)) {
+ if (log)
+ log->Printf("RewritePersistentAllocs() failed");
+
+ // RewritePersistentAllocs() reports its own errors, so we don't do so
+ // here
+
+ return false;
+ }
+
+ if (!RemoveCXAAtExit(*bbi)) {
+ if (log)
+ log->Printf("RemoveCXAAtExit() failed");
+
+ // RemoveCXAAtExit() reports its own errors, so we don't do so here
+
+ return false;
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////
+ // Fix all Objective-C constant strings to use NSStringWithCString:encoding:
+ //
+
+ if (!RewriteObjCConstStrings()) {
+ if (log)
+ log->Printf("RewriteObjCConstStrings() failed");
+
+ // RewriteObjCConstStrings() reports its own errors, so we don't do so here
+
+ return false;
+ }
+
+ for (Module::iterator fi = m_module->begin(), fe = m_module->end(); fi != fe;
+ ++fi) {
+ llvm::Function *function = &*fi;
+
+ for (llvm::Function::iterator bbi = function->begin(),
+ bbe = function->end();
+ bbi != bbe; ++bbi) {
+ if (!RewriteObjCSelectors(*bbi)) {
+ if (log)
+ log->Printf("RewriteObjCSelectors() failed");
+
+ // RewriteObjCSelectors() reports its own errors, so we don't do so
+ // here
+
+ return false;
+ }
+
+ if (!RewriteObjCClassReferences(*bbi)) {
+ if (log)
+ log->Printf("RewriteObjCClassReferences() failed");
+
+ // RewriteObjCClasses() reports its own errors, so we don't do so here
+
+ return false;
+ }
+ }
+ }
+
+ for (Module::iterator fi = m_module->begin(), fe = m_module->end(); fi != fe;
+ ++fi) {
+ llvm::Function *function = &*fi;
+
+ for (llvm::Function::iterator bbi = function->begin(),
+ bbe = function->end();
+ bbi != bbe; ++bbi) {
+ if (!ResolveCalls(*bbi)) {
+ if (log)
+ log->Printf("ResolveCalls() failed");
+
+ // ResolveCalls() reports its own errors, so we don't do so here
+
+ return false;
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ // Run function-level passes that only make sense on the main function
+ //
+
+ if (main_function) {
+ if (!ResolveExternals(*main_function)) {
+ if (log)
+ log->Printf("ResolveExternals() failed");
+
+ // ResolveExternals() reports its own errors, so we don't do so here
+
+ return false;
+ }
+
+ if (!ReplaceVariables(*main_function)) {
+ if (log)
+ log->Printf("ReplaceVariables() failed");
+
+ // ReplaceVariables() reports its own errors, so we don't do so here
+
+ return false;
+ }
+ }
+
+ if (log && log->GetVerbose()) {
+ std::string s;
+ raw_string_ostream oss(s);
+
+ m_module->print(oss, nullptr);
+
+ oss.flush();
+
+ log->Printf("Module after preparing for execution: \n\"%s\"", s.c_str());
+ }
+
+ return true;
+}
+
+void IRForTarget::assignPassManager(PMStack &pass_mgr_stack,
+ PassManagerType pass_mgr_type) {}
+
+PassManagerType IRForTarget::getPotentialPassManagerType() const {
+ return PMT_ModulePassManager;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.h
new file mode 100644
index 000000000000..f87fd8ac32cb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.h
@@ -0,0 +1,567 @@
+//===-- IRForTarget.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 liblldb_IRForTarget_h_
+#define liblldb_IRForTarget_h_
+
+#include "lldb/Symbol/TaggedASTType.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/lldb-public.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/Pass.h"
+
+#include <functional>
+#include <map>
+
+namespace llvm {
+class BasicBlock;
+class CallInst;
+class Constant;
+class ConstantInt;
+class Function;
+class GlobalValue;
+class GlobalVariable;
+class Instruction;
+class Module;
+class StoreInst;
+class DataLayout;
+class Value;
+}
+
+namespace lldb_private {
+class ClangExpressionDeclMap;
+class IRExecutionUnit;
+class IRMemoryMap;
+}
+
+/// \class IRForTarget IRForTarget.h "lldb/Expression/IRForTarget.h"
+/// Transforms the IR for a function to run in the target
+///
+/// Once an expression has been parsed and converted to IR, it can run in two
+/// contexts: interpreted by LLDB as a DWARF location expression, or compiled
+/// by the JIT and inserted into the target process for execution.
+///
+/// IRForTarget makes the second possible, by applying a series of
+/// transformations to the IR which make it relocatable. These
+/// transformations are discussed in more detail next to their relevant
+/// functions.
+class IRForTarget : public llvm::ModulePass {
+public:
+ enum class LookupResult { Success, Fail, Ignore };
+
+ /// Constructor
+ ///
+ /// \param[in] decl_map
+ /// The list of externally-referenced variables for the expression,
+ /// for use in looking up globals and allocating the argument
+ /// struct. See the documentation for ClangExpressionDeclMap.
+ ///
+ /// \param[in] resolve_vars
+ /// True if the external variable references (including persistent
+ /// variables) should be resolved. If not, only external functions
+ /// are resolved.
+ ///
+ /// \param[in] execution_policy
+ /// Determines whether an IR interpreter can be used to statically
+ /// evaluate the expression.
+ ///
+ /// \param[in] const_result
+ /// This variable is populated with the statically-computed result
+ /// of the function, if it has no side-effects and the result can
+ /// be computed statically.
+ ///
+ /// \param[in] execution_unit
+ /// The holder for raw data associated with the expression.
+ ///
+ /// \param[in] error_stream
+ /// If non-NULL, a stream on which errors can be printed.
+ ///
+ /// \param[in] func_name
+ /// The name of the function to prepare for execution in the target.
+ IRForTarget(lldb_private::ClangExpressionDeclMap *decl_map, bool resolve_vars,
+ lldb_private::IRExecutionUnit &execution_unit,
+ lldb_private::Stream &error_stream,
+ const char *func_name = "$__lldb_expr");
+
+ /// Destructor
+ ~IRForTarget() override;
+
+ /// Run this IR transformer on a single module
+ ///
+ /// Implementation of the llvm::ModulePass::runOnModule() function.
+ ///
+ /// \param[in] llvm_module
+ /// The module to run on. This module is searched for the function
+ /// $__lldb_expr, and that function is passed to the passes one by
+ /// one.
+ ///
+ /// \param[in] interpreter_error
+ /// An error. If the expression fails to be interpreted, this error
+ /// is set to a reason why.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool runOnModule(llvm::Module &llvm_module) override;
+
+ /// Interface stub
+ ///
+ /// Implementation of the llvm::ModulePass::assignPassManager() function.
+ void assignPassManager(llvm::PMStack &pass_mgr_stack,
+ llvm::PassManagerType pass_mgr_type =
+ llvm::PMT_ModulePassManager) override;
+
+ /// Returns PMT_ModulePassManager
+ ///
+ /// Implementation of the llvm::ModulePass::getPotentialPassManagerType()
+ /// function.
+ llvm::PassManagerType getPotentialPassManagerType() const override;
+
+private:
+ /// Ensures that the current function's linkage is set to external.
+ /// Otherwise the JIT may not return an address for it.
+ ///
+ /// \param[in] llvm_function
+ /// The function whose linkage is to be fixed.
+ ///
+ /// \return
+ /// True on success; false otherwise.
+ bool FixFunctionLinkage(llvm::Function &llvm_function);
+
+ /// A module-level pass to replace all function pointers with their
+ /// integer equivalents.
+
+ /// The top-level pass implementation
+ ///
+ /// \param[in] llvm_module
+ /// The module currently being processed.
+ ///
+ /// \param[in] llvm_function
+ /// The function currently being processed.
+ ///
+ /// \return
+ /// True on success; false otherwise.
+ bool HasSideEffects(llvm::Function &llvm_function);
+
+ /// A function-level pass to check whether the function has side
+ /// effects.
+
+ /// Get the address of a function, and a location to put the complete Value
+ /// of the function if one is available.
+ ///
+ /// \param[in] function
+ /// The function to find the location of.
+ ///
+ /// \param[out] ptr
+ /// The location of the function in the target.
+ ///
+ /// \param[out] name
+ /// The resolved name of the function (matters for intrinsics).
+ ///
+ /// \param[out] value_ptr
+ /// A variable to put the function's completed Value* in, or NULL
+ /// if the Value* shouldn't be stored anywhere.
+ ///
+ /// \return
+ /// The pointer.
+ LookupResult GetFunctionAddress(llvm::Function *function, uint64_t &ptr,
+ lldb_private::ConstString &name,
+ llvm::Constant **&value_ptr);
+
+ /// A function-level pass to take the generated global value
+ /// $__lldb_expr_result and make it into a persistent variable. Also see
+ /// ASTResultSynthesizer.
+
+ /// Find the NamedDecl corresponding to a Value. This interface is exposed
+ /// for the IR interpreter.
+ ///
+ /// \param[in] module
+ /// The module containing metadata to search
+ ///
+ /// \param[in] global
+ /// The global entity to search for
+ ///
+ /// \return
+ /// The corresponding variable declaration
+public:
+ static clang::NamedDecl *DeclForGlobal(const llvm::GlobalValue *global_val,
+ llvm::Module *module);
+
+private:
+ clang::NamedDecl *DeclForGlobal(llvm::GlobalValue *global);
+
+ /// Set the constant result variable m_const_result to the provided
+ /// constant, assuming it can be evaluated. The result variable will be
+ /// reset to NULL later if the expression has side effects.
+ ///
+ /// \param[in] initializer
+ /// The constant initializer for the variable.
+ ///
+ /// \param[in] name
+ /// The name of the result variable.
+ ///
+ /// \param[in] type
+ /// The Clang type of the result variable.
+ void MaybeSetConstantResult(llvm::Constant *initializer,
+ lldb_private::ConstString name,
+ lldb_private::TypeFromParser type);
+
+ /// If the IR represents a cast of a variable, set m_const_result to the
+ /// result of the cast. The result variable will be reset to
+ /// NULL latger if the expression has side effects.
+ ///
+ /// \param[in] type
+ /// The Clang type of the result variable.
+ void MaybeSetCastResult(lldb_private::TypeFromParser type);
+
+ /// The top-level pass implementation
+ ///
+ /// \param[in] llvm_function
+ /// The function currently being processed.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool CreateResultVariable(llvm::Function &llvm_function);
+
+ /// A module-level pass to find Objective-C constant strings and
+ /// transform them to calls to CFStringCreateWithBytes.
+
+ /// Rewrite a single Objective-C constant string.
+ ///
+ /// \param[in] NSStr
+ /// The constant NSString to be transformed
+ ///
+ /// \param[in] CStr
+ /// The constant C string inside the NSString. This will be
+ /// passed as the bytes argument to CFStringCreateWithBytes.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool RewriteObjCConstString(llvm::GlobalVariable *NSStr,
+ llvm::GlobalVariable *CStr);
+
+ /// The top-level pass implementation
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool RewriteObjCConstStrings();
+
+ /// A basic block-level pass to find all Objective-C method calls and
+ /// rewrite them to use sel_registerName instead of statically allocated
+ /// selectors. The reason is that the selectors are created on the
+ /// assumption that the Objective-C runtime will scan the appropriate
+ /// section and prepare them. This doesn't happen when code is copied into
+ /// the target, though, and there's no easy way to induce the runtime to
+ /// scan them. So instead we get our selectors from sel_registerName.
+
+ /// Replace a single selector reference
+ ///
+ /// \param[in] selector_load
+ /// The load of the statically-allocated selector.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool RewriteObjCSelector(llvm::Instruction *selector_load);
+
+ /// The top-level pass implementation
+ ///
+ /// \param[in] basic_block
+ /// The basic block currently being processed.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool RewriteObjCSelectors(llvm::BasicBlock &basic_block);
+
+ /// A basic block-level pass to find all Objective-C class references that
+ /// use the old-style Objective-C runtime and rewrite them to use
+ /// class_getClass instead of statically allocated class references.
+
+ /// Replace a single old-style class reference
+ ///
+ /// \param[in] selector_load
+ /// The load of the statically-allocated selector.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool RewriteObjCClassReference(llvm::Instruction *class_load);
+
+ /// The top-level pass implementation
+ ///
+ /// \param[in] basic_block
+ /// The basic block currently being processed.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool RewriteObjCClassReferences(llvm::BasicBlock &basic_block);
+
+ /// A basic block-level pass to find all newly-declared persistent
+ /// variables and register them with the ClangExprDeclMap. This allows them
+ /// to be materialized and dematerialized like normal external variables.
+ /// Before transformation, these persistent variables look like normal
+ /// locals, so they have an allocation. This pass excises these allocations
+ /// and makes references look like external references where they will be
+ /// resolved -- like all other external references -- by ResolveExternals().
+
+ /// Handle a single allocation of a persistent variable
+ ///
+ /// \param[in] persistent_alloc
+ /// The allocation of the persistent variable.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool RewritePersistentAlloc(llvm::Instruction *persistent_alloc);
+
+ /// The top-level pass implementation
+ ///
+ /// \param[in] basic_block
+ /// The basic block currently being processed.
+ bool RewritePersistentAllocs(llvm::BasicBlock &basic_block);
+
+ /// A function-level pass to find all external variables and functions
+ /// used in the IR. Each found external variable is added to the struct,
+ /// and each external function is resolved in place, its call replaced with
+ /// a call to a function pointer whose value is the address of the function
+ /// in the target process.
+
+ /// Write an initializer to a memory array of assumed sufficient size.
+ ///
+ /// \param[in] data
+ /// A pointer to the data to write to.
+ ///
+ /// \param[in] initializer
+ /// The initializer itself.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool MaterializeInitializer(uint8_t *data, llvm::Constant *initializer);
+
+ /// Move an internal variable into the static allocation section.
+ ///
+ /// \param[in] global_variable
+ /// The variable.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool MaterializeInternalVariable(llvm::GlobalVariable *global_variable);
+
+ /// Handle a single externally-defined variable
+ ///
+ /// \param[in] value
+ /// The variable.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool MaybeHandleVariable(llvm::Value *value);
+
+ /// Handle a single externally-defined symbol
+ ///
+ /// \param[in] symbol
+ /// The symbol.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool HandleSymbol(llvm::Value *symbol);
+
+ /// Handle a single externally-defined Objective-C class
+ ///
+ /// \param[in] classlist_reference
+ /// The reference, usually "01L_OBJC_CLASSLIST_REFERENCES_$_n"
+ /// where n (if present) is an index.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool HandleObjCClass(llvm::Value *classlist_reference);
+
+ /// Handle all the arguments to a function call
+ ///
+ /// \param[in] C
+ /// The call instruction.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool MaybeHandleCallArguments(llvm::CallInst *call_inst);
+
+ /// Resolve variable references in calls to external functions
+ ///
+ /// \param[in] basic_block
+ /// The basic block currently being processed.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool ResolveCalls(llvm::BasicBlock &basic_block);
+
+ /// Remove calls to __cxa_atexit, which should never be generated by
+ /// expressions.
+ ///
+ /// \param[in] call_inst
+ /// The call instruction.
+ ///
+ /// \return
+ /// True if the scan was successful; false if some operation
+ /// failed
+ bool RemoveCXAAtExit(llvm::BasicBlock &basic_block);
+
+ /// The top-level pass implementation
+ ///
+ /// \param[in] basic_block
+ /// The function currently being processed.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool ResolveExternals(llvm::Function &llvm_function);
+
+ /// A basic block-level pass to excise guard variables from the code.
+ /// The result for the function is passed through Clang as a static
+ /// variable. Static variables normally have guard variables to ensure that
+ /// they are only initialized once.
+
+ /// Rewrite a load to a guard variable to return constant 0.
+ ///
+ /// \param[in] guard_load
+ /// The load instruction to zero out.
+ void TurnGuardLoadIntoZero(llvm::Instruction *guard_load);
+
+ /// The top-level pass implementation
+ ///
+ /// \param[in] basic_block
+ /// The basic block currently being processed.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool RemoveGuards(llvm::BasicBlock &basic_block);
+
+ /// A function-level pass to make all external variable references
+ /// point at the correct offsets from the void* passed into the function.
+ /// ClangExpressionDeclMap::DoStructLayout() must be called beforehand, so
+ /// that the offsets are valid.
+
+ /// The top-level pass implementation
+ ///
+ /// \param[in] llvm_function
+ /// The function currently being processed.
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool ReplaceVariables(llvm::Function &llvm_function);
+
+ /// Flags
+ bool m_resolve_vars; ///< True if external variable references and persistent
+ ///variable references should be resolved
+ lldb_private::ConstString
+ m_func_name; ///< The name of the function to translate
+ lldb_private::ConstString
+ m_result_name; ///< The name of the result variable ($0, $1, ...)
+ lldb_private::TypeFromParser
+ m_result_type; ///< The type of the result variable.
+ llvm::Module *m_module; ///< The module being processed, or NULL if that has
+ ///not been determined yet.
+ std::unique_ptr<llvm::DataLayout> m_target_data; ///< The target data for the
+ ///module being processed, or
+ ///NULL if there is no
+ ///module.
+ lldb_private::ClangExpressionDeclMap
+ *m_decl_map; ///< The DeclMap containing the Decls
+ llvm::FunctionCallee
+ m_CFStringCreateWithBytes; ///< The address of the function
+ /// CFStringCreateWithBytes, cast to the
+ /// appropriate function pointer type
+ llvm::FunctionCallee m_sel_registerName; ///< The address of the function
+ /// sel_registerName, cast to the
+ /// appropriate function pointer type
+ llvm::FunctionCallee m_objc_getClass; ///< The address of the function
+ /// objc_getClass, cast to the
+ /// appropriate function pointer type
+ llvm::IntegerType
+ *m_intptr_ty; ///< The type of an integer large enough to hold a pointer.
+ lldb_private::Stream
+ &m_error_stream; ///< The stream on which errors should be printed
+ lldb_private::IRExecutionUnit &
+ m_execution_unit; ///< The execution unit containing the IR being created.
+
+ llvm::StoreInst *m_result_store; ///< If non-NULL, the store instruction that
+ ///writes to the result variable. If
+ /// m_has_side_effects is true, this is
+ /// NULL.
+ bool m_result_is_pointer; ///< True if the function's result in the AST is a
+ ///pointer (see comments in
+ /// ASTResultSynthesizer::SynthesizeBodyResult)
+
+ llvm::GlobalVariable *m_reloc_placeholder; ///< A placeholder that will be
+ ///replaced by a pointer to the
+ ///final
+ /// location of the static allocation.
+
+ /// UnfoldConstant operates on a constant [Old] which has just been replaced
+ /// with a value [New]. We assume that new_value has been properly placed
+ /// early in the function, in front of the first instruction in the entry
+ /// basic block [FirstEntryInstruction].
+ ///
+ /// UnfoldConstant reads through the uses of Old and replaces Old in those
+ /// uses with New. Where those uses are constants, the function generates
+ /// new instructions to compute the result of the new, non-constant
+ /// expression and places them before FirstEntryInstruction. These
+ /// instructions replace the constant uses, so UnfoldConstant calls itself
+ /// recursively for those.
+ ///
+ /// \param[in] llvm_function
+ /// The function currently being processed.
+ ///
+ /// \return
+ /// True on success; false otherwise
+
+ class FunctionValueCache {
+ public:
+ typedef std::function<llvm::Value *(llvm::Function *)> Maker;
+
+ FunctionValueCache(Maker const &maker);
+ ~FunctionValueCache();
+ llvm::Value *GetValue(llvm::Function *function);
+
+ private:
+ Maker const m_maker;
+ typedef std::map<llvm::Function *, llvm::Value *> FunctionValueMap;
+ FunctionValueMap m_values;
+ };
+
+ FunctionValueCache m_entry_instruction_finder;
+
+ static bool UnfoldConstant(llvm::Constant *old_constant,
+ llvm::Function *llvm_function,
+ FunctionValueCache &value_maker,
+ FunctionValueCache &entry_instruction_finder,
+ lldb_private::Stream &error_stream);
+
+ /// Construct a reference to m_reloc_placeholder with a given type and
+ /// offset. This typically happens after inserting data into
+ /// m_data_allocator.
+ ///
+ /// \param[in] type
+ /// The type of the value being loaded.
+ ///
+ /// \param[in] offset
+ /// The offset of the value from the base of m_data_allocator.
+ ///
+ /// \return
+ /// The Constant for the reference, usually a ConstantExpr.
+ llvm::Constant *BuildRelocation(llvm::Type *type, uint64_t offset);
+
+ /// Commit the allocation in m_data_allocator and use its final location to
+ /// replace m_reloc_placeholder.
+ ///
+ /// \param[in] module
+ /// The module that m_data_allocator resides in
+ ///
+ /// \return
+ /// True on success; false otherwise
+ bool CompleteDataAllocation();
+};
+
+#endif // liblldb_IRForTarget_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ModuleDependencyCollector.h b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ModuleDependencyCollector.h
new file mode 100644
index 000000000000..0e959f86fd2a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ModuleDependencyCollector.h
@@ -0,0 +1,38 @@
+//===-- ModuleDependencyCollector.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 liblldb_ModuleDependencyCollector_h_
+#define liblldb_ModuleDependencyCollector_h_
+
+#include "lldb/Utility/FileCollector.h"
+#include "clang/Frontend/Utils.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace lldb_private {
+class ModuleDependencyCollectorAdaptor
+ : public clang::ModuleDependencyCollector {
+public:
+ ModuleDependencyCollectorAdaptor(FileCollector &file_collector)
+ : clang::ModuleDependencyCollector(""), m_file_collector(file_collector) {
+ }
+
+ void addFile(llvm::StringRef Filename,
+ llvm::StringRef FileDst = {}) override {
+ m_file_collector.AddFile(Filename);
+ }
+
+ bool insertSeen(llvm::StringRef Filename) override { return false; }
+ void addFileMapping(llvm::StringRef VPath, llvm::StringRef RPath) override {}
+ void writeFileMap() override {}
+
+private:
+ FileCollector &m_file_collector;
+};
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp
new file mode 100644
index 000000000000..6323889c2e09
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp
@@ -0,0 +1,14512 @@
+//===-- EmulateInstructionARM.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 <stdlib.h>
+
+#include "EmulateInstructionARM.h"
+#include "EmulationStateARM.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Interpreter/OptionValueArray.h"
+#include "lldb/Interpreter/OptionValueDictionary.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Stream.h"
+
+#include "Plugins/Process/Utility/ARMDefines.h"
+#include "Plugins/Process/Utility/ARMUtils.h"
+#include "Utility/ARM_DWARF_Registers.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/MathExtras.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Convenient macro definitions.
+#define APSR_C Bit32(m_opcode_cpsr, CPSR_C_POS)
+#define APSR_V Bit32(m_opcode_cpsr, CPSR_V_POS)
+
+#define AlignPC(pc_val) (pc_val & 0xFFFFFFFC)
+
+//
+// ITSession implementation
+//
+
+static bool GetARMDWARFRegisterInfo(unsigned reg_num, RegisterInfo &reg_info) {
+ ::memset(&reg_info, 0, sizeof(RegisterInfo));
+ ::memset(reg_info.kinds, LLDB_INVALID_REGNUM, sizeof(reg_info.kinds));
+
+ if (reg_num >= dwarf_q0 && reg_num <= dwarf_q15) {
+ reg_info.byte_size = 16;
+ reg_info.format = eFormatVectorOfUInt8;
+ reg_info.encoding = eEncodingVector;
+ }
+
+ if (reg_num >= dwarf_d0 && reg_num <= dwarf_d31) {
+ reg_info.byte_size = 8;
+ reg_info.format = eFormatFloat;
+ reg_info.encoding = eEncodingIEEE754;
+ } else if (reg_num >= dwarf_s0 && reg_num <= dwarf_s31) {
+ reg_info.byte_size = 4;
+ reg_info.format = eFormatFloat;
+ reg_info.encoding = eEncodingIEEE754;
+ } else if (reg_num >= dwarf_f0 && reg_num <= dwarf_f7) {
+ reg_info.byte_size = 12;
+ reg_info.format = eFormatFloat;
+ reg_info.encoding = eEncodingIEEE754;
+ } else {
+ reg_info.byte_size = 4;
+ reg_info.format = eFormatHex;
+ reg_info.encoding = eEncodingUint;
+ }
+
+ reg_info.kinds[eRegisterKindDWARF] = reg_num;
+
+ switch (reg_num) {
+ case dwarf_r0:
+ reg_info.name = "r0";
+ break;
+ case dwarf_r1:
+ reg_info.name = "r1";
+ break;
+ case dwarf_r2:
+ reg_info.name = "r2";
+ break;
+ case dwarf_r3:
+ reg_info.name = "r3";
+ break;
+ case dwarf_r4:
+ reg_info.name = "r4";
+ break;
+ case dwarf_r5:
+ reg_info.name = "r5";
+ break;
+ case dwarf_r6:
+ reg_info.name = "r6";
+ break;
+ case dwarf_r7:
+ reg_info.name = "r7";
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP;
+ break;
+ case dwarf_r8:
+ reg_info.name = "r8";
+ break;
+ case dwarf_r9:
+ reg_info.name = "r9";
+ break;
+ case dwarf_r10:
+ reg_info.name = "r10";
+ break;
+ case dwarf_r11:
+ reg_info.name = "r11";
+ break;
+ case dwarf_r12:
+ reg_info.name = "r12";
+ break;
+ case dwarf_sp:
+ reg_info.name = "sp";
+ reg_info.alt_name = "r13";
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP;
+ break;
+ case dwarf_lr:
+ reg_info.name = "lr";
+ reg_info.alt_name = "r14";
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA;
+ break;
+ case dwarf_pc:
+ reg_info.name = "pc";
+ reg_info.alt_name = "r15";
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC;
+ break;
+ case dwarf_cpsr:
+ reg_info.name = "cpsr";
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS;
+ break;
+
+ case dwarf_s0:
+ reg_info.name = "s0";
+ break;
+ case dwarf_s1:
+ reg_info.name = "s1";
+ break;
+ case dwarf_s2:
+ reg_info.name = "s2";
+ break;
+ case dwarf_s3:
+ reg_info.name = "s3";
+ break;
+ case dwarf_s4:
+ reg_info.name = "s4";
+ break;
+ case dwarf_s5:
+ reg_info.name = "s5";
+ break;
+ case dwarf_s6:
+ reg_info.name = "s6";
+ break;
+ case dwarf_s7:
+ reg_info.name = "s7";
+ break;
+ case dwarf_s8:
+ reg_info.name = "s8";
+ break;
+ case dwarf_s9:
+ reg_info.name = "s9";
+ break;
+ case dwarf_s10:
+ reg_info.name = "s10";
+ break;
+ case dwarf_s11:
+ reg_info.name = "s11";
+ break;
+ case dwarf_s12:
+ reg_info.name = "s12";
+ break;
+ case dwarf_s13:
+ reg_info.name = "s13";
+ break;
+ case dwarf_s14:
+ reg_info.name = "s14";
+ break;
+ case dwarf_s15:
+ reg_info.name = "s15";
+ break;
+ case dwarf_s16:
+ reg_info.name = "s16";
+ break;
+ case dwarf_s17:
+ reg_info.name = "s17";
+ break;
+ case dwarf_s18:
+ reg_info.name = "s18";
+ break;
+ case dwarf_s19:
+ reg_info.name = "s19";
+ break;
+ case dwarf_s20:
+ reg_info.name = "s20";
+ break;
+ case dwarf_s21:
+ reg_info.name = "s21";
+ break;
+ case dwarf_s22:
+ reg_info.name = "s22";
+ break;
+ case dwarf_s23:
+ reg_info.name = "s23";
+ break;
+ case dwarf_s24:
+ reg_info.name = "s24";
+ break;
+ case dwarf_s25:
+ reg_info.name = "s25";
+ break;
+ case dwarf_s26:
+ reg_info.name = "s26";
+ break;
+ case dwarf_s27:
+ reg_info.name = "s27";
+ break;
+ case dwarf_s28:
+ reg_info.name = "s28";
+ break;
+ case dwarf_s29:
+ reg_info.name = "s29";
+ break;
+ case dwarf_s30:
+ reg_info.name = "s30";
+ break;
+ case dwarf_s31:
+ reg_info.name = "s31";
+ break;
+
+ // FPA Registers 0-7
+ case dwarf_f0:
+ reg_info.name = "f0";
+ break;
+ case dwarf_f1:
+ reg_info.name = "f1";
+ break;
+ case dwarf_f2:
+ reg_info.name = "f2";
+ break;
+ case dwarf_f3:
+ reg_info.name = "f3";
+ break;
+ case dwarf_f4:
+ reg_info.name = "f4";
+ break;
+ case dwarf_f5:
+ reg_info.name = "f5";
+ break;
+ case dwarf_f6:
+ reg_info.name = "f6";
+ break;
+ case dwarf_f7:
+ reg_info.name = "f7";
+ break;
+
+ // Intel wireless MMX general purpose registers 0 - 7 XScale accumulator
+ // register 0 - 7 (they do overlap with wCGR0 - wCGR7)
+ case dwarf_wCGR0:
+ reg_info.name = "wCGR0/ACC0";
+ break;
+ case dwarf_wCGR1:
+ reg_info.name = "wCGR1/ACC1";
+ break;
+ case dwarf_wCGR2:
+ reg_info.name = "wCGR2/ACC2";
+ break;
+ case dwarf_wCGR3:
+ reg_info.name = "wCGR3/ACC3";
+ break;
+ case dwarf_wCGR4:
+ reg_info.name = "wCGR4/ACC4";
+ break;
+ case dwarf_wCGR5:
+ reg_info.name = "wCGR5/ACC5";
+ break;
+ case dwarf_wCGR6:
+ reg_info.name = "wCGR6/ACC6";
+ break;
+ case dwarf_wCGR7:
+ reg_info.name = "wCGR7/ACC7";
+ break;
+
+ // Intel wireless MMX data registers 0 - 15
+ case dwarf_wR0:
+ reg_info.name = "wR0";
+ break;
+ case dwarf_wR1:
+ reg_info.name = "wR1";
+ break;
+ case dwarf_wR2:
+ reg_info.name = "wR2";
+ break;
+ case dwarf_wR3:
+ reg_info.name = "wR3";
+ break;
+ case dwarf_wR4:
+ reg_info.name = "wR4";
+ break;
+ case dwarf_wR5:
+ reg_info.name = "wR5";
+ break;
+ case dwarf_wR6:
+ reg_info.name = "wR6";
+ break;
+ case dwarf_wR7:
+ reg_info.name = "wR7";
+ break;
+ case dwarf_wR8:
+ reg_info.name = "wR8";
+ break;
+ case dwarf_wR9:
+ reg_info.name = "wR9";
+ break;
+ case dwarf_wR10:
+ reg_info.name = "wR10";
+ break;
+ case dwarf_wR11:
+ reg_info.name = "wR11";
+ break;
+ case dwarf_wR12:
+ reg_info.name = "wR12";
+ break;
+ case dwarf_wR13:
+ reg_info.name = "wR13";
+ break;
+ case dwarf_wR14:
+ reg_info.name = "wR14";
+ break;
+ case dwarf_wR15:
+ reg_info.name = "wR15";
+ break;
+
+ case dwarf_spsr:
+ reg_info.name = "spsr";
+ break;
+ case dwarf_spsr_fiq:
+ reg_info.name = "spsr_fiq";
+ break;
+ case dwarf_spsr_irq:
+ reg_info.name = "spsr_irq";
+ break;
+ case dwarf_spsr_abt:
+ reg_info.name = "spsr_abt";
+ break;
+ case dwarf_spsr_und:
+ reg_info.name = "spsr_und";
+ break;
+ case dwarf_spsr_svc:
+ reg_info.name = "spsr_svc";
+ break;
+
+ case dwarf_r8_usr:
+ reg_info.name = "r8_usr";
+ break;
+ case dwarf_r9_usr:
+ reg_info.name = "r9_usr";
+ break;
+ case dwarf_r10_usr:
+ reg_info.name = "r10_usr";
+ break;
+ case dwarf_r11_usr:
+ reg_info.name = "r11_usr";
+ break;
+ case dwarf_r12_usr:
+ reg_info.name = "r12_usr";
+ break;
+ case dwarf_r13_usr:
+ reg_info.name = "r13_usr";
+ break;
+ case dwarf_r14_usr:
+ reg_info.name = "r14_usr";
+ break;
+ case dwarf_r8_fiq:
+ reg_info.name = "r8_fiq";
+ break;
+ case dwarf_r9_fiq:
+ reg_info.name = "r9_fiq";
+ break;
+ case dwarf_r10_fiq:
+ reg_info.name = "r10_fiq";
+ break;
+ case dwarf_r11_fiq:
+ reg_info.name = "r11_fiq";
+ break;
+ case dwarf_r12_fiq:
+ reg_info.name = "r12_fiq";
+ break;
+ case dwarf_r13_fiq:
+ reg_info.name = "r13_fiq";
+ break;
+ case dwarf_r14_fiq:
+ reg_info.name = "r14_fiq";
+ break;
+ case dwarf_r13_irq:
+ reg_info.name = "r13_irq";
+ break;
+ case dwarf_r14_irq:
+ reg_info.name = "r14_irq";
+ break;
+ case dwarf_r13_abt:
+ reg_info.name = "r13_abt";
+ break;
+ case dwarf_r14_abt:
+ reg_info.name = "r14_abt";
+ break;
+ case dwarf_r13_und:
+ reg_info.name = "r13_und";
+ break;
+ case dwarf_r14_und:
+ reg_info.name = "r14_und";
+ break;
+ case dwarf_r13_svc:
+ reg_info.name = "r13_svc";
+ break;
+ case dwarf_r14_svc:
+ reg_info.name = "r14_svc";
+ break;
+
+ // Intel wireless MMX control register in co-processor 0 - 7
+ case dwarf_wC0:
+ reg_info.name = "wC0";
+ break;
+ case dwarf_wC1:
+ reg_info.name = "wC1";
+ break;
+ case dwarf_wC2:
+ reg_info.name = "wC2";
+ break;
+ case dwarf_wC3:
+ reg_info.name = "wC3";
+ break;
+ case dwarf_wC4:
+ reg_info.name = "wC4";
+ break;
+ case dwarf_wC5:
+ reg_info.name = "wC5";
+ break;
+ case dwarf_wC6:
+ reg_info.name = "wC6";
+ break;
+ case dwarf_wC7:
+ reg_info.name = "wC7";
+ break;
+
+ // VFP-v3/Neon
+ case dwarf_d0:
+ reg_info.name = "d0";
+ break;
+ case dwarf_d1:
+ reg_info.name = "d1";
+ break;
+ case dwarf_d2:
+ reg_info.name = "d2";
+ break;
+ case dwarf_d3:
+ reg_info.name = "d3";
+ break;
+ case dwarf_d4:
+ reg_info.name = "d4";
+ break;
+ case dwarf_d5:
+ reg_info.name = "d5";
+ break;
+ case dwarf_d6:
+ reg_info.name = "d6";
+ break;
+ case dwarf_d7:
+ reg_info.name = "d7";
+ break;
+ case dwarf_d8:
+ reg_info.name = "d8";
+ break;
+ case dwarf_d9:
+ reg_info.name = "d9";
+ break;
+ case dwarf_d10:
+ reg_info.name = "d10";
+ break;
+ case dwarf_d11:
+ reg_info.name = "d11";
+ break;
+ case dwarf_d12:
+ reg_info.name = "d12";
+ break;
+ case dwarf_d13:
+ reg_info.name = "d13";
+ break;
+ case dwarf_d14:
+ reg_info.name = "d14";
+ break;
+ case dwarf_d15:
+ reg_info.name = "d15";
+ break;
+ case dwarf_d16:
+ reg_info.name = "d16";
+ break;
+ case dwarf_d17:
+ reg_info.name = "d17";
+ break;
+ case dwarf_d18:
+ reg_info.name = "d18";
+ break;
+ case dwarf_d19:
+ reg_info.name = "d19";
+ break;
+ case dwarf_d20:
+ reg_info.name = "d20";
+ break;
+ case dwarf_d21:
+ reg_info.name = "d21";
+ break;
+ case dwarf_d22:
+ reg_info.name = "d22";
+ break;
+ case dwarf_d23:
+ reg_info.name = "d23";
+ break;
+ case dwarf_d24:
+ reg_info.name = "d24";
+ break;
+ case dwarf_d25:
+ reg_info.name = "d25";
+ break;
+ case dwarf_d26:
+ reg_info.name = "d26";
+ break;
+ case dwarf_d27:
+ reg_info.name = "d27";
+ break;
+ case dwarf_d28:
+ reg_info.name = "d28";
+ break;
+ case dwarf_d29:
+ reg_info.name = "d29";
+ break;
+ case dwarf_d30:
+ reg_info.name = "d30";
+ break;
+ case dwarf_d31:
+ reg_info.name = "d31";
+ break;
+
+ // NEON 128-bit vector registers (overlays the d registers)
+ case dwarf_q0:
+ reg_info.name = "q0";
+ break;
+ case dwarf_q1:
+ reg_info.name = "q1";
+ break;
+ case dwarf_q2:
+ reg_info.name = "q2";
+ break;
+ case dwarf_q3:
+ reg_info.name = "q3";
+ break;
+ case dwarf_q4:
+ reg_info.name = "q4";
+ break;
+ case dwarf_q5:
+ reg_info.name = "q5";
+ break;
+ case dwarf_q6:
+ reg_info.name = "q6";
+ break;
+ case dwarf_q7:
+ reg_info.name = "q7";
+ break;
+ case dwarf_q8:
+ reg_info.name = "q8";
+ break;
+ case dwarf_q9:
+ reg_info.name = "q9";
+ break;
+ case dwarf_q10:
+ reg_info.name = "q10";
+ break;
+ case dwarf_q11:
+ reg_info.name = "q11";
+ break;
+ case dwarf_q12:
+ reg_info.name = "q12";
+ break;
+ case dwarf_q13:
+ reg_info.name = "q13";
+ break;
+ case dwarf_q14:
+ reg_info.name = "q14";
+ break;
+ case dwarf_q15:
+ reg_info.name = "q15";
+ break;
+
+ default:
+ return false;
+ }
+ return true;
+}
+
+// A8.6.50
+// Valid return values are {1, 2, 3, 4}, with 0 signifying an error condition.
+static uint32_t CountITSize(uint32_t ITMask) {
+ // First count the trailing zeros of the IT mask.
+ uint32_t TZ = llvm::countTrailingZeros(ITMask);
+ if (TZ > 3) {
+#ifdef LLDB_CONFIGURATION_DEBUG
+ printf("Encoding error: IT Mask '0000'\n");
+#endif
+ return 0;
+ }
+ return (4 - TZ);
+}
+
+// Init ITState. Note that at least one bit is always 1 in mask.
+bool ITSession::InitIT(uint32_t bits7_0) {
+ ITCounter = CountITSize(Bits32(bits7_0, 3, 0));
+ if (ITCounter == 0)
+ return false;
+
+ // A8.6.50 IT
+ unsigned short FirstCond = Bits32(bits7_0, 7, 4);
+ if (FirstCond == 0xF) {
+#ifdef LLDB_CONFIGURATION_DEBUG
+ printf("Encoding error: IT FirstCond '1111'\n");
+#endif
+ return false;
+ }
+ if (FirstCond == 0xE && ITCounter != 1) {
+#ifdef LLDB_CONFIGURATION_DEBUG
+ printf("Encoding error: IT FirstCond '1110' && Mask != '1000'\n");
+#endif
+ return false;
+ }
+
+ ITState = bits7_0;
+ return true;
+}
+
+// Update ITState if necessary.
+void ITSession::ITAdvance() {
+ // assert(ITCounter);
+ --ITCounter;
+ if (ITCounter == 0)
+ ITState = 0;
+ else {
+ unsigned short NewITState4_0 = Bits32(ITState, 4, 0) << 1;
+ SetBits32(ITState, 4, 0, NewITState4_0);
+ }
+}
+
+// Return true if we're inside an IT Block.
+bool ITSession::InITBlock() { return ITCounter != 0; }
+
+// Return true if we're the last instruction inside an IT Block.
+bool ITSession::LastInITBlock() { return ITCounter == 1; }
+
+// Get condition bits for the current thumb instruction.
+uint32_t ITSession::GetCond() {
+ if (InITBlock())
+ return Bits32(ITState, 7, 4);
+ else
+ return COND_AL;
+}
+
+// ARM constants used during decoding
+#define REG_RD 0
+#define LDM_REGLIST 1
+#define SP_REG 13
+#define LR_REG 14
+#define PC_REG 15
+#define PC_REGLIST_BIT 0x8000
+
+#define ARMv4 (1u << 0)
+#define ARMv4T (1u << 1)
+#define ARMv5T (1u << 2)
+#define ARMv5TE (1u << 3)
+#define ARMv5TEJ (1u << 4)
+#define ARMv6 (1u << 5)
+#define ARMv6K (1u << 6)
+#define ARMv6T2 (1u << 7)
+#define ARMv7 (1u << 8)
+#define ARMv7S (1u << 9)
+#define ARMv8 (1u << 10)
+#define ARMvAll (0xffffffffu)
+
+#define ARMV4T_ABOVE \
+ (ARMv4T | ARMv5T | ARMv5TE | ARMv5TEJ | ARMv6 | ARMv6K | ARMv6T2 | ARMv7 | \
+ ARMv7S | ARMv8)
+#define ARMV5_ABOVE \
+ (ARMv5T | ARMv5TE | ARMv5TEJ | ARMv6 | ARMv6K | ARMv6T2 | ARMv7 | ARMv7S | \
+ ARMv8)
+#define ARMV5TE_ABOVE \
+ (ARMv5TE | ARMv5TEJ | ARMv6 | ARMv6K | ARMv6T2 | ARMv7 | ARMv7S | ARMv8)
+#define ARMV5J_ABOVE \
+ (ARMv5TEJ | ARMv6 | ARMv6K | ARMv6T2 | ARMv7 | ARMv7S | ARMv8)
+#define ARMV6_ABOVE (ARMv6 | ARMv6K | ARMv6T2 | ARMv7 | ARMv7S | ARMv8)
+#define ARMV6T2_ABOVE (ARMv6T2 | ARMv7 | ARMv7S | ARMv8)
+#define ARMV7_ABOVE (ARMv7 | ARMv7S | ARMv8)
+
+#define No_VFP 0
+#define VFPv1 (1u << 1)
+#define VFPv2 (1u << 2)
+#define VFPv3 (1u << 3)
+#define AdvancedSIMD (1u << 4)
+
+#define VFPv1_ABOVE (VFPv1 | VFPv2 | VFPv3 | AdvancedSIMD)
+#define VFPv2_ABOVE (VFPv2 | VFPv3 | AdvancedSIMD)
+#define VFPv2v3 (VFPv2 | VFPv3)
+
+//
+// EmulateInstructionARM implementation
+//
+
+void EmulateInstructionARM::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void EmulateInstructionARM::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+ConstString EmulateInstructionARM::GetPluginNameStatic() {
+ static ConstString g_name("arm");
+ return g_name;
+}
+
+const char *EmulateInstructionARM::GetPluginDescriptionStatic() {
+ return "Emulate instructions for the ARM architecture.";
+}
+
+EmulateInstruction *
+EmulateInstructionARM::CreateInstance(const ArchSpec &arch,
+ InstructionType inst_type) {
+ if (EmulateInstructionARM::SupportsEmulatingInstructionsOfTypeStatic(
+ inst_type)) {
+ if (arch.GetTriple().getArch() == llvm::Triple::arm) {
+ std::unique_ptr<EmulateInstructionARM> emulate_insn_up(
+ new EmulateInstructionARM(arch));
+
+ if (emulate_insn_up)
+ return emulate_insn_up.release();
+ } else if (arch.GetTriple().getArch() == llvm::Triple::thumb) {
+ std::unique_ptr<EmulateInstructionARM> emulate_insn_up(
+ new EmulateInstructionARM(arch));
+
+ if (emulate_insn_up)
+ return emulate_insn_up.release();
+ }
+ }
+
+ return nullptr;
+}
+
+bool EmulateInstructionARM::SetTargetTriple(const ArchSpec &arch) {
+ if (arch.GetTriple().getArch() == llvm::Triple::arm)
+ return true;
+ else if (arch.GetTriple().getArch() == llvm::Triple::thumb)
+ return true;
+
+ return false;
+}
+
+// Write "bits (32) UNKNOWN" to memory address "address". Helper function for
+// many ARM instructions.
+bool EmulateInstructionARM::WriteBits32UnknownToMemory(addr_t address) {
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextWriteMemoryRandomBits;
+ context.SetNoArgs();
+
+ uint32_t random_data = rand();
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ return MemAWrite(context, address, random_data, addr_byte_size);
+}
+
+// Write "bits (32) UNKNOWN" to register n. Helper function for many ARM
+// instructions.
+bool EmulateInstructionARM::WriteBits32Unknown(int n) {
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextWriteRegisterRandomBits;
+ context.SetNoArgs();
+
+ bool success;
+ uint32_t data =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, data))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionARM::GetRegisterInfo(lldb::RegisterKind reg_kind,
+ uint32_t reg_num,
+ RegisterInfo &reg_info) {
+ if (reg_kind == eRegisterKindGeneric) {
+ switch (reg_num) {
+ case LLDB_REGNUM_GENERIC_PC:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_pc;
+ break;
+ case LLDB_REGNUM_GENERIC_SP:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_sp;
+ break;
+ case LLDB_REGNUM_GENERIC_FP:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_r7;
+ break;
+ case LLDB_REGNUM_GENERIC_RA:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_lr;
+ break;
+ case LLDB_REGNUM_GENERIC_FLAGS:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_cpsr;
+ break;
+ default:
+ return false;
+ }
+ }
+
+ if (reg_kind == eRegisterKindDWARF)
+ return GetARMDWARFRegisterInfo(reg_num, reg_info);
+ return false;
+}
+
+uint32_t EmulateInstructionARM::GetFramePointerRegisterNumber() const {
+ if (m_arch.GetTriple().isAndroid())
+ return LLDB_INVALID_REGNUM; // Don't use frame pointer on android
+ bool is_apple = false;
+ if (m_arch.GetTriple().getVendor() == llvm::Triple::Apple)
+ is_apple = true;
+ switch (m_arch.GetTriple().getOS()) {
+ case llvm::Triple::Darwin:
+ case llvm::Triple::MacOSX:
+ case llvm::Triple::IOS:
+ case llvm::Triple::TvOS:
+ case llvm::Triple::WatchOS:
+ // NEED_BRIDGEOS_TRIPLE case llvm::Triple::BridgeOS:
+ is_apple = true;
+ break;
+ default:
+ break;
+ }
+
+ /* On Apple iOS et al, the frame pointer register is always r7.
+ * Typically on other ARM systems, thumb code uses r7; arm code uses r11.
+ */
+
+ uint32_t fp_regnum = 11;
+
+ if (is_apple)
+ fp_regnum = 7;
+
+ if (m_opcode_mode == eModeThumb)
+ fp_regnum = 7;
+
+ return fp_regnum;
+}
+
+uint32_t EmulateInstructionARM::GetFramePointerDWARFRegisterNumber() const {
+ bool is_apple = false;
+ if (m_arch.GetTriple().getVendor() == llvm::Triple::Apple)
+ is_apple = true;
+ switch (m_arch.GetTriple().getOS()) {
+ case llvm::Triple::Darwin:
+ case llvm::Triple::MacOSX:
+ case llvm::Triple::IOS:
+ is_apple = true;
+ break;
+ default:
+ break;
+ }
+
+ /* On Apple iOS et al, the frame pointer register is always r7.
+ * Typically on other ARM systems, thumb code uses r7; arm code uses r11.
+ */
+
+ uint32_t fp_regnum = dwarf_r11;
+
+ if (is_apple)
+ fp_regnum = dwarf_r7;
+
+ if (m_opcode_mode == eModeThumb)
+ fp_regnum = dwarf_r7;
+
+ return fp_regnum;
+}
+
+// Push Multiple Registers stores multiple registers to the stack, storing to
+// consecutive memory locations ending just below the address in SP, and
+// updates
+// SP to point to the start of the stored data.
+bool EmulateInstructionARM::EmulatePUSH(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ NullCheckIfThumbEE(13);
+ address = SP - 4*BitCount(registers);
+
+ for (i = 0 to 14)
+ {
+ if (registers<i> == '1')
+ {
+ if i == 13 && i != LowestSetBit(registers) // Only possible for encoding A1
+ MemA[address,4] = bits(32) UNKNOWN;
+ else
+ MemA[address,4] = R[i];
+ address = address + 4;
+ }
+ }
+
+ if (registers<15> == '1') // Only possible for encoding A1 or A2
+ MemA[address,4] = PCStoreValue();
+
+ SP = SP - 4*BitCount(registers);
+ }
+#endif
+
+ bool success = false;
+ if (ConditionPassed(opcode)) {
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ const addr_t sp = ReadCoreReg(SP_REG, &success);
+ if (!success)
+ return false;
+ uint32_t registers = 0;
+ uint32_t Rt; // the source register
+ switch (encoding) {
+ case eEncodingT1:
+ registers = Bits32(opcode, 7, 0);
+ // The M bit represents LR.
+ if (Bit32(opcode, 8))
+ registers |= (1u << 14);
+ // if BitCount(registers) < 1 then UNPREDICTABLE;
+ if (BitCount(registers) < 1)
+ return false;
+ break;
+ case eEncodingT2:
+ // Ignore bits 15 & 13.
+ registers = Bits32(opcode, 15, 0) & ~0xa000;
+ // if BitCount(registers) < 2 then UNPREDICTABLE;
+ if (BitCount(registers) < 2)
+ return false;
+ break;
+ case eEncodingT3:
+ Rt = Bits32(opcode, 15, 12);
+ // if BadReg(t) then UNPREDICTABLE;
+ if (BadReg(Rt))
+ return false;
+ registers = (1u << Rt);
+ break;
+ case eEncodingA1:
+ registers = Bits32(opcode, 15, 0);
+ // Instead of return false, let's handle the following case as well,
+ // which amounts to pushing one reg onto the full descending stacks.
+ // if BitCount(register_list) < 2 then SEE STMDB / STMFD;
+ break;
+ case eEncodingA2:
+ Rt = Bits32(opcode, 15, 12);
+ // if t == 13 then UNPREDICTABLE;
+ if (Rt == dwarf_sp)
+ return false;
+ registers = (1u << Rt);
+ break;
+ default:
+ return false;
+ }
+ addr_t sp_offset = addr_byte_size * BitCount(registers);
+ addr_t addr = sp - sp_offset;
+ uint32_t i;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextPushRegisterOnStack;
+ RegisterInfo reg_info;
+ RegisterInfo sp_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
+ for (i = 0; i < 15; ++i) {
+ if (BitIsSet(registers, i)) {
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + i, reg_info);
+ context.SetRegisterToRegisterPlusOffset(reg_info, sp_reg, addr - sp);
+ uint32_t reg_value = ReadCoreReg(i, &success);
+ if (!success)
+ return false;
+ if (!MemAWrite(context, addr, reg_value, addr_byte_size))
+ return false;
+ addr += addr_byte_size;
+ }
+ }
+
+ if (BitIsSet(registers, 15)) {
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_pc, reg_info);
+ context.SetRegisterToRegisterPlusOffset(reg_info, sp_reg, addr - sp);
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+ if (!MemAWrite(context, addr, pc, addr_byte_size))
+ return false;
+ }
+
+ context.type = EmulateInstruction::eContextAdjustStackPointer;
+ context.SetImmediateSigned(-sp_offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_SP, sp - sp_offset))
+ return false;
+ }
+ return true;
+}
+
+// Pop Multiple Registers loads multiple registers from the stack, loading from
+// consecutive memory locations staring at the address in SP, and updates
+// SP to point just above the loaded data.
+bool EmulateInstructionARM::EmulatePOP(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations(); NullCheckIfThumbEE(13);
+ address = SP;
+ for i = 0 to 14
+ if registers<i> == '1' then
+ R[i] = if UnalignedAllowed then MemU[address,4] else MemA[address,4]; address = address + 4;
+ if registers<15> == '1' then
+ if UnalignedAllowed then
+ LoadWritePC(MemU[address,4]);
+ else
+ LoadWritePC(MemA[address,4]);
+ if registers<13> == '0' then SP = SP + 4*BitCount(registers);
+ if registers<13> == '1' then SP = bits(32) UNKNOWN;
+ }
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ const addr_t sp = ReadCoreReg(SP_REG, &success);
+ if (!success)
+ return false;
+ uint32_t registers = 0;
+ uint32_t Rt; // the destination register
+ switch (encoding) {
+ case eEncodingT1:
+ registers = Bits32(opcode, 7, 0);
+ // The P bit represents PC.
+ if (Bit32(opcode, 8))
+ registers |= (1u << 15);
+ // if BitCount(registers) < 1 then UNPREDICTABLE;
+ if (BitCount(registers) < 1)
+ return false;
+ break;
+ case eEncodingT2:
+ // Ignore bit 13.
+ registers = Bits32(opcode, 15, 0) & ~0x2000;
+ // if BitCount(registers) < 2 || (P == '1' && M == '1') then
+ // UNPREDICTABLE;
+ if (BitCount(registers) < 2 || (Bit32(opcode, 15) && Bit32(opcode, 14)))
+ return false;
+ // if registers<15> == '1' && InITBlock() && !LastInITBlock() then
+ // UNPREDICTABLE;
+ if (BitIsSet(registers, 15) && InITBlock() && !LastInITBlock())
+ return false;
+ break;
+ case eEncodingT3:
+ Rt = Bits32(opcode, 15, 12);
+ // if t == 13 || (t == 15 && InITBlock() && !LastInITBlock()) then
+ // UNPREDICTABLE;
+ if (Rt == 13)
+ return false;
+ if (Rt == 15 && InITBlock() && !LastInITBlock())
+ return false;
+ registers = (1u << Rt);
+ break;
+ case eEncodingA1:
+ registers = Bits32(opcode, 15, 0);
+ // Instead of return false, let's handle the following case as well,
+ // which amounts to popping one reg from the full descending stacks.
+ // if BitCount(register_list) < 2 then SEE LDM / LDMIA / LDMFD;
+
+ // if registers<13> == '1' && ArchVersion() >= 7 then UNPREDICTABLE;
+ if (BitIsSet(opcode, 13) && ArchVersion() >= ARMv7)
+ return false;
+ break;
+ case eEncodingA2:
+ Rt = Bits32(opcode, 15, 12);
+ // if t == 13 then UNPREDICTABLE;
+ if (Rt == dwarf_sp)
+ return false;
+ registers = (1u << Rt);
+ break;
+ default:
+ return false;
+ }
+ addr_t sp_offset = addr_byte_size * BitCount(registers);
+ addr_t addr = sp;
+ uint32_t i, data;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextPopRegisterOffStack;
+
+ RegisterInfo sp_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
+
+ for (i = 0; i < 15; ++i) {
+ if (BitIsSet(registers, i)) {
+ context.SetAddress(addr);
+ data = MemARead(context, addr, 4, 0, &success);
+ if (!success)
+ return false;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + i,
+ data))
+ return false;
+ addr += addr_byte_size;
+ }
+ }
+
+ if (BitIsSet(registers, 15)) {
+ context.SetRegisterPlusOffset(sp_reg, addr - sp);
+ data = MemARead(context, addr, 4, 0, &success);
+ if (!success)
+ return false;
+ // In ARMv5T and above, this is an interworking branch.
+ if (!LoadWritePC(context, data))
+ return false;
+ // addr += addr_byte_size;
+ }
+
+ context.type = EmulateInstruction::eContextAdjustStackPointer;
+ context.SetImmediateSigned(sp_offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_SP, sp + sp_offset))
+ return false;
+ }
+ return true;
+}
+
+// Set r7 or ip to point to saved value residing within the stack.
+// ADD (SP plus immediate)
+bool EmulateInstructionARM::EmulateADDRdSPImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(SP, imm32, '0');
+ if d == 15 then
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+ }
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ const addr_t sp = ReadCoreReg(SP_REG, &success);
+ if (!success)
+ return false;
+ uint32_t Rd; // the destination register
+ uint32_t imm32;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = 7;
+ imm32 = Bits32(opcode, 7, 0) << 2; // imm32 = ZeroExtend(imm8:'00', 32)
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+ break;
+ default:
+ return false;
+ }
+ addr_t sp_offset = imm32;
+ addr_t addr = sp + sp_offset; // a pointer to the stack area
+
+ EmulateInstruction::Context context;
+ if (Rd == GetFramePointerRegisterNumber())
+ context.type = eContextSetFramePointer;
+ else
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+ RegisterInfo sp_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
+ context.SetRegisterPlusOffset(sp_reg, sp_offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + Rd,
+ addr))
+ return false;
+ }
+ return true;
+}
+
+// Set r7 or ip to the current stack pointer.
+// MOV (register)
+bool EmulateInstructionARM::EmulateMOVRdSP(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ result = R[m];
+ if d == 15 then
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ // APSR.C unchanged
+ // APSR.V unchanged
+ }
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ const addr_t sp = ReadCoreReg(SP_REG, &success);
+ if (!success)
+ return false;
+ uint32_t Rd; // the destination register
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = 7;
+ break;
+ case eEncodingA1:
+ Rd = 12;
+ break;
+ default:
+ return false;
+ }
+
+ EmulateInstruction::Context context;
+ if (Rd == GetFramePointerRegisterNumber())
+ context.type = EmulateInstruction::eContextSetFramePointer;
+ else
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+ RegisterInfo sp_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
+ context.SetRegisterPlusOffset(sp_reg, 0);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + Rd, sp))
+ return false;
+ }
+ return true;
+}
+
+// Move from high register (r8-r15) to low register (r0-r7).
+// MOV (register)
+bool EmulateInstructionARM::EmulateMOVLowHigh(const uint32_t opcode,
+ const ARMEncoding encoding) {
+ return EmulateMOVRdRm(opcode, encoding);
+}
+
+// Move from register to register.
+// MOV (register)
+bool EmulateInstructionARM::EmulateMOVRdRm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ result = R[m];
+ if d == 15 then
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ // APSR.C unchanged
+ // APSR.V unchanged
+ }
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rm; // the source register
+ uint32_t Rd; // the destination register
+ bool setflags;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bit32(opcode, 7) << 3 | Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 6, 3);
+ setflags = false;
+ if (Rd == 15 && InITBlock() && !LastInITBlock())
+ return false;
+ break;
+ case eEncodingT2:
+ Rd = Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 5, 3);
+ setflags = true;
+ if (InITBlock())
+ return false;
+ break;
+ case eEncodingT3:
+ Rd = Bits32(opcode, 11, 8);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ // if setflags && (BadReg(d) || BadReg(m)) then UNPREDICTABLE;
+ if (setflags && (BadReg(Rd) || BadReg(Rm)))
+ return false;
+ // if !setflags && (d == 15 || m == 15 || (d == 13 && m == 13)) then
+ // UNPREDICTABLE;
+ if (!setflags && (Rd == 15 || Rm == 15 || (Rd == 13 && Rm == 13)))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+ uint32_t result = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ // The context specifies that Rm is to be moved into Rd.
+ EmulateInstruction::Context context;
+ if (Rd == 13)
+ context.type = EmulateInstruction::eContextAdjustStackPointer;
+ else
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg);
+ context.SetRegisterPlusOffset(dwarf_reg, 0);
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags))
+ return false;
+ }
+ return true;
+}
+
+// Move (immediate) writes an immediate value to the destination register. It
+// can optionally update the condition flags based on the value.
+// MOV (immediate)
+bool EmulateInstructionARM::EmulateMOVRdImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ result = imm32;
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+ }
+#endif
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd; // the destination register
+ uint32_t imm32; // the immediate value to be written to Rd
+ uint32_t carry =
+ 0; // the carry bit after ThumbExpandImm_C or ARMExpandImm_C.
+ // for setflags == false, this value is a don't care initialized to
+ // 0 to silence the static analyzer
+ bool setflags;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 10, 8);
+ setflags = !InITBlock();
+ imm32 = Bits32(opcode, 7, 0); // imm32 = ZeroExtend(imm8, 32)
+ carry = APSR_C;
+
+ break;
+
+ case eEncodingT2:
+ Rd = Bits32(opcode, 11, 8);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbExpandImm_C(opcode, APSR_C, carry);
+ if (BadReg(Rd))
+ return false;
+
+ break;
+
+ case eEncodingT3: {
+ // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(imm4:i:imm3:imm8,
+ // 32);
+ Rd = Bits32(opcode, 11, 8);
+ setflags = false;
+ uint32_t imm4 = Bits32(opcode, 19, 16);
+ uint32_t imm3 = Bits32(opcode, 14, 12);
+ uint32_t i = Bit32(opcode, 26);
+ uint32_t imm8 = Bits32(opcode, 7, 0);
+ imm32 = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8;
+
+ // if BadReg(d) then UNPREDICTABLE;
+ if (BadReg(Rd))
+ return false;
+ } break;
+
+ case eEncodingA1:
+ // d = UInt(Rd); setflags = (S == '1'); (imm32, carry) =
+ // ARMExpandImm_C(imm12, APSR.C);
+ Rd = Bits32(opcode, 15, 12);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ARMExpandImm_C(opcode, APSR_C, carry);
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if ((Rd == 15) && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+
+ break;
+
+ case eEncodingA2: {
+ // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(imm4:imm12, 32);
+ Rd = Bits32(opcode, 15, 12);
+ setflags = false;
+ uint32_t imm4 = Bits32(opcode, 19, 16);
+ uint32_t imm12 = Bits32(opcode, 11, 0);
+ imm32 = (imm4 << 12) | imm12;
+
+ // if d == 15 then UNPREDICTABLE;
+ if (Rd == 15)
+ return false;
+ } break;
+
+ default:
+ return false;
+ }
+ uint32_t result = imm32;
+
+ // The context specifies that an immediate is to be moved into Rd.
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+// MUL multiplies two register values. The least significant 32 bits of the
+// result are written to the destination
+// register. These 32 bits do not depend on whether the source register values
+// are considered to be signed values or unsigned values.
+//
+// Optionally, it can update the condition flags based on the result. In the
+// Thumb instruction set, this option is limited to only a few forms of the
+// instruction.
+bool EmulateInstructionARM::EmulateMUL(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ operand1 = SInt(R[n]); // operand1 = UInt(R[n]) produces the same final results
+ operand2 = SInt(R[m]); // operand2 = UInt(R[m]) produces the same final results
+ result = operand1 * operand2;
+ R[d] = result<31:0>;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ if ArchVersion() == 4 then
+ APSR.C = bit UNKNOWN;
+ // else APSR.C unchanged
+ // APSR.V always unchanged
+#endif
+
+ if (ConditionPassed(opcode)) {
+ uint32_t d;
+ uint32_t n;
+ uint32_t m;
+ bool setflags;
+
+ // EncodingSpecificOperations();
+ switch (encoding) {
+ case eEncodingT1:
+ // d = UInt(Rdm); n = UInt(Rn); m = UInt(Rdm); setflags = !InITBlock();
+ d = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ m = Bits32(opcode, 2, 0);
+ setflags = !InITBlock();
+
+ // if ArchVersion() < 6 && d == n then UNPREDICTABLE;
+ if ((ArchVersion() < ARMv6) && (d == n))
+ return false;
+
+ break;
+
+ case eEncodingT2:
+ // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = FALSE;
+ d = Bits32(opcode, 11, 8);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+ setflags = false;
+
+ // if BadReg(d) || BadReg(n) || BadReg(m) then UNPREDICTABLE;
+ if (BadReg(d) || BadReg(n) || BadReg(m))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = (S == '1');
+ d = Bits32(opcode, 19, 16);
+ n = Bits32(opcode, 3, 0);
+ m = Bits32(opcode, 11, 8);
+ setflags = BitIsSet(opcode, 20);
+
+ // if d == 15 || n == 15 || m == 15 then UNPREDICTABLE;
+ if ((d == 15) || (n == 15) || (m == 15))
+ return false;
+
+ // if ArchVersion() < 6 && d == n then UNPREDICTABLE;
+ if ((ArchVersion() < ARMv6) && (d == n))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ bool success = false;
+
+ // operand1 = SInt(R[n]); // operand1 = UInt(R[n]) produces the same final
+ // results
+ uint64_t operand1 =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ // operand2 = SInt(R[m]); // operand2 = UInt(R[m]) produces the same final
+ // results
+ uint64_t operand2 =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success);
+ if (!success)
+ return false;
+
+ // result = operand1 * operand2;
+ uint64_t result = operand1 * operand2;
+
+ // R[d] = result<31:0>;
+ RegisterInfo op1_reg;
+ RegisterInfo op2_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, op1_reg);
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, op2_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextArithmetic;
+ context.SetRegisterRegisterOperands(op1_reg, op2_reg);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d,
+ (0x0000ffff & result)))
+ return false;
+
+ // if setflags then
+ if (setflags) {
+ // APSR.N = result<31>;
+ // APSR.Z = IsZeroBit(result);
+ m_new_inst_cpsr = m_opcode_cpsr;
+ SetBit32(m_new_inst_cpsr, CPSR_N_POS, Bit32(result, 31));
+ SetBit32(m_new_inst_cpsr, CPSR_Z_POS, result == 0 ? 1 : 0);
+ if (m_new_inst_cpsr != m_opcode_cpsr) {
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_FLAGS, m_new_inst_cpsr))
+ return false;
+ }
+
+ // if ArchVersion() == 4 then
+ // APSR.C = bit UNKNOWN;
+ }
+ }
+ return true;
+}
+
+// Bitwise NOT (immediate) writes the bitwise inverse of an immediate value to
+// the destination register. It can optionally update the condition flags based
+// on the value.
+bool EmulateInstructionARM::EmulateMVNImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ result = NOT(imm32);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+ }
+#endif
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd; // the destination register
+ uint32_t imm32; // the output after ThumbExpandImm_C or ARMExpandImm_C
+ uint32_t carry; // the carry bit after ThumbExpandImm_C or ARMExpandImm_C
+ bool setflags;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 11, 8);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbExpandImm_C(opcode, APSR_C, carry);
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ARMExpandImm_C(opcode, APSR_C, carry);
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+ uint32_t result = ~imm32;
+
+ // The context specifies that an immediate is to be moved into Rd.
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+// Bitwise NOT (register) writes the bitwise inverse of a register value to the
+// destination register. It can optionally update the condition flags based on
+// the result.
+bool EmulateInstructionARM::EmulateMVNReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C);
+ result = NOT(shifted);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+ }
+#endif
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rm; // the source register
+ uint32_t Rd; // the destination register
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ bool setflags;
+ uint32_t carry; // the carry bit after the shift operation
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 5, 3);
+ setflags = !InITBlock();
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ if (InITBlock())
+ return false;
+ break;
+ case eEncodingT2:
+ Rd = Bits32(opcode, 11, 8);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+ // if (BadReg(d) || BadReg(m)) then UNPREDICTABLE;
+ if (BadReg(Rd) || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+ break;
+ default:
+ return false;
+ }
+ bool success = false;
+ uint32_t value = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted =
+ Shift_C(value, shift_t, shift_n, APSR_C, carry, &success);
+ if (!success)
+ return false;
+ uint32_t result = ~shifted;
+
+ // The context specifies that an immediate is to be moved into Rd.
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+// PC relative immediate load into register, possibly followed by ADD (SP plus
+// register).
+// LDR (literal)
+bool EmulateInstructionARM::EmulateLDRRtPCRelative(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations(); NullCheckIfThumbEE(15);
+ base = Align(PC,4);
+ address = if add then (base + imm32) else (base - imm32);
+ data = MemU[address,4];
+ if t == 15 then
+ if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE;
+ elsif UnalignedSupport() || address<1:0> = '00' then
+ R[t] = data;
+ else // Can only apply before ARMv7
+ if CurrentInstrSet() == InstrSet_ARM then
+ R[t] = ROR(data, 8*UInt(address<1:0>));
+ else
+ R[t] = bits(32) UNKNOWN;
+ }
+#endif
+
+ if (ConditionPassed(opcode)) {
+ bool success = false;
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+
+ // PC relative immediate load context
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+ RegisterInfo pc_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_pc, pc_reg);
+ context.SetRegisterPlusOffset(pc_reg, 0);
+
+ uint32_t Rt; // the destination register
+ uint32_t imm32; // immediate offset from the PC
+ bool add; // +imm32 or -imm32?
+ addr_t base; // the base address
+ addr_t address; // the PC relative address
+ uint32_t data; // the literal data value from the PC relative load
+ switch (encoding) {
+ case eEncodingT1:
+ Rt = Bits32(opcode, 10, 8);
+ imm32 = Bits32(opcode, 7, 0) << 2; // imm32 = ZeroExtend(imm8:'00', 32);
+ add = true;
+ break;
+ case eEncodingT2:
+ Rt = Bits32(opcode, 15, 12);
+ imm32 = Bits32(opcode, 11, 0) << 2; // imm32 = ZeroExtend(imm12, 32);
+ add = BitIsSet(opcode, 23);
+ if (Rt == 15 && InITBlock() && !LastInITBlock())
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ base = Align(pc, 4);
+ if (add)
+ address = base + imm32;
+ else
+ address = base - imm32;
+
+ context.SetRegisterPlusOffset(pc_reg, address - base);
+ data = MemURead(context, address, 4, 0, &success);
+ if (!success)
+ return false;
+
+ if (Rt == 15) {
+ if (Bits32(address, 1, 0) == 0) {
+ // In ARMv5T and above, this is an interworking branch.
+ if (!LoadWritePC(context, data))
+ return false;
+ } else
+ return false;
+ } else if (UnalignedSupport() || Bits32(address, 1, 0) == 0) {
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + Rt,
+ data))
+ return false;
+ } else // We don't handle ARM for now.
+ return false;
+ }
+ return true;
+}
+
+// An add operation to adjust the SP.
+// ADD (SP plus immediate)
+bool EmulateInstructionARM::EmulateADDSPImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(SP, imm32, '0');
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+ }
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ const addr_t sp = ReadCoreReg(SP_REG, &success);
+ if (!success)
+ return false;
+ uint32_t imm32; // the immediate operand
+ uint32_t d;
+ bool setflags;
+ switch (encoding) {
+ case eEncodingT1:
+ // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(imm8:'00', 32);
+ d = Bits32(opcode, 10, 8);
+ imm32 = (Bits32(opcode, 7, 0) << 2);
+ setflags = false;
+ break;
+
+ case eEncodingT2:
+ // d = 13; setflags = FALSE; imm32 = ZeroExtend(imm7:'00', 32);
+ d = 13;
+ imm32 = ThumbImm7Scaled(opcode); // imm32 = ZeroExtend(imm7:'00', 32)
+ setflags = false;
+ break;
+
+ case eEncodingT3:
+ // d = UInt(Rd); setflags = (S == "1"); imm32 =
+ // ThumbExpandImm(i:imm3:imm8);
+ d = Bits32(opcode, 11, 8);
+ imm32 = ThumbExpandImm(opcode);
+ setflags = Bit32(opcode, 20);
+
+ // if Rd == "1111" && S == "1" then SEE CMN (immediate);
+ if (d == 15 && setflags == 1)
+ return false; // CMN (immediate) not yet supported
+
+ // if d == 15 && S == "0" then UNPREDICTABLE;
+ if (d == 15 && setflags == 0)
+ return false;
+ break;
+
+ case eEncodingT4: {
+ // if Rn == '1111' then SEE ADR;
+ // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(i:imm3:imm8, 32);
+ d = Bits32(opcode, 11, 8);
+ setflags = false;
+ uint32_t i = Bit32(opcode, 26);
+ uint32_t imm3 = Bits32(opcode, 14, 12);
+ uint32_t imm8 = Bits32(opcode, 7, 0);
+ imm32 = (i << 11) | (imm3 << 8) | imm8;
+
+ // if d == 15 then UNPREDICTABLE;
+ if (d == 15)
+ return false;
+ } break;
+
+ default:
+ return false;
+ }
+ // (result, carry, overflow) = AddWithCarry(R[n], imm32, '0');
+ AddWithCarryResult res = AddWithCarry(sp, imm32, 0);
+
+ EmulateInstruction::Context context;
+ if (d == 13)
+ context.type = EmulateInstruction::eContextAdjustStackPointer;
+ else
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+
+ RegisterInfo sp_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
+ context.SetRegisterPlusOffset(sp_reg, res.result - sp);
+
+ if (d == 15) {
+ if (!ALUWritePC(context, res.result))
+ return false;
+ } else {
+ // R[d] = result;
+ // if setflags then
+ // APSR.N = result<31>;
+ // APSR.Z = IsZeroBit(result);
+ // APSR.C = carry;
+ // APSR.V = overflow;
+ if (!WriteCoreRegOptionalFlags(context, res.result, d, setflags,
+ res.carry_out, res.overflow))
+ return false;
+ }
+ }
+ return true;
+}
+
+// An add operation to adjust the SP.
+// ADD (SP plus register)
+bool EmulateInstructionARM::EmulateADDSPRm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ (result, carry, overflow) = AddWithCarry(SP, shifted, '0');
+ if d == 15 then
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+ }
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ const addr_t sp = ReadCoreReg(SP_REG, &success);
+ if (!success)
+ return false;
+ uint32_t Rm; // the second operand
+ switch (encoding) {
+ case eEncodingT2:
+ Rm = Bits32(opcode, 6, 3);
+ break;
+ default:
+ return false;
+ }
+ int32_t reg_value = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ addr_t addr = (int32_t)sp + reg_value; // the adjusted stack pointer value
+
+ EmulateInstruction::Context context;
+ context.type = eContextArithmetic;
+ RegisterInfo sp_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
+
+ RegisterInfo other_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm, other_reg);
+ context.SetRegisterRegisterOperands(sp_reg, other_reg);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_SP, addr))
+ return false;
+ }
+ return true;
+}
+
+// Branch with Link and Exchange Instruction Sets (immediate) calls a
+// subroutine at a PC-relative address, and changes instruction set from ARM to
+// Thumb, or from Thumb to ARM.
+// BLX (immediate)
+bool EmulateInstructionARM::EmulateBLXImmediate(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ if CurrentInstrSet() == InstrSet_ARM then
+ LR = PC - 4;
+ else
+ LR = PC<31:1> : '1';
+ if targetInstrSet == InstrSet_ARM then
+ targetAddress = Align(PC,4) + imm32;
+ else
+ targetAddress = PC + imm32;
+ SelectInstrSet(targetInstrSet);
+ BranchWritePC(targetAddress);
+ }
+#endif
+
+ bool success = true;
+
+ if (ConditionPassed(opcode)) {
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRelativeBranchImmediate;
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+ addr_t lr; // next instruction address
+ addr_t target; // target address
+ int32_t imm32; // PC-relative offset
+ switch (encoding) {
+ case eEncodingT1: {
+ lr = pc | 1u; // return address
+ uint32_t S = Bit32(opcode, 26);
+ uint32_t imm10 = Bits32(opcode, 25, 16);
+ uint32_t J1 = Bit32(opcode, 13);
+ uint32_t J2 = Bit32(opcode, 11);
+ uint32_t imm11 = Bits32(opcode, 10, 0);
+ uint32_t I1 = !(J1 ^ S);
+ uint32_t I2 = !(J2 ^ S);
+ uint32_t imm25 =
+ (S << 24) | (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1);
+ imm32 = llvm::SignExtend32<25>(imm25);
+ target = pc + imm32;
+ SelectInstrSet(eModeThumb);
+ context.SetISAAndImmediateSigned(eModeThumb, 4 + imm32);
+ if (InITBlock() && !LastInITBlock())
+ return false;
+ break;
+ }
+ case eEncodingT2: {
+ lr = pc | 1u; // return address
+ uint32_t S = Bit32(opcode, 26);
+ uint32_t imm10H = Bits32(opcode, 25, 16);
+ uint32_t J1 = Bit32(opcode, 13);
+ uint32_t J2 = Bit32(opcode, 11);
+ uint32_t imm10L = Bits32(opcode, 10, 1);
+ uint32_t I1 = !(J1 ^ S);
+ uint32_t I2 = !(J2 ^ S);
+ uint32_t imm25 =
+ (S << 24) | (I1 << 23) | (I2 << 22) | (imm10H << 12) | (imm10L << 2);
+ imm32 = llvm::SignExtend32<25>(imm25);
+ target = Align(pc, 4) + imm32;
+ SelectInstrSet(eModeARM);
+ context.SetISAAndImmediateSigned(eModeARM, 4 + imm32);
+ if (InITBlock() && !LastInITBlock())
+ return false;
+ break;
+ }
+ case eEncodingA1:
+ lr = pc - 4; // return address
+ imm32 = llvm::SignExtend32<26>(Bits32(opcode, 23, 0) << 2);
+ target = Align(pc, 4) + imm32;
+ SelectInstrSet(eModeARM);
+ context.SetISAAndImmediateSigned(eModeARM, 8 + imm32);
+ break;
+ case eEncodingA2:
+ lr = pc - 4; // return address
+ imm32 = llvm::SignExtend32<26>(Bits32(opcode, 23, 0) << 2 |
+ Bits32(opcode, 24, 24) << 1);
+ target = pc + imm32;
+ SelectInstrSet(eModeThumb);
+ context.SetISAAndImmediateSigned(eModeThumb, 8 + imm32);
+ break;
+ default:
+ return false;
+ }
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_RA, lr))
+ return false;
+ if (!BranchWritePC(context, target))
+ return false;
+ if (m_opcode_cpsr != m_new_inst_cpsr)
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_FLAGS, m_new_inst_cpsr))
+ return false;
+ }
+ return true;
+}
+
+// Branch with Link and Exchange (register) calls a subroutine at an address
+// and instruction set specified by a register.
+// BLX (register)
+bool EmulateInstructionARM::EmulateBLXRm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ target = R[m];
+ if CurrentInstrSet() == InstrSet_ARM then
+ next_instr_addr = PC - 4;
+ LR = next_instr_addr;
+ else
+ next_instr_addr = PC - 2;
+ LR = next_instr_addr<31:1> : '1';
+ BXWritePC(target);
+ }
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextAbsoluteBranchRegister;
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ addr_t lr; // next instruction address
+ if (!success)
+ return false;
+ uint32_t Rm; // the register with the target address
+ switch (encoding) {
+ case eEncodingT1:
+ lr = (pc - 2) | 1u; // return address
+ Rm = Bits32(opcode, 6, 3);
+ // if m == 15 then UNPREDICTABLE;
+ if (Rm == 15)
+ return false;
+ if (InITBlock() && !LastInITBlock())
+ return false;
+ break;
+ case eEncodingA1:
+ lr = pc - 4; // return address
+ Rm = Bits32(opcode, 3, 0);
+ // if m == 15 then UNPREDICTABLE;
+ if (Rm == 15)
+ return false;
+ break;
+ default:
+ return false;
+ }
+ addr_t target = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg);
+ context.SetRegister(dwarf_reg);
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_RA, lr))
+ return false;
+ if (!BXWritePC(context, target))
+ return false;
+ }
+ return true;
+}
+
+// Branch and Exchange causes a branch to an address and instruction set
+// specified by a register.
+bool EmulateInstructionARM::EmulateBXRm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ BXWritePC(R[m]);
+ }
+#endif
+
+ if (ConditionPassed(opcode)) {
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextAbsoluteBranchRegister;
+ uint32_t Rm; // the register with the target address
+ switch (encoding) {
+ case eEncodingT1:
+ Rm = Bits32(opcode, 6, 3);
+ if (InITBlock() && !LastInITBlock())
+ return false;
+ break;
+ case eEncodingA1:
+ Rm = Bits32(opcode, 3, 0);
+ break;
+ default:
+ return false;
+ }
+ bool success = false;
+ addr_t target = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg);
+ context.SetRegister(dwarf_reg);
+ if (!BXWritePC(context, target))
+ return false;
+ }
+ return true;
+}
+
+// Branch and Exchange Jazelle attempts to change to Jazelle state. If the
+// attempt fails, it branches to an address and instruction set specified by a
+// register as though it were a BX instruction.
+//
+// TODO: Emulate Jazelle architecture?
+// We currently assume that switching to Jazelle state fails, thus
+// treating BXJ as a BX operation.
+bool EmulateInstructionARM::EmulateBXJRm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ if JMCR.JE == '0' || CurrentInstrSet() == InstrSet_ThumbEE then
+ BXWritePC(R[m]);
+ else
+ if JazelleAcceptsExecution() then
+ SwitchToJazelleExecution();
+ else
+ SUBARCHITECTURE_DEFINED handler call;
+ }
+#endif
+
+ if (ConditionPassed(opcode)) {
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextAbsoluteBranchRegister;
+ uint32_t Rm; // the register with the target address
+ switch (encoding) {
+ case eEncodingT1:
+ Rm = Bits32(opcode, 19, 16);
+ if (BadReg(Rm))
+ return false;
+ if (InITBlock() && !LastInITBlock())
+ return false;
+ break;
+ case eEncodingA1:
+ Rm = Bits32(opcode, 3, 0);
+ if (Rm == 15)
+ return false;
+ break;
+ default:
+ return false;
+ }
+ bool success = false;
+ addr_t target = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg);
+ context.SetRegister(dwarf_reg);
+ if (!BXWritePC(context, target))
+ return false;
+ }
+ return true;
+}
+
+// Set r7 to point to some ip offset.
+// SUB (immediate)
+bool EmulateInstructionARM::EmulateSUBR7IPImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(SP, NOT(imm32), '1');
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+ }
+#endif
+
+ if (ConditionPassed(opcode)) {
+ bool success = false;
+ const addr_t ip = ReadCoreReg(12, &success);
+ if (!success)
+ return false;
+ uint32_t imm32;
+ switch (encoding) {
+ case eEncodingA1:
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+ break;
+ default:
+ return false;
+ }
+ addr_t ip_offset = imm32;
+ addr_t addr = ip - ip_offset; // the adjusted ip value
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r12, dwarf_reg);
+ context.SetRegisterPlusOffset(dwarf_reg, -ip_offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r7, addr))
+ return false;
+ }
+ return true;
+}
+
+// Set ip to point to some stack offset.
+// SUB (SP minus immediate)
+bool EmulateInstructionARM::EmulateSUBIPSPImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(SP, NOT(imm32), '1');
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+ }
+#endif
+
+ if (ConditionPassed(opcode)) {
+ bool success = false;
+ const addr_t sp = ReadCoreReg(SP_REG, &success);
+ if (!success)
+ return false;
+ uint32_t imm32;
+ switch (encoding) {
+ case eEncodingA1:
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+ break;
+ default:
+ return false;
+ }
+ addr_t sp_offset = imm32;
+ addr_t addr = sp - sp_offset; // the adjusted stack pointer value
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, dwarf_reg);
+ context.SetRegisterPlusOffset(dwarf_reg, -sp_offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r12, addr))
+ return false;
+ }
+ return true;
+}
+
+// This instruction subtracts an immediate value from the SP value, and writes
+// the result to the destination register.
+//
+// If Rd == 13 => A sub operation to adjust the SP -- allocate space for local
+// storage.
+bool EmulateInstructionARM::EmulateSUBSPImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(SP, NOT(imm32), '1');
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+ }
+#endif
+
+ bool success = false;
+ if (ConditionPassed(opcode)) {
+ const addr_t sp = ReadCoreReg(SP_REG, &success);
+ if (!success)
+ return false;
+
+ uint32_t Rd;
+ bool setflags;
+ uint32_t imm32;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = 13;
+ setflags = false;
+ imm32 = ThumbImm7Scaled(opcode); // imm32 = ZeroExtend(imm7:'00', 32)
+ break;
+ case eEncodingT2:
+ Rd = Bits32(opcode, 11, 8);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8)
+ if (Rd == 15 && setflags)
+ return EmulateCMPImm(opcode, eEncodingT2);
+ if (Rd == 15 && !setflags)
+ return false;
+ break;
+ case eEncodingT3:
+ Rd = Bits32(opcode, 11, 8);
+ setflags = false;
+ imm32 = ThumbImm12(opcode); // imm32 = ZeroExtend(i:imm3:imm8, 32)
+ if (Rd == 15)
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+ AddWithCarryResult res = AddWithCarry(sp, ~imm32, 1);
+
+ EmulateInstruction::Context context;
+ if (Rd == 13) {
+ uint64_t imm64 = imm32; // Need to expand it to 64 bits before attempting
+ // to negate it, or the wrong
+ // value gets passed down to context.SetImmediateSigned.
+ context.type = EmulateInstruction::eContextAdjustStackPointer;
+ context.SetImmediateSigned(-imm64); // the stack pointer offset
+ } else {
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+ }
+
+ if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow))
+ return false;
+ }
+ return true;
+}
+
+// A store operation to the stack that also updates the SP.
+bool EmulateInstructionARM::EmulateSTRRtSP(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ MemU[address,4] = if t == 15 then PCStoreValue() else R[t];
+ if wback then R[n] = offset_addr;
+ }
+#endif
+
+ bool success = false;
+ if (ConditionPassed(opcode)) {
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ const addr_t sp = ReadCoreReg(SP_REG, &success);
+ if (!success)
+ return false;
+ uint32_t Rt; // the source register
+ uint32_t imm12;
+ uint32_t
+ Rn; // This function assumes Rn is the SP, but we should verify that.
+
+ bool index;
+ bool add;
+ bool wback;
+ switch (encoding) {
+ case eEncodingA1:
+ Rt = Bits32(opcode, 15, 12);
+ imm12 = Bits32(opcode, 11, 0);
+ Rn = Bits32(opcode, 19, 16);
+
+ if (Rn != 13) // 13 is the SP reg on ARM. Verify that Rn == SP.
+ return false;
+
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21));
+
+ if (wback && ((Rn == 15) || (Rn == Rt)))
+ return false;
+ break;
+ default:
+ return false;
+ }
+ addr_t offset_addr;
+ if (add)
+ offset_addr = sp + imm12;
+ else
+ offset_addr = sp - imm12;
+
+ addr_t addr;
+ if (index)
+ addr = offset_addr;
+ else
+ addr = sp;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextPushRegisterOnStack;
+ RegisterInfo sp_reg;
+ RegisterInfo dwarf_reg;
+
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rt, dwarf_reg);
+ context.SetRegisterToRegisterPlusOffset(dwarf_reg, sp_reg, addr - sp);
+ if (Rt != 15) {
+ uint32_t reg_value = ReadCoreReg(Rt, &success);
+ if (!success)
+ return false;
+ if (!MemUWrite(context, addr, reg_value, addr_byte_size))
+ return false;
+ } else {
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+ if (!MemUWrite(context, addr, pc, addr_byte_size))
+ return false;
+ }
+
+ if (wback) {
+ context.type = EmulateInstruction::eContextAdjustStackPointer;
+ context.SetImmediateSigned(addr - sp);
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_SP, offset_addr))
+ return false;
+ }
+ }
+ return true;
+}
+
+// Vector Push stores multiple extension registers to the stack. It also
+// updates SP to point to the start of the stored data.
+bool EmulateInstructionARM::EmulateVPUSH(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(13);
+ address = SP - imm32;
+ SP = SP - imm32;
+ if single_regs then
+ for r = 0 to regs-1
+ MemA[address,4] = S[d+r]; address = address+4;
+ else
+ for r = 0 to regs-1
+ // Store as two word-aligned words in the correct order for
+ // current endianness.
+ MemA[address,4] = if BigEndian() then D[d+r]<63:32> else D[d+r]<31:0>;
+ MemA[address+4,4] = if BigEndian() then D[d+r]<31:0> else D[d+r]<63:32>;
+ address = address+8;
+ }
+#endif
+
+ bool success = false;
+ if (ConditionPassed(opcode)) {
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ const addr_t sp = ReadCoreReg(SP_REG, &success);
+ if (!success)
+ return false;
+ bool single_regs;
+ uint32_t d; // UInt(D:Vd) or UInt(Vd:D) starting register
+ uint32_t imm32; // stack offset
+ uint32_t regs; // number of registers
+ switch (encoding) {
+ case eEncodingT1:
+ case eEncodingA1:
+ single_regs = false;
+ d = Bit32(opcode, 22) << 4 | Bits32(opcode, 15, 12);
+ imm32 = Bits32(opcode, 7, 0) * addr_byte_size;
+ // If UInt(imm8) is odd, see "FSTMX".
+ regs = Bits32(opcode, 7, 0) / 2;
+ // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE;
+ if (regs == 0 || regs > 16 || (d + regs) > 32)
+ return false;
+ break;
+ case eEncodingT2:
+ case eEncodingA2:
+ single_regs = true;
+ d = Bits32(opcode, 15, 12) << 1 | Bit32(opcode, 22);
+ imm32 = Bits32(opcode, 7, 0) * addr_byte_size;
+ regs = Bits32(opcode, 7, 0);
+ // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE;
+ if (regs == 0 || regs > 16 || (d + regs) > 32)
+ return false;
+ break;
+ default:
+ return false;
+ }
+ uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0;
+ uint32_t reg_byte_size = single_regs ? addr_byte_size : addr_byte_size * 2;
+ addr_t sp_offset = imm32;
+ addr_t addr = sp - sp_offset;
+ uint32_t i;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextPushRegisterOnStack;
+
+ RegisterInfo dwarf_reg;
+ RegisterInfo sp_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
+ for (i = 0; i < regs; ++i) {
+ GetRegisterInfo(eRegisterKindDWARF, start_reg + d + i, dwarf_reg);
+ context.SetRegisterToRegisterPlusOffset(dwarf_reg, sp_reg, addr - sp);
+ // uint64_t to accommodate 64-bit registers.
+ uint64_t reg_value = ReadRegisterUnsigned(&dwarf_reg, 0, &success);
+ if (!success)
+ return false;
+ if (!MemAWrite(context, addr, reg_value, reg_byte_size))
+ return false;
+ addr += reg_byte_size;
+ }
+
+ context.type = EmulateInstruction::eContextAdjustStackPointer;
+ context.SetImmediateSigned(-sp_offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_SP, sp - sp_offset))
+ return false;
+ }
+ return true;
+}
+
+// Vector Pop loads multiple extension registers from the stack. It also
+// updates SP to point just above the loaded data.
+bool EmulateInstructionARM::EmulateVPOP(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(13);
+ address = SP;
+ SP = SP + imm32;
+ if single_regs then
+ for r = 0 to regs-1
+ S[d+r] = MemA[address,4]; address = address+4;
+ else
+ for r = 0 to regs-1
+ word1 = MemA[address,4]; word2 = MemA[address+4,4]; address = address+8;
+ // Combine the word-aligned words in the correct order for
+ // current endianness.
+ D[d+r] = if BigEndian() then word1:word2 else word2:word1;
+ }
+#endif
+
+ bool success = false;
+ if (ConditionPassed(opcode)) {
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ const addr_t sp = ReadCoreReg(SP_REG, &success);
+ if (!success)
+ return false;
+ bool single_regs;
+ uint32_t d; // UInt(D:Vd) or UInt(Vd:D) starting register
+ uint32_t imm32; // stack offset
+ uint32_t regs; // number of registers
+ switch (encoding) {
+ case eEncodingT1:
+ case eEncodingA1:
+ single_regs = false;
+ d = Bit32(opcode, 22) << 4 | Bits32(opcode, 15, 12);
+ imm32 = Bits32(opcode, 7, 0) * addr_byte_size;
+ // If UInt(imm8) is odd, see "FLDMX".
+ regs = Bits32(opcode, 7, 0) / 2;
+ // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE;
+ if (regs == 0 || regs > 16 || (d + regs) > 32)
+ return false;
+ break;
+ case eEncodingT2:
+ case eEncodingA2:
+ single_regs = true;
+ d = Bits32(opcode, 15, 12) << 1 | Bit32(opcode, 22);
+ imm32 = Bits32(opcode, 7, 0) * addr_byte_size;
+ regs = Bits32(opcode, 7, 0);
+ // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE;
+ if (regs == 0 || regs > 16 || (d + regs) > 32)
+ return false;
+ break;
+ default:
+ return false;
+ }
+ uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0;
+ uint32_t reg_byte_size = single_regs ? addr_byte_size : addr_byte_size * 2;
+ addr_t sp_offset = imm32;
+ addr_t addr = sp;
+ uint32_t i;
+ uint64_t data; // uint64_t to accommodate 64-bit registers.
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextPopRegisterOffStack;
+
+ RegisterInfo dwarf_reg;
+ RegisterInfo sp_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
+ for (i = 0; i < regs; ++i) {
+ GetRegisterInfo(eRegisterKindDWARF, start_reg + d + i, dwarf_reg);
+ context.SetAddress(addr);
+ data = MemARead(context, addr, reg_byte_size, 0, &success);
+ if (!success)
+ return false;
+ if (!WriteRegisterUnsigned(context, &dwarf_reg, data))
+ return false;
+ addr += reg_byte_size;
+ }
+
+ context.type = EmulateInstruction::eContextAdjustStackPointer;
+ context.SetImmediateSigned(sp_offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_SP, sp + sp_offset))
+ return false;
+ }
+ return true;
+}
+
+// SVC (previously SWI)
+bool EmulateInstructionARM::EmulateSVC(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ CallSupervisor();
+ }
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ addr_t lr; // next instruction address
+ if (!success)
+ return false;
+ uint32_t imm32; // the immediate constant
+ uint32_t mode; // ARM or Thumb mode
+ switch (encoding) {
+ case eEncodingT1:
+ lr = (pc + 2) | 1u; // return address
+ imm32 = Bits32(opcode, 7, 0);
+ mode = eModeThumb;
+ break;
+ case eEncodingA1:
+ lr = pc + 4; // return address
+ imm32 = Bits32(opcode, 23, 0);
+ mode = eModeARM;
+ break;
+ default:
+ return false;
+ }
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextSupervisorCall;
+ context.SetISAAndImmediate(mode, imm32);
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_RA, lr))
+ return false;
+ }
+ return true;
+}
+
+// If Then makes up to four following instructions (the IT block) conditional.
+bool EmulateInstructionARM::EmulateIT(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ EncodingSpecificOperations();
+ ITSTATE.IT<7:0> = firstcond:mask;
+#endif
+
+ m_it_session.InitIT(Bits32(opcode, 7, 0));
+ return true;
+}
+
+bool EmulateInstructionARM::EmulateNop(const uint32_t opcode,
+ const ARMEncoding encoding) {
+ // NOP, nothing to do...
+ return true;
+}
+
+// Branch causes a branch to a target address.
+bool EmulateInstructionARM::EmulateB(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations();
+ BranchWritePC(PC + imm32);
+ }
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRelativeBranchImmediate;
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+ addr_t target; // target address
+ int32_t imm32; // PC-relative offset
+ switch (encoding) {
+ case eEncodingT1:
+ // The 'cond' field is handled in EmulateInstructionARM::CurrentCond().
+ imm32 = llvm::SignExtend32<9>(Bits32(opcode, 7, 0) << 1);
+ target = pc + imm32;
+ context.SetISAAndImmediateSigned(eModeThumb, 4 + imm32);
+ break;
+ case eEncodingT2:
+ imm32 = llvm::SignExtend32<12>(Bits32(opcode, 10, 0) << 1);
+ target = pc + imm32;
+ context.SetISAAndImmediateSigned(eModeThumb, 4 + imm32);
+ break;
+ case eEncodingT3:
+ // The 'cond' field is handled in EmulateInstructionARM::CurrentCond().
+ {
+ if (Bits32(opcode, 25, 23) == 7)
+ return false; // See Branches and miscellaneous control on page
+ // A6-235.
+
+ uint32_t S = Bit32(opcode, 26);
+ uint32_t imm6 = Bits32(opcode, 21, 16);
+ uint32_t J1 = Bit32(opcode, 13);
+ uint32_t J2 = Bit32(opcode, 11);
+ uint32_t imm11 = Bits32(opcode, 10, 0);
+ uint32_t imm21 =
+ (S << 20) | (J2 << 19) | (J1 << 18) | (imm6 << 12) | (imm11 << 1);
+ imm32 = llvm::SignExtend32<21>(imm21);
+ target = pc + imm32;
+ context.SetISAAndImmediateSigned(eModeThumb, 4 + imm32);
+ break;
+ }
+ case eEncodingT4: {
+ uint32_t S = Bit32(opcode, 26);
+ uint32_t imm10 = Bits32(opcode, 25, 16);
+ uint32_t J1 = Bit32(opcode, 13);
+ uint32_t J2 = Bit32(opcode, 11);
+ uint32_t imm11 = Bits32(opcode, 10, 0);
+ uint32_t I1 = !(J1 ^ S);
+ uint32_t I2 = !(J2 ^ S);
+ uint32_t imm25 =
+ (S << 24) | (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1);
+ imm32 = llvm::SignExtend32<25>(imm25);
+ target = pc + imm32;
+ context.SetISAAndImmediateSigned(eModeThumb, 4 + imm32);
+ break;
+ }
+ case eEncodingA1:
+ imm32 = llvm::SignExtend32<26>(Bits32(opcode, 23, 0) << 2);
+ target = pc + imm32;
+ context.SetISAAndImmediateSigned(eModeARM, 8 + imm32);
+ break;
+ default:
+ return false;
+ }
+ if (!BranchWritePC(context, target))
+ return false;
+ }
+ return true;
+}
+
+// Compare and Branch on Nonzero and Compare and Branch on Zero compare the
+// value in a register with zero and conditionally branch forward a constant
+// value. They do not affect the condition flags. CBNZ, CBZ
+bool EmulateInstructionARM::EmulateCB(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ EncodingSpecificOperations();
+ if nonzero ^ IsZero(R[n]) then
+ BranchWritePC(PC + imm32);
+#endif
+
+ bool success = false;
+
+ // Read the register value from the operand register Rn.
+ uint32_t reg_val = ReadCoreReg(Bits32(opcode, 2, 0), &success);
+ if (!success)
+ return false;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRelativeBranchImmediate;
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+
+ addr_t target; // target address
+ uint32_t imm32; // PC-relative offset to branch forward
+ bool nonzero;
+ switch (encoding) {
+ case eEncodingT1:
+ imm32 = Bit32(opcode, 9) << 6 | Bits32(opcode, 7, 3) << 1;
+ nonzero = BitIsSet(opcode, 11);
+ target = pc + imm32;
+ context.SetISAAndImmediateSigned(eModeThumb, 4 + imm32);
+ break;
+ default:
+ return false;
+ }
+ if (m_ignore_conditions || (nonzero ^ (reg_val == 0)))
+ if (!BranchWritePC(context, target))
+ return false;
+
+ return true;
+}
+
+// Table Branch Byte causes a PC-relative forward branch using a table of
+// single byte offsets.
+// A base register provides a pointer to the table, and a second register
+// supplies an index into the table.
+// The branch length is twice the value of the byte returned from the table.
+//
+// Table Branch Halfword causes a PC-relative forward branch using a table of
+// single halfword offsets.
+// A base register provides a pointer to the table, and a second register
+// supplies an index into the table.
+// The branch length is twice the value of the halfword returned from the
+// table. TBB, TBH
+bool EmulateInstructionARM::EmulateTB(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ if is_tbh then
+ halfwords = UInt(MemU[R[n]+LSL(R[m],1), 2]);
+ else
+ halfwords = UInt(MemU[R[n]+R[m], 1]);
+ BranchWritePC(PC + 2*halfwords);
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rn; // the base register which contains the address of the table of
+ // branch lengths
+ uint32_t Rm; // the index register which contains an integer pointing to a
+ // byte/halfword in the table
+ bool is_tbh; // true if table branch halfword
+ switch (encoding) {
+ case eEncodingT1:
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ is_tbh = BitIsSet(opcode, 4);
+ if (Rn == 13 || BadReg(Rm))
+ return false;
+ if (InITBlock() && !LastInITBlock())
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ // Read the address of the table from the operand register Rn. The PC can
+ // be used, in which case the table immediately follows this instruction.
+ uint32_t base = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // the table index
+ uint32_t index = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ // the offsetted table address
+ addr_t addr = base + (is_tbh ? index * 2 : index);
+
+ // PC-relative offset to branch forward
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextTableBranchReadMemory;
+ uint32_t offset = MemURead(context, addr, is_tbh ? 2 : 1, 0, &success) * 2;
+ if (!success)
+ return false;
+
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+
+ // target address
+ addr_t target = pc + offset;
+ context.type = EmulateInstruction::eContextRelativeBranchImmediate;
+ context.SetISAAndImmediateSigned(eModeThumb, 4 + offset);
+
+ if (!BranchWritePC(context, target))
+ return false;
+ }
+
+ return true;
+}
+
+// This instruction adds an immediate value to a register value, and writes the
+// result to the destination register. It can optionally update the condition
+// flags based on the result.
+bool EmulateInstructionARM::EmulateADDImmThumb(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(R[n], imm32, '0');
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t d;
+ uint32_t n;
+ bool setflags;
+ uint32_t imm32;
+ uint32_t carry_out;
+
+ // EncodingSpecificOperations();
+ switch (encoding) {
+ case eEncodingT1:
+ // d = UInt(Rd); n = UInt(Rn); setflags = !InITBlock(); imm32 =
+ // ZeroExtend(imm3, 32);
+ d = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ setflags = !InITBlock();
+ imm32 = Bits32(opcode, 8, 6);
+
+ break;
+
+ case eEncodingT2:
+ // d = UInt(Rdn); n = UInt(Rdn); setflags = !InITBlock(); imm32 =
+ // ZeroExtend(imm8, 32);
+ d = Bits32(opcode, 10, 8);
+ n = Bits32(opcode, 10, 8);
+ setflags = !InITBlock();
+ imm32 = Bits32(opcode, 7, 0);
+
+ break;
+
+ case eEncodingT3:
+ // if Rd == '1111' && S == '1' then SEE CMN (immediate);
+ // d = UInt(Rd); n = UInt(Rn); setflags = (S == '1'); imm32 =
+ // ThumbExpandImm(i:imm3:imm8);
+ d = Bits32(opcode, 11, 8);
+ n = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbExpandImm_C(opcode, APSR_C, carry_out);
+
+ // if Rn == '1101' then SEE ADD (SP plus immediate);
+ if (n == 13)
+ return EmulateADDSPImm(opcode, eEncodingT3);
+
+ // if BadReg(d) || n == 15 then UNPREDICTABLE;
+ if (BadReg(d) || (n == 15))
+ return false;
+
+ break;
+
+ case eEncodingT4: {
+ // if Rn == '1111' then SEE ADR;
+ // d = UInt(Rd); n = UInt(Rn); setflags = FALSE; imm32 =
+ // ZeroExtend(i:imm3:imm8, 32);
+ d = Bits32(opcode, 11, 8);
+ n = Bits32(opcode, 19, 16);
+ setflags = false;
+ uint32_t i = Bit32(opcode, 26);
+ uint32_t imm3 = Bits32(opcode, 14, 12);
+ uint32_t imm8 = Bits32(opcode, 7, 0);
+ imm32 = (i << 11) | (imm3 << 8) | imm8;
+
+ // if Rn == '1101' then SEE ADD (SP plus immediate);
+ if (n == 13)
+ return EmulateADDSPImm(opcode, eEncodingT4);
+
+ // if BadReg(d) then UNPREDICTABLE;
+ if (BadReg(d))
+ return false;
+
+ break;
+ }
+
+ default:
+ return false;
+ }
+
+ uint64_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ //(result, carry, overflow) = AddWithCarry(R[n], imm32, '0');
+ AddWithCarryResult res = AddWithCarry(Rn, imm32, 0);
+
+ RegisterInfo reg_n;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, reg_n);
+
+ EmulateInstruction::Context context;
+ context.type = eContextArithmetic;
+ context.SetRegisterPlusOffset(reg_n, imm32);
+
+ // R[d] = result;
+ // if setflags then
+ // APSR.N = result<31>;
+ // APSR.Z = IsZeroBit(result);
+ // APSR.C = carry;
+ // APSR.V = overflow;
+ if (!WriteCoreRegOptionalFlags(context, res.result, d, setflags,
+ res.carry_out, res.overflow))
+ return false;
+ }
+ return true;
+}
+
+// This instruction adds an immediate value to a register value, and writes the
+// result to the destination register. It can optionally update the condition
+// flags based on the result.
+bool EmulateInstructionARM::EmulateADDImmARM(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(R[n], imm32, '0');
+ if d == 15 then
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd, Rn;
+ uint32_t
+ imm32; // the immediate value to be added to the value obtained from Rn
+ bool setflags;
+ switch (encoding) {
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult res = AddWithCarry(val1, imm32, 0);
+
+ EmulateInstruction::Context context;
+ if (Rd == 13)
+ context.type = EmulateInstruction::eContextAdjustStackPointer;
+ else if (Rd == GetFramePointerRegisterNumber())
+ context.type = EmulateInstruction::eContextSetFramePointer;
+ else
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindDWARF, Rn, dwarf_reg);
+ context.SetRegisterPlusOffset(dwarf_reg, imm32);
+
+ if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow))
+ return false;
+ }
+ return true;
+}
+
+// This instruction adds a register value and an optionally-shifted register
+// value, and writes the result to the destination register. It can optionally
+// update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateADDReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ (result, carry, overflow) = AddWithCarry(R[n], shifted, '0');
+ if d == 15 then
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd, Rn, Rm;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ bool setflags;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 2, 0);
+ Rn = Bits32(opcode, 5, 3);
+ Rm = Bits32(opcode, 8, 6);
+ setflags = !InITBlock();
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ break;
+ case eEncodingT2:
+ Rd = Rn = Bit32(opcode, 7) << 3 | Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 6, 3);
+ setflags = false;
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ if (Rn == 15 && Rm == 15)
+ return false;
+ if (Rd == 15 && InITBlock() && !LastInITBlock())
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the second operand.
+ uint32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+ AddWithCarryResult res = AddWithCarry(val1, shifted, 0);
+
+ EmulateInstruction::Context context;
+ context.type = eContextArithmetic;
+ RegisterInfo op1_reg;
+ RegisterInfo op2_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rn, op1_reg);
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm, op2_reg);
+ context.SetRegisterRegisterOperands(op1_reg, op2_reg);
+
+ if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow))
+ return false;
+ }
+ return true;
+}
+
+// Compare Negative (immediate) adds a register value and an immediate value.
+// It updates the condition flags based on the result, and discards the result.
+bool EmulateInstructionARM::EmulateCMNImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(R[n], imm32, '0');
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ uint32_t Rn; // the first operand
+ uint32_t imm32; // the immediate value to be compared with
+ switch (encoding) {
+ case eEncodingT1:
+ Rn = Bits32(opcode, 19, 16);
+ imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8)
+ if (Rn == 15)
+ return false;
+ break;
+ case eEncodingA1:
+ Rn = Bits32(opcode, 19, 16);
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+ break;
+ default:
+ return false;
+ }
+ // Read the register value from the operand register Rn.
+ uint32_t reg_val = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult res = AddWithCarry(reg_val, imm32, 0);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+ return WriteFlags(context, res.result, res.carry_out, res.overflow);
+}
+
+// Compare Negative (register) adds a register value and an optionally-shifted
+// register value. It updates the condition flags based on the result, and
+// discards the result.
+bool EmulateInstructionARM::EmulateCMNReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ (result, carry, overflow) = AddWithCarry(R[n], shifted, '0');
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ uint32_t Rn; // the first operand
+ uint32_t Rm; // the second operand
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ switch (encoding) {
+ case eEncodingT1:
+ Rn = Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 5, 3);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ break;
+ case eEncodingT2:
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+ // if n == 15 || BadReg(m) then UNPREDICTABLE;
+ if (Rn == 15 || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+ break;
+ default:
+ return false;
+ }
+ // Read the register value from register Rn.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the register value from register Rm.
+ uint32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+ AddWithCarryResult res = AddWithCarry(val1, shifted, 0);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+ return WriteFlags(context, res.result, res.carry_out, res.overflow);
+}
+
+// Compare (immediate) subtracts an immediate value from a register value. It
+// updates the condition flags based on the result, and discards the result.
+bool EmulateInstructionARM::EmulateCMPImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), '1');
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ uint32_t Rn; // the first operand
+ uint32_t imm32; // the immediate value to be compared with
+ switch (encoding) {
+ case eEncodingT1:
+ Rn = Bits32(opcode, 10, 8);
+ imm32 = Bits32(opcode, 7, 0);
+ break;
+ case eEncodingT2:
+ Rn = Bits32(opcode, 19, 16);
+ imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8)
+ if (Rn == 15)
+ return false;
+ break;
+ case eEncodingA1:
+ Rn = Bits32(opcode, 19, 16);
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+ break;
+ default:
+ return false;
+ }
+ // Read the register value from the operand register Rn.
+ uint32_t reg_val = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, 1);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+ return WriteFlags(context, res.result, res.carry_out, res.overflow);
+}
+
+// Compare (register) subtracts an optionally-shifted register value from a
+// register value. It updates the condition flags based on the result, and
+// discards the result.
+bool EmulateInstructionARM::EmulateCMPReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), '1');
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ uint32_t Rn; // the first operand
+ uint32_t Rm; // the second operand
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ switch (encoding) {
+ case eEncodingT1:
+ Rn = Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 5, 3);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ break;
+ case eEncodingT2:
+ Rn = Bit32(opcode, 7) << 3 | Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 6, 3);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ if (Rn < 8 && Rm < 8)
+ return false;
+ if (Rn == 15 || Rm == 15)
+ return false;
+ break;
+ case eEncodingT3:
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+ if (Rn == 15 || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+ break;
+ default:
+ return false;
+ }
+ // Read the register value from register Rn.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the register value from register Rm.
+ uint32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+ AddWithCarryResult res = AddWithCarry(val1, ~shifted, 1);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+ return WriteFlags(context, res.result, res.carry_out, res.overflow);
+}
+
+// Arithmetic Shift Right (immediate) shifts a register value right by an
+// immediate number of bits, shifting in copies of its sign bit, and writes the
+// result to the destination register. It can optionally update the condition
+// flags based on the result.
+bool EmulateInstructionARM::EmulateASRImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry) = Shift_C(R[m], SRType_ASR, shift_n, APSR.C);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ return EmulateShiftImm(opcode, encoding, SRType_ASR);
+}
+
+// Arithmetic Shift Right (register) shifts a register value right by a
+// variable number of bits, shifting in copies of its sign bit, and writes the
+// result to the destination register. The variable number of bits is read from
+// the bottom byte of a register. It can optionally update the condition flags
+// based on the result.
+bool EmulateInstructionARM::EmulateASRReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shift_n = UInt(R[m]<7:0>);
+ (result, carry) = Shift_C(R[m], SRType_ASR, shift_n, APSR.C);
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ return EmulateShiftReg(opcode, encoding, SRType_ASR);
+}
+
+// Logical Shift Left (immediate) shifts a register value left by an immediate
+// number of bits, shifting in zeros, and writes the result to the destination
+// register. It can optionally update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateLSLImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry) = Shift_C(R[m], SRType_LSL, shift_n, APSR.C);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ return EmulateShiftImm(opcode, encoding, SRType_LSL);
+}
+
+// Logical Shift Left (register) shifts a register value left by a variable
+// number of bits, shifting in zeros, and writes the result to the destination
+// register. The variable number of bits is read from the bottom byte of a
+// register. It can optionally update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateLSLReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shift_n = UInt(R[m]<7:0>);
+ (result, carry) = Shift_C(R[m], SRType_LSL, shift_n, APSR.C);
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ return EmulateShiftReg(opcode, encoding, SRType_LSL);
+}
+
+// Logical Shift Right (immediate) shifts a register value right by an
+// immediate number of bits, shifting in zeros, and writes the result to the
+// destination register. It can optionally update the condition flags based on
+// the result.
+bool EmulateInstructionARM::EmulateLSRImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry) = Shift_C(R[m], SRType_LSR, shift_n, APSR.C);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ return EmulateShiftImm(opcode, encoding, SRType_LSR);
+}
+
+// Logical Shift Right (register) shifts a register value right by a variable
+// number of bits, shifting in zeros, and writes the result to the destination
+// register. The variable number of bits is read from the bottom byte of a
+// register. It can optionally update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateLSRReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shift_n = UInt(R[m]<7:0>);
+ (result, carry) = Shift_C(R[m], SRType_LSR, shift_n, APSR.C);
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ return EmulateShiftReg(opcode, encoding, SRType_LSR);
+}
+
+// Rotate Right (immediate) provides the value of the contents of a register
+// rotated by a constant value. The bits that are rotated off the right end are
+// inserted into the vacated bit positions on the left. It can optionally
+// update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateRORImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry) = Shift_C(R[m], SRType_ROR, shift_n, APSR.C);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ return EmulateShiftImm(opcode, encoding, SRType_ROR);
+}
+
+// Rotate Right (register) provides the value of the contents of a register
+// rotated by a variable number of bits. The bits that are rotated off the
+// right end are inserted into the vacated bit positions on the left. The
+// variable number of bits is read from the bottom byte of a register. It can
+// optionally update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateRORReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shift_n = UInt(R[m]<7:0>);
+ (result, carry) = Shift_C(R[m], SRType_ROR, shift_n, APSR.C);
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ return EmulateShiftReg(opcode, encoding, SRType_ROR);
+}
+
+// Rotate Right with Extend provides the value of the contents of a register
+// shifted right by one place, with the carry flag shifted into bit [31].
+//
+// RRX can optionally update the condition flags based on the result.
+// In that case, bit [0] is shifted into the carry flag.
+bool EmulateInstructionARM::EmulateRRX(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry) = Shift_C(R[m], SRType_RRX, 1, APSR.C);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ return EmulateShiftImm(opcode, encoding, SRType_RRX);
+}
+
+bool EmulateInstructionARM::EmulateShiftImm(const uint32_t opcode,
+ const ARMEncoding encoding,
+ ARM_ShifterType shift_type) {
+ // assert(shift_type == SRType_ASR
+ // || shift_type == SRType_LSL
+ // || shift_type == SRType_LSR
+ // || shift_type == SRType_ROR
+ // || shift_type == SRType_RRX);
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd; // the destination register
+ uint32_t Rm; // the first operand register
+ uint32_t imm5; // encoding for the shift amount
+ uint32_t carry; // the carry bit after the shift operation
+ bool setflags;
+
+ // Special case handling!
+ // A8.6.139 ROR (immediate) -- Encoding T1
+ ARMEncoding use_encoding = encoding;
+ if (shift_type == SRType_ROR && use_encoding == eEncodingT1) {
+ // Morph the T1 encoding from the ARM Architecture Manual into T2
+ // encoding to have the same decoding of bit fields as the other Thumb2
+ // shift operations.
+ use_encoding = eEncodingT2;
+ }
+
+ switch (use_encoding) {
+ case eEncodingT1:
+ // Due to the above special case handling!
+ if (shift_type == SRType_ROR)
+ return false;
+
+ Rd = Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 5, 3);
+ setflags = !InITBlock();
+ imm5 = Bits32(opcode, 10, 6);
+ break;
+ case eEncodingT2:
+ // A8.6.141 RRX
+ // There's no imm form of RRX instructions.
+ if (shift_type == SRType_RRX)
+ return false;
+
+ Rd = Bits32(opcode, 11, 8);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ imm5 = Bits32(opcode, 14, 12) << 2 | Bits32(opcode, 7, 6);
+ if (BadReg(Rd) || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ imm5 = Bits32(opcode, 11, 7);
+ break;
+ default:
+ return false;
+ }
+
+ // A8.6.139 ROR (immediate)
+ if (shift_type == SRType_ROR && imm5 == 0)
+ shift_type = SRType_RRX;
+
+ // Get the first operand.
+ uint32_t value = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ // Decode the shift amount if not RRX.
+ uint32_t amt =
+ (shift_type == SRType_RRX ? 1 : DecodeImmShift(shift_type, imm5));
+
+ uint32_t result = Shift_C(value, shift_type, amt, APSR_C, carry, &success);
+ if (!success)
+ return false;
+
+ // The context specifies that an immediate is to be moved into Rd.
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+bool EmulateInstructionARM::EmulateShiftReg(const uint32_t opcode,
+ const ARMEncoding encoding,
+ ARM_ShifterType shift_type) {
+ // assert(shift_type == SRType_ASR
+ // || shift_type == SRType_LSL
+ // || shift_type == SRType_LSR
+ // || shift_type == SRType_ROR);
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd; // the destination register
+ uint32_t Rn; // the first operand register
+ uint32_t
+ Rm; // the register whose bottom byte contains the amount to shift by
+ uint32_t carry; // the carry bit after the shift operation
+ bool setflags;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 2, 0);
+ Rn = Rd;
+ Rm = Bits32(opcode, 5, 3);
+ setflags = !InITBlock();
+ break;
+ case eEncodingT2:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 3, 0);
+ Rm = Bits32(opcode, 11, 8);
+ setflags = BitIsSet(opcode, 20);
+ if (Rd == 15 || Rn == 15 || Rm == 15)
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ // Get the first operand.
+ uint32_t value = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+ // Get the Rm register content.
+ uint32_t val = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ // Get the shift amount.
+ uint32_t amt = Bits32(val, 7, 0);
+
+ uint32_t result = Shift_C(value, shift_type, amt, APSR_C, carry, &success);
+ if (!success)
+ return false;
+
+ // The context specifies that an immediate is to be moved into Rd.
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+// LDM loads multiple registers from consecutive memory locations, using an
+// address from a base register. Optionally the address just above the highest
+// of those locations can be written back to the base register.
+bool EmulateInstructionARM::EmulateLDM(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed()
+ EncodingSpecificOperations(); NullCheckIfThumbEE (n);
+ address = R[n];
+
+ for i = 0 to 14
+ if registers<i> == '1' then
+ R[i] = MemA[address, 4]; address = address + 4;
+ if registers<15> == '1' then
+ LoadWritePC (MemA[address, 4]);
+
+ if wback && registers<n> == '0' then R[n] = R[n] + 4 * BitCount (registers);
+ if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN; // Only possible for encoding A1
+
+#endif
+
+ bool success = false;
+ if (ConditionPassed(opcode)) {
+ uint32_t n;
+ uint32_t registers = 0;
+ bool wback;
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ switch (encoding) {
+ case eEncodingT1:
+ // n = UInt(Rn); registers = '00000000':register_list; wback =
+ // (registers<n> == '0');
+ n = Bits32(opcode, 10, 8);
+ registers = Bits32(opcode, 7, 0);
+ registers = registers & 0x00ff; // Make sure the top 8 bits are zeros.
+ wback = BitIsClear(registers, n);
+ // if BitCount(registers) < 1 then UNPREDICTABLE;
+ if (BitCount(registers) < 1)
+ return false;
+ break;
+ case eEncodingT2:
+ // if W == '1' && Rn == '1101' then SEE POP;
+ // n = UInt(Rn); registers = P:M:'0':register_list; wback = (W == '1');
+ n = Bits32(opcode, 19, 16);
+ registers = Bits32(opcode, 15, 0);
+ registers = registers & 0xdfff; // Make sure bit 13 is zero.
+ wback = BitIsSet(opcode, 21);
+
+ // if n == 15 || BitCount(registers) < 2 || (P == '1' && M == '1') then
+ // UNPREDICTABLE;
+ if ((n == 15) || (BitCount(registers) < 2) ||
+ (BitIsSet(opcode, 14) && BitIsSet(opcode, 15)))
+ return false;
+
+ // if registers<15> == '1' && InITBlock() && !LastInITBlock() then
+ // UNPREDICTABLE;
+ if (BitIsSet(registers, 15) && InITBlock() && !LastInITBlock())
+ return false;
+
+ // if wback && registers<n> == '1' then UNPREDICTABLE;
+ if (wback && BitIsSet(registers, n))
+ return false;
+ break;
+
+ case eEncodingA1:
+ n = Bits32(opcode, 19, 16);
+ registers = Bits32(opcode, 15, 0);
+ wback = BitIsSet(opcode, 21);
+ if ((n == 15) || (BitCount(registers) < 1))
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ int32_t offset = 0;
+ const addr_t base_address =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, dwarf_reg);
+ context.SetRegisterPlusOffset(dwarf_reg, offset);
+
+ for (int i = 0; i < 14; ++i) {
+ if (BitIsSet(registers, i)) {
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+ context.SetRegisterPlusOffset(dwarf_reg, offset);
+ if (wback && (n == 13)) // Pop Instruction
+ {
+ context.type = EmulateInstruction::eContextPopRegisterOffStack;
+ context.SetAddress(base_address + offset);
+ }
+
+ // R[i] = MemA [address, 4]; address = address + 4;
+ uint32_t data = MemARead(context, base_address + offset, addr_byte_size,
+ 0, &success);
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + i,
+ data))
+ return false;
+
+ offset += addr_byte_size;
+ }
+ }
+
+ if (BitIsSet(registers, 15)) {
+ // LoadWritePC (MemA [address, 4]);
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+ context.SetRegisterPlusOffset(dwarf_reg, offset);
+ uint32_t data =
+ MemARead(context, base_address + offset, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+ // In ARMv5T and above, this is an interworking branch.
+ if (!LoadWritePC(context, data))
+ return false;
+ }
+
+ if (wback && BitIsClear(registers, n)) {
+ // R[n] = R[n] + 4 * BitCount (registers)
+ int32_t offset = addr_byte_size * BitCount(registers);
+ context.type = EmulateInstruction::eContextAdjustBaseRegister;
+ context.SetRegisterPlusOffset(dwarf_reg, offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ base_address + offset))
+ return false;
+ }
+ if (wback && BitIsSet(registers, n))
+ // R[n] bits(32) UNKNOWN;
+ return WriteBits32Unknown(n);
+ }
+ return true;
+}
+
+// LDMDA loads multiple registers from consecutive memory locations using an
+// address from a base register.
+// The consecutive memory locations end at this address and the address just
+// below the lowest of those locations can optionally be written back to the
+// base register.
+bool EmulateInstructionARM::EmulateLDMDA(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ address = R[n] - 4*BitCount(registers) + 4;
+
+ for i = 0 to 14
+ if registers<i> == '1' then
+ R[i] = MemA[address,4]; address = address + 4;
+
+ if registers<15> == '1' then
+ LoadWritePC(MemA[address,4]);
+
+ if wback && registers<n> == '0' then R[n] = R[n] - 4*BitCount(registers);
+ if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t n;
+ uint32_t registers = 0;
+ bool wback;
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ // EncodingSpecificOperations();
+ switch (encoding) {
+ case eEncodingA1:
+ // n = UInt(Rn); registers = register_list; wback = (W == '1');
+ n = Bits32(opcode, 19, 16);
+ registers = Bits32(opcode, 15, 0);
+ wback = BitIsSet(opcode, 21);
+
+ // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE;
+ if ((n == 15) || (BitCount(registers) < 1))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+ // address = R[n] - 4*BitCount(registers) + 4;
+
+ int32_t offset = 0;
+ addr_t Rn = ReadCoreReg(n, &success);
+
+ if (!success)
+ return false;
+
+ addr_t address =
+ Rn - (addr_byte_size * BitCount(registers)) + addr_byte_size;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, dwarf_reg);
+ context.SetRegisterPlusOffset(dwarf_reg, offset);
+
+ // for i = 0 to 14
+ for (int i = 0; i < 14; ++i) {
+ // if registers<i> == '1' then
+ if (BitIsSet(registers, i)) {
+ // R[i] = MemA[address,4]; address = address + 4;
+ context.SetRegisterPlusOffset(dwarf_reg, Rn - (address + offset));
+ uint32_t data =
+ MemARead(context, address + offset, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + i,
+ data))
+ return false;
+ offset += addr_byte_size;
+ }
+ }
+
+ // if registers<15> == '1' then
+ // LoadWritePC(MemA[address,4]);
+ if (BitIsSet(registers, 15)) {
+ context.SetRegisterPlusOffset(dwarf_reg, offset);
+ uint32_t data =
+ MemARead(context, address + offset, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+ // In ARMv5T and above, this is an interworking branch.
+ if (!LoadWritePC(context, data))
+ return false;
+ }
+
+ // if wback && registers<n> == '0' then R[n] = R[n] - 4*BitCount(registers);
+ if (wback && BitIsClear(registers, n)) {
+ if (!success)
+ return false;
+
+ offset = (addr_byte_size * BitCount(registers)) * -1;
+ context.type = EmulateInstruction::eContextAdjustBaseRegister;
+ context.SetImmediateSigned(offset);
+ addr_t addr = Rn + offset;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ addr))
+ return false;
+ }
+
+ // if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN;
+ if (wback && BitIsSet(registers, n))
+ return WriteBits32Unknown(n);
+ }
+ return true;
+}
+
+// LDMDB loads multiple registers from consecutive memory locations using an
+// address from a base register. The
+// consecutive memory locations end just below this address, and the address of
+// the lowest of those locations can be optionally written back to the base
+// register.
+bool EmulateInstructionARM::EmulateLDMDB(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ address = R[n] - 4*BitCount(registers);
+
+ for i = 0 to 14
+ if registers<i> == '1' then
+ R[i] = MemA[address,4]; address = address + 4;
+ if registers<15> == '1' then
+ LoadWritePC(MemA[address,4]);
+
+ if wback && registers<n> == '0' then R[n] = R[n] - 4*BitCount(registers);
+ if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN; // Only possible for encoding A1
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t n;
+ uint32_t registers = 0;
+ bool wback;
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ switch (encoding) {
+ case eEncodingT1:
+ // n = UInt(Rn); registers = P:M:'0':register_list; wback = (W == '1');
+ n = Bits32(opcode, 19, 16);
+ registers = Bits32(opcode, 15, 0);
+ registers = registers & 0xdfff; // Make sure bit 13 is a zero.
+ wback = BitIsSet(opcode, 21);
+
+ // if n == 15 || BitCount(registers) < 2 || (P == '1' && M == '1') then
+ // UNPREDICTABLE;
+ if ((n == 15) || (BitCount(registers) < 2) ||
+ (BitIsSet(opcode, 14) && BitIsSet(opcode, 15)))
+ return false;
+
+ // if registers<15> == '1' && InITBlock() && !LastInITBlock() then
+ // UNPREDICTABLE;
+ if (BitIsSet(registers, 15) && InITBlock() && !LastInITBlock())
+ return false;
+
+ // if wback && registers<n> == '1' then UNPREDICTABLE;
+ if (wback && BitIsSet(registers, n))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // n = UInt(Rn); registers = register_list; wback = (W == '1');
+ n = Bits32(opcode, 19, 16);
+ registers = Bits32(opcode, 15, 0);
+ wback = BitIsSet(opcode, 21);
+
+ // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE;
+ if ((n == 15) || (BitCount(registers) < 1))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ // address = R[n] - 4*BitCount(registers);
+
+ int32_t offset = 0;
+ addr_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+
+ if (!success)
+ return false;
+
+ addr_t address = Rn - (addr_byte_size * BitCount(registers));
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, dwarf_reg);
+ context.SetRegisterPlusOffset(dwarf_reg, Rn - address);
+
+ for (int i = 0; i < 14; ++i) {
+ if (BitIsSet(registers, i)) {
+ // R[i] = MemA[address,4]; address = address + 4;
+ context.SetRegisterPlusOffset(dwarf_reg, Rn - (address + offset));
+ uint32_t data =
+ MemARead(context, address + offset, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + i,
+ data))
+ return false;
+
+ offset += addr_byte_size;
+ }
+ }
+
+ // if registers<15> == '1' then
+ // LoadWritePC(MemA[address,4]);
+ if (BitIsSet(registers, 15)) {
+ context.SetRegisterPlusOffset(dwarf_reg, offset);
+ uint32_t data =
+ MemARead(context, address + offset, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+ // In ARMv5T and above, this is an interworking branch.
+ if (!LoadWritePC(context, data))
+ return false;
+ }
+
+ // if wback && registers<n> == '0' then R[n] = R[n] - 4*BitCount(registers);
+ if (wback && BitIsClear(registers, n)) {
+ if (!success)
+ return false;
+
+ offset = (addr_byte_size * BitCount(registers)) * -1;
+ context.type = EmulateInstruction::eContextAdjustBaseRegister;
+ context.SetImmediateSigned(offset);
+ addr_t addr = Rn + offset;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ addr))
+ return false;
+ }
+
+ // if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN; // Only
+ // possible for encoding A1
+ if (wback && BitIsSet(registers, n))
+ return WriteBits32Unknown(n);
+ }
+ return true;
+}
+
+// LDMIB loads multiple registers from consecutive memory locations using an
+// address from a base register. The
+// consecutive memory locations start just above this address, and thea ddress
+// of the last of those locations can optinoally be written back to the base
+// register.
+bool EmulateInstructionARM::EmulateLDMIB(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ address = R[n] + 4;
+
+ for i = 0 to 14
+ if registers<i> == '1' then
+ R[i] = MemA[address,4]; address = address + 4;
+ if registers<15> == '1' then
+ LoadWritePC(MemA[address,4]);
+
+ if wback && registers<n> == '0' then R[n] = R[n] + 4*BitCount(registers);
+ if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t n;
+ uint32_t registers = 0;
+ bool wback;
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ switch (encoding) {
+ case eEncodingA1:
+ // n = UInt(Rn); registers = register_list; wback = (W == '1');
+ n = Bits32(opcode, 19, 16);
+ registers = Bits32(opcode, 15, 0);
+ wback = BitIsSet(opcode, 21);
+
+ // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE;
+ if ((n == 15) || (BitCount(registers) < 1))
+ return false;
+
+ break;
+ default:
+ return false;
+ }
+ // address = R[n] + 4;
+
+ int32_t offset = 0;
+ addr_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+
+ if (!success)
+ return false;
+
+ addr_t address = Rn + addr_byte_size;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, dwarf_reg);
+ context.SetRegisterPlusOffset(dwarf_reg, offset);
+
+ for (int i = 0; i < 14; ++i) {
+ if (BitIsSet(registers, i)) {
+ // R[i] = MemA[address,4]; address = address + 4;
+
+ context.SetRegisterPlusOffset(dwarf_reg, offset + addr_byte_size);
+ uint32_t data =
+ MemARead(context, address + offset, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + i,
+ data))
+ return false;
+
+ offset += addr_byte_size;
+ }
+ }
+
+ // if registers<15> == '1' then
+ // LoadWritePC(MemA[address,4]);
+ if (BitIsSet(registers, 15)) {
+ context.SetRegisterPlusOffset(dwarf_reg, offset);
+ uint32_t data =
+ MemARead(context, address + offset, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+ // In ARMv5T and above, this is an interworking branch.
+ if (!LoadWritePC(context, data))
+ return false;
+ }
+
+ // if wback && registers<n> == '0' then R[n] = R[n] + 4*BitCount(registers);
+ if (wback && BitIsClear(registers, n)) {
+ if (!success)
+ return false;
+
+ offset = addr_byte_size * BitCount(registers);
+ context.type = EmulateInstruction::eContextAdjustBaseRegister;
+ context.SetImmediateSigned(offset);
+ addr_t addr = Rn + offset;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ addr))
+ return false;
+ }
+
+ // if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN; // Only
+ // possible for encoding A1
+ if (wback && BitIsSet(registers, n))
+ return WriteBits32Unknown(n);
+ }
+ return true;
+}
+
+// Load Register (immediate) calculates an address from a base register value
+// and an immediate offset, loads a word from memory, and writes to a register.
+// LDR (immediate, Thumb)
+bool EmulateInstructionARM::EmulateLDRRtRnImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if (ConditionPassed())
+ {
+ EncodingSpecificOperations(); NullCheckIfThumbEE(15);
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ data = MemU[address,4];
+ if wback then R[n] = offset_addr;
+ if t == 15 then
+ if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE;
+ elsif UnalignedSupport() || address<1:0> = '00' then
+ R[t] = data;
+ else R[t] = bits(32) UNKNOWN; // Can only apply before ARMv7
+ }
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rt; // the destination register
+ uint32_t Rn; // the base register
+ uint32_t imm32; // the immediate offset used to form the address
+ addr_t offset_addr; // the offset address
+ addr_t address; // the calculated address
+ uint32_t data; // the literal data value from memory load
+ bool add, index, wback;
+ switch (encoding) {
+ case eEncodingT1:
+ Rt = Bits32(opcode, 2, 0);
+ Rn = Bits32(opcode, 5, 3);
+ imm32 = Bits32(opcode, 10, 6) << 2; // imm32 = ZeroExtend(imm5:'00', 32);
+ // index = TRUE; add = TRUE; wback = FALSE
+ add = true;
+ index = true;
+ wback = false;
+
+ break;
+
+ case eEncodingT2:
+ // t = UInt(Rt); n = 13; imm32 = ZeroExtend(imm8:'00', 32);
+ Rt = Bits32(opcode, 10, 8);
+ Rn = 13;
+ imm32 = Bits32(opcode, 7, 0) << 2;
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ break;
+
+ case eEncodingT3:
+ // if Rn == '1111' then SEE LDR (literal);
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32);
+ Rt = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 11, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // if t == 15 && InITBlock() && !LastInITBlock() then UNPREDICTABLE;
+ if ((Rt == 15) && InITBlock() && !LastInITBlock())
+ return false;
+
+ break;
+
+ case eEncodingT4:
+ // if Rn == '1111' then SEE LDR (literal);
+ // if P == '1' && U == '1' && W == '0' then SEE LDRT;
+ // if Rn == '1101' && P == '0' && U == '1' && W == '1' && imm8 ==
+ // '00000100' then SEE POP;
+ // if P == '0' && W == '0' then UNDEFINED;
+ if (BitIsClear(opcode, 10) && BitIsClear(opcode, 8))
+ return false;
+
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32);
+ Rt = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 7, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (W == '1');
+ index = BitIsSet(opcode, 10);
+ add = BitIsSet(opcode, 9);
+ wback = BitIsSet(opcode, 8);
+
+ // if (wback && n == t) || (t == 15 && InITBlock() && !LastInITBlock())
+ // then UNPREDICTABLE;
+ if ((wback && (Rn == Rt)) ||
+ ((Rt == 15) && InITBlock() && !LastInITBlock()))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+ uint32_t base = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+ if (add)
+ offset_addr = base + imm32;
+ else
+ offset_addr = base - imm32;
+
+ address = (index ? offset_addr : base);
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rn, base_reg);
+ if (wback) {
+ EmulateInstruction::Context ctx;
+ if (Rn == 13) {
+ ctx.type = eContextAdjustStackPointer;
+ ctx.SetImmediateSigned((int32_t)(offset_addr - base));
+ } else if (Rn == GetFramePointerRegisterNumber()) {
+ ctx.type = eContextSetFramePointer;
+ ctx.SetRegisterPlusOffset(base_reg, (int32_t)(offset_addr - base));
+ } else {
+ ctx.type = EmulateInstruction::eContextAdjustBaseRegister;
+ ctx.SetRegisterPlusOffset(base_reg, (int32_t)(offset_addr - base));
+ }
+
+ if (!WriteRegisterUnsigned(ctx, eRegisterKindDWARF, dwarf_r0 + Rn,
+ offset_addr))
+ return false;
+ }
+
+ // Prepare to write to the Rt register.
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, (int32_t)(offset_addr - base));
+
+ // Read memory from the address.
+ data = MemURead(context, address, 4, 0, &success);
+ if (!success)
+ return false;
+
+ if (Rt == 15) {
+ if (Bits32(address, 1, 0) == 0) {
+ if (!LoadWritePC(context, data))
+ return false;
+ } else
+ return false;
+ } else if (UnalignedSupport() || Bits32(address, 1, 0) == 0) {
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + Rt,
+ data))
+ return false;
+ } else
+ WriteBits32Unknown(Rt);
+ }
+ return true;
+}
+
+// STM (Store Multiple Increment After) stores multiple registers to consecutive
+// memory locations using an address
+// from a base register. The consecutive memory locations start at this
+// address, and the address just above the last of those locations can
+// optionally be written back to the base register.
+bool EmulateInstructionARM::EmulateSTM(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ address = R[n];
+
+ for i = 0 to 14
+ if registers<i> == '1' then
+ if i == n && wback && i != LowestSetBit(registers) then
+ MemA[address,4] = bits(32) UNKNOWN; // Only possible for encodings T1 and A1
+ else
+ MemA[address,4] = R[i];
+ address = address + 4;
+
+ if registers<15> == '1' then // Only possible for encoding A1
+ MemA[address,4] = PCStoreValue();
+ if wback then R[n] = R[n] + 4*BitCount(registers);
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t n;
+ uint32_t registers = 0;
+ bool wback;
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // n = UInt(Rn); registers = '00000000':register_list; wback = TRUE;
+ n = Bits32(opcode, 10, 8);
+ registers = Bits32(opcode, 7, 0);
+ registers = registers & 0x00ff; // Make sure the top 8 bits are zeros.
+ wback = true;
+
+ // if BitCount(registers) < 1 then UNPREDICTABLE;
+ if (BitCount(registers) < 1)
+ return false;
+
+ break;
+
+ case eEncodingT2:
+ // n = UInt(Rn); registers = '0':M:'0':register_list; wback = (W == '1');
+ n = Bits32(opcode, 19, 16);
+ registers = Bits32(opcode, 15, 0);
+ registers = registers & 0x5fff; // Make sure bits 15 & 13 are zeros.
+ wback = BitIsSet(opcode, 21);
+
+ // if n == 15 || BitCount(registers) < 2 then UNPREDICTABLE;
+ if ((n == 15) || (BitCount(registers) < 2))
+ return false;
+
+ // if wback && registers<n> == '1' then UNPREDICTABLE;
+ if (wback && BitIsSet(registers, n))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // n = UInt(Rn); registers = register_list; wback = (W == '1');
+ n = Bits32(opcode, 19, 16);
+ registers = Bits32(opcode, 15, 0);
+ wback = BitIsSet(opcode, 21);
+
+ // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE;
+ if ((n == 15) || (BitCount(registers) < 1))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ // address = R[n];
+ int32_t offset = 0;
+ const addr_t address =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRegisterStore;
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ // for i = 0 to 14
+ uint32_t lowest_set_bit = 14;
+ for (uint32_t i = 0; i < 14; ++i) {
+ // if registers<i> == '1' then
+ if (BitIsSet(registers, i)) {
+ if (i < lowest_set_bit)
+ lowest_set_bit = i;
+ // if i == n && wback && i != LowestSetBit(registers) then
+ if ((i == n) && wback && (i != lowest_set_bit))
+ // MemA[address,4] = bits(32) UNKNOWN; // Only possible for encodings
+ // T1 and A1
+ WriteBits32UnknownToMemory(address + offset);
+ else {
+ // MemA[address,4] = R[i];
+ uint32_t data = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + i,
+ 0, &success);
+ if (!success)
+ return false;
+
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + i, data_reg);
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg, offset);
+ if (!MemAWrite(context, address + offset, data, addr_byte_size))
+ return false;
+ }
+
+ // address = address + 4;
+ offset += addr_byte_size;
+ }
+ }
+
+ // if registers<15> == '1' then // Only possible for encoding A1
+ // MemA[address,4] = PCStoreValue();
+ if (BitIsSet(registers, 15)) {
+ RegisterInfo pc_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_pc, pc_reg);
+ context.SetRegisterPlusOffset(pc_reg, 8);
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+
+ if (!MemAWrite(context, address + offset, pc, addr_byte_size))
+ return false;
+ }
+
+ // if wback then R[n] = R[n] + 4*BitCount(registers);
+ if (wback) {
+ offset = addr_byte_size * BitCount(registers);
+ context.type = EmulateInstruction::eContextAdjustBaseRegister;
+ context.SetImmediateSigned(offset);
+ addr_t data = address + offset;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ data))
+ return false;
+ }
+ }
+ return true;
+}
+
+// STMDA (Store Multiple Decrement After) stores multiple registers to
+// consecutive memory locations using an address from a base register. The
+// consecutive memory locations end at this address, and the address just below
+// the lowest of those locations can optionally be written back to the base
+// register.
+bool EmulateInstructionARM::EmulateSTMDA(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ address = R[n] - 4*BitCount(registers) + 4;
+
+ for i = 0 to 14
+ if registers<i> == '1' then
+ if i == n && wback && i != LowestSetBit(registers) then
+ MemA[address,4] = bits(32) UNKNOWN;
+ else
+ MemA[address,4] = R[i];
+ address = address + 4;
+
+ if registers<15> == '1' then
+ MemA[address,4] = PCStoreValue();
+
+ if wback then R[n] = R[n] - 4*BitCount(registers);
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t n;
+ uint32_t registers = 0;
+ bool wback;
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ // EncodingSpecificOperations();
+ switch (encoding) {
+ case eEncodingA1:
+ // n = UInt(Rn); registers = register_list; wback = (W == '1');
+ n = Bits32(opcode, 19, 16);
+ registers = Bits32(opcode, 15, 0);
+ wback = BitIsSet(opcode, 21);
+
+ // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE;
+ if ((n == 15) || (BitCount(registers) < 1))
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ // address = R[n] - 4*BitCount(registers) + 4;
+ int32_t offset = 0;
+ addr_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ addr_t address = Rn - (addr_byte_size * BitCount(registers)) + 4;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRegisterStore;
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ // for i = 0 to 14
+ uint32_t lowest_bit_set = 14;
+ for (uint32_t i = 0; i < 14; ++i) {
+ // if registers<i> == '1' then
+ if (BitIsSet(registers, i)) {
+ if (i < lowest_bit_set)
+ lowest_bit_set = i;
+ // if i == n && wback && i != LowestSetBit(registers) then
+ if ((i == n) && wback && (i != lowest_bit_set))
+ // MemA[address,4] = bits(32) UNKNOWN;
+ WriteBits32UnknownToMemory(address + offset);
+ else {
+ // MemA[address,4] = R[i];
+ uint32_t data = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + i,
+ 0, &success);
+ if (!success)
+ return false;
+
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + i, data_reg);
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ Rn - (address + offset));
+ if (!MemAWrite(context, address + offset, data, addr_byte_size))
+ return false;
+ }
+
+ // address = address + 4;
+ offset += addr_byte_size;
+ }
+ }
+
+ // if registers<15> == '1' then
+ // MemA[address,4] = PCStoreValue();
+ if (BitIsSet(registers, 15)) {
+ RegisterInfo pc_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_pc, pc_reg);
+ context.SetRegisterPlusOffset(pc_reg, 8);
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+
+ if (!MemAWrite(context, address + offset, pc, addr_byte_size))
+ return false;
+ }
+
+ // if wback then R[n] = R[n] - 4*BitCount(registers);
+ if (wback) {
+ offset = (addr_byte_size * BitCount(registers)) * -1;
+ context.type = EmulateInstruction::eContextAdjustBaseRegister;
+ context.SetImmediateSigned(offset);
+ addr_t data = Rn + offset;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ data))
+ return false;
+ }
+ }
+ return true;
+}
+
+// STMDB (Store Multiple Decrement Before) stores multiple registers to
+// consecutive memory locations using an address from a base register. The
+// consecutive memory locations end just below this address, and the address of
+// the first of those locations can optionally be written back to the base
+// register.
+bool EmulateInstructionARM::EmulateSTMDB(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ address = R[n] - 4*BitCount(registers);
+
+ for i = 0 to 14
+ if registers<i> == '1' then
+ if i == n && wback && i != LowestSetBit(registers) then
+ MemA[address,4] = bits(32) UNKNOWN; // Only possible for encoding A1
+ else
+ MemA[address,4] = R[i];
+ address = address + 4;
+
+ if registers<15> == '1' then // Only possible for encoding A1
+ MemA[address,4] = PCStoreValue();
+
+ if wback then R[n] = R[n] - 4*BitCount(registers);
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t n;
+ uint32_t registers = 0;
+ bool wback;
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // if W == '1' && Rn == '1101' then SEE PUSH;
+ if ((BitIsSet(opcode, 21)) && (Bits32(opcode, 19, 16) == 13)) {
+ // See PUSH
+ }
+ // n = UInt(Rn); registers = '0':M:'0':register_list; wback = (W == '1');
+ n = Bits32(opcode, 19, 16);
+ registers = Bits32(opcode, 15, 0);
+ registers = registers & 0x5fff; // Make sure bits 15 & 13 are zeros.
+ wback = BitIsSet(opcode, 21);
+ // if n == 15 || BitCount(registers) < 2 then UNPREDICTABLE;
+ if ((n == 15) || BitCount(registers) < 2)
+ return false;
+ // if wback && registers<n> == '1' then UNPREDICTABLE;
+ if (wback && BitIsSet(registers, n))
+ return false;
+ break;
+
+ case eEncodingA1:
+ // if W == '1' && Rn == '1101' && BitCount(register_list) >= 2 then SEE
+ // PUSH;
+ if (BitIsSet(opcode, 21) && (Bits32(opcode, 19, 16) == 13) &&
+ BitCount(Bits32(opcode, 15, 0)) >= 2) {
+ // See Push
+ }
+ // n = UInt(Rn); registers = register_list; wback = (W == '1');
+ n = Bits32(opcode, 19, 16);
+ registers = Bits32(opcode, 15, 0);
+ wback = BitIsSet(opcode, 21);
+ // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE;
+ if ((n == 15) || BitCount(registers) < 1)
+ return false;
+ break;
+
+ default:
+ return false;
+ }
+
+ // address = R[n] - 4*BitCount(registers);
+
+ int32_t offset = 0;
+ addr_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ addr_t address = Rn - (addr_byte_size * BitCount(registers));
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRegisterStore;
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ // for i = 0 to 14
+ uint32_t lowest_set_bit = 14;
+ for (uint32_t i = 0; i < 14; ++i) {
+ // if registers<i> == '1' then
+ if (BitIsSet(registers, i)) {
+ if (i < lowest_set_bit)
+ lowest_set_bit = i;
+ // if i == n && wback && i != LowestSetBit(registers) then
+ if ((i == n) && wback && (i != lowest_set_bit))
+ // MemA[address,4] = bits(32) UNKNOWN; // Only possible for encoding
+ // A1
+ WriteBits32UnknownToMemory(address + offset);
+ else {
+ // MemA[address,4] = R[i];
+ uint32_t data = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + i,
+ 0, &success);
+ if (!success)
+ return false;
+
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + i, data_reg);
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ Rn - (address + offset));
+ if (!MemAWrite(context, address + offset, data, addr_byte_size))
+ return false;
+ }
+
+ // address = address + 4;
+ offset += addr_byte_size;
+ }
+ }
+
+ // if registers<15> == '1' then // Only possible for encoding A1
+ // MemA[address,4] = PCStoreValue();
+ if (BitIsSet(registers, 15)) {
+ RegisterInfo pc_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_pc, pc_reg);
+ context.SetRegisterPlusOffset(pc_reg, 8);
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+
+ if (!MemAWrite(context, address + offset, pc, addr_byte_size))
+ return false;
+ }
+
+ // if wback then R[n] = R[n] - 4*BitCount(registers);
+ if (wback) {
+ offset = (addr_byte_size * BitCount(registers)) * -1;
+ context.type = EmulateInstruction::eContextAdjustBaseRegister;
+ context.SetImmediateSigned(offset);
+ addr_t data = Rn + offset;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ data))
+ return false;
+ }
+ }
+ return true;
+}
+
+// STMIB (Store Multiple Increment Before) stores multiple registers to
+// consecutive memory locations using an address from a base register. The
+// consecutive memory locations start just above this address, and the address
+// of the last of those locations can optionally be written back to the base
+// register.
+bool EmulateInstructionARM::EmulateSTMIB(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ address = R[n] + 4;
+
+ for i = 0 to 14
+ if registers<i> == '1' then
+ if i == n && wback && i != LowestSetBit(registers) then
+ MemA[address,4] = bits(32) UNKNOWN;
+ else
+ MemA[address,4] = R[i];
+ address = address + 4;
+
+ if registers<15> == '1' then
+ MemA[address,4] = PCStoreValue();
+
+ if wback then R[n] = R[n] + 4*BitCount(registers);
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t n;
+ uint32_t registers = 0;
+ bool wback;
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ // EncodingSpecificOperations();
+ switch (encoding) {
+ case eEncodingA1:
+ // n = UInt(Rn); registers = register_list; wback = (W == '1');
+ n = Bits32(opcode, 19, 16);
+ registers = Bits32(opcode, 15, 0);
+ wback = BitIsSet(opcode, 21);
+
+ // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE;
+ if ((n == 15) && (BitCount(registers) < 1))
+ return false;
+ break;
+ default:
+ return false;
+ }
+ // address = R[n] + 4;
+
+ int32_t offset = 0;
+ addr_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ addr_t address = Rn + addr_byte_size;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRegisterStore;
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ uint32_t lowest_set_bit = 14;
+ // for i = 0 to 14
+ for (uint32_t i = 0; i < 14; ++i) {
+ // if registers<i> == '1' then
+ if (BitIsSet(registers, i)) {
+ if (i < lowest_set_bit)
+ lowest_set_bit = i;
+ // if i == n && wback && i != LowestSetBit(registers) then
+ if ((i == n) && wback && (i != lowest_set_bit))
+ // MemA[address,4] = bits(32) UNKNOWN;
+ WriteBits32UnknownToMemory(address + offset);
+ // else
+ else {
+ // MemA[address,4] = R[i];
+ uint32_t data = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + i,
+ 0, &success);
+ if (!success)
+ return false;
+
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + i, data_reg);
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ offset + addr_byte_size);
+ if (!MemAWrite(context, address + offset, data, addr_byte_size))
+ return false;
+ }
+
+ // address = address + 4;
+ offset += addr_byte_size;
+ }
+ }
+
+ // if registers<15> == '1' then
+ // MemA[address,4] = PCStoreValue();
+ if (BitIsSet(registers, 15)) {
+ RegisterInfo pc_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_pc, pc_reg);
+ context.SetRegisterPlusOffset(pc_reg, 8);
+ const uint32_t pc = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+
+ if (!MemAWrite(context, address + offset, pc, addr_byte_size))
+ return false;
+ }
+
+ // if wback then R[n] = R[n] + 4*BitCount(registers);
+ if (wback) {
+ offset = addr_byte_size * BitCount(registers);
+ context.type = EmulateInstruction::eContextAdjustBaseRegister;
+ context.SetImmediateSigned(offset);
+ addr_t data = Rn + offset;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ data))
+ return false;
+ }
+ }
+ return true;
+}
+
+// STR (store immediate) calculates an address from a base register value and an
+// immediate offset, and stores a word
+// from a register to memory. It can use offset, post-indexed, or pre-indexed
+// addressing.
+bool EmulateInstructionARM::EmulateSTRThumb(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ if UnalignedSupport() || address<1:0> == '00' then
+ MemU[address,4] = R[t];
+ else // Can only occur before ARMv7
+ MemU[address,4] = bits(32) UNKNOWN;
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ uint32_t t;
+ uint32_t n;
+ uint32_t imm32;
+ bool index;
+ bool add;
+ bool wback;
+ // EncodingSpecificOperations (); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5:'00', 32);
+ t = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ imm32 = Bits32(opcode, 10, 6) << 2;
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = false;
+ wback = false;
+ break;
+
+ case eEncodingT2:
+ // t = UInt(Rt); n = 13; imm32 = ZeroExtend(imm8:'00', 32);
+ t = Bits32(opcode, 10, 8);
+ n = 13;
+ imm32 = Bits32(opcode, 7, 0) << 2;
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+ break;
+
+ case eEncodingT3:
+ // if Rn == '1111' then UNDEFINED;
+ if (Bits32(opcode, 19, 16) == 15)
+ return false;
+
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 11, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // if t == 15 then UNPREDICTABLE;
+ if (t == 15)
+ return false;
+ break;
+
+ case eEncodingT4:
+ // if P == '1' && U == '1' && W == '0' then SEE STRT;
+ // if Rn == '1101' && P == '1' && U == '0' && W == '1' && imm8 ==
+ // '00000100' then SEE PUSH;
+ // if Rn == '1111' || (P == '0' && W == '0') then UNDEFINED;
+ if ((Bits32(opcode, 19, 16) == 15) ||
+ (BitIsClear(opcode, 10) && BitIsClear(opcode, 8)))
+ return false;
+
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 7, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (W == '1');
+ index = BitIsSet(opcode, 10);
+ add = BitIsSet(opcode, 9);
+ wback = BitIsSet(opcode, 8);
+
+ // if t == 15 || (wback && n == t) then UNPREDICTABLE;
+ if ((t == 15) || (wback && (n == t)))
+ return false;
+ break;
+
+ default:
+ return false;
+ }
+
+ addr_t offset_addr;
+ addr_t address;
+
+ // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ uint32_t base_address = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ if (add)
+ offset_addr = base_address + imm32;
+ else
+ offset_addr = base_address - imm32;
+
+ // address = if index then offset_addr else R[n];
+ if (index)
+ address = offset_addr;
+ else
+ address = base_address;
+
+ EmulateInstruction::Context context;
+ if (n == 13)
+ context.type = eContextPushRegisterOnStack;
+ else
+ context.type = eContextRegisterStore;
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ // if UnalignedSupport() || address<1:0> == '00' then
+ if (UnalignedSupport() ||
+ (BitIsClear(address, 1) && BitIsClear(address, 0))) {
+ // MemU[address,4] = R[t];
+ uint32_t data =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + t, 0, &success);
+ if (!success)
+ return false;
+
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t, data_reg);
+ int32_t offset = address - base_address;
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg, offset);
+ if (!MemUWrite(context, address, data, addr_byte_size))
+ return false;
+ } else {
+ // MemU[address,4] = bits(32) UNKNOWN;
+ WriteBits32UnknownToMemory(address);
+ }
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ if (n == 13)
+ context.type = eContextAdjustStackPointer;
+ else
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+ return true;
+}
+
+// STR (Store Register) calculates an address from a base register value and an
+// offset register value, stores a
+// word from a register to memory. The offset register value can optionally
+// be shifted.
+bool EmulateInstructionARM::EmulateSTRRegister(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset = Shift(R[m], shift_t, shift_n, APSR.C);
+ offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ address = if index then offset_addr else R[n];
+ if t == 15 then // Only possible for encoding A1
+ data = PCStoreValue();
+ else
+ data = R[t];
+ if UnalignedSupport() || address<1:0> == '00' || CurrentInstrSet() == InstrSet_ARM then
+ MemU[address,4] = data;
+ else // Can only occur before ARMv7
+ MemU[address,4] = bits(32) UNKNOWN;
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ uint32_t t;
+ uint32_t n;
+ uint32_t m;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n;
+ bool index;
+ bool add;
+ bool wback;
+
+ // EncodingSpecificOperations (); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation
+ // in ThumbEE";
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ m = Bits32(opcode, 8, 6);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, 0);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ break;
+
+ case eEncodingT2:
+ // if Rn == '1111' then UNDEFINED;
+ if (Bits32(opcode, 19, 16) == 15)
+ return false;
+
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, UInt(imm2));
+ shift_t = SRType_LSL;
+ shift_n = Bits32(opcode, 5, 4);
+
+ // if t == 15 || BadReg(m) then UNPREDICTABLE;
+ if ((t == 15) || (BadReg(m)))
+ return false;
+ break;
+
+ case eEncodingA1: {
+ // if P == '0' && W == '1' then SEE STRT;
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') ||
+ // (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21));
+
+ // (shift_t, shift_n) = DecodeImmShift(type, imm5);
+ uint32_t typ = Bits32(opcode, 6, 5);
+ uint32_t imm5 = Bits32(opcode, 11, 7);
+ shift_n = DecodeImmShift(typ, imm5, shift_t);
+
+ // if m == 15 then UNPREDICTABLE;
+ if (m == 15)
+ return false;
+
+ // if wback && (n == 15 || n == t) then UNPREDICTABLE;
+ if (wback && ((n == 15) || (n == t)))
+ return false;
+
+ break;
+ }
+ default:
+ return false;
+ }
+
+ addr_t offset_addr;
+ addr_t address;
+ int32_t offset = 0;
+
+ addr_t base_address =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ uint32_t Rm_data =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success);
+ if (!success)
+ return false;
+
+ // offset = Shift(R[m], shift_t, shift_n, APSR.C);
+ offset = Shift(Rm_data, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+
+ // offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ if (add)
+ offset_addr = base_address + offset;
+ else
+ offset_addr = base_address - offset;
+
+ // address = if index then offset_addr else R[n];
+ if (index)
+ address = offset_addr;
+ else
+ address = base_address;
+
+ uint32_t data;
+ // if t == 15 then // Only possible for encoding A1
+ if (t == 15)
+ // data = PCStoreValue();
+ data = ReadCoreReg(PC_REG, &success);
+ else
+ // data = R[t];
+ data =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + t, 0, &success);
+
+ if (!success)
+ return false;
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterStore;
+
+ // if UnalignedSupport() || address<1:0> == '00' || CurrentInstrSet() ==
+ // InstrSet_ARM then
+ if (UnalignedSupport() ||
+ (BitIsClear(address, 1) && BitIsClear(address, 0)) ||
+ CurrentInstrSet() == eModeARM) {
+ // MemU[address,4] = data;
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t, data_reg);
+
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ address - base_address);
+ if (!MemUWrite(context, address, data, addr_byte_size))
+ return false;
+
+ } else
+ // MemU[address,4] = bits(32) UNKNOWN;
+ WriteBits32UnknownToMemory(address);
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextRegisterLoad;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+ return true;
+}
+
+bool EmulateInstructionARM::EmulateSTRBThumb(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ MemU[address,1] = R[t]<7:0>;
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t n;
+ uint32_t imm32;
+ bool index;
+ bool add;
+ bool wback;
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5, 32);
+ t = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ imm32 = Bits32(opcode, 10, 6);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+ break;
+
+ case eEncodingT2:
+ // if Rn == '1111' then UNDEFINED;
+ if (Bits32(opcode, 19, 16) == 15)
+ return false;
+
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 11, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // if BadReg(t) then UNPREDICTABLE;
+ if (BadReg(t))
+ return false;
+ break;
+
+ case eEncodingT3:
+ // if P == '1' && U == '1' && W == '0' then SEE STRBT;
+ // if Rn == '1111' || (P == '0' && W == '0') then UNDEFINED;
+ if (Bits32(opcode, 19, 16) == 15)
+ return false;
+
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 7, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (W == '1');
+ index = BitIsSet(opcode, 10);
+ add = BitIsSet(opcode, 9);
+ wback = BitIsSet(opcode, 8);
+
+ // if BadReg(t) || (wback && n == t) then UNPREDICTABLE
+ if ((BadReg(t)) || (wback && (n == t)))
+ return false;
+ break;
+
+ default:
+ return false;
+ }
+
+ addr_t offset_addr;
+ addr_t address;
+ addr_t base_address =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ if (add)
+ offset_addr = base_address + imm32;
+ else
+ offset_addr = base_address - imm32;
+
+ // address = if index then offset_addr else R[n];
+ if (index)
+ address = offset_addr;
+ else
+ address = base_address;
+
+ // MemU[address,1] = R[t]<7:0>
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t, data_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterStore;
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ address - base_address);
+
+ uint32_t data =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + t, 0, &success);
+ if (!success)
+ return false;
+
+ data = Bits32(data, 7, 0);
+
+ if (!MemUWrite(context, address, data, 1))
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextRegisterLoad;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// STRH (register) calculates an address from a base register value and an
+// offset register value, and stores a
+// halfword from a register to memory. The offset register value can be
+// shifted left by 0, 1, 2, or 3 bits.
+bool EmulateInstructionARM::EmulateSTRHRegister(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset = Shift(R[m], shift_t, shift_n, APSR.C);
+ offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ address = if index then offset_addr else R[n];
+ if UnalignedSupport() || address<0> == '0' then
+ MemU[address,2] = R[t]<15:0>;
+ else // Can only occur before ARMv7
+ MemU[address,2] = bits(16) UNKNOWN;
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t n;
+ uint32_t m;
+ bool index;
+ bool add;
+ bool wback;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n;
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation
+ // in ThumbEE";
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ m = Bits32(opcode, 8, 6);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, 0);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+
+ break;
+
+ case eEncodingT2:
+ // if Rn == '1111' then UNDEFINED;
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+ if (n == 15)
+ return false;
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, UInt(imm2));
+ shift_t = SRType_LSL;
+ shift_n = Bits32(opcode, 5, 4);
+
+ // if BadReg(t) || BadReg(m) then UNPREDICTABLE;
+ if (BadReg(t) || BadReg(m))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // if P == '0' && W == '1' then SEE STRHT;
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') ||
+ // (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21));
+
+ // (shift_t, shift_n) = (SRType_LSL, 0);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+
+ // if t == 15 || m == 15 then UNPREDICTABLE;
+ if ((t == 15) || (m == 15))
+ return false;
+
+ // if wback && (n == 15 || n == t) then UNPREDICTABLE;
+ if (wback && ((n == 15) || (n == t)))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ uint32_t Rm = ReadCoreReg(m, &success);
+ if (!success)
+ return false;
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ // offset = Shift(R[m], shift_t, shift_n, APSR.C);
+ uint32_t offset = Shift(Rm, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+
+ // offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ addr_t offset_addr;
+ if (add)
+ offset_addr = Rn + offset;
+ else
+ offset_addr = Rn - offset;
+
+ // address = if index then offset_addr else R[n];
+ addr_t address;
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterStore;
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+ RegisterInfo offset_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, offset_reg);
+
+ // if UnalignedSupport() || address<0> == '0' then
+ if (UnalignedSupport() || BitIsClear(address, 0)) {
+ // MemU[address,2] = R[t]<15:0>;
+ uint32_t Rt = ReadCoreReg(t, &success);
+ if (!success)
+ return false;
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterStore;
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+ RegisterInfo offset_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, offset_reg);
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t, data_reg);
+ context.SetRegisterToRegisterPlusIndirectOffset(base_reg, offset_reg,
+ data_reg);
+
+ if (!MemUWrite(context, address, Bits32(Rt, 15, 0), 2))
+ return false;
+ } else // Can only occur before ARMv7
+ {
+ // MemU[address,2] = bits(16) UNKNOWN;
+ }
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Add with Carry (immediate) adds an immediate value and the carry flag value
+// to a register value, and writes the result to the destination register. It
+// can optionally update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateADCImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(R[n], imm32, APSR.C);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd, Rn;
+ uint32_t
+ imm32; // the immediate value to be added to the value obtained from Rn
+ bool setflags;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8)
+ if (BadReg(Rd) || BadReg(Rn))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ int32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult res = AddWithCarry(val1, imm32, APSR_C);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow))
+ return false;
+ }
+ return true;
+}
+
+// Add with Carry (register) adds a register value, the carry flag value, and
+// an optionally-shifted register value, and writes the result to the
+// destination register. It can optionally update the condition flags based on
+// the result.
+bool EmulateInstructionARM::EmulateADCReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ (result, carry, overflow) = AddWithCarry(R[n], shifted, APSR.C);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd, Rn, Rm;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ bool setflags;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Rn = Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 5, 3);
+ setflags = !InITBlock();
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ break;
+ case eEncodingT2:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+ if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ int32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the second operand.
+ int32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+ AddWithCarryResult res = AddWithCarry(val1, shifted, APSR_C);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow))
+ return false;
+ }
+ return true;
+}
+
+// This instruction adds an immediate value to the PC value to form a PC-
+// relative address, and writes the result to the destination register.
+bool EmulateInstructionARM::EmulateADR(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ result = if add then (Align(PC,4) + imm32) else (Align(PC,4) - imm32);
+ if d == 15 then // Can only occur for ARM encodings
+ ALUWritePC(result);
+ else
+ R[d] = result;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd;
+ uint32_t imm32; // the immediate value to be added/subtracted to/from the PC
+ bool add;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 10, 8);
+ imm32 = ThumbImm8Scaled(opcode); // imm32 = ZeroExtend(imm8:'00', 32)
+ add = true;
+ break;
+ case eEncodingT2:
+ case eEncodingT3:
+ Rd = Bits32(opcode, 11, 8);
+ imm32 = ThumbImm12(opcode); // imm32 = ZeroExtend(i:imm3:imm8, 32)
+ add = (Bits32(opcode, 24, 21) == 0); // 0b0000 => ADD; 0b0101 => SUB
+ if (BadReg(Rd))
+ return false;
+ break;
+ case eEncodingA1:
+ case eEncodingA2:
+ Rd = Bits32(opcode, 15, 12);
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+ add = (Bits32(opcode, 24, 21) == 0x4); // 0b0100 => ADD; 0b0010 => SUB
+ break;
+ default:
+ return false;
+ }
+
+ // Read the PC value.
+ uint32_t pc = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+
+ uint32_t result = (add ? Align(pc, 4) + imm32 : Align(pc, 4) - imm32);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreReg(context, result, Rd))
+ return false;
+ }
+ return true;
+}
+
+// This instruction performs a bitwise AND of a register value and an immediate
+// value, and writes the result to the destination register. It can optionally
+// update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateANDImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ result = R[n] AND imm32;
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd, Rn;
+ uint32_t
+ imm32; // the immediate value to be ANDed to the value obtained from Rn
+ bool setflags;
+ uint32_t carry; // the carry bit after ARM/Thumb Expand operation
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbExpandImm_C(
+ opcode, APSR_C,
+ carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C)
+ // if Rd == '1111' && S == '1' then SEE TST (immediate);
+ if (Rd == 15 && setflags)
+ return EmulateTSTImm(opcode, eEncodingT1);
+ if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 =
+ ARMExpandImm_C(opcode, APSR_C,
+ carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C)
+
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ uint32_t result = val1 & imm32;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+// This instruction performs a bitwise AND of a register value and an
+// optionally-shifted register value, and writes the result to the destination
+// register. It can optionally update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateANDReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C);
+ result = R[n] AND shifted;
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd, Rn, Rm;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ bool setflags;
+ uint32_t carry;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Rn = Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 5, 3);
+ setflags = !InITBlock();
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ break;
+ case eEncodingT2:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+ // if Rd == '1111' && S == '1' then SEE TST (register);
+ if (Rd == 15 && setflags)
+ return EmulateTSTReg(opcode, eEncodingT2);
+ if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn) || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the second operand.
+ uint32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success);
+ if (!success)
+ return false;
+ uint32_t result = val1 & shifted;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+// Bitwise Bit Clear (immediate) performs a bitwise AND of a register value and
+// the complement of an immediate value, and writes the result to the
+// destination register. It can optionally update the condition flags based on
+// the result.
+bool EmulateInstructionARM::EmulateBICImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ result = R[n] AND NOT(imm32);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd, Rn;
+ uint32_t imm32; // the immediate value to be bitwise inverted and ANDed to
+ // the value obtained from Rn
+ bool setflags;
+ uint32_t carry; // the carry bit after ARM/Thumb Expand operation
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbExpandImm_C(
+ opcode, APSR_C,
+ carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C)
+ if (BadReg(Rd) || BadReg(Rn))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 =
+ ARMExpandImm_C(opcode, APSR_C,
+ carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C)
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ uint32_t result = val1 & ~imm32;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+// Bitwise Bit Clear (register) performs a bitwise AND of a register value and
+// the complement of an optionally-shifted register value, and writes the
+// result to the destination register. It can optionally update the condition
+// flags based on the result.
+bool EmulateInstructionARM::EmulateBICReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C);
+ result = R[n] AND NOT(shifted);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd, Rn, Rm;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ bool setflags;
+ uint32_t carry;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Rn = Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 5, 3);
+ setflags = !InITBlock();
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ break;
+ case eEncodingT2:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+ if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the second operand.
+ uint32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success);
+ if (!success)
+ return false;
+ uint32_t result = val1 & ~shifted;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+// LDR (immediate, ARM) calculates an address from a base register value and an
+// immediate offset, loads a word
+// from memory, and writes it to a register. It can use offset, post-indexed,
+// or pre-indexed addressing.
+bool EmulateInstructionARM::EmulateLDRImmediateARM(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ data = MemU[address,4];
+ if wback then R[n] = offset_addr;
+ if t == 15 then
+ if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE;
+ elsif UnalignedSupport() || address<1:0> = '00' then
+ R[t] = data;
+ else // Can only apply before ARMv7
+ R[t] = ROR(data, 8*UInt(address<1:0>));
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ uint32_t t;
+ uint32_t n;
+ uint32_t imm32;
+ bool index;
+ bool add;
+ bool wback;
+
+ switch (encoding) {
+ case eEncodingA1:
+ // if Rn == '1111' then SEE LDR (literal);
+ // if P == '0' && W == '1' then SEE LDRT;
+ // if Rn == '1101' && P == '0' && U == '1' && W == '0' && imm12 ==
+ // '000000000100' then SEE POP;
+ // t == UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 11, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') ||
+ // (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21));
+
+ // if wback && n == t then UNPREDICTABLE;
+ if (wback && (n == t))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ addr_t address;
+ addr_t offset_addr;
+ addr_t base_address = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ if (add)
+ offset_addr = base_address + imm32;
+ else
+ offset_addr = base_address - imm32;
+
+ // address = if index then offset_addr else R[n];
+ if (index)
+ address = offset_addr;
+ else
+ address = base_address;
+
+ // data = MemU[address,4];
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - base_address);
+
+ uint64_t data = MemURead(context, address, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+
+ // if t == 15 then
+ if (t == 15) {
+ // if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE;
+ if (BitIsClear(address, 1) && BitIsClear(address, 0)) {
+ // LoadWritePC (data);
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - base_address);
+ LoadWritePC(context, data);
+ } else
+ return false;
+ }
+ // elsif UnalignedSupport() || address<1:0> = '00' then
+ else if (UnalignedSupport() ||
+ (BitIsClear(address, 1) && BitIsClear(address, 0))) {
+ // R[t] = data;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - base_address);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ data))
+ return false;
+ }
+ // else // Can only apply before ARMv7
+ else {
+ // R[t] = ROR(data, 8*UInt(address<1:0>));
+ data = ROR(data, Bits32(address, 1, 0), &success);
+ if (!success)
+ return false;
+ context.type = eContextRegisterLoad;
+ context.SetImmediate(data);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ data))
+ return false;
+ }
+ }
+ return true;
+}
+
+// LDR (register) calculates an address from a base register value and an offset
+// register value, loads a word
+// from memory, and writes it to a register. The offset register value can
+// optionally be shifted.
+bool EmulateInstructionARM::EmulateLDRRegister(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset = Shift(R[m], shift_t, shift_n, APSR.C);
+ offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ address = if index then offset_addr else R[n];
+ data = MemU[address,4];
+ if wback then R[n] = offset_addr;
+ if t == 15 then
+ if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE;
+ elsif UnalignedSupport() || address<1:0> = '00' then
+ R[t] = data;
+ else // Can only apply before ARMv7
+ if CurrentInstrSet() == InstrSet_ARM then
+ R[t] = ROR(data, 8*UInt(address<1:0>));
+ else
+ R[t] = bits(32) UNKNOWN;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ uint32_t t;
+ uint32_t n;
+ uint32_t m;
+ bool index;
+ bool add;
+ bool wback;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n;
+
+ switch (encoding) {
+ case eEncodingT1:
+ // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation
+ // in ThumbEE";
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ m = Bits32(opcode, 8, 6);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, 0);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+
+ break;
+
+ case eEncodingT2:
+ // if Rn == '1111' then SEE LDR (literal);
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, UInt(imm2));
+ shift_t = SRType_LSL;
+ shift_n = Bits32(opcode, 5, 4);
+
+ // if BadReg(m) then UNPREDICTABLE;
+ if (BadReg(m))
+ return false;
+
+ // if t == 15 && InITBlock() && !LastInITBlock() then UNPREDICTABLE;
+ if ((t == 15) && InITBlock() && !LastInITBlock())
+ return false;
+
+ break;
+
+ case eEncodingA1: {
+ // if P == '0' && W == '1' then SEE LDRT;
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') ||
+ // (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21));
+
+ // (shift_t, shift_n) = DecodeImmShift(type, imm5);
+ uint32_t type = Bits32(opcode, 6, 5);
+ uint32_t imm5 = Bits32(opcode, 11, 7);
+ shift_n = DecodeImmShift(type, imm5, shift_t);
+
+ // if m == 15 then UNPREDICTABLE;
+ if (m == 15)
+ return false;
+
+ // if wback && (n == 15 || n == t) then UNPREDICTABLE;
+ if (wback && ((n == 15) || (n == t)))
+ return false;
+ } break;
+
+ default:
+ return false;
+ }
+
+ uint32_t Rm =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success);
+ if (!success)
+ return false;
+
+ uint32_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ addr_t offset_addr;
+ addr_t address;
+
+ // offset = Shift(R[m], shift_t, shift_n, APSR.C); -- Note "The APSR is
+ // an application level alias for the CPSR".
+ addr_t offset =
+ Shift(Rm, shift_t, shift_n, Bit32(m_opcode_cpsr, APSR_C), &success);
+ if (!success)
+ return false;
+
+ // offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ if (add)
+ offset_addr = Rn + offset;
+ else
+ offset_addr = Rn - offset;
+
+ // address = if index then offset_addr else R[n];
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ // data = MemU[address,4];
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+
+ uint64_t data = MemURead(context, address, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+
+ // if t == 15 then
+ if (t == 15) {
+ // if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE;
+ if (BitIsClear(address, 1) && BitIsClear(address, 0)) {
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+ LoadWritePC(context, data);
+ } else
+ return false;
+ }
+ // elsif UnalignedSupport() || address<1:0> = '00' then
+ else if (UnalignedSupport() ||
+ (BitIsClear(address, 1) && BitIsClear(address, 0))) {
+ // R[t] = data;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ data))
+ return false;
+ } else // Can only apply before ARMv7
+ {
+ // if CurrentInstrSet() == InstrSet_ARM then
+ if (CurrentInstrSet() == eModeARM) {
+ // R[t] = ROR(data, 8*UInt(address<1:0>));
+ data = ROR(data, Bits32(address, 1, 0), &success);
+ if (!success)
+ return false;
+ context.type = eContextRegisterLoad;
+ context.SetImmediate(data);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ data))
+ return false;
+ } else {
+ // R[t] = bits(32) UNKNOWN;
+ WriteBits32Unknown(t);
+ }
+ }
+ }
+ return true;
+}
+
+// LDRB (immediate, Thumb)
+bool EmulateInstructionARM::EmulateLDRBImmediate(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ R[t] = ZeroExtend(MemU[address,1], 32);
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t n;
+ uint32_t imm32;
+ bool index;
+ bool add;
+ bool wback;
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5, 32);
+ t = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ imm32 = Bits32(opcode, 10, 6);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ break;
+
+ case eEncodingT2:
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 11, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // if Rt == '1111' then SEE PLD;
+ if (t == 15)
+ return false; // PLD is not implemented yet
+
+ // if Rn == '1111' then SEE LDRB (literal);
+ if (n == 15)
+ return EmulateLDRBLiteral(opcode, eEncodingT1);
+
+ // if t == 13 then UNPREDICTABLE;
+ if (t == 13)
+ return false;
+
+ break;
+
+ case eEncodingT3:
+ // if P == '1' && U == '1' && W == '0' then SEE LDRBT;
+ // if P == '0' && W == '0' then UNDEFINED;
+ if (BitIsClear(opcode, 10) && BitIsClear(opcode, 8))
+ return false;
+
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 7, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (W == '1');
+ index = BitIsSet(opcode, 10);
+ add = BitIsSet(opcode, 9);
+ wback = BitIsSet(opcode, 8);
+
+ // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE PLD;
+ if (t == 15)
+ return false; // PLD is not implemented yet
+
+ // if Rn == '1111' then SEE LDRB (literal);
+ if (n == 15)
+ return EmulateLDRBLiteral(opcode, eEncodingT1);
+
+ // if BadReg(t) || (wback && n == t) then UNPREDICTABLE;
+ if (BadReg(t) || (wback && (n == t)))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ uint32_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ addr_t address;
+ addr_t offset_addr;
+
+ // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ if (add)
+ offset_addr = Rn + imm32;
+ else
+ offset_addr = Rn - imm32;
+
+ // address = if index then offset_addr else R[n];
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ // R[t] = ZeroExtend(MemU[address,1], 32);
+ RegisterInfo base_reg;
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t, data_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg, address - Rn);
+
+ uint64_t data = MemURead(context, address, 1, 0, &success);
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data))
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+ return true;
+}
+
+// LDRB (literal) calculates an address from the PC value and an immediate
+// offset, loads a byte from memory,
+// zero-extends it to form a 32-bit word and writes it to a register.
+bool EmulateInstructionARM::EmulateLDRBLiteral(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(15);
+ base = Align(PC,4);
+ address = if add then (base + imm32) else (base - imm32);
+ R[t] = ZeroExtend(MemU[address,1], 32);
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t imm32;
+ bool add;
+ switch (encoding) {
+ case eEncodingT1:
+ // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1');
+ t = Bits32(opcode, 15, 12);
+ imm32 = Bits32(opcode, 11, 0);
+ add = BitIsSet(opcode, 23);
+
+ // if Rt == '1111' then SEE PLD;
+ if (t == 15)
+ return false; // PLD is not implemented yet
+
+ // if t == 13 then UNPREDICTABLE;
+ if (t == 13)
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // t == UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1');
+ t = Bits32(opcode, 15, 12);
+ imm32 = Bits32(opcode, 11, 0);
+ add = BitIsSet(opcode, 23);
+
+ // if t == 15 then UNPREDICTABLE;
+ if (t == 15)
+ return false;
+ break;
+
+ default:
+ return false;
+ }
+
+ // base = Align(PC,4);
+ uint32_t pc_val = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+
+ uint32_t base = AlignPC(pc_val);
+
+ addr_t address;
+ // address = if add then (base + imm32) else (base - imm32);
+ if (add)
+ address = base + imm32;
+ else
+ address = base - imm32;
+
+ // R[t] = ZeroExtend(MemU[address,1], 32);
+ EmulateInstruction::Context context;
+ context.type = eContextRelativeBranchImmediate;
+ context.SetImmediate(address - base);
+
+ uint64_t data = MemURead(context, address, 1, 0, &success);
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data))
+ return false;
+ }
+ return true;
+}
+
+// LDRB (register) calculates an address from a base register value and an
+// offset rigister value, loads a byte from memory, zero-extends it to form a
+// 32-bit word, and writes it to a register. The offset register value can
+// optionally be shifted.
+bool EmulateInstructionARM::EmulateLDRBRegister(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset = Shift(R[m], shift_t, shift_n, APSR.C);
+ offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ address = if index then offset_addr else R[n];
+ R[t] = ZeroExtend(MemU[address,1],32);
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t n;
+ uint32_t m;
+ bool index;
+ bool add;
+ bool wback;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n;
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ m = Bits32(opcode, 8, 6);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, 0);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ break;
+
+ case eEncodingT2:
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, UInt(imm2));
+ shift_t = SRType_LSL;
+ shift_n = Bits32(opcode, 5, 4);
+
+ // if Rt == '1111' then SEE PLD;
+ if (t == 15)
+ return false; // PLD is not implemented yet
+
+ // if Rn == '1111' then SEE LDRB (literal);
+ if (n == 15)
+ return EmulateLDRBLiteral(opcode, eEncodingT1);
+
+ // if t == 13 || BadReg(m) then UNPREDICTABLE;
+ if ((t == 13) || BadReg(m))
+ return false;
+ break;
+
+ case eEncodingA1: {
+ // if P == '0' && W == '1' then SEE LDRBT;
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') ||
+ // (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21));
+
+ // (shift_t, shift_n) = DecodeImmShift(type, imm5);
+ uint32_t type = Bits32(opcode, 6, 5);
+ uint32_t imm5 = Bits32(opcode, 11, 7);
+ shift_n = DecodeImmShift(type, imm5, shift_t);
+
+ // if t == 15 || m == 15 then UNPREDICTABLE;
+ if ((t == 15) || (m == 15))
+ return false;
+
+ // if wback && (n == 15 || n == t) then UNPREDICTABLE;
+ if (wback && ((n == 15) || (n == t)))
+ return false;
+ } break;
+
+ default:
+ return false;
+ }
+
+ addr_t offset_addr;
+ addr_t address;
+
+ // offset = Shift(R[m], shift_t, shift_n, APSR.C);
+ uint32_t Rm =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success);
+ if (!success)
+ return false;
+
+ addr_t offset = Shift(Rm, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+
+ // offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ uint32_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ if (add)
+ offset_addr = Rn + offset;
+ else
+ offset_addr = Rn - offset;
+
+ // address = if index then offset_addr else R[n];
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ // R[t] = ZeroExtend(MemU[address,1],32);
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+
+ uint64_t data = MemURead(context, address, 1, 0, &success);
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data))
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+ return true;
+}
+
+// LDRH (immediate, Thumb) calculates an address from a base register value and
+// an immediate offset, loads a
+// halfword from memory, zero-extends it to form a 32-bit word, and writes it
+// to a register. It can use offset, post-indexed, or pre-indexed addressing.
+bool EmulateInstructionARM::EmulateLDRHImmediate(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ data = MemU[address,2];
+ if wback then R[n] = offset_addr;
+ if UnalignedSupport() || address<0> = '0' then
+ R[t] = ZeroExtend(data, 32);
+ else // Can only apply before ARMv7
+ R[t] = bits(32) UNKNOWN;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t n;
+ uint32_t imm32;
+ bool index;
+ bool add;
+ bool wback;
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5:'0', 32);
+ t = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ imm32 = Bits32(opcode, 10, 6) << 1;
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ break;
+
+ case eEncodingT2:
+ // if Rt == '1111' then SEE "Unallocated memory hints";
+ // if Rn == '1111' then SEE LDRH (literal);
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 11, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // if t == 13 then UNPREDICTABLE;
+ if (t == 13)
+ return false;
+ break;
+
+ case eEncodingT3:
+ // if Rn == '1111' then SEE LDRH (literal);
+ // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE
+ // "Unallocated memory hints";
+ // if P == '1' && U == '1' && W == '0' then SEE LDRHT;
+ // if P == '0' && W == '0' then UNDEFINED;
+ if (BitIsClear(opcode, 10) && BitIsClear(opcode, 8))
+ return false;
+
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 7, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (W == '1');
+ index = BitIsSet(opcode, 10);
+ add = BitIsSet(opcode, 9);
+ wback = BitIsSet(opcode, 8);
+
+ // if BadReg(t) || (wback && n == t) then UNPREDICTABLE;
+ if (BadReg(t) || (wback && (n == t)))
+ return false;
+ break;
+
+ default:
+ return false;
+ }
+
+ // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ uint32_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ addr_t offset_addr;
+ addr_t address;
+
+ if (add)
+ offset_addr = Rn + imm32;
+ else
+ offset_addr = Rn - imm32;
+
+ // address = if index then offset_addr else R[n];
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ // data = MemU[address,2];
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+
+ uint64_t data = MemURead(context, address, 2, 0, &success);
+ if (!success)
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+
+ // if UnalignedSupport() || address<0> = '0' then
+ if (UnalignedSupport() || BitIsClear(address, 0)) {
+ // R[t] = ZeroExtend(data, 32);
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ data))
+ return false;
+ } else // Can only apply before ARMv7
+ {
+ // R[t] = bits(32) UNKNOWN;
+ WriteBits32Unknown(t);
+ }
+ }
+ return true;
+}
+
+// LDRH (literal) caculates an address from the PC value and an immediate
+// offset, loads a halfword from memory,
+// zero-extends it to form a 32-bit word, and writes it to a register.
+bool EmulateInstructionARM::EmulateLDRHLiteral(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(15);
+ base = Align(PC,4);
+ address = if add then (base + imm32) else (base - imm32);
+ data = MemU[address,2];
+ if UnalignedSupport() || address<0> = '0' then
+ R[t] = ZeroExtend(data, 32);
+ else // Can only apply before ARMv7
+ R[t] = bits(32) UNKNOWN;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t imm32;
+ bool add;
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(15);
+ switch (encoding) {
+ case eEncodingT1:
+ // if Rt == '1111' then SEE "Unallocated memory hints";
+ // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1');
+ t = Bits32(opcode, 15, 12);
+ imm32 = Bits32(opcode, 11, 0);
+ add = BitIsSet(opcode, 23);
+
+ // if t == 13 then UNPREDICTABLE;
+ if (t == 13)
+ return false;
+
+ break;
+
+ case eEncodingA1: {
+ uint32_t imm4H = Bits32(opcode, 11, 8);
+ uint32_t imm4L = Bits32(opcode, 3, 0);
+
+ // t == UInt(Rt); imm32 = ZeroExtend(imm4H:imm4L, 32); add = (U == '1');
+ t = Bits32(opcode, 15, 12);
+ imm32 = (imm4H << 4) | imm4L;
+ add = BitIsSet(opcode, 23);
+
+ // if t == 15 then UNPREDICTABLE;
+ if (t == 15)
+ return false;
+ break;
+ }
+
+ default:
+ return false;
+ }
+
+ // base = Align(PC,4);
+ uint64_t pc_value = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+
+ addr_t base = AlignPC(pc_value);
+ addr_t address;
+
+ // address = if add then (base + imm32) else (base - imm32);
+ if (add)
+ address = base + imm32;
+ else
+ address = base - imm32;
+
+ // data = MemU[address,2];
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, base_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - base);
+
+ uint64_t data = MemURead(context, address, 2, 0, &success);
+ if (!success)
+ return false;
+
+ // if UnalignedSupport() || address<0> = '0' then
+ if (UnalignedSupport() || BitIsClear(address, 0)) {
+ // R[t] = ZeroExtend(data, 32);
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - base);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ data))
+ return false;
+
+ } else // Can only apply before ARMv7
+ {
+ // R[t] = bits(32) UNKNOWN;
+ WriteBits32Unknown(t);
+ }
+ }
+ return true;
+}
+
+// LDRH (literal) calculates an address from a base register value and an offset
+// register value, loads a halfword
+// from memory, zero-extends it to form a 32-bit word, and writes it to a
+// register. The offset register value can be shifted left by 0, 1, 2, or 3
+// bits.
+bool EmulateInstructionARM::EmulateLDRHRegister(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset = Shift(R[m], shift_t, shift_n, APSR.C);
+ offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ address = if index then offset_addr else R[n];
+ data = MemU[address,2];
+ if wback then R[n] = offset_addr;
+ if UnalignedSupport() || address<0> = '0' then
+ R[t] = ZeroExtend(data, 32);
+ else // Can only apply before ARMv7
+ R[t] = bits(32) UNKNOWN;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t n;
+ uint32_t m;
+ bool index;
+ bool add;
+ bool wback;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n;
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation
+ // in ThumbEE";
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ m = Bits32(opcode, 8, 6);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, 0);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+
+ break;
+
+ case eEncodingT2:
+ // if Rn == '1111' then SEE LDRH (literal);
+ // if Rt == '1111' then SEE "Unallocated memory hints";
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, UInt(imm2));
+ shift_t = SRType_LSL;
+ shift_n = Bits32(opcode, 5, 4);
+
+ // if t == 13 || BadReg(m) then UNPREDICTABLE;
+ if ((t == 13) || BadReg(m))
+ return false;
+ break;
+
+ case eEncodingA1:
+ // if P == '0' && W == '1' then SEE LDRHT;
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') ||
+ // (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21));
+
+ // (shift_t, shift_n) = (SRType_LSL, 0);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+
+ // if t == 15 || m == 15 then UNPREDICTABLE;
+ if ((t == 15) || (m == 15))
+ return false;
+
+ // if wback && (n == 15 || n == t) then UNPREDICTABLE;
+ if (wback && ((n == 15) || (n == t)))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ // offset = Shift(R[m], shift_t, shift_n, APSR.C);
+
+ uint64_t Rm =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success);
+ if (!success)
+ return false;
+
+ addr_t offset = Shift(Rm, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+
+ addr_t offset_addr;
+ addr_t address;
+
+ // offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ uint64_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ if (add)
+ offset_addr = Rn + offset;
+ else
+ offset_addr = Rn - offset;
+
+ // address = if index then offset_addr else R[n];
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ // data = MemU[address,2];
+ RegisterInfo base_reg;
+ RegisterInfo offset_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, offset_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusIndirectOffset(base_reg, offset_reg);
+ uint64_t data = MemURead(context, address, 2, 0, &success);
+ if (!success)
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+
+ // if UnalignedSupport() || address<0> = '0' then
+ if (UnalignedSupport() || BitIsClear(address, 0)) {
+ // R[t] = ZeroExtend(data, 32);
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusIndirectOffset(base_reg, offset_reg);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ data))
+ return false;
+ } else // Can only apply before ARMv7
+ {
+ // R[t] = bits(32) UNKNOWN;
+ WriteBits32Unknown(t);
+ }
+ }
+ return true;
+}
+
+// LDRSB (immediate) calculates an address from a base register value and an
+// immediate offset, loads a byte from
+// memory, sign-extends it to form a 32-bit word, and writes it to a register.
+// It can use offset, post-indexed, or pre-indexed addressing.
+bool EmulateInstructionARM::EmulateLDRSBImmediate(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ R[t] = SignExtend(MemU[address,1], 32);
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t n;
+ uint32_t imm32;
+ bool index;
+ bool add;
+ bool wback;
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // if Rt == '1111' then SEE PLI;
+ // if Rn == '1111' then SEE LDRSB (literal);
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 11, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // if t == 13 then UNPREDICTABLE;
+ if (t == 13)
+ return false;
+
+ break;
+
+ case eEncodingT2:
+ // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE PLI;
+ // if Rn == '1111' then SEE LDRSB (literal);
+ // if P == '1' && U == '1' && W == '0' then SEE LDRSBT;
+ // if P == '0' && W == '0' then UNDEFINED;
+ if (BitIsClear(opcode, 10) && BitIsClear(opcode, 8))
+ return false;
+
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 7, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (W == '1');
+ index = BitIsSet(opcode, 10);
+ add = BitIsSet(opcode, 9);
+ wback = BitIsSet(opcode, 8);
+
+ // if BadReg(t) || (wback && n == t) then UNPREDICTABLE;
+ if (((t == 13) ||
+ ((t == 15) && (BitIsClear(opcode, 10) || BitIsSet(opcode, 9) ||
+ BitIsSet(opcode, 8)))) ||
+ (wback && (n == t)))
+ return false;
+
+ break;
+
+ case eEncodingA1: {
+ // if Rn == '1111' then SEE LDRSB (literal);
+ // if P == '0' && W == '1' then SEE LDRSBT;
+ // t == UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+
+ uint32_t imm4H = Bits32(opcode, 11, 8);
+ uint32_t imm4L = Bits32(opcode, 3, 0);
+ imm32 = (imm4H << 4) | imm4L;
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') ||
+ // (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21));
+
+ // if t == 15 || (wback && n == t) then UNPREDICTABLE;
+ if ((t == 15) || (wback && (n == t)))
+ return false;
+
+ break;
+ }
+
+ default:
+ return false;
+ }
+
+ uint64_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ addr_t offset_addr;
+ addr_t address;
+
+ // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ if (add)
+ offset_addr = Rn + imm32;
+ else
+ offset_addr = Rn - imm32;
+
+ // address = if index then offset_addr else R[n];
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ // R[t] = SignExtend(MemU[address,1], 32);
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+
+ uint64_t unsigned_data = MemURead(context, address, 1, 0, &success);
+ if (!success)
+ return false;
+
+ int64_t signed_data = llvm::SignExtend64<8>(unsigned_data);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ (uint64_t)signed_data))
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// LDRSB (literal) calculates an address from the PC value and an immediate
+// offset, loads a byte from memory,
+// sign-extends it to form a 32-bit word, and writes tit to a register.
+bool EmulateInstructionARM::EmulateLDRSBLiteral(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(15);
+ base = Align(PC,4);
+ address = if add then (base + imm32) else (base - imm32);
+ R[t] = SignExtend(MemU[address,1], 32);
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t imm32;
+ bool add;
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(15);
+ switch (encoding) {
+ case eEncodingT1:
+ // if Rt == '1111' then SEE PLI;
+ // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1');
+ t = Bits32(opcode, 15, 12);
+ imm32 = Bits32(opcode, 11, 0);
+ add = BitIsSet(opcode, 23);
+
+ // if t == 13 then UNPREDICTABLE;
+ if (t == 13)
+ return false;
+
+ break;
+
+ case eEncodingA1: {
+ // t == UInt(Rt); imm32 = ZeroExtend(imm4H:imm4L, 32); add = (U == '1');
+ t = Bits32(opcode, 15, 12);
+ uint32_t imm4H = Bits32(opcode, 11, 8);
+ uint32_t imm4L = Bits32(opcode, 3, 0);
+ imm32 = (imm4H << 4) | imm4L;
+ add = BitIsSet(opcode, 23);
+
+ // if t == 15 then UNPREDICTABLE;
+ if (t == 15)
+ return false;
+
+ break;
+ }
+
+ default:
+ return false;
+ }
+
+ // base = Align(PC,4);
+ uint64_t pc_value = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+ uint64_t base = AlignPC(pc_value);
+
+ // address = if add then (base + imm32) else (base - imm32);
+ addr_t address;
+ if (add)
+ address = base + imm32;
+ else
+ address = base - imm32;
+
+ // R[t] = SignExtend(MemU[address,1], 32);
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, base_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - base);
+
+ uint64_t unsigned_data = MemURead(context, address, 1, 0, &success);
+ if (!success)
+ return false;
+
+ int64_t signed_data = llvm::SignExtend64<8>(unsigned_data);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ (uint64_t)signed_data))
+ return false;
+ }
+ return true;
+}
+
+// LDRSB (register) calculates an address from a base register value and an
+// offset register value, loadsa byte from
+// memory, sign-extends it to form a 32-bit word, and writes it to a register.
+// The offset register value can be shifted left by 0, 1, 2, or 3 bits.
+bool EmulateInstructionARM::EmulateLDRSBRegister(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset = Shift(R[m], shift_t, shift_n, APSR.C);
+ offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ address = if index then offset_addr else R[n];
+ R[t] = SignExtend(MemU[address,1], 32);
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t n;
+ uint32_t m;
+ bool index;
+ bool add;
+ bool wback;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n;
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ m = Bits32(opcode, 8, 6);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, 0);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+
+ break;
+
+ case eEncodingT2:
+ // if Rt == '1111' then SEE PLI;
+ // if Rn == '1111' then SEE LDRSB (literal);
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, UInt(imm2));
+ shift_t = SRType_LSL;
+ shift_n = Bits32(opcode, 5, 4);
+
+ // if t == 13 || BadReg(m) then UNPREDICTABLE;
+ if ((t == 13) || BadReg(m))
+ return false;
+ break;
+
+ case eEncodingA1:
+ // if P == '0' && W == '1' then SEE LDRSBT;
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') ||
+ // (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21);
+
+ // (shift_t, shift_n) = (SRType_LSL, 0);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+
+ // if t == 15 || m == 15 then UNPREDICTABLE;
+ if ((t == 15) || (m == 15))
+ return false;
+
+ // if wback && (n == 15 || n == t) then UNPREDICTABLE;
+ if (wback && ((n == 15) || (n == t)))
+ return false;
+ break;
+
+ default:
+ return false;
+ }
+
+ uint64_t Rm =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success);
+ if (!success)
+ return false;
+
+ // offset = Shift(R[m], shift_t, shift_n, APSR.C);
+ addr_t offset = Shift(Rm, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+
+ addr_t offset_addr;
+ addr_t address;
+
+ // offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ uint64_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ if (add)
+ offset_addr = Rn + offset;
+ else
+ offset_addr = Rn - offset;
+
+ // address = if index then offset_addr else R[n];
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ // R[t] = SignExtend(MemU[address,1], 32);
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+ RegisterInfo offset_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, offset_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusIndirectOffset(base_reg, offset_reg);
+
+ uint64_t unsigned_data = MemURead(context, address, 1, 0, &success);
+ if (!success)
+ return false;
+
+ int64_t signed_data = llvm::SignExtend64<8>(unsigned_data);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ (uint64_t)signed_data))
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+ return true;
+}
+
+// LDRSH (immediate) calculates an address from a base register value and an
+// immediate offset, loads a halfword from
+// memory, sign-extends it to form a 32-bit word, and writes it to a register.
+// It can use offset, post-indexed, or pre-indexed addressing.
+bool EmulateInstructionARM::EmulateLDRSHImmediate(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ data = MemU[address,2];
+ if wback then R[n] = offset_addr;
+ if UnalignedSupport() || address<0> = '0' then
+ R[t] = SignExtend(data, 32);
+ else // Can only apply before ARMv7
+ R[t] = bits(32) UNKNOWN;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t n;
+ uint32_t imm32;
+ bool index;
+ bool add;
+ bool wback;
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // if Rn == '1111' then SEE LDRSH (literal);
+ // if Rt == '1111' then SEE "Unallocated memory hints";
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 11, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // if t == 13 then UNPREDICTABLE;
+ if (t == 13)
+ return false;
+
+ break;
+
+ case eEncodingT2:
+ // if Rn == '1111' then SEE LDRSH (literal);
+ // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE
+ // "Unallocated memory hints";
+ // if P == '1' && U == '1' && W == '0' then SEE LDRSHT;
+ // if P == '0' && W == '0' then UNDEFINED;
+ if (BitIsClear(opcode, 10) && BitIsClear(opcode, 8))
+ return false;
+
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 7, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (W == '1');
+ index = BitIsSet(opcode, 10);
+ add = BitIsSet(opcode, 9);
+ wback = BitIsSet(opcode, 8);
+
+ // if BadReg(t) || (wback && n == t) then UNPREDICTABLE;
+ if (BadReg(t) || (wback && (n == t)))
+ return false;
+
+ break;
+
+ case eEncodingA1: {
+ // if Rn == '1111' then SEE LDRSH (literal);
+ // if P == '0' && W == '1' then SEE LDRSHT;
+ // t == UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ uint32_t imm4H = Bits32(opcode, 11, 8);
+ uint32_t imm4L = Bits32(opcode, 3, 0);
+ imm32 = (imm4H << 4) | imm4L;
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') ||
+ // (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21);
+
+ // if t == 15 || (wback && n == t) then UNPREDICTABLE;
+ if ((t == 15) || (wback && (n == t)))
+ return false;
+
+ break;
+ }
+
+ default:
+ return false;
+ }
+
+ // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ uint64_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ addr_t offset_addr;
+ if (add)
+ offset_addr = Rn + imm32;
+ else
+ offset_addr = Rn - imm32;
+
+ // address = if index then offset_addr else R[n];
+ addr_t address;
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ // data = MemU[address,2];
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+
+ uint64_t data = MemURead(context, address, 2, 0, &success);
+ if (!success)
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+
+ // if UnalignedSupport() || address<0> = '0' then
+ if (UnalignedSupport() || BitIsClear(address, 0)) {
+ // R[t] = SignExtend(data, 32);
+ int64_t signed_data = llvm::SignExtend64<16>(data);
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ (uint64_t)signed_data))
+ return false;
+ } else // Can only apply before ARMv7
+ {
+ // R[t] = bits(32) UNKNOWN;
+ WriteBits32Unknown(t);
+ }
+ }
+ return true;
+}
+
+// LDRSH (literal) calculates an address from the PC value and an immediate
+// offset, loads a halfword from memory,
+// sign-extends it to from a 32-bit word, and writes it to a register.
+bool EmulateInstructionARM::EmulateLDRSHLiteral(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(15);
+ base = Align(PC,4);
+ address = if add then (base + imm32) else (base - imm32);
+ data = MemU[address,2];
+ if UnalignedSupport() || address<0> = '0' then
+ R[t] = SignExtend(data, 32);
+ else // Can only apply before ARMv7
+ R[t] = bits(32) UNKNOWN;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t imm32;
+ bool add;
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(15);
+ switch (encoding) {
+ case eEncodingT1:
+ // if Rt == '1111' then SEE "Unallocated memory hints";
+ // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1');
+ t = Bits32(opcode, 15, 12);
+ imm32 = Bits32(opcode, 11, 0);
+ add = BitIsSet(opcode, 23);
+
+ // if t == 13 then UNPREDICTABLE;
+ if (t == 13)
+ return false;
+
+ break;
+
+ case eEncodingA1: {
+ // t == UInt(Rt); imm32 = ZeroExtend(imm4H:imm4L, 32); add = (U == '1');
+ t = Bits32(opcode, 15, 12);
+ uint32_t imm4H = Bits32(opcode, 11, 8);
+ uint32_t imm4L = Bits32(opcode, 3, 0);
+ imm32 = (imm4H << 4) | imm4L;
+ add = BitIsSet(opcode, 23);
+
+ // if t == 15 then UNPREDICTABLE;
+ if (t == 15)
+ return false;
+
+ break;
+ }
+ default:
+ return false;
+ }
+
+ // base = Align(PC,4);
+ uint64_t pc_value = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+
+ uint64_t base = AlignPC(pc_value);
+
+ addr_t address;
+ // address = if add then (base + imm32) else (base - imm32);
+ if (add)
+ address = base + imm32;
+ else
+ address = base - imm32;
+
+ // data = MemU[address,2];
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, base_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, imm32);
+
+ uint64_t data = MemURead(context, address, 2, 0, &success);
+ if (!success)
+ return false;
+
+ // if UnalignedSupport() || address<0> = '0' then
+ if (UnalignedSupport() || BitIsClear(address, 0)) {
+ // R[t] = SignExtend(data, 32);
+ int64_t signed_data = llvm::SignExtend64<16>(data);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ (uint64_t)signed_data))
+ return false;
+ } else // Can only apply before ARMv7
+ {
+ // R[t] = bits(32) UNKNOWN;
+ WriteBits32Unknown(t);
+ }
+ }
+ return true;
+}
+
+// LDRSH (register) calculates an address from a base register value and an
+// offset register value, loads a halfword
+// from memory, sign-extends it to form a 32-bit word, and writes it to a
+// register. The offset register value can be shifted left by 0, 1, 2, or 3
+// bits.
+bool EmulateInstructionARM::EmulateLDRSHRegister(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset = Shift(R[m], shift_t, shift_n, APSR.C);
+ offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ address = if index then offset_addr else R[n];
+ data = MemU[address,2];
+ if wback then R[n] = offset_addr;
+ if UnalignedSupport() || address<0> = '0' then
+ R[t] = SignExtend(data, 32);
+ else // Can only apply before ARMv7
+ R[t] = bits(32) UNKNOWN;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t n;
+ uint32_t m;
+ bool index;
+ bool add;
+ bool wback;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n;
+
+ // EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ switch (encoding) {
+ case eEncodingT1:
+ // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation
+ // in ThumbEE";
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ m = Bits32(opcode, 8, 6);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, 0);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+
+ break;
+
+ case eEncodingT2:
+ // if Rn == '1111' then SEE LDRSH (literal);
+ // if Rt == '1111' then SEE "Unallocated memory hints";
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = TRUE; add = TRUE; wback = FALSE;
+ index = true;
+ add = true;
+ wback = false;
+
+ // (shift_t, shift_n) = (SRType_LSL, UInt(imm2));
+ shift_t = SRType_LSL;
+ shift_n = Bits32(opcode, 5, 4);
+
+ // if t == 13 || BadReg(m) then UNPREDICTABLE;
+ if ((t == 13) || BadReg(m))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // if P == '0' && W == '1' then SEE LDRSHT;
+ // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') ||
+ // (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21);
+
+ // (shift_t, shift_n) = (SRType_LSL, 0);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+
+ // if t == 15 || m == 15 then UNPREDICTABLE;
+ if ((t == 15) || (m == 15))
+ return false;
+
+ // if wback && (n == 15 || n == t) then UNPREDICTABLE;
+ if (wback && ((n == 15) || (n == t)))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ uint64_t Rm =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success);
+ if (!success)
+ return false;
+
+ uint64_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ // offset = Shift(R[m], shift_t, shift_n, APSR.C);
+ addr_t offset = Shift(Rm, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+
+ addr_t offset_addr;
+ addr_t address;
+
+ // offset_addr = if add then (R[n] + offset) else (R[n] - offset);
+ if (add)
+ offset_addr = Rn + offset;
+ else
+ offset_addr = Rn - offset;
+
+ // address = if index then offset_addr else R[n];
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ // data = MemU[address,2];
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ RegisterInfo offset_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, offset_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusIndirectOffset(base_reg, offset_reg);
+
+ uint64_t data = MemURead(context, address, 2, 0, &success);
+ if (!success)
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+
+ // if UnalignedSupport() || address<0> = '0' then
+ if (UnalignedSupport() || BitIsClear(address, 0)) {
+ // R[t] = SignExtend(data, 32);
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusIndirectOffset(base_reg, offset_reg);
+
+ int64_t signed_data = llvm::SignExtend64<16>(data);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t,
+ (uint64_t)signed_data))
+ return false;
+ } else // Can only apply before ARMv7
+ {
+ // R[t] = bits(32) UNKNOWN;
+ WriteBits32Unknown(t);
+ }
+ }
+ return true;
+}
+
+// SXTB extracts an 8-bit value from a register, sign-extends it to 32 bits, and
+// writes the result to the destination
+// register. You can specifiy a rotation by 0, 8, 16, or 24 bits before
+// extracting the 8-bit value.
+bool EmulateInstructionARM::EmulateSXTB(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ rotated = ROR(R[m], rotation);
+ R[d] = SignExtend(rotated<7:0>, 32);
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t d;
+ uint32_t m;
+ uint32_t rotation;
+
+ // EncodingSpecificOperations();
+ switch (encoding) {
+ case eEncodingT1:
+ // d = UInt(Rd); m = UInt(Rm); rotation = 0;
+ d = Bits32(opcode, 2, 0);
+ m = Bits32(opcode, 5, 3);
+ rotation = 0;
+
+ break;
+
+ case eEncodingT2:
+ // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000');
+ d = Bits32(opcode, 11, 8);
+ m = Bits32(opcode, 3, 0);
+ rotation = Bits32(opcode, 5, 4) << 3;
+
+ // if BadReg(d) || BadReg(m) then UNPREDICTABLE;
+ if (BadReg(d) || BadReg(m))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000');
+ d = Bits32(opcode, 15, 12);
+ m = Bits32(opcode, 3, 0);
+ rotation = Bits32(opcode, 11, 10) << 3;
+
+ // if d == 15 || m == 15 then UNPREDICTABLE;
+ if ((d == 15) || (m == 15))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ uint64_t Rm =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success);
+ if (!success)
+ return false;
+
+ // rotated = ROR(R[m], rotation);
+ uint64_t rotated = ROR(Rm, rotation, &success);
+ if (!success)
+ return false;
+
+ // R[d] = SignExtend(rotated<7:0>, 32);
+ int64_t data = llvm::SignExtend64<8>(rotated);
+
+ RegisterInfo source_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, source_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegister(source_reg);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d,
+ (uint64_t)data))
+ return false;
+ }
+ return true;
+}
+
+// SXTH extracts a 16-bit value from a register, sign-extends it to 32 bits, and
+// writes the result to the destination
+// register. You can specify a rotation by 0, 8, 16, or 24 bits before
+// extracting the 16-bit value.
+bool EmulateInstructionARM::EmulateSXTH(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ rotated = ROR(R[m], rotation);
+ R[d] = SignExtend(rotated<15:0>, 32);
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t d;
+ uint32_t m;
+ uint32_t rotation;
+
+ // EncodingSpecificOperations();
+ switch (encoding) {
+ case eEncodingT1:
+ // d = UInt(Rd); m = UInt(Rm); rotation = 0;
+ d = Bits32(opcode, 2, 0);
+ m = Bits32(opcode, 5, 3);
+ rotation = 0;
+
+ break;
+
+ case eEncodingT2:
+ // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000');
+ d = Bits32(opcode, 11, 8);
+ m = Bits32(opcode, 3, 0);
+ rotation = Bits32(opcode, 5, 4) << 3;
+
+ // if BadReg(d) || BadReg(m) then UNPREDICTABLE;
+ if (BadReg(d) || BadReg(m))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000');
+ d = Bits32(opcode, 15, 12);
+ m = Bits32(opcode, 3, 0);
+ rotation = Bits32(opcode, 11, 10) << 3;
+
+ // if d == 15 || m == 15 then UNPREDICTABLE;
+ if ((d == 15) || (m == 15))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ uint64_t Rm =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success);
+ if (!success)
+ return false;
+
+ // rotated = ROR(R[m], rotation);
+ uint64_t rotated = ROR(Rm, rotation, &success);
+ if (!success)
+ return false;
+
+ // R[d] = SignExtend(rotated<15:0>, 32);
+ RegisterInfo source_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, source_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegister(source_reg);
+
+ int64_t data = llvm::SignExtend64<16>(rotated);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d,
+ (uint64_t)data))
+ return false;
+ }
+
+ return true;
+}
+
+// UXTB extracts an 8-bit value from a register, zero-extneds it to 32 bits, and
+// writes the result to the destination
+// register. You can specify a rotation by 0, 8, 16, or 24 bits before
+// extracting the 8-bit value.
+bool EmulateInstructionARM::EmulateUXTB(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ rotated = ROR(R[m], rotation);
+ R[d] = ZeroExtend(rotated<7:0>, 32);
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t d;
+ uint32_t m;
+ uint32_t rotation;
+
+ // EncodingSpecificOperations();
+ switch (encoding) {
+ case eEncodingT1:
+ // d = UInt(Rd); m = UInt(Rm); rotation = 0;
+ d = Bits32(opcode, 2, 0);
+ m = Bits32(opcode, 5, 3);
+ rotation = 0;
+
+ break;
+
+ case eEncodingT2:
+ // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000');
+ d = Bits32(opcode, 11, 8);
+ m = Bits32(opcode, 3, 0);
+ rotation = Bits32(opcode, 5, 4) << 3;
+
+ // if BadReg(d) || BadReg(m) then UNPREDICTABLE;
+ if (BadReg(d) || BadReg(m))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000');
+ d = Bits32(opcode, 15, 12);
+ m = Bits32(opcode, 3, 0);
+ rotation = Bits32(opcode, 11, 10) << 3;
+
+ // if d == 15 || m == 15 then UNPREDICTABLE;
+ if ((d == 15) || (m == 15))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ uint64_t Rm =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success);
+ if (!success)
+ return false;
+
+ // rotated = ROR(R[m], rotation);
+ uint64_t rotated = ROR(Rm, rotation, &success);
+ if (!success)
+ return false;
+
+ // R[d] = ZeroExtend(rotated<7:0>, 32);
+ RegisterInfo source_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, source_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegister(source_reg);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d,
+ Bits32(rotated, 7, 0)))
+ return false;
+ }
+ return true;
+}
+
+// UXTH extracts a 16-bit value from a register, zero-extends it to 32 bits, and
+// writes the result to the destination
+// register. You can specify a rotation by 0, 8, 16, or 24 bits before
+// extracting the 16-bit value.
+bool EmulateInstructionARM::EmulateUXTH(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ rotated = ROR(R[m], rotation);
+ R[d] = ZeroExtend(rotated<15:0>, 32);
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t d;
+ uint32_t m;
+ uint32_t rotation;
+
+ switch (encoding) {
+ case eEncodingT1:
+ // d = UInt(Rd); m = UInt(Rm); rotation = 0;
+ d = Bits32(opcode, 2, 0);
+ m = Bits32(opcode, 5, 3);
+ rotation = 0;
+
+ break;
+
+ case eEncodingT2:
+ // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000');
+ d = Bits32(opcode, 11, 8);
+ m = Bits32(opcode, 3, 0);
+ rotation = Bits32(opcode, 5, 4) << 3;
+
+ // if BadReg(d) || BadReg(m) then UNPREDICTABLE;
+ if (BadReg(d) || BadReg(m))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000');
+ d = Bits32(opcode, 15, 12);
+ m = Bits32(opcode, 3, 0);
+ rotation = Bits32(opcode, 11, 10) << 3;
+
+ // if d == 15 || m == 15 then UNPREDICTABLE;
+ if ((d == 15) || (m == 15))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ uint64_t Rm =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success);
+ if (!success)
+ return false;
+
+ // rotated = ROR(R[m], rotation);
+ uint64_t rotated = ROR(Rm, rotation, &success);
+ if (!success)
+ return false;
+
+ // R[d] = ZeroExtend(rotated<15:0>, 32);
+ RegisterInfo source_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, source_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegister(source_reg);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d,
+ Bits32(rotated, 15, 0)))
+ return false;
+ }
+ return true;
+}
+
+// RFE (Return From Exception) loads the PC and the CPSR from the word at the
+// specified address and the following
+// word respectively.
+bool EmulateInstructionARM::EmulateRFE(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ if !CurrentModeIsPrivileged() || CurrentInstrSet() == InstrSet_ThumbEE then
+ UNPREDICTABLE;
+ else
+ address = if increment then R[n] else R[n]-8;
+ if wordhigher then address = address+4;
+ CPSRWriteByInstr(MemA[address+4,4], '1111', TRUE);
+ BranchWritePC(MemA[address,4]);
+ if wback then R[n] = if increment then R[n]+8 else R[n]-8;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t n;
+ bool wback;
+ bool increment;
+ bool wordhigher;
+
+ // EncodingSpecificOperations();
+ switch (encoding) {
+ case eEncodingT1:
+ // n = UInt(Rn); wback = (W == '1'); increment = FALSE; wordhigher =
+ // FALSE;
+ n = Bits32(opcode, 19, 16);
+ wback = BitIsSet(opcode, 21);
+ increment = false;
+ wordhigher = false;
+
+ // if n == 15 then UNPREDICTABLE;
+ if (n == 15)
+ return false;
+
+ // if InITBlock() && !LastInITBlock() then UNPREDICTABLE;
+ if (InITBlock() && !LastInITBlock())
+ return false;
+
+ break;
+
+ case eEncodingT2:
+ // n = UInt(Rn); wback = (W == '1'); increment = TRUE; wordhigher = FALSE;
+ n = Bits32(opcode, 19, 16);
+ wback = BitIsSet(opcode, 21);
+ increment = true;
+ wordhigher = false;
+
+ // if n == 15 then UNPREDICTABLE;
+ if (n == 15)
+ return false;
+
+ // if InITBlock() && !LastInITBlock() then UNPREDICTABLE;
+ if (InITBlock() && !LastInITBlock())
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // n = UInt(Rn);
+ n = Bits32(opcode, 19, 16);
+
+ // wback = (W == '1'); inc = (U == '1'); wordhigher = (P == U);
+ wback = BitIsSet(opcode, 21);
+ increment = BitIsSet(opcode, 23);
+ wordhigher = (Bit32(opcode, 24) == Bit32(opcode, 23));
+
+ // if n == 15 then UNPREDICTABLE;
+ if (n == 15)
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ // if !CurrentModeIsPrivileged() || CurrentInstrSet() == InstrSet_ThumbEE
+ // then
+ if (!CurrentModeIsPrivileged())
+ // UNPREDICTABLE;
+ return false;
+ else {
+ uint64_t Rn =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
+ if (!success)
+ return false;
+
+ addr_t address;
+ // address = if increment then R[n] else R[n]-8;
+ if (increment)
+ address = Rn;
+ else
+ address = Rn - 8;
+
+ // if wordhigher then address = address+4;
+ if (wordhigher)
+ address = address + 4;
+
+ // CPSRWriteByInstr(MemA[address+4,4], '1111', TRUE);
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ EmulateInstruction::Context context;
+ context.type = eContextReturnFromException;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+
+ uint64_t data = MemARead(context, address + 4, 4, 0, &success);
+ if (!success)
+ return false;
+
+ CPSRWriteByInstr(data, 15, true);
+
+ // BranchWritePC(MemA[address,4]);
+ uint64_t data2 = MemARead(context, address, 4, 0, &success);
+ if (!success)
+ return false;
+
+ BranchWritePC(context, data2);
+
+ // if wback then R[n] = if increment then R[n]+8 else R[n]-8;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ if (increment) {
+ context.SetOffset(8);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ Rn + 8))
+ return false;
+ } else {
+ context.SetOffset(-8);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ Rn - 8))
+ return false;
+ }
+ } // if wback
+ }
+ } // if ConditionPassed()
+ return true;
+}
+
+// Bitwise Exclusive OR (immediate) performs a bitwise exclusive OR of a
+// register value and an immediate value, and writes the result to the
+// destination register. It can optionally update the condition flags based on
+// the result.
+bool EmulateInstructionARM::EmulateEORImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ result = R[n] EOR imm32;
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd, Rn;
+ uint32_t
+ imm32; // the immediate value to be ORed to the value obtained from Rn
+ bool setflags;
+ uint32_t carry; // the carry bit after ARM/Thumb Expand operation
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbExpandImm_C(
+ opcode, APSR_C,
+ carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C)
+ // if Rd == '1111' && S == '1' then SEE TEQ (immediate);
+ if (Rd == 15 && setflags)
+ return EmulateTEQImm(opcode, eEncodingT1);
+ if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 =
+ ARMExpandImm_C(opcode, APSR_C,
+ carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C)
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ uint32_t result = val1 ^ imm32;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+// Bitwise Exclusive OR (register) performs a bitwise exclusive OR of a
+// register value and an optionally-shifted register value, and writes the
+// result to the destination register. It can optionally update the condition
+// flags based on the result.
+bool EmulateInstructionARM::EmulateEORReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C);
+ result = R[n] EOR shifted;
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd, Rn, Rm;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ bool setflags;
+ uint32_t carry;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Rn = Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 5, 3);
+ setflags = !InITBlock();
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ break;
+ case eEncodingT2:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+ // if Rd == '1111' && S == '1' then SEE TEQ (register);
+ if (Rd == 15 && setflags)
+ return EmulateTEQReg(opcode, eEncodingT1);
+ if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn) || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the second operand.
+ uint32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success);
+ if (!success)
+ return false;
+ uint32_t result = val1 ^ shifted;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+// Bitwise OR (immediate) performs a bitwise (inclusive) OR of a register value
+// and an immediate value, and writes the result to the destination register.
+// It can optionally update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateORRImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ result = R[n] OR imm32;
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd, Rn;
+ uint32_t
+ imm32; // the immediate value to be ORed to the value obtained from Rn
+ bool setflags;
+ uint32_t carry; // the carry bit after ARM/Thumb Expand operation
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbExpandImm_C(
+ opcode, APSR_C,
+ carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C)
+ // if Rn == '1111' then SEE MOV (immediate);
+ if (Rn == 15)
+ return EmulateMOVRdImm(opcode, eEncodingT2);
+ if (BadReg(Rd) || Rn == 13)
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 =
+ ARMExpandImm_C(opcode, APSR_C,
+ carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C)
+
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ uint32_t result = val1 | imm32;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+// Bitwise OR (register) performs a bitwise (inclusive) OR of a register value
+// and an optionally-shifted register value, and writes the result to the
+// destination register. It can optionally update the condition flags based on
+// the result.
+bool EmulateInstructionARM::EmulateORRReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C);
+ result = R[n] OR shifted;
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd, Rn, Rm;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ bool setflags;
+ uint32_t carry;
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Rn = Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 5, 3);
+ setflags = !InITBlock();
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ break;
+ case eEncodingT2:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+ // if Rn == '1111' then SEE MOV (register);
+ if (Rn == 15)
+ return EmulateMOVRdRm(opcode, eEncodingT3);
+ if (BadReg(Rd) || Rn == 13 || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the second operand.
+ uint32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success);
+ if (!success)
+ return false;
+ uint32_t result = val1 | shifted;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry))
+ return false;
+ }
+ return true;
+}
+
+// Reverse Subtract (immediate) subtracts a register value from an immediate
+// value, and writes the result to the destination register. It can optionally
+// update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateRSBImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(NOT(R[n]), imm32, '1');
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ uint32_t Rd; // the destination register
+ uint32_t Rn; // the first operand
+ bool setflags;
+ uint32_t
+ imm32; // the immediate value to be added to the value obtained from Rn
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 2, 0);
+ Rn = Bits32(opcode, 5, 3);
+ setflags = !InITBlock();
+ imm32 = 0;
+ break;
+ case eEncodingT2:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8)
+ if (BadReg(Rd) || BadReg(Rn))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+ // Read the register value from the operand register Rn.
+ uint32_t reg_val = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult res = AddWithCarry(~reg_val, imm32, 1);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow);
+}
+
+// Reverse Subtract (register) subtracts a register value from an optionally-
+// shifted register value, and writes the result to the destination register.
+// It can optionally update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateRSBReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ (result, carry, overflow) = AddWithCarry(NOT(R[n]), shifted, '1');
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ uint32_t Rd; // the destination register
+ uint32_t Rn; // the first operand
+ uint32_t Rm; // the second operand
+ bool setflags;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+ // if (BadReg(d) || BadReg(m)) then UNPREDICTABLE;
+ if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+ // Read the register value from register Rn.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the register value from register Rm.
+ uint32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+ AddWithCarryResult res = AddWithCarry(~val1, shifted, 1);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+ return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow);
+}
+
+// Reverse Subtract with Carry (immediate) subtracts a register value and the
+// value of NOT (Carry flag) from an immediate value, and writes the result to
+// the destination register. It can optionally update the condition flags based
+// on the result.
+bool EmulateInstructionARM::EmulateRSCImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(NOT(R[n]), imm32, APSR.C);
+ if d == 15 then
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ uint32_t Rd; // the destination register
+ uint32_t Rn; // the first operand
+ bool setflags;
+ uint32_t
+ imm32; // the immediate value to be added to the value obtained from Rn
+ switch (encoding) {
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+ // Read the register value from the operand register Rn.
+ uint32_t reg_val = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult res = AddWithCarry(~reg_val, imm32, APSR_C);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow);
+}
+
+// Reverse Subtract with Carry (register) subtracts a register value and the
+// value of NOT (Carry flag) from an optionally-shifted register value, and
+// writes the result to the destination register. It can optionally update the
+// condition flags based on the result.
+bool EmulateInstructionARM::EmulateRSCReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ (result, carry, overflow) = AddWithCarry(NOT(R[n]), shifted, APSR.C);
+ if d == 15 then
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ uint32_t Rd; // the destination register
+ uint32_t Rn; // the first operand
+ uint32_t Rm; // the second operand
+ bool setflags;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ switch (encoding) {
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+ // Read the register value from register Rn.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the register value from register Rm.
+ uint32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+ AddWithCarryResult res = AddWithCarry(~val1, shifted, APSR_C);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+ return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow);
+}
+
+// Subtract with Carry (immediate) subtracts an immediate value and the value
+// of
+// NOT (Carry flag) from a register value, and writes the result to the
+// destination register.
+// It can optionally update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateSBCImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), APSR.C);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ uint32_t Rd; // the destination register
+ uint32_t Rn; // the first operand
+ bool setflags;
+ uint32_t
+ imm32; // the immediate value to be added to the value obtained from Rn
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8)
+ if (BadReg(Rd) || BadReg(Rn))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+ // Read the register value from the operand register Rn.
+ uint32_t reg_val = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, APSR_C);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow);
+}
+
+// Subtract with Carry (register) subtracts an optionally-shifted register
+// value and the value of
+// NOT (Carry flag) from a register value, and writes the result to the
+// destination register.
+// It can optionally update the condition flags based on the result.
+bool EmulateInstructionARM::EmulateSBCReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), APSR.C);
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ uint32_t Rd; // the destination register
+ uint32_t Rn; // the first operand
+ uint32_t Rm; // the second operand
+ bool setflags;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Rn = Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 5, 3);
+ setflags = !InITBlock();
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ break;
+ case eEncodingT2:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+ if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+ // Read the register value from register Rn.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the register value from register Rm.
+ uint32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+ AddWithCarryResult res = AddWithCarry(val1, ~shifted, APSR_C);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+ return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow);
+}
+
+// This instruction subtracts an immediate value from a register value, and
+// writes the result to the destination register. It can optionally update the
+// condition flags based on the result.
+bool EmulateInstructionARM::EmulateSUBImmThumb(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), '1');
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ uint32_t Rd; // the destination register
+ uint32_t Rn; // the first operand
+ bool setflags;
+ uint32_t imm32; // the immediate value to be subtracted from the value
+ // obtained from Rn
+ switch (encoding) {
+ case eEncodingT1:
+ Rd = Bits32(opcode, 2, 0);
+ Rn = Bits32(opcode, 5, 3);
+ setflags = !InITBlock();
+ imm32 = Bits32(opcode, 8, 6); // imm32 = ZeroExtend(imm3, 32)
+ break;
+ case eEncodingT2:
+ Rd = Rn = Bits32(opcode, 10, 8);
+ setflags = !InITBlock();
+ imm32 = Bits32(opcode, 7, 0); // imm32 = ZeroExtend(imm8, 32)
+ break;
+ case eEncodingT3:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8)
+
+ // if Rd == '1111' && S == '1' then SEE CMP (immediate);
+ if (Rd == 15 && setflags)
+ return EmulateCMPImm(opcode, eEncodingT2);
+
+ // if Rn == '1101' then SEE SUB (SP minus immediate);
+ if (Rn == 13)
+ return EmulateSUBSPImm(opcode, eEncodingT2);
+
+ // if d == 13 || (d == 15 && S == '0') || n == 15 then UNPREDICTABLE;
+ if (Rd == 13 || (Rd == 15 && !setflags) || Rn == 15)
+ return false;
+ break;
+ case eEncodingT4:
+ Rd = Bits32(opcode, 11, 8);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ThumbImm12(opcode); // imm32 = ZeroExtend(i:imm3:imm8, 32)
+
+ // if Rn == '1111' then SEE ADR;
+ if (Rn == 15)
+ return EmulateADR(opcode, eEncodingT2);
+
+ // if Rn == '1101' then SEE SUB (SP minus immediate);
+ if (Rn == 13)
+ return EmulateSUBSPImm(opcode, eEncodingT3);
+
+ if (BadReg(Rd))
+ return false;
+ break;
+ default:
+ return false;
+ }
+ // Read the register value from the operand register Rn.
+ uint32_t reg_val = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, 1);
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow);
+}
+
+// This instruction subtracts an immediate value from a register value, and
+// writes the result to the destination register. It can optionally update the
+// condition flags based on the result.
+bool EmulateInstructionARM::EmulateSUBImmARM(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), '1');
+ if d == 15 then
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rd; // the destination register
+ uint32_t Rn; // the first operand
+ bool setflags;
+ uint32_t imm32; // the immediate value to be subtracted from the value
+ // obtained from Rn
+ switch (encoding) {
+ case eEncodingA1:
+ Rd = Bits32(opcode, 15, 12);
+ Rn = Bits32(opcode, 19, 16);
+ setflags = BitIsSet(opcode, 20);
+ imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12)
+
+ // if Rn == '1111' && S == '0' then SEE ADR;
+ if (Rn == 15 && !setflags)
+ return EmulateADR(opcode, eEncodingA2);
+
+ // if Rn == '1101' then SEE SUB (SP minus immediate);
+ if (Rn == 13)
+ return EmulateSUBSPImm(opcode, eEncodingA1);
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (Rd == 15 && setflags)
+ return EmulateSUBSPcLrEtc(opcode, encoding);
+ break;
+ default:
+ return false;
+ }
+ // Read the register value from the operand register Rn.
+ uint32_t reg_val = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, 1);
+
+ EmulateInstruction::Context context;
+ if (Rd == 13)
+ context.type = EmulateInstruction::eContextAdjustStackPointer;
+ else
+ context.type = EmulateInstruction::eContextRegisterPlusOffset;
+
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindDWARF, Rn, dwarf_reg);
+ int64_t imm32_signed = imm32;
+ context.SetRegisterPlusOffset(dwarf_reg, -imm32_signed);
+
+ if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags,
+ res.carry_out, res.overflow))
+ return false;
+ }
+ return true;
+}
+
+// Test Equivalence (immediate) performs a bitwise exclusive OR operation on a
+// register value and an immediate value. It updates the condition flags based
+// on the result, and discards the result.
+bool EmulateInstructionARM::EmulateTEQImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ result = R[n] EOR imm32;
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rn;
+ uint32_t
+ imm32; // the immediate value to be ANDed to the value obtained from Rn
+ uint32_t carry; // the carry bit after ARM/Thumb Expand operation
+ switch (encoding) {
+ case eEncodingT1:
+ Rn = Bits32(opcode, 19, 16);
+ imm32 = ThumbExpandImm_C(
+ opcode, APSR_C,
+ carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C)
+ if (BadReg(Rn))
+ return false;
+ break;
+ case eEncodingA1:
+ Rn = Bits32(opcode, 19, 16);
+ imm32 =
+ ARMExpandImm_C(opcode, APSR_C,
+ carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C)
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ uint32_t result = val1 ^ imm32;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteFlags(context, result, carry))
+ return false;
+ }
+ return true;
+}
+
+// Test Equivalence (register) performs a bitwise exclusive OR operation on a
+// register value and an optionally-shifted register value. It updates the
+// condition flags based on the result, and discards the result.
+bool EmulateInstructionARM::EmulateTEQReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C);
+ result = R[n] EOR shifted;
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rn, Rm;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ uint32_t carry;
+ switch (encoding) {
+ case eEncodingT1:
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+ if (BadReg(Rn) || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the second operand.
+ uint32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success);
+ if (!success)
+ return false;
+ uint32_t result = val1 ^ shifted;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteFlags(context, result, carry))
+ return false;
+ }
+ return true;
+}
+
+// Test (immediate) performs a bitwise AND operation on a register value and an
+// immediate value. It updates the condition flags based on the result, and
+// discards the result.
+bool EmulateInstructionARM::EmulateTSTImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ result = R[n] AND imm32;
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rn;
+ uint32_t
+ imm32; // the immediate value to be ANDed to the value obtained from Rn
+ uint32_t carry; // the carry bit after ARM/Thumb Expand operation
+ switch (encoding) {
+ case eEncodingT1:
+ Rn = Bits32(opcode, 19, 16);
+ imm32 = ThumbExpandImm_C(
+ opcode, APSR_C,
+ carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C)
+ if (BadReg(Rn))
+ return false;
+ break;
+ case eEncodingA1:
+ Rn = Bits32(opcode, 19, 16);
+ imm32 =
+ ARMExpandImm_C(opcode, APSR_C,
+ carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C)
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ uint32_t result = val1 & imm32;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteFlags(context, result, carry))
+ return false;
+ }
+ return true;
+}
+
+// Test (register) performs a bitwise AND operation on a register value and an
+// optionally-shifted register value. It updates the condition flags based on
+// the result, and discards the result.
+bool EmulateInstructionARM::EmulateTSTReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ // ARM pseudo code...
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C);
+ result = R[n] AND shifted;
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ // APSR.V unchanged
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t Rn, Rm;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n; // the shift applied to the value read from Rm
+ uint32_t carry;
+ switch (encoding) {
+ case eEncodingT1:
+ Rn = Bits32(opcode, 2, 0);
+ Rm = Bits32(opcode, 5, 3);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+ break;
+ case eEncodingT2:
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+ if (BadReg(Rn) || BadReg(Rm))
+ return false;
+ break;
+ case eEncodingA1:
+ Rn = Bits32(opcode, 19, 16);
+ Rm = Bits32(opcode, 3, 0);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+ break;
+ default:
+ return false;
+ }
+
+ // Read the first operand.
+ uint32_t val1 = ReadCoreReg(Rn, &success);
+ if (!success)
+ return false;
+
+ // Read the second operand.
+ uint32_t val2 = ReadCoreReg(Rm, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success);
+ if (!success)
+ return false;
+ uint32_t result = val1 & shifted;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextImmediate;
+ context.SetNoArgs();
+
+ if (!WriteFlags(context, result, carry))
+ return false;
+ }
+ return true;
+}
+
+// A8.6.216 SUB (SP minus register)
+bool EmulateInstructionARM::EmulateSUBSPReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ (result, carry, overflow) = AddWithCarry(SP, NOT(shifted), '1');
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t d;
+ uint32_t m;
+ bool setflags;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n;
+
+ switch (encoding) {
+ case eEncodingT1:
+ // d = UInt(Rd); m = UInt(Rm); setflags = (S == '1');
+ d = Bits32(opcode, 11, 8);
+ m = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+
+ // (shift_t, shift_n) = DecodeImmShift(type, imm3:imm2);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+
+ // if d == 13 && (shift_t != SRType_LSL || shift_n > 3) then
+ // UNPREDICTABLE;
+ if ((d == 13) && ((shift_t != SRType_LSL) || (shift_n > 3)))
+ return false;
+
+ // if d == 15 || BadReg(m) then UNPREDICTABLE;
+ if ((d == 15) || BadReg(m))
+ return false;
+ break;
+
+ case eEncodingA1:
+ // d = UInt(Rd); m = UInt(Rm); setflags = (S == '1');
+ d = Bits32(opcode, 15, 12);
+ m = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if (d == 15 && setflags)
+ EmulateSUBSPcLrEtc(opcode, encoding);
+
+ // (shift_t, shift_n) = DecodeImmShift(type, imm5);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+ break;
+
+ default:
+ return false;
+ }
+
+ // shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ uint32_t Rm = ReadCoreReg(m, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift(Rm, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+
+ // (result, carry, overflow) = AddWithCarry(SP, NOT(shifted), '1');
+ uint32_t sp_val = ReadCoreReg(SP_REG, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult res = AddWithCarry(sp_val, ~shifted, 1);
+
+ EmulateInstruction::Context context;
+ context.type = eContextArithmetic;
+ RegisterInfo sp_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
+ RegisterInfo dwarf_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, dwarf_reg);
+ context.SetRegisterRegisterOperands(sp_reg, dwarf_reg);
+
+ if (!WriteCoreRegOptionalFlags(context, res.result, dwarf_r0 + d, setflags,
+ res.carry_out, res.overflow))
+ return false;
+ }
+ return true;
+}
+
+// A8.6.7 ADD (register-shifted register)
+bool EmulateInstructionARM::EmulateADDRegShift(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shift_n = UInt(R[s]<7:0>);
+ shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ (result, carry, overflow) = AddWithCarry(R[n], shifted, '0');
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t d;
+ uint32_t n;
+ uint32_t m;
+ uint32_t s;
+ bool setflags;
+ ARM_ShifterType shift_t;
+
+ switch (encoding) {
+ case eEncodingA1:
+ // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); s = UInt(Rs);
+ d = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+ s = Bits32(opcode, 11, 8);
+
+ // setflags = (S == '1'); shift_t = DecodeRegShift(type);
+ setflags = BitIsSet(opcode, 20);
+ shift_t = DecodeRegShift(Bits32(opcode, 6, 5));
+
+ // if d == 15 || n == 15 || m == 15 || s == 15 then UNPREDICTABLE;
+ if ((d == 15) || (n == 15) || (m == 15) || (s == 15))
+ return false;
+ break;
+
+ default:
+ return false;
+ }
+
+ // shift_n = UInt(R[s]<7:0>);
+ uint32_t Rs = ReadCoreReg(s, &success);
+ if (!success)
+ return false;
+
+ uint32_t shift_n = Bits32(Rs, 7, 0);
+
+ // shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ uint32_t Rm = ReadCoreReg(m, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift(Rm, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+
+ // (result, carry, overflow) = AddWithCarry(R[n], shifted, '0');
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult res = AddWithCarry(Rn, shifted, 0);
+
+ // R[d] = result;
+ EmulateInstruction::Context context;
+ context.type = eContextArithmetic;
+ RegisterInfo reg_n;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, reg_n);
+ RegisterInfo reg_m;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, reg_m);
+
+ context.SetRegisterRegisterOperands(reg_n, reg_m);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d,
+ res.result))
+ return false;
+
+ // if setflags then
+ // APSR.N = result<31>;
+ // APSR.Z = IsZeroBit(result);
+ // APSR.C = carry;
+ // APSR.V = overflow;
+ if (setflags)
+ return WriteFlags(context, res.result, res.carry_out, res.overflow);
+ }
+ return true;
+}
+
+// A8.6.213 SUB (register)
+bool EmulateInstructionARM::EmulateSUBReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), '1');
+ if d == 15 then // Can only occur for ARM encoding
+ ALUWritePC(result); // setflags is always FALSE here
+ else
+ R[d] = result;
+ if setflags then
+ APSR.N = result<31>;
+ APSR.Z = IsZeroBit(result);
+ APSR.C = carry;
+ APSR.V = overflow;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t d;
+ uint32_t n;
+ uint32_t m;
+ bool setflags;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n;
+
+ switch (encoding) {
+ case eEncodingT1:
+ // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = !InITBlock();
+ d = Bits32(opcode, 2, 0);
+ n = Bits32(opcode, 5, 3);
+ m = Bits32(opcode, 8, 6);
+ setflags = !InITBlock();
+
+ // (shift_t, shift_n) = (SRType_LSL, 0);
+ shift_t = SRType_LSL;
+ shift_n = 0;
+
+ break;
+
+ case eEncodingT2:
+ // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = (S =="1");
+ d = Bits32(opcode, 11, 8);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+
+ // if Rd == "1111" && S == "1" then SEE CMP (register);
+ if (d == 15 && setflags == 1)
+ return EmulateCMPImm(opcode, eEncodingT3);
+
+ // if Rn == "1101" then SEE SUB (SP minus register);
+ if (n == 13)
+ return EmulateSUBSPReg(opcode, eEncodingT1);
+
+ // (shift_t, shift_n) = DecodeImmShift(type, imm3:imm2);
+ shift_n = DecodeImmShiftThumb(opcode, shift_t);
+
+ // if d == 13 || (d == 15 && S == '0') || n == 15 || BadReg(m) then
+ // UNPREDICTABLE;
+ if ((d == 13) || ((d == 15) && BitIsClear(opcode, 20)) || (n == 15) ||
+ BadReg(m))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // if Rn == '1101' then SEE SUB (SP minus register);
+ // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = (S == '1');
+ d = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+ setflags = BitIsSet(opcode, 20);
+
+ // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related
+ // instructions;
+ if ((d == 15) && setflags)
+ EmulateSUBSPcLrEtc(opcode, encoding);
+
+ // (shift_t, shift_n) = DecodeImmShift(type, imm5);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+
+ break;
+
+ default:
+ return false;
+ }
+
+ // shifted = Shift(R[m], shift_t, shift_n, APSR.C);
+ uint32_t Rm = ReadCoreReg(m, &success);
+ if (!success)
+ return false;
+
+ uint32_t shifted = Shift(Rm, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+
+ // (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), '1');
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult res = AddWithCarry(Rn, ~shifted, 1);
+
+ // if d == 15 then // Can only occur for ARM encoding ALUWritePC(result);
+ // // setflags is always FALSE here else
+ // R[d] = result;
+ // if setflags then
+ // APSR.N = result<31>;
+ // APSR.Z = IsZeroBit(result);
+ // APSR.C = carry;
+ // APSR.V = overflow;
+
+ EmulateInstruction::Context context;
+ context.type = eContextArithmetic;
+ RegisterInfo reg_n;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, reg_n);
+ RegisterInfo reg_m;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, reg_m);
+ context.SetRegisterRegisterOperands(reg_n, reg_m);
+
+ if (!WriteCoreRegOptionalFlags(context, res.result, dwarf_r0 + d, setflags,
+ res.carry_out, res.overflow))
+ return false;
+ }
+ return true;
+}
+
+// A8.6.202 STREX
+// Store Register Exclusive calculates an address from a base register value
+// and an immediate offset, and stores a word from a register to memory if the
+// executing processor has exclusive access to the memory addressed.
+bool EmulateInstructionARM::EmulateSTREX(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ address = R[n] + imm32;
+ if ExclusiveMonitorsPass(address,4) then
+ MemA[address,4] = R[t];
+ R[d] = 0;
+ else
+ R[d] = 1;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t d;
+ uint32_t t;
+ uint32_t n;
+ uint32_t imm32;
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ switch (encoding) {
+ case eEncodingT1:
+ // d = UInt(Rd); t = UInt(Rt); n = UInt(Rn); imm32 =
+ // ZeroExtend(imm8:'00',
+ // 32);
+ d = Bits32(opcode, 11, 8);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 7, 0) << 2;
+
+ // if BadReg(d) || BadReg(t) || n == 15 then UNPREDICTABLE;
+ if (BadReg(d) || BadReg(t) || (n == 15))
+ return false;
+
+ // if d == n || d == t then UNPREDICTABLE;
+ if ((d == n) || (d == t))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // d = UInt(Rd); t = UInt(Rt); n = UInt(Rn); imm32 = Zeros(32); // Zero
+ // offset
+ d = Bits32(opcode, 15, 12);
+ t = Bits32(opcode, 3, 0);
+ n = Bits32(opcode, 19, 16);
+ imm32 = 0;
+
+ // if d == 15 || t == 15 || n == 15 then UNPREDICTABLE;
+ if ((d == 15) || (t == 15) || (n == 15))
+ return false;
+
+ // if d == n || d == t then UNPREDICTABLE;
+ if ((d == n) || (d == t))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ // address = R[n] + imm32;
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ addr_t address = Rn + imm32;
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t, data_reg);
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterStore;
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg, imm32);
+
+ // if ExclusiveMonitorsPass(address,4) then if (ExclusiveMonitorsPass
+ // (address, addr_byte_size)) -- For now, for the sake of emulation, we
+ // will say this
+ // always return
+ // true.
+ if (true) {
+ // MemA[address,4] = R[t];
+ uint32_t Rt =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + t, 0, &success);
+ if (!success)
+ return false;
+
+ if (!MemAWrite(context, address, Rt, addr_byte_size))
+ return false;
+
+ // R[d] = 0;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, 0))
+ return false;
+ }
+#if 0 // unreachable because if true
+ else
+ {
+ // R[d] = 1;
+ if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, 1))
+ return false;
+ }
+#endif // unreachable because if true
+ }
+ return true;
+}
+
+// A8.6.197 STRB (immediate, ARM)
+bool EmulateInstructionARM::EmulateSTRBImmARM(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ MemU[address,1] = R[t]<7:0>;
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t n;
+ uint32_t imm32;
+ bool index;
+ bool add;
+ bool wback;
+
+ switch (encoding) {
+ case eEncodingA1:
+ // if P == '0' && W == '1' then SEE STRBT;
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 11, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21);
+
+ // if t == 15 then UNPREDICTABLE;
+ if (t == 15)
+ return false;
+
+ // if wback && (n == 15 || n == t) then UNPREDICTABLE;
+ if (wback && ((n == 15) || (n == t)))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ addr_t offset_addr;
+ if (add)
+ offset_addr = Rn + imm32;
+ else
+ offset_addr = Rn - imm32;
+
+ // address = if index then offset_addr else R[n];
+ addr_t address;
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ // MemU[address,1] = R[t]<7:0>;
+ uint32_t Rt = ReadCoreReg(t, &success);
+ if (!success)
+ return false;
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t, data_reg);
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterStore;
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg, address - Rn);
+
+ if (!MemUWrite(context, address, Bits32(Rt, 7, 0), 1))
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+ return true;
+}
+
+// A8.6.194 STR (immediate, ARM)
+bool EmulateInstructionARM::EmulateSTRImmARM(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ MemU[address,4] = if t == 15 then PCStoreValue() else R[t];
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t n;
+ uint32_t imm32;
+ bool index;
+ bool add;
+ bool wback;
+
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ switch (encoding) {
+ case eEncodingA1:
+ // if P == '0' && W == '1' then SEE STRT;
+ // if Rn == '1101' && P == '1' && U == '0' && W == '1' && imm12 ==
+ // '000000000100' then SEE PUSH;
+ // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32);
+ t = Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 11, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21);
+
+ // if wback && (n == 15 || n == t) then UNPREDICTABLE;
+ if (wback && ((n == 15) || (n == t)))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ addr_t offset_addr;
+ if (add)
+ offset_addr = Rn + imm32;
+ else
+ offset_addr = Rn - imm32;
+
+ // address = if index then offset_addr else R[n];
+ addr_t address;
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t, data_reg);
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterStore;
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg, address - Rn);
+
+ // MemU[address,4] = if t == 15 then PCStoreValue() else R[t];
+ uint32_t Rt = ReadCoreReg(t, &success);
+ if (!success)
+ return false;
+
+ if (t == 15) {
+ uint32_t pc_value = ReadCoreReg(PC_REG, &success);
+ if (!success)
+ return false;
+
+ if (!MemUWrite(context, address, pc_value, addr_byte_size))
+ return false;
+ } else {
+ if (!MemUWrite(context, address, Rt, addr_byte_size))
+ return false;
+ }
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetImmediate(offset_addr);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+ return true;
+}
+
+// A8.6.66 LDRD (immediate)
+// Load Register Dual (immediate) calculates an address from a base register
+// value and an immediate offset, loads two words from memory, and writes them
+// to two registers. It can use offset, post-indexed, or pre-indexed
+// addressing.
+bool EmulateInstructionARM::EmulateLDRDImmediate(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ R[t] = MemA[address,4];
+ R[t2] = MemA[address+4,4];
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t t2;
+ uint32_t n;
+ uint32_t imm32;
+ bool index;
+ bool add;
+ bool wback;
+
+ switch (encoding) {
+ case eEncodingT1:
+ // if P == '0' && W == '0' then SEE 'Related encodings';
+ // if Rn == '1111' then SEE LDRD (literal);
+ // t = UInt(Rt); t2 = UInt(Rt2); n = UInt(Rn); imm32 =
+ // ZeroExtend(imm8:'00', 32);
+ t = Bits32(opcode, 15, 12);
+ t2 = Bits32(opcode, 11, 8);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 7, 0) << 2;
+
+ // index = (P == '1'); add = (U == '1'); wback = (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = BitIsSet(opcode, 21);
+
+ // if wback && (n == t || n == t2) then UNPREDICTABLE;
+ if (wback && ((n == t) || (n == t2)))
+ return false;
+
+ // if BadReg(t) || BadReg(t2) || t == t2 then UNPREDICTABLE;
+ if (BadReg(t) || BadReg(t2) || (t == t2))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // if Rn == '1111' then SEE LDRD (literal);
+ // if Rt<0> == '1' then UNPREDICTABLE;
+ // t = UInt(Rt); t2 = t+1; n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L,
+ // 32);
+ t = Bits32(opcode, 15, 12);
+ if (BitIsSet(t, 0))
+ return false;
+ t2 = t + 1;
+ n = Bits32(opcode, 19, 16);
+ imm32 = (Bits32(opcode, 11, 8) << 4) | Bits32(opcode, 3, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21);
+
+ // if P == '0' && W == '1' then UNPREDICTABLE;
+ if (BitIsClear(opcode, 24) && BitIsSet(opcode, 21))
+ return false;
+
+ // if wback && (n == t || n == t2) then UNPREDICTABLE;
+ if (wback && ((n == t) || (n == t2)))
+ return false;
+
+ // if t2 == 15 then UNPREDICTABLE;
+ if (t2 == 15)
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ addr_t offset_addr;
+ if (add)
+ offset_addr = Rn + imm32;
+ else
+ offset_addr = Rn - imm32;
+
+ // address = if index then offset_addr else R[n];
+ addr_t address;
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ // R[t] = MemA[address,4];
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ EmulateInstruction::Context context;
+ if (n == 13)
+ context.type = eContextPopRegisterOffStack;
+ else
+ context.type = eContextRegisterLoad;
+ context.SetAddress(address);
+
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ uint32_t data = MemARead(context, address, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data))
+ return false;
+
+ // R[t2] = MemA[address+4,4];
+ context.SetAddress(address + 4);
+ data = MemARead(context, address + 4, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t2,
+ data))
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+ return true;
+}
+
+// A8.6.68 LDRD (register)
+// Load Register Dual (register) calculates an address from a base register
+// value and a register offset, loads two words from memory, and writes them to
+// two registers. It can use offset, post-indexed or pre-indexed addressing.
+bool EmulateInstructionARM::EmulateLDRDRegister(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]);
+ address = if index then offset_addr else R[n];
+ R[t] = MemA[address,4];
+ R[t2] = MemA[address+4,4];
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t t2;
+ uint32_t n;
+ uint32_t m;
+ bool index;
+ bool add;
+ bool wback;
+
+ switch (encoding) {
+ case eEncodingA1:
+ // if Rt<0> == '1' then UNPREDICTABLE;
+ // t = UInt(Rt); t2 = t+1; n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ if (BitIsSet(t, 0))
+ return false;
+ t2 = t + 1;
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21);
+
+ // if P == '0' && W == '1' then UNPREDICTABLE;
+ if (BitIsClear(opcode, 24) && BitIsSet(opcode, 21))
+ return false;
+
+ // if t2 == 15 || m == 15 || m == t || m == t2 then UNPREDICTABLE;
+ if ((t2 == 15) || (m == 15) || (m == t) || (m == t2))
+ return false;
+
+ // if wback && (n == 15 || n == t || n == t2) then UNPREDICTABLE;
+ if (wback && ((n == 15) || (n == t) || (n == t2)))
+ return false;
+
+ // if ArchVersion() < 6 && wback && m == n then UNPREDICTABLE;
+ if ((ArchVersion() < 6) && wback && (m == n))
+ return false;
+ break;
+
+ default:
+ return false;
+ }
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ uint32_t Rm = ReadCoreReg(m, &success);
+ if (!success)
+ return false;
+ RegisterInfo offset_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, offset_reg);
+
+ // offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]);
+ addr_t offset_addr;
+ if (add)
+ offset_addr = Rn + Rm;
+ else
+ offset_addr = Rn - Rm;
+
+ // address = if index then offset_addr else R[n];
+ addr_t address;
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ EmulateInstruction::Context context;
+ if (n == 13)
+ context.type = eContextPopRegisterOffStack;
+ else
+ context.type = eContextRegisterLoad;
+ context.SetAddress(address);
+
+ // R[t] = MemA[address,4];
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ uint32_t data = MemARead(context, address, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data))
+ return false;
+
+ // R[t2] = MemA[address+4,4];
+
+ data = MemARead(context, address + 4, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t2,
+ data))
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+ return true;
+}
+
+// A8.6.200 STRD (immediate)
+// Store Register Dual (immediate) calculates an address from a base register
+// value and an immediate offset, and stores two words from two registers to
+// memory. It can use offset, post-indexed, or pre-indexed addressing.
+bool EmulateInstructionARM::EmulateSTRDImm(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); NullCheckIfThumbEE(n);
+ offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ address = if index then offset_addr else R[n];
+ MemA[address,4] = R[t];
+ MemA[address+4,4] = R[t2];
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t t2;
+ uint32_t n;
+ uint32_t imm32;
+ bool index;
+ bool add;
+ bool wback;
+
+ switch (encoding) {
+ case eEncodingT1:
+ // if P == '0' && W == '0' then SEE 'Related encodings';
+ // t = UInt(Rt); t2 = UInt(Rt2); n = UInt(Rn); imm32 =
+ // ZeroExtend(imm8:'00', 32);
+ t = Bits32(opcode, 15, 12);
+ t2 = Bits32(opcode, 11, 8);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 7, 0) << 2;
+
+ // index = (P == '1'); add = (U == '1'); wback = (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = BitIsSet(opcode, 21);
+
+ // if wback && (n == t || n == t2) then UNPREDICTABLE;
+ if (wback && ((n == t) || (n == t2)))
+ return false;
+
+ // if n == 15 || BadReg(t) || BadReg(t2) then UNPREDICTABLE;
+ if ((n == 15) || BadReg(t) || BadReg(t2))
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // if Rt<0> == '1' then UNPREDICTABLE;
+ // t = UInt(Rt); t2 = t+1; n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L,
+ // 32);
+ t = Bits32(opcode, 15, 12);
+ if (BitIsSet(t, 0))
+ return false;
+
+ t2 = t + 1;
+ n = Bits32(opcode, 19, 16);
+ imm32 = (Bits32(opcode, 11, 8) << 4) | Bits32(opcode, 3, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21);
+
+ // if P == '0' && W == '1' then UNPREDICTABLE;
+ if (BitIsClear(opcode, 24) && BitIsSet(opcode, 21))
+ return false;
+
+ // if wback && (n == 15 || n == t || n == t2) then UNPREDICTABLE;
+ if (wback && ((n == 15) || (n == t) || (n == t2)))
+ return false;
+
+ // if t2 == 15 then UNPREDICTABLE;
+ if (t2 == 15)
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32);
+ addr_t offset_addr;
+ if (add)
+ offset_addr = Rn + imm32;
+ else
+ offset_addr = Rn - imm32;
+
+ // address = if index then offset_addr else R[n];
+ addr_t address;
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+
+ // MemA[address,4] = R[t];
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t, data_reg);
+
+ uint32_t data = ReadCoreReg(t, &success);
+ if (!success)
+ return false;
+
+ EmulateInstruction::Context context;
+ if (n == 13)
+ context.type = eContextPushRegisterOnStack;
+ else
+ context.type = eContextRegisterStore;
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg, address - Rn);
+
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ if (!MemAWrite(context, address, data, addr_byte_size))
+ return false;
+
+ // MemA[address+4,4] = R[t2];
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t2, data_reg);
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ (address + 4) - Rn);
+
+ data = ReadCoreReg(t2, &success);
+ if (!success)
+ return false;
+
+ if (!MemAWrite(context, address + 4, data, addr_byte_size))
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ if (n == 13)
+ context.type = eContextAdjustStackPointer;
+ else
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+ return true;
+}
+
+// A8.6.201 STRD (register)
+bool EmulateInstructionARM::EmulateSTRDReg(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]);
+ address = if index then offset_addr else R[n];
+ MemA[address,4] = R[t];
+ MemA[address+4,4] = R[t2];
+ if wback then R[n] = offset_addr;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t t;
+ uint32_t t2;
+ uint32_t n;
+ uint32_t m;
+ bool index;
+ bool add;
+ bool wback;
+
+ switch (encoding) {
+ case eEncodingA1:
+ // if Rt<0> == '1' then UNPREDICTABLE;
+ // t = UInt(Rt); t2 = t+1; n = UInt(Rn); m = UInt(Rm);
+ t = Bits32(opcode, 15, 12);
+ if (BitIsSet(t, 0))
+ return false;
+
+ t2 = t + 1;
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1');
+ index = BitIsSet(opcode, 24);
+ add = BitIsSet(opcode, 23);
+ wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21);
+
+ // if P == '0' && W == '1' then UNPREDICTABLE;
+ if (BitIsClear(opcode, 24) && BitIsSet(opcode, 21))
+ return false;
+
+ // if t2 == 15 || m == 15 then UNPREDICTABLE;
+ if ((t2 == 15) || (m == 15))
+ return false;
+
+ // if wback && (n == 15 || n == t || n == t2) then UNPREDICTABLE;
+ if (wback && ((n == 15) || (n == t) || (n == t2)))
+ return false;
+
+ // if ArchVersion() < 6 && wback && m == n then UNPREDICTABLE;
+ if ((ArchVersion() < 6) && wback && (m == n))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+ RegisterInfo offset_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, offset_reg);
+ RegisterInfo data_reg;
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ uint32_t Rm = ReadCoreReg(m, &success);
+ if (!success)
+ return false;
+
+ // offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]);
+ addr_t offset_addr;
+ if (add)
+ offset_addr = Rn + Rm;
+ else
+ offset_addr = Rn - Rm;
+
+ // address = if index then offset_addr else R[n];
+ addr_t address;
+ if (index)
+ address = offset_addr;
+ else
+ address = Rn;
+ // MemA[address,4] = R[t];
+ uint32_t Rt = ReadCoreReg(t, &success);
+ if (!success)
+ return false;
+
+ EmulateInstruction::Context context;
+ if (t == 13)
+ context.type = eContextPushRegisterOnStack;
+ else
+ context.type = eContextRegisterStore;
+
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t, data_reg);
+ context.SetRegisterToRegisterPlusIndirectOffset(base_reg, offset_reg,
+ data_reg);
+
+ const uint32_t addr_byte_size = GetAddressByteSize();
+
+ if (!MemAWrite(context, address, Rt, addr_byte_size))
+ return false;
+
+ // MemA[address+4,4] = R[t2];
+ uint32_t Rt2 = ReadCoreReg(t2, &success);
+ if (!success)
+ return false;
+
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t2, data_reg);
+
+ context.SetRegisterToRegisterPlusIndirectOffset(base_reg, offset_reg,
+ data_reg);
+
+ if (!MemAWrite(context, address + 4, Rt2, addr_byte_size))
+ return false;
+
+ // if wback then R[n] = offset_addr;
+ if (wback) {
+ context.type = eContextAdjustBaseRegister;
+ context.SetAddress(offset_addr);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ offset_addr))
+ return false;
+ }
+ }
+ return true;
+}
+
+// A8.6.319 VLDM
+// Vector Load Multiple loads multiple extension registers from consecutive
+// memory locations using an address from an ARM core register.
+bool EmulateInstructionARM::EmulateVLDM(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n);
+ address = if add then R[n] else R[n]-imm32;
+ if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32;
+ for r = 0 to regs-1
+ if single_regs then
+ S[d+r] = MemA[address,4]; address = address+4;
+ else
+ word1 = MemA[address,4]; word2 = MemA[address+4,4]; address = address+8;
+ // Combine the word-aligned words in the correct order for
+ // current endianness.
+ D[d+r] = if BigEndian() then word1:word2 else word2:word1;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ bool single_regs;
+ bool add;
+ bool wback;
+ uint32_t d;
+ uint32_t n;
+ uint32_t imm32;
+ uint32_t regs;
+
+ switch (encoding) {
+ case eEncodingT1:
+ case eEncodingA1:
+ // if P == '0' && U == '0' && W == '0' then SEE 'Related encodings';
+ // if P == '0' && U == '1' && W == '1' && Rn == '1101' then SEE VPOP;
+ // if P == '1' && W == '0' then SEE VLDR;
+ // if P == U && W == '1' then UNDEFINED;
+ if ((Bit32(opcode, 24) == Bit32(opcode, 23)) && BitIsSet(opcode, 21))
+ return false;
+
+ // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with
+ // !), 101 (DB with !)
+ // single_regs = FALSE; add = (U == '1'); wback = (W == '1');
+ single_regs = false;
+ add = BitIsSet(opcode, 23);
+ wback = BitIsSet(opcode, 21);
+
+ // d = UInt(D:Vd); n = UInt(Rn); imm32 = ZeroExtend(imm8:'00', 32);
+ d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 7, 0) << 2;
+
+ // regs = UInt(imm8) DIV 2; // If UInt(imm8) is odd, see 'FLDMX'.
+ regs = Bits32(opcode, 7, 0) / 2;
+
+ // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then
+ // UNPREDICTABLE;
+ if (n == 15 && (wback || CurrentInstrSet() != eModeARM))
+ return false;
+
+ // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE;
+ if ((regs == 0) || (regs > 16) || ((d + regs) > 32))
+ return false;
+
+ break;
+
+ case eEncodingT2:
+ case eEncodingA2:
+ // if P == '0' && U == '0' && W == '0' then SEE 'Related encodings';
+ // if P == '0' && U == '1' && W == '1' && Rn == '1101' then SEE VPOP;
+ // if P == '1' && W == '0' then SEE VLDR;
+ // if P == U && W == '1' then UNDEFINED;
+ if ((Bit32(opcode, 24) == Bit32(opcode, 23)) && BitIsSet(opcode, 21))
+ return false;
+
+ // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with
+ // !), 101 (DB with !) single_regs = TRUE; add = (U == '1'); wback = (W
+ // == '1'); d =
+ // UInt(Vd:D); n = UInt(Rn);
+ single_regs = true;
+ add = BitIsSet(opcode, 23);
+ wback = BitIsSet(opcode, 21);
+ d = (Bits32(opcode, 15, 12) << 1) | Bit32(opcode, 22);
+ n = Bits32(opcode, 19, 16);
+
+ // imm32 = ZeroExtend(imm8:'00', 32); regs = UInt(imm8);
+ imm32 = Bits32(opcode, 7, 0) << 2;
+ regs = Bits32(opcode, 7, 0);
+
+ // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then
+ // UNPREDICTABLE;
+ if ((n == 15) && (wback || (CurrentInstrSet() != eModeARM)))
+ return false;
+
+ // if regs == 0 || (d+regs) > 32 then UNPREDICTABLE;
+ if ((regs == 0) || ((d + regs) > 32))
+ return false;
+ break;
+
+ default:
+ return false;
+ }
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ // address = if add then R[n] else R[n]-imm32;
+ addr_t address;
+ if (add)
+ address = Rn;
+ else
+ address = Rn - imm32;
+
+ // if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32;
+ EmulateInstruction::Context context;
+
+ if (wback) {
+ uint32_t value;
+ if (add)
+ value = Rn + imm32;
+ else
+ value = Rn - imm32;
+
+ context.type = eContextAdjustBaseRegister;
+ context.SetImmediateSigned(value - Rn);
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ value))
+ return false;
+ }
+
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0;
+
+ context.type = eContextRegisterLoad;
+
+ // for r = 0 to regs-1
+ for (uint32_t r = 0; r < regs; ++r) {
+ if (single_regs) {
+ // S[d+r] = MemA[address,4]; address = address+4;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+
+ uint32_t data = MemARead(context, address, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF,
+ start_reg + d + r, data))
+ return false;
+
+ address = address + 4;
+ } else {
+ // word1 = MemA[address,4]; word2 = MemA[address+4,4]; address =
+ // address+8;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+ uint32_t word1 =
+ MemARead(context, address, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ context.SetRegisterPlusOffset(base_reg, (address + 4) - Rn);
+ uint32_t word2 =
+ MemARead(context, address + 4, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ address = address + 8;
+ // // Combine the word-aligned words in the correct order for current
+ // endianness.
+ // D[d+r] = if BigEndian() then word1:word2 else word2:word1;
+ uint64_t data;
+ if (GetByteOrder() == eByteOrderBig) {
+ data = word1;
+ data = (data << 32) | word2;
+ } else {
+ data = word2;
+ data = (data << 32) | word1;
+ }
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF,
+ start_reg + d + r, data))
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+// A8.6.399 VSTM
+// Vector Store Multiple stores multiple extension registers to consecutive
+// memory locations using an address from an
+// ARM core register.
+bool EmulateInstructionARM::EmulateVSTM(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n);
+ address = if add then R[n] else R[n]-imm32;
+ if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32;
+ for r = 0 to regs-1
+ if single_regs then
+ MemA[address,4] = S[d+r]; address = address+4;
+ else
+ // Store as two word-aligned words in the correct order for
+ // current endianness.
+ MemA[address,4] = if BigEndian() then D[d+r]<63:32> else D[d+r]<31:0>;
+ MemA[address+4,4] = if BigEndian() then D[d+r]<31:0> else D[d+r]<63:32>;
+ address = address+8;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ bool single_regs;
+ bool add;
+ bool wback;
+ uint32_t d;
+ uint32_t n;
+ uint32_t imm32;
+ uint32_t regs;
+
+ switch (encoding) {
+ case eEncodingT1:
+ case eEncodingA1:
+ // if P == '0' && U == '0' && W == '0' then SEE 'Related encodings';
+ // if P == '1' && U == '0' && W == '1' && Rn == '1101' then SEE VPUSH;
+ // if P == '1' && W == '0' then SEE VSTR;
+ // if P == U && W == '1' then UNDEFINED;
+ if ((Bit32(opcode, 24) == Bit32(opcode, 23)) && BitIsSet(opcode, 21))
+ return false;
+
+ // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with
+ // !), 101 (DB with !)
+ // single_regs = FALSE; add = (U == '1'); wback = (W == '1');
+ single_regs = false;
+ add = BitIsSet(opcode, 23);
+ wback = BitIsSet(opcode, 21);
+
+ // d = UInt(D:Vd); n = UInt(Rn); imm32 = ZeroExtend(imm8:'00', 32);
+ d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ imm32 = Bits32(opcode, 7, 0) << 2;
+
+ // regs = UInt(imm8) DIV 2; // If UInt(imm8) is odd, see 'FSTMX'.
+ regs = Bits32(opcode, 7, 0) / 2;
+
+ // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then
+ // UNPREDICTABLE;
+ if ((n == 15) && (wback || (CurrentInstrSet() != eModeARM)))
+ return false;
+
+ // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE;
+ if ((regs == 0) || (regs > 16) || ((d + regs) > 32))
+ return false;
+
+ break;
+
+ case eEncodingT2:
+ case eEncodingA2:
+ // if P == '0' && U == '0' && W == '0' then SEE 'Related encodings';
+ // if P == '1' && U == '0' && W == '1' && Rn == '1101' then SEE VPUSH;
+ // if P == '1' && W == '0' then SEE VSTR;
+ // if P == U && W == '1' then UNDEFINED;
+ if ((Bit32(opcode, 24) == Bit32(opcode, 23)) && BitIsSet(opcode, 21))
+ return false;
+
+ // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with
+ // !), 101 (DB with !) single_regs = TRUE; add = (U == '1'); wback = (W
+ // == '1'); d =
+ // UInt(Vd:D); n = UInt(Rn);
+ single_regs = true;
+ add = BitIsSet(opcode, 23);
+ wback = BitIsSet(opcode, 21);
+ d = (Bits32(opcode, 15, 12) << 1) | Bit32(opcode, 22);
+ n = Bits32(opcode, 19, 16);
+
+ // imm32 = ZeroExtend(imm8:'00', 32); regs = UInt(imm8);
+ imm32 = Bits32(opcode, 7, 0) << 2;
+ regs = Bits32(opcode, 7, 0);
+
+ // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then
+ // UNPREDICTABLE;
+ if ((n == 15) && (wback || (CurrentInstrSet() != eModeARM)))
+ return false;
+
+ // if regs == 0 || (d+regs) > 32 then UNPREDICTABLE;
+ if ((regs == 0) || ((d + regs) > 32))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ // address = if add then R[n] else R[n]-imm32;
+ addr_t address;
+ if (add)
+ address = Rn;
+ else
+ address = Rn - imm32;
+
+ EmulateInstruction::Context context;
+ // if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32;
+ if (wback) {
+ uint32_t value;
+ if (add)
+ value = Rn + imm32;
+ else
+ value = Rn - imm32;
+
+ context.type = eContextAdjustBaseRegister;
+ context.SetRegisterPlusOffset(base_reg, value - Rn);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ value))
+ return false;
+ }
+
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0;
+
+ context.type = eContextRegisterStore;
+ // for r = 0 to regs-1
+ for (uint32_t r = 0; r < regs; ++r) {
+
+ if (single_regs) {
+ // MemA[address,4] = S[d+r]; address = address+4;
+ uint32_t data = ReadRegisterUnsigned(eRegisterKindDWARF,
+ start_reg + d + r, 0, &success);
+ if (!success)
+ return false;
+
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, start_reg + d + r, data_reg);
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ address - Rn);
+ if (!MemAWrite(context, address, data, addr_byte_size))
+ return false;
+
+ address = address + 4;
+ } else {
+ // // Store as two word-aligned words in the correct order for current
+ // endianness. MemA[address,4] = if BigEndian() then D[d+r]<63:32> else
+ // D[d+r]<31:0>;
+ // MemA[address+4,4] = if BigEndian() then D[d+r]<31:0> else
+ // D[d+r]<63:32>;
+ uint64_t data = ReadRegisterUnsigned(eRegisterKindDWARF,
+ start_reg + d + r, 0, &success);
+ if (!success)
+ return false;
+
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, start_reg + d + r, data_reg);
+
+ if (GetByteOrder() == eByteOrderBig) {
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ address - Rn);
+ if (!MemAWrite(context, address, Bits64(data, 63, 32),
+ addr_byte_size))
+ return false;
+
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ (address + 4) - Rn);
+ if (!MemAWrite(context, address + 4, Bits64(data, 31, 0),
+ addr_byte_size))
+ return false;
+ } else {
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ address - Rn);
+ if (!MemAWrite(context, address, Bits64(data, 31, 0), addr_byte_size))
+ return false;
+
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ (address + 4) - Rn);
+ if (!MemAWrite(context, address + 4, Bits64(data, 63, 32),
+ addr_byte_size))
+ return false;
+ }
+ // address = address+8;
+ address = address + 8;
+ }
+ }
+ }
+ return true;
+}
+
+// A8.6.320
+// This instruction loads a single extension register from memory, using an
+// address from an ARM core register, with an optional offset.
+bool EmulateInstructionARM::EmulateVLDR(const uint32_t opcode,
+ ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n);
+ base = if n == 15 then Align(PC,4) else R[n];
+ address = if add then (base + imm32) else (base - imm32);
+ if single_reg then
+ S[d] = MemA[address,4];
+ else
+ word1 = MemA[address,4]; word2 = MemA[address+4,4];
+ // Combine the word-aligned words in the correct order for current
+ // endianness.
+ D[d] = if BigEndian() then word1:word2 else word2:word1;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ bool single_reg;
+ bool add;
+ uint32_t imm32;
+ uint32_t d;
+ uint32_t n;
+
+ switch (encoding) {
+ case eEncodingT1:
+ case eEncodingA1:
+ // single_reg = FALSE; add = (U == '1'); imm32 = ZeroExtend(imm8:'00',
+ // 32);
+ single_reg = false;
+ add = BitIsSet(opcode, 23);
+ imm32 = Bits32(opcode, 7, 0) << 2;
+
+ // d = UInt(D:Vd); n = UInt(Rn);
+ d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+
+ break;
+
+ case eEncodingT2:
+ case eEncodingA2:
+ // single_reg = TRUE; add = (U == '1'); imm32 = ZeroExtend(imm8:'00', 32);
+ single_reg = true;
+ add = BitIsSet(opcode, 23);
+ imm32 = Bits32(opcode, 7, 0) << 2;
+
+ // d = UInt(Vd:D); n = UInt(Rn);
+ d = (Bits32(opcode, 15, 12) << 1) | Bit32(opcode, 22);
+ n = Bits32(opcode, 19, 16);
+
+ break;
+
+ default:
+ return false;
+ }
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ // base = if n == 15 then Align(PC,4) else R[n];
+ uint32_t base;
+ if (n == 15)
+ base = AlignPC(Rn);
+ else
+ base = Rn;
+
+ // address = if add then (base + imm32) else (base - imm32);
+ addr_t address;
+ if (add)
+ address = base + imm32;
+ else
+ address = base - imm32;
+
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ uint32_t start_reg = single_reg ? dwarf_s0 : dwarf_d0;
+
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - base);
+
+ if (single_reg) {
+ // S[d] = MemA[address,4];
+ uint32_t data = MemARead(context, address, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, start_reg + d,
+ data))
+ return false;
+ } else {
+ // word1 = MemA[address,4]; word2 = MemA[address+4,4];
+ uint32_t word1 = MemARead(context, address, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+
+ context.SetRegisterPlusOffset(base_reg, (address + 4) - base);
+ uint32_t word2 =
+ MemARead(context, address + 4, addr_byte_size, 0, &success);
+ if (!success)
+ return false;
+ // // Combine the word-aligned words in the correct order for current
+ // endianness.
+ // D[d] = if BigEndian() then word1:word2 else word2:word1;
+ uint64_t data64;
+ if (GetByteOrder() == eByteOrderBig) {
+ data64 = word1;
+ data64 = (data64 << 32) | word2;
+ } else {
+ data64 = word2;
+ data64 = (data64 << 32) | word1;
+ }
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, start_reg + d,
+ data64))
+ return false;
+ }
+ }
+ return true;
+}
+
+// A8.6.400 VSTR
+// This instruction stores a signle extension register to memory, using an
+// address from an ARM core register, with an optional offset.
+bool EmulateInstructionARM::EmulateVSTR(const uint32_t opcode,
+ ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n);
+ address = if add then (R[n] + imm32) else (R[n] - imm32);
+ if single_reg then
+ MemA[address,4] = S[d];
+ else
+ // Store as two word-aligned words in the correct order for current
+ // endianness.
+ MemA[address,4] = if BigEndian() then D[d]<63:32> else D[d]<31:0>;
+ MemA[address+4,4] = if BigEndian() then D[d]<31:0> else D[d]<63:32>;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ bool single_reg;
+ bool add;
+ uint32_t imm32;
+ uint32_t d;
+ uint32_t n;
+
+ switch (encoding) {
+ case eEncodingT1:
+ case eEncodingA1:
+ // single_reg = FALSE; add = (U == '1'); imm32 = ZeroExtend(imm8:'00',
+ // 32);
+ single_reg = false;
+ add = BitIsSet(opcode, 23);
+ imm32 = Bits32(opcode, 7, 0) << 2;
+
+ // d = UInt(D:Vd); n = UInt(Rn);
+ d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+
+ // if n == 15 && CurrentInstrSet() != InstrSet_ARM then UNPREDICTABLE;
+ if ((n == 15) && (CurrentInstrSet() != eModeARM))
+ return false;
+
+ break;
+
+ case eEncodingT2:
+ case eEncodingA2:
+ // single_reg = TRUE; add = (U == '1'); imm32 = ZeroExtend(imm8:'00', 32);
+ single_reg = true;
+ add = BitIsSet(opcode, 23);
+ imm32 = Bits32(opcode, 7, 0) << 2;
+
+ // d = UInt(Vd:D); n = UInt(Rn);
+ d = (Bits32(opcode, 15, 12) << 1) | Bit32(opcode, 22);
+ n = Bits32(opcode, 19, 16);
+
+ // if n == 15 && CurrentInstrSet() != InstrSet_ARM then UNPREDICTABLE;
+ if ((n == 15) && (CurrentInstrSet() != eModeARM))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ // address = if add then (R[n] + imm32) else (R[n] - imm32);
+ addr_t address;
+ if (add)
+ address = Rn + imm32;
+ else
+ address = Rn - imm32;
+
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ uint32_t start_reg = single_reg ? dwarf_s0 : dwarf_d0;
+
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, start_reg + d, data_reg);
+ EmulateInstruction::Context context;
+ context.type = eContextRegisterStore;
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg, address - Rn);
+
+ if (single_reg) {
+ // MemA[address,4] = S[d];
+ uint32_t data =
+ ReadRegisterUnsigned(eRegisterKindDWARF, start_reg + d, 0, &success);
+ if (!success)
+ return false;
+
+ if (!MemAWrite(context, address, data, addr_byte_size))
+ return false;
+ } else {
+ // // Store as two word-aligned words in the correct order for current
+ // endianness.
+ // MemA[address,4] = if BigEndian() then D[d]<63:32> else D[d]<31:0>;
+ // MemA[address+4,4] = if BigEndian() then D[d]<31:0> else D[d]<63:32>;
+ uint64_t data =
+ ReadRegisterUnsigned(eRegisterKindDWARF, start_reg + d, 0, &success);
+ if (!success)
+ return false;
+
+ if (GetByteOrder() == eByteOrderBig) {
+ if (!MemAWrite(context, address, Bits64(data, 63, 32), addr_byte_size))
+ return false;
+
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ (address + 4) - Rn);
+ if (!MemAWrite(context, address + 4, Bits64(data, 31, 0),
+ addr_byte_size))
+ return false;
+ } else {
+ if (!MemAWrite(context, address, Bits64(data, 31, 0), addr_byte_size))
+ return false;
+
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ (address + 4) - Rn);
+ if (!MemAWrite(context, address + 4, Bits64(data, 63, 32),
+ addr_byte_size))
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+// A8.6.307 VLDI1 (multiple single elements) This instruction loads elements
+// from memory into one, two, three or four registers, without de-interleaving.
+// Every element of each register is loaded.
+bool EmulateInstructionARM::EmulateVLD1Multiple(const uint32_t opcode,
+ ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n);
+ address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException();
+ if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs);
+ for r = 0 to regs-1
+ for e = 0 to elements-1
+ Elem[D[d+r],e,esize] = MemU[address,ebytes];
+ address = address + ebytes;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t regs;
+ uint32_t alignment;
+ uint32_t ebytes;
+ uint32_t esize;
+ uint32_t elements;
+ uint32_t d;
+ uint32_t n;
+ uint32_t m;
+ bool wback;
+ bool register_index;
+
+ switch (encoding) {
+ case eEncodingT1:
+ case eEncodingA1: {
+ // case type of
+ // when '0111'
+ // regs = 1; if align<1> == '1' then UNDEFINED;
+ // when '1010'
+ // regs = 2; if align == '11' then UNDEFINED;
+ // when '0110'
+ // regs = 3; if align<1> == '1' then UNDEFINED;
+ // when '0010'
+ // regs = 4;
+ // otherwise
+ // SEE 'Related encodings';
+ uint32_t type = Bits32(opcode, 11, 8);
+ uint32_t align = Bits32(opcode, 5, 4);
+ if (type == 7) // '0111'
+ {
+ regs = 1;
+ if (BitIsSet(align, 1))
+ return false;
+ } else if (type == 10) // '1010'
+ {
+ regs = 2;
+ if (align == 3)
+ return false;
+
+ } else if (type == 6) // '0110'
+ {
+ regs = 3;
+ if (BitIsSet(align, 1))
+ return false;
+ } else if (type == 2) // '0010'
+ {
+ regs = 4;
+ } else
+ return false;
+
+ // alignment = if align == '00' then 1 else 4 << UInt(align);
+ if (align == 0)
+ alignment = 1;
+ else
+ alignment = 4 << align;
+
+ // ebytes = 1 << UInt(size); esize = 8 * ebytes; elements = 8 DIV ebytes;
+ ebytes = 1 << Bits32(opcode, 7, 6);
+ esize = 8 * ebytes;
+ elements = 8 / ebytes;
+
+ // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm);
+ d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 15);
+ m = Bits32(opcode, 3, 0);
+
+ // wback = (m != 15); register_index = (m != 15 && m != 13);
+ wback = (m != 15);
+ register_index = ((m != 15) && (m != 13));
+
+ // if d+regs > 32 then UNPREDICTABLE;
+ if ((d + regs) > 32)
+ return false;
+ } break;
+
+ default:
+ return false;
+ }
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ // address = R[n]; if (address MOD alignment) != 0 then
+ // GenerateAlignmentException();
+ addr_t address = Rn;
+ if ((address % alignment) != 0)
+ return false;
+
+ EmulateInstruction::Context context;
+ // if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs);
+ if (wback) {
+ uint32_t Rm = ReadCoreReg(m, &success);
+ if (!success)
+ return false;
+
+ uint32_t offset;
+ if (register_index)
+ offset = Rm;
+ else
+ offset = 8 * regs;
+
+ uint32_t value = Rn + offset;
+ context.type = eContextAdjustBaseRegister;
+ context.SetRegisterPlusOffset(base_reg, offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ value))
+ return false;
+ }
+
+ // for r = 0 to regs-1
+ for (uint32_t r = 0; r < regs; ++r) {
+ // for e = 0 to elements-1
+ uint64_t assembled_data = 0;
+ for (uint32_t e = 0; e < elements; ++e) {
+ // Elem[D[d+r],e,esize] = MemU[address,ebytes];
+ context.type = eContextRegisterLoad;
+ context.SetRegisterPlusOffset(base_reg, address - Rn);
+ uint64_t data = MemURead(context, address, ebytes, 0, &success);
+ if (!success)
+ return false;
+
+ assembled_data =
+ (data << (e * esize)) |
+ assembled_data; // New data goes to the left of existing data
+
+ // address = address + ebytes;
+ address = address + ebytes;
+ }
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_d0 + d + r,
+ assembled_data))
+ return false;
+ }
+ }
+ return true;
+}
+
+// A8.6.308 VLD1 (single element to one lane)
+//
+bool EmulateInstructionARM::EmulateVLD1Single(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n);
+ address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException();
+ if wback then R[n] = R[n] + (if register_index then R[m] else ebytes);
+ Elem[D[d],index,esize] = MemU[address,ebytes];
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t ebytes;
+ uint32_t esize;
+ uint32_t index;
+ uint32_t alignment;
+ uint32_t d;
+ uint32_t n;
+ uint32_t m;
+ bool wback;
+ bool register_index;
+
+ switch (encoding) {
+ case eEncodingT1:
+ case eEncodingA1: {
+ uint32_t size = Bits32(opcode, 11, 10);
+ uint32_t index_align = Bits32(opcode, 7, 4);
+ // if size == '11' then SEE VLD1 (single element to all lanes);
+ if (size == 3)
+ return EmulateVLD1SingleAll(opcode, encoding);
+ // case size of
+ if (size == 0) // when '00'
+ {
+ // if index_align<0> != '0' then UNDEFINED;
+ if (BitIsClear(index_align, 0))
+ return false;
+
+ // ebytes = 1; esize = 8; index = UInt(index_align<3:1>); alignment = 1;
+ ebytes = 1;
+ esize = 8;
+ index = Bits32(index_align, 3, 1);
+ alignment = 1;
+ } else if (size == 1) // when '01'
+ {
+ // if index_align<1> != '0' then UNDEFINED;
+ if (BitIsClear(index_align, 1))
+ return false;
+
+ // ebytes = 2; esize = 16; index = UInt(index_align<3:2>);
+ ebytes = 2;
+ esize = 16;
+ index = Bits32(index_align, 3, 2);
+
+ // alignment = if index_align<0> == '0' then 1 else 2;
+ if (BitIsClear(index_align, 0))
+ alignment = 1;
+ else
+ alignment = 2;
+ } else if (size == 2) // when '10'
+ {
+ // if index_align<2> != '0' then UNDEFINED;
+ if (BitIsClear(index_align, 2))
+ return false;
+
+ // if index_align<1:0> != '00' && index_align<1:0> != '11' then
+ // UNDEFINED;
+ if ((Bits32(index_align, 1, 0) != 0) &&
+ (Bits32(index_align, 1, 0) != 3))
+ return false;
+
+ // ebytes = 4; esize = 32; index = UInt(index_align<3>);
+ ebytes = 4;
+ esize = 32;
+ index = Bit32(index_align, 3);
+
+ // alignment = if index_align<1:0> == '00' then 1 else 4;
+ if (Bits32(index_align, 1, 0) == 0)
+ alignment = 1;
+ else
+ alignment = 4;
+ } else {
+ return false;
+ }
+ // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm);
+ d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // wback = (m != 15); register_index = (m != 15 && m != 13); if n == 15
+ // then UNPREDICTABLE;
+ wback = (m != 15);
+ register_index = ((m != 15) && (m != 13));
+
+ if (n == 15)
+ return false;
+
+ } break;
+
+ default:
+ return false;
+ }
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ // address = R[n]; if (address MOD alignment) != 0 then
+ // GenerateAlignmentException();
+ addr_t address = Rn;
+ if ((address % alignment) != 0)
+ return false;
+
+ EmulateInstruction::Context context;
+ // if wback then R[n] = R[n] + (if register_index then R[m] else ebytes);
+ if (wback) {
+ uint32_t Rm = ReadCoreReg(m, &success);
+ if (!success)
+ return false;
+
+ uint32_t offset;
+ if (register_index)
+ offset = Rm;
+ else
+ offset = ebytes;
+
+ uint32_t value = Rn + offset;
+
+ context.type = eContextAdjustBaseRegister;
+ context.SetRegisterPlusOffset(base_reg, offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ value))
+ return false;
+ }
+
+ // Elem[D[d],index,esize] = MemU[address,ebytes];
+ uint32_t element = MemURead(context, address, esize, 0, &success);
+ if (!success)
+ return false;
+
+ element = element << (index * esize);
+
+ uint64_t reg_data =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_d0 + d, 0, &success);
+ if (!success)
+ return false;
+
+ uint64_t all_ones = -1;
+ uint64_t mask = all_ones
+ << ((index + 1) * esize); // mask is all 1's to left of
+ // where 'element' goes, & all 0's
+ // at element & to the right of element.
+ if (index > 0)
+ mask = mask | Bits64(all_ones, (index * esize) - 1,
+ 0); // add 1's to the right of where 'element' goes.
+ // now mask should be 0's where element goes & 1's everywhere else.
+
+ uint64_t masked_reg =
+ reg_data & mask; // Take original reg value & zero out 'element' bits
+ reg_data =
+ masked_reg & element; // Put 'element' into those bits in reg_data.
+
+ context.type = eContextRegisterLoad;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d,
+ reg_data))
+ return false;
+ }
+ return true;
+}
+
+// A8.6.391 VST1 (multiple single elements) Vector Store (multiple single
+// elements) stores elements to memory from one, two, three, or four registers,
+// without interleaving. Every element of each register is stored.
+bool EmulateInstructionARM::EmulateVST1Multiple(const uint32_t opcode,
+ ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n);
+ address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException();
+ if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs);
+ for r = 0 to regs-1
+ for e = 0 to elements-1
+ MemU[address,ebytes] = Elem[D[d+r],e,esize];
+ address = address + ebytes;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t regs;
+ uint32_t alignment;
+ uint32_t ebytes;
+ uint32_t esize;
+ uint32_t elements;
+ uint32_t d;
+ uint32_t n;
+ uint32_t m;
+ bool wback;
+ bool register_index;
+
+ switch (encoding) {
+ case eEncodingT1:
+ case eEncodingA1: {
+ uint32_t type = Bits32(opcode, 11, 8);
+ uint32_t align = Bits32(opcode, 5, 4);
+
+ // case type of
+ if (type == 7) // when '0111'
+ {
+ // regs = 1; if align<1> == '1' then UNDEFINED;
+ regs = 1;
+ if (BitIsSet(align, 1))
+ return false;
+ } else if (type == 10) // when '1010'
+ {
+ // regs = 2; if align == '11' then UNDEFINED;
+ regs = 2;
+ if (align == 3)
+ return false;
+ } else if (type == 6) // when '0110'
+ {
+ // regs = 3; if align<1> == '1' then UNDEFINED;
+ regs = 3;
+ if (BitIsSet(align, 1))
+ return false;
+ } else if (type == 2) // when '0010'
+ // regs = 4;
+ regs = 4;
+ else // otherwise
+ // SEE 'Related encodings';
+ return false;
+
+ // alignment = if align == '00' then 1 else 4 << UInt(align);
+ if (align == 0)
+ alignment = 1;
+ else
+ alignment = 4 << align;
+
+ // ebytes = 1 << UInt(size); esize = 8 * ebytes; elements = 8 DIV ebytes;
+ ebytes = 1 << Bits32(opcode, 7, 6);
+ esize = 8 * ebytes;
+ elements = 8 / ebytes;
+
+ // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm);
+ d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // wback = (m != 15); register_index = (m != 15 && m != 13);
+ wback = (m != 15);
+ register_index = ((m != 15) && (m != 13));
+
+ // if d+regs > 32 then UNPREDICTABLE; if n == 15 then UNPREDICTABLE;
+ if ((d + regs) > 32)
+ return false;
+
+ if (n == 15)
+ return false;
+
+ } break;
+
+ default:
+ return false;
+ }
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ // address = R[n]; if (address MOD alignment) != 0 then
+ // GenerateAlignmentException();
+ addr_t address = Rn;
+ if ((address % alignment) != 0)
+ return false;
+
+ EmulateInstruction::Context context;
+ // if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs);
+ if (wback) {
+ uint32_t Rm = ReadCoreReg(m, &success);
+ if (!success)
+ return false;
+
+ uint32_t offset;
+ if (register_index)
+ offset = Rm;
+ else
+ offset = 8 * regs;
+
+ context.type = eContextAdjustBaseRegister;
+ context.SetRegisterPlusOffset(base_reg, offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ Rn + offset))
+ return false;
+ }
+
+ RegisterInfo data_reg;
+ context.type = eContextRegisterStore;
+ // for r = 0 to regs-1
+ for (uint32_t r = 0; r < regs; ++r) {
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_d0 + d + r, data_reg);
+ uint64_t register_data = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_d0 + d + r, 0, &success);
+ if (!success)
+ return false;
+
+ // for e = 0 to elements-1
+ for (uint32_t e = 0; e < elements; ++e) {
+ // MemU[address,ebytes] = Elem[D[d+r],e,esize];
+ uint64_t word = Bits64(register_data, ((e + 1) * esize) - 1, e * esize);
+
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg,
+ address - Rn);
+ if (!MemUWrite(context, address, word, ebytes))
+ return false;
+
+ // address = address + ebytes;
+ address = address + ebytes;
+ }
+ }
+ }
+ return true;
+}
+
+// A8.6.392 VST1 (single element from one lane) This instruction stores one
+// element to memory from one element of a register.
+bool EmulateInstructionARM::EmulateVST1Single(const uint32_t opcode,
+ ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n);
+ address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException();
+ if wback then R[n] = R[n] + (if register_index then R[m] else ebytes);
+ MemU[address,ebytes] = Elem[D[d],index,esize];
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t ebytes;
+ uint32_t esize;
+ uint32_t index;
+ uint32_t alignment;
+ uint32_t d;
+ uint32_t n;
+ uint32_t m;
+ bool wback;
+ bool register_index;
+
+ switch (encoding) {
+ case eEncodingT1:
+ case eEncodingA1: {
+ uint32_t size = Bits32(opcode, 11, 10);
+ uint32_t index_align = Bits32(opcode, 7, 4);
+
+ // if size == '11' then UNDEFINED;
+ if (size == 3)
+ return false;
+
+ // case size of
+ if (size == 0) // when '00'
+ {
+ // if index_align<0> != '0' then UNDEFINED;
+ if (BitIsClear(index_align, 0))
+ return false;
+ // ebytes = 1; esize = 8; index = UInt(index_align<3:1>); alignment = 1;
+ ebytes = 1;
+ esize = 8;
+ index = Bits32(index_align, 3, 1);
+ alignment = 1;
+ } else if (size == 1) // when '01'
+ {
+ // if index_align<1> != '0' then UNDEFINED;
+ if (BitIsClear(index_align, 1))
+ return false;
+
+ // ebytes = 2; esize = 16; index = UInt(index_align<3:2>);
+ ebytes = 2;
+ esize = 16;
+ index = Bits32(index_align, 3, 2);
+
+ // alignment = if index_align<0> == '0' then 1 else 2;
+ if (BitIsClear(index_align, 0))
+ alignment = 1;
+ else
+ alignment = 2;
+ } else if (size == 2) // when '10'
+ {
+ // if index_align<2> != '0' then UNDEFINED;
+ if (BitIsClear(index_align, 2))
+ return false;
+
+ // if index_align<1:0> != '00' && index_align<1:0> != '11' then
+ // UNDEFINED;
+ if ((Bits32(index_align, 1, 0) != 0) &&
+ (Bits32(index_align, 1, 0) != 3))
+ return false;
+
+ // ebytes = 4; esize = 32; index = UInt(index_align<3>);
+ ebytes = 4;
+ esize = 32;
+ index = Bit32(index_align, 3);
+
+ // alignment = if index_align<1:0> == '00' then 1 else 4;
+ if (Bits32(index_align, 1, 0) == 0)
+ alignment = 1;
+ else
+ alignment = 4;
+ } else {
+ return false;
+ }
+ // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm);
+ d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // wback = (m != 15); register_index = (m != 15 && m != 13); if n == 15
+ // then UNPREDICTABLE;
+ wback = (m != 15);
+ register_index = ((m != 15) && (m != 13));
+
+ if (n == 15)
+ return false;
+ } break;
+
+ default:
+ return false;
+ }
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ // address = R[n]; if (address MOD alignment) != 0 then
+ // GenerateAlignmentException();
+ addr_t address = Rn;
+ if ((address % alignment) != 0)
+ return false;
+
+ EmulateInstruction::Context context;
+ // if wback then R[n] = R[n] + (if register_index then R[m] else ebytes);
+ if (wback) {
+ uint32_t Rm = ReadCoreReg(m, &success);
+ if (!success)
+ return false;
+
+ uint32_t offset;
+ if (register_index)
+ offset = Rm;
+ else
+ offset = ebytes;
+
+ context.type = eContextAdjustBaseRegister;
+ context.SetRegisterPlusOffset(base_reg, offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ Rn + offset))
+ return false;
+ }
+
+ // MemU[address,ebytes] = Elem[D[d],index,esize];
+ uint64_t register_data =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_d0 + d, 0, &success);
+ if (!success)
+ return false;
+
+ uint64_t word =
+ Bits64(register_data, ((index + 1) * esize) - 1, index * esize);
+
+ RegisterInfo data_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_d0 + d, data_reg);
+ context.type = eContextRegisterStore;
+ context.SetRegisterToRegisterPlusOffset(data_reg, base_reg, address - Rn);
+
+ if (!MemUWrite(context, address, word, ebytes))
+ return false;
+ }
+ return true;
+}
+
+// A8.6.309 VLD1 (single element to all lanes) This instruction loads one
+// element from memory into every element of one or two vectors.
+bool EmulateInstructionARM::EmulateVLD1SingleAll(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n);
+ address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException();
+ if wback then R[n] = R[n] + (if register_index then R[m] else ebytes);
+ replicated_element = Replicate(MemU[address,ebytes], elements);
+ for r = 0 to regs-1
+ D[d+r] = replicated_element;
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t ebytes;
+ uint32_t elements;
+ uint32_t regs;
+ uint32_t alignment;
+ uint32_t d;
+ uint32_t n;
+ uint32_t m;
+ bool wback;
+ bool register_index;
+
+ switch (encoding) {
+ case eEncodingT1:
+ case eEncodingA1: {
+ // if size == '11' || (size == '00' && a == '1') then UNDEFINED;
+ uint32_t size = Bits32(opcode, 7, 6);
+ if ((size == 3) || ((size == 0) && BitIsSet(opcode, 4)))
+ return false;
+
+ // ebytes = 1 << UInt(size); elements = 8 DIV ebytes; regs = if T == '0'
+ // then 1 else 2;
+ ebytes = 1 << size;
+ elements = 8 / ebytes;
+ if (BitIsClear(opcode, 5))
+ regs = 1;
+ else
+ regs = 2;
+
+ // alignment = if a == '0' then 1 else ebytes;
+ if (BitIsClear(opcode, 4))
+ alignment = 1;
+ else
+ alignment = ebytes;
+
+ // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm);
+ d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12);
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+
+ // wback = (m != 15); register_index = (m != 15 && m != 13);
+ wback = (m != 15);
+ register_index = ((m != 15) && (m != 13));
+
+ // if d+regs > 32 then UNPREDICTABLE; if n == 15 then UNPREDICTABLE;
+ if ((d + regs) > 32)
+ return false;
+
+ if (n == 15)
+ return false;
+ } break;
+
+ default:
+ return false;
+ }
+
+ RegisterInfo base_reg;
+ GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, base_reg);
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ // address = R[n]; if (address MOD alignment) != 0 then
+ // GenerateAlignmentException();
+ addr_t address = Rn;
+ if ((address % alignment) != 0)
+ return false;
+
+ EmulateInstruction::Context context;
+ // if wback then R[n] = R[n] + (if register_index then R[m] else ebytes);
+ if (wback) {
+ uint32_t Rm = ReadCoreReg(m, &success);
+ if (!success)
+ return false;
+
+ uint32_t offset;
+ if (register_index)
+ offset = Rm;
+ else
+ offset = ebytes;
+
+ context.type = eContextAdjustBaseRegister;
+ context.SetRegisterPlusOffset(base_reg, offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n,
+ Rn + offset))
+ return false;
+ }
+
+ // replicated_element = Replicate(MemU[address,ebytes], elements);
+
+ context.type = eContextRegisterLoad;
+ uint64_t word = MemURead(context, address, ebytes, 0, &success);
+ if (!success)
+ return false;
+
+ uint64_t replicated_element = 0;
+ uint32_t esize = ebytes * 8;
+ for (uint32_t e = 0; e < elements; ++e)
+ replicated_element =
+ (replicated_element << esize) | Bits64(word, esize - 1, 0);
+
+ // for r = 0 to regs-1
+ for (uint32_t r = 0; r < regs; ++r) {
+ // D[d+r] = replicated_element;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_d0 + d + r,
+ replicated_element))
+ return false;
+ }
+ }
+ return true;
+}
+
+// B6.2.13 SUBS PC, LR and related instructions The SUBS PC, LR, #<const?
+// instruction provides an exception return without the use of the stack. It
+// subtracts the immediate constant from the LR, branches to the resulting
+// address, and also copies the SPSR to the CPSR.
+bool EmulateInstructionARM::EmulateSUBSPcLrEtc(const uint32_t opcode,
+ const ARMEncoding encoding) {
+#if 0
+ if ConditionPassed() then
+ EncodingSpecificOperations();
+ if CurrentInstrSet() == InstrSet_ThumbEE then
+ UNPREDICTABLE;
+ operand2 = if register_form then Shift(R[m], shift_t, shift_n, APSR.C) else imm32;
+ case opcode of
+ when '0000' result = R[n] AND operand2; // AND
+ when '0001' result = R[n] EOR operand2; // EOR
+ when '0010' (result, -, -) = AddWithCarry(R[n], NOT(operand2), '1'); // SUB
+ when '0011' (result, -, -) = AddWithCarry(NOT(R[n]), operand2, '1'); // RSB
+ when '0100' (result, -, -) = AddWithCarry(R[n], operand2, '0'); // ADD
+ when '0101' (result, -, -) = AddWithCarry(R[n], operand2, APSR.c); // ADC
+ when '0110' (result, -, -) = AddWithCarry(R[n], NOT(operand2), APSR.C); // SBC
+ when '0111' (result, -, -) = AddWithCarry(NOT(R[n]), operand2, APSR.C); // RSC
+ when '1100' result = R[n] OR operand2; // ORR
+ when '1101' result = operand2; // MOV
+ when '1110' result = R[n] AND NOT(operand2); // BIC
+ when '1111' result = NOT(operand2); // MVN
+ CPSRWriteByInstr(SPSR[], '1111', TRUE);
+ BranchWritePC(result);
+#endif
+
+ bool success = false;
+
+ if (ConditionPassed(opcode)) {
+ uint32_t n;
+ uint32_t m;
+ uint32_t imm32;
+ bool register_form;
+ ARM_ShifterType shift_t;
+ uint32_t shift_n;
+ uint32_t code;
+
+ switch (encoding) {
+ case eEncodingT1:
+ // if CurrentInstrSet() == InstrSet_ThumbEE then UNPREDICTABLE n = 14;
+ // imm32 = ZeroExtend(imm8, 32); register_form = FALSE; opcode = '0010';
+ // // = SUB
+ n = 14;
+ imm32 = Bits32(opcode, 7, 0);
+ register_form = false;
+ code = 2;
+
+ // if InITBlock() && !LastInITBlock() then UNPREDICTABLE;
+ if (InITBlock() && !LastInITBlock())
+ return false;
+
+ break;
+
+ case eEncodingA1:
+ // n = UInt(Rn); imm32 = ARMExpandImm(imm12); register_form = FALSE;
+ n = Bits32(opcode, 19, 16);
+ imm32 = ARMExpandImm(opcode);
+ register_form = false;
+ code = Bits32(opcode, 24, 21);
+
+ break;
+
+ case eEncodingA2:
+ // n = UInt(Rn); m = UInt(Rm); register_form = TRUE;
+ n = Bits32(opcode, 19, 16);
+ m = Bits32(opcode, 3, 0);
+ register_form = true;
+
+ // (shift_t, shift_n) = DecodeImmShift(type, imm5);
+ shift_n = DecodeImmShiftARM(opcode, shift_t);
+
+ break;
+
+ default:
+ return false;
+ }
+
+ // operand2 = if register_form then Shift(R[m], shift_t, shift_n, APSR.C)
+ // else imm32;
+ uint32_t operand2;
+ if (register_form) {
+ uint32_t Rm = ReadCoreReg(m, &success);
+ if (!success)
+ return false;
+
+ operand2 = Shift(Rm, shift_t, shift_n, APSR_C, &success);
+ if (!success)
+ return false;
+ } else {
+ operand2 = imm32;
+ }
+
+ uint32_t Rn = ReadCoreReg(n, &success);
+ if (!success)
+ return false;
+
+ AddWithCarryResult result;
+
+ // case opcode of
+ switch (code) {
+ case 0: // when '0000'
+ // result = R[n] AND operand2; // AND
+ result.result = Rn & operand2;
+ break;
+
+ case 1: // when '0001'
+ // result = R[n] EOR operand2; // EOR
+ result.result = Rn ^ operand2;
+ break;
+
+ case 2: // when '0010'
+ // (result, -, -) = AddWithCarry(R[n], NOT(operand2), '1'); // SUB
+ result = AddWithCarry(Rn, ~(operand2), 1);
+ break;
+
+ case 3: // when '0011'
+ // (result, -, -) = AddWithCarry(NOT(R[n]), operand2, '1'); // RSB
+ result = AddWithCarry(~(Rn), operand2, 1);
+ break;
+
+ case 4: // when '0100'
+ // (result, -, -) = AddWithCarry(R[n], operand2, '0'); // ADD
+ result = AddWithCarry(Rn, operand2, 0);
+ break;
+
+ case 5: // when '0101'
+ // (result, -, -) = AddWithCarry(R[n], operand2, APSR.c); // ADC
+ result = AddWithCarry(Rn, operand2, APSR_C);
+ break;
+
+ case 6: // when '0110'
+ // (result, -, -) = AddWithCarry(R[n], NOT(operand2), APSR.C); // SBC
+ result = AddWithCarry(Rn, ~(operand2), APSR_C);
+ break;
+
+ case 7: // when '0111'
+ // (result, -, -) = AddWithCarry(NOT(R[n]), operand2, APSR.C); // RSC
+ result = AddWithCarry(~(Rn), operand2, APSR_C);
+ break;
+
+ case 10: // when '1100'
+ // result = R[n] OR operand2; // ORR
+ result.result = Rn | operand2;
+ break;
+
+ case 11: // when '1101'
+ // result = operand2; // MOV
+ result.result = operand2;
+ break;
+
+ case 12: // when '1110'
+ // result = R[n] AND NOT(operand2); // BIC
+ result.result = Rn & ~(operand2);
+ break;
+
+ case 15: // when '1111'
+ // result = NOT(operand2); // MVN
+ result.result = ~(operand2);
+ break;
+
+ default:
+ return false;
+ }
+ // CPSRWriteByInstr(SPSR[], '1111', TRUE);
+
+ // For now, in emulation mode, we don't have access to the SPSR, so we will
+ // use the CPSR instead, and hope for the best.
+ uint32_t spsr =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_cpsr, 0, &success);
+ if (!success)
+ return false;
+
+ CPSRWriteByInstr(spsr, 15, true);
+
+ // BranchWritePC(result);
+ EmulateInstruction::Context context;
+ context.type = eContextAdjustPC;
+ context.SetImmediate(result.result);
+
+ BranchWritePC(context, result.result);
+ }
+ return true;
+}
+
+EmulateInstructionARM::ARMOpcode *
+EmulateInstructionARM::GetARMOpcodeForInstruction(const uint32_t opcode,
+ uint32_t arm_isa) {
+ static ARMOpcode g_arm_opcodes[] = {
+ // Prologue instructions
+
+ // push register(s)
+ {0x0fff0000, 0x092d0000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulatePUSH, "push <registers>"},
+ {0x0fff0fff, 0x052d0004, ARMvAll, eEncodingA2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulatePUSH, "push <register>"},
+
+ // set r7 to point to a stack offset
+ {0x0ffff000, 0x028d7000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADDRdSPImm, "add r7, sp, #<const>"},
+ {0x0ffff000, 0x024c7000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBR7IPImm, "sub r7, ip, #<const>"},
+ // copy the stack pointer to ip
+ {0x0fffffff, 0x01a0c00d, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMOVRdSP, "mov ip, sp"},
+ {0x0ffff000, 0x028dc000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADDRdSPImm, "add ip, sp, #<const>"},
+ {0x0ffff000, 0x024dc000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBIPSPImm, "sub ip, sp, #<const>"},
+
+ // adjust the stack pointer
+ {0x0ffff000, 0x024dd000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBSPImm, "sub sp, sp, #<const>"},
+ {0x0fef0010, 0x004d0000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBSPReg,
+ "sub{s}<c> <Rd>, sp, <Rm>{,<shift>}"},
+
+ // push one register
+ // if Rn == '1101' && imm12 == '000000000100' then SEE PUSH;
+ {0x0e5f0000, 0x040d0000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRRtSP, "str Rt, [sp, #-imm12]!"},
+
+ // vector push consecutive extension register(s)
+ {0x0fbf0f00, 0x0d2d0b00, ARMV6T2_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateVPUSH, "vpush.64 <list>"},
+ {0x0fbf0f00, 0x0d2d0a00, ARMV6T2_ABOVE, eEncodingA2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateVPUSH, "vpush.32 <list>"},
+
+ // Epilogue instructions
+
+ {0x0fff0000, 0x08bd0000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulatePOP, "pop <registers>"},
+ {0x0fff0fff, 0x049d0004, ARMvAll, eEncodingA2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulatePOP, "pop <register>"},
+ {0x0fbf0f00, 0x0cbd0b00, ARMV6T2_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateVPOP, "vpop.64 <list>"},
+ {0x0fbf0f00, 0x0cbd0a00, ARMV6T2_ABOVE, eEncodingA2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateVPOP, "vpop.32 <list>"},
+
+ // Supervisor Call (previously Software Interrupt)
+ {0x0f000000, 0x0f000000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSVC, "svc #imm24"},
+
+ // Branch instructions
+ // To resolve ambiguity, "blx <label>" should come before "b #imm24" and
+ // "bl <label>".
+ {0xfe000000, 0xfa000000, ARMV5_ABOVE, eEncodingA2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBLXImmediate, "blx <label>"},
+ {0x0f000000, 0x0a000000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateB, "b #imm24"},
+ {0x0f000000, 0x0b000000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBLXImmediate, "bl <label>"},
+ {0x0ffffff0, 0x012fff30, ARMV5_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBLXRm, "blx <Rm>"},
+ // for example, "bx lr"
+ {0x0ffffff0, 0x012fff10, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBXRm, "bx <Rm>"},
+ // bxj
+ {0x0ffffff0, 0x012fff20, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBXJRm, "bxj <Rm>"},
+
+ // Data-processing instructions
+ // adc (immediate)
+ {0x0fe00000, 0x02a00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADCImm, "adc{s}<c> <Rd>, <Rn>, #const"},
+ // adc (register)
+ {0x0fe00010, 0x00a00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADCReg,
+ "adc{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // add (immediate)
+ {0x0fe00000, 0x02800000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADDImmARM,
+ "add{s}<c> <Rd>, <Rn>, #const"},
+ // add (register)
+ {0x0fe00010, 0x00800000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADDReg,
+ "add{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // add (register-shifted register)
+ {0x0fe00090, 0x00800010, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADDRegShift,
+ "add{s}<c> <Rd>, <Rn>, <Rm>, <type> <RS>"},
+ // adr
+ {0x0fff0000, 0x028f0000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADR, "add<c> <Rd>, PC, #<const>"},
+ {0x0fff0000, 0x024f0000, ARMvAll, eEncodingA2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADR, "sub<c> <Rd>, PC, #<const>"},
+ // and (immediate)
+ {0x0fe00000, 0x02000000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateANDImm, "and{s}<c> <Rd>, <Rn>, #const"},
+ // and (register)
+ {0x0fe00010, 0x00000000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateANDReg,
+ "and{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // bic (immediate)
+ {0x0fe00000, 0x03c00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBICImm, "bic{s}<c> <Rd>, <Rn>, #const"},
+ // bic (register)
+ {0x0fe00010, 0x01c00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBICReg,
+ "bic{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // eor (immediate)
+ {0x0fe00000, 0x02200000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateEORImm, "eor{s}<c> <Rd>, <Rn>, #const"},
+ // eor (register)
+ {0x0fe00010, 0x00200000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateEORReg,
+ "eor{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // orr (immediate)
+ {0x0fe00000, 0x03800000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateORRImm, "orr{s}<c> <Rd>, <Rn>, #const"},
+ // orr (register)
+ {0x0fe00010, 0x01800000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateORRReg,
+ "orr{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // rsb (immediate)
+ {0x0fe00000, 0x02600000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRSBImm, "rsb{s}<c> <Rd>, <Rn>, #<const>"},
+ // rsb (register)
+ {0x0fe00010, 0x00600000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRSBReg,
+ "rsb{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // rsc (immediate)
+ {0x0fe00000, 0x02e00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRSCImm, "rsc{s}<c> <Rd>, <Rn>, #<const>"},
+ // rsc (register)
+ {0x0fe00010, 0x00e00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRSCReg,
+ "rsc{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // sbc (immediate)
+ {0x0fe00000, 0x02c00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSBCImm, "sbc{s}<c> <Rd>, <Rn>, #<const>"},
+ // sbc (register)
+ {0x0fe00010, 0x00c00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSBCReg,
+ "sbc{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // sub (immediate, ARM)
+ {0x0fe00000, 0x02400000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBImmARM,
+ "sub{s}<c> <Rd>, <Rn>, #<const>"},
+ // sub (sp minus immediate)
+ {0x0fef0000, 0x024d0000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBSPImm, "sub{s}<c> <Rd>, sp, #<const>"},
+ // sub (register)
+ {0x0fe00010, 0x00400000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBReg,
+ "sub{s}<c> <Rd>, <Rn>, <Rm>{,<shift>}"},
+ // teq (immediate)
+ {0x0ff0f000, 0x03300000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateTEQImm, "teq<c> <Rn>, #const"},
+ // teq (register)
+ {0x0ff0f010, 0x01300000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateTEQReg, "teq<c> <Rn>, <Rm> {,<shift>}"},
+ // tst (immediate)
+ {0x0ff0f000, 0x03100000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateTSTImm, "tst<c> <Rn>, #const"},
+ // tst (register)
+ {0x0ff0f010, 0x01100000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateTSTReg, "tst<c> <Rn>, <Rm> {,<shift>}"},
+
+ // mov (immediate)
+ {0x0fef0000, 0x03a00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMOVRdImm, "mov{s}<c> <Rd>, #<const>"},
+ {0x0ff00000, 0x03000000, ARMV6T2_ABOVE, eEncodingA2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMOVRdImm, "movw<c> <Rd>, #<imm16>"},
+ // mov (register)
+ {0x0fef0ff0, 0x01a00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMOVRdRm, "mov{s}<c> <Rd>, <Rm>"},
+ // mvn (immediate)
+ {0x0fef0000, 0x03e00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMVNImm, "mvn{s}<c> <Rd>, #<const>"},
+ // mvn (register)
+ {0x0fef0010, 0x01e00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMVNReg,
+ "mvn{s}<c> <Rd>, <Rm> {,<shift>}"},
+ // cmn (immediate)
+ {0x0ff0f000, 0x03700000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateCMNImm, "cmn<c> <Rn>, #<const>"},
+ // cmn (register)
+ {0x0ff0f010, 0x01700000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateCMNReg, "cmn<c> <Rn>, <Rm> {,<shift>}"},
+ // cmp (immediate)
+ {0x0ff0f000, 0x03500000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateCMPImm, "cmp<c> <Rn>, #<const>"},
+ // cmp (register)
+ {0x0ff0f010, 0x01500000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateCMPReg, "cmp<c> <Rn>, <Rm> {,<shift>}"},
+ // asr (immediate)
+ {0x0fef0070, 0x01a00040, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateASRImm, "asr{s}<c> <Rd>, <Rm>, #imm"},
+ // asr (register)
+ {0x0fef00f0, 0x01a00050, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateASRReg, "asr{s}<c> <Rd>, <Rn>, <Rm>"},
+ // lsl (immediate)
+ {0x0fef0070, 0x01a00000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLSLImm, "lsl{s}<c> <Rd>, <Rm>, #imm"},
+ // lsl (register)
+ {0x0fef00f0, 0x01a00010, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLSLReg, "lsl{s}<c> <Rd>, <Rn>, <Rm>"},
+ // lsr (immediate)
+ {0x0fef0070, 0x01a00020, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLSRImm, "lsr{s}<c> <Rd>, <Rm>, #imm"},
+ // lsr (register)
+ {0x0fef00f0, 0x01a00050, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLSRReg, "lsr{s}<c> <Rd>, <Rn>, <Rm>"},
+ // rrx is a special case encoding of ror (immediate)
+ {0x0fef0ff0, 0x01a00060, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRRX, "rrx{s}<c> <Rd>, <Rm>"},
+ // ror (immediate)
+ {0x0fef0070, 0x01a00060, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRORImm, "ror{s}<c> <Rd>, <Rm>, #imm"},
+ // ror (register)
+ {0x0fef00f0, 0x01a00070, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRORReg, "ror{s}<c> <Rd>, <Rn>, <Rm>"},
+ // mul
+ {0x0fe000f0, 0x00000090, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMUL, "mul{s}<c> <Rd>,<R>,<Rm>"},
+
+ // subs pc, lr and related instructions
+ {0x0e10f000, 0x0210f000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBSPcLrEtc,
+ "<opc>S<c> PC,#<const> | <Rn>,#<const>"},
+ {0x0e10f010, 0x0010f000, ARMvAll, eEncodingA2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBSPcLrEtc,
+ "<opc>S<c> PC,<Rn>,<Rm{,<shift>}"},
+
+ // Load instructions
+ {0x0fd00000, 0x08900000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDM, "ldm<c> <Rn>{!} <registers>"},
+ {0x0fd00000, 0x08100000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDMDA, "ldmda<c> <Rn>{!} <registers>"},
+ {0x0fd00000, 0x09100000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDMDB, "ldmdb<c> <Rn>{!} <registers>"},
+ {0x0fd00000, 0x09900000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDMIB, "ldmib<c> <Rn<{!} <registers>"},
+ {0x0e500000, 0x04100000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRImmediateARM,
+ "ldr<c> <Rt> [<Rn> {#+/-<imm12>}]"},
+ {0x0e500010, 0x06100000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRRegister,
+ "ldr<c> <Rt> [<Rn> +/-<Rm> {<shift>}] {!}"},
+ {0x0e5f0000, 0x045f0000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRBLiteral, "ldrb<c> <Rt>, [...]"},
+ {0xfe500010, 0x06500000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRBRegister,
+ "ldrb<c> <Rt>, [<Rn>,+/-<Rm>{, <shift>}]{!}"},
+ {0x0e5f00f0, 0x005f00b0, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRHLiteral, "ldrh<c> <Rt>, <label>"},
+ {0x0e5000f0, 0x001000b0, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRHRegister,
+ "ldrh<c> <Rt>,[<Rn>,+/-<Rm>]{!}"},
+ {0x0e5000f0, 0x005000d0, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSBImmediate,
+ "ldrsb<c> <Rt>, [<Rn>{,#+/-<imm8>}]"},
+ {0x0e5f00f0, 0x005f00d0, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSBLiteral, "ldrsb<c> <Rt> <label>"},
+ {0x0e5000f0, 0x001000d0, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSBRegister,
+ "ldrsb<c> <Rt>,[<Rn>,+/-<Rm>]{!}"},
+ {0x0e5000f0, 0x005000f0, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSHImmediate,
+ "ldrsh<c> <Rt>,[<Rn>{,#+/-<imm8>}]"},
+ {0x0e5f00f0, 0x005f00f0, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSHLiteral, "ldrsh<c> <Rt>,<label>"},
+ {0x0e5000f0, 0x001000f0, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSHRegister,
+ "ldrsh<c> <Rt>,[<Rn>,+/-<Rm>]{!}"},
+ {0x0e5000f0, 0x004000d0, ARMV5TE_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRDImmediate,
+ "ldrd<c> <Rt>, <Rt2>, [<Rn>,#+/-<imm8>]!"},
+ {0x0e500ff0, 0x000000d0, ARMV5TE_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRDRegister,
+ "ldrd<c> <Rt>, <Rt2>, [<Rn>, +/-<Rm>]{!}"},
+ {0x0e100f00, 0x0c100b00, ARMvAll, eEncodingA1, VFPv2_ABOVE, eSize32,
+ &EmulateInstructionARM::EmulateVLDM, "vldm{mode}<c> <Rn>{!}, <list>"},
+ {0x0e100f00, 0x0c100a00, ARMvAll, eEncodingA2, VFPv2v3, eSize32,
+ &EmulateInstructionARM::EmulateVLDM, "vldm{mode}<c> <Rn>{!}, <list>"},
+ {0x0f300f00, 0x0d100b00, ARMvAll, eEncodingA1, VFPv2_ABOVE, eSize32,
+ &EmulateInstructionARM::EmulateVLDR, "vldr<c> <Dd>, [<Rn>{,#+/-<imm>}]"},
+ {0x0f300f00, 0x0d100a00, ARMvAll, eEncodingA2, VFPv2v3, eSize32,
+ &EmulateInstructionARM::EmulateVLDR, "vldr<c> <Sd>, [<Rn>{,#+/-<imm>}]"},
+ {0xffb00000, 0xf4200000, ARMvAll, eEncodingA1, AdvancedSIMD, eSize32,
+ &EmulateInstructionARM::EmulateVLD1Multiple,
+ "vld1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"},
+ {0xffb00300, 0xf4a00000, ARMvAll, eEncodingA1, AdvancedSIMD, eSize32,
+ &EmulateInstructionARM::EmulateVLD1Single,
+ "vld1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"},
+ {0xffb00f00, 0xf4a00c00, ARMvAll, eEncodingA1, AdvancedSIMD, eSize32,
+ &EmulateInstructionARM::EmulateVLD1SingleAll,
+ "vld1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"},
+
+ // Store instructions
+ {0x0fd00000, 0x08800000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTM, "stm<c> <Rn>{!} <registers>"},
+ {0x0fd00000, 0x08000000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTMDA, "stmda<c> <Rn>{!} <registers>"},
+ {0x0fd00000, 0x09000000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTMDB, "stmdb<c> <Rn>{!} <registers>"},
+ {0x0fd00000, 0x09800000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTMIB, "stmib<c> <Rn>{!} <registers>"},
+ {0x0e500010, 0x06000000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRRegister,
+ "str<c> <Rt> [<Rn> +/-<Rm> {<shift>}]{!}"},
+ {0x0e5000f0, 0x000000b0, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRHRegister,
+ "strh<c> <Rt>,[<Rn>,+/-<Rm>[{!}"},
+ {0x0ff00ff0, 0x01800f90, ARMV6_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTREX, "strex<c> <Rd>, <Rt>, [<Rn>]"},
+ {0x0e500000, 0x04400000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRBImmARM,
+ "strb<c> <Rt>,[<Rn>,#+/-<imm12>]!"},
+ {0x0e500000, 0x04000000, ARMvAll, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRImmARM,
+ "str<c> <Rt>,[<Rn>,#+/-<imm12>]!"},
+ {0x0e5000f0, 0x004000f0, ARMV5TE_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRDImm,
+ "strd<c> <Rt>, <Rt2>, [<Rn> #+/-<imm8>]!"},
+ {0x0e500ff0, 0x000000f0, ARMV5TE_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRDReg,
+ "strd<c> <Rt>, <Rt2>, [<Rn>, +/-<Rm>]{!}"},
+ {0x0e100f00, 0x0c000b00, ARMvAll, eEncodingA1, VFPv2_ABOVE, eSize32,
+ &EmulateInstructionARM::EmulateVSTM, "vstm{mode}<c> <Rn>{!} <list>"},
+ {0x0e100f00, 0x0c000a00, ARMvAll, eEncodingA2, VFPv2v3, eSize32,
+ &EmulateInstructionARM::EmulateVSTM, "vstm{mode}<c> <Rn>{!} <list>"},
+ {0x0f300f00, 0x0d000b00, ARMvAll, eEncodingA1, VFPv2_ABOVE, eSize32,
+ &EmulateInstructionARM::EmulateVSTR, "vstr<c> <Dd> [<Rn>{,#+/-<imm>}]"},
+ {0x0f300f00, 0x0d000a00, ARMvAll, eEncodingA2, VFPv2v3, eSize32,
+ &EmulateInstructionARM::EmulateVSTR, "vstr<c> <Sd> [<Rn>{,#+/-<imm>}]"},
+ {0xffb00000, 0xf4000000, ARMvAll, eEncodingA1, AdvancedSIMD, eSize32,
+ &EmulateInstructionARM::EmulateVST1Multiple,
+ "vst1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"},
+ {0xffb00300, 0xf4800000, ARMvAll, eEncodingA1, AdvancedSIMD, eSize32,
+ &EmulateInstructionARM::EmulateVST1Single,
+ "vst1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"},
+
+ // Other instructions
+ {0x0fff00f0, 0x06af00f0, ARMV6_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSXTB, "sxtb<c> <Rd>,<Rm>{,<rotation>}"},
+ {0x0fff00f0, 0x06bf0070, ARMV6_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSXTH, "sxth<c> <Rd>,<Rm>{,<rotation>}"},
+ {0x0fff00f0, 0x06ef0070, ARMV6_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateUXTB, "uxtb<c> <Rd>,<Rm>{,<rotation>}"},
+ {0x0fff00f0, 0x06ff0070, ARMV6_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateUXTH, "uxth<c> <Rd>,<Rm>{,<rotation>}"},
+ {0xfe500000, 0xf8100000, ARMV6_ABOVE, eEncodingA1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRFE, "rfe{<amode>} <Rn>{!}"}
+
+ };
+ static const size_t k_num_arm_opcodes = llvm::array_lengthof(g_arm_opcodes);
+
+ for (size_t i = 0; i < k_num_arm_opcodes; ++i) {
+ if ((g_arm_opcodes[i].mask & opcode) == g_arm_opcodes[i].value &&
+ (g_arm_opcodes[i].variants & arm_isa) != 0)
+ return &g_arm_opcodes[i];
+ }
+ return nullptr;
+}
+
+EmulateInstructionARM::ARMOpcode *
+EmulateInstructionARM::GetThumbOpcodeForInstruction(const uint32_t opcode,
+ uint32_t arm_isa) {
+
+ static ARMOpcode g_thumb_opcodes[] = {
+ // Prologue instructions
+
+ // push register(s)
+ {0xfffffe00, 0x0000b400, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulatePUSH, "push <registers>"},
+ {0xffff0000, 0xe92d0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulatePUSH, "push.w <registers>"},
+ {0xffff0fff, 0xf84d0d04, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulatePUSH, "push.w <register>"},
+
+ // set r7 to point to a stack offset
+ {0xffffff00, 0x0000af00, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateADDRdSPImm, "add r7, sp, #imm"},
+ // copy the stack pointer to r7
+ {0xffffffff, 0x0000466f, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateMOVRdSP, "mov r7, sp"},
+ // move from high register to low register (comes after "mov r7, sp" to
+ // resolve ambiguity)
+ {0xffffffc0, 0x00004640, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateMOVLowHigh, "mov r0-r7, r8-r15"},
+
+ // PC-relative load into register (see also EmulateADDSPRm)
+ {0xfffff800, 0x00004800, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLDRRtPCRelative, "ldr <Rt>, [PC, #imm]"},
+
+ // adjust the stack pointer
+ {0xffffff87, 0x00004485, ARMvAll, eEncodingT2, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateADDSPRm, "add sp, <Rm>"},
+ {0xffffff80, 0x0000b080, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSUBSPImm, "sub sp, sp, #imm"},
+ {0xfbef8f00, 0xf1ad0d00, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBSPImm, "sub.w sp, sp, #<const>"},
+ {0xfbff8f00, 0xf2ad0d00, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBSPImm, "subw sp, sp, #imm12"},
+ {0xffef8000, 0xebad0000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBSPReg,
+ "sub{s}<c> <Rd>, sp, <Rm>{,<shift>}"},
+
+ // vector push consecutive extension register(s)
+ {0xffbf0f00, 0xed2d0b00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateVPUSH, "vpush.64 <list>"},
+ {0xffbf0f00, 0xed2d0a00, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateVPUSH, "vpush.32 <list>"},
+
+ // Epilogue instructions
+
+ {0xfffff800, 0x0000a800, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateADDSPImm, "add<c> <Rd>, sp, #imm"},
+ {0xffffff80, 0x0000b000, ARMvAll, eEncodingT2, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateADDSPImm, "add sp, #imm"},
+ {0xfffffe00, 0x0000bc00, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulatePOP, "pop <registers>"},
+ {0xffff0000, 0xe8bd0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulatePOP, "pop.w <registers>"},
+ {0xffff0fff, 0xf85d0d04, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulatePOP, "pop.w <register>"},
+ {0xffbf0f00, 0xecbd0b00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateVPOP, "vpop.64 <list>"},
+ {0xffbf0f00, 0xecbd0a00, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateVPOP, "vpop.32 <list>"},
+
+ // Supervisor Call (previously Software Interrupt)
+ {0xffffff00, 0x0000df00, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSVC, "svc #imm8"},
+
+ // If Then makes up to four following instructions conditional.
+ // The next 5 opcode _must_ come before the if then instruction
+ {0xffffffff, 0x0000bf00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateNop, "nop"},
+ {0xffffffff, 0x0000bf10, ARMV7_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateNop, "nop YIELD (yield hint)"},
+ {0xffffffff, 0x0000bf20, ARMV7_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateNop, "nop WFE (wait for event hint)"},
+ {0xffffffff, 0x0000bf30, ARMV7_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateNop, "nop WFI (wait for interrupt hint)"},
+ {0xffffffff, 0x0000bf40, ARMV7_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateNop, "nop SEV (send event hint)"},
+ {0xffffff00, 0x0000bf00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateIT, "it{<x>{<y>{<z>}}} <firstcond>"},
+
+ // Branch instructions
+ // To resolve ambiguity, "b<c> #imm8" should come after "svc #imm8".
+ {0xfffff000, 0x0000d000, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateB, "b<c> #imm8 (outside IT)"},
+ {0xfffff800, 0x0000e000, ARMvAll, eEncodingT2, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateB, "b<c> #imm11 (outside or last in IT)"},
+ {0xf800d000, 0xf0008000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateB, "b<c>.w #imm8 (outside IT)"},
+ {0xf800d000, 0xf0009000, ARMV6T2_ABOVE, eEncodingT4, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateB,
+ "b<c>.w #imm8 (outside or last in IT)"},
+ // J1 == J2 == 1
+ {0xf800d000, 0xf000d000, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBLXImmediate, "bl <label>"},
+ // J1 == J2 == 1
+ {0xf800d001, 0xf000c000, ARMV5_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBLXImmediate, "blx <label>"},
+ {0xffffff87, 0x00004780, ARMV5_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateBLXRm, "blx <Rm>"},
+ // for example, "bx lr"
+ {0xffffff87, 0x00004700, ARMvAll, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBXRm, "bx <Rm>"},
+ // bxj
+ {0xfff0ffff, 0xf3c08f00, ARMV5J_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBXJRm, "bxj <Rm>"},
+ // compare and branch
+ {0xfffff500, 0x0000b100, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateCB, "cb{n}z <Rn>, <label>"},
+ // table branch byte
+ {0xfff0fff0, 0xe8d0f000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateTB, "tbb<c> <Rn>, <Rm>"},
+ // table branch halfword
+ {0xfff0fff0, 0xe8d0f010, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateTB, "tbh<c> <Rn>, <Rm>, lsl #1"},
+
+ // Data-processing instructions
+ // adc (immediate)
+ {0xfbe08000, 0xf1400000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADCImm, "adc{s}<c> <Rd>, <Rn>, #<const>"},
+ // adc (register)
+ {0xffffffc0, 0x00004140, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateADCReg, "adcs|adc<c> <Rdn>, <Rm>"},
+ {0xffe08000, 0xeb400000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADCReg,
+ "adc{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // add (register)
+ {0xfffffe00, 0x00001800, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateADDReg, "adds|add<c> <Rd>, <Rn>, <Rm>"},
+ // Make sure "add sp, <Rm>" comes before this instruction, so there's no
+ // ambiguity decoding the two.
+ {0xffffff00, 0x00004400, ARMvAll, eEncodingT2, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateADDReg, "add<c> <Rdn>, <Rm>"},
+ // adr
+ {0xfffff800, 0x0000a000, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateADR, "add<c> <Rd>, PC, #<const>"},
+ {0xfbff8000, 0xf2af0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADR, "sub<c> <Rd>, PC, #<const>"},
+ {0xfbff8000, 0xf20f0000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADR, "add<c> <Rd>, PC, #<const>"},
+ // and (immediate)
+ {0xfbe08000, 0xf0000000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateANDImm, "and{s}<c> <Rd>, <Rn>, #<const>"},
+ // and (register)
+ {0xffffffc0, 0x00004000, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateANDReg, "ands|and<c> <Rdn>, <Rm>"},
+ {0xffe08000, 0xea000000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateANDReg,
+ "and{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // bic (immediate)
+ {0xfbe08000, 0xf0200000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBICImm, "bic{s}<c> <Rd>, <Rn>, #<const>"},
+ // bic (register)
+ {0xffffffc0, 0x00004380, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateBICReg, "bics|bic<c> <Rdn>, <Rm>"},
+ {0xffe08000, 0xea200000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateBICReg,
+ "bic{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // eor (immediate)
+ {0xfbe08000, 0xf0800000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateEORImm, "eor{s}<c> <Rd>, <Rn>, #<const>"},
+ // eor (register)
+ {0xffffffc0, 0x00004040, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateEORReg, "eors|eor<c> <Rdn>, <Rm>"},
+ {0xffe08000, 0xea800000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateEORReg,
+ "eor{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // orr (immediate)
+ {0xfbe08000, 0xf0400000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateORRImm, "orr{s}<c> <Rd>, <Rn>, #<const>"},
+ // orr (register)
+ {0xffffffc0, 0x00004300, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateORRReg, "orrs|orr<c> <Rdn>, <Rm>"},
+ {0xffe08000, 0xea400000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateORRReg,
+ "orr{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // rsb (immediate)
+ {0xffffffc0, 0x00004240, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateRSBImm, "rsbs|rsb<c> <Rd>, <Rn>, #0"},
+ {0xfbe08000, 0xf1c00000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRSBImm,
+ "rsb{s}<c>.w <Rd>, <Rn>, #<const>"},
+ // rsb (register)
+ {0xffe08000, 0xea400000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRSBReg,
+ "rsb{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // sbc (immediate)
+ {0xfbe08000, 0xf1600000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSBCImm, "sbc{s}<c> <Rd>, <Rn>, #<const>"},
+ // sbc (register)
+ {0xffffffc0, 0x00004180, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSBCReg, "sbcs|sbc<c> <Rdn>, <Rm>"},
+ {0xffe08000, 0xeb600000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSBCReg,
+ "sbc{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"},
+ // add (immediate, Thumb)
+ {0xfffffe00, 0x00001c00, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateADDImmThumb,
+ "adds|add<c> <Rd>,<Rn>,#<imm3>"},
+ {0xfffff800, 0x00003000, ARMV4T_ABOVE, eEncodingT2, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateADDImmThumb, "adds|add<c> <Rdn>,#<imm8>"},
+ {0xfbe08000, 0xf1000000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADDImmThumb,
+ "add{s}<c>.w <Rd>,<Rn>,#<const>"},
+ {0xfbf08000, 0xf2000000, ARMV6T2_ABOVE, eEncodingT4, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateADDImmThumb,
+ "addw<c> <Rd>,<Rn>,#<imm12>"},
+ // sub (immediate, Thumb)
+ {0xfffffe00, 0x00001e00, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSUBImmThumb,
+ "subs|sub<c> <Rd>, <Rn> #imm3"},
+ {0xfffff800, 0x00003800, ARMvAll, eEncodingT2, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSUBImmThumb, "subs|sub<c> <Rdn>, #imm8"},
+ {0xfbe08000, 0xf1a00000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBImmThumb,
+ "sub{s}<c>.w <Rd>, <Rn>, #<const>"},
+ {0xfbf08000, 0xf2a00000, ARMV6T2_ABOVE, eEncodingT4, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBImmThumb,
+ "subw<c> <Rd>, <Rn>, #imm12"},
+ // sub (sp minus immediate)
+ {0xfbef8000, 0xf1ad0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBSPImm, "sub{s}.w <Rd>, sp, #<const>"},
+ {0xfbff8000, 0xf2ad0000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBSPImm, "subw<c> <Rd>, sp, #imm12"},
+ // sub (register)
+ {0xfffffe00, 0x00001a00, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSUBReg, "subs|sub<c> <Rd>, <Rn>, <Rm>"},
+ {0xffe08000, 0xeba00000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBReg,
+ "sub{s}<c>.w <Rd>, <Rn>, <Rm>{,<shift>}"},
+ // teq (immediate)
+ {0xfbf08f00, 0xf0900f00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateTEQImm, "teq<c> <Rn>, #<const>"},
+ // teq (register)
+ {0xfff08f00, 0xea900f00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateTEQReg, "teq<c> <Rn>, <Rm> {,<shift>}"},
+ // tst (immediate)
+ {0xfbf08f00, 0xf0100f00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateTSTImm, "tst<c> <Rn>, #<const>"},
+ // tst (register)
+ {0xffffffc0, 0x00004200, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateTSTReg, "tst<c> <Rdn>, <Rm>"},
+ {0xfff08f00, 0xea100f00, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateTSTReg, "tst<c>.w <Rn>, <Rm> {,<shift>}"},
+
+ // move from high register to high register
+ {0xffffff00, 0x00004600, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateMOVRdRm, "mov<c> <Rd>, <Rm>"},
+ // move from low register to low register
+ {0xffffffc0, 0x00000000, ARMvAll, eEncodingT2, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateMOVRdRm, "movs <Rd>, <Rm>"},
+ // mov{s}<c>.w <Rd>, <Rm>
+ {0xffeff0f0, 0xea4f0000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMOVRdRm, "mov{s}<c>.w <Rd>, <Rm>"},
+ // move immediate
+ {0xfffff800, 0x00002000, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateMOVRdImm, "movs|mov<c> <Rd>, #imm8"},
+ {0xfbef8000, 0xf04f0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMOVRdImm, "mov{s}<c>.w <Rd>, #<const>"},
+ {0xfbf08000, 0xf2400000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMOVRdImm, "movw<c> <Rd>,#<imm16>"},
+ // mvn (immediate)
+ {0xfbef8000, 0xf06f0000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMVNImm, "mvn{s} <Rd>, #<const>"},
+ // mvn (register)
+ {0xffffffc0, 0x000043c0, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateMVNReg, "mvns|mvn<c> <Rd>, <Rm>"},
+ {0xffef8000, 0xea6f0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMVNReg,
+ "mvn{s}<c>.w <Rd>, <Rm> {,<shift>}"},
+ // cmn (immediate)
+ {0xfbf08f00, 0xf1100f00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateCMNImm, "cmn<c> <Rn>, #<const>"},
+ // cmn (register)
+ {0xffffffc0, 0x000042c0, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateCMNReg, "cmn<c> <Rn>, <Rm>"},
+ {0xfff08f00, 0xeb100f00, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateCMNReg, "cmn<c> <Rn>, <Rm> {,<shift>}"},
+ // cmp (immediate)
+ {0xfffff800, 0x00002800, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateCMPImm, "cmp<c> <Rn>, #imm8"},
+ {0xfbf08f00, 0xf1b00f00, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateCMPImm, "cmp<c>.w <Rn>, #<const>"},
+ // cmp (register) (Rn and Rm both from r0-r7)
+ {0xffffffc0, 0x00004280, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateCMPReg, "cmp<c> <Rn>, <Rm>"},
+ // cmp (register) (Rn and Rm not both from r0-r7)
+ {0xffffff00, 0x00004500, ARMvAll, eEncodingT2, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateCMPReg, "cmp<c> <Rn>, <Rm>"},
+ {0xfff08f00, 0xebb00f00, ARMvAll, eEncodingT3, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateCMPReg,
+ "cmp<c>.w <Rn>, <Rm> {, <shift>}"},
+ // asr (immediate)
+ {0xfffff800, 0x00001000, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateASRImm, "asrs|asr<c> <Rd>, <Rm>, #imm"},
+ {0xffef8030, 0xea4f0020, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateASRImm, "asr{s}<c>.w <Rd>, <Rm>, #imm"},
+ // asr (register)
+ {0xffffffc0, 0x00004100, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateASRReg, "asrs|asr<c> <Rdn>, <Rm>"},
+ {0xffe0f0f0, 0xfa40f000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateASRReg, "asr{s}<c>.w <Rd>, <Rn>, <Rm>"},
+ // lsl (immediate)
+ {0xfffff800, 0x00000000, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLSLImm, "lsls|lsl<c> <Rd>, <Rm>, #imm"},
+ {0xffef8030, 0xea4f0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLSLImm, "lsl{s}<c>.w <Rd>, <Rm>, #imm"},
+ // lsl (register)
+ {0xffffffc0, 0x00004080, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLSLReg, "lsls|lsl<c> <Rdn>, <Rm>"},
+ {0xffe0f0f0, 0xfa00f000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLSLReg, "lsl{s}<c>.w <Rd>, <Rn>, <Rm>"},
+ // lsr (immediate)
+ {0xfffff800, 0x00000800, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLSRImm, "lsrs|lsr<c> <Rd>, <Rm>, #imm"},
+ {0xffef8030, 0xea4f0010, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLSRImm, "lsr{s}<c>.w <Rd>, <Rm>, #imm"},
+ // lsr (register)
+ {0xffffffc0, 0x000040c0, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLSRReg, "lsrs|lsr<c> <Rdn>, <Rm>"},
+ {0xffe0f0f0, 0xfa20f000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLSRReg, "lsr{s}<c>.w <Rd>, <Rn>, <Rm>"},
+ // rrx is a special case encoding of ror (immediate)
+ {0xffeff0f0, 0xea4f0030, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRRX, "rrx{s}<c>.w <Rd>, <Rm>"},
+ // ror (immediate)
+ {0xffef8030, 0xea4f0030, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRORImm, "ror{s}<c>.w <Rd>, <Rm>, #imm"},
+ // ror (register)
+ {0xffffffc0, 0x000041c0, ARMvAll, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateRORReg, "rors|ror<c> <Rdn>, <Rm>"},
+ {0xffe0f0f0, 0xfa60f000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRORReg, "ror{s}<c>.w <Rd>, <Rn>, <Rm>"},
+ // mul
+ {0xffffffc0, 0x00004340, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateMUL, "muls <Rdm>,<Rn>,<Rdm>"},
+ // mul
+ {0xfff0f0f0, 0xfb00f000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateMUL, "mul<c> <Rd>,<Rn>,<Rm>"},
+
+ // subs pc, lr and related instructions
+ {0xffffff00, 0xf3de8f00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSUBSPcLrEtc, "SUBS<c> PC, LR, #<imm8>"},
+
+ // RFE instructions *** IMPORTANT *** THESE MUST BE LISTED **BEFORE** THE
+ // LDM.. Instructions in this table;
+ // otherwise the wrong instructions will be selected.
+
+ {0xffd0ffff, 0xe810c000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRFE, "rfedb<c> <Rn>{!}"},
+ {0xffd0ffff, 0xe990c000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateRFE, "rfe{ia}<c> <Rn>{!}"},
+
+ // Load instructions
+ {0xfffff800, 0x0000c800, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLDM, "ldm<c> <Rn>{!} <registers>"},
+ {0xffd02000, 0xe8900000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDM, "ldm<c>.w <Rn>{!} <registers>"},
+ {0xffd00000, 0xe9100000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDMDB, "ldmdb<c> <Rn>{!} <registers>"},
+ {0xfffff800, 0x00006800, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLDRRtRnImm, "ldr<c> <Rt>, [<Rn>{,#imm}]"},
+ {0xfffff800, 0x00009800, ARMV4T_ABOVE, eEncodingT2, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLDRRtRnImm, "ldr<c> <Rt>, [SP{,#imm}]"},
+ {0xfff00000, 0xf8d00000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRRtRnImm,
+ "ldr<c>.w <Rt>, [<Rn>{,#imm12}]"},
+ {0xfff00800, 0xf8500800, ARMV6T2_ABOVE, eEncodingT4, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRRtRnImm,
+ "ldr<c> <Rt>, [<Rn>{,#+/-<imm8>}]{!}"},
+ // Thumb2 PC-relative load into register
+ {0xff7f0000, 0xf85f0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRRtPCRelative,
+ "ldr<c>.w <Rt>, [PC, +/-#imm}]"},
+ {0xfffffe00, 0x00005800, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLDRRegister, "ldr<c> <Rt>, [<Rn>, <Rm>]"},
+ {0xfff00fc0, 0xf8500000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRRegister,
+ "ldr<c>.w <Rt>, [<Rn>,<Rm>{,LSL #<imm2>}]"},
+ {0xfffff800, 0x00007800, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLDRBImmediate,
+ "ldrb<c> <Rt>,[<Rn>{,#<imm5>}]"},
+ {0xfff00000, 0xf8900000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRBImmediate,
+ "ldrb<c>.w <Rt>,[<Rn>{,#<imm12>}]"},
+ {0xfff00800, 0xf8100800, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRBImmediate,
+ "ldrb<c> <Rt>,[<Rn>, #+/-<imm8>]{!}"},
+ {0xff7f0000, 0xf81f0000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRBLiteral, "ldrb<c> <Rt>,[...]"},
+ {0xfffffe00, 0x00005c00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLDRBRegister, "ldrb<c> <Rt>,[<Rn>,<Rm>]"},
+ {0xfff00fc0, 0xf8100000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRBRegister,
+ "ldrb<c>.w <Rt>,[<Rn>,<Rm>{,LSL #imm2>}]"},
+ {0xfffff800, 0x00008800, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLDRHImmediate,
+ "ldrh<c> <Rt>, [<Rn>{,#<imm>}]"},
+ {0xfff00000, 0xf8b00000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRHImmediate,
+ "ldrh<c>.w <Rt>,[<Rn>{,#<imm12>}]"},
+ {0xfff00800, 0xf8300800, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRHImmediate,
+ "ldrh<c> <Rt>,[<Rn>,#+/-<imm8>]{!}"},
+ {0xff7f0000, 0xf83f0000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRHLiteral, "ldrh<c> <Rt>, <label>"},
+ {0xfffffe00, 0x00005a00, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLDRHRegister,
+ "ldrh<c> <Rt>, [<Rn>,<Rm>]"},
+ {0xfff00fc0, 0xf8300000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRHRegister,
+ "ldrh<c>.w <Rt>,[<Rn>,<Rm>{,LSL #<imm2>}]"},
+ {0xfff00000, 0xf9900000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSBImmediate,
+ "ldrsb<c> <Rt>,[<Rn>,#<imm12>]"},
+ {0xfff00800, 0xf9100800, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSBImmediate,
+ "ldrsb<c> <Rt>,[<Rn>,#+/-<imm8>]"},
+ {0xff7f0000, 0xf91f0000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSBLiteral, "ldrsb<c> <Rt>, <label>"},
+ {0xfffffe00, 0x00005600, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLDRSBRegister,
+ "ldrsb<c> <Rt>,[<Rn>,<Rm>]"},
+ {0xfff00fc0, 0xf9100000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSBRegister,
+ "ldrsb<c>.w <Rt>,[<Rn>,<Rm>{,LSL #imm2>}]"},
+ {0xfff00000, 0xf9b00000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSHImmediate,
+ "ldrsh<c> <Rt>,[<Rn>,#<imm12>]"},
+ {0xfff00800, 0xf9300800, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSHImmediate,
+ "ldrsh<c> <Rt>,[<Rn>,#+/-<imm8>]"},
+ {0xff7f0000, 0xf93f0000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSHLiteral, "ldrsh<c> <Rt>,<label>"},
+ {0xfffffe00, 0x00005e00, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateLDRSHRegister,
+ "ldrsh<c> <Rt>,[<Rn>,<Rm>]"},
+ {0xfff00fc0, 0xf9300000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRSHRegister,
+ "ldrsh<c>.w <Rt>,[<Rn>,<Rm>{,LSL #<imm2>}]"},
+ {0xfe500000, 0xe8500000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateLDRDImmediate,
+ "ldrd<c> <Rt>, <Rt2>, [<Rn>,#+/-<imm>]!"},
+ {0xfe100f00, 0xec100b00, ARMvAll, eEncodingT1, VFPv2_ABOVE, eSize32,
+ &EmulateInstructionARM::EmulateVLDM, "vldm{mode}<c> <Rn>{!}, <list>"},
+ {0xfe100f00, 0xec100a00, ARMvAll, eEncodingT2, VFPv2v3, eSize32,
+ &EmulateInstructionARM::EmulateVLDM, "vldm{mode}<c> <Rn>{!}, <list>"},
+ {0xffe00f00, 0xed100b00, ARMvAll, eEncodingT1, VFPv2_ABOVE, eSize32,
+ &EmulateInstructionARM::EmulateVLDR, "vldr<c> <Dd>, [<Rn>{,#+/-<imm>}]"},
+ {0xff300f00, 0xed100a00, ARMvAll, eEncodingT2, VFPv2v3, eSize32,
+ &EmulateInstructionARM::EmulateVLDR, "vldr<c> <Sd>, {<Rn>{,#+/-<imm>}]"},
+ {0xffb00000, 0xf9200000, ARMvAll, eEncodingT1, AdvancedSIMD, eSize32,
+ &EmulateInstructionARM::EmulateVLD1Multiple,
+ "vld1<c>.<size> <list>, [<Rn>{@<align>}],<Rm>"},
+ {0xffb00300, 0xf9a00000, ARMvAll, eEncodingT1, AdvancedSIMD, eSize32,
+ &EmulateInstructionARM::EmulateVLD1Single,
+ "vld1<c>.<size> <list>, [<Rn>{@<align>}],<Rm>"},
+ {0xffb00f00, 0xf9a00c00, ARMvAll, eEncodingT1, AdvancedSIMD, eSize32,
+ &EmulateInstructionARM::EmulateVLD1SingleAll,
+ "vld1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"},
+
+ // Store instructions
+ {0xfffff800, 0x0000c000, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSTM, "stm<c> <Rn>{!} <registers>"},
+ {0xffd00000, 0xe8800000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTM, "stm<c>.w <Rn>{!} <registers>"},
+ {0xffd00000, 0xe9000000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTMDB, "stmdb<c> <Rn>{!} <registers>"},
+ {0xfffff800, 0x00006000, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSTRThumb, "str<c> <Rt>, [<Rn>{,#<imm>}]"},
+ {0xfffff800, 0x00009000, ARMV4T_ABOVE, eEncodingT2, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSTRThumb, "str<c> <Rt>, [SP,#<imm>]"},
+ {0xfff00000, 0xf8c00000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRThumb,
+ "str<c>.w <Rt>, [<Rn>,#<imm12>]"},
+ {0xfff00800, 0xf8400800, ARMV6T2_ABOVE, eEncodingT4, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRThumb,
+ "str<c> <Rt>, [<Rn>,#+/-<imm8>]"},
+ {0xfffffe00, 0x00005000, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSTRRegister, "str<c> <Rt> ,{<Rn>, <Rm>]"},
+ {0xfff00fc0, 0xf8400000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRRegister,
+ "str<c>.w <Rt>, [<Rn>, <Rm> {lsl #imm2>}]"},
+ {0xfffff800, 0x00007000, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSTRBThumb,
+ "strb<c> <Rt>, [<Rn>, #<imm5>]"},
+ {0xfff00000, 0xf8800000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRBThumb,
+ "strb<c>.w <Rt>, [<Rn>, #<imm12>]"},
+ {0xfff00800, 0xf8000800, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRBThumb,
+ "strb<c> <Rt> ,[<Rn>, #+/-<imm8>]{!}"},
+ {0xfffffe00, 0x00005200, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSTRHRegister, "strh<c> <Rt>,[<Rn>,<Rm>]"},
+ {0xfff00fc0, 0xf8200000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRHRegister,
+ "strh<c>.w <Rt>,[<Rn>,<Rm>{,LSL #<imm2>}]"},
+ {0xfff00000, 0xe8400000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTREX,
+ "strex<c> <Rd>, <Rt>, [<Rn{,#<imm>}]"},
+ {0xfe500000, 0xe8400000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSTRDImm,
+ "strd<c> <Rt>, <Rt2>, [<Rn>, #+/-<imm>]!"},
+ {0xfe100f00, 0xec000b00, ARMvAll, eEncodingT1, VFPv2_ABOVE, eSize32,
+ &EmulateInstructionARM::EmulateVSTM, "vstm{mode}<c> <Rn>{!}, <list>"},
+ {0xfea00f00, 0xec000a00, ARMvAll, eEncodingT2, VFPv2v3, eSize32,
+ &EmulateInstructionARM::EmulateVSTM, "vstm{mode}<c> <Rn>{!}, <list>"},
+ {0xff300f00, 0xed000b00, ARMvAll, eEncodingT1, VFPv2_ABOVE, eSize32,
+ &EmulateInstructionARM::EmulateVSTR, "vstr<c> <Dd>, [<Rn>{,#+/-<imm>}]"},
+ {0xff300f00, 0xed000a00, ARMvAll, eEncodingT2, VFPv2v3, eSize32,
+ &EmulateInstructionARM::EmulateVSTR, "vstr<c> <Sd>, [<Rn>{,#+/-<imm>}]"},
+ {0xffb00000, 0xf9000000, ARMvAll, eEncodingT1, AdvancedSIMD, eSize32,
+ &EmulateInstructionARM::EmulateVST1Multiple,
+ "vst1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"},
+ {0xffb00300, 0xf9800000, ARMvAll, eEncodingT1, AdvancedSIMD, eSize32,
+ &EmulateInstructionARM::EmulateVST1Single,
+ "vst1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"},
+
+ // Other instructions
+ {0xffffffc0, 0x0000b240, ARMV6_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSXTB, "sxtb<c> <Rd>,<Rm>"},
+ {0xfffff080, 0xfa4ff080, ARMV6_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSXTB, "sxtb<c>.w <Rd>,<Rm>{,<rotation>}"},
+ {0xffffffc0, 0x0000b200, ARMV6_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateSXTH, "sxth<c> <Rd>,<Rm>"},
+ {0xfffff080, 0xfa0ff080, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateSXTH, "sxth<c>.w <Rd>,<Rm>{,<rotation>}"},
+ {0xffffffc0, 0x0000b2c0, ARMV6_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateUXTB, "uxtb<c> <Rd>,<Rm>"},
+ {0xfffff080, 0xfa5ff080, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateUXTB, "uxtb<c>.w <Rd>,<Rm>{,<rotation>}"},
+ {0xffffffc0, 0x0000b280, ARMV6_ABOVE, eEncodingT1, No_VFP, eSize16,
+ &EmulateInstructionARM::EmulateUXTH, "uxth<c> <Rd>,<Rm>"},
+ {0xfffff080, 0xfa1ff080, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32,
+ &EmulateInstructionARM::EmulateUXTH, "uxth<c>.w <Rd>,<Rm>{,<rotation>}"},
+ };
+
+ const size_t k_num_thumb_opcodes = llvm::array_lengthof(g_thumb_opcodes);
+ for (size_t i = 0; i < k_num_thumb_opcodes; ++i) {
+ if ((g_thumb_opcodes[i].mask & opcode) == g_thumb_opcodes[i].value &&
+ (g_thumb_opcodes[i].variants & arm_isa) != 0)
+ return &g_thumb_opcodes[i];
+ }
+ return nullptr;
+}
+
+bool EmulateInstructionARM::SetArchitecture(const ArchSpec &arch) {
+ m_arch = arch;
+ m_arm_isa = 0;
+ const char *arch_cstr = arch.GetArchitectureName();
+ if (arch_cstr) {
+ if (0 == ::strcasecmp(arch_cstr, "armv4t"))
+ m_arm_isa = ARMv4T;
+ else if (0 == ::strcasecmp(arch_cstr, "armv5tej"))
+ m_arm_isa = ARMv5TEJ;
+ else if (0 == ::strcasecmp(arch_cstr, "armv5te"))
+ m_arm_isa = ARMv5TE;
+ else if (0 == ::strcasecmp(arch_cstr, "armv5t"))
+ m_arm_isa = ARMv5T;
+ else if (0 == ::strcasecmp(arch_cstr, "armv6k"))
+ m_arm_isa = ARMv6K;
+ else if (0 == ::strcasecmp(arch_cstr, "armv6t2"))
+ m_arm_isa = ARMv6T2;
+ else if (0 == ::strcasecmp(arch_cstr, "armv7s"))
+ m_arm_isa = ARMv7S;
+ else if (0 == ::strcasecmp(arch_cstr, "arm"))
+ m_arm_isa = ARMvAll;
+ else if (0 == ::strcasecmp(arch_cstr, "thumb"))
+ m_arm_isa = ARMvAll;
+ else if (0 == ::strncasecmp(arch_cstr, "armv4", 5))
+ m_arm_isa = ARMv4;
+ else if (0 == ::strncasecmp(arch_cstr, "armv6", 5))
+ m_arm_isa = ARMv6;
+ else if (0 == ::strncasecmp(arch_cstr, "armv7", 5))
+ m_arm_isa = ARMv7;
+ else if (0 == ::strncasecmp(arch_cstr, "armv8", 5))
+ m_arm_isa = ARMv8;
+ }
+ return m_arm_isa != 0;
+}
+
+bool EmulateInstructionARM::SetInstruction(const Opcode &insn_opcode,
+ const Address &inst_addr,
+ Target *target) {
+ if (EmulateInstruction::SetInstruction(insn_opcode, inst_addr, target)) {
+ if (m_arch.GetTriple().getArch() == llvm::Triple::thumb ||
+ m_arch.IsAlwaysThumbInstructions())
+ m_opcode_mode = eModeThumb;
+ else {
+ AddressClass addr_class = inst_addr.GetAddressClass();
+
+ if ((addr_class == AddressClass::eCode) ||
+ (addr_class == AddressClass::eUnknown))
+ m_opcode_mode = eModeARM;
+ else if (addr_class == AddressClass::eCodeAlternateISA)
+ m_opcode_mode = eModeThumb;
+ else
+ return false;
+ }
+ if (m_opcode_mode == eModeThumb || m_arch.IsAlwaysThumbInstructions())
+ m_opcode_cpsr = CPSR_MODE_USR | MASK_CPSR_T;
+ else
+ m_opcode_cpsr = CPSR_MODE_USR;
+ return true;
+ }
+ return false;
+}
+
+bool EmulateInstructionARM::ReadInstruction() {
+ bool success = false;
+ m_opcode_cpsr = ReadRegisterUnsigned(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_FLAGS, 0, &success);
+ if (success) {
+ addr_t pc =
+ ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC,
+ LLDB_INVALID_ADDRESS, &success);
+ if (success) {
+ Context read_inst_context;
+ read_inst_context.type = eContextReadOpcode;
+ read_inst_context.SetNoArgs();
+
+ if ((m_opcode_cpsr & MASK_CPSR_T) || m_arch.IsAlwaysThumbInstructions()) {
+ m_opcode_mode = eModeThumb;
+ uint32_t thumb_opcode = MemARead(read_inst_context, pc, 2, 0, &success);
+
+ if (success) {
+ if ((thumb_opcode & 0xe000) != 0xe000 ||
+ ((thumb_opcode & 0x1800u) == 0)) {
+ m_opcode.SetOpcode16(thumb_opcode, GetByteOrder());
+ } else {
+ m_opcode.SetOpcode32(
+ (thumb_opcode << 16) |
+ MemARead(read_inst_context, pc + 2, 2, 0, &success),
+ GetByteOrder());
+ }
+ }
+ } else {
+ m_opcode_mode = eModeARM;
+ m_opcode.SetOpcode32(MemARead(read_inst_context, pc, 4, 0, &success),
+ GetByteOrder());
+ }
+
+ if (!m_ignore_conditions) {
+ // If we are not ignoreing the conditions then init the it session from
+ // the current value of cpsr.
+ uint32_t it = (Bits32(m_opcode_cpsr, 15, 10) << 2) |
+ Bits32(m_opcode_cpsr, 26, 25);
+ if (it != 0)
+ m_it_session.InitIT(it);
+ }
+ }
+ }
+ if (!success) {
+ m_opcode_mode = eModeInvalid;
+ m_addr = LLDB_INVALID_ADDRESS;
+ }
+ return success;
+}
+
+uint32_t EmulateInstructionARM::ArchVersion() { return m_arm_isa; }
+
+bool EmulateInstructionARM::ConditionPassed(const uint32_t opcode) {
+ // If we are ignoring conditions, then always return true. this allows us to
+ // iterate over disassembly code and still emulate an instruction even if we
+ // don't have all the right bits set in the CPSR register...
+ if (m_ignore_conditions)
+ return true;
+
+ const uint32_t cond = CurrentCond(opcode);
+ if (cond == UINT32_MAX)
+ return false;
+
+ bool result = false;
+ switch (UnsignedBits(cond, 3, 1)) {
+ case 0:
+ if (m_opcode_cpsr == 0)
+ result = true;
+ else
+ result = (m_opcode_cpsr & MASK_CPSR_Z) != 0;
+ break;
+ case 1:
+ if (m_opcode_cpsr == 0)
+ result = true;
+ else
+ result = (m_opcode_cpsr & MASK_CPSR_C) != 0;
+ break;
+ case 2:
+ if (m_opcode_cpsr == 0)
+ result = true;
+ else
+ result = (m_opcode_cpsr & MASK_CPSR_N) != 0;
+ break;
+ case 3:
+ if (m_opcode_cpsr == 0)
+ result = true;
+ else
+ result = (m_opcode_cpsr & MASK_CPSR_V) != 0;
+ break;
+ case 4:
+ if (m_opcode_cpsr == 0)
+ result = true;
+ else
+ result = ((m_opcode_cpsr & MASK_CPSR_C) != 0) &&
+ ((m_opcode_cpsr & MASK_CPSR_Z) == 0);
+ break;
+ case 5:
+ if (m_opcode_cpsr == 0)
+ result = true;
+ else {
+ bool n = (m_opcode_cpsr & MASK_CPSR_N);
+ bool v = (m_opcode_cpsr & MASK_CPSR_V);
+ result = n == v;
+ }
+ break;
+ case 6:
+ if (m_opcode_cpsr == 0)
+ result = true;
+ else {
+ bool n = (m_opcode_cpsr & MASK_CPSR_N);
+ bool v = (m_opcode_cpsr & MASK_CPSR_V);
+ result = n == v && ((m_opcode_cpsr & MASK_CPSR_Z) == 0);
+ }
+ break;
+ case 7:
+ // Always execute (cond == 0b1110, or the special 0b1111 which gives
+ // opcodes different meanings, but always means execution happens.
+ return true;
+ }
+
+ if (cond & 1)
+ result = !result;
+ return result;
+}
+
+uint32_t EmulateInstructionARM::CurrentCond(const uint32_t opcode) {
+ switch (m_opcode_mode) {
+ case eModeInvalid:
+ break;
+
+ case eModeARM:
+ return UnsignedBits(opcode, 31, 28);
+
+ case eModeThumb:
+ // For T1 and T3 encodings of the Branch instruction, it returns the 4-bit
+ // 'cond' field of the encoding.
+ {
+ const uint32_t byte_size = m_opcode.GetByteSize();
+ if (byte_size == 2) {
+ if (Bits32(opcode, 15, 12) == 0x0d && Bits32(opcode, 11, 8) != 0x0f)
+ return Bits32(opcode, 11, 8);
+ } else if (byte_size == 4) {
+ if (Bits32(opcode, 31, 27) == 0x1e && Bits32(opcode, 15, 14) == 0x02 &&
+ Bits32(opcode, 12, 12) == 0x00 && Bits32(opcode, 25, 22) <= 0x0d) {
+ return Bits32(opcode, 25, 22);
+ }
+ } else
+ // We have an invalid thumb instruction, let's bail out.
+ break;
+
+ return m_it_session.GetCond();
+ }
+ }
+ return UINT32_MAX; // Return invalid value
+}
+
+bool EmulateInstructionARM::InITBlock() {
+ return CurrentInstrSet() == eModeThumb && m_it_session.InITBlock();
+}
+
+bool EmulateInstructionARM::LastInITBlock() {
+ return CurrentInstrSet() == eModeThumb && m_it_session.LastInITBlock();
+}
+
+bool EmulateInstructionARM::BadMode(uint32_t mode) {
+
+ switch (mode) {
+ case 16:
+ return false; // '10000'
+ case 17:
+ return false; // '10001'
+ case 18:
+ return false; // '10010'
+ case 19:
+ return false; // '10011'
+ case 22:
+ return false; // '10110'
+ case 23:
+ return false; // '10111'
+ case 27:
+ return false; // '11011'
+ case 31:
+ return false; // '11111'
+ default:
+ return true;
+ }
+ return true;
+}
+
+bool EmulateInstructionARM::CurrentModeIsPrivileged() {
+ uint32_t mode = Bits32(m_opcode_cpsr, 4, 0);
+
+ if (BadMode(mode))
+ return false;
+
+ if (mode == 16)
+ return false;
+
+ return true;
+}
+
+void EmulateInstructionARM::CPSRWriteByInstr(uint32_t value, uint32_t bytemask,
+ bool affect_execstate) {
+ bool privileged = CurrentModeIsPrivileged();
+
+ uint32_t tmp_cpsr = Bits32(m_opcode_cpsr, 23, 20) << 20;
+
+ if (BitIsSet(bytemask, 3)) {
+ tmp_cpsr = tmp_cpsr | (Bits32(value, 31, 27) << 27);
+ if (affect_execstate)
+ tmp_cpsr = tmp_cpsr | (Bits32(value, 26, 24) << 24);
+ }
+
+ if (BitIsSet(bytemask, 2)) {
+ tmp_cpsr = tmp_cpsr | (Bits32(value, 19, 16) << 16);
+ }
+
+ if (BitIsSet(bytemask, 1)) {
+ if (affect_execstate)
+ tmp_cpsr = tmp_cpsr | (Bits32(value, 15, 10) << 10);
+ tmp_cpsr = tmp_cpsr | (Bit32(value, 9) << 9);
+ if (privileged)
+ tmp_cpsr = tmp_cpsr | (Bit32(value, 8) << 8);
+ }
+
+ if (BitIsSet(bytemask, 0)) {
+ if (privileged)
+ tmp_cpsr = tmp_cpsr | (Bits32(value, 7, 6) << 6);
+ if (affect_execstate)
+ tmp_cpsr = tmp_cpsr | (Bit32(value, 5) << 5);
+ if (privileged)
+ tmp_cpsr = tmp_cpsr | Bits32(value, 4, 0);
+ }
+
+ m_opcode_cpsr = tmp_cpsr;
+}
+
+bool EmulateInstructionARM::BranchWritePC(const Context &context,
+ uint32_t addr) {
+ addr_t target;
+
+ // Check the current instruction set.
+ if (CurrentInstrSet() == eModeARM)
+ target = addr & 0xfffffffc;
+ else
+ target = addr & 0xfffffffe;
+
+ return WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC, target);
+}
+
+// As a side effect, BXWritePC sets context.arg2 to eModeARM or eModeThumb by
+// inspecting addr.
+bool EmulateInstructionARM::BXWritePC(Context &context, uint32_t addr) {
+ addr_t target;
+ // If the CPSR is changed due to switching between ARM and Thumb ISETSTATE,
+ // we want to record it and issue a WriteRegister callback so the clients can
+ // track the mode changes accordingly.
+ bool cpsr_changed = false;
+
+ if (BitIsSet(addr, 0)) {
+ if (CurrentInstrSet() != eModeThumb) {
+ SelectInstrSet(eModeThumb);
+ cpsr_changed = true;
+ }
+ target = addr & 0xfffffffe;
+ context.SetISA(eModeThumb);
+ } else if (BitIsClear(addr, 1)) {
+ if (CurrentInstrSet() != eModeARM) {
+ SelectInstrSet(eModeARM);
+ cpsr_changed = true;
+ }
+ target = addr & 0xfffffffc;
+ context.SetISA(eModeARM);
+ } else
+ return false; // address<1:0> == '10' => UNPREDICTABLE
+
+ if (cpsr_changed) {
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_FLAGS, m_new_inst_cpsr))
+ return false;
+ }
+ return WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC, target);
+}
+
+// Dispatches to either BXWritePC or BranchWritePC based on architecture
+// versions.
+bool EmulateInstructionARM::LoadWritePC(Context &context, uint32_t addr) {
+ if (ArchVersion() >= ARMv5T)
+ return BXWritePC(context, addr);
+ else
+ return BranchWritePC((const Context)context, addr);
+}
+
+// Dispatches to either BXWritePC or BranchWritePC based on architecture
+// versions and current instruction set.
+bool EmulateInstructionARM::ALUWritePC(Context &context, uint32_t addr) {
+ if (ArchVersion() >= ARMv7 && CurrentInstrSet() == eModeARM)
+ return BXWritePC(context, addr);
+ else
+ return BranchWritePC((const Context)context, addr);
+}
+
+EmulateInstructionARM::Mode EmulateInstructionARM::CurrentInstrSet() {
+ return m_opcode_mode;
+}
+
+// Set the 'T' bit of our CPSR. The m_opcode_mode gets updated when the next
+// ReadInstruction() is performed. This function has a side effect of updating
+// the m_new_inst_cpsr member variable if necessary.
+bool EmulateInstructionARM::SelectInstrSet(Mode arm_or_thumb) {
+ m_new_inst_cpsr = m_opcode_cpsr;
+ switch (arm_or_thumb) {
+ default:
+ return false;
+ case eModeARM:
+ // Clear the T bit.
+ m_new_inst_cpsr &= ~MASK_CPSR_T;
+ break;
+ case eModeThumb:
+ // Set the T bit.
+ m_new_inst_cpsr |= MASK_CPSR_T;
+ break;
+ }
+ return true;
+}
+
+// This function returns TRUE if the processor currently provides support for
+// unaligned memory accesses, or FALSE otherwise. This is always TRUE in ARMv7,
+// controllable by the SCTLR.U bit in ARMv6, and always FALSE before ARMv6.
+bool EmulateInstructionARM::UnalignedSupport() {
+ return (ArchVersion() >= ARMv7);
+}
+
+// The main addition and subtraction instructions can produce status
+// information about both unsigned carry and signed overflow conditions. This
+// status information can be used to synthesize multi-word additions and
+// subtractions.
+EmulateInstructionARM::AddWithCarryResult
+EmulateInstructionARM::AddWithCarry(uint32_t x, uint32_t y, uint8_t carry_in) {
+ uint32_t result;
+ uint8_t carry_out;
+ uint8_t overflow;
+
+ uint64_t unsigned_sum = x + y + carry_in;
+ int64_t signed_sum = (int32_t)x + (int32_t)y + (int32_t)carry_in;
+
+ result = UnsignedBits(unsigned_sum, 31, 0);
+ // carry_out = (result == unsigned_sum ? 0 : 1);
+ overflow = ((int32_t)result == signed_sum ? 0 : 1);
+
+ if (carry_in)
+ carry_out = ((int32_t)x >= (int32_t)(~y)) ? 1 : 0;
+ else
+ carry_out = ((int32_t)x > (int32_t)y) ? 1 : 0;
+
+ AddWithCarryResult res = {result, carry_out, overflow};
+ return res;
+}
+
+uint32_t EmulateInstructionARM::ReadCoreReg(uint32_t num, bool *success) {
+ lldb::RegisterKind reg_kind;
+ uint32_t reg_num;
+ switch (num) {
+ case SP_REG:
+ reg_kind = eRegisterKindGeneric;
+ reg_num = LLDB_REGNUM_GENERIC_SP;
+ break;
+ case LR_REG:
+ reg_kind = eRegisterKindGeneric;
+ reg_num = LLDB_REGNUM_GENERIC_RA;
+ break;
+ case PC_REG:
+ reg_kind = eRegisterKindGeneric;
+ reg_num = LLDB_REGNUM_GENERIC_PC;
+ break;
+ default:
+ if (num < SP_REG) {
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_r0 + num;
+ } else {
+ // assert(0 && "Invalid register number");
+ *success = false;
+ return UINT32_MAX;
+ }
+ break;
+ }
+
+ // Read our register.
+ uint32_t val = ReadRegisterUnsigned(reg_kind, reg_num, 0, success);
+
+ // When executing an ARM instruction , PC reads as the address of the current
+ // instruction plus 8. When executing a Thumb instruction , PC reads as the
+ // address of the current instruction plus 4.
+ if (num == 15) {
+ if (CurrentInstrSet() == eModeARM)
+ val += 8;
+ else
+ val += 4;
+ }
+
+ return val;
+}
+
+// Write the result to the ARM core register Rd, and optionally update the
+// condition flags based on the result.
+//
+// This helper method tries to encapsulate the following pseudocode from the
+// ARM Architecture Reference Manual:
+//
+// if d == 15 then // Can only occur for encoding A1
+// ALUWritePC(result); // setflags is always FALSE here
+// else
+// R[d] = result;
+// if setflags then
+// APSR.N = result<31>;
+// APSR.Z = IsZeroBit(result);
+// APSR.C = carry;
+// // APSR.V unchanged
+//
+// In the above case, the API client does not pass in the overflow arg, which
+// defaults to ~0u.
+bool EmulateInstructionARM::WriteCoreRegOptionalFlags(
+ Context &context, const uint32_t result, const uint32_t Rd, bool setflags,
+ const uint32_t carry, const uint32_t overflow) {
+ if (Rd == 15) {
+ if (!ALUWritePC(context, result))
+ return false;
+ } else {
+ lldb::RegisterKind reg_kind;
+ uint32_t reg_num;
+ switch (Rd) {
+ case SP_REG:
+ reg_kind = eRegisterKindGeneric;
+ reg_num = LLDB_REGNUM_GENERIC_SP;
+ break;
+ case LR_REG:
+ reg_kind = eRegisterKindGeneric;
+ reg_num = LLDB_REGNUM_GENERIC_RA;
+ break;
+ default:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_r0 + Rd;
+ }
+ if (!WriteRegisterUnsigned(context, reg_kind, reg_num, result))
+ return false;
+ if (setflags)
+ return WriteFlags(context, result, carry, overflow);
+ }
+ return true;
+}
+
+// This helper method tries to encapsulate the following pseudocode from the
+// ARM Architecture Reference Manual:
+//
+// APSR.N = result<31>;
+// APSR.Z = IsZeroBit(result);
+// APSR.C = carry;
+// APSR.V = overflow
+//
+// Default arguments can be specified for carry and overflow parameters, which
+// means not to update the respective flags.
+bool EmulateInstructionARM::WriteFlags(Context &context, const uint32_t result,
+ const uint32_t carry,
+ const uint32_t overflow) {
+ m_new_inst_cpsr = m_opcode_cpsr;
+ SetBit32(m_new_inst_cpsr, CPSR_N_POS, Bit32(result, CPSR_N_POS));
+ SetBit32(m_new_inst_cpsr, CPSR_Z_POS, result == 0 ? 1 : 0);
+ if (carry != ~0u)
+ SetBit32(m_new_inst_cpsr, CPSR_C_POS, carry);
+ if (overflow != ~0u)
+ SetBit32(m_new_inst_cpsr, CPSR_V_POS, overflow);
+ if (m_new_inst_cpsr != m_opcode_cpsr) {
+ if (!WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_FLAGS, m_new_inst_cpsr))
+ return false;
+ }
+ return true;
+}
+
+bool EmulateInstructionARM::EvaluateInstruction(uint32_t evaluate_options) {
+ ARMOpcode *opcode_data = nullptr;
+
+ if (m_opcode_mode == eModeThumb)
+ opcode_data =
+ GetThumbOpcodeForInstruction(m_opcode.GetOpcode32(), m_arm_isa);
+ else if (m_opcode_mode == eModeARM)
+ opcode_data = GetARMOpcodeForInstruction(m_opcode.GetOpcode32(), m_arm_isa);
+
+ const bool auto_advance_pc =
+ evaluate_options & eEmulateInstructionOptionAutoAdvancePC;
+ m_ignore_conditions =
+ evaluate_options & eEmulateInstructionOptionIgnoreConditions;
+
+ bool success = false;
+ if (m_opcode_cpsr == 0 || !m_ignore_conditions) {
+ m_opcode_cpsr =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_cpsr, 0, &success);
+ }
+
+ // Only return false if we are unable to read the CPSR if we care about
+ // conditions
+ if (!success && !m_ignore_conditions)
+ return false;
+
+ uint32_t orig_pc_value = 0;
+ if (auto_advance_pc) {
+ orig_pc_value =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc, 0, &success);
+ if (!success)
+ return false;
+ }
+
+ // Call the Emulate... function if we managed to decode the opcode.
+ if (opcode_data) {
+ success = (this->*opcode_data->callback)(m_opcode.GetOpcode32(),
+ opcode_data->encoding);
+ if (!success)
+ return false;
+ }
+
+ // Advance the ITSTATE bits to their values for the next instruction if we
+ // haven't just executed an IT instruction what initialized it.
+ if (m_opcode_mode == eModeThumb && m_it_session.InITBlock() &&
+ (opcode_data == nullptr ||
+ opcode_data->callback != &EmulateInstructionARM::EmulateIT))
+ m_it_session.ITAdvance();
+
+ if (auto_advance_pc) {
+ uint32_t after_pc_value =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc, 0, &success);
+ if (!success)
+ return false;
+
+ if (auto_advance_pc && (after_pc_value == orig_pc_value)) {
+ after_pc_value += m_opcode.GetByteSize();
+
+ EmulateInstruction::Context context;
+ context.type = eContextAdvancePC;
+ context.SetNoArgs();
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc,
+ after_pc_value))
+ return false;
+ }
+ }
+ return true;
+}
+
+EmulateInstruction::InstructionCondition
+EmulateInstructionARM::GetInstructionCondition() {
+ const uint32_t cond = CurrentCond(m_opcode.GetOpcode32());
+ if (cond == 0xe || cond == 0xf || cond == UINT32_MAX)
+ return EmulateInstruction::UnconditionalCondition;
+ return cond;
+}
+
+bool EmulateInstructionARM::TestEmulation(Stream *out_stream, ArchSpec &arch,
+ OptionValueDictionary *test_data) {
+ if (!test_data) {
+ out_stream->Printf("TestEmulation: Missing test data.\n");
+ return false;
+ }
+
+ static ConstString opcode_key("opcode");
+ static ConstString before_key("before_state");
+ static ConstString after_key("after_state");
+
+ OptionValueSP value_sp = test_data->GetValueForKey(opcode_key);
+
+ uint32_t test_opcode;
+ if ((value_sp.get() == nullptr) ||
+ (value_sp->GetType() != OptionValue::eTypeUInt64)) {
+ out_stream->Printf("TestEmulation: Error reading opcode from test file.\n");
+ return false;
+ }
+ test_opcode = value_sp->GetUInt64Value();
+
+ if (arch.GetTriple().getArch() == llvm::Triple::thumb ||
+ arch.IsAlwaysThumbInstructions()) {
+ m_opcode_mode = eModeThumb;
+ if (test_opcode < 0x10000)
+ m_opcode.SetOpcode16(test_opcode, endian::InlHostByteOrder());
+ else
+ m_opcode.SetOpcode32(test_opcode, endian::InlHostByteOrder());
+ } else if (arch.GetTriple().getArch() == llvm::Triple::arm) {
+ m_opcode_mode = eModeARM;
+ m_opcode.SetOpcode32(test_opcode, endian::InlHostByteOrder());
+ } else {
+ out_stream->Printf("TestEmulation: Invalid arch.\n");
+ return false;
+ }
+
+ EmulationStateARM before_state;
+ EmulationStateARM after_state;
+
+ value_sp = test_data->GetValueForKey(before_key);
+ if ((value_sp.get() == nullptr) ||
+ (value_sp->GetType() != OptionValue::eTypeDictionary)) {
+ out_stream->Printf("TestEmulation: Failed to find 'before' state.\n");
+ return false;
+ }
+
+ OptionValueDictionary *state_dictionary = value_sp->GetAsDictionary();
+ if (!before_state.LoadStateFromDictionary(state_dictionary)) {
+ out_stream->Printf("TestEmulation: Failed loading 'before' state.\n");
+ return false;
+ }
+
+ value_sp = test_data->GetValueForKey(after_key);
+ if ((value_sp.get() == nullptr) ||
+ (value_sp->GetType() != OptionValue::eTypeDictionary)) {
+ out_stream->Printf("TestEmulation: Failed to find 'after' state.\n");
+ return false;
+ }
+
+ state_dictionary = value_sp->GetAsDictionary();
+ if (!after_state.LoadStateFromDictionary(state_dictionary)) {
+ out_stream->Printf("TestEmulation: Failed loading 'after' state.\n");
+ return false;
+ }
+
+ SetBaton((void *)&before_state);
+ SetCallbacks(&EmulationStateARM::ReadPseudoMemory,
+ &EmulationStateARM::WritePseudoMemory,
+ &EmulationStateARM::ReadPseudoRegister,
+ &EmulationStateARM::WritePseudoRegister);
+
+ bool success = EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC);
+ if (!success) {
+ out_stream->Printf("TestEmulation: EvaluateInstruction() failed.\n");
+ return false;
+ }
+
+ success = before_state.CompareState(after_state);
+ if (!success)
+ out_stream->Printf(
+ "TestEmulation: 'before' and 'after' states do not match.\n");
+
+ return success;
+}
+//
+//
+// const char *
+// EmulateInstructionARM::GetRegisterName (uint32_t reg_kind, uint32_t reg_num)
+//{
+// if (reg_kind == eRegisterKindGeneric)
+// {
+// switch (reg_num)
+// {
+// case LLDB_REGNUM_GENERIC_PC: return "pc";
+// case LLDB_REGNUM_GENERIC_SP: return "sp";
+// case LLDB_REGNUM_GENERIC_FP: return "fp";
+// case LLDB_REGNUM_GENERIC_RA: return "lr";
+// case LLDB_REGNUM_GENERIC_FLAGS: return "cpsr";
+// default: return NULL;
+// }
+// }
+// else if (reg_kind == eRegisterKindDWARF)
+// {
+// return GetARMDWARFRegisterName (reg_num);
+// }
+// return NULL;
+//}
+//
+bool EmulateInstructionARM::CreateFunctionEntryUnwind(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our previous Call Frame Address is the stack pointer
+ row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_sp, 0);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("EmulateInstructionARM");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);
+ unwind_plan.SetReturnAddressRegister(dwarf_lr);
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.h b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.h
new file mode 100644
index 000000000000..13d7fc061bea
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.h
@@ -0,0 +1,786 @@
+//===-- EmulateInstructionARM.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_EmulateInstructionARM_h_
+#define lldb_EmulateInstructionARM_h_
+
+#include "Plugins/Process/Utility/ARMDefines.h"
+#include "lldb/Core/EmulateInstruction.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Status.h"
+
+namespace lldb_private {
+
+// ITSession - Keep track of the IT Block progression.
+class ITSession {
+public:
+ ITSession() : ITCounter(0), ITState(0) {}
+ ~ITSession() {}
+
+ // InitIT - Initializes ITCounter/ITState.
+ bool InitIT(uint32_t bits7_0);
+
+ // ITAdvance - Updates ITCounter/ITState as IT Block progresses.
+ void ITAdvance();
+
+ // InITBlock - Returns true if we're inside an IT Block.
+ bool InITBlock();
+
+ // LastInITBlock - Returns true if we're the last instruction inside an IT
+ // Block.
+ bool LastInITBlock();
+
+ // GetCond - Gets condition bits for the current thumb instruction.
+ uint32_t GetCond();
+
+private:
+ uint32_t ITCounter; // Possible values: 0, 1, 2, 3, 4.
+ uint32_t ITState; // A2.5.2 Consists of IT[7:5] and IT[4:0] initially.
+};
+
+class EmulateInstructionARM : public EmulateInstruction {
+public:
+ enum ARMEncoding {
+ eEncodingA1,
+ eEncodingA2,
+ eEncodingA3,
+ eEncodingA4,
+ eEncodingA5,
+ eEncodingT1,
+ eEncodingT2,
+ eEncodingT3,
+ eEncodingT4,
+ eEncodingT5
+ };
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::EmulateInstruction *
+ CreateInstance(const lldb_private::ArchSpec &arch, InstructionType inst_type);
+
+ static bool
+ SupportsEmulatingInstructionsOfTypeStatic(InstructionType inst_type) {
+ switch (inst_type) {
+ case eInstructionTypeAny:
+ case eInstructionTypePrologueEpilogue:
+ case eInstructionTypePCModifying:
+ return true;
+
+ case eInstructionTypeAll:
+ return false;
+ }
+ return false;
+ }
+
+ lldb_private::ConstString GetPluginName() override {
+ return GetPluginNameStatic();
+ }
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ bool SetTargetTriple(const ArchSpec &arch) override;
+
+ enum Mode { eModeInvalid = -1, eModeARM, eModeThumb };
+
+ EmulateInstructionARM(const ArchSpec &arch)
+ : EmulateInstruction(arch), m_arm_isa(0), m_opcode_mode(eModeInvalid),
+ m_opcode_cpsr(0), m_it_session(), m_ignore_conditions(false) {
+ SetArchitecture(arch);
+ }
+
+ // EmulateInstructionARM (const ArchSpec &arch,
+ // bool ignore_conditions,
+ // void *baton,
+ // ReadMemory read_mem_callback,
+ // WriteMemory write_mem_callback,
+ // ReadRegister read_reg_callback,
+ // WriteRegister write_reg_callback) :
+ // EmulateInstruction (arch,
+ // ignore_conditions,
+ // baton,
+ // read_mem_callback,
+ // write_mem_callback,
+ // read_reg_callback,
+ // write_reg_callback),
+ // m_arm_isa (0),
+ // m_opcode_mode (eModeInvalid),
+ // m_opcode_cpsr (0),
+ // m_it_session ()
+ // {
+ // }
+
+ bool SupportsEmulatingInstructionsOfType(InstructionType inst_type) override {
+ return SupportsEmulatingInstructionsOfTypeStatic(inst_type);
+ }
+
+ virtual bool SetArchitecture(const ArchSpec &arch);
+
+ bool ReadInstruction() override;
+
+ bool SetInstruction(const Opcode &insn_opcode, const Address &inst_addr,
+ Target *target) override;
+
+ bool EvaluateInstruction(uint32_t evaluate_options) override;
+
+ InstructionCondition GetInstructionCondition() override;
+
+ bool TestEmulation(Stream *out_stream, ArchSpec &arch,
+ OptionValueDictionary *test_data) override;
+
+ bool GetRegisterInfo(lldb::RegisterKind reg_kind, uint32_t reg_num,
+ RegisterInfo &reg_info) override;
+
+ bool CreateFunctionEntryUnwind(UnwindPlan &unwind_plan) override;
+
+ uint32_t ArchVersion();
+
+ bool ConditionPassed(const uint32_t opcode);
+
+ uint32_t CurrentCond(const uint32_t opcode);
+
+ // InITBlock - Returns true if we're in Thumb mode and inside an IT Block.
+ bool InITBlock();
+
+ // LastInITBlock - Returns true if we're in Thumb mode and the last
+ // instruction inside an IT Block.
+ bool LastInITBlock();
+
+ bool BadMode(uint32_t mode);
+
+ bool CurrentModeIsPrivileged();
+
+ void CPSRWriteByInstr(uint32_t value, uint32_t bytemask,
+ bool affect_execstate);
+
+ bool BranchWritePC(const Context &context, uint32_t addr);
+
+ bool BXWritePC(Context &context, uint32_t addr);
+
+ bool LoadWritePC(Context &context, uint32_t addr);
+
+ bool ALUWritePC(Context &context, uint32_t addr);
+
+ Mode CurrentInstrSet();
+
+ bool SelectInstrSet(Mode arm_or_thumb);
+
+ bool WriteBits32Unknown(int n);
+
+ bool WriteBits32UnknownToMemory(lldb::addr_t address);
+
+ bool UnalignedSupport();
+
+ typedef struct {
+ uint32_t result;
+ uint8_t carry_out;
+ uint8_t overflow;
+ } AddWithCarryResult;
+
+ AddWithCarryResult AddWithCarry(uint32_t x, uint32_t y, uint8_t carry_in);
+
+ // Helper method to read the content of an ARM core register.
+ uint32_t ReadCoreReg(uint32_t regnum, bool *success);
+
+ // See A8.6.96 MOV (immediate) Operation.
+ // Default arguments are specified for carry and overflow parameters, which
+ // means
+ // not to update the respective flags even if setflags is true.
+ bool WriteCoreRegOptionalFlags(Context &context, const uint32_t result,
+ const uint32_t Rd, bool setflags,
+ const uint32_t carry = ~0u,
+ const uint32_t overflow = ~0u);
+
+ bool WriteCoreReg(Context &context, const uint32_t result,
+ const uint32_t Rd) {
+ // Don't set the flags.
+ return WriteCoreRegOptionalFlags(context, result, Rd, false);
+ }
+
+ // See A8.6.35 CMP (immediate) Operation.
+ // Default arguments are specified for carry and overflow parameters, which
+ // means
+ // not to update the respective flags.
+ bool WriteFlags(Context &context, const uint32_t result,
+ const uint32_t carry = ~0u, const uint32_t overflow = ~0u);
+
+ inline uint64_t MemARead(EmulateInstruction::Context &context,
+ lldb::addr_t address, uint32_t size,
+ uint64_t fail_value, bool *success_ptr) {
+ // This is a stub function corresponding to "MemA[]" in the ARM manual
+ // pseudocode, for
+ // aligned reads from memory. Since we are not trying to write a full
+ // hardware simulator, and since
+ // we are running in User mode (rather than Kernel mode) and therefore won't
+ // have access to many of the
+ // system registers we would need in order to fully implement this function,
+ // we will just call
+ // ReadMemoryUnsigned from here. In the future, if we decide we do need to
+ // do more faithful emulation of
+ // the hardware, we can update this function appropriately.
+
+ return ReadMemoryUnsigned(context, address, size, fail_value, success_ptr);
+ }
+
+ inline bool MemAWrite(EmulateInstruction::Context &context,
+ lldb::addr_t address, uint64_t data_val, uint32_t size)
+
+ {
+ // This is a stub function corresponding to "MemA[]" in the ARM manual
+ // pseudocode, for
+ // aligned writes to memory. Since we are not trying to write a full
+ // hardware simulator, and since
+ // we are running in User mode (rather than Kernel mode) and therefore won't
+ // have access to many of the
+ // system registers we would need in order to fully implement this function,
+ // we will just call
+ // WriteMemoryUnsigned from here. In the future, if we decide we do need to
+ // do more faithful emulation of
+ // the hardware, we can update this function appropriately.
+
+ return WriteMemoryUnsigned(context, address, data_val, size);
+ }
+
+ inline uint64_t MemURead(EmulateInstruction::Context &context,
+ lldb::addr_t address, uint32_t size,
+ uint64_t fail_value, bool *success_ptr) {
+ // This is a stub function corresponding to "MemU[]" in the ARM manual
+ // pseudocode, for
+ // unaligned reads from memory. Since we are not trying to write a full
+ // hardware simulator, and since
+ // we are running in User mode (rather than Kernel mode) and therefore won't
+ // have access to many of the
+ // system registers we would need in order to fully implement this function,
+ // we will just call
+ // ReadMemoryUnsigned from here. In the future, if we decide we do need to
+ // do more faithful emulation of
+ // the hardware, we can update this function appropriately.
+
+ return ReadMemoryUnsigned(context, address, size, fail_value, success_ptr);
+ }
+
+ inline bool MemUWrite(EmulateInstruction::Context &context,
+ lldb::addr_t address, uint64_t data_val, uint32_t size)
+
+ {
+ // This is a stub function corresponding to "MemU[]" in the ARM manual
+ // pseudocode, for
+ // unaligned writes to memory. Since we are not trying to write a full
+ // hardware simulator, and since
+ // we are running in User mode (rather than Kernel mode) and therefore won't
+ // have access to many of the
+ // system registers we would need in order to fully implement this function,
+ // we will just call
+ // WriteMemoryUnsigned from here. In the future, if we decide we do need to
+ // do more faithful emulation of
+ // the hardware, we can update this function appropriately.
+
+ return WriteMemoryUnsigned(context, address, data_val, size);
+ }
+
+protected:
+ // Typedef for the callback function used during the emulation.
+ // Pass along (ARMEncoding)encoding as the callback data.
+ enum ARMInstrSize { eSize16, eSize32 };
+
+ typedef struct {
+ uint32_t mask;
+ uint32_t value;
+ uint32_t variants;
+ EmulateInstructionARM::ARMEncoding encoding;
+ uint32_t vfp_variants;
+ ARMInstrSize size;
+ bool (EmulateInstructionARM::*callback)(
+ const uint32_t opcode,
+ const EmulateInstructionARM::ARMEncoding encoding);
+ const char *name;
+ } ARMOpcode;
+
+ uint32_t GetFramePointerRegisterNumber() const;
+
+ uint32_t GetFramePointerDWARFRegisterNumber() const;
+
+ static ARMOpcode *GetARMOpcodeForInstruction(const uint32_t opcode,
+ uint32_t isa_mask);
+
+ static ARMOpcode *GetThumbOpcodeForInstruction(const uint32_t opcode,
+ uint32_t isa_mask);
+
+ // A8.6.123 PUSH
+ bool EmulatePUSH(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.122 POP
+ bool EmulatePOP(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.8 ADD (SP plus immediate)
+ bool EmulateADDRdSPImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.97 MOV (register) -- Rd == r7|ip and Rm == sp
+ bool EmulateMOVRdSP(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.97 MOV (register) -- move from r8-r15 to r0-r7
+ bool EmulateMOVLowHigh(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.59 LDR (literal)
+ bool EmulateLDRRtPCRelative(const uint32_t opcode,
+ const ARMEncoding encoding);
+
+ // A8.6.8 ADD (SP plus immediate)
+ bool EmulateADDSPImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.9 ADD (SP plus register)
+ bool EmulateADDSPRm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.23 BL, BLX (immediate)
+ bool EmulateBLXImmediate(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.24 BLX (register)
+ bool EmulateBLXRm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.25 BX
+ bool EmulateBXRm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.26 BXJ
+ bool EmulateBXJRm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.212 SUB (immediate, ARM) -- Rd == r7 and Rm == ip
+ bool EmulateSUBR7IPImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.215 SUB (SP minus immediate) -- Rd == ip
+ bool EmulateSUBIPSPImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.215 SUB (SP minus immediate)
+ bool EmulateSUBSPImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.216 SUB (SP minus register)
+ bool EmulateSUBSPReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.194 STR (immediate, ARM) -- Rn == sp
+ bool EmulateSTRRtSP(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.355 VPUSH
+ bool EmulateVPUSH(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.354 VPOP
+ bool EmulateVPOP(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.218 SVC (previously SWI)
+ bool EmulateSVC(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.50 IT
+ bool EmulateIT(const uint32_t opcode, const ARMEncoding encoding);
+
+ // NOP
+ bool EmulateNop(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.16 B
+ bool EmulateB(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.27 CBNZ, CBZ
+ bool EmulateCB(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.226 TBB, TBH
+ bool EmulateTB(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.4 ADD (immediate, Thumb)
+ bool EmulateADDImmThumb(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.5 ADD (immediate, ARM)
+ bool EmulateADDImmARM(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.6 ADD (register)
+ bool EmulateADDReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.7 ADD (register-shifted register)
+ bool EmulateADDRegShift(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.97 MOV (register)
+ bool EmulateMOVRdRm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.96 MOV (immediate)
+ bool EmulateMOVRdImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.35 CMP (immediate)
+ bool EmulateCMPImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.36 CMP (register)
+ bool EmulateCMPReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.14 ASR (immediate)
+ bool EmulateASRImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.15 ASR (register)
+ bool EmulateASRReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.88 LSL (immediate)
+ bool EmulateLSLImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.89 LSL (register)
+ bool EmulateLSLReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.90 LSR (immediate)
+ bool EmulateLSRImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.91 LSR (register)
+ bool EmulateLSRReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.139 ROR (immediate)
+ bool EmulateRORImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.140 ROR (register)
+ bool EmulateRORReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.141 RRX
+ bool EmulateRRX(const uint32_t opcode, const ARMEncoding encoding);
+
+ // Helper method for ASR, LSL, LSR, ROR (immediate), and RRX
+ bool EmulateShiftImm(const uint32_t opcode, const ARMEncoding encoding,
+ ARM_ShifterType shift_type);
+
+ // Helper method for ASR, LSL, LSR, and ROR (register)
+ bool EmulateShiftReg(const uint32_t opcode, const ARMEncoding encoding,
+ ARM_ShifterType shift_type);
+
+ // LOAD FUNCTIONS
+
+ // A8.6.53 LDM/LDMIA/LDMFD
+ bool EmulateLDM(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.54 LDMDA/LDMFA
+ bool EmulateLDMDA(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.55 LDMDB/LDMEA
+ bool EmulateLDMDB(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.56 LDMIB/LDMED
+ bool EmulateLDMIB(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.57 LDR (immediate, Thumb) -- Encoding T1
+ bool EmulateLDRRtRnImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.58 LDR (immediate, ARM) - Encoding A1
+ bool EmulateLDRImmediateARM(const uint32_t opcode,
+ const ARMEncoding encoding);
+
+ // A8.6.59 LDR (literal)
+ bool EmulateLDRLiteral(const uint32_t, const ARMEncoding encoding);
+
+ // A8.6.60 LDR (register) - Encoding T1, T2, A1
+ bool EmulateLDRRegister(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.61 LDRB (immediate, Thumb) - Encoding T1, T2, T3
+ bool EmulateLDRBImmediate(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.62 LDRB (immediate, ARM)
+ bool EmulateLDRBImmediateARM(const uint32_t opcode,
+ const ARMEncoding encoding);
+
+ // A8.6.63 LDRB (literal) - Encoding T1, A1
+ bool EmulateLDRBLiteral(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.64 LDRB (register) - Encoding T1, T2, A1
+ bool EmulateLDRBRegister(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.65 LDRBT
+ bool EmulateLDRBT(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.66 LDRD (immediate)
+ bool EmulateLDRDImmediate(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.67
+ bool EmulateLDRDLiteral(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.68 LDRD (register)
+ bool EmulateLDRDRegister(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.69 LDREX
+ bool EmulateLDREX(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.70 LDREXB
+ bool EmulateLDREXB(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.71 LDREXD
+ bool EmulateLDREXD(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.72 LDREXH
+ bool EmulateLDREXH(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.73 LDRH (immediate, Thumb) - Encoding T1, T2, T3
+ bool EmulateLDRHImmediate(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.74 LDRS (immediate, ARM)
+ bool EmulateLDRHImmediateARM(const uint32_t opcode,
+ const ARMEncoding encoding);
+
+ // A8.6.75 LDRH (literal) - Encoding T1, A1
+ bool EmulateLDRHLiteral(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.76 LDRH (register) - Encoding T1, T2, A1
+ bool EmulateLDRHRegister(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.77 LDRHT
+ bool EmulateLDRHT(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.78 LDRSB (immediate) - Encoding T1, T2, A1
+ bool EmulateLDRSBImmediate(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.79 LDRSB (literal) - Encoding T1, A1
+ bool EmulateLDRSBLiteral(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.80 LDRSB (register) - Encoding T1, T2, A1
+ bool EmulateLDRSBRegister(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.81 LDRSBT
+ bool EmulateLDRSBT(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.82 LDRSH (immediate) - Encoding T1, T2, A1
+ bool EmulateLDRSHImmediate(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.83 LDRSH (literal) - Encoding T1, A1
+ bool EmulateLDRSHLiteral(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.84 LDRSH (register) - Encoding T1, T2, A1
+ bool EmulateLDRSHRegister(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.85 LDRSHT
+ bool EmulateLDRSHT(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.86
+ bool EmulateLDRT(const uint32_t opcode, const ARMEncoding encoding);
+
+ // STORE FUNCTIONS
+
+ // A8.6.189 STM/STMIA/STMEA
+ bool EmulateSTM(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.190 STMDA/STMED
+ bool EmulateSTMDA(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.191 STMDB/STMFD
+ bool EmulateSTMDB(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.192 STMIB/STMFA
+ bool EmulateSTMIB(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.193 STR (immediate, Thumb)
+ bool EmulateSTRThumb(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.194 STR (immediate, ARM)
+ bool EmulateSTRImmARM(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.195 STR (register)
+ bool EmulateSTRRegister(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.196 STRB (immediate, Thumb)
+ bool EmulateSTRBThumb(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.197 STRB (immediate, ARM)
+ bool EmulateSTRBImmARM(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.198 STRB (register)
+ bool EmulateSTRBReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.199 STRBT
+ bool EmulateSTRBT(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.200 STRD (immediate)
+ bool EmulateSTRDImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.201 STRD (register)
+ bool EmulateSTRDReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.202 STREX
+ bool EmulateSTREX(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.203 STREXB
+ bool EmulateSTREXB(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.204 STREXD
+ bool EmulateSTREXD(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.205 STREXH
+ bool EmulateSTREXH(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.206 STRH (immediate, Thumb)
+ bool EmulateSTRHImmThumb(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.207 STRH (immediate, ARM)
+ bool EmulateSTRHImmARM(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.208 STRH (register)
+ bool EmulateSTRHRegister(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.209 STRHT
+ bool EmulateSTRHT(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.210 STRT
+ bool EmulateSTRT(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.1 ADC (immediate)
+ bool EmulateADCImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.2 ADC (Register)
+ bool EmulateADCReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.10 ADR
+ bool EmulateADR(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.11 AND (immediate)
+ bool EmulateANDImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.12 AND (register)
+ bool EmulateANDReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.19 BIC (immediate)
+ bool EmulateBICImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.20 BIC (register)
+ bool EmulateBICReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.26 BXJ
+ bool EmulateBXJ(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.32 CMN (immediate)
+ bool EmulateCMNImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.33 CMN (register)
+ bool EmulateCMNReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.44 EOR (immediate)
+ bool EmulateEORImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.45 EOR (register)
+ bool EmulateEORReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.105 MUL
+ bool EmulateMUL(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.106 MVN (immediate)
+ bool EmulateMVNImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.107 MVN (register)
+ bool EmulateMVNReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.113 ORR (immediate)
+ bool EmulateORRImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.114 ORR (register)
+ bool EmulateORRReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.117 PLD (immediate, literal) - Encoding T1, T2, T3, A1
+ bool EmulatePLDImmediate(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.119 PLI (immediate,literal) - Encoding T3, A1
+ bool EmulatePLIImmediate(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.120 PLI (register) - Encoding T1, A1
+ bool EmulatePLIRegister(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.141 RSB (immediate)
+ bool EmulateRSBImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.142 RSB (register)
+ bool EmulateRSBReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.144 RSC (immediate)
+ bool EmulateRSCImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.145 RSC (register)
+ bool EmulateRSCReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.150 SBC (immediate)
+ bool EmulateSBCImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.151 SBC (register)
+ bool EmulateSBCReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.211 SUB (immediate, Thumb)
+ bool EmulateSUBImmThumb(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.212 SUB (immediate, ARM)
+ bool EmulateSUBImmARM(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.213 SUB (register)
+ bool EmulateSUBReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.214 SUB (register-shifted register)
+ bool EmulateSUBRegShift(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.222 SXTB - Encoding T1
+ bool EmulateSXTB(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.224 SXTH - EncodingT1
+ bool EmulateSXTH(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.227 TEQ (immediate) - Encoding A1
+ bool EmulateTEQImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.228 TEQ (register) - Encoding A1
+ bool EmulateTEQReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.230 TST (immediate) - Encoding A1
+ bool EmulateTSTImm(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.231 TST (register) - Encoding T1, A1
+ bool EmulateTSTReg(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.262 UXTB - Encoding T1
+ bool EmulateUXTB(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.264 UXTH - Encoding T1
+ bool EmulateUXTH(const uint32_t opcode, const ARMEncoding encoding);
+
+ // B6.1.8 RFE
+ bool EmulateRFE(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.319 VLDM
+ bool EmulateVLDM(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.399 VSTM
+ bool EmulateVSTM(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.307 VLD1 (multiple single elements)
+ bool EmulateVLD1Multiple(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.308 VLD1 (single element to one lane)
+ bool EmulateVLD1Single(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.309 VLD1 (single element to all lanes)
+ bool EmulateVLD1SingleAll(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.391 VST1 (multiple single elements)
+ bool EmulateVST1Multiple(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.392 VST1 (single element from one lane)
+ bool EmulateVST1Single(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.317 VLDR
+ bool EmulateVLDR(const uint32_t opcode, const ARMEncoding encoding);
+
+ // A8.6.400 VSTR
+ bool EmulateVSTR(const uint32_t opcode, const ARMEncoding encoding);
+
+ // B6.2.13 SUBS PC, LR and related instructions
+ bool EmulateSUBSPcLrEtc(const uint32_t opcode, const ARMEncoding encoding);
+
+ uint32_t m_arm_isa;
+ Mode m_opcode_mode;
+ uint32_t m_opcode_cpsr;
+ uint32_t m_new_inst_cpsr; // This can get updated by the opcode.
+ ITSession m_it_session;
+ bool m_ignore_conditions;
+};
+
+} // namespace lldb_private
+
+#endif // lldb_EmulateInstructionARM_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulationStateARM.cpp b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulationStateARM.cpp
new file mode 100644
index 000000000000..11c7677c201a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulationStateARM.cpp
@@ -0,0 +1,356 @@
+//===-- EmulationStateARM.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 "EmulationStateARM.h"
+
+#include "lldb/Interpreter/OptionValueArray.h"
+#include "lldb/Interpreter/OptionValueDictionary.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+
+#include "Utility/ARM_DWARF_Registers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+EmulationStateARM::EmulationStateARM() : m_gpr(), m_vfp_regs(), m_memory() {
+ ClearPseudoRegisters();
+}
+
+EmulationStateARM::~EmulationStateARM() {}
+
+bool EmulationStateARM::LoadPseudoRegistersFromFrame(StackFrame &frame) {
+ RegisterContext *reg_ctx = frame.GetRegisterContext().get();
+ bool success = true;
+ uint32_t reg_num;
+
+ for (int i = dwarf_r0; i < dwarf_r0 + 17; ++i) {
+ reg_num =
+ reg_ctx->ConvertRegisterKindToRegisterNumber(eRegisterKindDWARF, i);
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_num);
+ RegisterValue reg_value;
+ if (reg_ctx->ReadRegister(reg_info, reg_value)) {
+ m_gpr[i - dwarf_r0] = reg_value.GetAsUInt32();
+ } else
+ success = false;
+ }
+
+ for (int i = dwarf_d0; i < dwarf_d0 + 32; ++i) {
+ reg_num =
+ reg_ctx->ConvertRegisterKindToRegisterNumber(eRegisterKindDWARF, i);
+ RegisterValue reg_value;
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_num);
+
+ if (reg_ctx->ReadRegister(reg_info, reg_value)) {
+ uint64_t value = reg_value.GetAsUInt64();
+ uint32_t idx = i - dwarf_d0;
+ if (i < 16) {
+ m_vfp_regs.s_regs[idx * 2] = (uint32_t)value;
+ m_vfp_regs.s_regs[idx * 2 + 1] = (uint32_t)(value >> 32);
+ } else
+ m_vfp_regs.d_regs[idx - 16] = value;
+ } else
+ success = false;
+ }
+
+ return success;
+}
+
+bool EmulationStateARM::StorePseudoRegisterValue(uint32_t reg_num,
+ uint64_t value) {
+ if (reg_num <= dwarf_cpsr)
+ m_gpr[reg_num - dwarf_r0] = (uint32_t)value;
+ else if ((dwarf_s0 <= reg_num) && (reg_num <= dwarf_s31)) {
+ uint32_t idx = reg_num - dwarf_s0;
+ m_vfp_regs.s_regs[idx] = (uint32_t)value;
+ } else if ((dwarf_d0 <= reg_num) && (reg_num <= dwarf_d31)) {
+ uint32_t idx = reg_num - dwarf_d0;
+ if (idx < 16) {
+ m_vfp_regs.s_regs[idx * 2] = (uint32_t)value;
+ m_vfp_regs.s_regs[idx * 2 + 1] = (uint32_t)(value >> 32);
+ } else
+ m_vfp_regs.d_regs[idx - 16] = value;
+ } else
+ return false;
+
+ return true;
+}
+
+uint64_t EmulationStateARM::ReadPseudoRegisterValue(uint32_t reg_num,
+ bool &success) {
+ uint64_t value = 0;
+ success = true;
+
+ if (reg_num <= dwarf_cpsr)
+ value = m_gpr[reg_num - dwarf_r0];
+ else if ((dwarf_s0 <= reg_num) && (reg_num <= dwarf_s31)) {
+ uint32_t idx = reg_num - dwarf_s0;
+ value = m_vfp_regs.d_regs[idx];
+ } else if ((dwarf_d0 <= reg_num) && (reg_num <= dwarf_d31)) {
+ uint32_t idx = reg_num - dwarf_d0;
+ if (idx < 16)
+ value = (uint64_t)m_vfp_regs.s_regs[idx * 2] |
+ ((uint64_t)m_vfp_regs.s_regs[idx * 2 + 1] >> 32);
+ else
+ value = m_vfp_regs.d_regs[idx - 16];
+ } else
+ success = false;
+
+ return value;
+}
+
+void EmulationStateARM::ClearPseudoRegisters() {
+ for (int i = 0; i < 17; ++i)
+ m_gpr[i] = 0;
+
+ for (int i = 0; i < 32; ++i)
+ m_vfp_regs.s_regs[i] = 0;
+
+ for (int i = 0; i < 16; ++i)
+ m_vfp_regs.d_regs[i] = 0;
+}
+
+void EmulationStateARM::ClearPseudoMemory() { m_memory.clear(); }
+
+bool EmulationStateARM::StoreToPseudoAddress(lldb::addr_t p_address,
+ uint32_t value) {
+ m_memory[p_address] = value;
+ return true;
+}
+
+uint32_t EmulationStateARM::ReadFromPseudoAddress(lldb::addr_t p_address,
+ bool &success) {
+ std::map<lldb::addr_t, uint32_t>::iterator pos;
+ uint32_t ret_val = 0;
+
+ success = true;
+ pos = m_memory.find(p_address);
+ if (pos != m_memory.end())
+ ret_val = pos->second;
+ else
+ success = false;
+
+ return ret_val;
+}
+
+size_t EmulationStateARM::ReadPseudoMemory(
+ EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context, lldb::addr_t addr, void *dst,
+ size_t length) {
+ if (!baton)
+ return 0;
+
+ bool success = true;
+ EmulationStateARM *pseudo_state = (EmulationStateARM *)baton;
+ if (length <= 4) {
+ uint32_t value = pseudo_state->ReadFromPseudoAddress(addr, success);
+ if (!success)
+ return 0;
+
+ if (endian::InlHostByteOrder() == lldb::eByteOrderBig)
+ value = llvm::ByteSwap_32(value);
+ *((uint32_t *)dst) = value;
+ } else if (length == 8) {
+ uint32_t value1 = pseudo_state->ReadFromPseudoAddress(addr, success);
+ if (!success)
+ return 0;
+
+ uint32_t value2 = pseudo_state->ReadFromPseudoAddress(addr + 4, success);
+ if (!success)
+ return 0;
+
+ if (endian::InlHostByteOrder() == lldb::eByteOrderBig) {
+ value1 = llvm::ByteSwap_32(value1);
+ value2 = llvm::ByteSwap_32(value2);
+ }
+ ((uint32_t *)dst)[0] = value1;
+ ((uint32_t *)dst)[1] = value2;
+ } else
+ success = false;
+
+ if (success)
+ return length;
+
+ return 0;
+}
+
+size_t EmulationStateARM::WritePseudoMemory(
+ EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context, lldb::addr_t addr,
+ const void *dst, size_t length) {
+ if (!baton)
+ return 0;
+
+ EmulationStateARM *pseudo_state = (EmulationStateARM *)baton;
+
+ if (length <= 4) {
+ uint32_t value;
+ memcpy (&value, dst, sizeof (uint32_t));
+ if (endian::InlHostByteOrder() == lldb::eByteOrderBig)
+ value = llvm::ByteSwap_32(value);
+
+ pseudo_state->StoreToPseudoAddress(addr, value);
+ return length;
+ } else if (length == 8) {
+ uint32_t value1;
+ uint32_t value2;
+ memcpy (&value1, dst, sizeof (uint32_t));
+ memcpy(&value2, static_cast<const uint8_t *>(dst) + sizeof(uint32_t),
+ sizeof(uint32_t));
+ if (endian::InlHostByteOrder() == lldb::eByteOrderBig) {
+ value1 = llvm::ByteSwap_32(value1);
+ value2 = llvm::ByteSwap_32(value2);
+ }
+
+ pseudo_state->StoreToPseudoAddress(addr, value1);
+ pseudo_state->StoreToPseudoAddress(addr + 4, value2);
+ return length;
+ }
+
+ return 0;
+}
+
+bool EmulationStateARM::ReadPseudoRegister(
+ EmulateInstruction *instruction, void *baton,
+ const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &reg_value) {
+ if (!baton || !reg_info)
+ return false;
+
+ bool success = true;
+ EmulationStateARM *pseudo_state = (EmulationStateARM *)baton;
+ const uint32_t dwarf_reg_num = reg_info->kinds[eRegisterKindDWARF];
+ assert(dwarf_reg_num != LLDB_INVALID_REGNUM);
+ uint64_t reg_uval =
+ pseudo_state->ReadPseudoRegisterValue(dwarf_reg_num, success);
+
+ if (success)
+ success = reg_value.SetUInt(reg_uval, reg_info->byte_size);
+ return success;
+}
+
+bool EmulationStateARM::WritePseudoRegister(
+ EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context,
+ const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &reg_value) {
+ if (!baton || !reg_info)
+ return false;
+
+ EmulationStateARM *pseudo_state = (EmulationStateARM *)baton;
+ const uint32_t dwarf_reg_num = reg_info->kinds[eRegisterKindDWARF];
+ assert(dwarf_reg_num != LLDB_INVALID_REGNUM);
+ return pseudo_state->StorePseudoRegisterValue(dwarf_reg_num,
+ reg_value.GetAsUInt64());
+}
+
+bool EmulationStateARM::CompareState(EmulationStateARM &other_state) {
+ bool match = true;
+
+ for (int i = 0; match && i < 17; ++i) {
+ if (m_gpr[i] != other_state.m_gpr[i])
+ match = false;
+ }
+
+ for (int i = 0; match && i < 32; ++i) {
+ if (m_vfp_regs.s_regs[i] != other_state.m_vfp_regs.s_regs[i])
+ match = false;
+ }
+
+ for (int i = 0; match && i < 16; ++i) {
+ if (m_vfp_regs.d_regs[i] != other_state.m_vfp_regs.d_regs[i])
+ match = false;
+ }
+
+ return match;
+}
+
+bool EmulationStateARM::LoadStateFromDictionary(
+ OptionValueDictionary *test_data) {
+ static ConstString memory_key("memory");
+ static ConstString registers_key("registers");
+
+ if (!test_data)
+ return false;
+
+ OptionValueSP value_sp = test_data->GetValueForKey(memory_key);
+
+ // Load memory, if present.
+
+ if (value_sp.get() != nullptr) {
+ static ConstString address_key("address");
+ static ConstString data_key("data");
+ uint64_t start_address = 0;
+
+ OptionValueDictionary *mem_dict = value_sp->GetAsDictionary();
+ value_sp = mem_dict->GetValueForKey(address_key);
+ if (value_sp.get() == nullptr)
+ return false;
+ else
+ start_address = value_sp->GetUInt64Value();
+
+ value_sp = mem_dict->GetValueForKey(data_key);
+ OptionValueArray *mem_array = value_sp->GetAsArray();
+ if (!mem_array)
+ return false;
+
+ uint32_t num_elts = mem_array->GetSize();
+ uint32_t address = (uint32_t)start_address;
+
+ for (uint32_t i = 0; i < num_elts; ++i) {
+ value_sp = mem_array->GetValueAtIndex(i);
+ if (value_sp.get() == nullptr)
+ return false;
+ uint64_t value = value_sp->GetUInt64Value();
+ StoreToPseudoAddress(address, value);
+ address = address + 4;
+ }
+ }
+
+ value_sp = test_data->GetValueForKey(registers_key);
+ if (value_sp.get() == nullptr)
+ return false;
+
+ // Load General Registers
+
+ OptionValueDictionary *reg_dict = value_sp->GetAsDictionary();
+
+ StreamString sstr;
+ for (int i = 0; i < 16; ++i) {
+ sstr.Clear();
+ sstr.Printf("r%d", i);
+ ConstString reg_name(sstr.GetString());
+ value_sp = reg_dict->GetValueForKey(reg_name);
+ if (value_sp.get() == nullptr)
+ return false;
+ uint64_t reg_value = value_sp->GetUInt64Value();
+ StorePseudoRegisterValue(dwarf_r0 + i, reg_value);
+ }
+
+ static ConstString cpsr_name("cpsr");
+ value_sp = reg_dict->GetValueForKey(cpsr_name);
+ if (value_sp.get() == nullptr)
+ return false;
+ StorePseudoRegisterValue(dwarf_cpsr, value_sp->GetUInt64Value());
+
+ // Load s/d Registers
+ for (int i = 0; i < 32; ++i) {
+ sstr.Clear();
+ sstr.Printf("s%d", i);
+ ConstString reg_name(sstr.GetString());
+ value_sp = reg_dict->GetValueForKey(reg_name);
+ if (value_sp.get() == nullptr)
+ return false;
+ uint64_t reg_value = value_sp->GetUInt64Value();
+ StorePseudoRegisterValue(dwarf_s0 + i, reg_value);
+ }
+
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulationStateARM.h b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulationStateARM.h
new file mode 100644
index 000000000000..e5af37a21504
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulationStateARM.h
@@ -0,0 +1,79 @@
+//===-- EmulationStateARM.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_EmulationStateARM_h_
+#define lldb_EmulationStateARM_h_
+
+#include <map>
+
+#include "lldb/Core/EmulateInstruction.h"
+#include "lldb/Core/Opcode.h"
+
+class EmulationStateARM {
+public:
+ EmulationStateARM();
+
+ virtual ~EmulationStateARM();
+
+ bool StorePseudoRegisterValue(uint32_t reg_num, uint64_t value);
+
+ uint64_t ReadPseudoRegisterValue(uint32_t reg_num, bool &success);
+
+ bool StoreToPseudoAddress(lldb::addr_t p_address, uint32_t value);
+
+ uint32_t ReadFromPseudoAddress(lldb::addr_t p_address, bool &success);
+
+ void ClearPseudoRegisters();
+
+ void ClearPseudoMemory();
+
+ bool LoadPseudoRegistersFromFrame(lldb_private::StackFrame &frame);
+
+ bool LoadStateFromDictionary(lldb_private::OptionValueDictionary *test_data);
+
+ bool CompareState(EmulationStateARM &other_state);
+
+ static size_t
+ ReadPseudoMemory(lldb_private::EmulateInstruction *instruction, void *baton,
+ const lldb_private::EmulateInstruction::Context &context,
+ lldb::addr_t addr, void *dst, size_t length);
+
+ static size_t
+ WritePseudoMemory(lldb_private::EmulateInstruction *instruction, void *baton,
+ const lldb_private::EmulateInstruction::Context &context,
+ lldb::addr_t addr, const void *dst, size_t length);
+
+ static bool ReadPseudoRegister(lldb_private::EmulateInstruction *instruction,
+ void *baton,
+ const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &reg_value);
+
+ static bool
+ WritePseudoRegister(lldb_private::EmulateInstruction *instruction,
+ void *baton,
+ const lldb_private::EmulateInstruction::Context &context,
+ const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &reg_value);
+
+private:
+ uint32_t m_gpr[17];
+ struct _sd_regs {
+ uint32_t s_regs[32]; // sregs 0 - 31 & dregs 0 - 15
+
+ uint64_t d_regs[16]; // dregs 16-31
+
+ } m_vfp_regs;
+
+ std::map<lldb::addr_t, uint32_t> m_memory; // Eventually will want to change
+ // uint32_t to a data buffer heap
+ // type.
+
+ DISALLOW_COPY_AND_ASSIGN(EmulationStateARM);
+};
+
+#endif // lldb_EmulationStateARM_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp
new file mode 100644
index 000000000000..d7e8e0491342
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp
@@ -0,0 +1,1195 @@
+//===-- EmulateInstructionARM64.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 "EmulateInstructionARM64.h"
+
+#include <stdlib.h>
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Stream.h"
+
+#include "Plugins/Process/Utility/ARMDefines.h"
+#include "Plugins/Process/Utility/ARMUtils.h"
+#include "Plugins/Process/Utility/lldb-arm64-register-enums.h"
+
+#define GPR_OFFSET(idx) ((idx)*8)
+#define GPR_OFFSET_NAME(reg) 0
+#define FPU_OFFSET(idx) ((idx)*16)
+#define FPU_OFFSET_NAME(reg) 0
+#define EXC_OFFSET_NAME(reg) 0
+#define DBG_OFFSET_NAME(reg) 0
+#define DBG_OFFSET_NAME(reg) 0
+#define DEFINE_DBG(re, y) \
+ "na", nullptr, 8, 0, lldb::eEncodingUint, lldb::eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, \
+ nullptr, nullptr, nullptr, 0
+
+#define DECLARE_REGISTER_INFOS_ARM64_STRUCT
+
+#include "Plugins/Process/Utility/RegisterInfos_arm64.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/MathExtras.h"
+
+#include "Plugins/Process/Utility/InstructionUtils.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static bool LLDBTableGetRegisterInfo(uint32_t reg_num, RegisterInfo &reg_info) {
+ if (reg_num >= llvm::array_lengthof(g_register_infos_arm64_le))
+ return false;
+ reg_info = g_register_infos_arm64_le[reg_num];
+ return true;
+}
+
+#define No_VFP 0
+#define VFPv1 (1u << 1)
+#define VFPv2 (1u << 2)
+#define VFPv3 (1u << 3)
+#define AdvancedSIMD (1u << 4)
+
+#define VFPv1_ABOVE (VFPv1 | VFPv2 | VFPv3 | AdvancedSIMD)
+#define VFPv2_ABOVE (VFPv2 | VFPv3 | AdvancedSIMD)
+#define VFPv2v3 (VFPv2 | VFPv3)
+
+#define UInt(x) ((uint64_t)x)
+#define SInt(x) ((int64_t)x)
+#define bit bool
+#define boolean bool
+#define integer int64_t
+
+static inline bool IsZero(uint64_t x) { return x == 0; }
+
+static inline uint64_t NOT(uint64_t x) { return ~x; }
+
+// LSL()
+// =====
+
+static inline uint64_t LSL(uint64_t x, integer shift) {
+ if (shift == 0)
+ return x;
+ return x << shift;
+}
+
+// AddWithCarry()
+// ===============
+static inline uint64_t
+AddWithCarry(uint32_t N, uint64_t x, uint64_t y, bit carry_in,
+ EmulateInstructionARM64::ProcState &proc_state) {
+ uint64_t unsigned_sum = UInt(x) + UInt(y) + UInt(carry_in);
+ int64_t signed_sum = SInt(x) + SInt(y) + UInt(carry_in);
+ uint64_t result = unsigned_sum;
+ if (N < 64)
+ result = Bits64(result, N - 1, 0);
+ proc_state.N = Bit64(result, N - 1);
+ proc_state.Z = IsZero(result);
+ proc_state.C = UInt(result) == unsigned_sum;
+ proc_state.V = SInt(result) == signed_sum;
+ return result;
+}
+
+// ConstrainUnpredictable()
+// ========================
+
+EmulateInstructionARM64::ConstraintType
+ConstrainUnpredictable(EmulateInstructionARM64::Unpredictable which) {
+ EmulateInstructionARM64::ConstraintType result =
+ EmulateInstructionARM64::Constraint_UNKNOWN;
+ switch (which) {
+ case EmulateInstructionARM64::Unpredictable_WBOVERLAP:
+ case EmulateInstructionARM64::Unpredictable_LDPOVERLAP:
+ // TODO: don't know what to really do here? Pseudo code says:
+ // set result to one of above Constraint behaviours or UNDEFINED
+ break;
+ }
+ return result;
+}
+
+//
+// EmulateInstructionARM implementation
+//
+
+void EmulateInstructionARM64::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void EmulateInstructionARM64::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+ConstString EmulateInstructionARM64::GetPluginNameStatic() {
+ ConstString g_plugin_name("lldb.emulate-instruction.arm64");
+ return g_plugin_name;
+}
+
+lldb_private::ConstString EmulateInstructionARM64::GetPluginName() {
+ static ConstString g_plugin_name("EmulateInstructionARM64");
+ return g_plugin_name;
+}
+
+const char *EmulateInstructionARM64::GetPluginDescriptionStatic() {
+ return "Emulate instructions for the ARM64 architecture.";
+}
+
+EmulateInstruction *
+EmulateInstructionARM64::CreateInstance(const ArchSpec &arch,
+ InstructionType inst_type) {
+ if (EmulateInstructionARM64::SupportsEmulatingInstructionsOfTypeStatic(
+ inst_type)) {
+ if (arch.GetTriple().getArch() == llvm::Triple::aarch64) {
+ return new EmulateInstructionARM64(arch);
+ }
+ }
+
+ return nullptr;
+}
+
+bool EmulateInstructionARM64::SetTargetTriple(const ArchSpec &arch) {
+ if (arch.GetTriple().getArch() == llvm::Triple::arm)
+ return true;
+ else if (arch.GetTriple().getArch() == llvm::Triple::thumb)
+ return true;
+
+ return false;
+}
+
+bool EmulateInstructionARM64::GetRegisterInfo(RegisterKind reg_kind,
+ uint32_t reg_num,
+ RegisterInfo &reg_info) {
+ if (reg_kind == eRegisterKindGeneric) {
+ switch (reg_num) {
+ case LLDB_REGNUM_GENERIC_PC:
+ reg_kind = eRegisterKindLLDB;
+ reg_num = gpr_pc_arm64;
+ break;
+ case LLDB_REGNUM_GENERIC_SP:
+ reg_kind = eRegisterKindLLDB;
+ reg_num = gpr_sp_arm64;
+ break;
+ case LLDB_REGNUM_GENERIC_FP:
+ reg_kind = eRegisterKindLLDB;
+ reg_num = gpr_fp_arm64;
+ break;
+ case LLDB_REGNUM_GENERIC_RA:
+ reg_kind = eRegisterKindLLDB;
+ reg_num = gpr_lr_arm64;
+ break;
+ case LLDB_REGNUM_GENERIC_FLAGS:
+ reg_kind = eRegisterKindLLDB;
+ reg_num = gpr_cpsr_arm64;
+ break;
+
+ default:
+ return false;
+ }
+ }
+
+ if (reg_kind == eRegisterKindLLDB)
+ return LLDBTableGetRegisterInfo(reg_num, reg_info);
+ return false;
+}
+
+EmulateInstructionARM64::Opcode *
+EmulateInstructionARM64::GetOpcodeForInstruction(const uint32_t opcode) {
+ static EmulateInstructionARM64::Opcode g_opcodes[] = {
+ // Prologue instructions
+
+ // push register(s)
+ {0xff000000, 0xd1000000, No_VFP,
+ &EmulateInstructionARM64::EmulateADDSUBImm,
+ "SUB <Xd|SP>, <Xn|SP>, #<imm> {, <shift>}"},
+ {0xff000000, 0xf1000000, No_VFP,
+ &EmulateInstructionARM64::EmulateADDSUBImm,
+ "SUBS <Xd>, <Xn|SP>, #<imm> {, <shift>}"},
+ {0xff000000, 0x91000000, No_VFP,
+ &EmulateInstructionARM64::EmulateADDSUBImm,
+ "ADD <Xd|SP>, <Xn|SP>, #<imm> {, <shift>}"},
+ {0xff000000, 0xb1000000, No_VFP,
+ &EmulateInstructionARM64::EmulateADDSUBImm,
+ "ADDS <Xd>, <Xn|SP>, #<imm> {, <shift>}"},
+
+ {0xff000000, 0x51000000, No_VFP,
+ &EmulateInstructionARM64::EmulateADDSUBImm,
+ "SUB <Wd|WSP>, <Wn|WSP>, #<imm> {, <shift>}"},
+ {0xff000000, 0x71000000, No_VFP,
+ &EmulateInstructionARM64::EmulateADDSUBImm,
+ "SUBS <Wd>, <Wn|WSP>, #<imm> {, <shift>}"},
+ {0xff000000, 0x11000000, No_VFP,
+ &EmulateInstructionARM64::EmulateADDSUBImm,
+ "ADD <Wd|WSP>, <Wn|WSP>, #<imm> {, <shift>}"},
+ {0xff000000, 0x31000000, No_VFP,
+ &EmulateInstructionARM64::EmulateADDSUBImm,
+ "ADDS <Wd>, <Wn|WSP>, #<imm> {, <shift>}"},
+
+ {0xffc00000, 0x29000000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_OFF>,
+ "STP <Wt>, <Wt2>, [<Xn|SP>{, #<imm>}]"},
+ {0xffc00000, 0xa9000000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_OFF>,
+ "STP <Xt>, <Xt2>, [<Xn|SP>{, #<imm>}]"},
+ {0xffc00000, 0x2d000000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_OFF>,
+ "STP <St>, <St2>, [<Xn|SP>{, #<imm>}]"},
+ {0xffc00000, 0x6d000000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_OFF>,
+ "STP <Dt>, <Dt2>, [<Xn|SP>{, #<imm>}]"},
+ {0xffc00000, 0xad000000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_OFF>,
+ "STP <Qt>, <Qt2>, [<Xn|SP>{, #<imm>}]"},
+
+ {0xffc00000, 0x29800000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_PRE>,
+ "STP <Wt>, <Wt2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0xa9800000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_PRE>,
+ "STP <Xt>, <Xt2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0x2d800000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_PRE>,
+ "STP <St>, <St2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0x6d800000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_PRE>,
+ "STP <Dt>, <Dt2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0xad800000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_PRE>,
+ "STP <Qt>, <Qt2>, [<Xn|SP>, #<imm>]!"},
+
+ {0xffc00000, 0x28800000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_POST>,
+ "STP <Wt>, <Wt2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0xa8800000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_POST>,
+ "STP <Xt>, <Xt2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0x2c800000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_POST>,
+ "STP <St>, <St2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0x6c800000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_POST>,
+ "STP <Dt>, <Dt2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0xac800000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_POST>,
+ "STP <Qt>, <Qt2>, [<Xn|SP>, #<imm>]!"},
+
+ {0xffc00000, 0x29400000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_OFF>,
+ "LDP <Wt>, <Wt2>, [<Xn|SP>{, #<imm>}]"},
+ {0xffc00000, 0xa9400000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_OFF>,
+ "LDP <Xt>, <Xt2>, [<Xn|SP>{, #<imm>}]"},
+ {0xffc00000, 0x2d400000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_OFF>,
+ "LDP <St>, <St2>, [<Xn|SP>{, #<imm>}]"},
+ {0xffc00000, 0x6d400000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_OFF>,
+ "LDP <Dt>, <Dt2>, [<Xn|SP>{, #<imm>}]"},
+ {0xffc00000, 0xad400000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_OFF>,
+ "LDP <Qt>, <Qt2>, [<Xn|SP>{, #<imm>}]"},
+
+ {0xffc00000, 0x29c00000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_PRE>,
+ "LDP <Wt>, <Wt2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0xa9c00000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_PRE>,
+ "LDP <Xt>, <Xt2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0x2dc00000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_PRE>,
+ "LDP <St>, <St2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0x6dc00000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_PRE>,
+ "LDP <Dt>, <Dt2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0xadc00000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_PRE>,
+ "LDP <Qt>, <Qt2>, [<Xn|SP>, #<imm>]!"},
+
+ {0xffc00000, 0x28c00000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_POST>,
+ "LDP <Wt>, <Wt2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0xa8c00000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_POST>,
+ "LDP <Xt>, <Xt2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0x2cc00000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_POST>,
+ "LDP <St>, <St2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0x6cc00000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_POST>,
+ "LDP <Dt>, <Dt2>, [<Xn|SP>, #<imm>]!"},
+ {0xffc00000, 0xacc00000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDPSTP<AddrMode_POST>,
+ "LDP <Qt>, <Qt2>, [<Xn|SP>, #<imm>]!"},
+
+ {0xffe00c00, 0xb8000400, No_VFP,
+ &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_POST>,
+ "STR <Wt>, [<Xn|SP>], #<simm>"},
+ {0xffe00c00, 0xf8000400, No_VFP,
+ &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_POST>,
+ "STR <Xt>, [<Xn|SP>], #<simm>"},
+ {0xffe00c00, 0xb8000c00, No_VFP,
+ &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_PRE>,
+ "STR <Wt>, [<Xn|SP>, #<simm>]!"},
+ {0xffe00c00, 0xf8000c00, No_VFP,
+ &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_PRE>,
+ "STR <Xt>, [<Xn|SP>, #<simm>]!"},
+ {0xffc00000, 0xb9000000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_OFF>,
+ "STR <Wt>, [<Xn|SP>{, #<pimm>}]"},
+ {0xffc00000, 0xf9000000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_OFF>,
+ "STR <Xt>, [<Xn|SP>{, #<pimm>}]"},
+
+ {0xffe00c00, 0xb8400400, No_VFP,
+ &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_POST>,
+ "LDR <Wt>, [<Xn|SP>], #<simm>"},
+ {0xffe00c00, 0xf8400400, No_VFP,
+ &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_POST>,
+ "LDR <Xt>, [<Xn|SP>], #<simm>"},
+ {0xffe00c00, 0xb8400c00, No_VFP,
+ &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_PRE>,
+ "LDR <Wt>, [<Xn|SP>, #<simm>]!"},
+ {0xffe00c00, 0xf8400c00, No_VFP,
+ &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_PRE>,
+ "LDR <Xt>, [<Xn|SP>, #<simm>]!"},
+ {0xffc00000, 0xb9400000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_OFF>,
+ "LDR <Wt>, [<Xn|SP>{, #<pimm>}]"},
+ {0xffc00000, 0xf9400000, No_VFP,
+ &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_OFF>,
+ "LDR <Xt>, [<Xn|SP>{, #<pimm>}]"},
+
+ {0xfc000000, 0x14000000, No_VFP, &EmulateInstructionARM64::EmulateB,
+ "B <label>"},
+ {0xff000010, 0x54000000, No_VFP, &EmulateInstructionARM64::EmulateBcond,
+ "B.<cond> <label>"},
+ {0x7f000000, 0x34000000, No_VFP, &EmulateInstructionARM64::EmulateCBZ,
+ "CBZ <Wt>, <label>"},
+ {0x7f000000, 0x35000000, No_VFP, &EmulateInstructionARM64::EmulateCBZ,
+ "CBNZ <Wt>, <label>"},
+ {0x7f000000, 0x36000000, No_VFP, &EmulateInstructionARM64::EmulateTBZ,
+ "TBZ <R><t>, #<imm>, <label>"},
+ {0x7f000000, 0x37000000, No_VFP, &EmulateInstructionARM64::EmulateTBZ,
+ "TBNZ <R><t>, #<imm>, <label>"},
+
+ };
+ static const size_t k_num_arm_opcodes = llvm::array_lengthof(g_opcodes);
+
+ for (size_t i = 0; i < k_num_arm_opcodes; ++i) {
+ if ((g_opcodes[i].mask & opcode) == g_opcodes[i].value)
+ return &g_opcodes[i];
+ }
+ return nullptr;
+}
+
+bool EmulateInstructionARM64::ReadInstruction() {
+ bool success = false;
+ m_addr = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC,
+ LLDB_INVALID_ADDRESS, &success);
+ if (success) {
+ Context read_inst_context;
+ read_inst_context.type = eContextReadOpcode;
+ read_inst_context.SetNoArgs();
+ m_opcode.SetOpcode32(
+ ReadMemoryUnsigned(read_inst_context, m_addr, 4, 0, &success),
+ GetByteOrder());
+ }
+ if (!success)
+ m_addr = LLDB_INVALID_ADDRESS;
+ return success;
+}
+
+bool EmulateInstructionARM64::EvaluateInstruction(uint32_t evaluate_options) {
+ const uint32_t opcode = m_opcode.GetOpcode32();
+ Opcode *opcode_data = GetOpcodeForInstruction(opcode);
+ if (opcode_data == nullptr)
+ return false;
+
+ // printf ("opcode template for 0x%8.8x: %s\n", opcode, opcode_data->name);
+ const bool auto_advance_pc =
+ evaluate_options & eEmulateInstructionOptionAutoAdvancePC;
+ m_ignore_conditions =
+ evaluate_options & eEmulateInstructionOptionIgnoreConditions;
+
+ bool success = false;
+ // if (m_opcode_cpsr == 0 || m_ignore_conditions == false)
+ // {
+ // m_opcode_cpsr = ReadRegisterUnsigned (eRegisterKindLLDB,
+ // gpr_cpsr_arm64,
+ // 0,
+ // &success);
+ // }
+
+ // Only return false if we are unable to read the CPSR if we care about
+ // conditions
+ if (!success && !m_ignore_conditions)
+ return false;
+
+ uint32_t orig_pc_value = 0;
+ if (auto_advance_pc) {
+ orig_pc_value =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_arm64, 0, &success);
+ if (!success)
+ return false;
+ }
+
+ // Call the Emulate... function.
+ success = (this->*opcode_data->callback)(opcode);
+ if (!success)
+ return false;
+
+ if (auto_advance_pc) {
+ uint32_t new_pc_value =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_arm64, 0, &success);
+ if (!success)
+ return false;
+
+ if (auto_advance_pc && (new_pc_value == orig_pc_value)) {
+ EmulateInstruction::Context context;
+ context.type = eContextAdvancePC;
+ context.SetNoArgs();
+ if (!WriteRegisterUnsigned(context, eRegisterKindLLDB, gpr_pc_arm64,
+ orig_pc_value + 4))
+ return false;
+ }
+ }
+ return true;
+}
+
+bool EmulateInstructionARM64::CreateFunctionEntryUnwind(
+ UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindLLDB);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our previous Call Frame Address is the stack pointer
+ row->GetCFAValue().SetIsRegisterPlusOffset(gpr_sp_arm64, 0);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("EmulateInstructionARM64");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);
+ unwind_plan.SetReturnAddressRegister(gpr_lr_arm64);
+ return true;
+}
+
+uint32_t EmulateInstructionARM64::GetFramePointerRegisterNumber() const {
+ if (m_arch.GetTriple().isAndroid())
+ return LLDB_INVALID_REGNUM; // Don't use frame pointer on android
+
+ return gpr_fp_arm64;
+}
+
+bool EmulateInstructionARM64::UsingAArch32() {
+ bool aarch32 = m_opcode_pstate.RW == 1;
+ // if !HaveAnyAArch32() then assert !aarch32;
+ // if HighestELUsingAArch32() then assert aarch32;
+ return aarch32;
+}
+
+bool EmulateInstructionARM64::BranchTo(const Context &context, uint32_t N,
+ addr_t target) {
+#if 0
+ // Set program counter to a new address, with a branch reason hint for
+ // possible use by hardware fetching the next instruction.
+ BranchTo(bits(N) target, BranchType branch_type)
+ Hint_Branch(branch_type);
+ if N == 32 then
+ assert UsingAArch32();
+ _PC = ZeroExtend(target);
+ else
+ assert N == 64 && !UsingAArch32();
+ // Remove the tag bits from a tagged target
+ case PSTATE.EL of
+ when EL0, EL1
+ if target<55> == '1' && TCR_EL1.TBI1 == '1' then
+ target<63:56> = '11111111';
+ if target<55> == '0' && TCR_EL1.TBI0 == '1' then
+ target<63:56> = '00000000';
+ when EL2
+ if TCR_EL2.TBI == '1' then
+ target<63:56> = '00000000';
+ when EL3
+ if TCR_EL3.TBI == '1' then
+ target<63:56> = '00000000';
+ _PC = target<63:0>;
+ return;
+#endif
+
+ addr_t addr;
+
+ // Hint_Branch(branch_type);
+ if (N == 32) {
+ if (!UsingAArch32())
+ return false;
+ addr = target;
+ } else if (N == 64) {
+ if (UsingAArch32())
+ return false;
+ // TODO: Remove the tag bits from a tagged target
+ addr = target;
+ } else
+ return false;
+
+ return WriteRegisterUnsigned(context, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC, addr);
+}
+
+bool EmulateInstructionARM64::ConditionHolds(const uint32_t cond) {
+ // If we are ignoring conditions, then always return true. this allows us to
+ // iterate over disassembly code and still emulate an instruction even if we
+ // don't have all the right bits set in the CPSR register...
+ if (m_ignore_conditions)
+ return true;
+
+ bool result = false;
+ switch (UnsignedBits(cond, 3, 1)) {
+ case 0:
+ result = (m_opcode_pstate.Z == 1);
+ break;
+ case 1:
+ result = (m_opcode_pstate.C == 1);
+ break;
+ case 2:
+ result = (m_opcode_pstate.N == 1);
+ break;
+ case 3:
+ result = (m_opcode_pstate.V == 1);
+ break;
+ case 4:
+ result = (m_opcode_pstate.C == 1 && m_opcode_pstate.Z == 0);
+ break;
+ case 5:
+ result = (m_opcode_pstate.N == m_opcode_pstate.V);
+ break;
+ case 6:
+ result = (m_opcode_pstate.N == m_opcode_pstate.V && m_opcode_pstate.Z == 0);
+ break;
+ case 7:
+ // Always execute (cond == 0b1110, or the special 0b1111 which gives
+ // opcodes different meanings, but always means execution happens.
+ return true;
+ }
+
+ if (cond & 1)
+ result = !result;
+ return result;
+}
+
+bool EmulateInstructionARM64::EmulateADDSUBImm(const uint32_t opcode) {
+ // integer d = UInt(Rd);
+ // integer n = UInt(Rn);
+ // integer datasize = if sf == 1 then 64 else 32;
+ // boolean sub_op = (op == 1);
+ // boolean setflags = (S == 1);
+ // bits(datasize) imm;
+ //
+ // case shift of
+ // when '00' imm = ZeroExtend(imm12, datasize);
+ // when '01' imm = ZeroExtend(imm12 : Zeros(12), datasize);
+ // when '1x' UNDEFINED;
+ //
+ //
+ // bits(datasize) result;
+ // bits(datasize) operand1 = if n == 31 then SP[] else X[n];
+ // bits(datasize) operand2 = imm;
+ // bits(4) nzcv;
+ // bit carry_in;
+ //
+ // if sub_op then
+ // operand2 = NOT(operand2);
+ // carry_in = 1;
+ // else
+ // carry_in = 0;
+ //
+ // (result, nzcv) = AddWithCarry(operand1, operand2, carry_in);
+ //
+ // if setflags then
+ // PSTATE.NZCV = nzcv;
+ //
+ // if d == 31 && !setflags then
+ // SP[] = result;
+ // else
+ // X[d] = result;
+
+ const uint32_t sf = Bit32(opcode, 31);
+ const uint32_t op = Bit32(opcode, 30);
+ const uint32_t S = Bit32(opcode, 29);
+ const uint32_t shift = Bits32(opcode, 23, 22);
+ const uint32_t imm12 = Bits32(opcode, 21, 10);
+ const uint32_t Rn = Bits32(opcode, 9, 5);
+ const uint32_t Rd = Bits32(opcode, 4, 0);
+
+ bool success = false;
+
+ const uint32_t d = UInt(Rd);
+ const uint32_t n = UInt(Rn);
+ const uint32_t datasize = (sf == 1) ? 64 : 32;
+ boolean sub_op = op == 1;
+ boolean setflags = S == 1;
+ uint64_t imm;
+
+ switch (shift) {
+ case 0:
+ imm = imm12;
+ break;
+ case 1:
+ imm = imm12 << 12;
+ break;
+ default:
+ return false; // UNDEFINED;
+ }
+ uint64_t result;
+ uint64_t operand1 =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_x0_arm64 + n, 0, &success);
+ uint64_t operand2 = imm;
+ bit carry_in;
+
+ if (sub_op) {
+ operand2 = NOT(operand2);
+ carry_in = true;
+ imm = -imm; // For the Register plug offset context below
+ } else {
+ carry_in = false;
+ }
+
+ ProcState proc_state;
+
+ result = AddWithCarry(datasize, operand1, operand2, carry_in, proc_state);
+
+ if (setflags) {
+ m_emulated_pstate.N = proc_state.N;
+ m_emulated_pstate.Z = proc_state.Z;
+ m_emulated_pstate.C = proc_state.C;
+ m_emulated_pstate.V = proc_state.V;
+ }
+
+ Context context;
+ RegisterInfo reg_info_Rn;
+ if (GetRegisterInfo(eRegisterKindLLDB, n, reg_info_Rn))
+ context.SetRegisterPlusOffset(reg_info_Rn, imm);
+
+ if (n == GetFramePointerRegisterNumber() && d == gpr_sp_arm64 && !setflags) {
+ // 'mov sp, fp' - common epilogue instruction, CFA is now in terms of the
+ // stack pointer, instead of frame pointer.
+ context.type = EmulateInstruction::eContextRestoreStackPointer;
+ } else if ((n == gpr_sp_arm64 || n == GetFramePointerRegisterNumber()) &&
+ d == gpr_sp_arm64 && !setflags) {
+ context.type = EmulateInstruction::eContextAdjustStackPointer;
+ } else if (d == GetFramePointerRegisterNumber() && n == gpr_sp_arm64 &&
+ !setflags) {
+ context.type = EmulateInstruction::eContextSetFramePointer;
+ } else {
+ context.type = EmulateInstruction::eContextImmediate;
+ }
+
+ // If setflags && d == gpr_sp_arm64 then d = WZR/XZR. See CMN, CMP
+ if (!setflags || d != gpr_sp_arm64)
+ WriteRegisterUnsigned(context, eRegisterKindLLDB, gpr_x0_arm64 + d, result);
+
+ return false;
+}
+
+template <EmulateInstructionARM64::AddrMode a_mode>
+bool EmulateInstructionARM64::EmulateLDPSTP(const uint32_t opcode) {
+ uint32_t opc = Bits32(opcode, 31, 30);
+ uint32_t V = Bit32(opcode, 26);
+ uint32_t L = Bit32(opcode, 22);
+ uint32_t imm7 = Bits32(opcode, 21, 15);
+ uint32_t Rt2 = Bits32(opcode, 14, 10);
+ uint32_t Rn = Bits32(opcode, 9, 5);
+ uint32_t Rt = Bits32(opcode, 4, 0);
+
+ integer n = UInt(Rn);
+ integer t = UInt(Rt);
+ integer t2 = UInt(Rt2);
+ uint64_t idx;
+
+ MemOp memop = L == 1 ? MemOp_LOAD : MemOp_STORE;
+ boolean vector = (V == 1);
+ // AccType acctype = AccType_NORMAL;
+ boolean is_signed = false;
+ boolean wback = a_mode != AddrMode_OFF;
+ boolean wb_unknown = false;
+ boolean rt_unknown = false;
+ integer scale;
+ integer size;
+
+ if (opc == 3)
+ return false; // UNDEFINED
+
+ if (vector) {
+ scale = 2 + UInt(opc);
+ } else {
+ scale = (opc & 2) ? 3 : 2;
+ is_signed = (opc & 1) != 0;
+ if (is_signed && memop == MemOp_STORE)
+ return false; // UNDEFINED
+ }
+
+ if (!vector && wback && ((t == n) || (t2 == n))) {
+ switch (ConstrainUnpredictable(Unpredictable_WBOVERLAP)) {
+ case Constraint_UNKNOWN:
+ wb_unknown = true; // writeback is UNKNOWN
+ break;
+
+ case Constraint_SUPPRESSWB:
+ wback = false; // writeback is suppressed
+ break;
+
+ case Constraint_NOP:
+ memop = MemOp_NOP; // do nothing
+ wback = false;
+ break;
+
+ case Constraint_NONE:
+ break;
+ }
+ }
+
+ if (memop == MemOp_LOAD && t == t2) {
+ switch (ConstrainUnpredictable(Unpredictable_LDPOVERLAP)) {
+ case Constraint_UNKNOWN:
+ rt_unknown = true; // result is UNKNOWN
+ break;
+
+ case Constraint_NOP:
+ memop = MemOp_NOP; // do nothing
+ wback = false;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ idx = LSL(llvm::SignExtend64<7>(imm7), scale);
+ size = (integer)1 << scale;
+ uint64_t datasize = size * 8;
+ uint64_t address;
+ uint64_t wb_address;
+
+ RegisterValue data_Rt;
+ RegisterValue data_Rt2;
+
+ // if (vector)
+ // CheckFPEnabled(false);
+
+ RegisterInfo reg_info_base;
+ RegisterInfo reg_info_Rt;
+ RegisterInfo reg_info_Rt2;
+ if (!GetRegisterInfo(eRegisterKindLLDB, gpr_x0_arm64 + n, reg_info_base))
+ return false;
+
+ if (vector) {
+ if (!GetRegisterInfo(eRegisterKindLLDB, fpu_d0_arm64 + t, reg_info_Rt))
+ return false;
+ if (!GetRegisterInfo(eRegisterKindLLDB, fpu_d0_arm64 + t2, reg_info_Rt2))
+ return false;
+ } else {
+ if (!GetRegisterInfo(eRegisterKindLLDB, gpr_x0_arm64 + t, reg_info_Rt))
+ return false;
+ if (!GetRegisterInfo(eRegisterKindLLDB, gpr_x0_arm64 + t2, reg_info_Rt2))
+ return false;
+ }
+
+ bool success = false;
+ if (n == 31) {
+ // CheckSPAlignment();
+ address =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_sp_arm64, 0, &success);
+ } else
+ address =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_x0_arm64 + n, 0, &success);
+
+ wb_address = address + idx;
+ if (a_mode != AddrMode_POST)
+ address = wb_address;
+
+ Context context_t;
+ Context context_t2;
+
+ uint8_t buffer[RegisterValue::kMaxRegisterByteSize];
+ Status error;
+
+ switch (memop) {
+ case MemOp_STORE: {
+ if (n == 31 || n == GetFramePointerRegisterNumber()) // if this store is
+ // based off of the sp
+ // or fp register
+ {
+ context_t.type = eContextPushRegisterOnStack;
+ context_t2.type = eContextPushRegisterOnStack;
+ } else {
+ context_t.type = eContextRegisterStore;
+ context_t2.type = eContextRegisterStore;
+ }
+ context_t.SetRegisterToRegisterPlusOffset(reg_info_Rt, reg_info_base, 0);
+ context_t2.SetRegisterToRegisterPlusOffset(reg_info_Rt2, reg_info_base,
+ size);
+
+ if (!ReadRegister(&reg_info_Rt, data_Rt))
+ return false;
+
+ if (data_Rt.GetAsMemoryData(&reg_info_Rt, buffer, reg_info_Rt.byte_size,
+ eByteOrderLittle, error) == 0)
+ return false;
+
+ if (!WriteMemory(context_t, address + 0, buffer, reg_info_Rt.byte_size))
+ return false;
+
+ if (!ReadRegister(&reg_info_Rt2, data_Rt2))
+ return false;
+
+ if (data_Rt2.GetAsMemoryData(&reg_info_Rt2, buffer, reg_info_Rt2.byte_size,
+ eByteOrderLittle, error) == 0)
+ return false;
+
+ if (!WriteMemory(context_t2, address + size, buffer,
+ reg_info_Rt2.byte_size))
+ return false;
+ } break;
+
+ case MemOp_LOAD: {
+ if (n == 31 || n == GetFramePointerRegisterNumber()) // if this load is
+ // based off of the sp
+ // or fp register
+ {
+ context_t.type = eContextPopRegisterOffStack;
+ context_t2.type = eContextPopRegisterOffStack;
+ } else {
+ context_t.type = eContextRegisterLoad;
+ context_t2.type = eContextRegisterLoad;
+ }
+ context_t.SetAddress(address);
+ context_t2.SetAddress(address + size);
+
+ if (rt_unknown)
+ memset(buffer, 'U', reg_info_Rt.byte_size);
+ else {
+ if (!ReadMemory(context_t, address, buffer, reg_info_Rt.byte_size))
+ return false;
+ }
+
+ if (data_Rt.SetFromMemoryData(&reg_info_Rt, buffer, reg_info_Rt.byte_size,
+ eByteOrderLittle, error) == 0)
+ return false;
+
+ if (!vector && is_signed && !data_Rt.SignExtend(datasize))
+ return false;
+
+ if (!WriteRegister(context_t, &reg_info_Rt, data_Rt))
+ return false;
+
+ if (!rt_unknown) {
+ if (!ReadMemory(context_t2, address + size, buffer,
+ reg_info_Rt2.byte_size))
+ return false;
+ }
+
+ if (data_Rt2.SetFromMemoryData(&reg_info_Rt2, buffer,
+ reg_info_Rt2.byte_size, eByteOrderLittle,
+ error) == 0)
+ return false;
+
+ if (!vector && is_signed && !data_Rt2.SignExtend(datasize))
+ return false;
+
+ if (!WriteRegister(context_t2, &reg_info_Rt2, data_Rt2))
+ return false;
+ } break;
+
+ default:
+ break;
+ }
+
+ if (wback) {
+ if (wb_unknown)
+ wb_address = LLDB_INVALID_ADDRESS;
+ Context context;
+ context.SetImmediateSigned(idx);
+ if (n == 31)
+ context.type = eContextAdjustStackPointer;
+ else
+ context.type = eContextAdjustBaseRegister;
+ WriteRegisterUnsigned(context, &reg_info_base, wb_address);
+ }
+ return true;
+}
+
+template <EmulateInstructionARM64::AddrMode a_mode>
+bool EmulateInstructionARM64::EmulateLDRSTRImm(const uint32_t opcode) {
+ uint32_t size = Bits32(opcode, 31, 30);
+ uint32_t opc = Bits32(opcode, 23, 22);
+ uint32_t n = Bits32(opcode, 9, 5);
+ uint32_t t = Bits32(opcode, 4, 0);
+
+ bool wback;
+ bool postindex;
+ uint64_t offset;
+
+ switch (a_mode) {
+ case AddrMode_POST:
+ wback = true;
+ postindex = true;
+ offset = llvm::SignExtend64<9>(Bits32(opcode, 20, 12));
+ break;
+ case AddrMode_PRE:
+ wback = true;
+ postindex = false;
+ offset = llvm::SignExtend64<9>(Bits32(opcode, 20, 12));
+ break;
+ case AddrMode_OFF:
+ wback = false;
+ postindex = false;
+ offset = LSL(Bits32(opcode, 21, 10), size);
+ break;
+ }
+
+ MemOp memop;
+
+ if (Bit32(opc, 1) == 0) {
+ memop = Bit32(opc, 0) == 1 ? MemOp_LOAD : MemOp_STORE;
+ } else {
+ memop = MemOp_LOAD;
+ if (size == 2 && Bit32(opc, 0) == 1)
+ return false;
+ }
+
+ Status error;
+ bool success = false;
+ uint64_t address;
+ uint8_t buffer[RegisterValue::kMaxRegisterByteSize];
+ RegisterValue data_Rt;
+
+ if (n == 31)
+ address =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_sp_arm64, 0, &success);
+ else
+ address =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_x0_arm64 + n, 0, &success);
+
+ if (!success)
+ return false;
+
+ if (!postindex)
+ address += offset;
+
+ RegisterInfo reg_info_base;
+ if (!GetRegisterInfo(eRegisterKindLLDB, gpr_x0_arm64 + n, reg_info_base))
+ return false;
+
+ RegisterInfo reg_info_Rt;
+ if (!GetRegisterInfo(eRegisterKindLLDB, gpr_x0_arm64 + t, reg_info_Rt))
+ return false;
+
+ Context context;
+ switch (memop) {
+ case MemOp_STORE:
+ if (n == 31 || n == GetFramePointerRegisterNumber()) // if this store is
+ // based off of the sp
+ // or fp register
+ context.type = eContextPushRegisterOnStack;
+ else
+ context.type = eContextRegisterStore;
+ context.SetRegisterToRegisterPlusOffset(reg_info_Rt, reg_info_base,
+ postindex ? 0 : offset);
+
+ if (!ReadRegister(&reg_info_Rt, data_Rt))
+ return false;
+
+ if (data_Rt.GetAsMemoryData(&reg_info_Rt, buffer, reg_info_Rt.byte_size,
+ eByteOrderLittle, error) == 0)
+ return false;
+
+ if (!WriteMemory(context, address, buffer, reg_info_Rt.byte_size))
+ return false;
+ break;
+
+ case MemOp_LOAD:
+ if (n == 31 || n == GetFramePointerRegisterNumber()) // if this store is
+ // based off of the sp
+ // or fp register
+ context.type = eContextPopRegisterOffStack;
+ else
+ context.type = eContextRegisterLoad;
+ context.SetAddress(address);
+
+ if (!ReadMemory(context, address, buffer, reg_info_Rt.byte_size))
+ return false;
+
+ if (data_Rt.SetFromMemoryData(&reg_info_Rt, buffer, reg_info_Rt.byte_size,
+ eByteOrderLittle, error) == 0)
+ return false;
+
+ if (!WriteRegister(context, &reg_info_Rt, data_Rt))
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ if (wback) {
+ if (postindex)
+ address += offset;
+
+ if (n == 31)
+ context.type = eContextAdjustStackPointer;
+ else
+ context.type = eContextAdjustBaseRegister;
+ context.SetImmediateSigned(offset);
+
+ if (!WriteRegisterUnsigned(context, &reg_info_base, address))
+ return false;
+ }
+ return true;
+}
+
+bool EmulateInstructionARM64::EmulateB(const uint32_t opcode) {
+#if 0
+ // ARM64 pseudo code...
+ if branch_type == BranchType_CALL then X[30] = PC[] + 4;
+ BranchTo(PC[] + offset, branch_type);
+#endif
+
+ bool success = false;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRelativeBranchImmediate;
+ const uint64_t pc = ReadRegisterUnsigned(eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC, 0, &success);
+ if (!success)
+ return false;
+
+ int64_t offset = llvm::SignExtend64<28>(Bits32(opcode, 25, 0) << 2);
+ BranchType branch_type = Bit32(opcode, 31) ? BranchType_CALL : BranchType_JMP;
+ addr_t target = pc + offset;
+ context.SetImmediateSigned(offset);
+
+ switch (branch_type) {
+ case BranchType_CALL: {
+ addr_t x30 = pc + 4;
+ if (!WriteRegisterUnsigned(context, eRegisterKindLLDB, gpr_lr_arm64, x30))
+ return false;
+ } break;
+ case BranchType_JMP:
+ break;
+ default:
+ return false;
+ }
+
+ return BranchTo(context, 64, target);
+}
+
+bool EmulateInstructionARM64::EmulateBcond(const uint32_t opcode) {
+#if 0
+ // ARM64 pseudo code...
+ bits(64) offset = SignExtend(imm19:'00', 64);
+ bits(4) condition = cond;
+ if ConditionHolds(condition) then
+ BranchTo(PC[] + offset, BranchType_JMP);
+#endif
+
+ if (ConditionHolds(Bits32(opcode, 3, 0))) {
+ bool success = false;
+
+ const uint64_t pc = ReadRegisterUnsigned(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, 0, &success);
+ if (!success)
+ return false;
+
+ int64_t offset = llvm::SignExtend64<21>(Bits32(opcode, 23, 5) << 2);
+ addr_t target = pc + offset;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRelativeBranchImmediate;
+ context.SetImmediateSigned(offset);
+ if (!BranchTo(context, 64, target))
+ return false;
+ }
+ return true;
+}
+
+bool EmulateInstructionARM64::EmulateCBZ(const uint32_t opcode) {
+#if 0
+ integer t = UInt(Rt);
+ integer datasize = if sf == '1' then 64 else 32;
+ boolean iszero = (op == '0');
+ bits(64) offset = SignExtend(imm19:'00', 64);
+
+ bits(datasize) operand1 = X[t];
+ if IsZero(operand1) == iszero then
+ BranchTo(PC[] + offset, BranchType_JMP);
+#endif
+
+ bool success = false;
+
+ uint32_t t = Bits32(opcode, 4, 0);
+ bool is_zero = Bit32(opcode, 24) == 0;
+ int32_t offset = llvm::SignExtend64<21>(Bits32(opcode, 23, 5) << 2);
+
+ const uint64_t operand =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_x0_arm64 + t, 0, &success);
+ if (!success)
+ return false;
+
+ if (m_ignore_conditions || ((operand == 0) == is_zero)) {
+ const uint64_t pc = ReadRegisterUnsigned(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, 0, &success);
+ if (!success)
+ return false;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRelativeBranchImmediate;
+ context.SetImmediateSigned(offset);
+ if (!BranchTo(context, 64, pc + offset))
+ return false;
+ }
+ return true;
+}
+
+bool EmulateInstructionARM64::EmulateTBZ(const uint32_t opcode) {
+#if 0
+ integer t = UInt(Rt);
+ integer datasize = if b5 == '1' then 64 else 32;
+ integer bit_pos = UInt(b5:b40);
+ bit bit_val = op;
+ bits(64) offset = SignExtend(imm14:'00', 64);
+#endif
+
+ bool success = false;
+
+ uint32_t t = Bits32(opcode, 4, 0);
+ uint32_t bit_pos = (Bit32(opcode, 31) << 6) | (Bits32(opcode, 23, 19));
+ uint32_t bit_val = Bit32(opcode, 24);
+ int64_t offset = llvm::SignExtend64<16>(Bits32(opcode, 18, 5) << 2);
+
+ const uint64_t operand =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_x0_arm64 + t, 0, &success);
+ if (!success)
+ return false;
+
+ if (m_ignore_conditions || Bit32(operand, bit_pos) == bit_val) {
+ const uint64_t pc = ReadRegisterUnsigned(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, 0, &success);
+ if (!success)
+ return false;
+
+ EmulateInstruction::Context context;
+ context.type = EmulateInstruction::eContextRelativeBranchImmediate;
+ context.SetImmediateSigned(offset);
+ if (!BranchTo(context, 64, pc + offset))
+ return false;
+ }
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.h b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.h
new file mode 100644
index 000000000000..03a57a2cf92b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.h
@@ -0,0 +1,192 @@
+//===-- EmulateInstructionARM64.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 EmulateInstructionARM64_h_
+#define EmulateInstructionARM64_h_
+
+#include "Plugins/Process/Utility/ARMDefines.h"
+#include "lldb/Core/EmulateInstruction.h"
+#include "lldb/Interpreter/OptionValue.h"
+#include "lldb/Utility/Status.h"
+
+class EmulateInstructionARM64 : public lldb_private::EmulateInstruction {
+public:
+ EmulateInstructionARM64(const lldb_private::ArchSpec &arch)
+ : EmulateInstruction(arch), m_opcode_pstate(), m_emulated_pstate(),
+ m_ignore_conditions(false) {}
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::EmulateInstruction *
+ CreateInstance(const lldb_private::ArchSpec &arch,
+ lldb_private::InstructionType inst_type);
+
+ static bool SupportsEmulatingInstructionsOfTypeStatic(
+ lldb_private::InstructionType inst_type) {
+ switch (inst_type) {
+ case lldb_private::eInstructionTypeAny:
+ case lldb_private::eInstructionTypePrologueEpilogue:
+ return true;
+
+ case lldb_private::eInstructionTypePCModifying:
+ case lldb_private::eInstructionTypeAll:
+ return false;
+ }
+ return false;
+ }
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ bool SetTargetTriple(const lldb_private::ArchSpec &arch) override;
+
+ bool SupportsEmulatingInstructionsOfType(
+ lldb_private::InstructionType inst_type) override {
+ return SupportsEmulatingInstructionsOfTypeStatic(inst_type);
+ }
+
+ bool ReadInstruction() override;
+
+ bool EvaluateInstruction(uint32_t evaluate_options) override;
+
+ bool TestEmulation(lldb_private::Stream *out_stream,
+ lldb_private::ArchSpec &arch,
+ lldb_private::OptionValueDictionary *test_data) override {
+ return false;
+ }
+
+ bool GetRegisterInfo(lldb::RegisterKind reg_kind, uint32_t reg_num,
+ lldb_private::RegisterInfo &reg_info) override;
+
+ bool
+ CreateFunctionEntryUnwind(lldb_private::UnwindPlan &unwind_plan) override;
+
+ enum AddrMode { AddrMode_OFF, AddrMode_PRE, AddrMode_POST };
+
+ enum BranchType {
+ BranchType_CALL,
+ BranchType_ERET,
+ BranchType_DRET,
+ BranchType_RET,
+ BranchType_JMP
+ };
+
+ enum CountOp { CountOp_CLZ, CountOp_CLS, CountOp_CNT };
+
+ enum RevOp { RevOp_RBIT, RevOp_REV16, RevOp_REV32, RevOp_REV64 };
+
+ enum BitwiseOp { BitwiseOp_NOT, BitwiseOp_RBIT };
+
+ enum ExceptionLevel { EL0 = 0, EL1 = 1, EL2 = 2, EL3 = 3 };
+
+ enum ExtendType {
+ ExtendType_SXTB,
+ ExtendType_SXTH,
+ ExtendType_SXTW,
+ ExtendType_SXTX,
+ ExtendType_UXTB,
+ ExtendType_UXTH,
+ ExtendType_UXTW,
+ ExtendType_UXTX
+ };
+
+ enum ExtractType { ExtractType_LEFT, ExtractType_RIGHT };
+
+ enum LogicalOp { LogicalOp_AND, LogicalOp_EOR, LogicalOp_ORR };
+
+ enum MemOp { MemOp_LOAD, MemOp_STORE, MemOp_PREFETCH, MemOp_NOP };
+
+ enum MoveWideOp { MoveWideOp_N, MoveWideOp_Z, MoveWideOp_K };
+
+ enum ShiftType { ShiftType_LSL, ShiftType_LSR, ShiftType_ASR, ShiftType_ROR };
+
+ enum StackPointerSelection { SP0 = 0, SPx = 1 };
+
+ enum Unpredictable { Unpredictable_WBOVERLAP, Unpredictable_LDPOVERLAP };
+
+ enum ConstraintType {
+ Constraint_NONE,
+ Constraint_UNKNOWN,
+ Constraint_SUPPRESSWB,
+ Constraint_NOP
+ };
+
+ enum AccType {
+ AccType_NORMAL,
+ AccType_UNPRIV,
+ AccType_STREAM,
+ AccType_ALIGNED,
+ AccType_ORDERED
+ };
+
+ typedef struct {
+ uint32_t N : 1, V : 1, C : 1,
+ Z : 1, // condition code flags – can also be accessed as
+ // PSTATE.[N,Z,C,V]
+ Q : 1, // AArch32 only – CSPR.Q bit
+ IT : 8, // AArch32 only – CPSR.IT bits
+ J : 1, // AArch32 only – CSPR.J bit
+ T : 1, // AArch32 only – CPSR.T bit
+ SS : 1, // Single step process state bit
+ IL : 1, // Illegal state bit
+ D : 1, A : 1, I : 1,
+ F : 1, // Interrupt masks – can also be accessed as PSTATE.[D,A,I,F]
+ E : 1, // AArch32 only – CSPR.E bit
+ M : 5, // AArch32 only – mode encodings
+ RW : 1, // Current register width – 0 is AArch64, 1 is AArch32
+ EL : 2, // Current exception level (see ExceptionLevel enum)
+ SP : 1; // AArch64 only - Stack Pointer selection (see
+ // StackPointerSelection enum)
+ } ProcState;
+
+protected:
+ typedef struct {
+ uint32_t mask;
+ uint32_t value;
+ uint32_t vfp_variants;
+ bool (EmulateInstructionARM64::*callback)(const uint32_t opcode);
+ const char *name;
+ } Opcode;
+
+ static Opcode *GetOpcodeForInstruction(const uint32_t opcode);
+
+ uint32_t GetFramePointerRegisterNumber() const;
+
+ bool BranchTo(const Context &context, uint32_t N, lldb::addr_t target);
+
+ bool ConditionHolds(const uint32_t cond);
+
+ bool UsingAArch32();
+
+ bool EmulateADDSUBImm(const uint32_t opcode);
+
+ template <AddrMode a_mode> bool EmulateLDPSTP(const uint32_t opcode);
+
+ template <AddrMode a_mode> bool EmulateLDRSTRImm(const uint32_t opcode);
+
+ bool EmulateB(const uint32_t opcode);
+
+ bool EmulateBcond(const uint32_t opcode);
+
+ bool EmulateCBZ(const uint32_t opcode);
+
+ bool EmulateTBZ(const uint32_t opcode);
+
+ ProcState m_opcode_pstate;
+ ProcState m_emulated_pstate; // This can get updated by the opcode.
+ bool m_ignore_conditions;
+};
+
+#endif // EmulateInstructionARM64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp b/contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp
new file mode 100644
index 000000000000..cbf3dda7896e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp
@@ -0,0 +1,3050 @@
+//===-- EmulateInstructionMIPS.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 "EmulateInstructionMIPS.h"
+
+#include <stdlib.h>
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Opcode.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Stream.h"
+#include "llvm-c/Disassembler.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+#include "Plugins/Process/Utility/InstructionUtils.h"
+#include "Plugins/Process/Utility/RegisterContext_mips.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#define UInt(x) ((uint64_t)x)
+#define integer int64_t
+
+//
+// EmulateInstructionMIPS implementation
+//
+
+#ifdef __mips__
+extern "C" {
+void LLVMInitializeMipsTargetInfo();
+void LLVMInitializeMipsTarget();
+void LLVMInitializeMipsAsmPrinter();
+void LLVMInitializeMipsTargetMC();
+void LLVMInitializeMipsDisassembler();
+}
+#endif
+
+EmulateInstructionMIPS::EmulateInstructionMIPS(
+ const lldb_private::ArchSpec &arch)
+ : EmulateInstruction(arch) {
+ /* Create instance of llvm::MCDisassembler */
+ std::string Status;
+ llvm::Triple triple = arch.GetTriple();
+ const llvm::Target *target =
+ llvm::TargetRegistry::lookupTarget(triple.getTriple(), Status);
+
+/*
+ * If we fail to get the target then we haven't registered it. The
+ * SystemInitializerCommon
+ * does not initialize targets, MCs and disassemblers. However we need the
+ * MCDisassembler
+ * to decode the instructions so that the decoding complexity stays with LLVM.
+ * Initialize the MIPS targets and disassemblers.
+*/
+#ifdef __mips__
+ if (!target) {
+ LLVMInitializeMipsTargetInfo();
+ LLVMInitializeMipsTarget();
+ LLVMInitializeMipsAsmPrinter();
+ LLVMInitializeMipsTargetMC();
+ LLVMInitializeMipsDisassembler();
+ target = llvm::TargetRegistry::lookupTarget(triple.getTriple(), Status);
+ }
+#endif
+
+ assert(target);
+
+ llvm::StringRef cpu;
+
+ switch (arch.GetCore()) {
+ case ArchSpec::eCore_mips32:
+ case ArchSpec::eCore_mips32el:
+ cpu = "mips32";
+ break;
+ case ArchSpec::eCore_mips32r2:
+ case ArchSpec::eCore_mips32r2el:
+ cpu = "mips32r2";
+ break;
+ case ArchSpec::eCore_mips32r3:
+ case ArchSpec::eCore_mips32r3el:
+ cpu = "mips32r3";
+ break;
+ case ArchSpec::eCore_mips32r5:
+ case ArchSpec::eCore_mips32r5el:
+ cpu = "mips32r5";
+ break;
+ case ArchSpec::eCore_mips32r6:
+ case ArchSpec::eCore_mips32r6el:
+ cpu = "mips32r6";
+ break;
+ case ArchSpec::eCore_mips64:
+ case ArchSpec::eCore_mips64el:
+ cpu = "mips64";
+ break;
+ case ArchSpec::eCore_mips64r2:
+ case ArchSpec::eCore_mips64r2el:
+ cpu = "mips64r2";
+ break;
+ case ArchSpec::eCore_mips64r3:
+ case ArchSpec::eCore_mips64r3el:
+ cpu = "mips64r3";
+ break;
+ case ArchSpec::eCore_mips64r5:
+ case ArchSpec::eCore_mips64r5el:
+ cpu = "mips64r5";
+ break;
+ case ArchSpec::eCore_mips64r6:
+ case ArchSpec::eCore_mips64r6el:
+ cpu = "mips64r6";
+ break;
+ default:
+ cpu = "generic";
+ break;
+ }
+
+ std::string features = "";
+ uint32_t arch_flags = arch.GetFlags();
+ if (arch_flags & ArchSpec::eMIPSAse_msa)
+ features += "+msa,";
+ if (arch_flags & ArchSpec::eMIPSAse_dsp)
+ features += "+dsp,";
+ if (arch_flags & ArchSpec::eMIPSAse_dspr2)
+ features += "+dspr2,";
+
+ m_reg_info.reset(target->createMCRegInfo(triple.getTriple()));
+ assert(m_reg_info.get());
+
+ m_insn_info.reset(target->createMCInstrInfo());
+ assert(m_insn_info.get());
+
+ m_asm_info.reset(target->createMCAsmInfo(*m_reg_info, triple.getTriple()));
+ m_subtype_info.reset(
+ target->createMCSubtargetInfo(triple.getTriple(), cpu, features));
+ assert(m_asm_info.get() && m_subtype_info.get());
+
+ m_context.reset(
+ new llvm::MCContext(m_asm_info.get(), m_reg_info.get(), nullptr));
+ assert(m_context.get());
+
+ m_disasm.reset(target->createMCDisassembler(*m_subtype_info, *m_context));
+ assert(m_disasm.get());
+
+ /* Create alternate disassembler for microMIPS */
+ if (arch_flags & ArchSpec::eMIPSAse_mips16)
+ features += "+mips16,";
+ else if (arch_flags & ArchSpec::eMIPSAse_micromips)
+ features += "+micromips,";
+
+ m_alt_subtype_info.reset(
+ target->createMCSubtargetInfo(triple.getTriple(), cpu, features));
+ assert(m_alt_subtype_info.get());
+
+ m_alt_disasm.reset(
+ target->createMCDisassembler(*m_alt_subtype_info, *m_context));
+ assert(m_alt_disasm.get());
+
+ m_next_inst_size = 0;
+ m_use_alt_disaasm = false;
+}
+
+void EmulateInstructionMIPS::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void EmulateInstructionMIPS::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+ConstString EmulateInstructionMIPS::GetPluginNameStatic() {
+ ConstString g_plugin_name("lldb.emulate-instruction.mips32");
+ return g_plugin_name;
+}
+
+lldb_private::ConstString EmulateInstructionMIPS::GetPluginName() {
+ static ConstString g_plugin_name("EmulateInstructionMIPS");
+ return g_plugin_name;
+}
+
+const char *EmulateInstructionMIPS::GetPluginDescriptionStatic() {
+ return "Emulate instructions for the MIPS32 architecture.";
+}
+
+EmulateInstruction *
+EmulateInstructionMIPS::CreateInstance(const ArchSpec &arch,
+ InstructionType inst_type) {
+ if (EmulateInstructionMIPS::SupportsEmulatingInstructionsOfTypeStatic(
+ inst_type)) {
+ if (arch.GetTriple().getArch() == llvm::Triple::mips ||
+ arch.GetTriple().getArch() == llvm::Triple::mipsel) {
+ return new EmulateInstructionMIPS(arch);
+ }
+ }
+
+ return nullptr;
+}
+
+bool EmulateInstructionMIPS::SetTargetTriple(const ArchSpec &arch) {
+ return arch.GetTriple().getArch() == llvm::Triple::mips ||
+ arch.GetTriple().getArch() == llvm::Triple::mipsel;
+}
+
+const char *EmulateInstructionMIPS::GetRegisterName(unsigned reg_num,
+ bool alternate_name) {
+ if (alternate_name) {
+ switch (reg_num) {
+ case dwarf_sp_mips:
+ return "r29";
+ case dwarf_r30_mips:
+ return "r30";
+ case dwarf_ra_mips:
+ return "r31";
+ case dwarf_f0_mips:
+ return "f0";
+ case dwarf_f1_mips:
+ return "f1";
+ case dwarf_f2_mips:
+ return "f2";
+ case dwarf_f3_mips:
+ return "f3";
+ case dwarf_f4_mips:
+ return "f4";
+ case dwarf_f5_mips:
+ return "f5";
+ case dwarf_f6_mips:
+ return "f6";
+ case dwarf_f7_mips:
+ return "f7";
+ case dwarf_f8_mips:
+ return "f8";
+ case dwarf_f9_mips:
+ return "f9";
+ case dwarf_f10_mips:
+ return "f10";
+ case dwarf_f11_mips:
+ return "f11";
+ case dwarf_f12_mips:
+ return "f12";
+ case dwarf_f13_mips:
+ return "f13";
+ case dwarf_f14_mips:
+ return "f14";
+ case dwarf_f15_mips:
+ return "f15";
+ case dwarf_f16_mips:
+ return "f16";
+ case dwarf_f17_mips:
+ return "f17";
+ case dwarf_f18_mips:
+ return "f18";
+ case dwarf_f19_mips:
+ return "f19";
+ case dwarf_f20_mips:
+ return "f20";
+ case dwarf_f21_mips:
+ return "f21";
+ case dwarf_f22_mips:
+ return "f22";
+ case dwarf_f23_mips:
+ return "f23";
+ case dwarf_f24_mips:
+ return "f24";
+ case dwarf_f25_mips:
+ return "f25";
+ case dwarf_f26_mips:
+ return "f26";
+ case dwarf_f27_mips:
+ return "f27";
+ case dwarf_f28_mips:
+ return "f28";
+ case dwarf_f29_mips:
+ return "f29";
+ case dwarf_f30_mips:
+ return "f30";
+ case dwarf_f31_mips:
+ return "f31";
+ case dwarf_w0_mips:
+ return "w0";
+ case dwarf_w1_mips:
+ return "w1";
+ case dwarf_w2_mips:
+ return "w2";
+ case dwarf_w3_mips:
+ return "w3";
+ case dwarf_w4_mips:
+ return "w4";
+ case dwarf_w5_mips:
+ return "w5";
+ case dwarf_w6_mips:
+ return "w6";
+ case dwarf_w7_mips:
+ return "w7";
+ case dwarf_w8_mips:
+ return "w8";
+ case dwarf_w9_mips:
+ return "w9";
+ case dwarf_w10_mips:
+ return "w10";
+ case dwarf_w11_mips:
+ return "w11";
+ case dwarf_w12_mips:
+ return "w12";
+ case dwarf_w13_mips:
+ return "w13";
+ case dwarf_w14_mips:
+ return "w14";
+ case dwarf_w15_mips:
+ return "w15";
+ case dwarf_w16_mips:
+ return "w16";
+ case dwarf_w17_mips:
+ return "w17";
+ case dwarf_w18_mips:
+ return "w18";
+ case dwarf_w19_mips:
+ return "w19";
+ case dwarf_w20_mips:
+ return "w20";
+ case dwarf_w21_mips:
+ return "w21";
+ case dwarf_w22_mips:
+ return "w22";
+ case dwarf_w23_mips:
+ return "w23";
+ case dwarf_w24_mips:
+ return "w24";
+ case dwarf_w25_mips:
+ return "w25";
+ case dwarf_w26_mips:
+ return "w26";
+ case dwarf_w27_mips:
+ return "w27";
+ case dwarf_w28_mips:
+ return "w28";
+ case dwarf_w29_mips:
+ return "w29";
+ case dwarf_w30_mips:
+ return "w30";
+ case dwarf_w31_mips:
+ return "w31";
+ case dwarf_mir_mips:
+ return "mir";
+ case dwarf_mcsr_mips:
+ return "mcsr";
+ case dwarf_config5_mips:
+ return "config5";
+ default:
+ break;
+ }
+ return nullptr;
+ }
+
+ switch (reg_num) {
+ case dwarf_zero_mips:
+ return "r0";
+ case dwarf_r1_mips:
+ return "r1";
+ case dwarf_r2_mips:
+ return "r2";
+ case dwarf_r3_mips:
+ return "r3";
+ case dwarf_r4_mips:
+ return "r4";
+ case dwarf_r5_mips:
+ return "r5";
+ case dwarf_r6_mips:
+ return "r6";
+ case dwarf_r7_mips:
+ return "r7";
+ case dwarf_r8_mips:
+ return "r8";
+ case dwarf_r9_mips:
+ return "r9";
+ case dwarf_r10_mips:
+ return "r10";
+ case dwarf_r11_mips:
+ return "r11";
+ case dwarf_r12_mips:
+ return "r12";
+ case dwarf_r13_mips:
+ return "r13";
+ case dwarf_r14_mips:
+ return "r14";
+ case dwarf_r15_mips:
+ return "r15";
+ case dwarf_r16_mips:
+ return "r16";
+ case dwarf_r17_mips:
+ return "r17";
+ case dwarf_r18_mips:
+ return "r18";
+ case dwarf_r19_mips:
+ return "r19";
+ case dwarf_r20_mips:
+ return "r20";
+ case dwarf_r21_mips:
+ return "r21";
+ case dwarf_r22_mips:
+ return "r22";
+ case dwarf_r23_mips:
+ return "r23";
+ case dwarf_r24_mips:
+ return "r24";
+ case dwarf_r25_mips:
+ return "r25";
+ case dwarf_r26_mips:
+ return "r26";
+ case dwarf_r27_mips:
+ return "r27";
+ case dwarf_gp_mips:
+ return "gp";
+ case dwarf_sp_mips:
+ return "sp";
+ case dwarf_r30_mips:
+ return "fp";
+ case dwarf_ra_mips:
+ return "ra";
+ case dwarf_sr_mips:
+ return "sr";
+ case dwarf_lo_mips:
+ return "lo";
+ case dwarf_hi_mips:
+ return "hi";
+ case dwarf_bad_mips:
+ return "bad";
+ case dwarf_cause_mips:
+ return "cause";
+ case dwarf_pc_mips:
+ return "pc";
+ case dwarf_f0_mips:
+ return "f0";
+ case dwarf_f1_mips:
+ return "f1";
+ case dwarf_f2_mips:
+ return "f2";
+ case dwarf_f3_mips:
+ return "f3";
+ case dwarf_f4_mips:
+ return "f4";
+ case dwarf_f5_mips:
+ return "f5";
+ case dwarf_f6_mips:
+ return "f6";
+ case dwarf_f7_mips:
+ return "f7";
+ case dwarf_f8_mips:
+ return "f8";
+ case dwarf_f9_mips:
+ return "f9";
+ case dwarf_f10_mips:
+ return "f10";
+ case dwarf_f11_mips:
+ return "f11";
+ case dwarf_f12_mips:
+ return "f12";
+ case dwarf_f13_mips:
+ return "f13";
+ case dwarf_f14_mips:
+ return "f14";
+ case dwarf_f15_mips:
+ return "f15";
+ case dwarf_f16_mips:
+ return "f16";
+ case dwarf_f17_mips:
+ return "f17";
+ case dwarf_f18_mips:
+ return "f18";
+ case dwarf_f19_mips:
+ return "f19";
+ case dwarf_f20_mips:
+ return "f20";
+ case dwarf_f21_mips:
+ return "f21";
+ case dwarf_f22_mips:
+ return "f22";
+ case dwarf_f23_mips:
+ return "f23";
+ case dwarf_f24_mips:
+ return "f24";
+ case dwarf_f25_mips:
+ return "f25";
+ case dwarf_f26_mips:
+ return "f26";
+ case dwarf_f27_mips:
+ return "f27";
+ case dwarf_f28_mips:
+ return "f28";
+ case dwarf_f29_mips:
+ return "f29";
+ case dwarf_f30_mips:
+ return "f30";
+ case dwarf_f31_mips:
+ return "f31";
+ case dwarf_fcsr_mips:
+ return "fcsr";
+ case dwarf_fir_mips:
+ return "fir";
+ case dwarf_w0_mips:
+ return "w0";
+ case dwarf_w1_mips:
+ return "w1";
+ case dwarf_w2_mips:
+ return "w2";
+ case dwarf_w3_mips:
+ return "w3";
+ case dwarf_w4_mips:
+ return "w4";
+ case dwarf_w5_mips:
+ return "w5";
+ case dwarf_w6_mips:
+ return "w6";
+ case dwarf_w7_mips:
+ return "w7";
+ case dwarf_w8_mips:
+ return "w8";
+ case dwarf_w9_mips:
+ return "w9";
+ case dwarf_w10_mips:
+ return "w10";
+ case dwarf_w11_mips:
+ return "w11";
+ case dwarf_w12_mips:
+ return "w12";
+ case dwarf_w13_mips:
+ return "w13";
+ case dwarf_w14_mips:
+ return "w14";
+ case dwarf_w15_mips:
+ return "w15";
+ case dwarf_w16_mips:
+ return "w16";
+ case dwarf_w17_mips:
+ return "w17";
+ case dwarf_w18_mips:
+ return "w18";
+ case dwarf_w19_mips:
+ return "w19";
+ case dwarf_w20_mips:
+ return "w20";
+ case dwarf_w21_mips:
+ return "w21";
+ case dwarf_w22_mips:
+ return "w22";
+ case dwarf_w23_mips:
+ return "w23";
+ case dwarf_w24_mips:
+ return "w24";
+ case dwarf_w25_mips:
+ return "w25";
+ case dwarf_w26_mips:
+ return "w26";
+ case dwarf_w27_mips:
+ return "w27";
+ case dwarf_w28_mips:
+ return "w28";
+ case dwarf_w29_mips:
+ return "w29";
+ case dwarf_w30_mips:
+ return "w30";
+ case dwarf_w31_mips:
+ return "w31";
+ case dwarf_mcsr_mips:
+ return "mcsr";
+ case dwarf_mir_mips:
+ return "mir";
+ case dwarf_config5_mips:
+ return "config5";
+ }
+ return nullptr;
+}
+
+bool EmulateInstructionMIPS::GetRegisterInfo(RegisterKind reg_kind,
+ uint32_t reg_num,
+ RegisterInfo &reg_info) {
+ if (reg_kind == eRegisterKindGeneric) {
+ switch (reg_num) {
+ case LLDB_REGNUM_GENERIC_PC:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_pc_mips;
+ break;
+ case LLDB_REGNUM_GENERIC_SP:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_sp_mips;
+ break;
+ case LLDB_REGNUM_GENERIC_FP:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_r30_mips;
+ break;
+ case LLDB_REGNUM_GENERIC_RA:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_ra_mips;
+ break;
+ case LLDB_REGNUM_GENERIC_FLAGS:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_sr_mips;
+ break;
+ default:
+ return false;
+ }
+ }
+
+ if (reg_kind == eRegisterKindDWARF) {
+ ::memset(&reg_info, 0, sizeof(RegisterInfo));
+ ::memset(reg_info.kinds, LLDB_INVALID_REGNUM, sizeof(reg_info.kinds));
+
+ if (reg_num == dwarf_sr_mips || reg_num == dwarf_fcsr_mips ||
+ reg_num == dwarf_fir_mips || reg_num == dwarf_mcsr_mips ||
+ reg_num == dwarf_mir_mips || reg_num == dwarf_config5_mips) {
+ reg_info.byte_size = 4;
+ reg_info.format = eFormatHex;
+ reg_info.encoding = eEncodingUint;
+ } else if ((int)reg_num >= dwarf_zero_mips &&
+ (int)reg_num <= dwarf_f31_mips) {
+ reg_info.byte_size = 4;
+ reg_info.format = eFormatHex;
+ reg_info.encoding = eEncodingUint;
+ } else if ((int)reg_num >= dwarf_w0_mips &&
+ (int)reg_num <= dwarf_w31_mips) {
+ reg_info.byte_size = 16;
+ reg_info.format = eFormatVectorOfUInt8;
+ reg_info.encoding = eEncodingVector;
+ } else {
+ return false;
+ }
+
+ reg_info.name = GetRegisterName(reg_num, false);
+ reg_info.alt_name = GetRegisterName(reg_num, true);
+ reg_info.kinds[eRegisterKindDWARF] = reg_num;
+
+ switch (reg_num) {
+ case dwarf_r30_mips:
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP;
+ break;
+ case dwarf_ra_mips:
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA;
+ break;
+ case dwarf_sp_mips:
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP;
+ break;
+ case dwarf_pc_mips:
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC;
+ break;
+ case dwarf_sr_mips:
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS;
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+ return false;
+}
+
+EmulateInstructionMIPS::MipsOpcode *
+EmulateInstructionMIPS::GetOpcodeForInstruction(const char *op_name) {
+ static EmulateInstructionMIPS::MipsOpcode g_opcodes[] = {
+ // Prologue/Epilogue instructions
+ {"ADDiu", &EmulateInstructionMIPS::Emulate_ADDiu,
+ "ADDIU rt, rs, immediate"},
+ {"SW", &EmulateInstructionMIPS::Emulate_SW, "SW rt, offset(rs)"},
+ {"LW", &EmulateInstructionMIPS::Emulate_LW, "LW rt, offset(base)"},
+ {"SUBU", &EmulateInstructionMIPS::Emulate_SUBU_ADDU, "SUBU rd, rs, rt"},
+ {"ADDU", &EmulateInstructionMIPS::Emulate_SUBU_ADDU, "ADDU rd, rs, rt"},
+ {"LUI", &EmulateInstructionMIPS::Emulate_LUI, "LUI rt, immediate"},
+
+ // MicroMIPS Prologue/Epilogue instructions
+ {"ADDIUSP_MM", &EmulateInstructionMIPS::Emulate_ADDIUSP,
+ "ADDIU immediate"},
+ {"ADDIUS5_MM", &EmulateInstructionMIPS::Emulate_ADDIUS5,
+ "ADDIUS5 rd,immediate"},
+ {"SWSP_MM", &EmulateInstructionMIPS::Emulate_SWSP, "SWSP rt,offset(sp)"},
+ {"SWM16_MM", &EmulateInstructionMIPS::Emulate_SWM16_32,
+ "SWM16 reglist,offset(sp)"},
+ {"SWM32_MM", &EmulateInstructionMIPS::Emulate_SWM16_32,
+ "SWM32 reglist,offset(base)"},
+ {"SWP_MM", &EmulateInstructionMIPS::Emulate_SWM16_32,
+ "SWP rs1,offset(base)"},
+ {"LWSP_MM", &EmulateInstructionMIPS::Emulate_LWSP, "LWSP rt,offset(sp)"},
+ {"LWM16_MM", &EmulateInstructionMIPS::Emulate_LWM16_32,
+ "LWM16 reglist,offset(sp)"},
+ {"LWM32_MM", &EmulateInstructionMIPS::Emulate_LWM16_32,
+ "LWM32 reglist,offset(base)"},
+ {"LWP_MM", &EmulateInstructionMIPS::Emulate_LWM16_32,
+ "LWP rd,offset(base)"},
+ {"JRADDIUSP", &EmulateInstructionMIPS::Emulate_JRADDIUSP,
+ "JRADDIUSP immediate"},
+
+ // Load/Store instructions
+ /* Following list of emulated instructions are required by implementation
+ of hardware watchpoint
+ for MIPS in lldb. As we just need the address accessed by instructions,
+ we have generalised
+ all these instructions in 2 functions depending on their addressing
+ modes */
+
+ {"LB", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LB rt, offset(base)"},
+ {"LBE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LBE rt, offset(base)"},
+ {"LBU", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LBU rt, offset(base)"},
+ {"LBUE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LBUE rt, offset(base)"},
+ {"LDC1", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LDC1 ft, offset(base)"},
+ {"LD", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LD rt, offset(base)"},
+ {"LDL", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LDL rt, offset(base)"},
+ {"LDR", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LDR rt, offset(base)"},
+ {"LLD", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LLD rt, offset(base)"},
+ {"LDC2", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LDC2 rt, offset(base)"},
+ {"LDXC1", &EmulateInstructionMIPS::Emulate_LDST_Reg,
+ "LDXC1 fd, index (base)"},
+ {"LH", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LH rt, offset(base)"},
+ {"LHE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LHE rt, offset(base)"},
+ {"LHU", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LHU rt, offset(base)"},
+ {"LHUE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LHUE rt, offset(base)"},
+ {"LL", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LL rt, offset(base)"},
+ {"LLE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LLE rt, offset(base)"},
+ {"LUXC1", &EmulateInstructionMIPS::Emulate_LDST_Reg,
+ "LUXC1 fd, index (base)"},
+ {"LW", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LW rt, offset(base)"},
+ {"LWC1", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LWC1 ft, offset(base)"},
+ {"LWC2", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LWC2 rt, offset(base)"},
+ {"LWE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LWE rt, offset(base)"},
+ {"LWL", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LWL rt, offset(base)"},
+ {"LWLE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LWLE rt, offset(base)"},
+ {"LWR", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LWR rt, offset(base)"},
+ {"LWRE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LWRE rt, offset(base)"},
+ {"LWXC1", &EmulateInstructionMIPS::Emulate_LDST_Reg,
+ "LWXC1 fd, index (base)"},
+ {"LLX", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LLX rt, offset(base)"},
+ {"LLXE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LLXE rt, offset(base)"},
+ {"LLDX", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LLDX rt, offset(base)"},
+
+ {"SB", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SB rt, offset(base)"},
+ {"SBE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SBE rt, offset(base)"},
+ {"SC", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SC rt, offset(base)"},
+ {"SCE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SCE rt, offset(base)"},
+ {"SCD", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SCD rt, offset(base)"},
+ {"SD", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SD rt, offset(base)"},
+ {"SDL", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SDL rt, offset(base)"},
+ {"SDR", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SDR rt, offset(base)"},
+ {"SDC1", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SDC1 ft, offset(base)"},
+ {"SDC2", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SDC2 rt, offset(base)"},
+ {"SDXC1", &EmulateInstructionMIPS::Emulate_LDST_Reg,
+ "SDXC1 fs, index(base)"},
+ {"SH", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SH rt, offset(base)"},
+ {"SHE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SHE rt, offset(base)"},
+ {"SUXC1", &EmulateInstructionMIPS::Emulate_LDST_Reg,
+ "SUXC1 fs, index (base)"},
+ {"SWC1", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SWC1 ft, offset(base)"},
+ {"SWC2", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SWC2 rt, offset(base)"},
+ {"SWE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SWE rt, offset(base)"},
+ {"SWL", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SWL rt, offset(base)"},
+ {"SWLE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SWLE rt, offset(base)"},
+ {"SWR", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SWR rt, offset(base)"},
+ {"SWRE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SWRE rt, offset(base)"},
+ {"SWXC1", &EmulateInstructionMIPS::Emulate_LDST_Reg,
+ "SWXC1 fs, index (base)"},
+ {"SCX", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SCX rt, offset(base)"},
+ {"SCXE", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SCXE rt, offset(base)"},
+ {"SCDX", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SCDX rt, offset(base)"},
+
+ // MicroMIPS Load/Store instructions
+ {"LBU16_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LBU16 rt, decoded_offset(base)"},
+ {"LHU16_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LHU16 rt, left_shifted_offset(base)"},
+ {"LW16_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LW16 rt, left_shifted_offset(base)"},
+ {"LWGP_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "LWGP rt, left_shifted_offset(gp)"},
+ {"SH16_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SH16 rt, left_shifted_offset(base)"},
+ {"SW16_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SW16 rt, left_shifted_offset(base)"},
+ {"SW_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SWSP rt, left_shifted_offset(base)"},
+ {"SB16_MM", &EmulateInstructionMIPS::Emulate_LDST_Imm,
+ "SB16 rt, offset(base)"},
+
+ // Branch instructions
+ {"BEQ", &EmulateInstructionMIPS::Emulate_BXX_3ops, "BEQ rs,rt,offset"},
+ {"BNE", &EmulateInstructionMIPS::Emulate_BXX_3ops, "BNE rs,rt,offset"},
+ {"BEQL", &EmulateInstructionMIPS::Emulate_BXX_3ops, "BEQL rs,rt,offset"},
+ {"BNEL", &EmulateInstructionMIPS::Emulate_BXX_3ops, "BNEL rs,rt,offset"},
+ {"BGEZALL", &EmulateInstructionMIPS::Emulate_Bcond_Link,
+ "BGEZALL rt,offset"},
+ {"BAL", &EmulateInstructionMIPS::Emulate_BAL, "BAL offset"},
+ {"BGEZAL", &EmulateInstructionMIPS::Emulate_Bcond_Link,
+ "BGEZAL rs,offset"},
+ {"BALC", &EmulateInstructionMIPS::Emulate_BALC, "BALC offset"},
+ {"BC", &EmulateInstructionMIPS::Emulate_BC, "BC offset"},
+ {"BGEZ", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BGEZ rs,offset"},
+ {"BLEZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,
+ "BLEZALC rs,offset"},
+ {"BGEZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,
+ "BGEZALC rs,offset"},
+ {"BLTZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,
+ "BLTZALC rs,offset"},
+ {"BGTZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,
+ "BGTZALC rs,offset"},
+ {"BEQZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,
+ "BEQZALC rs,offset"},
+ {"BNEZALC", &EmulateInstructionMIPS::Emulate_Bcond_Link_C,
+ "BNEZALC rs,offset"},
+ {"BEQC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C,
+ "BEQC rs,rt,offset"},
+ {"BNEC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C,
+ "BNEC rs,rt,offset"},
+ {"BLTC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C,
+ "BLTC rs,rt,offset"},
+ {"BGEC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C,
+ "BGEC rs,rt,offset"},
+ {"BLTUC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C,
+ "BLTUC rs,rt,offset"},
+ {"BGEUC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C,
+ "BGEUC rs,rt,offset"},
+ {"BLTZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BLTZC rt,offset"},
+ {"BLEZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BLEZC rt,offset"},
+ {"BGEZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BGEZC rt,offset"},
+ {"BGTZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BGTZC rt,offset"},
+ {"BEQZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BEQZC rt,offset"},
+ {"BNEZC", &EmulateInstructionMIPS::Emulate_BXX_2ops_C, "BNEZC rt,offset"},
+ {"BGEZL", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BGEZL rt,offset"},
+ {"BGTZ", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BGTZ rt,offset"},
+ {"BGTZL", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BGTZL rt,offset"},
+ {"BLEZ", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BLEZ rt,offset"},
+ {"BLEZL", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BLEZL rt,offset"},
+ {"BLTZ", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BLTZ rt,offset"},
+ {"BLTZAL", &EmulateInstructionMIPS::Emulate_Bcond_Link,
+ "BLTZAL rt,offset"},
+ {"BLTZALL", &EmulateInstructionMIPS::Emulate_Bcond_Link,
+ "BLTZALL rt,offset"},
+ {"BLTZL", &EmulateInstructionMIPS::Emulate_BXX_2ops, "BLTZL rt,offset"},
+ {"BOVC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C,
+ "BOVC rs,rt,offset"},
+ {"BNVC", &EmulateInstructionMIPS::Emulate_BXX_3ops_C,
+ "BNVC rs,rt,offset"},
+ {"J", &EmulateInstructionMIPS::Emulate_J, "J target"},
+ {"JAL", &EmulateInstructionMIPS::Emulate_JAL, "JAL target"},
+ {"JALX", &EmulateInstructionMIPS::Emulate_JAL, "JALX target"},
+ {"JALR", &EmulateInstructionMIPS::Emulate_JALR, "JALR target"},
+ {"JALR_HB", &EmulateInstructionMIPS::Emulate_JALR, "JALR.HB target"},
+ {"JIALC", &EmulateInstructionMIPS::Emulate_JIALC, "JIALC rt,offset"},
+ {"JIC", &EmulateInstructionMIPS::Emulate_JIC, "JIC rt,offset"},
+ {"JR", &EmulateInstructionMIPS::Emulate_JR, "JR target"},
+ {"JR_HB", &EmulateInstructionMIPS::Emulate_JR, "JR.HB target"},
+ {"BC1F", &EmulateInstructionMIPS::Emulate_FP_branch, "BC1F cc, offset"},
+ {"BC1T", &EmulateInstructionMIPS::Emulate_FP_branch, "BC1T cc, offset"},
+ {"BC1FL", &EmulateInstructionMIPS::Emulate_FP_branch, "BC1FL cc, offset"},
+ {"BC1TL", &EmulateInstructionMIPS::Emulate_FP_branch, "BC1TL cc, offset"},
+ {"BC1EQZ", &EmulateInstructionMIPS::Emulate_BC1EQZ, "BC1EQZ ft, offset"},
+ {"BC1NEZ", &EmulateInstructionMIPS::Emulate_BC1NEZ, "BC1NEZ ft, offset"},
+ {"BC1ANY2F", &EmulateInstructionMIPS::Emulate_3D_branch,
+ "BC1ANY2F cc, offset"},
+ {"BC1ANY2T", &EmulateInstructionMIPS::Emulate_3D_branch,
+ "BC1ANY2T cc, offset"},
+ {"BC1ANY4F", &EmulateInstructionMIPS::Emulate_3D_branch,
+ "BC1ANY4F cc, offset"},
+ {"BC1ANY4T", &EmulateInstructionMIPS::Emulate_3D_branch,
+ "BC1ANY4T cc, offset"},
+ {"BNZ_B", &EmulateInstructionMIPS::Emulate_BNZB, "BNZ.b wt,s16"},
+ {"BNZ_H", &EmulateInstructionMIPS::Emulate_BNZH, "BNZ.h wt,s16"},
+ {"BNZ_W", &EmulateInstructionMIPS::Emulate_BNZW, "BNZ.w wt,s16"},
+ {"BNZ_D", &EmulateInstructionMIPS::Emulate_BNZD, "BNZ.d wt,s16"},
+ {"BZ_B", &EmulateInstructionMIPS::Emulate_BZB, "BZ.b wt,s16"},
+ {"BZ_H", &EmulateInstructionMIPS::Emulate_BZH, "BZ.h wt,s16"},
+ {"BZ_W", &EmulateInstructionMIPS::Emulate_BZW, "BZ.w wt,s16"},
+ {"BZ_D", &EmulateInstructionMIPS::Emulate_BZD, "BZ.d wt,s16"},
+ {"BNZ_V", &EmulateInstructionMIPS::Emulate_BNZV, "BNZ.V wt,s16"},
+ {"BZ_V", &EmulateInstructionMIPS::Emulate_BZV, "BZ.V wt,s16"},
+
+ // MicroMIPS Branch instructions
+ {"B16_MM", &EmulateInstructionMIPS::Emulate_B16_MM, "B16 offset"},
+ {"BEQZ16_MM", &EmulateInstructionMIPS::Emulate_Branch_MM,
+ "BEQZ16 rs, offset"},
+ {"BNEZ16_MM", &EmulateInstructionMIPS::Emulate_Branch_MM,
+ "BNEZ16 rs, offset"},
+ {"BEQZC_MM", &EmulateInstructionMIPS::Emulate_Branch_MM,
+ "BEQZC rs, offset"},
+ {"BNEZC_MM", &EmulateInstructionMIPS::Emulate_Branch_MM,
+ "BNEZC rs, offset"},
+ {"BGEZALS_MM", &EmulateInstructionMIPS::Emulate_Branch_MM,
+ "BGEZALS rs, offset"},
+ {"BLTZALS_MM", &EmulateInstructionMIPS::Emulate_Branch_MM,
+ "BLTZALS rs, offset"},
+ {"JALR16_MM", &EmulateInstructionMIPS::Emulate_JALRx16_MM, "JALR16 rs"},
+ {"JALRS16_MM", &EmulateInstructionMIPS::Emulate_JALRx16_MM, "JALRS16 rs"},
+ {"JR16_MM", &EmulateInstructionMIPS::Emulate_JR, "JR16 rs rs"},
+ {"JRC16_MM", &EmulateInstructionMIPS::Emulate_JR, "JRC16 rs rs"},
+ {"JALS_MM", &EmulateInstructionMIPS::Emulate_JALx, "JALS target"},
+ {"JALX_MM", &EmulateInstructionMIPS::Emulate_JALx, "JALX target"},
+ {"JALRS_MM", &EmulateInstructionMIPS::Emulate_JALRS, "JALRS rt, rs"},
+ };
+
+ static const size_t k_num_mips_opcodes = llvm::array_lengthof(g_opcodes);
+
+ for (size_t i = 0; i < k_num_mips_opcodes; ++i) {
+ if (!strcasecmp(g_opcodes[i].op_name, op_name))
+ return &g_opcodes[i];
+ }
+
+ return nullptr;
+}
+
+uint32_t
+EmulateInstructionMIPS::GetSizeOfInstruction(lldb_private::DataExtractor &data,
+ uint64_t inst_addr) {
+ uint64_t next_inst_size = 0;
+ llvm::MCInst mc_insn;
+ llvm::MCDisassembler::DecodeStatus decode_status;
+ llvm::ArrayRef<uint8_t> raw_insn(data.GetDataStart(), data.GetByteSize());
+
+ if (m_use_alt_disaasm)
+ decode_status =
+ m_alt_disasm->getInstruction(mc_insn, next_inst_size, raw_insn,
+ inst_addr, llvm::nulls(), llvm::nulls());
+ else
+ decode_status =
+ m_disasm->getInstruction(mc_insn, next_inst_size, raw_insn, inst_addr,
+ llvm::nulls(), llvm::nulls());
+
+ if (decode_status != llvm::MCDisassembler::Success)
+ return false;
+
+ return m_insn_info->get(mc_insn.getOpcode()).getSize();
+}
+
+bool EmulateInstructionMIPS::SetInstruction(const Opcode &insn_opcode,
+ const Address &inst_addr,
+ Target *target) {
+ m_use_alt_disaasm = false;
+
+ if (EmulateInstruction::SetInstruction(insn_opcode, inst_addr, target)) {
+ if (inst_addr.GetAddressClass() == AddressClass::eCodeAlternateISA) {
+ Status error;
+ lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
+
+ /*
+ * The address belongs to microMIPS function. To find the size of
+ * next instruction use microMIPS disassembler.
+ */
+ m_use_alt_disaasm = true;
+
+ uint32_t current_inst_size = insn_opcode.GetByteSize();
+ uint8_t buf[sizeof(uint32_t)];
+ uint64_t next_inst_addr = (m_addr & (~1ull)) + current_inst_size;
+ Address next_addr(next_inst_addr);
+
+ const size_t bytes_read =
+ target->ReadMemory(next_addr, /* Address of next instruction */
+ true, /* prefer_file_cache */
+ buf, sizeof(uint32_t), error, &load_addr);
+
+ if (bytes_read == 0)
+ return true;
+
+ DataExtractor data(buf, sizeof(uint32_t), GetByteOrder(),
+ GetAddressByteSize());
+ m_next_inst_size = GetSizeOfInstruction(data, next_inst_addr);
+ return true;
+ } else {
+ /*
+ * If the address class is not AddressClass::eCodeAlternateISA then
+ * the function is not microMIPS. In this case instruction size is
+ * always 4 bytes.
+ */
+ m_next_inst_size = 4;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool EmulateInstructionMIPS::ReadInstruction() {
+ bool success = false;
+ m_addr = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC,
+ LLDB_INVALID_ADDRESS, &success);
+ if (success) {
+ Context read_inst_context;
+ read_inst_context.type = eContextReadOpcode;
+ read_inst_context.SetNoArgs();
+ m_opcode.SetOpcode32(
+ ReadMemoryUnsigned(read_inst_context, m_addr, 4, 0, &success),
+ GetByteOrder());
+ }
+ if (!success)
+ m_addr = LLDB_INVALID_ADDRESS;
+ return success;
+}
+
+bool EmulateInstructionMIPS::EvaluateInstruction(uint32_t evaluate_options) {
+ bool success = false;
+ llvm::MCInst mc_insn;
+ uint64_t insn_size;
+ DataExtractor data;
+
+ /* Keep the complexity of the decode logic with the llvm::MCDisassembler
+ * class. */
+ if (m_opcode.GetData(data)) {
+ llvm::MCDisassembler::DecodeStatus decode_status;
+ llvm::ArrayRef<uint8_t> raw_insn(data.GetDataStart(), data.GetByteSize());
+ if (m_use_alt_disaasm)
+ decode_status = m_alt_disasm->getInstruction(
+ mc_insn, insn_size, raw_insn, m_addr, llvm::nulls(), llvm::nulls());
+ else
+ decode_status = m_disasm->getInstruction(
+ mc_insn, insn_size, raw_insn, m_addr, llvm::nulls(), llvm::nulls());
+
+ if (decode_status != llvm::MCDisassembler::Success)
+ return false;
+ }
+
+ /*
+ * mc_insn.getOpcode() returns decoded opcode. However to make use
+ * of llvm::Mips::<insn> we would need "MipsGenInstrInfo.inc".
+ */
+ const char *op_name = m_insn_info->getName(mc_insn.getOpcode()).data();
+
+ if (op_name == nullptr)
+ return false;
+
+ /*
+ * Decoding has been done already. Just get the call-back function
+ * and emulate the instruction.
+ */
+ MipsOpcode *opcode_data = GetOpcodeForInstruction(op_name);
+
+ if (opcode_data == nullptr)
+ return false;
+
+ uint64_t old_pc = 0, new_pc = 0;
+ const bool auto_advance_pc =
+ evaluate_options & eEmulateInstructionOptionAutoAdvancePC;
+
+ if (auto_advance_pc) {
+ old_pc =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+ }
+
+ /* emulate instruction */
+ success = (this->*opcode_data->callback)(mc_insn);
+ if (!success)
+ return false;
+
+ if (auto_advance_pc) {
+ new_pc =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ /* If we haven't changed the PC, change it here */
+ if (old_pc == new_pc) {
+ new_pc += 4;
+ Context context;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ new_pc))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::CreateFunctionEntryUnwind(
+ UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ const bool can_replace = false;
+
+ // Our previous Call Frame Address is the stack pointer
+ row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_sp_mips, 0);
+
+ // Our previous PC is in the RA
+ row->SetRegisterLocationToRegister(dwarf_pc_mips, dwarf_ra_mips, can_replace);
+
+ unwind_plan.AppendRow(row);
+
+ // All other registers are the same.
+ unwind_plan.SetSourceName("EmulateInstructionMIPS");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);
+ unwind_plan.SetReturnAddressRegister(dwarf_ra_mips);
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::nonvolatile_reg_p(uint32_t regnum) {
+ switch (regnum) {
+ case dwarf_r16_mips:
+ case dwarf_r17_mips:
+ case dwarf_r18_mips:
+ case dwarf_r19_mips:
+ case dwarf_r20_mips:
+ case dwarf_r21_mips:
+ case dwarf_r22_mips:
+ case dwarf_r23_mips:
+ case dwarf_gp_mips:
+ case dwarf_sp_mips:
+ case dwarf_r30_mips:
+ case dwarf_ra_mips:
+ return true;
+ default:
+ return false;
+ }
+ return false;
+}
+
+bool EmulateInstructionMIPS::Emulate_ADDiu(llvm::MCInst &insn) {
+ // ADDIU rt, rs, immediate
+ // GPR[rt] <- GPR[rs] + sign_extend(immediate)
+
+ uint8_t dst, src;
+ bool success = false;
+ const uint32_t imm16 = insn.getOperand(2).getImm();
+ int64_t imm = SignedBits(imm16, 15, 0);
+
+ dst = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ src = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+
+ // If immediate value is greater then 2^16 - 1 then clang generate LUI,
+ // ADDIU, SUBU instructions in prolog. Example lui $1, 0x2 addiu $1, $1,
+ // -0x5920 subu $sp, $sp, $1 In this case, ADDIU dst and src will be same
+ // and not equal to sp
+ if (dst == src) {
+ Context context;
+
+ /* read <src> register */
+ const int64_t src_opd_val = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips + src, 0, &success);
+ if (!success)
+ return false;
+
+ /* Check if this is daddiu sp, sp, imm16 */
+ if (dst == dwarf_sp_mips) {
+ uint64_t result = src_opd_val + imm;
+ RegisterInfo reg_info_sp;
+
+ if (GetRegisterInfo(eRegisterKindDWARF, dwarf_sp_mips, reg_info_sp))
+ context.SetRegisterPlusOffset(reg_info_sp, imm);
+
+ /* We are allocating bytes on stack */
+ context.type = eContextAdjustStackPointer;
+
+ WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_sp_mips, result);
+ return true;
+ }
+
+ imm += src_opd_val;
+ context.SetImmediateSigned(imm);
+ context.type = eContextImmediate;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF,
+ dwarf_zero_mips + dst, imm))
+ return false;
+ }
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_SW(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t imm16 = insn.getOperand(2).getImm();
+ uint32_t imm = SignedBits(imm16, 15, 0);
+ uint32_t src, base;
+ int32_t address;
+ Context bad_vaddr_context;
+
+ RegisterInfo reg_info_base;
+
+ src = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ base = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base,
+ reg_info_base))
+ return false;
+
+ /* read base register */
+ address = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + base, 0, &success);
+ if (!success)
+ return false;
+
+ /* destination address */
+ address = address + imm;
+
+ /* Set the bad_vaddr register with base address used in the instruction */
+ bad_vaddr_context.type = eContextInvalid;
+ WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips,
+ address);
+
+ /* We look for sp based non-volatile register stores */
+ if (nonvolatile_reg_p(src)) {
+
+ RegisterInfo reg_info_src;
+
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + src,
+ reg_info_src))
+ return false;
+
+ Context context;
+ RegisterValue data_src;
+ context.type = eContextPushRegisterOnStack;
+ context.SetRegisterToRegisterPlusOffset(reg_info_src, reg_info_base, 0);
+
+ uint8_t buffer[RegisterValue::kMaxRegisterByteSize];
+ Status error;
+
+ if (!ReadRegister(&reg_info_base, data_src))
+ return false;
+
+ if (data_src.GetAsMemoryData(&reg_info_src, buffer, reg_info_src.byte_size,
+ eByteOrderLittle, error) == 0)
+ return false;
+
+ if (!WriteMemory(context, address, buffer, reg_info_src.byte_size))
+ return false;
+
+ return true;
+ }
+
+ return false;
+}
+
+bool EmulateInstructionMIPS::Emulate_LW(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t src, base;
+ int32_t imm, address;
+ Context bad_vaddr_context;
+
+ src = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ base = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+ imm = insn.getOperand(2).getImm();
+
+ RegisterInfo reg_info_base;
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base,
+ reg_info_base))
+ return false;
+
+ /* read base register */
+ address = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + base, 0, &success);
+ if (!success)
+ return false;
+
+ /* destination address */
+ address = address + imm;
+
+ /* Set the bad_vaddr register with base address used in the instruction */
+ bad_vaddr_context.type = eContextInvalid;
+ WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips,
+ address);
+
+ if (nonvolatile_reg_p(src)) {
+ RegisterValue data_src;
+ RegisterInfo reg_info_src;
+
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + src,
+ reg_info_src))
+ return false;
+
+ Context context;
+ context.type = eContextPopRegisterOffStack;
+ context.SetAddress(address);
+
+ return WriteRegister(context, &reg_info_src, data_src);
+ }
+
+ return false;
+}
+
+bool EmulateInstructionMIPS::Emulate_SUBU_ADDU(llvm::MCInst &insn) {
+ // SUBU sp, <src>, <rt>
+ // ADDU sp, <src>, <rt>
+ // ADDU dst, sp, <rt>
+
+ bool success = false;
+ uint64_t result;
+ uint8_t src, dst, rt;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ dst = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ src = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+
+ /* Check if sp is destination register */
+ if (dst == dwarf_sp_mips) {
+ rt = m_reg_info->getEncodingValue(insn.getOperand(2).getReg());
+
+ /* read <src> register */
+ uint64_t src_opd_val = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips + src, 0, &success);
+ if (!success)
+ return false;
+
+ /* read <rt > register */
+ uint64_t rt_opd_val = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "SUBU"))
+ result = src_opd_val - rt_opd_val;
+ else
+ result = src_opd_val + rt_opd_val;
+
+ Context context;
+ RegisterInfo reg_info_sp;
+ if (GetRegisterInfo(eRegisterKindDWARF, dwarf_sp_mips, reg_info_sp))
+ context.SetRegisterPlusOffset(reg_info_sp, rt_opd_val);
+
+ /* We are allocating bytes on stack */
+ context.type = eContextAdjustStackPointer;
+
+ WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_sp_mips, result);
+
+ return true;
+ } else if (src == dwarf_sp_mips) {
+ rt = m_reg_info->getEncodingValue(insn.getOperand(2).getReg());
+
+ /* read <src> register */
+ uint64_t src_opd_val = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips + src, 0, &success);
+ if (!success)
+ return false;
+
+ /* read <rt> register */
+ uint64_t rt_opd_val = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips + rt, 0, &success);
+ if (!success)
+ return false;
+
+ Context context;
+
+ if (!strcasecmp(op_name, "SUBU"))
+ result = src_opd_val - rt_opd_val;
+ else
+ result = src_opd_val + rt_opd_val;
+
+ context.SetImmediateSigned(result);
+ context.type = eContextImmediate;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF,
+ dwarf_zero_mips + dst, result))
+ return false;
+ }
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_LUI(llvm::MCInst &insn) {
+ // LUI rt, immediate
+ // GPR[rt] <- sign_extend(immediate << 16)
+
+ const uint32_t imm32 = insn.getOperand(1).getImm() << 16;
+ int64_t imm = SignedBits(imm32, 31, 0);
+ uint8_t rt;
+ Context context;
+
+ rt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ context.SetImmediateSigned(imm);
+ context.type = eContextImmediate;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF,
+ dwarf_zero_mips + rt, imm);
+}
+
+bool EmulateInstructionMIPS::Emulate_ADDIUSP(llvm::MCInst &insn) {
+ bool success = false;
+ const uint32_t imm9 = insn.getOperand(0).getImm();
+ uint64_t result;
+
+ // This instruction operates implicitly on stack pointer, so read <sp>
+ // register.
+ uint64_t src_opd_val =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_sp_mips, 0, &success);
+ if (!success)
+ return false;
+
+ result = src_opd_val + imm9;
+
+ Context context;
+ RegisterInfo reg_info_sp;
+ if (GetRegisterInfo(eRegisterKindDWARF, dwarf_sp_mips, reg_info_sp))
+ context.SetRegisterPlusOffset(reg_info_sp, imm9);
+
+ // We are adjusting the stack.
+ context.type = eContextAdjustStackPointer;
+
+ WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_sp_mips, result);
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_ADDIUS5(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t base;
+ const uint32_t imm4 = insn.getOperand(2).getImm();
+ uint64_t result;
+
+ // The source and destination register is same for this instruction.
+ base = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+
+ // We are looking for stack adjustment only
+ if (base == dwarf_sp_mips) {
+ // Read stack pointer register
+ uint64_t src_opd_val = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips + base, 0, &success);
+ if (!success)
+ return false;
+
+ result = src_opd_val + imm4;
+
+ Context context;
+ RegisterInfo reg_info_sp;
+ if (GetRegisterInfo(eRegisterKindDWARF, dwarf_sp_mips, reg_info_sp))
+ context.SetRegisterPlusOffset(reg_info_sp, imm4);
+
+ // We are adjusting the stack.
+ context.type = eContextAdjustStackPointer;
+
+ WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_sp_mips, result);
+ }
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_SWSP(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t imm5 = insn.getOperand(2).getImm();
+ uint32_t src, base;
+ Context bad_vaddr_context;
+ uint32_t address;
+
+ src = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ base = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+
+ RegisterInfo reg_info_base;
+
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base,
+ reg_info_base))
+ return false;
+
+ // read base register
+ address = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips + base, 0,
+ &success);
+ if (!success)
+ return false;
+
+ // destination address
+ address = address + imm5;
+
+ // We use bad_vaddr_context to store base address which is used by H/W
+ // watchpoint Set the bad_vaddr register with base address used in the
+ // instruction
+ bad_vaddr_context.type = eContextInvalid;
+ WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips,
+ address);
+
+ // We look for sp based non-volatile register stores.
+ if (base == dwarf_sp_mips && nonvolatile_reg_p(src)) {
+ RegisterInfo reg_info_src = {};
+ Context context;
+ RegisterValue data_src;
+ context.type = eContextPushRegisterOnStack;
+ context.SetRegisterToRegisterPlusOffset(reg_info_src, reg_info_base, 0);
+
+ uint8_t buffer[RegisterValue::kMaxRegisterByteSize];
+ Status error;
+
+ if (!ReadRegister(&reg_info_base, data_src))
+ return false;
+
+ if (data_src.GetAsMemoryData(&reg_info_src, buffer, reg_info_src.byte_size,
+ eByteOrderLittle, error) == 0)
+ return false;
+
+ if (!WriteMemory(context, address, buffer, reg_info_src.byte_size))
+ return false;
+
+ return true;
+ }
+
+ return false;
+}
+
+/* Emulate SWM16,SWM32 and SWP instruction.
+
+ SWM16 always has stack pointer as a base register (but it is still available
+ in MCInst as an operand).
+ SWM32 and SWP can have base register other than stack pointer.
+*/
+bool EmulateInstructionMIPS::Emulate_SWM16_32(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t src, base;
+ uint32_t num_operands = insn.getNumOperands(); // No of operands vary based on
+ // no of regs to store.
+
+ // Base register is second last operand of the instruction.
+ base =
+ m_reg_info->getEncodingValue(insn.getOperand(num_operands - 2).getReg());
+
+ // We are looking for sp based stores so if base is not a stack pointer then
+ // don't proceed.
+ if (base != dwarf_sp_mips)
+ return false;
+
+ // offset is always the last operand.
+ uint32_t offset = insn.getOperand(num_operands - 1).getImm();
+
+ RegisterInfo reg_info_base;
+ RegisterInfo reg_info_src;
+
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base,
+ reg_info_base))
+ return false;
+
+ // read SP
+ uint32_t base_address = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips + base, 0, &success);
+ if (!success)
+ return false;
+
+ // Resulting base addrss
+ base_address = base_address + offset;
+
+ // Total no of registers to be stored are num_operands-2.
+ for (uint32_t i = 0; i < num_operands - 2; i++) {
+ // Get the register number to be stored.
+ src = m_reg_info->getEncodingValue(insn.getOperand(i).getReg());
+
+ /*
+ Record only non-volatile stores.
+ This check is required for SWP instruction because source operand could
+ be any register.
+ SWM16 and SWM32 instruction always has saved registers as source
+ operands.
+ */
+ if (!nonvolatile_reg_p(src))
+ return false;
+
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + src,
+ reg_info_src))
+ return false;
+
+ Context context;
+ RegisterValue data_src;
+ context.type = eContextPushRegisterOnStack;
+ context.SetRegisterToRegisterPlusOffset(reg_info_src, reg_info_base, 0);
+
+ uint8_t buffer[RegisterValue::kMaxRegisterByteSize];
+ Status error;
+
+ if (!ReadRegister(&reg_info_base, data_src))
+ return false;
+
+ if (data_src.GetAsMemoryData(&reg_info_src, buffer, reg_info_src.byte_size,
+ eByteOrderLittle, error) == 0)
+ return false;
+
+ if (!WriteMemory(context, base_address, buffer, reg_info_src.byte_size))
+ return false;
+
+ // Stack address for next register
+ base_address = base_address + reg_info_src.byte_size;
+ }
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_LWSP(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t src = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ uint32_t base = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+ uint32_t imm5 = insn.getOperand(2).getImm();
+ Context bad_vaddr_context;
+
+ RegisterInfo reg_info_base;
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base,
+ reg_info_base))
+ return false;
+
+ // read base register
+ uint32_t base_address = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips + base, 0, &success);
+ if (!success)
+ return false;
+
+ base_address = base_address + imm5;
+
+ // We use bad_vaddr_context to store base address which is used by H/W
+ // watchpoint Set the bad_vaddr register with base address used in the
+ // instruction
+ bad_vaddr_context.type = eContextInvalid;
+ WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips,
+ base_address);
+
+ if (base == dwarf_sp_mips && nonvolatile_reg_p(src)) {
+ RegisterValue data_src;
+ RegisterInfo reg_info_src;
+
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + src,
+ reg_info_src))
+ return false;
+
+ Context context;
+ context.type = eContextPopRegisterOffStack;
+ context.SetAddress(base_address);
+
+ return WriteRegister(context, &reg_info_src, data_src);
+ }
+
+ return false;
+}
+
+/* Emulate LWM16, LWM32 and LWP instructions.
+
+ LWM16 always has stack pointer as a base register (but it is still available
+ in MCInst as an operand).
+ LWM32 and LWP can have base register other than stack pointer.
+*/
+bool EmulateInstructionMIPS::Emulate_LWM16_32(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t dst, base;
+ uint32_t num_operands = insn.getNumOperands(); // No of operands vary based on
+ // no of regs to store.
+ uint32_t imm = insn.getOperand(num_operands - 1)
+ .getImm(); // imm is the last operand in the instruction.
+
+ // Base register is second last operand of the instruction.
+ base =
+ m_reg_info->getEncodingValue(insn.getOperand(num_operands - 2).getReg());
+
+ // We are looking for sp based loads so if base is not a stack pointer then
+ // don't proceed.
+ if (base != dwarf_sp_mips)
+ return false;
+
+ uint32_t base_address = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips + base, 0, &success);
+ if (!success)
+ return false;
+
+ base_address = base_address + imm;
+
+ RegisterValue data_dst;
+ RegisterInfo reg_info_dst;
+
+ // Total no of registers to be re-stored are num_operands-2.
+ for (uint32_t i = 0; i < num_operands - 2; i++) {
+ // Get the register number to be re-stored.
+ dst = m_reg_info->getEncodingValue(insn.getOperand(i).getReg());
+
+ /*
+ Record only non-volatile loads.
+ This check is required for LWP instruction because destination operand
+ could be any register.
+ LWM16 and LWM32 instruction always has saved registers as destination
+ operands.
+ */
+ if (!nonvolatile_reg_p(dst))
+ return false;
+
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + dst,
+ reg_info_dst))
+ return false;
+
+ Context context;
+ context.type = eContextPopRegisterOffStack;
+ context.SetAddress(base_address + (i * 4));
+
+ if (!WriteRegister(context, &reg_info_dst, data_dst))
+ return false;
+ }
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_JRADDIUSP(llvm::MCInst &insn) {
+ bool success = false;
+ int32_t imm5 = insn.getOperand(0).getImm();
+
+ /* JRADDIUSP immediate
+ * PC <- RA
+ * SP <- SP + zero_extend(Immediate << 2)
+ */
+
+ // This instruction operates implicitly on stack pointer, so read <sp>
+ // register.
+ int32_t src_opd_val =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_sp_mips, 0, &success);
+ if (!success)
+ return false;
+
+ int32_t ra_val =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_ra_mips, 0, &success);
+ if (!success)
+ return false;
+
+ int32_t result = src_opd_val + imm5;
+
+ Context context;
+
+ // Update the PC
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ ra_val))
+ return false;
+
+ RegisterInfo reg_info_sp;
+ if (GetRegisterInfo(eRegisterKindDWARF, dwarf_sp_mips, reg_info_sp))
+ context.SetRegisterPlusOffset(reg_info_sp, imm5);
+
+ // We are adjusting stack
+ context.type = eContextAdjustStackPointer;
+
+ // update SP
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_sp_mips,
+ result);
+}
+
+static int IsAdd64bitOverflow(int32_t a, int32_t b) {
+ int32_t r = (uint32_t)a + (uint32_t)b;
+ return (a < 0 && b < 0 && r >= 0) || (a >= 0 && b >= 0 && r < 0);
+}
+
+/*
+ Emulate below MIPS branch instructions.
+ BEQ, BNE : Branch on condition
+ BEQL, BNEL : Branch likely
+*/
+bool EmulateInstructionMIPS::Emulate_BXX_3ops(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs, rt;
+ int32_t offset, pc, target = 0, rs_val, rt_val;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ rt = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+ offset = insn.getOperand(2).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + rs, 0, &success);
+ if (!success)
+ return false;
+
+ rt_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + rt, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BEQ") || !strcasecmp(op_name, "BEQL")) {
+ if (rs_val == rt_val)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BNE") || !strcasecmp(op_name, "BNEL")) {
+ if (rs_val != rt_val)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ }
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+ context.SetImmediate(offset);
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+/*
+ Emulate below MIPS branch instructions.
+ BEQC, BNEC, BLTC, BGEC, BLTUC, BGEUC, BOVC, BNVC: Compact branch
+ instructions with no delay slot
+*/
+bool EmulateInstructionMIPS::Emulate_BXX_3ops_C(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs, rt;
+ int32_t offset, pc, target = 0, rs_val, rt_val;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+ uint32_t current_inst_size = m_insn_info->get(insn.getOpcode()).getSize();
+
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ rt = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+ offset = insn.getOperand(2).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + rs, 0, &success);
+ if (!success)
+ return false;
+
+ rt_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + rt, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BEQC")) {
+ if (rs_val == rt_val)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BNEC")) {
+ if (rs_val != rt_val)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BLTC")) {
+ if (rs_val < rt_val)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BGEC")) {
+ if (rs_val >= rt_val)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BLTUC")) {
+ if (rs_val < rt_val)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BGEUC")) {
+ if ((uint32_t)rs_val >= (uint32_t)rt_val)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BOVC")) {
+ if (IsAdd64bitOverflow(rs_val, rt_val))
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BNVC")) {
+ if (!IsAdd64bitOverflow(rs_val, rt_val))
+ target = pc + offset;
+ else
+ target = pc + 4;
+ }
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+ context.SetImmediate(current_inst_size + offset);
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+/*
+ Emulate below MIPS conditional branch and link instructions.
+ BLEZALC, BGEZALC, BLTZALC, BGTZALC, BEQZALC, BNEZALC : Compact branches
+*/
+bool EmulateInstructionMIPS::Emulate_Bcond_Link_C(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs;
+ int32_t offset, pc, target = 0;
+ int32_t rs_val;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + rs, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BLEZALC")) {
+ if (rs_val <= 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BGEZALC")) {
+ if (rs_val >= 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BLTZALC")) {
+ if (rs_val < 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BGTZALC")) {
+ if (rs_val > 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BEQZALC")) {
+ if (rs_val == 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BNEZALC")) {
+ if (rs_val != 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ }
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips,
+ pc + 4))
+ return false;
+
+ return true;
+}
+
+/*
+ Emulate below MIPS Non-Compact conditional branch and link instructions.
+ BLTZAL, BGEZAL :
+ BLTZALL, BGEZALL : Branch likely
+*/
+bool EmulateInstructionMIPS::Emulate_Bcond_Link(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs;
+ int32_t offset, pc, target = 0;
+ int32_t rs_val;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + rs, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BLTZAL") || !strcasecmp(op_name, "BLTZALL")) {
+ if ((int32_t)rs_val < 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BGEZAL") ||
+ !strcasecmp(op_name, "BGEZALL")) {
+ if ((int32_t)rs_val >= 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ }
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips,
+ pc + 8))
+ return false;
+
+ return true;
+}
+
+/*
+ Emulate below MIPS branch instructions.
+ BLTZL, BGEZL, BGTZL, BLEZL : Branch likely
+ BLTZ, BGEZ, BGTZ, BLEZ : Non-compact branches
+*/
+bool EmulateInstructionMIPS::Emulate_BXX_2ops(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs;
+ int32_t offset, pc, target = 0;
+ int32_t rs_val;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + rs, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BLTZL") || !strcasecmp(op_name, "BLTZ")) {
+ if (rs_val < 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BGEZL") || !strcasecmp(op_name, "BGEZ")) {
+ if (rs_val >= 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BGTZL") || !strcasecmp(op_name, "BGTZ")) {
+ if (rs_val > 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BLEZL") || !strcasecmp(op_name, "BLEZ")) {
+ if (rs_val <= 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ }
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+ context.SetImmediate(offset);
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+/*
+ Emulate below MIPS branch instructions.
+ BLTZC, BLEZC, BGEZC, BGTZC, BEQZC, BNEZC : Compact Branches
+*/
+bool EmulateInstructionMIPS::Emulate_BXX_2ops_C(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs;
+ int32_t offset, pc, target = 0;
+ int32_t rs_val;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+ uint32_t current_inst_size = m_insn_info->get(insn.getOpcode()).getSize();
+
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + rs, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BLTZC")) {
+ if (rs_val < 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BLEZC")) {
+ if (rs_val <= 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BGEZC")) {
+ if (rs_val >= 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BGTZC")) {
+ if (rs_val > 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BEQZC")) {
+ if (rs_val == 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BNEZC")) {
+ if (rs_val != 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ }
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+ context.SetImmediate(current_inst_size + offset);
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+bool EmulateInstructionMIPS::Emulate_B16_MM(llvm::MCInst &insn) {
+ bool success = false;
+ int32_t offset, pc, target;
+ uint32_t current_inst_size = m_insn_info->get(insn.getOpcode()).getSize();
+
+ offset = insn.getOperand(0).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ // unconditional branch
+ target = pc + offset;
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+ context.SetImmediate(current_inst_size + offset);
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+/*
+ BEQZC, BNEZC are 32 bit compact instructions without a delay slot.
+ BEQZ16, BNEZ16 are 16 bit instructions with delay slot.
+ BGEZALS, BLTZALS are 16 bit instructions with short (2-byte) delay slot.
+*/
+bool EmulateInstructionMIPS::Emulate_Branch_MM(llvm::MCInst &insn) {
+ bool success = false;
+ int32_t target = 0;
+ uint32_t current_inst_size = m_insn_info->get(insn.getOpcode()).getSize();
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+ bool update_ra = false;
+ uint32_t ra_offset = 0;
+
+ /*
+ * BEQZ16 rs, offset
+ * condition <- (GPR[rs] = 0)
+ * if condition then
+ * PC = PC + sign_ext (offset || 0)
+ *
+ * BNEZ16 rs, offset
+ * condition <- (GPR[rs] != 0)
+ * if condition then
+ * PC = PC + sign_ext (offset || 0)
+ *
+ * BEQZC rs, offset (compact instruction: No delay slot)
+ * condition <- (GPR[rs] == 0)
+ * if condition then
+ * PC = PC + 4 + sign_ext (offset || 0)
+ */
+
+ uint32_t rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ int32_t offset = insn.getOperand(1).getImm();
+
+ int32_t pc =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ int32_t rs_val = (int32_t)ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips + rs, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BEQZ16_MM")) {
+ if (rs_val == 0)
+ target = pc + offset;
+ else
+ target = pc + current_inst_size +
+ m_next_inst_size; // Skip delay slot instruction.
+ } else if (!strcasecmp(op_name, "BNEZ16_MM")) {
+ if (rs_val != 0)
+ target = pc + offset;
+ else
+ target = pc + current_inst_size +
+ m_next_inst_size; // Skip delay slot instruction.
+ } else if (!strcasecmp(op_name, "BEQZC_MM")) {
+ if (rs_val == 0)
+ target = pc + 4 + offset;
+ else
+ target =
+ pc +
+ 4; // 32 bit instruction and does not have delay slot instruction.
+ } else if (!strcasecmp(op_name, "BNEZC_MM")) {
+ if (rs_val != 0)
+ target = pc + 4 + offset;
+ else
+ target =
+ pc +
+ 4; // 32 bit instruction and does not have delay slot instruction.
+ } else if (!strcasecmp(op_name, "BGEZALS_MM")) {
+ if (rs_val >= 0)
+ target = pc + offset;
+ else
+ target = pc + 6; // 32 bit instruction with short (2-byte) delay slot
+
+ update_ra = true;
+ ra_offset = 6;
+ } else if (!strcasecmp(op_name, "BLTZALS_MM")) {
+ if (rs_val >= 0)
+ target = pc + offset;
+ else
+ target = pc + 6; // 32 bit instruction with short (2-byte) delay slot
+
+ update_ra = true;
+ ra_offset = 6;
+ }
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+ context.SetImmediate(current_inst_size + offset);
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target))
+ return false;
+
+ if (update_ra) {
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips,
+ pc + ra_offset))
+ return false;
+ }
+ return true;
+}
+
+/* Emulate micromips jump instructions.
+ JALR16,JALRS16
+*/
+bool EmulateInstructionMIPS::Emulate_JALRx16_MM(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t ra_offset = 0;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ uint32_t rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+
+ uint32_t pc =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ uint32_t rs_val = ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + rs, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "JALR16_MM"))
+ ra_offset = 6; // 2-byte instruction with 4-byte delay slot.
+ else if (!strcasecmp(op_name, "JALRS16_MM"))
+ ra_offset = 4; // 2-byte instruction with 2-byte delay slot.
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ rs_val))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips,
+ pc + ra_offset))
+ return false;
+
+ return true;
+}
+
+/* Emulate JALS and JALX instructions.
+ JALS 32 bit instruction with short (2-byte) delay slot.
+ JALX 32 bit instruction with 4-byte delay slot.
+*/
+bool EmulateInstructionMIPS::Emulate_JALx(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t offset = 0, target = 0, pc = 0, ra_offset = 0;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ /*
+ * JALS target
+ * RA = PC + 6
+ * offset = sign_ext (offset << 1)
+ * PC = PC[31-27] | offset
+ * JALX target
+ * RA = PC + 8
+ * offset = sign_ext (offset << 2)
+ * PC = PC[31-28] | offset
+ */
+ offset = insn.getOperand(0).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ // These are PC-region branches and not PC-relative.
+ if (!strcasecmp(op_name, "JALS_MM")) {
+ // target address is in the “current” 128 MB-aligned region
+ target = (pc & 0xF8000000UL) | offset;
+ ra_offset = 6;
+ } else if (!strcasecmp(op_name, "JALX_MM")) {
+ // target address is in the “current” 256 MB-aligned region
+ target = (pc & 0xF0000000UL) | offset;
+ ra_offset = 8;
+ }
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips,
+ pc + ra_offset))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_JALRS(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs = 0, rt = 0;
+ int32_t pc = 0, rs_val = 0;
+
+ /*
+ JALRS rt, rs
+ GPR[rt] <- PC + 6
+ PC <- GPR[rs]
+ */
+
+ rt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ rs = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+
+ rs_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + rs, 0, &success);
+ if (!success)
+ return false;
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ rs_val))
+ return false;
+
+ // This is 4-byte instruction with 2-byte delay slot.
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_zero_mips + rt,
+ pc + 6))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_BAL(llvm::MCInst &insn) {
+ bool success = false;
+ int32_t offset, pc, target;
+
+ /*
+ * BAL offset
+ * offset = sign_ext (offset << 2)
+ * RA = PC + 8
+ * PC = PC + offset
+ */
+ offset = insn.getOperand(0).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ target = pc + offset;
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips,
+ pc + 8))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_BALC(llvm::MCInst &insn) {
+ bool success = false;
+ int32_t offset, pc, target;
+
+ /*
+ * BALC offset
+ * offset = sign_ext (offset << 2)
+ * RA = PC + 4
+ * PC = PC + 4 + offset
+ */
+ offset = insn.getOperand(0).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ target = pc + offset;
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips,
+ pc + 4))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_BC(llvm::MCInst &insn) {
+ bool success = false;
+ int32_t offset, pc, target;
+
+ /*
+ * BC offset
+ * offset = sign_ext (offset << 2)
+ * PC = PC + 4 + offset
+ */
+ offset = insn.getOperand(0).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ target = pc + offset;
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+bool EmulateInstructionMIPS::Emulate_J(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t offset, pc;
+
+ /*
+ * J offset
+ * offset = sign_ext (offset << 2)
+ * PC = PC[63-28] | offset
+ */
+ offset = insn.getOperand(0).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ /* This is a PC-region branch and not PC-relative */
+ pc = (pc & 0xF0000000UL) | offset;
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips, pc);
+}
+
+bool EmulateInstructionMIPS::Emulate_JAL(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t offset, target, pc;
+
+ /*
+ * JAL offset
+ * offset = sign_ext (offset << 2)
+ * PC = PC[63-28] | offset
+ */
+ offset = insn.getOperand(0).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ /* This is a PC-region branch and not PC-relative */
+ target = (pc & 0xF0000000UL) | offset;
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips,
+ pc + 8))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_JALR(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs, rt;
+ uint32_t pc, rs_val;
+
+ /*
+ * JALR rt, rs
+ * GPR[rt] = PC + 8
+ * PC = GPR[rs]
+ */
+ rt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ rs = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips + rs, 0,
+ &success);
+ if (!success)
+ return false;
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ rs_val))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_zero_mips + rt,
+ pc + 8))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_JIALC(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rt;
+ int32_t target, offset, pc, rt_val;
+
+ /*
+ * JIALC rt, offset
+ * offset = sign_ext (offset)
+ * PC = GPR[rt] + offset
+ * RA = PC + 4
+ */
+ rt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ rt_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + rt, 0, &success);
+ if (!success)
+ return false;
+
+ target = rt_val + offset;
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips,
+ pc + 4))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_JIC(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rt;
+ int32_t target, offset, rt_val;
+
+ /*
+ * JIC rt, offset
+ * offset = sign_ext (offset)
+ * PC = GPR[rt] + offset
+ */
+ rt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ rt_val = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + rt, 0, &success);
+ if (!success)
+ return false;
+
+ target = rt_val + offset;
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+bool EmulateInstructionMIPS::Emulate_JR(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs;
+ uint32_t rs_val;
+
+ /*
+ * JR rs
+ * PC = GPR[rs]
+ */
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+
+ rs_val = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips + rs, 0,
+ &success);
+ if (!success)
+ return false;
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ rs_val);
+}
+
+/*
+ Emulate Branch on FP True/False
+ BC1F, BC1FL : Branch on FP False (L stands for branch likely)
+ BC1T, BC1TL : Branch on FP True (L stands for branch likely)
+*/
+bool EmulateInstructionMIPS::Emulate_FP_branch(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t cc, fcsr;
+ int32_t pc, offset, target = 0;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ cc = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ fcsr = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_fcsr_mips, 0, &success);
+ if (!success)
+ return false;
+
+ /* fcsr[23], fcsr[25-31] are vaild condition bits */
+ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01);
+
+ if (!strcasecmp(op_name, "BC1F") || !strcasecmp(op_name, "BC1FL")) {
+ if ((fcsr & (1 << cc)) == 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BC1T") || !strcasecmp(op_name, "BC1TL")) {
+ if ((fcsr & (1 << cc)) != 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ }
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+bool EmulateInstructionMIPS::Emulate_BC1EQZ(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t ft;
+ uint32_t ft_val;
+ int32_t target, pc, offset;
+
+ /*
+ * BC1EQZ ft, offset
+ * condition <- (FPR[ft].bit0 == 0)
+ * if condition then
+ * offset = sign_ext (offset)
+ * PC = PC + 4 + offset
+ */
+ ft = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ ft_val = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips + ft, 0,
+ &success);
+ if (!success)
+ return false;
+
+ if ((ft_val & 1) == 0)
+ target = pc + 4 + offset;
+ else
+ target = pc + 8;
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+bool EmulateInstructionMIPS::Emulate_BC1NEZ(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t ft;
+ uint32_t ft_val;
+ int32_t target, pc, offset;
+
+ /*
+ * BC1NEZ ft, offset
+ * condition <- (FPR[ft].bit0 != 0)
+ * if condition then
+ * offset = sign_ext (offset)
+ * PC = PC + 4 + offset
+ */
+ ft = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ ft_val = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips + ft, 0,
+ &success);
+ if (!success)
+ return false;
+
+ if ((ft_val & 1) != 0)
+ target = pc + 4 + offset;
+ else
+ target = pc + 8;
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+/*
+ Emulate MIPS-3D Branch instructions
+ BC1ANY2F, BC1ANY2T : Branch on Any of Two Floating Point Condition Codes
+ False/True
+ BC1ANY4F, BC1ANY4T : Branch on Any of Four Floating Point Condition Codes
+ False/True
+*/
+bool EmulateInstructionMIPS::Emulate_3D_branch(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t cc, fcsr;
+ int32_t pc, offset, target = 0;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ cc = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ fcsr = (uint32_t)ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_fcsr_mips, 0,
+ &success);
+ if (!success)
+ return false;
+
+ /* fcsr[23], fcsr[25-31] are vaild condition bits */
+ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01);
+
+ if (!strcasecmp(op_name, "BC1ANY2F")) {
+ /* if any one bit is 0 */
+ if (((fcsr >> cc) & 3) != 3)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BC1ANY2T")) {
+ /* if any one bit is 1 */
+ if (((fcsr >> cc) & 3) != 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BC1ANY4F")) {
+ /* if any one bit is 0 */
+ if (((fcsr >> cc) & 0xf) != 0xf)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BC1ANY4T")) {
+ /* if any one bit is 1 */
+ if (((fcsr >> cc) & 0xf) != 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ }
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+bool EmulateInstructionMIPS::Emulate_BNZB(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 1, true);
+}
+
+bool EmulateInstructionMIPS::Emulate_BNZH(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 2, true);
+}
+
+bool EmulateInstructionMIPS::Emulate_BNZW(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 4, true);
+}
+
+bool EmulateInstructionMIPS::Emulate_BNZD(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 8, true);
+}
+
+bool EmulateInstructionMIPS::Emulate_BZB(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 1, false);
+}
+
+bool EmulateInstructionMIPS::Emulate_BZH(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 2, false);
+}
+
+bool EmulateInstructionMIPS::Emulate_BZW(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 4, false);
+}
+
+bool EmulateInstructionMIPS::Emulate_BZD(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 8, false);
+}
+
+bool EmulateInstructionMIPS::Emulate_MSA_Branch_DF(llvm::MCInst &insn,
+ int element_byte_size,
+ bool bnz) {
+ bool success = false, branch_hit = true;
+ int32_t target = 0;
+ RegisterValue reg_value;
+ const uint8_t *ptr = nullptr;
+
+ uint32_t wt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ int32_t offset = insn.getOperand(1).getImm();
+
+ int32_t pc =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ if (ReadRegister(eRegisterKindDWARF, dwarf_w0_mips + wt, reg_value))
+ ptr = (const uint8_t *)reg_value.GetBytes();
+ else
+ return false;
+
+ for (int i = 0; i < 16 / element_byte_size; i++) {
+ switch (element_byte_size) {
+ case 1:
+ if ((*ptr == 0 && bnz) || (*ptr != 0 && !bnz))
+ branch_hit = false;
+ break;
+ case 2:
+ if ((*(const uint16_t *)ptr == 0 && bnz) ||
+ (*(const uint16_t *)ptr != 0 && !bnz))
+ branch_hit = false;
+ break;
+ case 4:
+ if ((*(const uint32_t *)ptr == 0 && bnz) ||
+ (*(const uint32_t *)ptr != 0 && !bnz))
+ branch_hit = false;
+ break;
+ case 8:
+ if ((*(const uint64_t *)ptr == 0 && bnz) ||
+ (*(const uint64_t *)ptr != 0 && !bnz))
+ branch_hit = false;
+ break;
+ }
+ if (!branch_hit)
+ break;
+ ptr = ptr + element_byte_size;
+ }
+
+ if (branch_hit)
+ target = pc + offset;
+ else
+ target = pc + 8;
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+bool EmulateInstructionMIPS::Emulate_BNZV(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_V(insn, true);
+}
+
+bool EmulateInstructionMIPS::Emulate_BZV(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_V(insn, false);
+}
+
+bool EmulateInstructionMIPS::Emulate_MSA_Branch_V(llvm::MCInst &insn,
+ bool bnz) {
+ bool success = false;
+ int32_t target = 0;
+ llvm::APInt wr_val = llvm::APInt::getNullValue(128);
+ llvm::APInt fail_value = llvm::APInt::getMaxValue(128);
+ llvm::APInt zero_value = llvm::APInt::getNullValue(128);
+ RegisterValue reg_value;
+
+ uint32_t wt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ int32_t offset = insn.getOperand(1).getImm();
+
+ int32_t pc =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips, 0, &success);
+ if (!success)
+ return false;
+
+ if (ReadRegister(eRegisterKindDWARF, dwarf_w0_mips + wt, reg_value))
+ wr_val = reg_value.GetAsUInt128(fail_value);
+ else
+ return false;
+
+ if ((llvm::APInt::isSameValue(zero_value, wr_val) && !bnz) ||
+ (!llvm::APInt::isSameValue(zero_value, wr_val) && bnz))
+ target = pc + offset;
+ else
+ target = pc + 8;
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips,
+ target);
+}
+
+bool EmulateInstructionMIPS::Emulate_LDST_Imm(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t base;
+ int32_t imm, address;
+ Context bad_vaddr_context;
+
+ uint32_t num_operands = insn.getNumOperands();
+ base =
+ m_reg_info->getEncodingValue(insn.getOperand(num_operands - 2).getReg());
+ imm = insn.getOperand(num_operands - 1).getImm();
+
+ RegisterInfo reg_info_base;
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base,
+ reg_info_base))
+ return false;
+
+ /* read base register */
+ address = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + base, 0, &success);
+ if (!success)
+ return false;
+
+ /* destination address */
+ address = address + imm;
+
+ /* Set the bad_vaddr register with base address used in the instruction */
+ bad_vaddr_context.type = eContextInvalid;
+ WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips,
+ address);
+
+ return true;
+}
+
+bool EmulateInstructionMIPS::Emulate_LDST_Reg(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t base, index;
+ int32_t address, index_address;
+ Context bad_vaddr_context;
+
+ uint32_t num_operands = insn.getNumOperands();
+ base =
+ m_reg_info->getEncodingValue(insn.getOperand(num_operands - 2).getReg());
+ index =
+ m_reg_info->getEncodingValue(insn.getOperand(num_operands - 1).getReg());
+
+ RegisterInfo reg_info_base, reg_info_index;
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base,
+ reg_info_base))
+ return false;
+
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + index,
+ reg_info_index))
+ return false;
+
+ /* read base register */
+ address = (int32_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + base, 0, &success);
+ if (!success)
+ return false;
+
+ /* read index register */
+ index_address = (int32_t)ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips + index, 0, &success);
+ if (!success)
+ return false;
+
+ /* destination address */
+ address = address + index_address;
+
+ /* Set the bad_vaddr register with base address used in the instruction */
+ bad_vaddr_context.type = eContextInvalid;
+ WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips,
+ address);
+
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.h b/contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.h
new file mode 100644
index 000000000000..cd447ae4975e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.h
@@ -0,0 +1,221 @@
+//===-- EmulateInstructionMIPS.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 EmulateInstructionMIPS_h_
+#define EmulateInstructionMIPS_h_
+
+namespace llvm {
+class MCDisassembler;
+class MCSubtargetInfo;
+class MCRegisterInfo;
+class MCAsmInfo;
+class MCContext;
+class MCInstrInfo;
+class MCInst;
+}
+
+namespace lldb_private {
+ class OptionValueDictionary;
+}
+
+#include "lldb/Core/EmulateInstruction.h"
+#include "lldb/Utility/Status.h"
+
+class EmulateInstructionMIPS : public lldb_private::EmulateInstruction {
+public:
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::EmulateInstruction *
+ CreateInstance(const lldb_private::ArchSpec &arch,
+ lldb_private::InstructionType inst_type);
+
+ static bool SupportsEmulatingInstructionsOfTypeStatic(
+ lldb_private::InstructionType inst_type) {
+ switch (inst_type) {
+ case lldb_private::eInstructionTypeAny:
+ case lldb_private::eInstructionTypePrologueEpilogue:
+ case lldb_private::eInstructionTypePCModifying:
+ return true;
+
+ case lldb_private::eInstructionTypeAll:
+ return false;
+ }
+ return false;
+ }
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ bool SetTargetTriple(const lldb_private::ArchSpec &arch) override;
+
+ EmulateInstructionMIPS(const lldb_private::ArchSpec &arch);
+
+ bool SupportsEmulatingInstructionsOfType(
+ lldb_private::InstructionType inst_type) override {
+ return SupportsEmulatingInstructionsOfTypeStatic(inst_type);
+ }
+
+ bool ReadInstruction() override;
+
+ bool EvaluateInstruction(uint32_t evaluate_options) override;
+
+ bool SetInstruction(const lldb_private::Opcode &insn_opcode,
+ const lldb_private::Address &inst_addr,
+ lldb_private::Target *target) override;
+
+ bool TestEmulation(lldb_private::Stream *out_stream,
+ lldb_private::ArchSpec &arch,
+ lldb_private::OptionValueDictionary *test_data) override {
+ return false;
+ }
+
+ bool GetRegisterInfo(lldb::RegisterKind reg_kind, uint32_t reg_num,
+ lldb_private::RegisterInfo &reg_info) override;
+
+ bool
+ CreateFunctionEntryUnwind(lldb_private::UnwindPlan &unwind_plan) override;
+
+protected:
+ typedef struct {
+ const char *op_name;
+ bool (EmulateInstructionMIPS::*callback)(llvm::MCInst &insn);
+ const char *insn_name;
+ } MipsOpcode;
+
+ static MipsOpcode *GetOpcodeForInstruction(const char *op_name);
+
+ uint32_t GetSizeOfInstruction(lldb_private::DataExtractor &data,
+ uint64_t inst_addr);
+
+ bool Emulate_ADDiu(llvm::MCInst &insn);
+
+ bool Emulate_SUBU_ADDU(llvm::MCInst &insn);
+
+ bool Emulate_LUI(llvm::MCInst &insn);
+
+ bool Emulate_SW(llvm::MCInst &insn);
+
+ bool Emulate_LW(llvm::MCInst &insn);
+
+ bool Emulate_ADDIUSP(llvm::MCInst &insn);
+
+ bool Emulate_ADDIUS5(llvm::MCInst &insn);
+
+ bool Emulate_SWSP(llvm::MCInst &insn);
+
+ bool Emulate_SWM16_32(llvm::MCInst &insn);
+
+ bool Emulate_LWSP(llvm::MCInst &insn);
+
+ bool Emulate_LWM16_32(llvm::MCInst &insn);
+
+ bool Emulate_JRADDIUSP(llvm::MCInst &insn);
+
+ bool Emulate_LDST_Imm(llvm::MCInst &insn);
+
+ bool Emulate_LDST_Reg(llvm::MCInst &insn);
+
+ bool Emulate_BXX_3ops(llvm::MCInst &insn);
+
+ bool Emulate_BXX_3ops_C(llvm::MCInst &insn);
+
+ bool Emulate_BXX_2ops(llvm::MCInst &insn);
+
+ bool Emulate_BXX_2ops_C(llvm::MCInst &insn);
+
+ bool Emulate_Bcond_Link_C(llvm::MCInst &insn);
+
+ bool Emulate_Bcond_Link(llvm::MCInst &insn);
+
+ bool Emulate_FP_branch(llvm::MCInst &insn);
+
+ bool Emulate_3D_branch(llvm::MCInst &insn);
+
+ bool Emulate_BAL(llvm::MCInst &insn);
+
+ bool Emulate_BALC(llvm::MCInst &insn);
+
+ bool Emulate_BC(llvm::MCInst &insn);
+
+ bool Emulate_J(llvm::MCInst &insn);
+
+ bool Emulate_JAL(llvm::MCInst &insn);
+
+ bool Emulate_JALR(llvm::MCInst &insn);
+
+ bool Emulate_JIALC(llvm::MCInst &insn);
+
+ bool Emulate_JIC(llvm::MCInst &insn);
+
+ bool Emulate_JR(llvm::MCInst &insn);
+
+ bool Emulate_BC1EQZ(llvm::MCInst &insn);
+
+ bool Emulate_BC1NEZ(llvm::MCInst &insn);
+
+ bool Emulate_BNZB(llvm::MCInst &insn);
+
+ bool Emulate_BNZH(llvm::MCInst &insn);
+
+ bool Emulate_BNZW(llvm::MCInst &insn);
+
+ bool Emulate_BNZD(llvm::MCInst &insn);
+
+ bool Emulate_BZB(llvm::MCInst &insn);
+
+ bool Emulate_BZH(llvm::MCInst &insn);
+
+ bool Emulate_BZW(llvm::MCInst &insn);
+
+ bool Emulate_BZD(llvm::MCInst &insn);
+
+ bool Emulate_MSA_Branch_DF(llvm::MCInst &insn, int element_byte_size,
+ bool bnz);
+
+ bool Emulate_BNZV(llvm::MCInst &insn);
+
+ bool Emulate_BZV(llvm::MCInst &insn);
+
+ bool Emulate_MSA_Branch_V(llvm::MCInst &insn, bool bnz);
+
+ bool Emulate_B16_MM(llvm::MCInst &insn);
+
+ bool Emulate_Branch_MM(llvm::MCInst &insn);
+
+ bool Emulate_JALRx16_MM(llvm::MCInst &insn);
+
+ bool Emulate_JALx(llvm::MCInst &insn);
+
+ bool Emulate_JALRS(llvm::MCInst &insn);
+
+ bool nonvolatile_reg_p(uint32_t regnum);
+
+ const char *GetRegisterName(unsigned reg_num, bool altnernate_name);
+
+private:
+ std::unique_ptr<llvm::MCDisassembler> m_disasm;
+ std::unique_ptr<llvm::MCDisassembler> m_alt_disasm;
+ std::unique_ptr<llvm::MCSubtargetInfo> m_subtype_info;
+ std::unique_ptr<llvm::MCSubtargetInfo> m_alt_subtype_info;
+ std::unique_ptr<llvm::MCRegisterInfo> m_reg_info;
+ std::unique_ptr<llvm::MCAsmInfo> m_asm_info;
+ std::unique_ptr<llvm::MCContext> m_context;
+ std::unique_ptr<llvm::MCInstrInfo> m_insn_info;
+ uint32_t m_next_inst_size;
+ bool m_use_alt_disaasm;
+};
+
+#endif // EmulateInstructionMIPS_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp b/contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp
new file mode 100644
index 000000000000..69f0278d1437
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp
@@ -0,0 +1,2361 @@
+//===-- EmulateInstructionMIPS64.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 "EmulateInstructionMIPS64.h"
+
+#include <stdlib.h>
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Opcode.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Stream.h"
+#include "llvm-c/Disassembler.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+#include "Plugins/Process/Utility/InstructionUtils.h"
+#include "Plugins/Process/Utility/RegisterContext_mips.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#define UInt(x) ((uint64_t)x)
+#define integer int64_t
+
+//
+// EmulateInstructionMIPS64 implementation
+//
+
+#ifdef __mips__
+extern "C" {
+void LLVMInitializeMipsTargetInfo();
+void LLVMInitializeMipsTarget();
+void LLVMInitializeMipsAsmPrinter();
+void LLVMInitializeMipsTargetMC();
+void LLVMInitializeMipsDisassembler();
+}
+#endif
+
+EmulateInstructionMIPS64::EmulateInstructionMIPS64(
+ const lldb_private::ArchSpec &arch)
+ : EmulateInstruction(arch) {
+ /* Create instance of llvm::MCDisassembler */
+ std::string Status;
+ llvm::Triple triple = arch.GetTriple();
+ const llvm::Target *target =
+ llvm::TargetRegistry::lookupTarget(triple.getTriple(), Status);
+
+/*
+ * If we fail to get the target then we haven't registered it. The
+ * SystemInitializerCommon
+ * does not initialize targets, MCs and disassemblers. However we need the
+ * MCDisassembler
+ * to decode the instructions so that the decoding complexity stays with LLVM.
+ * Initialize the MIPS targets and disassemblers.
+*/
+#ifdef __mips__
+ if (!target) {
+ LLVMInitializeMipsTargetInfo();
+ LLVMInitializeMipsTarget();
+ LLVMInitializeMipsAsmPrinter();
+ LLVMInitializeMipsTargetMC();
+ LLVMInitializeMipsDisassembler();
+ target = llvm::TargetRegistry::lookupTarget(triple.getTriple(), Status);
+ }
+#endif
+
+ assert(target);
+
+ llvm::StringRef cpu;
+
+ switch (arch.GetCore()) {
+ case ArchSpec::eCore_mips32:
+ case ArchSpec::eCore_mips32el:
+ cpu = "mips32";
+ break;
+ case ArchSpec::eCore_mips32r2:
+ case ArchSpec::eCore_mips32r2el:
+ cpu = "mips32r2";
+ break;
+ case ArchSpec::eCore_mips32r3:
+ case ArchSpec::eCore_mips32r3el:
+ cpu = "mips32r3";
+ break;
+ case ArchSpec::eCore_mips32r5:
+ case ArchSpec::eCore_mips32r5el:
+ cpu = "mips32r5";
+ break;
+ case ArchSpec::eCore_mips32r6:
+ case ArchSpec::eCore_mips32r6el:
+ cpu = "mips32r6";
+ break;
+ case ArchSpec::eCore_mips64:
+ case ArchSpec::eCore_mips64el:
+ cpu = "mips64";
+ break;
+ case ArchSpec::eCore_mips64r2:
+ case ArchSpec::eCore_mips64r2el:
+ cpu = "mips64r2";
+ break;
+ case ArchSpec::eCore_mips64r3:
+ case ArchSpec::eCore_mips64r3el:
+ cpu = "mips64r3";
+ break;
+ case ArchSpec::eCore_mips64r5:
+ case ArchSpec::eCore_mips64r5el:
+ cpu = "mips64r5";
+ break;
+ case ArchSpec::eCore_mips64r6:
+ case ArchSpec::eCore_mips64r6el:
+ cpu = "mips64r6";
+ break;
+ default:
+ cpu = "generic";
+ break;
+ }
+
+ std::string features = "";
+ uint32_t arch_flags = arch.GetFlags();
+ if (arch_flags & ArchSpec::eMIPSAse_msa)
+ features += "+msa,";
+ if (arch_flags & ArchSpec::eMIPSAse_dsp)
+ features += "+dsp,";
+ if (arch_flags & ArchSpec::eMIPSAse_dspr2)
+ features += "+dspr2,";
+ if (arch_flags & ArchSpec::eMIPSAse_mips16)
+ features += "+mips16,";
+ if (arch_flags & ArchSpec::eMIPSAse_micromips)
+ features += "+micromips,";
+
+ m_reg_info.reset(target->createMCRegInfo(triple.getTriple()));
+ assert(m_reg_info.get());
+
+ m_insn_info.reset(target->createMCInstrInfo());
+ assert(m_insn_info.get());
+
+ m_asm_info.reset(target->createMCAsmInfo(*m_reg_info, triple.getTriple()));
+ m_subtype_info.reset(
+ target->createMCSubtargetInfo(triple.getTriple(), cpu, features));
+ assert(m_asm_info.get() && m_subtype_info.get());
+
+ m_context.reset(
+ new llvm::MCContext(m_asm_info.get(), m_reg_info.get(), nullptr));
+ assert(m_context.get());
+
+ m_disasm.reset(target->createMCDisassembler(*m_subtype_info, *m_context));
+ assert(m_disasm.get());
+}
+
+void EmulateInstructionMIPS64::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void EmulateInstructionMIPS64::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+ConstString EmulateInstructionMIPS64::GetPluginNameStatic() {
+ ConstString g_plugin_name("lldb.emulate-instruction.mips64");
+ return g_plugin_name;
+}
+
+lldb_private::ConstString EmulateInstructionMIPS64::GetPluginName() {
+ static ConstString g_plugin_name("EmulateInstructionMIPS64");
+ return g_plugin_name;
+}
+
+const char *EmulateInstructionMIPS64::GetPluginDescriptionStatic() {
+ return "Emulate instructions for the MIPS64 architecture.";
+}
+
+EmulateInstruction *
+EmulateInstructionMIPS64::CreateInstance(const ArchSpec &arch,
+ InstructionType inst_type) {
+ if (EmulateInstructionMIPS64::SupportsEmulatingInstructionsOfTypeStatic(
+ inst_type)) {
+ if (arch.GetTriple().getArch() == llvm::Triple::mips64 ||
+ arch.GetTriple().getArch() == llvm::Triple::mips64el) {
+ return new EmulateInstructionMIPS64(arch);
+ }
+ }
+
+ return nullptr;
+}
+
+bool EmulateInstructionMIPS64::SetTargetTriple(const ArchSpec &arch) {
+ return arch.GetTriple().getArch() == llvm::Triple::mips64 ||
+ arch.GetTriple().getArch() == llvm::Triple::mips64el;
+}
+
+const char *EmulateInstructionMIPS64::GetRegisterName(unsigned reg_num,
+ bool alternate_name) {
+ if (alternate_name) {
+ switch (reg_num) {
+ case dwarf_sp_mips64:
+ return "r29";
+ case dwarf_r30_mips64:
+ return "r30";
+ case dwarf_ra_mips64:
+ return "r31";
+ case dwarf_f0_mips64:
+ return "f0";
+ case dwarf_f1_mips64:
+ return "f1";
+ case dwarf_f2_mips64:
+ return "f2";
+ case dwarf_f3_mips64:
+ return "f3";
+ case dwarf_f4_mips64:
+ return "f4";
+ case dwarf_f5_mips64:
+ return "f5";
+ case dwarf_f6_mips64:
+ return "f6";
+ case dwarf_f7_mips64:
+ return "f7";
+ case dwarf_f8_mips64:
+ return "f8";
+ case dwarf_f9_mips64:
+ return "f9";
+ case dwarf_f10_mips64:
+ return "f10";
+ case dwarf_f11_mips64:
+ return "f11";
+ case dwarf_f12_mips64:
+ return "f12";
+ case dwarf_f13_mips64:
+ return "f13";
+ case dwarf_f14_mips64:
+ return "f14";
+ case dwarf_f15_mips64:
+ return "f15";
+ case dwarf_f16_mips64:
+ return "f16";
+ case dwarf_f17_mips64:
+ return "f17";
+ case dwarf_f18_mips64:
+ return "f18";
+ case dwarf_f19_mips64:
+ return "f19";
+ case dwarf_f20_mips64:
+ return "f20";
+ case dwarf_f21_mips64:
+ return "f21";
+ case dwarf_f22_mips64:
+ return "f22";
+ case dwarf_f23_mips64:
+ return "f23";
+ case dwarf_f24_mips64:
+ return "f24";
+ case dwarf_f25_mips64:
+ return "f25";
+ case dwarf_f26_mips64:
+ return "f26";
+ case dwarf_f27_mips64:
+ return "f27";
+ case dwarf_f28_mips64:
+ return "f28";
+ case dwarf_f29_mips64:
+ return "f29";
+ case dwarf_f30_mips64:
+ return "f30";
+ case dwarf_f31_mips64:
+ return "f31";
+ case dwarf_w0_mips64:
+ return "w0";
+ case dwarf_w1_mips64:
+ return "w1";
+ case dwarf_w2_mips64:
+ return "w2";
+ case dwarf_w3_mips64:
+ return "w3";
+ case dwarf_w4_mips64:
+ return "w4";
+ case dwarf_w5_mips64:
+ return "w5";
+ case dwarf_w6_mips64:
+ return "w6";
+ case dwarf_w7_mips64:
+ return "w7";
+ case dwarf_w8_mips64:
+ return "w8";
+ case dwarf_w9_mips64:
+ return "w9";
+ case dwarf_w10_mips64:
+ return "w10";
+ case dwarf_w11_mips64:
+ return "w11";
+ case dwarf_w12_mips64:
+ return "w12";
+ case dwarf_w13_mips64:
+ return "w13";
+ case dwarf_w14_mips64:
+ return "w14";
+ case dwarf_w15_mips64:
+ return "w15";
+ case dwarf_w16_mips64:
+ return "w16";
+ case dwarf_w17_mips64:
+ return "w17";
+ case dwarf_w18_mips64:
+ return "w18";
+ case dwarf_w19_mips64:
+ return "w19";
+ case dwarf_w20_mips64:
+ return "w20";
+ case dwarf_w21_mips64:
+ return "w21";
+ case dwarf_w22_mips64:
+ return "w22";
+ case dwarf_w23_mips64:
+ return "w23";
+ case dwarf_w24_mips64:
+ return "w24";
+ case dwarf_w25_mips64:
+ return "w25";
+ case dwarf_w26_mips64:
+ return "w26";
+ case dwarf_w27_mips64:
+ return "w27";
+ case dwarf_w28_mips64:
+ return "w28";
+ case dwarf_w29_mips64:
+ return "w29";
+ case dwarf_w30_mips64:
+ return "w30";
+ case dwarf_w31_mips64:
+ return "w31";
+ case dwarf_mir_mips64:
+ return "mir";
+ case dwarf_mcsr_mips64:
+ return "mcsr";
+ case dwarf_config5_mips64:
+ return "config5";
+ default:
+ break;
+ }
+ return nullptr;
+ }
+
+ switch (reg_num) {
+ case dwarf_zero_mips64:
+ return "r0";
+ case dwarf_r1_mips64:
+ return "r1";
+ case dwarf_r2_mips64:
+ return "r2";
+ case dwarf_r3_mips64:
+ return "r3";
+ case dwarf_r4_mips64:
+ return "r4";
+ case dwarf_r5_mips64:
+ return "r5";
+ case dwarf_r6_mips64:
+ return "r6";
+ case dwarf_r7_mips64:
+ return "r7";
+ case dwarf_r8_mips64:
+ return "r8";
+ case dwarf_r9_mips64:
+ return "r9";
+ case dwarf_r10_mips64:
+ return "r10";
+ case dwarf_r11_mips64:
+ return "r11";
+ case dwarf_r12_mips64:
+ return "r12";
+ case dwarf_r13_mips64:
+ return "r13";
+ case dwarf_r14_mips64:
+ return "r14";
+ case dwarf_r15_mips64:
+ return "r15";
+ case dwarf_r16_mips64:
+ return "r16";
+ case dwarf_r17_mips64:
+ return "r17";
+ case dwarf_r18_mips64:
+ return "r18";
+ case dwarf_r19_mips64:
+ return "r19";
+ case dwarf_r20_mips64:
+ return "r20";
+ case dwarf_r21_mips64:
+ return "r21";
+ case dwarf_r22_mips64:
+ return "r22";
+ case dwarf_r23_mips64:
+ return "r23";
+ case dwarf_r24_mips64:
+ return "r24";
+ case dwarf_r25_mips64:
+ return "r25";
+ case dwarf_r26_mips64:
+ return "r26";
+ case dwarf_r27_mips64:
+ return "r27";
+ case dwarf_gp_mips64:
+ return "gp";
+ case dwarf_sp_mips64:
+ return "sp";
+ case dwarf_r30_mips64:
+ return "fp";
+ case dwarf_ra_mips64:
+ return "ra";
+ case dwarf_sr_mips64:
+ return "sr";
+ case dwarf_lo_mips64:
+ return "lo";
+ case dwarf_hi_mips64:
+ return "hi";
+ case dwarf_bad_mips64:
+ return "bad";
+ case dwarf_cause_mips64:
+ return "cause";
+ case dwarf_pc_mips64:
+ return "pc";
+ case dwarf_f0_mips64:
+ return "f0";
+ case dwarf_f1_mips64:
+ return "f1";
+ case dwarf_f2_mips64:
+ return "f2";
+ case dwarf_f3_mips64:
+ return "f3";
+ case dwarf_f4_mips64:
+ return "f4";
+ case dwarf_f5_mips64:
+ return "f5";
+ case dwarf_f6_mips64:
+ return "f6";
+ case dwarf_f7_mips64:
+ return "f7";
+ case dwarf_f8_mips64:
+ return "f8";
+ case dwarf_f9_mips64:
+ return "f9";
+ case dwarf_f10_mips64:
+ return "f10";
+ case dwarf_f11_mips64:
+ return "f11";
+ case dwarf_f12_mips64:
+ return "f12";
+ case dwarf_f13_mips64:
+ return "f13";
+ case dwarf_f14_mips64:
+ return "f14";
+ case dwarf_f15_mips64:
+ return "f15";
+ case dwarf_f16_mips64:
+ return "f16";
+ case dwarf_f17_mips64:
+ return "f17";
+ case dwarf_f18_mips64:
+ return "f18";
+ case dwarf_f19_mips64:
+ return "f19";
+ case dwarf_f20_mips64:
+ return "f20";
+ case dwarf_f21_mips64:
+ return "f21";
+ case dwarf_f22_mips64:
+ return "f22";
+ case dwarf_f23_mips64:
+ return "f23";
+ case dwarf_f24_mips64:
+ return "f24";
+ case dwarf_f25_mips64:
+ return "f25";
+ case dwarf_f26_mips64:
+ return "f26";
+ case dwarf_f27_mips64:
+ return "f27";
+ case dwarf_f28_mips64:
+ return "f28";
+ case dwarf_f29_mips64:
+ return "f29";
+ case dwarf_f30_mips64:
+ return "f30";
+ case dwarf_f31_mips64:
+ return "f31";
+ case dwarf_fcsr_mips64:
+ return "fcsr";
+ case dwarf_fir_mips64:
+ return "fir";
+ case dwarf_w0_mips64:
+ return "w0";
+ case dwarf_w1_mips64:
+ return "w1";
+ case dwarf_w2_mips64:
+ return "w2";
+ case dwarf_w3_mips64:
+ return "w3";
+ case dwarf_w4_mips64:
+ return "w4";
+ case dwarf_w5_mips64:
+ return "w5";
+ case dwarf_w6_mips64:
+ return "w6";
+ case dwarf_w7_mips64:
+ return "w7";
+ case dwarf_w8_mips64:
+ return "w8";
+ case dwarf_w9_mips64:
+ return "w9";
+ case dwarf_w10_mips64:
+ return "w10";
+ case dwarf_w11_mips64:
+ return "w11";
+ case dwarf_w12_mips64:
+ return "w12";
+ case dwarf_w13_mips64:
+ return "w13";
+ case dwarf_w14_mips64:
+ return "w14";
+ case dwarf_w15_mips64:
+ return "w15";
+ case dwarf_w16_mips64:
+ return "w16";
+ case dwarf_w17_mips64:
+ return "w17";
+ case dwarf_w18_mips64:
+ return "w18";
+ case dwarf_w19_mips64:
+ return "w19";
+ case dwarf_w20_mips64:
+ return "w20";
+ case dwarf_w21_mips64:
+ return "w21";
+ case dwarf_w22_mips64:
+ return "w22";
+ case dwarf_w23_mips64:
+ return "w23";
+ case dwarf_w24_mips64:
+ return "w24";
+ case dwarf_w25_mips64:
+ return "w25";
+ case dwarf_w26_mips64:
+ return "w26";
+ case dwarf_w27_mips64:
+ return "w27";
+ case dwarf_w28_mips64:
+ return "w28";
+ case dwarf_w29_mips64:
+ return "w29";
+ case dwarf_w30_mips64:
+ return "w30";
+ case dwarf_w31_mips64:
+ return "w31";
+ case dwarf_mcsr_mips64:
+ return "mcsr";
+ case dwarf_mir_mips64:
+ return "mir";
+ case dwarf_config5_mips64:
+ return "config5";
+ }
+ return nullptr;
+}
+
+bool EmulateInstructionMIPS64::GetRegisterInfo(RegisterKind reg_kind,
+ uint32_t reg_num,
+ RegisterInfo &reg_info) {
+ if (reg_kind == eRegisterKindGeneric) {
+ switch (reg_num) {
+ case LLDB_REGNUM_GENERIC_PC:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_pc_mips64;
+ break;
+ case LLDB_REGNUM_GENERIC_SP:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_sp_mips64;
+ break;
+ case LLDB_REGNUM_GENERIC_FP:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_r30_mips64;
+ break;
+ case LLDB_REGNUM_GENERIC_RA:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_ra_mips64;
+ break;
+ case LLDB_REGNUM_GENERIC_FLAGS:
+ reg_kind = eRegisterKindDWARF;
+ reg_num = dwarf_sr_mips64;
+ break;
+ default:
+ return false;
+ }
+ }
+
+ if (reg_kind == eRegisterKindDWARF) {
+ ::memset(&reg_info, 0, sizeof(RegisterInfo));
+ ::memset(reg_info.kinds, LLDB_INVALID_REGNUM, sizeof(reg_info.kinds));
+
+ if (reg_num == dwarf_sr_mips64 || reg_num == dwarf_fcsr_mips64 ||
+ reg_num == dwarf_fir_mips64 || reg_num == dwarf_mcsr_mips64 ||
+ reg_num == dwarf_mir_mips64 || reg_num == dwarf_config5_mips64) {
+ reg_info.byte_size = 4;
+ reg_info.format = eFormatHex;
+ reg_info.encoding = eEncodingUint;
+ } else if ((int)reg_num >= dwarf_zero_mips64 &&
+ (int)reg_num <= dwarf_f31_mips64) {
+ reg_info.byte_size = 8;
+ reg_info.format = eFormatHex;
+ reg_info.encoding = eEncodingUint;
+ } else if ((int)reg_num >= dwarf_w0_mips64 &&
+ (int)reg_num <= dwarf_w31_mips64) {
+ reg_info.byte_size = 16;
+ reg_info.format = eFormatVectorOfUInt8;
+ reg_info.encoding = eEncodingVector;
+ } else {
+ return false;
+ }
+
+ reg_info.name = GetRegisterName(reg_num, false);
+ reg_info.alt_name = GetRegisterName(reg_num, true);
+ reg_info.kinds[eRegisterKindDWARF] = reg_num;
+
+ switch (reg_num) {
+ case dwarf_r30_mips64:
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP;
+ break;
+ case dwarf_ra_mips64:
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA;
+ break;
+ case dwarf_sp_mips64:
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP;
+ break;
+ case dwarf_pc_mips64:
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC;
+ break;
+ case dwarf_sr_mips64:
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS;
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+ return false;
+}
+
+EmulateInstructionMIPS64::MipsOpcode *
+EmulateInstructionMIPS64::GetOpcodeForInstruction(const char *op_name) {
+ static EmulateInstructionMIPS64::MipsOpcode g_opcodes[] = {
+ // Prologue/Epilogue instructions
+ {"DADDiu", &EmulateInstructionMIPS64::Emulate_DADDiu,
+ "DADDIU rt, rs, immediate"},
+ {"ADDiu", &EmulateInstructionMIPS64::Emulate_DADDiu,
+ "ADDIU rt, rs, immediate"},
+ {"SD", &EmulateInstructionMIPS64::Emulate_SD, "SD rt, offset(rs)"},
+ {"LD", &EmulateInstructionMIPS64::Emulate_LD, "LD rt, offset(base)"},
+ {"DSUBU", &EmulateInstructionMIPS64::Emulate_DSUBU_DADDU,
+ "DSUBU rd, rs, rt"},
+ {"SUBU", &EmulateInstructionMIPS64::Emulate_DSUBU_DADDU,
+ "SUBU rd, rs, rt"},
+ {"DADDU", &EmulateInstructionMIPS64::Emulate_DSUBU_DADDU,
+ "DADDU rd, rs, rt"},
+ {"ADDU", &EmulateInstructionMIPS64::Emulate_DSUBU_DADDU,
+ "ADDU rd, rs, rt"},
+ {"LUI", &EmulateInstructionMIPS64::Emulate_LUI, "LUI rt, immediate"},
+
+ // Load/Store instructions
+ /* Following list of emulated instructions are required by implementation
+ of hardware watchpoint
+ for MIPS in lldb. As we just need the address accessed by instructions,
+ we have generalised
+ all these instructions in 2 functions depending on their addressing
+ modes */
+
+ {"LB", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LB rt, offset(base)"},
+ {"LBE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LBE rt, offset(base)"},
+ {"LBU", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LBU rt, offset(base)"},
+ {"LBUE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LBUE rt, offset(base)"},
+ {"LDC1", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LDC1 ft, offset(base)"},
+ {"LDL", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LDL rt, offset(base)"},
+ {"LDR", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LDR rt, offset(base)"},
+ {"LLD", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LLD rt, offset(base)"},
+ {"LDC2", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LDC2 rt, offset(base)"},
+ {"LDXC1", &EmulateInstructionMIPS64::Emulate_LDST_Reg,
+ "LDXC1 fd, index (base)"},
+ {"LH", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LH rt, offset(base)"},
+ {"LHE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LHE rt, offset(base)"},
+ {"LHU", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LHU rt, offset(base)"},
+ {"LHUE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LHUE rt, offset(base)"},
+ {"LL", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LL rt, offset(base)"},
+ {"LLE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LLE rt, offset(base)"},
+ {"LUXC1", &EmulateInstructionMIPS64::Emulate_LDST_Reg,
+ "LUXC1 fd, index (base)"},
+ {"LW", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LW rt, offset(rs)"},
+ {"LWC1", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LWC1 ft, offset(base)"},
+ {"LWC2", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LWC2 rt, offset(base)"},
+ {"LWE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LWE rt, offset(base)"},
+ {"LWL", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LWL rt, offset(base)"},
+ {"LWLE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LWLE rt, offset(base)"},
+ {"LWR", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LWR rt, offset(base)"},
+ {"LWRE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "LWRE rt, offset(base)"},
+ {"LWXC1", &EmulateInstructionMIPS64::Emulate_LDST_Reg,
+ "LWXC1 fd, index (base)"},
+
+ {"SB", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SB rt, offset(base)"},
+ {"SBE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SBE rt, offset(base)"},
+ {"SC", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SC rt, offset(base)"},
+ {"SCE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SCE rt, offset(base)"},
+ {"SCD", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SCD rt, offset(base)"},
+ {"SDL", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SDL rt, offset(base)"},
+ {"SDR", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SDR rt, offset(base)"},
+ {"SDC1", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SDC1 ft, offset(base)"},
+ {"SDC2", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SDC2 rt, offset(base)"},
+ {"SDXC1", &EmulateInstructionMIPS64::Emulate_LDST_Reg,
+ "SDXC1 fs, index (base)"},
+ {"SH", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SH rt, offset(base)"},
+ {"SHE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SHE rt, offset(base)"},
+ {"SUXC1", &EmulateInstructionMIPS64::Emulate_LDST_Reg,
+ "SUXC1 fs, index (base)"},
+ {"SW", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SW rt, offset(rs)"},
+ {"SWC1", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SWC1 ft, offset(base)"},
+ {"SWC2", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SWC2 rt, offset(base)"},
+ {"SWE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SWE rt, offset(base)"},
+ {"SWL", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SWL rt, offset(base)"},
+ {"SWLE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SWLE rt, offset(base)"},
+ {"SWR", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SWR rt, offset(base)"},
+ {"SWRE", &EmulateInstructionMIPS64::Emulate_LDST_Imm,
+ "SWRE rt, offset(base)"},
+ {"SWXC1", &EmulateInstructionMIPS64::Emulate_LDST_Reg,
+ "SWXC1 fs, index (base)"},
+
+ // Branch instructions
+ {"BEQ", &EmulateInstructionMIPS64::Emulate_BXX_3ops, "BEQ rs,rt,offset"},
+ {"BEQ64", &EmulateInstructionMIPS64::Emulate_BXX_3ops, "BEQ rs,rt,offset"},
+ {"BNE", &EmulateInstructionMIPS64::Emulate_BXX_3ops, "BNE rs,rt,offset"},
+ {"BNE64", &EmulateInstructionMIPS64::Emulate_BXX_3ops, "BNE rs,rt,offset"},
+ {"BEQL", &EmulateInstructionMIPS64::Emulate_BXX_3ops,
+ "BEQL rs,rt,offset"},
+ {"BNEL", &EmulateInstructionMIPS64::Emulate_BXX_3ops,
+ "BNEL rs,rt,offset"},
+ {"BGEZALL", &EmulateInstructionMIPS64::Emulate_Bcond_Link,
+ "BGEZALL rt,offset"},
+ {"BAL", &EmulateInstructionMIPS64::Emulate_BAL, "BAL offset"},
+ {"BGEZAL", &EmulateInstructionMIPS64::Emulate_Bcond_Link,
+ "BGEZAL rs,offset"},
+ {"BALC", &EmulateInstructionMIPS64::Emulate_BALC, "BALC offset"},
+ {"BC", &EmulateInstructionMIPS64::Emulate_BC, "BC offset"},
+ {"BGEZ", &EmulateInstructionMIPS64::Emulate_BXX_2ops, "BGEZ rs,offset"},
+ {"BGEZ64", &EmulateInstructionMIPS64::Emulate_BXX_2ops, "BGEZ rs,offset"},
+ {"BLEZALC", &EmulateInstructionMIPS64::Emulate_Bcond_Link_C,
+ "BLEZALC rs,offset"},
+ {"BGEZALC", &EmulateInstructionMIPS64::Emulate_Bcond_Link_C,
+ "BGEZALC rs,offset"},
+ {"BLTZALC", &EmulateInstructionMIPS64::Emulate_Bcond_Link_C,
+ "BLTZALC rs,offset"},
+ {"BGTZALC", &EmulateInstructionMIPS64::Emulate_Bcond_Link_C,
+ "BGTZALC rs,offset"},
+ {"BEQZALC", &EmulateInstructionMIPS64::Emulate_Bcond_Link_C,
+ "BEQZALC rs,offset"},
+ {"BNEZALC", &EmulateInstructionMIPS64::Emulate_Bcond_Link_C,
+ "BNEZALC rs,offset"},
+ {"BEQC", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BEQC rs,rt,offset"},
+ {"BEQC64", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BEQC rs,rt,offset"},
+ {"BNEC", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BNEC rs,rt,offset"},
+ {"BNEC64", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BNEC rs,rt,offset"},
+ {"BLTC", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BLTC rs,rt,offset"},
+ {"BLTC64", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BLTC rs,rt,offset"},
+ {"BGEC", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BGEC rs,rt,offset"},
+ {"BGEC64", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BGEC rs,rt,offset"},
+ {"BLTUC", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BLTUC rs,rt,offset"},
+ {"BLTUC64", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BLTUC rs,rt,offset"},
+ {"BGEUC", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BGEUC rs,rt,offset"},
+ {"BGEUC64", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BGEUC rs,rt,offset"},
+ {"BLTZC", &EmulateInstructionMIPS64::Emulate_BXX_2ops_C,
+ "BLTZC rt,offset"},
+ {"BLTZC64", &EmulateInstructionMIPS64::Emulate_BXX_2ops_C,
+ "BLTZC rt,offset"},
+ {"BLEZC", &EmulateInstructionMIPS64::Emulate_BXX_2ops_C,
+ "BLEZC rt,offset"},
+ {"BLEZC64", &EmulateInstructionMIPS64::Emulate_BXX_2ops_C,
+ "BLEZC rt,offset"},
+ {"BGEZC", &EmulateInstructionMIPS64::Emulate_BXX_2ops_C,
+ "BGEZC rt,offset"},
+ {"BGEZC64", &EmulateInstructionMIPS64::Emulate_BXX_2ops_C,
+ "BGEZC rt,offset"},
+ {"BGTZC", &EmulateInstructionMIPS64::Emulate_BXX_2ops_C,
+ "BGTZC rt,offset"},
+ {"BGTZC64", &EmulateInstructionMIPS64::Emulate_BXX_2ops_C,
+ "BGTZC rt,offset"},
+ {"BEQZC", &EmulateInstructionMIPS64::Emulate_BXX_2ops_C,
+ "BEQZC rt,offset"},
+ {"BEQZC64", &EmulateInstructionMIPS64::Emulate_BXX_2ops_C,
+ "BEQZC rt,offset"},
+ {"BNEZC", &EmulateInstructionMIPS64::Emulate_BXX_2ops_C,
+ "BNEZC rt,offset"},
+ {"BNEZC64", &EmulateInstructionMIPS64::Emulate_BXX_2ops_C,
+ "BNEZC rt,offset"},
+ {"BGEZL", &EmulateInstructionMIPS64::Emulate_BXX_2ops, "BGEZL rt,offset"},
+ {"BGTZ", &EmulateInstructionMIPS64::Emulate_BXX_2ops, "BGTZ rt,offset"},
+ {"BGTZ64", &EmulateInstructionMIPS64::Emulate_BXX_2ops, "BGTZ rt,offset"},
+ {"BGTZL", &EmulateInstructionMIPS64::Emulate_BXX_2ops, "BGTZL rt,offset"},
+ {"BLEZ", &EmulateInstructionMIPS64::Emulate_BXX_2ops, "BLEZ rt,offset"},
+ {"BLEZ64", &EmulateInstructionMIPS64::Emulate_BXX_2ops, "BLEZ rt,offset"},
+ {"BLEZL", &EmulateInstructionMIPS64::Emulate_BXX_2ops, "BLEZL rt,offset"},
+ {"BLTZ", &EmulateInstructionMIPS64::Emulate_BXX_2ops, "BLTZ rt,offset"},
+ {"BLTZ64", &EmulateInstructionMIPS64::Emulate_BXX_2ops, "BLTZ rt,offset"},
+ {"BLTZAL", &EmulateInstructionMIPS64::Emulate_Bcond_Link,
+ "BLTZAL rt,offset"},
+ {"BLTZALL", &EmulateInstructionMIPS64::Emulate_Bcond_Link,
+ "BLTZALL rt,offset"},
+ {"BLTZL", &EmulateInstructionMIPS64::Emulate_BXX_2ops, "BLTZL rt,offset"},
+ {"BOVC", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BOVC rs,rt,offset"},
+ {"BNVC", &EmulateInstructionMIPS64::Emulate_BXX_3ops_C,
+ "BNVC rs,rt,offset"},
+ {"J", &EmulateInstructionMIPS64::Emulate_J, "J target"},
+ {"JAL", &EmulateInstructionMIPS64::Emulate_JAL, "JAL target"},
+ {"JALX", &EmulateInstructionMIPS64::Emulate_JAL, "JALX target"},
+ {"JALR", &EmulateInstructionMIPS64::Emulate_JALR, "JALR target"},
+ {"JALR64", &EmulateInstructionMIPS64::Emulate_JALR, "JALR target"},
+ {"JALR_HB", &EmulateInstructionMIPS64::Emulate_JALR, "JALR.HB target"},
+ {"JIALC", &EmulateInstructionMIPS64::Emulate_JIALC, "JIALC rt,offset"},
+ {"JIALC64", &EmulateInstructionMIPS64::Emulate_JIALC, "JIALC rt,offset"},
+ {"JIC", &EmulateInstructionMIPS64::Emulate_JIC, "JIC rt,offset"},
+ {"JIC64", &EmulateInstructionMIPS64::Emulate_JIC, "JIC rt,offset"},
+ {"JR", &EmulateInstructionMIPS64::Emulate_JR, "JR target"},
+ {"JR64", &EmulateInstructionMIPS64::Emulate_JR, "JR target"},
+ {"JR_HB", &EmulateInstructionMIPS64::Emulate_JR, "JR.HB target"},
+ {"BC1F", &EmulateInstructionMIPS64::Emulate_FP_branch, "BC1F cc, offset"},
+ {"BC1T", &EmulateInstructionMIPS64::Emulate_FP_branch, "BC1T cc, offset"},
+ {"BC1FL", &EmulateInstructionMIPS64::Emulate_FP_branch,
+ "BC1FL cc, offset"},
+ {"BC1TL", &EmulateInstructionMIPS64::Emulate_FP_branch,
+ "BC1TL cc, offset"},
+ {"BC1EQZ", &EmulateInstructionMIPS64::Emulate_BC1EQZ,
+ "BC1EQZ ft, offset"},
+ {"BC1NEZ", &EmulateInstructionMIPS64::Emulate_BC1NEZ,
+ "BC1NEZ ft, offset"},
+ {"BC1ANY2F", &EmulateInstructionMIPS64::Emulate_3D_branch,
+ "BC1ANY2F cc, offset"},
+ {"BC1ANY2T", &EmulateInstructionMIPS64::Emulate_3D_branch,
+ "BC1ANY2T cc, offset"},
+ {"BC1ANY4F", &EmulateInstructionMIPS64::Emulate_3D_branch,
+ "BC1ANY4F cc, offset"},
+ {"BC1ANY4T", &EmulateInstructionMIPS64::Emulate_3D_branch,
+ "BC1ANY4T cc, offset"},
+ {"BNZ_B", &EmulateInstructionMIPS64::Emulate_BNZB, "BNZ.b wt,s16"},
+ {"BNZ_H", &EmulateInstructionMIPS64::Emulate_BNZH, "BNZ.h wt,s16"},
+ {"BNZ_W", &EmulateInstructionMIPS64::Emulate_BNZW, "BNZ.w wt,s16"},
+ {"BNZ_D", &EmulateInstructionMIPS64::Emulate_BNZD, "BNZ.d wt,s16"},
+ {"BZ_B", &EmulateInstructionMIPS64::Emulate_BZB, "BZ.b wt,s16"},
+ {"BZ_H", &EmulateInstructionMIPS64::Emulate_BZH, "BZ.h wt,s16"},
+ {"BZ_W", &EmulateInstructionMIPS64::Emulate_BZW, "BZ.w wt,s16"},
+ {"BZ_D", &EmulateInstructionMIPS64::Emulate_BZD, "BZ.d wt,s16"},
+ {"BNZ_V", &EmulateInstructionMIPS64::Emulate_BNZV, "BNZ.V wt,s16"},
+ {"BZ_V", &EmulateInstructionMIPS64::Emulate_BZV, "BZ.V wt,s16"},
+ };
+
+ static const size_t k_num_mips_opcodes = llvm::array_lengthof(g_opcodes);
+
+ for (size_t i = 0; i < k_num_mips_opcodes; ++i) {
+ if (!strcasecmp(g_opcodes[i].op_name, op_name))
+ return &g_opcodes[i];
+ }
+
+ return nullptr;
+}
+
+bool EmulateInstructionMIPS64::ReadInstruction() {
+ bool success = false;
+ m_addr = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC,
+ LLDB_INVALID_ADDRESS, &success);
+ if (success) {
+ Context read_inst_context;
+ read_inst_context.type = eContextReadOpcode;
+ read_inst_context.SetNoArgs();
+ m_opcode.SetOpcode32(
+ ReadMemoryUnsigned(read_inst_context, m_addr, 4, 0, &success),
+ GetByteOrder());
+ }
+ if (!success)
+ m_addr = LLDB_INVALID_ADDRESS;
+ return success;
+}
+
+bool EmulateInstructionMIPS64::EvaluateInstruction(uint32_t evaluate_options) {
+ bool success = false;
+ llvm::MCInst mc_insn;
+ uint64_t insn_size;
+ DataExtractor data;
+
+ /* Keep the complexity of the decode logic with the llvm::MCDisassembler
+ * class. */
+ if (m_opcode.GetData(data)) {
+ llvm::MCDisassembler::DecodeStatus decode_status;
+ llvm::ArrayRef<uint8_t> raw_insn(data.GetDataStart(), data.GetByteSize());
+ decode_status = m_disasm->getInstruction(
+ mc_insn, insn_size, raw_insn, m_addr, llvm::nulls(), llvm::nulls());
+ if (decode_status != llvm::MCDisassembler::Success)
+ return false;
+ }
+
+ /*
+ * mc_insn.getOpcode() returns decoded opcode. However to make use
+ * of llvm::Mips::<insn> we would need "MipsGenInstrInfo.inc".
+ */
+ const char *op_name = m_insn_info->getName(mc_insn.getOpcode()).data();
+
+ if (op_name == nullptr)
+ return false;
+
+ /*
+ * Decoding has been done already. Just get the call-back function
+ * and emulate the instruction.
+ */
+ MipsOpcode *opcode_data = GetOpcodeForInstruction(op_name);
+
+ if (opcode_data == nullptr)
+ return false;
+
+ uint64_t old_pc = 0, new_pc = 0;
+ const bool auto_advance_pc =
+ evaluate_options & eEmulateInstructionOptionAutoAdvancePC;
+
+ if (auto_advance_pc) {
+ old_pc =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+ }
+
+ /* emulate instruction */
+ success = (this->*opcode_data->callback)(mc_insn);
+ if (!success)
+ return false;
+
+ if (auto_advance_pc) {
+ new_pc =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ /* If we haven't changed the PC, change it here */
+ if (old_pc == new_pc) {
+ new_pc += 4;
+ Context context;
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ new_pc))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool EmulateInstructionMIPS64::CreateFunctionEntryUnwind(
+ UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindDWARF);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+ const bool can_replace = false;
+
+ // Our previous Call Frame Address is the stack pointer
+ row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_sp_mips64, 0);
+
+ // Our previous PC is in the RA
+ row->SetRegisterLocationToRegister(dwarf_pc_mips64, dwarf_ra_mips64,
+ can_replace);
+
+ unwind_plan.AppendRow(row);
+
+ // All other registers are the same.
+ unwind_plan.SetSourceName("EmulateInstructionMIPS64");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);
+ unwind_plan.SetReturnAddressRegister(dwarf_ra_mips64);
+
+ return true;
+}
+
+bool EmulateInstructionMIPS64::nonvolatile_reg_p(uint64_t regnum) {
+ switch (regnum) {
+ case dwarf_r16_mips64:
+ case dwarf_r17_mips64:
+ case dwarf_r18_mips64:
+ case dwarf_r19_mips64:
+ case dwarf_r20_mips64:
+ case dwarf_r21_mips64:
+ case dwarf_r22_mips64:
+ case dwarf_r23_mips64:
+ case dwarf_gp_mips64:
+ case dwarf_sp_mips64:
+ case dwarf_r30_mips64:
+ case dwarf_ra_mips64:
+ return true;
+ default:
+ return false;
+ }
+ return false;
+}
+
+bool EmulateInstructionMIPS64::Emulate_DADDiu(llvm::MCInst &insn) {
+ // DADDIU rt, rs, immediate
+ // GPR[rt] <- GPR[rs] + sign_extend(immediate)
+
+ uint8_t dst, src;
+ bool success = false;
+ const uint32_t imm16 = insn.getOperand(2).getImm();
+ int64_t imm = SignedBits(imm16, 15, 0);
+
+ dst = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ src = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+
+ // If immediate is greater than 2^16 - 1 then clang generate LUI,
+ // (D)ADDIU,(D)SUBU instructions in prolog. Example lui $1, 0x2 daddiu $1,
+ // $1, -0x5920 dsubu $sp, $sp, $1 In this case, (D)ADDIU dst and src will be
+ // same and not equal to sp
+ if (dst == src) {
+ Context context;
+
+ /* read <src> register */
+ const uint64_t src_opd_val = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips64 + src, 0, &success);
+ if (!success)
+ return false;
+
+ /* Check if this is daddiu sp, sp, imm16 */
+ if (dst == dwarf_sp_mips64) {
+ /*
+ * From the MIPS IV spec:
+ *
+ * The term “unsigned” in the instruction name is a misnomer; this
+ * operation is 64-bit modulo arithmetic that does not trap on overflow.
+ * It is appropriate for arithmetic which is not signed, such as address
+ * arithmetic, or integer arithmetic environments that ignore overflow,
+ * such as “C” language arithmetic.
+ *
+ * Assume 2's complement and rely on unsigned overflow here.
+ */
+ uint64_t result = src_opd_val + imm;
+ RegisterInfo reg_info_sp;
+
+ if (GetRegisterInfo(eRegisterKindDWARF, dwarf_sp_mips64, reg_info_sp))
+ context.SetRegisterPlusOffset(reg_info_sp, imm);
+
+ /* We are allocating bytes on stack */
+ context.type = eContextAdjustStackPointer;
+
+ WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_sp_mips64,
+ result);
+ return true;
+ }
+
+ imm += src_opd_val;
+ context.SetImmediateSigned(imm);
+ context.type = eContextImmediate;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF,
+ dwarf_zero_mips64 + dst, imm))
+ return false;
+ }
+
+ return true;
+}
+
+bool EmulateInstructionMIPS64::Emulate_SD(llvm::MCInst &insn) {
+ uint64_t address;
+ RegisterInfo reg_info_base;
+ RegisterInfo reg_info_src;
+ bool success = false;
+ uint32_t imm16 = insn.getOperand(2).getImm();
+ uint64_t imm = SignedBits(imm16, 15, 0);
+ uint32_t src, base;
+ Context bad_vaddr_context;
+
+ src = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ base = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips64 + base,
+ reg_info_base) ||
+ !GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips64 + src,
+ reg_info_src))
+ return false;
+
+ /* read SP */
+ address = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips64 + base,
+ 0, &success);
+ if (!success)
+ return false;
+
+ /* destination address */
+ address = address + imm;
+
+ /* We look for sp based non-volatile register stores */
+ if (nonvolatile_reg_p(src)) {
+ Context context;
+ RegisterValue data_src;
+ context.type = eContextPushRegisterOnStack;
+ context.SetRegisterToRegisterPlusOffset(reg_info_src, reg_info_base, 0);
+
+ uint8_t buffer[RegisterValue::kMaxRegisterByteSize];
+ Status error;
+
+ if (!ReadRegister(&reg_info_base, data_src))
+ return false;
+
+ if (data_src.GetAsMemoryData(&reg_info_src, buffer, reg_info_src.byte_size,
+ eByteOrderLittle, error) == 0)
+ return false;
+
+ if (!WriteMemory(context, address, buffer, reg_info_src.byte_size))
+ return false;
+ }
+
+ /* Set the bad_vaddr register with base address used in the instruction */
+ bad_vaddr_context.type = eContextInvalid;
+ WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips64,
+ address);
+
+ return true;
+}
+
+bool EmulateInstructionMIPS64::Emulate_LD(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t src, base;
+ int64_t imm, address;
+ Context bad_vaddr_context;
+
+ src = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ base = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+ imm = insn.getOperand(2).getImm();
+
+ RegisterInfo reg_info_base;
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips64 + base,
+ reg_info_base))
+ return false;
+
+ /* read base register */
+ address = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips64 + base,
+ 0, &success);
+ if (!success)
+ return false;
+
+ /* destination address */
+ address = address + imm;
+
+ /* Set the bad_vaddr register with base address used in the instruction */
+ bad_vaddr_context.type = eContextInvalid;
+ WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips64,
+ address);
+
+ if (nonvolatile_reg_p(src)) {
+ RegisterValue data_src;
+ RegisterInfo reg_info_src;
+
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips64 + src,
+ reg_info_src))
+ return false;
+
+ Context context;
+ context.type = eContextRegisterLoad;
+
+ return WriteRegister(context, &reg_info_src, data_src);
+ }
+
+ return false;
+}
+
+bool EmulateInstructionMIPS64::Emulate_LUI(llvm::MCInst &insn) {
+ // LUI rt, immediate
+ // GPR[rt] <- sign_extend(immediate << 16)
+
+ const uint32_t imm32 = insn.getOperand(1).getImm() << 16;
+ int64_t imm = SignedBits(imm32, 31, 0);
+ uint8_t rt;
+ Context context;
+
+ rt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ context.SetImmediateSigned(imm);
+ context.type = eContextImmediate;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF,
+ dwarf_zero_mips64 + rt, imm);
+}
+
+bool EmulateInstructionMIPS64::Emulate_DSUBU_DADDU(llvm::MCInst &insn) {
+ // DSUBU sp, <src>, <rt>
+ // DADDU sp, <src>, <rt>
+ // DADDU dst, sp, <rt>
+
+ bool success = false;
+ uint64_t result;
+ uint8_t src, dst, rt;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ dst = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ src = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+
+ /* Check if sp is destination register */
+ if (dst == dwarf_sp_mips64) {
+ rt = m_reg_info->getEncodingValue(insn.getOperand(2).getReg());
+
+ /* read <src> register */
+ uint64_t src_opd_val = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips64 + src, 0, &success);
+ if (!success)
+ return false;
+
+ /* read <rt > register */
+ uint64_t rt_opd_val = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips64 + rt, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "DSUBU") || !strcasecmp(op_name, "SUBU"))
+ result = src_opd_val - rt_opd_val;
+ else
+ result = src_opd_val + rt_opd_val;
+
+ Context context;
+ RegisterInfo reg_info_sp;
+ if (GetRegisterInfo(eRegisterKindDWARF, dwarf_sp_mips64, reg_info_sp))
+ context.SetRegisterPlusOffset(reg_info_sp, rt_opd_val);
+
+ /* We are allocating bytes on stack */
+ context.type = eContextAdjustStackPointer;
+
+ WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_sp_mips64, result);
+
+ return true;
+ } else if (src == dwarf_sp_mips64) {
+ rt = m_reg_info->getEncodingValue(insn.getOperand(2).getReg());
+
+ /* read <src> register */
+ uint64_t src_opd_val = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips64 + src, 0, &success);
+ if (!success)
+ return false;
+
+ /* read <rt> register */
+ uint64_t rt_opd_val = ReadRegisterUnsigned(
+ eRegisterKindDWARF, dwarf_zero_mips64 + rt, 0, &success);
+ if (!success)
+ return false;
+
+ Context context;
+
+ if (!strcasecmp(op_name, "DSUBU") || !strcasecmp(op_name, "SUBU"))
+ result = src_opd_val - rt_opd_val;
+ else
+ result = src_opd_val + rt_opd_val;
+
+ context.SetImmediateSigned(result);
+ context.type = eContextImmediate;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF,
+ dwarf_zero_mips64 + dst, result))
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ Emulate below MIPS branch instructions.
+ BEQ, BNE : Branch on condition
+ BEQL, BNEL : Branch likely
+*/
+bool EmulateInstructionMIPS64::Emulate_BXX_3ops(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs, rt;
+ int64_t offset, pc, rs_val, rt_val, target = 0;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ rt = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+ offset = insn.getOperand(2).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = (int64_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips64 + rs, 0, &success);
+ if (!success)
+ return false;
+
+ rt_val = (int64_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips64 + rt, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BEQ") || !strcasecmp(op_name, "BEQL")
+ || !strcasecmp(op_name, "BEQ64") ) {
+ if (rs_val == rt_val)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BNE") || !strcasecmp(op_name, "BNEL")
+ || !strcasecmp(op_name, "BNE64")) {
+ if (rs_val != rt_val)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ }
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+ context.SetImmediate(offset);
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target);
+}
+
+/*
+ Emulate below MIPS Non-Compact conditional branch and link instructions.
+ BLTZAL, BGEZAL :
+ BLTZALL, BGEZALL : Branch likely
+*/
+bool EmulateInstructionMIPS64::Emulate_Bcond_Link(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs;
+ int64_t offset, pc, target = 0;
+ int64_t rs_val;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = (int64_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips64 + rs, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BLTZAL") || !strcasecmp(op_name, "BLTZALL")) {
+ if (rs_val < 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BGEZAL") ||
+ !strcasecmp(op_name, "BGEZALL")) {
+ if (rs_val >= 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ }
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips64,
+ pc + 8))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionMIPS64::Emulate_BAL(llvm::MCInst &insn) {
+ bool success = false;
+ int64_t offset, pc, target;
+
+ /*
+ * BAL offset
+ * offset = sign_ext (offset << 2)
+ * RA = PC + 8
+ * PC = PC + offset
+ */
+ offset = insn.getOperand(0).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ target = pc + offset;
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips64,
+ pc + 8))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionMIPS64::Emulate_BALC(llvm::MCInst &insn) {
+ bool success = false;
+ int64_t offset, pc, target;
+
+ /*
+ * BALC offset
+ * offset = sign_ext (offset << 2)
+ * RA = PC + 4
+ * PC = PC + 4 + offset
+ */
+ offset = insn.getOperand(0).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ target = pc + offset;
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips64,
+ pc + 4))
+ return false;
+
+ return true;
+}
+
+/*
+ Emulate below MIPS conditional branch and link instructions.
+ BLEZALC, BGEZALC, BLTZALC, BGTZALC, BEQZALC, BNEZALC : Compact branches
+*/
+bool EmulateInstructionMIPS64::Emulate_Bcond_Link_C(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs;
+ int64_t offset, pc, rs_val, target = 0;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = (int64_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips64 + rs, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BLEZALC")) {
+ if (rs_val <= 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BGEZALC")) {
+ if (rs_val >= 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BLTZALC")) {
+ if (rs_val < 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BGTZALC")) {
+ if (rs_val > 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BEQZALC")) {
+ if (rs_val == 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BNEZALC")) {
+ if (rs_val != 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ }
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips64,
+ pc + 4))
+ return false;
+
+ return true;
+}
+
+/*
+ Emulate below MIPS branch instructions.
+ BLTZL, BGEZL, BGTZL, BLEZL : Branch likely
+ BLTZ, BGEZ, BGTZ, BLEZ : Non-compact branches
+*/
+bool EmulateInstructionMIPS64::Emulate_BXX_2ops(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs;
+ int64_t offset, pc, rs_val, target = 0;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = (int64_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips64 + rs, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BLTZL") || !strcasecmp(op_name, "BLTZ")
+ || !strcasecmp(op_name, "BLTZ64")) {
+ if (rs_val < 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BGEZL") || !strcasecmp(op_name, "BGEZ")
+ || !strcasecmp(op_name, "BGEZ64")) {
+ if (rs_val >= 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BGTZL") || !strcasecmp(op_name, "BGTZ")
+ || !strcasecmp(op_name, "BGTZ64")) {
+ if (rs_val > 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BLEZL") || !strcasecmp(op_name, "BLEZ")
+ || !strcasecmp(op_name, "BLEZ64")) {
+ if (rs_val <= 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ }
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+ context.SetImmediate(offset);
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BC(llvm::MCInst &insn) {
+ bool success = false;
+ int64_t offset, pc, target;
+
+ /*
+ * BC offset
+ * offset = sign_ext (offset << 2)
+ * PC = PC + 4 + offset
+ */
+ offset = insn.getOperand(0).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ target = pc + offset;
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target);
+}
+
+static int IsAdd64bitOverflow(int64_t a, int64_t b) {
+ int64_t r = (uint64_t)a + (uint64_t)b;
+ return (a < 0 && b < 0 && r >= 0) || (a >= 0 && b >= 0 && r < 0);
+}
+
+/*
+ Emulate below MIPS branch instructions.
+ BEQC, BNEC, BLTC, BGEC, BLTUC, BGEUC, BOVC, BNVC: Compact branch
+ instructions with no delay slot
+*/
+bool EmulateInstructionMIPS64::Emulate_BXX_3ops_C(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs, rt;
+ int64_t offset, pc, rs_val, rt_val, target = 0;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+ uint32_t current_inst_size = m_insn_info->get(insn.getOpcode()).getSize();
+
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ rt = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+ offset = insn.getOperand(2).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = (int64_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips64 + rs, 0, &success);
+ if (!success)
+ return false;
+
+ rt_val = (int64_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips64 + rt, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BEQC") || !strcasecmp(op_name, "BEQC64")) {
+ if (rs_val == rt_val)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BNEC") || !strcasecmp(op_name, "BNEC64")) {
+ if (rs_val != rt_val)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BLTC") || !strcasecmp(op_name, "BLTC64")) {
+ if (rs_val < rt_val)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BGEC64") || !strcasecmp(op_name, "BGEC")) {
+ if (rs_val >= rt_val)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BLTUC") || !strcasecmp(op_name, "BLTUC64")) {
+ if (rs_val < rt_val)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BGEUC") || !strcasecmp(op_name, "BGEUC64")) {
+ if ((uint32_t)rs_val >= (uint32_t)rt_val)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BOVC")) {
+ if (IsAdd64bitOverflow(rs_val, rt_val))
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BNVC")) {
+ if (!IsAdd64bitOverflow(rs_val, rt_val))
+ target = pc + offset;
+ else
+ target = pc + 4;
+ }
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+ context.SetImmediate(current_inst_size + offset);
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target);
+}
+
+/*
+ Emulate below MIPS branch instructions.
+ BLTZC, BLEZC, BGEZC, BGTZC, BEQZC, BNEZC : Compact Branches
+*/
+bool EmulateInstructionMIPS64::Emulate_BXX_2ops_C(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs;
+ int64_t offset, pc, target = 0;
+ int64_t rs_val;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+ uint32_t current_inst_size = m_insn_info->get(insn.getOpcode()).getSize();
+
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = (int64_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips64 + rs, 0, &success);
+ if (!success)
+ return false;
+
+ if (!strcasecmp(op_name, "BLTZC") || !strcasecmp(op_name, "BLTZC64")) {
+ if (rs_val < 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BLEZC") || !strcasecmp(op_name, "BLEZC64")) {
+ if (rs_val <= 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BGEZC") || !strcasecmp(op_name, "BGEZC64")) {
+ if (rs_val >= 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BGTZC") || !strcasecmp(op_name, "BGTZC64")) {
+ if (rs_val > 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BEQZC") || !strcasecmp(op_name, "BEQZC64")) {
+ if (rs_val == 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ } else if (!strcasecmp(op_name, "BNEZC") || !strcasecmp(op_name, "BNEZC64")) {
+ if (rs_val != 0)
+ target = pc + offset;
+ else
+ target = pc + 4;
+ }
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+ context.SetImmediate(current_inst_size + offset);
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target);
+}
+
+bool EmulateInstructionMIPS64::Emulate_J(llvm::MCInst &insn) {
+ bool success = false;
+ uint64_t offset, pc;
+
+ /*
+ * J offset
+ * offset = sign_ext (offset << 2)
+ * PC = PC[63-28] | offset
+ */
+ offset = insn.getOperand(0).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ /* This is a PC-region branch and not PC-relative */
+ pc = (pc & 0xFFFFFFFFF0000000ULL) | offset;
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ pc);
+}
+
+bool EmulateInstructionMIPS64::Emulate_JAL(llvm::MCInst &insn) {
+ bool success = false;
+ uint64_t offset, target, pc;
+
+ /*
+ * JAL offset
+ * offset = sign_ext (offset << 2)
+ * PC = PC[63-28] | offset
+ */
+ offset = insn.getOperand(0).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ /* This is a PC-region branch and not PC-relative */
+ target = (pc & 0xFFFFFFFFF0000000ULL) | offset;
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips64,
+ pc + 8))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionMIPS64::Emulate_JALR(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs, rt;
+ uint64_t pc, rs_val;
+
+ /*
+ * JALR rt, rs
+ * GPR[rt] = PC + 8
+ * PC = GPR[rs]
+ */
+ rt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ rs = m_reg_info->getEncodingValue(insn.getOperand(1).getReg());
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ rs_val = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips64 + rs, 0,
+ &success);
+ if (!success)
+ return false;
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ rs_val))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF,
+ dwarf_zero_mips64 + rt, pc + 8))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionMIPS64::Emulate_JIALC(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rt;
+ int64_t target, offset, pc, rt_val;
+
+ /*
+ * JIALC rt, offset
+ * offset = sign_ext (offset)
+ * PC = GPR[rt] + offset
+ * RA = PC + 4
+ */
+ rt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ rt_val = (int64_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips64 + rt, 0, &success);
+ if (!success)
+ return false;
+
+ target = rt_val + offset;
+
+ Context context;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target))
+ return false;
+
+ if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_ra_mips64,
+ pc + 4))
+ return false;
+
+ return true;
+}
+
+bool EmulateInstructionMIPS64::Emulate_JIC(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rt;
+ int64_t target, offset, rt_val;
+
+ /*
+ * JIC rt, offset
+ * offset = sign_ext (offset)
+ * PC = GPR[rt] + offset
+ */
+ rt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ rt_val = (int64_t)ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips64 + rt, 0, &success);
+ if (!success)
+ return false;
+
+ target = rt_val + offset;
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target);
+}
+
+bool EmulateInstructionMIPS64::Emulate_JR(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t rs;
+ uint64_t rs_val;
+
+ /*
+ * JR rs
+ * PC = GPR[rs]
+ */
+ rs = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+
+ rs_val = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips64 + rs, 0,
+ &success);
+ if (!success)
+ return false;
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ rs_val);
+}
+
+/*
+ Emulate Branch on FP True/False
+ BC1F, BC1FL : Branch on FP False (L stands for branch likely)
+ BC1T, BC1TL : Branch on FP True (L stands for branch likely)
+*/
+bool EmulateInstructionMIPS64::Emulate_FP_branch(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t cc, fcsr;
+ int64_t pc, offset, target = 0;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ /*
+ * BC1F cc, offset
+ * condition <- (FPConditionCode(cc) == 0)
+ * if condition then
+ * offset = sign_ext (offset)
+ * PC = PC + offset
+ */
+ cc = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ fcsr =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_fcsr_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ /* fcsr[23], fcsr[25-31] are vaild condition bits */
+ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01);
+
+ if (!strcasecmp(op_name, "BC1F") || !strcasecmp(op_name, "BC1FL")) {
+ if ((fcsr & (1 << cc)) == 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BC1T") || !strcasecmp(op_name, "BC1TL")) {
+ if ((fcsr & (1 << cc)) != 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ }
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BC1EQZ(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t ft;
+ uint64_t ft_val;
+ int64_t target, pc, offset;
+
+ /*
+ * BC1EQZ ft, offset
+ * condition <- (FPR[ft].bit0 == 0)
+ * if condition then
+ * offset = sign_ext (offset)
+ * PC = PC + 4 + offset
+ */
+ ft = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ ft_val = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips64 + ft, 0,
+ &success);
+ if (!success)
+ return false;
+
+ if ((ft_val & 1) == 0)
+ target = pc + 4 + offset;
+ else
+ target = pc + 8;
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BC1NEZ(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t ft;
+ uint64_t ft_val;
+ int64_t target, pc, offset;
+
+ /*
+ * BC1NEZ ft, offset
+ * condition <- (FPR[ft].bit0 != 0)
+ * if condition then
+ * offset = sign_ext (offset)
+ * PC = PC + 4 + offset
+ */
+ ft = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ ft_val = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips64 + ft, 0,
+ &success);
+ if (!success)
+ return false;
+
+ if ((ft_val & 1) != 0)
+ target = pc + 4 + offset;
+ else
+ target = pc + 8;
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target);
+}
+
+/*
+ Emulate MIPS-3D Branch instructions
+ BC1ANY2F, BC1ANY2T : Branch on Any of Two Floating Point Condition Codes
+ False/True
+ BC1ANY4F, BC1ANY4T : Branch on Any of Four Floating Point Condition Codes
+ False/True
+*/
+bool EmulateInstructionMIPS64::Emulate_3D_branch(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t cc, fcsr;
+ int64_t pc, offset, target = 0;
+ const char *op_name = m_insn_info->getName(insn.getOpcode()).data();
+
+ cc = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ offset = insn.getOperand(1).getImm();
+
+ pc = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ fcsr = (uint32_t)ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_fcsr_mips64,
+ 0, &success);
+ if (!success)
+ return false;
+
+ /* fcsr[23], fcsr[25-31] are vaild condition bits */
+ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01);
+
+ if (!strcasecmp(op_name, "BC1ANY2F")) {
+ /* if any one bit is 0 */
+ if (((fcsr >> cc) & 3) != 3)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BC1ANY2T")) {
+ /* if any one bit is 1 */
+ if (((fcsr >> cc) & 3) != 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BC1ANY4F")) {
+ /* if any one bit is 0 */
+ if (((fcsr >> cc) & 0xf) != 0xf)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ } else if (!strcasecmp(op_name, "BC1ANY4T")) {
+ /* if any one bit is 1 */
+ if (((fcsr >> cc) & 0xf) != 0)
+ target = pc + offset;
+ else
+ target = pc + 8;
+ }
+
+ Context context;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BNZB(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 1, true);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BNZH(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 2, true);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BNZW(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 4, true);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BNZD(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 8, true);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BZB(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 1, false);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BZH(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 2, false);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BZW(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 4, false);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BZD(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_DF(insn, 8, false);
+}
+
+bool EmulateInstructionMIPS64::Emulate_MSA_Branch_DF(llvm::MCInst &insn,
+ int element_byte_size,
+ bool bnz) {
+ bool success = false, branch_hit = true;
+ int64_t target = 0;
+ RegisterValue reg_value;
+ const uint8_t *ptr = nullptr;
+
+ uint32_t wt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ int64_t offset = insn.getOperand(1).getImm();
+
+ int64_t pc =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ if (ReadRegister(eRegisterKindDWARF, dwarf_w0_mips64 + wt, reg_value))
+ ptr = (const uint8_t *)reg_value.GetBytes();
+ else
+ return false;
+
+ for (int i = 0; i < 16 / element_byte_size; i++) {
+ switch (element_byte_size) {
+ case 1:
+ if ((*ptr == 0 && bnz) || (*ptr != 0 && !bnz))
+ branch_hit = false;
+ break;
+ case 2:
+ if ((*(const uint16_t *)ptr == 0 && bnz) ||
+ (*(const uint16_t *)ptr != 0 && !bnz))
+ branch_hit = false;
+ break;
+ case 4:
+ if ((*(const uint32_t *)ptr == 0 && bnz) ||
+ (*(const uint32_t *)ptr != 0 && !bnz))
+ branch_hit = false;
+ break;
+ case 8:
+ if ((*(const uint64_t *)ptr == 0 && bnz) ||
+ (*(const uint64_t *)ptr != 0 && !bnz))
+ branch_hit = false;
+ break;
+ }
+ if (!branch_hit)
+ break;
+ ptr = ptr + element_byte_size;
+ }
+
+ if (branch_hit)
+ target = pc + offset;
+ else
+ target = pc + 8;
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BNZV(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_V(insn, true);
+}
+
+bool EmulateInstructionMIPS64::Emulate_BZV(llvm::MCInst &insn) {
+ return Emulate_MSA_Branch_V(insn, false);
+}
+
+bool EmulateInstructionMIPS64::Emulate_MSA_Branch_V(llvm::MCInst &insn,
+ bool bnz) {
+ bool success = false;
+ int64_t target = 0;
+ llvm::APInt wr_val = llvm::APInt::getNullValue(128);
+ llvm::APInt fail_value = llvm::APInt::getMaxValue(128);
+ llvm::APInt zero_value = llvm::APInt::getNullValue(128);
+ RegisterValue reg_value;
+
+ uint32_t wt = m_reg_info->getEncodingValue(insn.getOperand(0).getReg());
+ int64_t offset = insn.getOperand(1).getImm();
+
+ int64_t pc =
+ ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc_mips64, 0, &success);
+ if (!success)
+ return false;
+
+ if (ReadRegister(eRegisterKindDWARF, dwarf_w0_mips64 + wt, reg_value))
+ wr_val = reg_value.GetAsUInt128(fail_value);
+ else
+ return false;
+
+ if ((llvm::APInt::isSameValue(zero_value, wr_val) && !bnz) ||
+ (!llvm::APInt::isSameValue(zero_value, wr_val) && bnz))
+ target = pc + offset;
+ else
+ target = pc + 8;
+
+ Context context;
+ context.type = eContextRelativeBranchImmediate;
+
+ return WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc_mips64,
+ target);
+}
+
+bool EmulateInstructionMIPS64::Emulate_LDST_Imm(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t base;
+ int64_t imm, address;
+ Context bad_vaddr_context;
+
+ uint32_t num_operands = insn.getNumOperands();
+ base =
+ m_reg_info->getEncodingValue(insn.getOperand(num_operands - 2).getReg());
+ imm = insn.getOperand(num_operands - 1).getImm();
+
+ RegisterInfo reg_info_base;
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base,
+ reg_info_base))
+ return false;
+
+ /* read base register */
+ address = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips + base, 0,
+ &success);
+ if (!success)
+ return false;
+
+ /* destination address */
+ address = address + imm;
+
+ /* Set the bad_vaddr register with base address used in the instruction */
+ bad_vaddr_context.type = eContextInvalid;
+ WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips,
+ address);
+
+ return true;
+}
+
+bool EmulateInstructionMIPS64::Emulate_LDST_Reg(llvm::MCInst &insn) {
+ bool success = false;
+ uint32_t base, index;
+ int64_t address, index_address;
+ Context bad_vaddr_context;
+
+ uint32_t num_operands = insn.getNumOperands();
+ base =
+ m_reg_info->getEncodingValue(insn.getOperand(num_operands - 2).getReg());
+ index =
+ m_reg_info->getEncodingValue(insn.getOperand(num_operands - 1).getReg());
+
+ RegisterInfo reg_info_base, reg_info_index;
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + base,
+ reg_info_base))
+ return false;
+
+ if (!GetRegisterInfo(eRegisterKindDWARF, dwarf_zero_mips + index,
+ reg_info_index))
+ return false;
+
+ /* read base register */
+ address = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_zero_mips + base, 0,
+ &success);
+ if (!success)
+ return false;
+
+ /* read index register */
+ index_address = ReadRegisterUnsigned(eRegisterKindDWARF,
+ dwarf_zero_mips + index, 0, &success);
+ if (!success)
+ return false;
+
+ /* destination address */
+ address = address + index_address;
+
+ /* Set the bad_vaddr register with base address used in the instruction */
+ bad_vaddr_context.type = eContextInvalid;
+ WriteRegisterUnsigned(bad_vaddr_context, eRegisterKindDWARF, dwarf_bad_mips,
+ address);
+
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.h b/contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.h
new file mode 100644
index 000000000000..953a0935bd06
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.h
@@ -0,0 +1,182 @@
+//===-- EmulateInstructionMIPS64.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 EmulateInstructionMIPS64_h_
+#define EmulateInstructionMIPS64_h_
+
+#include "lldb/Core/EmulateInstruction.h"
+#include "lldb/Interpreter/OptionValue.h"
+#include "lldb/Utility/Status.h"
+
+namespace llvm {
+class MCDisassembler;
+class MCSubtargetInfo;
+class MCRegisterInfo;
+class MCAsmInfo;
+class MCContext;
+class MCInstrInfo;
+class MCInst;
+} // namespace llvm
+
+class EmulateInstructionMIPS64 : public lldb_private::EmulateInstruction {
+public:
+ EmulateInstructionMIPS64(const lldb_private::ArchSpec &arch);
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::EmulateInstruction *
+ CreateInstance(const lldb_private::ArchSpec &arch,
+ lldb_private::InstructionType inst_type);
+
+ static bool SupportsEmulatingInstructionsOfTypeStatic(
+ lldb_private::InstructionType inst_type) {
+ switch (inst_type) {
+ case lldb_private::eInstructionTypeAny:
+ case lldb_private::eInstructionTypePrologueEpilogue:
+ case lldb_private::eInstructionTypePCModifying:
+ return true;
+
+ case lldb_private::eInstructionTypeAll:
+ return false;
+ }
+ return false;
+ }
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ bool SetTargetTriple(const lldb_private::ArchSpec &arch) override;
+
+ bool SupportsEmulatingInstructionsOfType(
+ lldb_private::InstructionType inst_type) override {
+ return SupportsEmulatingInstructionsOfTypeStatic(inst_type);
+ }
+
+ bool ReadInstruction() override;
+
+ bool EvaluateInstruction(uint32_t evaluate_options) override;
+
+ bool TestEmulation(lldb_private::Stream *out_stream,
+ lldb_private::ArchSpec &arch,
+ lldb_private::OptionValueDictionary *test_data) override {
+ return false;
+ }
+
+ bool GetRegisterInfo(lldb::RegisterKind reg_kind, uint32_t reg_num,
+ lldb_private::RegisterInfo &reg_info) override;
+
+ bool
+ CreateFunctionEntryUnwind(lldb_private::UnwindPlan &unwind_plan) override;
+
+protected:
+ typedef struct {
+ const char *op_name;
+ bool (EmulateInstructionMIPS64::*callback)(llvm::MCInst &insn);
+ const char *insn_name;
+ } MipsOpcode;
+
+ static MipsOpcode *GetOpcodeForInstruction(const char *op_name);
+
+ bool Emulate_DADDiu(llvm::MCInst &insn);
+
+ bool Emulate_DSUBU_DADDU(llvm::MCInst &insn);
+
+ bool Emulate_LUI(llvm::MCInst &insn);
+
+ bool Emulate_SD(llvm::MCInst &insn);
+
+ bool Emulate_LD(llvm::MCInst &insn);
+
+ bool Emulate_LDST_Imm(llvm::MCInst &insn);
+
+ bool Emulate_LDST_Reg(llvm::MCInst &insn);
+
+ bool Emulate_BXX_3ops(llvm::MCInst &insn);
+
+ bool Emulate_BXX_3ops_C(llvm::MCInst &insn);
+
+ bool Emulate_BXX_2ops(llvm::MCInst &insn);
+
+ bool Emulate_BXX_2ops_C(llvm::MCInst &insn);
+
+ bool Emulate_Bcond_Link_C(llvm::MCInst &insn);
+
+ bool Emulate_Bcond_Link(llvm::MCInst &insn);
+
+ bool Emulate_FP_branch(llvm::MCInst &insn);
+
+ bool Emulate_3D_branch(llvm::MCInst &insn);
+
+ bool Emulate_BAL(llvm::MCInst &insn);
+
+ bool Emulate_BALC(llvm::MCInst &insn);
+
+ bool Emulate_BC(llvm::MCInst &insn);
+
+ bool Emulate_J(llvm::MCInst &insn);
+
+ bool Emulate_JAL(llvm::MCInst &insn);
+
+ bool Emulate_JALR(llvm::MCInst &insn);
+
+ bool Emulate_JIALC(llvm::MCInst &insn);
+
+ bool Emulate_JIC(llvm::MCInst &insn);
+
+ bool Emulate_JR(llvm::MCInst &insn);
+
+ bool Emulate_BC1EQZ(llvm::MCInst &insn);
+
+ bool Emulate_BC1NEZ(llvm::MCInst &insn);
+
+ bool Emulate_BNZB(llvm::MCInst &insn);
+
+ bool Emulate_BNZH(llvm::MCInst &insn);
+
+ bool Emulate_BNZW(llvm::MCInst &insn);
+
+ bool Emulate_BNZD(llvm::MCInst &insn);
+
+ bool Emulate_BZB(llvm::MCInst &insn);
+
+ bool Emulate_BZH(llvm::MCInst &insn);
+
+ bool Emulate_BZW(llvm::MCInst &insn);
+
+ bool Emulate_BZD(llvm::MCInst &insn);
+
+ bool Emulate_MSA_Branch_DF(llvm::MCInst &insn, int element_byte_size,
+ bool bnz);
+
+ bool Emulate_BNZV(llvm::MCInst &insn);
+
+ bool Emulate_BZV(llvm::MCInst &insn);
+
+ bool Emulate_MSA_Branch_V(llvm::MCInst &insn, bool bnz);
+
+ bool nonvolatile_reg_p(uint64_t regnum);
+
+ const char *GetRegisterName(unsigned reg_num, bool altnernate_name);
+
+private:
+ std::unique_ptr<llvm::MCDisassembler> m_disasm;
+ std::unique_ptr<llvm::MCSubtargetInfo> m_subtype_info;
+ std::unique_ptr<llvm::MCRegisterInfo> m_reg_info;
+ std::unique_ptr<llvm::MCAsmInfo> m_asm_info;
+ std::unique_ptr<llvm::MCContext> m_context;
+ std::unique_ptr<llvm::MCInstrInfo> m_insn_info;
+};
+
+#endif // EmulateInstructionMIPS64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.cpp b/contrib/llvm-project/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.cpp
new file mode 100644
index 000000000000..c77fa04fc7d7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.cpp
@@ -0,0 +1,397 @@
+//===-- EmulateInstructionPPC64.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 "EmulateInstructionPPC64.h"
+
+#include <stdlib.h>
+
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/ConstString.h"
+
+#include "Plugins/Process/Utility/lldb-ppc64le-register-enums.h"
+
+#define DECLARE_REGISTER_INFOS_PPC64LE_STRUCT
+#include "Plugins/Process/Utility/RegisterInfos_ppc64le.h"
+
+#include "Plugins/Process/Utility/InstructionUtils.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+EmulateInstructionPPC64::EmulateInstructionPPC64(const ArchSpec &arch)
+ : EmulateInstruction(arch) {}
+
+void EmulateInstructionPPC64::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void EmulateInstructionPPC64::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+ConstString EmulateInstructionPPC64::GetPluginNameStatic() {
+ ConstString g_plugin_name("lldb.emulate-instruction.ppc64");
+ return g_plugin_name;
+}
+
+ConstString EmulateInstructionPPC64::GetPluginName() {
+ static ConstString g_plugin_name("EmulateInstructionPPC64");
+ return g_plugin_name;
+}
+
+const char *EmulateInstructionPPC64::GetPluginDescriptionStatic() {
+ return "Emulate instructions for the PPC64 architecture.";
+}
+
+EmulateInstruction *
+EmulateInstructionPPC64::CreateInstance(const ArchSpec &arch,
+ InstructionType inst_type) {
+ if (EmulateInstructionPPC64::SupportsEmulatingInstructionsOfTypeStatic(
+ inst_type))
+ if (arch.GetTriple().isPPC64())
+ return new EmulateInstructionPPC64(arch);
+
+ return nullptr;
+}
+
+bool EmulateInstructionPPC64::SetTargetTriple(const ArchSpec &arch) {
+ return arch.GetTriple().isPPC64();
+}
+
+static bool LLDBTableGetRegisterInfo(uint32_t reg_num, RegisterInfo &reg_info) {
+ if (reg_num >= llvm::array_lengthof(g_register_infos_ppc64le))
+ return false;
+ reg_info = g_register_infos_ppc64le[reg_num];
+ return true;
+}
+
+bool EmulateInstructionPPC64::GetRegisterInfo(RegisterKind reg_kind,
+ uint32_t reg_num,
+ RegisterInfo &reg_info) {
+ if (reg_kind == eRegisterKindGeneric) {
+ switch (reg_num) {
+ case LLDB_REGNUM_GENERIC_PC:
+ reg_kind = eRegisterKindLLDB;
+ reg_num = gpr_pc_ppc64le;
+ break;
+ case LLDB_REGNUM_GENERIC_SP:
+ reg_kind = eRegisterKindLLDB;
+ reg_num = gpr_r1_ppc64le;
+ break;
+ case LLDB_REGNUM_GENERIC_RA:
+ reg_kind = eRegisterKindLLDB;
+ reg_num = gpr_lr_ppc64le;
+ break;
+ case LLDB_REGNUM_GENERIC_FLAGS:
+ reg_kind = eRegisterKindLLDB;
+ reg_num = gpr_cr_ppc64le;
+ break;
+
+ default:
+ return false;
+ }
+ }
+
+ if (reg_kind == eRegisterKindLLDB)
+ return LLDBTableGetRegisterInfo(reg_num, reg_info);
+ return false;
+}
+
+bool EmulateInstructionPPC64::ReadInstruction() {
+ bool success = false;
+ m_addr = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC,
+ LLDB_INVALID_ADDRESS, &success);
+ if (success) {
+ Context ctx;
+ ctx.type = eContextReadOpcode;
+ ctx.SetNoArgs();
+ m_opcode.SetOpcode32(ReadMemoryUnsigned(ctx, m_addr, 4, 0, &success),
+ GetByteOrder());
+ }
+ if (!success)
+ m_addr = LLDB_INVALID_ADDRESS;
+ return success;
+}
+
+bool EmulateInstructionPPC64::CreateFunctionEntryUnwind(
+ UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ unwind_plan.SetRegisterKind(eRegisterKindLLDB);
+
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ // Our previous Call Frame Address is the stack pointer
+ row->GetCFAValue().SetIsRegisterPlusOffset(gpr_r1_ppc64le, 0);
+
+ unwind_plan.AppendRow(row);
+ unwind_plan.SetSourceName("EmulateInstructionPPC64");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);
+ unwind_plan.SetReturnAddressRegister(gpr_lr_ppc64le);
+ return true;
+}
+
+EmulateInstructionPPC64::Opcode *
+EmulateInstructionPPC64::GetOpcodeForInstruction(uint32_t opcode) {
+ static EmulateInstructionPPC64::Opcode g_opcodes[] = {
+ {0xfc0007ff, 0x7c0002a6, &EmulateInstructionPPC64::EmulateMFSPR,
+ "mfspr RT, SPR"},
+ {0xfc000003, 0xf8000000, &EmulateInstructionPPC64::EmulateSTD,
+ "std RS, DS(RA)"},
+ {0xfc000003, 0xf8000001, &EmulateInstructionPPC64::EmulateSTD,
+ "stdu RS, DS(RA)"},
+ {0xfc0007fe, 0x7c000378, &EmulateInstructionPPC64::EmulateOR,
+ "or RA, RS, RB"},
+ {0xfc000000, 0x38000000, &EmulateInstructionPPC64::EmulateADDI,
+ "addi RT, RA, SI"},
+ {0xfc000003, 0xe8000000, &EmulateInstructionPPC64::EmulateLD,
+ "ld RT, DS(RA)"}};
+ static const size_t k_num_ppc_opcodes = llvm::array_lengthof(g_opcodes);
+
+ for (size_t i = 0; i < k_num_ppc_opcodes; ++i) {
+ if ((g_opcodes[i].mask & opcode) == g_opcodes[i].value)
+ return &g_opcodes[i];
+ }
+ return nullptr;
+}
+
+bool EmulateInstructionPPC64::EvaluateInstruction(uint32_t evaluate_options) {
+ const uint32_t opcode = m_opcode.GetOpcode32();
+ // LLDB_LOG(log, "PPC64::EvaluateInstruction: opcode={0:X+8}", opcode);
+ Opcode *opcode_data = GetOpcodeForInstruction(opcode);
+ if (!opcode_data)
+ return false;
+
+ // LLDB_LOG(log, "PPC64::EvaluateInstruction: {0}", opcode_data->name);
+ const bool auto_advance_pc =
+ evaluate_options & eEmulateInstructionOptionAutoAdvancePC;
+
+ bool success = false;
+
+ uint32_t orig_pc_value = 0;
+ if (auto_advance_pc) {
+ orig_pc_value =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success);
+ if (!success)
+ return false;
+ }
+
+ // Call the Emulate... function.
+ success = (this->*opcode_data->callback)(opcode);
+ if (!success)
+ return false;
+
+ if (auto_advance_pc) {
+ uint32_t new_pc_value =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success);
+ if (!success)
+ return false;
+
+ if (auto_advance_pc && (new_pc_value == orig_pc_value)) {
+ EmulateInstruction::Context context;
+ context.type = eContextAdvancePC;
+ context.SetNoArgs();
+ if (!WriteRegisterUnsigned(context, eRegisterKindLLDB, gpr_pc_ppc64le,
+ orig_pc_value + 4))
+ return false;
+ }
+ }
+ return true;
+}
+
+bool EmulateInstructionPPC64::EmulateMFSPR(uint32_t opcode) {
+ uint32_t rt = Bits32(opcode, 25, 21);
+ uint32_t spr = Bits32(opcode, 20, 11);
+
+ enum { SPR_LR = 0x100 };
+
+ // For now, we're only insterested in 'mfspr r0, lr'
+ if (rt != gpr_r0_ppc64le || spr != SPR_LR)
+ return false;
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ LLDB_LOG(log, "EmulateMFSPR: {0:X+8}: mfspr r0, lr", m_addr);
+
+ bool success;
+ uint64_t lr =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_lr_ppc64le, 0, &success);
+ if (!success)
+ return false;
+ Context context;
+ context.type = eContextWriteRegisterRandomBits;
+ WriteRegisterUnsigned(context, eRegisterKindLLDB, gpr_r0_ppc64le, lr);
+ LLDB_LOG(log, "EmulateMFSPR: success!");
+ return true;
+}
+
+bool EmulateInstructionPPC64::EmulateLD(uint32_t opcode) {
+ uint32_t rt = Bits32(opcode, 25, 21);
+ uint32_t ra = Bits32(opcode, 20, 16);
+ uint32_t ds = Bits32(opcode, 15, 2);
+
+ int32_t ids = llvm::SignExtend32<16>(ds << 2);
+
+ // For now, tracking only loads from 0(r1) to r1 (0(r1) is the ABI defined
+ // location to save previous SP)
+ if (ra != gpr_r1_ppc64le || rt != gpr_r1_ppc64le || ids != 0)
+ return false;
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ LLDB_LOG(log, "EmulateLD: {0:X+8}: ld r{1}, {2}(r{3})", m_addr, rt, ids, ra);
+
+ RegisterInfo r1_info;
+ if (!GetRegisterInfo(eRegisterKindLLDB, gpr_r1_ppc64le, r1_info))
+ return false;
+
+ // restore SP
+ Context ctx;
+ ctx.type = eContextRestoreStackPointer;
+ ctx.SetRegisterToRegisterPlusOffset(r1_info, r1_info, 0);
+
+ WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_r1_ppc64le, 0);
+ LLDB_LOG(log, "EmulateLD: success!");
+ return true;
+}
+
+bool EmulateInstructionPPC64::EmulateSTD(uint32_t opcode) {
+ uint32_t rs = Bits32(opcode, 25, 21);
+ uint32_t ra = Bits32(opcode, 20, 16);
+ uint32_t ds = Bits32(opcode, 15, 2);
+ uint32_t u = Bits32(opcode, 1, 0);
+
+ // For now, tracking only stores to r1
+ if (ra != gpr_r1_ppc64le)
+ return false;
+ // ... and only stores of SP, FP and LR (moved into r0 by a previous mfspr)
+ if (rs != gpr_r1_ppc64le && rs != gpr_r31_ppc64le && rs != gpr_r30_ppc64le &&
+ rs != gpr_r0_ppc64le)
+ return false;
+
+ bool success;
+ uint64_t rs_val = ReadRegisterUnsigned(eRegisterKindLLDB, rs, 0, &success);
+ if (!success)
+ return false;
+
+ int32_t ids = llvm::SignExtend32<16>(ds << 2);
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ LLDB_LOG(log, "EmulateSTD: {0:X+8}: std{1} r{2}, {3}(r{4})", m_addr,
+ u ? "u" : "", rs, ids, ra);
+
+ // Make sure that r0 is really holding LR value (this won't catch unlikely
+ // cases, such as r0 being overwritten after mfspr)
+ uint32_t rs_num = rs;
+ if (rs == gpr_r0_ppc64le) {
+ uint64_t lr =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_lr_ppc64le, 0, &success);
+ if (!success || lr != rs_val)
+ return false;
+ rs_num = gpr_lr_ppc64le;
+ }
+
+ // set context
+ RegisterInfo rs_info;
+ if (!GetRegisterInfo(eRegisterKindLLDB, rs_num, rs_info))
+ return false;
+ RegisterInfo ra_info;
+ if (!GetRegisterInfo(eRegisterKindLLDB, ra, ra_info))
+ return false;
+
+ Context ctx;
+ ctx.type = eContextPushRegisterOnStack;
+ ctx.SetRegisterToRegisterPlusOffset(rs_info, ra_info, ids);
+
+ // store
+ uint64_t ra_val = ReadRegisterUnsigned(eRegisterKindLLDB, ra, 0, &success);
+ if (!success)
+ return false;
+
+ lldb::addr_t addr = ra_val + ids;
+ WriteMemory(ctx, addr, &rs_val, sizeof(rs_val));
+
+ // update RA?
+ if (u) {
+ Context ctx;
+ // NOTE Currently, RA will always be equal to SP(r1)
+ ctx.type = eContextAdjustStackPointer;
+ WriteRegisterUnsigned(ctx, eRegisterKindLLDB, ra, addr);
+ }
+
+ LLDB_LOG(log, "EmulateSTD: success!");
+ return true;
+}
+
+bool EmulateInstructionPPC64::EmulateOR(uint32_t opcode) {
+ uint32_t rs = Bits32(opcode, 25, 21);
+ uint32_t ra = Bits32(opcode, 20, 16);
+ uint32_t rb = Bits32(opcode, 15, 11);
+
+ // to be safe, process only the known 'mr r31/r30, r1' prologue instructions
+ if (m_fp != LLDB_INVALID_REGNUM || rs != rb ||
+ (ra != gpr_r30_ppc64le && ra != gpr_r31_ppc64le) || rb != gpr_r1_ppc64le)
+ return false;
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ LLDB_LOG(log, "EmulateOR: {0:X+8}: mr r{1}, r{2}", m_addr, ra, rb);
+
+ // set context
+ RegisterInfo ra_info;
+ if (!GetRegisterInfo(eRegisterKindLLDB, ra, ra_info))
+ return false;
+
+ Context ctx;
+ ctx.type = eContextSetFramePointer;
+ ctx.SetRegister(ra_info);
+
+ // move
+ bool success;
+ uint64_t rb_val = ReadRegisterUnsigned(eRegisterKindLLDB, rb, 0, &success);
+ if (!success)
+ return false;
+ WriteRegisterUnsigned(ctx, eRegisterKindLLDB, ra, rb_val);
+ m_fp = ra;
+ LLDB_LOG(log, "EmulateOR: success!");
+ return true;
+}
+
+bool EmulateInstructionPPC64::EmulateADDI(uint32_t opcode) {
+ uint32_t rt = Bits32(opcode, 25, 21);
+ uint32_t ra = Bits32(opcode, 20, 16);
+ uint32_t si = Bits32(opcode, 15, 0);
+
+ // handle stack adjustments only
+ // (this is a typical epilogue operation, with ra == r1. If it's
+ // something else, then we won't know the correct value of ra)
+ if (rt != gpr_r1_ppc64le || ra != gpr_r1_ppc64le)
+ return false;
+
+ int32_t si_val = llvm::SignExtend32<16>(si);
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ LLDB_LOG(log, "EmulateADDI: {0:X+8}: addi r1, r1, {1}", m_addr, si_val);
+
+ // set context
+ RegisterInfo r1_info;
+ if (!GetRegisterInfo(eRegisterKindLLDB, gpr_r1_ppc64le, r1_info))
+ return false;
+
+ Context ctx;
+ ctx.type = eContextRestoreStackPointer;
+ ctx.SetRegisterToRegisterPlusOffset(r1_info, r1_info, 0);
+
+ // adjust SP
+ bool success;
+ uint64_t r1 =
+ ReadRegisterUnsigned(eRegisterKindLLDB, gpr_r1_ppc64le, 0, &success);
+ if (!success)
+ return false;
+ WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_r1_ppc64le, r1 + si_val);
+ LLDB_LOG(log, "EmulateADDI: success!");
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h b/contrib/llvm-project/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h
new file mode 100644
index 000000000000..bf239770b933
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h
@@ -0,0 +1,92 @@
+//===-- EmulateInstructionPPC64.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 EmulateInstructionPPC64_h_
+#define EmulateInstructionPPC64_h_
+
+#include "lldb/Core/EmulateInstruction.h"
+#include "lldb/Interpreter/OptionValue.h"
+#include "lldb/Utility/Log.h"
+
+namespace lldb_private {
+
+class EmulateInstructionPPC64 : public EmulateInstruction {
+public:
+ EmulateInstructionPPC64(const ArchSpec &arch);
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static EmulateInstruction *CreateInstance(const ArchSpec &arch,
+ InstructionType inst_type);
+
+ static bool
+ SupportsEmulatingInstructionsOfTypeStatic(InstructionType inst_type) {
+ switch (inst_type) {
+ case eInstructionTypeAny:
+ case eInstructionTypePrologueEpilogue:
+ return true;
+
+ case eInstructionTypePCModifying:
+ case eInstructionTypeAll:
+ return false;
+ }
+ return false;
+ }
+
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ bool SetTargetTriple(const ArchSpec &arch) override;
+
+ bool SupportsEmulatingInstructionsOfType(InstructionType inst_type) override {
+ return SupportsEmulatingInstructionsOfTypeStatic(inst_type);
+ }
+
+ bool ReadInstruction() override;
+
+ bool EvaluateInstruction(uint32_t evaluate_options) override;
+
+ bool TestEmulation(Stream *out_stream, ArchSpec &arch,
+ OptionValueDictionary *test_data) override {
+ return false;
+ }
+
+ bool GetRegisterInfo(lldb::RegisterKind reg_kind, uint32_t reg_num,
+ RegisterInfo &reg_info) override;
+
+ bool CreateFunctionEntryUnwind(UnwindPlan &unwind_plan) override;
+
+private:
+ struct Opcode {
+ uint32_t mask;
+ uint32_t value;
+ bool (EmulateInstructionPPC64::*callback)(uint32_t opcode);
+ const char *name;
+ };
+
+ uint32_t m_fp = LLDB_INVALID_REGNUM;
+
+ Opcode *GetOpcodeForInstruction(uint32_t opcode);
+
+ bool EmulateMFSPR(uint32_t opcode);
+ bool EmulateLD(uint32_t opcode);
+ bool EmulateSTD(uint32_t opcode);
+ bool EmulateOR(uint32_t opcode);
+ bool EmulateADDI(uint32_t opcode);
+};
+
+} // namespace lldb_private
+
+#endif // EmulateInstructionPPC64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/ASan/ASanRuntime.cpp b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/ASan/ASanRuntime.cpp
new file mode 100644
index 000000000000..c8ac04641e68
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/ASan/ASanRuntime.cpp
@@ -0,0 +1,325 @@
+//===-- ASanRuntime.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 "ASanRuntime.h"
+
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginInterface.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/InstrumentationRuntimeStopInfo.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Stream.h"
+
+#include "llvm/ADT/StringSwitch.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+lldb::InstrumentationRuntimeSP
+AddressSanitizerRuntime::CreateInstance(const lldb::ProcessSP &process_sp) {
+ return InstrumentationRuntimeSP(new AddressSanitizerRuntime(process_sp));
+}
+
+void AddressSanitizerRuntime::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "AddressSanitizer instrumentation runtime plugin.",
+ CreateInstance, GetTypeStatic);
+}
+
+void AddressSanitizerRuntime::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString AddressSanitizerRuntime::GetPluginNameStatic() {
+ return ConstString("AddressSanitizer");
+}
+
+lldb::InstrumentationRuntimeType AddressSanitizerRuntime::GetTypeStatic() {
+ return eInstrumentationRuntimeTypeAddressSanitizer;
+}
+
+AddressSanitizerRuntime::~AddressSanitizerRuntime() { Deactivate(); }
+
+const RegularExpression &
+AddressSanitizerRuntime::GetPatternForRuntimeLibrary() {
+ // FIXME: This shouldn't include the "dylib" suffix.
+ static RegularExpression regex(
+ llvm::StringRef("libclang_rt.asan_(.*)_dynamic\\.dylib"));
+ return regex;
+}
+
+bool AddressSanitizerRuntime::CheckIfRuntimeIsValid(
+ const lldb::ModuleSP module_sp) {
+ const Symbol *symbol = module_sp->FindFirstSymbolWithNameAndType(
+ ConstString("__asan_get_alloc_stack"), lldb::eSymbolTypeAny);
+
+ return symbol != nullptr;
+}
+
+const char *address_sanitizer_retrieve_report_data_prefix = R"(
+extern "C"
+{
+int __asan_report_present();
+void *__asan_get_report_pc();
+void *__asan_get_report_bp();
+void *__asan_get_report_sp();
+void *__asan_get_report_address();
+const char *__asan_get_report_description();
+int __asan_get_report_access_type();
+size_t __asan_get_report_access_size();
+}
+)";
+
+const char *address_sanitizer_retrieve_report_data_command = R"(
+struct {
+ int present;
+ int access_type;
+ void *pc;
+ void *bp;
+ void *sp;
+ void *address;
+ size_t access_size;
+ const char *description;
+} t;
+
+t.present = __asan_report_present();
+t.access_type = __asan_get_report_access_type();
+t.pc = __asan_get_report_pc();
+t.bp = __asan_get_report_bp();
+t.sp = __asan_get_report_sp();
+t.address = __asan_get_report_address();
+t.access_size = __asan_get_report_access_size();
+t.description = __asan_get_report_description();
+t
+)";
+
+StructuredData::ObjectSP AddressSanitizerRuntime::RetrieveReportData() {
+ ProcessSP process_sp = GetProcessSP();
+ if (!process_sp)
+ return StructuredData::ObjectSP();
+
+ ThreadSP thread_sp =
+ process_sp->GetThreadList().GetExpressionExecutionThread();
+ StackFrameSP frame_sp = thread_sp->GetSelectedFrame();
+
+ if (!frame_sp)
+ return StructuredData::ObjectSP();
+
+ EvaluateExpressionOptions options;
+ options.SetUnwindOnError(true);
+ options.SetTryAllThreads(true);
+ options.SetStopOthers(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetTimeout(process_sp->GetUtilityExpressionTimeout());
+ options.SetPrefix(address_sanitizer_retrieve_report_data_prefix);
+ options.SetAutoApplyFixIts(false);
+ options.SetLanguage(eLanguageTypeObjC_plus_plus);
+
+ ValueObjectSP return_value_sp;
+ ExecutionContext exe_ctx;
+ Status eval_error;
+ frame_sp->CalculateExecutionContext(exe_ctx);
+ ExpressionResults result = UserExpression::Evaluate(
+ exe_ctx, options, address_sanitizer_retrieve_report_data_command, "",
+ return_value_sp, eval_error);
+ if (result != eExpressionCompleted) {
+ process_sp->GetTarget().GetDebugger().GetAsyncOutputStream()->Printf(
+ "Warning: Cannot evaluate AddressSanitizer expression:\n%s\n",
+ eval_error.AsCString());
+ return StructuredData::ObjectSP();
+ }
+
+ int present = return_value_sp->GetValueForExpressionPath(".present")
+ ->GetValueAsUnsigned(0);
+ if (present != 1)
+ return StructuredData::ObjectSP();
+
+ addr_t pc =
+ return_value_sp->GetValueForExpressionPath(".pc")->GetValueAsUnsigned(0);
+ /* commented out because rdar://problem/18533301
+ addr_t bp =
+ return_value_sp->GetValueForExpressionPath(".bp")->GetValueAsUnsigned(0);
+ addr_t sp =
+ return_value_sp->GetValueForExpressionPath(".sp")->GetValueAsUnsigned(0);
+ */
+ addr_t address = return_value_sp->GetValueForExpressionPath(".address")
+ ->GetValueAsUnsigned(0);
+ addr_t access_type =
+ return_value_sp->GetValueForExpressionPath(".access_type")
+ ->GetValueAsUnsigned(0);
+ addr_t access_size =
+ return_value_sp->GetValueForExpressionPath(".access_size")
+ ->GetValueAsUnsigned(0);
+ addr_t description_ptr =
+ return_value_sp->GetValueForExpressionPath(".description")
+ ->GetValueAsUnsigned(0);
+ std::string description;
+ Status error;
+ process_sp->ReadCStringFromMemory(description_ptr, description, error);
+
+ StructuredData::Dictionary *dict = new StructuredData::Dictionary();
+ dict->AddStringItem("instrumentation_class", "AddressSanitizer");
+ dict->AddStringItem("stop_type", "fatal_error");
+ dict->AddIntegerItem("pc", pc);
+ /* commented out because rdar://problem/18533301
+ dict->AddIntegerItem("bp", bp);
+ dict->AddIntegerItem("sp", sp);
+ */
+ dict->AddIntegerItem("address", address);
+ dict->AddIntegerItem("access_type", access_type);
+ dict->AddIntegerItem("access_size", access_size);
+ dict->AddStringItem("description", description);
+
+ return StructuredData::ObjectSP(dict);
+}
+
+std::string
+AddressSanitizerRuntime::FormatDescription(StructuredData::ObjectSP report) {
+ std::string description = report->GetAsDictionary()
+ ->GetValueForKey("description")
+ ->GetAsString()
+ ->GetValue();
+ return llvm::StringSwitch<std::string>(description)
+ .Case("heap-use-after-free", "Use of deallocated memory")
+ .Case("heap-buffer-overflow", "Heap buffer overflow")
+ .Case("stack-buffer-underflow", "Stack buffer underflow")
+ .Case("initialization-order-fiasco", "Initialization order problem")
+ .Case("stack-buffer-overflow", "Stack buffer overflow")
+ .Case("stack-use-after-return", "Use of stack memory after return")
+ .Case("use-after-poison", "Use of poisoned memory")
+ .Case("container-overflow", "Container overflow")
+ .Case("stack-use-after-scope", "Use of out-of-scope stack memory")
+ .Case("global-buffer-overflow", "Global buffer overflow")
+ .Case("unknown-crash", "Invalid memory access")
+ .Case("stack-overflow", "Stack space exhausted")
+ .Case("null-deref", "Dereference of null pointer")
+ .Case("wild-jump", "Jump to non-executable address")
+ .Case("wild-addr-write", "Write through wild pointer")
+ .Case("wild-addr-read", "Read from wild pointer")
+ .Case("wild-addr", "Access through wild pointer")
+ .Case("signal", "Deadly signal")
+ .Case("double-free", "Deallocation of freed memory")
+ .Case("new-delete-type-mismatch",
+ "Deallocation size different from allocation size")
+ .Case("bad-free", "Deallocation of non-allocated memory")
+ .Case("alloc-dealloc-mismatch",
+ "Mismatch between allocation and deallocation APIs")
+ .Case("bad-malloc_usable_size", "Invalid argument to malloc_usable_size")
+ .Case("bad-__sanitizer_get_allocated_size",
+ "Invalid argument to __sanitizer_get_allocated_size")
+ .Case("param-overlap",
+ "Call to function disallowing overlapping memory ranges")
+ .Case("negative-size-param", "Negative size used when accessing memory")
+ .Case("bad-__sanitizer_annotate_contiguous_container",
+ "Invalid argument to __sanitizer_annotate_contiguous_container")
+ .Case("odr-violation", "Symbol defined in multiple translation units")
+ .Case(
+ "invalid-pointer-pair",
+ "Comparison or arithmetic on pointers from different memory regions")
+ // for unknown report codes just show the code
+ .Default("AddressSanitizer detected: " + description);
+}
+
+bool AddressSanitizerRuntime::NotifyBreakpointHit(
+ void *baton, StoppointCallbackContext *context, user_id_t break_id,
+ user_id_t break_loc_id) {
+ assert(baton && "null baton");
+ if (!baton)
+ return false;
+
+ AddressSanitizerRuntime *const instance =
+ static_cast<AddressSanitizerRuntime *>(baton);
+
+ ProcessSP process_sp = instance->GetProcessSP();
+
+ if (process_sp->GetModIDRef().IsLastResumeForUserExpression())
+ return false;
+
+ StructuredData::ObjectSP report = instance->RetrieveReportData();
+ std::string description;
+ if (report) {
+ description = instance->FormatDescription(report);
+ }
+ // Make sure this is the right process
+ if (process_sp && process_sp == context->exe_ctx_ref.GetProcessSP()) {
+ ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP();
+ if (thread_sp)
+ thread_sp->SetStopInfo(InstrumentationRuntimeStopInfo::
+ CreateStopReasonWithInstrumentationData(
+ *thread_sp, description, report));
+
+ StreamFileSP stream_sp(
+ process_sp->GetTarget().GetDebugger().GetOutputFile());
+ if (stream_sp) {
+ stream_sp->Printf("AddressSanitizer report breakpoint hit. Use 'thread "
+ "info -s' to get extended information about the "
+ "report.\n");
+ }
+ return true; // Return true to stop the target
+ } else
+ return false; // Let target run
+}
+
+void AddressSanitizerRuntime::Activate() {
+ if (IsActive())
+ return;
+
+ ProcessSP process_sp = GetProcessSP();
+ if (!process_sp)
+ return;
+
+ ConstString symbol_name("__asan::AsanDie()");
+ const Symbol *symbol = GetRuntimeModuleSP()->FindFirstSymbolWithNameAndType(
+ symbol_name, eSymbolTypeCode);
+
+ if (symbol == nullptr)
+ return;
+
+ if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid())
+ return;
+
+ Target &target = process_sp->GetTarget();
+ addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target);
+
+ if (symbol_address == LLDB_INVALID_ADDRESS)
+ return;
+
+ bool internal = true;
+ bool hardware = false;
+ Breakpoint *breakpoint =
+ process_sp->GetTarget()
+ .CreateBreakpoint(symbol_address, internal, hardware)
+ .get();
+ breakpoint->SetCallback(AddressSanitizerRuntime::NotifyBreakpointHit, this,
+ true);
+ breakpoint->SetBreakpointKind("address-sanitizer-report");
+ SetBreakpointID(breakpoint->GetID());
+
+ SetActive(true);
+}
+
+void AddressSanitizerRuntime::Deactivate() {
+ if (GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
+ ProcessSP process_sp = GetProcessSP();
+ if (process_sp) {
+ process_sp->GetTarget().RemoveBreakpointByID(GetBreakpointID());
+ SetBreakpointID(LLDB_INVALID_BREAK_ID);
+ }
+ }
+ SetActive(false);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/ASan/ASanRuntime.h b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/ASan/ASanRuntime.h
new file mode 100644
index 000000000000..0771e624ff6b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/ASan/ASanRuntime.h
@@ -0,0 +1,66 @@
+//===-- ASanRuntime.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 liblldb_AddressSanitizerRuntime_h_
+#define liblldb_AddressSanitizerRuntime_h_
+
+#include "lldb/Target/InstrumentationRuntime.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/StructuredData.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class AddressSanitizerRuntime : public lldb_private::InstrumentationRuntime {
+public:
+ ~AddressSanitizerRuntime() override;
+
+ static lldb::InstrumentationRuntimeSP
+ CreateInstance(const lldb::ProcessSP &process_sp);
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static lldb::InstrumentationRuntimeType GetTypeStatic();
+
+ lldb_private::ConstString GetPluginName() override {
+ return GetPluginNameStatic();
+ }
+
+ virtual lldb::InstrumentationRuntimeType GetType() { return GetTypeStatic(); }
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+private:
+ AddressSanitizerRuntime(const lldb::ProcessSP &process_sp)
+ : lldb_private::InstrumentationRuntime(process_sp) {}
+
+ const RegularExpression &GetPatternForRuntimeLibrary() override;
+
+ bool CheckIfRuntimeIsValid(const lldb::ModuleSP module_sp) override;
+
+ void Activate() override;
+
+ void Deactivate();
+
+ static bool NotifyBreakpointHit(void *baton,
+ StoppointCallbackContext *context,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id);
+
+ StructuredData::ObjectSP RetrieveReportData();
+
+ std::string FormatDescription(StructuredData::ObjectSP report);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_AddressSanitizerRuntime_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.cpp b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.cpp
new file mode 100644
index 000000000000..dfe61316b042
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.cpp
@@ -0,0 +1,274 @@
+//===-- MainThreadCheckerRuntime.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 "MainThreadCheckerRuntime.h"
+
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/InstrumentationRuntimeStopInfo.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "Plugins/Process/Utility/HistoryThread.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+MainThreadCheckerRuntime::~MainThreadCheckerRuntime() {
+ Deactivate();
+}
+
+lldb::InstrumentationRuntimeSP
+MainThreadCheckerRuntime::CreateInstance(const lldb::ProcessSP &process_sp) {
+ return InstrumentationRuntimeSP(new MainThreadCheckerRuntime(process_sp));
+}
+
+void MainThreadCheckerRuntime::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "MainThreadChecker instrumentation runtime plugin.",
+ CreateInstance, GetTypeStatic);
+}
+
+void MainThreadCheckerRuntime::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString MainThreadCheckerRuntime::GetPluginNameStatic() {
+ return ConstString("MainThreadChecker");
+}
+
+lldb::InstrumentationRuntimeType MainThreadCheckerRuntime::GetTypeStatic() {
+ return eInstrumentationRuntimeTypeMainThreadChecker;
+}
+
+const RegularExpression &
+MainThreadCheckerRuntime::GetPatternForRuntimeLibrary() {
+ static RegularExpression regex(llvm::StringRef("libMainThreadChecker.dylib"));
+ return regex;
+}
+
+bool MainThreadCheckerRuntime::CheckIfRuntimeIsValid(
+ const lldb::ModuleSP module_sp) {
+ static ConstString test_sym("__main_thread_checker_on_report");
+ const Symbol *symbol =
+ module_sp->FindFirstSymbolWithNameAndType(test_sym, lldb::eSymbolTypeAny);
+ return symbol != nullptr;
+}
+
+StructuredData::ObjectSP
+MainThreadCheckerRuntime::RetrieveReportData(ExecutionContextRef exe_ctx_ref) {
+ ProcessSP process_sp = GetProcessSP();
+ if (!process_sp)
+ return StructuredData::ObjectSP();
+
+ ThreadSP thread_sp = exe_ctx_ref.GetThreadSP();
+ StackFrameSP frame_sp = thread_sp->GetSelectedFrame();
+ ModuleSP runtime_module_sp = GetRuntimeModuleSP();
+ Target &target = process_sp->GetTarget();
+
+ if (!frame_sp)
+ return StructuredData::ObjectSP();
+
+ RegisterContextSP regctx_sp = frame_sp->GetRegisterContext();
+ if (!regctx_sp)
+ return StructuredData::ObjectSP();
+
+ const RegisterInfo *reginfo = regctx_sp->GetRegisterInfoByName("arg1");
+ if (!reginfo)
+ return StructuredData::ObjectSP();
+
+ uint64_t apiname_ptr = regctx_sp->ReadRegisterAsUnsigned(reginfo, 0);
+ if (!apiname_ptr)
+ return StructuredData::ObjectSP();
+
+ std::string apiName = "";
+ Status read_error;
+ target.ReadCStringFromMemory(apiname_ptr, apiName, read_error);
+ if (read_error.Fail())
+ return StructuredData::ObjectSP();
+
+ std::string className = "";
+ std::string selector = "";
+ if (apiName.substr(0, 2) == "-[") {
+ size_t spacePos = apiName.find(" ");
+ if (spacePos != std::string::npos) {
+ className = apiName.substr(2, spacePos - 2);
+ selector = apiName.substr(spacePos + 1, apiName.length() - spacePos - 2);
+ }
+ }
+
+ // Gather the PCs of the user frames in the backtrace.
+ StructuredData::Array *trace = new StructuredData::Array();
+ auto trace_sp = StructuredData::ObjectSP(trace);
+ StackFrameSP responsible_frame;
+ for (unsigned I = 0; I < thread_sp->GetStackFrameCount(); ++I) {
+ StackFrameSP frame = thread_sp->GetStackFrameAtIndex(I);
+ Address addr = frame->GetFrameCodeAddress();
+ if (addr.GetModule() == runtime_module_sp) // Skip PCs from the runtime.
+ continue;
+
+ // The first non-runtime frame is responsible for the bug.
+ if (!responsible_frame)
+ responsible_frame = frame;
+
+ // First frame in stacktrace should point to a real PC, not return address.
+ if (I != 0 && trace->GetSize() == 0) {
+ addr.Slide(-1);
+ }
+
+ lldb::addr_t PC = addr.GetLoadAddress(&target);
+ trace->AddItem(StructuredData::ObjectSP(new StructuredData::Integer(PC)));
+ }
+
+ auto *d = new StructuredData::Dictionary();
+ auto dict_sp = StructuredData::ObjectSP(d);
+ d->AddStringItem("instrumentation_class", "MainThreadChecker");
+ d->AddStringItem("api_name", apiName);
+ d->AddStringItem("class_name", className);
+ d->AddStringItem("selector", selector);
+ d->AddStringItem("description",
+ apiName + " must be used from main thread only");
+ d->AddIntegerItem("tid", thread_sp->GetIndexID());
+ d->AddItem("trace", trace_sp);
+ return dict_sp;
+}
+
+bool MainThreadCheckerRuntime::NotifyBreakpointHit(
+ void *baton, StoppointCallbackContext *context, user_id_t break_id,
+ user_id_t break_loc_id) {
+ assert(baton && "null baton");
+ if (!baton)
+ return false; //< false => resume execution.
+
+ MainThreadCheckerRuntime *const instance =
+ static_cast<MainThreadCheckerRuntime *>(baton);
+
+ ProcessSP process_sp = instance->GetProcessSP();
+ ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP();
+ if (!process_sp || !thread_sp ||
+ process_sp != context->exe_ctx_ref.GetProcessSP())
+ return false;
+
+ if (process_sp->GetModIDRef().IsLastResumeForUserExpression())
+ return false;
+
+ StructuredData::ObjectSP report =
+ instance->RetrieveReportData(context->exe_ctx_ref);
+
+ if (report) {
+ std::string description = report->GetAsDictionary()
+ ->GetValueForKey("description")
+ ->GetAsString()
+ ->GetValue();
+ thread_sp->SetStopInfo(
+ InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData(
+ *thread_sp, description, report));
+ return true;
+ }
+
+ return false;
+}
+
+void MainThreadCheckerRuntime::Activate() {
+ if (IsActive())
+ return;
+
+ ProcessSP process_sp = GetProcessSP();
+ if (!process_sp)
+ return;
+
+ ModuleSP runtime_module_sp = GetRuntimeModuleSP();
+
+ ConstString symbol_name("__main_thread_checker_on_report");
+ const Symbol *symbol = runtime_module_sp->FindFirstSymbolWithNameAndType(
+ symbol_name, eSymbolTypeCode);
+
+ if (symbol == nullptr)
+ return;
+
+ if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid())
+ return;
+
+ Target &target = process_sp->GetTarget();
+ addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target);
+
+ if (symbol_address == LLDB_INVALID_ADDRESS)
+ return;
+
+ Breakpoint *breakpoint =
+ process_sp->GetTarget()
+ .CreateBreakpoint(symbol_address, /*internal=*/true,
+ /*hardware=*/false)
+ .get();
+ breakpoint->SetCallback(MainThreadCheckerRuntime::NotifyBreakpointHit, this,
+ true);
+ breakpoint->SetBreakpointKind("main-thread-checker-report");
+ SetBreakpointID(breakpoint->GetID());
+
+ SetActive(true);
+}
+
+void MainThreadCheckerRuntime::Deactivate() {
+ SetActive(false);
+
+ auto BID = GetBreakpointID();
+ if (BID == LLDB_INVALID_BREAK_ID)
+ return;
+
+ if (ProcessSP process_sp = GetProcessSP()) {
+ process_sp->GetTarget().RemoveBreakpointByID(BID);
+ SetBreakpointID(LLDB_INVALID_BREAK_ID);
+ }
+}
+
+lldb::ThreadCollectionSP
+MainThreadCheckerRuntime::GetBacktracesFromExtendedStopInfo(
+ StructuredData::ObjectSP info) {
+ ThreadCollectionSP threads;
+ threads = std::make_shared<ThreadCollection>();
+
+ ProcessSP process_sp = GetProcessSP();
+
+ if (info->GetObjectForDotSeparatedPath("instrumentation_class")
+ ->GetStringValue() != "MainThreadChecker")
+ return threads;
+
+ std::vector<lldb::addr_t> PCs;
+ auto trace = info->GetObjectForDotSeparatedPath("trace")->GetAsArray();
+ trace->ForEach([&PCs](StructuredData::Object *PC) -> bool {
+ PCs.push_back(PC->GetAsInteger()->GetValue());
+ return true;
+ });
+
+ if (PCs.empty())
+ return threads;
+
+ StructuredData::ObjectSP thread_id_obj =
+ info->GetObjectForDotSeparatedPath("tid");
+ tid_t tid = thread_id_obj ? thread_id_obj->GetIntegerValue() : 0;
+
+ HistoryThread *history_thread = new HistoryThread(*process_sp, tid, PCs);
+ ThreadSP new_thread_sp(history_thread);
+
+ // Save this in the Process' ExtendedThreadList so a strong pointer retains
+ // the object
+ process_sp->GetExtendedThreadList().AddThread(new_thread_sp);
+ threads->AddThread(new_thread_sp);
+
+ return threads;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.h b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.h
new file mode 100644
index 000000000000..1dcbc0f6bc89
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.h
@@ -0,0 +1,67 @@
+//===-- MainThreadCheckerRuntime.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 liblldb_MainThreadCheckerRuntime_h_
+#define liblldb_MainThreadCheckerRuntime_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/InstrumentationRuntime.h"
+#include "lldb/Utility/StructuredData.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+ class MainThreadCheckerRuntime : public lldb_private::InstrumentationRuntime {
+ public:
+ ~MainThreadCheckerRuntime() override;
+
+ static lldb::InstrumentationRuntimeSP
+ CreateInstance(const lldb::ProcessSP &process_sp);
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static lldb::InstrumentationRuntimeType GetTypeStatic();
+
+ lldb_private::ConstString GetPluginName() override {
+ return GetPluginNameStatic();
+ }
+
+ virtual lldb::InstrumentationRuntimeType GetType() { return GetTypeStatic(); }
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ lldb::ThreadCollectionSP
+ GetBacktracesFromExtendedStopInfo(StructuredData::ObjectSP info) override;
+
+ private:
+ MainThreadCheckerRuntime(const lldb::ProcessSP &process_sp)
+ : lldb_private::InstrumentationRuntime(process_sp) {}
+
+ const RegularExpression &GetPatternForRuntimeLibrary() override;
+
+ bool CheckIfRuntimeIsValid(const lldb::ModuleSP module_sp) override;
+
+ void Activate() override;
+
+ void Deactivate();
+
+ static bool NotifyBreakpointHit(void *baton,
+ StoppointCallbackContext *context,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id);
+
+ StructuredData::ObjectSP RetrieveReportData(ExecutionContextRef exe_ctx_ref);
+ };
+
+} // namespace lldb_private
+
+#endif // liblldb_MainThreadCheckerRuntime_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/TSan/TSanRuntime.cpp b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/TSan/TSanRuntime.cpp
new file mode 100644
index 000000000000..89f2139db71b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/TSan/TSanRuntime.cpp
@@ -0,0 +1,1066 @@
+//===-- TSanRuntime.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 "TSanRuntime.h"
+
+#include "Plugins/Process/Utility/HistoryThread.h"
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginInterface.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/InstrumentationRuntimeStopInfo.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Stream.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+lldb::InstrumentationRuntimeSP
+ThreadSanitizerRuntime::CreateInstance(const lldb::ProcessSP &process_sp) {
+ return InstrumentationRuntimeSP(new ThreadSanitizerRuntime(process_sp));
+}
+
+void ThreadSanitizerRuntime::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "ThreadSanitizer instrumentation runtime plugin.",
+ CreateInstance, GetTypeStatic);
+}
+
+void ThreadSanitizerRuntime::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ThreadSanitizerRuntime::GetPluginNameStatic() {
+ return ConstString("ThreadSanitizer");
+}
+
+lldb::InstrumentationRuntimeType ThreadSanitizerRuntime::GetTypeStatic() {
+ return eInstrumentationRuntimeTypeThreadSanitizer;
+}
+
+ThreadSanitizerRuntime::~ThreadSanitizerRuntime() { Deactivate(); }
+
+const char *thread_sanitizer_retrieve_report_data_prefix = R"(
+extern "C"
+{
+ void *__tsan_get_current_report();
+ int __tsan_get_report_data(void *report, const char **description, int *count,
+ int *stack_count, int *mop_count, int *loc_count,
+ int *mutex_count, int *thread_count,
+ int *unique_tid_count, void **sleep_trace,
+ unsigned long trace_size);
+ int __tsan_get_report_stack(void *report, unsigned long idx, void **trace,
+ unsigned long trace_size);
+ int __tsan_get_report_mop(void *report, unsigned long idx, int *tid, void **addr,
+ int *size, int *write, int *atomic, void **trace,
+ unsigned long trace_size);
+ int __tsan_get_report_loc(void *report, unsigned long idx, const char **type,
+ void **addr, unsigned long *start, unsigned long *size, int *tid,
+ int *fd, int *suppressable, void **trace,
+ unsigned long trace_size);
+ int __tsan_get_report_mutex(void *report, unsigned long idx, unsigned long *mutex_id, void **addr,
+ int *destroyed, void **trace, unsigned long trace_size);
+ int __tsan_get_report_thread(void *report, unsigned long idx, int *tid, unsigned long *os_id,
+ int *running, const char **name, int *parent_tid,
+ void **trace, unsigned long trace_size);
+ int __tsan_get_report_unique_tid(void *report, unsigned long idx, int *tid);
+
+ // TODO: dlsym won't work on Windows.
+ void *dlsym(void* handle, const char* symbol);
+ int (*ptr__tsan_get_report_loc_object_type)(void *report, unsigned long idx, const char **object_type);
+}
+
+const int REPORT_TRACE_SIZE = 128;
+const int REPORT_ARRAY_SIZE = 4;
+
+struct data {
+ void *report;
+ const char *description;
+ int report_count;
+
+ void *sleep_trace[REPORT_TRACE_SIZE];
+
+ int stack_count;
+ struct {
+ int idx;
+ void *trace[REPORT_TRACE_SIZE];
+ } stacks[REPORT_ARRAY_SIZE];
+
+ int mop_count;
+ struct {
+ int idx;
+ int tid;
+ int size;
+ int write;
+ int atomic;
+ void *addr;
+ void *trace[REPORT_TRACE_SIZE];
+ } mops[REPORT_ARRAY_SIZE];
+
+ int loc_count;
+ struct {
+ int idx;
+ const char *type;
+ void *addr;
+ unsigned long start;
+ unsigned long size;
+ int tid;
+ int fd;
+ int suppressable;
+ void *trace[REPORT_TRACE_SIZE];
+ const char *object_type;
+ } locs[REPORT_ARRAY_SIZE];
+
+ int mutex_count;
+ struct {
+ int idx;
+ unsigned long mutex_id;
+ void *addr;
+ int destroyed;
+ void *trace[REPORT_TRACE_SIZE];
+ } mutexes[REPORT_ARRAY_SIZE];
+
+ int thread_count;
+ struct {
+ int idx;
+ int tid;
+ unsigned long os_id;
+ int running;
+ const char *name;
+ int parent_tid;
+ void *trace[REPORT_TRACE_SIZE];
+ } threads[REPORT_ARRAY_SIZE];
+
+ int unique_tid_count;
+ struct {
+ int idx;
+ int tid;
+ } unique_tids[REPORT_ARRAY_SIZE];
+};
+)";
+
+const char *thread_sanitizer_retrieve_report_data_command = R"(
+data t = {0};
+
+ptr__tsan_get_report_loc_object_type = (typeof(ptr__tsan_get_report_loc_object_type))(void *)dlsym((void*)-2 /*RTLD_DEFAULT*/, "__tsan_get_report_loc_object_type");
+
+t.report = __tsan_get_current_report();
+__tsan_get_report_data(t.report, &t.description, &t.report_count, &t.stack_count, &t.mop_count, &t.loc_count, &t.mutex_count, &t.thread_count, &t.unique_tid_count, t.sleep_trace, REPORT_TRACE_SIZE);
+
+if (t.stack_count > REPORT_ARRAY_SIZE) t.stack_count = REPORT_ARRAY_SIZE;
+for (int i = 0; i < t.stack_count; i++) {
+ t.stacks[i].idx = i;
+ __tsan_get_report_stack(t.report, i, t.stacks[i].trace, REPORT_TRACE_SIZE);
+}
+
+if (t.mop_count > REPORT_ARRAY_SIZE) t.mop_count = REPORT_ARRAY_SIZE;
+for (int i = 0; i < t.mop_count; i++) {
+ t.mops[i].idx = i;
+ __tsan_get_report_mop(t.report, i, &t.mops[i].tid, &t.mops[i].addr, &t.mops[i].size, &t.mops[i].write, &t.mops[i].atomic, t.mops[i].trace, REPORT_TRACE_SIZE);
+}
+
+if (t.loc_count > REPORT_ARRAY_SIZE) t.loc_count = REPORT_ARRAY_SIZE;
+for (int i = 0; i < t.loc_count; i++) {
+ t.locs[i].idx = i;
+ __tsan_get_report_loc(t.report, i, &t.locs[i].type, &t.locs[i].addr, &t.locs[i].start, &t.locs[i].size, &t.locs[i].tid, &t.locs[i].fd, &t.locs[i].suppressable, t.locs[i].trace, REPORT_TRACE_SIZE);
+ if (ptr__tsan_get_report_loc_object_type)
+ ptr__tsan_get_report_loc_object_type(t.report, i, &t.locs[i].object_type);
+}
+
+if (t.mutex_count > REPORT_ARRAY_SIZE) t.mutex_count = REPORT_ARRAY_SIZE;
+for (int i = 0; i < t.mutex_count; i++) {
+ t.mutexes[i].idx = i;
+ __tsan_get_report_mutex(t.report, i, &t.mutexes[i].mutex_id, &t.mutexes[i].addr, &t.mutexes[i].destroyed, t.mutexes[i].trace, REPORT_TRACE_SIZE);
+}
+
+if (t.thread_count > REPORT_ARRAY_SIZE) t.thread_count = REPORT_ARRAY_SIZE;
+for (int i = 0; i < t.thread_count; i++) {
+ t.threads[i].idx = i;
+ __tsan_get_report_thread(t.report, i, &t.threads[i].tid, &t.threads[i].os_id, &t.threads[i].running, &t.threads[i].name, &t.threads[i].parent_tid, t.threads[i].trace, REPORT_TRACE_SIZE);
+}
+
+if (t.unique_tid_count > REPORT_ARRAY_SIZE) t.unique_tid_count = REPORT_ARRAY_SIZE;
+for (int i = 0; i < t.unique_tid_count; i++) {
+ t.unique_tids[i].idx = i;
+ __tsan_get_report_unique_tid(t.report, i, &t.unique_tids[i].tid);
+}
+
+t;
+)";
+
+static StructuredData::Array *
+CreateStackTrace(ValueObjectSP o,
+ const std::string &trace_item_name = ".trace") {
+ StructuredData::Array *trace = new StructuredData::Array();
+ ValueObjectSP trace_value_object =
+ o->GetValueForExpressionPath(trace_item_name.c_str());
+ size_t count = trace_value_object->GetNumChildren();
+ for (size_t j = 0; j < count; j++) {
+ addr_t trace_addr =
+ trace_value_object->GetChildAtIndex(j, true)->GetValueAsUnsigned(0);
+ if (trace_addr == 0)
+ break;
+ trace->AddItem(
+ StructuredData::ObjectSP(new StructuredData::Integer(trace_addr)));
+ }
+ return trace;
+}
+
+static StructuredData::Array *ConvertToStructuredArray(
+ ValueObjectSP return_value_sp, const std::string &items_name,
+ const std::string &count_name,
+ std::function<void(ValueObjectSP o, StructuredData::Dictionary *dict)> const
+ &callback) {
+ StructuredData::Array *array = new StructuredData::Array();
+ unsigned int count =
+ return_value_sp->GetValueForExpressionPath(count_name.c_str())
+ ->GetValueAsUnsigned(0);
+ ValueObjectSP objects =
+ return_value_sp->GetValueForExpressionPath(items_name.c_str());
+ for (unsigned int i = 0; i < count; i++) {
+ ValueObjectSP o = objects->GetChildAtIndex(i, true);
+ StructuredData::Dictionary *dict = new StructuredData::Dictionary();
+
+ callback(o, dict);
+
+ array->AddItem(StructuredData::ObjectSP(dict));
+ }
+ return array;
+}
+
+static std::string RetrieveString(ValueObjectSP return_value_sp,
+ ProcessSP process_sp,
+ const std::string &expression_path) {
+ addr_t ptr =
+ return_value_sp->GetValueForExpressionPath(expression_path.c_str())
+ ->GetValueAsUnsigned(0);
+ std::string str;
+ Status error;
+ process_sp->ReadCStringFromMemory(ptr, str, error);
+ return str;
+}
+
+static void
+GetRenumberedThreadIds(ProcessSP process_sp, ValueObjectSP data,
+ std::map<uint64_t, user_id_t> &thread_id_map) {
+ ConvertToStructuredArray(
+ data, ".threads", ".thread_count",
+ [process_sp, &thread_id_map](ValueObjectSP o,
+ StructuredData::Dictionary *dict) {
+ uint64_t thread_id =
+ o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0);
+ uint64_t thread_os_id =
+ o->GetValueForExpressionPath(".os_id")->GetValueAsUnsigned(0);
+ user_id_t lldb_user_id = 0;
+
+ bool can_update = true;
+ ThreadSP lldb_thread = process_sp->GetThreadList().FindThreadByID(
+ thread_os_id, can_update);
+ if (lldb_thread) {
+ lldb_user_id = lldb_thread->GetIndexID();
+ } else {
+ // This isn't a live thread anymore. Ask process to assign a new
+ // Index ID (or return an old one if we've already seen this
+ // thread_os_id). It will also make sure that no new threads are
+ // assigned this Index ID.
+ lldb_user_id = process_sp->AssignIndexIDToThread(thread_os_id);
+ }
+
+ thread_id_map[thread_id] = lldb_user_id;
+ });
+}
+
+static user_id_t Renumber(uint64_t id,
+ std::map<uint64_t, user_id_t> &thread_id_map) {
+ auto IT = thread_id_map.find(id);
+ if (IT == thread_id_map.end())
+ return 0;
+
+ return IT->second;
+}
+
+StructuredData::ObjectSP
+ThreadSanitizerRuntime::RetrieveReportData(ExecutionContextRef exe_ctx_ref) {
+ ProcessSP process_sp = GetProcessSP();
+ if (!process_sp)
+ return StructuredData::ObjectSP();
+
+ ThreadSP thread_sp = exe_ctx_ref.GetThreadSP();
+ StackFrameSP frame_sp = thread_sp->GetSelectedFrame();
+
+ if (!frame_sp)
+ return StructuredData::ObjectSP();
+
+ EvaluateExpressionOptions options;
+ options.SetUnwindOnError(true);
+ options.SetTryAllThreads(true);
+ options.SetStopOthers(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetTimeout(process_sp->GetUtilityExpressionTimeout());
+ options.SetPrefix(thread_sanitizer_retrieve_report_data_prefix);
+ options.SetAutoApplyFixIts(false);
+ options.SetLanguage(eLanguageTypeObjC_plus_plus);
+
+ ValueObjectSP main_value;
+ ExecutionContext exe_ctx;
+ Status eval_error;
+ frame_sp->CalculateExecutionContext(exe_ctx);
+ ExpressionResults result = UserExpression::Evaluate(
+ exe_ctx, options, thread_sanitizer_retrieve_report_data_command, "",
+ main_value, eval_error);
+ if (result != eExpressionCompleted) {
+ process_sp->GetTarget().GetDebugger().GetAsyncOutputStream()->Printf(
+ "Warning: Cannot evaluate ThreadSanitizer expression:\n%s\n",
+ eval_error.AsCString());
+ return StructuredData::ObjectSP();
+ }
+
+ std::map<uint64_t, user_id_t> thread_id_map;
+ GetRenumberedThreadIds(process_sp, main_value, thread_id_map);
+
+ StructuredData::Dictionary *dict = new StructuredData::Dictionary();
+ dict->AddStringItem("instrumentation_class", "ThreadSanitizer");
+ dict->AddStringItem("issue_type",
+ RetrieveString(main_value, process_sp, ".description"));
+ dict->AddIntegerItem("report_count",
+ main_value->GetValueForExpressionPath(".report_count")
+ ->GetValueAsUnsigned(0));
+ dict->AddItem("sleep_trace", StructuredData::ObjectSP(CreateStackTrace(
+ main_value, ".sleep_trace")));
+
+ StructuredData::Array *stacks = ConvertToStructuredArray(
+ main_value, ".stacks", ".stack_count",
+ [thread_sp](ValueObjectSP o, StructuredData::Dictionary *dict) {
+ dict->AddIntegerItem(
+ "index",
+ o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0));
+ dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o)));
+ // "stacks" happen on the current thread
+ dict->AddIntegerItem("thread_id", thread_sp->GetIndexID());
+ });
+ dict->AddItem("stacks", StructuredData::ObjectSP(stacks));
+
+ StructuredData::Array *mops = ConvertToStructuredArray(
+ main_value, ".mops", ".mop_count",
+ [&thread_id_map](ValueObjectSP o, StructuredData::Dictionary *dict) {
+ dict->AddIntegerItem(
+ "index",
+ o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0));
+ dict->AddIntegerItem(
+ "thread_id",
+ Renumber(
+ o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0),
+ thread_id_map));
+ dict->AddIntegerItem(
+ "size",
+ o->GetValueForExpressionPath(".size")->GetValueAsUnsigned(0));
+ dict->AddBooleanItem(
+ "is_write",
+ o->GetValueForExpressionPath(".write")->GetValueAsUnsigned(0));
+ dict->AddBooleanItem(
+ "is_atomic",
+ o->GetValueForExpressionPath(".atomic")->GetValueAsUnsigned(0));
+ dict->AddIntegerItem(
+ "address",
+ o->GetValueForExpressionPath(".addr")->GetValueAsUnsigned(0));
+ dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o)));
+ });
+ dict->AddItem("mops", StructuredData::ObjectSP(mops));
+
+ StructuredData::Array *locs = ConvertToStructuredArray(
+ main_value, ".locs", ".loc_count",
+ [process_sp, &thread_id_map](ValueObjectSP o,
+ StructuredData::Dictionary *dict) {
+ dict->AddIntegerItem(
+ "index",
+ o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0));
+ dict->AddStringItem("type", RetrieveString(o, process_sp, ".type"));
+ dict->AddIntegerItem(
+ "address",
+ o->GetValueForExpressionPath(".addr")->GetValueAsUnsigned(0));
+ dict->AddIntegerItem(
+ "start",
+ o->GetValueForExpressionPath(".start")->GetValueAsUnsigned(0));
+ dict->AddIntegerItem(
+ "size",
+ o->GetValueForExpressionPath(".size")->GetValueAsUnsigned(0));
+ dict->AddIntegerItem(
+ "thread_id",
+ Renumber(
+ o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0),
+ thread_id_map));
+ dict->AddIntegerItem(
+ "file_descriptor",
+ o->GetValueForExpressionPath(".fd")->GetValueAsUnsigned(0));
+ dict->AddIntegerItem("suppressable",
+ o->GetValueForExpressionPath(".suppressable")
+ ->GetValueAsUnsigned(0));
+ dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o)));
+ dict->AddStringItem("object_type",
+ RetrieveString(o, process_sp, ".object_type"));
+ });
+ dict->AddItem("locs", StructuredData::ObjectSP(locs));
+
+ StructuredData::Array *mutexes = ConvertToStructuredArray(
+ main_value, ".mutexes", ".mutex_count",
+ [](ValueObjectSP o, StructuredData::Dictionary *dict) {
+ dict->AddIntegerItem(
+ "index",
+ o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0));
+ dict->AddIntegerItem(
+ "mutex_id",
+ o->GetValueForExpressionPath(".mutex_id")->GetValueAsUnsigned(0));
+ dict->AddIntegerItem(
+ "address",
+ o->GetValueForExpressionPath(".addr")->GetValueAsUnsigned(0));
+ dict->AddIntegerItem(
+ "destroyed",
+ o->GetValueForExpressionPath(".destroyed")->GetValueAsUnsigned(0));
+ dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o)));
+ });
+ dict->AddItem("mutexes", StructuredData::ObjectSP(mutexes));
+
+ StructuredData::Array *threads = ConvertToStructuredArray(
+ main_value, ".threads", ".thread_count",
+ [process_sp, &thread_id_map](ValueObjectSP o,
+ StructuredData::Dictionary *dict) {
+ dict->AddIntegerItem(
+ "index",
+ o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0));
+ dict->AddIntegerItem(
+ "thread_id",
+ Renumber(
+ o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0),
+ thread_id_map));
+ dict->AddIntegerItem(
+ "thread_os_id",
+ o->GetValueForExpressionPath(".os_id")->GetValueAsUnsigned(0));
+ dict->AddIntegerItem(
+ "running",
+ o->GetValueForExpressionPath(".running")->GetValueAsUnsigned(0));
+ dict->AddStringItem("name", RetrieveString(o, process_sp, ".name"));
+ dict->AddIntegerItem(
+ "parent_thread_id",
+ Renumber(o->GetValueForExpressionPath(".parent_tid")
+ ->GetValueAsUnsigned(0),
+ thread_id_map));
+ dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o)));
+ });
+ dict->AddItem("threads", StructuredData::ObjectSP(threads));
+
+ StructuredData::Array *unique_tids = ConvertToStructuredArray(
+ main_value, ".unique_tids", ".unique_tid_count",
+ [&thread_id_map](ValueObjectSP o, StructuredData::Dictionary *dict) {
+ dict->AddIntegerItem(
+ "index",
+ o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0));
+ dict->AddIntegerItem(
+ "tid",
+ Renumber(
+ o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0),
+ thread_id_map));
+ });
+ dict->AddItem("unique_tids", StructuredData::ObjectSP(unique_tids));
+
+ return StructuredData::ObjectSP(dict);
+}
+
+std::string
+ThreadSanitizerRuntime::FormatDescription(StructuredData::ObjectSP report) {
+ std::string description = report->GetAsDictionary()
+ ->GetValueForKey("issue_type")
+ ->GetAsString()
+ ->GetValue();
+
+ if (description == "data-race") {
+ return "Data race";
+ } else if (description == "data-race-vptr") {
+ return "Data race on C++ virtual pointer";
+ } else if (description == "heap-use-after-free") {
+ return "Use of deallocated memory";
+ } else if (description == "heap-use-after-free-vptr") {
+ return "Use of deallocated C++ virtual pointer";
+ } else if (description == "thread-leak") {
+ return "Thread leak";
+ } else if (description == "locked-mutex-destroy") {
+ return "Destruction of a locked mutex";
+ } else if (description == "mutex-double-lock") {
+ return "Double lock of a mutex";
+ } else if (description == "mutex-invalid-access") {
+ return "Use of an uninitialized or destroyed mutex";
+ } else if (description == "mutex-bad-unlock") {
+ return "Unlock of an unlocked mutex (or by a wrong thread)";
+ } else if (description == "mutex-bad-read-lock") {
+ return "Read lock of a write locked mutex";
+ } else if (description == "mutex-bad-read-unlock") {
+ return "Read unlock of a write locked mutex";
+ } else if (description == "signal-unsafe-call") {
+ return "Signal-unsafe call inside a signal handler";
+ } else if (description == "errno-in-signal-handler") {
+ return "Overwrite of errno in a signal handler";
+ } else if (description == "lock-order-inversion") {
+ return "Lock order inversion (potential deadlock)";
+ } else if (description == "external-race") {
+ return "Race on a library object";
+ } else if (description == "swift-access-race") {
+ return "Swift access race";
+ }
+
+ // for unknown report codes just show the code
+ return description;
+}
+
+static std::string Sprintf(const char *format, ...) {
+ StreamString s;
+ va_list args;
+ va_start(args, format);
+ s.PrintfVarArg(format, args);
+ va_end(args);
+ return s.GetString();
+}
+
+static std::string GetSymbolNameFromAddress(ProcessSP process_sp, addr_t addr) {
+ lldb_private::Address so_addr;
+ if (!process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress(addr,
+ so_addr))
+ return "";
+
+ lldb_private::Symbol *symbol = so_addr.CalculateSymbolContextSymbol();
+ if (!symbol)
+ return "";
+
+ std::string sym_name = symbol->GetName().GetCString();
+ return sym_name;
+}
+
+static void GetSymbolDeclarationFromAddress(ProcessSP process_sp, addr_t addr,
+ Declaration &decl) {
+ lldb_private::Address so_addr;
+ if (!process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress(addr,
+ so_addr))
+ return;
+
+ lldb_private::Symbol *symbol = so_addr.CalculateSymbolContextSymbol();
+ if (!symbol)
+ return;
+
+ ConstString sym_name = symbol->GetMangled().GetName(
+ lldb::eLanguageTypeUnknown, Mangled::ePreferMangled);
+
+ ModuleSP module = symbol->CalculateSymbolContextModule();
+ if (!module)
+ return;
+
+ VariableList var_list;
+ module->FindGlobalVariables(sym_name, nullptr, 1U, var_list);
+ if (var_list.GetSize() < 1)
+ return;
+
+ VariableSP var = var_list.GetVariableAtIndex(0);
+ decl = var->GetDeclaration();
+}
+
+addr_t ThreadSanitizerRuntime::GetFirstNonInternalFramePc(
+ StructuredData::ObjectSP trace, bool skip_one_frame) {
+ ProcessSP process_sp = GetProcessSP();
+ ModuleSP runtime_module_sp = GetRuntimeModuleSP();
+
+ StructuredData::Array *trace_array = trace->GetAsArray();
+ for (size_t i = 0; i < trace_array->GetSize(); i++) {
+ if (skip_one_frame && i == 0)
+ continue;
+
+ addr_t addr;
+ if (!trace_array->GetItemAtIndexAsInteger(i, addr))
+ continue;
+
+ lldb_private::Address so_addr;
+ if (!process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress(
+ addr, so_addr))
+ continue;
+
+ if (so_addr.GetModule() == runtime_module_sp)
+ continue;
+
+ return addr;
+ }
+
+ return 0;
+}
+
+std::string
+ThreadSanitizerRuntime::GenerateSummary(StructuredData::ObjectSP report) {
+ ProcessSP process_sp = GetProcessSP();
+
+ std::string summary = report->GetAsDictionary()
+ ->GetValueForKey("description")
+ ->GetAsString()
+ ->GetValue();
+ bool skip_one_frame =
+ report->GetObjectForDotSeparatedPath("issue_type")->GetStringValue() ==
+ "external-race";
+
+ addr_t pc = 0;
+ if (report->GetAsDictionary()
+ ->GetValueForKey("mops")
+ ->GetAsArray()
+ ->GetSize() > 0)
+ pc = GetFirstNonInternalFramePc(report->GetAsDictionary()
+ ->GetValueForKey("mops")
+ ->GetAsArray()
+ ->GetItemAtIndex(0)
+ ->GetAsDictionary()
+ ->GetValueForKey("trace"),
+ skip_one_frame);
+
+ if (report->GetAsDictionary()
+ ->GetValueForKey("stacks")
+ ->GetAsArray()
+ ->GetSize() > 0)
+ pc = GetFirstNonInternalFramePc(report->GetAsDictionary()
+ ->GetValueForKey("stacks")
+ ->GetAsArray()
+ ->GetItemAtIndex(0)
+ ->GetAsDictionary()
+ ->GetValueForKey("trace"),
+ skip_one_frame);
+
+ if (pc != 0) {
+ summary = summary + " in " + GetSymbolNameFromAddress(process_sp, pc);
+ }
+
+ if (report->GetAsDictionary()
+ ->GetValueForKey("locs")
+ ->GetAsArray()
+ ->GetSize() > 0) {
+ StructuredData::ObjectSP loc = report->GetAsDictionary()
+ ->GetValueForKey("locs")
+ ->GetAsArray()
+ ->GetItemAtIndex(0);
+ std::string object_type = loc->GetAsDictionary()
+ ->GetValueForKey("object_type")
+ ->GetAsString()
+ ->GetValue();
+ if (!object_type.empty()) {
+ summary = "Race on " + object_type + " object";
+ }
+ addr_t addr = loc->GetAsDictionary()
+ ->GetValueForKey("address")
+ ->GetAsInteger()
+ ->GetValue();
+ if (addr == 0)
+ addr = loc->GetAsDictionary()
+ ->GetValueForKey("start")
+ ->GetAsInteger()
+ ->GetValue();
+
+ if (addr != 0) {
+ std::string global_name = GetSymbolNameFromAddress(process_sp, addr);
+ if (!global_name.empty()) {
+ summary = summary + " at " + global_name;
+ } else {
+ summary = summary + " at " + Sprintf("0x%llx", addr);
+ }
+ } else {
+ int fd = loc->GetAsDictionary()
+ ->GetValueForKey("file_descriptor")
+ ->GetAsInteger()
+ ->GetValue();
+ if (fd != 0) {
+ summary = summary + " on file descriptor " + Sprintf("%d", fd);
+ }
+ }
+ }
+
+ return summary;
+}
+
+addr_t
+ThreadSanitizerRuntime::GetMainRacyAddress(StructuredData::ObjectSP report) {
+ addr_t result = (addr_t)-1;
+
+ report->GetObjectForDotSeparatedPath("mops")->GetAsArray()->ForEach(
+ [&result](StructuredData::Object *o) -> bool {
+ addr_t addr =
+ o->GetObjectForDotSeparatedPath("address")->GetIntegerValue();
+ if (addr < result)
+ result = addr;
+ return true;
+ });
+
+ return (result == (addr_t)-1) ? 0 : result;
+}
+
+std::string ThreadSanitizerRuntime::GetLocationDescription(
+ StructuredData::ObjectSP report, addr_t &global_addr,
+ std::string &global_name, std::string &filename, uint32_t &line) {
+ std::string result = "";
+
+ ProcessSP process_sp = GetProcessSP();
+
+ if (report->GetAsDictionary()
+ ->GetValueForKey("locs")
+ ->GetAsArray()
+ ->GetSize() > 0) {
+ StructuredData::ObjectSP loc = report->GetAsDictionary()
+ ->GetValueForKey("locs")
+ ->GetAsArray()
+ ->GetItemAtIndex(0);
+ std::string type =
+ loc->GetAsDictionary()->GetValueForKey("type")->GetStringValue();
+ if (type == "global") {
+ global_addr = loc->GetAsDictionary()
+ ->GetValueForKey("address")
+ ->GetAsInteger()
+ ->GetValue();
+ global_name = GetSymbolNameFromAddress(process_sp, global_addr);
+ if (!global_name.empty()) {
+ result = Sprintf("'%s' is a global variable (0x%llx)",
+ global_name.c_str(), global_addr);
+ } else {
+ result = Sprintf("0x%llx is a global variable", global_addr);
+ }
+
+ Declaration decl;
+ GetSymbolDeclarationFromAddress(process_sp, global_addr, decl);
+ if (decl.GetFile()) {
+ filename = decl.GetFile().GetPath();
+ line = decl.GetLine();
+ }
+ } else if (type == "heap") {
+ addr_t addr = loc->GetAsDictionary()
+ ->GetValueForKey("start")
+ ->GetAsInteger()
+ ->GetValue();
+ long size = loc->GetAsDictionary()
+ ->GetValueForKey("size")
+ ->GetAsInteger()
+ ->GetValue();
+ std::string object_type = loc->GetAsDictionary()
+ ->GetValueForKey("object_type")
+ ->GetAsString()
+ ->GetValue();
+ if (!object_type.empty()) {
+ result = Sprintf("Location is a %ld-byte %s object at 0x%llx", size,
+ object_type.c_str(), addr);
+ } else {
+ result =
+ Sprintf("Location is a %ld-byte heap object at 0x%llx", size, addr);
+ }
+ } else if (type == "stack") {
+ int tid = loc->GetAsDictionary()
+ ->GetValueForKey("thread_id")
+ ->GetAsInteger()
+ ->GetValue();
+ result = Sprintf("Location is stack of thread %d", tid);
+ } else if (type == "tls") {
+ int tid = loc->GetAsDictionary()
+ ->GetValueForKey("thread_id")
+ ->GetAsInteger()
+ ->GetValue();
+ result = Sprintf("Location is TLS of thread %d", tid);
+ } else if (type == "fd") {
+ int fd = loc->GetAsDictionary()
+ ->GetValueForKey("file_descriptor")
+ ->GetAsInteger()
+ ->GetValue();
+ result = Sprintf("Location is file descriptor %d", fd);
+ }
+ }
+
+ return result;
+}
+
+bool ThreadSanitizerRuntime::NotifyBreakpointHit(
+ void *baton, StoppointCallbackContext *context, user_id_t break_id,
+ user_id_t break_loc_id) {
+ assert(baton && "null baton");
+ if (!baton)
+ return false;
+
+ ThreadSanitizerRuntime *const instance =
+ static_cast<ThreadSanitizerRuntime *>(baton);
+
+ ProcessSP process_sp = instance->GetProcessSP();
+
+ if (process_sp->GetModIDRef().IsLastResumeForUserExpression())
+ return false;
+
+ StructuredData::ObjectSP report =
+ instance->RetrieveReportData(context->exe_ctx_ref);
+ std::string stop_reason_description;
+ if (report) {
+ std::string issue_description = instance->FormatDescription(report);
+ report->GetAsDictionary()->AddStringItem("description", issue_description);
+ stop_reason_description = issue_description + " detected";
+ report->GetAsDictionary()->AddStringItem("stop_description",
+ stop_reason_description);
+ std::string summary = instance->GenerateSummary(report);
+ report->GetAsDictionary()->AddStringItem("summary", summary);
+ addr_t main_address = instance->GetMainRacyAddress(report);
+ report->GetAsDictionary()->AddIntegerItem("memory_address", main_address);
+
+ addr_t global_addr = 0;
+ std::string global_name = "";
+ std::string location_filename = "";
+ uint32_t location_line = 0;
+ std::string location_description = instance->GetLocationDescription(
+ report, global_addr, global_name, location_filename, location_line);
+ report->GetAsDictionary()->AddStringItem("location_description",
+ location_description);
+ if (global_addr != 0) {
+ report->GetAsDictionary()->AddIntegerItem("global_address", global_addr);
+ }
+ if (!global_name.empty()) {
+ report->GetAsDictionary()->AddStringItem("global_name", global_name);
+ }
+ if (location_filename != "") {
+ report->GetAsDictionary()->AddStringItem("location_filename",
+ location_filename);
+ report->GetAsDictionary()->AddIntegerItem("location_line", location_line);
+ }
+
+ bool all_addresses_are_same = true;
+ report->GetObjectForDotSeparatedPath("mops")->GetAsArray()->ForEach(
+ [&all_addresses_are_same,
+ main_address](StructuredData::Object *o) -> bool {
+ addr_t addr =
+ o->GetObjectForDotSeparatedPath("address")->GetIntegerValue();
+ if (main_address != addr)
+ all_addresses_are_same = false;
+ return true;
+ });
+ report->GetAsDictionary()->AddBooleanItem("all_addresses_are_same",
+ all_addresses_are_same);
+ }
+
+ // Make sure this is the right process
+ if (process_sp && process_sp == context->exe_ctx_ref.GetProcessSP()) {
+ ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP();
+ if (thread_sp)
+ thread_sp->SetStopInfo(
+ InstrumentationRuntimeStopInfo::
+ CreateStopReasonWithInstrumentationData(
+ *thread_sp, stop_reason_description, report));
+
+ StreamFileSP stream_sp(
+ process_sp->GetTarget().GetDebugger().GetOutputFile());
+ if (stream_sp) {
+ stream_sp->Printf("ThreadSanitizer report breakpoint hit. Use 'thread "
+ "info -s' to get extended information about the "
+ "report.\n");
+ }
+ return true; // Return true to stop the target
+ } else
+ return false; // Let target run
+}
+
+const RegularExpression &ThreadSanitizerRuntime::GetPatternForRuntimeLibrary() {
+ static RegularExpression regex(llvm::StringRef("libclang_rt.tsan_"));
+ return regex;
+}
+
+bool ThreadSanitizerRuntime::CheckIfRuntimeIsValid(
+ const lldb::ModuleSP module_sp) {
+ static ConstString g_tsan_get_current_report("__tsan_get_current_report");
+ const Symbol *symbol = module_sp->FindFirstSymbolWithNameAndType(
+ g_tsan_get_current_report, lldb::eSymbolTypeAny);
+ return symbol != nullptr;
+}
+
+void ThreadSanitizerRuntime::Activate() {
+ if (IsActive())
+ return;
+
+ ProcessSP process_sp = GetProcessSP();
+ if (!process_sp)
+ return;
+
+ ConstString symbol_name("__tsan_on_report");
+ const Symbol *symbol = GetRuntimeModuleSP()->FindFirstSymbolWithNameAndType(
+ symbol_name, eSymbolTypeCode);
+
+ if (symbol == nullptr)
+ return;
+
+ if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid())
+ return;
+
+ Target &target = process_sp->GetTarget();
+ addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target);
+
+ if (symbol_address == LLDB_INVALID_ADDRESS)
+ return;
+
+ bool internal = true;
+ bool hardware = false;
+ Breakpoint *breakpoint =
+ process_sp->GetTarget()
+ .CreateBreakpoint(symbol_address, internal, hardware)
+ .get();
+ breakpoint->SetCallback(ThreadSanitizerRuntime::NotifyBreakpointHit, this,
+ true);
+ breakpoint->SetBreakpointKind("thread-sanitizer-report");
+ SetBreakpointID(breakpoint->GetID());
+
+ SetActive(true);
+}
+
+void ThreadSanitizerRuntime::Deactivate() {
+ if (GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
+ ProcessSP process_sp = GetProcessSP();
+ if (process_sp) {
+ process_sp->GetTarget().RemoveBreakpointByID(GetBreakpointID());
+ SetBreakpointID(LLDB_INVALID_BREAK_ID);
+ }
+ }
+ SetActive(false);
+}
+static std::string GenerateThreadName(const std::string &path,
+ StructuredData::Object *o,
+ StructuredData::ObjectSP main_info) {
+ std::string result = "additional information";
+
+ if (path == "mops") {
+ int size = o->GetObjectForDotSeparatedPath("size")->GetIntegerValue();
+ int thread_id =
+ o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue();
+ bool is_write =
+ o->GetObjectForDotSeparatedPath("is_write")->GetBooleanValue();
+ bool is_atomic =
+ o->GetObjectForDotSeparatedPath("is_atomic")->GetBooleanValue();
+ addr_t addr = o->GetObjectForDotSeparatedPath("address")->GetIntegerValue();
+
+ std::string addr_string = Sprintf(" at 0x%llx", addr);
+
+ if (main_info->GetObjectForDotSeparatedPath("all_addresses_are_same")
+ ->GetBooleanValue()) {
+ addr_string = "";
+ }
+
+ if (main_info->GetObjectForDotSeparatedPath("issue_type")
+ ->GetStringValue() == "external-race") {
+ result = Sprintf("%s access by thread %d",
+ is_write ? "mutating" : "read-only", thread_id);
+ } else if (main_info->GetObjectForDotSeparatedPath("issue_type")
+ ->GetStringValue() == "swift-access-race") {
+ result = Sprintf("modifying access by thread %d", thread_id);
+ } else {
+ result = Sprintf("%s%s of size %d%s by thread %d",
+ is_atomic ? "atomic " : "", is_write ? "write" : "read",
+ size, addr_string.c_str(), thread_id);
+ }
+ }
+
+ if (path == "threads") {
+ int thread_id =
+ o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue();
+ result = Sprintf("Thread %d created", thread_id);
+ }
+
+ if (path == "locs") {
+ std::string type =
+ o->GetAsDictionary()->GetValueForKey("type")->GetStringValue();
+ int thread_id =
+ o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue();
+ int fd =
+ o->GetObjectForDotSeparatedPath("file_descriptor")->GetIntegerValue();
+ if (type == "heap") {
+ result = Sprintf("Heap block allocated by thread %d", thread_id);
+ } else if (type == "fd") {
+ result =
+ Sprintf("File descriptor %d created by thread %t", fd, thread_id);
+ }
+ }
+
+ if (path == "mutexes") {
+ int mutex_id =
+ o->GetObjectForDotSeparatedPath("mutex_id")->GetIntegerValue();
+
+ result = Sprintf("Mutex M%d created", mutex_id);
+ }
+
+ if (path == "stacks") {
+ int thread_id =
+ o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue();
+ result = Sprintf("Thread %d", thread_id);
+ }
+
+ result[0] = toupper(result[0]);
+
+ return result;
+}
+
+static void AddThreadsForPath(const std::string &path,
+ ThreadCollectionSP threads, ProcessSP process_sp,
+ StructuredData::ObjectSP info) {
+ info->GetObjectForDotSeparatedPath(path)->GetAsArray()->ForEach(
+ [process_sp, threads, path, info](StructuredData::Object *o) -> bool {
+ std::vector<lldb::addr_t> pcs;
+ o->GetObjectForDotSeparatedPath("trace")->GetAsArray()->ForEach(
+ [&pcs](StructuredData::Object *pc) -> bool {
+ pcs.push_back(pc->GetAsInteger()->GetValue());
+ return true;
+ });
+
+ if (pcs.size() == 0)
+ return true;
+
+ StructuredData::ObjectSP thread_id_obj =
+ o->GetObjectForDotSeparatedPath("thread_os_id");
+ tid_t tid = thread_id_obj ? thread_id_obj->GetIntegerValue() : 0;
+
+ HistoryThread *history_thread =
+ new HistoryThread(*process_sp, tid, pcs);
+ ThreadSP new_thread_sp(history_thread);
+ new_thread_sp->SetName(GenerateThreadName(path, o, info).c_str());
+
+ // Save this in the Process' ExtendedThreadList so a strong pointer
+ // retains the object
+ process_sp->GetExtendedThreadList().AddThread(new_thread_sp);
+ threads->AddThread(new_thread_sp);
+
+ return true;
+ });
+}
+
+lldb::ThreadCollectionSP
+ThreadSanitizerRuntime::GetBacktracesFromExtendedStopInfo(
+ StructuredData::ObjectSP info) {
+ ThreadCollectionSP threads;
+ threads = std::make_shared<ThreadCollection>();
+
+ if (info->GetObjectForDotSeparatedPath("instrumentation_class")
+ ->GetStringValue() != "ThreadSanitizer")
+ return threads;
+
+ ProcessSP process_sp = GetProcessSP();
+
+ AddThreadsForPath("stacks", threads, process_sp, info);
+ AddThreadsForPath("mops", threads, process_sp, info);
+ AddThreadsForPath("locs", threads, process_sp, info);
+ AddThreadsForPath("mutexes", threads, process_sp, info);
+ AddThreadsForPath("threads", threads, process_sp, info);
+
+ return threads;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/TSan/TSanRuntime.h b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/TSan/TSanRuntime.h
new file mode 100644
index 000000000000..db8bb1db7996
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/TSan/TSanRuntime.h
@@ -0,0 +1,81 @@
+//===-- TSanRuntime.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 liblldb_ThreadSanitizerRuntime_h_
+#define liblldb_ThreadSanitizerRuntime_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/InstrumentationRuntime.h"
+#include "lldb/Utility/StructuredData.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class ThreadSanitizerRuntime : public lldb_private::InstrumentationRuntime {
+public:
+ ~ThreadSanitizerRuntime() override;
+
+ static lldb::InstrumentationRuntimeSP
+ CreateInstance(const lldb::ProcessSP &process_sp);
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static lldb::InstrumentationRuntimeType GetTypeStatic();
+
+ lldb_private::ConstString GetPluginName() override {
+ return GetPluginNameStatic();
+ }
+
+ virtual lldb::InstrumentationRuntimeType GetType() { return GetTypeStatic(); }
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ lldb::ThreadCollectionSP
+ GetBacktracesFromExtendedStopInfo(StructuredData::ObjectSP info) override;
+
+private:
+ ThreadSanitizerRuntime(const lldb::ProcessSP &process_sp)
+ : lldb_private::InstrumentationRuntime(process_sp) {}
+
+ const RegularExpression &GetPatternForRuntimeLibrary() override;
+
+ bool CheckIfRuntimeIsValid(const lldb::ModuleSP module_sp) override;
+
+ void Activate() override;
+
+ void Deactivate();
+
+ static bool NotifyBreakpointHit(void *baton,
+ StoppointCallbackContext *context,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id);
+
+ StructuredData::ObjectSP RetrieveReportData(ExecutionContextRef exe_ctx_ref);
+
+ std::string FormatDescription(StructuredData::ObjectSP report);
+
+ std::string GenerateSummary(StructuredData::ObjectSP report);
+
+ lldb::addr_t GetMainRacyAddress(StructuredData::ObjectSP report);
+
+ std::string GetLocationDescription(StructuredData::ObjectSP report,
+ lldb::addr_t &global_addr,
+ std::string &global_name,
+ std::string &filename, uint32_t &line);
+
+ lldb::addr_t GetFirstNonInternalFramePc(StructuredData::ObjectSP trace,
+ bool skip_one_frame = false);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ThreadSanitizerRuntime_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.cpp b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.cpp
new file mode 100644
index 000000000000..367098bb448e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.cpp
@@ -0,0 +1,341 @@
+//===-- UBSanRuntime.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 "UBSanRuntime.h"
+
+#include "Plugins/Process/Utility/HistoryThread.h"
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginInterface.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/InstrumentationRuntimeStopInfo.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Stream.h"
+#include <ctype.h>
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+UndefinedBehaviorSanitizerRuntime::~UndefinedBehaviorSanitizerRuntime() {
+ Deactivate();
+}
+
+lldb::InstrumentationRuntimeSP
+UndefinedBehaviorSanitizerRuntime::CreateInstance(
+ const lldb::ProcessSP &process_sp) {
+ return InstrumentationRuntimeSP(
+ new UndefinedBehaviorSanitizerRuntime(process_sp));
+}
+
+void UndefinedBehaviorSanitizerRuntime::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(),
+ "UndefinedBehaviorSanitizer instrumentation runtime plugin.",
+ CreateInstance, GetTypeStatic);
+}
+
+void UndefinedBehaviorSanitizerRuntime::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString
+UndefinedBehaviorSanitizerRuntime::GetPluginNameStatic() {
+ return ConstString("UndefinedBehaviorSanitizer");
+}
+
+lldb::InstrumentationRuntimeType
+UndefinedBehaviorSanitizerRuntime::GetTypeStatic() {
+ return eInstrumentationRuntimeTypeUndefinedBehaviorSanitizer;
+}
+
+static const char *ub_sanitizer_retrieve_report_data_prefix = R"(
+extern "C" {
+void
+__ubsan_get_current_report_data(const char **OutIssueKind,
+ const char **OutMessage, const char **OutFilename, unsigned *OutLine,
+ unsigned *OutCol, char **OutMemoryAddr);
+}
+
+struct data {
+ const char *issue_kind;
+ const char *message;
+ const char *filename;
+ unsigned line;
+ unsigned col;
+ char *memory_addr;
+};
+)";
+
+static const char *ub_sanitizer_retrieve_report_data_command = R"(
+data t;
+__ubsan_get_current_report_data(&t.issue_kind, &t.message, &t.filename, &t.line,
+ &t.col, &t.memory_addr);
+t;
+)";
+
+static addr_t RetrieveUnsigned(ValueObjectSP return_value_sp,
+ ProcessSP process_sp,
+ const std::string &expression_path) {
+ return return_value_sp->GetValueForExpressionPath(expression_path.c_str())
+ ->GetValueAsUnsigned(0);
+}
+
+static std::string RetrieveString(ValueObjectSP return_value_sp,
+ ProcessSP process_sp,
+ const std::string &expression_path) {
+ addr_t ptr = RetrieveUnsigned(return_value_sp, process_sp, expression_path);
+ std::string str;
+ Status error;
+ process_sp->ReadCStringFromMemory(ptr, str, error);
+ return str;
+}
+
+StructuredData::ObjectSP UndefinedBehaviorSanitizerRuntime::RetrieveReportData(
+ ExecutionContextRef exe_ctx_ref) {
+ ProcessSP process_sp = GetProcessSP();
+ if (!process_sp)
+ return StructuredData::ObjectSP();
+
+ ThreadSP thread_sp = exe_ctx_ref.GetThreadSP();
+ StackFrameSP frame_sp = thread_sp->GetSelectedFrame();
+ ModuleSP runtime_module_sp = GetRuntimeModuleSP();
+ Target &target = process_sp->GetTarget();
+
+ if (!frame_sp)
+ return StructuredData::ObjectSP();
+
+ StreamFileSP Stream(target.GetDebugger().GetOutputFile());
+
+ EvaluateExpressionOptions options;
+ options.SetUnwindOnError(true);
+ options.SetTryAllThreads(true);
+ options.SetStopOthers(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetTimeout(process_sp->GetUtilityExpressionTimeout());
+ options.SetPrefix(ub_sanitizer_retrieve_report_data_prefix);
+ options.SetAutoApplyFixIts(false);
+ options.SetLanguage(eLanguageTypeObjC_plus_plus);
+
+ ValueObjectSP main_value;
+ ExecutionContext exe_ctx;
+ Status eval_error;
+ frame_sp->CalculateExecutionContext(exe_ctx);
+ ExpressionResults result = UserExpression::Evaluate(
+ exe_ctx, options, ub_sanitizer_retrieve_report_data_command, "",
+ main_value, eval_error);
+ if (result != eExpressionCompleted) {
+ target.GetDebugger().GetAsyncOutputStream()->Printf(
+ "Warning: Cannot evaluate UndefinedBehaviorSanitizer expression:\n%s\n",
+ eval_error.AsCString());
+ return StructuredData::ObjectSP();
+ }
+
+ // Gather the PCs of the user frames in the backtrace.
+ StructuredData::Array *trace = new StructuredData::Array();
+ auto trace_sp = StructuredData::ObjectSP(trace);
+ for (unsigned I = 0; I < thread_sp->GetStackFrameCount(); ++I) {
+ const Address FCA =
+ thread_sp->GetStackFrameAtIndex(I)->GetFrameCodeAddress();
+ if (FCA.GetModule() == runtime_module_sp) // Skip PCs from the runtime.
+ continue;
+
+ lldb::addr_t PC = FCA.GetLoadAddress(&target);
+ trace->AddItem(StructuredData::ObjectSP(new StructuredData::Integer(PC)));
+ }
+
+ std::string IssueKind = RetrieveString(main_value, process_sp, ".issue_kind");
+ std::string ErrMessage = RetrieveString(main_value, process_sp, ".message");
+ std::string Filename = RetrieveString(main_value, process_sp, ".filename");
+ unsigned Line = RetrieveUnsigned(main_value, process_sp, ".line");
+ unsigned Col = RetrieveUnsigned(main_value, process_sp, ".col");
+ uintptr_t MemoryAddr =
+ RetrieveUnsigned(main_value, process_sp, ".memory_addr");
+
+ auto *d = new StructuredData::Dictionary();
+ auto dict_sp = StructuredData::ObjectSP(d);
+ d->AddStringItem("instrumentation_class", "UndefinedBehaviorSanitizer");
+ d->AddStringItem("description", IssueKind);
+ d->AddStringItem("summary", ErrMessage);
+ d->AddStringItem("filename", Filename);
+ d->AddIntegerItem("line", Line);
+ d->AddIntegerItem("col", Col);
+ d->AddIntegerItem("memory_address", MemoryAddr);
+ d->AddIntegerItem("tid", thread_sp->GetID());
+ d->AddItem("trace", trace_sp);
+ return dict_sp;
+}
+
+static std::string GetStopReasonDescription(StructuredData::ObjectSP report) {
+ llvm::StringRef stop_reason_description_ref;
+ report->GetAsDictionary()->GetValueForKeyAsString("description",
+ stop_reason_description_ref);
+ std::string stop_reason_description = stop_reason_description_ref;
+
+ if (!stop_reason_description.size()) {
+ stop_reason_description = "Undefined behavior detected";
+ } else {
+ stop_reason_description[0] = toupper(stop_reason_description[0]);
+ for (unsigned I = 1; I < stop_reason_description.size(); ++I)
+ if (stop_reason_description[I] == '-')
+ stop_reason_description[I] = ' ';
+ }
+ return stop_reason_description;
+}
+
+bool UndefinedBehaviorSanitizerRuntime::NotifyBreakpointHit(
+ void *baton, StoppointCallbackContext *context, user_id_t break_id,
+ user_id_t break_loc_id) {
+ assert(baton && "null baton");
+ if (!baton)
+ return false; //< false => resume execution.
+
+ UndefinedBehaviorSanitizerRuntime *const instance =
+ static_cast<UndefinedBehaviorSanitizerRuntime *>(baton);
+
+ ProcessSP process_sp = instance->GetProcessSP();
+ ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP();
+ if (!process_sp || !thread_sp ||
+ process_sp != context->exe_ctx_ref.GetProcessSP())
+ return false;
+
+ if (process_sp->GetModIDRef().IsLastResumeForUserExpression())
+ return false;
+
+ StructuredData::ObjectSP report =
+ instance->RetrieveReportData(context->exe_ctx_ref);
+
+ if (report) {
+ thread_sp->SetStopInfo(
+ InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData(
+ *thread_sp, GetStopReasonDescription(report), report));
+ return true;
+ }
+
+ return false;
+}
+
+const RegularExpression &
+UndefinedBehaviorSanitizerRuntime::GetPatternForRuntimeLibrary() {
+ static RegularExpression regex(llvm::StringRef("libclang_rt\\.(a|t|ub)san_"));
+ return regex;
+}
+
+bool UndefinedBehaviorSanitizerRuntime::CheckIfRuntimeIsValid(
+ const lldb::ModuleSP module_sp) {
+ static ConstString ubsan_test_sym("__ubsan_on_report");
+ const Symbol *symbol = module_sp->FindFirstSymbolWithNameAndType(
+ ubsan_test_sym, lldb::eSymbolTypeAny);
+ return symbol != nullptr;
+}
+
+// FIXME: Factor out all the logic we have in common with the {a,t}san plugins.
+void UndefinedBehaviorSanitizerRuntime::Activate() {
+ if (IsActive())
+ return;
+
+ ProcessSP process_sp = GetProcessSP();
+ if (!process_sp)
+ return;
+
+ ModuleSP runtime_module_sp = GetRuntimeModuleSP();
+
+ ConstString symbol_name("__ubsan_on_report");
+ const Symbol *symbol = runtime_module_sp->FindFirstSymbolWithNameAndType(
+ symbol_name, eSymbolTypeCode);
+
+ if (symbol == nullptr)
+ return;
+
+ if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid())
+ return;
+
+ Target &target = process_sp->GetTarget();
+ addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target);
+
+ if (symbol_address == LLDB_INVALID_ADDRESS)
+ return;
+
+ Breakpoint *breakpoint =
+ process_sp->GetTarget()
+ .CreateBreakpoint(symbol_address, /*internal=*/true,
+ /*hardware=*/false)
+ .get();
+ breakpoint->SetCallback(
+ UndefinedBehaviorSanitizerRuntime::NotifyBreakpointHit, this, true);
+ breakpoint->SetBreakpointKind("undefined-behavior-sanitizer-report");
+ SetBreakpointID(breakpoint->GetID());
+
+ SetActive(true);
+}
+
+void UndefinedBehaviorSanitizerRuntime::Deactivate() {
+ SetActive(false);
+
+ auto BID = GetBreakpointID();
+ if (BID == LLDB_INVALID_BREAK_ID)
+ return;
+
+ if (ProcessSP process_sp = GetProcessSP()) {
+ process_sp->GetTarget().RemoveBreakpointByID(BID);
+ SetBreakpointID(LLDB_INVALID_BREAK_ID);
+ }
+}
+
+lldb::ThreadCollectionSP
+UndefinedBehaviorSanitizerRuntime::GetBacktracesFromExtendedStopInfo(
+ StructuredData::ObjectSP info) {
+ ThreadCollectionSP threads;
+ threads = std::make_shared<ThreadCollection>();
+
+ ProcessSP process_sp = GetProcessSP();
+
+ if (info->GetObjectForDotSeparatedPath("instrumentation_class")
+ ->GetStringValue() != "UndefinedBehaviorSanitizer")
+ return threads;
+
+ std::vector<lldb::addr_t> PCs;
+ auto trace = info->GetObjectForDotSeparatedPath("trace")->GetAsArray();
+ trace->ForEach([&PCs](StructuredData::Object *PC) -> bool {
+ PCs.push_back(PC->GetAsInteger()->GetValue());
+ return true;
+ });
+
+ if (PCs.empty())
+ return threads;
+
+ StructuredData::ObjectSP thread_id_obj =
+ info->GetObjectForDotSeparatedPath("tid");
+ tid_t tid = thread_id_obj ? thread_id_obj->GetIntegerValue() : 0;
+
+ HistoryThread *history_thread = new HistoryThread(*process_sp, tid, PCs);
+ ThreadSP new_thread_sp(history_thread);
+ std::string stop_reason_description = GetStopReasonDescription(info);
+ new_thread_sp->SetName(stop_reason_description.c_str());
+
+ // Save this in the Process' ExtendedThreadList so a strong pointer retains
+ // the object
+ process_sp->GetExtendedThreadList().AddThread(new_thread_sp);
+ threads->AddThread(new_thread_sp);
+
+ return threads;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.h b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.h
new file mode 100644
index 000000000000..1d854b7bf06f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/InstrumentationRuntime/UBSan/UBSanRuntime.h
@@ -0,0 +1,68 @@
+//===-- UBSanRuntime.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 liblldb_UndefinedBehaviorSanitizerRuntime_h_
+#define liblldb_UndefinedBehaviorSanitizerRuntime_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/InstrumentationRuntime.h"
+#include "lldb/Utility/StructuredData.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class UndefinedBehaviorSanitizerRuntime
+ : public lldb_private::InstrumentationRuntime {
+public:
+ ~UndefinedBehaviorSanitizerRuntime() override;
+
+ static lldb::InstrumentationRuntimeSP
+ CreateInstance(const lldb::ProcessSP &process_sp);
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static lldb::InstrumentationRuntimeType GetTypeStatic();
+
+ lldb_private::ConstString GetPluginName() override {
+ return GetPluginNameStatic();
+ }
+
+ virtual lldb::InstrumentationRuntimeType GetType() { return GetTypeStatic(); }
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ lldb::ThreadCollectionSP
+ GetBacktracesFromExtendedStopInfo(StructuredData::ObjectSP info) override;
+
+private:
+ UndefinedBehaviorSanitizerRuntime(const lldb::ProcessSP &process_sp)
+ : lldb_private::InstrumentationRuntime(process_sp) {}
+
+ const RegularExpression &GetPatternForRuntimeLibrary() override;
+
+ bool CheckIfRuntimeIsValid(const lldb::ModuleSP module_sp) override;
+
+ void Activate() override;
+
+ void Deactivate();
+
+ static bool NotifyBreakpointHit(void *baton,
+ StoppointCallbackContext *context,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id);
+
+ StructuredData::ObjectSP RetrieveReportData(ExecutionContextRef exe_ctx_ref);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_UndefinedBehaviorSanitizerRuntime_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp b/contrib/llvm-project/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp
new file mode 100644
index 000000000000..140d09ed43cf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp
@@ -0,0 +1,474 @@
+//===-- JITLoaderGDB.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/MathExtras.h"
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "JITLoaderGDB.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Debug Interface Structures
+enum jit_actions_t { JIT_NOACTION = 0, JIT_REGISTER_FN, JIT_UNREGISTER_FN };
+
+template <typename ptr_t> struct jit_code_entry {
+ ptr_t next_entry; // pointer
+ ptr_t prev_entry; // pointer
+ ptr_t symfile_addr; // pointer
+ uint64_t symfile_size;
+};
+
+template <typename ptr_t> struct jit_descriptor {
+ uint32_t version;
+ uint32_t action_flag; // Values are jit_action_t
+ ptr_t relevant_entry; // pointer
+ ptr_t first_entry; // pointer
+};
+
+namespace {
+
+enum EnableJITLoaderGDB {
+ eEnableJITLoaderGDBDefault,
+ eEnableJITLoaderGDBOn,
+ eEnableJITLoaderGDBOff,
+};
+
+static constexpr OptionEnumValueElement g_enable_jit_loader_gdb_enumerators[] = {
+ {eEnableJITLoaderGDBDefault, "default", "Enable JIT compilation interface "
+ "for all platforms except macOS"},
+ {eEnableJITLoaderGDBOn, "on", "Enable JIT compilation interface"},
+ {eEnableJITLoaderGDBOff, "off", "Disable JIT compilation interface"}
+ };
+
+static constexpr PropertyDefinition g_properties[] = {
+ {"enable", OptionValue::eTypeEnum, true,
+ eEnableJITLoaderGDBDefault, nullptr,
+ OptionEnumValues(g_enable_jit_loader_gdb_enumerators),
+ "Enable GDB's JIT compilation interface (default: enabled on "
+ "all platforms except macOS)"}};
+
+enum { ePropertyEnable, ePropertyEnableJITBreakpoint };
+
+class PluginProperties : public Properties {
+public:
+ static ConstString GetSettingName() {
+ return JITLoaderGDB::GetPluginNameStatic();
+ }
+
+ PluginProperties() {
+ m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
+ m_collection_sp->Initialize(g_properties);
+ }
+
+ EnableJITLoaderGDB GetEnable() const {
+ return (EnableJITLoaderGDB)m_collection_sp->GetPropertyAtIndexAsEnumeration(
+ nullptr, ePropertyEnable,
+ g_properties[ePropertyEnable].default_uint_value);
+ }
+};
+
+typedef std::shared_ptr<PluginProperties> JITLoaderGDBPropertiesSP;
+
+static const JITLoaderGDBPropertiesSP &GetGlobalPluginProperties() {
+ static const auto g_settings_sp(std::make_shared<PluginProperties>());
+ return g_settings_sp;
+}
+
+template <typename ptr_t>
+bool ReadJITEntry(const addr_t from_addr, Process *process,
+ jit_code_entry<ptr_t> *entry) {
+ lldbassert(from_addr % sizeof(ptr_t) == 0);
+
+ ArchSpec::Core core = process->GetTarget().GetArchitecture().GetCore();
+ bool i386_target = ArchSpec::kCore_x86_32_first <= core &&
+ core <= ArchSpec::kCore_x86_32_last;
+ uint8_t uint64_align_bytes = i386_target ? 4 : 8;
+ const size_t data_byte_size =
+ llvm::alignTo(sizeof(ptr_t) * 3, uint64_align_bytes) + sizeof(uint64_t);
+
+ Status error;
+ DataBufferHeap data(data_byte_size, 0);
+ size_t bytes_read = process->ReadMemory(from_addr, data.GetBytes(),
+ data.GetByteSize(), error);
+ if (bytes_read != data_byte_size || !error.Success())
+ return false;
+
+ DataExtractor extractor(data.GetBytes(), data.GetByteSize(),
+ process->GetByteOrder(), sizeof(ptr_t));
+ lldb::offset_t offset = 0;
+ entry->next_entry = extractor.GetPointer(&offset);
+ entry->prev_entry = extractor.GetPointer(&offset);
+ entry->symfile_addr = extractor.GetPointer(&offset);
+ offset = llvm::alignTo(offset, uint64_align_bytes);
+ entry->symfile_size = extractor.GetU64(&offset);
+
+ return true;
+}
+
+} // anonymous namespace end
+
+JITLoaderGDB::JITLoaderGDB(lldb_private::Process *process)
+ : JITLoader(process), m_jit_objects(),
+ m_jit_break_id(LLDB_INVALID_BREAK_ID),
+ m_jit_descriptor_addr(LLDB_INVALID_ADDRESS) {}
+
+JITLoaderGDB::~JITLoaderGDB() {
+ if (LLDB_BREAK_ID_IS_VALID(m_jit_break_id))
+ m_process->GetTarget().RemoveBreakpointByID(m_jit_break_id);
+}
+
+void JITLoaderGDB::DebuggerInitialize(Debugger &debugger) {
+ if (!PluginManager::GetSettingForJITLoaderPlugin(
+ debugger, PluginProperties::GetSettingName())) {
+ const bool is_global_setting = true;
+ PluginManager::CreateSettingForJITLoaderPlugin(
+ debugger, GetGlobalPluginProperties()->GetValueProperties(),
+ ConstString("Properties for the JIT LoaderGDB plug-in."),
+ is_global_setting);
+ }
+}
+
+void JITLoaderGDB::DidAttach() {
+ Target &target = m_process->GetTarget();
+ ModuleList &module_list = target.GetImages();
+ SetJITBreakpoint(module_list);
+}
+
+void JITLoaderGDB::DidLaunch() {
+ Target &target = m_process->GetTarget();
+ ModuleList &module_list = target.GetImages();
+ SetJITBreakpoint(module_list);
+}
+
+void JITLoaderGDB::ModulesDidLoad(ModuleList &module_list) {
+ if (!DidSetJITBreakpoint() && m_process->IsAlive())
+ SetJITBreakpoint(module_list);
+}
+
+// Setup the JIT Breakpoint
+void JITLoaderGDB::SetJITBreakpoint(lldb_private::ModuleList &module_list) {
+ if (DidSetJITBreakpoint())
+ return;
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_JIT_LOADER));
+ if (log)
+ log->Printf("JITLoaderGDB::%s looking for JIT register hook", __FUNCTION__);
+
+ addr_t jit_addr = GetSymbolAddress(
+ module_list, ConstString("__jit_debug_register_code"), eSymbolTypeAny);
+ if (jit_addr == LLDB_INVALID_ADDRESS)
+ return;
+
+ m_jit_descriptor_addr = GetSymbolAddress(
+ module_list, ConstString("__jit_debug_descriptor"), eSymbolTypeData);
+ if (m_jit_descriptor_addr == LLDB_INVALID_ADDRESS) {
+ if (log)
+ log->Printf("JITLoaderGDB::%s failed to find JIT descriptor address",
+ __FUNCTION__);
+ return;
+ }
+
+ if (log)
+ log->Printf("JITLoaderGDB::%s setting JIT breakpoint", __FUNCTION__);
+
+ Breakpoint *bp =
+ m_process->GetTarget().CreateBreakpoint(jit_addr, true, false).get();
+ bp->SetCallback(JITDebugBreakpointHit, this, true);
+ bp->SetBreakpointKind("jit-debug-register");
+ m_jit_break_id = bp->GetID();
+
+ ReadJITDescriptor(true);
+}
+
+bool JITLoaderGDB::JITDebugBreakpointHit(void *baton,
+ StoppointCallbackContext *context,
+ user_id_t break_id,
+ user_id_t break_loc_id) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_JIT_LOADER));
+ if (log)
+ log->Printf("JITLoaderGDB::%s hit JIT breakpoint", __FUNCTION__);
+ JITLoaderGDB *instance = static_cast<JITLoaderGDB *>(baton);
+ return instance->ReadJITDescriptor(false);
+}
+
+static void updateSectionLoadAddress(const SectionList &section_list,
+ Target &target, uint64_t symbolfile_addr,
+ uint64_t symbolfile_size,
+ uint64_t &vmaddrheuristic,
+ uint64_t &min_addr, uint64_t &max_addr) {
+ const uint32_t num_sections = section_list.GetSize();
+ for (uint32_t i = 0; i < num_sections; ++i) {
+ SectionSP section_sp(section_list.GetSectionAtIndex(i));
+ if (section_sp) {
+ if (section_sp->IsFake()) {
+ uint64_t lower = (uint64_t)-1;
+ uint64_t upper = 0;
+ updateSectionLoadAddress(section_sp->GetChildren(), target,
+ symbolfile_addr, symbolfile_size,
+ vmaddrheuristic, lower, upper);
+ if (lower < min_addr)
+ min_addr = lower;
+ if (upper > max_addr)
+ max_addr = upper;
+ const lldb::addr_t slide_amount = lower - section_sp->GetFileAddress();
+ section_sp->Slide(slide_amount, false);
+ section_sp->GetChildren().Slide(-slide_amount, false);
+ section_sp->SetByteSize(upper - lower);
+ } else {
+ vmaddrheuristic += 2 << section_sp->GetLog2Align();
+ uint64_t lower;
+ if (section_sp->GetFileAddress() > vmaddrheuristic)
+ lower = section_sp->GetFileAddress();
+ else {
+ lower = symbolfile_addr + section_sp->GetFileOffset();
+ section_sp->SetFileAddress(symbolfile_addr +
+ section_sp->GetFileOffset());
+ }
+ target.SetSectionLoadAddress(section_sp, lower, true);
+ uint64_t upper = lower + section_sp->GetByteSize();
+ if (lower < min_addr)
+ min_addr = lower;
+ if (upper > max_addr)
+ max_addr = upper;
+ // This is an upper bound, but a good enough heuristic
+ vmaddrheuristic += section_sp->GetByteSize();
+ }
+ }
+ }
+}
+
+bool JITLoaderGDB::ReadJITDescriptor(bool all_entries) {
+ if (m_process->GetTarget().GetArchitecture().GetAddressByteSize() == 8)
+ return ReadJITDescriptorImpl<uint64_t>(all_entries);
+ else
+ return ReadJITDescriptorImpl<uint32_t>(all_entries);
+}
+
+template <typename ptr_t>
+bool JITLoaderGDB::ReadJITDescriptorImpl(bool all_entries) {
+ if (m_jit_descriptor_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_JIT_LOADER));
+ Target &target = m_process->GetTarget();
+ ModuleList &module_list = target.GetImages();
+
+ jit_descriptor<ptr_t> jit_desc;
+ const size_t jit_desc_size = sizeof(jit_desc);
+ Status error;
+ size_t bytes_read = m_process->DoReadMemory(m_jit_descriptor_addr, &jit_desc,
+ jit_desc_size, error);
+ if (bytes_read != jit_desc_size || !error.Success()) {
+ if (log)
+ log->Printf("JITLoaderGDB::%s failed to read JIT descriptor",
+ __FUNCTION__);
+ return false;
+ }
+
+ jit_actions_t jit_action = (jit_actions_t)jit_desc.action_flag;
+ addr_t jit_relevant_entry = (addr_t)jit_desc.relevant_entry;
+ if (all_entries) {
+ jit_action = JIT_REGISTER_FN;
+ jit_relevant_entry = (addr_t)jit_desc.first_entry;
+ }
+
+ while (jit_relevant_entry != 0) {
+ jit_code_entry<ptr_t> jit_entry;
+ if (!ReadJITEntry(jit_relevant_entry, m_process, &jit_entry)) {
+ if (log)
+ log->Printf("JITLoaderGDB::%s failed to read JIT entry at 0x%" PRIx64,
+ __FUNCTION__, jit_relevant_entry);
+ return false;
+ }
+
+ const addr_t &symbolfile_addr = (addr_t)jit_entry.symfile_addr;
+ const size_t &symbolfile_size = (size_t)jit_entry.symfile_size;
+ ModuleSP module_sp;
+
+ if (jit_action == JIT_REGISTER_FN) {
+ if (log)
+ log->Printf("JITLoaderGDB::%s registering JIT entry at 0x%" PRIx64
+ " (%" PRIu64 " bytes)",
+ __FUNCTION__, symbolfile_addr, (uint64_t)symbolfile_size);
+
+ char jit_name[64];
+ snprintf(jit_name, 64, "JIT(0x%" PRIx64 ")", symbolfile_addr);
+ module_sp = m_process->ReadModuleFromMemory(
+ FileSpec(jit_name), symbolfile_addr, symbolfile_size);
+
+ if (module_sp && module_sp->GetObjectFile()) {
+ // Object formats (like ELF) have no representation for a JIT type.
+ // We will get it wrong, if we deduce it from the header.
+ module_sp->GetObjectFile()->SetType(ObjectFile::eTypeJIT);
+
+ // load the symbol table right away
+ module_sp->GetObjectFile()->GetSymtab();
+
+ m_jit_objects.insert(std::make_pair(symbolfile_addr, module_sp));
+ if (module_sp->GetObjectFile()->GetPluginName() ==
+ ConstString("mach-o")) {
+ ObjectFile *image_object_file = module_sp->GetObjectFile();
+ if (image_object_file) {
+ const SectionList *section_list =
+ image_object_file->GetSectionList();
+ if (section_list) {
+ uint64_t vmaddrheuristic = 0;
+ uint64_t lower = (uint64_t)-1;
+ uint64_t upper = 0;
+ updateSectionLoadAddress(*section_list, target, symbolfile_addr,
+ symbolfile_size, vmaddrheuristic, lower,
+ upper);
+ }
+ }
+ } else {
+ bool changed = false;
+ module_sp->SetLoadAddress(target, 0, true, changed);
+ }
+
+ module_list.AppendIfNeeded(module_sp);
+
+ ModuleList module_list;
+ module_list.Append(module_sp);
+ target.ModulesDidLoad(module_list);
+ } else {
+ if (log)
+ log->Printf("JITLoaderGDB::%s failed to load module for "
+ "JIT entry at 0x%" PRIx64,
+ __FUNCTION__, symbolfile_addr);
+ }
+ } else if (jit_action == JIT_UNREGISTER_FN) {
+ if (log)
+ log->Printf("JITLoaderGDB::%s unregistering JIT entry at 0x%" PRIx64,
+ __FUNCTION__, symbolfile_addr);
+
+ JITObjectMap::iterator it = m_jit_objects.find(symbolfile_addr);
+ if (it != m_jit_objects.end()) {
+ module_sp = it->second;
+ ObjectFile *image_object_file = module_sp->GetObjectFile();
+ if (image_object_file) {
+ const SectionList *section_list = image_object_file->GetSectionList();
+ if (section_list) {
+ const uint32_t num_sections = section_list->GetSize();
+ for (uint32_t i = 0; i < num_sections; ++i) {
+ SectionSP section_sp(section_list->GetSectionAtIndex(i));
+ if (section_sp) {
+ target.GetSectionLoadList().SetSectionUnloaded(section_sp);
+ }
+ }
+ }
+ }
+ module_list.Remove(module_sp);
+ m_jit_objects.erase(it);
+ }
+ } else if (jit_action == JIT_NOACTION) {
+ // Nothing to do
+ } else {
+ assert(false && "Unknown jit action");
+ }
+
+ if (all_entries)
+ jit_relevant_entry = (addr_t)jit_entry.next_entry;
+ else
+ jit_relevant_entry = 0;
+ }
+
+ return false; // Continue Running.
+}
+
+// PluginInterface protocol
+lldb_private::ConstString JITLoaderGDB::GetPluginNameStatic() {
+ static ConstString g_name("gdb");
+ return g_name;
+}
+
+JITLoaderSP JITLoaderGDB::CreateInstance(Process *process, bool force) {
+ JITLoaderSP jit_loader_sp;
+ bool enable;
+ switch (GetGlobalPluginProperties()->GetEnable()) {
+ case EnableJITLoaderGDB::eEnableJITLoaderGDBOn:
+ enable = true;
+ break;
+ case EnableJITLoaderGDB::eEnableJITLoaderGDBOff:
+ enable = false;
+ break;
+ case EnableJITLoaderGDB::eEnableJITLoaderGDBDefault:
+ ArchSpec arch(process->GetTarget().GetArchitecture());
+ enable = arch.GetTriple().getVendor() != llvm::Triple::Apple;
+ break;
+ }
+ if (enable)
+ jit_loader_sp = std::make_shared<JITLoaderGDB>(process);
+ return jit_loader_sp;
+}
+
+const char *JITLoaderGDB::GetPluginDescriptionStatic() {
+ return "JIT loader plug-in that watches for JIT events using the GDB "
+ "interface.";
+}
+
+lldb_private::ConstString JITLoaderGDB::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t JITLoaderGDB::GetPluginVersion() { return 1; }
+
+void JITLoaderGDB::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ DebuggerInitialize);
+}
+
+void JITLoaderGDB::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+bool JITLoaderGDB::DidSetJITBreakpoint() const {
+ return LLDB_BREAK_ID_IS_VALID(m_jit_break_id);
+}
+
+addr_t JITLoaderGDB::GetSymbolAddress(ModuleList &module_list,
+ ConstString name,
+ SymbolType symbol_type) const {
+ SymbolContextList target_symbols;
+ Target &target = m_process->GetTarget();
+
+ if (!module_list.FindSymbolsWithNameAndType(name, symbol_type,
+ target_symbols))
+ return LLDB_INVALID_ADDRESS;
+
+ SymbolContext sym_ctx;
+ target_symbols.GetContextAtIndex(0, sym_ctx);
+
+ const Address jit_descriptor_addr = sym_ctx.symbol->GetAddress();
+ if (!jit_descriptor_addr.IsValid())
+ return LLDB_INVALID_ADDRESS;
+
+ const addr_t jit_addr = jit_descriptor_addr.GetLoadAddress(&target);
+ return jit_addr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.h b/contrib/llvm-project/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.h
new file mode 100644
index 000000000000..2a2537c3edd4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.h
@@ -0,0 +1,80 @@
+//===-- JITLoaderGDB.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 liblldb_JITLoaderGDB_h_
+#define liblldb_JITLoaderGDB_h_
+
+#include <map>
+
+#include "lldb/Target/JITLoader.h"
+#include "lldb/Target/Process.h"
+
+class JITLoaderGDB : public lldb_private::JITLoader {
+public:
+ JITLoaderGDB(lldb_private::Process *process);
+
+ ~JITLoaderGDB() override;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb::JITLoaderSP CreateInstance(lldb_private::Process *process,
+ bool force);
+
+ static void DebuggerInitialize(lldb_private::Debugger &debugger);
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ // JITLoader interface
+ void DidAttach() override;
+
+ void DidLaunch() override;
+
+ void ModulesDidLoad(lldb_private::ModuleList &module_list) override;
+
+private:
+ lldb::addr_t GetSymbolAddress(lldb_private::ModuleList &module_list,
+ lldb_private::ConstString name,
+ lldb::SymbolType symbol_type) const;
+
+ void SetJITBreakpoint(lldb_private::ModuleList &module_list);
+
+ bool DidSetJITBreakpoint() const;
+
+ bool ReadJITDescriptor(bool all_entries);
+
+ template <typename ptr_t> bool ReadJITDescriptorImpl(bool all_entries);
+
+ static bool
+ JITDebugBreakpointHit(void *baton,
+ lldb_private::StoppointCallbackContext *context,
+ lldb::user_id_t break_id, lldb::user_id_t break_loc_id);
+
+ static void ProcessStateChangedCallback(void *baton,
+ lldb_private::Process *process,
+ lldb::StateType state);
+
+ // A collection of in-memory jitted object addresses and their corresponding
+ // modules
+ typedef std::map<lldb::addr_t, const lldb::ModuleSP> JITObjectMap;
+ JITObjectMap m_jit_objects;
+
+ lldb::user_id_t m_jit_break_id;
+ lldb::addr_t m_jit_descriptor_addr;
+};
+
+#endif // liblldb_JITLoaderGDB_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.cpp
new file mode 100644
index 000000000000..87b5b5947f35
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.cpp
@@ -0,0 +1,205 @@
+//===-- BlockPointer.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 "BlockPointer.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangASTImporter.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Target/Target.h"
+
+#include "lldb/Utility/LLDBAssert.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+namespace lldb_private {
+namespace formatters {
+
+class BlockPointerSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ BlockPointerSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_block_struct_type() {
+ CompilerType block_pointer_type(m_backend.GetCompilerType());
+ CompilerType function_pointer_type;
+ block_pointer_type.IsBlockPointerType(&function_pointer_type);
+
+ TargetSP target_sp(m_backend.GetTargetSP());
+
+ if (!target_sp) {
+ return;
+ }
+
+ Status err;
+ TypeSystem *type_system = target_sp->GetScratchTypeSystemForLanguage(
+ &err, lldb::eLanguageTypeC_plus_plus);
+
+ if (!err.Success() || !type_system) {
+ return;
+ }
+
+ ClangASTContext *clang_ast_context =
+ llvm::dyn_cast<ClangASTContext>(type_system);
+
+ if (!clang_ast_context) {
+ return;
+ }
+
+ ClangASTImporterSP clang_ast_importer = target_sp->GetClangASTImporter();
+
+ if (!clang_ast_importer) {
+ return;
+ }
+
+ const char *const isa_name("__isa");
+ const CompilerType isa_type =
+ clang_ast_context->GetBasicType(lldb::eBasicTypeObjCClass);
+ const char *const flags_name("__flags");
+ const CompilerType flags_type =
+ clang_ast_context->GetBasicType(lldb::eBasicTypeInt);
+ const char *const reserved_name("__reserved");
+ const CompilerType reserved_type =
+ clang_ast_context->GetBasicType(lldb::eBasicTypeInt);
+ const char *const FuncPtr_name("__FuncPtr");
+ const CompilerType FuncPtr_type =
+ clang_ast_importer->CopyType(*clang_ast_context, function_pointer_type);
+
+ m_block_struct_type = clang_ast_context->CreateStructForIdentifier(
+ ConstString(), {{isa_name, isa_type},
+ {flags_name, flags_type},
+ {reserved_name, reserved_type},
+ {FuncPtr_name, FuncPtr_type}});
+ }
+
+ ~BlockPointerSyntheticFrontEnd() override = default;
+
+ size_t CalculateNumChildren() override {
+ const bool omit_empty_base_classes = false;
+ return m_block_struct_type.GetNumChildren(omit_empty_base_classes, nullptr);
+ }
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override {
+ if (!m_block_struct_type.IsValid()) {
+ return lldb::ValueObjectSP();
+ }
+
+ if (idx >= CalculateNumChildren()) {
+ return lldb::ValueObjectSP();
+ }
+
+ const bool thread_and_frame_only_if_stopped = true;
+ ExecutionContext exe_ctx = m_backend.GetExecutionContextRef().Lock(
+ thread_and_frame_only_if_stopped);
+ const bool transparent_pointers = false;
+ const bool omit_empty_base_classes = false;
+ const bool ignore_array_bounds = false;
+ ValueObject *value_object = nullptr;
+
+ std::string child_name;
+ uint32_t child_byte_size = 0;
+ int32_t child_byte_offset = 0;
+ uint32_t child_bitfield_bit_size = 0;
+ uint32_t child_bitfield_bit_offset = 0;
+ bool child_is_base_class = false;
+ bool child_is_deref_of_parent = false;
+ uint64_t language_flags = 0;
+
+ const CompilerType child_type =
+ m_block_struct_type.GetChildCompilerTypeAtIndex(
+ &exe_ctx, idx, transparent_pointers, omit_empty_base_classes,
+ ignore_array_bounds, child_name, child_byte_size, child_byte_offset,
+ child_bitfield_bit_size, child_bitfield_bit_offset,
+ child_is_base_class, child_is_deref_of_parent, value_object,
+ language_flags);
+
+ ValueObjectSP struct_pointer_sp =
+ m_backend.Cast(m_block_struct_type.GetPointerType());
+
+ if (!struct_pointer_sp) {
+ return lldb::ValueObjectSP();
+ }
+
+ Status err;
+ ValueObjectSP struct_sp = struct_pointer_sp->Dereference(err);
+
+ if (!struct_sp || !err.Success()) {
+ return lldb::ValueObjectSP();
+ }
+
+ ValueObjectSP child_sp(struct_sp->GetSyntheticChildAtOffset(
+ child_byte_offset, child_type, true,
+ ConstString(child_name.c_str(), child_name.size())));
+
+ return child_sp;
+ }
+
+ // return true if this object is now safe to use forever without ever
+ // updating again; the typical (and tested) answer here is 'false'
+ bool Update() override { return false; }
+
+ // maybe return false if the block pointer is, say, null
+ bool MightHaveChildren() override { return true; }
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ if (!m_block_struct_type.IsValid())
+ return UINT32_MAX;
+
+ const bool omit_empty_base_classes = false;
+ return m_block_struct_type.GetIndexOfChildWithName(name.AsCString(),
+ omit_empty_base_classes);
+ }
+
+private:
+ CompilerType m_block_struct_type;
+};
+
+} // namespace formatters
+} // namespace lldb_private
+
+bool lldb_private::formatters::BlockPointerSummaryProvider(
+ ValueObject &valobj, Stream &s, const TypeSummaryOptions &) {
+ lldb_private::SyntheticChildrenFrontEnd *synthetic_children =
+ BlockPointerSyntheticFrontEndCreator(nullptr, valobj.GetSP());
+ if (!synthetic_children) {
+ return false;
+ }
+
+ synthetic_children->Update();
+
+ static const ConstString s_FuncPtr_name("__FuncPtr");
+
+ lldb::ValueObjectSP child_sp = synthetic_children->GetChildAtIndex(
+ synthetic_children->GetIndexOfChildWithName(s_FuncPtr_name));
+
+ if (!child_sp) {
+ return false;
+ }
+
+ lldb::ValueObjectSP qualified_child_representation_sp =
+ child_sp->GetQualifiedRepresentationIfAvailable(
+ lldb::eDynamicDontRunTarget, true);
+
+ const char *child_value =
+ qualified_child_representation_sp->GetValueAsCString();
+
+ s.Printf("%s", child_value);
+
+ return true;
+}
+
+lldb_private::SyntheticChildrenFrontEnd *
+lldb_private::formatters::BlockPointerSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ if (!valobj_sp)
+ return nullptr;
+ return new BlockPointerSyntheticFrontEnd(valobj_sp);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.h
new file mode 100644
index 000000000000..624c17a6a6af
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.h
@@ -0,0 +1,25 @@
+//===-- BlockPointer.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 liblldb_BlockPointer_h_
+#define liblldb_BlockPointer_h_
+
+#include "lldb/lldb-forward.h"
+
+namespace lldb_private {
+namespace formatters {
+bool BlockPointerSummaryProvider(ValueObject &, Stream &,
+ const TypeSummaryOptions &);
+
+SyntheticChildrenFrontEnd *
+BlockPointerSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // liblldb_BlockPointer_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
new file mode 100644
index 000000000000..44b9e5e24ccd
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -0,0 +1,1071 @@
+//===-- CPlusPlusLanguage.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 "CPlusPlusLanguage.h"
+
+#include <cctype>
+#include <cstring>
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <set>
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Demangle/ItaniumDemangle.h"
+
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/UniqueCStringMap.h"
+#include "lldb/DataFormatters/CXXFunctionPointer.h"
+#include "lldb/DataFormatters/DataVisualization.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/DataFormatters/VectorType.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegularExpression.h"
+
+#include "BlockPointer.h"
+#include "CPlusPlusNameParser.h"
+#include "CxxStringTypes.h"
+#include "LibCxx.h"
+#include "LibCxxAtomic.h"
+#include "LibCxxVariant.h"
+#include "LibStdcpp.h"
+#include "MSVCUndecoratedNameParser.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+void CPlusPlusLanguage::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(), "C++ Language",
+ CreateInstance);
+}
+
+void CPlusPlusLanguage::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString CPlusPlusLanguage::GetPluginNameStatic() {
+ static ConstString g_name("cplusplus");
+ return g_name;
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString CPlusPlusLanguage::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t CPlusPlusLanguage::GetPluginVersion() { return 1; }
+
+// Static Functions
+
+Language *CPlusPlusLanguage::CreateInstance(lldb::LanguageType language) {
+ if (Language::LanguageIsCPlusPlus(language))
+ return new CPlusPlusLanguage();
+ return nullptr;
+}
+
+void CPlusPlusLanguage::MethodName::Clear() {
+ m_full.Clear();
+ m_basename = llvm::StringRef();
+ m_context = llvm::StringRef();
+ m_arguments = llvm::StringRef();
+ m_qualifiers = llvm::StringRef();
+ m_parsed = false;
+ m_parse_error = false;
+}
+
+static bool ReverseFindMatchingChars(const llvm::StringRef &s,
+ const llvm::StringRef &left_right_chars,
+ size_t &left_pos, size_t &right_pos,
+ size_t pos = llvm::StringRef::npos) {
+ assert(left_right_chars.size() == 2);
+ left_pos = llvm::StringRef::npos;
+ const char left_char = left_right_chars[0];
+ const char right_char = left_right_chars[1];
+ pos = s.find_last_of(left_right_chars, pos);
+ if (pos == llvm::StringRef::npos || s[pos] == left_char)
+ return false;
+ right_pos = pos;
+ uint32_t depth = 1;
+ while (pos > 0 && depth > 0) {
+ pos = s.find_last_of(left_right_chars, pos);
+ if (pos == llvm::StringRef::npos)
+ return false;
+ if (s[pos] == left_char) {
+ if (--depth == 0) {
+ left_pos = pos;
+ return left_pos < right_pos;
+ }
+ } else if (s[pos] == right_char) {
+ ++depth;
+ }
+ }
+ return false;
+}
+
+static bool IsTrivialBasename(const llvm::StringRef &basename) {
+ // Check that the basename matches with the following regular expression
+ // "^~?([A-Za-z_][A-Za-z_0-9]*)$" We are using a hand written implementation
+ // because it is significantly more efficient then using the general purpose
+ // regular expression library.
+ size_t idx = 0;
+ if (basename.size() > 0 && basename[0] == '~')
+ idx = 1;
+
+ if (basename.size() <= idx)
+ return false; // Empty string or "~"
+
+ if (!std::isalpha(basename[idx]) && basename[idx] != '_')
+ return false; // First charater (after removing the possible '~'') isn't in
+ // [A-Za-z_]
+
+ // Read all characters matching [A-Za-z_0-9]
+ ++idx;
+ while (idx < basename.size()) {
+ if (!std::isalnum(basename[idx]) && basename[idx] != '_')
+ break;
+ ++idx;
+ }
+
+ // We processed all characters. It is a vaild basename.
+ return idx == basename.size();
+}
+
+bool CPlusPlusLanguage::MethodName::TrySimplifiedParse() {
+ // This method tries to parse simple method definitions which are presumably
+ // most comman in user programs. Definitions that can be parsed by this
+ // function don't have return types and templates in the name.
+ // A::B::C::fun(std::vector<T> &) const
+ size_t arg_start, arg_end;
+ llvm::StringRef full(m_full.GetCString());
+ llvm::StringRef parens("()", 2);
+ if (ReverseFindMatchingChars(full, parens, arg_start, arg_end)) {
+ m_arguments = full.substr(arg_start, arg_end - arg_start + 1);
+ if (arg_end + 1 < full.size())
+ m_qualifiers = full.substr(arg_end + 1).ltrim();
+
+ if (arg_start == 0)
+ return false;
+ size_t basename_end = arg_start;
+ size_t context_start = 0;
+ size_t context_end = full.rfind(':', basename_end);
+ if (context_end == llvm::StringRef::npos)
+ m_basename = full.substr(0, basename_end);
+ else {
+ if (context_start < context_end)
+ m_context = full.substr(context_start, context_end - 1 - context_start);
+ const size_t basename_begin = context_end + 1;
+ m_basename = full.substr(basename_begin, basename_end - basename_begin);
+ }
+
+ if (IsTrivialBasename(m_basename)) {
+ return true;
+ } else {
+ // The C++ basename doesn't match our regular expressions so this can't
+ // be a valid C++ method, clear everything out and indicate an error
+ m_context = llvm::StringRef();
+ m_basename = llvm::StringRef();
+ m_arguments = llvm::StringRef();
+ m_qualifiers = llvm::StringRef();
+ return false;
+ }
+ }
+ return false;
+}
+
+void CPlusPlusLanguage::MethodName::Parse() {
+ if (!m_parsed && m_full) {
+ if (TrySimplifiedParse()) {
+ m_parse_error = false;
+ } else {
+ CPlusPlusNameParser parser(m_full.GetStringRef());
+ if (auto function = parser.ParseAsFunctionDefinition()) {
+ m_basename = function.getValue().name.basename;
+ m_context = function.getValue().name.context;
+ m_arguments = function.getValue().arguments;
+ m_qualifiers = function.getValue().qualifiers;
+ m_parse_error = false;
+ } else {
+ m_parse_error = true;
+ }
+ }
+ m_parsed = true;
+ }
+}
+
+llvm::StringRef CPlusPlusLanguage::MethodName::GetBasename() {
+ if (!m_parsed)
+ Parse();
+ return m_basename;
+}
+
+llvm::StringRef CPlusPlusLanguage::MethodName::GetContext() {
+ if (!m_parsed)
+ Parse();
+ return m_context;
+}
+
+llvm::StringRef CPlusPlusLanguage::MethodName::GetArguments() {
+ if (!m_parsed)
+ Parse();
+ return m_arguments;
+}
+
+llvm::StringRef CPlusPlusLanguage::MethodName::GetQualifiers() {
+ if (!m_parsed)
+ Parse();
+ return m_qualifiers;
+}
+
+std::string CPlusPlusLanguage::MethodName::GetScopeQualifiedName() {
+ if (!m_parsed)
+ Parse();
+ if (m_context.empty())
+ return m_basename;
+
+ std::string res;
+ res += m_context;
+ res += "::";
+ res += m_basename;
+ return res;
+}
+
+bool CPlusPlusLanguage::IsCPPMangledName(const char *name) {
+ // FIXME!! we should really run through all the known C++ Language plugins
+ // and ask each one if this is a C++ mangled name
+
+ if (name == nullptr)
+ return false;
+
+ // MSVC style mangling
+ if (name[0] == '?')
+ return true;
+
+ return (name[0] != '\0' && name[0] == '_' && name[1] == 'Z');
+}
+
+bool CPlusPlusLanguage::ExtractContextAndIdentifier(
+ const char *name, llvm::StringRef &context, llvm::StringRef &identifier) {
+ if (MSVCUndecoratedNameParser::IsMSVCUndecoratedName(name))
+ return MSVCUndecoratedNameParser::ExtractContextAndIdentifier(name, context,
+ identifier);
+
+ CPlusPlusNameParser parser(name);
+ if (auto full_name = parser.ParseAsFullName()) {
+ identifier = full_name.getValue().basename;
+ context = full_name.getValue().context;
+ return true;
+ }
+ return false;
+}
+
+namespace {
+class NodeAllocator {
+ llvm::BumpPtrAllocator Alloc;
+
+public:
+ void reset() { Alloc.Reset(); }
+
+ template <typename T, typename... Args> T *makeNode(Args &&... args) {
+ return new (Alloc.Allocate(sizeof(T), alignof(T)))
+ T(std::forward<Args>(args)...);
+ }
+
+ void *allocateNodeArray(size_t sz) {
+ return Alloc.Allocate(sizeof(llvm::itanium_demangle::Node *) * sz,
+ alignof(llvm::itanium_demangle::Node *));
+ }
+};
+
+/// Given a mangled function `Mangled`, replace all the primitive function type
+/// arguments of `Search` with type `Replace`.
+class TypeSubstitutor
+ : public llvm::itanium_demangle::AbstractManglingParser<TypeSubstitutor,
+ NodeAllocator> {
+ /// Input character until which we have constructed the respective output
+ /// already
+ const char *Written;
+
+ llvm::StringRef Search;
+ llvm::StringRef Replace;
+ llvm::SmallString<128> Result;
+
+ /// Whether we have performed any substitutions.
+ bool Substituted;
+
+ void reset(llvm::StringRef Mangled, llvm::StringRef Search,
+ llvm::StringRef Replace) {
+ AbstractManglingParser::reset(Mangled.begin(), Mangled.end());
+ Written = Mangled.begin();
+ this->Search = Search;
+ this->Replace = Replace;
+ Result.clear();
+ Substituted = false;
+ }
+
+ void appendUnchangedInput() {
+ Result += llvm::StringRef(Written, First - Written);
+ Written = First;
+ }
+
+public:
+ TypeSubstitutor() : AbstractManglingParser(nullptr, nullptr) {}
+
+ ConstString substitute(llvm::StringRef Mangled, llvm::StringRef From,
+ llvm::StringRef To) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE);
+
+ reset(Mangled, From, To);
+ if (parse() == nullptr) {
+ LLDB_LOG(log, "Failed to substitute mangling in {0}", Mangled);
+ return ConstString();
+ }
+ if (!Substituted)
+ return ConstString();
+
+ // Append any trailing unmodified input.
+ appendUnchangedInput();
+ LLDB_LOG(log, "Substituted mangling {0} -> {1}", Mangled, Result);
+ return ConstString(Result);
+ }
+
+ llvm::itanium_demangle::Node *parseType() {
+ if (llvm::StringRef(First, numLeft()).startswith(Search)) {
+ // We found a match. Append unmodified input up to this point.
+ appendUnchangedInput();
+
+ // And then perform the replacement.
+ Result += Replace;
+ Written += Search.size();
+ Substituted = true;
+ }
+ return AbstractManglingParser::parseType();
+ }
+};
+}
+
+uint32_t CPlusPlusLanguage::FindAlternateFunctionManglings(
+ const ConstString mangled_name, std::set<ConstString> &alternates) {
+ const auto start_size = alternates.size();
+ /// Get a basic set of alternative manglings for the given symbol `name`, by
+ /// making a few basic possible substitutions on basic types, storage duration
+ /// and `const`ness for the given symbol. The output parameter `alternates`
+ /// is filled with a best-guess, non-exhaustive set of different manglings
+ /// for the given name.
+
+ // Maybe we're looking for a const symbol but the debug info told us it was
+ // non-const...
+ if (!strncmp(mangled_name.GetCString(), "_ZN", 3) &&
+ strncmp(mangled_name.GetCString(), "_ZNK", 4)) {
+ std::string fixed_scratch("_ZNK");
+ fixed_scratch.append(mangled_name.GetCString() + 3);
+ alternates.insert(ConstString(fixed_scratch));
+ }
+
+ // Maybe we're looking for a static symbol but we thought it was global...
+ if (!strncmp(mangled_name.GetCString(), "_Z", 2) &&
+ strncmp(mangled_name.GetCString(), "_ZL", 3)) {
+ std::string fixed_scratch("_ZL");
+ fixed_scratch.append(mangled_name.GetCString() + 2);
+ alternates.insert(ConstString(fixed_scratch));
+ }
+
+ TypeSubstitutor TS;
+ // `char` is implementation defined as either `signed` or `unsigned`. As a
+ // result a char parameter has 3 possible manglings: 'c'-char, 'a'-signed
+ // char, 'h'-unsigned char. If we're looking for symbols with a signed char
+ // parameter, try finding matches which have the general case 'c'.
+ if (ConstString char_fixup =
+ TS.substitute(mangled_name.GetStringRef(), "a", "c"))
+ alternates.insert(char_fixup);
+
+ // long long parameter mangling 'x', may actually just be a long 'l' argument
+ if (ConstString long_fixup =
+ TS.substitute(mangled_name.GetStringRef(), "x", "l"))
+ alternates.insert(long_fixup);
+
+ // unsigned long long parameter mangling 'y', may actually just be unsigned
+ // long 'm' argument
+ if (ConstString ulong_fixup =
+ TS.substitute(mangled_name.GetStringRef(), "y", "m"))
+ alternates.insert(ulong_fixup);
+
+ return alternates.size() - start_size;
+}
+
+static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
+ if (!cpp_category_sp)
+ return;
+
+ TypeSummaryImpl::Flags stl_summary_flags;
+ stl_summary_flags.SetCascades(true)
+ .SetSkipPointers(false)
+ .SetSkipReferences(false)
+ .SetDontShowChildren(true)
+ .SetDontShowValue(true)
+ .SetShowMembersOneLiner(false)
+ .SetHideItemNames(false);
+
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxStringSummaryProviderASCII,
+ "std::string summary provider",
+ ConstString("^std::__[[:alnum:]]+::string$"), stl_summary_flags,
+ true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxStringSummaryProviderASCII,
+ "std::string summary provider",
+ ConstString("^std::__[[:alnum:]]+::basic_string<char, "
+ "std::__[[:alnum:]]+::char_traits<char>, "
+ "std::__[[:alnum:]]+::allocator<char> >$"),
+ stl_summary_flags, true);
+
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxStringSummaryProviderUTF16,
+ "std::u16string summary provider",
+ ConstString(
+ "^std::__[[:alnum:]]+::basic_string<char16_t, "
+ "std::__[[:alnum:]]+::char_traits<char16_t>, "
+ "std::__[[:alnum:]]+::allocator<char16_t> >$"),
+ stl_summary_flags, true);
+
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxStringSummaryProviderUTF32,
+ "std::u32string summary provider",
+ ConstString(
+ "^std::__[[:alnum:]]+::basic_string<char32_t, "
+ "std::__[[:alnum:]]+::char_traits<char32_t>, "
+ "std::__[[:alnum:]]+::allocator<char32_t> >$"),
+ stl_summary_flags, true);
+
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxWStringSummaryProvider,
+ "std::wstring summary provider",
+ ConstString("^std::__[[:alnum:]]+::wstring$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxWStringSummaryProvider,
+ "std::wstring summary provider",
+ ConstString("^std::__[[:alnum:]]+::basic_string<wchar_t, "
+ "std::__[[:alnum:]]+::char_traits<wchar_t>, "
+ "std::__[[:alnum:]]+::allocator<wchar_t> >$"),
+ stl_summary_flags, true);
+
+ SyntheticChildren::Flags stl_synth_flags;
+ stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(
+ false);
+ SyntheticChildren::Flags stl_deref_flags = stl_synth_flags;
+ stl_deref_flags.SetFrontEndWantsDereference();
+
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxBitsetSyntheticFrontEndCreator,
+ "libc++ std::bitset synthetic children",
+ ConstString("^std::__[[:alnum:]]+::bitset<.+>(( )?&)?$"), stl_deref_flags,
+ true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxStdVectorSyntheticFrontEndCreator,
+ "libc++ std::vector synthetic children",
+ ConstString("^std::__[[:alnum:]]+::vector<.+>(( )?&)?$"), stl_deref_flags,
+ true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxStdForwardListSyntheticFrontEndCreator,
+ "libc++ std::forward_list synthetic children",
+ ConstString("^std::__[[:alnum:]]+::forward_list<.+>(( )?&)?$"),
+ stl_synth_flags, true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxStdListSyntheticFrontEndCreator,
+ "libc++ std::list synthetic children",
+ ConstString("^std::__[[:alnum:]]+::list<.+>(( )?&)?$"), stl_deref_flags,
+ true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator,
+ "libc++ std::map synthetic children",
+ ConstString("^std::__[[:alnum:]]+::map<.+> >(( )?&)?$"), stl_synth_flags,
+ true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator,
+ "libc++ std::set synthetic children",
+ ConstString("^std::__[[:alnum:]]+::set<.+> >(( )?&)?$"), stl_deref_flags,
+ true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator,
+ "libc++ std::multiset synthetic children",
+ ConstString("^std::__[[:alnum:]]+::multiset<.+> >(( )?&)?$"),
+ stl_deref_flags, true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator,
+ "libc++ std::multimap synthetic children",
+ ConstString("^std::__[[:alnum:]]+::multimap<.+> >(( )?&)?$"),
+ stl_synth_flags, true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator,
+ "libc++ std::unordered containers synthetic children",
+ ConstString("^(std::__[[:alnum:]]+::)unordered_(multi)?(map|set)<.+> >$"),
+ stl_synth_flags, true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxInitializerListSyntheticFrontEndCreator,
+ "libc++ std::initializer_list synthetic children",
+ ConstString("^std::initializer_list<.+>(( )?&)?$"), stl_synth_flags,
+ true);
+ AddCXXSynthetic(cpp_category_sp, LibcxxQueueFrontEndCreator,
+ "libc++ std::queue synthetic children",
+ ConstString("^std::__[[:alnum:]]+::queue<.+>(( )?&)?$"),
+ stl_synth_flags, true);
+ AddCXXSynthetic(cpp_category_sp, LibcxxTupleFrontEndCreator,
+ "libc++ std::tuple synthetic children",
+ ConstString("^std::__[[:alnum:]]+::tuple<.*>(( )?&)?$"),
+ stl_synth_flags, true);
+ AddCXXSynthetic(cpp_category_sp, LibcxxOptionalFrontEndCreator,
+ "libc++ std::optional synthetic children",
+ ConstString("^std::__[[:alnum:]]+::optional<.+>(( )?&)?$"),
+ stl_synth_flags, true);
+ AddCXXSynthetic(cpp_category_sp, LibcxxVariantFrontEndCreator,
+ "libc++ std::variant synthetic children",
+ ConstString("^std::__[[:alnum:]]+::variant<.+>(( )?&)?$"),
+ stl_synth_flags, true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxAtomicSyntheticFrontEndCreator,
+ "libc++ std::atomic synthetic children",
+ ConstString("^std::__[[:alnum:]]+::atomic<.+>$"), stl_synth_flags, true);
+
+ cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(
+ RegularExpressionSP(new RegularExpression(
+ llvm::StringRef("^(std::__[[:alnum:]]+::)deque<.+>(( )?&)?$"))),
+ SyntheticChildrenSP(new ScriptedSyntheticChildren(
+ stl_synth_flags,
+ "lldb.formatters.cpp.libcxx.stddeque_SynthProvider")));
+
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator,
+ "shared_ptr synthetic children",
+ ConstString("^(std::__[[:alnum:]]+::)shared_ptr<.+>(( )?&)?$"),
+ stl_synth_flags, true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator,
+ "weak_ptr synthetic children",
+ ConstString("^(std::__[[:alnum:]]+::)weak_ptr<.+>(( )?&)?$"),
+ stl_synth_flags, true);
+
+ AddCXXSummary(
+ cpp_category_sp, lldb_private::formatters::LibcxxFunctionSummaryProvider,
+ "libc++ std::function summary provider",
+ ConstString("^std::__[[:alnum:]]+::function<.+>$"), stl_summary_flags,
+ true);
+
+ stl_summary_flags.SetDontShowChildren(false);
+ stl_summary_flags.SetSkipPointers(false);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxContainerSummaryProvider,
+ "libc++ std::bitset summary provider",
+ ConstString("^std::__[[:alnum:]]+::bitset<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxContainerSummaryProvider,
+ "libc++ std::vector summary provider",
+ ConstString("^std::__[[:alnum:]]+::vector<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxContainerSummaryProvider,
+ "libc++ std::list summary provider",
+ ConstString("^std::__[[:alnum:]]+::forward_list<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxContainerSummaryProvider,
+ "libc++ std::list summary provider",
+ ConstString("^std::__[[:alnum:]]+::list<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxContainerSummaryProvider,
+ "libc++ std::map summary provider",
+ ConstString("^std::__[[:alnum:]]+::map<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxContainerSummaryProvider,
+ "libc++ std::deque summary provider",
+ ConstString("^std::__[[:alnum:]]+::deque<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxContainerSummaryProvider,
+ "libc++ std::queue summary provider",
+ ConstString("^std::__[[:alnum:]]+::queue<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxContainerSummaryProvider,
+ "libc++ std::set summary provider",
+ ConstString("^std::__[[:alnum:]]+::set<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxContainerSummaryProvider,
+ "libc++ std::multiset summary provider",
+ ConstString("^std::__[[:alnum:]]+::multiset<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxContainerSummaryProvider,
+ "libc++ std::multimap summary provider",
+ ConstString("^std::__[[:alnum:]]+::multimap<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(
+ cpp_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider,
+ "libc++ std::unordered containers summary provider",
+ ConstString("^(std::__[[:alnum:]]+::)unordered_(multi)?(map|set)<.+> >$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp, LibcxxContainerSummaryProvider,
+ "libc++ std::tuple summary provider",
+ ConstString("^std::__[[:alnum:]]+::tuple<.*>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(
+ cpp_category_sp, lldb_private::formatters::LibCxxAtomicSummaryProvider,
+ "libc++ std::atomic summary provider",
+ ConstString("^std::__[[:alnum:]]+::atomic<.+>$"), stl_summary_flags,
+ true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxOptionalSummaryProvider,
+ "libc++ std::optional summary provider",
+ ConstString("^std::__[[:alnum:]]+::optional<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxVariantSummaryProvider,
+ "libc++ std::variant summary provider",
+ ConstString("^std::__[[:alnum:]]+::variant<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+
+ stl_summary_flags.SetSkipPointers(true);
+
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxSmartPointerSummaryProvider,
+ "libc++ std::shared_ptr summary provider",
+ ConstString("^std::__[[:alnum:]]+::shared_ptr<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibcxxSmartPointerSummaryProvider,
+ "libc++ std::weak_ptr summary provider",
+ ConstString("^std::__[[:alnum:]]+::weak_ptr<.+>(( )?&)?$"),
+ stl_summary_flags, true);
+
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator,
+ "std::vector iterator synthetic children",
+ ConstString("^std::__[[:alnum:]]+::__wrap_iter<.+>$"), stl_synth_flags,
+ true);
+
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator,
+ "std::map iterator synthetic children",
+ ConstString("^std::__[[:alnum:]]+::__map_iterator<.+>$"), stl_synth_flags,
+ true);
+}
+
+static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
+ if (!cpp_category_sp)
+ return;
+
+ TypeSummaryImpl::Flags stl_summary_flags;
+ stl_summary_flags.SetCascades(true)
+ .SetSkipPointers(false)
+ .SetSkipReferences(false)
+ .SetDontShowChildren(true)
+ .SetDontShowValue(true)
+ .SetShowMembersOneLiner(false)
+ .SetHideItemNames(false);
+
+ lldb::TypeSummaryImplSP std_string_summary_sp(
+ new StringSummaryFormat(stl_summary_flags, "${var._M_dataplus._M_p}"));
+
+ lldb::TypeSummaryImplSP cxx11_string_summary_sp(new CXXFunctionSummaryFormat(
+ stl_summary_flags, LibStdcppStringSummaryProvider,
+ "libstdc++ c++11 std::string summary provider"));
+ lldb::TypeSummaryImplSP cxx11_wstring_summary_sp(new CXXFunctionSummaryFormat(
+ stl_summary_flags, LibStdcppWStringSummaryProvider,
+ "libstdc++ c++11 std::wstring summary provider"));
+
+ cpp_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::string"),
+ std_string_summary_sp);
+ cpp_category_sp->GetTypeSummariesContainer()->Add(
+ ConstString("std::basic_string<char>"), std_string_summary_sp);
+ cpp_category_sp->GetTypeSummariesContainer()->Add(
+ ConstString("std::basic_string<char,std::char_traits<char>,std::"
+ "allocator<char> >"),
+ std_string_summary_sp);
+ cpp_category_sp->GetTypeSummariesContainer()->Add(
+ ConstString("std::basic_string<char, std::char_traits<char>, "
+ "std::allocator<char> >"),
+ std_string_summary_sp);
+
+ cpp_category_sp->GetTypeSummariesContainer()->Add(
+ ConstString("std::__cxx11::string"), cxx11_string_summary_sp);
+ cpp_category_sp->GetTypeSummariesContainer()->Add(
+ ConstString("std::__cxx11::basic_string<char, std::char_traits<char>, "
+ "std::allocator<char> >"),
+ cxx11_string_summary_sp);
+
+ // making sure we force-pick the summary for printing wstring (_M_p is a
+ // wchar_t*)
+ lldb::TypeSummaryImplSP std_wstring_summary_sp(
+ new StringSummaryFormat(stl_summary_flags, "${var._M_dataplus._M_p%S}"));
+
+ cpp_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::wstring"),
+ std_wstring_summary_sp);
+ cpp_category_sp->GetTypeSummariesContainer()->Add(
+ ConstString("std::basic_string<wchar_t>"), std_wstring_summary_sp);
+ cpp_category_sp->GetTypeSummariesContainer()->Add(
+ ConstString("std::basic_string<wchar_t,std::char_traits<wchar_t>,std::"
+ "allocator<wchar_t> >"),
+ std_wstring_summary_sp);
+ cpp_category_sp->GetTypeSummariesContainer()->Add(
+ ConstString("std::basic_string<wchar_t, std::char_traits<wchar_t>, "
+ "std::allocator<wchar_t> >"),
+ std_wstring_summary_sp);
+
+ cpp_category_sp->GetTypeSummariesContainer()->Add(
+ ConstString("std::__cxx11::wstring"), cxx11_wstring_summary_sp);
+ cpp_category_sp->GetTypeSummariesContainer()->Add(
+ ConstString("std::__cxx11::basic_string<wchar_t, "
+ "std::char_traits<wchar_t>, std::allocator<wchar_t> >"),
+ cxx11_wstring_summary_sp);
+
+ SyntheticChildren::Flags stl_synth_flags;
+ stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(
+ false);
+
+ cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(
+ RegularExpressionSP(
+ new RegularExpression(llvm::StringRef("^std::vector<.+>(( )?&)?$"))),
+ SyntheticChildrenSP(new ScriptedSyntheticChildren(
+ stl_synth_flags,
+ "lldb.formatters.cpp.gnu_libstdcpp.StdVectorSynthProvider")));
+ cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(
+ RegularExpressionSP(
+ new RegularExpression(llvm::StringRef("^std::map<.+> >(( )?&)?$"))),
+ SyntheticChildrenSP(new ScriptedSyntheticChildren(
+ stl_synth_flags,
+ "lldb.formatters.cpp.gnu_libstdcpp.StdMapSynthProvider")));
+ cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(
+ RegularExpressionSP(new RegularExpression(
+ llvm::StringRef("^std::(__cxx11::)?list<.+>(( )?&)?$"))),
+ SyntheticChildrenSP(new ScriptedSyntheticChildren(
+ stl_synth_flags,
+ "lldb.formatters.cpp.gnu_libstdcpp.StdListSynthProvider")));
+ stl_summary_flags.SetDontShowChildren(false);
+ stl_summary_flags.SetSkipPointers(true);
+ cpp_category_sp->GetRegexTypeSummariesContainer()->Add(
+ RegularExpressionSP(
+ new RegularExpression(llvm::StringRef("^std::vector<.+>(( )?&)?$"))),
+ TypeSummaryImplSP(
+ new StringSummaryFormat(stl_summary_flags, "size=${svar%#}")));
+ cpp_category_sp->GetRegexTypeSummariesContainer()->Add(
+ RegularExpressionSP(
+ new RegularExpression(llvm::StringRef("^std::map<.+> >(( )?&)?$"))),
+ TypeSummaryImplSP(
+ new StringSummaryFormat(stl_summary_flags, "size=${svar%#}")));
+ cpp_category_sp->GetRegexTypeSummariesContainer()->Add(
+ RegularExpressionSP(new RegularExpression(
+ llvm::StringRef("^std::(__cxx11::)?list<.+>(( )?&)?$"))),
+ TypeSummaryImplSP(
+ new StringSummaryFormat(stl_summary_flags, "size=${svar%#}")));
+
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibStdcppVectorIteratorSyntheticFrontEndCreator,
+ "std::vector iterator synthetic children",
+ ConstString("^__gnu_cxx::__normal_iterator<.+>$"), stl_synth_flags, true);
+
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEndCreator,
+ "std::map iterator synthetic children",
+ ConstString("^std::_Rb_tree_iterator<.+>$"), stl_synth_flags, true);
+
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibStdcppUniquePtrSyntheticFrontEndCreator,
+ "std::unique_ptr synthetic children",
+ ConstString("^std::unique_ptr<.+>(( )?&)?$"), stl_synth_flags, true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator,
+ "std::shared_ptr synthetic children",
+ ConstString("^std::shared_ptr<.+>(( )?&)?$"), stl_synth_flags, true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator,
+ "std::weak_ptr synthetic children",
+ ConstString("^std::weak_ptr<.+>(( )?&)?$"), stl_synth_flags, true);
+ AddCXXSynthetic(
+ cpp_category_sp,
+ lldb_private::formatters::LibStdcppTupleSyntheticFrontEndCreator,
+ "std::tuple synthetic children", ConstString("^std::tuple<.+>(( )?&)?$"),
+ stl_synth_flags, true);
+
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibStdcppUniquePointerSummaryProvider,
+ "libstdc++ std::unique_ptr summary provider",
+ ConstString("^std::unique_ptr<.+>(( )?&)?$"), stl_summary_flags,
+ true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibStdcppSmartPointerSummaryProvider,
+ "libstdc++ std::shared_ptr summary provider",
+ ConstString("^std::shared_ptr<.+>(( )?&)?$"), stl_summary_flags,
+ true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::LibStdcppSmartPointerSummaryProvider,
+ "libstdc++ std::weak_ptr summary provider",
+ ConstString("^std::weak_ptr<.+>(( )?&)?$"), stl_summary_flags,
+ true);
+}
+
+static void LoadSystemFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
+ if (!cpp_category_sp)
+ return;
+
+ TypeSummaryImpl::Flags string_flags;
+ string_flags.SetCascades(true)
+ .SetSkipPointers(true)
+ .SetSkipReferences(false)
+ .SetDontShowChildren(true)
+ .SetDontShowValue(false)
+ .SetShowMembersOneLiner(false)
+ .SetHideItemNames(false);
+
+ TypeSummaryImpl::Flags string_array_flags;
+ string_array_flags.SetCascades(true)
+ .SetSkipPointers(true)
+ .SetSkipReferences(false)
+ .SetDontShowChildren(true)
+ .SetDontShowValue(true)
+ .SetShowMembersOneLiner(false)
+ .SetHideItemNames(false);
+
+ // FIXME because of a bug in the FormattersContainer we need to add a summary
+ // for both X* and const X* (<rdar://problem/12717717>)
+ AddCXXSummary(
+ cpp_category_sp, lldb_private::formatters::Char16StringSummaryProvider,
+ "char16_t * summary provider", ConstString("char16_t *"), string_flags);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::Char16StringSummaryProvider,
+ "char16_t [] summary provider",
+ ConstString("char16_t \\[[0-9]+\\]"), string_array_flags, true);
+
+ AddCXXSummary(
+ cpp_category_sp, lldb_private::formatters::Char32StringSummaryProvider,
+ "char32_t * summary provider", ConstString("char32_t *"), string_flags);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::Char32StringSummaryProvider,
+ "char32_t [] summary provider",
+ ConstString("char32_t \\[[0-9]+\\]"), string_array_flags, true);
+
+ AddCXXSummary(
+ cpp_category_sp, lldb_private::formatters::WCharStringSummaryProvider,
+ "wchar_t * summary provider", ConstString("wchar_t *"), string_flags);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::WCharStringSummaryProvider,
+ "wchar_t * summary provider",
+ ConstString("wchar_t \\[[0-9]+\\]"), string_array_flags, true);
+
+ AddCXXSummary(
+ cpp_category_sp, lldb_private::formatters::Char16StringSummaryProvider,
+ "unichar * summary provider", ConstString("unichar *"), string_flags);
+
+ TypeSummaryImpl::Flags widechar_flags;
+ widechar_flags.SetDontShowValue(true)
+ .SetSkipPointers(true)
+ .SetSkipReferences(false)
+ .SetCascades(true)
+ .SetDontShowChildren(true)
+ .SetHideItemNames(true)
+ .SetShowMembersOneLiner(false);
+
+ AddCXXSummary(
+ cpp_category_sp, lldb_private::formatters::Char16SummaryProvider,
+ "char16_t summary provider", ConstString("char16_t"), widechar_flags);
+ AddCXXSummary(
+ cpp_category_sp, lldb_private::formatters::Char32SummaryProvider,
+ "char32_t summary provider", ConstString("char32_t"), widechar_flags);
+ AddCXXSummary(cpp_category_sp, lldb_private::formatters::WCharSummaryProvider,
+ "wchar_t summary provider", ConstString("wchar_t"),
+ widechar_flags);
+
+ AddCXXSummary(
+ cpp_category_sp, lldb_private::formatters::Char16SummaryProvider,
+ "unichar summary provider", ConstString("unichar"), widechar_flags);
+}
+
+std::unique_ptr<Language::TypeScavenger> CPlusPlusLanguage::GetTypeScavenger() {
+ class CPlusPlusTypeScavenger : public Language::ImageListTypeScavenger {
+ public:
+ CompilerType AdjustForInclusion(CompilerType &candidate) override {
+ LanguageType lang_type(candidate.GetMinimumLanguage());
+ if (!Language::LanguageIsC(lang_type) &&
+ !Language::LanguageIsCPlusPlus(lang_type))
+ return CompilerType();
+ if (candidate.IsTypedefType())
+ return candidate.GetTypedefedType();
+ return candidate;
+ }
+ };
+
+ return std::unique_ptr<TypeScavenger>(new CPlusPlusTypeScavenger());
+}
+
+lldb::TypeCategoryImplSP CPlusPlusLanguage::GetFormatters() {
+ static llvm::once_flag g_initialize;
+ static TypeCategoryImplSP g_category;
+
+ llvm::call_once(g_initialize, [this]() -> void {
+ DataVisualization::Categories::GetCategory(GetPluginName(), g_category);
+ if (g_category) {
+ LoadLibStdcppFormatters(g_category);
+ LoadLibCxxFormatters(g_category);
+ LoadSystemFormatters(g_category);
+ }
+ });
+ return g_category;
+}
+
+HardcodedFormatters::HardcodedSummaryFinder
+CPlusPlusLanguage::GetHardcodedSummaries() {
+ static llvm::once_flag g_initialize;
+ static ConstString g_vectortypes("VectorTypes");
+ static HardcodedFormatters::HardcodedSummaryFinder g_formatters;
+
+ llvm::call_once(g_initialize, []() -> void {
+ g_formatters.push_back(
+ [](lldb_private::ValueObject &valobj, lldb::DynamicValueType,
+ FormatManager &) -> TypeSummaryImpl::SharedPointer {
+ static CXXFunctionSummaryFormat::SharedPointer formatter_sp(
+ new CXXFunctionSummaryFormat(
+ TypeSummaryImpl::Flags(),
+ lldb_private::formatters::CXXFunctionPointerSummaryProvider,
+ "Function pointer summary provider"));
+ if (valobj.GetCompilerType().IsFunctionPointerType()) {
+ return formatter_sp;
+ }
+ return nullptr;
+ });
+ g_formatters.push_back(
+ [](lldb_private::ValueObject &valobj, lldb::DynamicValueType,
+ FormatManager &fmt_mgr) -> TypeSummaryImpl::SharedPointer {
+ static CXXFunctionSummaryFormat::SharedPointer formatter_sp(
+ new CXXFunctionSummaryFormat(
+ TypeSummaryImpl::Flags()
+ .SetCascades(true)
+ .SetDontShowChildren(true)
+ .SetHideItemNames(true)
+ .SetShowMembersOneLiner(true)
+ .SetSkipPointers(true)
+ .SetSkipReferences(false),
+ lldb_private::formatters::VectorTypeSummaryProvider,
+ "vector_type pointer summary provider"));
+ if (valobj.GetCompilerType().IsVectorType(nullptr, nullptr)) {
+ if (fmt_mgr.GetCategory(g_vectortypes)->IsEnabled())
+ return formatter_sp;
+ }
+ return nullptr;
+ });
+ g_formatters.push_back(
+ [](lldb_private::ValueObject &valobj, lldb::DynamicValueType,
+ FormatManager &fmt_mgr) -> TypeSummaryImpl::SharedPointer {
+ static CXXFunctionSummaryFormat::SharedPointer formatter_sp(
+ new CXXFunctionSummaryFormat(
+ TypeSummaryImpl::Flags()
+ .SetCascades(true)
+ .SetDontShowChildren(true)
+ .SetHideItemNames(true)
+ .SetShowMembersOneLiner(true)
+ .SetSkipPointers(true)
+ .SetSkipReferences(false),
+ lldb_private::formatters::BlockPointerSummaryProvider,
+ "block pointer summary provider"));
+ if (valobj.GetCompilerType().IsBlockPointerType(nullptr)) {
+ return formatter_sp;
+ }
+ return nullptr;
+ });
+ });
+
+ return g_formatters;
+}
+
+HardcodedFormatters::HardcodedSyntheticFinder
+CPlusPlusLanguage::GetHardcodedSynthetics() {
+ static llvm::once_flag g_initialize;
+ static ConstString g_vectortypes("VectorTypes");
+ static HardcodedFormatters::HardcodedSyntheticFinder g_formatters;
+
+ llvm::call_once(g_initialize, []() -> void {
+ g_formatters.push_back([](lldb_private::ValueObject &valobj,
+ lldb::DynamicValueType,
+ FormatManager &
+ fmt_mgr) -> SyntheticChildren::SharedPointer {
+ static CXXSyntheticChildren::SharedPointer formatter_sp(
+ new CXXSyntheticChildren(
+ SyntheticChildren::Flags()
+ .SetCascades(true)
+ .SetSkipPointers(true)
+ .SetSkipReferences(true)
+ .SetNonCacheable(true),
+ "vector_type synthetic children",
+ lldb_private::formatters::VectorTypeSyntheticFrontEndCreator));
+ if (valobj.GetCompilerType().IsVectorType(nullptr, nullptr)) {
+ if (fmt_mgr.GetCategory(g_vectortypes)->IsEnabled())
+ return formatter_sp;
+ }
+ return nullptr;
+ });
+ g_formatters.push_back([](lldb_private::ValueObject &valobj,
+ lldb::DynamicValueType,
+ FormatManager &
+ fmt_mgr) -> SyntheticChildren::SharedPointer {
+ static CXXSyntheticChildren::SharedPointer formatter_sp(
+ new CXXSyntheticChildren(
+ SyntheticChildren::Flags()
+ .SetCascades(true)
+ .SetSkipPointers(true)
+ .SetSkipReferences(true)
+ .SetNonCacheable(true),
+ "block pointer synthetic children",
+ lldb_private::formatters::BlockPointerSyntheticFrontEndCreator));
+ if (valobj.GetCompilerType().IsBlockPointerType(nullptr)) {
+ return formatter_sp;
+ }
+ return nullptr;
+ });
+
+ });
+
+ return g_formatters;
+}
+
+bool CPlusPlusLanguage::IsSourceFile(llvm::StringRef file_path) const {
+ const auto suffixes = {".cpp", ".cxx", ".c++", ".cc", ".c",
+ ".h", ".hh", ".hpp", ".hxx", ".h++"};
+ for (auto suffix : suffixes) {
+ if (file_path.endswith_lower(suffix))
+ return true;
+ }
+
+ // Check if we're in a STL path (where the files usually have no extension
+ // that we could check for.
+ return file_path.contains("/usr/include/c++/");
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
new file mode 100644
index 000000000000..d30e56080732
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
@@ -0,0 +1,136 @@
+//===-- CPlusPlusLanguage.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 liblldb_CPlusPlusLanguage_h_
+#define liblldb_CPlusPlusLanguage_h_
+
+#include <set>
+#include <vector>
+
+#include "llvm/ADT/StringRef.h"
+
+#include "Plugins/Language/ClangCommon/ClangHighlighter.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class CPlusPlusLanguage : public Language {
+ ClangHighlighter m_highlighter;
+
+public:
+ class MethodName {
+ public:
+ MethodName()
+ : m_full(), m_basename(), m_context(), m_arguments(), m_qualifiers(),
+ m_parsed(false), m_parse_error(false) {}
+
+ MethodName(ConstString s)
+ : m_full(s), m_basename(), m_context(), m_arguments(), m_qualifiers(),
+ m_parsed(false), m_parse_error(false) {}
+
+ void Clear();
+
+ bool IsValid() {
+ if (!m_parsed)
+ Parse();
+ if (m_parse_error)
+ return false;
+ return (bool)m_full;
+ }
+
+ ConstString GetFullName() const { return m_full; }
+
+ std::string GetScopeQualifiedName();
+
+ llvm::StringRef GetBasename();
+
+ llvm::StringRef GetContext();
+
+ llvm::StringRef GetArguments();
+
+ llvm::StringRef GetQualifiers();
+
+ protected:
+ void Parse();
+ bool TrySimplifiedParse();
+
+ ConstString m_full; // Full name:
+ // "lldb::SBTarget::GetBreakpointAtIndex(unsigned int)
+ // const"
+ llvm::StringRef m_basename; // Basename: "GetBreakpointAtIndex"
+ llvm::StringRef m_context; // Decl context: "lldb::SBTarget"
+ llvm::StringRef m_arguments; // Arguments: "(unsigned int)"
+ llvm::StringRef m_qualifiers; // Qualifiers: "const"
+ bool m_parsed;
+ bool m_parse_error;
+ };
+
+ CPlusPlusLanguage() = default;
+
+ ~CPlusPlusLanguage() override = default;
+
+ lldb::LanguageType GetLanguageType() const override {
+ return lldb::eLanguageTypeC_plus_plus;
+ }
+
+ std::unique_ptr<TypeScavenger> GetTypeScavenger() override;
+ lldb::TypeCategoryImplSP GetFormatters() override;
+
+ HardcodedFormatters::HardcodedSummaryFinder GetHardcodedSummaries() override;
+
+ HardcodedFormatters::HardcodedSyntheticFinder
+ GetHardcodedSynthetics() override;
+
+ bool IsSourceFile(llvm::StringRef file_path) const override;
+
+ const Highlighter *GetHighlighter() const override { return &m_highlighter; }
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::Language *CreateInstance(lldb::LanguageType language);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static bool IsCPPMangledName(const char *name);
+
+ // Extract C++ context and identifier from a string using heuristic matching
+ // (as opposed to
+ // CPlusPlusLanguage::MethodName which has to have a fully qualified C++ name
+ // with parens and arguments.
+ // If the name is a lone C identifier (e.g. C) or a qualified C identifier
+ // (e.g. A::B::C) it will return true,
+ // and identifier will be the identifier (C and C respectively) and the
+ // context will be "" and "A::B" respectively.
+ // If the name fails the heuristic matching for a qualified or unqualified
+ // C/C++ identifier, then it will return false
+ // and identifier and context will be unchanged.
+
+ static bool ExtractContextAndIdentifier(const char *name,
+ llvm::StringRef &context,
+ llvm::StringRef &identifier);
+
+ // Given a mangled function name, calculates some alternative manglings since
+ // the compiler mangling may not line up with the symbol we are expecting
+ static uint32_t
+ FindAlternateFunctionManglings(const ConstString mangled,
+ std::set<ConstString> &candidates);
+
+ // PluginInterface protocol
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CPlusPlusLanguage_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp
new file mode 100644
index 000000000000..932db17b964a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp
@@ -0,0 +1,660 @@
+//===-- CPlusPlusNameParser.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 "CPlusPlusNameParser.h"
+
+#include "clang/Basic/IdentifierTable.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Threading.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using llvm::Optional;
+using llvm::None;
+using ParsedFunction = lldb_private::CPlusPlusNameParser::ParsedFunction;
+using ParsedName = lldb_private::CPlusPlusNameParser::ParsedName;
+namespace tok = clang::tok;
+
+Optional<ParsedFunction> CPlusPlusNameParser::ParseAsFunctionDefinition() {
+ m_next_token_index = 0;
+ Optional<ParsedFunction> result(None);
+
+ // Try to parse the name as function without a return type specified e.g.
+ // main(int, char*[])
+ {
+ Bookmark start_position = SetBookmark();
+ result = ParseFunctionImpl(false);
+ if (result && !HasMoreTokens())
+ return result;
+ }
+
+ // Try to parse the name as function with function pointer return type e.g.
+ // void (*get_func(const char*))()
+ result = ParseFuncPtr(true);
+ if (result)
+ return result;
+
+ // Finally try to parse the name as a function with non-function return type
+ // e.g. int main(int, char*[])
+ result = ParseFunctionImpl(true);
+ if (HasMoreTokens())
+ return None;
+ return result;
+}
+
+Optional<ParsedName> CPlusPlusNameParser::ParseAsFullName() {
+ m_next_token_index = 0;
+ Optional<ParsedNameRanges> name_ranges = ParseFullNameImpl();
+ if (!name_ranges)
+ return None;
+ if (HasMoreTokens())
+ return None;
+ ParsedName result;
+ result.basename = GetTextForRange(name_ranges.getValue().basename_range);
+ result.context = GetTextForRange(name_ranges.getValue().context_range);
+ return result;
+}
+
+bool CPlusPlusNameParser::HasMoreTokens() {
+ return m_next_token_index < m_tokens.size();
+}
+
+void CPlusPlusNameParser::Advance() { ++m_next_token_index; }
+
+void CPlusPlusNameParser::TakeBack() { --m_next_token_index; }
+
+bool CPlusPlusNameParser::ConsumeToken(tok::TokenKind kind) {
+ if (!HasMoreTokens())
+ return false;
+
+ if (!Peek().is(kind))
+ return false;
+
+ Advance();
+ return true;
+}
+
+template <typename... Ts> bool CPlusPlusNameParser::ConsumeToken(Ts... kinds) {
+ if (!HasMoreTokens())
+ return false;
+
+ if (!Peek().isOneOf(kinds...))
+ return false;
+
+ Advance();
+ return true;
+}
+
+CPlusPlusNameParser::Bookmark CPlusPlusNameParser::SetBookmark() {
+ return Bookmark(m_next_token_index);
+}
+
+size_t CPlusPlusNameParser::GetCurrentPosition() { return m_next_token_index; }
+
+clang::Token &CPlusPlusNameParser::Peek() {
+ assert(HasMoreTokens());
+ return m_tokens[m_next_token_index];
+}
+
+Optional<ParsedFunction>
+CPlusPlusNameParser::ParseFunctionImpl(bool expect_return_type) {
+ Bookmark start_position = SetBookmark();
+ if (expect_return_type) {
+ // Consume return type if it's expected.
+ if (!ConsumeTypename())
+ return None;
+ }
+
+ auto maybe_name = ParseFullNameImpl();
+ if (!maybe_name) {
+ return None;
+ }
+
+ size_t argument_start = GetCurrentPosition();
+ if (!ConsumeArguments()) {
+ return None;
+ }
+
+ size_t qualifiers_start = GetCurrentPosition();
+ SkipFunctionQualifiers();
+ size_t end_position = GetCurrentPosition();
+
+ ParsedFunction result;
+ result.name.basename = GetTextForRange(maybe_name.getValue().basename_range);
+ result.name.context = GetTextForRange(maybe_name.getValue().context_range);
+ result.arguments = GetTextForRange(Range(argument_start, qualifiers_start));
+ result.qualifiers = GetTextForRange(Range(qualifiers_start, end_position));
+ start_position.Remove();
+ return result;
+}
+
+Optional<ParsedFunction>
+CPlusPlusNameParser::ParseFuncPtr(bool expect_return_type) {
+ Bookmark start_position = SetBookmark();
+ if (expect_return_type) {
+ // Consume return type.
+ if (!ConsumeTypename())
+ return None;
+ }
+
+ if (!ConsumeToken(tok::l_paren))
+ return None;
+ if (!ConsumePtrsAndRefs())
+ return None;
+
+ {
+ Bookmark before_inner_function_pos = SetBookmark();
+ auto maybe_inner_function_name = ParseFunctionImpl(false);
+ if (maybe_inner_function_name)
+ if (ConsumeToken(tok::r_paren))
+ if (ConsumeArguments()) {
+ SkipFunctionQualifiers();
+ start_position.Remove();
+ before_inner_function_pos.Remove();
+ return maybe_inner_function_name;
+ }
+ }
+
+ auto maybe_inner_function_ptr_name = ParseFuncPtr(false);
+ if (maybe_inner_function_ptr_name)
+ if (ConsumeToken(tok::r_paren))
+ if (ConsumeArguments()) {
+ SkipFunctionQualifiers();
+ start_position.Remove();
+ return maybe_inner_function_ptr_name;
+ }
+ return None;
+}
+
+bool CPlusPlusNameParser::ConsumeArguments() {
+ return ConsumeBrackets(tok::l_paren, tok::r_paren);
+}
+
+bool CPlusPlusNameParser::ConsumeTemplateArgs() {
+ Bookmark start_position = SetBookmark();
+ if (!HasMoreTokens() || Peek().getKind() != tok::less)
+ return false;
+ Advance();
+
+ // Consuming template arguments is a bit trickier than consuming function
+ // arguments, because '<' '>' brackets are not always trivially balanced. In
+ // some rare cases tokens '<' and '>' can appear inside template arguments as
+ // arithmetic or shift operators not as template brackets. Examples:
+ // std::enable_if<(10u)<(64), bool>
+ // f<A<operator<(X,Y)::Subclass>>
+ // Good thing that compiler makes sure that really ambiguous cases of '>'
+ // usage should be enclosed within '()' brackets.
+ int template_counter = 1;
+ bool can_open_template = false;
+ while (HasMoreTokens() && template_counter > 0) {
+ tok::TokenKind kind = Peek().getKind();
+ switch (kind) {
+ case tok::greatergreater:
+ template_counter -= 2;
+ can_open_template = false;
+ Advance();
+ break;
+ case tok::greater:
+ --template_counter;
+ can_open_template = false;
+ Advance();
+ break;
+ case tok::less:
+ // '<' is an attempt to open a subteamplte
+ // check if parser is at the point where it's actually possible,
+ // otherwise it's just a part of an expression like 'sizeof(T)<(10)'. No
+ // need to do the same for '>' because compiler actually makes sure that
+ // '>' always surrounded by brackets to avoid ambiguity.
+ if (can_open_template)
+ ++template_counter;
+ can_open_template = false;
+ Advance();
+ break;
+ case tok::kw_operator: // C++ operator overloading.
+ if (!ConsumeOperator())
+ return false;
+ can_open_template = true;
+ break;
+ case tok::raw_identifier:
+ can_open_template = true;
+ Advance();
+ break;
+ case tok::l_square:
+ if (!ConsumeBrackets(tok::l_square, tok::r_square))
+ return false;
+ can_open_template = false;
+ break;
+ case tok::l_paren:
+ if (!ConsumeArguments())
+ return false;
+ can_open_template = false;
+ break;
+ default:
+ can_open_template = false;
+ Advance();
+ break;
+ }
+ }
+
+ if (template_counter != 0) {
+ return false;
+ }
+ start_position.Remove();
+ return true;
+}
+
+bool CPlusPlusNameParser::ConsumeAnonymousNamespace() {
+ Bookmark start_position = SetBookmark();
+ if (!ConsumeToken(tok::l_paren)) {
+ return false;
+ }
+ constexpr llvm::StringLiteral g_anonymous("anonymous");
+ if (HasMoreTokens() && Peek().is(tok::raw_identifier) &&
+ Peek().getRawIdentifier() == g_anonymous) {
+ Advance();
+ } else {
+ return false;
+ }
+
+ if (!ConsumeToken(tok::kw_namespace)) {
+ return false;
+ }
+
+ if (!ConsumeToken(tok::r_paren)) {
+ return false;
+ }
+ start_position.Remove();
+ return true;
+}
+
+bool CPlusPlusNameParser::ConsumeLambda() {
+ Bookmark start_position = SetBookmark();
+ if (!ConsumeToken(tok::l_brace)) {
+ return false;
+ }
+ constexpr llvm::StringLiteral g_lambda("lambda");
+ if (HasMoreTokens() && Peek().is(tok::raw_identifier) &&
+ Peek().getRawIdentifier() == g_lambda) {
+ // Put the matched brace back so we can use ConsumeBrackets
+ TakeBack();
+ } else {
+ return false;
+ }
+
+ if (!ConsumeBrackets(tok::l_brace, tok::r_brace)) {
+ return false;
+ }
+
+ start_position.Remove();
+ return true;
+}
+
+bool CPlusPlusNameParser::ConsumeBrackets(tok::TokenKind left,
+ tok::TokenKind right) {
+ Bookmark start_position = SetBookmark();
+ if (!HasMoreTokens() || Peek().getKind() != left)
+ return false;
+ Advance();
+
+ int counter = 1;
+ while (HasMoreTokens() && counter > 0) {
+ tok::TokenKind kind = Peek().getKind();
+ if (kind == right)
+ --counter;
+ else if (kind == left)
+ ++counter;
+ Advance();
+ }
+
+ assert(counter >= 0);
+ if (counter > 0) {
+ return false;
+ }
+ start_position.Remove();
+ return true;
+}
+
+bool CPlusPlusNameParser::ConsumeOperator() {
+ Bookmark start_position = SetBookmark();
+ if (!ConsumeToken(tok::kw_operator))
+ return false;
+
+ if (!HasMoreTokens()) {
+ return false;
+ }
+
+ const auto &token = Peek();
+ switch (token.getKind()) {
+ case tok::kw_new:
+ case tok::kw_delete:
+ // This is 'new' or 'delete' operators.
+ Advance();
+ // Check for array new/delete.
+ if (HasMoreTokens() && Peek().is(tok::l_square)) {
+ // Consume the '[' and ']'.
+ if (!ConsumeBrackets(tok::l_square, tok::r_square))
+ return false;
+ }
+ break;
+
+#define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \
+ case tok::Token: \
+ Advance(); \
+ break;
+#define OVERLOADED_OPERATOR_MULTI(Name, Spelling, Unary, Binary, MemberOnly)
+#include "clang/Basic/OperatorKinds.def"
+#undef OVERLOADED_OPERATOR
+#undef OVERLOADED_OPERATOR_MULTI
+
+ case tok::l_paren:
+ // Call operator consume '(' ... ')'.
+ if (ConsumeBrackets(tok::l_paren, tok::r_paren))
+ break;
+ return false;
+
+ case tok::l_square:
+ // This is a [] operator.
+ // Consume the '[' and ']'.
+ if (ConsumeBrackets(tok::l_square, tok::r_square))
+ break;
+ return false;
+
+ default:
+ // This might be a cast operator.
+ if (ConsumeTypename())
+ break;
+ return false;
+ }
+ start_position.Remove();
+ return true;
+}
+
+void CPlusPlusNameParser::SkipTypeQualifiers() {
+ while (ConsumeToken(tok::kw_const, tok::kw_volatile))
+ ;
+}
+
+void CPlusPlusNameParser::SkipFunctionQualifiers() {
+ while (ConsumeToken(tok::kw_const, tok::kw_volatile, tok::amp, tok::ampamp))
+ ;
+}
+
+bool CPlusPlusNameParser::ConsumeBuiltinType() {
+ bool result = false;
+ bool continue_parsing = true;
+ // Built-in types can be made of a few keywords like 'unsigned long long
+ // int'. This function consumes all built-in type keywords without checking
+ // if they make sense like 'unsigned char void'.
+ while (continue_parsing && HasMoreTokens()) {
+ switch (Peek().getKind()) {
+ case tok::kw_short:
+ case tok::kw_long:
+ case tok::kw___int64:
+ case tok::kw___int128:
+ case tok::kw_signed:
+ case tok::kw_unsigned:
+ case tok::kw_void:
+ case tok::kw_char:
+ case tok::kw_int:
+ case tok::kw_half:
+ case tok::kw_float:
+ case tok::kw_double:
+ case tok::kw___float128:
+ case tok::kw_wchar_t:
+ case tok::kw_bool:
+ case tok::kw_char16_t:
+ case tok::kw_char32_t:
+ result = true;
+ Advance();
+ break;
+ default:
+ continue_parsing = false;
+ break;
+ }
+ }
+ return result;
+}
+
+void CPlusPlusNameParser::SkipPtrsAndRefs() {
+ // Ignoring result.
+ ConsumePtrsAndRefs();
+}
+
+bool CPlusPlusNameParser::ConsumePtrsAndRefs() {
+ bool found = false;
+ SkipTypeQualifiers();
+ while (ConsumeToken(tok::star, tok::amp, tok::ampamp, tok::kw_const,
+ tok::kw_volatile)) {
+ found = true;
+ SkipTypeQualifiers();
+ }
+ return found;
+}
+
+bool CPlusPlusNameParser::ConsumeDecltype() {
+ Bookmark start_position = SetBookmark();
+ if (!ConsumeToken(tok::kw_decltype))
+ return false;
+
+ if (!ConsumeArguments())
+ return false;
+
+ start_position.Remove();
+ return true;
+}
+
+bool CPlusPlusNameParser::ConsumeTypename() {
+ Bookmark start_position = SetBookmark();
+ SkipTypeQualifiers();
+ if (!ConsumeBuiltinType() && !ConsumeDecltype()) {
+ if (!ParseFullNameImpl())
+ return false;
+ }
+ SkipPtrsAndRefs();
+ start_position.Remove();
+ return true;
+}
+
+Optional<CPlusPlusNameParser::ParsedNameRanges>
+CPlusPlusNameParser::ParseFullNameImpl() {
+ // Name parsing state machine.
+ enum class State {
+ Beginning, // start of the name
+ AfterTwoColons, // right after ::
+ AfterIdentifier, // right after alphanumerical identifier ([a-z0-9_]+)
+ AfterTemplate, // right after template brackets (<something>)
+ AfterOperator, // right after name of C++ operator
+ };
+
+ Bookmark start_position = SetBookmark();
+ State state = State::Beginning;
+ bool continue_parsing = true;
+ Optional<size_t> last_coloncolon_position = None;
+
+ while (continue_parsing && HasMoreTokens()) {
+ const auto &token = Peek();
+ switch (token.getKind()) {
+ case tok::raw_identifier: // Just a name.
+ if (state != State::Beginning && state != State::AfterTwoColons) {
+ continue_parsing = false;
+ break;
+ }
+ Advance();
+ state = State::AfterIdentifier;
+ break;
+ case tok::l_paren: {
+ if (state == State::Beginning || state == State::AfterTwoColons) {
+ // (anonymous namespace)
+ if (ConsumeAnonymousNamespace()) {
+ state = State::AfterIdentifier;
+ break;
+ }
+ }
+
+ // Type declared inside a function 'func()::Type'
+ if (state != State::AfterIdentifier && state != State::AfterTemplate &&
+ state != State::AfterOperator) {
+ continue_parsing = false;
+ break;
+ }
+ Bookmark l_paren_position = SetBookmark();
+ // Consume the '(' ... ') [const]'.
+ if (!ConsumeArguments()) {
+ continue_parsing = false;
+ break;
+ }
+ SkipFunctionQualifiers();
+
+ // Consume '::'
+ size_t coloncolon_position = GetCurrentPosition();
+ if (!ConsumeToken(tok::coloncolon)) {
+ continue_parsing = false;
+ break;
+ }
+ l_paren_position.Remove();
+ last_coloncolon_position = coloncolon_position;
+ state = State::AfterTwoColons;
+ break;
+ }
+ case tok::l_brace:
+ if (state == State::Beginning || state == State::AfterTwoColons) {
+ if (ConsumeLambda()) {
+ state = State::AfterIdentifier;
+ break;
+ }
+ }
+ continue_parsing = false;
+ break;
+ case tok::coloncolon: // Type nesting delimiter.
+ if (state != State::Beginning && state != State::AfterIdentifier &&
+ state != State::AfterTemplate) {
+ continue_parsing = false;
+ break;
+ }
+ last_coloncolon_position = GetCurrentPosition();
+ Advance();
+ state = State::AfterTwoColons;
+ break;
+ case tok::less: // Template brackets.
+ if (state != State::AfterIdentifier && state != State::AfterOperator) {
+ continue_parsing = false;
+ break;
+ }
+ if (!ConsumeTemplateArgs()) {
+ continue_parsing = false;
+ break;
+ }
+ state = State::AfterTemplate;
+ break;
+ case tok::kw_operator: // C++ operator overloading.
+ if (state != State::Beginning && state != State::AfterTwoColons) {
+ continue_parsing = false;
+ break;
+ }
+ if (!ConsumeOperator()) {
+ continue_parsing = false;
+ break;
+ }
+ state = State::AfterOperator;
+ break;
+ case tok::tilde: // Destructor.
+ if (state != State::Beginning && state != State::AfterTwoColons) {
+ continue_parsing = false;
+ break;
+ }
+ Advance();
+ if (ConsumeToken(tok::raw_identifier)) {
+ state = State::AfterIdentifier;
+ } else {
+ TakeBack();
+ continue_parsing = false;
+ }
+ break;
+ default:
+ continue_parsing = false;
+ break;
+ }
+ }
+
+ if (state == State::AfterIdentifier || state == State::AfterOperator ||
+ state == State::AfterTemplate) {
+ ParsedNameRanges result;
+ if (last_coloncolon_position) {
+ result.context_range = Range(start_position.GetSavedPosition(),
+ last_coloncolon_position.getValue());
+ result.basename_range =
+ Range(last_coloncolon_position.getValue() + 1, GetCurrentPosition());
+ } else {
+ result.basename_range =
+ Range(start_position.GetSavedPosition(), GetCurrentPosition());
+ }
+ start_position.Remove();
+ return result;
+ } else {
+ return None;
+ }
+}
+
+llvm::StringRef CPlusPlusNameParser::GetTextForRange(const Range &range) {
+ if (range.empty())
+ return llvm::StringRef();
+ assert(range.begin_index < range.end_index);
+ assert(range.begin_index < m_tokens.size());
+ assert(range.end_index <= m_tokens.size());
+ clang::Token &first_token = m_tokens[range.begin_index];
+ clang::Token &last_token = m_tokens[range.end_index - 1];
+ clang::SourceLocation start_loc = first_token.getLocation();
+ clang::SourceLocation end_loc = last_token.getLocation();
+ unsigned start_pos = start_loc.getRawEncoding();
+ unsigned end_pos = end_loc.getRawEncoding() + last_token.getLength();
+ return m_text.take_front(end_pos).drop_front(start_pos);
+}
+
+static const clang::LangOptions &GetLangOptions() {
+ static clang::LangOptions g_options;
+ static llvm::once_flag g_once_flag;
+ llvm::call_once(g_once_flag, []() {
+ g_options.LineComment = true;
+ g_options.C99 = true;
+ g_options.C11 = true;
+ g_options.CPlusPlus = true;
+ g_options.CPlusPlus11 = true;
+ g_options.CPlusPlus14 = true;
+ g_options.CPlusPlus17 = true;
+ });
+ return g_options;
+}
+
+static const llvm::StringMap<tok::TokenKind> &GetKeywordsMap() {
+ static llvm::StringMap<tok::TokenKind> g_map{
+#define KEYWORD(Name, Flags) {llvm::StringRef(#Name), tok::kw_##Name},
+#include "clang/Basic/TokenKinds.def"
+#undef KEYWORD
+ };
+ return g_map;
+}
+
+void CPlusPlusNameParser::ExtractTokens() {
+ if (m_text.empty())
+ return;
+ clang::Lexer lexer(clang::SourceLocation(), GetLangOptions(), m_text.data(),
+ m_text.data(), m_text.data() + m_text.size());
+ const auto &kw_map = GetKeywordsMap();
+ clang::Token token;
+ for (lexer.LexFromRawLexer(token); !token.is(clang::tok::eof);
+ lexer.LexFromRawLexer(token)) {
+ if (token.is(clang::tok::raw_identifier)) {
+ auto it = kw_map.find(token.getRawIdentifier());
+ if (it != kw_map.end()) {
+ token.setKind(it->getValue());
+ }
+ }
+
+ m_tokens.push_back(token);
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h
new file mode 100644
index 000000000000..414c3a009157
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h
@@ -0,0 +1,177 @@
+//===-- CPlusPlusNameParser.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 liblldb_CPlusPlusNameParser_h_
+#define liblldb_CPlusPlusNameParser_h_
+
+
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+
+#include "lldb/Utility/ConstString.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+// Helps to validate and obtain various parts of C++ definitions.
+class CPlusPlusNameParser {
+public:
+ CPlusPlusNameParser(llvm::StringRef text) : m_text(text) { ExtractTokens(); }
+
+ struct ParsedName {
+ llvm::StringRef basename;
+ llvm::StringRef context;
+ };
+
+ struct ParsedFunction {
+ ParsedName name;
+ llvm::StringRef arguments;
+ llvm::StringRef qualifiers;
+ };
+
+ // Treats given text as a function definition and parses it.
+ // Function definition might or might not have a return type and this should
+ // change parsing result.
+ // Examples:
+ // main(int, chat const*)
+ // T fun(int, bool)
+ // std::vector<int>::push_back(int)
+ // int& map<int, pair<short, int>>::operator[](short) const
+ // int (*get_function(const chat *))()
+ llvm::Optional<ParsedFunction> ParseAsFunctionDefinition();
+
+ // Treats given text as a potentially nested name of C++ entity (function,
+ // class, field) and parses it.
+ // Examples:
+ // main
+ // fun
+ // std::vector<int>::push_back
+ // map<int, pair<short, int>>::operator[]
+ // func<C>(int, C&)::nested_class::method
+ llvm::Optional<ParsedName> ParseAsFullName();
+
+private:
+ // A C++ definition to parse.
+ llvm::StringRef m_text;
+ // Tokens extracted from m_text.
+ llvm::SmallVector<clang::Token, 30> m_tokens;
+ // Index of the next token to look at from m_tokens.
+ size_t m_next_token_index = 0;
+
+ // Range of tokens saved in m_next_token_index.
+ struct Range {
+ size_t begin_index = 0;
+ size_t end_index = 0;
+
+ Range() {}
+ Range(size_t begin, size_t end) : begin_index(begin), end_index(end) {
+ assert(end >= begin);
+ }
+
+ size_t size() const { return end_index - begin_index; }
+
+ bool empty() const { return size() == 0; }
+ };
+
+ struct ParsedNameRanges {
+ Range basename_range;
+ Range context_range;
+ };
+
+ // Bookmark automatically restores parsing position (m_next_token_index)
+ // when destructed unless it's manually removed with Remove().
+ class Bookmark {
+ public:
+ Bookmark(size_t &position)
+ : m_position(position), m_position_value(position) {}
+ Bookmark(const Bookmark &) = delete;
+ Bookmark(Bookmark &&b)
+ : m_position(b.m_position), m_position_value(b.m_position_value),
+ m_restore(b.m_restore) {
+ b.Remove();
+ }
+ Bookmark &operator=(Bookmark &&) = delete;
+ Bookmark &operator=(const Bookmark &) = delete;
+
+ void Remove() { m_restore = false; }
+ size_t GetSavedPosition() { return m_position_value; }
+ ~Bookmark() {
+ if (m_restore) {
+ m_position = m_position_value;
+ }
+ }
+
+ private:
+ size_t &m_position;
+ size_t m_position_value;
+ bool m_restore = true;
+ };
+
+ bool HasMoreTokens();
+ void Advance();
+ void TakeBack();
+ bool ConsumeToken(clang::tok::TokenKind kind);
+ template <typename... Ts> bool ConsumeToken(Ts... kinds);
+ Bookmark SetBookmark();
+ size_t GetCurrentPosition();
+ clang::Token &Peek();
+ bool ConsumeBrackets(clang::tok::TokenKind left, clang::tok::TokenKind right);
+
+ llvm::Optional<ParsedFunction> ParseFunctionImpl(bool expect_return_type);
+
+ // Parses functions returning function pointers 'string (*f(int x))(float y)'
+ llvm::Optional<ParsedFunction> ParseFuncPtr(bool expect_return_type);
+
+ // Consumes function arguments enclosed within '(' ... ')'
+ bool ConsumeArguments();
+
+ // Consumes template arguments enclosed within '<' ... '>'
+ bool ConsumeTemplateArgs();
+
+ // Consumes '(anonymous namespace)'
+ bool ConsumeAnonymousNamespace();
+
+ // Consumes '{lambda ...}'
+ bool ConsumeLambda();
+
+ // Consumes operator declaration like 'operator *' or 'operator delete []'
+ bool ConsumeOperator();
+
+ // Skips 'const' and 'volatile'
+ void SkipTypeQualifiers();
+
+ // Skips 'const', 'volatile', '&', '&&' in the end of the function.
+ void SkipFunctionQualifiers();
+
+ // Consumes built-in types like 'int' or 'unsigned long long int'
+ bool ConsumeBuiltinType();
+
+ // Consumes types defined via decltype keyword.
+ bool ConsumeDecltype();
+
+ // Skips 'const' and 'volatile'
+ void SkipPtrsAndRefs();
+
+ // Consumes things like 'const * const &'
+ bool ConsumePtrsAndRefs();
+
+ // Consumes full type name like 'Namespace::Class<int>::Method()::InnerClass'
+ bool ConsumeTypename();
+
+ llvm::Optional<ParsedNameRanges> ParseFullNameImpl();
+ llvm::StringRef GetTextForRange(const Range &range);
+
+ // Populate m_tokens by calling clang lexer on m_text.
+ void ExtractTokens();
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CPlusPlusNameParser_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp
new file mode 100644
index 000000000000..959079070acc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp
@@ -0,0 +1,228 @@
+//===-- CxxStringTypes.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 "CxxStringTypes.h"
+
+#include "llvm/Support/ConvertUTF.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/DataFormatters/StringPrinter.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/Host/Time.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/ProcessStructReader.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+#include <algorithm>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+bool lldb_private::formatters::Char16StringSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ lldb::addr_t valobj_addr = GetArrayAddressOrPointerValue(valobj);
+ if (valobj_addr == 0 || valobj_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
+ options.SetLocation(valobj_addr);
+ options.SetProcessSP(process_sp);
+ options.SetStream(&stream);
+ options.SetPrefixToken("u");
+
+ if (!StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF16>(options)) {
+ stream.Printf("Summary Unavailable");
+ return true;
+ }
+
+ return true;
+}
+
+bool lldb_private::formatters::Char32StringSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ lldb::addr_t valobj_addr = GetArrayAddressOrPointerValue(valobj);
+ if (valobj_addr == 0 || valobj_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
+ options.SetLocation(valobj_addr);
+ options.SetProcessSP(process_sp);
+ options.SetStream(&stream);
+ options.SetPrefixToken("U");
+
+ if (!StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF32>(options)) {
+ stream.Printf("Summary Unavailable");
+ return true;
+ }
+
+ return true;
+}
+
+bool lldb_private::formatters::WCharStringSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ lldb::addr_t valobj_addr = GetArrayAddressOrPointerValue(valobj);
+ if (valobj_addr == 0 || valobj_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ // Get a wchar_t basic type from the current type system
+ CompilerType wchar_compiler_type =
+ valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeWChar);
+
+ if (!wchar_compiler_type)
+ return false;
+
+ // Safe to pass nullptr for exe_scope here.
+ llvm::Optional<uint64_t> size = wchar_compiler_type.GetBitSize(nullptr);
+ if (!size)
+ return false;
+ const uint32_t wchar_size = *size;
+
+ StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
+ options.SetLocation(valobj_addr);
+ options.SetProcessSP(process_sp);
+ options.SetStream(&stream);
+ options.SetPrefixToken("L");
+
+ switch (wchar_size) {
+ case 8:
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF8>(options);
+ case 16:
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF16>(options);
+ case 32:
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF32>(options);
+ default:
+ stream.Printf("size for wchar_t is not valid");
+ return true;
+ }
+ return true;
+}
+
+bool lldb_private::formatters::Char16SummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) {
+ DataExtractor data;
+ Status error;
+ valobj.GetData(data, error);
+
+ if (error.Fail())
+ return false;
+
+ std::string value;
+ valobj.GetValueAsCString(lldb::eFormatUnicode16, value);
+ if (!value.empty())
+ stream.Printf("%s ", value.c_str());
+
+ StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
+ options.SetData(data);
+ options.SetStream(&stream);
+ options.SetPrefixToken("u");
+ options.SetQuote('\'');
+ options.SetSourceSize(1);
+ options.SetBinaryZeroIsTerminator(false);
+
+ return StringPrinter::ReadBufferAndDumpToStream<
+ StringPrinter::StringElementType::UTF16>(options);
+}
+
+bool lldb_private::formatters::Char32SummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) {
+ DataExtractor data;
+ Status error;
+ valobj.GetData(data, error);
+
+ if (error.Fail())
+ return false;
+
+ std::string value;
+ valobj.GetValueAsCString(lldb::eFormatUnicode32, value);
+ if (!value.empty())
+ stream.Printf("%s ", value.c_str());
+
+ StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
+ options.SetData(data);
+ options.SetStream(&stream);
+ options.SetPrefixToken("U");
+ options.SetQuote('\'');
+ options.SetSourceSize(1);
+ options.SetBinaryZeroIsTerminator(false);
+
+ return StringPrinter::ReadBufferAndDumpToStream<
+ StringPrinter::StringElementType::UTF32>(options);
+}
+
+bool lldb_private::formatters::WCharSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) {
+ DataExtractor data;
+ Status error;
+ valobj.GetData(data, error);
+
+ if (error.Fail())
+ return false;
+
+ // Get a wchar_t basic type from the current type system
+ CompilerType wchar_compiler_type =
+ valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeWChar);
+
+ if (!wchar_compiler_type)
+ return false;
+
+ // Safe to pass nullptr for exe_scope here.
+ llvm::Optional<uint64_t> size = wchar_compiler_type.GetBitSize(nullptr);
+ if (!size)
+ return false;
+ const uint32_t wchar_size = *size;
+
+ StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
+ options.SetData(data);
+ options.SetStream(&stream);
+ options.SetPrefixToken("L");
+ options.SetQuote('\'');
+ options.SetSourceSize(1);
+ options.SetBinaryZeroIsTerminator(false);
+
+ switch (wchar_size) {
+ case 8:
+ return StringPrinter::ReadBufferAndDumpToStream<
+ StringPrinter::StringElementType::UTF8>(options);
+ case 16:
+ return StringPrinter::ReadBufferAndDumpToStream<
+ StringPrinter::StringElementType::UTF16>(options);
+ case 32:
+ return StringPrinter::ReadBufferAndDumpToStream<
+ StringPrinter::StringElementType::UTF32>(options);
+ default:
+ stream.Printf("size for wchar_t is not valid");
+ return true;
+ }
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.h
new file mode 100644
index 000000000000..92bef2382eac
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.h
@@ -0,0 +1,43 @@
+//===-- CxxStringTypes.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 liblldb_CxxStringTypes_h_
+#define liblldb_CxxStringTypes_h_
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/Utility/Stream.h"
+
+namespace lldb_private {
+namespace formatters {
+bool Char16StringSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // char16_t* and unichar*
+
+bool Char32StringSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // char32_t*
+
+bool WCharStringSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // wchar_t*
+
+bool Char16SummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // char16_t and unichar
+
+bool Char32SummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // char32_t
+
+bool WCharSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // wchar_t
+
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // liblldb_CxxStringTypes_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
new file mode 100644
index 000000000000..abe89035c532
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
@@ -0,0 +1,681 @@
+//===-- LibCxx.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 "LibCxx.h"
+
+#include "llvm/ADT/ScopeExit.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/FormatEntity.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/DataFormatters/StringPrinter.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/DataFormatters/VectorIterator.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/ProcessStructReader.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+#include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+bool lldb_private::formatters::LibcxxOptionalSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
+ if (!valobj_sp)
+ return false;
+
+ // An optional either contains a value or not, the member __engaged_ is
+ // a bool flag, it is true if the optional has a value and false otherwise.
+ ValueObjectSP engaged_sp(
+ valobj_sp->GetChildMemberWithName(ConstString("__engaged_"), true));
+
+ if (!engaged_sp)
+ return false;
+
+ llvm::StringRef engaged_as_cstring(
+ engaged_sp->GetValueAsUnsigned(0) == 1 ? "true" : "false");
+
+ stream.Printf(" Has Value=%s ", engaged_as_cstring.data());
+
+ return true;
+}
+
+bool lldb_private::formatters::LibcxxFunctionSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+
+ ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
+
+ if (!valobj_sp)
+ return false;
+
+ ExecutionContext exe_ctx(valobj_sp->GetExecutionContextRef());
+ Process *process = exe_ctx.GetProcessPtr();
+
+ if (process == nullptr)
+ return false;
+
+ CPPLanguageRuntime *cpp_runtime = CPPLanguageRuntime::Get(*process);
+
+ if (!cpp_runtime)
+ return false;
+
+ CPPLanguageRuntime::LibCppStdFunctionCallableInfo callable_info =
+ cpp_runtime->FindLibCppStdFunctionCallableInfo(valobj_sp);
+
+ switch (callable_info.callable_case) {
+ case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Invalid:
+ stream.Printf(" __f_ = %" PRIu64, callable_info.member__f_pointer_value);
+ return false;
+ break;
+ case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Lambda:
+ stream.Printf(
+ " Lambda in File %s at Line %u",
+ callable_info.callable_line_entry.file.GetFilename().GetCString(),
+ callable_info.callable_line_entry.line);
+ break;
+ case CPPLanguageRuntime::LibCppStdFunctionCallableCase::CallableObject:
+ stream.Printf(
+ " Function in File %s at Line %u",
+ callable_info.callable_line_entry.file.GetFilename().GetCString(),
+ callable_info.callable_line_entry.line);
+ break;
+ case CPPLanguageRuntime::LibCppStdFunctionCallableCase::FreeOrMemberFunction:
+ stream.Printf(" Function = %s ",
+ callable_info.callable_symbol.GetName().GetCString());
+ break;
+ }
+
+ return true;
+}
+
+bool lldb_private::formatters::LibcxxSmartPointerSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
+ if (!valobj_sp)
+ return false;
+ ValueObjectSP ptr_sp(
+ valobj_sp->GetChildMemberWithName(ConstString("__ptr_"), true));
+ ValueObjectSP count_sp(valobj_sp->GetChildAtNamePath(
+ {ConstString("__cntrl_"), ConstString("__shared_owners_")}));
+ ValueObjectSP weakcount_sp(valobj_sp->GetChildAtNamePath(
+ {ConstString("__cntrl_"), ConstString("__shared_weak_owners_")}));
+
+ if (!ptr_sp)
+ return false;
+
+ if (ptr_sp->GetValueAsUnsigned(0) == 0) {
+ stream.Printf("nullptr");
+ return true;
+ } else {
+ bool print_pointee = false;
+ Status error;
+ ValueObjectSP pointee_sp = ptr_sp->Dereference(error);
+ if (pointee_sp && error.Success()) {
+ if (pointee_sp->DumpPrintableRepresentation(
+ stream, ValueObject::eValueObjectRepresentationStyleSummary,
+ lldb::eFormatInvalid,
+ ValueObject::PrintableRepresentationSpecialCases::eDisable,
+ false))
+ print_pointee = true;
+ }
+ if (!print_pointee)
+ stream.Printf("ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(0));
+ }
+
+ if (count_sp)
+ stream.Printf(" strong=%" PRIu64, 1 + count_sp->GetValueAsUnsigned(0));
+
+ if (weakcount_sp)
+ stream.Printf(" weak=%" PRIu64, 1 + weakcount_sp->GetValueAsUnsigned(0));
+
+ return true;
+}
+
+/*
+ (lldb) fr var ibeg --raw --ptr-depth 1
+ (std::__1::__map_iterator<std::__1::__tree_iterator<std::__1::pair<int,
+ std::__1::basic_string<char, std::__1::char_traits<char>,
+ std::__1::allocator<char> > >, std::__1::__tree_node<std::__1::pair<int,
+ std::__1::basic_string<char, std::__1::char_traits<char>,
+ std::__1::allocator<char> > >, void *> *, long> >) ibeg = {
+ __i_ = {
+ __ptr_ = 0x0000000100103870 {
+ std::__1::__tree_node_base<void *> = {
+ std::__1::__tree_end_node<std::__1::__tree_node_base<void *> *> = {
+ __left_ = 0x0000000000000000
+ }
+ __right_ = 0x0000000000000000
+ __parent_ = 0x00000001001038b0
+ __is_black_ = true
+ }
+ __value_ = {
+ first = 0
+ second = { std::string }
+ */
+
+lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
+ LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_pair_ptr(), m_pair_sp() {
+ if (valobj_sp)
+ Update();
+}
+
+bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::Update() {
+ m_pair_sp.reset();
+ m_pair_ptr = nullptr;
+
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ if (!valobj_sp)
+ return false;
+
+ TargetSP target_sp(valobj_sp->GetTargetSP());
+
+ if (!target_sp)
+ return false;
+
+ if (!valobj_sp)
+ return false;
+
+ static ConstString g___i_("__i_");
+
+ // this must be a ValueObject* because it is a child of the ValueObject we
+ // are producing children for it if were a ValueObjectSP, we would end up
+ // with a loop (iterator -> synthetic -> child -> parent == iterator) and
+ // that would in turn leak memory by never allowing the ValueObjects to die
+ // and free their memory
+ m_pair_ptr = valobj_sp
+ ->GetValueForExpressionPath(
+ ".__i_.__ptr_->__value_", nullptr, nullptr,
+ ValueObject::GetValueForExpressionPathOptions()
+ .DontCheckDotVsArrowSyntax()
+ .SetSyntheticChildrenTraversal(
+ ValueObject::GetValueForExpressionPathOptions::
+ SyntheticChildrenTraversal::None),
+ nullptr)
+ .get();
+
+ if (!m_pair_ptr) {
+ m_pair_ptr = valobj_sp
+ ->GetValueForExpressionPath(
+ ".__i_.__ptr_", nullptr, nullptr,
+ ValueObject::GetValueForExpressionPathOptions()
+ .DontCheckDotVsArrowSyntax()
+ .SetSyntheticChildrenTraversal(
+ ValueObject::GetValueForExpressionPathOptions::
+ SyntheticChildrenTraversal::None),
+ nullptr)
+ .get();
+ if (m_pair_ptr) {
+ auto __i_(valobj_sp->GetChildMemberWithName(g___i_, true));
+ if (!__i_) {
+ m_pair_ptr = nullptr;
+ return false;
+ }
+ CompilerType pair_type(
+ __i_->GetCompilerType().GetTypeTemplateArgument(0));
+ std::string name;
+ uint64_t bit_offset_ptr;
+ uint32_t bitfield_bit_size_ptr;
+ bool is_bitfield_ptr;
+ pair_type = pair_type.GetFieldAtIndex(
+ 0, name, &bit_offset_ptr, &bitfield_bit_size_ptr, &is_bitfield_ptr);
+ if (!pair_type) {
+ m_pair_ptr = nullptr;
+ return false;
+ }
+
+ auto addr(m_pair_ptr->GetValueAsUnsigned(LLDB_INVALID_ADDRESS));
+ m_pair_ptr = nullptr;
+ if (addr && addr != LLDB_INVALID_ADDRESS) {
+ ClangASTContext *ast_ctx =
+ llvm::dyn_cast_or_null<ClangASTContext>(pair_type.GetTypeSystem());
+ if (!ast_ctx)
+ return false;
+ CompilerType tree_node_type = ast_ctx->CreateStructForIdentifier(
+ ConstString(),
+ {{"ptr0",
+ ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
+ {"ptr1",
+ ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
+ {"ptr2",
+ ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
+ {"cw", ast_ctx->GetBasicType(lldb::eBasicTypeBool)},
+ {"payload", pair_type}});
+ llvm::Optional<uint64_t> size = tree_node_type.GetByteSize(nullptr);
+ if (!size)
+ return false;
+ DataBufferSP buffer_sp(new DataBufferHeap(*size, 0));
+ ProcessSP process_sp(target_sp->GetProcessSP());
+ Status error;
+ process_sp->ReadMemory(addr, buffer_sp->GetBytes(),
+ buffer_sp->GetByteSize(), error);
+ if (error.Fail())
+ return false;
+ DataExtractor extractor(buffer_sp, process_sp->GetByteOrder(),
+ process_sp->GetAddressByteSize());
+ auto pair_sp = CreateValueObjectFromData(
+ "pair", extractor, valobj_sp->GetExecutionContextRef(),
+ tree_node_type);
+ if (pair_sp)
+ m_pair_sp = pair_sp->GetChildAtIndex(4, true);
+ }
+ }
+ }
+
+ return false;
+}
+
+size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
+ CalculateNumChildren() {
+ return 2;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::GetChildAtIndex(
+ size_t idx) {
+ if (m_pair_ptr)
+ return m_pair_ptr->GetChildAtIndex(idx, true);
+ if (m_pair_sp)
+ return m_pair_sp->GetChildAtIndex(idx, true);
+ return lldb::ValueObjectSP();
+}
+
+bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
+ MightHaveChildren() {
+ return true;
+}
+
+size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
+ GetIndexOfChildWithName(ConstString name) {
+ if (name == "first")
+ return 0;
+ if (name == "second")
+ return 1;
+ return UINT32_MAX;
+}
+
+lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
+ ~LibCxxMapIteratorSyntheticFrontEnd() {
+ // this will be deleted when its parent dies (since it's a child object)
+ // delete m_pair_ptr;
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return (valobj_sp ? new LibCxxMapIteratorSyntheticFrontEnd(valobj_sp)
+ : nullptr);
+}
+
+/*
+ (lldb) fr var ibeg --raw --ptr-depth 1 -T
+ (std::__1::__wrap_iter<int *>) ibeg = {
+ (std::__1::__wrap_iter<int *>::iterator_type) __i = 0x00000001001037a0 {
+ (int) *__i = 1
+ }
+ }
+*/
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ static ConstString g_item_name;
+ if (!g_item_name)
+ g_item_name.SetCString("__i");
+ return (valobj_sp
+ ? new VectorIteratorSyntheticFrontEnd(valobj_sp, g_item_name)
+ : nullptr);
+}
+
+lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
+ LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_cntrl(nullptr), m_count_sp(),
+ m_weak_count_sp(), m_ptr_size(0), m_byte_order(lldb::eByteOrderInvalid) {
+ if (valobj_sp)
+ Update();
+}
+
+size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
+ CalculateNumChildren() {
+ return (m_cntrl ? 1 : 0);
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex(
+ size_t idx) {
+ if (!m_cntrl)
+ return lldb::ValueObjectSP();
+
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ if (!valobj_sp)
+ return lldb::ValueObjectSP();
+
+ if (idx == 0)
+ return valobj_sp->GetChildMemberWithName(ConstString("__ptr_"), true);
+
+ if (idx > 2)
+ return lldb::ValueObjectSP();
+
+ if (idx == 1) {
+ if (!m_count_sp) {
+ ValueObjectSP shared_owners_sp(m_cntrl->GetChildMemberWithName(
+ ConstString("__shared_owners_"), true));
+ if (!shared_owners_sp)
+ return lldb::ValueObjectSP();
+ uint64_t count = 1 + shared_owners_sp->GetValueAsUnsigned(0);
+ DataExtractor data(&count, 8, m_byte_order, m_ptr_size);
+ m_count_sp = CreateValueObjectFromData(
+ "count", data, valobj_sp->GetExecutionContextRef(),
+ shared_owners_sp->GetCompilerType());
+ }
+ return m_count_sp;
+ } else /* if (idx == 2) */
+ {
+ if (!m_weak_count_sp) {
+ ValueObjectSP shared_weak_owners_sp(m_cntrl->GetChildMemberWithName(
+ ConstString("__shared_weak_owners_"), true));
+ if (!shared_weak_owners_sp)
+ return lldb::ValueObjectSP();
+ uint64_t count = 1 + shared_weak_owners_sp->GetValueAsUnsigned(0);
+ DataExtractor data(&count, 8, m_byte_order, m_ptr_size);
+ m_weak_count_sp = CreateValueObjectFromData(
+ "count", data, valobj_sp->GetExecutionContextRef(),
+ shared_weak_owners_sp->GetCompilerType());
+ }
+ return m_weak_count_sp;
+ }
+}
+
+bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::Update() {
+ m_count_sp.reset();
+ m_weak_count_sp.reset();
+ m_cntrl = nullptr;
+
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ if (!valobj_sp)
+ return false;
+
+ TargetSP target_sp(valobj_sp->GetTargetSP());
+ if (!target_sp)
+ return false;
+
+ m_byte_order = target_sp->GetArchitecture().GetByteOrder();
+ m_ptr_size = target_sp->GetArchitecture().GetAddressByteSize();
+
+ lldb::ValueObjectSP cntrl_sp(
+ valobj_sp->GetChildMemberWithName(ConstString("__cntrl_"), true));
+
+ m_cntrl = cntrl_sp.get(); // need to store the raw pointer to avoid a circular
+ // dependency
+ return false;
+}
+
+bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
+ MightHaveChildren() {
+ return true;
+}
+
+size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
+ GetIndexOfChildWithName(ConstString name) {
+ if (name == "__ptr_")
+ return 0;
+ if (name == "count")
+ return 1;
+ if (name == "weak_count")
+ return 2;
+ return UINT32_MAX;
+}
+
+lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::
+ ~LibcxxSharedPtrSyntheticFrontEnd() = default;
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return (valobj_sp ? new LibcxxSharedPtrSyntheticFrontEnd(valobj_sp)
+ : nullptr);
+}
+
+bool lldb_private::formatters::LibcxxContainerSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ if (valobj.IsPointerType()) {
+ uint64_t value = valobj.GetValueAsUnsigned(0);
+ if (!value)
+ return false;
+ stream.Printf("0x%016" PRIx64 " ", value);
+ }
+ return FormatEntity::FormatStringRef("size=${svar%#}", stream, nullptr,
+ nullptr, nullptr, &valobj, false, false);
+}
+
+// the field layout in a libc++ string (cap, side, data or data, size, cap)
+enum LibcxxStringLayoutMode {
+ eLibcxxStringLayoutModeCSD = 0,
+ eLibcxxStringLayoutModeDSC = 1,
+ eLibcxxStringLayoutModeInvalid = 0xffff
+};
+
+// this function abstracts away the layout and mode details of a libc++ string
+// and returns the address of the data and the size ready for callers to
+// consume
+static bool ExtractLibcxxStringInfo(ValueObject &valobj,
+ ValueObjectSP &location_sp,
+ uint64_t &size) {
+ ValueObjectSP D(valobj.GetChildAtIndexPath({0, 0, 0, 0}));
+ if (!D)
+ return false;
+
+ ValueObjectSP layout_decider(
+ D->GetChildAtIndexPath(llvm::ArrayRef<size_t>({0, 0})));
+
+ // this child should exist
+ if (!layout_decider)
+ return false;
+
+ ConstString g_data_name("__data_");
+ ConstString g_size_name("__size_");
+ bool short_mode = false; // this means the string is in short-mode and the
+ // data is stored inline
+ LibcxxStringLayoutMode layout = (layout_decider->GetName() == g_data_name)
+ ? eLibcxxStringLayoutModeDSC
+ : eLibcxxStringLayoutModeCSD;
+ uint64_t size_mode_value = 0;
+
+ if (layout == eLibcxxStringLayoutModeDSC) {
+ ValueObjectSP size_mode(D->GetChildAtIndexPath({1, 1, 0}));
+ if (!size_mode)
+ return false;
+
+ if (size_mode->GetName() != g_size_name) {
+ // we are hitting the padding structure, move along
+ size_mode = D->GetChildAtIndexPath({1, 1, 1});
+ if (!size_mode)
+ return false;
+ }
+
+ size_mode_value = (size_mode->GetValueAsUnsigned(0));
+ short_mode = ((size_mode_value & 0x80) == 0);
+ } else {
+ ValueObjectSP size_mode(D->GetChildAtIndexPath({1, 0, 0}));
+ if (!size_mode)
+ return false;
+
+ size_mode_value = (size_mode->GetValueAsUnsigned(0));
+ short_mode = ((size_mode_value & 1) == 0);
+ }
+
+ if (short_mode) {
+ ValueObjectSP s(D->GetChildAtIndex(1, true));
+ if (!s)
+ return false;
+ location_sp = s->GetChildAtIndex(
+ (layout == eLibcxxStringLayoutModeDSC) ? 0 : 1, true);
+ size = (layout == eLibcxxStringLayoutModeDSC)
+ ? size_mode_value
+ : ((size_mode_value >> 1) % 256);
+ return (location_sp.get() != nullptr);
+ } else {
+ ValueObjectSP l(D->GetChildAtIndex(0, true));
+ if (!l)
+ return false;
+ // we can use the layout_decider object as the data pointer
+ location_sp = (layout == eLibcxxStringLayoutModeDSC)
+ ? layout_decider
+ : l->GetChildAtIndex(2, true);
+ ValueObjectSP size_vo(l->GetChildAtIndex(1, true));
+ if (!size_vo || !location_sp)
+ return false;
+ size = size_vo->GetValueAsUnsigned(0);
+ return true;
+ }
+}
+
+bool lldb_private::formatters::LibcxxWStringSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &summary_options) {
+ uint64_t size = 0;
+ ValueObjectSP location_sp;
+ if (!ExtractLibcxxStringInfo(valobj, location_sp, size))
+ return false;
+ if (size == 0) {
+ stream.Printf("L\"\"");
+ return true;
+ }
+ if (!location_sp)
+ return false;
+
+ DataExtractor extractor;
+
+ StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
+
+ if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) {
+ const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary();
+ if (size > max_size) {
+ size = max_size;
+ options.SetIsTruncated(true);
+ }
+ }
+ location_sp->GetPointeeData(extractor, 0, size);
+
+ // std::wstring::size() is measured in 'characters', not bytes
+ auto wchar_t_size = valobj.GetTargetSP()
+ ->GetScratchClangASTContext()
+ ->GetBasicType(lldb::eBasicTypeWChar)
+ .GetByteSize(nullptr);
+ if (!wchar_t_size)
+ return false;
+
+ options.SetData(extractor);
+ options.SetStream(&stream);
+ options.SetPrefixToken("L");
+ options.SetQuote('"');
+ options.SetSourceSize(size);
+ options.SetBinaryZeroIsTerminator(false);
+
+ switch (*wchar_t_size) {
+ case 1:
+ StringPrinter::ReadBufferAndDumpToStream<
+ lldb_private::formatters::StringPrinter::StringElementType::UTF8>(
+ options);
+ break;
+
+ case 2:
+ lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStream<
+ lldb_private::formatters::StringPrinter::StringElementType::UTF16>(
+ options);
+ break;
+
+ case 4:
+ lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStream<
+ lldb_private::formatters::StringPrinter::StringElementType::UTF32>(
+ options);
+ break;
+
+ default:
+ stream.Printf("size for wchar_t is not valid");
+ return true;
+ }
+
+ return true;
+}
+
+template <StringPrinter::StringElementType element_type>
+bool LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &summary_options,
+ std::string prefix_token = "") {
+ uint64_t size = 0;
+ ValueObjectSP location_sp;
+
+ if (!ExtractLibcxxStringInfo(valobj, location_sp, size))
+ return false;
+
+ if (size == 0) {
+ stream.Printf("\"\"");
+ return true;
+ }
+
+ if (!location_sp)
+ return false;
+
+ StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj);
+
+ DataExtractor extractor;
+ if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) {
+ const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary();
+ if (size > max_size) {
+ size = max_size;
+ options.SetIsTruncated(true);
+ }
+ }
+ location_sp->GetPointeeData(extractor, 0, size);
+
+ options.SetData(extractor);
+ options.SetStream(&stream);
+
+ if (prefix_token.empty())
+ options.SetPrefixToken(nullptr);
+ else
+ options.SetPrefixToken(prefix_token);
+
+ options.SetQuote('"');
+ options.SetSourceSize(size);
+ options.SetBinaryZeroIsTerminator(false);
+ StringPrinter::ReadBufferAndDumpToStream<element_type>(options);
+
+ return true;
+}
+
+bool lldb_private::formatters::LibcxxStringSummaryProviderASCII(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &summary_options) {
+ return LibcxxStringSummaryProvider<StringPrinter::StringElementType::ASCII>(
+ valobj, stream, summary_options);
+}
+
+bool lldb_private::formatters::LibcxxStringSummaryProviderUTF16(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &summary_options) {
+ return LibcxxStringSummaryProvider<StringPrinter::StringElementType::UTF16>(
+ valobj, stream, summary_options, "u");
+}
+
+bool lldb_private::formatters::LibcxxStringSummaryProviderUTF32(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &summary_options) {
+ return LibcxxStringSummaryProvider<StringPrinter::StringElementType::UTF32>(
+ valobj, stream, summary_options, "U");
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
new file mode 100644
index 000000000000..214f5512e4a5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
@@ -0,0 +1,159 @@
+//===-- LibCxx.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 liblldb_LibCxx_h_
+#define liblldb_LibCxx_h_
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Utility/Stream.h"
+
+namespace lldb_private {
+namespace formatters {
+
+bool LibcxxStringSummaryProviderASCII(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &summary_options); // libc++ std::string
+
+bool LibcxxStringSummaryProviderUTF16(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &summary_options); // libc++ std::u16string
+
+bool LibcxxStringSummaryProviderUTF32(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &summary_options); // libc++ std::u32string
+
+bool LibcxxWStringSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // libc++ std::wstring
+
+bool LibcxxOptionalSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // libc++ std::optional<>
+
+bool LibcxxSmartPointerSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions
+ &options); // libc++ std::shared_ptr<> and std::weak_ptr<>
+
+bool LibcxxFunctionSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // libc++ std::function<>
+
+SyntheticChildrenFrontEnd *
+LibcxxVectorBoolSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+bool LibcxxContainerSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+class LibCxxMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+ ~LibCxxMapIteratorSyntheticFrontEnd() override;
+
+private:
+ ValueObject *m_pair_ptr;
+ lldb::ValueObjectSP m_pair_sp;
+};
+
+SyntheticChildrenFrontEnd *
+LibCxxMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibCxxVectorIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+class LibcxxSharedPtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+ ~LibcxxSharedPtrSyntheticFrontEnd() override;
+
+private:
+ ValueObject *m_cntrl;
+ lldb::ValueObjectSP m_count_sp;
+ lldb::ValueObjectSP m_weak_count_sp;
+ uint8_t m_ptr_size;
+ lldb::ByteOrder m_byte_order;
+};
+
+SyntheticChildrenFrontEnd *
+LibcxxBitsetSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibcxxSharedPtrSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibcxxStdVectorSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibcxxStdListSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibcxxStdForwardListSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibcxxStdMapSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibcxxStdUnorderedMapSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibcxxInitializerListSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *LibcxxQueueFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *LibcxxTupleFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibcxxOptionalFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP valobj_sp);
+
+SyntheticChildrenFrontEnd *
+LibcxxVariantFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP valobj_sp);
+
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // liblldb_LibCxx_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.cpp
new file mode 100644
index 000000000000..b4e7a1703e46
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.cpp
@@ -0,0 +1,159 @@
+//===-- LibCxxAtomic.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 "LibCxxAtomic.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+//
+// We are supporting two versions of libc++ std::atomic
+//
+// Given std::atomic<int> i;
+//
+// The previous version of std::atomic was laid out like this
+//
+// (lldb) frame var -L -R i
+// 0x00007ffeefbff9a0: (std::__1::atomic<int>) i = {
+// 0x00007ffeefbff9a0: std::__1::__atomic_base<int, true> = {
+// 0x00007ffeefbff9a0: std::__1::__atomic_base<int, false> = {
+// 0x00007ffeefbff9a0: __a_ = 5
+// }
+// }
+// }
+//
+// In this case we need to obtain __a_ and the current version is laid out as so
+//
+// (lldb) frame var -L -R i
+// 0x00007ffeefbff9b0: (std::__1::atomic<int>) i = {
+// 0x00007ffeefbff9b0: std::__1::__atomic_base<int, true> = {
+// 0x00007ffeefbff9b0: std::__1::__atomic_base<int, false> = {
+// 0x00007ffeefbff9b0: __a_ = {
+// 0x00007ffeefbff9b0: std::__1::__cxx_atomic_base_impl<int> = {
+// 0x00007ffeefbff9b0: __a_value = 5
+// }
+// }
+// }
+// }
+//}
+//
+// In this case we need to obtain __a_value
+//
+// The below method covers both cases and returns the relevant member as a
+// ValueObjectSP
+//
+ValueObjectSP
+lldb_private::formatters::GetLibCxxAtomicValue(ValueObject &valobj) {
+ ValueObjectSP non_sythetic = valobj.GetNonSyntheticValue();
+ if (!non_sythetic)
+ return {};
+
+ ValueObjectSP member__a_ =
+ non_sythetic->GetChildMemberWithName(ConstString("__a_"), true);
+ if (!member__a_)
+ return {};
+
+ ValueObjectSP member__a_value =
+ member__a_->GetChildMemberWithName(ConstString("__a_value"), true);
+ if (!member__a_value)
+ return member__a_;
+
+ return member__a_value;
+}
+
+bool lldb_private::formatters::LibCxxAtomicSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+
+ if (ValueObjectSP atomic_value = GetLibCxxAtomicValue(valobj)) {
+ std::string summary;
+ if (atomic_value->GetSummaryAsCString(summary, options) &&
+ summary.size() > 0) {
+ stream.Printf("%s", summary.c_str());
+ return true;
+ }
+ }
+
+ return false;
+}
+
+namespace lldb_private {
+namespace formatters {
+class LibcxxStdAtomicSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ LibcxxStdAtomicSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~LibcxxStdAtomicSyntheticFrontEnd() override = default;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+ lldb::ValueObjectSP GetSyntheticValue() override;
+
+private:
+ ValueObject *m_real_child;
+};
+} // namespace formatters
+} // namespace lldb_private
+
+lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd::
+ LibcxxStdAtomicSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_real_child(nullptr) {}
+
+bool lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd::Update() {
+ ValueObjectSP atomic_value = GetLibCxxAtomicValue(m_backend);
+ if (atomic_value)
+ m_real_child = GetLibCxxAtomicValue(m_backend).get();
+
+ return false;
+}
+
+bool lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd::
+ MightHaveChildren() {
+ return true;
+}
+
+size_t lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd::
+ CalculateNumChildren() {
+ return m_real_child ? m_real_child->GetNumChildren() : 0;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd::GetChildAtIndex(
+ size_t idx) {
+ return m_real_child ? m_real_child->GetChildAtIndex(idx, true) : nullptr;
+}
+
+size_t lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd::
+ GetIndexOfChildWithName(ConstString name) {
+ return m_real_child ? m_real_child->GetIndexOfChildWithName(name)
+ : UINT32_MAX;
+}
+
+lldb::ValueObjectSP lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd::
+ GetSyntheticValue() {
+ if (m_real_child && m_real_child->CanProvideValue())
+ return m_real_child->GetSP();
+ return nullptr;
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibcxxAtomicSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ if (valobj_sp)
+ return new LibcxxStdAtomicSyntheticFrontEnd(valobj_sp);
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.h
new file mode 100644
index 000000000000..8be833dd82f6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.h
@@ -0,0 +1,33 @@
+//===-- LibCxxAtomic.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 liblldb_LibCxxAtomic_h_
+#define liblldb_LibCxxAtomic_h_
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Utility/Stream.h"
+
+namespace lldb_private {
+namespace formatters {
+
+lldb::ValueObjectSP GetLibCxxAtomicValue(ValueObject &valobj);
+
+bool LibCxxAtomicSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+SyntheticChildrenFrontEnd *
+LibcxxAtomicSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // liblldb_LibCxxAtomic_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxBitset.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxBitset.cpp
new file mode 100644
index 000000000000..815dafb6c724
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxBitset.cpp
@@ -0,0 +1,113 @@
+//===-- LibCxxBitset.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 "LibCxx.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+
+class BitsetFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ BitsetFrontEnd(ValueObject &valobj);
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ return formatters::ExtractIndexFromString(name.GetCString());
+ }
+
+ bool MightHaveChildren() override { return true; }
+ bool Update() override;
+ size_t CalculateNumChildren() override { return m_elements.size(); }
+ ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+private:
+ std::vector<ValueObjectSP> m_elements;
+ ValueObjectSP m_first;
+ CompilerType m_bool_type;
+ ByteOrder m_byte_order = eByteOrderInvalid;
+ uint8_t m_byte_size = 0;
+};
+} // namespace
+
+BitsetFrontEnd::BitsetFrontEnd(ValueObject &valobj)
+ : SyntheticChildrenFrontEnd(valobj) {
+ m_bool_type = valobj.GetCompilerType().GetBasicTypeFromAST(eBasicTypeBool);
+ if (auto target_sp = m_backend.GetTargetSP()) {
+ m_byte_order = target_sp->GetArchitecture().GetByteOrder();
+ m_byte_size = target_sp->GetArchitecture().GetAddressByteSize();
+ Update();
+ }
+}
+
+bool BitsetFrontEnd::Update() {
+ m_elements.clear();
+ m_first.reset();
+
+ TargetSP target_sp = m_backend.GetTargetSP();
+ if (!target_sp)
+ return false;
+ size_t capping_size = target_sp->GetMaximumNumberOfChildrenToDisplay();
+
+ size_t size = 0;
+ if (auto arg = m_backend.GetCompilerType().GetIntegralTemplateArgument(0))
+ size = arg->value.getLimitedValue(capping_size);
+
+ m_elements.assign(size, ValueObjectSP());
+
+ m_first = m_backend.GetChildMemberWithName(ConstString("__first_"), true);
+ return false;
+}
+
+ValueObjectSP BitsetFrontEnd::GetChildAtIndex(size_t idx) {
+ if (idx >= m_elements.size() || !m_first)
+ return ValueObjectSP();
+
+ if (m_elements[idx])
+ return m_elements[idx];
+
+ ExecutionContext ctx = m_backend.GetExecutionContextRef().Lock(false);
+ CompilerType type;
+ ValueObjectSP chunk;
+ // For small bitsets __first_ is not an array, but a plain size_t.
+ if (m_first->GetCompilerType().IsArrayType(&type, nullptr, nullptr)) {
+ llvm::Optional<uint64_t> bit_size =
+ type.GetBitSize(ctx.GetBestExecutionContextScope());
+ if (!bit_size || *bit_size == 0)
+ return {};
+ chunk = m_first->GetChildAtIndex(idx / *bit_size, true);
+ } else {
+ type = m_first->GetCompilerType();
+ chunk = m_first;
+ }
+ if (!type || !chunk)
+ return {};
+
+ llvm::Optional<uint64_t> bit_size =
+ type.GetBitSize(ctx.GetBestExecutionContextScope());
+ if (!bit_size || *bit_size == 0)
+ return {};
+ size_t chunk_idx = idx % *bit_size;
+ uint8_t value = !!(chunk->GetValueAsUnsigned(0) & (uint64_t(1) << chunk_idx));
+ DataExtractor data(&value, sizeof(value), m_byte_order, m_byte_size);
+
+ m_elements[idx] = CreateValueObjectFromData(llvm::formatv("[{0}]", idx).str(),
+ data, ctx, m_bool_type);
+
+ return m_elements[idx];
+}
+
+SyntheticChildrenFrontEnd *formatters::LibcxxBitsetSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ if (valobj_sp)
+ return new BitsetFrontEnd(*valobj_sp);
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp
new file mode 100644
index 000000000000..79c7434f617f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp
@@ -0,0 +1,122 @@
+//===-- LibCxxInitializerList.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 "LibCxx.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Utility/ConstString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+namespace lldb_private {
+namespace formatters {
+class LibcxxInitializerListSyntheticFrontEnd
+ : public SyntheticChildrenFrontEnd {
+public:
+ LibcxxInitializerListSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~LibcxxInitializerListSyntheticFrontEnd() override;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ ValueObject *m_start;
+ CompilerType m_element_type;
+ uint32_t m_element_size;
+ size_t m_num_elements;
+};
+} // namespace formatters
+} // namespace lldb_private
+
+lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd::
+ LibcxxInitializerListSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_start(nullptr), m_element_type(),
+ m_element_size(0), m_num_elements(0) {
+ if (valobj_sp)
+ Update();
+}
+
+lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd::
+ ~LibcxxInitializerListSyntheticFrontEnd() {
+ // this needs to stay around because it's a child object who will follow its
+ // parent's life cycle
+ // delete m_start;
+}
+
+size_t lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd::
+ CalculateNumChildren() {
+ static ConstString g___size_("__size_");
+ m_num_elements = 0;
+ ValueObjectSP size_sp(m_backend.GetChildMemberWithName(g___size_, true));
+ if (size_sp)
+ m_num_elements = size_sp->GetValueAsUnsigned(0);
+ return m_num_elements;
+}
+
+lldb::ValueObjectSP lldb_private::formatters::
+ LibcxxInitializerListSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
+ if (!m_start)
+ return lldb::ValueObjectSP();
+
+ uint64_t offset = idx * m_element_size;
+ offset = offset + m_start->GetValueAsUnsigned(0);
+ StreamString name;
+ name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ return CreateValueObjectFromAddress(name.GetString(), offset,
+ m_backend.GetExecutionContextRef(),
+ m_element_type);
+}
+
+bool lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd::
+ Update() {
+ static ConstString g___begin_("__begin_");
+
+ m_start = nullptr;
+ m_num_elements = 0;
+ m_element_type = m_backend.GetCompilerType().GetTypeTemplateArgument(0);
+ if (!m_element_type.IsValid())
+ return false;
+
+ if (llvm::Optional<uint64_t> size = m_element_type.GetByteSize(nullptr)) {
+ m_element_size = *size;
+ // Store raw pointers or end up with a circular dependency.
+ m_start = m_backend.GetChildMemberWithName(g___begin_, true).get();
+ }
+
+ return false;
+}
+
+bool lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd::
+ MightHaveChildren() {
+ return true;
+}
+
+size_t lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd::
+ GetIndexOfChildWithName(ConstString name) {
+ if (!m_start)
+ return UINT32_MAX;
+ return ExtractIndexFromString(name.GetCString());
+}
+
+lldb_private::SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibcxxInitializerListSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return (valobj_sp ? new LibcxxInitializerListSyntheticFrontEnd(valobj_sp)
+ : nullptr);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp
new file mode 100644
index 000000000000..f5281f2ce532
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp
@@ -0,0 +1,444 @@
+//===-- LibCxxList.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 "LibCxx.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+namespace {
+
+class ListEntry {
+public:
+ ListEntry() = default;
+ ListEntry(ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {}
+ ListEntry(const ListEntry &rhs) = default;
+ ListEntry(ValueObject *entry)
+ : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {}
+
+ ListEntry next() {
+ static ConstString g_next("__next_");
+
+ if (!m_entry_sp)
+ return ListEntry();
+ return ListEntry(m_entry_sp->GetChildMemberWithName(g_next, true));
+ }
+
+ ListEntry prev() {
+ static ConstString g_prev("__prev_");
+
+ if (!m_entry_sp)
+ return ListEntry();
+ return ListEntry(m_entry_sp->GetChildMemberWithName(g_prev, true));
+ }
+
+ uint64_t value() const {
+ if (!m_entry_sp)
+ return 0;
+ return m_entry_sp->GetValueAsUnsigned(0);
+ }
+
+ bool null() { return (value() == 0); }
+
+ explicit operator bool() { return GetEntry() && !null(); }
+
+ ValueObjectSP GetEntry() { return m_entry_sp; }
+
+ void SetEntry(ValueObjectSP entry) { m_entry_sp = entry; }
+
+ bool operator==(const ListEntry &rhs) const { return value() == rhs.value(); }
+
+ bool operator!=(const ListEntry &rhs) const { return !(*this == rhs); }
+
+private:
+ ValueObjectSP m_entry_sp;
+};
+
+class ListIterator {
+public:
+ ListIterator() = default;
+ ListIterator(ListEntry entry) : m_entry(entry) {}
+ ListIterator(ValueObjectSP entry) : m_entry(entry) {}
+ ListIterator(const ListIterator &rhs) = default;
+ ListIterator(ValueObject *entry) : m_entry(entry) {}
+
+ ValueObjectSP value() { return m_entry.GetEntry(); }
+
+ ValueObjectSP advance(size_t count) {
+ if (count == 0)
+ return m_entry.GetEntry();
+ if (count == 1) {
+ next();
+ return m_entry.GetEntry();
+ }
+ while (count > 0) {
+ next();
+ count--;
+ if (m_entry.null())
+ return lldb::ValueObjectSP();
+ }
+ return m_entry.GetEntry();
+ }
+
+ bool operator==(const ListIterator &rhs) const {
+ return (rhs.m_entry == m_entry);
+ }
+
+protected:
+ void next() { m_entry = m_entry.next(); }
+
+ void prev() { m_entry = m_entry.prev(); }
+
+private:
+ ListEntry m_entry;
+};
+
+class AbstractListFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ return ExtractIndexFromString(name.GetCString());
+ }
+ bool MightHaveChildren() override { return true; }
+ bool Update() override;
+
+protected:
+ AbstractListFrontEnd(ValueObject &valobj)
+ : SyntheticChildrenFrontEnd(valobj) {}
+
+ size_t m_count;
+ ValueObject *m_head;
+
+ static constexpr bool g_use_loop_detect = true;
+ size_t m_loop_detected; // The number of elements that have had loop detection
+ // run over them.
+ ListEntry m_slow_runner; // Used for loop detection
+ ListEntry m_fast_runner; // Used for loop detection
+
+ size_t m_list_capping_size;
+ CompilerType m_element_type;
+ std::map<size_t, ListIterator> m_iterators;
+
+ bool HasLoop(size_t count);
+ ValueObjectSP GetItem(size_t idx);
+};
+
+class ForwardListFrontEnd : public AbstractListFrontEnd {
+public:
+ ForwardListFrontEnd(ValueObject &valobj);
+
+ size_t CalculateNumChildren() override;
+ ValueObjectSP GetChildAtIndex(size_t idx) override;
+ bool Update() override;
+};
+
+class ListFrontEnd : public AbstractListFrontEnd {
+public:
+ ListFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~ListFrontEnd() override = default;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+private:
+ lldb::addr_t m_node_address;
+ ValueObject *m_tail;
+};
+
+} // end anonymous namespace
+
+bool AbstractListFrontEnd::Update() {
+ m_loop_detected = 0;
+ m_count = UINT32_MAX;
+ m_head = nullptr;
+ m_list_capping_size = 0;
+ m_slow_runner.SetEntry(nullptr);
+ m_fast_runner.SetEntry(nullptr);
+ m_iterators.clear();
+
+ if (m_backend.GetTargetSP())
+ m_list_capping_size =
+ m_backend.GetTargetSP()->GetMaximumNumberOfChildrenToDisplay();
+ if (m_list_capping_size == 0)
+ m_list_capping_size = 255;
+
+ CompilerType list_type = m_backend.GetCompilerType();
+ if (list_type.IsReferenceType())
+ list_type = list_type.GetNonReferenceType();
+
+ if (list_type.GetNumTemplateArguments() == 0)
+ return false;
+ m_element_type = list_type.GetTypeTemplateArgument(0);
+
+ return false;
+}
+
+bool AbstractListFrontEnd::HasLoop(size_t count) {
+ if (!g_use_loop_detect)
+ return false;
+ // don't bother checking for a loop if we won't actually need to jump nodes
+ if (m_count < 2)
+ return false;
+
+ if (m_loop_detected == 0) {
+ // This is the first time we are being run (after the last update). Set up
+ // the loop invariant for the first element.
+ m_slow_runner = ListEntry(m_head).next();
+ m_fast_runner = m_slow_runner.next();
+ m_loop_detected = 1;
+ }
+
+ // Loop invariant:
+ // Loop detection has been run over the first m_loop_detected elements. If
+ // m_slow_runner == m_fast_runner then the loop has been detected after
+ // m_loop_detected elements.
+ const size_t steps_to_run = std::min(count, m_count);
+ while (m_loop_detected < steps_to_run && m_slow_runner && m_fast_runner &&
+ m_slow_runner != m_fast_runner) {
+
+ m_slow_runner = m_slow_runner.next();
+ m_fast_runner = m_fast_runner.next().next();
+ m_loop_detected++;
+ }
+ if (count <= m_loop_detected)
+ return false; // No loop in the first m_loop_detected elements.
+ if (!m_slow_runner || !m_fast_runner)
+ return false; // Reached the end of the list. Definitely no loops.
+ return m_slow_runner == m_fast_runner;
+}
+
+ValueObjectSP AbstractListFrontEnd::GetItem(size_t idx) {
+ size_t advance = idx;
+ ListIterator current(m_head);
+ if (idx > 0) {
+ auto cached_iterator = m_iterators.find(idx - 1);
+ if (cached_iterator != m_iterators.end()) {
+ current = cached_iterator->second;
+ advance = 1;
+ }
+ }
+ ValueObjectSP value_sp = current.advance(advance);
+ m_iterators[idx] = current;
+ return value_sp;
+}
+
+ForwardListFrontEnd::ForwardListFrontEnd(ValueObject &valobj)
+ : AbstractListFrontEnd(valobj) {
+ Update();
+}
+
+size_t ForwardListFrontEnd::CalculateNumChildren() {
+ if (m_count != UINT32_MAX)
+ return m_count;
+
+ ListEntry current(m_head);
+ m_count = 0;
+ while (current && m_count < m_list_capping_size) {
+ ++m_count;
+ current = current.next();
+ }
+ return m_count;
+}
+
+ValueObjectSP ForwardListFrontEnd::GetChildAtIndex(size_t idx) {
+ if (idx >= CalculateNumChildren())
+ return nullptr;
+
+ if (!m_head)
+ return nullptr;
+
+ if (HasLoop(idx + 1))
+ return nullptr;
+
+ ValueObjectSP current_sp = GetItem(idx);
+ if (!current_sp)
+ return nullptr;
+
+ current_sp = current_sp->GetChildAtIndex(1, true); // get the __value_ child
+ if (!current_sp)
+ return nullptr;
+
+ // we need to copy current_sp into a new object otherwise we will end up with
+ // all items named __value_
+ DataExtractor data;
+ Status error;
+ current_sp->GetData(data, error);
+ if (error.Fail())
+ return nullptr;
+
+ return CreateValueObjectFromData(llvm::formatv("[{0}]", idx).str(), data,
+ m_backend.GetExecutionContextRef(),
+ m_element_type);
+}
+
+static ValueObjectSP GetValueOfCompressedPair(ValueObject &pair) {
+ ValueObjectSP value = pair.GetChildMemberWithName(ConstString("__value_"), true);
+ if (! value) {
+ // pre-r300140 member name
+ value = pair.GetChildMemberWithName(ConstString("__first_"), true);
+ }
+ return value;
+}
+
+bool ForwardListFrontEnd::Update() {
+ AbstractListFrontEnd::Update();
+
+ Status err;
+ ValueObjectSP backend_addr(m_backend.AddressOf(err));
+ if (err.Fail() || !backend_addr)
+ return false;
+
+ ValueObjectSP impl_sp(
+ m_backend.GetChildMemberWithName(ConstString("__before_begin_"), true));
+ if (!impl_sp)
+ return false;
+ impl_sp = GetValueOfCompressedPair(*impl_sp);
+ if (!impl_sp)
+ return false;
+ m_head = impl_sp->GetChildMemberWithName(ConstString("__next_"), true).get();
+ return false;
+}
+
+ListFrontEnd::ListFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : AbstractListFrontEnd(*valobj_sp), m_node_address(), m_tail(nullptr) {
+ if (valobj_sp)
+ Update();
+}
+
+size_t ListFrontEnd::CalculateNumChildren() {
+ if (m_count != UINT32_MAX)
+ return m_count;
+ if (!m_head || !m_tail || m_node_address == 0)
+ return 0;
+ ValueObjectSP size_alloc(
+ m_backend.GetChildMemberWithName(ConstString("__size_alloc_"), true));
+ if (size_alloc) {
+ ValueObjectSP value = GetValueOfCompressedPair(*size_alloc);
+ if (value) {
+ m_count = value->GetValueAsUnsigned(UINT32_MAX);
+ }
+ }
+ if (m_count != UINT32_MAX) {
+ return m_count;
+ } else {
+ uint64_t next_val = m_head->GetValueAsUnsigned(0);
+ uint64_t prev_val = m_tail->GetValueAsUnsigned(0);
+ if (next_val == 0 || prev_val == 0)
+ return 0;
+ if (next_val == m_node_address)
+ return 0;
+ if (next_val == prev_val)
+ return 1;
+ uint64_t size = 2;
+ ListEntry current(m_head);
+ while (current.next() && current.next().value() != m_node_address) {
+ size++;
+ current = current.next();
+ if (size > m_list_capping_size)
+ break;
+ }
+ return m_count = (size - 1);
+ }
+}
+
+lldb::ValueObjectSP ListFrontEnd::GetChildAtIndex(size_t idx) {
+ static ConstString g_value("__value_");
+ static ConstString g_next("__next_");
+
+ if (idx >= CalculateNumChildren())
+ return lldb::ValueObjectSP();
+
+ if (!m_head || !m_tail || m_node_address == 0)
+ return lldb::ValueObjectSP();
+
+ if (HasLoop(idx + 1))
+ return lldb::ValueObjectSP();
+
+ ValueObjectSP current_sp = GetItem(idx);
+ if (!current_sp)
+ return lldb::ValueObjectSP();
+
+ current_sp = current_sp->GetChildAtIndex(1, true); // get the __value_ child
+ if (!current_sp)
+ return lldb::ValueObjectSP();
+
+ if (current_sp->GetName() == g_next) {
+ ProcessSP process_sp(current_sp->GetProcessSP());
+ if (!process_sp)
+ return lldb::ValueObjectSP();
+
+ // if we grabbed the __next_ pointer, then the child is one pointer deep-er
+ lldb::addr_t addr = current_sp->GetParent()->GetPointerValue();
+ addr = addr + 2 * process_sp->GetAddressByteSize();
+ ExecutionContext exe_ctx(process_sp);
+ current_sp =
+ CreateValueObjectFromAddress("__value_", addr, exe_ctx, m_element_type);
+ if (!current_sp)
+ return lldb::ValueObjectSP();
+ }
+
+ // we need to copy current_sp into a new object otherwise we will end up with
+ // all items named __value_
+ DataExtractor data;
+ Status error;
+ current_sp->GetData(data, error);
+ if (error.Fail())
+ return lldb::ValueObjectSP();
+
+ StreamString name;
+ name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ return CreateValueObjectFromData(name.GetString(), data,
+ m_backend.GetExecutionContextRef(),
+ m_element_type);
+}
+
+bool ListFrontEnd::Update() {
+ AbstractListFrontEnd::Update();
+ m_tail = nullptr;
+ m_node_address = 0;
+
+ Status err;
+ ValueObjectSP backend_addr(m_backend.AddressOf(err));
+ if (err.Fail() || !backend_addr)
+ return false;
+ m_node_address = backend_addr->GetValueAsUnsigned(0);
+ if (!m_node_address || m_node_address == LLDB_INVALID_ADDRESS)
+ return false;
+ ValueObjectSP impl_sp(
+ m_backend.GetChildMemberWithName(ConstString("__end_"), true));
+ if (!impl_sp)
+ return false;
+ m_head = impl_sp->GetChildMemberWithName(ConstString("__next_"), true).get();
+ m_tail = impl_sp->GetChildMemberWithName(ConstString("__prev_"), true).get();
+ return false;
+}
+
+SyntheticChildrenFrontEnd *formatters::LibcxxStdListSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return (valobj_sp ? new ListFrontEnd(valobj_sp) : nullptr);
+}
+
+SyntheticChildrenFrontEnd *
+formatters::LibcxxStdForwardListSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return valobj_sp ? new ForwardListFrontEnd(*valobj_sp) : nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
new file mode 100644
index 000000000000..619c718a1c1b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
@@ -0,0 +1,463 @@
+//===-- LibCxxMap.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 "LibCxx.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+class MapEntry {
+public:
+ MapEntry() = default;
+ explicit MapEntry(ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {}
+ MapEntry(const MapEntry &rhs) = default;
+ explicit MapEntry(ValueObject *entry)
+ : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {}
+
+ ValueObjectSP left() const {
+ static ConstString g_left("__left_");
+ if (!m_entry_sp)
+ return m_entry_sp;
+ return m_entry_sp->GetSyntheticChildAtOffset(
+ 0, m_entry_sp->GetCompilerType(), true);
+ }
+
+ ValueObjectSP right() const {
+ static ConstString g_right("__right_");
+ if (!m_entry_sp)
+ return m_entry_sp;
+ return m_entry_sp->GetSyntheticChildAtOffset(
+ m_entry_sp->GetProcessSP()->GetAddressByteSize(),
+ m_entry_sp->GetCompilerType(), true);
+ }
+
+ ValueObjectSP parent() const {
+ static ConstString g_parent("__parent_");
+ if (!m_entry_sp)
+ return m_entry_sp;
+ return m_entry_sp->GetSyntheticChildAtOffset(
+ 2 * m_entry_sp->GetProcessSP()->GetAddressByteSize(),
+ m_entry_sp->GetCompilerType(), true);
+ }
+
+ uint64_t value() const {
+ if (!m_entry_sp)
+ return 0;
+ return m_entry_sp->GetValueAsUnsigned(0);
+ }
+
+ bool error() const {
+ if (!m_entry_sp)
+ return true;
+ return m_entry_sp->GetError().Fail();
+ }
+
+ bool null() const { return (value() == 0); }
+
+ ValueObjectSP GetEntry() const { return m_entry_sp; }
+
+ void SetEntry(ValueObjectSP entry) { m_entry_sp = entry; }
+
+ bool operator==(const MapEntry &rhs) const {
+ return (rhs.m_entry_sp.get() == m_entry_sp.get());
+ }
+
+private:
+ ValueObjectSP m_entry_sp;
+};
+
+class MapIterator {
+public:
+ MapIterator() = default;
+ MapIterator(MapEntry entry, size_t depth = 0)
+ : m_entry(entry), m_max_depth(depth), m_error(false) {}
+ MapIterator(ValueObjectSP entry, size_t depth = 0)
+ : m_entry(entry), m_max_depth(depth), m_error(false) {}
+ MapIterator(const MapIterator &rhs)
+ : m_entry(rhs.m_entry), m_max_depth(rhs.m_max_depth), m_error(false) {}
+ MapIterator(ValueObject *entry, size_t depth = 0)
+ : m_entry(entry), m_max_depth(depth), m_error(false) {}
+
+ ValueObjectSP value() { return m_entry.GetEntry(); }
+
+ ValueObjectSP advance(size_t count) {
+ ValueObjectSP fail;
+ if (m_error)
+ return fail;
+ size_t steps = 0;
+ while (count > 0) {
+ next();
+ count--, steps++;
+ if (m_error || m_entry.null() || (steps > m_max_depth))
+ return fail;
+ }
+ return m_entry.GetEntry();
+ }
+
+protected:
+ void next() {
+ if (m_entry.null())
+ return;
+ MapEntry right(m_entry.right());
+ if (!right.null()) {
+ m_entry = tree_min(std::move(right));
+ return;
+ }
+ size_t steps = 0;
+ while (!is_left_child(m_entry)) {
+ if (m_entry.error()) {
+ m_error = true;
+ return;
+ }
+ m_entry.SetEntry(m_entry.parent());
+ steps++;
+ if (steps > m_max_depth) {
+ m_entry = MapEntry();
+ return;
+ }
+ }
+ m_entry = MapEntry(m_entry.parent());
+ }
+
+private:
+ MapEntry tree_min(MapEntry &&x) {
+ if (x.null())
+ return MapEntry();
+ MapEntry left(x.left());
+ size_t steps = 0;
+ while (!left.null()) {
+ if (left.error()) {
+ m_error = true;
+ return MapEntry();
+ }
+ x = left;
+ left.SetEntry(x.left());
+ steps++;
+ if (steps > m_max_depth)
+ return MapEntry();
+ }
+ return x;
+ }
+
+ bool is_left_child(const MapEntry &x) {
+ if (x.null())
+ return false;
+ MapEntry rhs(x.parent());
+ rhs.SetEntry(rhs.left());
+ return x.value() == rhs.value();
+ }
+
+ MapEntry m_entry;
+ size_t m_max_depth;
+ bool m_error;
+};
+
+namespace lldb_private {
+namespace formatters {
+class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ LibcxxStdMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~LibcxxStdMapSyntheticFrontEnd() override = default;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ bool GetDataType();
+
+ void GetValueOffset(const lldb::ValueObjectSP &node);
+
+ ValueObject *m_tree;
+ ValueObject *m_root_node;
+ CompilerType m_element_type;
+ uint32_t m_skip_size;
+ size_t m_count;
+ std::map<size_t, MapIterator> m_iterators;
+};
+} // namespace formatters
+} // namespace lldb_private
+
+lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
+ LibcxxStdMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_tree(nullptr),
+ m_root_node(nullptr), m_element_type(), m_skip_size(UINT32_MAX),
+ m_count(UINT32_MAX), m_iterators() {
+ if (valobj_sp)
+ Update();
+}
+
+size_t lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
+ CalculateNumChildren() {
+ static ConstString g___pair3_("__pair3_");
+ static ConstString g___first_("__first_");
+ static ConstString g___value_("__value_");
+
+ if (m_count != UINT32_MAX)
+ return m_count;
+ if (m_tree == nullptr)
+ return 0;
+ ValueObjectSP m_item(m_tree->GetChildMemberWithName(g___pair3_, true));
+ if (!m_item)
+ return 0;
+
+ switch (m_item->GetCompilerType().GetNumDirectBaseClasses()) {
+ case 1:
+ // Assume a pre llvm r300140 __compressed_pair implementation:
+ m_item = m_item->GetChildMemberWithName(g___first_, true);
+ break;
+ case 2: {
+ // Assume a post llvm r300140 __compressed_pair implementation:
+ ValueObjectSP first_elem_parent = m_item->GetChildAtIndex(0, true);
+ m_item = first_elem_parent->GetChildMemberWithName(g___value_, true);
+ break;
+ }
+ default:
+ return false;
+ }
+
+ if (!m_item)
+ return 0;
+ m_count = m_item->GetValueAsUnsigned(0);
+ return m_count;
+}
+
+bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetDataType() {
+ static ConstString g___value_("__value_");
+ static ConstString g_tree_("__tree_");
+ static ConstString g_pair3("__pair3_");
+
+ if (m_element_type.GetOpaqueQualType() && m_element_type.GetTypeSystem())
+ return true;
+ m_element_type.Clear();
+ ValueObjectSP deref;
+ Status error;
+ deref = m_root_node->Dereference(error);
+ if (!deref || error.Fail())
+ return false;
+ deref = deref->GetChildMemberWithName(g___value_, true);
+ if (deref) {
+ m_element_type = deref->GetCompilerType();
+ return true;
+ }
+ deref = m_backend.GetChildAtNamePath({g_tree_, g_pair3});
+ if (!deref)
+ return false;
+ m_element_type = deref->GetCompilerType()
+ .GetTypeTemplateArgument(1)
+ .GetTypeTemplateArgument(1);
+ if (m_element_type) {
+ std::string name;
+ uint64_t bit_offset_ptr;
+ uint32_t bitfield_bit_size_ptr;
+ bool is_bitfield_ptr;
+ m_element_type = m_element_type.GetFieldAtIndex(
+ 0, name, &bit_offset_ptr, &bitfield_bit_size_ptr, &is_bitfield_ptr);
+ m_element_type = m_element_type.GetTypedefedType();
+ return m_element_type.IsValid();
+ } else {
+ m_element_type = m_backend.GetCompilerType().GetTypeTemplateArgument(0);
+ return m_element_type.IsValid();
+ }
+}
+
+void lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetValueOffset(
+ const lldb::ValueObjectSP &node) {
+ if (m_skip_size != UINT32_MAX)
+ return;
+ if (!node)
+ return;
+ CompilerType node_type(node->GetCompilerType());
+ uint64_t bit_offset;
+ if (node_type.GetIndexOfFieldWithName("__value_", nullptr, &bit_offset) !=
+ UINT32_MAX) {
+ m_skip_size = bit_offset / 8u;
+ } else {
+ ClangASTContext *ast_ctx =
+ llvm::dyn_cast_or_null<ClangASTContext>(node_type.GetTypeSystem());
+ if (!ast_ctx)
+ return;
+ CompilerType tree_node_type = ast_ctx->CreateStructForIdentifier(
+ ConstString(),
+ {{"ptr0", ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
+ {"ptr1", ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
+ {"ptr2", ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
+ {"cw", ast_ctx->GetBasicType(lldb::eBasicTypeBool)},
+ {"payload", (m_element_type.GetCompleteType(), m_element_type)}});
+ std::string child_name;
+ uint32_t child_byte_size;
+ int32_t child_byte_offset = 0;
+ uint32_t child_bitfield_bit_size;
+ uint32_t child_bitfield_bit_offset;
+ bool child_is_base_class;
+ bool child_is_deref_of_parent;
+ uint64_t language_flags;
+ if (tree_node_type
+ .GetChildCompilerTypeAtIndex(
+ nullptr, 4, true, true, true, child_name, child_byte_size,
+ child_byte_offset, child_bitfield_bit_size,
+ child_bitfield_bit_offset, child_is_base_class,
+ child_is_deref_of_parent, nullptr, language_flags)
+ .IsValid())
+ m_skip_size = (uint32_t)child_byte_offset;
+ }
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetChildAtIndex(
+ size_t idx) {
+ static ConstString g___cc("__cc");
+ static ConstString g___nc("__nc");
+ static ConstString g___value_("__value_");
+
+ if (idx >= CalculateNumChildren())
+ return lldb::ValueObjectSP();
+ if (m_tree == nullptr || m_root_node == nullptr)
+ return lldb::ValueObjectSP();
+
+ MapIterator iterator(m_root_node, CalculateNumChildren());
+
+ const bool need_to_skip = (idx > 0);
+ size_t actual_advancde = idx;
+ if (need_to_skip) {
+ auto cached_iterator = m_iterators.find(idx - 1);
+ if (cached_iterator != m_iterators.end()) {
+ iterator = cached_iterator->second;
+ actual_advancde = 1;
+ }
+ }
+
+ ValueObjectSP iterated_sp(iterator.advance(actual_advancde));
+ if (!iterated_sp) {
+ // this tree is garbage - stop
+ m_tree =
+ nullptr; // this will stop all future searches until an Update() happens
+ return iterated_sp;
+ }
+ if (GetDataType()) {
+ if (!need_to_skip) {
+ Status error;
+ iterated_sp = iterated_sp->Dereference(error);
+ if (!iterated_sp || error.Fail()) {
+ m_tree = nullptr;
+ return lldb::ValueObjectSP();
+ }
+ GetValueOffset(iterated_sp);
+ auto child_sp = iterated_sp->GetChildMemberWithName(g___value_, true);
+ if (child_sp)
+ iterated_sp = child_sp;
+ else
+ iterated_sp = iterated_sp->GetSyntheticChildAtOffset(
+ m_skip_size, m_element_type, true);
+ if (!iterated_sp) {
+ m_tree = nullptr;
+ return lldb::ValueObjectSP();
+ }
+ } else {
+ // because of the way our debug info is made, we need to read item 0
+ // first so that we can cache information used to generate other elements
+ if (m_skip_size == UINT32_MAX)
+ GetChildAtIndex(0);
+ if (m_skip_size == UINT32_MAX) {
+ m_tree = nullptr;
+ return lldb::ValueObjectSP();
+ }
+ iterated_sp = iterated_sp->GetSyntheticChildAtOffset(
+ m_skip_size, m_element_type, true);
+ if (!iterated_sp) {
+ m_tree = nullptr;
+ return lldb::ValueObjectSP();
+ }
+ }
+ } else {
+ m_tree = nullptr;
+ return lldb::ValueObjectSP();
+ }
+ // at this point we have a valid
+ // we need to copy current_sp into a new object otherwise we will end up with
+ // all items named __value_
+ DataExtractor data;
+ Status error;
+ iterated_sp->GetData(data, error);
+ if (error.Fail()) {
+ m_tree = nullptr;
+ return lldb::ValueObjectSP();
+ }
+ StreamString name;
+ name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ auto potential_child_sp = CreateValueObjectFromData(
+ name.GetString(), data, m_backend.GetExecutionContextRef(),
+ m_element_type);
+ if (potential_child_sp) {
+ switch (potential_child_sp->GetNumChildren()) {
+ case 1: {
+ auto child0_sp = potential_child_sp->GetChildAtIndex(0, true);
+ if (child0_sp && child0_sp->GetName() == g___cc)
+ potential_child_sp = child0_sp->Clone(ConstString(name.GetString()));
+ break;
+ }
+ case 2: {
+ auto child0_sp = potential_child_sp->GetChildAtIndex(0, true);
+ auto child1_sp = potential_child_sp->GetChildAtIndex(1, true);
+ if (child0_sp && child0_sp->GetName() == g___cc && child1_sp &&
+ child1_sp->GetName() == g___nc)
+ potential_child_sp = child0_sp->Clone(ConstString(name.GetString()));
+ break;
+ }
+ }
+ }
+ m_iterators[idx] = iterator;
+ return potential_child_sp;
+}
+
+bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() {
+ static ConstString g___tree_("__tree_");
+ static ConstString g___begin_node_("__begin_node_");
+ m_count = UINT32_MAX;
+ m_tree = m_root_node = nullptr;
+ m_iterators.clear();
+ m_tree = m_backend.GetChildMemberWithName(g___tree_, true).get();
+ if (!m_tree)
+ return false;
+ m_root_node = m_tree->GetChildMemberWithName(g___begin_node_, true).get();
+ return false;
+}
+
+bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
+ MightHaveChildren() {
+ return true;
+}
+
+size_t lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
+ GetIndexOfChildWithName(ConstString name) {
+ return ExtractIndexFromString(name.GetCString());
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return (valobj_sp ? new LibcxxStdMapSyntheticFrontEnd(valobj_sp) : nullptr);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxOptional.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxOptional.cpp
new file mode 100644
index 000000000000..116021588848
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxOptional.cpp
@@ -0,0 +1,84 @@
+//===-- LibCxxOptional.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 "LibCxx.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+
+class OptionalFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ OptionalFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {
+ Update();
+ }
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ return formatters::ExtractIndexFromString(name.GetCString());
+ }
+
+ bool MightHaveChildren() override { return true; }
+ bool Update() override;
+ size_t CalculateNumChildren() override { return m_size; }
+ ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+private:
+ size_t m_size = 0;
+ ValueObjectSP m_base_sp;
+};
+} // namespace
+
+bool OptionalFrontEnd::Update() {
+ ValueObjectSP engaged_sp(
+ m_backend.GetChildMemberWithName(ConstString("__engaged_"), true));
+
+ if (!engaged_sp)
+ return false;
+
+ // __engaged_ is a bool flag and is true if the optional contains a value.
+ // Converting it to unsigned gives us a size of 1 if it contains a value
+ // and 0 if not.
+ m_size = engaged_sp->GetValueAsUnsigned(0);
+
+ return false;
+}
+
+ValueObjectSP OptionalFrontEnd::GetChildAtIndex(size_t idx) {
+ if (idx >= m_size)
+ return ValueObjectSP();
+
+ // __val_ contains the underlying value of an optional if it has one.
+ // Currently because it is part of an anonymous union GetChildMemberWithName()
+ // does not peer through and find it unless we are at the parent itself.
+ // We can obtain the parent through __engaged_.
+ ValueObjectSP val_sp(
+ m_backend.GetChildMemberWithName(ConstString("__engaged_"), true)
+ ->GetParent()
+ ->GetChildAtIndex(0, true)
+ ->GetChildMemberWithName(ConstString("__val_"), true));
+
+ if (!val_sp)
+ return ValueObjectSP();
+
+ CompilerType holder_type = val_sp->GetCompilerType();
+
+ if (!holder_type)
+ return ValueObjectSP();
+
+ return val_sp->Clone(ConstString(llvm::formatv("Value").str()));
+}
+
+SyntheticChildrenFrontEnd *
+formatters::LibcxxOptionalFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP valobj_sp) {
+ if (valobj_sp)
+ return new OptionalFrontEnd(*valobj_sp);
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxQueue.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxQueue.cpp
new file mode 100644
index 000000000000..4b72089c6ba2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxQueue.cpp
@@ -0,0 +1,60 @@
+//===-- LibCxxQueue.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 "LibCxx.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+
+class QueueFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ QueueFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {
+ Update();
+ }
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ return m_container_sp ? m_container_sp->GetIndexOfChildWithName(name)
+ : UINT32_MAX;
+ }
+
+ bool MightHaveChildren() override { return true; }
+ bool Update() override;
+
+ size_t CalculateNumChildren() override {
+ return m_container_sp ? m_container_sp->GetNumChildren() : 0;
+ }
+
+ ValueObjectSP GetChildAtIndex(size_t idx) override {
+ return m_container_sp ? m_container_sp->GetChildAtIndex(idx, true)
+ : nullptr;
+ }
+
+private:
+ ValueObjectSP m_container_sp;
+};
+} // namespace
+
+bool QueueFrontEnd::Update() {
+ m_container_sp.reset();
+ ValueObjectSP c_sp = m_backend.GetChildMemberWithName(ConstString("c"), true);
+ if (!c_sp)
+ return false;
+ m_container_sp = c_sp->GetSyntheticValue();
+ return false;
+}
+
+SyntheticChildrenFrontEnd *
+formatters::LibcxxQueueFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP valobj_sp) {
+ if (valobj_sp)
+ return new QueueFrontEnd(*valobj_sp);
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp
new file mode 100644
index 000000000000..8da7460f2275
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp
@@ -0,0 +1,82 @@
+//===-- LibCxxTuple.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 "LibCxx.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+
+class TupleFrontEnd: public SyntheticChildrenFrontEnd {
+public:
+ TupleFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {
+ Update();
+ }
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ return formatters::ExtractIndexFromString(name.GetCString());
+ }
+
+ bool MightHaveChildren() override { return true; }
+ bool Update() override;
+ size_t CalculateNumChildren() override { return m_elements.size(); }
+ ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+private:
+ std::vector<ValueObjectSP> m_elements;
+ ValueObjectSP m_base_sp;
+};
+}
+
+bool TupleFrontEnd::Update() {
+ m_elements.clear();
+ m_base_sp = m_backend.GetChildMemberWithName(ConstString("__base_"), true);
+ if (! m_base_sp) {
+ // Pre r304382 name of the base element.
+ m_base_sp = m_backend.GetChildMemberWithName(ConstString("base_"), true);
+ }
+ if (! m_base_sp)
+ return false;
+ m_elements.assign(m_base_sp->GetCompilerType().GetNumDirectBaseClasses(),
+ ValueObjectSP());
+ return false;
+}
+
+ValueObjectSP TupleFrontEnd::GetChildAtIndex(size_t idx) {
+ if (idx >= m_elements.size())
+ return ValueObjectSP();
+ if (!m_base_sp)
+ return ValueObjectSP();
+ if (m_elements[idx])
+ return m_elements[idx];
+
+ CompilerType holder_type =
+ m_base_sp->GetCompilerType().GetDirectBaseClassAtIndex(idx, nullptr);
+ if (!holder_type)
+ return ValueObjectSP();
+ ValueObjectSP holder_sp = m_base_sp->GetChildAtIndex(idx, true);
+ if (!holder_sp)
+ return ValueObjectSP();
+
+ ValueObjectSP elem_sp = holder_sp->GetChildAtIndex(0, true);
+ if (elem_sp)
+ m_elements[idx] =
+ elem_sp->Clone(ConstString(llvm::formatv("[{0}]", idx).str()));
+
+ return m_elements[idx];
+}
+
+SyntheticChildrenFrontEnd *
+formatters::LibcxxTupleFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP valobj_sp) {
+ if (valobj_sp)
+ return new TupleFrontEnd(*valobj_sp);
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
new file mode 100644
index 000000000000..b2c38c915c81
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
@@ -0,0 +1,221 @@
+//===-- LibCxxUnorderedMap.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 "LibCxx.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+namespace lldb_private {
+namespace formatters {
+class LibcxxStdUnorderedMapSyntheticFrontEnd
+ : public SyntheticChildrenFrontEnd {
+public:
+ LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~LibcxxStdUnorderedMapSyntheticFrontEnd() override = default;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ CompilerType m_element_type;
+ CompilerType m_node_type;
+ ValueObject *m_tree;
+ size_t m_num_elements;
+ ValueObject *m_next_element;
+ std::vector<std::pair<ValueObject *, uint64_t>> m_elements_cache;
+};
+} // namespace formatters
+} // namespace lldb_private
+
+lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
+ LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type(), m_tree(nullptr),
+ m_num_elements(0), m_next_element(nullptr), m_elements_cache() {
+ if (valobj_sp)
+ Update();
+}
+
+size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
+ CalculateNumChildren() {
+ if (m_num_elements != UINT32_MAX)
+ return m_num_elements;
+ return 0;
+}
+
+lldb::ValueObjectSP lldb_private::formatters::
+ LibcxxStdUnorderedMapSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
+ if (idx >= CalculateNumChildren())
+ return lldb::ValueObjectSP();
+ if (m_tree == nullptr)
+ return lldb::ValueObjectSP();
+
+ while (idx >= m_elements_cache.size()) {
+ if (m_next_element == nullptr)
+ return lldb::ValueObjectSP();
+
+ Status error;
+ ValueObjectSP node_sp = m_next_element->Dereference(error);
+ if (!node_sp || error.Fail())
+ return lldb::ValueObjectSP();
+
+ ValueObjectSP value_sp =
+ node_sp->GetChildMemberWithName(ConstString("__value_"), true);
+ ValueObjectSP hash_sp =
+ node_sp->GetChildMemberWithName(ConstString("__hash_"), true);
+ if (!hash_sp || !value_sp) {
+ if (!m_element_type) {
+ auto p1_sp = m_backend.GetChildAtNamePath({ConstString("__table_"),
+ ConstString("__p1_")});
+ if (!p1_sp)
+ return nullptr;
+
+ ValueObjectSP first_sp = nullptr;
+ switch (p1_sp->GetCompilerType().GetNumDirectBaseClasses()) {
+ case 1:
+ // Assume a pre llvm r300140 __compressed_pair implementation:
+ first_sp = p1_sp->GetChildMemberWithName(ConstString("__first_"),
+ true);
+ break;
+ case 2: {
+ // Assume a post llvm r300140 __compressed_pair implementation:
+ ValueObjectSP first_elem_parent_sp =
+ p1_sp->GetChildAtIndex(0, true);
+ first_sp = p1_sp->GetChildMemberWithName(ConstString("__value_"),
+ true);
+ break;
+ }
+ default:
+ return nullptr;
+ }
+
+ if (!first_sp)
+ return nullptr;
+ m_element_type = first_sp->GetCompilerType();
+ m_element_type = m_element_type.GetTypeTemplateArgument(0);
+ m_element_type = m_element_type.GetPointeeType();
+ m_node_type = m_element_type;
+ m_element_type = m_element_type.GetTypeTemplateArgument(0);
+ std::string name;
+ m_element_type =
+ m_element_type.GetFieldAtIndex(0, name, nullptr, nullptr, nullptr);
+ m_element_type = m_element_type.GetTypedefedType();
+ }
+ if (!m_node_type)
+ return nullptr;
+ node_sp = node_sp->Cast(m_node_type);
+ value_sp = node_sp->GetChildMemberWithName(ConstString("__value_"), true);
+ hash_sp = node_sp->GetChildMemberWithName(ConstString("__hash_"), true);
+ if (!value_sp || !hash_sp)
+ return nullptr;
+ }
+ m_elements_cache.push_back(
+ {value_sp.get(), hash_sp->GetValueAsUnsigned(0)});
+ m_next_element =
+ node_sp->GetChildMemberWithName(ConstString("__next_"), true).get();
+ if (!m_next_element || m_next_element->GetValueAsUnsigned(0) == 0)
+ m_next_element = nullptr;
+ }
+
+ std::pair<ValueObject *, uint64_t> val_hash = m_elements_cache[idx];
+ if (!val_hash.first)
+ return lldb::ValueObjectSP();
+ StreamString stream;
+ stream.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ DataExtractor data;
+ Status error;
+ val_hash.first->GetData(data, error);
+ if (error.Fail())
+ return lldb::ValueObjectSP();
+ const bool thread_and_frame_only_if_stopped = true;
+ ExecutionContext exe_ctx = val_hash.first->GetExecutionContextRef().Lock(
+ thread_and_frame_only_if_stopped);
+ return CreateValueObjectFromData(stream.GetString(), data, exe_ctx,
+ val_hash.first->GetCompilerType());
+}
+
+bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
+ Update() {
+ m_num_elements = UINT32_MAX;
+ m_next_element = nullptr;
+ m_elements_cache.clear();
+ ValueObjectSP table_sp =
+ m_backend.GetChildMemberWithName(ConstString("__table_"), true);
+ if (!table_sp)
+ return false;
+
+ ValueObjectSP p2_sp = table_sp->GetChildMemberWithName(
+ ConstString("__p2_"), true);
+ ValueObjectSP num_elements_sp = nullptr;
+ llvm::SmallVector<ConstString, 3> next_path;
+ switch (p2_sp->GetCompilerType().GetNumDirectBaseClasses()) {
+ case 1:
+ // Assume a pre llvm r300140 __compressed_pair implementation:
+ num_elements_sp = p2_sp->GetChildMemberWithName(
+ ConstString("__first_"), true);
+ next_path.append({ConstString("__p1_"), ConstString("__first_"),
+ ConstString("__next_")});
+ break;
+ case 2: {
+ // Assume a post llvm r300140 __compressed_pair implementation:
+ ValueObjectSP first_elem_parent = p2_sp->GetChildAtIndex(0, true);
+ num_elements_sp = first_elem_parent->GetChildMemberWithName(
+ ConstString("__value_"), true);
+ next_path.append({ConstString("__p1_"), ConstString("__value_"),
+ ConstString("__next_")});
+ break;
+ }
+ default:
+ return false;
+ }
+
+ if (!num_elements_sp)
+ return false;
+ m_num_elements = num_elements_sp->GetValueAsUnsigned(0);
+ m_tree = table_sp->GetChildAtNamePath(next_path).get();
+ if (m_num_elements > 0)
+ m_next_element =
+ table_sp->GetChildAtNamePath(next_path).get();
+ return false;
+}
+
+bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
+ MightHaveChildren() {
+ return true;
+}
+
+size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
+ GetIndexOfChildWithName(ConstString name) {
+ return ExtractIndexFromString(name.GetCString());
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return (valobj_sp ? new LibcxxStdUnorderedMapSyntheticFrontEnd(valobj_sp)
+ : nullptr);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp
new file mode 100644
index 000000000000..491cf048e459
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp
@@ -0,0 +1,255 @@
+//===-- LibCxxVariant.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 "LibCxxVariant.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/ScopeExit.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// libc++ variant implementation contains two members that we care about both
+// are contained in the __impl member.
+// - __index which tells us which of the variadic template types is the active
+// type for the variant
+// - __data is a variadic union which recursively contains itself as member
+// which refers to the tailing variadic types.
+// - __head which refers to the leading non pack type
+// - __value refers to the actual value contained
+// - __tail which refers to the remaining pack types
+//
+// e.g. given std::variant<int,double,char> v1
+//
+// (lldb) frame var -R v1.__impl.__data
+//(... __union<... 0, int, double, char>) v1.__impl.__data = {
+// ...
+// __head = {
+// __value = ...
+// }
+// __tail = {
+// ...
+// __head = {
+// __value = ...
+// }
+// __tail = {
+// ...
+// __head = {
+// __value = ...
+// ...
+//
+// So given
+// - __index equal to 0 the active value is contained in
+//
+// __data.__head.__value
+//
+// - __index equal to 1 the active value is contained in
+//
+// __data.__tail.__head.__value
+//
+// - __index equal to 2 the active value is contained in
+//
+// __data.__tail.__tail.__head.__value
+//
+
+namespace {
+// libc++ std::variant index could have one of three states
+// 1) VALID, we can obtain it and its not variant_npos
+// 2) INVALID, we can't obtain it or it is not a type we expect
+// 3) NPOS, its value is variant_npos which means the variant has no value
+enum class LibcxxVariantIndexValidity { VALID, INVALID, NPOS };
+
+LibcxxVariantIndexValidity
+LibcxxVariantGetIndexValidity(ValueObjectSP &impl_sp) {
+ ValueObjectSP index_sp(
+ impl_sp->GetChildMemberWithName(ConstString("__index"), true));
+
+ if (!index_sp)
+ return LibcxxVariantIndexValidity::INVALID;
+
+ int64_t index_value = index_sp->GetValueAsSigned(0);
+
+ if (index_value == -1)
+ return LibcxxVariantIndexValidity::NPOS;
+
+ return LibcxxVariantIndexValidity::VALID;
+}
+
+llvm::Optional<uint64_t> LibcxxVariantIndexValue(ValueObjectSP &impl_sp) {
+ ValueObjectSP index_sp(
+ impl_sp->GetChildMemberWithName(ConstString("__index"), true));
+
+ if (!index_sp)
+ return {};
+
+ return {index_sp->GetValueAsUnsigned(0)};
+}
+
+ValueObjectSP LibcxxVariantGetNthHead(ValueObjectSP &impl_sp, uint64_t index) {
+ ValueObjectSP data_sp(
+ impl_sp->GetChildMemberWithName(ConstString("__data"), true));
+
+ if (!data_sp)
+ return ValueObjectSP{};
+
+ ValueObjectSP current_level = data_sp;
+ for (uint64_t n = index; n != 0; --n) {
+ ValueObjectSP tail_sp(
+ current_level->GetChildMemberWithName(ConstString("__tail"), true));
+
+ if (!tail_sp)
+ return ValueObjectSP{};
+
+ current_level = tail_sp;
+ }
+
+ return current_level->GetChildMemberWithName(ConstString("__head"), true);
+}
+} // namespace
+
+namespace lldb_private {
+namespace formatters {
+bool LibcxxVariantSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options) {
+ ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
+ if (!valobj_sp)
+ return false;
+
+ ValueObjectSP impl_sp(
+ valobj_sp->GetChildMemberWithName(ConstString("__impl"), true));
+
+ if (!impl_sp)
+ return false;
+
+ LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp);
+
+ if (validity == LibcxxVariantIndexValidity::INVALID)
+ return false;
+
+ if (validity == LibcxxVariantIndexValidity::NPOS) {
+ stream.Printf(" No Value");
+ return true;
+ }
+
+ auto optional_index_value = LibcxxVariantIndexValue(impl_sp);
+
+ if (!optional_index_value)
+ return false;
+
+ uint64_t index_value = *optional_index_value;
+
+ ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value);
+
+ if (!nth_head)
+ return false;
+
+ CompilerType head_type = nth_head->GetCompilerType();
+
+ if (!head_type)
+ return false;
+
+ CompilerType template_type = head_type.GetTypeTemplateArgument(1);
+
+ if (!template_type)
+ return false;
+
+ stream.Printf(" Active Type = %s ", template_type.GetTypeName().GetCString());
+
+ return true;
+}
+} // namespace formatters
+} // namespace lldb_private
+
+namespace {
+class VariantFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {
+ Update();
+ }
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ return formatters::ExtractIndexFromString(name.GetCString());
+ }
+
+ bool MightHaveChildren() override { return true; }
+ bool Update() override;
+ size_t CalculateNumChildren() override { return m_size; }
+ ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+private:
+ size_t m_size = 0;
+ ValueObjectSP m_base_sp;
+};
+} // namespace
+
+bool VariantFrontEnd::Update() {
+ m_size = 0;
+ ValueObjectSP impl_sp(
+ m_backend.GetChildMemberWithName(ConstString("__impl"), true));
+ if (!impl_sp)
+ return false;
+
+ LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp);
+
+ if (validity == LibcxxVariantIndexValidity::INVALID)
+ return false;
+
+ if (validity == LibcxxVariantIndexValidity::NPOS)
+ return true;
+
+ m_size = 1;
+
+ return false;
+}
+
+ValueObjectSP VariantFrontEnd::GetChildAtIndex(size_t idx) {
+ if (idx >= m_size)
+ return ValueObjectSP();
+
+ ValueObjectSP impl_sp(
+ m_backend.GetChildMemberWithName(ConstString("__impl"), true));
+
+ auto optional_index_value = LibcxxVariantIndexValue(impl_sp);
+
+ if (!optional_index_value)
+ return ValueObjectSP();
+
+ uint64_t index_value = *optional_index_value;
+
+ ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value);
+
+ if (!nth_head)
+ return ValueObjectSP();
+
+ CompilerType head_type = nth_head->GetCompilerType();
+
+ if (!head_type)
+ return ValueObjectSP();
+
+ CompilerType template_type = head_type.GetTypeTemplateArgument(1);
+
+ if (!template_type)
+ return ValueObjectSP();
+
+ ValueObjectSP head_value(
+ nth_head->GetChildMemberWithName(ConstString("__value"), true));
+
+ if (!head_value)
+ return ValueObjectSP();
+
+ return head_value->Clone(ConstString(ConstString("Value").AsCString()));
+}
+
+SyntheticChildrenFrontEnd *
+formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP valobj_sp) {
+ if (valobj_sp)
+ return new VariantFrontEnd(*valobj_sp);
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.h
new file mode 100644
index 000000000000..65db5aeaa99d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.h
@@ -0,0 +1,30 @@
+//===-- LibCxxVariant.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 liblldb_LibCxxVariant_h_
+#define liblldb_LibCxxVariant_h_
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Utility/Stream.h"
+
+namespace lldb_private {
+namespace formatters {
+bool LibcxxVariantSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // libc++ std::variant<>
+
+SyntheticChildrenFrontEnd *LibcxxVariantFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // liblldb_LibCxxVariant_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp
new file mode 100644
index 000000000000..bcd7442bc669
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp
@@ -0,0 +1,296 @@
+//===-- LibCxxVector.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 "LibCxx.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Utility/ConstString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+namespace lldb_private {
+namespace formatters {
+class LibcxxStdVectorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ LibcxxStdVectorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~LibcxxStdVectorSyntheticFrontEnd() override;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ ValueObject *m_start;
+ ValueObject *m_finish;
+ CompilerType m_element_type;
+ uint32_t m_element_size;
+};
+
+class LibcxxVectorBoolSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ LibcxxVectorBoolSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override { return true; }
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ CompilerType m_bool_type;
+ ExecutionContextRef m_exe_ctx_ref;
+ uint64_t m_count;
+ lldb::addr_t m_base_data_address;
+ std::map<size_t, lldb::ValueObjectSP> m_children;
+};
+
+} // namespace formatters
+} // namespace lldb_private
+
+lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::
+ LibcxxStdVectorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_start(nullptr),
+ m_finish(nullptr), m_element_type(), m_element_size(0) {
+ if (valobj_sp)
+ Update();
+}
+
+lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::
+ ~LibcxxStdVectorSyntheticFrontEnd() {
+ // these need to stay around because they are child objects who will follow
+ // their parent's life cycle
+ // delete m_start;
+ // delete m_finish;
+}
+
+size_t lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::
+ CalculateNumChildren() {
+ if (!m_start || !m_finish)
+ return 0;
+ uint64_t start_val = m_start->GetValueAsUnsigned(0);
+ uint64_t finish_val = m_finish->GetValueAsUnsigned(0);
+
+ if (start_val == 0 || finish_val == 0)
+ return 0;
+
+ if (start_val >= finish_val)
+ return 0;
+
+ size_t num_children = (finish_val - start_val);
+ if (num_children % m_element_size)
+ return 0;
+ return num_children / m_element_size;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::GetChildAtIndex(
+ size_t idx) {
+ if (!m_start || !m_finish)
+ return lldb::ValueObjectSP();
+
+ uint64_t offset = idx * m_element_size;
+ offset = offset + m_start->GetValueAsUnsigned(0);
+ StreamString name;
+ name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ return CreateValueObjectFromAddress(name.GetString(), offset,
+ m_backend.GetExecutionContextRef(),
+ m_element_type);
+}
+
+bool lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::Update() {
+ m_start = m_finish = nullptr;
+ ValueObjectSP data_type_finder_sp(
+ m_backend.GetChildMemberWithName(ConstString("__end_cap_"), true));
+ if (!data_type_finder_sp)
+ return false;
+
+ switch (data_type_finder_sp->GetCompilerType().GetNumDirectBaseClasses()) {
+ case 1:
+ // Assume a pre llvm r300140 __compressed_pair implementation:
+ data_type_finder_sp = data_type_finder_sp->GetChildMemberWithName(
+ ConstString("__first_"), true);
+ break;
+ case 2: {
+ // Assume a post llvm r300140 __compressed_pair implementation:
+ ValueObjectSP first_elem_parent_sp =
+ data_type_finder_sp->GetChildAtIndex(0, true);
+ data_type_finder_sp = first_elem_parent_sp->GetChildMemberWithName(
+ ConstString("__value_"), true);
+ break;
+ }
+ default:
+ return false;
+ }
+
+ if (!data_type_finder_sp)
+ return false;
+ m_element_type = data_type_finder_sp->GetCompilerType().GetPointeeType();
+ if (llvm::Optional<uint64_t> size = m_element_type.GetByteSize(nullptr)) {
+ m_element_size = *size;
+
+ if (m_element_size > 0) {
+ // store raw pointers or end up with a circular dependency
+ m_start =
+ m_backend.GetChildMemberWithName(ConstString("__begin_"), true).get();
+ m_finish =
+ m_backend.GetChildMemberWithName(ConstString("__end_"), true).get();
+ }
+ }
+ return false;
+}
+
+bool lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::
+ MightHaveChildren() {
+ return true;
+}
+
+size_t lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::
+ GetIndexOfChildWithName(ConstString name) {
+ if (!m_start || !m_finish)
+ return UINT32_MAX;
+ return ExtractIndexFromString(name.GetCString());
+}
+
+lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::
+ LibcxxVectorBoolSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_bool_type(), m_exe_ctx_ref(),
+ m_count(0), m_base_data_address(0), m_children() {
+ if (valobj_sp) {
+ Update();
+ m_bool_type =
+ valobj_sp->GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeBool);
+ }
+}
+
+size_t lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::
+ CalculateNumChildren() {
+ return m_count;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::GetChildAtIndex(
+ size_t idx) {
+ auto iter = m_children.find(idx), end = m_children.end();
+ if (iter != end)
+ return iter->second;
+ if (idx >= m_count)
+ return {};
+ if (m_base_data_address == 0 || m_count == 0)
+ return {};
+ if (!m_bool_type)
+ return {};
+ size_t byte_idx = (idx >> 3); // divide by 8 to get byte index
+ size_t bit_index = (idx & 7); // efficient idx % 8 for bit index
+ lldb::addr_t byte_location = m_base_data_address + byte_idx;
+ ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP());
+ if (!process_sp)
+ return {};
+ uint8_t byte = 0;
+ uint8_t mask = 0;
+ Status err;
+ size_t bytes_read = process_sp->ReadMemory(byte_location, &byte, 1, err);
+ if (err.Fail() || bytes_read == 0)
+ return {};
+ mask = 1 << bit_index;
+ bool bit_set = ((byte & mask) != 0);
+ llvm::Optional<uint64_t> size = m_bool_type.GetByteSize(nullptr);
+ if (!size)
+ return {};
+ DataBufferSP buffer_sp(new DataBufferHeap(*size, 0));
+ if (bit_set && buffer_sp && buffer_sp->GetBytes()) {
+ // regardless of endianness, anything non-zero is true
+ *(buffer_sp->GetBytes()) = 1;
+ }
+ StreamString name;
+ name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ ValueObjectSP retval_sp(CreateValueObjectFromData(
+ name.GetString(),
+ DataExtractor(buffer_sp, process_sp->GetByteOrder(),
+ process_sp->GetAddressByteSize()),
+ m_exe_ctx_ref, m_bool_type));
+ if (retval_sp)
+ m_children[idx] = retval_sp;
+ return retval_sp;
+}
+
+/*(std::__1::vector<std::__1::allocator<bool> >) vBool = {
+ __begin_ = 0x00000001001000e0
+ __size_ = 56
+ __cap_alloc_ = {
+ std::__1::__libcpp_compressed_pair_imp<unsigned long,
+ std::__1::allocator<unsigned long> > = {
+ __first_ = 1
+ }
+ }
+ }*/
+
+bool lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::Update() {
+ m_children.clear();
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ if (!valobj_sp)
+ return false;
+ m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
+ ValueObjectSP size_sp(
+ valobj_sp->GetChildMemberWithName(ConstString("__size_"), true));
+ if (!size_sp)
+ return false;
+ m_count = size_sp->GetValueAsUnsigned(0);
+ if (!m_count)
+ return true;
+ ValueObjectSP begin_sp(
+ valobj_sp->GetChildMemberWithName(ConstString("__begin_"), true));
+ if (!begin_sp) {
+ m_count = 0;
+ return false;
+ }
+ m_base_data_address = begin_sp->GetValueAsUnsigned(0);
+ if (!m_base_data_address) {
+ m_count = 0;
+ return false;
+ }
+ return false;
+}
+
+size_t lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::
+ GetIndexOfChildWithName(ConstString name) {
+ if (!m_count || !m_base_data_address)
+ return UINT32_MAX;
+ const char *item_name = name.GetCString();
+ uint32_t idx = ExtractIndexFromString(item_name);
+ if (idx < UINT32_MAX && idx >= CalculateNumChildren())
+ return UINT32_MAX;
+ return idx;
+}
+
+lldb_private::SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibcxxStdVectorSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ if (!valobj_sp)
+ return nullptr;
+ CompilerType type = valobj_sp->GetCompilerType();
+ if (!type.IsValid() || type.GetNumTemplateArguments() == 0)
+ return nullptr;
+ CompilerType arg_type = type.GetTypeTemplateArgument(0);
+ if (arg_type.GetTypeName() == "bool")
+ return new LibcxxVectorBoolSyntheticFrontEnd(valobj_sp);
+ return new LibcxxStdVectorSyntheticFrontEnd(valobj_sp);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp
new file mode 100644
index 000000000000..0e0f6663c924
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp
@@ -0,0 +1,426 @@
+//===-- LibStdcpp.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 "LibStdcpp.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/StringPrinter.h"
+#include "lldb/DataFormatters/VectorIterator.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+namespace {
+
+class LibstdcppMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+ /*
+ (std::_Rb_tree_iterator<std::pair<const int, std::basic_string<char,
+ std::char_traits<char>, std::allocator<char> > > >) ibeg = {
+ (_Base_ptr) _M_node = 0x0000000100103910 {
+ (std::_Rb_tree_color) _M_color = _S_black
+ (std::_Rb_tree_node_base::_Base_ptr) _M_parent = 0x00000001001038c0
+ (std::_Rb_tree_node_base::_Base_ptr) _M_left = 0x0000000000000000
+ (std::_Rb_tree_node_base::_Base_ptr) _M_right = 0x0000000000000000
+ }
+ }
+ */
+
+public:
+ explicit LibstdcppMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ ExecutionContextRef m_exe_ctx_ref;
+ lldb::addr_t m_pair_address;
+ CompilerType m_pair_type;
+ lldb::ValueObjectSP m_pair_sp;
+};
+
+class LibStdcppSharedPtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ explicit LibStdcppSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+};
+
+} // end of anonymous namespace
+
+LibstdcppMapIteratorSyntheticFrontEnd::LibstdcppMapIteratorSyntheticFrontEnd(
+ lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_pair_address(0),
+ m_pair_type(), m_pair_sp() {
+ if (valobj_sp)
+ Update();
+}
+
+bool LibstdcppMapIteratorSyntheticFrontEnd::Update() {
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ if (!valobj_sp)
+ return false;
+
+ TargetSP target_sp(valobj_sp->GetTargetSP());
+
+ if (!target_sp)
+ return false;
+
+ bool is_64bit = (target_sp->GetArchitecture().GetAddressByteSize() == 8);
+
+ if (!valobj_sp)
+ return false;
+ m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
+
+ ValueObjectSP _M_node_sp(
+ valobj_sp->GetChildMemberWithName(ConstString("_M_node"), true));
+ if (!_M_node_sp)
+ return false;
+
+ m_pair_address = _M_node_sp->GetValueAsUnsigned(0);
+ if (m_pair_address == 0)
+ return false;
+
+ m_pair_address += (is_64bit ? 32 : 16);
+
+ CompilerType my_type(valobj_sp->GetCompilerType());
+ if (my_type.GetNumTemplateArguments() >= 1) {
+ CompilerType pair_type = my_type.GetTypeTemplateArgument(0);
+ if (!pair_type)
+ return false;
+ m_pair_type = pair_type;
+ } else
+ return false;
+
+ return true;
+}
+
+size_t LibstdcppMapIteratorSyntheticFrontEnd::CalculateNumChildren() {
+ return 2;
+}
+
+lldb::ValueObjectSP
+LibstdcppMapIteratorSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
+ if (m_pair_address != 0 && m_pair_type) {
+ if (!m_pair_sp)
+ m_pair_sp = CreateValueObjectFromAddress("pair", m_pair_address,
+ m_exe_ctx_ref, m_pair_type);
+ if (m_pair_sp)
+ return m_pair_sp->GetChildAtIndex(idx, true);
+ }
+ return lldb::ValueObjectSP();
+}
+
+bool LibstdcppMapIteratorSyntheticFrontEnd::MightHaveChildren() { return true; }
+
+size_t LibstdcppMapIteratorSyntheticFrontEnd::GetIndexOfChildWithName(
+ ConstString name) {
+ if (name == "first")
+ return 0;
+ if (name == "second")
+ return 1;
+ return UINT32_MAX;
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return (valobj_sp ? new LibstdcppMapIteratorSyntheticFrontEnd(valobj_sp)
+ : nullptr);
+}
+
+/*
+ (lldb) fr var ibeg --ptr-depth 1
+ (__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > >)
+ ibeg = {
+ _M_current = 0x00000001001037a0 {
+ *_M_current = 1
+ }
+ }
+ */
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibStdcppVectorIteratorSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ static ConstString g_item_name;
+ if (!g_item_name)
+ g_item_name.SetCString("_M_current");
+ return (valobj_sp
+ ? new VectorIteratorSyntheticFrontEnd(valobj_sp, g_item_name)
+ : nullptr);
+}
+
+lldb_private::formatters::VectorIteratorSyntheticFrontEnd::
+ VectorIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp,
+ ConstString item_name)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(),
+ m_item_name(item_name), m_item_sp() {
+ if (valobj_sp)
+ Update();
+}
+
+bool VectorIteratorSyntheticFrontEnd::Update() {
+ m_item_sp.reset();
+
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ if (!valobj_sp)
+ return false;
+
+ if (!valobj_sp)
+ return false;
+
+ ValueObjectSP item_ptr(valobj_sp->GetChildMemberWithName(m_item_name, true));
+ if (!item_ptr)
+ return false;
+ if (item_ptr->GetValueAsUnsigned(0) == 0)
+ return false;
+ Status err;
+ m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
+ m_item_sp = CreateValueObjectFromAddress(
+ "item", item_ptr->GetValueAsUnsigned(0), m_exe_ctx_ref,
+ item_ptr->GetCompilerType().GetPointeeType());
+ if (err.Fail())
+ m_item_sp.reset();
+ return false;
+}
+
+size_t VectorIteratorSyntheticFrontEnd::CalculateNumChildren() { return 1; }
+
+lldb::ValueObjectSP
+VectorIteratorSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
+ if (idx == 0)
+ return m_item_sp;
+ return lldb::ValueObjectSP();
+}
+
+bool VectorIteratorSyntheticFrontEnd::MightHaveChildren() { return true; }
+
+size_t VectorIteratorSyntheticFrontEnd::GetIndexOfChildWithName(
+ ConstString name) {
+ if (name == "item")
+ return 0;
+ return UINT32_MAX;
+}
+
+bool lldb_private::formatters::LibStdcppStringSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ const bool scalar_is_load_addr = true;
+ AddressType addr_type;
+ lldb::addr_t addr_of_string =
+ valobj.GetAddressOf(scalar_is_load_addr, &addr_type);
+ if (addr_of_string != LLDB_INVALID_ADDRESS) {
+ switch (addr_type) {
+ case eAddressTypeLoad: {
+ ProcessSP process_sp(valobj.GetProcessSP());
+ if (!process_sp)
+ return false;
+
+ StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
+ Status error;
+ lldb::addr_t addr_of_data =
+ process_sp->ReadPointerFromMemory(addr_of_string, error);
+ if (error.Fail() || addr_of_data == 0 ||
+ addr_of_data == LLDB_INVALID_ADDRESS)
+ return false;
+ options.SetLocation(addr_of_data);
+ options.SetProcessSP(process_sp);
+ options.SetStream(&stream);
+ options.SetNeedsZeroTermination(false);
+ options.SetBinaryZeroIsTerminator(true);
+ lldb::addr_t size_of_data = process_sp->ReadPointerFromMemory(
+ addr_of_string + process_sp->GetAddressByteSize(), error);
+ if (error.Fail())
+ return false;
+ options.SetSourceSize(size_of_data);
+
+ if (!StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF8>(options)) {
+ stream.Printf("Summary Unavailable");
+ return true;
+ } else
+ return true;
+ } break;
+ case eAddressTypeHost:
+ break;
+ case eAddressTypeInvalid:
+ case eAddressTypeFile:
+ break;
+ }
+ }
+ return false;
+}
+
+bool lldb_private::formatters::LibStdcppWStringSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ const bool scalar_is_load_addr = true;
+ AddressType addr_type;
+ lldb::addr_t addr_of_string =
+ valobj.GetAddressOf(scalar_is_load_addr, &addr_type);
+ if (addr_of_string != LLDB_INVALID_ADDRESS) {
+ switch (addr_type) {
+ case eAddressTypeLoad: {
+ ProcessSP process_sp(valobj.GetProcessSP());
+ if (!process_sp)
+ return false;
+
+ CompilerType wchar_compiler_type =
+ valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeWChar);
+
+ if (!wchar_compiler_type)
+ return false;
+
+ // Safe to pass nullptr for exe_scope here.
+ llvm::Optional<uint64_t> size = wchar_compiler_type.GetBitSize(nullptr);
+ if (!size)
+ return false;
+ const uint32_t wchar_size = *size;
+
+ StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
+ Status error;
+ lldb::addr_t addr_of_data =
+ process_sp->ReadPointerFromMemory(addr_of_string, error);
+ if (error.Fail() || addr_of_data == 0 ||
+ addr_of_data == LLDB_INVALID_ADDRESS)
+ return false;
+ options.SetLocation(addr_of_data);
+ options.SetProcessSP(process_sp);
+ options.SetStream(&stream);
+ options.SetNeedsZeroTermination(false);
+ options.SetBinaryZeroIsTerminator(false);
+ lldb::addr_t size_of_data = process_sp->ReadPointerFromMemory(
+ addr_of_string + process_sp->GetAddressByteSize(), error);
+ if (error.Fail())
+ return false;
+ options.SetSourceSize(size_of_data);
+ options.SetPrefixToken("L");
+
+ switch (wchar_size) {
+ case 8:
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF8>(options);
+ case 16:
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF16>(options);
+ case 32:
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF32>(options);
+ default:
+ stream.Printf("size for wchar_t is not valid");
+ return true;
+ }
+ return true;
+ } break;
+ case eAddressTypeHost:
+ break;
+ case eAddressTypeInvalid:
+ case eAddressTypeFile:
+ break;
+ }
+ }
+ return false;
+}
+
+LibStdcppSharedPtrSyntheticFrontEnd::LibStdcppSharedPtrSyntheticFrontEnd(
+ lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp) {
+ if (valobj_sp)
+ Update();
+}
+
+size_t LibStdcppSharedPtrSyntheticFrontEnd::CalculateNumChildren() { return 1; }
+
+lldb::ValueObjectSP
+LibStdcppSharedPtrSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ if (!valobj_sp)
+ return lldb::ValueObjectSP();
+
+ if (idx == 0)
+ return valobj_sp->GetChildMemberWithName(ConstString("_M_ptr"), true);
+ else
+ return lldb::ValueObjectSP();
+}
+
+bool LibStdcppSharedPtrSyntheticFrontEnd::Update() { return false; }
+
+bool LibStdcppSharedPtrSyntheticFrontEnd::MightHaveChildren() { return true; }
+
+size_t LibStdcppSharedPtrSyntheticFrontEnd::GetIndexOfChildWithName(
+ ConstString name) {
+ if (name == "_M_ptr")
+ return 0;
+ return UINT32_MAX;
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return (valobj_sp ? new LibStdcppSharedPtrSyntheticFrontEnd(valobj_sp)
+ : nullptr);
+}
+
+bool lldb_private::formatters::LibStdcppSmartPointerSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
+ if (!valobj_sp)
+ return false;
+
+ ValueObjectSP ptr_sp(
+ valobj_sp->GetChildMemberWithName(ConstString("_M_ptr"), true));
+ if (!ptr_sp)
+ return false;
+
+ ValueObjectSP usecount_sp(valobj_sp->GetChildAtNamePath(
+ {ConstString("_M_refcount"), ConstString("_M_pi"),
+ ConstString("_M_use_count")}));
+ if (!usecount_sp)
+ return false;
+
+ if (ptr_sp->GetValueAsUnsigned(0) == 0 ||
+ usecount_sp->GetValueAsUnsigned(0) == 0) {
+ stream.Printf("nullptr");
+ return true;
+ }
+
+ Status error;
+ ValueObjectSP pointee_sp = ptr_sp->Dereference(error);
+ if (pointee_sp && error.Success()) {
+ if (pointee_sp->DumpPrintableRepresentation(
+ stream, ValueObject::eValueObjectRepresentationStyleSummary,
+ lldb::eFormatInvalid,
+ ValueObject::PrintableRepresentationSpecialCases::eDisable,
+ false)) {
+ return true;
+ }
+ }
+
+ stream.Printf("ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(0));
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h
new file mode 100644
index 000000000000..e7f88d667c14
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h
@@ -0,0 +1,59 @@
+//===-- LibStdcpp.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 liblldb_LibStdCpp_h_
+#define liblldb_LibStdCpp_h_
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Utility/Stream.h"
+
+namespace lldb_private {
+namespace formatters {
+bool LibStdcppStringSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // libcstdc++ c++11 std::string
+
+bool LibStdcppWStringSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // libcstdc++ c++11 std::wstring
+
+bool LibStdcppSmartPointerSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions
+ &options); // libstdc++ std::shared_ptr<> and std::weak_ptr<>
+
+bool LibStdcppUniquePointerSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options); // libstdc++ std::unique_ptr<>
+
+SyntheticChildrenFrontEnd *
+LibstdcppMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibStdcppTupleSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibStdcppVectorIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibStdcppSharedPtrSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+LibStdcppUniquePtrSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // liblldb_LibStdCpp_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp
new file mode 100644
index 000000000000..66624e5beb6d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp
@@ -0,0 +1,106 @@
+//===-- LibStdcppTuple.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 "LibStdcpp.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Utility/ConstString.h"
+
+#include <memory>
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+namespace {
+
+class LibStdcppTupleSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ explicit LibStdcppTupleSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ std::vector<ValueObjectSP> m_members;
+};
+
+} // end of anonymous namespace
+
+LibStdcppTupleSyntheticFrontEnd::LibStdcppTupleSyntheticFrontEnd(
+ lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp) {
+ Update();
+}
+
+bool LibStdcppTupleSyntheticFrontEnd::Update() {
+ m_members.clear();
+
+ ValueObjectSP valobj_backend_sp = m_backend.GetSP();
+ if (!valobj_backend_sp)
+ return false;
+
+ ValueObjectSP next_child_sp = valobj_backend_sp->GetNonSyntheticValue();
+ while (next_child_sp != nullptr) {
+ ValueObjectSP current_child = next_child_sp;
+ next_child_sp = nullptr;
+
+ size_t child_count = current_child->GetNumChildren();
+ for (size_t i = 0; i < child_count; ++i) {
+ ValueObjectSP child_sp = current_child->GetChildAtIndex(i, true);
+ llvm::StringRef name_str = child_sp->GetName().GetStringRef();
+ if (name_str.startswith("std::_Tuple_impl<")) {
+ next_child_sp = child_sp;
+ } else if (name_str.startswith("std::_Head_base<")) {
+ ValueObjectSP value_sp =
+ child_sp->GetChildMemberWithName(ConstString("_M_head_impl"), true);
+ if (value_sp) {
+ StreamString name;
+ name.Printf("[%zd]", m_members.size());
+ m_members.push_back(value_sp->Clone(ConstString(name.GetString())));
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+bool LibStdcppTupleSyntheticFrontEnd::MightHaveChildren() { return true; }
+
+lldb::ValueObjectSP
+LibStdcppTupleSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
+ if (idx < m_members.size())
+ return m_members[idx];
+ return lldb::ValueObjectSP();
+}
+
+size_t LibStdcppTupleSyntheticFrontEnd::CalculateNumChildren() {
+ return m_members.size();
+}
+
+size_t LibStdcppTupleSyntheticFrontEnd::GetIndexOfChildWithName(
+ ConstString name) {
+ return ExtractIndexFromString(name.GetCString());
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibStdcppTupleSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return (valobj_sp ? new LibStdcppTupleSyntheticFrontEnd(valobj_sp) : nullptr);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp
new file mode 100644
index 000000000000..3860f960cb3d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp
@@ -0,0 +1,169 @@
+//===-- LibStdcppUniquePointer.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 "LibStdcpp.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Utility/ConstString.h"
+
+#include <memory>
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+namespace {
+
+class LibStdcppUniquePtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ explicit LibStdcppUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+ bool GetSummary(Stream &stream, const TypeSummaryOptions &options);
+
+private:
+ ValueObjectSP m_ptr_obj;
+ ValueObjectSP m_obj_obj;
+ ValueObjectSP m_del_obj;
+
+ ValueObjectSP GetTuple();
+};
+
+} // end of anonymous namespace
+
+LibStdcppUniquePtrSyntheticFrontEnd::LibStdcppUniquePtrSyntheticFrontEnd(
+ lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp) {
+ Update();
+}
+
+ValueObjectSP LibStdcppUniquePtrSyntheticFrontEnd::GetTuple() {
+ ValueObjectSP valobj_backend_sp = m_backend.GetSP();
+
+ if (!valobj_backend_sp)
+ return nullptr;
+
+ ValueObjectSP valobj_sp = valobj_backend_sp->GetNonSyntheticValue();
+ if (!valobj_sp)
+ return nullptr;
+
+ ValueObjectSP obj_child_sp =
+ valobj_sp->GetChildMemberWithName(ConstString("_M_t"), true);
+ if (!obj_child_sp)
+ return nullptr;
+
+ ValueObjectSP obj_subchild_sp =
+ obj_child_sp->GetChildMemberWithName(ConstString("_M_t"), true);
+
+ // if there is a _M_t subchild, the tuple is found in the obj_subchild_sp
+ // (for libstdc++ 6.0.23).
+ if (obj_subchild_sp) {
+ return obj_subchild_sp;
+ }
+
+ return obj_child_sp;
+}
+
+bool LibStdcppUniquePtrSyntheticFrontEnd::Update() {
+ ValueObjectSP tuple_sp = GetTuple();
+
+ if (!tuple_sp)
+ return false;
+
+ std::unique_ptr<SyntheticChildrenFrontEnd> tuple_frontend(
+ LibStdcppTupleSyntheticFrontEndCreator(nullptr, tuple_sp));
+
+ ValueObjectSP ptr_obj = tuple_frontend->GetChildAtIndex(0);
+ if (ptr_obj)
+ m_ptr_obj = ptr_obj->Clone(ConstString("pointer"));
+
+ ValueObjectSP del_obj = tuple_frontend->GetChildAtIndex(1);
+ if (del_obj)
+ m_del_obj = del_obj->Clone(ConstString("deleter"));
+
+ if (m_ptr_obj) {
+ Status error;
+ ValueObjectSP obj_obj = m_ptr_obj->Dereference(error);
+ if (error.Success()) {
+ m_obj_obj = obj_obj->Clone(ConstString("object"));
+ }
+ }
+
+ return false;
+}
+
+bool LibStdcppUniquePtrSyntheticFrontEnd::MightHaveChildren() { return true; }
+
+lldb::ValueObjectSP
+LibStdcppUniquePtrSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
+ if (idx == 0)
+ return m_ptr_obj;
+ if (idx == 1)
+ return m_del_obj;
+ if (idx == 2)
+ return m_obj_obj;
+ return lldb::ValueObjectSP();
+}
+
+size_t LibStdcppUniquePtrSyntheticFrontEnd::CalculateNumChildren() {
+ if (m_del_obj)
+ return 2;
+ return 1;
+}
+
+size_t LibStdcppUniquePtrSyntheticFrontEnd::GetIndexOfChildWithName(
+ ConstString name) {
+ if (name == "ptr" || name == "pointer")
+ return 0;
+ if (name == "del" || name == "deleter")
+ return 1;
+ if (name == "obj" || name == "object" || name == "$$dereference$$")
+ return 2;
+ return UINT32_MAX;
+}
+
+bool LibStdcppUniquePtrSyntheticFrontEnd::GetSummary(
+ Stream &stream, const TypeSummaryOptions &options) {
+ if (!m_ptr_obj)
+ return false;
+
+ bool success;
+ uint64_t ptr_value = m_ptr_obj->GetValueAsUnsigned(0, &success);
+ if (!success)
+ return false;
+ if (ptr_value == 0)
+ stream.Printf("nullptr");
+ else
+ stream.Printf("0x%" PRIx64, ptr_value);
+ return true;
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibStdcppUniquePtrSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return (valobj_sp ? new LibStdcppUniquePtrSyntheticFrontEnd(valobj_sp)
+ : nullptr);
+}
+
+bool lldb_private::formatters::LibStdcppUniquePointerSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ LibStdcppUniquePtrSyntheticFrontEnd formatter(valobj.GetSP());
+ return formatter.GetSummary(stream, options);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.cpp
new file mode 100644
index 000000000000..248c51acea42
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.cpp
@@ -0,0 +1,98 @@
+//===-- MSVCUndecoratedNameParser.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 "MSVCUndecoratedNameParser.h"
+
+#include <stack>
+
+MSVCUndecoratedNameParser::MSVCUndecoratedNameParser(llvm::StringRef name) {
+ std::size_t last_base_start = 0;
+
+ std::stack<std::size_t> stack;
+ unsigned int open_angle_brackets = 0;
+ for (size_t i = 0; i < name.size(); i++) {
+ switch (name[i]) {
+ case '<':
+ // Do not treat `operator<' and `operator<<' as templates
+ // (sometimes they represented as `<' and `<<' in the name).
+ if (i == last_base_start ||
+ (i == last_base_start + 1 && name[last_base_start] == '<'))
+ break;
+
+ stack.push(i);
+ open_angle_brackets++;
+
+ break;
+ case '>':
+ if (!stack.empty() && name[stack.top()] == '<') {
+ open_angle_brackets--;
+ stack.pop();
+ }
+
+ break;
+ case '`':
+ stack.push(i);
+
+ break;
+ case '\'':
+ while (!stack.empty()) {
+ std::size_t top = stack.top();
+ if (name[top] == '<')
+ open_angle_brackets--;
+
+ stack.pop();
+
+ if (name[top] == '`')
+ break;
+ }
+
+ break;
+ case ':':
+ if (open_angle_brackets)
+ break;
+ if (i == 0 || name[i - 1] != ':')
+ break;
+
+ m_specifiers.emplace_back(name.take_front(i - 1),
+ name.slice(last_base_start, i - 1));
+
+ last_base_start = i + 1;
+ break;
+ default:
+ break;
+ }
+ }
+
+ m_specifiers.emplace_back(name, name.drop_front(last_base_start));
+}
+
+bool MSVCUndecoratedNameParser::IsMSVCUndecoratedName(llvm::StringRef name) {
+ return name.find('`') != llvm::StringRef::npos;
+}
+
+bool MSVCUndecoratedNameParser::ExtractContextAndIdentifier(
+ llvm::StringRef name, llvm::StringRef &context,
+ llvm::StringRef &identifier) {
+ MSVCUndecoratedNameParser parser(name);
+ llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs = parser.GetSpecifiers();
+
+ std::size_t count = specs.size();
+ identifier = count > 0 ? specs[count - 1].GetBaseName() : "";
+ context = count > 1 ? specs[count - 2].GetFullName() : "";
+
+ return count;
+}
+
+llvm::StringRef MSVCUndecoratedNameParser::DropScope(llvm::StringRef name) {
+ MSVCUndecoratedNameParser parser(name);
+ llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs = parser.GetSpecifiers();
+ if (specs.empty())
+ return "";
+
+ return specs[specs.size() - 1].GetBaseName();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h
new file mode 100644
index 000000000000..6e20877cae1b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h
@@ -0,0 +1,50 @@
+//===-- MSVCUndecoratedNameParser.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 liblldb_MSVCUndecoratedNameParser_h_
+#define liblldb_MSVCUndecoratedNameParser_h_
+
+#include <vector>
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+
+class MSVCUndecoratedNameSpecifier {
+public:
+ MSVCUndecoratedNameSpecifier(llvm::StringRef full_name,
+ llvm::StringRef base_name)
+ : m_full_name(full_name), m_base_name(base_name) {}
+
+ llvm::StringRef GetFullName() const { return m_full_name; }
+ llvm::StringRef GetBaseName() const { return m_base_name; }
+
+private:
+ llvm::StringRef m_full_name;
+ llvm::StringRef m_base_name;
+};
+
+class MSVCUndecoratedNameParser {
+public:
+ explicit MSVCUndecoratedNameParser(llvm::StringRef name);
+
+ llvm::ArrayRef<MSVCUndecoratedNameSpecifier> GetSpecifiers() const {
+ return m_specifiers;
+ }
+
+ static bool IsMSVCUndecoratedName(llvm::StringRef name);
+ static bool ExtractContextAndIdentifier(llvm::StringRef name,
+ llvm::StringRef &context,
+ llvm::StringRef &identifier);
+
+ static llvm::StringRef DropScope(llvm::StringRef name);
+
+private:
+ std::vector<MSVCUndecoratedNameSpecifier> m_specifiers;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.cpp
new file mode 100644
index 000000000000..a9a1b44731f2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.cpp
@@ -0,0 +1,238 @@
+//===-- ClangHighlighter.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 "ClangHighlighter.h"
+
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Utility/AnsiTerminal.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+using namespace lldb_private;
+
+bool ClangHighlighter::isKeyword(llvm::StringRef token) const {
+ return keywords.find(token) != keywords.end();
+}
+
+ClangHighlighter::ClangHighlighter() {
+#define KEYWORD(X, N) keywords.insert(#X);
+#include "clang/Basic/TokenKinds.def"
+}
+
+/// Determines which style should be applied to the given token.
+/// \param highlighter
+/// The current highlighter that should use the style.
+/// \param token
+/// The current token.
+/// \param tok_str
+/// The string in the source code the token represents.
+/// \param options
+/// The style we use for coloring the source code.
+/// \param in_pp_directive
+/// If we are currently in a preprocessor directive. NOTE: This is
+/// passed by reference and will be updated if the current token starts
+/// or ends a preprocessor directive.
+/// \return
+/// The ColorStyle that should be applied to the token.
+static HighlightStyle::ColorStyle
+determineClangStyle(const ClangHighlighter &highlighter,
+ const clang::Token &token, llvm::StringRef tok_str,
+ const HighlightStyle &options, bool &in_pp_directive) {
+ using namespace clang;
+
+ if (token.is(tok::comment)) {
+ // If we were in a preprocessor directive before, we now left it.
+ in_pp_directive = false;
+ return options.comment;
+ } else if (in_pp_directive || token.getKind() == tok::hash) {
+ // Let's assume that the rest of the line is a PP directive.
+ in_pp_directive = true;
+ // Preprocessor directives are hard to match, so we have to hack this in.
+ return options.pp_directive;
+ } else if (tok::isStringLiteral(token.getKind()))
+ return options.string_literal;
+ else if (tok::isLiteral(token.getKind()))
+ return options.scalar_literal;
+ else if (highlighter.isKeyword(tok_str))
+ return options.keyword;
+ else
+ switch (token.getKind()) {
+ case tok::raw_identifier:
+ case tok::identifier:
+ return options.identifier;
+ case tok::l_brace:
+ case tok::r_brace:
+ return options.braces;
+ case tok::l_square:
+ case tok::r_square:
+ return options.square_brackets;
+ case tok::l_paren:
+ case tok::r_paren:
+ return options.parentheses;
+ case tok::comma:
+ return options.comma;
+ case tok::coloncolon:
+ case tok::colon:
+ return options.colon;
+
+ case tok::amp:
+ case tok::ampamp:
+ case tok::ampequal:
+ case tok::star:
+ case tok::starequal:
+ case tok::plus:
+ case tok::plusplus:
+ case tok::plusequal:
+ case tok::minus:
+ case tok::arrow:
+ case tok::minusminus:
+ case tok::minusequal:
+ case tok::tilde:
+ case tok::exclaim:
+ case tok::exclaimequal:
+ case tok::slash:
+ case tok::slashequal:
+ case tok::percent:
+ case tok::percentequal:
+ case tok::less:
+ case tok::lessless:
+ case tok::lessequal:
+ case tok::lesslessequal:
+ case tok::spaceship:
+ case tok::greater:
+ case tok::greatergreater:
+ case tok::greaterequal:
+ case tok::greatergreaterequal:
+ case tok::caret:
+ case tok::caretequal:
+ case tok::pipe:
+ case tok::pipepipe:
+ case tok::pipeequal:
+ case tok::question:
+ case tok::equal:
+ case tok::equalequal:
+ return options.operators;
+ default:
+ break;
+ }
+ return HighlightStyle::ColorStyle();
+}
+
+void ClangHighlighter::Highlight(const HighlightStyle &options,
+ llvm::StringRef line,
+ llvm::Optional<size_t> cursor_pos,
+ llvm::StringRef previous_lines,
+ Stream &result) const {
+ using namespace clang;
+
+ FileSystemOptions file_opts;
+ FileManager file_mgr(file_opts,
+ FileSystem::Instance().GetVirtualFileSystem());
+
+ unsigned line_number = previous_lines.count('\n') + 1U;
+
+ // Let's build the actual source code Clang needs and setup some utility
+ // objects.
+ std::string full_source = previous_lines.str() + line.str();
+ llvm::IntrusiveRefCntPtr<DiagnosticIDs> diag_ids(new DiagnosticIDs());
+ llvm::IntrusiveRefCntPtr<DiagnosticOptions> diags_opts(
+ new DiagnosticOptions());
+ DiagnosticsEngine diags(diag_ids, diags_opts);
+ clang::SourceManager SM(diags, file_mgr);
+ auto buf = llvm::MemoryBuffer::getMemBuffer(full_source);
+
+ FileID FID = SM.createFileID(clang::SourceManager::Unowned, buf.get());
+
+ // Let's just enable the latest ObjC and C++ which should get most tokens
+ // right.
+ LangOptions Opts;
+ Opts.ObjC = true;
+ // FIXME: This should probably set CPlusPlus, CPlusPlus11, ... too
+ Opts.CPlusPlus17 = true;
+ Opts.LineComment = true;
+
+ Lexer lex(FID, buf.get(), SM, Opts);
+ // The lexer should keep whitespace around.
+ lex.SetKeepWhitespaceMode(true);
+
+ // Keeps track if we have entered a PP directive.
+ bool in_pp_directive = false;
+
+ // True once we actually lexed the user provided line.
+ bool found_user_line = false;
+
+ // True if we already highlighted the token under the cursor, false otherwise.
+ bool highlighted_cursor = false;
+ Token token;
+ bool exit = false;
+ while (!exit) {
+ // Returns true if this is the last token we get from the lexer.
+ exit = lex.LexFromRawLexer(token);
+
+ bool invalid = false;
+ unsigned current_line_number =
+ SM.getSpellingLineNumber(token.getLocation(), &invalid);
+ if (current_line_number != line_number)
+ continue;
+ found_user_line = true;
+
+ // We don't need to print any tokens without a spelling line number.
+ if (invalid)
+ continue;
+
+ // Same as above but with the column number.
+ invalid = false;
+ unsigned start = SM.getSpellingColumnNumber(token.getLocation(), &invalid);
+ if (invalid)
+ continue;
+ // Column numbers start at 1, but indexes in our string start at 0.
+ --start;
+
+ // Annotations don't have a length, so let's skip them.
+ if (token.isAnnotation())
+ continue;
+
+ // Extract the token string from our source code.
+ llvm::StringRef tok_str = line.substr(start, token.getLength());
+
+ // If the token is just an empty string, we can skip all the work below.
+ if (tok_str.empty())
+ continue;
+
+ // If the cursor is inside this token, we have to apply the 'selected'
+ // highlight style before applying the actual token color.
+ llvm::StringRef to_print = tok_str;
+ StreamString storage;
+ auto end = start + token.getLength();
+ if (cursor_pos && end > *cursor_pos && !highlighted_cursor) {
+ highlighted_cursor = true;
+ options.selected.Apply(storage, tok_str);
+ to_print = storage.GetString();
+ }
+
+ // See how we are supposed to highlight this token.
+ HighlightStyle::ColorStyle color =
+ determineClangStyle(*this, token, tok_str, options, in_pp_directive);
+
+ color.Apply(result, to_print);
+ }
+
+ // If we went over the whole file but couldn't find our own file, then
+ // somehow our setup was wrong. When we're in release mode we just give the
+ // user the normal line and pretend we don't know how to highlight it. In
+ // debug mode we bail out with an assert as this should never happen.
+ if (!found_user_line) {
+ result << line;
+ assert(false && "We couldn't find the user line in the input file?");
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.h b/contrib/llvm-project/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.h
new file mode 100644
index 000000000000..f459f9424697
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.h
@@ -0,0 +1,37 @@
+//===-- ClangHighlighter.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 liblldb_ClangHighlighter_h_
+#define liblldb_ClangHighlighter_h_
+
+#include "lldb/Utility/Stream.h"
+#include "llvm/ADT/StringSet.h"
+
+#include "lldb/Core/Highlighter.h"
+
+namespace lldb_private {
+
+class ClangHighlighter : public Highlighter {
+ llvm::StringSet<> keywords;
+
+public:
+ ClangHighlighter();
+ llvm::StringRef GetName() const override { return "clang"; }
+
+ void Highlight(const HighlightStyle &options, llvm::StringRef line,
+ llvm::Optional<size_t> cursor_pos,
+ llvm::StringRef previous_lines, Stream &s) const override;
+
+ /// Returns true if the given string represents a keywords in any Clang
+ /// supported language.
+ bool isKeyword(llvm::StringRef token) const;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ClangHighlighter_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CF.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CF.cpp
new file mode 100644
index 000000000000..5bca260616ea
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CF.cpp
@@ -0,0 +1,295 @@
+//===-- CF.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 "CF.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+bool lldb_private::formatters::CFAbsoluteTimeSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ time_t epoch = GetOSXEpoch();
+ epoch = epoch + (time_t)valobj.GetValueAsUnsigned(0);
+ tm *tm_date = localtime(&epoch);
+ if (!tm_date)
+ return false;
+ std::string buffer(1024, 0);
+ if (strftime(&buffer[0], 1023, "%Z", tm_date) == 0)
+ return false;
+ stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year + 1900,
+ tm_date->tm_mon + 1, tm_date->tm_mday, tm_date->tm_hour,
+ tm_date->tm_min, tm_date->tm_sec, buffer.c_str());
+ return true;
+}
+
+bool lldb_private::formatters::CFBagSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ static ConstString g_TypeHint("CFBag");
+
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor.get() || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ uint32_t count = 0;
+
+ bool is_type_ok = false; // check to see if this is a CFBag we know about
+ if (descriptor->IsCFType()) {
+ ConstString type_name(valobj.GetTypeName());
+
+ static ConstString g___CFBag("__CFBag");
+ static ConstString g_conststruct__CFBag("const struct __CFBag");
+
+ if (type_name == g___CFBag || type_name == g_conststruct__CFBag) {
+ if (valobj.IsPointerType())
+ is_type_ok = true;
+ }
+ }
+
+ if (is_type_ok) {
+ lldb::addr_t offset = 2 * ptr_size + 4 + valobj_addr;
+ Status error;
+ count = process_sp->ReadUnsignedIntegerFromMemory(offset, 4, 0, error);
+ if (error.Fail())
+ return false;
+ } else
+ return false;
+
+ std::string prefix, suffix;
+ if (Language *language = Language::FindPlugin(options.GetLanguage())) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ stream.Printf("%s\"%u value%s\"%s", prefix.c_str(), count,
+ (count == 1 ? "" : "s"), suffix.c_str());
+ return true;
+}
+
+bool lldb_private::formatters::CFBitVectorSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor.get() || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ uint32_t count = 0;
+
+ bool is_type_ok = false; // check to see if this is a CFBag we know about
+ if (descriptor->IsCFType()) {
+ ConstString type_name(valobj.GetTypeName());
+ if (type_name == "__CFMutableBitVector" || type_name == "__CFBitVector" ||
+ type_name == "CFMutableBitVectorRef" || type_name == "CFBitVectorRef") {
+ if (valobj.IsPointerType())
+ is_type_ok = true;
+ }
+ }
+
+ if (!is_type_ok)
+ return false;
+
+ Status error;
+ count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + 2 * ptr_size,
+ ptr_size, 0, error);
+ if (error.Fail())
+ return false;
+ uint64_t num_bytes = count / 8 + ((count & 7) ? 1 : 0);
+ addr_t data_ptr = process_sp->ReadPointerFromMemory(
+ valobj_addr + 2 * ptr_size + 2 * ptr_size, error);
+ if (error.Fail())
+ return false;
+ // make sure we do not try to read huge amounts of data
+ if (num_bytes > 1024)
+ num_bytes = 1024;
+ DataBufferSP buffer_sp(new DataBufferHeap(num_bytes, 0));
+ num_bytes =
+ process_sp->ReadMemory(data_ptr, buffer_sp->GetBytes(), num_bytes, error);
+ if (error.Fail() || num_bytes == 0)
+ return false;
+ uint8_t *bytes = buffer_sp->GetBytes();
+ for (uint64_t byte_idx = 0; byte_idx < num_bytes - 1; byte_idx++) {
+ uint8_t byte = bytes[byte_idx];
+ bool bit0 = (byte & 1) == 1;
+ bool bit1 = (byte & 2) == 2;
+ bool bit2 = (byte & 4) == 4;
+ bool bit3 = (byte & 8) == 8;
+ bool bit4 = (byte & 16) == 16;
+ bool bit5 = (byte & 32) == 32;
+ bool bit6 = (byte & 64) == 64;
+ bool bit7 = (byte & 128) == 128;
+ stream.Printf("%c%c%c%c %c%c%c%c ", (bit7 ? '1' : '0'), (bit6 ? '1' : '0'),
+ (bit5 ? '1' : '0'), (bit4 ? '1' : '0'), (bit3 ? '1' : '0'),
+ (bit2 ? '1' : '0'), (bit1 ? '1' : '0'), (bit0 ? '1' : '0'));
+ count -= 8;
+ }
+ {
+ // print the last byte ensuring we do not print spurious bits
+ uint8_t byte = bytes[num_bytes - 1];
+ bool bit0 = (byte & 1) == 1;
+ bool bit1 = (byte & 2) == 2;
+ bool bit2 = (byte & 4) == 4;
+ bool bit3 = (byte & 8) == 8;
+ bool bit4 = (byte & 16) == 16;
+ bool bit5 = (byte & 32) == 32;
+ bool bit6 = (byte & 64) == 64;
+ bool bit7 = (byte & 128) == 128;
+ if (count) {
+ stream.Printf("%c", bit7 ? '1' : '0');
+ count -= 1;
+ }
+ if (count) {
+ stream.Printf("%c", bit6 ? '1' : '0');
+ count -= 1;
+ }
+ if (count) {
+ stream.Printf("%c", bit5 ? '1' : '0');
+ count -= 1;
+ }
+ if (count) {
+ stream.Printf("%c", bit4 ? '1' : '0');
+ count -= 1;
+ }
+ if (count) {
+ stream.Printf("%c", bit3 ? '1' : '0');
+ count -= 1;
+ }
+ if (count) {
+ stream.Printf("%c", bit2 ? '1' : '0');
+ count -= 1;
+ }
+ if (count) {
+ stream.Printf("%c", bit1 ? '1' : '0');
+ count -= 1;
+ }
+ if (count)
+ stream.Printf("%c", bit0 ? '1' : '0');
+ }
+ return true;
+}
+
+bool lldb_private::formatters::CFBinaryHeapSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ static ConstString g_TypeHint("CFBinaryHeap");
+
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor.get() || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ uint32_t count = 0;
+
+ bool is_type_ok =
+ false; // check to see if this is a CFBinaryHeap we know about
+ if (descriptor->IsCFType()) {
+ ConstString type_name(valobj.GetTypeName());
+
+ static ConstString g___CFBinaryHeap("__CFBinaryHeap");
+ static ConstString g_conststruct__CFBinaryHeap(
+ "const struct __CFBinaryHeap");
+ static ConstString g_CFBinaryHeapRef("CFBinaryHeapRef");
+
+ if (type_name == g___CFBinaryHeap ||
+ type_name == g_conststruct__CFBinaryHeap ||
+ type_name == g_CFBinaryHeapRef) {
+ if (valobj.IsPointerType())
+ is_type_ok = true;
+ }
+ }
+
+ if (is_type_ok) {
+ lldb::addr_t offset = 2 * ptr_size + valobj_addr;
+ Status error;
+ count = process_sp->ReadUnsignedIntegerFromMemory(offset, 4, 0, error);
+ if (error.Fail())
+ return false;
+ } else
+ return false;
+
+ std::string prefix, suffix;
+ if (Language *language = Language::FindPlugin(options.GetLanguage())) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ stream.Printf("%s\"%u item%s\"%s", prefix.c_str(), count,
+ (count == 1 ? "" : "s"), suffix.c_str());
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CF.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CF.h
new file mode 100644
index 000000000000..2abb56d407eb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CF.h
@@ -0,0 +1,32 @@
+//===-- CF.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 liblldb_CF_h_
+#define liblldb_CF_h_
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/Utility/Stream.h"
+
+namespace lldb_private {
+namespace formatters {
+bool CFBagSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool CFBinaryHeapSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool CFBitVectorSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool CFAbsoluteTimeSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // liblldb_CF_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/Cocoa.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/Cocoa.cpp
new file mode 100644
index 000000000000..ddf3953bb512
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/Cocoa.cpp
@@ -0,0 +1,1123 @@
+//===-- Cocoa.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 "Cocoa.h"
+
+#include "lldb/Core/Mangled.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/DataFormatters/StringPrinter.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/Host/Time.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/ProcessStructReader.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/bit.h"
+
+#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
+
+#include "NSString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+bool lldb_private::formatters::NSBundleSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ llvm::StringRef class_name(descriptor->GetClassName().GetCString());
+
+ if (class_name.empty())
+ return false;
+
+ if (class_name == "NSBundle") {
+ uint64_t offset = 5 * ptr_size;
+ ValueObjectSP text(valobj.GetSyntheticChildAtOffset(
+ offset,
+ valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID),
+ true));
+
+ StreamString summary_stream;
+ bool was_nsstring_ok =
+ NSStringSummaryProvider(*text, summary_stream, options);
+ if (was_nsstring_ok && summary_stream.GetSize() > 0) {
+ stream.Printf("%s", summary_stream.GetData());
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool lldb_private::formatters::NSTimeZoneSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ llvm::StringRef class_name(descriptor->GetClassName().GetCString());
+
+ if (class_name.empty())
+ return false;
+
+ if (class_name == "__NSTimeZone") {
+ uint64_t offset = ptr_size;
+ ValueObjectSP text(valobj.GetSyntheticChildAtOffset(
+ offset, valobj.GetCompilerType(), true));
+ StreamString summary_stream;
+ bool was_nsstring_ok =
+ NSStringSummaryProvider(*text, summary_stream, options);
+ if (was_nsstring_ok && summary_stream.GetSize() > 0) {
+ stream.Printf("%s", summary_stream.GetData());
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool lldb_private::formatters::NSNotificationSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ llvm::StringRef class_name(descriptor->GetClassName().GetCString());
+
+ if (class_name.empty())
+ return false;
+
+ if (class_name == "NSConcreteNotification") {
+ uint64_t offset = ptr_size;
+ ValueObjectSP text(valobj.GetSyntheticChildAtOffset(
+ offset, valobj.GetCompilerType(), true));
+ StreamString summary_stream;
+ bool was_nsstring_ok =
+ NSStringSummaryProvider(*text, summary_stream, options);
+ if (was_nsstring_ok && summary_stream.GetSize() > 0) {
+ stream.Printf("%s", summary_stream.GetData());
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool lldb_private::formatters::NSMachPortSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ llvm::StringRef class_name(descriptor->GetClassName().GetCString());
+
+ if (class_name.empty())
+ return false;
+
+ uint64_t port_number = 0;
+
+ if (class_name == "NSMachPort") {
+ uint64_t offset = (ptr_size == 4 ? 12 : 20);
+ Status error;
+ port_number = process_sp->ReadUnsignedIntegerFromMemory(
+ offset + valobj_addr, 4, 0, error);
+ if (error.Success()) {
+ stream.Printf("mach port: %u",
+ (uint32_t)(port_number & 0x00000000FFFFFFFF));
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool lldb_private::formatters::NSIndexSetSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ llvm::StringRef class_name(descriptor->GetClassName().GetCString());
+
+ if (class_name.empty())
+ return false;
+
+ uint64_t count = 0;
+
+ do {
+ if (class_name == "NSIndexSet" || class_name == "NSMutableIndexSet") {
+ Status error;
+ uint32_t mode = process_sp->ReadUnsignedIntegerFromMemory(
+ valobj_addr + ptr_size, 4, 0, error);
+ if (error.Fail())
+ return false;
+ // this means the set is empty - count = 0
+ if ((mode & 1) == 1) {
+ count = 0;
+ break;
+ }
+ if ((mode & 2) == 2)
+ mode = 1; // this means the set only has one range
+ else
+ mode = 2; // this means the set has multiple ranges
+ if (mode == 1) {
+ count = process_sp->ReadUnsignedIntegerFromMemory(
+ valobj_addr + 3 * ptr_size, ptr_size, 0, error);
+ if (error.Fail())
+ return false;
+ } else {
+ // read a pointer to the data at 2*ptr_size
+ count = process_sp->ReadUnsignedIntegerFromMemory(
+ valobj_addr + 2 * ptr_size, ptr_size, 0, error);
+ if (error.Fail())
+ return false;
+ // read the data at 2*ptr_size from the first location
+ count = process_sp->ReadUnsignedIntegerFromMemory(count + 2 * ptr_size,
+ ptr_size, 0, error);
+ if (error.Fail())
+ return false;
+ }
+ } else
+ return false;
+ } while (false);
+ stream.Printf("%" PRIu64 " index%s", count, (count == 1 ? "" : "es"));
+ return true;
+}
+
+static void NSNumber_FormatChar(ValueObject &valobj, Stream &stream, char value,
+ lldb::LanguageType lang) {
+ static ConstString g_TypeHint("NSNumber:char");
+
+ std::string prefix, suffix;
+ if (Language *language = Language::FindPlugin(lang)) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ stream.Printf("%s%hhd%s", prefix.c_str(), value, suffix.c_str());
+}
+
+static void NSNumber_FormatShort(ValueObject &valobj, Stream &stream,
+ short value, lldb::LanguageType lang) {
+ static ConstString g_TypeHint("NSNumber:short");
+
+ std::string prefix, suffix;
+ if (Language *language = Language::FindPlugin(lang)) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ stream.Printf("%s%hd%s", prefix.c_str(), value, suffix.c_str());
+}
+
+static void NSNumber_FormatInt(ValueObject &valobj, Stream &stream, int value,
+ lldb::LanguageType lang) {
+ static ConstString g_TypeHint("NSNumber:int");
+
+ std::string prefix, suffix;
+ if (Language *language = Language::FindPlugin(lang)) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ stream.Printf("%s%d%s", prefix.c_str(), value, suffix.c_str());
+}
+
+static void NSNumber_FormatLong(ValueObject &valobj, Stream &stream,
+ uint64_t value, lldb::LanguageType lang) {
+ static ConstString g_TypeHint("NSNumber:long");
+
+ std::string prefix, suffix;
+ if (Language *language = Language::FindPlugin(lang)) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ stream.Printf("%s%" PRId64 "%s", prefix.c_str(), value, suffix.c_str());
+}
+
+static void NSNumber_FormatInt128(ValueObject &valobj, Stream &stream,
+ const llvm::APInt &value,
+ lldb::LanguageType lang) {
+ static ConstString g_TypeHint("NSNumber:int128_t");
+
+ std::string prefix, suffix;
+ if (Language *language = Language::FindPlugin(lang)) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ stream.PutCString(prefix.c_str());
+ const int radix = 10;
+ const bool isSigned = true;
+ std::string str = value.toString(radix, isSigned);
+ stream.PutCString(str.c_str());
+ stream.PutCString(suffix.c_str());
+}
+
+static void NSNumber_FormatFloat(ValueObject &valobj, Stream &stream,
+ float value, lldb::LanguageType lang) {
+ static ConstString g_TypeHint("NSNumber:float");
+
+ std::string prefix, suffix;
+ if (Language *language = Language::FindPlugin(lang)) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ stream.Printf("%s%f%s", prefix.c_str(), value, suffix.c_str());
+}
+
+static void NSNumber_FormatDouble(ValueObject &valobj, Stream &stream,
+ double value, lldb::LanguageType lang) {
+ static ConstString g_TypeHint("NSNumber:double");
+
+ std::string prefix, suffix;
+ if (Language *language = Language::FindPlugin(lang)) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ stream.Printf("%s%g%s", prefix.c_str(), value, suffix.c_str());
+}
+
+bool lldb_private::formatters::NSNumberSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ llvm::StringRef class_name(descriptor->GetClassName().GetCString());
+
+ if (class_name.empty())
+ return false;
+
+ if (class_name == "__NSCFBoolean")
+ return ObjCBooleanSummaryProvider(valobj, stream, options);
+
+ if (class_name == "NSDecimalNumber")
+ return NSDecimalNumberSummaryProvider(valobj, stream, options);
+
+ if (class_name == "NSNumber" || class_name == "__NSCFNumber") {
+ uint64_t value = 0;
+ uint64_t i_bits = 0;
+ if (descriptor->GetTaggedPointerInfo(&i_bits, &value)) {
+ switch (i_bits) {
+ case 0:
+ NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage());
+ break;
+ case 1:
+ case 4:
+ NSNumber_FormatShort(valobj, stream, (short)value,
+ options.GetLanguage());
+ break;
+ case 2:
+ case 8:
+ NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage());
+ break;
+ case 3:
+ case 12:
+ NSNumber_FormatLong(valobj, stream, value, options.GetLanguage());
+ break;
+ default:
+ return false;
+ }
+ return true;
+ } else {
+ Status error;
+
+ AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
+ ObjCLanguageRuntime::Get(*process_sp));
+
+ const bool new_format =
+ (runtime && runtime->GetFoundationVersion() >= 1400);
+
+ enum class TypeCodes : int {
+ sint8 = 0x0,
+ sint16 = 0x1,
+ sint32 = 0x2,
+ sint64 = 0x3,
+ f32 = 0x4,
+ f64 = 0x5,
+ sint128 = 0x6
+ };
+
+ uint64_t data_location = valobj_addr + 2 * ptr_size;
+ TypeCodes type_code;
+
+ if (new_format) {
+ uint64_t cfinfoa =
+ process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
+ ptr_size, 0, error);
+
+ if (error.Fail())
+ return false;
+
+ bool is_preserved_number = cfinfoa & 0x8;
+ if (is_preserved_number) {
+ lldbassert(!static_cast<bool>("We should handle preserved numbers!"));
+ return false;
+ }
+
+ type_code = static_cast<TypeCodes>(cfinfoa & 0x7);
+ } else {
+ uint8_t data_type =
+ process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 1,
+ 0, error) & 0x1F;
+
+ if (error.Fail())
+ return false;
+
+ switch (data_type) {
+ case 1: type_code = TypeCodes::sint8; break;
+ case 2: type_code = TypeCodes::sint16; break;
+ case 3: type_code = TypeCodes::sint32; break;
+ case 17: data_location += 8; LLVM_FALLTHROUGH;
+ case 4: type_code = TypeCodes::sint64; break;
+ case 5: type_code = TypeCodes::f32; break;
+ case 6: type_code = TypeCodes::f64; break;
+ default: return false;
+ }
+ }
+
+ uint64_t value = 0;
+ bool success = false;
+ switch (type_code) {
+ case TypeCodes::sint8:
+ value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0,
+ error);
+ if (error.Fail())
+ return false;
+ NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage());
+ success = true;
+ break;
+ case TypeCodes::sint16:
+ value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0,
+ error);
+ if (error.Fail())
+ return false;
+ NSNumber_FormatShort(valobj, stream, (short)value,
+ options.GetLanguage());
+ success = true;
+ break;
+ case TypeCodes::sint32:
+ value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0,
+ error);
+ if (error.Fail())
+ return false;
+ NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage());
+ success = true;
+ break;
+ case TypeCodes::sint64:
+ value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0,
+ error);
+ if (error.Fail())
+ return false;
+ NSNumber_FormatLong(valobj, stream, value, options.GetLanguage());
+ success = true;
+ break;
+ case TypeCodes::f32:
+ {
+ uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(
+ data_location, 4, 0, error);
+ if (error.Fail())
+ return false;
+ float flt_value = 0.0f;
+ memcpy(&flt_value, &flt_as_int, sizeof(flt_as_int));
+ NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage());
+ success = true;
+ break;
+ }
+ case TypeCodes::f64:
+ {
+ uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(
+ data_location, 8, 0, error);
+ if (error.Fail())
+ return false;
+ double dbl_value = 0.0;
+ memcpy(&dbl_value, &dbl_as_lng, sizeof(dbl_as_lng));
+ NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage());
+ success = true;
+ break;
+ }
+ case TypeCodes::sint128: // internally, this is the same
+ {
+ uint64_t words[2];
+ words[1] = process_sp->ReadUnsignedIntegerFromMemory(
+ data_location, 8, 0, error);
+ if (error.Fail())
+ return false;
+ words[0] = process_sp->ReadUnsignedIntegerFromMemory(
+ data_location + 8, 8, 0, error);
+ if (error.Fail())
+ return false;
+ llvm::APInt i128_value(128, words);
+ NSNumber_FormatInt128(valobj, stream, i128_value, options.GetLanguage());
+ success = true;
+ break;
+ }
+ }
+ return success;
+ }
+ }
+
+ return false;
+}
+
+bool lldb_private::formatters::NSDecimalNumberSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ Status error;
+ int8_t exponent = process_sp->ReadUnsignedIntegerFromMemory(
+ valobj_addr + ptr_size, 1, 0, error);
+ if (error.Fail())
+ return false;
+
+ uint8_t length_and_negative = process_sp->ReadUnsignedIntegerFromMemory(
+ valobj_addr + ptr_size + 1, 1, 0, error);
+ if (error.Fail())
+ return false;
+
+ // Fifth bit marks negativity.
+ const bool is_negative = (length_and_negative >> 4) & 1;
+
+ // Zero length and negative means NaN.
+ uint8_t length = length_and_negative & 0xf;
+ const bool is_nan = is_negative && (length == 0);
+
+ if (is_nan) {
+ stream.Printf("NaN");
+ return true;
+ }
+
+ if (length == 0) {
+ stream.Printf("0");
+ return true;
+ }
+
+ uint64_t mantissa = process_sp->ReadUnsignedIntegerFromMemory(
+ valobj_addr + ptr_size + 4, 8, 0, error);
+ if (error.Fail())
+ return false;
+
+ if (is_negative)
+ stream.Printf("-");
+
+ stream.Printf("%" PRIu64 " x 10^%" PRIi8, mantissa, exponent);
+ return true;
+}
+
+bool lldb_private::formatters::NSURLSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ llvm::StringRef class_name = descriptor->GetClassName().GetStringRef();
+
+ if (!class_name.equals("NSURL"))
+ return false;
+
+ uint64_t offset_text = ptr_size + ptr_size +
+ 8; // ISA + pointer + 8 bytes of data (even on 32bit)
+ uint64_t offset_base = offset_text + ptr_size;
+ CompilerType type(valobj.GetCompilerType());
+ ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true));
+ ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true));
+ if (!text)
+ return false;
+ if (text->GetValueAsUnsigned(0) == 0)
+ return false;
+ StreamString summary;
+ if (!NSStringSummaryProvider(*text, summary, options))
+ return false;
+ if (base && base->GetValueAsUnsigned(0)) {
+ std::string summary_str = summary.GetString();
+
+ if (!summary_str.empty())
+ summary_str.pop_back();
+ summary_str += " -- ";
+ StreamString base_summary;
+ if (NSURLSummaryProvider(*base, base_summary, options) &&
+ !base_summary.Empty()) {
+ llvm::StringRef base_str = base_summary.GetString();
+ if (base_str.size() > 2)
+ base_str = base_str.drop_front(2);
+ summary_str += base_str;
+ }
+ summary.Clear();
+ summary.PutCString(summary_str);
+ }
+ if (!summary.Empty()) {
+ stream.PutCString(summary.GetString());
+ return true;
+ }
+
+ return false;
+}
+
+/// Bias value for tagged pointer exponents.
+/// Recommended values:
+/// 0x3e3: encodes all dates between distantPast and distantFuture
+/// except for the range within about 1e-28 second of the reference date.
+/// 0x3ef: encodes all dates for a few million years beyond distantPast and
+/// distantFuture, except within about 1e-25 second of the reference date.
+const int TAGGED_DATE_EXPONENT_BIAS = 0x3ef;
+
+struct DoubleBits {
+ uint64_t fraction : 52; // unsigned
+ uint64_t exponent : 11; // signed
+ uint64_t sign : 1;
+};
+
+struct TaggedDoubleBits {
+ uint64_t fraction : 52; // unsigned
+ uint64_t exponent : 7; // signed
+ uint64_t sign : 1;
+ uint64_t unused : 4; // placeholder for pointer tag bits
+};
+
+static uint64_t decodeExponent(uint64_t exp) {
+ // Tagged exponent field is 7-bit signed. Sign-extend the value to 64 bits
+ // before performing arithmetic.
+ return llvm::SignExtend64<7>(exp) + TAGGED_DATE_EXPONENT_BIAS;
+}
+
+static double decodeTaggedTimeInterval(uint64_t encodedTimeInterval) {
+ if (encodedTimeInterval == 0)
+ return 0.0;
+ if (encodedTimeInterval == std::numeric_limits<uint64_t>::max())
+ return (uint64_t)-0.0;
+
+ TaggedDoubleBits encodedBits =
+ llvm::bit_cast<TaggedDoubleBits>(encodedTimeInterval);
+ assert(encodedBits.unused == 0);
+
+ // Sign and fraction are represented exactly.
+ // Exponent is encoded.
+ DoubleBits decodedBits;
+ decodedBits.sign = encodedBits.sign;
+ decodedBits.fraction = encodedBits.fraction;
+ decodedBits.exponent = decodeExponent(encodedBits.exponent);
+
+ return llvm::bit_cast<double>(decodedBits);
+}
+
+bool lldb_private::formatters::NSDateSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ uint64_t date_value_bits = 0;
+ double date_value = 0.0;
+
+ ConstString class_name = descriptor->GetClassName();
+
+ static const ConstString g_NSDate("NSDate");
+ static const ConstString g___NSDate("__NSDate");
+ static const ConstString g___NSTaggedDate("__NSTaggedDate");
+ static const ConstString g_NSCalendarDate("NSCalendarDate");
+
+ if (class_name.IsEmpty())
+ return false;
+
+ uint64_t info_bits = 0, value_bits = 0;
+ if ((class_name == g_NSDate) || (class_name == g___NSDate) ||
+ (class_name == g___NSTaggedDate)) {
+ if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits)) {
+ date_value_bits = ((value_bits << 8) | (info_bits << 4));
+ memcpy(&date_value, &date_value_bits, sizeof(date_value_bits));
+ } else {
+ llvm::Triple triple(
+ process_sp->GetTarget().GetArchitecture().GetTriple());
+ uint32_t delta =
+ (triple.isWatchOS() && triple.isWatchABI()) ? 8 : ptr_size;
+ Status error;
+ date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(
+ valobj_addr + delta, 8, 0, error);
+ memcpy(&date_value, &date_value_bits, sizeof(date_value_bits));
+ if (error.Fail())
+ return false;
+ }
+ } else if (class_name == g_NSCalendarDate) {
+ Status error;
+ date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(
+ valobj_addr + 2 * ptr_size, 8, 0, error);
+ memcpy(&date_value, &date_value_bits, sizeof(date_value_bits));
+ if (error.Fail())
+ return false;
+ } else
+ return false;
+
+ if (date_value == -63114076800) {
+ stream.Printf("0001-12-30 00:00:00 +0000");
+ return true;
+ }
+
+ // Accomodate for the __NSTaggedDate format introduced in Foundation 1600.
+ if (class_name == g___NSTaggedDate) {
+ auto *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
+ ObjCLanguageRuntime::Get(*process_sp));
+ if (runtime && runtime->GetFoundationVersion() >= 1600)
+ date_value = decodeTaggedTimeInterval(value_bits << 4);
+ }
+
+ // this snippet of code assumes that time_t == seconds since Jan-1-1970 this
+ // is generally true and POSIXly happy, but might break if a library vendor
+ // decides to get creative
+ time_t epoch = GetOSXEpoch();
+ epoch = epoch + (time_t)date_value;
+ tm *tm_date = gmtime(&epoch);
+ if (!tm_date)
+ return false;
+ std::string buffer(1024, 0);
+ if (strftime(&buffer[0], 1023, "%Z", tm_date) == 0)
+ return false;
+ stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year + 1900,
+ tm_date->tm_mon + 1, tm_date->tm_mday, tm_date->tm_hour,
+ tm_date->tm_min, tm_date->tm_sec, buffer.c_str());
+ return true;
+}
+
+bool lldb_private::formatters::ObjCClassSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptorFromISA(valobj.GetValueAsUnsigned(0)));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ ConstString class_name = descriptor->GetClassName();
+
+ if (class_name.IsEmpty())
+ return false;
+
+ if (ConstString cs =
+ Mangled(class_name).GetDemangledName(lldb::eLanguageTypeUnknown))
+ class_name = cs;
+
+ stream.Printf("%s", class_name.AsCString("<unknown class>"));
+ return true;
+}
+
+class ObjCClassSyntheticChildrenFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ ObjCClassSyntheticChildrenFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp) {}
+
+ ~ObjCClassSyntheticChildrenFrontEnd() override = default;
+
+ size_t CalculateNumChildren() override { return 0; }
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override {
+ return lldb::ValueObjectSP();
+ }
+
+ bool Update() override { return false; }
+
+ bool MightHaveChildren() override { return false; }
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ return UINT32_MAX;
+ }
+};
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::ObjCClassSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp);
+}
+
+template <bool needs_at>
+bool lldb_private::formatters::NSDataSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ bool is_64bit = (process_sp->GetAddressByteSize() == 8);
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ uint64_t value = 0;
+
+ llvm::StringRef class_name = descriptor->GetClassName().GetCString();
+
+ if (class_name.empty())
+ return false;
+
+ bool isNSConcreteData = class_name == "NSConcreteData";
+ bool isNSConcreteMutableData = class_name == "NSConcreteMutableData";
+ bool isNSCFData = class_name == "__NSCFData";
+ if (isNSConcreteData || isNSConcreteMutableData || isNSCFData) {
+ uint32_t offset;
+ if (isNSConcreteData)
+ offset = is_64bit ? 8 : 4;
+ else
+ offset = is_64bit ? 16 : 8;
+
+ Status error;
+ value = process_sp->ReadUnsignedIntegerFromMemory(
+ valobj_addr + offset, is_64bit ? 8 : 4, 0, error);
+ if (error.Fail())
+ return false;
+ } else if (class_name == "_NSInlineData") {
+ uint32_t offset = (is_64bit ? 8 : 4);
+ Status error;
+ value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + offset, 2,
+ 0, error);
+ if (error.Fail())
+ return false;
+ } else if (class_name == "_NSZeroData") {
+ value = 0;
+ } else
+ return false;
+
+ stream.Printf("%s%" PRIu64 " byte%s%s", (needs_at ? "@\"" : ""), value,
+ (value != 1 ? "s" : ""), (needs_at ? "\"" : ""));
+
+ return true;
+}
+
+bool lldb_private::formatters::ObjCBOOLSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ const uint32_t type_info = valobj.GetCompilerType().GetTypeInfo();
+
+ ValueObjectSP real_guy_sp = valobj.GetSP();
+
+ if (type_info & eTypeIsPointer) {
+ Status err;
+ real_guy_sp = valobj.Dereference(err);
+ if (err.Fail() || !real_guy_sp)
+ return false;
+ } else if (type_info & eTypeIsReference) {
+ real_guy_sp = valobj.GetChildAtIndex(0, true);
+ if (!real_guy_sp)
+ return false;
+ }
+ uint8_t value = (real_guy_sp->GetValueAsUnsigned(0) & 0xFF);
+ switch (value) {
+ case 0:
+ stream.Printf("NO");
+ break;
+ case 1:
+ stream.Printf("YES");
+ break;
+ default:
+ stream.Printf("%u", value);
+ break;
+ }
+ return true;
+}
+
+bool lldb_private::formatters::ObjCBooleanSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ lldb::addr_t valobj_ptr_value =
+ valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+ if (valobj_ptr_value == LLDB_INVALID_ADDRESS)
+ return false;
+
+ ProcessSP process_sp(valobj.GetProcessSP());
+ if (!process_sp)
+ return false;
+
+ if (AppleObjCRuntime *objc_runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
+ ObjCLanguageRuntime::Get(*process_sp))) {
+ lldb::addr_t cf_true = LLDB_INVALID_ADDRESS,
+ cf_false = LLDB_INVALID_ADDRESS;
+ objc_runtime->GetValuesForGlobalCFBooleans(cf_true, cf_false);
+ if (valobj_ptr_value == cf_true) {
+ stream.PutCString("YES");
+ return true;
+ }
+ if (valobj_ptr_value == cf_false) {
+ stream.PutCString("NO");
+ return true;
+ }
+ }
+
+ return false;
+}
+
+template <bool is_sel_ptr>
+bool lldb_private::formatters::ObjCSELSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ lldb::ValueObjectSP valobj_sp;
+
+ CompilerType charstar(valobj.GetCompilerType()
+ .GetBasicTypeFromAST(eBasicTypeChar)
+ .GetPointerType());
+
+ if (!charstar)
+ return false;
+
+ ExecutionContext exe_ctx(valobj.GetExecutionContextRef());
+
+ if (is_sel_ptr) {
+ lldb::addr_t data_address = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+ if (data_address == LLDB_INVALID_ADDRESS)
+ return false;
+ valobj_sp = ValueObject::CreateValueObjectFromAddress("text", data_address,
+ exe_ctx, charstar);
+ } else {
+ DataExtractor data;
+ Status error;
+ valobj.GetData(data, error);
+ if (error.Fail())
+ return false;
+ valobj_sp =
+ ValueObject::CreateValueObjectFromData("text", data, exe_ctx, charstar);
+ }
+
+ if (!valobj_sp)
+ return false;
+
+ stream.Printf("%s", valobj_sp->GetSummaryAsCString());
+ return true;
+}
+
+// POSIX has an epoch on Jan-1-1970, but Cocoa prefers Jan-1-2001
+// this call gives the POSIX equivalent of the Cocoa epoch
+time_t lldb_private::formatters::GetOSXEpoch() {
+ static time_t epoch = 0;
+ if (!epoch) {
+#ifndef _WIN32
+ tzset();
+ tm tm_epoch;
+ tm_epoch.tm_sec = 0;
+ tm_epoch.tm_hour = 0;
+ tm_epoch.tm_min = 0;
+ tm_epoch.tm_mon = 0;
+ tm_epoch.tm_mday = 1;
+ tm_epoch.tm_year = 2001 - 1900;
+ tm_epoch.tm_isdst = -1;
+ tm_epoch.tm_gmtoff = 0;
+ tm_epoch.tm_zone = nullptr;
+ epoch = timegm(&tm_epoch);
+#endif
+ }
+ return epoch;
+}
+
+template bool lldb_private::formatters::NSDataSummaryProvider<true>(
+ ValueObject &, Stream &, const TypeSummaryOptions &);
+
+template bool lldb_private::formatters::NSDataSummaryProvider<false>(
+ ValueObject &, Stream &, const TypeSummaryOptions &);
+
+template bool lldb_private::formatters::ObjCSELSummaryProvider<true>(
+ ValueObject &, Stream &, const TypeSummaryOptions &);
+
+template bool lldb_private::formatters::ObjCSELSummaryProvider<false>(
+ ValueObject &, Stream &, const TypeSummaryOptions &);
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/Cocoa.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/Cocoa.h
new file mode 100644
index 000000000000..388e6f03aa0f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/Cocoa.h
@@ -0,0 +1,116 @@
+//===-- Cocoa.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 liblldb_Cocoa_h_
+#define liblldb_Cocoa_h_
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Utility/Stream.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+namespace lldb_private {
+namespace formatters {
+bool NSIndexSetSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool NSArraySummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+template <bool needs_at>
+bool NSDataSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool NSNumberSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool NSDecimalNumberSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool NSNotificationSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool NSTimeZoneSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool NSMachPortSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool NSDateSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool NSBundleSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool NSURLSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+extern template bool NSDataSummaryProvider<true>(ValueObject &, Stream &,
+ const TypeSummaryOptions &);
+
+extern template bool NSDataSummaryProvider<false>(ValueObject &, Stream &,
+ const TypeSummaryOptions &);
+
+SyntheticChildrenFrontEnd *
+NSArraySyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP);
+
+SyntheticChildrenFrontEnd *
+NSIndexPathSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+bool ObjCClassSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+SyntheticChildrenFrontEnd *
+ObjCClassSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP);
+
+bool ObjCBOOLSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool ObjCBooleanSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+template <bool is_sel_ptr>
+bool ObjCSELSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+extern template bool ObjCSELSummaryProvider<true>(ValueObject &, Stream &,
+ const TypeSummaryOptions &);
+
+extern template bool ObjCSELSummaryProvider<false>(ValueObject &, Stream &,
+ const TypeSummaryOptions &);
+
+bool NSError_SummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool NSException_SummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+SyntheticChildrenFrontEnd *
+NSErrorSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP valobj_sp);
+
+SyntheticChildrenFrontEnd *
+NSExceptionSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP valobj_sp);
+
+class NSArray_Additionals {
+public:
+ static std::map<ConstString, CXXFunctionSummaryFormat::Callback> &
+ GetAdditionalSummaries();
+
+ static std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> &
+ GetAdditionalSynthetics();
+};
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // liblldb_Cocoa_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CoreMedia.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CoreMedia.cpp
new file mode 100644
index 000000000000..d19290ec56fb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CoreMedia.cpp
@@ -0,0 +1,95 @@
+//===-- CoreMedia.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 "CoreMedia.h"
+
+#include "lldb/Utility/Flags.h"
+
+#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Target/Target.h"
+#include <inttypes.h>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+bool lldb_private::formatters::CMTimeSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ CompilerType type = valobj.GetCompilerType();
+ if (!type.IsValid())
+ return false;
+
+ TypeSystem *type_system =
+ valobj.GetExecutionContextRef()
+ .GetTargetSP()
+ ->GetScratchTypeSystemForLanguage(nullptr, lldb::eLanguageTypeC);
+ if (!type_system)
+ return false;
+
+ // fetch children by offset to compensate for potential lack of debug info
+ auto int64_ty =
+ type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingSint, 64);
+ auto int32_ty =
+ type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingSint, 32);
+
+ auto value_sp(valobj.GetSyntheticChildAtOffset(0, int64_ty, true));
+ auto timescale_sp(valobj.GetSyntheticChildAtOffset(8, int32_ty, true));
+ auto flags_sp(valobj.GetSyntheticChildAtOffset(12, int32_ty, true));
+
+ if (!value_sp || !timescale_sp || !flags_sp)
+ return false;
+
+ auto value = value_sp->GetValueAsUnsigned(0);
+ auto timescale = (int32_t)timescale_sp->GetValueAsUnsigned(
+ 0); // the timescale specifies the fraction of a second each unit in the
+ // numerator occupies
+ auto flags = Flags(flags_sp->GetValueAsUnsigned(0) &
+ 0x00000000000000FF); // the flags I need sit in the LSB
+
+ const unsigned int FlagPositiveInf = 4;
+ const unsigned int FlagNegativeInf = 8;
+ const unsigned int FlagIndefinite = 16;
+
+ if (flags.AnySet(FlagIndefinite)) {
+ stream.Printf("indefinite");
+ return true;
+ }
+
+ if (flags.AnySet(FlagPositiveInf)) {
+ stream.Printf("+oo");
+ return true;
+ }
+
+ if (flags.AnySet(FlagNegativeInf)) {
+ stream.Printf("-oo");
+ return true;
+ }
+
+ if (timescale == 0)
+ return false;
+
+ switch (timescale) {
+ case 0:
+ return false;
+ case 1:
+ stream.Printf("%" PRId64 " seconds", value);
+ return true;
+ case 2:
+ stream.Printf("%" PRId64 " half seconds", value);
+ return true;
+ case 3:
+ stream.Printf("%" PRId64 " third%sof a second", value,
+ value == 1 ? " " : "s ");
+ return true;
+ default:
+ stream.Printf("%" PRId64 " %" PRId32 "th%sof a second", value, timescale,
+ value == 1 ? " " : "s ");
+ return true;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CoreMedia.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CoreMedia.h
new file mode 100644
index 000000000000..79abb67b9d7e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CoreMedia.h
@@ -0,0 +1,25 @@
+//===-- CoreMedia.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 liblldb_CoreMedia_h_
+#define liblldb_CoreMedia_h_
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/Utility/Stream.h"
+
+namespace lldb_private {
+namespace formatters {
+
+bool CMTimeSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // liblldb_CF_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSArray.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSArray.cpp
new file mode 100644
index 000000000000..404dabf2870c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSArray.cpp
@@ -0,0 +1,870 @@
+//===-- NSArray.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 "clang/AST/ASTContext.h"
+
+#include "Cocoa.h"
+
+#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Expression/FunctionCaller.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+namespace lldb_private {
+namespace formatters {
+std::map<ConstString, CXXFunctionSummaryFormat::Callback> &
+NSArray_Additionals::GetAdditionalSummaries() {
+ static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map;
+ return g_map;
+}
+
+std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> &
+NSArray_Additionals::GetAdditionalSynthetics() {
+ static std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback>
+ g_map;
+ return g_map;
+}
+
+class NSArrayMSyntheticFrontEndBase : public SyntheticChildrenFrontEnd {
+public:
+ NSArrayMSyntheticFrontEndBase(lldb::ValueObjectSP valobj_sp);
+
+ ~NSArrayMSyntheticFrontEndBase() override = default;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override = 0;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+protected:
+ virtual lldb::addr_t GetDataAddress() = 0;
+
+ virtual uint64_t GetUsedCount() = 0;
+
+ virtual uint64_t GetOffset() = 0;
+
+ virtual uint64_t GetSize() = 0;
+
+ ExecutionContextRef m_exe_ctx_ref;
+ uint8_t m_ptr_size;
+ CompilerType m_id_type;
+};
+
+template <typename D32, typename D64>
+class GenericNSArrayMSyntheticFrontEnd : public NSArrayMSyntheticFrontEndBase {
+public:
+ GenericNSArrayMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~GenericNSArrayMSyntheticFrontEnd() override;
+
+ bool Update() override;
+
+protected:
+ lldb::addr_t GetDataAddress() override;
+
+ uint64_t GetUsedCount() override;
+
+ uint64_t GetOffset() override;
+
+ uint64_t GetSize() override;
+
+private:
+ D32 *m_data_32;
+ D64 *m_data_64;
+};
+
+namespace Foundation109 {
+ struct DataDescriptor_32 {
+ uint32_t _used;
+ uint32_t _priv1 : 2;
+ uint32_t _size : 30;
+ uint32_t _priv2 : 2;
+ uint32_t _offset : 30;
+ uint32_t _priv3;
+ uint32_t _data;
+ };
+
+ struct DataDescriptor_64 {
+ uint64_t _used;
+ uint64_t _priv1 : 2;
+ uint64_t _size : 62;
+ uint64_t _priv2 : 2;
+ uint64_t _offset : 62;
+ uint32_t _priv3;
+ uint64_t _data;
+ };
+
+ using NSArrayMSyntheticFrontEnd =
+ GenericNSArrayMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
+}
+
+namespace Foundation1010 {
+ struct DataDescriptor_32 {
+ uint32_t _used;
+ uint32_t _offset;
+ uint32_t _size : 28;
+ uint64_t _priv1 : 4;
+ uint32_t _priv2;
+ uint32_t _data;
+ };
+
+ struct DataDescriptor_64 {
+ uint64_t _used;
+ uint64_t _offset;
+ uint64_t _size : 60;
+ uint64_t _priv1 : 4;
+ uint32_t _priv2;
+ uint64_t _data;
+ };
+
+ using NSArrayMSyntheticFrontEnd =
+ GenericNSArrayMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
+}
+
+namespace Foundation1428 {
+ struct DataDescriptor_32 {
+ uint32_t _used;
+ uint32_t _offset;
+ uint32_t _size;
+ uint32_t _data;
+ };
+
+ struct DataDescriptor_64 {
+ uint64_t _used;
+ uint64_t _offset;
+ uint64_t _size;
+ uint64_t _data;
+ };
+
+ using NSArrayMSyntheticFrontEnd =
+ GenericNSArrayMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
+}
+
+namespace Foundation1437 {
+ template <typename PtrType>
+ struct DataDescriptor {
+ PtrType _cow;
+ // __deque
+ PtrType _data;
+ uint32_t _offset;
+ uint32_t _size;
+ uint32_t _muts;
+ uint32_t _used;
+ };
+
+ using NSArrayMSyntheticFrontEnd =
+ GenericNSArrayMSyntheticFrontEnd<
+ DataDescriptor<uint32_t>, DataDescriptor<uint64_t>>;
+
+ template <typename DD>
+ uint64_t
+ __NSArrayMSize_Impl(lldb_private::Process &process,
+ lldb::addr_t valobj_addr, Status &error) {
+ const lldb::addr_t start_of_descriptor =
+ valobj_addr + process.GetAddressByteSize();
+ DD descriptor = DD();
+ process.ReadMemory(start_of_descriptor, &descriptor,
+ sizeof(descriptor), error);
+ if (error.Fail()) {
+ return 0;
+ }
+ return descriptor._used;
+ }
+
+ uint64_t
+ __NSArrayMSize(lldb_private::Process &process, lldb::addr_t valobj_addr,
+ Status &error) {
+ if (process.GetAddressByteSize() == 4) {
+ return __NSArrayMSize_Impl<DataDescriptor<uint32_t>>(process, valobj_addr,
+ error);
+ } else {
+ return __NSArrayMSize_Impl<DataDescriptor<uint64_t>>(process, valobj_addr,
+ error);
+ }
+ }
+
+}
+
+namespace CallStackArray {
+struct DataDescriptor_32 {
+ uint32_t _data;
+ uint32_t _used;
+ uint32_t _offset;
+ const uint32_t _size = 0;
+};
+
+struct DataDescriptor_64 {
+ uint64_t _data;
+ uint64_t _used;
+ uint64_t _offset;
+ const uint64_t _size = 0;
+};
+
+using NSCallStackArraySyntheticFrontEnd =
+ GenericNSArrayMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
+} // namespace CallStackArray
+
+template <typename D32, typename D64, bool Inline>
+class GenericNSArrayISyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ GenericNSArrayISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~GenericNSArrayISyntheticFrontEnd() override;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ ExecutionContextRef m_exe_ctx_ref;
+ uint8_t m_ptr_size;
+
+ D32 *m_data_32;
+ D64 *m_data_64;
+ CompilerType m_id_type;
+};
+
+namespace Foundation1300 {
+ struct IDD32 {
+ uint32_t used;
+ uint32_t list;
+ };
+
+ struct IDD64 {
+ uint64_t used;
+ uint64_t list;
+ };
+
+ using NSArrayISyntheticFrontEnd =
+ GenericNSArrayISyntheticFrontEnd<IDD32, IDD64, true>;
+}
+
+namespace Foundation1430 {
+ using NSArrayISyntheticFrontEnd =
+ Foundation1428::NSArrayMSyntheticFrontEnd;
+}
+
+namespace Foundation1436 {
+ struct IDD32 {
+ uint32_t used;
+ uint32_t list; // in Inline cases, this is the first element
+ };
+
+ struct IDD64 {
+ uint64_t used;
+ uint64_t list; // in Inline cases, this is the first element
+ };
+
+ using NSArrayI_TransferSyntheticFrontEnd =
+ GenericNSArrayISyntheticFrontEnd<IDD32, IDD64, false>;
+
+ using NSArrayISyntheticFrontEnd =
+ GenericNSArrayISyntheticFrontEnd<IDD32, IDD64, true>;
+
+ using NSFrozenArrayMSyntheticFrontEnd =
+ Foundation1437::NSArrayMSyntheticFrontEnd;
+
+ uint64_t
+ __NSFrozenArrayMSize(lldb_private::Process &process, lldb::addr_t valobj_addr,
+ Status &error) {
+ return Foundation1437::__NSArrayMSize(process, valobj_addr, error);
+ }
+}
+
+class NSArray0SyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ NSArray0SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~NSArray0SyntheticFrontEnd() override = default;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+};
+
+class NSArray1SyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ NSArray1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~NSArray1SyntheticFrontEnd() override = default;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+};
+} // namespace formatters
+} // namespace lldb_private
+
+bool lldb_private::formatters::NSArraySummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ static ConstString g_TypeHint("NSArray");
+
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ uint64_t value = 0;
+
+ ConstString class_name(descriptor->GetClassName());
+
+ static const ConstString g_NSArrayI("__NSArrayI");
+ static const ConstString g_NSArrayM("__NSArrayM");
+ static const ConstString g_NSArrayI_Transfer("__NSArrayI_Transfer");
+ static const ConstString g_NSFrozenArrayM("__NSFrozenArrayM");
+ static const ConstString g_NSArray0("__NSArray0");
+ static const ConstString g_NSArray1("__NSSingleObjectArrayI");
+ static const ConstString g_NSArrayCF("__NSCFArray");
+ static const ConstString g_NSArrayMLegacy("__NSArrayM_Legacy");
+ static const ConstString g_NSArrayMImmutable("__NSArrayM_Immutable");
+ static const ConstString g_NSCallStackArray("_NSCallStackArray");
+
+ if (class_name.IsEmpty())
+ return false;
+
+ if (class_name == g_NSArrayI) {
+ Status error;
+ value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
+ ptr_size, 0, error);
+ if (error.Fail())
+ return false;
+ } else if (class_name == g_NSArrayM) {
+ AppleObjCRuntime *apple_runtime =
+ llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
+ Status error;
+ if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
+ value = Foundation1437::__NSArrayMSize(*process_sp, valobj_addr, error);
+ } else {
+ value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
+ ptr_size, 0, error);
+ }
+ if (error.Fail())
+ return false;
+ } else if (class_name == g_NSArrayI_Transfer) {
+ Status error;
+ value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
+ ptr_size, 0, error);
+ if (error.Fail())
+ return false;
+ } else if (class_name == g_NSFrozenArrayM) {
+ Status error;
+ value = Foundation1436::__NSFrozenArrayMSize(*process_sp, valobj_addr, error);
+ if (error.Fail())
+ return false;
+ } else if (class_name == g_NSArrayMLegacy) {
+ Status error;
+ value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
+ ptr_size, 0, error);
+ if (error.Fail())
+ return false;
+ } else if (class_name == g_NSArrayMImmutable) {
+ Status error;
+ value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
+ ptr_size, 0, error);
+ if (error.Fail())
+ return false;
+ } else if (class_name == g_NSArray0) {
+ value = 0;
+ } else if (class_name == g_NSArray1) {
+ value = 1;
+ } else if (class_name == g_NSArrayCF || class_name == g_NSCallStackArray) {
+ // __NSCFArray and _NSCallStackArray store the number of elements as a
+ // pointer-sized value at offset `2 * ptr_size`.
+ Status error;
+ value = process_sp->ReadUnsignedIntegerFromMemory(
+ valobj_addr + 2 * ptr_size, ptr_size, 0, error);
+ if (error.Fail())
+ return false;
+ } else {
+ auto &map(NSArray_Additionals::GetAdditionalSummaries());
+ auto iter = map.find(class_name), end = map.end();
+ if (iter != end)
+ return iter->second(valobj, stream, options);
+ else
+ return false;
+ }
+
+ std::string prefix, suffix;
+ if (Language *language = Language::FindPlugin(options.GetLanguage())) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ stream.Printf("%s%" PRIu64 " %s%s%s", prefix.c_str(), value, "element",
+ value == 1 ? "" : "s", suffix.c_str());
+ return true;
+}
+
+lldb_private::formatters::NSArrayMSyntheticFrontEndBase::NSArrayMSyntheticFrontEndBase(
+ lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
+ m_id_type() {
+ if (valobj_sp) {
+ clang::ASTContext *ast = valobj_sp->GetExecutionContextRef()
+ .GetTargetSP()
+ ->GetScratchClangASTContext()
+ ->getASTContext();
+ if (ast)
+ m_id_type = CompilerType(ast, ast->ObjCBuiltinIdTy);
+ if (valobj_sp->GetProcessSP())
+ m_ptr_size = valobj_sp->GetProcessSP()->GetAddressByteSize();
+ }
+}
+
+template <typename D32, typename D64>
+lldb_private::formatters::
+ GenericNSArrayMSyntheticFrontEnd<D32, D64>::
+ GenericNSArrayMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : NSArrayMSyntheticFrontEndBase(valobj_sp), m_data_32(nullptr),
+ m_data_64(nullptr) {}
+
+size_t
+lldb_private::formatters::NSArrayMSyntheticFrontEndBase::CalculateNumChildren() {
+ return GetUsedCount();
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::NSArrayMSyntheticFrontEndBase::GetChildAtIndex(
+ size_t idx) {
+ if (idx >= CalculateNumChildren())
+ return lldb::ValueObjectSP();
+ lldb::addr_t object_at_idx = GetDataAddress();
+ size_t pyhs_idx = idx;
+ pyhs_idx += GetOffset();
+ if (GetSize() <= pyhs_idx)
+ pyhs_idx -= GetSize();
+ object_at_idx += (pyhs_idx * m_ptr_size);
+ StreamString idx_name;
+ idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ return CreateValueObjectFromAddress(idx_name.GetString(), object_at_idx,
+ m_exe_ctx_ref, m_id_type);
+}
+
+template <typename D32, typename D64>
+bool
+lldb_private::formatters::
+ GenericNSArrayMSyntheticFrontEnd<D32, D64>::Update() {
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ m_ptr_size = 0;
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+ if (!valobj_sp)
+ return false;
+ m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
+ Status error;
+ error.Clear();
+ lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
+ if (!process_sp)
+ return false;
+ m_ptr_size = process_sp->GetAddressByteSize();
+ uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
+ if (m_ptr_size == 4) {
+ m_data_32 = new D32();
+ process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
+ error);
+ } else {
+ m_data_64 = new D64();
+ process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
+ error);
+ }
+ if (error.Fail())
+ return false;
+ return false;
+}
+
+bool
+lldb_private::formatters::NSArrayMSyntheticFrontEndBase::MightHaveChildren() {
+ return true;
+}
+
+size_t
+lldb_private::formatters::NSArrayMSyntheticFrontEndBase::GetIndexOfChildWithName(
+ ConstString name) {
+ const char *item_name = name.GetCString();
+ uint32_t idx = ExtractIndexFromString(item_name);
+ if (idx < UINT32_MAX && idx >= CalculateNumChildren())
+ return UINT32_MAX;
+ return idx;
+}
+
+template <typename D32, typename D64>
+lldb_private::formatters::
+ GenericNSArrayMSyntheticFrontEnd<D32, D64>::
+ ~GenericNSArrayMSyntheticFrontEnd() {
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+}
+
+template <typename D32, typename D64>
+lldb::addr_t
+lldb_private::formatters::
+ GenericNSArrayMSyntheticFrontEnd<D32, D64>::
+ GenericNSArrayMSyntheticFrontEnd::GetDataAddress() {
+ if (!m_data_32 && !m_data_64)
+ return LLDB_INVALID_ADDRESS;
+ return m_data_32 ? m_data_32->_data : m_data_64->_data;
+}
+
+template <typename D32, typename D64>
+uint64_t
+lldb_private::formatters::
+ GenericNSArrayMSyntheticFrontEnd<D32, D64>::
+ GenericNSArrayMSyntheticFrontEnd::GetUsedCount() {
+ if (!m_data_32 && !m_data_64)
+ return 0;
+ return m_data_32 ? m_data_32->_used : m_data_64->_used;
+}
+
+template <typename D32, typename D64>
+uint64_t
+lldb_private::formatters::
+ GenericNSArrayMSyntheticFrontEnd<D32, D64>::
+ GenericNSArrayMSyntheticFrontEnd::GetOffset() {
+ if (!m_data_32 && !m_data_64)
+ return 0;
+ return m_data_32 ? m_data_32->_offset : m_data_64->_offset;
+}
+
+template <typename D32, typename D64>
+uint64_t
+lldb_private::formatters::
+ GenericNSArrayMSyntheticFrontEnd<D32, D64>::
+ GenericNSArrayMSyntheticFrontEnd::GetSize() {
+ if (!m_data_32 && !m_data_64)
+ return 0;
+ return m_data_32 ? m_data_32->_size : m_data_64->_size;
+}
+
+template <typename D32, typename D64, bool Inline>
+lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
+ GenericNSArrayISyntheticFrontEnd(
+ lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
+ m_data_32(nullptr), m_data_64(nullptr) {
+ if (valobj_sp) {
+ CompilerType type = valobj_sp->GetCompilerType();
+ if (type) {
+ ClangASTContext *ast = valobj_sp->GetExecutionContextRef()
+ .GetTargetSP()
+ ->GetScratchClangASTContext();
+ if (ast)
+ m_id_type = CompilerType(ast->getASTContext(),
+ ast->getASTContext()->ObjCBuiltinIdTy);
+ }
+ }
+}
+
+template <typename D32, typename D64, bool Inline>
+lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
+ ~GenericNSArrayISyntheticFrontEnd() {
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+}
+
+template <typename D32, typename D64, bool Inline>
+size_t
+lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
+ GetIndexOfChildWithName(ConstString name) {
+ const char *item_name = name.GetCString();
+ uint32_t idx = ExtractIndexFromString(item_name);
+ if (idx < UINT32_MAX && idx >= CalculateNumChildren())
+ return UINT32_MAX;
+ return idx;
+}
+
+template <typename D32, typename D64, bool Inline>
+size_t
+lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
+ CalculateNumChildren() {
+ return m_data_32 ? m_data_32->used : m_data_64->used;
+}
+
+template <typename D32, typename D64, bool Inline>
+bool
+lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
+ Update() {
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ m_ptr_size = 0;
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+ if (!valobj_sp)
+ return false;
+ m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
+ Status error;
+ error.Clear();
+ lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
+ if (!process_sp)
+ return false;
+ m_ptr_size = process_sp->GetAddressByteSize();
+ uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
+ if (m_ptr_size == 4) {
+ m_data_32 = new D32();
+ process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
+ error);
+ } else {
+ m_data_64 = new D64();
+ process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
+ error);
+ }
+ if (error.Fail())
+ return false;
+ return false;
+}
+
+template <typename D32, typename D64, bool Inline>
+bool
+lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
+ MightHaveChildren() {
+ return true;
+}
+
+template <typename D32, typename D64, bool Inline>
+lldb::ValueObjectSP
+lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>::
+ GetChildAtIndex(size_t idx) {
+ if (idx >= CalculateNumChildren())
+ return lldb::ValueObjectSP();
+ lldb::addr_t object_at_idx;
+ if (Inline) {
+ object_at_idx = m_backend.GetSP()->GetValueAsUnsigned(0) + m_ptr_size;
+ object_at_idx += m_ptr_size == 4 ? sizeof(D32) : sizeof(D64); // skip the data header
+ object_at_idx -= m_ptr_size; // we treat the last entry in the data header as the first pointer
+ } else {
+ object_at_idx = m_data_32 ? m_data_32->list : m_data_64->list;
+ }
+ object_at_idx += (idx * m_ptr_size);
+
+ ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
+ if (!process_sp)
+ return lldb::ValueObjectSP();
+ Status error;
+ if (error.Fail())
+ return lldb::ValueObjectSP();
+ StreamString idx_name;
+ idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ return CreateValueObjectFromAddress(idx_name.GetString(), object_at_idx,
+ m_exe_ctx_ref, m_id_type);
+}
+
+lldb_private::formatters::NSArray0SyntheticFrontEnd::NSArray0SyntheticFrontEnd(
+ lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp) {}
+
+size_t
+lldb_private::formatters::NSArray0SyntheticFrontEnd::GetIndexOfChildWithName(
+ ConstString name) {
+ return UINT32_MAX;
+}
+
+size_t
+lldb_private::formatters::NSArray0SyntheticFrontEnd::CalculateNumChildren() {
+ return 0;
+}
+
+bool lldb_private::formatters::NSArray0SyntheticFrontEnd::Update() {
+ return false;
+}
+
+bool lldb_private::formatters::NSArray0SyntheticFrontEnd::MightHaveChildren() {
+ return false;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::NSArray0SyntheticFrontEnd::GetChildAtIndex(
+ size_t idx) {
+ return lldb::ValueObjectSP();
+}
+
+lldb_private::formatters::NSArray1SyntheticFrontEnd::NSArray1SyntheticFrontEnd(
+ lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp.get()) {}
+
+size_t
+lldb_private::formatters::NSArray1SyntheticFrontEnd::GetIndexOfChildWithName(
+ ConstString name) {
+ static const ConstString g_zero("[0]");
+
+ if (name == g_zero)
+ return 0;
+
+ return UINT32_MAX;
+}
+
+size_t
+lldb_private::formatters::NSArray1SyntheticFrontEnd::CalculateNumChildren() {
+ return 1;
+}
+
+bool lldb_private::formatters::NSArray1SyntheticFrontEnd::Update() {
+ return false;
+}
+
+bool lldb_private::formatters::NSArray1SyntheticFrontEnd::MightHaveChildren() {
+ return true;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::NSArray1SyntheticFrontEnd::GetChildAtIndex(
+ size_t idx) {
+ static const ConstString g_zero("[0]");
+
+ if (idx == 0) {
+ CompilerType id_type(
+ m_backend.GetTargetSP()->GetScratchClangASTContext()->GetBasicType(
+ lldb::eBasicTypeObjCID));
+ return m_backend.GetSyntheticChildAtOffset(
+ m_backend.GetProcessSP()->GetAddressByteSize(), id_type, true, g_zero);
+ }
+ return lldb::ValueObjectSP();
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::NSArraySyntheticFrontEndCreator(
+ CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
+ if (!valobj_sp)
+ return nullptr;
+
+ lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
+ if (!process_sp)
+ return nullptr;
+ AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
+ ObjCLanguageRuntime::Get(*process_sp));
+ if (!runtime)
+ return nullptr;
+
+ CompilerType valobj_type(valobj_sp->GetCompilerType());
+ Flags flags(valobj_type.GetTypeInfo());
+
+ if (flags.IsClear(eTypeIsPointer)) {
+ Status error;
+ valobj_sp = valobj_sp->AddressOf(error);
+ if (error.Fail() || !valobj_sp)
+ return nullptr;
+ }
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(*valobj_sp));
+
+ if (!descriptor || !descriptor->IsValid())
+ return nullptr;
+
+ ConstString class_name(descriptor->GetClassName());
+
+ static const ConstString g_NSArrayI("__NSArrayI");
+ static const ConstString g_NSArrayI_Transfer("__NSArrayI_Transfer");
+ static const ConstString g_NSFrozenArrayM("__NSFrozenArrayM");
+ static const ConstString g_NSArrayM("__NSArrayM");
+ static const ConstString g_NSArray0("__NSArray0");
+ static const ConstString g_NSArray1("__NSSingleObjectArrayI");
+ static const ConstString g_NSArrayMLegacy("__NSArrayM_Legacy");
+ static const ConstString g_NSArrayMImmutable("__NSArrayM_Immutable");
+ static const ConstString g_NSCallStackArray("_NSCallStackArray");
+
+ if (class_name.IsEmpty())
+ return nullptr;
+
+ if (class_name == g_NSArrayI) {
+ if (runtime->GetFoundationVersion() >= 1436)
+ return (new Foundation1436::NSArrayISyntheticFrontEnd(valobj_sp));
+ if (runtime->GetFoundationVersion() >= 1430)
+ return (new Foundation1430::NSArrayISyntheticFrontEnd(valobj_sp));
+ else
+ return (new Foundation1300::NSArrayISyntheticFrontEnd(valobj_sp));
+ } else if (class_name == g_NSArrayI_Transfer) {
+ return (new Foundation1436::NSArrayI_TransferSyntheticFrontEnd(valobj_sp));
+ } else if (class_name == g_NSArray0) {
+ } else if (class_name == g_NSFrozenArrayM) {
+ return (new Foundation1436::NSFrozenArrayMSyntheticFrontEnd(valobj_sp));
+ } else if (class_name == g_NSArray0) {
+ return (new NSArray0SyntheticFrontEnd(valobj_sp));
+ } else if (class_name == g_NSArray1) {
+ return (new NSArray1SyntheticFrontEnd(valobj_sp));
+ } else if (class_name == g_NSArrayM) {
+ if (runtime->GetFoundationVersion() >= 1437)
+ return (new Foundation1437::NSArrayMSyntheticFrontEnd(valobj_sp));
+ if (runtime->GetFoundationVersion() >= 1428)
+ return (new Foundation1428::NSArrayMSyntheticFrontEnd(valobj_sp));
+ if (runtime->GetFoundationVersion() >= 1100)
+ return (new Foundation1010::NSArrayMSyntheticFrontEnd(valobj_sp));
+ else
+ return (new Foundation109::NSArrayMSyntheticFrontEnd(valobj_sp));
+ } else if (class_name == g_NSCallStackArray) {
+ return (new CallStackArray::NSCallStackArraySyntheticFrontEnd(valobj_sp));
+ } else {
+ auto &map(NSArray_Additionals::GetAdditionalSynthetics());
+ auto iter = map.find(class_name), end = map.end();
+ if (iter != end)
+ return iter->second(synth, valobj_sp);
+ }
+
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp
new file mode 100644
index 000000000000..10f66c4a37f8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp
@@ -0,0 +1,1049 @@
+//===-- NSDictionary.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 <mutex>
+
+#include "clang/AST/DeclCXX.h"
+
+#include "NSDictionary.h"
+
+#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+NSDictionary_Additionals::AdditionalFormatterMatching::Prefix::Prefix(
+ ConstString p)
+ : m_prefix(p) {}
+
+bool NSDictionary_Additionals::AdditionalFormatterMatching::Prefix::Match(
+ ConstString class_name) {
+ return class_name.GetStringRef().startswith(m_prefix.GetStringRef());
+}
+
+NSDictionary_Additionals::AdditionalFormatterMatching::Full::Full(ConstString n)
+ : m_name(n) {}
+
+bool NSDictionary_Additionals::AdditionalFormatterMatching::Full::Match(
+ ConstString class_name) {
+ return (class_name == m_name);
+}
+
+NSDictionary_Additionals::AdditionalFormatters<
+ CXXFunctionSummaryFormat::Callback> &
+NSDictionary_Additionals::GetAdditionalSummaries() {
+ static AdditionalFormatters<CXXFunctionSummaryFormat::Callback> g_map;
+ return g_map;
+}
+
+NSDictionary_Additionals::AdditionalFormatters<
+ CXXSyntheticChildren::CreateFrontEndCallback> &
+NSDictionary_Additionals::GetAdditionalSynthetics() {
+ static AdditionalFormatters<CXXSyntheticChildren::CreateFrontEndCallback>
+ g_map;
+ return g_map;
+}
+
+static CompilerType GetLLDBNSPairType(TargetSP target_sp) {
+ CompilerType compiler_type;
+
+ ClangASTContext *target_ast_context = target_sp->GetScratchClangASTContext();
+
+ if (target_ast_context) {
+ ConstString g___lldb_autogen_nspair("__lldb_autogen_nspair");
+
+ compiler_type =
+ target_ast_context->GetTypeForIdentifier<clang::CXXRecordDecl>(
+ g___lldb_autogen_nspair);
+
+ if (!compiler_type) {
+ compiler_type = target_ast_context->CreateRecordType(
+ nullptr, lldb::eAccessPublic, g___lldb_autogen_nspair.GetCString(),
+ clang::TTK_Struct, lldb::eLanguageTypeC);
+
+ if (compiler_type) {
+ ClangASTContext::StartTagDeclarationDefinition(compiler_type);
+ CompilerType id_compiler_type =
+ target_ast_context->GetBasicType(eBasicTypeObjCID);
+ ClangASTContext::AddFieldToRecordType(
+ compiler_type, "key", id_compiler_type, lldb::eAccessPublic, 0);
+ ClangASTContext::AddFieldToRecordType(
+ compiler_type, "value", id_compiler_type, lldb::eAccessPublic, 0);
+ ClangASTContext::CompleteTagDeclarationDefinition(compiler_type);
+ }
+ }
+ }
+ return compiler_type;
+}
+
+namespace lldb_private {
+namespace formatters {
+class NSDictionaryISyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~NSDictionaryISyntheticFrontEnd() override;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ struct DataDescriptor_32 {
+ uint32_t _used : 26;
+ uint32_t _szidx : 6;
+ };
+
+ struct DataDescriptor_64 {
+ uint64_t _used : 58;
+ uint32_t _szidx : 6;
+ };
+
+ struct DictionaryItemDescriptor {
+ lldb::addr_t key_ptr;
+ lldb::addr_t val_ptr;
+ lldb::ValueObjectSP valobj_sp;
+ };
+
+ ExecutionContextRef m_exe_ctx_ref;
+ uint8_t m_ptr_size;
+ lldb::ByteOrder m_order;
+ DataDescriptor_32 *m_data_32;
+ DataDescriptor_64 *m_data_64;
+ lldb::addr_t m_data_ptr;
+ CompilerType m_pair_type;
+ std::vector<DictionaryItemDescriptor> m_children;
+};
+
+class NSDictionary1SyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~NSDictionary1SyntheticFrontEnd() override = default;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ ValueObjectSP m_pair;
+};
+
+template <typename D32, typename D64>
+class GenericNSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~GenericNSDictionaryMSyntheticFrontEnd() override;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ struct DictionaryItemDescriptor {
+ lldb::addr_t key_ptr;
+ lldb::addr_t val_ptr;
+ lldb::ValueObjectSP valobj_sp;
+ };
+
+ ExecutionContextRef m_exe_ctx_ref;
+ uint8_t m_ptr_size;
+ lldb::ByteOrder m_order;
+ D32 *m_data_32;
+ D64 *m_data_64;
+ CompilerType m_pair_type;
+ std::vector<DictionaryItemDescriptor> m_children;
+};
+
+namespace Foundation1100 {
+ class NSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+ public:
+ NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~NSDictionaryMSyntheticFrontEnd() override;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+ private:
+ struct DataDescriptor_32 {
+ uint32_t _used : 26;
+ uint32_t _kvo : 1;
+ uint32_t _size;
+ uint32_t _mutations;
+ uint32_t _objs_addr;
+ uint32_t _keys_addr;
+ };
+
+ struct DataDescriptor_64 {
+ uint64_t _used : 58;
+ uint32_t _kvo : 1;
+ uint64_t _size;
+ uint64_t _mutations;
+ uint64_t _objs_addr;
+ uint64_t _keys_addr;
+ };
+
+ struct DictionaryItemDescriptor {
+ lldb::addr_t key_ptr;
+ lldb::addr_t val_ptr;
+ lldb::ValueObjectSP valobj_sp;
+ };
+
+ ExecutionContextRef m_exe_ctx_ref;
+ uint8_t m_ptr_size;
+ lldb::ByteOrder m_order;
+ DataDescriptor_32 *m_data_32;
+ DataDescriptor_64 *m_data_64;
+ CompilerType m_pair_type;
+ std::vector<DictionaryItemDescriptor> m_children;
+ };
+}
+
+namespace Foundation1428 {
+ struct DataDescriptor_32 {
+ uint32_t _used : 26;
+ uint32_t _kvo : 1;
+ uint32_t _size;
+ uint32_t _buffer;
+ uint64_t GetSize() { return _size; }
+ };
+
+ struct DataDescriptor_64 {
+ uint64_t _used : 58;
+ uint32_t _kvo : 1;
+ uint64_t _size;
+ uint64_t _buffer;
+ uint64_t GetSize() { return _size; }
+ };
+
+
+
+ using NSDictionaryMSyntheticFrontEnd =
+ GenericNSDictionaryMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
+}
+
+namespace Foundation1437 {
+ static const uint64_t NSDictionaryCapacities[] = {
+ 0, 3, 7, 13, 23, 41, 71, 127, 191, 251, 383, 631, 1087, 1723,
+ 2803, 4523, 7351, 11959, 19447, 31231, 50683, 81919, 132607,
+ 214519, 346607, 561109, 907759, 1468927, 2376191, 3845119,
+ 6221311, 10066421, 16287743, 26354171, 42641881, 68996069,
+ 111638519, 180634607, 292272623, 472907251
+ };
+
+ static const size_t NSDictionaryNumSizeBuckets = sizeof(NSDictionaryCapacities) / sizeof(uint64_t);
+
+ struct DataDescriptor_32 {
+ uint32_t _buffer;
+ uint32_t _muts;
+ uint32_t _used : 25;
+ uint32_t _kvo : 1;
+ uint32_t _szidx : 6;
+
+ uint64_t GetSize() {
+ return (_szidx) >= NSDictionaryNumSizeBuckets ?
+ 0 : NSDictionaryCapacities[_szidx];
+ }
+ };
+
+ struct DataDescriptor_64 {
+ uint64_t _buffer;
+ uint32_t _muts;
+ uint32_t _used : 25;
+ uint32_t _kvo : 1;
+ uint32_t _szidx : 6;
+
+ uint64_t GetSize() {
+ return (_szidx) >= NSDictionaryNumSizeBuckets ?
+ 0 : NSDictionaryCapacities[_szidx];
+ }
+ };
+
+ using NSDictionaryMSyntheticFrontEnd =
+ GenericNSDictionaryMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
+
+ template <typename DD>
+ uint64_t
+ __NSDictionaryMSize_Impl(lldb_private::Process &process,
+ lldb::addr_t valobj_addr, Status &error) {
+ const lldb::addr_t start_of_descriptor =
+ valobj_addr + process.GetAddressByteSize();
+ DD descriptor = DD();
+ process.ReadMemory(start_of_descriptor, &descriptor, sizeof(descriptor),
+ error);
+ if (error.Fail()) {
+ return 0;
+ }
+ return descriptor._used;
+ }
+
+ uint64_t
+ __NSDictionaryMSize(lldb_private::Process &process, lldb::addr_t valobj_addr,
+ Status &error) {
+ if (process.GetAddressByteSize() == 4) {
+ return __NSDictionaryMSize_Impl<DataDescriptor_32>(process, valobj_addr,
+ error);
+ } else {
+ return __NSDictionaryMSize_Impl<DataDescriptor_64>(process, valobj_addr,
+ error);
+ }
+ }
+
+}
+} // namespace formatters
+} // namespace lldb_private
+
+template <bool name_entries>
+bool lldb_private::formatters::NSDictionarySummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ static ConstString g_TypeHint("NSDictionary");
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+ bool is_64bit = (ptr_size == 8);
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ uint64_t value = 0;
+
+ ConstString class_name(descriptor->GetClassName());
+
+ static const ConstString g_DictionaryI("__NSDictionaryI");
+ static const ConstString g_DictionaryM("__NSDictionaryM");
+ static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
+ static const ConstString g_DictionaryMImmutable("__NSDictionaryM_Immutable");
+ static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
+ static const ConstString g_Dictionary0("__NSDictionary0");
+ static const ConstString g_DictionaryCF("__NSCFDictionary");
+
+ if (class_name.IsEmpty())
+ return false;
+
+ if (class_name == g_DictionaryI || class_name == g_DictionaryMImmutable) {
+ Status error;
+ value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
+ ptr_size, 0, error);
+ if (error.Fail())
+ return false;
+ value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
+ } else if (class_name == g_DictionaryM || class_name == g_DictionaryMLegacy ||
+ class_name == g_DictionaryCF) {
+ AppleObjCRuntime *apple_runtime =
+ llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
+ Status error;
+ if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
+ value = Foundation1437::__NSDictionaryMSize(*process_sp, valobj_addr,
+ error);
+ } else {
+ value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
+ ptr_size, 0, error);
+ value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
+ }
+ if (error.Fail())
+ return false;
+ } else if (class_name == g_Dictionary1) {
+ value = 1;
+ } else if (class_name == g_Dictionary0) {
+ value = 0;
+ }
+ else {
+ auto &map(NSDictionary_Additionals::GetAdditionalSummaries());
+ for (auto &candidate : map) {
+ if (candidate.first && candidate.first->Match(class_name))
+ return candidate.second(valobj, stream, options);
+ }
+ return false;
+ }
+
+ std::string prefix, suffix;
+ if (Language *language = Language::FindPlugin(options.GetLanguage())) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ stream.Printf("%s%" PRIu64 " %s%s%s", prefix.c_str(), value, "key/value pair",
+ value == 1 ? "" : "s", suffix.c_str());
+ return true;
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::NSDictionarySyntheticFrontEndCreator(
+ CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
+ lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
+ if (!process_sp)
+ return nullptr;
+ AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
+ ObjCLanguageRuntime::Get(*process_sp));
+ if (!runtime)
+ return nullptr;
+
+ CompilerType valobj_type(valobj_sp->GetCompilerType());
+ Flags flags(valobj_type.GetTypeInfo());
+
+ if (flags.IsClear(eTypeIsPointer)) {
+ Status error;
+ valobj_sp = valobj_sp->AddressOf(error);
+ if (error.Fail() || !valobj_sp)
+ return nullptr;
+ }
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(*valobj_sp));
+
+ if (!descriptor || !descriptor->IsValid())
+ return nullptr;
+
+ ConstString class_name(descriptor->GetClassName());
+
+ static const ConstString g_DictionaryI("__NSDictionaryI");
+ static const ConstString g_DictionaryM("__NSDictionaryM");
+ static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
+ static const ConstString g_DictionaryImmutable("__NSDictionaryM_Immutable");
+ static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
+ static const ConstString g_Dictionary0("__NSDictionary0");
+
+ if (class_name.IsEmpty())
+ return nullptr;
+
+ if (class_name == g_DictionaryI) {
+ return (new NSDictionaryISyntheticFrontEnd(valobj_sp));
+ } else if (class_name == g_DictionaryM) {
+ if (runtime->GetFoundationVersion() >= 1437) {
+ return (new Foundation1437::NSDictionaryMSyntheticFrontEnd(valobj_sp));
+ } else if (runtime->GetFoundationVersion() >= 1428) {
+ return (new Foundation1428::NSDictionaryMSyntheticFrontEnd(valobj_sp));
+ } else {
+ return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
+ }
+ } else if (class_name == g_DictionaryMLegacy) {
+ return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
+ } else if (class_name == g_Dictionary1) {
+ return (new NSDictionary1SyntheticFrontEnd(valobj_sp));
+ } else {
+ auto &map(NSDictionary_Additionals::GetAdditionalSynthetics());
+ for (auto &candidate : map) {
+ if (candidate.first && candidate.first->Match((class_name)))
+ return candidate.second(synth, valobj_sp);
+ }
+ }
+
+ return nullptr;
+}
+
+lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
+ NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
+ m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
+ m_pair_type() {}
+
+lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
+ ~NSDictionaryISyntheticFrontEnd() {
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+}
+
+size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
+ GetIndexOfChildWithName(ConstString name) {
+ const char *item_name = name.GetCString();
+ uint32_t idx = ExtractIndexFromString(item_name);
+ if (idx < UINT32_MAX && idx >= CalculateNumChildren())
+ return UINT32_MAX;
+ return idx;
+}
+
+size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
+ CalculateNumChildren() {
+ if (!m_data_32 && !m_data_64)
+ return 0;
+ return (m_data_32 ? m_data_32->_used : m_data_64->_used);
+}
+
+bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::Update() {
+ m_children.clear();
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+ m_ptr_size = 0;
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ if (!valobj_sp)
+ return false;
+ m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
+ Status error;
+ error.Clear();
+ lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
+ if (!process_sp)
+ return false;
+ m_ptr_size = process_sp->GetAddressByteSize();
+ m_order = process_sp->GetByteOrder();
+ uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
+ if (m_ptr_size == 4) {
+ m_data_32 = new DataDescriptor_32();
+ process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
+ error);
+ } else {
+ m_data_64 = new DataDescriptor_64();
+ process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
+ error);
+ }
+ if (error.Fail())
+ return false;
+ m_data_ptr = data_location + m_ptr_size;
+ return false;
+}
+
+bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
+ MightHaveChildren() {
+ return true;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::NSDictionaryISyntheticFrontEnd::GetChildAtIndex(
+ size_t idx) {
+ uint32_t num_children = CalculateNumChildren();
+
+ if (idx >= num_children)
+ return lldb::ValueObjectSP();
+
+ if (m_children.empty()) {
+ // do the scan phase
+ lldb::addr_t key_at_idx = 0, val_at_idx = 0;
+
+ uint32_t tries = 0;
+ uint32_t test_idx = 0;
+
+ while (tries < num_children) {
+ key_at_idx = m_data_ptr + (2 * test_idx * m_ptr_size);
+ val_at_idx = key_at_idx + m_ptr_size;
+ ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
+ if (!process_sp)
+ return lldb::ValueObjectSP();
+ Status error;
+ key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
+ if (error.Fail())
+ return lldb::ValueObjectSP();
+ val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
+ if (error.Fail())
+ return lldb::ValueObjectSP();
+
+ test_idx++;
+
+ if (!key_at_idx || !val_at_idx)
+ continue;
+ tries++;
+
+ DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
+ lldb::ValueObjectSP()};
+
+ m_children.push_back(descriptor);
+ }
+ }
+
+ if (idx >= m_children.size()) // should never happen
+ return lldb::ValueObjectSP();
+
+ DictionaryItemDescriptor &dict_item = m_children[idx];
+ if (!dict_item.valobj_sp) {
+ if (!m_pair_type.IsValid()) {
+ TargetSP target_sp(m_backend.GetTargetSP());
+ if (!target_sp)
+ return ValueObjectSP();
+ m_pair_type = GetLLDBNSPairType(target_sp);
+ }
+ if (!m_pair_type.IsValid())
+ return ValueObjectSP();
+
+ DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
+
+ if (m_ptr_size == 8) {
+ uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
+ *data_ptr = dict_item.key_ptr;
+ *(data_ptr + 1) = dict_item.val_ptr;
+ } else {
+ uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
+ *data_ptr = dict_item.key_ptr;
+ *(data_ptr + 1) = dict_item.val_ptr;
+ }
+
+ StreamString idx_name;
+ idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ DataExtractor data(buffer_sp, m_order, m_ptr_size);
+ dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
+ m_exe_ctx_ref, m_pair_type);
+ }
+ return dict_item.valobj_sp;
+}
+
+lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
+ NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_pair(nullptr) {}
+
+size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
+ GetIndexOfChildWithName(ConstString name) {
+ static const ConstString g_zero("[0]");
+ return name == g_zero ? 0 : UINT32_MAX;
+}
+
+size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
+ CalculateNumChildren() {
+ return 1;
+}
+
+bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::Update() {
+ m_pair.reset();
+ return false;
+}
+
+bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
+ MightHaveChildren() {
+ return true;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::NSDictionary1SyntheticFrontEnd::GetChildAtIndex(
+ size_t idx) {
+ if (idx != 0)
+ return lldb::ValueObjectSP();
+
+ if (m_pair.get())
+ return m_pair;
+
+ auto process_sp(m_backend.GetProcessSP());
+ if (!process_sp)
+ return nullptr;
+
+ auto ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t key_ptr =
+ m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS) + ptr_size;
+ lldb::addr_t value_ptr = key_ptr + ptr_size;
+
+ Status error;
+
+ lldb::addr_t value_at_idx = process_sp->ReadPointerFromMemory(key_ptr, error);
+ if (error.Fail())
+ return nullptr;
+ lldb::addr_t key_at_idx = process_sp->ReadPointerFromMemory(value_ptr, error);
+ if (error.Fail())
+ return nullptr;
+
+ auto pair_type =
+ GetLLDBNSPairType(process_sp->GetTarget().shared_from_this());
+
+ DataBufferSP buffer_sp(new DataBufferHeap(2 * ptr_size, 0));
+
+ if (ptr_size == 8) {
+ uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
+ *data_ptr = key_at_idx;
+ *(data_ptr + 1) = value_at_idx;
+ } else {
+ uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
+ *data_ptr = key_at_idx;
+ *(data_ptr + 1) = value_at_idx;
+ }
+
+ DataExtractor data(buffer_sp, process_sp->GetByteOrder(), ptr_size);
+ m_pair = CreateValueObjectFromData(
+ "[0]", data, m_backend.GetExecutionContextRef(), pair_type);
+
+ return m_pair;
+}
+
+template <typename D32, typename D64>
+lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
+ GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
+ m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
+ m_pair_type() {}
+
+template <typename D32, typename D64>
+lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
+ ~GenericNSDictionaryMSyntheticFrontEnd() {
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+}
+
+template <typename D32, typename D64>
+size_t
+lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>:: GetIndexOfChildWithName(ConstString name) {
+ const char *item_name = name.GetCString();
+ uint32_t idx = ExtractIndexFromString(item_name);
+ if (idx < UINT32_MAX && idx >= CalculateNumChildren())
+ return UINT32_MAX;
+ return idx;
+}
+
+template <typename D32, typename D64>
+size_t
+lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::CalculateNumChildren() {
+ if (!m_data_32 && !m_data_64)
+ return 0;
+ return (m_data_32 ? m_data_32->_used : m_data_64->_used);
+}
+
+template <typename D32, typename D64>
+bool
+lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
+ Update() {
+ m_children.clear();
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ m_ptr_size = 0;
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+ if (!valobj_sp)
+ return false;
+ m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
+ Status error;
+ error.Clear();
+ lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
+ if (!process_sp)
+ return false;
+ m_ptr_size = process_sp->GetAddressByteSize();
+ m_order = process_sp->GetByteOrder();
+ uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
+ if (m_ptr_size == 4) {
+ m_data_32 = new D32();
+ process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
+ error);
+ } else {
+ m_data_64 = new D64();
+ process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
+ error);
+ }
+ if (error.Fail())
+ return false;
+ return false;
+}
+
+template <typename D32, typename D64>
+bool
+lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
+ MightHaveChildren() {
+ return true;
+}
+
+template <typename D32, typename D64>
+lldb::ValueObjectSP
+lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
+ GetChildAtIndex(
+ size_t idx) {
+ lldb::addr_t m_keys_ptr;
+ lldb::addr_t m_values_ptr;
+ if (m_data_32) {
+ uint32_t size = m_data_32->GetSize();
+ m_keys_ptr = m_data_32->_buffer;
+ m_values_ptr = m_data_32->_buffer + (m_ptr_size * size);
+ } else {
+ uint32_t size = m_data_64->GetSize();
+ m_keys_ptr = m_data_64->_buffer;
+ m_values_ptr = m_data_64->_buffer + (m_ptr_size * size);
+ }
+
+ uint32_t num_children = CalculateNumChildren();
+
+ if (idx >= num_children)
+ return lldb::ValueObjectSP();
+
+ if (m_children.empty()) {
+ // do the scan phase
+ lldb::addr_t key_at_idx = 0, val_at_idx = 0;
+
+ uint32_t tries = 0;
+ uint32_t test_idx = 0;
+
+ while (tries < num_children) {
+ key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
+ val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
+ ;
+ ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
+ if (!process_sp)
+ return lldb::ValueObjectSP();
+ Status error;
+ key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
+ if (error.Fail())
+ return lldb::ValueObjectSP();
+ val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
+ if (error.Fail())
+ return lldb::ValueObjectSP();
+
+ test_idx++;
+
+ if (!key_at_idx || !val_at_idx)
+ continue;
+ tries++;
+
+ DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
+ lldb::ValueObjectSP()};
+
+ m_children.push_back(descriptor);
+ }
+ }
+
+ if (idx >= m_children.size()) // should never happen
+ return lldb::ValueObjectSP();
+
+ DictionaryItemDescriptor &dict_item = m_children[idx];
+ if (!dict_item.valobj_sp) {
+ if (!m_pair_type.IsValid()) {
+ TargetSP target_sp(m_backend.GetTargetSP());
+ if (!target_sp)
+ return ValueObjectSP();
+ m_pair_type = GetLLDBNSPairType(target_sp);
+ }
+ if (!m_pair_type.IsValid())
+ return ValueObjectSP();
+
+ DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
+
+ if (m_ptr_size == 8) {
+ uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
+ *data_ptr = dict_item.key_ptr;
+ *(data_ptr + 1) = dict_item.val_ptr;
+ } else {
+ uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
+ *data_ptr = dict_item.key_ptr;
+ *(data_ptr + 1) = dict_item.val_ptr;
+ }
+
+ StreamString idx_name;
+ idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ DataExtractor data(buffer_sp, m_order, m_ptr_size);
+ dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
+ m_exe_ctx_ref, m_pair_type);
+ }
+ return dict_item.valobj_sp;
+}
+
+
+lldb_private::formatters::Foundation1100::
+ NSDictionaryMSyntheticFrontEnd::
+ NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
+ m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
+ m_pair_type() {}
+
+lldb_private::formatters::Foundation1100::
+ NSDictionaryMSyntheticFrontEnd::~NSDictionaryMSyntheticFrontEnd() {
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+}
+
+size_t
+lldb_private::formatters::Foundation1100::
+ NSDictionaryMSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) {
+ const char *item_name = name.GetCString();
+ uint32_t idx = ExtractIndexFromString(item_name);
+ if (idx < UINT32_MAX && idx >= CalculateNumChildren())
+ return UINT32_MAX;
+ return idx;
+}
+
+size_t
+lldb_private::formatters::Foundation1100::
+ NSDictionaryMSyntheticFrontEnd::CalculateNumChildren() {
+ if (!m_data_32 && !m_data_64)
+ return 0;
+ return (m_data_32 ? m_data_32->_used : m_data_64->_used);
+}
+
+bool
+lldb_private::formatters::Foundation1100::
+ NSDictionaryMSyntheticFrontEnd::Update() {
+ m_children.clear();
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ m_ptr_size = 0;
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+ if (!valobj_sp)
+ return false;
+ m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
+ Status error;
+ error.Clear();
+ lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
+ if (!process_sp)
+ return false;
+ m_ptr_size = process_sp->GetAddressByteSize();
+ m_order = process_sp->GetByteOrder();
+ uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
+ if (m_ptr_size == 4) {
+ m_data_32 = new DataDescriptor_32();
+ process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
+ error);
+ } else {
+ m_data_64 = new DataDescriptor_64();
+ process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
+ error);
+ }
+ if (error.Fail())
+ return false;
+ return false;
+}
+
+bool
+lldb_private::formatters::Foundation1100::
+ NSDictionaryMSyntheticFrontEnd::MightHaveChildren() {
+ return true;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::Foundation1100::
+ NSDictionaryMSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
+ lldb::addr_t m_keys_ptr =
+ (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr);
+ lldb::addr_t m_values_ptr =
+ (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
+
+ uint32_t num_children = CalculateNumChildren();
+
+ if (idx >= num_children)
+ return lldb::ValueObjectSP();
+
+ if (m_children.empty()) {
+ // do the scan phase
+ lldb::addr_t key_at_idx = 0, val_at_idx = 0;
+
+ uint32_t tries = 0;
+ uint32_t test_idx = 0;
+
+ while (tries < num_children) {
+ key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
+ val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
+ ;
+ ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
+ if (!process_sp)
+ return lldb::ValueObjectSP();
+ Status error;
+ key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
+ if (error.Fail())
+ return lldb::ValueObjectSP();
+ val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
+ if (error.Fail())
+ return lldb::ValueObjectSP();
+
+ test_idx++;
+
+ if (!key_at_idx || !val_at_idx)
+ continue;
+ tries++;
+
+ DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
+ lldb::ValueObjectSP()};
+
+ m_children.push_back(descriptor);
+ }
+ }
+
+ if (idx >= m_children.size()) // should never happen
+ return lldb::ValueObjectSP();
+
+ DictionaryItemDescriptor &dict_item = m_children[idx];
+ if (!dict_item.valobj_sp) {
+ if (!m_pair_type.IsValid()) {
+ TargetSP target_sp(m_backend.GetTargetSP());
+ if (!target_sp)
+ return ValueObjectSP();
+ m_pair_type = GetLLDBNSPairType(target_sp);
+ }
+ if (!m_pair_type.IsValid())
+ return ValueObjectSP();
+
+ DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
+
+ if (m_ptr_size == 8) {
+ uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
+ *data_ptr = dict_item.key_ptr;
+ *(data_ptr + 1) = dict_item.val_ptr;
+ } else {
+ uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
+ *data_ptr = dict_item.key_ptr;
+ *(data_ptr + 1) = dict_item.val_ptr;
+ }
+
+ StreamString idx_name;
+ idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ DataExtractor data(buffer_sp, m_order, m_ptr_size);
+ dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
+ m_exe_ctx_ref, m_pair_type);
+ }
+ return dict_item.valobj_sp;
+}
+
+template bool lldb_private::formatters::NSDictionarySummaryProvider<true>(
+ ValueObject &, Stream &, const TypeSummaryOptions &);
+
+template bool lldb_private::formatters::NSDictionarySummaryProvider<false>(
+ ValueObject &, Stream &, const TypeSummaryOptions &);
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.h
new file mode 100644
index 000000000000..ecb3fccdf877
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.h
@@ -0,0 +1,94 @@
+//===-- NSDictionary.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 liblldb_NSDictionary_h_
+#define liblldb_NSDictionary_h_
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Stream.h"
+
+#include <map>
+#include <memory>
+
+namespace lldb_private {
+namespace formatters {
+template <bool name_entries>
+bool NSDictionarySummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+extern template bool
+NSDictionarySummaryProvider<true>(ValueObject &, Stream &,
+ const TypeSummaryOptions &);
+
+extern template bool
+NSDictionarySummaryProvider<false>(ValueObject &, Stream &,
+ const TypeSummaryOptions &);
+
+SyntheticChildrenFrontEnd *
+NSDictionarySyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+class NSDictionary_Additionals {
+public:
+ class AdditionalFormatterMatching {
+ public:
+ class Matcher {
+ public:
+ virtual ~Matcher() = default;
+ virtual bool Match(ConstString class_name) = 0;
+
+ typedef std::unique_ptr<Matcher> UP;
+ };
+ class Prefix : public Matcher {
+ public:
+ Prefix(ConstString p);
+ ~Prefix() override = default;
+ bool Match(ConstString class_name) override;
+
+ private:
+ ConstString m_prefix;
+ };
+ class Full : public Matcher {
+ public:
+ Full(ConstString n);
+ ~Full() override = default;
+ bool Match(ConstString class_name) override;
+
+ private:
+ ConstString m_name;
+ };
+ typedef Matcher::UP MatcherUP;
+
+ MatcherUP GetFullMatch(ConstString n) { return llvm::make_unique<Full>(n); }
+
+ MatcherUP GetPrefixMatch(ConstString p) {
+ return llvm::make_unique<Prefix>(p);
+ }
+ };
+
+ template <typename FormatterType>
+ using AdditionalFormatter =
+ std::pair<AdditionalFormatterMatching::MatcherUP, FormatterType>;
+
+ template <typename FormatterType>
+ using AdditionalFormatters = std::vector<AdditionalFormatter<FormatterType>>;
+
+ static AdditionalFormatters<CXXFunctionSummaryFormat::Callback> &
+ GetAdditionalSummaries();
+
+ static AdditionalFormatters<CXXSyntheticChildren::CreateFrontEndCallback> &
+ GetAdditionalSynthetics();
+};
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // liblldb_NSDictionary_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSError.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSError.cpp
new file mode 100644
index 000000000000..97df3be72c84
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSError.cpp
@@ -0,0 +1,211 @@
+//===-- NSError.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 "clang/AST/DeclCXX.h"
+
+#include "Cocoa.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/ProcessStructReader.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+#include "Plugins/Language/ObjC/NSString.h"
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+static lldb::addr_t DerefToNSErrorPointer(ValueObject &valobj) {
+ CompilerType valobj_type(valobj.GetCompilerType());
+ Flags type_flags(valobj_type.GetTypeInfo());
+ if (type_flags.AllClear(eTypeHasValue)) {
+ if (valobj.IsBaseClass() && valobj.GetParent())
+ return valobj.GetParent()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+ } else {
+ lldb::addr_t ptr_value = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+ if (type_flags.AllSet(eTypeIsPointer)) {
+ CompilerType pointee_type(valobj_type.GetPointeeType());
+ Flags pointee_flags(pointee_type.GetTypeInfo());
+ if (pointee_flags.AllSet(eTypeIsPointer)) {
+ if (ProcessSP process_sp = valobj.GetProcessSP()) {
+ Status error;
+ ptr_value = process_sp->ReadPointerFromMemory(ptr_value, error);
+ }
+ }
+ }
+ return ptr_value;
+ }
+
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool lldb_private::formatters::NSError_SummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ProcessSP process_sp(valobj.GetProcessSP());
+ if (!process_sp)
+ return false;
+
+ lldb::addr_t ptr_value = DerefToNSErrorPointer(valobj);
+ if (ptr_value == LLDB_INVALID_ADDRESS)
+ return false;
+
+ size_t ptr_size = process_sp->GetAddressByteSize();
+ lldb::addr_t code_location = ptr_value + 2 * ptr_size;
+ lldb::addr_t domain_location = ptr_value + 3 * ptr_size;
+
+ Status error;
+ uint64_t code = process_sp->ReadUnsignedIntegerFromMemory(code_location,
+ ptr_size, 0, error);
+ if (error.Fail())
+ return false;
+
+ lldb::addr_t domain_str_value =
+ process_sp->ReadPointerFromMemory(domain_location, error);
+ if (error.Fail() || domain_str_value == LLDB_INVALID_ADDRESS)
+ return false;
+
+ if (!domain_str_value) {
+ stream.Printf("domain: nil - code: %" PRIu64, code);
+ return true;
+ }
+
+ InferiorSizedWord isw(domain_str_value, *process_sp);
+
+ ValueObjectSP domain_str_sp = ValueObject::CreateValueObjectFromData(
+ "domain_str", isw.GetAsData(process_sp->GetByteOrder()),
+ valobj.GetExecutionContextRef(), process_sp->GetTarget()
+ .GetScratchClangASTContext()
+ ->GetBasicType(lldb::eBasicTypeVoid)
+ .GetPointerType());
+
+ if (!domain_str_sp)
+ return false;
+
+ StreamString domain_str_summary;
+ if (NSStringSummaryProvider(*domain_str_sp, domain_str_summary, options) &&
+ !domain_str_summary.Empty()) {
+ stream.Printf("domain: %s - code: %" PRIu64, domain_str_summary.GetData(),
+ code);
+ return true;
+ } else {
+ stream.Printf("domain: nil - code: %" PRIu64, code);
+ return true;
+ }
+}
+
+class NSErrorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ NSErrorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp) {}
+
+ ~NSErrorSyntheticFrontEnd() override = default;
+ // no need to delete m_child_ptr - it's kept alive by the cluster manager on
+ // our behalf
+
+ size_t CalculateNumChildren() override {
+ if (m_child_ptr)
+ return 1;
+ if (m_child_sp)
+ return 1;
+ return 0;
+ }
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override {
+ if (idx != 0)
+ return lldb::ValueObjectSP();
+
+ if (m_child_ptr)
+ return m_child_ptr->GetSP();
+ return m_child_sp;
+ }
+
+ bool Update() override {
+ m_child_ptr = nullptr;
+ m_child_sp.reset();
+
+ ProcessSP process_sp(m_backend.GetProcessSP());
+ if (!process_sp)
+ return false;
+
+ lldb::addr_t userinfo_location = DerefToNSErrorPointer(m_backend);
+ if (userinfo_location == LLDB_INVALID_ADDRESS)
+ return false;
+
+ size_t ptr_size = process_sp->GetAddressByteSize();
+
+ userinfo_location += 4 * ptr_size;
+ Status error;
+ lldb::addr_t userinfo =
+ process_sp->ReadPointerFromMemory(userinfo_location, error);
+ if (userinfo == LLDB_INVALID_ADDRESS || error.Fail())
+ return false;
+ InferiorSizedWord isw(userinfo, *process_sp);
+ m_child_sp = CreateValueObjectFromData(
+ "_userInfo", isw.GetAsData(process_sp->GetByteOrder()),
+ m_backend.GetExecutionContextRef(),
+ process_sp->GetTarget().GetScratchClangASTContext()->GetBasicType(
+ lldb::eBasicTypeObjCID));
+ return false;
+ }
+
+ bool MightHaveChildren() override { return true; }
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ static ConstString g___userInfo("_userInfo");
+ if (name == g___userInfo)
+ return 0;
+ return UINT32_MAX;
+ }
+
+private:
+ // the child here can be "real" (i.e. an actual child of the root) or
+ // synthetized from raw memory if the former, I need to store a plain pointer
+ // to it - or else a loop of references will cause this entire hierarchy of
+ // values to leak if the latter, then I need to store a SharedPointer to it -
+ // so that it only goes away when everyone else in the cluster goes away oh
+ // joy!
+ ValueObject *m_child_ptr;
+ ValueObjectSP m_child_sp;
+};
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::NSErrorSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
+ if (!process_sp)
+ return nullptr;
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+ if (!runtime)
+ return nullptr;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(*valobj_sp.get()));
+
+ if (!descriptor.get() || !descriptor->IsValid())
+ return nullptr;
+
+ const char *class_name = descriptor->GetClassName().GetCString();
+
+ if (!class_name || !*class_name)
+ return nullptr;
+
+ if (!strcmp(class_name, "NSError"))
+ return (new NSErrorSyntheticFrontEnd(valobj_sp));
+ else if (!strcmp(class_name, "__NSCFError"))
+ return (new NSErrorSyntheticFrontEnd(valobj_sp));
+
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSException.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSException.cpp
new file mode 100644
index 000000000000..931794a12ab1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSException.cpp
@@ -0,0 +1,205 @@
+//===-- NSException.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 "clang/AST/DeclCXX.h"
+
+#include "Cocoa.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/ProcessStructReader.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+#include "Plugins/Language/ObjC/NSString.h"
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+static bool ExtractFields(ValueObject &valobj, ValueObjectSP *name_sp,
+ ValueObjectSP *reason_sp, ValueObjectSP *userinfo_sp,
+ ValueObjectSP *reserved_sp) {
+ ProcessSP process_sp(valobj.GetProcessSP());
+ if (!process_sp)
+ return false;
+
+ lldb::addr_t ptr = LLDB_INVALID_ADDRESS;
+
+ CompilerType valobj_type(valobj.GetCompilerType());
+ Flags type_flags(valobj_type.GetTypeInfo());
+ if (type_flags.AllClear(eTypeHasValue)) {
+ if (valobj.IsBaseClass() && valobj.GetParent())
+ ptr = valobj.GetParent()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+ } else {
+ ptr = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+ }
+
+ if (ptr == LLDB_INVALID_ADDRESS)
+ return false;
+ size_t ptr_size = process_sp->GetAddressByteSize();
+
+ Status error;
+ auto name = process_sp->ReadPointerFromMemory(ptr + 1 * ptr_size, error);
+ if (error.Fail() || name == LLDB_INVALID_ADDRESS)
+ return false;
+ auto reason = process_sp->ReadPointerFromMemory(ptr + 2 * ptr_size, error);
+ if (error.Fail() || reason == LLDB_INVALID_ADDRESS)
+ return false;
+ auto userinfo = process_sp->ReadPointerFromMemory(ptr + 3 * ptr_size, error);
+ if (error.Fail() || userinfo == LLDB_INVALID_ADDRESS)
+ return false;
+ auto reserved = process_sp->ReadPointerFromMemory(ptr + 4 * ptr_size, error);
+ if (error.Fail() || reserved == LLDB_INVALID_ADDRESS)
+ return false;
+
+ InferiorSizedWord name_isw(name, *process_sp);
+ InferiorSizedWord reason_isw(reason, *process_sp);
+ InferiorSizedWord userinfo_isw(userinfo, *process_sp);
+ InferiorSizedWord reserved_isw(reserved, *process_sp);
+
+ CompilerType voidstar = process_sp->GetTarget()
+ .GetScratchClangASTContext()
+ ->GetBasicType(lldb::eBasicTypeVoid)
+ .GetPointerType();
+
+ if (name_sp)
+ *name_sp = ValueObject::CreateValueObjectFromData(
+ "name", name_isw.GetAsData(process_sp->GetByteOrder()),
+ valobj.GetExecutionContextRef(), voidstar);
+ if (reason_sp)
+ *reason_sp = ValueObject::CreateValueObjectFromData(
+ "reason", reason_isw.GetAsData(process_sp->GetByteOrder()),
+ valobj.GetExecutionContextRef(), voidstar);
+ if (userinfo_sp)
+ *userinfo_sp = ValueObject::CreateValueObjectFromData(
+ "userInfo", userinfo_isw.GetAsData(process_sp->GetByteOrder()),
+ valobj.GetExecutionContextRef(), voidstar);
+ if (reserved_sp)
+ *reserved_sp = ValueObject::CreateValueObjectFromData(
+ "reserved", reserved_isw.GetAsData(process_sp->GetByteOrder()),
+ valobj.GetExecutionContextRef(), voidstar);
+
+ return true;
+}
+
+bool lldb_private::formatters::NSException_SummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ lldb::ValueObjectSP name_sp;
+ lldb::ValueObjectSP reason_sp;
+ if (!ExtractFields(valobj, &name_sp, &reason_sp, nullptr, nullptr))
+ return false;
+
+ if (!name_sp || !reason_sp)
+ return false;
+
+ StreamString name_str_summary;
+ StreamString reason_str_summary;
+ if (NSStringSummaryProvider(*name_sp, name_str_summary, options) &&
+ NSStringSummaryProvider(*reason_sp, reason_str_summary, options) &&
+ !name_str_summary.Empty() && !reason_str_summary.Empty()) {
+ stream.Printf("name: %s - reason: %s", name_str_summary.GetData(),
+ reason_str_summary.GetData());
+ return true;
+ } else
+ return false;
+}
+
+class NSExceptionSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ NSExceptionSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp) {}
+
+ ~NSExceptionSyntheticFrontEnd() override = default;
+
+ size_t CalculateNumChildren() override {
+ return 4;
+ }
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override {
+ switch (idx) {
+ case 0: return m_name_sp;
+ case 1: return m_reason_sp;
+ case 2: return m_userinfo_sp;
+ case 3: return m_reserved_sp;
+ }
+ return lldb::ValueObjectSP();
+ }
+
+ bool Update() override {
+ m_name_sp.reset();
+ m_reason_sp.reset();
+ m_userinfo_sp.reset();
+ m_reserved_sp.reset();
+
+ return ExtractFields(m_backend, &m_name_sp, &m_reason_sp, &m_userinfo_sp,
+ &m_reserved_sp);
+ }
+
+ bool MightHaveChildren() override { return true; }
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ // NSException has 4 members:
+ // NSString *name;
+ // NSString *reason;
+ // NSDictionary *userInfo;
+ // id reserved;
+ static ConstString g___name("name");
+ static ConstString g___reason("reason");
+ static ConstString g___userInfo("userInfo");
+ static ConstString g___reserved("reserved");
+ if (name == g___name) return 0;
+ if (name == g___reason) return 1;
+ if (name == g___userInfo) return 2;
+ if (name == g___reserved) return 3;
+ return UINT32_MAX;
+ }
+
+private:
+ ValueObjectSP m_name_sp;
+ ValueObjectSP m_reason_sp;
+ ValueObjectSP m_userinfo_sp;
+ ValueObjectSP m_reserved_sp;
+};
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::NSExceptionSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
+ if (!process_sp)
+ return nullptr;
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+ if (!runtime)
+ return nullptr;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(*valobj_sp.get()));
+
+ if (!descriptor.get() || !descriptor->IsValid())
+ return nullptr;
+
+ const char *class_name = descriptor->GetClassName().GetCString();
+
+ if (!class_name || !*class_name)
+ return nullptr;
+
+ if (!strcmp(class_name, "NSException"))
+ return (new NSExceptionSyntheticFrontEnd(valobj_sp));
+ else if (!strcmp(class_name, "NSCFException"))
+ return (new NSExceptionSyntheticFrontEnd(valobj_sp));
+ else if (!strcmp(class_name, "__NSCFException"))
+ return (new NSExceptionSyntheticFrontEnd(valobj_sp));
+
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp
new file mode 100644
index 000000000000..9ee6021ae56b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp
@@ -0,0 +1,313 @@
+//===-- NSIndexPath.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 "Cocoa.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+static constexpr size_t PACKED_INDEX_SHIFT_64(size_t i) {
+ return (60 - (13 * (4 - i)));
+}
+
+static constexpr size_t PACKED_INDEX_SHIFT_32(size_t i) {
+ return (32 - (13 * (2 - i)));
+}
+
+class NSIndexPathSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ NSIndexPathSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_descriptor_sp(nullptr),
+ m_impl(), m_ptr_size(0), m_uint_star_type() {
+ m_ptr_size =
+ m_backend.GetTargetSP()->GetArchitecture().GetAddressByteSize();
+ }
+
+ ~NSIndexPathSyntheticFrontEnd() override = default;
+
+ size_t CalculateNumChildren() override { return m_impl.GetNumIndexes(); }
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override {
+ return m_impl.GetIndexAtIndex(idx, m_uint_star_type);
+ }
+
+ bool Update() override {
+ m_impl.Clear();
+
+ TypeSystem *type_system = m_backend.GetCompilerType().GetTypeSystem();
+ if (!type_system)
+ return false;
+
+ ClangASTContext *ast = m_backend.GetExecutionContextRef()
+ .GetTargetSP()
+ ->GetScratchClangASTContext();
+ if (!ast)
+ return false;
+
+ m_uint_star_type = ast->GetPointerSizedIntType(false);
+
+ static ConstString g__indexes("_indexes");
+ static ConstString g__length("_length");
+
+ ProcessSP process_sp = m_backend.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(m_backend));
+
+ if (!descriptor.get() || !descriptor->IsValid())
+ return false;
+
+ uint64_t info_bits(0), value_bits(0), payload(0);
+
+ if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits, &payload)) {
+ m_impl.m_inlined.SetIndexes(payload, *process_sp);
+ m_impl.m_mode = Mode::Inlined;
+ } else {
+ ObjCLanguageRuntime::ClassDescriptor::iVarDescriptor _indexes_id;
+ ObjCLanguageRuntime::ClassDescriptor::iVarDescriptor _length_id;
+
+ bool has_indexes(false), has_length(false);
+
+ for (size_t x = 0; x < descriptor->GetNumIVars(); x++) {
+ const auto &ivar = descriptor->GetIVarAtIndex(x);
+ if (ivar.m_name == g__indexes) {
+ _indexes_id = ivar;
+ has_indexes = true;
+ } else if (ivar.m_name == g__length) {
+ _length_id = ivar;
+ has_length = true;
+ }
+
+ if (has_length && has_indexes)
+ break;
+ }
+
+ if (has_length && has_indexes) {
+ m_impl.m_outsourced.m_indexes =
+ m_backend
+ .GetSyntheticChildAtOffset(_indexes_id.m_offset,
+ m_uint_star_type.GetPointerType(),
+ true)
+ .get();
+ ValueObjectSP length_sp(m_backend.GetSyntheticChildAtOffset(
+ _length_id.m_offset, m_uint_star_type, true));
+ if (length_sp) {
+ m_impl.m_outsourced.m_count = length_sp->GetValueAsUnsigned(0);
+ if (m_impl.m_outsourced.m_indexes)
+ m_impl.m_mode = Mode::Outsourced;
+ }
+ }
+ }
+ return false;
+ }
+
+ bool MightHaveChildren() override { return m_impl.m_mode != Mode::Invalid; }
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ const char *item_name = name.GetCString();
+ uint32_t idx = ExtractIndexFromString(item_name);
+ if (idx < UINT32_MAX && idx >= CalculateNumChildren())
+ return UINT32_MAX;
+ return idx;
+ }
+
+ lldb::ValueObjectSP GetSyntheticValue() override { return nullptr; }
+
+protected:
+ ObjCLanguageRuntime::ClassDescriptorSP m_descriptor_sp;
+
+ enum class Mode { Inlined, Outsourced, Invalid };
+
+ struct Impl {
+ size_t GetNumIndexes() {
+ switch (m_mode) {
+ case Mode::Inlined:
+ return m_inlined.GetNumIndexes();
+ case Mode::Outsourced:
+ return m_outsourced.m_count;
+ default:
+ return 0;
+ }
+ }
+
+ lldb::ValueObjectSP GetIndexAtIndex(size_t idx,
+ const CompilerType &desired_type) {
+ if (idx >= GetNumIndexes())
+ return nullptr;
+ switch (m_mode) {
+ default:
+ return nullptr;
+ case Mode::Inlined:
+ return m_inlined.GetIndexAtIndex(idx, desired_type);
+ case Mode::Outsourced:
+ return m_outsourced.GetIndexAtIndex(idx);
+ }
+ }
+
+ struct InlinedIndexes {
+ public:
+ void SetIndexes(uint64_t value, Process &p) {
+ m_indexes = value;
+ _lengthForInlinePayload(p.GetAddressByteSize());
+ m_process = &p;
+ }
+
+ size_t GetNumIndexes() { return m_count; }
+
+ lldb::ValueObjectSP GetIndexAtIndex(size_t idx,
+ const CompilerType &desired_type) {
+ if (!m_process)
+ return nullptr;
+
+ std::pair<uint64_t, bool> value(_indexAtPositionForInlinePayload(idx));
+ if (!value.second)
+ return nullptr;
+
+ Value v;
+ if (m_ptr_size == 8) {
+ Scalar scalar((unsigned long long)value.first);
+ v = Value(scalar);
+ } else {
+ Scalar scalar((unsigned int)value.first);
+ v = Value(scalar);
+ }
+
+ v.SetCompilerType(desired_type);
+
+ StreamString idx_name;
+ idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+
+ return ValueObjectConstResult::Create(
+ m_process, v, ConstString(idx_name.GetString()));
+ }
+
+ void Clear() {
+ m_indexes = 0;
+ m_count = 0;
+ m_ptr_size = 0;
+ m_process = nullptr;
+ }
+
+ InlinedIndexes()
+ : m_indexes(0), m_count(0), m_ptr_size(0), m_process(nullptr) {}
+
+ private:
+ uint64_t m_indexes;
+ size_t m_count;
+ uint32_t m_ptr_size;
+ Process *m_process;
+
+ // cfr. Foundation for the details of this code
+ size_t _lengthForInlinePayload(uint32_t ptr_size) {
+ m_ptr_size = ptr_size;
+ if (m_ptr_size == 8)
+ m_count = ((m_indexes >> 3) & 0x7);
+ else
+ m_count = ((m_indexes >> 3) & 0x3);
+ return m_count;
+ }
+
+ std::pair<uint64_t, bool> _indexAtPositionForInlinePayload(size_t pos) {
+ static const uint64_t PACKED_INDEX_MASK = ((1 << 13) - 1);
+ if (m_ptr_size == 8) {
+ switch (pos) {
+ case 3:
+ case 2:
+ case 1:
+ case 0:
+ return {(m_indexes >> PACKED_INDEX_SHIFT_64(pos)) &
+ PACKED_INDEX_MASK,
+ true};
+ default:
+ return {0, false};
+ }
+ } else {
+ switch (pos) {
+ case 0:
+ case 1:
+ return {(m_indexes >> PACKED_INDEX_SHIFT_32(pos)) &
+ PACKED_INDEX_MASK,
+ true};
+ default:
+ return {0, false};
+ }
+ }
+ return {0, false};
+ }
+ };
+
+ struct OutsourcedIndexes {
+ lldb::ValueObjectSP GetIndexAtIndex(size_t idx) {
+ if (m_indexes) {
+ ValueObjectSP index_sp(m_indexes->GetSyntheticArrayMember(idx, true));
+ return index_sp;
+ }
+ return nullptr;
+ }
+
+ void Clear() {
+ m_indexes = nullptr;
+ m_count = 0;
+ }
+
+ OutsourcedIndexes() : m_indexes(nullptr), m_count(0) {}
+
+ ValueObject *m_indexes;
+ size_t m_count;
+ };
+
+ union {
+ struct InlinedIndexes m_inlined;
+ struct OutsourcedIndexes m_outsourced;
+ };
+
+ void Clear() {
+ m_mode = Mode::Invalid;
+ m_inlined.Clear();
+ m_outsourced.Clear();
+ }
+
+ Impl() : m_mode(Mode::Invalid) {}
+
+ Mode m_mode;
+ } m_impl;
+
+ uint32_t m_ptr_size;
+ CompilerType m_uint_star_type;
+};
+
+namespace lldb_private {
+namespace formatters {
+
+SyntheticChildrenFrontEnd *
+NSIndexPathSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP valobj_sp) {
+ if (valobj_sp)
+ return new NSIndexPathSyntheticFrontEnd(valobj_sp);
+ return nullptr;
+}
+
+} // namespace formatters
+} // namespace lldb_private
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSSet.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSSet.cpp
new file mode 100644
index 000000000000..ebaa990fb74b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSSet.cpp
@@ -0,0 +1,675 @@
+//===-- NSSet.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 "NSSet.h"
+
+#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+std::map<ConstString, CXXFunctionSummaryFormat::Callback> &
+NSSet_Additionals::GetAdditionalSummaries() {
+ static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map;
+ return g_map;
+}
+
+std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> &
+NSSet_Additionals::GetAdditionalSynthetics() {
+ static std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback>
+ g_map;
+ return g_map;
+}
+
+namespace lldb_private {
+namespace formatters {
+class NSSetISyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ NSSetISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~NSSetISyntheticFrontEnd() override;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ struct DataDescriptor_32 {
+ uint32_t _used : 26;
+ uint32_t _szidx : 6;
+ };
+
+ struct DataDescriptor_64 {
+ uint64_t _used : 58;
+ uint32_t _szidx : 6;
+ };
+
+ struct SetItemDescriptor {
+ lldb::addr_t item_ptr;
+ lldb::ValueObjectSP valobj_sp;
+ };
+
+ ExecutionContextRef m_exe_ctx_ref;
+ uint8_t m_ptr_size;
+ DataDescriptor_32 *m_data_32;
+ DataDescriptor_64 *m_data_64;
+ lldb::addr_t m_data_ptr;
+ std::vector<SetItemDescriptor> m_children;
+};
+
+template <typename D32, typename D64>
+class GenericNSSetMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ GenericNSSetMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~GenericNSSetMSyntheticFrontEnd() override;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+
+ struct SetItemDescriptor {
+ lldb::addr_t item_ptr;
+ lldb::ValueObjectSP valobj_sp;
+ };
+
+ ExecutionContextRef m_exe_ctx_ref;
+ uint8_t m_ptr_size;
+ D32 *m_data_32;
+ D64 *m_data_64;
+ std::vector<SetItemDescriptor> m_children;
+};
+
+namespace Foundation1300 {
+ struct DataDescriptor_32 {
+ uint32_t _used : 26;
+ uint32_t _size;
+ uint32_t _mutations;
+ uint32_t _objs_addr;
+ };
+
+ struct DataDescriptor_64 {
+ uint64_t _used : 58;
+ uint64_t _size;
+ uint64_t _mutations;
+ uint64_t _objs_addr;
+ };
+
+ using NSSetMSyntheticFrontEnd =
+ GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
+}
+
+namespace Foundation1428 {
+ struct DataDescriptor_32 {
+ uint32_t _used : 26;
+ uint32_t _size;
+ uint32_t _objs_addr;
+ uint32_t _mutations;
+ };
+
+ struct DataDescriptor_64 {
+ uint64_t _used : 58;
+ uint64_t _size;
+ uint64_t _objs_addr;
+ uint64_t _mutations;
+ };
+
+ using NSSetMSyntheticFrontEnd =
+ GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
+}
+
+namespace Foundation1437 {
+ struct DataDescriptor_32 {
+ uint32_t _cow;
+ // __table storage
+ uint32_t _objs_addr;
+ uint32_t _muts;
+ uint32_t _used : 26;
+ uint32_t _szidx : 6;
+ };
+
+ struct DataDescriptor_64 {
+ uint64_t _cow;
+ // __Table storage
+ uint64_t _objs_addr;
+ uint32_t _muts;
+ uint32_t _used : 26;
+ uint32_t _szidx : 6;
+ };
+
+ using NSSetMSyntheticFrontEnd =
+ GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
+
+ template <typename DD>
+ uint64_t
+ __NSSetMSize_Impl(lldb_private::Process &process, lldb::addr_t valobj_addr,
+ Status &error) {
+ const lldb::addr_t start_of_descriptor =
+ valobj_addr + process.GetAddressByteSize();
+ DD descriptor = DD();
+ process.ReadMemory(start_of_descriptor, &descriptor, sizeof(descriptor),
+ error);
+ if (error.Fail()) {
+ return 0;
+ }
+ return descriptor._used;
+ }
+
+ uint64_t
+ __NSSetMSize(lldb_private::Process &process, lldb::addr_t valobj_addr,
+ Status &error) {
+ if (process.GetAddressByteSize() == 4) {
+ return __NSSetMSize_Impl<DataDescriptor_32>(process, valobj_addr, error);
+ } else {
+ return __NSSetMSize_Impl<DataDescriptor_64>(process, valobj_addr, error);
+ }
+ }
+}
+
+class NSSetCodeRunningSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ NSSetCodeRunningSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~NSSetCodeRunningSyntheticFrontEnd() override;
+
+ size_t CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+ bool Update() override;
+
+ bool MightHaveChildren() override;
+
+ size_t GetIndexOfChildWithName(ConstString name) override;
+};
+} // namespace formatters
+} // namespace lldb_private
+
+template <bool cf_style>
+bool lldb_private::formatters::NSSetSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ static ConstString g_TypeHint("NSSet");
+
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+ bool is_64bit = (ptr_size == 8);
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ uint64_t value = 0;
+
+ ConstString class_name_cs = descriptor->GetClassName();
+ const char *class_name = class_name_cs.GetCString();
+
+ if (!class_name || !*class_name)
+ return false;
+
+ if (!strcmp(class_name, "__NSSetI") ||
+ !strcmp(class_name, "__NSOrderedSetI")) {
+ Status error;
+ value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
+ ptr_size, 0, error);
+ if (error.Fail())
+ return false;
+ value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
+ } else if (!strcmp(class_name, "__NSSetM")) {
+ AppleObjCRuntime *apple_runtime =
+ llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
+ Status error;
+ if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
+ value = Foundation1437::__NSSetMSize(*process_sp, valobj_addr, error);
+ } else {
+ value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
+ ptr_size, 0, error);
+ value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
+ }
+ if (error.Fail())
+ return false;
+ } else {
+ auto &map(NSSet_Additionals::GetAdditionalSummaries());
+ auto iter = map.find(class_name_cs), end = map.end();
+ if (iter != end)
+ return iter->second(valobj, stream, options);
+ else
+ return false;
+ }
+
+ std::string prefix, suffix;
+ if (Language *language = Language::FindPlugin(options.GetLanguage())) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ stream.Printf("%s%" PRIu64 " %s%s%s", prefix.c_str(), value, "element",
+ value == 1 ? "" : "s", suffix.c_str());
+ return true;
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::NSSetSyntheticFrontEndCreator(
+ CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
+ lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
+ if (!process_sp)
+ return nullptr;
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+ if (!runtime)
+ return nullptr;
+
+ CompilerType valobj_type(valobj_sp->GetCompilerType());
+ Flags flags(valobj_type.GetTypeInfo());
+
+ if (flags.IsClear(eTypeIsPointer)) {
+ Status error;
+ valobj_sp = valobj_sp->AddressOf(error);
+ if (error.Fail() || !valobj_sp)
+ return nullptr;
+ }
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(*valobj_sp));
+
+ if (!descriptor || !descriptor->IsValid())
+ return nullptr;
+
+ ConstString class_name_cs = descriptor->GetClassName();
+ const char *class_name = class_name_cs.GetCString();
+
+ if (!class_name || !*class_name)
+ return nullptr;
+
+ if (!strcmp(class_name, "__NSSetI") ||
+ !strcmp(class_name, "__NSOrderedSetI")) {
+ return (new NSSetISyntheticFrontEnd(valobj_sp));
+ } else if (!strcmp(class_name, "__NSSetM")) {
+ AppleObjCRuntime *apple_runtime =
+ llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
+ if (apple_runtime) {
+ if (apple_runtime->GetFoundationVersion() >= 1437)
+ return (new Foundation1437::NSSetMSyntheticFrontEnd(valobj_sp));
+ else if (apple_runtime->GetFoundationVersion() >= 1428)
+ return (new Foundation1428::NSSetMSyntheticFrontEnd(valobj_sp));
+ else
+ return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp));
+ } else {
+ return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp));
+ }
+ } else {
+ auto &map(NSSet_Additionals::GetAdditionalSynthetics());
+ auto iter = map.find(class_name_cs), end = map.end();
+ if (iter != end)
+ return iter->second(synth, valobj_sp);
+ return nullptr;
+ }
+}
+
+lldb_private::formatters::NSSetISyntheticFrontEnd::NSSetISyntheticFrontEnd(
+ lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
+ m_data_32(nullptr), m_data_64(nullptr) {
+ if (valobj_sp)
+ Update();
+}
+
+lldb_private::formatters::NSSetISyntheticFrontEnd::~NSSetISyntheticFrontEnd() {
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+}
+
+size_t
+lldb_private::formatters::NSSetISyntheticFrontEnd::GetIndexOfChildWithName(
+ ConstString name) {
+ const char *item_name = name.GetCString();
+ uint32_t idx = ExtractIndexFromString(item_name);
+ if (idx < UINT32_MAX && idx >= CalculateNumChildren())
+ return UINT32_MAX;
+ return idx;
+}
+
+size_t
+lldb_private::formatters::NSSetISyntheticFrontEnd::CalculateNumChildren() {
+ if (!m_data_32 && !m_data_64)
+ return 0;
+ return (m_data_32 ? m_data_32->_used : m_data_64->_used);
+}
+
+bool lldb_private::formatters::NSSetISyntheticFrontEnd::Update() {
+ m_children.clear();
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+ m_ptr_size = 0;
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ if (!valobj_sp)
+ return false;
+ if (!valobj_sp)
+ return false;
+ m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
+ Status error;
+ if (valobj_sp->IsPointerType()) {
+ valobj_sp = valobj_sp->Dereference(error);
+ if (error.Fail() || !valobj_sp)
+ return false;
+ }
+ error.Clear();
+ lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
+ if (!process_sp)
+ return false;
+ m_ptr_size = process_sp->GetAddressByteSize();
+ uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size;
+ if (m_ptr_size == 4) {
+ m_data_32 = new DataDescriptor_32();
+ process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
+ error);
+ } else {
+ m_data_64 = new DataDescriptor_64();
+ process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
+ error);
+ }
+ if (error.Fail())
+ return false;
+ m_data_ptr = data_location + m_ptr_size;
+ return false;
+}
+
+bool lldb_private::formatters::NSSetISyntheticFrontEnd::MightHaveChildren() {
+ return true;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::NSSetISyntheticFrontEnd::GetChildAtIndex(size_t idx) {
+ uint32_t num_children = CalculateNumChildren();
+
+ if (idx >= num_children)
+ return lldb::ValueObjectSP();
+
+ ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
+ if (!process_sp)
+ return lldb::ValueObjectSP();
+
+ if (m_children.empty()) {
+ // do the scan phase
+ lldb::addr_t obj_at_idx = 0;
+
+ uint32_t tries = 0;
+ uint32_t test_idx = 0;
+
+ while (tries < num_children) {
+ obj_at_idx = m_data_ptr + (test_idx * m_ptr_size);
+ if (!process_sp)
+ return lldb::ValueObjectSP();
+ Status error;
+ obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error);
+ if (error.Fail())
+ return lldb::ValueObjectSP();
+
+ test_idx++;
+
+ if (!obj_at_idx)
+ continue;
+ tries++;
+
+ SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()};
+
+ m_children.push_back(descriptor);
+ }
+ }
+
+ if (idx >= m_children.size()) // should never happen
+ return lldb::ValueObjectSP();
+
+ SetItemDescriptor &set_item = m_children[idx];
+ if (!set_item.valobj_sp) {
+ auto ptr_size = process_sp->GetAddressByteSize();
+ DataBufferHeap buffer(ptr_size, 0);
+ switch (ptr_size) {
+ case 0: // architecture has no clue?? - fail
+ return lldb::ValueObjectSP();
+ case 4:
+ *((uint32_t *)buffer.GetBytes()) = (uint32_t)set_item.item_ptr;
+ break;
+ case 8:
+ *((uint64_t *)buffer.GetBytes()) = (uint64_t)set_item.item_ptr;
+ break;
+ default:
+ assert(false && "pointer size is not 4 nor 8 - get out of here ASAP");
+ }
+ StreamString idx_name;
+ idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+
+ DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(),
+ process_sp->GetByteOrder(),
+ process_sp->GetAddressByteSize());
+
+ set_item.valobj_sp = CreateValueObjectFromData(
+ idx_name.GetString(), data, m_exe_ctx_ref,
+ m_backend.GetCompilerType().GetBasicTypeFromAST(
+ lldb::eBasicTypeObjCID));
+ }
+ return set_item.valobj_sp;
+}
+
+template <typename D32, typename D64>
+lldb_private::formatters::
+ GenericNSSetMSyntheticFrontEnd<D32, D64>::GenericNSSetMSyntheticFrontEnd(
+ lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
+ m_data_32(nullptr), m_data_64(nullptr) {
+ if (valobj_sp)
+ Update();
+}
+
+template <typename D32, typename D64>
+lldb_private::formatters::
+ GenericNSSetMSyntheticFrontEnd<D32, D64>::~GenericNSSetMSyntheticFrontEnd() {
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+}
+
+template <typename D32, typename D64>
+size_t
+lldb_private::formatters::
+ GenericNSSetMSyntheticFrontEnd<D32, D64>::GetIndexOfChildWithName(
+ ConstString name) {
+ const char *item_name = name.GetCString();
+ uint32_t idx = ExtractIndexFromString(item_name);
+ if (idx < UINT32_MAX && idx >= CalculateNumChildren())
+ return UINT32_MAX;
+ return idx;
+}
+
+template <typename D32, typename D64>
+size_t
+lldb_private::formatters::
+ GenericNSSetMSyntheticFrontEnd<D32, D64>::CalculateNumChildren() {
+ if (!m_data_32 && !m_data_64)
+ return 0;
+ return (m_data_32 ? m_data_32->_used : m_data_64->_used);
+}
+
+template <typename D32, typename D64>
+bool
+lldb_private::formatters::
+ GenericNSSetMSyntheticFrontEnd<D32, D64>::Update() {
+ m_children.clear();
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ m_ptr_size = 0;
+ delete m_data_32;
+ m_data_32 = nullptr;
+ delete m_data_64;
+ m_data_64 = nullptr;
+ if (!valobj_sp)
+ return false;
+ if (!valobj_sp)
+ return false;
+ m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
+ Status error;
+ if (valobj_sp->IsPointerType()) {
+ valobj_sp = valobj_sp->Dereference(error);
+ if (error.Fail() || !valobj_sp)
+ return false;
+ }
+ error.Clear();
+ lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
+ if (!process_sp)
+ return false;
+ m_ptr_size = process_sp->GetAddressByteSize();
+ uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size;
+ if (m_ptr_size == 4) {
+ m_data_32 = new D32();
+ process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
+ error);
+ } else {
+ m_data_64 = new D64();
+ process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
+ error);
+ }
+ if (error.Fail())
+ return false;
+ return false;
+}
+
+template <typename D32, typename D64>
+bool
+lldb_private::formatters::
+ GenericNSSetMSyntheticFrontEnd<D32, D64>::MightHaveChildren() {
+ return true;
+}
+
+template <typename D32, typename D64>
+lldb::ValueObjectSP
+lldb_private::formatters::
+ GenericNSSetMSyntheticFrontEnd<D32, D64>::GetChildAtIndex(size_t idx) {
+ lldb::addr_t m_objs_addr =
+ (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
+
+ uint32_t num_children = CalculateNumChildren();
+
+ if (idx >= num_children)
+ return lldb::ValueObjectSP();
+
+ ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
+ if (!process_sp)
+ return lldb::ValueObjectSP();
+
+ if (m_children.empty()) {
+ // do the scan phase
+ lldb::addr_t obj_at_idx = 0;
+
+ uint32_t tries = 0;
+ uint32_t test_idx = 0;
+
+ while (tries < num_children) {
+ obj_at_idx = m_objs_addr + (test_idx * m_ptr_size);
+ if (!process_sp)
+ return lldb::ValueObjectSP();
+ Status error;
+ obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error);
+ if (error.Fail())
+ return lldb::ValueObjectSP();
+
+ test_idx++;
+
+ if (!obj_at_idx)
+ continue;
+ tries++;
+
+ SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()};
+
+ m_children.push_back(descriptor);
+ }
+ }
+
+ if (idx >= m_children.size()) // should never happen
+ return lldb::ValueObjectSP();
+
+ SetItemDescriptor &set_item = m_children[idx];
+ if (!set_item.valobj_sp) {
+ auto ptr_size = process_sp->GetAddressByteSize();
+ DataBufferHeap buffer(ptr_size, 0);
+ switch (ptr_size) {
+ case 0: // architecture has no clue?? - fail
+ return lldb::ValueObjectSP();
+ case 4:
+ *((uint32_t *)buffer.GetBytes()) = (uint32_t)set_item.item_ptr;
+ break;
+ case 8:
+ *((uint64_t *)buffer.GetBytes()) = (uint64_t)set_item.item_ptr;
+ break;
+ default:
+ assert(false && "pointer size is not 4 nor 8 - get out of here ASAP");
+ }
+ StreamString idx_name;
+ idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+
+ DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(),
+ process_sp->GetByteOrder(),
+ process_sp->GetAddressByteSize());
+
+ set_item.valobj_sp = CreateValueObjectFromData(
+ idx_name.GetString(), data, m_exe_ctx_ref,
+ m_backend.GetCompilerType().GetBasicTypeFromAST(
+ lldb::eBasicTypeObjCID));
+ }
+ return set_item.valobj_sp;
+}
+
+template bool lldb_private::formatters::NSSetSummaryProvider<true>(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options);
+
+template bool lldb_private::formatters::NSSetSummaryProvider<false>(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options);
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSSet.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSSet.h
new file mode 100644
index 000000000000..f11b6d406dec
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSSet.h
@@ -0,0 +1,39 @@
+//===-- NSSet.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 liblldb_NSSet_h_
+#define liblldb_NSSet_h_
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Stream.h"
+
+namespace lldb_private {
+namespace formatters {
+template <bool cf_style>
+bool NSSetSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+SyntheticChildrenFrontEnd *NSSetSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
+class NSSet_Additionals {
+public:
+ static std::map<ConstString, CXXFunctionSummaryFormat::Callback> &
+ GetAdditionalSummaries();
+
+ static std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> &
+ GetAdditionalSynthetics();
+};
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // liblldb_NSSet_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.cpp
new file mode 100644
index 000000000000..4800c955e5f5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.cpp
@@ -0,0 +1,396 @@
+//===-- NSString.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 "NSString.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/DataFormatters/StringPrinter.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/ProcessStructReader.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+std::map<ConstString, CXXFunctionSummaryFormat::Callback> &
+NSString_Additionals::GetAdditionalSummaries() {
+ static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map;
+ return g_map;
+}
+
+static CompilerType GetNSPathStore2Type(Target &target) {
+ static ConstString g_type_name("__lldb_autogen_nspathstore2");
+
+ ClangASTContext *ast_ctx = target.GetScratchClangASTContext();
+
+ if (!ast_ctx)
+ return CompilerType();
+
+ CompilerType voidstar =
+ ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType();
+ CompilerType uint32 =
+ ast_ctx->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32);
+
+ return ast_ctx->GetOrCreateStructForIdentifier(
+ g_type_name,
+ {{"isa", voidstar}, {"lengthAndRef", uint32}, {"buffer", voidstar}});
+}
+
+bool lldb_private::formatters::NSStringSummaryProvider(
+ ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &summary_options) {
+ static ConstString g_TypeHint("NSString");
+
+ ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+
+ if (!runtime)
+ return false;
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor(
+ runtime->GetClassDescriptor(valobj));
+
+ if (!descriptor.get() || !descriptor->IsValid())
+ return false;
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
+
+ if (!valobj_addr)
+ return false;
+
+ ConstString class_name_cs = descriptor->GetClassName();
+ const char *class_name = class_name_cs.GetCString();
+
+ if (!class_name || !*class_name)
+ return false;
+
+ bool is_tagged_ptr = (0 == strcmp(class_name, "NSTaggedPointerString")) &&
+ descriptor->GetTaggedPointerInfo();
+ // for a tagged pointer, the descriptor has everything we need
+ if (is_tagged_ptr)
+ return NSTaggedString_SummaryProvider(valobj, descriptor, stream,
+ summary_options);
+
+ auto &additionals_map(NSString_Additionals::GetAdditionalSummaries());
+ auto iter = additionals_map.find(class_name_cs), end = additionals_map.end();
+ if (iter != end)
+ return iter->second(valobj, stream, summary_options);
+
+ // if not a tagged pointer that we know about, try the normal route
+ uint64_t info_bits_location = valobj_addr + ptr_size;
+ if (process_sp->GetByteOrder() != lldb::eByteOrderLittle)
+ info_bits_location += 3;
+
+ Status error;
+
+ uint8_t info_bits = process_sp->ReadUnsignedIntegerFromMemory(
+ info_bits_location, 1, 0, error);
+ if (error.Fail())
+ return false;
+
+ bool is_mutable = (info_bits & 1) == 1;
+ bool is_inline = (info_bits & 0x60) == 0;
+ bool has_explicit_length = (info_bits & (1 | 4)) != 4;
+ bool is_unicode = (info_bits & 0x10) == 0x10;
+ bool is_path_store = strcmp(class_name, "NSPathStore2") == 0;
+ bool has_null = (info_bits & 8) == 8;
+
+ size_t explicit_length = 0;
+ if (!has_null && has_explicit_length && !is_path_store) {
+ lldb::addr_t explicit_length_offset = 2 * ptr_size;
+ if (is_mutable && !is_inline)
+ explicit_length_offset =
+ explicit_length_offset + ptr_size; // notInlineMutable.length;
+ else if (is_inline)
+ explicit_length = explicit_length + 0; // inline1.length;
+ else if (!is_inline && !is_mutable)
+ explicit_length_offset =
+ explicit_length_offset + ptr_size; // notInlineImmutable1.length;
+ else
+ explicit_length_offset = 0;
+
+ if (explicit_length_offset) {
+ explicit_length_offset = valobj_addr + explicit_length_offset;
+ explicit_length = process_sp->ReadUnsignedIntegerFromMemory(
+ explicit_length_offset, 4, 0, error);
+ }
+ }
+
+ if (strcmp(class_name, "NSString") && strcmp(class_name, "CFStringRef") &&
+ strcmp(class_name, "CFMutableStringRef") &&
+ strcmp(class_name, "__NSCFConstantString") &&
+ strcmp(class_name, "__NSCFString") &&
+ strcmp(class_name, "NSCFConstantString") &&
+ strcmp(class_name, "NSCFString") && strcmp(class_name, "NSPathStore2")) {
+ // not one of us - but tell me class name
+ stream.Printf("class name = %s", class_name);
+ return true;
+ }
+
+ std::string prefix, suffix;
+ if (Language *language =
+ Language::FindPlugin(summary_options.GetLanguage())) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);
+ options.SetPrefixToken(prefix);
+ options.SetSuffixToken(suffix);
+
+ if (is_mutable) {
+ uint64_t location = 2 * ptr_size + valobj_addr;
+ location = process_sp->ReadPointerFromMemory(location, error);
+ if (error.Fail())
+ return false;
+ if (has_explicit_length && is_unicode) {
+ options.SetLocation(location);
+ options.SetProcessSP(process_sp);
+ options.SetStream(&stream);
+ options.SetQuote('"');
+ options.SetSourceSize(explicit_length);
+ options.SetNeedsZeroTermination(false);
+ options.SetIgnoreMaxLength(summary_options.GetCapping() ==
+ TypeSummaryCapping::eTypeSummaryUncapped);
+ options.SetBinaryZeroIsTerminator(false);
+ options.SetLanguage(summary_options.GetLanguage());
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF16>(options);
+ } else {
+ options.SetLocation(location + 1);
+ options.SetProcessSP(process_sp);
+ options.SetStream(&stream);
+ options.SetSourceSize(explicit_length);
+ options.SetNeedsZeroTermination(false);
+ options.SetIgnoreMaxLength(summary_options.GetCapping() ==
+ TypeSummaryCapping::eTypeSummaryUncapped);
+ options.SetBinaryZeroIsTerminator(false);
+ options.SetLanguage(summary_options.GetLanguage());
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::ASCII>(options);
+ }
+ } else if (is_inline && has_explicit_length && !is_unicode &&
+ !is_path_store && !is_mutable) {
+ uint64_t location = 3 * ptr_size + valobj_addr;
+
+ options.SetLocation(location);
+ options.SetProcessSP(process_sp);
+ options.SetStream(&stream);
+ options.SetQuote('"');
+ options.SetSourceSize(explicit_length);
+ options.SetIgnoreMaxLength(summary_options.GetCapping() ==
+ TypeSummaryCapping::eTypeSummaryUncapped);
+ options.SetLanguage(summary_options.GetLanguage());
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::ASCII>(options);
+ } else if (is_unicode) {
+ uint64_t location = valobj_addr + 2 * ptr_size;
+ if (is_inline) {
+ if (!has_explicit_length) {
+ return false;
+ } else
+ location += ptr_size;
+ } else {
+ location = process_sp->ReadPointerFromMemory(location, error);
+ if (error.Fail())
+ return false;
+ }
+ options.SetLocation(location);
+ options.SetProcessSP(process_sp);
+ options.SetStream(&stream);
+ options.SetQuote('"');
+ options.SetSourceSize(explicit_length);
+ options.SetNeedsZeroTermination(!has_explicit_length);
+ options.SetIgnoreMaxLength(summary_options.GetCapping() ==
+ TypeSummaryCapping::eTypeSummaryUncapped);
+ options.SetBinaryZeroIsTerminator(!has_explicit_length);
+ options.SetLanguage(summary_options.GetLanguage());
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF16>(options);
+ } else if (is_path_store) {
+ ProcessStructReader reader(valobj.GetProcessSP().get(),
+ valobj.GetValueAsUnsigned(0),
+ GetNSPathStore2Type(*valobj.GetTargetSP()));
+ explicit_length =
+ reader.GetField<uint32_t>(ConstString("lengthAndRef")) >> 20;
+ lldb::addr_t location = valobj.GetValueAsUnsigned(0) + ptr_size + 4;
+
+ options.SetLocation(location);
+ options.SetProcessSP(process_sp);
+ options.SetStream(&stream);
+ options.SetQuote('"');
+ options.SetSourceSize(explicit_length);
+ options.SetNeedsZeroTermination(!has_explicit_length);
+ options.SetIgnoreMaxLength(summary_options.GetCapping() ==
+ TypeSummaryCapping::eTypeSummaryUncapped);
+ options.SetBinaryZeroIsTerminator(!has_explicit_length);
+ options.SetLanguage(summary_options.GetLanguage());
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF16>(options);
+ } else if (is_inline) {
+ uint64_t location = valobj_addr + 2 * ptr_size;
+ if (!has_explicit_length) {
+ // in this kind of string, the byte before the string content is a length
+ // byte so let's try and use it to handle the embedded NUL case
+ Status error;
+ explicit_length =
+ process_sp->ReadUnsignedIntegerFromMemory(location, 1, 0, error);
+ has_explicit_length = !(error.Fail() || explicit_length == 0);
+ location++;
+ }
+ options.SetLocation(location);
+ options.SetProcessSP(process_sp);
+ options.SetStream(&stream);
+ options.SetSourceSize(explicit_length);
+ options.SetNeedsZeroTermination(!has_explicit_length);
+ options.SetIgnoreMaxLength(summary_options.GetCapping() ==
+ TypeSummaryCapping::eTypeSummaryUncapped);
+ options.SetBinaryZeroIsTerminator(!has_explicit_length);
+ options.SetLanguage(summary_options.GetLanguage());
+ if (has_explicit_length)
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::UTF8>(options);
+ else
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::ASCII>(options);
+ } else {
+ uint64_t location = valobj_addr + 2 * ptr_size;
+ location = process_sp->ReadPointerFromMemory(location, error);
+ if (error.Fail())
+ return false;
+ if (has_explicit_length && !has_null)
+ explicit_length++; // account for the fact that there is no NULL and we
+ // need to have one added
+ options.SetLocation(location);
+ options.SetProcessSP(process_sp);
+ options.SetStream(&stream);
+ options.SetSourceSize(explicit_length);
+ options.SetIgnoreMaxLength(summary_options.GetCapping() ==
+ TypeSummaryCapping::eTypeSummaryUncapped);
+ options.SetLanguage(summary_options.GetLanguage());
+ return StringPrinter::ReadStringAndDumpToStream<
+ StringPrinter::StringElementType::ASCII>(options);
+ }
+}
+
+bool lldb_private::formatters::NSAttributedStringSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ TargetSP target_sp(valobj.GetTargetSP());
+ if (!target_sp)
+ return false;
+ uint32_t addr_size = target_sp->GetArchitecture().GetAddressByteSize();
+ uint64_t pointer_value = valobj.GetValueAsUnsigned(0);
+ if (!pointer_value)
+ return false;
+ pointer_value += addr_size;
+ CompilerType type(valobj.GetCompilerType());
+ ExecutionContext exe_ctx(target_sp, false);
+ ValueObjectSP child_ptr_sp(valobj.CreateValueObjectFromAddress(
+ "string_ptr", pointer_value, exe_ctx, type));
+ if (!child_ptr_sp)
+ return false;
+ DataExtractor data;
+ Status error;
+ child_ptr_sp->GetData(data, error);
+ if (error.Fail())
+ return false;
+ ValueObjectSP child_sp(child_ptr_sp->CreateValueObjectFromData(
+ "string_data", data, exe_ctx, type));
+ child_sp->GetValueAsUnsigned(0);
+ if (child_sp)
+ return NSStringSummaryProvider(*child_sp, stream, options);
+ return false;
+}
+
+bool lldb_private::formatters::NSMutableAttributedStringSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ return NSAttributedStringSummaryProvider(valobj, stream, options);
+}
+
+bool lldb_private::formatters::NSTaggedString_SummaryProvider(
+ ValueObject &valobj, ObjCLanguageRuntime::ClassDescriptorSP descriptor,
+ Stream &stream, const TypeSummaryOptions &summary_options) {
+ static ConstString g_TypeHint("NSString");
+
+ if (!descriptor)
+ return false;
+ uint64_t len_bits = 0, data_bits = 0;
+ if (!descriptor->GetTaggedPointerInfo(&len_bits, &data_bits, nullptr))
+ return false;
+
+ static const int g_MaxNonBitmaskedLen = 7; // TAGGED_STRING_UNPACKED_MAXLEN
+ static const int g_SixbitMaxLen = 9;
+ static const int g_fiveBitMaxLen = 11;
+
+ static const char *sixBitToCharLookup = "eilotrm.apdnsIc ufkMShjTRxgC4013"
+ "bDNvwyUL2O856P-B79AFKEWV_zGJ/HYX";
+
+ if (len_bits > g_fiveBitMaxLen)
+ return false;
+
+ std::string prefix, suffix;
+ if (Language *language =
+ Language::FindPlugin(summary_options.GetLanguage())) {
+ if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
+ suffix)) {
+ prefix.clear();
+ suffix.clear();
+ }
+ }
+
+ // this is a fairly ugly trick - pretend that the numeric value is actually a
+ // char* this works under a few assumptions: little endian architecture
+ // sizeof(uint64_t) > g_MaxNonBitmaskedLen
+ if (len_bits <= g_MaxNonBitmaskedLen) {
+ stream.Printf("%s", prefix.c_str());
+ stream.Printf("\"%s\"", (const char *)&data_bits);
+ stream.Printf("%s", suffix.c_str());
+ return true;
+ }
+
+ // if the data is bitmasked, we need to actually process the bytes
+ uint8_t bitmask = 0;
+ uint8_t shift_offset = 0;
+
+ if (len_bits <= g_SixbitMaxLen) {
+ bitmask = 0x03f;
+ shift_offset = 6;
+ } else {
+ bitmask = 0x01f;
+ shift_offset = 5;
+ }
+
+ std::vector<uint8_t> bytes;
+ bytes.resize(len_bits);
+ for (; len_bits > 0; data_bits >>= shift_offset, --len_bits) {
+ uint8_t packed = data_bits & bitmask;
+ bytes.insert(bytes.begin(), sixBitToCharLookup[packed]);
+ }
+
+ stream.Printf("%s", prefix.c_str());
+ stream.Printf("\"%s\"", &bytes[0]);
+ stream.Printf("%s", suffix.c_str());
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.h
new file mode 100644
index 000000000000..699d8eb36f88
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.h
@@ -0,0 +1,42 @@
+//===-- NSString.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 liblldb_NSString_h_
+#define liblldb_NSString_h_
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/Utility/Stream.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+namespace lldb_private {
+namespace formatters {
+bool NSStringSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool NSTaggedString_SummaryProvider(
+ ValueObject &valobj, ObjCLanguageRuntime::ClassDescriptorSP descriptor,
+ Stream &stream, const TypeSummaryOptions &summary_options);
+
+bool NSAttributedStringSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+bool NSMutableAttributedStringSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options);
+
+class NSString_Additionals {
+public:
+ static std::map<ConstString, CXXFunctionSummaryFormat::Callback> &
+ GetAdditionalSummaries();
+};
+} // namespace formatters
+} // namespace lldb_private
+
+#endif // liblldb_CF_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp
new file mode 100644
index 000000000000..f9ab18688de7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp
@@ -0,0 +1,1094 @@
+//===-- ObjCLanguage.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 <mutex>
+
+#include "ObjCLanguage.h"
+
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/DataVisualization.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "llvm/Support/Threading.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+#include "CF.h"
+#include "Cocoa.h"
+#include "CoreMedia.h"
+#include "NSDictionary.h"
+#include "NSSet.h"
+#include "NSString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+void ObjCLanguage::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(), "Objective-C Language",
+ CreateInstance);
+}
+
+void ObjCLanguage::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ObjCLanguage::GetPluginNameStatic() {
+ static ConstString g_name("objc");
+ return g_name;
+}
+
+// PluginInterface protocol
+
+lldb_private::ConstString ObjCLanguage::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ObjCLanguage::GetPluginVersion() { return 1; }
+
+// Static Functions
+
+Language *ObjCLanguage::CreateInstance(lldb::LanguageType language) {
+ switch (language) {
+ case lldb::eLanguageTypeObjC:
+ return new ObjCLanguage();
+ default:
+ return nullptr;
+ }
+}
+
+void ObjCLanguage::MethodName::Clear() {
+ m_full.Clear();
+ m_class.Clear();
+ m_category.Clear();
+ m_selector.Clear();
+ m_type = eTypeUnspecified;
+ m_category_is_valid = false;
+}
+
+bool ObjCLanguage::MethodName::SetName(llvm::StringRef name, bool strict) {
+ Clear();
+ if (name.empty())
+ return IsValid(strict);
+
+ // If "strict" is true. then the method must be specified with a '+' or '-'
+ // at the beginning. If "strict" is false, then the '+' or '-' can be omitted
+ bool valid_prefix = false;
+
+ if (name.size() > 1 && (name[0] == '+' || name[0] == '-')) {
+ valid_prefix = name[1] == '[';
+ if (name[0] == '+')
+ m_type = eTypeClassMethod;
+ else
+ m_type = eTypeInstanceMethod;
+ } else if (!strict) {
+ // "strict" is false, the name just needs to start with '['
+ valid_prefix = name[0] == '[';
+ }
+
+ if (valid_prefix) {
+ int name_len = name.size();
+ // Objective-C methods must have at least:
+ // "-[" or "+[" prefix
+ // One character for a class name
+ // One character for the space between the class name
+ // One character for the method name
+ // "]" suffix
+ if (name_len >= (5 + (strict ? 1 : 0)) && name.back() == ']') {
+ m_full.SetString(name);
+ }
+ }
+ return IsValid(strict);
+}
+
+bool ObjCLanguage::MethodName::SetName(const char *name, bool strict) {
+ return SetName(llvm::StringRef(name), strict);
+}
+
+ConstString ObjCLanguage::MethodName::GetClassName() {
+ if (!m_class) {
+ if (IsValid(false)) {
+ const char *full = m_full.GetCString();
+ const char *class_start = (full[0] == '[' ? full + 1 : full + 2);
+ const char *paren_pos = strchr(class_start, '(');
+ if (paren_pos) {
+ m_class.SetCStringWithLength(class_start, paren_pos - class_start);
+ } else {
+ // No '(' was found in the full name, we can definitively say that our
+ // category was valid (and empty).
+ m_category_is_valid = true;
+ const char *space_pos = strchr(full, ' ');
+ if (space_pos) {
+ m_class.SetCStringWithLength(class_start, space_pos - class_start);
+ if (!m_class_category) {
+ // No category in name, so we can also fill in the m_class_category
+ m_class_category = m_class;
+ }
+ }
+ }
+ }
+ }
+ return m_class;
+}
+
+ConstString ObjCLanguage::MethodName::GetClassNameWithCategory() {
+ if (!m_class_category) {
+ if (IsValid(false)) {
+ const char *full = m_full.GetCString();
+ const char *class_start = (full[0] == '[' ? full + 1 : full + 2);
+ const char *space_pos = strchr(full, ' ');
+ if (space_pos) {
+ m_class_category.SetCStringWithLength(class_start,
+ space_pos - class_start);
+ // If m_class hasn't been filled in and the class with category doesn't
+ // contain a '(', then we can also fill in the m_class
+ if (!m_class && strchr(m_class_category.GetCString(), '(') == nullptr) {
+ m_class = m_class_category;
+ // No '(' was found in the full name, we can definitively say that
+ // our category was valid (and empty).
+ m_category_is_valid = true;
+ }
+ }
+ }
+ }
+ return m_class_category;
+}
+
+ConstString ObjCLanguage::MethodName::GetSelector() {
+ if (!m_selector) {
+ if (IsValid(false)) {
+ const char *full = m_full.GetCString();
+ const char *space_pos = strchr(full, ' ');
+ if (space_pos) {
+ ++space_pos; // skip the space
+ m_selector.SetCStringWithLength(space_pos, m_full.GetLength() -
+ (space_pos - full) - 1);
+ }
+ }
+ }
+ return m_selector;
+}
+
+ConstString ObjCLanguage::MethodName::GetCategory() {
+ if (!m_category_is_valid && !m_category) {
+ if (IsValid(false)) {
+ m_category_is_valid = true;
+ const char *full = m_full.GetCString();
+ const char *class_start = (full[0] == '[' ? full + 1 : full + 2);
+ const char *open_paren_pos = strchr(class_start, '(');
+ if (open_paren_pos) {
+ ++open_paren_pos; // Skip the open paren
+ const char *close_paren_pos = strchr(open_paren_pos, ')');
+ if (close_paren_pos)
+ m_category.SetCStringWithLength(open_paren_pos,
+ close_paren_pos - open_paren_pos);
+ }
+ }
+ }
+ return m_category;
+}
+
+ConstString ObjCLanguage::MethodName::GetFullNameWithoutCategory(
+ bool empty_if_no_category) {
+ if (IsValid(false)) {
+ if (HasCategory()) {
+ StreamString strm;
+ if (m_type == eTypeClassMethod)
+ strm.PutChar('+');
+ else if (m_type == eTypeInstanceMethod)
+ strm.PutChar('-');
+ strm.Printf("[%s %s]", GetClassName().GetCString(),
+ GetSelector().GetCString());
+ return ConstString(strm.GetString());
+ }
+
+ if (!empty_if_no_category) {
+ // Just return the full name since it doesn't have a category
+ return GetFullName();
+ }
+ }
+ return ConstString();
+}
+
+std::vector<ConstString>
+ObjCLanguage::GetMethodNameVariants(ConstString method_name) const {
+ std::vector<ConstString> variant_names;
+ ObjCLanguage::MethodName objc_method(method_name.GetCString(), false);
+ if (!objc_method.IsValid(false)) {
+ return variant_names;
+ }
+
+ const bool is_class_method =
+ objc_method.GetType() == MethodName::eTypeClassMethod;
+ const bool is_instance_method =
+ objc_method.GetType() == MethodName::eTypeInstanceMethod;
+ ConstString name_sans_category =
+ objc_method.GetFullNameWithoutCategory(/*empty_if_no_category*/ true);
+
+ if (is_class_method || is_instance_method) {
+ if (name_sans_category)
+ variant_names.emplace_back(name_sans_category);
+ } else {
+ StreamString strm;
+
+ strm.Printf("+%s", objc_method.GetFullName().GetCString());
+ variant_names.emplace_back(strm.GetString());
+ strm.Clear();
+
+ strm.Printf("-%s", objc_method.GetFullName().GetCString());
+ variant_names.emplace_back(strm.GetString());
+ strm.Clear();
+
+ if (name_sans_category) {
+ strm.Printf("+%s", name_sans_category.GetCString());
+ variant_names.emplace_back(strm.GetString());
+ strm.Clear();
+
+ strm.Printf("-%s", name_sans_category.GetCString());
+ variant_names.emplace_back(strm.GetString());
+ }
+ }
+
+ return variant_names;
+}
+
+static void LoadObjCFormatters(TypeCategoryImplSP objc_category_sp) {
+ if (!objc_category_sp)
+ return;
+
+ TypeSummaryImpl::Flags objc_flags;
+ objc_flags.SetCascades(false)
+ .SetSkipPointers(true)
+ .SetSkipReferences(true)
+ .SetDontShowChildren(true)
+ .SetDontShowValue(true)
+ .SetShowMembersOneLiner(false)
+ .SetHideItemNames(false);
+
+ lldb::TypeSummaryImplSP ObjC_BOOL_summary(new CXXFunctionSummaryFormat(
+ objc_flags, lldb_private::formatters::ObjCBOOLSummaryProvider, ""));
+ objc_category_sp->GetTypeSummariesContainer()->Add(ConstString("BOOL"),
+ ObjC_BOOL_summary);
+ objc_category_sp->GetTypeSummariesContainer()->Add(ConstString("BOOL &"),
+ ObjC_BOOL_summary);
+ objc_category_sp->GetTypeSummariesContainer()->Add(ConstString("BOOL *"),
+ ObjC_BOOL_summary);
+
+ // we need to skip pointers here since we are special casing a SEL* when
+ // retrieving its value
+ objc_flags.SetSkipPointers(true);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::ObjCSELSummaryProvider<false>,
+ "SEL summary provider", ConstString("SEL"), objc_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::ObjCSELSummaryProvider<false>,
+ "SEL summary provider", ConstString("struct objc_selector"), objc_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::ObjCSELSummaryProvider<false>,
+ "SEL summary provider", ConstString("objc_selector"), objc_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::ObjCSELSummaryProvider<true>,
+ "SEL summary provider", ConstString("objc_selector *"), objc_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::ObjCSELSummaryProvider<true>,
+ "SEL summary provider", ConstString("SEL *"), objc_flags);
+
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::ObjCClassSummaryProvider,
+ "Class summary provider", ConstString("Class"), objc_flags);
+
+ SyntheticChildren::Flags class_synth_flags;
+ class_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(
+ false);
+
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::ObjCClassSyntheticFrontEndCreator,
+ "Class synthetic children", ConstString("Class"),
+ class_synth_flags);
+
+ objc_flags.SetSkipPointers(false);
+ objc_flags.SetCascades(true);
+ objc_flags.SetSkipReferences(false);
+
+ AddStringSummary(objc_category_sp, "${var.__FuncPtr%A}",
+ ConstString("__block_literal_generic"), objc_flags);
+
+ AddStringSummary(objc_category_sp, "${var.years} years, ${var.months} "
+ "months, ${var.days} days, ${var.hours} "
+ "hours, ${var.minutes} minutes "
+ "${var.seconds} seconds",
+ ConstString("CFGregorianUnits"), objc_flags);
+ AddStringSummary(objc_category_sp,
+ "location=${var.location} length=${var.length}",
+ ConstString("CFRange"), objc_flags);
+
+ AddStringSummary(objc_category_sp,
+ "location=${var.location}, length=${var.length}",
+ ConstString("NSRange"), objc_flags);
+ AddStringSummary(objc_category_sp, "(${var.origin}, ${var.size}), ...",
+ ConstString("NSRectArray"), objc_flags);
+
+ AddOneLineSummary(objc_category_sp, ConstString("NSPoint"), objc_flags);
+ AddOneLineSummary(objc_category_sp, ConstString("NSSize"), objc_flags);
+ AddOneLineSummary(objc_category_sp, ConstString("NSRect"), objc_flags);
+
+ AddOneLineSummary(objc_category_sp, ConstString("CGSize"), objc_flags);
+ AddOneLineSummary(objc_category_sp, ConstString("CGPoint"), objc_flags);
+ AddOneLineSummary(objc_category_sp, ConstString("CGRect"), objc_flags);
+
+ AddStringSummary(objc_category_sp,
+ "red=${var.red} green=${var.green} blue=${var.blue}",
+ ConstString("RGBColor"), objc_flags);
+ AddStringSummary(
+ objc_category_sp,
+ "(t=${var.top}, l=${var.left}, b=${var.bottom}, r=${var.right})",
+ ConstString("Rect"), objc_flags);
+ AddStringSummary(objc_category_sp, "{(v=${var.v}, h=${var.h})}",
+ ConstString("Point"), objc_flags);
+ AddStringSummary(objc_category_sp,
+ "${var.month}/${var.day}/${var.year} ${var.hour} "
+ ":${var.minute} :${var.second} dayOfWeek:${var.dayOfWeek}",
+ ConstString("DateTimeRect *"), objc_flags);
+ AddStringSummary(objc_category_sp, "${var.ld.month}/${var.ld.day}/"
+ "${var.ld.year} ${var.ld.hour} "
+ ":${var.ld.minute} :${var.ld.second} "
+ "dayOfWeek:${var.ld.dayOfWeek}",
+ ConstString("LongDateRect"), objc_flags);
+ AddStringSummary(objc_category_sp, "(x=${var.x}, y=${var.y})",
+ ConstString("HIPoint"), objc_flags);
+ AddStringSummary(objc_category_sp, "origin=${var.origin} size=${var.size}",
+ ConstString("HIRect"), objc_flags);
+
+ TypeSummaryImpl::Flags appkit_flags;
+ appkit_flags.SetCascades(true)
+ .SetSkipPointers(false)
+ .SetSkipReferences(false)
+ .SetDontShowChildren(true)
+ .SetDontShowValue(false)
+ .SetShowMembersOneLiner(false)
+ .SetHideItemNames(false);
+
+ appkit_flags.SetDontShowChildren(false);
+
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSArraySummaryProvider,
+ "NSArray summary provider", ConstString("NSArray"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSArraySummaryProvider,
+ "NSArray summary provider", ConstString("NSMutableArray"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSArraySummaryProvider,
+ "NSArray summary provider", ConstString("__NSArrayI"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSArraySummaryProvider,
+ "NSArray summary provider", ConstString("__NSArray0"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSArraySummaryProvider,
+ "NSArray summary provider",
+ ConstString("__NSSingleObjectArrayI"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSArraySummaryProvider,
+ "NSArray summary provider", ConstString("__NSArrayM"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSArraySummaryProvider,
+ "NSArray summary provider", ConstString("__NSCFArray"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSArraySummaryProvider,
+ "NSArray summary provider", ConstString("_NSCallStackArray"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSArraySummaryProvider,
+ "NSArray summary provider", ConstString("CFArrayRef"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSArraySummaryProvider,
+ "NSArray summary provider", ConstString("CFMutableArrayRef"),
+ appkit_flags);
+
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSDictionarySummaryProvider<false>,
+ "NSDictionary summary provider", ConstString("NSDictionary"),
+ appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSDictionarySummaryProvider<false>,
+ "NSDictionary summary provider",
+ ConstString("NSMutableDictionary"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSDictionarySummaryProvider<false>,
+ "NSDictionary summary provider",
+ ConstString("__NSCFDictionary"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSDictionarySummaryProvider<false>,
+ "NSDictionary summary provider", ConstString("__NSDictionaryI"),
+ appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSDictionarySummaryProvider<false>,
+ "NSDictionary summary provider",
+ ConstString("__NSSingleEntryDictionaryI"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSDictionarySummaryProvider<false>,
+ "NSDictionary summary provider", ConstString("__NSDictionaryM"),
+ appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSDictionarySummaryProvider<true>,
+ "NSDictionary summary provider", ConstString("CFDictionaryRef"),
+ appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSDictionarySummaryProvider<true>,
+ "NSDictionary summary provider",
+ ConstString("CFMutableDictionaryRef"), appkit_flags);
+
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSSetSummaryProvider<false>,
+ "NSSet summary", ConstString("NSSet"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSSetSummaryProvider<false>,
+ "NSMutableSet summary", ConstString("NSMutableSet"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSSetSummaryProvider<true>,
+ "CFSetRef summary", ConstString("CFSetRef"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSSetSummaryProvider<true>,
+ "CFMutableSetRef summary", ConstString("CFMutableSetRef"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSSetSummaryProvider<false>,
+ "__NSCFSet summary", ConstString("__NSCFSet"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSSetSummaryProvider<false>,
+ "__NSSetI summary", ConstString("__NSSetI"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSSetSummaryProvider<false>,
+ "__NSSetM summary", ConstString("__NSSetM"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSSetSummaryProvider<false>,
+ "NSCountedSet summary", ConstString("NSCountedSet"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSSetSummaryProvider<false>,
+ "NSMutableSet summary", ConstString("NSMutableSet"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSSetSummaryProvider<false>,
+ "NSOrderedSet summary", ConstString("NSOrderedSet"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSSetSummaryProvider<false>,
+ "__NSOrderedSetI summary", ConstString("__NSOrderedSetI"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSSetSummaryProvider<false>,
+ "__NSOrderedSetM summary", ConstString("__NSOrderedSetM"), appkit_flags);
+
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSError_SummaryProvider,
+ "NSError summary provider", ConstString("NSError"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSException_SummaryProvider,
+ "NSException summary provider", ConstString("NSException"), appkit_flags);
+
+ // AddSummary(appkit_category_sp, "${var.key%@} -> ${var.value%@}",
+ // ConstString("$_lldb_typegen_nspair"), appkit_flags);
+
+ appkit_flags.SetDontShowChildren(true);
+
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSArraySyntheticFrontEndCreator,
+ "NSArray synthetic children", ConstString("__NSArrayM"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSArraySyntheticFrontEndCreator,
+ "NSArray synthetic children", ConstString("__NSArrayI"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSArraySyntheticFrontEndCreator,
+ "NSArray synthetic children", ConstString("__NSArray0"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSArraySyntheticFrontEndCreator,
+ "NSArray synthetic children",
+ ConstString("__NSSingleObjectArrayI"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSArraySyntheticFrontEndCreator,
+ "NSArray synthetic children", ConstString("NSArray"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSArraySyntheticFrontEndCreator,
+ "NSArray synthetic children", ConstString("NSMutableArray"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSArraySyntheticFrontEndCreator,
+ "NSArray synthetic children", ConstString("__NSCFArray"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSArraySyntheticFrontEndCreator,
+ "NSArray synthetic children", ConstString("_NSCallStackArray"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSArraySyntheticFrontEndCreator,
+ "NSArray synthetic children",
+ ConstString("CFMutableArrayRef"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSArraySyntheticFrontEndCreator,
+ "NSArray synthetic children", ConstString("CFArrayRef"),
+ ScriptedSyntheticChildren::Flags());
+
+ AddCXXSynthetic(
+ objc_category_sp,
+ lldb_private::formatters::NSDictionarySyntheticFrontEndCreator,
+ "NSDictionary synthetic children", ConstString("__NSDictionaryM"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(
+ objc_category_sp,
+ lldb_private::formatters::NSDictionarySyntheticFrontEndCreator,
+ "NSDictionary synthetic children", ConstString("__NSDictionaryI"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(
+ objc_category_sp,
+ lldb_private::formatters::NSDictionarySyntheticFrontEndCreator,
+ "NSDictionary synthetic children",
+ ConstString("__NSSingleEntryDictionaryI"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(
+ objc_category_sp,
+ lldb_private::formatters::NSDictionarySyntheticFrontEndCreator,
+ "NSDictionary synthetic children", ConstString("__NSCFDictionary"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(
+ objc_category_sp,
+ lldb_private::formatters::NSDictionarySyntheticFrontEndCreator,
+ "NSDictionary synthetic children", ConstString("NSDictionary"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(
+ objc_category_sp,
+ lldb_private::formatters::NSDictionarySyntheticFrontEndCreator,
+ "NSDictionary synthetic children", ConstString("NSMutableDictionary"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(
+ objc_category_sp,
+ lldb_private::formatters::NSDictionarySyntheticFrontEndCreator,
+ "NSDictionary synthetic children", ConstString("CFDictionaryRef"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(
+ objc_category_sp,
+ lldb_private::formatters::NSDictionarySyntheticFrontEndCreator,
+ "NSDictionary synthetic children", ConstString("CFMutableDictionaryRef"),
+ ScriptedSyntheticChildren::Flags());
+
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSErrorSyntheticFrontEndCreator,
+ "NSError synthetic children", ConstString("NSError"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSExceptionSyntheticFrontEndCreator,
+ "NSException synthetic children", ConstString("NSException"),
+ ScriptedSyntheticChildren::Flags());
+
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSSetSyntheticFrontEndCreator,
+ "NSSet synthetic children", ConstString("NSSet"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSSetSyntheticFrontEndCreator,
+ "__NSSetI synthetic children", ConstString("__NSSetI"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSSetSyntheticFrontEndCreator,
+ "__NSSetM synthetic children", ConstString("__NSSetM"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(
+ objc_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator,
+ "NSMutableSet synthetic children", ConstString("NSMutableSet"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(
+ objc_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator,
+ "NSOrderedSet synthetic children", ConstString("NSOrderedSet"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(
+ objc_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator,
+ "__NSOrderedSetI synthetic children", ConstString("__NSOrderedSetI"),
+ ScriptedSyntheticChildren::Flags());
+ AddCXXSynthetic(
+ objc_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator,
+ "__NSOrderedSetM synthetic children", ConstString("__NSOrderedSetM"),
+ ScriptedSyntheticChildren::Flags());
+
+ AddCXXSynthetic(objc_category_sp,
+ lldb_private::formatters::NSIndexPathSyntheticFrontEndCreator,
+ "NSIndexPath synthetic children", ConstString("NSIndexPath"),
+ ScriptedSyntheticChildren::Flags());
+
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::CFBagSummaryProvider,
+ "CFBag summary provider", ConstString("CFBagRef"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::CFBagSummaryProvider,
+ "CFBag summary provider", ConstString("__CFBag"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::CFBagSummaryProvider,
+ "CFBag summary provider", ConstString("const struct __CFBag"),
+ appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::CFBagSummaryProvider,
+ "CFBag summary provider", ConstString("CFMutableBagRef"), appkit_flags);
+
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::CFBinaryHeapSummaryProvider,
+ "CFBinaryHeap summary provider", ConstString("CFBinaryHeapRef"),
+ appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::CFBinaryHeapSummaryProvider,
+ "CFBinaryHeap summary provider", ConstString("__CFBinaryHeap"),
+ appkit_flags);
+
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSStringSummaryProvider,
+ "NSString summary provider", ConstString("NSString"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSStringSummaryProvider,
+ "NSString summary provider", ConstString("CFStringRef"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSStringSummaryProvider,
+ "NSString summary provider", ConstString("__CFString"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSStringSummaryProvider,
+ "NSString summary provider", ConstString("CFMutableStringRef"),
+ appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSStringSummaryProvider,
+ "NSString summary provider", ConstString("NSMutableString"),
+ appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSStringSummaryProvider,
+ "NSString summary provider",
+ ConstString("__NSCFConstantString"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSStringSummaryProvider,
+ "NSString summary provider", ConstString("__NSCFString"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSStringSummaryProvider,
+ "NSString summary provider", ConstString("NSCFConstantString"),
+ appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSStringSummaryProvider,
+ "NSString summary provider", ConstString("NSCFString"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSStringSummaryProvider,
+ "NSString summary provider", ConstString("NSPathStore2"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSStringSummaryProvider,
+ "NSString summary provider",
+ ConstString("NSTaggedPointerString"), appkit_flags);
+
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSAttributedStringSummaryProvider,
+ "NSAttributedString summary provider",
+ ConstString("NSAttributedString"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp,
+ lldb_private::formatters::NSMutableAttributedStringSummaryProvider,
+ "NSMutableAttributedString summary provider",
+ ConstString("NSMutableAttributedString"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp,
+ lldb_private::formatters::NSMutableAttributedStringSummaryProvider,
+ "NSMutableAttributedString summary provider",
+ ConstString("NSConcreteMutableAttributedString"), appkit_flags);
+
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSBundleSummaryProvider,
+ "NSBundle summary provider", ConstString("NSBundle"), appkit_flags);
+
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSDataSummaryProvider<false>,
+ "NSData summary provider", ConstString("NSData"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSDataSummaryProvider<false>,
+ "NSData summary provider", ConstString("_NSInlineData"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSDataSummaryProvider<false>,
+ "NSData summary provider", ConstString("NSConcreteData"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSDataSummaryProvider<false>,
+ "NSData summary provider", ConstString("NSConcreteMutableData"),
+ appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSDataSummaryProvider<false>,
+ "NSData summary provider", ConstString("NSMutableData"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSDataSummaryProvider<false>,
+ "NSData summary provider", ConstString("__NSCFData"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSDataSummaryProvider<true>,
+ "NSData summary provider", ConstString("CFDataRef"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSDataSummaryProvider<true>,
+ "NSData summary provider", ConstString("CFMutableDataRef"), appkit_flags);
+
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSMachPortSummaryProvider,
+ "NSMachPort summary provider", ConstString("NSMachPort"), appkit_flags);
+
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSNotificationSummaryProvider,
+ "NSNotification summary provider",
+ ConstString("NSNotification"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSNotificationSummaryProvider,
+ "NSNotification summary provider",
+ ConstString("NSConcreteNotification"), appkit_flags);
+
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSNumberSummaryProvider,
+ "NSNumber summary provider", ConstString("NSNumber"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSNumberSummaryProvider,
+ "CFNumberRef summary provider", ConstString("CFNumberRef"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSNumberSummaryProvider,
+ "NSNumber summary provider", ConstString("__NSCFBoolean"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSNumberSummaryProvider,
+ "NSNumber summary provider", ConstString("__NSCFNumber"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSNumberSummaryProvider,
+ "NSNumber summary provider", ConstString("NSCFBoolean"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSNumberSummaryProvider,
+ "NSNumber summary provider", ConstString("NSCFNumber"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSNumberSummaryProvider,
+ "NSDecimalNumber summary provider",
+ ConstString("NSDecimalNumber"), appkit_flags);
+
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSURLSummaryProvider,
+ "NSURL summary provider", ConstString("NSURL"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSURLSummaryProvider,
+ "NSURL summary provider", ConstString("CFURLRef"), appkit_flags);
+
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSDateSummaryProvider,
+ "NSDate summary provider", ConstString("NSDate"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSDateSummaryProvider,
+ "NSDate summary provider", ConstString("__NSDate"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSDateSummaryProvider,
+ "NSDate summary provider", ConstString("__NSTaggedDate"), appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSDateSummaryProvider,
+ "NSDate summary provider", ConstString("NSCalendarDate"), appkit_flags);
+
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSTimeZoneSummaryProvider,
+ "NSTimeZone summary provider", ConstString("NSTimeZone"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSTimeZoneSummaryProvider,
+ "NSTimeZone summary provider", ConstString("CFTimeZoneRef"),
+ appkit_flags);
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSTimeZoneSummaryProvider,
+ "NSTimeZone summary provider", ConstString("__NSTimeZone"), appkit_flags);
+
+ // CFAbsoluteTime is actually a double rather than a pointer to an object we
+ // do not care about the numeric value, since it is probably meaningless to
+ // users
+ appkit_flags.SetDontShowValue(true);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::CFAbsoluteTimeSummaryProvider,
+ "CFAbsoluteTime summary provider",
+ ConstString("CFAbsoluteTime"), appkit_flags);
+ appkit_flags.SetDontShowValue(false);
+
+ AddCXXSummary(
+ objc_category_sp, lldb_private::formatters::NSIndexSetSummaryProvider,
+ "NSIndexSet summary provider", ConstString("NSIndexSet"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::NSIndexSetSummaryProvider,
+ "NSIndexSet summary provider", ConstString("NSMutableIndexSet"),
+ appkit_flags);
+
+ AddStringSummary(objc_category_sp,
+ "@\"${var.month%d}/${var.day%d}/${var.year%d} "
+ "${var.hour%d}:${var.minute%d}:${var.second}\"",
+ ConstString("CFGregorianDate"), appkit_flags);
+
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::CFBitVectorSummaryProvider,
+ "CFBitVector summary provider", ConstString("CFBitVectorRef"),
+ appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::CFBitVectorSummaryProvider,
+ "CFBitVector summary provider",
+ ConstString("CFMutableBitVectorRef"), appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::CFBitVectorSummaryProvider,
+ "CFBitVector summary provider", ConstString("__CFBitVector"),
+ appkit_flags);
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::CFBitVectorSummaryProvider,
+ "CFBitVector summary provider",
+ ConstString("__CFMutableBitVector"), appkit_flags);
+}
+
+static void LoadCoreMediaFormatters(TypeCategoryImplSP objc_category_sp) {
+ if (!objc_category_sp)
+ return;
+
+ TypeSummaryImpl::Flags cm_flags;
+ cm_flags.SetCascades(true)
+ .SetDontShowChildren(false)
+ .SetDontShowValue(false)
+ .SetHideItemNames(false)
+ .SetShowMembersOneLiner(false)
+ .SetSkipPointers(false)
+ .SetSkipReferences(false);
+
+ AddCXXSummary(objc_category_sp,
+ lldb_private::formatters::CMTimeSummaryProvider,
+ "CMTime summary provider", ConstString("CMTime"), cm_flags);
+}
+
+lldb::TypeCategoryImplSP ObjCLanguage::GetFormatters() {
+ static llvm::once_flag g_initialize;
+ static TypeCategoryImplSP g_category;
+
+ llvm::call_once(g_initialize, [this]() -> void {
+ DataVisualization::Categories::GetCategory(GetPluginName(), g_category);
+ if (g_category) {
+ LoadCoreMediaFormatters(g_category);
+ LoadObjCFormatters(g_category);
+ }
+ });
+ return g_category;
+}
+
+std::vector<ConstString>
+ObjCLanguage::GetPossibleFormattersMatches(ValueObject &valobj,
+ lldb::DynamicValueType use_dynamic) {
+ std::vector<ConstString> result;
+
+ if (use_dynamic == lldb::eNoDynamicValues)
+ return result;
+
+ CompilerType compiler_type(valobj.GetCompilerType());
+
+ const bool check_cpp = false;
+ const bool check_objc = true;
+ bool canBeObjCDynamic =
+ compiler_type.IsPossibleDynamicType(nullptr, check_cpp, check_objc);
+
+ if (canBeObjCDynamic) {
+ do {
+ lldb::ProcessSP process_sp = valobj.GetProcessSP();
+ if (!process_sp)
+ break;
+ ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
+ if (runtime == nullptr)
+ break;
+ ObjCLanguageRuntime::ClassDescriptorSP objc_class_sp(
+ runtime->GetClassDescriptor(valobj));
+ if (!objc_class_sp)
+ break;
+ if (ConstString name = objc_class_sp->GetClassName())
+ result.push_back(name);
+ } while (false);
+ }
+
+ return result;
+}
+
+std::unique_ptr<Language::TypeScavenger> ObjCLanguage::GetTypeScavenger() {
+ class ObjCScavengerResult : public Language::TypeScavenger::Result {
+ public:
+ ObjCScavengerResult(CompilerType type)
+ : Language::TypeScavenger::Result(), m_compiler_type(type) {}
+
+ bool IsValid() override { return m_compiler_type.IsValid(); }
+
+ bool DumpToStream(Stream &stream, bool print_help_if_available) override {
+ if (IsValid()) {
+ m_compiler_type.DumpTypeDescription(&stream);
+ stream.EOL();
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ CompilerType m_compiler_type;
+ };
+
+ class ObjCRuntimeScavenger : public Language::TypeScavenger {
+ protected:
+ bool Find_Impl(ExecutionContextScope *exe_scope, const char *key,
+ ResultSet &results) override {
+ bool result = false;
+
+ if (auto *process = exe_scope->CalculateProcess().get()) {
+ if (auto *objc_runtime = ObjCLanguageRuntime::Get(*process)) {
+ if (auto *decl_vendor = objc_runtime->GetDeclVendor()) {
+ ConstString name(key);
+ for (const CompilerType &type :
+ decl_vendor->FindTypes(name, /*max_matches*/ UINT32_MAX)) {
+ result = true;
+ std::unique_ptr<Language::TypeScavenger::Result> result(
+ new ObjCScavengerResult(type));
+ results.insert(std::move(result));
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ friend class lldb_private::ObjCLanguage;
+ };
+
+ class ObjCModulesScavenger : public Language::TypeScavenger {
+ protected:
+ bool Find_Impl(ExecutionContextScope *exe_scope, const char *key,
+ ResultSet &results) override {
+ bool result = false;
+
+ if (auto *target = exe_scope->CalculateTarget().get()) {
+ if (auto *clang_modules_decl_vendor =
+ target->GetClangModulesDeclVendor()) {
+ ConstString key_cs(key);
+ auto types = clang_modules_decl_vendor->FindTypes(
+ key_cs, /*max_matches*/ UINT32_MAX);
+ if (!types.empty()) {
+ result = true;
+ std::unique_ptr<Language::TypeScavenger::Result> result(
+ new ObjCScavengerResult(types.front()));
+ results.insert(std::move(result));
+ }
+ }
+ }
+
+ return result;
+ }
+
+ friend class lldb_private::ObjCLanguage;
+ };
+
+ class ObjCDebugInfoScavenger : public Language::ImageListTypeScavenger {
+ public:
+ CompilerType AdjustForInclusion(CompilerType &candidate) override {
+ LanguageType lang_type(candidate.GetMinimumLanguage());
+ if (!Language::LanguageIsObjC(lang_type))
+ return CompilerType();
+ if (candidate.IsTypedefType())
+ return candidate.GetTypedefedType();
+ return candidate;
+ }
+ };
+
+ return std::unique_ptr<TypeScavenger>(
+ new Language::EitherTypeScavenger<ObjCModulesScavenger,
+ ObjCRuntimeScavenger,
+ ObjCDebugInfoScavenger>());
+}
+
+bool ObjCLanguage::GetFormatterPrefixSuffix(ValueObject &valobj,
+ ConstString type_hint,
+ std::string &prefix,
+ std::string &suffix) {
+ static ConstString g_CFBag("CFBag");
+ static ConstString g_CFBinaryHeap("CFBinaryHeap");
+
+ static ConstString g_NSNumberChar("NSNumber:char");
+ static ConstString g_NSNumberShort("NSNumber:short");
+ static ConstString g_NSNumberInt("NSNumber:int");
+ static ConstString g_NSNumberLong("NSNumber:long");
+ static ConstString g_NSNumberInt128("NSNumber:int128_t");
+ static ConstString g_NSNumberFloat("NSNumber:float");
+ static ConstString g_NSNumberDouble("NSNumber:double");
+
+ static ConstString g_NSData("NSData");
+ static ConstString g_NSArray("NSArray");
+ static ConstString g_NSString("NSString");
+ static ConstString g_NSStringStar("NSString*");
+
+ if (type_hint.IsEmpty())
+ return false;
+
+ prefix.clear();
+ suffix.clear();
+
+ if (type_hint == g_CFBag || type_hint == g_CFBinaryHeap) {
+ prefix = "@";
+ return true;
+ }
+
+ if (type_hint == g_NSNumberChar) {
+ prefix = "(char)";
+ return true;
+ }
+ if (type_hint == g_NSNumberShort) {
+ prefix = "(short)";
+ return true;
+ }
+ if (type_hint == g_NSNumberInt) {
+ prefix = "(int)";
+ return true;
+ }
+ if (type_hint == g_NSNumberLong) {
+ prefix = "(long)";
+ return true;
+ }
+ if (type_hint == g_NSNumberInt128) {
+ prefix = "(int128_t)";
+ return true;
+ }
+ if (type_hint == g_NSNumberFloat) {
+ prefix = "(float)";
+ return true;
+ }
+ if (type_hint == g_NSNumberDouble) {
+ prefix = "(double)";
+ return true;
+ }
+
+ if (type_hint == g_NSData || type_hint == g_NSArray) {
+ prefix = "@\"";
+ suffix = "\"";
+ return true;
+ }
+
+ if (type_hint == g_NSString || type_hint == g_NSStringStar) {
+ prefix = "@";
+ return true;
+ }
+
+ return false;
+}
+
+bool ObjCLanguage::IsNilReference(ValueObject &valobj) {
+ const uint32_t mask = eTypeIsObjC | eTypeIsPointer;
+ bool isObjCpointer =
+ (((valobj.GetCompilerType().GetTypeInfo(nullptr)) & mask) == mask);
+ if (!isObjCpointer)
+ return false;
+ bool canReadValue = true;
+ bool isZero = valobj.GetValueAsUnsigned(0, &canReadValue) == 0;
+ return canReadValue && isZero;
+}
+
+bool ObjCLanguage::IsSourceFile(llvm::StringRef file_path) const {
+ const auto suffixes = {".h", ".m", ".M"};
+ for (auto suffix : suffixes) {
+ if (file_path.endswith_lower(suffix))
+ return true;
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h
new file mode 100644
index 000000000000..3e2cc0972993
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h
@@ -0,0 +1,163 @@
+//===-- ObjCLanguage.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 liblldb_ObjCLanguage_h_
+#define liblldb_ObjCLanguage_h_
+
+#include <cstring>
+#include <vector>
+
+#include "Plugins/Language/ClangCommon/ClangHighlighter.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class ObjCLanguage : public Language {
+ ClangHighlighter m_highlighter;
+
+public:
+ class MethodName {
+ public:
+ enum Type { eTypeUnspecified, eTypeClassMethod, eTypeInstanceMethod };
+
+ MethodName()
+ : m_full(), m_class(), m_category(), m_selector(),
+ m_type(eTypeUnspecified), m_category_is_valid(false) {}
+
+ MethodName(const char *name, bool strict)
+ : m_full(), m_class(), m_category(), m_selector(),
+ m_type(eTypeUnspecified), m_category_is_valid(false) {
+ SetName(name, strict);
+ }
+ MethodName(llvm::StringRef name, bool strict)
+ : m_full(), m_class(), m_category(), m_selector(),
+ m_type(eTypeUnspecified), m_category_is_valid(false) {
+ SetName(name, strict);
+ }
+
+ void Clear();
+
+ bool IsValid(bool strict) const {
+ // If "strict" is true, the name must have everything specified including
+ // the leading "+" or "-" on the method name
+ if (strict && m_type == eTypeUnspecified)
+ return false;
+ // Other than that, m_full will only be filled in if the objective C
+ // name is valid.
+ return (bool)m_full;
+ }
+
+ bool HasCategory() { return !GetCategory().IsEmpty(); }
+
+ Type GetType() const { return m_type; }
+
+ ConstString GetFullName() const { return m_full; }
+
+ ConstString GetFullNameWithoutCategory(bool empty_if_no_category);
+
+ bool SetName(const char *name, bool strict);
+ bool SetName(llvm::StringRef name, bool strict);
+
+ ConstString GetClassName();
+
+ ConstString GetClassNameWithCategory();
+
+ ConstString GetCategory();
+
+ ConstString GetSelector();
+
+ protected:
+ ConstString
+ m_full; // Full name: "+[NSString(my_additions) myStringWithCString:]"
+ ConstString m_class; // Class name: "NSString"
+ ConstString
+ m_class_category; // Class with category: "NSString(my_additions)"
+ ConstString m_category; // Category: "my_additions"
+ ConstString m_selector; // Selector: "myStringWithCString:"
+ Type m_type;
+ bool m_category_is_valid;
+ };
+
+ ObjCLanguage() = default;
+
+ ~ObjCLanguage() override = default;
+
+ lldb::LanguageType GetLanguageType() const override {
+ return lldb::eLanguageTypeObjC;
+ }
+
+ // Get all possible names for a method. Examples:
+ // If method_name is "+[NSString(my_additions) myStringWithCString:]"
+ // variant_names[0] => "+[NSString myStringWithCString:]"
+ // If name is specified without the leading '+' or '-' like
+ // "[NSString(my_additions) myStringWithCString:]"
+ // variant_names[0] => "+[NSString(my_additions) myStringWithCString:]"
+ // variant_names[1] => "-[NSString(my_additions) myStringWithCString:]"
+ // variant_names[2] => "+[NSString myStringWithCString:]"
+ // variant_names[3] => "-[NSString myStringWithCString:]"
+ std::vector<ConstString>
+ GetMethodNameVariants(ConstString method_name) const override;
+
+ lldb::TypeCategoryImplSP GetFormatters() override;
+
+ std::vector<ConstString>
+ GetPossibleFormattersMatches(ValueObject &valobj,
+ lldb::DynamicValueType use_dynamic) override;
+
+ std::unique_ptr<TypeScavenger> GetTypeScavenger() override;
+
+ bool GetFormatterPrefixSuffix(ValueObject &valobj, ConstString type_hint,
+ std::string &prefix,
+ std::string &suffix) override;
+
+ bool IsNilReference(ValueObject &valobj) override;
+
+ bool IsSourceFile(llvm::StringRef file_path) const override;
+
+ const Highlighter *GetHighlighter() const override { return &m_highlighter; }
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::Language *CreateInstance(lldb::LanguageType language);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static bool IsPossibleObjCMethodName(const char *name) {
+ if (!name)
+ return false;
+ bool starts_right = (name[0] == '+' || name[0] == '-') && name[1] == '[';
+ bool ends_right = (name[strlen(name) - 1] == ']');
+ return (starts_right && ends_right);
+ }
+
+ static bool IsPossibleObjCSelector(const char *name) {
+ if (!name)
+ return false;
+
+ if (strchr(name, ':') == nullptr)
+ return true;
+ else if (name[strlen(name) - 1] == ':')
+ return true;
+ else
+ return false;
+ }
+
+ // PluginInterface protocol
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ObjCLanguage_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.cpp
new file mode 100644
index 000000000000..81b3c5807c41
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.cpp
@@ -0,0 +1,56 @@
+//===-- ObjCPlusPlusLanguage.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 "ObjCPlusPlusLanguage.h"
+
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Utility/ConstString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+bool ObjCPlusPlusLanguage::IsSourceFile(llvm::StringRef file_path) const {
+ const auto suffixes = {".h", ".mm"};
+ for (auto suffix : suffixes) {
+ if (file_path.endswith_lower(suffix))
+ return true;
+ }
+ return false;
+}
+
+void ObjCPlusPlusLanguage::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(), "Objective-C++ Language",
+ CreateInstance);
+}
+
+void ObjCPlusPlusLanguage::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ObjCPlusPlusLanguage::GetPluginNameStatic() {
+ static ConstString g_name("objcplusplus");
+ return g_name;
+}
+
+// PluginInterface protocol
+lldb_private::ConstString ObjCPlusPlusLanguage::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ObjCPlusPlusLanguage::GetPluginVersion() { return 1; }
+
+// Static Functions
+Language *ObjCPlusPlusLanguage::CreateInstance(lldb::LanguageType language) {
+ switch (language) {
+ case lldb::eLanguageTypeObjC_plus_plus:
+ return new ObjCPlusPlusLanguage();
+ default:
+ return nullptr;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h
new file mode 100644
index 000000000000..6224a3f47b3b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h
@@ -0,0 +1,51 @@
+//===-- ObjCPlusPlusLanguage.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 liblldb_ObjCPlusPlusLanguage_h_
+#define liblldb_ObjCPlusPlusLanguage_h_
+
+#include "Plugins/Language/ClangCommon/ClangHighlighter.h"
+#include "lldb/Target/Language.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class ObjCPlusPlusLanguage : public Language {
+ ClangHighlighter m_highlighter;
+
+public:
+ ObjCPlusPlusLanguage() = default;
+
+ ~ObjCPlusPlusLanguage() override = default;
+
+ lldb::LanguageType GetLanguageType() const override {
+ return lldb::eLanguageTypeObjC_plus_plus;
+ }
+
+ bool IsSourceFile(llvm::StringRef file_path) const override;
+
+ const Highlighter *GetHighlighter() const override { return &m_highlighter; }
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::Language *CreateInstance(lldb::LanguageType language);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ // PluginInterface protocol
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CPlusPlusLanguage_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
new file mode 100644
index 000000000000..b392282c3eb1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
@@ -0,0 +1,353 @@
+//===-- CPPLanguageRuntime.cpp
+//
+// 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 <string.h>
+
+#include <memory>
+
+#include "CPPLanguageRuntime.h"
+
+#include "llvm/ADT/StringRef.h"
+
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Symbol/VariableList.h"
+
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/UniqueCStringMap.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/ThreadPlanRunToAddress.h"
+#include "lldb/Target/ThreadPlanStepInRange.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static ConstString g_this = ConstString("this");
+
+char CPPLanguageRuntime::ID = 0;
+
+// Destructor
+CPPLanguageRuntime::~CPPLanguageRuntime() {}
+
+CPPLanguageRuntime::CPPLanguageRuntime(Process *process)
+ : LanguageRuntime(process) {}
+
+bool CPPLanguageRuntime::IsWhitelistedRuntimeValue(ConstString name) {
+ return name == g_this;
+}
+
+bool CPPLanguageRuntime::GetObjectDescription(Stream &str,
+ ValueObject &object) {
+ // C++ has no generic way to do this.
+ return false;
+}
+
+bool CPPLanguageRuntime::GetObjectDescription(
+ Stream &str, Value &value, ExecutionContextScope *exe_scope) {
+ // C++ has no generic way to do this.
+ return false;
+}
+
+CPPLanguageRuntime::LibCppStdFunctionCallableInfo
+CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo(
+ lldb::ValueObjectSP &valobj_sp) {
+ LibCppStdFunctionCallableInfo optional_info;
+
+ if (!valobj_sp)
+ return optional_info;
+
+ // Member __f_ has type __base*, the contents of which will hold:
+ // 1) a vtable entry which may hold type information needed to discover the
+ // lambda being called
+ // 2) possibly hold a pointer to the callable object
+ // e.g.
+ //
+ // (lldb) frame var -R f_display
+ // (std::__1::function<void (int)>) f_display = {
+ // __buf_ = {
+ // …
+ // }
+ // __f_ = 0x00007ffeefbffa00
+ // }
+ // (lldb) memory read -fA 0x00007ffeefbffa00
+ // 0x7ffeefbffa00: ... `vtable for std::__1::__function::__func<void (*) ...
+ // 0x7ffeefbffa08: ... `print_num(int) at std_function_cppreference_exam ...
+ //
+ // We will be handling five cases below, std::function is wrapping:
+ //
+ // 1) a lambda we know at compile time. We will obtain the name of the lambda
+ // from the first template pameter from __func's vtable. We will look up
+ // the lambda's operator()() and obtain the line table entry.
+ // 2) a lambda we know at runtime. A pointer to the lambdas __invoke method
+ // will be stored after the vtable. We will obtain the lambdas name from
+ // this entry and lookup operator()() and obtain the line table entry.
+ // 3) a callable object via operator()(). We will obtain the name of the
+ // object from the first template parameter from __func's vtable. We will
+ // look up the objectc operator()() and obtain the line table entry.
+ // 4) a member function. A pointer to the function will stored after the
+ // we will obtain the name from this pointer.
+ // 5) a free function. A pointer to the function will stored after the vtable
+ // we will obtain the name from this pointer.
+ ValueObjectSP member__f_(
+ valobj_sp->GetChildMemberWithName(ConstString("__f_"), true));
+
+ if (member__f_) {
+ ValueObjectSP sub_member__f_(
+ member__f_->GetChildMemberWithName(ConstString("__f_"), true));
+
+ if (sub_member__f_)
+ member__f_ = sub_member__f_;
+ }
+
+ lldb::addr_t member__f_pointer_value = member__f_->GetValueAsUnsigned(0);
+
+ optional_info.member__f_pointer_value = member__f_pointer_value;
+
+ ExecutionContext exe_ctx(valobj_sp->GetExecutionContextRef());
+ Process *process = exe_ctx.GetProcessPtr();
+
+ if (process == nullptr)
+ return optional_info;
+
+ uint32_t address_size = process->GetAddressByteSize();
+ Status status;
+
+ // First item pointed to by __f_ should be the pointer to the vtable for
+ // a __base object.
+ lldb::addr_t vtable_address =
+ process->ReadPointerFromMemory(member__f_pointer_value, status);
+
+ if (status.Fail())
+ return optional_info;
+
+ lldb::addr_t address_after_vtable = member__f_pointer_value + address_size;
+ // As commened above we may not have a function pointer but if we do we will
+ // need it.
+ lldb::addr_t possible_function_address =
+ process->ReadPointerFromMemory(address_after_vtable, status);
+
+ if (status.Fail())
+ return optional_info;
+
+ Target &target = process->GetTarget();
+
+ if (target.GetSectionLoadList().IsEmpty())
+ return optional_info;
+
+ Address vtable_addr_resolved;
+ SymbolContext sc;
+ Symbol *symbol;
+
+ if (!target.GetSectionLoadList().ResolveLoadAddress(vtable_address,
+ vtable_addr_resolved))
+ return optional_info;
+
+ target.GetImages().ResolveSymbolContextForAddress(
+ vtable_addr_resolved, eSymbolContextEverything, sc);
+ symbol = sc.symbol;
+
+ if (symbol == nullptr)
+ return optional_info;
+
+ llvm::StringRef vtable_name(symbol->GetName().GetCString());
+ bool found_expected_start_string =
+ vtable_name.startswith("vtable for std::__1::__function::__func<");
+
+ if (!found_expected_start_string)
+ return optional_info;
+
+ // Given case 1 or 3 we have a vtable name, we are want to extract the first
+ // template parameter
+ //
+ // ... __func<main::$_0, std::__1::allocator<main::$_0> ...
+ // ^^^^^^^^^
+ //
+ // We do this by find the first < and , and extracting in between.
+ //
+ // This covers the case of the lambda known at compile time.
+ size_t first_open_angle_bracket = vtable_name.find('<') + 1;
+ size_t first_comma = vtable_name.find(',');
+
+ llvm::StringRef first_template_parameter =
+ vtable_name.slice(first_open_angle_bracket, first_comma);
+
+ Address function_address_resolved;
+
+ // Setup for cases 2, 4 and 5 we have a pointer to a function after the
+ // vtable. We will use a process of elimination to drop through each case
+ // and obtain the data we need.
+ if (target.GetSectionLoadList().ResolveLoadAddress(
+ possible_function_address, function_address_resolved)) {
+ target.GetImages().ResolveSymbolContextForAddress(
+ function_address_resolved, eSymbolContextEverything, sc);
+ symbol = sc.symbol;
+ }
+
+ auto get_name = [&first_template_parameter, &symbol]() {
+ // Given case 1:
+ //
+ // main::$_0
+ //
+ // we want to append ::operator()()
+ if (first_template_parameter.contains("$_"))
+ return llvm::Regex::escape(first_template_parameter.str()) +
+ R"(::operator\(\)\(.*\))";
+
+ if (symbol != nullptr &&
+ symbol->GetName().GetStringRef().contains("__invoke")) {
+
+ llvm::StringRef symbol_name = symbol->GetName().GetStringRef();
+ size_t pos2 = symbol_name.find_last_of(':');
+
+ // Given case 2:
+ //
+ // main::$_1::__invoke(...)
+ //
+ // We want to slice off __invoke(...) and append operator()()
+ std::string lambda_operator =
+ llvm::Regex::escape(symbol_name.slice(0, pos2 + 1).str()) +
+ R"(operator\(\)\(.*\))";
+
+ return lambda_operator;
+ }
+
+ // Case 3
+ return first_template_parameter.str() + R"(::operator\(\)\(.*\))";
+ ;
+ };
+
+ std::string func_to_match = get_name();
+
+ SymbolContextList scl;
+
+ target.GetImages().FindSymbolsMatchingRegExAndType(
+ RegularExpression{R"(^)" + func_to_match}, eSymbolTypeAny, scl, true);
+
+ // Case 1,2 or 3
+ if (scl.GetSize() >= 1) {
+ SymbolContext sc2 = scl[0];
+
+ AddressRange range;
+ sc2.GetAddressRange(eSymbolContextEverything, 0, false, range);
+
+ Address address = range.GetBaseAddress();
+
+ Address addr;
+ if (target.ResolveLoadAddress(address.GetCallableLoadAddress(&target),
+ addr)) {
+ LineEntry line_entry;
+ addr.CalculateSymbolContextLineEntry(line_entry);
+
+ if (first_template_parameter.contains("$_") ||
+ (symbol != nullptr &&
+ symbol->GetName().GetStringRef().contains("__invoke"))) {
+ // Case 1 and 2
+ optional_info.callable_case = LibCppStdFunctionCallableCase::Lambda;
+ } else {
+ // Case 3
+ optional_info.callable_case =
+ LibCppStdFunctionCallableCase::CallableObject;
+ }
+
+ optional_info.callable_symbol = *symbol;
+ optional_info.callable_line_entry = line_entry;
+ optional_info.callable_address = addr;
+ return optional_info;
+ }
+ }
+
+ // Case 4 or 5
+ if (symbol && !symbol->GetName().GetStringRef().startswith("vtable for")) {
+ optional_info.callable_case =
+ LibCppStdFunctionCallableCase::FreeOrMemberFunction;
+ optional_info.callable_address = function_address_resolved;
+ optional_info.callable_symbol = *symbol;
+
+ return optional_info;
+ }
+
+ return optional_info;
+}
+
+lldb::ThreadPlanSP
+CPPLanguageRuntime::GetStepThroughTrampolinePlan(Thread &thread,
+ bool stop_others) {
+ ThreadPlanSP ret_plan_sp;
+
+ lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC();
+
+ TargetSP target_sp(thread.CalculateTarget());
+
+ if (target_sp->GetSectionLoadList().IsEmpty())
+ return ret_plan_sp;
+
+ Address pc_addr_resolved;
+ SymbolContext sc;
+ Symbol *symbol;
+
+ if (!target_sp->GetSectionLoadList().ResolveLoadAddress(curr_pc,
+ pc_addr_resolved))
+ return ret_plan_sp;
+
+ target_sp->GetImages().ResolveSymbolContextForAddress(
+ pc_addr_resolved, eSymbolContextEverything, sc);
+ symbol = sc.symbol;
+
+ if (symbol == nullptr)
+ return ret_plan_sp;
+
+ llvm::StringRef function_name(symbol->GetName().GetCString());
+
+ // Handling the case where we are attempting to step into std::function.
+ // The behavior will be that we will attempt to obtain the wrapped
+ // callable via FindLibCppStdFunctionCallableInfo() and if we find it we
+ // will return a ThreadPlanRunToAddress to the callable. Therefore we will
+ // step into the wrapped callable.
+ //
+ bool found_expected_start_string =
+ function_name.startswith("std::__1::function<");
+
+ if (!found_expected_start_string)
+ return ret_plan_sp;
+
+ AddressRange range_of_curr_func;
+ sc.GetAddressRange(eSymbolContextEverything, 0, false, range_of_curr_func);
+
+ StackFrameSP frame = thread.GetStackFrameAtIndex(0);
+
+ if (frame) {
+ ValueObjectSP value_sp = frame->FindVariable(g_this);
+
+ CPPLanguageRuntime::LibCppStdFunctionCallableInfo callable_info =
+ FindLibCppStdFunctionCallableInfo(value_sp);
+
+ if (callable_info.callable_case != LibCppStdFunctionCallableCase::Invalid &&
+ value_sp->GetValueIsValid()) {
+ // We found the std::function wrapped callable and we have its address.
+ // We now create a ThreadPlan to run to the callable.
+ ret_plan_sp = std::make_shared<ThreadPlanRunToAddress>(
+ thread, callable_info.callable_address, stop_others);
+ return ret_plan_sp;
+ } else {
+ // We are in std::function but we could not obtain the callable.
+ // We create a ThreadPlan to keep stepping through using the address range
+ // of the current function.
+ ret_plan_sp = std::make_shared<ThreadPlanStepInRange>(
+ thread, range_of_curr_func, sc, eOnlyThisThread, eLazyBoolYes,
+ eLazyBoolYes);
+ return ret_plan_sp;
+ }
+ }
+
+ return ret_plan_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h
new file mode 100644
index 000000000000..28526361efc4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h
@@ -0,0 +1,90 @@
+//===-- CPPLanguageRuntime.h
+//
+// 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 liblldb_CPPLanguageRuntime_h_
+#define liblldb_CPPLanguageRuntime_h_
+
+#include <vector>
+#include "lldb/Core/PluginInterface.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class CPPLanguageRuntime : public LanguageRuntime {
+public:
+ enum class LibCppStdFunctionCallableCase {
+ Lambda = 0,
+ CallableObject,
+ FreeOrMemberFunction,
+ Invalid
+ };
+
+ struct LibCppStdFunctionCallableInfo {
+ Symbol callable_symbol;
+ Address callable_address;
+ LineEntry callable_line_entry;
+ lldb::addr_t member__f_pointer_value = 0u;
+ LibCppStdFunctionCallableCase callable_case =
+ LibCppStdFunctionCallableCase::Invalid;
+ };
+
+ LibCppStdFunctionCallableInfo
+ FindLibCppStdFunctionCallableInfo(lldb::ValueObjectSP &valobj_sp);
+
+ ~CPPLanguageRuntime() override;
+
+ static char ID;
+
+ bool isA(const void *ClassID) const override {
+ return ClassID == &ID || LanguageRuntime::isA(ClassID);
+ }
+
+ static bool classof(const LanguageRuntime *runtime) {
+ return runtime->isA(&ID);
+ }
+
+ lldb::LanguageType GetLanguageType() const override {
+ return lldb::eLanguageTypeC_plus_plus;
+ }
+
+ static CPPLanguageRuntime *Get(Process &process) {
+ return llvm::cast_or_null<CPPLanguageRuntime>(
+ process.GetLanguageRuntime(lldb::eLanguageTypeC_plus_plus));
+ }
+
+ bool GetObjectDescription(Stream &str, ValueObject &object) override;
+
+ bool GetObjectDescription(Stream &str, Value &value,
+ ExecutionContextScope *exe_scope) override;
+
+ /// Obtain a ThreadPlan to get us into C++ constructs such as std::function.
+ ///
+ /// \param[in] thread
+ /// Curent thrad of execution.
+ ///
+ /// \param[in] stop_others
+ /// True if other threads should pause during execution.
+ ///
+ /// \return
+ /// A ThreadPlan Shared pointer
+ lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
+ bool stop_others) override;
+
+ bool IsWhitelistedRuntimeValue(ConstString name) override;
+protected:
+ // Classes that inherit from CPPLanguageRuntime can see and modify these
+ CPPLanguageRuntime(Process *process);
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(CPPLanguageRuntime);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CPPLanguageRuntime_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
new file mode 100644
index 000000000000..41f38a4e3dcd
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
@@ -0,0 +1,617 @@
+//===-- ItaniumABILanguageRuntime.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 "ItaniumABILanguageRuntime.h"
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Mangled.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/FunctionCaller.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+
+static const char *vtable_demangled_prefix = "vtable for ";
+
+char ItaniumABILanguageRuntime::ID = 0;
+
+bool ItaniumABILanguageRuntime::CouldHaveDynamicValue(ValueObject &in_value) {
+ const bool check_cxx = true;
+ const bool check_objc = false;
+ return in_value.GetCompilerType().IsPossibleDynamicType(nullptr, check_cxx,
+ check_objc);
+}
+
+TypeAndOrName ItaniumABILanguageRuntime::GetTypeInfoFromVTableAddress(
+ ValueObject &in_value, lldb::addr_t original_ptr,
+ lldb::addr_t vtable_load_addr) {
+ if (m_process && vtable_load_addr != LLDB_INVALID_ADDRESS) {
+ // Find the symbol that contains the "vtable_load_addr" address
+ Address vtable_addr;
+ Target &target = m_process->GetTarget();
+ if (!target.GetSectionLoadList().IsEmpty()) {
+ if (target.GetSectionLoadList().ResolveLoadAddress(vtable_load_addr,
+ vtable_addr)) {
+ // See if we have cached info for this type already
+ TypeAndOrName type_info = GetDynamicTypeInfo(vtable_addr);
+ if (type_info)
+ return type_info;
+
+ SymbolContext sc;
+ target.GetImages().ResolveSymbolContextForAddress(
+ vtable_addr, eSymbolContextSymbol, sc);
+ Symbol *symbol = sc.symbol;
+ if (symbol != nullptr) {
+ const char *name =
+ symbol->GetMangled()
+ .GetDemangledName(lldb::eLanguageTypeC_plus_plus)
+ .AsCString();
+ if (name && strstr(name, vtable_demangled_prefix) == name) {
+ Log *log(
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf("0x%16.16" PRIx64
+ ": static-type = '%s' has vtable symbol '%s'\n",
+ original_ptr, in_value.GetTypeName().GetCString(),
+ name);
+ // We are a C++ class, that's good. Get the class name and look it
+ // up:
+ const char *class_name = name + strlen(vtable_demangled_prefix);
+ // We know the class name is absolute, so tell FindTypes that by
+ // prefixing it with the root namespace:
+ std::string lookup_name("::");
+ lookup_name.append(class_name);
+
+ type_info.SetName(class_name);
+ const bool exact_match = true;
+ TypeList class_types;
+
+ uint32_t num_matches = 0;
+ // First look in the module that the vtable symbol came from and
+ // look for a single exact match.
+ llvm::DenseSet<SymbolFile *> searched_symbol_files;
+ if (sc.module_sp) {
+ num_matches = sc.module_sp->FindTypes(
+ ConstString(lookup_name), exact_match, 1,
+ searched_symbol_files, class_types);
+ }
+
+ // If we didn't find a symbol, then move on to the entire module
+ // list in the target and get as many unique matches as possible
+ if (num_matches == 0) {
+ num_matches = target.GetImages().FindTypes(
+ nullptr, ConstString(lookup_name), exact_match, UINT32_MAX,
+ searched_symbol_files, class_types);
+ }
+
+ lldb::TypeSP type_sp;
+ if (num_matches == 0) {
+ if (log)
+ log->Printf("0x%16.16" PRIx64 ": is not dynamic\n",
+ original_ptr);
+ return TypeAndOrName();
+ }
+ if (num_matches == 1) {
+ type_sp = class_types.GetTypeAtIndex(0);
+ if (type_sp) {
+ if (ClangASTContext::IsCXXClassType(
+ type_sp->GetForwardCompilerType())) {
+ if (log)
+ log->Printf(
+ "0x%16.16" PRIx64
+ ": static-type = '%s' has dynamic type: uid={0x%" PRIx64
+ "}, type-name='%s'\n",
+ original_ptr, in_value.GetTypeName().AsCString(),
+ type_sp->GetID(), type_sp->GetName().GetCString());
+ type_info.SetTypeSP(type_sp);
+ }
+ }
+ } else if (num_matches > 1) {
+ size_t i;
+ if (log) {
+ for (i = 0; i < num_matches; i++) {
+ type_sp = class_types.GetTypeAtIndex(i);
+ if (type_sp) {
+ if (log)
+ log->Printf(
+ "0x%16.16" PRIx64
+ ": static-type = '%s' has multiple matching dynamic "
+ "types: uid={0x%" PRIx64 "}, type-name='%s'\n",
+ original_ptr, in_value.GetTypeName().AsCString(),
+ type_sp->GetID(), type_sp->GetName().GetCString());
+ }
+ }
+ }
+
+ for (i = 0; i < num_matches; i++) {
+ type_sp = class_types.GetTypeAtIndex(i);
+ if (type_sp) {
+ if (ClangASTContext::IsCXXClassType(
+ type_sp->GetForwardCompilerType())) {
+ if (log)
+ log->Printf(
+ "0x%16.16" PRIx64 ": static-type = '%s' has multiple "
+ "matching dynamic types, picking "
+ "this one: uid={0x%" PRIx64
+ "}, type-name='%s'\n",
+ original_ptr, in_value.GetTypeName().AsCString(),
+ type_sp->GetID(), type_sp->GetName().GetCString());
+ type_info.SetTypeSP(type_sp);
+ }
+ }
+ }
+
+ if (log && i == num_matches) {
+ log->Printf(
+ "0x%16.16" PRIx64
+ ": static-type = '%s' has multiple matching dynamic "
+ "types, didn't find a C++ match\n",
+ original_ptr, in_value.GetTypeName().AsCString());
+ }
+ }
+ if (type_info)
+ SetDynamicTypeInfo(vtable_addr, type_info);
+ return type_info;
+ }
+ }
+ }
+ }
+ }
+ return TypeAndOrName();
+}
+
+bool ItaniumABILanguageRuntime::GetDynamicTypeAndAddress(
+ ValueObject &in_value, lldb::DynamicValueType use_dynamic,
+ TypeAndOrName &class_type_or_name, Address &dynamic_address,
+ Value::ValueType &value_type) {
+ // For Itanium, if the type has a vtable pointer in the object, it will be at
+ // offset 0 in the object. That will point to the "address point" within the
+ // vtable (not the beginning of the vtable.) We can then look up the symbol
+ // containing this "address point" and that symbol's name demangled will
+ // contain the full class name. The second pointer above the "address point"
+ // is the "offset_to_top". We'll use that to get the start of the value
+ // object which holds the dynamic type.
+ //
+
+ class_type_or_name.Clear();
+ value_type = Value::ValueType::eValueTypeScalar;
+
+ // Only a pointer or reference type can have a different dynamic and static
+ // type:
+ if (!CouldHaveDynamicValue(in_value))
+ return false;
+
+ // First job, pull out the address at 0 offset from the object.
+ AddressType address_type;
+ lldb::addr_t original_ptr = in_value.GetPointerValue(&address_type);
+ if (original_ptr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ ExecutionContext exe_ctx(in_value.GetExecutionContextRef());
+
+ Process *process = exe_ctx.GetProcessPtr();
+
+ if (process == nullptr)
+ return false;
+
+ Status error;
+ const lldb::addr_t vtable_address_point =
+ process->ReadPointerFromMemory(original_ptr, error);
+
+ if (!error.Success() || vtable_address_point == LLDB_INVALID_ADDRESS)
+ return false;
+
+ class_type_or_name = GetTypeInfoFromVTableAddress(in_value, original_ptr,
+ vtable_address_point);
+
+ if (!class_type_or_name)
+ return false;
+
+ CompilerType type = class_type_or_name.GetCompilerType();
+ // There can only be one type with a given name, so we've just found
+ // duplicate definitions, and this one will do as well as any other. We
+ // don't consider something to have a dynamic type if it is the same as
+ // the static type. So compare against the value we were handed.
+ if (!type)
+ return true;
+
+ if (ClangASTContext::AreTypesSame(in_value.GetCompilerType(), type)) {
+ // The dynamic type we found was the same type, so we don't have a
+ // dynamic type here...
+ return false;
+ }
+
+ // The offset_to_top is two pointers above the vtable pointer.
+ const uint32_t addr_byte_size = process->GetAddressByteSize();
+ const lldb::addr_t offset_to_top_location =
+ vtable_address_point - 2 * addr_byte_size;
+ // Watch for underflow, offset_to_top_location should be less than
+ // vtable_address_point
+ if (offset_to_top_location >= vtable_address_point)
+ return false;
+ const int64_t offset_to_top = process->ReadSignedIntegerFromMemory(
+ offset_to_top_location, addr_byte_size, INT64_MIN, error);
+
+ if (offset_to_top == INT64_MIN)
+ return false;
+ // So the dynamic type is a value that starts at offset_to_top above
+ // the original address.
+ lldb::addr_t dynamic_addr = original_ptr + offset_to_top;
+ if (!process->GetTarget().GetSectionLoadList().ResolveLoadAddress(
+ dynamic_addr, dynamic_address)) {
+ dynamic_address.SetRawAddress(dynamic_addr);
+ }
+ return true;
+}
+
+TypeAndOrName ItaniumABILanguageRuntime::FixUpDynamicType(
+ const TypeAndOrName &type_and_or_name, ValueObject &static_value) {
+ CompilerType static_type(static_value.GetCompilerType());
+ Flags static_type_flags(static_type.GetTypeInfo());
+
+ TypeAndOrName ret(type_and_or_name);
+ if (type_and_or_name.HasType()) {
+ // The type will always be the type of the dynamic object. If our parent's
+ // type was a pointer, then our type should be a pointer to the type of the
+ // dynamic object. If a reference, then the original type should be
+ // okay...
+ CompilerType orig_type = type_and_or_name.GetCompilerType();
+ CompilerType corrected_type = orig_type;
+ if (static_type_flags.AllSet(eTypeIsPointer))
+ corrected_type = orig_type.GetPointerType();
+ else if (static_type_flags.AllSet(eTypeIsReference))
+ corrected_type = orig_type.GetLValueReferenceType();
+ ret.SetCompilerType(corrected_type);
+ } else {
+ // If we are here we need to adjust our dynamic type name to include the
+ // correct & or * symbol
+ std::string corrected_name(type_and_or_name.GetName().GetCString());
+ if (static_type_flags.AllSet(eTypeIsPointer))
+ corrected_name.append(" *");
+ else if (static_type_flags.AllSet(eTypeIsReference))
+ corrected_name.append(" &");
+ // the parent type should be a correctly pointer'ed or referenc'ed type
+ ret.SetCompilerType(static_type);
+ ret.SetName(corrected_name.c_str());
+ }
+ return ret;
+}
+
+// Static Functions
+LanguageRuntime *
+ItaniumABILanguageRuntime::CreateInstance(Process *process,
+ lldb::LanguageType language) {
+ // FIXME: We have to check the process and make sure we actually know that
+ // this process supports
+ // the Itanium ABI.
+ if (language == eLanguageTypeC_plus_plus ||
+ language == eLanguageTypeC_plus_plus_03 ||
+ language == eLanguageTypeC_plus_plus_11 ||
+ language == eLanguageTypeC_plus_plus_14)
+ return new ItaniumABILanguageRuntime(process);
+ else
+ return nullptr;
+}
+
+class CommandObjectMultiwordItaniumABI_Demangle : public CommandObjectParsed {
+public:
+ CommandObjectMultiwordItaniumABI_Demangle(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "demangle",
+ "Demangle a C++ mangled name.",
+ "language cplusplus demangle") {
+ CommandArgumentEntry arg;
+ CommandArgumentData index_arg;
+
+ // Define the first (and only) variant of this arg.
+ index_arg.arg_type = eArgTypeSymbol;
+ index_arg.arg_repetition = eArgRepeatPlus;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(index_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectMultiwordItaniumABI_Demangle() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ bool demangled_any = false;
+ bool error_any = false;
+ for (auto &entry : command.entries()) {
+ if (entry.ref.empty())
+ continue;
+
+ // the actual Mangled class should be strict about this, but on the
+ // command line if you're copying mangled names out of 'nm' on Darwin,
+ // they will come out with an extra underscore - be willing to strip this
+ // on behalf of the user. This is the moral equivalent of the -_/-n
+ // options to c++filt
+ auto name = entry.ref;
+ if (name.startswith("__Z"))
+ name = name.drop_front();
+
+ Mangled mangled(name, true);
+ if (mangled.GuessLanguage() == lldb::eLanguageTypeC_plus_plus) {
+ ConstString demangled(
+ mangled.GetDisplayDemangledName(lldb::eLanguageTypeC_plus_plus));
+ demangled_any = true;
+ result.AppendMessageWithFormat("%s ---> %s\n", entry.ref.str().c_str(),
+ demangled.GetCString());
+ } else {
+ error_any = true;
+ result.AppendErrorWithFormat("%s is not a valid C++ mangled name\n",
+ entry.ref.str().c_str());
+ }
+ }
+
+ result.SetStatus(
+ error_any ? lldb::eReturnStatusFailed
+ : (demangled_any ? lldb::eReturnStatusSuccessFinishResult
+ : lldb::eReturnStatusSuccessFinishNoResult));
+ return result.Succeeded();
+ }
+};
+
+class CommandObjectMultiwordItaniumABI : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordItaniumABI(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "cplusplus",
+ "Commands for operating on the C++ language runtime.",
+ "cplusplus <subcommand> [<subcommand-options>]") {
+ LoadSubCommand(
+ "demangle",
+ CommandObjectSP(
+ new CommandObjectMultiwordItaniumABI_Demangle(interpreter)));
+ }
+
+ ~CommandObjectMultiwordItaniumABI() override = default;
+};
+
+void ItaniumABILanguageRuntime::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "Itanium ABI for the C++ language", CreateInstance,
+ [](CommandInterpreter &interpreter) -> lldb::CommandObjectSP {
+ return CommandObjectSP(
+ new CommandObjectMultiwordItaniumABI(interpreter));
+ });
+}
+
+void ItaniumABILanguageRuntime::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ItaniumABILanguageRuntime::GetPluginNameStatic() {
+ static ConstString g_name("itanium");
+ return g_name;
+}
+
+// PluginInterface protocol
+lldb_private::ConstString ItaniumABILanguageRuntime::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ItaniumABILanguageRuntime::GetPluginVersion() { return 1; }
+
+BreakpointResolverSP ItaniumABILanguageRuntime::CreateExceptionResolver(
+ Breakpoint *bkpt, bool catch_bp, bool throw_bp) {
+ return CreateExceptionResolver(bkpt, catch_bp, throw_bp, false);
+}
+
+BreakpointResolverSP ItaniumABILanguageRuntime::CreateExceptionResolver(
+ Breakpoint *bkpt, bool catch_bp, bool throw_bp, bool for_expressions) {
+ // One complication here is that most users DON'T want to stop at
+ // __cxa_allocate_expression, but until we can do anything better with
+ // predicting unwinding the expression parser does. So we have two forms of
+ // the exception breakpoints, one for expressions that leaves out
+ // __cxa_allocate_exception, and one that includes it. The
+ // SetExceptionBreakpoints does the latter, the CreateExceptionBreakpoint in
+ // the runtime the former.
+ static const char *g_catch_name = "__cxa_begin_catch";
+ static const char *g_throw_name1 = "__cxa_throw";
+ static const char *g_throw_name2 = "__cxa_rethrow";
+ static const char *g_exception_throw_name = "__cxa_allocate_exception";
+ std::vector<const char *> exception_names;
+ exception_names.reserve(4);
+ if (catch_bp)
+ exception_names.push_back(g_catch_name);
+
+ if (throw_bp) {
+ exception_names.push_back(g_throw_name1);
+ exception_names.push_back(g_throw_name2);
+ }
+
+ if (for_expressions)
+ exception_names.push_back(g_exception_throw_name);
+
+ BreakpointResolverSP resolver_sp(new BreakpointResolverName(
+ bkpt, exception_names.data(), exception_names.size(),
+ eFunctionNameTypeBase, eLanguageTypeUnknown, 0, eLazyBoolNo));
+
+ return resolver_sp;
+}
+
+lldb::SearchFilterSP ItaniumABILanguageRuntime::CreateExceptionSearchFilter() {
+ Target &target = m_process->GetTarget();
+
+ FileSpecList filter_modules;
+ if (target.GetArchitecture().GetTriple().getVendor() == llvm::Triple::Apple) {
+ // Limit the number of modules that are searched for these breakpoints for
+ // Apple binaries.
+ filter_modules.Append(FileSpec("libc++abi.dylib"));
+ filter_modules.Append(FileSpec("libSystem.B.dylib"));
+ }
+ return target.GetSearchFilterForModuleList(&filter_modules);
+}
+
+lldb::BreakpointSP ItaniumABILanguageRuntime::CreateExceptionBreakpoint(
+ bool catch_bp, bool throw_bp, bool for_expressions, bool is_internal) {
+ Target &target = m_process->GetTarget();
+ FileSpecList filter_modules;
+ BreakpointResolverSP exception_resolver_sp =
+ CreateExceptionResolver(nullptr, catch_bp, throw_bp, for_expressions);
+ SearchFilterSP filter_sp(CreateExceptionSearchFilter());
+ const bool hardware = false;
+ const bool resolve_indirect_functions = false;
+ return target.CreateBreakpoint(filter_sp, exception_resolver_sp, is_internal,
+ hardware, resolve_indirect_functions);
+}
+
+void ItaniumABILanguageRuntime::SetExceptionBreakpoints() {
+ if (!m_process)
+ return;
+
+ const bool catch_bp = false;
+ const bool throw_bp = true;
+ const bool is_internal = true;
+ const bool for_expressions = true;
+
+ // For the exception breakpoints set by the Expression parser, we'll be a
+ // little more aggressive and stop at exception allocation as well.
+
+ if (m_cxx_exception_bp_sp) {
+ m_cxx_exception_bp_sp->SetEnabled(true);
+ } else {
+ m_cxx_exception_bp_sp = CreateExceptionBreakpoint(
+ catch_bp, throw_bp, for_expressions, is_internal);
+ if (m_cxx_exception_bp_sp)
+ m_cxx_exception_bp_sp->SetBreakpointKind("c++ exception");
+ }
+}
+
+void ItaniumABILanguageRuntime::ClearExceptionBreakpoints() {
+ if (!m_process)
+ return;
+
+ if (m_cxx_exception_bp_sp) {
+ m_cxx_exception_bp_sp->SetEnabled(false);
+ }
+}
+
+bool ItaniumABILanguageRuntime::ExceptionBreakpointsAreSet() {
+ return m_cxx_exception_bp_sp && m_cxx_exception_bp_sp->IsEnabled();
+}
+
+bool ItaniumABILanguageRuntime::ExceptionBreakpointsExplainStop(
+ lldb::StopInfoSP stop_reason) {
+ if (!m_process)
+ return false;
+
+ if (!stop_reason || stop_reason->GetStopReason() != eStopReasonBreakpoint)
+ return false;
+
+ uint64_t break_site_id = stop_reason->GetValue();
+ return m_process->GetBreakpointSiteList().BreakpointSiteContainsBreakpoint(
+ break_site_id, m_cxx_exception_bp_sp->GetID());
+}
+
+ValueObjectSP ItaniumABILanguageRuntime::GetExceptionObjectForThread(
+ ThreadSP thread_sp) {
+ if (!thread_sp->SafeToCallFunctions())
+ return {};
+
+ ClangASTContext *clang_ast_context =
+ m_process->GetTarget().GetScratchClangASTContext();
+ CompilerType voidstar =
+ clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
+
+ DiagnosticManager diagnostics;
+ ExecutionContext exe_ctx;
+ EvaluateExpressionOptions options;
+
+ options.SetUnwindOnError(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetStopOthers(true);
+ options.SetTimeout(m_process->GetUtilityExpressionTimeout());
+ options.SetTryAllThreads(false);
+ thread_sp->CalculateExecutionContext(exe_ctx);
+
+ const ModuleList &modules = m_process->GetTarget().GetImages();
+ SymbolContextList contexts;
+ SymbolContext context;
+
+ modules.FindSymbolsWithNameAndType(
+ ConstString("__cxa_current_exception_type"), eSymbolTypeCode, contexts);
+ contexts.GetContextAtIndex(0, context);
+ Address addr = context.symbol->GetAddress();
+
+ Status error;
+ FunctionCaller *function_caller =
+ m_process->GetTarget().GetFunctionCallerForLanguage(
+ eLanguageTypeC, voidstar, addr, ValueList(), "caller", error);
+
+ ExpressionResults func_call_ret;
+ Value results;
+ func_call_ret = function_caller->ExecuteFunction(exe_ctx, nullptr, options,
+ diagnostics, results);
+ if (func_call_ret != eExpressionCompleted || !error.Success()) {
+ return ValueObjectSP();
+ }
+
+ size_t ptr_size = m_process->GetAddressByteSize();
+ addr_t result_ptr = results.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+ addr_t exception_addr =
+ m_process->ReadPointerFromMemory(result_ptr - ptr_size, error);
+
+ if (!error.Success()) {
+ return ValueObjectSP();
+ }
+
+ lldb_private::formatters::InferiorSizedWord exception_isw(exception_addr,
+ *m_process);
+ ValueObjectSP exception = ValueObject::CreateValueObjectFromData(
+ "exception", exception_isw.GetAsData(m_process->GetByteOrder()), exe_ctx,
+ voidstar);
+ exception = exception->GetDynamicValue(eDynamicDontRunTarget);
+
+ return exception;
+}
+
+TypeAndOrName ItaniumABILanguageRuntime::GetDynamicTypeInfo(
+ const lldb_private::Address &vtable_addr) {
+ std::lock_guard<std::mutex> locker(m_dynamic_type_map_mutex);
+ DynamicTypeCache::const_iterator pos = m_dynamic_type_map.find(vtable_addr);
+ if (pos == m_dynamic_type_map.end())
+ return TypeAndOrName();
+ else
+ return pos->second;
+}
+
+void ItaniumABILanguageRuntime::SetDynamicTypeInfo(
+ const lldb_private::Address &vtable_addr, const TypeAndOrName &type_info) {
+ std::lock_guard<std::mutex> locker(m_dynamic_type_map_mutex);
+ m_dynamic_type_map[vtable_addr] = type_info;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h
new file mode 100644
index 000000000000..97cc81b8681f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h
@@ -0,0 +1,117 @@
+//===-- ItaniumABILanguageRuntime.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 liblldb_ItaniumABILanguageRuntime_h_
+#define liblldb_ItaniumABILanguageRuntime_h_
+
+#include <map>
+#include <mutex>
+#include <vector>
+
+#include "lldb/Breakpoint/BreakpointResolver.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/lldb-private.h"
+
+#include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h"
+
+namespace lldb_private {
+
+class ItaniumABILanguageRuntime : public lldb_private::CPPLanguageRuntime {
+public:
+ ~ItaniumABILanguageRuntime() override = default;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::LanguageRuntime *
+ CreateInstance(Process *process, lldb::LanguageType language);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static char ID;
+
+ bool isA(const void *ClassID) const override {
+ return ClassID == &ID || CPPLanguageRuntime::isA(ClassID);
+ }
+
+ static bool classof(const LanguageRuntime *runtime) {
+ return runtime->isA(&ID);
+ }
+
+ bool GetDynamicTypeAndAddress(ValueObject &in_value,
+ lldb::DynamicValueType use_dynamic,
+ TypeAndOrName &class_type_or_name,
+ Address &address,
+ Value::ValueType &value_type) override;
+
+ TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name,
+ ValueObject &static_value) override;
+
+ bool CouldHaveDynamicValue(ValueObject &in_value) override;
+
+ void SetExceptionBreakpoints() override;
+
+ void ClearExceptionBreakpoints() override;
+
+ bool ExceptionBreakpointsAreSet() override;
+
+ bool ExceptionBreakpointsExplainStop(lldb::StopInfoSP stop_reason) override;
+
+ lldb::BreakpointResolverSP CreateExceptionResolver(Breakpoint *bkpt,
+ bool catch_bp,
+ bool throw_bp) override;
+
+ lldb::SearchFilterSP CreateExceptionSearchFilter() override;
+
+ lldb::ValueObjectSP GetExceptionObjectForThread(
+ lldb::ThreadSP thread_sp) override;
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ lldb::BreakpointResolverSP CreateExceptionResolver(Breakpoint *bkpt,
+ bool catch_bp,
+ bool throw_bp,
+ bool for_expressions);
+
+ lldb::BreakpointSP CreateExceptionBreakpoint(bool catch_bp, bool throw_bp,
+ bool for_expressions,
+ bool is_internal);
+
+private:
+ typedef std::map<lldb_private::Address, TypeAndOrName> DynamicTypeCache;
+
+ ItaniumABILanguageRuntime(Process *process)
+ : // Call CreateInstance instead.
+ lldb_private::CPPLanguageRuntime(process), m_cxx_exception_bp_sp(),
+ m_dynamic_type_map(), m_dynamic_type_map_mutex() {}
+
+ lldb::BreakpointSP m_cxx_exception_bp_sp;
+ DynamicTypeCache m_dynamic_type_map;
+ std::mutex m_dynamic_type_map_mutex;
+
+ TypeAndOrName GetTypeInfoFromVTableAddress(ValueObject &in_value,
+ lldb::addr_t original_ptr,
+ lldb::addr_t vtable_addr);
+
+ TypeAndOrName GetDynamicTypeInfo(const lldb_private::Address &vtable_addr);
+
+ void SetDynamicTypeInfo(const lldb_private::Address &vtable_addr,
+ const TypeAndOrName &type_info);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ItaniumABILanguageRuntime_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp
new file mode 100644
index 000000000000..93aa07f89165
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp
@@ -0,0 +1,541 @@
+//===-- AppleObjCClassDescriptorV2.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 "AppleObjCClassDescriptorV2.h"
+
+#include "lldb/Expression/FunctionCaller.h"
+#include "lldb/Utility/Log.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+bool ClassDescriptorV2::Read_objc_class(
+ Process *process, std::unique_ptr<objc_class_t> &objc_class) const {
+ objc_class.reset(new objc_class_t);
+
+ bool ret = objc_class->Read(process, m_objc_class_ptr);
+
+ if (!ret)
+ objc_class.reset();
+
+ return ret;
+}
+
+static lldb::addr_t GetClassDataMask(Process *process) {
+ switch (process->GetAddressByteSize()) {
+ case 4:
+ return 0xfffffffcUL;
+ case 8:
+ return 0x00007ffffffffff8UL;
+ default:
+ break;
+ }
+
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool ClassDescriptorV2::objc_class_t::Read(Process *process,
+ lldb::addr_t addr) {
+ size_t ptr_size = process->GetAddressByteSize();
+
+ size_t objc_class_size = ptr_size // uintptr_t isa;
+ + ptr_size // Class superclass;
+ + ptr_size // void *cache;
+ + ptr_size // IMP *vtable;
+ + ptr_size; // uintptr_t data_NEVER_USE;
+
+ DataBufferHeap objc_class_buf(objc_class_size, '\0');
+ Status error;
+
+ process->ReadMemory(addr, objc_class_buf.GetBytes(), objc_class_size, error);
+ if (error.Fail()) {
+ return false;
+ }
+
+ DataExtractor extractor(objc_class_buf.GetBytes(), objc_class_size,
+ process->GetByteOrder(),
+ process->GetAddressByteSize());
+
+ lldb::offset_t cursor = 0;
+
+ m_isa = extractor.GetAddress_unchecked(&cursor); // uintptr_t isa;
+ m_superclass = extractor.GetAddress_unchecked(&cursor); // Class superclass;
+ m_cache_ptr = extractor.GetAddress_unchecked(&cursor); // void *cache;
+ m_vtable_ptr = extractor.GetAddress_unchecked(&cursor); // IMP *vtable;
+ lldb::addr_t data_NEVER_USE =
+ extractor.GetAddress_unchecked(&cursor); // uintptr_t data_NEVER_USE;
+
+ m_flags = (uint8_t)(data_NEVER_USE & (lldb::addr_t)3);
+ m_data_ptr = data_NEVER_USE & GetClassDataMask(process);
+
+ return true;
+}
+
+bool ClassDescriptorV2::class_rw_t::Read(Process *process, lldb::addr_t addr) {
+ size_t ptr_size = process->GetAddressByteSize();
+
+ size_t size = sizeof(uint32_t) // uint32_t flags;
+ + sizeof(uint32_t) // uint32_t version;
+ + ptr_size // const class_ro_t *ro;
+ + ptr_size // union { method_list_t **method_lists;
+ // method_list_t *method_list; };
+ + ptr_size // struct chained_property_list *properties;
+ + ptr_size // const protocol_list_t **protocols;
+ + ptr_size // Class firstSubclass;
+ + ptr_size; // Class nextSiblingClass;
+
+ DataBufferHeap buffer(size, '\0');
+ Status error;
+
+ process->ReadMemory(addr, buffer.GetBytes(), size, error);
+ if (error.Fail()) {
+ return false;
+ }
+
+ DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
+ process->GetAddressByteSize());
+
+ lldb::offset_t cursor = 0;
+
+ m_flags = extractor.GetU32_unchecked(&cursor);
+ m_version = extractor.GetU32_unchecked(&cursor);
+ m_ro_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_method_list_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_properties_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_firstSubclass = extractor.GetAddress_unchecked(&cursor);
+ m_nextSiblingClass = extractor.GetAddress_unchecked(&cursor);
+
+ return true;
+}
+
+bool ClassDescriptorV2::class_ro_t::Read(Process *process, lldb::addr_t addr) {
+ size_t ptr_size = process->GetAddressByteSize();
+
+ size_t size = sizeof(uint32_t) // uint32_t flags;
+ + sizeof(uint32_t) // uint32_t instanceStart;
+ + sizeof(uint32_t) // uint32_t instanceSize;
+ + (ptr_size == 8 ? sizeof(uint32_t)
+ : 0) // uint32_t reserved; // __LP64__ only
+ + ptr_size // const uint8_t *ivarLayout;
+ + ptr_size // const char *name;
+ + ptr_size // const method_list_t *baseMethods;
+ + ptr_size // const protocol_list_t *baseProtocols;
+ + ptr_size // const ivar_list_t *ivars;
+ + ptr_size // const uint8_t *weakIvarLayout;
+ + ptr_size; // const property_list_t *baseProperties;
+
+ DataBufferHeap buffer(size, '\0');
+ Status error;
+
+ process->ReadMemory(addr, buffer.GetBytes(), size, error);
+ if (error.Fail()) {
+ return false;
+ }
+
+ DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
+ process->GetAddressByteSize());
+
+ lldb::offset_t cursor = 0;
+
+ m_flags = extractor.GetU32_unchecked(&cursor);
+ m_instanceStart = extractor.GetU32_unchecked(&cursor);
+ m_instanceSize = extractor.GetU32_unchecked(&cursor);
+ if (ptr_size == 8)
+ m_reserved = extractor.GetU32_unchecked(&cursor);
+ else
+ m_reserved = 0;
+ m_ivarLayout_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_name_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_baseMethods_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_baseProtocols_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_ivars_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_weakIvarLayout_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_baseProperties_ptr = extractor.GetAddress_unchecked(&cursor);
+
+ DataBufferHeap name_buf(1024, '\0');
+
+ process->ReadCStringFromMemory(m_name_ptr, (char *)name_buf.GetBytes(),
+ name_buf.GetByteSize(), error);
+
+ if (error.Fail()) {
+ return false;
+ }
+
+ m_name.assign((char *)name_buf.GetBytes());
+
+ return true;
+}
+
+bool ClassDescriptorV2::Read_class_row(
+ Process *process, const objc_class_t &objc_class,
+ std::unique_ptr<class_ro_t> &class_ro,
+ std::unique_ptr<class_rw_t> &class_rw) const {
+ class_ro.reset();
+ class_rw.reset();
+
+ Status error;
+ uint32_t class_row_t_flags = process->ReadUnsignedIntegerFromMemory(
+ objc_class.m_data_ptr, sizeof(uint32_t), 0, error);
+ if (!error.Success())
+ return false;
+
+ if (class_row_t_flags & RW_REALIZED) {
+ class_rw.reset(new class_rw_t);
+
+ if (!class_rw->Read(process, objc_class.m_data_ptr)) {
+ class_rw.reset();
+ return false;
+ }
+
+ class_ro.reset(new class_ro_t);
+
+ if (!class_ro->Read(process, class_rw->m_ro_ptr)) {
+ class_rw.reset();
+ class_ro.reset();
+ return false;
+ }
+ } else {
+ class_ro.reset(new class_ro_t);
+
+ if (!class_ro->Read(process, objc_class.m_data_ptr)) {
+ class_ro.reset();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ClassDescriptorV2::method_list_t::Read(Process *process,
+ lldb::addr_t addr) {
+ size_t size = sizeof(uint32_t) // uint32_t entsize_NEVER_USE;
+ + sizeof(uint32_t); // uint32_t count;
+
+ DataBufferHeap buffer(size, '\0');
+ Status error;
+
+ process->ReadMemory(addr, buffer.GetBytes(), size, error);
+ if (error.Fail()) {
+ return false;
+ }
+
+ DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
+ process->GetAddressByteSize());
+
+ lldb::offset_t cursor = 0;
+
+ m_entsize = extractor.GetU32_unchecked(&cursor) & ~(uint32_t)3;
+ m_count = extractor.GetU32_unchecked(&cursor);
+ m_first_ptr = addr + cursor;
+
+ return true;
+}
+
+bool ClassDescriptorV2::method_t::Read(Process *process, lldb::addr_t addr) {
+ size_t size = GetSize(process);
+
+ DataBufferHeap buffer(size, '\0');
+ Status error;
+
+ process->ReadMemory(addr, buffer.GetBytes(), size, error);
+ if (error.Fail()) {
+ return false;
+ }
+
+ DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
+ process->GetAddressByteSize());
+
+ lldb::offset_t cursor = 0;
+
+ m_name_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_types_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_imp_ptr = extractor.GetAddress_unchecked(&cursor);
+
+ process->ReadCStringFromMemory(m_name_ptr, m_name, error);
+ if (error.Fail()) {
+ return false;
+ }
+
+ process->ReadCStringFromMemory(m_types_ptr, m_types, error);
+ return !error.Fail();
+}
+
+bool ClassDescriptorV2::ivar_list_t::Read(Process *process, lldb::addr_t addr) {
+ size_t size = sizeof(uint32_t) // uint32_t entsize;
+ + sizeof(uint32_t); // uint32_t count;
+
+ DataBufferHeap buffer(size, '\0');
+ Status error;
+
+ process->ReadMemory(addr, buffer.GetBytes(), size, error);
+ if (error.Fail()) {
+ return false;
+ }
+
+ DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
+ process->GetAddressByteSize());
+
+ lldb::offset_t cursor = 0;
+
+ m_entsize = extractor.GetU32_unchecked(&cursor);
+ m_count = extractor.GetU32_unchecked(&cursor);
+ m_first_ptr = addr + cursor;
+
+ return true;
+}
+
+bool ClassDescriptorV2::ivar_t::Read(Process *process, lldb::addr_t addr) {
+ size_t size = GetSize(process);
+
+ DataBufferHeap buffer(size, '\0');
+ Status error;
+
+ process->ReadMemory(addr, buffer.GetBytes(), size, error);
+ if (error.Fail()) {
+ return false;
+ }
+
+ DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
+ process->GetAddressByteSize());
+
+ lldb::offset_t cursor = 0;
+
+ m_offset_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_name_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_type_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_alignment = extractor.GetU32_unchecked(&cursor);
+ m_size = extractor.GetU32_unchecked(&cursor);
+
+ process->ReadCStringFromMemory(m_name_ptr, m_name, error);
+ if (error.Fail()) {
+ return false;
+ }
+
+ process->ReadCStringFromMemory(m_type_ptr, m_type, error);
+ return !error.Fail();
+}
+
+bool ClassDescriptorV2::Describe(
+ std::function<void(ObjCLanguageRuntime::ObjCISA)> const &superclass_func,
+ std::function<bool(const char *, const char *)> const &instance_method_func,
+ std::function<bool(const char *, const char *)> const &class_method_func,
+ std::function<bool(const char *, const char *, lldb::addr_t,
+ uint64_t)> const &ivar_func) const {
+ lldb_private::Process *process = m_runtime.GetProcess();
+
+ std::unique_ptr<objc_class_t> objc_class;
+ std::unique_ptr<class_ro_t> class_ro;
+ std::unique_ptr<class_rw_t> class_rw;
+
+ if (!Read_objc_class(process, objc_class))
+ return false;
+ if (!Read_class_row(process, *objc_class, class_ro, class_rw))
+ return false;
+
+ static ConstString NSObject_name("NSObject");
+
+ if (m_name != NSObject_name && superclass_func)
+ superclass_func(objc_class->m_superclass);
+
+ if (instance_method_func) {
+ std::unique_ptr<method_list_t> base_method_list;
+
+ base_method_list.reset(new method_list_t);
+ if (!base_method_list->Read(process, class_ro->m_baseMethods_ptr))
+ return false;
+
+ if (base_method_list->m_entsize != method_t::GetSize(process))
+ return false;
+
+ std::unique_ptr<method_t> method;
+ method.reset(new method_t);
+
+ for (uint32_t i = 0, e = base_method_list->m_count; i < e; ++i) {
+ method->Read(process, base_method_list->m_first_ptr +
+ (i * base_method_list->m_entsize));
+
+ if (instance_method_func(method->m_name.c_str(), method->m_types.c_str()))
+ break;
+ }
+ }
+
+ if (class_method_func) {
+ AppleObjCRuntime::ClassDescriptorSP metaclass(GetMetaclass());
+
+ // We don't care about the metaclass's superclass, or its class methods.
+ // Its instance methods are our class methods.
+
+ if (metaclass) {
+ metaclass->Describe(
+ std::function<void(ObjCLanguageRuntime::ObjCISA)>(nullptr),
+ class_method_func,
+ std::function<bool(const char *, const char *)>(nullptr),
+ std::function<bool(const char *, const char *, lldb::addr_t,
+ uint64_t)>(nullptr));
+ }
+ }
+
+ if (ivar_func) {
+ if (class_ro->m_ivars_ptr != 0) {
+ ivar_list_t ivar_list;
+ if (!ivar_list.Read(process, class_ro->m_ivars_ptr))
+ return false;
+
+ if (ivar_list.m_entsize != ivar_t::GetSize(process))
+ return false;
+
+ ivar_t ivar;
+
+ for (uint32_t i = 0, e = ivar_list.m_count; i < e; ++i) {
+ ivar.Read(process, ivar_list.m_first_ptr + (i * ivar_list.m_entsize));
+
+ if (ivar_func(ivar.m_name.c_str(), ivar.m_type.c_str(),
+ ivar.m_offset_ptr, ivar.m_size))
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+ConstString ClassDescriptorV2::GetClassName() {
+ if (!m_name) {
+ lldb_private::Process *process = m_runtime.GetProcess();
+
+ if (process) {
+ std::unique_ptr<objc_class_t> objc_class;
+ std::unique_ptr<class_ro_t> class_ro;
+ std::unique_ptr<class_rw_t> class_rw;
+
+ if (!Read_objc_class(process, objc_class))
+ return m_name;
+ if (!Read_class_row(process, *objc_class, class_ro, class_rw))
+ return m_name;
+
+ m_name = ConstString(class_ro->m_name.c_str());
+ }
+ }
+ return m_name;
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP ClassDescriptorV2::GetSuperclass() {
+ lldb_private::Process *process = m_runtime.GetProcess();
+
+ if (!process)
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+
+ std::unique_ptr<objc_class_t> objc_class;
+
+ if (!Read_objc_class(process, objc_class))
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+
+ return m_runtime.ObjCLanguageRuntime::GetClassDescriptorFromISA(
+ objc_class->m_superclass);
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP ClassDescriptorV2::GetMetaclass() const {
+ lldb_private::Process *process = m_runtime.GetProcess();
+
+ if (!process)
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+
+ std::unique_ptr<objc_class_t> objc_class;
+
+ if (!Read_objc_class(process, objc_class))
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+
+ lldb::addr_t candidate_isa = m_runtime.GetPointerISA(objc_class->m_isa);
+
+ return ObjCLanguageRuntime::ClassDescriptorSP(
+ new ClassDescriptorV2(m_runtime, candidate_isa, nullptr));
+}
+
+uint64_t ClassDescriptorV2::GetInstanceSize() {
+ lldb_private::Process *process = m_runtime.GetProcess();
+
+ if (process) {
+ std::unique_ptr<objc_class_t> objc_class;
+ std::unique_ptr<class_ro_t> class_ro;
+ std::unique_ptr<class_rw_t> class_rw;
+
+ if (!Read_objc_class(process, objc_class))
+ return 0;
+ if (!Read_class_row(process, *objc_class, class_ro, class_rw))
+ return 0;
+
+ return class_ro->m_instanceSize;
+ }
+
+ return 0;
+}
+
+ClassDescriptorV2::iVarsStorage::iVarsStorage()
+ : m_filled(false), m_ivars(), m_mutex() {}
+
+size_t ClassDescriptorV2::iVarsStorage::size() { return m_ivars.size(); }
+
+ClassDescriptorV2::iVarDescriptor &ClassDescriptorV2::iVarsStorage::
+operator[](size_t idx) {
+ return m_ivars[idx];
+}
+
+void ClassDescriptorV2::iVarsStorage::fill(AppleObjCRuntimeV2 &runtime,
+ ClassDescriptorV2 &descriptor) {
+ if (m_filled)
+ return;
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES));
+ LLDB_LOGV(log, "class_name = {0}", descriptor.GetClassName());
+ m_filled = true;
+ ObjCLanguageRuntime::EncodingToTypeSP encoding_to_type_sp(
+ runtime.GetEncodingToType());
+ Process *process(runtime.GetProcess());
+ if (!encoding_to_type_sp)
+ return;
+ descriptor.Describe(nullptr, nullptr, nullptr, [this, process,
+ encoding_to_type_sp,
+ log](const char *name,
+ const char *type,
+ lldb::addr_t offset_ptr,
+ uint64_t size) -> bool {
+ const bool for_expression = false;
+ const bool stop_loop = false;
+ LLDB_LOGV(log, "name = {0}, encoding = {1}, offset_ptr = {2:x}, size = {3}",
+ name, type, offset_ptr, size);
+ CompilerType ivar_type =
+ encoding_to_type_sp->RealizeType(type, for_expression);
+ if (ivar_type) {
+ LLDB_LOGV(log,
+ "name = {0}, encoding = {1}, offset_ptr = {2:x}, size = "
+ "{3}, type_size = {4}",
+ name, type, offset_ptr, size,
+ ivar_type.GetByteSize(nullptr).getValueOr(0));
+ Scalar offset_scalar;
+ Status error;
+ const int offset_ptr_size = 4;
+ const bool is_signed = false;
+ size_t read = process->ReadScalarIntegerFromMemory(
+ offset_ptr, offset_ptr_size, is_signed, offset_scalar, error);
+ if (error.Success() && 4 == read) {
+ LLDB_LOGV(log, "offset_ptr = {0:x} --> {1}", offset_ptr,
+ offset_scalar.SInt());
+ m_ivars.push_back(
+ {ConstString(name), ivar_type, size, offset_scalar.SInt()});
+ } else
+ LLDB_LOGV(log, "offset_ptr = {0:x} --> read fail, read = %{1}",
+ offset_ptr, read);
+ }
+ return stop_loop;
+ });
+}
+
+void ClassDescriptorV2::GetIVarInformation() {
+ m_ivars_storage.fill(m_runtime, *this);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h
new file mode 100644
index 000000000000..b8ba9dbb65f4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h
@@ -0,0 +1,331 @@
+//===-- AppleObjCClassDescriptorV2.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 liblldb_AppleObjCClassDescriptorV2_h_
+#define liblldb_AppleObjCClassDescriptorV2_h_
+
+#include <mutex>
+
+#include "AppleObjCRuntimeV2.h"
+#include "lldb/lldb-private.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+namespace lldb_private {
+
+class ClassDescriptorV2 : public ObjCLanguageRuntime::ClassDescriptor {
+public:
+ friend class lldb_private::AppleObjCRuntimeV2;
+
+ ~ClassDescriptorV2() override = default;
+
+ ConstString GetClassName() override;
+
+ ObjCLanguageRuntime::ClassDescriptorSP GetSuperclass() override;
+
+ ObjCLanguageRuntime::ClassDescriptorSP GetMetaclass() const override;
+
+ bool IsValid() override {
+ return true; // any Objective-C v2 runtime class descriptor we vend is valid
+ }
+
+ // a custom descriptor is used for tagged pointers
+ bool GetTaggedPointerInfo(uint64_t *info_bits = nullptr,
+ uint64_t *value_bits = nullptr,
+ uint64_t *payload = nullptr) override {
+ return false;
+ }
+
+ uint64_t GetInstanceSize() override;
+
+ ObjCLanguageRuntime::ObjCISA GetISA() override { return m_objc_class_ptr; }
+
+ bool Describe(
+ std::function<void(ObjCLanguageRuntime::ObjCISA)> const &superclass_func,
+ std::function<bool(const char *, const char *)> const
+ &instance_method_func,
+ std::function<bool(const char *, const char *)> const &class_method_func,
+ std::function<bool(const char *, const char *, lldb::addr_t,
+ uint64_t)> const &ivar_func) const override;
+
+ size_t GetNumIVars() override {
+ GetIVarInformation();
+ return m_ivars_storage.size();
+ }
+
+ iVarDescriptor GetIVarAtIndex(size_t idx) override {
+ if (idx >= GetNumIVars())
+ return iVarDescriptor();
+ return m_ivars_storage[idx];
+ }
+
+protected:
+ void GetIVarInformation();
+
+private:
+ static const uint32_t RW_REALIZED = (1 << 31);
+
+ struct objc_class_t {
+ ObjCLanguageRuntime::ObjCISA m_isa; // The class's metaclass.
+ ObjCLanguageRuntime::ObjCISA m_superclass;
+ lldb::addr_t m_cache_ptr;
+ lldb::addr_t m_vtable_ptr;
+ lldb::addr_t m_data_ptr;
+ uint8_t m_flags;
+
+ objc_class_t()
+ : m_isa(0), m_superclass(0), m_cache_ptr(0), m_vtable_ptr(0),
+ m_data_ptr(0), m_flags(0) {}
+
+ void Clear() {
+ m_isa = 0;
+ m_superclass = 0;
+ m_cache_ptr = 0;
+ m_vtable_ptr = 0;
+ m_data_ptr = 0;
+ m_flags = 0;
+ }
+
+ bool Read(Process *process, lldb::addr_t addr);
+ };
+
+ struct class_ro_t {
+ uint32_t m_flags;
+ uint32_t m_instanceStart;
+ uint32_t m_instanceSize;
+ uint32_t m_reserved;
+
+ lldb::addr_t m_ivarLayout_ptr;
+ lldb::addr_t m_name_ptr;
+ lldb::addr_t m_baseMethods_ptr;
+ lldb::addr_t m_baseProtocols_ptr;
+ lldb::addr_t m_ivars_ptr;
+
+ lldb::addr_t m_weakIvarLayout_ptr;
+ lldb::addr_t m_baseProperties_ptr;
+
+ std::string m_name;
+
+ bool Read(Process *process, lldb::addr_t addr);
+ };
+
+ struct class_rw_t {
+ uint32_t m_flags;
+ uint32_t m_version;
+
+ lldb::addr_t m_ro_ptr;
+ union {
+ lldb::addr_t m_method_list_ptr;
+ lldb::addr_t m_method_lists_ptr;
+ };
+ lldb::addr_t m_properties_ptr;
+ lldb::addr_t m_protocols_ptr;
+
+ ObjCLanguageRuntime::ObjCISA m_firstSubclass;
+ ObjCLanguageRuntime::ObjCISA m_nextSiblingClass;
+
+ bool Read(Process *process, lldb::addr_t addr);
+ };
+
+ struct method_list_t {
+ uint32_t m_entsize;
+ uint32_t m_count;
+ lldb::addr_t m_first_ptr;
+
+ bool Read(Process *process, lldb::addr_t addr);
+ };
+
+ struct method_t {
+ lldb::addr_t m_name_ptr;
+ lldb::addr_t m_types_ptr;
+ lldb::addr_t m_imp_ptr;
+
+ std::string m_name;
+ std::string m_types;
+
+ static size_t GetSize(Process *process) {
+ size_t ptr_size = process->GetAddressByteSize();
+
+ return ptr_size // SEL name;
+ + ptr_size // const char *types;
+ + ptr_size; // IMP imp;
+ }
+
+ bool Read(Process *process, lldb::addr_t addr);
+ };
+
+ struct ivar_list_t {
+ uint32_t m_entsize;
+ uint32_t m_count;
+ lldb::addr_t m_first_ptr;
+
+ bool Read(Process *process, lldb::addr_t addr);
+ };
+
+ struct ivar_t {
+ lldb::addr_t m_offset_ptr;
+ lldb::addr_t m_name_ptr;
+ lldb::addr_t m_type_ptr;
+ uint32_t m_alignment;
+ uint32_t m_size;
+
+ std::string m_name;
+ std::string m_type;
+
+ static size_t GetSize(Process *process) {
+ size_t ptr_size = process->GetAddressByteSize();
+
+ return ptr_size // uintptr_t *offset;
+ + ptr_size // const char *name;
+ + ptr_size // const char *type;
+ + sizeof(uint32_t) // uint32_t alignment;
+ + sizeof(uint32_t); // uint32_t size;
+ }
+
+ bool Read(Process *process, lldb::addr_t addr);
+ };
+
+ class iVarsStorage {
+ public:
+ iVarsStorage();
+
+ size_t size();
+
+ iVarDescriptor &operator[](size_t idx);
+
+ void fill(AppleObjCRuntimeV2 &runtime, ClassDescriptorV2 &descriptor);
+
+ private:
+ bool m_filled;
+ std::vector<iVarDescriptor> m_ivars;
+ std::recursive_mutex m_mutex;
+ };
+
+ // The constructor should only be invoked by the runtime as it builds its
+ // caches
+ // or populates them. A ClassDescriptorV2 should only ever exist in a cache.
+ ClassDescriptorV2(AppleObjCRuntimeV2 &runtime,
+ ObjCLanguageRuntime::ObjCISA isa, const char *name)
+ : m_runtime(runtime), m_objc_class_ptr(isa), m_name(name),
+ m_ivars_storage() {}
+
+ bool Read_objc_class(Process *process,
+ std::unique_ptr<objc_class_t> &objc_class) const;
+
+ bool Read_class_row(Process *process, const objc_class_t &objc_class,
+ std::unique_ptr<class_ro_t> &class_ro,
+ std::unique_ptr<class_rw_t> &class_rw) const;
+
+ AppleObjCRuntimeV2
+ &m_runtime; // The runtime, so we can read information lazily.
+ lldb::addr_t m_objc_class_ptr; // The address of the objc_class_t. (I.e.,
+ // objects of this class type have this as
+ // their ISA)
+ ConstString m_name; // May be NULL
+ iVarsStorage m_ivars_storage;
+};
+
+// tagged pointer descriptor
+class ClassDescriptorV2Tagged : public ObjCLanguageRuntime::ClassDescriptor {
+public:
+ ClassDescriptorV2Tagged(ConstString class_name, uint64_t payload) {
+ m_name = class_name;
+ if (!m_name) {
+ m_valid = false;
+ return;
+ }
+ m_valid = true;
+ m_payload = payload;
+ m_info_bits = (m_payload & 0xF0ULL) >> 4;
+ m_value_bits = (m_payload & ~0x0000000000000000FFULL) >> 8;
+ }
+
+ ClassDescriptorV2Tagged(
+ ObjCLanguageRuntime::ClassDescriptorSP actual_class_sp,
+ uint64_t payload) {
+ if (!actual_class_sp) {
+ m_valid = false;
+ return;
+ }
+ m_name = actual_class_sp->GetClassName();
+ if (!m_name) {
+ m_valid = false;
+ return;
+ }
+ m_valid = true;
+ m_payload = payload;
+ m_info_bits = (m_payload & 0x0FULL);
+ m_value_bits = (m_payload & ~0x0FULL) >> 4;
+ }
+
+ ~ClassDescriptorV2Tagged() override = default;
+
+ ConstString GetClassName() override { return m_name; }
+
+ ObjCLanguageRuntime::ClassDescriptorSP GetSuperclass() override {
+ // tagged pointers can represent a class that has a superclass, but since
+ // that information is not
+ // stored in the object itself, we would have to query the runtime to
+ // discover the hierarchy
+ // for the time being, we skip this step in the interest of static discovery
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+ }
+
+ ObjCLanguageRuntime::ClassDescriptorSP GetMetaclass() const override {
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+ }
+
+ bool IsValid() override { return m_valid; }
+
+ bool IsKVO() override {
+ return false; // tagged pointers are not KVO'ed
+ }
+
+ bool IsCFType() override {
+ return false; // tagged pointers are not CF objects
+ }
+
+ bool GetTaggedPointerInfo(uint64_t *info_bits = nullptr,
+ uint64_t *value_bits = nullptr,
+ uint64_t *payload = nullptr) override {
+ if (info_bits)
+ *info_bits = GetInfoBits();
+ if (value_bits)
+ *value_bits = GetValueBits();
+ if (payload)
+ *payload = GetPayload();
+ return true;
+ }
+
+ uint64_t GetInstanceSize() override {
+ return (IsValid() ? m_pointer_size : 0);
+ }
+
+ ObjCLanguageRuntime::ObjCISA GetISA() override {
+ return 0; // tagged pointers have no ISA
+ }
+
+ // these calls are not part of any formal tagged pointers specification
+ virtual uint64_t GetValueBits() { return (IsValid() ? m_value_bits : 0); }
+
+ virtual uint64_t GetInfoBits() { return (IsValid() ? m_info_bits : 0); }
+
+ virtual uint64_t GetPayload() { return (IsValid() ? m_payload : 0); }
+
+private:
+ ConstString m_name;
+ uint8_t m_pointer_size;
+ bool m_valid;
+ uint64_t m_info_bits;
+ uint64_t m_value_bits;
+ uint64_t m_payload;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_AppleObjCClassDescriptorV2_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp
new file mode 100644
index 000000000000..18f2a1829a41
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.cpp
@@ -0,0 +1,662 @@
+//===-- AppleObjCDeclVendor.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 "AppleObjCDeclVendor.h"
+
+#include "Plugins/ExpressionParser/Clang/ASTDumper.h"
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
+#include "lldb/Symbol/ClangUtil.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Log.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclObjC.h"
+
+
+using namespace lldb_private;
+
+class lldb_private::AppleObjCExternalASTSource
+ : public ClangExternalASTSourceCommon {
+public:
+ AppleObjCExternalASTSource(AppleObjCDeclVendor &decl_vendor)
+ : m_decl_vendor(decl_vendor) {}
+
+ bool FindExternalVisibleDeclsByName(const clang::DeclContext *decl_ctx,
+ clang::DeclarationName name) override {
+ static unsigned int invocation_id = 0;
+ unsigned int current_id = invocation_id++;
+
+ Log *log(GetLogIfAllCategoriesSet(
+ LIBLLDB_LOG_EXPRESSIONS)); // FIXME - a more appropriate log channel?
+
+ if (log) {
+ log->Printf("AppleObjCExternalASTSource::FindExternalVisibleDeclsByName[%"
+ "u] on (ASTContext*)%p Looking for %s in (%sDecl*)%p",
+ current_id,
+ static_cast<void *>(&decl_ctx->getParentASTContext()),
+ name.getAsString().c_str(), decl_ctx->getDeclKindName(),
+ static_cast<const void *>(decl_ctx));
+ }
+
+ do {
+ const clang::ObjCInterfaceDecl *interface_decl =
+ llvm::dyn_cast<clang::ObjCInterfaceDecl>(decl_ctx);
+
+ if (!interface_decl)
+ break;
+
+ clang::ObjCInterfaceDecl *non_const_interface_decl =
+ const_cast<clang::ObjCInterfaceDecl *>(interface_decl);
+
+ if (!m_decl_vendor.FinishDecl(non_const_interface_decl))
+ break;
+
+ clang::DeclContext::lookup_result result =
+ non_const_interface_decl->lookup(name);
+
+ return (result.size() != 0);
+ } while (false);
+
+ SetNoExternalVisibleDeclsForName(decl_ctx, name);
+ return false;
+ }
+
+ void CompleteType(clang::TagDecl *tag_decl) override {
+ static unsigned int invocation_id = 0;
+ unsigned int current_id = invocation_id++;
+
+ Log *log(GetLogIfAllCategoriesSet(
+ LIBLLDB_LOG_EXPRESSIONS)); // FIXME - a more appropriate log channel?
+
+ if (log) {
+ log->Printf("AppleObjCExternalASTSource::CompleteType[%u] on "
+ "(ASTContext*)%p Completing (TagDecl*)%p named %s",
+ current_id, static_cast<void *>(&tag_decl->getASTContext()),
+ static_cast<void *>(tag_decl),
+ tag_decl->getName().str().c_str());
+
+ log->Printf(" AOEAS::CT[%u] Before:", current_id);
+ ASTDumper dumper((clang::Decl *)tag_decl);
+ dumper.ToLog(log, " [CT] ");
+ }
+
+ if (log) {
+ log->Printf(" AOEAS::CT[%u] After:", current_id);
+ ASTDumper dumper((clang::Decl *)tag_decl);
+ dumper.ToLog(log, " [CT] ");
+ }
+ return;
+ }
+
+ void CompleteType(clang::ObjCInterfaceDecl *interface_decl) override {
+ static unsigned int invocation_id = 0;
+ unsigned int current_id = invocation_id++;
+
+ Log *log(GetLogIfAllCategoriesSet(
+ LIBLLDB_LOG_EXPRESSIONS)); // FIXME - a more appropriate log channel?
+
+ if (log) {
+ log->Printf("AppleObjCExternalASTSource::CompleteType[%u] on "
+ "(ASTContext*)%p Completing (ObjCInterfaceDecl*)%p named %s",
+ current_id,
+ static_cast<void *>(&interface_decl->getASTContext()),
+ static_cast<void *>(interface_decl),
+ interface_decl->getName().str().c_str());
+
+ log->Printf(" AOEAS::CT[%u] Before:", current_id);
+ ASTDumper dumper((clang::Decl *)interface_decl);
+ dumper.ToLog(log, " [CT] ");
+ }
+
+ m_decl_vendor.FinishDecl(interface_decl);
+
+ if (log) {
+ log->Printf(" [CT] After:");
+ ASTDumper dumper((clang::Decl *)interface_decl);
+ dumper.ToLog(log, " [CT] ");
+ }
+ return;
+ }
+
+ bool layoutRecordType(
+ const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment,
+ llvm::DenseMap<const clang::FieldDecl *, uint64_t> &FieldOffsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &BaseOffsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &VirtualBaseOffsets) override {
+ return false;
+ }
+
+ void StartTranslationUnit(clang::ASTConsumer *Consumer) override {
+ clang::TranslationUnitDecl *translation_unit_decl =
+ m_decl_vendor.m_ast_ctx.getASTContext()->getTranslationUnitDecl();
+ translation_unit_decl->setHasExternalVisibleStorage();
+ translation_unit_decl->setHasExternalLexicalStorage();
+ }
+
+private:
+ AppleObjCDeclVendor &m_decl_vendor;
+};
+
+AppleObjCDeclVendor::AppleObjCDeclVendor(ObjCLanguageRuntime &runtime)
+ : DeclVendor(), m_runtime(runtime), m_ast_ctx(runtime.GetProcess()
+ ->GetTarget()
+ .GetArchitecture()
+ .GetTriple()
+ .getTriple()
+ .c_str()),
+ m_type_realizer_sp(m_runtime.GetEncodingToType()) {
+ m_external_source = new AppleObjCExternalASTSource(*this);
+ llvm::IntrusiveRefCntPtr<clang::ExternalASTSource> external_source_owning_ptr(
+ m_external_source);
+ m_ast_ctx.getASTContext()->setExternalSource(external_source_owning_ptr);
+}
+
+clang::ObjCInterfaceDecl *
+AppleObjCDeclVendor::GetDeclForISA(ObjCLanguageRuntime::ObjCISA isa) {
+ ISAToInterfaceMap::const_iterator iter = m_isa_to_interface.find(isa);
+
+ if (iter != m_isa_to_interface.end())
+ return iter->second;
+
+ clang::ASTContext *ast_ctx = m_ast_ctx.getASTContext();
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor =
+ m_runtime.GetClassDescriptorFromISA(isa);
+
+ if (!descriptor)
+ return nullptr;
+
+ ConstString name(descriptor->GetClassName());
+
+ clang::IdentifierInfo &identifier_info =
+ ast_ctx->Idents.get(name.GetStringRef());
+
+ clang::ObjCInterfaceDecl *new_iface_decl = clang::ObjCInterfaceDecl::Create(
+ *ast_ctx, ast_ctx->getTranslationUnitDecl(), clang::SourceLocation(),
+ &identifier_info, nullptr, nullptr);
+
+ ClangASTMetadata meta_data;
+ meta_data.SetISAPtr(isa);
+ m_external_source->SetMetadata(new_iface_decl, meta_data);
+
+ new_iface_decl->setHasExternalVisibleStorage();
+ new_iface_decl->setHasExternalLexicalStorage();
+
+ ast_ctx->getTranslationUnitDecl()->addDecl(new_iface_decl);
+
+ m_isa_to_interface[isa] = new_iface_decl;
+
+ return new_iface_decl;
+}
+
+class ObjCRuntimeMethodType {
+public:
+ ObjCRuntimeMethodType(const char *types) : m_is_valid(false) {
+ const char *cursor = types;
+ enum ParserState { Start = 0, InType, InPos } state = Start;
+ const char *type = nullptr;
+ int brace_depth = 0;
+
+ uint32_t stepsLeft = 256;
+
+ while (true) {
+ if (--stepsLeft == 0) {
+ m_is_valid = false;
+ return;
+ }
+
+ switch (state) {
+ case Start: {
+ switch (*cursor) {
+ default:
+ state = InType;
+ type = cursor;
+ break;
+ case '\0':
+ m_is_valid = true;
+ return;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ m_is_valid = false;
+ return;
+ }
+ } break;
+ case InType: {
+ switch (*cursor) {
+ default:
+ ++cursor;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (!brace_depth) {
+ state = InPos;
+ if (type) {
+ m_type_vector.push_back(std::string(type, (cursor - type)));
+ } else {
+ m_is_valid = false;
+ return;
+ }
+ type = nullptr;
+ } else {
+ ++cursor;
+ }
+ break;
+ case '[':
+ case '{':
+ case '(':
+ ++brace_depth;
+ ++cursor;
+ break;
+ case ']':
+ case '}':
+ case ')':
+ if (!brace_depth) {
+ m_is_valid = false;
+ return;
+ }
+ --brace_depth;
+ ++cursor;
+ break;
+ case '\0':
+ m_is_valid = false;
+ return;
+ }
+ } break;
+ case InPos: {
+ switch (*cursor) {
+ default:
+ state = InType;
+ type = cursor;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ ++cursor;
+ break;
+ case '\0':
+ m_is_valid = true;
+ return;
+ }
+ } break;
+ }
+ }
+ }
+
+ clang::ObjCMethodDecl *
+ BuildMethod(clang::ObjCInterfaceDecl *interface_decl, const char *name,
+ bool instance,
+ ObjCLanguageRuntime::EncodingToTypeSP type_realizer_sp) {
+ if (!m_is_valid || m_type_vector.size() < 3)
+ return nullptr;
+
+ clang::ASTContext &ast_ctx(interface_decl->getASTContext());
+
+ const bool isInstance = instance;
+ const bool isVariadic = false;
+ const bool isSynthesized = false;
+ const bool isImplicitlyDeclared = true;
+ const bool isDefined = false;
+ const clang::ObjCMethodDecl::ImplementationControl impControl =
+ clang::ObjCMethodDecl::None;
+ const bool HasRelatedResultType = false;
+ const bool for_expression = true;
+
+ std::vector<clang::IdentifierInfo *> selector_components;
+
+ const char *name_cursor = name;
+ bool is_zero_argument = true;
+
+ while (*name_cursor != '\0') {
+ const char *colon_loc = strchr(name_cursor, ':');
+ if (!colon_loc) {
+ selector_components.push_back(
+ &ast_ctx.Idents.get(llvm::StringRef(name_cursor)));
+ break;
+ } else {
+ is_zero_argument = false;
+ selector_components.push_back(&ast_ctx.Idents.get(
+ llvm::StringRef(name_cursor, colon_loc - name_cursor)));
+ name_cursor = colon_loc + 1;
+ }
+ }
+
+ clang::IdentifierInfo **identifier_infos = selector_components.data();
+ if (!identifier_infos) {
+ return nullptr;
+ }
+
+ clang::Selector sel = ast_ctx.Selectors.getSelector(
+ is_zero_argument ? 0 : selector_components.size(),
+ identifier_infos);
+
+ clang::QualType ret_type =
+ ClangUtil::GetQualType(type_realizer_sp->RealizeType(
+ interface_decl->getASTContext(), m_type_vector[0].c_str(),
+ for_expression));
+
+ if (ret_type.isNull())
+ return nullptr;
+
+ clang::ObjCMethodDecl *ret = clang::ObjCMethodDecl::Create(
+ ast_ctx, clang::SourceLocation(), clang::SourceLocation(), sel,
+ ret_type, nullptr, interface_decl, isInstance, isVariadic,
+ isSynthesized, isImplicitlyDeclared, isDefined, impControl,
+ HasRelatedResultType);
+
+ std::vector<clang::ParmVarDecl *> parm_vars;
+
+ for (size_t ai = 3, ae = m_type_vector.size(); ai != ae; ++ai) {
+ const bool for_expression = true;
+ clang::QualType arg_type =
+ ClangUtil::GetQualType(type_realizer_sp->RealizeType(
+ ast_ctx, m_type_vector[ai].c_str(), for_expression));
+
+ if (arg_type.isNull())
+ return nullptr; // well, we just wasted a bunch of time. Wish we could
+ // delete the stuff we'd just made!
+
+ parm_vars.push_back(clang::ParmVarDecl::Create(
+ ast_ctx, ret, clang::SourceLocation(), clang::SourceLocation(),
+ nullptr, arg_type, nullptr, clang::SC_None, nullptr));
+ }
+
+ ret->setMethodParams(ast_ctx,
+ llvm::ArrayRef<clang::ParmVarDecl *>(parm_vars),
+ llvm::ArrayRef<clang::SourceLocation>());
+
+ return ret;
+ }
+
+ explicit operator bool() { return m_is_valid; }
+
+ size_t GetNumTypes() { return m_type_vector.size(); }
+
+ const char *GetTypeAtIndex(size_t idx) { return m_type_vector[idx].c_str(); }
+
+private:
+ typedef std::vector<std::string> TypeVector;
+
+ TypeVector m_type_vector;
+ bool m_is_valid;
+};
+
+bool AppleObjCDeclVendor::FinishDecl(clang::ObjCInterfaceDecl *interface_decl) {
+ Log *log(GetLogIfAllCategoriesSet(
+ LIBLLDB_LOG_EXPRESSIONS)); // FIXME - a more appropriate log channel?
+
+ ClangASTMetadata *metadata = m_external_source->GetMetadata(interface_decl);
+ ObjCLanguageRuntime::ObjCISA objc_isa = 0;
+ if (metadata)
+ objc_isa = metadata->GetISAPtr();
+
+ if (!objc_isa)
+ return false;
+
+ if (!interface_decl->hasExternalVisibleStorage())
+ return true;
+
+ interface_decl->startDefinition();
+
+ interface_decl->setHasExternalVisibleStorage(false);
+ interface_decl->setHasExternalLexicalStorage(false);
+
+ ObjCLanguageRuntime::ClassDescriptorSP descriptor =
+ m_runtime.GetClassDescriptorFromISA(objc_isa);
+
+ if (!descriptor)
+ return false;
+
+ auto superclass_func = [interface_decl,
+ this](ObjCLanguageRuntime::ObjCISA isa) {
+ clang::ObjCInterfaceDecl *superclass_decl = GetDeclForISA(isa);
+
+ if (!superclass_decl)
+ return;
+
+ FinishDecl(superclass_decl);
+ clang::ASTContext *context = m_ast_ctx.getASTContext();
+ interface_decl->setSuperClass(context->getTrivialTypeSourceInfo(
+ context->getObjCInterfaceType(superclass_decl)));
+ };
+
+ auto instance_method_func =
+ [log, interface_decl, this](const char *name, const char *types) -> bool {
+ if (!name || !types)
+ return false; // skip this one
+
+ ObjCRuntimeMethodType method_type(types);
+
+ clang::ObjCMethodDecl *method_decl =
+ method_type.BuildMethod(interface_decl, name, true, m_type_realizer_sp);
+
+ if (log)
+ log->Printf("[ AOTV::FD] Instance method [%s] [%s]", name, types);
+
+ if (method_decl)
+ interface_decl->addDecl(method_decl);
+
+ return false;
+ };
+
+ auto class_method_func = [log, interface_decl,
+ this](const char *name, const char *types) -> bool {
+ if (!name || !types)
+ return false; // skip this one
+
+ ObjCRuntimeMethodType method_type(types);
+
+ clang::ObjCMethodDecl *method_decl = method_type.BuildMethod(
+ interface_decl, name, false, m_type_realizer_sp);
+
+ if (log)
+ log->Printf("[ AOTV::FD] Class method [%s] [%s]", name, types);
+
+ if (method_decl)
+ interface_decl->addDecl(method_decl);
+
+ return false;
+ };
+
+ auto ivar_func = [log, interface_decl,
+ this](const char *name, const char *type,
+ lldb::addr_t offset_ptr, uint64_t size) -> bool {
+ if (!name || !type)
+ return false;
+
+ const bool for_expression = false;
+
+ if (log)
+ log->Printf(
+ "[ AOTV::FD] Instance variable [%s] [%s], offset at %" PRIx64, name,
+ type, offset_ptr);
+
+ CompilerType ivar_type = m_runtime.GetEncodingToType()->RealizeType(
+ m_ast_ctx, type, for_expression);
+
+ if (ivar_type.IsValid()) {
+ clang::TypeSourceInfo *const type_source_info = nullptr;
+ const bool is_synthesized = false;
+ clang::ObjCIvarDecl *ivar_decl = clang::ObjCIvarDecl::Create(
+ *m_ast_ctx.getASTContext(), interface_decl, clang::SourceLocation(),
+ clang::SourceLocation(), &m_ast_ctx.getASTContext()->Idents.get(name),
+ ClangUtil::GetQualType(ivar_type),
+ type_source_info, // TypeSourceInfo *
+ clang::ObjCIvarDecl::Public, nullptr, is_synthesized);
+
+ if (ivar_decl) {
+ interface_decl->addDecl(ivar_decl);
+ }
+ }
+
+ return false;
+ };
+
+ if (log) {
+ ASTDumper method_dumper((clang::Decl *)interface_decl);
+
+ log->Printf("[AppleObjCDeclVendor::FinishDecl] Finishing Objective-C "
+ "interface for %s",
+ descriptor->GetClassName().AsCString());
+ }
+
+ if (!descriptor->Describe(superclass_func, instance_method_func,
+ class_method_func, ivar_func))
+ return false;
+
+ if (log) {
+ ASTDumper method_dumper((clang::Decl *)interface_decl);
+
+ log->Printf(
+ "[AppleObjCDeclVendor::FinishDecl] Finished Objective-C interface");
+
+ method_dumper.ToLog(log, " [AOTV::FD] ");
+ }
+
+ return true;
+}
+
+uint32_t
+AppleObjCDeclVendor::FindDecls(ConstString name, bool append,
+ uint32_t max_matches,
+ std::vector<clang::NamedDecl *> &decls) {
+ static unsigned int invocation_id = 0;
+ unsigned int current_id = invocation_id++;
+
+ Log *log(GetLogIfAllCategoriesSet(
+ LIBLLDB_LOG_EXPRESSIONS)); // FIXME - a more appropriate log channel?
+
+ if (log)
+ log->Printf("AppleObjCDeclVendor::FindDecls [%u] ('%s', %s, %u, )",
+ current_id, (const char *)name.AsCString(),
+ append ? "true" : "false", max_matches);
+
+ if (!append)
+ decls.clear();
+
+ uint32_t ret = 0;
+
+ do {
+ // See if the type is already in our ASTContext.
+
+ clang::ASTContext *ast_ctx = m_ast_ctx.getASTContext();
+
+ clang::IdentifierInfo &identifier_info =
+ ast_ctx->Idents.get(name.GetStringRef());
+ clang::DeclarationName decl_name =
+ ast_ctx->DeclarationNames.getIdentifier(&identifier_info);
+
+ clang::DeclContext::lookup_result lookup_result =
+ ast_ctx->getTranslationUnitDecl()->lookup(decl_name);
+
+ if (!lookup_result.empty()) {
+ if (clang::ObjCInterfaceDecl *result_iface_decl =
+ llvm::dyn_cast<clang::ObjCInterfaceDecl>(lookup_result[0])) {
+ if (log) {
+ clang::QualType result_iface_type =
+ ast_ctx->getObjCInterfaceType(result_iface_decl);
+ ASTDumper dumper(result_iface_type);
+
+ uint64_t isa_value = LLDB_INVALID_ADDRESS;
+ ClangASTMetadata *metadata =
+ m_external_source->GetMetadata(result_iface_decl);
+ if (metadata)
+ isa_value = metadata->GetISAPtr();
+
+ log->Printf("AOCTV::FT [%u] Found %s (isa 0x%" PRIx64
+ ") in the ASTContext",
+ current_id, dumper.GetCString(), isa_value);
+ }
+
+ decls.push_back(result_iface_decl);
+ ret++;
+ break;
+ } else {
+ if (log)
+ log->Printf("AOCTV::FT [%u] There's something in the ASTContext, but "
+ "it's not something we know about",
+ current_id);
+ break;
+ }
+ } else if (log) {
+ log->Printf("AOCTV::FT [%u] Couldn't find %s in the ASTContext",
+ current_id, name.AsCString());
+ }
+
+ // It's not. If it exists, we have to put it into our ASTContext.
+
+ ObjCLanguageRuntime::ObjCISA isa = m_runtime.GetISA(name);
+
+ if (!isa) {
+ if (log)
+ log->Printf("AOCTV::FT [%u] Couldn't find the isa", current_id);
+
+ break;
+ }
+
+ clang::ObjCInterfaceDecl *iface_decl = GetDeclForISA(isa);
+
+ if (!iface_decl) {
+ if (log)
+ log->Printf("AOCTV::FT [%u] Couldn't get the Objective-C interface for "
+ "isa 0x%" PRIx64,
+ current_id, (uint64_t)isa);
+
+ break;
+ }
+
+ if (log) {
+ clang::QualType new_iface_type =
+ ast_ctx->getObjCInterfaceType(iface_decl);
+ ASTDumper dumper(new_iface_type);
+ log->Printf("AOCTV::FT [%u] Created %s (isa 0x%" PRIx64 ")", current_id,
+ dumper.GetCString(), (uint64_t)isa);
+ }
+
+ decls.push_back(iface_decl);
+ ret++;
+ break;
+ } while (false);
+
+ return ret;
+}
+
+clang::ExternalASTMerger::ImporterSource
+AppleObjCDeclVendor::GetImporterSource() {
+ return {*m_ast_ctx.getASTContext(),
+ *m_ast_ctx.getFileManager(),
+ m_ast_ctx.GetOriginMap()
+ };
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.h
new file mode 100644
index 000000000000..77b30b7fde79
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCDeclVendor.h
@@ -0,0 +1,51 @@
+//===-- AppleObjCDeclVendor.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 liblldb_AppleObjCDeclVendor_h_
+#define liblldb_AppleObjCDeclVendor_h_
+
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/DeclVendor.h"
+#include "lldb/lldb-private.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+namespace lldb_private {
+
+class AppleObjCExternalASTSource;
+
+class AppleObjCDeclVendor : public DeclVendor {
+public:
+ AppleObjCDeclVendor(ObjCLanguageRuntime &runtime);
+
+ uint32_t FindDecls(ConstString name, bool append, uint32_t max_matches,
+ std::vector<clang::NamedDecl *> &decls) override;
+
+ clang::ExternalASTMerger::ImporterSource GetImporterSource() override;
+
+ friend class AppleObjCExternalASTSource;
+
+private:
+ clang::ObjCInterfaceDecl *GetDeclForISA(ObjCLanguageRuntime::ObjCISA isa);
+ bool FinishDecl(clang::ObjCInterfaceDecl *decl);
+
+ ObjCLanguageRuntime &m_runtime;
+ ClangASTContext m_ast_ctx;
+ ObjCLanguageRuntime::EncodingToTypeSP m_type_realizer_sp;
+ AppleObjCExternalASTSource *m_external_source;
+
+ typedef llvm::DenseMap<ObjCLanguageRuntime::ObjCISA,
+ clang::ObjCInterfaceDecl *>
+ ISAToInterfaceMap;
+
+ ISAToInterfaceMap m_isa_to_interface;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_AppleObjCDeclVendor_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp
new file mode 100644
index 000000000000..52ed3628520f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp
@@ -0,0 +1,583 @@
+//===-- AppleObjCRuntime.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 "AppleObjCRuntime.h"
+#include "AppleObjCTrampolineHandler.h"
+
+#include "clang/AST/Type.h"
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/FunctionCaller.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "Plugins/Process/Utility/HistoryThread.h"
+#include "Plugins/Language/ObjC/NSString.h"
+#include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h"
+
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+
+char AppleObjCRuntime::ID = 0;
+
+AppleObjCRuntime::~AppleObjCRuntime() {}
+
+AppleObjCRuntime::AppleObjCRuntime(Process *process)
+ : ObjCLanguageRuntime(process), m_read_objc_library(false),
+ m_objc_trampoline_handler_up(), m_Foundation_major() {
+ ReadObjCLibraryIfNeeded(process->GetTarget().GetImages());
+}
+
+bool AppleObjCRuntime::GetObjectDescription(Stream &str, ValueObject &valobj) {
+ CompilerType compiler_type(valobj.GetCompilerType());
+ bool is_signed;
+ // ObjC objects can only be pointers (or numbers that actually represents
+ // pointers but haven't been typecast, because reasons..)
+ if (!compiler_type.IsIntegerType(is_signed) && !compiler_type.IsPointerType())
+ return false;
+
+ // Make the argument list: we pass one arg, the address of our pointer, to
+ // the print function.
+ Value val;
+
+ if (!valobj.ResolveValue(val.GetScalar()))
+ return false;
+
+ // Value Objects may not have a process in their ExecutionContextRef. But we
+ // need to have one in the ref we pass down to eventually call description.
+ // Get it from the target if it isn't present.
+ ExecutionContext exe_ctx;
+ if (valobj.GetProcessSP()) {
+ exe_ctx = ExecutionContext(valobj.GetExecutionContextRef());
+ } else {
+ exe_ctx.SetContext(valobj.GetTargetSP(), true);
+ if (!exe_ctx.HasProcessScope())
+ return false;
+ }
+ return GetObjectDescription(str, val, exe_ctx.GetBestExecutionContextScope());
+}
+bool AppleObjCRuntime::GetObjectDescription(Stream &strm, Value &value,
+ ExecutionContextScope *exe_scope) {
+ if (!m_read_objc_library)
+ return false;
+
+ ExecutionContext exe_ctx;
+ exe_scope->CalculateExecutionContext(exe_ctx);
+ Process *process = exe_ctx.GetProcessPtr();
+ if (!process)
+ return false;
+
+ // We need other parts of the exe_ctx, but the processes have to match.
+ assert(m_process == process);
+
+ // Get the function address for the print function.
+ const Address *function_address = GetPrintForDebuggerAddr();
+ if (!function_address)
+ return false;
+
+ Target *target = exe_ctx.GetTargetPtr();
+ CompilerType compiler_type = value.GetCompilerType();
+ if (compiler_type) {
+ if (!ClangASTContext::IsObjCObjectPointerType(compiler_type)) {
+ strm.Printf("Value doesn't point to an ObjC object.\n");
+ return false;
+ }
+ } else {
+ // If it is not a pointer, see if we can make it into a pointer.
+ ClangASTContext *ast_context = target->GetScratchClangASTContext();
+ CompilerType opaque_type = ast_context->GetBasicType(eBasicTypeObjCID);
+ if (!opaque_type)
+ opaque_type = ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
+ // value.SetContext(Value::eContextTypeClangType, opaque_type_ptr);
+ value.SetCompilerType(opaque_type);
+ }
+
+ ValueList arg_value_list;
+ arg_value_list.PushValue(value);
+
+ // This is the return value:
+ ClangASTContext *ast_context = target->GetScratchClangASTContext();
+
+ CompilerType return_compiler_type = ast_context->GetCStringType(true);
+ Value ret;
+ // ret.SetContext(Value::eContextTypeClangType, return_compiler_type);
+ ret.SetCompilerType(return_compiler_type);
+
+ if (exe_ctx.GetFramePtr() == nullptr) {
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (thread == nullptr) {
+ exe_ctx.SetThreadSP(process->GetThreadList().GetSelectedThread());
+ thread = exe_ctx.GetThreadPtr();
+ }
+ if (thread) {
+ exe_ctx.SetFrameSP(thread->GetSelectedFrame());
+ }
+ }
+
+ // Now we're ready to call the function:
+
+ DiagnosticManager diagnostics;
+ lldb::addr_t wrapper_struct_addr = LLDB_INVALID_ADDRESS;
+
+ if (!m_print_object_caller_up) {
+ Status error;
+ m_print_object_caller_up.reset(
+ exe_scope->CalculateTarget()->GetFunctionCallerForLanguage(
+ eLanguageTypeObjC, return_compiler_type, *function_address,
+ arg_value_list, "objc-object-description", error));
+ if (error.Fail()) {
+ m_print_object_caller_up.reset();
+ strm.Printf("Could not get function runner to call print for debugger "
+ "function: %s.",
+ error.AsCString());
+ return false;
+ }
+ m_print_object_caller_up->InsertFunction(exe_ctx, wrapper_struct_addr,
+ diagnostics);
+ } else {
+ m_print_object_caller_up->WriteFunctionArguments(
+ exe_ctx, wrapper_struct_addr, arg_value_list, diagnostics);
+ }
+
+ EvaluateExpressionOptions options;
+ options.SetUnwindOnError(true);
+ options.SetTryAllThreads(true);
+ options.SetStopOthers(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetTimeout(process->GetUtilityExpressionTimeout());
+ options.SetIsForUtilityExpr(true);
+
+ ExpressionResults results = m_print_object_caller_up->ExecuteFunction(
+ exe_ctx, &wrapper_struct_addr, options, diagnostics, ret);
+ if (results != eExpressionCompleted) {
+ strm.Printf("Error evaluating Print Object function: %d.\n", results);
+ return false;
+ }
+
+ addr_t result_ptr = ret.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+
+ char buf[512];
+ size_t cstr_len = 0;
+ size_t full_buffer_len = sizeof(buf) - 1;
+ size_t curr_len = full_buffer_len;
+ while (curr_len == full_buffer_len) {
+ Status error;
+ curr_len = process->ReadCStringFromMemory(result_ptr + cstr_len, buf,
+ sizeof(buf), error);
+ strm.Write(buf, curr_len);
+ cstr_len += curr_len;
+ }
+ return cstr_len > 0;
+}
+
+lldb::ModuleSP AppleObjCRuntime::GetObjCModule() {
+ ModuleSP module_sp(m_objc_module_wp.lock());
+ if (module_sp)
+ return module_sp;
+
+ Process *process = GetProcess();
+ if (process) {
+ const ModuleList &modules = process->GetTarget().GetImages();
+ for (uint32_t idx = 0; idx < modules.GetSize(); idx++) {
+ module_sp = modules.GetModuleAtIndex(idx);
+ if (AppleObjCRuntime::AppleIsModuleObjCLibrary(module_sp)) {
+ m_objc_module_wp = module_sp;
+ return module_sp;
+ }
+ }
+ }
+ return ModuleSP();
+}
+
+Address *AppleObjCRuntime::GetPrintForDebuggerAddr() {
+ if (!m_PrintForDebugger_addr) {
+ const ModuleList &modules = m_process->GetTarget().GetImages();
+
+ SymbolContextList contexts;
+ SymbolContext context;
+
+ if ((!modules.FindSymbolsWithNameAndType(ConstString("_NSPrintForDebugger"),
+ eSymbolTypeCode, contexts)) &&
+ (!modules.FindSymbolsWithNameAndType(ConstString("_CFPrintForDebugger"),
+ eSymbolTypeCode, contexts)))
+ return nullptr;
+
+ contexts.GetContextAtIndex(0, context);
+
+ m_PrintForDebugger_addr.reset(new Address(context.symbol->GetAddress()));
+ }
+
+ return m_PrintForDebugger_addr.get();
+}
+
+bool AppleObjCRuntime::CouldHaveDynamicValue(ValueObject &in_value) {
+ return in_value.GetCompilerType().IsPossibleDynamicType(
+ nullptr,
+ false, // do not check C++
+ true); // check ObjC
+}
+
+bool AppleObjCRuntime::GetDynamicTypeAndAddress(
+ ValueObject &in_value, lldb::DynamicValueType use_dynamic,
+ TypeAndOrName &class_type_or_name, Address &address,
+ Value::ValueType &value_type) {
+ return false;
+}
+
+TypeAndOrName
+AppleObjCRuntime::FixUpDynamicType(const TypeAndOrName &type_and_or_name,
+ ValueObject &static_value) {
+ CompilerType static_type(static_value.GetCompilerType());
+ Flags static_type_flags(static_type.GetTypeInfo());
+
+ TypeAndOrName ret(type_and_or_name);
+ if (type_and_or_name.HasType()) {
+ // The type will always be the type of the dynamic object. If our parent's
+ // type was a pointer, then our type should be a pointer to the type of the
+ // dynamic object. If a reference, then the original type should be
+ // okay...
+ CompilerType orig_type = type_and_or_name.GetCompilerType();
+ CompilerType corrected_type = orig_type;
+ if (static_type_flags.AllSet(eTypeIsPointer))
+ corrected_type = orig_type.GetPointerType();
+ ret.SetCompilerType(corrected_type);
+ } else {
+ // If we are here we need to adjust our dynamic type name to include the
+ // correct & or * symbol
+ std::string corrected_name(type_and_or_name.GetName().GetCString());
+ if (static_type_flags.AllSet(eTypeIsPointer))
+ corrected_name.append(" *");
+ // the parent type should be a correctly pointer'ed or referenc'ed type
+ ret.SetCompilerType(static_type);
+ ret.SetName(corrected_name.c_str());
+ }
+ return ret;
+}
+
+bool AppleObjCRuntime::AppleIsModuleObjCLibrary(const ModuleSP &module_sp) {
+ if (module_sp) {
+ const FileSpec &module_file_spec = module_sp->GetFileSpec();
+ static ConstString ObjCName("libobjc.A.dylib");
+
+ if (module_file_spec) {
+ if (module_file_spec.GetFilename() == ObjCName)
+ return true;
+ }
+ }
+ return false;
+}
+
+// we use the version of Foundation to make assumptions about the ObjC runtime
+// on a target
+uint32_t AppleObjCRuntime::GetFoundationVersion() {
+ if (!m_Foundation_major.hasValue()) {
+ const ModuleList &modules = m_process->GetTarget().GetImages();
+ for (uint32_t idx = 0; idx < modules.GetSize(); idx++) {
+ lldb::ModuleSP module_sp = modules.GetModuleAtIndex(idx);
+ if (!module_sp)
+ continue;
+ if (strcmp(module_sp->GetFileSpec().GetFilename().AsCString(""),
+ "Foundation") == 0) {
+ m_Foundation_major = module_sp->GetVersion().getMajor();
+ return *m_Foundation_major;
+ }
+ }
+ return LLDB_INVALID_MODULE_VERSION;
+ } else
+ return m_Foundation_major.getValue();
+}
+
+void AppleObjCRuntime::GetValuesForGlobalCFBooleans(lldb::addr_t &cf_true,
+ lldb::addr_t &cf_false) {
+ cf_true = cf_false = LLDB_INVALID_ADDRESS;
+}
+
+bool AppleObjCRuntime::IsModuleObjCLibrary(const ModuleSP &module_sp) {
+ return AppleIsModuleObjCLibrary(module_sp);
+}
+
+bool AppleObjCRuntime::ReadObjCLibrary(const ModuleSP &module_sp) {
+ // Maybe check here and if we have a handler already, and the UUID of this
+ // module is the same as the one in the current module, then we don't have to
+ // reread it?
+ m_objc_trampoline_handler_up.reset(
+ new AppleObjCTrampolineHandler(m_process->shared_from_this(), module_sp));
+ if (m_objc_trampoline_handler_up != nullptr) {
+ m_read_objc_library = true;
+ return true;
+ } else
+ return false;
+}
+
+ThreadPlanSP AppleObjCRuntime::GetStepThroughTrampolinePlan(Thread &thread,
+ bool stop_others) {
+ ThreadPlanSP thread_plan_sp;
+ if (m_objc_trampoline_handler_up)
+ thread_plan_sp = m_objc_trampoline_handler_up->GetStepThroughDispatchPlan(
+ thread, stop_others);
+ return thread_plan_sp;
+}
+
+// Static Functions
+ObjCLanguageRuntime::ObjCRuntimeVersions
+AppleObjCRuntime::GetObjCVersion(Process *process, ModuleSP &objc_module_sp) {
+ if (!process)
+ return ObjCRuntimeVersions::eObjC_VersionUnknown;
+
+ Target &target = process->GetTarget();
+ if (target.GetArchitecture().GetTriple().getVendor() !=
+ llvm::Triple::VendorType::Apple)
+ return ObjCRuntimeVersions::eObjC_VersionUnknown;
+
+ const ModuleList &target_modules = target.GetImages();
+ std::lock_guard<std::recursive_mutex> gaurd(target_modules.GetMutex());
+
+ size_t num_images = target_modules.GetSize();
+ for (size_t i = 0; i < num_images; i++) {
+ ModuleSP module_sp = target_modules.GetModuleAtIndexUnlocked(i);
+ // One tricky bit here is that we might get called as part of the initial
+ // module loading, but before all the pre-run libraries get winnowed from
+ // the module list. So there might actually be an old and incorrect ObjC
+ // library sitting around in the list, and we don't want to look at that.
+ // That's why we call IsLoadedInTarget.
+
+ if (AppleIsModuleObjCLibrary(module_sp) &&
+ module_sp->IsLoadedInTarget(&target)) {
+ objc_module_sp = module_sp;
+ ObjectFile *ofile = module_sp->GetObjectFile();
+ if (!ofile)
+ return ObjCRuntimeVersions::eObjC_VersionUnknown;
+
+ SectionList *sections = module_sp->GetSectionList();
+ if (!sections)
+ return ObjCRuntimeVersions::eObjC_VersionUnknown;
+ SectionSP v1_telltale_section_sp =
+ sections->FindSectionByName(ConstString("__OBJC"));
+ if (v1_telltale_section_sp) {
+ return ObjCRuntimeVersions::eAppleObjC_V1;
+ }
+ return ObjCRuntimeVersions::eAppleObjC_V2;
+ }
+ }
+
+ return ObjCRuntimeVersions::eObjC_VersionUnknown;
+}
+
+void AppleObjCRuntime::SetExceptionBreakpoints() {
+ const bool catch_bp = false;
+ const bool throw_bp = true;
+ const bool is_internal = true;
+
+ if (!m_objc_exception_bp_sp) {
+ m_objc_exception_bp_sp = LanguageRuntime::CreateExceptionBreakpoint(
+ m_process->GetTarget(), GetLanguageType(), catch_bp, throw_bp,
+ is_internal);
+ if (m_objc_exception_bp_sp)
+ m_objc_exception_bp_sp->SetBreakpointKind("ObjC exception");
+ } else
+ m_objc_exception_bp_sp->SetEnabled(true);
+}
+
+void AppleObjCRuntime::ClearExceptionBreakpoints() {
+ if (!m_process)
+ return;
+
+ if (m_objc_exception_bp_sp.get()) {
+ m_objc_exception_bp_sp->SetEnabled(false);
+ }
+}
+
+bool AppleObjCRuntime::ExceptionBreakpointsAreSet() {
+ return m_objc_exception_bp_sp && m_objc_exception_bp_sp->IsEnabled();
+}
+
+bool AppleObjCRuntime::ExceptionBreakpointsExplainStop(
+ lldb::StopInfoSP stop_reason) {
+ if (!m_process)
+ return false;
+
+ if (!stop_reason || stop_reason->GetStopReason() != eStopReasonBreakpoint)
+ return false;
+
+ uint64_t break_site_id = stop_reason->GetValue();
+ return m_process->GetBreakpointSiteList().BreakpointSiteContainsBreakpoint(
+ break_site_id, m_objc_exception_bp_sp->GetID());
+}
+
+bool AppleObjCRuntime::CalculateHasNewLiteralsAndIndexing() {
+ if (!m_process)
+ return false;
+
+ Target &target(m_process->GetTarget());
+
+ static ConstString s_method_signature(
+ "-[NSDictionary objectForKeyedSubscript:]");
+ static ConstString s_arclite_method_signature(
+ "__arclite_objectForKeyedSubscript");
+
+ SymbolContextList sc_list;
+
+ return target.GetImages().FindSymbolsWithNameAndType(
+ s_method_signature, eSymbolTypeCode, sc_list) ||
+ target.GetImages().FindSymbolsWithNameAndType(
+ s_arclite_method_signature, eSymbolTypeCode, sc_list);
+}
+
+lldb::SearchFilterSP AppleObjCRuntime::CreateExceptionSearchFilter() {
+ Target &target = m_process->GetTarget();
+
+ FileSpecList filter_modules;
+ if (target.GetArchitecture().GetTriple().getVendor() == llvm::Triple::Apple) {
+ filter_modules.Append(std::get<0>(GetExceptionThrowLocation()));
+ }
+ return target.GetSearchFilterForModuleList(&filter_modules);
+}
+
+ValueObjectSP AppleObjCRuntime::GetExceptionObjectForThread(
+ ThreadSP thread_sp) {
+ auto *cpp_runtime = m_process->GetLanguageRuntime(eLanguageTypeC_plus_plus);
+ if (!cpp_runtime) return ValueObjectSP();
+ auto cpp_exception = cpp_runtime->GetExceptionObjectForThread(thread_sp);
+ if (!cpp_exception) return ValueObjectSP();
+
+ auto descriptor = GetClassDescriptor(*cpp_exception);
+ if (!descriptor || !descriptor->IsValid()) return ValueObjectSP();
+
+ while (descriptor) {
+ ConstString class_name(descriptor->GetClassName());
+ if (class_name == "NSException")
+ return cpp_exception;
+ descriptor = descriptor->GetSuperclass();
+ }
+
+ return ValueObjectSP();
+}
+
+ThreadSP AppleObjCRuntime::GetBacktraceThreadFromException(
+ lldb::ValueObjectSP exception_sp) {
+ ValueObjectSP reserved_dict =
+ exception_sp->GetChildMemberWithName(ConstString("reserved"), true);
+ if (!reserved_dict) return ThreadSP();
+
+ reserved_dict = reserved_dict->GetSyntheticValue();
+ if (!reserved_dict) return ThreadSP();
+
+ CompilerType objc_id =
+ exception_sp->GetTargetSP()->GetScratchClangASTContext()->GetBasicType(
+ lldb::eBasicTypeObjCID);
+ ValueObjectSP return_addresses;
+
+ auto objc_object_from_address = [&exception_sp, &objc_id](uint64_t addr,
+ const char *name) {
+ Value value(addr);
+ value.SetCompilerType(objc_id);
+ auto object = ValueObjectConstResult::Create(
+ exception_sp->GetTargetSP().get(), value, ConstString(name));
+ object = object->GetDynamicValue(eDynamicDontRunTarget);
+ return object;
+ };
+
+ for (size_t idx = 0; idx < reserved_dict->GetNumChildren(); idx++) {
+ ValueObjectSP dict_entry = reserved_dict->GetChildAtIndex(idx, true);
+
+ DataExtractor data;
+ data.SetAddressByteSize(dict_entry->GetProcessSP()->GetAddressByteSize());
+ Status error;
+ dict_entry->GetData(data, error);
+ if (error.Fail()) return ThreadSP();
+
+ lldb::offset_t data_offset = 0;
+ auto dict_entry_key = data.GetPointer(&data_offset);
+ auto dict_entry_value = data.GetPointer(&data_offset);
+
+ auto key_nsstring = objc_object_from_address(dict_entry_key, "key");
+ StreamString key_summary;
+ if (lldb_private::formatters::NSStringSummaryProvider(
+ *key_nsstring, key_summary, TypeSummaryOptions()) &&
+ !key_summary.Empty()) {
+ if (key_summary.GetString() == "\"callStackReturnAddresses\"") {
+ return_addresses = objc_object_from_address(dict_entry_value,
+ "callStackReturnAddresses");
+ break;
+ }
+ }
+ }
+
+ if (!return_addresses) return ThreadSP();
+ auto frames_value =
+ return_addresses->GetChildMemberWithName(ConstString("_frames"), true);
+ addr_t frames_addr = frames_value->GetValueAsUnsigned(0);
+ auto count_value =
+ return_addresses->GetChildMemberWithName(ConstString("_cnt"), true);
+ size_t count = count_value->GetValueAsUnsigned(0);
+ auto ignore_value =
+ return_addresses->GetChildMemberWithName(ConstString("_ignore"), true);
+ size_t ignore = ignore_value->GetValueAsUnsigned(0);
+
+ size_t ptr_size = m_process->GetAddressByteSize();
+ std::vector<lldb::addr_t> pcs;
+ for (size_t idx = 0; idx < count; idx++) {
+ Status error;
+ addr_t pc = m_process->ReadPointerFromMemory(
+ frames_addr + (ignore + idx) * ptr_size, error);
+ pcs.push_back(pc);
+ }
+
+ if (pcs.empty()) return ThreadSP();
+
+ ThreadSP new_thread_sp(new HistoryThread(*m_process, 0, pcs));
+ m_process->GetExtendedThreadList().AddThread(new_thread_sp);
+ return new_thread_sp;
+}
+
+std::tuple<FileSpec, ConstString>
+AppleObjCRuntime::GetExceptionThrowLocation() {
+ return std::make_tuple(
+ FileSpec("libobjc.A.dylib"), ConstString("objc_exception_throw"));
+}
+
+void AppleObjCRuntime::ReadObjCLibraryIfNeeded(const ModuleList &module_list) {
+ if (!HasReadObjCLibrary()) {
+ std::lock_guard<std::recursive_mutex> guard(module_list.GetMutex());
+
+ size_t num_modules = module_list.GetSize();
+ for (size_t i = 0; i < num_modules; i++) {
+ auto mod = module_list.GetModuleAtIndex(i);
+ if (IsModuleObjCLibrary(mod)) {
+ ReadObjCLibrary(mod);
+ break;
+ }
+ }
+ }
+}
+
+void AppleObjCRuntime::ModulesDidLoad(const ModuleList &module_list) {
+ ReadObjCLibraryIfNeeded(module_list);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h
new file mode 100644
index 000000000000..79ac53e1e440
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h
@@ -0,0 +1,131 @@
+//===-- AppleObjCRuntime.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 liblldb_AppleObjCRuntime_h_
+#define liblldb_AppleObjCRuntime_h_
+
+#include "llvm/ADT/Optional.h"
+
+#include "AppleObjCTrampolineHandler.h"
+#include "AppleThreadPlanStepThroughObjCTrampoline.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/lldb-private.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+namespace lldb_private {
+
+class AppleObjCRuntime : public lldb_private::ObjCLanguageRuntime {
+public:
+ ~AppleObjCRuntime() override;
+
+ // Static Functions
+ // Note there is no CreateInstance, Initialize & Terminate functions here,
+ // because
+ // you can't make an instance of this generic runtime.
+
+ static char ID;
+
+ bool isA(const void *ClassID) const override {
+ return ClassID == &ID || ObjCLanguageRuntime::isA(ClassID);
+ }
+
+ static bool classof(const LanguageRuntime *runtime) {
+ return runtime->isA(&ID);
+ }
+
+ // These are generic runtime functions:
+ bool GetObjectDescription(Stream &str, Value &value,
+ ExecutionContextScope *exe_scope) override;
+
+ bool GetObjectDescription(Stream &str, ValueObject &object) override;
+
+ bool CouldHaveDynamicValue(ValueObject &in_value) override;
+
+ bool GetDynamicTypeAndAddress(ValueObject &in_value,
+ lldb::DynamicValueType use_dynamic,
+ TypeAndOrName &class_type_or_name,
+ Address &address,
+ Value::ValueType &value_type) override;
+
+ TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name,
+ ValueObject &static_value) override;
+
+ // These are the ObjC specific functions.
+
+ bool IsModuleObjCLibrary(const lldb::ModuleSP &module_sp) override;
+
+ bool ReadObjCLibrary(const lldb::ModuleSP &module_sp) override;
+
+ bool HasReadObjCLibrary() override { return m_read_objc_library; }
+
+ lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
+ bool stop_others) override;
+
+ // Get the "libobjc.A.dylib" module from the current target if we can find
+ // it, also cache it once it is found to ensure quick lookups.
+ lldb::ModuleSP GetObjCModule();
+
+ // Sync up with the target
+
+ void ModulesDidLoad(const ModuleList &module_list) override;
+
+ void SetExceptionBreakpoints() override;
+
+ void ClearExceptionBreakpoints() override;
+
+ bool ExceptionBreakpointsAreSet() override;
+
+ bool ExceptionBreakpointsExplainStop(lldb::StopInfoSP stop_reason) override;
+
+ lldb::SearchFilterSP CreateExceptionSearchFilter() override;
+
+ static std::tuple<FileSpec, ConstString> GetExceptionThrowLocation();
+
+ lldb::ValueObjectSP GetExceptionObjectForThread(
+ lldb::ThreadSP thread_sp) override;
+
+ lldb::ThreadSP GetBacktraceThreadFromException(
+ lldb::ValueObjectSP thread_sp) override;
+
+ uint32_t GetFoundationVersion();
+
+ virtual void GetValuesForGlobalCFBooleans(lldb::addr_t &cf_true,
+ lldb::addr_t &cf_false);
+
+ virtual bool IsTaggedPointer (lldb::addr_t addr) { return false; }
+
+protected:
+ // Call CreateInstance instead.
+ AppleObjCRuntime(Process *process);
+
+ bool CalculateHasNewLiteralsAndIndexing() override;
+
+ static bool AppleIsModuleObjCLibrary(const lldb::ModuleSP &module_sp);
+
+ static ObjCRuntimeVersions GetObjCVersion(Process *process,
+ lldb::ModuleSP &objc_module_sp);
+
+ void ReadObjCLibraryIfNeeded(const ModuleList &module_list);
+
+ Address *GetPrintForDebuggerAddr();
+
+ std::unique_ptr<Address> m_PrintForDebugger_addr;
+ bool m_read_objc_library;
+ std::unique_ptr<lldb_private::AppleObjCTrampolineHandler>
+ m_objc_trampoline_handler_up;
+ lldb::BreakpointSP m_objc_exception_bp_sp;
+ lldb::ModuleWP m_objc_module_wp;
+ std::unique_ptr<FunctionCaller> m_print_object_caller_up;
+
+ llvm::Optional<uint32_t> m_Foundation_major;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_AppleObjCRuntime_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp
new file mode 100644
index 000000000000..c8884fd5c9b9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp
@@ -0,0 +1,443 @@
+//===-- AppleObjCRuntimeV1.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 "AppleObjCRuntimeV1.h"
+#include "AppleObjCDeclVendor.h"
+#include "AppleObjCTrampolineHandler.h"
+
+#include "clang/AST/Type.h"
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Expression/FunctionCaller.h"
+#include "lldb/Expression/UtilityFunction.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+
+#include <memory>
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+
+char AppleObjCRuntimeV1::ID = 0;
+
+AppleObjCRuntimeV1::AppleObjCRuntimeV1(Process *process)
+ : AppleObjCRuntime(process), m_hash_signature(),
+ m_isa_hash_table_ptr(LLDB_INVALID_ADDRESS) {}
+
+// for V1 runtime we just try to return a class name as that is the minimum
+// level of support required for the data formatters to work
+bool AppleObjCRuntimeV1::GetDynamicTypeAndAddress(
+ ValueObject &in_value, lldb::DynamicValueType use_dynamic,
+ TypeAndOrName &class_type_or_name, Address &address,
+ Value::ValueType &value_type) {
+ class_type_or_name.Clear();
+ value_type = Value::ValueType::eValueTypeScalar;
+ if (CouldHaveDynamicValue(in_value)) {
+ auto class_descriptor(GetClassDescriptor(in_value));
+ if (class_descriptor && class_descriptor->IsValid() &&
+ class_descriptor->GetClassName()) {
+ const addr_t object_ptr = in_value.GetPointerValue();
+ address.SetRawAddress(object_ptr);
+ class_type_or_name.SetName(class_descriptor->GetClassName());
+ }
+ }
+ return !class_type_or_name.IsEmpty();
+}
+
+// Static Functions
+lldb_private::LanguageRuntime *
+AppleObjCRuntimeV1::CreateInstance(Process *process,
+ lldb::LanguageType language) {
+ // FIXME: This should be a MacOS or iOS process, and we need to look for the
+ // OBJC section to make
+ // sure we aren't using the V1 runtime.
+ if (language == eLanguageTypeObjC) {
+ ModuleSP objc_module_sp;
+
+ if (AppleObjCRuntime::GetObjCVersion(process, objc_module_sp) ==
+ ObjCRuntimeVersions::eAppleObjC_V1)
+ return new AppleObjCRuntimeV1(process);
+ else
+ return nullptr;
+ } else
+ return nullptr;
+}
+
+void AppleObjCRuntimeV1::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "Apple Objective-C Language Runtime - Version 1",
+ CreateInstance,
+ /*command_callback = */ nullptr, GetBreakpointExceptionPrecondition);
+}
+
+void AppleObjCRuntimeV1::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString AppleObjCRuntimeV1::GetPluginNameStatic() {
+ static ConstString g_name("apple-objc-v1");
+ return g_name;
+}
+
+// PluginInterface protocol
+ConstString AppleObjCRuntimeV1::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t AppleObjCRuntimeV1::GetPluginVersion() { return 1; }
+
+BreakpointResolverSP
+AppleObjCRuntimeV1::CreateExceptionResolver(Breakpoint *bkpt, bool catch_bp,
+ bool throw_bp) {
+ BreakpointResolverSP resolver_sp;
+
+ if (throw_bp)
+ resolver_sp = std::make_shared<BreakpointResolverName>(
+ bkpt, std::get<1>(GetExceptionThrowLocation()).AsCString(),
+ eFunctionNameTypeBase, eLanguageTypeUnknown, Breakpoint::Exact, 0,
+ eLazyBoolNo);
+ // FIXME: don't do catch yet.
+ return resolver_sp;
+}
+
+struct BufStruct {
+ char contents[2048];
+};
+
+UtilityFunction *AppleObjCRuntimeV1::CreateObjectChecker(const char *name) {
+ std::unique_ptr<BufStruct> buf(new BufStruct);
+
+ int strformatsize = snprintf(&buf->contents[0], sizeof(buf->contents),
+ "struct __objc_class "
+ " \n"
+ "{ "
+ " \n"
+ " struct __objc_class *isa; "
+ " \n"
+ " struct __objc_class *super_class; "
+ " \n"
+ " const char *name; "
+ " \n"
+ " // rest of struct elided because unused "
+ " \n"
+ "}; "
+ " \n"
+ " "
+ " \n"
+ "struct __objc_object "
+ " \n"
+ "{ "
+ " \n"
+ " struct __objc_class *isa; "
+ " \n"
+ "}; "
+ " \n"
+ " "
+ " \n"
+ "extern \"C\" void "
+ " \n"
+ "%s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) "
+ " \n"
+ "{ "
+ " \n"
+ " struct __objc_object *obj = (struct "
+ "__objc_object*)$__lldb_arg_obj; \n"
+ " if ($__lldb_arg_obj == (void *)0) "
+ " \n"
+ " return; // nil is ok "
+ " (int)strlen(obj->isa->name); "
+ " \n"
+ "} "
+ " \n",
+ name);
+ assert(strformatsize < (int)sizeof(buf->contents));
+ (void)strformatsize;
+
+ Status error;
+ return GetTargetRef().GetUtilityFunctionForLanguage(
+ buf->contents, eLanguageTypeObjC, name, error);
+}
+
+AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1(
+ ValueObject &isa_pointer) {
+ Initialize(isa_pointer.GetValueAsUnsigned(0), isa_pointer.GetProcessSP());
+}
+
+AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1(
+ ObjCISA isa, lldb::ProcessSP process_sp) {
+ Initialize(isa, process_sp);
+}
+
+void AppleObjCRuntimeV1::ClassDescriptorV1::Initialize(
+ ObjCISA isa, lldb::ProcessSP process_sp) {
+ if (!isa || !process_sp) {
+ m_valid = false;
+ return;
+ }
+
+ m_valid = true;
+
+ Status error;
+
+ m_isa = process_sp->ReadPointerFromMemory(isa, error);
+
+ if (error.Fail()) {
+ m_valid = false;
+ return;
+ }
+
+ uint32_t ptr_size = process_sp->GetAddressByteSize();
+
+ if (!IsPointerValid(m_isa, ptr_size)) {
+ m_valid = false;
+ return;
+ }
+
+ m_parent_isa = process_sp->ReadPointerFromMemory(m_isa + ptr_size, error);
+
+ if (error.Fail()) {
+ m_valid = false;
+ return;
+ }
+
+ if (!IsPointerValid(m_parent_isa, ptr_size, true)) {
+ m_valid = false;
+ return;
+ }
+
+ lldb::addr_t name_ptr =
+ process_sp->ReadPointerFromMemory(m_isa + 2 * ptr_size, error);
+
+ if (error.Fail()) {
+ m_valid = false;
+ return;
+ }
+
+ lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0));
+
+ size_t count = process_sp->ReadCStringFromMemory(
+ name_ptr, (char *)buffer_sp->GetBytes(), 1024, error);
+
+ if (error.Fail()) {
+ m_valid = false;
+ return;
+ }
+
+ if (count)
+ m_name = ConstString((char *)buffer_sp->GetBytes());
+ else
+ m_name = ConstString();
+
+ m_instance_size = process_sp->ReadUnsignedIntegerFromMemory(
+ m_isa + 5 * ptr_size, ptr_size, 0, error);
+
+ if (error.Fail()) {
+ m_valid = false;
+ return;
+ }
+
+ m_process_wp = lldb::ProcessWP(process_sp);
+}
+
+AppleObjCRuntime::ClassDescriptorSP
+AppleObjCRuntimeV1::ClassDescriptorV1::GetSuperclass() {
+ if (!m_valid)
+ return AppleObjCRuntime::ClassDescriptorSP();
+ ProcessSP process_sp = m_process_wp.lock();
+ if (!process_sp)
+ return AppleObjCRuntime::ClassDescriptorSP();
+ return ObjCLanguageRuntime::ClassDescriptorSP(
+ new AppleObjCRuntimeV1::ClassDescriptorV1(m_parent_isa, process_sp));
+}
+
+AppleObjCRuntime::ClassDescriptorSP
+AppleObjCRuntimeV1::ClassDescriptorV1::GetMetaclass() const {
+ return ClassDescriptorSP();
+}
+
+bool AppleObjCRuntimeV1::ClassDescriptorV1::Describe(
+ std::function<void(ObjCLanguageRuntime::ObjCISA)> const &superclass_func,
+ std::function<bool(const char *, const char *)> const &instance_method_func,
+ std::function<bool(const char *, const char *)> const &class_method_func,
+ std::function<bool(const char *, const char *, lldb::addr_t,
+ uint64_t)> const &ivar_func) const {
+ return false;
+}
+
+lldb::addr_t AppleObjCRuntimeV1::GetTaggedPointerObfuscator() {
+ return 0;
+}
+
+lldb::addr_t AppleObjCRuntimeV1::GetISAHashTablePointer() {
+ if (m_isa_hash_table_ptr == LLDB_INVALID_ADDRESS) {
+ ModuleSP objc_module_sp(GetObjCModule());
+
+ if (!objc_module_sp)
+ return LLDB_INVALID_ADDRESS;
+
+ static ConstString g_objc_debug_class_hash("_objc_debug_class_hash");
+
+ const Symbol *symbol = objc_module_sp->FindFirstSymbolWithNameAndType(
+ g_objc_debug_class_hash, lldb::eSymbolTypeData);
+ if (symbol && symbol->ValueIsAddress()) {
+ Process *process = GetProcess();
+ if (process) {
+
+ lldb::addr_t objc_debug_class_hash_addr =
+ symbol->GetAddressRef().GetLoadAddress(&process->GetTarget());
+
+ if (objc_debug_class_hash_addr != LLDB_INVALID_ADDRESS) {
+ Status error;
+ lldb::addr_t objc_debug_class_hash_ptr =
+ process->ReadPointerFromMemory(objc_debug_class_hash_addr, error);
+ if (objc_debug_class_hash_ptr != 0 &&
+ objc_debug_class_hash_ptr != LLDB_INVALID_ADDRESS) {
+ m_isa_hash_table_ptr = objc_debug_class_hash_ptr;
+ }
+ }
+ }
+ }
+ }
+ return m_isa_hash_table_ptr;
+}
+
+void AppleObjCRuntimeV1::UpdateISAToDescriptorMapIfNeeded() {
+ // TODO: implement HashTableSignature...
+ Process *process = GetProcess();
+
+ if (process) {
+ // Update the process stop ID that indicates the last time we updated the
+ // map, whether it was successful or not.
+ m_isa_to_descriptor_stop_id = process->GetStopID();
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ ProcessSP process_sp = process->shared_from_this();
+
+ ModuleSP objc_module_sp(GetObjCModule());
+
+ if (!objc_module_sp)
+ return;
+
+ uint32_t isa_count = 0;
+
+ lldb::addr_t hash_table_ptr = GetISAHashTablePointer();
+ if (hash_table_ptr != LLDB_INVALID_ADDRESS) {
+ // Read the NXHashTable struct:
+ //
+ // typedef struct {
+ // const NXHashTablePrototype *prototype;
+ // unsigned count;
+ // unsigned nbBuckets;
+ // void *buckets;
+ // const void *info;
+ // } NXHashTable;
+
+ Status error;
+ DataBufferHeap buffer(1024, 0);
+ if (process->ReadMemory(hash_table_ptr, buffer.GetBytes(), 20, error) ==
+ 20) {
+ const uint32_t addr_size = m_process->GetAddressByteSize();
+ const ByteOrder byte_order = m_process->GetByteOrder();
+ DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), byte_order,
+ addr_size);
+ lldb::offset_t offset = addr_size; // Skip prototype
+ const uint32_t count = data.GetU32(&offset);
+ const uint32_t num_buckets = data.GetU32(&offset);
+ const addr_t buckets_ptr = data.GetPointer(&offset);
+ if (m_hash_signature.NeedsUpdate(count, num_buckets, buckets_ptr)) {
+ m_hash_signature.UpdateSignature(count, num_buckets, buckets_ptr);
+
+ const uint32_t data_size = num_buckets * 2 * sizeof(uint32_t);
+ buffer.SetByteSize(data_size);
+
+ if (process->ReadMemory(buckets_ptr, buffer.GetBytes(), data_size,
+ error) == data_size) {
+ data.SetData(buffer.GetBytes(), buffer.GetByteSize(), byte_order);
+ offset = 0;
+ for (uint32_t bucket_idx = 0; bucket_idx < num_buckets;
+ ++bucket_idx) {
+ const uint32_t bucket_isa_count = data.GetU32(&offset);
+ const lldb::addr_t bucket_data = data.GetU32(&offset);
+
+ if (bucket_isa_count == 0)
+ continue;
+
+ isa_count += bucket_isa_count;
+
+ ObjCISA isa;
+ if (bucket_isa_count == 1) {
+ // When we only have one entry in the bucket, the bucket data
+ // is the "isa"
+ isa = bucket_data;
+ if (isa) {
+ if (!ISAIsCached(isa)) {
+ ClassDescriptorSP descriptor_sp(
+ new ClassDescriptorV1(isa, process_sp));
+
+ if (log && log->GetVerbose())
+ log->Printf("AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64
+ " from _objc_debug_class_hash to "
+ "isa->descriptor cache",
+ isa);
+
+ AddClass(isa, descriptor_sp);
+ }
+ }
+ } else {
+ // When we have more than one entry in the bucket, the bucket
+ // data is a pointer to an array of "isa" values
+ addr_t isa_addr = bucket_data;
+ for (uint32_t isa_idx = 0; isa_idx < bucket_isa_count;
+ ++isa_idx, isa_addr += addr_size) {
+ isa = m_process->ReadPointerFromMemory(isa_addr, error);
+
+ if (isa && isa != LLDB_INVALID_ADDRESS) {
+ if (!ISAIsCached(isa)) {
+ ClassDescriptorSP descriptor_sp(
+ new ClassDescriptorV1(isa, process_sp));
+
+ if (log && log->GetVerbose())
+ log->Printf(
+ "AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64
+ " from _objc_debug_class_hash to isa->descriptor "
+ "cache",
+ isa);
+
+ AddClass(isa, descriptor_sp);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } else {
+ m_isa_to_descriptor_stop_id = UINT32_MAX;
+ }
+}
+
+DeclVendor *AppleObjCRuntimeV1::GetDeclVendor() {
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h
new file mode 100644
index 000000000000..6fdae63d4126
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h
@@ -0,0 +1,156 @@
+//===-- AppleObjCRuntimeV1.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 liblldb_AppleObjCRuntimeV1_h_
+#define liblldb_AppleObjCRuntimeV1_h_
+
+#include "AppleObjCRuntime.h"
+#include "lldb/lldb-private.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+namespace lldb_private {
+
+class AppleObjCRuntimeV1 : public AppleObjCRuntime {
+public:
+ ~AppleObjCRuntimeV1() override = default;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::LanguageRuntime *
+ CreateInstance(Process *process, lldb::LanguageType language);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static char ID;
+
+ bool isA(const void *ClassID) const override {
+ return ClassID == &ID || AppleObjCRuntime::isA(ClassID);
+ }
+
+ static bool classof(const LanguageRuntime *runtime) {
+ return runtime->isA(&ID);
+ }
+
+ lldb::addr_t GetTaggedPointerObfuscator();
+
+ class ClassDescriptorV1 : public ObjCLanguageRuntime::ClassDescriptor {
+ public:
+ ClassDescriptorV1(ValueObject &isa_pointer);
+ ClassDescriptorV1(ObjCISA isa, lldb::ProcessSP process_sp);
+
+ ~ClassDescriptorV1() override = default;
+
+ ConstString GetClassName() override { return m_name; }
+
+ ClassDescriptorSP GetSuperclass() override;
+
+ ClassDescriptorSP GetMetaclass() const override;
+
+ bool IsValid() override { return m_valid; }
+
+ // v1 does not support tagged pointers
+ bool GetTaggedPointerInfo(uint64_t *info_bits = nullptr,
+ uint64_t *value_bits = nullptr,
+ uint64_t *payload = nullptr) override {
+ return false;
+ }
+
+ uint64_t GetInstanceSize() override { return m_instance_size; }
+
+ ObjCISA GetISA() override { return m_isa; }
+
+ bool
+ Describe(std::function<void(ObjCLanguageRuntime::ObjCISA)> const
+ &superclass_func,
+ std::function<bool(const char *, const char *)> const
+ &instance_method_func,
+ std::function<bool(const char *, const char *)> const
+ &class_method_func,
+ std::function<bool(const char *, const char *, lldb::addr_t,
+ uint64_t)> const &ivar_func) const override;
+
+ protected:
+ void Initialize(ObjCISA isa, lldb::ProcessSP process_sp);
+
+ private:
+ ConstString m_name;
+ ObjCISA m_isa;
+ ObjCISA m_parent_isa;
+ bool m_valid;
+ lldb::ProcessWP m_process_wp;
+ uint64_t m_instance_size;
+ };
+
+ // These are generic runtime functions:
+ bool GetDynamicTypeAndAddress(ValueObject &in_value,
+ lldb::DynamicValueType use_dynamic,
+ TypeAndOrName &class_type_or_name,
+ Address &address,
+ Value::ValueType &value_type) override;
+
+ UtilityFunction *CreateObjectChecker(const char *) override;
+
+ // PluginInterface protocol
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ ObjCRuntimeVersions GetRuntimeVersion() const override {
+ return ObjCRuntimeVersions::eAppleObjC_V1;
+ }
+
+ void UpdateISAToDescriptorMapIfNeeded() override;
+
+ DeclVendor *GetDeclVendor() override;
+
+protected:
+ lldb::BreakpointResolverSP CreateExceptionResolver(Breakpoint *bkpt,
+ bool catch_bp,
+ bool throw_bp) override;
+
+ class HashTableSignature {
+ public:
+ HashTableSignature()
+ : m_count(0), m_num_buckets(0), m_buckets_ptr(LLDB_INVALID_ADDRESS) {}
+
+ bool NeedsUpdate(uint32_t count, uint32_t num_buckets,
+ lldb::addr_t buckets_ptr) {
+ return m_count != count || m_num_buckets != num_buckets ||
+ m_buckets_ptr != buckets_ptr;
+ }
+
+ void UpdateSignature(uint32_t count, uint32_t num_buckets,
+ lldb::addr_t buckets_ptr) {
+ m_count = count;
+ m_num_buckets = num_buckets;
+ m_buckets_ptr = buckets_ptr;
+ }
+
+ protected:
+ uint32_t m_count;
+ uint32_t m_num_buckets;
+ lldb::addr_t m_buckets_ptr;
+ };
+
+ lldb::addr_t GetISAHashTablePointer();
+
+ HashTableSignature m_hash_signature;
+ lldb::addr_t m_isa_hash_table_ptr;
+ std::unique_ptr<DeclVendor> m_decl_vendor_up;
+
+private:
+ AppleObjCRuntimeV1(Process *process);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_AppleObjCRuntimeV1_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
new file mode 100644
index 000000000000..635eaff637bc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
@@ -0,0 +1,2736 @@
+//===-- AppleObjCRuntimeV2.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 <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclObjC.h"
+
+#include "lldb/Core/ClangForward.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/lldb-enumerations.h"
+
+#include "lldb/Core/ClangForward.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/FunctionCaller.h"
+#include "lldb/Expression/UtilityFunction.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionValueBoolean.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrameRecognizer.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/Timer.h"
+
+#include "AppleObjCClassDescriptorV2.h"
+#include "AppleObjCDeclVendor.h"
+#include "AppleObjCRuntimeV2.h"
+#include "AppleObjCTrampolineHandler.h"
+#include "AppleObjCTypeEncodingParser.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclObjC.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+
+char AppleObjCRuntimeV2::ID = 0;
+
+static const char *g_get_dynamic_class_info_name =
+ "__lldb_apple_objc_v2_get_dynamic_class_info";
+// Testing using the new C++11 raw string literals. If this breaks GCC then we
+// will need to revert to the code above...
+static const char *g_get_dynamic_class_info_body = R"(
+
+extern "C"
+{
+ size_t strlen(const char *);
+ char *strncpy (char * s1, const char * s2, size_t n);
+ int printf(const char * format, ...);
+}
+#define DEBUG_PRINTF(fmt, ...) if (should_log) printf(fmt, ## __VA_ARGS__)
+
+typedef struct _NXMapTable {
+ void *prototype;
+ unsigned num_classes;
+ unsigned num_buckets_minus_one;
+ void *buckets;
+} NXMapTable;
+
+#define NX_MAPNOTAKEY ((void *)(-1))
+
+typedef struct BucketInfo
+{
+ const char *name_ptr;
+ Class isa;
+} BucketInfo;
+
+struct ClassInfo
+{
+ Class isa;
+ uint32_t hash;
+} __attribute__((__packed__));
+
+uint32_t
+__lldb_apple_objc_v2_get_dynamic_class_info (void *gdb_objc_realized_classes_ptr,
+ void *class_infos_ptr,
+ uint32_t class_infos_byte_size,
+ uint32_t should_log)
+{
+ DEBUG_PRINTF ("gdb_objc_realized_classes_ptr = %p\n", gdb_objc_realized_classes_ptr);
+ DEBUG_PRINTF ("class_infos_ptr = %p\n", class_infos_ptr);
+ DEBUG_PRINTF ("class_infos_byte_size = %u\n", class_infos_byte_size);
+ const NXMapTable *grc = (const NXMapTable *)gdb_objc_realized_classes_ptr;
+ if (grc)
+ {
+ const unsigned num_classes = grc->num_classes;
+ if (class_infos_ptr)
+ {
+ const size_t max_class_infos = class_infos_byte_size/sizeof(ClassInfo);
+ ClassInfo *class_infos = (ClassInfo *)class_infos_ptr;
+ BucketInfo *buckets = (BucketInfo *)grc->buckets;
+
+ uint32_t idx = 0;
+ for (unsigned i=0; i<=grc->num_buckets_minus_one; ++i)
+ {
+ if (buckets[i].name_ptr != NX_MAPNOTAKEY)
+ {
+ if (idx < max_class_infos)
+ {
+ const char *s = buckets[i].name_ptr;
+ uint32_t h = 5381;
+ for (unsigned char c = *s; c; c = *++s)
+ h = ((h << 5) + h) + c;
+ class_infos[idx].hash = h;
+ class_infos[idx].isa = buckets[i].isa;
+ }
+ ++idx;
+ }
+ }
+ if (idx < max_class_infos)
+ {
+ class_infos[idx].isa = NULL;
+ class_infos[idx].hash = 0;
+ }
+ }
+ return num_classes;
+ }
+ return 0;
+}
+
+)";
+
+// We'll substitute in class_getName or class_getNameRaw depending
+// on which is present.
+static const char *g_shared_cache_class_name_funcptr = R"(
+extern "C"
+{
+ const char *%s(void *objc_class);
+ const char *(*class_name_lookup_func)(void *) = %s;
+}
+)";
+
+static const char *g_get_shared_cache_class_info_name =
+ "__lldb_apple_objc_v2_get_shared_cache_class_info";
+// Testing using the new C++11 raw string literals. If this breaks GCC then we
+// will need to revert to the code above...
+static const char *g_get_shared_cache_class_info_body = R"(
+
+extern "C"
+{
+ size_t strlen(const char *);
+ char *strncpy (char * s1, const char * s2, size_t n);
+ int printf(const char * format, ...);
+}
+
+#define DEBUG_PRINTF(fmt, ...) if (should_log) printf(fmt, ## __VA_ARGS__)
+
+
+struct objc_classheader_t {
+ int32_t clsOffset;
+ int32_t hiOffset;
+};
+
+struct objc_clsopt_t {
+ uint32_t capacity;
+ uint32_t occupied;
+ uint32_t shift;
+ uint32_t mask;
+ uint32_t zero;
+ uint32_t unused;
+ uint64_t salt;
+ uint32_t scramble[256];
+ uint8_t tab[0]; // tab[mask+1]
+ // uint8_t checkbytes[capacity];
+ // int32_t offset[capacity];
+ // objc_classheader_t clsOffsets[capacity];
+ // uint32_t duplicateCount;
+ // objc_classheader_t duplicateOffsets[duplicateCount];
+};
+
+struct objc_opt_t {
+ uint32_t version;
+ int32_t selopt_offset;
+ int32_t headeropt_offset;
+ int32_t clsopt_offset;
+};
+
+struct objc_opt_v14_t {
+ uint32_t version;
+ uint32_t flags;
+ int32_t selopt_offset;
+ int32_t headeropt_offset;
+ int32_t clsopt_offset;
+};
+
+struct ClassInfo
+{
+ Class isa;
+ uint32_t hash;
+} __attribute__((__packed__));
+
+uint32_t
+__lldb_apple_objc_v2_get_shared_cache_class_info (void *objc_opt_ro_ptr,
+ void *class_infos_ptr,
+ uint32_t class_infos_byte_size,
+ uint32_t should_log)
+{
+ uint32_t idx = 0;
+ DEBUG_PRINTF ("objc_opt_ro_ptr = %p\n", objc_opt_ro_ptr);
+ DEBUG_PRINTF ("class_infos_ptr = %p\n", class_infos_ptr);
+ DEBUG_PRINTF ("class_infos_byte_size = %u (%llu class infos)\n", class_infos_byte_size, (uint64_t)(class_infos_byte_size/sizeof(ClassInfo)));
+ if (objc_opt_ro_ptr)
+ {
+ const objc_opt_t *objc_opt = (objc_opt_t *)objc_opt_ro_ptr;
+ const objc_opt_v14_t* objc_opt_v14 = (objc_opt_v14_t*)objc_opt_ro_ptr;
+ const bool is_v14_format = objc_opt->version >= 14;
+ if (is_v14_format)
+ {
+ DEBUG_PRINTF ("objc_opt->version = %u\n", objc_opt_v14->version);
+ DEBUG_PRINTF ("objc_opt->flags = %u\n", objc_opt_v14->flags);
+ DEBUG_PRINTF ("objc_opt->selopt_offset = %d\n", objc_opt_v14->selopt_offset);
+ DEBUG_PRINTF ("objc_opt->headeropt_offset = %d\n", objc_opt_v14->headeropt_offset);
+ DEBUG_PRINTF ("objc_opt->clsopt_offset = %d\n", objc_opt_v14->clsopt_offset);
+ }
+ else
+ {
+ DEBUG_PRINTF ("objc_opt->version = %u\n", objc_opt->version);
+ DEBUG_PRINTF ("objc_opt->selopt_offset = %d\n", objc_opt->selopt_offset);
+ DEBUG_PRINTF ("objc_opt->headeropt_offset = %d\n", objc_opt->headeropt_offset);
+ DEBUG_PRINTF ("objc_opt->clsopt_offset = %d\n", objc_opt->clsopt_offset);
+ }
+ if (objc_opt->version == 12 || objc_opt->version == 13 || objc_opt->version == 14 || objc_opt->version == 15)
+ {
+ const objc_clsopt_t* clsopt = NULL;
+ if (is_v14_format)
+ clsopt = (const objc_clsopt_t*)((uint8_t *)objc_opt_v14 + objc_opt_v14->clsopt_offset);
+ else
+ clsopt = (const objc_clsopt_t*)((uint8_t *)objc_opt + objc_opt->clsopt_offset);
+ const size_t max_class_infos = class_infos_byte_size/sizeof(ClassInfo);
+ DEBUG_PRINTF("max_class_infos = %llu\n", (uint64_t)max_class_infos);
+ ClassInfo *class_infos = (ClassInfo *)class_infos_ptr;
+ int32_t invalidEntryOffset = 0;
+ // this is safe to do because the version field order is invariant
+ if (objc_opt->version == 12)
+ invalidEntryOffset = 16;
+ const uint8_t *checkbytes = &clsopt->tab[clsopt->mask+1];
+ const int32_t *offsets = (const int32_t *)(checkbytes + clsopt->capacity);
+ const objc_classheader_t *classOffsets = (const objc_classheader_t *)(offsets + clsopt->capacity);
+ DEBUG_PRINTF ("clsopt->capacity = %u\n", clsopt->capacity);
+ DEBUG_PRINTF ("clsopt->mask = 0x%8.8x\n", clsopt->mask);
+ DEBUG_PRINTF ("classOffsets = %p\n", classOffsets);
+ DEBUG_PRINTF("invalidEntryOffset = %d\n", invalidEntryOffset);
+ for (uint32_t i=0; i<clsopt->capacity; ++i)
+ {
+ const int32_t clsOffset = classOffsets[i].clsOffset;
+ DEBUG_PRINTF("clsOffset[%u] = %u\n", i, clsOffset);
+ if (clsOffset & 1)
+ {
+ DEBUG_PRINTF("clsOffset & 1\n");
+ continue; // duplicate
+ }
+ else if (clsOffset == invalidEntryOffset)
+ {
+ DEBUG_PRINTF("clsOffset == invalidEntryOffset\n");
+ continue; // invalid offset
+ }
+
+ if (class_infos && idx < max_class_infos)
+ {
+ class_infos[idx].isa = (Class)((uint8_t *)clsopt + clsOffset);
+ const char *name = class_name_lookup_func (class_infos[idx].isa);
+ DEBUG_PRINTF ("[%u] isa = %8p %s\n", idx, class_infos[idx].isa, name);
+ // Hash the class name so we don't have to read it
+ const char *s = name;
+ uint32_t h = 5381;
+ for (unsigned char c = *s; c; c = *++s)
+ {
+ // class_getName demangles swift names and the hash must
+ // be calculated on the mangled name. hash==0 means lldb
+ // will fetch the mangled name and compute the hash in
+ // ParseClassInfoArray.
+ if (c == '.')
+ {
+ h = 0;
+ break;
+ }
+ h = ((h << 5) + h) + c;
+ }
+ class_infos[idx].hash = h;
+ }
+ else
+ {
+ DEBUG_PRINTF("not(class_infos && idx < max_class_infos)\n");
+ }
+ ++idx;
+ }
+
+ const uint32_t *duplicate_count_ptr = (uint32_t *)&classOffsets[clsopt->capacity];
+ const uint32_t duplicate_count = *duplicate_count_ptr;
+ const objc_classheader_t *duplicateClassOffsets = (const objc_classheader_t *)(&duplicate_count_ptr[1]);
+ DEBUG_PRINTF ("duplicate_count = %u\n", duplicate_count);
+ DEBUG_PRINTF ("duplicateClassOffsets = %p\n", duplicateClassOffsets);
+ for (uint32_t i=0; i<duplicate_count; ++i)
+ {
+ const int32_t clsOffset = duplicateClassOffsets[i].clsOffset;
+ if (clsOffset & 1)
+ continue; // duplicate
+ else if (clsOffset == invalidEntryOffset)
+ continue; // invalid offset
+
+ if (class_infos && idx < max_class_infos)
+ {
+ class_infos[idx].isa = (Class)((uint8_t *)clsopt + clsOffset);
+ const char *name = class_name_lookup_func (class_infos[idx].isa);
+ DEBUG_PRINTF ("[%u] isa = %8p %s\n", idx, class_infos[idx].isa, name);
+ // Hash the class name so we don't have to read it
+ const char *s = name;
+ uint32_t h = 5381;
+ for (unsigned char c = *s; c; c = *++s)
+ {
+ // class_getName demangles swift names and the hash must
+ // be calculated on the mangled name. hash==0 means lldb
+ // will fetch the mangled name and compute the hash in
+ // ParseClassInfoArray.
+ if (c == '.')
+ {
+ h = 0;
+ break;
+ }
+ h = ((h << 5) + h) + c;
+ }
+ class_infos[idx].hash = h;
+ }
+ ++idx;
+ }
+ }
+ DEBUG_PRINTF ("%u class_infos\n", idx);
+ DEBUG_PRINTF ("done\n");
+ }
+ return idx;
+}
+
+
+)";
+
+static uint64_t
+ExtractRuntimeGlobalSymbol(Process *process, ConstString name,
+ const ModuleSP &module_sp, Status &error,
+ bool read_value = true, uint8_t byte_size = 0,
+ uint64_t default_value = LLDB_INVALID_ADDRESS,
+ SymbolType sym_type = lldb::eSymbolTypeData) {
+ if (!process) {
+ error.SetErrorString("no process");
+ return default_value;
+ }
+ if (!module_sp) {
+ error.SetErrorString("no module");
+ return default_value;
+ }
+ if (!byte_size)
+ byte_size = process->GetAddressByteSize();
+ const Symbol *symbol =
+ module_sp->FindFirstSymbolWithNameAndType(name, lldb::eSymbolTypeData);
+ if (symbol && symbol->ValueIsAddress()) {
+ lldb::addr_t symbol_load_addr =
+ symbol->GetAddressRef().GetLoadAddress(&process->GetTarget());
+ if (symbol_load_addr != LLDB_INVALID_ADDRESS) {
+ if (read_value)
+ return process->ReadUnsignedIntegerFromMemory(
+ symbol_load_addr, byte_size, default_value, error);
+ else
+ return symbol_load_addr;
+ } else {
+ error.SetErrorString("symbol address invalid");
+ return default_value;
+ }
+ } else {
+ error.SetErrorString("no symbol");
+ return default_value;
+ }
+}
+
+static void RegisterObjCExceptionRecognizer();
+
+AppleObjCRuntimeV2::AppleObjCRuntimeV2(Process *process,
+ const ModuleSP &objc_module_sp)
+ : AppleObjCRuntime(process), m_get_class_info_code(),
+ m_get_class_info_args(LLDB_INVALID_ADDRESS),
+ m_get_class_info_args_mutex(), m_get_shared_cache_class_info_code(),
+ m_get_shared_cache_class_info_args(LLDB_INVALID_ADDRESS),
+ m_get_shared_cache_class_info_args_mutex(), m_decl_vendor_up(),
+ m_tagged_pointer_obfuscator(LLDB_INVALID_ADDRESS),
+ m_isa_hash_table_ptr(LLDB_INVALID_ADDRESS), m_hash_signature(),
+ m_has_object_getClass(false), m_loaded_objc_opt(false),
+ m_non_pointer_isa_cache_up(
+ NonPointerISACache::CreateInstance(*this, objc_module_sp)),
+ m_tagged_pointer_vendor_up(
+ TaggedPointerVendorV2::CreateInstance(*this, objc_module_sp)),
+ m_encoding_to_type_sp(), m_noclasses_warning_emitted(false),
+ m_CFBoolean_values() {
+ static const ConstString g_gdb_object_getClass("gdb_object_getClass");
+ m_has_object_getClass =
+ (objc_module_sp->FindFirstSymbolWithNameAndType(
+ g_gdb_object_getClass, eSymbolTypeCode) != nullptr);
+ RegisterObjCExceptionRecognizer();
+}
+
+bool AppleObjCRuntimeV2::GetDynamicTypeAndAddress(
+ ValueObject &in_value, lldb::DynamicValueType use_dynamic,
+ TypeAndOrName &class_type_or_name, Address &address,
+ Value::ValueType &value_type) {
+ // We should never get here with a null process...
+ assert(m_process != nullptr);
+
+ // The Runtime is attached to a particular process, you shouldn't pass in a
+ // value from another process. Note, however, the process might be NULL (e.g.
+ // if the value was made with SBTarget::EvaluateExpression...) in which case
+ // it is sufficient if the target's match:
+
+ Process *process = in_value.GetProcessSP().get();
+ if (process)
+ assert(process == m_process);
+ else
+ assert(in_value.GetTargetSP().get() == m_process->CalculateTarget().get());
+
+ class_type_or_name.Clear();
+ value_type = Value::ValueType::eValueTypeScalar;
+
+ // Make sure we can have a dynamic value before starting...
+ if (CouldHaveDynamicValue(in_value)) {
+ // First job, pull out the address at 0 offset from the object That will
+ // be the ISA pointer.
+ ClassDescriptorSP objc_class_sp(GetNonKVOClassDescriptor(in_value));
+ if (objc_class_sp) {
+ const addr_t object_ptr = in_value.GetPointerValue();
+ address.SetRawAddress(object_ptr);
+
+ ConstString class_name(objc_class_sp->GetClassName());
+ class_type_or_name.SetName(class_name);
+ TypeSP type_sp(objc_class_sp->GetType());
+ if (type_sp)
+ class_type_or_name.SetTypeSP(type_sp);
+ else {
+ type_sp = LookupInCompleteClassCache(class_name);
+ if (type_sp) {
+ objc_class_sp->SetType(type_sp);
+ class_type_or_name.SetTypeSP(type_sp);
+ } else {
+ // try to go for a CompilerType at least
+ if (auto *vendor = GetDeclVendor()) {
+ auto types = vendor->FindTypes(class_name, /*max_matches*/ 1);
+ if (!types.empty())
+ class_type_or_name.SetCompilerType(types.front());
+ }
+ }
+ }
+ }
+ }
+ return !class_type_or_name.IsEmpty();
+}
+
+// Static Functions
+LanguageRuntime *AppleObjCRuntimeV2::CreateInstance(Process *process,
+ LanguageType language) {
+ // FIXME: This should be a MacOS or iOS process, and we need to look for the
+ // OBJC section to make
+ // sure we aren't using the V1 runtime.
+ if (language == eLanguageTypeObjC) {
+ ModuleSP objc_module_sp;
+
+ if (AppleObjCRuntime::GetObjCVersion(process, objc_module_sp) ==
+ ObjCRuntimeVersions::eAppleObjC_V2)
+ return new AppleObjCRuntimeV2(process, objc_module_sp);
+ else
+ return nullptr;
+ } else
+ return nullptr;
+}
+
+static constexpr OptionDefinition g_objc_classtable_dump_options[] = {
+ {LLDB_OPT_SET_ALL, false, "verbose", 'v', OptionParser::eNoArgument,
+ nullptr, {}, 0, eArgTypeNone,
+ "Print ivar and method information in detail"}};
+
+class CommandObjectObjC_ClassTable_Dump : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options(), m_verbose(false, false) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+ switch (short_option) {
+ case 'v':
+ m_verbose.SetCurrentValue(true);
+ m_verbose.SetOptionWasSet();
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized short option '%c'",
+ short_option);
+ break;
+ }
+
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_verbose.Clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_objc_classtable_dump_options);
+ }
+
+ OptionValueBoolean m_verbose;
+ };
+
+ CommandObjectObjC_ClassTable_Dump(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "dump", "Dump information on Objective-C classes "
+ "known to the current process.",
+ "language objc class-table dump",
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused),
+ m_options() {
+ CommandArgumentEntry arg;
+ CommandArgumentData index_arg;
+
+ // Define the first (and only) variant of this arg.
+ index_arg.arg_type = eArgTypeRegularExpression;
+ index_arg.arg_repetition = eArgRepeatOptional;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(index_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectObjC_ClassTable_Dump() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ std::unique_ptr<RegularExpression> regex_up;
+ switch (command.GetArgumentCount()) {
+ case 0:
+ break;
+ case 1: {
+ regex_up.reset(new RegularExpression());
+ if (!regex_up->Compile(llvm::StringRef::withNullAsEmpty(
+ command.GetArgumentAtIndex(0)))) {
+ result.AppendError(
+ "invalid argument - please provide a valid regular expression");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+ break;
+ }
+ default: {
+ result.AppendError("please provide 0 or 1 arguments");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ Process *process = m_exe_ctx.GetProcessPtr();
+ ObjCLanguageRuntime *objc_runtime = ObjCLanguageRuntime::Get(*process);
+ if (objc_runtime) {
+ auto iterators_pair = objc_runtime->GetDescriptorIteratorPair();
+ auto iterator = iterators_pair.first;
+ auto &std_out = result.GetOutputStream();
+ for (; iterator != iterators_pair.second; iterator++) {
+ if (iterator->second) {
+ const char *class_name =
+ iterator->second->GetClassName().AsCString("<unknown>");
+ if (regex_up && class_name &&
+ !regex_up->Execute(llvm::StringRef(class_name)))
+ continue;
+ std_out.Printf("isa = 0x%" PRIx64, iterator->first);
+ std_out.Printf(" name = %s", class_name);
+ std_out.Printf(" instance size = %" PRIu64,
+ iterator->second->GetInstanceSize());
+ std_out.Printf(" num ivars = %" PRIuPTR,
+ (uintptr_t)iterator->second->GetNumIVars());
+ if (auto superclass = iterator->second->GetSuperclass()) {
+ std_out.Printf(" superclass = %s",
+ superclass->GetClassName().AsCString("<unknown>"));
+ }
+ std_out.Printf("\n");
+ if (m_options.m_verbose) {
+ for (size_t i = 0; i < iterator->second->GetNumIVars(); i++) {
+ auto ivar = iterator->second->GetIVarAtIndex(i);
+ std_out.Printf(
+ " ivar name = %s type = %s size = %" PRIu64
+ " offset = %" PRId32 "\n",
+ ivar.m_name.AsCString("<unknown>"),
+ ivar.m_type.GetDisplayTypeName().AsCString("<unknown>"),
+ ivar.m_size, ivar.m_offset);
+ }
+ iterator->second->Describe(
+ nullptr,
+ [&std_out](const char *name, const char *type) -> bool {
+ std_out.Printf(" instance method name = %s type = %s\n",
+ name, type);
+ return false;
+ },
+ [&std_out](const char *name, const char *type) -> bool {
+ std_out.Printf(" class method name = %s type = %s\n", name,
+ type);
+ return false;
+ },
+ nullptr);
+ }
+ } else {
+ if (regex_up && !regex_up->Execute(llvm::StringRef()))
+ continue;
+ std_out.Printf("isa = 0x%" PRIx64 " has no associated class.\n",
+ iterator->first);
+ }
+ }
+ result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
+ return true;
+ } else {
+ result.AppendError("current process has no Objective-C runtime loaded");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ CommandOptions m_options;
+};
+
+class CommandObjectMultiwordObjC_TaggedPointer_Info
+ : public CommandObjectParsed {
+public:
+ CommandObjectMultiwordObjC_TaggedPointer_Info(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "info", "Dump information on a tagged pointer.",
+ "language objc tagged-pointer info",
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused) {
+ CommandArgumentEntry arg;
+ CommandArgumentData index_arg;
+
+ // Define the first (and only) variant of this arg.
+ index_arg.arg_type = eArgTypeAddress;
+ index_arg.arg_repetition = eArgRepeatPlus;
+
+ // There is only one variant this argument could be; put it into the
+ // argument entry.
+ arg.push_back(index_arg);
+
+ // Push the data for the first argument into the m_arguments vector.
+ m_arguments.push_back(arg);
+ }
+
+ ~CommandObjectMultiwordObjC_TaggedPointer_Info() override = default;
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ if (command.GetArgumentCount() == 0) {
+ result.AppendError("this command requires arguments");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+
+ Process *process = m_exe_ctx.GetProcessPtr();
+ ExecutionContext exe_ctx(process);
+ ObjCLanguageRuntime *objc_runtime = ObjCLanguageRuntime::Get(*process);
+ if (objc_runtime) {
+ ObjCLanguageRuntime::TaggedPointerVendor *tagged_ptr_vendor =
+ objc_runtime->GetTaggedPointerVendor();
+ if (tagged_ptr_vendor) {
+ for (size_t i = 0; i < command.GetArgumentCount(); i++) {
+ const char *arg_str = command.GetArgumentAtIndex(i);
+ if (!arg_str)
+ continue;
+ Status error;
+ lldb::addr_t arg_addr = OptionArgParser::ToAddress(
+ &exe_ctx, arg_str, LLDB_INVALID_ADDRESS, &error);
+ if (arg_addr == 0 || arg_addr == LLDB_INVALID_ADDRESS || error.Fail())
+ continue;
+ auto descriptor_sp = tagged_ptr_vendor->GetClassDescriptor(arg_addr);
+ if (!descriptor_sp)
+ continue;
+ uint64_t info_bits = 0;
+ uint64_t value_bits = 0;
+ uint64_t payload = 0;
+ if (descriptor_sp->GetTaggedPointerInfo(&info_bits, &value_bits,
+ &payload)) {
+ result.GetOutputStream().Printf(
+ "0x%" PRIx64 " is tagged.\n\tpayload = 0x%" PRIx64
+ "\n\tvalue = 0x%" PRIx64 "\n\tinfo bits = 0x%" PRIx64
+ "\n\tclass = %s\n",
+ (uint64_t)arg_addr, payload, value_bits, info_bits,
+ descriptor_sp->GetClassName().AsCString("<unknown>"));
+ } else {
+ result.GetOutputStream().Printf("0x%" PRIx64 " is not tagged.\n",
+ (uint64_t)arg_addr);
+ }
+ }
+ } else {
+ result.AppendError("current process has no tagged pointer support");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+ result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
+ return true;
+ } else {
+ result.AppendError("current process has no Objective-C runtime loaded");
+ result.SetStatus(lldb::eReturnStatusFailed);
+ return false;
+ }
+ }
+};
+
+class CommandObjectMultiwordObjC_ClassTable : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordObjC_ClassTable(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "class-table",
+ "Commands for operating on the Objective-C class table.",
+ "class-table <subcommand> [<subcommand-options>]") {
+ LoadSubCommand(
+ "dump",
+ CommandObjectSP(new CommandObjectObjC_ClassTable_Dump(interpreter)));
+ }
+
+ ~CommandObjectMultiwordObjC_ClassTable() override = default;
+};
+
+class CommandObjectMultiwordObjC_TaggedPointer : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordObjC_TaggedPointer(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "tagged-pointer",
+ "Commands for operating on Objective-C tagged pointers.",
+ "class-table <subcommand> [<subcommand-options>]") {
+ LoadSubCommand(
+ "info",
+ CommandObjectSP(
+ new CommandObjectMultiwordObjC_TaggedPointer_Info(interpreter)));
+ }
+
+ ~CommandObjectMultiwordObjC_TaggedPointer() override = default;
+};
+
+class CommandObjectMultiwordObjC : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordObjC(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "objc",
+ "Commands for operating on the Objective-C language runtime.",
+ "objc <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("class-table",
+ CommandObjectSP(
+ new CommandObjectMultiwordObjC_ClassTable(interpreter)));
+ LoadSubCommand("tagged-pointer",
+ CommandObjectSP(new CommandObjectMultiwordObjC_TaggedPointer(
+ interpreter)));
+ }
+
+ ~CommandObjectMultiwordObjC() override = default;
+};
+
+void AppleObjCRuntimeV2::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "Apple Objective-C Language Runtime - Version 2",
+ CreateInstance,
+ [](CommandInterpreter &interpreter) -> lldb::CommandObjectSP {
+ return CommandObjectSP(new CommandObjectMultiwordObjC(interpreter));
+ },
+ GetBreakpointExceptionPrecondition);
+}
+
+void AppleObjCRuntimeV2::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString AppleObjCRuntimeV2::GetPluginNameStatic() {
+ static ConstString g_name("apple-objc-v2");
+ return g_name;
+}
+
+// PluginInterface protocol
+lldb_private::ConstString AppleObjCRuntimeV2::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t AppleObjCRuntimeV2::GetPluginVersion() { return 1; }
+
+BreakpointResolverSP
+AppleObjCRuntimeV2::CreateExceptionResolver(Breakpoint *bkpt, bool catch_bp,
+ bool throw_bp) {
+ BreakpointResolverSP resolver_sp;
+
+ if (throw_bp)
+ resolver_sp = std::make_shared<BreakpointResolverName>(
+ bkpt, std::get<1>(GetExceptionThrowLocation()).AsCString(),
+ eFunctionNameTypeBase, eLanguageTypeUnknown, Breakpoint::Exact, 0,
+ eLazyBoolNo);
+ // FIXME: We don't do catch breakpoints for ObjC yet.
+ // Should there be some way for the runtime to specify what it can do in this
+ // regard?
+ return resolver_sp;
+}
+
+UtilityFunction *AppleObjCRuntimeV2::CreateObjectChecker(const char *name) {
+ char check_function_code[2048];
+
+ int len = 0;
+ if (m_has_object_getClass) {
+ len = ::snprintf(check_function_code, sizeof(check_function_code), R"(
+ extern "C" void *gdb_object_getClass(void *);
+ extern "C" int printf(const char *format, ...);
+ extern "C" void
+ %s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) {
+ if ($__lldb_arg_obj == (void *)0)
+ return; // nil is ok
+ if (!gdb_object_getClass($__lldb_arg_obj)) {
+ *((volatile int *)0) = 'ocgc';
+ } else if ($__lldb_arg_selector != (void *)0) {
+ signed char $responds = (signed char)
+ [(id)$__lldb_arg_obj respondsToSelector:
+ (void *) $__lldb_arg_selector];
+ if ($responds == (signed char) 0)
+ *((volatile int *)0) = 'ocgc';
+ }
+ })", name);
+ } else {
+ len = ::snprintf(check_function_code, sizeof(check_function_code), R"(
+ extern "C" void *gdb_class_getClass(void *);
+ extern "C" int printf(const char *format, ...);
+ extern "C" void
+ %s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) {
+ if ($__lldb_arg_obj == (void *)0)
+ return; // nil is ok
+ void **$isa_ptr = (void **)$__lldb_arg_obj;
+ if (*$isa_ptr == (void *)0 ||
+ !gdb_class_getClass(*$isa_ptr))
+ *((volatile int *)0) = 'ocgc';
+ else if ($__lldb_arg_selector != (void *)0) {
+ signed char $responds = (signed char)
+ [(id)$__lldb_arg_obj respondsToSelector:
+ (void *) $__lldb_arg_selector];
+ if ($responds == (signed char) 0)
+ *((volatile int *)0) = 'ocgc';
+ }
+ })", name);
+ }
+
+ assert(len < (int)sizeof(check_function_code));
+ UNUSED_IF_ASSERT_DISABLED(len);
+
+ Status error;
+ return GetTargetRef().GetUtilityFunctionForLanguage(
+ check_function_code, eLanguageTypeObjC, name, error);
+}
+
+size_t AppleObjCRuntimeV2::GetByteOffsetForIvar(CompilerType &parent_ast_type,
+ const char *ivar_name) {
+ uint32_t ivar_offset = LLDB_INVALID_IVAR_OFFSET;
+
+ const char *class_name = parent_ast_type.GetConstTypeName().AsCString();
+ if (class_name && class_name[0] && ivar_name && ivar_name[0]) {
+ // Make the objective C V2 mangled name for the ivar offset from the class
+ // name and ivar name
+ std::string buffer("OBJC_IVAR_$_");
+ buffer.append(class_name);
+ buffer.push_back('.');
+ buffer.append(ivar_name);
+ ConstString ivar_const_str(buffer.c_str());
+
+ // Try to get the ivar offset address from the symbol table first using the
+ // name we created above
+ SymbolContextList sc_list;
+ Target &target = m_process->GetTarget();
+ target.GetImages().FindSymbolsWithNameAndType(ivar_const_str,
+ eSymbolTypeObjCIVar, sc_list);
+
+ addr_t ivar_offset_address = LLDB_INVALID_ADDRESS;
+
+ Status error;
+ SymbolContext ivar_offset_symbol;
+ if (sc_list.GetSize() == 1 &&
+ sc_list.GetContextAtIndex(0, ivar_offset_symbol)) {
+ if (ivar_offset_symbol.symbol)
+ ivar_offset_address =
+ ivar_offset_symbol.symbol->GetLoadAddress(&target);
+ }
+
+ // If we didn't get the ivar offset address from the symbol table, fall
+ // back to getting it from the runtime
+ if (ivar_offset_address == LLDB_INVALID_ADDRESS)
+ ivar_offset_address = LookupRuntimeSymbol(ivar_const_str);
+
+ if (ivar_offset_address != LLDB_INVALID_ADDRESS)
+ ivar_offset = m_process->ReadUnsignedIntegerFromMemory(
+ ivar_offset_address, 4, LLDB_INVALID_IVAR_OFFSET, error);
+ }
+ return ivar_offset;
+}
+
+// tagged pointers are special not-a-real-pointer values that contain both type
+// and value information this routine attempts to check with as little
+// computational effort as possible whether something could possibly be a
+// tagged pointer - false positives are possible but false negatives shouldn't
+bool AppleObjCRuntimeV2::IsTaggedPointer(addr_t ptr) {
+ if (!m_tagged_pointer_vendor_up)
+ return false;
+ return m_tagged_pointer_vendor_up->IsPossibleTaggedPointer(ptr);
+}
+
+class RemoteNXMapTable {
+public:
+ RemoteNXMapTable()
+ : m_count(0), m_num_buckets_minus_one(0),
+ m_buckets_ptr(LLDB_INVALID_ADDRESS), m_process(nullptr),
+ m_end_iterator(*this, -1), m_load_addr(LLDB_INVALID_ADDRESS),
+ m_map_pair_size(0), m_invalid_key(0) {}
+
+ void Dump() {
+ printf("RemoteNXMapTable.m_load_addr = 0x%" PRIx64 "\n", m_load_addr);
+ printf("RemoteNXMapTable.m_count = %u\n", m_count);
+ printf("RemoteNXMapTable.m_num_buckets_minus_one = %u\n",
+ m_num_buckets_minus_one);
+ printf("RemoteNXMapTable.m_buckets_ptr = 0x%" PRIX64 "\n", m_buckets_ptr);
+ }
+
+ bool ParseHeader(Process *process, lldb::addr_t load_addr) {
+ m_process = process;
+ m_load_addr = load_addr;
+ m_map_pair_size = m_process->GetAddressByteSize() * 2;
+ m_invalid_key =
+ m_process->GetAddressByteSize() == 8 ? UINT64_MAX : UINT32_MAX;
+ Status err;
+
+ // This currently holds true for all platforms we support, but we might
+ // need to change this to use get the actually byte size of "unsigned" from
+ // the target AST...
+ const uint32_t unsigned_byte_size = sizeof(uint32_t);
+ // Skip the prototype as we don't need it (const struct
+ // +NXMapTablePrototype *prototype)
+
+ bool success = true;
+ if (load_addr == LLDB_INVALID_ADDRESS)
+ success = false;
+ else {
+ lldb::addr_t cursor = load_addr + m_process->GetAddressByteSize();
+
+ // unsigned count;
+ m_count = m_process->ReadUnsignedIntegerFromMemory(
+ cursor, unsigned_byte_size, 0, err);
+ if (m_count) {
+ cursor += unsigned_byte_size;
+
+ // unsigned nbBucketsMinusOne;
+ m_num_buckets_minus_one = m_process->ReadUnsignedIntegerFromMemory(
+ cursor, unsigned_byte_size, 0, err);
+ cursor += unsigned_byte_size;
+
+ // void *buckets;
+ m_buckets_ptr = m_process->ReadPointerFromMemory(cursor, err);
+
+ success = m_count > 0 && m_buckets_ptr != LLDB_INVALID_ADDRESS;
+ }
+ }
+
+ if (!success) {
+ m_count = 0;
+ m_num_buckets_minus_one = 0;
+ m_buckets_ptr = LLDB_INVALID_ADDRESS;
+ }
+ return success;
+ }
+
+ // const_iterator mimics NXMapState and its code comes from NXInitMapState
+ // and NXNextMapState.
+ typedef std::pair<ConstString, ObjCLanguageRuntime::ObjCISA> element;
+
+ friend class const_iterator;
+ class const_iterator {
+ public:
+ const_iterator(RemoteNXMapTable &parent, int index)
+ : m_parent(parent), m_index(index) {
+ AdvanceToValidIndex();
+ }
+
+ const_iterator(const const_iterator &rhs)
+ : m_parent(rhs.m_parent), m_index(rhs.m_index) {
+ // AdvanceToValidIndex() has been called by rhs already.
+ }
+
+ const_iterator &operator=(const const_iterator &rhs) {
+ // AdvanceToValidIndex() has been called by rhs already.
+ assert(&m_parent == &rhs.m_parent);
+ m_index = rhs.m_index;
+ return *this;
+ }
+
+ bool operator==(const const_iterator &rhs) const {
+ if (&m_parent != &rhs.m_parent)
+ return false;
+ if (m_index != rhs.m_index)
+ return false;
+
+ return true;
+ }
+
+ bool operator!=(const const_iterator &rhs) const {
+ return !(operator==(rhs));
+ }
+
+ const_iterator &operator++() {
+ AdvanceToValidIndex();
+ return *this;
+ }
+
+ const element operator*() const {
+ if (m_index == -1) {
+ // TODO find a way to make this an error, but not an assert
+ return element();
+ }
+
+ lldb::addr_t pairs_ptr = m_parent.m_buckets_ptr;
+ size_t map_pair_size = m_parent.m_map_pair_size;
+ lldb::addr_t pair_ptr = pairs_ptr + (m_index * map_pair_size);
+
+ Status err;
+
+ lldb::addr_t key =
+ m_parent.m_process->ReadPointerFromMemory(pair_ptr, err);
+ if (!err.Success())
+ return element();
+ lldb::addr_t value = m_parent.m_process->ReadPointerFromMemory(
+ pair_ptr + m_parent.m_process->GetAddressByteSize(), err);
+ if (!err.Success())
+ return element();
+
+ std::string key_string;
+
+ m_parent.m_process->ReadCStringFromMemory(key, key_string, err);
+ if (!err.Success())
+ return element();
+
+ return element(ConstString(key_string.c_str()),
+ (ObjCLanguageRuntime::ObjCISA)value);
+ }
+
+ private:
+ void AdvanceToValidIndex() {
+ if (m_index == -1)
+ return;
+
+ const lldb::addr_t pairs_ptr = m_parent.m_buckets_ptr;
+ const size_t map_pair_size = m_parent.m_map_pair_size;
+ const lldb::addr_t invalid_key = m_parent.m_invalid_key;
+ Status err;
+
+ while (m_index--) {
+ lldb::addr_t pair_ptr = pairs_ptr + (m_index * map_pair_size);
+ lldb::addr_t key =
+ m_parent.m_process->ReadPointerFromMemory(pair_ptr, err);
+
+ if (!err.Success()) {
+ m_index = -1;
+ return;
+ }
+
+ if (key != invalid_key)
+ return;
+ }
+ }
+ RemoteNXMapTable &m_parent;
+ int m_index;
+ };
+
+ const_iterator begin() {
+ return const_iterator(*this, m_num_buckets_minus_one + 1);
+ }
+
+ const_iterator end() { return m_end_iterator; }
+
+ uint32_t GetCount() const { return m_count; }
+
+ uint32_t GetBucketCount() const { return m_num_buckets_minus_one; }
+
+ lldb::addr_t GetBucketDataPointer() const { return m_buckets_ptr; }
+
+ lldb::addr_t GetTableLoadAddress() const { return m_load_addr; }
+
+private:
+ // contents of _NXMapTable struct
+ uint32_t m_count;
+ uint32_t m_num_buckets_minus_one;
+ lldb::addr_t m_buckets_ptr;
+ lldb_private::Process *m_process;
+ const_iterator m_end_iterator;
+ lldb::addr_t m_load_addr;
+ size_t m_map_pair_size;
+ lldb::addr_t m_invalid_key;
+};
+
+AppleObjCRuntimeV2::HashTableSignature::HashTableSignature()
+ : m_count(0), m_num_buckets(0), m_buckets_ptr(0) {}
+
+void AppleObjCRuntimeV2::HashTableSignature::UpdateSignature(
+ const RemoteNXMapTable &hash_table) {
+ m_count = hash_table.GetCount();
+ m_num_buckets = hash_table.GetBucketCount();
+ m_buckets_ptr = hash_table.GetBucketDataPointer();
+}
+
+bool AppleObjCRuntimeV2::HashTableSignature::NeedsUpdate(
+ Process *process, AppleObjCRuntimeV2 *runtime,
+ RemoteNXMapTable &hash_table) {
+ if (!hash_table.ParseHeader(process, runtime->GetISAHashTablePointer())) {
+ return false; // Failed to parse the header, no need to update anything
+ }
+
+ // Check with out current signature and return true if the count, number of
+ // buckets or the hash table address changes.
+ if (m_count == hash_table.GetCount() &&
+ m_num_buckets == hash_table.GetBucketCount() &&
+ m_buckets_ptr == hash_table.GetBucketDataPointer()) {
+ // Hash table hasn't changed
+ return false;
+ }
+ // Hash table data has changed, we need to update
+ return true;
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+AppleObjCRuntimeV2::GetClassDescriptorFromISA(ObjCISA isa) {
+ ObjCLanguageRuntime::ClassDescriptorSP class_descriptor_sp;
+ if (m_non_pointer_isa_cache_up)
+ class_descriptor_sp = m_non_pointer_isa_cache_up->GetClassDescriptor(isa);
+ if (!class_descriptor_sp)
+ class_descriptor_sp = ObjCLanguageRuntime::GetClassDescriptorFromISA(isa);
+ return class_descriptor_sp;
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+AppleObjCRuntimeV2::GetClassDescriptor(ValueObject &valobj) {
+ ClassDescriptorSP objc_class_sp;
+ if (valobj.IsBaseClass()) {
+ ValueObject *parent = valobj.GetParent();
+ // if I am my own parent, bail out of here fast..
+ if (parent && parent != &valobj) {
+ ClassDescriptorSP parent_descriptor_sp = GetClassDescriptor(*parent);
+ if (parent_descriptor_sp)
+ return parent_descriptor_sp->GetSuperclass();
+ }
+ return nullptr;
+ }
+ // if we get an invalid VO (which might still happen when playing around with
+ // pointers returned by the expression parser, don't consider this a valid
+ // ObjC object)
+ if (valobj.GetCompilerType().IsValid()) {
+ addr_t isa_pointer = valobj.GetPointerValue();
+
+ // tagged pointer
+ if (IsTaggedPointer(isa_pointer)) {
+ return m_tagged_pointer_vendor_up->GetClassDescriptor(isa_pointer);
+ } else {
+ ExecutionContext exe_ctx(valobj.GetExecutionContextRef());
+
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process) {
+ Status error;
+ ObjCISA isa = process->ReadPointerFromMemory(isa_pointer, error);
+ if (isa != LLDB_INVALID_ADDRESS) {
+ objc_class_sp = GetClassDescriptorFromISA(isa);
+ if (isa && !objc_class_sp) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("0x%" PRIx64
+ ": AppleObjCRuntimeV2::GetClassDescriptor() ISA was "
+ "not in class descriptor cache 0x%" PRIx64,
+ isa_pointer, isa);
+ }
+ }
+ }
+ }
+ }
+ return objc_class_sp;
+}
+
+lldb::addr_t AppleObjCRuntimeV2::GetTaggedPointerObfuscator() {
+ if (m_tagged_pointer_obfuscator != LLDB_INVALID_ADDRESS)
+ return m_tagged_pointer_obfuscator;
+
+
+ Process *process = GetProcess();
+ ModuleSP objc_module_sp(GetObjCModule());
+
+ if (!objc_module_sp)
+ return LLDB_INVALID_ADDRESS;
+
+ static ConstString g_gdb_objc_obfuscator("objc_debug_taggedpointer_obfuscator");
+
+ const Symbol *symbol = objc_module_sp->FindFirstSymbolWithNameAndType(
+ g_gdb_objc_obfuscator, lldb::eSymbolTypeAny);
+ if (symbol) {
+ lldb::addr_t g_gdb_obj_obfuscator_ptr =
+ symbol->GetLoadAddress(&process->GetTarget());
+
+ if (g_gdb_obj_obfuscator_ptr != LLDB_INVALID_ADDRESS) {
+ Status error;
+ m_tagged_pointer_obfuscator = process->ReadPointerFromMemory(
+ g_gdb_obj_obfuscator_ptr, error);
+ }
+ }
+ // If we don't have a correct value at this point, there must be no obfuscation.
+ if (m_tagged_pointer_obfuscator == LLDB_INVALID_ADDRESS)
+ m_tagged_pointer_obfuscator = 0;
+
+ return m_tagged_pointer_obfuscator;
+}
+
+lldb::addr_t AppleObjCRuntimeV2::GetISAHashTablePointer() {
+ if (m_isa_hash_table_ptr == LLDB_INVALID_ADDRESS) {
+ Process *process = GetProcess();
+
+ ModuleSP objc_module_sp(GetObjCModule());
+
+ if (!objc_module_sp)
+ return LLDB_INVALID_ADDRESS;
+
+ static ConstString g_gdb_objc_realized_classes("gdb_objc_realized_classes");
+
+ const Symbol *symbol = objc_module_sp->FindFirstSymbolWithNameAndType(
+ g_gdb_objc_realized_classes, lldb::eSymbolTypeAny);
+ if (symbol) {
+ lldb::addr_t gdb_objc_realized_classes_ptr =
+ symbol->GetLoadAddress(&process->GetTarget());
+
+ if (gdb_objc_realized_classes_ptr != LLDB_INVALID_ADDRESS) {
+ Status error;
+ m_isa_hash_table_ptr = process->ReadPointerFromMemory(
+ gdb_objc_realized_classes_ptr, error);
+ }
+ }
+ }
+ return m_isa_hash_table_ptr;
+}
+
+AppleObjCRuntimeV2::DescriptorMapUpdateResult
+AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(
+ RemoteNXMapTable &hash_table) {
+ Process *process = GetProcess();
+
+ if (process == nullptr)
+ return DescriptorMapUpdateResult::Fail();
+
+ uint32_t num_class_infos = 0;
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_TYPES));
+
+ ExecutionContext exe_ctx;
+
+ ThreadSP thread_sp = process->GetThreadList().GetExpressionExecutionThread();
+
+ if (!thread_sp)
+ return DescriptorMapUpdateResult::Fail();
+
+ thread_sp->CalculateExecutionContext(exe_ctx);
+ ClangASTContext *ast = process->GetTarget().GetScratchClangASTContext();
+
+ if (!ast)
+ return DescriptorMapUpdateResult::Fail();
+
+ Address function_address;
+
+ DiagnosticManager diagnostics;
+
+ const uint32_t addr_size = process->GetAddressByteSize();
+
+ Status err;
+
+ // Read the total number of classes from the hash table
+ const uint32_t num_classes = hash_table.GetCount();
+ if (num_classes == 0) {
+ if (log)
+ log->Printf("No dynamic classes found in gdb_objc_realized_classes.");
+ return DescriptorMapUpdateResult::Success(0);
+ }
+
+ // Make some types for our arguments
+ CompilerType clang_uint32_t_type =
+ ast->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32);
+ CompilerType clang_void_pointer_type =
+ ast->GetBasicType(eBasicTypeVoid).GetPointerType();
+
+ ValueList arguments;
+ FunctionCaller *get_class_info_function = nullptr;
+
+ if (!m_get_class_info_code) {
+ Status error;
+ m_get_class_info_code.reset(GetTargetRef().GetUtilityFunctionForLanguage(
+ g_get_dynamic_class_info_body, eLanguageTypeObjC,
+ g_get_dynamic_class_info_name, error));
+ if (error.Fail()) {
+ if (log)
+ log->Printf(
+ "Failed to get Utility Function for implementation lookup: %s",
+ error.AsCString());
+ m_get_class_info_code.reset();
+ } else {
+ diagnostics.Clear();
+
+ if (!m_get_class_info_code->Install(diagnostics, exe_ctx)) {
+ if (log) {
+ log->Printf("Failed to install implementation lookup");
+ diagnostics.Dump(log);
+ }
+ m_get_class_info_code.reset();
+ }
+ }
+ if (!m_get_class_info_code)
+ return DescriptorMapUpdateResult::Fail();
+
+ // Next make the runner function for our implementation utility function.
+ Value value;
+ value.SetValueType(Value::eValueTypeScalar);
+ value.SetCompilerType(clang_void_pointer_type);
+ arguments.PushValue(value);
+ arguments.PushValue(value);
+
+ value.SetValueType(Value::eValueTypeScalar);
+ value.SetCompilerType(clang_uint32_t_type);
+ arguments.PushValue(value);
+ arguments.PushValue(value);
+
+ get_class_info_function = m_get_class_info_code->MakeFunctionCaller(
+ clang_uint32_t_type, arguments, thread_sp, error);
+
+ if (error.Fail()) {
+ if (log)
+ log->Printf(
+ "Failed to make function caller for implementation lookup: %s.",
+ error.AsCString());
+ return DescriptorMapUpdateResult::Fail();
+ }
+ } else {
+ get_class_info_function = m_get_class_info_code->GetFunctionCaller();
+ if (!get_class_info_function) {
+ if (log) {
+ log->Printf("Failed to get implementation lookup function caller.");
+ diagnostics.Dump(log);
+ }
+
+ return DescriptorMapUpdateResult::Fail();
+ }
+ arguments = get_class_info_function->GetArgumentValues();
+ }
+
+ diagnostics.Clear();
+
+ const uint32_t class_info_byte_size = addr_size + 4;
+ const uint32_t class_infos_byte_size = num_classes * class_info_byte_size;
+ lldb::addr_t class_infos_addr = process->AllocateMemory(
+ class_infos_byte_size, ePermissionsReadable | ePermissionsWritable, err);
+
+ if (class_infos_addr == LLDB_INVALID_ADDRESS) {
+ if (log)
+ log->Printf("unable to allocate %" PRIu32
+ " bytes in process for shared cache read",
+ class_infos_byte_size);
+ return DescriptorMapUpdateResult::Fail();
+ }
+
+ std::lock_guard<std::mutex> guard(m_get_class_info_args_mutex);
+
+ // Fill in our function argument values
+ arguments.GetValueAtIndex(0)->GetScalar() = hash_table.GetTableLoadAddress();
+ arguments.GetValueAtIndex(1)->GetScalar() = class_infos_addr;
+ arguments.GetValueAtIndex(2)->GetScalar() = class_infos_byte_size;
+
+ // Only dump the runtime classes from the expression evaluation if the log is
+ // verbose:
+ Log *type_log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES);
+ bool dump_log = type_log && type_log->GetVerbose();
+
+ arguments.GetValueAtIndex(3)->GetScalar() = dump_log ? 1 : 0;
+
+ bool success = false;
+
+ diagnostics.Clear();
+
+ // Write our function arguments into the process so we can run our function
+ if (get_class_info_function->WriteFunctionArguments(
+ exe_ctx, m_get_class_info_args, arguments, diagnostics)) {
+ EvaluateExpressionOptions options;
+ options.SetUnwindOnError(true);
+ options.SetTryAllThreads(false);
+ options.SetStopOthers(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetTimeout(process->GetUtilityExpressionTimeout());
+ options.SetIsForUtilityExpr(true);
+
+ Value return_value;
+ return_value.SetValueType(Value::eValueTypeScalar);
+ // return_value.SetContext (Value::eContextTypeClangType,
+ // clang_uint32_t_type);
+ return_value.SetCompilerType(clang_uint32_t_type);
+ return_value.GetScalar() = 0;
+
+ diagnostics.Clear();
+
+ // Run the function
+ ExpressionResults results = get_class_info_function->ExecuteFunction(
+ exe_ctx, &m_get_class_info_args, options, diagnostics, return_value);
+
+ if (results == eExpressionCompleted) {
+ // The result is the number of ClassInfo structures that were filled in
+ num_class_infos = return_value.GetScalar().ULong();
+ if (log)
+ log->Printf("Discovered %u ObjC classes\n", num_class_infos);
+ if (num_class_infos > 0) {
+ // Read the ClassInfo structures
+ DataBufferHeap buffer(num_class_infos * class_info_byte_size, 0);
+ if (process->ReadMemory(class_infos_addr, buffer.GetBytes(),
+ buffer.GetByteSize(),
+ err) == buffer.GetByteSize()) {
+ DataExtractor class_infos_data(buffer.GetBytes(),
+ buffer.GetByteSize(),
+ process->GetByteOrder(), addr_size);
+ ParseClassInfoArray(class_infos_data, num_class_infos);
+ }
+ }
+ success = true;
+ } else {
+ if (log) {
+ log->Printf("Error evaluating our find class name function.");
+ diagnostics.Dump(log);
+ }
+ }
+ } else {
+ if (log) {
+ log->Printf("Error writing function arguments.");
+ diagnostics.Dump(log);
+ }
+ }
+
+ // Deallocate the memory we allocated for the ClassInfo array
+ process->DeallocateMemory(class_infos_addr);
+
+ return DescriptorMapUpdateResult(success, num_class_infos);
+}
+
+uint32_t AppleObjCRuntimeV2::ParseClassInfoArray(const DataExtractor &data,
+ uint32_t num_class_infos) {
+ // Parses an array of "num_class_infos" packed ClassInfo structures:
+ //
+ // struct ClassInfo
+ // {
+ // Class isa;
+ // uint32_t hash;
+ // } __attribute__((__packed__));
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES));
+ bool should_log = log && log->GetVerbose();
+
+ uint32_t num_parsed = 0;
+
+ // Iterate through all ClassInfo structures
+ lldb::offset_t offset = 0;
+ for (uint32_t i = 0; i < num_class_infos; ++i) {
+ ObjCISA isa = data.GetPointer(&offset);
+
+ if (isa == 0) {
+ if (should_log)
+ log->Printf(
+ "AppleObjCRuntimeV2 found NULL isa, ignoring this class info");
+ continue;
+ }
+ // Check if we already know about this ISA, if we do, the info will never
+ // change, so we can just skip it.
+ if (ISAIsCached(isa)) {
+ if (should_log)
+ log->Printf("AppleObjCRuntimeV2 found cached isa=0x%" PRIx64
+ ", ignoring this class info",
+ isa);
+ offset += 4;
+ } else {
+ // Read the 32 bit hash for the class name
+ const uint32_t name_hash = data.GetU32(&offset);
+ ClassDescriptorSP descriptor_sp(
+ new ClassDescriptorV2(*this, isa, nullptr));
+
+ // The code in g_get_shared_cache_class_info_body sets the value of the hash
+ // to 0 to signal a demangled symbol. We use class_getName() in that code to
+ // find the class name, but this returns a demangled name for Swift symbols.
+ // For those symbols, recompute the hash here by reading their name from the
+ // runtime.
+ if (name_hash)
+ AddClass(isa, descriptor_sp, name_hash);
+ else
+ AddClass(isa, descriptor_sp, descriptor_sp->GetClassName().AsCString(nullptr));
+ num_parsed++;
+ if (should_log)
+ log->Printf("AppleObjCRuntimeV2 added isa=0x%" PRIx64
+ ", hash=0x%8.8x, name=%s",
+ isa, name_hash,
+ descriptor_sp->GetClassName().AsCString("<unknown>"));
+ }
+ }
+ if (should_log)
+ log->Printf("AppleObjCRuntimeV2 parsed %" PRIu32 " class infos",
+ num_parsed);
+ return num_parsed;
+}
+
+AppleObjCRuntimeV2::DescriptorMapUpdateResult
+AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache() {
+ Process *process = GetProcess();
+
+ if (process == nullptr)
+ return DescriptorMapUpdateResult::Fail();
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_TYPES));
+
+ ExecutionContext exe_ctx;
+
+ ThreadSP thread_sp = process->GetThreadList().GetExpressionExecutionThread();
+
+ if (!thread_sp)
+ return DescriptorMapUpdateResult::Fail();
+
+ thread_sp->CalculateExecutionContext(exe_ctx);
+ ClangASTContext *ast = process->GetTarget().GetScratchClangASTContext();
+
+ if (!ast)
+ return DescriptorMapUpdateResult::Fail();
+
+ Address function_address;
+
+ DiagnosticManager diagnostics;
+
+ const uint32_t addr_size = process->GetAddressByteSize();
+
+ Status err;
+
+ uint32_t num_class_infos = 0;
+
+ const lldb::addr_t objc_opt_ptr = GetSharedCacheReadOnlyAddress();
+
+ if (objc_opt_ptr == LLDB_INVALID_ADDRESS)
+ return DescriptorMapUpdateResult::Fail();
+
+ const uint32_t num_classes = 128 * 1024;
+
+ // Make some types for our arguments
+ CompilerType clang_uint32_t_type =
+ ast->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32);
+ CompilerType clang_void_pointer_type =
+ ast->GetBasicType(eBasicTypeVoid).GetPointerType();
+
+ ValueList arguments;
+ FunctionCaller *get_shared_cache_class_info_function = nullptr;
+
+ if (!m_get_shared_cache_class_info_code) {
+ Status error;
+
+ // If the inferior objc.dylib has the class_getNameRaw function,
+ // use that in our jitted expression. Else fall back to the old
+ // class_getName.
+ static ConstString g_class_getName_symbol_name("class_getName");
+ static ConstString g_class_getNameRaw_symbol_name("class_getNameRaw");
+ ConstString class_name_getter_function_name = g_class_getName_symbol_name;
+
+ ObjCLanguageRuntime *objc_runtime = ObjCLanguageRuntime::Get(*process);
+ if (objc_runtime) {
+ const ModuleList &images = process->GetTarget().GetImages();
+ std::lock_guard<std::recursive_mutex> guard(images.GetMutex());
+ for (size_t i = 0; i < images.GetSize(); ++i) {
+ lldb::ModuleSP mod_sp = images.GetModuleAtIndexUnlocked(i);
+ if (objc_runtime->IsModuleObjCLibrary(mod_sp)) {
+ const Symbol *symbol =
+ mod_sp->FindFirstSymbolWithNameAndType(g_class_getNameRaw_symbol_name,
+ lldb::eSymbolTypeCode);
+ if (symbol &&
+ (symbol->ValueIsAddress() || symbol->GetAddressRef().IsValid())) {
+ class_name_getter_function_name = g_class_getNameRaw_symbol_name;
+ }
+ }
+ }
+ }
+
+ // Substitute in the correct class_getName / class_getNameRaw function name,
+ // concatenate the two parts of our expression text. The format string
+ // has two %s's, so provide the name twice.
+ int prefix_string_size = snprintf (nullptr, 0,
+ g_shared_cache_class_name_funcptr,
+ class_name_getter_function_name.AsCString(),
+ class_name_getter_function_name.AsCString());
+
+ char *class_name_func_ptr_expr = (char*) malloc (prefix_string_size + 1);
+ snprintf (class_name_func_ptr_expr, prefix_string_size + 1,
+ g_shared_cache_class_name_funcptr,
+ class_name_getter_function_name.AsCString(),
+ class_name_getter_function_name.AsCString());
+ std::string shared_class_expression = class_name_func_ptr_expr;
+ shared_class_expression += g_get_shared_cache_class_info_body;
+ free (class_name_func_ptr_expr);
+
+ m_get_shared_cache_class_info_code.reset(
+ GetTargetRef().GetUtilityFunctionForLanguage(
+ shared_class_expression.c_str(), eLanguageTypeObjC,
+ g_get_shared_cache_class_info_name, error));
+ if (error.Fail()) {
+ if (log)
+ log->Printf(
+ "Failed to get Utility function for implementation lookup: %s.",
+ error.AsCString());
+ m_get_shared_cache_class_info_code.reset();
+ } else {
+ diagnostics.Clear();
+
+ if (!m_get_shared_cache_class_info_code->Install(diagnostics, exe_ctx)) {
+ if (log) {
+ log->Printf("Failed to install implementation lookup.");
+ diagnostics.Dump(log);
+ }
+ m_get_shared_cache_class_info_code.reset();
+ }
+ }
+
+ if (!m_get_shared_cache_class_info_code)
+ return DescriptorMapUpdateResult::Fail();
+
+ // Next make the function caller for our implementation utility function.
+ Value value;
+ value.SetValueType(Value::eValueTypeScalar);
+ // value.SetContext (Value::eContextTypeClangType, clang_void_pointer_type);
+ value.SetCompilerType(clang_void_pointer_type);
+ arguments.PushValue(value);
+ arguments.PushValue(value);
+
+ value.SetValueType(Value::eValueTypeScalar);
+ // value.SetContext (Value::eContextTypeClangType, clang_uint32_t_type);
+ value.SetCompilerType(clang_uint32_t_type);
+ arguments.PushValue(value);
+ arguments.PushValue(value);
+
+ get_shared_cache_class_info_function =
+ m_get_shared_cache_class_info_code->MakeFunctionCaller(
+ clang_uint32_t_type, arguments, thread_sp, error);
+
+ if (get_shared_cache_class_info_function == nullptr)
+ return DescriptorMapUpdateResult::Fail();
+
+ } else {
+ get_shared_cache_class_info_function =
+ m_get_shared_cache_class_info_code->GetFunctionCaller();
+ if (get_shared_cache_class_info_function == nullptr)
+ return DescriptorMapUpdateResult::Fail();
+ arguments = get_shared_cache_class_info_function->GetArgumentValues();
+ }
+
+ diagnostics.Clear();
+
+ const uint32_t class_info_byte_size = addr_size + 4;
+ const uint32_t class_infos_byte_size = num_classes * class_info_byte_size;
+ lldb::addr_t class_infos_addr = process->AllocateMemory(
+ class_infos_byte_size, ePermissionsReadable | ePermissionsWritable, err);
+
+ if (class_infos_addr == LLDB_INVALID_ADDRESS) {
+ if (log)
+ log->Printf("unable to allocate %" PRIu32
+ " bytes in process for shared cache read",
+ class_infos_byte_size);
+ return DescriptorMapUpdateResult::Fail();
+ }
+
+ std::lock_guard<std::mutex> guard(m_get_shared_cache_class_info_args_mutex);
+
+ // Fill in our function argument values
+ arguments.GetValueAtIndex(0)->GetScalar() = objc_opt_ptr;
+ arguments.GetValueAtIndex(1)->GetScalar() = class_infos_addr;
+ arguments.GetValueAtIndex(2)->GetScalar() = class_infos_byte_size;
+ // Only dump the runtime classes from the expression evaluation if the log is
+ // verbose:
+ Log *type_log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES);
+ bool dump_log = type_log && type_log->GetVerbose();
+
+ arguments.GetValueAtIndex(3)->GetScalar() = dump_log ? 1 : 0;
+
+ bool success = false;
+
+ diagnostics.Clear();
+
+ // Write our function arguments into the process so we can run our function
+ if (get_shared_cache_class_info_function->WriteFunctionArguments(
+ exe_ctx, m_get_shared_cache_class_info_args, arguments,
+ diagnostics)) {
+ EvaluateExpressionOptions options;
+ options.SetUnwindOnError(true);
+ options.SetTryAllThreads(false);
+ options.SetStopOthers(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetTimeout(process->GetUtilityExpressionTimeout());
+ options.SetIsForUtilityExpr(true);
+
+ Value return_value;
+ return_value.SetValueType(Value::eValueTypeScalar);
+ // return_value.SetContext (Value::eContextTypeClangType,
+ // clang_uint32_t_type);
+ return_value.SetCompilerType(clang_uint32_t_type);
+ return_value.GetScalar() = 0;
+
+ diagnostics.Clear();
+
+ // Run the function
+ ExpressionResults results =
+ get_shared_cache_class_info_function->ExecuteFunction(
+ exe_ctx, &m_get_shared_cache_class_info_args, options, diagnostics,
+ return_value);
+
+ if (results == eExpressionCompleted) {
+ // The result is the number of ClassInfo structures that were filled in
+ num_class_infos = return_value.GetScalar().ULong();
+ if (log)
+ log->Printf("Discovered %u ObjC classes in shared cache\n",
+ num_class_infos);
+ assert(num_class_infos <= num_classes);
+ if (num_class_infos > 0) {
+ if (num_class_infos > num_classes) {
+ num_class_infos = num_classes;
+
+ success = false;
+ } else {
+ success = true;
+ }
+
+ // Read the ClassInfo structures
+ DataBufferHeap buffer(num_class_infos * class_info_byte_size, 0);
+ if (process->ReadMemory(class_infos_addr, buffer.GetBytes(),
+ buffer.GetByteSize(),
+ err) == buffer.GetByteSize()) {
+ DataExtractor class_infos_data(buffer.GetBytes(),
+ buffer.GetByteSize(),
+ process->GetByteOrder(), addr_size);
+
+ ParseClassInfoArray(class_infos_data, num_class_infos);
+ }
+ } else {
+ success = true;
+ }
+ } else {
+ if (log) {
+ log->Printf("Error evaluating our find class name function.");
+ diagnostics.Dump(log);
+ }
+ }
+ } else {
+ if (log) {
+ log->Printf("Error writing function arguments.");
+ diagnostics.Dump(log);
+ }
+ }
+
+ // Deallocate the memory we allocated for the ClassInfo array
+ process->DeallocateMemory(class_infos_addr);
+
+ return DescriptorMapUpdateResult(success, num_class_infos);
+}
+
+bool AppleObjCRuntimeV2::UpdateISAToDescriptorMapFromMemory(
+ RemoteNXMapTable &hash_table) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_TYPES));
+
+ Process *process = GetProcess();
+
+ if (process == nullptr)
+ return false;
+
+ uint32_t num_map_table_isas = 0;
+
+ ModuleSP objc_module_sp(GetObjCModule());
+
+ if (objc_module_sp) {
+ for (RemoteNXMapTable::element elt : hash_table) {
+ ++num_map_table_isas;
+
+ if (ISAIsCached(elt.second))
+ continue;
+
+ ClassDescriptorSP descriptor_sp = ClassDescriptorSP(
+ new ClassDescriptorV2(*this, elt.second, elt.first.AsCString()));
+
+ if (log && log->GetVerbose())
+ log->Printf("AppleObjCRuntimeV2 added (ObjCISA)0x%" PRIx64
+ " (%s) from dynamic table to isa->descriptor cache",
+ elt.second, elt.first.AsCString());
+
+ AddClass(elt.second, descriptor_sp, elt.first.AsCString());
+ }
+ }
+
+ return num_map_table_isas > 0;
+}
+
+lldb::addr_t AppleObjCRuntimeV2::GetSharedCacheReadOnlyAddress() {
+ Process *process = GetProcess();
+
+ if (process) {
+ ModuleSP objc_module_sp(GetObjCModule());
+
+ if (objc_module_sp) {
+ ObjectFile *objc_object = objc_module_sp->GetObjectFile();
+
+ if (objc_object) {
+ SectionList *section_list = objc_module_sp->GetSectionList();
+
+ if (section_list) {
+ SectionSP text_segment_sp(
+ section_list->FindSectionByName(ConstString("__TEXT")));
+
+ if (text_segment_sp) {
+ SectionSP objc_opt_section_sp(
+ text_segment_sp->GetChildren().FindSectionByName(
+ ConstString("__objc_opt_ro")));
+
+ if (objc_opt_section_sp) {
+ return objc_opt_section_sp->GetLoadBaseAddress(
+ &process->GetTarget());
+ }
+ }
+ }
+ }
+ }
+ }
+ return LLDB_INVALID_ADDRESS;
+}
+
+void AppleObjCRuntimeV2::UpdateISAToDescriptorMapIfNeeded() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_TYPES));
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION);
+
+ // Else we need to check with our process to see when the map was updated.
+ Process *process = GetProcess();
+
+ if (process) {
+ RemoteNXMapTable hash_table;
+
+ // Update the process stop ID that indicates the last time we updated the
+ // map, whether it was successful or not.
+ m_isa_to_descriptor_stop_id = process->GetStopID();
+
+ if (!m_hash_signature.NeedsUpdate(process, this, hash_table))
+ return;
+
+ m_hash_signature.UpdateSignature(hash_table);
+
+ // Grab the dynamically loaded objc classes from the hash table in memory
+ DescriptorMapUpdateResult dynamic_update_result =
+ UpdateISAToDescriptorMapDynamic(hash_table);
+
+ // Now get the objc classes that are baked into the Objective-C runtime in
+ // the shared cache, but only once per process as this data never changes
+ if (!m_loaded_objc_opt) {
+ // it is legitimately possible for the shared cache to be empty - in that
+ // case, the dynamic hash table will contain all the class information we
+ // need; the situation we're trying to detect is one where we aren't
+ // seeing class information from the runtime - in order to detect that
+ // vs. just the shared cache being empty or sparsely populated, we set an
+ // arbitrary (very low) threshold for the number of classes that we want
+ // to see in a "good" scenario - anything below that is suspicious
+ // (Foundation alone has thousands of classes)
+ const uint32_t num_classes_to_warn_at = 500;
+
+ DescriptorMapUpdateResult shared_cache_update_result =
+ UpdateISAToDescriptorMapSharedCache();
+
+ if (log)
+ log->Printf("attempted to read objc class data - results: "
+ "[dynamic_update]: ran: %s, count: %" PRIu32
+ " [shared_cache_update]: ran: %s, count: %" PRIu32,
+ dynamic_update_result.m_update_ran ? "yes" : "no",
+ dynamic_update_result.m_num_found,
+ shared_cache_update_result.m_update_ran ? "yes" : "no",
+ shared_cache_update_result.m_num_found);
+
+ // warn if:
+ // - we could not run either expression
+ // - we found fewer than num_classes_to_warn_at classes total
+ if ((!shared_cache_update_result.m_update_ran) ||
+ (!dynamic_update_result.m_update_ran))
+ WarnIfNoClassesCached(
+ SharedCacheWarningReason::eExpressionExecutionFailure);
+ else if (dynamic_update_result.m_num_found +
+ shared_cache_update_result.m_num_found <
+ num_classes_to_warn_at)
+ WarnIfNoClassesCached(SharedCacheWarningReason::eNotEnoughClassesRead);
+ else
+ m_loaded_objc_opt = true;
+ }
+ } else {
+ m_isa_to_descriptor_stop_id = UINT32_MAX;
+ }
+}
+
+static bool DoesProcessHaveSharedCache(Process &process) {
+ PlatformSP platform_sp = process.GetTarget().GetPlatform();
+ if (!platform_sp)
+ return true; // this should not happen
+
+ ConstString platform_plugin_name = platform_sp->GetPluginName();
+ if (platform_plugin_name) {
+ llvm::StringRef platform_plugin_name_sr =
+ platform_plugin_name.GetStringRef();
+ if (platform_plugin_name_sr.endswith("-simulator"))
+ return false;
+ }
+
+ return true;
+}
+
+void AppleObjCRuntimeV2::WarnIfNoClassesCached(
+ SharedCacheWarningReason reason) {
+ if (m_noclasses_warning_emitted)
+ return;
+
+ if (GetProcess() && !DoesProcessHaveSharedCache(*GetProcess())) {
+ // Simulators do not have the objc_opt_ro class table so don't actually
+ // complain to the user
+ m_noclasses_warning_emitted = true;
+ return;
+ }
+
+ Debugger &debugger(GetProcess()->GetTarget().GetDebugger());
+ if (auto stream = debugger.GetAsyncOutputStream()) {
+ switch (reason) {
+ case SharedCacheWarningReason::eNotEnoughClassesRead:
+ stream->PutCString("warning: could not find Objective-C class data in "
+ "the process. This may reduce the quality of type "
+ "information available.\n");
+ m_noclasses_warning_emitted = true;
+ break;
+ case SharedCacheWarningReason::eExpressionExecutionFailure:
+ stream->PutCString("warning: could not execute support code to read "
+ "Objective-C class data in the process. This may "
+ "reduce the quality of type information available.\n");
+ m_noclasses_warning_emitted = true;
+ break;
+ }
+ }
+}
+
+ConstString
+AppleObjCRuntimeV2::GetActualTypeName(ObjCLanguageRuntime::ObjCISA isa) {
+ if (isa == g_objc_Tagged_ISA) {
+ static const ConstString g_objc_tagged_isa_name("_lldb_Tagged_ObjC_ISA");
+ return g_objc_tagged_isa_name;
+ }
+ if (isa == g_objc_Tagged_ISA_NSAtom) {
+ static const ConstString g_objc_tagged_isa_nsatom_name("NSAtom");
+ return g_objc_tagged_isa_nsatom_name;
+ }
+ if (isa == g_objc_Tagged_ISA_NSNumber) {
+ static const ConstString g_objc_tagged_isa_nsnumber_name("NSNumber");
+ return g_objc_tagged_isa_nsnumber_name;
+ }
+ if (isa == g_objc_Tagged_ISA_NSDateTS) {
+ static const ConstString g_objc_tagged_isa_nsdatets_name("NSDateTS");
+ return g_objc_tagged_isa_nsdatets_name;
+ }
+ if (isa == g_objc_Tagged_ISA_NSManagedObject) {
+ static const ConstString g_objc_tagged_isa_nsmanagedobject_name(
+ "NSManagedObject");
+ return g_objc_tagged_isa_nsmanagedobject_name;
+ }
+ if (isa == g_objc_Tagged_ISA_NSDate) {
+ static const ConstString g_objc_tagged_isa_nsdate_name("NSDate");
+ return g_objc_tagged_isa_nsdate_name;
+ }
+ return ObjCLanguageRuntime::GetActualTypeName(isa);
+}
+
+DeclVendor *AppleObjCRuntimeV2::GetDeclVendor() {
+ if (!m_decl_vendor_up)
+ m_decl_vendor_up.reset(new AppleObjCDeclVendor(*this));
+
+ return m_decl_vendor_up.get();
+}
+
+lldb::addr_t AppleObjCRuntimeV2::LookupRuntimeSymbol(ConstString name) {
+ lldb::addr_t ret = LLDB_INVALID_ADDRESS;
+
+ const char *name_cstr = name.AsCString();
+
+ if (name_cstr) {
+ llvm::StringRef name_strref(name_cstr);
+
+ static const llvm::StringRef ivar_prefix("OBJC_IVAR_$_");
+ static const llvm::StringRef class_prefix("OBJC_CLASS_$_");
+
+ if (name_strref.startswith(ivar_prefix)) {
+ llvm::StringRef ivar_skipped_prefix =
+ name_strref.substr(ivar_prefix.size());
+ std::pair<llvm::StringRef, llvm::StringRef> class_and_ivar =
+ ivar_skipped_prefix.split('.');
+
+ if (class_and_ivar.first.size() && class_and_ivar.second.size()) {
+ const ConstString class_name_cs(class_and_ivar.first);
+ ClassDescriptorSP descriptor =
+ ObjCLanguageRuntime::GetClassDescriptorFromClassName(class_name_cs);
+
+ if (descriptor) {
+ const ConstString ivar_name_cs(class_and_ivar.second);
+ const char *ivar_name_cstr = ivar_name_cs.AsCString();
+
+ auto ivar_func = [&ret, ivar_name_cstr](
+ const char *name, const char *type, lldb::addr_t offset_addr,
+ uint64_t size) -> lldb::addr_t {
+ if (!strcmp(name, ivar_name_cstr)) {
+ ret = offset_addr;
+ return true;
+ }
+ return false;
+ };
+
+ descriptor->Describe(
+ std::function<void(ObjCISA)>(nullptr),
+ std::function<bool(const char *, const char *)>(nullptr),
+ std::function<bool(const char *, const char *)>(nullptr),
+ ivar_func);
+ }
+ }
+ } else if (name_strref.startswith(class_prefix)) {
+ llvm::StringRef class_skipped_prefix =
+ name_strref.substr(class_prefix.size());
+ const ConstString class_name_cs(class_skipped_prefix);
+ ClassDescriptorSP descriptor =
+ GetClassDescriptorFromClassName(class_name_cs);
+
+ if (descriptor)
+ ret = descriptor->GetISA();
+ }
+ }
+
+ return ret;
+}
+
+AppleObjCRuntimeV2::NonPointerISACache *
+AppleObjCRuntimeV2::NonPointerISACache::CreateInstance(
+ AppleObjCRuntimeV2 &runtime, const lldb::ModuleSP &objc_module_sp) {
+ Process *process(runtime.GetProcess());
+
+ Status error;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES));
+
+ auto objc_debug_isa_magic_mask = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_isa_magic_mask"), objc_module_sp, error);
+ if (error.Fail())
+ return nullptr;
+
+ auto objc_debug_isa_magic_value = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_isa_magic_value"), objc_module_sp,
+ error);
+ if (error.Fail())
+ return nullptr;
+
+ auto objc_debug_isa_class_mask = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_isa_class_mask"), objc_module_sp, error);
+ if (error.Fail())
+ return nullptr;
+
+ if (log)
+ log->PutCString("AOCRT::NPI: Found all the non-indexed ISA masks");
+
+ bool foundError = false;
+ auto objc_debug_indexed_isa_magic_mask = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_indexed_isa_magic_mask"), objc_module_sp,
+ error);
+ foundError |= error.Fail();
+
+ auto objc_debug_indexed_isa_magic_value = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_indexed_isa_magic_value"),
+ objc_module_sp, error);
+ foundError |= error.Fail();
+
+ auto objc_debug_indexed_isa_index_mask = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_indexed_isa_index_mask"), objc_module_sp,
+ error);
+ foundError |= error.Fail();
+
+ auto objc_debug_indexed_isa_index_shift = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_indexed_isa_index_shift"),
+ objc_module_sp, error);
+ foundError |= error.Fail();
+
+ auto objc_indexed_classes =
+ ExtractRuntimeGlobalSymbol(process, ConstString("objc_indexed_classes"),
+ objc_module_sp, error, false);
+ foundError |= error.Fail();
+
+ if (log)
+ log->PutCString("AOCRT::NPI: Found all the indexed ISA masks");
+
+ // we might want to have some rules to outlaw these other values (e.g if the
+ // mask is zero but the value is non-zero, ...)
+
+ return new NonPointerISACache(
+ runtime, objc_module_sp, objc_debug_isa_class_mask,
+ objc_debug_isa_magic_mask, objc_debug_isa_magic_value,
+ objc_debug_indexed_isa_magic_mask, objc_debug_indexed_isa_magic_value,
+ objc_debug_indexed_isa_index_mask, objc_debug_indexed_isa_index_shift,
+ foundError ? 0 : objc_indexed_classes);
+}
+
+AppleObjCRuntimeV2::TaggedPointerVendorV2 *
+AppleObjCRuntimeV2::TaggedPointerVendorV2::CreateInstance(
+ AppleObjCRuntimeV2 &runtime, const lldb::ModuleSP &objc_module_sp) {
+ Process *process(runtime.GetProcess());
+
+ Status error;
+
+ auto objc_debug_taggedpointer_mask = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_taggedpointer_mask"), objc_module_sp,
+ error);
+ if (error.Fail())
+ return new TaggedPointerVendorLegacy(runtime);
+
+ auto objc_debug_taggedpointer_slot_shift = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_taggedpointer_slot_shift"),
+ objc_module_sp, error, true, 4);
+ if (error.Fail())
+ return new TaggedPointerVendorLegacy(runtime);
+
+ auto objc_debug_taggedpointer_slot_mask = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_taggedpointer_slot_mask"),
+ objc_module_sp, error, true, 4);
+ if (error.Fail())
+ return new TaggedPointerVendorLegacy(runtime);
+
+ auto objc_debug_taggedpointer_payload_lshift = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_taggedpointer_payload_lshift"),
+ objc_module_sp, error, true, 4);
+ if (error.Fail())
+ return new TaggedPointerVendorLegacy(runtime);
+
+ auto objc_debug_taggedpointer_payload_rshift = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_taggedpointer_payload_rshift"),
+ objc_module_sp, error, true, 4);
+ if (error.Fail())
+ return new TaggedPointerVendorLegacy(runtime);
+
+ auto objc_debug_taggedpointer_classes = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_taggedpointer_classes"), objc_module_sp,
+ error, false);
+ if (error.Fail())
+ return new TaggedPointerVendorLegacy(runtime);
+
+ // try to detect the "extended tagged pointer" variables - if any are
+ // missing, use the non-extended vendor
+ do {
+ auto objc_debug_taggedpointer_ext_mask = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_taggedpointer_ext_mask"),
+ objc_module_sp, error);
+ if (error.Fail())
+ break;
+
+ auto objc_debug_taggedpointer_ext_slot_shift = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_taggedpointer_ext_slot_shift"),
+ objc_module_sp, error, true, 4);
+ if (error.Fail())
+ break;
+
+ auto objc_debug_taggedpointer_ext_slot_mask = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_taggedpointer_ext_slot_mask"),
+ objc_module_sp, error, true, 4);
+ if (error.Fail())
+ break;
+
+ auto objc_debug_taggedpointer_ext_classes = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_taggedpointer_ext_classes"),
+ objc_module_sp, error, false);
+ if (error.Fail())
+ break;
+
+ auto objc_debug_taggedpointer_ext_payload_lshift =
+ ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_taggedpointer_ext_payload_lshift"),
+ objc_module_sp, error, true, 4);
+ if (error.Fail())
+ break;
+
+ auto objc_debug_taggedpointer_ext_payload_rshift =
+ ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_taggedpointer_ext_payload_rshift"),
+ objc_module_sp, error, true, 4);
+ if (error.Fail())
+ break;
+
+ return new TaggedPointerVendorExtended(
+ runtime, objc_debug_taggedpointer_mask,
+ objc_debug_taggedpointer_ext_mask, objc_debug_taggedpointer_slot_shift,
+ objc_debug_taggedpointer_ext_slot_shift,
+ objc_debug_taggedpointer_slot_mask,
+ objc_debug_taggedpointer_ext_slot_mask,
+ objc_debug_taggedpointer_payload_lshift,
+ objc_debug_taggedpointer_payload_rshift,
+ objc_debug_taggedpointer_ext_payload_lshift,
+ objc_debug_taggedpointer_ext_payload_rshift,
+ objc_debug_taggedpointer_classes, objc_debug_taggedpointer_ext_classes);
+ } while (false);
+
+ // we might want to have some rules to outlaw these values (e.g if the
+ // table's address is zero)
+
+ return new TaggedPointerVendorRuntimeAssisted(
+ runtime, objc_debug_taggedpointer_mask,
+ objc_debug_taggedpointer_slot_shift, objc_debug_taggedpointer_slot_mask,
+ objc_debug_taggedpointer_payload_lshift,
+ objc_debug_taggedpointer_payload_rshift,
+ objc_debug_taggedpointer_classes);
+}
+
+bool AppleObjCRuntimeV2::TaggedPointerVendorLegacy::IsPossibleTaggedPointer(
+ lldb::addr_t ptr) {
+ return (ptr & 1);
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+AppleObjCRuntimeV2::TaggedPointerVendorLegacy::GetClassDescriptor(
+ lldb::addr_t ptr) {
+ if (!IsPossibleTaggedPointer(ptr))
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+
+ uint32_t foundation_version = m_runtime.GetFoundationVersion();
+
+ if (foundation_version == LLDB_INVALID_MODULE_VERSION)
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+
+ uint64_t class_bits = (ptr & 0xE) >> 1;
+ ConstString name;
+
+ static ConstString g_NSAtom("NSAtom");
+ static ConstString g_NSNumber("NSNumber");
+ static ConstString g_NSDateTS("NSDateTS");
+ static ConstString g_NSManagedObject("NSManagedObject");
+ static ConstString g_NSDate("NSDate");
+
+ if (foundation_version >= 900) {
+ switch (class_bits) {
+ case 0:
+ name = g_NSAtom;
+ break;
+ case 3:
+ name = g_NSNumber;
+ break;
+ case 4:
+ name = g_NSDateTS;
+ break;
+ case 5:
+ name = g_NSManagedObject;
+ break;
+ case 6:
+ name = g_NSDate;
+ break;
+ default:
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+ }
+ } else {
+ switch (class_bits) {
+ case 1:
+ name = g_NSNumber;
+ break;
+ case 5:
+ name = g_NSManagedObject;
+ break;
+ case 6:
+ name = g_NSDate;
+ break;
+ case 7:
+ name = g_NSDateTS;
+ break;
+ default:
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+ }
+ }
+
+ lldb::addr_t unobfuscated = ptr ^ m_runtime.GetTaggedPointerObfuscator();
+ return ClassDescriptorSP(new ClassDescriptorV2Tagged(name, unobfuscated));
+}
+
+AppleObjCRuntimeV2::TaggedPointerVendorRuntimeAssisted::
+ TaggedPointerVendorRuntimeAssisted(
+ AppleObjCRuntimeV2 &runtime, uint64_t objc_debug_taggedpointer_mask,
+ uint32_t objc_debug_taggedpointer_slot_shift,
+ uint32_t objc_debug_taggedpointer_slot_mask,
+ uint32_t objc_debug_taggedpointer_payload_lshift,
+ uint32_t objc_debug_taggedpointer_payload_rshift,
+ lldb::addr_t objc_debug_taggedpointer_classes)
+ : TaggedPointerVendorV2(runtime), m_cache(),
+ m_objc_debug_taggedpointer_mask(objc_debug_taggedpointer_mask),
+ m_objc_debug_taggedpointer_slot_shift(
+ objc_debug_taggedpointer_slot_shift),
+ m_objc_debug_taggedpointer_slot_mask(objc_debug_taggedpointer_slot_mask),
+ m_objc_debug_taggedpointer_payload_lshift(
+ objc_debug_taggedpointer_payload_lshift),
+ m_objc_debug_taggedpointer_payload_rshift(
+ objc_debug_taggedpointer_payload_rshift),
+ m_objc_debug_taggedpointer_classes(objc_debug_taggedpointer_classes) {}
+
+bool AppleObjCRuntimeV2::TaggedPointerVendorRuntimeAssisted::
+ IsPossibleTaggedPointer(lldb::addr_t ptr) {
+ return (ptr & m_objc_debug_taggedpointer_mask) != 0;
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+AppleObjCRuntimeV2::TaggedPointerVendorRuntimeAssisted::GetClassDescriptor(
+ lldb::addr_t ptr) {
+ ClassDescriptorSP actual_class_descriptor_sp;
+ uint64_t data_payload;
+ uint64_t unobfuscated = (ptr) ^ m_runtime.GetTaggedPointerObfuscator();
+
+ if (!IsPossibleTaggedPointer(unobfuscated))
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+
+ uintptr_t slot = (ptr >> m_objc_debug_taggedpointer_slot_shift) &
+ m_objc_debug_taggedpointer_slot_mask;
+
+ CacheIterator iterator = m_cache.find(slot), end = m_cache.end();
+ if (iterator != end) {
+ actual_class_descriptor_sp = iterator->second;
+ } else {
+ Process *process(m_runtime.GetProcess());
+ uintptr_t slot_ptr = slot * process->GetAddressByteSize() +
+ m_objc_debug_taggedpointer_classes;
+ Status error;
+ uintptr_t slot_data = process->ReadPointerFromMemory(slot_ptr, error);
+ if (error.Fail() || slot_data == 0 ||
+ slot_data == uintptr_t(LLDB_INVALID_ADDRESS))
+ return nullptr;
+ actual_class_descriptor_sp =
+ m_runtime.GetClassDescriptorFromISA((ObjCISA)slot_data);
+ if (!actual_class_descriptor_sp)
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+ m_cache[slot] = actual_class_descriptor_sp;
+ }
+
+ data_payload =
+ (((uint64_t)unobfuscated << m_objc_debug_taggedpointer_payload_lshift) >>
+ m_objc_debug_taggedpointer_payload_rshift);
+
+ return ClassDescriptorSP(
+ new ClassDescriptorV2Tagged(actual_class_descriptor_sp, data_payload));
+}
+
+AppleObjCRuntimeV2::TaggedPointerVendorExtended::TaggedPointerVendorExtended(
+ AppleObjCRuntimeV2 &runtime, uint64_t objc_debug_taggedpointer_mask,
+ uint64_t objc_debug_taggedpointer_ext_mask,
+ uint32_t objc_debug_taggedpointer_slot_shift,
+ uint32_t objc_debug_taggedpointer_ext_slot_shift,
+ uint32_t objc_debug_taggedpointer_slot_mask,
+ uint32_t objc_debug_taggedpointer_ext_slot_mask,
+ uint32_t objc_debug_taggedpointer_payload_lshift,
+ uint32_t objc_debug_taggedpointer_payload_rshift,
+ uint32_t objc_debug_taggedpointer_ext_payload_lshift,
+ uint32_t objc_debug_taggedpointer_ext_payload_rshift,
+ lldb::addr_t objc_debug_taggedpointer_classes,
+ lldb::addr_t objc_debug_taggedpointer_ext_classes)
+ : TaggedPointerVendorRuntimeAssisted(
+ runtime, objc_debug_taggedpointer_mask,
+ objc_debug_taggedpointer_slot_shift,
+ objc_debug_taggedpointer_slot_mask,
+ objc_debug_taggedpointer_payload_lshift,
+ objc_debug_taggedpointer_payload_rshift,
+ objc_debug_taggedpointer_classes),
+ m_ext_cache(),
+ m_objc_debug_taggedpointer_ext_mask(objc_debug_taggedpointer_ext_mask),
+ m_objc_debug_taggedpointer_ext_slot_shift(
+ objc_debug_taggedpointer_ext_slot_shift),
+ m_objc_debug_taggedpointer_ext_slot_mask(
+ objc_debug_taggedpointer_ext_slot_mask),
+ m_objc_debug_taggedpointer_ext_payload_lshift(
+ objc_debug_taggedpointer_ext_payload_lshift),
+ m_objc_debug_taggedpointer_ext_payload_rshift(
+ objc_debug_taggedpointer_ext_payload_rshift),
+ m_objc_debug_taggedpointer_ext_classes(
+ objc_debug_taggedpointer_ext_classes) {}
+
+bool AppleObjCRuntimeV2::TaggedPointerVendorExtended::
+ IsPossibleExtendedTaggedPointer(lldb::addr_t ptr) {
+ if (!IsPossibleTaggedPointer(ptr))
+ return false;
+
+ if (m_objc_debug_taggedpointer_ext_mask == 0)
+ return false;
+
+ return ((ptr & m_objc_debug_taggedpointer_ext_mask) ==
+ m_objc_debug_taggedpointer_ext_mask);
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+AppleObjCRuntimeV2::TaggedPointerVendorExtended::GetClassDescriptor(
+ lldb::addr_t ptr) {
+ ClassDescriptorSP actual_class_descriptor_sp;
+ uint64_t data_payload;
+ uint64_t unobfuscated = (ptr) ^ m_runtime.GetTaggedPointerObfuscator();
+
+ if (!IsPossibleTaggedPointer(unobfuscated))
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+
+ if (!IsPossibleExtendedTaggedPointer(unobfuscated))
+ return this->TaggedPointerVendorRuntimeAssisted::GetClassDescriptor(ptr);
+
+ uintptr_t slot = (ptr >> m_objc_debug_taggedpointer_ext_slot_shift) &
+ m_objc_debug_taggedpointer_ext_slot_mask;
+
+ CacheIterator iterator = m_ext_cache.find(slot), end = m_ext_cache.end();
+ if (iterator != end) {
+ actual_class_descriptor_sp = iterator->second;
+ } else {
+ Process *process(m_runtime.GetProcess());
+ uintptr_t slot_ptr = slot * process->GetAddressByteSize() +
+ m_objc_debug_taggedpointer_ext_classes;
+ Status error;
+ uintptr_t slot_data = process->ReadPointerFromMemory(slot_ptr, error);
+ if (error.Fail() || slot_data == 0 ||
+ slot_data == uintptr_t(LLDB_INVALID_ADDRESS))
+ return nullptr;
+ actual_class_descriptor_sp =
+ m_runtime.GetClassDescriptorFromISA((ObjCISA)slot_data);
+ if (!actual_class_descriptor_sp)
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+ m_ext_cache[slot] = actual_class_descriptor_sp;
+ }
+
+ data_payload =
+ (((uint64_t)unobfuscated << m_objc_debug_taggedpointer_ext_payload_lshift) >>
+ m_objc_debug_taggedpointer_ext_payload_rshift);
+
+ return ClassDescriptorSP(
+ new ClassDescriptorV2Tagged(actual_class_descriptor_sp, data_payload));
+}
+
+AppleObjCRuntimeV2::NonPointerISACache::NonPointerISACache(
+ AppleObjCRuntimeV2 &runtime, const ModuleSP &objc_module_sp,
+ uint64_t objc_debug_isa_class_mask, uint64_t objc_debug_isa_magic_mask,
+ uint64_t objc_debug_isa_magic_value,
+ uint64_t objc_debug_indexed_isa_magic_mask,
+ uint64_t objc_debug_indexed_isa_magic_value,
+ uint64_t objc_debug_indexed_isa_index_mask,
+ uint64_t objc_debug_indexed_isa_index_shift,
+ lldb::addr_t objc_indexed_classes)
+ : m_runtime(runtime), m_cache(), m_objc_module_wp(objc_module_sp),
+ m_objc_debug_isa_class_mask(objc_debug_isa_class_mask),
+ m_objc_debug_isa_magic_mask(objc_debug_isa_magic_mask),
+ m_objc_debug_isa_magic_value(objc_debug_isa_magic_value),
+ m_objc_debug_indexed_isa_magic_mask(objc_debug_indexed_isa_magic_mask),
+ m_objc_debug_indexed_isa_magic_value(objc_debug_indexed_isa_magic_value),
+ m_objc_debug_indexed_isa_index_mask(objc_debug_indexed_isa_index_mask),
+ m_objc_debug_indexed_isa_index_shift(objc_debug_indexed_isa_index_shift),
+ m_objc_indexed_classes(objc_indexed_classes), m_indexed_isa_cache() {}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+AppleObjCRuntimeV2::NonPointerISACache::GetClassDescriptor(ObjCISA isa) {
+ ObjCISA real_isa = 0;
+ if (!EvaluateNonPointerISA(isa, real_isa))
+ return ObjCLanguageRuntime::ClassDescriptorSP();
+ auto cache_iter = m_cache.find(real_isa);
+ if (cache_iter != m_cache.end())
+ return cache_iter->second;
+ auto descriptor_sp =
+ m_runtime.ObjCLanguageRuntime::GetClassDescriptorFromISA(real_isa);
+ if (descriptor_sp) // cache only positive matches since the table might grow
+ m_cache[real_isa] = descriptor_sp;
+ return descriptor_sp;
+}
+
+bool AppleObjCRuntimeV2::NonPointerISACache::EvaluateNonPointerISA(
+ ObjCISA isa, ObjCISA &ret_isa) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES));
+
+ if (log)
+ log->Printf("AOCRT::NPI Evalulate(isa = 0x%" PRIx64 ")", (uint64_t)isa);
+
+ if ((isa & ~m_objc_debug_isa_class_mask) == 0)
+ return false;
+
+ // If all of the indexed ISA variables are set, then its possible that this
+ // ISA is indexed, and we should first try to get its value using the index.
+ // Note, we check these variables first as the ObjC runtime will set at least
+ // one of their values to 0 if they aren't needed.
+ if (m_objc_debug_indexed_isa_magic_mask &&
+ m_objc_debug_indexed_isa_magic_value &&
+ m_objc_debug_indexed_isa_index_mask &&
+ m_objc_debug_indexed_isa_index_shift && m_objc_indexed_classes) {
+ if ((isa & ~m_objc_debug_indexed_isa_index_mask) == 0)
+ return false;
+
+ if ((isa & m_objc_debug_indexed_isa_magic_mask) ==
+ m_objc_debug_indexed_isa_magic_value) {
+ // Magic bits are correct, so try extract the index.
+ uintptr_t index = (isa & m_objc_debug_indexed_isa_index_mask) >>
+ m_objc_debug_indexed_isa_index_shift;
+ // If the index is out of bounds of the length of the array then check if
+ // the array has been updated. If that is the case then we should try
+ // read the count again, and update the cache if the count has been
+ // updated.
+ if (index > m_indexed_isa_cache.size()) {
+ if (log)
+ log->Printf("AOCRT::NPI (index = %" PRIu64
+ ") exceeds cache (size = %" PRIu64 ")",
+ (uint64_t)index, (uint64_t)m_indexed_isa_cache.size());
+
+ Process *process(m_runtime.GetProcess());
+
+ ModuleSP objc_module_sp(m_objc_module_wp.lock());
+ if (!objc_module_sp)
+ return false;
+
+ Status error;
+ auto objc_indexed_classes_count = ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_indexed_classes_count"), objc_module_sp,
+ error);
+ if (error.Fail())
+ return false;
+
+ if (log)
+ log->Printf("AOCRT::NPI (new class count = %" PRIu64 ")",
+ (uint64_t)objc_indexed_classes_count);
+
+ if (objc_indexed_classes_count > m_indexed_isa_cache.size()) {
+ // Read the class entries we don't have. We should just read all of
+ // them instead of just the one we need as then we can cache those we
+ // may need later.
+ auto num_new_classes =
+ objc_indexed_classes_count - m_indexed_isa_cache.size();
+ const uint32_t addr_size = process->GetAddressByteSize();
+ DataBufferHeap buffer(num_new_classes * addr_size, 0);
+
+ lldb::addr_t last_read_class =
+ m_objc_indexed_classes + (m_indexed_isa_cache.size() * addr_size);
+ size_t bytes_read = process->ReadMemory(
+ last_read_class, buffer.GetBytes(), buffer.GetByteSize(), error);
+ if (error.Fail() || bytes_read != buffer.GetByteSize())
+ return false;
+
+ if (log)
+ log->Printf("AOCRT::NPI (read new classes count = %" PRIu64 ")",
+ (uint64_t)num_new_classes);
+
+ // Append the new entries to the existing cache.
+ DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(),
+ process->GetByteOrder(),
+ process->GetAddressByteSize());
+
+ lldb::offset_t offset = 0;
+ for (unsigned i = 0; i != num_new_classes; ++i)
+ m_indexed_isa_cache.push_back(data.GetPointer(&offset));
+ }
+ }
+
+ // If the index is still out of range then this isn't a pointer.
+ if (index > m_indexed_isa_cache.size())
+ return false;
+
+ if (log)
+ log->Printf("AOCRT::NPI Evalulate(ret_isa = 0x%" PRIx64 ")",
+ (uint64_t)m_indexed_isa_cache[index]);
+
+ ret_isa = m_indexed_isa_cache[index];
+ return (ret_isa != 0); // this is a pointer so 0 is not a valid value
+ }
+
+ return false;
+ }
+
+ // Definitely not an indexed ISA, so try to use a mask to extract the pointer
+ // from the ISA.
+ if ((isa & m_objc_debug_isa_magic_mask) == m_objc_debug_isa_magic_value) {
+ ret_isa = isa & m_objc_debug_isa_class_mask;
+ return (ret_isa != 0); // this is a pointer so 0 is not a valid value
+ }
+ return false;
+}
+
+ObjCLanguageRuntime::EncodingToTypeSP AppleObjCRuntimeV2::GetEncodingToType() {
+ if (!m_encoding_to_type_sp)
+ m_encoding_to_type_sp =
+ std::make_shared<AppleObjCTypeEncodingParser>(*this);
+ return m_encoding_to_type_sp;
+}
+
+lldb_private::AppleObjCRuntime::ObjCISA
+AppleObjCRuntimeV2::GetPointerISA(ObjCISA isa) {
+ ObjCISA ret = isa;
+
+ if (m_non_pointer_isa_cache_up)
+ m_non_pointer_isa_cache_up->EvaluateNonPointerISA(isa, ret);
+
+ return ret;
+}
+
+bool AppleObjCRuntimeV2::GetCFBooleanValuesIfNeeded() {
+ if (m_CFBoolean_values)
+ return true;
+
+ static ConstString g_kCFBooleanFalse("__kCFBooleanFalse");
+ static ConstString g_kCFBooleanTrue("__kCFBooleanTrue");
+
+ std::function<lldb::addr_t(ConstString)> get_symbol =
+ [this](ConstString sym) -> lldb::addr_t {
+ SymbolContextList sc_list;
+ if (GetProcess()->GetTarget().GetImages().FindSymbolsWithNameAndType(
+ sym, lldb::eSymbolTypeData, sc_list) == 1) {
+ SymbolContext sc;
+ sc_list.GetContextAtIndex(0, sc);
+ if (sc.symbol)
+ return sc.symbol->GetLoadAddress(&GetProcess()->GetTarget());
+ }
+
+ return LLDB_INVALID_ADDRESS;
+ };
+
+ lldb::addr_t false_addr = get_symbol(g_kCFBooleanFalse);
+ lldb::addr_t true_addr = get_symbol(g_kCFBooleanTrue);
+
+ return (m_CFBoolean_values = {false_addr, true_addr}).operator bool();
+}
+
+void AppleObjCRuntimeV2::GetValuesForGlobalCFBooleans(lldb::addr_t &cf_true,
+ lldb::addr_t &cf_false) {
+ if (GetCFBooleanValuesIfNeeded()) {
+ cf_true = m_CFBoolean_values->second;
+ cf_false = m_CFBoolean_values->first;
+ } else
+ this->AppleObjCRuntime::GetValuesForGlobalCFBooleans(cf_true, cf_false);
+}
+
+#pragma mark Frame recognizers
+
+class ObjCExceptionRecognizedStackFrame : public RecognizedStackFrame {
+ public:
+ ObjCExceptionRecognizedStackFrame(StackFrameSP frame_sp) {
+ ThreadSP thread_sp = frame_sp->GetThread();
+ ProcessSP process_sp = thread_sp->GetProcess();
+
+ const lldb::ABISP &abi = process_sp->GetABI();
+ if (!abi) return;
+
+ CompilerType voidstar = process_sp->GetTarget()
+ .GetScratchClangASTContext()
+ ->GetBasicType(lldb::eBasicTypeVoid)
+ .GetPointerType();
+
+ ValueList args;
+ Value input_value;
+ input_value.SetCompilerType(voidstar);
+ args.PushValue(input_value);
+
+ if (!abi->GetArgumentValues(*thread_sp, args)) return;
+
+ addr_t exception_addr = args.GetValueAtIndex(0)->GetScalar().ULongLong();
+
+ Value value(exception_addr);
+ value.SetCompilerType(voidstar);
+ exception = ValueObjectConstResult::Create(frame_sp.get(), value,
+ ConstString("exception"));
+ exception = ValueObjectRecognizerSynthesizedValue::Create(
+ *exception, eValueTypeVariableArgument);
+ exception = exception->GetDynamicValue(eDynamicDontRunTarget);
+
+ m_arguments = ValueObjectListSP(new ValueObjectList());
+ m_arguments->Append(exception);
+ }
+
+ ValueObjectSP exception;
+
+ lldb::ValueObjectSP GetExceptionObject() override { return exception; }
+};
+
+class ObjCExceptionThrowFrameRecognizer : public StackFrameRecognizer {
+ lldb::RecognizedStackFrameSP
+ RecognizeFrame(lldb::StackFrameSP frame) override {
+ return lldb::RecognizedStackFrameSP(
+ new ObjCExceptionRecognizedStackFrame(frame));
+ };
+};
+
+static void RegisterObjCExceptionRecognizer() {
+ static llvm::once_flag g_once_flag;
+ llvm::call_once(g_once_flag, []() {
+ FileSpec module;
+ ConstString function;
+ std::tie(module, function) = AppleObjCRuntime::GetExceptionThrowLocation();
+ StackFrameRecognizerManager::AddRecognizer(
+ StackFrameRecognizerSP(new ObjCExceptionThrowFrameRecognizer()),
+ module.GetFilename(), function, /*first_instruction_only*/ true);
+ });
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
new file mode 100644
index 000000000000..a0fd39dc03b2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
@@ -0,0 +1,342 @@
+//===-- AppleObjCRuntimeV2.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 liblldb_AppleObjCRuntimeV2_h_
+#define liblldb_AppleObjCRuntimeV2_h_
+
+#include <map>
+#include <memory>
+#include <mutex>
+
+#include "AppleObjCRuntime.h"
+#include "lldb/lldb-private.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+class RemoteNXMapTable;
+
+namespace lldb_private {
+
+class AppleObjCRuntimeV2 : public AppleObjCRuntime {
+public:
+ ~AppleObjCRuntimeV2() override = default;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::LanguageRuntime *
+ CreateInstance(Process *process, lldb::LanguageType language);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static char ID;
+
+ bool isA(const void *ClassID) const override {
+ return ClassID == &ID || AppleObjCRuntime::isA(ClassID);
+ }
+
+ static bool classof(const LanguageRuntime *runtime) {
+ return runtime->isA(&ID);
+ }
+
+ // These are generic runtime functions:
+ bool GetDynamicTypeAndAddress(ValueObject &in_value,
+ lldb::DynamicValueType use_dynamic,
+ TypeAndOrName &class_type_or_name,
+ Address &address,
+ Value::ValueType &value_type) override;
+
+ UtilityFunction *CreateObjectChecker(const char *) override;
+
+ // PluginInterface protocol
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ ObjCRuntimeVersions GetRuntimeVersion() const override {
+ return ObjCRuntimeVersions::eAppleObjC_V2;
+ }
+
+ size_t GetByteOffsetForIvar(CompilerType &parent_qual_type,
+ const char *ivar_name) override;
+
+ void UpdateISAToDescriptorMapIfNeeded() override;
+
+ ConstString GetActualTypeName(ObjCLanguageRuntime::ObjCISA isa) override;
+
+ ClassDescriptorSP GetClassDescriptor(ValueObject &in_value) override;
+
+ ClassDescriptorSP GetClassDescriptorFromISA(ObjCISA isa) override;
+
+ DeclVendor *GetDeclVendor() override;
+
+ lldb::addr_t LookupRuntimeSymbol(ConstString name) override;
+
+ EncodingToTypeSP GetEncodingToType() override;
+
+ bool IsTaggedPointer(lldb::addr_t ptr) override;
+
+ TaggedPointerVendor *GetTaggedPointerVendor() override {
+ return m_tagged_pointer_vendor_up.get();
+ }
+
+ lldb::addr_t GetTaggedPointerObfuscator();
+
+ void GetValuesForGlobalCFBooleans(lldb::addr_t &cf_true,
+ lldb::addr_t &cf_false) override;
+
+ // none of these are valid ISAs - we use them to infer the type
+ // of tagged pointers - if we have something meaningful to say
+ // we report an actual type - otherwise, we just say tagged
+ // there is no connection between the values here and the tagged pointers map
+ static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA = 1;
+ static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSAtom = 2;
+ static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSNumber = 3;
+ static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSDateTS = 4;
+ static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSManagedObject =
+ 5;
+ static const ObjCLanguageRuntime::ObjCISA g_objc_Tagged_ISA_NSDate = 6;
+
+protected:
+ lldb::BreakpointResolverSP CreateExceptionResolver(Breakpoint *bkpt,
+ bool catch_bp,
+ bool throw_bp) override;
+
+private:
+ class HashTableSignature {
+ public:
+ HashTableSignature();
+
+ bool NeedsUpdate(Process *process, AppleObjCRuntimeV2 *runtime,
+ RemoteNXMapTable &hash_table);
+
+ void UpdateSignature(const RemoteNXMapTable &hash_table);
+
+ protected:
+ uint32_t m_count;
+ uint32_t m_num_buckets;
+ lldb::addr_t m_buckets_ptr;
+ };
+
+ class NonPointerISACache {
+ public:
+ static NonPointerISACache *
+ CreateInstance(AppleObjCRuntimeV2 &runtime,
+ const lldb::ModuleSP &objc_module_sp);
+
+ ObjCLanguageRuntime::ClassDescriptorSP GetClassDescriptor(ObjCISA isa);
+
+ private:
+ NonPointerISACache(AppleObjCRuntimeV2 &runtime,
+ const lldb::ModuleSP &objc_module_sp,
+ uint64_t objc_debug_isa_class_mask,
+ uint64_t objc_debug_isa_magic_mask,
+ uint64_t objc_debug_isa_magic_value,
+ uint64_t objc_debug_indexed_isa_magic_mask,
+ uint64_t objc_debug_indexed_isa_magic_value,
+ uint64_t objc_debug_indexed_isa_index_mask,
+ uint64_t objc_debug_indexed_isa_index_shift,
+ lldb::addr_t objc_indexed_classes);
+
+ bool EvaluateNonPointerISA(ObjCISA isa, ObjCISA &ret_isa);
+
+ AppleObjCRuntimeV2 &m_runtime;
+ std::map<ObjCISA, ObjCLanguageRuntime::ClassDescriptorSP> m_cache;
+ lldb::ModuleWP m_objc_module_wp;
+ uint64_t m_objc_debug_isa_class_mask;
+ uint64_t m_objc_debug_isa_magic_mask;
+ uint64_t m_objc_debug_isa_magic_value;
+
+ uint64_t m_objc_debug_indexed_isa_magic_mask;
+ uint64_t m_objc_debug_indexed_isa_magic_value;
+ uint64_t m_objc_debug_indexed_isa_index_mask;
+ uint64_t m_objc_debug_indexed_isa_index_shift;
+ lldb::addr_t m_objc_indexed_classes;
+
+ std::vector<lldb::addr_t> m_indexed_isa_cache;
+
+ friend class AppleObjCRuntimeV2;
+
+ DISALLOW_COPY_AND_ASSIGN(NonPointerISACache);
+ };
+
+ class TaggedPointerVendorV2
+ : public ObjCLanguageRuntime::TaggedPointerVendor {
+ public:
+ ~TaggedPointerVendorV2() override = default;
+
+ static TaggedPointerVendorV2 *
+ CreateInstance(AppleObjCRuntimeV2 &runtime,
+ const lldb::ModuleSP &objc_module_sp);
+
+ protected:
+ AppleObjCRuntimeV2 &m_runtime;
+
+ TaggedPointerVendorV2(AppleObjCRuntimeV2 &runtime)
+ : TaggedPointerVendor(), m_runtime(runtime) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TaggedPointerVendorV2);
+ };
+
+ class TaggedPointerVendorRuntimeAssisted : public TaggedPointerVendorV2 {
+ public:
+ bool IsPossibleTaggedPointer(lldb::addr_t ptr) override;
+
+ ObjCLanguageRuntime::ClassDescriptorSP
+ GetClassDescriptor(lldb::addr_t ptr) override;
+
+ protected:
+ TaggedPointerVendorRuntimeAssisted(
+ AppleObjCRuntimeV2 &runtime, uint64_t objc_debug_taggedpointer_mask,
+ uint32_t objc_debug_taggedpointer_slot_shift,
+ uint32_t objc_debug_taggedpointer_slot_mask,
+ uint32_t objc_debug_taggedpointer_payload_lshift,
+ uint32_t objc_debug_taggedpointer_payload_rshift,
+ lldb::addr_t objc_debug_taggedpointer_classes);
+
+ typedef std::map<uint8_t, ObjCLanguageRuntime::ClassDescriptorSP> Cache;
+ typedef Cache::iterator CacheIterator;
+ Cache m_cache;
+ uint64_t m_objc_debug_taggedpointer_mask;
+ uint32_t m_objc_debug_taggedpointer_slot_shift;
+ uint32_t m_objc_debug_taggedpointer_slot_mask;
+ uint32_t m_objc_debug_taggedpointer_payload_lshift;
+ uint32_t m_objc_debug_taggedpointer_payload_rshift;
+ lldb::addr_t m_objc_debug_taggedpointer_classes;
+
+ friend class AppleObjCRuntimeV2::TaggedPointerVendorV2;
+
+ DISALLOW_COPY_AND_ASSIGN(TaggedPointerVendorRuntimeAssisted);
+ };
+
+ class TaggedPointerVendorExtended
+ : public TaggedPointerVendorRuntimeAssisted {
+ public:
+ ObjCLanguageRuntime::ClassDescriptorSP
+ GetClassDescriptor(lldb::addr_t ptr) override;
+
+ protected:
+ TaggedPointerVendorExtended(
+ AppleObjCRuntimeV2 &runtime, uint64_t objc_debug_taggedpointer_mask,
+ uint64_t objc_debug_taggedpointer_ext_mask,
+ uint32_t objc_debug_taggedpointer_slot_shift,
+ uint32_t objc_debug_taggedpointer_ext_slot_shift,
+ uint32_t objc_debug_taggedpointer_slot_mask,
+ uint32_t objc_debug_taggedpointer_ext_slot_mask,
+ uint32_t objc_debug_taggedpointer_payload_lshift,
+ uint32_t objc_debug_taggedpointer_payload_rshift,
+ uint32_t objc_debug_taggedpointer_ext_payload_lshift,
+ uint32_t objc_debug_taggedpointer_ext_payload_rshift,
+ lldb::addr_t objc_debug_taggedpointer_classes,
+ lldb::addr_t objc_debug_taggedpointer_ext_classes);
+
+ bool IsPossibleExtendedTaggedPointer(lldb::addr_t ptr);
+
+ typedef std::map<uint8_t, ObjCLanguageRuntime::ClassDescriptorSP> Cache;
+ typedef Cache::iterator CacheIterator;
+ Cache m_ext_cache;
+ uint64_t m_objc_debug_taggedpointer_ext_mask;
+ uint32_t m_objc_debug_taggedpointer_ext_slot_shift;
+ uint32_t m_objc_debug_taggedpointer_ext_slot_mask;
+ uint32_t m_objc_debug_taggedpointer_ext_payload_lshift;
+ uint32_t m_objc_debug_taggedpointer_ext_payload_rshift;
+ lldb::addr_t m_objc_debug_taggedpointer_ext_classes;
+
+ friend class AppleObjCRuntimeV2::TaggedPointerVendorV2;
+
+ DISALLOW_COPY_AND_ASSIGN(TaggedPointerVendorExtended);
+ };
+
+ class TaggedPointerVendorLegacy : public TaggedPointerVendorV2 {
+ public:
+ bool IsPossibleTaggedPointer(lldb::addr_t ptr) override;
+
+ ObjCLanguageRuntime::ClassDescriptorSP
+ GetClassDescriptor(lldb::addr_t ptr) override;
+
+ protected:
+ TaggedPointerVendorLegacy(AppleObjCRuntimeV2 &runtime)
+ : TaggedPointerVendorV2(runtime) {}
+
+ friend class AppleObjCRuntimeV2::TaggedPointerVendorV2;
+
+ DISALLOW_COPY_AND_ASSIGN(TaggedPointerVendorLegacy);
+ };
+
+ struct DescriptorMapUpdateResult {
+ bool m_update_ran;
+ uint32_t m_num_found;
+
+ DescriptorMapUpdateResult(bool ran, uint32_t found) {
+ m_update_ran = ran;
+ m_num_found = found;
+ }
+
+ static DescriptorMapUpdateResult Fail() { return {false, 0}; }
+
+ static DescriptorMapUpdateResult Success(uint32_t found) {
+ return {true, found};
+ }
+ };
+
+ AppleObjCRuntimeV2(Process *process, const lldb::ModuleSP &objc_module_sp);
+
+ ObjCISA GetPointerISA(ObjCISA isa);
+
+ lldb::addr_t GetISAHashTablePointer();
+
+ bool UpdateISAToDescriptorMapFromMemory(RemoteNXMapTable &hash_table);
+
+ DescriptorMapUpdateResult
+ UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table);
+
+ uint32_t ParseClassInfoArray(const lldb_private::DataExtractor &data,
+ uint32_t num_class_infos);
+
+ DescriptorMapUpdateResult UpdateISAToDescriptorMapSharedCache();
+
+ enum class SharedCacheWarningReason {
+ eExpressionExecutionFailure,
+ eNotEnoughClassesRead
+ };
+
+ void WarnIfNoClassesCached(SharedCacheWarningReason reason);
+
+ lldb::addr_t GetSharedCacheReadOnlyAddress();
+
+ bool GetCFBooleanValuesIfNeeded();
+
+ friend class ClassDescriptorV2;
+
+ std::unique_ptr<UtilityFunction> m_get_class_info_code;
+ lldb::addr_t m_get_class_info_args;
+ std::mutex m_get_class_info_args_mutex;
+
+ std::unique_ptr<UtilityFunction> m_get_shared_cache_class_info_code;
+ lldb::addr_t m_get_shared_cache_class_info_args;
+ std::mutex m_get_shared_cache_class_info_args_mutex;
+
+ std::unique_ptr<DeclVendor> m_decl_vendor_up;
+ lldb::addr_t m_tagged_pointer_obfuscator;
+ lldb::addr_t m_isa_hash_table_ptr;
+ HashTableSignature m_hash_signature;
+ bool m_has_object_getClass;
+ bool m_loaded_objc_opt;
+ std::unique_ptr<NonPointerISACache> m_non_pointer_isa_cache_up;
+ std::unique_ptr<TaggedPointerVendor> m_tagged_pointer_vendor_up;
+ EncodingToTypeSP m_encoding_to_type_sp;
+ bool m_noclasses_warning_emitted;
+ llvm::Optional<std::pair<lldb::addr_t, lldb::addr_t>> m_CFBoolean_values;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_AppleObjCRuntimeV2_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.cpp
new file mode 100644
index 000000000000..b3eb09caa86d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.cpp
@@ -0,0 +1,1151 @@
+//===-- AppleObjCTrampolineHandler.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 "AppleObjCTrampolineHandler.h"
+#include "AppleThreadPlanStepThroughObjCTrampoline.h"
+
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/FunctionCaller.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Expression/UtilityFunction.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlanRunToAddress.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+const char *AppleObjCTrampolineHandler::g_lookup_implementation_function_name =
+ "__lldb_objc_find_implementation_for_selector";
+const char *AppleObjCTrampolineHandler::
+ g_lookup_implementation_with_stret_function_code =
+ " \n\
+extern \"C\" \n\
+{ \n\
+ extern void *class_getMethodImplementation(void *objc_class, void *sel); \n\
+ extern void *class_getMethodImplementation_stret(void *objc_class, \n\
+ void *sel); \n\
+ extern void * object_getClass (id object); \n\
+ extern void * sel_getUid(char *name); \n\
+ extern int printf(const char *format, ...); \n\
+} \n\
+extern \"C\" void * __lldb_objc_find_implementation_for_selector ( \n\
+ void *object, \n\
+ void *sel, \n\
+ int is_stret, \n\
+ int is_super, \n\
+ int is_super2, \n\
+ int is_fixup, \n\
+ int is_fixed, \n\
+ int debug) \n\
+{ \n\
+ struct __lldb_imp_return_struct \n\
+ { \n\
+ void *class_addr; \n\
+ void *sel_addr; \n\
+ void *impl_addr; \n\
+ }; \n\
+ \n\
+ struct __lldb_objc_class { \n\
+ void *isa; \n\
+ void *super_ptr; \n\
+ }; \n\
+ struct __lldb_objc_super { \n\
+ void *receiver; \n\
+ struct __lldb_objc_class *class_ptr; \n\
+ }; \n\
+ struct __lldb_msg_ref { \n\
+ void *dont_know; \n\
+ void *sel; \n\
+ }; \n\
+ \n\
+ struct __lldb_imp_return_struct return_struct; \n\
+ \n\
+ if (debug) \n\
+ printf (\"\\n*** Called with obj: 0x%p sel: 0x%p is_stret: %d is_super: %d, \"\n\
+ \"is_super2: %d, is_fixup: %d, is_fixed: %d\\n\", \n\
+ object, sel, is_stret, is_super, is_super2, is_fixup, is_fixed);\n\
+ if (is_super) \n\
+ { \n\
+ if (is_super2) \n\
+ { \n\
+ return_struct.class_addr = ((__lldb_objc_super *) object)->class_ptr->super_ptr;\n\
+ } \n\
+ else \n\
+ { \n\
+ return_struct.class_addr = ((__lldb_objc_super *) object)->class_ptr;\n\
+ } \n\
+ } \n\
+ else \n\
+ { \n\
+ // This code seems a little funny, but has its reasons... \n\
+ \n\
+ // The call to [object class] is here because if this is a \n\
+ // class, and has not been called into yet, we need to do \n\
+ // something to force the class to initialize itself. \n\
+ // Then the call to object_getClass will actually return the \n\
+ // correct class, either the class if object is a class \n\
+ // instance, or the meta-class if it is a class pointer. \n\
+ void *class_ptr = (void *) [(id) object class]; \n\
+ return_struct.class_addr = (id) object_getClass((id) object); \n\
+ if (debug) \n\
+ { \n\
+ if (class_ptr == object) \n\
+ { \n\
+ printf (\"Found a class object, need to use the meta class %p -> %p\\n\",\n\
+ class_ptr, return_struct.class_addr); \n\
+ } \n\
+ else \n\
+ { \n\
+ printf (\"[object class] returned: %p object_getClass: %p.\\n\", \n\
+ class_ptr, return_struct.class_addr); \n\
+ } \n\
+ } \n\
+ } \n\
+ \n\
+ if (is_fixup) \n\
+ { \n\
+ if (is_fixed) \n\
+ { \n\
+ return_struct.sel_addr = ((__lldb_msg_ref *) sel)->sel; \n\
+ } \n\
+ else \n\
+ { \n\
+ char *sel_name = (char *) ((__lldb_msg_ref *) sel)->sel; \n\
+ return_struct.sel_addr = sel_getUid (sel_name); \n\
+ if (debug) \n\
+ printf (\"\\n*** Got fixed up selector: %p for name %s.\\n\",\n\
+ return_struct.sel_addr, sel_name); \n\
+ } \n\
+ } \n\
+ else \n\
+ { \n\
+ return_struct.sel_addr = sel; \n\
+ } \n\
+ \n\
+ if (is_stret) \n\
+ { \n\
+ return_struct.impl_addr = \n\
+ class_getMethodImplementation_stret (return_struct.class_addr, \n\
+ return_struct.sel_addr); \n\
+ } \n\
+ else \n\
+ { \n\
+ return_struct.impl_addr = \n\
+ class_getMethodImplementation (return_struct.class_addr, \n\
+ return_struct.sel_addr); \n\
+ } \n\
+ if (debug) \n\
+ printf (\"\\n*** Returning implementation: %p.\\n\", \n\
+ return_struct.impl_addr); \n\
+ \n\
+ return return_struct.impl_addr; \n\
+} \n\
+";
+const char *
+ AppleObjCTrampolineHandler::g_lookup_implementation_no_stret_function_code =
+ " \n\
+extern \"C\" \n\
+{ \n\
+ extern void *class_getMethodImplementation(void *objc_class, void *sel); \n\
+ extern void * object_getClass (id object); \n\
+ extern void * sel_getUid(char *name); \n\
+ extern int printf(const char *format, ...); \n\
+} \n\
+extern \"C\" void * __lldb_objc_find_implementation_for_selector (void *object, \n\
+ void *sel, \n\
+ int is_stret, \n\
+ int is_super, \n\
+ int is_super2, \n\
+ int is_fixup, \n\
+ int is_fixed, \n\
+ int debug) \n\
+{ \n\
+ struct __lldb_imp_return_struct \n\
+ { \n\
+ void *class_addr; \n\
+ void *sel_addr; \n\
+ void *impl_addr; \n\
+ }; \n\
+ \n\
+ struct __lldb_objc_class { \n\
+ void *isa; \n\
+ void *super_ptr; \n\
+ }; \n\
+ struct __lldb_objc_super { \n\
+ void *receiver; \n\
+ struct __lldb_objc_class *class_ptr; \n\
+ }; \n\
+ struct __lldb_msg_ref { \n\
+ void *dont_know; \n\
+ void *sel; \n\
+ }; \n\
+ \n\
+ struct __lldb_imp_return_struct return_struct; \n\
+ \n\
+ if (debug) \n\
+ printf (\"\\n*** Called with obj: 0x%p sel: 0x%p is_stret: %d is_super: %d, \" \n\
+ \"is_super2: %d, is_fixup: %d, is_fixed: %d\\n\", \n\
+ object, sel, is_stret, is_super, is_super2, is_fixup, is_fixed); \n\
+ if (is_super) \n\
+ { \n\
+ if (is_super2) \n\
+ { \n\
+ return_struct.class_addr = ((__lldb_objc_super *) object)->class_ptr->super_ptr; \n\
+ } \n\
+ else \n\
+ { \n\
+ return_struct.class_addr = ((__lldb_objc_super *) object)->class_ptr; \n\
+ } \n\
+ } \n\
+ else \n\
+ { \n\
+ // This code seems a little funny, but has its reasons... \n\
+ // The call to [object class] is here because if this is a class, and has not been called into \n\
+ // yet, we need to do something to force the class to initialize itself. \n\
+ // Then the call to object_getClass will actually return the correct class, either the class \n\
+ // if object is a class instance, or the meta-class if it is a class pointer. \n\
+ void *class_ptr = (void *) [(id) object class]; \n\
+ return_struct.class_addr = (id) object_getClass((id) object); \n\
+ if (debug) \n\
+ { \n\
+ if (class_ptr == object) \n\
+ { \n\
+ printf (\"Found a class object, need to return the meta class %p -> %p\\n\", \n\
+ class_ptr, return_struct.class_addr); \n\
+ } \n\
+ else \n\
+ { \n\
+ printf (\"[object class] returned: %p object_getClass: %p.\\n\", \n\
+ class_ptr, return_struct.class_addr); \n\
+ } \n\
+ } \n\
+ } \n\
+ \n\
+ if (is_fixup) \n\
+ { \n\
+ if (is_fixed) \n\
+ { \n\
+ return_struct.sel_addr = ((__lldb_msg_ref *) sel)->sel; \n\
+ } \n\
+ else \n\
+ { \n\
+ char *sel_name = (char *) ((__lldb_msg_ref *) sel)->sel; \n\
+ return_struct.sel_addr = sel_getUid (sel_name); \n\
+ if (debug) \n\
+ printf (\"\\n*** Got fixed up selector: %p for name %s.\\n\",\n\
+ return_struct.sel_addr, sel_name); \n\
+ } \n\
+ } \n\
+ else \n\
+ { \n\
+ return_struct.sel_addr = sel; \n\
+ } \n\
+ \n\
+ return_struct.impl_addr = \n\
+ class_getMethodImplementation (return_struct.class_addr, \n\
+ return_struct.sel_addr); \n\
+ if (debug) \n\
+ printf (\"\\n*** Returning implementation: 0x%p.\\n\", \n\
+ return_struct.impl_addr); \n\
+ \n\
+ return return_struct.impl_addr; \n\
+} \n\
+";
+
+AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::VTableRegion(
+ AppleObjCVTables *owner, lldb::addr_t header_addr)
+ : m_valid(true), m_owner(owner), m_header_addr(header_addr),
+ m_code_start_addr(0), m_code_end_addr(0), m_next_region(0) {
+ SetUpRegion();
+}
+
+AppleObjCTrampolineHandler::~AppleObjCTrampolineHandler() {}
+
+void AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::SetUpRegion() {
+ // The header looks like:
+ //
+ // uint16_t headerSize
+ // uint16_t descSize
+ // uint32_t descCount
+ // void * next
+ //
+ // First read in the header:
+
+ char memory_buffer[16];
+ ProcessSP process_sp = m_owner->GetProcessSP();
+ if (!process_sp)
+ return;
+ DataExtractor data(memory_buffer, sizeof(memory_buffer),
+ process_sp->GetByteOrder(),
+ process_sp->GetAddressByteSize());
+ size_t actual_size = 8 + process_sp->GetAddressByteSize();
+ Status error;
+ size_t bytes_read =
+ process_sp->ReadMemory(m_header_addr, memory_buffer, actual_size, error);
+ if (bytes_read != actual_size) {
+ m_valid = false;
+ return;
+ }
+
+ lldb::offset_t offset = 0;
+ const uint16_t header_size = data.GetU16(&offset);
+ const uint16_t descriptor_size = data.GetU16(&offset);
+ const size_t num_descriptors = data.GetU32(&offset);
+
+ m_next_region = data.GetPointer(&offset);
+
+ // If the header size is 0, that means we've come in too early before this
+ // data is set up.
+ // Set ourselves as not valid, and continue.
+ if (header_size == 0 || num_descriptors == 0) {
+ m_valid = false;
+ return;
+ }
+
+ // Now read in all the descriptors:
+ // The descriptor looks like:
+ //
+ // uint32_t offset
+ // uint32_t flags
+ //
+ // Where offset is either 0 - in which case it is unused, or it is
+ // the offset of the vtable code from the beginning of the
+ // descriptor record. Below, we'll convert that into an absolute
+ // code address, since I don't want to have to compute it over and
+ // over.
+
+ // Ingest the whole descriptor array:
+ const lldb::addr_t desc_ptr = m_header_addr + header_size;
+ const size_t desc_array_size = num_descriptors * descriptor_size;
+ DataBufferSP data_sp(new DataBufferHeap(desc_array_size, '\0'));
+ uint8_t *dst = (uint8_t *)data_sp->GetBytes();
+
+ DataExtractor desc_extractor(dst, desc_array_size, process_sp->GetByteOrder(),
+ process_sp->GetAddressByteSize());
+ bytes_read = process_sp->ReadMemory(desc_ptr, dst, desc_array_size, error);
+ if (bytes_read != desc_array_size) {
+ m_valid = false;
+ return;
+ }
+
+ // The actual code for the vtables will be laid out consecutively, so I also
+ // compute the start and end of the whole code block.
+
+ offset = 0;
+ m_code_start_addr = 0;
+ m_code_end_addr = 0;
+
+ for (size_t i = 0; i < num_descriptors; i++) {
+ lldb::addr_t start_offset = offset;
+ uint32_t voffset = desc_extractor.GetU32(&offset);
+ uint32_t flags = desc_extractor.GetU32(&offset);
+ lldb::addr_t code_addr = desc_ptr + start_offset + voffset;
+ m_descriptors.push_back(VTableDescriptor(flags, code_addr));
+
+ if (m_code_start_addr == 0 || code_addr < m_code_start_addr)
+ m_code_start_addr = code_addr;
+ if (code_addr > m_code_end_addr)
+ m_code_end_addr = code_addr;
+
+ offset = start_offset + descriptor_size;
+ }
+ // Finally, a little bird told me that all the vtable code blocks
+ // are the same size. Let's compute the blocks and if they are all
+ // the same add the size to the code end address:
+ lldb::addr_t code_size = 0;
+ bool all_the_same = true;
+ for (size_t i = 0; i < num_descriptors - 1; i++) {
+ lldb::addr_t this_size =
+ m_descriptors[i + 1].code_start - m_descriptors[i].code_start;
+ if (code_size == 0)
+ code_size = this_size;
+ else {
+ if (this_size != code_size)
+ all_the_same = false;
+ if (this_size > code_size)
+ code_size = this_size;
+ }
+ }
+ if (all_the_same)
+ m_code_end_addr += code_size;
+}
+
+bool AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::
+ AddressInRegion(lldb::addr_t addr, uint32_t &flags) {
+ if (!IsValid())
+ return false;
+
+ if (addr < m_code_start_addr || addr > m_code_end_addr)
+ return false;
+
+ std::vector<VTableDescriptor>::iterator pos, end = m_descriptors.end();
+ for (pos = m_descriptors.begin(); pos != end; pos++) {
+ if (addr <= (*pos).code_start) {
+ flags = (*pos).flags;
+ return true;
+ }
+ }
+ return false;
+}
+
+void AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::Dump(
+ Stream &s) {
+ s.Printf("Header addr: 0x%" PRIx64 " Code start: 0x%" PRIx64
+ " Code End: 0x%" PRIx64 " Next: 0x%" PRIx64 "\n",
+ m_header_addr, m_code_start_addr, m_code_end_addr, m_next_region);
+ size_t num_elements = m_descriptors.size();
+ for (size_t i = 0; i < num_elements; i++) {
+ s.Indent();
+ s.Printf("Code start: 0x%" PRIx64 " Flags: %d\n",
+ m_descriptors[i].code_start, m_descriptors[i].flags);
+ }
+}
+
+AppleObjCTrampolineHandler::AppleObjCVTables::AppleObjCVTables(
+ const ProcessSP &process_sp, const ModuleSP &objc_module_sp)
+ : m_process_wp(), m_trampoline_header(LLDB_INVALID_ADDRESS),
+ m_trampolines_changed_bp_id(LLDB_INVALID_BREAK_ID),
+ m_objc_module_sp(objc_module_sp) {
+ if (process_sp)
+ m_process_wp = process_sp;
+}
+
+AppleObjCTrampolineHandler::AppleObjCVTables::~AppleObjCVTables() {
+ ProcessSP process_sp = GetProcessSP();
+ if (process_sp) {
+ if (m_trampolines_changed_bp_id != LLDB_INVALID_BREAK_ID)
+ process_sp->GetTarget().RemoveBreakpointByID(m_trampolines_changed_bp_id);
+ }
+}
+
+bool AppleObjCTrampolineHandler::AppleObjCVTables::InitializeVTableSymbols() {
+ if (m_trampoline_header != LLDB_INVALID_ADDRESS)
+ return true;
+
+ ProcessSP process_sp = GetProcessSP();
+ if (process_sp) {
+ Target &target = process_sp->GetTarget();
+
+ const ModuleList &target_modules = target.GetImages();
+ std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
+ size_t num_modules = target_modules.GetSize();
+ if (!m_objc_module_sp) {
+ for (size_t i = 0; i < num_modules; i++) {
+ if (ObjCLanguageRuntime::Get(*process_sp)
+ ->IsModuleObjCLibrary(
+ target_modules.GetModuleAtIndexUnlocked(i))) {
+ m_objc_module_sp = target_modules.GetModuleAtIndexUnlocked(i);
+ break;
+ }
+ }
+ }
+
+ if (m_objc_module_sp) {
+ ConstString trampoline_name("gdb_objc_trampolines");
+ const Symbol *trampoline_symbol =
+ m_objc_module_sp->FindFirstSymbolWithNameAndType(trampoline_name,
+ eSymbolTypeData);
+ if (trampoline_symbol != nullptr) {
+ m_trampoline_header = trampoline_symbol->GetLoadAddress(&target);
+ if (m_trampoline_header == LLDB_INVALID_ADDRESS)
+ return false;
+
+ // Next look up the "changed" symbol and set a breakpoint on that...
+ ConstString changed_name("gdb_objc_trampolines_changed");
+ const Symbol *changed_symbol =
+ m_objc_module_sp->FindFirstSymbolWithNameAndType(changed_name,
+ eSymbolTypeCode);
+ if (changed_symbol != nullptr) {
+ const Address changed_symbol_addr = changed_symbol->GetAddress();
+ if (!changed_symbol_addr.IsValid())
+ return false;
+
+ lldb::addr_t changed_addr =
+ changed_symbol_addr.GetOpcodeLoadAddress(&target);
+ if (changed_addr != LLDB_INVALID_ADDRESS) {
+ BreakpointSP trampolines_changed_bp_sp =
+ target.CreateBreakpoint(changed_addr, true, false);
+ if (trampolines_changed_bp_sp) {
+ m_trampolines_changed_bp_id = trampolines_changed_bp_sp->GetID();
+ trampolines_changed_bp_sp->SetCallback(RefreshTrampolines, this,
+ true);
+ trampolines_changed_bp_sp->SetBreakpointKind(
+ "objc-trampolines-changed");
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool AppleObjCTrampolineHandler::AppleObjCVTables::RefreshTrampolines(
+ void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id) {
+ AppleObjCVTables *vtable_handler = (AppleObjCVTables *)baton;
+ if (vtable_handler->InitializeVTableSymbols()) {
+ // The Update function is called with the address of an added region. So we
+ // grab that address, and
+ // feed it into ReadRegions. Of course, our friend the ABI will get the
+ // values for us.
+ ExecutionContext exe_ctx(context->exe_ctx_ref);
+ Process *process = exe_ctx.GetProcessPtr();
+ const ABI *abi = process->GetABI().get();
+
+ ClangASTContext *clang_ast_context =
+ process->GetTarget().GetScratchClangASTContext();
+ ValueList argument_values;
+ Value input_value;
+ CompilerType clang_void_ptr_type =
+ clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
+
+ input_value.SetValueType(Value::eValueTypeScalar);
+ // input_value.SetContext (Value::eContextTypeClangType,
+ // clang_void_ptr_type);
+ input_value.SetCompilerType(clang_void_ptr_type);
+ argument_values.PushValue(input_value);
+
+ bool success =
+ abi->GetArgumentValues(exe_ctx.GetThreadRef(), argument_values);
+ if (!success)
+ return false;
+
+ // Now get a pointer value from the zeroth argument.
+ Status error;
+ DataExtractor data;
+ error = argument_values.GetValueAtIndex(0)->GetValueAsData(&exe_ctx, data,
+ 0, nullptr);
+ lldb::offset_t offset = 0;
+ lldb::addr_t region_addr = data.GetPointer(&offset);
+
+ if (region_addr != 0)
+ vtable_handler->ReadRegions(region_addr);
+ }
+ return false;
+}
+
+bool AppleObjCTrampolineHandler::AppleObjCVTables::ReadRegions() {
+ // The no argument version reads the start region from the value of
+ // the gdb_regions_header, and gets started from there.
+
+ m_regions.clear();
+ if (!InitializeVTableSymbols())
+ return false;
+ Status error;
+ ProcessSP process_sp = GetProcessSP();
+ if (process_sp) {
+ lldb::addr_t region_addr =
+ process_sp->ReadPointerFromMemory(m_trampoline_header, error);
+ if (error.Success())
+ return ReadRegions(region_addr);
+ }
+ return false;
+}
+
+bool AppleObjCTrampolineHandler::AppleObjCVTables::ReadRegions(
+ lldb::addr_t region_addr) {
+ ProcessSP process_sp = GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ // We aren't starting at the trampoline symbol.
+ InitializeVTableSymbols();
+ lldb::addr_t next_region = region_addr;
+
+ // Read in the sizes of the headers.
+ while (next_region != 0) {
+ m_regions.push_back(VTableRegion(this, next_region));
+ if (!m_regions.back().IsValid()) {
+ m_regions.clear();
+ return false;
+ }
+ if (log) {
+ StreamString s;
+ m_regions.back().Dump(s);
+ log->Printf("Read vtable region: \n%s", s.GetData());
+ }
+
+ next_region = m_regions.back().GetNextRegionAddr();
+ }
+
+ return true;
+}
+
+bool AppleObjCTrampolineHandler::AppleObjCVTables::IsAddressInVTables(
+ lldb::addr_t addr, uint32_t &flags) {
+ region_collection::iterator pos, end = m_regions.end();
+ for (pos = m_regions.begin(); pos != end; pos++) {
+ if ((*pos).AddressInRegion(addr, flags))
+ return true;
+ }
+ return false;
+}
+
+const AppleObjCTrampolineHandler::DispatchFunction
+ AppleObjCTrampolineHandler::g_dispatch_functions[] = {
+ // NAME STRET SUPER SUPER2 FIXUP TYPE
+ {"objc_msgSend", false, false, false, DispatchFunction::eFixUpNone},
+ {"objc_msgSend_fixup", false, false, false,
+ DispatchFunction::eFixUpToFix},
+ {"objc_msgSend_fixedup", false, false, false,
+ DispatchFunction::eFixUpFixed},
+ {"objc_msgSend_stret", true, false, false,
+ DispatchFunction::eFixUpNone},
+ {"objc_msgSend_stret_fixup", true, false, false,
+ DispatchFunction::eFixUpToFix},
+ {"objc_msgSend_stret_fixedup", true, false, false,
+ DispatchFunction::eFixUpFixed},
+ {"objc_msgSend_fpret", false, false, false,
+ DispatchFunction::eFixUpNone},
+ {"objc_msgSend_fpret_fixup", false, false, false,
+ DispatchFunction::eFixUpToFix},
+ {"objc_msgSend_fpret_fixedup", false, false, false,
+ DispatchFunction::eFixUpFixed},
+ {"objc_msgSend_fp2ret", false, false, true,
+ DispatchFunction::eFixUpNone},
+ {"objc_msgSend_fp2ret_fixup", false, false, true,
+ DispatchFunction::eFixUpToFix},
+ {"objc_msgSend_fp2ret_fixedup", false, false, true,
+ DispatchFunction::eFixUpFixed},
+ {"objc_msgSendSuper", false, true, false, DispatchFunction::eFixUpNone},
+ {"objc_msgSendSuper_stret", true, true, false,
+ DispatchFunction::eFixUpNone},
+ {"objc_msgSendSuper2", false, true, true, DispatchFunction::eFixUpNone},
+ {"objc_msgSendSuper2_fixup", false, true, true,
+ DispatchFunction::eFixUpToFix},
+ {"objc_msgSendSuper2_fixedup", false, true, true,
+ DispatchFunction::eFixUpFixed},
+ {"objc_msgSendSuper2_stret", true, true, true,
+ DispatchFunction::eFixUpNone},
+ {"objc_msgSendSuper2_stret_fixup", true, true, true,
+ DispatchFunction::eFixUpToFix},
+ {"objc_msgSendSuper2_stret_fixedup", true, true, true,
+ DispatchFunction::eFixUpFixed},
+};
+
+AppleObjCTrampolineHandler::AppleObjCTrampolineHandler(
+ const ProcessSP &process_sp, const ModuleSP &objc_module_sp)
+ : m_process_wp(), m_objc_module_sp(objc_module_sp),
+ m_lookup_implementation_function_code(nullptr),
+ m_impl_fn_addr(LLDB_INVALID_ADDRESS),
+ m_impl_stret_fn_addr(LLDB_INVALID_ADDRESS),
+ m_msg_forward_addr(LLDB_INVALID_ADDRESS) {
+ if (process_sp)
+ m_process_wp = process_sp;
+ // Look up the known resolution functions:
+
+ ConstString get_impl_name("class_getMethodImplementation");
+ ConstString get_impl_stret_name("class_getMethodImplementation_stret");
+ ConstString msg_forward_name("_objc_msgForward");
+ ConstString msg_forward_stret_name("_objc_msgForward_stret");
+
+ Target *target = process_sp ? &process_sp->GetTarget() : nullptr;
+ const Symbol *class_getMethodImplementation =
+ m_objc_module_sp->FindFirstSymbolWithNameAndType(get_impl_name,
+ eSymbolTypeCode);
+ const Symbol *class_getMethodImplementation_stret =
+ m_objc_module_sp->FindFirstSymbolWithNameAndType(get_impl_stret_name,
+ eSymbolTypeCode);
+ const Symbol *msg_forward = m_objc_module_sp->FindFirstSymbolWithNameAndType(
+ msg_forward_name, eSymbolTypeCode);
+ const Symbol *msg_forward_stret =
+ m_objc_module_sp->FindFirstSymbolWithNameAndType(msg_forward_stret_name,
+ eSymbolTypeCode);
+
+ if (class_getMethodImplementation)
+ m_impl_fn_addr =
+ class_getMethodImplementation->GetAddress().GetOpcodeLoadAddress(
+ target);
+ if (class_getMethodImplementation_stret)
+ m_impl_stret_fn_addr =
+ class_getMethodImplementation_stret->GetAddress().GetOpcodeLoadAddress(
+ target);
+ if (msg_forward)
+ m_msg_forward_addr = msg_forward->GetAddress().GetOpcodeLoadAddress(target);
+ if (msg_forward_stret)
+ m_msg_forward_stret_addr =
+ msg_forward_stret->GetAddress().GetOpcodeLoadAddress(target);
+
+ // FIXME: Do some kind of logging here.
+ if (m_impl_fn_addr == LLDB_INVALID_ADDRESS) {
+ // If we can't even find the ordinary get method implementation function,
+ // then we aren't going to be able to
+ // step through any method dispatches. Warn to that effect and get out of
+ // here.
+ if (process_sp->CanJIT()) {
+ process_sp->GetTarget().GetDebugger().GetErrorFile()->Printf(
+ "Could not find implementation lookup function \"%s\""
+ " step in through ObjC method dispatch will not work.\n",
+ get_impl_name.AsCString());
+ }
+ return;
+ } else if (m_impl_stret_fn_addr == LLDB_INVALID_ADDRESS) {
+ // It there is no stret return lookup function, assume that it is the same
+ // as the straight lookup:
+ m_impl_stret_fn_addr = m_impl_fn_addr;
+ // Also we will use the version of the lookup code that doesn't rely on the
+ // stret version of the function.
+ m_lookup_implementation_function_code =
+ g_lookup_implementation_no_stret_function_code;
+ } else {
+ m_lookup_implementation_function_code =
+ g_lookup_implementation_with_stret_function_code;
+ }
+
+ // Look up the addresses for the objc dispatch functions and cache
+ // them. For now I'm inspecting the symbol names dynamically to
+ // figure out how to dispatch to them. If it becomes more
+ // complicated than this we can turn the g_dispatch_functions char *
+ // array into a template table, and populate the DispatchFunction
+ // map from there.
+
+ for (size_t i = 0; i != llvm::array_lengthof(g_dispatch_functions); i++) {
+ ConstString name_const_str(g_dispatch_functions[i].name);
+ const Symbol *msgSend_symbol =
+ m_objc_module_sp->FindFirstSymbolWithNameAndType(name_const_str,
+ eSymbolTypeCode);
+ if (msgSend_symbol && msgSend_symbol->ValueIsAddress()) {
+ // FIXME: Make g_dispatch_functions static table of
+ // DispatchFunctions, and have the map be address->index.
+ // Problem is we also need to lookup the dispatch function. For
+ // now we could have a side table of stret & non-stret dispatch
+ // functions. If that's as complex as it gets, we're fine.
+
+ lldb::addr_t sym_addr =
+ msgSend_symbol->GetAddressRef().GetOpcodeLoadAddress(target);
+
+ m_msgSend_map.insert(std::pair<lldb::addr_t, int>(sym_addr, i));
+ }
+ }
+
+ // Build our vtable dispatch handler here:
+ m_vtables_up.reset(new AppleObjCVTables(process_sp, m_objc_module_sp));
+ if (m_vtables_up)
+ m_vtables_up->ReadRegions();
+}
+
+lldb::addr_t
+AppleObjCTrampolineHandler::SetupDispatchFunction(Thread &thread,
+ ValueList &dispatch_values) {
+ ThreadSP thread_sp(thread.shared_from_this());
+ ExecutionContext exe_ctx(thread_sp);
+ DiagnosticManager diagnostics;
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ lldb::addr_t args_addr = LLDB_INVALID_ADDRESS;
+ FunctionCaller *impl_function_caller = nullptr;
+
+ // Scope for mutex locker:
+ {
+ std::lock_guard<std::mutex> guard(m_impl_function_mutex);
+
+ // First stage is to make the ClangUtility to hold our injected function:
+
+ if (!m_impl_code) {
+ if (m_lookup_implementation_function_code != nullptr) {
+ Status error;
+ m_impl_code.reset(exe_ctx.GetTargetRef().GetUtilityFunctionForLanguage(
+ m_lookup_implementation_function_code, eLanguageTypeObjC,
+ g_lookup_implementation_function_name, error));
+ if (error.Fail()) {
+ if (log)
+ log->Printf(
+ "Failed to get Utility Function for implementation lookup: %s.",
+ error.AsCString());
+ m_impl_code.reset();
+ return args_addr;
+ }
+
+ if (!m_impl_code->Install(diagnostics, exe_ctx)) {
+ if (log) {
+ log->Printf("Failed to install implementation lookup.");
+ diagnostics.Dump(log);
+ }
+ m_impl_code.reset();
+ return args_addr;
+ }
+ } else {
+ if (log)
+ log->Printf("No method lookup implementation code.");
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ // Next make the runner function for our implementation utility function.
+ ClangASTContext *clang_ast_context =
+ thread.GetProcess()->GetTarget().GetScratchClangASTContext();
+ CompilerType clang_void_ptr_type =
+ clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
+ Status error;
+
+ impl_function_caller = m_impl_code->MakeFunctionCaller(
+ clang_void_ptr_type, dispatch_values, thread_sp, error);
+ if (error.Fail()) {
+ if (log)
+ log->Printf(
+ "Error getting function caller for dispatch lookup: \"%s\".",
+ error.AsCString());
+ return args_addr;
+ }
+ } else {
+ impl_function_caller = m_impl_code->GetFunctionCaller();
+ }
+ }
+
+ diagnostics.Clear();
+
+ // Now write down the argument values for this particular call.
+ // This looks like it might be a race condition if other threads
+ // were calling into here, but actually it isn't because we allocate
+ // a new args structure for this call by passing args_addr =
+ // LLDB_INVALID_ADDRESS...
+
+ if (!impl_function_caller->WriteFunctionArguments(
+ exe_ctx, args_addr, dispatch_values, diagnostics)) {
+ if (log) {
+ log->Printf("Error writing function arguments.");
+ diagnostics.Dump(log);
+ }
+ return args_addr;
+ }
+
+ return args_addr;
+}
+
+ThreadPlanSP
+AppleObjCTrampolineHandler::GetStepThroughDispatchPlan(Thread &thread,
+ bool stop_others) {
+ ThreadPlanSP ret_plan_sp;
+ lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC();
+
+ DispatchFunction this_dispatch;
+ bool found_it = false;
+
+ // First step is to look and see if we are in one of the known ObjC
+ // dispatch functions. We've already compiled a table of same, so
+ // consult it.
+
+ MsgsendMap::iterator pos;
+ pos = m_msgSend_map.find(curr_pc);
+ if (pos != m_msgSend_map.end()) {
+ this_dispatch = g_dispatch_functions[(*pos).second];
+ found_it = true;
+ }
+
+ // Next check to see if we are in a vtable region:
+
+ if (!found_it) {
+ uint32_t flags;
+ if (m_vtables_up) {
+ found_it = m_vtables_up->IsAddressInVTables(curr_pc, flags);
+ if (found_it) {
+ this_dispatch.name = "vtable";
+ this_dispatch.stret_return =
+ (flags & AppleObjCVTables::eOBJC_TRAMPOLINE_STRET) ==
+ AppleObjCVTables::eOBJC_TRAMPOLINE_STRET;
+ this_dispatch.is_super = false;
+ this_dispatch.is_super2 = false;
+ this_dispatch.fixedup = DispatchFunction::eFixUpFixed;
+ }
+ }
+ }
+
+ if (found_it) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+ // We are decoding a method dispatch. First job is to pull the
+ // arguments out:
+
+ lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0);
+
+ const ABI *abi = nullptr;
+ ProcessSP process_sp(thread.CalculateProcess());
+ if (process_sp)
+ abi = process_sp->GetABI().get();
+ if (abi == nullptr)
+ return ret_plan_sp;
+
+ TargetSP target_sp(thread.CalculateTarget());
+
+ ClangASTContext *clang_ast_context = target_sp->GetScratchClangASTContext();
+ ValueList argument_values;
+ Value void_ptr_value;
+ CompilerType clang_void_ptr_type =
+ clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
+ void_ptr_value.SetValueType(Value::eValueTypeScalar);
+ // void_ptr_value.SetContext (Value::eContextTypeClangType,
+ // clang_void_ptr_type);
+ void_ptr_value.SetCompilerType(clang_void_ptr_type);
+
+ int obj_index;
+ int sel_index;
+
+ // If this is a struct return dispatch, then the first argument is
+ // the return struct pointer, and the object is the second, and
+ // the selector is the third. Otherwise the object is the first
+ // and the selector the second.
+ if (this_dispatch.stret_return) {
+ obj_index = 1;
+ sel_index = 2;
+ argument_values.PushValue(void_ptr_value);
+ argument_values.PushValue(void_ptr_value);
+ argument_values.PushValue(void_ptr_value);
+ } else {
+ obj_index = 0;
+ sel_index = 1;
+ argument_values.PushValue(void_ptr_value);
+ argument_values.PushValue(void_ptr_value);
+ }
+
+ bool success = abi->GetArgumentValues(thread, argument_values);
+ if (!success)
+ return ret_plan_sp;
+
+ lldb::addr_t obj_addr =
+ argument_values.GetValueAtIndex(obj_index)->GetScalar().ULongLong();
+ if (obj_addr == 0x0) {
+ if (log)
+ log->Printf(
+ "Asked to step to dispatch to nil object, returning empty plan.");
+ return ret_plan_sp;
+ }
+
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ Process *process = exe_ctx.GetProcessPtr();
+ // isa_addr will store the class pointer that the method is being
+ // dispatched to - so either the class directly or the super class
+ // if this is one of the objc_msgSendSuper flavors. That's mostly
+ // used to look up the class/selector pair in our cache.
+
+ lldb::addr_t isa_addr = LLDB_INVALID_ADDRESS;
+ lldb::addr_t sel_addr =
+ argument_values.GetValueAtIndex(sel_index)->GetScalar().ULongLong();
+
+ // Figure out the class this is being dispatched to and see if
+ // we've already cached this method call, If so we can push a
+ // run-to-address plan directly. Otherwise we have to figure out
+ // where the implementation lives.
+
+ if (this_dispatch.is_super) {
+ if (this_dispatch.is_super2) {
+ // In the objc_msgSendSuper2 case, we don't get the object
+ // directly, we get a structure containing the object and the
+ // class to which the super message is being sent. So we need
+ // to dig the super out of the class and use that.
+
+ Value super_value(*(argument_values.GetValueAtIndex(obj_index)));
+ super_value.GetScalar() += process->GetAddressByteSize();
+ super_value.ResolveValue(&exe_ctx);
+
+ if (super_value.GetScalar().IsValid()) {
+
+ // isa_value now holds the class pointer. The second word of the
+ // class pointer is the super-class pointer:
+ super_value.GetScalar() += process->GetAddressByteSize();
+ super_value.ResolveValue(&exe_ctx);
+ if (super_value.GetScalar().IsValid())
+ isa_addr = super_value.GetScalar().ULongLong();
+ else {
+ if (log)
+ log->Printf("Failed to extract the super class value from the "
+ "class in objc_super.");
+ }
+ } else {
+ if (log)
+ log->Printf("Failed to extract the class value from objc_super.");
+ }
+ } else {
+ // In the objc_msgSendSuper case, we don't get the object
+ // directly, we get a two element structure containing the
+ // object and the super class to which the super message is
+ // being sent. So the class we want is the second element of
+ // this structure.
+
+ Value super_value(*(argument_values.GetValueAtIndex(obj_index)));
+ super_value.GetScalar() += process->GetAddressByteSize();
+ super_value.ResolveValue(&exe_ctx);
+
+ if (super_value.GetScalar().IsValid()) {
+ isa_addr = super_value.GetScalar().ULongLong();
+ } else {
+ if (log)
+ log->Printf("Failed to extract the class value from objc_super.");
+ }
+ }
+ } else {
+ // In the direct dispatch case, the object->isa is the class pointer we
+ // want.
+
+ // This is a little cheesy, but since object->isa is the first field,
+ // making the object value a load address value and resolving it will get
+ // the pointer sized data pointed to by that value...
+
+ // Note, it isn't a fatal error not to be able to get the
+ // address from the object, since this might be a "tagged
+ // pointer" which isn't a real object, but rather some word
+ // length encoded dingus.
+
+ Value isa_value(*(argument_values.GetValueAtIndex(obj_index)));
+
+ isa_value.SetValueType(Value::eValueTypeLoadAddress);
+ isa_value.ResolveValue(&exe_ctx);
+ if (isa_value.GetScalar().IsValid()) {
+ isa_addr = isa_value.GetScalar().ULongLong();
+ } else {
+ if (log)
+ log->Printf("Failed to extract the isa value from object.");
+ }
+ }
+
+ // Okay, we've got the address of the class for which we're resolving this,
+ // let's see if it's in our cache:
+ lldb::addr_t impl_addr = LLDB_INVALID_ADDRESS;
+
+ if (isa_addr != LLDB_INVALID_ADDRESS) {
+ if (log) {
+ log->Printf("Resolving call for class - 0x%" PRIx64
+ " and selector - 0x%" PRIx64,
+ isa_addr, sel_addr);
+ }
+ ObjCLanguageRuntime *objc_runtime =
+ ObjCLanguageRuntime::Get(*thread.GetProcess());
+ assert(objc_runtime != nullptr);
+
+ impl_addr = objc_runtime->LookupInMethodCache(isa_addr, sel_addr);
+ }
+
+ if (impl_addr != LLDB_INVALID_ADDRESS) {
+ // Yup, it was in the cache, so we can run to that address directly.
+
+ if (log)
+ log->Printf("Found implementation address in cache: 0x%" PRIx64,
+ impl_addr);
+
+ ret_plan_sp = std::make_shared<ThreadPlanRunToAddress>(thread, impl_addr,
+ stop_others);
+ } else {
+ // We haven't seen this class/selector pair yet. Look it up.
+ StreamString errors;
+ Address impl_code_address;
+
+ ValueList dispatch_values;
+
+ // We've will inject a little function in the target that takes the
+ // object, selector and some flags,
+ // and figures out the implementation. Looks like:
+ // void *__lldb_objc_find_implementation_for_selector (void *object,
+ // void *sel,
+ // int is_stret,
+ // int is_super,
+ // int is_super2,
+ // int is_fixup,
+ // int is_fixed,
+ // int debug)
+ // So set up the arguments for that call.
+
+ dispatch_values.PushValue(*(argument_values.GetValueAtIndex(obj_index)));
+ dispatch_values.PushValue(*(argument_values.GetValueAtIndex(sel_index)));
+
+ Value flag_value;
+ CompilerType clang_int_type =
+ clang_ast_context->GetBuiltinTypeForEncodingAndBitSize(
+ lldb::eEncodingSint, 32);
+ flag_value.SetValueType(Value::eValueTypeScalar);
+ // flag_value.SetContext (Value::eContextTypeClangType, clang_int_type);
+ flag_value.SetCompilerType(clang_int_type);
+
+ if (this_dispatch.stret_return)
+ flag_value.GetScalar() = 1;
+ else
+ flag_value.GetScalar() = 0;
+ dispatch_values.PushValue(flag_value);
+
+ if (this_dispatch.is_super)
+ flag_value.GetScalar() = 1;
+ else
+ flag_value.GetScalar() = 0;
+ dispatch_values.PushValue(flag_value);
+
+ if (this_dispatch.is_super2)
+ flag_value.GetScalar() = 1;
+ else
+ flag_value.GetScalar() = 0;
+ dispatch_values.PushValue(flag_value);
+
+ switch (this_dispatch.fixedup) {
+ case DispatchFunction::eFixUpNone:
+ flag_value.GetScalar() = 0;
+ dispatch_values.PushValue(flag_value);
+ dispatch_values.PushValue(flag_value);
+ break;
+ case DispatchFunction::eFixUpFixed:
+ flag_value.GetScalar() = 1;
+ dispatch_values.PushValue(flag_value);
+ flag_value.GetScalar() = 1;
+ dispatch_values.PushValue(flag_value);
+ break;
+ case DispatchFunction::eFixUpToFix:
+ flag_value.GetScalar() = 1;
+ dispatch_values.PushValue(flag_value);
+ flag_value.GetScalar() = 0;
+ dispatch_values.PushValue(flag_value);
+ break;
+ }
+ if (log && log->GetVerbose())
+ flag_value.GetScalar() = 1;
+ else
+ flag_value.GetScalar() = 0; // FIXME - Set to 0 when debugging is done.
+ dispatch_values.PushValue(flag_value);
+
+ // The step through code might have to fill in the cache, so it
+ // is not safe to run only one thread. So we override the
+ // stop_others value passed in to us here:
+ const bool trampoline_stop_others = false;
+ ret_plan_sp = std::make_shared<AppleThreadPlanStepThroughObjCTrampoline>(
+ thread, this, dispatch_values, isa_addr, sel_addr,
+ trampoline_stop_others);
+ if (log) {
+ StreamString s;
+ ret_plan_sp->GetDescription(&s, eDescriptionLevelFull);
+ log->Printf("Using ObjC step plan: %s.\n", s.GetData());
+ }
+ }
+ }
+
+ return ret_plan_sp;
+}
+
+FunctionCaller *
+AppleObjCTrampolineHandler::GetLookupImplementationFunctionCaller() {
+ return m_impl_code->GetFunctionCaller();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.h
new file mode 100644
index 000000000000..d120d671eeb3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.h
@@ -0,0 +1,158 @@
+//===-- AppleObjCTrampolineHandler.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_AppleObjCTrampolineHandler_h_
+#define lldb_AppleObjCTrampolineHandler_h_
+
+#include <map>
+#include <mutex>
+#include <vector>
+
+#include "lldb/Expression/UtilityFunction.h"
+#include "lldb/lldb-public.h"
+
+namespace lldb_private {
+
+class AppleObjCTrampolineHandler {
+public:
+ AppleObjCTrampolineHandler(const lldb::ProcessSP &process_sp,
+ const lldb::ModuleSP &objc_module_sp);
+
+ ~AppleObjCTrampolineHandler();
+
+ lldb::ThreadPlanSP GetStepThroughDispatchPlan(Thread &thread,
+ bool stop_others);
+
+ FunctionCaller *GetLookupImplementationFunctionCaller();
+
+ bool AddrIsMsgForward(lldb::addr_t addr) const {
+ return (addr == m_msg_forward_addr || addr == m_msg_forward_stret_addr);
+ }
+
+ struct DispatchFunction {
+ public:
+ enum FixUpState { eFixUpNone, eFixUpFixed, eFixUpToFix };
+
+ const char *name;
+ bool stret_return;
+ bool is_super;
+ bool is_super2;
+ FixUpState fixedup;
+ };
+
+ lldb::addr_t SetupDispatchFunction(Thread &thread,
+ ValueList &dispatch_values);
+
+private:
+ static const char *g_lookup_implementation_function_name;
+ static const char *g_lookup_implementation_with_stret_function_code;
+ static const char *g_lookup_implementation_no_stret_function_code;
+
+ class AppleObjCVTables {
+ public:
+ // These come from objc-gdb.h.
+ enum VTableFlags {
+ eOBJC_TRAMPOLINE_MESSAGE = (1 << 0), // trampoline acts like objc_msgSend
+ eOBJC_TRAMPOLINE_STRET = (1 << 1), // trampoline is struct-returning
+ eOBJC_TRAMPOLINE_VTABLE = (1 << 2) // trampoline is vtable dispatcher
+ };
+
+ private:
+ struct VTableDescriptor {
+ VTableDescriptor(uint32_t in_flags, lldb::addr_t in_code_start)
+ : flags(in_flags), code_start(in_code_start) {}
+
+ uint32_t flags;
+ lldb::addr_t code_start;
+ };
+
+ class VTableRegion {
+ public:
+ VTableRegion()
+ : m_valid(false), m_owner(nullptr),
+ m_header_addr(LLDB_INVALID_ADDRESS), m_code_start_addr(0),
+ m_code_end_addr(0), m_next_region(0) {}
+
+ VTableRegion(AppleObjCVTables *owner, lldb::addr_t header_addr);
+
+ void SetUpRegion();
+
+ lldb::addr_t GetNextRegionAddr() { return m_next_region; }
+
+ lldb::addr_t GetCodeStart() { return m_code_start_addr; }
+
+ lldb::addr_t GetCodeEnd() { return m_code_end_addr; }
+
+ uint32_t GetFlagsForVTableAtAddress(lldb::addr_t address) { return 0; }
+
+ bool IsValid() { return m_valid; }
+
+ bool AddressInRegion(lldb::addr_t addr, uint32_t &flags);
+
+ void Dump(Stream &s);
+
+ public:
+ bool m_valid;
+ AppleObjCVTables *m_owner;
+ lldb::addr_t m_header_addr;
+ lldb::addr_t m_code_start_addr;
+ lldb::addr_t m_code_end_addr;
+ std::vector<VTableDescriptor> m_descriptors;
+ lldb::addr_t m_next_region;
+ };
+
+ public:
+ AppleObjCVTables(const lldb::ProcessSP &process_sp,
+ const lldb::ModuleSP &objc_module_sp);
+
+ ~AppleObjCVTables();
+
+ bool InitializeVTableSymbols();
+
+ static bool RefreshTrampolines(void *baton,
+ StoppointCallbackContext *context,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id);
+ bool ReadRegions();
+
+ bool ReadRegions(lldb::addr_t region_addr);
+
+ bool IsAddressInVTables(lldb::addr_t addr, uint32_t &flags);
+
+ lldb::ProcessSP GetProcessSP() { return m_process_wp.lock(); }
+
+ private:
+ lldb::ProcessWP m_process_wp;
+ typedef std::vector<VTableRegion> region_collection;
+ lldb::addr_t m_trampoline_header;
+ lldb::break_id_t m_trampolines_changed_bp_id;
+ region_collection m_regions;
+ lldb::ModuleSP m_objc_module_sp;
+ };
+
+ static const DispatchFunction g_dispatch_functions[];
+
+ typedef std::map<lldb::addr_t, int> MsgsendMap; // This table maps an dispatch
+ // fn address to the index in
+ // g_dispatch_functions
+ MsgsendMap m_msgSend_map;
+ lldb::ProcessWP m_process_wp;
+ lldb::ModuleSP m_objc_module_sp;
+ const char *m_lookup_implementation_function_code;
+ std::unique_ptr<UtilityFunction> m_impl_code;
+ std::mutex m_impl_function_mutex;
+ lldb::addr_t m_impl_fn_addr;
+ lldb::addr_t m_impl_stret_fn_addr;
+ lldb::addr_t m_msg_forward_addr;
+ lldb::addr_t m_msg_forward_stret_addr;
+ std::unique_ptr<AppleObjCVTables> m_vtables_up;
+};
+
+} // namespace lldb_private
+
+#endif // lldb_AppleObjCTrampolineHandler_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeEncodingParser.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeEncodingParser.cpp
new file mode 100644
index 000000000000..26654e9212b9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeEncodingParser.cpp
@@ -0,0 +1,381 @@
+//===-- AppleObjCTypeEncodingParser.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 "AppleObjCTypeEncodingParser.h"
+
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangUtil.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/StringLexer.h"
+
+#include <vector>
+
+using namespace lldb_private;
+using namespace lldb_utility;
+
+AppleObjCTypeEncodingParser::AppleObjCTypeEncodingParser(
+ ObjCLanguageRuntime &runtime)
+ : ObjCLanguageRuntime::EncodingToType(), m_runtime(runtime) {
+ if (!m_scratch_ast_ctx_up)
+ m_scratch_ast_ctx_up.reset(new ClangASTContext(runtime.GetProcess()
+ ->GetTarget()
+ .GetArchitecture()
+ .GetTriple()
+ .str()
+ .c_str()));
+}
+
+std::string
+AppleObjCTypeEncodingParser::ReadStructName(lldb_utility::StringLexer &type) {
+ StreamString buffer;
+ while (type.HasAtLeast(1) && type.Peek() != '=')
+ buffer.Printf("%c", type.Next());
+ return buffer.GetString();
+}
+
+std::string
+AppleObjCTypeEncodingParser::ReadQuotedString(lldb_utility::StringLexer &type) {
+ StreamString buffer;
+ while (type.HasAtLeast(1) && type.Peek() != '"')
+ buffer.Printf("%c", type.Next());
+ StringLexer::Character next = type.Next();
+ UNUSED_IF_ASSERT_DISABLED(next);
+ assert(next == '"');
+ return buffer.GetString();
+}
+
+uint32_t
+AppleObjCTypeEncodingParser::ReadNumber(lldb_utility::StringLexer &type) {
+ uint32_t total = 0;
+ while (type.HasAtLeast(1) && isdigit(type.Peek()))
+ total = 10 * total + (type.Next() - '0');
+ return total;
+}
+
+// as an extension to the published grammar recent runtimes emit structs like
+// this:
+// "{CGRect=\"origin\"{CGPoint=\"x\"d\"y\"d}\"size\"{CGSize=\"width\"d\"height\"d}}"
+
+AppleObjCTypeEncodingParser::StructElement::StructElement()
+ : name(""), type(clang::QualType()), bitfield(0) {}
+
+AppleObjCTypeEncodingParser::StructElement
+AppleObjCTypeEncodingParser::ReadStructElement(clang::ASTContext &ast_ctx,
+ lldb_utility::StringLexer &type,
+ bool for_expression) {
+ StructElement retval;
+ if (type.NextIf('"'))
+ retval.name = ReadQuotedString(type);
+ if (!type.NextIf('"'))
+ return retval;
+ uint32_t bitfield_size = 0;
+ retval.type = BuildType(ast_ctx, type, for_expression, &bitfield_size);
+ retval.bitfield = bitfield_size;
+ return retval;
+}
+
+clang::QualType
+AppleObjCTypeEncodingParser::BuildStruct(clang::ASTContext &ast_ctx,
+ lldb_utility::StringLexer &type,
+ bool for_expression) {
+ return BuildAggregate(ast_ctx, type, for_expression, '{', '}',
+ clang::TTK_Struct);
+}
+
+clang::QualType
+AppleObjCTypeEncodingParser::BuildUnion(clang::ASTContext &ast_ctx,
+ lldb_utility::StringLexer &type,
+ bool for_expression) {
+ return BuildAggregate(ast_ctx, type, for_expression, '(', ')',
+ clang::TTK_Union);
+}
+
+clang::QualType AppleObjCTypeEncodingParser::BuildAggregate(
+ clang::ASTContext &ast_ctx, lldb_utility::StringLexer &type,
+ bool for_expression, char opener, char closer, uint32_t kind) {
+ if (!type.NextIf(opener))
+ return clang::QualType();
+ std::string name(ReadStructName(type));
+
+ // We do not handle templated classes/structs at the moment. If the name has
+ // a < in it, we are going to abandon this. We're still obliged to parse it,
+ // so we just set a flag that means "Don't actually build anything."
+
+ const bool is_templated = name.find('<') != std::string::npos;
+
+ if (!type.NextIf('='))
+ return clang::QualType();
+ bool in_union = true;
+ std::vector<StructElement> elements;
+ while (in_union && type.HasAtLeast(1)) {
+ if (type.NextIf(closer)) {
+ in_union = false;
+ break;
+ } else {
+ auto element = ReadStructElement(ast_ctx, type, for_expression);
+ if (element.type.isNull())
+ break;
+ else
+ elements.push_back(element);
+ }
+ }
+ if (in_union)
+ return clang::QualType();
+
+ if (is_templated)
+ return clang::QualType(); // This is where we bail out. Sorry!
+
+ ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx);
+ if (!lldb_ctx)
+ return clang::QualType();
+ CompilerType union_type(lldb_ctx->CreateRecordType(
+ nullptr, lldb::eAccessPublic, name.c_str(), kind, lldb::eLanguageTypeC));
+ if (union_type) {
+ ClangASTContext::StartTagDeclarationDefinition(union_type);
+
+ unsigned int count = 0;
+ for (auto element : elements) {
+ if (element.name.empty()) {
+ StreamString elem_name;
+ elem_name.Printf("__unnamed_%u", count);
+ element.name = elem_name.GetString();
+ }
+ ClangASTContext::AddFieldToRecordType(
+ union_type, element.name.c_str(),
+ CompilerType(&ast_ctx, element.type), lldb::eAccessPublic,
+ element.bitfield);
+ ++count;
+ }
+ ClangASTContext::CompleteTagDeclarationDefinition(union_type);
+ }
+ return ClangUtil::GetQualType(union_type);
+}
+
+clang::QualType
+AppleObjCTypeEncodingParser::BuildArray(clang::ASTContext &ast_ctx,
+ lldb_utility::StringLexer &type,
+ bool for_expression) {
+ if (!type.NextIf('['))
+ return clang::QualType();
+ uint32_t size = ReadNumber(type);
+ clang::QualType element_type(BuildType(ast_ctx, type, for_expression));
+ if (!type.NextIf(']'))
+ return clang::QualType();
+ ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx);
+ if (!lldb_ctx)
+ return clang::QualType();
+ CompilerType array_type(lldb_ctx->CreateArrayType(
+ CompilerType(&ast_ctx, element_type), size, false));
+ return ClangUtil::GetQualType(array_type);
+}
+
+// the runtime can emit these in the form of @"SomeType", giving more specifics
+// this would be interesting for expression parser interop, but since we
+// actually try to avoid exposing the ivar info to the expression evaluator,
+// consume but ignore the type info and always return an 'id'; if anything,
+// dynamic typing will resolve things for us anyway
+clang::QualType AppleObjCTypeEncodingParser::BuildObjCObjectPointerType(
+ clang::ASTContext &ast_ctx, lldb_utility::StringLexer &type,
+ bool for_expression) {
+ if (!type.NextIf('@'))
+ return clang::QualType();
+
+ std::string name;
+
+ if (type.NextIf('"')) {
+ // We have to be careful here. We're used to seeing
+ // @"NSString"
+ // but in records it is possible that the string following an @ is the name
+ // of the next field and @ means "id". This is the case if anything
+ // unquoted except for "}", the end of the type, or another name follows
+ // the quoted string.
+ //
+ // E.g.
+ // - @"NSString"@ means "id, followed by a field named NSString of type id"
+ // - @"NSString"} means "a pointer to NSString and the end of the struct" -
+ // @"NSString""nextField" means "a pointer to NSString and a field named
+ // nextField" - @"NSString" followed by the end of the string means "a
+ // pointer to NSString"
+ //
+ // As a result, the rule is: If we see @ followed by a quoted string, we
+ // peek. - If we see }, ), ], the end of the string, or a quote ("), the
+ // quoted string is a class name. - If we see anything else, the quoted
+ // string is a field name and we push it back onto type.
+
+ name = ReadQuotedString(type);
+
+ if (type.HasAtLeast(1)) {
+ switch (type.Peek()) {
+ default:
+ // roll back
+ type.PutBack(name.length() +
+ 2); // undo our consumption of the string and of the quotes
+ name.clear();
+ break;
+ case '}':
+ case ')':
+ case ']':
+ case '"':
+ // the quoted string is a class name – see the rule
+ break;
+ }
+ } else {
+ // the quoted string is a class name – see the rule
+ }
+ }
+
+ if (for_expression && !name.empty()) {
+ size_t less_than_pos = name.find('<');
+
+ if (less_than_pos != std::string::npos) {
+ if (less_than_pos == 0)
+ return ast_ctx.getObjCIdType();
+ else
+ name.erase(less_than_pos);
+ }
+
+ DeclVendor *decl_vendor = m_runtime.GetDeclVendor();
+ if (!decl_vendor)
+ return clang::QualType();
+
+ auto types = decl_vendor->FindTypes(ConstString(name), /*max_matches*/ 1);
+
+// The user can forward-declare something that has no definition. The runtime
+// doesn't prohibit this at all. This is a rare and very weird case. We keep
+// this assert in debug builds so we catch other weird cases.
+#ifdef LLDB_CONFIGURATION_DEBUG
+ assert(!types.empty());
+#else
+ if (types.empty())
+ return ast_ctx.getObjCIdType();
+#endif
+
+ return ClangUtil::GetQualType(types.front().GetPointerType());
+ } else {
+ // We're going to resolve this dynamically anyway, so just smile and wave.
+ return ast_ctx.getObjCIdType();
+ }
+}
+
+clang::QualType
+AppleObjCTypeEncodingParser::BuildType(clang::ASTContext &ast_ctx,
+ StringLexer &type, bool for_expression,
+ uint32_t *bitfield_bit_size) {
+ if (!type.HasAtLeast(1))
+ return clang::QualType();
+
+ switch (type.Peek()) {
+ default:
+ break;
+ case '{':
+ return BuildStruct(ast_ctx, type, for_expression);
+ case '[':
+ return BuildArray(ast_ctx, type, for_expression);
+ case '(':
+ return BuildUnion(ast_ctx, type, for_expression);
+ case '@':
+ return BuildObjCObjectPointerType(ast_ctx, type, for_expression);
+ }
+
+ switch (type.Next()) {
+ default:
+ type.PutBack(1);
+ return clang::QualType();
+ case 'c':
+ return ast_ctx.CharTy;
+ case 'i':
+ return ast_ctx.IntTy;
+ case 's':
+ return ast_ctx.ShortTy;
+ case 'l':
+ return ast_ctx.getIntTypeForBitwidth(32, true);
+ // this used to be done like this:
+ // ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx);
+ // if (!lldb_ctx)
+ // return clang::QualType();
+ // return lldb_ctx->GetIntTypeFromBitSize(32, true).GetQualType();
+ // which uses one of the constants if one is available, but we don't think
+ // all this work is necessary.
+ case 'q':
+ return ast_ctx.LongLongTy;
+ case 'C':
+ return ast_ctx.UnsignedCharTy;
+ case 'I':
+ return ast_ctx.UnsignedIntTy;
+ case 'S':
+ return ast_ctx.UnsignedShortTy;
+ case 'L':
+ return ast_ctx.getIntTypeForBitwidth(32, false);
+ // see note for 'l'
+ case 'Q':
+ return ast_ctx.UnsignedLongLongTy;
+ case 'f':
+ return ast_ctx.FloatTy;
+ case 'd':
+ return ast_ctx.DoubleTy;
+ case 'B':
+ return ast_ctx.BoolTy;
+ case 'v':
+ return ast_ctx.VoidTy;
+ case '*':
+ return ast_ctx.getPointerType(ast_ctx.CharTy);
+ case '#':
+ return ast_ctx.getObjCClassType();
+ case ':':
+ return ast_ctx.getObjCSelType();
+ case 'b': {
+ uint32_t size = ReadNumber(type);
+ if (bitfield_bit_size) {
+ *bitfield_bit_size = size;
+ return ast_ctx.UnsignedIntTy; // FIXME: the spec is fairly vague here.
+ } else
+ return clang::QualType();
+ }
+ case 'r': {
+ clang::QualType target_type = BuildType(ast_ctx, type, for_expression);
+ if (target_type.isNull())
+ return clang::QualType();
+ else if (target_type == ast_ctx.UnknownAnyTy)
+ return ast_ctx.UnknownAnyTy;
+ else
+ return ast_ctx.getConstType(target_type);
+ }
+ case '^': {
+ if (!for_expression && type.NextIf('?')) {
+ // if we are not supporting the concept of unknownAny, but what is being
+ // created here is an unknownAny*, then we can just get away with a void*
+ // this is theoretically wrong (in the same sense as 'theoretically
+ // nothing exists') but is way better than outright failure in many
+ // practical cases
+ return ast_ctx.VoidPtrTy;
+ } else {
+ clang::QualType target_type = BuildType(ast_ctx, type, for_expression);
+ if (target_type.isNull())
+ return clang::QualType();
+ else if (target_type == ast_ctx.UnknownAnyTy)
+ return ast_ctx.UnknownAnyTy;
+ else
+ return ast_ctx.getPointerType(target_type);
+ }
+ }
+ case '?':
+ return for_expression ? ast_ctx.UnknownAnyTy : clang::QualType();
+ }
+}
+
+CompilerType AppleObjCTypeEncodingParser::RealizeType(
+ clang::ASTContext &ast_ctx, const char *name, bool for_expression) {
+ if (name && name[0]) {
+ StringLexer lexer(name);
+ clang::QualType qual_type = BuildType(ast_ctx, lexer, for_expression);
+ return CompilerType(&ast_ctx, qual_type);
+ }
+ return CompilerType();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeEncodingParser.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeEncodingParser.h
new file mode 100644
index 000000000000..e576e8f283f2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeEncodingParser.h
@@ -0,0 +1,83 @@
+//===-- AppleObjCTypeEncodingParser.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 liblldb_AppleObjCTypeEncodingParser_h_
+#define liblldb_AppleObjCTypeEncodingParser_h_
+
+#include "clang/AST/ASTContext.h"
+
+#include "lldb/lldb-private.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+namespace lldb_utility {
+class StringLexer;
+}
+
+namespace lldb_private {
+
+class AppleObjCTypeEncodingParser : public ObjCLanguageRuntime::EncodingToType {
+public:
+ AppleObjCTypeEncodingParser(ObjCLanguageRuntime &runtime);
+ ~AppleObjCTypeEncodingParser() override = default;
+
+ CompilerType RealizeType(clang::ASTContext &ast_ctx, const char *name,
+ bool for_expression) override;
+
+private:
+ struct StructElement {
+ std::string name;
+ clang::QualType type;
+ uint32_t bitfield;
+
+ StructElement();
+ ~StructElement() = default;
+ };
+
+ clang::QualType BuildType(clang::ASTContext &ast_ctx,
+ lldb_utility::StringLexer &type,
+ bool for_expression,
+ uint32_t *bitfield_bit_size = nullptr);
+
+ clang::QualType BuildStruct(clang::ASTContext &ast_ctx,
+ lldb_utility::StringLexer &type,
+ bool for_expression);
+
+ clang::QualType BuildAggregate(clang::ASTContext &ast_ctx,
+ lldb_utility::StringLexer &type,
+ bool for_expression, char opener, char closer,
+ uint32_t kind);
+
+ clang::QualType BuildUnion(clang::ASTContext &ast_ctx,
+ lldb_utility::StringLexer &type,
+ bool for_expression);
+
+ clang::QualType BuildArray(clang::ASTContext &ast_ctx,
+ lldb_utility::StringLexer &type,
+ bool for_expression);
+
+ std::string ReadStructName(lldb_utility::StringLexer &type);
+
+ StructElement ReadStructElement(clang::ASTContext &ast_ctx,
+ lldb_utility::StringLexer &type,
+ bool for_expression);
+
+ clang::QualType BuildObjCObjectPointerType(clang::ASTContext &ast_ctx,
+ lldb_utility::StringLexer &type,
+ bool for_expression);
+
+ uint32_t ReadNumber(lldb_utility::StringLexer &type);
+
+ std::string ReadQuotedString(lldb_utility::StringLexer &type);
+
+ ObjCLanguageRuntime &m_runtime;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_AppleObjCTypeEncodingParser_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp
new file mode 100644
index 000000000000..d18435c9c6db
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp
@@ -0,0 +1,204 @@
+//===-- AppleThreadPlanStepThroughObjCTrampoline.cpp
+//
+// 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 "AppleThreadPlanStepThroughObjCTrampoline.h"
+
+#include "AppleObjCTrampolineHandler.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/FunctionCaller.h"
+#include "lldb/Expression/UtilityFunction.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlanRunToAddress.h"
+#include "lldb/Target/ThreadPlanStepOut.h"
+#include "lldb/Utility/Log.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// ThreadPlanStepThroughObjCTrampoline constructor
+AppleThreadPlanStepThroughObjCTrampoline::
+ AppleThreadPlanStepThroughObjCTrampoline(
+ Thread &thread, AppleObjCTrampolineHandler *trampoline_handler,
+ ValueList &input_values, lldb::addr_t isa_addr, lldb::addr_t sel_addr,
+ bool stop_others)
+ : ThreadPlan(ThreadPlan::eKindGeneric,
+ "MacOSX Step through ObjC Trampoline", thread, eVoteNoOpinion,
+ eVoteNoOpinion),
+ m_trampoline_handler(trampoline_handler),
+ m_args_addr(LLDB_INVALID_ADDRESS), m_input_values(input_values),
+ m_isa_addr(isa_addr), m_sel_addr(sel_addr), m_impl_function(nullptr),
+ m_stop_others(stop_others) {}
+
+// Destructor
+AppleThreadPlanStepThroughObjCTrampoline::
+ ~AppleThreadPlanStepThroughObjCTrampoline() {}
+
+void AppleThreadPlanStepThroughObjCTrampoline::DidPush() {
+ // Setting up the memory space for the called function text might require
+ // allocations, i.e. a nested function call. This needs to be done as a
+ // PreResumeAction.
+ m_thread.GetProcess()->AddPreResumeAction(PreResumeInitializeFunctionCaller,
+ (void *)this);
+}
+
+bool AppleThreadPlanStepThroughObjCTrampoline::InitializeFunctionCaller() {
+ if (!m_func_sp) {
+ DiagnosticManager diagnostics;
+ m_args_addr =
+ m_trampoline_handler->SetupDispatchFunction(m_thread, m_input_values);
+
+ if (m_args_addr == LLDB_INVALID_ADDRESS) {
+ return false;
+ }
+ m_impl_function =
+ m_trampoline_handler->GetLookupImplementationFunctionCaller();
+ ExecutionContext exc_ctx;
+ EvaluateExpressionOptions options;
+ options.SetUnwindOnError(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetStopOthers(m_stop_others);
+ m_thread.CalculateExecutionContext(exc_ctx);
+ m_func_sp = m_impl_function->GetThreadPlanToCallFunction(
+ exc_ctx, m_args_addr, options, diagnostics);
+ m_func_sp->SetOkayToDiscard(true);
+ m_thread.QueueThreadPlan(m_func_sp, false);
+ }
+ return true;
+}
+
+bool AppleThreadPlanStepThroughObjCTrampoline::
+ PreResumeInitializeFunctionCaller(void *void_myself) {
+ AppleThreadPlanStepThroughObjCTrampoline *myself =
+ static_cast<AppleThreadPlanStepThroughObjCTrampoline *>(void_myself);
+ return myself->InitializeFunctionCaller();
+}
+
+void AppleThreadPlanStepThroughObjCTrampoline::GetDescription(
+ Stream *s, lldb::DescriptionLevel level) {
+ if (level == lldb::eDescriptionLevelBrief)
+ s->Printf("Step through ObjC trampoline");
+ else {
+ s->Printf("Stepping to implementation of ObjC method - obj: 0x%llx, isa: "
+ "0x%" PRIx64 ", sel: 0x%" PRIx64,
+ m_input_values.GetValueAtIndex(0)->GetScalar().ULongLong(),
+ m_isa_addr, m_sel_addr);
+ }
+}
+
+bool AppleThreadPlanStepThroughObjCTrampoline::ValidatePlan(Stream *error) {
+ return true;
+}
+
+bool AppleThreadPlanStepThroughObjCTrampoline::DoPlanExplainsStop(
+ Event *event_ptr) {
+ // If we get asked to explain the stop it will be because something went
+ // wrong (like the implementation for selector function crashed... We're
+ // going to figure out what to do about that, so we do explain the stop.
+ return true;
+}
+
+lldb::StateType AppleThreadPlanStepThroughObjCTrampoline::GetPlanRunState() {
+ return eStateRunning;
+}
+
+bool AppleThreadPlanStepThroughObjCTrampoline::ShouldStop(Event *event_ptr) {
+ // First stage: we are still handling the "call a function to get the target
+ // of the dispatch"
+ if (m_func_sp) {
+ if (!m_func_sp->IsPlanComplete()) {
+ return false;
+ } else {
+ if (!m_func_sp->PlanSucceeded()) {
+ SetPlanComplete(false);
+ return true;
+ }
+ m_func_sp.reset();
+ }
+ }
+
+ // Second stage, if all went well with the function calling, then fetch the
+ // target address, and queue up a "run to that address" plan.
+ if (!m_run_to_sp) {
+ Value target_addr_value;
+ ExecutionContext exc_ctx;
+ m_thread.CalculateExecutionContext(exc_ctx);
+ m_impl_function->FetchFunctionResults(exc_ctx, m_args_addr,
+ target_addr_value);
+ m_impl_function->DeallocateFunctionResults(exc_ctx, m_args_addr);
+ lldb::addr_t target_addr = target_addr_value.GetScalar().ULongLong();
+ Address target_so_addr;
+ target_so_addr.SetOpcodeLoadAddress(target_addr, exc_ctx.GetTargetPtr());
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (target_addr == 0) {
+ if (log)
+ log->Printf("Got target implementation of 0x0, stopping.");
+ SetPlanComplete();
+ return true;
+ }
+ if (m_trampoline_handler->AddrIsMsgForward(target_addr)) {
+ if (log)
+ log->Printf(
+ "Implementation lookup returned msgForward function: 0x%" PRIx64
+ ", stopping.",
+ target_addr);
+
+ SymbolContext sc = m_thread.GetStackFrameAtIndex(0)->GetSymbolContext(
+ eSymbolContextEverything);
+ Status status;
+ const bool abort_other_plans = false;
+ const bool first_insn = true;
+ const uint32_t frame_idx = 0;
+ m_run_to_sp = m_thread.QueueThreadPlanForStepOutNoShouldStop(
+ abort_other_plans, &sc, first_insn, m_stop_others, eVoteNoOpinion,
+ eVoteNoOpinion, frame_idx, status);
+ if (m_run_to_sp && status.Success())
+ m_run_to_sp->SetPrivate(true);
+ return false;
+ }
+
+ if (log)
+ log->Printf("Running to ObjC method implementation: 0x%" PRIx64,
+ target_addr);
+
+ ObjCLanguageRuntime *objc_runtime =
+ ObjCLanguageRuntime::Get(*GetThread().GetProcess());
+ assert(objc_runtime != nullptr);
+ objc_runtime->AddToMethodCache(m_isa_addr, m_sel_addr, target_addr);
+ if (log)
+ log->Printf("Adding {isa-addr=0x%" PRIx64 ", sel-addr=0x%" PRIx64
+ "} = addr=0x%" PRIx64 " to cache.",
+ m_isa_addr, m_sel_addr, target_addr);
+
+ // Extract the target address from the value:
+
+ m_run_to_sp = std::make_shared<ThreadPlanRunToAddress>(
+ m_thread, target_so_addr, m_stop_others);
+ m_thread.QueueThreadPlan(m_run_to_sp, false);
+ m_run_to_sp->SetPrivate(true);
+ return false;
+ } else if (m_thread.IsThreadPlanDone(m_run_to_sp.get())) {
+ // Third stage, work the run to target plan.
+ SetPlanComplete();
+ return true;
+ }
+ return false;
+}
+
+// The base class MischiefManaged does some cleanup - so you have to call it in
+// your MischiefManaged derived class.
+bool AppleThreadPlanStepThroughObjCTrampoline::MischiefManaged() {
+ return IsPlanComplete();
+}
+
+bool AppleThreadPlanStepThroughObjCTrampoline::WillStop() { return true; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.h
new file mode 100644
index 000000000000..96f37851a35f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.h
@@ -0,0 +1,76 @@
+//===-- AppleThreadPlanStepThroughObjCTrampoline.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_AppleThreadPlanStepThroughObjCTrampoline_h_
+#define lldb_AppleThreadPlanStepThroughObjCTrampoline_h_
+
+#include "AppleObjCTrampolineHandler.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+
+class AppleThreadPlanStepThroughObjCTrampoline : public ThreadPlan {
+public:
+ AppleThreadPlanStepThroughObjCTrampoline(
+ Thread &thread, AppleObjCTrampolineHandler *trampoline_handler,
+ ValueList &values, lldb::addr_t isa_addr, lldb::addr_t sel_addr,
+ bool stop_others);
+
+ ~AppleThreadPlanStepThroughObjCTrampoline() override;
+
+ static bool PreResumeInitializeFunctionCaller(void *myself);
+
+ void GetDescription(Stream *s, lldb::DescriptionLevel level) override;
+
+ bool ValidatePlan(Stream *error) override;
+
+ lldb::StateType GetPlanRunState() override;
+
+ bool ShouldStop(Event *event_ptr) override;
+
+ bool StopOthers() override { return m_stop_others; }
+
+ // The base class MischiefManaged does some cleanup - so you have to call it
+ // in your MischiefManaged derived class.
+ bool MischiefManaged() override;
+
+ void DidPush() override;
+
+ bool WillStop() override;
+
+protected:
+ bool DoPlanExplainsStop(Event *event_ptr) override;
+
+private:
+ bool InitializeFunctionCaller();
+
+ AppleObjCTrampolineHandler *m_trampoline_handler; // FIXME - ensure this
+ // doesn't go away on us?
+ // SP maybe?
+ lldb::addr_t m_args_addr; // Stores the address for our step through function
+ // result structure.
+ // lldb::addr_t m_object_addr; // This is only for Description.
+ ValueList m_input_values;
+ lldb::addr_t m_isa_addr; // isa_addr and sel_addr are the keys we will use to
+ // cache the implementation.
+ lldb::addr_t m_sel_addr;
+ lldb::ThreadPlanSP m_func_sp; // This is the function call plan. We fill it
+ // at start, then set it
+ // to NULL when this plan is done. That way we know to go to:
+ lldb::ThreadPlanSP m_run_to_sp; // The plan that runs to the target.
+ FunctionCaller *m_impl_function; // This is a pointer to a impl function that
+ // is owned by the client that pushes this plan.
+ bool m_stop_others;
+};
+
+} // namespace lldb_private
+
+#endif // lldb_AppleThreadPlanStepThroughObjCTrampoline_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp
new file mode 100644
index 000000000000..631c15c46ce8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp
@@ -0,0 +1,436 @@
+//===-- ObjCLanguageRuntime.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 "clang/AST/Type.h"
+
+#include "ObjCLanguageRuntime.h"
+
+#include "lldb/Core/MappedHash.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Timer.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/DJB.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+char ObjCLanguageRuntime::ID = 0;
+
+// Destructor
+ObjCLanguageRuntime::~ObjCLanguageRuntime() {}
+
+ObjCLanguageRuntime::ObjCLanguageRuntime(Process *process)
+ : LanguageRuntime(process), m_impl_cache(),
+ m_has_new_literals_and_indexing(eLazyBoolCalculate),
+ m_isa_to_descriptor(), m_hash_to_isa_map(), m_type_size_cache(),
+ m_isa_to_descriptor_stop_id(UINT32_MAX), m_complete_class_cache(),
+ m_negative_complete_class_cache() {}
+
+bool ObjCLanguageRuntime::IsWhitelistedRuntimeValue(ConstString name) {
+ static ConstString g_self = ConstString("self");
+ static ConstString g_cmd = ConstString("_cmd");
+ return name == g_self || name == g_cmd;
+}
+
+bool ObjCLanguageRuntime::AddClass(ObjCISA isa,
+ const ClassDescriptorSP &descriptor_sp,
+ const char *class_name) {
+ if (isa != 0) {
+ m_isa_to_descriptor[isa] = descriptor_sp;
+ // class_name is assumed to be valid
+ m_hash_to_isa_map.insert(std::make_pair(llvm::djbHash(class_name), isa));
+ return true;
+ }
+ return false;
+}
+
+void ObjCLanguageRuntime::AddToMethodCache(lldb::addr_t class_addr,
+ lldb::addr_t selector,
+ lldb::addr_t impl_addr) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log) {
+ log->Printf("Caching: class 0x%" PRIx64 " selector 0x%" PRIx64
+ " implementation 0x%" PRIx64 ".",
+ class_addr, selector, impl_addr);
+ }
+ m_impl_cache.insert(std::pair<ClassAndSel, lldb::addr_t>(
+ ClassAndSel(class_addr, selector), impl_addr));
+}
+
+lldb::addr_t ObjCLanguageRuntime::LookupInMethodCache(lldb::addr_t class_addr,
+ lldb::addr_t selector) {
+ MsgImplMap::iterator pos, end = m_impl_cache.end();
+ pos = m_impl_cache.find(ClassAndSel(class_addr, selector));
+ if (pos != end)
+ return (*pos).second;
+ return LLDB_INVALID_ADDRESS;
+}
+
+lldb::TypeSP
+ObjCLanguageRuntime::LookupInCompleteClassCache(ConstString &name) {
+ CompleteClassMap::iterator complete_class_iter =
+ m_complete_class_cache.find(name);
+
+ if (complete_class_iter != m_complete_class_cache.end()) {
+ // Check the weak pointer to make sure the type hasn't been unloaded
+ TypeSP complete_type_sp(complete_class_iter->second.lock());
+
+ if (complete_type_sp)
+ return complete_type_sp;
+ else
+ m_complete_class_cache.erase(name);
+ }
+
+ if (m_negative_complete_class_cache.count(name) > 0)
+ return TypeSP();
+
+ const ModuleList &modules = m_process->GetTarget().GetImages();
+
+ SymbolContextList sc_list;
+ const size_t matching_symbols =
+ modules.FindSymbolsWithNameAndType(name, eSymbolTypeObjCClass, sc_list);
+
+ if (matching_symbols) {
+ SymbolContext sc;
+
+ sc_list.GetContextAtIndex(0, sc);
+
+ ModuleSP module_sp(sc.module_sp);
+
+ if (!module_sp)
+ return TypeSP();
+
+ const bool exact_match = true;
+ const uint32_t max_matches = UINT32_MAX;
+ TypeList types;
+
+ llvm::DenseSet<SymbolFile *> searched_symbol_files;
+ const uint32_t num_types = module_sp->FindTypes(
+ name, exact_match, max_matches, searched_symbol_files, types);
+
+ if (num_types) {
+ uint32_t i;
+ for (i = 0; i < num_types; ++i) {
+ TypeSP type_sp(types.GetTypeAtIndex(i));
+
+ if (ClangASTContext::IsObjCObjectOrInterfaceType(
+ type_sp->GetForwardCompilerType())) {
+ if (type_sp->IsCompleteObjCClass()) {
+ m_complete_class_cache[name] = type_sp;
+ return type_sp;
+ }
+ }
+ }
+ }
+ }
+ m_negative_complete_class_cache.insert(name);
+ return TypeSP();
+}
+
+size_t ObjCLanguageRuntime::GetByteOffsetForIvar(CompilerType &parent_qual_type,
+ const char *ivar_name) {
+ return LLDB_INVALID_IVAR_OFFSET;
+}
+
+bool ObjCLanguageRuntime::ClassDescriptor::IsPointerValid(
+ lldb::addr_t value, uint32_t ptr_size, bool allow_NULLs, bool allow_tagged,
+ bool check_version_specific) const {
+ if (!value)
+ return allow_NULLs;
+ if ((value % 2) == 1 && allow_tagged)
+ return true;
+ if ((value % ptr_size) == 0)
+ return (check_version_specific ? CheckPointer(value, ptr_size) : true);
+ else
+ return false;
+}
+
+ObjCLanguageRuntime::ObjCISA
+ObjCLanguageRuntime::GetISA(ConstString name) {
+ ISAToDescriptorIterator pos = GetDescriptorIterator(name);
+ if (pos != m_isa_to_descriptor.end())
+ return pos->first;
+ return 0;
+}
+
+ObjCLanguageRuntime::ISAToDescriptorIterator
+ObjCLanguageRuntime::GetDescriptorIterator(ConstString name) {
+ ISAToDescriptorIterator end = m_isa_to_descriptor.end();
+
+ if (name) {
+ UpdateISAToDescriptorMap();
+ if (m_hash_to_isa_map.empty()) {
+ // No name hashes were provided, we need to just linearly power through
+ // the names and find a match
+ for (ISAToDescriptorIterator pos = m_isa_to_descriptor.begin();
+ pos != end; ++pos) {
+ if (pos->second->GetClassName() == name)
+ return pos;
+ }
+ } else {
+ // Name hashes were provided, so use them to efficiently lookup name to
+ // isa/descriptor
+ const uint32_t name_hash = llvm::djbHash(name.GetStringRef());
+ std::pair<HashToISAIterator, HashToISAIterator> range =
+ m_hash_to_isa_map.equal_range(name_hash);
+ for (HashToISAIterator range_pos = range.first; range_pos != range.second;
+ ++range_pos) {
+ ISAToDescriptorIterator pos =
+ m_isa_to_descriptor.find(range_pos->second);
+ if (pos != m_isa_to_descriptor.end()) {
+ if (pos->second->GetClassName() == name)
+ return pos;
+ }
+ }
+ }
+ }
+ return end;
+}
+
+std::pair<ObjCLanguageRuntime::ISAToDescriptorIterator,
+ ObjCLanguageRuntime::ISAToDescriptorIterator>
+ObjCLanguageRuntime::GetDescriptorIteratorPair(bool update_if_needed) {
+ if (update_if_needed)
+ UpdateISAToDescriptorMapIfNeeded();
+
+ return std::pair<ObjCLanguageRuntime::ISAToDescriptorIterator,
+ ObjCLanguageRuntime::ISAToDescriptorIterator>(
+ m_isa_to_descriptor.begin(), m_isa_to_descriptor.end());
+}
+
+ObjCLanguageRuntime::ObjCISA
+ObjCLanguageRuntime::GetParentClass(ObjCLanguageRuntime::ObjCISA isa) {
+ ClassDescriptorSP objc_class_sp(GetClassDescriptorFromISA(isa));
+ if (objc_class_sp) {
+ ClassDescriptorSP objc_super_class_sp(objc_class_sp->GetSuperclass());
+ if (objc_super_class_sp)
+ return objc_super_class_sp->GetISA();
+ }
+ return 0;
+}
+
+ConstString
+ObjCLanguageRuntime::GetActualTypeName(ObjCLanguageRuntime::ObjCISA isa) {
+ ClassDescriptorSP objc_class_sp(GetNonKVOClassDescriptor(isa));
+ if (objc_class_sp)
+ return objc_class_sp->GetClassName();
+ return ConstString();
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+ObjCLanguageRuntime::GetClassDescriptorFromClassName(
+ ConstString class_name) {
+ ISAToDescriptorIterator pos = GetDescriptorIterator(class_name);
+ if (pos != m_isa_to_descriptor.end())
+ return pos->second;
+ return ClassDescriptorSP();
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+ObjCLanguageRuntime::GetClassDescriptor(ValueObject &valobj) {
+ ClassDescriptorSP objc_class_sp;
+ // if we get an invalid VO (which might still happen when playing around with
+ // pointers returned by the expression parser, don't consider this a valid
+ // ObjC object)
+ if (valobj.GetCompilerType().IsValid()) {
+ addr_t isa_pointer = valobj.GetPointerValue();
+ if (isa_pointer != LLDB_INVALID_ADDRESS) {
+ ExecutionContext exe_ctx(valobj.GetExecutionContextRef());
+
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process) {
+ Status error;
+ ObjCISA isa = process->ReadPointerFromMemory(isa_pointer, error);
+ if (isa != LLDB_INVALID_ADDRESS)
+ objc_class_sp = GetClassDescriptorFromISA(isa);
+ }
+ }
+ }
+ return objc_class_sp;
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+ObjCLanguageRuntime::GetNonKVOClassDescriptor(ValueObject &valobj) {
+ ObjCLanguageRuntime::ClassDescriptorSP objc_class_sp(
+ GetClassDescriptor(valobj));
+ if (objc_class_sp) {
+ if (!objc_class_sp->IsKVO())
+ return objc_class_sp;
+
+ ClassDescriptorSP non_kvo_objc_class_sp(objc_class_sp->GetSuperclass());
+ if (non_kvo_objc_class_sp && non_kvo_objc_class_sp->IsValid())
+ return non_kvo_objc_class_sp;
+ }
+ return ClassDescriptorSP();
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+ObjCLanguageRuntime::GetClassDescriptorFromISA(ObjCISA isa) {
+ if (isa) {
+ UpdateISAToDescriptorMap();
+ ObjCLanguageRuntime::ISAToDescriptorIterator pos =
+ m_isa_to_descriptor.find(isa);
+ if (pos != m_isa_to_descriptor.end())
+ return pos->second;
+ }
+ return ClassDescriptorSP();
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+ObjCLanguageRuntime::GetNonKVOClassDescriptor(ObjCISA isa) {
+ if (isa) {
+ ClassDescriptorSP objc_class_sp = GetClassDescriptorFromISA(isa);
+ if (objc_class_sp && objc_class_sp->IsValid()) {
+ if (!objc_class_sp->IsKVO())
+ return objc_class_sp;
+
+ ClassDescriptorSP non_kvo_objc_class_sp(objc_class_sp->GetSuperclass());
+ if (non_kvo_objc_class_sp && non_kvo_objc_class_sp->IsValid())
+ return non_kvo_objc_class_sp;
+ }
+ }
+ return ClassDescriptorSP();
+}
+
+CompilerType
+ObjCLanguageRuntime::EncodingToType::RealizeType(const char *name,
+ bool for_expression) {
+ if (m_scratch_ast_ctx_up)
+ return RealizeType(*m_scratch_ast_ctx_up, name, for_expression);
+ return CompilerType();
+}
+
+CompilerType ObjCLanguageRuntime::EncodingToType::RealizeType(
+ ClangASTContext &ast_ctx, const char *name, bool for_expression) {
+ clang::ASTContext *clang_ast = ast_ctx.getASTContext();
+ if (!clang_ast)
+ return CompilerType();
+ return RealizeType(*clang_ast, name, for_expression);
+}
+
+ObjCLanguageRuntime::EncodingToType::~EncodingToType() {}
+
+ObjCLanguageRuntime::EncodingToTypeSP ObjCLanguageRuntime::GetEncodingToType() {
+ return nullptr;
+}
+
+bool ObjCLanguageRuntime::GetTypeBitSize(const CompilerType &compiler_type,
+ uint64_t &size) {
+ void *opaque_ptr = compiler_type.GetOpaqueQualType();
+ size = m_type_size_cache.Lookup(opaque_ptr);
+ // an ObjC object will at least have an ISA, so 0 is definitely not OK
+ if (size > 0)
+ return true;
+
+ ClassDescriptorSP class_descriptor_sp =
+ GetClassDescriptorFromClassName(compiler_type.GetTypeName());
+ if (!class_descriptor_sp)
+ return false;
+
+ int32_t max_offset = INT32_MIN;
+ uint64_t sizeof_max = 0;
+ bool found = false;
+
+ for (size_t idx = 0; idx < class_descriptor_sp->GetNumIVars(); idx++) {
+ const auto &ivar = class_descriptor_sp->GetIVarAtIndex(idx);
+ int32_t cur_offset = ivar.m_offset;
+ if (cur_offset > max_offset) {
+ max_offset = cur_offset;
+ sizeof_max = ivar.m_size;
+ found = true;
+ }
+ }
+
+ size = 8 * (max_offset + sizeof_max);
+ if (found)
+ m_type_size_cache.Insert(opaque_ptr, size);
+
+ return found;
+}
+
+lldb::BreakpointPreconditionSP
+ObjCLanguageRuntime::GetBreakpointExceptionPrecondition(LanguageType language,
+ bool throw_bp) {
+ if (language != eLanguageTypeObjC)
+ return lldb::BreakpointPreconditionSP();
+ if (!throw_bp)
+ return lldb::BreakpointPreconditionSP();
+ BreakpointPreconditionSP precondition_sp(
+ new ObjCLanguageRuntime::ObjCExceptionPrecondition());
+ return precondition_sp;
+}
+
+// Exception breakpoint Precondition class for ObjC:
+void ObjCLanguageRuntime::ObjCExceptionPrecondition::AddClassName(
+ const char *class_name) {
+ m_class_names.insert(class_name);
+}
+
+ObjCLanguageRuntime::ObjCExceptionPrecondition::ObjCExceptionPrecondition() {}
+
+bool ObjCLanguageRuntime::ObjCExceptionPrecondition::EvaluatePrecondition(
+ StoppointCallbackContext &context) {
+ return true;
+}
+
+void ObjCLanguageRuntime::ObjCExceptionPrecondition::GetDescription(
+ Stream &stream, lldb::DescriptionLevel level) {}
+
+Status ObjCLanguageRuntime::ObjCExceptionPrecondition::ConfigurePrecondition(
+ Args &args) {
+ Status error;
+ if (args.GetArgumentCount() > 0)
+ error.SetErrorString(
+ "The ObjC Exception breakpoint doesn't support extra options.");
+ return error;
+}
+
+llvm::Optional<CompilerType>
+ObjCLanguageRuntime::GetRuntimeType(CompilerType base_type) {
+ CompilerType class_type;
+ bool is_pointer_type = false;
+
+ if (ClangASTContext::IsObjCObjectPointerType(base_type, &class_type))
+ is_pointer_type = true;
+ else if (ClangASTContext::IsObjCObjectOrInterfaceType(base_type))
+ class_type = base_type;
+ else
+ return llvm::None;
+
+ if (!class_type)
+ return llvm::None;
+
+ ConstString class_name(class_type.GetConstTypeName());
+ if (!class_name)
+ return llvm::None;
+
+ TypeSP complete_objc_class_type_sp = LookupInCompleteClassCache(class_name);
+ if (!complete_objc_class_type_sp)
+ return llvm::None;
+
+ CompilerType complete_class(
+ complete_objc_class_type_sp->GetFullCompilerType());
+ if (complete_class.GetCompleteType()) {
+ if (is_pointer_type)
+ return complete_class.GetPointerType();
+ else
+ return complete_class;
+ }
+
+ return llvm::None;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h
new file mode 100644
index 000000000000..1925c78ed342
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h
@@ -0,0 +1,429 @@
+//===-- ObjCLanguageRuntime.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 liblldb_ObjCLanguageRuntime_h_
+#define liblldb_ObjCLanguageRuntime_h_
+
+#include <functional>
+#include <map>
+#include <memory>
+#include <unordered_set>
+
+#include "llvm/Support/Casting.h"
+
+#include "lldb/Breakpoint/BreakpointPrecondition.h"
+#include "lldb/Core/PluginInterface.h"
+#include "lldb/Core/ThreadSafeDenseMap.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/lldb-private.h"
+
+class CommandObjectObjC_ClassTable_Dump;
+
+namespace lldb_private {
+
+class UtilityFunction;
+
+class ObjCLanguageRuntime : public LanguageRuntime {
+public:
+ enum class ObjCRuntimeVersions {
+ eObjC_VersionUnknown = 0,
+ eAppleObjC_V1 = 1,
+ eAppleObjC_V2 = 2
+ };
+
+ typedef lldb::addr_t ObjCISA;
+
+ class ClassDescriptor;
+ typedef std::shared_ptr<ClassDescriptor> ClassDescriptorSP;
+
+ // the information that we want to support retrieving from an ObjC class this
+ // needs to be pure virtual since there are at least 2 different
+ // implementations of the runtime, and more might come
+ class ClassDescriptor {
+ public:
+ ClassDescriptor()
+ : m_is_kvo(eLazyBoolCalculate), m_is_cf(eLazyBoolCalculate),
+ m_type_wp() {}
+
+ virtual ~ClassDescriptor() = default;
+
+ virtual ConstString GetClassName() = 0;
+
+ virtual ClassDescriptorSP GetSuperclass() = 0;
+
+ virtual ClassDescriptorSP GetMetaclass() const = 0;
+
+ // virtual if any implementation has some other version-specific rules but
+ // for the known v1/v2 this is all that needs to be done
+ virtual bool IsKVO() {
+ if (m_is_kvo == eLazyBoolCalculate) {
+ const char *class_name = GetClassName().AsCString();
+ if (class_name && *class_name)
+ m_is_kvo =
+ (LazyBool)(strstr(class_name, "NSKVONotifying_") == class_name);
+ }
+ return (m_is_kvo == eLazyBoolYes);
+ }
+
+ // virtual if any implementation has some other version-specific rules but
+ // for the known v1/v2 this is all that needs to be done
+ virtual bool IsCFType() {
+ if (m_is_cf == eLazyBoolCalculate) {
+ const char *class_name = GetClassName().AsCString();
+ if (class_name && *class_name)
+ m_is_cf = (LazyBool)(strcmp(class_name, "__NSCFType") == 0 ||
+ strcmp(class_name, "NSCFType") == 0);
+ }
+ return (m_is_cf == eLazyBoolYes);
+ }
+
+ virtual bool IsValid() = 0;
+
+ virtual bool GetTaggedPointerInfo(uint64_t *info_bits = nullptr,
+ uint64_t *value_bits = nullptr,
+ uint64_t *payload = nullptr) = 0;
+
+ virtual uint64_t GetInstanceSize() = 0;
+
+ // use to implement version-specific additional constraints on pointers
+ virtual bool CheckPointer(lldb::addr_t value, uint32_t ptr_size) const {
+ return true;
+ }
+
+ virtual ObjCISA GetISA() = 0;
+
+ // This should return true iff the interface could be completed
+ virtual bool
+ Describe(std::function<void(ObjCISA)> const &superclass_func,
+ std::function<bool(const char *, const char *)> const
+ &instance_method_func,
+ std::function<bool(const char *, const char *)> const
+ &class_method_func,
+ std::function<bool(const char *, const char *, lldb::addr_t,
+ uint64_t)> const &ivar_func) const {
+ return false;
+ }
+
+ lldb::TypeSP GetType() { return m_type_wp.lock(); }
+
+ void SetType(const lldb::TypeSP &type_sp) { m_type_wp = type_sp; }
+
+ struct iVarDescriptor {
+ ConstString m_name;
+ CompilerType m_type;
+ uint64_t m_size;
+ int32_t m_offset;
+ };
+
+ virtual size_t GetNumIVars() { return 0; }
+
+ virtual iVarDescriptor GetIVarAtIndex(size_t idx) {
+ return iVarDescriptor();
+ }
+
+ protected:
+ bool IsPointerValid(lldb::addr_t value, uint32_t ptr_size,
+ bool allow_NULLs = false, bool allow_tagged = false,
+ bool check_version_specific = false) const;
+
+ private:
+ LazyBool m_is_kvo;
+ LazyBool m_is_cf;
+ lldb::TypeWP m_type_wp;
+ };
+
+ class EncodingToType {
+ public:
+ virtual ~EncodingToType();
+
+ virtual CompilerType RealizeType(ClangASTContext &ast_ctx, const char *name,
+ bool for_expression);
+ virtual CompilerType RealizeType(const char *name, bool for_expression);
+
+ virtual CompilerType RealizeType(clang::ASTContext &ast_ctx,
+ const char *name, bool for_expression) = 0;
+
+ protected:
+ std::unique_ptr<ClangASTContext> m_scratch_ast_ctx_up;
+ };
+
+ class ObjCExceptionPrecondition : public BreakpointPrecondition {
+ public:
+ ObjCExceptionPrecondition();
+
+ ~ObjCExceptionPrecondition() override = default;
+
+ bool EvaluatePrecondition(StoppointCallbackContext &context) override;
+ void GetDescription(Stream &stream, lldb::DescriptionLevel level) override;
+ Status ConfigurePrecondition(Args &args) override;
+
+ protected:
+ void AddClassName(const char *class_name);
+
+ private:
+ std::unordered_set<std::string> m_class_names;
+ };
+
+ static lldb::BreakpointPreconditionSP
+ GetBreakpointExceptionPrecondition(lldb::LanguageType language,
+ bool throw_bp);
+
+ class TaggedPointerVendor {
+ public:
+ virtual ~TaggedPointerVendor() = default;
+
+ virtual bool IsPossibleTaggedPointer(lldb::addr_t ptr) = 0;
+
+ virtual ObjCLanguageRuntime::ClassDescriptorSP
+ GetClassDescriptor(lldb::addr_t ptr) = 0;
+
+ protected:
+ TaggedPointerVendor() = default;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TaggedPointerVendor);
+ };
+
+ ~ObjCLanguageRuntime() override;
+
+ static char ID;
+
+ bool isA(const void *ClassID) const override {
+ return ClassID == &ID || LanguageRuntime::isA(ClassID);
+ }
+
+ static bool classof(const LanguageRuntime *runtime) {
+ return runtime->isA(&ID);
+ }
+
+ static ObjCLanguageRuntime *Get(Process &process) {
+ return llvm::cast_or_null<ObjCLanguageRuntime>(
+ process.GetLanguageRuntime(lldb::eLanguageTypeObjC));
+ }
+
+ virtual TaggedPointerVendor *GetTaggedPointerVendor() { return nullptr; }
+
+ typedef std::shared_ptr<EncodingToType> EncodingToTypeSP;
+
+ virtual EncodingToTypeSP GetEncodingToType();
+
+ virtual ClassDescriptorSP GetClassDescriptor(ValueObject &in_value);
+
+ ClassDescriptorSP GetNonKVOClassDescriptor(ValueObject &in_value);
+
+ virtual ClassDescriptorSP
+ GetClassDescriptorFromClassName(ConstString class_name);
+
+ virtual ClassDescriptorSP GetClassDescriptorFromISA(ObjCISA isa);
+
+ ClassDescriptorSP GetNonKVOClassDescriptor(ObjCISA isa);
+
+ lldb::LanguageType GetLanguageType() const override {
+ return lldb::eLanguageTypeObjC;
+ }
+
+ virtual bool IsModuleObjCLibrary(const lldb::ModuleSP &module_sp) = 0;
+
+ virtual bool ReadObjCLibrary(const lldb::ModuleSP &module_sp) = 0;
+
+ virtual bool HasReadObjCLibrary() = 0;
+
+ lldb::addr_t LookupInMethodCache(lldb::addr_t class_addr, lldb::addr_t sel);
+
+ void AddToMethodCache(lldb::addr_t class_addr, lldb::addr_t sel,
+ lldb::addr_t impl_addr);
+
+ TypeAndOrName LookupInClassNameCache(lldb::addr_t class_addr);
+
+ void AddToClassNameCache(lldb::addr_t class_addr, const char *name,
+ lldb::TypeSP type_sp);
+
+ void AddToClassNameCache(lldb::addr_t class_addr,
+ const TypeAndOrName &class_or_type_name);
+
+ lldb::TypeSP LookupInCompleteClassCache(ConstString &name);
+
+ llvm::Optional<CompilerType> GetRuntimeType(CompilerType base_type) override;
+
+ virtual UtilityFunction *CreateObjectChecker(const char *) = 0;
+
+ virtual ObjCRuntimeVersions GetRuntimeVersion() const {
+ return ObjCRuntimeVersions::eObjC_VersionUnknown;
+ }
+
+ bool IsValidISA(ObjCISA isa) {
+ UpdateISAToDescriptorMap();
+ return m_isa_to_descriptor.count(isa) > 0;
+ }
+
+ virtual void UpdateISAToDescriptorMapIfNeeded() = 0;
+
+ void UpdateISAToDescriptorMap() {
+ if (m_process && m_process->GetStopID() != m_isa_to_descriptor_stop_id) {
+ UpdateISAToDescriptorMapIfNeeded();
+ }
+ }
+
+ virtual ObjCISA GetISA(ConstString name);
+
+ virtual ConstString GetActualTypeName(ObjCISA isa);
+
+ virtual ObjCISA GetParentClass(ObjCISA isa);
+
+ // Finds the byte offset of the child_type ivar in parent_type. If it can't
+ // find the offset, returns LLDB_INVALID_IVAR_OFFSET.
+
+ virtual size_t GetByteOffsetForIvar(CompilerType &parent_qual_type,
+ const char *ivar_name);
+
+ bool HasNewLiteralsAndIndexing() {
+ if (m_has_new_literals_and_indexing == eLazyBoolCalculate) {
+ if (CalculateHasNewLiteralsAndIndexing())
+ m_has_new_literals_and_indexing = eLazyBoolYes;
+ else
+ m_has_new_literals_and_indexing = eLazyBoolNo;
+ }
+
+ return (m_has_new_literals_and_indexing == eLazyBoolYes);
+ }
+
+ void SymbolsDidLoad(const ModuleList &module_list) override {
+ m_negative_complete_class_cache.clear();
+ }
+
+ bool GetTypeBitSize(const CompilerType &compiler_type,
+ uint64_t &size) override;
+
+ /// Check whether the name is "self" or "_cmd" and should show up in
+ /// "frame variable".
+ bool IsWhitelistedRuntimeValue(ConstString name) override;
+
+protected:
+ // Classes that inherit from ObjCLanguageRuntime can see and modify these
+ ObjCLanguageRuntime(Process *process);
+
+ virtual bool CalculateHasNewLiteralsAndIndexing() { return false; }
+
+ bool ISAIsCached(ObjCISA isa) const {
+ return m_isa_to_descriptor.find(isa) != m_isa_to_descriptor.end();
+ }
+
+ bool AddClass(ObjCISA isa, const ClassDescriptorSP &descriptor_sp) {
+ if (isa != 0) {
+ m_isa_to_descriptor[isa] = descriptor_sp;
+ return true;
+ }
+ return false;
+ }
+
+ bool AddClass(ObjCISA isa, const ClassDescriptorSP &descriptor_sp,
+ const char *class_name);
+
+ bool AddClass(ObjCISA isa, const ClassDescriptorSP &descriptor_sp,
+ uint32_t class_name_hash) {
+ if (isa != 0) {
+ m_isa_to_descriptor[isa] = descriptor_sp;
+ m_hash_to_isa_map.insert(std::make_pair(class_name_hash, isa));
+ return true;
+ }
+ return false;
+ }
+
+private:
+ // We keep a map of <Class,Selector>->Implementation so we don't have to call
+ // the resolver function over and over.
+
+ // FIXME: We need to watch for the loading of Protocols, and flush the cache
+ // for any
+ // class that we see so changed.
+
+ struct ClassAndSel {
+ ClassAndSel() {
+ sel_addr = LLDB_INVALID_ADDRESS;
+ class_addr = LLDB_INVALID_ADDRESS;
+ }
+
+ ClassAndSel(lldb::addr_t in_sel_addr, lldb::addr_t in_class_addr)
+ : class_addr(in_class_addr), sel_addr(in_sel_addr) {}
+
+ bool operator==(const ClassAndSel &rhs) {
+ if (class_addr == rhs.class_addr && sel_addr == rhs.sel_addr)
+ return true;
+ else
+ return false;
+ }
+
+ bool operator<(const ClassAndSel &rhs) const {
+ if (class_addr < rhs.class_addr)
+ return true;
+ else if (class_addr > rhs.class_addr)
+ return false;
+ else {
+ if (sel_addr < rhs.sel_addr)
+ return true;
+ else
+ return false;
+ }
+ }
+
+ lldb::addr_t class_addr;
+ lldb::addr_t sel_addr;
+ };
+
+ typedef std::map<ClassAndSel, lldb::addr_t> MsgImplMap;
+ typedef std::map<ObjCISA, ClassDescriptorSP> ISAToDescriptorMap;
+ typedef std::multimap<uint32_t, ObjCISA> HashToISAMap;
+ typedef ISAToDescriptorMap::iterator ISAToDescriptorIterator;
+ typedef HashToISAMap::iterator HashToISAIterator;
+ typedef ThreadSafeDenseMap<void *, uint64_t> TypeSizeCache;
+
+ MsgImplMap m_impl_cache;
+ LazyBool m_has_new_literals_and_indexing;
+ ISAToDescriptorMap m_isa_to_descriptor;
+ HashToISAMap m_hash_to_isa_map;
+ TypeSizeCache m_type_size_cache;
+
+protected:
+ uint32_t m_isa_to_descriptor_stop_id;
+
+ typedef std::map<ConstString, lldb::TypeWP> CompleteClassMap;
+ CompleteClassMap m_complete_class_cache;
+
+ struct ConstStringSetHelpers {
+ size_t operator()(ConstString arg) const // for hashing
+ {
+ return (size_t)arg.GetCString();
+ }
+ bool operator()(ConstString arg1,
+ ConstString arg2) const // for equality
+ {
+ return arg1.operator==(arg2);
+ }
+ };
+ typedef std::unordered_set<ConstString, ConstStringSetHelpers,
+ ConstStringSetHelpers>
+ CompleteClassSet;
+ CompleteClassSet m_negative_complete_class_cache;
+
+ ISAToDescriptorIterator GetDescriptorIterator(ConstString name);
+
+ friend class ::CommandObjectObjC_ClassTable_Dump;
+
+ std::pair<ISAToDescriptorIterator, ISAToDescriptorIterator>
+ GetDescriptorIteratorPair(bool update_if_needed = true);
+
+ void ReadObjCLibraryIfNeeded(const ModuleList &module_list);
+
+ DISALLOW_COPY_AND_ASSIGN(ObjCLanguageRuntime);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ObjCLanguageRuntime_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptExpressionOpts.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptExpressionOpts.cpp
new file mode 100644
index 000000000000..60549663db66
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptExpressionOpts.cpp
@@ -0,0 +1,191 @@
+//===-- RenderScriptExpressionOpts.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 <string>
+
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Target/TargetOptions.h"
+
+#include "clang/Basic/TargetOptions.h"
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Log.h"
+
+#include "RenderScriptExpressionOpts.h"
+#include "RenderScriptRuntime.h"
+#include "RenderScriptx86ABIFixups.h"
+
+using namespace lldb_private;
+using namespace lldb_renderscript;
+
+// [``slang``](https://android.googlesource.com/platform/frameworks/compile/slang),
+// the compiler frontend for RenderScript embeds an ARM specific triple in IR
+// that is shipped in the app, after generating IR that has some assumptions
+// that an ARM device is the target. As the IR is then compiled on a device of
+// unknown (at time the IR was generated at least) architecture, when calling
+// RenderScript API function as part of debugger expressions, we have to
+// perform a fixup pass that removes those assumptions right before the module
+// is sent to be generated by the llvm backend.
+
+namespace {
+bool registerRSDefaultTargetOpts(clang::TargetOptions &proto,
+ const llvm::Triple::ArchType &arch) {
+ switch (arch) {
+ case llvm::Triple::ArchType::x86:
+ proto.Triple = "i686--linux-android";
+ proto.CPU = "atom";
+ proto.Features.push_back("+long64");
+ // Fallthrough for common x86 family features
+ LLVM_FALLTHROUGH;
+ case llvm::Triple::ArchType::x86_64:
+ proto.Features.push_back("+mmx");
+ proto.Features.push_back("+sse");
+ proto.Features.push_back("+sse2");
+ proto.Features.push_back("+sse3");
+ proto.Features.push_back("+ssse3");
+ proto.Features.push_back("+sse4.1");
+ proto.Features.push_back("+sse4.2");
+ break;
+ case llvm::Triple::ArchType::mipsel:
+ // pretend this is `arm' for the front-end
+ proto.Triple = "armv7-none-linux-android";
+ proto.CPU = "";
+ proto.Features.push_back("+long64");
+ break;
+ case llvm::Triple::ArchType::mips64el:
+ // pretend this is `aarch64' for the front-end
+ proto.Triple = "aarch64-none-linux-android";
+ proto.CPU = "";
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+} // end anonymous namespace
+
+bool RenderScriptRuntimeModulePass::runOnModule(llvm::Module &module) {
+ bool changed_module = false;
+ Log *log(
+ GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_EXPRESSIONS));
+
+ std::string err;
+ llvm::StringRef real_triple =
+ m_process_ptr->GetTarget().GetArchitecture().GetTriple().getTriple();
+ const llvm::Target *target_info =
+ llvm::TargetRegistry::lookupTarget(real_triple, err);
+ if (!target_info) {
+ if (log)
+ log->Warning("couldn't determine real target architecture: '%s'",
+ err.c_str());
+ return false;
+ }
+
+ llvm::Optional<llvm::Reloc::Model> reloc_model = llvm::None;
+ assert(m_process_ptr && "no available lldb process");
+ switch (m_process_ptr->GetTarget().GetArchitecture().GetMachine()) {
+ case llvm::Triple::ArchType::x86:
+ changed_module |= fixupX86FunctionCalls(module);
+ // For some reason this triple gets totally missed by the backend, and must
+ // be set manually. There a reference in bcc/Main.cpp about auto feature-
+ // detection being removed from LLVM3.5, but I can't see that discussion
+ // anywhere public.
+ real_triple = "i686--linux-android";
+ break;
+ case llvm::Triple::ArchType::x86_64:
+ changed_module |= fixupX86_64FunctionCalls(module);
+ break;
+ case llvm::Triple::ArchType::mipsel:
+ case llvm::Triple::ArchType::mips64el:
+ // No actual IR fixup pass is needed on MIPS, but the datalayout and
+ // targetmachine do need to be explicitly set.
+
+ // bcc explicitly compiles MIPS code to use the static relocation model due
+ // to an issue with relocations in mclinker. see
+ // libbcc/support/CompilerConfig.cpp for details
+ reloc_model = llvm::Reloc::Static;
+ changed_module = true;
+ break;
+ case llvm::Triple::ArchType::arm:
+ case llvm::Triple::ArchType::aarch64:
+ // ARM subtargets need no fixup passes as they are the initial target as
+ // generated by the
+ // slang compiler frontend.
+ break;
+ default:
+ if (log)
+ log->Warning("Ignoring unknown renderscript target");
+ return false;
+ }
+
+ if (changed_module) {
+ llvm::TargetOptions options;
+ llvm::TargetMachine *target_machine = target_info->createTargetMachine(
+ real_triple, "", "", options, reloc_model);
+ assert(target_machine &&
+ "failed to identify RenderScriptRuntime target machine");
+ // We've been using a triple and datalayout of some ARM variant all along,
+ // so we need to let the backend know that this is no longer the case.
+ if (log) {
+ log->Printf("%s - Changing RS target triple to '%s'", __FUNCTION__,
+ real_triple.str().c_str());
+ log->Printf(
+ "%s - Changing RS datalayout to '%s'", __FUNCTION__,
+ target_machine->createDataLayout().getStringRepresentation().c_str());
+ }
+ module.setTargetTriple(real_triple);
+ module.setDataLayout(target_machine->createDataLayout());
+ }
+ return changed_module;
+}
+
+char RenderScriptRuntimeModulePass::ID = 0;
+
+namespace lldb_private {
+
+bool RenderScriptRuntime::GetOverrideExprOptions(clang::TargetOptions &proto) {
+ auto *process = GetProcess();
+ assert(process);
+ return registerRSDefaultTargetOpts(
+ proto, process->GetTarget().GetArchitecture().GetMachine());
+}
+
+bool RenderScriptRuntime::GetIRPasses(LLVMUserExpression::IRPasses &passes) {
+ if (!m_ir_passes)
+ m_ir_passes = new RSIRPasses(GetProcess());
+ assert(m_ir_passes);
+
+ passes.EarlyPasses = m_ir_passes->EarlyPasses;
+ passes.LatePasses = m_ir_passes->LatePasses;
+
+ return true;
+}
+
+namespace lldb_renderscript {
+
+RSIRPasses::RSIRPasses(Process *process) {
+ IRPasses();
+ assert(process);
+
+ EarlyPasses = std::make_shared<llvm::legacy::PassManager>();
+ assert(EarlyPasses);
+ EarlyPasses->add(new RenderScriptRuntimeModulePass(process));
+}
+
+RSIRPasses::~RSIRPasses() {}
+
+} // namespace lldb_renderscript
+} // namespace lldb_private
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptExpressionOpts.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptExpressionOpts.h
new file mode 100644
index 000000000000..3ec4e37b6db0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptExpressionOpts.h
@@ -0,0 +1,52 @@
+//===-- RenderScriptExpressionOpts.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_RENDERSCRIPT_EXPROPTS_H
+#define LLDB_RENDERSCRIPT_EXPROPTS_H
+
+#include "llvm/IR/Module.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Target/TargetOptions.h"
+
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/Target/Process.h"
+#include "lldb/lldb-private.h"
+
+#include "RenderScriptRuntime.h"
+#include "RenderScriptx86ABIFixups.h"
+
+// RenderScriptRuntimeModulePass is a simple llvm::ModulesPass that is used
+// during expression evaluation to apply RenderScript-specific fixes for
+// expression evaluation. In particular this is used to make expression IR
+// conformant with the ABI generated by the slang frontend. This ModulePass is
+// executed in ClangExpressionParser::PrepareForExecution whenever an
+// expression's DWARF language is eLanguageTypeExtRenderscript
+
+class RenderScriptRuntimeModulePass : public llvm::ModulePass {
+public:
+ static char ID;
+ RenderScriptRuntimeModulePass(const lldb_private::Process *process)
+ : ModulePass(ID), m_process_ptr(process) {}
+
+ bool runOnModule(llvm::Module &module) override;
+
+private:
+ const lldb_private::Process *m_process_ptr;
+};
+
+namespace lldb_private {
+namespace lldb_renderscript {
+struct RSIRPasses : public lldb_private::LLVMUserExpression::IRPasses {
+ RSIRPasses(lldb_private::Process *process);
+
+ ~RSIRPasses();
+};
+} // namespace lldb_renderscript
+} // namespace lldb_private
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp
new file mode 100644
index 000000000000..c9cd34cf379d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp
@@ -0,0 +1,5055 @@
+//===-- RenderScriptRuntime.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 "RenderScriptRuntime.h"
+#include "RenderScriptScriptGroup.h"
+
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/DumpDataExtractor.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/DataFormatters/DumpValueObjectOptions.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Status.h"
+
+#include "llvm/ADT/StringSwitch.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_renderscript;
+
+#define FMT_COORD "(%" PRIu32 ", %" PRIu32 ", %" PRIu32 ")"
+
+char RenderScriptRuntime::ID = 0;
+
+namespace {
+
+// The empirical_type adds a basic level of validation to arbitrary data
+// allowing us to track if data has been discovered and stored or not. An
+// empirical_type will be marked as valid only if it has been explicitly
+// assigned to.
+template <typename type_t> class empirical_type {
+public:
+ // Ctor. Contents is invalid when constructed.
+ empirical_type() : valid(false) {}
+
+ // Return true and copy contents to out if valid, else return false.
+ bool get(type_t &out) const {
+ if (valid)
+ out = data;
+ return valid;
+ }
+
+ // Return a pointer to the contents or nullptr if it was not valid.
+ const type_t *get() const { return valid ? &data : nullptr; }
+
+ // Assign data explicitly.
+ void set(const type_t in) {
+ data = in;
+ valid = true;
+ }
+
+ // Mark contents as invalid.
+ void invalidate() { valid = false; }
+
+ // Returns true if this type contains valid data.
+ bool isValid() const { return valid; }
+
+ // Assignment operator.
+ empirical_type<type_t> &operator=(const type_t in) {
+ set(in);
+ return *this;
+ }
+
+ // Dereference operator returns contents.
+ // Warning: Will assert if not valid so use only when you know data is valid.
+ const type_t &operator*() const {
+ assert(valid);
+ return data;
+ }
+
+protected:
+ bool valid;
+ type_t data;
+};
+
+// ArgItem is used by the GetArgs() function when reading function arguments
+// from the target.
+struct ArgItem {
+ enum { ePointer, eInt32, eInt64, eLong, eBool } type;
+
+ uint64_t value;
+
+ explicit operator uint64_t() const { return value; }
+};
+
+// Context structure to be passed into GetArgsXXX(), argument reading functions
+// below.
+struct GetArgsCtx {
+ RegisterContext *reg_ctx;
+ Process *process;
+};
+
+bool GetArgsX86(const GetArgsCtx &ctx, ArgItem *arg_list, size_t num_args) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE);
+
+ Status err;
+
+ // get the current stack pointer
+ uint64_t sp = ctx.reg_ctx->GetSP();
+
+ for (size_t i = 0; i < num_args; ++i) {
+ ArgItem &arg = arg_list[i];
+ // advance up the stack by one argument
+ sp += sizeof(uint32_t);
+ // get the argument type size
+ size_t arg_size = sizeof(uint32_t);
+ // read the argument from memory
+ arg.value = 0;
+ Status err;
+ size_t read =
+ ctx.process->ReadMemory(sp, &arg.value, sizeof(uint32_t), err);
+ if (read != arg_size || !err.Success()) {
+ if (log)
+ log->Printf("%s - error reading argument: %" PRIu64 " '%s'",
+ __FUNCTION__, uint64_t(i), err.AsCString());
+ return false;
+ }
+ }
+ return true;
+}
+
+bool GetArgsX86_64(GetArgsCtx &ctx, ArgItem *arg_list, size_t num_args) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE);
+
+ // number of arguments passed in registers
+ static const uint32_t args_in_reg = 6;
+ // register passing order
+ static const std::array<const char *, args_in_reg> reg_names{
+ {"rdi", "rsi", "rdx", "rcx", "r8", "r9"}};
+ // argument type to size mapping
+ static const std::array<size_t, 5> arg_size{{
+ 8, // ePointer,
+ 4, // eInt32,
+ 8, // eInt64,
+ 8, // eLong,
+ 4, // eBool,
+ }};
+
+ Status err;
+
+ // get the current stack pointer
+ uint64_t sp = ctx.reg_ctx->GetSP();
+ // step over the return address
+ sp += sizeof(uint64_t);
+
+ // check the stack alignment was correct (16 byte aligned)
+ if ((sp & 0xf) != 0x0) {
+ if (log)
+ log->Printf("%s - stack misaligned", __FUNCTION__);
+ return false;
+ }
+
+ // find the start of arguments on the stack
+ uint64_t sp_offset = 0;
+ for (uint32_t i = args_in_reg; i < num_args; ++i) {
+ sp_offset += arg_size[arg_list[i].type];
+ }
+ // round up to multiple of 16
+ sp_offset = (sp_offset + 0xf) & 0xf;
+ sp += sp_offset;
+
+ for (size_t i = 0; i < num_args; ++i) {
+ bool success = false;
+ ArgItem &arg = arg_list[i];
+ // arguments passed in registers
+ if (i < args_in_reg) {
+ const RegisterInfo *reg =
+ ctx.reg_ctx->GetRegisterInfoByName(reg_names[i]);
+ RegisterValue reg_val;
+ if (ctx.reg_ctx->ReadRegister(reg, reg_val))
+ arg.value = reg_val.GetAsUInt64(0, &success);
+ }
+ // arguments passed on the stack
+ else {
+ // get the argument type size
+ const size_t size = arg_size[arg_list[i].type];
+ // read the argument from memory
+ arg.value = 0;
+ // note: due to little endian layout reading 4 or 8 bytes will give the
+ // correct value.
+ size_t read = ctx.process->ReadMemory(sp, &arg.value, size, err);
+ success = (err.Success() && read == size);
+ // advance past this argument
+ sp -= size;
+ }
+ // fail if we couldn't read this argument
+ if (!success) {
+ if (log)
+ log->Printf("%s - error reading argument: %" PRIu64 ", reason: %s",
+ __FUNCTION__, uint64_t(i), err.AsCString("n/a"));
+ return false;
+ }
+ }
+ return true;
+}
+
+bool GetArgsArm(GetArgsCtx &ctx, ArgItem *arg_list, size_t num_args) {
+ // number of arguments passed in registers
+ static const uint32_t args_in_reg = 4;
+
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE);
+
+ Status err;
+
+ // get the current stack pointer
+ uint64_t sp = ctx.reg_ctx->GetSP();
+
+ for (size_t i = 0; i < num_args; ++i) {
+ bool success = false;
+ ArgItem &arg = arg_list[i];
+ // arguments passed in registers
+ if (i < args_in_reg) {
+ const RegisterInfo *reg = ctx.reg_ctx->GetRegisterInfoAtIndex(i);
+ RegisterValue reg_val;
+ if (ctx.reg_ctx->ReadRegister(reg, reg_val))
+ arg.value = reg_val.GetAsUInt32(0, &success);
+ }
+ // arguments passed on the stack
+ else {
+ // get the argument type size
+ const size_t arg_size = sizeof(uint32_t);
+ // clear all 64bits
+ arg.value = 0;
+ // read this argument from memory
+ size_t bytes_read =
+ ctx.process->ReadMemory(sp, &arg.value, arg_size, err);
+ success = (err.Success() && bytes_read == arg_size);
+ // advance the stack pointer
+ sp += sizeof(uint32_t);
+ }
+ // fail if we couldn't read this argument
+ if (!success) {
+ if (log)
+ log->Printf("%s - error reading argument: %" PRIu64 ", reason: %s",
+ __FUNCTION__, uint64_t(i), err.AsCString("n/a"));
+ return false;
+ }
+ }
+ return true;
+}
+
+bool GetArgsAarch64(GetArgsCtx &ctx, ArgItem *arg_list, size_t num_args) {
+ // number of arguments passed in registers
+ static const uint32_t args_in_reg = 8;
+
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE);
+
+ for (size_t i = 0; i < num_args; ++i) {
+ bool success = false;
+ ArgItem &arg = arg_list[i];
+ // arguments passed in registers
+ if (i < args_in_reg) {
+ const RegisterInfo *reg = ctx.reg_ctx->GetRegisterInfoAtIndex(i);
+ RegisterValue reg_val;
+ if (ctx.reg_ctx->ReadRegister(reg, reg_val))
+ arg.value = reg_val.GetAsUInt64(0, &success);
+ }
+ // arguments passed on the stack
+ else {
+ if (log)
+ log->Printf("%s - reading arguments spilled to stack not implemented",
+ __FUNCTION__);
+ }
+ // fail if we couldn't read this argument
+ if (!success) {
+ if (log)
+ log->Printf("%s - error reading argument: %" PRIu64, __FUNCTION__,
+ uint64_t(i));
+ return false;
+ }
+ }
+ return true;
+}
+
+bool GetArgsMipsel(GetArgsCtx &ctx, ArgItem *arg_list, size_t num_args) {
+ // number of arguments passed in registers
+ static const uint32_t args_in_reg = 4;
+ // register file offset to first argument
+ static const uint32_t reg_offset = 4;
+
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE);
+
+ Status err;
+
+ // find offset to arguments on the stack (+16 to skip over a0-a3 shadow
+ // space)
+ uint64_t sp = ctx.reg_ctx->GetSP() + 16;
+
+ for (size_t i = 0; i < num_args; ++i) {
+ bool success = false;
+ ArgItem &arg = arg_list[i];
+ // arguments passed in registers
+ if (i < args_in_reg) {
+ const RegisterInfo *reg =
+ ctx.reg_ctx->GetRegisterInfoAtIndex(i + reg_offset);
+ RegisterValue reg_val;
+ if (ctx.reg_ctx->ReadRegister(reg, reg_val))
+ arg.value = reg_val.GetAsUInt64(0, &success);
+ }
+ // arguments passed on the stack
+ else {
+ const size_t arg_size = sizeof(uint32_t);
+ arg.value = 0;
+ size_t bytes_read =
+ ctx.process->ReadMemory(sp, &arg.value, arg_size, err);
+ success = (err.Success() && bytes_read == arg_size);
+ // advance the stack pointer
+ sp += arg_size;
+ }
+ // fail if we couldn't read this argument
+ if (!success) {
+ if (log)
+ log->Printf("%s - error reading argument: %" PRIu64 ", reason: %s",
+ __FUNCTION__, uint64_t(i), err.AsCString("n/a"));
+ return false;
+ }
+ }
+ return true;
+}
+
+bool GetArgsMips64el(GetArgsCtx &ctx, ArgItem *arg_list, size_t num_args) {
+ // number of arguments passed in registers
+ static const uint32_t args_in_reg = 8;
+ // register file offset to first argument
+ static const uint32_t reg_offset = 4;
+
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE);
+
+ Status err;
+
+ // get the current stack pointer
+ uint64_t sp = ctx.reg_ctx->GetSP();
+
+ for (size_t i = 0; i < num_args; ++i) {
+ bool success = false;
+ ArgItem &arg = arg_list[i];
+ // arguments passed in registers
+ if (i < args_in_reg) {
+ const RegisterInfo *reg =
+ ctx.reg_ctx->GetRegisterInfoAtIndex(i + reg_offset);
+ RegisterValue reg_val;
+ if (ctx.reg_ctx->ReadRegister(reg, reg_val))
+ arg.value = reg_val.GetAsUInt64(0, &success);
+ }
+ // arguments passed on the stack
+ else {
+ // get the argument type size
+ const size_t arg_size = sizeof(uint64_t);
+ // clear all 64bits
+ arg.value = 0;
+ // read this argument from memory
+ size_t bytes_read =
+ ctx.process->ReadMemory(sp, &arg.value, arg_size, err);
+ success = (err.Success() && bytes_read == arg_size);
+ // advance the stack pointer
+ sp += arg_size;
+ }
+ // fail if we couldn't read this argument
+ if (!success) {
+ if (log)
+ log->Printf("%s - error reading argument: %" PRIu64 ", reason: %s",
+ __FUNCTION__, uint64_t(i), err.AsCString("n/a"));
+ return false;
+ }
+ }
+ return true;
+}
+
+bool GetArgs(ExecutionContext &exe_ctx, ArgItem *arg_list, size_t num_args) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE);
+
+ // verify that we have a target
+ if (!exe_ctx.GetTargetPtr()) {
+ if (log)
+ log->Printf("%s - invalid target", __FUNCTION__);
+ return false;
+ }
+
+ GetArgsCtx ctx = {exe_ctx.GetRegisterContext(), exe_ctx.GetProcessPtr()};
+ assert(ctx.reg_ctx && ctx.process);
+
+ // dispatch based on architecture
+ switch (exe_ctx.GetTargetPtr()->GetArchitecture().GetMachine()) {
+ case llvm::Triple::ArchType::x86:
+ return GetArgsX86(ctx, arg_list, num_args);
+
+ case llvm::Triple::ArchType::x86_64:
+ return GetArgsX86_64(ctx, arg_list, num_args);
+
+ case llvm::Triple::ArchType::arm:
+ return GetArgsArm(ctx, arg_list, num_args);
+
+ case llvm::Triple::ArchType::aarch64:
+ return GetArgsAarch64(ctx, arg_list, num_args);
+
+ case llvm::Triple::ArchType::mipsel:
+ return GetArgsMipsel(ctx, arg_list, num_args);
+
+ case llvm::Triple::ArchType::mips64el:
+ return GetArgsMips64el(ctx, arg_list, num_args);
+
+ default:
+ // unsupported architecture
+ if (log) {
+ log->Printf(
+ "%s - architecture not supported: '%s'", __FUNCTION__,
+ exe_ctx.GetTargetRef().GetArchitecture().GetArchitectureName());
+ }
+ return false;
+ }
+}
+
+bool IsRenderScriptScriptModule(ModuleSP module) {
+ if (!module)
+ return false;
+ return module->FindFirstSymbolWithNameAndType(ConstString(".rs.info"),
+ eSymbolTypeData) != nullptr;
+}
+
+bool ParseCoordinate(llvm::StringRef coord_s, RSCoordinate &coord) {
+ // takes an argument of the form 'num[,num][,num]'. Where 'coord_s' is a
+ // comma separated 1,2 or 3-dimensional coordinate with the whitespace
+ // trimmed. Missing coordinates are defaulted to zero. If parsing of any
+ // elements fails the contents of &coord are undefined and `false` is
+ // returned, `true` otherwise
+
+ RegularExpression regex;
+ RegularExpression::Match regex_match(3);
+
+ bool matched = false;
+ if (regex.Compile(llvm::StringRef("^([0-9]+),([0-9]+),([0-9]+)$")) &&
+ regex.Execute(coord_s, &regex_match))
+ matched = true;
+ else if (regex.Compile(llvm::StringRef("^([0-9]+),([0-9]+)$")) &&
+ regex.Execute(coord_s, &regex_match))
+ matched = true;
+ else if (regex.Compile(llvm::StringRef("^([0-9]+)$")) &&
+ regex.Execute(coord_s, &regex_match))
+ matched = true;
+
+ if (!matched)
+ return false;
+
+ auto get_index = [&](int idx, uint32_t &i) -> bool {
+ std::string group;
+ errno = 0;
+ if (regex_match.GetMatchAtIndex(coord_s.str().c_str(), idx + 1, group))
+ return !llvm::StringRef(group).getAsInteger<uint32_t>(10, i);
+ return true;
+ };
+
+ return get_index(0, coord.x) && get_index(1, coord.y) &&
+ get_index(2, coord.z);
+}
+
+bool SkipPrologue(lldb::ModuleSP &module, Address &addr) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+ SymbolContext sc;
+ uint32_t resolved_flags =
+ module->ResolveSymbolContextForAddress(addr, eSymbolContextFunction, sc);
+ if (resolved_flags & eSymbolContextFunction) {
+ if (sc.function) {
+ const uint32_t offset = sc.function->GetPrologueByteSize();
+ ConstString name = sc.GetFunctionName();
+ if (offset)
+ addr.Slide(offset);
+ if (log)
+ log->Printf("%s: Prologue offset for %s is %" PRIu32, __FUNCTION__,
+ name.AsCString(), offset);
+ }
+ return true;
+ } else
+ return false;
+}
+} // anonymous namespace
+
+// The ScriptDetails class collects data associated with a single script
+// instance.
+struct RenderScriptRuntime::ScriptDetails {
+ ~ScriptDetails() = default;
+
+ enum ScriptType { eScript, eScriptC };
+
+ // The derived type of the script.
+ empirical_type<ScriptType> type;
+ // The name of the original source file.
+ empirical_type<std::string> res_name;
+ // Path to script .so file on the device.
+ empirical_type<std::string> shared_lib;
+ // Directory where kernel objects are cached on device.
+ empirical_type<std::string> cache_dir;
+ // Pointer to the context which owns this script.
+ empirical_type<lldb::addr_t> context;
+ // Pointer to the script object itself.
+ empirical_type<lldb::addr_t> script;
+};
+
+// This Element class represents the Element object in RS, defining the type
+// associated with an Allocation.
+struct RenderScriptRuntime::Element {
+ // Taken from rsDefines.h
+ enum DataKind {
+ RS_KIND_USER,
+ RS_KIND_PIXEL_L = 7,
+ RS_KIND_PIXEL_A,
+ RS_KIND_PIXEL_LA,
+ RS_KIND_PIXEL_RGB,
+ RS_KIND_PIXEL_RGBA,
+ RS_KIND_PIXEL_DEPTH,
+ RS_KIND_PIXEL_YUV,
+ RS_KIND_INVALID = 100
+ };
+
+ // Taken from rsDefines.h
+ enum DataType {
+ RS_TYPE_NONE = 0,
+ RS_TYPE_FLOAT_16,
+ RS_TYPE_FLOAT_32,
+ RS_TYPE_FLOAT_64,
+ RS_TYPE_SIGNED_8,
+ RS_TYPE_SIGNED_16,
+ RS_TYPE_SIGNED_32,
+ RS_TYPE_SIGNED_64,
+ RS_TYPE_UNSIGNED_8,
+ RS_TYPE_UNSIGNED_16,
+ RS_TYPE_UNSIGNED_32,
+ RS_TYPE_UNSIGNED_64,
+ RS_TYPE_BOOLEAN,
+
+ RS_TYPE_UNSIGNED_5_6_5,
+ RS_TYPE_UNSIGNED_5_5_5_1,
+ RS_TYPE_UNSIGNED_4_4_4_4,
+
+ RS_TYPE_MATRIX_4X4,
+ RS_TYPE_MATRIX_3X3,
+ RS_TYPE_MATRIX_2X2,
+
+ RS_TYPE_ELEMENT = 1000,
+ RS_TYPE_TYPE,
+ RS_TYPE_ALLOCATION,
+ RS_TYPE_SAMPLER,
+ RS_TYPE_SCRIPT,
+ RS_TYPE_MESH,
+ RS_TYPE_PROGRAM_FRAGMENT,
+ RS_TYPE_PROGRAM_VERTEX,
+ RS_TYPE_PROGRAM_RASTER,
+ RS_TYPE_PROGRAM_STORE,
+ RS_TYPE_FONT,
+
+ RS_TYPE_INVALID = 10000
+ };
+
+ std::vector<Element> children; // Child Element fields for structs
+ empirical_type<lldb::addr_t>
+ element_ptr; // Pointer to the RS Element of the Type
+ empirical_type<DataType>
+ type; // Type of each data pointer stored by the allocation
+ empirical_type<DataKind>
+ type_kind; // Defines pixel type if Allocation is created from an image
+ empirical_type<uint32_t>
+ type_vec_size; // Vector size of each data point, e.g '4' for uchar4
+ empirical_type<uint32_t> field_count; // Number of Subelements
+ empirical_type<uint32_t> datum_size; // Size of a single Element with padding
+ empirical_type<uint32_t> padding; // Number of padding bytes
+ empirical_type<uint32_t>
+ array_size; // Number of items in array, only needed for structs
+ ConstString type_name; // Name of type, only needed for structs
+
+ static ConstString
+ GetFallbackStructName(); // Print this as the type name of a struct Element
+ // If we can't resolve the actual struct name
+
+ bool ShouldRefresh() const {
+ const bool valid_ptr = element_ptr.isValid() && *element_ptr.get() != 0x0;
+ const bool valid_type =
+ type.isValid() && type_vec_size.isValid() && type_kind.isValid();
+ return !valid_ptr || !valid_type || !datum_size.isValid();
+ }
+};
+
+// This AllocationDetails class collects data associated with a single
+// allocation instance.
+struct RenderScriptRuntime::AllocationDetails {
+ struct Dimension {
+ uint32_t dim_1;
+ uint32_t dim_2;
+ uint32_t dim_3;
+ uint32_t cube_map;
+
+ Dimension() {
+ dim_1 = 0;
+ dim_2 = 0;
+ dim_3 = 0;
+ cube_map = 0;
+ }
+ };
+
+ // The FileHeader struct specifies the header we use for writing allocations
+ // to a binary file. Our format begins with the ASCII characters "RSAD",
+ // identifying the file as an allocation dump. Member variables dims and
+ // hdr_size are then written consecutively, immediately followed by an
+ // instance of the ElementHeader struct. Because Elements can contain
+ // subelements, there may be more than one instance of the ElementHeader
+ // struct. With this first instance being the root element, and the other
+ // instances being the root's descendants. To identify which instances are an
+ // ElementHeader's children, each struct is immediately followed by a
+ // sequence of consecutive offsets to the start of its child structs. These
+ // offsets are
+ // 4 bytes in size, and the 0 offset signifies no more children.
+ struct FileHeader {
+ uint8_t ident[4]; // ASCII 'RSAD' identifying the file
+ uint32_t dims[3]; // Dimensions
+ uint16_t hdr_size; // Header size in bytes, including all element headers
+ };
+
+ struct ElementHeader {
+ uint16_t type; // DataType enum
+ uint32_t kind; // DataKind enum
+ uint32_t element_size; // Size of a single element, including padding
+ uint16_t vector_size; // Vector width
+ uint32_t array_size; // Number of elements in array
+ };
+
+ // Monotonically increasing from 1
+ static uint32_t ID;
+
+ // Maps Allocation DataType enum and vector size to printable strings using
+ // mapping from RenderScript numerical types summary documentation
+ static const char *RsDataTypeToString[][4];
+
+ // Maps Allocation DataKind enum to printable strings
+ static const char *RsDataKindToString[];
+
+ // Maps allocation types to format sizes for printing.
+ static const uint32_t RSTypeToFormat[][3];
+
+ // Give each allocation an ID as a way
+ // for commands to reference it.
+ const uint32_t id;
+
+ // Allocation Element type
+ RenderScriptRuntime::Element element;
+ // Dimensions of the Allocation
+ empirical_type<Dimension> dimension;
+ // Pointer to address of the RS Allocation
+ empirical_type<lldb::addr_t> address;
+ // Pointer to the data held by the Allocation
+ empirical_type<lldb::addr_t> data_ptr;
+ // Pointer to the RS Type of the Allocation
+ empirical_type<lldb::addr_t> type_ptr;
+ // Pointer to the RS Context of the Allocation
+ empirical_type<lldb::addr_t> context;
+ // Size of the allocation
+ empirical_type<uint32_t> size;
+ // Stride between rows of the allocation
+ empirical_type<uint32_t> stride;
+
+ // Give each allocation an id, so we can reference it in user commands.
+ AllocationDetails() : id(ID++) {}
+
+ bool ShouldRefresh() const {
+ bool valid_ptrs = data_ptr.isValid() && *data_ptr.get() != 0x0;
+ valid_ptrs = valid_ptrs && type_ptr.isValid() && *type_ptr.get() != 0x0;
+ return !valid_ptrs || !dimension.isValid() || !size.isValid() ||
+ element.ShouldRefresh();
+ }
+};
+
+ConstString RenderScriptRuntime::Element::GetFallbackStructName() {
+ static const ConstString FallbackStructName("struct");
+ return FallbackStructName;
+}
+
+uint32_t RenderScriptRuntime::AllocationDetails::ID = 1;
+
+const char *RenderScriptRuntime::AllocationDetails::RsDataKindToString[] = {
+ "User", "Undefined", "Undefined", "Undefined",
+ "Undefined", "Undefined", "Undefined", // Enum jumps from 0 to 7
+ "L Pixel", "A Pixel", "LA Pixel", "RGB Pixel",
+ "RGBA Pixel", "Pixel Depth", "YUV Pixel"};
+
+const char *RenderScriptRuntime::AllocationDetails::RsDataTypeToString[][4] = {
+ {"None", "None", "None", "None"},
+ {"half", "half2", "half3", "half4"},
+ {"float", "float2", "float3", "float4"},
+ {"double", "double2", "double3", "double4"},
+ {"char", "char2", "char3", "char4"},
+ {"short", "short2", "short3", "short4"},
+ {"int", "int2", "int3", "int4"},
+ {"long", "long2", "long3", "long4"},
+ {"uchar", "uchar2", "uchar3", "uchar4"},
+ {"ushort", "ushort2", "ushort3", "ushort4"},
+ {"uint", "uint2", "uint3", "uint4"},
+ {"ulong", "ulong2", "ulong3", "ulong4"},
+ {"bool", "bool2", "bool3", "bool4"},
+ {"packed_565", "packed_565", "packed_565", "packed_565"},
+ {"packed_5551", "packed_5551", "packed_5551", "packed_5551"},
+ {"packed_4444", "packed_4444", "packed_4444", "packed_4444"},
+ {"rs_matrix4x4", "rs_matrix4x4", "rs_matrix4x4", "rs_matrix4x4"},
+ {"rs_matrix3x3", "rs_matrix3x3", "rs_matrix3x3", "rs_matrix3x3"},
+ {"rs_matrix2x2", "rs_matrix2x2", "rs_matrix2x2", "rs_matrix2x2"},
+
+ // Handlers
+ {"RS Element", "RS Element", "RS Element", "RS Element"},
+ {"RS Type", "RS Type", "RS Type", "RS Type"},
+ {"RS Allocation", "RS Allocation", "RS Allocation", "RS Allocation"},
+ {"RS Sampler", "RS Sampler", "RS Sampler", "RS Sampler"},
+ {"RS Script", "RS Script", "RS Script", "RS Script"},
+
+ // Deprecated
+ {"RS Mesh", "RS Mesh", "RS Mesh", "RS Mesh"},
+ {"RS Program Fragment", "RS Program Fragment", "RS Program Fragment",
+ "RS Program Fragment"},
+ {"RS Program Vertex", "RS Program Vertex", "RS Program Vertex",
+ "RS Program Vertex"},
+ {"RS Program Raster", "RS Program Raster", "RS Program Raster",
+ "RS Program Raster"},
+ {"RS Program Store", "RS Program Store", "RS Program Store",
+ "RS Program Store"},
+ {"RS Font", "RS Font", "RS Font", "RS Font"}};
+
+// Used as an index into the RSTypeToFormat array elements
+enum TypeToFormatIndex { eFormatSingle = 0, eFormatVector, eElementSize };
+
+// { format enum of single element, format enum of element vector, size of
+// element}
+const uint32_t RenderScriptRuntime::AllocationDetails::RSTypeToFormat[][3] = {
+ // RS_TYPE_NONE
+ {eFormatHex, eFormatHex, 1},
+ // RS_TYPE_FLOAT_16
+ {eFormatFloat, eFormatVectorOfFloat16, 2},
+ // RS_TYPE_FLOAT_32
+ {eFormatFloat, eFormatVectorOfFloat32, sizeof(float)},
+ // RS_TYPE_FLOAT_64
+ {eFormatFloat, eFormatVectorOfFloat64, sizeof(double)},
+ // RS_TYPE_SIGNED_8
+ {eFormatDecimal, eFormatVectorOfSInt8, sizeof(int8_t)},
+ // RS_TYPE_SIGNED_16
+ {eFormatDecimal, eFormatVectorOfSInt16, sizeof(int16_t)},
+ // RS_TYPE_SIGNED_32
+ {eFormatDecimal, eFormatVectorOfSInt32, sizeof(int32_t)},
+ // RS_TYPE_SIGNED_64
+ {eFormatDecimal, eFormatVectorOfSInt64, sizeof(int64_t)},
+ // RS_TYPE_UNSIGNED_8
+ {eFormatDecimal, eFormatVectorOfUInt8, sizeof(uint8_t)},
+ // RS_TYPE_UNSIGNED_16
+ {eFormatDecimal, eFormatVectorOfUInt16, sizeof(uint16_t)},
+ // RS_TYPE_UNSIGNED_32
+ {eFormatDecimal, eFormatVectorOfUInt32, sizeof(uint32_t)},
+ // RS_TYPE_UNSIGNED_64
+ {eFormatDecimal, eFormatVectorOfUInt64, sizeof(uint64_t)},
+ // RS_TYPE_BOOL
+ {eFormatBoolean, eFormatBoolean, 1},
+ // RS_TYPE_UNSIGNED_5_6_5
+ {eFormatHex, eFormatHex, sizeof(uint16_t)},
+ // RS_TYPE_UNSIGNED_5_5_5_1
+ {eFormatHex, eFormatHex, sizeof(uint16_t)},
+ // RS_TYPE_UNSIGNED_4_4_4_4
+ {eFormatHex, eFormatHex, sizeof(uint16_t)},
+ // RS_TYPE_MATRIX_4X4
+ {eFormatVectorOfFloat32, eFormatVectorOfFloat32, sizeof(float) * 16},
+ // RS_TYPE_MATRIX_3X3
+ {eFormatVectorOfFloat32, eFormatVectorOfFloat32, sizeof(float) * 9},
+ // RS_TYPE_MATRIX_2X2
+ {eFormatVectorOfFloat32, eFormatVectorOfFloat32, sizeof(float) * 4}};
+
+// Static Functions
+LanguageRuntime *
+RenderScriptRuntime::CreateInstance(Process *process,
+ lldb::LanguageType language) {
+
+ if (language == eLanguageTypeExtRenderScript)
+ return new RenderScriptRuntime(process);
+ else
+ return nullptr;
+}
+
+// Callback with a module to search for matching symbols. We first check that
+// the module contains RS kernels. Then look for a symbol which matches our
+// kernel name. The breakpoint address is finally set using the address of this
+// symbol.
+Searcher::CallbackReturn
+RSBreakpointResolver::SearchCallback(SearchFilter &filter,
+ SymbolContext &context, Address *, bool) {
+ ModuleSP module = context.module_sp;
+
+ if (!module || !IsRenderScriptScriptModule(module))
+ return Searcher::eCallbackReturnContinue;
+
+ // Attempt to set a breakpoint on the kernel name symbol within the module
+ // library. If it's not found, it's likely debug info is unavailable - try to
+ // set a breakpoint on <name>.expand.
+ const Symbol *kernel_sym =
+ module->FindFirstSymbolWithNameAndType(m_kernel_name, eSymbolTypeCode);
+ if (!kernel_sym) {
+ std::string kernel_name_expanded(m_kernel_name.AsCString());
+ kernel_name_expanded.append(".expand");
+ kernel_sym = module->FindFirstSymbolWithNameAndType(
+ ConstString(kernel_name_expanded.c_str()), eSymbolTypeCode);
+ }
+
+ if (kernel_sym) {
+ Address bp_addr = kernel_sym->GetAddress();
+ if (filter.AddressPasses(bp_addr))
+ m_breakpoint->AddLocation(bp_addr);
+ }
+
+ return Searcher::eCallbackReturnContinue;
+}
+
+Searcher::CallbackReturn
+RSReduceBreakpointResolver::SearchCallback(lldb_private::SearchFilter &filter,
+ lldb_private::SymbolContext &context,
+ Address *, bool) {
+ // We need to have access to the list of reductions currently parsed, as
+ // reduce names don't actually exist as symbols in a module. They are only
+ // identifiable by parsing the .rs.info packet, or finding the expand symbol.
+ // We therefore need access to the list of parsed rs modules to properly
+ // resolve reduction names.
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ ModuleSP module = context.module_sp;
+
+ if (!module || !IsRenderScriptScriptModule(module))
+ return Searcher::eCallbackReturnContinue;
+
+ if (!m_rsmodules)
+ return Searcher::eCallbackReturnContinue;
+
+ for (const auto &module_desc : *m_rsmodules) {
+ if (module_desc->m_module != module)
+ continue;
+
+ for (const auto &reduction : module_desc->m_reductions) {
+ if (reduction.m_reduce_name != m_reduce_name)
+ continue;
+
+ std::array<std::pair<ConstString, int>, 5> funcs{
+ {{reduction.m_init_name, eKernelTypeInit},
+ {reduction.m_accum_name, eKernelTypeAccum},
+ {reduction.m_comb_name, eKernelTypeComb},
+ {reduction.m_outc_name, eKernelTypeOutC},
+ {reduction.m_halter_name, eKernelTypeHalter}}};
+
+ for (const auto &kernel : funcs) {
+ // Skip constituent functions that don't match our spec
+ if (!(m_kernel_types & kernel.second))
+ continue;
+
+ const auto kernel_name = kernel.first;
+ const auto symbol = module->FindFirstSymbolWithNameAndType(
+ kernel_name, eSymbolTypeCode);
+ if (!symbol)
+ continue;
+
+ auto address = symbol->GetAddress();
+ if (filter.AddressPasses(address)) {
+ bool new_bp;
+ if (!SkipPrologue(module, address)) {
+ if (log)
+ log->Printf("%s: Error trying to skip prologue", __FUNCTION__);
+ }
+ m_breakpoint->AddLocation(address, &new_bp);
+ if (log)
+ log->Printf("%s: %s reduction breakpoint on %s in %s", __FUNCTION__,
+ new_bp ? "new" : "existing", kernel_name.GetCString(),
+ address.GetModule()->GetFileSpec().GetCString());
+ }
+ }
+ }
+ }
+ return eCallbackReturnContinue;
+}
+
+Searcher::CallbackReturn RSScriptGroupBreakpointResolver::SearchCallback(
+ SearchFilter &filter, SymbolContext &context, Address *addr,
+ bool containing) {
+
+ if (!m_breakpoint)
+ return eCallbackReturnContinue;
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ ModuleSP &module = context.module_sp;
+
+ if (!module || !IsRenderScriptScriptModule(module))
+ return Searcher::eCallbackReturnContinue;
+
+ std::vector<std::string> names;
+ m_breakpoint->GetNames(names);
+ if (names.empty())
+ return eCallbackReturnContinue;
+
+ for (auto &name : names) {
+ const RSScriptGroupDescriptorSP sg = FindScriptGroup(ConstString(name));
+ if (!sg) {
+ if (log)
+ log->Printf("%s: could not find script group for %s", __FUNCTION__,
+ name.c_str());
+ continue;
+ }
+
+ if (log)
+ log->Printf("%s: Found ScriptGroup for %s", __FUNCTION__, name.c_str());
+
+ for (const RSScriptGroupDescriptor::Kernel &k : sg->m_kernels) {
+ if (log) {
+ log->Printf("%s: Adding breakpoint for %s", __FUNCTION__,
+ k.m_name.AsCString());
+ log->Printf("%s: Kernel address 0x%" PRIx64, __FUNCTION__, k.m_addr);
+ }
+
+ const lldb_private::Symbol *sym =
+ module->FindFirstSymbolWithNameAndType(k.m_name, eSymbolTypeCode);
+ if (!sym) {
+ if (log)
+ log->Printf("%s: Unable to find symbol for %s", __FUNCTION__,
+ k.m_name.AsCString());
+ continue;
+ }
+
+ if (log) {
+ log->Printf("%s: Found symbol name is %s", __FUNCTION__,
+ sym->GetName().AsCString());
+ }
+
+ auto address = sym->GetAddress();
+ if (!SkipPrologue(module, address)) {
+ if (log)
+ log->Printf("%s: Error trying to skip prologue", __FUNCTION__);
+ }
+
+ bool new_bp;
+ m_breakpoint->AddLocation(address, &new_bp);
+
+ if (log)
+ log->Printf("%s: Placed %sbreakpoint on %s", __FUNCTION__,
+ new_bp ? "new " : "", k.m_name.AsCString());
+
+ // exit after placing the first breakpoint if we do not intend to stop on
+ // all kernels making up this script group
+ if (!m_stop_on_all)
+ break;
+ }
+ }
+
+ return eCallbackReturnContinue;
+}
+
+void RenderScriptRuntime::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ "RenderScript language support", CreateInstance,
+ GetCommandObject);
+}
+
+void RenderScriptRuntime::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString RenderScriptRuntime::GetPluginNameStatic() {
+ static ConstString plugin_name("renderscript");
+ return plugin_name;
+}
+
+RenderScriptRuntime::ModuleKind
+RenderScriptRuntime::GetModuleKind(const lldb::ModuleSP &module_sp) {
+ if (module_sp) {
+ if (IsRenderScriptScriptModule(module_sp))
+ return eModuleKindKernelObj;
+
+ // Is this the main RS runtime library
+ const ConstString rs_lib("libRS.so");
+ if (module_sp->GetFileSpec().GetFilename() == rs_lib) {
+ return eModuleKindLibRS;
+ }
+
+ const ConstString rs_driverlib("libRSDriver.so");
+ if (module_sp->GetFileSpec().GetFilename() == rs_driverlib) {
+ return eModuleKindDriver;
+ }
+
+ const ConstString rs_cpureflib("libRSCpuRef.so");
+ if (module_sp->GetFileSpec().GetFilename() == rs_cpureflib) {
+ return eModuleKindImpl;
+ }
+ }
+ return eModuleKindIgnored;
+}
+
+bool RenderScriptRuntime::IsRenderScriptModule(
+ const lldb::ModuleSP &module_sp) {
+ return GetModuleKind(module_sp) != eModuleKindIgnored;
+}
+
+void RenderScriptRuntime::ModulesDidLoad(const ModuleList &module_list) {
+ std::lock_guard<std::recursive_mutex> guard(module_list.GetMutex());
+
+ size_t num_modules = module_list.GetSize();
+ for (size_t i = 0; i < num_modules; i++) {
+ auto mod = module_list.GetModuleAtIndex(i);
+ if (IsRenderScriptModule(mod)) {
+ LoadModule(mod);
+ }
+ }
+}
+
+// PluginInterface protocol
+lldb_private::ConstString RenderScriptRuntime::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t RenderScriptRuntime::GetPluginVersion() { return 1; }
+
+bool RenderScriptRuntime::GetDynamicTypeAndAddress(
+ ValueObject &in_value, lldb::DynamicValueType use_dynamic,
+ TypeAndOrName &class_type_or_name, Address &address,
+ Value::ValueType &value_type) {
+ return false;
+}
+
+TypeAndOrName
+RenderScriptRuntime::FixUpDynamicType(const TypeAndOrName &type_and_or_name,
+ ValueObject &static_value) {
+ return type_and_or_name;
+}
+
+bool RenderScriptRuntime::CouldHaveDynamicValue(ValueObject &in_value) {
+ return false;
+}
+
+lldb::BreakpointResolverSP
+RenderScriptRuntime::CreateExceptionResolver(Breakpoint *bp, bool catch_bp,
+ bool throw_bp) {
+ BreakpointResolverSP resolver_sp;
+ return resolver_sp;
+}
+
+const RenderScriptRuntime::HookDefn RenderScriptRuntime::s_runtimeHookDefns[] =
+ {
+ // rsdScript
+ {"rsdScriptInit", "_Z13rsdScriptInitPKN7android12renderscript7ContextEP"
+ "NS0_7ScriptCEPKcS7_PKhjj",
+ "_Z13rsdScriptInitPKN7android12renderscript7ContextEPNS0_"
+ "7ScriptCEPKcS7_PKhmj",
+ 0, RenderScriptRuntime::eModuleKindDriver,
+ &lldb_private::RenderScriptRuntime::CaptureScriptInit},
+ {"rsdScriptInvokeForEachMulti",
+ "_Z27rsdScriptInvokeForEachMultiPKN7android12renderscript7ContextEPNS0"
+ "_6ScriptEjPPKNS0_10AllocationEjPS6_PKvjPK12RsScriptCall",
+ "_Z27rsdScriptInvokeForEachMultiPKN7android12renderscript7ContextEPNS0"
+ "_6ScriptEjPPKNS0_10AllocationEmPS6_PKvmPK12RsScriptCall",
+ 0, RenderScriptRuntime::eModuleKindDriver,
+ &lldb_private::RenderScriptRuntime::CaptureScriptInvokeForEachMulti},
+ {"rsdScriptSetGlobalVar", "_Z21rsdScriptSetGlobalVarPKN7android12render"
+ "script7ContextEPKNS0_6ScriptEjPvj",
+ "_Z21rsdScriptSetGlobalVarPKN7android12renderscript7ContextEPKNS0_"
+ "6ScriptEjPvm",
+ 0, RenderScriptRuntime::eModuleKindDriver,
+ &lldb_private::RenderScriptRuntime::CaptureSetGlobalVar},
+
+ // rsdAllocation
+ {"rsdAllocationInit", "_Z17rsdAllocationInitPKN7android12renderscript7C"
+ "ontextEPNS0_10AllocationEb",
+ "_Z17rsdAllocationInitPKN7android12renderscript7ContextEPNS0_"
+ "10AllocationEb",
+ 0, RenderScriptRuntime::eModuleKindDriver,
+ &lldb_private::RenderScriptRuntime::CaptureAllocationInit},
+ {"rsdAllocationRead2D",
+ "_Z19rsdAllocationRead2DPKN7android12renderscript7ContextEPKNS0_"
+ "10AllocationEjjj23RsAllocationCubemapFacejjPvjj",
+ "_Z19rsdAllocationRead2DPKN7android12renderscript7ContextEPKNS0_"
+ "10AllocationEjjj23RsAllocationCubemapFacejjPvmm",
+ 0, RenderScriptRuntime::eModuleKindDriver, nullptr},
+ {"rsdAllocationDestroy", "_Z20rsdAllocationDestroyPKN7android12rendersc"
+ "ript7ContextEPNS0_10AllocationE",
+ "_Z20rsdAllocationDestroyPKN7android12renderscript7ContextEPNS0_"
+ "10AllocationE",
+ 0, RenderScriptRuntime::eModuleKindDriver,
+ &lldb_private::RenderScriptRuntime::CaptureAllocationDestroy},
+
+ // renderscript script groups
+ {"rsdDebugHintScriptGroup2", "_ZN7android12renderscript21debugHintScrip"
+ "tGroup2EPKcjPKPFvPK24RsExpandKernelDriver"
+ "InfojjjEj",
+ "_ZN7android12renderscript21debugHintScriptGroup2EPKcjPKPFvPK24RsExpan"
+ "dKernelDriverInfojjjEj",
+ 0, RenderScriptRuntime::eModuleKindImpl,
+ &lldb_private::RenderScriptRuntime::CaptureDebugHintScriptGroup2}};
+
+const size_t RenderScriptRuntime::s_runtimeHookCount =
+ sizeof(s_runtimeHookDefns) / sizeof(s_runtimeHookDefns[0]);
+
+bool RenderScriptRuntime::HookCallback(void *baton,
+ StoppointCallbackContext *ctx,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id) {
+ RuntimeHook *hook = (RuntimeHook *)baton;
+ ExecutionContext exe_ctx(ctx->exe_ctx_ref);
+
+ RenderScriptRuntime *lang_rt = llvm::cast<RenderScriptRuntime>(
+ exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+
+ lang_rt->HookCallback(hook, exe_ctx);
+
+ return false;
+}
+
+void RenderScriptRuntime::HookCallback(RuntimeHook *hook,
+ ExecutionContext &exe_ctx) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ if (log)
+ log->Printf("%s - '%s'", __FUNCTION__, hook->defn->name);
+
+ if (hook->defn->grabber) {
+ (this->*(hook->defn->grabber))(hook, exe_ctx);
+ }
+}
+
+void RenderScriptRuntime::CaptureDebugHintScriptGroup2(
+ RuntimeHook *hook_info, ExecutionContext &context) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ enum {
+ eGroupName = 0,
+ eGroupNameSize,
+ eKernel,
+ eKernelCount,
+ };
+
+ std::array<ArgItem, 4> args{{
+ {ArgItem::ePointer, 0}, // const char *groupName
+ {ArgItem::eInt32, 0}, // const uint32_t groupNameSize
+ {ArgItem::ePointer, 0}, // const ExpandFuncTy *kernel
+ {ArgItem::eInt32, 0}, // const uint32_t kernelCount
+ }};
+
+ if (!GetArgs(context, args.data(), args.size())) {
+ if (log)
+ log->Printf("%s - Error while reading the function parameters",
+ __FUNCTION__);
+ return;
+ } else if (log) {
+ log->Printf("%s - groupName : 0x%" PRIx64, __FUNCTION__,
+ addr_t(args[eGroupName]));
+ log->Printf("%s - groupNameSize: %" PRIu64, __FUNCTION__,
+ uint64_t(args[eGroupNameSize]));
+ log->Printf("%s - kernel : 0x%" PRIx64, __FUNCTION__,
+ addr_t(args[eKernel]));
+ log->Printf("%s - kernelCount : %" PRIu64, __FUNCTION__,
+ uint64_t(args[eKernelCount]));
+ }
+
+ // parse script group name
+ ConstString group_name;
+ {
+ Status err;
+ const uint64_t len = uint64_t(args[eGroupNameSize]);
+ std::unique_ptr<char[]> buffer(new char[uint32_t(len + 1)]);
+ m_process->ReadMemory(addr_t(args[eGroupName]), buffer.get(), len, err);
+ buffer.get()[len] = '\0';
+ if (!err.Success()) {
+ if (log)
+ log->Printf("Error reading scriptgroup name from target");
+ return;
+ } else {
+ if (log)
+ log->Printf("Extracted scriptgroup name %s", buffer.get());
+ }
+ // write back the script group name
+ group_name.SetCString(buffer.get());
+ }
+
+ // create or access existing script group
+ RSScriptGroupDescriptorSP group;
+ {
+ // search for existing script group
+ for (auto sg : m_scriptGroups) {
+ if (sg->m_name == group_name) {
+ group = sg;
+ break;
+ }
+ }
+ if (!group) {
+ group = std::make_shared<RSScriptGroupDescriptor>();
+ group->m_name = group_name;
+ m_scriptGroups.push_back(group);
+ } else {
+ // already have this script group
+ if (log)
+ log->Printf("Attempt to add duplicate script group %s",
+ group_name.AsCString());
+ return;
+ }
+ }
+ assert(group);
+
+ const uint32_t target_ptr_size = m_process->GetAddressByteSize();
+ std::vector<addr_t> kernels;
+ // parse kernel addresses in script group
+ for (uint64_t i = 0; i < uint64_t(args[eKernelCount]); ++i) {
+ RSScriptGroupDescriptor::Kernel kernel;
+ // extract script group kernel addresses from the target
+ const addr_t ptr_addr = addr_t(args[eKernel]) + i * target_ptr_size;
+ uint64_t kernel_addr = 0;
+ Status err;
+ size_t read =
+ m_process->ReadMemory(ptr_addr, &kernel_addr, target_ptr_size, err);
+ if (!err.Success() || read != target_ptr_size) {
+ if (log)
+ log->Printf("Error parsing kernel address %" PRIu64 " in script group",
+ i);
+ return;
+ }
+ if (log)
+ log->Printf("Extracted scriptgroup kernel address - 0x%" PRIx64,
+ kernel_addr);
+ kernel.m_addr = kernel_addr;
+
+ // try to resolve the associated kernel name
+ if (!ResolveKernelName(kernel.m_addr, kernel.m_name)) {
+ if (log)
+ log->Printf("Parsed scriptgroup kernel %" PRIu64 " - 0x%" PRIx64, i,
+ kernel_addr);
+ return;
+ }
+
+ // try to find the non '.expand' function
+ {
+ const llvm::StringRef expand(".expand");
+ const llvm::StringRef name_ref = kernel.m_name.GetStringRef();
+ if (name_ref.endswith(expand)) {
+ const ConstString base_kernel(name_ref.drop_back(expand.size()));
+ // verify this function is a valid kernel
+ if (IsKnownKernel(base_kernel)) {
+ kernel.m_name = base_kernel;
+ if (log)
+ log->Printf("%s - found non expand version '%s'", __FUNCTION__,
+ base_kernel.GetCString());
+ }
+ }
+ }
+ // add to a list of script group kernels we know about
+ group->m_kernels.push_back(kernel);
+ }
+
+ // Resolve any pending scriptgroup breakpoints
+ {
+ Target &target = m_process->GetTarget();
+ const BreakpointList &list = target.GetBreakpointList();
+ const size_t num_breakpoints = list.GetSize();
+ if (log)
+ log->Printf("Resolving %zu breakpoints", num_breakpoints);
+ for (size_t i = 0; i < num_breakpoints; ++i) {
+ const BreakpointSP bp = list.GetBreakpointAtIndex(i);
+ if (bp) {
+ if (bp->MatchesName(group_name.AsCString())) {
+ if (log)
+ log->Printf("Found breakpoint with name %s",
+ group_name.AsCString());
+ bp->ResolveBreakpoint();
+ }
+ }
+ }
+ }
+}
+
+void RenderScriptRuntime::CaptureScriptInvokeForEachMulti(
+ RuntimeHook *hook, ExecutionContext &exe_ctx) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ enum {
+ eRsContext = 0,
+ eRsScript,
+ eRsSlot,
+ eRsAIns,
+ eRsInLen,
+ eRsAOut,
+ eRsUsr,
+ eRsUsrLen,
+ eRsSc,
+ };
+
+ std::array<ArgItem, 9> args{{
+ ArgItem{ArgItem::ePointer, 0}, // const Context *rsc
+ ArgItem{ArgItem::ePointer, 0}, // Script *s
+ ArgItem{ArgItem::eInt32, 0}, // uint32_t slot
+ ArgItem{ArgItem::ePointer, 0}, // const Allocation **aIns
+ ArgItem{ArgItem::eInt32, 0}, // size_t inLen
+ ArgItem{ArgItem::ePointer, 0}, // Allocation *aout
+ ArgItem{ArgItem::ePointer, 0}, // const void *usr
+ ArgItem{ArgItem::eInt32, 0}, // size_t usrLen
+ ArgItem{ArgItem::ePointer, 0}, // const RsScriptCall *sc
+ }};
+
+ bool success = GetArgs(exe_ctx, &args[0], args.size());
+ if (!success) {
+ if (log)
+ log->Printf("%s - Error while reading the function parameters",
+ __FUNCTION__);
+ return;
+ }
+
+ const uint32_t target_ptr_size = m_process->GetAddressByteSize();
+ Status err;
+ std::vector<uint64_t> allocs;
+
+ // traverse allocation list
+ for (uint64_t i = 0; i < uint64_t(args[eRsInLen]); ++i) {
+ // calculate offest to allocation pointer
+ const addr_t addr = addr_t(args[eRsAIns]) + i * target_ptr_size;
+
+ // Note: due to little endian layout, reading 32bits or 64bits into res
+ // will give the correct results.
+ uint64_t result = 0;
+ size_t read = m_process->ReadMemory(addr, &result, target_ptr_size, err);
+ if (read != target_ptr_size || !err.Success()) {
+ if (log)
+ log->Printf(
+ "%s - Error while reading allocation list argument %" PRIu64,
+ __FUNCTION__, i);
+ } else {
+ allocs.push_back(result);
+ }
+ }
+
+ // if there is an output allocation track it
+ if (uint64_t alloc_out = uint64_t(args[eRsAOut])) {
+ allocs.push_back(alloc_out);
+ }
+
+ // for all allocations we have found
+ for (const uint64_t alloc_addr : allocs) {
+ AllocationDetails *alloc = LookUpAllocation(alloc_addr);
+ if (!alloc)
+ alloc = CreateAllocation(alloc_addr);
+
+ if (alloc) {
+ // save the allocation address
+ if (alloc->address.isValid()) {
+ // check the allocation address we already have matches
+ assert(*alloc->address.get() == alloc_addr);
+ } else {
+ alloc->address = alloc_addr;
+ }
+
+ // save the context
+ if (log) {
+ if (alloc->context.isValid() &&
+ *alloc->context.get() != addr_t(args[eRsContext]))
+ log->Printf("%s - Allocation used by multiple contexts",
+ __FUNCTION__);
+ }
+ alloc->context = addr_t(args[eRsContext]);
+ }
+ }
+
+ // make sure we track this script object
+ if (lldb_private::RenderScriptRuntime::ScriptDetails *script =
+ LookUpScript(addr_t(args[eRsScript]), true)) {
+ if (log) {
+ if (script->context.isValid() &&
+ *script->context.get() != addr_t(args[eRsContext]))
+ log->Printf("%s - Script used by multiple contexts", __FUNCTION__);
+ }
+ script->context = addr_t(args[eRsContext]);
+ }
+}
+
+void RenderScriptRuntime::CaptureSetGlobalVar(RuntimeHook *hook,
+ ExecutionContext &context) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ enum {
+ eRsContext,
+ eRsScript,
+ eRsId,
+ eRsData,
+ eRsLength,
+ };
+
+ std::array<ArgItem, 5> args{{
+ ArgItem{ArgItem::ePointer, 0}, // eRsContext
+ ArgItem{ArgItem::ePointer, 0}, // eRsScript
+ ArgItem{ArgItem::eInt32, 0}, // eRsId
+ ArgItem{ArgItem::ePointer, 0}, // eRsData
+ ArgItem{ArgItem::eInt32, 0}, // eRsLength
+ }};
+
+ bool success = GetArgs(context, &args[0], args.size());
+ if (!success) {
+ if (log)
+ log->Printf("%s - error reading the function parameters.", __FUNCTION__);
+ return;
+ }
+
+ if (log) {
+ log->Printf("%s - 0x%" PRIx64 ",0x%" PRIx64 " slot %" PRIu64 " = 0x%" PRIx64
+ ":%" PRIu64 "bytes.",
+ __FUNCTION__, uint64_t(args[eRsContext]),
+ uint64_t(args[eRsScript]), uint64_t(args[eRsId]),
+ uint64_t(args[eRsData]), uint64_t(args[eRsLength]));
+
+ addr_t script_addr = addr_t(args[eRsScript]);
+ if (m_scriptMappings.find(script_addr) != m_scriptMappings.end()) {
+ auto rsm = m_scriptMappings[script_addr];
+ if (uint64_t(args[eRsId]) < rsm->m_globals.size()) {
+ auto rsg = rsm->m_globals[uint64_t(args[eRsId])];
+ log->Printf("%s - Setting of '%s' within '%s' inferred", __FUNCTION__,
+ rsg.m_name.AsCString(),
+ rsm->m_module->GetFileSpec().GetFilename().AsCString());
+ }
+ }
+ }
+}
+
+void RenderScriptRuntime::CaptureAllocationInit(RuntimeHook *hook,
+ ExecutionContext &exe_ctx) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ enum { eRsContext, eRsAlloc, eRsForceZero };
+
+ std::array<ArgItem, 3> args{{
+ ArgItem{ArgItem::ePointer, 0}, // eRsContext
+ ArgItem{ArgItem::ePointer, 0}, // eRsAlloc
+ ArgItem{ArgItem::eBool, 0}, // eRsForceZero
+ }};
+
+ bool success = GetArgs(exe_ctx, &args[0], args.size());
+ if (!success) {
+ if (log)
+ log->Printf("%s - error while reading the function parameters",
+ __FUNCTION__);
+ return;
+ }
+
+ if (log)
+ log->Printf("%s - 0x%" PRIx64 ",0x%" PRIx64 ",0x%" PRIx64 " .",
+ __FUNCTION__, uint64_t(args[eRsContext]),
+ uint64_t(args[eRsAlloc]), uint64_t(args[eRsForceZero]));
+
+ AllocationDetails *alloc = CreateAllocation(uint64_t(args[eRsAlloc]));
+ if (alloc)
+ alloc->context = uint64_t(args[eRsContext]);
+}
+
+void RenderScriptRuntime::CaptureAllocationDestroy(RuntimeHook *hook,
+ ExecutionContext &exe_ctx) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ enum {
+ eRsContext,
+ eRsAlloc,
+ };
+
+ std::array<ArgItem, 2> args{{
+ ArgItem{ArgItem::ePointer, 0}, // eRsContext
+ ArgItem{ArgItem::ePointer, 0}, // eRsAlloc
+ }};
+
+ bool success = GetArgs(exe_ctx, &args[0], args.size());
+ if (!success) {
+ if (log)
+ log->Printf("%s - error while reading the function parameters.",
+ __FUNCTION__);
+ return;
+ }
+
+ if (log)
+ log->Printf("%s - 0x%" PRIx64 ", 0x%" PRIx64 ".", __FUNCTION__,
+ uint64_t(args[eRsContext]), uint64_t(args[eRsAlloc]));
+
+ for (auto iter = m_allocations.begin(); iter != m_allocations.end(); ++iter) {
+ auto &allocation_up = *iter; // get the unique pointer
+ if (allocation_up->address.isValid() &&
+ *allocation_up->address.get() == addr_t(args[eRsAlloc])) {
+ m_allocations.erase(iter);
+ if (log)
+ log->Printf("%s - deleted allocation entry.", __FUNCTION__);
+ return;
+ }
+ }
+
+ if (log)
+ log->Printf("%s - couldn't find destroyed allocation.", __FUNCTION__);
+}
+
+void RenderScriptRuntime::CaptureScriptInit(RuntimeHook *hook,
+ ExecutionContext &exe_ctx) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ Status err;
+ Process *process = exe_ctx.GetProcessPtr();
+
+ enum { eRsContext, eRsScript, eRsResNamePtr, eRsCachedDirPtr };
+
+ std::array<ArgItem, 4> args{
+ {ArgItem{ArgItem::ePointer, 0}, ArgItem{ArgItem::ePointer, 0},
+ ArgItem{ArgItem::ePointer, 0}, ArgItem{ArgItem::ePointer, 0}}};
+ bool success = GetArgs(exe_ctx, &args[0], args.size());
+ if (!success) {
+ if (log)
+ log->Printf("%s - error while reading the function parameters.",
+ __FUNCTION__);
+ return;
+ }
+
+ std::string res_name;
+ process->ReadCStringFromMemory(addr_t(args[eRsResNamePtr]), res_name, err);
+ if (err.Fail()) {
+ if (log)
+ log->Printf("%s - error reading res_name: %s.", __FUNCTION__,
+ err.AsCString());
+ }
+
+ std::string cache_dir;
+ process->ReadCStringFromMemory(addr_t(args[eRsCachedDirPtr]), cache_dir, err);
+ if (err.Fail()) {
+ if (log)
+ log->Printf("%s - error reading cache_dir: %s.", __FUNCTION__,
+ err.AsCString());
+ }
+
+ if (log)
+ log->Printf("%s - 0x%" PRIx64 ",0x%" PRIx64 " => '%s' at '%s' .",
+ __FUNCTION__, uint64_t(args[eRsContext]),
+ uint64_t(args[eRsScript]), res_name.c_str(), cache_dir.c_str());
+
+ if (res_name.size() > 0) {
+ StreamString strm;
+ strm.Printf("librs.%s.so", res_name.c_str());
+
+ ScriptDetails *script = LookUpScript(addr_t(args[eRsScript]), true);
+ if (script) {
+ script->type = ScriptDetails::eScriptC;
+ script->cache_dir = cache_dir;
+ script->res_name = res_name;
+ script->shared_lib = strm.GetString();
+ script->context = addr_t(args[eRsContext]);
+ }
+
+ if (log)
+ log->Printf("%s - '%s' tagged with context 0x%" PRIx64
+ " and script 0x%" PRIx64 ".",
+ __FUNCTION__, strm.GetData(), uint64_t(args[eRsContext]),
+ uint64_t(args[eRsScript]));
+ } else if (log) {
+ log->Printf("%s - resource name invalid, Script not tagged.", __FUNCTION__);
+ }
+}
+
+void RenderScriptRuntime::LoadRuntimeHooks(lldb::ModuleSP module,
+ ModuleKind kind) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ if (!module) {
+ return;
+ }
+
+ Target &target = GetProcess()->GetTarget();
+ const llvm::Triple::ArchType machine = target.GetArchitecture().GetMachine();
+
+ if (machine != llvm::Triple::ArchType::x86 &&
+ machine != llvm::Triple::ArchType::arm &&
+ machine != llvm::Triple::ArchType::aarch64 &&
+ machine != llvm::Triple::ArchType::mipsel &&
+ machine != llvm::Triple::ArchType::mips64el &&
+ machine != llvm::Triple::ArchType::x86_64) {
+ if (log)
+ log->Printf("%s - unable to hook runtime functions.", __FUNCTION__);
+ return;
+ }
+
+ const uint32_t target_ptr_size =
+ target.GetArchitecture().GetAddressByteSize();
+
+ std::array<bool, s_runtimeHookCount> hook_placed;
+ hook_placed.fill(false);
+
+ for (size_t idx = 0; idx < s_runtimeHookCount; idx++) {
+ const HookDefn *hook_defn = &s_runtimeHookDefns[idx];
+ if (hook_defn->kind != kind) {
+ continue;
+ }
+
+ const char *symbol_name = (target_ptr_size == 4)
+ ? hook_defn->symbol_name_m32
+ : hook_defn->symbol_name_m64;
+
+ const Symbol *sym = module->FindFirstSymbolWithNameAndType(
+ ConstString(symbol_name), eSymbolTypeCode);
+ if (!sym) {
+ if (log) {
+ log->Printf("%s - symbol '%s' related to the function %s not found",
+ __FUNCTION__, symbol_name, hook_defn->name);
+ }
+ continue;
+ }
+
+ addr_t addr = sym->GetLoadAddress(&target);
+ if (addr == LLDB_INVALID_ADDRESS) {
+ if (log)
+ log->Printf("%s - unable to resolve the address of hook function '%s' "
+ "with symbol '%s'.",
+ __FUNCTION__, hook_defn->name, symbol_name);
+ continue;
+ } else {
+ if (log)
+ log->Printf("%s - function %s, address resolved at 0x%" PRIx64,
+ __FUNCTION__, hook_defn->name, addr);
+ }
+
+ RuntimeHookSP hook(new RuntimeHook());
+ hook->address = addr;
+ hook->defn = hook_defn;
+ hook->bp_sp = target.CreateBreakpoint(addr, true, false);
+ hook->bp_sp->SetCallback(HookCallback, hook.get(), true);
+ m_runtimeHooks[addr] = hook;
+ if (log) {
+ log->Printf("%s - successfully hooked '%s' in '%s' version %" PRIu64
+ " at 0x%" PRIx64 ".",
+ __FUNCTION__, hook_defn->name,
+ module->GetFileSpec().GetFilename().AsCString(),
+ (uint64_t)hook_defn->version, (uint64_t)addr);
+ }
+ hook_placed[idx] = true;
+ }
+
+ // log any unhooked function
+ if (log) {
+ for (size_t i = 0; i < hook_placed.size(); ++i) {
+ if (hook_placed[i])
+ continue;
+ const HookDefn &hook_defn = s_runtimeHookDefns[i];
+ if (hook_defn.kind != kind)
+ continue;
+ log->Printf("%s - function %s was not hooked", __FUNCTION__,
+ hook_defn.name);
+ }
+ }
+}
+
+void RenderScriptRuntime::FixupScriptDetails(RSModuleDescriptorSP rsmodule_sp) {
+ if (!rsmodule_sp)
+ return;
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ const ModuleSP module = rsmodule_sp->m_module;
+ const FileSpec &file = module->GetPlatformFileSpec();
+
+ // Iterate over all of the scripts that we currently know of. Note: We cant
+ // push or pop to m_scripts here or it may invalidate rs_script.
+ for (const auto &rs_script : m_scripts) {
+ // Extract the expected .so file path for this script.
+ std::string shared_lib;
+ if (!rs_script->shared_lib.get(shared_lib))
+ continue;
+
+ // Only proceed if the module that has loaded corresponds to this script.
+ if (file.GetFilename() != ConstString(shared_lib.c_str()))
+ continue;
+
+ // Obtain the script address which we use as a key.
+ lldb::addr_t script;
+ if (!rs_script->script.get(script))
+ continue;
+
+ // If we have a script mapping for the current script.
+ if (m_scriptMappings.find(script) != m_scriptMappings.end()) {
+ // if the module we have stored is different to the one we just received.
+ if (m_scriptMappings[script] != rsmodule_sp) {
+ if (log)
+ log->Printf(
+ "%s - script %" PRIx64 " wants reassigned to new rsmodule '%s'.",
+ __FUNCTION__, (uint64_t)script,
+ rsmodule_sp->m_module->GetFileSpec().GetFilename().AsCString());
+ }
+ }
+ // We don't have a script mapping for the current script.
+ else {
+ // Obtain the script resource name.
+ std::string res_name;
+ if (rs_script->res_name.get(res_name))
+ // Set the modules resource name.
+ rsmodule_sp->m_resname = res_name;
+ // Add Script/Module pair to map.
+ m_scriptMappings[script] = rsmodule_sp;
+ if (log)
+ log->Printf(
+ "%s - script %" PRIx64 " associated with rsmodule '%s'.",
+ __FUNCTION__, (uint64_t)script,
+ rsmodule_sp->m_module->GetFileSpec().GetFilename().AsCString());
+ }
+ }
+}
+
+// Uses the Target API to evaluate the expression passed as a parameter to the
+// function The result of that expression is returned an unsigned 64 bit int,
+// via the result* parameter. Function returns true on success, and false on
+// failure
+bool RenderScriptRuntime::EvalRSExpression(const char *expr,
+ StackFrame *frame_ptr,
+ uint64_t *result) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+ if (log)
+ log->Printf("%s(%s)", __FUNCTION__, expr);
+
+ ValueObjectSP expr_result;
+ EvaluateExpressionOptions options;
+ options.SetLanguage(lldb::eLanguageTypeC_plus_plus);
+ // Perform the actual expression evaluation
+ auto &target = GetProcess()->GetTarget();
+ target.EvaluateExpression(expr, frame_ptr, expr_result, options);
+
+ if (!expr_result) {
+ if (log)
+ log->Printf("%s: couldn't evaluate expression.", __FUNCTION__);
+ return false;
+ }
+
+ // The result of the expression is invalid
+ if (!expr_result->GetError().Success()) {
+ Status err = expr_result->GetError();
+ // Expression returned is void, so this is actually a success
+ if (err.GetError() == UserExpression::kNoResult) {
+ if (log)
+ log->Printf("%s - expression returned void.", __FUNCTION__);
+
+ result = nullptr;
+ return true;
+ }
+
+ if (log)
+ log->Printf("%s - error evaluating expression result: %s", __FUNCTION__,
+ err.AsCString());
+ return false;
+ }
+
+ bool success = false;
+ // We only read the result as an uint32_t.
+ *result = expr_result->GetValueAsUnsigned(0, &success);
+
+ if (!success) {
+ if (log)
+ log->Printf("%s - couldn't convert expression result to uint32_t",
+ __FUNCTION__);
+ return false;
+ }
+
+ return true;
+}
+
+namespace {
+// Used to index expression format strings
+enum ExpressionStrings {
+ eExprGetOffsetPtr = 0,
+ eExprAllocGetType,
+ eExprTypeDimX,
+ eExprTypeDimY,
+ eExprTypeDimZ,
+ eExprTypeElemPtr,
+ eExprElementType,
+ eExprElementKind,
+ eExprElementVec,
+ eExprElementFieldCount,
+ eExprSubelementsId,
+ eExprSubelementsName,
+ eExprSubelementsArrSize,
+
+ _eExprLast // keep at the end, implicit size of the array runtime_expressions
+};
+
+// max length of an expanded expression
+const int jit_max_expr_size = 512;
+
+// Retrieve the string to JIT for the given expression
+#define JIT_TEMPLATE_CONTEXT "void* ctxt = (void*)rsDebugGetContextWrapper(0x%" PRIx64 "); "
+const char *JITTemplate(ExpressionStrings e) {
+ // Format strings containing the expressions we may need to evaluate.
+ static std::array<const char *, _eExprLast> runtime_expressions = {
+ {// Mangled GetOffsetPointer(Allocation*, xoff, yoff, zoff, lod, cubemap)
+ "(int*)_"
+ "Z12GetOffsetPtrPKN7android12renderscript10AllocationEjjjj23RsAllocation"
+ "CubemapFace"
+ "(0x%" PRIx64 ", %" PRIu32 ", %" PRIu32 ", %" PRIu32 ", 0, 0)", // eExprGetOffsetPtr
+
+ // Type* rsaAllocationGetType(Context*, Allocation*)
+ JIT_TEMPLATE_CONTEXT "(void*)rsaAllocationGetType(ctxt, 0x%" PRIx64 ")", // eExprAllocGetType
+
+ // rsaTypeGetNativeData(Context*, Type*, void* typeData, size) Pack the
+ // data in the following way mHal.state.dimX; mHal.state.dimY;
+ // mHal.state.dimZ; mHal.state.lodCount; mHal.state.faces; mElement;
+ // into typeData Need to specify 32 or 64 bit for uint_t since this
+ // differs between devices
+ JIT_TEMPLATE_CONTEXT
+ "uint%" PRIu32 "_t data[6]; (void*)rsaTypeGetNativeData(ctxt"
+ ", 0x%" PRIx64 ", data, 6); data[0]", // eExprTypeDimX
+ JIT_TEMPLATE_CONTEXT
+ "uint%" PRIu32 "_t data[6]; (void*)rsaTypeGetNativeData(ctxt"
+ ", 0x%" PRIx64 ", data, 6); data[1]", // eExprTypeDimY
+ JIT_TEMPLATE_CONTEXT
+ "uint%" PRIu32 "_t data[6]; (void*)rsaTypeGetNativeData(ctxt"
+ ", 0x%" PRIx64 ", data, 6); data[2]", // eExprTypeDimZ
+ JIT_TEMPLATE_CONTEXT
+ "uint%" PRIu32 "_t data[6]; (void*)rsaTypeGetNativeData(ctxt"
+ ", 0x%" PRIx64 ", data, 6); data[5]", // eExprTypeElemPtr
+
+ // rsaElementGetNativeData(Context*, Element*, uint32_t* elemData,size)
+ // Pack mType; mKind; mNormalized; mVectorSize; NumSubElements into
+ // elemData
+ JIT_TEMPLATE_CONTEXT
+ "uint32_t data[5]; (void*)rsaElementGetNativeData(ctxt"
+ ", 0x%" PRIx64 ", data, 5); data[0]", // eExprElementType
+ JIT_TEMPLATE_CONTEXT
+ "uint32_t data[5]; (void*)rsaElementGetNativeData(ctxt"
+ ", 0x%" PRIx64 ", data, 5); data[1]", // eExprElementKind
+ JIT_TEMPLATE_CONTEXT
+ "uint32_t data[5]; (void*)rsaElementGetNativeData(ctxt"
+ ", 0x%" PRIx64 ", data, 5); data[3]", // eExprElementVec
+ JIT_TEMPLATE_CONTEXT
+ "uint32_t data[5]; (void*)rsaElementGetNativeData(ctxt"
+ ", 0x%" PRIx64 ", data, 5); data[4]", // eExprElementFieldCount
+
+ // rsaElementGetSubElements(RsContext con, RsElement elem, uintptr_t
+ // *ids, const char **names, size_t *arraySizes, uint32_t dataSize)
+ // Needed for Allocations of structs to gather details about
+ // fields/Subelements Element* of field
+ JIT_TEMPLATE_CONTEXT "void* ids[%" PRIu32 "]; const char* names[%" PRIu32
+ "]; size_t arr_size[%" PRIu32 "];"
+ "(void*)rsaElementGetSubElements(ctxt, 0x%" PRIx64
+ ", ids, names, arr_size, %" PRIu32 "); ids[%" PRIu32 "]", // eExprSubelementsId
+
+ // Name of field
+ JIT_TEMPLATE_CONTEXT "void* ids[%" PRIu32 "]; const char* names[%" PRIu32
+ "]; size_t arr_size[%" PRIu32 "];"
+ "(void*)rsaElementGetSubElements(ctxt, 0x%" PRIx64
+ ", ids, names, arr_size, %" PRIu32 "); names[%" PRIu32 "]", // eExprSubelementsName
+
+ // Array size of field
+ JIT_TEMPLATE_CONTEXT "void* ids[%" PRIu32 "]; const char* names[%" PRIu32
+ "]; size_t arr_size[%" PRIu32 "];"
+ "(void*)rsaElementGetSubElements(ctxt, 0x%" PRIx64
+ ", ids, names, arr_size, %" PRIu32 "); arr_size[%" PRIu32 "]"}}; // eExprSubelementsArrSize
+
+ return runtime_expressions[e];
+}
+} // end of the anonymous namespace
+
+// JITs the RS runtime for the internal data pointer of an allocation. Is
+// passed x,y,z coordinates for the pointer to a specific element. Then sets
+// the data_ptr member in Allocation with the result. Returns true on success,
+// false otherwise
+bool RenderScriptRuntime::JITDataPointer(AllocationDetails *alloc,
+ StackFrame *frame_ptr, uint32_t x,
+ uint32_t y, uint32_t z) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ if (!alloc->address.isValid()) {
+ if (log)
+ log->Printf("%s - failed to find allocation details.", __FUNCTION__);
+ return false;
+ }
+
+ const char *fmt_str = JITTemplate(eExprGetOffsetPtr);
+ char expr_buf[jit_max_expr_size];
+
+ int written = snprintf(expr_buf, jit_max_expr_size, fmt_str,
+ *alloc->address.get(), x, y, z);
+ if (written < 0) {
+ if (log)
+ log->Printf("%s - encoding error in snprintf().", __FUNCTION__);
+ return false;
+ } else if (written >= jit_max_expr_size) {
+ if (log)
+ log->Printf("%s - expression too long.", __FUNCTION__);
+ return false;
+ }
+
+ uint64_t result = 0;
+ if (!EvalRSExpression(expr_buf, frame_ptr, &result))
+ return false;
+
+ addr_t data_ptr = static_cast<lldb::addr_t>(result);
+ alloc->data_ptr = data_ptr;
+
+ return true;
+}
+
+// JITs the RS runtime for the internal pointer to the RS Type of an allocation
+// Then sets the type_ptr member in Allocation with the result. Returns true on
+// success, false otherwise
+bool RenderScriptRuntime::JITTypePointer(AllocationDetails *alloc,
+ StackFrame *frame_ptr) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ if (!alloc->address.isValid() || !alloc->context.isValid()) {
+ if (log)
+ log->Printf("%s - failed to find allocation details.", __FUNCTION__);
+ return false;
+ }
+
+ const char *fmt_str = JITTemplate(eExprAllocGetType);
+ char expr_buf[jit_max_expr_size];
+
+ int written = snprintf(expr_buf, jit_max_expr_size, fmt_str,
+ *alloc->context.get(), *alloc->address.get());
+ if (written < 0) {
+ if (log)
+ log->Printf("%s - encoding error in snprintf().", __FUNCTION__);
+ return false;
+ } else if (written >= jit_max_expr_size) {
+ if (log)
+ log->Printf("%s - expression too long.", __FUNCTION__);
+ return false;
+ }
+
+ uint64_t result = 0;
+ if (!EvalRSExpression(expr_buf, frame_ptr, &result))
+ return false;
+
+ addr_t type_ptr = static_cast<lldb::addr_t>(result);
+ alloc->type_ptr = type_ptr;
+
+ return true;
+}
+
+// JITs the RS runtime for information about the dimensions and type of an
+// allocation Then sets dimension and element_ptr members in Allocation with
+// the result. Returns true on success, false otherwise
+bool RenderScriptRuntime::JITTypePacked(AllocationDetails *alloc,
+ StackFrame *frame_ptr) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ if (!alloc->type_ptr.isValid() || !alloc->context.isValid()) {
+ if (log)
+ log->Printf("%s - Failed to find allocation details.", __FUNCTION__);
+ return false;
+ }
+
+ // Expression is different depending on if device is 32 or 64 bit
+ uint32_t target_ptr_size =
+ GetProcess()->GetTarget().GetArchitecture().GetAddressByteSize();
+ const uint32_t bits = target_ptr_size == 4 ? 32 : 64;
+
+ // We want 4 elements from packed data
+ const uint32_t num_exprs = 4;
+ static_assert(num_exprs == (eExprTypeElemPtr - eExprTypeDimX + 1),
+ "Invalid number of expressions");
+
+ char expr_bufs[num_exprs][jit_max_expr_size];
+ uint64_t results[num_exprs];
+
+ for (uint32_t i = 0; i < num_exprs; ++i) {
+ const char *fmt_str = JITTemplate(ExpressionStrings(eExprTypeDimX + i));
+ int written = snprintf(expr_bufs[i], jit_max_expr_size, fmt_str,
+ *alloc->context.get(), bits, *alloc->type_ptr.get());
+ if (written < 0) {
+ if (log)
+ log->Printf("%s - encoding error in snprintf().", __FUNCTION__);
+ return false;
+ } else if (written >= jit_max_expr_size) {
+ if (log)
+ log->Printf("%s - expression too long.", __FUNCTION__);
+ return false;
+ }
+
+ // Perform expression evaluation
+ if (!EvalRSExpression(expr_bufs[i], frame_ptr, &results[i]))
+ return false;
+ }
+
+ // Assign results to allocation members
+ AllocationDetails::Dimension dims;
+ dims.dim_1 = static_cast<uint32_t>(results[0]);
+ dims.dim_2 = static_cast<uint32_t>(results[1]);
+ dims.dim_3 = static_cast<uint32_t>(results[2]);
+ alloc->dimension = dims;
+
+ addr_t element_ptr = static_cast<lldb::addr_t>(results[3]);
+ alloc->element.element_ptr = element_ptr;
+
+ if (log)
+ log->Printf("%s - dims (%" PRIu32 ", %" PRIu32 ", %" PRIu32
+ ") Element*: 0x%" PRIx64 ".",
+ __FUNCTION__, dims.dim_1, dims.dim_2, dims.dim_3, element_ptr);
+
+ return true;
+}
+
+// JITs the RS runtime for information about the Element of an allocation Then
+// sets type, type_vec_size, field_count and type_kind members in Element with
+// the result. Returns true on success, false otherwise
+bool RenderScriptRuntime::JITElementPacked(Element &elem,
+ const lldb::addr_t context,
+ StackFrame *frame_ptr) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ if (!elem.element_ptr.isValid()) {
+ if (log)
+ log->Printf("%s - failed to find allocation details.", __FUNCTION__);
+ return false;
+ }
+
+ // We want 4 elements from packed data
+ const uint32_t num_exprs = 4;
+ static_assert(num_exprs == (eExprElementFieldCount - eExprElementType + 1),
+ "Invalid number of expressions");
+
+ char expr_bufs[num_exprs][jit_max_expr_size];
+ uint64_t results[num_exprs];
+
+ for (uint32_t i = 0; i < num_exprs; i++) {
+ const char *fmt_str = JITTemplate(ExpressionStrings(eExprElementType + i));
+ int written = snprintf(expr_bufs[i], jit_max_expr_size, fmt_str, context,
+ *elem.element_ptr.get());
+ if (written < 0) {
+ if (log)
+ log->Printf("%s - encoding error in snprintf().", __FUNCTION__);
+ return false;
+ } else if (written >= jit_max_expr_size) {
+ if (log)
+ log->Printf("%s - expression too long.", __FUNCTION__);
+ return false;
+ }
+
+ // Perform expression evaluation
+ if (!EvalRSExpression(expr_bufs[i], frame_ptr, &results[i]))
+ return false;
+ }
+
+ // Assign results to allocation members
+ elem.type = static_cast<RenderScriptRuntime::Element::DataType>(results[0]);
+ elem.type_kind =
+ static_cast<RenderScriptRuntime::Element::DataKind>(results[1]);
+ elem.type_vec_size = static_cast<uint32_t>(results[2]);
+ elem.field_count = static_cast<uint32_t>(results[3]);
+
+ if (log)
+ log->Printf("%s - data type %" PRIu32 ", pixel type %" PRIu32
+ ", vector size %" PRIu32 ", field count %" PRIu32,
+ __FUNCTION__, *elem.type.get(), *elem.type_kind.get(),
+ *elem.type_vec_size.get(), *elem.field_count.get());
+
+ // If this Element has subelements then JIT rsaElementGetSubElements() for
+ // details about its fields
+ return !(*elem.field_count.get() > 0 &&
+ !JITSubelements(elem, context, frame_ptr));
+}
+
+// JITs the RS runtime for information about the subelements/fields of a struct
+// allocation This is necessary for infering the struct type so we can pretty
+// print the allocation's contents. Returns true on success, false otherwise
+bool RenderScriptRuntime::JITSubelements(Element &elem,
+ const lldb::addr_t context,
+ StackFrame *frame_ptr) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ if (!elem.element_ptr.isValid() || !elem.field_count.isValid()) {
+ if (log)
+ log->Printf("%s - failed to find allocation details.", __FUNCTION__);
+ return false;
+ }
+
+ const short num_exprs = 3;
+ static_assert(num_exprs == (eExprSubelementsArrSize - eExprSubelementsId + 1),
+ "Invalid number of expressions");
+
+ char expr_buffer[jit_max_expr_size];
+ uint64_t results;
+
+ // Iterate over struct fields.
+ const uint32_t field_count = *elem.field_count.get();
+ for (uint32_t field_index = 0; field_index < field_count; ++field_index) {
+ Element child;
+ for (uint32_t expr_index = 0; expr_index < num_exprs; ++expr_index) {
+ const char *fmt_str =
+ JITTemplate(ExpressionStrings(eExprSubelementsId + expr_index));
+ int written = snprintf(expr_buffer, jit_max_expr_size, fmt_str,
+ context, field_count, field_count, field_count,
+ *elem.element_ptr.get(), field_count, field_index);
+ if (written < 0) {
+ if (log)
+ log->Printf("%s - encoding error in snprintf().", __FUNCTION__);
+ return false;
+ } else if (written >= jit_max_expr_size) {
+ if (log)
+ log->Printf("%s - expression too long.", __FUNCTION__);
+ return false;
+ }
+
+ // Perform expression evaluation
+ if (!EvalRSExpression(expr_buffer, frame_ptr, &results))
+ return false;
+
+ if (log)
+ log->Printf("%s - expr result 0x%" PRIx64 ".", __FUNCTION__, results);
+
+ switch (expr_index) {
+ case 0: // Element* of child
+ child.element_ptr = static_cast<addr_t>(results);
+ break;
+ case 1: // Name of child
+ {
+ lldb::addr_t address = static_cast<addr_t>(results);
+ Status err;
+ std::string name;
+ GetProcess()->ReadCStringFromMemory(address, name, err);
+ if (!err.Fail())
+ child.type_name = ConstString(name);
+ else {
+ if (log)
+ log->Printf("%s - warning: Couldn't read field name.",
+ __FUNCTION__);
+ }
+ break;
+ }
+ case 2: // Array size of child
+ child.array_size = static_cast<uint32_t>(results);
+ break;
+ }
+ }
+
+ // We need to recursively JIT each Element field of the struct since
+ // structs can be nested inside structs.
+ if (!JITElementPacked(child, context, frame_ptr))
+ return false;
+ elem.children.push_back(child);
+ }
+
+ // Try to infer the name of the struct type so we can pretty print the
+ // allocation contents.
+ FindStructTypeName(elem, frame_ptr);
+
+ return true;
+}
+
+// JITs the RS runtime for the address of the last element in the allocation.
+// The `elem_size` parameter represents the size of a single element, including
+// padding. Which is needed as an offset from the last element pointer. Using
+// this offset minus the starting address we can calculate the size of the
+// allocation. Returns true on success, false otherwise
+bool RenderScriptRuntime::JITAllocationSize(AllocationDetails *alloc,
+ StackFrame *frame_ptr) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ if (!alloc->address.isValid() || !alloc->dimension.isValid() ||
+ !alloc->data_ptr.isValid() || !alloc->element.datum_size.isValid()) {
+ if (log)
+ log->Printf("%s - failed to find allocation details.", __FUNCTION__);
+ return false;
+ }
+
+ // Find dimensions
+ uint32_t dim_x = alloc->dimension.get()->dim_1;
+ uint32_t dim_y = alloc->dimension.get()->dim_2;
+ uint32_t dim_z = alloc->dimension.get()->dim_3;
+
+ // Our plan of jitting the last element address doesn't seem to work for
+ // struct Allocations` Instead try to infer the size ourselves without any
+ // inter element padding.
+ if (alloc->element.children.size() > 0) {
+ if (dim_x == 0)
+ dim_x = 1;
+ if (dim_y == 0)
+ dim_y = 1;
+ if (dim_z == 0)
+ dim_z = 1;
+
+ alloc->size = dim_x * dim_y * dim_z * *alloc->element.datum_size.get();
+
+ if (log)
+ log->Printf("%s - inferred size of struct allocation %" PRIu32 ".",
+ __FUNCTION__, *alloc->size.get());
+ return true;
+ }
+
+ const char *fmt_str = JITTemplate(eExprGetOffsetPtr);
+ char expr_buf[jit_max_expr_size];
+
+ // Calculate last element
+ dim_x = dim_x == 0 ? 0 : dim_x - 1;
+ dim_y = dim_y == 0 ? 0 : dim_y - 1;
+ dim_z = dim_z == 0 ? 0 : dim_z - 1;
+
+ int written = snprintf(expr_buf, jit_max_expr_size, fmt_str,
+ *alloc->address.get(), dim_x, dim_y, dim_z);
+ if (written < 0) {
+ if (log)
+ log->Printf("%s - encoding error in snprintf().", __FUNCTION__);
+ return false;
+ } else if (written >= jit_max_expr_size) {
+ if (log)
+ log->Printf("%s - expression too long.", __FUNCTION__);
+ return false;
+ }
+
+ uint64_t result = 0;
+ if (!EvalRSExpression(expr_buf, frame_ptr, &result))
+ return false;
+
+ addr_t mem_ptr = static_cast<lldb::addr_t>(result);
+ // Find pointer to last element and add on size of an element
+ alloc->size = static_cast<uint32_t>(mem_ptr - *alloc->data_ptr.get()) +
+ *alloc->element.datum_size.get();
+
+ return true;
+}
+
+// JITs the RS runtime for information about the stride between rows in the
+// allocation. This is done to detect padding, since allocated memory is
+// 16-byte aligned. Returns true on success, false otherwise
+bool RenderScriptRuntime::JITAllocationStride(AllocationDetails *alloc,
+ StackFrame *frame_ptr) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ if (!alloc->address.isValid() || !alloc->data_ptr.isValid()) {
+ if (log)
+ log->Printf("%s - failed to find allocation details.", __FUNCTION__);
+ return false;
+ }
+
+ const char *fmt_str = JITTemplate(eExprGetOffsetPtr);
+ char expr_buf[jit_max_expr_size];
+
+ int written = snprintf(expr_buf, jit_max_expr_size, fmt_str,
+ *alloc->address.get(), 0, 1, 0);
+ if (written < 0) {
+ if (log)
+ log->Printf("%s - encoding error in snprintf().", __FUNCTION__);
+ return false;
+ } else if (written >= jit_max_expr_size) {
+ if (log)
+ log->Printf("%s - expression too long.", __FUNCTION__);
+ return false;
+ }
+
+ uint64_t result = 0;
+ if (!EvalRSExpression(expr_buf, frame_ptr, &result))
+ return false;
+
+ addr_t mem_ptr = static_cast<lldb::addr_t>(result);
+ alloc->stride = static_cast<uint32_t>(mem_ptr - *alloc->data_ptr.get());
+
+ return true;
+}
+
+// JIT all the current runtime info regarding an allocation
+bool RenderScriptRuntime::RefreshAllocation(AllocationDetails *alloc,
+ StackFrame *frame_ptr) {
+ // GetOffsetPointer()
+ if (!JITDataPointer(alloc, frame_ptr))
+ return false;
+
+ // rsaAllocationGetType()
+ if (!JITTypePointer(alloc, frame_ptr))
+ return false;
+
+ // rsaTypeGetNativeData()
+ if (!JITTypePacked(alloc, frame_ptr))
+ return false;
+
+ // rsaElementGetNativeData()
+ if (!JITElementPacked(alloc->element, *alloc->context.get(), frame_ptr))
+ return false;
+
+ // Sets the datum_size member in Element
+ SetElementSize(alloc->element);
+
+ // Use GetOffsetPointer() to infer size of the allocation
+ return JITAllocationSize(alloc, frame_ptr);
+}
+
+// Function attempts to set the type_name member of the paramaterised Element
+// object. This string should be the name of the struct type the Element
+// represents. We need this string for pretty printing the Element to users.
+void RenderScriptRuntime::FindStructTypeName(Element &elem,
+ StackFrame *frame_ptr) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ if (!elem.type_name.IsEmpty()) // Name already set
+ return;
+ else
+ elem.type_name = Element::GetFallbackStructName(); // Default type name if
+ // we don't succeed
+
+ // Find all the global variables from the script rs modules
+ VariableList var_list;
+ for (auto module_sp : m_rsmodules)
+ module_sp->m_module->FindGlobalVariables(
+ RegularExpression(llvm::StringRef(".")), UINT32_MAX, var_list);
+
+ // Iterate over all the global variables looking for one with a matching type
+ // to the Element. We make the assumption a match exists since there needs to
+ // be a global variable to reflect the struct type back into java host code.
+ for (uint32_t i = 0; i < var_list.GetSize(); ++i) {
+ const VariableSP var_sp(var_list.GetVariableAtIndex(i));
+ if (!var_sp)
+ continue;
+
+ ValueObjectSP valobj_sp = ValueObjectVariable::Create(frame_ptr, var_sp);
+ if (!valobj_sp)
+ continue;
+
+ // Find the number of variable fields.
+ // If it has no fields, or more fields than our Element, then it can't be
+ // the struct we're looking for. Don't check for equality since RS can add
+ // extra struct members for padding.
+ size_t num_children = valobj_sp->GetNumChildren();
+ if (num_children > elem.children.size() || num_children == 0)
+ continue;
+
+ // Iterate over children looking for members with matching field names. If
+ // all the field names match, this is likely the struct we want.
+ // TODO: This could be made more robust by also checking children data
+ // sizes, or array size
+ bool found = true;
+ for (size_t i = 0; i < num_children; ++i) {
+ ValueObjectSP child = valobj_sp->GetChildAtIndex(i, true);
+ if (!child || (child->GetName() != elem.children[i].type_name)) {
+ found = false;
+ break;
+ }
+ }
+
+ // RS can add extra struct members for padding in the format
+ // '#rs_padding_[0-9]+'
+ if (found && num_children < elem.children.size()) {
+ const uint32_t size_diff = elem.children.size() - num_children;
+ if (log)
+ log->Printf("%s - %" PRIu32 " padding struct entries", __FUNCTION__,
+ size_diff);
+
+ for (uint32_t i = 0; i < size_diff; ++i) {
+ ConstString name = elem.children[num_children + i].type_name;
+ if (strcmp(name.AsCString(), "#rs_padding") < 0)
+ found = false;
+ }
+ }
+
+ // We've found a global variable with matching type
+ if (found) {
+ // Dereference since our Element type isn't a pointer.
+ if (valobj_sp->IsPointerType()) {
+ Status err;
+ ValueObjectSP deref_valobj = valobj_sp->Dereference(err);
+ if (!err.Fail())
+ valobj_sp = deref_valobj;
+ }
+
+ // Save name of variable in Element.
+ elem.type_name = valobj_sp->GetTypeName();
+ if (log)
+ log->Printf("%s - element name set to %s", __FUNCTION__,
+ elem.type_name.AsCString());
+
+ return;
+ }
+ }
+}
+
+// Function sets the datum_size member of Element. Representing the size of a
+// single instance including padding. Assumes the relevant allocation
+// information has already been jitted.
+void RenderScriptRuntime::SetElementSize(Element &elem) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+ const Element::DataType type = *elem.type.get();
+ assert(type >= Element::RS_TYPE_NONE && type <= Element::RS_TYPE_FONT &&
+ "Invalid allocation type");
+
+ const uint32_t vec_size = *elem.type_vec_size.get();
+ uint32_t data_size = 0;
+ uint32_t padding = 0;
+
+ // Element is of a struct type, calculate size recursively.
+ if ((type == Element::RS_TYPE_NONE) && (elem.children.size() > 0)) {
+ for (Element &child : elem.children) {
+ SetElementSize(child);
+ const uint32_t array_size =
+ child.array_size.isValid() ? *child.array_size.get() : 1;
+ data_size += *child.datum_size.get() * array_size;
+ }
+ }
+ // These have been packed already
+ else if (type == Element::RS_TYPE_UNSIGNED_5_6_5 ||
+ type == Element::RS_TYPE_UNSIGNED_5_5_5_1 ||
+ type == Element::RS_TYPE_UNSIGNED_4_4_4_4) {
+ data_size = AllocationDetails::RSTypeToFormat[type][eElementSize];
+ } else if (type < Element::RS_TYPE_ELEMENT) {
+ data_size =
+ vec_size * AllocationDetails::RSTypeToFormat[type][eElementSize];
+ if (vec_size == 3)
+ padding = AllocationDetails::RSTypeToFormat[type][eElementSize];
+ } else
+ data_size =
+ GetProcess()->GetTarget().GetArchitecture().GetAddressByteSize();
+
+ elem.padding = padding;
+ elem.datum_size = data_size + padding;
+ if (log)
+ log->Printf("%s - element size set to %" PRIu32, __FUNCTION__,
+ data_size + padding);
+}
+
+// Given an allocation, this function copies the allocation contents from
+// device into a buffer on the heap. Returning a shared pointer to the buffer
+// containing the data.
+std::shared_ptr<uint8_t>
+RenderScriptRuntime::GetAllocationData(AllocationDetails *alloc,
+ StackFrame *frame_ptr) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ // JIT all the allocation details
+ if (alloc->ShouldRefresh()) {
+ if (log)
+ log->Printf("%s - allocation details not calculated yet, jitting info",
+ __FUNCTION__);
+
+ if (!RefreshAllocation(alloc, frame_ptr)) {
+ if (log)
+ log->Printf("%s - couldn't JIT allocation details", __FUNCTION__);
+ return nullptr;
+ }
+ }
+
+ assert(alloc->data_ptr.isValid() && alloc->element.type.isValid() &&
+ alloc->element.type_vec_size.isValid() && alloc->size.isValid() &&
+ "Allocation information not available");
+
+ // Allocate a buffer to copy data into
+ const uint32_t size = *alloc->size.get();
+ std::shared_ptr<uint8_t> buffer(new uint8_t[size]);
+ if (!buffer) {
+ if (log)
+ log->Printf("%s - couldn't allocate a %" PRIu32 " byte buffer",
+ __FUNCTION__, size);
+ return nullptr;
+ }
+
+ // Read the inferior memory
+ Status err;
+ lldb::addr_t data_ptr = *alloc->data_ptr.get();
+ GetProcess()->ReadMemory(data_ptr, buffer.get(), size, err);
+ if (err.Fail()) {
+ if (log)
+ log->Printf("%s - '%s' Couldn't read %" PRIu32
+ " bytes of allocation data from 0x%" PRIx64,
+ __FUNCTION__, err.AsCString(), size, data_ptr);
+ return nullptr;
+ }
+
+ return buffer;
+}
+
+// Function copies data from a binary file into an allocation. There is a
+// header at the start of the file, FileHeader, before the data content itself.
+// Information from this header is used to display warnings to the user about
+// incompatibilities
+bool RenderScriptRuntime::LoadAllocation(Stream &strm, const uint32_t alloc_id,
+ const char *path,
+ StackFrame *frame_ptr) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ // Find allocation with the given id
+ AllocationDetails *alloc = FindAllocByID(strm, alloc_id);
+ if (!alloc)
+ return false;
+
+ if (log)
+ log->Printf("%s - found allocation 0x%" PRIx64, __FUNCTION__,
+ *alloc->address.get());
+
+ // JIT all the allocation details
+ if (alloc->ShouldRefresh()) {
+ if (log)
+ log->Printf("%s - allocation details not calculated yet, jitting info.",
+ __FUNCTION__);
+
+ if (!RefreshAllocation(alloc, frame_ptr)) {
+ if (log)
+ log->Printf("%s - couldn't JIT allocation details", __FUNCTION__);
+ return false;
+ }
+ }
+
+ assert(alloc->data_ptr.isValid() && alloc->element.type.isValid() &&
+ alloc->element.type_vec_size.isValid() && alloc->size.isValid() &&
+ alloc->element.datum_size.isValid() &&
+ "Allocation information not available");
+
+ // Check we can read from file
+ FileSpec file(path);
+ FileSystem::Instance().Resolve(file);
+ if (!FileSystem::Instance().Exists(file)) {
+ strm.Printf("Error: File %s does not exist", path);
+ strm.EOL();
+ return false;
+ }
+
+ if (!FileSystem::Instance().Readable(file)) {
+ strm.Printf("Error: File %s does not have readable permissions", path);
+ strm.EOL();
+ return false;
+ }
+
+ // Read file into data buffer
+ auto data_sp = FileSystem::Instance().CreateDataBuffer(file.GetPath());
+
+ // Cast start of buffer to FileHeader and use pointer to read metadata
+ void *file_buf = data_sp->GetBytes();
+ if (file_buf == nullptr ||
+ data_sp->GetByteSize() < (sizeof(AllocationDetails::FileHeader) +
+ sizeof(AllocationDetails::ElementHeader))) {
+ strm.Printf("Error: File %s does not contain enough data for header", path);
+ strm.EOL();
+ return false;
+ }
+ const AllocationDetails::FileHeader *file_header =
+ static_cast<AllocationDetails::FileHeader *>(file_buf);
+
+ // Check file starts with ascii characters "RSAD"
+ if (memcmp(file_header->ident, "RSAD", 4)) {
+ strm.Printf("Error: File doesn't contain identifier for an RS allocation "
+ "dump. Are you sure this is the correct file?");
+ strm.EOL();
+ return false;
+ }
+
+ // Look at the type of the root element in the header
+ AllocationDetails::ElementHeader root_el_hdr;
+ memcpy(&root_el_hdr, static_cast<uint8_t *>(file_buf) +
+ sizeof(AllocationDetails::FileHeader),
+ sizeof(AllocationDetails::ElementHeader));
+
+ if (log)
+ log->Printf("%s - header type %" PRIu32 ", element size %" PRIu32,
+ __FUNCTION__, root_el_hdr.type, root_el_hdr.element_size);
+
+ // Check if the target allocation and file both have the same number of bytes
+ // for an Element
+ if (*alloc->element.datum_size.get() != root_el_hdr.element_size) {
+ strm.Printf("Warning: Mismatched Element sizes - file %" PRIu32
+ " bytes, allocation %" PRIu32 " bytes",
+ root_el_hdr.element_size, *alloc->element.datum_size.get());
+ strm.EOL();
+ }
+
+ // Check if the target allocation and file both have the same type
+ const uint32_t alloc_type = static_cast<uint32_t>(*alloc->element.type.get());
+ const uint32_t file_type = root_el_hdr.type;
+
+ if (file_type > Element::RS_TYPE_FONT) {
+ strm.Printf("Warning: File has unknown allocation type");
+ strm.EOL();
+ } else if (alloc_type != file_type) {
+ // Enum value isn't monotonous, so doesn't always index RsDataTypeToString
+ // array
+ uint32_t target_type_name_idx = alloc_type;
+ uint32_t head_type_name_idx = file_type;
+ if (alloc_type >= Element::RS_TYPE_ELEMENT &&
+ alloc_type <= Element::RS_TYPE_FONT)
+ target_type_name_idx = static_cast<Element::DataType>(
+ (alloc_type - Element::RS_TYPE_ELEMENT) +
+ Element::RS_TYPE_MATRIX_2X2 + 1);
+
+ if (file_type >= Element::RS_TYPE_ELEMENT &&
+ file_type <= Element::RS_TYPE_FONT)
+ head_type_name_idx = static_cast<Element::DataType>(
+ (file_type - Element::RS_TYPE_ELEMENT) + Element::RS_TYPE_MATRIX_2X2 +
+ 1);
+
+ const char *head_type_name =
+ AllocationDetails::RsDataTypeToString[head_type_name_idx][0];
+ const char *target_type_name =
+ AllocationDetails::RsDataTypeToString[target_type_name_idx][0];
+
+ strm.Printf(
+ "Warning: Mismatched Types - file '%s' type, allocation '%s' type",
+ head_type_name, target_type_name);
+ strm.EOL();
+ }
+
+ // Advance buffer past header
+ file_buf = static_cast<uint8_t *>(file_buf) + file_header->hdr_size;
+
+ // Calculate size of allocation data in file
+ size_t size = data_sp->GetByteSize() - file_header->hdr_size;
+
+ // Check if the target allocation and file both have the same total data
+ // size.
+ const uint32_t alloc_size = *alloc->size.get();
+ if (alloc_size != size) {
+ strm.Printf("Warning: Mismatched allocation sizes - file 0x%" PRIx64
+ " bytes, allocation 0x%" PRIx32 " bytes",
+ (uint64_t)size, alloc_size);
+ strm.EOL();
+ // Set length to copy to minimum
+ size = alloc_size < size ? alloc_size : size;
+ }
+
+ // Copy file data from our buffer into the target allocation.
+ lldb::addr_t alloc_data = *alloc->data_ptr.get();
+ Status err;
+ size_t written = GetProcess()->WriteMemory(alloc_data, file_buf, size, err);
+ if (!err.Success() || written != size) {
+ strm.Printf("Error: Couldn't write data to allocation %s", err.AsCString());
+ strm.EOL();
+ return false;
+ }
+
+ strm.Printf("Contents of file '%s' read into allocation %" PRIu32, path,
+ alloc->id);
+ strm.EOL();
+
+ return true;
+}
+
+// Function takes as parameters a byte buffer, which will eventually be written
+// to file as the element header, an offset into that buffer, and an Element
+// that will be saved into the buffer at the parametrised offset. Return value
+// is the new offset after writing the element into the buffer. Elements are
+// saved to the file as the ElementHeader struct followed by offsets to the
+// structs of all the element's children.
+size_t RenderScriptRuntime::PopulateElementHeaders(
+ const std::shared_ptr<uint8_t> header_buffer, size_t offset,
+ const Element &elem) {
+ // File struct for an element header with all the relevant details copied
+ // from elem. We assume members are valid already.
+ AllocationDetails::ElementHeader elem_header;
+ elem_header.type = *elem.type.get();
+ elem_header.kind = *elem.type_kind.get();
+ elem_header.element_size = *elem.datum_size.get();
+ elem_header.vector_size = *elem.type_vec_size.get();
+ elem_header.array_size =
+ elem.array_size.isValid() ? *elem.array_size.get() : 0;
+ const size_t elem_header_size = sizeof(AllocationDetails::ElementHeader);
+
+ // Copy struct into buffer and advance offset We assume that header_buffer
+ // has been checked for nullptr before this method is called
+ memcpy(header_buffer.get() + offset, &elem_header, elem_header_size);
+ offset += elem_header_size;
+
+ // Starting offset of child ElementHeader struct
+ size_t child_offset =
+ offset + ((elem.children.size() + 1) * sizeof(uint32_t));
+ for (const RenderScriptRuntime::Element &child : elem.children) {
+ // Recursively populate the buffer with the element header structs of
+ // children. Then save the offsets where they were set after the parent
+ // element header.
+ memcpy(header_buffer.get() + offset, &child_offset, sizeof(uint32_t));
+ offset += sizeof(uint32_t);
+
+ child_offset = PopulateElementHeaders(header_buffer, child_offset, child);
+ }
+
+ // Zero indicates no more children
+ memset(header_buffer.get() + offset, 0, sizeof(uint32_t));
+
+ return child_offset;
+}
+
+// Given an Element object this function returns the total size needed in the
+// file header to store the element's details. Taking into account the size of
+// the element header struct, plus the offsets to all the element's children.
+// Function is recursive so that the size of all ancestors is taken into
+// account.
+size_t RenderScriptRuntime::CalculateElementHeaderSize(const Element &elem) {
+ // Offsets to children plus zero terminator
+ size_t size = (elem.children.size() + 1) * sizeof(uint32_t);
+ // Size of header struct with type details
+ size += sizeof(AllocationDetails::ElementHeader);
+
+ // Calculate recursively for all descendants
+ for (const Element &child : elem.children)
+ size += CalculateElementHeaderSize(child);
+
+ return size;
+}
+
+// Function copies allocation contents into a binary file. This file can then
+// be loaded later into a different allocation. There is a header, FileHeader,
+// before the allocation data containing meta-data.
+bool RenderScriptRuntime::SaveAllocation(Stream &strm, const uint32_t alloc_id,
+ const char *path,
+ StackFrame *frame_ptr) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ // Find allocation with the given id
+ AllocationDetails *alloc = FindAllocByID(strm, alloc_id);
+ if (!alloc)
+ return false;
+
+ if (log)
+ log->Printf("%s - found allocation 0x%" PRIx64 ".", __FUNCTION__,
+ *alloc->address.get());
+
+ // JIT all the allocation details
+ if (alloc->ShouldRefresh()) {
+ if (log)
+ log->Printf("%s - allocation details not calculated yet, jitting info.",
+ __FUNCTION__);
+
+ if (!RefreshAllocation(alloc, frame_ptr)) {
+ if (log)
+ log->Printf("%s - couldn't JIT allocation details.", __FUNCTION__);
+ return false;
+ }
+ }
+
+ assert(alloc->data_ptr.isValid() && alloc->element.type.isValid() &&
+ alloc->element.type_vec_size.isValid() &&
+ alloc->element.datum_size.get() &&
+ alloc->element.type_kind.isValid() && alloc->dimension.isValid() &&
+ "Allocation information not available");
+
+ // Check we can create writable file
+ FileSpec file_spec(path);
+ FileSystem::Instance().Resolve(file_spec);
+ File file;
+ FileSystem::Instance().Open(file, file_spec,
+ File::eOpenOptionWrite |
+ File::eOpenOptionCanCreate |
+ File::eOpenOptionTruncate);
+
+ if (!file) {
+ strm.Printf("Error: Failed to open '%s' for writing", path);
+ strm.EOL();
+ return false;
+ }
+
+ // Read allocation into buffer of heap memory
+ const std::shared_ptr<uint8_t> buffer = GetAllocationData(alloc, frame_ptr);
+ if (!buffer) {
+ strm.Printf("Error: Couldn't read allocation data into buffer");
+ strm.EOL();
+ return false;
+ }
+
+ // Create the file header
+ AllocationDetails::FileHeader head;
+ memcpy(head.ident, "RSAD", 4);
+ head.dims[0] = static_cast<uint32_t>(alloc->dimension.get()->dim_1);
+ head.dims[1] = static_cast<uint32_t>(alloc->dimension.get()->dim_2);
+ head.dims[2] = static_cast<uint32_t>(alloc->dimension.get()->dim_3);
+
+ const size_t element_header_size = CalculateElementHeaderSize(alloc->element);
+ assert((sizeof(AllocationDetails::FileHeader) + element_header_size) <
+ UINT16_MAX &&
+ "Element header too large");
+ head.hdr_size = static_cast<uint16_t>(sizeof(AllocationDetails::FileHeader) +
+ element_header_size);
+
+ // Write the file header
+ size_t num_bytes = sizeof(AllocationDetails::FileHeader);
+ if (log)
+ log->Printf("%s - writing File Header, 0x%" PRIx64 " bytes", __FUNCTION__,
+ (uint64_t)num_bytes);
+
+ Status err = file.Write(&head, num_bytes);
+ if (!err.Success()) {
+ strm.Printf("Error: '%s' when writing to file '%s'", err.AsCString(), path);
+ strm.EOL();
+ return false;
+ }
+
+ // Create the headers describing the element type of the allocation.
+ std::shared_ptr<uint8_t> element_header_buffer(
+ new uint8_t[element_header_size]);
+ if (element_header_buffer == nullptr) {
+ strm.Printf("Internal Error: Couldn't allocate %" PRIu64
+ " bytes on the heap",
+ (uint64_t)element_header_size);
+ strm.EOL();
+ return false;
+ }
+
+ PopulateElementHeaders(element_header_buffer, 0, alloc->element);
+
+ // Write headers for allocation element type to file
+ num_bytes = element_header_size;
+ if (log)
+ log->Printf("%s - writing element headers, 0x%" PRIx64 " bytes.",
+ __FUNCTION__, (uint64_t)num_bytes);
+
+ err = file.Write(element_header_buffer.get(), num_bytes);
+ if (!err.Success()) {
+ strm.Printf("Error: '%s' when writing to file '%s'", err.AsCString(), path);
+ strm.EOL();
+ return false;
+ }
+
+ // Write allocation data to file
+ num_bytes = static_cast<size_t>(*alloc->size.get());
+ if (log)
+ log->Printf("%s - writing 0x%" PRIx64 " bytes", __FUNCTION__,
+ (uint64_t)num_bytes);
+
+ err = file.Write(buffer.get(), num_bytes);
+ if (!err.Success()) {
+ strm.Printf("Error: '%s' when writing to file '%s'", err.AsCString(), path);
+ strm.EOL();
+ return false;
+ }
+
+ strm.Printf("Allocation written to file '%s'", path);
+ strm.EOL();
+ return true;
+}
+
+bool RenderScriptRuntime::LoadModule(const lldb::ModuleSP &module_sp) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ if (module_sp) {
+ for (const auto &rs_module : m_rsmodules) {
+ if (rs_module->m_module == module_sp) {
+ // Check if the user has enabled automatically breaking on all RS
+ // kernels.
+ if (m_breakAllKernels)
+ BreakOnModuleKernels(rs_module);
+
+ return false;
+ }
+ }
+ bool module_loaded = false;
+ switch (GetModuleKind(module_sp)) {
+ case eModuleKindKernelObj: {
+ RSModuleDescriptorSP module_desc;
+ module_desc = std::make_shared<RSModuleDescriptor>(module_sp);
+ if (module_desc->ParseRSInfo()) {
+ m_rsmodules.push_back(module_desc);
+ module_desc->WarnIfVersionMismatch(GetProcess()
+ ->GetTarget()
+ .GetDebugger()
+ .GetAsyncOutputStream()
+ .get());
+ module_loaded = true;
+ }
+ if (module_loaded) {
+ FixupScriptDetails(module_desc);
+ }
+ break;
+ }
+ case eModuleKindDriver: {
+ if (!m_libRSDriver) {
+ m_libRSDriver = module_sp;
+ LoadRuntimeHooks(m_libRSDriver, RenderScriptRuntime::eModuleKindDriver);
+ }
+ break;
+ }
+ case eModuleKindImpl: {
+ if (!m_libRSCpuRef) {
+ m_libRSCpuRef = module_sp;
+ LoadRuntimeHooks(m_libRSCpuRef, RenderScriptRuntime::eModuleKindImpl);
+ }
+ break;
+ }
+ case eModuleKindLibRS: {
+ if (!m_libRS) {
+ m_libRS = module_sp;
+ static ConstString gDbgPresentStr("gDebuggerPresent");
+ const Symbol *debug_present = m_libRS->FindFirstSymbolWithNameAndType(
+ gDbgPresentStr, eSymbolTypeData);
+ if (debug_present) {
+ Status err;
+ uint32_t flag = 0x00000001U;
+ Target &target = GetProcess()->GetTarget();
+ addr_t addr = debug_present->GetLoadAddress(&target);
+ GetProcess()->WriteMemory(addr, &flag, sizeof(flag), err);
+ if (err.Success()) {
+ if (log)
+ log->Printf("%s - debugger present flag set on debugee.",
+ __FUNCTION__);
+
+ m_debuggerPresentFlagged = true;
+ } else if (log) {
+ log->Printf("%s - error writing debugger present flags '%s' ",
+ __FUNCTION__, err.AsCString());
+ }
+ } else if (log) {
+ log->Printf(
+ "%s - error writing debugger present flags - symbol not found",
+ __FUNCTION__);
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ if (module_loaded)
+ Update();
+ return module_loaded;
+ }
+ return false;
+}
+
+void RenderScriptRuntime::Update() {
+ if (m_rsmodules.size() > 0) {
+ if (!m_initiated) {
+ Initiate();
+ }
+ }
+}
+
+void RSModuleDescriptor::WarnIfVersionMismatch(lldb_private::Stream *s) const {
+ if (!s)
+ return;
+
+ if (m_slang_version.empty() || m_bcc_version.empty()) {
+ s->PutCString("WARNING: Unknown bcc or slang (llvm-rs-cc) version; debug "
+ "experience may be unreliable");
+ s->EOL();
+ } else if (m_slang_version != m_bcc_version) {
+ s->Printf("WARNING: The debug info emitted by the slang frontend "
+ "(llvm-rs-cc) used to build this module (%s) does not match the "
+ "version of bcc used to generate the debug information (%s). "
+ "This is an unsupported configuration and may result in a poor "
+ "debugging experience; proceed with caution",
+ m_slang_version.c_str(), m_bcc_version.c_str());
+ s->EOL();
+ }
+}
+
+bool RSModuleDescriptor::ParsePragmaCount(llvm::StringRef *lines,
+ size_t n_lines) {
+ // Skip the pragma prototype line
+ ++lines;
+ for (; n_lines--; ++lines) {
+ const auto kv_pair = lines->split(" - ");
+ m_pragmas[kv_pair.first.trim().str()] = kv_pair.second.trim().str();
+ }
+ return true;
+}
+
+bool RSModuleDescriptor::ParseExportReduceCount(llvm::StringRef *lines,
+ size_t n_lines) {
+ // The list of reduction kernels in the `.rs.info` symbol is of the form
+ // "signature - accumulatordatasize - reduction_name - initializer_name -
+ // accumulator_name - combiner_name - outconverter_name - halter_name" Where
+ // a function is not explicitly named by the user, or is not generated by the
+ // compiler, it is named "." so the dash separated list should always be 8
+ // items long
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE);
+ // Skip the exportReduceCount line
+ ++lines;
+ for (; n_lines--; ++lines) {
+ llvm::SmallVector<llvm::StringRef, 8> spec;
+ lines->split(spec, " - ");
+ if (spec.size() != 8) {
+ if (spec.size() < 8) {
+ if (log)
+ log->Error("Error parsing RenderScript reduction spec. wrong number "
+ "of fields");
+ return false;
+ } else if (log)
+ log->Warning("Extraneous members in reduction spec: '%s'",
+ lines->str().c_str());
+ }
+
+ const auto sig_s = spec[0];
+ uint32_t sig;
+ if (sig_s.getAsInteger(10, sig)) {
+ if (log)
+ log->Error("Error parsing Renderscript reduction spec: invalid kernel "
+ "signature: '%s'",
+ sig_s.str().c_str());
+ return false;
+ }
+
+ const auto accum_data_size_s = spec[1];
+ uint32_t accum_data_size;
+ if (accum_data_size_s.getAsInteger(10, accum_data_size)) {
+ if (log)
+ log->Error("Error parsing Renderscript reduction spec: invalid "
+ "accumulator data size %s",
+ accum_data_size_s.str().c_str());
+ return false;
+ }
+
+ if (log)
+ log->Printf("Found RenderScript reduction '%s'", spec[2].str().c_str());
+
+ m_reductions.push_back(RSReductionDescriptor(this, sig, accum_data_size,
+ spec[2], spec[3], spec[4],
+ spec[5], spec[6], spec[7]));
+ }
+ return true;
+}
+
+bool RSModuleDescriptor::ParseVersionInfo(llvm::StringRef *lines,
+ size_t n_lines) {
+ // Skip the versionInfo line
+ ++lines;
+ for (; n_lines--; ++lines) {
+ // We're only interested in bcc and slang versions, and ignore all other
+ // versionInfo lines
+ const auto kv_pair = lines->split(" - ");
+ if (kv_pair.first == "slang")
+ m_slang_version = kv_pair.second.str();
+ else if (kv_pair.first == "bcc")
+ m_bcc_version = kv_pair.second.str();
+ }
+ return true;
+}
+
+bool RSModuleDescriptor::ParseExportForeachCount(llvm::StringRef *lines,
+ size_t n_lines) {
+ // Skip the exportForeachCount line
+ ++lines;
+ for (; n_lines--; ++lines) {
+ uint32_t slot;
+ // `forEach` kernels are listed in the `.rs.info` packet as a "slot - name"
+ // pair per line
+ const auto kv_pair = lines->split(" - ");
+ if (kv_pair.first.getAsInteger(10, slot))
+ return false;
+ m_kernels.push_back(RSKernelDescriptor(this, kv_pair.second, slot));
+ }
+ return true;
+}
+
+bool RSModuleDescriptor::ParseExportVarCount(llvm::StringRef *lines,
+ size_t n_lines) {
+ // Skip the ExportVarCount line
+ ++lines;
+ for (; n_lines--; ++lines)
+ m_globals.push_back(RSGlobalDescriptor(this, *lines));
+ return true;
+}
+
+// The .rs.info symbol in renderscript modules contains a string which needs to
+// be parsed. The string is basic and is parsed on a line by line basis.
+bool RSModuleDescriptor::ParseRSInfo() {
+ assert(m_module);
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+ const Symbol *info_sym = m_module->FindFirstSymbolWithNameAndType(
+ ConstString(".rs.info"), eSymbolTypeData);
+ if (!info_sym)
+ return false;
+
+ const addr_t addr = info_sym->GetAddressRef().GetFileAddress();
+ if (addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ const addr_t size = info_sym->GetByteSize();
+ const FileSpec fs = m_module->GetFileSpec();
+
+ auto buffer =
+ FileSystem::Instance().CreateDataBuffer(fs.GetPath(), size, addr);
+ if (!buffer)
+ return false;
+
+ // split rs.info. contents into lines
+ llvm::SmallVector<llvm::StringRef, 128> info_lines;
+ {
+ const llvm::StringRef raw_rs_info((const char *)buffer->GetBytes());
+ raw_rs_info.split(info_lines, '\n');
+ if (log)
+ log->Printf("'.rs.info symbol for '%s':\n%s",
+ m_module->GetFileSpec().GetCString(),
+ raw_rs_info.str().c_str());
+ }
+
+ enum {
+ eExportVar,
+ eExportForEach,
+ eExportReduce,
+ ePragma,
+ eBuildChecksum,
+ eObjectSlot,
+ eVersionInfo,
+ };
+
+ const auto rs_info_handler = [](llvm::StringRef name) -> int {
+ return llvm::StringSwitch<int>(name)
+ // The number of visible global variables in the script
+ .Case("exportVarCount", eExportVar)
+ // The number of RenderScrip `forEach` kernels __attribute__((kernel))
+ .Case("exportForEachCount", eExportForEach)
+ // The number of generalreductions: This marked in the script by
+ // `#pragma reduce()`
+ .Case("exportReduceCount", eExportReduce)
+ // Total count of all RenderScript specific `#pragmas` used in the
+ // script
+ .Case("pragmaCount", ePragma)
+ .Case("objectSlotCount", eObjectSlot)
+ .Case("versionInfo", eVersionInfo)
+ .Default(-1);
+ };
+
+ // parse all text lines of .rs.info
+ for (auto line = info_lines.begin(); line != info_lines.end(); ++line) {
+ const auto kv_pair = line->split(": ");
+ const auto key = kv_pair.first;
+ const auto val = kv_pair.second.trim();
+
+ const auto handler = rs_info_handler(key);
+ if (handler == -1)
+ continue;
+ // getAsInteger returns `true` on an error condition - we're only
+ // interested in numeric fields at the moment
+ uint64_t n_lines;
+ if (val.getAsInteger(10, n_lines)) {
+ LLDB_LOGV(log, "Failed to parse non-numeric '.rs.info' section {0}",
+ line->str());
+ continue;
+ }
+ if (info_lines.end() - (line + 1) < (ptrdiff_t)n_lines)
+ return false;
+
+ bool success = false;
+ switch (handler) {
+ case eExportVar:
+ success = ParseExportVarCount(line, n_lines);
+ break;
+ case eExportForEach:
+ success = ParseExportForeachCount(line, n_lines);
+ break;
+ case eExportReduce:
+ success = ParseExportReduceCount(line, n_lines);
+ break;
+ case ePragma:
+ success = ParsePragmaCount(line, n_lines);
+ break;
+ case eVersionInfo:
+ success = ParseVersionInfo(line, n_lines);
+ break;
+ default: {
+ if (log)
+ log->Printf("%s - skipping .rs.info field '%s'", __FUNCTION__,
+ line->str().c_str());
+ continue;
+ }
+ }
+ if (!success)
+ return false;
+ line += n_lines;
+ }
+ return info_lines.size() > 0;
+}
+
+void RenderScriptRuntime::DumpStatus(Stream &strm) const {
+ if (m_libRS) {
+ strm.Printf("Runtime Library discovered.");
+ strm.EOL();
+ }
+ if (m_libRSDriver) {
+ strm.Printf("Runtime Driver discovered.");
+ strm.EOL();
+ }
+ if (m_libRSCpuRef) {
+ strm.Printf("CPU Reference Implementation discovered.");
+ strm.EOL();
+ }
+
+ if (m_runtimeHooks.size()) {
+ strm.Printf("Runtime functions hooked:");
+ strm.EOL();
+ for (auto b : m_runtimeHooks) {
+ strm.Indent(b.second->defn->name);
+ strm.EOL();
+ }
+ } else {
+ strm.Printf("Runtime is not hooked.");
+ strm.EOL();
+ }
+}
+
+void RenderScriptRuntime::DumpContexts(Stream &strm) const {
+ strm.Printf("Inferred RenderScript Contexts:");
+ strm.EOL();
+ strm.IndentMore();
+
+ std::map<addr_t, uint64_t> contextReferences;
+
+ // Iterate over all of the currently discovered scripts. Note: We cant push
+ // or pop from m_scripts inside this loop or it may invalidate script.
+ for (const auto &script : m_scripts) {
+ if (!script->context.isValid())
+ continue;
+ lldb::addr_t context = *script->context;
+
+ if (contextReferences.find(context) != contextReferences.end()) {
+ contextReferences[context]++;
+ } else {
+ contextReferences[context] = 1;
+ }
+ }
+
+ for (const auto &cRef : contextReferences) {
+ strm.Printf("Context 0x%" PRIx64 ": %" PRIu64 " script instances",
+ cRef.first, cRef.second);
+ strm.EOL();
+ }
+ strm.IndentLess();
+}
+
+void RenderScriptRuntime::DumpKernels(Stream &strm) const {
+ strm.Printf("RenderScript Kernels:");
+ strm.EOL();
+ strm.IndentMore();
+ for (const auto &module : m_rsmodules) {
+ strm.Printf("Resource '%s':", module->m_resname.c_str());
+ strm.EOL();
+ for (const auto &kernel : module->m_kernels) {
+ strm.Indent(kernel.m_name.AsCString());
+ strm.EOL();
+ }
+ }
+ strm.IndentLess();
+}
+
+RenderScriptRuntime::AllocationDetails *
+RenderScriptRuntime::FindAllocByID(Stream &strm, const uint32_t alloc_id) {
+ AllocationDetails *alloc = nullptr;
+
+ // See if we can find allocation using id as an index;
+ if (alloc_id <= m_allocations.size() && alloc_id != 0 &&
+ m_allocations[alloc_id - 1]->id == alloc_id) {
+ alloc = m_allocations[alloc_id - 1].get();
+ return alloc;
+ }
+
+ // Fallback to searching
+ for (const auto &a : m_allocations) {
+ if (a->id == alloc_id) {
+ alloc = a.get();
+ break;
+ }
+ }
+
+ if (alloc == nullptr) {
+ strm.Printf("Error: Couldn't find allocation with id matching %" PRIu32,
+ alloc_id);
+ strm.EOL();
+ }
+
+ return alloc;
+}
+
+// Prints the contents of an allocation to the output stream, which may be a
+// file
+bool RenderScriptRuntime::DumpAllocation(Stream &strm, StackFrame *frame_ptr,
+ const uint32_t id) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ // Check we can find the desired allocation
+ AllocationDetails *alloc = FindAllocByID(strm, id);
+ if (!alloc)
+ return false; // FindAllocByID() will print error message for us here
+
+ if (log)
+ log->Printf("%s - found allocation 0x%" PRIx64, __FUNCTION__,
+ *alloc->address.get());
+
+ // Check we have information about the allocation, if not calculate it
+ if (alloc->ShouldRefresh()) {
+ if (log)
+ log->Printf("%s - allocation details not calculated yet, jitting info.",
+ __FUNCTION__);
+
+ // JIT all the allocation information
+ if (!RefreshAllocation(alloc, frame_ptr)) {
+ strm.Printf("Error: Couldn't JIT allocation details");
+ strm.EOL();
+ return false;
+ }
+ }
+
+ // Establish format and size of each data element
+ const uint32_t vec_size = *alloc->element.type_vec_size.get();
+ const Element::DataType type = *alloc->element.type.get();
+
+ assert(type >= Element::RS_TYPE_NONE && type <= Element::RS_TYPE_FONT &&
+ "Invalid allocation type");
+
+ lldb::Format format;
+ if (type >= Element::RS_TYPE_ELEMENT)
+ format = eFormatHex;
+ else
+ format = vec_size == 1
+ ? static_cast<lldb::Format>(
+ AllocationDetails::RSTypeToFormat[type][eFormatSingle])
+ : static_cast<lldb::Format>(
+ AllocationDetails::RSTypeToFormat[type][eFormatVector]);
+
+ const uint32_t data_size = *alloc->element.datum_size.get();
+
+ if (log)
+ log->Printf("%s - element size %" PRIu32 " bytes, including padding",
+ __FUNCTION__, data_size);
+
+ // Allocate a buffer to copy data into
+ std::shared_ptr<uint8_t> buffer = GetAllocationData(alloc, frame_ptr);
+ if (!buffer) {
+ strm.Printf("Error: Couldn't read allocation data");
+ strm.EOL();
+ return false;
+ }
+
+ // Calculate stride between rows as there may be padding at end of rows since
+ // allocated memory is 16-byte aligned
+ if (!alloc->stride.isValid()) {
+ if (alloc->dimension.get()->dim_2 == 0) // We only have one dimension
+ alloc->stride = 0;
+ else if (!JITAllocationStride(alloc, frame_ptr)) {
+ strm.Printf("Error: Couldn't calculate allocation row stride");
+ strm.EOL();
+ return false;
+ }
+ }
+ const uint32_t stride = *alloc->stride.get();
+ const uint32_t size = *alloc->size.get(); // Size of whole allocation
+ const uint32_t padding =
+ alloc->element.padding.isValid() ? *alloc->element.padding.get() : 0;
+ if (log)
+ log->Printf("%s - stride %" PRIu32 " bytes, size %" PRIu32
+ " bytes, padding %" PRIu32,
+ __FUNCTION__, stride, size, padding);
+
+ // Find dimensions used to index loops, so need to be non-zero
+ uint32_t dim_x = alloc->dimension.get()->dim_1;
+ dim_x = dim_x == 0 ? 1 : dim_x;
+
+ uint32_t dim_y = alloc->dimension.get()->dim_2;
+ dim_y = dim_y == 0 ? 1 : dim_y;
+
+ uint32_t dim_z = alloc->dimension.get()->dim_3;
+ dim_z = dim_z == 0 ? 1 : dim_z;
+
+ // Use data extractor to format output
+ const uint32_t target_ptr_size =
+ GetProcess()->GetTarget().GetArchitecture().GetAddressByteSize();
+ DataExtractor alloc_data(buffer.get(), size, GetProcess()->GetByteOrder(),
+ target_ptr_size);
+
+ uint32_t offset = 0; // Offset in buffer to next element to be printed
+ uint32_t prev_row = 0; // Offset to the start of the previous row
+
+ // Iterate over allocation dimensions, printing results to user
+ strm.Printf("Data (X, Y, Z):");
+ for (uint32_t z = 0; z < dim_z; ++z) {
+ for (uint32_t y = 0; y < dim_y; ++y) {
+ // Use stride to index start of next row.
+ if (!(y == 0 && z == 0))
+ offset = prev_row + stride;
+ prev_row = offset;
+
+ // Print each element in the row individually
+ for (uint32_t x = 0; x < dim_x; ++x) {
+ strm.Printf("\n(%" PRIu32 ", %" PRIu32 ", %" PRIu32 ") = ", x, y, z);
+ if ((type == Element::RS_TYPE_NONE) &&
+ (alloc->element.children.size() > 0) &&
+ (alloc->element.type_name != Element::GetFallbackStructName())) {
+ // Here we are dumping an Element of struct type. This is done using
+ // expression evaluation with the name of the struct type and pointer
+ // to element. Don't print the name of the resulting expression,
+ // since this will be '$[0-9]+'
+ DumpValueObjectOptions expr_options;
+ expr_options.SetHideName(true);
+
+ // Setup expression as dereferencing a pointer cast to element
+ // address.
+ char expr_char_buffer[jit_max_expr_size];
+ int written =
+ snprintf(expr_char_buffer, jit_max_expr_size, "*(%s*) 0x%" PRIx64,
+ alloc->element.type_name.AsCString(),
+ *alloc->data_ptr.get() + offset);
+
+ if (written < 0 || written >= jit_max_expr_size) {
+ if (log)
+ log->Printf("%s - error in snprintf().", __FUNCTION__);
+ continue;
+ }
+
+ // Evaluate expression
+ ValueObjectSP expr_result;
+ GetProcess()->GetTarget().EvaluateExpression(expr_char_buffer,
+ frame_ptr, expr_result);
+
+ // Print the results to our stream.
+ expr_result->Dump(strm, expr_options);
+ } else {
+ DumpDataExtractor(alloc_data, &strm, offset, format,
+ data_size - padding, 1, 1, LLDB_INVALID_ADDRESS, 0,
+ 0);
+ }
+ offset += data_size;
+ }
+ }
+ }
+ strm.EOL();
+
+ return true;
+}
+
+// Function recalculates all our cached information about allocations by
+// jitting the RS runtime regarding each allocation we know about. Returns true
+// if all allocations could be recomputed, false otherwise.
+bool RenderScriptRuntime::RecomputeAllAllocations(Stream &strm,
+ StackFrame *frame_ptr) {
+ bool success = true;
+ for (auto &alloc : m_allocations) {
+ // JIT current allocation information
+ if (!RefreshAllocation(alloc.get(), frame_ptr)) {
+ strm.Printf("Error: Couldn't evaluate details for allocation %" PRIu32
+ "\n",
+ alloc->id);
+ success = false;
+ }
+ }
+
+ if (success)
+ strm.Printf("All allocations successfully recomputed");
+ strm.EOL();
+
+ return success;
+}
+
+// Prints information regarding currently loaded allocations. These details are
+// gathered by jitting the runtime, which has as latency. Index parameter
+// specifies a single allocation ID to print, or a zero value to print them all
+void RenderScriptRuntime::ListAllocations(Stream &strm, StackFrame *frame_ptr,
+ const uint32_t index) {
+ strm.Printf("RenderScript Allocations:");
+ strm.EOL();
+ strm.IndentMore();
+
+ for (auto &alloc : m_allocations) {
+ // index will only be zero if we want to print all allocations
+ if (index != 0 && index != alloc->id)
+ continue;
+
+ // JIT current allocation information
+ if (alloc->ShouldRefresh() && !RefreshAllocation(alloc.get(), frame_ptr)) {
+ strm.Printf("Error: Couldn't evaluate details for allocation %" PRIu32,
+ alloc->id);
+ strm.EOL();
+ continue;
+ }
+
+ strm.Printf("%" PRIu32 ":", alloc->id);
+ strm.EOL();
+ strm.IndentMore();
+
+ strm.Indent("Context: ");
+ if (!alloc->context.isValid())
+ strm.Printf("unknown\n");
+ else
+ strm.Printf("0x%" PRIx64 "\n", *alloc->context.get());
+
+ strm.Indent("Address: ");
+ if (!alloc->address.isValid())
+ strm.Printf("unknown\n");
+ else
+ strm.Printf("0x%" PRIx64 "\n", *alloc->address.get());
+
+ strm.Indent("Data pointer: ");
+ if (!alloc->data_ptr.isValid())
+ strm.Printf("unknown\n");
+ else
+ strm.Printf("0x%" PRIx64 "\n", *alloc->data_ptr.get());
+
+ strm.Indent("Dimensions: ");
+ if (!alloc->dimension.isValid())
+ strm.Printf("unknown\n");
+ else
+ strm.Printf("(%" PRId32 ", %" PRId32 ", %" PRId32 ")\n",
+ alloc->dimension.get()->dim_1, alloc->dimension.get()->dim_2,
+ alloc->dimension.get()->dim_3);
+
+ strm.Indent("Data Type: ");
+ if (!alloc->element.type.isValid() ||
+ !alloc->element.type_vec_size.isValid())
+ strm.Printf("unknown\n");
+ else {
+ const int vector_size = *alloc->element.type_vec_size.get();
+ Element::DataType type = *alloc->element.type.get();
+
+ if (!alloc->element.type_name.IsEmpty())
+ strm.Printf("%s\n", alloc->element.type_name.AsCString());
+ else {
+ // Enum value isn't monotonous, so doesn't always index
+ // RsDataTypeToString array
+ if (type >= Element::RS_TYPE_ELEMENT && type <= Element::RS_TYPE_FONT)
+ type =
+ static_cast<Element::DataType>((type - Element::RS_TYPE_ELEMENT) +
+ Element::RS_TYPE_MATRIX_2X2 + 1);
+
+ if (type >= (sizeof(AllocationDetails::RsDataTypeToString) /
+ sizeof(AllocationDetails::RsDataTypeToString[0])) ||
+ vector_size > 4 || vector_size < 1)
+ strm.Printf("invalid type\n");
+ else
+ strm.Printf(
+ "%s\n",
+ AllocationDetails::RsDataTypeToString[static_cast<uint32_t>(type)]
+ [vector_size - 1]);
+ }
+ }
+
+ strm.Indent("Data Kind: ");
+ if (!alloc->element.type_kind.isValid())
+ strm.Printf("unknown\n");
+ else {
+ const Element::DataKind kind = *alloc->element.type_kind.get();
+ if (kind < Element::RS_KIND_USER || kind > Element::RS_KIND_PIXEL_YUV)
+ strm.Printf("invalid kind\n");
+ else
+ strm.Printf(
+ "%s\n",
+ AllocationDetails::RsDataKindToString[static_cast<uint32_t>(kind)]);
+ }
+
+ strm.EOL();
+ strm.IndentLess();
+ }
+ strm.IndentLess();
+}
+
+// Set breakpoints on every kernel found in RS module
+void RenderScriptRuntime::BreakOnModuleKernels(
+ const RSModuleDescriptorSP rsmodule_sp) {
+ for (const auto &kernel : rsmodule_sp->m_kernels) {
+ // Don't set breakpoint on 'root' kernel
+ if (strcmp(kernel.m_name.AsCString(), "root") == 0)
+ continue;
+
+ CreateKernelBreakpoint(kernel.m_name);
+ }
+}
+
+// Method is internally called by the 'kernel breakpoint all' command to enable
+// or disable breaking on all kernels. When do_break is true we want to enable
+// this functionality. When do_break is false we want to disable it.
+void RenderScriptRuntime::SetBreakAllKernels(bool do_break, TargetSP target) {
+ Log *log(
+ GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_BREAKPOINTS));
+
+ InitSearchFilter(target);
+
+ // Set breakpoints on all the kernels
+ if (do_break && !m_breakAllKernels) {
+ m_breakAllKernels = true;
+
+ for (const auto &module : m_rsmodules)
+ BreakOnModuleKernels(module);
+
+ if (log)
+ log->Printf("%s(True) - breakpoints set on all currently loaded kernels.",
+ __FUNCTION__);
+ } else if (!do_break &&
+ m_breakAllKernels) // Breakpoints won't be set on any new kernels.
+ {
+ m_breakAllKernels = false;
+
+ if (log)
+ log->Printf("%s(False) - breakpoints no longer automatically set.",
+ __FUNCTION__);
+ }
+}
+
+// Given the name of a kernel this function creates a breakpoint using our own
+// breakpoint resolver, and returns the Breakpoint shared pointer.
+BreakpointSP
+RenderScriptRuntime::CreateKernelBreakpoint(ConstString name) {
+ Log *log(
+ GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_BREAKPOINTS));
+
+ if (!m_filtersp) {
+ if (log)
+ log->Printf("%s - error, no breakpoint search filter set.", __FUNCTION__);
+ return nullptr;
+ }
+
+ BreakpointResolverSP resolver_sp(new RSBreakpointResolver(nullptr, name));
+ Target &target = GetProcess()->GetTarget();
+ BreakpointSP bp = target.CreateBreakpoint(
+ m_filtersp, resolver_sp, false, false, false);
+
+ // Give RS breakpoints a specific name, so the user can manipulate them as a
+ // group.
+ Status err;
+ target.AddNameToBreakpoint(bp, "RenderScriptKernel", err);
+ if (err.Fail() && log)
+ if (log)
+ log->Printf("%s - error setting break name, '%s'.", __FUNCTION__,
+ err.AsCString());
+
+ return bp;
+}
+
+BreakpointSP
+RenderScriptRuntime::CreateReductionBreakpoint(ConstString name,
+ int kernel_types) {
+ Log *log(
+ GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_BREAKPOINTS));
+
+ if (!m_filtersp) {
+ if (log)
+ log->Printf("%s - error, no breakpoint search filter set.", __FUNCTION__);
+ return nullptr;
+ }
+
+ BreakpointResolverSP resolver_sp(new RSReduceBreakpointResolver(
+ nullptr, name, &m_rsmodules, kernel_types));
+ Target &target = GetProcess()->GetTarget();
+ BreakpointSP bp = target.CreateBreakpoint(
+ m_filtersp, resolver_sp, false, false, false);
+
+ // Give RS breakpoints a specific name, so the user can manipulate them as a
+ // group.
+ Status err;
+ target.AddNameToBreakpoint(bp, "RenderScriptReduction", err);
+ if (err.Fail() && log)
+ log->Printf("%s - error setting break name, '%s'.", __FUNCTION__,
+ err.AsCString());
+
+ return bp;
+}
+
+// Given an expression for a variable this function tries to calculate the
+// variable's value. If this is possible it returns true and sets the uint64_t
+// parameter to the variables unsigned value. Otherwise function returns false.
+bool RenderScriptRuntime::GetFrameVarAsUnsigned(const StackFrameSP frame_sp,
+ const char *var_name,
+ uint64_t &val) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+ Status err;
+ VariableSP var_sp;
+
+ // Find variable in stack frame
+ ValueObjectSP value_sp(frame_sp->GetValueForVariableExpressionPath(
+ var_name, eNoDynamicValues,
+ StackFrame::eExpressionPathOptionCheckPtrVsMember |
+ StackFrame::eExpressionPathOptionsAllowDirectIVarAccess,
+ var_sp, err));
+ if (!err.Success()) {
+ if (log)
+ log->Printf("%s - error, couldn't find '%s' in frame", __FUNCTION__,
+ var_name);
+ return false;
+ }
+
+ // Find the uint32_t value for the variable
+ bool success = false;
+ val = value_sp->GetValueAsUnsigned(0, &success);
+ if (!success) {
+ if (log)
+ log->Printf("%s - error, couldn't parse '%s' as an uint32_t.",
+ __FUNCTION__, var_name);
+ return false;
+ }
+
+ return true;
+}
+
+// Function attempts to find the current coordinate of a kernel invocation by
+// investigating the values of frame variables in the .expand function. These
+// coordinates are returned via the coord array reference parameter. Returns
+// true if the coordinates could be found, and false otherwise.
+bool RenderScriptRuntime::GetKernelCoordinate(RSCoordinate &coord,
+ Thread *thread_ptr) {
+ static const char *const x_expr = "rsIndex";
+ static const char *const y_expr = "p->current.y";
+ static const char *const z_expr = "p->current.z";
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE));
+
+ if (!thread_ptr) {
+ if (log)
+ log->Printf("%s - Error, No thread pointer", __FUNCTION__);
+
+ return false;
+ }
+
+ // Walk the call stack looking for a function whose name has the suffix
+ // '.expand' and contains the variables we're looking for.
+ for (uint32_t i = 0; i < thread_ptr->GetStackFrameCount(); ++i) {
+ if (!thread_ptr->SetSelectedFrameByIndex(i))
+ continue;
+
+ StackFrameSP frame_sp = thread_ptr->GetSelectedFrame();
+ if (!frame_sp)
+ continue;
+
+ // Find the function name
+ const SymbolContext sym_ctx =
+ frame_sp->GetSymbolContext(eSymbolContextFunction);
+ const ConstString func_name = sym_ctx.GetFunctionName();
+ if (!func_name)
+ continue;
+
+ if (log)
+ log->Printf("%s - Inspecting function '%s'", __FUNCTION__,
+ func_name.GetCString());
+
+ // Check if function name has .expand suffix
+ if (!func_name.GetStringRef().endswith(".expand"))
+ continue;
+
+ if (log)
+ log->Printf("%s - Found .expand function '%s'", __FUNCTION__,
+ func_name.GetCString());
+
+ // Get values for variables in .expand frame that tell us the current
+ // kernel invocation
+ uint64_t x, y, z;
+ bool found = GetFrameVarAsUnsigned(frame_sp, x_expr, x) &&
+ GetFrameVarAsUnsigned(frame_sp, y_expr, y) &&
+ GetFrameVarAsUnsigned(frame_sp, z_expr, z);
+
+ if (found) {
+ // The RenderScript runtime uses uint32_t for these vars. If they're not
+ // within bounds, our frame parsing is garbage
+ assert(x <= UINT32_MAX && y <= UINT32_MAX && z <= UINT32_MAX);
+ coord.x = (uint32_t)x;
+ coord.y = (uint32_t)y;
+ coord.z = (uint32_t)z;
+ return true;
+ }
+ }
+ return false;
+}
+
+// Callback when a kernel breakpoint hits and we're looking for a specific
+// coordinate. Baton parameter contains a pointer to the target coordinate we
+// want to break on. Function then checks the .expand frame for the current
+// coordinate and breaks to user if it matches. Parameter 'break_id' is the id
+// of the Breakpoint which made the callback. Parameter 'break_loc_id' is the
+// id for the BreakpointLocation which was hit, a single logical breakpoint can
+// have multiple addresses.
+bool RenderScriptRuntime::KernelBreakpointHit(void *baton,
+ StoppointCallbackContext *ctx,
+ user_id_t break_id,
+ user_id_t break_loc_id) {
+ Log *log(
+ GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_BREAKPOINTS));
+
+ assert(baton &&
+ "Error: null baton in conditional kernel breakpoint callback");
+
+ // Coordinate we want to stop on
+ RSCoordinate target_coord = *static_cast<RSCoordinate *>(baton);
+
+ if (log)
+ log->Printf("%s - Break ID %" PRIu64 ", " FMT_COORD, __FUNCTION__, break_id,
+ target_coord.x, target_coord.y, target_coord.z);
+
+ // Select current thread
+ ExecutionContext context(ctx->exe_ctx_ref);
+ Thread *thread_ptr = context.GetThreadPtr();
+ assert(thread_ptr && "Null thread pointer");
+
+ // Find current kernel invocation from .expand frame variables
+ RSCoordinate current_coord{};
+ if (!GetKernelCoordinate(current_coord, thread_ptr)) {
+ if (log)
+ log->Printf("%s - Error, couldn't select .expand stack frame",
+ __FUNCTION__);
+ return false;
+ }
+
+ if (log)
+ log->Printf("%s - " FMT_COORD, __FUNCTION__, current_coord.x,
+ current_coord.y, current_coord.z);
+
+ // Check if the current kernel invocation coordinate matches our target
+ // coordinate
+ if (target_coord == current_coord) {
+ if (log)
+ log->Printf("%s, BREAKING " FMT_COORD, __FUNCTION__, current_coord.x,
+ current_coord.y, current_coord.z);
+
+ BreakpointSP breakpoint_sp =
+ context.GetTargetPtr()->GetBreakpointByID(break_id);
+ assert(breakpoint_sp != nullptr &&
+ "Error: Couldn't find breakpoint matching break id for callback");
+ breakpoint_sp->SetEnabled(false); // Optimise since conditional breakpoint
+ // should only be hit once.
+ return true;
+ }
+
+ // No match on coordinate
+ return false;
+}
+
+void RenderScriptRuntime::SetConditional(BreakpointSP bp, Stream &messages,
+ const RSCoordinate &coord) {
+ messages.Printf("Conditional kernel breakpoint on coordinate " FMT_COORD,
+ coord.x, coord.y, coord.z);
+ messages.EOL();
+
+ // Allocate memory for the baton, and copy over coordinate
+ RSCoordinate *baton = new RSCoordinate(coord);
+
+ // Create a callback that will be invoked every time the breakpoint is hit.
+ // The baton object passed to the handler is the target coordinate we want to
+ // break on.
+ bp->SetCallback(KernelBreakpointHit, baton, true);
+
+ // Store a shared pointer to the baton, so the memory will eventually be
+ // cleaned up after destruction
+ m_conditional_breaks[bp->GetID()] = std::unique_ptr<RSCoordinate>(baton);
+}
+
+// Tries to set a breakpoint on the start of a kernel, resolved using the
+// kernel name. Argument 'coords', represents a three dimensional coordinate
+// which can be used to specify a single kernel instance to break on. If this
+// is set then we add a callback to the breakpoint.
+bool RenderScriptRuntime::PlaceBreakpointOnKernel(TargetSP target,
+ Stream &messages,
+ const char *name,
+ const RSCoordinate *coord) {
+ if (!name)
+ return false;
+
+ InitSearchFilter(target);
+
+ ConstString kernel_name(name);
+ BreakpointSP bp = CreateKernelBreakpoint(kernel_name);
+ if (!bp)
+ return false;
+
+ // We have a conditional breakpoint on a specific coordinate
+ if (coord)
+ SetConditional(bp, messages, *coord);
+
+ bp->GetDescription(&messages, lldb::eDescriptionLevelInitial, false);
+
+ return true;
+}
+
+BreakpointSP
+RenderScriptRuntime::CreateScriptGroupBreakpoint(ConstString name,
+ bool stop_on_all) {
+ Log *log(
+ GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_BREAKPOINTS));
+
+ if (!m_filtersp) {
+ if (log)
+ log->Printf("%s - error, no breakpoint search filter set.", __FUNCTION__);
+ return nullptr;
+ }
+
+ BreakpointResolverSP resolver_sp(new RSScriptGroupBreakpointResolver(
+ nullptr, name, m_scriptGroups, stop_on_all));
+ Target &target = GetProcess()->GetTarget();
+ BreakpointSP bp = target.CreateBreakpoint(
+ m_filtersp, resolver_sp, false, false, false);
+ // Give RS breakpoints a specific name, so the user can manipulate them as a
+ // group.
+ Status err;
+ target.AddNameToBreakpoint(bp, name.GetCString(), err);
+ if (err.Fail() && log)
+ log->Printf("%s - error setting break name, '%s'.", __FUNCTION__,
+ err.AsCString());
+ // ask the breakpoint to resolve itself
+ bp->ResolveBreakpoint();
+ return bp;
+}
+
+bool RenderScriptRuntime::PlaceBreakpointOnScriptGroup(TargetSP target,
+ Stream &strm,
+ ConstString name,
+ bool multi) {
+ InitSearchFilter(target);
+ BreakpointSP bp = CreateScriptGroupBreakpoint(name, multi);
+ if (bp)
+ bp->GetDescription(&strm, lldb::eDescriptionLevelInitial, false);
+ return bool(bp);
+}
+
+bool RenderScriptRuntime::PlaceBreakpointOnReduction(TargetSP target,
+ Stream &messages,
+ const char *reduce_name,
+ const RSCoordinate *coord,
+ int kernel_types) {
+ if (!reduce_name)
+ return false;
+
+ InitSearchFilter(target);
+ BreakpointSP bp =
+ CreateReductionBreakpoint(ConstString(reduce_name), kernel_types);
+ if (!bp)
+ return false;
+
+ if (coord)
+ SetConditional(bp, messages, *coord);
+
+ bp->GetDescription(&messages, lldb::eDescriptionLevelInitial, false);
+
+ return true;
+}
+
+void RenderScriptRuntime::DumpModules(Stream &strm) const {
+ strm.Printf("RenderScript Modules:");
+ strm.EOL();
+ strm.IndentMore();
+ for (const auto &module : m_rsmodules) {
+ module->Dump(strm);
+ }
+ strm.IndentLess();
+}
+
+RenderScriptRuntime::ScriptDetails *
+RenderScriptRuntime::LookUpScript(addr_t address, bool create) {
+ for (const auto &s : m_scripts) {
+ if (s->script.isValid())
+ if (*s->script == address)
+ return s.get();
+ }
+ if (create) {
+ std::unique_ptr<ScriptDetails> s(new ScriptDetails);
+ s->script = address;
+ m_scripts.push_back(std::move(s));
+ return m_scripts.back().get();
+ }
+ return nullptr;
+}
+
+RenderScriptRuntime::AllocationDetails *
+RenderScriptRuntime::LookUpAllocation(addr_t address) {
+ for (const auto &a : m_allocations) {
+ if (a->address.isValid())
+ if (*a->address == address)
+ return a.get();
+ }
+ return nullptr;
+}
+
+RenderScriptRuntime::AllocationDetails *
+RenderScriptRuntime::CreateAllocation(addr_t address) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE);
+
+ // Remove any previous allocation which contains the same address
+ auto it = m_allocations.begin();
+ while (it != m_allocations.end()) {
+ if (*((*it)->address) == address) {
+ if (log)
+ log->Printf("%s - Removing allocation id: %d, address: 0x%" PRIx64,
+ __FUNCTION__, (*it)->id, address);
+
+ it = m_allocations.erase(it);
+ } else {
+ it++;
+ }
+ }
+
+ std::unique_ptr<AllocationDetails> a(new AllocationDetails);
+ a->address = address;
+ m_allocations.push_back(std::move(a));
+ return m_allocations.back().get();
+}
+
+bool RenderScriptRuntime::ResolveKernelName(lldb::addr_t kernel_addr,
+ ConstString &name) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS);
+
+ Target &target = GetProcess()->GetTarget();
+ Address resolved;
+ // RenderScript module
+ if (!target.GetSectionLoadList().ResolveLoadAddress(kernel_addr, resolved)) {
+ if (log)
+ log->Printf("%s: unable to resolve 0x%" PRIx64 " to a loaded symbol",
+ __FUNCTION__, kernel_addr);
+ return false;
+ }
+
+ Symbol *sym = resolved.CalculateSymbolContextSymbol();
+ if (!sym)
+ return false;
+
+ name = sym->GetName();
+ assert(IsRenderScriptModule(resolved.CalculateSymbolContextModule()));
+ if (log)
+ log->Printf("%s: 0x%" PRIx64 " resolved to the symbol '%s'", __FUNCTION__,
+ kernel_addr, name.GetCString());
+ return true;
+}
+
+void RSModuleDescriptor::Dump(Stream &strm) const {
+ int indent = strm.GetIndentLevel();
+
+ strm.Indent();
+ m_module->GetFileSpec().Dump(&strm);
+ strm.Indent(m_module->GetNumCompileUnits() ? "Debug info loaded."
+ : "Debug info does not exist.");
+ strm.EOL();
+ strm.IndentMore();
+
+ strm.Indent();
+ strm.Printf("Globals: %" PRIu64, static_cast<uint64_t>(m_globals.size()));
+ strm.EOL();
+ strm.IndentMore();
+ for (const auto &global : m_globals) {
+ global.Dump(strm);
+ }
+ strm.IndentLess();
+
+ strm.Indent();
+ strm.Printf("Kernels: %" PRIu64, static_cast<uint64_t>(m_kernels.size()));
+ strm.EOL();
+ strm.IndentMore();
+ for (const auto &kernel : m_kernels) {
+ kernel.Dump(strm);
+ }
+ strm.IndentLess();
+
+ strm.Indent();
+ strm.Printf("Pragmas: %" PRIu64, static_cast<uint64_t>(m_pragmas.size()));
+ strm.EOL();
+ strm.IndentMore();
+ for (const auto &key_val : m_pragmas) {
+ strm.Indent();
+ strm.Printf("%s: %s", key_val.first.c_str(), key_val.second.c_str());
+ strm.EOL();
+ }
+ strm.IndentLess();
+
+ strm.Indent();
+ strm.Printf("Reductions: %" PRIu64,
+ static_cast<uint64_t>(m_reductions.size()));
+ strm.EOL();
+ strm.IndentMore();
+ for (const auto &reduction : m_reductions) {
+ reduction.Dump(strm);
+ }
+
+ strm.SetIndentLevel(indent);
+}
+
+void RSGlobalDescriptor::Dump(Stream &strm) const {
+ strm.Indent(m_name.AsCString());
+ VariableList var_list;
+ m_module->m_module->FindGlobalVariables(m_name, nullptr, 1U, var_list);
+ if (var_list.GetSize() == 1) {
+ auto var = var_list.GetVariableAtIndex(0);
+ auto type = var->GetType();
+ if (type) {
+ strm.Printf(" - ");
+ type->DumpTypeName(&strm);
+ } else {
+ strm.Printf(" - Unknown Type");
+ }
+ } else {
+ strm.Printf(" - variable identified, but not found in binary");
+ const Symbol *s = m_module->m_module->FindFirstSymbolWithNameAndType(
+ m_name, eSymbolTypeData);
+ if (s) {
+ strm.Printf(" (symbol exists) ");
+ }
+ }
+
+ strm.EOL();
+}
+
+void RSKernelDescriptor::Dump(Stream &strm) const {
+ strm.Indent(m_name.AsCString());
+ strm.EOL();
+}
+
+void RSReductionDescriptor::Dump(lldb_private::Stream &stream) const {
+ stream.Indent(m_reduce_name.AsCString());
+ stream.IndentMore();
+ stream.EOL();
+ stream.Indent();
+ stream.Printf("accumulator: %s", m_accum_name.AsCString());
+ stream.EOL();
+ stream.Indent();
+ stream.Printf("initializer: %s", m_init_name.AsCString());
+ stream.EOL();
+ stream.Indent();
+ stream.Printf("combiner: %s", m_comb_name.AsCString());
+ stream.EOL();
+ stream.Indent();
+ stream.Printf("outconverter: %s", m_outc_name.AsCString());
+ stream.EOL();
+ // XXX This is currently unspecified by RenderScript, and unused
+ // stream.Indent();
+ // stream.Printf("halter: '%s'", m_init_name.AsCString());
+ // stream.EOL();
+ stream.IndentLess();
+}
+
+class CommandObjectRenderScriptRuntimeModuleDump : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeModuleDump(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "renderscript module dump",
+ "Dumps renderscript specific information for all modules.",
+ "renderscript module dump",
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched) {}
+
+ ~CommandObjectRenderScriptRuntimeModuleDump() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ RenderScriptRuntime *runtime = llvm::cast<RenderScriptRuntime>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+ runtime->DumpModules(result.GetOutputStream());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+class CommandObjectRenderScriptRuntimeModule : public CommandObjectMultiword {
+public:
+ CommandObjectRenderScriptRuntimeModule(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "renderscript module",
+ "Commands that deal with RenderScript modules.",
+ nullptr) {
+ LoadSubCommand(
+ "dump", CommandObjectSP(new CommandObjectRenderScriptRuntimeModuleDump(
+ interpreter)));
+ }
+
+ ~CommandObjectRenderScriptRuntimeModule() override = default;
+};
+
+class CommandObjectRenderScriptRuntimeKernelList : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeKernelList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "renderscript kernel list",
+ "Lists renderscript kernel names and associated script resources.",
+ "renderscript kernel list",
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched) {}
+
+ ~CommandObjectRenderScriptRuntimeKernelList() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ RenderScriptRuntime *runtime = llvm::cast<RenderScriptRuntime>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+ runtime->DumpKernels(result.GetOutputStream());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+static constexpr OptionDefinition g_renderscript_reduction_bp_set_options[] = {
+ {LLDB_OPT_SET_1, false, "function-role", 't',
+ OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeOneLiner,
+ "Break on a comma separated set of reduction kernel types "
+ "(accumulator,outcoverter,combiner,initializer"},
+ {LLDB_OPT_SET_1, false, "coordinate", 'c', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeValue,
+ "Set a breakpoint on a single invocation of the kernel with specified "
+ "coordinate.\n"
+ "Coordinate takes the form 'x[,y][,z] where x,y,z are positive "
+ "integers representing kernel dimensions. "
+ "Any unset dimensions will be defaulted to zero."}};
+
+class CommandObjectRenderScriptRuntimeReductionBreakpointSet
+ : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeReductionBreakpointSet(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "renderscript reduction breakpoint set",
+ "Set a breakpoint on named RenderScript general reductions",
+ "renderscript reduction breakpoint set <kernel_name> [-t "
+ "<reduction_kernel_type,...>]",
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused),
+ m_options(){};
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions()
+ : Options(),
+ m_kernel_types(RSReduceBreakpointResolver::eKernelTypeAll) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *exe_ctx) override {
+ Status err;
+ StreamString err_str;
+ const int short_option = m_getopt_table[option_idx].val;
+ switch (short_option) {
+ case 't':
+ if (!ParseReductionTypes(option_arg, err_str))
+ err.SetErrorStringWithFormat(
+ "Unable to deduce reduction types for %s: %s",
+ option_arg.str().c_str(), err_str.GetData());
+ break;
+ case 'c': {
+ auto coord = RSCoordinate{};
+ if (!ParseCoordinate(option_arg, coord))
+ err.SetErrorStringWithFormat("unable to parse coordinate for %s",
+ option_arg.str().c_str());
+ else {
+ m_have_coord = true;
+ m_coord = coord;
+ }
+ break;
+ }
+ default:
+ err.SetErrorStringWithFormat("Invalid option '-%c'", short_option);
+ }
+ return err;
+ }
+
+ void OptionParsingStarting(ExecutionContext *exe_ctx) override {
+ m_have_coord = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_renderscript_reduction_bp_set_options);
+ }
+
+ bool ParseReductionTypes(llvm::StringRef option_val,
+ StreamString &err_str) {
+ m_kernel_types = RSReduceBreakpointResolver::eKernelTypeNone;
+ const auto reduce_name_to_type = [](llvm::StringRef name) -> int {
+ return llvm::StringSwitch<int>(name)
+ .Case("accumulator", RSReduceBreakpointResolver::eKernelTypeAccum)
+ .Case("initializer", RSReduceBreakpointResolver::eKernelTypeInit)
+ .Case("outconverter", RSReduceBreakpointResolver::eKernelTypeOutC)
+ .Case("combiner", RSReduceBreakpointResolver::eKernelTypeComb)
+ .Case("all", RSReduceBreakpointResolver::eKernelTypeAll)
+ // Currently not exposed by the runtime
+ // .Case("halter", RSReduceBreakpointResolver::eKernelTypeHalter)
+ .Default(0);
+ };
+
+ // Matching a comma separated list of known words is fairly
+ // straightforward with PCRE, but we're using ERE, so we end up with a
+ // little ugliness...
+ RegularExpression::Match match(/* max_matches */ 5);
+ RegularExpression match_type_list(
+ llvm::StringRef("^([[:alpha:]]+)(,[[:alpha:]]+){0,4}$"));
+
+ assert(match_type_list.IsValid());
+
+ if (!match_type_list.Execute(option_val, &match)) {
+ err_str.PutCString(
+ "a comma-separated list of kernel types is required");
+ return false;
+ }
+
+ // splitting on commas is much easier with llvm::StringRef than regex
+ llvm::SmallVector<llvm::StringRef, 5> type_names;
+ llvm::StringRef(option_val).split(type_names, ',');
+
+ for (const auto &name : type_names) {
+ const int type = reduce_name_to_type(name);
+ if (!type) {
+ err_str.Printf("unknown kernel type name %s", name.str().c_str());
+ return false;
+ }
+ m_kernel_types |= type;
+ }
+
+ return true;
+ }
+
+ int m_kernel_types;
+ llvm::StringRef m_reduce_name;
+ RSCoordinate m_coord;
+ bool m_have_coord;
+ };
+
+ Options *GetOptions() override { return &m_options; }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ if (argc < 1) {
+ result.AppendErrorWithFormat("'%s' takes 1 argument of reduction name, "
+ "and an optional kernel type list",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ RenderScriptRuntime *runtime = static_cast<RenderScriptRuntime *>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+
+ auto &outstream = result.GetOutputStream();
+ auto name = command.GetArgumentAtIndex(0);
+ auto &target = m_exe_ctx.GetTargetSP();
+ auto coord = m_options.m_have_coord ? &m_options.m_coord : nullptr;
+ if (!runtime->PlaceBreakpointOnReduction(target, outstream, name, coord,
+ m_options.m_kernel_types)) {
+ result.SetStatus(eReturnStatusFailed);
+ result.AppendError("Error: unable to place breakpoint on reduction");
+ return false;
+ }
+ result.AppendMessage("Breakpoint(s) created");
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+static constexpr OptionDefinition g_renderscript_kernel_bp_set_options[] = {
+ {LLDB_OPT_SET_1, false, "coordinate", 'c', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeValue,
+ "Set a breakpoint on a single invocation of the kernel with specified "
+ "coordinate.\n"
+ "Coordinate takes the form 'x[,y][,z] where x,y,z are positive "
+ "integers representing kernel dimensions. "
+ "Any unset dimensions will be defaulted to zero."}};
+
+class CommandObjectRenderScriptRuntimeKernelBreakpointSet
+ : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeKernelBreakpointSet(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "renderscript kernel breakpoint set",
+ "Sets a breakpoint on a renderscript kernel.",
+ "renderscript kernel breakpoint set <kernel_name> [-c x,y,z]",
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused),
+ m_options() {}
+
+ ~CommandObjectRenderScriptRuntimeKernelBreakpointSet() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *exe_ctx) override {
+ Status err;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'c': {
+ auto coord = RSCoordinate{};
+ if (!ParseCoordinate(option_arg, coord))
+ err.SetErrorStringWithFormat(
+ "Couldn't parse coordinate '%s', should be in format 'x,y,z'.",
+ option_arg.str().c_str());
+ else {
+ m_have_coord = true;
+ m_coord = coord;
+ }
+ break;
+ }
+ default:
+ err.SetErrorStringWithFormat("unrecognized option '%c'", short_option);
+ break;
+ }
+ return err;
+ }
+
+ void OptionParsingStarting(ExecutionContext *exe_ctx) override {
+ m_have_coord = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_renderscript_kernel_bp_set_options);
+ }
+
+ RSCoordinate m_coord;
+ bool m_have_coord;
+ };
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ if (argc < 1) {
+ result.AppendErrorWithFormat(
+ "'%s' takes 1 argument of kernel name, and an optional coordinate.",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ RenderScriptRuntime *runtime = llvm::cast<RenderScriptRuntime>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+
+ auto &outstream = result.GetOutputStream();
+ auto &target = m_exe_ctx.GetTargetSP();
+ auto name = command.GetArgumentAtIndex(0);
+ auto coord = m_options.m_have_coord ? &m_options.m_coord : nullptr;
+ if (!runtime->PlaceBreakpointOnKernel(target, outstream, name, coord)) {
+ result.SetStatus(eReturnStatusFailed);
+ result.AppendErrorWithFormat(
+ "Error: unable to set breakpoint on kernel '%s'", name);
+ return false;
+ }
+
+ result.AppendMessage("Breakpoint(s) created");
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+class CommandObjectRenderScriptRuntimeKernelBreakpointAll
+ : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeKernelBreakpointAll(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "renderscript kernel breakpoint all",
+ "Automatically sets a breakpoint on all renderscript kernels that "
+ "are or will be loaded.\n"
+ "Disabling option means breakpoints will no longer be set on any "
+ "kernels loaded in the future, "
+ "but does not remove currently set breakpoints.",
+ "renderscript kernel breakpoint all <enable/disable>",
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused) {}
+
+ ~CommandObjectRenderScriptRuntimeKernelBreakpointAll() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ if (argc != 1) {
+ result.AppendErrorWithFormat(
+ "'%s' takes 1 argument of 'enable' or 'disable'", m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ RenderScriptRuntime *runtime = static_cast<RenderScriptRuntime *>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+
+ bool do_break = false;
+ const char *argument = command.GetArgumentAtIndex(0);
+ if (strcmp(argument, "enable") == 0) {
+ do_break = true;
+ result.AppendMessage("Breakpoints will be set on all kernels.");
+ } else if (strcmp(argument, "disable") == 0) {
+ do_break = false;
+ result.AppendMessage("Breakpoints will not be set on any new kernels.");
+ } else {
+ result.AppendErrorWithFormat(
+ "Argument must be either 'enable' or 'disable'");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ runtime->SetBreakAllKernels(do_break, m_exe_ctx.GetTargetSP());
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+class CommandObjectRenderScriptRuntimeReductionBreakpoint
+ : public CommandObjectMultiword {
+public:
+ CommandObjectRenderScriptRuntimeReductionBreakpoint(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "renderscript reduction breakpoint",
+ "Commands that manipulate breakpoints on "
+ "renderscript general reductions.",
+ nullptr) {
+ LoadSubCommand(
+ "set", CommandObjectSP(
+ new CommandObjectRenderScriptRuntimeReductionBreakpointSet(
+ interpreter)));
+ }
+
+ ~CommandObjectRenderScriptRuntimeReductionBreakpoint() override = default;
+};
+
+class CommandObjectRenderScriptRuntimeKernelCoordinate
+ : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeKernelCoordinate(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "renderscript kernel coordinate",
+ "Shows the (x,y,z) coordinate of the current kernel invocation.",
+ "renderscript kernel coordinate",
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused) {}
+
+ ~CommandObjectRenderScriptRuntimeKernelCoordinate() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ RSCoordinate coord{};
+ bool success = RenderScriptRuntime::GetKernelCoordinate(
+ coord, m_exe_ctx.GetThreadPtr());
+ Stream &stream = result.GetOutputStream();
+
+ if (success) {
+ stream.Printf("Coordinate: " FMT_COORD, coord.x, coord.y, coord.z);
+ stream.EOL();
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ stream.Printf("Error: Coordinate could not be found.");
+ stream.EOL();
+ result.SetStatus(eReturnStatusFailed);
+ }
+ return true;
+ }
+};
+
+class CommandObjectRenderScriptRuntimeKernelBreakpoint
+ : public CommandObjectMultiword {
+public:
+ CommandObjectRenderScriptRuntimeKernelBreakpoint(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "renderscript kernel",
+ "Commands that generate breakpoints on renderscript kernels.",
+ nullptr) {
+ LoadSubCommand(
+ "set",
+ CommandObjectSP(new CommandObjectRenderScriptRuntimeKernelBreakpointSet(
+ interpreter)));
+ LoadSubCommand(
+ "all",
+ CommandObjectSP(new CommandObjectRenderScriptRuntimeKernelBreakpointAll(
+ interpreter)));
+ }
+
+ ~CommandObjectRenderScriptRuntimeKernelBreakpoint() override = default;
+};
+
+class CommandObjectRenderScriptRuntimeKernel : public CommandObjectMultiword {
+public:
+ CommandObjectRenderScriptRuntimeKernel(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "renderscript kernel",
+ "Commands that deal with RenderScript kernels.",
+ nullptr) {
+ LoadSubCommand(
+ "list", CommandObjectSP(new CommandObjectRenderScriptRuntimeKernelList(
+ interpreter)));
+ LoadSubCommand(
+ "coordinate",
+ CommandObjectSP(
+ new CommandObjectRenderScriptRuntimeKernelCoordinate(interpreter)));
+ LoadSubCommand(
+ "breakpoint",
+ CommandObjectSP(
+ new CommandObjectRenderScriptRuntimeKernelBreakpoint(interpreter)));
+ }
+
+ ~CommandObjectRenderScriptRuntimeKernel() override = default;
+};
+
+class CommandObjectRenderScriptRuntimeContextDump : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeContextDump(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "renderscript context dump",
+ "Dumps renderscript context information.",
+ "renderscript context dump",
+ eCommandRequiresProcess |
+ eCommandProcessMustBeLaunched) {}
+
+ ~CommandObjectRenderScriptRuntimeContextDump() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ RenderScriptRuntime *runtime = llvm::cast<RenderScriptRuntime>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+ runtime->DumpContexts(result.GetOutputStream());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+static constexpr OptionDefinition g_renderscript_runtime_alloc_dump_options[] = {
+ {LLDB_OPT_SET_1, false, "file", 'f', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgTypeFilename,
+ "Print results to specified file instead of command line."}};
+
+class CommandObjectRenderScriptRuntimeContext : public CommandObjectMultiword {
+public:
+ CommandObjectRenderScriptRuntimeContext(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "renderscript context",
+ "Commands that deal with RenderScript contexts.",
+ nullptr) {
+ LoadSubCommand(
+ "dump", CommandObjectSP(new CommandObjectRenderScriptRuntimeContextDump(
+ interpreter)));
+ }
+
+ ~CommandObjectRenderScriptRuntimeContext() override = default;
+};
+
+class CommandObjectRenderScriptRuntimeAllocationDump
+ : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeAllocationDump(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "renderscript allocation dump",
+ "Displays the contents of a particular allocation",
+ "renderscript allocation dump <ID>",
+ eCommandRequiresProcess |
+ eCommandProcessMustBeLaunched),
+ m_options() {}
+
+ ~CommandObjectRenderScriptRuntimeAllocationDump() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *exe_ctx) override {
+ Status err;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'f':
+ m_outfile.SetFile(option_arg, FileSpec::Style::native);
+ FileSystem::Instance().Resolve(m_outfile);
+ if (FileSystem::Instance().Exists(m_outfile)) {
+ m_outfile.Clear();
+ err.SetErrorStringWithFormat("file already exists: '%s'",
+ option_arg.str().c_str());
+ }
+ break;
+ default:
+ err.SetErrorStringWithFormat("unrecognized option '%c'", short_option);
+ break;
+ }
+ return err;
+ }
+
+ void OptionParsingStarting(ExecutionContext *exe_ctx) override {
+ m_outfile.Clear();
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_renderscript_runtime_alloc_dump_options);
+ }
+
+ FileSpec m_outfile;
+ };
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ if (argc < 1) {
+ result.AppendErrorWithFormat("'%s' takes 1 argument, an allocation ID. "
+ "As well as an optional -f argument",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ RenderScriptRuntime *runtime = static_cast<RenderScriptRuntime *>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+
+ const char *id_cstr = command.GetArgumentAtIndex(0);
+ bool success = false;
+ const uint32_t id =
+ StringConvert::ToUInt32(id_cstr, UINT32_MAX, 0, &success);
+ if (!success) {
+ result.AppendErrorWithFormat("invalid allocation id argument '%s'",
+ id_cstr);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ Stream *output_strm = nullptr;
+ StreamFile outfile_stream;
+ const FileSpec &outfile_spec =
+ m_options.m_outfile; // Dump allocation to file instead
+ if (outfile_spec) {
+ // Open output file
+ std::string path = outfile_spec.GetPath();
+ auto error = FileSystem::Instance().Open(
+ outfile_stream.GetFile(), outfile_spec,
+ File::eOpenOptionWrite | File::eOpenOptionCanCreate);
+ if (error.Success()) {
+ output_strm = &outfile_stream;
+ result.GetOutputStream().Printf("Results written to '%s'",
+ path.c_str());
+ result.GetOutputStream().EOL();
+ } else {
+ result.AppendErrorWithFormat("Couldn't open file '%s'", path.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ } else
+ output_strm = &result.GetOutputStream();
+
+ assert(output_strm != nullptr);
+ bool dumped =
+ runtime->DumpAllocation(*output_strm, m_exe_ctx.GetFramePtr(), id);
+
+ if (dumped)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+
+ return true;
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+static constexpr OptionDefinition g_renderscript_runtime_alloc_list_options[] = {
+ {LLDB_OPT_SET_1, false, "id", 'i', OptionParser::eRequiredArgument, nullptr,
+ {}, 0, eArgTypeIndex,
+ "Only show details of a single allocation with specified id."}};
+
+class CommandObjectRenderScriptRuntimeAllocationList
+ : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeAllocationList(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "renderscript allocation list",
+ "List renderscript allocations and their information.",
+ "renderscript allocation list",
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched),
+ m_options() {}
+
+ ~CommandObjectRenderScriptRuntimeAllocationList() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options(), m_id(0) {}
+
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *exe_ctx) override {
+ Status err;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'i':
+ if (option_arg.getAsInteger(0, m_id))
+ err.SetErrorStringWithFormat("invalid integer value for option '%c'",
+ short_option);
+ break;
+ default:
+ err.SetErrorStringWithFormat("unrecognized option '%c'", short_option);
+ break;
+ }
+ return err;
+ }
+
+ void OptionParsingStarting(ExecutionContext *exe_ctx) override { m_id = 0; }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_renderscript_runtime_alloc_list_options);
+ }
+
+ uint32_t m_id;
+ };
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ RenderScriptRuntime *runtime = static_cast<RenderScriptRuntime *>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+ runtime->ListAllocations(result.GetOutputStream(), m_exe_ctx.GetFramePtr(),
+ m_options.m_id);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+
+private:
+ CommandOptions m_options;
+};
+
+class CommandObjectRenderScriptRuntimeAllocationLoad
+ : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeAllocationLoad(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "renderscript allocation load",
+ "Loads renderscript allocation contents from a file.",
+ "renderscript allocation load <ID> <filename>",
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched) {}
+
+ ~CommandObjectRenderScriptRuntimeAllocationLoad() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ if (argc != 2) {
+ result.AppendErrorWithFormat(
+ "'%s' takes 2 arguments, an allocation ID and filename to read from.",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ RenderScriptRuntime *runtime = static_cast<RenderScriptRuntime *>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+
+ const char *id_cstr = command.GetArgumentAtIndex(0);
+ bool success = false;
+ const uint32_t id =
+ StringConvert::ToUInt32(id_cstr, UINT32_MAX, 0, &success);
+ if (!success) {
+ result.AppendErrorWithFormat("invalid allocation id argument '%s'",
+ id_cstr);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const char *path = command.GetArgumentAtIndex(1);
+ bool loaded = runtime->LoadAllocation(result.GetOutputStream(), id, path,
+ m_exe_ctx.GetFramePtr());
+
+ if (loaded)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+
+ return true;
+ }
+};
+
+class CommandObjectRenderScriptRuntimeAllocationSave
+ : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeAllocationSave(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "renderscript allocation save",
+ "Write renderscript allocation contents to a file.",
+ "renderscript allocation save <ID> <filename>",
+ eCommandRequiresProcess |
+ eCommandProcessMustBeLaunched) {}
+
+ ~CommandObjectRenderScriptRuntimeAllocationSave() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ if (argc != 2) {
+ result.AppendErrorWithFormat(
+ "'%s' takes 2 arguments, an allocation ID and filename to read from.",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ RenderScriptRuntime *runtime = static_cast<RenderScriptRuntime *>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+
+ const char *id_cstr = command.GetArgumentAtIndex(0);
+ bool success = false;
+ const uint32_t id =
+ StringConvert::ToUInt32(id_cstr, UINT32_MAX, 0, &success);
+ if (!success) {
+ result.AppendErrorWithFormat("invalid allocation id argument '%s'",
+ id_cstr);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const char *path = command.GetArgumentAtIndex(1);
+ bool saved = runtime->SaveAllocation(result.GetOutputStream(), id, path,
+ m_exe_ctx.GetFramePtr());
+
+ if (saved)
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ else
+ result.SetStatus(eReturnStatusFailed);
+
+ return true;
+ }
+};
+
+class CommandObjectRenderScriptRuntimeAllocationRefresh
+ : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeAllocationRefresh(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "renderscript allocation refresh",
+ "Recomputes the details of all allocations.",
+ "renderscript allocation refresh",
+ eCommandRequiresProcess |
+ eCommandProcessMustBeLaunched) {}
+
+ ~CommandObjectRenderScriptRuntimeAllocationRefresh() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ RenderScriptRuntime *runtime = static_cast<RenderScriptRuntime *>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+
+ bool success = runtime->RecomputeAllAllocations(result.GetOutputStream(),
+ m_exe_ctx.GetFramePtr());
+
+ if (success) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ } else {
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+};
+
+class CommandObjectRenderScriptRuntimeAllocation
+ : public CommandObjectMultiword {
+public:
+ CommandObjectRenderScriptRuntimeAllocation(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "renderscript allocation",
+ "Commands that deal with RenderScript allocations.", nullptr) {
+ LoadSubCommand(
+ "list",
+ CommandObjectSP(
+ new CommandObjectRenderScriptRuntimeAllocationList(interpreter)));
+ LoadSubCommand(
+ "dump",
+ CommandObjectSP(
+ new CommandObjectRenderScriptRuntimeAllocationDump(interpreter)));
+ LoadSubCommand(
+ "save",
+ CommandObjectSP(
+ new CommandObjectRenderScriptRuntimeAllocationSave(interpreter)));
+ LoadSubCommand(
+ "load",
+ CommandObjectSP(
+ new CommandObjectRenderScriptRuntimeAllocationLoad(interpreter)));
+ LoadSubCommand(
+ "refresh",
+ CommandObjectSP(new CommandObjectRenderScriptRuntimeAllocationRefresh(
+ interpreter)));
+ }
+
+ ~CommandObjectRenderScriptRuntimeAllocation() override = default;
+};
+
+class CommandObjectRenderScriptRuntimeStatus : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptRuntimeStatus(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "renderscript status",
+ "Displays current RenderScript runtime status.",
+ "renderscript status",
+ eCommandRequiresProcess |
+ eCommandProcessMustBeLaunched) {}
+
+ ~CommandObjectRenderScriptRuntimeStatus() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ RenderScriptRuntime *runtime = llvm::cast<RenderScriptRuntime>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+ runtime->DumpStatus(result.GetOutputStream());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+class CommandObjectRenderScriptRuntimeReduction
+ : public CommandObjectMultiword {
+public:
+ CommandObjectRenderScriptRuntimeReduction(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "renderscript reduction",
+ "Commands that handle general reduction kernels",
+ nullptr) {
+ LoadSubCommand(
+ "breakpoint",
+ CommandObjectSP(new CommandObjectRenderScriptRuntimeReductionBreakpoint(
+ interpreter)));
+ }
+ ~CommandObjectRenderScriptRuntimeReduction() override = default;
+};
+
+class CommandObjectRenderScriptRuntime : public CommandObjectMultiword {
+public:
+ CommandObjectRenderScriptRuntime(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "renderscript",
+ "Commands for operating on the RenderScript runtime.",
+ "renderscript <subcommand> [<subcommand-options>]") {
+ LoadSubCommand(
+ "module", CommandObjectSP(
+ new CommandObjectRenderScriptRuntimeModule(interpreter)));
+ LoadSubCommand(
+ "status", CommandObjectSP(
+ new CommandObjectRenderScriptRuntimeStatus(interpreter)));
+ LoadSubCommand(
+ "kernel", CommandObjectSP(
+ new CommandObjectRenderScriptRuntimeKernel(interpreter)));
+ LoadSubCommand("context",
+ CommandObjectSP(new CommandObjectRenderScriptRuntimeContext(
+ interpreter)));
+ LoadSubCommand(
+ "allocation",
+ CommandObjectSP(
+ new CommandObjectRenderScriptRuntimeAllocation(interpreter)));
+ LoadSubCommand("scriptgroup",
+ NewCommandObjectRenderScriptScriptGroup(interpreter));
+ LoadSubCommand(
+ "reduction",
+ CommandObjectSP(
+ new CommandObjectRenderScriptRuntimeReduction(interpreter)));
+ }
+
+ ~CommandObjectRenderScriptRuntime() override = default;
+};
+
+void RenderScriptRuntime::Initiate() { assert(!m_initiated); }
+
+RenderScriptRuntime::RenderScriptRuntime(Process *process)
+ : lldb_private::CPPLanguageRuntime(process), m_initiated(false),
+ m_debuggerPresentFlagged(false), m_breakAllKernels(false),
+ m_ir_passes(nullptr) {
+ ModulesDidLoad(process->GetTarget().GetImages());
+}
+
+lldb::CommandObjectSP RenderScriptRuntime::GetCommandObject(
+ lldb_private::CommandInterpreter &interpreter) {
+ return CommandObjectSP(new CommandObjectRenderScriptRuntime(interpreter));
+}
+
+RenderScriptRuntime::~RenderScriptRuntime() = default;
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h
new file mode 100644
index 000000000000..3923221d4302
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h
@@ -0,0 +1,587 @@
+//===-- RenderScriptRuntime.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 liblldb_RenderScriptRuntime_h_
+#define liblldb_RenderScriptRuntime_h_
+
+#include <array>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Expression/LLVMUserExpression.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/lldb-private.h"
+
+#include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h"
+
+namespace lldb_private {
+namespace lldb_renderscript {
+
+typedef uint32_t RSSlot;
+class RSModuleDescriptor;
+struct RSGlobalDescriptor;
+struct RSKernelDescriptor;
+struct RSReductionDescriptor;
+struct RSScriptGroupDescriptor;
+
+typedef std::shared_ptr<RSModuleDescriptor> RSModuleDescriptorSP;
+typedef std::shared_ptr<RSGlobalDescriptor> RSGlobalDescriptorSP;
+typedef std::shared_ptr<RSKernelDescriptor> RSKernelDescriptorSP;
+typedef std::shared_ptr<RSScriptGroupDescriptor> RSScriptGroupDescriptorSP;
+
+struct RSCoordinate {
+ uint32_t x, y, z;
+
+ RSCoordinate() : x(), y(), z(){};
+
+ bool operator==(const lldb_renderscript::RSCoordinate &rhs) {
+ return x == rhs.x && y == rhs.y && z == rhs.z;
+ }
+};
+
+// Breakpoint Resolvers decide where a breakpoint is placed, so having our own
+// allows us to limit the search scope to RS kernel modules. As well as check
+// for .expand kernels as a fallback.
+class RSBreakpointResolver : public BreakpointResolver {
+public:
+ RSBreakpointResolver(Breakpoint *bp, ConstString name)
+ : BreakpointResolver(bp, BreakpointResolver::NameResolver),
+ m_kernel_name(name) {}
+
+ void GetDescription(Stream *strm) override {
+ if (strm)
+ strm->Printf("RenderScript kernel breakpoint for '%s'",
+ m_kernel_name.AsCString());
+ }
+
+ void Dump(Stream *s) const override {}
+
+ Searcher::CallbackReturn SearchCallback(SearchFilter &filter,
+ SymbolContext &context, Address *addr,
+ bool containing) override;
+
+ lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; }
+
+ lldb::BreakpointResolverSP
+ CopyForBreakpoint(Breakpoint &breakpoint) override {
+ lldb::BreakpointResolverSP ret_sp(
+ new RSBreakpointResolver(&breakpoint, m_kernel_name));
+ return ret_sp;
+ }
+
+protected:
+ ConstString m_kernel_name;
+};
+
+class RSReduceBreakpointResolver : public BreakpointResolver {
+public:
+ enum ReduceKernelTypeFlags {
+ eKernelTypeAll = ~(0),
+ eKernelTypeNone = 0,
+ eKernelTypeAccum = (1 << 0),
+ eKernelTypeInit = (1 << 1),
+ eKernelTypeComb = (1 << 2),
+ eKernelTypeOutC = (1 << 3),
+ eKernelTypeHalter = (1 << 4)
+ };
+
+ RSReduceBreakpointResolver(
+ Breakpoint *breakpoint, ConstString reduce_name,
+ std::vector<lldb_renderscript::RSModuleDescriptorSP> *rs_modules,
+ int kernel_types = eKernelTypeAll)
+ : BreakpointResolver(breakpoint, BreakpointResolver::NameResolver),
+ m_reduce_name(reduce_name), m_rsmodules(rs_modules),
+ m_kernel_types(kernel_types) {
+ // The reduce breakpoint resolver handles adding breakpoints for named
+ // reductions.
+ // Breakpoints will be resolved for all constituent kernels in the named
+ // reduction
+ }
+
+ void GetDescription(Stream *strm) override {
+ if (strm)
+ strm->Printf("RenderScript reduce breakpoint for '%s'",
+ m_reduce_name.AsCString());
+ }
+
+ void Dump(Stream *s) const override {}
+
+ Searcher::CallbackReturn SearchCallback(SearchFilter &filter,
+ SymbolContext &context, Address *addr,
+ bool containing) override;
+
+ lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; }
+
+ lldb::BreakpointResolverSP
+ CopyForBreakpoint(Breakpoint &breakpoint) override {
+ lldb::BreakpointResolverSP ret_sp(new RSReduceBreakpointResolver(
+ &breakpoint, m_reduce_name, m_rsmodules, m_kernel_types));
+ return ret_sp;
+ }
+
+private:
+ ConstString m_reduce_name; // The name of the reduction
+ std::vector<lldb_renderscript::RSModuleDescriptorSP> *m_rsmodules;
+ int m_kernel_types;
+};
+
+struct RSKernelDescriptor {
+public:
+ RSKernelDescriptor(const RSModuleDescriptor *module, llvm::StringRef name,
+ uint32_t slot)
+ : m_module(module), m_name(name), m_slot(slot) {}
+
+ void Dump(Stream &strm) const;
+
+ const RSModuleDescriptor *m_module;
+ ConstString m_name;
+ RSSlot m_slot;
+};
+
+struct RSGlobalDescriptor {
+public:
+ RSGlobalDescriptor(const RSModuleDescriptor *module, llvm::StringRef name)
+ : m_module(module), m_name(name) {}
+
+ void Dump(Stream &strm) const;
+
+ const RSModuleDescriptor *m_module;
+ ConstString m_name;
+};
+
+struct RSReductionDescriptor {
+ RSReductionDescriptor(const RSModuleDescriptor *module, uint32_t sig,
+ uint32_t accum_data_size, llvm::StringRef name,
+ llvm::StringRef init_name, llvm::StringRef accum_name,
+ llvm::StringRef comb_name, llvm::StringRef outc_name,
+ llvm::StringRef halter_name = ".")
+ : m_module(module), m_reduce_name(name), m_init_name(init_name),
+ m_accum_name(accum_name), m_comb_name(comb_name),
+ m_outc_name(outc_name), m_halter_name(halter_name) {
+ // TODO Check whether the combiner is an autogenerated name, and track
+ // this
+ }
+
+ void Dump(Stream &strm) const;
+
+ const RSModuleDescriptor *m_module;
+ ConstString m_reduce_name; // This is the name given to the general reduction
+ // as a group as passed to pragma
+ // reduce(m_reduce_name). There is no kernel function with this name
+ ConstString m_init_name; // The name of the initializer name. "." if no
+ // initializer given
+ ConstString m_accum_name; // The accumulator function name. "." if not given
+ ConstString m_comb_name; // The name of the combiner function. If this was not
+ // given, a name is generated by the
+ // compiler. TODO
+ ConstString m_outc_name; // The name of the outconverter
+
+ ConstString m_halter_name; // The name of the halter function. XXX This is not
+ // yet specified by the RenderScript
+ // compiler or runtime, and its semantics and existence is still under
+ // discussion by the
+ // RenderScript Contributors
+ RSSlot m_accum_sig; // metatdata signature for this reduction (bitwise mask of
+ // type information (see
+ // libbcc/include/bcinfo/MetadataExtractor.h
+ uint32_t m_accum_data_size; // Data size of the accumulator function input
+ bool m_comb_name_generated; // Was the combiner name generated by the compiler
+};
+
+class RSModuleDescriptor {
+ std::string m_slang_version;
+ std::string m_bcc_version;
+
+ bool ParseVersionInfo(llvm::StringRef *, size_t n_lines);
+
+ bool ParseExportForeachCount(llvm::StringRef *, size_t n_lines);
+
+ bool ParseExportVarCount(llvm::StringRef *, size_t n_lines);
+
+ bool ParseExportReduceCount(llvm::StringRef *, size_t n_lines);
+
+ bool ParseBuildChecksum(llvm::StringRef *, size_t n_lines);
+
+ bool ParsePragmaCount(llvm::StringRef *, size_t n_lines);
+
+public:
+ RSModuleDescriptor(const lldb::ModuleSP &module) : m_module(module) {}
+
+ ~RSModuleDescriptor() = default;
+
+ bool ParseRSInfo();
+
+ void Dump(Stream &strm) const;
+
+ void WarnIfVersionMismatch(Stream *s) const;
+
+ const lldb::ModuleSP m_module;
+ std::vector<RSKernelDescriptor> m_kernels;
+ std::vector<RSGlobalDescriptor> m_globals;
+ std::vector<RSReductionDescriptor> m_reductions;
+ std::map<std::string, std::string> m_pragmas;
+ std::string m_resname;
+};
+
+struct RSScriptGroupDescriptor {
+ struct Kernel {
+ ConstString m_name;
+ lldb::addr_t m_addr;
+ };
+ ConstString m_name;
+ std::vector<Kernel> m_kernels;
+};
+
+typedef std::vector<RSScriptGroupDescriptorSP> RSScriptGroupList;
+
+class RSScriptGroupBreakpointResolver : public BreakpointResolver {
+public:
+ RSScriptGroupBreakpointResolver(Breakpoint *bp, ConstString name,
+ const RSScriptGroupList &groups,
+ bool stop_on_all)
+ : BreakpointResolver(bp, BreakpointResolver::NameResolver),
+ m_group_name(name), m_script_groups(groups),
+ m_stop_on_all(stop_on_all) {}
+
+ void GetDescription(Stream *strm) override {
+ if (strm)
+ strm->Printf("RenderScript ScriptGroup breakpoint for '%s'",
+ m_group_name.AsCString());
+ }
+
+ void Dump(Stream *s) const override {}
+
+ Searcher::CallbackReturn SearchCallback(SearchFilter &filter,
+ SymbolContext &context, Address *addr,
+ bool containing) override;
+
+ lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; }
+
+ lldb::BreakpointResolverSP
+ CopyForBreakpoint(Breakpoint &breakpoint) override {
+ lldb::BreakpointResolverSP ret_sp(new RSScriptGroupBreakpointResolver(
+ &breakpoint, m_group_name, m_script_groups, m_stop_on_all));
+ return ret_sp;
+ }
+
+protected:
+ const RSScriptGroupDescriptorSP
+ FindScriptGroup(ConstString name) const {
+ for (auto sg : m_script_groups) {
+ if (ConstString::Compare(sg->m_name, name) == 0)
+ return sg;
+ }
+ return RSScriptGroupDescriptorSP();
+ }
+
+ ConstString m_group_name;
+ const RSScriptGroupList &m_script_groups;
+ bool m_stop_on_all;
+};
+} // namespace lldb_renderscript
+
+class RenderScriptRuntime : public lldb_private::CPPLanguageRuntime {
+public:
+ enum ModuleKind {
+ eModuleKindIgnored,
+ eModuleKindLibRS,
+ eModuleKindDriver,
+ eModuleKindImpl,
+ eModuleKindKernelObj
+ };
+
+ ~RenderScriptRuntime() override;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::LanguageRuntime *
+ CreateInstance(Process *process, lldb::LanguageType language);
+
+ static lldb::CommandObjectSP
+ GetCommandObject(CommandInterpreter &interpreter);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static char ID;
+
+ bool isA(const void *ClassID) const override {
+ return ClassID == &ID || CPPLanguageRuntime::isA(ClassID);
+ }
+
+ static bool classof(const LanguageRuntime *runtime) {
+ return runtime->isA(&ID);
+ }
+
+ static bool IsRenderScriptModule(const lldb::ModuleSP &module_sp);
+
+ static ModuleKind GetModuleKind(const lldb::ModuleSP &module_sp);
+
+ static void ModulesDidLoad(const lldb::ProcessSP &process_sp,
+ const ModuleList &module_list);
+
+ bool GetDynamicTypeAndAddress(ValueObject &in_value,
+ lldb::DynamicValueType use_dynamic,
+ TypeAndOrName &class_type_or_name,
+ Address &address,
+ Value::ValueType &value_type) override;
+
+ TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name,
+ ValueObject &static_value) override;
+
+ bool CouldHaveDynamicValue(ValueObject &in_value) override;
+
+ lldb::BreakpointResolverSP CreateExceptionResolver(Breakpoint *bp,
+ bool catch_bp,
+ bool throw_bp) override;
+
+ bool LoadModule(const lldb::ModuleSP &module_sp);
+
+ void DumpModules(Stream &strm) const;
+
+ void DumpContexts(Stream &strm) const;
+
+ void DumpKernels(Stream &strm) const;
+
+ bool DumpAllocation(Stream &strm, StackFrame *frame_ptr, const uint32_t id);
+
+ void ListAllocations(Stream &strm, StackFrame *frame_ptr,
+ const uint32_t index);
+
+ bool RecomputeAllAllocations(Stream &strm, StackFrame *frame_ptr);
+
+ bool PlaceBreakpointOnKernel(
+ lldb::TargetSP target, Stream &messages, const char *name,
+ const lldb_renderscript::RSCoordinate *coords = nullptr);
+
+ bool PlaceBreakpointOnReduction(
+ lldb::TargetSP target, Stream &messages, const char *reduce_name,
+ const lldb_renderscript::RSCoordinate *coords = nullptr,
+ int kernel_types = ~(0));
+
+ bool PlaceBreakpointOnScriptGroup(lldb::TargetSP target, Stream &strm,
+ ConstString name, bool stop_on_all);
+
+ void SetBreakAllKernels(bool do_break, lldb::TargetSP target);
+
+ void DumpStatus(Stream &strm) const;
+
+ void ModulesDidLoad(const ModuleList &module_list) override;
+
+ bool LoadAllocation(Stream &strm, const uint32_t alloc_id,
+ const char *filename, StackFrame *frame_ptr);
+
+ bool SaveAllocation(Stream &strm, const uint32_t alloc_id,
+ const char *filename, StackFrame *frame_ptr);
+
+ void Update();
+
+ void Initiate();
+
+ const lldb_renderscript::RSScriptGroupList &GetScriptGroups() const {
+ return m_scriptGroups;
+ };
+
+ bool IsKnownKernel(ConstString name) {
+ for (const auto &module : m_rsmodules)
+ for (const auto &kernel : module->m_kernels)
+ if (kernel.m_name == name)
+ return true;
+ return false;
+ }
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ static bool GetKernelCoordinate(lldb_renderscript::RSCoordinate &coord,
+ Thread *thread_ptr);
+
+ bool ResolveKernelName(lldb::addr_t kernel_address, ConstString &name);
+
+protected:
+ struct ScriptDetails;
+ struct AllocationDetails;
+ struct Element;
+
+ lldb_renderscript::RSScriptGroupList m_scriptGroups;
+
+ void InitSearchFilter(lldb::TargetSP target) {
+ if (!m_filtersp)
+ m_filtersp.reset(new SearchFilterForUnconstrainedSearches(target));
+ }
+
+ void FixupScriptDetails(lldb_renderscript::RSModuleDescriptorSP rsmodule_sp);
+
+ void LoadRuntimeHooks(lldb::ModuleSP module, ModuleKind kind);
+
+ bool RefreshAllocation(AllocationDetails *alloc, StackFrame *frame_ptr);
+
+ bool EvalRSExpression(const char *expression, StackFrame *frame_ptr,
+ uint64_t *result);
+
+ lldb::BreakpointSP CreateScriptGroupBreakpoint(ConstString name,
+ bool multi);
+
+ lldb::BreakpointSP CreateKernelBreakpoint(ConstString name);
+
+ lldb::BreakpointSP CreateReductionBreakpoint(ConstString name,
+ int kernel_types);
+
+ void BreakOnModuleKernels(
+ const lldb_renderscript::RSModuleDescriptorSP rsmodule_sp);
+
+ struct RuntimeHook;
+ typedef void (RenderScriptRuntime::*CaptureStateFn)(
+ RuntimeHook *hook_info,
+ ExecutionContext &context); // Please do this!
+
+ struct HookDefn {
+ const char *name;
+ const char *symbol_name_m32; // mangled name for the 32 bit architectures
+ const char *symbol_name_m64; // mangled name for the 64 bit archs
+ uint32_t version;
+ ModuleKind kind;
+ CaptureStateFn grabber;
+ };
+
+ struct RuntimeHook {
+ lldb::addr_t address;
+ const HookDefn *defn;
+ lldb::BreakpointSP bp_sp;
+ };
+
+ typedef std::shared_ptr<RuntimeHook> RuntimeHookSP;
+
+ lldb::ModuleSP m_libRS;
+ lldb::ModuleSP m_libRSDriver;
+ lldb::ModuleSP m_libRSCpuRef;
+ std::vector<lldb_renderscript::RSModuleDescriptorSP> m_rsmodules;
+
+ std::vector<std::unique_ptr<ScriptDetails>> m_scripts;
+ std::vector<std::unique_ptr<AllocationDetails>> m_allocations;
+
+ std::map<lldb::addr_t, lldb_renderscript::RSModuleDescriptorSP>
+ m_scriptMappings;
+ std::map<lldb::addr_t, RuntimeHookSP> m_runtimeHooks;
+ std::map<lldb::user_id_t, std::unique_ptr<lldb_renderscript::RSCoordinate>>
+ m_conditional_breaks;
+
+ lldb::SearchFilterSP
+ m_filtersp; // Needed to create breakpoints through Target API
+
+ bool m_initiated;
+ bool m_debuggerPresentFlagged;
+ bool m_breakAllKernels;
+ static const HookDefn s_runtimeHookDefns[];
+ static const size_t s_runtimeHookCount;
+ LLVMUserExpression::IRPasses *m_ir_passes;
+
+private:
+ RenderScriptRuntime(Process *process); // Call CreateInstance instead.
+
+ static bool HookCallback(void *baton, StoppointCallbackContext *ctx,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id);
+
+ static bool KernelBreakpointHit(void *baton, StoppointCallbackContext *ctx,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id);
+
+ void HookCallback(RuntimeHook *hook_info, ExecutionContext &context);
+
+ // Callback function when 'debugHintScriptGroup2' executes on the target.
+ void CaptureDebugHintScriptGroup2(RuntimeHook *hook_info,
+ ExecutionContext &context);
+
+ void CaptureScriptInit(RuntimeHook *hook_info, ExecutionContext &context);
+
+ void CaptureAllocationInit(RuntimeHook *hook_info, ExecutionContext &context);
+
+ void CaptureAllocationDestroy(RuntimeHook *hook_info,
+ ExecutionContext &context);
+
+ void CaptureSetGlobalVar(RuntimeHook *hook_info, ExecutionContext &context);
+
+ void CaptureScriptInvokeForEachMulti(RuntimeHook *hook_info,
+ ExecutionContext &context);
+
+ AllocationDetails *FindAllocByID(Stream &strm, const uint32_t alloc_id);
+
+ std::shared_ptr<uint8_t> GetAllocationData(AllocationDetails *alloc,
+ StackFrame *frame_ptr);
+
+ void SetElementSize(Element &elem);
+
+ static bool GetFrameVarAsUnsigned(const lldb::StackFrameSP,
+ const char *var_name, uint64_t &val);
+
+ void FindStructTypeName(Element &elem, StackFrame *frame_ptr);
+
+ size_t PopulateElementHeaders(const std::shared_ptr<uint8_t> header_buffer,
+ size_t offset, const Element &elem);
+
+ size_t CalculateElementHeaderSize(const Element &elem);
+
+ void SetConditional(lldb::BreakpointSP bp, lldb_private::Stream &messages,
+ const lldb_renderscript::RSCoordinate &coord);
+ //
+ // Helper functions for jitting the runtime
+ //
+
+ bool JITDataPointer(AllocationDetails *alloc, StackFrame *frame_ptr,
+ uint32_t x = 0, uint32_t y = 0, uint32_t z = 0);
+
+ bool JITTypePointer(AllocationDetails *alloc, StackFrame *frame_ptr);
+
+ bool JITTypePacked(AllocationDetails *alloc, StackFrame *frame_ptr);
+
+ bool JITElementPacked(Element &elem, const lldb::addr_t context,
+ StackFrame *frame_ptr);
+
+ bool JITAllocationSize(AllocationDetails *alloc, StackFrame *frame_ptr);
+
+ bool JITSubelements(Element &elem, const lldb::addr_t context,
+ StackFrame *frame_ptr);
+
+ bool JITAllocationStride(AllocationDetails *alloc, StackFrame *frame_ptr);
+
+ // Search for a script detail object using a target address.
+ // If a script does not currently exist this function will return nullptr.
+ // If 'create' is true and there is no previous script with this address,
+ // then a new Script detail object will be created for this address and
+ // returned.
+ ScriptDetails *LookUpScript(lldb::addr_t address, bool create);
+
+ // Search for a previously saved allocation detail object using a target
+ // address.
+ // If an allocation does not exist for this address then nullptr will be
+ // returned.
+ AllocationDetails *LookUpAllocation(lldb::addr_t address);
+
+ // Creates a new allocation with the specified address assigning a new ID and
+ // removes
+ // any previous stored allocation which has the same address.
+ AllocationDetails *CreateAllocation(lldb::addr_t address);
+
+ bool GetOverrideExprOptions(clang::TargetOptions &prototype) override;
+
+ bool GetIRPasses(LLVMUserExpression::IRPasses &passes) override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_RenderScriptRuntime_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.cpp
new file mode 100644
index 000000000000..45d0d028d047
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.cpp
@@ -0,0 +1,160 @@
+//===-- RenderScriptScriptGroup.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 "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Status.h"
+
+#include "RenderScriptRuntime.h"
+#include "RenderScriptScriptGroup.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_renderscript;
+
+class CommandObjectRenderScriptScriptGroupBreakpointSet
+ : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptScriptGroupBreakpointSet(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "renderscript scriptgroup breakpoint set",
+ "Place a breakpoint on all kernels forming a script group.",
+ "renderscript scriptgroup breakpoint set <group_name>",
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched) {}
+
+ ~CommandObjectRenderScriptScriptGroupBreakpointSet() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Stream &stream = result.GetOutputStream();
+ RenderScriptRuntime *runtime = static_cast<RenderScriptRuntime *>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+ assert(runtime);
+ auto &target = m_exe_ctx.GetTargetSP();
+ bool stop_on_all = false;
+ const llvm::StringRef long_stop_all("--stop-on-all"), short_stop_all("-a");
+ std::vector<ConstString> sites;
+ sites.reserve(command.GetArgumentCount());
+ for (size_t i = 0; i < command.GetArgumentCount(); ++i) {
+ const auto arg = command.GetArgumentAtIndex(i);
+ if (long_stop_all == arg || short_stop_all == arg)
+ stop_on_all = true;
+ else
+ sites.push_back(ConstString(arg));
+ }
+ for (const auto &name : sites) {
+ runtime->PlaceBreakpointOnScriptGroup(target, stream, name, stop_on_all);
+ }
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+class CommandObjectRenderScriptScriptGroupBreakpoint
+ : public CommandObjectMultiword {
+public:
+ CommandObjectRenderScriptScriptGroupBreakpoint(
+ CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "renderscript scriptgroup breakpoint",
+ "Renderscript scriptgroup breakpoint interaction.",
+ "renderscript scriptgroup breakpoint set [--stop-on-all/-a]"
+ "<scriptgroup name> ...",
+ eCommandRequiresProcess | eCommandProcessMustBeLaunched) {
+ LoadSubCommand(
+ "set",
+ CommandObjectSP(new CommandObjectRenderScriptScriptGroupBreakpointSet(
+ interpreter)));
+ }
+
+ ~CommandObjectRenderScriptScriptGroupBreakpoint() override = default;
+};
+
+class CommandObjectRenderScriptScriptGroupList : public CommandObjectParsed {
+public:
+ CommandObjectRenderScriptScriptGroupList(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "renderscript scriptgroup list",
+ "List all currently discovered script groups.",
+ "renderscript scriptgroup list",
+ eCommandRequiresProcess |
+ eCommandProcessMustBeLaunched) {}
+
+ ~CommandObjectRenderScriptScriptGroupList() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ Stream &stream = result.GetOutputStream();
+ RenderScriptRuntime *runtime = static_cast<RenderScriptRuntime *>(
+ m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(
+ eLanguageTypeExtRenderScript));
+ assert(runtime);
+ const RSScriptGroupList &groups = runtime->GetScriptGroups();
+ // print script group count
+ stream.Printf("%" PRIu64 " script %s", uint64_t(groups.size()),
+ (groups.size() == 1) ? "group" : "groups");
+ stream.EOL();
+ // print script group details
+ stream.IndentMore();
+ for (const RSScriptGroupDescriptorSP &g : groups) {
+ if (g) {
+ stream.Indent();
+ // script group name
+ stream.Printf("%s", g->m_name.AsCString());
+ stream.EOL();
+ // print out the kernels
+ stream.IndentMore();
+ for (const auto &k : g->m_kernels) {
+ stream.Indent();
+ stream.Printf(". %s", k.m_name.AsCString());
+ stream.EOL();
+ }
+ stream.IndentLess();
+ }
+ }
+ stream.IndentLess();
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+class CommandObjectRenderScriptScriptGroup : public CommandObjectMultiword {
+public:
+ CommandObjectRenderScriptScriptGroup(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "renderscript scriptgroup",
+ "Command set for interacting with scriptgroups.",
+ nullptr, eCommandRequiresProcess |
+ eCommandProcessMustBeLaunched) {
+ LoadSubCommand(
+ "breakpoint",
+ CommandObjectSP(
+ new CommandObjectRenderScriptScriptGroupBreakpoint(interpreter)));
+ LoadSubCommand(
+ "list", CommandObjectSP(
+ new CommandObjectRenderScriptScriptGroupList(interpreter)));
+ }
+
+ ~CommandObjectRenderScriptScriptGroup() override = default;
+};
+
+lldb::CommandObjectSP NewCommandObjectRenderScriptScriptGroup(
+ lldb_private::CommandInterpreter &interpreter) {
+ return CommandObjectSP(new CommandObjectRenderScriptScriptGroup(interpreter));
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.h
new file mode 100644
index 000000000000..c25e240f6d52
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.h
@@ -0,0 +1,17 @@
+//===-- RenderScriptScriptGroup.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 liblldb_RenderScriptScriptGroup_h_
+#define liblldb_RenderScriptScriptGroup_h_
+
+#include "lldb/Interpreter/CommandInterpreter.h"
+
+lldb::CommandObjectSP NewCommandObjectRenderScriptScriptGroup(
+ lldb_private::CommandInterpreter &interpreter);
+
+#endif // liblldb_RenderScriptScriptGroup_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.cpp
new file mode 100644
index 000000000000..4725e8c5b0eb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.cpp
@@ -0,0 +1,281 @@
+//===-- RenderScriptx86ABIFixups.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 <set>
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/CallSite.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/Pass.h"
+
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/Log.h"
+
+using namespace lldb_private;
+namespace {
+
+bool isRSAPICall(llvm::Module &module, llvm::CallInst *call_inst) {
+ // TODO get the list of renderscript modules from lldb and check if
+ // this llvm::Module calls into any of them.
+ (void)module;
+ const auto func_name = call_inst->getCalledFunction()->getName();
+ if (func_name.startswith("llvm") || func_name.startswith("lldb"))
+ return false;
+
+ if (call_inst->getCalledFunction()->isIntrinsic())
+ return false;
+
+ return true;
+}
+
+bool isRSLargeReturnCall(llvm::Module &module, llvm::CallInst *call_inst) {
+ // i686 and x86_64 returns for large vectors in the RenderScript API are not
+ // handled as normal register pairs, but as a hidden sret type. This is not
+ // reflected in the debug info or mangled symbol name, and the android ABI
+ // for x86 and x86_64, (as well as the emulators) specifies there is no AVX,
+ // so bcc generates an sret function because we cannot natively return
+ // 256 bit vectors.
+ // This function simply checks whether a function has a > 128bit return type.
+ // It is perhaps an unreliable heuristic, and relies on bcc not generating
+ // AVX code, so if the android ABI one day provides for AVX, this function
+ // may go out of fashion.
+ (void)module;
+ if (!call_inst || !call_inst->getCalledFunction())
+ return false;
+
+ return call_inst->getCalledFunction()
+ ->getReturnType()
+ ->getPrimitiveSizeInBits() > 128;
+}
+
+bool isRSAllocationPtrTy(const llvm::Type *type) {
+ if (!type->isPointerTy())
+ return false;
+ auto ptr_type = type->getPointerElementType();
+
+ return ptr_type->isStructTy() &&
+ ptr_type->getStructName().startswith("struct.rs_allocation");
+}
+
+bool isRSAllocationTyCallSite(llvm::Module &module, llvm::CallInst *call_inst) {
+ (void)module;
+ if (!call_inst->hasByValArgument())
+ return false;
+ for (const auto &param : call_inst->operand_values())
+ if (isRSAllocationPtrTy(param->getType()))
+ return true;
+ return false;
+}
+
+llvm::FunctionType *cloneToStructRetFnTy(llvm::CallInst *call_inst) {
+ // on x86 StructReturn functions return a pointer to the return value, rather
+ // than the return value itself
+ // [ref](http://www.agner.org/optimize/calling_conventions.pdf section 6). We
+ // create a return type by getting the pointer type of the old return type,
+ // and inserting a new initial argument of pointer type of the original
+ // return type.
+ Log *log(
+ GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_EXPRESSIONS));
+
+ assert(call_inst && "no CallInst");
+ llvm::Function *orig = call_inst->getCalledFunction();
+ assert(orig && "CallInst has no called function");
+ llvm::FunctionType *orig_type = orig->getFunctionType();
+ auto name = orig->getName();
+ if (log)
+ log->Printf("%s - cloning to StructRet function for '%s'", __FUNCTION__,
+ name.str().c_str());
+
+ unsigned num_params = orig_type->getNumParams();
+ std::vector<llvm::Type *> new_params{num_params + 1, nullptr};
+ std::vector<llvm::Type *> params{orig_type->param_begin(),
+ orig_type->param_end()};
+
+ // This may not work if the function is somehow declared void as llvm is
+ // strongly typed and represents void* with i8*
+ assert(!orig_type->getReturnType()->isVoidTy() &&
+ "Cannot add StructRet attribute to void function");
+ llvm::PointerType *return_type_ptr_type =
+ llvm::PointerType::getUnqual(orig->getReturnType());
+ assert(return_type_ptr_type &&
+ "failed to get function return type PointerType");
+ if (!return_type_ptr_type)
+ return nullptr;
+
+ if (log)
+ log->Printf("%s - return type pointer type for StructRet clone @ '0x%p':\n",
+ __FUNCTION__, (void *)return_type_ptr_type);
+ // put the sret pointer argument in place at the beginning of the
+ // argument list.
+ params.emplace(params.begin(), return_type_ptr_type);
+ assert(params.size() == num_params + 1);
+ return llvm::FunctionType::get(return_type_ptr_type, params,
+ orig->isVarArg());
+}
+
+bool findRSCallSites(llvm::Module &module,
+ std::set<llvm::CallInst *> &rs_callsites,
+ bool (*predicate)(llvm::Module &, llvm::CallInst *)) {
+ bool found = false;
+
+ for (auto &func : module.getFunctionList())
+ for (auto &block : func.getBasicBlockList())
+ for (auto &inst : block) {
+ llvm::CallInst *call_inst =
+ llvm::dyn_cast_or_null<llvm::CallInst>(&inst);
+ if (!call_inst || !call_inst->getCalledFunction())
+ // This is not the call-site you are looking for...
+ continue;
+ if (isRSAPICall(module, call_inst) && predicate(module, call_inst)) {
+ rs_callsites.insert(call_inst);
+ found = true;
+ }
+ }
+ return found;
+}
+
+bool fixupX86StructRetCalls(llvm::Module &module) {
+ bool changed = false;
+ // changing a basic block while iterating over it seems to have some
+ // undefined behaviour going on so we find all RS callsites first, then fix
+ // them up after consuming the iterator.
+ std::set<llvm::CallInst *> rs_callsites;
+ if (!findRSCallSites(module, rs_callsites, isRSLargeReturnCall))
+ return false;
+
+ for (auto call_inst : rs_callsites) {
+ llvm::FunctionType *new_func_type = cloneToStructRetFnTy(call_inst);
+ assert(new_func_type &&
+ "failed to clone functionType for Renderscript ABI fixup");
+
+ llvm::CallSite call_site(call_inst);
+ llvm::Function *func = call_inst->getCalledFunction();
+ assert(func && "cannot resolve function in RenderScriptRuntime");
+ // Copy the original call arguments
+ std::vector<llvm::Value *> new_call_args(call_site.arg_begin(),
+ call_site.arg_end());
+
+ // Allocate enough space to store the return value of the original function
+ // we pass a pointer to this allocation as the StructRet param, and then
+ // copy its value into the lldb return value
+ const llvm::DataLayout &DL = module.getDataLayout();
+ llvm::AllocaInst *return_value_alloc = new llvm::AllocaInst(
+ func->getReturnType(), DL.getAllocaAddrSpace(), "var_vector_return_alloc",
+ call_inst);
+ // use the new allocation as the new first argument
+ new_call_args.emplace(new_call_args.begin(),
+ llvm::cast<llvm::Value>(return_value_alloc));
+ llvm::PointerType *new_func_ptr_type =
+ llvm::PointerType::get(new_func_type, 0);
+ // Create the type cast from the old function type to the new one
+ llvm::Constant *new_func_cast = llvm::ConstantExpr::getCast(
+ llvm::Instruction::BitCast, func, new_func_ptr_type);
+ // create an allocation for a new function pointer
+ llvm::AllocaInst *new_func_ptr =
+ new llvm::AllocaInst(new_func_ptr_type, DL.getAllocaAddrSpace(),
+ "new_func_ptr", call_inst);
+ // store the new_func_cast to the newly allocated space
+ (new llvm::StoreInst(new_func_cast, new_func_ptr, call_inst))
+ ->setName("new_func_ptr_load_cast");
+ // load the new function address ready for a jump
+ llvm::LoadInst *new_func_addr_load =
+ new llvm::LoadInst(new_func_ptr, "load_func_pointer", call_inst);
+ // and create a callinstruction from it
+ llvm::CallInst *new_call_inst =
+ llvm::CallInst::Create(new_func_type, new_func_addr_load, new_call_args,
+ "new_func_call", call_inst);
+ new_call_inst->setCallingConv(call_inst->getCallingConv());
+ new_call_inst->setTailCall(call_inst->isTailCall());
+ llvm::LoadInst *lldb_save_result_address =
+ new llvm::LoadInst(return_value_alloc, "save_return_val", call_inst);
+
+ // Now remove the old broken call
+ call_inst->replaceAllUsesWith(lldb_save_result_address);
+ call_inst->eraseFromParent();
+ changed = true;
+ }
+ return changed;
+}
+
+bool fixupRSAllocationStructByValCalls(llvm::Module &module) {
+ // On x86_64, calls to functions in the RS runtime that take an
+ // `rs_allocation` type argument are actually handled as by-ref params by
+ // bcc, but appear to be passed by value by lldb (the callsite all use
+ // `struct byval`). On x86_64 Linux, struct arguments are transferred in
+ // registers if the struct size is no bigger than 128bits
+ // [ref](http://www.agner.org/optimize/calling_conventions.pdf) section 7.1
+ // "Passing and returning objects" otherwise passed on the stack. an object
+ // of type `rs_allocation` is actually 256bits, so should be passed on the
+ // stack. However, code generated by bcc actually treats formal params of
+ // type `rs_allocation` as `rs_allocation *` so we need to convert the
+ // calling convention to pass by reference, and remove any hint of byval from
+ // formal parameters.
+ bool changed = false;
+ std::set<llvm::CallInst *> rs_callsites;
+ if (!findRSCallSites(module, rs_callsites, isRSAllocationTyCallSite))
+ return false;
+
+ std::set<llvm::Function *> rs_functions;
+
+ // for all call instructions
+ for (auto call_inst : rs_callsites) {
+ // add the called function to a set so that we can strip its byval
+ // attributes in another pass
+ rs_functions.insert(call_inst->getCalledFunction());
+
+ // get the function attributes
+ llvm::AttributeList call_attribs = call_inst->getAttributes();
+
+ // iterate over the argument attributes
+ for (unsigned I = call_attribs.index_begin(); I != call_attribs.index_end();
+ I++) {
+ // if this argument is passed by val
+ if (call_attribs.hasAttribute(I, llvm::Attribute::ByVal)) {
+ // strip away the byval attribute
+ call_inst->removeAttribute(I, llvm::Attribute::ByVal);
+ changed = true;
+ }
+ }
+ }
+
+ // for all called function decls
+ for (auto func : rs_functions) {
+ // inspect all of the arguments in the call
+ for (auto &arg : func->args()) {
+ if (arg.hasByValAttr()) {
+ arg.removeAttr(llvm::Attribute::ByVal);
+ changed = true;
+ }
+ }
+ }
+ return changed;
+}
+} // end anonymous namespace
+
+namespace lldb_private {
+namespace lldb_renderscript {
+
+bool fixupX86FunctionCalls(llvm::Module &module) {
+ return fixupX86StructRetCalls(module);
+}
+
+bool fixupX86_64FunctionCalls(llvm::Module &module) {
+ bool changed = false;
+ changed |= fixupX86StructRetCalls(module);
+ changed |= fixupRSAllocationStructByValCalls(module);
+ return changed;
+}
+
+} // end namespace lldb_renderscript
+} // end namespace lldb_private
diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.h
new file mode 100644
index 000000000000..a5efc999aea4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.h
@@ -0,0 +1,22 @@
+//===-- RenderScriptx86ABIFixups.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_RENDERSCRIPT_X86_H
+#define LLDB_RENDERSCRIPT_X86_H
+
+#include "llvm/IR/Module.h"
+
+namespace lldb_private {
+namespace lldb_renderscript {
+
+bool fixupX86FunctionCalls(llvm::Module &module);
+
+bool fixupX86_64FunctionCalls(llvm::Module &module);
+}
+}
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp b/contrib/llvm-project/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp
new file mode 100644
index 000000000000..e0d2c5d0eef8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp
@@ -0,0 +1,200 @@
+//===-- MemoryHistoryASan.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 "MemoryHistoryASan.h"
+
+#include "lldb/Target/MemoryHistory.h"
+
+#include "Plugins/Process/Utility/HistoryThread.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginInterface.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadList.h"
+#include "lldb/lldb-private.h"
+
+#include <sstream>
+
+using namespace lldb;
+using namespace lldb_private;
+
+MemoryHistorySP MemoryHistoryASan::CreateInstance(const ProcessSP &process_sp) {
+ if (!process_sp.get())
+ return nullptr;
+
+ Target &target = process_sp->GetTarget();
+
+ const ModuleList &target_modules = target.GetImages();
+ std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
+ const size_t num_modules = target_modules.GetSize();
+ for (size_t i = 0; i < num_modules; ++i) {
+ Module *module_pointer = target_modules.GetModulePointerAtIndexUnlocked(i);
+
+ const Symbol *symbol = module_pointer->FindFirstSymbolWithNameAndType(
+ ConstString("__asan_get_alloc_stack"), lldb::eSymbolTypeAny);
+
+ if (symbol != nullptr)
+ return MemoryHistorySP(new MemoryHistoryASan(process_sp));
+ }
+
+ return MemoryHistorySP();
+}
+
+void MemoryHistoryASan::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "ASan memory history provider.", CreateInstance);
+}
+
+void MemoryHistoryASan::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+ConstString MemoryHistoryASan::GetPluginNameStatic() {
+ static ConstString g_name("asan");
+ return g_name;
+}
+
+MemoryHistoryASan::MemoryHistoryASan(const ProcessSP &process_sp) {
+ if (process_sp)
+ m_process_wp = process_sp;
+}
+
+const char *memory_history_asan_command_prefix = R"(
+ extern "C"
+ {
+ size_t __asan_get_alloc_stack(void *addr, void **trace, size_t size, int *thread_id);
+ size_t __asan_get_free_stack(void *addr, void **trace, size_t size, int *thread_id);
+ }
+
+ struct data {
+ void *alloc_trace[256];
+ size_t alloc_count;
+ int alloc_tid;
+
+ void *free_trace[256];
+ size_t free_count;
+ int free_tid;
+ };
+)";
+
+const char *memory_history_asan_command_format =
+ R"(
+ data t;
+
+ t.alloc_count = __asan_get_alloc_stack((void *)0x%)" PRIx64
+ R"(, t.alloc_trace, 256, &t.alloc_tid);
+ t.free_count = __asan_get_free_stack((void *)0x%)" PRIx64
+ R"(, t.free_trace, 256, &t.free_tid);
+
+ t;
+)";
+
+static void CreateHistoryThreadFromValueObject(ProcessSP process_sp,
+ ValueObjectSP return_value_sp,
+ const char *type,
+ const char *thread_name,
+ HistoryThreads &result) {
+ std::string count_path = "." + std::string(type) + "_count";
+ std::string tid_path = "." + std::string(type) + "_tid";
+ std::string trace_path = "." + std::string(type) + "_trace";
+
+ ValueObjectSP count_sp =
+ return_value_sp->GetValueForExpressionPath(count_path.c_str());
+ ValueObjectSP tid_sp =
+ return_value_sp->GetValueForExpressionPath(tid_path.c_str());
+
+ if (!count_sp || !tid_sp)
+ return;
+
+ int count = count_sp->GetValueAsUnsigned(0);
+ tid_t tid = tid_sp->GetValueAsUnsigned(0) + 1;
+
+ if (count <= 0)
+ return;
+
+ ValueObjectSP trace_sp =
+ return_value_sp->GetValueForExpressionPath(trace_path.c_str());
+
+ if (!trace_sp)
+ return;
+
+ std::vector<lldb::addr_t> pcs;
+ for (int i = 0; i < count; i++) {
+ addr_t pc = trace_sp->GetChildAtIndex(i, true)->GetValueAsUnsigned(0);
+ if (pc == 0 || pc == 1 || pc == LLDB_INVALID_ADDRESS)
+ continue;
+ pcs.push_back(pc);
+ }
+
+ HistoryThread *history_thread = new HistoryThread(*process_sp, tid, pcs);
+ ThreadSP new_thread_sp(history_thread);
+ std::ostringstream thread_name_with_number;
+ thread_name_with_number << thread_name << " Thread " << tid;
+ history_thread->SetThreadName(thread_name_with_number.str().c_str());
+ // Save this in the Process' ExtendedThreadList so a strong pointer retains
+ // the object
+ process_sp->GetExtendedThreadList().AddThread(new_thread_sp);
+ result.push_back(new_thread_sp);
+}
+
+HistoryThreads MemoryHistoryASan::GetHistoryThreads(lldb::addr_t address) {
+ HistoryThreads result;
+
+ ProcessSP process_sp = m_process_wp.lock();
+ if (!process_sp)
+ return result;
+
+ ThreadSP thread_sp =
+ process_sp->GetThreadList().GetExpressionExecutionThread();
+ if (!thread_sp)
+ return result;
+
+ StackFrameSP frame_sp = thread_sp->GetSelectedFrame();
+ if (!frame_sp)
+ return result;
+
+ ExecutionContext exe_ctx(frame_sp);
+ ValueObjectSP return_value_sp;
+ StreamString expr;
+ Status eval_error;
+ expr.Printf(memory_history_asan_command_format, address, address);
+
+ EvaluateExpressionOptions options;
+ options.SetUnwindOnError(true);
+ options.SetTryAllThreads(true);
+ options.SetStopOthers(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetTimeout(process_sp->GetUtilityExpressionTimeout());
+ options.SetPrefix(memory_history_asan_command_prefix);
+ options.SetAutoApplyFixIts(false);
+ options.SetLanguage(eLanguageTypeObjC_plus_plus);
+
+ ExpressionResults expr_result = UserExpression::Evaluate(
+ exe_ctx, options, expr.GetString(), "", return_value_sp, eval_error);
+ if (expr_result != eExpressionCompleted) {
+ process_sp->GetTarget().GetDebugger().GetAsyncOutputStream()->Printf(
+ "Warning: Cannot evaluate AddressSanitizer expression:\n%s\n",
+ eval_error.AsCString());
+ return result;
+ }
+
+ if (!return_value_sp)
+ return result;
+
+ CreateHistoryThreadFromValueObject(process_sp, return_value_sp, "free",
+ "Memory deallocated by", result);
+ CreateHistoryThreadFromValueObject(process_sp, return_value_sp, "alloc",
+ "Memory allocated by", result);
+
+ return result;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.h b/contrib/llvm-project/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.h
new file mode 100644
index 000000000000..266576b0cd96
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.h
@@ -0,0 +1,48 @@
+//===-- MemoryHistoryASan.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 liblldb_MemoryHistoryASan_h_
+#define liblldb_MemoryHistoryASan_h_
+
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/MemoryHistory.h"
+#include "lldb/Target/Process.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class MemoryHistoryASan : public lldb_private::MemoryHistory {
+public:
+ ~MemoryHistoryASan() override = default;
+
+ static lldb::MemoryHistorySP
+ CreateInstance(const lldb::ProcessSP &process_sp);
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ lldb_private::ConstString GetPluginName() override {
+ return GetPluginNameStatic();
+ }
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ lldb_private::HistoryThreads GetHistoryThreads(lldb::addr_t address) override;
+
+private:
+ MemoryHistoryASan(const lldb::ProcessSP &process_sp);
+
+ lldb::ProcessWP m_process_wp;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_MemoryHistoryASan_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp b/contrib/llvm-project/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp
new file mode 100644
index 000000000000..512b5bebf07f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp
@@ -0,0 +1,514 @@
+//===-- ObjectContainerBSDArchive.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 "ObjectContainerBSDArchive.h"
+
+#if defined(_WIN32) || defined(__ANDROID__)
+// Defines from ar, missing on Windows
+#define ARMAG "!<arch>\n"
+#define SARMAG 8
+#define ARFMAG "`\n"
+
+typedef struct ar_hdr {
+ char ar_name[16];
+ char ar_date[12];
+ char ar_uid[6], ar_gid[6];
+ char ar_mode[8];
+ char ar_size[10];
+ char ar_fmag[2];
+} ar_hdr;
+#else
+#include <ar.h>
+#endif
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/Timer.h"
+
+#include "llvm/Support/MemoryBuffer.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+ObjectContainerBSDArchive::Object::Object()
+ : ar_name(), modification_time(0), uid(0), gid(0), mode(0), size(0),
+ file_offset(0), file_size(0) {}
+
+void ObjectContainerBSDArchive::Object::Clear() {
+ ar_name.Clear();
+ modification_time = 0;
+ uid = 0;
+ gid = 0;
+ mode = 0;
+ size = 0;
+ file_offset = 0;
+ file_size = 0;
+}
+
+lldb::offset_t
+ObjectContainerBSDArchive::Object::Extract(const DataExtractor &data,
+ lldb::offset_t offset) {
+ size_t ar_name_len = 0;
+ std::string str;
+ char *err;
+
+ // File header
+ //
+ // The common format is as follows.
+ //
+ // Offset Length Name Format
+ // 0 16 File name ASCII right padded with spaces (no spaces
+ // allowed in file name)
+ // 16 12 File mod Decimal as cstring right padded with
+ // spaces
+ // 28 6 Owner ID Decimal as cstring right padded with
+ // spaces
+ // 34 6 Group ID Decimal as cstring right padded with
+ // spaces
+ // 40 8 File mode Octal as cstring right padded with
+ // spaces
+ // 48 10 File byte size Decimal as cstring right padded with
+ // spaces
+ // 58 2 File magic 0x60 0x0A
+
+ // Make sure there is enough data for the file header and bail if not
+ if (!data.ValidOffsetForDataOfSize(offset, 60))
+ return LLDB_INVALID_OFFSET;
+
+ str.assign((const char *)data.GetData(&offset, 16), 16);
+ if (str.find("#1/") == 0) {
+ // If the name is longer than 16 bytes, or contains an embedded space then
+ // it will use this format where the length of the name is here and the
+ // name characters are after this header.
+ ar_name_len = strtoul(str.c_str() + 3, &err, 10);
+ } else {
+ // Strip off any trailing spaces.
+ const size_t last_pos = str.find_last_not_of(' ');
+ if (last_pos != std::string::npos) {
+ if (last_pos + 1 < 16)
+ str.erase(last_pos + 1);
+ }
+ ar_name.SetCString(str.c_str());
+ }
+
+ str.assign((const char *)data.GetData(&offset, 12), 12);
+ modification_time = strtoul(str.c_str(), &err, 10);
+
+ str.assign((const char *)data.GetData(&offset, 6), 6);
+ uid = strtoul(str.c_str(), &err, 10);
+
+ str.assign((const char *)data.GetData(&offset, 6), 6);
+ gid = strtoul(str.c_str(), &err, 10);
+
+ str.assign((const char *)data.GetData(&offset, 8), 8);
+ mode = strtoul(str.c_str(), &err, 8);
+
+ str.assign((const char *)data.GetData(&offset, 10), 10);
+ size = strtoul(str.c_str(), &err, 10);
+
+ str.assign((const char *)data.GetData(&offset, 2), 2);
+ if (str == ARFMAG) {
+ if (ar_name_len > 0) {
+ const void *ar_name_ptr = data.GetData(&offset, ar_name_len);
+ // Make sure there was enough data for the string value and bail if not
+ if (ar_name_ptr == nullptr)
+ return LLDB_INVALID_OFFSET;
+ str.assign((const char *)ar_name_ptr, ar_name_len);
+ ar_name.SetCString(str.c_str());
+ }
+ file_offset = offset;
+ file_size = size - ar_name_len;
+ return offset;
+ }
+ return LLDB_INVALID_OFFSET;
+}
+
+ObjectContainerBSDArchive::Archive::Archive(const lldb_private::ArchSpec &arch,
+ const llvm::sys::TimePoint<> &time,
+ lldb::offset_t file_offset,
+ lldb_private::DataExtractor &data)
+ : m_arch(arch), m_modification_time(time), m_file_offset(file_offset),
+ m_objects(), m_data(data) {}
+
+ObjectContainerBSDArchive::Archive::~Archive() {}
+
+size_t ObjectContainerBSDArchive::Archive::ParseObjects() {
+ DataExtractor &data = m_data;
+ std::string str;
+ lldb::offset_t offset = 0;
+ str.assign((const char *)data.GetData(&offset, SARMAG), SARMAG);
+ if (str == ARMAG) {
+ Object obj;
+ do {
+ offset = obj.Extract(data, offset);
+ if (offset == LLDB_INVALID_OFFSET)
+ break;
+ size_t obj_idx = m_objects.size();
+ m_objects.push_back(obj);
+ // Insert all of the C strings out of order for now...
+ m_object_name_to_index_map.Append(obj.ar_name, obj_idx);
+ offset += obj.file_size;
+ obj.Clear();
+ } while (data.ValidOffset(offset));
+
+ // Now sort all of the object name pointers
+ m_object_name_to_index_map.Sort();
+ }
+ return m_objects.size();
+}
+
+ObjectContainerBSDArchive::Object *
+ObjectContainerBSDArchive::Archive::FindObject(
+ ConstString object_name, const llvm::sys::TimePoint<> &object_mod_time) {
+ const ObjectNameToIndexMap::Entry *match =
+ m_object_name_to_index_map.FindFirstValueForName(object_name);
+ if (!match)
+ return nullptr;
+ if (object_mod_time == llvm::sys::TimePoint<>())
+ return &m_objects[match->value];
+
+ const uint64_t object_modification_date = llvm::sys::toTimeT(object_mod_time);
+ if (m_objects[match->value].modification_time == object_modification_date)
+ return &m_objects[match->value];
+
+ const ObjectNameToIndexMap::Entry *next_match =
+ m_object_name_to_index_map.FindNextValueForName(match);
+ while (next_match) {
+ if (m_objects[next_match->value].modification_time ==
+ object_modification_date)
+ return &m_objects[next_match->value];
+ next_match = m_object_name_to_index_map.FindNextValueForName(next_match);
+ }
+
+ return nullptr;
+}
+
+ObjectContainerBSDArchive::Archive::shared_ptr
+ObjectContainerBSDArchive::Archive::FindCachedArchive(
+ const FileSpec &file, const ArchSpec &arch,
+ const llvm::sys::TimePoint<> &time, lldb::offset_t file_offset) {
+ std::lock_guard<std::recursive_mutex> guard(Archive::GetArchiveCacheMutex());
+ shared_ptr archive_sp;
+ Archive::Map &archive_map = Archive::GetArchiveCache();
+ Archive::Map::iterator pos = archive_map.find(file);
+ // Don't cache a value for "archive_map.end()" below since we might delete an
+ // archive entry...
+ while (pos != archive_map.end() && pos->first == file) {
+ bool match = true;
+ if (arch.IsValid() &&
+ !pos->second->GetArchitecture().IsCompatibleMatch(arch))
+ match = false;
+ else if (file_offset != LLDB_INVALID_OFFSET &&
+ pos->second->GetFileOffset() != file_offset)
+ match = false;
+ if (match) {
+ if (pos->second->GetModificationTime() == time) {
+ return pos->second;
+ } else {
+ // We have a file at the same path with the same architecture whose
+ // modification time doesn't match. It doesn't make sense for us to
+ // continue to use this BSD archive since we cache only the object info
+ // which consists of file time info and also the file offset and file
+ // size of any contained objects. Since this information is now out of
+ // date, we won't get the correct information if we go and extract the
+ // file data, so we should remove the old and outdated entry.
+ archive_map.erase(pos);
+ pos = archive_map.find(file);
+ continue; // Continue to next iteration so we don't increment pos
+ // below...
+ }
+ }
+ ++pos;
+ }
+ return archive_sp;
+}
+
+ObjectContainerBSDArchive::Archive::shared_ptr
+ObjectContainerBSDArchive::Archive::ParseAndCacheArchiveForFile(
+ const FileSpec &file, const ArchSpec &arch,
+ const llvm::sys::TimePoint<> &time, lldb::offset_t file_offset,
+ DataExtractor &data) {
+ shared_ptr archive_sp(new Archive(arch, time, file_offset, data));
+ if (archive_sp) {
+ const size_t num_objects = archive_sp->ParseObjects();
+ if (num_objects > 0) {
+ std::lock_guard<std::recursive_mutex> guard(
+ Archive::GetArchiveCacheMutex());
+ Archive::GetArchiveCache().insert(std::make_pair(file, archive_sp));
+ } else {
+ archive_sp.reset();
+ }
+ }
+ return archive_sp;
+}
+
+ObjectContainerBSDArchive::Archive::Map &
+ObjectContainerBSDArchive::Archive::GetArchiveCache() {
+ static Archive::Map g_archive_map;
+ return g_archive_map;
+}
+
+std::recursive_mutex &
+ObjectContainerBSDArchive::Archive::GetArchiveCacheMutex() {
+ static std::recursive_mutex g_archive_map_mutex;
+ return g_archive_map_mutex;
+}
+
+void ObjectContainerBSDArchive::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ GetModuleSpecifications);
+}
+
+void ObjectContainerBSDArchive::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ObjectContainerBSDArchive::GetPluginNameStatic() {
+ static ConstString g_name("bsd-archive");
+ return g_name;
+}
+
+const char *ObjectContainerBSDArchive::GetPluginDescriptionStatic() {
+ return "BSD Archive object container reader.";
+}
+
+ObjectContainer *ObjectContainerBSDArchive::CreateInstance(
+ const lldb::ModuleSP &module_sp, DataBufferSP &data_sp,
+ lldb::offset_t data_offset, const FileSpec *file,
+ lldb::offset_t file_offset, lldb::offset_t length) {
+ ConstString object_name(module_sp->GetObjectName());
+ if (!object_name)
+ return nullptr;
+
+ if (data_sp) {
+ // We have data, which means this is the first 512 bytes of the file Check
+ // to see if the magic bytes match and if they do, read the entire table of
+ // contents for the archive and cache it
+ DataExtractor data;
+ data.SetData(data_sp, data_offset, length);
+ if (file && data_sp && ObjectContainerBSDArchive::MagicBytesMatch(data)) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(
+ func_cat,
+ "ObjectContainerBSDArchive::CreateInstance (module = %s, file = "
+ "%p, file_offset = 0x%8.8" PRIx64 ", file_size = 0x%8.8" PRIx64 ")",
+ module_sp->GetFileSpec().GetPath().c_str(),
+ static_cast<const void *>(file), static_cast<uint64_t>(file_offset),
+ static_cast<uint64_t>(length));
+
+ // Map the entire .a file to be sure that we don't lose any data if the
+ // file gets updated by a new build while this .a file is being used for
+ // debugging
+ DataBufferSP archive_data_sp =
+ FileSystem::Instance().CreateDataBuffer(*file, length, file_offset);
+ if (!archive_data_sp)
+ return nullptr;
+
+ lldb::offset_t archive_data_offset = 0;
+
+ Archive::shared_ptr archive_sp(Archive::FindCachedArchive(
+ *file, module_sp->GetArchitecture(), module_sp->GetModificationTime(),
+ file_offset));
+ std::unique_ptr<ObjectContainerBSDArchive> container_up(
+ new ObjectContainerBSDArchive(module_sp, archive_data_sp,
+ archive_data_offset, file, file_offset,
+ length));
+
+ if (container_up) {
+ if (archive_sp) {
+ // We already have this archive in our cache, use it
+ container_up->SetArchive(archive_sp);
+ return container_up.release();
+ } else if (container_up->ParseHeader())
+ return container_up.release();
+ }
+ }
+ } else {
+ // No data, just check for a cached archive
+ Archive::shared_ptr archive_sp(Archive::FindCachedArchive(
+ *file, module_sp->GetArchitecture(), module_sp->GetModificationTime(),
+ file_offset));
+ if (archive_sp) {
+ std::unique_ptr<ObjectContainerBSDArchive> container_up(
+ new ObjectContainerBSDArchive(module_sp, data_sp, data_offset, file,
+ file_offset, length));
+
+ if (container_up) {
+ // We already have this archive in our cache, use it
+ container_up->SetArchive(archive_sp);
+ return container_up.release();
+ }
+ }
+ }
+ return nullptr;
+}
+
+bool ObjectContainerBSDArchive::MagicBytesMatch(const DataExtractor &data) {
+ uint32_t offset = 0;
+ const char *armag = (const char *)data.PeekData(offset, sizeof(ar_hdr));
+ if (armag && ::strncmp(armag, ARMAG, SARMAG) == 0) {
+ armag += offsetof(struct ar_hdr, ar_fmag) + SARMAG;
+ if (strncmp(armag, ARFMAG, 2) == 0)
+ return true;
+ }
+ return false;
+}
+
+ObjectContainerBSDArchive::ObjectContainerBSDArchive(
+ const lldb::ModuleSP &module_sp, DataBufferSP &data_sp,
+ lldb::offset_t data_offset, const lldb_private::FileSpec *file,
+ lldb::offset_t file_offset, lldb::offset_t size)
+ : ObjectContainer(module_sp, file, file_offset, size, data_sp, data_offset),
+ m_archive_sp() {}
+void ObjectContainerBSDArchive::SetArchive(Archive::shared_ptr &archive_sp) {
+ m_archive_sp = archive_sp;
+}
+
+ObjectContainerBSDArchive::~ObjectContainerBSDArchive() {}
+
+bool ObjectContainerBSDArchive::ParseHeader() {
+ if (m_archive_sp.get() == nullptr) {
+ if (m_data.GetByteSize() > 0) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ m_archive_sp = Archive::ParseAndCacheArchiveForFile(
+ m_file, module_sp->GetArchitecture(),
+ module_sp->GetModificationTime(), m_offset, m_data);
+ }
+ // Clear the m_data that contains the entire archive data and let our
+ // m_archive_sp hold onto the data.
+ m_data.Clear();
+ }
+ }
+ return m_archive_sp.get() != nullptr;
+}
+
+void ObjectContainerBSDArchive::Dump(Stream *s) const {
+ s->Printf("%p: ", static_cast<const void *>(this));
+ s->Indent();
+ const size_t num_archs = GetNumArchitectures();
+ const size_t num_objects = GetNumObjects();
+ s->Printf("ObjectContainerBSDArchive, num_archs = %" PRIu64
+ ", num_objects = %" PRIu64 "",
+ (uint64_t)num_archs, (uint64_t)num_objects);
+ uint32_t i;
+ ArchSpec arch;
+ s->IndentMore();
+ for (i = 0; i < num_archs; i++) {
+ s->Indent();
+ GetArchitectureAtIndex(i, arch);
+ s->Printf("arch[%u] = %s\n", i, arch.GetArchitectureName());
+ }
+ for (i = 0; i < num_objects; i++) {
+ s->Indent();
+ s->Printf("object[%u] = %s\n", i, GetObjectNameAtIndex(i));
+ }
+ s->IndentLess();
+ s->EOL();
+}
+
+ObjectFileSP ObjectContainerBSDArchive::GetObjectFile(const FileSpec *file) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ if (module_sp->GetObjectName() && m_archive_sp) {
+ Object *object = m_archive_sp->FindObject(
+ module_sp->GetObjectName(), module_sp->GetObjectModificationTime());
+ if (object) {
+ lldb::offset_t data_offset = object->file_offset;
+ return ObjectFile::FindPlugin(
+ module_sp, file, m_offset + object->file_offset, object->file_size,
+ m_archive_sp->GetData().GetSharedDataBuffer(), data_offset);
+ }
+ }
+ }
+ return ObjectFileSP();
+}
+
+// PluginInterface protocol
+lldb_private::ConstString ObjectContainerBSDArchive::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ObjectContainerBSDArchive::GetPluginVersion() { return 1; }
+
+size_t ObjectContainerBSDArchive::GetModuleSpecifications(
+ const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset, lldb::offset_t file_offset,
+ lldb::offset_t file_size, lldb_private::ModuleSpecList &specs) {
+
+ // We have data, which means this is the first 512 bytes of the file Check to
+ // see if the magic bytes match and if they do, read the entire table of
+ // contents for the archive and cache it
+ DataExtractor data;
+ data.SetData(data_sp, data_offset, data_sp->GetByteSize());
+ if (!file || !data_sp || !ObjectContainerBSDArchive::MagicBytesMatch(data))
+ return 0;
+
+ const size_t initial_count = specs.GetSize();
+ llvm::sys::TimePoint<> file_mod_time = FileSystem::Instance().GetModificationTime(file);
+ Archive::shared_ptr archive_sp(
+ Archive::FindCachedArchive(file, ArchSpec(), file_mod_time, file_offset));
+ bool set_archive_arch = false;
+ if (!archive_sp) {
+ set_archive_arch = true;
+ data_sp =
+ FileSystem::Instance().CreateDataBuffer(file, file_size, file_offset);
+ if (data_sp) {
+ data.SetData(data_sp, 0, data_sp->GetByteSize());
+ archive_sp = Archive::ParseAndCacheArchiveForFile(
+ file, ArchSpec(), file_mod_time, file_offset, data);
+ }
+ }
+
+ if (archive_sp) {
+ const size_t num_objects = archive_sp->GetNumObjects();
+ for (size_t idx = 0; idx < num_objects; ++idx) {
+ const Object *object = archive_sp->GetObjectAtIndex(idx);
+ if (object) {
+ const lldb::offset_t object_file_offset =
+ file_offset + object->file_offset;
+ if (object->file_offset < file_size && file_size > object_file_offset) {
+ if (ObjectFile::GetModuleSpecifications(
+ file, object_file_offset, file_size - object_file_offset,
+ specs)) {
+ ModuleSpec &spec =
+ specs.GetModuleSpecRefAtIndex(specs.GetSize() - 1);
+ llvm::sys::TimePoint<> object_mod_time(
+ std::chrono::seconds(object->modification_time));
+ spec.GetObjectName() = object->ar_name;
+ spec.SetObjectOffset(object_file_offset);
+ spec.SetObjectSize(file_size - object_file_offset);
+ spec.GetObjectModificationTime() = object_mod_time;
+ }
+ }
+ }
+ }
+ }
+ const size_t end_count = specs.GetSize();
+ size_t num_specs_added = end_count - initial_count;
+ if (set_archive_arch && num_specs_added > 0) {
+ // The archive was created but we didn't have an architecture so we need to
+ // set it
+ for (size_t i = initial_count; i < end_count; ++i) {
+ ModuleSpec module_spec;
+ if (specs.GetModuleSpecAtIndex(i, module_spec)) {
+ if (module_spec.GetArchitecture().IsValid()) {
+ archive_sp->SetArchitecture(module_spec.GetArchitecture());
+ break;
+ }
+ }
+ }
+ }
+ return num_specs_added;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h b/contrib/llvm-project/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h
new file mode 100644
index 000000000000..5d9c01315a66
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h
@@ -0,0 +1,177 @@
+//===-- ObjectContainerBSDArchive.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 liblldb_ObjectContainerBSDArchive_h_
+#define liblldb_ObjectContainerBSDArchive_h_
+
+#include "lldb/Core/UniqueCStringMap.h"
+#include "lldb/Symbol/ObjectContainer.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/FileSpec.h"
+
+#include "llvm/Support/Chrono.h"
+
+#include <map>
+#include <memory>
+#include <mutex>
+
+class ObjectContainerBSDArchive : public lldb_private::ObjectContainer {
+public:
+ ObjectContainerBSDArchive(const lldb::ModuleSP &module_sp,
+ lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset,
+ const lldb_private::FileSpec *file,
+ lldb::offset_t offset, lldb::offset_t length);
+
+ ~ObjectContainerBSDArchive() override;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::ObjectContainer *
+ CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset, const lldb_private::FileSpec *file,
+ lldb::offset_t offset, lldb::offset_t length);
+
+ static size_t GetModuleSpecifications(const lldb_private::FileSpec &file,
+ lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset,
+ lldb::offset_t file_offset,
+ lldb::offset_t length,
+ lldb_private::ModuleSpecList &specs);
+
+ static bool MagicBytesMatch(const lldb_private::DataExtractor &data);
+
+ // Member Functions
+ bool ParseHeader() override;
+
+ size_t GetNumObjects() const override {
+ if (m_archive_sp)
+ return m_archive_sp->GetNumObjects();
+ return 0;
+ }
+
+ void Dump(lldb_private::Stream *s) const override;
+
+ lldb::ObjectFileSP GetObjectFile(const lldb_private::FileSpec *file) override;
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ struct Object {
+ Object();
+
+ void Clear();
+
+ lldb::offset_t Extract(const lldb_private::DataExtractor &data,
+ lldb::offset_t offset);
+ /// Object name in the archive.
+ lldb_private::ConstString ar_name;
+
+ /// Object modification time in the archive.
+ uint32_t modification_time;
+
+ /// Object user id in the archive.
+ uint16_t uid;
+
+ /// Object group id in the archive.
+ uint16_t gid;
+
+ /// Object octal file permissions in the archive.
+ uint16_t mode;
+
+ /// Object size in bytes in the archive.
+ uint32_t size;
+
+ /// File offset in bytes from the beginning of the file of the object data.
+ lldb::offset_t file_offset;
+
+ /// Length of the object data.
+ lldb::offset_t file_size;
+ };
+
+ class Archive {
+ public:
+ typedef std::shared_ptr<Archive> shared_ptr;
+ typedef std::multimap<lldb_private::FileSpec, shared_ptr> Map;
+
+ Archive(const lldb_private::ArchSpec &arch,
+ const llvm::sys::TimePoint<> &mod_time, lldb::offset_t file_offset,
+ lldb_private::DataExtractor &data);
+
+ ~Archive();
+
+ static Map &GetArchiveCache();
+
+ static std::recursive_mutex &GetArchiveCacheMutex();
+
+ static Archive::shared_ptr FindCachedArchive(
+ const lldb_private::FileSpec &file, const lldb_private::ArchSpec &arch,
+ const llvm::sys::TimePoint<> &mod_time, lldb::offset_t file_offset);
+
+ static Archive::shared_ptr ParseAndCacheArchiveForFile(
+ const lldb_private::FileSpec &file, const lldb_private::ArchSpec &arch,
+ const llvm::sys::TimePoint<> &mod_time, lldb::offset_t file_offset,
+ lldb_private::DataExtractor &data);
+
+ size_t GetNumObjects() const { return m_objects.size(); }
+
+ const Object *GetObjectAtIndex(size_t idx) {
+ if (idx < m_objects.size())
+ return &m_objects[idx];
+ return nullptr;
+ }
+
+ size_t ParseObjects();
+
+ Object *FindObject(lldb_private::ConstString object_name,
+ const llvm::sys::TimePoint<> &object_mod_time);
+
+ lldb::offset_t GetFileOffset() const { return m_file_offset; }
+
+ const llvm::sys::TimePoint<> &GetModificationTime() {
+ return m_modification_time;
+ }
+
+ const lldb_private::ArchSpec &GetArchitecture() const { return m_arch; }
+
+ void SetArchitecture(const lldb_private::ArchSpec &arch) { m_arch = arch; }
+
+ bool HasNoExternalReferences() const;
+
+ lldb_private::DataExtractor &GetData() { return m_data; }
+
+ protected:
+ typedef lldb_private::UniqueCStringMap<uint32_t> ObjectNameToIndexMap;
+ // Member Variables
+ lldb_private::ArchSpec m_arch;
+ llvm::sys::TimePoint<> m_modification_time;
+ lldb::offset_t m_file_offset;
+ std::vector<Object> m_objects;
+ ObjectNameToIndexMap m_object_name_to_index_map;
+ lldb_private::DataExtractor m_data; ///< The data for this object container
+ ///so we don't lose data if the .a files
+ ///gets modified
+ };
+
+ void SetArchive(Archive::shared_ptr &archive_sp);
+
+ Archive::shared_ptr m_archive_sp;
+};
+
+#endif // liblldb_ObjectContainerBSDArchive_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp
new file mode 100644
index 000000000000..d489eaf11115
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp
@@ -0,0 +1,411 @@
+//===-- BreakpadRecords.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 "Plugins/ObjectFile/Breakpad/BreakpadRecords.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/FormatVariadic.h"
+
+using namespace lldb_private;
+using namespace lldb_private::breakpad;
+
+namespace {
+enum class Token { Unknown, Module, Info, CodeID, File, Func, Public, Stack, CFI, Init };
+}
+
+template<typename T>
+static T stringTo(llvm::StringRef Str);
+
+template <> Token stringTo<Token>(llvm::StringRef Str) {
+ return llvm::StringSwitch<Token>(Str)
+ .Case("MODULE", Token::Module)
+ .Case("INFO", Token::Info)
+ .Case("CODE_ID", Token::CodeID)
+ .Case("FILE", Token::File)
+ .Case("FUNC", Token::Func)
+ .Case("PUBLIC", Token::Public)
+ .Case("STACK", Token::Stack)
+ .Case("CFI", Token::CFI)
+ .Case("INIT", Token::Init)
+ .Default(Token::Unknown);
+}
+
+template <>
+llvm::Triple::OSType stringTo<llvm::Triple::OSType>(llvm::StringRef Str) {
+ using llvm::Triple;
+ return llvm::StringSwitch<Triple::OSType>(Str)
+ .Case("Linux", Triple::Linux)
+ .Case("mac", Triple::MacOSX)
+ .Case("windows", Triple::Win32)
+ .Default(Triple::UnknownOS);
+}
+
+template <>
+llvm::Triple::ArchType stringTo<llvm::Triple::ArchType>(llvm::StringRef Str) {
+ using llvm::Triple;
+ return llvm::StringSwitch<Triple::ArchType>(Str)
+ .Case("arm", Triple::arm)
+ .Cases("arm64", "arm64e", Triple::aarch64)
+ .Case("mips", Triple::mips)
+ .Case("ppc", Triple::ppc)
+ .Case("ppc64", Triple::ppc64)
+ .Case("s390", Triple::systemz)
+ .Case("sparc", Triple::sparc)
+ .Case("sparcv9", Triple::sparcv9)
+ .Case("x86", Triple::x86)
+ .Case("x86_64", Triple::x86_64)
+ .Default(Triple::UnknownArch);
+}
+
+template<typename T>
+static T consume(llvm::StringRef &Str) {
+ llvm::StringRef Token;
+ std::tie(Token, Str) = getToken(Str);
+ return stringTo<T>(Token);
+}
+
+/// Return the number of hex digits needed to encode an (POD) object of a given
+/// type.
+template <typename T> static constexpr size_t hex_digits() {
+ return 2 * sizeof(T);
+}
+
+static UUID parseModuleId(llvm::Triple::OSType os, llvm::StringRef str) {
+ struct data_t {
+ using uuid_t = uint8_t[16];
+ uuid_t uuid;
+ llvm::support::ubig32_t age;
+ } data;
+ static_assert(sizeof(data) == 20, "");
+ // The textual module id encoding should be between 33 and 40 bytes long,
+ // depending on the size of the age field, which is of variable length.
+ // The first three chunks of the id are encoded in big endian, so we need to
+ // byte-swap those.
+ if (str.size() <= hex_digits<data_t::uuid_t>() ||
+ str.size() > hex_digits<data_t>())
+ return UUID();
+ if (!all_of(str, llvm::isHexDigit))
+ return UUID();
+
+ llvm::StringRef uuid_str = str.take_front(hex_digits<data_t::uuid_t>());
+ llvm::StringRef age_str = str.drop_front(hex_digits<data_t::uuid_t>());
+
+ llvm::copy(fromHex(uuid_str), data.uuid);
+ uint32_t age;
+ bool success = to_integer(age_str, age, 16);
+ assert(success);
+ (void)success;
+ data.age = age;
+
+ // On non-windows, the age field should always be zero, so we don't include to
+ // match the native uuid format of these platforms.
+ return UUID::fromData(&data, os == llvm::Triple::Win32 ? sizeof(data)
+ : sizeof(data.uuid));
+}
+
+llvm::Optional<Record::Kind> Record::classify(llvm::StringRef Line) {
+ Token Tok = consume<Token>(Line);
+ switch (Tok) {
+ case Token::Module:
+ return Record::Module;
+ case Token::Info:
+ return Record::Info;
+ case Token::File:
+ return Record::File;
+ case Token::Func:
+ return Record::Func;
+ case Token::Public:
+ return Record::Public;
+ case Token::Stack:
+ Tok = consume<Token>(Line);
+ switch (Tok) {
+ case Token::CFI:
+ return Record::StackCFI;
+ default:
+ return llvm::None;
+ }
+
+ case Token::Unknown:
+ // Optimistically assume that any unrecognised token means this is a line
+ // record, those don't have a special keyword and start directly with a
+ // hex number. CODE_ID should never be at the start of a line, but if it
+ // is, it can be treated the same way as a garbled line record.
+ return Record::Line;
+
+ case Token::CodeID:
+ case Token::CFI:
+ case Token::Init:
+ // These should never appear at the start of a valid record.
+ return llvm::None;
+ }
+ llvm_unreachable("Fully covered switch above!");
+}
+
+llvm::Optional<ModuleRecord> ModuleRecord::parse(llvm::StringRef Line) {
+ // MODULE Linux x86_64 E5894855C35DCCCCCCCCCCCCCCCCCCCC0 a.out
+ if (consume<Token>(Line) != Token::Module)
+ return llvm::None;
+
+ llvm::Triple::OSType OS = consume<llvm::Triple::OSType>(Line);
+ if (OS == llvm::Triple::UnknownOS)
+ return llvm::None;
+
+ llvm::Triple::ArchType Arch = consume<llvm::Triple::ArchType>(Line);
+ if (Arch == llvm::Triple::UnknownArch)
+ return llvm::None;
+
+ llvm::StringRef Str;
+ std::tie(Str, Line) = getToken(Line);
+ UUID ID = parseModuleId(OS, Str);
+ if (!ID)
+ return llvm::None;
+
+ return ModuleRecord(OS, Arch, std::move(ID));
+}
+
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+ const ModuleRecord &R) {
+ return OS << "MODULE " << llvm::Triple::getOSTypeName(R.OS) << " "
+ << llvm::Triple::getArchTypeName(R.Arch) << " "
+ << R.ID.GetAsString();
+}
+
+llvm::Optional<InfoRecord> InfoRecord::parse(llvm::StringRef Line) {
+ // INFO CODE_ID 554889E55DC3CCCCCCCCCCCCCCCCCCCC [a.exe]
+ if (consume<Token>(Line) != Token::Info)
+ return llvm::None;
+
+ if (consume<Token>(Line) != Token::CodeID)
+ return llvm::None;
+
+ llvm::StringRef Str;
+ std::tie(Str, Line) = getToken(Line);
+ // If we don't have any text following the code ID (e.g. on linux), we should
+ // use this as the UUID. Otherwise, we should revert back to the module ID.
+ UUID ID;
+ if (Line.trim().empty()) {
+ if (Str.empty() || ID.SetFromStringRef(Str, Str.size() / 2) != Str.size())
+ return llvm::None;
+ }
+ return InfoRecord(std::move(ID));
+}
+
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+ const InfoRecord &R) {
+ return OS << "INFO CODE_ID " << R.ID.GetAsString();
+}
+
+llvm::Optional<FileRecord> FileRecord::parse(llvm::StringRef Line) {
+ // FILE number name
+ if (consume<Token>(Line) != Token::File)
+ return llvm::None;
+
+ llvm::StringRef Str;
+ size_t Number;
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, Number))
+ return llvm::None;
+
+ llvm::StringRef Name = Line.trim();
+ if (Name.empty())
+ return llvm::None;
+
+ return FileRecord(Number, Name);
+}
+
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+ const FileRecord &R) {
+ return OS << "FILE " << R.Number << " " << R.Name;
+}
+
+static bool parsePublicOrFunc(llvm::StringRef Line, bool &Multiple,
+ lldb::addr_t &Address, lldb::addr_t *Size,
+ lldb::addr_t &ParamSize, llvm::StringRef &Name) {
+ // PUBLIC [m] address param_size name
+ // or
+ // FUNC [m] address size param_size name
+
+ Token Tok = Size ? Token::Func : Token::Public;
+
+ if (consume<Token>(Line) != Tok)
+ return false;
+
+ llvm::StringRef Str;
+ std::tie(Str, Line) = getToken(Line);
+ Multiple = Str == "m";
+
+ if (Multiple)
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, Address, 16))
+ return false;
+
+ if (Tok == Token::Func) {
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, *Size, 16))
+ return false;
+ }
+
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, ParamSize, 16))
+ return false;
+
+ Name = Line.trim();
+ if (Name.empty())
+ return false;
+
+ return true;
+}
+
+llvm::Optional<FuncRecord> FuncRecord::parse(llvm::StringRef Line) {
+ bool Multiple;
+ lldb::addr_t Address, Size, ParamSize;
+ llvm::StringRef Name;
+
+ if (parsePublicOrFunc(Line, Multiple, Address, &Size, ParamSize, Name))
+ return FuncRecord(Multiple, Address, Size, ParamSize, Name);
+
+ return llvm::None;
+}
+
+bool breakpad::operator==(const FuncRecord &L, const FuncRecord &R) {
+ return L.Multiple == R.Multiple && L.Address == R.Address &&
+ L.Size == R.Size && L.ParamSize == R.ParamSize && L.Name == R.Name;
+}
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+ const FuncRecord &R) {
+ return OS << llvm::formatv("FUNC {0}{1:x-} {2:x-} {3:x-} {4}",
+ R.Multiple ? "m " : "", R.Address, R.Size,
+ R.ParamSize, R.Name);
+}
+
+llvm::Optional<LineRecord> LineRecord::parse(llvm::StringRef Line) {
+ lldb::addr_t Address;
+ llvm::StringRef Str;
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, Address, 16))
+ return llvm::None;
+
+ lldb::addr_t Size;
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, Size, 16))
+ return llvm::None;
+
+ uint32_t LineNum;
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, LineNum))
+ return llvm::None;
+
+ size_t FileNum;
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, FileNum))
+ return llvm::None;
+
+ return LineRecord(Address, Size, LineNum, FileNum);
+}
+
+bool breakpad::operator==(const LineRecord &L, const LineRecord &R) {
+ return L.Address == R.Address && L.Size == R.Size && L.LineNum == R.LineNum &&
+ L.FileNum == R.FileNum;
+}
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+ const LineRecord &R) {
+ return OS << llvm::formatv("{0:x-} {1:x-} {2} {3}", R.Address, R.Size,
+ R.LineNum, R.FileNum);
+}
+
+llvm::Optional<PublicRecord> PublicRecord::parse(llvm::StringRef Line) {
+ bool Multiple;
+ lldb::addr_t Address, ParamSize;
+ llvm::StringRef Name;
+
+ if (parsePublicOrFunc(Line, Multiple, Address, nullptr, ParamSize, Name))
+ return PublicRecord(Multiple, Address, ParamSize, Name);
+
+ return llvm::None;
+}
+
+bool breakpad::operator==(const PublicRecord &L, const PublicRecord &R) {
+ return L.Multiple == R.Multiple && L.Address == R.Address &&
+ L.ParamSize == R.ParamSize && L.Name == R.Name;
+}
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+ const PublicRecord &R) {
+ return OS << llvm::formatv("PUBLIC {0}{1:x-} {2:x-} {3}",
+ R.Multiple ? "m " : "", R.Address, R.ParamSize,
+ R.Name);
+}
+
+llvm::Optional<StackCFIRecord> StackCFIRecord::parse(llvm::StringRef Line) {
+ // STACK CFI INIT address size reg1: expr1 reg2: expr2 ...
+ // or
+ // STACK CFI address reg1: expr1 reg2: expr2 ...
+ // No token in exprN ends with a colon.
+
+ if (consume<Token>(Line) != Token::Stack)
+ return llvm::None;
+ if (consume<Token>(Line) != Token::CFI)
+ return llvm::None;
+
+ llvm::StringRef Str;
+ std::tie(Str, Line) = getToken(Line);
+
+ bool IsInitRecord = stringTo<Token>(Str) == Token::Init;
+ if (IsInitRecord)
+ std::tie(Str, Line) = getToken(Line);
+
+ lldb::addr_t Address;
+ if (!to_integer(Str, Address, 16))
+ return llvm::None;
+
+ llvm::Optional<lldb::addr_t> Size;
+ if (IsInitRecord) {
+ Size.emplace();
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, *Size, 16))
+ return llvm::None;
+ }
+
+ return StackCFIRecord(Address, Size, Line.trim());
+}
+
+bool breakpad::operator==(const StackCFIRecord &L, const StackCFIRecord &R) {
+ return L.Address == R.Address && L.Size == R.Size &&
+ L.UnwindRules == R.UnwindRules;
+}
+
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+ const StackCFIRecord &R) {
+ OS << "STACK CFI ";
+ if (R.Size)
+ OS << "INIT ";
+ OS << llvm::formatv("{0:x-} ", R.Address);
+ if (R.Size)
+ OS << llvm::formatv("{0:x-} ", *R.Size);
+ return OS << " " << R.UnwindRules;
+}
+
+llvm::StringRef breakpad::toString(Record::Kind K) {
+ switch (K) {
+ case Record::Module:
+ return "MODULE";
+ case Record::Info:
+ return "INFO";
+ case Record::File:
+ return "FILE";
+ case Record::Func:
+ return "FUNC";
+ case Record::Line:
+ return "LINE";
+ case Record::Public:
+ return "PUBLIC";
+ case Record::StackCFI:
+ return "STACK CFI";
+ }
+ llvm_unreachable("Unknown record kind!");
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h
new file mode 100644
index 000000000000..5d5cdb319c10
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h
@@ -0,0 +1,163 @@
+//===-- BreakpadRecords.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_PLUGINS_OBJECTFILE_BREAKPAD_BREAKPADRECORDS_H
+#define LLDB_PLUGINS_OBJECTFILE_BREAKPAD_BREAKPADRECORDS_H
+
+#include "lldb/Utility/UUID.h"
+#include "lldb/lldb-types.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/FormatProviders.h"
+
+namespace lldb_private {
+namespace breakpad {
+
+class Record {
+public:
+ enum Kind { Module, Info, File, Func, Line, Public, StackCFI };
+
+ /// Attempt to guess the kind of the record present in the argument without
+ /// doing a full parse. The returned kind will always be correct for valid
+ /// records, but the full parse can still fail in case of corrupted input.
+ static llvm::Optional<Kind> classify(llvm::StringRef Line);
+
+protected:
+ Record(Kind K) : TheKind(K) {}
+
+ ~Record() = default;
+
+public:
+ Kind getKind() { return TheKind; }
+
+private:
+ Kind TheKind;
+};
+
+llvm::StringRef toString(Record::Kind K);
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Record::Kind K) {
+ OS << toString(K);
+ return OS;
+}
+
+class ModuleRecord : public Record {
+public:
+ static llvm::Optional<ModuleRecord> parse(llvm::StringRef Line);
+ ModuleRecord(llvm::Triple::OSType OS, llvm::Triple::ArchType Arch, UUID ID)
+ : Record(Module), OS(OS), Arch(Arch), ID(std::move(ID)) {}
+
+ llvm::Triple::OSType OS;
+ llvm::Triple::ArchType Arch;
+ UUID ID;
+};
+
+inline bool operator==(const ModuleRecord &L, const ModuleRecord &R) {
+ return L.OS == R.OS && L.Arch == R.Arch && L.ID == R.ID;
+}
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const ModuleRecord &R);
+
+class InfoRecord : public Record {
+public:
+ static llvm::Optional<InfoRecord> parse(llvm::StringRef Line);
+ InfoRecord(UUID ID) : Record(Info), ID(std::move(ID)) {}
+
+ UUID ID;
+};
+
+inline bool operator==(const InfoRecord &L, const InfoRecord &R) {
+ return L.ID == R.ID;
+}
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const InfoRecord &R);
+
+class FileRecord : public Record {
+public:
+ static llvm::Optional<FileRecord> parse(llvm::StringRef Line);
+ FileRecord(size_t Number, llvm::StringRef Name)
+ : Record(File), Number(Number), Name(Name) {}
+
+ size_t Number;
+ llvm::StringRef Name;
+};
+
+inline bool operator==(const FileRecord &L, const FileRecord &R) {
+ return L.Number == R.Number && L.Name == R.Name;
+}
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const FileRecord &R);
+
+class FuncRecord : public Record {
+public:
+ static llvm::Optional<FuncRecord> parse(llvm::StringRef Line);
+ FuncRecord(bool Multiple, lldb::addr_t Address, lldb::addr_t Size,
+ lldb::addr_t ParamSize, llvm::StringRef Name)
+ : Record(Module), Multiple(Multiple), Address(Address), Size(Size),
+ ParamSize(ParamSize), Name(Name) {}
+
+ bool Multiple;
+ lldb::addr_t Address;
+ lldb::addr_t Size;
+ lldb::addr_t ParamSize;
+ llvm::StringRef Name;
+};
+
+bool operator==(const FuncRecord &L, const FuncRecord &R);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const FuncRecord &R);
+
+class LineRecord : public Record {
+public:
+ static llvm::Optional<LineRecord> parse(llvm::StringRef Line);
+ LineRecord(lldb::addr_t Address, lldb::addr_t Size, uint32_t LineNum,
+ size_t FileNum)
+ : Record(Line), Address(Address), Size(Size), LineNum(LineNum),
+ FileNum(FileNum) {}
+
+ lldb::addr_t Address;
+ lldb::addr_t Size;
+ uint32_t LineNum;
+ size_t FileNum;
+};
+
+bool operator==(const LineRecord &L, const LineRecord &R);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const LineRecord &R);
+
+class PublicRecord : public Record {
+public:
+ static llvm::Optional<PublicRecord> parse(llvm::StringRef Line);
+ PublicRecord(bool Multiple, lldb::addr_t Address, lldb::addr_t ParamSize,
+ llvm::StringRef Name)
+ : Record(Module), Multiple(Multiple), Address(Address),
+ ParamSize(ParamSize), Name(Name) {}
+
+ bool Multiple;
+ lldb::addr_t Address;
+ lldb::addr_t ParamSize;
+ llvm::StringRef Name;
+};
+
+bool operator==(const PublicRecord &L, const PublicRecord &R);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const PublicRecord &R);
+
+class StackCFIRecord : public Record {
+public:
+ static llvm::Optional<StackCFIRecord> parse(llvm::StringRef Line);
+ StackCFIRecord(lldb::addr_t Address, llvm::Optional<lldb::addr_t> Size,
+ llvm::StringRef UnwindRules)
+ : Record(StackCFI), Address(Address), Size(Size),
+ UnwindRules(UnwindRules) {}
+
+ lldb::addr_t Address;
+ llvm::Optional<lldb::addr_t> Size;
+ llvm::StringRef UnwindRules;
+};
+
+bool operator==(const StackCFIRecord &L, const StackCFIRecord &R);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const StackCFIRecord &R);
+
+} // namespace breakpad
+} // namespace lldb_private
+
+#endif // LLDB_PLUGINS_OBJECTFILE_BREAKPAD_BREAKPADRECORDS_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp
new file mode 100644
index 000000000000..60dd9f9cecf0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp
@@ -0,0 +1,168 @@
+//===-- ObjectFileBreakpad.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 "Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h"
+#include "Plugins/ObjectFile/Breakpad/BreakpadRecords.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::breakpad;
+
+namespace {
+struct Header {
+ ArchSpec arch;
+ UUID uuid;
+ static llvm::Optional<Header> parse(llvm::StringRef text);
+};
+} // namespace
+
+llvm::Optional<Header> Header::parse(llvm::StringRef text) {
+ llvm::StringRef line;
+ std::tie(line, text) = text.split('\n');
+ auto Module = ModuleRecord::parse(line);
+ if (!Module)
+ return llvm::None;
+
+ llvm::Triple triple;
+ triple.setArch(Module->Arch);
+ triple.setOS(Module->OS);
+
+ std::tie(line, text) = text.split('\n');
+
+ auto Info = InfoRecord::parse(line);
+ UUID uuid = Info && Info->ID ? Info->ID : Module->ID;
+ return Header{ArchSpec(triple), std::move(uuid)};
+}
+
+void ObjectFileBreakpad::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ CreateMemoryInstance, GetModuleSpecifications);
+}
+
+void ObjectFileBreakpad::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+ConstString ObjectFileBreakpad::GetPluginNameStatic() {
+ static ConstString g_name("breakpad");
+ return g_name;
+}
+
+ObjectFile *ObjectFileBreakpad::CreateInstance(
+ const ModuleSP &module_sp, DataBufferSP &data_sp, offset_t data_offset,
+ const FileSpec *file, offset_t file_offset, offset_t length) {
+ if (!data_sp) {
+ data_sp = MapFileData(*file, length, file_offset);
+ if (!data_sp)
+ return nullptr;
+ data_offset = 0;
+ }
+ auto text = toStringRef(data_sp->GetData());
+ llvm::Optional<Header> header = Header::parse(text);
+ if (!header)
+ return nullptr;
+
+ // Update the data to contain the entire file if it doesn't already
+ if (data_sp->GetByteSize() < length) {
+ data_sp = MapFileData(*file, length, file_offset);
+ if (!data_sp)
+ return nullptr;
+ data_offset = 0;
+ }
+
+ return new ObjectFileBreakpad(module_sp, data_sp, data_offset, file,
+ file_offset, length, std::move(header->arch),
+ std::move(header->uuid));
+}
+
+ObjectFile *ObjectFileBreakpad::CreateMemoryInstance(
+ const ModuleSP &module_sp, DataBufferSP &data_sp,
+ const ProcessSP &process_sp, addr_t header_addr) {
+ return nullptr;
+}
+
+size_t ObjectFileBreakpad::GetModuleSpecifications(
+ const FileSpec &file, DataBufferSP &data_sp, offset_t data_offset,
+ offset_t file_offset, offset_t length, ModuleSpecList &specs) {
+ auto text = toStringRef(data_sp->GetData());
+ llvm::Optional<Header> header = Header::parse(text);
+ if (!header)
+ return 0;
+ ModuleSpec spec(file, std::move(header->arch));
+ spec.GetUUID() = std::move(header->uuid);
+ specs.Append(spec);
+ return 1;
+}
+
+ObjectFileBreakpad::ObjectFileBreakpad(const ModuleSP &module_sp,
+ DataBufferSP &data_sp,
+ offset_t data_offset,
+ const FileSpec *file, offset_t offset,
+ offset_t length, ArchSpec arch,
+ UUID uuid)
+ : ObjectFile(module_sp, file, offset, length, data_sp, data_offset),
+ m_arch(std::move(arch)), m_uuid(std::move(uuid)) {}
+
+bool ObjectFileBreakpad::ParseHeader() {
+ // We already parsed the header during initialization.
+ return true;
+}
+
+Symtab *ObjectFileBreakpad::GetSymtab() {
+ // TODO
+ return nullptr;
+}
+
+void ObjectFileBreakpad::CreateSections(SectionList &unified_section_list) {
+ if (m_sections_up)
+ return;
+ m_sections_up = llvm::make_unique<SectionList>();
+
+ llvm::Optional<Record::Kind> current_section;
+ offset_t section_start;
+ llvm::StringRef text = toStringRef(m_data.GetData());
+ uint32_t next_section_id = 1;
+ auto maybe_add_section = [&](const uint8_t *end_ptr) {
+ if (!current_section)
+ return; // We have been called before parsing the first line.
+
+ offset_t end_offset = end_ptr - m_data.GetDataStart();
+ auto section_sp = std::make_shared<Section>(
+ GetModule(), this, next_section_id++,
+ ConstString(toString(*current_section)), eSectionTypeOther,
+ /*file_vm_addr*/ 0, /*vm_size*/ 0, section_start,
+ end_offset - section_start, /*log2align*/ 0, /*flags*/ 0);
+ m_sections_up->AddSection(section_sp);
+ unified_section_list.AddSection(section_sp);
+ };
+ while (!text.empty()) {
+ llvm::StringRef line;
+ std::tie(line, text) = text.split('\n');
+
+ llvm::Optional<Record::Kind> next_section = Record::classify(line);
+ if (next_section == Record::Line) {
+ // Line records logically belong to the preceding Func record, so we put
+ // them in the same section.
+ next_section = Record::Func;
+ }
+ if (next_section == current_section)
+ continue;
+
+ // Changing sections, finish off the previous one, if there was any.
+ maybe_add_section(line.bytes_begin());
+ // And start a new one.
+ current_section = next_section;
+ section_start = line.bytes_begin() - m_data.GetDataStart();
+ }
+ // Finally, add the last section.
+ maybe_add_section(m_data.GetDataEnd());
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h
new file mode 100644
index 000000000000..e8885e0cc898
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h
@@ -0,0 +1,101 @@
+//===-- ObjectFileBreakpad.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_PLUGINS_OBJECTFILE_BREAKPAD_OBJECTFILEBREAKPAD_H
+#define LLDB_PLUGINS_OBJECTFILE_BREAKPAD_OBJECTFILEBREAKPAD_H
+
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/ArchSpec.h"
+
+namespace lldb_private {
+namespace breakpad {
+
+class ObjectFileBreakpad : public ObjectFile {
+public:
+ // Static Functions
+ static void Initialize();
+ static void Terminate();
+
+ static ConstString GetPluginNameStatic();
+ static const char *GetPluginDescriptionStatic() {
+ return "Breakpad object file reader.";
+ }
+
+ static ObjectFile *
+ CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset, const FileSpec *file,
+ lldb::offset_t file_offset, lldb::offset_t length);
+
+ static ObjectFile *CreateMemoryInstance(const lldb::ModuleSP &module_sp,
+ lldb::DataBufferSP &data_sp,
+ const lldb::ProcessSP &process_sp,
+ lldb::addr_t header_addr);
+
+ static size_t GetModuleSpecifications(const FileSpec &file,
+ lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset,
+ lldb::offset_t file_offset,
+ lldb::offset_t length,
+ ModuleSpecList &specs);
+
+ // PluginInterface protocol
+ ConstString GetPluginName() override { return GetPluginNameStatic(); }
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ // ObjectFile Protocol.
+
+ bool ParseHeader() override;
+
+ lldb::ByteOrder GetByteOrder() const override {
+ return m_arch.GetByteOrder();
+ }
+
+ bool IsExecutable() const override { return false; }
+
+ uint32_t GetAddressByteSize() const override {
+ return m_arch.GetAddressByteSize();
+ }
+
+ AddressClass GetAddressClass(lldb::addr_t file_addr) override {
+ return AddressClass::eInvalid;
+ }
+
+ Symtab *GetSymtab() override;
+
+ bool IsStripped() override { return false; }
+
+ void CreateSections(SectionList &unified_section_list) override;
+
+ void Dump(Stream *s) override {}
+
+ ArchSpec GetArchitecture() override { return m_arch; }
+
+ UUID GetUUID() override { return m_uuid; }
+
+ FileSpecList GetDebugSymbolFilePaths() override { return FileSpecList(); }
+
+ uint32_t GetDependentModules(FileSpecList &files) override { return 0; }
+
+ Type CalculateType() override { return eTypeDebugInfo; }
+
+ Strata CalculateStrata() override { return eStrataUser; }
+
+private:
+ ArchSpec m_arch;
+ UUID m_uuid;
+
+ ObjectFileBreakpad(const lldb::ModuleSP &module_sp,
+ lldb::DataBufferSP &data_sp, lldb::offset_t data_offset,
+ const FileSpec *file, lldb::offset_t offset,
+ lldb::offset_t length, ArchSpec arch, UUID uuid);
+};
+
+} // namespace breakpad
+} // namespace lldb_private
+#endif // LLDB_PLUGINS_OBJECTFILE_BREAKPAD_OBJECTFILEBREAKPAD_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.cpp b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.cpp
new file mode 100644
index 000000000000..aa9871071b0e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.cpp
@@ -0,0 +1,435 @@
+//===-- ELFHeader.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 <cstring>
+
+#include "lldb/Core/Section.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Stream.h"
+
+#include "ELFHeader.h"
+
+using namespace elf;
+using namespace lldb;
+using namespace llvm::ELF;
+
+// Static utility functions.
+//
+// GetMaxU64 and GetMaxS64 wrap the similarly named methods from DataExtractor
+// with error handling code and provide for parsing a sequence of values.
+static bool GetMaxU64(const lldb_private::DataExtractor &data,
+ lldb::offset_t *offset, uint64_t *value,
+ uint32_t byte_size) {
+ const lldb::offset_t saved_offset = *offset;
+ *value = data.GetMaxU64(offset, byte_size);
+ return *offset != saved_offset;
+}
+
+static bool GetMaxU64(const lldb_private::DataExtractor &data,
+ lldb::offset_t *offset, uint64_t *value,
+ uint32_t byte_size, uint32_t count) {
+ lldb::offset_t saved_offset = *offset;
+
+ for (uint32_t i = 0; i < count; ++i, ++value) {
+ if (!GetMaxU64(data, offset, value, byte_size)) {
+ *offset = saved_offset;
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool GetMaxS64(const lldb_private::DataExtractor &data,
+ lldb::offset_t *offset, int64_t *value,
+ uint32_t byte_size) {
+ const lldb::offset_t saved_offset = *offset;
+ *value = data.GetMaxS64(offset, byte_size);
+ return *offset != saved_offset;
+}
+
+static bool GetMaxS64(const lldb_private::DataExtractor &data,
+ lldb::offset_t *offset, int64_t *value,
+ uint32_t byte_size, uint32_t count) {
+ lldb::offset_t saved_offset = *offset;
+
+ for (uint32_t i = 0; i < count; ++i, ++value) {
+ if (!GetMaxS64(data, offset, value, byte_size)) {
+ *offset = saved_offset;
+ return false;
+ }
+ }
+ return true;
+}
+
+// ELFHeader
+
+ELFHeader::ELFHeader() { memset(this, 0, sizeof(ELFHeader)); }
+
+ByteOrder ELFHeader::GetByteOrder() const {
+ if (e_ident[EI_DATA] == ELFDATA2MSB)
+ return eByteOrderBig;
+ if (e_ident[EI_DATA] == ELFDATA2LSB)
+ return eByteOrderLittle;
+ return eByteOrderInvalid;
+}
+
+bool ELFHeader::HasHeaderExtension() const {
+ bool result = false;
+
+ // Check if any of these values looks like sentinel.
+ result |= e_phnum_hdr == 0xFFFF; // PN_XNUM
+ result |= e_shnum_hdr == SHN_UNDEF;
+ result |= e_shstrndx_hdr == SHN_XINDEX;
+
+ // If header extension is present, the section offset cannot be null.
+ result &= e_shoff != 0;
+
+ // Done.
+ return result;
+}
+
+void ELFHeader::ParseHeaderExtension(lldb_private::DataExtractor &data) {
+ // Extract section #0 header.
+ ELFSectionHeader section_zero;
+ lldb::offset_t offset = 0;
+ lldb_private::DataExtractor sh_data(data, e_shoff, e_shentsize);
+ bool ok = section_zero.Parse(sh_data, &offset);
+
+ // If we succeeded, fix the header.
+ if (ok) {
+ if (e_phnum_hdr == 0xFFFF) // PN_XNUM
+ e_phnum = section_zero.sh_info;
+ if (e_shnum_hdr == SHN_UNDEF)
+ e_shnum = section_zero.sh_size;
+ if (e_shstrndx_hdr == SHN_XINDEX)
+ e_shstrndx = section_zero.sh_link;
+ }
+}
+
+bool ELFHeader::Parse(lldb_private::DataExtractor &data,
+ lldb::offset_t *offset) {
+ // Read e_ident. This provides byte order and address size info.
+ if (data.GetU8(offset, &e_ident, EI_NIDENT) == nullptr)
+ return false;
+
+ const unsigned byte_size = Is32Bit() ? 4 : 8;
+ data.SetByteOrder(GetByteOrder());
+ data.SetAddressByteSize(byte_size);
+
+ // Read e_type and e_machine.
+ if (data.GetU16(offset, &e_type, 2) == nullptr)
+ return false;
+
+ // Read e_version.
+ if (data.GetU32(offset, &e_version, 1) == nullptr)
+ return false;
+
+ // Read e_entry, e_phoff and e_shoff.
+ if (!GetMaxU64(data, offset, &e_entry, byte_size, 3))
+ return false;
+
+ // Read e_flags.
+ if (data.GetU32(offset, &e_flags, 1) == nullptr)
+ return false;
+
+ // Read e_ehsize, e_phentsize, e_phnum, e_shentsize, e_shnum and e_shstrndx.
+ if (data.GetU16(offset, &e_ehsize, 6) == nullptr)
+ return false;
+
+ // Initialize e_phnum, e_shnum, and e_shstrndx with the values read from the
+ // header.
+ e_phnum = e_phnum_hdr;
+ e_shnum = e_shnum_hdr;
+ e_shstrndx = e_shstrndx_hdr;
+
+ // See if we have extended header in section #0.
+ if (HasHeaderExtension())
+ ParseHeaderExtension(data);
+
+ return true;
+}
+
+bool ELFHeader::MagicBytesMatch(const uint8_t *magic) {
+ return memcmp(magic, ElfMagic, strlen(ElfMagic)) == 0;
+}
+
+unsigned ELFHeader::AddressSizeInBytes(const uint8_t *magic) {
+ unsigned address_size = 0;
+
+ switch (magic[EI_CLASS]) {
+ case ELFCLASS32:
+ address_size = 4;
+ break;
+
+ case ELFCLASS64:
+ address_size = 8;
+ break;
+ }
+ return address_size;
+}
+
+unsigned ELFHeader::GetRelocationJumpSlotType() const {
+ unsigned slot = 0;
+
+ switch (e_machine) {
+ default:
+ assert(false && "architecture not supported");
+ break;
+ case EM_PPC:
+ slot = R_PPC_JMP_SLOT;
+ break;
+ case EM_PPC64:
+ slot = R_PPC64_JMP_SLOT;
+ break;
+ case EM_386:
+ case EM_IAMCU: // FIXME: is this correct?
+ slot = R_386_JUMP_SLOT;
+ break;
+ case EM_X86_64:
+ slot = R_X86_64_JUMP_SLOT;
+ break;
+ case EM_ARM:
+ slot = R_ARM_JUMP_SLOT;
+ break;
+ case EM_HEXAGON:
+ slot = R_HEX_JMP_SLOT;
+ break;
+ case EM_AARCH64:
+ slot = R_AARCH64_JUMP_SLOT;
+ break;
+ case EM_MIPS:
+ slot = R_MIPS_JUMP_SLOT;
+ break;
+ case EM_S390:
+ slot = R_390_JMP_SLOT;
+ break;
+ }
+
+ return slot;
+}
+
+// ELFSectionHeader
+
+ELFSectionHeader::ELFSectionHeader() {
+ memset(this, 0, sizeof(ELFSectionHeader));
+}
+
+bool ELFSectionHeader::Parse(const lldb_private::DataExtractor &data,
+ lldb::offset_t *offset) {
+ const unsigned byte_size = data.GetAddressByteSize();
+
+ // Read sh_name and sh_type.
+ if (data.GetU32(offset, &sh_name, 2) == nullptr)
+ return false;
+
+ // Read sh_flags.
+ if (!GetMaxU64(data, offset, &sh_flags, byte_size))
+ return false;
+
+ // Read sh_addr, sh_off and sh_size.
+ if (!GetMaxU64(data, offset, &sh_addr, byte_size, 3))
+ return false;
+
+ // Read sh_link and sh_info.
+ if (data.GetU32(offset, &sh_link, 2) == nullptr)
+ return false;
+
+ // Read sh_addralign and sh_entsize.
+ if (!GetMaxU64(data, offset, &sh_addralign, byte_size, 2))
+ return false;
+
+ return true;
+}
+
+// ELFSymbol
+
+ELFSymbol::ELFSymbol() { memset(this, 0, sizeof(ELFSymbol)); }
+
+#define ENUM_TO_CSTR(e) \
+ case e: \
+ return #e
+
+const char *ELFSymbol::bindingToCString(unsigned char binding) {
+ switch (binding) {
+ ENUM_TO_CSTR(STB_LOCAL);
+ ENUM_TO_CSTR(STB_GLOBAL);
+ ENUM_TO_CSTR(STB_WEAK);
+ ENUM_TO_CSTR(STB_LOOS);
+ ENUM_TO_CSTR(STB_HIOS);
+ ENUM_TO_CSTR(STB_LOPROC);
+ ENUM_TO_CSTR(STB_HIPROC);
+ }
+ return "";
+}
+
+const char *ELFSymbol::typeToCString(unsigned char type) {
+ switch (type) {
+ ENUM_TO_CSTR(STT_NOTYPE);
+ ENUM_TO_CSTR(STT_OBJECT);
+ ENUM_TO_CSTR(STT_FUNC);
+ ENUM_TO_CSTR(STT_SECTION);
+ ENUM_TO_CSTR(STT_FILE);
+ ENUM_TO_CSTR(STT_COMMON);
+ ENUM_TO_CSTR(STT_TLS);
+ ENUM_TO_CSTR(STT_GNU_IFUNC);
+ ENUM_TO_CSTR(STT_HIOS);
+ ENUM_TO_CSTR(STT_LOPROC);
+ ENUM_TO_CSTR(STT_HIPROC);
+ }
+ return "";
+}
+
+const char *ELFSymbol::sectionIndexToCString(
+ elf_half shndx, const lldb_private::SectionList *section_list) {
+ switch (shndx) {
+ ENUM_TO_CSTR(SHN_UNDEF);
+ ENUM_TO_CSTR(SHN_LOPROC);
+ ENUM_TO_CSTR(SHN_HIPROC);
+ ENUM_TO_CSTR(SHN_LOOS);
+ ENUM_TO_CSTR(SHN_HIOS);
+ ENUM_TO_CSTR(SHN_ABS);
+ ENUM_TO_CSTR(SHN_COMMON);
+ ENUM_TO_CSTR(SHN_XINDEX);
+ default: {
+ const lldb_private::Section *section =
+ section_list->GetSectionAtIndex(shndx).get();
+ if (section)
+ return section->GetName().AsCString("");
+ } break;
+ }
+ return "";
+}
+
+void ELFSymbol::Dump(lldb_private::Stream *s, uint32_t idx,
+ const lldb_private::DataExtractor *strtab_data,
+ const lldb_private::SectionList *section_list) {
+ s->Printf("[%3u] 0x%16.16" PRIx64 " 0x%16.16" PRIx64
+ " 0x%8.8x 0x%2.2x (%-10s %-13s) 0x%2.2x 0x%4.4x (%-10s) %s\n",
+ idx, st_value, st_size, st_name, st_info,
+ bindingToCString(getBinding()), typeToCString(getType()), st_other,
+ st_shndx, sectionIndexToCString(st_shndx, section_list),
+ strtab_data ? strtab_data->PeekCStr(st_name) : "");
+}
+
+bool ELFSymbol::Parse(const lldb_private::DataExtractor &data,
+ lldb::offset_t *offset) {
+ const unsigned byte_size = data.GetAddressByteSize();
+ const bool parsing_32 = byte_size == 4;
+
+ // Read st_name.
+ if (data.GetU32(offset, &st_name, 1) == nullptr)
+ return false;
+
+ if (parsing_32) {
+ // Read st_value and st_size.
+ if (!GetMaxU64(data, offset, &st_value, byte_size, 2))
+ return false;
+
+ // Read st_info and st_other.
+ if (data.GetU8(offset, &st_info, 2) == nullptr)
+ return false;
+
+ // Read st_shndx.
+ if (data.GetU16(offset, &st_shndx, 1) == nullptr)
+ return false;
+ } else {
+ // Read st_info and st_other.
+ if (data.GetU8(offset, &st_info, 2) == nullptr)
+ return false;
+
+ // Read st_shndx.
+ if (data.GetU16(offset, &st_shndx, 1) == nullptr)
+ return false;
+
+ // Read st_value and st_size.
+ if (data.GetU64(offset, &st_value, 2) == nullptr)
+ return false;
+ }
+ return true;
+}
+
+// ELFProgramHeader
+
+ELFProgramHeader::ELFProgramHeader() {
+ memset(this, 0, sizeof(ELFProgramHeader));
+}
+
+bool ELFProgramHeader::Parse(const lldb_private::DataExtractor &data,
+ lldb::offset_t *offset) {
+ const uint32_t byte_size = data.GetAddressByteSize();
+ const bool parsing_32 = byte_size == 4;
+
+ // Read p_type;
+ if (data.GetU32(offset, &p_type, 1) == nullptr)
+ return false;
+
+ if (parsing_32) {
+ // Read p_offset, p_vaddr, p_paddr, p_filesz and p_memsz.
+ if (!GetMaxU64(data, offset, &p_offset, byte_size, 5))
+ return false;
+
+ // Read p_flags.
+ if (data.GetU32(offset, &p_flags, 1) == nullptr)
+ return false;
+
+ // Read p_align.
+ if (!GetMaxU64(data, offset, &p_align, byte_size))
+ return false;
+ } else {
+ // Read p_flags.
+ if (data.GetU32(offset, &p_flags, 1) == nullptr)
+ return false;
+
+ // Read p_offset, p_vaddr, p_paddr, p_filesz, p_memsz and p_align.
+ if (!GetMaxU64(data, offset, &p_offset, byte_size, 6))
+ return false;
+ }
+
+ return true;
+}
+
+// ELFDynamic
+
+ELFDynamic::ELFDynamic() { memset(this, 0, sizeof(ELFDynamic)); }
+
+bool ELFDynamic::Parse(const lldb_private::DataExtractor &data,
+ lldb::offset_t *offset) {
+ const unsigned byte_size = data.GetAddressByteSize();
+ return GetMaxS64(data, offset, &d_tag, byte_size, 2);
+}
+
+// ELFRel
+
+ELFRel::ELFRel() { memset(this, 0, sizeof(ELFRel)); }
+
+bool ELFRel::Parse(const lldb_private::DataExtractor &data,
+ lldb::offset_t *offset) {
+ const unsigned byte_size = data.GetAddressByteSize();
+
+ // Read r_offset and r_info.
+ return GetMaxU64(data, offset, &r_offset, byte_size, 2) != false;
+}
+
+// ELFRela
+
+ELFRela::ELFRela() { memset(this, 0, sizeof(ELFRela)); }
+
+bool ELFRela::Parse(const lldb_private::DataExtractor &data,
+ lldb::offset_t *offset) {
+ const unsigned byte_size = data.GetAddressByteSize();
+
+ // Read r_offset and r_info.
+ if (!GetMaxU64(data, offset, &r_offset, byte_size, 2))
+ return false;
+
+ // Read r_addend;
+ if (!GetMaxS64(data, offset, &r_addend, byte_size))
+ return false;
+
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.h b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.h
new file mode 100644
index 000000000000..bb228e269d40
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.h
@@ -0,0 +1,394 @@
+//===-- ELFHeader.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
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file
+/// Generic structures and typedefs for ELF files.
+///
+/// This file provides definitions for the various entities comprising an ELF
+/// file. The structures are generic in the sense that they do not correspond
+/// to the exact binary layout of an ELF, but can be used to hold the
+/// information present in both 32 and 64 bit variants of the format. Each
+/// entity provides a \c Parse method which is capable of transparently
+/// reading both 32 and 64 bit instances of the object.
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ELFHeader_h_
+#define liblldb_ELFHeader_h_
+
+#include "llvm/BinaryFormat/ELF.h"
+
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+class DataExtractor;
+} // End namespace lldb_private.
+
+namespace elf {
+
+/// \name ELF type definitions.
+///
+/// Types used to represent the various components of ELF structures. All
+/// types are signed or unsigned integral types wide enough to hold values
+/// from both
+/// 32 and 64 bit ELF variants.
+//@{
+typedef uint64_t elf_addr;
+typedef uint64_t elf_off;
+typedef uint16_t elf_half;
+typedef uint32_t elf_word;
+typedef int32_t elf_sword;
+typedef uint64_t elf_size;
+typedef uint64_t elf_xword;
+typedef int64_t elf_sxword;
+//@}
+
+/// \class ELFHeader
+/// Generic representation of an ELF file header.
+///
+/// This object is used to identify the general attributes on an ELF file and
+/// to locate additional sections within the file.
+struct ELFHeader {
+ unsigned char e_ident[llvm::ELF::EI_NIDENT]; ///< ELF file identification.
+ elf_addr e_entry; ///< Virtual address program entry point.
+ elf_off e_phoff; ///< File offset of program header table.
+ elf_off e_shoff; ///< File offset of section header table.
+ elf_word e_flags; ///< Processor specific flags.
+ elf_word e_version; ///< Version of object file (always 1).
+ elf_half e_type; ///< Object file type.
+ elf_half e_machine; ///< Target architecture.
+ elf_half e_ehsize; ///< Byte size of the ELF header.
+ elf_half e_phentsize; ///< Size of a program header table entry.
+ elf_half e_phnum_hdr; ///< Number of program header entries.
+ elf_half e_shentsize; ///< Size of a section header table entry.
+ elf_half e_shnum_hdr; ///< Number of section header entries.
+ elf_half e_shstrndx_hdr; ///< String table section index.
+
+ // In some cases these numbers do not fit in 16 bits and they are
+ // stored outside of the header in section #0. Here are the actual
+ // values.
+ elf_word e_phnum; ///< Number of program header entries.
+ elf_word e_shnum; ///< Number of section header entries.
+ elf_word e_shstrndx; ///< String table section index.
+
+ ELFHeader();
+
+ /// Returns true if this is a 32 bit ELF file header.
+ ///
+ /// \return
+ /// True if this is a 32 bit ELF file header.
+ bool Is32Bit() const {
+ return e_ident[llvm::ELF::EI_CLASS] == llvm::ELF::ELFCLASS32;
+ }
+
+ /// Returns true if this is a 64 bit ELF file header.
+ ///
+ /// \return
+ /// True if this is a 64 bit ELF file header.
+ bool Is64Bit() const {
+ return e_ident[llvm::ELF::EI_CLASS] == llvm::ELF::ELFCLASS64;
+ }
+
+ /// The byte order of this ELF file header.
+ ///
+ /// \return
+ /// The byte order of this ELF file as described by the header.
+ lldb::ByteOrder GetByteOrder() const;
+
+ /// The jump slot relocation type of this ELF.
+ unsigned GetRelocationJumpSlotType() const;
+
+ /// Check if there should be header extension in section header #0
+ ///
+ /// \return
+ /// True if parsing the ELFHeader requires reading header extension
+ /// and false otherwise.
+ bool HasHeaderExtension() const;
+
+ /// Parse an ELFHeader entry starting at position \p offset and update the
+ /// data extractor with the address size and byte order attributes as
+ /// defined by the header.
+ ///
+ /// \param[in,out] data
+ /// The DataExtractor to read from. Updated with the address size and
+ /// byte order attributes appropriate to this header.
+ ///
+ /// \param[in,out] offset
+ /// Pointer to an offset in the data. On return the offset will be
+ /// advanced by the number of bytes read.
+ ///
+ /// \return
+ /// True if the ELFHeader was successfully read and false
+ /// otherwise.
+ bool Parse(lldb_private::DataExtractor &data, lldb::offset_t *offset);
+
+ /// Examines at most EI_NIDENT bytes starting from the given pointer and
+ /// determines if the magic ELF identification exists.
+ ///
+ /// \return
+ /// True if the given sequence of bytes identifies an ELF file.
+ static bool MagicBytesMatch(const uint8_t *magic);
+
+ /// Examines at most EI_NIDENT bytes starting from the given address and
+ /// determines the address size of the underlying ELF file. This function
+ /// should only be called on an pointer for which MagicBytesMatch returns
+ /// true.
+ ///
+ /// \return
+ /// The number of bytes forming an address in the ELF file (either 4 or
+ /// 8), else zero if the address size could not be determined.
+ static unsigned AddressSizeInBytes(const uint8_t *magic);
+
+private:
+
+ /// Parse an ELFHeader header extension entry. This method is called by
+ /// Parse().
+ ///
+ /// \param[in] data
+ /// The DataExtractor to read from.
+ void ParseHeaderExtension(lldb_private::DataExtractor &data);
+};
+
+/// \class ELFSectionHeader
+/// Generic representation of an ELF section header.
+struct ELFSectionHeader {
+ elf_word sh_name; ///< Section name string index.
+ elf_word sh_type; ///< Section type.
+ elf_xword sh_flags; ///< Section attributes.
+ elf_addr sh_addr; ///< Virtual address of the section in memory.
+ elf_off sh_offset; ///< Start of section from beginning of file.
+ elf_xword sh_size; ///< Number of bytes occupied in the file.
+ elf_word sh_link; ///< Index of associated section.
+ elf_word sh_info; ///< Extra section info (overloaded).
+ elf_xword sh_addralign; ///< Power of two alignment constraint.
+ elf_xword sh_entsize; ///< Byte size of each section entry.
+
+ ELFSectionHeader();
+
+ /// Parse an ELFSectionHeader entry from the given DataExtracter starting at
+ /// position \p offset.
+ ///
+ /// \param[in] data
+ /// The DataExtractor to read from. The address size of the extractor
+ /// determines if a 32 or 64 bit object should be read.
+ ///
+ /// \param[in,out] offset
+ /// Pointer to an offset in the data. On return the offset will be
+ /// advanced by the number of bytes read.
+ ///
+ /// \return
+ /// True if the ELFSectionHeader was successfully read and false
+ /// otherwise.
+ bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset);
+};
+
+/// \class ELFProgramHeader
+/// Generic representation of an ELF program header.
+struct ELFProgramHeader {
+ elf_word p_type; ///< Type of program segment.
+ elf_word p_flags; ///< Segment attributes.
+ elf_off p_offset; ///< Start of segment from beginning of file.
+ elf_addr p_vaddr; ///< Virtual address of segment in memory.
+ elf_addr p_paddr; ///< Physical address (for non-VM systems).
+ elf_xword p_filesz; ///< Byte size of the segment in file.
+ elf_xword p_memsz; ///< Byte size of the segment in memory.
+ elf_xword p_align; ///< Segment alignment constraint.
+
+ ELFProgramHeader();
+
+ /// Parse an ELFProgramHeader entry from the given DataExtractor starting at
+ /// position \p offset. The address size of the DataExtractor determines if
+ /// a 32 or 64 bit object is to be parsed.
+ ///
+ /// \param[in] data
+ /// The DataExtractor to read from. The address size of the extractor
+ /// determines if a 32 or 64 bit object should be read.
+ ///
+ /// \param[in,out] offset
+ /// Pointer to an offset in the data. On return the offset will be
+ /// advanced by the number of bytes read.
+ ///
+ /// \return
+ /// True if the ELFProgramHeader was successfully read and false
+ /// otherwise.
+ bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset);
+};
+
+/// \class ELFSymbol
+/// Represents a symbol within an ELF symbol table.
+struct ELFSymbol {
+ elf_addr st_value; ///< Absolute or relocatable address.
+ elf_xword st_size; ///< Size of the symbol or zero.
+ elf_word st_name; ///< Symbol name string index.
+ unsigned char st_info; ///< Symbol type and binding attributes.
+ unsigned char st_other; ///< Reserved for future use.
+ elf_half st_shndx; ///< Section to which this symbol applies.
+
+ ELFSymbol();
+
+ /// Returns the binding attribute of the st_info member.
+ unsigned char getBinding() const { return st_info >> 4; }
+
+ /// Returns the type attribute of the st_info member.
+ unsigned char getType() const { return st_info & 0x0F; }
+
+ /// Sets the binding and type of the st_info member.
+ void setBindingAndType(unsigned char binding, unsigned char type) {
+ st_info = (binding << 4) + (type & 0x0F);
+ }
+
+ static const char *bindingToCString(unsigned char binding);
+
+ static const char *typeToCString(unsigned char type);
+
+ static const char *
+ sectionIndexToCString(elf_half shndx,
+ const lldb_private::SectionList *section_list);
+
+ /// Parse an ELFSymbol entry from the given DataExtractor starting at
+ /// position \p offset. The address size of the DataExtractor determines if
+ /// a 32 or 64 bit object is to be parsed.
+ ///
+ /// \param[in] data
+ /// The DataExtractor to read from. The address size of the extractor
+ /// determines if a 32 or 64 bit object should be read.
+ ///
+ /// \param[in,out] offset
+ /// Pointer to an offset in the data. On return the offset will be
+ /// advanced by the number of bytes read.
+ ///
+ /// \return
+ /// True if the ELFSymbol was successfully read and false otherwise.
+ bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset);
+
+ void Dump(lldb_private::Stream *s, uint32_t idx,
+ const lldb_private::DataExtractor *strtab_data,
+ const lldb_private::SectionList *section_list);
+};
+
+/// \class ELFDynamic
+/// Represents an entry in an ELF dynamic table.
+struct ELFDynamic {
+ elf_sxword d_tag; ///< Type of dynamic table entry.
+ union {
+ elf_xword d_val; ///< Integer value of the table entry.
+ elf_addr d_ptr; ///< Pointer value of the table entry.
+ };
+
+ ELFDynamic();
+
+ /// Parse an ELFDynamic entry from the given DataExtractor starting at
+ /// position \p offset. The address size of the DataExtractor determines if
+ /// a 32 or 64 bit object is to be parsed.
+ ///
+ /// \param[in] data
+ /// The DataExtractor to read from. The address size of the extractor
+ /// determines if a 32 or 64 bit object should be read.
+ ///
+ /// \param[in,out] offset
+ /// Pointer to an offset in the data. On return the offset will be
+ /// advanced by the number of bytes read.
+ ///
+ /// \return
+ /// True if the ELFDynamic entry was successfully read and false
+ /// otherwise.
+ bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset);
+};
+
+/// \class ELFRel
+/// Represents a relocation entry with an implicit addend.
+struct ELFRel {
+ elf_addr r_offset; ///< Address of reference.
+ elf_xword r_info; ///< symbol index and type of relocation.
+
+ ELFRel();
+
+ /// Parse an ELFRel entry from the given DataExtractor starting at position
+ /// \p offset. The address size of the DataExtractor determines if a 32 or
+ /// 64 bit object is to be parsed.
+ ///
+ /// \param[in] data
+ /// The DataExtractor to read from. The address size of the extractor
+ /// determines if a 32 or 64 bit object should be read.
+ ///
+ /// \param[in,out] offset
+ /// Pointer to an offset in the data. On return the offset will be
+ /// advanced by the number of bytes read.
+ ///
+ /// \return
+ /// True if the ELFRel entry was successfully read and false otherwise.
+ bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset);
+
+ /// Returns the type when the given entry represents a 32-bit relocation.
+ static unsigned RelocType32(const ELFRel &rel) { return rel.r_info & 0x0ff; }
+
+ /// Returns the type when the given entry represents a 64-bit relocation.
+ static unsigned RelocType64(const ELFRel &rel) {
+ return rel.r_info & 0xffffffff;
+ }
+
+ /// Returns the symbol index when the given entry represents a 32-bit
+ /// relocation.
+ static unsigned RelocSymbol32(const ELFRel &rel) { return rel.r_info >> 8; }
+
+ /// Returns the symbol index when the given entry represents a 64-bit
+ /// relocation.
+ static unsigned RelocSymbol64(const ELFRel &rel) { return rel.r_info >> 32; }
+};
+
+/// \class ELFRela
+/// Represents a relocation entry with an explicit addend.
+struct ELFRela {
+ elf_addr r_offset; ///< Address of reference.
+ elf_xword r_info; ///< Symbol index and type of relocation.
+ elf_sxword r_addend; ///< Constant part of expression.
+
+ ELFRela();
+
+ /// Parse an ELFRela entry from the given DataExtractor starting at position
+ /// \p offset. The address size of the DataExtractor determines if a 32 or
+ /// 64 bit object is to be parsed.
+ ///
+ /// \param[in] data
+ /// The DataExtractor to read from. The address size of the extractor
+ /// determines if a 32 or 64 bit object should be read.
+ ///
+ /// \param[in,out] offset
+ /// Pointer to an offset in the data. On return the offset will be
+ /// advanced by the number of bytes read.
+ ///
+ /// \return
+ /// True if the ELFRela entry was successfully read and false otherwise.
+ bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset);
+
+ /// Returns the type when the given entry represents a 32-bit relocation.
+ static unsigned RelocType32(const ELFRela &rela) {
+ return rela.r_info & 0x0ff;
+ }
+
+ /// Returns the type when the given entry represents a 64-bit relocation.
+ static unsigned RelocType64(const ELFRela &rela) {
+ return rela.r_info & 0xffffffff;
+ }
+
+ /// Returns the symbol index when the given entry represents a 32-bit
+ /// relocation.
+ static unsigned RelocSymbol32(const ELFRela &rela) {
+ return rela.r_info >> 8;
+ }
+
+ /// Returns the symbol index when the given entry represents a 64-bit
+ /// relocation.
+ static unsigned RelocSymbol64(const ELFRela &rela) {
+ return rela.r_info >> 32;
+ }
+};
+
+} // End namespace elf.
+
+#endif // #ifndef liblldb_ELFHeader_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
new file mode 100644
index 000000000000..d62afa34bbe8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
@@ -0,0 +1,3380 @@
+//===-- ObjectFileELF.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 "ObjectFileELF.h"
+
+#include <algorithm>
+#include <cassert>
+#include <unordered_map>
+
+#include "lldb/Core/FileSpecList.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Symbol/DWARFCallFrameInfo.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RangeMap.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/Timer.h"
+
+#include "llvm/ADT/IntervalMap.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Object/Decompressor.h"
+#include "llvm/Support/ARMBuildAttributes.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/MipsABIFlags.h"
+
+#define CASE_AND_STREAM(s, def, width) \
+ case def: \
+ s->Printf("%-*s", width, #def); \
+ break;
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace elf;
+using namespace llvm::ELF;
+
+namespace {
+
+// ELF note owner definitions
+const char *const LLDB_NT_OWNER_FREEBSD = "FreeBSD";
+const char *const LLDB_NT_OWNER_GNU = "GNU";
+const char *const LLDB_NT_OWNER_NETBSD = "NetBSD";
+const char *const LLDB_NT_OWNER_NETBSDCORE = "NetBSD-CORE";
+const char *const LLDB_NT_OWNER_OPENBSD = "OpenBSD";
+const char *const LLDB_NT_OWNER_ANDROID = "Android";
+const char *const LLDB_NT_OWNER_CORE = "CORE";
+const char *const LLDB_NT_OWNER_LINUX = "LINUX";
+
+// ELF note type definitions
+const elf_word LLDB_NT_FREEBSD_ABI_TAG = 0x01;
+const elf_word LLDB_NT_FREEBSD_ABI_SIZE = 4;
+
+const elf_word LLDB_NT_GNU_ABI_TAG = 0x01;
+const elf_word LLDB_NT_GNU_ABI_SIZE = 16;
+
+const elf_word LLDB_NT_GNU_BUILD_ID_TAG = 0x03;
+
+const elf_word LLDB_NT_NETBSD_IDENT_TAG = 1;
+const elf_word LLDB_NT_NETBSD_IDENT_DESCSZ = 4;
+const elf_word LLDB_NT_NETBSD_IDENT_NAMESZ = 7;
+const elf_word LLDB_NT_NETBSD_PROCINFO = 1;
+
+// GNU ABI note OS constants
+const elf_word LLDB_NT_GNU_ABI_OS_LINUX = 0x00;
+const elf_word LLDB_NT_GNU_ABI_OS_HURD = 0x01;
+const elf_word LLDB_NT_GNU_ABI_OS_SOLARIS = 0x02;
+
+// LLDB_NT_OWNER_CORE and LLDB_NT_OWNER_LINUX note contants
+#define NT_PRSTATUS 1
+#define NT_PRFPREG 2
+#define NT_PRPSINFO 3
+#define NT_TASKSTRUCT 4
+#define NT_AUXV 6
+#define NT_SIGINFO 0x53494749
+#define NT_FILE 0x46494c45
+#define NT_PRXFPREG 0x46e62b7f
+#define NT_PPC_VMX 0x100
+#define NT_PPC_SPE 0x101
+#define NT_PPC_VSX 0x102
+#define NT_386_TLS 0x200
+#define NT_386_IOPERM 0x201
+#define NT_X86_XSTATE 0x202
+#define NT_S390_HIGH_GPRS 0x300
+#define NT_S390_TIMER 0x301
+#define NT_S390_TODCMP 0x302
+#define NT_S390_TODPREG 0x303
+#define NT_S390_CTRS 0x304
+#define NT_S390_PREFIX 0x305
+#define NT_S390_LAST_BREAK 0x306
+#define NT_S390_SYSTEM_CALL 0x307
+#define NT_S390_TDB 0x308
+#define NT_S390_VXRS_LOW 0x309
+#define NT_S390_VXRS_HIGH 0x30a
+#define NT_ARM_VFP 0x400
+#define NT_ARM_TLS 0x401
+#define NT_ARM_HW_BREAK 0x402
+#define NT_ARM_HW_WATCH 0x403
+#define NT_ARM_SYSTEM_CALL 0x404
+#define NT_METAG_CBUF 0x500
+#define NT_METAG_RPIPE 0x501
+#define NT_METAG_TLS 0x502
+
+//===----------------------------------------------------------------------===//
+/// \class ELFRelocation
+/// Generic wrapper for ELFRel and ELFRela.
+///
+/// This helper class allows us to parse both ELFRel and ELFRela relocation
+/// entries in a generic manner.
+class ELFRelocation {
+public:
+ /// Constructs an ELFRelocation entry with a personality as given by @p
+ /// type.
+ ///
+ /// \param type Either DT_REL or DT_RELA. Any other value is invalid.
+ ELFRelocation(unsigned type);
+
+ ~ELFRelocation();
+
+ bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset);
+
+ static unsigned RelocType32(const ELFRelocation &rel);
+
+ static unsigned RelocType64(const ELFRelocation &rel);
+
+ static unsigned RelocSymbol32(const ELFRelocation &rel);
+
+ static unsigned RelocSymbol64(const ELFRelocation &rel);
+
+ static unsigned RelocOffset32(const ELFRelocation &rel);
+
+ static unsigned RelocOffset64(const ELFRelocation &rel);
+
+ static unsigned RelocAddend32(const ELFRelocation &rel);
+
+ static unsigned RelocAddend64(const ELFRelocation &rel);
+
+private:
+ typedef llvm::PointerUnion<ELFRel *, ELFRela *> RelocUnion;
+
+ RelocUnion reloc;
+};
+
+ELFRelocation::ELFRelocation(unsigned type) {
+ if (type == DT_REL || type == SHT_REL)
+ reloc = new ELFRel();
+ else if (type == DT_RELA || type == SHT_RELA)
+ reloc = new ELFRela();
+ else {
+ assert(false && "unexpected relocation type");
+ reloc = static_cast<ELFRel *>(nullptr);
+ }
+}
+
+ELFRelocation::~ELFRelocation() {
+ if (reloc.is<ELFRel *>())
+ delete reloc.get<ELFRel *>();
+ else
+ delete reloc.get<ELFRela *>();
+}
+
+bool ELFRelocation::Parse(const lldb_private::DataExtractor &data,
+ lldb::offset_t *offset) {
+ if (reloc.is<ELFRel *>())
+ return reloc.get<ELFRel *>()->Parse(data, offset);
+ else
+ return reloc.get<ELFRela *>()->Parse(data, offset);
+}
+
+unsigned ELFRelocation::RelocType32(const ELFRelocation &rel) {
+ if (rel.reloc.is<ELFRel *>())
+ return ELFRel::RelocType32(*rel.reloc.get<ELFRel *>());
+ else
+ return ELFRela::RelocType32(*rel.reloc.get<ELFRela *>());
+}
+
+unsigned ELFRelocation::RelocType64(const ELFRelocation &rel) {
+ if (rel.reloc.is<ELFRel *>())
+ return ELFRel::RelocType64(*rel.reloc.get<ELFRel *>());
+ else
+ return ELFRela::RelocType64(*rel.reloc.get<ELFRela *>());
+}
+
+unsigned ELFRelocation::RelocSymbol32(const ELFRelocation &rel) {
+ if (rel.reloc.is<ELFRel *>())
+ return ELFRel::RelocSymbol32(*rel.reloc.get<ELFRel *>());
+ else
+ return ELFRela::RelocSymbol32(*rel.reloc.get<ELFRela *>());
+}
+
+unsigned ELFRelocation::RelocSymbol64(const ELFRelocation &rel) {
+ if (rel.reloc.is<ELFRel *>())
+ return ELFRel::RelocSymbol64(*rel.reloc.get<ELFRel *>());
+ else
+ return ELFRela::RelocSymbol64(*rel.reloc.get<ELFRela *>());
+}
+
+unsigned ELFRelocation::RelocOffset32(const ELFRelocation &rel) {
+ if (rel.reloc.is<ELFRel *>())
+ return rel.reloc.get<ELFRel *>()->r_offset;
+ else
+ return rel.reloc.get<ELFRela *>()->r_offset;
+}
+
+unsigned ELFRelocation::RelocOffset64(const ELFRelocation &rel) {
+ if (rel.reloc.is<ELFRel *>())
+ return rel.reloc.get<ELFRel *>()->r_offset;
+ else
+ return rel.reloc.get<ELFRela *>()->r_offset;
+}
+
+unsigned ELFRelocation::RelocAddend32(const ELFRelocation &rel) {
+ if (rel.reloc.is<ELFRel *>())
+ return 0;
+ else
+ return rel.reloc.get<ELFRela *>()->r_addend;
+}
+
+unsigned ELFRelocation::RelocAddend64(const ELFRelocation &rel) {
+ if (rel.reloc.is<ELFRel *>())
+ return 0;
+ else
+ return rel.reloc.get<ELFRela *>()->r_addend;
+}
+
+} // end anonymous namespace
+
+static user_id_t SegmentID(size_t PHdrIndex) { return ~PHdrIndex; }
+
+bool ELFNote::Parse(const DataExtractor &data, lldb::offset_t *offset) {
+ // Read all fields.
+ if (data.GetU32(offset, &n_namesz, 3) == nullptr)
+ return false;
+
+ // The name field is required to be nul-terminated, and n_namesz includes the
+ // terminating nul in observed implementations (contrary to the ELF-64 spec).
+ // A special case is needed for cores generated by some older Linux versions,
+ // which write a note named "CORE" without a nul terminator and n_namesz = 4.
+ if (n_namesz == 4) {
+ char buf[4];
+ if (data.ExtractBytes(*offset, 4, data.GetByteOrder(), buf) != 4)
+ return false;
+ if (strncmp(buf, "CORE", 4) == 0) {
+ n_name = "CORE";
+ *offset += 4;
+ return true;
+ }
+ }
+
+ const char *cstr = data.GetCStr(offset, llvm::alignTo(n_namesz, 4));
+ if (cstr == nullptr) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS));
+ if (log)
+ log->Printf("Failed to parse note name lacking nul terminator");
+
+ return false;
+ }
+ n_name = cstr;
+ return true;
+}
+
+static uint32_t mipsVariantFromElfFlags (const elf::ELFHeader &header) {
+ const uint32_t mips_arch = header.e_flags & llvm::ELF::EF_MIPS_ARCH;
+ uint32_t endian = header.e_ident[EI_DATA];
+ uint32_t arch_variant = ArchSpec::eMIPSSubType_unknown;
+ uint32_t fileclass = header.e_ident[EI_CLASS];
+
+ // If there aren't any elf flags available (e.g core elf file) then return
+ // default
+ // 32 or 64 bit arch (without any architecture revision) based on object file's class.
+ if (header.e_type == ET_CORE) {
+ switch (fileclass) {
+ case llvm::ELF::ELFCLASS32:
+ return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips32el
+ : ArchSpec::eMIPSSubType_mips32;
+ case llvm::ELF::ELFCLASS64:
+ return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips64el
+ : ArchSpec::eMIPSSubType_mips64;
+ default:
+ return arch_variant;
+ }
+ }
+
+ switch (mips_arch) {
+ case llvm::ELF::EF_MIPS_ARCH_1:
+ case llvm::ELF::EF_MIPS_ARCH_2:
+ case llvm::ELF::EF_MIPS_ARCH_32:
+ return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips32el
+ : ArchSpec::eMIPSSubType_mips32;
+ case llvm::ELF::EF_MIPS_ARCH_32R2:
+ return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips32r2el
+ : ArchSpec::eMIPSSubType_mips32r2;
+ case llvm::ELF::EF_MIPS_ARCH_32R6:
+ return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips32r6el
+ : ArchSpec::eMIPSSubType_mips32r6;
+ case llvm::ELF::EF_MIPS_ARCH_3:
+ case llvm::ELF::EF_MIPS_ARCH_4:
+ case llvm::ELF::EF_MIPS_ARCH_5:
+ case llvm::ELF::EF_MIPS_ARCH_64:
+ return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips64el
+ : ArchSpec::eMIPSSubType_mips64;
+ case llvm::ELF::EF_MIPS_ARCH_64R2:
+ return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips64r2el
+ : ArchSpec::eMIPSSubType_mips64r2;
+ case llvm::ELF::EF_MIPS_ARCH_64R6:
+ return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips64r6el
+ : ArchSpec::eMIPSSubType_mips64r6;
+ default:
+ break;
+ }
+
+ return arch_variant;
+}
+
+static uint32_t subTypeFromElfHeader(const elf::ELFHeader &header) {
+ if (header.e_machine == llvm::ELF::EM_MIPS)
+ return mipsVariantFromElfFlags(header);
+
+ return LLDB_INVALID_CPUTYPE;
+}
+
+// Arbitrary constant used as UUID prefix for core files.
+const uint32_t ObjectFileELF::g_core_uuid_magic(0xE210C);
+
+// Static methods.
+void ObjectFileELF::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ CreateMemoryInstance, GetModuleSpecifications);
+}
+
+void ObjectFileELF::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ObjectFileELF::GetPluginNameStatic() {
+ static ConstString g_name("elf");
+ return g_name;
+}
+
+const char *ObjectFileELF::GetPluginDescriptionStatic() {
+ return "ELF object file reader.";
+}
+
+ObjectFile *ObjectFileELF::CreateInstance(const lldb::ModuleSP &module_sp,
+ DataBufferSP &data_sp,
+ lldb::offset_t data_offset,
+ const lldb_private::FileSpec *file,
+ lldb::offset_t file_offset,
+ lldb::offset_t length) {
+ if (!data_sp) {
+ data_sp = MapFileData(*file, length, file_offset);
+ if (!data_sp)
+ return nullptr;
+ data_offset = 0;
+ }
+
+ assert(data_sp);
+
+ if (data_sp->GetByteSize() <= (llvm::ELF::EI_NIDENT + data_offset))
+ return nullptr;
+
+ const uint8_t *magic = data_sp->GetBytes() + data_offset;
+ if (!ELFHeader::MagicBytesMatch(magic))
+ return nullptr;
+
+ // Update the data to contain the entire file if it doesn't already
+ if (data_sp->GetByteSize() < length) {
+ data_sp = MapFileData(*file, length, file_offset);
+ if (!data_sp)
+ return nullptr;
+ data_offset = 0;
+ magic = data_sp->GetBytes();
+ }
+
+ unsigned address_size = ELFHeader::AddressSizeInBytes(magic);
+ if (address_size == 4 || address_size == 8) {
+ std::unique_ptr<ObjectFileELF> objfile_up(new ObjectFileELF(
+ module_sp, data_sp, data_offset, file, file_offset, length));
+ ArchSpec spec = objfile_up->GetArchitecture();
+ if (spec && objfile_up->SetModulesArchitecture(spec))
+ return objfile_up.release();
+ }
+
+ return nullptr;
+}
+
+ObjectFile *ObjectFileELF::CreateMemoryInstance(
+ const lldb::ModuleSP &module_sp, DataBufferSP &data_sp,
+ const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) {
+ if (data_sp && data_sp->GetByteSize() > (llvm::ELF::EI_NIDENT)) {
+ const uint8_t *magic = data_sp->GetBytes();
+ if (ELFHeader::MagicBytesMatch(magic)) {
+ unsigned address_size = ELFHeader::AddressSizeInBytes(magic);
+ if (address_size == 4 || address_size == 8) {
+ std::unique_ptr<ObjectFileELF> objfile_up(
+ new ObjectFileELF(module_sp, data_sp, process_sp, header_addr));
+ ArchSpec spec = objfile_up->GetArchitecture();
+ if (spec && objfile_up->SetModulesArchitecture(spec))
+ return objfile_up.release();
+ }
+ }
+ }
+ return nullptr;
+}
+
+bool ObjectFileELF::MagicBytesMatch(DataBufferSP &data_sp,
+ lldb::addr_t data_offset,
+ lldb::addr_t data_length) {
+ if (data_sp &&
+ data_sp->GetByteSize() > (llvm::ELF::EI_NIDENT + data_offset)) {
+ const uint8_t *magic = data_sp->GetBytes() + data_offset;
+ return ELFHeader::MagicBytesMatch(magic);
+ }
+ return false;
+}
+
+/*
+ * crc function from http://svnweb.freebsd.org/base/head/sys/libkern/crc32.c
+ *
+ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ */
+static uint32_t calc_crc32(uint32_t crc, const void *buf, size_t size) {
+ static const uint32_t g_crc32_tab[] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d};
+ const uint8_t *p = (const uint8_t *)buf;
+
+ crc = crc ^ ~0U;
+ while (size--)
+ crc = g_crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
+ return crc ^ ~0U;
+}
+
+static uint32_t calc_gnu_debuglink_crc32(const void *buf, size_t size) {
+ return calc_crc32(0U, buf, size);
+}
+
+uint32_t ObjectFileELF::CalculateELFNotesSegmentsCRC32(
+ const ProgramHeaderColl &program_headers, DataExtractor &object_data) {
+
+ uint32_t core_notes_crc = 0;
+
+ for (const ELFProgramHeader &H : program_headers) {
+ if (H.p_type == llvm::ELF::PT_NOTE) {
+ const elf_off ph_offset = H.p_offset;
+ const size_t ph_size = H.p_filesz;
+
+ DataExtractor segment_data;
+ if (segment_data.SetData(object_data, ph_offset, ph_size) != ph_size) {
+ // The ELF program header contained incorrect data, probably corefile
+ // is incomplete or corrupted.
+ break;
+ }
+
+ core_notes_crc = calc_crc32(core_notes_crc, segment_data.GetDataStart(),
+ segment_data.GetByteSize());
+ }
+ }
+
+ return core_notes_crc;
+}
+
+static const char *OSABIAsCString(unsigned char osabi_byte) {
+#define _MAKE_OSABI_CASE(x) \
+ case x: \
+ return #x
+ switch (osabi_byte) {
+ _MAKE_OSABI_CASE(ELFOSABI_NONE);
+ _MAKE_OSABI_CASE(ELFOSABI_HPUX);
+ _MAKE_OSABI_CASE(ELFOSABI_NETBSD);
+ _MAKE_OSABI_CASE(ELFOSABI_GNU);
+ _MAKE_OSABI_CASE(ELFOSABI_HURD);
+ _MAKE_OSABI_CASE(ELFOSABI_SOLARIS);
+ _MAKE_OSABI_CASE(ELFOSABI_AIX);
+ _MAKE_OSABI_CASE(ELFOSABI_IRIX);
+ _MAKE_OSABI_CASE(ELFOSABI_FREEBSD);
+ _MAKE_OSABI_CASE(ELFOSABI_TRU64);
+ _MAKE_OSABI_CASE(ELFOSABI_MODESTO);
+ _MAKE_OSABI_CASE(ELFOSABI_OPENBSD);
+ _MAKE_OSABI_CASE(ELFOSABI_OPENVMS);
+ _MAKE_OSABI_CASE(ELFOSABI_NSK);
+ _MAKE_OSABI_CASE(ELFOSABI_AROS);
+ _MAKE_OSABI_CASE(ELFOSABI_FENIXOS);
+ _MAKE_OSABI_CASE(ELFOSABI_C6000_ELFABI);
+ _MAKE_OSABI_CASE(ELFOSABI_C6000_LINUX);
+ _MAKE_OSABI_CASE(ELFOSABI_ARM);
+ _MAKE_OSABI_CASE(ELFOSABI_STANDALONE);
+ default:
+ return "<unknown-osabi>";
+ }
+#undef _MAKE_OSABI_CASE
+}
+
+//
+// WARNING : This function is being deprecated
+// It's functionality has moved to ArchSpec::SetArchitecture This function is
+// only being kept to validate the move.
+//
+// TODO : Remove this function
+static bool GetOsFromOSABI(unsigned char osabi_byte,
+ llvm::Triple::OSType &ostype) {
+ switch (osabi_byte) {
+ case ELFOSABI_AIX:
+ ostype = llvm::Triple::OSType::AIX;
+ break;
+ case ELFOSABI_FREEBSD:
+ ostype = llvm::Triple::OSType::FreeBSD;
+ break;
+ case ELFOSABI_GNU:
+ ostype = llvm::Triple::OSType::Linux;
+ break;
+ case ELFOSABI_NETBSD:
+ ostype = llvm::Triple::OSType::NetBSD;
+ break;
+ case ELFOSABI_OPENBSD:
+ ostype = llvm::Triple::OSType::OpenBSD;
+ break;
+ case ELFOSABI_SOLARIS:
+ ostype = llvm::Triple::OSType::Solaris;
+ break;
+ default:
+ ostype = llvm::Triple::OSType::UnknownOS;
+ }
+ return ostype != llvm::Triple::OSType::UnknownOS;
+}
+
+size_t ObjectFileELF::GetModuleSpecifications(
+ const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset, lldb::offset_t file_offset,
+ lldb::offset_t length, lldb_private::ModuleSpecList &specs) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES));
+
+ const size_t initial_count = specs.GetSize();
+
+ if (ObjectFileELF::MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) {
+ DataExtractor data;
+ data.SetData(data_sp);
+ elf::ELFHeader header;
+ lldb::offset_t header_offset = data_offset;
+ if (header.Parse(data, &header_offset)) {
+ if (data_sp) {
+ ModuleSpec spec(file);
+
+ const uint32_t sub_type = subTypeFromElfHeader(header);
+ spec.GetArchitecture().SetArchitecture(
+ eArchTypeELF, header.e_machine, sub_type, header.e_ident[EI_OSABI]);
+
+ if (spec.GetArchitecture().IsValid()) {
+ llvm::Triple::OSType ostype;
+ llvm::Triple::VendorType vendor;
+ llvm::Triple::OSType spec_ostype =
+ spec.GetArchitecture().GetTriple().getOS();
+
+ if (log)
+ log->Printf("ObjectFileELF::%s file '%s' module OSABI: %s",
+ __FUNCTION__, file.GetPath().c_str(),
+ OSABIAsCString(header.e_ident[EI_OSABI]));
+
+ // SetArchitecture should have set the vendor to unknown
+ vendor = spec.GetArchitecture().GetTriple().getVendor();
+ assert(vendor == llvm::Triple::UnknownVendor);
+ UNUSED_IF_ASSERT_DISABLED(vendor);
+
+ //
+ // Validate it is ok to remove GetOsFromOSABI
+ GetOsFromOSABI(header.e_ident[EI_OSABI], ostype);
+ assert(spec_ostype == ostype);
+ if (spec_ostype != llvm::Triple::OSType::UnknownOS) {
+ if (log)
+ log->Printf("ObjectFileELF::%s file '%s' set ELF module OS type "
+ "from ELF header OSABI.",
+ __FUNCTION__, file.GetPath().c_str());
+ }
+
+ data_sp = MapFileData(file, -1, file_offset);
+ if (data_sp)
+ data.SetData(data_sp);
+ // In case there is header extension in the section #0, the header we
+ // parsed above could have sentinel values for e_phnum, e_shnum, and
+ // e_shstrndx. In this case we need to reparse the header with a
+ // bigger data source to get the actual values.
+ if (header.HasHeaderExtension()) {
+ lldb::offset_t header_offset = data_offset;
+ header.Parse(data, &header_offset);
+ }
+
+ uint32_t gnu_debuglink_crc = 0;
+ std::string gnu_debuglink_file;
+ SectionHeaderColl section_headers;
+ lldb_private::UUID &uuid = spec.GetUUID();
+
+ GetSectionHeaderInfo(section_headers, data, header, uuid,
+ gnu_debuglink_file, gnu_debuglink_crc,
+ spec.GetArchitecture());
+
+ llvm::Triple &spec_triple = spec.GetArchitecture().GetTriple();
+
+ if (log)
+ log->Printf("ObjectFileELF::%s file '%s' module set to triple: %s "
+ "(architecture %s)",
+ __FUNCTION__, file.GetPath().c_str(),
+ spec_triple.getTriple().c_str(),
+ spec.GetArchitecture().GetArchitectureName());
+
+ if (!uuid.IsValid()) {
+ uint32_t core_notes_crc = 0;
+
+ if (!gnu_debuglink_crc) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ lldb_private::Timer scoped_timer(
+ func_cat,
+ "Calculating module crc32 %s with size %" PRIu64 " KiB",
+ file.GetLastPathComponent().AsCString(),
+ (FileSystem::Instance().GetByteSize(file) - file_offset) /
+ 1024);
+
+ // For core files - which usually don't happen to have a
+ // gnu_debuglink, and are pretty bulky - calculating whole
+ // contents crc32 would be too much of luxury. Thus we will need
+ // to fallback to something simpler.
+ if (header.e_type == llvm::ELF::ET_CORE) {
+ ProgramHeaderColl program_headers;
+ GetProgramHeaderInfo(program_headers, data, header);
+
+ core_notes_crc =
+ CalculateELFNotesSegmentsCRC32(program_headers, data);
+ } else {
+ gnu_debuglink_crc = calc_gnu_debuglink_crc32(
+ data.GetDataStart(), data.GetByteSize());
+ }
+ }
+ using u32le = llvm::support::ulittle32_t;
+ if (gnu_debuglink_crc) {
+ // Use 4 bytes of crc from the .gnu_debuglink section.
+ u32le data(gnu_debuglink_crc);
+ uuid = UUID::fromData(&data, sizeof(data));
+ } else if (core_notes_crc) {
+ // Use 8 bytes - first 4 bytes for *magic* prefix, mainly to make
+ // it look different form .gnu_debuglink crc followed by 4 bytes
+ // of note segments crc.
+ u32le data[] = {u32le(g_core_uuid_magic), u32le(core_notes_crc)};
+ uuid = UUID::fromData(data, sizeof(data));
+ }
+ }
+
+ specs.Append(spec);
+ }
+ }
+ }
+ }
+
+ return specs.GetSize() - initial_count;
+}
+
+// PluginInterface protocol
+lldb_private::ConstString ObjectFileELF::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ObjectFileELF::GetPluginVersion() { return m_plugin_version; }
+// ObjectFile protocol
+
+ObjectFileELF::ObjectFileELF(const lldb::ModuleSP &module_sp,
+ DataBufferSP &data_sp, lldb::offset_t data_offset,
+ const FileSpec *file, lldb::offset_t file_offset,
+ lldb::offset_t length)
+ : ObjectFile(module_sp, file, file_offset, length, data_sp, data_offset),
+ m_header(), m_uuid(), m_gnu_debuglink_file(), m_gnu_debuglink_crc(0),
+ m_program_headers(), m_section_headers(), m_dynamic_symbols(),
+ m_filespec_up(), m_entry_point_address(), m_arch_spec() {
+ if (file)
+ m_file = *file;
+ ::memset(&m_header, 0, sizeof(m_header));
+}
+
+ObjectFileELF::ObjectFileELF(const lldb::ModuleSP &module_sp,
+ DataBufferSP &header_data_sp,
+ const lldb::ProcessSP &process_sp,
+ addr_t header_addr)
+ : ObjectFile(module_sp, process_sp, header_addr, header_data_sp),
+ m_header(), m_uuid(), m_gnu_debuglink_file(), m_gnu_debuglink_crc(0),
+ m_program_headers(), m_section_headers(), m_dynamic_symbols(),
+ m_filespec_up(), m_entry_point_address(), m_arch_spec() {
+ ::memset(&m_header, 0, sizeof(m_header));
+}
+
+ObjectFileELF::~ObjectFileELF() {}
+
+bool ObjectFileELF::IsExecutable() const {
+ return ((m_header.e_type & ET_EXEC) != 0) || (m_header.e_entry != 0);
+}
+
+bool ObjectFileELF::SetLoadAddress(Target &target, lldb::addr_t value,
+ bool value_is_offset) {
+ ModuleSP module_sp = GetModule();
+ if (module_sp) {
+ size_t num_loaded_sections = 0;
+ SectionList *section_list = GetSectionList();
+ if (section_list) {
+ if (!value_is_offset) {
+ addr_t base = GetBaseAddress().GetFileAddress();
+ if (base == LLDB_INVALID_ADDRESS)
+ return false;
+ value -= base;
+ }
+
+ const size_t num_sections = section_list->GetSize();
+ size_t sect_idx = 0;
+
+ for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) {
+ // Iterate through the object file sections to find all of the sections
+ // that have SHF_ALLOC in their flag bits.
+ SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx));
+ if (section_sp->Test(SHF_ALLOC) ||
+ section_sp->GetType() == eSectionTypeContainer) {
+ lldb::addr_t load_addr = section_sp->GetFileAddress();
+ // We don't want to update the load address of a section with type
+ // eSectionTypeAbsoluteAddress as they already have the absolute load
+ // address already specified
+ if (section_sp->GetType() != eSectionTypeAbsoluteAddress)
+ load_addr += value;
+
+ // On 32-bit systems the load address have to fit into 4 bytes. The
+ // rest of the bytes are the overflow from the addition.
+ if (GetAddressByteSize() == 4)
+ load_addr &= 0xFFFFFFFF;
+
+ if (target.GetSectionLoadList().SetSectionLoadAddress(section_sp,
+ load_addr))
+ ++num_loaded_sections;
+ }
+ }
+ return num_loaded_sections > 0;
+ }
+ }
+ return false;
+}
+
+ByteOrder ObjectFileELF::GetByteOrder() const {
+ if (m_header.e_ident[EI_DATA] == ELFDATA2MSB)
+ return eByteOrderBig;
+ if (m_header.e_ident[EI_DATA] == ELFDATA2LSB)
+ return eByteOrderLittle;
+ return eByteOrderInvalid;
+}
+
+uint32_t ObjectFileELF::GetAddressByteSize() const {
+ return m_data.GetAddressByteSize();
+}
+
+AddressClass ObjectFileELF::GetAddressClass(addr_t file_addr) {
+ Symtab *symtab = GetSymtab();
+ if (!symtab)
+ return AddressClass::eUnknown;
+
+ // The address class is determined based on the symtab. Ask it from the
+ // object file what contains the symtab information.
+ ObjectFile *symtab_objfile = symtab->GetObjectFile();
+ if (symtab_objfile != nullptr && symtab_objfile != this)
+ return symtab_objfile->GetAddressClass(file_addr);
+
+ auto res = ObjectFile::GetAddressClass(file_addr);
+ if (res != AddressClass::eCode)
+ return res;
+
+ auto ub = m_address_class_map.upper_bound(file_addr);
+ if (ub == m_address_class_map.begin()) {
+ // No entry in the address class map before the address. Return default
+ // address class for an address in a code section.
+ return AddressClass::eCode;
+ }
+
+ // Move iterator to the address class entry preceding address
+ --ub;
+
+ return ub->second;
+}
+
+size_t ObjectFileELF::SectionIndex(const SectionHeaderCollIter &I) {
+ return std::distance(m_section_headers.begin(), I);
+}
+
+size_t ObjectFileELF::SectionIndex(const SectionHeaderCollConstIter &I) const {
+ return std::distance(m_section_headers.begin(), I);
+}
+
+bool ObjectFileELF::ParseHeader() {
+ lldb::offset_t offset = 0;
+ return m_header.Parse(m_data, &offset);
+}
+
+UUID ObjectFileELF::GetUUID() {
+ // Need to parse the section list to get the UUIDs, so make sure that's been
+ // done.
+ if (!ParseSectionHeaders() && GetType() != ObjectFile::eTypeCoreFile)
+ return UUID();
+
+ if (!m_uuid) {
+ using u32le = llvm::support::ulittle32_t;
+ if (GetType() == ObjectFile::eTypeCoreFile) {
+ uint32_t core_notes_crc = 0;
+
+ if (!ParseProgramHeaders())
+ return UUID();
+
+ core_notes_crc =
+ CalculateELFNotesSegmentsCRC32(m_program_headers, m_data);
+
+ if (core_notes_crc) {
+ // Use 8 bytes - first 4 bytes for *magic* prefix, mainly to make it
+ // look different form .gnu_debuglink crc - followed by 4 bytes of note
+ // segments crc.
+ u32le data[] = {u32le(g_core_uuid_magic), u32le(core_notes_crc)};
+ m_uuid = UUID::fromData(data, sizeof(data));
+ }
+ } else {
+ if (!m_gnu_debuglink_crc)
+ m_gnu_debuglink_crc = calc_gnu_debuglink_crc32(m_data.GetDataStart(),
+ m_data.GetByteSize());
+ if (m_gnu_debuglink_crc) {
+ // Use 4 bytes of crc from the .gnu_debuglink section.
+ u32le data(m_gnu_debuglink_crc);
+ m_uuid = UUID::fromData(&data, sizeof(data));
+ }
+ }
+ }
+
+ return m_uuid;
+}
+
+lldb_private::FileSpecList ObjectFileELF::GetDebugSymbolFilePaths() {
+ FileSpecList file_spec_list;
+
+ if (!m_gnu_debuglink_file.empty()) {
+ FileSpec file_spec(m_gnu_debuglink_file);
+ file_spec_list.Append(file_spec);
+ }
+ return file_spec_list;
+}
+
+uint32_t ObjectFileELF::GetDependentModules(FileSpecList &files) {
+ size_t num_modules = ParseDependentModules();
+ uint32_t num_specs = 0;
+
+ for (unsigned i = 0; i < num_modules; ++i) {
+ if (files.AppendIfUnique(m_filespec_up->GetFileSpecAtIndex(i)))
+ num_specs++;
+ }
+
+ return num_specs;
+}
+
+Address ObjectFileELF::GetImageInfoAddress(Target *target) {
+ if (!ParseDynamicSymbols())
+ return Address();
+
+ SectionList *section_list = GetSectionList();
+ if (!section_list)
+ return Address();
+
+ // Find the SHT_DYNAMIC (.dynamic) section.
+ SectionSP dynsym_section_sp(
+ section_list->FindSectionByType(eSectionTypeELFDynamicLinkInfo, true));
+ if (!dynsym_section_sp)
+ return Address();
+ assert(dynsym_section_sp->GetObjectFile() == this);
+
+ user_id_t dynsym_id = dynsym_section_sp->GetID();
+ const ELFSectionHeaderInfo *dynsym_hdr = GetSectionHeaderByIndex(dynsym_id);
+ if (!dynsym_hdr)
+ return Address();
+
+ for (size_t i = 0; i < m_dynamic_symbols.size(); ++i) {
+ ELFDynamic &symbol = m_dynamic_symbols[i];
+
+ if (symbol.d_tag == DT_DEBUG) {
+ // Compute the offset as the number of previous entries plus the size of
+ // d_tag.
+ addr_t offset = i * dynsym_hdr->sh_entsize + GetAddressByteSize();
+ return Address(dynsym_section_sp, offset);
+ }
+ // MIPS executables uses DT_MIPS_RLD_MAP_REL to support PIE. DT_MIPS_RLD_MAP
+ // exists in non-PIE.
+ else if ((symbol.d_tag == DT_MIPS_RLD_MAP ||
+ symbol.d_tag == DT_MIPS_RLD_MAP_REL) &&
+ target) {
+ addr_t offset = i * dynsym_hdr->sh_entsize + GetAddressByteSize();
+ addr_t dyn_base = dynsym_section_sp->GetLoadBaseAddress(target);
+ if (dyn_base == LLDB_INVALID_ADDRESS)
+ return Address();
+
+ Status error;
+ if (symbol.d_tag == DT_MIPS_RLD_MAP) {
+ // DT_MIPS_RLD_MAP tag stores an absolute address of the debug pointer.
+ Address addr;
+ if (target->ReadPointerFromMemory(dyn_base + offset, false, error,
+ addr))
+ return addr;
+ }
+ if (symbol.d_tag == DT_MIPS_RLD_MAP_REL) {
+ // DT_MIPS_RLD_MAP_REL tag stores the offset to the debug pointer,
+ // relative to the address of the tag.
+ uint64_t rel_offset;
+ rel_offset = target->ReadUnsignedIntegerFromMemory(
+ dyn_base + offset, false, GetAddressByteSize(), UINT64_MAX, error);
+ if (error.Success() && rel_offset != UINT64_MAX) {
+ Address addr;
+ addr_t debug_ptr_address =
+ dyn_base + (offset - GetAddressByteSize()) + rel_offset;
+ addr.SetOffset(debug_ptr_address);
+ return addr;
+ }
+ }
+ }
+ }
+
+ return Address();
+}
+
+lldb_private::Address ObjectFileELF::GetEntryPointAddress() {
+ if (m_entry_point_address.IsValid())
+ return m_entry_point_address;
+
+ if (!ParseHeader() || !IsExecutable())
+ return m_entry_point_address;
+
+ SectionList *section_list = GetSectionList();
+ addr_t offset = m_header.e_entry;
+
+ if (!section_list)
+ m_entry_point_address.SetOffset(offset);
+ else
+ m_entry_point_address.ResolveAddressUsingFileSections(offset, section_list);
+ return m_entry_point_address;
+}
+
+Address ObjectFileELF::GetBaseAddress() {
+ for (const auto &EnumPHdr : llvm::enumerate(ProgramHeaders())) {
+ const ELFProgramHeader &H = EnumPHdr.value();
+ if (H.p_type != PT_LOAD)
+ continue;
+
+ return Address(
+ GetSectionList()->FindSectionByID(SegmentID(EnumPHdr.index())), 0);
+ }
+ return LLDB_INVALID_ADDRESS;
+}
+
+// ParseDependentModules
+size_t ObjectFileELF::ParseDependentModules() {
+ if (m_filespec_up)
+ return m_filespec_up->GetSize();
+
+ m_filespec_up.reset(new FileSpecList());
+
+ if (!ParseSectionHeaders())
+ return 0;
+
+ SectionList *section_list = GetSectionList();
+ if (!section_list)
+ return 0;
+
+ // Find the SHT_DYNAMIC section.
+ Section *dynsym =
+ section_list->FindSectionByType(eSectionTypeELFDynamicLinkInfo, true)
+ .get();
+ if (!dynsym)
+ return 0;
+ assert(dynsym->GetObjectFile() == this);
+
+ const ELFSectionHeaderInfo *header = GetSectionHeaderByIndex(dynsym->GetID());
+ if (!header)
+ return 0;
+ // sh_link: section header index of string table used by entries in the
+ // section.
+ Section *dynstr = section_list->FindSectionByID(header->sh_link).get();
+ if (!dynstr)
+ return 0;
+
+ DataExtractor dynsym_data;
+ DataExtractor dynstr_data;
+ if (ReadSectionData(dynsym, dynsym_data) &&
+ ReadSectionData(dynstr, dynstr_data)) {
+ ELFDynamic symbol;
+ const lldb::offset_t section_size = dynsym_data.GetByteSize();
+ lldb::offset_t offset = 0;
+
+ // The only type of entries we are concerned with are tagged DT_NEEDED,
+ // yielding the name of a required library.
+ while (offset < section_size) {
+ if (!symbol.Parse(dynsym_data, &offset))
+ break;
+
+ if (symbol.d_tag != DT_NEEDED)
+ continue;
+
+ uint32_t str_index = static_cast<uint32_t>(symbol.d_val);
+ const char *lib_name = dynstr_data.PeekCStr(str_index);
+ FileSpec file_spec(lib_name);
+ FileSystem::Instance().Resolve(file_spec);
+ m_filespec_up->Append(file_spec);
+ }
+ }
+
+ return m_filespec_up->GetSize();
+}
+
+// GetProgramHeaderInfo
+size_t ObjectFileELF::GetProgramHeaderInfo(ProgramHeaderColl &program_headers,
+ DataExtractor &object_data,
+ const ELFHeader &header) {
+ // We have already parsed the program headers
+ if (!program_headers.empty())
+ return program_headers.size();
+
+ // If there are no program headers to read we are done.
+ if (header.e_phnum == 0)
+ return 0;
+
+ program_headers.resize(header.e_phnum);
+ if (program_headers.size() != header.e_phnum)
+ return 0;
+
+ const size_t ph_size = header.e_phnum * header.e_phentsize;
+ const elf_off ph_offset = header.e_phoff;
+ DataExtractor data;
+ if (data.SetData(object_data, ph_offset, ph_size) != ph_size)
+ return 0;
+
+ uint32_t idx;
+ lldb::offset_t offset;
+ for (idx = 0, offset = 0; idx < header.e_phnum; ++idx) {
+ if (!program_headers[idx].Parse(data, &offset))
+ break;
+ }
+
+ if (idx < program_headers.size())
+ program_headers.resize(idx);
+
+ return program_headers.size();
+}
+
+// ParseProgramHeaders
+bool ObjectFileELF::ParseProgramHeaders() {
+ return GetProgramHeaderInfo(m_program_headers, m_data, m_header) != 0;
+}
+
+lldb_private::Status
+ObjectFileELF::RefineModuleDetailsFromNote(lldb_private::DataExtractor &data,
+ lldb_private::ArchSpec &arch_spec,
+ lldb_private::UUID &uuid) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES));
+ Status error;
+
+ lldb::offset_t offset = 0;
+
+ while (true) {
+ // Parse the note header. If this fails, bail out.
+ const lldb::offset_t note_offset = offset;
+ ELFNote note = ELFNote();
+ if (!note.Parse(data, &offset)) {
+ // We're done.
+ return error;
+ }
+
+ if (log)
+ log->Printf("ObjectFileELF::%s parsing note name='%s', type=%" PRIu32,
+ __FUNCTION__, note.n_name.c_str(), note.n_type);
+
+ // Process FreeBSD ELF notes.
+ if ((note.n_name == LLDB_NT_OWNER_FREEBSD) &&
+ (note.n_type == LLDB_NT_FREEBSD_ABI_TAG) &&
+ (note.n_descsz == LLDB_NT_FREEBSD_ABI_SIZE)) {
+ // Pull out the min version info.
+ uint32_t version_info;
+ if (data.GetU32(&offset, &version_info, 1) == nullptr) {
+ error.SetErrorString("failed to read FreeBSD ABI note payload");
+ return error;
+ }
+
+ // Convert the version info into a major/minor number.
+ const uint32_t version_major = version_info / 100000;
+ const uint32_t version_minor = (version_info / 1000) % 100;
+
+ char os_name[32];
+ snprintf(os_name, sizeof(os_name), "freebsd%" PRIu32 ".%" PRIu32,
+ version_major, version_minor);
+
+ // Set the elf OS version to FreeBSD. Also clear the vendor.
+ arch_spec.GetTriple().setOSName(os_name);
+ arch_spec.GetTriple().setVendor(llvm::Triple::VendorType::UnknownVendor);
+
+ if (log)
+ log->Printf("ObjectFileELF::%s detected FreeBSD %" PRIu32 ".%" PRIu32
+ ".%" PRIu32,
+ __FUNCTION__, version_major, version_minor,
+ static_cast<uint32_t>(version_info % 1000));
+ }
+ // Process GNU ELF notes.
+ else if (note.n_name == LLDB_NT_OWNER_GNU) {
+ switch (note.n_type) {
+ case LLDB_NT_GNU_ABI_TAG:
+ if (note.n_descsz == LLDB_NT_GNU_ABI_SIZE) {
+ // Pull out the min OS version supporting the ABI.
+ uint32_t version_info[4];
+ if (data.GetU32(&offset, &version_info[0], note.n_descsz / 4) ==
+ nullptr) {
+ error.SetErrorString("failed to read GNU ABI note payload");
+ return error;
+ }
+
+ // Set the OS per the OS field.
+ switch (version_info[0]) {
+ case LLDB_NT_GNU_ABI_OS_LINUX:
+ arch_spec.GetTriple().setOS(llvm::Triple::OSType::Linux);
+ arch_spec.GetTriple().setVendor(
+ llvm::Triple::VendorType::UnknownVendor);
+ if (log)
+ log->Printf(
+ "ObjectFileELF::%s detected Linux, min version %" PRIu32
+ ".%" PRIu32 ".%" PRIu32,
+ __FUNCTION__, version_info[1], version_info[2],
+ version_info[3]);
+ // FIXME we have the minimal version number, we could be propagating
+ // that. version_info[1] = OS Major, version_info[2] = OS Minor,
+ // version_info[3] = Revision.
+ break;
+ case LLDB_NT_GNU_ABI_OS_HURD:
+ arch_spec.GetTriple().setOS(llvm::Triple::OSType::UnknownOS);
+ arch_spec.GetTriple().setVendor(
+ llvm::Triple::VendorType::UnknownVendor);
+ if (log)
+ log->Printf("ObjectFileELF::%s detected Hurd (unsupported), min "
+ "version %" PRIu32 ".%" PRIu32 ".%" PRIu32,
+ __FUNCTION__, version_info[1], version_info[2],
+ version_info[3]);
+ break;
+ case LLDB_NT_GNU_ABI_OS_SOLARIS:
+ arch_spec.GetTriple().setOS(llvm::Triple::OSType::Solaris);
+ arch_spec.GetTriple().setVendor(
+ llvm::Triple::VendorType::UnknownVendor);
+ if (log)
+ log->Printf(
+ "ObjectFileELF::%s detected Solaris, min version %" PRIu32
+ ".%" PRIu32 ".%" PRIu32,
+ __FUNCTION__, version_info[1], version_info[2],
+ version_info[3]);
+ break;
+ default:
+ if (log)
+ log->Printf(
+ "ObjectFileELF::%s unrecognized OS in note, id %" PRIu32
+ ", min version %" PRIu32 ".%" PRIu32 ".%" PRIu32,
+ __FUNCTION__, version_info[0], version_info[1],
+ version_info[2], version_info[3]);
+ break;
+ }
+ }
+ break;
+
+ case LLDB_NT_GNU_BUILD_ID_TAG:
+ // Only bother processing this if we don't already have the uuid set.
+ if (!uuid.IsValid()) {
+ // 16 bytes is UUID|MD5, 20 bytes is SHA1. Other linkers may produce a
+ // build-id of a different length. Accept it as long as it's at least
+ // 4 bytes as it will be better than our own crc32.
+ if (note.n_descsz >= 4) {
+ if (const uint8_t *buf = data.PeekData(offset, note.n_descsz)) {
+ // Save the build id as the UUID for the module.
+ uuid = UUID::fromData(buf, note.n_descsz);
+ } else {
+ error.SetErrorString("failed to read GNU_BUILD_ID note payload");
+ return error;
+ }
+ }
+ }
+ break;
+ }
+ if (arch_spec.IsMIPS() &&
+ arch_spec.GetTriple().getOS() == llvm::Triple::OSType::UnknownOS)
+ // The note.n_name == LLDB_NT_OWNER_GNU is valid for Linux platform
+ arch_spec.GetTriple().setOS(llvm::Triple::OSType::Linux);
+ }
+ // Process NetBSD ELF executables and shared libraries
+ else if ((note.n_name == LLDB_NT_OWNER_NETBSD) &&
+ (note.n_type == LLDB_NT_NETBSD_IDENT_TAG) &&
+ (note.n_descsz == LLDB_NT_NETBSD_IDENT_DESCSZ) &&
+ (note.n_namesz == LLDB_NT_NETBSD_IDENT_NAMESZ)) {
+ // Pull out the version info.
+ uint32_t version_info;
+ if (data.GetU32(&offset, &version_info, 1) == nullptr) {
+ error.SetErrorString("failed to read NetBSD ABI note payload");
+ return error;
+ }
+ // Convert the version info into a major/minor/patch number.
+ // #define __NetBSD_Version__ MMmmrrpp00
+ //
+ // M = major version
+ // m = minor version; a minor number of 99 indicates current.
+ // r = 0 (since NetBSD 3.0 not used)
+ // p = patchlevel
+ const uint32_t version_major = version_info / 100000000;
+ const uint32_t version_minor = (version_info % 100000000) / 1000000;
+ const uint32_t version_patch = (version_info % 10000) / 100;
+ // Set the elf OS version to NetBSD. Also clear the vendor.
+ arch_spec.GetTriple().setOSName(
+ llvm::formatv("netbsd{0}.{1}.{2}", version_major, version_minor,
+ version_patch).str());
+ arch_spec.GetTriple().setVendor(llvm::Triple::VendorType::UnknownVendor);
+ }
+ // Process NetBSD ELF core(5) notes
+ else if ((note.n_name == LLDB_NT_OWNER_NETBSDCORE) &&
+ (note.n_type == LLDB_NT_NETBSD_PROCINFO)) {
+ // Set the elf OS version to NetBSD. Also clear the vendor.
+ arch_spec.GetTriple().setOS(llvm::Triple::OSType::NetBSD);
+ arch_spec.GetTriple().setVendor(llvm::Triple::VendorType::UnknownVendor);
+ }
+ // Process OpenBSD ELF notes.
+ else if (note.n_name == LLDB_NT_OWNER_OPENBSD) {
+ // Set the elf OS version to OpenBSD. Also clear the vendor.
+ arch_spec.GetTriple().setOS(llvm::Triple::OSType::OpenBSD);
+ arch_spec.GetTriple().setVendor(llvm::Triple::VendorType::UnknownVendor);
+ } else if (note.n_name == LLDB_NT_OWNER_ANDROID) {
+ arch_spec.GetTriple().setOS(llvm::Triple::OSType::Linux);
+ arch_spec.GetTriple().setEnvironment(
+ llvm::Triple::EnvironmentType::Android);
+ } else if (note.n_name == LLDB_NT_OWNER_LINUX) {
+ // This is sometimes found in core files and usually contains extended
+ // register info
+ arch_spec.GetTriple().setOS(llvm::Triple::OSType::Linux);
+ } else if (note.n_name == LLDB_NT_OWNER_CORE) {
+ // Parse the NT_FILE to look for stuff in paths to shared libraries As
+ // the contents look like this in a 64 bit ELF core file: count =
+ // 0x000000000000000a (10) page_size = 0x0000000000001000 (4096) Index
+ // start end file_ofs path =====
+ // 0x0000000000401000 0x0000000000000000 /tmp/a.out [ 1]
+ // 0x0000000000600000 0x0000000000601000 0x0000000000000000 /tmp/a.out [
+ // 2] 0x0000000000601000 0x0000000000602000 0x0000000000000001 /tmp/a.out
+ // [ 3] 0x00007fa79c9ed000 0x00007fa79cba8000 0x0000000000000000
+ // /lib/x86_64-linux-gnu/libc-2.19.so [ 4] 0x00007fa79cba8000
+ // 0x00007fa79cda7000 0x00000000000001bb /lib/x86_64-linux-
+ // gnu/libc-2.19.so [ 5] 0x00007fa79cda7000 0x00007fa79cdab000
+ // 0x00000000000001ba /lib/x86_64-linux-gnu/libc-2.19.so [ 6]
+ // 0x00007fa79cdab000 0x00007fa79cdad000 0x00000000000001be /lib/x86_64
+ // -linux-gnu/libc-2.19.so [ 7] 0x00007fa79cdb2000 0x00007fa79cdd5000
+ // 0x0000000000000000 /lib/x86_64-linux-gnu/ld-2.19.so [ 8]
+ // 0x00007fa79cfd4000 0x00007fa79cfd5000 0x0000000000000022 /lib/x86_64
+ // -linux-gnu/ld-2.19.so [ 9] 0x00007fa79cfd5000 0x00007fa79cfd6000
+ // 0x0000000000000023 /lib/x86_64-linux-gnu/ld-2.19.so In the 32 bit ELFs
+ // the count, page_size, start, end, file_ofs are uint32_t For reference:
+ // see readelf source code (in binutils).
+ if (note.n_type == NT_FILE) {
+ uint64_t count = data.GetAddress(&offset);
+ const char *cstr;
+ data.GetAddress(&offset); // Skip page size
+ offset += count * 3 *
+ data.GetAddressByteSize(); // Skip all start/end/file_ofs
+ for (size_t i = 0; i < count; ++i) {
+ cstr = data.GetCStr(&offset);
+ if (cstr == nullptr) {
+ error.SetErrorStringWithFormat("ObjectFileELF::%s trying to read "
+ "at an offset after the end "
+ "(GetCStr returned nullptr)",
+ __FUNCTION__);
+ return error;
+ }
+ llvm::StringRef path(cstr);
+ if (path.contains("/lib/x86_64-linux-gnu") || path.contains("/lib/i386-linux-gnu")) {
+ arch_spec.GetTriple().setOS(llvm::Triple::OSType::Linux);
+ break;
+ }
+ }
+ if (arch_spec.IsMIPS() &&
+ arch_spec.GetTriple().getOS() == llvm::Triple::OSType::UnknownOS)
+ // In case of MIPSR6, the LLDB_NT_OWNER_GNU note is missing for some
+ // cases (e.g. compile with -nostdlib) Hence set OS to Linux
+ arch_spec.GetTriple().setOS(llvm::Triple::OSType::Linux);
+ }
+ }
+
+ // Calculate the offset of the next note just in case "offset" has been
+ // used to poke at the contents of the note data
+ offset = note_offset + note.GetByteSize();
+ }
+
+ return error;
+}
+
+void ObjectFileELF::ParseARMAttributes(DataExtractor &data, uint64_t length,
+ ArchSpec &arch_spec) {
+ lldb::offset_t Offset = 0;
+
+ uint8_t FormatVersion = data.GetU8(&Offset);
+ if (FormatVersion != llvm::ARMBuildAttrs::Format_Version)
+ return;
+
+ Offset = Offset + sizeof(uint32_t); // Section Length
+ llvm::StringRef VendorName = data.GetCStr(&Offset);
+
+ if (VendorName != "aeabi")
+ return;
+
+ if (arch_spec.GetTriple().getEnvironment() ==
+ llvm::Triple::UnknownEnvironment)
+ arch_spec.GetTriple().setEnvironment(llvm::Triple::EABI);
+
+ while (Offset < length) {
+ uint8_t Tag = data.GetU8(&Offset);
+ uint32_t Size = data.GetU32(&Offset);
+
+ if (Tag != llvm::ARMBuildAttrs::File || Size == 0)
+ continue;
+
+ while (Offset < length) {
+ uint64_t Tag = data.GetULEB128(&Offset);
+ switch (Tag) {
+ default:
+ if (Tag < 32)
+ data.GetULEB128(&Offset);
+ else if (Tag % 2 == 0)
+ data.GetULEB128(&Offset);
+ else
+ data.GetCStr(&Offset);
+
+ break;
+
+ case llvm::ARMBuildAttrs::CPU_raw_name:
+ case llvm::ARMBuildAttrs::CPU_name:
+ data.GetCStr(&Offset);
+
+ break;
+
+ case llvm::ARMBuildAttrs::ABI_VFP_args: {
+ uint64_t VFPArgs = data.GetULEB128(&Offset);
+
+ if (VFPArgs == llvm::ARMBuildAttrs::BaseAAPCS) {
+ if (arch_spec.GetTriple().getEnvironment() ==
+ llvm::Triple::UnknownEnvironment ||
+ arch_spec.GetTriple().getEnvironment() == llvm::Triple::EABIHF)
+ arch_spec.GetTriple().setEnvironment(llvm::Triple::EABI);
+
+ arch_spec.SetFlags(ArchSpec::eARM_abi_soft_float);
+ } else if (VFPArgs == llvm::ARMBuildAttrs::HardFPAAPCS) {
+ if (arch_spec.GetTriple().getEnvironment() ==
+ llvm::Triple::UnknownEnvironment ||
+ arch_spec.GetTriple().getEnvironment() == llvm::Triple::EABI)
+ arch_spec.GetTriple().setEnvironment(llvm::Triple::EABIHF);
+
+ arch_spec.SetFlags(ArchSpec::eARM_abi_hard_float);
+ }
+
+ break;
+ }
+ }
+ }
+ }
+}
+
+// GetSectionHeaderInfo
+size_t ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl &section_headers,
+ DataExtractor &object_data,
+ const elf::ELFHeader &header,
+ lldb_private::UUID &uuid,
+ std::string &gnu_debuglink_file,
+ uint32_t &gnu_debuglink_crc,
+ ArchSpec &arch_spec) {
+ // Don't reparse the section headers if we already did that.
+ if (!section_headers.empty())
+ return section_headers.size();
+
+ // Only initialize the arch_spec to okay defaults if they're not already set.
+ // We'll refine this with note data as we parse the notes.
+ if (arch_spec.GetTriple().getOS() == llvm::Triple::OSType::UnknownOS) {
+ llvm::Triple::OSType ostype;
+ llvm::Triple::OSType spec_ostype;
+ const uint32_t sub_type = subTypeFromElfHeader(header);
+ arch_spec.SetArchitecture(eArchTypeELF, header.e_machine, sub_type,
+ header.e_ident[EI_OSABI]);
+
+ // Validate if it is ok to remove GetOsFromOSABI. Note, that now the OS is
+ // determined based on EI_OSABI flag and the info extracted from ELF notes
+ // (see RefineModuleDetailsFromNote). However in some cases that still
+ // might be not enough: for example a shared library might not have any
+ // notes at all and have EI_OSABI flag set to System V, as result the OS
+ // will be set to UnknownOS.
+ GetOsFromOSABI(header.e_ident[EI_OSABI], ostype);
+ spec_ostype = arch_spec.GetTriple().getOS();
+ assert(spec_ostype == ostype);
+ UNUSED_IF_ASSERT_DISABLED(spec_ostype);
+ }
+
+ if (arch_spec.GetMachine() == llvm::Triple::mips ||
+ arch_spec.GetMachine() == llvm::Triple::mipsel ||
+ arch_spec.GetMachine() == llvm::Triple::mips64 ||
+ arch_spec.GetMachine() == llvm::Triple::mips64el) {
+ switch (header.e_flags & llvm::ELF::EF_MIPS_ARCH_ASE) {
+ case llvm::ELF::EF_MIPS_MICROMIPS:
+ arch_spec.SetFlags(ArchSpec::eMIPSAse_micromips);
+ break;
+ case llvm::ELF::EF_MIPS_ARCH_ASE_M16:
+ arch_spec.SetFlags(ArchSpec::eMIPSAse_mips16);
+ break;
+ case llvm::ELF::EF_MIPS_ARCH_ASE_MDMX:
+ arch_spec.SetFlags(ArchSpec::eMIPSAse_mdmx);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (arch_spec.GetMachine() == llvm::Triple::arm ||
+ arch_spec.GetMachine() == llvm::Triple::thumb) {
+ if (header.e_flags & llvm::ELF::EF_ARM_SOFT_FLOAT)
+ arch_spec.SetFlags(ArchSpec::eARM_abi_soft_float);
+ else if (header.e_flags & llvm::ELF::EF_ARM_VFP_FLOAT)
+ arch_spec.SetFlags(ArchSpec::eARM_abi_hard_float);
+ }
+
+ // If there are no section headers we are done.
+ if (header.e_shnum == 0)
+ return 0;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES));
+
+ section_headers.resize(header.e_shnum);
+ if (section_headers.size() != header.e_shnum)
+ return 0;
+
+ const size_t sh_size = header.e_shnum * header.e_shentsize;
+ const elf_off sh_offset = header.e_shoff;
+ DataExtractor sh_data;
+ if (sh_data.SetData(object_data, sh_offset, sh_size) != sh_size)
+ return 0;
+
+ uint32_t idx;
+ lldb::offset_t offset;
+ for (idx = 0, offset = 0; idx < header.e_shnum; ++idx) {
+ if (!section_headers[idx].Parse(sh_data, &offset))
+ break;
+ }
+ if (idx < section_headers.size())
+ section_headers.resize(idx);
+
+ const unsigned strtab_idx = header.e_shstrndx;
+ if (strtab_idx && strtab_idx < section_headers.size()) {
+ const ELFSectionHeaderInfo &sheader = section_headers[strtab_idx];
+ const size_t byte_size = sheader.sh_size;
+ const Elf64_Off offset = sheader.sh_offset;
+ lldb_private::DataExtractor shstr_data;
+
+ if (shstr_data.SetData(object_data, offset, byte_size) == byte_size) {
+ for (SectionHeaderCollIter I = section_headers.begin();
+ I != section_headers.end(); ++I) {
+ static ConstString g_sect_name_gnu_debuglink(".gnu_debuglink");
+ const ELFSectionHeaderInfo &sheader = *I;
+ const uint64_t section_size =
+ sheader.sh_type == SHT_NOBITS ? 0 : sheader.sh_size;
+ ConstString name(shstr_data.PeekCStr(I->sh_name));
+
+ I->section_name = name;
+
+ if (arch_spec.IsMIPS()) {
+ uint32_t arch_flags = arch_spec.GetFlags();
+ DataExtractor data;
+ if (sheader.sh_type == SHT_MIPS_ABIFLAGS) {
+
+ if (section_size && (data.SetData(object_data, sheader.sh_offset,
+ section_size) == section_size)) {
+ // MIPS ASE Mask is at offset 12 in MIPS.abiflags section
+ lldb::offset_t offset = 12; // MIPS ABI Flags Version: 0
+ arch_flags |= data.GetU32(&offset);
+
+ // The floating point ABI is at offset 7
+ offset = 7;
+ switch (data.GetU8(&offset)) {
+ case llvm::Mips::Val_GNU_MIPS_ABI_FP_ANY:
+ arch_flags |= lldb_private::ArchSpec::eMIPS_ABI_FP_ANY;
+ break;
+ case llvm::Mips::Val_GNU_MIPS_ABI_FP_DOUBLE:
+ arch_flags |= lldb_private::ArchSpec::eMIPS_ABI_FP_DOUBLE;
+ break;
+ case llvm::Mips::Val_GNU_MIPS_ABI_FP_SINGLE:
+ arch_flags |= lldb_private::ArchSpec::eMIPS_ABI_FP_SINGLE;
+ break;
+ case llvm::Mips::Val_GNU_MIPS_ABI_FP_SOFT:
+ arch_flags |= lldb_private::ArchSpec::eMIPS_ABI_FP_SOFT;
+ break;
+ case llvm::Mips::Val_GNU_MIPS_ABI_FP_OLD_64:
+ arch_flags |= lldb_private::ArchSpec::eMIPS_ABI_FP_OLD_64;
+ break;
+ case llvm::Mips::Val_GNU_MIPS_ABI_FP_XX:
+ arch_flags |= lldb_private::ArchSpec::eMIPS_ABI_FP_XX;
+ break;
+ case llvm::Mips::Val_GNU_MIPS_ABI_FP_64:
+ arch_flags |= lldb_private::ArchSpec::eMIPS_ABI_FP_64;
+ break;
+ case llvm::Mips::Val_GNU_MIPS_ABI_FP_64A:
+ arch_flags |= lldb_private::ArchSpec::eMIPS_ABI_FP_64A;
+ break;
+ }
+ }
+ }
+ // Settings appropriate ArchSpec ABI Flags
+ switch (header.e_flags & llvm::ELF::EF_MIPS_ABI) {
+ case llvm::ELF::EF_MIPS_ABI_O32:
+ arch_flags |= lldb_private::ArchSpec::eMIPSABI_O32;
+ break;
+ case EF_MIPS_ABI_O64:
+ arch_flags |= lldb_private::ArchSpec::eMIPSABI_O64;
+ break;
+ case EF_MIPS_ABI_EABI32:
+ arch_flags |= lldb_private::ArchSpec::eMIPSABI_EABI32;
+ break;
+ case EF_MIPS_ABI_EABI64:
+ arch_flags |= lldb_private::ArchSpec::eMIPSABI_EABI64;
+ break;
+ default:
+ // ABI Mask doesn't cover N32 and N64 ABI.
+ if (header.e_ident[EI_CLASS] == llvm::ELF::ELFCLASS64)
+ arch_flags |= lldb_private::ArchSpec::eMIPSABI_N64;
+ else if (header.e_flags & llvm::ELF::EF_MIPS_ABI2)
+ arch_flags |= lldb_private::ArchSpec::eMIPSABI_N32;
+ break;
+ }
+ arch_spec.SetFlags(arch_flags);
+ }
+
+ if (arch_spec.GetMachine() == llvm::Triple::arm ||
+ arch_spec.GetMachine() == llvm::Triple::thumb) {
+ DataExtractor data;
+
+ if (sheader.sh_type == SHT_ARM_ATTRIBUTES && section_size != 0 &&
+ data.SetData(object_data, sheader.sh_offset, section_size) == section_size)
+ ParseARMAttributes(data, section_size, arch_spec);
+ }
+
+ if (name == g_sect_name_gnu_debuglink) {
+ DataExtractor data;
+ if (section_size && (data.SetData(object_data, sheader.sh_offset,
+ section_size) == section_size)) {
+ lldb::offset_t gnu_debuglink_offset = 0;
+ gnu_debuglink_file = data.GetCStr(&gnu_debuglink_offset);
+ gnu_debuglink_offset = llvm::alignTo(gnu_debuglink_offset, 4);
+ data.GetU32(&gnu_debuglink_offset, &gnu_debuglink_crc, 1);
+ }
+ }
+
+ // Process ELF note section entries.
+ bool is_note_header = (sheader.sh_type == SHT_NOTE);
+
+ // The section header ".note.android.ident" is stored as a
+ // PROGBITS type header but it is actually a note header.
+ static ConstString g_sect_name_android_ident(".note.android.ident");
+ if (!is_note_header && name == g_sect_name_android_ident)
+ is_note_header = true;
+
+ if (is_note_header) {
+ // Allow notes to refine module info.
+ DataExtractor data;
+ if (section_size && (data.SetData(object_data, sheader.sh_offset,
+ section_size) == section_size)) {
+ Status error = RefineModuleDetailsFromNote(data, arch_spec, uuid);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("ObjectFileELF::%s ELF note processing failed: %s",
+ __FUNCTION__, error.AsCString());
+ }
+ }
+ }
+ }
+
+ // Make any unknown triple components to be unspecified unknowns.
+ if (arch_spec.GetTriple().getVendor() == llvm::Triple::UnknownVendor)
+ arch_spec.GetTriple().setVendorName(llvm::StringRef());
+ if (arch_spec.GetTriple().getOS() == llvm::Triple::UnknownOS)
+ arch_spec.GetTriple().setOSName(llvm::StringRef());
+
+ return section_headers.size();
+ }
+ }
+
+ section_headers.clear();
+ return 0;
+}
+
+llvm::StringRef
+ObjectFileELF::StripLinkerSymbolAnnotations(llvm::StringRef symbol_name) const {
+ size_t pos = symbol_name.find('@');
+ return symbol_name.substr(0, pos);
+}
+
+// ParseSectionHeaders
+size_t ObjectFileELF::ParseSectionHeaders() {
+ return GetSectionHeaderInfo(m_section_headers, m_data, m_header, m_uuid,
+ m_gnu_debuglink_file, m_gnu_debuglink_crc,
+ m_arch_spec);
+}
+
+const ObjectFileELF::ELFSectionHeaderInfo *
+ObjectFileELF::GetSectionHeaderByIndex(lldb::user_id_t id) {
+ if (!ParseSectionHeaders())
+ return nullptr;
+
+ if (id < m_section_headers.size())
+ return &m_section_headers[id];
+
+ return nullptr;
+}
+
+lldb::user_id_t ObjectFileELF::GetSectionIndexByName(const char *name) {
+ if (!name || !name[0] || !ParseSectionHeaders())
+ return 0;
+ for (size_t i = 1; i < m_section_headers.size(); ++i)
+ if (m_section_headers[i].section_name == ConstString(name))
+ return i;
+ return 0;
+}
+
+static SectionType GetSectionTypeFromName(llvm::StringRef Name) {
+ if (Name.consume_front(".debug_") || Name.consume_front(".zdebug_")) {
+ return llvm::StringSwitch<SectionType>(Name)
+ .Case("abbrev", eSectionTypeDWARFDebugAbbrev)
+ .Case("abbrev.dwo", eSectionTypeDWARFDebugAbbrevDwo)
+ .Case("addr", eSectionTypeDWARFDebugAddr)
+ .Case("aranges", eSectionTypeDWARFDebugAranges)
+ .Case("cu_index", eSectionTypeDWARFDebugCuIndex)
+ .Case("frame", eSectionTypeDWARFDebugFrame)
+ .Case("info", eSectionTypeDWARFDebugInfo)
+ .Case("info.dwo", eSectionTypeDWARFDebugInfoDwo)
+ .Cases("line", "line.dwo", eSectionTypeDWARFDebugLine)
+ .Cases("line_str", "line_str.dwo", eSectionTypeDWARFDebugLineStr)
+ .Cases("loc", "loc.dwo", eSectionTypeDWARFDebugLoc)
+ .Cases("loclists", "loclists.dwo", eSectionTypeDWARFDebugLocLists)
+ .Case("macinfo", eSectionTypeDWARFDebugMacInfo)
+ .Cases("macro", "macro.dwo", eSectionTypeDWARFDebugMacro)
+ .Case("names", eSectionTypeDWARFDebugNames)
+ .Case("pubnames", eSectionTypeDWARFDebugPubNames)
+ .Case("pubtypes", eSectionTypeDWARFDebugPubTypes)
+ .Case("ranges", eSectionTypeDWARFDebugRanges)
+ .Case("rnglists", eSectionTypeDWARFDebugRngLists)
+ .Case("str", eSectionTypeDWARFDebugStr)
+ .Case("str.dwo", eSectionTypeDWARFDebugStrDwo)
+ .Case("str_offsets", eSectionTypeDWARFDebugStrOffsets)
+ .Case("str_offsets.dwo", eSectionTypeDWARFDebugStrOffsetsDwo)
+ .Case("types", eSectionTypeDWARFDebugTypes)
+ .Case("types.dwo", eSectionTypeDWARFDebugTypesDwo)
+ .Default(eSectionTypeOther);
+ }
+ return llvm::StringSwitch<SectionType>(Name)
+ .Case(".ARM.exidx", eSectionTypeARMexidx)
+ .Case(".ARM.extab", eSectionTypeARMextab)
+ .Cases(".bss", ".tbss", eSectionTypeZeroFill)
+ .Cases(".data", ".tdata", eSectionTypeData)
+ .Case(".eh_frame", eSectionTypeEHFrame)
+ .Case(".gnu_debugaltlink", eSectionTypeDWARFGNUDebugAltLink)
+ .Case(".gosymtab", eSectionTypeGoSymtab)
+ .Case(".text", eSectionTypeCode)
+ .Default(eSectionTypeOther);
+}
+
+SectionType ObjectFileELF::GetSectionType(const ELFSectionHeaderInfo &H) const {
+ switch (H.sh_type) {
+ case SHT_PROGBITS:
+ if (H.sh_flags & SHF_EXECINSTR)
+ return eSectionTypeCode;
+ break;
+ case SHT_SYMTAB:
+ return eSectionTypeELFSymbolTable;
+ case SHT_DYNSYM:
+ return eSectionTypeELFDynamicSymbols;
+ case SHT_RELA:
+ case SHT_REL:
+ return eSectionTypeELFRelocationEntries;
+ case SHT_DYNAMIC:
+ return eSectionTypeELFDynamicLinkInfo;
+ }
+ return GetSectionTypeFromName(H.section_name.GetStringRef());
+}
+
+static uint32_t GetTargetByteSize(SectionType Type, const ArchSpec &arch) {
+ switch (Type) {
+ case eSectionTypeData:
+ case eSectionTypeZeroFill:
+ return arch.GetDataByteSize();
+ case eSectionTypeCode:
+ return arch.GetCodeByteSize();
+ default:
+ return 1;
+ }
+}
+
+static Permissions GetPermissions(const ELFSectionHeader &H) {
+ Permissions Perm = Permissions(0);
+ if (H.sh_flags & SHF_ALLOC)
+ Perm |= ePermissionsReadable;
+ if (H.sh_flags & SHF_WRITE)
+ Perm |= ePermissionsWritable;
+ if (H.sh_flags & SHF_EXECINSTR)
+ Perm |= ePermissionsExecutable;
+ return Perm;
+}
+
+static Permissions GetPermissions(const ELFProgramHeader &H) {
+ Permissions Perm = Permissions(0);
+ if (H.p_flags & PF_R)
+ Perm |= ePermissionsReadable;
+ if (H.p_flags & PF_W)
+ Perm |= ePermissionsWritable;
+ if (H.p_flags & PF_X)
+ Perm |= ePermissionsExecutable;
+ return Perm;
+}
+
+namespace {
+
+using VMRange = lldb_private::Range<addr_t, addr_t>;
+
+struct SectionAddressInfo {
+ SectionSP Segment;
+ VMRange Range;
+};
+
+// (Unlinked) ELF object files usually have 0 for every section address, meaning
+// we need to compute synthetic addresses in order for "file addresses" from
+// different sections to not overlap. This class handles that logic.
+class VMAddressProvider {
+ using VMMap = llvm::IntervalMap<addr_t, SectionSP, 4,
+ llvm::IntervalMapHalfOpenInfo<addr_t>>;
+
+ ObjectFile::Type ObjectType;
+ addr_t NextVMAddress = 0;
+ VMMap::Allocator Alloc;
+ VMMap Segments = VMMap(Alloc);
+ VMMap Sections = VMMap(Alloc);
+ lldb_private::Log *Log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES);
+
+ VMRange GetVMRange(const ELFSectionHeader &H) {
+ addr_t Address = H.sh_addr;
+ addr_t Size = H.sh_flags & SHF_ALLOC ? H.sh_size : 0;
+ if (ObjectType == ObjectFile::Type::eTypeObjectFile && Segments.empty() && (H.sh_flags & SHF_ALLOC)) {
+ NextVMAddress =
+ llvm::alignTo(NextVMAddress, std::max<addr_t>(H.sh_addralign, 1));
+ Address = NextVMAddress;
+ NextVMAddress += Size;
+ }
+ return VMRange(Address, Size);
+ }
+
+public:
+ VMAddressProvider(ObjectFile::Type Type) : ObjectType(Type) {}
+
+ llvm::Optional<VMRange> GetAddressInfo(const ELFProgramHeader &H) {
+ if (H.p_memsz == 0) {
+ LLDB_LOG(Log,
+ "Ignoring zero-sized PT_LOAD segment. Corrupt object file?");
+ return llvm::None;
+ }
+
+ if (Segments.overlaps(H.p_vaddr, H.p_vaddr + H.p_memsz)) {
+ LLDB_LOG(Log,
+ "Ignoring overlapping PT_LOAD segment. Corrupt object file?");
+ return llvm::None;
+ }
+ return VMRange(H.p_vaddr, H.p_memsz);
+ }
+
+ llvm::Optional<SectionAddressInfo> GetAddressInfo(const ELFSectionHeader &H) {
+ VMRange Range = GetVMRange(H);
+ SectionSP Segment;
+ auto It = Segments.find(Range.GetRangeBase());
+ if ((H.sh_flags & SHF_ALLOC) && It.valid()) {
+ addr_t MaxSize;
+ if (It.start() <= Range.GetRangeBase()) {
+ MaxSize = It.stop() - Range.GetRangeBase();
+ Segment = *It;
+ } else
+ MaxSize = It.start() - Range.GetRangeBase();
+ if (Range.GetByteSize() > MaxSize) {
+ LLDB_LOG(Log, "Shortening section crossing segment boundaries. "
+ "Corrupt object file?");
+ Range.SetByteSize(MaxSize);
+ }
+ }
+ if (Range.GetByteSize() > 0 &&
+ Sections.overlaps(Range.GetRangeBase(), Range.GetRangeEnd())) {
+ LLDB_LOG(Log, "Ignoring overlapping section. Corrupt object file?");
+ return llvm::None;
+ }
+ if (Segment)
+ Range.Slide(-Segment->GetFileAddress());
+ return SectionAddressInfo{Segment, Range};
+ }
+
+ void AddSegment(const VMRange &Range, SectionSP Seg) {
+ Segments.insert(Range.GetRangeBase(), Range.GetRangeEnd(), std::move(Seg));
+ }
+
+ void AddSection(SectionAddressInfo Info, SectionSP Sect) {
+ if (Info.Range.GetByteSize() == 0)
+ return;
+ if (Info.Segment)
+ Info.Range.Slide(Info.Segment->GetFileAddress());
+ Sections.insert(Info.Range.GetRangeBase(), Info.Range.GetRangeEnd(),
+ std::move(Sect));
+ }
+};
+}
+
+void ObjectFileELF::CreateSections(SectionList &unified_section_list) {
+ if (m_sections_up)
+ return;
+
+ m_sections_up = llvm::make_unique<SectionList>();
+ VMAddressProvider address_provider(GetType());
+
+ size_t LoadID = 0;
+ for (const auto &EnumPHdr : llvm::enumerate(ProgramHeaders())) {
+ const ELFProgramHeader &PHdr = EnumPHdr.value();
+ if (PHdr.p_type != PT_LOAD)
+ continue;
+
+ auto InfoOr = address_provider.GetAddressInfo(PHdr);
+ if (!InfoOr)
+ continue;
+
+ ConstString Name(("PT_LOAD[" + llvm::Twine(LoadID++) + "]").str());
+ uint32_t Log2Align = llvm::Log2_64(std::max<elf_xword>(PHdr.p_align, 1));
+ SectionSP Segment = std::make_shared<Section>(
+ GetModule(), this, SegmentID(EnumPHdr.index()), Name,
+ eSectionTypeContainer, InfoOr->GetRangeBase(), InfoOr->GetByteSize(),
+ PHdr.p_offset, PHdr.p_filesz, Log2Align, /*flags*/ 0);
+ Segment->SetPermissions(GetPermissions(PHdr));
+ m_sections_up->AddSection(Segment);
+
+ address_provider.AddSegment(*InfoOr, std::move(Segment));
+ }
+
+ ParseSectionHeaders();
+ if (m_section_headers.empty())
+ return;
+
+ for (SectionHeaderCollIter I = std::next(m_section_headers.begin());
+ I != m_section_headers.end(); ++I) {
+ const ELFSectionHeaderInfo &header = *I;
+
+ ConstString &name = I->section_name;
+ const uint64_t file_size =
+ header.sh_type == SHT_NOBITS ? 0 : header.sh_size;
+
+ auto InfoOr = address_provider.GetAddressInfo(header);
+ if (!InfoOr)
+ continue;
+
+ SectionType sect_type = GetSectionType(header);
+
+ const uint32_t target_bytes_size =
+ GetTargetByteSize(sect_type, m_arch_spec);
+
+ elf::elf_xword log2align =
+ (header.sh_addralign == 0) ? 0 : llvm::Log2_64(header.sh_addralign);
+
+ SectionSP section_sp(new Section(
+ InfoOr->Segment, GetModule(), // Module to which this section belongs.
+ this, // ObjectFile to which this section belongs and should
+ // read section data from.
+ SectionIndex(I), // Section ID.
+ name, // Section name.
+ sect_type, // Section type.
+ InfoOr->Range.GetRangeBase(), // VM address.
+ InfoOr->Range.GetByteSize(), // VM size in bytes of this section.
+ header.sh_offset, // Offset of this section in the file.
+ file_size, // Size of the section as found in the file.
+ log2align, // Alignment of the section
+ header.sh_flags, // Flags for this section.
+ target_bytes_size)); // Number of host bytes per target byte
+
+ section_sp->SetPermissions(GetPermissions(header));
+ section_sp->SetIsThreadSpecific(header.sh_flags & SHF_TLS);
+ (InfoOr->Segment ? InfoOr->Segment->GetChildren() : *m_sections_up)
+ .AddSection(section_sp);
+ address_provider.AddSection(std::move(*InfoOr), std::move(section_sp));
+ }
+
+ // For eTypeDebugInfo files, the Symbol Vendor will take care of updating the
+ // unified section list.
+ if (GetType() != eTypeDebugInfo)
+ unified_section_list = *m_sections_up;
+}
+
+// Find the arm/aarch64 mapping symbol character in the given symbol name.
+// Mapping symbols have the form of "$<char>[.<any>]*". Additionally we
+// recognize cases when the mapping symbol prefixed by an arbitrary string
+// because if a symbol prefix added to each symbol in the object file with
+// objcopy then the mapping symbols are also prefixed.
+static char FindArmAarch64MappingSymbol(const char *symbol_name) {
+ if (!symbol_name)
+ return '\0';
+
+ const char *dollar_pos = ::strchr(symbol_name, '$');
+ if (!dollar_pos || dollar_pos[1] == '\0')
+ return '\0';
+
+ if (dollar_pos[2] == '\0' || dollar_pos[2] == '.')
+ return dollar_pos[1];
+ return '\0';
+}
+
+#define STO_MIPS_ISA (3 << 6)
+#define STO_MICROMIPS (2 << 6)
+#define IS_MICROMIPS(ST_OTHER) (((ST_OTHER)&STO_MIPS_ISA) == STO_MICROMIPS)
+
+// private
+unsigned ObjectFileELF::ParseSymbols(Symtab *symtab, user_id_t start_id,
+ SectionList *section_list,
+ const size_t num_symbols,
+ const DataExtractor &symtab_data,
+ const DataExtractor &strtab_data) {
+ ELFSymbol symbol;
+ lldb::offset_t offset = 0;
+
+ static ConstString text_section_name(".text");
+ static ConstString init_section_name(".init");
+ static ConstString fini_section_name(".fini");
+ static ConstString ctors_section_name(".ctors");
+ static ConstString dtors_section_name(".dtors");
+
+ static ConstString data_section_name(".data");
+ static ConstString rodata_section_name(".rodata");
+ static ConstString rodata1_section_name(".rodata1");
+ static ConstString data2_section_name(".data1");
+ static ConstString bss_section_name(".bss");
+ static ConstString opd_section_name(".opd"); // For ppc64
+
+ // On Android the oatdata and the oatexec symbols in the oat and odex files
+ // covers the full .text section what causes issues with displaying unusable
+ // symbol name to the user and very slow unwinding speed because the
+ // instruction emulation based unwind plans try to emulate all instructions
+ // in these symbols. Don't add these symbols to the symbol list as they have
+ // no use for the debugger and they are causing a lot of trouble. Filtering
+ // can't be restricted to Android because this special object file don't
+ // contain the note section specifying the environment to Android but the
+ // custom extension and file name makes it highly unlikely that this will
+ // collide with anything else.
+ ConstString file_extension = m_file.GetFileNameExtension();
+ bool skip_oatdata_oatexec =
+ file_extension == ".oat" || file_extension == ".odex";
+
+ ArchSpec arch = GetArchitecture();
+ ModuleSP module_sp(GetModule());
+ SectionList *module_section_list =
+ module_sp ? module_sp->GetSectionList() : nullptr;
+
+ // Local cache to avoid doing a FindSectionByName for each symbol. The "const
+ // char*" key must came from a ConstString object so they can be compared by
+ // pointer
+ std::unordered_map<const char *, lldb::SectionSP> section_name_to_section;
+
+ unsigned i;
+ for (i = 0; i < num_symbols; ++i) {
+ if (!symbol.Parse(symtab_data, &offset))
+ break;
+
+ const char *symbol_name = strtab_data.PeekCStr(symbol.st_name);
+ if (!symbol_name)
+ symbol_name = "";
+
+ // No need to add non-section symbols that have no names
+ if (symbol.getType() != STT_SECTION &&
+ (symbol_name == nullptr || symbol_name[0] == '\0'))
+ continue;
+
+ // Skipping oatdata and oatexec sections if it is requested. See details
+ // above the definition of skip_oatdata_oatexec for the reasons.
+ if (skip_oatdata_oatexec && (::strcmp(symbol_name, "oatdata") == 0 ||
+ ::strcmp(symbol_name, "oatexec") == 0))
+ continue;
+
+ SectionSP symbol_section_sp;
+ SymbolType symbol_type = eSymbolTypeInvalid;
+ Elf64_Half shndx = symbol.st_shndx;
+
+ switch (shndx) {
+ case SHN_ABS:
+ symbol_type = eSymbolTypeAbsolute;
+ break;
+ case SHN_UNDEF:
+ symbol_type = eSymbolTypeUndefined;
+ break;
+ default:
+ symbol_section_sp = section_list->FindSectionByID(shndx);
+ break;
+ }
+
+ // If a symbol is undefined do not process it further even if it has a STT
+ // type
+ if (symbol_type != eSymbolTypeUndefined) {
+ switch (symbol.getType()) {
+ default:
+ case STT_NOTYPE:
+ // The symbol's type is not specified.
+ break;
+
+ case STT_OBJECT:
+ // The symbol is associated with a data object, such as a variable, an
+ // array, etc.
+ symbol_type = eSymbolTypeData;
+ break;
+
+ case STT_FUNC:
+ // The symbol is associated with a function or other executable code.
+ symbol_type = eSymbolTypeCode;
+ break;
+
+ case STT_SECTION:
+ // The symbol is associated with a section. Symbol table entries of
+ // this type exist primarily for relocation and normally have STB_LOCAL
+ // binding.
+ break;
+
+ case STT_FILE:
+ // Conventionally, the symbol's name gives the name of the source file
+ // associated with the object file. A file symbol has STB_LOCAL
+ // binding, its section index is SHN_ABS, and it precedes the other
+ // STB_LOCAL symbols for the file, if it is present.
+ symbol_type = eSymbolTypeSourceFile;
+ break;
+
+ case STT_GNU_IFUNC:
+ // The symbol is associated with an indirect function. The actual
+ // function will be resolved if it is referenced.
+ symbol_type = eSymbolTypeResolver;
+ break;
+ }
+ }
+
+ if (symbol_type == eSymbolTypeInvalid && symbol.getType() != STT_SECTION) {
+ if (symbol_section_sp) {
+ ConstString sect_name = symbol_section_sp->GetName();
+ if (sect_name == text_section_name || sect_name == init_section_name ||
+ sect_name == fini_section_name || sect_name == ctors_section_name ||
+ sect_name == dtors_section_name) {
+ symbol_type = eSymbolTypeCode;
+ } else if (sect_name == data_section_name ||
+ sect_name == data2_section_name ||
+ sect_name == rodata_section_name ||
+ sect_name == rodata1_section_name ||
+ sect_name == bss_section_name) {
+ symbol_type = eSymbolTypeData;
+ }
+ }
+ }
+
+ int64_t symbol_value_offset = 0;
+ uint32_t additional_flags = 0;
+
+ if (arch.IsValid()) {
+ if (arch.GetMachine() == llvm::Triple::arm) {
+ if (symbol.getBinding() == STB_LOCAL) {
+ char mapping_symbol = FindArmAarch64MappingSymbol(symbol_name);
+ if (symbol_type == eSymbolTypeCode) {
+ switch (mapping_symbol) {
+ case 'a':
+ // $a[.<any>]* - marks an ARM instruction sequence
+ m_address_class_map[symbol.st_value] = AddressClass::eCode;
+ break;
+ case 'b':
+ case 't':
+ // $b[.<any>]* - marks a THUMB BL instruction sequence
+ // $t[.<any>]* - marks a THUMB instruction sequence
+ m_address_class_map[symbol.st_value] =
+ AddressClass::eCodeAlternateISA;
+ break;
+ case 'd':
+ // $d[.<any>]* - marks a data item sequence (e.g. lit pool)
+ m_address_class_map[symbol.st_value] = AddressClass::eData;
+ break;
+ }
+ }
+ if (mapping_symbol)
+ continue;
+ }
+ } else if (arch.GetMachine() == llvm::Triple::aarch64) {
+ if (symbol.getBinding() == STB_LOCAL) {
+ char mapping_symbol = FindArmAarch64MappingSymbol(symbol_name);
+ if (symbol_type == eSymbolTypeCode) {
+ switch (mapping_symbol) {
+ case 'x':
+ // $x[.<any>]* - marks an A64 instruction sequence
+ m_address_class_map[symbol.st_value] = AddressClass::eCode;
+ break;
+ case 'd':
+ // $d[.<any>]* - marks a data item sequence (e.g. lit pool)
+ m_address_class_map[symbol.st_value] = AddressClass::eData;
+ break;
+ }
+ }
+ if (mapping_symbol)
+ continue;
+ }
+ }
+
+ if (arch.GetMachine() == llvm::Triple::arm) {
+ if (symbol_type == eSymbolTypeCode) {
+ if (symbol.st_value & 1) {
+ // Subtracting 1 from the address effectively unsets the low order
+ // bit, which results in the address actually pointing to the
+ // beginning of the symbol. This delta will be used below in
+ // conjunction with symbol.st_value to produce the final
+ // symbol_value that we store in the symtab.
+ symbol_value_offset = -1;
+ m_address_class_map[symbol.st_value ^ 1] =
+ AddressClass::eCodeAlternateISA;
+ } else {
+ // This address is ARM
+ m_address_class_map[symbol.st_value] = AddressClass::eCode;
+ }
+ }
+ }
+
+ /*
+ * MIPS:
+ * The bit #0 of an address is used for ISA mode (1 for microMIPS, 0 for
+ * MIPS).
+ * This allows processor to switch between microMIPS and MIPS without any
+ * need
+ * for special mode-control register. However, apart from .debug_line,
+ * none of
+ * the ELF/DWARF sections set the ISA bit (for symbol or section). Use
+ * st_other
+ * flag to check whether the symbol is microMIPS and then set the address
+ * class
+ * accordingly.
+ */
+ if (arch.IsMIPS()) {
+ if (IS_MICROMIPS(symbol.st_other))
+ m_address_class_map[symbol.st_value] = AddressClass::eCodeAlternateISA;
+ else if ((symbol.st_value & 1) && (symbol_type == eSymbolTypeCode)) {
+ symbol.st_value = symbol.st_value & (~1ull);
+ m_address_class_map[symbol.st_value] = AddressClass::eCodeAlternateISA;
+ } else {
+ if (symbol_type == eSymbolTypeCode)
+ m_address_class_map[symbol.st_value] = AddressClass::eCode;
+ else if (symbol_type == eSymbolTypeData)
+ m_address_class_map[symbol.st_value] = AddressClass::eData;
+ else
+ m_address_class_map[symbol.st_value] = AddressClass::eUnknown;
+ }
+ }
+ }
+
+ // symbol_value_offset may contain 0 for ARM symbols or -1 for THUMB
+ // symbols. See above for more details.
+ uint64_t symbol_value = symbol.st_value + symbol_value_offset;
+
+ if (symbol_section_sp == nullptr && shndx == SHN_ABS &&
+ symbol.st_size != 0) {
+ // We don't have a section for a symbol with non-zero size. Create a new
+ // section for it so the address range covered by the symbol is also
+ // covered by the module (represented through the section list). It is
+ // needed so module lookup for the addresses covered by this symbol will
+ // be successfull. This case happens for absolute symbols.
+ ConstString fake_section_name(std::string(".absolute.") + symbol_name);
+ symbol_section_sp =
+ std::make_shared<Section>(module_sp, this, SHN_ABS, fake_section_name,
+ eSectionTypeAbsoluteAddress, symbol_value,
+ symbol.st_size, 0, 0, 0, SHF_ALLOC);
+
+ module_section_list->AddSection(symbol_section_sp);
+ section_list->AddSection(symbol_section_sp);
+ }
+
+ if (symbol_section_sp &&
+ CalculateType() != ObjectFile::Type::eTypeObjectFile)
+ symbol_value -= symbol_section_sp->GetFileAddress();
+
+ if (symbol_section_sp && module_section_list &&
+ module_section_list != section_list) {
+ ConstString sect_name = symbol_section_sp->GetName();
+ auto section_it = section_name_to_section.find(sect_name.GetCString());
+ if (section_it == section_name_to_section.end())
+ section_it =
+ section_name_to_section
+ .emplace(sect_name.GetCString(),
+ module_section_list->FindSectionByName(sect_name))
+ .first;
+ if (section_it->second)
+ symbol_section_sp = section_it->second;
+ }
+
+ bool is_global = symbol.getBinding() == STB_GLOBAL;
+ uint32_t flags = symbol.st_other << 8 | symbol.st_info | additional_flags;
+ bool is_mangled = (symbol_name[0] == '_' && symbol_name[1] == 'Z');
+
+ llvm::StringRef symbol_ref(symbol_name);
+
+ // Symbol names may contain @VERSION suffixes. Find those and strip them
+ // temporarily.
+ size_t version_pos = symbol_ref.find('@');
+ bool has_suffix = version_pos != llvm::StringRef::npos;
+ llvm::StringRef symbol_bare = symbol_ref.substr(0, version_pos);
+ Mangled mangled(ConstString(symbol_bare), is_mangled);
+
+ // Now append the suffix back to mangled and unmangled names. Only do it if
+ // the demangling was successful (string is not empty).
+ if (has_suffix) {
+ llvm::StringRef suffix = symbol_ref.substr(version_pos);
+
+ llvm::StringRef mangled_name = mangled.GetMangledName().GetStringRef();
+ if (!mangled_name.empty())
+ mangled.SetMangledName(ConstString((mangled_name + suffix).str()));
+
+ ConstString demangled =
+ mangled.GetDemangledName(lldb::eLanguageTypeUnknown);
+ llvm::StringRef demangled_name = demangled.GetStringRef();
+ if (!demangled_name.empty())
+ mangled.SetDemangledName(ConstString((demangled_name + suffix).str()));
+ }
+
+ // In ELF all symbol should have a valid size but it is not true for some
+ // function symbols coming from hand written assembly. As none of the
+ // function symbol should have 0 size we try to calculate the size for
+ // these symbols in the symtab with saying that their original size is not
+ // valid.
+ bool symbol_size_valid =
+ symbol.st_size != 0 || symbol.getType() != STT_FUNC;
+
+ Symbol dc_symbol(
+ i + start_id, // ID is the original symbol table index.
+ mangled,
+ symbol_type, // Type of this symbol
+ is_global, // Is this globally visible?
+ false, // Is this symbol debug info?
+ false, // Is this symbol a trampoline?
+ false, // Is this symbol artificial?
+ AddressRange(symbol_section_sp, // Section in which this symbol is
+ // defined or null.
+ symbol_value, // Offset in section or symbol value.
+ symbol.st_size), // Size in bytes of this symbol.
+ symbol_size_valid, // Symbol size is valid
+ has_suffix, // Contains linker annotations?
+ flags); // Symbol flags.
+ symtab->AddSymbol(dc_symbol);
+ }
+ return i;
+}
+
+unsigned ObjectFileELF::ParseSymbolTable(Symtab *symbol_table,
+ user_id_t start_id,
+ lldb_private::Section *symtab) {
+ if (symtab->GetObjectFile() != this) {
+ // If the symbol table section is owned by a different object file, have it
+ // do the parsing.
+ ObjectFileELF *obj_file_elf =
+ static_cast<ObjectFileELF *>(symtab->GetObjectFile());
+ return obj_file_elf->ParseSymbolTable(symbol_table, start_id, symtab);
+ }
+
+ // Get section list for this object file.
+ SectionList *section_list = m_sections_up.get();
+ if (!section_list)
+ return 0;
+
+ user_id_t symtab_id = symtab->GetID();
+ const ELFSectionHeaderInfo *symtab_hdr = GetSectionHeaderByIndex(symtab_id);
+ assert(symtab_hdr->sh_type == SHT_SYMTAB ||
+ symtab_hdr->sh_type == SHT_DYNSYM);
+
+ // sh_link: section header index of associated string table.
+ user_id_t strtab_id = symtab_hdr->sh_link;
+ Section *strtab = section_list->FindSectionByID(strtab_id).get();
+
+ if (symtab && strtab) {
+ assert(symtab->GetObjectFile() == this);
+ assert(strtab->GetObjectFile() == this);
+
+ DataExtractor symtab_data;
+ DataExtractor strtab_data;
+ if (ReadSectionData(symtab, symtab_data) &&
+ ReadSectionData(strtab, strtab_data)) {
+ size_t num_symbols = symtab_data.GetByteSize() / symtab_hdr->sh_entsize;
+
+ return ParseSymbols(symbol_table, start_id, section_list, num_symbols,
+ symtab_data, strtab_data);
+ }
+ }
+
+ return 0;
+}
+
+size_t ObjectFileELF::ParseDynamicSymbols() {
+ if (m_dynamic_symbols.size())
+ return m_dynamic_symbols.size();
+
+ SectionList *section_list = GetSectionList();
+ if (!section_list)
+ return 0;
+
+ // Find the SHT_DYNAMIC section.
+ Section *dynsym =
+ section_list->FindSectionByType(eSectionTypeELFDynamicLinkInfo, true)
+ .get();
+ if (!dynsym)
+ return 0;
+ assert(dynsym->GetObjectFile() == this);
+
+ ELFDynamic symbol;
+ DataExtractor dynsym_data;
+ if (ReadSectionData(dynsym, dynsym_data)) {
+ const lldb::offset_t section_size = dynsym_data.GetByteSize();
+ lldb::offset_t cursor = 0;
+
+ while (cursor < section_size) {
+ if (!symbol.Parse(dynsym_data, &cursor))
+ break;
+
+ m_dynamic_symbols.push_back(symbol);
+ }
+ }
+
+ return m_dynamic_symbols.size();
+}
+
+const ELFDynamic *ObjectFileELF::FindDynamicSymbol(unsigned tag) {
+ if (!ParseDynamicSymbols())
+ return nullptr;
+
+ DynamicSymbolCollIter I = m_dynamic_symbols.begin();
+ DynamicSymbolCollIter E = m_dynamic_symbols.end();
+ for (; I != E; ++I) {
+ ELFDynamic *symbol = &*I;
+
+ if (symbol->d_tag == tag)
+ return symbol;
+ }
+
+ return nullptr;
+}
+
+unsigned ObjectFileELF::PLTRelocationType() {
+ // DT_PLTREL
+ // This member specifies the type of relocation entry to which the
+ // procedure linkage table refers. The d_val member holds DT_REL or
+ // DT_RELA, as appropriate. All relocations in a procedure linkage table
+ // must use the same relocation.
+ const ELFDynamic *symbol = FindDynamicSymbol(DT_PLTREL);
+
+ if (symbol)
+ return symbol->d_val;
+
+ return 0;
+}
+
+// Returns the size of the normal plt entries and the offset of the first
+// normal plt entry. The 0th entry in the plt table is usually a resolution
+// entry which have different size in some architectures then the rest of the
+// plt entries.
+static std::pair<uint64_t, uint64_t>
+GetPltEntrySizeAndOffset(const ELFSectionHeader *rel_hdr,
+ const ELFSectionHeader *plt_hdr) {
+ const elf_xword num_relocations = rel_hdr->sh_size / rel_hdr->sh_entsize;
+
+ // Clang 3.3 sets entsize to 4 for 32-bit binaries, but the plt entries are
+ // 16 bytes. So round the entsize up by the alignment if addralign is set.
+ elf_xword plt_entsize =
+ plt_hdr->sh_addralign
+ ? llvm::alignTo(plt_hdr->sh_entsize, plt_hdr->sh_addralign)
+ : plt_hdr->sh_entsize;
+
+ // Some linkers e.g ld for arm, fill plt_hdr->sh_entsize field incorrectly.
+ // PLT entries relocation code in general requires multiple instruction and
+ // should be greater than 4 bytes in most cases. Try to guess correct size
+ // just in case.
+ if (plt_entsize <= 4) {
+ // The linker haven't set the plt_hdr->sh_entsize field. Try to guess the
+ // size of the plt entries based on the number of entries and the size of
+ // the plt section with the assumption that the size of the 0th entry is at
+ // least as big as the size of the normal entries and it isn't much bigger
+ // then that.
+ if (plt_hdr->sh_addralign)
+ plt_entsize = plt_hdr->sh_size / plt_hdr->sh_addralign /
+ (num_relocations + 1) * plt_hdr->sh_addralign;
+ else
+ plt_entsize = plt_hdr->sh_size / (num_relocations + 1);
+ }
+
+ elf_xword plt_offset = plt_hdr->sh_size - num_relocations * plt_entsize;
+
+ return std::make_pair(plt_entsize, plt_offset);
+}
+
+static unsigned ParsePLTRelocations(
+ Symtab *symbol_table, user_id_t start_id, unsigned rel_type,
+ const ELFHeader *hdr, const ELFSectionHeader *rel_hdr,
+ const ELFSectionHeader *plt_hdr, const ELFSectionHeader *sym_hdr,
+ const lldb::SectionSP &plt_section_sp, DataExtractor &rel_data,
+ DataExtractor &symtab_data, DataExtractor &strtab_data) {
+ ELFRelocation rel(rel_type);
+ ELFSymbol symbol;
+ lldb::offset_t offset = 0;
+
+ uint64_t plt_offset, plt_entsize;
+ std::tie(plt_entsize, plt_offset) =
+ GetPltEntrySizeAndOffset(rel_hdr, plt_hdr);
+ const elf_xword num_relocations = rel_hdr->sh_size / rel_hdr->sh_entsize;
+
+ typedef unsigned (*reloc_info_fn)(const ELFRelocation &rel);
+ reloc_info_fn reloc_type;
+ reloc_info_fn reloc_symbol;
+
+ if (hdr->Is32Bit()) {
+ reloc_type = ELFRelocation::RelocType32;
+ reloc_symbol = ELFRelocation::RelocSymbol32;
+ } else {
+ reloc_type = ELFRelocation::RelocType64;
+ reloc_symbol = ELFRelocation::RelocSymbol64;
+ }
+
+ unsigned slot_type = hdr->GetRelocationJumpSlotType();
+ unsigned i;
+ for (i = 0; i < num_relocations; ++i) {
+ if (!rel.Parse(rel_data, &offset))
+ break;
+
+ if (reloc_type(rel) != slot_type)
+ continue;
+
+ lldb::offset_t symbol_offset = reloc_symbol(rel) * sym_hdr->sh_entsize;
+ if (!symbol.Parse(symtab_data, &symbol_offset))
+ break;
+
+ const char *symbol_name = strtab_data.PeekCStr(symbol.st_name);
+ bool is_mangled =
+ symbol_name ? (symbol_name[0] == '_' && symbol_name[1] == 'Z') : false;
+ uint64_t plt_index = plt_offset + i * plt_entsize;
+
+ Symbol jump_symbol(
+ i + start_id, // Symbol table index
+ symbol_name, // symbol name.
+ is_mangled, // is the symbol name mangled?
+ eSymbolTypeTrampoline, // Type of this symbol
+ false, // Is this globally visible?
+ false, // Is this symbol debug info?
+ true, // Is this symbol a trampoline?
+ true, // Is this symbol artificial?
+ plt_section_sp, // Section in which this symbol is defined or null.
+ plt_index, // Offset in section or symbol value.
+ plt_entsize, // Size in bytes of this symbol.
+ true, // Size is valid
+ false, // Contains linker annotations?
+ 0); // Symbol flags.
+
+ symbol_table->AddSymbol(jump_symbol);
+ }
+
+ return i;
+}
+
+unsigned
+ObjectFileELF::ParseTrampolineSymbols(Symtab *symbol_table, user_id_t start_id,
+ const ELFSectionHeaderInfo *rel_hdr,
+ user_id_t rel_id) {
+ assert(rel_hdr->sh_type == SHT_RELA || rel_hdr->sh_type == SHT_REL);
+
+ // The link field points to the associated symbol table.
+ user_id_t symtab_id = rel_hdr->sh_link;
+
+ // If the link field doesn't point to the appropriate symbol name table then
+ // try to find it by name as some compiler don't fill in the link fields.
+ if (!symtab_id)
+ symtab_id = GetSectionIndexByName(".dynsym");
+
+ // Get PLT section. We cannot use rel_hdr->sh_info, since current linkers
+ // point that to the .got.plt or .got section instead of .plt.
+ user_id_t plt_id = GetSectionIndexByName(".plt");
+
+ if (!symtab_id || !plt_id)
+ return 0;
+
+ const ELFSectionHeaderInfo *plt_hdr = GetSectionHeaderByIndex(plt_id);
+ if (!plt_hdr)
+ return 0;
+
+ const ELFSectionHeaderInfo *sym_hdr = GetSectionHeaderByIndex(symtab_id);
+ if (!sym_hdr)
+ return 0;
+
+ SectionList *section_list = m_sections_up.get();
+ if (!section_list)
+ return 0;
+
+ Section *rel_section = section_list->FindSectionByID(rel_id).get();
+ if (!rel_section)
+ return 0;
+
+ SectionSP plt_section_sp(section_list->FindSectionByID(plt_id));
+ if (!plt_section_sp)
+ return 0;
+
+ Section *symtab = section_list->FindSectionByID(symtab_id).get();
+ if (!symtab)
+ return 0;
+
+ // sh_link points to associated string table.
+ Section *strtab = section_list->FindSectionByID(sym_hdr->sh_link).get();
+ if (!strtab)
+ return 0;
+
+ DataExtractor rel_data;
+ if (!ReadSectionData(rel_section, rel_data))
+ return 0;
+
+ DataExtractor symtab_data;
+ if (!ReadSectionData(symtab, symtab_data))
+ return 0;
+
+ DataExtractor strtab_data;
+ if (!ReadSectionData(strtab, strtab_data))
+ return 0;
+
+ unsigned rel_type = PLTRelocationType();
+ if (!rel_type)
+ return 0;
+
+ return ParsePLTRelocations(symbol_table, start_id, rel_type, &m_header,
+ rel_hdr, plt_hdr, sym_hdr, plt_section_sp,
+ rel_data, symtab_data, strtab_data);
+}
+
+unsigned ObjectFileELF::ApplyRelocations(
+ Symtab *symtab, const ELFHeader *hdr, const ELFSectionHeader *rel_hdr,
+ const ELFSectionHeader *symtab_hdr, const ELFSectionHeader *debug_hdr,
+ DataExtractor &rel_data, DataExtractor &symtab_data,
+ DataExtractor &debug_data, Section *rel_section) {
+ ELFRelocation rel(rel_hdr->sh_type);
+ lldb::addr_t offset = 0;
+ const unsigned num_relocations = rel_hdr->sh_size / rel_hdr->sh_entsize;
+ typedef unsigned (*reloc_info_fn)(const ELFRelocation &rel);
+ reloc_info_fn reloc_type;
+ reloc_info_fn reloc_symbol;
+
+ if (hdr->Is32Bit()) {
+ reloc_type = ELFRelocation::RelocType32;
+ reloc_symbol = ELFRelocation::RelocSymbol32;
+ } else {
+ reloc_type = ELFRelocation::RelocType64;
+ reloc_symbol = ELFRelocation::RelocSymbol64;
+ }
+
+ for (unsigned i = 0; i < num_relocations; ++i) {
+ if (!rel.Parse(rel_data, &offset))
+ break;
+
+ Symbol *symbol = nullptr;
+
+ if (hdr->Is32Bit()) {
+ switch (reloc_type(rel)) {
+ case R_386_32:
+ case R_386_PC32:
+ default:
+ // FIXME: This asserts with this input:
+ //
+ // foo.cpp
+ // int main(int argc, char **argv) { return 0; }
+ //
+ // clang++.exe --target=i686-unknown-linux-gnu -g -c foo.cpp -o foo.o
+ //
+ // and running this on the foo.o module.
+ assert(false && "unexpected relocation type");
+ }
+ } else {
+ switch (reloc_type(rel)) {
+ case R_AARCH64_ABS64:
+ case R_X86_64_64: {
+ symbol = symtab->FindSymbolByID(reloc_symbol(rel));
+ if (symbol) {
+ addr_t value = symbol->GetAddressRef().GetFileAddress();
+ DataBufferSP &data_buffer_sp = debug_data.GetSharedDataBuffer();
+ uint64_t *dst = reinterpret_cast<uint64_t *>(
+ data_buffer_sp->GetBytes() + rel_section->GetFileOffset() +
+ ELFRelocation::RelocOffset64(rel));
+ uint64_t val_offset = value + ELFRelocation::RelocAddend64(rel);
+ memcpy(dst, &val_offset, sizeof(uint64_t));
+ }
+ break;
+ }
+ case R_X86_64_32:
+ case R_X86_64_32S:
+ case R_AARCH64_ABS32: {
+ symbol = symtab->FindSymbolByID(reloc_symbol(rel));
+ if (symbol) {
+ addr_t value = symbol->GetAddressRef().GetFileAddress();
+ value += ELFRelocation::RelocAddend32(rel);
+ if ((reloc_type(rel) == R_X86_64_32 && (value > UINT32_MAX)) ||
+ (reloc_type(rel) == R_X86_64_32S &&
+ ((int64_t)value > INT32_MAX && (int64_t)value < INT32_MIN)) ||
+ (reloc_type(rel) == R_AARCH64_ABS32 &&
+ ((int64_t)value > INT32_MAX && (int64_t)value < INT32_MIN))) {
+ Log *log =
+ lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES);
+ log->Printf("Failed to apply debug info relocations");
+ break;
+ }
+ uint32_t truncated_addr = (value & 0xFFFFFFFF);
+ DataBufferSP &data_buffer_sp = debug_data.GetSharedDataBuffer();
+ uint32_t *dst = reinterpret_cast<uint32_t *>(
+ data_buffer_sp->GetBytes() + rel_section->GetFileOffset() +
+ ELFRelocation::RelocOffset32(rel));
+ memcpy(dst, &truncated_addr, sizeof(uint32_t));
+ }
+ break;
+ }
+ case R_X86_64_PC32:
+ default:
+ assert(false && "unexpected relocation type");
+ }
+ }
+ }
+
+ return 0;
+}
+
+unsigned ObjectFileELF::RelocateDebugSections(const ELFSectionHeader *rel_hdr,
+ user_id_t rel_id,
+ lldb_private::Symtab *thetab) {
+ assert(rel_hdr->sh_type == SHT_RELA || rel_hdr->sh_type == SHT_REL);
+
+ // Parse in the section list if needed.
+ SectionList *section_list = GetSectionList();
+ if (!section_list)
+ return 0;
+
+ user_id_t symtab_id = rel_hdr->sh_link;
+ user_id_t debug_id = rel_hdr->sh_info;
+
+ const ELFSectionHeader *symtab_hdr = GetSectionHeaderByIndex(symtab_id);
+ if (!symtab_hdr)
+ return 0;
+
+ const ELFSectionHeader *debug_hdr = GetSectionHeaderByIndex(debug_id);
+ if (!debug_hdr)
+ return 0;
+
+ Section *rel = section_list->FindSectionByID(rel_id).get();
+ if (!rel)
+ return 0;
+
+ Section *symtab = section_list->FindSectionByID(symtab_id).get();
+ if (!symtab)
+ return 0;
+
+ Section *debug = section_list->FindSectionByID(debug_id).get();
+ if (!debug)
+ return 0;
+
+ DataExtractor rel_data;
+ DataExtractor symtab_data;
+ DataExtractor debug_data;
+
+ if (GetData(rel->GetFileOffset(), rel->GetFileSize(), rel_data) &&
+ GetData(symtab->GetFileOffset(), symtab->GetFileSize(), symtab_data) &&
+ GetData(debug->GetFileOffset(), debug->GetFileSize(), debug_data)) {
+ ApplyRelocations(thetab, &m_header, rel_hdr, symtab_hdr, debug_hdr,
+ rel_data, symtab_data, debug_data, debug);
+ }
+
+ return 0;
+}
+
+Symtab *ObjectFileELF::GetSymtab() {
+ ModuleSP module_sp(GetModule());
+ if (!module_sp)
+ return nullptr;
+
+ // We always want to use the main object file so we (hopefully) only have one
+ // cached copy of our symtab, dynamic sections, etc.
+ ObjectFile *module_obj_file = module_sp->GetObjectFile();
+ if (module_obj_file && module_obj_file != this)
+ return module_obj_file->GetSymtab();
+
+ if (m_symtab_up == nullptr) {
+ SectionList *section_list = module_sp->GetSectionList();
+ if (!section_list)
+ return nullptr;
+
+ uint64_t symbol_id = 0;
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+
+ // Sharable objects and dynamic executables usually have 2 distinct symbol
+ // tables, one named ".symtab", and the other ".dynsym". The dynsym is a
+ // smaller version of the symtab that only contains global symbols. The
+ // information found in the dynsym is therefore also found in the symtab,
+ // while the reverse is not necessarily true.
+ Section *symtab =
+ section_list->FindSectionByType(eSectionTypeELFSymbolTable, true).get();
+ if (!symtab) {
+ // The symtab section is non-allocable and can be stripped, so if it
+ // doesn't exist then use the dynsym section which should always be
+ // there.
+ symtab =
+ section_list->FindSectionByType(eSectionTypeELFDynamicSymbols, true)
+ .get();
+ }
+ if (symtab) {
+ m_symtab_up.reset(new Symtab(symtab->GetObjectFile()));
+ symbol_id += ParseSymbolTable(m_symtab_up.get(), symbol_id, symtab);
+ }
+
+ // DT_JMPREL
+ // If present, this entry's d_ptr member holds the address of
+ // relocation
+ // entries associated solely with the procedure linkage table.
+ // Separating
+ // these relocation entries lets the dynamic linker ignore them during
+ // process initialization, if lazy binding is enabled. If this entry is
+ // present, the related entries of types DT_PLTRELSZ and DT_PLTREL must
+ // also be present.
+ const ELFDynamic *symbol = FindDynamicSymbol(DT_JMPREL);
+ if (symbol) {
+ // Synthesize trampoline symbols to help navigate the PLT.
+ addr_t addr = symbol->d_ptr;
+ Section *reloc_section =
+ section_list->FindSectionContainingFileAddress(addr).get();
+ if (reloc_section) {
+ user_id_t reloc_id = reloc_section->GetID();
+ const ELFSectionHeaderInfo *reloc_header =
+ GetSectionHeaderByIndex(reloc_id);
+ assert(reloc_header);
+
+ if (m_symtab_up == nullptr)
+ m_symtab_up.reset(new Symtab(reloc_section->GetObjectFile()));
+
+ ParseTrampolineSymbols(m_symtab_up.get(), symbol_id, reloc_header,
+ reloc_id);
+ }
+ }
+
+ if (DWARFCallFrameInfo *eh_frame =
+ GetModule()->GetUnwindTable().GetEHFrameInfo()) {
+ if (m_symtab_up == nullptr)
+ m_symtab_up.reset(new Symtab(this));
+ ParseUnwindSymbols(m_symtab_up.get(), eh_frame);
+ }
+
+ // If we still don't have any symtab then create an empty instance to avoid
+ // do the section lookup next time.
+ if (m_symtab_up == nullptr)
+ m_symtab_up.reset(new Symtab(this));
+
+ m_symtab_up->CalculateSymbolSizes();
+ }
+
+ return m_symtab_up.get();
+}
+
+void ObjectFileELF::RelocateSection(lldb_private::Section *section)
+{
+ static const char *debug_prefix = ".debug";
+
+ // Set relocated bit so we stop getting called, regardless of whether we
+ // actually relocate.
+ section->SetIsRelocated(true);
+
+ // We only relocate in ELF relocatable files
+ if (CalculateType() != eTypeObjectFile)
+ return;
+
+ const char *section_name = section->GetName().GetCString();
+ // Can't relocate that which can't be named
+ if (section_name == nullptr)
+ return;
+
+ // We don't relocate non-debug sections at the moment
+ if (strncmp(section_name, debug_prefix, strlen(debug_prefix)))
+ return;
+
+ // Relocation section names to look for
+ std::string needle = std::string(".rel") + section_name;
+ std::string needlea = std::string(".rela") + section_name;
+
+ for (SectionHeaderCollIter I = m_section_headers.begin();
+ I != m_section_headers.end(); ++I) {
+ if (I->sh_type == SHT_RELA || I->sh_type == SHT_REL) {
+ const char *hay_name = I->section_name.GetCString();
+ if (hay_name == nullptr)
+ continue;
+ if (needle == hay_name || needlea == hay_name) {
+ const ELFSectionHeader &reloc_header = *I;
+ user_id_t reloc_id = SectionIndex(I);
+ RelocateDebugSections(&reloc_header, reloc_id, GetSymtab());
+ break;
+ }
+ }
+ }
+}
+
+void ObjectFileELF::ParseUnwindSymbols(Symtab *symbol_table,
+ DWARFCallFrameInfo *eh_frame) {
+ SectionList *section_list = GetSectionList();
+ if (!section_list)
+ return;
+
+ // First we save the new symbols into a separate list and add them to the
+ // symbol table after we colleced all symbols we want to add. This is
+ // neccessary because adding a new symbol invalidates the internal index of
+ // the symtab what causing the next lookup to be slow because it have to
+ // recalculate the index first.
+ std::vector<Symbol> new_symbols;
+
+ eh_frame->ForEachFDEEntries([this, symbol_table, section_list, &new_symbols](
+ lldb::addr_t file_addr, uint32_t size, dw_offset_t) {
+ Symbol *symbol = symbol_table->FindSymbolAtFileAddress(file_addr);
+ if (symbol) {
+ if (!symbol->GetByteSizeIsValid()) {
+ symbol->SetByteSize(size);
+ symbol->SetSizeIsSynthesized(true);
+ }
+ } else {
+ SectionSP section_sp =
+ section_list->FindSectionContainingFileAddress(file_addr);
+ if (section_sp) {
+ addr_t offset = file_addr - section_sp->GetFileAddress();
+ const char *symbol_name = GetNextSyntheticSymbolName().GetCString();
+ uint64_t symbol_id = symbol_table->GetNumSymbols();
+ Symbol eh_symbol(
+ symbol_id, // Symbol table index.
+ symbol_name, // Symbol name.
+ false, // Is the symbol name mangled?
+ eSymbolTypeCode, // Type of this symbol.
+ true, // Is this globally visible?
+ false, // Is this symbol debug info?
+ false, // Is this symbol a trampoline?
+ true, // Is this symbol artificial?
+ section_sp, // Section in which this symbol is defined or null.
+ offset, // Offset in section or symbol value.
+ 0, // Size: Don't specify the size as an FDE can
+ false, // Size is valid: cover multiple symbols.
+ false, // Contains linker annotations?
+ 0); // Symbol flags.
+ new_symbols.push_back(eh_symbol);
+ }
+ }
+ return true;
+ });
+
+ for (const Symbol &s : new_symbols)
+ symbol_table->AddSymbol(s);
+}
+
+bool ObjectFileELF::IsStripped() {
+ // TODO: determine this for ELF
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// Dump
+//
+// Dump the specifics of the runtime file container (such as any headers
+// segments, sections, etc).
+void ObjectFileELF::Dump(Stream *s) {
+ ModuleSP module_sp(GetModule());
+ if (!module_sp) {
+ return;
+ }
+
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ s->Printf("%p: ", static_cast<void *>(this));
+ s->Indent();
+ s->PutCString("ObjectFileELF");
+
+ ArchSpec header_arch = GetArchitecture();
+
+ *s << ", file = '" << m_file
+ << "', arch = " << header_arch.GetArchitectureName() << "\n";
+
+ DumpELFHeader(s, m_header);
+ s->EOL();
+ DumpELFProgramHeaders(s);
+ s->EOL();
+ DumpELFSectionHeaders(s);
+ s->EOL();
+ SectionList *section_list = GetSectionList();
+ if (section_list)
+ section_list->Dump(s, nullptr, true, UINT32_MAX);
+ Symtab *symtab = GetSymtab();
+ if (symtab)
+ symtab->Dump(s, nullptr, eSortOrderNone);
+ s->EOL();
+ DumpDependentModules(s);
+ s->EOL();
+}
+
+// DumpELFHeader
+//
+// Dump the ELF header to the specified output stream
+void ObjectFileELF::DumpELFHeader(Stream *s, const ELFHeader &header) {
+ s->PutCString("ELF Header\n");
+ s->Printf("e_ident[EI_MAG0 ] = 0x%2.2x\n", header.e_ident[EI_MAG0]);
+ s->Printf("e_ident[EI_MAG1 ] = 0x%2.2x '%c'\n", header.e_ident[EI_MAG1],
+ header.e_ident[EI_MAG1]);
+ s->Printf("e_ident[EI_MAG2 ] = 0x%2.2x '%c'\n", header.e_ident[EI_MAG2],
+ header.e_ident[EI_MAG2]);
+ s->Printf("e_ident[EI_MAG3 ] = 0x%2.2x '%c'\n", header.e_ident[EI_MAG3],
+ header.e_ident[EI_MAG3]);
+
+ s->Printf("e_ident[EI_CLASS ] = 0x%2.2x\n", header.e_ident[EI_CLASS]);
+ s->Printf("e_ident[EI_DATA ] = 0x%2.2x ", header.e_ident[EI_DATA]);
+ DumpELFHeader_e_ident_EI_DATA(s, header.e_ident[EI_DATA]);
+ s->Printf("\ne_ident[EI_VERSION] = 0x%2.2x\n", header.e_ident[EI_VERSION]);
+ s->Printf("e_ident[EI_PAD ] = 0x%2.2x\n", header.e_ident[EI_PAD]);
+
+ s->Printf("e_type = 0x%4.4x ", header.e_type);
+ DumpELFHeader_e_type(s, header.e_type);
+ s->Printf("\ne_machine = 0x%4.4x\n", header.e_machine);
+ s->Printf("e_version = 0x%8.8x\n", header.e_version);
+ s->Printf("e_entry = 0x%8.8" PRIx64 "\n", header.e_entry);
+ s->Printf("e_phoff = 0x%8.8" PRIx64 "\n", header.e_phoff);
+ s->Printf("e_shoff = 0x%8.8" PRIx64 "\n", header.e_shoff);
+ s->Printf("e_flags = 0x%8.8x\n", header.e_flags);
+ s->Printf("e_ehsize = 0x%4.4x\n", header.e_ehsize);
+ s->Printf("e_phentsize = 0x%4.4x\n", header.e_phentsize);
+ s->Printf("e_phnum = 0x%8.8x\n", header.e_phnum);
+ s->Printf("e_shentsize = 0x%4.4x\n", header.e_shentsize);
+ s->Printf("e_shnum = 0x%8.8x\n", header.e_shnum);
+ s->Printf("e_shstrndx = 0x%8.8x\n", header.e_shstrndx);
+}
+
+// DumpELFHeader_e_type
+//
+// Dump an token value for the ELF header member e_type
+void ObjectFileELF::DumpELFHeader_e_type(Stream *s, elf_half e_type) {
+ switch (e_type) {
+ case ET_NONE:
+ *s << "ET_NONE";
+ break;
+ case ET_REL:
+ *s << "ET_REL";
+ break;
+ case ET_EXEC:
+ *s << "ET_EXEC";
+ break;
+ case ET_DYN:
+ *s << "ET_DYN";
+ break;
+ case ET_CORE:
+ *s << "ET_CORE";
+ break;
+ default:
+ break;
+ }
+}
+
+// DumpELFHeader_e_ident_EI_DATA
+//
+// Dump an token value for the ELF header member e_ident[EI_DATA]
+void ObjectFileELF::DumpELFHeader_e_ident_EI_DATA(Stream *s,
+ unsigned char ei_data) {
+ switch (ei_data) {
+ case ELFDATANONE:
+ *s << "ELFDATANONE";
+ break;
+ case ELFDATA2LSB:
+ *s << "ELFDATA2LSB - Little Endian";
+ break;
+ case ELFDATA2MSB:
+ *s << "ELFDATA2MSB - Big Endian";
+ break;
+ default:
+ break;
+ }
+}
+
+// DumpELFProgramHeader
+//
+// Dump a single ELF program header to the specified output stream
+void ObjectFileELF::DumpELFProgramHeader(Stream *s,
+ const ELFProgramHeader &ph) {
+ DumpELFProgramHeader_p_type(s, ph.p_type);
+ s->Printf(" %8.8" PRIx64 " %8.8" PRIx64 " %8.8" PRIx64, ph.p_offset,
+ ph.p_vaddr, ph.p_paddr);
+ s->Printf(" %8.8" PRIx64 " %8.8" PRIx64 " %8.8x (", ph.p_filesz, ph.p_memsz,
+ ph.p_flags);
+
+ DumpELFProgramHeader_p_flags(s, ph.p_flags);
+ s->Printf(") %8.8" PRIx64, ph.p_align);
+}
+
+// DumpELFProgramHeader_p_type
+//
+// Dump an token value for the ELF program header member p_type which describes
+// the type of the program header
+void ObjectFileELF::DumpELFProgramHeader_p_type(Stream *s, elf_word p_type) {
+ const int kStrWidth = 15;
+ switch (p_type) {
+ CASE_AND_STREAM(s, PT_NULL, kStrWidth);
+ CASE_AND_STREAM(s, PT_LOAD, kStrWidth);
+ CASE_AND_STREAM(s, PT_DYNAMIC, kStrWidth);
+ CASE_AND_STREAM(s, PT_INTERP, kStrWidth);
+ CASE_AND_STREAM(s, PT_NOTE, kStrWidth);
+ CASE_AND_STREAM(s, PT_SHLIB, kStrWidth);
+ CASE_AND_STREAM(s, PT_PHDR, kStrWidth);
+ CASE_AND_STREAM(s, PT_TLS, kStrWidth);
+ CASE_AND_STREAM(s, PT_GNU_EH_FRAME, kStrWidth);
+ default:
+ s->Printf("0x%8.8x%*s", p_type, kStrWidth - 10, "");
+ break;
+ }
+}
+
+// DumpELFProgramHeader_p_flags
+//
+// Dump an token value for the ELF program header member p_flags
+void ObjectFileELF::DumpELFProgramHeader_p_flags(Stream *s, elf_word p_flags) {
+ *s << ((p_flags & PF_X) ? "PF_X" : " ")
+ << (((p_flags & PF_X) && (p_flags & PF_W)) ? '+' : ' ')
+ << ((p_flags & PF_W) ? "PF_W" : " ")
+ << (((p_flags & PF_W) && (p_flags & PF_R)) ? '+' : ' ')
+ << ((p_flags & PF_R) ? "PF_R" : " ");
+}
+
+// DumpELFProgramHeaders
+//
+// Dump all of the ELF program header to the specified output stream
+void ObjectFileELF::DumpELFProgramHeaders(Stream *s) {
+ if (!ParseProgramHeaders())
+ return;
+
+ s->PutCString("Program Headers\n");
+ s->PutCString("IDX p_type p_offset p_vaddr p_paddr "
+ "p_filesz p_memsz p_flags p_align\n");
+ s->PutCString("==== --------------- -------- -------- -------- "
+ "-------- -------- ------------------------- --------\n");
+
+ for (const auto &H : llvm::enumerate(m_program_headers)) {
+ s->Format("[{0,2}] ", H.index());
+ ObjectFileELF::DumpELFProgramHeader(s, H.value());
+ s->EOL();
+ }
+}
+
+// DumpELFSectionHeader
+//
+// Dump a single ELF section header to the specified output stream
+void ObjectFileELF::DumpELFSectionHeader(Stream *s,
+ const ELFSectionHeaderInfo &sh) {
+ s->Printf("%8.8x ", sh.sh_name);
+ DumpELFSectionHeader_sh_type(s, sh.sh_type);
+ s->Printf(" %8.8" PRIx64 " (", sh.sh_flags);
+ DumpELFSectionHeader_sh_flags(s, sh.sh_flags);
+ s->Printf(") %8.8" PRIx64 " %8.8" PRIx64 " %8.8" PRIx64, sh.sh_addr,
+ sh.sh_offset, sh.sh_size);
+ s->Printf(" %8.8x %8.8x", sh.sh_link, sh.sh_info);
+ s->Printf(" %8.8" PRIx64 " %8.8" PRIx64, sh.sh_addralign, sh.sh_entsize);
+}
+
+// DumpELFSectionHeader_sh_type
+//
+// Dump an token value for the ELF section header member sh_type which
+// describes the type of the section
+void ObjectFileELF::DumpELFSectionHeader_sh_type(Stream *s, elf_word sh_type) {
+ const int kStrWidth = 12;
+ switch (sh_type) {
+ CASE_AND_STREAM(s, SHT_NULL, kStrWidth);
+ CASE_AND_STREAM(s, SHT_PROGBITS, kStrWidth);
+ CASE_AND_STREAM(s, SHT_SYMTAB, kStrWidth);
+ CASE_AND_STREAM(s, SHT_STRTAB, kStrWidth);
+ CASE_AND_STREAM(s, SHT_RELA, kStrWidth);
+ CASE_AND_STREAM(s, SHT_HASH, kStrWidth);
+ CASE_AND_STREAM(s, SHT_DYNAMIC, kStrWidth);
+ CASE_AND_STREAM(s, SHT_NOTE, kStrWidth);
+ CASE_AND_STREAM(s, SHT_NOBITS, kStrWidth);
+ CASE_AND_STREAM(s, SHT_REL, kStrWidth);
+ CASE_AND_STREAM(s, SHT_SHLIB, kStrWidth);
+ CASE_AND_STREAM(s, SHT_DYNSYM, kStrWidth);
+ CASE_AND_STREAM(s, SHT_LOPROC, kStrWidth);
+ CASE_AND_STREAM(s, SHT_HIPROC, kStrWidth);
+ CASE_AND_STREAM(s, SHT_LOUSER, kStrWidth);
+ CASE_AND_STREAM(s, SHT_HIUSER, kStrWidth);
+ default:
+ s->Printf("0x%8.8x%*s", sh_type, kStrWidth - 10, "");
+ break;
+ }
+}
+
+// DumpELFSectionHeader_sh_flags
+//
+// Dump an token value for the ELF section header member sh_flags
+void ObjectFileELF::DumpELFSectionHeader_sh_flags(Stream *s,
+ elf_xword sh_flags) {
+ *s << ((sh_flags & SHF_WRITE) ? "WRITE" : " ")
+ << (((sh_flags & SHF_WRITE) && (sh_flags & SHF_ALLOC)) ? '+' : ' ')
+ << ((sh_flags & SHF_ALLOC) ? "ALLOC" : " ")
+ << (((sh_flags & SHF_ALLOC) && (sh_flags & SHF_EXECINSTR)) ? '+' : ' ')
+ << ((sh_flags & SHF_EXECINSTR) ? "EXECINSTR" : " ");
+}
+
+// DumpELFSectionHeaders
+//
+// Dump all of the ELF section header to the specified output stream
+void ObjectFileELF::DumpELFSectionHeaders(Stream *s) {
+ if (!ParseSectionHeaders())
+ return;
+
+ s->PutCString("Section Headers\n");
+ s->PutCString("IDX name type flags "
+ "addr offset size link info addralgn "
+ "entsize Name\n");
+ s->PutCString("==== -------- ------------ -------------------------------- "
+ "-------- -------- -------- -------- -------- -------- "
+ "-------- ====================\n");
+
+ uint32_t idx = 0;
+ for (SectionHeaderCollConstIter I = m_section_headers.begin();
+ I != m_section_headers.end(); ++I, ++idx) {
+ s->Printf("[%2u] ", idx);
+ ObjectFileELF::DumpELFSectionHeader(s, *I);
+ const char *section_name = I->section_name.AsCString("");
+ if (section_name)
+ *s << ' ' << section_name << "\n";
+ }
+}
+
+void ObjectFileELF::DumpDependentModules(lldb_private::Stream *s) {
+ size_t num_modules = ParseDependentModules();
+
+ if (num_modules > 0) {
+ s->PutCString("Dependent Modules:\n");
+ for (unsigned i = 0; i < num_modules; ++i) {
+ const FileSpec &spec = m_filespec_up->GetFileSpecAtIndex(i);
+ s->Printf(" %s\n", spec.GetFilename().GetCString());
+ }
+ }
+}
+
+ArchSpec ObjectFileELF::GetArchitecture() {
+ if (!ParseHeader())
+ return ArchSpec();
+
+ if (m_section_headers.empty()) {
+ // Allow elf notes to be parsed which may affect the detected architecture.
+ ParseSectionHeaders();
+ }
+
+ if (CalculateType() == eTypeCoreFile &&
+ !m_arch_spec.TripleOSWasSpecified()) {
+ // Core files don't have section headers yet they have PT_NOTE program
+ // headers that might shed more light on the architecture
+ for (const elf::ELFProgramHeader &H : ProgramHeaders()) {
+ if (H.p_type != PT_NOTE || H.p_offset == 0 || H.p_filesz == 0)
+ continue;
+ DataExtractor data;
+ if (data.SetData(m_data, H.p_offset, H.p_filesz) == H.p_filesz) {
+ UUID uuid;
+ RefineModuleDetailsFromNote(data, m_arch_spec, uuid);
+ }
+ }
+ }
+ return m_arch_spec;
+}
+
+ObjectFile::Type ObjectFileELF::CalculateType() {
+ switch (m_header.e_type) {
+ case llvm::ELF::ET_NONE:
+ // 0 - No file type
+ return eTypeUnknown;
+
+ case llvm::ELF::ET_REL:
+ // 1 - Relocatable file
+ return eTypeObjectFile;
+
+ case llvm::ELF::ET_EXEC:
+ // 2 - Executable file
+ return eTypeExecutable;
+
+ case llvm::ELF::ET_DYN:
+ // 3 - Shared object file
+ return eTypeSharedLibrary;
+
+ case ET_CORE:
+ // 4 - Core file
+ return eTypeCoreFile;
+
+ default:
+ break;
+ }
+ return eTypeUnknown;
+}
+
+ObjectFile::Strata ObjectFileELF::CalculateStrata() {
+ switch (m_header.e_type) {
+ case llvm::ELF::ET_NONE:
+ // 0 - No file type
+ return eStrataUnknown;
+
+ case llvm::ELF::ET_REL:
+ // 1 - Relocatable file
+ return eStrataUnknown;
+
+ case llvm::ELF::ET_EXEC:
+ // 2 - Executable file
+ // TODO: is there any way to detect that an executable is a kernel
+ // related executable by inspecting the program headers, section headers,
+ // symbols, or any other flag bits???
+ return eStrataUser;
+
+ case llvm::ELF::ET_DYN:
+ // 3 - Shared object file
+ // TODO: is there any way to detect that an shared library is a kernel
+ // related executable by inspecting the program headers, section headers,
+ // symbols, or any other flag bits???
+ return eStrataUnknown;
+
+ case ET_CORE:
+ // 4 - Core file
+ // TODO: is there any way to detect that an core file is a kernel
+ // related executable by inspecting the program headers, section headers,
+ // symbols, or any other flag bits???
+ return eStrataUnknown;
+
+ default:
+ break;
+ }
+ return eStrataUnknown;
+}
+
+size_t ObjectFileELF::ReadSectionData(Section *section,
+ lldb::offset_t section_offset, void *dst,
+ size_t dst_len) {
+ // If some other objectfile owns this data, pass this to them.
+ if (section->GetObjectFile() != this)
+ return section->GetObjectFile()->ReadSectionData(section, section_offset,
+ dst, dst_len);
+
+ if (!section->Test(SHF_COMPRESSED))
+ return ObjectFile::ReadSectionData(section, section_offset, dst, dst_len);
+
+ // For compressed sections we need to read to full data to be able to
+ // decompress.
+ DataExtractor data;
+ ReadSectionData(section, data);
+ return data.CopyData(section_offset, dst_len, dst);
+}
+
+size_t ObjectFileELF::ReadSectionData(Section *section,
+ DataExtractor &section_data) {
+ // If some other objectfile owns this data, pass this to them.
+ if (section->GetObjectFile() != this)
+ return section->GetObjectFile()->ReadSectionData(section, section_data);
+
+ size_t result = ObjectFile::ReadSectionData(section, section_data);
+ if (result == 0 || !llvm::object::Decompressor::isCompressedELFSection(
+ section->Get(), section->GetName().GetStringRef()))
+ return result;
+
+ auto Decompressor = llvm::object::Decompressor::create(
+ section->GetName().GetStringRef(),
+ {reinterpret_cast<const char *>(section_data.GetDataStart()),
+ size_t(section_data.GetByteSize())},
+ GetByteOrder() == eByteOrderLittle, GetAddressByteSize() == 8);
+ if (!Decompressor) {
+ GetModule()->ReportWarning(
+ "Unable to initialize decompressor for section '%s': %s",
+ section->GetName().GetCString(),
+ llvm::toString(Decompressor.takeError()).c_str());
+ section_data.Clear();
+ return 0;
+ }
+
+ auto buffer_sp =
+ std::make_shared<DataBufferHeap>(Decompressor->getDecompressedSize(), 0);
+ if (auto error = Decompressor->decompress(
+ {reinterpret_cast<char *>(buffer_sp->GetBytes()),
+ size_t(buffer_sp->GetByteSize())})) {
+ GetModule()->ReportWarning(
+ "Decompression of section '%s' failed: %s",
+ section->GetName().GetCString(),
+ llvm::toString(std::move(error)).c_str());
+ section_data.Clear();
+ return 0;
+ }
+
+ section_data.SetData(buffer_sp);
+ return buffer_sp->GetByteSize();
+}
+
+llvm::ArrayRef<ELFProgramHeader> ObjectFileELF::ProgramHeaders() {
+ ParseProgramHeaders();
+ return m_program_headers;
+}
+
+DataExtractor ObjectFileELF::GetSegmentData(const ELFProgramHeader &H) {
+ return DataExtractor(m_data, H.p_offset, H.p_filesz);
+}
+
+bool ObjectFileELF::AnySegmentHasPhysicalAddress() {
+ for (const ELFProgramHeader &H : ProgramHeaders()) {
+ if (H.p_paddr != 0)
+ return true;
+ }
+ return false;
+}
+
+std::vector<ObjectFile::LoadableData>
+ObjectFileELF::GetLoadableData(Target &target) {
+ // Create a list of loadable data from loadable segments, using physical
+ // addresses if they aren't all null
+ std::vector<LoadableData> loadables;
+ bool should_use_paddr = AnySegmentHasPhysicalAddress();
+ for (const ELFProgramHeader &H : ProgramHeaders()) {
+ LoadableData loadable;
+ if (H.p_type != llvm::ELF::PT_LOAD)
+ continue;
+ loadable.Dest = should_use_paddr ? H.p_paddr : H.p_vaddr;
+ if (loadable.Dest == LLDB_INVALID_ADDRESS)
+ continue;
+ if (H.p_filesz == 0)
+ continue;
+ auto segment_data = GetSegmentData(H);
+ loadable.Contents = llvm::ArrayRef<uint8_t>(segment_data.GetDataStart(),
+ segment_data.GetByteSize());
+ loadables.push_back(loadable);
+ }
+ return loadables;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h
new file mode 100644
index 000000000000..b63a5d14d4f5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h
@@ -0,0 +1,381 @@
+//===-- ObjectFileELF.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 liblldb_ObjectFileELF_h_
+#define liblldb_ObjectFileELF_h_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/UUID.h"
+#include "lldb/lldb-private.h"
+
+#include "ELFHeader.h"
+
+struct ELFNote {
+ elf::elf_word n_namesz;
+ elf::elf_word n_descsz;
+ elf::elf_word n_type;
+
+ std::string n_name;
+
+ ELFNote() : n_namesz(0), n_descsz(0), n_type(0) {}
+
+ /// Parse an ELFNote entry from the given DataExtractor starting at position
+ /// \p offset.
+ ///
+ /// \param[in] data
+ /// The DataExtractor to read from.
+ ///
+ /// \param[in,out] offset
+ /// Pointer to an offset in the data. On return the offset will be
+ /// advanced by the number of bytes read.
+ ///
+ /// \return
+ /// True if the ELFRel entry was successfully read and false otherwise.
+ bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset);
+
+ size_t GetByteSize() const {
+ return 12 + llvm::alignTo(n_namesz, 4) + llvm::alignTo(n_descsz, 4);
+ }
+};
+
+/// \class ObjectFileELF
+/// Generic ELF object file reader.
+///
+/// This class provides a generic ELF (32/64 bit) reader plugin implementing
+/// the ObjectFile protocol.
+class ObjectFileELF : public lldb_private::ObjectFile {
+public:
+ ~ObjectFileELF() override;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::ObjectFile *
+ CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset, const lldb_private::FileSpec *file,
+ lldb::offset_t file_offset, lldb::offset_t length);
+
+ static lldb_private::ObjectFile *CreateMemoryInstance(
+ const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
+ const lldb::ProcessSP &process_sp, lldb::addr_t header_addr);
+
+ static size_t GetModuleSpecifications(const lldb_private::FileSpec &file,
+ lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset,
+ lldb::offset_t file_offset,
+ lldb::offset_t length,
+ lldb_private::ModuleSpecList &specs);
+
+ static bool MagicBytesMatch(lldb::DataBufferSP &data_sp, lldb::addr_t offset,
+ lldb::addr_t length);
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ // ObjectFile Protocol.
+ bool ParseHeader() override;
+
+ bool SetLoadAddress(lldb_private::Target &target, lldb::addr_t value,
+ bool value_is_offset) override;
+
+ lldb::ByteOrder GetByteOrder() const override;
+
+ bool IsExecutable() const override;
+
+ uint32_t GetAddressByteSize() const override;
+
+ lldb_private::AddressClass GetAddressClass(lldb::addr_t file_addr) override;
+
+ lldb_private::Symtab *GetSymtab() override;
+
+ bool IsStripped() override;
+
+ void CreateSections(lldb_private::SectionList &unified_section_list) override;
+
+ void Dump(lldb_private::Stream *s) override;
+
+ lldb_private::ArchSpec GetArchitecture() override;
+
+ lldb_private::UUID GetUUID() override;
+
+ lldb_private::FileSpecList GetDebugSymbolFilePaths() override;
+
+ uint32_t GetDependentModules(lldb_private::FileSpecList &files) override;
+
+ lldb_private::Address
+ GetImageInfoAddress(lldb_private::Target *target) override;
+
+ lldb_private::Address GetEntryPointAddress() override;
+
+ lldb_private::Address GetBaseAddress() override;
+
+ ObjectFile::Type CalculateType() override;
+
+ ObjectFile::Strata CalculateStrata() override;
+
+ size_t ReadSectionData(lldb_private::Section *section,
+ lldb::offset_t section_offset, void *dst,
+ size_t dst_len) override;
+
+ size_t ReadSectionData(lldb_private::Section *section,
+ lldb_private::DataExtractor &section_data) override;
+
+ llvm::ArrayRef<elf::ELFProgramHeader> ProgramHeaders();
+ lldb_private::DataExtractor GetSegmentData(const elf::ELFProgramHeader &H);
+
+ llvm::StringRef
+ StripLinkerSymbolAnnotations(llvm::StringRef symbol_name) const override;
+
+ void RelocateSection(lldb_private::Section *section) override;
+
+protected:
+
+ std::vector<LoadableData>
+ GetLoadableData(lldb_private::Target &target) override;
+
+private:
+ ObjectFileELF(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset, const lldb_private::FileSpec *file,
+ lldb::offset_t offset, lldb::offset_t length);
+
+ ObjectFileELF(const lldb::ModuleSP &module_sp,
+ lldb::DataBufferSP &header_data_sp,
+ const lldb::ProcessSP &process_sp, lldb::addr_t header_addr);
+
+ typedef std::vector<elf::ELFProgramHeader> ProgramHeaderColl;
+
+ struct ELFSectionHeaderInfo : public elf::ELFSectionHeader {
+ lldb_private::ConstString section_name;
+ };
+
+ typedef std::vector<ELFSectionHeaderInfo> SectionHeaderColl;
+ typedef SectionHeaderColl::iterator SectionHeaderCollIter;
+ typedef SectionHeaderColl::const_iterator SectionHeaderCollConstIter;
+
+ typedef std::vector<elf::ELFDynamic> DynamicSymbolColl;
+ typedef DynamicSymbolColl::iterator DynamicSymbolCollIter;
+ typedef DynamicSymbolColl::const_iterator DynamicSymbolCollConstIter;
+
+ typedef std::map<lldb::addr_t, lldb_private::AddressClass>
+ FileAddressToAddressClassMap;
+
+ /// Version of this reader common to all plugins based on this class.
+ static const uint32_t m_plugin_version = 1;
+ static const uint32_t g_core_uuid_magic;
+
+ /// ELF file header.
+ elf::ELFHeader m_header;
+
+ /// ELF build ID.
+ lldb_private::UUID m_uuid;
+
+ /// ELF .gnu_debuglink file and crc data if available.
+ std::string m_gnu_debuglink_file;
+ uint32_t m_gnu_debuglink_crc;
+
+ /// Collection of program headers.
+ ProgramHeaderColl m_program_headers;
+
+ /// Collection of section headers.
+ SectionHeaderColl m_section_headers;
+
+ /// Collection of symbols from the dynamic table.
+ DynamicSymbolColl m_dynamic_symbols;
+
+ /// List of file specifications corresponding to the modules (shared
+ /// libraries) on which this object file depends.
+ mutable std::unique_ptr<lldb_private::FileSpecList> m_filespec_up;
+
+ /// Cached value of the entry point for this module.
+ lldb_private::Address m_entry_point_address;
+
+ /// The architecture detected from parsing elf file contents.
+ lldb_private::ArchSpec m_arch_spec;
+
+ /// The address class for each symbol in the elf file
+ FileAddressToAddressClassMap m_address_class_map;
+
+ /// Returns the index of the given section header.
+ size_t SectionIndex(const SectionHeaderCollIter &I);
+
+ /// Returns the index of the given section header.
+ size_t SectionIndex(const SectionHeaderCollConstIter &I) const;
+
+ // Parses the ELF program headers.
+ static size_t GetProgramHeaderInfo(ProgramHeaderColl &program_headers,
+ lldb_private::DataExtractor &object_data,
+ const elf::ELFHeader &header);
+
+ // Finds PT_NOTE segments and calculates their crc sum.
+ static uint32_t
+ CalculateELFNotesSegmentsCRC32(const ProgramHeaderColl &program_headers,
+ lldb_private::DataExtractor &data);
+
+ /// Parses all section headers present in this object file and populates
+ /// m_program_headers. This method will compute the header list only once.
+ /// Returns true iff the headers have been successfully parsed.
+ bool ParseProgramHeaders();
+
+ /// Parses all section headers present in this object file and populates
+ /// m_section_headers. This method will compute the header list only once.
+ /// Returns the number of headers parsed.
+ size_t ParseSectionHeaders();
+
+ lldb::SectionType GetSectionType(const ELFSectionHeaderInfo &H) const;
+
+ static void ParseARMAttributes(lldb_private::DataExtractor &data,
+ uint64_t length,
+ lldb_private::ArchSpec &arch_spec);
+
+ /// Parses the elf section headers and returns the uuid, debug link name,
+ /// crc, archspec.
+ static size_t GetSectionHeaderInfo(SectionHeaderColl &section_headers,
+ lldb_private::DataExtractor &object_data,
+ const elf::ELFHeader &header,
+ lldb_private::UUID &uuid,
+ std::string &gnu_debuglink_file,
+ uint32_t &gnu_debuglink_crc,
+ lldb_private::ArchSpec &arch_spec);
+
+ /// Scans the dynamic section and locates all dependent modules (shared
+ /// libraries) populating m_filespec_up. This method will compute the
+ /// dependent module list only once. Returns the number of dependent
+ /// modules parsed.
+ size_t ParseDependentModules();
+
+ /// Parses the dynamic symbol table and populates m_dynamic_symbols. The
+ /// vector retains the order as found in the object file. Returns the
+ /// number of dynamic symbols parsed.
+ size_t ParseDynamicSymbols();
+
+ /// Populates m_symtab_up will all non-dynamic linker symbols. This method
+ /// will parse the symbols only once. Returns the number of symbols parsed.
+ unsigned ParseSymbolTable(lldb_private::Symtab *symbol_table,
+ lldb::user_id_t start_id,
+ lldb_private::Section *symtab);
+
+ /// Helper routine for ParseSymbolTable().
+ unsigned ParseSymbols(lldb_private::Symtab *symbol_table,
+ lldb::user_id_t start_id,
+ lldb_private::SectionList *section_list,
+ const size_t num_symbols,
+ const lldb_private::DataExtractor &symtab_data,
+ const lldb_private::DataExtractor &strtab_data);
+
+ /// Scans the relocation entries and adds a set of artificial symbols to the
+ /// given symbol table for each PLT slot. Returns the number of symbols
+ /// added.
+ unsigned ParseTrampolineSymbols(lldb_private::Symtab *symbol_table,
+ lldb::user_id_t start_id,
+ const ELFSectionHeaderInfo *rela_hdr,
+ lldb::user_id_t section_id);
+
+ void ParseUnwindSymbols(lldb_private::Symtab *symbol_table,
+ lldb_private::DWARFCallFrameInfo *eh_frame);
+
+ /// Relocates debug sections
+ unsigned RelocateDebugSections(const elf::ELFSectionHeader *rel_hdr,
+ lldb::user_id_t rel_id,
+ lldb_private::Symtab *thetab);
+
+ unsigned ApplyRelocations(lldb_private::Symtab *symtab,
+ const elf::ELFHeader *hdr,
+ const elf::ELFSectionHeader *rel_hdr,
+ const elf::ELFSectionHeader *symtab_hdr,
+ const elf::ELFSectionHeader *debug_hdr,
+ lldb_private::DataExtractor &rel_data,
+ lldb_private::DataExtractor &symtab_data,
+ lldb_private::DataExtractor &debug_data,
+ lldb_private::Section *rel_section);
+
+ /// Loads the section name string table into m_shstr_data. Returns the
+ /// number of bytes constituting the table.
+ size_t GetSectionHeaderStringTable();
+
+ /// Utility method for looking up a section given its name. Returns the
+ /// index of the corresponding section or zero if no section with the given
+ /// name can be found (note that section indices are always 1 based, and so
+ /// section index 0 is never valid).
+ lldb::user_id_t GetSectionIndexByName(const char *name);
+
+ // Returns the ID of the first section that has the given type.
+ lldb::user_id_t GetSectionIndexByType(unsigned type);
+
+ /// Returns the section header with the given id or NULL.
+ const ELFSectionHeaderInfo *GetSectionHeaderByIndex(lldb::user_id_t id);
+
+ /// \name ELF header dump routines
+ //@{
+ static void DumpELFHeader(lldb_private::Stream *s,
+ const elf::ELFHeader &header);
+
+ static void DumpELFHeader_e_ident_EI_DATA(lldb_private::Stream *s,
+ unsigned char ei_data);
+
+ static void DumpELFHeader_e_type(lldb_private::Stream *s,
+ elf::elf_half e_type);
+ //@}
+
+ /// \name ELF program header dump routines
+ //@{
+ void DumpELFProgramHeaders(lldb_private::Stream *s);
+
+ static void DumpELFProgramHeader(lldb_private::Stream *s,
+ const elf::ELFProgramHeader &ph);
+
+ static void DumpELFProgramHeader_p_type(lldb_private::Stream *s,
+ elf::elf_word p_type);
+
+ static void DumpELFProgramHeader_p_flags(lldb_private::Stream *s,
+ elf::elf_word p_flags);
+ //@}
+
+ /// \name ELF section header dump routines
+ //@{
+ void DumpELFSectionHeaders(lldb_private::Stream *s);
+
+ static void DumpELFSectionHeader(lldb_private::Stream *s,
+ const ELFSectionHeaderInfo &sh);
+
+ static void DumpELFSectionHeader_sh_type(lldb_private::Stream *s,
+ elf::elf_word sh_type);
+
+ static void DumpELFSectionHeader_sh_flags(lldb_private::Stream *s,
+ elf::elf_xword sh_flags);
+ //@}
+
+ /// ELF dependent module dump routine.
+ void DumpDependentModules(lldb_private::Stream *s);
+
+ const elf::ELFDynamic *FindDynamicSymbol(unsigned tag);
+
+ unsigned PLTRelocationType();
+
+ static lldb_private::Status
+ RefineModuleDetailsFromNote(lldb_private::DataExtractor &data,
+ lldb_private::ArchSpec &arch_spec,
+ lldb_private::UUID &uuid);
+
+ bool AnySegmentHasPhysicalAddress();
+};
+
+#endif // liblldb_ObjectFileELF_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.cpp b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.cpp
new file mode 100644
index 000000000000..eaf973da3835
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.cpp
@@ -0,0 +1,258 @@
+//===-- ObjectFileJIT.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/ADT/StringRef.h"
+
+#include "ObjectFileJIT.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/FileSpecList.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/DataBuffer.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RangeMap.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/Timer.h"
+#include "lldb/Utility/UUID.h"
+
+#ifndef __APPLE__
+#include "Utility/UuidCompatibility.h"
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+
+void ObjectFileJIT::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ CreateMemoryInstance, GetModuleSpecifications);
+}
+
+void ObjectFileJIT::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString ObjectFileJIT::GetPluginNameStatic() {
+ static ConstString g_name("jit");
+ return g_name;
+}
+
+const char *ObjectFileJIT::GetPluginDescriptionStatic() {
+ return "JIT code object file";
+}
+
+ObjectFile *ObjectFileJIT::CreateInstance(const lldb::ModuleSP &module_sp,
+ DataBufferSP &data_sp,
+ lldb::offset_t data_offset,
+ const FileSpec *file,
+ lldb::offset_t file_offset,
+ lldb::offset_t length) {
+ // JIT'ed object file is backed by the ObjectFileJITDelegate, never read from
+ // a file
+ return nullptr;
+}
+
+ObjectFile *ObjectFileJIT::CreateMemoryInstance(const lldb::ModuleSP &module_sp,
+ DataBufferSP &data_sp,
+ const ProcessSP &process_sp,
+ lldb::addr_t header_addr) {
+ // JIT'ed object file is backed by the ObjectFileJITDelegate, never read from
+ // memory
+ return nullptr;
+}
+
+size_t ObjectFileJIT::GetModuleSpecifications(
+ const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset, lldb::offset_t file_offset,
+ lldb::offset_t length, lldb_private::ModuleSpecList &specs) {
+ // JIT'ed object file can't be read from a file on disk
+ return 0;
+}
+
+ObjectFileJIT::ObjectFileJIT(const lldb::ModuleSP &module_sp,
+ const ObjectFileJITDelegateSP &delegate_sp)
+ : ObjectFile(module_sp, nullptr, 0, 0, DataBufferSP(), 0), m_delegate_wp() {
+ if (delegate_sp) {
+ m_delegate_wp = delegate_sp;
+ m_data.SetByteOrder(delegate_sp->GetByteOrder());
+ m_data.SetAddressByteSize(delegate_sp->GetAddressByteSize());
+ }
+}
+
+ObjectFileJIT::~ObjectFileJIT() {}
+
+bool ObjectFileJIT::ParseHeader() {
+ // JIT code is never in a file, nor is it required to have any header
+ return false;
+}
+
+ByteOrder ObjectFileJIT::GetByteOrder() const { return m_data.GetByteOrder(); }
+
+bool ObjectFileJIT::IsExecutable() const { return false; }
+
+uint32_t ObjectFileJIT::GetAddressByteSize() const {
+ return m_data.GetAddressByteSize();
+}
+
+Symtab *ObjectFileJIT::GetSymtab() {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ if (m_symtab_up == nullptr) {
+ m_symtab_up.reset(new Symtab(this));
+ std::lock_guard<std::recursive_mutex> symtab_guard(
+ m_symtab_up->GetMutex());
+ ObjectFileJITDelegateSP delegate_sp(m_delegate_wp.lock());
+ if (delegate_sp)
+ delegate_sp->PopulateSymtab(this, *m_symtab_up);
+ // TODO: get symbols from delegate
+ m_symtab_up->Finalize();
+ }
+ }
+ return m_symtab_up.get();
+}
+
+bool ObjectFileJIT::IsStripped() {
+ return false; // JIT code that is in a module is never stripped
+}
+
+void ObjectFileJIT::CreateSections(SectionList &unified_section_list) {
+ if (!m_sections_up) {
+ m_sections_up.reset(new SectionList());
+ ObjectFileJITDelegateSP delegate_sp(m_delegate_wp.lock());
+ if (delegate_sp) {
+ delegate_sp->PopulateSectionList(this, *m_sections_up);
+ unified_section_list = *m_sections_up;
+ }
+ }
+}
+
+void ObjectFileJIT::Dump(Stream *s) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+ s->Printf("%p: ", static_cast<void *>(this));
+ s->Indent();
+ s->PutCString("ObjectFileJIT");
+
+ if (ArchSpec arch = GetArchitecture())
+ *s << ", arch = " << arch.GetArchitectureName();
+
+ s->EOL();
+
+ SectionList *sections = GetSectionList();
+ if (sections)
+ sections->Dump(s, nullptr, true, UINT32_MAX);
+
+ if (m_symtab_up)
+ m_symtab_up->Dump(s, nullptr, eSortOrderNone);
+ }
+}
+
+UUID ObjectFileJIT::GetUUID() {
+ // TODO: maybe get from delegate, not needed for first pass
+ return UUID();
+}
+
+uint32_t ObjectFileJIT::GetDependentModules(FileSpecList &files) {
+ // JIT modules don't have dependencies, but they could
+ // if external functions are called and we know where they are
+ files.Clear();
+ return 0;
+}
+
+lldb_private::Address ObjectFileJIT::GetEntryPointAddress() {
+ return Address();
+}
+
+lldb_private::Address ObjectFileJIT::GetBaseAddress() { return Address(); }
+
+ObjectFile::Type ObjectFileJIT::CalculateType() { return eTypeJIT; }
+
+ObjectFile::Strata ObjectFileJIT::CalculateStrata() { return eStrataJIT; }
+
+ArchSpec ObjectFileJIT::GetArchitecture() {
+ if (ObjectFileJITDelegateSP delegate_sp = m_delegate_wp.lock())
+ return delegate_sp->GetArchitecture();
+ return ArchSpec();
+}
+
+// PluginInterface protocol
+lldb_private::ConstString ObjectFileJIT::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ObjectFileJIT::GetPluginVersion() { return 1; }
+
+bool ObjectFileJIT::SetLoadAddress(Target &target, lldb::addr_t value,
+ bool value_is_offset) {
+ size_t num_loaded_sections = 0;
+ SectionList *section_list = GetSectionList();
+ if (section_list) {
+ const size_t num_sections = section_list->GetSize();
+ // "value" is an offset to apply to each top level segment
+ for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) {
+ // Iterate through the object file sections to find all of the sections
+ // that size on disk (to avoid __PAGEZERO) and load them
+ SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx));
+ if (section_sp && section_sp->GetFileSize() > 0 &&
+ !section_sp->IsThreadSpecific()) {
+ if (target.GetSectionLoadList().SetSectionLoadAddress(
+ section_sp, section_sp->GetFileAddress() + value))
+ ++num_loaded_sections;
+ }
+ }
+ }
+ return num_loaded_sections > 0;
+}
+
+size_t ObjectFileJIT::ReadSectionData(lldb_private::Section *section,
+ lldb::offset_t section_offset, void *dst,
+ size_t dst_len) {
+ lldb::offset_t file_size = section->GetFileSize();
+ if (section_offset < file_size) {
+ size_t src_len = file_size - section_offset;
+ if (src_len > dst_len)
+ src_len = dst_len;
+ const uint8_t *src =
+ ((uint8_t *)(uintptr_t)section->GetFileOffset()) + section_offset;
+
+ memcpy(dst, src, src_len);
+ return src_len;
+ }
+ return 0;
+}
+
+size_t ObjectFileJIT::ReadSectionData(
+ lldb_private::Section *section,
+ lldb_private::DataExtractor &section_data) {
+ if (section->GetFileSize()) {
+ const void *src = (void *)(uintptr_t)section->GetFileOffset();
+
+ DataBufferSP data_sp =
+ std::make_shared<DataBufferHeap>(src, section->GetFileSize());
+ section_data.SetData(data_sp, 0, data_sp->GetByteSize());
+ section_data.SetByteOrder(GetByteOrder());
+ section_data.SetAddressByteSize(GetAddressByteSize());
+ return section_data.GetByteSize();
+ }
+ section_data.Clear();
+ return 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.h b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.h
new file mode 100644
index 000000000000..99241126cd1a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.h
@@ -0,0 +1,100 @@
+//===-- ObjectFileJIT.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 liblldb_ObjectFileJIT_h_
+#define liblldb_ObjectFileJIT_h_
+
+#include "lldb/Core/Address.h"
+#include "lldb/Symbol/ObjectFile.h"
+
+// This class needs to be hidden as eventually belongs in a plugin that
+// will export the ObjectFile protocol
+class ObjectFileJIT : public lldb_private::ObjectFile {
+public:
+ ObjectFileJIT(const lldb::ModuleSP &module_sp,
+ const lldb::ObjectFileJITDelegateSP &delegate_sp);
+
+ ~ObjectFileJIT() override;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::ObjectFile *
+ CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset, const lldb_private::FileSpec *file,
+ lldb::offset_t file_offset, lldb::offset_t length);
+
+ static lldb_private::ObjectFile *CreateMemoryInstance(
+ const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
+ const lldb::ProcessSP &process_sp, lldb::addr_t header_addr);
+
+ static size_t GetModuleSpecifications(const lldb_private::FileSpec &file,
+ lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset,
+ lldb::offset_t file_offset,
+ lldb::offset_t length,
+ lldb_private::ModuleSpecList &specs);
+
+ // Member Functions
+ bool ParseHeader() override;
+
+ bool SetLoadAddress(lldb_private::Target &target, lldb::addr_t value,
+ bool value_is_offset) override;
+
+ lldb::ByteOrder GetByteOrder() const override;
+
+ bool IsExecutable() const override;
+
+ uint32_t GetAddressByteSize() const override;
+
+ lldb_private::Symtab *GetSymtab() override;
+
+ bool IsStripped() override;
+
+ void CreateSections(lldb_private::SectionList &unified_section_list) override;
+
+ void Dump(lldb_private::Stream *s) override;
+
+ lldb_private::ArchSpec GetArchitecture() override;
+
+ lldb_private::UUID GetUUID() override;
+
+ uint32_t GetDependentModules(lldb_private::FileSpecList &files) override;
+
+ size_t ReadSectionData(lldb_private::Section *section,
+ lldb::offset_t section_offset, void *dst,
+ size_t dst_len) override;
+
+ size_t
+ ReadSectionData(lldb_private::Section *section,
+ lldb_private::DataExtractor &section_data) override;
+
+ lldb_private::Address GetEntryPointAddress() override;
+
+ lldb_private::Address GetBaseAddress() override;
+
+ ObjectFile::Type CalculateType() override;
+
+ ObjectFile::Strata CalculateStrata() override;
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ lldb::ObjectFileJITDelegateWP m_delegate_wp;
+};
+
+#endif // liblldb_ObjectFileJIT_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp b/contrib/llvm-project/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp
new file mode 100644
index 000000000000..c1fe0cc8ddda
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp
@@ -0,0 +1,419 @@
+//===-- OperatingSystemPython.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_DISABLE_PYTHON
+
+#include "OperatingSystemPython.h"
+
+#include "Plugins/Process/Utility/DynamicRegisterInfo.h"
+#include "Plugins/Process/Utility/RegisterContextDummy.h"
+#include "Plugins/Process/Utility/RegisterContextMemory.h"
+#include "Plugins/Process/Utility/ThreadMemory.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadList.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StructuredData.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+void OperatingSystemPython::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ nullptr);
+}
+
+void OperatingSystemPython::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+OperatingSystem *OperatingSystemPython::CreateInstance(Process *process,
+ bool force) {
+ // Python OperatingSystem plug-ins must be requested by name, so force must
+ // be true
+ FileSpec python_os_plugin_spec(process->GetPythonOSPluginPath());
+ if (python_os_plugin_spec &&
+ FileSystem::Instance().Exists(python_os_plugin_spec)) {
+ std::unique_ptr<OperatingSystemPython> os_up(
+ new OperatingSystemPython(process, python_os_plugin_spec));
+ if (os_up.get() && os_up->IsValid())
+ return os_up.release();
+ }
+ return nullptr;
+}
+
+ConstString OperatingSystemPython::GetPluginNameStatic() {
+ static ConstString g_name("python");
+ return g_name;
+}
+
+const char *OperatingSystemPython::GetPluginDescriptionStatic() {
+ return "Operating system plug-in that gathers OS information from a python "
+ "class that implements the necessary OperatingSystem functionality.";
+}
+
+OperatingSystemPython::OperatingSystemPython(lldb_private::Process *process,
+ const FileSpec &python_module_path)
+ : OperatingSystem(process), m_thread_list_valobj_sp(), m_register_info_up(),
+ m_interpreter(nullptr), m_python_object_sp() {
+ if (!process)
+ return;
+ TargetSP target_sp = process->CalculateTarget();
+ if (!target_sp)
+ return;
+ m_interpreter = target_sp->GetDebugger().GetScriptInterpreter();
+ if (m_interpreter) {
+
+ std::string os_plugin_class_name(
+ python_module_path.GetFilename().AsCString(""));
+ if (!os_plugin_class_name.empty()) {
+ const bool init_session = false;
+ const bool allow_reload = true;
+ char python_module_path_cstr[PATH_MAX];
+ python_module_path.GetPath(python_module_path_cstr,
+ sizeof(python_module_path_cstr));
+ Status error;
+ if (m_interpreter->LoadScriptingModule(
+ python_module_path_cstr, allow_reload, init_session, error)) {
+ // Strip the ".py" extension if there is one
+ size_t py_extension_pos = os_plugin_class_name.rfind(".py");
+ if (py_extension_pos != std::string::npos)
+ os_plugin_class_name.erase(py_extension_pos);
+ // Add ".OperatingSystemPlugIn" to the module name to get a string like
+ // "modulename.OperatingSystemPlugIn"
+ os_plugin_class_name += ".OperatingSystemPlugIn";
+ StructuredData::ObjectSP object_sp =
+ m_interpreter->OSPlugin_CreatePluginObject(
+ os_plugin_class_name.c_str(), process->CalculateProcess());
+ if (object_sp && object_sp->IsValid())
+ m_python_object_sp = object_sp;
+ }
+ }
+ }
+}
+
+OperatingSystemPython::~OperatingSystemPython() {}
+
+DynamicRegisterInfo *OperatingSystemPython::GetDynamicRegisterInfo() {
+ if (m_register_info_up == nullptr) {
+ if (!m_interpreter || !m_python_object_sp)
+ return nullptr;
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OS));
+
+ if (log)
+ log->Printf("OperatingSystemPython::GetDynamicRegisterInfo() fetching "
+ "thread register definitions from python for pid %" PRIu64,
+ m_process->GetID());
+
+ StructuredData::DictionarySP dictionary =
+ m_interpreter->OSPlugin_RegisterInfo(m_python_object_sp);
+ if (!dictionary)
+ return nullptr;
+
+ m_register_info_up.reset(new DynamicRegisterInfo(
+ *dictionary, m_process->GetTarget().GetArchitecture()));
+ assert(m_register_info_up->GetNumRegisters() > 0);
+ assert(m_register_info_up->GetNumRegisterSets() > 0);
+ }
+ return m_register_info_up.get();
+}
+
+// PluginInterface protocol
+ConstString OperatingSystemPython::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t OperatingSystemPython::GetPluginVersion() { return 1; }
+
+bool OperatingSystemPython::UpdateThreadList(ThreadList &old_thread_list,
+ ThreadList &core_thread_list,
+ ThreadList &new_thread_list) {
+ if (!m_interpreter || !m_python_object_sp)
+ return false;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OS));
+
+ // First thing we have to do is to try to get the API lock, and the
+ // interpreter lock. We're going to change the thread content of the process,
+ // and we're going to use python, which requires the API lock to do it. We
+ // need the interpreter lock to make sure thread_info_dict stays alive.
+ //
+ // If someone already has the API lock, that is ok, we just want to avoid
+ // external code from making new API calls while this call is happening.
+ //
+ // This is a recursive lock so we can grant it to any Python code called on
+ // the stack below us.
+ Target &target = m_process->GetTarget();
+ std::unique_lock<std::recursive_mutex> api_lock(target.GetAPIMutex(),
+ std::defer_lock);
+ api_lock.try_lock();
+ auto interpreter_lock = m_interpreter->AcquireInterpreterLock();
+
+ if (log)
+ log->Printf("OperatingSystemPython::UpdateThreadList() fetching thread "
+ "data from python for pid %" PRIu64,
+ m_process->GetID());
+
+ // The threads that are in "new_thread_list" upon entry are the threads from
+ // the lldb_private::Process subclass, no memory threads will be in this
+ // list.
+ StructuredData::ArraySP threads_list =
+ m_interpreter->OSPlugin_ThreadsInfo(m_python_object_sp);
+
+ const uint32_t num_cores = core_thread_list.GetSize(false);
+
+ // Make a map so we can keep track of which cores were used from the
+ // core_thread list. Any real threads/cores that weren't used should later be
+ // put back into the "new_thread_list".
+ std::vector<bool> core_used_map(num_cores, false);
+ if (threads_list) {
+ if (log) {
+ StreamString strm;
+ threads_list->Dump(strm);
+ log->Printf("threads_list = %s", strm.GetData());
+ }
+
+ const uint32_t num_threads = threads_list->GetSize();
+ for (uint32_t i = 0; i < num_threads; ++i) {
+ StructuredData::ObjectSP thread_dict_obj =
+ threads_list->GetItemAtIndex(i);
+ if (auto thread_dict = thread_dict_obj->GetAsDictionary()) {
+ ThreadSP thread_sp(CreateThreadFromThreadInfo(
+ *thread_dict, core_thread_list, old_thread_list, core_used_map,
+ nullptr));
+ if (thread_sp)
+ new_thread_list.AddThread(thread_sp);
+ }
+ }
+ }
+
+ // Any real core threads that didn't end up backing a memory thread should
+ // still be in the main thread list, and they should be inserted at the
+ // beginning of the list
+ uint32_t insert_idx = 0;
+ for (uint32_t core_idx = 0; core_idx < num_cores; ++core_idx) {
+ if (!core_used_map[core_idx]) {
+ new_thread_list.InsertThread(
+ core_thread_list.GetThreadAtIndex(core_idx, false), insert_idx);
+ ++insert_idx;
+ }
+ }
+
+ return new_thread_list.GetSize(false) > 0;
+}
+
+ThreadSP OperatingSystemPython::CreateThreadFromThreadInfo(
+ StructuredData::Dictionary &thread_dict, ThreadList &core_thread_list,
+ ThreadList &old_thread_list, std::vector<bool> &core_used_map,
+ bool *did_create_ptr) {
+ ThreadSP thread_sp;
+ tid_t tid = LLDB_INVALID_THREAD_ID;
+ if (!thread_dict.GetValueForKeyAsInteger("tid", tid))
+ return ThreadSP();
+
+ uint32_t core_number;
+ addr_t reg_data_addr;
+ llvm::StringRef name;
+ llvm::StringRef queue;
+
+ thread_dict.GetValueForKeyAsInteger("core", core_number, UINT32_MAX);
+ thread_dict.GetValueForKeyAsInteger("register_data_addr", reg_data_addr,
+ LLDB_INVALID_ADDRESS);
+ thread_dict.GetValueForKeyAsString("name", name);
+ thread_dict.GetValueForKeyAsString("queue", queue);
+
+ // See if a thread already exists for "tid"
+ thread_sp = old_thread_list.FindThreadByID(tid, false);
+ if (thread_sp) {
+ // A thread already does exist for "tid", make sure it was an operating
+ // system
+ // plug-in generated thread.
+ if (!IsOperatingSystemPluginThread(thread_sp)) {
+ // We have thread ID overlap between the protocol threads and the
+ // operating system threads, clear the thread so we create an operating
+ // system thread for this.
+ thread_sp.reset();
+ }
+ }
+
+ if (!thread_sp) {
+ if (did_create_ptr)
+ *did_create_ptr = true;
+ thread_sp = std::make_shared<ThreadMemory>(*m_process, tid, name, queue,
+ reg_data_addr);
+ }
+
+ if (core_number < core_thread_list.GetSize(false)) {
+ ThreadSP core_thread_sp(
+ core_thread_list.GetThreadAtIndex(core_number, false));
+ if (core_thread_sp) {
+ // Keep track of which cores were set as the backing thread for memory
+ // threads...
+ if (core_number < core_used_map.size())
+ core_used_map[core_number] = true;
+
+ ThreadSP backing_core_thread_sp(core_thread_sp->GetBackingThread());
+ if (backing_core_thread_sp) {
+ thread_sp->SetBackingThread(backing_core_thread_sp);
+ } else {
+ thread_sp->SetBackingThread(core_thread_sp);
+ }
+ }
+ }
+ return thread_sp;
+}
+
+void OperatingSystemPython::ThreadWasSelected(Thread *thread) {}
+
+RegisterContextSP
+OperatingSystemPython::CreateRegisterContextForThread(Thread *thread,
+ addr_t reg_data_addr) {
+ RegisterContextSP reg_ctx_sp;
+ if (!m_interpreter || !m_python_object_sp || !thread)
+ return reg_ctx_sp;
+
+ if (!IsOperatingSystemPluginThread(thread->shared_from_this()))
+ return reg_ctx_sp;
+
+ // First thing we have to do is to try to get the API lock, and the
+ // interpreter lock. We're going to change the thread content of the process,
+ // and we're going to use python, which requires the API lock to do it. We
+ // need the interpreter lock to make sure thread_info_dict stays alive.
+ //
+ // If someone already has the API lock, that is ok, we just want to avoid
+ // external code from making new API calls while this call is happening.
+ //
+ // This is a recursive lock so we can grant it to any Python code called on
+ // the stack below us.
+ Target &target = m_process->GetTarget();
+ std::unique_lock<std::recursive_mutex> api_lock(target.GetAPIMutex(),
+ std::defer_lock);
+ api_lock.try_lock();
+ auto interpreter_lock = m_interpreter->AcquireInterpreterLock();
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ if (reg_data_addr != LLDB_INVALID_ADDRESS) {
+ // The registers data is in contiguous memory, just create the register
+ // context using the address provided
+ if (log)
+ log->Printf("OperatingSystemPython::CreateRegisterContextForThread (tid "
+ "= 0x%" PRIx64 ", 0x%" PRIx64 ", reg_data_addr = 0x%" PRIx64
+ ") creating memory register context",
+ thread->GetID(), thread->GetProtocolID(), reg_data_addr);
+ reg_ctx_sp = std::make_shared<RegisterContextMemory>(
+ *thread, 0, *GetDynamicRegisterInfo(), reg_data_addr);
+ } else {
+ // No register data address is provided, query the python plug-in to let it
+ // make up the data as it sees fit
+ if (log)
+ log->Printf("OperatingSystemPython::CreateRegisterContextForThread (tid "
+ "= 0x%" PRIx64 ", 0x%" PRIx64
+ ") fetching register data from python",
+ thread->GetID(), thread->GetProtocolID());
+
+ StructuredData::StringSP reg_context_data =
+ m_interpreter->OSPlugin_RegisterContextData(m_python_object_sp,
+ thread->GetID());
+ if (reg_context_data) {
+ std::string value = reg_context_data->GetValue();
+ DataBufferSP data_sp(new DataBufferHeap(value.c_str(), value.length()));
+ if (data_sp->GetByteSize()) {
+ RegisterContextMemory *reg_ctx_memory = new RegisterContextMemory(
+ *thread, 0, *GetDynamicRegisterInfo(), LLDB_INVALID_ADDRESS);
+ if (reg_ctx_memory) {
+ reg_ctx_sp.reset(reg_ctx_memory);
+ reg_ctx_memory->SetAllRegisterData(data_sp);
+ }
+ }
+ }
+ }
+ // if we still have no register data, fallback on a dummy context to avoid
+ // crashing
+ if (!reg_ctx_sp) {
+ if (log)
+ log->Printf("OperatingSystemPython::CreateRegisterContextForThread (tid "
+ "= 0x%" PRIx64 ") forcing a dummy register context",
+ thread->GetID());
+ reg_ctx_sp = std::make_shared<RegisterContextDummy>(
+ *thread, 0, target.GetArchitecture().GetAddressByteSize());
+ }
+ return reg_ctx_sp;
+}
+
+StopInfoSP
+OperatingSystemPython::CreateThreadStopReason(lldb_private::Thread *thread) {
+ // We should have gotten the thread stop info from the dictionary of data for
+ // the thread in the initial call to get_thread_info(), this should have been
+ // cached so we can return it here
+ StopInfoSP
+ stop_info_sp; //(StopInfo::CreateStopReasonWithSignal (*thread, SIGSTOP));
+ return stop_info_sp;
+}
+
+lldb::ThreadSP OperatingSystemPython::CreateThread(lldb::tid_t tid,
+ addr_t context) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ if (log)
+ log->Printf("OperatingSystemPython::CreateThread (tid = 0x%" PRIx64
+ ", context = 0x%" PRIx64 ") fetching register data from python",
+ tid, context);
+
+ if (m_interpreter && m_python_object_sp) {
+ // First thing we have to do is to try to get the API lock, and the
+ // interpreter lock. We're going to change the thread content of the
+ // process, and we're going to use python, which requires the API lock to
+ // do it. We need the interpreter lock to make sure thread_info_dict stays
+ // alive.
+ //
+ // If someone already has the API lock, that is ok, we just want to avoid
+ // external code from making new API calls while this call is happening.
+ //
+ // This is a recursive lock so we can grant it to any Python code called on
+ // the stack below us.
+ Target &target = m_process->GetTarget();
+ std::unique_lock<std::recursive_mutex> api_lock(target.GetAPIMutex(),
+ std::defer_lock);
+ api_lock.try_lock();
+ auto interpreter_lock = m_interpreter->AcquireInterpreterLock();
+
+ StructuredData::DictionarySP thread_info_dict =
+ m_interpreter->OSPlugin_CreateThread(m_python_object_sp, tid, context);
+ std::vector<bool> core_used_map;
+ if (thread_info_dict) {
+ ThreadList core_threads(m_process);
+ ThreadList &thread_list = m_process->GetThreadList();
+ bool did_create = false;
+ ThreadSP thread_sp(
+ CreateThreadFromThreadInfo(*thread_info_dict, core_threads,
+ thread_list, core_used_map, &did_create));
+ if (did_create)
+ thread_list.AddThread(thread_sp);
+ return thread_sp;
+ }
+ }
+ return ThreadSP();
+}
+
+#endif // #ifndef LLDB_DISABLE_PYTHON
diff --git a/contrib/llvm-project/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.h b/contrib/llvm-project/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.h
new file mode 100644
index 000000000000..e76227ddb981
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.h
@@ -0,0 +1,85 @@
+//===-- OperatingSystemPython.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 liblldb_OperatingSystemPython_h_
+#define liblldb_OperatingSystemPython_h_
+
+#ifndef LLDB_DISABLE_PYTHON
+
+#include "lldb/Target/OperatingSystem.h"
+#include "lldb/Utility/StructuredData.h"
+
+class DynamicRegisterInfo;
+
+namespace lldb_private {
+class ScriptInterpreter;
+}
+
+class OperatingSystemPython : public lldb_private::OperatingSystem {
+public:
+ OperatingSystemPython(lldb_private::Process *process,
+ const lldb_private::FileSpec &python_module_path);
+
+ ~OperatingSystemPython() override;
+
+ // Static Functions
+ static lldb_private::OperatingSystem *
+ CreateInstance(lldb_private::Process *process, bool force);
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ // lldb_private::PluginInterface Methods
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ // lldb_private::OperatingSystem Methods
+ bool UpdateThreadList(lldb_private::ThreadList &old_thread_list,
+ lldb_private::ThreadList &real_thread_list,
+ lldb_private::ThreadList &new_thread_list) override;
+
+ void ThreadWasSelected(lldb_private::Thread *thread) override;
+
+ lldb::RegisterContextSP
+ CreateRegisterContextForThread(lldb_private::Thread *thread,
+ lldb::addr_t reg_data_addr) override;
+
+ lldb::StopInfoSP
+ CreateThreadStopReason(lldb_private::Thread *thread) override;
+
+ // Method for lazy creation of threads on demand
+ lldb::ThreadSP CreateThread(lldb::tid_t tid, lldb::addr_t context) override;
+
+protected:
+ bool IsValid() const {
+ return m_python_object_sp && m_python_object_sp->IsValid();
+ }
+
+ lldb::ThreadSP CreateThreadFromThreadInfo(
+ lldb_private::StructuredData::Dictionary &thread_dict,
+ lldb_private::ThreadList &core_thread_list,
+ lldb_private::ThreadList &old_thread_list,
+ std::vector<bool> &core_used_map, bool *did_create_ptr);
+
+ DynamicRegisterInfo *GetDynamicRegisterInfo();
+
+ lldb::ValueObjectSP m_thread_list_valobj_sp;
+ std::unique_ptr<DynamicRegisterInfo> m_register_info_up;
+ lldb_private::ScriptInterpreter *m_interpreter;
+ lldb_private::StructuredData::ObjectSP m_python_object_sp;
+};
+
+#endif // LLDB_DISABLE_PYTHON
+
+#endif // liblldb_OperatingSystemPython_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp b/contrib/llvm-project/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp
new file mode 100644
index 000000000000..946f0ea3a5cf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp
@@ -0,0 +1,321 @@
+//===-- PlatformFreeBSD.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 "PlatformFreeBSD.h"
+#include "lldb/Host/Config.h"
+
+#include <stdio.h>
+#ifndef LLDB_DISABLE_POSIX
+#include <sys/utsname.h>
+#endif
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/BreakpointSite.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+
+// Define these constants from FreeBSD mman.h for use when targeting remote
+// FreeBSD systems even when host has different values.
+#define MAP_PRIVATE 0x0002
+#define MAP_ANON 0x1000
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::platform_freebsd;
+
+static uint32_t g_initialize_count = 0;
+
+
+PlatformSP PlatformFreeBSD::CreateInstance(bool force, const ArchSpec *arch) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
+ LLDB_LOG(log, "force = {0}, arch=({1}, {2})", force,
+ arch ? arch->GetArchitectureName() : "<null>",
+ arch ? arch->GetTriple().getTriple() : "<null>");
+
+ bool create = force;
+ if (!create && arch && arch->IsValid()) {
+ const llvm::Triple &triple = arch->GetTriple();
+ switch (triple.getOS()) {
+ case llvm::Triple::FreeBSD:
+ create = true;
+ break;
+
+#if defined(__FreeBSD__)
+ // Only accept "unknown" for the OS if the host is BSD and it "unknown"
+ // wasn't specified (it was just returned because it was NOT specified)
+ case llvm::Triple::OSType::UnknownOS:
+ create = !arch->TripleOSWasSpecified();
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+ LLDB_LOG(log, "create = {0}", create);
+ if (create) {
+ return PlatformSP(new PlatformFreeBSD(false));
+ }
+ return PlatformSP();
+}
+
+ConstString PlatformFreeBSD::GetPluginNameStatic(bool is_host) {
+ if (is_host) {
+ static ConstString g_host_name(Platform::GetHostPlatformName());
+ return g_host_name;
+ } else {
+ static ConstString g_remote_name("remote-freebsd");
+ return g_remote_name;
+ }
+}
+
+const char *PlatformFreeBSD::GetPluginDescriptionStatic(bool is_host) {
+ if (is_host)
+ return "Local FreeBSD user platform plug-in.";
+ else
+ return "Remote FreeBSD user platform plug-in.";
+}
+
+ConstString PlatformFreeBSD::GetPluginName() {
+ return GetPluginNameStatic(IsHost());
+}
+
+void PlatformFreeBSD::Initialize() {
+ Platform::Initialize();
+
+ if (g_initialize_count++ == 0) {
+#if defined(__FreeBSD__)
+ PlatformSP default_platform_sp(new PlatformFreeBSD(true));
+ default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture());
+ Platform::SetHostPlatform(default_platform_sp);
+#endif
+ PluginManager::RegisterPlugin(
+ PlatformFreeBSD::GetPluginNameStatic(false),
+ PlatformFreeBSD::GetPluginDescriptionStatic(false),
+ PlatformFreeBSD::CreateInstance, nullptr);
+ }
+}
+
+void PlatformFreeBSD::Terminate() {
+ if (g_initialize_count > 0) {
+ if (--g_initialize_count == 0) {
+ PluginManager::UnregisterPlugin(PlatformFreeBSD::CreateInstance);
+ }
+ }
+
+ PlatformPOSIX::Terminate();
+}
+
+/// Default Constructor
+PlatformFreeBSD::PlatformFreeBSD(bool is_host)
+ : PlatformPOSIX(is_host) // This is the local host platform
+{}
+
+PlatformFreeBSD::~PlatformFreeBSD() = default;
+
+bool PlatformFreeBSD::GetSupportedArchitectureAtIndex(uint32_t idx,
+ ArchSpec &arch) {
+ if (IsHost()) {
+ ArchSpec hostArch = HostInfo::GetArchitecture(HostInfo::eArchKindDefault);
+ if (hostArch.GetTriple().isOSFreeBSD()) {
+ if (idx == 0) {
+ arch = hostArch;
+ return arch.IsValid();
+ } else if (idx == 1) {
+ // If the default host architecture is 64-bit, look for a 32-bit
+ // variant
+ if (hostArch.IsValid() && hostArch.GetTriple().isArch64Bit()) {
+ arch = HostInfo::GetArchitecture(HostInfo::eArchKind32);
+ return arch.IsValid();
+ }
+ }
+ }
+ } else {
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->GetSupportedArchitectureAtIndex(idx, arch);
+
+ llvm::Triple triple;
+ // Set the OS to FreeBSD
+ triple.setOS(llvm::Triple::FreeBSD);
+ // Set the architecture
+ switch (idx) {
+ case 0:
+ triple.setArchName("x86_64");
+ break;
+ case 1:
+ triple.setArchName("i386");
+ break;
+ case 2:
+ triple.setArchName("aarch64");
+ break;
+ case 3:
+ triple.setArchName("arm");
+ break;
+ case 4:
+ triple.setArchName("mips64");
+ break;
+ case 5:
+ triple.setArchName("mips");
+ break;
+ case 6:
+ triple.setArchName("ppc64");
+ break;
+ case 7:
+ triple.setArchName("ppc");
+ break;
+ default:
+ return false;
+ }
+ // Leave the vendor as "llvm::Triple:UnknownVendor" and don't specify the
+ // vendor by calling triple.SetVendorName("unknown") so that it is a
+ // "unspecified unknown". This means when someone calls
+ // triple.GetVendorName() it will return an empty string which indicates
+ // that the vendor can be set when two architectures are merged
+
+ // Now set the triple into "arch" and return true
+ arch.SetTriple(triple);
+ return true;
+ }
+ return false;
+}
+
+void PlatformFreeBSD::GetStatus(Stream &strm) {
+ Platform::GetStatus(strm);
+
+#ifndef LLDB_DISABLE_POSIX
+ // Display local kernel information only when we are running in host mode.
+ // Otherwise, we would end up printing non-FreeBSD information (when running
+ // on Mac OS for example).
+ if (IsHost()) {
+ struct utsname un;
+
+ if (uname(&un))
+ return;
+
+ strm.Printf(" Kernel: %s\n", un.sysname);
+ strm.Printf(" Release: %s\n", un.release);
+ strm.Printf(" Version: %s\n", un.version);
+ }
+#endif
+}
+
+size_t
+PlatformFreeBSD::GetSoftwareBreakpointTrapOpcode(Target &target,
+ BreakpointSite *bp_site) {
+ switch (target.GetArchitecture().GetMachine()) {
+ case llvm::Triple::arm: {
+ lldb::BreakpointLocationSP bp_loc_sp(bp_site->GetOwnerAtIndex(0));
+ AddressClass addr_class = AddressClass::eUnknown;
+
+ if (bp_loc_sp) {
+ addr_class = bp_loc_sp->GetAddress().GetAddressClass();
+ if (addr_class == AddressClass::eUnknown &&
+ (bp_loc_sp->GetAddress().GetFileAddress() & 1))
+ addr_class = AddressClass::eCodeAlternateISA;
+ }
+
+ if (addr_class == AddressClass::eCodeAlternateISA) {
+ // TODO: Enable when FreeBSD supports thumb breakpoints.
+ // FreeBSD kernel as of 10.x, does not support thumb breakpoints
+ return 0;
+ }
+
+ static const uint8_t g_arm_breakpoint_opcode[] = {0xFE, 0xDE, 0xFF, 0xE7};
+ size_t trap_opcode_size = sizeof(g_arm_breakpoint_opcode);
+ assert(bp_site);
+ if (bp_site->SetTrapOpcode(g_arm_breakpoint_opcode, trap_opcode_size))
+ return trap_opcode_size;
+ }
+ LLVM_FALLTHROUGH;
+ default:
+ return Platform::GetSoftwareBreakpointTrapOpcode(target, bp_site);
+ }
+}
+
+Status PlatformFreeBSD::LaunchProcess(ProcessLaunchInfo &launch_info) {
+ Status error;
+ if (IsHost()) {
+ error = Platform::LaunchProcess(launch_info);
+ } else {
+ if (m_remote_platform_sp)
+ error = m_remote_platform_sp->LaunchProcess(launch_info);
+ else
+ error.SetErrorString("the platform is not currently connected");
+ }
+ return error;
+}
+
+lldb::ProcessSP PlatformFreeBSD::Attach(ProcessAttachInfo &attach_info,
+ Debugger &debugger, Target *target,
+ Status &error) {
+ lldb::ProcessSP process_sp;
+ if (IsHost()) {
+ if (target == nullptr) {
+ TargetSP new_target_sp;
+ ArchSpec emptyArchSpec;
+
+ error = debugger.GetTargetList().CreateTarget(
+ debugger, "", emptyArchSpec, eLoadDependentsNo, m_remote_platform_sp,
+ new_target_sp);
+ target = new_target_sp.get();
+ } else
+ error.Clear();
+
+ if (target && error.Success()) {
+ debugger.GetTargetList().SetSelectedTarget(target);
+ // The freebsd always currently uses the GDB remote debugger plug-in so
+ // even when debugging locally we are debugging remotely! Just like the
+ // darwin plugin.
+ process_sp = target->CreateProcess(
+ attach_info.GetListenerForProcess(debugger), "gdb-remote", nullptr);
+
+ if (process_sp)
+ error = process_sp->Attach(attach_info);
+ }
+ } else {
+ if (m_remote_platform_sp)
+ process_sp =
+ m_remote_platform_sp->Attach(attach_info, debugger, target, error);
+ else
+ error.SetErrorString("the platform is not currently connected");
+ }
+ return process_sp;
+}
+
+// FreeBSD processes cannot yet be launched by spawning and attaching.
+bool PlatformFreeBSD::CanDebugProcess() {
+ return false;
+}
+
+void PlatformFreeBSD::CalculateTrapHandlerSymbolNames() {
+ m_trap_handlers.push_back(ConstString("_sigtramp"));
+}
+
+MmapArgList PlatformFreeBSD::GetMmapArgumentList(const ArchSpec &arch,
+ addr_t addr, addr_t length,
+ unsigned prot, unsigned flags,
+ addr_t fd, addr_t offset) {
+ uint64_t flags_platform = 0;
+
+ if (flags & eMmapFlagsPrivate)
+ flags_platform |= MAP_PRIVATE;
+ if (flags & eMmapFlagsAnon)
+ flags_platform |= MAP_ANON;
+
+ MmapArgList args({addr, length, prot, flags_platform, fd, offset});
+ if (arch.GetTriple().getArch() == llvm::Triple::x86)
+ args.push_back(0);
+ return args;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h b/contrib/llvm-project/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h
new file mode 100644
index 000000000000..e3a3aa7145f0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h
@@ -0,0 +1,71 @@
+//===-- PlatformFreeBSD.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 liblldb_PlatformFreeBSD_h_
+#define liblldb_PlatformFreeBSD_h_
+
+#include "Plugins/Platform/POSIX/PlatformPOSIX.h"
+
+namespace lldb_private {
+namespace platform_freebsd {
+
+class PlatformFreeBSD : public PlatformPOSIX {
+public:
+ PlatformFreeBSD(bool is_host);
+
+ ~PlatformFreeBSD() override;
+
+ static void Initialize();
+
+ static void Terminate();
+
+ // lldb_private::PluginInterface functions
+ static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch);
+
+ static ConstString GetPluginNameStatic(bool is_host);
+
+ static const char *GetPluginDescriptionStatic(bool is_host);
+
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ // lldb_private::Platform functions
+ const char *GetDescription() override {
+ return GetPluginDescriptionStatic(IsHost());
+ }
+
+ void GetStatus(Stream &strm) override;
+
+ bool GetSupportedArchitectureAtIndex(uint32_t idx, ArchSpec &arch) override;
+
+ bool CanDebugProcess() override;
+
+ size_t GetSoftwareBreakpointTrapOpcode(Target &target,
+ BreakpointSite *bp_site) override;
+
+ Status LaunchProcess(ProcessLaunchInfo &launch_info) override;
+
+ lldb::ProcessSP Attach(ProcessAttachInfo &attach_info, Debugger &debugger,
+ Target *target, Status &error) override;
+
+ void CalculateTrapHandlerSymbolNames() override;
+
+ MmapArgList GetMmapArgumentList(const ArchSpec &arch, lldb::addr_t addr,
+ lldb::addr_t length, unsigned prot,
+ unsigned flags, lldb::addr_t fd,
+ lldb::addr_t offset) override;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(PlatformFreeBSD);
+};
+
+} // namespace platform_freebsd
+} // namespace lldb_private
+
+#endif // liblldb_PlatformFreeBSD_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/NetBSD/PlatformNetBSD.cpp b/contrib/llvm-project/lldb/source/Plugins/Platform/NetBSD/PlatformNetBSD.cpp
new file mode 100644
index 000000000000..63245d18fc5c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Platform/NetBSD/PlatformNetBSD.cpp
@@ -0,0 +1,364 @@
+//===-- PlatformNetBSD.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 "PlatformNetBSD.h"
+#include "lldb/Host/Config.h"
+
+#include <stdio.h>
+#ifndef LLDB_DISABLE_POSIX
+#include <sys/utsname.h>
+#endif
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+
+// Define these constants from NetBSD mman.h for use when targeting remote
+// netbsd systems even when host has different values.
+#define MAP_PRIVATE 0x0002
+#define MAP_ANON 0x1000
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::platform_netbsd;
+
+static uint32_t g_initialize_count = 0;
+
+
+PlatformSP PlatformNetBSD::CreateInstance(bool force, const ArchSpec *arch) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
+ LLDB_LOG(log, "force = {0}, arch=({1}, {2})", force,
+ arch ? arch->GetArchitectureName() : "<null>",
+ arch ? arch->GetTriple().getTriple() : "<null>");
+
+ bool create = force;
+ if (!create && arch && arch->IsValid()) {
+ const llvm::Triple &triple = arch->GetTriple();
+ switch (triple.getOS()) {
+ case llvm::Triple::NetBSD:
+ create = true;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ LLDB_LOG(log, "create = {0}", create);
+ if (create) {
+ return PlatformSP(new PlatformNetBSD(false));
+ }
+ return PlatformSP();
+}
+
+ConstString PlatformNetBSD::GetPluginNameStatic(bool is_host) {
+ if (is_host) {
+ static ConstString g_host_name(Platform::GetHostPlatformName());
+ return g_host_name;
+ } else {
+ static ConstString g_remote_name("remote-netbsd");
+ return g_remote_name;
+ }
+}
+
+const char *PlatformNetBSD::GetPluginDescriptionStatic(bool is_host) {
+ if (is_host)
+ return "Local NetBSD user platform plug-in.";
+ else
+ return "Remote NetBSD user platform plug-in.";
+}
+
+ConstString PlatformNetBSD::GetPluginName() {
+ return GetPluginNameStatic(IsHost());
+}
+
+void PlatformNetBSD::Initialize() {
+ PlatformPOSIX::Initialize();
+
+ if (g_initialize_count++ == 0) {
+#if defined(__NetBSD__)
+ PlatformSP default_platform_sp(new PlatformNetBSD(true));
+ default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture());
+ Platform::SetHostPlatform(default_platform_sp);
+#endif
+ PluginManager::RegisterPlugin(
+ PlatformNetBSD::GetPluginNameStatic(false),
+ PlatformNetBSD::GetPluginDescriptionStatic(false),
+ PlatformNetBSD::CreateInstance, nullptr);
+ }
+}
+
+void PlatformNetBSD::Terminate() {
+ if (g_initialize_count > 0) {
+ if (--g_initialize_count == 0) {
+ PluginManager::UnregisterPlugin(PlatformNetBSD::CreateInstance);
+ }
+ }
+
+ PlatformPOSIX::Terminate();
+}
+
+/// Default Constructor
+PlatformNetBSD::PlatformNetBSD(bool is_host)
+ : PlatformPOSIX(is_host) // This is the local host platform
+{}
+
+PlatformNetBSD::~PlatformNetBSD() = default;
+
+bool PlatformNetBSD::GetSupportedArchitectureAtIndex(uint32_t idx,
+ ArchSpec &arch) {
+ if (IsHost()) {
+ ArchSpec hostArch = HostInfo::GetArchitecture(HostInfo::eArchKindDefault);
+ if (hostArch.GetTriple().isOSNetBSD()) {
+ if (idx == 0) {
+ arch = hostArch;
+ return arch.IsValid();
+ } else if (idx == 1) {
+ // If the default host architecture is 64-bit, look for a 32-bit
+ // variant
+ if (hostArch.IsValid() && hostArch.GetTriple().isArch64Bit()) {
+ arch = HostInfo::GetArchitecture(HostInfo::eArchKind32);
+ return arch.IsValid();
+ }
+ }
+ }
+ } else {
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->GetSupportedArchitectureAtIndex(idx, arch);
+
+ llvm::Triple triple;
+ // Set the OS to NetBSD
+ triple.setOS(llvm::Triple::NetBSD);
+ // Set the architecture
+ switch (idx) {
+ case 0:
+ triple.setArchName("x86_64");
+ break;
+ case 1:
+ triple.setArchName("i386");
+ break;
+ default:
+ return false;
+ }
+ // Leave the vendor as "llvm::Triple:UnknownVendor" and don't specify the
+ // vendor by calling triple.SetVendorName("unknown") so that it is a
+ // "unspecified unknown". This means when someone calls
+ // triple.GetVendorName() it will return an empty string which indicates
+ // that the vendor can be set when two architectures are merged
+
+ // Now set the triple into "arch" and return true
+ arch.SetTriple(triple);
+ return true;
+ }
+ return false;
+}
+
+void PlatformNetBSD::GetStatus(Stream &strm) {
+ Platform::GetStatus(strm);
+
+#ifndef LLDB_DISABLE_POSIX
+ // Display local kernel information only when we are running in host mode.
+ // Otherwise, we would end up printing non-NetBSD information (when running
+ // on Mac OS for example).
+ if (IsHost()) {
+ struct utsname un;
+
+ if (uname(&un))
+ return;
+
+ strm.Printf(" Kernel: %s\n", un.sysname);
+ strm.Printf(" Release: %s\n", un.release);
+ strm.Printf(" Version: %s\n", un.version);
+ }
+#endif
+}
+
+int32_t
+PlatformNetBSD::GetResumeCountForLaunchInfo(ProcessLaunchInfo &launch_info) {
+ int32_t resume_count = 0;
+
+ // Always resume past the initial stop when we use eLaunchFlagDebug
+ if (launch_info.GetFlags().Test(eLaunchFlagDebug)) {
+ // Resume past the stop for the final exec into the true inferior.
+ ++resume_count;
+ }
+
+ // If we're not launching a shell, we're done.
+ const FileSpec &shell = launch_info.GetShell();
+ if (!shell)
+ return resume_count;
+
+ std::string shell_string = shell.GetPath();
+ // We're in a shell, so for sure we have to resume past the shell exec.
+ ++resume_count;
+
+ // Figure out what shell we're planning on using.
+ const char *shell_name = strrchr(shell_string.c_str(), '/');
+ if (shell_name == nullptr)
+ shell_name = shell_string.c_str();
+ else
+ shell_name++;
+
+ if (strcmp(shell_name, "csh") == 0 || strcmp(shell_name, "tcsh") == 0 ||
+ strcmp(shell_name, "zsh") == 0 || strcmp(shell_name, "sh") == 0) {
+ // These shells seem to re-exec themselves. Add another resume.
+ ++resume_count;
+ }
+
+ return resume_count;
+}
+
+bool PlatformNetBSD::CanDebugProcess() {
+ if (IsHost()) {
+ return true;
+ } else {
+ // If we're connected, we can debug.
+ return IsConnected();
+ }
+}
+
+// For local debugging, NetBSD will override the debug logic to use llgs-launch
+// rather than lldb-launch, llgs-attach. This differs from current lldb-
+// launch, debugserver-attach approach on MacOSX.
+lldb::ProcessSP
+PlatformNetBSD::DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger,
+ Target *target, // Can be NULL, if NULL create a new
+ // target, else use existing one
+ Status &error) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
+ LLDB_LOG(log, "target {0}", target);
+
+ // If we're a remote host, use standard behavior from parent class.
+ if (!IsHost())
+ return PlatformPOSIX::DebugProcess(launch_info, debugger, target, error);
+
+ //
+ // For local debugging, we'll insist on having ProcessGDBRemote create the
+ // process.
+ //
+
+ ProcessSP process_sp;
+
+ // Make sure we stop at the entry point
+ launch_info.GetFlags().Set(eLaunchFlagDebug);
+
+ // We always launch the process we are going to debug in a separate process
+ // group, since then we can handle ^C interrupts ourselves w/o having to
+ // worry about the target getting them as well.
+ launch_info.SetLaunchInSeparateProcessGroup(true);
+
+ // Ensure we have a target.
+ if (target == nullptr) {
+ LLDB_LOG(log, "creating new target");
+ TargetSP new_target_sp;
+ error = debugger.GetTargetList().CreateTarget(
+ debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
+ if (error.Fail()) {
+ LLDB_LOG(log, "failed to create new target: {0}", error);
+ return process_sp;
+ }
+
+ target = new_target_sp.get();
+ if (!target) {
+ error.SetErrorString("CreateTarget() returned nullptr");
+ LLDB_LOG(log, "error: {0}", error);
+ return process_sp;
+ }
+ }
+
+ // Mark target as currently selected target.
+ debugger.GetTargetList().SetSelectedTarget(target);
+
+ // Now create the gdb-remote process.
+ LLDB_LOG(log, "having target create process with gdb-remote plugin");
+ process_sp =
+ target->CreateProcess(launch_info.GetListener(), "gdb-remote", nullptr);
+
+ if (!process_sp) {
+ error.SetErrorString("CreateProcess() failed for gdb-remote process");
+ LLDB_LOG(log, "error: {0}", error);
+ return process_sp;
+ }
+
+ LLDB_LOG(log, "successfully created process");
+ // Adjust launch for a hijacker.
+ ListenerSP listener_sp;
+ if (!launch_info.GetHijackListener()) {
+ LLDB_LOG(log, "setting up hijacker");
+ listener_sp =
+ Listener::MakeListener("lldb.PlatformNetBSD.DebugProcess.hijack");
+ launch_info.SetHijackListener(listener_sp);
+ process_sp->HijackProcessEvents(listener_sp);
+ }
+
+ // Log file actions.
+ if (log) {
+ LLDB_LOG(log, "launching process with the following file actions:");
+ StreamString stream;
+ size_t i = 0;
+ const FileAction *file_action;
+ while ((file_action = launch_info.GetFileActionAtIndex(i++)) != nullptr) {
+ file_action->Dump(stream);
+ LLDB_LOG(log, "{0}", stream.GetData());
+ stream.Clear();
+ }
+ }
+
+ // Do the launch.
+ error = process_sp->Launch(launch_info);
+ if (error.Success()) {
+ // Handle the hijacking of process events.
+ if (listener_sp) {
+ const StateType state = process_sp->WaitForProcessToStop(
+ llvm::None, nullptr, false, listener_sp);
+
+ LLDB_LOG(log, "pid {0} state {0}", process_sp->GetID(), state);
+ }
+
+ // Hook up process PTY if we have one (which we should for local debugging
+ // with llgs).
+ int pty_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor();
+ if (pty_fd != PseudoTerminal::invalid_fd) {
+ process_sp->SetSTDIOFileDescriptor(pty_fd);
+ LLDB_LOG(log, "hooked up STDIO pty to process");
+ } else
+ LLDB_LOG(log, "not using process STDIO pty");
+ } else {
+ LLDB_LOG(log, "process launch failed: {0}", error);
+ // FIXME figure out appropriate cleanup here. Do we delete the target? Do
+ // we delete the process? Does our caller do that?
+ }
+
+ return process_sp;
+}
+
+void PlatformNetBSD::CalculateTrapHandlerSymbolNames() {
+ m_trap_handlers.push_back(ConstString("_sigtramp"));
+}
+
+MmapArgList PlatformNetBSD::GetMmapArgumentList(const ArchSpec &arch,
+ addr_t addr, addr_t length,
+ unsigned prot, unsigned flags,
+ addr_t fd, addr_t offset) {
+ uint64_t flags_platform = 0;
+
+ if (flags & eMmapFlagsPrivate)
+ flags_platform |= MAP_PRIVATE;
+ if (flags & eMmapFlagsAnon)
+ flags_platform |= MAP_ANON;
+
+ MmapArgList args({addr, length, prot, flags_platform, fd, offset});
+ return args;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/NetBSD/PlatformNetBSD.h b/contrib/llvm-project/lldb/source/Plugins/Platform/NetBSD/PlatformNetBSD.h
new file mode 100644
index 000000000000..0584d92d6c99
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Platform/NetBSD/PlatformNetBSD.h
@@ -0,0 +1,69 @@
+//===-- PlatformNetBSD.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 liblldb_PlatformNetBSD_h_
+#define liblldb_PlatformNetBSD_h_
+
+#include "Plugins/Platform/POSIX/PlatformPOSIX.h"
+
+namespace lldb_private {
+namespace platform_netbsd {
+
+class PlatformNetBSD : public PlatformPOSIX {
+public:
+ PlatformNetBSD(bool is_host);
+
+ ~PlatformNetBSD() override;
+
+ static void Initialize();
+
+ static void Terminate();
+
+ // lldb_private::PluginInterface functions
+ static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch);
+
+ static ConstString GetPluginNameStatic(bool is_host);
+
+ static const char *GetPluginDescriptionStatic(bool is_host);
+
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ // lldb_private::Platform functions
+ const char *GetDescription() override {
+ return GetPluginDescriptionStatic(IsHost());
+ }
+
+ void GetStatus(Stream &strm) override;
+
+ bool GetSupportedArchitectureAtIndex(uint32_t idx, ArchSpec &arch) override;
+
+ int32_t GetResumeCountForLaunchInfo(ProcessLaunchInfo &launch_info) override;
+
+ bool CanDebugProcess() override;
+
+ lldb::ProcessSP DebugProcess(ProcessLaunchInfo &launch_info,
+ Debugger &debugger, Target *target,
+ Status &error) override;
+
+ void CalculateTrapHandlerSymbolNames() override;
+
+ MmapArgList GetMmapArgumentList(const ArchSpec &arch, lldb::addr_t addr,
+ lldb::addr_t length, unsigned prot,
+ unsigned flags, lldb::addr_t fd,
+ lldb::addr_t offset) override;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(PlatformNetBSD);
+};
+
+} // namespace platform_netbsd
+} // namespace lldb_private
+
+#endif // liblldb_PlatformNetBSD_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.cpp b/contrib/llvm-project/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.cpp
new file mode 100644
index 000000000000..9dfb8844c574
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.cpp
@@ -0,0 +1,215 @@
+//===-- PlatformOpenBSD.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 "PlatformOpenBSD.h"
+#include "lldb/Host/Config.h"
+
+#include <stdio.h>
+#ifndef LLDB_DISABLE_POSIX
+#include <sys/utsname.h>
+#endif
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+
+// Define these constants from OpenBSD mman.h for use when targeting remote
+// openbsd systems even when host has different values.
+#define MAP_PRIVATE 0x0002
+#define MAP_ANON 0x1000
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::platform_openbsd;
+
+static uint32_t g_initialize_count = 0;
+
+
+PlatformSP PlatformOpenBSD::CreateInstance(bool force, const ArchSpec *arch) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
+ LLDB_LOG(log, "force = {0}, arch=({1}, {2})", force,
+ arch ? arch->GetArchitectureName() : "<null>",
+ arch ? arch->GetTriple().getTriple() : "<null>");
+
+ bool create = force;
+ if (!create && arch && arch->IsValid()) {
+ const llvm::Triple &triple = arch->GetTriple();
+ switch (triple.getOS()) {
+ case llvm::Triple::OpenBSD:
+ create = true;
+ break;
+
+#if defined(__OpenBSD__)
+ // Only accept "unknown" for the OS if the host is BSD and it "unknown"
+ // wasn't specified (it was just returned because it was NOT specified)
+ case llvm::Triple::OSType::UnknownOS:
+ create = !arch->TripleOSWasSpecified();
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+ LLDB_LOG(log, "create = {0}", create);
+ if (create) {
+ return PlatformSP(new PlatformOpenBSD(false));
+ }
+ return PlatformSP();
+}
+
+ConstString PlatformOpenBSD::GetPluginNameStatic(bool is_host) {
+ if (is_host) {
+ static ConstString g_host_name(Platform::GetHostPlatformName());
+ return g_host_name;
+ } else {
+ static ConstString g_remote_name("remote-openbsd");
+ return g_remote_name;
+ }
+}
+
+const char *PlatformOpenBSD::GetPluginDescriptionStatic(bool is_host) {
+ if (is_host)
+ return "Local OpenBSD user platform plug-in.";
+ else
+ return "Remote OpenBSD user platform plug-in.";
+}
+
+ConstString PlatformOpenBSD::GetPluginName() {
+ return GetPluginNameStatic(IsHost());
+}
+
+void PlatformOpenBSD::Initialize() {
+ Platform::Initialize();
+
+ if (g_initialize_count++ == 0) {
+#if defined(__OpenBSD__)
+ PlatformSP default_platform_sp(new PlatformOpenBSD(true));
+ default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture());
+ Platform::SetHostPlatform(default_platform_sp);
+#endif
+ PluginManager::RegisterPlugin(
+ PlatformOpenBSD::GetPluginNameStatic(false),
+ PlatformOpenBSD::GetPluginDescriptionStatic(false),
+ PlatformOpenBSD::CreateInstance, nullptr);
+ }
+}
+
+void PlatformOpenBSD::Terminate() {
+ if (g_initialize_count > 0) {
+ if (--g_initialize_count == 0) {
+ PluginManager::UnregisterPlugin(PlatformOpenBSD::CreateInstance);
+ }
+ }
+
+ PlatformPOSIX::Terminate();
+}
+
+/// Default Constructor
+PlatformOpenBSD::PlatformOpenBSD(bool is_host)
+ : PlatformPOSIX(is_host) // This is the local host platform
+{}
+
+PlatformOpenBSD::~PlatformOpenBSD() = default;
+
+bool PlatformOpenBSD::GetSupportedArchitectureAtIndex(uint32_t idx,
+ ArchSpec &arch) {
+ if (IsHost()) {
+ ArchSpec hostArch = HostInfo::GetArchitecture(HostInfo::eArchKindDefault);
+ if (hostArch.GetTriple().isOSOpenBSD()) {
+ if (idx == 0) {
+ arch = hostArch;
+ return arch.IsValid();
+ }
+ }
+ } else {
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->GetSupportedArchitectureAtIndex(idx, arch);
+
+ llvm::Triple triple;
+ // Set the OS to OpenBSD
+ triple.setOS(llvm::Triple::OpenBSD);
+ // Set the architecture
+ switch (idx) {
+ case 0:
+ triple.setArchName("x86_64");
+ break;
+ case 1:
+ triple.setArchName("i386");
+ break;
+ case 2:
+ triple.setArchName("aarch64");
+ break;
+ case 3:
+ triple.setArchName("arm");
+ break;
+ default:
+ return false;
+ }
+ // Leave the vendor as "llvm::Triple:UnknownVendor" and don't specify the
+ // vendor by calling triple.SetVendorName("unknown") so that it is a
+ // "unspecified unknown". This means when someone calls
+ // triple.GetVendorName() it will return an empty string which indicates
+ // that the vendor can be set when two architectures are merged
+
+ // Now set the triple into "arch" and return true
+ arch.SetTriple(triple);
+ return true;
+ }
+ return false;
+}
+
+void PlatformOpenBSD::GetStatus(Stream &strm) {
+ Platform::GetStatus(strm);
+
+#ifndef LLDB_DISABLE_POSIX
+ // Display local kernel information only when we are running in host mode.
+ // Otherwise, we would end up printing non-OpenBSD information (when running
+ // on Mac OS for example).
+ if (IsHost()) {
+ struct utsname un;
+
+ if (uname(&un))
+ return;
+
+ strm.Printf(" Kernel: %s\n", un.sysname);
+ strm.Printf(" Release: %s\n", un.release);
+ strm.Printf(" Version: %s\n", un.version);
+ }
+#endif
+}
+
+// OpenBSD processes cannot yet be launched by spawning and attaching.
+bool PlatformOpenBSD::CanDebugProcess() {
+ return false;
+}
+
+void PlatformOpenBSD::CalculateTrapHandlerSymbolNames() {
+ m_trap_handlers.push_back(ConstString("_sigtramp"));
+}
+
+MmapArgList PlatformOpenBSD::GetMmapArgumentList(const ArchSpec &arch,
+ addr_t addr, addr_t length,
+ unsigned prot, unsigned flags,
+ addr_t fd, addr_t offset) {
+ uint64_t flags_platform = 0;
+
+ if (flags & eMmapFlagsPrivate)
+ flags_platform |= MAP_PRIVATE;
+ if (flags & eMmapFlagsAnon)
+ flags_platform |= MAP_ANON;
+
+ MmapArgList args({addr, length, prot, flags_platform, fd, offset});
+ return args;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.h b/contrib/llvm-project/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.h
new file mode 100644
index 000000000000..3cb724f30325
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.h
@@ -0,0 +1,63 @@
+//===-- PlatformOpenBSD.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 liblldb_PlatformOpenBSD_h_
+#define liblldb_PlatformOpenBSD_h_
+
+#include "Plugins/Platform/POSIX/PlatformPOSIX.h"
+
+namespace lldb_private {
+namespace platform_openbsd {
+
+class PlatformOpenBSD : public PlatformPOSIX {
+public:
+ PlatformOpenBSD(bool is_host);
+
+ ~PlatformOpenBSD() override;
+
+ static void Initialize();
+
+ static void Terminate();
+
+ // lldb_private::PluginInterface functions
+ static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch);
+
+ static ConstString GetPluginNameStatic(bool is_host);
+
+ static const char *GetPluginDescriptionStatic(bool is_host);
+
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ // lldb_private::Platform functions
+ const char *GetDescription() override {
+ return GetPluginDescriptionStatic(IsHost());
+ }
+
+ void GetStatus(Stream &strm) override;
+
+ bool GetSupportedArchitectureAtIndex(uint32_t idx, ArchSpec &arch) override;
+
+ bool CanDebugProcess() override;
+
+ void CalculateTrapHandlerSymbolNames() override;
+
+ MmapArgList GetMmapArgumentList(const ArchSpec &arch, lldb::addr_t addr,
+ lldb::addr_t length, unsigned prot,
+ unsigned flags, lldb::addr_t fd,
+ lldb::addr_t offset) override;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(PlatformOpenBSD);
+};
+
+} // namespace platform_openbsd
+} // namespace lldb_private
+
+#endif // liblldb_PlatformOpenBSD_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp b/contrib/llvm-project/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
new file mode 100644
index 000000000000..d10557596ff8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
@@ -0,0 +1,1062 @@
+//===-- PlatformPOSIX.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 "PlatformPOSIX.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/FunctionCaller.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Expression/UtilityFunction.h"
+#include "lldb/Host/File.h"
+#include "lldb/Host/FileCache.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/ProcessLaunchInfo.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/CleanUp.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+/// Default Constructor
+PlatformPOSIX::PlatformPOSIX(bool is_host)
+ : RemoteAwarePlatform(is_host), // This is the local host platform
+ m_option_group_platform_rsync(new OptionGroupPlatformRSync()),
+ m_option_group_platform_ssh(new OptionGroupPlatformSSH()),
+ m_option_group_platform_caching(new OptionGroupPlatformCaching()) {}
+
+/// Destructor.
+///
+/// The destructor is virtual since this class is designed to be
+/// inherited from by the plug-in instance.
+PlatformPOSIX::~PlatformPOSIX() {}
+
+lldb_private::OptionGroupOptions *PlatformPOSIX::GetConnectionOptions(
+ lldb_private::CommandInterpreter &interpreter) {
+ auto iter = m_options.find(&interpreter), end = m_options.end();
+ if (iter == end) {
+ std::unique_ptr<lldb_private::OptionGroupOptions> options(
+ new OptionGroupOptions());
+ options->Append(m_option_group_platform_rsync.get());
+ options->Append(m_option_group_platform_ssh.get());
+ options->Append(m_option_group_platform_caching.get());
+ m_options[&interpreter] = std::move(options);
+ }
+
+ return m_options.at(&interpreter).get();
+}
+
+Status
+PlatformPOSIX::ResolveExecutable(const ModuleSpec &module_spec,
+ lldb::ModuleSP &exe_module_sp,
+ const FileSpecList *module_search_paths_ptr) {
+ Status error;
+ // Nothing special to do here, just use the actual file and architecture
+
+ char exe_path[PATH_MAX];
+ ModuleSpec resolved_module_spec(module_spec);
+
+ if (IsHost()) {
+ // If we have "ls" as the exe_file, resolve the executable location based
+ // on the current path variables
+ if (!FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec())) {
+ resolved_module_spec.GetFileSpec().GetPath(exe_path, sizeof(exe_path));
+ resolved_module_spec.GetFileSpec().SetFile(exe_path,
+ FileSpec::Style::native);
+ FileSystem::Instance().Resolve(resolved_module_spec.GetFileSpec());
+ }
+
+ if (!FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec()))
+ FileSystem::Instance().ResolveExecutableLocation(
+ resolved_module_spec.GetFileSpec());
+
+ // Resolve any executable within a bundle on MacOSX
+ Host::ResolveExecutableInBundle(resolved_module_spec.GetFileSpec());
+
+ if (FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec()))
+ error.Clear();
+ else {
+ const uint32_t permissions = FileSystem::Instance().GetPermissions(
+ resolved_module_spec.GetFileSpec());
+ if (permissions && (permissions & eFilePermissionsEveryoneR) == 0)
+ error.SetErrorStringWithFormat(
+ "executable '%s' is not readable",
+ resolved_module_spec.GetFileSpec().GetPath().c_str());
+ else
+ error.SetErrorStringWithFormat(
+ "unable to find executable for '%s'",
+ resolved_module_spec.GetFileSpec().GetPath().c_str());
+ }
+ } else {
+ if (m_remote_platform_sp) {
+ error =
+ GetCachedExecutable(resolved_module_spec, exe_module_sp,
+ module_search_paths_ptr, *m_remote_platform_sp);
+ } else {
+ // We may connect to a process and use the provided executable (Don't use
+ // local $PATH).
+
+ // Resolve any executable within a bundle on MacOSX
+ Host::ResolveExecutableInBundle(resolved_module_spec.GetFileSpec());
+
+ if (FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec()))
+ error.Clear();
+ else
+ error.SetErrorStringWithFormat("the platform is not currently "
+ "connected, and '%s' doesn't exist in "
+ "the system root.",
+ exe_path);
+ }
+ }
+
+ if (error.Success()) {
+ if (resolved_module_spec.GetArchitecture().IsValid()) {
+ error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp,
+ module_search_paths_ptr, nullptr, nullptr);
+ if (error.Fail()) {
+ // If we failed, it may be because the vendor and os aren't known. If
+ // that is the case, try setting them to the host architecture and give
+ // it another try.
+ llvm::Triple &module_triple =
+ resolved_module_spec.GetArchitecture().GetTriple();
+ bool is_vendor_specified =
+ (module_triple.getVendor() != llvm::Triple::UnknownVendor);
+ bool is_os_specified =
+ (module_triple.getOS() != llvm::Triple::UnknownOS);
+ if (!is_vendor_specified || !is_os_specified) {
+ const llvm::Triple &host_triple =
+ HostInfo::GetArchitecture(HostInfo::eArchKindDefault).GetTriple();
+
+ if (!is_vendor_specified)
+ module_triple.setVendorName(host_triple.getVendorName());
+ if (!is_os_specified)
+ module_triple.setOSName(host_triple.getOSName());
+
+ error = ModuleList::GetSharedModule(resolved_module_spec,
+ exe_module_sp, module_search_paths_ptr, nullptr, nullptr);
+ }
+ }
+
+ // TODO find out why exe_module_sp might be NULL
+ if (error.Fail() || !exe_module_sp || !exe_module_sp->GetObjectFile()) {
+ exe_module_sp.reset();
+ error.SetErrorStringWithFormat(
+ "'%s' doesn't contain the architecture %s",
+ resolved_module_spec.GetFileSpec().GetPath().c_str(),
+ resolved_module_spec.GetArchitecture().GetArchitectureName());
+ }
+ } else {
+ // No valid architecture was specified, ask the platform for the
+ // architectures that we should be using (in the correct order) and see
+ // if we can find a match that way
+ StreamString arch_names;
+ for (uint32_t idx = 0; GetSupportedArchitectureAtIndex(
+ idx, resolved_module_spec.GetArchitecture());
+ ++idx) {
+ error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp,
+ module_search_paths_ptr, nullptr, nullptr);
+ // Did we find an executable using one of the
+ if (error.Success()) {
+ if (exe_module_sp && exe_module_sp->GetObjectFile())
+ break;
+ else
+ error.SetErrorToGenericError();
+ }
+
+ if (idx > 0)
+ arch_names.PutCString(", ");
+ arch_names.PutCString(
+ resolved_module_spec.GetArchitecture().GetArchitectureName());
+ }
+
+ if (error.Fail() || !exe_module_sp) {
+ if (FileSystem::Instance().Readable(
+ resolved_module_spec.GetFileSpec())) {
+ error.SetErrorStringWithFormat(
+ "'%s' doesn't contain any '%s' platform architectures: %s",
+ resolved_module_spec.GetFileSpec().GetPath().c_str(),
+ GetPluginName().GetCString(), arch_names.GetData());
+ } else {
+ error.SetErrorStringWithFormat(
+ "'%s' is not readable",
+ resolved_module_spec.GetFileSpec().GetPath().c_str());
+ }
+ }
+ }
+ }
+
+ return error;
+}
+
+static uint32_t chown_file(Platform *platform, const char *path,
+ uint32_t uid = UINT32_MAX,
+ uint32_t gid = UINT32_MAX) {
+ if (!platform || !path || *path == 0)
+ return UINT32_MAX;
+
+ if (uid == UINT32_MAX && gid == UINT32_MAX)
+ return 0; // pretend I did chown correctly - actually I just didn't care
+
+ StreamString command;
+ command.PutCString("chown ");
+ if (uid != UINT32_MAX)
+ command.Printf("%d", uid);
+ if (gid != UINT32_MAX)
+ command.Printf(":%d", gid);
+ command.Printf("%s", path);
+ int status;
+ platform->RunShellCommand(command.GetData(), nullptr, &status, nullptr,
+ nullptr, std::chrono::seconds(10));
+ return status;
+}
+
+lldb_private::Status
+PlatformPOSIX::PutFile(const lldb_private::FileSpec &source,
+ const lldb_private::FileSpec &destination, uint32_t uid,
+ uint32_t gid) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
+
+ if (IsHost()) {
+ if (FileSpec::Equal(source, destination, true))
+ return Status();
+ // cp src dst
+ // chown uid:gid dst
+ std::string src_path(source.GetPath());
+ if (src_path.empty())
+ return Status("unable to get file path for source");
+ std::string dst_path(destination.GetPath());
+ if (dst_path.empty())
+ return Status("unable to get file path for destination");
+ StreamString command;
+ command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str());
+ int status;
+ RunShellCommand(command.GetData(), nullptr, &status, nullptr, nullptr,
+ std::chrono::seconds(10));
+ if (status != 0)
+ return Status("unable to perform copy");
+ if (uid == UINT32_MAX && gid == UINT32_MAX)
+ return Status();
+ if (chown_file(this, dst_path.c_str(), uid, gid) != 0)
+ return Status("unable to perform chown");
+ return Status();
+ } else if (m_remote_platform_sp) {
+ if (GetSupportsRSync()) {
+ std::string src_path(source.GetPath());
+ if (src_path.empty())
+ return Status("unable to get file path for source");
+ std::string dst_path(destination.GetPath());
+ if (dst_path.empty())
+ return Status("unable to get file path for destination");
+ StreamString command;
+ if (GetIgnoresRemoteHostname()) {
+ if (!GetRSyncPrefix())
+ command.Printf("rsync %s %s %s", GetRSyncOpts(), src_path.c_str(),
+ dst_path.c_str());
+ else
+ command.Printf("rsync %s %s %s%s", GetRSyncOpts(), src_path.c_str(),
+ GetRSyncPrefix(), dst_path.c_str());
+ } else
+ command.Printf("rsync %s %s %s:%s", GetRSyncOpts(), src_path.c_str(),
+ GetHostname(), dst_path.c_str());
+ if (log)
+ log->Printf("[PutFile] Running command: %s\n", command.GetData());
+ int retcode;
+ Host::RunShellCommand(command.GetData(), nullptr, &retcode, nullptr,
+ nullptr, std::chrono::minutes(1));
+ if (retcode == 0) {
+ // Don't chown a local file for a remote system
+ // if (chown_file(this,dst_path.c_str(),uid,gid) != 0)
+ // return Status("unable to perform chown");
+ return Status();
+ }
+ // if we are still here rsync has failed - let's try the slow way before
+ // giving up
+ }
+ }
+ return Platform::PutFile(source, destination, uid, gid);
+}
+
+lldb_private::Status PlatformPOSIX::GetFile(
+ const lldb_private::FileSpec &source, // remote file path
+ const lldb_private::FileSpec &destination) // local file path
+{
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
+
+ // Check the args, first.
+ std::string src_path(source.GetPath());
+ if (src_path.empty())
+ return Status("unable to get file path for source");
+ std::string dst_path(destination.GetPath());
+ if (dst_path.empty())
+ return Status("unable to get file path for destination");
+ if (IsHost()) {
+ if (FileSpec::Equal(source, destination, true))
+ return Status("local scenario->source and destination are the same file "
+ "path: no operation performed");
+ // cp src dst
+ StreamString cp_command;
+ cp_command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str());
+ int status;
+ RunShellCommand(cp_command.GetData(), nullptr, &status, nullptr, nullptr,
+ std::chrono::seconds(10));
+ if (status != 0)
+ return Status("unable to perform copy");
+ return Status();
+ } else if (m_remote_platform_sp) {
+ if (GetSupportsRSync()) {
+ StreamString command;
+ if (GetIgnoresRemoteHostname()) {
+ if (!GetRSyncPrefix())
+ command.Printf("rsync %s %s %s", GetRSyncOpts(), src_path.c_str(),
+ dst_path.c_str());
+ else
+ command.Printf("rsync %s %s%s %s", GetRSyncOpts(), GetRSyncPrefix(),
+ src_path.c_str(), dst_path.c_str());
+ } else
+ command.Printf("rsync %s %s:%s %s", GetRSyncOpts(),
+ m_remote_platform_sp->GetHostname(), src_path.c_str(),
+ dst_path.c_str());
+ if (log)
+ log->Printf("[GetFile] Running command: %s\n", command.GetData());
+ int retcode;
+ Host::RunShellCommand(command.GetData(), nullptr, &retcode, nullptr,
+ nullptr, std::chrono::minutes(1));
+ if (retcode == 0)
+ return Status();
+ // If we are here, rsync has failed - let's try the slow way before
+ // giving up
+ }
+ // open src and dst
+ // read/write, read/write, read/write, ...
+ // close src
+ // close dst
+ if (log)
+ log->Printf("[GetFile] Using block by block transfer....\n");
+ Status error;
+ user_id_t fd_src = OpenFile(source, File::eOpenOptionRead,
+ lldb::eFilePermissionsFileDefault, error);
+
+ if (fd_src == UINT64_MAX)
+ return Status("unable to open source file");
+
+ uint32_t permissions = 0;
+ error = GetFilePermissions(source, permissions);
+
+ if (permissions == 0)
+ permissions = lldb::eFilePermissionsFileDefault;
+
+ user_id_t fd_dst = FileCache::GetInstance().OpenFile(
+ destination, File::eOpenOptionCanCreate | File::eOpenOptionWrite |
+ File::eOpenOptionTruncate,
+ permissions, error);
+
+ if (fd_dst == UINT64_MAX) {
+ if (error.Success())
+ error.SetErrorString("unable to open destination file");
+ }
+
+ if (error.Success()) {
+ lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0));
+ uint64_t offset = 0;
+ error.Clear();
+ while (error.Success()) {
+ const uint64_t n_read = ReadFile(fd_src, offset, buffer_sp->GetBytes(),
+ buffer_sp->GetByteSize(), error);
+ if (error.Fail())
+ break;
+ if (n_read == 0)
+ break;
+ if (FileCache::GetInstance().WriteFile(fd_dst, offset,
+ buffer_sp->GetBytes(), n_read,
+ error) != n_read) {
+ if (!error.Fail())
+ error.SetErrorString("unable to write to destination file");
+ break;
+ }
+ offset += n_read;
+ }
+ }
+ // Ignore the close error of src.
+ if (fd_src != UINT64_MAX)
+ CloseFile(fd_src, error);
+ // And close the dst file descriptot.
+ if (fd_dst != UINT64_MAX &&
+ !FileCache::GetInstance().CloseFile(fd_dst, error)) {
+ if (!error.Fail())
+ error.SetErrorString("unable to close destination file");
+ }
+ return error;
+ }
+ return Platform::GetFile(source, destination);
+}
+
+std::string PlatformPOSIX::GetPlatformSpecificConnectionInformation() {
+ StreamString stream;
+ if (GetSupportsRSync()) {
+ stream.PutCString("rsync");
+ if ((GetRSyncOpts() && *GetRSyncOpts()) ||
+ (GetRSyncPrefix() && *GetRSyncPrefix()) || GetIgnoresRemoteHostname()) {
+ stream.Printf(", options: ");
+ if (GetRSyncOpts() && *GetRSyncOpts())
+ stream.Printf("'%s' ", GetRSyncOpts());
+ stream.Printf(", prefix: ");
+ if (GetRSyncPrefix() && *GetRSyncPrefix())
+ stream.Printf("'%s' ", GetRSyncPrefix());
+ if (GetIgnoresRemoteHostname())
+ stream.Printf("ignore remote-hostname ");
+ }
+ }
+ if (GetSupportsSSH()) {
+ stream.PutCString("ssh");
+ if (GetSSHOpts() && *GetSSHOpts())
+ stream.Printf(", options: '%s' ", GetSSHOpts());
+ }
+ if (GetLocalCacheDirectory() && *GetLocalCacheDirectory())
+ stream.Printf("cache dir: %s", GetLocalCacheDirectory());
+ if (stream.GetSize())
+ return stream.GetString();
+ else
+ return "";
+}
+
+const lldb::UnixSignalsSP &PlatformPOSIX::GetRemoteUnixSignals() {
+ if (IsRemote() && m_remote_platform_sp)
+ return m_remote_platform_sp->GetRemoteUnixSignals();
+ return Platform::GetRemoteUnixSignals();
+}
+
+Status PlatformPOSIX::ConnectRemote(Args &args) {
+ Status error;
+ if (IsHost()) {
+ error.SetErrorStringWithFormat(
+ "can't connect to the host platform '%s', always connected",
+ GetPluginName().GetCString());
+ } else {
+ if (!m_remote_platform_sp)
+ m_remote_platform_sp =
+ Platform::Create(ConstString("remote-gdb-server"), error);
+
+ if (m_remote_platform_sp && error.Success())
+ error = m_remote_platform_sp->ConnectRemote(args);
+ else
+ error.SetErrorString("failed to create a 'remote-gdb-server' platform");
+
+ if (error.Fail())
+ m_remote_platform_sp.reset();
+ }
+
+ if (error.Success() && m_remote_platform_sp) {
+ if (m_option_group_platform_rsync.get() &&
+ m_option_group_platform_ssh.get() &&
+ m_option_group_platform_caching.get()) {
+ if (m_option_group_platform_rsync->m_rsync) {
+ SetSupportsRSync(true);
+ SetRSyncOpts(m_option_group_platform_rsync->m_rsync_opts.c_str());
+ SetRSyncPrefix(m_option_group_platform_rsync->m_rsync_prefix.c_str());
+ SetIgnoresRemoteHostname(
+ m_option_group_platform_rsync->m_ignores_remote_hostname);
+ }
+ if (m_option_group_platform_ssh->m_ssh) {
+ SetSupportsSSH(true);
+ SetSSHOpts(m_option_group_platform_ssh->m_ssh_opts.c_str());
+ }
+ SetLocalCacheDirectory(
+ m_option_group_platform_caching->m_cache_dir.c_str());
+ }
+ }
+
+ return error;
+}
+
+Status PlatformPOSIX::DisconnectRemote() {
+ Status error;
+
+ if (IsHost()) {
+ error.SetErrorStringWithFormat(
+ "can't disconnect from the host platform '%s', always connected",
+ GetPluginName().GetCString());
+ } else {
+ if (m_remote_platform_sp)
+ error = m_remote_platform_sp->DisconnectRemote();
+ else
+ error.SetErrorString("the platform is not currently connected");
+ }
+ return error;
+}
+
+lldb::ProcessSP PlatformPOSIX::Attach(ProcessAttachInfo &attach_info,
+ Debugger &debugger, Target *target,
+ Status &error) {
+ lldb::ProcessSP process_sp;
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
+
+ if (IsHost()) {
+ if (target == nullptr) {
+ TargetSP new_target_sp;
+
+ error = debugger.GetTargetList().CreateTarget(
+ debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
+ target = new_target_sp.get();
+ if (log)
+ log->Printf("PlatformPOSIX::%s created new target", __FUNCTION__);
+ } else {
+ error.Clear();
+ if (log)
+ log->Printf("PlatformPOSIX::%s target already existed, setting target",
+ __FUNCTION__);
+ }
+
+ if (target && error.Success()) {
+ debugger.GetTargetList().SetSelectedTarget(target);
+ if (log) {
+ ModuleSP exe_module_sp = target->GetExecutableModule();
+ log->Printf("PlatformPOSIX::%s set selected target to %p %s",
+ __FUNCTION__, (void *)target,
+ exe_module_sp
+ ? exe_module_sp->GetFileSpec().GetPath().c_str()
+ : "<null>");
+ }
+
+ process_sp =
+ target->CreateProcess(attach_info.GetListenerForProcess(debugger),
+ attach_info.GetProcessPluginName(), nullptr);
+
+ if (process_sp) {
+ ListenerSP listener_sp = attach_info.GetHijackListener();
+ if (listener_sp == nullptr) {
+ listener_sp =
+ Listener::MakeListener("lldb.PlatformPOSIX.attach.hijack");
+ attach_info.SetHijackListener(listener_sp);
+ }
+ process_sp->HijackProcessEvents(listener_sp);
+ error = process_sp->Attach(attach_info);
+ }
+ }
+ } else {
+ if (m_remote_platform_sp)
+ process_sp =
+ m_remote_platform_sp->Attach(attach_info, debugger, target, error);
+ else
+ error.SetErrorString("the platform is not currently connected");
+ }
+ return process_sp;
+}
+
+lldb::ProcessSP
+PlatformPOSIX::DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger,
+ Target *target, // Can be NULL, if NULL create a new
+ // target, else use existing one
+ Status &error) {
+ ProcessSP process_sp;
+
+ if (IsHost()) {
+ // We are going to hand this process off to debugserver which will be in
+ // charge of setting the exit status. However, we still need to reap it
+ // from lldb. So, make sure we use a exit callback which does not set exit
+ // status.
+ const bool monitor_signals = false;
+ launch_info.SetMonitorProcessCallback(
+ &ProcessLaunchInfo::NoOpMonitorCallback, monitor_signals);
+ process_sp = Platform::DebugProcess(launch_info, debugger, target, error);
+ } else {
+ if (m_remote_platform_sp)
+ process_sp = m_remote_platform_sp->DebugProcess(launch_info, debugger,
+ target, error);
+ else
+ error.SetErrorString("the platform is not currently connected");
+ }
+ return process_sp;
+}
+
+void PlatformPOSIX::CalculateTrapHandlerSymbolNames() {
+ m_trap_handlers.push_back(ConstString("_sigtramp"));
+}
+
+Status PlatformPOSIX::EvaluateLibdlExpression(
+ lldb_private::Process *process, const char *expr_cstr,
+ llvm::StringRef expr_prefix, lldb::ValueObjectSP &result_valobj_sp) {
+ DynamicLoader *loader = process->GetDynamicLoader();
+ if (loader) {
+ Status error = loader->CanLoadImage();
+ if (error.Fail())
+ return error;
+ }
+
+ ThreadSP thread_sp(process->GetThreadList().GetExpressionExecutionThread());
+ if (!thread_sp)
+ return Status("Selected thread isn't valid");
+
+ StackFrameSP frame_sp(thread_sp->GetStackFrameAtIndex(0));
+ if (!frame_sp)
+ return Status("Frame 0 isn't valid");
+
+ ExecutionContext exe_ctx;
+ frame_sp->CalculateExecutionContext(exe_ctx);
+ EvaluateExpressionOptions expr_options;
+ expr_options.SetUnwindOnError(true);
+ expr_options.SetIgnoreBreakpoints(true);
+ expr_options.SetExecutionPolicy(eExecutionPolicyAlways);
+ expr_options.SetLanguage(eLanguageTypeC_plus_plus);
+ expr_options.SetTrapExceptions(false); // dlopen can't throw exceptions, so
+ // don't do the work to trap them.
+ expr_options.SetTimeout(process->GetUtilityExpressionTimeout());
+
+ Status expr_error;
+ ExpressionResults result =
+ UserExpression::Evaluate(exe_ctx, expr_options, expr_cstr, expr_prefix,
+ result_valobj_sp, expr_error);
+ if (result != eExpressionCompleted)
+ return expr_error;
+
+ if (result_valobj_sp->GetError().Fail())
+ return result_valobj_sp->GetError();
+ return Status();
+}
+
+std::unique_ptr<UtilityFunction>
+PlatformPOSIX::MakeLoadImageUtilityFunction(ExecutionContext &exe_ctx,
+ Status &error) {
+ // Remember to prepend this with the prefix from
+ // GetLibdlFunctionDeclarations. The returned values are all in
+ // __lldb_dlopen_result for consistency. The wrapper returns a void * but
+ // doesn't use it because UtilityFunctions don't work with void returns at
+ // present.
+ static const char *dlopen_wrapper_code = R"(
+ struct __lldb_dlopen_result {
+ void *image_ptr;
+ const char *error_str;
+ };
+
+ extern void *memcpy(void *, const void *, size_t size);
+ extern size_t strlen(const char *);
+
+
+ void * __lldb_dlopen_wrapper (const char *name,
+ const char *path_strings,
+ char *buffer,
+ __lldb_dlopen_result *result_ptr)
+ {
+ // This is the case where the name is the full path:
+ if (!path_strings) {
+ result_ptr->image_ptr = dlopen(name, 2);
+ if (result_ptr->image_ptr)
+ result_ptr->error_str = nullptr;
+ return nullptr;
+ }
+
+ // This is the case where we have a list of paths:
+ size_t name_len = strlen(name);
+ while (path_strings && path_strings[0] != '\0') {
+ size_t path_len = strlen(path_strings);
+ memcpy((void *) buffer, (void *) path_strings, path_len);
+ buffer[path_len] = '/';
+ char *target_ptr = buffer+path_len+1;
+ memcpy((void *) target_ptr, (void *) name, name_len + 1);
+ result_ptr->image_ptr = dlopen(buffer, 2);
+ if (result_ptr->image_ptr) {
+ result_ptr->error_str = nullptr;
+ break;
+ }
+ result_ptr->error_str = dlerror();
+ path_strings = path_strings + path_len + 1;
+ }
+ return nullptr;
+ }
+ )";
+
+ static const char *dlopen_wrapper_name = "__lldb_dlopen_wrapper";
+ Process *process = exe_ctx.GetProcessSP().get();
+ // Insert the dlopen shim defines into our generic expression:
+ std::string expr(GetLibdlFunctionDeclarations(process));
+ expr.append(dlopen_wrapper_code);
+ Status utility_error;
+ DiagnosticManager diagnostics;
+
+ std::unique_ptr<UtilityFunction> dlopen_utility_func_up(process
+ ->GetTarget().GetUtilityFunctionForLanguage(expr.c_str(),
+ eLanguageTypeObjC,
+ dlopen_wrapper_name,
+ utility_error));
+ if (utility_error.Fail()) {
+ error.SetErrorStringWithFormat("dlopen error: could not make utility"
+ "function: %s", utility_error.AsCString());
+ return nullptr;
+ }
+ if (!dlopen_utility_func_up->Install(diagnostics, exe_ctx)) {
+ error.SetErrorStringWithFormat("dlopen error: could not install utility"
+ "function: %s",
+ diagnostics.GetString().c_str());
+ return nullptr;
+ }
+
+ Value value;
+ ValueList arguments;
+ FunctionCaller *do_dlopen_function = nullptr;
+
+ // Fetch the clang types we will need:
+ ClangASTContext *ast = process->GetTarget().GetScratchClangASTContext();
+
+ CompilerType clang_void_pointer_type
+ = ast->GetBasicType(eBasicTypeVoid).GetPointerType();
+ CompilerType clang_char_pointer_type
+ = ast->GetBasicType(eBasicTypeChar).GetPointerType();
+
+ // We are passing four arguments, the basename, the list of places to look,
+ // a buffer big enough for all the path + name combos, and
+ // a pointer to the storage we've made for the result:
+ value.SetValueType(Value::eValueTypeScalar);
+ value.SetCompilerType(clang_void_pointer_type);
+ arguments.PushValue(value);
+ value.SetCompilerType(clang_char_pointer_type);
+ arguments.PushValue(value);
+ arguments.PushValue(value);
+ arguments.PushValue(value);
+
+ do_dlopen_function = dlopen_utility_func_up->MakeFunctionCaller(
+ clang_void_pointer_type, arguments, exe_ctx.GetThreadSP(), utility_error);
+ if (utility_error.Fail()) {
+ error.SetErrorStringWithFormat("dlopen error: could not make function"
+ "caller: %s", utility_error.AsCString());
+ return nullptr;
+ }
+
+ do_dlopen_function = dlopen_utility_func_up->GetFunctionCaller();
+ if (!do_dlopen_function) {
+ error.SetErrorString("dlopen error: could not get function caller.");
+ return nullptr;
+ }
+
+ // We made a good utility function, so cache it in the process:
+ return dlopen_utility_func_up;
+}
+
+uint32_t PlatformPOSIX::DoLoadImage(lldb_private::Process *process,
+ const lldb_private::FileSpec &remote_file,
+ const std::vector<std::string> *paths,
+ lldb_private::Status &error,
+ lldb_private::FileSpec *loaded_image) {
+ if (loaded_image)
+ loaded_image->Clear();
+
+ std::string path;
+ path = remote_file.GetPath();
+
+ ThreadSP thread_sp = process->GetThreadList().GetExpressionExecutionThread();
+ if (!thread_sp) {
+ error.SetErrorString("dlopen error: no thread available to call dlopen.");
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+
+ DiagnosticManager diagnostics;
+
+ ExecutionContext exe_ctx;
+ thread_sp->CalculateExecutionContext(exe_ctx);
+
+ Status utility_error;
+ UtilityFunction *dlopen_utility_func;
+ ValueList arguments;
+ FunctionCaller *do_dlopen_function = nullptr;
+
+ // The UtilityFunction is held in the Process. Platforms don't track the
+ // lifespan of the Targets that use them, we can't put this in the Platform.
+ dlopen_utility_func = process->GetLoadImageUtilityFunction(
+ this, [&]() -> std::unique_ptr<UtilityFunction> {
+ return MakeLoadImageUtilityFunction(exe_ctx, error);
+ });
+ // If we couldn't make it, the error will be in error, so we can exit here.
+ if (!dlopen_utility_func)
+ return LLDB_INVALID_IMAGE_TOKEN;
+
+ do_dlopen_function = dlopen_utility_func->GetFunctionCaller();
+ if (!do_dlopen_function) {
+ error.SetErrorString("dlopen error: could not get function caller.");
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+ arguments = do_dlopen_function->GetArgumentValues();
+
+ // Now insert the path we are searching for and the result structure into the
+ // target.
+ uint32_t permissions = ePermissionsReadable|ePermissionsWritable;
+ size_t path_len = path.size() + 1;
+ lldb::addr_t path_addr = process->AllocateMemory(path_len,
+ permissions,
+ utility_error);
+ if (path_addr == LLDB_INVALID_ADDRESS) {
+ error.SetErrorStringWithFormat("dlopen error: could not allocate memory"
+ "for path: %s", utility_error.AsCString());
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+
+ // Make sure we deallocate the input string memory:
+ CleanUp path_cleanup([process, path_addr] {
+ process->DeallocateMemory(path_addr);
+ });
+
+ process->WriteMemory(path_addr, path.c_str(), path_len, utility_error);
+ if (utility_error.Fail()) {
+ error.SetErrorStringWithFormat("dlopen error: could not write path string:"
+ " %s", utility_error.AsCString());
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+
+ // Make space for our return structure. It is two pointers big: the token
+ // and the error string.
+ const uint32_t addr_size = process->GetAddressByteSize();
+ lldb::addr_t return_addr = process->CallocateMemory(2*addr_size,
+ permissions,
+ utility_error);
+ if (utility_error.Fail()) {
+ error.SetErrorStringWithFormat("dlopen error: could not allocate memory"
+ "for path: %s", utility_error.AsCString());
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+
+ // Make sure we deallocate the result structure memory
+ CleanUp return_cleanup([process, return_addr] {
+ process->DeallocateMemory(return_addr);
+ });
+
+ // This will be the address of the storage for paths, if we are using them,
+ // or nullptr to signal we aren't.
+ lldb::addr_t path_array_addr = 0x0;
+ llvm::Optional<CleanUp> path_array_cleanup;
+
+ // This is the address to a buffer large enough to hold the largest path
+ // conjoined with the library name we're passing in. This is a convenience
+ // to avoid having to call malloc in the dlopen function.
+ lldb::addr_t buffer_addr = 0x0;
+ llvm::Optional<CleanUp> buffer_cleanup;
+
+ // Set the values into our args and write them to the target:
+ if (paths != nullptr) {
+ // First insert the paths into the target. This is expected to be a
+ // continuous buffer with the strings laid out null terminated and
+ // end to end with an empty string terminating the buffer.
+ // We also compute the buffer's required size as we go.
+ size_t buffer_size = 0;
+ std::string path_array;
+ for (auto path : *paths) {
+ // Don't insert empty paths, they will make us abort the path
+ // search prematurely.
+ if (path.empty())
+ continue;
+ size_t path_size = path.size();
+ path_array.append(path);
+ path_array.push_back('\0');
+ if (path_size > buffer_size)
+ buffer_size = path_size;
+ }
+ path_array.push_back('\0');
+
+ path_array_addr = process->AllocateMemory(path_array.size(),
+ permissions,
+ utility_error);
+ if (path_array_addr == LLDB_INVALID_ADDRESS) {
+ error.SetErrorStringWithFormat("dlopen error: could not allocate memory"
+ "for path array: %s",
+ utility_error.AsCString());
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+
+ // Make sure we deallocate the paths array.
+ path_array_cleanup.emplace([process, path_array_addr] {
+ process->DeallocateMemory(path_array_addr);
+ });
+
+ process->WriteMemory(path_array_addr, path_array.data(),
+ path_array.size(), utility_error);
+
+ if (utility_error.Fail()) {
+ error.SetErrorStringWithFormat("dlopen error: could not write path array:"
+ " %s", utility_error.AsCString());
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+ // Now make spaces in the target for the buffer. We need to add one for
+ // the '/' that the utility function will insert and one for the '\0':
+ buffer_size += path.size() + 2;
+
+ buffer_addr = process->AllocateMemory(buffer_size,
+ permissions,
+ utility_error);
+ if (buffer_addr == LLDB_INVALID_ADDRESS) {
+ error.SetErrorStringWithFormat("dlopen error: could not allocate memory"
+ "for buffer: %s",
+ utility_error.AsCString());
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+
+ // Make sure we deallocate the buffer memory:
+ buffer_cleanup.emplace([process, buffer_addr] {
+ process->DeallocateMemory(buffer_addr);
+ });
+ }
+
+ arguments.GetValueAtIndex(0)->GetScalar() = path_addr;
+ arguments.GetValueAtIndex(1)->GetScalar() = path_array_addr;
+ arguments.GetValueAtIndex(2)->GetScalar() = buffer_addr;
+ arguments.GetValueAtIndex(3)->GetScalar() = return_addr;
+
+ lldb::addr_t func_args_addr = LLDB_INVALID_ADDRESS;
+
+ diagnostics.Clear();
+ if (!do_dlopen_function->WriteFunctionArguments(exe_ctx,
+ func_args_addr,
+ arguments,
+ diagnostics)) {
+ error.SetErrorStringWithFormat("dlopen error: could not write function "
+ "arguments: %s",
+ diagnostics.GetString().c_str());
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+
+ // Make sure we clean up the args structure. We can't reuse it because the
+ // Platform lives longer than the process and the Platforms don't get a
+ // signal to clean up cached data when a process goes away.
+ CleanUp args_cleanup([do_dlopen_function, &exe_ctx, func_args_addr] {
+ do_dlopen_function->DeallocateFunctionResults(exe_ctx, func_args_addr);
+ });
+
+ // Now run the caller:
+ EvaluateExpressionOptions options;
+ options.SetExecutionPolicy(eExecutionPolicyAlways);
+ options.SetLanguage(eLanguageTypeC_plus_plus);
+ options.SetIgnoreBreakpoints(true);
+ options.SetUnwindOnError(true);
+ options.SetTrapExceptions(false); // dlopen can't throw exceptions, so
+ // don't do the work to trap them.
+ options.SetTimeout(process->GetUtilityExpressionTimeout());
+ options.SetIsForUtilityExpr(true);
+
+ Value return_value;
+ // Fetch the clang types we will need:
+ ClangASTContext *ast = process->GetTarget().GetScratchClangASTContext();
+
+ CompilerType clang_void_pointer_type
+ = ast->GetBasicType(eBasicTypeVoid).GetPointerType();
+
+ return_value.SetCompilerType(clang_void_pointer_type);
+
+ ExpressionResults results = do_dlopen_function->ExecuteFunction(
+ exe_ctx, &func_args_addr, options, diagnostics, return_value);
+ if (results != eExpressionCompleted) {
+ error.SetErrorStringWithFormat("dlopen error: failed executing "
+ "dlopen wrapper function: %s",
+ diagnostics.GetString().c_str());
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+
+ // Read the dlopen token from the return area:
+ lldb::addr_t token = process->ReadPointerFromMemory(return_addr,
+ utility_error);
+ if (utility_error.Fail()) {
+ error.SetErrorStringWithFormat("dlopen error: could not read the return "
+ "struct: %s", utility_error.AsCString());
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+
+ // The dlopen succeeded!
+ if (token != 0x0) {
+ if (loaded_image && buffer_addr != 0x0)
+ {
+ // Capture the image which was loaded. We leave it in the buffer on
+ // exit from the dlopen function, so we can just read it from there:
+ std::string name_string;
+ process->ReadCStringFromMemory(buffer_addr, name_string, utility_error);
+ if (utility_error.Success())
+ loaded_image->SetFile(name_string, llvm::sys::path::Style::posix);
+ }
+ return process->AddImageToken(token);
+ }
+
+ // We got an error, lets read in the error string:
+ std::string dlopen_error_str;
+ lldb::addr_t error_addr
+ = process->ReadPointerFromMemory(return_addr + addr_size, utility_error);
+ if (utility_error.Fail()) {
+ error.SetErrorStringWithFormat("dlopen error: could not read error string: "
+ "%s", utility_error.AsCString());
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+
+ size_t num_chars = process->ReadCStringFromMemory(error_addr + addr_size,
+ dlopen_error_str,
+ utility_error);
+ if (utility_error.Success() && num_chars > 0)
+ error.SetErrorStringWithFormat("dlopen error: %s",
+ dlopen_error_str.c_str());
+ else
+ error.SetErrorStringWithFormat("dlopen failed for unknown reasons.");
+
+ return LLDB_INVALID_IMAGE_TOKEN;
+}
+
+Status PlatformPOSIX::UnloadImage(lldb_private::Process *process,
+ uint32_t image_token) {
+ const addr_t image_addr = process->GetImagePtrFromToken(image_token);
+ if (image_addr == LLDB_INVALID_ADDRESS)
+ return Status("Invalid image token");
+
+ StreamString expr;
+ expr.Printf("dlclose((void *)0x%" PRIx64 ")", image_addr);
+ llvm::StringRef prefix = GetLibdlFunctionDeclarations(process);
+ lldb::ValueObjectSP result_valobj_sp;
+ Status error = EvaluateLibdlExpression(process, expr.GetData(), prefix,
+ result_valobj_sp);
+ if (error.Fail())
+ return error;
+
+ if (result_valobj_sp->GetError().Fail())
+ return result_valobj_sp->GetError();
+
+ Scalar scalar;
+ if (result_valobj_sp->ResolveValue(scalar)) {
+ if (scalar.UInt(1))
+ return Status("expression failed: \"%s\"", expr.GetData());
+ process->ResetImageToken(image_token);
+ }
+ return Status();
+}
+
+llvm::StringRef
+PlatformPOSIX::GetLibdlFunctionDeclarations(lldb_private::Process *process) {
+ return R"(
+ extern "C" void* dlopen(const char*, int);
+ extern "C" void* dlsym(void*, const char*);
+ extern "C" int dlclose(void*);
+ extern "C" char* dlerror(void);
+ )";
+}
+
+size_t PlatformPOSIX::ConnectToWaitingProcesses(Debugger &debugger,
+ Status &error) {
+ if (m_remote_platform_sp)
+ return m_remote_platform_sp->ConnectToWaitingProcesses(debugger, error);
+ return Platform::ConnectToWaitingProcesses(debugger, error);
+}
+
+ConstString PlatformPOSIX::GetFullNameForDylib(ConstString basename) {
+ if (basename.IsEmpty())
+ return basename;
+
+ StreamString stream;
+ stream.Printf("lib%s.so", basename.GetCString());
+ return ConstString(stream.GetString());
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.h b/contrib/llvm-project/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.h
new file mode 100644
index 000000000000..5858f99088e6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.h
@@ -0,0 +1,111 @@
+//===-- PlatformPOSIX.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 liblldb_PlatformPOSIX_h_
+#define liblldb_PlatformPOSIX_h_
+
+#include <map>
+#include <memory>
+
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Target/RemoteAwarePlatform.h"
+
+class PlatformPOSIX : public lldb_private::RemoteAwarePlatform {
+public:
+ PlatformPOSIX(bool is_host);
+
+ ~PlatformPOSIX() override;
+
+ // lldb_private::Platform functions
+
+ lldb_private::OptionGroupOptions *
+ GetConnectionOptions(lldb_private::CommandInterpreter &interpreter) override;
+
+ lldb_private::Status PutFile(const lldb_private::FileSpec &source,
+ const lldb_private::FileSpec &destination,
+ uint32_t uid = UINT32_MAX,
+ uint32_t gid = UINT32_MAX) override;
+
+ lldb_private::Status
+ GetFile(const lldb_private::FileSpec &source,
+ const lldb_private::FileSpec &destination) override;
+
+ const lldb::UnixSignalsSP &GetRemoteUnixSignals() override;
+
+ lldb_private::Status ResolveExecutable(
+ const lldb_private::ModuleSpec &module_spec, lldb::ModuleSP &module_sp,
+ const lldb_private::FileSpecList *module_search_paths_ptr) override;
+
+ lldb::ProcessSP Attach(lldb_private::ProcessAttachInfo &attach_info,
+ lldb_private::Debugger &debugger,
+ lldb_private::Target *target, // Can be nullptr, if
+ // nullptr create a new
+ // target, else use
+ // existing one
+ lldb_private::Status &error) override;
+
+ lldb::ProcessSP DebugProcess(lldb_private::ProcessLaunchInfo &launch_info,
+ lldb_private::Debugger &debugger,
+ lldb_private::Target *target, // Can be nullptr,
+ // if nullptr
+ // create a new
+ // target, else use
+ // existing one
+ lldb_private::Status &error) override;
+
+ std::string GetPlatformSpecificConnectionInformation() override;
+
+ void CalculateTrapHandlerSymbolNames() override;
+
+ lldb_private::Status ConnectRemote(lldb_private::Args &args) override;
+
+ lldb_private::Status DisconnectRemote() override;
+
+ uint32_t DoLoadImage(lldb_private::Process *process,
+ const lldb_private::FileSpec &remote_file,
+ const std::vector<std::string> *paths,
+ lldb_private::Status &error,
+ lldb_private::FileSpec *loaded_image) override;
+
+ lldb_private::Status UnloadImage(lldb_private::Process *process,
+ uint32_t image_token) override;
+
+ size_t ConnectToWaitingProcesses(lldb_private::Debugger &debugger,
+ lldb_private::Status &error) override;
+
+ lldb_private::ConstString GetFullNameForDylib(lldb_private::ConstString basename) override;
+
+protected:
+ std::unique_ptr<lldb_private::OptionGroupPlatformRSync>
+ m_option_group_platform_rsync;
+ std::unique_ptr<lldb_private::OptionGroupPlatformSSH>
+ m_option_group_platform_ssh;
+ std::unique_ptr<lldb_private::OptionGroupPlatformCaching>
+ m_option_group_platform_caching;
+
+ std::map<lldb_private::CommandInterpreter *,
+ std::unique_ptr<lldb_private::OptionGroupOptions>>
+ m_options;
+
+ lldb_private::Status
+ EvaluateLibdlExpression(lldb_private::Process *process, const char *expr_cstr,
+ llvm::StringRef expr_prefix,
+ lldb::ValueObjectSP &result_valobj_sp);
+
+ std::unique_ptr<lldb_private::UtilityFunction>
+ MakeLoadImageUtilityFunction(lldb_private::ExecutionContext &exe_ctx,
+ lldb_private::Status &error);
+
+ virtual
+ llvm::StringRef GetLibdlFunctionDeclarations(lldb_private::Process *process);
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(PlatformPOSIX);
+};
+
+#endif // liblldb_PlatformPOSIX_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp b/contrib/llvm-project/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
new file mode 100644
index 000000000000..9c52b59e2b06
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
@@ -0,0 +1,858 @@
+//===-- PlatformRemoteGDBServer.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 "PlatformRemoteGDBServer.h"
+#include "lldb/Host/Config.h"
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Host/ConnectionFileDescriptor.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/ProcessInfo.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/UriParser.h"
+
+#include "Plugins/Process/Utility/GDBRemoteSignals.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::platform_gdb_server;
+
+static bool g_initialized = false;
+
+void PlatformRemoteGDBServer::Initialize() {
+ Platform::Initialize();
+
+ if (!g_initialized) {
+ g_initialized = true;
+ PluginManager::RegisterPlugin(
+ PlatformRemoteGDBServer::GetPluginNameStatic(),
+ PlatformRemoteGDBServer::GetDescriptionStatic(),
+ PlatformRemoteGDBServer::CreateInstance);
+ }
+}
+
+void PlatformRemoteGDBServer::Terminate() {
+ if (g_initialized) {
+ g_initialized = false;
+ PluginManager::UnregisterPlugin(PlatformRemoteGDBServer::CreateInstance);
+ }
+
+ Platform::Terminate();
+}
+
+PlatformSP PlatformRemoteGDBServer::CreateInstance(bool force,
+ const ArchSpec *arch) {
+ bool create = force;
+ if (!create) {
+ create = !arch->TripleVendorWasSpecified() && !arch->TripleOSWasSpecified();
+ }
+ if (create)
+ return PlatformSP(new PlatformRemoteGDBServer());
+ return PlatformSP();
+}
+
+ConstString PlatformRemoteGDBServer::GetPluginNameStatic() {
+ static ConstString g_name("remote-gdb-server");
+ return g_name;
+}
+
+const char *PlatformRemoteGDBServer::GetDescriptionStatic() {
+ return "A platform that uses the GDB remote protocol as the communication "
+ "transport.";
+}
+
+const char *PlatformRemoteGDBServer::GetDescription() {
+ if (m_platform_description.empty()) {
+ if (IsConnected()) {
+ // Send the get description packet
+ }
+ }
+
+ if (!m_platform_description.empty())
+ return m_platform_description.c_str();
+ return GetDescriptionStatic();
+}
+
+Status PlatformRemoteGDBServer::ResolveExecutable(
+ const ModuleSpec &module_spec, lldb::ModuleSP &exe_module_sp,
+ const FileSpecList *module_search_paths_ptr) {
+ // copied from PlatformRemoteiOS
+
+ Status error;
+ // Nothing special to do here, just use the actual file and architecture
+
+ ModuleSpec resolved_module_spec(module_spec);
+
+ // Resolve any executable within an apk on Android?
+ // Host::ResolveExecutableInBundle (resolved_module_spec.GetFileSpec());
+
+ if (FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec()) ||
+ module_spec.GetUUID().IsValid()) {
+ if (resolved_module_spec.GetArchitecture().IsValid() ||
+ resolved_module_spec.GetUUID().IsValid()) {
+ error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp,
+ module_search_paths_ptr, nullptr,
+ nullptr);
+
+ if (exe_module_sp && exe_module_sp->GetObjectFile())
+ return error;
+ exe_module_sp.reset();
+ }
+ // No valid architecture was specified or the exact arch wasn't found so
+ // ask the platform for the architectures that we should be using (in the
+ // correct order) and see if we can find a match that way
+ StreamString arch_names;
+ for (uint32_t idx = 0; GetSupportedArchitectureAtIndex(
+ idx, resolved_module_spec.GetArchitecture());
+ ++idx) {
+ error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp,
+ module_search_paths_ptr, nullptr,
+ nullptr);
+ // Did we find an executable using one of the
+ if (error.Success()) {
+ if (exe_module_sp && exe_module_sp->GetObjectFile())
+ break;
+ else
+ error.SetErrorToGenericError();
+ }
+
+ if (idx > 0)
+ arch_names.PutCString(", ");
+ arch_names.PutCString(
+ resolved_module_spec.GetArchitecture().GetArchitectureName());
+ }
+
+ if (error.Fail() || !exe_module_sp) {
+ if (FileSystem::Instance().Readable(resolved_module_spec.GetFileSpec())) {
+ error.SetErrorStringWithFormat(
+ "'%s' doesn't contain any '%s' platform architectures: %s",
+ resolved_module_spec.GetFileSpec().GetPath().c_str(),
+ GetPluginName().GetCString(), arch_names.GetData());
+ } else {
+ error.SetErrorStringWithFormat(
+ "'%s' is not readable",
+ resolved_module_spec.GetFileSpec().GetPath().c_str());
+ }
+ }
+ } else {
+ error.SetErrorStringWithFormat(
+ "'%s' does not exist",
+ resolved_module_spec.GetFileSpec().GetPath().c_str());
+ }
+
+ return error;
+}
+
+bool PlatformRemoteGDBServer::GetModuleSpec(const FileSpec &module_file_spec,
+ const ArchSpec &arch,
+ ModuleSpec &module_spec) {
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
+
+ const auto module_path = module_file_spec.GetPath(false);
+
+ if (!m_gdb_client.GetModuleInfo(module_file_spec, arch, module_spec)) {
+ if (log)
+ log->Printf(
+ "PlatformRemoteGDBServer::%s - failed to get module info for %s:%s",
+ __FUNCTION__, module_path.c_str(),
+ arch.GetTriple().getTriple().c_str());
+ return false;
+ }
+
+ if (log) {
+ StreamString stream;
+ module_spec.Dump(stream);
+ log->Printf(
+ "PlatformRemoteGDBServer::%s - got module info for (%s:%s) : %s",
+ __FUNCTION__, module_path.c_str(), arch.GetTriple().getTriple().c_str(),
+ stream.GetData());
+ }
+
+ return true;
+}
+
+Status PlatformRemoteGDBServer::GetFileWithUUID(const FileSpec &platform_file,
+ const UUID *uuid_ptr,
+ FileSpec &local_file) {
+ // Default to the local case
+ local_file = platform_file;
+ return Status();
+}
+
+/// Default Constructor
+PlatformRemoteGDBServer::PlatformRemoteGDBServer()
+ : Platform(false), // This is a remote platform
+ m_gdb_client() {}
+
+/// Destructor.
+///
+/// The destructor is virtual since this class is designed to be
+/// inherited from by the plug-in instance.
+PlatformRemoteGDBServer::~PlatformRemoteGDBServer() {}
+
+bool PlatformRemoteGDBServer::GetSupportedArchitectureAtIndex(uint32_t idx,
+ ArchSpec &arch) {
+ ArchSpec remote_arch = m_gdb_client.GetSystemArchitecture();
+
+ if (idx == 0) {
+ arch = remote_arch;
+ return arch.IsValid();
+ } else if (idx == 1 && remote_arch.IsValid() &&
+ remote_arch.GetTriple().isArch64Bit()) {
+ arch.SetTriple(remote_arch.GetTriple().get32BitArchVariant());
+ return arch.IsValid();
+ }
+ return false;
+}
+
+size_t PlatformRemoteGDBServer::GetSoftwareBreakpointTrapOpcode(
+ Target &target, BreakpointSite *bp_site) {
+ // This isn't needed if the z/Z packets are supported in the GDB remote
+ // server. But we might need a packet to detect this.
+ return 0;
+}
+
+bool PlatformRemoteGDBServer::GetRemoteOSVersion() {
+ m_os_version = m_gdb_client.GetOSVersion();
+ return !m_os_version.empty();
+}
+
+bool PlatformRemoteGDBServer::GetRemoteOSBuildString(std::string &s) {
+ return m_gdb_client.GetOSBuildString(s);
+}
+
+bool PlatformRemoteGDBServer::GetRemoteOSKernelDescription(std::string &s) {
+ return m_gdb_client.GetOSKernelDescription(s);
+}
+
+// Remote Platform subclasses need to override this function
+ArchSpec PlatformRemoteGDBServer::GetRemoteSystemArchitecture() {
+ return m_gdb_client.GetSystemArchitecture();
+}
+
+FileSpec PlatformRemoteGDBServer::GetRemoteWorkingDirectory() {
+ if (IsConnected()) {
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
+ FileSpec working_dir;
+ if (m_gdb_client.GetWorkingDir(working_dir) && log)
+ log->Printf(
+ "PlatformRemoteGDBServer::GetRemoteWorkingDirectory() -> '%s'",
+ working_dir.GetCString());
+ return working_dir;
+ } else {
+ return Platform::GetRemoteWorkingDirectory();
+ }
+}
+
+bool PlatformRemoteGDBServer::SetRemoteWorkingDirectory(
+ const FileSpec &working_dir) {
+ if (IsConnected()) {
+ // Clear the working directory it case it doesn't get set correctly. This
+ // will for use to re-read it
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
+ if (log)
+ log->Printf("PlatformRemoteGDBServer::SetRemoteWorkingDirectory('%s')",
+ working_dir.GetCString());
+ return m_gdb_client.SetWorkingDir(working_dir) == 0;
+ } else
+ return Platform::SetRemoteWorkingDirectory(working_dir);
+}
+
+bool PlatformRemoteGDBServer::IsConnected() const {
+ return m_gdb_client.IsConnected();
+}
+
+Status PlatformRemoteGDBServer::ConnectRemote(Args &args) {
+ Status error;
+ if (IsConnected()) {
+ error.SetErrorStringWithFormat("the platform is already connected to '%s', "
+ "execute 'platform disconnect' to close the "
+ "current connection",
+ GetHostname());
+ } else {
+ if (args.GetArgumentCount() == 1) {
+ m_gdb_client.SetConnection(new ConnectionFileDescriptor());
+ // we're going to reuse the hostname when we connect to the debugserver
+ int port;
+ std::string path;
+ const char *url = args.GetArgumentAtIndex(0);
+ if (!url)
+ return Status("URL is null.");
+ llvm::StringRef scheme, hostname, pathname;
+ if (!UriParser::Parse(url, scheme, hostname, port, pathname))
+ return Status("Invalid URL: %s", url);
+ m_platform_scheme = scheme;
+ m_platform_hostname = hostname;
+ path = pathname;
+
+ const ConnectionStatus status = m_gdb_client.Connect(url, &error);
+ if (status == eConnectionStatusSuccess) {
+ if (m_gdb_client.HandshakeWithServer(&error)) {
+ m_gdb_client.GetHostInfo();
+ // If a working directory was set prior to connecting, send it down
+ // now
+ if (m_working_dir)
+ m_gdb_client.SetWorkingDir(m_working_dir);
+ } else {
+ m_gdb_client.Disconnect();
+ if (error.Success())
+ error.SetErrorString("handshake failed");
+ }
+ }
+ } else {
+ error.SetErrorString(
+ "\"platform connect\" takes a single argument: <connect-url>");
+ }
+ }
+ return error;
+}
+
+Status PlatformRemoteGDBServer::DisconnectRemote() {
+ Status error;
+ m_gdb_client.Disconnect(&error);
+ m_remote_signals_sp.reset();
+ return error;
+}
+
+const char *PlatformRemoteGDBServer::GetHostname() {
+ m_gdb_client.GetHostname(m_name);
+ if (m_name.empty())
+ return nullptr;
+ return m_name.c_str();
+}
+
+llvm::Optional<std::string>
+PlatformRemoteGDBServer::DoGetUserName(UserIDResolver::id_t uid) {
+ std::string name;
+ if (m_gdb_client.GetUserName(uid, name))
+ return std::move(name);
+ return llvm::None;
+}
+
+llvm::Optional<std::string>
+PlatformRemoteGDBServer::DoGetGroupName(UserIDResolver::id_t gid) {
+ std::string name;
+ if (m_gdb_client.GetGroupName(gid, name))
+ return std::move(name);
+ return llvm::None;
+}
+
+uint32_t PlatformRemoteGDBServer::FindProcesses(
+ const ProcessInstanceInfoMatch &match_info,
+ ProcessInstanceInfoList &process_infos) {
+ return m_gdb_client.FindProcesses(match_info, process_infos);
+}
+
+bool PlatformRemoteGDBServer::GetProcessInfo(
+ lldb::pid_t pid, ProcessInstanceInfo &process_info) {
+ return m_gdb_client.GetProcessInfo(pid, process_info);
+}
+
+Status PlatformRemoteGDBServer::LaunchProcess(ProcessLaunchInfo &launch_info) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
+ Status error;
+
+ if (log)
+ log->Printf("PlatformRemoteGDBServer::%s() called", __FUNCTION__);
+
+ auto num_file_actions = launch_info.GetNumFileActions();
+ for (decltype(num_file_actions) i = 0; i < num_file_actions; ++i) {
+ const auto file_action = launch_info.GetFileActionAtIndex(i);
+ if (file_action->GetAction() != FileAction::eFileActionOpen)
+ continue;
+ switch (file_action->GetFD()) {
+ case STDIN_FILENO:
+ m_gdb_client.SetSTDIN(file_action->GetFileSpec());
+ break;
+ case STDOUT_FILENO:
+ m_gdb_client.SetSTDOUT(file_action->GetFileSpec());
+ break;
+ case STDERR_FILENO:
+ m_gdb_client.SetSTDERR(file_action->GetFileSpec());
+ break;
+ }
+ }
+
+ m_gdb_client.SetDisableASLR(
+ launch_info.GetFlags().Test(eLaunchFlagDisableASLR));
+ m_gdb_client.SetDetachOnError(
+ launch_info.GetFlags().Test(eLaunchFlagDetachOnError));
+
+ FileSpec working_dir = launch_info.GetWorkingDirectory();
+ if (working_dir) {
+ m_gdb_client.SetWorkingDir(working_dir);
+ }
+
+ // Send the environment and the program + arguments after we connect
+ m_gdb_client.SendEnvironment(launch_info.GetEnvironment());
+
+ ArchSpec arch_spec = launch_info.GetArchitecture();
+ const char *arch_triple = arch_spec.GetTriple().str().c_str();
+
+ m_gdb_client.SendLaunchArchPacket(arch_triple);
+ if (log)
+ log->Printf(
+ "PlatformRemoteGDBServer::%s() set launch architecture triple to '%s'",
+ __FUNCTION__, arch_triple ? arch_triple : "<NULL>");
+
+ int arg_packet_err;
+ {
+ // Scope for the scoped timeout object
+ process_gdb_remote::GDBRemoteCommunication::ScopedTimeout timeout(
+ m_gdb_client, std::chrono::seconds(5));
+ arg_packet_err = m_gdb_client.SendArgumentsPacket(launch_info);
+ }
+
+ if (arg_packet_err == 0) {
+ std::string error_str;
+ if (m_gdb_client.GetLaunchSuccess(error_str)) {
+ const auto pid = m_gdb_client.GetCurrentProcessID(false);
+ if (pid != LLDB_INVALID_PROCESS_ID) {
+ launch_info.SetProcessID(pid);
+ if (log)
+ log->Printf("PlatformRemoteGDBServer::%s() pid %" PRIu64
+ " launched successfully",
+ __FUNCTION__, pid);
+ } else {
+ if (log)
+ log->Printf("PlatformRemoteGDBServer::%s() launch succeeded but we "
+ "didn't get a valid process id back!",
+ __FUNCTION__);
+ error.SetErrorString("failed to get PID");
+ }
+ } else {
+ error.SetErrorString(error_str.c_str());
+ if (log)
+ log->Printf("PlatformRemoteGDBServer::%s() launch failed: %s",
+ __FUNCTION__, error.AsCString());
+ }
+ } else {
+ error.SetErrorStringWithFormat("'A' packet returned an error: %i",
+ arg_packet_err);
+ }
+ return error;
+}
+
+Status PlatformRemoteGDBServer::KillProcess(const lldb::pid_t pid) {
+ if (!KillSpawnedProcess(pid))
+ return Status("failed to kill remote spawned process");
+ return Status();
+}
+
+lldb::ProcessSP PlatformRemoteGDBServer::DebugProcess(
+ ProcessLaunchInfo &launch_info, Debugger &debugger,
+ Target *target, // Can be NULL, if NULL create a new target, else use
+ // existing one
+ Status &error) {
+ lldb::ProcessSP process_sp;
+ if (IsRemote()) {
+ if (IsConnected()) {
+ lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID;
+ std::string connect_url;
+ if (!LaunchGDBServer(debugserver_pid, connect_url)) {
+ error.SetErrorStringWithFormat("unable to launch a GDB server on '%s'",
+ GetHostname());
+ } else {
+ if (target == nullptr) {
+ TargetSP new_target_sp;
+
+ error = debugger.GetTargetList().CreateTarget(
+ debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
+ target = new_target_sp.get();
+ } else
+ error.Clear();
+
+ if (target && error.Success()) {
+ debugger.GetTargetList().SetSelectedTarget(target);
+
+ // The darwin always currently uses the GDB remote debugger plug-in
+ // so even when debugging locally we are debugging remotely!
+ process_sp = target->CreateProcess(launch_info.GetListener(),
+ "gdb-remote", nullptr);
+
+ if (process_sp) {
+ error = process_sp->ConnectRemote(nullptr, connect_url.c_str());
+ // Retry the connect remote one time...
+ if (error.Fail())
+ error = process_sp->ConnectRemote(nullptr, connect_url.c_str());
+ if (error.Success())
+ error = process_sp->Launch(launch_info);
+ else if (debugserver_pid != LLDB_INVALID_PROCESS_ID) {
+ printf("error: connect remote failed (%s)\n", error.AsCString());
+ KillSpawnedProcess(debugserver_pid);
+ }
+ }
+ }
+ }
+ } else {
+ error.SetErrorString("not connected to remote gdb server");
+ }
+ }
+ return process_sp;
+}
+
+bool PlatformRemoteGDBServer::LaunchGDBServer(lldb::pid_t &pid,
+ std::string &connect_url) {
+ ArchSpec remote_arch = GetRemoteSystemArchitecture();
+ llvm::Triple &remote_triple = remote_arch.GetTriple();
+
+ uint16_t port = 0;
+ std::string socket_name;
+ bool launch_result = false;
+ if (remote_triple.getVendor() == llvm::Triple::Apple &&
+ remote_triple.getOS() == llvm::Triple::IOS) {
+ // When remote debugging to iOS, we use a USB mux that always talks to
+ // localhost, so we will need the remote debugserver to accept connections
+ // only from localhost, no matter what our current hostname is
+ launch_result =
+ m_gdb_client.LaunchGDBServer("127.0.0.1", pid, port, socket_name);
+ } else {
+ // All other hosts should use their actual hostname
+ launch_result =
+ m_gdb_client.LaunchGDBServer(nullptr, pid, port, socket_name);
+ }
+
+ if (!launch_result)
+ return false;
+
+ connect_url =
+ MakeGdbServerUrl(m_platform_scheme, m_platform_hostname, port,
+ (socket_name.empty()) ? nullptr : socket_name.c_str());
+ return true;
+}
+
+bool PlatformRemoteGDBServer::KillSpawnedProcess(lldb::pid_t pid) {
+ return m_gdb_client.KillSpawnedProcess(pid);
+}
+
+lldb::ProcessSP PlatformRemoteGDBServer::Attach(
+ ProcessAttachInfo &attach_info, Debugger &debugger,
+ Target *target, // Can be NULL, if NULL create a new target, else use
+ // existing one
+ Status &error) {
+ lldb::ProcessSP process_sp;
+ if (IsRemote()) {
+ if (IsConnected()) {
+ lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID;
+ std::string connect_url;
+ if (!LaunchGDBServer(debugserver_pid, connect_url)) {
+ error.SetErrorStringWithFormat("unable to launch a GDB server on '%s'",
+ GetHostname());
+ } else {
+ if (target == nullptr) {
+ TargetSP new_target_sp;
+
+ error = debugger.GetTargetList().CreateTarget(
+ debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
+ target = new_target_sp.get();
+ } else
+ error.Clear();
+
+ if (target && error.Success()) {
+ debugger.GetTargetList().SetSelectedTarget(target);
+
+ // The darwin always currently uses the GDB remote debugger plug-in
+ // so even when debugging locally we are debugging remotely!
+ process_sp =
+ target->CreateProcess(attach_info.GetListenerForProcess(debugger),
+ "gdb-remote", nullptr);
+ if (process_sp) {
+ error = process_sp->ConnectRemote(nullptr, connect_url.c_str());
+ if (error.Success()) {
+ ListenerSP listener_sp = attach_info.GetHijackListener();
+ if (listener_sp)
+ process_sp->HijackProcessEvents(listener_sp);
+ error = process_sp->Attach(attach_info);
+ }
+
+ if (error.Fail() && debugserver_pid != LLDB_INVALID_PROCESS_ID) {
+ KillSpawnedProcess(debugserver_pid);
+ }
+ }
+ }
+ }
+ } else {
+ error.SetErrorString("not connected to remote gdb server");
+ }
+ }
+ return process_sp;
+}
+
+Status PlatformRemoteGDBServer::MakeDirectory(const FileSpec &file_spec,
+ uint32_t mode) {
+ Status error = m_gdb_client.MakeDirectory(file_spec, mode);
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
+ if (log)
+ log->Printf("PlatformRemoteGDBServer::MakeDirectory(path='%s', mode=%o) "
+ "error = %u (%s)",
+ file_spec.GetCString(), mode, error.GetError(),
+ error.AsCString());
+ return error;
+}
+
+Status PlatformRemoteGDBServer::GetFilePermissions(const FileSpec &file_spec,
+ uint32_t &file_permissions) {
+ Status error = m_gdb_client.GetFilePermissions(file_spec, file_permissions);
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
+ if (log)
+ log->Printf("PlatformRemoteGDBServer::GetFilePermissions(path='%s', "
+ "file_permissions=%o) error = %u (%s)",
+ file_spec.GetCString(), file_permissions, error.GetError(),
+ error.AsCString());
+ return error;
+}
+
+Status PlatformRemoteGDBServer::SetFilePermissions(const FileSpec &file_spec,
+ uint32_t file_permissions) {
+ Status error = m_gdb_client.SetFilePermissions(file_spec, file_permissions);
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
+ if (log)
+ log->Printf("PlatformRemoteGDBServer::SetFilePermissions(path='%s', "
+ "file_permissions=%o) error = %u (%s)",
+ file_spec.GetCString(), file_permissions, error.GetError(),
+ error.AsCString());
+ return error;
+}
+
+lldb::user_id_t PlatformRemoteGDBServer::OpenFile(const FileSpec &file_spec,
+ uint32_t flags, uint32_t mode,
+ Status &error) {
+ return m_gdb_client.OpenFile(file_spec, flags, mode, error);
+}
+
+bool PlatformRemoteGDBServer::CloseFile(lldb::user_id_t fd, Status &error) {
+ return m_gdb_client.CloseFile(fd, error);
+}
+
+lldb::user_id_t
+PlatformRemoteGDBServer::GetFileSize(const FileSpec &file_spec) {
+ return m_gdb_client.GetFileSize(file_spec);
+}
+
+uint64_t PlatformRemoteGDBServer::ReadFile(lldb::user_id_t fd, uint64_t offset,
+ void *dst, uint64_t dst_len,
+ Status &error) {
+ return m_gdb_client.ReadFile(fd, offset, dst, dst_len, error);
+}
+
+uint64_t PlatformRemoteGDBServer::WriteFile(lldb::user_id_t fd, uint64_t offset,
+ const void *src, uint64_t src_len,
+ Status &error) {
+ return m_gdb_client.WriteFile(fd, offset, src, src_len, error);
+}
+
+Status PlatformRemoteGDBServer::PutFile(const FileSpec &source,
+ const FileSpec &destination,
+ uint32_t uid, uint32_t gid) {
+ return Platform::PutFile(source, destination, uid, gid);
+}
+
+Status PlatformRemoteGDBServer::CreateSymlink(
+ const FileSpec &src, // The name of the link is in src
+ const FileSpec &dst) // The symlink points to dst
+{
+ Status error = m_gdb_client.CreateSymlink(src, dst);
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
+ if (log)
+ log->Printf("PlatformRemoteGDBServer::CreateSymlink(src='%s', dst='%s') "
+ "error = %u (%s)",
+ src.GetCString(), dst.GetCString(), error.GetError(),
+ error.AsCString());
+ return error;
+}
+
+Status PlatformRemoteGDBServer::Unlink(const FileSpec &file_spec) {
+ Status error = m_gdb_client.Unlink(file_spec);
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
+ if (log)
+ log->Printf("PlatformRemoteGDBServer::Unlink(path='%s') error = %u (%s)",
+ file_spec.GetCString(), error.GetError(), error.AsCString());
+ return error;
+}
+
+bool PlatformRemoteGDBServer::GetFileExists(const FileSpec &file_spec) {
+ return m_gdb_client.GetFileExists(file_spec);
+}
+
+Status PlatformRemoteGDBServer::RunShellCommand(
+ const char *command, // Shouldn't be NULL
+ const FileSpec &
+ working_dir, // Pass empty FileSpec to use the current working directory
+ int *status_ptr, // Pass NULL if you don't want the process exit status
+ int *signo_ptr, // Pass NULL if you don't want the signal that caused the
+ // process to exit
+ std::string
+ *command_output, // Pass NULL if you don't want the command output
+ const Timeout<std::micro> &timeout) {
+ return m_gdb_client.RunShellCommand(command, working_dir, status_ptr,
+ signo_ptr, command_output, timeout);
+}
+
+void PlatformRemoteGDBServer::CalculateTrapHandlerSymbolNames() {
+ m_trap_handlers.push_back(ConstString("_sigtramp"));
+}
+
+const UnixSignalsSP &PlatformRemoteGDBServer::GetRemoteUnixSignals() {
+ if (!IsConnected())
+ return Platform::GetRemoteUnixSignals();
+
+ if (m_remote_signals_sp)
+ return m_remote_signals_sp;
+
+ // If packet not implemented or JSON failed to parse, we'll guess the signal
+ // set based on the remote architecture.
+ m_remote_signals_sp = UnixSignals::Create(GetRemoteSystemArchitecture());
+
+ StringExtractorGDBRemote response;
+ auto result = m_gdb_client.SendPacketAndWaitForResponse("jSignalsInfo",
+ response, false);
+
+ if (result != decltype(result)::Success ||
+ response.GetResponseType() != response.eResponse)
+ return m_remote_signals_sp;
+
+ auto object_sp = StructuredData::ParseJSON(response.GetStringRef());
+ if (!object_sp || !object_sp->IsValid())
+ return m_remote_signals_sp;
+
+ auto array_sp = object_sp->GetAsArray();
+ if (!array_sp || !array_sp->IsValid())
+ return m_remote_signals_sp;
+
+ auto remote_signals_sp = std::make_shared<lldb_private::GDBRemoteSignals>();
+
+ bool done = array_sp->ForEach(
+ [&remote_signals_sp](StructuredData::Object *object) -> bool {
+ if (!object || !object->IsValid())
+ return false;
+
+ auto dict = object->GetAsDictionary();
+ if (!dict || !dict->IsValid())
+ return false;
+
+ // Signal number and signal name are required.
+ int signo;
+ if (!dict->GetValueForKeyAsInteger("signo", signo))
+ return false;
+
+ llvm::StringRef name;
+ if (!dict->GetValueForKeyAsString("name", name))
+ return false;
+
+ // We can live without short_name, description, etc.
+ bool suppress{false};
+ auto object_sp = dict->GetValueForKey("suppress");
+ if (object_sp && object_sp->IsValid())
+ suppress = object_sp->GetBooleanValue();
+
+ bool stop{false};
+ object_sp = dict->GetValueForKey("stop");
+ if (object_sp && object_sp->IsValid())
+ stop = object_sp->GetBooleanValue();
+
+ bool notify{false};
+ object_sp = dict->GetValueForKey("notify");
+ if (object_sp && object_sp->IsValid())
+ notify = object_sp->GetBooleanValue();
+
+ std::string description{""};
+ object_sp = dict->GetValueForKey("description");
+ if (object_sp && object_sp->IsValid())
+ description = object_sp->GetStringValue();
+
+ remote_signals_sp->AddSignal(signo, name.str().c_str(), suppress, stop,
+ notify, description.c_str());
+ return true;
+ });
+
+ if (done)
+ m_remote_signals_sp = std::move(remote_signals_sp);
+
+ return m_remote_signals_sp;
+}
+
+std::string PlatformRemoteGDBServer::MakeGdbServerUrl(
+ const std::string &platform_scheme, const std::string &platform_hostname,
+ uint16_t port, const char *socket_name) {
+ const char *override_scheme =
+ getenv("LLDB_PLATFORM_REMOTE_GDB_SERVER_SCHEME");
+ const char *override_hostname =
+ getenv("LLDB_PLATFORM_REMOTE_GDB_SERVER_HOSTNAME");
+ const char *port_offset_c_str =
+ getenv("LLDB_PLATFORM_REMOTE_GDB_SERVER_PORT_OFFSET");
+ int port_offset = port_offset_c_str ? ::atoi(port_offset_c_str) : 0;
+
+ return MakeUrl(override_scheme ? override_scheme : platform_scheme.c_str(),
+ override_hostname ? override_hostname
+ : platform_hostname.c_str(),
+ port + port_offset, socket_name);
+}
+
+std::string PlatformRemoteGDBServer::MakeUrl(const char *scheme,
+ const char *hostname,
+ uint16_t port, const char *path) {
+ StreamString result;
+ result.Printf("%s://%s", scheme, hostname);
+ if (port != 0)
+ result.Printf(":%u", port);
+ if (path)
+ result.Write(path, strlen(path));
+ return result.GetString();
+}
+
+lldb::ProcessSP PlatformRemoteGDBServer::ConnectProcess(
+ llvm::StringRef connect_url, llvm::StringRef plugin_name,
+ lldb_private::Debugger &debugger, lldb_private::Target *target,
+ lldb_private::Status &error) {
+ if (!IsRemote() || !IsConnected()) {
+ error.SetErrorString("Not connected to remote gdb server");
+ return nullptr;
+ }
+ return Platform::ConnectProcess(connect_url, plugin_name, debugger, target,
+ error);
+}
+
+size_t PlatformRemoteGDBServer::ConnectToWaitingProcesses(Debugger &debugger,
+ Status &error) {
+ std::vector<std::string> connection_urls;
+ GetPendingGdbServerList(connection_urls);
+
+ for (size_t i = 0; i < connection_urls.size(); ++i) {
+ ConnectProcess(connection_urls[i].c_str(), "", debugger, nullptr, error);
+ if (error.Fail())
+ return i; // We already connected to i process succsessfully
+ }
+ return connection_urls.size();
+}
+
+size_t PlatformRemoteGDBServer::GetPendingGdbServerList(
+ std::vector<std::string> &connection_urls) {
+ std::vector<std::pair<uint16_t, std::string>> remote_servers;
+ m_gdb_client.QueryGDBServer(remote_servers);
+ for (const auto &gdbserver : remote_servers) {
+ const char *socket_name_cstr =
+ gdbserver.second.empty() ? nullptr : gdbserver.second.c_str();
+ connection_urls.emplace_back(
+ MakeGdbServerUrl(m_platform_scheme, m_platform_hostname,
+ gdbserver.first, socket_name_cstr));
+ }
+ return connection_urls.size();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h b/contrib/llvm-project/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h
new file mode 100644
index 000000000000..c774daa8ab73
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h
@@ -0,0 +1,201 @@
+//===-- PlatformRemoteGDBServer.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 liblldb_PlatformRemoteGDBServer_h_
+#define liblldb_PlatformRemoteGDBServer_h_
+
+#include <string>
+
+#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h"
+#include "Plugins/Process/Utility/GDBRemoteSignals.h"
+#include "lldb/Target/Platform.h"
+
+namespace lldb_private {
+namespace platform_gdb_server {
+
+class PlatformRemoteGDBServer : public Platform, private UserIDResolver {
+public:
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch);
+
+ static ConstString GetPluginNameStatic();
+
+ static const char *GetDescriptionStatic();
+
+ PlatformRemoteGDBServer();
+
+ ~PlatformRemoteGDBServer() override;
+
+ // lldb_private::PluginInterface functions
+ ConstString GetPluginName() override { return GetPluginNameStatic(); }
+
+ uint32_t GetPluginVersion() override { return 1; }
+
+ // lldb_private::Platform functions
+ Status
+ ResolveExecutable(const ModuleSpec &module_spec, lldb::ModuleSP &module_sp,
+ const FileSpecList *module_search_paths_ptr) override;
+
+ bool GetModuleSpec(const FileSpec &module_file_spec, const ArchSpec &arch,
+ ModuleSpec &module_spec) override;
+
+ const char *GetDescription() override;
+
+ Status GetFileWithUUID(const FileSpec &platform_file, const UUID *uuid_ptr,
+ FileSpec &local_file) override;
+
+ bool GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &proc_info) override;
+
+ uint32_t FindProcesses(const ProcessInstanceInfoMatch &match_info,
+ ProcessInstanceInfoList &process_infos) override;
+
+ Status LaunchProcess(ProcessLaunchInfo &launch_info) override;
+
+ Status KillProcess(const lldb::pid_t pid) override;
+
+ lldb::ProcessSP DebugProcess(ProcessLaunchInfo &launch_info,
+ Debugger &debugger,
+ Target *target, // Can be NULL, if NULL create a
+ // new target, else use existing
+ // one
+ Status &error) override;
+
+ lldb::ProcessSP Attach(ProcessAttachInfo &attach_info, Debugger &debugger,
+ Target *target, // Can be NULL, if NULL create a new
+ // target, else use existing one
+ Status &error) override;
+
+ bool GetSupportedArchitectureAtIndex(uint32_t idx, ArchSpec &arch) override;
+
+ size_t GetSoftwareBreakpointTrapOpcode(Target &target,
+ BreakpointSite *bp_site) override;
+
+ bool GetRemoteOSVersion() override;
+
+ bool GetRemoteOSBuildString(std::string &s) override;
+
+ bool GetRemoteOSKernelDescription(std::string &s) override;
+
+ // Remote Platform subclasses need to override this function
+ ArchSpec GetRemoteSystemArchitecture() override;
+
+ FileSpec GetRemoteWorkingDirectory() override;
+
+ bool SetRemoteWorkingDirectory(const FileSpec &working_dir) override;
+
+ // Remote subclasses should override this and return a valid instance
+ // name if connected.
+ const char *GetHostname() override;
+
+ UserIDResolver &GetUserIDResolver() override { return *this; }
+
+ bool IsConnected() const override;
+
+ Status ConnectRemote(Args &args) override;
+
+ Status DisconnectRemote() override;
+
+ Status MakeDirectory(const FileSpec &file_spec,
+ uint32_t file_permissions) override;
+
+ Status GetFilePermissions(const FileSpec &file_spec,
+ uint32_t &file_permissions) override;
+
+ Status SetFilePermissions(const FileSpec &file_spec,
+ uint32_t file_permissions) override;
+
+ lldb::user_id_t OpenFile(const FileSpec &file_spec, uint32_t flags,
+ uint32_t mode, Status &error) override;
+
+ bool CloseFile(lldb::user_id_t fd, Status &error) override;
+
+ uint64_t ReadFile(lldb::user_id_t fd, uint64_t offset, void *data_ptr,
+ uint64_t len, Status &error) override;
+
+ uint64_t WriteFile(lldb::user_id_t fd, uint64_t offset, const void *data,
+ uint64_t len, Status &error) override;
+
+ lldb::user_id_t GetFileSize(const FileSpec &file_spec) override;
+
+ Status PutFile(const FileSpec &source, const FileSpec &destination,
+ uint32_t uid = UINT32_MAX, uint32_t gid = UINT32_MAX) override;
+
+ Status CreateSymlink(const FileSpec &src, const FileSpec &dst) override;
+
+ bool GetFileExists(const FileSpec &file_spec) override;
+
+ Status Unlink(const FileSpec &path) override;
+
+ Status RunShellCommand(
+ const char *command, // Shouldn't be NULL
+ const FileSpec &working_dir, // Pass empty FileSpec to use the current
+ // working directory
+ int *status_ptr, // Pass NULL if you don't want the process exit status
+ int *signo_ptr, // Pass NULL if you don't want the signal that caused the
+ // process to exit
+ std::string
+ *command_output, // Pass NULL if you don't want the command output
+ const lldb_private::Timeout<std::micro> &timeout) override;
+
+ void CalculateTrapHandlerSymbolNames() override;
+
+ const lldb::UnixSignalsSP &GetRemoteUnixSignals() override;
+
+ lldb::ProcessSP ConnectProcess(llvm::StringRef connect_url,
+ llvm::StringRef plugin_name,
+ lldb_private::Debugger &debugger,
+ lldb_private::Target *target,
+ lldb_private::Status &error) override;
+
+ size_t ConnectToWaitingProcesses(lldb_private::Debugger &debugger,
+ lldb_private::Status &error) override;
+
+ virtual size_t
+ GetPendingGdbServerList(std::vector<std::string> &connection_urls);
+
+protected:
+ process_gdb_remote::GDBRemoteCommunicationClient m_gdb_client;
+ std::string m_platform_description; // After we connect we can get a more
+ // complete description of what we are
+ // connected to
+ std::string m_platform_scheme;
+ std::string m_platform_hostname;
+
+ lldb::UnixSignalsSP m_remote_signals_sp;
+
+ // Launch the debug server on the remote host - caller connects to launched
+ // debug server using connect_url.
+ // Subclasses should override this method if they want to do extra actions
+ // before or
+ // after launching the debug server.
+ virtual bool LaunchGDBServer(lldb::pid_t &pid, std::string &connect_url);
+
+ virtual bool KillSpawnedProcess(lldb::pid_t pid);
+
+ virtual std::string MakeUrl(const char *scheme, const char *hostname,
+ uint16_t port, const char *path);
+
+private:
+ std::string MakeGdbServerUrl(const std::string &platform_scheme,
+ const std::string &platform_hostname,
+ uint16_t port, const char *socket_name);
+
+ llvm::Optional<std::string> DoGetUserName(UserIDResolver::id_t uid) override;
+ llvm::Optional<std::string> DoGetGroupName(UserIDResolver::id_t uid) override;
+
+ DISALLOW_COPY_AND_ASSIGN(PlatformRemoteGDBServer);
+};
+
+} // namespace platform_gdb_server
+} // namespace lldb_private
+
+#endif // liblldb_PlatformRemoteGDBServer_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFBundle.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFBundle.cpp
new file mode 100644
index 000000000000..3cdd2fa575e7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFBundle.cpp
@@ -0,0 +1,68 @@
+//===-- 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) {
+ *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/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFBundle.h b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFBundle.h
new file mode 100644
index 000000000000..f49dc30f1f8f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/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/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFString.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFString.cpp
new file mode 100644
index 000000000000..4dcc05c86657
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFString.cpp
@@ -0,0 +1,153 @@
+//===-- 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/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFString.h b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFString.h
new file mode 100644
index 000000000000..d1bd5682689e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/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/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFUtils.h b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/CFUtils.h
new file mode 100644
index 000000000000..b567524ce63a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/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/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/DarwinProcessLauncher.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/DarwinProcessLauncher.cpp
new file mode 100644
index 000000000000..3ec410fe7d76
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/DarwinProcessLauncher.cpp
@@ -0,0 +1,642 @@
+//===-- DarwinProcessLauncher.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
+//
+//===----------------------------------------------------------------------===//
+
+//
+// DarwinProcessLauncher.cpp
+// lldb
+//
+// Created by Todd Fiala on 8/30/16.
+//
+//
+
+#include "DarwinProcessLauncher.h"
+
+// C includes
+#include <spawn.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+
+#ifndef _POSIX_SPAWN_DISABLE_ASLR
+#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
+#endif
+
+// LLDB includes
+#include "lldb/lldb-enumerations.h"
+
+#include "lldb/Host/PseudoTerminal.h"
+#include "lldb/Target/ProcessLaunchInfo.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+#include "llvm/Support/Errno.h"
+
+#include "CFBundle.h"
+#include "CFString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_darwin;
+using namespace lldb_private::darwin_process_launcher;
+
+namespace {
+static LaunchFlavor g_launch_flavor = LaunchFlavor::Default;
+}
+
+namespace lldb_private {
+namespace darwin_process_launcher {
+
+static uint32_t 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;
+}
+
+static bool ResolveExecutablePath(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...
+ ::strncpy(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) {
+ ::strncpy(resolved_path, result.c_str(), resolved_path_size);
+ return result.size() + 1 < resolved_path_size;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+// TODO check if we have a general purpose fork and exec. We may be
+// able to get rid of this entirely.
+static Status ForkChildForPTraceDebugging(const char *path, char const *argv[],
+ char const *envp[], ::pid_t *pid,
+ int *pty_fd) {
+ Status error;
+ if (!path || !argv || !envp || !pid || !pty_fd) {
+ error.SetErrorString("invalid arguments");
+ return error;
+ }
+
+ // 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;
+ char fork_error[256];
+ memset(fork_error, 0, sizeof(fork_error));
+ *pid = static_cast<::pid_t>(pty.Fork(fork_error, sizeof(fork_error)));
+ if (*pid < 0) {
+ // Status during fork.
+ *pid = static_cast<::pid_t>(LLDB_INVALID_PROCESS_ID);
+ error.SetErrorStringWithFormat("%s(): fork failed: %s", __FUNCTION__,
+ fork_error);
+ return error;
+ } else if (pid == 0) {
+ // Child process
+
+ // Debug this process.
+ ::ptrace(PT_TRACE_ME, 0, 0, 0);
+
+ // Get BSD signals as mach exceptions.
+ ::ptrace(PT_SIGEXC, 0, 0, 0);
+
+ // 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.
+
+ // Set the child process group to match its pid.
+ ::setpgid(0, 0);
+
+ // Sleep a bit to before the exec call.
+ ::sleep(1);
+
+ // Turn this process into the given executable.
+ ::execv(path, (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.
+
+ // Set the child process group to match its pid
+ ::setpgid(*pid, *pid);
+ if (pty_fd) {
+ // 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
+ *pty_fd = pty.ReleaseMasterFileDescriptor();
+ }
+ }
+ return error;
+}
+
+static Status
+CreatePosixSpawnFileAction(const FileAction &action,
+ posix_spawn_file_actions_t *file_actions) {
+ Status error;
+
+ // Log it.
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log) {
+ StreamString stream;
+ stream.PutCString("converting file action for posix_spawn(): ");
+ action.Dump(stream);
+ stream.Flush();
+ log->PutCString(stream.GetString().c_str());
+ }
+
+ // Validate args.
+ if (!file_actions) {
+ error.SetErrorString("mandatory file_actions arg is null");
+ return error;
+ }
+
+ // Build the posix file action.
+ switch (action.GetAction()) {
+ case FileAction::eFileActionOpen: {
+ const int error_code = ::posix_spawn_file_actions_addopen(
+ file_actions, action.GetFD(), action.GetPath(),
+ action.GetActionArgument(), 0);
+ if (error_code != 0) {
+ error.SetError(error_code, eErrorTypePOSIX);
+ return error;
+ }
+ break;
+ }
+
+ case FileAction::eFileActionClose: {
+ const int error_code =
+ ::posix_spawn_file_actions_addclose(file_actions, action.GetFD());
+ if (error_code != 0) {
+ error.SetError(error_code, eErrorTypePOSIX);
+ return error;
+ }
+ break;
+ }
+
+ case FileAction::eFileActionDuplicate: {
+ const int error_code = ::posix_spawn_file_actions_adddup2(
+ file_actions, action.GetFD(), action.GetActionArgument());
+ if (error_code != 0) {
+ error.SetError(error_code, eErrorTypePOSIX);
+ return error;
+ }
+ break;
+ }
+
+ case FileAction::eFileActionNone:
+ default:
+ if (log)
+ log->Printf("%s(): unsupported file action %u", __FUNCTION__,
+ action.GetAction());
+ break;
+ }
+
+ return error;
+}
+
+static Status PosixSpawnChildForPTraceDebugging(const char *path,
+ ProcessLaunchInfo &launch_info,
+ ::pid_t *pid,
+ cpu_type_t *actual_cpu_type) {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ if (!pid) {
+ error.SetErrorStringWithFormat("%s(): pid arg cannot be null",
+ __FUNCTION__);
+ return error;
+ }
+
+ posix_spawnattr_t attr;
+ short flags;
+ if (log) {
+ StreamString stream;
+ stream.Printf("%s(path='%s',...)\n", __FUNCTION__, path);
+ launch_info.Dump(stream, nullptr);
+ stream.Flush();
+ log->PutCString(stream.GetString().c_str());
+ }
+
+ int error_code;
+ if ((error_code = ::posix_spawnattr_init(&attr)) != 0) {
+ if (log)
+ log->Printf("::posix_spawnattr_init(&attr) failed");
+ error.SetError(error_code, eErrorTypePOSIX);
+ return error;
+ }
+
+ // Ensure we clean up the spawnattr structure however we exit this function.
+ std::unique_ptr<posix_spawnattr_t, int (*)(posix_spawnattr_t *)> spawnattr_up(
+ &attr, ::posix_spawnattr_destroy);
+
+ flags = POSIX_SPAWN_START_SUSPENDED | POSIX_SPAWN_SETSIGDEF |
+ POSIX_SPAWN_SETSIGMASK;
+ if (launch_info.GetFlags().Test(eLaunchFlagDisableASLR))
+ 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);
+
+ if ((error_code = ::posix_spawnattr_setflags(&attr, flags)) != 0) {
+ LLDB_LOG(log,
+ "::posix_spawnattr_setflags(&attr, "
+ "POSIX_SPAWN_START_SUSPENDED{0}) failed: {1}",
+ flags & _POSIX_SPAWN_DISABLE_ASLR ? " | _POSIX_SPAWN_DISABLE_ASLR"
+ : "",
+ llvm::sys::StrError(error_code));
+ error.SetError(error_code, eErrorTypePOSIX);
+ return error;
+ }
+
+#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...
+ cpu_type_t desired_cpu_type = launch_info.GetArchitecture().GetMachOCPUType();
+ if (desired_cpu_type != LLDB_INVALID_CPUTYPE) {
+ size_t ocount = 0;
+ error_code =
+ ::posix_spawnattr_setbinpref_np(&attr, 1, &desired_cpu_type, &ocount);
+ if (error_code != 0) {
+ LLDB_LOG(log,
+ "::posix_spawnattr_setbinpref_np(&attr, 1, "
+ "cpu_type = {0:x8}, count => {1}): {2}",
+ desired_cpu_type, ocount, llvm::sys::StrError(error_code));
+ error.SetError(error_code, eErrorTypePOSIX);
+ return error;
+ }
+ if (ocount != 1) {
+ error.SetErrorStringWithFormat("posix_spawnattr_setbinpref_np "
+ "did not set the expected number "
+ "of cpu_type entries: expected 1 "
+ "but was %zu",
+ ocount);
+ return error;
+ }
+ }
+#endif
+
+ posix_spawn_file_actions_t file_actions;
+ if ((error_code = ::posix_spawn_file_actions_init(&file_actions)) != 0) {
+ LLDB_LOG(log, "::posix_spawn_file_actions_init(&file_actions) failed: {0}",
+ llvm::sys::StrError(error_code));
+ error.SetError(error_code, eErrorTypePOSIX);
+ return error;
+ }
+
+ // Ensure we clean up file actions however we exit this. When the
+ // file_actions_up below goes out of scope, we'll get our file action
+ // cleanup.
+ std::unique_ptr<posix_spawn_file_actions_t,
+ int (*)(posix_spawn_file_actions_t *)>
+ file_actions_up(&file_actions, ::posix_spawn_file_actions_destroy);
+
+ // We assume the caller has setup the file actions appropriately. We are not
+ // in the business of figuring out what we really need here. lldb-server will
+ // have already called FinalizeFileActions() as well to button these up
+ // properly.
+ const size_t num_actions = launch_info.GetNumFileActions();
+ for (size_t action_index = 0; action_index < num_actions; ++action_index) {
+ const FileAction *const action =
+ launch_info.GetFileActionAtIndex(action_index);
+ if (!action)
+ continue;
+
+ error = CreatePosixSpawnFileAction(*action, &file_actions);
+ if (!error.Success()) {
+ if (log)
+ log->Printf("%s(): error converting FileAction to posix_spawn "
+ "file action: %s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
+ }
+
+ // TODO: Verify if we can set the working directory back immediately
+ // after the posix_spawnp call without creating a race condition???
+ const char *const working_directory =
+ launch_info.GetWorkingDirectory().GetCString();
+ if (working_directory && working_directory[0])
+ ::chdir(working_directory);
+
+ auto argv = launch_info.GetArguments().GetArgumentVector();
+ auto envp = launch_info.GetEnvironmentEntries().GetArgumentVector();
+ error_code = ::posix_spawnp(pid, path, &file_actions, &attr,
+ (char *const *)argv, (char *const *)envp);
+ if (error_code != 0) {
+ LLDB_LOG(log,
+ "::posix_spawnp(pid => {0}, path = '{1}', file_actions "
+ "= {2}, attr = {3}, argv = {4}, envp = {5}) failed: {6}",
+ pid, path, &file_actions, &attr, argv, envp,
+ llvm::sys::StrError(error_code));
+ error.SetError(error_code, eErrorTypePOSIX);
+ return error;
+ }
+
+ // Validate we got a pid.
+ if (pid == LLDB_INVALID_PROCESS_ID) {
+ error.SetErrorString("posix_spawn() did not indicate a failure but it "
+ "failed to return a pid, aborting.");
+ return error;
+ }
+
+ if (actual_cpu_type) {
+ *actual_cpu_type = GetCPUTypeForLocalProcess(*pid);
+ if (log)
+ log->Printf("%s(): cpu type for launched process pid=%i: "
+ "cpu_type=0x%8.8x",
+ __FUNCTION__, *pid, *actual_cpu_type);
+ }
+
+ return error;
+}
+
+Status LaunchInferior(ProcessLaunchInfo &launch_info, int *pty_master_fd,
+ LaunchFlavor *launch_flavor) {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ if (!launch_flavor) {
+ error.SetErrorString("mandatory launch_flavor field was null");
+ return error;
+ }
+
+ if (log) {
+ StreamString stream;
+ stream.Printf("NativeProcessDarwin::%s(): launching with the "
+ "following launch info:",
+ __FUNCTION__);
+ launch_info.Dump(stream, nullptr);
+ stream.Flush();
+ log->PutCString(stream.GetString().c_str());
+ }
+
+ // Retrieve the binary name given to us.
+ char given_path[PATH_MAX];
+ given_path[0] = '\0';
+ launch_info.GetExecutableFile().GetPath(given_path, sizeof(given_path));
+
+ // Determine the manner in which we'll launch.
+ *launch_flavor = g_launch_flavor;
+ if (*launch_flavor == LaunchFlavor::Default) {
+ // Our default launch method is posix spawn
+ *launch_flavor = LaunchFlavor::PosixSpawn;
+#if defined WITH_FBS
+ // Check if we have an app bundle, if so launch using BackBoard Services.
+ if (strstr(given_path, ".app")) {
+ *launch_flavor = eLaunchFlavorFBS;
+ }
+#elif defined WITH_BKS
+ // Check if we have an app bundle, if so launch using BackBoard Services.
+ if (strstr(given_path, ".app")) {
+ *launch_flavor = eLaunchFlavorBKS;
+ }
+#elif defined WITH_SPRINGBOARD
+ // Check if we have an app bundle, if so launch using SpringBoard.
+ if (strstr(given_path, ".app")) {
+ *launch_flavor = eLaunchFlavorSpringBoard;
+ }
+#endif
+ }
+
+ // Attempt to resolve the binary name to an absolute path.
+ char resolved_path[PATH_MAX];
+ resolved_path[0] = '\0';
+
+ if (log)
+ log->Printf("%s(): attempting to resolve given binary path: \"%s\"",
+ __FUNCTION__, given_path);
+
+ // If we fail to resolve the path to our executable, then just use what we
+ // were given and hope for the best
+ if (!ResolveExecutablePath(given_path, resolved_path,
+ sizeof(resolved_path))) {
+ if (log)
+ log->Printf("%s(): failed to resolve binary path, using "
+ "what was given verbatim and hoping for the best",
+ __FUNCTION__);
+ ::strncpy(resolved_path, given_path, sizeof(resolved_path));
+ } else {
+ if (log)
+ log->Printf("%s(): resolved given binary path to: \"%s\"", __FUNCTION__,
+ resolved_path);
+ }
+
+ char launch_err_str[PATH_MAX];
+ launch_err_str[0] = '\0';
+
+ // TODO figure out how to handle QSetProcessEvent
+ // const char *process_event = ctx.GetProcessEvent();
+
+ // Ensure the binary is there.
+ struct stat path_stat;
+ if (::stat(resolved_path, &path_stat) == -1) {
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ // Fork a child process for debugging
+ // state_callback(eStateLaunching);
+
+ const auto argv = launch_info.GetArguments().GetConstArgumentVector();
+ const auto envp =
+ launch_info.GetEnvironmentEntries().GetConstArgumentVector();
+
+ switch (*launch_flavor) {
+ case LaunchFlavor::ForkExec: {
+ ::pid_t pid = LLDB_INVALID_PROCESS_ID;
+ error = ForkChildForPTraceDebugging(resolved_path, argv, envp, &pid,
+ pty_master_fd);
+ if (error.Success()) {
+ launch_info.SetProcessID(static_cast<lldb::pid_t>(pid));
+ } else {
+ // Reset any variables that might have been set during a failed launch
+ // attempt.
+ if (pty_master_fd)
+ *pty_master_fd = -1;
+
+ // We're done.
+ return error;
+ }
+ } break;
+
+#ifdef WITH_FBS
+ case LaunchFlavor::FBS: {
+ 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;
+ 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 LaunchFlavor::BKS: {
+ 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;
+ 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 LaunchFlavor::SpringBoard: {
+ // .../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 LaunchFlavor::PosixSpawn: {
+ ::pid_t pid = LLDB_INVALID_PROCESS_ID;
+
+ // Retrieve paths for stdin/stdout/stderr.
+ cpu_type_t actual_cpu_type = 0;
+ error = PosixSpawnChildForPTraceDebugging(resolved_path, launch_info, &pid,
+ &actual_cpu_type);
+ if (error.Success()) {
+ launch_info.SetProcessID(static_cast<lldb::pid_t>(pid));
+ if (pty_master_fd)
+ *pty_master_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor();
+ } else {
+ // Reset any variables that might have been set during a failed launch
+ // attempt.
+ if (pty_master_fd)
+ *pty_master_fd = -1;
+
+ // We're done.
+ return error;
+ }
+ break;
+ }
+
+ default:
+ // Invalid launch flavor.
+ error.SetErrorStringWithFormat("NativeProcessDarwin::%s(): unknown "
+ "launch flavor %d",
+ __FUNCTION__, (int)*launch_flavor);
+ return error;
+ }
+
+ if (launch_info.GetProcessID() == LLDB_INVALID_PROCESS_ID) {
+ // If we don't have a valid process ID and no one has set the error, then
+ // return a generic error.
+ if (error.Success())
+ error.SetErrorStringWithFormat("%s(): failed to launch, no reason "
+ "specified",
+ __FUNCTION__);
+ }
+
+ // We're done with the launch side of the operation.
+ return error;
+}
+}
+} // namespaces
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/DarwinProcessLauncher.h b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/DarwinProcessLauncher.h
new file mode 100644
index 000000000000..0e65b56a143e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/DarwinProcessLauncher.h
@@ -0,0 +1,48 @@
+//===-- DarwinProcessLauncher.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 DarwinProcessLauncher_h
+#define DarwinProcessLauncher_h
+
+// C headers
+#include <mach/machine.h>
+#include <sys/types.h>
+
+// C++ headers
+#include <functional>
+
+// LLDB headers
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
+
+#include "LaunchFlavor.h"
+
+namespace lldb_private {
+namespace darwin_process_launcher {
+// =============================================================================
+/// Launches a process for debugging.
+///
+/// \param[inout] launch_info
+/// Specifies details about the process to launch (e.g. path, architecture,
+/// etc.). On output, includes the launched ProcessID (pid).
+///
+/// \param[out] pty_master_fd
+/// Returns the master side of the pseudo-terminal used to communicate
+/// with stdin/stdout from the launched process. May be nullptr.
+///
+/// \param[out] launch_flavor
+/// Contains the launch flavor used when launching the process.
+// =============================================================================
+Status
+LaunchInferior(ProcessLaunchInfo &launch_info, int *pty_master_fd,
+ lldb_private::process_darwin::LaunchFlavor *launch_flavor);
+
+} // darwin_process_launcher
+} // lldb_private
+
+#endif /* DarwinProcessLauncher_h */
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/LaunchFlavor.h b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/LaunchFlavor.h
new file mode 100644
index 000000000000..cfd76d1b9c3c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/LaunchFlavor.h
@@ -0,0 +1,32 @@
+//===-- LaunchFlavor.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 LaunchFlavor_h
+#define LaunchFlavor_h
+
+namespace lldb_private {
+namespace process_darwin {
+
+enum class LaunchFlavor {
+ Default = 0,
+ PosixSpawn = 1,
+ ForkExec = 2,
+#ifdef WITH_SPRINGBOARD
+ SpringBoard = 3,
+#endif
+#ifdef WITH_BKS
+ BKS = 4,
+#endif
+#ifdef WITH_FBS
+ FBS = 5
+#endif
+};
+}
+} // namespaces
+
+#endif /* LaunchFlavor_h */
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/MachException.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/MachException.cpp
new file mode 100644
index 000000000000..70ad6736a748
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/MachException.cpp
@@ -0,0 +1,504 @@
+//===-- 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"
+
+// C includes
+#include <errno.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+
+// C++ includes
+#include <mutex>
+
+// LLDB includes
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_darwin;
+
+// 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);
+
+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) {
+ // TODO change to LIBLLDB_LOG_EXCEPTION
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+ if (log) {
+ log->Printf("::%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) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+ if (log) {
+ log->Printf("::%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) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+ if (log) {
+ log->Printf("::%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));
+ }
+
+ 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->exc_data.resize(exc_data_count);
+ ::memcpy(&g_message->exc_data[0], exc_data,
+ g_message->exc_data.size() * sizeof(mach_exception_data_type_t));
+ return KERN_SUCCESS;
+ }
+ return KERN_FAILURE;
+}
+
+bool MachException::Data::GetStopInfo(struct ThreadStopInfo *stop_info,
+ const UnixSignals &signals,
+ Stream &stream) const {
+ if (!stop_info)
+ return false;
+
+ // Zero out the structure.
+ memset(stop_info, 0, sizeof(struct ThreadStopInfo));
+
+ if (exc_type == 0) {
+ stop_info->reason = eStopReasonInvalid;
+ return true;
+ }
+
+ // We always stop with a mach exception.
+ stop_info->reason = eStopReasonException;
+ // 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);
+ if (exc_name)
+ stream.Printf("%s", exc_name);
+ else
+ stream.Printf("%i", exc_type);
+
+ stop_info->details.exception.data_count = exc_data.size();
+
+ int soft_signal = SoftSignal();
+ if (soft_signal) {
+ const char *sig_str = signals.GetSignalAsCString(soft_signal);
+ stream.Printf(" EXC_SOFT_SIGNAL( %i ( %s ))", soft_signal,
+ sig_str ? sig_str : "unknown signal");
+ } else {
+ // No special disassembly for exception data, just print it.
+ size_t idx;
+ stream.Printf(" data[%llu] = {",
+ (uint64_t)stop_info->details.exception.data_count);
+
+ for (idx = 0; idx < stop_info->details.exception.data_count; ++idx) {
+ stream.Printf(
+ "0x%llx%c", (uint64_t)exc_data[idx],
+ ((idx + 1 == stop_info->details.exception.data_count) ? '}' : ','));
+ }
+ }
+
+ // Copy the exception data
+ for (size_t i = 0; i < stop_info->details.exception.data_count; i++)
+ stop_info->details.exception.data[i] = exc_data[i];
+
+ return true;
+}
+
+Status MachException::Message::Receive(mach_port_t port,
+ mach_msg_option_t options,
+ mach_msg_timeout_t timeout,
+ mach_port_t notify_port) {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+
+ mach_msg_timeout_t mach_msg_timeout =
+ options & MACH_RCV_TIMEOUT ? timeout : 0;
+ if (log && ((options & MACH_RCV_TIMEOUT) == 0)) {
+ // Dump this log message if we have no timeout in case it never returns
+ log->Printf("::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);
+ }
+
+ mach_msg_return_t mach_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);
+ error.SetError(mach_err, eErrorTypeMachKernel);
+
+ // Dump any errors we get
+ if (error.Fail() && log) {
+ log->Printf("::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 = %lu, rcv_name "
+ "= %#x, timeout = %u, notify = %#x) failed: %s",
+ 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,
+ error.AsCString());
+ }
+ return error;
+}
+
+void MachException::Message::Dump(Stream &stream) const {
+ stream.Printf(" 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 }\n",
+ 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);
+
+ stream.Printf(" 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);
+}
+
+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 {
+ Log *log(
+ GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+ if (log)
+ log->Printf("MachException::Message::%s(): mach_exc_server "
+ "returned zero...",
+ __FUNCTION__);
+ }
+ g_message = NULL;
+ return success;
+}
+
+Status MachException::Message::Reply(::pid_t inferior_pid, task_t inferior_task,
+ int signal) {
+ // Reply to the exception...
+ Status error;
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+
+ // 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 (inferior_task == state.task_port) {
+ // This is our task, so we can update the signal to send to it
+ state_pid = inferior_pid;
+ soft_signal = signal;
+ } else {
+ auto mach_err = ::pid_for_task(state.task_port, &state_pid);
+ if (mach_err) {
+ error.SetError(mach_err, eErrorTypeMachKernel);
+ if (log)
+ log->Printf("MachException::Message::%s(): pid_for_task() "
+ "failed: %s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
+ }
+
+ lldbassert(state_pid != -1);
+ if (state_pid != -1) {
+ errno = 0;
+ caddr_t thread_port_caddr = (caddr_t)(uintptr_t)state.thread_port;
+ if (::ptrace(PT_THUPDATE, state_pid, thread_port_caddr, soft_signal) != 0)
+ error.SetError(errno, eErrorTypePOSIX);
+
+ if (!error.Success()) {
+ if (log)
+ log->Printf("::ptrace(request = PT_THUPDATE, pid = "
+ "0x%4.4x, tid = 0x%4.4x, signal = %i)",
+ state_pid, state.thread_port, soft_signal);
+ return error;
+ }
+ }
+ }
+
+ if (log)
+ log->Printf("::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);
+
+ auto mach_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 (mach_err)
+ error.SetError(mach_err, eErrorTypeMachKernel);
+
+ // Log our error if we have one.
+ if (error.Fail() && log) {
+ if (error.GetError() == MACH_SEND_INTERRUPTED) {
+ log->PutCString("::mach_msg() - send interrupted");
+ // TODO: keep retrying to reply???
+ } else if (state.task_port == inferior_task) {
+ log->Printf("mach_msg(): returned an error when replying "
+ "to a mach exception: error = %u (%s)",
+ error.GetError(), error.AsCString());
+ } else {
+ log->Printf("::mach_msg() - failed (child of task): %u (%s)",
+ error.GetError(), error.AsCString());
+ }
+ }
+
+ return error;
+}
+
+#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_MACHINE)
+
+// Don't listen for EXC_RESOURCE, it should really get handled by the system
+// handler.
+
+#ifndef EXC_RESOURCE
+#define EXC_RESOURCE 11
+#endif
+
+#ifndef EXC_MASK_RESOURCE
+#define EXC_MASK_RESOURCE (1 << EXC_RESOURCE)
+#endif
+
+#define LLDB_EXC_MASK (EXC_MASK_ALL & ~EXC_MASK_RESOURCE)
+
+Status MachException::PortInfo::Save(task_t task) {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+
+ if (log)
+ log->Printf("MachException::PortInfo::%s(task = 0x%4.4x)", __FUNCTION__,
+ 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
+ mask = LLDB_EXC_MASK;
+
+ count = (sizeof(ports) / sizeof(ports[0]));
+ auto mach_err = ::task_get_exception_ports(task, mask, masks, &count, ports,
+ behaviors, flavors);
+ if (mach_err)
+ error.SetError(mach_err, eErrorTypeMachKernel);
+
+ if (log) {
+ if (error.Success()) {
+ log->Printf("::task_get_exception_ports(task = 0x%4.4x, mask = "
+ "0x%x, maskCnt => %u, ports, behaviors, flavors)",
+ task, mask, count);
+ } else {
+ log->Printf("::task_get_exception_ports(task = 0x%4.4x, mask = 0x%x, "
+ "maskCnt => %u, ports, behaviors, flavors) error: %u (%s)",
+ task, mask, count, error.GetError(), error.AsCString());
+ }
+ }
+
+ if ((error.GetError() == KERN_INVALID_ARGUMENT) &&
+ (mask != PREV_EXC_MASK_ALL)) {
+ mask = PREV_EXC_MASK_ALL;
+ count = (sizeof(ports) / sizeof(ports[0]));
+ mach_err = ::task_get_exception_ports(task, mask, masks, &count, ports,
+ behaviors, flavors);
+ error.SetError(mach_err, eErrorTypeMachKernel);
+ if (log) {
+ if (error.Success()) {
+ log->Printf("::task_get_exception_ports(task = 0x%4.4x, "
+ "mask = 0x%x, maskCnt => %u, ports, behaviors, "
+ "flavors)",
+ task, mask, count);
+ } else {
+ log->Printf("::task_get_exception_ports(task = 0x%4.4x, mask = "
+ "0x%x, maskCnt => %u, ports, behaviors, flavors) "
+ "error: %u (%s)",
+ task, mask, count, error.GetError(), error.AsCString());
+ }
+ }
+ }
+ if (error.Fail()) {
+ mask = 0;
+ count = 0;
+ }
+ return error;
+}
+
+Status MachException::PortInfo::Restore(task_t task) {
+ Status error;
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+
+ if (log)
+ log->Printf("MachException::PortInfo::Restore(task = 0x%4.4x)", task);
+
+ uint32_t i = 0;
+ if (count > 0) {
+ for (i = 0; i < count; i++) {
+ auto mach_err = ::task_set_exception_ports(task, masks[i], ports[i],
+ behaviors[i], flavors[i]);
+ if (mach_err)
+ error.SetError(mach_err, eErrorTypeMachKernel);
+ if (log) {
+ if (error.Success()) {
+ log->Printf("::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]);
+ } else {
+ log->Printf("::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): "
+ "error %u (%s)",
+ task, masks[i], ports[i], behaviors[i], flavors[i],
+ error.GetError(), error.AsCString());
+ }
+ }
+
+ // Bail if we encounter any errors
+ if (error.Fail())
+ break;
+ }
+ }
+
+ count = 0;
+ return error;
+}
+
+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
+ default:
+ break;
+ }
+ return NULL;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/MachException.h b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/MachException.h
new file mode 100644
index 000000000000..18e49173b020
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/MachException.h
@@ -0,0 +1,139 @@
+//===-- 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>
+
+#include "lldb/Host/Debug.h"
+#include "lldb/lldb-private-forward.h"
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+namespace process_darwin {
+
+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;
+
+ Status Save(task_t task);
+
+ Status 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));
+ }
+
+ bool GetStopInfo(ThreadStopInfo *stop_info, const UnixSignals &signals,
+ Stream &stream) 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);
+
+ Status Reply(::pid_t inferior_pid, task_t inferior_task, int signal);
+
+ Status Receive(mach_port_t receive_port, mach_msg_option_t options,
+ mach_msg_timeout_t timeout,
+ mach_port_t notify_port = MACH_PORT_NULL);
+
+ void Dump(Stream &stream) const;
+
+ 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);
+};
+
+} // namespace process_darwin
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.cpp
new file mode 100644
index 000000000000..fe7de27e0ee6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.cpp
@@ -0,0 +1,1541 @@
+//===-- NativeProcessDarwin.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 "NativeProcessDarwin.h"
+
+// C includes
+#include <mach/mach_init.h>
+#include <mach/mach_traps.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+// C++ includes
+// LLDB includes
+#include "lldb/Host/PseudoTerminal.h"
+#include "lldb/Target/ProcessLaunchInfo.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "CFBundle.h"
+#include "CFString.h"
+#include "DarwinProcessLauncher.h"
+
+#include "MachException.h"
+
+#include "llvm/Support/FileSystem.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_darwin;
+using namespace lldb_private::darwin_process_launcher;
+
+// Hidden Impl
+
+namespace {
+struct hack_task_dyld_info {
+ mach_vm_address_t all_image_info_addr;
+ mach_vm_size_t all_image_info_size;
+};
+}
+
+// Public Static Methods
+
+Status NativeProcessProtocol::Launch(
+ ProcessLaunchInfo &launch_info,
+ NativeProcessProtocol::NativeDelegate &native_delegate, MainLoop &mainloop,
+ NativeProcessProtocolSP &native_process_sp) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ Status error;
+
+ // Verify the working directory is valid if one was specified.
+ FileSpec working_dir(launch_info.GetWorkingDirectory());
+ if (working_dir) {
+ FileInstance::Instance().Resolve(working_dir);
+ if (!FileSystem::Instance().IsDirectory(working_dir)) {
+ error.SetErrorStringWithFormat("No such file or directory: %s",
+ working_dir.GetCString());
+ return error;
+ }
+ }
+
+ // Launch the inferior.
+ int pty_master_fd = -1;
+ LaunchFlavor launch_flavor = LaunchFlavor::Default;
+
+ error = LaunchInferior(launch_info, &pty_master_fd, &launch_flavor);
+
+ // Handle launch failure.
+ if (!error.Success()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s() failed to launch process: "
+ "%s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
+
+ // Handle failure to return a pid.
+ if (launch_info.GetProcessID() == LLDB_INVALID_PROCESS_ID) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s() launch succeeded but no "
+ "pid was returned! Aborting.",
+ __FUNCTION__);
+ return error;
+ }
+
+ // Create the Darwin native process impl.
+ std::shared_ptr<NativeProcessDarwin> np_darwin_sp(
+ new NativeProcessDarwin(launch_info.GetProcessID(), pty_master_fd));
+ if (!np_darwin_sp->RegisterNativeDelegate(native_delegate)) {
+ native_process_sp.reset();
+ error.SetErrorStringWithFormat("failed to register the native delegate");
+ return error;
+ }
+
+ // Finalize the processing needed to debug the launched process with a
+ // NativeProcessDarwin instance.
+ error = np_darwin_sp->FinalizeLaunch(launch_flavor, mainloop);
+ if (!error.Success()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s() aborting, failed to finalize"
+ " the launching of the process: %s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
+
+ // Return the process and process id to the caller through the launch args.
+ native_process_sp = np_darwin_sp;
+ return error;
+}
+
+Status NativeProcessProtocol::Attach(
+ lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate,
+ MainLoop &mainloop, NativeProcessProtocolSP &native_process_sp) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(pid = %" PRIi64 ")", __FUNCTION__,
+ pid);
+
+ // Retrieve the architecture for the running process.
+ ArchSpec process_arch;
+ Status error = ResolveProcessArchitecture(pid, process_arch);
+ if (!error.Success())
+ return error;
+
+ // TODO get attach to return this value.
+ const int pty_master_fd = -1;
+ std::shared_ptr<NativeProcessDarwin> native_process_darwin_sp(
+ new NativeProcessDarwin(pid, pty_master_fd));
+
+ if (!native_process_darwin_sp->RegisterNativeDelegate(native_delegate)) {
+ error.SetErrorStringWithFormat("failed to register the native "
+ "delegate");
+ return error;
+ }
+
+ native_process_darwin_sp->AttachToInferior(mainloop, pid, error);
+ if (!error.Success())
+ return error;
+
+ native_process_sp = native_process_darwin_sp;
+ return error;
+}
+
+// ctor/dtor
+
+NativeProcessDarwin::NativeProcessDarwin(lldb::pid_t pid, int pty_master_fd)
+ : NativeProcessProtocol(pid), m_task(TASK_NULL), m_did_exec(false),
+ m_cpu_type(0), m_exception_port(MACH_PORT_NULL), m_exc_port_info(),
+ m_exception_thread(nullptr), m_exception_messages_mutex(),
+ m_sent_interrupt_signo(0), m_auto_resume_signo(0), m_thread_list(),
+ m_thread_actions(), m_waitpid_pipe(), m_waitpid_thread(nullptr),
+ m_waitpid_reader_handle() {
+ // TODO add this to the NativeProcessProtocol constructor.
+ m_terminal_fd = pty_master_fd;
+}
+
+NativeProcessDarwin::~NativeProcessDarwin() {}
+
+// Instance methods
+
+Status NativeProcessDarwin::FinalizeLaunch(LaunchFlavor launch_flavor,
+ MainLoop &main_loop) {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ error = StartExceptionThread();
+ if (!error.Success()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): failure starting the "
+ "mach exception port monitor thread: %s",
+ __FUNCTION__, error.AsCString());
+
+ // Terminate the inferior process. There's nothing meaningful we can do if
+ // we can't receive signals and exceptions. Since we launched the process,
+ // it's fair game for us to kill it.
+ ::ptrace(PT_KILL, m_pid, 0, 0);
+ SetState(eStateExited);
+
+ return error;
+ }
+
+ StartSTDIOThread();
+
+ if (launch_flavor == LaunchFlavor::PosixSpawn) {
+ SetState(eStateAttaching);
+ errno = 0;
+ int err = ::ptrace(PT_ATTACHEXC, m_pid, 0, 0);
+ if (err == 0) {
+ // m_flags |= eMachProcessFlagsAttached;
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): successfully spawned "
+ "process with pid %" PRIu64,
+ __FUNCTION__, m_pid);
+ } else {
+ error.SetErrorToErrno();
+ SetState(eStateExited);
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): error: failed to "
+ "attach to spawned pid %" PRIu64 " (error=%d (%s))",
+ __FUNCTION__, m_pid, (int)error.GetError(),
+ error.AsCString());
+ return error;
+ }
+ }
+
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): new pid is %" PRIu64 "...",
+ __FUNCTION__, m_pid);
+
+ // Spawn a thread to reap our child inferior process...
+ error = StartWaitpidThread(main_loop);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): failed to start waitpid() "
+ "thread: %s",
+ __FUNCTION__, error.AsCString());
+ kill(SIGKILL, static_cast<::pid_t>(m_pid));
+ return error;
+ }
+
+ if (TaskPortForProcessID(error) == 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.
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): could not get task port "
+ "for process, sending SIGKILL and exiting: %s",
+ __FUNCTION__, error.AsCString());
+ kill(SIGKILL, static_cast<::pid_t>(m_pid));
+ return error;
+ }
+
+ // Indicate that we're stopped, as we always launch suspended.
+ SetState(eStateStopped);
+
+ // Success.
+ return error;
+}
+
+Status NativeProcessDarwin::SaveExceptionPortInfo() {
+ return m_exc_port_info.Save(m_task);
+}
+
+bool NativeProcessDarwin::ProcessUsingSpringBoard() const {
+ // TODO implement flags
+ // return (m_flags & eMachProcessFlagsUsingSBS) != 0;
+ return false;
+}
+
+bool NativeProcessDarwin::ProcessUsingBackBoard() const {
+ // TODO implement flags
+ // return (m_flags & eMachProcessFlagsUsingBKS) != 0;
+ 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 NativeProcessDarwin::ExceptionMessageReceived(
+ const MachException::Message &message) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+
+ std::lock_guard<std::recursive_mutex> locker(m_exception_messages_mutex);
+ if (m_exception_messages.empty()) {
+ // Suspend the task the moment we receive our first exception message.
+ SuspendTask();
+ }
+
+ // 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(message);
+
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): new queued message count: %lu",
+ __FUNCTION__, m_exception_messages.size());
+}
+
+void *NativeProcessDarwin::ExceptionThread(void *arg) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+ if (!arg) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): cannot run mach exception "
+ "thread, mandatory process arg was null",
+ __FUNCTION__);
+ return nullptr;
+ }
+
+ return reinterpret_cast<NativeProcessDarwin *>(arg)->DoExceptionThread();
+}
+
+void *NativeProcessDarwin::DoExceptionThread() {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(arg=%p) starting thread...",
+ __FUNCTION__, this);
+
+ pthread_setname_np("exception monitoring thread");
+
+ // Ensure we don't get CPU starved.
+ MaybeRaiseThreadPriority();
+
+ // 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.
+ //
+ // [did we lose some words here?]
+ //
+ // flag set in the options, so we will wait forever for an exception on
+ // 0 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.
+ //
+ // We choose to park a thread on this, rather than polling, because the
+ // polling is expensive. On devices, we need to minimize overhead caused by
+ // the process monitor.
+ uint32_t num_exceptions_received = 0;
+ Status error;
+ task_t task = m_task;
+ 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 = (::pid_t)process->GetID();
+ CFReleaser<SBSWatchdogAssertionRef> watchdog;
+
+ if (process->ProcessUsingSpringBoard()) {
+ // Request a renewal for every 60 seconds if we attached using SpringBoard.
+ watchdog.reset(::SBSWatchdogAssertionCreateForPID(nullptr, pid, 60));
+ if (log)
+ log->Printf("::SBSWatchdogAssertionCreateForPID(NULL, %4.4x, 60) "
+ "=> %p",
+ pid, watchdog.get());
+
+ if (watchdog.get()) {
+ ::SBSWatchdogAssertionRenew(watchdog.get());
+
+ CFTimeInterval watchdogRenewalInterval =
+ ::SBSWatchdogAssertionGetRenewalInterval(watchdog.get());
+ if (log)
+ log->Printf("::SBSWatchdogAssertionGetRenewalInterval(%p) => "
+ "%g seconds",
+ watchdog.get(), watchdogRenewalInterval);
+ if (watchdogRenewalInterval > 0.0) {
+ watchdog_timeout = (mach_msg_timeout_t)watchdogRenewalInterval * 1000;
+ if (watchdog_timeout > 3000) {
+ // Give us a second to renew our timeout.
+ watchdog_timeout -= 1000;
+ } else if (watchdog_timeout > 1000) {
+ // Give us a quarter of a second to renew our timeout.
+ watchdog_timeout -= 250;
+ }
+ }
+ }
+ 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 (process->ProcessUsingBackBoard()) {
+ ::pid_t pid = process->GetID();
+ CFAllocatorRef alloc = kCFAllocatorDefault;
+ watchdog.reset(::BKSWatchdogAssertionCreateForPID(alloc, pid));
+ }
+#endif // #ifdef WITH_BKS
+
+ // Do we want to use a weak pointer to the NativeProcessDarwin here, in which
+ // case we can guarantee we don't whack the process monitor if we race
+ // between this thread and the main one on shutdown?
+ while (IsExceptionPortValid()) {
+ ::pthread_testcancel();
+
+ MachException::Message exception_message;
+
+ if (num_exceptions_received > 0) {
+ // We don't want a timeout here, just receive as many exceptions as we
+ // can since we already have one. We want to get all currently available
+ // exceptions for this task at once.
+ error = exception_message.Receive(
+ GetExceptionPort(),
+ MACH_RCV_MSG | MACH_RCV_INTERRUPT | MACH_RCV_TIMEOUT, 0);
+ } 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).
+ error = exception_message.Receive(GetExceptionPort(),
+ 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.
+ error = exception_message.Receive(GetExceptionPort(),
+ MACH_RCV_MSG | MACH_RCV_INTERRUPT, 0);
+ }
+
+ if (error.Success()) {
+ // We successfully received an exception.
+ if (exception_message.CatchExceptionRaise(task)) {
+ ++num_exceptions_received;
+ ExceptionMessageReceived(exception_message);
+ }
+ } else {
+ if (error.GetError() == MACH_RCV_INTERRUPTED) {
+ // We were interrupted.
+
+ // If we have no task port we should exit this thread, as it implies
+ // the inferior went down.
+ if (!IsExceptionPortValid()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): the inferior "
+ "exception port is no longer valid, "
+ "canceling exception thread...",
+ __FUNCTION__);
+ // Should we be setting a process state here?
+ break;
+ }
+
+ // Make sure the inferior task is still valid.
+ if (IsTaskValid()) {
+ // Task is still ok.
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): interrupted, but "
+ "the inferior task iss till valid, "
+ "continuing...",
+ __FUNCTION__);
+ continue;
+ } else {
+ // The inferior task is no longer valid. Time to exit as the process
+ // has gone away.
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): the inferior task "
+ "has exited, and so will we...",
+ __FUNCTION__);
+ // Does this race at all with our waitpid()?
+ SetState(eStateExited);
+ break;
+ }
+ } else if (error.GetError() == MACH_RCV_TIMED_OUT) {
+ // We timed out when waiting for exceptions.
+
+ 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. Get the possibly updated task port back from the
+ // process in case we exec'ed and our task port changed.
+ task = ExceptionMessageBundleComplete();
+
+ // In case we use a timeout value when getting exceptions, make sure
+ // our task is still valid.
+ if (IsTaskValid(task)) {
+ // Task is still ok.
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): got a timeout, "
+ "continuing...",
+ __FUNCTION__);
+ continue;
+ } else {
+ // The inferior task is no longer valid. Time to exit as the
+ // process has gone away.
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): the inferior "
+ "task has exited, and so will we...",
+ __FUNCTION__);
+ // Does this race at all with our waitpid()?
+ SetState(eStateExited);
+ break;
+ }
+ }
+
+#if defined(WITH_SPRINGBOARD) && !defined(WITH_BKS)
+ if (watchdog.get()) {
+ watchdog_elapsed += periodic_timeout;
+ if (watchdog_elapsed >= watchdog_timeout) {
+ if (log)
+ log->Printf("SBSWatchdogAssertionRenew(%p)", watchdog.get());
+ ::SBSWatchdogAssertionRenew(watchdog.get());
+ watchdog_elapsed = 0;
+ }
+ }
+#endif
+ } else {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): continuing after "
+ "receiving an unexpected error: %u (%s)",
+ __FUNCTION__, error.GetError(), error.AsCString());
+ // TODO: notify of error?
+ }
+ }
+ }
+
+#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)
+
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(%p): thread exiting...", __FUNCTION__,
+ this);
+ return nullptr;
+}
+
+Status NativeProcessDarwin::StartExceptionThread() {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("NativeProcessDarwin::%s() called", __FUNCTION__);
+
+ // Make sure we've looked up the inferior port.
+ TaskPortForProcessID(error);
+
+ // Ensure the inferior task is valid.
+ if (!IsTaskValid()) {
+ error.SetErrorStringWithFormat("cannot start exception thread: "
+ "task 0x%4.4x is not valid",
+ m_task);
+ return error;
+ }
+
+ // Get the mach port for the process monitor.
+ mach_port_t task_self = mach_task_self();
+
+ // Allocate an exception port that we will use to track our child process
+ auto mach_err = ::mach_port_allocate(task_self, MACH_PORT_RIGHT_RECEIVE,
+ &m_exception_port);
+ error.SetError(mach_err, eErrorTypeMachKernel);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): mach_port_allocate("
+ "task_self=0x%4.4x, MACH_PORT_RIGHT_RECEIVE, "
+ "&m_exception_port) failed: %u (%s)",
+ __FUNCTION__, task_self, error.GetError(), error.AsCString());
+ return error;
+ }
+
+ // Add the ability to send messages on the new exception port
+ mach_err = ::mach_port_insert_right(
+ task_self, m_exception_port, m_exception_port, MACH_MSG_TYPE_MAKE_SEND);
+ error.SetError(mach_err, eErrorTypeMachKernel);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): mach_port_insert_right("
+ "task_self=0x%4.4x, m_exception_port=0x%4.4x, "
+ "m_exception_port=0x%4.4x, MACH_MSG_TYPE_MAKE_SEND) "
+ "failed: %u (%s)",
+ __FUNCTION__, task_self, m_exception_port, m_exception_port,
+ error.GetError(), error.AsCString());
+ return error;
+ }
+
+ // Save the original state of the exception ports for our child process.
+ error = SaveExceptionPortInfo();
+ if (error.Fail() || (m_exc_port_info.mask == 0)) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): SaveExceptionPortInfo() "
+ "failed, cannot install exception handler: %s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
+
+ // Set the ability to get all exceptions on this port.
+ mach_err = ::task_set_exception_ports(
+ m_task, m_exc_port_info.mask, m_exception_port,
+ EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE);
+ error.SetError(mach_err, eErrorTypeMachKernel);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("::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) failed: "
+ "%u (%s)",
+ m_task, m_exc_port_info.mask, m_exception_port,
+ (EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES), THREAD_STATE_NONE,
+ error.GetError(), error.AsCString());
+ return error;
+ }
+
+ // Create the exception thread.
+ auto pthread_err =
+ ::pthread_create(&m_exception_thread, nullptr, ExceptionThread, this);
+ error.SetError(pthread_err, eErrorTypePOSIX);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): failed to create Mach "
+ "exception-handling thread: %u (%s)",
+ __FUNCTION__, error.GetError(), error.AsCString());
+ }
+
+ return error;
+}
+
+lldb::addr_t
+NativeProcessDarwin::GetDYLDAllImageInfosAddress(Status &error) const {
+ error.Clear();
+
+ 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));
+ }
+
+ TaskPortForProcessID(error);
+ if (error.Fail())
+ return LLDB_INVALID_ADDRESS;
+
+ auto mach_err =
+ ::task_info(m_task, TASK_DYLD_INFO, (task_info_t)&dyld_info, &count);
+ error.SetError(mach_err, eErrorTypeMachKernel);
+ if (error.Success()) {
+ // We now have the address of the all image infos structure.
+ return dyld_info.all_image_info_addr;
+ }
+
+ // We don't have it.
+ return LLDB_INVALID_ADDRESS;
+}
+
+uint32_t NativeProcessDarwin::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;
+}
+
+uint32_t NativeProcessDarwin::GetCPUType() const {
+ if (m_cpu_type == 0 && m_pid != 0)
+ m_cpu_type = GetCPUTypeForLocalProcess(m_pid);
+ return m_cpu_type;
+}
+
+task_t NativeProcessDarwin::ExceptionMessageBundleComplete() {
+ // We have a complete bundle of exceptions for our child process.
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+
+ std::lock_guard<std::recursive_mutex> locker(m_exception_messages_mutex);
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): processing %lu exception "
+ "messages.",
+ __FUNCTION__, m_exception_messages.size());
+
+ if (m_exception_messages.empty()) {
+ // Not particularly useful...
+ return m_task;
+ }
+
+ bool auto_resume = false;
+ m_did_exec = false;
+
+ // First check for any SIGTRAP and make sure we didn't exec
+ const task_t task = m_task;
+ 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) {
+ // This is an exception that is not for our inferior, ignore.
+ continue;
+ }
+
+ // This is an exception for the inferior.
+ ++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 addr_t aii_addr = GetDYLDAllImageInfosAddress(error);
+ if (aii_addr == LLDB_INVALID_ADDRESS)
+ break;
+
+ const addr_t info_array_count_addr = aii_addr + 4;
+ uint32_t info_array_count = 0;
+ size_t bytes_read = 0;
+ Status read_error;
+ read_error = ReadMemory(info_array_count_addr, // source addr
+ &info_array_count, // dest addr
+ 4, // byte count
+ bytes_read); // #bytes read
+ if (read_error.Success() && (bytes_read == 4)) {
+ if (info_array_count == 0) {
+ // We got the all infos address, and there are zero entries. We
+ // think we exec'd.
+ m_did_exec = true;
+
+ // Force the task port to update itself in case the task port
+ // changed after exec
+ const task_t old_task = m_task;
+ const bool force_update = true;
+ const task_t new_task = TaskPortForProcessID(error, force_update);
+ if (old_task != new_task) {
+ if (log)
+ log->Printf("exec: inferior task port changed "
+ "from 0x%4.4x to 0x%4.4x",
+ old_task, new_task);
+ }
+ }
+ } else {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s() warning: "
+ "failed to read all_image_infos."
+ "infoArrayCount from 0x%8.8llx",
+ __FUNCTION__, info_array_count_addr);
+ }
+ } else if ((m_sent_interrupt_signo != 0) &&
+ (signo == m_sent_interrupt_signo)) {
+ // We just received the interrupt that we sent to ourselves.
+ received_interrupt = true;
+ }
+ }
+
+ if (m_did_exec) {
+ cpu_type_t process_cpu_type = GetCPUTypeForLocalProcess(m_pid);
+ if (m_cpu_type != process_cpu_type) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): arch changed from "
+ "0x%8.8x to 0x%8.8x",
+ __FUNCTION__, m_cpu_type, process_cpu_type);
+ m_cpu_type = process_cpu_type;
+ // TODO figure out if we need to do something here.
+ // DNBArchProtocol::SetArchitecture (process_cpu_type);
+ }
+ m_thread_list.Clear();
+
+ // TODO hook up breakpoints.
+ // m_breakpoints.DisableAll();
+ }
+
+ if (m_sent_interrupt_signo != 0) {
+ if (received_interrupt) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): process "
+ "successfully interrupted with signal %i",
+ __FUNCTION__, m_sent_interrupt_signo);
+
+ // Mark that we received the interrupt signal
+ m_sent_interrupt_signo = 0;
+ // Now check if we had a case where:
+ // 1 - We called NativeProcessDarwin::Interrupt() but we stopped
+ // for another reason.
+ // 2 - We called NativeProcessDarwin::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;
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): auto "
+ "resuming due to unhandled interrupt "
+ "signal %i",
+ __FUNCTION__, m_auto_resume_signo);
+ }
+ m_auto_resume_signo = 0;
+ }
+ } else {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): didn't get signal "
+ "%i after MachProcess::Interrupt()",
+ __FUNCTION__, 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);
+
+ // Let each thread know of any exceptions
+ for (i = 0; i < m_exception_messages.size(); ++i) {
+ // Let the thread list forward all exceptions on down to each thread.
+ if (m_exception_messages[i].state.task_port == task) {
+ // This exception is for our inferior.
+ m_thread_list.NotifyException(m_exception_messages[i].state);
+ }
+
+ if (log) {
+ StreamString stream;
+ m_exception_messages[i].Dump(stream);
+ stream.Flush();
+ log->PutCString(stream.GetString().c_str());
+ }
+ }
+
+ if (log) {
+ StreamString stream;
+ m_thread_list.Dump(stream);
+ stream.Flush();
+ log->PutCString(stream.GetString().c_str());
+ }
+
+ bool step_more = false;
+ if (m_thread_list.ShouldStop(step_more) && (auto_resume == false)) {
+// TODO - need to hook up event system here. !!!!
+#if 0
+ // 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);
+#endif
+ SetState(eStateStopped);
+ } else {
+ // Resume without checking our current state.
+ PrivateResume();
+ }
+
+ return m_task;
+}
+
+void NativeProcessDarwin::StartSTDIOThread() {
+ // TODO implement
+}
+
+Status NativeProcessDarwin::StartWaitpidThread(MainLoop &main_loop) {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ // Strategy: create a thread that sits on waitpid(), waiting for the inferior
+ // process to die, reaping it in the process. Arrange for the thread to have
+ // a pipe file descriptor that it can send a byte over when the waitpid
+ // completes. Have the main loop have a read object for the other side of
+ // the pipe, and have the callback for the read do the process termination
+ // message sending.
+
+ // Create a single-direction communication channel.
+ const bool child_inherits = false;
+ error = m_waitpid_pipe.CreateNew(child_inherits);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): failed to create waitpid "
+ "communication pipe: %s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
+
+ // Hook up the waitpid reader callback.
+
+ // TODO make PipePOSIX derive from IOObject. This is goofy here.
+ const bool transfer_ownership = false;
+ auto io_sp = IOObjectSP(
+ new File(m_waitpid_pipe.GetReadFileDescriptor(), transfer_ownership));
+ m_waitpid_reader_handle = main_loop.RegisterReadObject(
+ io_sp, [this](MainLoopBase &) { HandleWaitpidResult(); }, error);
+
+ // Create the thread.
+ auto pthread_err =
+ ::pthread_create(&m_waitpid_thread, nullptr, WaitpidThread, this);
+ error.SetError(pthread_err, eErrorTypePOSIX);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): failed to create waitpid "
+ "handling thread: %u (%s)",
+ __FUNCTION__, error.GetError(), error.AsCString());
+ return error;
+ }
+
+ return error;
+}
+
+void *NativeProcessDarwin::WaitpidThread(void *arg) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (!arg) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): cannot run waitpid "
+ "thread, mandatory process arg was null",
+ __FUNCTION__);
+ return nullptr;
+ }
+
+ return reinterpret_cast<NativeProcessDarwin *>(arg)->DoWaitpidThread();
+}
+
+void NativeProcessDarwin::MaybeRaiseThreadPriority() {
+#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
+}
+
+void *NativeProcessDarwin::DoWaitpidThread() {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ if (m_pid == LLDB_INVALID_PROCESS_ID) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): inferior process ID is "
+ "not set, cannot waitpid on it",
+ __FUNCTION__);
+ return nullptr;
+ }
+
+ // Name the thread.
+ pthread_setname_np("waitpid thread");
+
+ // Ensure we don't get CPU starved.
+ MaybeRaiseThreadPriority();
+
+ Status error;
+ int status = -1;
+
+ while (1) {
+ // Do a waitpid.
+ ::pid_t child_pid = ::waitpid(m_pid, &status, 0);
+ if (child_pid < 0)
+ error.SetErrorToErrno();
+ if (error.Fail()) {
+ if (error.GetError() == EINTR) {
+ // This is okay, we can keep going.
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): waitpid(pid = %" PRIu64
+ ", &status, 0) interrupted, continuing",
+ __FUNCTION__, m_pid);
+ continue;
+ }
+
+ // This error is not okay, abort.
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): waitpid(pid = %" PRIu64
+ ", &status, 0) aborting due to error: %u (%s)",
+ __FUNCTION__, m_pid, error.GetError(), error.AsCString());
+ break;
+ }
+
+ // Log the successful result.
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): waitpid(pid = %" PRIu64
+ ", &status, 0) => %i, status = %i",
+ __FUNCTION__, m_pid, child_pid, status);
+
+ // Handle the result.
+ if (WIFSTOPPED(status)) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): waitpid(pid = %" PRIu64
+ ") received a stop, continuing waitpid() loop",
+ __FUNCTION__, m_pid);
+ continue;
+ } else // if (WIFEXITED(status) || WIFSIGNALED(status))
+ {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(pid = %" PRIu64 "): "
+ "waitpid thread is setting exit status for pid = "
+ "%i to %i",
+ __FUNCTION__, m_pid, child_pid, status);
+
+ error = SendInferiorExitStatusToMainLoop(child_pid, status);
+ return nullptr;
+ }
+ }
+
+ // We should never exit as long as our child process is alive. If we get
+ // here, something completely unexpected went wrong and we should exit.
+ if (log)
+ log->Printf(
+ "NativeProcessDarwin::%s(): internal error: waitpid thread "
+ "exited out of its main loop in an unexpected way. pid = %" PRIu64
+ ". Sending exit status of -1.",
+ __FUNCTION__, m_pid);
+
+ error = SendInferiorExitStatusToMainLoop((::pid_t)m_pid, -1);
+ return nullptr;
+}
+
+Status NativeProcessDarwin::SendInferiorExitStatusToMainLoop(::pid_t pid,
+ int status) {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ size_t bytes_written = 0;
+
+ // Send the pid.
+ error = m_waitpid_pipe.Write(&pid, sizeof(pid), bytes_written);
+ if (error.Fail() || (bytes_written < sizeof(pid))) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s() - failed to write "
+ "waitpid exiting pid to the pipe. Client will not "
+ "hear about inferior exit status!",
+ __FUNCTION__);
+ return error;
+ }
+
+ // Send the status.
+ bytes_written = 0;
+ error = m_waitpid_pipe.Write(&status, sizeof(status), bytes_written);
+ if (error.Fail() || (bytes_written < sizeof(status))) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s() - failed to write "
+ "waitpid exit result to the pipe. Client will not "
+ "hear about inferior exit status!",
+ __FUNCTION__);
+ }
+ return error;
+}
+
+Status NativeProcessDarwin::HandleWaitpidResult() {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ // Read the pid.
+ const bool notify_status = true;
+
+ ::pid_t pid = -1;
+ size_t bytes_read = 0;
+ error = m_waitpid_pipe.Read(&pid, sizeof(pid), bytes_read);
+ if (error.Fail() || (bytes_read < sizeof(pid))) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s() - failed to read "
+ "waitpid exiting pid from the pipe. Will notify "
+ "as if parent process died with exit status -1.",
+ __FUNCTION__);
+ SetExitStatus(WaitStatus(WaitStatus::Exit, -1), notify_status);
+ return error;
+ }
+
+ // Read the status.
+ int status = -1;
+ error = m_waitpid_pipe.Read(&status, sizeof(status), bytes_read);
+ if (error.Fail() || (bytes_read < sizeof(status))) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s() - failed to read "
+ "waitpid exit status from the pipe. Will notify "
+ "as if parent process died with exit status -1.",
+ __FUNCTION__);
+ SetExitStatus(WaitStatus(WaitStatus::Exit, -1), notify_status);
+ return error;
+ }
+
+ // Notify the monitor that our state has changed.
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): main loop received waitpid "
+ "exit status info: pid=%i (%s), status=%i",
+ __FUNCTION__, pid,
+ (pid == m_pid) ? "the inferior" : "not the inferior", status);
+
+ SetExitStatus(WaitStatus::Decode(status), notify_status);
+ return error;
+}
+
+task_t NativeProcessDarwin::TaskPortForProcessID(Status &error,
+ bool force) const {
+ if ((m_task == TASK_NULL) || force) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (m_pid == LLDB_INVALID_PROCESS_ID) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): cannot get task due "
+ "to invalid pid",
+ __FUNCTION__);
+ return TASK_NULL;
+ }
+
+ const uint32_t num_retries = 10;
+ const uint32_t usec_interval = 10000;
+
+ mach_port_t task_self = mach_task_self();
+ task_t task = TASK_NULL;
+
+ for (uint32_t i = 0; i < num_retries; i++) {
+ kern_return_t err = ::task_for_pid(task_self, m_pid, &task);
+ if (err == 0) {
+ // Succeeded. Save and return it.
+ error.Clear();
+ m_task = task;
+ log->Printf("NativeProcessDarwin::%s(): ::task_for_pid("
+ "stub_port = 0x%4.4x, pid = %llu, &task) "
+ "succeeded: inferior task port = 0x%4.4x",
+ __FUNCTION__, task_self, m_pid, m_task);
+ return m_task;
+ } else {
+ // Failed to get the task for the inferior process.
+ error.SetError(err, eErrorTypeMachKernel);
+ if (log) {
+ log->Printf("NativeProcessDarwin::%s(): ::task_for_pid("
+ "stub_port = 0x%4.4x, pid = %llu, &task) "
+ "failed, err = 0x%8.8x (%s)",
+ __FUNCTION__, task_self, m_pid, err, error.AsCString());
+ }
+ }
+
+ // Sleep a bit and try again
+ ::usleep(usec_interval);
+ }
+
+ // We failed to get the task for the inferior process. Ensure that it is
+ // cleared out.
+ m_task = TASK_NULL;
+ }
+ return m_task;
+}
+
+void NativeProcessDarwin::AttachToInferior(MainLoop &mainloop, lldb::pid_t pid,
+ Status &error) {
+ error.SetErrorString("TODO: implement");
+}
+
+Status NativeProcessDarwin::PrivateResume() {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ std::lock_guard<std::recursive_mutex> locker(m_exception_messages_mutex);
+ m_auto_resume_signo = m_sent_interrupt_signo;
+
+ if (log) {
+ if (m_auto_resume_signo)
+ log->Printf("NativeProcessDarwin::%s(): task 0x%x resuming (with "
+ "unhandled interrupt signal %i)...",
+ __FUNCTION__, m_task, m_auto_resume_signo);
+ else
+ log->Printf("NativeProcessDarwin::%s(): task 0x%x resuming...",
+ __FUNCTION__, m_task);
+ }
+
+ error = ReplyToAllExceptions();
+ if (error.Fail()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): aborting, failed to "
+ "reply to exceptions: %s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
+ // 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.
+ error = ResumeTask();
+ return error;
+}
+
+Status NativeProcessDarwin::ReplyToAllExceptions() {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+
+ TaskPortForProcessID(error);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): no task port, aborting",
+ __FUNCTION__);
+ return error;
+ }
+
+ std::lock_guard<std::recursive_mutex> locker(m_exception_messages_mutex);
+ if (m_exception_messages.empty()) {
+ // We're done.
+ return error;
+ }
+
+ size_t index = 0;
+ for (auto &message : m_exception_messages) {
+ if (log) {
+ log->Printf("NativeProcessDarwin::%s(): replying to exception "
+ "%zu...",
+ __FUNCTION__, index++);
+ }
+
+ int thread_reply_signal = 0;
+
+ const tid_t tid =
+ m_thread_list.GetThreadIDByMachPortNumber(message.state.thread_port);
+ const ResumeAction *action = nullptr;
+ if (tid != LLDB_INVALID_THREAD_ID)
+ action = m_thread_actions.GetActionForThread(tid, false);
+
+ if (action) {
+ thread_reply_signal = action->signal;
+ if (thread_reply_signal)
+ m_thread_actions.SetSignalHandledForThread(tid);
+ }
+
+ error = message.Reply(m_pid, m_task, thread_reply_signal);
+ if (error.Fail() && log) {
+ // We log any error here, but we don't stop the exception response
+ // handling.
+ log->Printf("NativeProcessDarwin::%s(): failed to reply to "
+ "exception: %s",
+ __FUNCTION__, error.AsCString());
+ error.Clear();
+ }
+ }
+
+ // Erase all exception message as we should have used and replied to them all
+ // already.
+ m_exception_messages.clear();
+ return error;
+}
+
+Status NativeProcessDarwin::ResumeTask() {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ TaskPortForProcessID(error);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): failed to get task port "
+ "for process when attempting to resume: %s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
+ if (m_task == TASK_NULL) {
+ error.SetErrorString("task port retrieval succeeded but task port is "
+ "null when attempting to resume the task");
+ return error;
+ }
+
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): requesting resume of task "
+ "0x%4.4x",
+ __FUNCTION__, m_task);
+
+ // Get the BasicInfo struct to verify that we're suspended before we try to
+ // resume the task.
+ struct task_basic_info task_info;
+ error = GetTaskBasicInfo(m_task, &task_info);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): failed to get task "
+ "BasicInfo when attempting to resume: %s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
+
+ // task_resume isn't counted like task_suspend calls 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) {
+ auto mach_err = ::task_resume(m_task);
+ error.SetError(mach_err, eErrorTypeMachKernel);
+ if (log) {
+ if (error.Success())
+ log->Printf("::task_resume(target_task = 0x%4.4x): success", m_task);
+ else
+ log->Printf("::task_resume(target_task = 0x%4.4x) error: %s", m_task,
+ error.AsCString());
+ }
+ } else {
+ if (log)
+ log->Printf("::task_resume(target_task = 0x%4.4x): ignored, "
+ "already running",
+ m_task);
+ }
+
+ return error;
+}
+
+bool NativeProcessDarwin::IsTaskValid() const {
+ if (m_task == TASK_NULL)
+ return false;
+
+ struct task_basic_info task_info;
+ return GetTaskBasicInfo(m_task, &task_info).Success();
+}
+
+bool NativeProcessDarwin::IsTaskValid(task_t task) const {
+ if (task == TASK_NULL)
+ return false;
+
+ struct task_basic_info task_info;
+ return GetTaskBasicInfo(task, &task_info).Success();
+}
+
+mach_port_t NativeProcessDarwin::GetExceptionPort() const {
+ return m_exception_port;
+}
+
+bool NativeProcessDarwin::IsExceptionPortValid() const {
+ return MACH_PORT_VALID(m_exception_port);
+}
+
+Status
+NativeProcessDarwin::GetTaskBasicInfo(task_t task,
+ struct task_basic_info *info) const {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ // Validate args.
+ if (info == NULL) {
+ error.SetErrorStringWithFormat("NativeProcessDarwin::%s(): mandatory "
+ "info arg is null",
+ __FUNCTION__);
+ return error;
+ }
+
+ // Grab the task if we don't already have it.
+ if (task == TASK_NULL) {
+ error.SetErrorStringWithFormat("NativeProcessDarwin::%s(): given task "
+ "is invalid",
+ __FUNCTION__);
+ }
+
+ mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
+ auto err = ::task_info(m_task, TASK_BASIC_INFO, (task_info_t)info, &count);
+ error.SetError(err, eErrorTypeMachKernel);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("::task_info(target_task = 0x%4.4x, "
+ "flavor = TASK_BASIC_INFO, task_info_out => %p, "
+ "task_info_outCnt => %u) failed: %u (%s)",
+ m_task, info, count, error.GetError(), error.AsCString());
+ return error;
+ }
+
+ Log *verbose_log(
+ GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+ if (verbose_log) {
+ 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;
+ verbose_log->Printf("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 error;
+}
+
+Status NativeProcessDarwin::SuspendTask() {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ if (m_task == TASK_NULL) {
+ error.SetErrorString("task port is null, cannot suspend task");
+ if (log)
+ log->Printf("NativeProcessDarwin::%s() failed: %s", __FUNCTION__,
+ error.AsCString());
+ return error;
+ }
+
+ auto mach_err = ::task_suspend(m_task);
+ error.SetError(mach_err, eErrorTypeMachKernel);
+ if (error.Fail() && log)
+ log->Printf("::task_suspend(target_task = 0x%4.4x)", m_task);
+
+ return error;
+}
+
+Status NativeProcessDarwin::Resume(const ResumeActionList &resume_actions) {
+ Status error;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ if (log)
+ log->Printf("NativeProcessDarwin::%s() called", __FUNCTION__);
+
+ if (CanResume()) {
+ m_thread_actions = resume_actions;
+ error = PrivateResume();
+ return error;
+ }
+
+ auto state = GetState();
+ if (state == eStateRunning) {
+ if (log)
+ log->Printf("NativeProcessDarwin::%s(): task 0x%x is already "
+ "running, ignoring...",
+ __FUNCTION__, TaskPortForProcessID(error));
+ return error;
+ }
+
+ // We can't resume from this state.
+ error.SetErrorStringWithFormat("task 0x%x has state %s, can't resume",
+ TaskPortForProcessID(error),
+ StateAsCString(state));
+ return error;
+}
+
+Status NativeProcessDarwin::Halt() {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+Status NativeProcessDarwin::Detach() {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+Status NativeProcessDarwin::Signal(int signo) {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+Status NativeProcessDarwin::Interrupt() {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+Status NativeProcessDarwin::Kill() {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+Status NativeProcessDarwin::GetMemoryRegionInfo(lldb::addr_t load_addr,
+ MemoryRegionInfo &range_info) {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+Status NativeProcessDarwin::ReadMemory(lldb::addr_t addr, void *buf,
+ size_t size, size_t &bytes_read) {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+Status NativeProcessDarwin::ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf,
+ size_t size,
+ size_t &bytes_read) {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+Status NativeProcessDarwin::WriteMemory(lldb::addr_t addr, const void *buf,
+ size_t size, size_t &bytes_written) {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+Status NativeProcessDarwin::AllocateMemory(size_t size, uint32_t permissions,
+ lldb::addr_t &addr) {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+Status NativeProcessDarwin::DeallocateMemory(lldb::addr_t addr) {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+lldb::addr_t NativeProcessDarwin::GetSharedLibraryInfoAddress() {
+ return LLDB_INVALID_ADDRESS;
+}
+
+size_t NativeProcessDarwin::UpdateThreads() { return 0; }
+
+bool NativeProcessDarwin::GetArchitecture(ArchSpec &arch) const {
+ return false;
+}
+
+Status NativeProcessDarwin::SetBreakpoint(lldb::addr_t addr, uint32_t size,
+ bool hardware) {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+void NativeProcessDarwin::DoStopIDBumped(uint32_t newBumpId) {}
+
+Status NativeProcessDarwin::GetLoadedModuleFileSpec(const char *module_path,
+ FileSpec &file_spec) {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+Status NativeProcessDarwin::GetFileLoadAddress(const llvm::StringRef &file_name,
+ lldb::addr_t &load_addr) {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
+
+// NativeProcessProtocol protected interface
+Status NativeProcessDarwin::GetSoftwareBreakpointTrapOpcode(
+ size_t trap_opcode_size_hint, size_t &actual_opcode_size,
+ const uint8_t *&trap_opcode_bytes) {
+ Status error;
+ error.SetErrorString("TODO: implement");
+ return error;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.h b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.h
new file mode 100644
index 000000000000..6741d4ddc5d8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.h
@@ -0,0 +1,337 @@
+//===-- NativeProcessDarwin.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 NativeProcessDarwin_h
+#define NativeProcessDarwin_h
+
+// NOTE: this code should only be compiled on Apple Darwin systems. It is
+// not cross-platform code and is not intended to build on any other platform.
+// Therefore, platform-specific headers and code are okay here.
+
+// C includes
+#include <mach/mach_types.h>
+
+// C++ includes
+#include <mutex>
+#include <unordered_set>
+
+#include "lldb/Host/Debug.h"
+#include "lldb/Host/HostThread.h"
+#include "lldb/Host/Pipe.h"
+#include "lldb/Host/common/NativeProcessProtocol.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/lldb-types.h"
+
+#include "LaunchFlavor.h"
+#include "MachException.h"
+#include "NativeThreadDarwin.h"
+#include "NativeThreadListDarwin.h"
+
+namespace lldb_private {
+class Status;
+class Scalar;
+
+namespace process_darwin {
+
+/// \class NativeProcessDarwin
+/// Manages communication with the inferior (debugee) process.
+///
+/// Upon construction, this class prepares and launches an inferior process
+/// for debugging.
+///
+/// Changes in the inferior process state are broadcasted.
+class NativeProcessDarwin : public NativeProcessProtocol {
+ friend Status NativeProcessProtocol::Launch(
+ ProcessLaunchInfo &launch_info, NativeDelegate &native_delegate,
+ MainLoop &mainloop, NativeProcessProtocolSP &process_sp);
+
+ friend Status NativeProcessProtocol::Attach(
+ lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate,
+ MainLoop &mainloop, NativeProcessProtocolSP &process_sp);
+
+public:
+ ~NativeProcessDarwin() override;
+
+ // NativeProcessProtocol Interface
+ Status Resume(const ResumeActionList &resume_actions) override;
+
+ Status Halt() override;
+
+ Status Detach() override;
+
+ Status Signal(int signo) override;
+
+ Status Interrupt() override;
+
+ Status Kill() override;
+
+ Status GetMemoryRegionInfo(lldb::addr_t load_addr,
+ MemoryRegionInfo &range_info) override;
+
+ Status ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ size_t &bytes_read) override;
+
+ Status ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t size,
+ size_t &bytes_read) override;
+
+ Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size,
+ size_t &bytes_written) override;
+
+ Status AllocateMemory(size_t size, uint32_t permissions,
+ lldb::addr_t &addr) override;
+
+ Status DeallocateMemory(lldb::addr_t addr) override;
+
+ lldb::addr_t GetSharedLibraryInfoAddress() override;
+
+ size_t UpdateThreads() override;
+
+ bool GetArchitecture(ArchSpec &arch) const override;
+
+ Status SetBreakpoint(lldb::addr_t addr, uint32_t size,
+ bool hardware) override;
+
+ void DoStopIDBumped(uint32_t newBumpId) override;
+
+ Status GetLoadedModuleFileSpec(const char *module_path,
+ FileSpec &file_spec) override;
+
+ Status GetFileLoadAddress(const llvm::StringRef &file_name,
+ lldb::addr_t &load_addr) override;
+
+ NativeThreadDarwinSP GetThreadByID(lldb::tid_t id);
+
+ task_t GetTask() const { return m_task; }
+
+ // Interface used by NativeRegisterContext-derived classes.
+ static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr = nullptr,
+ void *data = nullptr, size_t data_size = 0,
+ long *result = nullptr);
+
+ bool SupportHardwareSingleStepping() const;
+
+protected:
+ // NativeProcessProtocol protected interface
+ Status
+ GetSoftwareBreakpointTrapOpcode(size_t trap_opcode_size_hint,
+ size_t &actual_opcode_size,
+ const uint8_t *&trap_opcode_bytes) override;
+
+private:
+ /// Mach task-related Member Variables
+
+ // The task port for the inferior process.
+ mutable task_t m_task;
+
+ // True if the inferior process did an exec since we started
+ // monitoring it.
+ bool m_did_exec;
+
+ // The CPU type of this process.
+ mutable cpu_type_t m_cpu_type;
+
+ /// Exception/Signal Handling Member Variables
+
+ // Exception port on which we will receive child exceptions
+ mach_port_t m_exception_port;
+
+ // Saved state of the child exception port prior to us installing
+ // our own intercepting port.
+ MachException::PortInfo m_exc_port_info;
+
+ // The thread that runs the Mach exception read and reply handler.
+ pthread_t m_exception_thread;
+
+ // TODO see if we can remove this if we get the exception collection
+ // and distribution to happen in a single-threaded fashion.
+ std::recursive_mutex m_exception_messages_mutex;
+
+ // A collection of exception messages caught when listening to the
+ // exception port.
+ MachException::Message::collection m_exception_messages;
+
+ // When we call MachProcess::Interrupt(), we want to send this
+ // signal (if non-zero).
+ int m_sent_interrupt_signo;
+
+ // If we resume the process and still haven't received our
+ // interrupt signal (if this is non-zero).
+ int m_auto_resume_signo;
+
+ /// Thread-related Member Variables
+ NativeThreadListDarwin m_thread_list;
+ ResumeActionList m_thread_actions;
+
+ /// Process Lifetime Member Variable
+
+ // The pipe over which the waitpid thread and the main loop will
+ // communicate.
+ Pipe m_waitpid_pipe;
+
+ // The thread that runs the waitpid handler.
+ pthread_t m_waitpid_thread;
+
+ // waitpid reader callback handle.
+ MainLoop::ReadHandleUP m_waitpid_reader_handle;
+
+ // Private Instance Methods
+ NativeProcessDarwin(lldb::pid_t pid, int pty_master_fd);
+
+ /// Finalize the launch.
+ ///
+ /// This method associates the NativeProcessDarwin instance with the host
+ /// process that was just launched. It peforms actions like attaching a
+ /// listener to the inferior exception port, ptracing the process, and the
+ /// like.
+ ///
+ /// \param[in] launch_flavor
+ /// The launch flavor that was used to launch the process.
+ ///
+ /// \param[in] main_loop
+ /// The main loop that will run the process monitor. Work
+ /// that needs to be done (e.g. reading files) gets registered
+ /// here along with callbacks to process the work.
+ ///
+ /// \return
+ /// Any error that occurred during the aforementioned
+ /// operations. Failure here will force termination of the
+ /// launched process and debugging session.
+ Status FinalizeLaunch(LaunchFlavor launch_flavor, MainLoop &main_loop);
+
+ Status SaveExceptionPortInfo();
+
+ void ExceptionMessageReceived(const MachException::Message &message);
+
+ void MaybeRaiseThreadPriority();
+
+ Status StartExceptionThread();
+
+ Status SendInferiorExitStatusToMainLoop(::pid_t pid, int status);
+
+ Status HandleWaitpidResult();
+
+ bool ProcessUsingSpringBoard() const;
+
+ bool ProcessUsingBackBoard() const;
+
+ static void *ExceptionThread(void *arg);
+
+ void *DoExceptionThread();
+
+ lldb::addr_t GetDYLDAllImageInfosAddress(Status &error) const;
+
+ static uint32_t GetCPUTypeForLocalProcess(::pid_t pid);
+
+ uint32_t GetCPUType() const;
+
+ task_t ExceptionMessageBundleComplete();
+
+ void StartSTDIOThread();
+
+ Status StartWaitpidThread(MainLoop &main_loop);
+
+ static void *WaitpidThread(void *arg);
+
+ void *DoWaitpidThread();
+
+ task_t TaskPortForProcessID(Status &error, bool force = false) const;
+
+ /// Attaches to an existing process. Forms the implementation of
+ /// Process::DoAttach.
+ void AttachToInferior(MainLoop &mainloop, lldb::pid_t pid, Status &error);
+
+ ::pid_t Attach(lldb::pid_t pid, Status &error);
+
+ Status PrivateResume();
+
+ Status ReplyToAllExceptions();
+
+ Status ResumeTask();
+
+ bool IsTaskValid() const;
+
+ bool IsTaskValid(task_t task) const;
+
+ mach_port_t GetExceptionPort() const;
+
+ bool IsExceptionPortValid() const;
+
+ Status GetTaskBasicInfo(task_t task, struct task_basic_info *info) const;
+
+ Status SuspendTask();
+
+ static Status SetDefaultPtraceOpts(const lldb::pid_t);
+
+ static void *MonitorThread(void *baton);
+
+ void MonitorCallback(lldb::pid_t pid, bool exited, int signal, int status);
+
+ void WaitForNewThread(::pid_t tid);
+
+ void MonitorSIGTRAP(const siginfo_t &info, NativeThreadDarwin &thread);
+
+ void MonitorTrace(NativeThreadDarwin &thread);
+
+ void MonitorBreakpoint(NativeThreadDarwin &thread);
+
+ void MonitorWatchpoint(NativeThreadDarwin &thread, uint32_t wp_index);
+
+ void MonitorSignal(const siginfo_t &info, NativeThreadDarwin &thread,
+ bool exited);
+
+ Status SetupSoftwareSingleStepping(NativeThreadDarwin &thread);
+
+ bool HasThreadNoLock(lldb::tid_t thread_id);
+
+ bool StopTrackingThread(lldb::tid_t thread_id);
+
+ NativeThreadDarwinSP AddThread(lldb::tid_t thread_id);
+
+ Status GetSoftwareBreakpointPCOffset(uint32_t &actual_opcode_size);
+
+ Status FixupBreakpointPCAsNeeded(NativeThreadDarwin &thread);
+
+ /// Writes a siginfo_t structure corresponding to the given thread
+ /// ID to the memory region pointed to by \p siginfo.
+ Status GetSignalInfo(lldb::tid_t tid, void *siginfo);
+
+ /// Writes the raw event message code (vis-a-vis PTRACE_GETEVENTMSG)
+ /// corresponding to the given thread ID to the memory pointed to by @p
+ /// message.
+ Status GetEventMessage(lldb::tid_t tid, unsigned long *message);
+
+ void NotifyThreadDeath(lldb::tid_t tid);
+
+ Status Detach(lldb::tid_t tid);
+
+ // This method is requests a stop on all threads which are still
+ // running. It sets up a deferred delegate notification, which will
+ // fire once threads report as stopped. The triggerring_tid will be
+ // set as the current thread (main stop reason).
+ void StopRunningThreads(lldb::tid_t triggering_tid);
+
+ // Notify the delegate if all threads have stopped.
+ void SignalIfAllThreadsStopped();
+
+ // Resume the given thread, optionally passing it the given signal.
+ // The type of resume operation (continue, single-step) depends on
+ // the state parameter.
+ Status ResumeThread(NativeThreadDarwin &thread, lldb::StateType state,
+ int signo);
+
+ void ThreadWasCreated(NativeThreadDarwin &thread);
+
+ void SigchldHandler();
+};
+
+} // namespace process_darwin
+} // namespace lldb_private
+
+#endif /* NativeProcessDarwin_h */
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadDarwin.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadDarwin.cpp
new file mode 100644
index 000000000000..bcd6d8c2c4c1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadDarwin.cpp
@@ -0,0 +1,281 @@
+//===-- NativeThreadDarwin.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 "NativeThreadDarwin.h"
+
+// C includes
+#include <libproc.h>
+
+// LLDB includes
+#include "lldb/Utility/Stream.h"
+
+#include "NativeProcessDarwin.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_darwin;
+
+uint64_t NativeThreadDarwin::GetGloballyUniqueThreadIDForMachPortID(
+ ::thread_t mach_port_id) {
+ thread_identifier_info_data_t tident;
+ mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT;
+
+ auto mach_err = ::thread_info(mach_port_id, THREAD_IDENTIFIER_INFO,
+ (thread_info_t)&tident, &tident_count);
+ if (mach_err != KERN_SUCCESS) {
+ // When we fail to get thread info for the supposed port, assume it is
+ // really a globally unique thread id already, or return the best thing we
+ // can, which is the thread port.
+ return mach_port_id;
+ }
+ return tident.thread_id;
+}
+
+NativeThreadDarwin::NativeThreadDarwin(NativeProcessDarwin *process,
+ bool is_64_bit,
+ lldb::tid_t unique_thread_id,
+ ::thread_t mach_thread_port)
+ : NativeThreadProtocol(process, unique_thread_id),
+ m_mach_thread_port(mach_thread_port), m_basic_info(),
+ m_proc_threadinfo() {}
+
+bool NativeThreadDarwin::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_thread_port, THREAD_IDENTIFIER_INFO,
+ (thread_info_t)&m_ident_info, &count);
+ return kret == KERN_SUCCESS;
+
+ return false;
+}
+
+std::string NativeThreadDarwin::GetName() {
+ std::string name;
+
+ if (GetIdentifierInfo()) {
+ auto process_sp = GetProcess();
+ if (!process_sp) {
+ name = "<unavailable>";
+ return name;
+ }
+
+ int len = ::proc_pidinfo(process_sp->GetID(), PROC_PIDTHREADINFO,
+ m_ident_info.thread_handle, &m_proc_threadinfo,
+ sizeof(m_proc_threadinfo));
+
+ if (len && m_proc_threadinfo.pth_name[0])
+ name = m_proc_threadinfo.pth_name;
+ }
+ return name;
+}
+
+lldb::StateType NativeThreadDarwin::GetState() {
+ // TODO implement
+ return eStateInvalid;
+}
+
+bool NativeThreadDarwin::GetStopReason(ThreadStopInfo &stop_info,
+ std::string &description) {
+ // TODO implement
+ return false;
+}
+
+NativeRegisterContextSP NativeThreadDarwin::GetRegisterContext() {
+ // TODO implement
+ return NativeRegisterContextSP();
+}
+
+Status NativeThreadDarwin::SetWatchpoint(lldb::addr_t addr, size_t size,
+ uint32_t watch_flags, bool hardware) {
+ Status error;
+ error.SetErrorString("not yet implemented");
+ return error;
+}
+
+Status NativeThreadDarwin::RemoveWatchpoint(lldb::addr_t addr) {
+ Status error;
+ error.SetErrorString("not yet implemented");
+ return error;
+}
+
+void NativeThreadDarwin::Dump(Stream &stream) const {
+// This is what we really want once we have the thread class wired up.
+#if 0
+ 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);
+
+#else
+ // Here's all we have right now.
+ stream.Printf("tid: 0x%8.8" PRIx64 ", thread port: 0x%4.4x", GetID(),
+ m_mach_thread_port);
+#endif
+}
+
+bool NativeThreadDarwin::NotifyException(MachException::Data &exc) {
+// TODO implement this.
+#if 0
+ // 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;
+#else
+ // Pretend we handled it.
+ return true;
+#endif
+}
+
+bool NativeThreadDarwin::ShouldStop(bool &step_more) const {
+// TODO: implement this
+#if 0
+ // 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;
+#else
+ return false;
+#endif
+}
+
+void NativeThreadDarwin::ThreadDidStop() {
+// TODO implement this.
+#if 0
+ // 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);
+#endif
+}
+
+bool NativeThreadDarwin::MachPortNumberIsValid(::thread_t thread) {
+ return thread != (::thread_t)(0);
+}
+
+const struct thread_basic_info *NativeThreadDarwin::GetBasicInfo() const {
+ if (GetBasicInfo(m_mach_thread_port, &m_basic_info))
+ return &m_basic_info;
+ return NULL;
+}
+
+bool NativeThreadDarwin::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 NativeThreadDarwin::IsUserReady() const {
+ 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 false;
+}
+
+NativeProcessDarwinSP NativeThreadDarwin::GetNativeProcessDarwinSP() {
+ return std::static_pointer_cast<NativeProcessDarwin>(GetProcess());
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadDarwin.h b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadDarwin.h
new file mode 100644
index 000000000000..616a9a7b9bf0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadDarwin.h
@@ -0,0 +1,165 @@
+//===-- NativeThreadDarwin.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 NativeThreadDarwin_H
+#define NativeThreadDarwin_H
+
+// C includes
+#include <mach/mach_types.h>
+#include <sched.h>
+#include <sys/proc_info.h>
+
+// C++ includes
+#include <map>
+#include <memory>
+#include <string>
+
+// LLDB includes
+#include "lldb/Host/common/NativeThreadProtocol.h"
+#include "lldb/lldb-private-forward.h"
+
+#include "MachException.h"
+
+namespace lldb_private {
+namespace process_darwin {
+
+class NativeProcessDarwin;
+using NativeProcessDarwinSP = std::shared_ptr<NativeProcessDarwin>;
+
+class NativeThreadListDarwin;
+
+class NativeThreadDarwin : public NativeThreadProtocol {
+ friend class NativeProcessDarwin;
+ friend class NativeThreadListDarwin;
+
+public:
+ static uint64_t
+ GetGloballyUniqueThreadIDForMachPortID(::thread_t mach_port_id);
+
+ NativeThreadDarwin(NativeProcessDarwin *process, bool is_64_bit,
+ lldb::tid_t unique_thread_id = 0,
+ ::thread_t mach_thread_port = 0);
+
+ // NativeThreadProtocol Interface
+ std::string GetName() override;
+
+ lldb::StateType GetState() override;
+
+ bool GetStopReason(ThreadStopInfo &stop_info,
+ std::string &description) override;
+
+ NativeRegisterContextSP GetRegisterContext() override;
+
+ Status SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags,
+ bool hardware) override;
+
+ Status RemoveWatchpoint(lldb::addr_t addr) override;
+
+ // New methods that are fine for others to call.
+ void Dump(Stream &stream) const;
+
+private:
+ // Interface for friend classes
+
+ /// Resumes the thread. If \p signo is anything but
+ /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread.
+ Status Resume(uint32_t signo);
+
+ /// Single steps the thread. If \p signo is anything but
+ /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread.
+ Status SingleStep(uint32_t signo);
+
+ bool NotifyException(MachException::Data &exc);
+
+ bool ShouldStop(bool &step_more) const;
+
+ void ThreadDidStop();
+
+ void SetStoppedBySignal(uint32_t signo, const siginfo_t *info = nullptr);
+
+ /// Return true if the thread is stopped.
+ /// If stopped by a signal, indicate the signo in the signo
+ /// argument. Otherwise, return LLDB_INVALID_SIGNAL_NUMBER.
+ bool IsStopped(int *signo);
+
+ const struct thread_basic_info *GetBasicInfo() const;
+
+ static bool GetBasicInfo(::thread_t thread,
+ struct thread_basic_info *basicInfoPtr);
+
+ bool IsUserReady() const;
+
+ void SetStoppedByExec();
+
+ void SetStoppedByBreakpoint();
+
+ void SetStoppedByWatchpoint(uint32_t wp_index);
+
+ bool IsStoppedAtBreakpoint();
+
+ bool IsStoppedAtWatchpoint();
+
+ void SetStoppedByTrace();
+
+ void SetStoppedWithNoReason();
+
+ void SetExited();
+
+ Status RequestStop();
+
+ /// Return the mach thread port number for this thread.
+ ///
+ /// \return
+ /// The mach port number for this thread. Returns NULL_THREAD
+ /// when the thread is invalid.
+ thread_t GetMachPortNumber() const { return m_mach_thread_port; }
+
+ static bool MachPortNumberIsValid(::thread_t thread);
+
+ // Private interface
+ bool GetIdentifierInfo();
+
+ void MaybeLogStateChange(lldb::StateType new_state);
+
+ NativeProcessDarwinSP GetNativeProcessDarwinSP();
+
+ void SetStopped();
+
+ inline void MaybePrepareSingleStepWorkaround();
+
+ inline void MaybeCleanupSingleStepWorkaround();
+
+ // Member Variables
+
+ // The mach thread port for the thread.
+ ::thread_t m_mach_thread_port;
+
+ // The most recently-retrieved thread basic info.
+ mutable ::thread_basic_info m_basic_info;
+
+ struct proc_threadinfo m_proc_threadinfo;
+
+ thread_identifier_info_data_t m_ident_info;
+
+#if 0
+ lldb::StateType m_state;
+ ThreadStopInfo m_stop_info;
+ NativeRegisterContextSP m_reg_context_sp;
+ std::string m_stop_description;
+ using WatchpointIndexMap = std::map<lldb::addr_t, uint32_t>;
+ WatchpointIndexMap m_watchpoint_index_map;
+ // cpu_set_t m_original_cpu_set; // For single-step workaround.
+#endif
+};
+
+typedef std::shared_ptr<NativeThreadDarwin> NativeThreadDarwinSP;
+
+} // namespace process_darwin
+} // namespace lldb_private
+
+#endif // #ifndef NativeThreadDarwin_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadListDarwin.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadListDarwin.cpp
new file mode 100644
index 000000000000..89de92a6df4d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadListDarwin.cpp
@@ -0,0 +1,702 @@
+//===-- NativeThreadListDarwin.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 "NativeThreadListDarwin.h"
+
+// C includes
+#include <inttypes.h>
+#include <mach/vm_map.h>
+#include <sys/sysctl.h>
+
+// LLDB includes
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/lldb-enumerations.h"
+
+#include "NativeProcessDarwin.h"
+#include "NativeThreadDarwin.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_darwin;
+
+NativeThreadListDarwin::NativeThreadListDarwin()
+ : m_threads(), m_threads_mutex(), m_is_64_bit(false) {}
+
+NativeThreadListDarwin::~NativeThreadListDarwin() {}
+
+// These methods will be accessed directly from NativeThreadDarwin
+#if 0
+nub_state_t
+NativeThreadListDarwin::GetState(nub_thread_t tid)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetState();
+ return eStateInvalid;
+}
+
+const char *
+NativeThreadListDarwin::GetName (nub_thread_t tid)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetName();
+ return NULL;
+}
+#endif
+
+// TODO: figure out if we need to add this to NativeThreadDarwin yet.
+#if 0
+ThreadInfo::QoS
+NativeThreadListDarwin::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
+NativeThreadListDarwin::GetPThreadT (nub_thread_t tid)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetPThreadT();
+ return INVALID_NUB_ADDRESS;
+}
+
+nub_addr_t
+NativeThreadListDarwin::GetDispatchQueueT (nub_thread_t tid)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetDispatchQueueT();
+ return INVALID_NUB_ADDRESS;
+}
+
+nub_addr_t
+NativeThreadListDarwin::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;
+}
+#endif
+
+// TODO implement these
+#if 0
+nub_thread_t
+NativeThreadListDarwin::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
+NativeThreadListDarwin::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
+NativeThreadListDarwin::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
+NativeThreadListDarwin::DumpThreadStoppedReason (nub_thread_t tid) const
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ thread_sp->GetStopException().DumpStopReason();
+}
+
+const char *
+NativeThreadListDarwin::GetThreadInfo (nub_thread_t tid) const
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->GetBasicInfoAsString();
+ return NULL;
+}
+
+#endif
+
+NativeThreadDarwinSP
+NativeThreadListDarwin::GetThreadByID(lldb::tid_t tid) const {
+ std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+ for (auto thread_sp : m_threads) {
+ if (thread_sp && (thread_sp->GetID() == tid))
+ return thread_sp;
+ }
+ return NativeThreadDarwinSP();
+}
+
+NativeThreadDarwinSP NativeThreadListDarwin::GetThreadByMachPortNumber(
+ ::thread_t mach_port_number) const {
+ std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+ for (auto thread_sp : m_threads) {
+ if (thread_sp && (thread_sp->GetMachPortNumber() == mach_port_number))
+ return thread_sp;
+ }
+ return NativeThreadDarwinSP();
+}
+
+lldb::tid_t NativeThreadListDarwin::GetThreadIDByMachPortNumber(
+ ::thread_t mach_port_number) const {
+ std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+ for (auto thread_sp : m_threads) {
+ if (thread_sp && (thread_sp->GetMachPortNumber() == mach_port_number))
+ return thread_sp->GetID();
+ }
+ return LLDB_INVALID_THREAD_ID;
+}
+
+// TODO implement
+#if 0
+thread_t
+NativeThreadListDarwin::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
+NativeThreadListDarwin::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
+NativeThreadListDarwin::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
+NativeThreadListDarwin::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
+NativeThreadListDarwin::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
+NativeThreadListDarwin::SaveRegisterState (nub_thread_t tid)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->SaveRegisterState ();
+ return 0;
+}
+
+bool
+NativeThreadListDarwin::RestoreRegisterState (nub_thread_t tid, uint32_t save_id)
+{
+ MachThreadSP thread_sp (GetThreadByID (tid));
+ if (thread_sp)
+ return thread_sp->RestoreRegisterState (save_id);
+ return 0;
+}
+#endif
+
+size_t NativeThreadListDarwin::GetNumberOfThreads() const {
+ std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+ return static_cast<size_t>(m_threads.size());
+}
+
+// TODO implement
+#if 0
+nub_thread_t
+NativeThreadListDarwin::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
+NativeThreadListDarwin::CurrentThreadID ( )
+{
+ MachThreadSP thread_sp;
+ CurrentThread(thread_sp);
+ if (thread_sp.get())
+ return thread_sp->ThreadID();
+ return INVALID_NUB_THREAD;
+}
+
+#endif
+
+bool NativeThreadListDarwin::NotifyException(MachException::Data &exc) {
+ auto thread_sp = GetThreadByMachPortNumber(exc.thread_port);
+ if (thread_sp) {
+ thread_sp->NotifyException(exc);
+ return true;
+ }
+ return false;
+}
+
+void NativeThreadListDarwin::Clear() {
+ std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+ m_threads.clear();
+}
+
+uint32_t NativeThreadListDarwin::UpdateThreadList(NativeProcessDarwin &process,
+ bool update,
+ collection *new_threads) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+ if (log)
+ log->Printf("NativeThreadListDarwin::%s() (pid = %" PRIu64 ", update = "
+ "%u) process stop count = %u",
+ __FUNCTION__, process.GetID(), update, process.GetStopID());
+
+ if (process.GetStopID() == 0) {
+ // On our first stop, we'll record details like 32/64 bitness and select
+ // the proper architecture implementation.
+ //
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, (int)process.GetID()};
+
+ 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;
+ }
+
+// TODO implement architecture selection and abstraction.
+#if 0
+#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
+ DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM);
+#endif
+#endif
+ }
+
+ if (m_threads.empty() || update) {
+ thread_array_t thread_list = nullptr;
+ mach_msg_type_number_t thread_list_count = 0;
+ task_t task = process.GetTask();
+
+ Status error;
+ auto mach_err = ::task_threads(task, &thread_list, &thread_list_count);
+ error.SetError(mach_err, eErrorTypeMachKernel);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("::task_threads(task = 0x%4.4x, thread_list => %p, "
+ "thread_list_count => %u) failed: %u (%s)",
+ task, thread_list, thread_list_count, error.GetError(),
+ error.AsCString());
+ return 0;
+ }
+
+ if (thread_list_count > 0) {
+ 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) {
+ // Get the Mach thread port.
+ const ::thread_t mach_port_num = thread_list[idx];
+
+ // Get the unique thread id for the mach port number.
+ uint64_t unique_thread_id =
+ NativeThreadDarwin::GetGloballyUniqueThreadIDForMachPortID(
+ mach_port_num);
+
+ // Retrieve the thread if it exists.
+ auto thread_sp = GetThreadByID(unique_thread_id);
+ if (thread_sp) {
+ // We are already tracking it. Keep the existing native thread
+ // instance.
+ currThreads.push_back(thread_sp);
+ } else {
+ // We don't have a native thread instance for this thread. Create it
+ // now.
+ thread_sp.reset(new NativeThreadDarwin(
+ &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());
+}
+
+// TODO implement
+#if 0
+
+void
+NativeThreadListDarwin::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;
+}
+
+#endif
+
+void NativeThreadListDarwin::Dump(Stream &stream) const {
+ bool first = true;
+
+ std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+ for (auto thread_sp : m_threads) {
+ if (thread_sp) {
+ // Handle newlines between thread entries.
+ if (first)
+ first = false;
+ else
+ stream.PutChar('\n');
+ thread_sp->Dump(stream);
+ }
+ }
+}
+
+void NativeThreadListDarwin::ProcessWillResume(
+ NativeProcessDarwin &process, const ResumeActionList &thread_actions) {
+ std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+
+ // Update our thread list, because sometimes libdispatch or the kernel will
+ // spawn threads while a task is suspended.
+ NativeThreadListDarwin::collection new_threads;
+
+// TODO implement this.
+#if 0
+ // First figure out if we were planning on running only one thread, and if
+ // so, force that thread to resume.
+ bool run_one_thread;
+ thread_t solo_thread = THREAD_NULL;
+ 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;
+#endif
+
+ UpdateThreadList(process, true, &new_threads);
+
+#if 0
+ 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, "NativeThreadListDarwin::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());
+ }
+ }
+#endif
+}
+
+uint32_t NativeThreadListDarwin::ProcessDidStop(NativeProcessDarwin &process) {
+ std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+
+ // Update our thread list.
+ UpdateThreadList(process, true);
+
+ for (auto thread_sp : m_threads) {
+ if (thread_sp)
+ thread_sp->ThreadDidStop();
+ }
+ return (uint32_t)m_threads.size();
+}
+
+// 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 NativeThreadListDarwin::ShouldStop(bool &step_more) {
+ std::lock_guard<std::recursive_mutex> locker(m_threads_mutex);
+ for (auto thread_sp : m_threads) {
+ if (thread_sp && thread_sp->ShouldStop(step_more))
+ return true;
+ }
+ return false;
+}
+
+// Implement.
+#if 0
+
+void
+NativeThreadListDarwin::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
+NativeThreadListDarwin::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
+NativeThreadListDarwin::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() ->
+// NativeThreadListDarwin::EnableHardwareWatchpoint().
+uint32_t
+NativeThreadListDarwin::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
+NativeThreadListDarwin::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
+NativeThreadListDarwin::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
+NativeThreadListDarwin::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;
+}
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadListDarwin.h b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadListDarwin.h
new file mode 100644
index 000000000000..9ab0a7c8c802
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Darwin/NativeThreadListDarwin.h
@@ -0,0 +1,138 @@
+//===-- NativeThreadListDarwin.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 __NativeThreadListDarwin_h__
+#define __NativeThreadListDarwin_h__
+
+#include <memory>
+#include <mutex>
+#include <vector>
+
+#include "lldb/lldb-private-forward.h"
+#include "lldb/lldb-types.h"
+
+#include "MachException.h"
+
+// #include "ThreadInfo.h"
+
+namespace lldb_private {
+namespace process_darwin {
+
+class NativeBreakpointDarwin;
+class NativeProcessDarwin;
+
+class NativeThreadDarwin;
+using NativeThreadDarwinSP = std::shared_ptr<NativeThreadDarwin>;
+
+class NativeThreadListDarwin {
+public:
+ NativeThreadListDarwin();
+ ~NativeThreadListDarwin();
+
+ void Clear();
+
+ void Dump(Stream &stream) const;
+
+// These methods will be accessed directly from NativeThreadDarwin
+#if 0
+ 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);
+#endif
+
+ const char *GetThreadInfo(lldb::tid_t tid) const;
+
+ void ProcessWillResume(NativeProcessDarwin &process,
+ const ResumeActionList &thread_actions);
+
+ uint32_t ProcessDidStop(NativeProcessDarwin &process);
+
+ bool NotifyException(MachException::Data &exc);
+
+ bool ShouldStop(bool &step_more);
+
+// These methods will be accessed directly from NativeThreadDarwin
+#if 0
+ const char * GetName (nub_thread_t tid);
+ nub_state_t GetState (nub_thread_t tid);
+ nub_thread_t SetCurrentThread (nub_thread_t tid);
+#endif
+
+// TODO: figure out if we need to add this to NativeThreadDarwin yet.
+#if 0
+ 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);
+#endif
+
+// These methods will be accessed directly from NativeThreadDarwin
+#if 0
+ 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);
+#endif
+
+ size_t GetNumberOfThreads() const;
+
+ lldb::tid_t ThreadIDAtIndex(size_t idx) const;
+
+ lldb::tid_t GetCurrentThreadID();
+
+ NativeThreadDarwinSP GetCurrentThread();
+
+ void NotifyBreakpointChanged(const NativeBreakpointDarwin *bp);
+
+ uint32_t EnableHardwareBreakpoint(const NativeBreakpointDarwin *bp) const;
+
+ bool DisableHardwareBreakpoint(const NativeBreakpointDarwin *bp) const;
+
+ uint32_t EnableHardwareWatchpoint(const NativeBreakpointDarwin *wp) const;
+
+ bool DisableHardwareWatchpoint(const NativeBreakpointDarwin *wp) const;
+
+ uint32_t GetNumberOfSupportedHardwareWatchpoints() const;
+
+ size_t GetThreadIndexForThreadStoppedWithSignal(const int signo) const;
+
+ NativeThreadDarwinSP GetThreadByID(lldb::tid_t tid) const;
+
+ NativeThreadDarwinSP
+ GetThreadByMachPortNumber(::thread_t mach_port_number) const;
+
+ lldb::tid_t GetThreadIDByMachPortNumber(::thread_t mach_port_number) const;
+
+ thread_t GetMachPortNumberByThreadID(lldb::tid_t globally_unique_id) const;
+
+protected:
+ typedef std::vector<NativeThreadDarwinSP> collection;
+ typedef collection::iterator iterator;
+ typedef collection::const_iterator const_iterator;
+
+ // Consider having this return an lldb_private::Status.
+ uint32_t UpdateThreadList(NativeProcessDarwin &process, bool update,
+ collection *num_threads = nullptr);
+
+ collection m_threads;
+ mutable std::recursive_mutex m_threads_mutex;
+ NativeThreadDarwinSP m_current_thread;
+ bool m_is_64_bit;
+};
+
+} // namespace process_darwin
+} // namespace lldb_private
+
+#endif // #ifndef __NativeThreadListDarwin_h__
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp
new file mode 100644
index 000000000000..3c6264dcaa1a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp
@@ -0,0 +1,632 @@
+//===-- FreeBSDThread.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 <pthread.h>
+#include <pthread_np.h>
+#include <stdlib.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/user.h>
+
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/State.h"
+
+#include "FreeBSDThread.h"
+#include "POSIXStopInfo.h"
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
+#include "Plugins/Process/Utility/RegisterContextFreeBSD_i386.h"
+#include "Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h"
+#include "Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h"
+#include "Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h"
+#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm.h"
+#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"
+#include "Plugins/Process/Utility/UnwindLLDB.h"
+#include "ProcessFreeBSD.h"
+#include "ProcessMonitor.h"
+#include "RegisterContextPOSIXProcessMonitor_arm.h"
+#include "RegisterContextPOSIXProcessMonitor_arm64.h"
+#include "RegisterContextPOSIXProcessMonitor_mips64.h"
+#include "RegisterContextPOSIXProcessMonitor_powerpc.h"
+#include "RegisterContextPOSIXProcessMonitor_x86.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/HostNativeThread.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Utility/State.h"
+#include "llvm/ADT/SmallString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+FreeBSDThread::FreeBSDThread(Process &process, lldb::tid_t tid)
+ : Thread(process, tid), m_frame_up(), m_breakpoint(),
+ m_thread_name_valid(false), m_thread_name(), m_posix_thread(nullptr) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+ LLDB_LOGV(log, "tid = {0}", tid);
+
+ // Set the current watchpoints for this thread.
+ Target &target = GetProcess()->GetTarget();
+ const WatchpointList &wp_list = target.GetWatchpointList();
+ size_t wp_size = wp_list.GetSize();
+
+ for (uint32_t wp_idx = 0; wp_idx < wp_size; wp_idx++) {
+ lldb::WatchpointSP wp = wp_list.GetByIndex(wp_idx);
+ if (wp.get() && wp->IsEnabled()) {
+ // This watchpoint as been enabled; obviously this "new" thread has been
+ // created since that watchpoint was enabled. Since the
+ // POSIXBreakpointProtocol has yet to be initialized, its
+ // m_watchpoints_initialized member will be FALSE. Attempting to read
+ // the debug status register to determine if a watchpoint has been hit
+ // would result in the zeroing of that register. Since the active debug
+ // registers would have been cloned when this thread was created, simply
+ // force the m_watchpoints_initized member to TRUE and avoid resetting
+ // dr6 and dr7.
+ GetPOSIXBreakpointProtocol()->ForceWatchpointsInitialized();
+ }
+ }
+}
+
+FreeBSDThread::~FreeBSDThread() { DestroyThread(); }
+
+ProcessMonitor &FreeBSDThread::GetMonitor() {
+ ProcessSP base = GetProcess();
+ ProcessFreeBSD &process = static_cast<ProcessFreeBSD &>(*base);
+ return process.GetMonitor();
+}
+
+void FreeBSDThread::RefreshStateAfterStop() {
+ // Invalidate all registers in our register context. We don't set "force" to
+ // true because the stop reply packet might have had some register values
+ // that were expedited and these will already be copied into the register
+ // context by the time this function gets called. The KDPRegisterContext
+ // class has been made smart enough to detect when it needs to invalidate
+ // which registers are valid by putting hooks in the register read and
+ // register supply functions where they check the process stop ID and do the
+ // right thing. if (StateIsStoppedState(GetState())
+ {
+ const bool force = false;
+ GetRegisterContext()->InvalidateIfNeeded(force);
+ }
+}
+
+const char *FreeBSDThread::GetInfo() { return nullptr; }
+
+void FreeBSDThread::SetName(const char *name) {
+ m_thread_name_valid = (name && name[0]);
+ if (m_thread_name_valid)
+ m_thread_name.assign(name);
+ else
+ m_thread_name.clear();
+}
+
+const char *FreeBSDThread::GetName() {
+ if (!m_thread_name_valid) {
+ m_thread_name.clear();
+ int pid = GetProcess()->GetID();
+
+ struct kinfo_proc *kp = nullptr, *nkp;
+ size_t len = 0;
+ int error;
+ int ctl[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID | KERN_PROC_INC_THREAD,
+ pid};
+
+ while (1) {
+ error = sysctl(ctl, 4, kp, &len, nullptr, 0);
+ if (kp == nullptr || (error != 0 && errno == ENOMEM)) {
+ // Add extra space in case threads are added before next call.
+ len += sizeof(*kp) + len / 10;
+ nkp = (struct kinfo_proc *)realloc(kp, len);
+ if (nkp == nullptr) {
+ free(kp);
+ return nullptr;
+ }
+ kp = nkp;
+ continue;
+ }
+ if (error != 0)
+ len = 0;
+ break;
+ }
+
+ for (size_t i = 0; i < len / sizeof(*kp); i++) {
+ if (kp[i].ki_tid == (lwpid_t)GetID()) {
+ m_thread_name.append(kp[i].ki_tdname,
+ kp[i].ki_tdname + strlen(kp[i].ki_tdname));
+ break;
+ }
+ }
+ free(kp);
+ m_thread_name_valid = true;
+ }
+
+ if (m_thread_name.empty())
+ return nullptr;
+ return m_thread_name.c_str();
+}
+
+lldb::RegisterContextSP FreeBSDThread::GetRegisterContext() {
+ if (!m_reg_context_sp) {
+ m_posix_thread = nullptr;
+
+ RegisterInfoInterface *reg_interface = nullptr;
+ const ArchSpec &target_arch = GetProcess()->GetTarget().GetArchitecture();
+
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::aarch64:
+ reg_interface = new RegisterInfoPOSIX_arm64(target_arch);
+ break;
+ case llvm::Triple::arm:
+ reg_interface = new RegisterInfoPOSIX_arm(target_arch);
+ break;
+ case llvm::Triple::ppc:
+#ifndef __powerpc64__
+ reg_interface = new RegisterContextFreeBSD_powerpc32(target_arch);
+ break;
+#endif
+ case llvm::Triple::ppc64:
+ reg_interface = new RegisterContextFreeBSD_powerpc64(target_arch);
+ break;
+ case llvm::Triple::mips64:
+ reg_interface = new RegisterContextFreeBSD_mips64(target_arch);
+ break;
+ case llvm::Triple::x86:
+ reg_interface = new RegisterContextFreeBSD_i386(target_arch);
+ break;
+ case llvm::Triple::x86_64:
+ reg_interface = new RegisterContextFreeBSD_x86_64(target_arch);
+ break;
+ default:
+ llvm_unreachable("CPU not supported");
+ }
+
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::aarch64: {
+ RegisterContextPOSIXProcessMonitor_arm64 *reg_ctx =
+ new RegisterContextPOSIXProcessMonitor_arm64(*this, 0, reg_interface);
+ m_posix_thread = reg_ctx;
+ m_reg_context_sp.reset(reg_ctx);
+ break;
+ }
+ case llvm::Triple::arm: {
+ RegisterContextPOSIXProcessMonitor_arm *reg_ctx =
+ new RegisterContextPOSIXProcessMonitor_arm(*this, 0, reg_interface);
+ m_posix_thread = reg_ctx;
+ m_reg_context_sp.reset(reg_ctx);
+ break;
+ }
+ case llvm::Triple::mips64: {
+ RegisterContextPOSIXProcessMonitor_mips64 *reg_ctx =
+ new RegisterContextPOSIXProcessMonitor_mips64(*this, 0,
+ reg_interface);
+ m_posix_thread = reg_ctx;
+ m_reg_context_sp.reset(reg_ctx);
+ break;
+ }
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64: {
+ RegisterContextPOSIXProcessMonitor_powerpc *reg_ctx =
+ new RegisterContextPOSIXProcessMonitor_powerpc(*this, 0,
+ reg_interface);
+ m_posix_thread = reg_ctx;
+ m_reg_context_sp.reset(reg_ctx);
+ break;
+ }
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64: {
+ RegisterContextPOSIXProcessMonitor_x86_64 *reg_ctx =
+ new RegisterContextPOSIXProcessMonitor_x86_64(*this, 0,
+ reg_interface);
+ m_posix_thread = reg_ctx;
+ m_reg_context_sp.reset(reg_ctx);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return m_reg_context_sp;
+}
+
+lldb::RegisterContextSP
+FreeBSDThread::CreateRegisterContextForFrame(lldb_private::StackFrame *frame) {
+ lldb::RegisterContextSP reg_ctx_sp;
+ uint32_t concrete_frame_idx = 0;
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+ LLDB_LOGV(log, "called");
+
+ if (frame)
+ concrete_frame_idx = frame->GetConcreteFrameIndex();
+
+ if (concrete_frame_idx == 0)
+ reg_ctx_sp = GetRegisterContext();
+ else {
+ assert(GetUnwinder());
+ reg_ctx_sp = GetUnwinder()->CreateRegisterContextForFrame(frame);
+ }
+
+ return reg_ctx_sp;
+}
+
+lldb::addr_t FreeBSDThread::GetThreadPointer() {
+ ProcessMonitor &monitor = GetMonitor();
+ addr_t addr;
+ if (monitor.ReadThreadPointer(GetID(), addr))
+ return addr;
+ else
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool FreeBSDThread::CalculateStopInfo() {
+ SetStopInfo(m_stop_info_sp);
+ return true;
+}
+
+Unwind *FreeBSDThread::GetUnwinder() {
+ if (!m_unwinder_up)
+ m_unwinder_up.reset(new UnwindLLDB(*this));
+
+ return m_unwinder_up.get();
+}
+
+void FreeBSDThread::DidStop() {
+ // Don't set the thread state to stopped unless we really stopped.
+}
+
+void FreeBSDThread::WillResume(lldb::StateType resume_state) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+ if (log)
+ log->Printf("tid %" PRIu64 " resume_state = %s", GetID(),
+ lldb_private::StateAsCString(resume_state));
+ ProcessSP process_sp(GetProcess());
+ ProcessFreeBSD *process = static_cast<ProcessFreeBSD *>(process_sp.get());
+ int signo = GetResumeSignal();
+ bool signo_valid = process->GetUnixSignals()->SignalIsValid(signo);
+
+ switch (resume_state) {
+ case eStateSuspended:
+ case eStateStopped:
+ process->m_suspend_tids.push_back(GetID());
+ break;
+ case eStateRunning:
+ process->m_run_tids.push_back(GetID());
+ if (signo_valid)
+ process->m_resume_signo = signo;
+ break;
+ case eStateStepping:
+ process->m_step_tids.push_back(GetID());
+ if (signo_valid)
+ process->m_resume_signo = signo;
+ break;
+ default:
+ break;
+ }
+}
+
+bool FreeBSDThread::Resume() {
+ lldb::StateType resume_state = GetResumeState();
+ ProcessMonitor &monitor = GetMonitor();
+ bool status;
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+ if (log)
+ log->Printf("FreeBSDThread::%s (), resume_state = %s", __FUNCTION__,
+ StateAsCString(resume_state));
+
+ switch (resume_state) {
+ default:
+ assert(false && "Unexpected state for resume!");
+ status = false;
+ break;
+
+ case lldb::eStateRunning:
+ SetState(resume_state);
+ status = monitor.Resume(GetID(), GetResumeSignal());
+ break;
+
+ case lldb::eStateStepping:
+ SetState(resume_state);
+ status = monitor.SingleStep(GetID(), GetResumeSignal());
+ break;
+ case lldb::eStateStopped:
+ case lldb::eStateSuspended:
+ status = true;
+ break;
+ }
+
+ return status;
+}
+
+void FreeBSDThread::Notify(const ProcessMessage &message) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+ if (log)
+ log->Printf("FreeBSDThread::%s () message kind = '%s' for tid %" PRIu64,
+ __FUNCTION__, message.PrintKind(), GetID());
+
+ switch (message.GetKind()) {
+ default:
+ assert(false && "Unexpected message kind!");
+ break;
+
+ case ProcessMessage::eExitMessage:
+ // Nothing to be done.
+ break;
+
+ case ProcessMessage::eLimboMessage:
+ LimboNotify(message);
+ break;
+
+ case ProcessMessage::eCrashMessage:
+ case ProcessMessage::eSignalMessage:
+ SignalNotify(message);
+ break;
+
+ case ProcessMessage::eSignalDeliveredMessage:
+ SignalDeliveredNotify(message);
+ break;
+
+ case ProcessMessage::eTraceMessage:
+ TraceNotify(message);
+ break;
+
+ case ProcessMessage::eBreakpointMessage:
+ BreakNotify(message);
+ break;
+
+ case ProcessMessage::eWatchpointMessage:
+ WatchNotify(message);
+ break;
+
+ case ProcessMessage::eExecMessage:
+ ExecNotify(message);
+ break;
+ }
+}
+
+bool FreeBSDThread::EnableHardwareWatchpoint(Watchpoint *wp) {
+ bool wp_set = false;
+ if (wp) {
+ addr_t wp_addr = wp->GetLoadAddress();
+ size_t wp_size = wp->GetByteSize();
+ bool wp_read = wp->WatchpointRead();
+ bool wp_write = wp->WatchpointWrite();
+ uint32_t wp_hw_index = wp->GetHardwareIndex();
+ POSIXBreakpointProtocol *reg_ctx = GetPOSIXBreakpointProtocol();
+ if (reg_ctx)
+ wp_set = reg_ctx->SetHardwareWatchpointWithIndex(
+ wp_addr, wp_size, wp_read, wp_write, wp_hw_index);
+ }
+ return wp_set;
+}
+
+bool FreeBSDThread::DisableHardwareWatchpoint(Watchpoint *wp) {
+ bool result = false;
+ if (wp) {
+ lldb::RegisterContextSP reg_ctx_sp = GetRegisterContext();
+ if (reg_ctx_sp.get())
+ result = reg_ctx_sp->ClearHardwareWatchpoint(wp->GetHardwareIndex());
+ }
+ return result;
+}
+
+uint32_t FreeBSDThread::NumSupportedHardwareWatchpoints() {
+ lldb::RegisterContextSP reg_ctx_sp = GetRegisterContext();
+ if (reg_ctx_sp.get())
+ return reg_ctx_sp->NumSupportedHardwareWatchpoints();
+ return 0;
+}
+
+uint32_t FreeBSDThread::FindVacantWatchpointIndex() {
+ uint32_t hw_index = LLDB_INVALID_INDEX32;
+ uint32_t num_hw_wps = NumSupportedHardwareWatchpoints();
+ uint32_t wp_idx;
+ POSIXBreakpointProtocol *reg_ctx = GetPOSIXBreakpointProtocol();
+ if (reg_ctx) {
+ for (wp_idx = 0; wp_idx < num_hw_wps; wp_idx++) {
+ if (reg_ctx->IsWatchpointVacant(wp_idx)) {
+ hw_index = wp_idx;
+ break;
+ }
+ }
+ }
+ return hw_index;
+}
+
+void FreeBSDThread::BreakNotify(const ProcessMessage &message) {
+ bool status;
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+
+ assert(GetRegisterContext());
+ status = GetPOSIXBreakpointProtocol()->UpdateAfterBreakpoint();
+ assert(status && "Breakpoint update failed!");
+
+ // With our register state restored, resolve the breakpoint object
+ // corresponding to our current PC.
+ assert(GetRegisterContext());
+ lldb::addr_t pc = GetRegisterContext()->GetPC();
+ if (log)
+ log->Printf("FreeBSDThread::%s () PC=0x%8.8" PRIx64, __FUNCTION__, pc);
+ lldb::BreakpointSiteSP bp_site(
+ GetProcess()->GetBreakpointSiteList().FindByAddress(pc));
+
+ // If the breakpoint is for this thread, then we'll report the hit, but if it
+ // is for another thread, we create a stop reason with should_stop=false. If
+ // there is no breakpoint location, then report an invalid stop reason. We
+ // don't need to worry about stepping over the breakpoint here, that will be
+ // taken care of when the thread resumes and notices that there's a
+ // breakpoint under the pc.
+ if (bp_site) {
+ lldb::break_id_t bp_id = bp_site->GetID();
+ // If we have an operating system plug-in, we might have set a thread
+ // specific breakpoint using the operating system thread ID, so we can't
+ // make any assumptions about the thread ID so we must always report the
+ // breakpoint regardless of the thread.
+ if (bp_site->ValidForThisThread(this) ||
+ GetProcess()->GetOperatingSystem() != nullptr)
+ SetStopInfo(StopInfo::CreateStopReasonWithBreakpointSiteID(*this, bp_id));
+ else {
+ const bool should_stop = false;
+ SetStopInfo(StopInfo::CreateStopReasonWithBreakpointSiteID(*this, bp_id,
+ should_stop));
+ }
+ } else
+ SetStopInfo(StopInfoSP());
+}
+
+void FreeBSDThread::WatchNotify(const ProcessMessage &message) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+
+ lldb::addr_t halt_addr = message.GetHWAddress();
+ if (log)
+ log->Printf(
+ "FreeBSDThread::%s () Hardware Watchpoint Address = 0x%8.8" PRIx64,
+ __FUNCTION__, halt_addr);
+
+ POSIXBreakpointProtocol *reg_ctx = GetPOSIXBreakpointProtocol();
+ if (reg_ctx) {
+ uint32_t num_hw_wps = reg_ctx->NumSupportedHardwareWatchpoints();
+ uint32_t wp_idx;
+ for (wp_idx = 0; wp_idx < num_hw_wps; wp_idx++) {
+ if (reg_ctx->IsWatchpointHit(wp_idx)) {
+ // Clear the watchpoint hit here
+ reg_ctx->ClearWatchpointHits();
+ break;
+ }
+ }
+
+ if (wp_idx == num_hw_wps)
+ return;
+
+ Target &target = GetProcess()->GetTarget();
+ lldb::addr_t wp_monitor_addr = reg_ctx->GetWatchpointAddress(wp_idx);
+ const WatchpointList &wp_list = target.GetWatchpointList();
+ lldb::WatchpointSP wp_sp = wp_list.FindByAddress(wp_monitor_addr);
+
+ assert(wp_sp.get() && "No watchpoint found");
+ SetStopInfo(
+ StopInfo::CreateStopReasonWithWatchpointID(*this, wp_sp->GetID()));
+ }
+}
+
+void FreeBSDThread::TraceNotify(const ProcessMessage &message) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+
+ // Try to resolve the breakpoint object corresponding to the current PC.
+ assert(GetRegisterContext());
+ lldb::addr_t pc = GetRegisterContext()->GetPC();
+ if (log)
+ log->Printf("FreeBSDThread::%s () PC=0x%8.8" PRIx64, __FUNCTION__, pc);
+ lldb::BreakpointSiteSP bp_site(
+ GetProcess()->GetBreakpointSiteList().FindByAddress(pc));
+
+ // If the current pc is a breakpoint site then set the StopInfo to
+ // Breakpoint. Otherwise, set the StopInfo to Watchpoint or Trace. If we have
+ // an operating system plug-in, we might have set a thread specific
+ // breakpoint using the operating system thread ID, so we can't make any
+ // assumptions about the thread ID so we must always report the breakpoint
+ // regardless of the thread.
+ if (bp_site && (bp_site->ValidForThisThread(this) ||
+ GetProcess()->GetOperatingSystem() != nullptr))
+ SetStopInfo(StopInfo::CreateStopReasonWithBreakpointSiteID(
+ *this, bp_site->GetID()));
+ else {
+ POSIXBreakpointProtocol *reg_ctx = GetPOSIXBreakpointProtocol();
+ if (reg_ctx) {
+ uint32_t num_hw_wps = reg_ctx->NumSupportedHardwareWatchpoints();
+ uint32_t wp_idx;
+ for (wp_idx = 0; wp_idx < num_hw_wps; wp_idx++) {
+ if (reg_ctx->IsWatchpointHit(wp_idx)) {
+ WatchNotify(message);
+ return;
+ }
+ }
+ }
+ SetStopInfo(StopInfo::CreateStopReasonToTrace(*this));
+ }
+}
+
+void FreeBSDThread::LimboNotify(const ProcessMessage &message) {
+ SetStopInfo(lldb::StopInfoSP(new POSIXLimboStopInfo(*this)));
+}
+
+void FreeBSDThread::SignalNotify(const ProcessMessage &message) {
+ int signo = message.GetSignal();
+ if (message.GetKind() == ProcessMessage::eCrashMessage) {
+ std::string stop_description = GetCrashReasonString(
+ message.GetCrashReason(), message.GetFaultAddress());
+ SetStopInfo(StopInfo::CreateStopReasonWithSignal(
+ *this, signo, stop_description.c_str()));
+ } else {
+ SetStopInfo(StopInfo::CreateStopReasonWithSignal(*this, signo));
+ }
+}
+
+void FreeBSDThread::SignalDeliveredNotify(const ProcessMessage &message) {
+ int signo = message.GetSignal();
+ SetStopInfo(StopInfo::CreateStopReasonWithSignal(*this, signo));
+}
+
+unsigned FreeBSDThread::GetRegisterIndexFromOffset(unsigned offset) {
+ unsigned reg = LLDB_INVALID_REGNUM;
+ ArchSpec arch = HostInfo::GetArchitecture();
+
+ switch (arch.GetMachine()) {
+ default:
+ llvm_unreachable("CPU type not supported!");
+ break;
+
+ case llvm::Triple::aarch64:
+ case llvm::Triple::arm:
+ case llvm::Triple::mips64:
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64: {
+ POSIXBreakpointProtocol *reg_ctx = GetPOSIXBreakpointProtocol();
+ reg = reg_ctx->GetRegisterIndexFromOffset(offset);
+ } break;
+ }
+ return reg;
+}
+
+void FreeBSDThread::ExecNotify(const ProcessMessage &message) {
+ SetStopInfo(StopInfo::CreateStopReasonWithExec(*this));
+}
+
+const char *FreeBSDThread::GetRegisterName(unsigned reg) {
+ const char *name = nullptr;
+ ArchSpec arch = HostInfo::GetArchitecture();
+
+ switch (arch.GetMachine()) {
+ default:
+ assert(false && "CPU type not supported!");
+ break;
+
+ case llvm::Triple::aarch64:
+ case llvm::Triple::arm:
+ case llvm::Triple::mips64:
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ name = GetRegisterContext()->GetRegisterName(reg);
+ break;
+ }
+ return name;
+}
+
+const char *FreeBSDThread::GetRegisterNameFromOffset(unsigned offset) {
+ return GetRegisterName(GetRegisterIndexFromOffset(offset));
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.h
new file mode 100644
index 000000000000..6d3c253a519e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/FreeBSDThread.h
@@ -0,0 +1,113 @@
+//===-- FreeBSDThread.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 liblldb_FreeBSDThread_H_
+#define liblldb_FreeBSDThread_H_
+
+#include <memory>
+#include <string>
+
+#include "RegisterContextPOSIX.h"
+#include "lldb/Target/Thread.h"
+
+class ProcessMessage;
+class ProcessMonitor;
+class POSIXBreakpointProtocol;
+
+// @class FreeBSDThread
+// Abstraction of a FreeBSD thread.
+class FreeBSDThread : public lldb_private::Thread {
+public:
+ // Constructors and destructors
+ FreeBSDThread(lldb_private::Process &process, lldb::tid_t tid);
+
+ virtual ~FreeBSDThread();
+
+ // POSIXThread
+ void RefreshStateAfterStop() override;
+
+ // This notifies the thread when a private stop occurs.
+ void DidStop() override;
+
+ const char *GetInfo() override;
+
+ void SetName(const char *name) override;
+
+ const char *GetName() override;
+
+ lldb::RegisterContextSP GetRegisterContext() override;
+
+ lldb::RegisterContextSP
+ CreateRegisterContextForFrame(lldb_private::StackFrame *frame) override;
+
+ lldb::addr_t GetThreadPointer() override;
+
+ // These functions provide a mapping from the register offset
+ // back to the register index or name for use in debugging or log
+ // output.
+
+ unsigned GetRegisterIndexFromOffset(unsigned offset);
+
+ const char *GetRegisterName(unsigned reg);
+
+ const char *GetRegisterNameFromOffset(unsigned offset);
+
+ // These methods form a specialized interface to POSIX threads.
+ //
+ bool Resume();
+
+ void Notify(const ProcessMessage &message);
+
+ // These methods provide an interface to watchpoints
+ //
+ bool EnableHardwareWatchpoint(lldb_private::Watchpoint *wp);
+
+ bool DisableHardwareWatchpoint(lldb_private::Watchpoint *wp);
+
+ uint32_t NumSupportedHardwareWatchpoints();
+
+ uint32_t FindVacantWatchpointIndex();
+
+protected:
+ POSIXBreakpointProtocol *GetPOSIXBreakpointProtocol() {
+ if (!m_reg_context_sp)
+ m_reg_context_sp = GetRegisterContext();
+ return m_posix_thread;
+ }
+
+ std::unique_ptr<lldb_private::StackFrame> m_frame_up;
+
+ lldb::BreakpointSiteSP m_breakpoint;
+
+ bool m_thread_name_valid;
+ std::string m_thread_name;
+ POSIXBreakpointProtocol *m_posix_thread;
+
+ ProcessMonitor &GetMonitor();
+
+ bool CalculateStopInfo() override;
+
+ void BreakNotify(const ProcessMessage &message);
+ void WatchNotify(const ProcessMessage &message);
+ virtual void TraceNotify(const ProcessMessage &message);
+ void LimboNotify(const ProcessMessage &message);
+ void SignalNotify(const ProcessMessage &message);
+ void SignalDeliveredNotify(const ProcessMessage &message);
+ void CrashNotify(const ProcessMessage &message);
+ void ExitNotify(const ProcessMessage &message);
+ void ExecNotify(const ProcessMessage &message);
+
+ lldb_private::Unwind *GetUnwinder() override;
+
+ // FreeBSDThread internal API.
+
+ // POSIXThread override
+ virtual void WillResume(lldb::StateType resume_state) override;
+};
+
+#endif // #ifndef liblldb_FreeBSDThread_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/POSIXStopInfo.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/POSIXStopInfo.cpp
new file mode 100644
index 000000000000..71f012944a9a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/POSIXStopInfo.cpp
@@ -0,0 +1,44 @@
+//===-- POSIXStopInfo.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 "POSIXStopInfo.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//===----------------------------------------------------------------------===//
+// POSIXLimboStopInfo
+
+POSIXLimboStopInfo::~POSIXLimboStopInfo() {}
+
+lldb::StopReason POSIXLimboStopInfo::GetStopReason() const {
+ return lldb::eStopReasonThreadExiting;
+}
+
+const char *POSIXLimboStopInfo::GetDescription() { return "thread exiting"; }
+
+bool POSIXLimboStopInfo::ShouldStop(Event *event_ptr) { return false; }
+
+bool POSIXLimboStopInfo::ShouldNotify(Event *event_ptr) { return false; }
+
+//===----------------------------------------------------------------------===//
+// POSIXNewThreadStopInfo
+
+POSIXNewThreadStopInfo::~POSIXNewThreadStopInfo() {}
+
+lldb::StopReason POSIXNewThreadStopInfo::GetStopReason() const {
+ return lldb::eStopReasonNone;
+}
+
+const char *POSIXNewThreadStopInfo::GetDescription() {
+ return "thread spawned";
+}
+
+bool POSIXNewThreadStopInfo::ShouldStop(Event *event_ptr) { return false; }
+
+bool POSIXNewThreadStopInfo::ShouldNotify(Event *event_ptr) { return false; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/POSIXStopInfo.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/POSIXStopInfo.h
new file mode 100644
index 000000000000..88fb7f31fe06
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/POSIXStopInfo.h
@@ -0,0 +1,66 @@
+//===-- POSIXStopInfo.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 liblldb_POSIXStopInfo_H_
+#define liblldb_POSIXStopInfo_H_
+
+#include "FreeBSDThread.h"
+#include "Plugins/Process/POSIX/CrashReason.h"
+#include "lldb/Target/StopInfo.h"
+#include <string>
+
+//===----------------------------------------------------------------------===//
+/// \class POSIXStopInfo
+/// Simple base class for all POSIX-specific StopInfo objects.
+///
+class POSIXStopInfo : public lldb_private::StopInfo {
+public:
+ POSIXStopInfo(lldb_private::Thread &thread, uint32_t status)
+ : StopInfo(thread, status) {}
+};
+
+//===----------------------------------------------------------------------===//
+/// \class POSIXLimboStopInfo
+/// Represents the stop state of a process ready to exit.
+///
+class POSIXLimboStopInfo : public POSIXStopInfo {
+public:
+ POSIXLimboStopInfo(FreeBSDThread &thread) : POSIXStopInfo(thread, 0) {}
+
+ ~POSIXLimboStopInfo();
+
+ lldb::StopReason GetStopReason() const;
+
+ const char *GetDescription();
+
+ bool ShouldStop(lldb_private::Event *event_ptr);
+
+ bool ShouldNotify(lldb_private::Event *event_ptr);
+};
+
+//===----------------------------------------------------------------------===//
+/// \class POSIXNewThreadStopInfo
+/// Represents the stop state of process when a new thread is spawned.
+///
+
+class POSIXNewThreadStopInfo : public POSIXStopInfo {
+public:
+ POSIXNewThreadStopInfo(FreeBSDThread &thread) : POSIXStopInfo(thread, 0) {}
+
+ ~POSIXNewThreadStopInfo();
+
+ lldb::StopReason GetStopReason() const;
+
+ const char *GetDescription();
+
+ bool ShouldStop(lldb_private::Event *event_ptr);
+
+ bool ShouldNotify(lldb_private::Event *event_ptr);
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp
new file mode 100644
index 000000000000..770794569f72
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp
@@ -0,0 +1,1081 @@
+//===-- ProcessFreeBSD.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 <pthread.h>
+#include <pthread_np.h>
+#include <stdlib.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/user.h>
+#include <machine/elf.h>
+
+#include <mutex>
+#include <unordered_map>
+
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/State.h"
+
+#include "FreeBSDThread.h"
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
+#include "Plugins/Process/Utility/FreeBSDSignals.h"
+#include "Plugins/Process/Utility/InferiorCallPOSIX.h"
+#include "ProcessFreeBSD.h"
+#include "ProcessMonitor.h"
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/State.h"
+
+#include "lldb/Host/posix/Fcntl.h"
+
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Threading.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+UnixSignalsSP &GetFreeBSDSignals() {
+ static UnixSignalsSP s_freebsd_signals_sp(new FreeBSDSignals());
+ return s_freebsd_signals_sp;
+}
+}
+
+// Static functions.
+
+lldb::ProcessSP
+ProcessFreeBSD::CreateInstance(lldb::TargetSP target_sp,
+ lldb::ListenerSP listener_sp,
+ const FileSpec *crash_file_path) {
+ lldb::ProcessSP process_sp;
+ if (crash_file_path == NULL)
+ process_sp.reset(
+ new ProcessFreeBSD(target_sp, listener_sp, GetFreeBSDSignals()));
+ return process_sp;
+}
+
+void ProcessFreeBSD::Initialize() {
+ static llvm::once_flag g_once_flag;
+
+ llvm::call_once(g_once_flag, []() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+ });
+}
+
+lldb_private::ConstString ProcessFreeBSD::GetPluginNameStatic() {
+ static ConstString g_name("freebsd");
+ return g_name;
+}
+
+const char *ProcessFreeBSD::GetPluginDescriptionStatic() {
+ return "Process plugin for FreeBSD";
+}
+
+// ProcessInterface protocol.
+
+lldb_private::ConstString ProcessFreeBSD::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ProcessFreeBSD::GetPluginVersion() { return 1; }
+
+void ProcessFreeBSD::Terminate() {}
+
+Status ProcessFreeBSD::DoDetach(bool keep_stopped) {
+ Status error;
+ if (keep_stopped) {
+ error.SetErrorString("Detaching with keep_stopped true is not currently "
+ "supported on FreeBSD.");
+ return error;
+ }
+
+ error = m_monitor->Detach(GetID());
+
+ if (error.Success())
+ SetPrivateState(eStateDetached);
+
+ return error;
+}
+
+Status ProcessFreeBSD::DoResume() {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+
+ SetPrivateState(eStateRunning);
+
+ std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
+ bool do_step = false;
+ bool software_single_step = !SupportHardwareSingleStepping();
+
+ for (tid_collection::const_iterator t_pos = m_run_tids.begin(),
+ t_end = m_run_tids.end();
+ t_pos != t_end; ++t_pos) {
+ m_monitor->ThreadSuspend(*t_pos, false);
+ }
+ for (tid_collection::const_iterator t_pos = m_step_tids.begin(),
+ t_end = m_step_tids.end();
+ t_pos != t_end; ++t_pos) {
+ m_monitor->ThreadSuspend(*t_pos, false);
+ do_step = true;
+ if (software_single_step) {
+ Status error = SetupSoftwareSingleStepping(*t_pos);
+ if (error.Fail())
+ return error;
+ }
+ }
+ for (tid_collection::const_iterator t_pos = m_suspend_tids.begin(),
+ t_end = m_suspend_tids.end();
+ t_pos != t_end; ++t_pos) {
+ m_monitor->ThreadSuspend(*t_pos, true);
+ // XXX Cannot PT_CONTINUE properly with suspended threads.
+ do_step = true;
+ }
+
+ if (log)
+ log->Printf("process %" PRIu64 " resuming (%s)", GetID(),
+ do_step ? "step" : "continue");
+ if (do_step && !software_single_step)
+ m_monitor->SingleStep(GetID(), m_resume_signo);
+ else
+ m_monitor->Resume(GetID(), m_resume_signo);
+
+ return Status();
+}
+
+bool ProcessFreeBSD::UpdateThreadList(ThreadList &old_thread_list,
+ ThreadList &new_thread_list) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ if (log)
+ log->Printf("ProcessFreeBSD::%s (pid = %" PRIu64 ")", __FUNCTION__,
+ GetID());
+
+ std::vector<lldb::pid_t> tds;
+ if (!GetMonitor().GetCurrentThreadIDs(tds)) {
+ return false;
+ }
+
+ ThreadList old_thread_list_copy(old_thread_list);
+ for (size_t i = 0; i < tds.size(); ++i) {
+ tid_t tid = tds[i];
+ ThreadSP thread_sp(old_thread_list_copy.RemoveThreadByID(tid, false));
+ if (!thread_sp) {
+ thread_sp.reset(new FreeBSDThread(*this, tid));
+ if (log)
+ log->Printf("ProcessFreeBSD::%s new tid = %" PRIu64, __FUNCTION__, tid);
+ } else {
+ if (log)
+ log->Printf("ProcessFreeBSD::%s existing tid = %" PRIu64, __FUNCTION__,
+ tid);
+ }
+ new_thread_list.AddThread(thread_sp);
+ }
+ for (size_t i = 0; i < old_thread_list_copy.GetSize(false); ++i) {
+ ThreadSP old_thread_sp(old_thread_list_copy.GetThreadAtIndex(i, false));
+ if (old_thread_sp) {
+ if (log)
+ log->Printf("ProcessFreeBSD::%s remove tid", __FUNCTION__);
+ }
+ }
+
+ return true;
+}
+
+Status ProcessFreeBSD::WillResume() {
+ m_resume_signo = 0;
+ m_suspend_tids.clear();
+ m_run_tids.clear();
+ m_step_tids.clear();
+ return Process::WillResume();
+}
+
+void ProcessFreeBSD::SendMessage(const ProcessMessage &message) {
+ std::lock_guard<std::recursive_mutex> guard(m_message_mutex);
+
+ switch (message.GetKind()) {
+ case ProcessMessage::eInvalidMessage:
+ return;
+
+ case ProcessMessage::eAttachMessage:
+ SetPrivateState(eStateStopped);
+ return;
+
+ case ProcessMessage::eLimboMessage:
+ case ProcessMessage::eExitMessage:
+ SetExitStatus(message.GetExitStatus(), NULL);
+ break;
+
+ case ProcessMessage::eSignalMessage:
+ case ProcessMessage::eSignalDeliveredMessage:
+ case ProcessMessage::eBreakpointMessage:
+ case ProcessMessage::eTraceMessage:
+ case ProcessMessage::eWatchpointMessage:
+ case ProcessMessage::eCrashMessage:
+ SetPrivateState(eStateStopped);
+ break;
+
+ case ProcessMessage::eNewThreadMessage:
+ llvm_unreachable("eNewThreadMessage unexpected on FreeBSD");
+ break;
+
+ case ProcessMessage::eExecMessage:
+ SetPrivateState(eStateStopped);
+ break;
+ }
+
+ m_message_queue.push(message);
+}
+
+// Constructors and destructors.
+
+ProcessFreeBSD::ProcessFreeBSD(lldb::TargetSP target_sp,
+ lldb::ListenerSP listener_sp,
+ UnixSignalsSP &unix_signals_sp)
+ : Process(target_sp, listener_sp, unix_signals_sp),
+ m_byte_order(endian::InlHostByteOrder()), m_monitor(NULL), m_module(NULL),
+ m_message_mutex(), m_exit_now(false), m_seen_initial_stop(),
+ m_resume_signo(0) {
+ // FIXME: Putting this code in the ctor and saving the byte order in a
+ // member variable is a hack to avoid const qual issues in GetByteOrder.
+ lldb::ModuleSP module = GetTarget().GetExecutableModule();
+ if (module && module->GetObjectFile())
+ m_byte_order = module->GetObjectFile()->GetByteOrder();
+}
+
+ProcessFreeBSD::~ProcessFreeBSD() { delete m_monitor; }
+
+// Process protocol.
+void ProcessFreeBSD::Finalize() {
+ Process::Finalize();
+
+ if (m_monitor)
+ m_monitor->StopMonitor();
+}
+
+bool ProcessFreeBSD::CanDebug(lldb::TargetSP target_sp,
+ bool plugin_specified_by_name) {
+ // For now we are just making sure the file exists for a given module
+ ModuleSP exe_module_sp(target_sp->GetExecutableModule());
+ if (exe_module_sp.get())
+ return FileSystem::Instance().Exists(exe_module_sp->GetFileSpec());
+ // If there is no executable module, we return true since we might be
+ // preparing to attach.
+ return true;
+}
+
+Status
+ProcessFreeBSD::DoAttachToProcessWithID(lldb::pid_t pid,
+ const ProcessAttachInfo &attach_info) {
+ Status error;
+ assert(m_monitor == NULL);
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ LLDB_LOGV(log, "pid = {0}", GetID());
+
+ m_monitor = new ProcessMonitor(this, pid, error);
+
+ if (!error.Success())
+ return error;
+
+ PlatformSP platform_sp(GetTarget().GetPlatform());
+ assert(platform_sp.get());
+ if (!platform_sp)
+ return error; // FIXME: Detatch?
+
+ // Find out what we can about this process
+ ProcessInstanceInfo process_info;
+ platform_sp->GetProcessInfo(pid, process_info);
+
+ // Resolve the executable module
+ ModuleSP exe_module_sp;
+ FileSpecList executable_search_paths(
+ Target::GetDefaultExecutableSearchPaths());
+ ModuleSpec exe_module_spec(process_info.GetExecutableFile(),
+ GetTarget().GetArchitecture());
+ error = platform_sp->ResolveExecutable(
+ exe_module_spec, exe_module_sp,
+ executable_search_paths.GetSize() ? &executable_search_paths : NULL);
+ if (!error.Success())
+ return error;
+
+ // Fix the target architecture if necessary
+ const ArchSpec &module_arch = exe_module_sp->GetArchitecture();
+ if (module_arch.IsValid() &&
+ !GetTarget().GetArchitecture().IsExactMatch(module_arch))
+ GetTarget().SetArchitecture(module_arch);
+
+ // Initialize the target module list
+ GetTarget().SetExecutableModule(exe_module_sp, eLoadDependentsYes);
+
+ SetSTDIOFileDescriptor(m_monitor->GetTerminalFD());
+
+ SetID(pid);
+
+ return error;
+}
+
+Status ProcessFreeBSD::WillLaunch(Module *module) {
+ Status error;
+ return error;
+}
+
+FileSpec
+ProcessFreeBSD::GetFileSpec(const lldb_private::FileAction *file_action,
+ const FileSpec &default_file_spec,
+ const FileSpec &dbg_pts_file_spec) {
+ FileSpec file_spec{};
+
+ if (file_action && file_action->GetAction() == FileAction::eFileActionOpen) {
+ file_spec = file_action->GetFileSpec();
+ // By default the stdio paths passed in will be pseudo-terminal (/dev/pts).
+ // If so, convert to using a different default path instead to redirect I/O
+ // to the debugger console. This should also handle user overrides to
+ // /dev/null or a different file.
+ if (!file_spec || file_spec == dbg_pts_file_spec)
+ file_spec = default_file_spec;
+ }
+ return file_spec;
+}
+
+Status ProcessFreeBSD::DoLaunch(Module *module,
+ ProcessLaunchInfo &launch_info) {
+ Status error;
+ assert(m_monitor == NULL);
+
+ FileSpec working_dir = launch_info.GetWorkingDirectory();
+ if (working_dir) {
+ FileSystem::Instance().Resolve(working_dir);
+ if (!FileSystem::Instance().IsDirectory(working_dir.GetPath())) {
+ error.SetErrorStringWithFormat("No such file or directory: %s",
+ working_dir.GetCString());
+ return error;
+ }
+ }
+
+ SetPrivateState(eStateLaunching);
+
+ const lldb_private::FileAction *file_action;
+
+ // Default of empty will mean to use existing open file descriptors
+ FileSpec stdin_file_spec{};
+ FileSpec stdout_file_spec{};
+ FileSpec stderr_file_spec{};
+
+ const FileSpec dbg_pts_file_spec{launch_info.GetPTY().GetSlaveName(NULL, 0)};
+
+ file_action = launch_info.GetFileActionForFD(STDIN_FILENO);
+ stdin_file_spec =
+ GetFileSpec(file_action, stdin_file_spec, dbg_pts_file_spec);
+
+ file_action = launch_info.GetFileActionForFD(STDOUT_FILENO);
+ stdout_file_spec =
+ GetFileSpec(file_action, stdout_file_spec, dbg_pts_file_spec);
+
+ file_action = launch_info.GetFileActionForFD(STDERR_FILENO);
+ stderr_file_spec =
+ GetFileSpec(file_action, stderr_file_spec, dbg_pts_file_spec);
+
+ m_monitor = new ProcessMonitor(
+ this, module, launch_info.GetArguments().GetConstArgumentVector(),
+ launch_info.GetEnvironment(), stdin_file_spec, stdout_file_spec,
+ stderr_file_spec, working_dir, launch_info, error);
+
+ m_module = module;
+
+ if (!error.Success())
+ return error;
+
+ int terminal = m_monitor->GetTerminalFD();
+ if (terminal >= 0) {
+// The reader thread will close the file descriptor when done, so we pass it a
+// copy.
+#ifdef F_DUPFD_CLOEXEC
+ int stdio = fcntl(terminal, F_DUPFD_CLOEXEC, 0);
+ if (stdio == -1) {
+ error.SetErrorToErrno();
+ return error;
+ }
+#else
+ // Special case when F_DUPFD_CLOEXEC does not exist (Debian kFreeBSD)
+ int stdio = fcntl(terminal, F_DUPFD, 0);
+ if (stdio == -1) {
+ error.SetErrorToErrno();
+ return error;
+ }
+ stdio = fcntl(terminal, F_SETFD, FD_CLOEXEC);
+ if (stdio == -1) {
+ error.SetErrorToErrno();
+ return error;
+ }
+#endif
+ SetSTDIOFileDescriptor(stdio);
+ }
+
+ SetID(m_monitor->GetPID());
+ return error;
+}
+
+void ProcessFreeBSD::DidLaunch() {}
+
+addr_t ProcessFreeBSD::GetImageInfoAddress() {
+ Target *target = &GetTarget();
+ ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile();
+ Address addr = obj_file->GetImageInfoAddress(target);
+
+ if (addr.IsValid())
+ return addr.GetLoadAddress(target);
+ return LLDB_INVALID_ADDRESS;
+}
+
+Status ProcessFreeBSD::DoHalt(bool &caused_stop) {
+ Status error;
+
+ if (IsStopped()) {
+ caused_stop = false;
+ } else if (kill(GetID(), SIGSTOP)) {
+ caused_stop = false;
+ error.SetErrorToErrno();
+ } else {
+ caused_stop = true;
+ }
+ return error;
+}
+
+Status ProcessFreeBSD::DoSignal(int signal) {
+ Status error;
+
+ if (kill(GetID(), signal))
+ error.SetErrorToErrno();
+
+ return error;
+}
+
+Status ProcessFreeBSD::DoDestroy() {
+ Status error;
+
+ if (!HasExited()) {
+ assert(m_monitor);
+ m_exit_now = true;
+ if (GetID() == LLDB_INVALID_PROCESS_ID) {
+ error.SetErrorString("invalid process id");
+ return error;
+ }
+ if (!m_monitor->Kill()) {
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ SetPrivateState(eStateExited);
+ }
+
+ return error;
+}
+
+void ProcessFreeBSD::DoDidExec() {
+ Target *target = &GetTarget();
+ if (target) {
+ PlatformSP platform_sp(target->GetPlatform());
+ assert(platform_sp.get());
+ if (platform_sp) {
+ ProcessInstanceInfo process_info;
+ platform_sp->GetProcessInfo(GetID(), process_info);
+ ModuleSP exe_module_sp;
+ ModuleSpec exe_module_spec(process_info.GetExecutableFile(),
+ target->GetArchitecture());
+ FileSpecList executable_search_paths(
+ Target::GetDefaultExecutableSearchPaths());
+ Status error = platform_sp->ResolveExecutable(
+ exe_module_spec, exe_module_sp,
+ executable_search_paths.GetSize() ? &executable_search_paths : NULL);
+ if (!error.Success())
+ return;
+ target->SetExecutableModule(exe_module_sp, eLoadDependentsYes);
+ }
+ }
+}
+
+bool ProcessFreeBSD::AddThreadForInitialStopIfNeeded(lldb::tid_t stop_tid) {
+ bool added_to_set = false;
+ ThreadStopSet::iterator it = m_seen_initial_stop.find(stop_tid);
+ if (it == m_seen_initial_stop.end()) {
+ m_seen_initial_stop.insert(stop_tid);
+ added_to_set = true;
+ }
+ return added_to_set;
+}
+
+bool ProcessFreeBSD::WaitingForInitialStop(lldb::tid_t stop_tid) {
+ return (m_seen_initial_stop.find(stop_tid) == m_seen_initial_stop.end());
+}
+
+FreeBSDThread *
+ProcessFreeBSD::CreateNewFreeBSDThread(lldb_private::Process &process,
+ lldb::tid_t tid) {
+ return new FreeBSDThread(process, tid);
+}
+
+void ProcessFreeBSD::RefreshStateAfterStop() {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ LLDB_LOGV(log, "message_queue size = {0}", m_message_queue.size());
+
+ std::lock_guard<std::recursive_mutex> guard(m_message_mutex);
+
+ // This method used to only handle one message. Changing it to loop allows
+ // it to handle the case where we hit a breakpoint while handling a different
+ // breakpoint.
+ while (!m_message_queue.empty()) {
+ ProcessMessage &message = m_message_queue.front();
+
+ // Resolve the thread this message corresponds to and pass it along.
+ lldb::tid_t tid = message.GetTID();
+ LLDB_LOGV(log, " message_queue size = {0}, pid = {1}",
+ m_message_queue.size(), tid);
+
+ m_thread_list.RefreshStateAfterStop();
+
+ FreeBSDThread *thread = static_cast<FreeBSDThread *>(
+ GetThreadList().FindThreadByID(tid, false).get());
+ if (thread)
+ thread->Notify(message);
+
+ if (message.GetKind() == ProcessMessage::eExitMessage) {
+ // FIXME: We should tell the user about this, but the limbo message is
+ // probably better for that.
+ LLDB_LOG(log, "removing thread, tid = {0}", tid);
+ std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
+
+ ThreadSP thread_sp = m_thread_list.RemoveThreadByID(tid, false);
+ thread_sp.reset();
+ m_seen_initial_stop.erase(tid);
+ }
+
+ m_message_queue.pop();
+ }
+}
+
+bool ProcessFreeBSD::IsAlive() {
+ StateType state = GetPrivateState();
+ return state != eStateDetached && state != eStateExited &&
+ state != eStateInvalid && state != eStateUnloaded;
+}
+
+size_t ProcessFreeBSD::DoReadMemory(addr_t vm_addr, void *buf, size_t size,
+ Status &error) {
+ assert(m_monitor);
+ return m_monitor->ReadMemory(vm_addr, buf, size, error);
+}
+
+size_t ProcessFreeBSD::DoWriteMemory(addr_t vm_addr, const void *buf,
+ size_t size, Status &error) {
+ assert(m_monitor);
+ return m_monitor->WriteMemory(vm_addr, buf, size, error);
+}
+
+addr_t ProcessFreeBSD::DoAllocateMemory(size_t size, uint32_t permissions,
+ Status &error) {
+ addr_t allocated_addr = LLDB_INVALID_ADDRESS;
+
+ unsigned prot = 0;
+ if (permissions & lldb::ePermissionsReadable)
+ prot |= eMmapProtRead;
+ if (permissions & lldb::ePermissionsWritable)
+ prot |= eMmapProtWrite;
+ if (permissions & lldb::ePermissionsExecutable)
+ prot |= eMmapProtExec;
+
+ if (InferiorCallMmap(this, allocated_addr, 0, size, prot,
+ eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) {
+ m_addr_to_mmap_size[allocated_addr] = size;
+ error.Clear();
+ } else {
+ allocated_addr = LLDB_INVALID_ADDRESS;
+ error.SetErrorStringWithFormat(
+ "unable to allocate %zu bytes of memory with permissions %s", size,
+ GetPermissionsAsCString(permissions));
+ }
+
+ return allocated_addr;
+}
+
+Status ProcessFreeBSD::DoDeallocateMemory(lldb::addr_t addr) {
+ Status error;
+ MMapMap::iterator pos = m_addr_to_mmap_size.find(addr);
+ if (pos != m_addr_to_mmap_size.end() &&
+ InferiorCallMunmap(this, addr, pos->second))
+ m_addr_to_mmap_size.erase(pos);
+ else
+ error.SetErrorStringWithFormat("unable to deallocate memory at 0x%" PRIx64,
+ addr);
+
+ return error;
+}
+
+size_t
+ProcessFreeBSD::GetSoftwareBreakpointTrapOpcode(BreakpointSite *bp_site) {
+ static const uint8_t g_aarch64_opcode[] = {0x00, 0x00, 0x20, 0xD4};
+ static const uint8_t g_i386_opcode[] = {0xCC};
+
+ ArchSpec arch = GetTarget().GetArchitecture();
+ const uint8_t *opcode = NULL;
+ size_t opcode_size = 0;
+
+ switch (arch.GetMachine()) {
+ default:
+ assert(false && "CPU type not supported!");
+ break;
+
+ case llvm::Triple::arm: {
+ // The ARM reference recommends the use of 0xe7fddefe and 0xdefe but the
+ // linux kernel does otherwise.
+ static const uint8_t g_arm_breakpoint_opcode[] = {0xf0, 0x01, 0xf0, 0xe7};
+ static const uint8_t g_thumb_breakpoint_opcode[] = {0x01, 0xde};
+
+ lldb::BreakpointLocationSP bp_loc_sp(bp_site->GetOwnerAtIndex(0));
+ AddressClass addr_class = AddressClass::eUnknown;
+
+ if (bp_loc_sp)
+ addr_class = bp_loc_sp->GetAddress().GetAddressClass();
+
+ if (addr_class == AddressClass::eCodeAlternateISA ||
+ (addr_class == AddressClass::eUnknown &&
+ bp_loc_sp->GetAddress().GetOffset() & 1)) {
+ opcode = g_thumb_breakpoint_opcode;
+ opcode_size = sizeof(g_thumb_breakpoint_opcode);
+ } else {
+ opcode = g_arm_breakpoint_opcode;
+ opcode_size = sizeof(g_arm_breakpoint_opcode);
+ }
+ } break;
+ case llvm::Triple::aarch64:
+ opcode = g_aarch64_opcode;
+ opcode_size = sizeof(g_aarch64_opcode);
+ break;
+
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ opcode = g_i386_opcode;
+ opcode_size = sizeof(g_i386_opcode);
+ break;
+ }
+
+ bp_site->SetTrapOpcode(opcode, opcode_size);
+ return opcode_size;
+}
+
+Status ProcessFreeBSD::EnableBreakpointSite(BreakpointSite *bp_site) {
+ return EnableSoftwareBreakpoint(bp_site);
+}
+
+Status ProcessFreeBSD::DisableBreakpointSite(BreakpointSite *bp_site) {
+ return DisableSoftwareBreakpoint(bp_site);
+}
+
+Status ProcessFreeBSD::EnableWatchpoint(Watchpoint *wp, bool notify) {
+ Status error;
+ if (wp) {
+ user_id_t watchID = wp->GetID();
+ addr_t addr = wp->GetLoadAddress();
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf("ProcessFreeBSD::EnableWatchpoint(watchID = %" PRIu64 ")",
+ watchID);
+ if (wp->IsEnabled()) {
+ if (log)
+ log->Printf("ProcessFreeBSD::EnableWatchpoint(watchID = %" PRIu64
+ ") addr = 0x%8.8" PRIx64 ": watchpoint already enabled.",
+ watchID, (uint64_t)addr);
+ return error;
+ }
+
+ // Try to find a vacant watchpoint slot in the inferiors' main thread
+ uint32_t wp_hw_index = LLDB_INVALID_INDEX32;
+ std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
+ FreeBSDThread *thread = static_cast<FreeBSDThread *>(
+ m_thread_list.GetThreadAtIndex(0, false).get());
+
+ if (thread)
+ wp_hw_index = thread->FindVacantWatchpointIndex();
+
+ if (wp_hw_index == LLDB_INVALID_INDEX32) {
+ error.SetErrorString("Setting hardware watchpoint failed.");
+ } else {
+ wp->SetHardwareIndex(wp_hw_index);
+ bool wp_enabled = true;
+ uint32_t thread_count = m_thread_list.GetSize(false);
+ for (uint32_t i = 0; i < thread_count; ++i) {
+ thread = static_cast<FreeBSDThread *>(
+ m_thread_list.GetThreadAtIndex(i, false).get());
+ if (thread)
+ wp_enabled &= thread->EnableHardwareWatchpoint(wp);
+ else
+ wp_enabled = false;
+ }
+ if (wp_enabled) {
+ wp->SetEnabled(true, notify);
+ return error;
+ } else {
+ // Watchpoint enabling failed on at least one of the threads so roll
+ // back all of them
+ DisableWatchpoint(wp, false);
+ error.SetErrorString("Setting hardware watchpoint failed");
+ }
+ }
+ } else
+ error.SetErrorString("Watchpoint argument was NULL.");
+ return error;
+}
+
+Status ProcessFreeBSD::DisableWatchpoint(Watchpoint *wp, bool notify) {
+ Status error;
+ if (wp) {
+ user_id_t watchID = wp->GetID();
+ addr_t addr = wp->GetLoadAddress();
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf("ProcessFreeBSD::DisableWatchpoint(watchID = %" PRIu64 ")",
+ watchID);
+ if (!wp->IsEnabled()) {
+ if (log)
+ log->Printf("ProcessFreeBSD::DisableWatchpoint(watchID = %" PRIu64
+ ") addr = 0x%8.8" PRIx64 ": watchpoint already disabled.",
+ watchID, (uint64_t)addr);
+ // This is needed (for now) to keep watchpoints disabled correctly
+ wp->SetEnabled(false, notify);
+ return error;
+ }
+
+ if (wp->IsHardware()) {
+ bool wp_disabled = true;
+ std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
+ uint32_t thread_count = m_thread_list.GetSize(false);
+ for (uint32_t i = 0; i < thread_count; ++i) {
+ FreeBSDThread *thread = static_cast<FreeBSDThread *>(
+ m_thread_list.GetThreadAtIndex(i, false).get());
+ if (thread)
+ wp_disabled &= thread->DisableHardwareWatchpoint(wp);
+ else
+ wp_disabled = false;
+ }
+ if (wp_disabled) {
+ wp->SetHardwareIndex(LLDB_INVALID_INDEX32);
+ wp->SetEnabled(false, notify);
+ return error;
+ } else
+ error.SetErrorString("Disabling hardware watchpoint failed");
+ }
+ } else
+ error.SetErrorString("Watchpoint argument was NULL.");
+ return error;
+}
+
+Status ProcessFreeBSD::GetWatchpointSupportInfo(uint32_t &num) {
+ Status error;
+ std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
+ FreeBSDThread *thread = static_cast<FreeBSDThread *>(
+ m_thread_list.GetThreadAtIndex(0, false).get());
+ if (thread)
+ num = thread->NumSupportedHardwareWatchpoints();
+ else
+ error.SetErrorString("Process does not exist.");
+ return error;
+}
+
+Status ProcessFreeBSD::GetWatchpointSupportInfo(uint32_t &num, bool &after) {
+ Status error = GetWatchpointSupportInfo(num);
+ // Watchpoints trigger and halt the inferior after the corresponding
+ // instruction has been executed.
+ after = true;
+ return error;
+}
+
+uint32_t ProcessFreeBSD::UpdateThreadListIfNeeded() {
+ std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
+ // Do not allow recursive updates.
+ return m_thread_list.GetSize(false);
+}
+
+ByteOrder ProcessFreeBSD::GetByteOrder() const {
+ // FIXME: We should be able to extract this value directly. See comment in
+ // ProcessFreeBSD().
+ return m_byte_order;
+}
+
+size_t ProcessFreeBSD::PutSTDIN(const char *buf, size_t len, Status &error) {
+ ssize_t status;
+ if ((status = write(m_monitor->GetTerminalFD(), buf, len)) < 0) {
+ error.SetErrorToErrno();
+ return 0;
+ }
+ return status;
+}
+
+// Utility functions.
+
+bool ProcessFreeBSD::HasExited() {
+ switch (GetPrivateState()) {
+ default:
+ break;
+
+ case eStateDetached:
+ case eStateExited:
+ return true;
+ }
+
+ return false;
+}
+
+bool ProcessFreeBSD::IsStopped() {
+ switch (GetPrivateState()) {
+ default:
+ break;
+
+ case eStateStopped:
+ case eStateCrashed:
+ case eStateSuspended:
+ return true;
+ }
+
+ return false;
+}
+
+bool ProcessFreeBSD::IsAThreadRunning() {
+ bool is_running = false;
+ std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
+ uint32_t thread_count = m_thread_list.GetSize(false);
+ for (uint32_t i = 0; i < thread_count; ++i) {
+ FreeBSDThread *thread = static_cast<FreeBSDThread *>(
+ m_thread_list.GetThreadAtIndex(i, false).get());
+ StateType thread_state = thread->GetState();
+ if (thread_state == eStateRunning || thread_state == eStateStepping) {
+ is_running = true;
+ break;
+ }
+ }
+ return is_running;
+}
+
+lldb_private::DataExtractor ProcessFreeBSD::GetAuxvData() {
+ // If we're the local platform, we can ask the host for auxv data.
+ PlatformSP platform_sp = GetTarget().GetPlatform();
+ assert(platform_sp && platform_sp->IsHost());
+
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_AUXV, (int)m_process->GetID()};
+ size_t auxv_size = AT_COUNT * sizeof(Elf_Auxinfo);
+ DataBufferSP buf_sp(new DataBufferHeap(auxv_size, 0));
+
+ if (::sysctl(mib, 4, buf_sp->GetBytes(), &auxv_size, NULL, 0) != 0) {
+ perror("sysctl failed on auxv");
+ buf_sp.reset();
+ }
+
+ return DataExtractor(buf_sp, GetByteOrder(), GetAddressByteSize());
+}
+
+struct EmulatorBaton {
+ ProcessFreeBSD *m_process;
+ RegisterContext *m_reg_context;
+
+ // eRegisterKindDWARF -> RegisterValue
+ std::unordered_map<uint32_t, RegisterValue> m_register_values;
+
+ EmulatorBaton(ProcessFreeBSD *process, RegisterContext *reg_context)
+ : m_process(process), m_reg_context(reg_context) {}
+};
+
+static size_t ReadMemoryCallback(EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context,
+ lldb::addr_t addr, void *dst, size_t length) {
+ EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);
+
+ Status error;
+ size_t bytes_read =
+ emulator_baton->m_process->DoReadMemory(addr, dst, length, error);
+ if (!error.Success())
+ bytes_read = 0;
+ return bytes_read;
+}
+
+static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton,
+ const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+ EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);
+
+ auto it = emulator_baton->m_register_values.find(
+ reg_info->kinds[eRegisterKindDWARF]);
+ if (it != emulator_baton->m_register_values.end()) {
+ reg_value = it->second;
+ return true;
+ }
+
+ // The emulator only fills in the dwarf register numbers (and in some cases
+ // the generic register numbers). Get the full register info from the
+ // register context based on the dwarf register numbers.
+ const RegisterInfo *full_reg_info =
+ emulator_baton->m_reg_context->GetRegisterInfo(
+ eRegisterKindDWARF, reg_info->kinds[eRegisterKindDWARF]);
+
+ bool error =
+ emulator_baton->m_reg_context->ReadRegister(full_reg_info, reg_value);
+ return error;
+}
+
+static bool WriteRegisterCallback(EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context,
+ const RegisterInfo *reg_info,
+ const RegisterValue &reg_value) {
+ EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);
+ emulator_baton->m_register_values[reg_info->kinds[eRegisterKindDWARF]] =
+ reg_value;
+ return true;
+}
+
+static size_t WriteMemoryCallback(EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context,
+ lldb::addr_t addr, const void *dst,
+ size_t length) {
+ return length;
+}
+
+bool ProcessFreeBSD::SingleStepBreakpointHit(
+ void *baton, lldb_private::StoppointCallbackContext *context,
+ lldb::user_id_t break_id, lldb::user_id_t break_loc_id) {
+ return false;
+}
+
+Status ProcessFreeBSD::SetSoftwareSingleStepBreakpoint(lldb::tid_t tid,
+ lldb::addr_t addr) {
+ Status error;
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ if (log) {
+ log->Printf("ProcessFreeBSD::%s addr = 0x%" PRIx64, __FUNCTION__, addr);
+ log->Printf("SoftwareBreakpoint::%s addr = 0x%" PRIx64, __FUNCTION__, addr);
+ }
+
+ // Validate the address.
+ if (addr == LLDB_INVALID_ADDRESS)
+ return Status("ProcessFreeBSD::%s invalid load address specified.",
+ __FUNCTION__);
+
+ Breakpoint *const sw_step_break =
+ m_process->GetTarget().CreateBreakpoint(addr, true, false).get();
+ sw_step_break->SetCallback(SingleStepBreakpointHit, this, true);
+ sw_step_break->SetBreakpointKind("software-signle-step");
+
+ if (log)
+ log->Printf("ProcessFreeBSD::%s addr = 0x%" PRIx64 " -- SUCCESS",
+ __FUNCTION__, addr);
+
+ m_threads_stepping_with_breakpoint.insert({tid, sw_step_break->GetID()});
+ return Status();
+}
+
+bool ProcessFreeBSD::IsSoftwareStepBreakpoint(lldb::tid_t tid) {
+ ThreadSP thread = GetThreadList().FindThreadByID(tid);
+ if (!thread)
+ return false;
+
+ assert(thread->GetRegisterContext());
+ lldb::addr_t stop_pc = thread->GetRegisterContext()->GetPC();
+
+ const auto &iter = m_threads_stepping_with_breakpoint.find(tid);
+ if (iter == m_threads_stepping_with_breakpoint.end())
+ return false;
+
+ lldb::break_id_t bp_id = iter->second;
+ BreakpointSP bp = GetTarget().GetBreakpointByID(bp_id);
+ if (!bp)
+ return false;
+
+ BreakpointLocationSP bp_loc = bp->FindLocationByAddress(stop_pc);
+ if (!bp_loc)
+ return false;
+
+ GetTarget().RemoveBreakpointByID(bp_id);
+ m_threads_stepping_with_breakpoint.erase(tid);
+ return true;
+}
+
+bool ProcessFreeBSD::SupportHardwareSingleStepping() const {
+ lldb_private::ArchSpec arch = GetTarget().GetArchitecture();
+ if (arch.GetMachine() == llvm::Triple::arm || arch.IsMIPS())
+ return false;
+ return true;
+}
+
+Status ProcessFreeBSD::SetupSoftwareSingleStepping(lldb::tid_t tid) {
+ std::unique_ptr<EmulateInstruction> emulator_up(
+ EmulateInstruction::FindPlugin(GetTarget().GetArchitecture(),
+ eInstructionTypePCModifying, nullptr));
+
+ if (emulator_up == nullptr)
+ return Status("Instruction emulator not found!");
+
+ FreeBSDThread *thread = static_cast<FreeBSDThread *>(
+ m_thread_list.FindThreadByID(tid, false).get());
+ if (thread == NULL)
+ return Status("Thread not found not found!");
+
+ lldb::RegisterContextSP register_context_sp = thread->GetRegisterContext();
+
+ EmulatorBaton baton(this, register_context_sp.get());
+ emulator_up->SetBaton(&baton);
+ emulator_up->SetReadMemCallback(&ReadMemoryCallback);
+ emulator_up->SetReadRegCallback(&ReadRegisterCallback);
+ emulator_up->SetWriteMemCallback(&WriteMemoryCallback);
+ emulator_up->SetWriteRegCallback(&WriteRegisterCallback);
+
+ if (!emulator_up->ReadInstruction())
+ return Status("Read instruction failed!");
+
+ bool emulation_result =
+ emulator_up->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC);
+ const RegisterInfo *reg_info_pc = register_context_sp->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ auto pc_it =
+ baton.m_register_values.find(reg_info_pc->kinds[eRegisterKindDWARF]);
+
+ lldb::addr_t next_pc;
+ if (emulation_result) {
+ assert(pc_it != baton.m_register_values.end() &&
+ "Emulation was successful but PC wasn't updated");
+ next_pc = pc_it->second.GetAsUInt64();
+ } else if (pc_it == baton.m_register_values.end()) {
+ // Emulate instruction failed and it haven't changed PC. Advance PC with
+ // the size of the current opcode because the emulation of all
+ // PC modifying instruction should be successful. The failure most
+ // likely caused by a not supported instruction which don't modify PC.
+ next_pc =
+ register_context_sp->GetPC() + emulator_up->GetOpcode().GetByteSize();
+ } else {
+ // The instruction emulation failed after it modified the PC. It is an
+ // unknown error where we can't continue because the next instruction is
+ // modifying the PC but we don't know how.
+ return Status("Instruction emulation failed unexpectedly");
+ }
+
+ SetSoftwareSingleStepBreakpoint(tid, next_pc);
+ return Status();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h
new file mode 100644
index 000000000000..536da0c0aa70
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h
@@ -0,0 +1,220 @@
+//===-- ProcessFreeBSD.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 liblldb_ProcessFreeBSD_H_
+#define liblldb_ProcessFreeBSD_H_
+
+#include "Plugins/Process/POSIX/ProcessMessage.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/ThreadList.h"
+#include <mutex>
+#include <queue>
+#include <set>
+
+class ProcessMonitor;
+class FreeBSDThread;
+
+class ProcessFreeBSD : public lldb_private::Process {
+
+public:
+ // Static functions.
+ static lldb::ProcessSP
+ CreateInstance(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp,
+ const lldb_private::FileSpec *crash_file_path);
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ // Constructors and destructors
+ ProcessFreeBSD(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp,
+ lldb::UnixSignalsSP &unix_signals_sp);
+
+ ~ProcessFreeBSD();
+
+ virtual lldb_private::Status WillResume() override;
+
+ // PluginInterface protocol
+ virtual lldb_private::ConstString GetPluginName() override;
+
+ virtual uint32_t GetPluginVersion() override;
+
+public:
+ // Process protocol.
+ void Finalize() override;
+
+ bool CanDebug(lldb::TargetSP target_sp,
+ bool plugin_specified_by_name) override;
+
+ lldb_private::Status WillLaunch(lldb_private::Module *module) override;
+
+ lldb_private::Status DoAttachToProcessWithID(
+ lldb::pid_t pid,
+ const lldb_private::ProcessAttachInfo &attach_info) override;
+
+ lldb_private::Status
+ DoLaunch(lldb_private::Module *exe_module,
+ lldb_private::ProcessLaunchInfo &launch_info) override;
+
+ void DidLaunch() override;
+
+ lldb_private::Status DoResume() override;
+
+ lldb_private::Status DoHalt(bool &caused_stop) override;
+
+ lldb_private::Status DoDetach(bool keep_stopped) override;
+
+ lldb_private::Status DoSignal(int signal) override;
+
+ lldb_private::Status DoDestroy() override;
+
+ void DoDidExec() override;
+
+ void RefreshStateAfterStop() override;
+
+ bool IsAlive() override;
+
+ size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
+ lldb_private::Status &error) override;
+
+ size_t DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size,
+ lldb_private::Status &error) override;
+
+ lldb::addr_t DoAllocateMemory(size_t size, uint32_t permissions,
+ lldb_private::Status &error) override;
+
+ lldb_private::Status DoDeallocateMemory(lldb::addr_t ptr) override;
+
+ virtual size_t
+ GetSoftwareBreakpointTrapOpcode(lldb_private::BreakpointSite *bp_site);
+
+ lldb_private::Status
+ EnableBreakpointSite(lldb_private::BreakpointSite *bp_site) override;
+
+ lldb_private::Status
+ DisableBreakpointSite(lldb_private::BreakpointSite *bp_site) override;
+
+ lldb_private::Status EnableWatchpoint(lldb_private::Watchpoint *wp,
+ bool notify = true) override;
+
+ lldb_private::Status DisableWatchpoint(lldb_private::Watchpoint *wp,
+ bool notify = true) override;
+
+ lldb_private::Status GetWatchpointSupportInfo(uint32_t &num) override;
+
+ lldb_private::Status GetWatchpointSupportInfo(uint32_t &num,
+ bool &after) override;
+
+ virtual uint32_t UpdateThreadListIfNeeded();
+
+ bool UpdateThreadList(lldb_private::ThreadList &old_thread_list,
+ lldb_private::ThreadList &new_thread_list) override;
+
+ virtual lldb::ByteOrder GetByteOrder() const;
+
+ lldb::addr_t GetImageInfoAddress() override;
+
+ size_t PutSTDIN(const char *buf, size_t len,
+ lldb_private::Status &error) override;
+
+ lldb_private::DataExtractor GetAuxvData() override;
+
+ // ProcessFreeBSD internal API.
+
+ /// Registers the given message with this process.
+ virtual void SendMessage(const ProcessMessage &message);
+
+ ProcessMonitor &GetMonitor() {
+ assert(m_monitor);
+ return *m_monitor;
+ }
+
+ lldb_private::FileSpec
+ GetFileSpec(const lldb_private::FileAction *file_action,
+ const lldb_private::FileSpec &default_file_spec,
+ const lldb_private::FileSpec &dbg_pts_file_spec);
+
+ /// Adds the thread to the list of threads for which we have received the
+ /// initial stopping signal.
+ /// The \p stop_tid parameter indicates the thread which the stop happened
+ /// for.
+ bool AddThreadForInitialStopIfNeeded(lldb::tid_t stop_tid);
+
+ bool WaitingForInitialStop(lldb::tid_t stop_tid);
+
+ virtual FreeBSDThread *CreateNewFreeBSDThread(lldb_private::Process &process,
+ lldb::tid_t tid);
+
+ static bool SingleStepBreakpointHit(
+ void *baton, lldb_private::StoppointCallbackContext *context,
+ lldb::user_id_t break_id, lldb::user_id_t break_loc_id);
+
+ lldb_private::Status SetupSoftwareSingleStepping(lldb::tid_t tid);
+
+ lldb_private::Status SetSoftwareSingleStepBreakpoint(lldb::tid_t tid,
+ lldb::addr_t addr);
+
+ bool IsSoftwareStepBreakpoint(lldb::tid_t tid);
+
+ bool SupportHardwareSingleStepping() const;
+
+ typedef std::vector<lldb::tid_t> tid_collection;
+ tid_collection &GetStepTids() { return m_step_tids; }
+
+protected:
+ static const size_t MAX_TRAP_OPCODE_SIZE = 8;
+
+ /// Target byte order.
+ lldb::ByteOrder m_byte_order;
+
+ /// Process monitor;
+ ProcessMonitor *m_monitor;
+
+ /// The module we are executing.
+ lldb_private::Module *m_module;
+
+ /// Message queue notifying this instance of inferior process state changes.
+ std::recursive_mutex m_message_mutex;
+ std::queue<ProcessMessage> m_message_queue;
+
+ /// Drive any exit events to completion.
+ bool m_exit_now;
+
+ /// Returns true if the process has exited.
+ bool HasExited();
+
+ /// Returns true if the process is stopped.
+ bool IsStopped();
+
+ /// Returns true if at least one running is currently running
+ bool IsAThreadRunning();
+
+ typedef std::map<lldb::addr_t, lldb::addr_t> MMapMap;
+ MMapMap m_addr_to_mmap_size;
+
+ typedef std::set<lldb::tid_t> ThreadStopSet;
+ /// Every thread begins with a stop signal. This keeps track
+ /// of the threads for which we have received the stop signal.
+ ThreadStopSet m_seen_initial_stop;
+
+ friend class FreeBSDThread;
+
+ tid_collection m_suspend_tids;
+ tid_collection m_run_tids;
+ tid_collection m_step_tids;
+ std::map<lldb::tid_t, lldb::break_id_t> m_threads_stepping_with_breakpoint;
+
+ int m_resume_signo;
+};
+
+#endif // liblldb_ProcessFreeBSD_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp
new file mode 100644
index 000000000000..2778ae13a9f2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp
@@ -0,0 +1,1437 @@
+//===-- ProcessMonitor.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 <poll.h>
+#include <signal.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "lldb/Host/Host.h"
+#include "lldb/Host/PseudoTerminal.h"
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+#include "llvm/Support/Errno.h"
+
+#include "FreeBSDThread.h"
+#include "Plugins/Process/POSIX/CrashReason.h"
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
+#include "ProcessFreeBSD.h"
+#include "ProcessMonitor.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// We disable the tracing of ptrace calls for integration builds to avoid the
+// additional indirection and checks.
+#ifndef LLDB_CONFIGURATION_BUILDANDINTEGRATION
+// Wrapper for ptrace to catch errors and log calls.
+
+const char *Get_PT_IO_OP(int op) {
+ switch (op) {
+ case PIOD_READ_D:
+ return "READ_D";
+ case PIOD_WRITE_D:
+ return "WRITE_D";
+ case PIOD_READ_I:
+ return "READ_I";
+ case PIOD_WRITE_I:
+ return "WRITE_I";
+ default:
+ return "Unknown op";
+ }
+}
+
+// Wrapper for ptrace to catch errors and log calls. Note that ptrace sets
+// errno on error because -1 is reserved as a valid result.
+extern long PtraceWrapper(int req, lldb::pid_t pid, void *addr, int data,
+ const char *reqName, const char *file, int line) {
+ long int result;
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+
+ if (log) {
+ log->Printf("ptrace(%s, %" PRIu64 ", %p, %x) called from file %s line %d",
+ reqName, pid, addr, data, file, line);
+ if (req == PT_IO) {
+ struct ptrace_io_desc *pi = (struct ptrace_io_desc *)addr;
+
+ log->Printf("PT_IO: op=%s offs=%zx size=%zu", Get_PT_IO_OP(pi->piod_op),
+ (size_t)pi->piod_offs, pi->piod_len);
+ }
+ }
+
+ // PtraceDisplayBytes(req, data);
+
+ errno = 0;
+ result = ptrace(req, pid, (caddr_t)addr, data);
+
+ // PtraceDisplayBytes(req, data);
+
+ if (log && errno != 0) {
+ const char *str;
+ switch (errno) {
+ case ESRCH:
+ str = "ESRCH";
+ break;
+ case EINVAL:
+ str = "EINVAL";
+ break;
+ case EBUSY:
+ str = "EBUSY";
+ break;
+ case EPERM:
+ str = "EPERM";
+ break;
+ default:
+ str = "<unknown>";
+ }
+ log->Printf("ptrace() failed; errno=%d (%s)", errno, str);
+ }
+
+ if (log) {
+#ifdef __amd64__
+ if (req == PT_GETREGS) {
+ struct reg *r = (struct reg *)addr;
+
+ log->Printf("PT_GETREGS: rip=0x%lx rsp=0x%lx rbp=0x%lx rax=0x%lx",
+ r->r_rip, r->r_rsp, r->r_rbp, r->r_rax);
+ }
+ if (req == PT_GETDBREGS || req == PT_SETDBREGS) {
+ struct dbreg *r = (struct dbreg *)addr;
+ char setget = (req == PT_GETDBREGS) ? 'G' : 'S';
+
+ for (int i = 0; i <= 7; i++)
+ log->Printf("PT_%cETDBREGS: dr[%d]=0x%lx", setget, i, r->dr[i]);
+ }
+#endif
+ }
+
+ return result;
+}
+
+// Wrapper for ptrace when logging is not required. Sets errno to 0 prior to
+// calling ptrace.
+extern long PtraceWrapper(int req, lldb::pid_t pid, void *addr, int data) {
+ long result = 0;
+ errno = 0;
+ result = ptrace(req, pid, (caddr_t)addr, data);
+ return result;
+}
+
+#define PTRACE(req, pid, addr, data) \
+ PtraceWrapper((req), (pid), (addr), (data), #req, __FILE__, __LINE__)
+#else
+PtraceWrapper((req), (pid), (addr), (data))
+#endif
+
+// Static implementations of ProcessMonitor::ReadMemory and
+// ProcessMonitor::WriteMemory. This enables mutual recursion between these
+// functions without needed to go thru the thread funnel.
+
+static size_t DoReadMemory(lldb::pid_t pid, lldb::addr_t vm_addr, void *buf,
+ size_t size, Status &error) {
+ struct ptrace_io_desc pi_desc;
+
+ pi_desc.piod_op = PIOD_READ_D;
+ pi_desc.piod_offs = (void *)vm_addr;
+ pi_desc.piod_addr = buf;
+ pi_desc.piod_len = size;
+
+ if (PTRACE(PT_IO, pid, (caddr_t)&pi_desc, 0) < 0) {
+ error.SetErrorToErrno();
+ return 0;
+ }
+ return pi_desc.piod_len;
+}
+
+static size_t DoWriteMemory(lldb::pid_t pid, lldb::addr_t vm_addr,
+ const void *buf, size_t size, Status &error) {
+ struct ptrace_io_desc pi_desc;
+
+ pi_desc.piod_op = PIOD_WRITE_D;
+ pi_desc.piod_offs = (void *)vm_addr;
+ pi_desc.piod_addr = const_cast<void *>(buf);
+ pi_desc.piod_len = size;
+
+ if (PTRACE(PT_IO, pid, (caddr_t)&pi_desc, 0) < 0) {
+ error.SetErrorToErrno();
+ return 0;
+ }
+ return pi_desc.piod_len;
+}
+
+// Simple helper function to ensure flags are enabled on the given file
+// descriptor.
+static bool EnsureFDFlags(int fd, int flags, Status &error) {
+ int status;
+
+ if ((status = fcntl(fd, F_GETFL)) == -1) {
+ error.SetErrorToErrno();
+ return false;
+ }
+
+ if (fcntl(fd, F_SETFL, status | flags) == -1) {
+ error.SetErrorToErrno();
+ return false;
+ }
+
+ return true;
+}
+
+/// \class Operation
+/// Represents a ProcessMonitor operation.
+///
+/// Under FreeBSD, it is not possible to ptrace() from any other thread but
+/// the one that spawned or attached to the process from the start.
+/// Therefore, when a ProcessMonitor is asked to deliver or change the state
+/// of an inferior process the operation must be "funneled" to a specific
+/// thread to perform the task. The Operation class provides an abstract base
+/// for all services the ProcessMonitor must perform via the single virtual
+/// function Execute, thus encapsulating the code that needs to run in the
+/// privileged context.
+class Operation {
+public:
+ virtual ~Operation() {}
+ virtual void Execute(ProcessMonitor *monitor) = 0;
+};
+
+/// \class ReadOperation
+/// Implements ProcessMonitor::ReadMemory.
+class ReadOperation : public Operation {
+public:
+ ReadOperation(lldb::addr_t addr, void *buff, size_t size, Status &error,
+ size_t &result)
+ : m_addr(addr), m_buff(buff), m_size(size), m_error(error),
+ m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::addr_t m_addr;
+ void *m_buff;
+ size_t m_size;
+ Status &m_error;
+ size_t &m_result;
+};
+
+void ReadOperation::Execute(ProcessMonitor *monitor) {
+ lldb::pid_t pid = monitor->GetPID();
+
+ m_result = DoReadMemory(pid, m_addr, m_buff, m_size, m_error);
+}
+
+/// \class WriteOperation
+/// Implements ProcessMonitor::WriteMemory.
+class WriteOperation : public Operation {
+public:
+ WriteOperation(lldb::addr_t addr, const void *buff, size_t size,
+ Status &error, size_t &result)
+ : m_addr(addr), m_buff(buff), m_size(size), m_error(error),
+ m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::addr_t m_addr;
+ const void *m_buff;
+ size_t m_size;
+ Status &m_error;
+ size_t &m_result;
+};
+
+void WriteOperation::Execute(ProcessMonitor *monitor) {
+ lldb::pid_t pid = monitor->GetPID();
+
+ m_result = DoWriteMemory(pid, m_addr, m_buff, m_size, m_error);
+}
+
+/// \class ReadRegOperation
+/// Implements ProcessMonitor::ReadRegisterValue.
+class ReadRegOperation : public Operation {
+public:
+ ReadRegOperation(lldb::tid_t tid, unsigned offset, unsigned size,
+ RegisterValue &value, bool &result)
+ : m_tid(tid), m_offset(offset), m_size(size), m_value(value),
+ m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ unsigned m_offset;
+ unsigned m_size;
+ RegisterValue &m_value;
+ bool &m_result;
+};
+
+void ReadRegOperation::Execute(ProcessMonitor *monitor) {
+ struct reg regs;
+ int rc;
+
+ if ((rc = PTRACE(PT_GETREGS, m_tid, (caddr_t)&regs, 0)) < 0) {
+ m_result = false;
+ } else {
+ // 'struct reg' contains only 32- or 64-bit register values. Punt on
+ // others. Also, not all entries may be uintptr_t sized, such as 32-bit
+ // processes on powerpc64 (probably the same for i386 on amd64)
+ if (m_size == sizeof(uint32_t))
+ m_value = *(uint32_t *)(((caddr_t)&regs) + m_offset);
+ else if (m_size == sizeof(uint64_t))
+ m_value = *(uint64_t *)(((caddr_t)&regs) + m_offset);
+ else
+ memcpy((void *)&m_value, (((caddr_t)&regs) + m_offset), m_size);
+ m_result = true;
+ }
+}
+
+/// \class WriteRegOperation
+/// Implements ProcessMonitor::WriteRegisterValue.
+class WriteRegOperation : public Operation {
+public:
+ WriteRegOperation(lldb::tid_t tid, unsigned offset,
+ const RegisterValue &value, bool &result)
+ : m_tid(tid), m_offset(offset), m_value(value), m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ unsigned m_offset;
+ const RegisterValue &m_value;
+ bool &m_result;
+};
+
+void WriteRegOperation::Execute(ProcessMonitor *monitor) {
+ struct reg regs;
+
+ if (PTRACE(PT_GETREGS, m_tid, (caddr_t)&regs, 0) < 0) {
+ m_result = false;
+ return;
+ }
+ *(uintptr_t *)(((caddr_t)&regs) + m_offset) =
+ (uintptr_t)m_value.GetAsUInt64();
+ if (PTRACE(PT_SETREGS, m_tid, (caddr_t)&regs, 0) < 0)
+ m_result = false;
+ else
+ m_result = true;
+}
+
+/// \class ReadDebugRegOperation
+/// Implements ProcessMonitor::ReadDebugRegisterValue.
+class ReadDebugRegOperation : public Operation {
+public:
+ ReadDebugRegOperation(lldb::tid_t tid, unsigned offset, unsigned size,
+ RegisterValue &value, bool &result)
+ : m_tid(tid), m_offset(offset), m_size(size), m_value(value),
+ m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ unsigned m_offset;
+ unsigned m_size;
+ RegisterValue &m_value;
+ bool &m_result;
+};
+
+void ReadDebugRegOperation::Execute(ProcessMonitor *monitor) {
+ struct dbreg regs;
+ int rc;
+
+ if ((rc = PTRACE(PT_GETDBREGS, m_tid, (caddr_t)&regs, 0)) < 0) {
+ m_result = false;
+ } else {
+ if (m_size == sizeof(uintptr_t))
+ m_value = *(uintptr_t *)(((caddr_t)&regs) + m_offset);
+ else
+ memcpy((void *)&m_value, (((caddr_t)&regs) + m_offset), m_size);
+ m_result = true;
+ }
+}
+
+/// \class WriteDebugRegOperation
+/// Implements ProcessMonitor::WriteDebugRegisterValue.
+class WriteDebugRegOperation : public Operation {
+public:
+ WriteDebugRegOperation(lldb::tid_t tid, unsigned offset,
+ const RegisterValue &value, bool &result)
+ : m_tid(tid), m_offset(offset), m_value(value), m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ unsigned m_offset;
+ const RegisterValue &m_value;
+ bool &m_result;
+};
+
+void WriteDebugRegOperation::Execute(ProcessMonitor *monitor) {
+ struct dbreg regs;
+
+ if (PTRACE(PT_GETDBREGS, m_tid, (caddr_t)&regs, 0) < 0) {
+ m_result = false;
+ return;
+ }
+ *(uintptr_t *)(((caddr_t)&regs) + m_offset) =
+ (uintptr_t)m_value.GetAsUInt64();
+ if (PTRACE(PT_SETDBREGS, m_tid, (caddr_t)&regs, 0) < 0)
+ m_result = false;
+ else
+ m_result = true;
+}
+
+/// \class ReadGPROperation
+/// Implements ProcessMonitor::ReadGPR.
+class ReadGPROperation : public Operation {
+public:
+ ReadGPROperation(lldb::tid_t tid, void *buf, bool &result)
+ : m_tid(tid), m_buf(buf), m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ void *m_buf;
+ bool &m_result;
+};
+
+void ReadGPROperation::Execute(ProcessMonitor *monitor) {
+ int rc;
+
+ errno = 0;
+ rc = PTRACE(PT_GETREGS, m_tid, (caddr_t)m_buf, 0);
+ if (errno != 0)
+ m_result = false;
+ else
+ m_result = true;
+}
+
+/// \class ReadFPROperation
+/// Implements ProcessMonitor::ReadFPR.
+class ReadFPROperation : public Operation {
+public:
+ ReadFPROperation(lldb::tid_t tid, void *buf, bool &result)
+ : m_tid(tid), m_buf(buf), m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ void *m_buf;
+ bool &m_result;
+};
+
+void ReadFPROperation::Execute(ProcessMonitor *monitor) {
+ if (PTRACE(PT_GETFPREGS, m_tid, (caddr_t)m_buf, 0) < 0)
+ m_result = false;
+ else
+ m_result = true;
+}
+
+/// \class WriteGPROperation
+/// Implements ProcessMonitor::WriteGPR.
+class WriteGPROperation : public Operation {
+public:
+ WriteGPROperation(lldb::tid_t tid, void *buf, bool &result)
+ : m_tid(tid), m_buf(buf), m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ void *m_buf;
+ bool &m_result;
+};
+
+void WriteGPROperation::Execute(ProcessMonitor *monitor) {
+ if (PTRACE(PT_SETREGS, m_tid, (caddr_t)m_buf, 0) < 0)
+ m_result = false;
+ else
+ m_result = true;
+}
+
+/// \class WriteFPROperation
+/// Implements ProcessMonitor::WriteFPR.
+class WriteFPROperation : public Operation {
+public:
+ WriteFPROperation(lldb::tid_t tid, void *buf, bool &result)
+ : m_tid(tid), m_buf(buf), m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ void *m_buf;
+ bool &m_result;
+};
+
+void WriteFPROperation::Execute(ProcessMonitor *monitor) {
+ if (PTRACE(PT_SETFPREGS, m_tid, (caddr_t)m_buf, 0) < 0)
+ m_result = false;
+ else
+ m_result = true;
+}
+
+/// \class ResumeOperation
+/// Implements ProcessMonitor::Resume.
+class ResumeOperation : public Operation {
+public:
+ ResumeOperation(uint32_t signo, bool &result)
+ : m_signo(signo), m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ uint32_t m_signo;
+ bool &m_result;
+};
+
+void ResumeOperation::Execute(ProcessMonitor *monitor) {
+ lldb::pid_t pid = monitor->GetPID();
+ int data = 0;
+
+ if (m_signo != LLDB_INVALID_SIGNAL_NUMBER)
+ data = m_signo;
+
+ if (PTRACE(PT_CONTINUE, pid, (caddr_t)1, data)) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ LLDB_LOG(log, "ResumeOperation ({0}) failed: {1}", pid,
+ llvm::sys::StrError(errno));
+ m_result = false;
+ } else
+ m_result = true;
+}
+
+/// \class SingleStepOperation
+/// Implements ProcessMonitor::SingleStep.
+class SingleStepOperation : public Operation {
+public:
+ SingleStepOperation(uint32_t signo, bool &result)
+ : m_signo(signo), m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ uint32_t m_signo;
+ bool &m_result;
+};
+
+void SingleStepOperation::Execute(ProcessMonitor *monitor) {
+ lldb::pid_t pid = monitor->GetPID();
+ int data = 0;
+
+ if (m_signo != LLDB_INVALID_SIGNAL_NUMBER)
+ data = m_signo;
+
+ if (PTRACE(PT_STEP, pid, NULL, data))
+ m_result = false;
+ else
+ m_result = true;
+}
+
+/// \class LwpInfoOperation
+/// Implements ProcessMonitor::GetLwpInfo.
+class LwpInfoOperation : public Operation {
+public:
+ LwpInfoOperation(lldb::tid_t tid, void *info, bool &result, int &ptrace_err)
+ : m_tid(tid), m_info(info), m_result(result), m_err(ptrace_err) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ void *m_info;
+ bool &m_result;
+ int &m_err;
+};
+
+void LwpInfoOperation::Execute(ProcessMonitor *monitor) {
+ struct ptrace_lwpinfo plwp;
+
+ if (PTRACE(PT_LWPINFO, m_tid, (caddr_t)&plwp, sizeof(plwp))) {
+ m_result = false;
+ m_err = errno;
+ } else {
+ memcpy(m_info, &plwp, sizeof(plwp));
+ m_result = true;
+ }
+}
+
+/// \class ThreadSuspendOperation
+/// Implements ProcessMonitor::ThreadSuspend.
+class ThreadSuspendOperation : public Operation {
+public:
+ ThreadSuspendOperation(lldb::tid_t tid, bool suspend, bool &result)
+ : m_tid(tid), m_suspend(suspend), m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ bool m_suspend;
+ bool &m_result;
+};
+
+void ThreadSuspendOperation::Execute(ProcessMonitor *monitor) {
+ m_result = !PTRACE(m_suspend ? PT_SUSPEND : PT_RESUME, m_tid, NULL, 0);
+}
+
+/// \class EventMessageOperation
+/// Implements ProcessMonitor::GetEventMessage.
+class EventMessageOperation : public Operation {
+public:
+ EventMessageOperation(lldb::tid_t tid, unsigned long *message, bool &result)
+ : m_tid(tid), m_message(message), m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ lldb::tid_t m_tid;
+ unsigned long *m_message;
+ bool &m_result;
+};
+
+void EventMessageOperation::Execute(ProcessMonitor *monitor) {
+ struct ptrace_lwpinfo plwp;
+
+ if (PTRACE(PT_LWPINFO, m_tid, (caddr_t)&plwp, sizeof(plwp)))
+ m_result = false;
+ else {
+ if (plwp.pl_flags & PL_FLAG_FORKED) {
+ *m_message = plwp.pl_child_pid;
+ m_result = true;
+ } else
+ m_result = false;
+ }
+}
+
+/// \class KillOperation
+/// Implements ProcessMonitor::Kill.
+class KillOperation : public Operation {
+public:
+ KillOperation(bool &result) : m_result(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ bool &m_result;
+};
+
+void KillOperation::Execute(ProcessMonitor *monitor) {
+ lldb::pid_t pid = monitor->GetPID();
+
+ if (PTRACE(PT_KILL, pid, NULL, 0))
+ m_result = false;
+ else
+ m_result = true;
+}
+
+/// \class DetachOperation
+/// Implements ProcessMonitor::Detach.
+class DetachOperation : public Operation {
+public:
+ DetachOperation(Status &result) : m_error(result) {}
+
+ void Execute(ProcessMonitor *monitor);
+
+private:
+ Status &m_error;
+};
+
+void DetachOperation::Execute(ProcessMonitor *monitor) {
+ lldb::pid_t pid = monitor->GetPID();
+
+ if (PTRACE(PT_DETACH, pid, NULL, 0) < 0)
+ m_error.SetErrorToErrno();
+}
+
+ProcessMonitor::OperationArgs::OperationArgs(ProcessMonitor *monitor)
+ : m_monitor(monitor) {
+ sem_init(&m_semaphore, 0, 0);
+}
+
+ProcessMonitor::OperationArgs::~OperationArgs() { sem_destroy(&m_semaphore); }
+
+ProcessMonitor::LaunchArgs::LaunchArgs(ProcessMonitor *monitor,
+ lldb_private::Module *module,
+ char const **argv, Environment env,
+ const FileSpec &stdin_file_spec,
+ const FileSpec &stdout_file_spec,
+ const FileSpec &stderr_file_spec,
+ const FileSpec &working_dir)
+ : OperationArgs(monitor), m_module(module), m_argv(argv),
+ m_env(std::move(env)), m_stdin_file_spec(stdin_file_spec),
+ m_stdout_file_spec(stdout_file_spec),
+ m_stderr_file_spec(stderr_file_spec), m_working_dir(working_dir) {}
+
+ProcessMonitor::LaunchArgs::~LaunchArgs() {}
+
+ProcessMonitor::AttachArgs::AttachArgs(ProcessMonitor *monitor, lldb::pid_t pid)
+ : OperationArgs(monitor), m_pid(pid) {}
+
+ProcessMonitor::AttachArgs::~AttachArgs() {}
+
+/// The basic design of the ProcessMonitor is built around two threads.
+///
+/// One thread (@see SignalThread) simply blocks on a call to waitpid()
+/// looking for changes in the debugee state. When a change is detected a
+/// ProcessMessage is sent to the associated ProcessFreeBSD instance. This
+/// thread "drives" state changes in the debugger.
+///
+/// The second thread (@see OperationThread) is responsible for two things 1)
+/// launching or attaching to the inferior process, and then 2) servicing
+/// operations such as register reads/writes, stepping, etc. See the comments
+/// on the Operation class for more info as to why this is needed.
+ProcessMonitor::ProcessMonitor(
+ ProcessFreeBSD *process, Module *module, const char *argv[],
+ Environment env, const FileSpec &stdin_file_spec,
+ const FileSpec &stdout_file_spec, const FileSpec &stderr_file_spec,
+ const FileSpec &working_dir,
+ const lldb_private::ProcessLaunchInfo & /* launch_info */,
+ lldb_private::Status &error)
+ : m_process(static_cast<ProcessFreeBSD *>(process)),
+ m_operation_thread(), m_monitor_thread(), m_pid(LLDB_INVALID_PROCESS_ID), m_terminal_fd(-1), m_operation(0) {
+ using namespace std::placeholders;
+
+ std::unique_ptr<LaunchArgs> args(
+ new LaunchArgs(this, module, argv, std::move(env), stdin_file_spec,
+ stdout_file_spec, stderr_file_spec, working_dir));
+
+ sem_init(&m_operation_pending, 0, 0);
+ sem_init(&m_operation_done, 0, 0);
+
+ StartLaunchOpThread(args.get(), error);
+ if (!error.Success())
+ return;
+
+ if (llvm::sys::RetryAfterSignal(-1, sem_wait, &args->m_semaphore) == -1) {
+ error.SetErrorToErrno();
+ return;
+ }
+
+ // Check that the launch was a success.
+ if (!args->m_error.Success()) {
+ StopOpThread();
+ error = args->m_error;
+ return;
+ }
+
+ // Finally, start monitoring the child process for change in state.
+ llvm::Expected<lldb_private::HostThread> monitor_thread =
+ Host::StartMonitoringChildProcess(
+ std::bind(&ProcessMonitor::MonitorCallback, this, _1, _2, _3, _4),
+ GetPID(), true);
+ if (!monitor_thread || !monitor_thread->IsJoinable()) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Process launch failed.");
+ return;
+ }
+ m_monitor_thread = *monitor_thread;
+}
+
+ProcessMonitor::ProcessMonitor(ProcessFreeBSD *process, lldb::pid_t pid,
+ lldb_private::Status &error)
+ : m_process(static_cast<ProcessFreeBSD *>(process)),
+ m_operation_thread(), m_monitor_thread(), m_pid(pid), m_terminal_fd(-1), m_operation(0) {
+ using namespace std::placeholders;
+
+ sem_init(&m_operation_pending, 0, 0);
+ sem_init(&m_operation_done, 0, 0);
+
+ std::unique_ptr<AttachArgs> args(new AttachArgs(this, pid));
+
+ StartAttachOpThread(args.get(), error);
+ if (!error.Success())
+ return;
+
+ if (llvm::sys::RetryAfterSignal(-1, sem_wait, &args->m_semaphore) == -1) {
+ error.SetErrorToErrno();
+ return;
+ }
+
+ // Check that the attach was a success.
+ if (!args->m_error.Success()) {
+ StopOpThread();
+ error = args->m_error;
+ return;
+ }
+
+ // Finally, start monitoring the child process for change in state.
+ llvm::Expected<lldb_private::HostThread> monitor_thread =
+ Host::StartMonitoringChildProcess(
+ std::bind(&ProcessMonitor::MonitorCallback, this, _1, _2, _3, _4),
+ GetPID(), true);
+ if (!monitor_thread || !monitor_thread->IsJoinable()) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Process attach failed.");
+ return;
+ }
+ m_monitor_thread = *monitor_thread;
+}
+
+ProcessMonitor::~ProcessMonitor() { StopMonitor(); }
+
+// Thread setup and tear down.
+void ProcessMonitor::StartLaunchOpThread(LaunchArgs *args, Status &error) {
+ static const char *g_thread_name = "freebsd.op";
+
+ if (m_operation_thread && m_operation_thread->IsJoinable())
+ return;
+
+ llvm::Expected<lldb_private::HostThread> operation_thread =
+ ThreadLauncher::LaunchThread(g_thread_name, LaunchOpThread, args);
+ if (operation_thread)
+ m_operation_thread = *operation_thread;
+ else
+ error = operation_thread.takeError();
+}
+
+void *ProcessMonitor::LaunchOpThread(void *arg) {
+ LaunchArgs *args = static_cast<LaunchArgs *>(arg);
+
+ if (!Launch(args)) {
+ sem_post(&args->m_semaphore);
+ return NULL;
+ }
+
+ ServeOperation(args);
+ return NULL;
+}
+
+bool ProcessMonitor::Launch(LaunchArgs *args) {
+ ProcessMonitor *monitor = args->m_monitor;
+ ProcessFreeBSD &process = monitor->GetProcess();
+ const char **argv = args->m_argv;
+ const FileSpec &stdin_file_spec = args->m_stdin_file_spec;
+ const FileSpec &stdout_file_spec = args->m_stdout_file_spec;
+ const FileSpec &stderr_file_spec = args->m_stderr_file_spec;
+ const FileSpec &working_dir = args->m_working_dir;
+
+ PseudoTerminal terminal;
+ const size_t err_len = 1024;
+ char err_str[err_len];
+ ::pid_t pid;
+
+ // Propagate the environment if one is not supplied.
+ Environment::Envp envp =
+ (args->m_env.empty() ? Host::GetEnvironment() : args->m_env).getEnvp();
+
+ if ((pid = terminal.Fork(err_str, err_len)) == -1) {
+ args->m_error.SetErrorToGenericError();
+ args->m_error.SetErrorString("Process fork failed.");
+ goto FINISH;
+ }
+
+ // Recognized child exit status codes.
+ enum {
+ ePtraceFailed = 1,
+ eDupStdinFailed,
+ eDupStdoutFailed,
+ eDupStderrFailed,
+ eChdirFailed,
+ eExecFailed,
+ eSetGidFailed
+ };
+
+ // Child process.
+ if (pid == 0) {
+ // Trace this process.
+ if (PTRACE(PT_TRACE_ME, 0, NULL, 0) < 0)
+ exit(ePtraceFailed);
+
+ // terminal has already dupped the tty descriptors to stdin/out/err. This
+ // closes original fd from which they were copied (and avoids leaking
+ // descriptors to the debugged process.
+ terminal.CloseSlaveFileDescriptor();
+
+ // Do not inherit setgid powers.
+ if (setgid(getgid()) != 0)
+ exit(eSetGidFailed);
+
+ // Let us have our own process group.
+ setpgid(0, 0);
+
+ // Dup file descriptors if needed.
+ //
+ // FIXME: If two or more of the paths are the same we needlessly open
+ // the same file multiple times.
+ if (stdin_file_spec)
+ if (!DupDescriptor(stdin_file_spec, STDIN_FILENO, O_RDONLY))
+ exit(eDupStdinFailed);
+
+ if (stdout_file_spec)
+ if (!DupDescriptor(stdout_file_spec, STDOUT_FILENO, O_WRONLY | O_CREAT))
+ exit(eDupStdoutFailed);
+
+ if (stderr_file_spec)
+ if (!DupDescriptor(stderr_file_spec, STDERR_FILENO, O_WRONLY | O_CREAT))
+ exit(eDupStderrFailed);
+
+ // Change working directory
+ if (working_dir && 0 != ::chdir(working_dir.GetCString()))
+ exit(eChdirFailed);
+
+ // Execute. We should never return.
+ execve(argv[0], const_cast<char *const *>(argv), envp);
+ exit(eExecFailed);
+ }
+
+ // Wait for the child process to to trap on its call to execve.
+ ::pid_t wpid;
+ int status;
+ if ((wpid = waitpid(pid, &status, 0)) < 0) {
+ args->m_error.SetErrorToErrno();
+ goto FINISH;
+ } else if (WIFEXITED(status)) {
+ // open, dup or execve likely failed for some reason.
+ args->m_error.SetErrorToGenericError();
+ switch (WEXITSTATUS(status)) {
+ case ePtraceFailed:
+ args->m_error.SetErrorString("Child ptrace failed.");
+ break;
+ case eDupStdinFailed:
+ args->m_error.SetErrorString("Child open stdin failed.");
+ break;
+ case eDupStdoutFailed:
+ args->m_error.SetErrorString("Child open stdout failed.");
+ break;
+ case eDupStderrFailed:
+ args->m_error.SetErrorString("Child open stderr failed.");
+ break;
+ case eChdirFailed:
+ args->m_error.SetErrorString("Child failed to set working directory.");
+ break;
+ case eExecFailed:
+ args->m_error.SetErrorString("Child exec failed.");
+ break;
+ case eSetGidFailed:
+ args->m_error.SetErrorString("Child setgid failed.");
+ break;
+ default:
+ args->m_error.SetErrorString("Child returned unknown exit status.");
+ break;
+ }
+ goto FINISH;
+ }
+ assert(WIFSTOPPED(status) && wpid == (::pid_t)pid &&
+ "Could not sync with inferior process.");
+
+#ifdef notyet
+ // Have the child raise an event on exit. This is used to keep the child in
+ // limbo until it is destroyed.
+ if (PTRACE(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACEEXIT) < 0) {
+ args->m_error.SetErrorToErrno();
+ goto FINISH;
+ }
+#endif
+ // Release the master terminal descriptor and pass it off to the
+ // ProcessMonitor instance. Similarly stash the inferior pid.
+ monitor->m_terminal_fd = terminal.ReleaseMasterFileDescriptor();
+ monitor->m_pid = pid;
+
+ // Set the terminal fd to be in non blocking mode (it simplifies the
+ // implementation of ProcessFreeBSD::GetSTDOUT to have a non-blocking
+ // descriptor to read from).
+ if (!EnsureFDFlags(monitor->m_terminal_fd, O_NONBLOCK, args->m_error))
+ goto FINISH;
+
+ process.SendMessage(ProcessMessage::Attach(pid));
+
+FINISH:
+ return args->m_error.Success();
+}
+
+void ProcessMonitor::StartAttachOpThread(AttachArgs *args,
+ lldb_private::Status &error) {
+ static const char *g_thread_name = "freebsd.op";
+
+ if (m_operation_thread && m_operation_thread->IsJoinable())
+ return;
+
+ llvm::Expected<lldb_private::HostThread> operation_thread =
+ ThreadLauncher::LaunchThread(g_thread_name, AttachOpThread, args);
+ if (operation_thread)
+ m_operation_thread = *operation_thread;
+ else
+ error = operation_thread.takeError();
+}
+
+void *ProcessMonitor::AttachOpThread(void *arg) {
+ AttachArgs *args = static_cast<AttachArgs *>(arg);
+
+ Attach(args);
+
+ ServeOperation(args);
+ return NULL;
+}
+
+void ProcessMonitor::Attach(AttachArgs *args) {
+ lldb::pid_t pid = args->m_pid;
+
+ ProcessMonitor *monitor = args->m_monitor;
+ ProcessFreeBSD &process = monitor->GetProcess();
+
+ if (pid <= 1) {
+ args->m_error.SetErrorToGenericError();
+ args->m_error.SetErrorString("Attaching to process 1 is not allowed.");
+ return;
+ }
+
+ // Attach to the requested process.
+ if (PTRACE(PT_ATTACH, pid, NULL, 0) < 0) {
+ args->m_error.SetErrorToErrno();
+ return;
+ }
+
+ int status;
+ if ((status = waitpid(pid, NULL, 0)) < 0) {
+ args->m_error.SetErrorToErrno();
+ return;
+ }
+
+ process.SendMessage(ProcessMessage::Attach(pid));
+}
+
+size_t
+ProcessMonitor::GetCurrentThreadIDs(std::vector<lldb::tid_t> &thread_ids) {
+ lwpid_t *tids;
+ int tdcnt;
+
+ thread_ids.clear();
+
+ tdcnt = PTRACE(PT_GETNUMLWPS, m_pid, NULL, 0);
+ if (tdcnt <= 0)
+ return 0;
+ tids = (lwpid_t *)malloc(tdcnt * sizeof(*tids));
+ if (tids == NULL)
+ return 0;
+ if (PTRACE(PT_GETLWPLIST, m_pid, (void *)tids, tdcnt) < 0) {
+ free(tids);
+ return 0;
+ }
+ thread_ids = std::vector<lldb::tid_t>(tids, tids + tdcnt);
+ free(tids);
+ return thread_ids.size();
+}
+
+bool ProcessMonitor::MonitorCallback(ProcessMonitor *monitor, lldb::pid_t pid,
+ bool exited, int signal, int status) {
+ ProcessMessage message;
+ ProcessFreeBSD *process = monitor->m_process;
+ assert(process);
+ bool stop_monitoring;
+ struct ptrace_lwpinfo plwp;
+ int ptrace_err;
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+
+ if (exited) {
+ if (log)
+ log->Printf("ProcessMonitor::%s() got exit signal, tid = %" PRIu64,
+ __FUNCTION__, pid);
+ message = ProcessMessage::Exit(pid, status);
+ process->SendMessage(message);
+ return pid == process->GetID();
+ }
+
+ if (!monitor->GetLwpInfo(pid, &plwp, ptrace_err))
+ stop_monitoring = true; // pid is gone. Bail.
+ else {
+ switch (plwp.pl_siginfo.si_signo) {
+ case SIGTRAP:
+ message = MonitorSIGTRAP(monitor, &plwp.pl_siginfo, plwp.pl_lwpid);
+ break;
+
+ default:
+ message = MonitorSignal(monitor, &plwp.pl_siginfo, plwp.pl_lwpid);
+ break;
+ }
+
+ process->SendMessage(message);
+ stop_monitoring = message.GetKind() == ProcessMessage::eExitMessage;
+ }
+
+ return stop_monitoring;
+}
+
+ProcessMessage ProcessMonitor::MonitorSIGTRAP(ProcessMonitor *monitor,
+ const siginfo_t *info,
+ lldb::tid_t tid) {
+ ProcessMessage message;
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+
+ assert(monitor);
+ assert(info && info->si_signo == SIGTRAP && "Unexpected child signal!");
+
+ switch (info->si_code) {
+ default:
+ assert(false && "Unexpected SIGTRAP code!");
+ break;
+
+ case (SIGTRAP /* | (PTRACE_EVENT_EXIT << 8) */): {
+ // The inferior process is about to exit. Maintain the process in a state
+ // of "limbo" until we are explicitly commanded to detach, destroy, resume,
+ // etc.
+ unsigned long data = 0;
+ if (!monitor->GetEventMessage(tid, &data))
+ data = -1;
+ if (log)
+ log->Printf("ProcessMonitor::%s() received exit? event, data = %lx, tid "
+ "= %" PRIu64,
+ __FUNCTION__, data, tid);
+ message = ProcessMessage::Limbo(tid, (data >> 8));
+ break;
+ }
+
+ case 0:
+ case TRAP_TRACE:
+#ifdef TRAP_CAP
+ // Map TRAP_CAP to a trace trap in the absense of a more specific handler.
+ case TRAP_CAP:
+#endif
+ if (log)
+ log->Printf("ProcessMonitor::%s() received trace event, tid = %" PRIu64
+ " : si_code = %d",
+ __FUNCTION__, tid, info->si_code);
+ message = ProcessMessage::Trace(tid);
+ break;
+
+ case SI_KERNEL:
+ case TRAP_BRKPT:
+ if (monitor->m_process->IsSoftwareStepBreakpoint(tid)) {
+ if (log)
+ log->Printf("ProcessMonitor::%s() received sw single step breakpoint "
+ "event, tid = %" PRIu64,
+ __FUNCTION__, tid);
+ message = ProcessMessage::Trace(tid);
+ } else {
+ if (log)
+ log->Printf(
+ "ProcessMonitor::%s() received breakpoint event, tid = %" PRIu64,
+ __FUNCTION__, tid);
+ message = ProcessMessage::Break(tid);
+ }
+ break;
+ }
+
+ return message;
+}
+
+ProcessMessage ProcessMonitor::MonitorSignal(ProcessMonitor *monitor,
+ const siginfo_t *info,
+ lldb::tid_t tid) {
+ ProcessMessage message;
+ int signo = info->si_signo;
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+
+ // POSIX says that process behaviour is undefined after it ignores a SIGFPE,
+ // SIGILL, SIGSEGV, or SIGBUS *unless* that signal was generated by a kill(2)
+ // or raise(3). Similarly for tgkill(2) on FreeBSD.
+ //
+ // IOW, user generated signals never generate what we consider to be a
+ // "crash".
+ //
+ // Similarly, ACK signals generated by this monitor.
+ if (info->si_code == SI_USER) {
+ if (log)
+ log->Printf(
+ "ProcessMonitor::%s() received signal %s with code %s, pid = %d",
+ __FUNCTION__,
+ monitor->m_process->GetUnixSignals()->GetSignalAsCString(signo),
+ "SI_USER", info->si_pid);
+ if (info->si_pid == getpid())
+ return ProcessMessage::SignalDelivered(tid, signo);
+ else
+ return ProcessMessage::Signal(tid, signo);
+ }
+
+ if (log)
+ log->Printf(
+ "ProcessMonitor::%s() received signal %s", __FUNCTION__,
+ monitor->m_process->GetUnixSignals()->GetSignalAsCString(signo));
+
+ switch (signo) {
+ case SIGSEGV:
+ case SIGILL:
+ case SIGFPE:
+ case SIGBUS:
+ lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr);
+ const auto reason = GetCrashReason(*info);
+ if (reason != CrashReason::eInvalidCrashReason) {
+ return ProcessMessage::Crash(tid, reason, signo, fault_addr);
+ } // else; Use atleast si_signo info for other si_code
+ }
+
+ // Everything else is "normal" and does not require any special action on our
+ // part.
+ return ProcessMessage::Signal(tid, signo);
+}
+
+void ProcessMonitor::ServeOperation(OperationArgs *args) {
+ ProcessMonitor *monitor = args->m_monitor;
+
+ // We are finised with the arguments and are ready to go. Sync with the
+ // parent thread and start serving operations on the inferior.
+ sem_post(&args->m_semaphore);
+
+ for (;;) {
+ // wait for next pending operation
+ sem_wait(&monitor->m_operation_pending);
+
+ monitor->m_operation->Execute(monitor);
+
+ // notify calling thread that operation is complete
+ sem_post(&monitor->m_operation_done);
+ }
+}
+
+void ProcessMonitor::DoOperation(Operation *op) {
+ std::lock_guard<std::mutex> guard(m_operation_mutex);
+
+ m_operation = op;
+
+ // notify operation thread that an operation is ready to be processed
+ sem_post(&m_operation_pending);
+
+ // wait for operation to complete
+ sem_wait(&m_operation_done);
+}
+
+size_t ProcessMonitor::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
+ Status &error) {
+ size_t result;
+ ReadOperation op(vm_addr, buf, size, error, result);
+ DoOperation(&op);
+ return result;
+}
+
+size_t ProcessMonitor::WriteMemory(lldb::addr_t vm_addr, const void *buf,
+ size_t size, lldb_private::Status &error) {
+ size_t result;
+ WriteOperation op(vm_addr, buf, size, error, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::ReadRegisterValue(lldb::tid_t tid, unsigned offset,
+ const char *reg_name, unsigned size,
+ RegisterValue &value) {
+ bool result;
+ ReadRegOperation op(tid, offset, size, value, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::WriteRegisterValue(lldb::tid_t tid, unsigned offset,
+ const char *reg_name,
+ const RegisterValue &value) {
+ bool result;
+ WriteRegOperation op(tid, offset, value, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::ReadDebugRegisterValue(
+ lldb::tid_t tid, unsigned offset, const char *reg_name, unsigned size,
+ lldb_private::RegisterValue &value) {
+ bool result;
+ ReadDebugRegOperation op(tid, offset, size, value, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::WriteDebugRegisterValue(
+ lldb::tid_t tid, unsigned offset, const char *reg_name,
+ const lldb_private::RegisterValue &value) {
+ bool result;
+ WriteDebugRegOperation op(tid, offset, value, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::ReadGPR(lldb::tid_t tid, void *buf, size_t buf_size) {
+ bool result;
+ ReadGPROperation op(tid, buf, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::ReadFPR(lldb::tid_t tid, void *buf, size_t buf_size) {
+ bool result;
+ ReadFPROperation op(tid, buf, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::ReadRegisterSet(lldb::tid_t tid, void *buf,
+ size_t buf_size, unsigned int regset) {
+ return false;
+}
+
+bool ProcessMonitor::WriteGPR(lldb::tid_t tid, void *buf, size_t buf_size) {
+ bool result;
+ WriteGPROperation op(tid, buf, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::WriteFPR(lldb::tid_t tid, void *buf, size_t buf_size) {
+ bool result;
+ WriteFPROperation op(tid, buf, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::WriteRegisterSet(lldb::tid_t tid, void *buf,
+ size_t buf_size, unsigned int regset) {
+ return false;
+}
+
+bool ProcessMonitor::ReadThreadPointer(lldb::tid_t tid, lldb::addr_t &value) {
+ return false;
+}
+
+bool ProcessMonitor::Resume(lldb::tid_t unused, uint32_t signo) {
+ bool result;
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+
+ if (log) {
+ const char *signame =
+ m_process->GetUnixSignals()->GetSignalAsCString(signo);
+ if (signame == nullptr)
+ signame = "<none>";
+ log->Printf("ProcessMonitor::%s() resuming pid %" PRIu64 " with signal %s",
+ __FUNCTION__, GetPID(), signame);
+ }
+ ResumeOperation op(signo, result);
+ DoOperation(&op);
+ if (log)
+ log->Printf("ProcessMonitor::%s() resuming result = %s", __FUNCTION__,
+ result ? "true" : "false");
+ return result;
+}
+
+bool ProcessMonitor::SingleStep(lldb::tid_t unused, uint32_t signo) {
+ bool result;
+ SingleStepOperation op(signo, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::Kill() {
+ bool result;
+ KillOperation op(result);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::GetLwpInfo(lldb::tid_t tid, void *lwpinfo,
+ int &ptrace_err) {
+ bool result;
+ LwpInfoOperation op(tid, lwpinfo, result, ptrace_err);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::ThreadSuspend(lldb::tid_t tid, bool suspend) {
+ bool result;
+ ThreadSuspendOperation op(tid, suspend, result);
+ DoOperation(&op);
+ return result;
+}
+
+bool ProcessMonitor::GetEventMessage(lldb::tid_t tid, unsigned long *message) {
+ bool result;
+ EventMessageOperation op(tid, message, result);
+ DoOperation(&op);
+ return result;
+}
+
+lldb_private::Status ProcessMonitor::Detach(lldb::tid_t tid) {
+ lldb_private::Status error;
+ if (tid != LLDB_INVALID_THREAD_ID) {
+ DetachOperation op(error);
+ DoOperation(&op);
+ }
+ return error;
+}
+
+bool ProcessMonitor::DupDescriptor(const FileSpec &file_spec, int fd,
+ int flags) {
+ int target_fd = llvm::sys::RetryAfterSignal(-1, open,
+ file_spec.GetCString(), flags, 0666);
+
+ if (target_fd == -1)
+ return false;
+
+ if (dup2(target_fd, fd) == -1)
+ return false;
+
+ return (close(target_fd) == -1) ? false : true;
+}
+
+void ProcessMonitor::StopMonitoringChildProcess() {
+ if (m_monitor_thread && m_monitor_thread->IsJoinable()) {
+ m_monitor_thread->Cancel();
+ m_monitor_thread->Join(nullptr);
+ m_monitor_thread->Reset();
+ }
+}
+
+void ProcessMonitor::StopMonitor() {
+ StopMonitoringChildProcess();
+ StopOpThread();
+ sem_destroy(&m_operation_pending);
+ sem_destroy(&m_operation_done);
+ if (m_terminal_fd >= 0) {
+ close(m_terminal_fd);
+ m_terminal_fd = -1;
+ }
+}
+
+// FIXME: On Linux, when a new thread is created, we receive to notifications,
+// (1) a SIGTRAP|PTRACE_EVENT_CLONE from the main process thread with the child
+// thread id as additional information, and (2) a SIGSTOP|SI_USER from the new
+// child thread indicating that it has is stopped because we attached. We have
+// no guarantee of the order in which these arrive, but we need both before we
+// are ready to proceed. We currently keep a list of threads which have sent
+// the initial SIGSTOP|SI_USER event. Then when we receive the
+// SIGTRAP|PTRACE_EVENT_CLONE notification, if the initial stop has not
+// occurred we call ProcessMonitor::WaitForInitialTIDStop() to wait for it.
+//
+// Right now, the above logic is in ProcessPOSIX, so we need a definition of
+// this function in the FreeBSD ProcessMonitor implementation even if it isn't
+// logically needed.
+//
+// We really should figure out what actually happens on FreeBSD and move the
+// Linux-specific logic out of ProcessPOSIX as needed.
+
+bool ProcessMonitor::WaitForInitialTIDStop(lldb::tid_t tid) { return true; }
+
+void ProcessMonitor::StopOpThread() {
+ if (m_operation_thread && m_operation_thread->IsJoinable()) {
+ m_operation_thread->Cancel();
+ m_operation_thread->Join(nullptr);
+ m_operation_thread->Reset();
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.h
new file mode 100644
index 000000000000..c5edfc0be95a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.h
@@ -0,0 +1,279 @@
+//===-- ProcessMonitor.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 liblldb_ProcessMonitor_H_
+#define liblldb_ProcessMonitor_H_
+
+#include <semaphore.h>
+#include <signal.h>
+
+#include <mutex>
+
+#include "lldb/Host/HostThread.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+class Status;
+class Module;
+class Scalar;
+} // End lldb_private namespace.
+
+class ProcessFreeBSD;
+class Operation;
+
+/// \class ProcessMonitor
+/// Manages communication with the inferior (debugee) process.
+///
+/// Upon construction, this class prepares and launches an inferior process
+/// for debugging.
+///
+/// Changes in the inferior process state are propagated to the associated
+/// ProcessFreeBSD instance by calling ProcessFreeBSD::SendMessage with the
+/// appropriate ProcessMessage events.
+///
+/// A purposely minimal set of operations are provided to interrogate and change
+/// the inferior process state.
+class ProcessMonitor {
+public:
+ /// Launches an inferior process ready for debugging. Forms the
+ /// implementation of Process::DoLaunch.
+ ProcessMonitor(ProcessFreeBSD *process, lldb_private::Module *module,
+ char const *argv[], lldb_private::Environment env,
+ const lldb_private::FileSpec &stdin_file_spec,
+ const lldb_private::FileSpec &stdout_file_spec,
+ const lldb_private::FileSpec &stderr_file_spec,
+ const lldb_private::FileSpec &working_dir,
+ const lldb_private::ProcessLaunchInfo &launch_info,
+ lldb_private::Status &error);
+
+ ProcessMonitor(ProcessFreeBSD *process, lldb::pid_t pid,
+ lldb_private::Status &error);
+
+ ~ProcessMonitor();
+
+ /// Provides the process number of debugee.
+ lldb::pid_t GetPID() const { return m_pid; }
+
+ /// Returns the process associated with this ProcessMonitor.
+ ProcessFreeBSD &GetProcess() { return *m_process; }
+
+ /// Returns a file descriptor to the controlling terminal of the inferior
+ /// process.
+ ///
+ /// Reads from this file descriptor yield both the standard output and
+ /// standard error of this debugee. Even if stderr and stdout were
+ /// redirected on launch it may still happen that data is available on this
+ /// descriptor (if the inferior process opens /dev/tty, for example). This
+ /// descriptor is closed after a call to StopMonitor().
+ ///
+ /// If this monitor was attached to an existing process this method returns
+ /// -1.
+ int GetTerminalFD() const { return m_terminal_fd; }
+
+ /// Reads \p size bytes from address @vm_adder in the inferior process
+ /// address space.
+ ///
+ /// This method is provided to implement Process::DoReadMemory.
+ size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
+ lldb_private::Status &error);
+
+ /// Writes \p size bytes from address \p vm_adder in the inferior process
+ /// address space.
+ ///
+ /// This method is provided to implement Process::DoWriteMemory.
+ size_t WriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size,
+ lldb_private::Status &error);
+
+ /// Reads the contents from the register identified by the given
+ /// (architecture dependent) offset.
+ ///
+ /// This method is provided for use by RegisterContextFreeBSD derivatives.
+ bool ReadRegisterValue(lldb::tid_t tid, unsigned offset, const char *reg_name,
+ unsigned size, lldb_private::RegisterValue &value);
+
+ /// Writes the given value to the register identified by the given
+ /// (architecture dependent) offset.
+ ///
+ /// This method is provided for use by RegisterContextFreeBSD derivatives.
+ bool WriteRegisterValue(lldb::tid_t tid, unsigned offset,
+ const char *reg_name,
+ const lldb_private::RegisterValue &value);
+
+ /// Reads the contents from the debug register identified by the given
+ /// (architecture dependent) offset.
+ ///
+ /// This method is provided for use by RegisterContextFreeBSD derivatives.
+ bool ReadDebugRegisterValue(lldb::tid_t tid, unsigned offset,
+ const char *reg_name, unsigned size,
+ lldb_private::RegisterValue &value);
+
+ /// Writes the given value to the debug register identified by the given
+ /// (architecture dependent) offset.
+ ///
+ /// This method is provided for use by RegisterContextFreeBSD derivatives.
+ bool WriteDebugRegisterValue(lldb::tid_t tid, unsigned offset,
+ const char *reg_name,
+ const lldb_private::RegisterValue &value);
+ /// Reads all general purpose registers into the specified buffer.
+ bool ReadGPR(lldb::tid_t tid, void *buf, size_t buf_size);
+
+ /// Reads all floating point registers into the specified buffer.
+ bool ReadFPR(lldb::tid_t tid, void *buf, size_t buf_size);
+
+ /// Reads the specified register set into the specified buffer.
+ ///
+ /// This method is provided for use by RegisterContextFreeBSD derivatives.
+ bool ReadRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size,
+ unsigned int regset);
+
+ /// Writes all general purpose registers into the specified buffer.
+ bool WriteGPR(lldb::tid_t tid, void *buf, size_t buf_size);
+
+ /// Writes all floating point registers into the specified buffer.
+ bool WriteFPR(lldb::tid_t tid, void *buf, size_t buf_size);
+
+ /// Writes the specified register set into the specified buffer.
+ ///
+ /// This method is provided for use by RegisterContextFreeBSD derivatives.
+ bool WriteRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size,
+ unsigned int regset);
+
+ /// Reads the value of the thread-specific pointer for a given thread ID.
+ bool ReadThreadPointer(lldb::tid_t tid, lldb::addr_t &value);
+
+ /// Returns current thread IDs in process
+ size_t GetCurrentThreadIDs(std::vector<lldb::tid_t> &thread_ids);
+
+ /// Writes a ptrace_lwpinfo structure corresponding to the given thread ID
+ /// to the memory region pointed to by \p lwpinfo.
+ bool GetLwpInfo(lldb::tid_t tid, void *lwpinfo, int &error_no);
+
+ /// Suspends or unsuspends a thread prior to process resume or step.
+ bool ThreadSuspend(lldb::tid_t tid, bool suspend);
+
+ /// Writes the raw event message code (vis-a-vis PTRACE_GETEVENTMSG)
+ /// corresponding to the given thread IDto the memory pointed to by @p
+ /// message.
+ bool GetEventMessage(lldb::tid_t tid, unsigned long *message);
+
+ /// Resumes the process. If \p signo is anything but
+ /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the process.
+ bool Resume(lldb::tid_t unused, uint32_t signo);
+
+ /// Single steps the process. If \p signo is anything but
+ /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the process.
+ bool SingleStep(lldb::tid_t unused, uint32_t signo);
+
+ /// Terminate the traced process.
+ bool Kill();
+
+ lldb_private::Status Detach(lldb::tid_t tid);
+
+ void StopMonitor();
+
+ // Waits for the initial stop message from a new thread.
+ bool WaitForInitialTIDStop(lldb::tid_t tid);
+
+private:
+ ProcessFreeBSD *m_process;
+
+ llvm::Optional<lldb_private::HostThread> m_operation_thread;
+ llvm::Optional<lldb_private::HostThread> m_monitor_thread;
+ lldb::pid_t m_pid;
+
+ int m_terminal_fd;
+
+ // current operation which must be executed on the privileged thread
+ Operation *m_operation;
+ std::mutex m_operation_mutex;
+
+ // semaphores notified when Operation is ready to be processed and when
+ // the operation is complete.
+ sem_t m_operation_pending;
+ sem_t m_operation_done;
+
+ struct OperationArgs {
+ OperationArgs(ProcessMonitor *monitor);
+
+ ~OperationArgs();
+
+ ProcessMonitor *m_monitor; // The monitor performing the attach.
+ sem_t m_semaphore; // Posted to once operation complete.
+ lldb_private::Status m_error; // Set if process operation failed.
+ };
+
+ /// \class LauchArgs
+ ///
+ /// Simple structure to pass data to the thread responsible for launching a
+ /// child process.
+ struct LaunchArgs : OperationArgs {
+ LaunchArgs(ProcessMonitor *monitor, lldb_private::Module *module,
+ char const **argv, lldb_private::Environment env,
+ const lldb_private::FileSpec &stdin_file_spec,
+ const lldb_private::FileSpec &stdout_file_spec,
+ const lldb_private::FileSpec &stderr_file_spec,
+ const lldb_private::FileSpec &working_dir);
+
+ ~LaunchArgs();
+
+ lldb_private::Module *m_module; // The executable image to launch.
+ char const **m_argv; // Process arguments.
+ lldb_private::Environment m_env; // Process environment.
+ const lldb_private::FileSpec m_stdin_file_spec; // Redirect stdin or empty.
+ const lldb_private::FileSpec
+ m_stdout_file_spec; // Redirect stdout or empty.
+ const lldb_private::FileSpec
+ m_stderr_file_spec; // Redirect stderr or empty.
+ const lldb_private::FileSpec m_working_dir; // Working directory or empty.
+ };
+
+ void StartLaunchOpThread(LaunchArgs *args, lldb_private::Status &error);
+
+ static void *LaunchOpThread(void *arg);
+
+ static bool Launch(LaunchArgs *args);
+
+ struct AttachArgs : OperationArgs {
+ AttachArgs(ProcessMonitor *monitor, lldb::pid_t pid);
+
+ ~AttachArgs();
+
+ lldb::pid_t m_pid; // pid of the process to be attached.
+ };
+
+ void StartAttachOpThread(AttachArgs *args, lldb_private::Status &error);
+
+ static void *AttachOpThread(void *args);
+
+ static void Attach(AttachArgs *args);
+
+ static void ServeOperation(OperationArgs *args);
+
+ static bool DupDescriptor(const lldb_private::FileSpec &file_spec, int fd,
+ int flags);
+
+ static bool MonitorCallback(ProcessMonitor *monitor, lldb::pid_t pid,
+ bool exited, int signal, int status);
+
+ static ProcessMessage MonitorSIGTRAP(ProcessMonitor *monitor,
+ const siginfo_t *info, lldb::pid_t pid);
+
+ static ProcessMessage MonitorSignal(ProcessMonitor *monitor,
+ const siginfo_t *info, lldb::pid_t pid);
+
+ void DoOperation(Operation *op);
+
+ /// Stops the child monitor thread.
+ void StopMonitoringChildProcess();
+
+ /// Stops the operation thread used to attach/launch a process.
+ void StopOpThread();
+};
+
+#endif // #ifndef liblldb_ProcessMonitor_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIX.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIX.h
new file mode 100644
index 000000000000..cf52a065232c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIX.h
@@ -0,0 +1,63 @@
+//===-- RegisterContextPOSIX.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 liblldb_RegisterContextPOSIX_H_
+#define liblldb_RegisterContextPOSIX_H_
+
+#include "Plugins/Process/Utility/RegisterInfoInterface.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/ArchSpec.h"
+
+/// \class POSIXBreakpointProtocol
+///
+/// Extends RegisterClass with a few virtual operations useful on POSIX.
+class POSIXBreakpointProtocol {
+public:
+ POSIXBreakpointProtocol() { m_watchpoints_initialized = false; }
+ virtual ~POSIXBreakpointProtocol() {}
+
+ /// Updates the register state of the associated thread after hitting a
+ /// breakpoint (if that make sense for the architecture). Default
+ /// implementation simply returns true for architectures which do not
+ /// require any update.
+ ///
+ /// \return
+ /// True if the operation succeeded and false otherwise.
+ virtual bool UpdateAfterBreakpoint() = 0;
+
+ /// Determines the index in lldb's register file given a kernel byte offset.
+ virtual unsigned GetRegisterIndexFromOffset(unsigned offset) = 0;
+
+ // Checks to see if a watchpoint specified by hw_index caused the inferior
+ // to stop.
+ virtual bool IsWatchpointHit(uint32_t hw_index) = 0;
+
+ // Resets any watchpoints that have been hit.
+ virtual bool ClearWatchpointHits() = 0;
+
+ // Returns the watchpoint address associated with a watchpoint hardware
+ // index.
+ virtual lldb::addr_t GetWatchpointAddress(uint32_t hw_index) = 0;
+
+ virtual bool IsWatchpointVacant(uint32_t hw_index) = 0;
+
+ virtual bool SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size,
+ bool read, bool write,
+ uint32_t hw_index) = 0;
+
+ // From lldb_private::RegisterContext
+ virtual uint32_t NumSupportedHardwareWatchpoints() = 0;
+
+ // Force m_watchpoints_initialized to TRUE
+ void ForceWatchpointsInitialized() { m_watchpoints_initialized = true; }
+
+protected:
+ bool m_watchpoints_initialized;
+};
+
+#endif // #ifndef liblldb_RegisterContextPOSIX_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm.cpp
new file mode 100644
index 000000000000..f0c4526357cc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm.cpp
@@ -0,0 +1,259 @@
+//===-- RegisterContextPOSIXProcessMonitor_arm.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 "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include "ProcessFreeBSD.h"
+#include "ProcessMonitor.h"
+#include "RegisterContextPOSIXProcessMonitor_arm.h"
+#include "Plugins/Process/Utility/RegisterContextPOSIX_arm.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+#define REG_CONTEXT_SIZE (GetGPRSize())
+
+RegisterContextPOSIXProcessMonitor_arm::RegisterContextPOSIXProcessMonitor_arm(
+ Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info)
+ : RegisterContextPOSIX_arm(thread, concrete_frame_idx, register_info) {}
+
+ProcessMonitor &RegisterContextPOSIXProcessMonitor_arm::GetMonitor() {
+ ProcessSP base = CalculateProcess();
+ ProcessFreeBSD *process = static_cast<ProcessFreeBSD *>(base.get());
+ return process->GetMonitor();
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::ReadGPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadGPR(m_thread.GetID(), &m_gpr_arm, GetGPRSize());
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::ReadFPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadFPR(m_thread.GetID(), &m_fpr, sizeof(m_fpr));
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::WriteGPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteGPR(m_thread.GetID(), &m_gpr_arm, GetGPRSize());
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::WriteFPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteFPR(m_thread.GetID(), &m_fpr, sizeof(m_fpr));
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::ReadRegister(
+ const unsigned reg, RegisterValue &value) {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadRegisterValue(m_thread.GetID(), GetRegisterOffset(reg),
+ GetRegisterName(reg), GetRegisterSize(reg),
+ value);
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::WriteRegister(
+ const unsigned reg, const RegisterValue &value) {
+ unsigned reg_to_write = reg;
+ RegisterValue value_to_write = value;
+
+ // Check if this is a subregister of a full register.
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg);
+ if (reg_info->invalidate_regs &&
+ (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM)) {
+ RegisterValue full_value;
+ uint32_t full_reg = reg_info->invalidate_regs[0];
+ const RegisterInfo *full_reg_info = GetRegisterInfoAtIndex(full_reg);
+
+ // Read the full register.
+ if (ReadRegister(full_reg_info, full_value)) {
+ Status error;
+ ByteOrder byte_order = GetByteOrder();
+ uint8_t dst[RegisterValue::kMaxRegisterByteSize];
+
+ // Get the bytes for the full register.
+ const uint32_t dest_size = full_value.GetAsMemoryData(
+ full_reg_info, dst, sizeof(dst), byte_order, error);
+ if (error.Success() && dest_size) {
+ uint8_t src[RegisterValue::kMaxRegisterByteSize];
+
+ // Get the bytes for the source data.
+ const uint32_t src_size = value.GetAsMemoryData(
+ reg_info, src, sizeof(src), byte_order, error);
+ if (error.Success() && src_size && (src_size < dest_size)) {
+ // Copy the src bytes to the destination.
+ memcpy(dst + (reg_info->byte_offset & 0x1), src, src_size);
+ // Set this full register as the value to write.
+ value_to_write.SetBytes(dst, full_value.GetByteSize(), byte_order);
+ value_to_write.SetType(full_reg_info);
+ reg_to_write = full_reg;
+ }
+ }
+ }
+ }
+
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteRegisterValue(
+ m_thread.GetID(), GetRegisterOffset(reg_to_write),
+ GetRegisterName(reg_to_write), value_to_write);
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::ReadRegister(
+ const RegisterInfo *reg_info, RegisterValue &value) {
+ if (!reg_info)
+ return false;
+
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ if (IsFPR(reg)) {
+ if (!ReadFPR())
+ return false;
+ } else {
+ return ReadRegister(reg, value);
+ }
+
+ // Get pointer to m_fpr variable and set the data from it.
+ assert(reg_info->byte_offset < sizeof m_fpr);
+ uint8_t *src = (uint8_t *)&m_fpr + reg_info->byte_offset;
+ switch (reg_info->byte_size) {
+ case 2:
+ value.SetUInt16(*(uint16_t *)src);
+ return true;
+ case 4:
+ value.SetUInt32(*(uint32_t *)src);
+ return true;
+ case 8:
+ value.SetUInt64(*(uint64_t *)src);
+ return true;
+ default:
+ assert(false && "Unhandled data size.");
+ return false;
+ }
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ if (IsGPR(reg)) {
+ return WriteRegister(reg, value);
+ } else if (IsFPR(reg)) {
+ return WriteFPR();
+ }
+
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::ReadAllRegisterValues(
+ DataBufferSP &data_sp) {
+ bool success = false;
+ data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0));
+ if (ReadGPR() && ReadFPR()) {
+ uint8_t *dst = data_sp->GetBytes();
+ success = dst != 0;
+
+ if (success) {
+ ::memcpy(dst, &m_gpr_arm, GetGPRSize());
+ dst += GetGPRSize();
+ ::memcpy(dst, &m_fpr, sizeof(m_fpr));
+ }
+ }
+ return success;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::WriteAllRegisterValues(
+ const DataBufferSP &data_sp) {
+ bool success = false;
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) {
+ uint8_t *src = data_sp->GetBytes();
+ if (src) {
+ ::memcpy(&m_gpr_arm, src, GetGPRSize());
+
+ if (WriteGPR()) {
+ src += GetGPRSize();
+ ::memcpy(&m_fpr, src, sizeof(m_fpr));
+
+ success = WriteFPR();
+ }
+ }
+ }
+ return success;
+}
+
+uint32_t RegisterContextPOSIXProcessMonitor_arm::SetHardwareWatchpoint(
+ addr_t addr, size_t size, bool read, bool write) {
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+ uint32_t hw_index;
+
+ for (hw_index = 0; hw_index < num_hw_watchpoints; ++hw_index) {
+ if (IsWatchpointVacant(hw_index))
+ return SetHardwareWatchpointWithIndex(addr, size, read, write, hw_index);
+ }
+
+ return LLDB_INVALID_INDEX32;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::ClearHardwareWatchpoint(
+ uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::HardwareSingleStep(bool enable) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::UpdateAfterBreakpoint() {
+ lldb::addr_t pc;
+
+ if ((pc = GetPC()) == LLDB_INVALID_ADDRESS)
+ return false;
+
+ return true;
+}
+
+unsigned RegisterContextPOSIXProcessMonitor_arm::GetRegisterIndexFromOffset(
+ unsigned offset) {
+ unsigned reg;
+ for (reg = 0; reg < k_num_registers_arm; reg++) {
+ if (GetRegisterInfo()[reg].byte_offset == offset)
+ break;
+ }
+ assert(reg < k_num_registers_arm && "Invalid register offset.");
+ return reg;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::IsWatchpointHit(
+ uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::ClearWatchpointHits() {
+ return false;
+}
+
+addr_t RegisterContextPOSIXProcessMonitor_arm::GetWatchpointAddress(
+ uint32_t hw_index) {
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::IsWatchpointVacant(
+ uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm::SetHardwareWatchpointWithIndex(
+ addr_t addr, size_t size, bool read, bool write, uint32_t hw_index) {
+ return false;
+}
+
+uint32_t
+RegisterContextPOSIXProcessMonitor_arm::NumSupportedHardwareWatchpoints() {
+ return 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm.h
new file mode 100644
index 000000000000..b376967df99c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm.h
@@ -0,0 +1,76 @@
+//===-- RegisterContextPOSIXProcessMonitor_arm.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 liblldb_RegisterContextPOSIXProcessMonitor_arm_H_
+#define liblldb_RegisterContextPOSIXProcessMonitor_arm_H_
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_arm.h"
+#include "RegisterContextPOSIX.h"
+
+class RegisterContextPOSIXProcessMonitor_arm : public RegisterContextPOSIX_arm,
+ public POSIXBreakpointProtocol {
+public:
+ RegisterContextPOSIXProcessMonitor_arm(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info);
+
+protected:
+ bool ReadGPR();
+
+ bool ReadFPR();
+
+ bool WriteGPR();
+
+ bool WriteFPR();
+
+ // lldb_private::RegisterContext
+ bool ReadRegister(const unsigned reg, lldb_private::RegisterValue &value);
+
+ bool WriteRegister(const unsigned reg,
+ const lldb_private::RegisterValue &value);
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value);
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value);
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp);
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp);
+
+ uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read,
+ bool write);
+
+ bool ClearHardwareWatchpoint(uint32_t hw_index);
+
+ bool HardwareSingleStep(bool enable);
+
+ // POSIXBreakpointProtocol
+ bool UpdateAfterBreakpoint();
+
+ unsigned GetRegisterIndexFromOffset(unsigned offset);
+
+ bool IsWatchpointHit(uint32_t hw_index);
+
+ bool ClearWatchpointHits();
+
+ lldb::addr_t GetWatchpointAddress(uint32_t hw_index);
+
+ bool IsWatchpointVacant(uint32_t hw_index);
+
+ bool SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, bool read,
+ bool write, uint32_t hw_index);
+
+ uint32_t NumSupportedHardwareWatchpoints();
+
+private:
+ ProcessMonitor &GetMonitor();
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.cpp
new file mode 100644
index 000000000000..147f4b56a804
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.cpp
@@ -0,0 +1,264 @@
+//===-- RegisterContextPOSIXProcessMonitor_arm64.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 "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_arm64.h"
+#include "ProcessFreeBSD.h"
+#include "ProcessMonitor.h"
+#include "RegisterContextPOSIXProcessMonitor_arm64.h"
+
+#define REG_CONTEXT_SIZE (GetGPRSize())
+
+using namespace lldb;
+using namespace lldb_private;
+
+RegisterContextPOSIXProcessMonitor_arm64::
+ RegisterContextPOSIXProcessMonitor_arm64(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info)
+ : RegisterContextPOSIX_arm64(thread, concrete_frame_idx, register_info) {}
+
+ProcessMonitor &RegisterContextPOSIXProcessMonitor_arm64::GetMonitor() {
+ lldb::ProcessSP base = CalculateProcess();
+ ProcessFreeBSD *process = static_cast<ProcessFreeBSD *>(base.get());
+ return process->GetMonitor();
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::ReadGPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadGPR(m_thread.GetID(), &m_gpr_arm64, GetGPRSize());
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::ReadFPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadFPR(m_thread.GetID(), &m_fpr, sizeof m_fpr);
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::WriteGPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteGPR(m_thread.GetID(), &m_gpr_arm64, GetGPRSize());
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::WriteFPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteFPR(m_thread.GetID(), &m_fpr, sizeof m_fpr);
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::ReadRegister(
+ const unsigned reg, lldb_private::RegisterValue &value) {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadRegisterValue(m_thread.GetID(), GetRegisterOffset(reg),
+ GetRegisterName(reg), GetRegisterSize(reg),
+ value);
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::WriteRegister(
+ const unsigned reg, const lldb_private::RegisterValue &value) {
+ unsigned reg_to_write = reg;
+ lldb_private::RegisterValue value_to_write = value;
+
+ // Check if this is a subregister of a full register.
+ const lldb_private::RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg);
+ if (reg_info->invalidate_regs &&
+ (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM)) {
+ lldb_private::RegisterValue full_value;
+ uint32_t full_reg = reg_info->invalidate_regs[0];
+ const lldb_private::RegisterInfo *full_reg_info =
+ GetRegisterInfoAtIndex(full_reg);
+
+ // Read the full register.
+ if (ReadRegister(full_reg_info, full_value)) {
+ lldb_private::Status error;
+ lldb::ByteOrder byte_order = GetByteOrder();
+ uint8_t dst[lldb_private::RegisterValue::kMaxRegisterByteSize];
+
+ // Get the bytes for the full register.
+ const uint32_t dest_size = full_value.GetAsMemoryData(
+ full_reg_info, dst, sizeof(dst), byte_order, error);
+ if (error.Success() && dest_size) {
+ uint8_t src[lldb_private::RegisterValue::kMaxRegisterByteSize];
+
+ // Get the bytes for the source data.
+ const uint32_t src_size = value.GetAsMemoryData(
+ reg_info, src, sizeof(src), byte_order, error);
+ if (error.Success() && src_size && (src_size < dest_size)) {
+ // Copy the src bytes to the destination.
+ ::memcpy(dst + (reg_info->byte_offset & 0x1), src, src_size);
+ // Set this full register as the value to write.
+ value_to_write.SetBytes(dst, full_value.GetByteSize(), byte_order);
+ value_to_write.SetType(full_reg_info);
+ reg_to_write = full_reg;
+ }
+ }
+ }
+ }
+
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteRegisterValue(
+ m_thread.GetID(), GetRegisterOffset(reg_to_write),
+ GetRegisterName(reg_to_write), value_to_write);
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::ReadRegister(
+ const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) {
+ if (!reg_info)
+ return false;
+
+ const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+
+ if (IsFPR(reg)) {
+ if (!ReadFPR())
+ return false;
+ } else {
+ uint32_t full_reg = reg;
+ bool is_subreg = reg_info->invalidate_regs &&
+ (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM);
+
+ if (is_subreg) {
+ // Read the full aligned 64-bit register.
+ full_reg = reg_info->invalidate_regs[0];
+ }
+ return ReadRegister(full_reg, value);
+ }
+
+ // Get pointer to m_fpr variable and set the data from it.
+ assert(reg_info->byte_offset < sizeof m_fpr);
+ uint8_t *src = (uint8_t *)&m_fpr + reg_info->byte_offset;
+ switch (reg_info->byte_size) {
+ case 2:
+ value.SetUInt16(*(uint16_t *)src);
+ return true;
+ case 4:
+ value.SetUInt32(*(uint32_t *)src);
+ return true;
+ case 8:
+ value.SetUInt64(*(uint64_t *)src);
+ return true;
+ default:
+ assert(false && "Unhandled data size.");
+ return false;
+ }
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::WriteRegister(
+ const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+
+ if (IsGPR(reg))
+ return WriteRegister(reg, value);
+
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ bool success = false;
+ data_sp.reset(new lldb_private::DataBufferHeap(REG_CONTEXT_SIZE, 0));
+ if (ReadGPR() && ReadFPR()) {
+ uint8_t *dst = data_sp->GetBytes();
+ success = dst != 0;
+
+ if (success) {
+ ::memcpy(dst, &m_gpr_arm64, GetGPRSize());
+ dst += GetGPRSize();
+ ::memcpy(dst, &m_fpr, sizeof m_fpr);
+ }
+ }
+ return success;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ bool success = false;
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) {
+ uint8_t *src = data_sp->GetBytes();
+ if (src) {
+ ::memcpy(&m_gpr_arm64, src, GetGPRSize());
+ if (WriteGPR()) {
+ src += GetGPRSize();
+ ::memcpy(&m_fpr, src, sizeof m_fpr);
+ success = WriteFPR();
+ }
+ }
+ }
+ return success;
+}
+
+uint32_t RegisterContextPOSIXProcessMonitor_arm64::SetHardwareWatchpoint(
+ lldb::addr_t addr, size_t size, bool read, bool write) {
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+ uint32_t hw_index;
+
+ for (hw_index = 0; hw_index < num_hw_watchpoints; ++hw_index) {
+ if (IsWatchpointVacant(hw_index))
+ return SetHardwareWatchpointWithIndex(addr, size, read, write, hw_index);
+ }
+
+ return LLDB_INVALID_INDEX32;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::ClearHardwareWatchpoint(
+ uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::HardwareSingleStep(bool enable) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::UpdateAfterBreakpoint() {
+ if (GetPC() == LLDB_INVALID_ADDRESS)
+ return false;
+
+ return true;
+}
+
+unsigned RegisterContextPOSIXProcessMonitor_arm64::GetRegisterIndexFromOffset(
+ unsigned offset) {
+ unsigned reg;
+ for (reg = 0; reg < k_num_registers_arm64; reg++) {
+ if (GetRegisterInfo()[reg].byte_offset == offset)
+ break;
+ }
+ assert(reg < k_num_registers_arm64 && "Invalid register offset.");
+ return reg;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::IsWatchpointHit(
+ uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::ClearWatchpointHits() {
+ return false;
+}
+
+lldb::addr_t RegisterContextPOSIXProcessMonitor_arm64::GetWatchpointAddress(
+ uint32_t hw_index) {
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::IsWatchpointVacant(
+ uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_arm64::SetHardwareWatchpointWithIndex(
+ lldb::addr_t addr, size_t size, bool read, bool write, uint32_t hw_index) {
+ return false;
+}
+
+uint32_t
+RegisterContextPOSIXProcessMonitor_arm64::NumSupportedHardwareWatchpoints() {
+ return 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.h
new file mode 100644
index 000000000000..d54d34e89cad
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.h
@@ -0,0 +1,77 @@
+//===-- RegisterContextPOSIXProcessMonitor_arm64.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 liblldb_RegisterContextPOSIXProcessMonitor_arm64_H_
+#define liblldb_RegisterContextPOSIXProcessMonitor_arm64_H_
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_arm64.h"
+#include "RegisterContextPOSIX.h"
+
+class RegisterContextPOSIXProcessMonitor_arm64
+ : public RegisterContextPOSIX_arm64,
+ public POSIXBreakpointProtocol {
+public:
+ RegisterContextPOSIXProcessMonitor_arm64(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info);
+
+protected:
+ bool ReadGPR();
+
+ bool ReadFPR();
+
+ bool WriteGPR();
+
+ bool WriteFPR();
+
+ // lldb_private::RegisterContext
+ bool ReadRegister(const unsigned reg, lldb_private::RegisterValue &value);
+
+ bool WriteRegister(const unsigned reg,
+ const lldb_private::RegisterValue &value);
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value);
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value);
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp);
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp);
+
+ uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read,
+ bool write);
+
+ bool ClearHardwareWatchpoint(uint32_t hw_index);
+
+ bool HardwareSingleStep(bool enable);
+
+ // POSIXBreakpointProtocol
+ bool UpdateAfterBreakpoint();
+
+ unsigned GetRegisterIndexFromOffset(unsigned offset);
+
+ bool IsWatchpointHit(uint32_t hw_index);
+
+ bool ClearWatchpointHits();
+
+ lldb::addr_t GetWatchpointAddress(uint32_t hw_index);
+
+ bool IsWatchpointVacant(uint32_t hw_index);
+
+ bool SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, bool read,
+ bool write, uint32_t hw_index);
+
+ uint32_t NumSupportedHardwareWatchpoints();
+
+private:
+ ProcessMonitor &GetMonitor();
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_mips64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_mips64.cpp
new file mode 100644
index 000000000000..db9b5a6a038c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_mips64.cpp
@@ -0,0 +1,262 @@
+//===-- RegisterContextPOSIXProcessMonitor_mips64.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 "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_mips64.h"
+#include "ProcessFreeBSD.h"
+#include "ProcessMonitor.h"
+#include "RegisterContextPOSIXProcessMonitor_mips64.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+#define REG_CONTEXT_SIZE (GetGPRSize())
+
+RegisterContextPOSIXProcessMonitor_mips64::
+ RegisterContextPOSIXProcessMonitor_mips64(
+ Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info)
+ : RegisterContextPOSIX_mips64(thread, concrete_frame_idx, register_info) {}
+
+ProcessMonitor &RegisterContextPOSIXProcessMonitor_mips64::GetMonitor() {
+ ProcessSP base = CalculateProcess();
+ ProcessFreeBSD *process = static_cast<ProcessFreeBSD *>(base.get());
+ return process->GetMonitor();
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::ReadGPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadGPR(m_thread.GetID(), &m_gpr_mips64, GetGPRSize());
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::ReadFPR() {
+ // XXX not yet implemented
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::WriteGPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteGPR(m_thread.GetID(), &m_gpr_mips64, GetGPRSize());
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::WriteFPR() {
+ // XXX not yet implemented
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::ReadRegister(
+ const unsigned reg, RegisterValue &value) {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadRegisterValue(m_thread.GetID(), GetRegisterOffset(reg),
+ GetRegisterName(reg), GetRegisterSize(reg),
+ value);
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::WriteRegister(
+ const unsigned reg, const RegisterValue &value) {
+ unsigned reg_to_write = reg;
+ RegisterValue value_to_write = value;
+
+ // Check if this is a subregister of a full register.
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg);
+ if (reg_info->invalidate_regs &&
+ (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM)) {
+ RegisterValue full_value;
+ uint32_t full_reg = reg_info->invalidate_regs[0];
+ const RegisterInfo *full_reg_info = GetRegisterInfoAtIndex(full_reg);
+
+ // Read the full register.
+ if (ReadRegister(full_reg_info, full_value)) {
+ Status error;
+ ByteOrder byte_order = GetByteOrder();
+ uint8_t dst[RegisterValue::kMaxRegisterByteSize];
+
+ // Get the bytes for the full register.
+ const uint32_t dest_size = full_value.GetAsMemoryData(
+ full_reg_info, dst, sizeof(dst), byte_order, error);
+ if (error.Success() && dest_size) {
+ uint8_t src[RegisterValue::kMaxRegisterByteSize];
+
+ // Get the bytes for the source data.
+ const uint32_t src_size = value.GetAsMemoryData(
+ reg_info, src, sizeof(src), byte_order, error);
+ if (error.Success() && src_size && (src_size < dest_size)) {
+ // Copy the src bytes to the destination.
+ memcpy(dst + (reg_info->byte_offset & 0x1), src, src_size);
+ // Set this full register as the value to write.
+ value_to_write.SetBytes(dst, full_value.GetByteSize(), byte_order);
+ value_to_write.SetType(full_reg_info);
+ reg_to_write = full_reg;
+ }
+ }
+ }
+ }
+
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteRegisterValue(
+ m_thread.GetID(), GetRegisterOffset(reg_to_write),
+ GetRegisterName(reg_to_write), value_to_write);
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::ReadRegister(
+ const RegisterInfo *reg_info, RegisterValue &value) {
+ if (!reg_info)
+ return false;
+
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ if (IsFPR(reg)) {
+ if (!ReadFPR())
+ return false;
+ } else {
+ uint32_t full_reg = reg;
+ bool is_subreg = reg_info->invalidate_regs &&
+ (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM);
+
+ if (is_subreg) {
+ // Read the full aligned 64-bit register.
+ full_reg = reg_info->invalidate_regs[0];
+ }
+
+ bool success = ReadRegister(full_reg, value);
+
+ if (success) {
+ // If our read was not aligned (for ah,bh,ch,dh), shift our returned
+ // value one byte to the right.
+ if (is_subreg && (reg_info->byte_offset & 0x1))
+ value.SetUInt64(value.GetAsUInt64() >> 8);
+
+ // If our return byte size was greater than the return value reg size,
+ // then use the type specified by reg_info rather than the uint64_t
+ // default
+ if (value.GetByteSize() > reg_info->byte_size)
+ value.SetType(reg_info);
+ }
+ return success;
+ }
+
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ if (IsGPR(reg))
+ return WriteRegister(reg, value);
+
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::ReadAllRegisterValues(
+ DataBufferSP &data_sp) {
+ bool success = false;
+ data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0));
+ if (ReadGPR() && ReadFPR()) {
+ uint8_t *dst = data_sp->GetBytes();
+ success = dst != 0;
+
+ if (success) {
+ ::memcpy(dst, &m_gpr_mips64, GetGPRSize());
+ }
+ }
+ return success;
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::WriteAllRegisterValues(
+ const DataBufferSP &data_sp) {
+ bool success = false;
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) {
+ uint8_t *src = data_sp->GetBytes();
+ if (src) {
+ ::memcpy(&m_gpr_mips64, src, GetGPRSize());
+
+ if (WriteGPR()) {
+ src += GetGPRSize();
+ }
+ }
+ }
+ return success;
+}
+
+uint32_t RegisterContextPOSIXProcessMonitor_mips64::SetHardwareWatchpoint(
+ addr_t addr, size_t size, bool read, bool write) {
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+ uint32_t hw_index;
+
+ for (hw_index = 0; hw_index < num_hw_watchpoints; ++hw_index) {
+ if (IsWatchpointVacant(hw_index))
+ return SetHardwareWatchpointWithIndex(addr, size, read, write, hw_index);
+ }
+
+ return LLDB_INVALID_INDEX32;
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::ClearHardwareWatchpoint(
+ uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::HardwareSingleStep(
+ bool enable) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::UpdateAfterBreakpoint() {
+ // PC points one byte past the int3 responsible for the breakpoint.
+ lldb::addr_t pc;
+
+ if ((pc = GetPC()) == LLDB_INVALID_ADDRESS)
+ return false;
+
+ SetPC(pc - 1);
+ return true;
+}
+
+unsigned RegisterContextPOSIXProcessMonitor_mips64::GetRegisterIndexFromOffset(
+ unsigned offset) {
+ unsigned reg;
+ for (reg = 0; reg < k_num_registers_mips64; reg++) {
+ if (GetRegisterInfo()[reg].byte_offset == offset)
+ break;
+ }
+ assert(reg < k_num_registers_mips64 && "Invalid register offset.");
+ return reg;
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::IsWatchpointHit(
+ uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::ClearWatchpointHits() {
+ return false;
+}
+
+addr_t RegisterContextPOSIXProcessMonitor_mips64::GetWatchpointAddress(
+ uint32_t hw_index) {
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::IsWatchpointVacant(
+ uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_mips64::SetHardwareWatchpointWithIndex(
+ addr_t addr, size_t size, bool read, bool write, uint32_t hw_index) {
+ return false;
+}
+
+uint32_t
+RegisterContextPOSIXProcessMonitor_mips64::NumSupportedHardwareWatchpoints() {
+ return 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_mips64.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_mips64.h
new file mode 100644
index 000000000000..a482cd3f1e1c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_mips64.h
@@ -0,0 +1,80 @@
+//===-- RegisterContextPOSIXProcessMonitor_mips64.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 liblldb_RegisterContextPOSIXProcessMonitor_mips64_H_
+#define liblldb_RegisterContextPOSIXProcessMonitor_mips64_H_
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_mips64.h"
+#include "Plugins/Process/Utility/lldb-mips-freebsd-register-enums.h"
+#include "RegisterContextPOSIX.h"
+
+class RegisterContextPOSIXProcessMonitor_mips64
+ : public RegisterContextPOSIX_mips64,
+ public POSIXBreakpointProtocol {
+public:
+ RegisterContextPOSIXProcessMonitor_mips64(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info);
+
+protected:
+ bool ReadGPR();
+
+ bool ReadFPR();
+
+ bool WriteGPR();
+
+ bool WriteFPR();
+
+ // lldb_private::RegisterContext
+ bool ReadRegister(const unsigned reg, lldb_private::RegisterValue &value);
+
+ bool WriteRegister(const unsigned reg,
+ const lldb_private::RegisterValue &value);
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value);
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value);
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp);
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp);
+
+ uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read,
+ bool write);
+
+ bool ClearHardwareWatchpoint(uint32_t hw_index);
+
+ bool HardwareSingleStep(bool enable);
+
+ // POSIXBreakpointProtocol
+ bool UpdateAfterBreakpoint();
+
+ unsigned GetRegisterIndexFromOffset(unsigned offset);
+
+ bool IsWatchpointHit(uint32_t hw_index);
+
+ bool ClearWatchpointHits();
+
+ lldb::addr_t GetWatchpointAddress(uint32_t hw_index);
+
+ bool IsWatchpointVacant(uint32_t hw_index);
+
+ bool SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, bool read,
+ bool write, uint32_t hw_index);
+
+ uint32_t NumSupportedHardwareWatchpoints();
+
+private:
+ uint64_t
+ m_gpr_mips64[k_num_gpr_registers_mips64]; // general purpose registers.
+ ProcessMonitor &GetMonitor();
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_powerpc.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_powerpc.cpp
new file mode 100644
index 000000000000..77694733fa39
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_powerpc.cpp
@@ -0,0 +1,274 @@
+//===-- RegisterContextPOSIXProcessMonitor_powerpc.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 "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include "ProcessFreeBSD.h"
+#include "ProcessMonitor.h"
+#include "RegisterContextPOSIXProcessMonitor_powerpc.h"
+#include "Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+#define REG_CONTEXT_SIZE (GetGPRSize())
+
+RegisterContextPOSIXProcessMonitor_powerpc::
+ RegisterContextPOSIXProcessMonitor_powerpc(
+ Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info)
+ : RegisterContextPOSIX_powerpc(thread, concrete_frame_idx, register_info) {}
+
+ProcessMonitor &RegisterContextPOSIXProcessMonitor_powerpc::GetMonitor() {
+ ProcessSP base = CalculateProcess();
+ ProcessFreeBSD *process = static_cast<ProcessFreeBSD *>(base.get());
+ return process->GetMonitor();
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::ReadGPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadGPR(m_thread.GetID(), &m_gpr_powerpc, GetGPRSize());
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::ReadFPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadFPR(m_thread.GetID(), &m_fpr_powerpc,
+ sizeof(m_fpr_powerpc));
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::ReadVMX() {
+ // XXX: Need a way to read/write process VMX registers with ptrace.
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::WriteGPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteGPR(m_thread.GetID(), &m_gpr_powerpc, GetGPRSize());
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::WriteFPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteFPR(m_thread.GetID(), &m_fpr_powerpc,
+ sizeof(m_fpr_powerpc));
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::WriteVMX() {
+ // XXX: Need a way to read/write process VMX registers with ptrace.
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::ReadRegister(
+ const unsigned reg, RegisterValue &value) {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadRegisterValue(m_thread.GetID(), GetRegisterOffset(reg),
+ GetRegisterName(reg), GetRegisterSize(reg),
+ value);
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::WriteRegister(
+ const unsigned reg, const RegisterValue &value) {
+ unsigned reg_to_write = reg;
+ RegisterValue value_to_write = value;
+
+ // Check if this is a subregister of a full register.
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg);
+ if (reg_info->invalidate_regs &&
+ (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM)) {
+ RegisterValue full_value;
+ uint32_t full_reg = reg_info->invalidate_regs[0];
+ const RegisterInfo *full_reg_info = GetRegisterInfoAtIndex(full_reg);
+
+ // Read the full register.
+ if (ReadRegister(full_reg_info, full_value)) {
+ Status error;
+ ByteOrder byte_order = GetByteOrder();
+ uint8_t dst[RegisterValue::kMaxRegisterByteSize];
+
+ // Get the bytes for the full register.
+ const uint32_t dest_size = full_value.GetAsMemoryData(
+ full_reg_info, dst, sizeof(dst), byte_order, error);
+ if (error.Success() && dest_size) {
+ uint8_t src[RegisterValue::kMaxRegisterByteSize];
+
+ // Get the bytes for the source data.
+ const uint32_t src_size = value.GetAsMemoryData(
+ reg_info, src, sizeof(src), byte_order, error);
+ if (error.Success() && src_size && (src_size < dest_size)) {
+ // Copy the src bytes to the destination.
+ memcpy(dst + (reg_info->byte_offset & 0x1), src, src_size);
+ // Set this full register as the value to write.
+ value_to_write.SetBytes(dst, full_value.GetByteSize(), byte_order);
+ value_to_write.SetType(full_reg_info);
+ reg_to_write = full_reg;
+ }
+ }
+ }
+ }
+
+ ProcessMonitor &monitor = GetMonitor();
+ // Account for the fact that 32-bit targets on powerpc64 really use 64-bit
+ // registers in ptrace, but expose here 32-bit registers with a higher
+ // offset.
+ uint64_t offset = GetRegisterOffset(reg_to_write);
+ offset &= ~(sizeof(uintptr_t) - 1);
+ return monitor.WriteRegisterValue(
+ m_thread.GetID(), offset, GetRegisterName(reg_to_write), value_to_write);
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::ReadRegister(
+ const RegisterInfo *reg_info, RegisterValue &value) {
+ if (!reg_info)
+ return false;
+
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ if (IsFPR(reg)) {
+ if (!ReadFPR())
+ return false;
+ uint8_t *src = (uint8_t *)&m_fpr_powerpc + reg_info->byte_offset;
+ value.SetUInt64(*(uint64_t *)src);
+ } else if (IsGPR(reg)) {
+ bool success = ReadRegister(reg, value);
+
+ if (success) {
+ // If our return byte size was greater than the return value reg size,
+ // then use the type specified by reg_info rather than the uint64_t
+ // default
+ if (value.GetByteSize() > reg_info->byte_size)
+ value.SetType(reg_info);
+ }
+ return success;
+ }
+
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ if (IsGPR(reg)) {
+ return WriteRegister(reg, value);
+ } else if (IsFPR(reg)) {
+ assert(reg_info->byte_offset < sizeof(m_fpr_powerpc));
+ uint8_t *dst = (uint8_t *)&m_fpr_powerpc + reg_info->byte_offset;
+ *(uint64_t *)dst = value.GetAsUInt64();
+ return WriteFPR();
+ }
+
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::ReadAllRegisterValues(
+ DataBufferSP &data_sp) {
+ bool success = false;
+ data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0));
+ if (ReadGPR() && ReadFPR()) {
+ uint8_t *dst = data_sp->GetBytes();
+ success = dst != 0;
+
+ if (success) {
+ ::memcpy(dst, &m_gpr_powerpc, GetGPRSize());
+ dst += GetGPRSize();
+ }
+ }
+ return success;
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::WriteAllRegisterValues(
+ const DataBufferSP &data_sp) {
+ bool success = false;
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) {
+ uint8_t *src = data_sp->GetBytes();
+ if (src) {
+ ::memcpy(&m_gpr_powerpc, src, GetGPRSize());
+
+ if (WriteGPR()) {
+ src += GetGPRSize();
+ ::memcpy(&m_fpr_powerpc, src, sizeof(m_fpr_powerpc));
+
+ success = WriteFPR();
+ }
+ }
+ }
+ return success;
+}
+
+uint32_t RegisterContextPOSIXProcessMonitor_powerpc::SetHardwareWatchpoint(
+ addr_t addr, size_t size, bool read, bool write) {
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+ uint32_t hw_index;
+
+ for (hw_index = 0; hw_index < num_hw_watchpoints; ++hw_index) {
+ if (IsWatchpointVacant(hw_index))
+ return SetHardwareWatchpointWithIndex(addr, size, read, write, hw_index);
+ }
+
+ return LLDB_INVALID_INDEX32;
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::ClearHardwareWatchpoint(
+ uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::HardwareSingleStep(
+ bool enable) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::UpdateAfterBreakpoint() {
+ lldb::addr_t pc;
+
+ if ((pc = GetPC()) == LLDB_INVALID_ADDRESS)
+ return false;
+
+ return true;
+}
+
+unsigned RegisterContextPOSIXProcessMonitor_powerpc::GetRegisterIndexFromOffset(
+ unsigned offset) {
+ unsigned reg;
+ for (reg = 0; reg < k_num_registers_powerpc; reg++) {
+ if (GetRegisterInfo()[reg].byte_offset == offset)
+ break;
+ }
+ assert(reg < k_num_registers_powerpc && "Invalid register offset.");
+ return reg;
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::IsWatchpointHit(
+ uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::ClearWatchpointHits() {
+ return false;
+}
+
+addr_t RegisterContextPOSIXProcessMonitor_powerpc::GetWatchpointAddress(
+ uint32_t hw_index) {
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::IsWatchpointVacant(
+ uint32_t hw_index) {
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_powerpc::SetHardwareWatchpointWithIndex(
+ addr_t addr, size_t size, bool read, bool write, uint32_t hw_index) {
+ return false;
+}
+
+uint32_t
+RegisterContextPOSIXProcessMonitor_powerpc::NumSupportedHardwareWatchpoints() {
+ return 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_powerpc.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_powerpc.h
new file mode 100644
index 000000000000..17b649feebf1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_powerpc.h
@@ -0,0 +1,84 @@
+//===-- RegisterContextPOSIXProcessMonitor_powerpc.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 liblldb_RegisterContextPOSIXProcessMonitor_powerpc_H_
+#define liblldb_RegisterContextPOSIXProcessMonitor_powerpc_H_
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h"
+#include "RegisterContextPOSIX.h"
+
+class RegisterContextPOSIXProcessMonitor_powerpc
+ : public RegisterContextPOSIX_powerpc,
+ public POSIXBreakpointProtocol {
+public:
+ RegisterContextPOSIXProcessMonitor_powerpc(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info);
+
+protected:
+ bool IsVMX();
+
+ bool ReadGPR();
+
+ bool ReadFPR();
+
+ bool ReadVMX();
+
+ bool WriteGPR();
+
+ bool WriteFPR();
+
+ bool WriteVMX();
+
+ // lldb_private::RegisterContext
+ bool ReadRegister(const unsigned reg, lldb_private::RegisterValue &value);
+
+ bool WriteRegister(const unsigned reg,
+ const lldb_private::RegisterValue &value);
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value);
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value);
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp);
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp);
+
+ uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read,
+ bool write);
+
+ bool ClearHardwareWatchpoint(uint32_t hw_index);
+
+ bool HardwareSingleStep(bool enable);
+
+ // POSIXBreakpointProtocol
+ bool UpdateAfterBreakpoint();
+
+ unsigned GetRegisterIndexFromOffset(unsigned offset);
+
+ bool IsWatchpointHit(uint32_t hw_index);
+
+ bool ClearWatchpointHits();
+
+ lldb::addr_t GetWatchpointAddress(uint32_t hw_index);
+
+ bool IsWatchpointVacant(uint32_t hw_index);
+
+ bool SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, bool read,
+ bool write, uint32_t hw_index);
+
+ uint32_t NumSupportedHardwareWatchpoints();
+
+private:
+ ProcessMonitor &GetMonitor();
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_x86.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_x86.cpp
new file mode 100644
index 000000000000..3046d97f153c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_x86.cpp
@@ -0,0 +1,613 @@
+//===-- RegisterContextPOSIXProcessMonitor_x86.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 "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include "Plugins/Process/FreeBSD/ProcessFreeBSD.h"
+#include "Plugins/Process/FreeBSD/ProcessMonitor.h"
+#include "RegisterContextPOSIXProcessMonitor_x86.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+// Support ptrace extensions even when compiled without required kernel support
+#ifndef NT_X86_XSTATE
+#define NT_X86_XSTATE 0x202
+#endif
+
+#define REG_CONTEXT_SIZE (GetGPRSize() + sizeof(FPR))
+
+static uint32_t size_and_rw_bits(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;
+ default:
+ assert(0 && "invalid size, must be one of 1, 2, 4, or 8");
+ return 0; // Unreachable. Just to silence compiler.
+ }
+}
+
+RegisterContextPOSIXProcessMonitor_x86_64::
+ RegisterContextPOSIXProcessMonitor_x86_64(
+ Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info)
+ : RegisterContextPOSIX_x86(thread, concrete_frame_idx, register_info) {
+ // Store byte offset of fctrl (i.e. first register of FPR) wrt 'UserArea'
+ const RegisterInfo *reg_info_fctrl = GetRegisterInfoByName("fctrl");
+ m_fctrl_offset_in_userarea = reg_info_fctrl->byte_offset;
+
+ m_iovec.iov_base = &m_fpr.xsave;
+ m_iovec.iov_len = sizeof(m_fpr.xsave);
+}
+
+ProcessMonitor &RegisterContextPOSIXProcessMonitor_x86_64::GetMonitor() {
+ ProcessSP base = CalculateProcess();
+ ProcessFreeBSD *process = static_cast<ProcessFreeBSD *>(base.get());
+ return process->GetMonitor();
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::ReadGPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.ReadGPR(m_thread.GetID(), &m_gpr_x86_64, GetGPRSize());
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::ReadFPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ if (GetFPRType() == eFXSAVE)
+ return monitor.ReadFPR(m_thread.GetID(), &m_fpr.fxsave,
+ sizeof(m_fpr.fxsave));
+
+ if (GetFPRType() == eXSAVE)
+ return monitor.ReadRegisterSet(m_thread.GetID(), &m_iovec,
+ sizeof(m_fpr.xsave), NT_X86_XSTATE);
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::WriteGPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ return monitor.WriteGPR(m_thread.GetID(), &m_gpr_x86_64, GetGPRSize());
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::WriteFPR() {
+ ProcessMonitor &monitor = GetMonitor();
+ if (GetFPRType() == eFXSAVE)
+ return monitor.WriteFPR(m_thread.GetID(), &m_fpr.fxsave,
+ sizeof(m_fpr.fxsave));
+
+ if (GetFPRType() == eXSAVE)
+ return monitor.WriteRegisterSet(m_thread.GetID(), &m_iovec,
+ sizeof(m_fpr.xsave), NT_X86_XSTATE);
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::ReadRegister(
+ const unsigned reg, RegisterValue &value) {
+ ProcessMonitor &monitor = GetMonitor();
+
+#if defined(__FreeBSD__)
+ if (reg >= m_reg_info.first_dr)
+ return monitor.ReadDebugRegisterValue(
+ m_thread.GetID(), GetRegisterOffset(reg), GetRegisterName(reg),
+ GetRegisterSize(reg), value);
+#endif
+ return monitor.ReadRegisterValue(m_thread.GetID(), GetRegisterOffset(reg),
+ GetRegisterName(reg), GetRegisterSize(reg),
+ value);
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::WriteRegister(
+ const unsigned reg, const RegisterValue &value) {
+ unsigned reg_to_write = reg;
+ RegisterValue value_to_write = value;
+
+ // Check if this is a subregister of a full register.
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg);
+ if (reg_info->invalidate_regs &&
+ (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM)) {
+ RegisterValue full_value;
+ uint32_t full_reg = reg_info->invalidate_regs[0];
+ const RegisterInfo *full_reg_info = GetRegisterInfoAtIndex(full_reg);
+
+ // Read the full register.
+ if (ReadRegister(full_reg_info, full_value)) {
+ Status error;
+ ByteOrder byte_order = GetByteOrder();
+ uint8_t dst[RegisterValue::kMaxRegisterByteSize];
+
+ // Get the bytes for the full register.
+ const uint32_t dest_size = full_value.GetAsMemoryData(
+ full_reg_info, dst, sizeof(dst), byte_order, error);
+ if (error.Success() && dest_size) {
+ uint8_t src[RegisterValue::kMaxRegisterByteSize];
+
+ // Get the bytes for the source data.
+ const uint32_t src_size = value.GetAsMemoryData(
+ reg_info, src, sizeof(src), byte_order, error);
+ if (error.Success() && src_size && (src_size < dest_size)) {
+ // Copy the src bytes to the destination.
+ memcpy(dst + (reg_info->byte_offset & 0x1), src, src_size);
+ // Set this full register as the value to write.
+ value_to_write.SetBytes(dst, full_value.GetByteSize(), byte_order);
+ value_to_write.SetType(full_reg_info);
+ reg_to_write = full_reg;
+ }
+ }
+ }
+ }
+
+ ProcessMonitor &monitor = GetMonitor();
+#if defined(__FreeBSD__)
+ if (reg >= m_reg_info.first_dr)
+ return monitor.WriteDebugRegisterValue(
+ m_thread.GetID(), GetRegisterOffset(reg_to_write),
+ GetRegisterName(reg_to_write), value_to_write);
+#endif
+ return monitor.WriteRegisterValue(
+ m_thread.GetID(), GetRegisterOffset(reg_to_write),
+ GetRegisterName(reg_to_write), value_to_write);
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::ReadRegister(
+ const RegisterInfo *reg_info, RegisterValue &value) {
+ if (!reg_info)
+ return false;
+
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ if (IsFPR(reg, GetFPRType())) {
+ if (!ReadFPR())
+ return false;
+ } else {
+ uint32_t full_reg = reg;
+ bool is_subreg = reg_info->invalidate_regs &&
+ (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM);
+
+ if (is_subreg) {
+ // Read the full aligned 64-bit register.
+ full_reg = reg_info->invalidate_regs[0];
+ }
+
+ bool success = ReadRegister(full_reg, value);
+
+ if (success) {
+ // If our read was not aligned (for ah,bh,ch,dh), shift our returned
+ // value one byte to the right.
+ if (is_subreg && (reg_info->byte_offset & 0x1))
+ value.SetUInt64(value.GetAsUInt64() >> 8);
+
+ // If our return byte size was greater than the return value reg size,
+ // then use the type specified by reg_info rather than the uint64_t
+ // default
+ if (value.GetByteSize() > reg_info->byte_size)
+ value.SetType(reg_info);
+ }
+ return success;
+ }
+
+ if (reg_info->encoding == eEncodingVector) {
+ ByteOrder byte_order = GetByteOrder();
+
+ if (byte_order != ByteOrder::eByteOrderInvalid) {
+ if (reg >= m_reg_info.first_st && reg <= m_reg_info.last_st)
+ value.SetBytes(m_fpr.fxsave.stmm[reg - m_reg_info.first_st].bytes,
+ reg_info->byte_size, byte_order);
+ if (reg >= m_reg_info.first_mm && reg <= m_reg_info.last_mm)
+ value.SetBytes(m_fpr.fxsave.stmm[reg - m_reg_info.first_mm].bytes,
+ reg_info->byte_size, byte_order);
+ if (reg >= m_reg_info.first_xmm && reg <= m_reg_info.last_xmm)
+ value.SetBytes(m_fpr.fxsave.xmm[reg - m_reg_info.first_xmm].bytes,
+ reg_info->byte_size, byte_order);
+ if (reg >= m_reg_info.first_ymm && reg <= m_reg_info.last_ymm) {
+ // Concatenate ymm using the register halves in xmm.bytes and
+ // ymmh.bytes
+ if (GetFPRType() == eXSAVE && CopyXSTATEtoYMM(reg, byte_order))
+ value.SetBytes(m_ymm_set.ymm[reg - m_reg_info.first_ymm].bytes,
+ reg_info->byte_size, byte_order);
+ else
+ return false;
+ }
+ return value.GetType() == RegisterValue::eTypeBytes;
+ }
+ return false;
+ }
+
+ // Get pointer to m_fpr.fxsave variable and set the data from it. Byte
+ // offsets of all registers are calculated wrt 'UserArea' structure. However,
+ // ReadFPR() reads fpu registers {using ptrace(PT_GETFPREGS,..)} and stores
+ // them in 'm_fpr' (of type FPR structure). To extract values of fpu
+ // registers, m_fpr should be read at byte offsets calculated wrt to FPR
+ // structure.
+
+ // Since, FPR structure is also one of the member of UserArea structure.
+ // byte_offset(fpu wrt FPR) = byte_offset(fpu wrt UserArea) -
+ // byte_offset(fctrl wrt UserArea)
+ assert((reg_info->byte_offset - m_fctrl_offset_in_userarea) < sizeof(m_fpr));
+ uint8_t *src =
+ (uint8_t *)&m_fpr + reg_info->byte_offset - m_fctrl_offset_in_userarea;
+ switch (reg_info->byte_size) {
+ case 1:
+ value.SetUInt8(*(uint8_t *)src);
+ return true;
+ case 2:
+ value.SetUInt16(*(uint16_t *)src);
+ return true;
+ case 4:
+ value.SetUInt32(*(uint32_t *)src);
+ return true;
+ case 8:
+ value.SetUInt64(*(uint64_t *)src);
+ return true;
+ default:
+ assert(false && "Unhandled data size.");
+ return false;
+ }
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ if (IsGPR(reg))
+ return WriteRegister(reg, value);
+
+ if (IsFPR(reg, GetFPRType())) {
+ if (reg_info->encoding == eEncodingVector) {
+ if (reg >= m_reg_info.first_st && reg <= m_reg_info.last_st)
+ ::memcpy(m_fpr.fxsave.stmm[reg - m_reg_info.first_st].bytes,
+ value.GetBytes(), value.GetByteSize());
+
+ if (reg >= m_reg_info.first_mm && reg <= m_reg_info.last_mm)
+ ::memcpy(m_fpr.fxsave.stmm[reg - m_reg_info.first_mm].bytes,
+ value.GetBytes(), value.GetByteSize());
+
+ if (reg >= m_reg_info.first_xmm && reg <= m_reg_info.last_xmm)
+ ::memcpy(m_fpr.fxsave.xmm[reg - m_reg_info.first_xmm].bytes,
+ value.GetBytes(), value.GetByteSize());
+
+ if (reg >= m_reg_info.first_ymm && reg <= m_reg_info.last_ymm) {
+ if (GetFPRType() != eXSAVE)
+ return false; // the target processor does not support AVX
+
+ // Store ymm register content, and split into the register halves in
+ // xmm.bytes and ymmh.bytes
+ ::memcpy(m_ymm_set.ymm[reg - m_reg_info.first_ymm].bytes,
+ value.GetBytes(), value.GetByteSize());
+ if (false == CopyYMMtoXSTATE(reg, GetByteOrder()))
+ return false;
+ }
+ } else {
+ // Get pointer to m_fpr.fxsave variable and set the data to it. Byte
+ // offsets of all registers are calculated wrt 'UserArea' structure.
+ // However, WriteFPR() takes m_fpr (of type FPR structure) and writes
+ // only fpu registers using ptrace(PT_SETFPREGS,..) API. Hence fpu
+ // registers should be written in m_fpr at byte offsets calculated wrt
+ // FPR structure.
+
+ // Since, FPR structure is also one of the member of UserArea structure.
+ // byte_offset(fpu wrt FPR) = byte_offset(fpu wrt UserArea) -
+ // byte_offset(fctrl wrt UserArea)
+ assert((reg_info->byte_offset - m_fctrl_offset_in_userarea) <
+ sizeof(m_fpr));
+ uint8_t *dst = (uint8_t *)&m_fpr + reg_info->byte_offset -
+ m_fctrl_offset_in_userarea;
+ switch (reg_info->byte_size) {
+ case 1:
+ *(uint8_t *)dst = value.GetAsUInt8();
+ break;
+ case 2:
+ *(uint16_t *)dst = value.GetAsUInt16();
+ break;
+ case 4:
+ *(uint32_t *)dst = value.GetAsUInt32();
+ break;
+ case 8:
+ *(uint64_t *)dst = value.GetAsUInt64();
+ break;
+ default:
+ assert(false && "Unhandled data size.");
+ return false;
+ }
+ }
+
+ if (WriteFPR()) {
+ if (IsAVX(reg))
+ return CopyYMMtoXSTATE(reg, GetByteOrder());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::ReadAllRegisterValues(
+ DataBufferSP &data_sp) {
+ bool success = false;
+ data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0));
+ if (ReadGPR() && ReadFPR()) {
+ uint8_t *dst = data_sp->GetBytes();
+ success = dst != 0;
+
+ if (success) {
+ ::memcpy(dst, &m_gpr_x86_64, GetGPRSize());
+ dst += GetGPRSize();
+ if (GetFPRType() == eFXSAVE)
+ ::memcpy(dst, &m_fpr.fxsave, sizeof(m_fpr.fxsave));
+ }
+
+ if (GetFPRType() == eXSAVE) {
+ ByteOrder byte_order = GetByteOrder();
+
+ // Assemble the YMM register content from the register halves.
+ for (uint32_t reg = m_reg_info.first_ymm;
+ success && reg <= m_reg_info.last_ymm; ++reg)
+ success = CopyXSTATEtoYMM(reg, byte_order);
+
+ if (success) {
+ // Copy the extended register state including the assembled ymm
+ // registers.
+ ::memcpy(dst, &m_fpr, sizeof(m_fpr));
+ }
+ }
+ }
+ return success;
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::WriteAllRegisterValues(
+ const DataBufferSP &data_sp) {
+ bool success = false;
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) {
+ uint8_t *src = data_sp->GetBytes();
+ if (src) {
+ ::memcpy(&m_gpr_x86_64, src, GetGPRSize());
+
+ if (WriteGPR()) {
+ src += GetGPRSize();
+ if (GetFPRType() == eFXSAVE)
+ ::memcpy(&m_fpr.fxsave, src, sizeof(m_fpr.fxsave));
+ if (GetFPRType() == eXSAVE)
+ ::memcpy(&m_fpr.xsave, src, sizeof(m_fpr.xsave));
+
+ success = WriteFPR();
+ if (success) {
+ if (GetFPRType() == eXSAVE) {
+ ByteOrder byte_order = GetByteOrder();
+
+ // Parse the YMM register content from the register halves.
+ for (uint32_t reg = m_reg_info.first_ymm;
+ success && reg <= m_reg_info.last_ymm; ++reg)
+ success = CopyYMMtoXSTATE(reg, byte_order);
+ }
+ }
+ }
+ }
+ }
+ return success;
+}
+
+uint32_t RegisterContextPOSIXProcessMonitor_x86_64::SetHardwareWatchpoint(
+ addr_t addr, size_t size, bool read, bool write) {
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+ uint32_t hw_index;
+
+ for (hw_index = 0; hw_index < num_hw_watchpoints; ++hw_index) {
+ if (IsWatchpointVacant(hw_index))
+ return SetHardwareWatchpointWithIndex(addr, size, read, write, hw_index);
+ }
+
+ return LLDB_INVALID_INDEX32;
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::ClearHardwareWatchpoint(
+ uint32_t hw_index) {
+ if (hw_index < NumSupportedHardwareWatchpoints()) {
+ RegisterValue current_dr7_bits;
+
+ if (ReadRegister(m_reg_info.first_dr + 7, current_dr7_bits)) {
+ uint64_t new_dr7_bits =
+ current_dr7_bits.GetAsUInt64() & ~(3 << (2 * hw_index));
+
+ if (WriteRegister(m_reg_info.first_dr + 7, RegisterValue(new_dr7_bits)))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::HardwareSingleStep(
+ bool enable) {
+ enum { TRACE_BIT = 0x100 };
+ uint64_t rflags;
+
+ if ((rflags = ReadRegisterAsUnsigned(m_reg_info.gpr_flags, -1UL)) == -1UL)
+ return false;
+
+ if (enable) {
+ if (rflags & TRACE_BIT)
+ return true;
+
+ rflags |= TRACE_BIT;
+ } else {
+ if (!(rflags & TRACE_BIT))
+ return false;
+
+ rflags &= ~TRACE_BIT;
+ }
+
+ return WriteRegisterFromUnsigned(m_reg_info.gpr_flags, rflags);
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::UpdateAfterBreakpoint() {
+ // PC points one byte past the int3 responsible for the breakpoint.
+ lldb::addr_t pc;
+
+ if ((pc = GetPC()) == LLDB_INVALID_ADDRESS)
+ return false;
+
+ SetPC(pc - 1);
+ return true;
+}
+
+unsigned RegisterContextPOSIXProcessMonitor_x86_64::GetRegisterIndexFromOffset(
+ unsigned offset) {
+ unsigned reg;
+ for (reg = 0; reg < m_reg_info.num_registers; reg++) {
+ if (GetRegisterInfo()[reg].byte_offset == offset)
+ break;
+ }
+ assert(reg < m_reg_info.num_registers && "Invalid register offset.");
+ return reg;
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::IsWatchpointHit(
+ uint32_t hw_index) {
+ bool is_hit = false;
+
+ if (m_watchpoints_initialized == false) {
+ // Reset the debug status and debug control registers
+ RegisterValue zero_bits = RegisterValue(uint64_t(0));
+ if (!WriteRegister(m_reg_info.first_dr + 6, zero_bits) ||
+ !WriteRegister(m_reg_info.first_dr + 7, zero_bits))
+ assert(false && "Could not initialize watchpoint registers");
+ m_watchpoints_initialized = true;
+ }
+
+ if (hw_index < NumSupportedHardwareWatchpoints()) {
+ RegisterValue value;
+
+ if (ReadRegister(m_reg_info.first_dr + 6, value)) {
+ uint64_t val = value.GetAsUInt64();
+ is_hit = val & (1 << hw_index);
+ }
+ }
+
+ return is_hit;
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::ClearWatchpointHits() {
+ return WriteRegister(m_reg_info.first_dr + 6, RegisterValue((uint64_t)0));
+}
+
+addr_t RegisterContextPOSIXProcessMonitor_x86_64::GetWatchpointAddress(
+ uint32_t hw_index) {
+ addr_t wp_monitor_addr = LLDB_INVALID_ADDRESS;
+
+ if (hw_index < NumSupportedHardwareWatchpoints()) {
+ if (!IsWatchpointVacant(hw_index)) {
+ RegisterValue value;
+
+ if (ReadRegister(m_reg_info.first_dr + hw_index, value))
+ wp_monitor_addr = value.GetAsUInt64();
+ }
+ }
+
+ return wp_monitor_addr;
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::IsWatchpointVacant(
+ uint32_t hw_index) {
+ bool is_vacant = false;
+ RegisterValue value;
+
+ assert(hw_index < NumSupportedHardwareWatchpoints());
+
+ if (m_watchpoints_initialized == false) {
+ // Reset the debug status and debug control registers
+ RegisterValue zero_bits = RegisterValue(uint64_t(0));
+ if (!WriteRegister(m_reg_info.first_dr + 6, zero_bits) ||
+ !WriteRegister(m_reg_info.first_dr + 7, zero_bits))
+ assert(false && "Could not initialize watchpoint registers");
+ m_watchpoints_initialized = true;
+ }
+
+ if (ReadRegister(m_reg_info.first_dr + 7, value)) {
+ uint64_t val = value.GetAsUInt64();
+ is_vacant = (val & (3 << 2 * hw_index)) == 0;
+ }
+
+ return is_vacant;
+}
+
+bool RegisterContextPOSIXProcessMonitor_x86_64::SetHardwareWatchpointWithIndex(
+ addr_t addr, size_t size, bool read, bool write, uint32_t hw_index) {
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+
+ if (num_hw_watchpoints == 0 || hw_index >= num_hw_watchpoints)
+ return false;
+
+ if (!(size == 1 || size == 2 || size == 4 || size == 8))
+ return false;
+
+ if (read == false && write == false)
+ return false;
+
+ if (!IsWatchpointVacant(hw_index))
+ return false;
+
+ // Set both dr7 (debug control register) and dri (debug address register).
+
+ // dr7{7-0} encodes the 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}
+ //
+ // 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}
+ if (hw_index < num_hw_watchpoints) {
+ RegisterValue current_dr7_bits;
+
+ if (ReadRegister(m_reg_info.first_dr + 7, current_dr7_bits)) {
+ uint64_t new_dr7_bits =
+ current_dr7_bits.GetAsUInt64() |
+ (1 << (2 * hw_index) |
+ size_and_rw_bits(size, read, write) << (16 + 4 * hw_index));
+
+ if (WriteRegister(m_reg_info.first_dr + hw_index, RegisterValue(addr)) &&
+ WriteRegister(m_reg_info.first_dr + 7, RegisterValue(new_dr7_bits)))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+uint32_t
+RegisterContextPOSIXProcessMonitor_x86_64::NumSupportedHardwareWatchpoints() {
+ // Available debug address registers: dr0, dr1, dr2, dr3
+ return 4;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_x86.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_x86.h
new file mode 100644
index 000000000000..c9dc02a88e01
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_x86.h
@@ -0,0 +1,81 @@
+//===-- RegisterContextPOSIXProcessMonitor_x86.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 liblldb_RegisterContextPOSIXProcessMonitor_x86_H_
+#define liblldb_RegisterContextPOSIXProcessMonitor_x86_H_
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_x86.h"
+#include "RegisterContextPOSIX.h"
+#include <sys/uio.h>
+
+class RegisterContextPOSIXProcessMonitor_x86_64
+ : public RegisterContextPOSIX_x86,
+ public POSIXBreakpointProtocol {
+public:
+ RegisterContextPOSIXProcessMonitor_x86_64(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info);
+
+protected:
+ bool ReadGPR();
+
+ bool ReadFPR();
+
+ bool WriteGPR();
+
+ bool WriteFPR();
+
+ // lldb_private::RegisterContext
+ bool ReadRegister(const unsigned reg, lldb_private::RegisterValue &value);
+
+ bool WriteRegister(const unsigned reg,
+ const lldb_private::RegisterValue &value);
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value);
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value);
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp);
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp);
+
+ uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read,
+ bool write);
+
+ bool ClearHardwareWatchpoint(uint32_t hw_index);
+
+ bool HardwareSingleStep(bool enable);
+
+ // POSIXBreakpointProtocol
+ bool UpdateAfterBreakpoint();
+
+ unsigned GetRegisterIndexFromOffset(unsigned offset);
+
+ bool IsWatchpointHit(uint32_t hw_index);
+
+ bool ClearWatchpointHits();
+
+ lldb::addr_t GetWatchpointAddress(uint32_t hw_index);
+
+ bool IsWatchpointVacant(uint32_t hw_index);
+
+ bool SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, bool read,
+ bool write, uint32_t hw_index);
+
+ uint32_t NumSupportedHardwareWatchpoints();
+
+private:
+ ProcessMonitor &GetMonitor();
+ uint32_t
+ m_fctrl_offset_in_userarea; // Offset of 'fctrl' in 'UserArea' Structure
+ struct iovec m_iovec;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp
new file mode 100644
index 000000000000..32d20d2b1215
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp
@@ -0,0 +1,798 @@
+//===-- NativeProcessNetBSD.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 "NativeProcessNetBSD.h"
+
+
+
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
+#include "lldb/Host/HostProcess.h"
+#include "lldb/Host/common/NativeRegisterContext.h"
+#include "lldb/Host/posix/ProcessLauncherPosixFork.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/State.h"
+#include "llvm/Support/Errno.h"
+
+// System includes - They have to be included after framework includes because
+// they define some macros which collide with variable names in other modules
+// clang-format off
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+#include <uvm/uvm_prot.h>
+#include <elf.h>
+#include <util.h>
+// clang-format on
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_netbsd;
+using namespace llvm;
+
+// Simple helper function to ensure flags are enabled on the given file
+// descriptor.
+static Status EnsureFDFlags(int fd, int flags) {
+ Status error;
+
+ int status = fcntl(fd, F_GETFL);
+ if (status == -1) {
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ if (fcntl(fd, F_SETFL, status | flags) == -1) {
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ return error;
+}
+
+// Public Static Methods
+
+llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+NativeProcessNetBSD::Factory::Launch(ProcessLaunchInfo &launch_info,
+ NativeDelegate &native_delegate,
+ MainLoop &mainloop) const {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+
+ Status status;
+ ::pid_t pid = ProcessLauncherPosixFork()
+ .LaunchProcess(launch_info, status)
+ .GetProcessId();
+ LLDB_LOG(log, "pid = {0:x}", pid);
+ if (status.Fail()) {
+ LLDB_LOG(log, "failed to launch process: {0}", status);
+ return status.ToError();
+ }
+
+ // Wait for the child process to trap on its call to execve.
+ int wstatus;
+ ::pid_t wpid = llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, &wstatus, 0);
+ assert(wpid == pid);
+ (void)wpid;
+ if (!WIFSTOPPED(wstatus)) {
+ LLDB_LOG(log, "Could not sync with inferior process: wstatus={1}",
+ WaitStatus::Decode(wstatus));
+ return llvm::make_error<StringError>("Could not sync with inferior process",
+ llvm::inconvertibleErrorCode());
+ }
+ LLDB_LOG(log, "inferior started, now in stopped state");
+
+ ProcessInstanceInfo Info;
+ if (!Host::GetProcessInfo(pid, Info)) {
+ return llvm::make_error<StringError>("Cannot get process architecture",
+ llvm::inconvertibleErrorCode());
+ }
+
+ // Set the architecture to the exe architecture.
+ LLDB_LOG(log, "pid = {0:x}, detected architecture {1}", pid,
+ Info.GetArchitecture().GetArchitectureName());
+
+ std::unique_ptr<NativeProcessNetBSD> process_up(new NativeProcessNetBSD(
+ pid, launch_info.GetPTY().ReleaseMasterFileDescriptor(), native_delegate,
+ Info.GetArchitecture(), mainloop));
+
+ status = process_up->ReinitializeThreads();
+ if (status.Fail())
+ return status.ToError();
+
+ for (const auto &thread : process_up->m_threads)
+ static_cast<NativeThreadNetBSD &>(*thread).SetStoppedBySignal(SIGSTOP);
+ process_up->SetState(StateType::eStateStopped, false);
+
+ return std::move(process_up);
+}
+
+llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+NativeProcessNetBSD::Factory::Attach(
+ lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate,
+ MainLoop &mainloop) const {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ LLDB_LOG(log, "pid = {0:x}", pid);
+
+ // Retrieve the architecture for the running process.
+ ProcessInstanceInfo Info;
+ if (!Host::GetProcessInfo(pid, Info)) {
+ return llvm::make_error<StringError>("Cannot get process architecture",
+ llvm::inconvertibleErrorCode());
+ }
+
+ std::unique_ptr<NativeProcessNetBSD> process_up(new NativeProcessNetBSD(
+ pid, -1, native_delegate, Info.GetArchitecture(), mainloop));
+
+ Status status = process_up->Attach();
+ if (!status.Success())
+ return status.ToError();
+
+ return std::move(process_up);
+}
+
+// Public Instance Methods
+
+NativeProcessNetBSD::NativeProcessNetBSD(::pid_t pid, int terminal_fd,
+ NativeDelegate &delegate,
+ const ArchSpec &arch,
+ MainLoop &mainloop)
+ : NativeProcessProtocol(pid, terminal_fd, delegate), m_arch(arch) {
+ if (m_terminal_fd != -1) {
+ Status status = EnsureFDFlags(m_terminal_fd, O_NONBLOCK);
+ assert(status.Success());
+ }
+
+ Status status;
+ m_sigchld_handle = mainloop.RegisterSignal(
+ SIGCHLD, [this](MainLoopBase &) { SigchldHandler(); }, status);
+ assert(m_sigchld_handle && status.Success());
+}
+
+// Handles all waitpid events from the inferior process.
+void NativeProcessNetBSD::MonitorCallback(lldb::pid_t pid, int signal) {
+ switch (signal) {
+ case SIGTRAP:
+ return MonitorSIGTRAP(pid);
+ case SIGSTOP:
+ return MonitorSIGSTOP(pid);
+ default:
+ return MonitorSignal(pid, signal);
+ }
+}
+
+void NativeProcessNetBSD::MonitorExited(lldb::pid_t pid, WaitStatus status) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+
+ LLDB_LOG(log, "got exit signal({0}) , pid = {1}", status, pid);
+
+ /* Stop Tracking All Threads attached to Process */
+ m_threads.clear();
+
+ SetExitStatus(status, true);
+
+ // Notify delegate that our process has exited.
+ SetState(StateType::eStateExited, true);
+}
+
+void NativeProcessNetBSD::MonitorSIGSTOP(lldb::pid_t pid) {
+ ptrace_siginfo_t info;
+
+ const auto siginfo_err =
+ PtraceWrapper(PT_GET_SIGINFO, pid, &info, sizeof(info));
+
+ // Get details on the signal raised.
+ if (siginfo_err.Success()) {
+ // Handle SIGSTOP from LLGS (LLDB GDB Server)
+ if (info.psi_siginfo.si_code == SI_USER &&
+ info.psi_siginfo.si_pid == ::getpid()) {
+ /* Stop Tracking all Threads attached to Process */
+ for (const auto &thread : m_threads) {
+ static_cast<NativeThreadNetBSD &>(*thread).SetStoppedBySignal(
+ SIGSTOP, &info.psi_siginfo);
+ }
+ }
+ }
+}
+
+void NativeProcessNetBSD::MonitorSIGTRAP(lldb::pid_t pid) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ ptrace_siginfo_t info;
+
+ const auto siginfo_err =
+ PtraceWrapper(PT_GET_SIGINFO, pid, &info, sizeof(info));
+
+ // Get details on the signal raised.
+ if (siginfo_err.Fail()) {
+ return;
+ }
+
+ switch (info.psi_siginfo.si_code) {
+ case TRAP_BRKPT:
+ for (const auto &thread : m_threads) {
+ static_cast<NativeThreadNetBSD &>(*thread).SetStoppedByBreakpoint();
+ FixupBreakpointPCAsNeeded(static_cast<NativeThreadNetBSD &>(*thread));
+ }
+ SetState(StateType::eStateStopped, true);
+ break;
+ case TRAP_TRACE:
+ for (const auto &thread : m_threads)
+ static_cast<NativeThreadNetBSD &>(*thread).SetStoppedByTrace();
+ SetState(StateType::eStateStopped, true);
+ break;
+ case TRAP_EXEC: {
+ Status error = ReinitializeThreads();
+ if (error.Fail()) {
+ SetState(StateType::eStateInvalid);
+ return;
+ }
+
+ // Let our delegate know we have just exec'd.
+ NotifyDidExec();
+
+ for (const auto &thread : m_threads)
+ static_cast<NativeThreadNetBSD &>(*thread).SetStoppedByExec();
+ SetState(StateType::eStateStopped, true);
+ } break;
+ case TRAP_DBREG: {
+ // Find the thread.
+ NativeThreadNetBSD* thread = nullptr;
+ for (const auto &t : m_threads) {
+ if (t->GetID() == info.psi_lwpid) {
+ thread = static_cast<NativeThreadNetBSD *>(t.get());
+ break;
+ }
+ }
+ if (!thread) {
+ LLDB_LOG(log,
+ "thread not found in m_threads, pid = {0}, LWP = {1}",
+ GetID(), info.psi_lwpid);
+ break;
+ }
+
+ // If a watchpoint was hit, report it
+ uint32_t wp_index = LLDB_INVALID_INDEX32;
+ Status error = thread->GetRegisterContext().GetWatchpointHitIndex(
+ wp_index, (uintptr_t)info.psi_siginfo.si_addr);
+ if (error.Fail())
+ LLDB_LOG(log,
+ "received error while checking for watchpoint hits, pid = "
+ "{0}, LWP = {1}, error = {2}",
+ GetID(), info.psi_lwpid, error);
+ if (wp_index != LLDB_INVALID_INDEX32) {
+ for (const auto &thread : m_threads)
+ static_cast<NativeThreadNetBSD &>(*thread).SetStoppedByWatchpoint(
+ wp_index);
+ SetState(StateType::eStateStopped, true);
+ break;
+ }
+
+ // If a breakpoint was hit, report it
+ uint32_t bp_index = LLDB_INVALID_INDEX32;
+ error = thread->GetRegisterContext().GetHardwareBreakHitIndex(
+ bp_index, (uintptr_t)info.psi_siginfo.si_addr);
+ if (error.Fail())
+ LLDB_LOG(log,
+ "received error while checking for hardware "
+ "breakpoint hits, pid = {0}, LWP = {1}, error = {2}",
+ GetID(), info.psi_lwpid, error);
+ if (bp_index != LLDB_INVALID_INDEX32) {
+ for (const auto &thread : m_threads)
+ static_cast<NativeThreadNetBSD &>(*thread).SetStoppedByBreakpoint();
+ SetState(StateType::eStateStopped, true);
+ break;
+ }
+ } break;
+ }
+}
+
+void NativeProcessNetBSD::MonitorSignal(lldb::pid_t pid, int signal) {
+ ptrace_siginfo_t info;
+ const auto siginfo_err =
+ PtraceWrapper(PT_GET_SIGINFO, pid, &info, sizeof(info));
+
+ for (const auto &thread : m_threads) {
+ static_cast<NativeThreadNetBSD &>(*thread).SetStoppedBySignal(
+ info.psi_siginfo.si_signo, &info.psi_siginfo);
+ }
+ SetState(StateType::eStateStopped, true);
+}
+
+Status NativeProcessNetBSD::PtraceWrapper(int req, lldb::pid_t pid, void *addr,
+ int data, int *result) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+ Status error;
+ int ret;
+
+ errno = 0;
+ ret = ptrace(req, static_cast<::pid_t>(pid), addr, data);
+
+ if (ret == -1)
+ error.SetErrorToErrno();
+
+ if (result)
+ *result = ret;
+
+ LLDB_LOG(log, "ptrace({0}, {1}, {2}, {3})={4:x}", req, pid, addr, data, ret);
+
+ if (error.Fail())
+ LLDB_LOG(log, "ptrace() failed: {0}", error);
+
+ return error;
+}
+
+Status NativeProcessNetBSD::Resume(const ResumeActionList &resume_actions) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ LLDB_LOG(log, "pid {0}", GetID());
+
+ const auto &thread = m_threads[0];
+ const ResumeAction *const action =
+ resume_actions.GetActionForThread(thread->GetID(), true);
+
+ if (action == nullptr) {
+ LLDB_LOG(log, "no action specified for pid {0} tid {1}", GetID(),
+ thread->GetID());
+ return Status();
+ }
+
+ Status error;
+
+ switch (action->state) {
+ case eStateRunning: {
+ // Run the thread, possibly feeding it the signal.
+ error = NativeProcessNetBSD::PtraceWrapper(PT_CONTINUE, GetID(), (void *)1,
+ action->signal);
+ if (!error.Success())
+ return error;
+ for (const auto &thread : m_threads)
+ static_cast<NativeThreadNetBSD &>(*thread).SetRunning();
+ SetState(eStateRunning, true);
+ break;
+ }
+ case eStateStepping:
+ // Run the thread, possibly feeding it the signal.
+ error = NativeProcessNetBSD::PtraceWrapper(PT_STEP, GetID(), (void *)1,
+ action->signal);
+ if (!error.Success())
+ return error;
+ for (const auto &thread : m_threads)
+ static_cast<NativeThreadNetBSD &>(*thread).SetStepping();
+ SetState(eStateStepping, true);
+ break;
+
+ case eStateSuspended:
+ case eStateStopped:
+ llvm_unreachable("Unexpected state");
+
+ default:
+ return Status("NativeProcessNetBSD::%s (): unexpected state %s specified "
+ "for pid %" PRIu64 ", tid %" PRIu64,
+ __FUNCTION__, StateAsCString(action->state), GetID(),
+ thread->GetID());
+ }
+
+ return Status();
+}
+
+Status NativeProcessNetBSD::Halt() {
+ Status error;
+
+ if (kill(GetID(), SIGSTOP) != 0)
+ error.SetErrorToErrno();
+
+ return error;
+}
+
+Status NativeProcessNetBSD::Detach() {
+ Status error;
+
+ // Stop monitoring the inferior.
+ m_sigchld_handle.reset();
+
+ // Tell ptrace to detach from the process.
+ if (GetID() == LLDB_INVALID_PROCESS_ID)
+ return error;
+
+ return PtraceWrapper(PT_DETACH, GetID());
+}
+
+Status NativeProcessNetBSD::Signal(int signo) {
+ Status error;
+
+ if (kill(GetID(), signo))
+ error.SetErrorToErrno();
+
+ return error;
+}
+
+Status NativeProcessNetBSD::Kill() {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ LLDB_LOG(log, "pid {0}", GetID());
+
+ Status error;
+
+ switch (m_state) {
+ case StateType::eStateInvalid:
+ case StateType::eStateExited:
+ case StateType::eStateCrashed:
+ case StateType::eStateDetached:
+ case StateType::eStateUnloaded:
+ // Nothing to do - the process is already dead.
+ LLDB_LOG(log, "ignored for PID {0} due to current state: {1}", GetID(),
+ StateAsCString(m_state));
+ return error;
+
+ case StateType::eStateConnected:
+ case StateType::eStateAttaching:
+ case StateType::eStateLaunching:
+ case StateType::eStateStopped:
+ case StateType::eStateRunning:
+ case StateType::eStateStepping:
+ case StateType::eStateSuspended:
+ // We can try to kill a process in these states.
+ break;
+ }
+
+ if (kill(GetID(), SIGKILL) != 0) {
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ return error;
+}
+
+Status NativeProcessNetBSD::GetMemoryRegionInfo(lldb::addr_t load_addr,
+ MemoryRegionInfo &range_info) {
+
+ if (m_supports_mem_region == LazyBool::eLazyBoolNo) {
+ // We're done.
+ return Status("unsupported");
+ }
+
+ Status error = PopulateMemoryRegionCache();
+ if (error.Fail()) {
+ return error;
+ }
+
+ lldb::addr_t prev_base_address = 0;
+ // FIXME start by finding the last region that is <= target address using
+ // binary search. Data is sorted.
+ // There can be a ton of regions on pthreads apps with lots of threads.
+ for (auto it = m_mem_region_cache.begin(); it != m_mem_region_cache.end();
+ ++it) {
+ MemoryRegionInfo &proc_entry_info = it->first;
+ // Sanity check assumption that memory map entries are ascending.
+ assert((proc_entry_info.GetRange().GetRangeBase() >= prev_base_address) &&
+ "descending memory map entries detected, unexpected");
+ prev_base_address = proc_entry_info.GetRange().GetRangeBase();
+ UNUSED_IF_ASSERT_DISABLED(prev_base_address);
+ // If the target address comes before this entry, indicate distance to next
+ // region.
+ if (load_addr < proc_entry_info.GetRange().GetRangeBase()) {
+ range_info.GetRange().SetRangeBase(load_addr);
+ range_info.GetRange().SetByteSize(
+ proc_entry_info.GetRange().GetRangeBase() - load_addr);
+ range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo);
+ range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo);
+ range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo);
+ range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo);
+ return error;
+ } else if (proc_entry_info.GetRange().Contains(load_addr)) {
+ // The target address is within the memory region we're processing here.
+ range_info = proc_entry_info;
+ return error;
+ }
+ // The target memory address comes somewhere after the region we just
+ // parsed.
+ }
+ // If we made it here, we didn't find an entry that contained the given
+ // address. Return the load_addr as start and the amount of bytes betwwen
+ // load address and the end of the memory as size.
+ range_info.GetRange().SetRangeBase(load_addr);
+ range_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS);
+ range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo);
+ range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo);
+ range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo);
+ range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo);
+ return error;
+}
+
+Status NativeProcessNetBSD::PopulateMemoryRegionCache() {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ // If our cache is empty, pull the latest. There should always be at least
+ // one memory region if memory region handling is supported.
+ if (!m_mem_region_cache.empty()) {
+ LLDB_LOG(log, "reusing {0} cached memory region entries",
+ m_mem_region_cache.size());
+ return Status();
+ }
+
+ struct kinfo_vmentry *vm;
+ size_t count, i;
+ vm = kinfo_getvmmap(GetID(), &count);
+ if (vm == NULL) {
+ m_supports_mem_region = LazyBool::eLazyBoolNo;
+ Status error;
+ error.SetErrorString("not supported");
+ return error;
+ }
+ for (i = 0; i < count; i++) {
+ MemoryRegionInfo info;
+ info.Clear();
+ info.GetRange().SetRangeBase(vm[i].kve_start);
+ info.GetRange().SetRangeEnd(vm[i].kve_end);
+ info.SetMapped(MemoryRegionInfo::OptionalBool::eYes);
+
+ if (vm[i].kve_protection & VM_PROT_READ)
+ info.SetReadable(MemoryRegionInfo::OptionalBool::eYes);
+ else
+ info.SetReadable(MemoryRegionInfo::OptionalBool::eNo);
+
+ if (vm[i].kve_protection & VM_PROT_WRITE)
+ info.SetWritable(MemoryRegionInfo::OptionalBool::eYes);
+ else
+ info.SetWritable(MemoryRegionInfo::OptionalBool::eNo);
+
+ if (vm[i].kve_protection & VM_PROT_EXECUTE)
+ info.SetExecutable(MemoryRegionInfo::OptionalBool::eYes);
+ else
+ info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo);
+
+ if (vm[i].kve_path[0])
+ info.SetName(vm[i].kve_path);
+
+ m_mem_region_cache.emplace_back(
+ info, FileSpec(info.GetName().GetCString()));
+ }
+ free(vm);
+
+ if (m_mem_region_cache.empty()) {
+ // No entries after attempting to read them. This shouldn't happen. Assume
+ // we don't support map entries.
+ LLDB_LOG(log, "failed to find any vmmap entries, assuming no support "
+ "for memory region metadata retrieval");
+ m_supports_mem_region = LazyBool::eLazyBoolNo;
+ Status error;
+ error.SetErrorString("not supported");
+ return error;
+ }
+ LLDB_LOG(log, "read {0} memory region entries from process {1}",
+ m_mem_region_cache.size(), GetID());
+ // We support memory retrieval, remember that.
+ m_supports_mem_region = LazyBool::eLazyBoolYes;
+ return Status();
+}
+
+Status NativeProcessNetBSD::AllocateMemory(size_t size, uint32_t permissions,
+ lldb::addr_t &addr) {
+ return Status("Unimplemented");
+}
+
+Status NativeProcessNetBSD::DeallocateMemory(lldb::addr_t addr) {
+ return Status("Unimplemented");
+}
+
+lldb::addr_t NativeProcessNetBSD::GetSharedLibraryInfoAddress() {
+ // punt on this for now
+ return LLDB_INVALID_ADDRESS;
+}
+
+size_t NativeProcessNetBSD::UpdateThreads() { return m_threads.size(); }
+
+Status NativeProcessNetBSD::SetBreakpoint(lldb::addr_t addr, uint32_t size,
+ bool hardware) {
+ if (hardware)
+ return Status("NativeProcessNetBSD does not support hardware breakpoints");
+ else
+ return SetSoftwareBreakpoint(addr, size);
+}
+
+Status NativeProcessNetBSD::GetLoadedModuleFileSpec(const char *module_path,
+ FileSpec &file_spec) {
+ return Status("Unimplemented");
+}
+
+Status NativeProcessNetBSD::GetFileLoadAddress(const llvm::StringRef &file_name,
+ lldb::addr_t &load_addr) {
+ load_addr = LLDB_INVALID_ADDRESS;
+ return Status();
+}
+
+void NativeProcessNetBSD::SigchldHandler() {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ // Process all pending waitpid notifications.
+ int status;
+ ::pid_t wait_pid =
+ llvm::sys::RetryAfterSignal(-1, waitpid, GetID(), &status, WALLSIG | WNOHANG);
+
+ if (wait_pid == 0)
+ return; // We are done.
+
+ if (wait_pid == -1) {
+ Status error(errno, eErrorTypePOSIX);
+ LLDB_LOG(log, "waitpid ({0}, &status, _) failed: {1}", GetID(), error);
+ }
+
+ WaitStatus wait_status = WaitStatus::Decode(status);
+ bool exited = wait_status.type == WaitStatus::Exit ||
+ (wait_status.type == WaitStatus::Signal &&
+ wait_pid == static_cast<::pid_t>(GetID()));
+
+ LLDB_LOG(log,
+ "waitpid ({0}, &status, _) => pid = {1}, status = {2}, exited = {3}",
+ GetID(), wait_pid, status, exited);
+
+ if (exited)
+ MonitorExited(wait_pid, wait_status);
+ else {
+ assert(wait_status.type == WaitStatus::Stop);
+ MonitorCallback(wait_pid, wait_status.status);
+ }
+}
+
+bool NativeProcessNetBSD::HasThreadNoLock(lldb::tid_t thread_id) {
+ for (const auto &thread : m_threads) {
+ assert(thread && "thread list should not contain NULL threads");
+ if (thread->GetID() == thread_id) {
+ // We have this thread.
+ return true;
+ }
+ }
+
+ // We don't have this thread.
+ return false;
+}
+
+NativeThreadNetBSD &NativeProcessNetBSD::AddThread(lldb::tid_t thread_id) {
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+ LLDB_LOG(log, "pid {0} adding thread with tid {1}", GetID(), thread_id);
+
+ assert(!HasThreadNoLock(thread_id) &&
+ "attempted to add a thread by id that already exists");
+
+ // If this is the first thread, save it as the current thread
+ if (m_threads.empty())
+ SetCurrentThreadID(thread_id);
+
+ m_threads.push_back(llvm::make_unique<NativeThreadNetBSD>(*this, thread_id));
+ return static_cast<NativeThreadNetBSD &>(*m_threads.back());
+}
+
+Status NativeProcessNetBSD::Attach() {
+ // Attach to the requested process.
+ // An attach will cause the thread to stop with a SIGSTOP.
+ Status status = PtraceWrapper(PT_ATTACH, m_pid);
+ if (status.Fail())
+ return status;
+
+ int wstatus;
+ // Need to use WALLSIG otherwise we receive an error with errno=ECHLD At this
+ // point we should have a thread stopped if waitpid succeeds.
+ if ((wstatus = llvm::sys::RetryAfterSignal(-1, waitpid,
+ m_pid, nullptr, WALLSIG)) < 0)
+ return Status(errno, eErrorTypePOSIX);
+
+ /* Initialize threads */
+ status = ReinitializeThreads();
+ if (status.Fail())
+ return status;
+
+ for (const auto &thread : m_threads)
+ static_cast<NativeThreadNetBSD &>(*thread).SetStoppedBySignal(SIGSTOP);
+
+ // Let our process instance know the thread has stopped.
+ SetState(StateType::eStateStopped);
+ return Status();
+}
+
+Status NativeProcessNetBSD::ReadMemory(lldb::addr_t addr, void *buf,
+ size_t size, size_t &bytes_read) {
+ unsigned char *dst = static_cast<unsigned char *>(buf);
+ struct ptrace_io_desc io;
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_MEMORY));
+ LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size);
+
+ bytes_read = 0;
+ io.piod_op = PIOD_READ_D;
+ io.piod_len = size;
+
+ do {
+ io.piod_offs = (void *)(addr + bytes_read);
+ io.piod_addr = dst + bytes_read;
+
+ Status error = NativeProcessNetBSD::PtraceWrapper(PT_IO, GetID(), &io);
+ if (error.Fail() || io.piod_len == 0)
+ return error;
+
+ bytes_read += io.piod_len;
+ io.piod_len = size - bytes_read;
+ } while (bytes_read < size);
+
+ return Status();
+}
+
+Status NativeProcessNetBSD::WriteMemory(lldb::addr_t addr, const void *buf,
+ size_t size, size_t &bytes_written) {
+ const unsigned char *src = static_cast<const unsigned char *>(buf);
+ Status error;
+ struct ptrace_io_desc io;
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_MEMORY));
+ LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size);
+
+ bytes_written = 0;
+ io.piod_op = PIOD_WRITE_D;
+ io.piod_len = size;
+
+ do {
+ io.piod_addr = const_cast<void *>(static_cast<const void *>(src + bytes_written));
+ io.piod_offs = (void *)(addr + bytes_written);
+
+ Status error = NativeProcessNetBSD::PtraceWrapper(PT_IO, GetID(), &io);
+ if (error.Fail() || io.piod_len == 0)
+ return error;
+
+ bytes_written += io.piod_len;
+ io.piod_len = size - bytes_written;
+ } while (bytes_written < size);
+
+ return error;
+}
+
+llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+NativeProcessNetBSD::GetAuxvData() const {
+ /*
+ * ELF_AUX_ENTRIES is currently restricted to kernel
+ * (<sys/exec_elf.h> r. 1.155 specifies 15)
+ *
+ * ptrace(2) returns the whole AUXV including extra fiels after AT_NULL this
+ * information isn't needed.
+ */
+ size_t auxv_size = 100 * sizeof(AuxInfo);
+
+ ErrorOr<std::unique_ptr<WritableMemoryBuffer>> buf =
+ llvm::WritableMemoryBuffer::getNewMemBuffer(auxv_size);
+
+ struct ptrace_io_desc io;
+ io.piod_op = PIOD_READ_AUXV;
+ io.piod_offs = 0;
+ io.piod_addr = static_cast<void *>(buf.get()->getBufferStart());
+ io.piod_len = auxv_size;
+
+ Status error = NativeProcessNetBSD::PtraceWrapper(PT_IO, GetID(), &io);
+
+ if (error.Fail())
+ return std::error_code(error.GetError(), std::generic_category());
+
+ if (io.piod_len < 1)
+ return std::error_code(ECANCELED, std::generic_category());
+
+ return std::move(buf);
+}
+
+Status NativeProcessNetBSD::ReinitializeThreads() {
+ // Clear old threads
+ m_threads.clear();
+
+ // Initialize new thread
+ struct ptrace_lwpinfo info = {};
+ Status error = PtraceWrapper(PT_LWPINFO, GetID(), &info, sizeof(info));
+ if (error.Fail()) {
+ return error;
+ }
+ // Reinitialize from scratch threads and register them in process
+ while (info.pl_lwpid != 0) {
+ AddThread(info.pl_lwpid);
+ error = PtraceWrapper(PT_LWPINFO, GetID(), &info, sizeof(info));
+ if (error.Fail()) {
+ return error;
+ }
+ }
+
+ return error;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h
new file mode 100644
index 000000000000..e85ca3b7a925
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h
@@ -0,0 +1,118 @@
+//===-- NativeProcessNetBSD.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 liblldb_NativeProcessNetBSD_H_
+#define liblldb_NativeProcessNetBSD_H_
+
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/FileSpec.h"
+
+#include "NativeThreadNetBSD.h"
+#include "lldb/Host/common/NativeProcessProtocol.h"
+
+namespace lldb_private {
+namespace process_netbsd {
+/// \class NativeProcessNetBSD
+/// Manages communication with the inferior (debugee) process.
+///
+/// Upon construction, this class prepares and launches an inferior process
+/// for debugging.
+///
+/// Changes in the inferior process state are broadcasted.
+class NativeProcessNetBSD : public NativeProcessProtocol {
+public:
+ class Factory : public NativeProcessProtocol::Factory {
+ public:
+ llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+ Launch(ProcessLaunchInfo &launch_info, NativeDelegate &native_delegate,
+ MainLoop &mainloop) const override;
+
+ llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+ Attach(lldb::pid_t pid, NativeDelegate &native_delegate,
+ MainLoop &mainloop) const override;
+ };
+
+ // NativeProcessProtocol Interface
+ Status Resume(const ResumeActionList &resume_actions) override;
+
+ Status Halt() override;
+
+ Status Detach() override;
+
+ Status Signal(int signo) override;
+
+ Status Kill() override;
+
+ Status GetMemoryRegionInfo(lldb::addr_t load_addr,
+ MemoryRegionInfo &range_info) override;
+
+ Status ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ size_t &bytes_read) override;
+
+ Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size,
+ size_t &bytes_written) override;
+
+ Status AllocateMemory(size_t size, uint32_t permissions,
+ lldb::addr_t &addr) override;
+
+ Status DeallocateMemory(lldb::addr_t addr) override;
+
+ lldb::addr_t GetSharedLibraryInfoAddress() override;
+
+ size_t UpdateThreads() override;
+
+ const ArchSpec &GetArchitecture() const override { return m_arch; }
+
+ Status SetBreakpoint(lldb::addr_t addr, uint32_t size,
+ bool hardware) override;
+
+ Status GetLoadedModuleFileSpec(const char *module_path,
+ FileSpec &file_spec) override;
+
+ Status GetFileLoadAddress(const llvm::StringRef &file_name,
+ lldb::addr_t &load_addr) override;
+
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+ GetAuxvData() const override;
+
+ // Interface used by NativeRegisterContext-derived classes.
+ static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr = nullptr,
+ int data = 0, int *result = nullptr);
+
+private:
+ MainLoop::SignalHandleUP m_sigchld_handle;
+ ArchSpec m_arch;
+ LazyBool m_supports_mem_region = eLazyBoolCalculate;
+ std::vector<std::pair<MemoryRegionInfo, FileSpec>> m_mem_region_cache;
+
+ // Private Instance Methods
+ NativeProcessNetBSD(::pid_t pid, int terminal_fd, NativeDelegate &delegate,
+ const ArchSpec &arch, MainLoop &mainloop);
+
+ bool HasThreadNoLock(lldb::tid_t thread_id);
+
+ NativeThreadNetBSD &AddThread(lldb::tid_t thread_id);
+
+ void MonitorCallback(lldb::pid_t pid, int signal);
+ void MonitorExited(lldb::pid_t pid, WaitStatus status);
+ void MonitorSIGSTOP(lldb::pid_t pid);
+ void MonitorSIGTRAP(lldb::pid_t pid);
+ void MonitorSignal(lldb::pid_t pid, int signal);
+
+ Status PopulateMemoryRegionCache();
+ void SigchldHandler();
+
+ Status Attach();
+ Status ReinitializeThreads();
+};
+
+} // namespace process_netbsd
+} // namespace lldb_private
+
+#endif // #ifndef liblldb_NativeProcessNetBSD_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.cpp
new file mode 100644
index 000000000000..3a9caaad74c8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.cpp
@@ -0,0 +1,38 @@
+//===-- NativeRegisterContextNetBSD.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 "NativeRegisterContextNetBSD.h"
+
+#include "lldb/Host/common/NativeProcessProtocol.h"
+
+using namespace lldb_private;
+using namespace lldb_private::process_netbsd;
+
+// clang-format off
+#include <sys/types.h>
+#include <sys/ptrace.h>
+// clang-format on
+
+NativeRegisterContextNetBSD::NativeRegisterContextNetBSD(
+ NativeThreadProtocol &native_thread,
+ RegisterInfoInterface *reg_info_interface_p)
+ : NativeRegisterContextRegisterInfo(native_thread,
+ reg_info_interface_p) {}
+
+Status NativeRegisterContextNetBSD::DoRegisterSet(int ptrace_req, void *buf) {
+ return NativeProcessNetBSD::PtraceWrapper(ptrace_req, GetProcessPid(), buf,
+ m_thread.GetID());
+}
+
+NativeProcessNetBSD &NativeRegisterContextNetBSD::GetProcess() {
+ return static_cast<NativeProcessNetBSD &>(m_thread.GetProcess());
+}
+
+::pid_t NativeRegisterContextNetBSD::GetProcessPid() {
+ return GetProcess().GetID();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h
new file mode 100644
index 000000000000..f5dd0c33b677
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h
@@ -0,0 +1,43 @@
+//===-- NativeRegisterContextNetBSD.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_NativeRegisterContextNetBSD_h
+#define lldb_NativeRegisterContextNetBSD_h
+
+#include "lldb/Host/common/NativeThreadProtocol.h"
+
+#include "Plugins/Process/NetBSD/NativeProcessNetBSD.h"
+#include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h"
+
+namespace lldb_private {
+namespace process_netbsd {
+
+class NativeRegisterContextNetBSD : public NativeRegisterContextRegisterInfo {
+public:
+ NativeRegisterContextNetBSD(NativeThreadProtocol &native_thread,
+ RegisterInfoInterface *reg_info_interface_p);
+
+ // This function is implemented in the NativeRegisterContextNetBSD_*
+ // subclasses to create a new instance of the host specific
+ // NativeRegisterContextNetBSD. The implementations can't collide as only one
+ // NativeRegisterContextNetBSD_* variant should be compiled into the final
+ // executable.
+ static NativeRegisterContextNetBSD *
+ CreateHostNativeRegisterContextNetBSD(const ArchSpec &target_arch,
+ NativeThreadProtocol &native_thread);
+
+protected:
+ Status DoRegisterSet(int req, void *buf);
+ virtual NativeProcessNetBSD &GetProcess();
+ virtual ::pid_t GetProcessPid();
+};
+
+} // namespace process_netbsd
+} // namespace lldb_private
+
+#endif // #ifndef lldb_NativeRegisterContextNetBSD_h
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.cpp
new file mode 100644
index 000000000000..a7cd637bf826
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.cpp
@@ -0,0 +1,933 @@
+//===-- NativeRegisterContextNetBSD_x86_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
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__x86_64__)
+
+#include "NativeRegisterContextNetBSD_x86_64.h"
+
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+#include "Plugins/Process/Utility/RegisterContextNetBSD_x86_64.h"
+
+// clang-format off
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+#include <x86/cpu.h>
+#include <x86/cpu_extended_state.h>
+#include <x86/specialreg.h>
+#include <elf.h>
+#include <err.h>
+#include <stdint.h>
+#include <stdlib.h>
+// clang-format on
+
+using namespace lldb_private;
+using namespace lldb_private::process_netbsd;
+
+// Private namespace.
+
+namespace {
+// x86 64-bit general purpose registers.
+static const uint32_t g_gpr_regnums_x86_64[] = {
+ lldb_rax_x86_64, lldb_rbx_x86_64, lldb_rcx_x86_64, lldb_rdx_x86_64,
+ lldb_rdi_x86_64, lldb_rsi_x86_64, lldb_rbp_x86_64, lldb_rsp_x86_64,
+ lldb_r8_x86_64, lldb_r9_x86_64, lldb_r10_x86_64, lldb_r11_x86_64,
+ lldb_r12_x86_64, lldb_r13_x86_64, lldb_r14_x86_64, lldb_r15_x86_64,
+ lldb_rip_x86_64, lldb_rflags_x86_64, lldb_cs_x86_64, lldb_fs_x86_64,
+ lldb_gs_x86_64, lldb_ss_x86_64, lldb_ds_x86_64, lldb_es_x86_64,
+ lldb_eax_x86_64, lldb_ebx_x86_64, lldb_ecx_x86_64, lldb_edx_x86_64,
+ lldb_edi_x86_64, lldb_esi_x86_64, lldb_ebp_x86_64, lldb_esp_x86_64,
+ lldb_r8d_x86_64, // Low 32 bits or r8
+ lldb_r9d_x86_64, // Low 32 bits or r9
+ lldb_r10d_x86_64, // Low 32 bits or r10
+ lldb_r11d_x86_64, // Low 32 bits or r11
+ lldb_r12d_x86_64, // Low 32 bits or r12
+ lldb_r13d_x86_64, // Low 32 bits or r13
+ lldb_r14d_x86_64, // Low 32 bits or r14
+ lldb_r15d_x86_64, // Low 32 bits or r15
+ lldb_ax_x86_64, lldb_bx_x86_64, lldb_cx_x86_64, lldb_dx_x86_64,
+ lldb_di_x86_64, lldb_si_x86_64, lldb_bp_x86_64, lldb_sp_x86_64,
+ lldb_r8w_x86_64, // Low 16 bits or r8
+ lldb_r9w_x86_64, // Low 16 bits or r9
+ lldb_r10w_x86_64, // Low 16 bits or r10
+ lldb_r11w_x86_64, // Low 16 bits or r11
+ lldb_r12w_x86_64, // Low 16 bits or r12
+ lldb_r13w_x86_64, // Low 16 bits or r13
+ lldb_r14w_x86_64, // Low 16 bits or r14
+ lldb_r15w_x86_64, // Low 16 bits or r15
+ lldb_ah_x86_64, lldb_bh_x86_64, lldb_ch_x86_64, lldb_dh_x86_64,
+ lldb_al_x86_64, lldb_bl_x86_64, lldb_cl_x86_64, lldb_dl_x86_64,
+ lldb_dil_x86_64, lldb_sil_x86_64, lldb_bpl_x86_64, lldb_spl_x86_64,
+ lldb_r8l_x86_64, // Low 8 bits or r8
+ lldb_r9l_x86_64, // Low 8 bits or r9
+ lldb_r10l_x86_64, // Low 8 bits or r10
+ lldb_r11l_x86_64, // Low 8 bits or r11
+ lldb_r12l_x86_64, // Low 8 bits or r12
+ lldb_r13l_x86_64, // Low 8 bits or r13
+ lldb_r14l_x86_64, // Low 8 bits or r14
+ lldb_r15l_x86_64, // Low 8 bits or r15
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+static_assert((sizeof(g_gpr_regnums_x86_64) / sizeof(g_gpr_regnums_x86_64[0])) -
+ 1 ==
+ k_num_gpr_registers_x86_64,
+ "g_gpr_regnums_x86_64 has wrong number of register infos");
+
+// Number of register sets provided by this context.
+enum { k_num_extended_register_sets = 2, k_num_register_sets = 4 };
+
+// Register sets for x86 64-bit.
+static const RegisterSet g_reg_sets_x86_64[k_num_register_sets] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_registers_x86_64,
+ g_gpr_regnums_x86_64},
+};
+
+#define REG_CONTEXT_SIZE (GetRegisterInfoInterface().GetGPRSize())
+} // namespace
+
+NativeRegisterContextNetBSD *
+NativeRegisterContextNetBSD::CreateHostNativeRegisterContextNetBSD(
+ const ArchSpec &target_arch, NativeThreadProtocol &native_thread) {
+ return new NativeRegisterContextNetBSD_x86_64(target_arch, native_thread);
+}
+
+// NativeRegisterContextNetBSD_x86_64 members.
+
+static RegisterInfoInterface *
+CreateRegisterInfoInterface(const ArchSpec &target_arch) {
+ assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) &&
+ "Register setting path assumes this is a 64-bit host");
+ // X86_64 hosts know how to work with 64-bit and 32-bit EXEs using the x86_64
+ // register context.
+ return new RegisterContextNetBSD_x86_64(target_arch);
+}
+
+NativeRegisterContextNetBSD_x86_64::NativeRegisterContextNetBSD_x86_64(
+ const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
+ : NativeRegisterContextNetBSD(native_thread,
+ CreateRegisterInfoInterface(target_arch)),
+ m_gpr_x86_64(), m_fpr_x86_64(), m_dbr_x86_64() {}
+
+// CONSIDER after local and llgs debugging are merged, register set support can
+// be moved into a base x86-64 class with IsRegisterSetAvailable made virtual.
+uint32_t NativeRegisterContextNetBSD_x86_64::GetRegisterSetCount() const {
+ uint32_t sets = 0;
+ for (uint32_t set_index = 0; set_index < k_num_register_sets; ++set_index) {
+ if (GetSetForNativeRegNum(set_index) != -1)
+ ++sets;
+ }
+
+ return sets;
+}
+
+const RegisterSet *
+NativeRegisterContextNetBSD_x86_64::GetRegisterSet(uint32_t set_index) const {
+ switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) {
+ case llvm::Triple::x86_64:
+ return &g_reg_sets_x86_64[set_index];
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+
+ return nullptr;
+}
+
+int NativeRegisterContextNetBSD_x86_64::GetSetForNativeRegNum(
+ int reg_num) const {
+ if (reg_num <= k_last_gpr_x86_64)
+ return GPRegSet;
+ else if (reg_num <= k_last_fpr_x86_64)
+ return FPRegSet;
+ else if (reg_num <= k_last_avx_x86_64)
+ return XStateRegSet; // AVX
+ else if (reg_num <= k_last_mpxr_x86_64)
+ return -1; // MPXR
+ else if (reg_num <= k_last_mpxc_x86_64)
+ return -1; // MPXC
+ else if (reg_num <= lldb_dr7_x86_64)
+ return DBRegSet; // DBR
+ else
+ return -1;
+}
+
+Status NativeRegisterContextNetBSD_x86_64::ReadRegisterSet(uint32_t set) {
+ switch (set) {
+ case GPRegSet:
+ return DoRegisterSet(PT_GETREGS, &m_gpr_x86_64);
+ case FPRegSet:
+ return DoRegisterSet(PT_GETFPREGS, &m_fpr_x86_64);
+ case DBRegSet:
+ return DoRegisterSet(PT_GETDBREGS, &m_dbr_x86_64);
+ case XStateRegSet:
+#ifdef HAVE_XSTATE
+ {
+ struct iovec iov = {&m_xstate_x86_64, sizeof(m_xstate_x86_64)};
+ return DoRegisterSet(PT_GETXSTATE, &iov);
+ }
+#else
+ return Status("XState is not supported by the kernel");
+#endif
+ }
+ llvm_unreachable("NativeRegisterContextNetBSD_x86_64::ReadRegisterSet");
+}
+
+Status NativeRegisterContextNetBSD_x86_64::WriteRegisterSet(uint32_t set) {
+ switch (set) {
+ case GPRegSet:
+ return DoRegisterSet(PT_SETREGS, &m_gpr_x86_64);
+ case FPRegSet:
+ return DoRegisterSet(PT_SETFPREGS, &m_fpr_x86_64);
+ case DBRegSet:
+ return DoRegisterSet(PT_SETDBREGS, &m_dbr_x86_64);
+ case XStateRegSet:
+#ifdef HAVE_XSTATE
+ {
+ struct iovec iov = {&m_xstate_x86_64, sizeof(m_xstate_x86_64)};
+ return DoRegisterSet(PT_SETXSTATE, &iov);
+ }
+#else
+ return Status("XState is not supported by the kernel");
+#endif
+ }
+ llvm_unreachable("NativeRegisterContextNetBSD_x86_64::WriteRegisterSet");
+}
+
+Status
+NativeRegisterContextNetBSD_x86_64::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+ Status error;
+
+ if (!reg_info) {
+ error.SetErrorString("reg_info NULL");
+ return error;
+ }
+
+ const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+ if (reg == LLDB_INVALID_REGNUM) {
+ // This is likely an internal register for lldb use only and should not be
+ // directly queried.
+ error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb "
+ "register, cannot read directly",
+ reg_info->name);
+ return error;
+ }
+
+ int set = GetSetForNativeRegNum(reg);
+ if (set == -1) {
+ // This is likely an internal register for lldb use only and should not be
+ // directly queried.
+ error.SetErrorStringWithFormat("register \"%s\" is in unrecognized set",
+ reg_info->name);
+ return error;
+ }
+
+ error = ReadRegisterSet(set);
+ if (error.Fail())
+ return error;
+
+ switch (reg) {
+ case lldb_rax_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RAX];
+ break;
+ case lldb_rbx_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RBX];
+ break;
+ case lldb_rcx_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RCX];
+ break;
+ case lldb_rdx_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RDX];
+ break;
+ case lldb_rdi_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RDI];
+ break;
+ case lldb_rsi_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RSI];
+ break;
+ case lldb_rbp_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RBP];
+ break;
+ case lldb_rsp_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RSP];
+ break;
+ case lldb_r8_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_R8];
+ break;
+ case lldb_r9_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_R9];
+ break;
+ case lldb_r10_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_R10];
+ break;
+ case lldb_r11_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_R11];
+ break;
+ case lldb_r12_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_R12];
+ break;
+ case lldb_r13_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_R13];
+ break;
+ case lldb_r14_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_R14];
+ break;
+ case lldb_r15_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_R15];
+ break;
+ case lldb_rip_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RIP];
+ break;
+ case lldb_rflags_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_RFLAGS];
+ break;
+ case lldb_cs_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_CS];
+ break;
+ case lldb_fs_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_FS];
+ break;
+ case lldb_gs_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_GS];
+ break;
+ case lldb_ss_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_SS];
+ break;
+ case lldb_ds_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_DS];
+ break;
+ case lldb_es_x86_64:
+ reg_value = (uint64_t)m_gpr_x86_64.regs[_REG_ES];
+ break;
+ case lldb_fctrl_x86_64:
+ reg_value = (uint16_t)m_fpr_x86_64.fxstate.fx_cw;
+ break;
+ case lldb_fstat_x86_64:
+ reg_value = (uint16_t)m_fpr_x86_64.fxstate.fx_sw;
+ break;
+ case lldb_ftag_x86_64:
+ reg_value = (uint8_t)m_fpr_x86_64.fxstate.fx_tw;
+ break;
+ case lldb_fop_x86_64:
+ reg_value = (uint64_t)m_fpr_x86_64.fxstate.fx_opcode;
+ break;
+ case lldb_fiseg_x86_64:
+ reg_value = (uint64_t)m_fpr_x86_64.fxstate.fx_ip.fa_64;
+ break;
+ case lldb_fioff_x86_64:
+ reg_value = (uint32_t)m_fpr_x86_64.fxstate.fx_ip.fa_32.fa_off;
+ break;
+ case lldb_foseg_x86_64:
+ reg_value = (uint64_t)m_fpr_x86_64.fxstate.fx_dp.fa_64;
+ break;
+ case lldb_fooff_x86_64:
+ reg_value = (uint32_t)m_fpr_x86_64.fxstate.fx_dp.fa_32.fa_off;
+ break;
+ case lldb_mxcsr_x86_64:
+ reg_value = (uint32_t)m_fpr_x86_64.fxstate.fx_mxcsr;
+ break;
+ case lldb_mxcsrmask_x86_64:
+ reg_value = (uint32_t)m_fpr_x86_64.fxstate.fx_mxcsr_mask;
+ break;
+ case lldb_st0_x86_64:
+ case lldb_st1_x86_64:
+ case lldb_st2_x86_64:
+ case lldb_st3_x86_64:
+ case lldb_st4_x86_64:
+ case lldb_st5_x86_64:
+ case lldb_st6_x86_64:
+ case lldb_st7_x86_64:
+ reg_value.SetBytes(&m_fpr_x86_64.fxstate.fx_87_ac[reg - lldb_st0_x86_64],
+ reg_info->byte_size, endian::InlHostByteOrder());
+ break;
+ case lldb_mm0_x86_64:
+ case lldb_mm1_x86_64:
+ case lldb_mm2_x86_64:
+ case lldb_mm3_x86_64:
+ case lldb_mm4_x86_64:
+ case lldb_mm5_x86_64:
+ case lldb_mm6_x86_64:
+ case lldb_mm7_x86_64:
+ reg_value.SetBytes(&m_fpr_x86_64.fxstate.fx_87_ac[reg - lldb_mm0_x86_64],
+ reg_info->byte_size, endian::InlHostByteOrder());
+ break;
+ case lldb_xmm0_x86_64:
+ case lldb_xmm1_x86_64:
+ case lldb_xmm2_x86_64:
+ case lldb_xmm3_x86_64:
+ case lldb_xmm4_x86_64:
+ case lldb_xmm5_x86_64:
+ case lldb_xmm6_x86_64:
+ case lldb_xmm7_x86_64:
+ case lldb_xmm8_x86_64:
+ case lldb_xmm9_x86_64:
+ case lldb_xmm10_x86_64:
+ case lldb_xmm11_x86_64:
+ case lldb_xmm12_x86_64:
+ case lldb_xmm13_x86_64:
+ case lldb_xmm14_x86_64:
+ case lldb_xmm15_x86_64:
+ reg_value.SetBytes(&m_fpr_x86_64.fxstate.fx_xmm[reg - lldb_xmm0_x86_64],
+ reg_info->byte_size, endian::InlHostByteOrder());
+ break;
+ case lldb_ymm0_x86_64:
+ case lldb_ymm1_x86_64:
+ case lldb_ymm2_x86_64:
+ case lldb_ymm3_x86_64:
+ case lldb_ymm4_x86_64:
+ case lldb_ymm5_x86_64:
+ case lldb_ymm6_x86_64:
+ case lldb_ymm7_x86_64:
+ case lldb_ymm8_x86_64:
+ case lldb_ymm9_x86_64:
+ case lldb_ymm10_x86_64:
+ case lldb_ymm11_x86_64:
+ case lldb_ymm12_x86_64:
+ case lldb_ymm13_x86_64:
+ case lldb_ymm14_x86_64:
+ case lldb_ymm15_x86_64:
+#ifdef HAVE_XSTATE
+ if (!(m_xstate_x86_64.xs_rfbm & XCR0_SSE) ||
+ !(m_xstate_x86_64.xs_rfbm & XCR0_YMM_Hi128)) {
+ error.SetErrorStringWithFormat("register \"%s\" not supported by CPU/kernel",
+ reg_info->name);
+ } else {
+ uint32_t reg_index = reg - lldb_ymm0_x86_64;
+ YMMReg ymm = XStateToYMM(
+ m_xstate_x86_64.xs_fxsave.fx_xmm[reg_index].xmm_bytes,
+ m_xstate_x86_64.xs_ymm_hi128.xs_ymm[reg_index].ymm_bytes);
+ reg_value.SetBytes(ymm.bytes, reg_info->byte_size,
+ endian::InlHostByteOrder());
+ }
+#else
+ error.SetErrorString("XState queries not supported by the kernel");
+#endif
+ break;
+ case lldb_dr0_x86_64:
+ case lldb_dr1_x86_64:
+ case lldb_dr2_x86_64:
+ case lldb_dr3_x86_64:
+ case lldb_dr4_x86_64:
+ case lldb_dr5_x86_64:
+ case lldb_dr6_x86_64:
+ case lldb_dr7_x86_64:
+ reg_value = (uint64_t)m_dbr_x86_64.dr[reg - lldb_dr0_x86_64];
+ break;
+ }
+
+ return error;
+}
+
+Status NativeRegisterContextNetBSD_x86_64::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue &reg_value) {
+
+ Status error;
+
+ if (!reg_info) {
+ error.SetErrorString("reg_info NULL");
+ return error;
+ }
+
+ const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+ if (reg == LLDB_INVALID_REGNUM) {
+ // This is likely an internal register for lldb use only and should not be
+ // directly queried.
+ error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb "
+ "register, cannot read directly",
+ reg_info->name);
+ return error;
+ }
+
+ int set = GetSetForNativeRegNum(reg);
+ if (set == -1) {
+ // This is likely an internal register for lldb use only and should not be
+ // directly queried.
+ error.SetErrorStringWithFormat("register \"%s\" is in unrecognized set",
+ reg_info->name);
+ return error;
+ }
+
+ error = ReadRegisterSet(set);
+ if (error.Fail())
+ return error;
+
+ switch (reg) {
+ case lldb_rax_x86_64:
+ m_gpr_x86_64.regs[_REG_RAX] = reg_value.GetAsUInt64();
+ break;
+ case lldb_rbx_x86_64:
+ m_gpr_x86_64.regs[_REG_RBX] = reg_value.GetAsUInt64();
+ break;
+ case lldb_rcx_x86_64:
+ m_gpr_x86_64.regs[_REG_RCX] = reg_value.GetAsUInt64();
+ break;
+ case lldb_rdx_x86_64:
+ m_gpr_x86_64.regs[_REG_RDX] = reg_value.GetAsUInt64();
+ break;
+ case lldb_rdi_x86_64:
+ m_gpr_x86_64.regs[_REG_RDI] = reg_value.GetAsUInt64();
+ break;
+ case lldb_rsi_x86_64:
+ m_gpr_x86_64.regs[_REG_RSI] = reg_value.GetAsUInt64();
+ break;
+ case lldb_rbp_x86_64:
+ m_gpr_x86_64.regs[_REG_RBP] = reg_value.GetAsUInt64();
+ break;
+ case lldb_rsp_x86_64:
+ m_gpr_x86_64.regs[_REG_RSP] = reg_value.GetAsUInt64();
+ break;
+ case lldb_r8_x86_64:
+ m_gpr_x86_64.regs[_REG_R8] = reg_value.GetAsUInt64();
+ break;
+ case lldb_r9_x86_64:
+ m_gpr_x86_64.regs[_REG_R9] = reg_value.GetAsUInt64();
+ break;
+ case lldb_r10_x86_64:
+ m_gpr_x86_64.regs[_REG_R10] = reg_value.GetAsUInt64();
+ break;
+ case lldb_r11_x86_64:
+ m_gpr_x86_64.regs[_REG_R11] = reg_value.GetAsUInt64();
+ break;
+ case lldb_r12_x86_64:
+ m_gpr_x86_64.regs[_REG_R12] = reg_value.GetAsUInt64();
+ break;
+ case lldb_r13_x86_64:
+ m_gpr_x86_64.regs[_REG_R13] = reg_value.GetAsUInt64();
+ break;
+ case lldb_r14_x86_64:
+ m_gpr_x86_64.regs[_REG_R14] = reg_value.GetAsUInt64();
+ break;
+ case lldb_r15_x86_64:
+ m_gpr_x86_64.regs[_REG_R15] = reg_value.GetAsUInt64();
+ break;
+ case lldb_rip_x86_64:
+ m_gpr_x86_64.regs[_REG_RIP] = reg_value.GetAsUInt64();
+ break;
+ case lldb_rflags_x86_64:
+ m_gpr_x86_64.regs[_REG_RFLAGS] = reg_value.GetAsUInt64();
+ break;
+ case lldb_cs_x86_64:
+ m_gpr_x86_64.regs[_REG_CS] = reg_value.GetAsUInt64();
+ break;
+ case lldb_fs_x86_64:
+ m_gpr_x86_64.regs[_REG_FS] = reg_value.GetAsUInt64();
+ break;
+ case lldb_gs_x86_64:
+ m_gpr_x86_64.regs[_REG_GS] = reg_value.GetAsUInt64();
+ break;
+ case lldb_ss_x86_64:
+ m_gpr_x86_64.regs[_REG_SS] = reg_value.GetAsUInt64();
+ break;
+ case lldb_ds_x86_64:
+ m_gpr_x86_64.regs[_REG_DS] = reg_value.GetAsUInt64();
+ break;
+ case lldb_es_x86_64:
+ m_gpr_x86_64.regs[_REG_ES] = reg_value.GetAsUInt64();
+ break;
+ case lldb_fctrl_x86_64:
+ m_fpr_x86_64.fxstate.fx_cw = reg_value.GetAsUInt16();
+ break;
+ case lldb_fstat_x86_64:
+ m_fpr_x86_64.fxstate.fx_sw = reg_value.GetAsUInt16();
+ break;
+ case lldb_ftag_x86_64:
+ m_fpr_x86_64.fxstate.fx_tw = reg_value.GetAsUInt8();
+ break;
+ case lldb_fop_x86_64:
+ m_fpr_x86_64.fxstate.fx_opcode = reg_value.GetAsUInt16();
+ break;
+ case lldb_fiseg_x86_64:
+ m_fpr_x86_64.fxstate.fx_ip.fa_64 = reg_value.GetAsUInt64();
+ break;
+ case lldb_fioff_x86_64:
+ m_fpr_x86_64.fxstate.fx_ip.fa_32.fa_off = reg_value.GetAsUInt32();
+ break;
+ case lldb_foseg_x86_64:
+ m_fpr_x86_64.fxstate.fx_dp.fa_64 = reg_value.GetAsUInt64();
+ break;
+ case lldb_fooff_x86_64:
+ m_fpr_x86_64.fxstate.fx_dp.fa_32.fa_off = reg_value.GetAsUInt32();
+ break;
+ case lldb_mxcsr_x86_64:
+ m_fpr_x86_64.fxstate.fx_mxcsr = reg_value.GetAsUInt32();
+ break;
+ case lldb_mxcsrmask_x86_64:
+ m_fpr_x86_64.fxstate.fx_mxcsr_mask = reg_value.GetAsUInt32();
+ break;
+ case lldb_st0_x86_64:
+ case lldb_st1_x86_64:
+ case lldb_st2_x86_64:
+ case lldb_st3_x86_64:
+ case lldb_st4_x86_64:
+ case lldb_st5_x86_64:
+ case lldb_st6_x86_64:
+ case lldb_st7_x86_64:
+ ::memcpy(&m_fpr_x86_64.fxstate.fx_87_ac[reg - lldb_st0_x86_64],
+ reg_value.GetBytes(), reg_value.GetByteSize());
+ break;
+ case lldb_mm0_x86_64:
+ case lldb_mm1_x86_64:
+ case lldb_mm2_x86_64:
+ case lldb_mm3_x86_64:
+ case lldb_mm4_x86_64:
+ case lldb_mm5_x86_64:
+ case lldb_mm6_x86_64:
+ case lldb_mm7_x86_64:
+ ::memcpy(&m_fpr_x86_64.fxstate.fx_87_ac[reg - lldb_mm0_x86_64],
+ reg_value.GetBytes(), reg_value.GetByteSize());
+ break;
+ case lldb_xmm0_x86_64:
+ case lldb_xmm1_x86_64:
+ case lldb_xmm2_x86_64:
+ case lldb_xmm3_x86_64:
+ case lldb_xmm4_x86_64:
+ case lldb_xmm5_x86_64:
+ case lldb_xmm6_x86_64:
+ case lldb_xmm7_x86_64:
+ case lldb_xmm8_x86_64:
+ case lldb_xmm9_x86_64:
+ case lldb_xmm10_x86_64:
+ case lldb_xmm11_x86_64:
+ case lldb_xmm12_x86_64:
+ case lldb_xmm13_x86_64:
+ case lldb_xmm14_x86_64:
+ case lldb_xmm15_x86_64:
+ ::memcpy(&m_fpr_x86_64.fxstate.fx_xmm[reg - lldb_xmm0_x86_64],
+ reg_value.GetBytes(), reg_value.GetByteSize());
+ break;
+ case lldb_ymm0_x86_64:
+ case lldb_ymm1_x86_64:
+ case lldb_ymm2_x86_64:
+ case lldb_ymm3_x86_64:
+ case lldb_ymm4_x86_64:
+ case lldb_ymm5_x86_64:
+ case lldb_ymm6_x86_64:
+ case lldb_ymm7_x86_64:
+ case lldb_ymm8_x86_64:
+ case lldb_ymm9_x86_64:
+ case lldb_ymm10_x86_64:
+ case lldb_ymm11_x86_64:
+ case lldb_ymm12_x86_64:
+ case lldb_ymm13_x86_64:
+ case lldb_ymm14_x86_64:
+ case lldb_ymm15_x86_64:
+#ifdef HAVE_XSTATE
+ if (!(m_xstate_x86_64.xs_rfbm & XCR0_SSE) ||
+ !(m_xstate_x86_64.xs_rfbm & XCR0_YMM_Hi128)) {
+ error.SetErrorStringWithFormat("register \"%s\" not supported by CPU/kernel",
+ reg_info->name);
+ } else {
+ uint32_t reg_index = reg - lldb_ymm0_x86_64;
+ YMMReg ymm;
+ ::memcpy(ymm.bytes, reg_value.GetBytes(), reg_value.GetByteSize());
+ YMMToXState(ymm,
+ m_xstate_x86_64.xs_fxsave.fx_xmm[reg_index].xmm_bytes,
+ m_xstate_x86_64.xs_ymm_hi128.xs_ymm[reg_index].ymm_bytes);
+ }
+#else
+ error.SetErrorString("XState not supported by the kernel");
+#endif
+ break;
+ case lldb_dr0_x86_64:
+ case lldb_dr1_x86_64:
+ case lldb_dr2_x86_64:
+ case lldb_dr3_x86_64:
+ case lldb_dr4_x86_64:
+ case lldb_dr5_x86_64:
+ case lldb_dr6_x86_64:
+ case lldb_dr7_x86_64:
+ m_dbr_x86_64.dr[reg - lldb_dr0_x86_64] = reg_value.GetAsUInt64();
+ break;
+ }
+
+ return WriteRegisterSet(set);
+}
+
+Status NativeRegisterContextNetBSD_x86_64::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ Status error;
+
+ data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0));
+ error = ReadRegisterSet(GPRegSet);
+ if (error.Fail())
+ return error;
+
+ uint8_t *dst = data_sp->GetBytes();
+ ::memcpy(dst, &m_gpr_x86_64, GetRegisterInfoInterface().GetGPRSize());
+ dst += GetRegisterInfoInterface().GetGPRSize();
+
+ RegisterValue value((uint64_t)-1);
+ const RegisterInfo *reg_info =
+ GetRegisterInfoInterface().GetDynamicRegisterInfo("orig_eax");
+ if (reg_info == nullptr)
+ reg_info = GetRegisterInfoInterface().GetDynamicRegisterInfo("orig_rax");
+ return error;
+}
+
+Status NativeRegisterContextNetBSD_x86_64::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ Status error;
+
+ if (!data_sp) {
+ error.SetErrorStringWithFormat(
+ "NativeRegisterContextNetBSD_x86_64::%s invalid data_sp provided",
+ __FUNCTION__);
+ return error;
+ }
+
+ if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) {
+ error.SetErrorStringWithFormat(
+ "NativeRegisterContextNetBSD_x86_64::%s data_sp contained mismatched "
+ "data size, expected %" PRIu64 ", actual %" PRIu64,
+ __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize());
+ return error;
+ }
+
+ uint8_t *src = data_sp->GetBytes();
+ if (src == nullptr) {
+ error.SetErrorStringWithFormat("NativeRegisterContextNetBSD_x86_64::%s "
+ "DataBuffer::GetBytes() returned a null "
+ "pointer",
+ __FUNCTION__);
+ return error;
+ }
+ ::memcpy(&m_gpr_x86_64, src, GetRegisterInfoInterface().GetGPRSize());
+
+ error = WriteRegisterSet(GPRegSet);
+ if (error.Fail())
+ return error;
+ src += GetRegisterInfoInterface().GetGPRSize();
+
+ return error;
+}
+
+Status NativeRegisterContextNetBSD_x86_64::IsWatchpointHit(uint32_t wp_index,
+ bool &is_hit) {
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return Status("Watchpoint index out of range");
+
+ RegisterValue reg_value;
+ const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(lldb_dr6_x86_64);
+ Status error = ReadRegister(reg_info, reg_value);
+ if (error.Fail()) {
+ is_hit = false;
+ return error;
+ }
+
+ uint64_t status_bits = reg_value.GetAsUInt64();
+
+ is_hit = status_bits & (1 << wp_index);
+
+ return error;
+}
+
+Status NativeRegisterContextNetBSD_x86_64::GetWatchpointHitIndex(
+ uint32_t &wp_index, lldb::addr_t trap_addr) {
+ uint32_t num_hw_wps = NumSupportedHardwareWatchpoints();
+ for (wp_index = 0; wp_index < num_hw_wps; ++wp_index) {
+ bool is_hit;
+ Status error = IsWatchpointHit(wp_index, is_hit);
+ if (error.Fail()) {
+ wp_index = LLDB_INVALID_INDEX32;
+ return error;
+ } else if (is_hit) {
+ return error;
+ }
+ }
+ wp_index = LLDB_INVALID_INDEX32;
+ return Status();
+}
+
+Status NativeRegisterContextNetBSD_x86_64::IsWatchpointVacant(uint32_t wp_index,
+ bool &is_vacant) {
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return Status("Watchpoint index out of range");
+
+ RegisterValue reg_value;
+ const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(lldb_dr7_x86_64);
+ Status error = ReadRegister(reg_info, reg_value);
+ if (error.Fail()) {
+ is_vacant = false;
+ return error;
+ }
+
+ uint64_t control_bits = reg_value.GetAsUInt64();
+
+ is_vacant = !(control_bits & (1 << (2 * wp_index + 1)));
+
+ return error;
+}
+
+Status NativeRegisterContextNetBSD_x86_64::SetHardwareWatchpointWithIndex(
+ lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) {
+
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return Status("Watchpoint index out of range");
+
+ // Read only watchpoints aren't supported on x86_64. Fall back to read/write
+ // waitchpoints instead.
+ // TODO: Add logic to detect when a write happens and ignore that watchpoint
+ // hit.
+ if (watch_flags == 0x2)
+ watch_flags = 0x3;
+
+ if (watch_flags != 0x1 && watch_flags != 0x3)
+ return Status("Invalid read/write bits for watchpoint");
+
+ if (size != 1 && size != 2 && size != 4 && size != 8)
+ return Status("Invalid size for watchpoint");
+
+ bool is_vacant;
+ Status error = IsWatchpointVacant(wp_index, is_vacant);
+ if (error.Fail())
+ return error;
+ if (!is_vacant)
+ return Status("Watchpoint index not vacant");
+
+ RegisterValue reg_value;
+ const RegisterInfo *const reg_info_dr7 =
+ GetRegisterInfoAtIndex(lldb_dr7_x86_64);
+ error = ReadRegister(reg_info_dr7, reg_value);
+ if (error.Fail())
+ return error;
+
+ // for watchpoints 0, 1, 2, or 3, respectively, set bits 1, 3, 5, or 7
+ uint64_t enable_bit = 1 << (2 * wp_index + 1);
+
+ // set bits 16-17, 20-21, 24-25, or 28-29
+ // with 0b01 for write, and 0b11 for read/write
+ uint64_t rw_bits = watch_flags << (16 + 4 * wp_index);
+
+ // set bits 18-19, 22-23, 26-27, or 30-31
+ // with 0b00, 0b01, 0b10, or 0b11
+ // for 1, 2, 8 (if supported), or 4 bytes, respectively
+ uint64_t size_bits = (size == 8 ? 0x2 : size - 1) << (18 + 4 * wp_index);
+
+ uint64_t bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index));
+
+ uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask;
+
+ control_bits |= enable_bit | rw_bits | size_bits;
+
+ const RegisterInfo *const reg_info_drN =
+ GetRegisterInfoAtIndex(lldb_dr0_x86_64 + wp_index);
+ error = WriteRegister(reg_info_drN, RegisterValue(addr));
+ if (error.Fail())
+ return error;
+
+ error = WriteRegister(reg_info_dr7, RegisterValue(control_bits));
+ if (error.Fail())
+ return error;
+
+ error.Clear();
+ return error;
+}
+
+bool NativeRegisterContextNetBSD_x86_64::ClearHardwareWatchpoint(
+ uint32_t wp_index) {
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return false;
+
+ RegisterValue reg_value;
+
+ // for watchpoints 0, 1, 2, or 3, respectively, clear bits 0, 1, 2, or 3 of
+ // the debug status register (DR6)
+ const RegisterInfo *const reg_info_dr6 =
+ GetRegisterInfoAtIndex(lldb_dr6_x86_64);
+ Status error = ReadRegister(reg_info_dr6, reg_value);
+ if (error.Fail())
+ return false;
+ uint64_t bit_mask = 1 << wp_index;
+ uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask;
+ error = WriteRegister(reg_info_dr6, RegisterValue(status_bits));
+ if (error.Fail())
+ return false;
+
+ // for watchpoints 0, 1, 2, or 3, respectively, clear bits {0-1,16-19},
+ // {2-3,20-23}, {4-5,24-27}, or {6-7,28-31} of the debug control register
+ // (DR7)
+ const RegisterInfo *const reg_info_dr7 =
+ GetRegisterInfoAtIndex(lldb_dr7_x86_64);
+ error = ReadRegister(reg_info_dr7, reg_value);
+ if (error.Fail())
+ return false;
+ bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index));
+ uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask;
+ return WriteRegister(reg_info_dr7, RegisterValue(control_bits)).Success();
+}
+
+Status NativeRegisterContextNetBSD_x86_64::ClearAllHardwareWatchpoints() {
+ RegisterValue reg_value;
+
+ // clear bits {0-4} of the debug status register (DR6)
+ const RegisterInfo *const reg_info_dr6 =
+ GetRegisterInfoAtIndex(lldb_dr6_x86_64);
+ Status error = ReadRegister(reg_info_dr6, reg_value);
+ if (error.Fail())
+ return error;
+ uint64_t bit_mask = 0xF;
+ uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask;
+ error = WriteRegister(reg_info_dr6, RegisterValue(status_bits));
+ if (error.Fail())
+ return error;
+
+ // clear bits {0-7,16-31} of the debug control register (DR7)
+ const RegisterInfo *const reg_info_dr7 =
+ GetRegisterInfoAtIndex(lldb_dr7_x86_64);
+ error = ReadRegister(reg_info_dr7, reg_value);
+ if (error.Fail())
+ return error;
+ bit_mask = 0xFF | (0xFFFF << 16);
+ uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask;
+ return WriteRegister(reg_info_dr7, RegisterValue(control_bits));
+}
+
+uint32_t NativeRegisterContextNetBSD_x86_64::SetHardwareWatchpoint(
+ lldb::addr_t addr, size_t size, uint32_t watch_flags) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+ for (uint32_t wp_index = 0; wp_index < num_hw_watchpoints; ++wp_index) {
+ bool is_vacant;
+ Status error = IsWatchpointVacant(wp_index, is_vacant);
+ if (is_vacant) {
+ error = SetHardwareWatchpointWithIndex(addr, size, watch_flags, wp_index);
+ if (error.Success())
+ return wp_index;
+ }
+ if (error.Fail() && log) {
+ log->Printf("NativeRegisterContextNetBSD_x86_64::%s Error: %s",
+ __FUNCTION__, error.AsCString());
+ }
+ }
+ return LLDB_INVALID_INDEX32;
+}
+
+lldb::addr_t
+NativeRegisterContextNetBSD_x86_64::GetWatchpointAddress(uint32_t wp_index) {
+ if (wp_index >= NumSupportedHardwareWatchpoints())
+ return LLDB_INVALID_ADDRESS;
+ RegisterValue reg_value;
+ const RegisterInfo *const reg_info_drN =
+ GetRegisterInfoAtIndex(lldb_dr0_x86_64 + wp_index);
+ if (ReadRegister(reg_info_drN, reg_value).Fail())
+ return LLDB_INVALID_ADDRESS;
+ return reg_value.GetAsUInt64();
+}
+
+uint32_t NativeRegisterContextNetBSD_x86_64::NumSupportedHardwareWatchpoints() {
+ // Available debug address registers: dr0, dr1, dr2, dr3
+ return 4;
+}
+
+#endif // defined(__x86_64__)
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.h
new file mode 100644
index 000000000000..0fed16542a95
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.h
@@ -0,0 +1,97 @@
+//===-- NativeRegisterContextNetBSD_x86_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
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__x86_64__)
+
+#ifndef lldb_NativeRegisterContextNetBSD_x86_64_h
+#define lldb_NativeRegisterContextNetBSD_x86_64_h
+
+// clang-format off
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <machine/reg.h>
+// clang-format on
+
+#include "Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h"
+#include "Plugins/Process/Utility/RegisterContext_x86.h"
+#include "Plugins/Process/Utility/lldb-x86-register-enums.h"
+
+#if defined(PT_GETXSTATE) && defined(PT_SETXSTATE)
+#define HAVE_XSTATE
+#endif
+
+namespace lldb_private {
+namespace process_netbsd {
+
+class NativeProcessNetBSD;
+
+class NativeRegisterContextNetBSD_x86_64 : public NativeRegisterContextNetBSD {
+public:
+ NativeRegisterContextNetBSD_x86_64(const ArchSpec &target_arch,
+ NativeThreadProtocol &native_thread);
+ uint32_t GetRegisterSetCount() const override;
+
+ const RegisterSet *GetRegisterSet(uint32_t set_index) const override;
+
+ Status ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &reg_value) override;
+
+ Status WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &reg_value) override;
+
+ Status ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ Status IsWatchpointHit(uint32_t wp_index, bool &is_hit) override;
+
+ Status GetWatchpointHitIndex(uint32_t &wp_index,
+ lldb::addr_t trap_addr) override;
+
+ Status IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) override;
+
+ bool ClearHardwareWatchpoint(uint32_t wp_index) override;
+
+ Status ClearAllHardwareWatchpoints() override;
+
+ Status SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size,
+ uint32_t watch_flags,
+ uint32_t wp_index);
+
+ uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size,
+ uint32_t watch_flags) override;
+
+ lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override;
+
+ uint32_t NumSupportedHardwareWatchpoints() override;
+
+private:
+ // Private member types.
+ enum { GPRegSet, FPRegSet, DBRegSet, XStateRegSet };
+
+ // Private member variables.
+ struct reg m_gpr_x86_64;
+ struct fpreg m_fpr_x86_64;
+ struct dbreg m_dbr_x86_64;
+#ifdef HAVE_XSTATE
+ struct xstate m_xstate_x86_64;
+#endif
+
+ int GetSetForNativeRegNum(int reg_num) const;
+
+ Status ReadRegisterSet(uint32_t set);
+ Status WriteRegisterSet(uint32_t set);
+};
+
+} // namespace process_netbsd
+} // namespace lldb_private
+
+#endif // #ifndef lldb_NativeRegisterContextNetBSD_x86_64_h
+
+#endif // defined(__x86_64__)
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.cpp
new file mode 100644
index 000000000000..e25975c93446
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.cpp
@@ -0,0 +1,205 @@
+//===-- NativeThreadNetBSD.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 "NativeThreadNetBSD.h"
+#include "NativeRegisterContextNetBSD.h"
+
+#include "NativeProcessNetBSD.h"
+
+#include "Plugins/Process/POSIX/CrashReason.h"
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/State.h"
+
+#include <sstream>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_netbsd;
+
+NativeThreadNetBSD::NativeThreadNetBSD(NativeProcessNetBSD &process,
+ lldb::tid_t tid)
+ : NativeThreadProtocol(process, tid), m_state(StateType::eStateInvalid),
+ m_stop_info(), m_reg_context_up(
+NativeRegisterContextNetBSD::CreateHostNativeRegisterContextNetBSD(process.GetArchitecture(), *this)
+), m_stop_description() {}
+
+void NativeThreadNetBSD::SetStoppedBySignal(uint32_t signo,
+ const siginfo_t *info) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+ LLDB_LOG(log, "tid = {0} in called with signal {1}", GetID(), signo);
+
+ SetStopped();
+
+ m_stop_info.reason = StopReason::eStopReasonSignal;
+ m_stop_info.details.signal.signo = signo;
+
+ m_stop_description.clear();
+ if (info) {
+ switch (signo) {
+ case SIGSEGV:
+ case SIGBUS:
+ case SIGFPE:
+ case SIGILL:
+ const auto reason = GetCrashReason(*info);
+ m_stop_description = GetCrashReasonString(reason, *info);
+ break;
+ }
+ }
+}
+
+void NativeThreadNetBSD::SetStoppedByBreakpoint() {
+ SetStopped();
+ m_stop_info.reason = StopReason::eStopReasonBreakpoint;
+ m_stop_info.details.signal.signo = SIGTRAP;
+}
+
+void NativeThreadNetBSD::SetStoppedByTrace() {
+ SetStopped();
+ m_stop_info.reason = StopReason::eStopReasonTrace;
+ m_stop_info.details.signal.signo = SIGTRAP;
+}
+
+void NativeThreadNetBSD::SetStoppedByExec() {
+ SetStopped();
+ m_stop_info.reason = StopReason::eStopReasonExec;
+ m_stop_info.details.signal.signo = SIGTRAP;
+}
+
+void NativeThreadNetBSD::SetStoppedByWatchpoint(uint32_t wp_index) {
+ SetStopped();
+
+ lldbassert(wp_index != LLDB_INVALID_INDEX32 && "wp_index cannot be invalid");
+
+ std::ostringstream ostr;
+ ostr << GetRegisterContext().GetWatchpointAddress(wp_index) << " ";
+ ostr << wp_index;
+
+ ostr << " " << GetRegisterContext().GetWatchpointHitAddress(wp_index);
+
+ m_stop_description = ostr.str();
+
+ m_stop_info.reason = StopReason::eStopReasonWatchpoint;
+ m_stop_info.details.signal.signo = SIGTRAP;
+}
+
+void NativeThreadNetBSD::SetStopped() {
+ const StateType new_state = StateType::eStateStopped;
+ m_state = new_state;
+ m_stop_description.clear();
+}
+
+void NativeThreadNetBSD::SetRunning() {
+ m_state = StateType::eStateRunning;
+ m_stop_info.reason = StopReason::eStopReasonNone;
+}
+
+void NativeThreadNetBSD::SetStepping() {
+ m_state = StateType::eStateStepping;
+ m_stop_info.reason = StopReason::eStopReasonNone;
+}
+
+std::string NativeThreadNetBSD::GetName() { return std::string(""); }
+
+lldb::StateType NativeThreadNetBSD::GetState() { return m_state; }
+
+bool NativeThreadNetBSD::GetStopReason(ThreadStopInfo &stop_info,
+ std::string &description) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+
+ description.clear();
+
+ switch (m_state) {
+ case eStateStopped:
+ case eStateCrashed:
+ case eStateExited:
+ case eStateSuspended:
+ case eStateUnloaded:
+ stop_info = m_stop_info;
+ description = m_stop_description;
+
+ return true;
+
+ case eStateInvalid:
+ case eStateConnected:
+ case eStateAttaching:
+ case eStateLaunching:
+ case eStateRunning:
+ case eStateStepping:
+ case eStateDetached:
+ LLDB_LOG(log, "tid = {0} in state {1} cannot answer stop reason", GetID(),
+ StateAsCString(m_state));
+ return false;
+ }
+ llvm_unreachable("unhandled StateType!");
+}
+
+NativeRegisterContext& NativeThreadNetBSD::GetRegisterContext() {
+ assert(m_reg_context_up);
+return *m_reg_context_up;
+}
+
+Status NativeThreadNetBSD::SetWatchpoint(lldb::addr_t addr, size_t size,
+ uint32_t watch_flags, bool hardware) {
+ if (!hardware)
+ return Status("not implemented");
+ if (m_state == eStateLaunching)
+ return Status();
+ Status error = RemoveWatchpoint(addr);
+ if (error.Fail())
+ return error;
+ uint32_t wp_index = GetRegisterContext().SetHardwareWatchpoint(addr, size, watch_flags);
+ if (wp_index == LLDB_INVALID_INDEX32)
+ return Status("Setting hardware watchpoint failed.");
+ m_watchpoint_index_map.insert({addr, wp_index});
+ return Status();
+}
+
+Status NativeThreadNetBSD::RemoveWatchpoint(lldb::addr_t addr) {
+ auto wp = m_watchpoint_index_map.find(addr);
+ if (wp == m_watchpoint_index_map.end())
+ return Status();
+ uint32_t wp_index = wp->second;
+ m_watchpoint_index_map.erase(wp);
+ if (GetRegisterContext().ClearHardwareWatchpoint(wp_index))
+ return Status();
+ return Status("Clearing hardware watchpoint failed.");
+}
+
+Status NativeThreadNetBSD::SetHardwareBreakpoint(lldb::addr_t addr,
+ size_t size) {
+ if (m_state == eStateLaunching)
+ return Status();
+
+ Status error = RemoveHardwareBreakpoint(addr);
+ if (error.Fail())
+ return error;
+
+ uint32_t bp_index = GetRegisterContext().SetHardwareBreakpoint(addr, size);
+
+ if (bp_index == LLDB_INVALID_INDEX32)
+ return Status("Setting hardware breakpoint failed.");
+
+ m_hw_break_index_map.insert({addr, bp_index});
+ return Status();
+}
+
+Status NativeThreadNetBSD::RemoveHardwareBreakpoint(lldb::addr_t addr) {
+ auto bp = m_hw_break_index_map.find(addr);
+ if (bp == m_hw_break_index_map.end())
+ return Status();
+
+ uint32_t bp_index = bp->second;
+ if (GetRegisterContext().ClearHardwareBreakpoint(bp_index)) {
+ m_hw_break_index_map.erase(bp);
+ return Status();
+ }
+
+ return Status("Clearing hardware breakpoint failed.");
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.h b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.h
new file mode 100644
index 000000000000..015d8995db34
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.h
@@ -0,0 +1,74 @@
+//===-- NativeThreadNetBSD.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 liblldb_NativeThreadNetBSD_H_
+#define liblldb_NativeThreadNetBSD_H_
+
+#include "lldb/Host/common/NativeThreadProtocol.h"
+
+#include <csignal>
+#include <map>
+#include <string>
+
+namespace lldb_private {
+namespace process_netbsd {
+
+class NativeProcessNetBSD;
+
+class NativeThreadNetBSD : public NativeThreadProtocol {
+ friend class NativeProcessNetBSD;
+
+public:
+ NativeThreadNetBSD(NativeProcessNetBSD &process, lldb::tid_t tid);
+
+ // NativeThreadProtocol Interface
+ std::string GetName() override;
+
+ lldb::StateType GetState() override;
+
+ bool GetStopReason(ThreadStopInfo &stop_info,
+ std::string &description) override;
+
+ NativeRegisterContext& GetRegisterContext() override;
+
+ Status SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags,
+ bool hardware) override;
+
+ Status RemoveWatchpoint(lldb::addr_t addr) override;
+
+ Status SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override;
+
+ Status RemoveHardwareBreakpoint(lldb::addr_t addr) override;
+
+private:
+ // Interface for friend classes
+
+ void SetStoppedBySignal(uint32_t signo, const siginfo_t *info = nullptr);
+ void SetStoppedByBreakpoint();
+ void SetStoppedByTrace();
+ void SetStoppedByExec();
+ void SetStoppedByWatchpoint(uint32_t wp_index);
+ void SetStopped();
+ void SetRunning();
+ void SetStepping();
+
+ // Member Variables
+ lldb::StateType m_state;
+ ThreadStopInfo m_stop_info;
+ std::unique_ptr<NativeRegisterContext> m_reg_context_up;
+ std::string m_stop_description;
+ using WatchpointIndexMap = std::map<lldb::addr_t, uint32_t>;
+ WatchpointIndexMap m_watchpoint_index_map;
+ WatchpointIndexMap m_hw_break_index_map;
+};
+
+typedef std::shared_ptr<NativeThreadNetBSD> NativeThreadNetBSDSP;
+} // namespace process_netbsd
+} // namespace lldb_private
+
+#endif // #ifndef liblldb_NativeThreadNetBSD_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/CrashReason.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/CrashReason.cpp
new file mode 100644
index 000000000000..70c2687e3b8c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/CrashReason.cpp
@@ -0,0 +1,337 @@
+//===-- CrashReason.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 "CrashReason.h"
+
+#include "llvm/Support/raw_ostream.h"
+
+#include <sstream>
+
+namespace {
+
+void AppendFaultAddr(std::string &str, lldb::addr_t addr) {
+ std::stringstream ss;
+ ss << " (fault address: 0x" << std::hex << addr << ")";
+ str += ss.str();
+}
+
+#if defined(si_lower) && defined(si_upper)
+void AppendBounds(std::string &str, lldb::addr_t lower_bound,
+ lldb::addr_t upper_bound, lldb::addr_t addr) {
+ llvm::raw_string_ostream stream(str);
+ if ((unsigned long)addr < lower_bound)
+ stream << ": lower bound violation ";
+ else
+ stream << ": upper bound violation ";
+ stream << "(fault address: 0x";
+ stream.write_hex(addr);
+ stream << ", lower bound: 0x";
+ stream.write_hex(lower_bound);
+ stream << ", upper bound: 0x";
+ stream.write_hex(upper_bound);
+ stream << ")";
+ stream.flush();
+}
+#endif
+
+CrashReason GetCrashReasonForSIGSEGV(const siginfo_t &info) {
+ assert(info.si_signo == SIGSEGV);
+
+ switch (info.si_code) {
+#ifdef SI_KERNEL
+ case SI_KERNEL:
+ // Some platforms will occasionally send nonstandard spurious SI_KERNEL
+ // codes. One way to get this is via unaligned SIMD loads.
+ return CrashReason::eInvalidAddress; // for lack of anything better
+#endif
+ case SEGV_MAPERR:
+ return CrashReason::eInvalidAddress;
+ case SEGV_ACCERR:
+ return CrashReason::ePrivilegedAddress;
+#ifndef SEGV_BNDERR
+#define SEGV_BNDERR 3
+#endif
+ case SEGV_BNDERR:
+ return CrashReason::eBoundViolation;
+ }
+
+ return CrashReason::eInvalidCrashReason;
+}
+
+CrashReason GetCrashReasonForSIGILL(const siginfo_t &info) {
+ assert(info.si_signo == SIGILL);
+
+ switch (info.si_code) {
+ case ILL_ILLOPC:
+ return CrashReason::eIllegalOpcode;
+ case ILL_ILLOPN:
+ return CrashReason::eIllegalOperand;
+ case ILL_ILLADR:
+ return CrashReason::eIllegalAddressingMode;
+ case ILL_ILLTRP:
+ return CrashReason::eIllegalTrap;
+ case ILL_PRVOPC:
+ return CrashReason::ePrivilegedOpcode;
+ case ILL_PRVREG:
+ return CrashReason::ePrivilegedRegister;
+ case ILL_COPROC:
+ return CrashReason::eCoprocessorError;
+ case ILL_BADSTK:
+ return CrashReason::eInternalStackError;
+ }
+
+ return CrashReason::eInvalidCrashReason;
+}
+
+CrashReason GetCrashReasonForSIGFPE(const siginfo_t &info) {
+ assert(info.si_signo == SIGFPE);
+
+ switch (info.si_code) {
+ case FPE_INTDIV:
+ return CrashReason::eIntegerDivideByZero;
+ case FPE_INTOVF:
+ return CrashReason::eIntegerOverflow;
+ case FPE_FLTDIV:
+ return CrashReason::eFloatDivideByZero;
+ case FPE_FLTOVF:
+ return CrashReason::eFloatOverflow;
+ case FPE_FLTUND:
+ return CrashReason::eFloatUnderflow;
+ case FPE_FLTRES:
+ return CrashReason::eFloatInexactResult;
+ case FPE_FLTINV:
+ return CrashReason::eFloatInvalidOperation;
+ case FPE_FLTSUB:
+ return CrashReason::eFloatSubscriptRange;
+ }
+
+ return CrashReason::eInvalidCrashReason;
+}
+
+CrashReason GetCrashReasonForSIGBUS(const siginfo_t &info) {
+ assert(info.si_signo == SIGBUS);
+
+ switch (info.si_code) {
+ case BUS_ADRALN:
+ return CrashReason::eIllegalAlignment;
+ case BUS_ADRERR:
+ return CrashReason::eIllegalAddress;
+ case BUS_OBJERR:
+ return CrashReason::eHardwareError;
+ }
+
+ return CrashReason::eInvalidCrashReason;
+}
+}
+
+std::string GetCrashReasonString(CrashReason reason, const siginfo_t &info) {
+ std::string str;
+
+// make sure that siginfo_t has the bound fields available.
+#if defined(si_lower) && defined(si_upper)
+ if (reason == CrashReason::eBoundViolation) {
+ str = "signal SIGSEGV";
+ AppendBounds(str, reinterpret_cast<lldb::addr_t>(info.si_lower),
+ reinterpret_cast<lldb::addr_t>(info.si_upper),
+ reinterpret_cast<lldb::addr_t>(info.si_addr));
+ return str;
+ }
+#endif
+
+ return GetCrashReasonString(reason,
+ reinterpret_cast<lldb::addr_t>(info.si_addr));
+}
+
+std::string GetCrashReasonString(CrashReason reason, lldb::addr_t fault_addr) {
+ std::string str;
+
+ switch (reason) {
+ default:
+ str = "unknown crash reason";
+ break;
+
+ case CrashReason::eInvalidAddress:
+ str = "signal SIGSEGV: invalid address";
+ AppendFaultAddr(str, fault_addr);
+ break;
+ case CrashReason::ePrivilegedAddress:
+ str = "signal SIGSEGV: address access protected";
+ AppendFaultAddr(str, fault_addr);
+ break;
+ case CrashReason::eBoundViolation:
+ str = "signal SIGSEGV: bound violation";
+ break;
+ case CrashReason::eIllegalOpcode:
+ str = "signal SIGILL: illegal instruction";
+ break;
+ case CrashReason::eIllegalOperand:
+ str = "signal SIGILL: illegal instruction operand";
+ break;
+ case CrashReason::eIllegalAddressingMode:
+ str = "signal SIGILL: illegal addressing mode";
+ break;
+ case CrashReason::eIllegalTrap:
+ str = "signal SIGILL: illegal trap";
+ break;
+ case CrashReason::ePrivilegedOpcode:
+ str = "signal SIGILL: privileged instruction";
+ break;
+ case CrashReason::ePrivilegedRegister:
+ str = "signal SIGILL: privileged register";
+ break;
+ case CrashReason::eCoprocessorError:
+ str = "signal SIGILL: coprocessor error";
+ break;
+ case CrashReason::eInternalStackError:
+ str = "signal SIGILL: internal stack error";
+ break;
+ case CrashReason::eIllegalAlignment:
+ str = "signal SIGBUS: illegal alignment";
+ break;
+ case CrashReason::eIllegalAddress:
+ str = "signal SIGBUS: illegal address";
+ break;
+ case CrashReason::eHardwareError:
+ str = "signal SIGBUS: hardware error";
+ break;
+ case CrashReason::eIntegerDivideByZero:
+ str = "signal SIGFPE: integer divide by zero";
+ break;
+ case CrashReason::eIntegerOverflow:
+ str = "signal SIGFPE: integer overflow";
+ break;
+ case CrashReason::eFloatDivideByZero:
+ str = "signal SIGFPE: floating point divide by zero";
+ break;
+ case CrashReason::eFloatOverflow:
+ str = "signal SIGFPE: floating point overflow";
+ break;
+ case CrashReason::eFloatUnderflow:
+ str = "signal SIGFPE: floating point underflow";
+ break;
+ case CrashReason::eFloatInexactResult:
+ str = "signal SIGFPE: inexact floating point result";
+ break;
+ case CrashReason::eFloatInvalidOperation:
+ str = "signal SIGFPE: invalid floating point operation";
+ break;
+ case CrashReason::eFloatSubscriptRange:
+ str = "signal SIGFPE: invalid floating point subscript range";
+ break;
+ }
+
+ return str;
+}
+
+const char *CrashReasonAsString(CrashReason reason) {
+#ifdef LLDB_CONFIGURATION_BUILDANDINTEGRATION
+ // Just return the code in ascii for integration builds.
+ chcar str[8];
+ sprintf(str, "%d", reason);
+#else
+ const char *str = nullptr;
+
+ switch (reason) {
+ case CrashReason::eInvalidCrashReason:
+ str = "eInvalidCrashReason";
+ break;
+
+ // SIGSEGV crash reasons.
+ case CrashReason::eInvalidAddress:
+ str = "eInvalidAddress";
+ break;
+ case CrashReason::ePrivilegedAddress:
+ str = "ePrivilegedAddress";
+ break;
+ case CrashReason::eBoundViolation:
+ str = "eBoundViolation";
+ break;
+
+ // SIGILL crash reasons.
+ case CrashReason::eIllegalOpcode:
+ str = "eIllegalOpcode";
+ break;
+ case CrashReason::eIllegalOperand:
+ str = "eIllegalOperand";
+ break;
+ case CrashReason::eIllegalAddressingMode:
+ str = "eIllegalAddressingMode";
+ break;
+ case CrashReason::eIllegalTrap:
+ str = "eIllegalTrap";
+ break;
+ case CrashReason::ePrivilegedOpcode:
+ str = "ePrivilegedOpcode";
+ break;
+ case CrashReason::ePrivilegedRegister:
+ str = "ePrivilegedRegister";
+ break;
+ case CrashReason::eCoprocessorError:
+ str = "eCoprocessorError";
+ break;
+ case CrashReason::eInternalStackError:
+ str = "eInternalStackError";
+ break;
+
+ // SIGBUS crash reasons:
+ case CrashReason::eIllegalAlignment:
+ str = "eIllegalAlignment";
+ break;
+ case CrashReason::eIllegalAddress:
+ str = "eIllegalAddress";
+ break;
+ case CrashReason::eHardwareError:
+ str = "eHardwareError";
+ break;
+
+ // SIGFPE crash reasons:
+ case CrashReason::eIntegerDivideByZero:
+ str = "eIntegerDivideByZero";
+ break;
+ case CrashReason::eIntegerOverflow:
+ str = "eIntegerOverflow";
+ break;
+ case CrashReason::eFloatDivideByZero:
+ str = "eFloatDivideByZero";
+ break;
+ case CrashReason::eFloatOverflow:
+ str = "eFloatOverflow";
+ break;
+ case CrashReason::eFloatUnderflow:
+ str = "eFloatUnderflow";
+ break;
+ case CrashReason::eFloatInexactResult:
+ str = "eFloatInexactResult";
+ break;
+ case CrashReason::eFloatInvalidOperation:
+ str = "eFloatInvalidOperation";
+ break;
+ case CrashReason::eFloatSubscriptRange:
+ str = "eFloatSubscriptRange";
+ break;
+ }
+#endif
+
+ return str;
+}
+
+CrashReason GetCrashReason(const siginfo_t &info) {
+ switch (info.si_signo) {
+ case SIGSEGV:
+ return GetCrashReasonForSIGSEGV(info);
+ case SIGBUS:
+ return GetCrashReasonForSIGBUS(info);
+ case SIGFPE:
+ return GetCrashReasonForSIGFPE(info);
+ case SIGILL:
+ return GetCrashReasonForSIGILL(info);
+ }
+
+ assert(false && "unexpected signal");
+ return CrashReason::eInvalidCrashReason;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/CrashReason.h b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/CrashReason.h
new file mode 100644
index 000000000000..9b4784a1e68e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/CrashReason.h
@@ -0,0 +1,59 @@
+//===-- CrashReason.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 liblldb_CrashReason_H_
+#define liblldb_CrashReason_H_
+
+#include "lldb/lldb-types.h"
+
+#include <signal.h>
+
+#include <string>
+
+enum class CrashReason {
+ eInvalidCrashReason,
+
+ // SIGSEGV crash reasons.
+ eInvalidAddress,
+ ePrivilegedAddress,
+ eBoundViolation,
+
+ // SIGILL crash reasons.
+ eIllegalOpcode,
+ eIllegalOperand,
+ eIllegalAddressingMode,
+ eIllegalTrap,
+ ePrivilegedOpcode,
+ ePrivilegedRegister,
+ eCoprocessorError,
+ eInternalStackError,
+
+ // SIGBUS crash reasons,
+ eIllegalAlignment,
+ eIllegalAddress,
+ eHardwareError,
+
+ // SIGFPE crash reasons,
+ eIntegerDivideByZero,
+ eIntegerOverflow,
+ eFloatDivideByZero,
+ eFloatOverflow,
+ eFloatUnderflow,
+ eFloatInexactResult,
+ eFloatInvalidOperation,
+ eFloatSubscriptRange
+};
+
+std::string GetCrashReasonString(CrashReason reason, lldb::addr_t fault_addr);
+std::string GetCrashReasonString(CrashReason reason, const siginfo_t &info);
+
+const char *CrashReasonAsString(CrashReason reason);
+
+CrashReason GetCrashReason(const siginfo_t &info);
+
+#endif // #ifndef liblldb_CrashReason_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/NativeProcessELF.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/NativeProcessELF.cpp
new file mode 100644
index 000000000000..559b16c8fd69
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/NativeProcessELF.cpp
@@ -0,0 +1,110 @@
+//===-- NativeProcessELF.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 "NativeProcessELF.h"
+
+#include "lldb/Utility/DataExtractor.h"
+
+namespace lldb_private {
+
+llvm::Optional<uint64_t>
+NativeProcessELF::GetAuxValue(enum AuxVector::EntryType type) {
+ if (m_aux_vector == nullptr) {
+ auto buffer_or_error = GetAuxvData();
+ if (!buffer_or_error)
+ return llvm::None;
+ DataExtractor auxv_data(buffer_or_error.get()->getBufferStart(),
+ buffer_or_error.get()->getBufferSize(),
+ GetByteOrder(), GetAddressByteSize());
+ m_aux_vector = llvm::make_unique<AuxVector>(auxv_data);
+ }
+
+ return m_aux_vector->GetAuxValue(type);
+}
+
+lldb::addr_t NativeProcessELF::GetSharedLibraryInfoAddress() {
+ if (!m_shared_library_info_addr.hasValue()) {
+ if (GetAddressByteSize() == 8)
+ m_shared_library_info_addr =
+ GetELFImageInfoAddress<llvm::ELF::Elf64_Ehdr, llvm::ELF::Elf64_Phdr,
+ llvm::ELF::Elf64_Dyn>();
+ else
+ m_shared_library_info_addr =
+ GetELFImageInfoAddress<llvm::ELF::Elf32_Ehdr, llvm::ELF::Elf32_Phdr,
+ llvm::ELF::Elf32_Dyn>();
+ }
+
+ return m_shared_library_info_addr.getValue();
+}
+
+template <typename ELF_EHDR, typename ELF_PHDR, typename ELF_DYN>
+lldb::addr_t NativeProcessELF::GetELFImageInfoAddress() {
+ llvm::Optional<uint64_t> maybe_phdr_addr =
+ GetAuxValue(AuxVector::AUXV_AT_PHDR);
+ llvm::Optional<uint64_t> maybe_phdr_entry_size =
+ GetAuxValue(AuxVector::AUXV_AT_PHENT);
+ llvm::Optional<uint64_t> maybe_phdr_num_entries =
+ GetAuxValue(AuxVector::AUXV_AT_PHNUM);
+ if (!maybe_phdr_addr || !maybe_phdr_entry_size || !maybe_phdr_num_entries)
+ return LLDB_INVALID_ADDRESS;
+ lldb::addr_t phdr_addr = *maybe_phdr_addr;
+ size_t phdr_entry_size = *maybe_phdr_entry_size;
+ size_t phdr_num_entries = *maybe_phdr_num_entries;
+
+ // Find the PT_DYNAMIC segment (.dynamic section) in the program header and
+ // what the load bias by calculating the difference of the program header
+ // load address and its virtual address.
+ lldb::offset_t load_bias;
+ bool found_load_bias = false;
+ lldb::addr_t dynamic_section_addr = 0;
+ uint64_t dynamic_section_size = 0;
+ bool found_dynamic_section = false;
+ ELF_PHDR phdr_entry;
+ for (size_t i = 0; i < phdr_num_entries; i++) {
+ size_t bytes_read;
+ auto error = ReadMemory(phdr_addr + i * phdr_entry_size, &phdr_entry,
+ sizeof(phdr_entry), bytes_read);
+ if (!error.Success())
+ return LLDB_INVALID_ADDRESS;
+ if (phdr_entry.p_type == llvm::ELF::PT_PHDR) {
+ load_bias = phdr_addr - phdr_entry.p_vaddr;
+ found_load_bias = true;
+ }
+
+ if (phdr_entry.p_type == llvm::ELF::PT_DYNAMIC) {
+ dynamic_section_addr = phdr_entry.p_vaddr;
+ dynamic_section_size = phdr_entry.p_memsz;
+ found_dynamic_section = true;
+ }
+ }
+
+ if (!found_load_bias || !found_dynamic_section)
+ return LLDB_INVALID_ADDRESS;
+
+ // Find the DT_DEBUG entry in the .dynamic section
+ dynamic_section_addr += load_bias;
+ ELF_DYN dynamic_entry;
+ size_t dynamic_num_entries = dynamic_section_size / sizeof(dynamic_entry);
+ for (size_t i = 0; i < dynamic_num_entries; i++) {
+ size_t bytes_read;
+ auto error = ReadMemory(dynamic_section_addr + i * sizeof(dynamic_entry),
+ &dynamic_entry, sizeof(dynamic_entry), bytes_read);
+ if (!error.Success())
+ return LLDB_INVALID_ADDRESS;
+ // Return the &DT_DEBUG->d_ptr which points to r_debug which contains the
+ // link_map.
+ if (dynamic_entry.d_tag == llvm::ELF::DT_DEBUG) {
+ return dynamic_section_addr + i * sizeof(dynamic_entry) +
+ sizeof(dynamic_entry.d_tag);
+ }
+ }
+
+ return LLDB_INVALID_ADDRESS;
+}
+
+} // namespace lldb_private \ No newline at end of file
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/NativeProcessELF.h b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/NativeProcessELF.h
new file mode 100644
index 000000000000..84dc8d08a340
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/NativeProcessELF.h
@@ -0,0 +1,46 @@
+//===-- NativeProcessELF.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 liblldb_NativeProcessELF_H_
+#define liblldb_NativeProcessELF_H_
+
+#include "Plugins/Process/Utility/AuxVector.h"
+#include "lldb/Host/common/NativeProcessProtocol.h"
+#include "llvm/BinaryFormat/ELF.h"
+
+namespace lldb_private {
+
+/// \class NativeProcessELF
+/// Abstract class that extends \a NativeProcessProtocol with ELF specific
+/// logic. Meant to be subclassed by ELF based NativeProcess* implementations.
+class NativeProcessELF : public NativeProcessProtocol {
+ using NativeProcessProtocol::NativeProcessProtocol;
+
+protected:
+ template <typename T> struct ELFLinkMap {
+ T l_addr;
+ T l_name;
+ T l_ld;
+ T l_next;
+ T l_prev;
+ };
+
+ llvm::Optional<uint64_t> GetAuxValue(enum AuxVector::EntryType type);
+
+ lldb::addr_t GetSharedLibraryInfoAddress() override;
+
+ template <typename ELF_EHDR, typename ELF_PHDR, typename ELF_DYN>
+ lldb::addr_t GetELFImageInfoAddress();
+
+ std::unique_ptr<AuxVector> m_aux_vector;
+ llvm::Optional<lldb::addr_t> m_shared_library_info_addr;
+};
+
+} // namespace lldb_private
+
+#endif \ No newline at end of file
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessMessage.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessMessage.cpp
new file mode 100644
index 000000000000..aa8449131a68
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessMessage.cpp
@@ -0,0 +1,68 @@
+//===-- ProcessMessage.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 "ProcessMessage.h"
+
+using namespace lldb_private;
+
+const char *ProcessMessage::PrintCrashReason() const {
+ return CrashReasonAsString(m_crash_reason);
+}
+
+const char *ProcessMessage::PrintKind(Kind kind) {
+#ifdef LLDB_CONFIGURATION_BUILDANDINTEGRATION
+ // Just return the code in ascii for integration builds.
+ chcar str[8];
+ sprintf(str, "%d", reason);
+#else
+ const char *str = nullptr;
+
+ switch (kind) {
+ case eInvalidMessage:
+ str = "eInvalidMessage";
+ break;
+ case eAttachMessage:
+ str = "eAttachMessage";
+ break;
+ case eExitMessage:
+ str = "eExitMessage";
+ break;
+ case eLimboMessage:
+ str = "eLimboMessage";
+ break;
+ case eSignalMessage:
+ str = "eSignalMessage";
+ break;
+ case eSignalDeliveredMessage:
+ str = "eSignalDeliveredMessage";
+ break;
+ case eTraceMessage:
+ str = "eTraceMessage";
+ break;
+ case eBreakpointMessage:
+ str = "eBreakpointMessage";
+ break;
+ case eWatchpointMessage:
+ str = "eWatchpointMessage";
+ break;
+ case eCrashMessage:
+ str = "eCrashMessage";
+ break;
+ case eNewThreadMessage:
+ str = "eNewThreadMessage";
+ break;
+ case eExecMessage:
+ str = "eExecMessage";
+ break;
+ }
+#endif
+
+ return str;
+}
+
+const char *ProcessMessage::PrintKind() const { return PrintKind(m_kind); }
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessMessage.h b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessMessage.h
new file mode 100644
index 000000000000..d9c10caaa95e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessMessage.h
@@ -0,0 +1,168 @@
+//===-- ProcessMessage.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 liblldb_ProcessMessage_H_
+#define liblldb_ProcessMessage_H_
+
+#include "CrashReason.h"
+
+#include <cassert>
+#include <string>
+
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-types.h"
+
+class ProcessMessage {
+public:
+ /// The type of signal this message can correspond to.
+ enum Kind {
+ eInvalidMessage,
+ eAttachMessage,
+ eExitMessage,
+ eLimboMessage,
+ eSignalMessage,
+ eSignalDeliveredMessage,
+ eTraceMessage,
+ eBreakpointMessage,
+ eWatchpointMessage,
+ eCrashMessage,
+ eNewThreadMessage,
+ eExecMessage
+ };
+
+ ProcessMessage()
+ : m_tid(LLDB_INVALID_PROCESS_ID), m_kind(eInvalidMessage),
+ m_crash_reason(CrashReason::eInvalidCrashReason), m_status(0),
+ m_addr(0) {}
+
+ Kind GetKind() const { return m_kind; }
+
+ lldb::tid_t GetTID() const { return m_tid; }
+
+ /// Indicates that the process \p pid has successfully attached.
+ static ProcessMessage Attach(lldb::pid_t pid) {
+ return ProcessMessage(pid, eAttachMessage);
+ }
+
+ /// Indicates that the thread \p tid is about to exit with status \p status.
+ static ProcessMessage Limbo(lldb::tid_t tid, int status) {
+ return ProcessMessage(tid, eLimboMessage, status);
+ }
+
+ /// Indicates that the thread \p tid had the signal \p signum delivered.
+ static ProcessMessage Signal(lldb::tid_t tid, int signum) {
+ return ProcessMessage(tid, eSignalMessage, signum);
+ }
+
+ /// Indicates that a signal \p signum generated by the debugging process was
+ /// delivered to the thread \p tid.
+ static ProcessMessage SignalDelivered(lldb::tid_t tid, int signum) {
+ return ProcessMessage(tid, eSignalDeliveredMessage, signum);
+ }
+
+ /// Indicates that the thread \p tid encountered a trace point.
+ static ProcessMessage Trace(lldb::tid_t tid) {
+ return ProcessMessage(tid, eTraceMessage);
+ }
+
+ /// Indicates that the thread \p tid encountered a break point.
+ static ProcessMessage Break(lldb::tid_t tid) {
+ return ProcessMessage(tid, eBreakpointMessage);
+ }
+
+ static ProcessMessage Watch(lldb::tid_t tid, lldb::addr_t wp_addr) {
+ return ProcessMessage(tid, eWatchpointMessage, 0, wp_addr);
+ }
+
+ /// Indicates that the thread \p tid crashed.
+ static ProcessMessage Crash(lldb::pid_t pid, CrashReason reason, int signo,
+ lldb::addr_t fault_addr) {
+ ProcessMessage message(pid, eCrashMessage, signo, fault_addr);
+ message.m_crash_reason = reason;
+ return message;
+ }
+
+ /// Indicates that the thread \p child_tid was spawned.
+ static ProcessMessage NewThread(lldb::tid_t parent_tid,
+ lldb::tid_t child_tid) {
+ return ProcessMessage(parent_tid, eNewThreadMessage, child_tid);
+ }
+
+ /// Indicates that the thread \p tid is about to exit with status \p status.
+ static ProcessMessage Exit(lldb::tid_t tid, int status) {
+ return ProcessMessage(tid, eExitMessage, status);
+ }
+
+ /// Indicates that the thread \p pid has exec'd.
+ static ProcessMessage Exec(lldb::tid_t tid) {
+ return ProcessMessage(tid, eExecMessage);
+ }
+
+ int GetExitStatus() const {
+ assert(GetKind() == eExitMessage || GetKind() == eLimboMessage);
+ return m_status;
+ }
+
+ int GetSignal() const {
+ assert(GetKind() == eSignalMessage || GetKind() == eCrashMessage ||
+ GetKind() == eSignalDeliveredMessage);
+ return m_status;
+ }
+
+ int GetStopStatus() const {
+ assert(GetKind() == eSignalMessage);
+ return m_status;
+ }
+
+ CrashReason GetCrashReason() const {
+ assert(GetKind() == eCrashMessage);
+ return m_crash_reason;
+ }
+
+ lldb::addr_t GetFaultAddress() const {
+ assert(GetKind() == eCrashMessage);
+ return m_addr;
+ }
+
+ lldb::addr_t GetHWAddress() const {
+ assert(GetKind() == eWatchpointMessage || GetKind() == eTraceMessage);
+ return m_addr;
+ }
+
+ lldb::tid_t GetChildTID() const {
+ assert(GetKind() == eNewThreadMessage);
+ return m_child_tid;
+ }
+
+ const char *PrintCrashReason() const;
+
+ const char *PrintKind() const;
+
+ static const char *PrintKind(Kind);
+
+private:
+ ProcessMessage(lldb::tid_t tid, Kind kind, int status = 0,
+ lldb::addr_t addr = 0)
+ : m_tid(tid), m_kind(kind),
+ m_crash_reason(CrashReason::eInvalidCrashReason), m_status(status),
+ m_addr(addr), m_child_tid(0) {}
+
+ ProcessMessage(lldb::tid_t tid, Kind kind, lldb::tid_t child_tid)
+ : m_tid(tid), m_kind(kind),
+ m_crash_reason(CrashReason::eInvalidCrashReason), m_status(0),
+ m_addr(0), m_child_tid(child_tid) {}
+
+ lldb::tid_t m_tid;
+ Kind m_kind : 8;
+ CrashReason m_crash_reason;
+ int m_status;
+ lldb::addr_t m_addr;
+ lldb::tid_t m_child_tid;
+};
+
+#endif // #ifndef liblldb_ProcessMessage_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp
new file mode 100644
index 000000000000..a17558bfe7c6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp
@@ -0,0 +1,31 @@
+//===-- ProcessPOSIXLog.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 "ProcessPOSIXLog.h"
+
+#include "llvm/Support/Threading.h"
+
+using namespace lldb_private;
+
+static constexpr Log::Category g_categories[] = {
+ {{"break"}, {"log breakpoints"}, POSIX_LOG_BREAKPOINTS},
+ {{"memory"}, {"log memory reads and writes"}, POSIX_LOG_MEMORY},
+ {{"process"}, {"log process events and activities"}, POSIX_LOG_PROCESS},
+ {{"ptrace"}, {"log all calls to ptrace"}, POSIX_LOG_PTRACE},
+ {{"registers"}, {"log register read/writes"}, POSIX_LOG_REGISTERS},
+ {{"thread"}, {"log thread events and activities"}, POSIX_LOG_THREAD},
+ {{"watch"}, {"log watchpoint related activities"}, POSIX_LOG_WATCHPOINTS},
+};
+
+Log::Channel ProcessPOSIXLog::g_channel(g_categories, POSIX_LOG_DEFAULT);
+
+void ProcessPOSIXLog::Initialize() {
+ static llvm::once_flag g_once_flag;
+ llvm::call_once(g_once_flag, []() { Log::Register("posix", g_channel); });
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.h b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.h
new file mode 100644
index 000000000000..c0147c43410f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.h
@@ -0,0 +1,39 @@
+//===-- ProcessPOSIXLog.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 liblldb_ProcessPOSIXLog_h_
+#define liblldb_ProcessPOSIXLog_h_
+
+
+#include "lldb/Utility/Log.h"
+
+#define POSIX_LOG_PROCESS (1u << 1)
+#define POSIX_LOG_THREAD (1u << 2)
+#define POSIX_LOG_MEMORY (1u << 4) // Log memory reads/writes calls
+#define POSIX_LOG_PTRACE (1u << 5)
+#define POSIX_LOG_REGISTERS (1u << 6)
+#define POSIX_LOG_BREAKPOINTS (1u << 7)
+#define POSIX_LOG_WATCHPOINTS (1u << 8)
+#define POSIX_LOG_ALL (UINT32_MAX)
+#define POSIX_LOG_DEFAULT POSIX_LOG_PROCESS
+
+namespace lldb_private {
+class ProcessPOSIXLog {
+ static Log::Channel g_channel;
+
+public:
+ static void Initialize();
+
+ static Log *GetLogIfAllCategoriesSet(uint32_t mask) {
+ return g_channel.GetLogIfAll(mask);
+ }
+};
+}
+
+#endif // liblldb_ProcessPOSIXLog_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ARMDefines.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ARMDefines.h
new file mode 100644
index 000000000000..1f7eb54d10e7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ARMDefines.h
@@ -0,0 +1,191 @@
+//===-- ARMDefines.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_ARMDefines_h_
+#define lldb_ARMDefines_h_
+
+#include "llvm/Support/ErrorHandling.h"
+
+#include <cassert>
+#include <cstdint>
+
+// Common definitions for the ARM/Thumb Instruction Set Architecture.
+
+namespace lldb_private {
+
+// ARM shifter types
+enum ARM_ShifterType {
+ SRType_LSL,
+ SRType_LSR,
+ SRType_ASR,
+ SRType_ROR,
+ SRType_RRX,
+ SRType_Invalid
+};
+
+// ARM conditions // Meaning (integer) Meaning (floating-point)
+// Condition flags
+#define COND_EQ \
+ 0x0 // Equal Equal Z == 1
+#define COND_NE \
+ 0x1 // Not equal Not equal, or unordered Z == 0
+#define COND_CS \
+ 0x2 // Carry set >, ==, or unordered C == 1
+#define COND_HS 0x2
+#define COND_CC \
+ 0x3 // Carry clear Less than C == 0
+#define COND_LO 0x3
+#define COND_MI \
+ 0x4 // Minus, negative Less than N == 1
+#define COND_PL \
+ 0x5 // Plus, positive or zero >, ==, or unordered N == 0
+#define COND_VS \
+ 0x6 // Overflow Unordered V == 1
+#define COND_VC \
+ 0x7 // No overflow Not unordered V == 0
+#define COND_HI \
+ 0x8 // Unsigned higher Greater than, or unordered C == 1 and Z ==
+ // 0
+#define COND_LS \
+ 0x9 // Unsigned lower or same Less than or equal C == 0 or Z ==
+ // 1
+#define COND_GE \
+ 0xA // Greater than or equal Greater than or equal N == V
+#define COND_LT \
+ 0xB // Less than Less than, or unordered N != V
+#define COND_GT \
+ 0xC // Greater than Greater than Z == 0 and N ==
+ // V
+#define COND_LE \
+ 0xD // Less than or equal <, ==, or unordered Z == 1 or N !=
+ // V
+#define COND_AL \
+ 0xE // Always (unconditional) Always (unconditional) Any
+#define COND_UNCOND 0xF
+
+static inline const char *ARMCondCodeToString(uint32_t CC) {
+ switch (CC) {
+ case COND_EQ:
+ return "eq";
+ case COND_NE:
+ return "ne";
+ case COND_HS:
+ return "hs";
+ case COND_LO:
+ return "lo";
+ case COND_MI:
+ return "mi";
+ case COND_PL:
+ return "pl";
+ case COND_VS:
+ return "vs";
+ case COND_VC:
+ return "vc";
+ case COND_HI:
+ return "hi";
+ case COND_LS:
+ return "ls";
+ case COND_GE:
+ return "ge";
+ case COND_LT:
+ return "lt";
+ case COND_GT:
+ return "gt";
+ case COND_LE:
+ return "le";
+ case COND_AL:
+ return "al";
+ }
+ llvm_unreachable("Unknown condition code");
+}
+
+static inline bool ARMConditionPassed(const uint32_t condition,
+ const uint32_t cpsr) {
+ const uint32_t cpsr_n = (cpsr >> 31) & 1u; // Negative condition code flag
+ const uint32_t cpsr_z = (cpsr >> 30) & 1u; // Zero condition code flag
+ const uint32_t cpsr_c = (cpsr >> 29) & 1u; // Carry condition code flag
+ const uint32_t cpsr_v = (cpsr >> 28) & 1u; // Overflow condition code flag
+
+ switch (condition) {
+ case COND_EQ:
+ return (cpsr_z == 1);
+ case COND_NE:
+ return (cpsr_z == 0);
+ case COND_CS:
+ return (cpsr_c == 1);
+ case COND_CC:
+ return (cpsr_c == 0);
+ case COND_MI:
+ return (cpsr_n == 1);
+ case COND_PL:
+ return (cpsr_n == 0);
+ case COND_VS:
+ return (cpsr_v == 1);
+ case COND_VC:
+ return (cpsr_v == 0);
+ case COND_HI:
+ return ((cpsr_c == 1) && (cpsr_z == 0));
+ case COND_LS:
+ return ((cpsr_c == 0) || (cpsr_z == 1));
+ case COND_GE:
+ return (cpsr_n == cpsr_v);
+ case COND_LT:
+ return (cpsr_n != cpsr_v);
+ case COND_GT:
+ return ((cpsr_z == 0) && (cpsr_n == cpsr_v));
+ case COND_LE:
+ return ((cpsr_z == 1) || (cpsr_n != cpsr_v));
+ case COND_AL:
+ case COND_UNCOND:
+ default:
+ return true;
+ }
+ return false;
+}
+
+// Bit positions for CPSR
+#define CPSR_T_POS 5
+#define CPSR_F_POS 6
+#define CPSR_I_POS 7
+#define CPSR_A_POS 8
+#define CPSR_E_POS 9
+#define CPSR_J_POS 24
+#define CPSR_Q_POS 27
+#define CPSR_V_POS 28
+#define CPSR_C_POS 29
+#define CPSR_Z_POS 30
+#define CPSR_N_POS 31
+
+// CPSR mode definitions
+#define CPSR_MODE_USR 0x10u
+#define CPSR_MODE_FIQ 0x11u
+#define CPSR_MODE_IRQ 0x12u
+#define CPSR_MODE_SVC 0x13u
+#define CPSR_MODE_ABT 0x17u
+#define CPSR_MODE_UND 0x1bu
+#define CPSR_MODE_SYS 0x1fu
+
+// Masks for CPSR
+#define MASK_CPSR_MODE_MASK (0x0000001fu)
+#define MASK_CPSR_IT_MASK (0x0600fc00u)
+#define MASK_CPSR_T (1u << CPSR_T_POS)
+#define MASK_CPSR_F (1u << CPSR_F_POS)
+#define MASK_CPSR_I (1u << CPSR_I_POS)
+#define MASK_CPSR_A (1u << CPSR_A_POS)
+#define MASK_CPSR_E (1u << CPSR_E_POS)
+#define MASK_CPSR_GE_MASK (0x000f0000u)
+#define MASK_CPSR_J (1u << CPSR_J_POS)
+#define MASK_CPSR_Q (1u << CPSR_Q_POS)
+#define MASK_CPSR_V (1u << CPSR_V_POS)
+#define MASK_CPSR_C (1u << CPSR_C_POS)
+#define MASK_CPSR_Z (1u << CPSR_Z_POS)
+#define MASK_CPSR_N (1u << CPSR_N_POS)
+
+} // namespace lldb_private
+
+#endif // lldb_ARMDefines_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ARMUtils.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ARMUtils.h
new file mode 100644
index 000000000000..d860348818d3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ARMUtils.h
@@ -0,0 +1,374 @@
+//===-- ARMUtils.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_ARMUtils_h_
+#define lldb_ARMUtils_h_
+
+#include "ARMDefines.h"
+#include "InstructionUtils.h"
+#include "llvm/Support/MathExtras.h"
+
+// Common utilities for the ARM/Thumb Instruction Set Architecture.
+
+namespace lldb_private {
+
+static inline uint32_t Align(uint32_t val, uint32_t alignment) {
+ return alignment * (val / alignment);
+}
+
+static inline uint32_t DecodeImmShift(const uint32_t type, const uint32_t imm5,
+ ARM_ShifterType &shift_t) {
+ switch (type) {
+ default:
+ // assert(0 && "Invalid shift type");
+ case 0:
+ shift_t = SRType_LSL;
+ return imm5;
+ case 1:
+ shift_t = SRType_LSR;
+ return (imm5 == 0 ? 32 : imm5);
+ case 2:
+ shift_t = SRType_ASR;
+ return (imm5 == 0 ? 32 : imm5);
+ case 3:
+ if (imm5 == 0) {
+ shift_t = SRType_RRX;
+ return 1;
+ } else {
+ shift_t = SRType_ROR;
+ return imm5;
+ }
+ }
+ shift_t = SRType_Invalid;
+ return UINT32_MAX;
+}
+
+// A8.6.35 CMP (register) -- Encoding T3
+// Convenience function.
+static inline uint32_t DecodeImmShiftThumb(const uint32_t opcode,
+ ARM_ShifterType &shift_t) {
+ return DecodeImmShift(Bits32(opcode, 5, 4),
+ Bits32(opcode, 14, 12) << 2 | Bits32(opcode, 7, 6),
+ shift_t);
+}
+
+// A8.6.35 CMP (register) -- Encoding A1
+// Convenience function.
+static inline uint32_t DecodeImmShiftARM(const uint32_t opcode,
+ ARM_ShifterType &shift_t) {
+ return DecodeImmShift(Bits32(opcode, 6, 5), Bits32(opcode, 11, 7), shift_t);
+}
+
+static inline uint32_t DecodeImmShift(const ARM_ShifterType shift_t,
+ const uint32_t imm5) {
+ ARM_ShifterType dont_care;
+ return DecodeImmShift(shift_t, imm5, dont_care);
+}
+
+static inline ARM_ShifterType DecodeRegShift(const uint32_t type) {
+ switch (type) {
+ default:
+ // assert(0 && "Invalid shift type");
+ return SRType_Invalid;
+ case 0:
+ return SRType_LSL;
+ case 1:
+ return SRType_LSR;
+ case 2:
+ return SRType_ASR;
+ case 3:
+ return SRType_ROR;
+ }
+}
+
+static inline uint32_t LSL_C(const uint32_t value, const uint32_t amount,
+ uint32_t &carry_out, bool *success) {
+ if (amount == 0) {
+ *success = false;
+ return 0;
+ }
+ *success = true;
+ carry_out = amount <= 32 ? Bit32(value, 32 - amount) : 0;
+ return value << amount;
+}
+
+static inline uint32_t LSL(const uint32_t value, const uint32_t amount,
+ bool *success) {
+ *success = true;
+ if (amount == 0)
+ return value;
+ uint32_t dont_care;
+ uint32_t result = LSL_C(value, amount, dont_care, success);
+ if (*success)
+ return result;
+ else
+ return 0;
+}
+
+static inline uint32_t LSR_C(const uint32_t value, const uint32_t amount,
+ uint32_t &carry_out, bool *success) {
+ if (amount == 0) {
+ *success = false;
+ return 0;
+ }
+ *success = true;
+ carry_out = amount <= 32 ? Bit32(value, amount - 1) : 0;
+ return value >> amount;
+}
+
+static inline uint32_t LSR(const uint32_t value, const uint32_t amount,
+ bool *success) {
+ *success = true;
+ if (amount == 0)
+ return value;
+ uint32_t dont_care;
+ uint32_t result = LSR_C(value, amount, dont_care, success);
+ if (*success)
+ return result;
+ else
+ return 0;
+}
+
+static inline uint32_t ASR_C(const uint32_t value, const uint32_t amount,
+ uint32_t &carry_out, bool *success) {
+ if (amount == 0 || amount > 32) {
+ *success = false;
+ return 0;
+ }
+ *success = true;
+ bool negative = BitIsSet(value, 31);
+ if (amount <= 32) {
+ carry_out = Bit32(value, amount - 1);
+ int64_t extended = llvm::SignExtend64<32>(value);
+ return UnsignedBits(extended, amount + 31, amount);
+ } else {
+ carry_out = (negative ? 1 : 0);
+ return (negative ? 0xffffffff : 0);
+ }
+}
+
+static inline uint32_t ASR(const uint32_t value, const uint32_t amount,
+ bool *success) {
+ *success = true;
+ if (amount == 0)
+ return value;
+ uint32_t dont_care;
+ uint32_t result = ASR_C(value, amount, dont_care, success);
+ if (*success)
+ return result;
+ else
+ return 0;
+}
+
+static inline uint32_t ROR_C(const uint32_t value, const uint32_t amount,
+ uint32_t &carry_out, bool *success) {
+ if (amount == 0) {
+ *success = false;
+ return 0;
+ }
+ *success = true;
+ uint32_t amt = amount % 32;
+ uint32_t result = Rotr32(value, amt);
+ carry_out = Bit32(value, 31);
+ return result;
+}
+
+static inline uint32_t ROR(const uint32_t value, const uint32_t amount,
+ bool *success) {
+ *success = true;
+ if (amount == 0)
+ return value;
+ uint32_t dont_care;
+ uint32_t result = ROR_C(value, amount, dont_care, success);
+ if (*success)
+ return result;
+ else
+ return 0;
+}
+
+static inline uint32_t RRX_C(const uint32_t value, const uint32_t carry_in,
+ uint32_t &carry_out, bool *success) {
+ *success = true;
+ carry_out = Bit32(value, 0);
+ return Bit32(carry_in, 0) << 31 | Bits32(value, 31, 1);
+}
+
+static inline uint32_t RRX(const uint32_t value, const uint32_t carry_in,
+ bool *success) {
+ *success = true;
+ uint32_t dont_care;
+ uint32_t result = RRX_C(value, carry_in, dont_care, success);
+ if (*success)
+ return result;
+ else
+ return 0;
+}
+
+static inline uint32_t Shift_C(const uint32_t value, ARM_ShifterType type,
+ const uint32_t amount, const uint32_t carry_in,
+ uint32_t &carry_out, bool *success) {
+ if (type == SRType_RRX && amount != 1) {
+ *success = false;
+ return 0;
+ }
+ *success = true;
+
+ if (amount == 0) {
+ carry_out = carry_in;
+ return value;
+ }
+ uint32_t result;
+ switch (type) {
+ case SRType_LSL:
+ result = LSL_C(value, amount, carry_out, success);
+ break;
+ case SRType_LSR:
+ result = LSR_C(value, amount, carry_out, success);
+ break;
+ case SRType_ASR:
+ result = ASR_C(value, amount, carry_out, success);
+ break;
+ case SRType_ROR:
+ result = ROR_C(value, amount, carry_out, success);
+ break;
+ case SRType_RRX:
+ result = RRX_C(value, carry_in, carry_out, success);
+ break;
+ default:
+ *success = false;
+ break;
+ }
+ if (*success)
+ return result;
+ else
+ return 0;
+}
+
+static inline uint32_t Shift(const uint32_t value, ARM_ShifterType type,
+ const uint32_t amount, const uint32_t carry_in,
+ bool *success) {
+ // Don't care about carry out in this case.
+ uint32_t dont_care;
+ uint32_t result = Shift_C(value, type, amount, carry_in, dont_care, success);
+ if (*success)
+ return result;
+ else
+ return 0;
+}
+
+static inline uint32_t bits(const uint32_t val, const uint32_t msbit,
+ const uint32_t lsbit) {
+ return Bits32(val, msbit, lsbit);
+}
+
+static inline uint32_t bit(const uint32_t val, const uint32_t msbit) {
+ return bits(val, msbit, msbit);
+}
+
+static uint32_t ror(uint32_t val, uint32_t N, uint32_t shift) {
+ uint32_t m = shift % N;
+ return (val >> m) | (val << (N - m));
+}
+
+// (imm32, carry_out) = ARMExpandImm_C(imm12, carry_in)
+static inline uint32_t ARMExpandImm_C(uint32_t opcode, uint32_t carry_in,
+ uint32_t &carry_out) {
+ uint32_t imm32; // the expanded result
+ uint32_t imm = bits(opcode, 7, 0); // immediate value
+ uint32_t amt = 2 * bits(opcode, 11, 8); // rotate amount
+ if (amt == 0) {
+ imm32 = imm;
+ carry_out = carry_in;
+ } else {
+ imm32 = ror(imm, 32, amt);
+ carry_out = Bit32(imm32, 31);
+ }
+ return imm32;
+}
+
+static inline uint32_t ARMExpandImm(uint32_t opcode) {
+ // 'carry_in' argument to following function call does not affect the imm32
+ // result.
+ uint32_t carry_in = 0;
+ uint32_t carry_out;
+ return ARMExpandImm_C(opcode, carry_in, carry_out);
+}
+
+// (imm32, carry_out) = ThumbExpandImm_C(imm12, carry_in)
+static inline uint32_t ThumbExpandImm_C(uint32_t opcode, uint32_t carry_in,
+ uint32_t &carry_out) {
+ uint32_t imm32; // the expanded result
+ const uint32_t i = bit(opcode, 26);
+ const uint32_t imm3 = bits(opcode, 14, 12);
+ const uint32_t abcdefgh = bits(opcode, 7, 0);
+ const uint32_t imm12 = i << 11 | imm3 << 8 | abcdefgh;
+
+ if (bits(imm12, 11, 10) == 0) {
+ switch (bits(imm12, 9, 8)) {
+ default: // Keep static analyzer happy with a default case
+ case 0:
+ imm32 = abcdefgh;
+ break;
+
+ case 1:
+ imm32 = abcdefgh << 16 | abcdefgh;
+ break;
+
+ case 2:
+ imm32 = abcdefgh << 24 | abcdefgh << 8;
+ break;
+
+ case 3:
+ imm32 = abcdefgh << 24 | abcdefgh << 16 | abcdefgh << 8 | abcdefgh;
+ break;
+ }
+ carry_out = carry_in;
+ } else {
+ const uint32_t unrotated_value = 0x80 | bits(imm12, 6, 0);
+ imm32 = ror(unrotated_value, 32, bits(imm12, 11, 7));
+ carry_out = Bit32(imm32, 31);
+ }
+ return imm32;
+}
+
+static inline uint32_t ThumbExpandImm(uint32_t opcode) {
+ // 'carry_in' argument to following function call does not affect the imm32
+ // result.
+ uint32_t carry_in = 0;
+ uint32_t carry_out;
+ return ThumbExpandImm_C(opcode, carry_in, carry_out);
+}
+
+// imm32 = ZeroExtend(i:imm3:imm8, 32)
+static inline uint32_t ThumbImm12(uint32_t opcode) {
+ const uint32_t i = bit(opcode, 26);
+ const uint32_t imm3 = bits(opcode, 14, 12);
+ const uint32_t imm8 = bits(opcode, 7, 0);
+ const uint32_t imm12 = i << 11 | imm3 << 8 | imm8;
+ return imm12;
+}
+
+// imm32 = ZeroExtend(imm7:'00', 32)
+static inline uint32_t ThumbImm7Scaled(uint32_t opcode) {
+ const uint32_t imm7 = bits(opcode, 6, 0);
+ return imm7 * 4;
+}
+
+// imm32 = ZeroExtend(imm8:'00', 32)
+static inline uint32_t ThumbImm8Scaled(uint32_t opcode) {
+ const uint32_t imm8 = bits(opcode, 7, 0);
+ return imm8 * 4;
+}
+
+// This function performs the check for the register numbers 13 and 15 that are
+// not permitted for many Thumb register specifiers.
+static inline bool BadReg(uint32_t n) { return n == 13 || n == 15; }
+
+} // namespace lldb_private
+
+#endif // lldb_ARMUtils_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/AuxVector.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/AuxVector.cpp
new file mode 100644
index 000000000000..aab164ff93a6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/AuxVector.cpp
@@ -0,0 +1,96 @@
+//===-- AuxVector.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 "AuxVector.h"
+
+AuxVector::AuxVector(const lldb_private::DataExtractor &data) {
+ ParseAuxv(data);
+}
+
+void AuxVector::ParseAuxv(const lldb_private::DataExtractor &data) {
+ lldb::offset_t offset = 0;
+ const size_t value_type_size = data.GetAddressByteSize() * 2;
+ while (data.ValidOffsetForDataOfSize(offset, value_type_size)) {
+ // We're not reading an address but an int that could be 32 or 64 bit
+ // depending on the address size, which is what GetAddress does.
+ const uint64_t type = data.GetAddress(&offset);
+ const uint64_t value = data.GetAddress(&offset);
+ if (type == AUXV_AT_NULL)
+ break;
+ if (type == AUXV_AT_IGNORE)
+ continue;
+
+ m_auxv_entries[type] = value;
+ }
+}
+
+llvm::Optional<uint64_t>
+AuxVector::GetAuxValue(enum EntryType entry_type) const {
+ auto it = m_auxv_entries.find(static_cast<uint64_t>(entry_type));
+ if (it != m_auxv_entries.end())
+ return it->second;
+ return llvm::None;
+}
+
+void AuxVector::DumpToLog(lldb_private::Log *log) const {
+ if (!log)
+ return;
+
+ log->PutCString("AuxVector: ");
+ for (auto entry : m_auxv_entries) {
+ log->Printf(" %s [%" PRIu64 "]: %" PRIx64,
+ GetEntryName(static_cast<EntryType>(entry.first)), entry.first,
+ entry.second);
+ }
+}
+
+const char *AuxVector::GetEntryName(EntryType type) const {
+ const char *name = "AT_???";
+
+#define ENTRY_NAME(_type) \
+ _type: \
+ name = &#_type[5]
+ switch (type) {
+ case ENTRY_NAME(AUXV_AT_NULL); break;
+ case ENTRY_NAME(AUXV_AT_IGNORE); break;
+ case ENTRY_NAME(AUXV_AT_EXECFD); break;
+ case ENTRY_NAME(AUXV_AT_PHDR); break;
+ case ENTRY_NAME(AUXV_AT_PHENT); break;
+ case ENTRY_NAME(AUXV_AT_PHNUM); break;
+ case ENTRY_NAME(AUXV_AT_PAGESZ); break;
+ case ENTRY_NAME(AUXV_AT_BASE); break;
+ case ENTRY_NAME(AUXV_AT_FLAGS); break;
+ case ENTRY_NAME(AUXV_AT_ENTRY); break;
+ case ENTRY_NAME(AUXV_AT_NOTELF); break;
+ case ENTRY_NAME(AUXV_AT_UID); break;
+ case ENTRY_NAME(AUXV_AT_EUID); break;
+ case ENTRY_NAME(AUXV_AT_GID); break;
+ case ENTRY_NAME(AUXV_AT_EGID); break;
+ case ENTRY_NAME(AUXV_AT_CLKTCK); break;
+ case ENTRY_NAME(AUXV_AT_PLATFORM); break;
+ case ENTRY_NAME(AUXV_AT_HWCAP); break;
+ case ENTRY_NAME(AUXV_AT_FPUCW); break;
+ case ENTRY_NAME(AUXV_AT_DCACHEBSIZE); break;
+ case ENTRY_NAME(AUXV_AT_ICACHEBSIZE); break;
+ case ENTRY_NAME(AUXV_AT_UCACHEBSIZE); break;
+ case ENTRY_NAME(AUXV_AT_IGNOREPPC); break;
+ case ENTRY_NAME(AUXV_AT_SECURE); break;
+ case ENTRY_NAME(AUXV_AT_BASE_PLATFORM); break;
+ case ENTRY_NAME(AUXV_AT_RANDOM); break;
+ case ENTRY_NAME(AUXV_AT_EXECFN); break;
+ case ENTRY_NAME(AUXV_AT_SYSINFO); break;
+ case ENTRY_NAME(AUXV_AT_SYSINFO_EHDR); break;
+ case ENTRY_NAME(AUXV_AT_L1I_CACHESHAPE); break;
+ case ENTRY_NAME(AUXV_AT_L1D_CACHESHAPE); break;
+ case ENTRY_NAME(AUXV_AT_L2_CACHESHAPE); break;
+ case ENTRY_NAME(AUXV_AT_L3_CACHESHAPE); break;
+ }
+#undef ENTRY_NAME
+
+ return name;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/AuxVector.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/AuxVector.h
new file mode 100644
index 000000000000..c16be68aedb1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/AuxVector.h
@@ -0,0 +1,73 @@
+//===-- AuxVector.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 liblldb_AuxVector_H_
+#define liblldb_AuxVector_H_
+
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include <unordered_map>
+
+class AuxVector {
+
+public:
+ AuxVector(const lldb_private::DataExtractor &data);
+
+ /// Constants describing the type of entry.
+ /// On Linux, running "LD_SHOW_AUXV=1 ./executable" will spew AUX
+ /// information. Added AUXV prefix to avoid potential conflicts with system-
+ /// defined macros
+ enum EntryType {
+ AUXV_AT_NULL = 0, ///< End of auxv.
+ AUXV_AT_IGNORE = 1, ///< Ignore entry.
+ AUXV_AT_EXECFD = 2, ///< File descriptor of program.
+ AUXV_AT_PHDR = 3, ///< Program headers.
+ AUXV_AT_PHENT = 4, ///< Size of program header.
+ AUXV_AT_PHNUM = 5, ///< Number of program headers.
+ AUXV_AT_PAGESZ = 6, ///< Page size.
+ AUXV_AT_BASE = 7, ///< Interpreter base address.
+ AUXV_AT_FLAGS = 8, ///< Flags.
+ AUXV_AT_ENTRY = 9, ///< Program entry point.
+ AUXV_AT_NOTELF = 10, ///< Set if program is not an ELF.
+ AUXV_AT_UID = 11, ///< UID.
+ AUXV_AT_EUID = 12, ///< Effective UID.
+ AUXV_AT_GID = 13, ///< GID.
+ AUXV_AT_EGID = 14, ///< Effective GID.
+ AUXV_AT_CLKTCK = 17, ///< Clock frequency (e.g. times(2)).
+ AUXV_AT_PLATFORM = 15, ///< String identifying platform.
+ AUXV_AT_HWCAP =
+ 16, ///< Machine dependent hints about processor capabilities.
+ AUXV_AT_FPUCW = 18, ///< Used FPU control word.
+ AUXV_AT_DCACHEBSIZE = 19, ///< Data cache block size.
+ AUXV_AT_ICACHEBSIZE = 20, ///< Instruction cache block size.
+ AUXV_AT_UCACHEBSIZE = 21, ///< Unified cache block size.
+ AUXV_AT_IGNOREPPC = 22, ///< Entry should be ignored.
+ AUXV_AT_SECURE = 23, ///< Boolean, was exec setuid-like?
+ AUXV_AT_BASE_PLATFORM = 24, ///< String identifying real platforms.
+ AUXV_AT_RANDOM = 25, ///< Address of 16 random bytes.
+ AUXV_AT_EXECFN = 31, ///< Filename of executable.
+ AUXV_AT_SYSINFO = 32, ///< Pointer to the global system page used for system
+ /// calls and other nice things.
+ AUXV_AT_SYSINFO_EHDR = 33,
+ AUXV_AT_L1I_CACHESHAPE = 34, ///< Shapes of the caches.
+ AUXV_AT_L1D_CACHESHAPE = 35,
+ AUXV_AT_L2_CACHESHAPE = 36,
+ AUXV_AT_L3_CACHESHAPE = 37,
+ };
+
+ llvm::Optional<uint64_t> GetAuxValue(enum EntryType entry_type) const;
+ void DumpToLog(lldb_private::Log *log) const;
+ const char *GetEntryName(EntryType type) const;
+
+private:
+ void ParseAuxv(const lldb_private::DataExtractor &data);
+
+ std::unordered_map<uint64_t, uint64_t> m_auxv_entries;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp
new file mode 100644
index 000000000000..1afe4d920599
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp
@@ -0,0 +1,758 @@
+//===-- DynamicRegisterInfo.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 "DynamicRegisterInfo.h"
+
+#include "lldb/Core/StreamFile.h"
+#include "lldb/DataFormatters/FormatManager.h"
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/StringExtractor.h"
+#include "lldb/Utility/StructuredData.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+DynamicRegisterInfo::DynamicRegisterInfo(
+ const lldb_private::StructuredData::Dictionary &dict,
+ const lldb_private::ArchSpec &arch) {
+ SetRegisterInfo(dict, arch);
+}
+
+DynamicRegisterInfo::DynamicRegisterInfo(DynamicRegisterInfo &&info) {
+ MoveFrom(std::move(info));
+}
+
+DynamicRegisterInfo &
+DynamicRegisterInfo::operator=(DynamicRegisterInfo &&info) {
+ MoveFrom(std::move(info));
+ return *this;
+}
+
+void DynamicRegisterInfo::MoveFrom(DynamicRegisterInfo &&info) {
+ m_regs = std::move(info.m_regs);
+ m_sets = std::move(info.m_sets);
+ m_set_reg_nums = std::move(info.m_set_reg_nums);
+ m_set_names = std::move(info.m_set_names);
+ m_value_regs_map = std::move(info.m_value_regs_map);
+ m_invalidate_regs_map = std::move(info.m_invalidate_regs_map);
+ m_dynamic_reg_size_map = std::move(info.m_dynamic_reg_size_map);
+
+ m_reg_data_byte_size = info.m_reg_data_byte_size;
+ m_finalized = info.m_finalized;
+
+ if (m_finalized) {
+ const size_t num_sets = m_sets.size();
+ for (size_t set = 0; set < num_sets; ++set)
+ m_sets[set].registers = m_set_reg_nums[set].data();
+ }
+
+ info.Clear();
+}
+
+size_t
+DynamicRegisterInfo::SetRegisterInfo(const StructuredData::Dictionary &dict,
+ const ArchSpec &arch) {
+ assert(!m_finalized);
+ StructuredData::Array *sets = nullptr;
+ if (dict.GetValueForKeyAsArray("sets", sets)) {
+ const uint32_t num_sets = sets->GetSize();
+ for (uint32_t i = 0; i < num_sets; ++i) {
+ ConstString set_name;
+ if (sets->GetItemAtIndexAsString(i, set_name) && !set_name.IsEmpty()) {
+ m_sets.push_back({set_name.AsCString(), nullptr, 0, nullptr});
+ } else {
+ Clear();
+ printf("error: register sets must have valid names\n");
+ return 0;
+ }
+ }
+ m_set_reg_nums.resize(m_sets.size());
+ }
+
+ StructuredData::Array *regs = nullptr;
+ if (!dict.GetValueForKeyAsArray("registers", regs))
+ return 0;
+
+ const uint32_t num_regs = regs->GetSize();
+ // typedef std::map<std::string, std::vector<std::string> >
+ // InvalidateNameMap;
+ // InvalidateNameMap invalidate_map;
+ for (uint32_t i = 0; i < num_regs; ++i) {
+ StructuredData::Dictionary *reg_info_dict = nullptr;
+ if (!regs->GetItemAtIndexAsDictionary(i, reg_info_dict)) {
+ Clear();
+ printf("error: items in the 'registers' array must be dictionaries\n");
+ regs->DumpToStdout();
+ return 0;
+ }
+
+ // { 'name':'rcx' , 'bitsize' : 64, 'offset' : 16,
+ // 'encoding':'uint' , 'format':'hex' , 'set': 0, 'ehframe' : 2,
+ // 'dwarf' : 2, 'generic':'arg4', 'alt-name':'arg4', },
+ RegisterInfo reg_info;
+ std::vector<uint32_t> value_regs;
+ std::vector<uint32_t> invalidate_regs;
+ memset(&reg_info, 0, sizeof(reg_info));
+
+ ConstString name_val;
+ ConstString alt_name_val;
+ if (!reg_info_dict->GetValueForKeyAsString("name", name_val, nullptr)) {
+ Clear();
+ printf("error: registers must have valid names and offsets\n");
+ reg_info_dict->DumpToStdout();
+ return 0;
+ }
+ reg_info.name = name_val.GetCString();
+ reg_info_dict->GetValueForKeyAsString("alt-name", alt_name_val, nullptr);
+ reg_info.alt_name = alt_name_val.GetCString();
+
+ reg_info_dict->GetValueForKeyAsInteger("offset", reg_info.byte_offset,
+ UINT32_MAX);
+
+ const ByteOrder byte_order = arch.GetByteOrder();
+
+ if (reg_info.byte_offset == UINT32_MAX) {
+ // No offset for this register, see if the register has a value
+ // expression which indicates this register is part of another register.
+ // Value expressions are things like "rax[31:0]" which state that the
+ // current register's value is in a concrete register "rax" in bits 31:0.
+ // If there is a value expression we can calculate the offset
+ bool success = false;
+ llvm::StringRef slice_str;
+ if (reg_info_dict->GetValueForKeyAsString("slice", slice_str, nullptr)) {
+ // Slices use the following format:
+ // REGNAME[MSBIT:LSBIT]
+ // REGNAME - name of the register to grab a slice of
+ // MSBIT - the most significant bit at which the current register value
+ // starts at
+ // LSBIT - the least significant bit at which the current register value
+ // ends at
+ static RegularExpression g_bitfield_regex(
+ llvm::StringRef("([A-Za-z_][A-Za-z0-9_]*)\\[([0-9]+):([0-9]+)\\]"));
+ RegularExpression::Match regex_match(3);
+ if (g_bitfield_regex.Execute(slice_str, &regex_match)) {
+ llvm::StringRef reg_name_str;
+ std::string msbit_str;
+ std::string lsbit_str;
+ if (regex_match.GetMatchAtIndex(slice_str, 1, reg_name_str) &&
+ regex_match.GetMatchAtIndex(slice_str, 2, msbit_str) &&
+ regex_match.GetMatchAtIndex(slice_str, 3, lsbit_str)) {
+ const uint32_t msbit =
+ StringConvert::ToUInt32(msbit_str.c_str(), UINT32_MAX);
+ const uint32_t lsbit =
+ StringConvert::ToUInt32(lsbit_str.c_str(), UINT32_MAX);
+ if (msbit != UINT32_MAX && lsbit != UINT32_MAX) {
+ if (msbit > lsbit) {
+ const uint32_t msbyte = msbit / 8;
+ const uint32_t lsbyte = lsbit / 8;
+
+ ConstString containing_reg_name(reg_name_str);
+
+ const RegisterInfo *containing_reg_info =
+ GetRegisterInfo(containing_reg_name);
+ if (containing_reg_info) {
+ const uint32_t max_bit = containing_reg_info->byte_size * 8;
+ if (msbit < max_bit && lsbit < max_bit) {
+ m_invalidate_regs_map[containing_reg_info
+ ->kinds[eRegisterKindLLDB]]
+ .push_back(i);
+ m_value_regs_map[i].push_back(
+ containing_reg_info->kinds[eRegisterKindLLDB]);
+ m_invalidate_regs_map[i].push_back(
+ containing_reg_info->kinds[eRegisterKindLLDB]);
+
+ if (byte_order == eByteOrderLittle) {
+ success = true;
+ reg_info.byte_offset =
+ containing_reg_info->byte_offset + lsbyte;
+ } else if (byte_order == eByteOrderBig) {
+ success = true;
+ reg_info.byte_offset =
+ containing_reg_info->byte_offset + msbyte;
+ } else {
+ llvm_unreachable("Invalid byte order");
+ }
+ } else {
+ if (msbit > max_bit)
+ printf("error: msbit (%u) must be less than the bitsize "
+ "of the register (%u)\n",
+ msbit, max_bit);
+ else
+ printf("error: lsbit (%u) must be less than the bitsize "
+ "of the register (%u)\n",
+ lsbit, max_bit);
+ }
+ } else {
+ printf("error: invalid concrete register \"%s\"\n",
+ containing_reg_name.GetCString());
+ }
+ } else {
+ printf("error: msbit (%u) must be greater than lsbit (%u)\n",
+ msbit, lsbit);
+ }
+ } else {
+ printf("error: msbit (%u) and lsbit (%u) must be valid\n", msbit,
+ lsbit);
+ }
+ } else {
+ // TODO: print error invalid slice string that doesn't follow the
+ // format
+ printf("error: failed to extract regex matches for parsing the "
+ "register bitfield regex\n");
+ }
+ } else {
+ // TODO: print error invalid slice string that doesn't follow the
+ // format
+ printf("error: failed to match against register bitfield regex\n");
+ }
+ } else {
+ StructuredData::Array *composite_reg_list = nullptr;
+ if (reg_info_dict->GetValueForKeyAsArray("composite",
+ composite_reg_list)) {
+ const size_t num_composite_regs = composite_reg_list->GetSize();
+ if (num_composite_regs > 0) {
+ uint32_t composite_offset = UINT32_MAX;
+ for (uint32_t composite_idx = 0; composite_idx < num_composite_regs;
+ ++composite_idx) {
+ ConstString composite_reg_name;
+ if (composite_reg_list->GetItemAtIndexAsString(
+ composite_idx, composite_reg_name, nullptr)) {
+ const RegisterInfo *composite_reg_info =
+ GetRegisterInfo(composite_reg_name);
+ if (composite_reg_info) {
+ composite_offset = std::min(composite_offset,
+ composite_reg_info->byte_offset);
+ m_value_regs_map[i].push_back(
+ composite_reg_info->kinds[eRegisterKindLLDB]);
+ m_invalidate_regs_map[composite_reg_info
+ ->kinds[eRegisterKindLLDB]]
+ .push_back(i);
+ m_invalidate_regs_map[i].push_back(
+ composite_reg_info->kinds[eRegisterKindLLDB]);
+ } else {
+ // TODO: print error invalid slice string that doesn't follow
+ // the format
+ printf("error: failed to find composite register by name: "
+ "\"%s\"\n",
+ composite_reg_name.GetCString());
+ }
+ } else {
+ printf(
+ "error: 'composite' list value wasn't a python string\n");
+ }
+ }
+ if (composite_offset != UINT32_MAX) {
+ reg_info.byte_offset = composite_offset;
+ success = m_value_regs_map.find(i) != m_value_regs_map.end();
+ } else {
+ printf("error: 'composite' registers must specify at least one "
+ "real register\n");
+ }
+ } else {
+ printf("error: 'composite' list was empty\n");
+ }
+ }
+ }
+
+ if (!success) {
+ Clear();
+ reg_info_dict->DumpToStdout();
+ return 0;
+ }
+ }
+
+ int64_t bitsize = 0;
+ if (!reg_info_dict->GetValueForKeyAsInteger("bitsize", bitsize)) {
+ Clear();
+ printf("error: invalid or missing 'bitsize' key/value pair in register "
+ "dictionary\n");
+ reg_info_dict->DumpToStdout();
+ return 0;
+ }
+
+ reg_info.byte_size = bitsize / 8;
+
+ llvm::StringRef dwarf_opcode_string;
+ if (reg_info_dict->GetValueForKeyAsString("dynamic_size_dwarf_expr_bytes",
+ dwarf_opcode_string)) {
+ reg_info.dynamic_size_dwarf_len = dwarf_opcode_string.size() / 2;
+ assert(reg_info.dynamic_size_dwarf_len > 0);
+
+ std::vector<uint8_t> dwarf_opcode_bytes(reg_info.dynamic_size_dwarf_len);
+ uint32_t j;
+ StringExtractor opcode_extractor(dwarf_opcode_string);
+ uint32_t ret_val = opcode_extractor.GetHexBytesAvail(dwarf_opcode_bytes);
+ UNUSED_IF_ASSERT_DISABLED(ret_val);
+ assert(ret_val == reg_info.dynamic_size_dwarf_len);
+
+ for (j = 0; j < reg_info.dynamic_size_dwarf_len; ++j)
+ m_dynamic_reg_size_map[i].push_back(dwarf_opcode_bytes[j]);
+
+ reg_info.dynamic_size_dwarf_expr_bytes = m_dynamic_reg_size_map[i].data();
+ }
+
+ llvm::StringRef format_str;
+ if (reg_info_dict->GetValueForKeyAsString("format", format_str, nullptr)) {
+ if (OptionArgParser::ToFormat(format_str.str().c_str(), reg_info.format,
+ nullptr)
+ .Fail()) {
+ Clear();
+ printf("error: invalid 'format' value in register dictionary\n");
+ reg_info_dict->DumpToStdout();
+ return 0;
+ }
+ } else {
+ reg_info_dict->GetValueForKeyAsInteger("format", reg_info.format,
+ eFormatHex);
+ }
+
+ llvm::StringRef encoding_str;
+ if (reg_info_dict->GetValueForKeyAsString("encoding", encoding_str))
+ reg_info.encoding = Args::StringToEncoding(encoding_str, eEncodingUint);
+ else
+ reg_info_dict->GetValueForKeyAsInteger("encoding", reg_info.encoding,
+ eEncodingUint);
+
+ size_t set = 0;
+ if (!reg_info_dict->GetValueForKeyAsInteger<size_t>("set", set, -1) ||
+ set >= m_sets.size()) {
+ Clear();
+ printf("error: invalid 'set' value in register dictionary, valid values "
+ "are 0 - %i\n",
+ (int)set);
+ reg_info_dict->DumpToStdout();
+ return 0;
+ }
+
+ // Fill in the register numbers
+ reg_info.kinds[lldb::eRegisterKindLLDB] = i;
+ reg_info.kinds[lldb::eRegisterKindProcessPlugin] = i;
+ uint32_t eh_frame_regno = LLDB_INVALID_REGNUM;
+ reg_info_dict->GetValueForKeyAsInteger("gcc", eh_frame_regno,
+ LLDB_INVALID_REGNUM);
+ if (eh_frame_regno == LLDB_INVALID_REGNUM)
+ reg_info_dict->GetValueForKeyAsInteger("ehframe", eh_frame_regno,
+ LLDB_INVALID_REGNUM);
+ reg_info.kinds[lldb::eRegisterKindEHFrame] = eh_frame_regno;
+ reg_info_dict->GetValueForKeyAsInteger(
+ "dwarf", reg_info.kinds[lldb::eRegisterKindDWARF], LLDB_INVALID_REGNUM);
+ llvm::StringRef generic_str;
+ if (reg_info_dict->GetValueForKeyAsString("generic", generic_str))
+ reg_info.kinds[lldb::eRegisterKindGeneric] =
+ Args::StringToGenericRegister(generic_str);
+ else
+ reg_info_dict->GetValueForKeyAsInteger(
+ "generic", reg_info.kinds[lldb::eRegisterKindGeneric],
+ LLDB_INVALID_REGNUM);
+
+ // Check if this register invalidates any other register values when it is
+ // modified
+ StructuredData::Array *invalidate_reg_list = nullptr;
+ if (reg_info_dict->GetValueForKeyAsArray("invalidate-regs",
+ invalidate_reg_list)) {
+ const size_t num_regs = invalidate_reg_list->GetSize();
+ if (num_regs > 0) {
+ for (uint32_t idx = 0; idx < num_regs; ++idx) {
+ ConstString invalidate_reg_name;
+ uint64_t invalidate_reg_num;
+ if (invalidate_reg_list->GetItemAtIndexAsString(
+ idx, invalidate_reg_name)) {
+ const RegisterInfo *invalidate_reg_info =
+ GetRegisterInfo(invalidate_reg_name);
+ if (invalidate_reg_info) {
+ m_invalidate_regs_map[i].push_back(
+ invalidate_reg_info->kinds[eRegisterKindLLDB]);
+ } else {
+ // TODO: print error invalid slice string that doesn't follow the
+ // format
+ printf("error: failed to find a 'invalidate-regs' register for "
+ "\"%s\" while parsing register \"%s\"\n",
+ invalidate_reg_name.GetCString(), reg_info.name);
+ }
+ } else if (invalidate_reg_list->GetItemAtIndexAsInteger(
+ idx, invalidate_reg_num)) {
+ if (invalidate_reg_num != UINT64_MAX)
+ m_invalidate_regs_map[i].push_back(invalidate_reg_num);
+ else
+ printf("error: 'invalidate-regs' list value wasn't a valid "
+ "integer\n");
+ } else {
+ printf("error: 'invalidate-regs' list value wasn't a python string "
+ "or integer\n");
+ }
+ }
+ } else {
+ printf("error: 'invalidate-regs' contained an empty list\n");
+ }
+ }
+
+ // Calculate the register offset
+ const size_t end_reg_offset = reg_info.byte_offset + reg_info.byte_size;
+ if (m_reg_data_byte_size < end_reg_offset)
+ m_reg_data_byte_size = end_reg_offset;
+
+ m_regs.push_back(reg_info);
+ m_set_reg_nums[set].push_back(i);
+ }
+ Finalize(arch);
+ return m_regs.size();
+}
+
+void DynamicRegisterInfo::AddRegister(RegisterInfo &reg_info,
+ ConstString &reg_name,
+ ConstString &reg_alt_name,
+ ConstString &set_name) {
+ assert(!m_finalized);
+ const uint32_t reg_num = m_regs.size();
+ reg_info.name = reg_name.AsCString();
+ assert(reg_info.name);
+ reg_info.alt_name = reg_alt_name.AsCString(nullptr);
+ uint32_t i;
+ if (reg_info.value_regs) {
+ for (i = 0; reg_info.value_regs[i] != LLDB_INVALID_REGNUM; ++i)
+ m_value_regs_map[reg_num].push_back(reg_info.value_regs[i]);
+ }
+ if (reg_info.invalidate_regs) {
+ for (i = 0; reg_info.invalidate_regs[i] != LLDB_INVALID_REGNUM; ++i)
+ m_invalidate_regs_map[reg_num].push_back(reg_info.invalidate_regs[i]);
+ }
+ if (reg_info.dynamic_size_dwarf_expr_bytes) {
+ for (i = 0; i < reg_info.dynamic_size_dwarf_len; ++i)
+ m_dynamic_reg_size_map[reg_num].push_back(
+ reg_info.dynamic_size_dwarf_expr_bytes[i]);
+
+ reg_info.dynamic_size_dwarf_expr_bytes =
+ m_dynamic_reg_size_map[reg_num].data();
+ }
+
+ m_regs.push_back(reg_info);
+ uint32_t set = GetRegisterSetIndexByName(set_name, true);
+ assert(set < m_sets.size());
+ assert(set < m_set_reg_nums.size());
+ assert(set < m_set_names.size());
+ m_set_reg_nums[set].push_back(reg_num);
+ size_t end_reg_offset = reg_info.byte_offset + reg_info.byte_size;
+ if (m_reg_data_byte_size < end_reg_offset)
+ m_reg_data_byte_size = end_reg_offset;
+}
+
+void DynamicRegisterInfo::Finalize(const ArchSpec &arch) {
+ if (m_finalized)
+ return;
+
+ m_finalized = true;
+ const size_t num_sets = m_sets.size();
+ for (size_t set = 0; set < num_sets; ++set) {
+ assert(m_sets.size() == m_set_reg_nums.size());
+ m_sets[set].num_registers = m_set_reg_nums[set].size();
+ m_sets[set].registers = m_set_reg_nums[set].data();
+ }
+
+ // sort and unique all value registers and make sure each is terminated with
+ // LLDB_INVALID_REGNUM
+
+ for (reg_to_regs_map::iterator pos = m_value_regs_map.begin(),
+ end = m_value_regs_map.end();
+ pos != end; ++pos) {
+ if (pos->second.size() > 1) {
+ llvm::sort(pos->second.begin(), pos->second.end());
+ reg_num_collection::iterator unique_end =
+ std::unique(pos->second.begin(), pos->second.end());
+ if (unique_end != pos->second.end())
+ pos->second.erase(unique_end, pos->second.end());
+ }
+ assert(!pos->second.empty());
+ if (pos->second.back() != LLDB_INVALID_REGNUM)
+ pos->second.push_back(LLDB_INVALID_REGNUM);
+ }
+
+ // Now update all value_regs with each register info as needed
+ const size_t num_regs = m_regs.size();
+ for (size_t i = 0; i < num_regs; ++i) {
+ if (m_value_regs_map.find(i) != m_value_regs_map.end())
+ m_regs[i].value_regs = m_value_regs_map[i].data();
+ else
+ m_regs[i].value_regs = nullptr;
+ }
+
+ // Expand all invalidation dependencies
+ for (reg_to_regs_map::iterator pos = m_invalidate_regs_map.begin(),
+ end = m_invalidate_regs_map.end();
+ pos != end; ++pos) {
+ const uint32_t reg_num = pos->first;
+
+ if (m_regs[reg_num].value_regs) {
+ reg_num_collection extra_invalid_regs;
+ for (const uint32_t invalidate_reg_num : pos->second) {
+ reg_to_regs_map::iterator invalidate_pos =
+ m_invalidate_regs_map.find(invalidate_reg_num);
+ if (invalidate_pos != m_invalidate_regs_map.end()) {
+ for (const uint32_t concrete_invalidate_reg_num :
+ invalidate_pos->second) {
+ if (concrete_invalidate_reg_num != reg_num)
+ extra_invalid_regs.push_back(concrete_invalidate_reg_num);
+ }
+ }
+ }
+ pos->second.insert(pos->second.end(), extra_invalid_regs.begin(),
+ extra_invalid_regs.end());
+ }
+ }
+
+ // sort and unique all invalidate registers and make sure each is terminated
+ // with LLDB_INVALID_REGNUM
+ for (reg_to_regs_map::iterator pos = m_invalidate_regs_map.begin(),
+ end = m_invalidate_regs_map.end();
+ pos != end; ++pos) {
+ if (pos->second.size() > 1) {
+ llvm::sort(pos->second.begin(), pos->second.end());
+ reg_num_collection::iterator unique_end =
+ std::unique(pos->second.begin(), pos->second.end());
+ if (unique_end != pos->second.end())
+ pos->second.erase(unique_end, pos->second.end());
+ }
+ assert(!pos->second.empty());
+ if (pos->second.back() != LLDB_INVALID_REGNUM)
+ pos->second.push_back(LLDB_INVALID_REGNUM);
+ }
+
+ // Now update all invalidate_regs with each register info as needed
+ for (size_t i = 0; i < num_regs; ++i) {
+ if (m_invalidate_regs_map.find(i) != m_invalidate_regs_map.end())
+ m_regs[i].invalidate_regs = m_invalidate_regs_map[i].data();
+ else
+ m_regs[i].invalidate_regs = nullptr;
+ }
+
+ // Check if we need to automatically set the generic registers in case they
+ // weren't set
+ bool generic_regs_specified = false;
+ for (const auto &reg : m_regs) {
+ if (reg.kinds[eRegisterKindGeneric] != LLDB_INVALID_REGNUM) {
+ generic_regs_specified = true;
+ break;
+ }
+ }
+
+ if (!generic_regs_specified) {
+ switch (arch.GetMachine()) {
+ case llvm::Triple::aarch64:
+ case llvm::Triple::aarch64_be:
+ for (auto &reg : m_regs) {
+ if (strcmp(reg.name, "pc") == 0)
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC;
+ else if ((strcmp(reg.name, "fp") == 0) ||
+ (strcmp(reg.name, "x29") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP;
+ else if ((strcmp(reg.name, "lr") == 0) ||
+ (strcmp(reg.name, "x30") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA;
+ else if ((strcmp(reg.name, "sp") == 0) ||
+ (strcmp(reg.name, "x31") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP;
+ else if (strcmp(reg.name, "cpsr") == 0)
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS;
+ }
+ break;
+
+ case llvm::Triple::arm:
+ case llvm::Triple::armeb:
+ case llvm::Triple::thumb:
+ case llvm::Triple::thumbeb:
+ for (auto &reg : m_regs) {
+ if ((strcmp(reg.name, "pc") == 0) || (strcmp(reg.name, "r15") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC;
+ else if ((strcmp(reg.name, "sp") == 0) ||
+ (strcmp(reg.name, "r13") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP;
+ else if ((strcmp(reg.name, "lr") == 0) ||
+ (strcmp(reg.name, "r14") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA;
+ else if ((strcmp(reg.name, "r7") == 0) &&
+ arch.GetTriple().getVendor() == llvm::Triple::Apple)
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP;
+ else if ((strcmp(reg.name, "r11") == 0) &&
+ arch.GetTriple().getVendor() != llvm::Triple::Apple)
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP;
+ else if (strcmp(reg.name, "fp") == 0)
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP;
+ else if (strcmp(reg.name, "cpsr") == 0)
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS;
+ }
+ break;
+
+ case llvm::Triple::x86:
+ for (auto &reg : m_regs) {
+ if ((strcmp(reg.name, "eip") == 0) || (strcmp(reg.name, "pc") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC;
+ else if ((strcmp(reg.name, "esp") == 0) ||
+ (strcmp(reg.name, "sp") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP;
+ else if ((strcmp(reg.name, "ebp") == 0) ||
+ (strcmp(reg.name, "fp") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP;
+ else if ((strcmp(reg.name, "eflags") == 0) ||
+ (strcmp(reg.name, "flags") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS;
+ }
+ break;
+
+ case llvm::Triple::x86_64:
+ for (auto &reg : m_regs) {
+ if ((strcmp(reg.name, "rip") == 0) || (strcmp(reg.name, "pc") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC;
+ else if ((strcmp(reg.name, "rsp") == 0) ||
+ (strcmp(reg.name, "sp") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP;
+ else if ((strcmp(reg.name, "rbp") == 0) ||
+ (strcmp(reg.name, "fp") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP;
+ else if ((strcmp(reg.name, "rflags") == 0) ||
+ (strcmp(reg.name, "flags") == 0))
+ reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+size_t DynamicRegisterInfo::GetNumRegisters() const { return m_regs.size(); }
+
+size_t DynamicRegisterInfo::GetNumRegisterSets() const { return m_sets.size(); }
+
+size_t DynamicRegisterInfo::GetRegisterDataByteSize() const {
+ return m_reg_data_byte_size;
+}
+
+const RegisterInfo *
+DynamicRegisterInfo::GetRegisterInfoAtIndex(uint32_t i) const {
+ if (i < m_regs.size())
+ return &m_regs[i];
+ return nullptr;
+}
+
+RegisterInfo *DynamicRegisterInfo::GetRegisterInfoAtIndex(uint32_t i) {
+ if (i < m_regs.size())
+ return &m_regs[i];
+ return nullptr;
+}
+
+const RegisterSet *DynamicRegisterInfo::GetRegisterSet(uint32_t i) const {
+ if (i < m_sets.size())
+ return &m_sets[i];
+ return nullptr;
+}
+
+uint32_t DynamicRegisterInfo::GetRegisterSetIndexByName(ConstString &set_name,
+ bool can_create) {
+ name_collection::iterator pos, end = m_set_names.end();
+ for (pos = m_set_names.begin(); pos != end; ++pos) {
+ if (*pos == set_name)
+ return std::distance(m_set_names.begin(), pos);
+ }
+
+ m_set_names.push_back(set_name);
+ m_set_reg_nums.resize(m_set_reg_nums.size() + 1);
+ RegisterSet new_set = {set_name.AsCString(), nullptr, 0, nullptr};
+ m_sets.push_back(new_set);
+ return m_sets.size() - 1;
+}
+
+uint32_t
+DynamicRegisterInfo::ConvertRegisterKindToRegisterNumber(uint32_t kind,
+ uint32_t num) const {
+ reg_collection::const_iterator pos, end = m_regs.end();
+ for (pos = m_regs.begin(); pos != end; ++pos) {
+ if (pos->kinds[kind] == num)
+ return std::distance(m_regs.begin(), pos);
+ }
+
+ return LLDB_INVALID_REGNUM;
+}
+
+void DynamicRegisterInfo::Clear() {
+ m_regs.clear();
+ m_sets.clear();
+ m_set_reg_nums.clear();
+ m_set_names.clear();
+ m_value_regs_map.clear();
+ m_invalidate_regs_map.clear();
+ m_dynamic_reg_size_map.clear();
+ m_reg_data_byte_size = 0;
+ m_finalized = false;
+}
+
+void DynamicRegisterInfo::Dump() const {
+ StreamFile s(stdout, false);
+ const size_t num_regs = m_regs.size();
+ s.Printf("%p: DynamicRegisterInfo contains %" PRIu64 " registers:\n",
+ static_cast<const void *>(this), static_cast<uint64_t>(num_regs));
+ for (size_t i = 0; i < num_regs; ++i) {
+ s.Printf("[%3" PRIu64 "] name = %-10s", (uint64_t)i, m_regs[i].name);
+ s.Printf(", size = %2u, offset = %4u, encoding = %u, format = %-10s",
+ m_regs[i].byte_size, m_regs[i].byte_offset, m_regs[i].encoding,
+ FormatManager::GetFormatAsCString(m_regs[i].format));
+ if (m_regs[i].kinds[eRegisterKindProcessPlugin] != LLDB_INVALID_REGNUM)
+ s.Printf(", process plugin = %3u",
+ m_regs[i].kinds[eRegisterKindProcessPlugin]);
+ if (m_regs[i].kinds[eRegisterKindDWARF] != LLDB_INVALID_REGNUM)
+ s.Printf(", dwarf = %3u", m_regs[i].kinds[eRegisterKindDWARF]);
+ if (m_regs[i].kinds[eRegisterKindEHFrame] != LLDB_INVALID_REGNUM)
+ s.Printf(", ehframe = %3u", m_regs[i].kinds[eRegisterKindEHFrame]);
+ if (m_regs[i].kinds[eRegisterKindGeneric] != LLDB_INVALID_REGNUM)
+ s.Printf(", generic = %3u", m_regs[i].kinds[eRegisterKindGeneric]);
+ if (m_regs[i].alt_name)
+ s.Printf(", alt-name = %s", m_regs[i].alt_name);
+ if (m_regs[i].value_regs) {
+ s.Printf(", value_regs = [ ");
+ for (size_t j = 0; m_regs[i].value_regs[j] != LLDB_INVALID_REGNUM; ++j) {
+ s.Printf("%s ", m_regs[m_regs[i].value_regs[j]].name);
+ }
+ s.Printf("]");
+ }
+ if (m_regs[i].invalidate_regs) {
+ s.Printf(", invalidate_regs = [ ");
+ for (size_t j = 0; m_regs[i].invalidate_regs[j] != LLDB_INVALID_REGNUM;
+ ++j) {
+ s.Printf("%s ", m_regs[m_regs[i].invalidate_regs[j]].name);
+ }
+ s.Printf("]");
+ }
+ s.EOL();
+ }
+
+ const size_t num_sets = m_sets.size();
+ s.Printf("%p: DynamicRegisterInfo contains %" PRIu64 " register sets:\n",
+ static_cast<const void *>(this), static_cast<uint64_t>(num_sets));
+ for (size_t i = 0; i < num_sets; ++i) {
+ s.Printf("set[%" PRIu64 "] name = %s, regs = [", (uint64_t)i,
+ m_sets[i].name);
+ for (size_t idx = 0; idx < m_sets[i].num_registers; ++idx) {
+ s.Printf("%s ", m_regs[m_sets[i].registers[idx]].name);
+ }
+ s.Printf("]\n");
+ }
+}
+
+const lldb_private::RegisterInfo *DynamicRegisterInfo::GetRegisterInfo(
+ lldb_private::ConstString reg_name) const {
+ for (auto &reg_info : m_regs) {
+ // We can use pointer comparison since we used a ConstString to set the
+ // "name" member in AddRegister()
+ if (reg_info.name == reg_name.GetCString()) {
+ return &reg_info;
+ }
+ }
+ return nullptr;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.h
new file mode 100644
index 000000000000..aacf547e187d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/DynamicRegisterInfo.h
@@ -0,0 +1,93 @@
+//===-- DynamicRegisterInfo.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_DynamicRegisterInfo_h_
+#define lldb_DynamicRegisterInfo_h_
+
+#include <map>
+#include <vector>
+
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/StructuredData.h"
+#include "lldb/lldb-private.h"
+
+class DynamicRegisterInfo {
+public:
+ DynamicRegisterInfo() = default;
+
+ DynamicRegisterInfo(const lldb_private::StructuredData::Dictionary &dict,
+ const lldb_private::ArchSpec &arch);
+
+ virtual ~DynamicRegisterInfo() = default;
+
+ DynamicRegisterInfo(DynamicRegisterInfo &) = delete;
+ void operator=(DynamicRegisterInfo &) = delete;
+
+ DynamicRegisterInfo(DynamicRegisterInfo &&info);
+ DynamicRegisterInfo &operator=(DynamicRegisterInfo &&info);
+
+ size_t SetRegisterInfo(const lldb_private::StructuredData::Dictionary &dict,
+ const lldb_private::ArchSpec &arch);
+
+ void AddRegister(lldb_private::RegisterInfo &reg_info,
+ lldb_private::ConstString &reg_name,
+ lldb_private::ConstString &reg_alt_name,
+ lldb_private::ConstString &set_name);
+
+ void Finalize(const lldb_private::ArchSpec &arch);
+
+ size_t GetNumRegisters() const;
+
+ size_t GetNumRegisterSets() const;
+
+ size_t GetRegisterDataByteSize() const;
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(uint32_t i) const;
+
+ lldb_private::RegisterInfo *GetRegisterInfoAtIndex(uint32_t i);
+
+ const lldb_private::RegisterSet *GetRegisterSet(uint32_t i) const;
+
+ uint32_t GetRegisterSetIndexByName(lldb_private::ConstString &set_name,
+ bool can_create);
+
+ uint32_t ConvertRegisterKindToRegisterNumber(uint32_t kind,
+ uint32_t num) const;
+
+ void Dump() const;
+
+ void Clear();
+
+protected:
+ // Classes that inherit from DynamicRegisterInfo can see and modify these
+ typedef std::vector<lldb_private::RegisterInfo> reg_collection;
+ typedef std::vector<lldb_private::RegisterSet> set_collection;
+ typedef std::vector<uint32_t> reg_num_collection;
+ typedef std::vector<reg_num_collection> set_reg_num_collection;
+ typedef std::vector<lldb_private::ConstString> name_collection;
+ typedef std::map<uint32_t, reg_num_collection> reg_to_regs_map;
+ typedef std::vector<uint8_t> dwarf_opcode;
+ typedef std::map<uint32_t, dwarf_opcode> dynamic_reg_size_map;
+
+ const lldb_private::RegisterInfo *
+ GetRegisterInfo(lldb_private::ConstString reg_name) const;
+
+ void MoveFrom(DynamicRegisterInfo &&info);
+
+ reg_collection m_regs;
+ set_collection m_sets;
+ set_reg_num_collection m_set_reg_nums;
+ name_collection m_set_names;
+ reg_to_regs_map m_value_regs_map;
+ reg_to_regs_map m_invalidate_regs_map;
+ dynamic_reg_size_map m_dynamic_reg_size_map;
+ size_t m_reg_data_byte_size = 0u; // The number of bytes required to store
+ // all registers
+ bool m_finalized = false;
+};
+#endif // lldb_DynamicRegisterInfo_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/FreeBSDSignals.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/FreeBSDSignals.cpp
new file mode 100644
index 000000000000..9f63a594e054
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/FreeBSDSignals.cpp
@@ -0,0 +1,86 @@
+//===-- FreeBSDSignals.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 "FreeBSDSignals.h"
+
+using namespace lldb_private;
+
+FreeBSDSignals::FreeBSDSignals() : UnixSignals() { Reset(); }
+
+void FreeBSDSignals::Reset() {
+ UnixSignals::Reset();
+
+ // SIGNO NAME SUPPRESS STOP NOTIFY DESCRIPTION
+ // ====== ============ ======== ====== ======
+ // ===================================================
+ AddSignal(32, "SIGTHR", false, false, false, "thread interrupt");
+ AddSignal(33, "SIGLIBRT", false, false, false,
+ "reserved by real-time library");
+ AddSignal(65, "SIGRTMIN", false, false, false, "real time signal 0");
+ AddSignal(66, "SIGRTMIN+1", false, false, false, "real time signal 1");
+ AddSignal(67, "SIGRTMIN+2", false, false, false, "real time signal 2");
+ AddSignal(68, "SIGRTMIN+3", false, false, false, "real time signal 3");
+ AddSignal(69, "SIGRTMIN+4", false, false, false, "real time signal 4");
+ AddSignal(70, "SIGRTMIN+5", false, false, false, "real time signal 5");
+ AddSignal(71, "SIGRTMIN+6", false, false, false, "real time signal 6");
+ AddSignal(72, "SIGRTMIN+7", false, false, false, "real time signal 7");
+ AddSignal(73, "SIGRTMIN+8", false, false, false, "real time signal 8");
+ AddSignal(74, "SIGRTMIN+9", false, false, false, "real time signal 9");
+ AddSignal(75, "SIGRTMIN+10", false, false, false, "real time signal 10");
+ AddSignal(76, "SIGRTMIN+11", false, false, false, "real time signal 11");
+ AddSignal(77, "SIGRTMIN+12", false, false, false, "real time signal 12");
+ AddSignal(78, "SIGRTMIN+13", false, false, false, "real time signal 13");
+ AddSignal(79, "SIGRTMIN+14", false, false, false, "real time signal 14");
+ AddSignal(80, "SIGRTMIN+15", false, false, false, "real time signal 15");
+ AddSignal(81, "SIGRTMIN+16", false, false, false, "real time signal 16");
+ AddSignal(82, "SIGRTMIN+17", false, false, false, "real time signal 17");
+ AddSignal(83, "SIGRTMIN+18", false, false, false, "real time signal 18");
+ AddSignal(84, "SIGRTMIN+19", false, false, false, "real time signal 19");
+ AddSignal(85, "SIGRTMIN+20", false, false, false, "real time signal 20");
+ AddSignal(86, "SIGRTMIN+21", false, false, false, "real time signal 21");
+ AddSignal(87, "SIGRTMIN+22", false, false, false, "real time signal 22");
+ AddSignal(88, "SIGRTMIN+23", false, false, false, "real time signal 23");
+ AddSignal(89, "SIGRTMIN+24", false, false, false, "real time signal 24");
+ AddSignal(90, "SIGRTMIN+25", false, false, false, "real time signal 25");
+ AddSignal(91, "SIGRTMIN+26", false, false, false, "real time signal 26");
+ AddSignal(92, "SIGRTMIN+27", false, false, false, "real time signal 27");
+ AddSignal(93, "SIGRTMIN+28", false, false, false, "real time signal 28");
+ AddSignal(94, "SIGRTMIN+29", false, false, false, "real time signal 29");
+ AddSignal(95, "SIGRTMIN+30", false, false, false, "real time signal 30");
+ AddSignal(96, "SIGRTMAX-30", false, false, false, "real time signal 31");
+ AddSignal(97, "SIGRTMAX-29", false, false, false, "real time signal 32");
+ AddSignal(98, "SIGRTMAX-28", false, false, false, "real time signal 33");
+ AddSignal(99, "SIGRTMAX-27", false, false, false, "real time signal 34");
+ AddSignal(100, "SIGRTMAX-26", false, false, false, "real time signal 35");
+ AddSignal(101, "SIGRTMAX-25", false, false, false, "real time signal 36");
+ AddSignal(102, "SIGRTMAX-24", false, false, false, "real time signal 37");
+ AddSignal(103, "SIGRTMAX-23", false, false, false, "real time signal 38");
+ AddSignal(104, "SIGRTMAX-22", false, false, false, "real time signal 39");
+ AddSignal(105, "SIGRTMAX-21", false, false, false, "real time signal 40");
+ AddSignal(106, "SIGRTMAX-20", false, false, false, "real time signal 41");
+ AddSignal(107, "SIGRTMAX-19", false, false, false, "real time signal 42");
+ AddSignal(108, "SIGRTMAX-18", false, false, false, "real time signal 43");
+ AddSignal(109, "SIGRTMAX-17", false, false, false, "real time signal 44");
+ AddSignal(110, "SIGRTMAX-16", false, false, false, "real time signal 45");
+ AddSignal(111, "SIGRTMAX-15", false, false, false, "real time signal 46");
+ AddSignal(112, "SIGRTMAX-14", false, false, false, "real time signal 47");
+ AddSignal(113, "SIGRTMAX-13", false, false, false, "real time signal 48");
+ AddSignal(114, "SIGRTMAX-12", false, false, false, "real time signal 49");
+ AddSignal(115, "SIGRTMAX-11", false, false, false, "real time signal 50");
+ AddSignal(116, "SIGRTMAX-10", false, false, false, "real time signal 51");
+ AddSignal(117, "SIGRTMAX-9", false, false, false, "real time signal 52");
+ AddSignal(118, "SIGRTMAX-8", false, false, false, "real time signal 53");
+ AddSignal(119, "SIGRTMAX-7", false, false, false, "real time signal 54");
+ AddSignal(120, "SIGRTMAX-6", false, false, false, "real time signal 55");
+ AddSignal(121, "SIGRTMAX-5", false, false, false, "real time signal 56");
+ AddSignal(122, "SIGRTMAX-4", false, false, false, "real time signal 57");
+ AddSignal(123, "SIGRTMAX-3", false, false, false, "real time signal 58");
+ AddSignal(124, "SIGRTMAX-2", false, false, false, "real time signal 59");
+ AddSignal(125, "SIGRTMAX-1", false, false, false, "real time signal 60");
+ AddSignal(126, "SIGRTMAX", false, false, false, "real time signal 61");
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/FreeBSDSignals.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/FreeBSDSignals.h
new file mode 100644
index 000000000000..75462f3c76ff
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/FreeBSDSignals.h
@@ -0,0 +1,27 @@
+//===-- FreeBSDSignals.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 liblldb_FreeBSDSignals_H_
+#define liblldb_FreeBSDSignals_H_
+
+#include "lldb/Target/UnixSignals.h"
+
+namespace lldb_private {
+
+/// FreeBSD specific set of Unix signals.
+class FreeBSDSignals : public UnixSignals {
+public:
+ FreeBSDSignals();
+
+private:
+ void Reset() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_FreeBSDSignals_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.cpp
new file mode 100644
index 000000000000..ed35273ce3fe
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.cpp
@@ -0,0 +1,18 @@
+//===-- GDBRemoteSignals.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 "GDBRemoteSignals.h"
+
+using namespace lldb_private;
+
+GDBRemoteSignals::GDBRemoteSignals() : UnixSignals() { Reset(); }
+
+GDBRemoteSignals::GDBRemoteSignals(const lldb::UnixSignalsSP &rhs)
+ : UnixSignals(*rhs) {}
+
+void GDBRemoteSignals::Reset() { m_signals.clear(); }
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.h
new file mode 100644
index 000000000000..a02dd0604e67
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.h
@@ -0,0 +1,29 @@
+//===-- GDBRemoteSignals.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 liblldb_GDBRemoteSignals_H_
+#define liblldb_GDBRemoteSignals_H_
+
+#include "lldb/Target/UnixSignals.h"
+
+namespace lldb_private {
+
+/// Empty set of Unix signals to be filled by PlatformRemoteGDBServer
+class GDBRemoteSignals : public UnixSignals {
+public:
+ GDBRemoteSignals();
+
+ GDBRemoteSignals(const lldb::UnixSignalsSP &rhs);
+
+private:
+ void Reset() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_GDBRemoteSignals_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryThread.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryThread.cpp
new file mode 100644
index 000000000000..3cb583172623
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryThread.cpp
@@ -0,0 +1,84 @@
+//===-- HistoryThread.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 "lldb/lldb-private.h"
+
+#include "Plugins/Process/Utility/HistoryThread.h"
+
+#include "Plugins/Process/Utility/HistoryUnwind.h"
+#include "Plugins/Process/Utility/RegisterContextHistory.h"
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrameList.h"
+#include "lldb/Utility/Log.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Constructor
+
+HistoryThread::HistoryThread(lldb_private::Process &process, lldb::tid_t tid,
+ std::vector<lldb::addr_t> pcs)
+ : Thread(process, tid, true), m_framelist_mutex(), m_framelist(),
+ m_pcs(pcs), m_extended_unwind_token(LLDB_INVALID_ADDRESS), m_queue_name(),
+ m_thread_name(), m_originating_unique_thread_id(tid),
+ m_queue_id(LLDB_INVALID_QUEUE_ID) {
+ m_unwinder_up.reset(new HistoryUnwind(*this, pcs));
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf("%p HistoryThread::HistoryThread", static_cast<void *>(this));
+}
+
+// Destructor
+
+HistoryThread::~HistoryThread() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf("%p HistoryThread::~HistoryThread (tid=0x%" PRIx64 ")",
+ static_cast<void *>(this), GetID());
+ DestroyThread();
+}
+
+lldb::RegisterContextSP HistoryThread::GetRegisterContext() {
+ RegisterContextSP rctx;
+ if (m_pcs.size() > 0) {
+ rctx = std::make_shared<RegisterContextHistory>(
+ *this, 0, GetProcess()->GetAddressByteSize(), m_pcs[0]);
+ }
+ return rctx;
+}
+
+lldb::RegisterContextSP
+HistoryThread::CreateRegisterContextForFrame(StackFrame *frame) {
+ return m_unwinder_up->CreateRegisterContextForFrame(frame);
+}
+
+lldb::StackFrameListSP HistoryThread::GetStackFrameList() {
+ // FIXME do not throw away the lock after we acquire it..
+ std::unique_lock<std::mutex> lock(m_framelist_mutex);
+ lock.unlock();
+ if (m_framelist.get() == nullptr) {
+ m_framelist =
+ std::make_shared<StackFrameList>(*this, StackFrameListSP(), true);
+ }
+
+ return m_framelist;
+}
+
+uint32_t HistoryThread::GetExtendedBacktraceOriginatingIndexID() {
+ if (m_originating_unique_thread_id != LLDB_INVALID_THREAD_ID) {
+ if (GetProcess()->HasAssignedIndexIDToThread(
+ m_originating_unique_thread_id)) {
+ return GetProcess()->AssignIndexIDToThread(
+ m_originating_unique_thread_id);
+ }
+ }
+ return LLDB_INVALID_THREAD_ID;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryThread.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryThread.h
new file mode 100644
index 000000000000..1e2658640172
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryThread.h
@@ -0,0 +1,91 @@
+//===-- HistoryThread.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 liblldb_HistoryThread_h_
+#define liblldb_HistoryThread_h_
+
+#include <mutex>
+
+#include "lldb/Core/UserSettingsController.h"
+#include "lldb/Target/ExecutionContextScope.h"
+#include "lldb/Target/StackFrameList.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Broadcaster.h"
+#include "lldb/Utility/Event.h"
+#include "lldb/Utility/UserID.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+/// \class HistoryThread HistoryThread.h "HistoryThread.h"
+/// A thread object representing a backtrace from a previous point in the
+/// process execution
+///
+/// This subclass of Thread is used to provide a backtrace from earlier in
+/// process execution. It is given a backtrace list of pc addresses and it
+/// will create stack frames for them.
+
+class HistoryThread : public lldb_private::Thread {
+public:
+ HistoryThread(lldb_private::Process &process, lldb::tid_t tid,
+ std::vector<lldb::addr_t> pcs);
+
+ ~HistoryThread() override;
+
+ lldb::RegisterContextSP GetRegisterContext() override;
+
+ lldb::RegisterContextSP
+ CreateRegisterContextForFrame(StackFrame *frame) override;
+
+ void RefreshStateAfterStop() override {}
+
+ bool CalculateStopInfo() override { return false; }
+
+ void SetExtendedBacktraceToken(uint64_t token) override {
+ m_extended_unwind_token = token;
+ }
+
+ uint64_t GetExtendedBacktraceToken() override {
+ return m_extended_unwind_token;
+ }
+
+ const char *GetQueueName() override { return m_queue_name.c_str(); }
+
+ void SetQueueName(const char *name) override { m_queue_name = name; }
+
+ lldb::queue_id_t GetQueueID() override { return m_queue_id; }
+
+ void SetQueueID(lldb::queue_id_t queue) override { m_queue_id = queue; }
+
+ const char *GetThreadName() { return m_thread_name.c_str(); }
+
+ uint32_t GetExtendedBacktraceOriginatingIndexID() override;
+
+ void SetThreadName(const char *name) { m_thread_name = name; }
+
+ const char *GetName() override { return m_thread_name.c_str(); }
+
+ void SetName(const char *name) override { m_thread_name = name; }
+
+protected:
+ virtual lldb::StackFrameListSP GetStackFrameList();
+
+ mutable std::mutex m_framelist_mutex;
+ lldb::StackFrameListSP m_framelist;
+ std::vector<lldb::addr_t> m_pcs;
+
+ uint64_t m_extended_unwind_token;
+ std::string m_queue_name;
+ std::string m_thread_name;
+ lldb::tid_t m_originating_unique_thread_id;
+ lldb::queue_id_t m_queue_id;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_HistoryThread_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryUnwind.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryUnwind.cpp
new file mode 100644
index 000000000000..7d473bff8200
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryUnwind.cpp
@@ -0,0 +1,66 @@
+//===-- HistoryUnwind.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 "lldb/lldb-private.h"
+
+#include "Plugins/Process/Utility/HistoryUnwind.h"
+#include "Plugins/Process/Utility/RegisterContextHistory.h"
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Constructor
+
+HistoryUnwind::HistoryUnwind(Thread &thread, std::vector<lldb::addr_t> pcs)
+ : Unwind(thread), m_pcs(pcs) {}
+
+// Destructor
+
+HistoryUnwind::~HistoryUnwind() {}
+
+void HistoryUnwind::DoClear() {
+ std::lock_guard<std::recursive_mutex> guard(m_unwind_mutex);
+ m_pcs.clear();
+}
+
+lldb::RegisterContextSP
+HistoryUnwind::DoCreateRegisterContextForFrame(StackFrame *frame) {
+ RegisterContextSP rctx;
+ if (frame) {
+ addr_t pc = frame->GetFrameCodeAddress().GetLoadAddress(
+ &frame->GetThread()->GetProcess()->GetTarget());
+ if (pc != LLDB_INVALID_ADDRESS) {
+ rctx = std::make_shared<RegisterContextHistory>(
+ *frame->GetThread().get(), frame->GetConcreteFrameIndex(),
+ frame->GetThread()->GetProcess()->GetAddressByteSize(), pc);
+ }
+ }
+ return rctx;
+}
+
+bool HistoryUnwind::DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa,
+ lldb::addr_t &pc) {
+ // FIXME do not throw away the lock after we acquire it..
+ std::unique_lock<std::recursive_mutex> guard(m_unwind_mutex);
+ guard.unlock();
+ if (frame_idx < m_pcs.size()) {
+ cfa = frame_idx;
+ pc = m_pcs[frame_idx];
+ return true;
+ }
+ return false;
+}
+
+uint32_t HistoryUnwind::DoGetFrameCount() { return m_pcs.size(); }
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryUnwind.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryUnwind.h
new file mode 100644
index 000000000000..6c4522e6b35b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryUnwind.h
@@ -0,0 +1,41 @@
+//===-- HistoryUnwind.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 liblldb_HistoryUnwind_h_
+#define liblldb_HistoryUnwind_h_
+
+#include <vector>
+
+#include "lldb/Target/Unwind.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class HistoryUnwind : public lldb_private::Unwind {
+public:
+ HistoryUnwind(Thread &thread, std::vector<lldb::addr_t> pcs);
+
+ ~HistoryUnwind() override;
+
+protected:
+ void DoClear() override;
+
+ lldb::RegisterContextSP
+ DoCreateRegisterContextForFrame(StackFrame *frame) override;
+
+ bool DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa,
+ lldb::addr_t &pc) override;
+ uint32_t DoGetFrameCount() override;
+
+private:
+ std::vector<lldb::addr_t> m_pcs;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_HistoryUnwind_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp
new file mode 100644
index 000000000000..9beaf2fc7ac8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp
@@ -0,0 +1,237 @@
+//===-- InferiorCallPOSIX.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 "InferiorCallPOSIX.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Host/Config.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadPlanCallFunction.h"
+
+#ifndef LLDB_DISABLE_POSIX
+#include <sys/mman.h>
+#else
+// define them
+#define PROT_NONE 0
+#define PROT_READ 1
+#define PROT_WRITE 2
+#define PROT_EXEC 4
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+
+bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr,
+ addr_t addr, addr_t length, unsigned prot,
+ unsigned flags, addr_t fd, addr_t offset) {
+ Thread *thread =
+ process->GetThreadList().GetExpressionExecutionThread().get();
+ if (thread == nullptr)
+ return false;
+
+ const bool append = true;
+ const bool include_symbols = true;
+ const bool include_inlines = false;
+ SymbolContextList sc_list;
+ const uint32_t count = process->GetTarget().GetImages().FindFunctions(
+ ConstString("mmap"), eFunctionNameTypeFull, include_symbols,
+ include_inlines, append, sc_list);
+ if (count > 0) {
+ SymbolContext sc;
+ if (sc_list.GetContextAtIndex(0, sc)) {
+ const uint32_t range_scope =
+ eSymbolContextFunction | eSymbolContextSymbol;
+ const bool use_inline_block_range = false;
+ EvaluateExpressionOptions options;
+ options.SetStopOthers(true);
+ options.SetUnwindOnError(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetTryAllThreads(true);
+ options.SetDebug(false);
+ options.SetTimeout(process->GetUtilityExpressionTimeout());
+ options.SetTrapExceptions(false);
+
+ addr_t prot_arg;
+ if (prot == eMmapProtNone)
+ prot_arg = PROT_NONE;
+ else {
+ prot_arg = 0;
+ if (prot & eMmapProtExec)
+ prot_arg |= PROT_EXEC;
+ if (prot & eMmapProtRead)
+ prot_arg |= PROT_READ;
+ if (prot & eMmapProtWrite)
+ prot_arg |= PROT_WRITE;
+ }
+
+ AddressRange mmap_range;
+ if (sc.GetAddressRange(range_scope, 0, use_inline_block_range,
+ mmap_range)) {
+ ClangASTContext *clang_ast_context =
+ process->GetTarget().GetScratchClangASTContext();
+ CompilerType clang_void_ptr_type =
+ clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
+ const ArchSpec arch = process->GetTarget().GetArchitecture();
+ MmapArgList args =
+ process->GetTarget().GetPlatform()->GetMmapArgumentList(
+ arch, addr, length, prot_arg, flags, fd, offset);
+ lldb::ThreadPlanSP call_plan_sp(
+ new ThreadPlanCallFunction(*thread, mmap_range.GetBaseAddress(),
+ clang_void_ptr_type, args, options));
+ if (call_plan_sp) {
+ DiagnosticManager diagnostics;
+
+ StackFrame *frame = thread->GetStackFrameAtIndex(0).get();
+ if (frame) {
+ ExecutionContext exe_ctx;
+ frame->CalculateExecutionContext(exe_ctx);
+ ExpressionResults result = process->RunThreadPlan(
+ exe_ctx, call_plan_sp, options, diagnostics);
+ if (result == eExpressionCompleted) {
+
+ allocated_addr =
+ call_plan_sp->GetReturnValueObject()->GetValueAsUnsigned(
+ LLDB_INVALID_ADDRESS);
+ if (process->GetAddressByteSize() == 4) {
+ if (allocated_addr == UINT32_MAX)
+ return false;
+ } else if (process->GetAddressByteSize() == 8) {
+ if (allocated_addr == UINT64_MAX)
+ return false;
+ }
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+bool lldb_private::InferiorCallMunmap(Process *process, addr_t addr,
+ addr_t length) {
+ Thread *thread =
+ process->GetThreadList().GetExpressionExecutionThread().get();
+ if (thread == nullptr)
+ return false;
+
+ const bool append = true;
+ const bool include_symbols = true;
+ const bool include_inlines = false;
+ SymbolContextList sc_list;
+ const uint32_t count = process->GetTarget().GetImages().FindFunctions(
+ ConstString("munmap"), eFunctionNameTypeFull, include_symbols,
+ include_inlines, append, sc_list);
+ if (count > 0) {
+ SymbolContext sc;
+ if (sc_list.GetContextAtIndex(0, sc)) {
+ const uint32_t range_scope =
+ eSymbolContextFunction | eSymbolContextSymbol;
+ const bool use_inline_block_range = false;
+ EvaluateExpressionOptions options;
+ options.SetStopOthers(true);
+ options.SetUnwindOnError(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetTryAllThreads(true);
+ options.SetDebug(false);
+ options.SetTimeout(process->GetUtilityExpressionTimeout());
+ options.SetTrapExceptions(false);
+
+ AddressRange munmap_range;
+ if (sc.GetAddressRange(range_scope, 0, use_inline_block_range,
+ munmap_range)) {
+ lldb::addr_t args[] = {addr, length};
+ lldb::ThreadPlanSP call_plan_sp(
+ new ThreadPlanCallFunction(*thread, munmap_range.GetBaseAddress(),
+ CompilerType(), args, options));
+ if (call_plan_sp) {
+ DiagnosticManager diagnostics;
+
+ StackFrame *frame = thread->GetStackFrameAtIndex(0).get();
+ if (frame) {
+ ExecutionContext exe_ctx;
+ frame->CalculateExecutionContext(exe_ctx);
+ ExpressionResults result = process->RunThreadPlan(
+ exe_ctx, call_plan_sp, options, diagnostics);
+ if (result == eExpressionCompleted) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+// FIXME: This has nothing to do with Posix, it is just a convenience function
+// that calls a
+// function of the form "void * (*)(void)". We should find a better place to
+// put this.
+
+bool lldb_private::InferiorCall(Process *process, const Address *address,
+ addr_t &returned_func, bool trap_exceptions) {
+ Thread *thread =
+ process->GetThreadList().GetExpressionExecutionThread().get();
+ if (thread == nullptr || address == nullptr)
+ return false;
+
+ EvaluateExpressionOptions options;
+ options.SetStopOthers(true);
+ options.SetUnwindOnError(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetTryAllThreads(true);
+ options.SetDebug(false);
+ options.SetTimeout(process->GetUtilityExpressionTimeout());
+ options.SetTrapExceptions(trap_exceptions);
+
+ ClangASTContext *clang_ast_context =
+ process->GetTarget().GetScratchClangASTContext();
+ CompilerType clang_void_ptr_type =
+ clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
+ lldb::ThreadPlanSP call_plan_sp(
+ new ThreadPlanCallFunction(*thread, *address, clang_void_ptr_type,
+ llvm::ArrayRef<addr_t>(), options));
+ if (call_plan_sp) {
+ DiagnosticManager diagnostics;
+
+ StackFrame *frame = thread->GetStackFrameAtIndex(0).get();
+ if (frame) {
+ ExecutionContext exe_ctx;
+ frame->CalculateExecutionContext(exe_ctx);
+ ExpressionResults result =
+ process->RunThreadPlan(exe_ctx, call_plan_sp, options, diagnostics);
+ if (result == eExpressionCompleted) {
+ returned_func =
+ call_plan_sp->GetReturnValueObject()->GetValueAsUnsigned(
+ LLDB_INVALID_ADDRESS);
+
+ if (process->GetAddressByteSize() == 4) {
+ if (returned_func == UINT32_MAX)
+ return false;
+ } else if (process->GetAddressByteSize() == 8) {
+ if (returned_func == UINT64_MAX)
+ return false;
+ }
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.h
new file mode 100644
index 000000000000..04316801b351
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.h
@@ -0,0 +1,38 @@
+//===-- InferiorCallPOSIX.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_InferiorCallPOSIX_h_
+#define lldb_InferiorCallPOSIX_h_
+
+// Inferior execution of POSIX functions.
+
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+
+class Process;
+
+enum MmapProt {
+ eMmapProtNone = 0,
+ eMmapProtExec = 1,
+ eMmapProtRead = 2,
+ eMmapProtWrite = 4
+};
+
+bool InferiorCallMmap(Process *proc, lldb::addr_t &allocated_addr,
+ lldb::addr_t addr, lldb::addr_t length, unsigned prot,
+ unsigned flags, lldb::addr_t fd, lldb::addr_t offset);
+
+bool InferiorCallMunmap(Process *proc, lldb::addr_t addr, lldb::addr_t length);
+
+bool InferiorCall(Process *proc, const Address *address,
+ lldb::addr_t &returned_func, bool trap_exceptions = false);
+
+} // namespace lldb_private
+
+#endif // lldb_InferiorCallPOSIX_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InstructionUtils.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InstructionUtils.h
new file mode 100644
index 000000000000..f74933e691ee
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InstructionUtils.h
@@ -0,0 +1,116 @@
+//===-- InstructionUtils.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_InstructionUtils_h_
+#define lldb_InstructionUtils_h_
+
+#include <cassert>
+#include <cstdint>
+
+// Common utilities for manipulating instruction bit fields.
+
+namespace lldb_private {
+
+// Return the bit field(s) from the most significant bit (msbit) to the
+// least significant bit (lsbit) of a 64-bit unsigned value.
+static inline uint64_t Bits64(const uint64_t bits, const uint32_t msbit,
+ const uint32_t lsbit) {
+ assert(msbit < 64 && lsbit <= msbit);
+ return (bits >> lsbit) & ((1ull << (msbit - lsbit + 1)) - 1);
+}
+
+// Return the bit field(s) from the most significant bit (msbit) to the
+// least significant bit (lsbit) of a 32-bit unsigned value.
+static inline uint32_t Bits32(const uint32_t bits, const uint32_t msbit,
+ const uint32_t lsbit) {
+ assert(msbit < 32 && lsbit <= msbit);
+ return (bits >> lsbit) & ((1u << (msbit - lsbit + 1)) - 1);
+}
+
+// Return the bit value from the 'bit' position of a 32-bit unsigned value.
+static inline uint32_t Bit32(const uint32_t bits, const uint32_t bit) {
+ return (bits >> bit) & 1u;
+}
+
+static inline uint64_t Bit64(const uint64_t bits, const uint32_t bit) {
+ return (bits >> bit) & 1ull;
+}
+
+// Set the bit field(s) from the most significant bit (msbit) to the
+// least significant bit (lsbit) of a 32-bit unsigned value to 'val'.
+static inline void SetBits32(uint32_t &bits, const uint32_t msbit,
+ const uint32_t lsbit, const uint32_t val) {
+ assert(msbit < 32 && lsbit < 32 && msbit >= lsbit);
+ uint32_t mask = ((1u << (msbit - lsbit + 1)) - 1);
+ bits &= ~(mask << lsbit);
+ bits |= (val & mask) << lsbit;
+}
+
+// Set the 'bit' position of a 32-bit unsigned value to 'val'.
+static inline void SetBit32(uint32_t &bits, const uint32_t bit,
+ const uint32_t val) {
+ SetBits32(bits, bit, bit, val);
+}
+
+// Rotate a 32-bit unsigned value right by the specified amount.
+static inline uint32_t Rotr32(uint32_t bits, uint32_t amt) {
+ assert(amt < 32 && "Invalid rotate amount");
+ return (bits >> amt) | (bits << ((32 - amt) & 31));
+}
+
+// Rotate a 32-bit unsigned value left by the specified amount.
+static inline uint32_t Rotl32(uint32_t bits, uint32_t amt) {
+ assert(amt < 32 && "Invalid rotate amount");
+ return (bits << amt) | (bits >> ((32 - amt) & 31));
+}
+
+// Create a mask that starts at bit zero and includes "bit"
+static inline uint64_t MaskUpToBit(const uint64_t bit) {
+ if (bit >= 63)
+ return -1ll;
+ return (1ull << (bit + 1ull)) - 1ull;
+}
+
+// Return an integer result equal to the number of bits of x that are ones.
+static inline uint32_t BitCount(uint64_t x) {
+ // c accumulates the total bits set in x
+ uint32_t c;
+ for (c = 0; x; ++c) {
+ x &= x - 1; // clear the least significant bit set
+ }
+ return c;
+}
+
+static inline bool BitIsSet(const uint64_t value, const uint64_t bit) {
+ return (value & (1ull << bit)) != 0;
+}
+
+static inline bool BitIsClear(const uint64_t value, const uint64_t bit) {
+ return (value & (1ull << bit)) == 0;
+}
+
+static inline uint64_t UnsignedBits(const uint64_t value, const uint64_t msbit,
+ const uint64_t lsbit) {
+ uint64_t result = value >> lsbit;
+ result &= MaskUpToBit(msbit - lsbit);
+ return result;
+}
+
+static inline int64_t SignedBits(const uint64_t value, const uint64_t msbit,
+ const uint64_t lsbit) {
+ uint64_t result = UnsignedBits(value, msbit, lsbit);
+ if (BitIsSet(value, msbit)) {
+ // Sign extend
+ result |= ~MaskUpToBit(msbit - lsbit);
+ }
+ return result;
+}
+
+} // namespace lldb_private
+
+#endif // lldb_InstructionUtils_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.cpp
new file mode 100644
index 000000000000..1ba432aa542b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.cpp
@@ -0,0 +1,112 @@
+//===-- LinuxProcMaps.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 "LinuxProcMaps.h"
+#include "llvm/ADT/StringRef.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StringExtractor.h"
+
+using namespace lldb_private;
+
+static Status
+ParseMemoryRegionInfoFromProcMapsLine(llvm::StringRef maps_line,
+ MemoryRegionInfo &memory_region_info) {
+ memory_region_info.Clear();
+
+ StringExtractor line_extractor(maps_line);
+
+ // Format: {address_start_hex}-{address_end_hex} perms offset dev inode
+ // pathname perms: rwxp (letter is present if set, '-' if not, final
+ // character is p=private, s=shared).
+
+ // Parse out the starting address
+ lldb::addr_t start_address = line_extractor.GetHexMaxU64(false, 0);
+
+ // Parse out hyphen separating start and end address from range.
+ if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != '-'))
+ return Status(
+ "malformed /proc/{pid}/maps entry, missing dash between address range");
+
+ // Parse out the ending address
+ lldb::addr_t end_address = line_extractor.GetHexMaxU64(false, start_address);
+
+ // Parse out the space after the address.
+ if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != ' '))
+ return Status(
+ "malformed /proc/{pid}/maps entry, missing space after range");
+
+ // Save the range.
+ memory_region_info.GetRange().SetRangeBase(start_address);
+ memory_region_info.GetRange().SetRangeEnd(end_address);
+
+ // Any memory region in /proc/{pid}/maps is by definition mapped into the
+ // process.
+ memory_region_info.SetMapped(MemoryRegionInfo::OptionalBool::eYes);
+
+ // Parse out each permission entry.
+ if (line_extractor.GetBytesLeft() < 4)
+ return Status("malformed /proc/{pid}/maps entry, missing some portion of "
+ "permissions");
+
+ // Handle read permission.
+ const char read_perm_char = line_extractor.GetChar();
+ if (read_perm_char == 'r')
+ memory_region_info.SetReadable(MemoryRegionInfo::OptionalBool::eYes);
+ else if (read_perm_char == '-')
+ memory_region_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo);
+ else
+ return Status("unexpected /proc/{pid}/maps read permission char");
+
+ // Handle write permission.
+ const char write_perm_char = line_extractor.GetChar();
+ if (write_perm_char == 'w')
+ memory_region_info.SetWritable(MemoryRegionInfo::OptionalBool::eYes);
+ else if (write_perm_char == '-')
+ memory_region_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo);
+ else
+ return Status("unexpected /proc/{pid}/maps write permission char");
+
+ // Handle execute permission.
+ const char exec_perm_char = line_extractor.GetChar();
+ if (exec_perm_char == 'x')
+ memory_region_info.SetExecutable(MemoryRegionInfo::OptionalBool::eYes);
+ else if (exec_perm_char == '-')
+ memory_region_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo);
+ else
+ return Status("unexpected /proc/{pid}/maps exec permission char");
+
+ line_extractor.GetChar(); // Read the private bit
+ line_extractor.SkipSpaces(); // Skip the separator
+ line_extractor.GetHexMaxU64(false, 0); // Read the offset
+ line_extractor.GetHexMaxU64(false, 0); // Read the major device number
+ line_extractor.GetChar(); // Read the device id separator
+ line_extractor.GetHexMaxU64(false, 0); // Read the major device number
+ line_extractor.SkipSpaces(); // Skip the separator
+ line_extractor.GetU64(0, 10); // Read the inode number
+
+ line_extractor.SkipSpaces();
+ const char *name = line_extractor.Peek();
+ if (name)
+ memory_region_info.SetName(name);
+
+ return Status();
+}
+
+void lldb_private::ParseLinuxMapRegions(llvm::StringRef linux_map,
+ LinuxMapCallback const &callback) {
+ llvm::StringRef lines(linux_map);
+ llvm::StringRef line;
+ while (!lines.empty()) {
+ std::tie(line, lines) = lines.split('\n');
+ MemoryRegionInfo region;
+ Status error = ParseMemoryRegionInfoFromProcMapsLine(line, region);
+ if (!callback(region, error))
+ break;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.h
new file mode 100644
index 000000000000..e1f0e48ac5c9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.h
@@ -0,0 +1,27 @@
+//===-- LinuxProcMaps.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 liblldb_LinuxProcMaps_H_
+#define liblldb_LinuxProcMaps_H_
+
+#include "lldb/lldb-forward.h"
+#include "llvm/ADT/StringRef.h"
+#include <functional>
+
+
+namespace lldb_private {
+
+typedef std::function<bool(const lldb_private::MemoryRegionInfo &,
+ const lldb_private::Status &)> LinuxMapCallback;
+
+void ParseLinuxMapRegions(llvm::StringRef linux_map,
+ LinuxMapCallback const &callback);
+
+} // namespace lldb_private
+
+#endif // liblldb_LinuxProcMaps_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxSignals.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxSignals.cpp
new file mode 100644
index 000000000000..bef47cd26307
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxSignals.cpp
@@ -0,0 +1,92 @@
+//===-- LinuxSignals.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 "LinuxSignals.h"
+
+using namespace lldb_private;
+
+LinuxSignals::LinuxSignals() : UnixSignals() { Reset(); }
+
+void LinuxSignals::Reset() {
+ m_signals.clear();
+ // SIGNO NAME SUPPRESS STOP NOTIFY DESCRIPTION ALIAS
+ // ===== =========== ======== ===== ======
+ // ====================================== ======
+ AddSignal(1, "SIGHUP", false, true, true, "hangup");
+ AddSignal(2, "SIGINT", true, true, true, "interrupt");
+ AddSignal(3, "SIGQUIT", false, true, true, "quit");
+ AddSignal(4, "SIGILL", false, true, true, "illegal instruction");
+ AddSignal(5, "SIGTRAP", true, true, true,
+ "trace trap (not reset when caught)");
+ AddSignal(6, "SIGABRT", false, true, true, "abort()/IOT trap", "SIGIOT");
+ AddSignal(7, "SIGBUS", false, true, true, "bus error");
+ AddSignal(8, "SIGFPE", false, true, true, "floating point exception");
+ AddSignal(9, "SIGKILL", false, true, true, "kill");
+ AddSignal(10, "SIGUSR1", false, true, true, "user defined signal 1");
+ AddSignal(11, "SIGSEGV", false, true, true, "segmentation violation");
+ AddSignal(12, "SIGUSR2", false, true, true, "user defined signal 2");
+ AddSignal(13, "SIGPIPE", false, true, true,
+ "write to pipe with reading end closed");
+ AddSignal(14, "SIGALRM", false, false, false, "alarm");
+ AddSignal(15, "SIGTERM", false, true, true, "termination requested");
+ AddSignal(16, "SIGSTKFLT", false, true, true, "stack fault");
+ AddSignal(17, "SIGCHLD", false, false, true, "child status has changed",
+ "SIGCLD");
+ AddSignal(18, "SIGCONT", false, true, true, "process continue");
+ AddSignal(19, "SIGSTOP", true, true, true, "process stop");
+ AddSignal(20, "SIGTSTP", false, true, true, "tty stop");
+ AddSignal(21, "SIGTTIN", false, true, true, "background tty read");
+ AddSignal(22, "SIGTTOU", false, true, true, "background tty write");
+ AddSignal(23, "SIGURG", false, true, true, "urgent data on socket");
+ AddSignal(24, "SIGXCPU", false, true, true, "CPU resource exceeded");
+ AddSignal(25, "SIGXFSZ", false, true, true, "file size limit exceeded");
+ AddSignal(26, "SIGVTALRM", false, true, true, "virtual time alarm");
+ AddSignal(27, "SIGPROF", false, false, false, "profiling time alarm");
+ AddSignal(28, "SIGWINCH", false, true, true, "window size changes");
+ AddSignal(29, "SIGIO", false, true, true, "input/output ready/Pollable event",
+ "SIGPOLL");
+ AddSignal(30, "SIGPWR", false, true, true, "power failure");
+ AddSignal(31, "SIGSYS", false, true, true, "invalid system call");
+ AddSignal(32, "SIG32", false, false, false,
+ "threading library internal signal 1");
+ AddSignal(33, "SIG33", false, false, false,
+ "threading library internal signal 2");
+ AddSignal(34, "SIGRTMIN", false, false, false, "real time signal 0");
+ AddSignal(35, "SIGRTMIN+1", false, false, false, "real time signal 1");
+ AddSignal(36, "SIGRTMIN+2", false, false, false, "real time signal 2");
+ AddSignal(37, "SIGRTMIN+3", false, false, false, "real time signal 3");
+ AddSignal(38, "SIGRTMIN+4", false, false, false, "real time signal 4");
+ AddSignal(39, "SIGRTMIN+5", false, false, false, "real time signal 5");
+ AddSignal(40, "SIGRTMIN+6", false, false, false, "real time signal 6");
+ AddSignal(41, "SIGRTMIN+7", false, false, false, "real time signal 7");
+ AddSignal(42, "SIGRTMIN+8", false, false, false, "real time signal 8");
+ AddSignal(43, "SIGRTMIN+9", false, false, false, "real time signal 9");
+ AddSignal(44, "SIGRTMIN+10", false, false, false, "real time signal 10");
+ AddSignal(45, "SIGRTMIN+11", false, false, false, "real time signal 11");
+ AddSignal(46, "SIGRTMIN+12", false, false, false, "real time signal 12");
+ AddSignal(47, "SIGRTMIN+13", false, false, false, "real time signal 13");
+ AddSignal(48, "SIGRTMIN+14", false, false, false, "real time signal 14");
+ AddSignal(49, "SIGRTMIN+15", false, false, false, "real time signal 15");
+ AddSignal(50, "SIGRTMAX-14", false, false, false,
+ "real time signal 16"); // switching to SIGRTMAX-xxx to match "kill
+ // -l" output
+ AddSignal(51, "SIGRTMAX-13", false, false, false, "real time signal 17");
+ AddSignal(52, "SIGRTMAX-12", false, false, false, "real time signal 18");
+ AddSignal(53, "SIGRTMAX-11", false, false, false, "real time signal 19");
+ AddSignal(54, "SIGRTMAX-10", false, false, false, "real time signal 20");
+ AddSignal(55, "SIGRTMAX-9", false, false, false, "real time signal 21");
+ AddSignal(56, "SIGRTMAX-8", false, false, false, "real time signal 22");
+ AddSignal(57, "SIGRTMAX-7", false, false, false, "real time signal 23");
+ AddSignal(58, "SIGRTMAX-6", false, false, false, "real time signal 24");
+ AddSignal(59, "SIGRTMAX-5", false, false, false, "real time signal 25");
+ AddSignal(60, "SIGRTMAX-4", false, false, false, "real time signal 26");
+ AddSignal(61, "SIGRTMAX-3", false, false, false, "real time signal 27");
+ AddSignal(62, "SIGRTMAX-2", false, false, false, "real time signal 28");
+ AddSignal(63, "SIGRTMAX-1", false, false, false, "real time signal 29");
+ AddSignal(64, "SIGRTMAX", false, false, false, "real time signal 30");
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxSignals.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxSignals.h
new file mode 100644
index 000000000000..7ad8cfcbef68
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxSignals.h
@@ -0,0 +1,27 @@
+//===-- LinuxSignals.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 liblldb_LinuxSignals_H_
+#define liblldb_LinuxSignals_H_
+
+#include "lldb/Target/UnixSignals.h"
+
+namespace lldb_private {
+
+/// Linux specific set of Unix signals.
+class LinuxSignals : public UnixSignals {
+public:
+ LinuxSignals();
+
+private:
+ void Reset() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_LinuxSignals_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/MipsLinuxSignals.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/MipsLinuxSignals.cpp
new file mode 100644
index 000000000000..d8e5426ab5a5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/MipsLinuxSignals.cpp
@@ -0,0 +1,93 @@
+//===-- MipsLinuxSignals.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 "MipsLinuxSignals.h"
+
+using namespace lldb_private;
+
+MipsLinuxSignals::MipsLinuxSignals() : UnixSignals() { Reset(); }
+
+void MipsLinuxSignals::Reset() {
+ m_signals.clear();
+ // SIGNO NAME SUPPRESS STOP NOTIFY DESCRIPTION ALIAS
+ // ===== =========== ======== ===== ======
+ // ====================================== ========
+ AddSignal(1, "SIGHUP", false, true, true, "hangup");
+ AddSignal(2, "SIGINT", true, true, true, "interrupt");
+ AddSignal(3, "SIGQUIT", false, true, true, "quit");
+ AddSignal(4, "SIGILL", false, true, true, "illegal instruction");
+ AddSignal(5, "SIGTRAP", true, true, true,
+ "trace trap (not reset when caught)");
+ AddSignal(6, "SIGABRT", false, true, true, "abort()/IOT trap", "SIGIOT");
+ AddSignal(7, "SIGEMT", false, true, true, "terminate process with core dump");
+ AddSignal(8, "SIGFPE", false, true, true, "floating point exception");
+ AddSignal(9, "SIGKILL", false, true, true, "kill");
+ AddSignal(10, "SIGBUS", false, true, true, "bus error");
+ AddSignal(11, "SIGSEGV", false, true, true, "segmentation violation");
+ AddSignal(12, "SIGSYS", false, true, true, "invalid system call");
+ AddSignal(13, "SIGPIPE", false, true, true,
+ "write to pipe with reading end closed");
+ AddSignal(14, "SIGALRM", false, false, false, "alarm");
+ AddSignal(15, "SIGTERM", false, true, true, "termination requested");
+ AddSignal(16, "SIGUSR1", false, true, true, "user defined signal 1");
+ AddSignal(17, "SIGUSR2", false, true, true, "user defined signal 2");
+ AddSignal(18, "SIGCHLD", false, false, true, "child status has changed",
+ "SIGCLD");
+ AddSignal(19, "SIGPWR", false, true, true, "power failure");
+ AddSignal(20, "SIGWINCH", false, true, true, "window size changes");
+ AddSignal(21, "SIGURG", false, true, true, "urgent data on socket");
+ AddSignal(22, "SIGIO", false, true, true, "input/output ready/Pollable event",
+ "SIGPOLL");
+ AddSignal(23, "SIGSTOP", true, true, true, "process stop");
+ AddSignal(24, "SIGTSTP", false, true, true, "tty stop");
+ AddSignal(25, "SIGCONT", false, true, true, "process continue");
+ AddSignal(26, "SIGTTIN", false, true, true, "background tty read");
+ AddSignal(27, "SIGTTOU", false, true, true, "background tty write");
+ AddSignal(28, "SIGVTALRM", false, true, true, "virtual time alarm");
+ AddSignal(29, "SIGPROF", false, false, false, "profiling time alarm");
+ AddSignal(30, "SIGXCPU", false, true, true, "CPU resource exceeded");
+ AddSignal(31, "SIGXFSZ", false, true, true, "file size limit exceeded");
+ AddSignal(32, "SIG32", false, false, false,
+ "threading library internal signal 1");
+ AddSignal(33, "SIG33", false, false, false,
+ "threading library internal signal 2");
+ AddSignal(34, "SIGRTMIN", false, false, false, "real time signal 0");
+ AddSignal(35, "SIGRTMIN+1", false, false, false, "real time signal 1");
+ AddSignal(36, "SIGRTMIN+2", false, false, false, "real time signal 2");
+ AddSignal(37, "SIGRTMIN+3", false, false, false, "real time signal 3");
+ AddSignal(38, "SIGRTMIN+4", false, false, false, "real time signal 4");
+ AddSignal(39, "SIGRTMIN+5", false, false, false, "real time signal 5");
+ AddSignal(40, "SIGRTMIN+6", false, false, false, "real time signal 6");
+ AddSignal(41, "SIGRTMIN+7", false, false, false, "real time signal 7");
+ AddSignal(42, "SIGRTMIN+8", false, false, false, "real time signal 8");
+ AddSignal(43, "SIGRTMIN+9", false, false, false, "real time signal 9");
+ AddSignal(44, "SIGRTMIN+10", false, false, false, "real time signal 10");
+ AddSignal(45, "SIGRTMIN+11", false, false, false, "real time signal 11");
+ AddSignal(46, "SIGRTMIN+12", false, false, false, "real time signal 12");
+ AddSignal(47, "SIGRTMIN+13", false, false, false, "real time signal 13");
+ AddSignal(48, "SIGRTMIN+14", false, false, false, "real time signal 14");
+ AddSignal(49, "SIGRTMIN+15", false, false, false, "real time signal 15");
+ AddSignal(50, "SIGRTMAX-14", false, false, false,
+ "real time signal 16"); // switching to SIGRTMAX-xxx to match "kill
+ // -l" output
+ AddSignal(51, "SIGRTMAX-13", false, false, false, "real time signal 17");
+ AddSignal(52, "SIGRTMAX-12", false, false, false, "real time signal 18");
+ AddSignal(53, "SIGRTMAX-11", false, false, false, "real time signal 19");
+ AddSignal(54, "SIGRTMAX-10", false, false, false, "real time signal 20");
+ AddSignal(55, "SIGRTMAX-9", false, false, false, "real time signal 21");
+ AddSignal(56, "SIGRTMAX-8", false, false, false, "real time signal 22");
+ AddSignal(57, "SIGRTMAX-7", false, false, false, "real time signal 23");
+ AddSignal(58, "SIGRTMAX-6", false, false, false, "real time signal 24");
+ AddSignal(59, "SIGRTMAX-5", false, false, false, "real time signal 25");
+ AddSignal(60, "SIGRTMAX-4", false, false, false, "real time signal 26");
+ AddSignal(61, "SIGRTMAX-3", false, false, false, "real time signal 27");
+ AddSignal(62, "SIGRTMAX-2", false, false, false, "real time signal 28");
+ AddSignal(63, "SIGRTMAX-1", false, false, false, "real time signal 29");
+ AddSignal(64, "SIGRTMAX", false, false, false, "real time signal 30");
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/MipsLinuxSignals.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/MipsLinuxSignals.h
new file mode 100644
index 000000000000..b5e3ed86f568
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/MipsLinuxSignals.h
@@ -0,0 +1,28 @@
+//===-- MipsLinuxSignals.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 liblldb_MipsLinuxSignals_H_
+#define liblldb_MipsLinuxSignals_H_
+
+#include "lldb/Target/UnixSignals.h"
+
+namespace lldb_private {
+
+/// Linux specific set of Unix signals.
+class MipsLinuxSignals : public UnixSignals {
+public:
+ MipsLinuxSignals();
+
+private:
+ void Reset() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_MipsLinuxSignals_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextRegisterInfo.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextRegisterInfo.cpp
new file mode 100644
index 000000000000..be61cfdd7374
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextRegisterInfo.cpp
@@ -0,0 +1,42 @@
+//===-- NativeRegisterContextRegisterInfo.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 "NativeRegisterContextRegisterInfo.h"
+#include "lldb/lldb-private-forward.h"
+#include "lldb/lldb-types.h"
+
+using namespace lldb_private;
+
+NativeRegisterContextRegisterInfo::NativeRegisterContextRegisterInfo(
+ NativeThreadProtocol &thread,
+ RegisterInfoInterface *register_info_interface)
+ : NativeRegisterContext(thread),
+ m_register_info_interface_up(register_info_interface) {
+ assert(register_info_interface && "null register_info_interface");
+}
+
+uint32_t NativeRegisterContextRegisterInfo::GetRegisterCount() const {
+ return m_register_info_interface_up->GetRegisterCount();
+}
+
+uint32_t NativeRegisterContextRegisterInfo::GetUserRegisterCount() const {
+ return m_register_info_interface_up->GetUserRegisterCount();
+}
+
+const RegisterInfo *NativeRegisterContextRegisterInfo::GetRegisterInfoAtIndex(
+ uint32_t reg_index) const {
+ if (reg_index <= GetRegisterCount())
+ return m_register_info_interface_up->GetRegisterInfo() + reg_index;
+ else
+ return nullptr;
+}
+
+const RegisterInfoInterface &
+NativeRegisterContextRegisterInfo::GetRegisterInfoInterface() const {
+ return *m_register_info_interface_up;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h
new file mode 100644
index 000000000000..b285c477cd96
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h
@@ -0,0 +1,40 @@
+//===-- NativeRegisterContextRegisterInfo.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_PLUGINS_PROCESS_UTIILTY_NATIVE_REGISTER_CONTEXT_REGISTER_INFO
+#define LLDB_PLUGINS_PROCESS_UTIILTY_NATIVE_REGISTER_CONTEXT_REGISTER_INFO
+
+#include <memory>
+
+#include "RegisterInfoInterface.h"
+#include "lldb/Host/common/NativeRegisterContext.h"
+
+namespace lldb_private {
+class NativeRegisterContextRegisterInfo : public NativeRegisterContext {
+public:
+ ///
+ /// Construct a NativeRegisterContextRegisterInfo, taking ownership
+ /// of the register_info_interface pointer.
+ ///
+ NativeRegisterContextRegisterInfo(
+ NativeThreadProtocol &thread,
+ RegisterInfoInterface *register_info_interface);
+
+ uint32_t GetRegisterCount() const override;
+
+ uint32_t GetUserRegisterCount() const override;
+
+ const RegisterInfo *GetRegisterInfoAtIndex(uint32_t reg_index) const override;
+
+ const RegisterInfoInterface &GetRegisterInfoInterface() const;
+
+private:
+ std::unique_ptr<RegisterInfoInterface> m_register_info_interface_up;
+};
+}
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NetBSDSignals.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NetBSDSignals.cpp
new file mode 100644
index 000000000000..29967deb7e9b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NetBSDSignals.cpp
@@ -0,0 +1,53 @@
+//===-- NetBSDSignals.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 "NetBSDSignals.h"
+
+using namespace lldb_private;
+
+NetBSDSignals::NetBSDSignals() : UnixSignals() { Reset(); }
+
+void NetBSDSignals::Reset() {
+ UnixSignals::Reset();
+ // SIGNO NAME SUPPRESS STOP NOTIFY DESCRIPTION
+ // ====== ============ ======== ====== ======
+ // ===================================================
+ AddSignal(32, "SIGPWR", false, true, true,
+ "power fail/restart (not reset when caught)");
+ AddSignal(33, "SIGRTMIN", false, false, false, "real time signal 0");
+ AddSignal(34, "SIGRTMIN+1", false, false, false, "real time signal 1");
+ AddSignal(35, "SIGRTMIN+2", false, false, false, "real time signal 2");
+ AddSignal(36, "SIGRTMIN+3", false, false, false, "real time signal 3");
+ AddSignal(37, "SIGRTMIN+4", false, false, false, "real time signal 4");
+ AddSignal(38, "SIGRTMIN+5", false, false, false, "real time signal 5");
+ AddSignal(39, "SIGRTMIN+6", false, false, false, "real time signal 6");
+ AddSignal(40, "SIGRTMIN+7", false, false, false, "real time signal 7");
+ AddSignal(41, "SIGRTMIN+8", false, false, false, "real time signal 8");
+ AddSignal(42, "SIGRTMIN+9", false, false, false, "real time signal 9");
+ AddSignal(43, "SIGRTMIN+10", false, false, false, "real time signal 10");
+ AddSignal(44, "SIGRTMIN+11", false, false, false, "real time signal 11");
+ AddSignal(45, "SIGRTMIN+12", false, false, false, "real time signal 12");
+ AddSignal(46, "SIGRTMIN+13", false, false, false, "real time signal 13");
+ AddSignal(47, "SIGRTMIN+14", false, false, false, "real time signal 14");
+ AddSignal(48, "SIGRTMIN+15", false, false, false, "real time signal 15");
+ AddSignal(49, "SIGRTMIN-14", false, false, false, "real time signal 16");
+ AddSignal(50, "SIGRTMAX-13", false, false, false, "real time signal 17");
+ AddSignal(51, "SIGRTMAX-12", false, false, false, "real time signal 18");
+ AddSignal(52, "SIGRTMAX-11", false, false, false, "real time signal 19");
+ AddSignal(53, "SIGRTMAX-10", false, false, false, "real time signal 20");
+ AddSignal(54, "SIGRTMAX-9", false, false, false, "real time signal 21");
+ AddSignal(55, "SIGRTMAX-8", false, false, false, "real time signal 22");
+ AddSignal(56, "SIGRTMAX-7", false, false, false, "real time signal 23");
+ AddSignal(57, "SIGRTMAX-6", false, false, false, "real time signal 24");
+ AddSignal(58, "SIGRTMAX-5", false, false, false, "real time signal 25");
+ AddSignal(59, "SIGRTMAX-4", false, false, false, "real time signal 26");
+ AddSignal(60, "SIGRTMAX-3", false, false, false, "real time signal 27");
+ AddSignal(61, "SIGRTMAX-2", false, false, false, "real time signal 28");
+ AddSignal(62, "SIGRTMAX-1", false, false, false, "real time signal 29");
+ AddSignal(63, "SIGRTMAX", false, false, false, "real time signal 30");
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NetBSDSignals.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NetBSDSignals.h
new file mode 100644
index 000000000000..bf7399a89060
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NetBSDSignals.h
@@ -0,0 +1,27 @@
+//===-- NetBSDSignals.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 liblldb_NetBSDSignals_H_
+#define liblldb_NetBSDSignals_H_
+
+#include "lldb/Target/UnixSignals.h"
+
+namespace lldb_private {
+
+/// NetBSD specific set of Unix signals.
+class NetBSDSignals : public UnixSignals {
+public:
+ NetBSDSignals();
+
+private:
+ void Reset() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_NetBSDSignals_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwinConstants.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwinConstants.h
new file mode 100644
index 000000000000..ef40162984f1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwinConstants.h
@@ -0,0 +1,25 @@
+//===-- RegisterContextDarwinConstants.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_REGISTERCONTEXTDARWINCONSTANTS_H
+#define LLDB_REGISTERCONTEXTDARWINCONSTANTS_H
+
+namespace lldb_private {
+
+/// Constants returned by various RegisterContextDarwin_*** functions.
+#ifndef KERN_SUCCESS
+#define KERN_SUCCESS 0
+#endif
+
+#ifndef KERN_INVALID_ARGUMENT
+#define KERN_INVALID_ARGUMENT 4
+#endif
+
+} // namespace lldb_private
+
+#endif // LLDB_REGISTERCONTEXTDARWINCONSTANTS_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm.cpp
new file mode 100644
index 000000000000..e804a4d251f7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm.cpp
@@ -0,0 +1,1757 @@
+//===-- RegisterContextDarwin_arm.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 "RegisterContextDarwin_arm.h"
+#include "RegisterContextDarwinConstants.h"
+
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/Support/Compiler.h"
+
+#include "Plugins/Process/Utility/InstructionUtils.h"
+
+#include <memory>
+
+// Support building against older versions of LLVM, this macro was added
+// recently.
+#ifndef LLVM_EXTENSION
+#define LLVM_EXTENSION
+#endif
+
+#include "Utility/ARM_DWARF_Registers.h"
+#include "Utility/ARM_ehframe_Registers.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum {
+ 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_r13,
+ gpr_sp = gpr_r13,
+ gpr_r14,
+ gpr_lr = gpr_r14,
+ gpr_r15,
+ gpr_pc = gpr_r15,
+ gpr_cpsr,
+
+ fpu_s0,
+ fpu_s1,
+ fpu_s2,
+ fpu_s3,
+ fpu_s4,
+ fpu_s5,
+ fpu_s6,
+ fpu_s7,
+ fpu_s8,
+ fpu_s9,
+ fpu_s10,
+ fpu_s11,
+ fpu_s12,
+ fpu_s13,
+ fpu_s14,
+ fpu_s15,
+ fpu_s16,
+ fpu_s17,
+ fpu_s18,
+ fpu_s19,
+ fpu_s20,
+ fpu_s21,
+ fpu_s22,
+ fpu_s23,
+ fpu_s24,
+ fpu_s25,
+ fpu_s26,
+ fpu_s27,
+ fpu_s28,
+ fpu_s29,
+ fpu_s30,
+ fpu_s31,
+ fpu_fpscr,
+
+ exc_exception,
+ exc_fsr,
+ exc_far,
+
+ dbg_bvr0,
+ dbg_bvr1,
+ dbg_bvr2,
+ dbg_bvr3,
+ dbg_bvr4,
+ dbg_bvr5,
+ dbg_bvr6,
+ dbg_bvr7,
+ dbg_bvr8,
+ dbg_bvr9,
+ dbg_bvr10,
+ dbg_bvr11,
+ dbg_bvr12,
+ dbg_bvr13,
+ dbg_bvr14,
+ dbg_bvr15,
+
+ dbg_bcr0,
+ dbg_bcr1,
+ dbg_bcr2,
+ dbg_bcr3,
+ dbg_bcr4,
+ dbg_bcr5,
+ dbg_bcr6,
+ dbg_bcr7,
+ dbg_bcr8,
+ dbg_bcr9,
+ dbg_bcr10,
+ dbg_bcr11,
+ dbg_bcr12,
+ dbg_bcr13,
+ dbg_bcr14,
+ dbg_bcr15,
+
+ dbg_wvr0,
+ dbg_wvr1,
+ dbg_wvr2,
+ dbg_wvr3,
+ dbg_wvr4,
+ dbg_wvr5,
+ dbg_wvr6,
+ dbg_wvr7,
+ dbg_wvr8,
+ dbg_wvr9,
+ dbg_wvr10,
+ dbg_wvr11,
+ dbg_wvr12,
+ dbg_wvr13,
+ dbg_wvr14,
+ dbg_wvr15,
+
+ dbg_wcr0,
+ dbg_wcr1,
+ dbg_wcr2,
+ dbg_wcr3,
+ dbg_wcr4,
+ dbg_wcr5,
+ dbg_wcr6,
+ dbg_wcr7,
+ dbg_wcr8,
+ dbg_wcr9,
+ dbg_wcr10,
+ dbg_wcr11,
+ dbg_wcr12,
+ dbg_wcr13,
+ dbg_wcr14,
+ dbg_wcr15,
+
+ k_num_registers
+};
+
+#define GPR_OFFSET(idx) ((idx)*4)
+#define FPU_OFFSET(idx) ((idx)*4 + sizeof(RegisterContextDarwin_arm::GPR))
+#define EXC_OFFSET(idx) \
+ ((idx)*4 + sizeof(RegisterContextDarwin_arm::GPR) + \
+ sizeof(RegisterContextDarwin_arm::FPU))
+#define DBG_OFFSET(reg) \
+ ((LLVM_EXTENSION offsetof(RegisterContextDarwin_arm::DBG, reg) + \
+ sizeof(RegisterContextDarwin_arm::GPR) + \
+ sizeof(RegisterContextDarwin_arm::FPU) + \
+ sizeof(RegisterContextDarwin_arm::EXC)))
+
+#define DEFINE_DBG(reg, i) \
+ #reg, NULL, sizeof(((RegisterContextDarwin_arm::DBG *) NULL)->reg[i]), \
+ DBG_OFFSET(reg[i]), eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM }, \
+ nullptr, nullptr, nullptr, 0
+#define REG_CONTEXT_SIZE \
+ (sizeof(RegisterContextDarwin_arm::GPR) + \
+ sizeof(RegisterContextDarwin_arm::FPU) + \
+ sizeof(RegisterContextDarwin_arm::EXC))
+
+static RegisterInfo g_register_infos[] = {
+ // General purpose registers
+ // NAME ALT SZ OFFSET ENCODING FORMAT
+ // EH_FRAME DWARF GENERIC
+ // PROCESS PLUGIN LLDB NATIVE
+ // ====== ======= == ============= ============= ============
+ // =============== =============== =========================
+ // ===================== =============
+ {"r0",
+ nullptr,
+ 4,
+ GPR_OFFSET(0),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r0, dwarf_r0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r0},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r1",
+ nullptr,
+ 4,
+ GPR_OFFSET(1),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r1, dwarf_r1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r1},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r2",
+ nullptr,
+ 4,
+ GPR_OFFSET(2),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r2, dwarf_r2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r2},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r3",
+ nullptr,
+ 4,
+ GPR_OFFSET(3),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r3, dwarf_r3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r3},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r4",
+ nullptr,
+ 4,
+ GPR_OFFSET(4),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r4, dwarf_r4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r4},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r5",
+ nullptr,
+ 4,
+ GPR_OFFSET(5),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r5, dwarf_r5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r5},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r6",
+ nullptr,
+ 4,
+ GPR_OFFSET(6),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r6, dwarf_r6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r6},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r7",
+ nullptr,
+ 4,
+ GPR_OFFSET(7),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM,
+ gpr_r7},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r8",
+ nullptr,
+ 4,
+ GPR_OFFSET(8),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r8, dwarf_r8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r8},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r9",
+ nullptr,
+ 4,
+ GPR_OFFSET(9),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r9, dwarf_r9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r9},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r10",
+ nullptr,
+ 4,
+ GPR_OFFSET(10),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r10, dwarf_r10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ gpr_r10},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r11",
+ nullptr,
+ 4,
+ GPR_OFFSET(11),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r11, dwarf_r11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ gpr_r11},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r12",
+ nullptr,
+ 4,
+ GPR_OFFSET(12),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r12, dwarf_r12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ gpr_r12},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"sp",
+ "r13",
+ 4,
+ GPR_OFFSET(13),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM,
+ gpr_sp},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lr",
+ "r14",
+ 4,
+ GPR_OFFSET(14),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM,
+ gpr_lr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"pc",
+ "r15",
+ 4,
+ GPR_OFFSET(15),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM,
+ gpr_pc},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cpsr",
+ "psr",
+ 4,
+ GPR_OFFSET(16),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM,
+ gpr_cpsr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"s0",
+ nullptr,
+ 4,
+ FPU_OFFSET(0),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s0},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s1",
+ nullptr,
+ 4,
+ FPU_OFFSET(1),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s1},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s2",
+ nullptr,
+ 4,
+ FPU_OFFSET(2),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s2},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s3",
+ nullptr,
+ 4,
+ FPU_OFFSET(3),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s3},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s4",
+ nullptr,
+ 4,
+ FPU_OFFSET(4),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s4},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s5",
+ nullptr,
+ 4,
+ FPU_OFFSET(5),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s5},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s6",
+ nullptr,
+ 4,
+ FPU_OFFSET(6),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s6},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s7",
+ nullptr,
+ 4,
+ FPU_OFFSET(7),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s7},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s8",
+ nullptr,
+ 4,
+ FPU_OFFSET(8),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s8},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s9",
+ nullptr,
+ 4,
+ FPU_OFFSET(9),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s9},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s10",
+ nullptr,
+ 4,
+ FPU_OFFSET(10),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s10},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s11",
+ nullptr,
+ 4,
+ FPU_OFFSET(11),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s11},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s12",
+ nullptr,
+ 4,
+ FPU_OFFSET(12),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s12},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s13",
+ nullptr,
+ 4,
+ FPU_OFFSET(13),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s13},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s14",
+ nullptr,
+ 4,
+ FPU_OFFSET(14),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s14},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s15",
+ nullptr,
+ 4,
+ FPU_OFFSET(15),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s15},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s16",
+ nullptr,
+ 4,
+ FPU_OFFSET(16),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s16},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s17",
+ nullptr,
+ 4,
+ FPU_OFFSET(17),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s17},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s18",
+ nullptr,
+ 4,
+ FPU_OFFSET(18),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s18},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s19",
+ nullptr,
+ 4,
+ FPU_OFFSET(19),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s19},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s20",
+ nullptr,
+ 4,
+ FPU_OFFSET(20),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s20},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s21",
+ nullptr,
+ 4,
+ FPU_OFFSET(21),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s21},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s22",
+ nullptr,
+ 4,
+ FPU_OFFSET(22),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s22},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s23",
+ nullptr,
+ 4,
+ FPU_OFFSET(23),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s23},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s24",
+ nullptr,
+ 4,
+ FPU_OFFSET(24),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s24},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s25",
+ nullptr,
+ 4,
+ FPU_OFFSET(25),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s25},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s26",
+ nullptr,
+ 4,
+ FPU_OFFSET(26),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s26},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s27",
+ nullptr,
+ 4,
+ FPU_OFFSET(27),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s27},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s28",
+ nullptr,
+ 4,
+ FPU_OFFSET(28),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s28},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s29",
+ nullptr,
+ 4,
+ FPU_OFFSET(29),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s29},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s30",
+ nullptr,
+ 4,
+ FPU_OFFSET(30),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s30},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"s31",
+ nullptr,
+ 4,
+ FPU_OFFSET(31),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s31},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fpscr",
+ nullptr,
+ 4,
+ FPU_OFFSET(32),
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_fpscr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"exception",
+ nullptr,
+ 4,
+ EXC_OFFSET(0),
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, exc_exception},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fsr",
+ nullptr,
+ 4,
+ EXC_OFFSET(1),
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, exc_fsr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"far",
+ nullptr,
+ 4,
+ EXC_OFFSET(2),
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, exc_far},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {DEFINE_DBG(bvr, 0)},
+ {DEFINE_DBG(bvr, 1)},
+ {DEFINE_DBG(bvr, 2)},
+ {DEFINE_DBG(bvr, 3)},
+ {DEFINE_DBG(bvr, 4)},
+ {DEFINE_DBG(bvr, 5)},
+ {DEFINE_DBG(bvr, 6)},
+ {DEFINE_DBG(bvr, 7)},
+ {DEFINE_DBG(bvr, 8)},
+ {DEFINE_DBG(bvr, 9)},
+ {DEFINE_DBG(bvr, 10)},
+ {DEFINE_DBG(bvr, 11)},
+ {DEFINE_DBG(bvr, 12)},
+ {DEFINE_DBG(bvr, 13)},
+ {DEFINE_DBG(bvr, 14)},
+ {DEFINE_DBG(bvr, 15)},
+
+ {DEFINE_DBG(bcr, 0)},
+ {DEFINE_DBG(bcr, 1)},
+ {DEFINE_DBG(bcr, 2)},
+ {DEFINE_DBG(bcr, 3)},
+ {DEFINE_DBG(bcr, 4)},
+ {DEFINE_DBG(bcr, 5)},
+ {DEFINE_DBG(bcr, 6)},
+ {DEFINE_DBG(bcr, 7)},
+ {DEFINE_DBG(bcr, 8)},
+ {DEFINE_DBG(bcr, 9)},
+ {DEFINE_DBG(bcr, 10)},
+ {DEFINE_DBG(bcr, 11)},
+ {DEFINE_DBG(bcr, 12)},
+ {DEFINE_DBG(bcr, 13)},
+ {DEFINE_DBG(bcr, 14)},
+ {DEFINE_DBG(bcr, 15)},
+
+ {DEFINE_DBG(wvr, 0)},
+ {DEFINE_DBG(wvr, 1)},
+ {DEFINE_DBG(wvr, 2)},
+ {DEFINE_DBG(wvr, 3)},
+ {DEFINE_DBG(wvr, 4)},
+ {DEFINE_DBG(wvr, 5)},
+ {DEFINE_DBG(wvr, 6)},
+ {DEFINE_DBG(wvr, 7)},
+ {DEFINE_DBG(wvr, 8)},
+ {DEFINE_DBG(wvr, 9)},
+ {DEFINE_DBG(wvr, 10)},
+ {DEFINE_DBG(wvr, 11)},
+ {DEFINE_DBG(wvr, 12)},
+ {DEFINE_DBG(wvr, 13)},
+ {DEFINE_DBG(wvr, 14)},
+ {DEFINE_DBG(wvr, 15)},
+
+ {DEFINE_DBG(wcr, 0)},
+ {DEFINE_DBG(wcr, 1)},
+ {DEFINE_DBG(wcr, 2)},
+ {DEFINE_DBG(wcr, 3)},
+ {DEFINE_DBG(wcr, 4)},
+ {DEFINE_DBG(wcr, 5)},
+ {DEFINE_DBG(wcr, 6)},
+ {DEFINE_DBG(wcr, 7)},
+ {DEFINE_DBG(wcr, 8)},
+ {DEFINE_DBG(wcr, 9)},
+ {DEFINE_DBG(wcr, 10)},
+ {DEFINE_DBG(wcr, 11)},
+ {DEFINE_DBG(wcr, 12)},
+ {DEFINE_DBG(wcr, 13)},
+ {DEFINE_DBG(wcr, 14)},
+ {DEFINE_DBG(wcr, 15)}};
+
+// General purpose registers
+static uint32_t g_gpr_regnums[] = {
+ gpr_r0, 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};
+
+// Floating point registers
+static uint32_t g_fpu_regnums[] = {
+ fpu_s0, fpu_s1, fpu_s2, fpu_s3, fpu_s4, fpu_s5, fpu_s6,
+ fpu_s7, fpu_s8, fpu_s9, fpu_s10, fpu_s11, fpu_s12, fpu_s13,
+ fpu_s14, fpu_s15, fpu_s16, fpu_s17, fpu_s18, fpu_s19, fpu_s20,
+ fpu_s21, fpu_s22, fpu_s23, fpu_s24, fpu_s25, fpu_s26, fpu_s27,
+ fpu_s28, fpu_s29, fpu_s30, fpu_s31, fpu_fpscr,
+};
+
+// Exception registers
+
+static uint32_t g_exc_regnums[] = {
+ exc_exception, exc_fsr, exc_far,
+};
+
+static size_t k_num_register_infos = llvm::array_lengthof(g_register_infos);
+
+RegisterContextDarwin_arm::RegisterContextDarwin_arm(
+ Thread &thread, uint32_t concrete_frame_idx)
+ : RegisterContext(thread, concrete_frame_idx), gpr(), fpu(), exc() {
+ uint32_t i;
+ for (i = 0; i < kNumErrors; i++) {
+ gpr_errs[i] = -1;
+ fpu_errs[i] = -1;
+ exc_errs[i] = -1;
+ }
+}
+
+RegisterContextDarwin_arm::~RegisterContextDarwin_arm() {}
+
+void RegisterContextDarwin_arm::InvalidateAllRegisters() {
+ InvalidateAllRegisterStates();
+}
+
+size_t RegisterContextDarwin_arm::GetRegisterCount() {
+ assert(k_num_register_infos == k_num_registers);
+ return k_num_registers;
+}
+
+const RegisterInfo *
+RegisterContextDarwin_arm::GetRegisterInfoAtIndex(size_t reg) {
+ assert(k_num_register_infos == k_num_registers);
+ if (reg < k_num_registers)
+ return &g_register_infos[reg];
+ return nullptr;
+}
+
+size_t RegisterContextDarwin_arm::GetRegisterInfosCount() {
+ return k_num_register_infos;
+}
+
+const RegisterInfo *RegisterContextDarwin_arm::GetRegisterInfos() {
+ return g_register_infos;
+}
+
+// Number of registers in each register set
+const size_t k_num_gpr_registers = llvm::array_lengthof(g_gpr_regnums);
+const size_t k_num_fpu_registers = llvm::array_lengthof(g_fpu_regnums);
+const size_t k_num_exc_registers = llvm::array_lengthof(g_exc_regnums);
+
+// 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 RegisterSet g_reg_sets[] = {
+ {
+ "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums,
+ },
+ {"Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums},
+ {"Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums}};
+
+const size_t k_num_regsets = llvm::array_lengthof(g_reg_sets);
+
+size_t RegisterContextDarwin_arm::GetRegisterSetCount() {
+ return k_num_regsets;
+}
+
+const RegisterSet *RegisterContextDarwin_arm::GetRegisterSet(size_t reg_set) {
+ if (reg_set < k_num_regsets)
+ return &g_reg_sets[reg_set];
+ return nullptr;
+}
+
+// Register information definitions for 32 bit i386.
+int RegisterContextDarwin_arm::GetSetForNativeRegNum(int reg) {
+ if (reg < fpu_s0)
+ return GPRRegSet;
+ else if (reg < exc_exception)
+ return FPURegSet;
+ else if (reg < k_num_registers)
+ return EXCRegSet;
+ return -1;
+}
+
+int RegisterContextDarwin_arm::ReadGPR(bool force) {
+ int set = GPRRegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadGPR(GetThreadID(), set, gpr));
+ }
+ return GetError(GPRRegSet, Read);
+}
+
+int RegisterContextDarwin_arm::ReadFPU(bool force) {
+ int set = FPURegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadFPU(GetThreadID(), set, fpu));
+ }
+ return GetError(FPURegSet, Read);
+}
+
+int RegisterContextDarwin_arm::ReadEXC(bool force) {
+ int set = EXCRegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadEXC(GetThreadID(), set, exc));
+ }
+ return GetError(EXCRegSet, Read);
+}
+
+int RegisterContextDarwin_arm::ReadDBG(bool force) {
+ int set = DBGRegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadDBG(GetThreadID(), set, dbg));
+ }
+ return GetError(DBGRegSet, Read);
+}
+
+int RegisterContextDarwin_arm::WriteGPR() {
+ int set = GPRRegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError(set, Write, DoWriteGPR(GetThreadID(), set, gpr));
+ SetError(set, Read, -1);
+ return GetError(GPRRegSet, Write);
+}
+
+int RegisterContextDarwin_arm::WriteFPU() {
+ int set = FPURegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError(set, Write, DoWriteFPU(GetThreadID(), set, fpu));
+ SetError(set, Read, -1);
+ return GetError(FPURegSet, Write);
+}
+
+int RegisterContextDarwin_arm::WriteEXC() {
+ int set = EXCRegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError(set, Write, DoWriteEXC(GetThreadID(), set, exc));
+ SetError(set, Read, -1);
+ return GetError(EXCRegSet, Write);
+}
+
+int RegisterContextDarwin_arm::WriteDBG() {
+ int set = DBGRegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError(set, Write, DoWriteDBG(GetThreadID(), set, dbg));
+ SetError(set, Read, -1);
+ return GetError(DBGRegSet, Write);
+}
+
+int RegisterContextDarwin_arm::ReadRegisterSet(uint32_t set, bool force) {
+ switch (set) {
+ case GPRRegSet:
+ return ReadGPR(force);
+ case GPRAltRegSet:
+ return ReadGPR(force);
+ case FPURegSet:
+ return ReadFPU(force);
+ case EXCRegSet:
+ return ReadEXC(force);
+ case DBGRegSet:
+ return ReadDBG(force);
+ default:
+ break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+int RegisterContextDarwin_arm::WriteRegisterSet(uint32_t set) {
+ // Make sure we have a valid context to set.
+ if (RegisterSetIsCached(set)) {
+ switch (set) {
+ case GPRRegSet:
+ return WriteGPR();
+ case GPRAltRegSet:
+ return WriteGPR();
+ case FPURegSet:
+ return WriteFPU();
+ case EXCRegSet:
+ return WriteEXC();
+ case DBGRegSet:
+ return WriteDBG();
+ default:
+ break;
+ }
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+void RegisterContextDarwin_arm::LogDBGRegisters(Log *log, const DBG &dbg) {
+ if (log) {
+ for (uint32_t i = 0; i < 16; i++)
+ log->Printf("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]);
+ }
+}
+
+bool RegisterContextDarwin_arm::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ int set = RegisterContextDarwin_arm::GetSetForNativeRegNum(reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != KERN_SUCCESS)
+ return false;
+
+ switch (reg) {
+ case gpr_r0:
+ case gpr_r1:
+ case gpr_r2:
+ case gpr_r3:
+ case gpr_r4:
+ case gpr_r5:
+ case gpr_r6:
+ case gpr_r7:
+ case gpr_r8:
+ case gpr_r9:
+ case gpr_r10:
+ case gpr_r11:
+ case gpr_r12:
+ case gpr_sp:
+ case gpr_lr:
+ case gpr_pc:
+ case gpr_cpsr:
+ value.SetUInt32(gpr.r[reg - gpr_r0]);
+ break;
+
+ case fpu_s0:
+ case fpu_s1:
+ case fpu_s2:
+ case fpu_s3:
+ case fpu_s4:
+ case fpu_s5:
+ case fpu_s6:
+ case fpu_s7:
+ case fpu_s8:
+ case fpu_s9:
+ case fpu_s10:
+ case fpu_s11:
+ case fpu_s12:
+ case fpu_s13:
+ case fpu_s14:
+ case fpu_s15:
+ case fpu_s16:
+ case fpu_s17:
+ case fpu_s18:
+ case fpu_s19:
+ case fpu_s20:
+ case fpu_s21:
+ case fpu_s22:
+ case fpu_s23:
+ case fpu_s24:
+ case fpu_s25:
+ case fpu_s26:
+ case fpu_s27:
+ case fpu_s28:
+ case fpu_s29:
+ case fpu_s30:
+ case fpu_s31:
+ value.SetUInt32(fpu.floats.s[reg], RegisterValue::eTypeFloat);
+ break;
+
+ case fpu_fpscr:
+ value.SetUInt32(fpu.fpscr);
+ break;
+
+ case exc_exception:
+ value.SetUInt32(exc.exception);
+ break;
+ case exc_fsr:
+ value.SetUInt32(exc.fsr);
+ break;
+ case exc_far:
+ value.SetUInt32(exc.far);
+ break;
+
+ default:
+ value.SetValueToInvalid();
+ return false;
+ }
+ return true;
+}
+
+bool RegisterContextDarwin_arm::WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ int set = GetSetForNativeRegNum(reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != KERN_SUCCESS)
+ return false;
+
+ switch (reg) {
+ case gpr_r0:
+ case gpr_r1:
+ case gpr_r2:
+ case gpr_r3:
+ case gpr_r4:
+ case gpr_r5:
+ case gpr_r6:
+ case gpr_r7:
+ case gpr_r8:
+ case gpr_r9:
+ case gpr_r10:
+ case gpr_r11:
+ case gpr_r12:
+ case gpr_sp:
+ case gpr_lr:
+ case gpr_pc:
+ case gpr_cpsr:
+ gpr.r[reg - gpr_r0] = value.GetAsUInt32();
+ break;
+
+ case fpu_s0:
+ case fpu_s1:
+ case fpu_s2:
+ case fpu_s3:
+ case fpu_s4:
+ case fpu_s5:
+ case fpu_s6:
+ case fpu_s7:
+ case fpu_s8:
+ case fpu_s9:
+ case fpu_s10:
+ case fpu_s11:
+ case fpu_s12:
+ case fpu_s13:
+ case fpu_s14:
+ case fpu_s15:
+ case fpu_s16:
+ case fpu_s17:
+ case fpu_s18:
+ case fpu_s19:
+ case fpu_s20:
+ case fpu_s21:
+ case fpu_s22:
+ case fpu_s23:
+ case fpu_s24:
+ case fpu_s25:
+ case fpu_s26:
+ case fpu_s27:
+ case fpu_s28:
+ case fpu_s29:
+ case fpu_s30:
+ case fpu_s31:
+ fpu.floats.s[reg] = value.GetAsUInt32();
+ break;
+
+ case fpu_fpscr:
+ fpu.fpscr = value.GetAsUInt32();
+ break;
+
+ case exc_exception:
+ exc.exception = value.GetAsUInt32();
+ break;
+ case exc_fsr:
+ exc.fsr = value.GetAsUInt32();
+ break;
+ case exc_far:
+ exc.far = value.GetAsUInt32();
+ break;
+
+ default:
+ return false;
+ }
+ return WriteRegisterSet(set) == KERN_SUCCESS;
+}
+
+bool RegisterContextDarwin_arm::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ data_sp = std::make_shared<DataBufferHeap>(REG_CONTEXT_SIZE, 0);
+ if (data_sp && ReadGPR(false) == KERN_SUCCESS &&
+ ReadFPU(false) == KERN_SUCCESS && ReadEXC(false) == KERN_SUCCESS) {
+ uint8_t *dst = data_sp->GetBytes();
+ ::memcpy(dst, &gpr, sizeof(gpr));
+ dst += sizeof(gpr);
+
+ ::memcpy(dst, &fpu, sizeof(fpu));
+ dst += sizeof(gpr);
+
+ ::memcpy(dst, &exc, sizeof(exc));
+ return true;
+ }
+ return false;
+}
+
+bool RegisterContextDarwin_arm::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) {
+ const uint8_t *src = data_sp->GetBytes();
+ ::memcpy(&gpr, src, sizeof(gpr));
+ src += sizeof(gpr);
+
+ ::memcpy(&fpu, src, sizeof(fpu));
+ src += sizeof(gpr);
+
+ ::memcpy(&exc, src, sizeof(exc));
+ uint32_t success_count = 0;
+ if (WriteGPR() == KERN_SUCCESS)
+ ++success_count;
+ if (WriteFPU() == KERN_SUCCESS)
+ ++success_count;
+ if (WriteEXC() == KERN_SUCCESS)
+ ++success_count;
+ return success_count == 3;
+ }
+ return false;
+}
+
+uint32_t RegisterContextDarwin_arm::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t reg) {
+ if (kind == eRegisterKindGeneric) {
+ switch (reg) {
+ case LLDB_REGNUM_GENERIC_PC:
+ return gpr_pc;
+ case LLDB_REGNUM_GENERIC_SP:
+ return gpr_sp;
+ case LLDB_REGNUM_GENERIC_FP:
+ return gpr_r7;
+ case LLDB_REGNUM_GENERIC_RA:
+ return gpr_lr;
+ case LLDB_REGNUM_GENERIC_FLAGS:
+ return gpr_cpsr;
+ default:
+ break;
+ }
+ } else if (kind == eRegisterKindDWARF) {
+ switch (reg) {
+ case dwarf_r0:
+ return gpr_r0;
+ case dwarf_r1:
+ return gpr_r1;
+ case dwarf_r2:
+ return gpr_r2;
+ case dwarf_r3:
+ return gpr_r3;
+ case dwarf_r4:
+ return gpr_r4;
+ case dwarf_r5:
+ return gpr_r5;
+ case dwarf_r6:
+ return gpr_r6;
+ case dwarf_r7:
+ return gpr_r7;
+ case dwarf_r8:
+ return gpr_r8;
+ case dwarf_r9:
+ return gpr_r9;
+ case dwarf_r10:
+ return gpr_r10;
+ case dwarf_r11:
+ return gpr_r11;
+ case dwarf_r12:
+ return gpr_r12;
+ case dwarf_sp:
+ return gpr_sp;
+ case dwarf_lr:
+ return gpr_lr;
+ case dwarf_pc:
+ return gpr_pc;
+ case dwarf_spsr:
+ return gpr_cpsr;
+
+ case dwarf_s0:
+ return fpu_s0;
+ case dwarf_s1:
+ return fpu_s1;
+ case dwarf_s2:
+ return fpu_s2;
+ case dwarf_s3:
+ return fpu_s3;
+ case dwarf_s4:
+ return fpu_s4;
+ case dwarf_s5:
+ return fpu_s5;
+ case dwarf_s6:
+ return fpu_s6;
+ case dwarf_s7:
+ return fpu_s7;
+ case dwarf_s8:
+ return fpu_s8;
+ case dwarf_s9:
+ return fpu_s9;
+ case dwarf_s10:
+ return fpu_s10;
+ case dwarf_s11:
+ return fpu_s11;
+ case dwarf_s12:
+ return fpu_s12;
+ case dwarf_s13:
+ return fpu_s13;
+ case dwarf_s14:
+ return fpu_s14;
+ case dwarf_s15:
+ return fpu_s15;
+ case dwarf_s16:
+ return fpu_s16;
+ case dwarf_s17:
+ return fpu_s17;
+ case dwarf_s18:
+ return fpu_s18;
+ case dwarf_s19:
+ return fpu_s19;
+ case dwarf_s20:
+ return fpu_s20;
+ case dwarf_s21:
+ return fpu_s21;
+ case dwarf_s22:
+ return fpu_s22;
+ case dwarf_s23:
+ return fpu_s23;
+ case dwarf_s24:
+ return fpu_s24;
+ case dwarf_s25:
+ return fpu_s25;
+ case dwarf_s26:
+ return fpu_s26;
+ case dwarf_s27:
+ return fpu_s27;
+ case dwarf_s28:
+ return fpu_s28;
+ case dwarf_s29:
+ return fpu_s29;
+ case dwarf_s30:
+ return fpu_s30;
+ case dwarf_s31:
+ return fpu_s31;
+
+ default:
+ break;
+ }
+ } else if (kind == eRegisterKindEHFrame) {
+ switch (reg) {
+ case ehframe_r0:
+ return gpr_r0;
+ case ehframe_r1:
+ return gpr_r1;
+ case ehframe_r2:
+ return gpr_r2;
+ case ehframe_r3:
+ return gpr_r3;
+ case ehframe_r4:
+ return gpr_r4;
+ case ehframe_r5:
+ return gpr_r5;
+ case ehframe_r6:
+ return gpr_r6;
+ case ehframe_r7:
+ return gpr_r7;
+ case ehframe_r8:
+ return gpr_r8;
+ case ehframe_r9:
+ return gpr_r9;
+ case ehframe_r10:
+ return gpr_r10;
+ case ehframe_r11:
+ return gpr_r11;
+ case ehframe_r12:
+ return gpr_r12;
+ case ehframe_sp:
+ return gpr_sp;
+ case ehframe_lr:
+ return gpr_lr;
+ case ehframe_pc:
+ return gpr_pc;
+ case ehframe_cpsr:
+ return gpr_cpsr;
+ }
+ } else if (kind == eRegisterKindLLDB) {
+ return reg;
+ }
+ return LLDB_INVALID_REGNUM;
+}
+
+uint32_t RegisterContextDarwin_arm::NumSupportedHardwareBreakpoints() {
+#if defined(__APPLE__) && defined(__arm__)
+ // 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 = UINT32_MAX;
+ if (g_num_supported_hw_breakpoints == UINT32_MAX) {
+ // Set this to zero in case we can't tell if there are any HW breakpoints
+ g_num_supported_hw_breakpoints = 0;
+
+ uint32_t register_DBGDIDR;
+
+ asm("mrc p14, 0, %0, c0, c0, 0" : "=r"(register_DBGDIDR));
+ g_num_supported_hw_breakpoints = Bits32(register_DBGDIDR, 27, 24);
+ // Zero is reserved for the BRP count, so don't increment it if it is zero
+ if (g_num_supported_hw_breakpoints > 0)
+ g_num_supported_hw_breakpoints++;
+ // if (log) log->Printf ("DBGDIDR=0x%8.8x (number BRP pairs = %u)",
+ // register_DBGDIDR, g_num_supported_hw_breakpoints);
+ }
+ return g_num_supported_hw_breakpoints;
+#else
+ // TODO: figure out remote case here!
+ return 6;
+#endif
+}
+
+uint32_t RegisterContextDarwin_arm::SetHardwareBreakpoint(lldb::addr_t addr,
+ size_t size) {
+ // Make sure our address isn't bogus
+ if (addr & 1)
+ return LLDB_INVALID_INDEX32;
+
+ int kret = ReadDBG(false);
+
+ if (kret == KERN_SUCCESS) {
+ const uint32_t num_hw_breakpoints = NumSupportedHardwareBreakpoints();
+ uint32_t i;
+ for (i = 0; i < num_hw_breakpoints; ++i) {
+ if ((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
+ dbg.bvr[i] = addr & ~((lldb::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
+ 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
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm::EnableHardwareBreakpoint(
+ // addr = %8.8p, size = %u ) - BVR%u/BCR%u = 0x%8.8x /
+ // 0x%8.8x (Thumb)",
+ // addr,
+ // size,
+ // i,
+ // i,
+ // dbg.bvr[i],
+ // dbg.bcr[i]);
+ } else if (size == 4) {
+ // We have an ARM breakpoint
+ 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
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm::EnableHardwareBreakpoint(
+ // addr = %8.8p, size = %u ) - BVR%u/BCR%u = 0x%8.8x /
+ // 0x%8.8x (ARM)",
+ // addr,
+ // size,
+ // i,
+ // i,
+ // dbg.bvr[i],
+ // dbg.bcr[i]);
+ }
+
+ kret = WriteDBG();
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm::EnableHardwareBreakpoint()
+ // WriteDBG() => 0x%8.8x.", kret);
+
+ if (kret == KERN_SUCCESS)
+ return i;
+ }
+ // else
+ // {
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm::EnableHardwareBreakpoint(addr =
+ // %8.8p, size = %u) => all hardware breakpoint resources are
+ // being used.", addr, size);
+ // }
+ }
+
+ return LLDB_INVALID_INDEX32;
+}
+
+bool RegisterContextDarwin_arm::ClearHardwareBreakpoint(uint32_t hw_index) {
+ int kret = ReadDBG(false);
+
+ const uint32_t num_hw_points = NumSupportedHardwareBreakpoints();
+ if (kret == KERN_SUCCESS) {
+ if (hw_index < num_hw_points) {
+ dbg.bcr[hw_index] = 0;
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm::SetHardwareBreakpoint( %u ) -
+ // BVR%u = 0x%8.8x BCR%u = 0x%8.8x",
+ // hw_index,
+ // hw_index,
+ // dbg.bvr[hw_index],
+ // hw_index,
+ // dbg.bcr[hw_index]);
+
+ kret = WriteDBG();
+
+ if (kret == KERN_SUCCESS)
+ return true;
+ }
+ }
+ return false;
+}
+
+uint32_t RegisterContextDarwin_arm::NumSupportedHardwareWatchpoints() {
+#if defined(__APPLE__) && defined(__arm__)
+ // 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 = UINT32_MAX;
+ if (g_num_supported_hw_watchpoints == UINT32_MAX) {
+ // Set this to zero in case we can't tell if there are any HW breakpoints
+ g_num_supported_hw_watchpoints = 0;
+
+ uint32_t register_DBGDIDR;
+ asm("mrc p14, 0, %0, c0, c0, 0" : "=r"(register_DBGDIDR));
+ g_num_supported_hw_watchpoints = Bits32(register_DBGDIDR, 31, 28) + 1;
+ // if (log) log->Printf ("DBGDIDR=0x%8.8x (number WRP pairs = %u)",
+ // register_DBGDIDR, g_num_supported_hw_watchpoints);
+ }
+ return g_num_supported_hw_watchpoints;
+#else
+ // TODO: figure out remote case here!
+ return 2;
+#endif
+}
+
+uint32_t RegisterContextDarwin_arm::SetHardwareWatchpoint(lldb::addr_t addr,
+ size_t size,
+ bool read,
+ bool write) {
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm::EnableHardwareWatchpoint(addr = %8.8p, size
+ // = %u, read = %u, write = %u)", addr, size, read, write);
+
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+
+ // Can't watch zero bytes
+ if (size == 0)
+ return LLDB_INVALID_INDEX32;
+
+ // We must watch for either read or write
+ if (!read && !write)
+ return LLDB_INVALID_INDEX32;
+
+ // Can't watch more than 4 bytes per WVR/WCR pair
+ if (size > 4)
+ return LLDB_INVALID_INDEX32;
+
+ // We can only watch up to four bytes that follow a 4 byte aligned address
+ // per watchpoint register pair. Since we have at most so we can only watch
+ // until the next 4 byte boundary and we need to make sure we can properly
+ // encode this.
+ uint32_t addr_word_offset = addr % 4;
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm::EnableHardwareWatchpoint() -
+ // addr_word_offset = 0x%8.8x", addr_word_offset);
+
+ uint32_t byte_mask = ((1u << size) - 1u) << addr_word_offset;
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm::EnableHardwareWatchpoint() - byte_mask =
+ // 0x%8.8x", byte_mask);
+ if (byte_mask > 0xfu)
+ return LLDB_INVALID_INDEX32;
+
+ // Read the debug state
+ int kret = ReadDBG(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 ((dbg.wcr[i] & WCR_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_watchpoints) {
+ // Make the byte_mask into a valid Byte Address Select mask
+ uint32_t byte_address_select = byte_mask << 5;
+ // Make sure bits 1:0 are clear in our address
+ dbg.wvr[i] = addr & ~((lldb::addr_t)3);
+ dbg.wcr[i] = byte_address_select | // Which bytes that follow the IMVA
+ // 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;
+
+ kret = WriteDBG();
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm::EnableHardwareWatchpoint()
+ // WriteDBG() => 0x%8.8x.", kret);
+
+ if (kret == KERN_SUCCESS)
+ return i;
+ } else {
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm::EnableHardwareWatchpoint(): All
+ // hardware resources (%u) are in use.", num_hw_watchpoints);
+ }
+ }
+ return LLDB_INVALID_INDEX32;
+}
+
+bool RegisterContextDarwin_arm::ClearHardwareWatchpoint(uint32_t hw_index) {
+ int kret = ReadDBG(false);
+
+ const uint32_t num_hw_points = NumSupportedHardwareWatchpoints();
+ if (kret == KERN_SUCCESS) {
+ if (hw_index < num_hw_points) {
+ dbg.wcr[hw_index] = 0;
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm::ClearHardwareWatchpoint( %u ) -
+ // WVR%u = 0x%8.8x WCR%u = 0x%8.8x",
+ // hw_index,
+ // hw_index,
+ // dbg.wvr[hw_index],
+ // hw_index,
+ // dbg.wcr[hw_index]);
+
+ kret = WriteDBG();
+
+ if (kret == KERN_SUCCESS)
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm.h
new file mode 100644
index 000000000000..d7c1809a3222
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm.h
@@ -0,0 +1,264 @@
+//===-- RegisterContextDarwin_arm.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 liblldb_RegisterContextDarwin_arm_h_
+#define liblldb_RegisterContextDarwin_arm_h_
+
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/lldb-private.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))
+
+class RegisterContextDarwin_arm : public lldb_private::RegisterContext {
+public:
+ RegisterContextDarwin_arm(lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx);
+
+ ~RegisterContextDarwin_arm() override;
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &reg_value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &reg_value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+ uint32_t NumSupportedHardwareBreakpoints() override;
+
+ uint32_t SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override;
+
+ bool ClearHardwareBreakpoint(uint32_t hw_idx) override;
+
+ uint32_t NumSupportedHardwareWatchpoints() override;
+
+ uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read,
+ bool write) override;
+
+ bool ClearHardwareWatchpoint(uint32_t hw_index) override;
+
+ struct GPR {
+ uint32_t r[16]; // R0-R15
+ uint32_t cpsr; // CPSR
+ };
+
+ struct QReg {
+ uint8_t bytes[16];
+ };
+
+ struct FPU {
+ union {
+ uint32_t s[32];
+ uint64_t d[32];
+ QReg q[16]; // the 128-bit NEON registers
+ } floats;
+ uint32_t fpscr;
+ };
+
+ // struct NeonReg
+ // {
+ // uint8_t bytes[16];
+ // };
+ //
+ // struct VFPv3
+ // {
+ // union {
+ // uint32_t s[32];
+ // uint64_t d[32];
+ // NeonReg q[16];
+ // } v3;
+ // uint32_t fpscr;
+ // };
+
+ struct EXC {
+ uint32_t exception;
+ uint32_t fsr; /* Fault status */
+ uint32_t far; /* Virtual Fault Address */
+ };
+
+ struct DBG {
+ uint32_t bvr[16];
+ uint32_t bcr[16];
+ uint32_t wvr[16];
+ uint32_t wcr[16];
+ };
+
+ static void LogDBGRegisters(lldb_private::Log *log, const DBG &dbg);
+
+protected:
+ enum {
+ GPRRegSet = 1, // ARM_THREAD_STATE
+ GPRAltRegSet = 9, // ARM_THREAD_STATE32
+ FPURegSet = 2, // ARM_VFP_STATE
+ EXCRegSet = 3, // ARM_EXCEPTION_STATE
+ DBGRegSet = 4 // ARM_DEBUG_STATE
+ };
+
+ enum {
+ GPRWordCount = sizeof(GPR) / sizeof(uint32_t),
+ FPUWordCount = sizeof(FPU) / sizeof(uint32_t),
+ EXCWordCount = sizeof(EXC) / sizeof(uint32_t),
+ DBGWordCount = sizeof(DBG) / sizeof(uint32_t)
+ };
+
+ enum { Read = 0, Write = 1, kNumErrors = 2 };
+
+ GPR gpr;
+ FPU fpu;
+ EXC exc;
+ DBG dbg;
+ int gpr_errs[2]; // Read/Write errors
+ int fpu_errs[2]; // Read/Write errors
+ int exc_errs[2]; // Read/Write errors
+ int dbg_errs[2]; // Read/Write errors
+
+ void InvalidateAllRegisterStates() {
+ SetError(GPRRegSet, Read, -1);
+ SetError(FPURegSet, Read, -1);
+ SetError(EXCRegSet, Read, -1);
+ }
+
+ int 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 GPRRegSet:
+ return gpr_errs[err_idx];
+ case FPURegSet:
+ return fpu_errs[err_idx];
+ case EXCRegSet:
+ return exc_errs[err_idx];
+ case DBGRegSet:
+ return dbg_errs[err_idx];
+ default:
+ break;
+ }
+ }
+ return -1;
+ }
+
+ bool SetError(int flavor, uint32_t err_idx, int err) {
+ if (err_idx < kNumErrors) {
+ switch (flavor) {
+ case GPRRegSet:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case FPURegSet:
+ fpu_errs[err_idx] = err;
+ return true;
+
+ case EXCRegSet:
+ exc_errs[err_idx] = err;
+ return true;
+
+ case DBGRegSet:
+ exc_errs[err_idx] = err;
+ return true;
+
+ default:
+ break;
+ }
+ }
+ return false;
+ }
+
+ bool RegisterSetIsCached(int set) const { return GetError(set, Read) == 0; }
+
+ int ReadGPR(bool force);
+
+ int ReadFPU(bool force);
+
+ int ReadEXC(bool force);
+
+ int ReadDBG(bool force);
+
+ int WriteGPR();
+
+ int WriteFPU();
+
+ int WriteEXC();
+
+ int WriteDBG();
+
+ // Subclasses override these to do the actual reading.
+ virtual int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) { return -1; }
+
+ virtual int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) = 0;
+
+ virtual int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) = 0;
+
+ virtual int DoReadDBG(lldb::tid_t tid, int flavor, DBG &dbg) = 0;
+
+ virtual int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) = 0;
+
+ virtual int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) = 0;
+
+ virtual int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) = 0;
+
+ virtual int DoWriteDBG(lldb::tid_t tid, int flavor, const DBG &dbg) = 0;
+
+ int ReadRegisterSet(uint32_t set, bool force);
+
+ int WriteRegisterSet(uint32_t set);
+
+ static uint32_t GetRegisterNumber(uint32_t reg_kind, uint32_t reg_num);
+
+ static int GetSetForNativeRegNum(int reg_num);
+
+ static size_t GetRegisterInfosCount();
+
+ static const lldb_private::RegisterInfo *GetRegisterInfos();
+};
+
+#endif // liblldb_RegisterContextDarwin_arm_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm64.cpp
new file mode 100644
index 000000000000..85d518a487bf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm64.cpp
@@ -0,0 +1,1043 @@
+//===-- RegisterContextDarwin_arm64.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 "RegisterContextDarwin_arm64.h"
+#include "RegisterContextDarwinConstants.h"
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Compiler.h"
+
+#include "Plugins/Process/Utility/InstructionUtils.h"
+
+#include <memory>
+
+// Support building against older versions of LLVM, this macro was added
+// recently.
+#ifndef LLVM_EXTENSION
+#define LLVM_EXTENSION
+#endif
+
+#include "Utility/ARM64_DWARF_Registers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#define GPR_OFFSET(idx) ((idx)*8)
+#define GPR_OFFSET_NAME(reg) \
+ (LLVM_EXTENSION offsetof(RegisterContextDarwin_arm64::GPR, reg))
+
+#define FPU_OFFSET(idx) ((idx)*16 + sizeof(RegisterContextDarwin_arm64::GPR))
+#define FPU_OFFSET_NAME(reg) \
+ (LLVM_EXTENSION offsetof(RegisterContextDarwin_arm64::FPU, reg))
+
+#define EXC_OFFSET_NAME(reg) \
+ (LLVM_EXTENSION offsetof(RegisterContextDarwin_arm64::EXC, reg) + \
+ sizeof(RegisterContextDarwin_arm64::GPR) + \
+ sizeof(RegisterContextDarwin_arm64::FPU))
+#define DBG_OFFSET_NAME(reg) \
+ (LLVM_EXTENSION offsetof(RegisterContextDarwin_arm64::DBG, reg) + \
+ sizeof(RegisterContextDarwin_arm64::GPR) + \
+ sizeof(RegisterContextDarwin_arm64::FPU) + \
+ sizeof(RegisterContextDarwin_arm64::EXC))
+
+#define DEFINE_DBG(reg, i) \
+ #reg, NULL, \
+ sizeof(((RegisterContextDarwin_arm64::DBG *) NULL)->reg[i]), \
+ DBG_OFFSET_NAME(reg[i]), eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM }, \
+ NULL, NULL, NULL, 0
+#define REG_CONTEXT_SIZE \
+ (sizeof(RegisterContextDarwin_arm64::GPR) + \
+ sizeof(RegisterContextDarwin_arm64::FPU) + \
+ sizeof(RegisterContextDarwin_arm64::EXC))
+
+// Include RegisterInfos_arm64 to declare our g_register_infos_arm64 structure.
+#define DECLARE_REGISTER_INFOS_ARM64_STRUCT
+#include "RegisterInfos_arm64.h"
+#undef DECLARE_REGISTER_INFOS_ARM64_STRUCT
+
+// General purpose registers
+static uint32_t g_gpr_regnums[] = {
+ gpr_x0, 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_lr, gpr_sp, gpr_pc, gpr_cpsr};
+
+// Floating point registers
+static uint32_t g_fpu_regnums[] = {
+ fpu_v0, fpu_v1, fpu_v2, fpu_v3, fpu_v4, fpu_v5, fpu_v6,
+ fpu_v7, fpu_v8, fpu_v9, fpu_v10, fpu_v11, fpu_v12, fpu_v13,
+ fpu_v14, fpu_v15, fpu_v16, fpu_v17, fpu_v18, fpu_v19, fpu_v20,
+ fpu_v21, fpu_v22, fpu_v23, fpu_v24, fpu_v25, fpu_v26, fpu_v27,
+ fpu_v28, fpu_v29, fpu_v30, fpu_v31, fpu_fpsr, fpu_fpcr};
+
+// Exception registers
+
+static uint32_t g_exc_regnums[] = {exc_far, exc_esr, exc_exception};
+
+static size_t k_num_register_infos =
+ llvm::array_lengthof(g_register_infos_arm64_le);
+
+RegisterContextDarwin_arm64::RegisterContextDarwin_arm64(
+ Thread &thread, uint32_t concrete_frame_idx)
+ : RegisterContext(thread, concrete_frame_idx), gpr(), fpu(), exc() {
+ uint32_t i;
+ for (i = 0; i < kNumErrors; i++) {
+ gpr_errs[i] = -1;
+ fpu_errs[i] = -1;
+ exc_errs[i] = -1;
+ }
+}
+
+RegisterContextDarwin_arm64::~RegisterContextDarwin_arm64() {}
+
+void RegisterContextDarwin_arm64::InvalidateAllRegisters() {
+ InvalidateAllRegisterStates();
+}
+
+size_t RegisterContextDarwin_arm64::GetRegisterCount() {
+ assert(k_num_register_infos == k_num_registers);
+ return k_num_registers;
+}
+
+const RegisterInfo *
+RegisterContextDarwin_arm64::GetRegisterInfoAtIndex(size_t reg) {
+ assert(k_num_register_infos == k_num_registers);
+ if (reg < k_num_registers)
+ return &g_register_infos_arm64_le[reg];
+ return nullptr;
+}
+
+size_t RegisterContextDarwin_arm64::GetRegisterInfosCount() {
+ return k_num_register_infos;
+}
+
+const RegisterInfo *RegisterContextDarwin_arm64::GetRegisterInfos() {
+ return g_register_infos_arm64_le;
+}
+
+// Number of registers in each register set
+const size_t k_num_gpr_registers = llvm::array_lengthof(g_gpr_regnums);
+const size_t k_num_fpu_registers = llvm::array_lengthof(g_fpu_regnums);
+const size_t k_num_exc_registers = llvm::array_lengthof(g_exc_regnums);
+
+// 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 RegisterSet g_reg_sets[] = {
+ {
+ "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums,
+ },
+ {"Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums},
+ {"Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums}};
+
+const size_t k_num_regsets = llvm::array_lengthof(g_reg_sets);
+
+size_t RegisterContextDarwin_arm64::GetRegisterSetCount() {
+ return k_num_regsets;
+}
+
+const RegisterSet *RegisterContextDarwin_arm64::GetRegisterSet(size_t reg_set) {
+ if (reg_set < k_num_regsets)
+ return &g_reg_sets[reg_set];
+ return nullptr;
+}
+
+// Register information definitions for arm64
+int RegisterContextDarwin_arm64::GetSetForNativeRegNum(int reg) {
+ if (reg < fpu_v0)
+ return GPRRegSet;
+ else if (reg < exc_far)
+ return FPURegSet;
+ else if (reg < k_num_registers)
+ return EXCRegSet;
+ return -1;
+}
+
+int RegisterContextDarwin_arm64::ReadGPR(bool force) {
+ int set = GPRRegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadGPR(GetThreadID(), set, gpr));
+ }
+ return GetError(GPRRegSet, Read);
+}
+
+int RegisterContextDarwin_arm64::ReadFPU(bool force) {
+ int set = FPURegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadFPU(GetThreadID(), set, fpu));
+ }
+ return GetError(FPURegSet, Read);
+}
+
+int RegisterContextDarwin_arm64::ReadEXC(bool force) {
+ int set = EXCRegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadEXC(GetThreadID(), set, exc));
+ }
+ return GetError(EXCRegSet, Read);
+}
+
+int RegisterContextDarwin_arm64::ReadDBG(bool force) {
+ int set = DBGRegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadDBG(GetThreadID(), set, dbg));
+ }
+ return GetError(DBGRegSet, Read);
+}
+
+int RegisterContextDarwin_arm64::WriteGPR() {
+ int set = GPRRegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError(set, Write, DoWriteGPR(GetThreadID(), set, gpr));
+ SetError(set, Read, -1);
+ return GetError(GPRRegSet, Write);
+}
+
+int RegisterContextDarwin_arm64::WriteFPU() {
+ int set = FPURegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError(set, Write, DoWriteFPU(GetThreadID(), set, fpu));
+ SetError(set, Read, -1);
+ return GetError(FPURegSet, Write);
+}
+
+int RegisterContextDarwin_arm64::WriteEXC() {
+ int set = EXCRegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError(set, Write, DoWriteEXC(GetThreadID(), set, exc));
+ SetError(set, Read, -1);
+ return GetError(EXCRegSet, Write);
+}
+
+int RegisterContextDarwin_arm64::WriteDBG() {
+ int set = DBGRegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError(set, Write, DoWriteDBG(GetThreadID(), set, dbg));
+ SetError(set, Read, -1);
+ return GetError(DBGRegSet, Write);
+}
+
+int RegisterContextDarwin_arm64::ReadRegisterSet(uint32_t set, bool force) {
+ switch (set) {
+ case GPRRegSet:
+ return ReadGPR(force);
+ case FPURegSet:
+ return ReadFPU(force);
+ case EXCRegSet:
+ return ReadEXC(force);
+ case DBGRegSet:
+ return ReadDBG(force);
+ default:
+ break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+int RegisterContextDarwin_arm64::WriteRegisterSet(uint32_t set) {
+ // Make sure we have a valid context to set.
+ if (RegisterSetIsCached(set)) {
+ switch (set) {
+ case GPRRegSet:
+ return WriteGPR();
+ case FPURegSet:
+ return WriteFPU();
+ case EXCRegSet:
+ return WriteEXC();
+ case DBGRegSet:
+ return WriteDBG();
+ default:
+ break;
+ }
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+void RegisterContextDarwin_arm64::LogDBGRegisters(Log *log, const DBG &dbg) {
+ if (log) {
+ for (uint32_t i = 0; i < 16; i++)
+ log->Printf("BVR%-2u/BCR%-2u = { 0x%8.8" PRIu64 ", 0x%8.8" PRIu64
+ " } WVR%-2u/WCR%-2u "
+ "= { 0x%8.8" PRIu64 ", 0x%8.8" PRIu64 " }",
+ i, i, dbg.bvr[i], dbg.bcr[i], i, i, dbg.wvr[i], dbg.wcr[i]);
+ }
+}
+
+bool RegisterContextDarwin_arm64::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ int set = RegisterContextDarwin_arm64::GetSetForNativeRegNum(reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != KERN_SUCCESS)
+ return false;
+
+ switch (reg) {
+ case gpr_x0:
+ case gpr_x1:
+ case gpr_x2:
+ case gpr_x3:
+ case gpr_x4:
+ case gpr_x5:
+ case gpr_x6:
+ case gpr_x7:
+ case gpr_x8:
+ case gpr_x9:
+ case gpr_x10:
+ case gpr_x11:
+ case gpr_x12:
+ case gpr_x13:
+ case gpr_x14:
+ case gpr_x15:
+ case gpr_x16:
+ case gpr_x17:
+ case gpr_x18:
+ case gpr_x19:
+ case gpr_x20:
+ case gpr_x21:
+ case gpr_x22:
+ case gpr_x23:
+ case gpr_x24:
+ case gpr_x25:
+ case gpr_x26:
+ case gpr_x27:
+ case gpr_x28:
+ value.SetUInt64(gpr.x[reg - gpr_x0]);
+ break;
+ case gpr_fp:
+ value.SetUInt64(gpr.fp);
+ break;
+ case gpr_sp:
+ value.SetUInt64(gpr.sp);
+ break;
+ case gpr_lr:
+ value.SetUInt64(gpr.lr);
+ break;
+ case gpr_pc:
+ value.SetUInt64(gpr.pc);
+ break;
+ case gpr_cpsr:
+ value.SetUInt64(gpr.cpsr);
+ break;
+
+ case gpr_w0:
+ case gpr_w1:
+ case gpr_w2:
+ case gpr_w3:
+ case gpr_w4:
+ case gpr_w5:
+ case gpr_w6:
+ case gpr_w7:
+ case gpr_w8:
+ case gpr_w9:
+ case gpr_w10:
+ case gpr_w11:
+ case gpr_w12:
+ case gpr_w13:
+ case gpr_w14:
+ case gpr_w15:
+ case gpr_w16:
+ case gpr_w17:
+ case gpr_w18:
+ case gpr_w19:
+ case gpr_w20:
+ case gpr_w21:
+ case gpr_w22:
+ case gpr_w23:
+ case gpr_w24:
+ case gpr_w25:
+ case gpr_w26:
+ case gpr_w27:
+ case gpr_w28: {
+ ProcessSP process_sp(m_thread.GetProcess());
+ if (process_sp.get()) {
+ DataExtractor regdata(&gpr.x[reg - gpr_w0], 8, process_sp->GetByteOrder(),
+ process_sp->GetAddressByteSize());
+ offset_t offset = 0;
+ uint64_t retval = regdata.GetMaxU64(&offset, 8);
+ uint32_t retval_lower32 = static_cast<uint32_t>(retval & 0xffffffff);
+ value.SetUInt32(retval_lower32);
+ }
+ } break;
+
+ case fpu_v0:
+ case fpu_v1:
+ case fpu_v2:
+ case fpu_v3:
+ case fpu_v4:
+ case fpu_v5:
+ case fpu_v6:
+ case fpu_v7:
+ case fpu_v8:
+ case fpu_v9:
+ case fpu_v10:
+ case fpu_v11:
+ case fpu_v12:
+ case fpu_v13:
+ case fpu_v14:
+ case fpu_v15:
+ case fpu_v16:
+ case fpu_v17:
+ case fpu_v18:
+ case fpu_v19:
+ case fpu_v20:
+ case fpu_v21:
+ case fpu_v22:
+ case fpu_v23:
+ case fpu_v24:
+ case fpu_v25:
+ case fpu_v26:
+ case fpu_v27:
+ case fpu_v28:
+ case fpu_v29:
+ case fpu_v30:
+ case fpu_v31:
+ value.SetBytes(fpu.v[reg - fpu_v0].bytes.buffer, reg_info->byte_size,
+ endian::InlHostByteOrder());
+ break;
+
+ case fpu_s0:
+ case fpu_s1:
+ case fpu_s2:
+ case fpu_s3:
+ case fpu_s4:
+ case fpu_s5:
+ case fpu_s6:
+ case fpu_s7:
+ case fpu_s8:
+ case fpu_s9:
+ case fpu_s10:
+ case fpu_s11:
+ case fpu_s12:
+ case fpu_s13:
+ case fpu_s14:
+ case fpu_s15:
+ case fpu_s16:
+ case fpu_s17:
+ case fpu_s18:
+ case fpu_s19:
+ case fpu_s20:
+ case fpu_s21:
+ case fpu_s22:
+ case fpu_s23:
+ case fpu_s24:
+ case fpu_s25:
+ case fpu_s26:
+ case fpu_s27:
+ case fpu_s28:
+ case fpu_s29:
+ case fpu_s30:
+ case fpu_s31: {
+ ProcessSP process_sp(m_thread.GetProcess());
+ if (process_sp.get()) {
+ DataExtractor regdata(&fpu.v[reg - fpu_s0], 4, process_sp->GetByteOrder(),
+ process_sp->GetAddressByteSize());
+ offset_t offset = 0;
+ value.SetFloat(regdata.GetFloat(&offset));
+ }
+ } break;
+
+ case fpu_d0:
+ case fpu_d1:
+ case fpu_d2:
+ case fpu_d3:
+ case fpu_d4:
+ case fpu_d5:
+ case fpu_d6:
+ case fpu_d7:
+ case fpu_d8:
+ case fpu_d9:
+ case fpu_d10:
+ case fpu_d11:
+ case fpu_d12:
+ case fpu_d13:
+ case fpu_d14:
+ case fpu_d15:
+ case fpu_d16:
+ case fpu_d17:
+ case fpu_d18:
+ case fpu_d19:
+ case fpu_d20:
+ case fpu_d21:
+ case fpu_d22:
+ case fpu_d23:
+ case fpu_d24:
+ case fpu_d25:
+ case fpu_d26:
+ case fpu_d27:
+ case fpu_d28:
+ case fpu_d29:
+ case fpu_d30:
+ case fpu_d31: {
+ ProcessSP process_sp(m_thread.GetProcess());
+ if (process_sp.get()) {
+ DataExtractor regdata(&fpu.v[reg - fpu_s0], 8, process_sp->GetByteOrder(),
+ process_sp->GetAddressByteSize());
+ offset_t offset = 0;
+ value.SetDouble(regdata.GetDouble(&offset));
+ }
+ } break;
+
+ case fpu_fpsr:
+ value.SetUInt32(fpu.fpsr);
+ break;
+
+ case fpu_fpcr:
+ value.SetUInt32(fpu.fpcr);
+ break;
+
+ case exc_exception:
+ value.SetUInt32(exc.exception);
+ break;
+ case exc_esr:
+ value.SetUInt32(exc.esr);
+ break;
+ case exc_far:
+ value.SetUInt64(exc.far);
+ break;
+
+ default:
+ value.SetValueToInvalid();
+ return false;
+ }
+ return true;
+}
+
+bool RegisterContextDarwin_arm64::WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ int set = GetSetForNativeRegNum(reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != KERN_SUCCESS)
+ return false;
+
+ switch (reg) {
+ case gpr_x0:
+ case gpr_x1:
+ case gpr_x2:
+ case gpr_x3:
+ case gpr_x4:
+ case gpr_x5:
+ case gpr_x6:
+ case gpr_x7:
+ case gpr_x8:
+ case gpr_x9:
+ case gpr_x10:
+ case gpr_x11:
+ case gpr_x12:
+ case gpr_x13:
+ case gpr_x14:
+ case gpr_x15:
+ case gpr_x16:
+ case gpr_x17:
+ case gpr_x18:
+ case gpr_x19:
+ case gpr_x20:
+ case gpr_x21:
+ case gpr_x22:
+ case gpr_x23:
+ case gpr_x24:
+ case gpr_x25:
+ case gpr_x26:
+ case gpr_x27:
+ case gpr_x28:
+ case gpr_fp:
+ case gpr_sp:
+ case gpr_lr:
+ case gpr_pc:
+ case gpr_cpsr:
+ gpr.x[reg - gpr_x0] = value.GetAsUInt64();
+ break;
+
+ case fpu_v0:
+ case fpu_v1:
+ case fpu_v2:
+ case fpu_v3:
+ case fpu_v4:
+ case fpu_v5:
+ case fpu_v6:
+ case fpu_v7:
+ case fpu_v8:
+ case fpu_v9:
+ case fpu_v10:
+ case fpu_v11:
+ case fpu_v12:
+ case fpu_v13:
+ case fpu_v14:
+ case fpu_v15:
+ case fpu_v16:
+ case fpu_v17:
+ case fpu_v18:
+ case fpu_v19:
+ case fpu_v20:
+ case fpu_v21:
+ case fpu_v22:
+ case fpu_v23:
+ case fpu_v24:
+ case fpu_v25:
+ case fpu_v26:
+ case fpu_v27:
+ case fpu_v28:
+ case fpu_v29:
+ case fpu_v30:
+ case fpu_v31:
+ ::memcpy(fpu.v[reg - fpu_v0].bytes.buffer, value.GetBytes(),
+ value.GetByteSize());
+ break;
+
+ case fpu_fpsr:
+ fpu.fpsr = value.GetAsUInt32();
+ break;
+
+ case fpu_fpcr:
+ fpu.fpcr = value.GetAsUInt32();
+ break;
+
+ case exc_exception:
+ exc.exception = value.GetAsUInt32();
+ break;
+ case exc_esr:
+ exc.esr = value.GetAsUInt32();
+ break;
+ case exc_far:
+ exc.far = value.GetAsUInt64();
+ break;
+
+ default:
+ return false;
+ }
+ return WriteRegisterSet(set) == KERN_SUCCESS;
+}
+
+bool RegisterContextDarwin_arm64::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ data_sp = std::make_shared<DataBufferHeap>(REG_CONTEXT_SIZE, 0);
+ if (ReadGPR(false) == KERN_SUCCESS && ReadFPU(false) == KERN_SUCCESS &&
+ ReadEXC(false) == KERN_SUCCESS) {
+ uint8_t *dst = data_sp->GetBytes();
+ ::memcpy(dst, &gpr, sizeof(gpr));
+ dst += sizeof(gpr);
+
+ ::memcpy(dst, &fpu, sizeof(fpu));
+ dst += sizeof(gpr);
+
+ ::memcpy(dst, &exc, sizeof(exc));
+ return true;
+ }
+ return false;
+}
+
+bool RegisterContextDarwin_arm64::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) {
+ const uint8_t *src = data_sp->GetBytes();
+ ::memcpy(&gpr, src, sizeof(gpr));
+ src += sizeof(gpr);
+
+ ::memcpy(&fpu, src, sizeof(fpu));
+ src += sizeof(gpr);
+
+ ::memcpy(&exc, src, sizeof(exc));
+ uint32_t success_count = 0;
+ if (WriteGPR() == KERN_SUCCESS)
+ ++success_count;
+ if (WriteFPU() == KERN_SUCCESS)
+ ++success_count;
+ if (WriteEXC() == KERN_SUCCESS)
+ ++success_count;
+ return success_count == 3;
+ }
+ return false;
+}
+
+uint32_t RegisterContextDarwin_arm64::ConvertRegisterKindToRegisterNumber(
+ RegisterKind kind, uint32_t reg) {
+ if (kind == eRegisterKindGeneric) {
+ switch (reg) {
+ case LLDB_REGNUM_GENERIC_PC:
+ return gpr_pc;
+ case LLDB_REGNUM_GENERIC_SP:
+ return gpr_sp;
+ case LLDB_REGNUM_GENERIC_FP:
+ return gpr_fp;
+ case LLDB_REGNUM_GENERIC_RA:
+ return gpr_lr;
+ case LLDB_REGNUM_GENERIC_FLAGS:
+ return gpr_cpsr;
+ default:
+ break;
+ }
+ } else if (kind == eRegisterKindDWARF) {
+ switch (reg) {
+ case arm64_dwarf::x0:
+ return gpr_x0;
+ case arm64_dwarf::x1:
+ return gpr_x1;
+ case arm64_dwarf::x2:
+ return gpr_x2;
+ case arm64_dwarf::x3:
+ return gpr_x3;
+ case arm64_dwarf::x4:
+ return gpr_x4;
+ case arm64_dwarf::x5:
+ return gpr_x5;
+ case arm64_dwarf::x6:
+ return gpr_x6;
+ case arm64_dwarf::x7:
+ return gpr_x7;
+ case arm64_dwarf::x8:
+ return gpr_x8;
+ case arm64_dwarf::x9:
+ return gpr_x9;
+ case arm64_dwarf::x10:
+ return gpr_x10;
+ case arm64_dwarf::x11:
+ return gpr_x11;
+ case arm64_dwarf::x12:
+ return gpr_x12;
+ case arm64_dwarf::x13:
+ return gpr_x13;
+ case arm64_dwarf::x14:
+ return gpr_x14;
+ case arm64_dwarf::x15:
+ return gpr_x15;
+ case arm64_dwarf::x16:
+ return gpr_x16;
+ case arm64_dwarf::x17:
+ return gpr_x17;
+ case arm64_dwarf::x18:
+ return gpr_x18;
+ case arm64_dwarf::x19:
+ return gpr_x19;
+ case arm64_dwarf::x20:
+ return gpr_x20;
+ case arm64_dwarf::x21:
+ return gpr_x21;
+ case arm64_dwarf::x22:
+ return gpr_x22;
+ case arm64_dwarf::x23:
+ return gpr_x23;
+ case arm64_dwarf::x24:
+ return gpr_x24;
+ case arm64_dwarf::x25:
+ return gpr_x25;
+ case arm64_dwarf::x26:
+ return gpr_x26;
+ case arm64_dwarf::x27:
+ return gpr_x27;
+ case arm64_dwarf::x28:
+ return gpr_x28;
+
+ case arm64_dwarf::fp:
+ return gpr_fp;
+ case arm64_dwarf::sp:
+ return gpr_sp;
+ case arm64_dwarf::lr:
+ return gpr_lr;
+ case arm64_dwarf::pc:
+ return gpr_pc;
+ case arm64_dwarf::cpsr:
+ return gpr_cpsr;
+
+ case arm64_dwarf::v0:
+ return fpu_v0;
+ case arm64_dwarf::v1:
+ return fpu_v1;
+ case arm64_dwarf::v2:
+ return fpu_v2;
+ case arm64_dwarf::v3:
+ return fpu_v3;
+ case arm64_dwarf::v4:
+ return fpu_v4;
+ case arm64_dwarf::v5:
+ return fpu_v5;
+ case arm64_dwarf::v6:
+ return fpu_v6;
+ case arm64_dwarf::v7:
+ return fpu_v7;
+ case arm64_dwarf::v8:
+ return fpu_v8;
+ case arm64_dwarf::v9:
+ return fpu_v9;
+ case arm64_dwarf::v10:
+ return fpu_v10;
+ case arm64_dwarf::v11:
+ return fpu_v11;
+ case arm64_dwarf::v12:
+ return fpu_v12;
+ case arm64_dwarf::v13:
+ return fpu_v13;
+ case arm64_dwarf::v14:
+ return fpu_v14;
+ case arm64_dwarf::v15:
+ return fpu_v15;
+ case arm64_dwarf::v16:
+ return fpu_v16;
+ case arm64_dwarf::v17:
+ return fpu_v17;
+ case arm64_dwarf::v18:
+ return fpu_v18;
+ case arm64_dwarf::v19:
+ return fpu_v19;
+ case arm64_dwarf::v20:
+ return fpu_v20;
+ case arm64_dwarf::v21:
+ return fpu_v21;
+ case arm64_dwarf::v22:
+ return fpu_v22;
+ case arm64_dwarf::v23:
+ return fpu_v23;
+ case arm64_dwarf::v24:
+ return fpu_v24;
+ case arm64_dwarf::v25:
+ return fpu_v25;
+ case arm64_dwarf::v26:
+ return fpu_v26;
+ case arm64_dwarf::v27:
+ return fpu_v27;
+ case arm64_dwarf::v28:
+ return fpu_v28;
+ case arm64_dwarf::v29:
+ return fpu_v29;
+ case arm64_dwarf::v30:
+ return fpu_v30;
+ case arm64_dwarf::v31:
+ return fpu_v31;
+
+ default:
+ break;
+ }
+ } else if (kind == eRegisterKindEHFrame) {
+ switch (reg) {
+ case arm64_ehframe::x0:
+ return gpr_x0;
+ case arm64_ehframe::x1:
+ return gpr_x1;
+ case arm64_ehframe::x2:
+ return gpr_x2;
+ case arm64_ehframe::x3:
+ return gpr_x3;
+ case arm64_ehframe::x4:
+ return gpr_x4;
+ case arm64_ehframe::x5:
+ return gpr_x5;
+ case arm64_ehframe::x6:
+ return gpr_x6;
+ case arm64_ehframe::x7:
+ return gpr_x7;
+ case arm64_ehframe::x8:
+ return gpr_x8;
+ case arm64_ehframe::x9:
+ return gpr_x9;
+ case arm64_ehframe::x10:
+ return gpr_x10;
+ case arm64_ehframe::x11:
+ return gpr_x11;
+ case arm64_ehframe::x12:
+ return gpr_x12;
+ case arm64_ehframe::x13:
+ return gpr_x13;
+ case arm64_ehframe::x14:
+ return gpr_x14;
+ case arm64_ehframe::x15:
+ return gpr_x15;
+ case arm64_ehframe::x16:
+ return gpr_x16;
+ case arm64_ehframe::x17:
+ return gpr_x17;
+ case arm64_ehframe::x18:
+ return gpr_x18;
+ case arm64_ehframe::x19:
+ return gpr_x19;
+ case arm64_ehframe::x20:
+ return gpr_x20;
+ case arm64_ehframe::x21:
+ return gpr_x21;
+ case arm64_ehframe::x22:
+ return gpr_x22;
+ case arm64_ehframe::x23:
+ return gpr_x23;
+ case arm64_ehframe::x24:
+ return gpr_x24;
+ case arm64_ehframe::x25:
+ return gpr_x25;
+ case arm64_ehframe::x26:
+ return gpr_x26;
+ case arm64_ehframe::x27:
+ return gpr_x27;
+ case arm64_ehframe::x28:
+ return gpr_x28;
+ case arm64_ehframe::fp:
+ return gpr_fp;
+ case arm64_ehframe::sp:
+ return gpr_sp;
+ case arm64_ehframe::lr:
+ return gpr_lr;
+ case arm64_ehframe::pc:
+ return gpr_pc;
+ case arm64_ehframe::cpsr:
+ return gpr_cpsr;
+ }
+ } else if (kind == eRegisterKindLLDB) {
+ return reg;
+ }
+ return LLDB_INVALID_REGNUM;
+}
+
+uint32_t RegisterContextDarwin_arm64::NumSupportedHardwareWatchpoints() {
+#if defined(__APPLE__) && (defined(__arm64__) || defined(__aarch64__))
+ // autodetect how many watchpoints are supported dynamically...
+ static uint32_t g_num_supported_hw_watchpoints = UINT32_MAX;
+ if (g_num_supported_hw_watchpoints == UINT32_MAX) {
+ 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;
+ }
+ }
+ return g_num_supported_hw_watchpoints;
+#else
+ // TODO: figure out remote case here!
+ return 2;
+#endif
+}
+
+uint32_t RegisterContextDarwin_arm64::SetHardwareWatchpoint(lldb::addr_t addr,
+ size_t size,
+ bool read,
+ bool write) {
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm64::EnableHardwareWatchpoint(addr = %8.8p,
+ // size = %u, read = %u, write = %u)", addr, size, read, write);
+
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+
+ // Can't watch zero bytes
+ if (size == 0)
+ return LLDB_INVALID_INDEX32;
+
+ // We must watch for either read or write
+ if (!read && !write)
+ return LLDB_INVALID_INDEX32;
+
+ // Can't watch more than 4 bytes per WVR/WCR pair
+ if (size > 4)
+ return LLDB_INVALID_INDEX32;
+
+ // We can only watch up to four bytes that follow a 4 byte aligned address
+ // per watchpoint register pair. Since we have at most so we can only watch
+ // until the next 4 byte boundary and we need to make sure we can properly
+ // encode this.
+ uint32_t addr_word_offset = addr % 4;
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm64::EnableHardwareWatchpoint() -
+ // addr_word_offset = 0x%8.8x", addr_word_offset);
+
+ uint32_t byte_mask = ((1u << size) - 1u) << addr_word_offset;
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm64::EnableHardwareWatchpoint() - byte_mask =
+ // 0x%8.8x", byte_mask);
+ if (byte_mask > 0xfu)
+ return LLDB_INVALID_INDEX32;
+
+ // Read the debug state
+ int kret = ReadDBG(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 ((dbg.wcr[i] & WCR_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_watchpoints) {
+ // Make the byte_mask into a valid Byte Address Select mask
+ uint32_t byte_address_select = byte_mask << 5;
+ // Make sure bits 1:0 are clear in our address
+ dbg.wvr[i] = addr & ~((lldb::addr_t)3);
+ dbg.wcr[i] = byte_address_select | // Which bytes that follow the IMVA
+ // 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;
+
+ kret = WriteDBG();
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm64::EnableHardwareWatchpoint()
+ // WriteDBG() => 0x%8.8x.", kret);
+
+ if (kret == KERN_SUCCESS)
+ return i;
+ } else {
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm64::EnableHardwareWatchpoint():
+ // All hardware resources (%u) are in use.",
+ // num_hw_watchpoints);
+ }
+ }
+ return LLDB_INVALID_INDEX32;
+}
+
+bool RegisterContextDarwin_arm64::ClearHardwareWatchpoint(uint32_t hw_index) {
+ int kret = ReadDBG(false);
+
+ const uint32_t num_hw_points = NumSupportedHardwareWatchpoints();
+ if (kret == KERN_SUCCESS) {
+ if (hw_index < num_hw_points) {
+ dbg.wcr[hw_index] = 0;
+ // if (log) log->Printf
+ // ("RegisterContextDarwin_arm64::ClearHardwareWatchpoint( %u )
+ // - WVR%u = 0x%8.8x WCR%u = 0x%8.8x",
+ // hw_index,
+ // hw_index,
+ // dbg.wvr[hw_index],
+ // hw_index,
+ // dbg.wcr[hw_index]);
+
+ kret = WriteDBG();
+
+ if (kret == KERN_SUCCESS)
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm64.h
new file mode 100644
index 000000000000..2f691c807d50
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm64.h
@@ -0,0 +1,231 @@
+//===-- RegisterContextDarwin_arm64.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 liblldb_RegisterContextDarwin_arm64_h_
+#define liblldb_RegisterContextDarwin_arm64_h_
+
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/lldb-private.h"
+
+// 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 WCR_ENABLE ((uint32_t)(1u))
+
+// Watchpoint load/store
+#define WCR_LOAD ((uint32_t)(1u << 3))
+#define WCR_STORE ((uint32_t)(1u << 4))
+
+class RegisterContextDarwin_arm64 : public lldb_private::RegisterContext {
+public:
+ RegisterContextDarwin_arm64(lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx);
+
+ ~RegisterContextDarwin_arm64() override;
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &reg_value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &reg_value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+ uint32_t NumSupportedHardwareWatchpoints() override;
+
+ uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read,
+ bool write) override;
+
+ bool ClearHardwareWatchpoint(uint32_t hw_index) override;
+
+ // mirrors <mach/arm/thread_status.h> arm_thread_state64_t
+ struct GPR {
+ uint64_t x[29]; // x0-x28
+ uint64_t fp; // x29
+ uint64_t lr; // x30
+ uint64_t sp; // x31
+ uint64_t pc; // pc
+ uint32_t cpsr; // cpsr
+ };
+
+ struct VReg {
+ llvm::AlignedCharArray<16, 16> bytes;
+ };
+
+ // mirrors <mach/arm/thread_status.h> arm_neon_state64_t
+ struct FPU {
+ VReg v[32];
+ uint32_t fpsr;
+ uint32_t fpcr;
+ };
+
+ // mirrors <mach/arm/thread_status.h> arm_exception_state64_t
+ struct EXC {
+ uint64_t far; // Virtual Fault Address
+ uint32_t esr; // Exception syndrome
+ uint32_t exception; // number of arm exception token
+ };
+
+ // mirrors <mach/arm/thread_status.h> arm_debug_state64_t
+ struct DBG {
+ uint64_t bvr[16];
+ uint64_t bcr[16];
+ uint64_t wvr[16];
+ uint64_t wcr[16];
+ uint64_t mdscr_el1;
+ };
+
+ static void LogDBGRegisters(lldb_private::Log *log, const DBG &dbg);
+
+protected:
+ enum {
+ GPRRegSet = 6, // ARM_THREAD_STATE64
+ FPURegSet = 17, // ARM_NEON_STATE64
+ EXCRegSet = 7, // ARM_EXCEPTION_STATE64
+ DBGRegSet = 15 // ARM_DEBUG_STATE64
+ };
+
+ enum {
+ GPRWordCount = sizeof(GPR) / sizeof(uint32_t), // ARM_THREAD_STATE64_COUNT
+ FPUWordCount = sizeof(FPU) / sizeof(uint32_t), // ARM_NEON_STATE64_COUNT
+ EXCWordCount =
+ sizeof(EXC) / sizeof(uint32_t), // ARM_EXCEPTION_STATE64_COUNT
+ DBGWordCount = sizeof(DBG) / sizeof(uint32_t) // ARM_DEBUG_STATE64_COUNT
+ };
+
+ enum { Read = 0, Write = 1, kNumErrors = 2 };
+
+ GPR gpr;
+ FPU fpu;
+ EXC exc;
+ DBG dbg;
+ int gpr_errs[2]; // Read/Write errors
+ int fpu_errs[2]; // Read/Write errors
+ int exc_errs[2]; // Read/Write errors
+ int dbg_errs[2]; // Read/Write errors
+
+ void InvalidateAllRegisterStates() {
+ SetError(GPRRegSet, Read, -1);
+ SetError(FPURegSet, Read, -1);
+ SetError(EXCRegSet, Read, -1);
+ }
+
+ int 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 GPRRegSet:
+ return gpr_errs[err_idx];
+ case FPURegSet:
+ return fpu_errs[err_idx];
+ case EXCRegSet:
+ return exc_errs[err_idx];
+ case DBGRegSet:
+ return dbg_errs[err_idx];
+ default:
+ break;
+ }
+ }
+ return -1;
+ }
+
+ bool SetError(int flavor, uint32_t err_idx, int err) {
+ if (err_idx < kNumErrors) {
+ switch (flavor) {
+ case GPRRegSet:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case FPURegSet:
+ fpu_errs[err_idx] = err;
+ return true;
+
+ case EXCRegSet:
+ exc_errs[err_idx] = err;
+ return true;
+
+ case DBGRegSet:
+ exc_errs[err_idx] = err;
+ return true;
+
+ default:
+ break;
+ }
+ }
+ return false;
+ }
+
+ bool RegisterSetIsCached(int set) const { return GetError(set, Read) == 0; }
+
+ int ReadGPR(bool force);
+
+ int ReadFPU(bool force);
+
+ int ReadEXC(bool force);
+
+ int ReadDBG(bool force);
+
+ int WriteGPR();
+
+ int WriteFPU();
+
+ int WriteEXC();
+
+ int WriteDBG();
+
+ // Subclasses override these to do the actual reading.
+ virtual int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) { return -1; }
+
+ virtual int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) = 0;
+
+ virtual int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) = 0;
+
+ virtual int DoReadDBG(lldb::tid_t tid, int flavor, DBG &dbg) = 0;
+
+ virtual int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) = 0;
+
+ virtual int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) = 0;
+
+ virtual int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) = 0;
+
+ virtual int DoWriteDBG(lldb::tid_t tid, int flavor, const DBG &dbg) = 0;
+
+ int ReadRegisterSet(uint32_t set, bool force);
+
+ int WriteRegisterSet(uint32_t set);
+
+ static uint32_t GetRegisterNumber(uint32_t reg_kind, uint32_t reg_num);
+
+ static int GetSetForNativeRegNum(int reg_num);
+
+ static size_t GetRegisterInfosCount();
+
+ static const lldb_private::RegisterInfo *GetRegisterInfos();
+};
+
+#endif // liblldb_RegisterContextDarwin_arm64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_i386.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_i386.cpp
new file mode 100644
index 000000000000..820d280c37f7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_i386.cpp
@@ -0,0 +1,969 @@
+//===-- RegisterContextDarwin_i386.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 "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Compiler.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+// Support building against older versions of LLVM, this macro was added
+// recently.
+#ifndef LLVM_EXTENSION
+#define LLVM_EXTENSION
+#endif
+
+#include "RegisterContextDarwin_i386.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum {
+ gpr_eax = 0,
+ gpr_ebx,
+ gpr_ecx,
+ gpr_edx,
+ gpr_edi,
+ gpr_esi,
+ gpr_ebp,
+ gpr_esp,
+ gpr_ss,
+ gpr_eflags,
+ gpr_eip,
+ gpr_cs,
+ gpr_ds,
+ gpr_es,
+ gpr_fs,
+ gpr_gs,
+
+ 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,
+
+ exc_trapno,
+ exc_err,
+ exc_faultvaddr,
+
+ k_num_registers,
+
+ // 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 {
+ ehframe_eax = 0,
+ ehframe_ecx,
+ ehframe_edx,
+ ehframe_ebx,
+ 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
+};
+
+#define GPR_OFFSET(reg) \
+ (LLVM_EXTENSION offsetof(RegisterContextDarwin_i386::GPR, reg))
+#define FPU_OFFSET(reg) \
+ (LLVM_EXTENSION offsetof(RegisterContextDarwin_i386::FPU, reg) + \
+ sizeof(RegisterContextDarwin_i386::GPR))
+#define EXC_OFFSET(reg) \
+ (LLVM_EXTENSION offsetof(RegisterContextDarwin_i386::EXC, reg) + \
+ sizeof(RegisterContextDarwin_i386::GPR) + \
+ sizeof(RegisterContextDarwin_i386::FPU))
+
+// 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, alt) \
+ #reg, alt, sizeof(((RegisterContextDarwin_i386::GPR *) NULL)->reg), \
+ GPR_OFFSET(reg), eEncodingUint, eFormatHex
+#define DEFINE_FPU_UINT(reg) \
+ #reg, NULL, sizeof(((RegisterContextDarwin_i386::FPU *) NULL)->reg), \
+ FPU_OFFSET(reg), eEncodingUint, eFormatHex
+#define DEFINE_FPU_VECT(reg, i) \
+ #reg #i, NULL, \
+ sizeof(((RegisterContextDarwin_i386::FPU *) NULL)->reg[i].bytes), \
+ FPU_OFFSET(reg[i]), eEncodingVector, eFormatVectorOfUInt8, \
+ {LLDB_INVALID_REGNUM, dwarf_##reg##i, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ fpu_##reg##i }, \
+ nullptr, nullptr, nullptr, 0
+
+#define DEFINE_EXC(reg) \
+ #reg, NULL, sizeof(((RegisterContextDarwin_i386::EXC *) NULL)->reg), \
+ EXC_OFFSET(reg), eEncodingUint, eFormatHex
+#define REG_CONTEXT_SIZE \
+ (sizeof(RegisterContextDarwin_i386::GPR) + \
+ sizeof(RegisterContextDarwin_i386::FPU) + \
+ sizeof(RegisterContextDarwin_i386::EXC))
+
+static RegisterInfo g_register_infos[] = {
+ // Macro auto defines most stuff eh_frame DWARF
+ // GENERIC PROCESS PLUGIN LLDB
+ // =============================== =======================
+ // =================== ========================= ==================
+ // =================
+ {DEFINE_GPR(eax, nullptr),
+ {ehframe_eax, dwarf_eax, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ gpr_eax},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(ebx, nullptr),
+ {ehframe_ebx, dwarf_ebx, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ gpr_ebx},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(ecx, nullptr),
+ {ehframe_ecx, dwarf_ecx, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ gpr_ecx},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(edx, nullptr),
+ {ehframe_edx, dwarf_edx, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ gpr_edx},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(edi, nullptr),
+ {ehframe_edi, dwarf_edi, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ gpr_edi},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(esi, nullptr),
+ {ehframe_esi, dwarf_esi, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ gpr_esi},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(ebp, "fp"),
+ {ehframe_ebp, dwarf_ebp, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM,
+ gpr_ebp},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(esp, "sp"),
+ {ehframe_esp, dwarf_esp, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM,
+ gpr_esp},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(ss, nullptr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_ss},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(eflags, "flags"),
+ {ehframe_eflags, dwarf_eflags, LLDB_REGNUM_GENERIC_FLAGS,
+ LLDB_INVALID_REGNUM, gpr_eflags},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(eip, "pc"),
+ {ehframe_eip, dwarf_eip, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM,
+ gpr_eip},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(cs, nullptr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_cs},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(ds, nullptr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_ds},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(es, nullptr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_es},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(fs, nullptr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_fs},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(gs, nullptr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_gs},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {DEFINE_FPU_UINT(fcw),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_fcw},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(fsw),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_fsw},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(ftw),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_ftw},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(fop),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_fop},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(ip),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_ip},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(cs),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_cs},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(dp),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_dp},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(ds),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_ds},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(mxcsr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_mxcsr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(mxcsrmask),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_mxcsrmask},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_VECT(stmm, 0)},
+ {DEFINE_FPU_VECT(stmm, 1)},
+ {DEFINE_FPU_VECT(stmm, 2)},
+ {DEFINE_FPU_VECT(stmm, 3)},
+ {DEFINE_FPU_VECT(stmm, 4)},
+ {DEFINE_FPU_VECT(stmm, 5)},
+ {DEFINE_FPU_VECT(stmm, 6)},
+ {DEFINE_FPU_VECT(stmm, 7)},
+ {DEFINE_FPU_VECT(xmm, 0)},
+ {DEFINE_FPU_VECT(xmm, 1)},
+ {DEFINE_FPU_VECT(xmm, 2)},
+ {DEFINE_FPU_VECT(xmm, 3)},
+ {DEFINE_FPU_VECT(xmm, 4)},
+ {DEFINE_FPU_VECT(xmm, 5)},
+ {DEFINE_FPU_VECT(xmm, 6)},
+ {DEFINE_FPU_VECT(xmm, 7)},
+
+ {DEFINE_EXC(trapno),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, exc_trapno},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_EXC(err),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, exc_err},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_EXC(faultvaddr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, exc_faultvaddr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0}};
+
+static size_t k_num_register_infos = llvm::array_lengthof(g_register_infos);
+
+RegisterContextDarwin_i386::RegisterContextDarwin_i386(
+ Thread &thread, uint32_t concrete_frame_idx)
+ : RegisterContext(thread, concrete_frame_idx), gpr(), fpu(), exc() {
+ uint32_t i;
+ for (i = 0; i < kNumErrors; i++) {
+ gpr_errs[i] = -1;
+ fpu_errs[i] = -1;
+ exc_errs[i] = -1;
+ }
+}
+
+RegisterContextDarwin_i386::~RegisterContextDarwin_i386() {}
+
+void RegisterContextDarwin_i386::InvalidateAllRegisters() {
+ InvalidateAllRegisterStates();
+}
+
+size_t RegisterContextDarwin_i386::GetRegisterCount() {
+ assert(k_num_register_infos == k_num_registers);
+ return k_num_registers;
+}
+
+const RegisterInfo *
+RegisterContextDarwin_i386::GetRegisterInfoAtIndex(size_t reg) {
+ assert(k_num_register_infos == k_num_registers);
+ if (reg < k_num_registers)
+ return &g_register_infos[reg];
+ return nullptr;
+}
+
+size_t RegisterContextDarwin_i386::GetRegisterInfosCount() {
+ return k_num_register_infos;
+}
+
+const RegisterInfo *RegisterContextDarwin_i386::GetRegisterInfos() {
+ return g_register_infos;
+}
+
+// General purpose registers
+static uint32_t g_gpr_regnums[] = {
+ gpr_eax, gpr_ebx, gpr_ecx, gpr_edx, gpr_edi, gpr_esi, gpr_ebp, gpr_esp,
+ gpr_ss, gpr_eflags, gpr_eip, gpr_cs, gpr_ds, gpr_es, gpr_fs, gpr_gs};
+
+// Floating point registers
+static uint32_t g_fpu_regnums[] = {
+ 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};
+
+// Exception registers
+
+static uint32_t g_exc_regnums[] = {exc_trapno, exc_err, exc_faultvaddr};
+
+// Number of registers in each register set
+const size_t k_num_gpr_registers = llvm::array_lengthof(g_gpr_regnums);
+const size_t k_num_fpu_registers = llvm::array_lengthof(g_fpu_regnums);
+const size_t k_num_exc_registers = llvm::array_lengthof(g_exc_regnums);
+
+// 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 RegisterSet g_reg_sets[] = {
+ {
+ "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums,
+ },
+ {"Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums},
+ {"Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums}};
+
+const size_t k_num_regsets = llvm::array_lengthof(g_reg_sets);
+
+size_t RegisterContextDarwin_i386::GetRegisterSetCount() {
+ return k_num_regsets;
+}
+
+const RegisterSet *RegisterContextDarwin_i386::GetRegisterSet(size_t reg_set) {
+ if (reg_set < k_num_regsets)
+ return &g_reg_sets[reg_set];
+ return nullptr;
+}
+
+// Register information definitions for 32 bit i386.
+int RegisterContextDarwin_i386::GetSetForNativeRegNum(int reg_num) {
+ if (reg_num < fpu_fcw)
+ return GPRRegSet;
+ else if (reg_num < exc_trapno)
+ return FPURegSet;
+ else if (reg_num < k_num_registers)
+ return EXCRegSet;
+ return -1;
+}
+
+void RegisterContextDarwin_i386::LogGPR(Log *log, const char *title) {
+ if (log) {
+ if (title)
+ log->Printf("%s", title);
+ for (uint32_t i = 0; i < k_num_gpr_registers; i++) {
+ uint32_t reg = gpr_eax + i;
+ log->Printf("%12s = 0x%8.8x", g_register_infos[reg].name,
+ (&gpr.eax)[reg]);
+ }
+ }
+}
+
+int RegisterContextDarwin_i386::ReadGPR(bool force) {
+ int set = GPRRegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadGPR(GetThreadID(), set, gpr));
+ }
+ return GetError(set, Read);
+}
+
+int RegisterContextDarwin_i386::ReadFPU(bool force) {
+ int set = FPURegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadFPU(GetThreadID(), set, fpu));
+ }
+ return GetError(set, Read);
+}
+
+int RegisterContextDarwin_i386::ReadEXC(bool force) {
+ int set = EXCRegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadEXC(GetThreadID(), set, exc));
+ }
+ return GetError(set, Read);
+}
+
+int RegisterContextDarwin_i386::WriteGPR() {
+ int set = GPRRegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return -1;
+ }
+ SetError(set, Write, DoWriteGPR(GetThreadID(), set, gpr));
+ SetError(set, Read, -1);
+ return GetError(set, Write);
+}
+
+int RegisterContextDarwin_i386::WriteFPU() {
+ int set = FPURegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return -1;
+ }
+ SetError(set, Write, DoWriteFPU(GetThreadID(), set, fpu));
+ SetError(set, Read, -1);
+ return GetError(set, Write);
+}
+
+int RegisterContextDarwin_i386::WriteEXC() {
+ int set = EXCRegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return -1;
+ }
+ SetError(set, Write, DoWriteEXC(GetThreadID(), set, exc));
+ SetError(set, Read, -1);
+ return GetError(set, Write);
+}
+
+int RegisterContextDarwin_i386::ReadRegisterSet(uint32_t set, bool force) {
+ switch (set) {
+ case GPRRegSet:
+ return ReadGPR(force);
+ case FPURegSet:
+ return ReadFPU(force);
+ case EXCRegSet:
+ return ReadEXC(force);
+ default:
+ break;
+ }
+ return -1;
+}
+
+int RegisterContextDarwin_i386::WriteRegisterSet(uint32_t set) {
+ // Make sure we have a valid context to set.
+ if (RegisterSetIsCached(set)) {
+ switch (set) {
+ case GPRRegSet:
+ return WriteGPR();
+ case FPURegSet:
+ return WriteFPU();
+ case EXCRegSet:
+ return WriteEXC();
+ default:
+ break;
+ }
+ }
+ return -1;
+}
+
+bool RegisterContextDarwin_i386::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ int set = RegisterContextDarwin_i386::GetSetForNativeRegNum(reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != 0)
+ return false;
+
+ switch (reg) {
+ case gpr_eax:
+ case gpr_ebx:
+ case gpr_ecx:
+ case gpr_edx:
+ case gpr_edi:
+ case gpr_esi:
+ case gpr_ebp:
+ case gpr_esp:
+ case gpr_ss:
+ case gpr_eflags:
+ case gpr_eip:
+ case gpr_cs:
+ case gpr_ds:
+ case gpr_es:
+ case gpr_fs:
+ case gpr_gs:
+ value = (&gpr.eax)[reg - gpr_eax];
+ break;
+
+ case fpu_fcw:
+ value = fpu.fcw;
+ break;
+
+ case fpu_fsw:
+ value = fpu.fsw;
+ break;
+
+ case fpu_ftw:
+ value = fpu.ftw;
+ break;
+
+ case fpu_fop:
+ value = fpu.fop;
+ break;
+
+ case fpu_ip:
+ value = fpu.ip;
+ break;
+
+ case fpu_cs:
+ value = fpu.cs;
+ break;
+
+ case fpu_dp:
+ value = fpu.dp;
+ break;
+
+ case fpu_ds:
+ value = fpu.ds;
+ break;
+
+ case fpu_mxcsr:
+ value = fpu.mxcsr;
+ break;
+
+ case fpu_mxcsrmask:
+ value = fpu.mxcsrmask;
+ 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:
+ // These values don't fit into scalar types,
+ // RegisterContext::ReadRegisterBytes() must be used for these registers
+ //::memcpy (reg_value.value.vector.uint8, fpu.stmm[reg - fpu_stmm0].bytes,
+ //10);
+ return false;
+
+ case fpu_xmm0:
+ case fpu_xmm1:
+ case fpu_xmm2:
+ case fpu_xmm3:
+ case fpu_xmm4:
+ case fpu_xmm5:
+ case fpu_xmm6:
+ case fpu_xmm7:
+ // These values don't fit into scalar types,
+ // RegisterContext::ReadRegisterBytes() must be used for these registers
+ //::memcpy (reg_value.value.vector.uint8, fpu.xmm[reg - fpu_xmm0].bytes,
+ //16);
+ return false;
+
+ case exc_trapno:
+ value = exc.trapno;
+ break;
+
+ case exc_err:
+ value = exc.err;
+ break;
+
+ case exc_faultvaddr:
+ value = exc.faultvaddr;
+ break;
+
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool RegisterContextDarwin_i386::WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ int set = GetSetForNativeRegNum(reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != 0)
+ return false;
+
+ switch (reg) {
+ case gpr_eax:
+ case gpr_ebx:
+ case gpr_ecx:
+ case gpr_edx:
+ case gpr_edi:
+ case gpr_esi:
+ case gpr_ebp:
+ case gpr_esp:
+ case gpr_ss:
+ case gpr_eflags:
+ case gpr_eip:
+ case gpr_cs:
+ case gpr_ds:
+ case gpr_es:
+ case gpr_fs:
+ case gpr_gs:
+ (&gpr.eax)[reg - gpr_eax] = value.GetAsUInt32();
+ break;
+
+ case fpu_fcw:
+ fpu.fcw = value.GetAsUInt16();
+ break;
+
+ case fpu_fsw:
+ fpu.fsw = value.GetAsUInt16();
+ break;
+
+ case fpu_ftw:
+ fpu.ftw = value.GetAsUInt8();
+ break;
+
+ case fpu_fop:
+ fpu.fop = value.GetAsUInt16();
+ break;
+
+ case fpu_ip:
+ fpu.ip = value.GetAsUInt32();
+ break;
+
+ case fpu_cs:
+ fpu.cs = value.GetAsUInt16();
+ break;
+
+ case fpu_dp:
+ fpu.dp = value.GetAsUInt32();
+ break;
+
+ case fpu_ds:
+ fpu.ds = value.GetAsUInt16();
+ break;
+
+ case fpu_mxcsr:
+ fpu.mxcsr = value.GetAsUInt32();
+ break;
+
+ case fpu_mxcsrmask:
+ fpu.mxcsrmask = value.GetAsUInt32();
+ 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:
+ // These values don't fit into scalar types,
+ // RegisterContext::ReadRegisterBytes() must be used for these registers
+ ::memcpy(fpu.stmm[reg - fpu_stmm0].bytes, value.GetBytes(),
+ value.GetByteSize());
+ return false;
+
+ case fpu_xmm0:
+ case fpu_xmm1:
+ case fpu_xmm2:
+ case fpu_xmm3:
+ case fpu_xmm4:
+ case fpu_xmm5:
+ case fpu_xmm6:
+ case fpu_xmm7:
+ // These values don't fit into scalar types,
+ // RegisterContext::ReadRegisterBytes() must be used for these registers
+ ::memcpy(fpu.xmm[reg - fpu_xmm0].bytes, value.GetBytes(),
+ value.GetByteSize());
+ return false;
+
+ case exc_trapno:
+ exc.trapno = value.GetAsUInt32();
+ break;
+
+ case exc_err:
+ exc.err = value.GetAsUInt32();
+ break;
+
+ case exc_faultvaddr:
+ exc.faultvaddr = value.GetAsUInt32();
+ break;
+
+ default:
+ return false;
+ }
+ return WriteRegisterSet(set) == 0;
+}
+
+bool RegisterContextDarwin_i386::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ data_sp = std::make_shared<DataBufferHeap>(REG_CONTEXT_SIZE, 0);
+ if (ReadGPR(false) == 0 && ReadFPU(false) == 0 && ReadEXC(false) == 0) {
+ uint8_t *dst = data_sp->GetBytes();
+ ::memcpy(dst, &gpr, sizeof(gpr));
+ dst += sizeof(gpr);
+
+ ::memcpy(dst, &fpu, sizeof(fpu));
+ dst += sizeof(gpr);
+
+ ::memcpy(dst, &exc, sizeof(exc));
+ return true;
+ }
+ return false;
+}
+
+bool RegisterContextDarwin_i386::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) {
+ const uint8_t *src = data_sp->GetBytes();
+ ::memcpy(&gpr, src, sizeof(gpr));
+ src += sizeof(gpr);
+
+ ::memcpy(&fpu, src, sizeof(fpu));
+ src += sizeof(gpr);
+
+ ::memcpy(&exc, src, sizeof(exc));
+ uint32_t success_count = 0;
+ if (WriteGPR() == 0)
+ ++success_count;
+ if (WriteFPU() == 0)
+ ++success_count;
+ if (WriteEXC() == 0)
+ ++success_count;
+ return success_count == 3;
+ }
+ return false;
+}
+
+uint32_t RegisterContextDarwin_i386::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t reg) {
+ if (kind == eRegisterKindGeneric) {
+ switch (reg) {
+ case LLDB_REGNUM_GENERIC_PC:
+ return gpr_eip;
+ case LLDB_REGNUM_GENERIC_SP:
+ return gpr_esp;
+ case LLDB_REGNUM_GENERIC_FP:
+ return gpr_ebp;
+ case LLDB_REGNUM_GENERIC_FLAGS:
+ return gpr_eflags;
+ case LLDB_REGNUM_GENERIC_RA:
+ default:
+ break;
+ }
+ } else if (kind == eRegisterKindEHFrame || kind == eRegisterKindDWARF) {
+ switch (reg) {
+ case dwarf_eax:
+ return gpr_eax;
+ case dwarf_ecx:
+ return gpr_ecx;
+ case dwarf_edx:
+ return gpr_edx;
+ case dwarf_ebx:
+ return gpr_ebx;
+ case dwarf_esp:
+ return gpr_esp;
+ case dwarf_ebp:
+ return gpr_ebp;
+ case dwarf_esi:
+ return gpr_esi;
+ case dwarf_edi:
+ return gpr_edi;
+ case dwarf_eip:
+ return gpr_eip;
+ case dwarf_eflags:
+ return gpr_eflags;
+ case dwarf_stmm0:
+ return fpu_stmm0;
+ case dwarf_stmm1:
+ return fpu_stmm1;
+ case dwarf_stmm2:
+ return fpu_stmm2;
+ case dwarf_stmm3:
+ return fpu_stmm3;
+ case dwarf_stmm4:
+ return fpu_stmm4;
+ case dwarf_stmm5:
+ return fpu_stmm5;
+ case dwarf_stmm6:
+ return fpu_stmm6;
+ case dwarf_stmm7:
+ return fpu_stmm7;
+ case dwarf_xmm0:
+ return fpu_xmm0;
+ case dwarf_xmm1:
+ return fpu_xmm1;
+ case dwarf_xmm2:
+ return fpu_xmm2;
+ case dwarf_xmm3:
+ return fpu_xmm3;
+ case dwarf_xmm4:
+ return fpu_xmm4;
+ case dwarf_xmm5:
+ return fpu_xmm5;
+ case dwarf_xmm6:
+ return fpu_xmm6;
+ case dwarf_xmm7:
+ return fpu_xmm7;
+ default:
+ break;
+ }
+ } else if (kind == eRegisterKindLLDB) {
+ return reg;
+ }
+ return LLDB_INVALID_REGNUM;
+}
+
+bool RegisterContextDarwin_i386::HardwareSingleStep(bool enable) {
+ if (ReadGPR(false) != 0)
+ return false;
+
+ const uint32_t trace_bit = 0x100u;
+ if (enable) {
+ // If the trace bit is already set, there is nothing to do
+ if (gpr.eflags & trace_bit)
+ return true;
+ else
+ gpr.eflags |= trace_bit;
+ } else {
+ // If the trace bit is already cleared, there is nothing to do
+ if (gpr.eflags & trace_bit)
+ gpr.eflags &= ~trace_bit;
+ else
+ return true;
+ }
+
+ return WriteGPR() == 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_i386.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_i386.h
new file mode 100644
index 000000000000..e52f0fe63250
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_i386.h
@@ -0,0 +1,208 @@
+//===-- RegisterContextDarwin_i386.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 liblldb_RegisterContextDarwin_i386_h_
+#define liblldb_RegisterContextDarwin_i386_h_
+
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/lldb-private.h"
+
+class RegisterContextDarwin_i386 : public lldb_private::RegisterContext {
+public:
+ RegisterContextDarwin_i386(lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx);
+
+ ~RegisterContextDarwin_i386() override;
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+ bool HardwareSingleStep(bool enable) override;
+
+ struct GPR {
+ 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;
+ };
+
+ struct MMSReg {
+ uint8_t bytes[10];
+ uint8_t pad[6];
+ };
+
+ struct XMMReg {
+ uint8_t bytes[16];
+ };
+
+ struct FPU {
+ uint32_t pad[2];
+ uint16_t fcw;
+ uint16_t fsw;
+ uint8_t ftw;
+ uint8_t pad1;
+ uint16_t fop;
+ uint32_t ip;
+ uint16_t cs;
+ uint16_t pad2;
+ uint32_t dp;
+ uint16_t ds;
+ uint16_t pad3;
+ uint32_t mxcsr;
+ uint32_t mxcsrmask;
+ MMSReg stmm[8];
+ XMMReg xmm[8];
+ uint8_t pad4[14 * 16];
+ int pad5;
+ };
+
+ struct EXC {
+ uint32_t trapno;
+ uint32_t err;
+ uint32_t faultvaddr;
+ };
+
+protected:
+ enum { GPRRegSet = 1, FPURegSet = 2, EXCRegSet = 3 };
+
+ enum {
+ GPRWordCount = sizeof(GPR) / sizeof(uint32_t),
+ FPUWordCount = sizeof(FPU) / sizeof(uint32_t),
+ EXCWordCount = sizeof(EXC) / sizeof(uint32_t)
+ };
+
+ enum { Read = 0, Write = 1, kNumErrors = 2 };
+
+ GPR gpr;
+ FPU fpu;
+ EXC exc;
+ int gpr_errs[2]; // Read/Write errors
+ int fpu_errs[2]; // Read/Write errors
+ int exc_errs[2]; // Read/Write errors
+
+ void InvalidateAllRegisterStates() {
+ SetError(GPRRegSet, Read, -1);
+ SetError(FPURegSet, Read, -1);
+ SetError(EXCRegSet, Read, -1);
+ }
+
+ int 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 GPRRegSet:
+ return gpr_errs[err_idx];
+ case FPURegSet:
+ return fpu_errs[err_idx];
+ case EXCRegSet:
+ return exc_errs[err_idx];
+ default:
+ break;
+ }
+ }
+ return -1;
+ }
+
+ bool SetError(int flavor, uint32_t err_idx, int err) {
+ if (err_idx < kNumErrors) {
+ switch (flavor) {
+ case GPRRegSet:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case FPURegSet:
+ fpu_errs[err_idx] = err;
+ return true;
+
+ case EXCRegSet:
+ exc_errs[err_idx] = err;
+ return true;
+
+ default:
+ break;
+ }
+ }
+ return false;
+ }
+
+ bool RegisterSetIsCached(int set) const { return GetError(set, Read) == 0; }
+
+ void LogGPR(lldb_private::Log *log, const char *title);
+
+ int ReadGPR(bool force);
+
+ int ReadFPU(bool force);
+
+ int ReadEXC(bool force);
+
+ int WriteGPR();
+
+ int WriteFPU();
+
+ int WriteEXC();
+
+ // Subclasses override these to do the actual reading.
+ virtual int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) = 0;
+
+ virtual int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) = 0;
+
+ virtual int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) = 0;
+
+ virtual int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) = 0;
+
+ virtual int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) = 0;
+
+ virtual int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) = 0;
+
+ int ReadRegisterSet(uint32_t set, bool force);
+
+ int WriteRegisterSet(uint32_t set);
+
+ static uint32_t GetRegisterNumber(uint32_t reg_kind, uint32_t reg_num);
+
+ static int GetSetForNativeRegNum(int reg_num);
+
+ static size_t GetRegisterInfosCount();
+
+ static const lldb_private::RegisterInfo *GetRegisterInfos();
+};
+
+#endif // liblldb_RegisterContextDarwin_i386_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.cpp
new file mode 100644
index 000000000000..62e512adc9f7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.cpp
@@ -0,0 +1,1078 @@
+//===-- RegisterContextDarwin_x86_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
+//
+//===----------------------------------------------------------------------===//
+
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stddef.h>
+
+#include <memory>
+
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Compiler.h"
+
+// Support building against older versions of LLVM, this macro was added
+// recently.
+#ifndef LLVM_EXTENSION
+#define LLVM_EXTENSION
+#endif
+
+#include "RegisterContextDarwin_x86_64.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+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,
+
+ 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,
+
+ exc_trapno,
+ exc_err,
+ exc_faultvaddr,
+
+ k_num_registers,
+
+ // 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 ehframe_dwarf_regnums {
+ ehframe_dwarf_gpr_rax = 0,
+ ehframe_dwarf_gpr_rdx,
+ ehframe_dwarf_gpr_rcx,
+ ehframe_dwarf_gpr_rbx,
+ ehframe_dwarf_gpr_rsi,
+ ehframe_dwarf_gpr_rdi,
+ ehframe_dwarf_gpr_rbp,
+ ehframe_dwarf_gpr_rsp,
+ ehframe_dwarf_gpr_r8,
+ ehframe_dwarf_gpr_r9,
+ ehframe_dwarf_gpr_r10,
+ ehframe_dwarf_gpr_r11,
+ ehframe_dwarf_gpr_r12,
+ ehframe_dwarf_gpr_r13,
+ ehframe_dwarf_gpr_r14,
+ ehframe_dwarf_gpr_r15,
+ ehframe_dwarf_gpr_rip,
+ ehframe_dwarf_fpu_xmm0,
+ ehframe_dwarf_fpu_xmm1,
+ ehframe_dwarf_fpu_xmm2,
+ ehframe_dwarf_fpu_xmm3,
+ ehframe_dwarf_fpu_xmm4,
+ ehframe_dwarf_fpu_xmm5,
+ ehframe_dwarf_fpu_xmm6,
+ ehframe_dwarf_fpu_xmm7,
+ ehframe_dwarf_fpu_xmm8,
+ ehframe_dwarf_fpu_xmm9,
+ ehframe_dwarf_fpu_xmm10,
+ ehframe_dwarf_fpu_xmm11,
+ ehframe_dwarf_fpu_xmm12,
+ ehframe_dwarf_fpu_xmm13,
+ ehframe_dwarf_fpu_xmm14,
+ ehframe_dwarf_fpu_xmm15,
+ ehframe_dwarf_fpu_stmm0,
+ ehframe_dwarf_fpu_stmm1,
+ ehframe_dwarf_fpu_stmm2,
+ ehframe_dwarf_fpu_stmm3,
+ ehframe_dwarf_fpu_stmm4,
+ ehframe_dwarf_fpu_stmm5,
+ ehframe_dwarf_fpu_stmm6,
+ ehframe_dwarf_fpu_stmm7
+
+};
+
+#define GPR_OFFSET(reg) \
+ (LLVM_EXTENSION offsetof(RegisterContextDarwin_x86_64::GPR, reg))
+#define FPU_OFFSET(reg) \
+ (LLVM_EXTENSION offsetof(RegisterContextDarwin_x86_64::FPU, reg) + \
+ sizeof(RegisterContextDarwin_x86_64::GPR))
+#define EXC_OFFSET(reg) \
+ (LLVM_EXTENSION offsetof(RegisterContextDarwin_x86_64::EXC, reg) + \
+ sizeof(RegisterContextDarwin_x86_64::GPR) + \
+ sizeof(RegisterContextDarwin_x86_64::FPU))
+
+// 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, alt) \
+ #reg, alt, sizeof(((RegisterContextDarwin_x86_64::GPR *) NULL)->reg), \
+ GPR_OFFSET(reg), eEncodingUint, eFormatHex
+#define DEFINE_FPU_UINT(reg) \
+ #reg, NULL, sizeof(((RegisterContextDarwin_x86_64::FPU *) NULL)->reg), \
+ FPU_OFFSET(reg), eEncodingUint, eFormatHex
+#define DEFINE_FPU_VECT(reg, i) \
+ #reg #i, NULL, \
+ sizeof(((RegisterContextDarwin_x86_64::FPU *) NULL)->reg[i].bytes), \
+ FPU_OFFSET(reg[i]), eEncodingVector, eFormatVectorOfUInt8, \
+ {ehframe_dwarf_fpu_##reg##i, \
+ ehframe_dwarf_fpu_##reg##i, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, fpu_##reg##i }, \
+ nullptr, nullptr, nullptr, 0
+#define DEFINE_EXC(reg) \
+ #reg, NULL, sizeof(((RegisterContextDarwin_x86_64::EXC *) NULL)->reg), \
+ EXC_OFFSET(reg), eEncodingUint, eFormatHex
+
+#define REG_CONTEXT_SIZE \
+ (sizeof(RegisterContextDarwin_x86_64::GPR) + \
+ sizeof(RegisterContextDarwin_x86_64::FPU) + \
+ sizeof(RegisterContextDarwin_x86_64::EXC))
+
+// General purpose registers for 64 bit
+static RegisterInfo g_register_infos[] = {
+ // Macro auto defines most stuff EH_FRAME DWARF
+ // GENERIC PROCESS PLUGIN LLDB
+ // =============================== ======================
+ // =================== ========================== ====================
+ // ===================
+ {DEFINE_GPR(rax, nullptr),
+ {ehframe_dwarf_gpr_rax, ehframe_dwarf_gpr_rax, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_rax},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(rbx, nullptr),
+ {ehframe_dwarf_gpr_rbx, ehframe_dwarf_gpr_rbx, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_rbx},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(rcx, nullptr),
+ {ehframe_dwarf_gpr_rcx, ehframe_dwarf_gpr_rcx, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_rcx},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(rdx, nullptr),
+ {ehframe_dwarf_gpr_rdx, ehframe_dwarf_gpr_rdx, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_rdx},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(rdi, nullptr),
+ {ehframe_dwarf_gpr_rdi, ehframe_dwarf_gpr_rdi, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_rdi},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(rsi, nullptr),
+ {ehframe_dwarf_gpr_rsi, ehframe_dwarf_gpr_rsi, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_rsi},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(rbp, "fp"),
+ {ehframe_dwarf_gpr_rbp, ehframe_dwarf_gpr_rbp, LLDB_REGNUM_GENERIC_FP,
+ LLDB_INVALID_REGNUM, gpr_rbp},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(rsp, "sp"),
+ {ehframe_dwarf_gpr_rsp, ehframe_dwarf_gpr_rsp, LLDB_REGNUM_GENERIC_SP,
+ LLDB_INVALID_REGNUM, gpr_rsp},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(r8, nullptr),
+ {ehframe_dwarf_gpr_r8, ehframe_dwarf_gpr_r8, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_r8},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(r9, nullptr),
+ {ehframe_dwarf_gpr_r9, ehframe_dwarf_gpr_r9, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_r9},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(r10, nullptr),
+ {ehframe_dwarf_gpr_r10, ehframe_dwarf_gpr_r10, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_r10},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(r11, nullptr),
+ {ehframe_dwarf_gpr_r11, ehframe_dwarf_gpr_r11, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_r11},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(r12, nullptr),
+ {ehframe_dwarf_gpr_r12, ehframe_dwarf_gpr_r12, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_r12},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(r13, nullptr),
+ {ehframe_dwarf_gpr_r13, ehframe_dwarf_gpr_r13, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_r13},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(r14, nullptr),
+ {ehframe_dwarf_gpr_r14, ehframe_dwarf_gpr_r14, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_r14},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(r15, nullptr),
+ {ehframe_dwarf_gpr_r15, ehframe_dwarf_gpr_r15, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_r15},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(rip, "pc"),
+ {ehframe_dwarf_gpr_rip, ehframe_dwarf_gpr_rip, LLDB_REGNUM_GENERIC_PC,
+ LLDB_INVALID_REGNUM, gpr_rip},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(rflags, "flags"),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_FLAGS,
+ LLDB_INVALID_REGNUM, gpr_rflags},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(cs, nullptr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_cs},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(fs, nullptr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_fs},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_GPR(gs, nullptr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, gpr_gs},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {DEFINE_FPU_UINT(fcw),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_fcw},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(fsw),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_fsw},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(ftw),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_ftw},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(fop),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_fop},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(ip),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_ip},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(cs),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_cs},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(dp),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_dp},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(ds),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_ds},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(mxcsr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_mxcsr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_UINT(mxcsrmask),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_mxcsrmask},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_FPU_VECT(stmm, 0)},
+ {DEFINE_FPU_VECT(stmm, 1)},
+ {DEFINE_FPU_VECT(stmm, 2)},
+ {DEFINE_FPU_VECT(stmm, 3)},
+ {DEFINE_FPU_VECT(stmm, 4)},
+ {DEFINE_FPU_VECT(stmm, 5)},
+ {DEFINE_FPU_VECT(stmm, 6)},
+ {DEFINE_FPU_VECT(stmm, 7)},
+ {DEFINE_FPU_VECT(xmm, 0)},
+ {DEFINE_FPU_VECT(xmm, 1)},
+ {DEFINE_FPU_VECT(xmm, 2)},
+ {DEFINE_FPU_VECT(xmm, 3)},
+ {DEFINE_FPU_VECT(xmm, 4)},
+ {DEFINE_FPU_VECT(xmm, 5)},
+ {DEFINE_FPU_VECT(xmm, 6)},
+ {DEFINE_FPU_VECT(xmm, 7)},
+ {DEFINE_FPU_VECT(xmm, 8)},
+ {DEFINE_FPU_VECT(xmm, 9)},
+ {DEFINE_FPU_VECT(xmm, 10)},
+ {DEFINE_FPU_VECT(xmm, 11)},
+ {DEFINE_FPU_VECT(xmm, 12)},
+ {DEFINE_FPU_VECT(xmm, 13)},
+ {DEFINE_FPU_VECT(xmm, 14)},
+ {DEFINE_FPU_VECT(xmm, 15)},
+
+ {DEFINE_EXC(trapno),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, exc_trapno},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_EXC(err),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, exc_err},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {DEFINE_EXC(faultvaddr),
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, exc_faultvaddr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0}};
+
+static size_t k_num_register_infos = llvm::array_lengthof(g_register_infos);
+
+RegisterContextDarwin_x86_64::RegisterContextDarwin_x86_64(
+ Thread &thread, uint32_t concrete_frame_idx)
+ : RegisterContext(thread, concrete_frame_idx), gpr(), fpu(), exc() {
+ uint32_t i;
+ for (i = 0; i < kNumErrors; i++) {
+ gpr_errs[i] = -1;
+ fpu_errs[i] = -1;
+ exc_errs[i] = -1;
+ }
+}
+
+RegisterContextDarwin_x86_64::~RegisterContextDarwin_x86_64() {}
+
+void RegisterContextDarwin_x86_64::InvalidateAllRegisters() {
+ InvalidateAllRegisterStates();
+}
+
+size_t RegisterContextDarwin_x86_64::GetRegisterCount() {
+ assert(k_num_register_infos == k_num_registers);
+ return k_num_registers;
+}
+
+const RegisterInfo *
+RegisterContextDarwin_x86_64::GetRegisterInfoAtIndex(size_t reg) {
+ assert(k_num_register_infos == k_num_registers);
+ if (reg < k_num_registers)
+ return &g_register_infos[reg];
+ return nullptr;
+}
+
+size_t RegisterContextDarwin_x86_64::GetRegisterInfosCount() {
+ return k_num_register_infos;
+}
+
+const lldb_private::RegisterInfo *
+RegisterContextDarwin_x86_64::GetRegisterInfos() {
+ return g_register_infos;
+}
+
+static uint32_t g_gpr_regnums[] = {
+ gpr_rax, 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};
+
+static uint32_t g_fpu_regnums[] = {
+ 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};
+
+static uint32_t g_exc_regnums[] = {exc_trapno, exc_err, exc_faultvaddr};
+
+// Number of registers in each register set
+const size_t k_num_gpr_registers = llvm::array_lengthof(g_gpr_regnums);
+const size_t k_num_fpu_registers = llvm::array_lengthof(g_fpu_regnums);
+const size_t k_num_exc_registers = llvm::array_lengthof(g_exc_regnums);
+
+// 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 RegisterSet g_reg_sets[] = {
+ {
+ "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums,
+ },
+ {"Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums},
+ {"Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums}};
+
+const size_t k_num_regsets = llvm::array_lengthof(g_reg_sets);
+
+size_t RegisterContextDarwin_x86_64::GetRegisterSetCount() {
+ return k_num_regsets;
+}
+
+const RegisterSet *
+RegisterContextDarwin_x86_64::GetRegisterSet(size_t reg_set) {
+ if (reg_set < k_num_regsets)
+ return &g_reg_sets[reg_set];
+ return nullptr;
+}
+
+int RegisterContextDarwin_x86_64::GetSetForNativeRegNum(int reg_num) {
+ if (reg_num < fpu_fcw)
+ return GPRRegSet;
+ else if (reg_num < exc_trapno)
+ return FPURegSet;
+ else if (reg_num < k_num_registers)
+ return EXCRegSet;
+ return -1;
+}
+
+void RegisterContextDarwin_x86_64::LogGPR(Log *log, const char *format, ...) {
+ if (log) {
+ if (format) {
+ va_list args;
+ va_start(args, format);
+ log->VAPrintf(format, args);
+ va_end(args);
+ }
+ for (uint32_t i = 0; i < k_num_gpr_registers; i++) {
+ uint32_t reg = gpr_rax + i;
+ log->Printf("%12s = 0x%16.16" PRIx64, g_register_infos[reg].name,
+ (&gpr.rax)[reg]);
+ }
+ }
+}
+
+int RegisterContextDarwin_x86_64::ReadGPR(bool force) {
+ int set = GPRRegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadGPR(GetThreadID(), set, gpr));
+ }
+ return GetError(GPRRegSet, Read);
+}
+
+int RegisterContextDarwin_x86_64::ReadFPU(bool force) {
+ int set = FPURegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadFPU(GetThreadID(), set, fpu));
+ }
+ return GetError(FPURegSet, Read);
+}
+
+int RegisterContextDarwin_x86_64::ReadEXC(bool force) {
+ int set = EXCRegSet;
+ if (force || !RegisterSetIsCached(set)) {
+ SetError(set, Read, DoReadEXC(GetThreadID(), set, exc));
+ }
+ return GetError(EXCRegSet, Read);
+}
+
+int RegisterContextDarwin_x86_64::WriteGPR() {
+ int set = GPRRegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return -1;
+ }
+ SetError(set, Write, DoWriteGPR(GetThreadID(), set, gpr));
+ SetError(set, Read, -1);
+ return GetError(set, Write);
+}
+
+int RegisterContextDarwin_x86_64::WriteFPU() {
+ int set = FPURegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return -1;
+ }
+ SetError(set, Write, DoWriteFPU(GetThreadID(), set, fpu));
+ SetError(set, Read, -1);
+ return GetError(set, Write);
+}
+
+int RegisterContextDarwin_x86_64::WriteEXC() {
+ int set = EXCRegSet;
+ if (!RegisterSetIsCached(set)) {
+ SetError(set, Write, -1);
+ return -1;
+ }
+ SetError(set, Write, DoWriteEXC(GetThreadID(), set, exc));
+ SetError(set, Read, -1);
+ return GetError(set, Write);
+}
+
+int RegisterContextDarwin_x86_64::ReadRegisterSet(uint32_t set, bool force) {
+ switch (set) {
+ case GPRRegSet:
+ return ReadGPR(force);
+ case FPURegSet:
+ return ReadFPU(force);
+ case EXCRegSet:
+ return ReadEXC(force);
+ default:
+ break;
+ }
+ return -1;
+}
+
+int RegisterContextDarwin_x86_64::WriteRegisterSet(uint32_t set) {
+ // Make sure we have a valid context to set.
+ switch (set) {
+ case GPRRegSet:
+ return WriteGPR();
+ case FPURegSet:
+ return WriteFPU();
+ case EXCRegSet:
+ return WriteEXC();
+ default:
+ break;
+ }
+ return -1;
+}
+
+bool RegisterContextDarwin_x86_64::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ int set = RegisterContextDarwin_x86_64::GetSetForNativeRegNum(reg);
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != 0)
+ return false;
+
+ switch (reg) {
+ case gpr_rax:
+ case gpr_rbx:
+ case gpr_rcx:
+ case gpr_rdx:
+ case gpr_rdi:
+ case gpr_rsi:
+ case gpr_rbp:
+ case gpr_rsp:
+ case gpr_r8:
+ case gpr_r9:
+ case gpr_r10:
+ case gpr_r11:
+ case gpr_r12:
+ case gpr_r13:
+ case gpr_r14:
+ case gpr_r15:
+ case gpr_rip:
+ case gpr_rflags:
+ case gpr_cs:
+ case gpr_fs:
+ case gpr_gs:
+ value = (&gpr.rax)[reg - gpr_rax];
+ break;
+
+ case fpu_fcw:
+ value = fpu.fcw;
+ break;
+
+ case fpu_fsw:
+ value = fpu.fsw;
+ break;
+
+ case fpu_ftw:
+ value = fpu.ftw;
+ break;
+
+ case fpu_fop:
+ value = fpu.fop;
+ break;
+
+ case fpu_ip:
+ value = fpu.ip;
+ break;
+
+ case fpu_cs:
+ value = fpu.cs;
+ break;
+
+ case fpu_dp:
+ value = fpu.dp;
+ break;
+
+ case fpu_ds:
+ value = fpu.ds;
+ break;
+
+ case fpu_mxcsr:
+ value = fpu.mxcsr;
+ break;
+
+ case fpu_mxcsrmask:
+ value = fpu.mxcsrmask;
+ 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:
+ value.SetBytes(fpu.stmm[reg - fpu_stmm0].bytes, reg_info->byte_size,
+ endian::InlHostByteOrder());
+ 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:
+ value.SetBytes(fpu.xmm[reg - fpu_xmm0].bytes, reg_info->byte_size,
+ endian::InlHostByteOrder());
+ break;
+
+ case exc_trapno:
+ value = exc.trapno;
+ break;
+
+ case exc_err:
+ value = exc.err;
+ break;
+
+ case exc_faultvaddr:
+ value = exc.faultvaddr;
+ break;
+
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool RegisterContextDarwin_x86_64::WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ int set = RegisterContextDarwin_x86_64::GetSetForNativeRegNum(reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != 0)
+ return false;
+
+ switch (reg) {
+ case gpr_rax:
+ case gpr_rbx:
+ case gpr_rcx:
+ case gpr_rdx:
+ case gpr_rdi:
+ case gpr_rsi:
+ case gpr_rbp:
+ case gpr_rsp:
+ case gpr_r8:
+ case gpr_r9:
+ case gpr_r10:
+ case gpr_r11:
+ case gpr_r12:
+ case gpr_r13:
+ case gpr_r14:
+ case gpr_r15:
+ case gpr_rip:
+ case gpr_rflags:
+ case gpr_cs:
+ case gpr_fs:
+ case gpr_gs:
+ (&gpr.rax)[reg - gpr_rax] = value.GetAsUInt64();
+ break;
+
+ case fpu_fcw:
+ fpu.fcw = value.GetAsUInt16();
+ break;
+
+ case fpu_fsw:
+ fpu.fsw = value.GetAsUInt16();
+ break;
+
+ case fpu_ftw:
+ fpu.ftw = value.GetAsUInt8();
+ break;
+
+ case fpu_fop:
+ fpu.fop = value.GetAsUInt16();
+ break;
+
+ case fpu_ip:
+ fpu.ip = value.GetAsUInt32();
+ break;
+
+ case fpu_cs:
+ fpu.cs = value.GetAsUInt16();
+ break;
+
+ case fpu_dp:
+ fpu.dp = value.GetAsUInt32();
+ break;
+
+ case fpu_ds:
+ fpu.ds = value.GetAsUInt16();
+ break;
+
+ case fpu_mxcsr:
+ fpu.mxcsr = value.GetAsUInt32();
+ break;
+
+ case fpu_mxcsrmask:
+ fpu.mxcsrmask = value.GetAsUInt32();
+ 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(fpu.stmm[reg - fpu_stmm0].bytes, value.GetBytes(),
+ value.GetByteSize());
+ 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(fpu.xmm[reg - fpu_xmm0].bytes, value.GetBytes(),
+ value.GetByteSize());
+ return false;
+
+ case exc_trapno:
+ exc.trapno = value.GetAsUInt32();
+ break;
+
+ case exc_err:
+ exc.err = value.GetAsUInt32();
+ break;
+
+ case exc_faultvaddr:
+ exc.faultvaddr = value.GetAsUInt64();
+ break;
+
+ default:
+ return false;
+ }
+ return WriteRegisterSet(set) == 0;
+}
+
+bool RegisterContextDarwin_x86_64::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ data_sp = std::make_shared<DataBufferHeap>(REG_CONTEXT_SIZE, 0);
+ if (ReadGPR(false) == 0 && ReadFPU(false) == 0 && ReadEXC(false) == 0) {
+ uint8_t *dst = data_sp->GetBytes();
+ ::memcpy(dst, &gpr, sizeof(gpr));
+ dst += sizeof(gpr);
+
+ ::memcpy(dst, &fpu, sizeof(fpu));
+ dst += sizeof(gpr);
+
+ ::memcpy(dst, &exc, sizeof(exc));
+ return true;
+ }
+ return false;
+}
+
+bool RegisterContextDarwin_x86_64::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) {
+ const uint8_t *src = data_sp->GetBytes();
+ ::memcpy(&gpr, src, sizeof(gpr));
+ src += sizeof(gpr);
+
+ ::memcpy(&fpu, src, sizeof(fpu));
+ src += sizeof(gpr);
+
+ ::memcpy(&exc, src, sizeof(exc));
+ uint32_t success_count = 0;
+ if (WriteGPR() == 0)
+ ++success_count;
+ if (WriteFPU() == 0)
+ ++success_count;
+ if (WriteEXC() == 0)
+ ++success_count;
+ return success_count == 3;
+ }
+ return false;
+}
+
+uint32_t RegisterContextDarwin_x86_64::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t reg) {
+ if (kind == eRegisterKindGeneric) {
+ switch (reg) {
+ case LLDB_REGNUM_GENERIC_PC:
+ return gpr_rip;
+ case LLDB_REGNUM_GENERIC_SP:
+ return gpr_rsp;
+ case LLDB_REGNUM_GENERIC_FP:
+ return gpr_rbp;
+ case LLDB_REGNUM_GENERIC_FLAGS:
+ return gpr_rflags;
+ case LLDB_REGNUM_GENERIC_RA:
+ default:
+ break;
+ }
+ } else if (kind == eRegisterKindEHFrame || kind == eRegisterKindDWARF) {
+ switch (reg) {
+ case ehframe_dwarf_gpr_rax:
+ return gpr_rax;
+ case ehframe_dwarf_gpr_rdx:
+ return gpr_rdx;
+ case ehframe_dwarf_gpr_rcx:
+ return gpr_rcx;
+ case ehframe_dwarf_gpr_rbx:
+ return gpr_rbx;
+ case ehframe_dwarf_gpr_rsi:
+ return gpr_rsi;
+ case ehframe_dwarf_gpr_rdi:
+ return gpr_rdi;
+ case ehframe_dwarf_gpr_rbp:
+ return gpr_rbp;
+ case ehframe_dwarf_gpr_rsp:
+ return gpr_rsp;
+ case ehframe_dwarf_gpr_r8:
+ return gpr_r8;
+ case ehframe_dwarf_gpr_r9:
+ return gpr_r9;
+ case ehframe_dwarf_gpr_r10:
+ return gpr_r10;
+ case ehframe_dwarf_gpr_r11:
+ return gpr_r11;
+ case ehframe_dwarf_gpr_r12:
+ return gpr_r12;
+ case ehframe_dwarf_gpr_r13:
+ return gpr_r13;
+ case ehframe_dwarf_gpr_r14:
+ return gpr_r14;
+ case ehframe_dwarf_gpr_r15:
+ return gpr_r15;
+ case ehframe_dwarf_gpr_rip:
+ return gpr_rip;
+ case ehframe_dwarf_fpu_xmm0:
+ return fpu_xmm0;
+ case ehframe_dwarf_fpu_xmm1:
+ return fpu_xmm1;
+ case ehframe_dwarf_fpu_xmm2:
+ return fpu_xmm2;
+ case ehframe_dwarf_fpu_xmm3:
+ return fpu_xmm3;
+ case ehframe_dwarf_fpu_xmm4:
+ return fpu_xmm4;
+ case ehframe_dwarf_fpu_xmm5:
+ return fpu_xmm5;
+ case ehframe_dwarf_fpu_xmm6:
+ return fpu_xmm6;
+ case ehframe_dwarf_fpu_xmm7:
+ return fpu_xmm7;
+ case ehframe_dwarf_fpu_xmm8:
+ return fpu_xmm8;
+ case ehframe_dwarf_fpu_xmm9:
+ return fpu_xmm9;
+ case ehframe_dwarf_fpu_xmm10:
+ return fpu_xmm10;
+ case ehframe_dwarf_fpu_xmm11:
+ return fpu_xmm11;
+ case ehframe_dwarf_fpu_xmm12:
+ return fpu_xmm12;
+ case ehframe_dwarf_fpu_xmm13:
+ return fpu_xmm13;
+ case ehframe_dwarf_fpu_xmm14:
+ return fpu_xmm14;
+ case ehframe_dwarf_fpu_xmm15:
+ return fpu_xmm15;
+ case ehframe_dwarf_fpu_stmm0:
+ return fpu_stmm0;
+ case ehframe_dwarf_fpu_stmm1:
+ return fpu_stmm1;
+ case ehframe_dwarf_fpu_stmm2:
+ return fpu_stmm2;
+ case ehframe_dwarf_fpu_stmm3:
+ return fpu_stmm3;
+ case ehframe_dwarf_fpu_stmm4:
+ return fpu_stmm4;
+ case ehframe_dwarf_fpu_stmm5:
+ return fpu_stmm5;
+ case ehframe_dwarf_fpu_stmm6:
+ return fpu_stmm6;
+ case ehframe_dwarf_fpu_stmm7:
+ return fpu_stmm7;
+ default:
+ break;
+ }
+ } else if (kind == eRegisterKindLLDB) {
+ return reg;
+ }
+ return LLDB_INVALID_REGNUM;
+}
+
+bool RegisterContextDarwin_x86_64::HardwareSingleStep(bool enable) {
+ if (ReadGPR(true) != 0)
+ return false;
+
+ const uint64_t trace_bit = 0x100ull;
+ if (enable) {
+
+ if (gpr.rflags & trace_bit)
+ return true; // trace bit is already set, there is nothing to do
+ else
+ gpr.rflags |= trace_bit;
+ } else {
+ if (gpr.rflags & trace_bit)
+ gpr.rflags &= ~trace_bit;
+ else
+ return true; // trace bit is clear, there is nothing to do
+ }
+
+ return WriteGPR() == 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.h
new file mode 100644
index 000000000000..1a65a4f28b33
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.h
@@ -0,0 +1,213 @@
+//===-- RegisterContextDarwin_x86_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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextDarwin_x86_64_h_
+#define liblldb_RegisterContextDarwin_x86_64_h_
+
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/lldb-private.h"
+
+class RegisterContextDarwin_x86_64 : public lldb_private::RegisterContext {
+public:
+ RegisterContextDarwin_x86_64(lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx);
+
+ ~RegisterContextDarwin_x86_64() override;
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+ bool HardwareSingleStep(bool enable) override;
+
+ struct GPR {
+ 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;
+ };
+
+ struct MMSReg {
+ uint8_t bytes[10];
+ uint8_t pad[6];
+ };
+
+ struct XMMReg {
+ uint8_t bytes[16];
+ };
+
+ struct FPU {
+ uint32_t pad[2];
+ uint16_t fcw; // "fctrl"
+ uint16_t fsw; // "fstat"
+ uint8_t ftw; // "ftag"
+ uint8_t pad1;
+ uint16_t fop; // "fop"
+ uint32_t ip; // "fioff"
+ uint16_t cs; // "fiseg"
+ uint16_t pad2;
+ uint32_t dp; // "fooff"
+ uint16_t ds; // "foseg"
+ uint16_t pad3;
+ uint32_t mxcsr;
+ uint32_t mxcsrmask;
+ MMSReg stmm[8];
+ XMMReg xmm[16];
+ uint8_t pad4[6 * 16];
+ int pad5;
+ };
+
+ struct EXC {
+ uint32_t trapno;
+ uint32_t err;
+ uint64_t faultvaddr;
+ };
+
+protected:
+ enum { GPRRegSet = 4, FPURegSet = 5, EXCRegSet = 6 };
+
+ enum {
+ GPRWordCount = sizeof(GPR) / sizeof(uint32_t),
+ FPUWordCount = sizeof(FPU) / sizeof(uint32_t),
+ EXCWordCount = sizeof(EXC) / sizeof(uint32_t)
+ };
+
+ enum { Read = 0, Write = 1, kNumErrors = 2 };
+
+ GPR gpr;
+ FPU fpu;
+ EXC exc;
+ int gpr_errs[2]; // Read/Write errors
+ int fpu_errs[2]; // Read/Write errors
+ int exc_errs[2]; // Read/Write errors
+
+ void InvalidateAllRegisterStates() {
+ SetError(GPRRegSet, Read, -1);
+ SetError(FPURegSet, Read, -1);
+ SetError(EXCRegSet, Read, -1);
+ }
+
+ int 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 GPRRegSet:
+ return gpr_errs[err_idx];
+ case FPURegSet:
+ return fpu_errs[err_idx];
+ case EXCRegSet:
+ return exc_errs[err_idx];
+ default:
+ break;
+ }
+ }
+ return -1;
+ }
+
+ bool SetError(int flavor, uint32_t err_idx, int err) {
+ if (err_idx < kNumErrors) {
+ switch (flavor) {
+ case GPRRegSet:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case FPURegSet:
+ fpu_errs[err_idx] = err;
+ return true;
+
+ case EXCRegSet:
+ exc_errs[err_idx] = err;
+ return true;
+
+ default:
+ break;
+ }
+ }
+ return false;
+ }
+
+ bool RegisterSetIsCached(int set) const { return GetError(set, Read) == 0; }
+
+ void LogGPR(lldb_private::Log *log, const char *format, ...);
+
+ int ReadGPR(bool force);
+
+ int ReadFPU(bool force);
+
+ int ReadEXC(bool force);
+
+ int WriteGPR();
+
+ int WriteFPU();
+
+ int WriteEXC();
+
+ // Subclasses override these to do the actual reading.
+ virtual int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) = 0;
+
+ virtual int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) = 0;
+
+ virtual int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) = 0;
+
+ virtual int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) = 0;
+
+ virtual int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) = 0;
+
+ virtual int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) = 0;
+
+ int ReadRegisterSet(uint32_t set, bool force);
+
+ int WriteRegisterSet(uint32_t set);
+
+ static uint32_t GetRegisterNumber(uint32_t reg_kind, uint32_t reg_num);
+
+ static int GetSetForNativeRegNum(int reg_num);
+
+ static size_t GetRegisterInfosCount();
+
+ static const lldb_private::RegisterInfo *GetRegisterInfos();
+};
+
+#endif // liblldb_RegisterContextDarwin_x86_64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDummy.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDummy.cpp
new file mode 100644
index 000000000000..6832b6095931
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDummy.cpp
@@ -0,0 +1,120 @@
+//===-- RegisterContextDummy.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 "lldb/Core/Address.h"
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Symbol/FuncUnwinders.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/lldb-private.h"
+
+#include "RegisterContextDummy.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+RegisterContextDummy::RegisterContextDummy(Thread &thread,
+ uint32_t concrete_frame_idx,
+ uint32_t address_byte_size)
+ : RegisterContext(thread, concrete_frame_idx) {
+ m_reg_set0.name = "General Purpose Registers";
+ m_reg_set0.short_name = "GPR";
+ m_reg_set0.num_registers = 1;
+ m_reg_set0.registers = new uint32_t(0);
+
+ m_pc_reg_info.name = "pc";
+ m_pc_reg_info.alt_name = "pc";
+ m_pc_reg_info.byte_offset = 0;
+ m_pc_reg_info.byte_size = address_byte_size;
+ m_pc_reg_info.encoding = eEncodingUint;
+ m_pc_reg_info.format = eFormatPointer;
+ m_pc_reg_info.invalidate_regs = nullptr;
+ m_pc_reg_info.value_regs = nullptr;
+ m_pc_reg_info.kinds[eRegisterKindEHFrame] = LLDB_INVALID_REGNUM;
+ m_pc_reg_info.kinds[eRegisterKindDWARF] = LLDB_INVALID_REGNUM;
+ m_pc_reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC;
+ m_pc_reg_info.kinds[eRegisterKindProcessPlugin] = LLDB_INVALID_REGNUM;
+ m_pc_reg_info.kinds[eRegisterKindLLDB] = LLDB_INVALID_REGNUM;
+}
+
+RegisterContextDummy::~RegisterContextDummy() {
+ delete m_reg_set0.registers;
+ delete m_pc_reg_info.invalidate_regs;
+ delete m_pc_reg_info.value_regs;
+}
+
+void RegisterContextDummy::InvalidateAllRegisters() {}
+
+size_t RegisterContextDummy::GetRegisterCount() { return 1; }
+
+const lldb_private::RegisterInfo *
+RegisterContextDummy::GetRegisterInfoAtIndex(size_t reg) {
+ if (reg)
+ return nullptr;
+ return &m_pc_reg_info;
+}
+
+size_t RegisterContextDummy::GetRegisterSetCount() { return 1; }
+
+const lldb_private::RegisterSet *
+RegisterContextDummy::GetRegisterSet(size_t reg_set) {
+ if (reg_set)
+ return nullptr;
+ return &m_reg_set0;
+}
+
+bool RegisterContextDummy::ReadRegister(
+ const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) {
+ if (!reg_info)
+ return false;
+ uint32_t reg_number = reg_info->kinds[eRegisterKindGeneric];
+ if (reg_number == LLDB_REGNUM_GENERIC_PC) {
+ value.SetUInt(LLDB_INVALID_ADDRESS, reg_info->byte_size);
+ return true;
+ }
+ return false;
+}
+
+bool RegisterContextDummy::WriteRegister(
+ const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) {
+ return false;
+}
+
+bool RegisterContextDummy::ReadAllRegisterValues(lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextDummy::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+uint32_t RegisterContextDummy::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ if (kind == eRegisterKindGeneric && num == LLDB_REGNUM_GENERIC_PC)
+ return 0;
+ return LLDB_INVALID_REGNUM;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDummy.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDummy.h
new file mode 100644
index 000000000000..bdaa2217d207
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDummy.h
@@ -0,0 +1,64 @@
+//===-- RegisterContextDummy.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_RegisterContextDummy_h_
+#define lldb_RegisterContextDummy_h_
+
+#include <vector>
+
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class RegisterContextDummy : public lldb_private::RegisterContext {
+public:
+ typedef std::shared_ptr<RegisterContextDummy> SharedPtr;
+
+ RegisterContextDummy(Thread &thread, uint32_t concrete_frame_idx,
+ uint32_t address_byte_size);
+
+ ~RegisterContextDummy() override;
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t reg_set) override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+private:
+ // For RegisterContextLLDB only
+
+ lldb_private::RegisterSet m_reg_set0; // register set 0 (PC only)
+ lldb_private::RegisterInfo m_pc_reg_info;
+
+ DISALLOW_COPY_AND_ASSIGN(RegisterContextDummy);
+};
+
+} // namespace lldb_private
+
+#endif // lldb_RegisterContextDummy_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.cpp
new file mode 100644
index 000000000000..b90b38108267
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.cpp
@@ -0,0 +1,80 @@
+//===-- RegisterContextFreeBSD_i386.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 "RegisterContextFreeBSD_i386.h"
+#include "RegisterContextPOSIX_x86.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+// http://svnweb.freebsd.org/base/head/sys/x86/include/reg.h
+struct GPR {
+ uint32_t fs;
+ uint32_t es;
+ uint32_t ds;
+ uint32_t edi;
+ uint32_t esi;
+ uint32_t ebp;
+ uint32_t isp;
+ uint32_t ebx;
+ uint32_t edx;
+ uint32_t ecx;
+ uint32_t eax;
+ uint32_t trapno;
+ uint32_t err;
+ uint32_t eip;
+ uint32_t cs;
+ uint32_t eflags;
+ uint32_t esp;
+ uint32_t ss;
+ uint32_t gs;
+};
+
+struct dbreg {
+ uint32_t dr[8]; /* debug registers */
+ /* Index 0-3: debug address registers */
+ /* Index 4-5: reserved */
+ /* Index 6: debug status */
+ /* Index 7: debug control */
+};
+
+using FPR_i386 = FXSAVE;
+
+struct UserArea {
+ GPR gpr;
+ FPR_i386 i387;
+};
+
+#define DR_SIZE sizeof(uint32_t)
+#define DR_OFFSET(reg_index) (LLVM_EXTENSION offsetof(dbreg, dr[reg_index]))
+
+// Include RegisterInfos_i386 to declare our g_register_infos_i386 structure.
+#define DECLARE_REGISTER_INFOS_I386_STRUCT
+#include "RegisterInfos_i386.h"
+#undef DECLARE_REGISTER_INFOS_I386_STRUCT
+
+RegisterContextFreeBSD_i386::RegisterContextFreeBSD_i386(
+ const ArchSpec &target_arch)
+ : RegisterInfoInterface(target_arch) {}
+
+size_t RegisterContextFreeBSD_i386::GetGPRSize() const { return sizeof(GPR); }
+
+const RegisterInfo *RegisterContextFreeBSD_i386::GetRegisterInfo() const {
+ switch (m_target_arch.GetMachine()) {
+ case llvm::Triple::x86:
+ return g_register_infos_i386;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+uint32_t RegisterContextFreeBSD_i386::GetRegisterCount() const {
+ return static_cast<uint32_t>(sizeof(g_register_infos_i386) /
+ sizeof(g_register_infos_i386[0]));
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.h
new file mode 100644
index 000000000000..7aadf3a0a4c9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.h
@@ -0,0 +1,25 @@
+//===-- RegisterContextFreeBSD_i386.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 liblldb_RegisterContextFreeBSD_i386_H_
+#define liblldb_RegisterContextFreeBSD_i386_H_
+
+#include "RegisterInfoInterface.h"
+
+class RegisterContextFreeBSD_i386 : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterContextFreeBSD_i386(const lldb_private::ArchSpec &target_arch);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.cpp
new file mode 100644
index 000000000000..4331ef5ad14e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.cpp
@@ -0,0 +1,117 @@
+//===-- RegisterContextFreeBSD_mips64.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 "RegisterContextFreeBSD_mips64.h"
+#include "RegisterContextPOSIX_mips64.h"
+#include "lldb-mips-freebsd-register-enums.h"
+#include <vector>
+
+using namespace lldb_private;
+using namespace lldb;
+
+static const uint32_t g_gpr_regnums[] = {
+ gpr_zero_mips64, gpr_r1_mips64, gpr_r2_mips64, gpr_r3_mips64,
+ gpr_r4_mips64, gpr_r5_mips64, gpr_r6_mips64, gpr_r7_mips64,
+ gpr_r8_mips64, gpr_r9_mips64, gpr_r10_mips64, gpr_r11_mips64,
+ gpr_r12_mips64, gpr_r13_mips64, gpr_r14_mips64, gpr_r15_mips64,
+ gpr_r16_mips64, gpr_r17_mips64, gpr_r18_mips64, gpr_r19_mips64,
+ gpr_r20_mips64, gpr_r21_mips64, gpr_r22_mips64, gpr_r23_mips64,
+ gpr_r24_mips64, gpr_r25_mips64, gpr_r26_mips64, gpr_r27_mips64,
+ gpr_gp_mips64, gpr_sp_mips64, gpr_r30_mips64, gpr_ra_mips64,
+ gpr_sr_mips64, gpr_mullo_mips64, gpr_mulhi_mips64, gpr_badvaddr_mips64,
+ gpr_cause_mips64, gpr_pc_mips64, gpr_ic_mips64, gpr_dummy_mips64};
+
+// Number of register sets provided by this context.
+constexpr size_t k_num_register_sets = 1;
+
+static const RegisterSet g_reg_sets_mips64[k_num_register_sets] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_registers_mips64,
+ g_gpr_regnums},
+};
+
+
+// http://svnweb.freebsd.org/base/head/sys/mips/include/regnum.h
+typedef struct _GPR {
+ uint64_t zero;
+ uint64_t r1;
+ uint64_t r2;
+ uint64_t r3;
+ uint64_t r4;
+ uint64_t r5;
+ uint64_t r6;
+ uint64_t r7;
+ 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 r16;
+ uint64_t r17;
+ uint64_t r18;
+ uint64_t r19;
+ uint64_t r20;
+ uint64_t r21;
+ uint64_t r22;
+ uint64_t r23;
+ uint64_t r24;
+ uint64_t r25;
+ uint64_t r26;
+ uint64_t r27;
+ uint64_t gp;
+ uint64_t sp;
+ uint64_t r30;
+ uint64_t ra;
+ uint64_t sr;
+ uint64_t mullo;
+ uint64_t mulhi;
+ uint64_t badvaddr;
+ uint64_t cause;
+ uint64_t pc;
+ uint64_t ic;
+ uint64_t dummy;
+} GPR_freebsd_mips;
+
+// Include RegisterInfos_mips64 to declare our g_register_infos_mips64
+// structure.
+#define DECLARE_REGISTER_INFOS_MIPS64_STRUCT
+#include "RegisterInfos_mips64.h"
+#undef DECLARE_REGISTER_INFOS_MIPS64_STRUCT
+
+RegisterContextFreeBSD_mips64::RegisterContextFreeBSD_mips64(
+ const ArchSpec &target_arch)
+ : RegisterInfoInterface(target_arch) {}
+
+size_t RegisterContextFreeBSD_mips64::GetGPRSize() const {
+ return sizeof(GPR_freebsd_mips);
+}
+
+const RegisterSet *
+RegisterContextFreeBSD_mips64::GetRegisterSet(size_t set) const {
+ // Check if RegisterSet is available
+ if (set < k_num_register_sets)
+ return &g_reg_sets_mips64[set];
+ return nullptr;
+}
+
+size_t
+RegisterContextFreeBSD_mips64::GetRegisterSetCount() const {
+ return k_num_register_sets;
+}
+
+const RegisterInfo *RegisterContextFreeBSD_mips64::GetRegisterInfo() const {
+ assert(m_target_arch.GetCore() == ArchSpec::eCore_mips64);
+ return g_register_infos_mips64;
+}
+
+uint32_t RegisterContextFreeBSD_mips64::GetRegisterCount() const {
+ return static_cast<uint32_t>(sizeof(g_register_infos_mips64) /
+ sizeof(g_register_infos_mips64[0]));
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h
new file mode 100644
index 000000000000..96f02b4440c5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h
@@ -0,0 +1,30 @@
+//===-- RegisterContextFreeBSD_mips64.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 liblldb_RegisterContextFreeBSD_mips64_H_
+#define liblldb_RegisterContextFreeBSD_mips64_H_
+
+#include "RegisterInfoInterface.h"
+
+class RegisterContextFreeBSD_mips64
+ : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterContextFreeBSD_mips64(const lldb_private::ArchSpec &target_arch);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) const;
+
+ size_t GetRegisterSetCount() const;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.cpp
new file mode 100644
index 000000000000..4f869eb3b177
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.cpp
@@ -0,0 +1,236 @@
+//===-- RegisterContextFreeBSD_powerpc.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 "RegisterContextFreeBSD_powerpc.h"
+#include "RegisterContextPOSIX_powerpc.h"
+#include <vector>
+
+using namespace lldb_private;
+using namespace lldb;
+
+// http://svnweb.freebsd.org/base/head/sys/powerpc/include/reg.h
+typedef struct _GPR64 {
+ uint64_t r0;
+ uint64_t r1;
+ uint64_t r2;
+ uint64_t r3;
+ uint64_t r4;
+ uint64_t r5;
+ uint64_t r6;
+ uint64_t r7;
+ 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 r16;
+ uint64_t r17;
+ uint64_t r18;
+ uint64_t r19;
+ uint64_t r20;
+ uint64_t r21;
+ uint64_t r22;
+ uint64_t r23;
+ uint64_t r24;
+ uint64_t r25;
+ uint64_t r26;
+ uint64_t r27;
+ uint64_t r28;
+ uint64_t r29;
+ uint64_t r30;
+ uint64_t r31;
+ uint64_t lr;
+ uint64_t cr;
+ uint64_t xer;
+ uint64_t ctr;
+ uint64_t pc;
+} GPR64;
+
+typedef struct _GPR32 {
+ uint32_t r0;
+ uint32_t r1;
+ uint32_t r2;
+ uint32_t r3;
+ uint32_t r4;
+ uint32_t r5;
+ uint32_t r6;
+ uint32_t r7;
+ uint32_t r8;
+ uint32_t r9;
+ uint32_t r10;
+ uint32_t r11;
+ uint32_t r12;
+ uint32_t r13;
+ uint32_t r14;
+ uint32_t r15;
+ uint32_t r16;
+ uint32_t r17;
+ uint32_t r18;
+ uint32_t r19;
+ uint32_t r20;
+ uint32_t r21;
+ uint32_t r22;
+ uint32_t r23;
+ uint32_t r24;
+ uint32_t r25;
+ uint32_t r26;
+ uint32_t r27;
+ uint32_t r28;
+ uint32_t r29;
+ uint32_t r30;
+ uint32_t r31;
+ uint32_t lr;
+ uint32_t cr;
+ uint32_t xer;
+ uint32_t ctr;
+ uint32_t pc;
+} GPR32;
+
+typedef struct _FPR {
+ uint64_t f0;
+ uint64_t f1;
+ uint64_t f2;
+ uint64_t f3;
+ uint64_t f4;
+ uint64_t f5;
+ uint64_t f6;
+ uint64_t f7;
+ uint64_t f8;
+ uint64_t f9;
+ uint64_t f10;
+ uint64_t f11;
+ uint64_t f12;
+ uint64_t f13;
+ uint64_t f14;
+ uint64_t f15;
+ uint64_t f16;
+ uint64_t f17;
+ uint64_t f18;
+ uint64_t f19;
+ uint64_t f20;
+ uint64_t f21;
+ uint64_t f22;
+ uint64_t f23;
+ uint64_t f24;
+ uint64_t f25;
+ uint64_t f26;
+ uint64_t f27;
+ uint64_t f28;
+ uint64_t f29;
+ uint64_t f30;
+ uint64_t f31;
+ uint64_t fpscr;
+} FPR;
+
+typedef struct _VMX {
+ uint32_t v0[4];
+ uint32_t v1[4];
+ uint32_t v2[4];
+ uint32_t v3[4];
+ uint32_t v4[4];
+ uint32_t v5[4];
+ uint32_t v6[4];
+ uint32_t v7[4];
+ uint32_t v8[4];
+ uint32_t v9[4];
+ uint32_t v10[4];
+ uint32_t v11[4];
+ uint32_t v12[4];
+ uint32_t v13[4];
+ uint32_t v14[4];
+ uint32_t v15[4];
+ uint32_t v16[4];
+ uint32_t v17[4];
+ uint32_t v18[4];
+ uint32_t v19[4];
+ uint32_t v20[4];
+ uint32_t v21[4];
+ uint32_t v22[4];
+ uint32_t v23[4];
+ uint32_t v24[4];
+ uint32_t v25[4];
+ uint32_t v26[4];
+ uint32_t v27[4];
+ uint32_t v28[4];
+ uint32_t v29[4];
+ uint32_t v30[4];
+ uint32_t v31[4];
+ uint32_t pad[2];
+ uint32_t vrsave;
+ uint32_t vscr;
+} VMX;
+
+// Include RegisterInfos_powerpc to declare our g_register_infos_powerpc
+// structure.
+#define DECLARE_REGISTER_INFOS_POWERPC_STRUCT
+#include "RegisterInfos_powerpc.h"
+#undef DECLARE_REGISTER_INFOS_POWERPC_STRUCT
+
+RegisterContextFreeBSD_powerpc::RegisterContextFreeBSD_powerpc(
+ const ArchSpec &target_arch)
+ : RegisterInfoInterface(target_arch) {}
+
+RegisterContextFreeBSD_powerpc::~RegisterContextFreeBSD_powerpc() {}
+
+size_t RegisterContextFreeBSD_powerpc::GetGPRSize() const {
+ // This is an 'abstract' base, so no GPR struct.
+ return 0;
+}
+
+const RegisterInfo *RegisterContextFreeBSD_powerpc::GetRegisterInfo() const {
+ // assert (m_target_arch.GetCore() == ArchSpec::eCore_powerpc);
+ llvm_unreachable("Abstract class!");
+ return nullptr;
+}
+
+uint32_t RegisterContextFreeBSD_powerpc::GetRegisterCount() const { return 0; }
+
+RegisterContextFreeBSD_powerpc32::RegisterContextFreeBSD_powerpc32(
+ const ArchSpec &target_arch)
+ : RegisterContextFreeBSD_powerpc(target_arch) {}
+
+RegisterContextFreeBSD_powerpc32::~RegisterContextFreeBSD_powerpc32() {}
+
+size_t RegisterContextFreeBSD_powerpc32::GetGPRSize() const {
+ return sizeof(GPR32);
+}
+
+const RegisterInfo *RegisterContextFreeBSD_powerpc32::GetRegisterInfo() const {
+ // assert (m_target_arch.GetCore() == ArchSpec::eCore_powerpc);
+ return g_register_infos_powerpc32;
+}
+
+uint32_t RegisterContextFreeBSD_powerpc32::GetRegisterCount() const {
+ return static_cast<uint32_t>(sizeof(g_register_infos_powerpc32) /
+ sizeof(g_register_infos_powerpc32[0]));
+}
+
+RegisterContextFreeBSD_powerpc64::RegisterContextFreeBSD_powerpc64(
+ const ArchSpec &target_arch)
+ : RegisterContextFreeBSD_powerpc(target_arch) {}
+
+RegisterContextFreeBSD_powerpc64::~RegisterContextFreeBSD_powerpc64() {}
+
+size_t RegisterContextFreeBSD_powerpc64::GetGPRSize() const {
+ return sizeof(GPR64);
+}
+
+const RegisterInfo *RegisterContextFreeBSD_powerpc64::GetRegisterInfo() const {
+ // assert (m_target_arch.GetCore() == ArchSpec::eCore_powerpc);
+ if (m_target_arch.GetMachine() == llvm::Triple::ppc)
+ return g_register_infos_powerpc64_32;
+ return g_register_infos_powerpc64;
+}
+
+uint32_t RegisterContextFreeBSD_powerpc64::GetRegisterCount() const {
+ return static_cast<uint32_t>(sizeof(g_register_infos_powerpc64) /
+ sizeof(g_register_infos_powerpc64[0]));
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h
new file mode 100644
index 000000000000..ba2751194d16
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h
@@ -0,0 +1,52 @@
+//===-- RegisterContextFreeBSD_powerpc.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 liblldb_RegisterContextFreeBSD_powerpc_h_
+#define liblldb_RegisterContextFreeBSD_powerpc_h_
+
+#include "RegisterInfoInterface.h"
+
+class RegisterContextFreeBSD_powerpc
+ : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterContextFreeBSD_powerpc(const lldb_private::ArchSpec &target_arch);
+ ~RegisterContextFreeBSD_powerpc() override;
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+};
+
+class RegisterContextFreeBSD_powerpc32 : public RegisterContextFreeBSD_powerpc {
+public:
+ RegisterContextFreeBSD_powerpc32(const lldb_private::ArchSpec &target_arch);
+ ~RegisterContextFreeBSD_powerpc32() override;
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+};
+
+class RegisterContextFreeBSD_powerpc64 : public RegisterContextFreeBSD_powerpc {
+public:
+ RegisterContextFreeBSD_powerpc64(const lldb_private::ArchSpec &target_arch);
+ ~RegisterContextFreeBSD_powerpc64() override;
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+};
+
+#endif // liblldb_RegisterContextFreeBSD_powerpc_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.cpp
new file mode 100644
index 000000000000..bcf3951ee077
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.cpp
@@ -0,0 +1,143 @@
+//===-- RegisterContextFreeBSD_x86_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
+//
+//===---------------------------------------------------------------------===//
+
+#include "RegisterContextFreeBSD_x86_64.h"
+#include "RegisterContextFreeBSD_i386.h"
+#include "RegisterContextPOSIX_x86.h"
+#include <vector>
+
+using namespace lldb_private;
+using namespace lldb;
+
+// http://svnweb.freebsd.org/base/head/sys/x86/include/reg.h
+typedef struct _GPR {
+ uint64_t r15;
+ uint64_t r14;
+ uint64_t r13;
+ uint64_t r12;
+ uint64_t r11;
+ uint64_t r10;
+ uint64_t r9;
+ uint64_t r8;
+ uint64_t rdi;
+ uint64_t rsi;
+ uint64_t rbp;
+ uint64_t rbx;
+ uint64_t rdx;
+ uint64_t rcx;
+ uint64_t rax;
+ uint32_t trapno;
+ uint16_t fs;
+ uint16_t gs;
+ uint32_t err;
+ uint16_t es;
+ uint16_t ds;
+ uint64_t rip;
+ uint64_t cs;
+ uint64_t rflags;
+ uint64_t rsp;
+ uint64_t ss;
+} GPR;
+
+struct DBG {
+ uint64_t dr[16]; /* debug registers */
+ /* Index 0-3: debug address registers */
+ /* Index 4-5: reserved */
+ /* Index 6: debug status */
+ /* Index 7: debug control */
+ /* Index 8-15: reserved */
+};
+
+struct UserArea {
+ GPR gpr;
+ FPR fpr;
+ DBG dbg;
+};
+
+#define DR_OFFSET(reg_index) (LLVM_EXTENSION offsetof(DBG, dr[reg_index]))
+
+// Include RegisterInfos_x86_64 to declare our g_register_infos_x86_64
+// structure.
+#define DECLARE_REGISTER_INFOS_X86_64_STRUCT
+#include "RegisterInfos_x86_64.h"
+#undef DECLARE_REGISTER_INFOS_X86_64_STRUCT
+
+static std::vector<lldb_private::RegisterInfo> &GetSharedRegisterInfoVector() {
+ static std::vector<lldb_private::RegisterInfo> register_infos;
+ return register_infos;
+}
+
+static const RegisterInfo *
+GetRegisterInfo_i386(const lldb_private::ArchSpec &arch) {
+ static std::vector<lldb_private::RegisterInfo> g_register_infos(
+ GetSharedRegisterInfoVector());
+
+ // Allocate RegisterInfo only once
+ if (g_register_infos.empty()) {
+ // Copy the register information from base class
+ std::unique_ptr<RegisterContextFreeBSD_i386> reg_interface(
+ new RegisterContextFreeBSD_i386(arch));
+ const RegisterInfo *base_info = reg_interface->GetRegisterInfo();
+ g_register_infos.insert(g_register_infos.end(), &base_info[0],
+ &base_info[k_num_registers_i386]);
+
+// Include RegisterInfos_x86_64 to update the g_register_infos structure
+// with x86_64 offsets.
+#define UPDATE_REGISTER_INFOS_I386_STRUCT_WITH_X86_64_OFFSETS
+#include "RegisterInfos_x86_64.h"
+#undef UPDATE_REGISTER_INFOS_I386_STRUCT_WITH_X86_64_OFFSETS
+ }
+
+ return &g_register_infos[0];
+}
+
+static const RegisterInfo *
+PrivateGetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::x86:
+ return GetRegisterInfo_i386(target_arch);
+ case llvm::Triple::x86_64:
+ return g_register_infos_x86_64;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+static uint32_t
+PrivateGetRegisterCount(const lldb_private::ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::x86:
+ // This vector should have already been filled.
+ assert(!GetSharedRegisterInfoVector().empty() &&
+ "i386 register info vector not filled.");
+ return static_cast<uint32_t>(GetSharedRegisterInfoVector().size());
+ case llvm::Triple::x86_64:
+ return static_cast<uint32_t>(sizeof(g_register_infos_x86_64) /
+ sizeof(g_register_infos_x86_64[0]));
+ default:
+ assert(false && "Unhandled target architecture.");
+ return 0;
+ }
+}
+
+RegisterContextFreeBSD_x86_64::RegisterContextFreeBSD_x86_64(
+ const ArchSpec &target_arch)
+ : lldb_private::RegisterInfoInterface(target_arch),
+ m_register_info_p(PrivateGetRegisterInfoPtr(target_arch)),
+ m_register_count(PrivateGetRegisterCount(target_arch)) {}
+
+size_t RegisterContextFreeBSD_x86_64::GetGPRSize() const { return sizeof(GPR); }
+
+const RegisterInfo *RegisterContextFreeBSD_x86_64::GetRegisterInfo() const {
+ return m_register_info_p;
+}
+
+uint32_t RegisterContextFreeBSD_x86_64::GetRegisterCount() const {
+ return m_register_count;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h
new file mode 100644
index 000000000000..c379e1a5cd75
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h
@@ -0,0 +1,30 @@
+//===-- RegisterContextFreeBSD_x86_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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextFreeBSD_x86_64_H_
+#define liblldb_RegisterContextFreeBSD_x86_64_H_
+
+#include "RegisterInfoInterface.h"
+
+class RegisterContextFreeBSD_x86_64
+ : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterContextFreeBSD_x86_64(const lldb_private::ArchSpec &target_arch);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+
+private:
+ const lldb_private::RegisterInfo *m_register_info_p;
+ const uint32_t m_register_count;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextHistory.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextHistory.cpp
new file mode 100644
index 000000000000..c19a2bfae668
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextHistory.cpp
@@ -0,0 +1,122 @@
+//===-- RegisterContextHistory.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 "lldb/Core/Address.h"
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Symbol/FuncUnwinders.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/lldb-private.h"
+
+#include "RegisterContextHistory.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+RegisterContextHistory::RegisterContextHistory(Thread &thread,
+ uint32_t concrete_frame_idx,
+ uint32_t address_byte_size,
+ addr_t pc_value)
+ : RegisterContext(thread, concrete_frame_idx), m_pc_value(pc_value) {
+ m_reg_set0.name = "General Purpose Registers";
+ m_reg_set0.short_name = "GPR";
+ m_reg_set0.num_registers = 1;
+ m_reg_set0.registers = new uint32_t(0);
+
+ m_pc_reg_info.name = "pc";
+ m_pc_reg_info.alt_name = "pc";
+ m_pc_reg_info.byte_offset = 0;
+ m_pc_reg_info.byte_size = address_byte_size;
+ m_pc_reg_info.encoding = eEncodingUint;
+ m_pc_reg_info.format = eFormatPointer;
+ m_pc_reg_info.invalidate_regs = nullptr;
+ m_pc_reg_info.value_regs = nullptr;
+ m_pc_reg_info.kinds[eRegisterKindEHFrame] = LLDB_INVALID_REGNUM;
+ m_pc_reg_info.kinds[eRegisterKindDWARF] = LLDB_INVALID_REGNUM;
+ m_pc_reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC;
+ m_pc_reg_info.kinds[eRegisterKindProcessPlugin] = LLDB_INVALID_REGNUM;
+ m_pc_reg_info.kinds[eRegisterKindLLDB] = LLDB_INVALID_REGNUM;
+}
+
+RegisterContextHistory::~RegisterContextHistory() {
+ delete m_reg_set0.registers;
+ delete m_pc_reg_info.invalidate_regs;
+ delete m_pc_reg_info.value_regs;
+}
+
+void RegisterContextHistory::InvalidateAllRegisters() {}
+
+size_t RegisterContextHistory::GetRegisterCount() { return 1; }
+
+const lldb_private::RegisterInfo *
+RegisterContextHistory::GetRegisterInfoAtIndex(size_t reg) {
+ if (reg)
+ return nullptr;
+ return &m_pc_reg_info;
+}
+
+size_t RegisterContextHistory::GetRegisterSetCount() { return 1; }
+
+const lldb_private::RegisterSet *
+RegisterContextHistory::GetRegisterSet(size_t reg_set) {
+ if (reg_set)
+ return nullptr;
+ return &m_reg_set0;
+}
+
+bool RegisterContextHistory::ReadRegister(
+ const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) {
+ if (!reg_info)
+ return false;
+ uint32_t reg_number = reg_info->kinds[eRegisterKindGeneric];
+ if (reg_number == LLDB_REGNUM_GENERIC_PC) {
+ value.SetUInt(m_pc_value, reg_info->byte_size);
+ return true;
+ }
+ return false;
+}
+
+bool RegisterContextHistory::WriteRegister(
+ const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) {
+ return false;
+}
+
+bool RegisterContextHistory::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextHistory::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+uint32_t RegisterContextHistory::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ if (kind == eRegisterKindGeneric && num == LLDB_REGNUM_GENERIC_PC)
+ return 0;
+ return LLDB_INVALID_REGNUM;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextHistory.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextHistory.h
new file mode 100644
index 000000000000..952e4263d955
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextHistory.h
@@ -0,0 +1,65 @@
+//===-- RegisterContextHistory.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_RegisterContextHistory_h_
+#define lldb_RegisterContextHistory_h_
+
+#include <vector>
+
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class RegisterContextHistory : public lldb_private::RegisterContext {
+public:
+ typedef std::shared_ptr<RegisterContextHistory> SharedPtr;
+
+ RegisterContextHistory(Thread &thread, uint32_t concrete_frame_idx,
+ uint32_t address_byte_size, lldb::addr_t pc_value);
+
+ ~RegisterContextHistory() override;
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t reg_set) override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+private:
+ // For RegisterContextLLDB only
+
+ lldb_private::RegisterSet m_reg_set0; // register set 0 (PC only)
+ lldb_private::RegisterInfo m_pc_reg_info;
+
+ lldb::addr_t m_pc_value;
+
+ DISALLOW_COPY_AND_ASSIGN(RegisterContextHistory);
+};
+} // namespace lldb_private
+
+#endif // lldb_RegisterContextHistory_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp
new file mode 100644
index 000000000000..76646d8897d1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp
@@ -0,0 +1,2105 @@
+//===-- RegisterContextLLDB.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 "lldb/Core/Address.h"
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Symbol/ArmUnwindInfo.h"
+#include "lldb/Symbol/DWARFCallFrameInfo.h"
+#include "lldb/Symbol/FuncUnwinders.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/lldb-private.h"
+
+#include "RegisterContextLLDB.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+static ConstString GetSymbolOrFunctionName(const SymbolContext &sym_ctx) {
+ if (sym_ctx.symbol)
+ return sym_ctx.symbol->GetName();
+ else if (sym_ctx.function)
+ return sym_ctx.function->GetName();
+ return ConstString();
+}
+
+RegisterContextLLDB::RegisterContextLLDB(Thread &thread,
+ const SharedPtr &next_frame,
+ SymbolContext &sym_ctx,
+ uint32_t frame_number,
+ UnwindLLDB &unwind_lldb)
+ : RegisterContext(thread, frame_number), m_thread(thread),
+ m_fast_unwind_plan_sp(), m_full_unwind_plan_sp(),
+ m_fallback_unwind_plan_sp(), m_all_registers_available(false),
+ m_frame_type(-1), m_cfa(LLDB_INVALID_ADDRESS),
+ m_afa(LLDB_INVALID_ADDRESS), m_start_pc(),
+ m_current_pc(), m_current_offset(0), m_current_offset_backed_up_one(0),
+ m_sym_ctx(sym_ctx), m_sym_ctx_valid(false), m_frame_number(frame_number),
+ m_registers(), m_parent_unwind(unwind_lldb) {
+ m_sym_ctx.Clear(false);
+ m_sym_ctx_valid = false;
+
+ if (IsFrameZero()) {
+ InitializeZerothFrame();
+ } else {
+ InitializeNonZerothFrame();
+ }
+
+ // This same code exists over in the GetFullUnwindPlanForFrame() but it may
+ // not have been executed yet
+ if (IsFrameZero() || next_frame->m_frame_type == eTrapHandlerFrame ||
+ next_frame->m_frame_type == eDebuggerFrame) {
+ m_all_registers_available = true;
+ }
+}
+
+bool RegisterContextLLDB::IsUnwindPlanValidForCurrentPC(
+ lldb::UnwindPlanSP unwind_plan_sp, int &valid_pc_offset) {
+ if (!unwind_plan_sp)
+ return false;
+
+ // check if m_current_pc is valid
+ if (unwind_plan_sp->PlanValidAtAddress(m_current_pc)) {
+ // yes - current offset can be used as is
+ valid_pc_offset = m_current_offset;
+ return true;
+ }
+
+ // if m_current_offset <= 0, we've got nothing else to try
+ if (m_current_offset <= 0)
+ return false;
+
+ // check pc - 1 to see if it's valid
+ Address pc_minus_one(m_current_pc);
+ pc_minus_one.SetOffset(m_current_pc.GetOffset() - 1);
+ if (unwind_plan_sp->PlanValidAtAddress(pc_minus_one)) {
+ // *valid_pc_offset = m_current_offset - 1;
+ valid_pc_offset = m_current_pc.GetOffset() - 1;
+ return true;
+ }
+
+ return false;
+}
+
+// Initialize a RegisterContextLLDB which is the first frame of a stack -- the
+// zeroth frame or currently executing frame.
+
+void RegisterContextLLDB::InitializeZerothFrame() {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ ExecutionContext exe_ctx(m_thread.shared_from_this());
+ RegisterContextSP reg_ctx_sp = m_thread.GetRegisterContext();
+
+ if (reg_ctx_sp.get() == nullptr) {
+ m_frame_type = eNotAValidFrame;
+ UnwindLogMsg("frame does not have a register context");
+ return;
+ }
+
+ addr_t current_pc = reg_ctx_sp->GetPC();
+
+ if (current_pc == LLDB_INVALID_ADDRESS) {
+ m_frame_type = eNotAValidFrame;
+ UnwindLogMsg("frame does not have a pc");
+ return;
+ }
+
+ Process *process = exe_ctx.GetProcessPtr();
+
+ // Let ABIs fixup code addresses to make sure they are valid. In ARM ABIs
+ // this will strip bit zero in case we read a PC from memory or from the LR.
+ // (which would be a no-op in frame 0 where we get it from the register set,
+ // but still a good idea to make the call here for other ABIs that may
+ // exist.)
+ ABI *abi = process->GetABI().get();
+ if (abi)
+ current_pc = abi->FixCodeAddress(current_pc);
+
+ // Initialize m_current_pc, an Address object, based on current_pc, an
+ // addr_t.
+ m_current_pc.SetLoadAddress(current_pc, &process->GetTarget());
+
+ // If we don't have a Module for some reason, we're not going to find
+ // symbol/function information - just stick in some reasonable defaults and
+ // hope we can unwind past this frame.
+ ModuleSP pc_module_sp(m_current_pc.GetModule());
+ if (!m_current_pc.IsValid() || !pc_module_sp) {
+ UnwindLogMsg("using architectural default unwind method");
+ }
+
+ // We require either a symbol or function in the symbols context to be
+ // successfully filled in or this context is of no use to us.
+ const SymbolContextItem resolve_scope =
+ eSymbolContextFunction | eSymbolContextSymbol;
+ if (pc_module_sp.get() && (pc_module_sp->ResolveSymbolContextForAddress(
+ m_current_pc, resolve_scope, m_sym_ctx) &
+ resolve_scope)) {
+ m_sym_ctx_valid = true;
+ }
+
+ if (m_sym_ctx.symbol) {
+ UnwindLogMsg("with pc value of 0x%" PRIx64 ", symbol name is '%s'",
+ current_pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
+ } else if (m_sym_ctx.function) {
+ UnwindLogMsg("with pc value of 0x%" PRIx64 ", function name is '%s'",
+ current_pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
+ } else {
+ UnwindLogMsg("with pc value of 0x%" PRIx64
+ ", no symbol/function name is known.",
+ current_pc);
+ }
+
+ AddressRange addr_range;
+ m_sym_ctx.GetAddressRange(resolve_scope, 0, false, addr_range);
+
+ if (IsTrapHandlerSymbol(process, m_sym_ctx)) {
+ m_frame_type = eTrapHandlerFrame;
+ } else {
+ // FIXME: Detect eDebuggerFrame here.
+ m_frame_type = eNormalFrame;
+ }
+
+ // If we were able to find a symbol/function, set addr_range to the bounds of
+ // that symbol/function. else treat the current pc value as the start_pc and
+ // record no offset.
+ if (addr_range.GetBaseAddress().IsValid()) {
+ m_start_pc = addr_range.GetBaseAddress();
+ if (m_current_pc.GetSection() == m_start_pc.GetSection()) {
+ m_current_offset = m_current_pc.GetOffset() - m_start_pc.GetOffset();
+ } else if (m_current_pc.GetModule() == m_start_pc.GetModule()) {
+ // This means that whatever symbol we kicked up isn't really correct ---
+ // we should not cross section boundaries ... We really should NULL out
+ // the function/symbol in this case unless there is a bad assumption here
+ // due to inlined functions?
+ m_current_offset =
+ m_current_pc.GetFileAddress() - m_start_pc.GetFileAddress();
+ }
+ m_current_offset_backed_up_one = m_current_offset;
+ } else {
+ m_start_pc = m_current_pc;
+ m_current_offset = -1;
+ m_current_offset_backed_up_one = -1;
+ }
+
+ // We've set m_frame_type and m_sym_ctx before these calls.
+
+ m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame();
+ m_full_unwind_plan_sp = GetFullUnwindPlanForFrame();
+
+ UnwindPlan::RowSP active_row;
+ lldb::RegisterKind row_register_kind = eRegisterKindGeneric;
+ if (m_full_unwind_plan_sp &&
+ m_full_unwind_plan_sp->PlanValidAtAddress(m_current_pc)) {
+ active_row =
+ m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset);
+ row_register_kind = m_full_unwind_plan_sp->GetRegisterKind();
+ if (active_row.get() && log) {
+ StreamString active_row_strm;
+ active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread,
+ m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr()));
+ UnwindLogMsg("%s", active_row_strm.GetData());
+ }
+ }
+
+ if (!active_row.get()) {
+ UnwindLogMsg("could not find an unwindplan row for this frame's pc");
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+
+ if (!ReadFrameAddress(row_register_kind, active_row->GetCFAValue(), m_cfa)) {
+ // Try the fall back unwind plan since the
+ // full unwind plan failed.
+ FuncUnwindersSP func_unwinders_sp;
+ UnwindPlanSP call_site_unwind_plan;
+ bool cfa_status = false;
+
+ if (m_sym_ctx_valid) {
+ func_unwinders_sp =
+ pc_module_sp->GetUnwindTable().GetFuncUnwindersContainingAddress(
+ m_current_pc, m_sym_ctx);
+ }
+
+ if (func_unwinders_sp.get() != nullptr)
+ call_site_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite(
+ process->GetTarget(), m_thread);
+
+ if (call_site_unwind_plan.get() != nullptr) {
+ m_fallback_unwind_plan_sp = call_site_unwind_plan;
+ if (TryFallbackUnwindPlan())
+ cfa_status = true;
+ }
+ if (!cfa_status) {
+ UnwindLogMsg("could not read CFA value for first frame.");
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+ } else
+ ReadFrameAddress(row_register_kind, active_row->GetAFAValue(), m_afa);
+
+ UnwindLogMsg("initialized frame current pc is 0x%" PRIx64 " cfa is 0x%" PRIx64
+ " afa is 0x%" PRIx64 " using %s UnwindPlan",
+ (uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()),
+ (uint64_t)m_cfa,
+ (uint64_t)m_afa,
+ m_full_unwind_plan_sp->GetSourceName().GetCString());
+}
+
+// Initialize a RegisterContextLLDB for the non-zeroth frame -- rely on the
+// RegisterContextLLDB "below" it to provide things like its current pc value.
+
+void RegisterContextLLDB::InitializeNonZerothFrame() {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ if (IsFrameZero()) {
+ m_frame_type = eNotAValidFrame;
+ UnwindLogMsg("non-zeroth frame tests positive for IsFrameZero -- that "
+ "shouldn't happen.");
+ return;
+ }
+
+ if (!GetNextFrame().get() || !GetNextFrame()->IsValid()) {
+ m_frame_type = eNotAValidFrame;
+ UnwindLogMsg("Could not get next frame, marking this frame as invalid.");
+ return;
+ }
+ if (!m_thread.GetRegisterContext()) {
+ m_frame_type = eNotAValidFrame;
+ UnwindLogMsg("Could not get register context for this thread, marking this "
+ "frame as invalid.");
+ return;
+ }
+
+ addr_t pc;
+ if (!ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc)) {
+ UnwindLogMsg("could not get pc value");
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+
+ ExecutionContext exe_ctx(m_thread.shared_from_this());
+ Process *process = exe_ctx.GetProcessPtr();
+ // Let ABIs fixup code addresses to make sure they are valid. In ARM ABIs
+ // this will strip bit zero in case we read a PC from memory or from the LR.
+ ABI *abi = process->GetABI().get();
+ if (abi)
+ pc = abi->FixCodeAddress(pc);
+
+ if (log) {
+ UnwindLogMsg("pc = 0x%" PRIx64, pc);
+ addr_t reg_val;
+ if (ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP, reg_val))
+ UnwindLogMsg("fp = 0x%" PRIx64, reg_val);
+ if (ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, reg_val))
+ UnwindLogMsg("sp = 0x%" PRIx64, reg_val);
+ }
+
+ // A pc of 0x0 means it's the end of the stack crawl unless we're above a trap
+ // handler function
+ bool above_trap_handler = false;
+ if (GetNextFrame().get() && GetNextFrame()->IsValid() &&
+ GetNextFrame()->IsTrapHandlerFrame())
+ above_trap_handler = true;
+
+ if (pc == 0 || pc == 0x1) {
+ if (!above_trap_handler) {
+ m_frame_type = eNotAValidFrame;
+ UnwindLogMsg("this frame has a pc of 0x0");
+ return;
+ }
+ }
+
+ const bool allow_section_end = true;
+ m_current_pc.SetLoadAddress(pc, &process->GetTarget(), allow_section_end);
+
+ // If we don't have a Module for some reason, we're not going to find
+ // symbol/function information - just stick in some reasonable defaults and
+ // hope we can unwind past this frame.
+ ModuleSP pc_module_sp(m_current_pc.GetModule());
+ if (!m_current_pc.IsValid() || !pc_module_sp) {
+ UnwindLogMsg("using architectural default unwind method");
+
+ // Test the pc value to see if we know it's in an unmapped/non-executable
+ // region of memory.
+ uint32_t permissions;
+ if (process->GetLoadAddressPermissions(pc, permissions) &&
+ (permissions & ePermissionsExecutable) == 0) {
+ // If this is the second frame off the stack, we may have unwound the
+ // first frame incorrectly. But using the architecture default unwind
+ // plan may get us back on track -- albeit possibly skipping a real
+ // frame. Give this frame a clearly-invalid pc and see if we can get any
+ // further.
+ if (GetNextFrame().get() && GetNextFrame()->IsValid() &&
+ GetNextFrame()->IsFrameZero()) {
+ UnwindLogMsg("had a pc of 0x%" PRIx64 " which is not in executable "
+ "memory but on frame 1 -- "
+ "allowing it once.",
+ (uint64_t)pc);
+ m_frame_type = eSkipFrame;
+ } else {
+ // anywhere other than the second frame, a non-executable pc means
+ // we're off in the weeds -- stop now.
+ m_frame_type = eNotAValidFrame;
+ UnwindLogMsg("pc is in a non-executable section of memory and this "
+ "isn't the 2nd frame in the stack walk.");
+ return;
+ }
+ }
+
+ if (abi) {
+ m_fast_unwind_plan_sp.reset();
+ m_full_unwind_plan_sp =
+ std::make_shared<UnwindPlan>(lldb::eRegisterKindGeneric);
+ abi->CreateDefaultUnwindPlan(*m_full_unwind_plan_sp);
+ if (m_frame_type != eSkipFrame) // don't override eSkipFrame
+ {
+ m_frame_type = eNormalFrame;
+ }
+ m_all_registers_available = false;
+ m_current_offset = -1;
+ m_current_offset_backed_up_one = -1;
+ RegisterKind row_register_kind = m_full_unwind_plan_sp->GetRegisterKind();
+ UnwindPlan::RowSP row = m_full_unwind_plan_sp->GetRowForFunctionOffset(0);
+ if (row.get()) {
+ if (!ReadFrameAddress(row_register_kind, row->GetCFAValue(), m_cfa)) {
+ UnwindLogMsg("failed to get cfa value");
+ if (m_frame_type != eSkipFrame) // don't override eSkipFrame
+ {
+ m_frame_type = eNotAValidFrame;
+ }
+ return;
+ }
+
+ ReadFrameAddress(row_register_kind, row->GetAFAValue(), m_afa);
+
+ // A couple of sanity checks..
+ if (m_cfa == LLDB_INVALID_ADDRESS || m_cfa == 0 || m_cfa == 1) {
+ UnwindLogMsg("could not find a valid cfa address");
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+
+ // m_cfa should point into the stack memory; if we can query memory
+ // region permissions, see if the memory is allocated & readable.
+ if (process->GetLoadAddressPermissions(m_cfa, permissions) &&
+ (permissions & ePermissionsReadable) == 0) {
+ m_frame_type = eNotAValidFrame;
+ UnwindLogMsg(
+ "the CFA points to a region of memory that is not readable");
+ return;
+ }
+ } else {
+ UnwindLogMsg("could not find a row for function offset zero");
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+
+ if (CheckIfLoopingStack()) {
+ TryFallbackUnwindPlan();
+ if (CheckIfLoopingStack()) {
+ UnwindLogMsg("same CFA address as next frame, assuming the unwind is "
+ "looping - stopping");
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+ }
+
+ UnwindLogMsg("initialized frame cfa is 0x%" PRIx64 " afa is 0x%" PRIx64,
+ (uint64_t)m_cfa, (uint64_t)m_afa);
+ return;
+ }
+ m_frame_type = eNotAValidFrame;
+ UnwindLogMsg("could not find any symbol for this pc, or a default unwind "
+ "plan, to continue unwind.");
+ return;
+ }
+
+ bool resolve_tail_call_address = false; // m_current_pc can be one past the
+ // address range of the function...
+ // If the saved pc does not point to a function/symbol because it is beyond
+ // the bounds of the correct function and there's no symbol there, we do
+ // *not* want ResolveSymbolContextForAddress to back up the pc by 1, because
+ // then we might not find the correct unwind information later. Instead, let
+ // ResolveSymbolContextForAddress fail, and handle the case via
+ // decr_pc_and_recompute_addr_range below.
+ const SymbolContextItem resolve_scope =
+ eSymbolContextFunction | eSymbolContextSymbol;
+ uint32_t resolved_scope = pc_module_sp->ResolveSymbolContextForAddress(
+ m_current_pc, resolve_scope, m_sym_ctx, resolve_tail_call_address);
+
+ // We require either a symbol or function in the symbols context to be
+ // successfully filled in or this context is of no use to us.
+ if (resolve_scope & resolved_scope) {
+ m_sym_ctx_valid = true;
+ }
+
+ if (m_sym_ctx.symbol) {
+ UnwindLogMsg("with pc value of 0x%" PRIx64 ", symbol name is '%s'", pc,
+ GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
+ } else if (m_sym_ctx.function) {
+ UnwindLogMsg("with pc value of 0x%" PRIx64 ", function name is '%s'", pc,
+ GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
+ } else {
+ UnwindLogMsg("with pc value of 0x%" PRIx64
+ ", no symbol/function name is known.",
+ pc);
+ }
+
+ AddressRange addr_range;
+ if (!m_sym_ctx.GetAddressRange(resolve_scope, 0, false, addr_range)) {
+ m_sym_ctx_valid = false;
+ }
+
+ bool decr_pc_and_recompute_addr_range = false;
+
+ // If the symbol lookup failed...
+ if (!m_sym_ctx_valid)
+ decr_pc_and_recompute_addr_range = true;
+
+ // Or if we're in the middle of the stack (and not "above" an asynchronous
+ // event like sigtramp), and our "current" pc is the start of a function...
+ if (GetNextFrame()->m_frame_type != eTrapHandlerFrame &&
+ GetNextFrame()->m_frame_type != eDebuggerFrame &&
+ (!m_sym_ctx_valid ||
+ (addr_range.GetBaseAddress().IsValid() &&
+ addr_range.GetBaseAddress().GetSection() == m_current_pc.GetSection() &&
+ addr_range.GetBaseAddress().GetOffset() == m_current_pc.GetOffset()))) {
+ decr_pc_and_recompute_addr_range = true;
+ }
+
+ // We need to back up the pc by 1 byte and re-search for the Symbol to handle
+ // the case where the "saved pc" value is pointing to the next function, e.g.
+ // if a function ends with a CALL instruction.
+ // FIXME this may need to be an architectural-dependent behavior; if so we'll
+ // need to add a member function
+ // to the ABI plugin and consult that.
+ if (decr_pc_and_recompute_addr_range) {
+ UnwindLogMsg("Backing up the pc value of 0x%" PRIx64
+ " by 1 and re-doing symbol lookup; old symbol was %s",
+ pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
+ Address temporary_pc;
+ temporary_pc.SetLoadAddress(pc - 1, &process->GetTarget());
+ m_sym_ctx.Clear(false);
+ m_sym_ctx_valid = false;
+ SymbolContextItem resolve_scope =
+ eSymbolContextFunction | eSymbolContextSymbol;
+
+ ModuleSP temporary_module_sp = temporary_pc.GetModule();
+ if (temporary_module_sp &&
+ temporary_module_sp->ResolveSymbolContextForAddress(
+ temporary_pc, resolve_scope, m_sym_ctx) &
+ resolve_scope) {
+ if (m_sym_ctx.GetAddressRange(resolve_scope, 0, false, addr_range))
+ m_sym_ctx_valid = true;
+ }
+ UnwindLogMsg("Symbol is now %s",
+ GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
+ }
+
+ // If we were able to find a symbol/function, set addr_range_ptr to the
+ // bounds of that symbol/function. else treat the current pc value as the
+ // start_pc and record no offset.
+ if (addr_range.GetBaseAddress().IsValid()) {
+ m_start_pc = addr_range.GetBaseAddress();
+ m_current_offset = pc - m_start_pc.GetLoadAddress(&process->GetTarget());
+ m_current_offset_backed_up_one = m_current_offset;
+ if (decr_pc_and_recompute_addr_range &&
+ m_current_offset_backed_up_one > 0) {
+ m_current_offset_backed_up_one--;
+ if (m_sym_ctx_valid) {
+ m_current_pc.SetLoadAddress(pc - 1, &process->GetTarget());
+ }
+ }
+ } else {
+ m_start_pc = m_current_pc;
+ m_current_offset = -1;
+ m_current_offset_backed_up_one = -1;
+ }
+
+ if (IsTrapHandlerSymbol(process, m_sym_ctx)) {
+ m_frame_type = eTrapHandlerFrame;
+ } else {
+ // FIXME: Detect eDebuggerFrame here.
+ if (m_frame_type != eSkipFrame) // don't override eSkipFrame
+ {
+ m_frame_type = eNormalFrame;
+ }
+ }
+
+ // We've set m_frame_type and m_sym_ctx before this call.
+ m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame();
+
+ UnwindPlan::RowSP active_row;
+ RegisterKind row_register_kind = eRegisterKindGeneric;
+
+ // Try to get by with just the fast UnwindPlan if possible - the full
+ // UnwindPlan may be expensive to get (e.g. if we have to parse the entire
+ // eh_frame section of an ObjectFile for the first time.)
+
+ if (m_fast_unwind_plan_sp &&
+ m_fast_unwind_plan_sp->PlanValidAtAddress(m_current_pc)) {
+ active_row =
+ m_fast_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset);
+ row_register_kind = m_fast_unwind_plan_sp->GetRegisterKind();
+ if (active_row.get() && log) {
+ StreamString active_row_strm;
+ active_row->Dump(active_row_strm, m_fast_unwind_plan_sp.get(), &m_thread,
+ m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr()));
+ UnwindLogMsg("active row: %s", active_row_strm.GetData());
+ }
+ } else {
+ m_full_unwind_plan_sp = GetFullUnwindPlanForFrame();
+ int valid_offset = -1;
+ if (IsUnwindPlanValidForCurrentPC(m_full_unwind_plan_sp, valid_offset)) {
+ active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset(valid_offset);
+ row_register_kind = m_full_unwind_plan_sp->GetRegisterKind();
+ if (active_row.get() && log) {
+ StreamString active_row_strm;
+ active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(),
+ &m_thread,
+ m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr()));
+ UnwindLogMsg("active row: %s", active_row_strm.GetData());
+ }
+ }
+ }
+
+ if (!active_row.get()) {
+ m_frame_type = eNotAValidFrame;
+ UnwindLogMsg("could not find unwind row for this pc");
+ return;
+ }
+
+ if (!ReadFrameAddress(row_register_kind, active_row->GetCFAValue(), m_cfa)) {
+ UnwindLogMsg("failed to get cfa");
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+
+ ReadFrameAddress(row_register_kind, active_row->GetAFAValue(), m_afa);
+
+ UnwindLogMsg("m_cfa = 0x%" PRIx64 " m_afa = 0x%" PRIx64, m_cfa, m_afa);
+
+ if (CheckIfLoopingStack()) {
+ TryFallbackUnwindPlan();
+ if (CheckIfLoopingStack()) {
+ UnwindLogMsg("same CFA address as next frame, assuming the unwind is "
+ "looping - stopping");
+ m_frame_type = eNotAValidFrame;
+ return;
+ }
+ }
+
+ UnwindLogMsg("initialized frame current pc is 0x%" PRIx64
+ " cfa is 0x%" PRIx64 " afa is 0x%" PRIx64,
+ (uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()),
+ (uint64_t)m_cfa,
+ (uint64_t)m_afa);
+}
+
+bool RegisterContextLLDB::CheckIfLoopingStack() {
+ // If we have a bad stack setup, we can get the same CFA value multiple times
+ // -- or even more devious, we can actually oscillate between two CFA values.
+ // Detect that here and break out to avoid a possible infinite loop in lldb
+ // trying to unwind the stack. To detect when we have the same CFA value
+ // multiple times, we compare the
+ // CFA of the current
+ // frame with the 2nd next frame because in some specail case (e.g. signal
+ // hanlders, hand written assembly without ABI compiance) we can have 2
+ // frames with the same
+ // CFA (in theory we
+ // can have arbitrary number of frames with the same CFA, but more then 2 is
+ // very very unlikely)
+
+ RegisterContextLLDB::SharedPtr next_frame = GetNextFrame();
+ if (next_frame) {
+ RegisterContextLLDB::SharedPtr next_next_frame = next_frame->GetNextFrame();
+ addr_t next_next_frame_cfa = LLDB_INVALID_ADDRESS;
+ if (next_next_frame && next_next_frame->GetCFA(next_next_frame_cfa)) {
+ if (next_next_frame_cfa == m_cfa) {
+ // We have a loop in the stack unwind
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool RegisterContextLLDB::IsFrameZero() const { return m_frame_number == 0; }
+
+// Find a fast unwind plan for this frame, if possible.
+//
+// On entry to this method,
+//
+// 1. m_frame_type should already be set to eTrapHandlerFrame/eDebuggerFrame
+// if either of those are correct,
+// 2. m_sym_ctx should already be filled in, and
+// 3. m_current_pc should have the current pc value for this frame
+// 4. m_current_offset_backed_up_one should have the current byte offset into
+// the function, maybe backed up by 1, -1 if unknown
+
+UnwindPlanSP RegisterContextLLDB::GetFastUnwindPlanForFrame() {
+ UnwindPlanSP unwind_plan_sp;
+ ModuleSP pc_module_sp(m_current_pc.GetModule());
+
+ if (!m_current_pc.IsValid() || !pc_module_sp ||
+ pc_module_sp->GetObjectFile() == nullptr)
+ return unwind_plan_sp;
+
+ if (IsFrameZero())
+ return unwind_plan_sp;
+
+ FuncUnwindersSP func_unwinders_sp(
+ pc_module_sp->GetUnwindTable().GetFuncUnwindersContainingAddress(
+ m_current_pc, m_sym_ctx));
+ if (!func_unwinders_sp)
+ return unwind_plan_sp;
+
+ // If we're in _sigtramp(), unwinding past this frame requires special
+ // knowledge.
+ if (m_frame_type == eTrapHandlerFrame || m_frame_type == eDebuggerFrame)
+ return unwind_plan_sp;
+
+ unwind_plan_sp = func_unwinders_sp->GetUnwindPlanFastUnwind(
+ *m_thread.CalculateTarget(), m_thread);
+ if (unwind_plan_sp) {
+ if (unwind_plan_sp->PlanValidAtAddress(m_current_pc)) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ if (log && log->GetVerbose()) {
+ if (m_fast_unwind_plan_sp)
+ UnwindLogMsgVerbose("frame, and has a fast UnwindPlan");
+ else
+ UnwindLogMsgVerbose("frame");
+ }
+ m_frame_type = eNormalFrame;
+ return unwind_plan_sp;
+ } else {
+ unwind_plan_sp.reset();
+ }
+ }
+ return unwind_plan_sp;
+}
+
+// On entry to this method,
+//
+// 1. m_frame_type should already be set to eTrapHandlerFrame/eDebuggerFrame
+// if either of those are correct,
+// 2. m_sym_ctx should already be filled in, and
+// 3. m_current_pc should have the current pc value for this frame
+// 4. m_current_offset_backed_up_one should have the current byte offset into
+// the function, maybe backed up by 1, -1 if unknown
+
+UnwindPlanSP RegisterContextLLDB::GetFullUnwindPlanForFrame() {
+ UnwindPlanSP unwind_plan_sp;
+ UnwindPlanSP arch_default_unwind_plan_sp;
+ ExecutionContext exe_ctx(m_thread.shared_from_this());
+ Process *process = exe_ctx.GetProcessPtr();
+ ABI *abi = process ? process->GetABI().get() : nullptr;
+ if (abi) {
+ arch_default_unwind_plan_sp =
+ std::make_shared<UnwindPlan>(lldb::eRegisterKindGeneric);
+ abi->CreateDefaultUnwindPlan(*arch_default_unwind_plan_sp);
+ } else {
+ UnwindLogMsg(
+ "unable to get architectural default UnwindPlan from ABI plugin");
+ }
+
+ bool behaves_like_zeroth_frame = false;
+ if (IsFrameZero() || GetNextFrame()->m_frame_type == eTrapHandlerFrame ||
+ GetNextFrame()->m_frame_type == eDebuggerFrame) {
+ behaves_like_zeroth_frame = true;
+ // If this frame behaves like a 0th frame (currently executing or
+ // interrupted asynchronously), all registers can be retrieved.
+ m_all_registers_available = true;
+ }
+
+ // If we've done a jmp 0x0 / bl 0x0 (called through a null function pointer)
+ // so the pc is 0x0 in the zeroth frame, we need to use the "unwind at first
+ // instruction" arch default UnwindPlan Also, if this Process can report on
+ // memory region attributes, any non-executable region means we jumped
+ // through a bad function pointer - handle the same way as 0x0. Note, if we
+ // have a symbol context & a symbol, we don't want to follow this code path.
+ // This is for jumping to memory regions without any information available.
+
+ if ((!m_sym_ctx_valid ||
+ (m_sym_ctx.function == nullptr && m_sym_ctx.symbol == nullptr)) &&
+ behaves_like_zeroth_frame && m_current_pc.IsValid()) {
+ uint32_t permissions;
+ addr_t current_pc_addr =
+ m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr());
+ if (current_pc_addr == 0 ||
+ (process &&
+ process->GetLoadAddressPermissions(current_pc_addr, permissions) &&
+ (permissions & ePermissionsExecutable) == 0)) {
+ if (abi) {
+ unwind_plan_sp =
+ std::make_shared<UnwindPlan>(lldb::eRegisterKindGeneric);
+ abi->CreateFunctionEntryUnwindPlan(*unwind_plan_sp);
+ m_frame_type = eNormalFrame;
+ return unwind_plan_sp;
+ }
+ }
+ }
+
+ // No Module for the current pc, try using the architecture default unwind.
+ ModuleSP pc_module_sp(m_current_pc.GetModule());
+ if (!m_current_pc.IsValid() || !pc_module_sp ||
+ pc_module_sp->GetObjectFile() == nullptr) {
+ m_frame_type = eNormalFrame;
+ return arch_default_unwind_plan_sp;
+ }
+
+ FuncUnwindersSP func_unwinders_sp;
+ if (m_sym_ctx_valid) {
+ func_unwinders_sp =
+ pc_module_sp->GetUnwindTable().GetFuncUnwindersContainingAddress(
+ m_current_pc, m_sym_ctx);
+ }
+
+ // No FuncUnwinders available for this pc (stripped function symbols, lldb
+ // could not augment its function table with another source, like
+ // LC_FUNCTION_STARTS or eh_frame in ObjectFileMachO). See if eh_frame or the
+ // .ARM.exidx tables have unwind information for this address, else fall back
+ // to the architectural default unwind.
+ if (!func_unwinders_sp) {
+ m_frame_type = eNormalFrame;
+
+ if (!pc_module_sp || !pc_module_sp->GetObjectFile() ||
+ !m_current_pc.IsValid())
+ return arch_default_unwind_plan_sp;
+
+ // Even with -fomit-frame-pointer, we can try eh_frame to get back on
+ // track.
+ DWARFCallFrameInfo *eh_frame =
+ pc_module_sp->GetUnwindTable().GetEHFrameInfo();
+ if (eh_frame) {
+ unwind_plan_sp = std::make_shared<UnwindPlan>(lldb::eRegisterKindGeneric);
+ if (eh_frame->GetUnwindPlan(m_current_pc, *unwind_plan_sp))
+ return unwind_plan_sp;
+ else
+ unwind_plan_sp.reset();
+ }
+
+ ArmUnwindInfo *arm_exidx =
+ pc_module_sp->GetUnwindTable().GetArmUnwindInfo();
+ if (arm_exidx) {
+ unwind_plan_sp = std::make_shared<UnwindPlan>(lldb::eRegisterKindGeneric);
+ if (arm_exidx->GetUnwindPlan(exe_ctx.GetTargetRef(), m_current_pc,
+ *unwind_plan_sp))
+ return unwind_plan_sp;
+ else
+ unwind_plan_sp.reset();
+ }
+
+ return arch_default_unwind_plan_sp;
+ }
+
+ // If we're in _sigtramp(), unwinding past this frame requires special
+ // knowledge. On Mac OS X this knowledge is properly encoded in the eh_frame
+ // section, so prefer that if available. On other platforms we may need to
+ // provide a platform-specific UnwindPlan which encodes the details of how to
+ // unwind out of sigtramp.
+ if (m_frame_type == eTrapHandlerFrame && process) {
+ m_fast_unwind_plan_sp.reset();
+ unwind_plan_sp =
+ func_unwinders_sp->GetEHFrameUnwindPlan(process->GetTarget());
+ if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress(m_current_pc) &&
+ unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolYes) {
+ return unwind_plan_sp;
+ }
+ }
+
+ // Ask the DynamicLoader if the eh_frame CFI should be trusted in this frame
+ // even when it's frame zero This comes up if we have hand-written functions
+ // in a Module and hand-written eh_frame. The assembly instruction
+ // inspection may fail and the eh_frame CFI were probably written with some
+ // care to do the right thing. It'd be nice if there was a way to ask the
+ // eh_frame directly if it is asynchronous (can be trusted at every
+ // instruction point) or synchronous (the normal case - only at call sites).
+ // But there is not.
+ if (process && process->GetDynamicLoader() &&
+ process->GetDynamicLoader()->AlwaysRelyOnEHUnwindInfo(m_sym_ctx)) {
+ // We must specifically call the GetEHFrameUnwindPlan() method here --
+ // normally we would call GetUnwindPlanAtCallSite() -- because CallSite may
+ // return an unwind plan sourced from either eh_frame (that's what we
+ // intend) or compact unwind (this won't work)
+ unwind_plan_sp =
+ func_unwinders_sp->GetEHFrameUnwindPlan(process->GetTarget());
+ if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress(m_current_pc)) {
+ UnwindLogMsgVerbose("frame uses %s for full UnwindPlan because the "
+ "DynamicLoader suggested we prefer it",
+ unwind_plan_sp->GetSourceName().GetCString());
+ return unwind_plan_sp;
+ }
+ }
+
+ // Typically the NonCallSite UnwindPlan is the unwind created by inspecting
+ // the assembly language instructions
+ if (behaves_like_zeroth_frame && process) {
+ unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite(
+ process->GetTarget(), m_thread);
+ if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress(m_current_pc)) {
+ if (unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolNo) {
+ // We probably have an UnwindPlan created by inspecting assembly
+ // instructions. The assembly profilers work really well with compiler-
+ // generated functions but hand- written assembly can be problematic.
+ // We set the eh_frame based unwind plan as our fallback unwind plan if
+ // instruction emulation doesn't work out even for non call sites if it
+ // is available and use the architecture default unwind plan if it is
+ // not available. The eh_frame unwind plan is more reliable even on non
+ // call sites then the architecture default plan and for hand written
+ // assembly code it is often written in a way that it valid at all
+ // location what helps in the most common cases when the instruction
+ // emulation fails.
+ UnwindPlanSP call_site_unwind_plan =
+ func_unwinders_sp->GetUnwindPlanAtCallSite(process->GetTarget(),
+ m_thread);
+ if (call_site_unwind_plan &&
+ call_site_unwind_plan.get() != unwind_plan_sp.get() &&
+ call_site_unwind_plan->GetSourceName() !=
+ unwind_plan_sp->GetSourceName()) {
+ m_fallback_unwind_plan_sp = call_site_unwind_plan;
+ } else {
+ m_fallback_unwind_plan_sp = arch_default_unwind_plan_sp;
+ }
+ }
+ UnwindLogMsgVerbose("frame uses %s for full UnwindPlan because this "
+ "is the non-call site unwind plan and this is a "
+ "zeroth frame",
+ unwind_plan_sp->GetSourceName().GetCString());
+ return unwind_plan_sp;
+ }
+
+ // If we're on the first instruction of a function, and we have an
+ // architectural default UnwindPlan for the initial instruction of a
+ // function, use that.
+ if (m_current_offset == 0) {
+ unwind_plan_sp =
+ func_unwinders_sp->GetUnwindPlanArchitectureDefaultAtFunctionEntry(
+ m_thread);
+ if (unwind_plan_sp) {
+ UnwindLogMsgVerbose("frame uses %s for full UnwindPlan because we are at "
+ "the first instruction of a function",
+ unwind_plan_sp->GetSourceName().GetCString());
+ return unwind_plan_sp;
+ }
+ }
+ }
+
+ // Typically this is unwind info from an eh_frame section intended for
+ // exception handling; only valid at call sites
+ if (process) {
+ unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtCallSite(
+ process->GetTarget(), m_thread);
+ }
+ int valid_offset = -1;
+ if (IsUnwindPlanValidForCurrentPC(unwind_plan_sp, valid_offset)) {
+ UnwindLogMsgVerbose("frame uses %s for full UnwindPlan because this "
+ "is the call-site unwind plan",
+ unwind_plan_sp->GetSourceName().GetCString());
+ return unwind_plan_sp;
+ }
+
+ // We'd prefer to use an UnwindPlan intended for call sites when we're at a
+ // call site but if we've struck out on that, fall back to using the non-
+ // call-site assembly inspection UnwindPlan if possible.
+ if (process) {
+ unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite(
+ process->GetTarget(), m_thread);
+ }
+ if (unwind_plan_sp &&
+ unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolNo) {
+ // We probably have an UnwindPlan created by inspecting assembly
+ // instructions. The assembly profilers work really well with compiler-
+ // generated functions but hand- written assembly can be problematic. We
+ // set the eh_frame based unwind plan as our fallback unwind plan if
+ // instruction emulation doesn't work out even for non call sites if it is
+ // available and use the architecture default unwind plan if it is not
+ // available. The eh_frame unwind plan is more reliable even on non call
+ // sites then the architecture default plan and for hand written assembly
+ // code it is often written in a way that it valid at all location what
+ // helps in the most common cases when the instruction emulation fails.
+ UnwindPlanSP call_site_unwind_plan =
+ func_unwinders_sp->GetUnwindPlanAtCallSite(process->GetTarget(),
+ m_thread);
+ if (call_site_unwind_plan &&
+ call_site_unwind_plan.get() != unwind_plan_sp.get() &&
+ call_site_unwind_plan->GetSourceName() !=
+ unwind_plan_sp->GetSourceName()) {
+ m_fallback_unwind_plan_sp = call_site_unwind_plan;
+ } else {
+ m_fallback_unwind_plan_sp = arch_default_unwind_plan_sp;
+ }
+ }
+
+ if (IsUnwindPlanValidForCurrentPC(unwind_plan_sp, valid_offset)) {
+ UnwindLogMsgVerbose("frame uses %s for full UnwindPlan because we "
+ "failed to find a call-site unwind plan that would work",
+ unwind_plan_sp->GetSourceName().GetCString());
+ return unwind_plan_sp;
+ }
+
+ // If nothing else, use the architectural default UnwindPlan and hope that
+ // does the job.
+ if (arch_default_unwind_plan_sp)
+ UnwindLogMsgVerbose(
+ "frame uses %s for full UnwindPlan because we are falling back "
+ "to the arch default plan",
+ arch_default_unwind_plan_sp->GetSourceName().GetCString());
+ else
+ UnwindLogMsg(
+ "Unable to find any UnwindPlan for full unwind of this frame.");
+
+ return arch_default_unwind_plan_sp;
+}
+
+void RegisterContextLLDB::InvalidateAllRegisters() {
+ m_frame_type = eNotAValidFrame;
+}
+
+size_t RegisterContextLLDB::GetRegisterCount() {
+ return m_thread.GetRegisterContext()->GetRegisterCount();
+}
+
+const RegisterInfo *RegisterContextLLDB::GetRegisterInfoAtIndex(size_t reg) {
+ return m_thread.GetRegisterContext()->GetRegisterInfoAtIndex(reg);
+}
+
+size_t RegisterContextLLDB::GetRegisterSetCount() {
+ return m_thread.GetRegisterContext()->GetRegisterSetCount();
+}
+
+const RegisterSet *RegisterContextLLDB::GetRegisterSet(size_t reg_set) {
+ return m_thread.GetRegisterContext()->GetRegisterSet(reg_set);
+}
+
+uint32_t RegisterContextLLDB::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ return m_thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber(
+ kind, num);
+}
+
+bool RegisterContextLLDB::ReadRegisterValueFromRegisterLocation(
+ lldb_private::UnwindLLDB::RegisterLocation regloc,
+ const RegisterInfo *reg_info, RegisterValue &value) {
+ if (!IsValid())
+ return false;
+ bool success = false;
+
+ switch (regloc.type) {
+ case UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext: {
+ const RegisterInfo *other_reg_info =
+ GetRegisterInfoAtIndex(regloc.location.register_number);
+
+ if (!other_reg_info)
+ return false;
+
+ success =
+ m_thread.GetRegisterContext()->ReadRegister(other_reg_info, value);
+ } break;
+ case UnwindLLDB::RegisterLocation::eRegisterInRegister: {
+ const RegisterInfo *other_reg_info =
+ GetRegisterInfoAtIndex(regloc.location.register_number);
+
+ if (!other_reg_info)
+ return false;
+
+ if (IsFrameZero()) {
+ success =
+ m_thread.GetRegisterContext()->ReadRegister(other_reg_info, value);
+ } else {
+ success = GetNextFrame()->ReadRegister(other_reg_info, value);
+ }
+ } break;
+ case UnwindLLDB::RegisterLocation::eRegisterValueInferred:
+ success =
+ value.SetUInt(regloc.location.inferred_value, reg_info->byte_size);
+ break;
+
+ case UnwindLLDB::RegisterLocation::eRegisterNotSaved:
+ break;
+ case UnwindLLDB::RegisterLocation::eRegisterSavedAtHostMemoryLocation:
+ llvm_unreachable("FIXME debugger inferior function call unwind");
+ case UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation: {
+ Status error(ReadRegisterValueFromMemory(
+ reg_info, regloc.location.target_memory_location, reg_info->byte_size,
+ value));
+ success = error.Success();
+ } break;
+ default:
+ llvm_unreachable("Unknown RegisterLocation type.");
+ }
+ return success;
+}
+
+bool RegisterContextLLDB::WriteRegisterValueToRegisterLocation(
+ lldb_private::UnwindLLDB::RegisterLocation regloc,
+ const RegisterInfo *reg_info, const RegisterValue &value) {
+ if (!IsValid())
+ return false;
+
+ bool success = false;
+
+ switch (regloc.type) {
+ case UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext: {
+ const RegisterInfo *other_reg_info =
+ GetRegisterInfoAtIndex(regloc.location.register_number);
+ success =
+ m_thread.GetRegisterContext()->WriteRegister(other_reg_info, value);
+ } break;
+ case UnwindLLDB::RegisterLocation::eRegisterInRegister: {
+ const RegisterInfo *other_reg_info =
+ GetRegisterInfoAtIndex(regloc.location.register_number);
+ if (IsFrameZero()) {
+ success =
+ m_thread.GetRegisterContext()->WriteRegister(other_reg_info, value);
+ } else {
+ success = GetNextFrame()->WriteRegister(other_reg_info, value);
+ }
+ } break;
+ case UnwindLLDB::RegisterLocation::eRegisterValueInferred:
+ case UnwindLLDB::RegisterLocation::eRegisterNotSaved:
+ break;
+ case UnwindLLDB::RegisterLocation::eRegisterSavedAtHostMemoryLocation:
+ llvm_unreachable("FIXME debugger inferior function call unwind");
+ case UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation: {
+ Status error(WriteRegisterValueToMemory(
+ reg_info, regloc.location.target_memory_location, reg_info->byte_size,
+ value));
+ success = error.Success();
+ } break;
+ default:
+ llvm_unreachable("Unknown RegisterLocation type.");
+ }
+ return success;
+}
+
+bool RegisterContextLLDB::IsValid() const {
+ return m_frame_type != eNotAValidFrame;
+}
+
+// After the final stack frame in a stack walk we'll get one invalid
+// (eNotAValidFrame) stack frame -- one past the end of the stack walk. But
+// higher-level code will need to tell the differnece between "the unwind plan
+// below this frame failed" versus "we successfully completed the stack walk"
+// so this method helps to disambiguate that.
+
+bool RegisterContextLLDB::IsTrapHandlerFrame() const {
+ return m_frame_type == eTrapHandlerFrame;
+}
+
+// A skip frame is a bogus frame on the stack -- but one where we're likely to
+// find a real frame farther
+// up the stack if we keep looking. It's always the second frame in an unwind
+// (i.e. the first frame after frame zero) where unwinding can be the
+// trickiest. Ideally we'll mark up this frame in some way so the user knows
+// we're displaying bad data and we may have skipped one frame of their real
+// program in the process of getting back on track.
+
+bool RegisterContextLLDB::IsSkipFrame() const {
+ return m_frame_type == eSkipFrame;
+}
+
+bool RegisterContextLLDB::IsTrapHandlerSymbol(
+ lldb_private::Process *process,
+ const lldb_private::SymbolContext &m_sym_ctx) const {
+ PlatformSP platform_sp(process->GetTarget().GetPlatform());
+ if (platform_sp) {
+ const std::vector<ConstString> trap_handler_names(
+ platform_sp->GetTrapHandlerSymbolNames());
+ for (ConstString name : trap_handler_names) {
+ if ((m_sym_ctx.function && m_sym_ctx.function->GetName() == name) ||
+ (m_sym_ctx.symbol && m_sym_ctx.symbol->GetName() == name)) {
+ return true;
+ }
+ }
+ }
+ const std::vector<ConstString> user_specified_trap_handler_names(
+ m_parent_unwind.GetUserSpecifiedTrapHandlerFunctionNames());
+ for (ConstString name : user_specified_trap_handler_names) {
+ if ((m_sym_ctx.function && m_sym_ctx.function->GetName() == name) ||
+ (m_sym_ctx.symbol && m_sym_ctx.symbol->GetName() == name)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Answer the question: Where did THIS frame save the CALLER frame ("previous"
+// frame)'s register value?
+
+enum UnwindLLDB::RegisterSearchResult
+RegisterContextLLDB::SavedLocationForRegister(
+ uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation &regloc) {
+ RegisterNumber regnum(m_thread, eRegisterKindLLDB, lldb_regnum);
+
+ // Have we already found this register location?
+ if (!m_registers.empty()) {
+ std::map<uint32_t,
+ lldb_private::UnwindLLDB::RegisterLocation>::const_iterator
+ iterator;
+ iterator = m_registers.find(regnum.GetAsKind(eRegisterKindLLDB));
+ if (iterator != m_registers.end()) {
+ regloc = iterator->second;
+ UnwindLogMsg("supplying caller's saved %s (%d)'s location, cached",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+ }
+
+ // Look through the available UnwindPlans for the register location.
+
+ UnwindPlan::Row::RegisterLocation unwindplan_regloc;
+ bool have_unwindplan_regloc = false;
+ RegisterKind unwindplan_registerkind = kNumRegisterKinds;
+
+ if (m_fast_unwind_plan_sp) {
+ UnwindPlan::RowSP active_row =
+ m_fast_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset);
+ unwindplan_registerkind = m_fast_unwind_plan_sp->GetRegisterKind();
+ if (regnum.GetAsKind(unwindplan_registerkind) == LLDB_INVALID_REGNUM) {
+ UnwindLogMsg("could not convert lldb regnum %s (%d) into %d RegisterKind "
+ "reg numbering scheme",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB),
+ (int)unwindplan_registerkind);
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ }
+ if (active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind),
+ unwindplan_regloc)) {
+ UnwindLogMsg(
+ "supplying caller's saved %s (%d)'s location using FastUnwindPlan",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ have_unwindplan_regloc = true;
+ }
+ }
+
+ if (!have_unwindplan_regloc) {
+ // m_full_unwind_plan_sp being NULL means that we haven't tried to find a
+ // full UnwindPlan yet
+ if (!m_full_unwind_plan_sp)
+ m_full_unwind_plan_sp = GetFullUnwindPlanForFrame();
+
+ if (m_full_unwind_plan_sp) {
+ RegisterNumber pc_regnum(m_thread, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC);
+
+ UnwindPlan::RowSP active_row =
+ m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset);
+ unwindplan_registerkind = m_full_unwind_plan_sp->GetRegisterKind();
+
+ RegisterNumber return_address_reg;
+
+ // If we're fetching the saved pc and this UnwindPlan defines a
+ // ReturnAddress register (e.g. lr on arm), look for the return address
+ // register number in the UnwindPlan's row.
+ if (pc_regnum.IsValid() && pc_regnum == regnum &&
+ m_full_unwind_plan_sp->GetReturnAddressRegister() !=
+ LLDB_INVALID_REGNUM) {
+
+ return_address_reg.init(
+ m_thread, m_full_unwind_plan_sp->GetRegisterKind(),
+ m_full_unwind_plan_sp->GetReturnAddressRegister());
+ regnum = return_address_reg;
+ UnwindLogMsg("requested caller's saved PC but this UnwindPlan uses a "
+ "RA reg; getting %s (%d) instead",
+ return_address_reg.GetName(),
+ return_address_reg.GetAsKind(eRegisterKindLLDB));
+ } else {
+ if (regnum.GetAsKind(unwindplan_registerkind) == LLDB_INVALID_REGNUM) {
+ if (unwindplan_registerkind == eRegisterKindGeneric) {
+ UnwindLogMsg("could not convert lldb regnum %s (%d) into "
+ "eRegisterKindGeneric reg numbering scheme",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ } else {
+ UnwindLogMsg("could not convert lldb regnum %s (%d) into %d "
+ "RegisterKind reg numbering scheme",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB),
+ (int)unwindplan_registerkind);
+ }
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ }
+ }
+
+ if (regnum.IsValid() &&
+ active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind),
+ unwindplan_regloc)) {
+ have_unwindplan_regloc = true;
+ UnwindLogMsg(
+ "supplying caller's saved %s (%d)'s location using %s UnwindPlan",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB),
+ m_full_unwind_plan_sp->GetSourceName().GetCString());
+ }
+
+ // This is frame 0 and we're retrieving the PC and it's saved in a Return
+ // Address register and it hasn't been saved anywhere yet -- that is,
+ // it's still live in the actual register. Handle this specially.
+
+ if (!have_unwindplan_regloc && return_address_reg.IsValid() &&
+ IsFrameZero()) {
+ if (return_address_reg.GetAsKind(eRegisterKindLLDB) !=
+ LLDB_INVALID_REGNUM) {
+ lldb_private::UnwindLLDB::RegisterLocation new_regloc;
+ new_regloc.type =
+ UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext;
+ new_regloc.location.register_number =
+ return_address_reg.GetAsKind(eRegisterKindLLDB);
+ m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = new_regloc;
+ regloc = new_regloc;
+ UnwindLogMsg("supplying caller's register %s (%d) from the live "
+ "RegisterContext at frame 0, saved in %d",
+ return_address_reg.GetName(),
+ return_address_reg.GetAsKind(eRegisterKindLLDB),
+ return_address_reg.GetAsKind(eRegisterKindLLDB));
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+ }
+
+ // If this architecture stores the return address in a register (it
+ // defines a Return Address register) and we're on a non-zero stack frame
+ // and the Full UnwindPlan says that the pc is stored in the
+ // RA registers (e.g. lr on arm), then we know that the full unwindplan is
+ // not trustworthy -- this
+ // is an impossible situation and the instruction emulation code has
+ // likely been misled. If this stack frame meets those criteria, we need
+ // to throw away the Full UnwindPlan that the instruction emulation came
+ // up with and fall back to the architecture's Default UnwindPlan so the
+ // stack walk can get past this point.
+
+ // Special note: If the Full UnwindPlan was generated from the compiler,
+ // don't second-guess it when we're at a call site location.
+
+ // arch_default_ra_regnum is the return address register # in the Full
+ // UnwindPlan register numbering
+ RegisterNumber arch_default_ra_regnum(m_thread, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_RA);
+
+ if (arch_default_ra_regnum.GetAsKind(unwindplan_registerkind) !=
+ LLDB_INVALID_REGNUM &&
+ pc_regnum == regnum && unwindplan_regloc.IsInOtherRegister() &&
+ unwindplan_regloc.GetRegisterNumber() ==
+ arch_default_ra_regnum.GetAsKind(unwindplan_registerkind) &&
+ m_full_unwind_plan_sp->GetSourcedFromCompiler() != eLazyBoolYes &&
+ !m_all_registers_available) {
+ UnwindLogMsg("%s UnwindPlan tried to restore the pc from the link "
+ "register but this is a non-zero frame",
+ m_full_unwind_plan_sp->GetSourceName().GetCString());
+
+ // Throw away the full unwindplan; install the arch default unwindplan
+ if (ForceSwitchToFallbackUnwindPlan()) {
+ // Update for the possibly new unwind plan
+ unwindplan_registerkind = m_full_unwind_plan_sp->GetRegisterKind();
+ UnwindPlan::RowSP active_row =
+ m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset);
+
+ // Sanity check: Verify that we can fetch a pc value and CFA value
+ // with this unwind plan
+
+ RegisterNumber arch_default_pc_reg(m_thread, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC);
+ bool can_fetch_pc_value = false;
+ bool can_fetch_cfa = false;
+ addr_t cfa_value;
+ if (active_row) {
+ if (arch_default_pc_reg.GetAsKind(unwindplan_registerkind) !=
+ LLDB_INVALID_REGNUM &&
+ active_row->GetRegisterInfo(
+ arch_default_pc_reg.GetAsKind(unwindplan_registerkind),
+ unwindplan_regloc)) {
+ can_fetch_pc_value = true;
+ }
+ if (ReadFrameAddress(unwindplan_registerkind,
+ active_row->GetCFAValue(), cfa_value)) {
+ can_fetch_cfa = true;
+ }
+ }
+
+ have_unwindplan_regloc = can_fetch_pc_value && can_fetch_cfa;
+ } else {
+ // We were unable to fall back to another unwind plan
+ have_unwindplan_regloc = false;
+ }
+ }
+ }
+ }
+
+ ExecutionContext exe_ctx(m_thread.shared_from_this());
+ Process *process = exe_ctx.GetProcessPtr();
+ if (!have_unwindplan_regloc) {
+ // If the UnwindPlan failed to give us an unwind location for this
+ // register, we may be able to fall back to some ABI-defined default. For
+ // example, some ABIs allow to determine the caller's SP via the CFA. Also,
+ // the ABI may set volatile registers to the undefined state.
+ ABI *abi = process ? process->GetABI().get() : nullptr;
+ if (abi) {
+ const RegisterInfo *reg_info =
+ GetRegisterInfoAtIndex(regnum.GetAsKind(eRegisterKindLLDB));
+ if (reg_info &&
+ abi->GetFallbackRegisterLocation(reg_info, unwindplan_regloc)) {
+ UnwindLogMsg(
+ "supplying caller's saved %s (%d)'s location using ABI default",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ have_unwindplan_regloc = true;
+ }
+ }
+ }
+
+ if (!have_unwindplan_regloc) {
+ if (IsFrameZero()) {
+ // This is frame 0 - we should return the actual live register context
+ // value
+ lldb_private::UnwindLLDB::RegisterLocation new_regloc;
+ new_regloc.type =
+ UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext;
+ new_regloc.location.register_number = regnum.GetAsKind(eRegisterKindLLDB);
+ m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = new_regloc;
+ regloc = new_regloc;
+ UnwindLogMsg("supplying caller's register %s (%d) from the live "
+ "RegisterContext at frame 0",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ } else {
+ std::string unwindplan_name("");
+ if (m_full_unwind_plan_sp) {
+ unwindplan_name += "via '";
+ unwindplan_name += m_full_unwind_plan_sp->GetSourceName().AsCString();
+ unwindplan_name += "'";
+ }
+ UnwindLogMsg("no save location for %s (%d) %s", regnum.GetName(),
+ regnum.GetAsKind(eRegisterKindLLDB),
+ unwindplan_name.c_str());
+ }
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ }
+
+ // unwindplan_regloc has valid contents about where to retrieve the register
+ if (unwindplan_regloc.IsUnspecified()) {
+ lldb_private::UnwindLLDB::RegisterLocation new_regloc;
+ new_regloc.type = UnwindLLDB::RegisterLocation::eRegisterNotSaved;
+ m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = new_regloc;
+ UnwindLogMsg("save location for %s (%d) is unspecified, continue searching",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ }
+
+ if (unwindplan_regloc.IsUndefined()) {
+ UnwindLogMsg(
+ "did not supply reg location for %s (%d) because it is volatile",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ return UnwindLLDB::RegisterSearchResult::eRegisterIsVolatile;
+ }
+
+ if (unwindplan_regloc.IsSame()) {
+ if (!IsFrameZero() &&
+ (regnum.GetAsKind(eRegisterKindGeneric) == LLDB_REGNUM_GENERIC_PC ||
+ regnum.GetAsKind(eRegisterKindGeneric) == LLDB_REGNUM_GENERIC_RA)) {
+ UnwindLogMsg("register %s (%d) is marked as 'IsSame' - it is a pc or "
+ "return address reg on a non-zero frame -- treat as if we "
+ "have no information",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ } else {
+ regloc.type = UnwindLLDB::RegisterLocation::eRegisterInRegister;
+ regloc.location.register_number = regnum.GetAsKind(eRegisterKindLLDB);
+ m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
+ UnwindLogMsg(
+ "supplying caller's register %s (%d), saved in register %s (%d)",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB),
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+ }
+
+ if (unwindplan_regloc.IsCFAPlusOffset()) {
+ int offset = unwindplan_regloc.GetOffset();
+ regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred;
+ regloc.location.inferred_value = m_cfa + offset;
+ m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
+ UnwindLogMsg("supplying caller's register %s (%d), value is CFA plus "
+ "offset %d [value is 0x%" PRIx64 "]",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), offset,
+ regloc.location.inferred_value);
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+
+ if (unwindplan_regloc.IsAtCFAPlusOffset()) {
+ int offset = unwindplan_regloc.GetOffset();
+ regloc.type = UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation;
+ regloc.location.target_memory_location = m_cfa + offset;
+ m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
+ UnwindLogMsg("supplying caller's register %s (%d) from the stack, saved at "
+ "CFA plus offset %d [saved at 0x%" PRIx64 "]",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), offset,
+ regloc.location.target_memory_location);
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+
+ if (unwindplan_regloc.IsAFAPlusOffset()) {
+ if (m_afa == LLDB_INVALID_ADDRESS)
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+
+ int offset = unwindplan_regloc.GetOffset();
+ regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred;
+ regloc.location.inferred_value = m_afa + offset;
+ m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
+ UnwindLogMsg("supplying caller's register %s (%d), value is AFA plus "
+ "offset %d [value is 0x%" PRIx64 "]",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), offset,
+ regloc.location.inferred_value);
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+
+ if (unwindplan_regloc.IsAtAFAPlusOffset()) {
+ if (m_afa == LLDB_INVALID_ADDRESS)
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+
+ int offset = unwindplan_regloc.GetOffset();
+ regloc.type = UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation;
+ regloc.location.target_memory_location = m_afa + offset;
+ m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
+ UnwindLogMsg("supplying caller's register %s (%d) from the stack, saved at "
+ "AFA plus offset %d [saved at 0x%" PRIx64 "]",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), offset,
+ regloc.location.target_memory_location);
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+
+ if (unwindplan_regloc.IsInOtherRegister()) {
+ uint32_t unwindplan_regnum = unwindplan_regloc.GetRegisterNumber();
+ RegisterNumber row_regnum(m_thread, unwindplan_registerkind,
+ unwindplan_regnum);
+ if (row_regnum.GetAsKind(eRegisterKindLLDB) == LLDB_INVALID_REGNUM) {
+ UnwindLogMsg("could not supply caller's %s (%d) location - was saved in "
+ "another reg but couldn't convert that regnum",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ }
+ regloc.type = UnwindLLDB::RegisterLocation::eRegisterInRegister;
+ regloc.location.register_number = row_regnum.GetAsKind(eRegisterKindLLDB);
+ m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
+ UnwindLogMsg(
+ "supplying caller's register %s (%d), saved in register %s (%d)",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB),
+ row_regnum.GetName(), row_regnum.GetAsKind(eRegisterKindLLDB));
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+
+ if (unwindplan_regloc.IsDWARFExpression() ||
+ unwindplan_regloc.IsAtDWARFExpression()) {
+ DataExtractor dwarfdata(unwindplan_regloc.GetDWARFExpressionBytes(),
+ unwindplan_regloc.GetDWARFExpressionLength(),
+ process->GetByteOrder(),
+ process->GetAddressByteSize());
+ ModuleSP opcode_ctx;
+ DWARFExpression dwarfexpr(opcode_ctx, dwarfdata, nullptr, 0,
+ unwindplan_regloc.GetDWARFExpressionLength());
+ dwarfexpr.SetRegisterKind(unwindplan_registerkind);
+ Value cfa_val = Scalar(m_cfa);
+ cfa_val.SetValueType(Value::eValueTypeLoadAddress);
+ Value result;
+ Status error;
+ if (dwarfexpr.Evaluate(&exe_ctx, this, 0, &cfa_val, nullptr, result,
+ &error)) {
+ addr_t val;
+ val = result.GetScalar().ULongLong();
+ if (unwindplan_regloc.IsDWARFExpression()) {
+ regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred;
+ regloc.location.inferred_value = val;
+ m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
+ UnwindLogMsg("supplying caller's register %s (%d) via DWARF expression "
+ "(IsDWARFExpression)",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ } else {
+ regloc.type =
+ UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation;
+ regloc.location.target_memory_location = val;
+ m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
+ UnwindLogMsg("supplying caller's register %s (%d) via DWARF expression "
+ "(IsAtDWARFExpression)",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ return UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+ }
+ UnwindLogMsg("tried to use IsDWARFExpression or IsAtDWARFExpression for %s "
+ "(%d) but failed",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ }
+
+ UnwindLogMsg("no save location for %s (%d) in this stack frame",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+
+ // FIXME UnwindPlan::Row types atDWARFExpression and isDWARFExpression are
+ // unsupported.
+
+ return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+}
+
+// TryFallbackUnwindPlan() -- this method is a little tricky.
+//
+// When this is called, the frame above -- the caller frame, the "previous"
+// frame -- is invalid or bad.
+//
+// Instead of stopping the stack walk here, we'll try a different UnwindPlan
+// and see if we can get a valid frame above us.
+//
+// This most often happens when an unwind plan based on assembly instruction
+// inspection is not correct -- mostly with hand-written assembly functions or
+// functions where the stack frame is set up "out of band", e.g. the kernel
+// saved the register context and then called an asynchronous trap handler like
+// _sigtramp.
+//
+// Often in these cases, if we just do a dumb stack walk we'll get past this
+// tricky frame and our usual techniques can continue to be used.
+
+bool RegisterContextLLDB::TryFallbackUnwindPlan() {
+ if (m_fallback_unwind_plan_sp.get() == nullptr)
+ return false;
+
+ if (m_full_unwind_plan_sp.get() == nullptr)
+ return false;
+
+ if (m_full_unwind_plan_sp.get() == m_fallback_unwind_plan_sp.get() ||
+ m_full_unwind_plan_sp->GetSourceName() ==
+ m_fallback_unwind_plan_sp->GetSourceName()) {
+ return false;
+ }
+
+ // If a compiler generated unwind plan failed, trying the arch default
+ // unwindplan isn't going to do any better.
+ if (m_full_unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolYes)
+ return false;
+
+ // Get the caller's pc value and our own CFA value. Swap in the fallback
+ // unwind plan, re-fetch the caller's pc value and CFA value. If they're the
+ // same, then the fallback unwind plan provides no benefit.
+
+ RegisterNumber pc_regnum(m_thread, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC);
+
+ addr_t old_caller_pc_value = LLDB_INVALID_ADDRESS;
+ addr_t new_caller_pc_value = LLDB_INVALID_ADDRESS;
+ UnwindLLDB::RegisterLocation regloc;
+ if (SavedLocationForRegister(pc_regnum.GetAsKind(eRegisterKindLLDB),
+ regloc) ==
+ UnwindLLDB::RegisterSearchResult::eRegisterFound) {
+ const RegisterInfo *reg_info =
+ GetRegisterInfoAtIndex(pc_regnum.GetAsKind(eRegisterKindLLDB));
+ if (reg_info) {
+ RegisterValue reg_value;
+ if (ReadRegisterValueFromRegisterLocation(regloc, reg_info, reg_value)) {
+ old_caller_pc_value = reg_value.GetAsUInt64();
+ }
+ }
+ }
+
+ // This is a tricky wrinkle! If SavedLocationForRegister() detects a really
+ // impossible register location for the full unwind plan, it may call
+ // ForceSwitchToFallbackUnwindPlan() which in turn replaces the full
+ // unwindplan with the fallback... in short, we're done, we're using the
+ // fallback UnwindPlan. We checked if m_fallback_unwind_plan_sp was nullptr
+ // at the top -- the only way it became nullptr since then is via
+ // SavedLocationForRegister().
+ if (m_fallback_unwind_plan_sp.get() == nullptr)
+ return true;
+
+ // Switch the full UnwindPlan to be the fallback UnwindPlan. If we decide
+ // this isn't working, we need to restore. We'll also need to save & restore
+ // the value of the m_cfa ivar. Save is down below a bit in 'old_cfa'.
+ UnwindPlanSP original_full_unwind_plan_sp = m_full_unwind_plan_sp;
+ addr_t old_cfa = m_cfa;
+ addr_t old_afa = m_afa;
+
+ m_registers.clear();
+
+ m_full_unwind_plan_sp = m_fallback_unwind_plan_sp;
+
+ UnwindPlan::RowSP active_row =
+ m_fallback_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset);
+
+ if (active_row &&
+ active_row->GetCFAValue().GetValueType() !=
+ UnwindPlan::Row::FAValue::unspecified) {
+ addr_t new_cfa;
+ if (!ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(),
+ active_row->GetCFAValue(), new_cfa) ||
+ new_cfa == 0 || new_cfa == 1 || new_cfa == LLDB_INVALID_ADDRESS) {
+ UnwindLogMsg("failed to get cfa with fallback unwindplan");
+ m_fallback_unwind_plan_sp.reset();
+ m_full_unwind_plan_sp = original_full_unwind_plan_sp;
+ return false;
+ }
+ m_cfa = new_cfa;
+
+ ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(),
+ active_row->GetAFAValue(), m_afa);
+
+ if (SavedLocationForRegister(pc_regnum.GetAsKind(eRegisterKindLLDB),
+ regloc) ==
+ UnwindLLDB::RegisterSearchResult::eRegisterFound) {
+ const RegisterInfo *reg_info =
+ GetRegisterInfoAtIndex(pc_regnum.GetAsKind(eRegisterKindLLDB));
+ if (reg_info) {
+ RegisterValue reg_value;
+ if (ReadRegisterValueFromRegisterLocation(regloc, reg_info,
+ reg_value)) {
+ new_caller_pc_value = reg_value.GetAsUInt64();
+ }
+ }
+ }
+
+ if (new_caller_pc_value == LLDB_INVALID_ADDRESS) {
+ UnwindLogMsg("failed to get a pc value for the caller frame with the "
+ "fallback unwind plan");
+ m_fallback_unwind_plan_sp.reset();
+ m_full_unwind_plan_sp = original_full_unwind_plan_sp;
+ m_cfa = old_cfa;
+ m_afa = old_afa;
+ return false;
+ }
+
+ if (old_caller_pc_value == new_caller_pc_value &&
+ m_cfa == old_cfa &&
+ m_afa == old_afa) {
+ UnwindLogMsg("fallback unwind plan got the same values for this frame "
+ "CFA and caller frame pc, not using");
+ m_fallback_unwind_plan_sp.reset();
+ m_full_unwind_plan_sp = original_full_unwind_plan_sp;
+ return false;
+ }
+
+ UnwindLogMsg("trying to unwind from this function with the UnwindPlan '%s' "
+ "because UnwindPlan '%s' failed.",
+ m_fallback_unwind_plan_sp->GetSourceName().GetCString(),
+ original_full_unwind_plan_sp->GetSourceName().GetCString());
+
+ // We've copied the fallback unwind plan into the full - now clear the
+ // fallback.
+ m_fallback_unwind_plan_sp.reset();
+ }
+
+ return true;
+}
+
+bool RegisterContextLLDB::ForceSwitchToFallbackUnwindPlan() {
+ if (m_fallback_unwind_plan_sp.get() == nullptr)
+ return false;
+
+ if (m_full_unwind_plan_sp.get() == nullptr)
+ return false;
+
+ if (m_full_unwind_plan_sp.get() == m_fallback_unwind_plan_sp.get() ||
+ m_full_unwind_plan_sp->GetSourceName() ==
+ m_fallback_unwind_plan_sp->GetSourceName()) {
+ return false;
+ }
+
+ UnwindPlan::RowSP active_row =
+ m_fallback_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset);
+
+ if (active_row &&
+ active_row->GetCFAValue().GetValueType() !=
+ UnwindPlan::Row::FAValue::unspecified) {
+ addr_t new_cfa;
+ if (!ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(),
+ active_row->GetCFAValue(), new_cfa) ||
+ new_cfa == 0 || new_cfa == 1 || new_cfa == LLDB_INVALID_ADDRESS) {
+ UnwindLogMsg("failed to get cfa with fallback unwindplan");
+ m_fallback_unwind_plan_sp.reset();
+ return false;
+ }
+
+ ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(),
+ active_row->GetAFAValue(), m_afa);
+
+ m_full_unwind_plan_sp = m_fallback_unwind_plan_sp;
+ m_fallback_unwind_plan_sp.reset();
+
+ m_registers.clear();
+
+ m_cfa = new_cfa;
+
+ UnwindLogMsg("switched unconditionally to the fallback unwindplan %s",
+ m_full_unwind_plan_sp->GetSourceName().GetCString());
+ return true;
+ }
+ return false;
+}
+
+bool RegisterContextLLDB::ReadFrameAddress(
+ lldb::RegisterKind row_register_kind, UnwindPlan::Row::FAValue &fa,
+ addr_t &address) {
+ RegisterValue reg_value;
+
+ address = LLDB_INVALID_ADDRESS;
+ addr_t cfa_reg_contents;
+
+ switch (fa.GetValueType()) {
+ case UnwindPlan::Row::FAValue::isRegisterDereferenced: {
+ RegisterNumber cfa_reg(m_thread, row_register_kind,
+ fa.GetRegisterNumber());
+ if (ReadGPRValue(cfa_reg, cfa_reg_contents)) {
+ const RegisterInfo *reg_info =
+ GetRegisterInfoAtIndex(cfa_reg.GetAsKind(eRegisterKindLLDB));
+ RegisterValue reg_value;
+ if (reg_info) {
+ Status error = ReadRegisterValueFromMemory(
+ reg_info, cfa_reg_contents, reg_info->byte_size, reg_value);
+ if (error.Success()) {
+ address = reg_value.GetAsUInt64();
+ UnwindLogMsg(
+ "CFA value via dereferencing reg %s (%d): reg has val 0x%" PRIx64
+ ", CFA value is 0x%" PRIx64,
+ cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB),
+ cfa_reg_contents, address);
+ return true;
+ } else {
+ UnwindLogMsg("Tried to deref reg %s (%d) [0x%" PRIx64
+ "] but memory read failed.",
+ cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB),
+ cfa_reg_contents);
+ }
+ }
+ }
+ break;
+ }
+ case UnwindPlan::Row::FAValue::isRegisterPlusOffset: {
+ RegisterNumber cfa_reg(m_thread, row_register_kind,
+ fa.GetRegisterNumber());
+ if (ReadGPRValue(cfa_reg, cfa_reg_contents)) {
+ if (cfa_reg_contents == LLDB_INVALID_ADDRESS || cfa_reg_contents == 0 ||
+ cfa_reg_contents == 1) {
+ UnwindLogMsg(
+ "Got an invalid CFA register value - reg %s (%d), value 0x%" PRIx64,
+ cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB),
+ cfa_reg_contents);
+ cfa_reg_contents = LLDB_INVALID_ADDRESS;
+ return false;
+ }
+ address = cfa_reg_contents + fa.GetOffset();
+ UnwindLogMsg(
+ "CFA is 0x%" PRIx64 ": Register %s (%d) contents are 0x%" PRIx64
+ ", offset is %d",
+ address, cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB),
+ cfa_reg_contents, fa.GetOffset());
+ return true;
+ }
+ break;
+ }
+ case UnwindPlan::Row::FAValue::isDWARFExpression: {
+ ExecutionContext exe_ctx(m_thread.shared_from_this());
+ Process *process = exe_ctx.GetProcessPtr();
+ DataExtractor dwarfdata(fa.GetDWARFExpressionBytes(),
+ fa.GetDWARFExpressionLength(),
+ process->GetByteOrder(),
+ process->GetAddressByteSize());
+ ModuleSP opcode_ctx;
+ DWARFExpression dwarfexpr(opcode_ctx, dwarfdata, nullptr, 0,
+ fa.GetDWARFExpressionLength());
+ dwarfexpr.SetRegisterKind(row_register_kind);
+ Value result;
+ Status error;
+ if (dwarfexpr.Evaluate(&exe_ctx, this, 0, nullptr, nullptr, result,
+ &error)) {
+ address = result.GetScalar().ULongLong();
+
+ UnwindLogMsg("CFA value set by DWARF expression is 0x%" PRIx64,
+ address);
+ return true;
+ }
+ UnwindLogMsg("Failed to set CFA value via DWARF expression: %s",
+ error.AsCString());
+ break;
+ }
+ default:
+ return false;
+ }
+ return false;
+}
+
+// Retrieve a general purpose register value for THIS frame, as saved by the
+// NEXT frame, i.e. the frame that
+// this frame called. e.g.
+//
+// foo () { }
+// bar () { foo (); }
+// main () { bar (); }
+//
+// stopped in foo() so
+// frame 0 - foo
+// frame 1 - bar
+// frame 2 - main
+// and this RegisterContext is for frame 1 (bar) - if we want to get the pc
+// value for frame 1, we need to ask
+// where frame 0 (the "next" frame) saved that and retrieve the value.
+
+bool RegisterContextLLDB::ReadGPRValue(lldb::RegisterKind register_kind,
+ uint32_t regnum, addr_t &value) {
+ if (!IsValid())
+ return false;
+
+ uint32_t lldb_regnum;
+ if (register_kind == eRegisterKindLLDB) {
+ lldb_regnum = regnum;
+ } else if (!m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds(
+ register_kind, regnum, eRegisterKindLLDB, lldb_regnum)) {
+ return false;
+ }
+
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(lldb_regnum);
+ RegisterValue reg_value;
+ // if this is frame 0 (currently executing frame), get the requested reg
+ // contents from the actual thread registers
+ if (IsFrameZero()) {
+ if (m_thread.GetRegisterContext()->ReadRegister(reg_info, reg_value)) {
+ value = reg_value.GetAsUInt64();
+ return true;
+ }
+ return false;
+ }
+
+ bool pc_register = false;
+ uint32_t generic_regnum;
+ if (register_kind == eRegisterKindGeneric &&
+ (regnum == LLDB_REGNUM_GENERIC_PC || regnum == LLDB_REGNUM_GENERIC_RA)) {
+ pc_register = true;
+ } else if (m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds(
+ register_kind, regnum, eRegisterKindGeneric, generic_regnum) &&
+ (generic_regnum == LLDB_REGNUM_GENERIC_PC ||
+ generic_regnum == LLDB_REGNUM_GENERIC_RA)) {
+ pc_register = true;
+ }
+
+ lldb_private::UnwindLLDB::RegisterLocation regloc;
+ if (!m_parent_unwind.SearchForSavedLocationForRegister(
+ lldb_regnum, regloc, m_frame_number - 1, pc_register)) {
+ return false;
+ }
+ if (ReadRegisterValueFromRegisterLocation(regloc, reg_info, reg_value)) {
+ value = reg_value.GetAsUInt64();
+ return true;
+ }
+ return false;
+}
+
+bool RegisterContextLLDB::ReadGPRValue(const RegisterNumber &regnum,
+ addr_t &value) {
+ return ReadGPRValue(regnum.GetRegisterKind(), regnum.GetRegisterNumber(),
+ value);
+}
+
+// Find the value of a register in THIS frame
+
+bool RegisterContextLLDB::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value) {
+ if (!IsValid())
+ return false;
+
+ const uint32_t lldb_regnum = reg_info->kinds[eRegisterKindLLDB];
+ UnwindLogMsgVerbose("looking for register saved location for reg %d",
+ lldb_regnum);
+
+ // If this is the 0th frame, hand this over to the live register context
+ if (IsFrameZero()) {
+ UnwindLogMsgVerbose("passing along to the live register context for reg %d",
+ lldb_regnum);
+ return m_thread.GetRegisterContext()->ReadRegister(reg_info, value);
+ }
+
+ bool is_pc_regnum = false;
+ if (reg_info->kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_PC ||
+ reg_info->kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_RA) {
+ is_pc_regnum = true;
+ }
+
+ lldb_private::UnwindLLDB::RegisterLocation regloc;
+ // Find out where the NEXT frame saved THIS frame's register contents
+ if (!m_parent_unwind.SearchForSavedLocationForRegister(
+ lldb_regnum, regloc, m_frame_number - 1, is_pc_regnum))
+ return false;
+
+ return ReadRegisterValueFromRegisterLocation(regloc, reg_info, value);
+}
+
+bool RegisterContextLLDB::WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &value) {
+ if (!IsValid())
+ return false;
+
+ const uint32_t lldb_regnum = reg_info->kinds[eRegisterKindLLDB];
+ UnwindLogMsgVerbose("looking for register saved location for reg %d",
+ lldb_regnum);
+
+ // If this is the 0th frame, hand this over to the live register context
+ if (IsFrameZero()) {
+ UnwindLogMsgVerbose("passing along to the live register context for reg %d",
+ lldb_regnum);
+ return m_thread.GetRegisterContext()->WriteRegister(reg_info, value);
+ }
+
+ lldb_private::UnwindLLDB::RegisterLocation regloc;
+ // Find out where the NEXT frame saved THIS frame's register contents
+ if (!m_parent_unwind.SearchForSavedLocationForRegister(
+ lldb_regnum, regloc, m_frame_number - 1, false))
+ return false;
+
+ return WriteRegisterValueToRegisterLocation(regloc, reg_info, value);
+}
+
+// Don't need to implement this one
+bool RegisterContextLLDB::ReadAllRegisterValues(lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+// Don't need to implement this one
+bool RegisterContextLLDB::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+// Retrieve the pc value for THIS from
+
+bool RegisterContextLLDB::GetCFA(addr_t &cfa) {
+ if (!IsValid()) {
+ return false;
+ }
+ if (m_cfa == LLDB_INVALID_ADDRESS) {
+ return false;
+ }
+ cfa = m_cfa;
+ return true;
+}
+
+RegisterContextLLDB::SharedPtr RegisterContextLLDB::GetNextFrame() const {
+ RegisterContextLLDB::SharedPtr regctx;
+ if (m_frame_number == 0)
+ return regctx;
+ return m_parent_unwind.GetRegisterContextForFrameNum(m_frame_number - 1);
+}
+
+RegisterContextLLDB::SharedPtr RegisterContextLLDB::GetPrevFrame() const {
+ RegisterContextLLDB::SharedPtr regctx;
+ return m_parent_unwind.GetRegisterContextForFrameNum(m_frame_number + 1);
+}
+
+// Retrieve the address of the start of the function of THIS frame
+
+bool RegisterContextLLDB::GetStartPC(addr_t &start_pc) {
+ if (!IsValid())
+ return false;
+
+ if (!m_start_pc.IsValid()) {
+ bool read_successfully = ReadPC (start_pc);
+ if (read_successfully)
+ {
+ ProcessSP process_sp (m_thread.GetProcess());
+ if (process_sp)
+ {
+ ABI *abi = process_sp->GetABI().get();
+ if (abi)
+ start_pc = abi->FixCodeAddress(start_pc);
+ }
+ }
+ return read_successfully;
+ }
+ start_pc = m_start_pc.GetLoadAddress(CalculateTarget().get());
+ return true;
+}
+
+// Retrieve the current pc value for THIS frame, as saved by the NEXT frame.
+
+bool RegisterContextLLDB::ReadPC(addr_t &pc) {
+ if (!IsValid())
+ return false;
+
+ bool above_trap_handler = false;
+ if (GetNextFrame().get() && GetNextFrame()->IsValid() &&
+ GetNextFrame()->IsTrapHandlerFrame())
+ above_trap_handler = true;
+
+ if (ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc)) {
+ // A pc value of 0 or 1 is impossible in the middle of the stack -- it
+ // indicates the end of a stack walk.
+ // On the currently executing frame (or such a frame interrupted
+ // asynchronously by sigtramp et al) this may occur if code has jumped
+ // through a NULL pointer -- we want to be able to unwind past that frame
+ // to help find the bug.
+
+ ProcessSP process_sp (m_thread.GetProcess());
+ if (process_sp)
+ {
+ ABI *abi = process_sp->GetABI().get();
+ if (abi)
+ pc = abi->FixCodeAddress(pc);
+ }
+
+ return !(m_all_registers_available == false &&
+ above_trap_handler == false && (pc == 0 || pc == 1));
+ } else {
+ return false;
+ }
+}
+
+void RegisterContextLLDB::UnwindLogMsg(const char *fmt, ...) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ if (log) {
+ va_list args;
+ va_start(args, fmt);
+
+ char *logmsg;
+ if (vasprintf(&logmsg, fmt, args) == -1 || logmsg == nullptr) {
+ if (logmsg)
+ free(logmsg);
+ va_end(args);
+ return;
+ }
+ va_end(args);
+
+ log->Printf("%*sth%d/fr%u %s", m_frame_number < 100 ? m_frame_number : 100,
+ "", m_thread.GetIndexID(), m_frame_number, logmsg);
+ free(logmsg);
+ }
+}
+
+void RegisterContextLLDB::UnwindLogMsgVerbose(const char *fmt, ...) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ if (log && log->GetVerbose()) {
+ va_list args;
+ va_start(args, fmt);
+
+ char *logmsg;
+ if (vasprintf(&logmsg, fmt, args) == -1 || logmsg == nullptr) {
+ if (logmsg)
+ free(logmsg);
+ va_end(args);
+ return;
+ }
+ va_end(args);
+
+ log->Printf("%*sth%d/fr%u %s", m_frame_number < 100 ? m_frame_number : 100,
+ "", m_thread.GetIndexID(), m_frame_number, logmsg);
+ free(logmsg);
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.h
new file mode 100644
index 000000000000..64dd394d233b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.h
@@ -0,0 +1,253 @@
+//===-- RegisterContextLLDB.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_RegisterContextLLDB_h_
+#define lldb_RegisterContextLLDB_h_
+
+#include <vector>
+
+#include "UnwindLLDB.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/RegisterNumber.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class UnwindLLDB;
+
+class RegisterContextLLDB : public lldb_private::RegisterContext {
+public:
+ typedef std::shared_ptr<RegisterContextLLDB> SharedPtr;
+
+ RegisterContextLLDB(lldb_private::Thread &thread, const SharedPtr &next_frame,
+ lldb_private::SymbolContext &sym_ctx,
+ uint32_t frame_number,
+ lldb_private::UnwindLLDB &unwind_lldb);
+
+ ~RegisterContextLLDB() override = default;
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t reg_set) override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+ bool IsValid() const;
+
+ bool IsTrapHandlerFrame() const;
+
+ bool GetCFA(lldb::addr_t &cfa);
+
+ bool GetStartPC(lldb::addr_t &start_pc);
+
+ bool ReadPC(lldb::addr_t &start_pc);
+
+private:
+ enum FrameType {
+ eNormalFrame,
+ eTrapHandlerFrame,
+ eDebuggerFrame, // a debugger inferior function call frame; we get caller's
+ // registers from debugger
+ eSkipFrame, // The unwind resulted in a bogus frame but may get back on
+ // track so we don't want to give up yet
+ eNotAValidFrame // this frame is invalid for some reason - most likely it is
+ // past the top (end) of the stack
+ };
+
+ // UnwindLLDB needs to pass around references to RegisterLocations
+ friend class UnwindLLDB;
+
+ // Returns true if we have an unwind loop -- the same stack frame unwinding
+ // multiple times.
+ bool CheckIfLoopingStack();
+
+ // Indicates whether this frame is frame zero -- the currently
+ // executing frame -- or not.
+ bool IsFrameZero() const;
+
+ void InitializeZerothFrame();
+
+ void InitializeNonZerothFrame();
+
+ SharedPtr GetNextFrame() const;
+
+ SharedPtr GetPrevFrame() const;
+
+ // A SkipFrame occurs when the unwind out of frame 0 didn't go right -- we've
+ // got one bogus frame at frame #1.
+ // There is a good chance we'll get back on track if we follow the frame
+ // pointer chain (or whatever is appropriate
+ // on this ABI) so we allow one invalid frame to be in the stack. Ideally
+ // we'll mark this frame specially at some
+ // point and indicate to the user that the unwinder had a hiccup. Often when
+ // this happens we will miss a frame of
+ // the program's actual stack in the unwind and we want to flag that for the
+ // user somehow.
+ bool IsSkipFrame() const;
+
+ /// Determines if a SymbolContext is a trap handler or not
+ ///
+ /// Given a SymbolContext, determines if this is a trap handler function
+ /// aka asynchronous signal handler.
+ ///
+ /// \return
+ /// Returns true if the SymbolContext is a trap handler.
+ bool IsTrapHandlerSymbol(lldb_private::Process *process,
+ const lldb_private::SymbolContext &m_sym_ctx) const;
+
+ // Provide a location for where THIS function saved the CALLER's register
+ // value
+ // Or a frame "below" this one saved it, i.e. a function called by this one,
+ // preserved a register that this
+ // function didn't modify/use.
+ //
+ // The RegisterLocation type may be set to eRegisterNotAvailable -- this will
+ // happen for a volatile register
+ // being queried mid-stack. Instead of floating frame 0's contents of that
+ // register up the stack (which may
+ // or may not be the value of that reg when the function was executing), we
+ // won't return any value.
+ //
+ // If a non-volatile register (a "preserved" register) is requested mid-stack
+ // and no frames "below" the requested
+ // stack have saved the register anywhere, it is safe to assume that frame 0's
+ // register values are still the same
+ // as the requesting frame's.
+ lldb_private::UnwindLLDB::RegisterSearchResult
+ SavedLocationForRegister(uint32_t lldb_regnum,
+ lldb_private::UnwindLLDB::RegisterLocation &regloc);
+
+ bool ReadRegisterValueFromRegisterLocation(
+ lldb_private::UnwindLLDB::RegisterLocation regloc,
+ const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value);
+
+ bool WriteRegisterValueToRegisterLocation(
+ lldb_private::UnwindLLDB::RegisterLocation regloc,
+ const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value);
+
+ /// If the unwind has to the caller frame has failed, try something else
+ ///
+ /// If lldb is using an assembly language based UnwindPlan for a frame and
+ /// the unwind to the caller frame fails, try falling back to a generic
+ /// UnwindPlan (architecture default unwindplan) to see if that might work
+ /// better. This is mostly helping to work around problems where the
+ /// assembly language inspection fails on hand-written assembly code.
+ ///
+ /// \return
+ /// Returns true if a fallback unwindplan was found & was installed.
+ bool TryFallbackUnwindPlan();
+
+ /// Switch to the fallback unwind plan unconditionally without any safety
+ /// checks that it is providing better results than the normal unwind plan.
+ ///
+ /// The only time it is valid to call this method is if the full unwindplan is
+ /// found to be fundamentally incorrect/impossible.
+ ///
+ /// Returns true if it was able to install the fallback unwind plan.
+ bool ForceSwitchToFallbackUnwindPlan();
+
+ // Get the contents of a general purpose (address-size) register for this
+ // frame
+ // (usually retrieved from the next frame)
+ bool ReadGPRValue(lldb::RegisterKind register_kind, uint32_t regnum,
+ lldb::addr_t &value);
+
+ bool ReadGPRValue(const RegisterNumber &reg_num, lldb::addr_t &value);
+
+ // Get the Frame Address register for a given frame.
+ bool ReadFrameAddress(lldb::RegisterKind register_kind,
+ UnwindPlan::Row::FAValue &fa, lldb::addr_t &address);
+
+ lldb::UnwindPlanSP GetFastUnwindPlanForFrame();
+
+ lldb::UnwindPlanSP GetFullUnwindPlanForFrame();
+
+ void UnwindLogMsg(const char *fmt, ...) __attribute__((format(printf, 2, 3)));
+
+ void UnwindLogMsgVerbose(const char *fmt, ...)
+ __attribute__((format(printf, 2, 3)));
+
+ bool IsUnwindPlanValidForCurrentPC(lldb::UnwindPlanSP unwind_plan_sp,
+ int &valid_pc_offset);
+
+ lldb_private::Thread &m_thread;
+
+ ///
+ // The following tell us how to retrieve the CALLER's register values (ie the
+ // "previous" frame, aka the frame above)
+ // i.e. where THIS frame saved them
+ ///
+
+ lldb::UnwindPlanSP m_fast_unwind_plan_sp; // may be NULL
+ lldb::UnwindPlanSP m_full_unwind_plan_sp;
+ lldb::UnwindPlanSP m_fallback_unwind_plan_sp; // may be NULL
+
+ bool m_all_registers_available; // Can we retrieve all regs or just
+ // nonvolatile regs?
+ int m_frame_type; // enum FrameType
+
+ lldb::addr_t m_cfa;
+ lldb::addr_t m_afa;
+ lldb_private::Address m_start_pc;
+ lldb_private::Address m_current_pc;
+
+ int m_current_offset; // how far into the function we've executed; -1 if
+ // unknown
+ // 0 if no instructions have been executed yet.
+
+ int m_current_offset_backed_up_one; // how far into the function we've
+ // executed; -1 if unknown
+ // 0 if no instructions have been executed yet.
+ // On architectures where the return address on the stack points
+ // to the instruction after the CALL, this value will have 1
+ // subtracted from it. Else a function that ends in a CALL will
+ // have an offset pointing into the next function's address range.
+ // m_current_pc has the actual address of the "current" pc.
+
+ lldb_private::SymbolContext &m_sym_ctx;
+ bool m_sym_ctx_valid; // if ResolveSymbolContextForAddress fails, don't try to
+ // use m_sym_ctx
+
+ uint32_t m_frame_number; // What stack frame this RegisterContext is
+
+ std::map<uint32_t, lldb_private::UnwindLLDB::RegisterLocation>
+ m_registers; // where to find reg values for this frame
+
+ lldb_private::UnwindLLDB &m_parent_unwind; // The UnwindLLDB that is creating
+ // this RegisterContextLLDB
+
+ // For RegisterContextLLDB only
+
+ DISALLOW_COPY_AND_ASSIGN(RegisterContextLLDB);
+};
+
+} // namespace lldb_private
+
+#endif // lldb_RegisterContextLLDB_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_i386.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_i386.cpp
new file mode 100644
index 000000000000..79979639dc7e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_i386.cpp
@@ -0,0 +1,133 @@
+//===-- RegisterContextLinux_i386.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 "RegisterContextLinux_i386.h"
+#include "RegisterContextPOSIX_x86.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+struct GPR {
+ uint32_t ebx;
+ uint32_t ecx;
+ uint32_t edx;
+ uint32_t esi;
+ uint32_t edi;
+ uint32_t ebp;
+ uint32_t eax;
+ uint32_t ds;
+ uint32_t es;
+ uint32_t fs;
+ uint32_t gs;
+ uint32_t orig_eax;
+ uint32_t eip;
+ uint32_t cs;
+ uint32_t eflags;
+ uint32_t esp;
+ uint32_t ss;
+};
+
+struct FPR_i386 {
+ uint16_t fctrl; // FPU Control Word (fcw)
+ uint16_t fstat; // FPU Status Word (fsw)
+ uint16_t ftag; // FPU Tag Word (ftw)
+ uint16_t fop; // Last Instruction Opcode (fop)
+ union {
+ struct {
+ uint64_t fip; // Instruction Pointer
+ uint64_t fdp; // Data Pointer
+ } x86_64;
+ struct {
+ uint32_t fioff; // FPU IP Offset (fip)
+ uint32_t fiseg; // FPU IP Selector (fcs)
+ uint32_t fooff; // FPU Operand Pointer Offset (foo)
+ uint32_t foseg; // FPU Operand Pointer Selector (fos)
+ } i386_; // Added _ in the end to avoid error with gcc defining i386 in some
+ // cases
+ } ptr;
+ uint32_t mxcsr; // MXCSR Register State
+ uint32_t mxcsrmask; // MXCSR Mask
+ MMSReg stmm[8]; // 8*16 bytes for each FP-reg = 128 bytes
+ XMMReg xmm[8]; // 8*16 bytes for each XMM-reg = 128 bytes
+ uint32_t padding[56];
+};
+
+struct UserArea {
+ GPR regs; // General purpose registers.
+ int32_t fpvalid; // True if FPU is being used.
+ FPR_i386 i387; // FPU registers.
+ uint32_t tsize; // Text segment size.
+ uint32_t dsize; // Data segment size.
+ uint32_t ssize; // Stack segment size.
+ uint32_t start_code; // VM address of text.
+ uint32_t start_stack; // VM address of stack bottom (top in rsp).
+ int32_t signal; // Signal causing core dump.
+ int32_t reserved; // Unused.
+ uint32_t ar0; // Location of GPR's.
+ uint32_t fpstate; // Location of FPR's. Should be a FXSTATE *, but this
+ // has to be 32-bits even on 64-bit systems.
+ uint32_t magic; // Identifier for core dumps.
+ char u_comm[32]; // Command causing core dump.
+ uint32_t u_debugreg[8]; // Debug registers (DR0 - DR7).
+};
+
+#define DR_SIZE sizeof(((UserArea *)NULL)->u_debugreg[0])
+#define DR_0_OFFSET 0xFC
+#define DR_OFFSET(reg_index) (DR_0_OFFSET + (reg_index * 4))
+#define FPR_SIZE(reg) sizeof(((FPR_i386 *)NULL)->reg)
+
+// Include RegisterInfos_i386 to declare our g_register_infos_i386 structure.
+#define DECLARE_REGISTER_INFOS_I386_STRUCT
+#include "RegisterInfos_i386.h"
+#undef DECLARE_REGISTER_INFOS_I386_STRUCT
+
+RegisterContextLinux_i386::RegisterContextLinux_i386(
+ const ArchSpec &target_arch)
+ : RegisterInfoInterface(target_arch) {
+ RegisterInfo orig_ax = {"orig_eax",
+ nullptr,
+ sizeof(((GPR *)nullptr)->orig_eax),
+ (LLVM_EXTENSION offsetof(GPR, orig_eax)),
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0};
+ d_register_infos.push_back(orig_ax);
+}
+
+size_t RegisterContextLinux_i386::GetGPRSize() const { return sizeof(GPR); }
+
+const RegisterInfo *RegisterContextLinux_i386::GetRegisterInfo() const {
+ switch (m_target_arch.GetMachine()) {
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ return g_register_infos_i386;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+uint32_t RegisterContextLinux_i386::GetRegisterCount() const {
+ return static_cast<uint32_t>(sizeof(g_register_infos_i386) /
+ sizeof(g_register_infos_i386[0]));
+}
+
+uint32_t RegisterContextLinux_i386::GetUserRegisterCount() const {
+ return static_cast<uint32_t>(k_num_user_registers_i386);
+}
+
+const std::vector<lldb_private::RegisterInfo> *
+RegisterContextLinux_i386::GetDynamicRegisterInfoP() const {
+ return &d_register_infos;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_i386.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_i386.h
new file mode 100644
index 000000000000..5567a1ac42e5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_i386.h
@@ -0,0 +1,33 @@
+//===-- RegisterContextLinux_i386.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 liblldb_RegisterContextLinux_i386_H_
+#define liblldb_RegisterContextLinux_i386_H_
+
+#include "RegisterInfoInterface.h"
+
+class RegisterContextLinux_i386 : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterContextLinux_i386(const lldb_private::ArchSpec &target_arch);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+
+ uint32_t GetUserRegisterCount() const override;
+
+ const std::vector<lldb_private::RegisterInfo> *
+ GetDynamicRegisterInfoP() const override;
+
+private:
+ std::vector<lldb_private::RegisterInfo> d_register_infos;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips.cpp
new file mode 100644
index 000000000000..fc60fea79176
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips.cpp
@@ -0,0 +1,149 @@
+//===-- RegisterContextLinux_mips.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 <stddef.h>
+#include <vector>
+
+// For eh_frame and DWARF Register numbers
+#include "RegisterContextLinux_mips.h"
+
+// Internal codes for mips registers
+#include "lldb-mips-linux-register-enums.h"
+
+// For GP and FP buffers
+#include "RegisterContext_mips.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+// Include RegisterInfos_mips to declare our g_register_infos_mips structure.
+#define DECLARE_REGISTER_INFOS_MIPS_STRUCT
+#include "RegisterInfos_mips.h"
+#undef DECLARE_REGISTER_INFOS_MIPS_STRUCT
+
+// mips general purpose registers.
+const uint32_t g_gp_regnums_mips[] = {
+ gpr_zero_mips, gpr_r1_mips, gpr_r2_mips, gpr_r3_mips,
+ gpr_r4_mips, gpr_r5_mips, gpr_r6_mips, gpr_r7_mips,
+ gpr_r8_mips, gpr_r9_mips, gpr_r10_mips, gpr_r11_mips,
+ gpr_r12_mips, gpr_r13_mips, gpr_r14_mips, gpr_r15_mips,
+ gpr_r16_mips, gpr_r17_mips, gpr_r18_mips, gpr_r19_mips,
+ gpr_r20_mips, gpr_r21_mips, gpr_r22_mips, gpr_r23_mips,
+ gpr_r24_mips, gpr_r25_mips, gpr_r26_mips, gpr_r27_mips,
+ gpr_gp_mips, gpr_sp_mips, gpr_r30_mips, gpr_ra_mips,
+ gpr_sr_mips, gpr_mullo_mips, gpr_mulhi_mips, gpr_badvaddr_mips,
+ gpr_cause_mips, gpr_pc_mips, gpr_config5_mips,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+
+static_assert((sizeof(g_gp_regnums_mips) / sizeof(g_gp_regnums_mips[0])) - 1 ==
+ k_num_gpr_registers_mips,
+ "g_gp_regnums_mips has wrong number of register infos");
+// mips floating point registers.
+const uint32_t g_fp_regnums_mips[] = {
+ fpr_f0_mips, fpr_f1_mips, fpr_f2_mips, fpr_f3_mips,
+ fpr_f4_mips, fpr_f5_mips, fpr_f6_mips, fpr_f7_mips,
+ fpr_f8_mips, fpr_f9_mips, fpr_f10_mips, fpr_f11_mips,
+ fpr_f12_mips, fpr_f13_mips, fpr_f14_mips, fpr_f15_mips,
+ fpr_f16_mips, fpr_f17_mips, fpr_f18_mips, fpr_f19_mips,
+ fpr_f20_mips, fpr_f21_mips, fpr_f22_mips, fpr_f23_mips,
+ fpr_f24_mips, fpr_f25_mips, fpr_f26_mips, fpr_f27_mips,
+ fpr_f28_mips, fpr_f29_mips, fpr_f30_mips, fpr_f31_mips,
+ fpr_fcsr_mips, fpr_fir_mips, fpr_config5_mips,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+
+static_assert((sizeof(g_fp_regnums_mips) / sizeof(g_fp_regnums_mips[0])) - 1 ==
+ k_num_fpr_registers_mips,
+ "g_fp_regnums_mips has wrong number of register infos");
+
+// mips MSA registers.
+const uint32_t g_msa_regnums_mips[] = {
+ msa_w0_mips, msa_w1_mips, msa_w2_mips, msa_w3_mips,
+ msa_w4_mips, msa_w5_mips, msa_w6_mips, msa_w7_mips,
+ msa_w8_mips, msa_w9_mips, msa_w10_mips, msa_w11_mips,
+ msa_w12_mips, msa_w13_mips, msa_w14_mips, msa_w15_mips,
+ msa_w16_mips, msa_w17_mips, msa_w18_mips, msa_w19_mips,
+ msa_w20_mips, msa_w21_mips, msa_w22_mips, msa_w23_mips,
+ msa_w24_mips, msa_w25_mips, msa_w26_mips, msa_w27_mips,
+ msa_w28_mips, msa_w29_mips, msa_w30_mips, msa_w31_mips,
+ msa_fcsr_mips, msa_fir_mips, msa_mcsr_mips, msa_mir_mips,
+ msa_config5_mips,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+
+static_assert((sizeof(g_msa_regnums_mips) / sizeof(g_msa_regnums_mips[0])) -
+ 1 ==
+ k_num_msa_registers_mips,
+ "g_msa_regnums_mips has wrong number of register infos");
+
+// Number of register sets provided by this context.
+constexpr size_t k_num_register_sets = 3;
+
+// Register sets for mips.
+static const RegisterSet g_reg_sets_mips[k_num_register_sets] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_registers_mips,
+ g_gp_regnums_mips},
+ {"Floating Point Registers", "fpu", k_num_fpr_registers_mips,
+ g_fp_regnums_mips},
+ {"MSA Registers", "msa", k_num_msa_registers_mips, g_msa_regnums_mips}};
+
+uint32_t GetUserRegisterInfoCount(bool msa_present) {
+ if (msa_present)
+ return static_cast<uint32_t>(k_num_user_registers_mips);
+ return static_cast<uint32_t>(k_num_user_registers_mips -
+ k_num_msa_registers_mips);
+}
+
+RegisterContextLinux_mips::RegisterContextLinux_mips(
+ const ArchSpec &target_arch, bool msa_present)
+ : RegisterInfoInterface(target_arch),
+ m_user_register_count(GetUserRegisterInfoCount(msa_present)) {}
+
+size_t RegisterContextLinux_mips::GetGPRSize() const {
+ return sizeof(GPR_linux_mips);
+}
+
+const RegisterInfo *RegisterContextLinux_mips::GetRegisterInfo() const {
+ switch (m_target_arch.GetMachine()) {
+ case llvm::Triple::mips:
+ case llvm::Triple::mipsel:
+ return g_register_infos_mips;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+const RegisterSet *
+RegisterContextLinux_mips::GetRegisterSet(size_t set) const {
+ if (set >= k_num_register_sets)
+ return nullptr;
+ switch (m_target_arch.GetMachine()) {
+ case llvm::Triple::mips:
+ case llvm::Triple::mipsel:
+ return &g_reg_sets_mips[set];
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+size_t
+RegisterContextLinux_mips::GetRegisterSetCount() const {
+ return k_num_register_sets;
+}
+
+uint32_t RegisterContextLinux_mips::GetRegisterCount() const {
+ return static_cast<uint32_t>(sizeof(g_register_infos_mips) /
+ sizeof(g_register_infos_mips[0]));
+}
+
+uint32_t RegisterContextLinux_mips::GetUserRegisterCount() const {
+ return static_cast<uint32_t>(m_user_register_count);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips.h
new file mode 100644
index 000000000000..e637dfc15e4d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips.h
@@ -0,0 +1,36 @@
+//===-- RegisterContextLinux_mips.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 liblldb_RegisterContextLinux_mips_H_
+#define liblldb_RegisterContextLinux_mips_H_
+
+#include "RegisterInfoInterface.h"
+#include "lldb/lldb-private.h"
+
+class RegisterContextLinux_mips : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterContextLinux_mips(const lldb_private::ArchSpec &target_arch,
+ bool msa_present = true);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) const;
+
+ size_t GetRegisterSetCount() const;
+
+ uint32_t GetRegisterCount() const override;
+
+ uint32_t GetUserRegisterCount() const override;
+
+private:
+ uint32_t m_user_register_count;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips64.cpp
new file mode 100644
index 000000000000..3927883c47a4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips64.cpp
@@ -0,0 +1,207 @@
+//===-- RegisterContextLinux_mips64.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 <stddef.h>
+#include <vector>
+
+// For eh_frame and DWARF Register numbers
+#include "RegisterContextLinux_mips64.h"
+
+// For GP and FP buffers
+#include "RegisterContext_mips.h"
+
+// Internal codes for all mips32 and mips64 registers
+#include "lldb-mips-linux-register-enums.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Include RegisterInfos_mips64 to declare our g_register_infos_mips64
+// structure.
+#define DECLARE_REGISTER_INFOS_MIPS64_STRUCT
+#define LINUX_MIPS64
+#include "RegisterInfos_mips64.h"
+#undef LINUX_MIPS64
+#undef DECLARE_REGISTER_INFOS_MIPS64_STRUCT
+
+// Include RegisterInfos_mips to declare our g_register_infos_mips structure.
+#define DECLARE_REGISTER_INFOS_MIPS_STRUCT
+#include "RegisterInfos_mips.h"
+#undef DECLARE_REGISTER_INFOS_MIPS_STRUCT
+
+// mips64 general purpose registers.
+const uint32_t g_gp_regnums_mips64[] = {
+ gpr_zero_mips64, gpr_r1_mips64, gpr_r2_mips64,
+ gpr_r3_mips64, gpr_r4_mips64, gpr_r5_mips64,
+ gpr_r6_mips64, gpr_r7_mips64, gpr_r8_mips64,
+ gpr_r9_mips64, gpr_r10_mips64, gpr_r11_mips64,
+ gpr_r12_mips64, gpr_r13_mips64, gpr_r14_mips64,
+ gpr_r15_mips64, gpr_r16_mips64, gpr_r17_mips64,
+ gpr_r18_mips64, gpr_r19_mips64, gpr_r20_mips64,
+ gpr_r21_mips64, gpr_r22_mips64, gpr_r23_mips64,
+ gpr_r24_mips64, gpr_r25_mips64, gpr_r26_mips64,
+ gpr_r27_mips64, gpr_gp_mips64, gpr_sp_mips64,
+ gpr_r30_mips64, gpr_ra_mips64, gpr_sr_mips64,
+ gpr_mullo_mips64, gpr_mulhi_mips64, gpr_badvaddr_mips64,
+ gpr_cause_mips64, gpr_pc_mips64, gpr_config5_mips64,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+
+static_assert((sizeof(g_gp_regnums_mips64) / sizeof(g_gp_regnums_mips64[0])) -
+ 1 ==
+ k_num_gpr_registers_mips64,
+ "g_gp_regnums_mips64 has wrong number of register infos");
+
+// mips64 floating point registers.
+const uint32_t g_fp_regnums_mips64[] = {
+ fpr_f0_mips64, fpr_f1_mips64, fpr_f2_mips64, fpr_f3_mips64,
+ fpr_f4_mips64, fpr_f5_mips64, fpr_f6_mips64, fpr_f7_mips64,
+ fpr_f8_mips64, fpr_f9_mips64, fpr_f10_mips64, fpr_f11_mips64,
+ fpr_f12_mips64, fpr_f13_mips64, fpr_f14_mips64, fpr_f15_mips64,
+ fpr_f16_mips64, fpr_f17_mips64, fpr_f18_mips64, fpr_f19_mips64,
+ fpr_f20_mips64, fpr_f21_mips64, fpr_f22_mips64, fpr_f23_mips64,
+ fpr_f24_mips64, fpr_f25_mips64, fpr_f26_mips64, fpr_f27_mips64,
+ fpr_f28_mips64, fpr_f29_mips64, fpr_f30_mips64, fpr_f31_mips64,
+ fpr_fcsr_mips64, fpr_fir_mips64, fpr_config5_mips64,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+
+static_assert((sizeof(g_fp_regnums_mips64) / sizeof(g_fp_regnums_mips64[0])) -
+ 1 ==
+ k_num_fpr_registers_mips64,
+ "g_fp_regnums_mips64 has wrong number of register infos");
+
+// mips64 MSA registers.
+const uint32_t g_msa_regnums_mips64[] = {
+ msa_w0_mips64, msa_w1_mips64, msa_w2_mips64, msa_w3_mips64,
+ msa_w4_mips64, msa_w5_mips64, msa_w6_mips64, msa_w7_mips64,
+ msa_w8_mips64, msa_w9_mips64, msa_w10_mips64, msa_w11_mips64,
+ msa_w12_mips64, msa_w13_mips64, msa_w14_mips64, msa_w15_mips64,
+ msa_w16_mips64, msa_w17_mips64, msa_w18_mips64, msa_w19_mips64,
+ msa_w20_mips64, msa_w21_mips64, msa_w22_mips64, msa_w23_mips64,
+ msa_w24_mips64, msa_w25_mips64, msa_w26_mips64, msa_w27_mips64,
+ msa_w28_mips64, msa_w29_mips64, msa_w30_mips64, msa_w31_mips64,
+ msa_fcsr_mips64, msa_fir_mips64, msa_mcsr_mips64, msa_mir_mips64,
+ msa_config5_mips64,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+
+static_assert((sizeof(g_msa_regnums_mips64) / sizeof(g_msa_regnums_mips64[0])) -
+ 1 ==
+ k_num_msa_registers_mips64,
+ "g_msa_regnums_mips64 has wrong number of register infos");
+
+// Number of register sets provided by this context.
+constexpr size_t k_num_register_sets = 3;
+
+// Register sets for mips64.
+static const RegisterSet g_reg_sets_mips64[k_num_register_sets] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_registers_mips64,
+ g_gp_regnums_mips64},
+ {"Floating Point Registers", "fpu", k_num_fpr_registers_mips64,
+ g_fp_regnums_mips64},
+ {"MSA Registers", "msa", k_num_msa_registers_mips64, g_msa_regnums_mips64},
+};
+
+const RegisterSet *
+RegisterContextLinux_mips64::GetRegisterSet(size_t set) const {
+ if (set >= k_num_register_sets)
+ return nullptr;
+
+ switch (m_target_arch.GetMachine()) {
+ case llvm::Triple::mips64:
+ case llvm::Triple::mips64el:
+ return &g_reg_sets_mips64[set];
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+ return nullptr;
+}
+
+size_t
+RegisterContextLinux_mips64::GetRegisterSetCount() const {
+ return k_num_register_sets;
+}
+
+static const RegisterInfo *GetRegisterInfoPtr(const ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::mips64:
+ case llvm::Triple::mips64el:
+ return g_register_infos_mips64;
+ case llvm::Triple::mips:
+ case llvm::Triple::mipsel:
+ return g_register_infos_mips;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+static uint32_t GetRegisterInfoCount(const ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::mips64:
+ case llvm::Triple::mips64el:
+ return static_cast<uint32_t>(sizeof(g_register_infos_mips64) /
+ sizeof(g_register_infos_mips64[0]));
+ case llvm::Triple::mips:
+ case llvm::Triple::mipsel:
+ return static_cast<uint32_t>(sizeof(g_register_infos_mips) /
+ sizeof(g_register_infos_mips[0]));
+ default:
+ assert(false && "Unhandled target architecture.");
+ return 0;
+ }
+}
+
+uint32_t GetUserRegisterInfoCount(const ArchSpec &target_arch,
+ bool msa_present) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::mips:
+ case llvm::Triple::mipsel:
+ if (msa_present)
+ return static_cast<uint32_t>(k_num_user_registers_mips);
+ return static_cast<uint32_t>(k_num_user_registers_mips -
+ k_num_msa_registers_mips);
+ case llvm::Triple::mips64el:
+ case llvm::Triple::mips64:
+ if (msa_present)
+ return static_cast<uint32_t>(k_num_user_registers_mips64);
+ return static_cast<uint32_t>(k_num_user_registers_mips64 -
+ k_num_msa_registers_mips64);
+ default:
+ assert(false && "Unhandled target architecture.");
+ return 0;
+ }
+}
+
+RegisterContextLinux_mips64::RegisterContextLinux_mips64(
+ const ArchSpec &target_arch, bool msa_present)
+ : lldb_private::RegisterInfoInterface(target_arch),
+ m_register_info_p(GetRegisterInfoPtr(target_arch)),
+ m_register_info_count(GetRegisterInfoCount(target_arch)),
+ m_user_register_count(
+ GetUserRegisterInfoCount(target_arch, msa_present)) {}
+
+size_t RegisterContextLinux_mips64::GetGPRSize() const {
+ return sizeof(GPR_linux_mips);
+}
+
+const RegisterInfo *RegisterContextLinux_mips64::GetRegisterInfo() const {
+ return m_register_info_p;
+}
+
+uint32_t RegisterContextLinux_mips64::GetRegisterCount() const {
+ return m_register_info_count;
+}
+
+uint32_t RegisterContextLinux_mips64::GetUserRegisterCount() const {
+ return m_user_register_count;
+}
+
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips64.h
new file mode 100644
index 000000000000..ca0f0140a22d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_mips64.h
@@ -0,0 +1,39 @@
+//===-- RegisterContextLinux_mips64.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 liblldb_RegisterContextLinux_mips64_H_
+#define liblldb_RegisterContextLinux_mips64_H_
+
+#include "RegisterInfoInterface.h"
+#include "lldb/lldb-private.h"
+
+class RegisterContextLinux_mips64 : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterContextLinux_mips64(const lldb_private::ArchSpec &target_arch,
+ bool msa_present = true);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) const;
+
+ size_t GetRegisterSetCount() const;
+
+ uint32_t GetRegisterCount() const override;
+
+ uint32_t GetUserRegisterCount() const override;
+
+private:
+ const lldb_private::RegisterInfo *m_register_info_p;
+ uint32_t m_register_info_count;
+ uint32_t m_user_register_count;
+};
+
+#endif
+
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_s390x.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_s390x.cpp
new file mode 100644
index 000000000000..d6401d788ab2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_s390x.cpp
@@ -0,0 +1,74 @@
+//===-- RegisterContextLinux_s390x.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 "RegisterContextLinux_s390x.h"
+#include "RegisterContextPOSIX_s390x.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+// Include RegisterInfos_s390x to declare our g_register_infos_s390x structure.
+#define DECLARE_REGISTER_INFOS_S390X_STRUCT
+#include "RegisterInfos_s390x.h"
+#undef DECLARE_REGISTER_INFOS_S390X_STRUCT
+
+static const RegisterInfo *GetRegisterInfoPtr(const ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::systemz:
+ return g_register_infos_s390x;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+static uint32_t GetRegisterInfoCount(const ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::systemz:
+ return k_num_registers_s390x;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return 0;
+ }
+}
+
+static uint32_t GetUserRegisterInfoCount(const ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::systemz:
+ return k_num_user_registers_s390x + k_num_linux_registers_s390x;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return 0;
+ }
+}
+
+RegisterContextLinux_s390x::RegisterContextLinux_s390x(
+ const ArchSpec &target_arch)
+ : lldb_private::RegisterInfoInterface(target_arch),
+ m_register_info_p(GetRegisterInfoPtr(target_arch)),
+ m_register_info_count(GetRegisterInfoCount(target_arch)),
+ m_user_register_count(GetUserRegisterInfoCount(target_arch)) {}
+
+const std::vector<lldb_private::RegisterInfo> *
+RegisterContextLinux_s390x::GetDynamicRegisterInfoP() const {
+ return &d_register_infos;
+}
+
+const RegisterInfo *RegisterContextLinux_s390x::GetRegisterInfo() const {
+ return m_register_info_p;
+}
+
+uint32_t RegisterContextLinux_s390x::GetRegisterCount() const {
+ return m_register_info_count;
+}
+
+uint32_t RegisterContextLinux_s390x::GetUserRegisterCount() const {
+ return m_user_register_count;
+}
+
+size_t RegisterContextLinux_s390x::GetGPRSize() const { return 0; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_s390x.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_s390x.h
new file mode 100644
index 000000000000..10810c97af80
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_s390x.h
@@ -0,0 +1,36 @@
+//===-- RegisterContextLinux_s390x.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 liblldb_RegisterContextLinux_s390x_h_
+#define liblldb_RegisterContextLinux_s390x_h_
+
+#include "RegisterInfoInterface.h"
+
+class RegisterContextLinux_s390x : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterContextLinux_s390x(const lldb_private::ArchSpec &target_arch);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+
+ uint32_t GetUserRegisterCount() const override;
+
+ const std::vector<lldb_private::RegisterInfo> *
+ GetDynamicRegisterInfoP() const override;
+
+private:
+ const lldb_private::RegisterInfo *m_register_info_p;
+ uint32_t m_register_info_count;
+ uint32_t m_user_register_count;
+ std::vector<lldb_private::RegisterInfo> d_register_infos;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.cpp
new file mode 100644
index 000000000000..640d5bc02256
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.cpp
@@ -0,0 +1,192 @@
+//===-- RegisterContextLinux_x86_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
+//
+//===---------------------------------------------------------------------===//
+
+#include "RegisterContextLinux_x86_64.h"
+#include "RegisterContextLinux_i386.h"
+#include "RegisterContextPOSIX_x86.h"
+#include <vector>
+
+using namespace lldb_private;
+using namespace lldb;
+
+typedef struct _GPR {
+ uint64_t r15;
+ uint64_t r14;
+ uint64_t r13;
+ uint64_t r12;
+ uint64_t rbp;
+ uint64_t rbx;
+ uint64_t r11;
+ uint64_t r10;
+ uint64_t r9;
+ uint64_t r8;
+ uint64_t rax;
+ uint64_t rcx;
+ uint64_t rdx;
+ uint64_t rsi;
+ uint64_t rdi;
+ uint64_t orig_rax;
+ uint64_t rip;
+ uint64_t cs;
+ uint64_t rflags;
+ uint64_t rsp;
+ uint64_t ss;
+ uint64_t fs_base;
+ uint64_t gs_base;
+ uint64_t ds;
+ uint64_t es;
+ uint64_t fs;
+ uint64_t gs;
+} GPR;
+
+struct DBG {
+ uint64_t dr[8];
+};
+
+struct UserArea {
+ GPR gpr; // General purpose registers.
+ int32_t fpvalid; // True if FPU is being used.
+ int32_t pad0;
+ FXSAVE fpr; // General purpose floating point registers (see FPR for extended
+ // register sets).
+ uint64_t tsize; // Text segment size.
+ uint64_t dsize; // Data segment size.
+ uint64_t ssize; // Stack segment size.
+ uint64_t start_code; // VM address of text.
+ uint64_t start_stack; // VM address of stack bottom (top in rsp).
+ int64_t signal; // Signal causing core dump.
+ int32_t reserved; // Unused.
+ int32_t pad1;
+ uint64_t ar0; // Location of GPR's.
+ FXSAVE *fpstate; // Location of FPR's.
+ uint64_t magic; // Identifier for core dumps.
+ char u_comm[32]; // Command causing core dump.
+ DBG dbg; // Debug registers.
+ uint64_t error_code; // CPU error code.
+ uint64_t fault_address; // Control register CR3.
+};
+
+#define DR_OFFSET(reg_index) \
+ (LLVM_EXTENSION offsetof(UserArea, dbg) + \
+ LLVM_EXTENSION offsetof(DBG, dr[reg_index]))
+
+// Include RegisterInfos_x86_64 to declare our g_register_infos_x86_64
+// structure.
+#define DECLARE_REGISTER_INFOS_X86_64_STRUCT
+#include "RegisterInfos_x86_64.h"
+#undef DECLARE_REGISTER_INFOS_X86_64_STRUCT
+
+static std::vector<lldb_private::RegisterInfo> &GetPrivateRegisterInfoVector() {
+ static std::vector<lldb_private::RegisterInfo> g_register_infos;
+ return g_register_infos;
+}
+
+static const RegisterInfo *
+GetRegisterInfo_i386(const lldb_private::ArchSpec &arch) {
+ std::vector<lldb_private::RegisterInfo> &g_register_infos =
+ GetPrivateRegisterInfoVector();
+
+ // Allocate RegisterInfo only once
+ if (g_register_infos.empty()) {
+ // Copy the register information from base class
+ std::unique_ptr<RegisterContextLinux_i386> reg_interface(
+ new RegisterContextLinux_i386(arch));
+ const RegisterInfo *base_info = reg_interface->GetRegisterInfo();
+ g_register_infos.insert(g_register_infos.end(), &base_info[0],
+ &base_info[k_num_registers_i386]);
+
+// Include RegisterInfos_x86_64 to update the g_register_infos structure
+// with x86_64 offsets.
+#define UPDATE_REGISTER_INFOS_I386_STRUCT_WITH_X86_64_OFFSETS
+#include "RegisterInfos_x86_64.h"
+#undef UPDATE_REGISTER_INFOS_I386_STRUCT_WITH_X86_64_OFFSETS
+ }
+
+ return &g_register_infos[0];
+}
+
+static const RegisterInfo *GetRegisterInfoPtr(const ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::x86:
+ return GetRegisterInfo_i386(target_arch);
+ case llvm::Triple::x86_64:
+ return g_register_infos_x86_64;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+static uint32_t GetRegisterInfoCount(const ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::x86: {
+ assert(!GetPrivateRegisterInfoVector().empty() &&
+ "i386 register info not yet filled.");
+ return static_cast<uint32_t>(GetPrivateRegisterInfoVector().size());
+ }
+ case llvm::Triple::x86_64:
+ return static_cast<uint32_t>(sizeof(g_register_infos_x86_64) /
+ sizeof(g_register_infos_x86_64[0]));
+ default:
+ assert(false && "Unhandled target architecture.");
+ return 0;
+ }
+}
+
+static uint32_t GetUserRegisterInfoCount(const ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::x86:
+ return static_cast<uint32_t>(k_num_user_registers_i386);
+ case llvm::Triple::x86_64:
+ return static_cast<uint32_t>(k_num_user_registers_x86_64);
+ default:
+ assert(false && "Unhandled target architecture.");
+ return 0;
+ }
+}
+
+RegisterContextLinux_x86_64::RegisterContextLinux_x86_64(
+ const ArchSpec &target_arch)
+ : lldb_private::RegisterInfoInterface(target_arch),
+ m_register_info_p(GetRegisterInfoPtr(target_arch)),
+ m_register_info_count(GetRegisterInfoCount(target_arch)),
+ m_user_register_count(GetUserRegisterInfoCount(target_arch)) {
+ RegisterInfo orig_ax = {"orig_rax",
+ nullptr,
+ sizeof(((GPR *)nullptr)->orig_rax),
+ (LLVM_EXTENSION offsetof(GPR, orig_rax)),
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0};
+ d_register_infos.push_back(orig_ax);
+}
+
+size_t RegisterContextLinux_x86_64::GetGPRSize() const { return sizeof(GPR); }
+
+const std::vector<lldb_private::RegisterInfo> *
+RegisterContextLinux_x86_64::GetDynamicRegisterInfoP() const {
+ return &d_register_infos;
+}
+
+const RegisterInfo *RegisterContextLinux_x86_64::GetRegisterInfo() const {
+ return m_register_info_p;
+}
+
+uint32_t RegisterContextLinux_x86_64::GetRegisterCount() const {
+ return m_register_info_count;
+}
+
+uint32_t RegisterContextLinux_x86_64::GetUserRegisterCount() const {
+ return m_user_register_count;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.h
new file mode 100644
index 000000000000..02f273cb02c9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.h
@@ -0,0 +1,36 @@
+//===-- RegisterContextLinux_x86_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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextLinux_x86_64_H_
+#define liblldb_RegisterContextLinux_x86_64_H_
+
+#include "RegisterInfoInterface.h"
+
+class RegisterContextLinux_x86_64 : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterContextLinux_x86_64(const lldb_private::ArchSpec &target_arch);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+
+ uint32_t GetUserRegisterCount() const override;
+
+ const std::vector<lldb_private::RegisterInfo> *
+ GetDynamicRegisterInfoP() const override;
+
+private:
+ const lldb_private::RegisterInfo *m_register_info_p;
+ uint32_t m_register_info_count;
+ uint32_t m_user_register_count;
+ std::vector<lldb_private::RegisterInfo> d_register_infos;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp
new file mode 100644
index 000000000000..bc78c1d6160c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp
@@ -0,0 +1,160 @@
+//===-- RegisterContextMacOSXFrameBackchain.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 "RegisterContextMacOSXFrameBackchain.h"
+
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// RegisterContextMacOSXFrameBackchain constructor
+RegisterContextMacOSXFrameBackchain::RegisterContextMacOSXFrameBackchain(
+ Thread &thread, uint32_t concrete_frame_idx,
+ const UnwindMacOSXFrameBackchain::Cursor &cursor)
+ : RegisterContext(thread, concrete_frame_idx), m_cursor(cursor),
+ m_cursor_is_valid(true) {}
+
+// Destructor
+RegisterContextMacOSXFrameBackchain::~RegisterContextMacOSXFrameBackchain() {}
+
+void RegisterContextMacOSXFrameBackchain::InvalidateAllRegisters() {
+ m_cursor_is_valid = false;
+}
+
+size_t RegisterContextMacOSXFrameBackchain::GetRegisterCount() {
+ return m_thread.GetRegisterContext()->GetRegisterCount();
+}
+
+const RegisterInfo *
+RegisterContextMacOSXFrameBackchain::GetRegisterInfoAtIndex(size_t reg) {
+ return m_thread.GetRegisterContext()->GetRegisterInfoAtIndex(reg);
+}
+
+size_t RegisterContextMacOSXFrameBackchain::GetRegisterSetCount() {
+ return m_thread.GetRegisterContext()->GetRegisterSetCount();
+}
+
+const RegisterSet *
+RegisterContextMacOSXFrameBackchain::GetRegisterSet(size_t reg_set) {
+ return m_thread.GetRegisterContext()->GetRegisterSet(reg_set);
+}
+
+bool RegisterContextMacOSXFrameBackchain::ReadRegister(
+ const RegisterInfo *reg_info, RegisterValue &value) {
+ if (!m_cursor_is_valid)
+ return false;
+
+ uint64_t reg_value = LLDB_INVALID_ADDRESS;
+
+ switch (reg_info->kinds[eRegisterKindGeneric]) {
+ case LLDB_REGNUM_GENERIC_PC:
+ if (m_cursor.pc == LLDB_INVALID_ADDRESS)
+ return false;
+ reg_value = m_cursor.pc;
+ break;
+
+ case LLDB_REGNUM_GENERIC_FP:
+ if (m_cursor.fp == LLDB_INVALID_ADDRESS)
+ return false;
+ reg_value = m_cursor.fp;
+ break;
+
+ default:
+ return false;
+ }
+
+ switch (reg_info->encoding) {
+ case eEncodingInvalid:
+ case eEncodingVector:
+ break;
+
+ case eEncodingUint:
+ case eEncodingSint:
+ value.SetUInt(reg_value, reg_info->byte_size);
+ return true;
+
+ case eEncodingIEEE754:
+ switch (reg_info->byte_size) {
+ case sizeof(float):
+ if (sizeof(float) == sizeof(uint32_t)) {
+ value.SetUInt32(reg_value, RegisterValue::eTypeFloat);
+ return true;
+ } else if (sizeof(float) == sizeof(uint64_t)) {
+ value.SetUInt64(reg_value, RegisterValue::eTypeFloat);
+ return true;
+ }
+ break;
+
+ case sizeof(double):
+ if (sizeof(double) == sizeof(uint32_t)) {
+ value.SetUInt32(reg_value, RegisterValue::eTypeDouble);
+ return true;
+ } else if (sizeof(double) == sizeof(uint64_t)) {
+ value.SetUInt64(reg_value, RegisterValue::eTypeDouble);
+ return true;
+ }
+ break;
+
+// TOOD: need a better way to detect when "long double" types are
+// the same bytes size as "double"
+#if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) && \
+ !defined(_MSC_VER) && !defined(__mips__) && !defined(__powerpc__) && \
+ !defined(__ANDROID__)
+ case sizeof(long double):
+ if (sizeof(long double) == sizeof(uint32_t)) {
+ value.SetUInt32(reg_value, RegisterValue::eTypeLongDouble);
+ return true;
+ } else if (sizeof(long double) == sizeof(uint64_t)) {
+ value.SetUInt64(reg_value, RegisterValue::eTypeLongDouble);
+ return true;
+ }
+ break;
+#endif
+ }
+ break;
+ }
+ return false;
+}
+
+bool RegisterContextMacOSXFrameBackchain::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue &value) {
+ // Not supported yet. We could easily add support for this by remembering the
+ // address of each entry (it would need to be part of the cursor)
+ return false;
+}
+
+bool RegisterContextMacOSXFrameBackchain::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ // libunwind frames can't handle this it doesn't always have all register
+ // values. This call should only be called on frame zero anyway so there
+ // shouldn't be any problem
+ return false;
+}
+
+bool RegisterContextMacOSXFrameBackchain::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ // Since this class doesn't respond to "ReadAllRegisterValues()", it must not
+ // have been the one that saved all the register values. So we just let the
+ // thread's register context (the register context for frame zero) do the
+ // writing.
+ return m_thread.GetRegisterContext()->WriteAllRegisterValues(data_sp);
+}
+
+uint32_t
+RegisterContextMacOSXFrameBackchain::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ return m_thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber(
+ kind, num);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h
new file mode 100644
index 000000000000..36e5538daa8a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h
@@ -0,0 +1,56 @@
+//===-- RegisterContextMacOSXFrameBackchain.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_RegisterContextMacOSXFrameBackchain_h_
+#define lldb_RegisterContextMacOSXFrameBackchain_h_
+
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/lldb-private.h"
+
+#include "UnwindMacOSXFrameBackchain.h"
+
+class RegisterContextMacOSXFrameBackchain
+ : public lldb_private::RegisterContext {
+public:
+ RegisterContextMacOSXFrameBackchain(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ const UnwindMacOSXFrameBackchain::Cursor &cursor);
+
+ ~RegisterContextMacOSXFrameBackchain() override;
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t reg_set) override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+private:
+ UnwindMacOSXFrameBackchain::Cursor m_cursor;
+ bool m_cursor_is_valid;
+
+ DISALLOW_COPY_AND_ASSIGN(RegisterContextMacOSXFrameBackchain);
+};
+
+#endif // lldb_RegisterContextMacOSXFrameBackchain_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_arm.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_arm.cpp
new file mode 100644
index 000000000000..c7042ab5137a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_arm.cpp
@@ -0,0 +1,74 @@
+//===-- RegisterContextMach_arm.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
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__APPLE__)
+
+#include "RegisterContextMach_arm.h"
+
+#include <mach/mach_types.h>
+#include <mach/thread_act.h>
+
+
+using namespace lldb;
+using namespace lldb_private;
+
+RegisterContextMach_arm::RegisterContextMach_arm(Thread &thread,
+ uint32_t concrete_frame_idx)
+ : RegisterContextDarwin_arm(thread, concrete_frame_idx) {}
+
+RegisterContextMach_arm::~RegisterContextMach_arm() {}
+
+int RegisterContextMach_arm::DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) {
+ mach_msg_type_number_t count = GPRWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&gpr, &count);
+}
+
+int RegisterContextMach_arm::DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) {
+ mach_msg_type_number_t count = FPUWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&fpu, &count);
+}
+
+int RegisterContextMach_arm::DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) {
+ mach_msg_type_number_t count = EXCWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&exc, &count);
+}
+
+int RegisterContextMach_arm::DoReadDBG(lldb::tid_t tid, int flavor, DBG &dbg) {
+ mach_msg_type_number_t count = DBGWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&dbg, &count);
+}
+
+int RegisterContextMach_arm::DoWriteGPR(lldb::tid_t tid, int flavor,
+ const GPR &gpr) {
+ return ::thread_set_state(
+ tid, flavor, reinterpret_cast<thread_state_t>(const_cast<GPR *>(&gpr)),
+ GPRWordCount);
+}
+
+int RegisterContextMach_arm::DoWriteFPU(lldb::tid_t tid, int flavor,
+ const FPU &fpu) {
+ return ::thread_set_state(
+ tid, flavor, reinterpret_cast<thread_state_t>(const_cast<FPU *>(&fpu)),
+ FPUWordCount);
+}
+
+int RegisterContextMach_arm::DoWriteEXC(lldb::tid_t tid, int flavor,
+ const EXC &exc) {
+ return ::thread_set_state(
+ tid, flavor, reinterpret_cast<thread_state_t>(const_cast<EXC *>(&exc)),
+ EXCWordCount);
+}
+
+int RegisterContextMach_arm::DoWriteDBG(lldb::tid_t tid, int flavor,
+ const DBG &dbg) {
+ return ::thread_set_state(
+ tid, flavor, reinterpret_cast<thread_state_t>(const_cast<DBG *>(&dbg)),
+ DBGWordCount);
+}
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_arm.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_arm.h
new file mode 100644
index 000000000000..8b2425a193be
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_arm.h
@@ -0,0 +1,40 @@
+//===-- RegisterContextMach_arm.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 liblldb_RegisterContextMach_arm_h_
+#define liblldb_RegisterContextMach_arm_h_
+
+
+#include "RegisterContextDarwin_arm.h"
+
+class RegisterContextMach_arm : public RegisterContextDarwin_arm {
+public:
+ RegisterContextMach_arm(lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx);
+
+ virtual ~RegisterContextMach_arm();
+
+protected:
+ virtual int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr);
+
+ int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu);
+
+ int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc);
+
+ int DoReadDBG(lldb::tid_t tid, int flavor, DBG &dbg);
+
+ int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr);
+
+ int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu);
+
+ int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc);
+
+ int DoWriteDBG(lldb::tid_t tid, int flavor, const DBG &dbg);
+};
+
+#endif // liblldb_RegisterContextMach_arm_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_i386.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_i386.cpp
new file mode 100644
index 000000000000..e631ab9bb26c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_i386.cpp
@@ -0,0 +1,60 @@
+//===-- RegisterContextMach_i386.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
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__APPLE__)
+
+#include <mach/thread_act.h>
+
+#include "RegisterContextMach_i386.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+RegisterContextMach_i386::RegisterContextMach_i386(Thread &thread,
+ uint32_t concrete_frame_idx)
+ : RegisterContextDarwin_i386(thread, concrete_frame_idx) {}
+
+RegisterContextMach_i386::~RegisterContextMach_i386() {}
+
+int RegisterContextMach_i386::DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) {
+ mach_msg_type_number_t count = GPRWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&gpr, &count);
+}
+
+int RegisterContextMach_i386::DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) {
+ mach_msg_type_number_t count = FPUWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&fpu, &count);
+}
+
+int RegisterContextMach_i386::DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) {
+ mach_msg_type_number_t count = EXCWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&exc, &count);
+}
+
+int RegisterContextMach_i386::DoWriteGPR(lldb::tid_t tid, int flavor,
+ const GPR &gpr) {
+ return ::thread_set_state(
+ tid, flavor, reinterpret_cast<thread_state_t>(const_cast<GPR *>(&gpr)),
+ GPRWordCount);
+}
+
+int RegisterContextMach_i386::DoWriteFPU(lldb::tid_t tid, int flavor,
+ const FPU &fpu) {
+ return ::thread_set_state(
+ tid, flavor, reinterpret_cast<thread_state_t>(const_cast<FPU *>(&fpu)),
+ FPUWordCount);
+}
+
+int RegisterContextMach_i386::DoWriteEXC(lldb::tid_t tid, int flavor,
+ const EXC &exc) {
+ return ::thread_set_state(
+ tid, flavor, reinterpret_cast<thread_state_t>(const_cast<EXC *>(&exc)),
+ EXCWordCount);
+}
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_i386.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_i386.h
new file mode 100644
index 000000000000..b8835561e98c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_i386.h
@@ -0,0 +1,35 @@
+//===-- RegisterContextMach_i386.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 liblldb_RegisterContextMach_i386_h_
+#define liblldb_RegisterContextMach_i386_h_
+
+#include "RegisterContextDarwin_i386.h"
+
+class RegisterContextMach_i386 : public RegisterContextDarwin_i386 {
+public:
+ RegisterContextMach_i386(lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx);
+
+ virtual ~RegisterContextMach_i386();
+
+protected:
+ virtual int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr);
+
+ int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu);
+
+ int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc);
+
+ int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr);
+
+ int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu);
+
+ int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc);
+};
+
+#endif // liblldb_RegisterContextMach_i386_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_x86_64.cpp
new file mode 100644
index 000000000000..db17d7d88778
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_x86_64.cpp
@@ -0,0 +1,63 @@
+//===-- RegisterContextMach_x86_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
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__APPLE__)
+
+#include <mach/thread_act.h>
+
+#include "RegisterContextMach_x86_64.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+RegisterContextMach_x86_64::RegisterContextMach_x86_64(
+ Thread &thread, uint32_t concrete_frame_idx)
+ : RegisterContextDarwin_x86_64(thread, concrete_frame_idx) {}
+
+RegisterContextMach_x86_64::~RegisterContextMach_x86_64() {}
+
+int RegisterContextMach_x86_64::DoReadGPR(lldb::tid_t tid, int flavor,
+ GPR &gpr) {
+ mach_msg_type_number_t count = GPRWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&gpr, &count);
+}
+
+int RegisterContextMach_x86_64::DoReadFPU(lldb::tid_t tid, int flavor,
+ FPU &fpu) {
+ mach_msg_type_number_t count = FPUWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&fpu, &count);
+}
+
+int RegisterContextMach_x86_64::DoReadEXC(lldb::tid_t tid, int flavor,
+ EXC &exc) {
+ mach_msg_type_number_t count = EXCWordCount;
+ return ::thread_get_state(tid, flavor, (thread_state_t)&exc, &count);
+}
+
+int RegisterContextMach_x86_64::DoWriteGPR(lldb::tid_t tid, int flavor,
+ const GPR &gpr) {
+ return ::thread_set_state(
+ tid, flavor, reinterpret_cast<thread_state_t>(const_cast<GPR *>(&gpr)),
+ GPRWordCount);
+}
+
+int RegisterContextMach_x86_64::DoWriteFPU(lldb::tid_t tid, int flavor,
+ const FPU &fpu) {
+ return ::thread_set_state(
+ tid, flavor, reinterpret_cast<thread_state_t>(const_cast<FPU *>(&fpu)),
+ FPUWordCount);
+}
+
+int RegisterContextMach_x86_64::DoWriteEXC(lldb::tid_t tid, int flavor,
+ const EXC &exc) {
+ return ::thread_set_state(
+ tid, flavor, reinterpret_cast<thread_state_t>(const_cast<EXC *>(&exc)),
+ EXCWordCount);
+}
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_x86_64.h
new file mode 100644
index 000000000000..688009aef8af
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_x86_64.h
@@ -0,0 +1,36 @@
+//===-- RegisterContextMach_x86_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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextMach_x86_64_h_
+#define liblldb_RegisterContextMach_x86_64_h_
+
+#include "RegisterContextDarwin_x86_64.h"
+
+class RegisterContextMach_x86_64 : public RegisterContextDarwin_x86_64 {
+public:
+ RegisterContextMach_x86_64(lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx);
+
+ virtual ~RegisterContextMach_x86_64();
+
+protected:
+ virtual int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr);
+
+ int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu);
+
+ int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc);
+
+ int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr);
+
+ int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu);
+
+ int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc);
+};
+
+#endif // liblldb_RegisterContextMach_x86_64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMemory.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMemory.cpp
new file mode 100644
index 000000000000..946d4fa9f8e5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMemory.cpp
@@ -0,0 +1,139 @@
+//===-- RegisterContextMemory.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 "RegisterContextMemory.h"
+
+#include "DynamicRegisterInfo.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// RegisterContextMemory constructor
+RegisterContextMemory::RegisterContextMemory(Thread &thread,
+ uint32_t concrete_frame_idx,
+ DynamicRegisterInfo &reg_infos,
+ addr_t reg_data_addr)
+ : RegisterContext(thread, concrete_frame_idx), m_reg_infos(reg_infos),
+ m_reg_valid(), m_reg_data(), m_reg_data_addr(reg_data_addr) {
+ // Resize our vector of bools to contain one bool for every register. We will
+ // use these boolean values to know when a register value is valid in
+ // m_reg_data.
+ const size_t num_regs = reg_infos.GetNumRegisters();
+ assert(num_regs > 0);
+ m_reg_valid.resize(num_regs);
+
+ // Make a heap based buffer that is big enough to store all registers
+ DataBufferSP reg_data_sp(
+ new DataBufferHeap(reg_infos.GetRegisterDataByteSize(), 0));
+ m_reg_data.SetData(reg_data_sp);
+}
+
+// Destructor
+RegisterContextMemory::~RegisterContextMemory() {}
+
+void RegisterContextMemory::InvalidateAllRegisters() {
+ if (m_reg_data_addr != LLDB_INVALID_ADDRESS)
+ SetAllRegisterValid(false);
+}
+
+void RegisterContextMemory::SetAllRegisterValid(bool b) {
+ std::vector<bool>::iterator pos, end = m_reg_valid.end();
+ for (pos = m_reg_valid.begin(); pos != end; ++pos)
+ *pos = b;
+}
+
+size_t RegisterContextMemory::GetRegisterCount() {
+ return m_reg_infos.GetNumRegisters();
+}
+
+const RegisterInfo *RegisterContextMemory::GetRegisterInfoAtIndex(size_t reg) {
+ return m_reg_infos.GetRegisterInfoAtIndex(reg);
+}
+
+size_t RegisterContextMemory::GetRegisterSetCount() {
+ return m_reg_infos.GetNumRegisterSets();
+}
+
+const RegisterSet *RegisterContextMemory::GetRegisterSet(size_t reg_set) {
+ return m_reg_infos.GetRegisterSet(reg_set);
+}
+
+uint32_t RegisterContextMemory::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ return m_reg_infos.ConvertRegisterKindToRegisterNumber(kind, num);
+}
+
+bool RegisterContextMemory::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+ const uint32_t reg_num = reg_info->kinds[eRegisterKindLLDB];
+ if (!m_reg_valid[reg_num]) {
+ if (!ReadAllRegisterValues(m_reg_data.GetSharedDataBuffer()))
+ return false;
+ }
+ const bool partial_data_ok = false;
+ return reg_value
+ .SetValueFromData(reg_info, m_reg_data, reg_info->byte_offset,
+ partial_data_ok)
+ .Success();
+}
+
+bool RegisterContextMemory::WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &reg_value) {
+ if (m_reg_data_addr != LLDB_INVALID_ADDRESS) {
+ const uint32_t reg_num = reg_info->kinds[eRegisterKindLLDB];
+ addr_t reg_addr = m_reg_data_addr + reg_info->byte_offset;
+ Status error(WriteRegisterValueToMemory(reg_info, reg_addr,
+ reg_info->byte_size, reg_value));
+ m_reg_valid[reg_num] = false;
+ return error.Success();
+ }
+ return false;
+}
+
+bool RegisterContextMemory::ReadAllRegisterValues(DataBufferSP &data_sp) {
+ if (m_reg_data_addr != LLDB_INVALID_ADDRESS) {
+ ProcessSP process_sp(CalculateProcess());
+ if (process_sp) {
+ Status error;
+ if (process_sp->ReadMemory(m_reg_data_addr, data_sp->GetBytes(),
+ data_sp->GetByteSize(),
+ error) == data_sp->GetByteSize()) {
+ SetAllRegisterValid(true);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool RegisterContextMemory::WriteAllRegisterValues(
+ const DataBufferSP &data_sp) {
+ if (m_reg_data_addr != LLDB_INVALID_ADDRESS) {
+ ProcessSP process_sp(CalculateProcess());
+ if (process_sp) {
+ Status error;
+ SetAllRegisterValid(false);
+ if (process_sp->WriteMemory(m_reg_data_addr, data_sp->GetBytes(),
+ data_sp->GetByteSize(),
+ error) == data_sp->GetByteSize())
+ return true;
+ }
+ }
+ return false;
+}
+
+void RegisterContextMemory::SetAllRegisterData(
+ const lldb::DataBufferSP &data_sp) {
+ m_reg_data.SetData(data_sp);
+ SetAllRegisterValid(true);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMemory.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMemory.h
new file mode 100644
index 000000000000..68223eaeffd7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMemory.h
@@ -0,0 +1,73 @@
+//===-- RegisterContextMemory.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_RegisterContextMemory_h_
+#define lldb_RegisterContextMemory_h_
+
+#include <vector>
+
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/lldb-private.h"
+
+class DynamicRegisterInfo;
+
+class RegisterContextMemory : public lldb_private::RegisterContext {
+public:
+ RegisterContextMemory(lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx,
+ DynamicRegisterInfo &reg_info,
+ lldb::addr_t reg_data_addr);
+
+ ~RegisterContextMemory() override;
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t reg_set) override;
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+ // If all of the thread register are in a contiguous buffer in
+ // memory, then the default ReadRegister/WriteRegister and
+ // ReadAllRegisterValues/WriteAllRegisterValues will work. If thread
+ // registers are not contiguous, clients will want to subclass this
+ // class and modify the read/write functions as needed.
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &reg_value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &reg_value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ void SetAllRegisterData(const lldb::DataBufferSP &data_sp);
+
+protected:
+ void SetAllRegisterValid(bool b);
+
+ DynamicRegisterInfo &m_reg_infos;
+ std::vector<bool> m_reg_valid;
+ lldb_private::DataExtractor m_reg_data;
+ lldb::addr_t m_reg_data_addr; // If this is valid, then we have a register
+ // context that is stored in memmory
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(RegisterContextMemory);
+};
+
+#endif // lldb_RegisterContextMemory_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.cpp
new file mode 100644
index 000000000000..e620ff66c922
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.cpp
@@ -0,0 +1,123 @@
+//===-- RegisterContextNetBSD_x86_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
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextNetBSD_x86_64.h"
+#include "RegisterContextPOSIX_x86.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/Compiler.h"
+#include <cassert>
+#include <cstddef>
+
+using namespace lldb_private;
+using namespace lldb;
+
+// src/sys/arch/amd64/include/frame_regs.h
+typedef struct _GPR {
+ uint64_t rdi; /* 0 */
+ uint64_t rsi; /* 1 */
+ uint64_t rdx; /* 2 */
+ uint64_t rcx; /* 3 */
+ uint64_t r8; /* 4 */
+ uint64_t r9; /* 5 */
+ uint64_t r10; /* 6 */
+ uint64_t r11; /* 7 */
+ uint64_t r12; /* 8 */
+ uint64_t r13; /* 9 */
+ uint64_t r14; /* 10 */
+ uint64_t r15; /* 11 */
+ uint64_t rbp; /* 12 */
+ uint64_t rbx; /* 13 */
+ uint64_t rax; /* 14 */
+ uint64_t gs; /* 15 */
+ uint64_t fs; /* 16 */
+ uint64_t es; /* 17 */
+ uint64_t ds; /* 18 */
+ uint64_t trapno; /* 19 */
+ uint64_t err; /* 20 */
+ uint64_t rip; /* 21 */
+ uint64_t cs; /* 22 */
+ uint64_t rflags; /* 23 */
+ uint64_t rsp; /* 24 */
+ uint64_t ss; /* 25 */
+} GPR;
+
+struct DBG {
+ uint64_t dr[16]; /* debug registers */
+ /* Index 0-3: debug address registers */
+ /* Index 4-5: reserved */
+ /* Index 6: debug status */
+ /* Index 7: debug control */
+ /* Index 8-15: reserved */
+};
+
+/*
+ * src/sys/arch/amd64/include/mcontext.h
+ *
+ * typedef struct {
+ * __gregset_t __gregs;
+ * __greg_t _mc_tlsbase;
+ * __fpregset_t __fpregs;
+ * } mcontext_t;
+ */
+
+struct UserArea {
+ GPR gpr;
+ uint64_t mc_tlsbase;
+ FPR fpr;
+ DBG dbg;
+};
+
+#define DR_OFFSET(reg_index) \
+ (LLVM_EXTENSION offsetof(UserArea, dbg) + \
+ LLVM_EXTENSION offsetof(DBG, dr[reg_index]))
+
+
+// Include RegisterInfos_x86_64 to declare our g_register_infos_x86_64
+// structure.
+#define DECLARE_REGISTER_INFOS_X86_64_STRUCT
+#include "RegisterInfos_x86_64.h"
+#undef DECLARE_REGISTER_INFOS_X86_64_STRUCT
+
+static const RegisterInfo *
+PrivateGetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::x86_64:
+ return g_register_infos_x86_64;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+static uint32_t
+PrivateGetRegisterCount(const lldb_private::ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::x86_64:
+ return static_cast<uint32_t>(sizeof(g_register_infos_x86_64) /
+ sizeof(g_register_infos_x86_64[0]));
+ default:
+ assert(false && "Unhandled target architecture.");
+ return 0;
+ }
+}
+
+RegisterContextNetBSD_x86_64::RegisterContextNetBSD_x86_64(
+ const ArchSpec &target_arch)
+ : lldb_private::RegisterInfoInterface(target_arch),
+ m_register_info_p(PrivateGetRegisterInfoPtr(target_arch)),
+ m_register_count(PrivateGetRegisterCount(target_arch)) {}
+
+size_t RegisterContextNetBSD_x86_64::GetGPRSize() const { return sizeof(GPR); }
+
+const RegisterInfo *RegisterContextNetBSD_x86_64::GetRegisterInfo() const {
+ return m_register_info_p;
+}
+
+uint32_t RegisterContextNetBSD_x86_64::GetRegisterCount() const {
+ return m_register_count;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.h
new file mode 100644
index 000000000000..4820ef8d17ba
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.h
@@ -0,0 +1,30 @@
+//===-- RegisterContextNetBSD_x86_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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextNetBSD_x86_64_H_
+#define liblldb_RegisterContextNetBSD_x86_64_H_
+
+#include "RegisterInfoInterface.h"
+
+class RegisterContextNetBSD_x86_64
+ : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterContextNetBSD_x86_64(const lldb_private::ArchSpec &target_arch);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+
+private:
+ const lldb_private::RegisterInfo *m_register_info_p;
+ const uint32_t m_register_count;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_i386.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_i386.cpp
new file mode 100644
index 000000000000..06eac6f7f991
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_i386.cpp
@@ -0,0 +1,77 @@
+//===-- RegisterContextOpenBSD_i386.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 "RegisterContextOpenBSD_i386.h"
+#include "RegisterContextPOSIX_x86.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+// /usr/include/machine/reg.h
+struct GPR {
+ uint32_t eax;
+ uint32_t ecx;
+ uint32_t edx;
+ uint32_t ebx;
+ uint32_t esp;
+ uint32_t ebp;
+ uint32_t esi;
+ uint32_t edi;
+ uint32_t eip;
+ uint32_t eflags;
+ uint32_t cs;
+ uint32_t ss;
+ uint32_t ds;
+ uint32_t es;
+ uint32_t fs;
+ uint32_t gs;
+};
+
+struct dbreg {
+ uint32_t dr[8]; /* debug registers */
+ /* Index 0-3: debug address registers */
+ /* Index 4-5: reserved */
+ /* Index 6: debug status */
+ /* Index 7: debug control */
+};
+
+using FPR_i386 = FXSAVE;
+
+struct UserArea {
+ GPR gpr;
+ FPR_i386 i387;
+};
+
+#define DR_SIZE sizeof(uint32_t)
+#define DR_OFFSET(reg_index) (LLVM_EXTENSION offsetof(dbreg, dr[reg_index]))
+
+// Include RegisterInfos_i386 to declare our g_register_infos_i386 structure.
+#define DECLARE_REGISTER_INFOS_I386_STRUCT
+#include "RegisterInfos_i386.h"
+#undef DECLARE_REGISTER_INFOS_I386_STRUCT
+
+RegisterContextOpenBSD_i386::RegisterContextOpenBSD_i386(
+ const ArchSpec &target_arch)
+ : RegisterInfoInterface(target_arch) {}
+
+size_t RegisterContextOpenBSD_i386::GetGPRSize() const { return sizeof(GPR); }
+
+const RegisterInfo *RegisterContextOpenBSD_i386::GetRegisterInfo() const {
+ switch (m_target_arch.GetMachine()) {
+ case llvm::Triple::x86:
+ return g_register_infos_i386;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+uint32_t RegisterContextOpenBSD_i386::GetRegisterCount() const {
+ return static_cast<uint32_t>(sizeof(g_register_infos_i386) /
+ sizeof(g_register_infos_i386[0]));
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_i386.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_i386.h
new file mode 100644
index 000000000000..992ce0959fdf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_i386.h
@@ -0,0 +1,25 @@
+//===-- RegisterContextOpenBSD_i386.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 liblldb_RegisterContextOpenBSD_i386_H_
+#define liblldb_RegisterContextOpenBSD_i386_H_
+
+#include "RegisterInfoInterface.h"
+
+class RegisterContextOpenBSD_i386 : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterContextOpenBSD_i386(const lldb_private::ArchSpec &target_arch);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.cpp
new file mode 100644
index 000000000000..e210196d921d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.cpp
@@ -0,0 +1,104 @@
+//===-- RegisterContextOpenBSD_x86_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
+//
+//===---------------------------------------------------------------------===//
+
+#include "RegisterContextOpenBSD_x86_64.h"
+#include "RegisterContextPOSIX_x86.h"
+#include <vector>
+
+using namespace lldb_private;
+using namespace lldb;
+
+// /usr/include/machine/reg.h
+typedef struct _GPR {
+ uint64_t rdi;
+ uint64_t rsi;
+ uint64_t rdx;
+ uint64_t rcx;
+ 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 rbp;
+ uint64_t rbx;
+ uint64_t rax;
+ uint64_t rsp;
+ uint64_t rip;
+ uint64_t rflags;
+ uint64_t cs;
+ uint64_t ss;
+ uint64_t ds;
+ uint64_t es;
+ uint64_t fs;
+ uint64_t gs;
+} GPR;
+
+struct DBG {
+ uint64_t dr[16]; /* debug registers */
+ /* Index 0-3: debug address registers */
+ /* Index 4-5: reserved */
+ /* Index 6: debug status */
+ /* Index 7: debug control */
+ /* Index 8-15: reserved */
+};
+
+struct UserArea {
+ GPR gpr;
+ FPR fpr;
+ DBG dbg;
+};
+
+#define DR_OFFSET(reg_index) (LLVM_EXTENSION offsetof(DBG, dr[reg_index]))
+
+// Include RegisterInfos_x86_64 to declare our g_register_infos_x86_64
+// structure.
+#define DECLARE_REGISTER_INFOS_X86_64_STRUCT
+#include "RegisterInfos_x86_64.h"
+#undef DECLARE_REGISTER_INFOS_X86_64_STRUCT
+
+static const RegisterInfo *
+PrivateGetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::x86_64:
+ return g_register_infos_x86_64;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+static uint32_t
+PrivateGetRegisterCount(const lldb_private::ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::x86_64:
+ return static_cast<uint32_t>(sizeof(g_register_infos_x86_64) /
+ sizeof(g_register_infos_x86_64[0]));
+ default:
+ assert(false && "Unhandled target architecture.");
+ return 0;
+ }
+}
+
+RegisterContextOpenBSD_x86_64::RegisterContextOpenBSD_x86_64(
+ const ArchSpec &target_arch)
+ : lldb_private::RegisterInfoInterface(target_arch),
+ m_register_info_p(PrivateGetRegisterInfoPtr(target_arch)),
+ m_register_count(PrivateGetRegisterCount(target_arch)) {}
+
+size_t RegisterContextOpenBSD_x86_64::GetGPRSize() const { return sizeof(GPR); }
+
+const RegisterInfo *RegisterContextOpenBSD_x86_64::GetRegisterInfo() const {
+ return m_register_info_p;
+}
+
+uint32_t RegisterContextOpenBSD_x86_64::GetRegisterCount() const {
+ return m_register_count;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.h
new file mode 100644
index 000000000000..9c76e7211132
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.h
@@ -0,0 +1,30 @@
+//===-- RegisterContextOpenBSD_x86_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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextOpenBSD_x86_64_H_
+#define liblldb_RegisterContextOpenBSD_x86_64_H_
+
+#include "RegisterInfoInterface.h"
+
+class RegisterContextOpenBSD_x86_64
+ : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterContextOpenBSD_x86_64(const lldb_private::ArchSpec &target_arch);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+
+private:
+ const lldb_private::RegisterInfo *m_register_info_p;
+ const uint32_t m_register_count;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.cpp
new file mode 100644
index 000000000000..821e2aa73b5b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.cpp
@@ -0,0 +1,212 @@
+//===-- RegisterContextPOSIX_arm.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 <cstring>
+#include <errno.h>
+#include <stdint.h>
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/Support/Compiler.h"
+
+#include "RegisterContextPOSIX_arm.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// arm general purpose registers.
+const uint32_t g_gpr_regnums_arm[] = {
+ gpr_r0_arm, gpr_r1_arm, gpr_r2_arm, gpr_r3_arm, gpr_r4_arm,
+ gpr_r5_arm, gpr_r6_arm, gpr_r7_arm, gpr_r8_arm, gpr_r9_arm,
+ gpr_r10_arm, gpr_r11_arm, gpr_r12_arm, gpr_sp_arm, gpr_lr_arm,
+ gpr_pc_arm, gpr_cpsr_arm,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+
+};
+static_assert(((sizeof g_gpr_regnums_arm / sizeof g_gpr_regnums_arm[0]) - 1) ==
+ k_num_gpr_registers_arm,
+ "g_gpr_regnums_arm has wrong number of register infos");
+
+// arm floating point registers.
+static const uint32_t g_fpu_regnums_arm[] = {
+ fpu_s0_arm, fpu_s1_arm, fpu_s2_arm, fpu_s3_arm, fpu_s4_arm,
+ fpu_s5_arm, fpu_s6_arm, fpu_s7_arm, fpu_s8_arm, fpu_s9_arm,
+ fpu_s10_arm, fpu_s11_arm, fpu_s12_arm, fpu_s13_arm, fpu_s14_arm,
+ fpu_s15_arm, fpu_s16_arm, fpu_s17_arm, fpu_s18_arm, fpu_s19_arm,
+ fpu_s20_arm, fpu_s21_arm, fpu_s22_arm, fpu_s23_arm, fpu_s24_arm,
+ fpu_s25_arm, fpu_s26_arm, fpu_s27_arm, fpu_s28_arm, fpu_s29_arm,
+ fpu_s30_arm, fpu_s31_arm, fpu_fpscr_arm, fpu_d0_arm, fpu_d1_arm,
+ fpu_d2_arm, fpu_d3_arm, fpu_d4_arm, fpu_d5_arm, fpu_d6_arm,
+ fpu_d7_arm, fpu_d8_arm, fpu_d9_arm, fpu_d10_arm, fpu_d11_arm,
+ fpu_d12_arm, fpu_d13_arm, fpu_d14_arm, fpu_d15_arm, fpu_d16_arm,
+ fpu_d17_arm, fpu_d18_arm, fpu_d19_arm, fpu_d20_arm, fpu_d21_arm,
+ fpu_d22_arm, fpu_d23_arm, fpu_d24_arm, fpu_d25_arm, fpu_d26_arm,
+ fpu_d27_arm, fpu_d28_arm, fpu_d29_arm, fpu_d30_arm, fpu_d31_arm,
+ fpu_q0_arm, fpu_q1_arm, fpu_q2_arm, fpu_q3_arm, fpu_q4_arm,
+ fpu_q5_arm, fpu_q6_arm, fpu_q7_arm, fpu_q8_arm, fpu_q9_arm,
+ fpu_q10_arm, fpu_q11_arm, fpu_q12_arm, fpu_q13_arm, fpu_q14_arm,
+ fpu_q15_arm,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+
+};
+static_assert(((sizeof g_fpu_regnums_arm / sizeof g_fpu_regnums_arm[0]) - 1) ==
+ k_num_fpr_registers_arm,
+ "g_fpu_regnums_arm has wrong number of register infos");
+
+// Number of register sets provided by this context.
+enum { k_num_register_sets = 2 };
+
+// Register sets for arm.
+static const lldb_private::RegisterSet g_reg_sets_arm[k_num_register_sets] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_registers_arm,
+ g_gpr_regnums_arm},
+ {"Floating Point Registers", "fpu", k_num_fpr_registers_arm,
+ g_fpu_regnums_arm}};
+
+bool RegisterContextPOSIX_arm::IsGPR(unsigned reg) {
+ return reg <= m_reg_info.last_gpr; // GPR's come first.
+}
+
+bool RegisterContextPOSIX_arm::IsFPR(unsigned reg) {
+ return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr);
+}
+
+RegisterContextPOSIX_arm::RegisterContextPOSIX_arm(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info)
+ : lldb_private::RegisterContext(thread, concrete_frame_idx) {
+ m_register_info_up.reset(register_info);
+
+ switch (register_info->m_target_arch.GetMachine()) {
+ case llvm::Triple::arm:
+ m_reg_info.num_registers = k_num_registers_arm;
+ m_reg_info.num_gpr_registers = k_num_gpr_registers_arm;
+ m_reg_info.num_fpr_registers = k_num_fpr_registers_arm;
+ m_reg_info.last_gpr = k_last_gpr_arm;
+ m_reg_info.first_fpr = k_first_fpr_arm;
+ m_reg_info.last_fpr = k_last_fpr_arm;
+ m_reg_info.first_fpr_v = fpu_s0_arm;
+ m_reg_info.last_fpr_v = fpu_s31_arm;
+ m_reg_info.gpr_flags = gpr_cpsr_arm;
+ break;
+ default:
+ assert(false && "Unhandled target architecture.");
+ break;
+ }
+
+ ::memset(&m_fpr, 0, sizeof m_fpr);
+}
+
+RegisterContextPOSIX_arm::~RegisterContextPOSIX_arm() {}
+
+void RegisterContextPOSIX_arm::Invalidate() {}
+
+void RegisterContextPOSIX_arm::InvalidateAllRegisters() {}
+
+unsigned RegisterContextPOSIX_arm::GetRegisterOffset(unsigned reg) {
+ assert(reg < m_reg_info.num_registers && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_offset;
+}
+
+unsigned RegisterContextPOSIX_arm::GetRegisterSize(unsigned reg) {
+ assert(reg < m_reg_info.num_registers && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_size;
+}
+
+size_t RegisterContextPOSIX_arm::GetRegisterCount() {
+ size_t num_registers =
+ m_reg_info.num_gpr_registers + m_reg_info.num_fpr_registers;
+ return num_registers;
+}
+
+size_t RegisterContextPOSIX_arm::GetGPRSize() {
+ return m_register_info_up->GetGPRSize();
+}
+
+const lldb_private::RegisterInfo *RegisterContextPOSIX_arm::GetRegisterInfo() {
+ // Commonly, this method is overridden and g_register_infos is copied and
+ // specialized. So, use GetRegisterInfo() rather than g_register_infos in
+ // this scope.
+ return m_register_info_up->GetRegisterInfo();
+}
+
+const lldb_private::RegisterInfo *
+RegisterContextPOSIX_arm::GetRegisterInfoAtIndex(size_t reg) {
+ if (reg < m_reg_info.num_registers)
+ return &GetRegisterInfo()[reg];
+ else
+ return nullptr;
+}
+
+size_t RegisterContextPOSIX_arm::GetRegisterSetCount() {
+ size_t sets = 0;
+ for (size_t set = 0; set < k_num_register_sets; ++set) {
+ if (IsRegisterSetAvailable(set))
+ ++sets;
+ }
+
+ return sets;
+}
+
+const lldb_private::RegisterSet *
+RegisterContextPOSIX_arm::GetRegisterSet(size_t set) {
+ if (IsRegisterSetAvailable(set)) {
+ switch (m_register_info_up->m_target_arch.GetMachine()) {
+ case llvm::Triple::arm:
+ return &g_reg_sets_arm[set];
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+ }
+ return nullptr;
+}
+
+const char *RegisterContextPOSIX_arm::GetRegisterName(unsigned reg) {
+ assert(reg < m_reg_info.num_registers && "Invalid register offset.");
+ return GetRegisterInfo()[reg].name;
+}
+
+lldb::ByteOrder RegisterContextPOSIX_arm::GetByteOrder() {
+ // Get the target process whose privileged thread was used for the register
+ // read.
+ lldb::ByteOrder byte_order = lldb::eByteOrderInvalid;
+ lldb_private::Process *process = CalculateProcess().get();
+
+ if (process)
+ byte_order = process->GetByteOrder();
+ return byte_order;
+}
+
+bool RegisterContextPOSIX_arm::IsRegisterSetAvailable(size_t set_index) {
+ return set_index < k_num_register_sets;
+}
+
+// Used when parsing DWARF and EH frame information and any other object file
+// sections that contain register numbers in them.
+uint32_t RegisterContextPOSIX_arm::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ const uint32_t num_regs = GetRegisterCount();
+
+ assert(kind < lldb::kNumRegisterKinds);
+ for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
+ const lldb_private::RegisterInfo *reg_info =
+ GetRegisterInfoAtIndex(reg_idx);
+
+ if (reg_info->kinds[kind] == num)
+ return reg_idx;
+ }
+
+ return LLDB_INVALID_REGNUM;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.h
new file mode 100644
index 000000000000..603ba76430e6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.h
@@ -0,0 +1,106 @@
+//===-- RegisterContextPOSIX_arm.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 liblldb_RegisterContextPOSIX_arm_h_
+#define liblldb_RegisterContextPOSIX_arm_h_
+
+#include "RegisterInfoInterface.h"
+#include "lldb-arm-register-enums.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/Log.h"
+
+class ProcessMonitor;
+
+class RegisterContextPOSIX_arm : public lldb_private::RegisterContext {
+public:
+ RegisterContextPOSIX_arm(lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info);
+
+ ~RegisterContextPOSIX_arm() override;
+
+ void Invalidate();
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ virtual size_t GetGPRSize();
+
+ virtual unsigned GetRegisterSize(unsigned reg);
+
+ virtual unsigned GetRegisterOffset(unsigned reg);
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ const char *GetRegisterName(unsigned reg);
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+protected:
+ struct RegInfo {
+ uint32_t num_registers;
+ uint32_t num_gpr_registers;
+ uint32_t num_fpr_registers;
+
+ uint32_t last_gpr;
+ uint32_t first_fpr;
+ uint32_t last_fpr;
+
+ uint32_t first_fpr_v;
+ uint32_t last_fpr_v;
+
+ uint32_t gpr_flags;
+ };
+
+ struct QReg {
+ uint8_t bytes[16];
+ };
+
+ struct FPU {
+ union {
+ uint32_t s[32];
+ uint64_t d[32];
+ QReg q[16]; // the 128-bit NEON registers
+ } floats;
+ uint32_t fpscr;
+ };
+
+ uint32_t m_gpr_arm[lldb_private::k_num_gpr_registers_arm]; // 32-bit general
+ // purpose
+ // registers.
+ RegInfo m_reg_info;
+ struct RegisterContextPOSIX_arm::FPU
+ m_fpr; // floating-point registers including extended register sets.
+ std::unique_ptr<lldb_private::RegisterInfoInterface>
+ m_register_info_up; // Register Info Interface (FreeBSD or Linux)
+
+ // Determines if an extended register set is supported on the processor
+ // running the inferior process.
+ virtual bool IsRegisterSetAvailable(size_t set_index);
+
+ virtual const lldb_private::RegisterInfo *GetRegisterInfo();
+
+ bool IsGPR(unsigned reg);
+
+ bool IsFPR(unsigned reg);
+
+ lldb::ByteOrder GetByteOrder();
+
+ virtual bool ReadGPR() = 0;
+ virtual bool ReadFPR() = 0;
+ virtual bool WriteGPR() = 0;
+ virtual bool WriteFPR() = 0;
+};
+
+#endif // liblldb_RegisterContextPOSIX_arm_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.cpp
new file mode 100644
index 000000000000..99b897d441b5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.cpp
@@ -0,0 +1,232 @@
+//===-- RegisterContextPOSIX_arm64.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 <cstring>
+#include <errno.h>
+#include <stdint.h>
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/Support/Compiler.h"
+
+#include "RegisterContextPOSIX_arm64.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// ARM64 general purpose registers.
+const uint32_t g_gpr_regnums_arm64[] = {
+ gpr_x0_arm64, gpr_x1_arm64, gpr_x2_arm64, gpr_x3_arm64,
+ gpr_x4_arm64, gpr_x5_arm64, gpr_x6_arm64, gpr_x7_arm64,
+ gpr_x8_arm64, gpr_x9_arm64, gpr_x10_arm64, gpr_x11_arm64,
+ gpr_x12_arm64, gpr_x13_arm64, gpr_x14_arm64, gpr_x15_arm64,
+ gpr_x16_arm64, gpr_x17_arm64, gpr_x18_arm64, gpr_x19_arm64,
+ gpr_x20_arm64, gpr_x21_arm64, gpr_x22_arm64, gpr_x23_arm64,
+ gpr_x24_arm64, gpr_x25_arm64, gpr_x26_arm64, gpr_x27_arm64,
+ gpr_x28_arm64, gpr_fp_arm64, gpr_lr_arm64, gpr_sp_arm64,
+ gpr_pc_arm64, gpr_cpsr_arm64, gpr_w0_arm64, gpr_w1_arm64,
+ gpr_w2_arm64, gpr_w3_arm64, gpr_w4_arm64, gpr_w5_arm64,
+ gpr_w6_arm64, gpr_w7_arm64, gpr_w8_arm64, gpr_w9_arm64,
+ gpr_w10_arm64, gpr_w11_arm64, gpr_w12_arm64, gpr_w13_arm64,
+ gpr_w14_arm64, gpr_w15_arm64, gpr_w16_arm64, gpr_w17_arm64,
+ gpr_w18_arm64, gpr_w19_arm64, gpr_w20_arm64, gpr_w21_arm64,
+ gpr_w22_arm64, gpr_w23_arm64, gpr_w24_arm64, gpr_w25_arm64,
+ gpr_w26_arm64, gpr_w27_arm64, gpr_w28_arm64,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+static_assert(((sizeof g_gpr_regnums_arm64 / sizeof g_gpr_regnums_arm64[0]) -
+ 1) == k_num_gpr_registers_arm64,
+ "g_gpr_regnums_arm64 has wrong number of register infos");
+
+// ARM64 floating point registers.
+static const uint32_t g_fpu_regnums_arm64[] = {
+ fpu_v0_arm64, fpu_v1_arm64, fpu_v2_arm64, fpu_v3_arm64,
+ fpu_v4_arm64, fpu_v5_arm64, fpu_v6_arm64, fpu_v7_arm64,
+ fpu_v8_arm64, fpu_v9_arm64, fpu_v10_arm64, fpu_v11_arm64,
+ fpu_v12_arm64, fpu_v13_arm64, fpu_v14_arm64, fpu_v15_arm64,
+ fpu_v16_arm64, fpu_v17_arm64, fpu_v18_arm64, fpu_v19_arm64,
+ fpu_v20_arm64, fpu_v21_arm64, fpu_v22_arm64, fpu_v23_arm64,
+ fpu_v24_arm64, fpu_v25_arm64, fpu_v26_arm64, fpu_v27_arm64,
+ fpu_v28_arm64, fpu_v29_arm64, fpu_v30_arm64, fpu_v31_arm64,
+ fpu_s0_arm64, fpu_s1_arm64, fpu_s2_arm64, fpu_s3_arm64,
+ fpu_s4_arm64, fpu_s5_arm64, fpu_s6_arm64, fpu_s7_arm64,
+ fpu_s8_arm64, fpu_s9_arm64, fpu_s10_arm64, fpu_s11_arm64,
+ fpu_s12_arm64, fpu_s13_arm64, fpu_s14_arm64, fpu_s15_arm64,
+ fpu_s16_arm64, fpu_s17_arm64, fpu_s18_arm64, fpu_s19_arm64,
+ fpu_s20_arm64, fpu_s21_arm64, fpu_s22_arm64, fpu_s23_arm64,
+ fpu_s24_arm64, fpu_s25_arm64, fpu_s26_arm64, fpu_s27_arm64,
+ fpu_s28_arm64, fpu_s29_arm64, fpu_s30_arm64, fpu_s31_arm64,
+
+ fpu_d0_arm64, fpu_d1_arm64, fpu_d2_arm64, fpu_d3_arm64,
+ fpu_d4_arm64, fpu_d5_arm64, fpu_d6_arm64, fpu_d7_arm64,
+ fpu_d8_arm64, fpu_d9_arm64, fpu_d10_arm64, fpu_d11_arm64,
+ fpu_d12_arm64, fpu_d13_arm64, fpu_d14_arm64, fpu_d15_arm64,
+ fpu_d16_arm64, fpu_d17_arm64, fpu_d18_arm64, fpu_d19_arm64,
+ fpu_d20_arm64, fpu_d21_arm64, fpu_d22_arm64, fpu_d23_arm64,
+ fpu_d24_arm64, fpu_d25_arm64, fpu_d26_arm64, fpu_d27_arm64,
+ fpu_d28_arm64, fpu_d29_arm64, fpu_d30_arm64, fpu_d31_arm64,
+ fpu_fpsr_arm64, fpu_fpcr_arm64,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+static_assert(((sizeof g_fpu_regnums_arm64 / sizeof g_fpu_regnums_arm64[0]) -
+ 1) == k_num_fpr_registers_arm64,
+ "g_fpu_regnums_arm64 has wrong number of register infos");
+
+// Number of register sets provided by this context.
+enum { k_num_register_sets = 2 };
+
+// Register sets for ARM64.
+static const lldb_private::RegisterSet g_reg_sets_arm64[k_num_register_sets] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_registers_arm64,
+ g_gpr_regnums_arm64},
+ {"Floating Point Registers", "fpu", k_num_fpr_registers_arm64,
+ g_fpu_regnums_arm64}};
+
+bool RegisterContextPOSIX_arm64::IsGPR(unsigned reg) {
+ return reg <= m_reg_info.last_gpr; // GPR's come first.
+}
+
+bool RegisterContextPOSIX_arm64::IsFPR(unsigned reg) {
+ return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr);
+}
+
+RegisterContextPOSIX_arm64::RegisterContextPOSIX_arm64(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info)
+ : lldb_private::RegisterContext(thread, concrete_frame_idx) {
+ m_register_info_up.reset(register_info);
+
+ switch (register_info->m_target_arch.GetMachine()) {
+ case llvm::Triple::aarch64:
+ m_reg_info.num_registers = k_num_registers_arm64;
+ m_reg_info.num_gpr_registers = k_num_gpr_registers_arm64;
+ m_reg_info.num_fpr_registers = k_num_fpr_registers_arm64;
+ m_reg_info.last_gpr = k_last_gpr_arm64;
+ m_reg_info.first_fpr = k_first_fpr_arm64;
+ m_reg_info.last_fpr = k_last_fpr_arm64;
+ m_reg_info.first_fpr_v = fpu_v0_arm64;
+ m_reg_info.last_fpr_v = fpu_v31_arm64;
+ m_reg_info.gpr_flags = gpr_cpsr_arm64;
+ break;
+ default:
+ assert(false && "Unhandled target architecture.");
+ break;
+ }
+
+ ::memset(&m_fpr, 0, sizeof m_fpr);
+}
+
+RegisterContextPOSIX_arm64::~RegisterContextPOSIX_arm64() {}
+
+void RegisterContextPOSIX_arm64::Invalidate() {}
+
+void RegisterContextPOSIX_arm64::InvalidateAllRegisters() {}
+
+unsigned RegisterContextPOSIX_arm64::GetRegisterOffset(unsigned reg) {
+ assert(reg < m_reg_info.num_registers && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_offset;
+}
+
+unsigned RegisterContextPOSIX_arm64::GetRegisterSize(unsigned reg) {
+ assert(reg < m_reg_info.num_registers && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_size;
+}
+
+size_t RegisterContextPOSIX_arm64::GetRegisterCount() {
+ size_t num_registers =
+ m_reg_info.num_gpr_registers + m_reg_info.num_fpr_registers;
+ return num_registers;
+}
+
+size_t RegisterContextPOSIX_arm64::GetGPRSize() {
+ return m_register_info_up->GetGPRSize();
+}
+
+const lldb_private::RegisterInfo *
+RegisterContextPOSIX_arm64::GetRegisterInfo() {
+ // Commonly, this method is overridden and g_register_infos is copied and
+ // specialized. So, use GetRegisterInfo() rather than g_register_infos in
+ // this scope.
+ return m_register_info_up->GetRegisterInfo();
+}
+
+const lldb_private::RegisterInfo *
+RegisterContextPOSIX_arm64::GetRegisterInfoAtIndex(size_t reg) {
+ if (reg < m_reg_info.num_registers)
+ return &GetRegisterInfo()[reg];
+ else
+ return nullptr;
+}
+
+size_t RegisterContextPOSIX_arm64::GetRegisterSetCount() {
+ size_t sets = 0;
+ for (size_t set = 0; set < k_num_register_sets; ++set) {
+ if (IsRegisterSetAvailable(set))
+ ++sets;
+ }
+
+ return sets;
+}
+
+const lldb_private::RegisterSet *
+RegisterContextPOSIX_arm64::GetRegisterSet(size_t set) {
+ if (IsRegisterSetAvailable(set)) {
+ switch (m_register_info_up->m_target_arch.GetMachine()) {
+ case llvm::Triple::aarch64:
+ return &g_reg_sets_arm64[set];
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+ }
+ return nullptr;
+}
+
+const char *RegisterContextPOSIX_arm64::GetRegisterName(unsigned reg) {
+ assert(reg < m_reg_info.num_registers && "Invalid register offset.");
+ return GetRegisterInfo()[reg].name;
+}
+
+lldb::ByteOrder RegisterContextPOSIX_arm64::GetByteOrder() {
+ // Get the target process whose privileged thread was used for the register
+ // read.
+ lldb::ByteOrder byte_order = lldb::eByteOrderInvalid;
+ lldb_private::Process *process = CalculateProcess().get();
+
+ if (process)
+ byte_order = process->GetByteOrder();
+ return byte_order;
+}
+
+bool RegisterContextPOSIX_arm64::IsRegisterSetAvailable(size_t set_index) {
+ return set_index < k_num_register_sets;
+}
+
+// Used when parsing DWARF and EH frame information and any other object file
+// sections that contain register numbers in them.
+uint32_t RegisterContextPOSIX_arm64::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ const uint32_t num_regs = GetRegisterCount();
+
+ assert(kind < lldb::kNumRegisterKinds);
+ for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
+ const lldb_private::RegisterInfo *reg_info =
+ GetRegisterInfoAtIndex(reg_idx);
+
+ if (reg_info->kinds[kind] == num)
+ return reg_idx;
+ }
+
+ return LLDB_INVALID_REGNUM;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.h
new file mode 100644
index 000000000000..49a49b69da6b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.h
@@ -0,0 +1,106 @@
+//===-- RegisterContextPOSIX_arm64.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 liblldb_RegisterContextPOSIX_arm64_h_
+#define liblldb_RegisterContextPOSIX_arm64_h_
+
+#include "RegisterInfoInterface.h"
+#include "lldb-arm64-register-enums.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/Log.h"
+
+class ProcessMonitor;
+
+class RegisterContextPOSIX_arm64 : public lldb_private::RegisterContext {
+public:
+ RegisterContextPOSIX_arm64(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info);
+
+ ~RegisterContextPOSIX_arm64() override;
+
+ void Invalidate();
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ virtual size_t GetGPRSize();
+
+ virtual unsigned GetRegisterSize(unsigned reg);
+
+ virtual unsigned GetRegisterOffset(unsigned reg);
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ const char *GetRegisterName(unsigned reg);
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+protected:
+ struct RegInfo {
+ uint32_t num_registers;
+ uint32_t num_gpr_registers;
+ uint32_t num_fpr_registers;
+
+ uint32_t last_gpr;
+ uint32_t first_fpr;
+ uint32_t last_fpr;
+
+ uint32_t first_fpr_v;
+ uint32_t last_fpr_v;
+
+ uint32_t gpr_flags;
+ };
+
+ // based on RegisterContextDarwin_arm64.h
+ struct VReg {
+ uint8_t bytes[16];
+ };
+
+ // based on RegisterContextDarwin_arm64.h
+ struct FPU {
+ VReg v[32];
+ uint32_t fpsr;
+ uint32_t fpcr;
+ };
+
+ uint64_t m_gpr_arm64[lldb_private::k_num_gpr_registers_arm64]; // 64-bit
+ // general
+ // purpose
+ // registers.
+ RegInfo m_reg_info;
+ struct RegisterContextPOSIX_arm64::FPU
+ m_fpr; // floating-point registers including extended register sets.
+ std::unique_ptr<lldb_private::RegisterInfoInterface>
+ m_register_info_up; // Register Info Interface (FreeBSD or Linux)
+
+ // Determines if an extended register set is supported on the processor
+ // running the inferior process.
+ virtual bool IsRegisterSetAvailable(size_t set_index);
+
+ virtual const lldb_private::RegisterInfo *GetRegisterInfo();
+
+ bool IsGPR(unsigned reg);
+
+ bool IsFPR(unsigned reg);
+
+ lldb::ByteOrder GetByteOrder();
+
+ virtual bool ReadGPR() = 0;
+ virtual bool ReadFPR() = 0;
+ virtual bool WriteGPR() = 0;
+ virtual bool WriteFPR() = 0;
+};
+
+#endif // liblldb_RegisterContextPOSIX_arm64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.cpp
new file mode 100644
index 000000000000..f1fa3035b2ef
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.cpp
@@ -0,0 +1,184 @@
+//===-- RegisterContextPOSIX_mips64.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 <cstring>
+#include <errno.h>
+#include <stdint.h>
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/Support/Compiler.h"
+
+#include "RegisterContextPOSIX_mips64.h"
+#include "RegisterContextFreeBSD_mips64.h"
+#include "RegisterContextLinux_mips64.h"
+#include "RegisterContextLinux_mips.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+bool RegisterContextPOSIX_mips64::IsGPR(unsigned reg) {
+ return reg < m_registers_count[gpr_registers_count]; // GPR's come first.
+}
+
+bool RegisterContextPOSIX_mips64::IsFPR(unsigned reg) {
+ int set = GetRegisterSetCount();
+ if (set > 1)
+ return reg < (m_registers_count[fpr_registers_count]
+ + m_registers_count[gpr_registers_count]);
+ return false;
+}
+
+RegisterContextPOSIX_mips64::RegisterContextPOSIX_mips64(
+ Thread &thread, uint32_t concrete_frame_idx,
+ RegisterInfoInterface *register_info)
+ : RegisterContext(thread, concrete_frame_idx) {
+ m_register_info_up.reset(register_info);
+ m_num_registers = GetRegisterCount();
+ int set = GetRegisterSetCount();
+
+ const RegisterSet *reg_set_ptr;
+ for(int i = 0; i < set; ++i) {
+ reg_set_ptr = GetRegisterSet(i);
+ m_registers_count[i] = reg_set_ptr->num_registers;
+ }
+
+ assert(m_num_registers ==
+ static_cast<uint32_t>(m_registers_count[gpr_registers_count] +
+ m_registers_count[fpr_registers_count] +
+ m_registers_count[msa_registers_count]));
+}
+
+RegisterContextPOSIX_mips64::~RegisterContextPOSIX_mips64() {}
+
+void RegisterContextPOSIX_mips64::Invalidate() {}
+
+void RegisterContextPOSIX_mips64::InvalidateAllRegisters() {}
+
+unsigned RegisterContextPOSIX_mips64::GetRegisterOffset(unsigned reg) {
+ assert(reg < m_num_registers && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_offset;
+}
+
+unsigned RegisterContextPOSIX_mips64::GetRegisterSize(unsigned reg) {
+ assert(reg < m_num_registers && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_size;
+}
+
+size_t RegisterContextPOSIX_mips64::GetRegisterCount() {
+ return m_register_info_up->GetRegisterCount();
+}
+
+size_t RegisterContextPOSIX_mips64::GetGPRSize() {
+ return m_register_info_up->GetGPRSize();
+}
+
+const RegisterInfo *RegisterContextPOSIX_mips64::GetRegisterInfo() {
+ // Commonly, this method is overridden and g_register_infos is copied and
+ // specialized. So, use GetRegisterInfo() rather than g_register_infos in
+ // this scope.
+ return m_register_info_up->GetRegisterInfo();
+}
+
+const RegisterInfo *
+RegisterContextPOSIX_mips64::GetRegisterInfoAtIndex(size_t reg) {
+ if (reg < m_num_registers)
+ return &GetRegisterInfo()[reg];
+ else
+ return nullptr;
+}
+
+size_t RegisterContextPOSIX_mips64::GetRegisterSetCount() {
+ ArchSpec target_arch = m_register_info_up->GetTargetArchitecture();
+ switch (target_arch.GetTriple().getOS()) {
+ case llvm::Triple::Linux: {
+ if ((target_arch.GetMachine() == llvm::Triple::mipsel) ||
+ (target_arch.GetMachine() == llvm::Triple::mips)) {
+ const auto *context = static_cast<const RegisterContextLinux_mips *>(
+ m_register_info_up.get());
+ return context->GetRegisterSetCount();
+ }
+ const auto *context = static_cast<const RegisterContextLinux_mips64 *>(
+ m_register_info_up.get());
+ return context->GetRegisterSetCount();
+ }
+ default: {
+ const auto *context = static_cast<const RegisterContextFreeBSD_mips64 *>(
+ m_register_info_up.get());
+ return context->GetRegisterSetCount();
+ }
+
+ }
+}
+
+const RegisterSet *RegisterContextPOSIX_mips64::GetRegisterSet(size_t set) {
+ ArchSpec target_arch = m_register_info_up->GetTargetArchitecture();
+ switch (target_arch.GetTriple().getOS()) {
+ case llvm::Triple::Linux: {
+ if ((target_arch.GetMachine() == llvm::Triple::mipsel) ||
+ (target_arch.GetMachine() == llvm::Triple::mips)) {
+ const auto *context = static_cast<const RegisterContextLinux_mips *>(
+ m_register_info_up.get());
+ return context->GetRegisterSet(set);
+ }
+ const auto *context = static_cast<const RegisterContextLinux_mips64 *>(
+ m_register_info_up.get());
+ return context->GetRegisterSet(set);
+ }
+ default: {
+ const auto *context = static_cast<const RegisterContextFreeBSD_mips64 *>(
+ m_register_info_up.get());
+ return context->GetRegisterSet(set);
+ }
+ }
+}
+
+const char *RegisterContextPOSIX_mips64::GetRegisterName(unsigned reg) {
+ assert(reg < m_num_registers && "Invalid register offset.");
+ return GetRegisterInfo()[reg].name;
+}
+
+lldb::ByteOrder RegisterContextPOSIX_mips64::GetByteOrder() {
+ // Get the target process whose privileged thread was used for the register
+ // read.
+ lldb::ByteOrder byte_order = eByteOrderInvalid;
+ Process *process = CalculateProcess().get();
+
+ if (process)
+ byte_order = process->GetByteOrder();
+ return byte_order;
+}
+
+bool RegisterContextPOSIX_mips64::IsRegisterSetAvailable(size_t set_index) {
+ size_t num_sets = GetRegisterSetCount();
+
+ return (set_index < num_sets);
+}
+
+// Used when parsing DWARF and EH frame information and any other object file
+// sections that contain register numbers in them.
+uint32_t RegisterContextPOSIX_mips64::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ const uint32_t num_regs = m_num_registers;
+
+ assert(kind < kNumRegisterKinds);
+ for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_idx);
+
+ if (reg_info->kinds[kind] == num)
+ return reg_idx;
+ }
+
+ return LLDB_INVALID_REGNUM;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.h
new file mode 100644
index 000000000000..c507e14bd5b6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.h
@@ -0,0 +1,84 @@
+//===-- RegisterContextPOSIX_mips64.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 liblldb_RegisterContextPOSIX_mips64_h_
+#define liblldb_RegisterContextPOSIX_mips64_h_
+
+#include "RegisterContext_mips.h"
+#include "RegisterInfoInterface.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/Log.h"
+
+using namespace lldb_private;
+
+class ProcessMonitor;
+
+class RegisterContextPOSIX_mips64 : public lldb_private::RegisterContext {
+public:
+
+ enum Register_count{
+ gpr_registers_count = 0,
+ fpr_registers_count,
+ msa_registers_count,
+ register_set_count
+ };
+
+ RegisterContextPOSIX_mips64(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info);
+
+ ~RegisterContextPOSIX_mips64() override;
+
+ void Invalidate();
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ virtual size_t GetGPRSize();
+
+ virtual unsigned GetRegisterSize(unsigned reg);
+
+ virtual unsigned GetRegisterOffset(unsigned reg);
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ const char *GetRegisterName(unsigned reg);
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+protected:
+ uint32_t m_num_registers;
+ uint8_t m_registers_count[register_set_count];
+ std::unique_ptr<lldb_private::RegisterInfoInterface>
+ m_register_info_up; // Register Info Interface (FreeBSD or Linux)
+
+ // Determines if an extended register set is supported on the processor
+ // running the inferior process.
+ virtual bool IsRegisterSetAvailable(size_t set_index);
+
+ virtual const lldb_private::RegisterInfo *GetRegisterInfo();
+
+ bool IsGPR(unsigned reg);
+
+ bool IsFPR(unsigned reg);
+
+ lldb::ByteOrder GetByteOrder();
+
+ virtual bool ReadGPR() = 0;
+ virtual bool ReadFPR() = 0;
+ virtual bool WriteGPR() = 0;
+ virtual bool WriteFPR() = 0;
+};
+
+#endif // liblldb_RegisterContextPOSIX_mips64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.cpp
new file mode 100644
index 000000000000..a78e9ed37947
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.cpp
@@ -0,0 +1,193 @@
+//===-- RegisterContextPOSIX_powerpc.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 <cstring>
+#include <errno.h>
+#include <stdint.h>
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/Support/Compiler.h"
+
+#include "RegisterContextPOSIX_powerpc.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+static const uint32_t g_gpr_regnums[] = {
+ gpr_r0_powerpc, gpr_r1_powerpc, gpr_r2_powerpc, gpr_r3_powerpc,
+ gpr_r4_powerpc, gpr_r5_powerpc, gpr_r6_powerpc, gpr_r7_powerpc,
+ gpr_r8_powerpc, gpr_r9_powerpc, gpr_r10_powerpc, gpr_r11_powerpc,
+ gpr_r12_powerpc, gpr_r13_powerpc, gpr_r14_powerpc, gpr_r15_powerpc,
+ gpr_r16_powerpc, gpr_r17_powerpc, gpr_r18_powerpc, gpr_r19_powerpc,
+ gpr_r20_powerpc, gpr_r21_powerpc, gpr_r22_powerpc, gpr_r23_powerpc,
+ gpr_r24_powerpc, gpr_r25_powerpc, gpr_r26_powerpc, gpr_r27_powerpc,
+ gpr_r28_powerpc, gpr_r29_powerpc, gpr_r30_powerpc, gpr_r31_powerpc,
+ gpr_lr_powerpc, gpr_cr_powerpc, gpr_xer_powerpc, gpr_ctr_powerpc,
+ gpr_pc_powerpc,
+};
+
+static const uint32_t g_fpr_regnums[] = {
+ fpr_f0_powerpc, fpr_f1_powerpc, fpr_f2_powerpc, fpr_f3_powerpc,
+ fpr_f4_powerpc, fpr_f5_powerpc, fpr_f6_powerpc, fpr_f7_powerpc,
+ fpr_f8_powerpc, fpr_f9_powerpc, fpr_f10_powerpc, fpr_f11_powerpc,
+ fpr_f12_powerpc, fpr_f13_powerpc, fpr_f14_powerpc, fpr_f15_powerpc,
+ fpr_f16_powerpc, fpr_f17_powerpc, fpr_f18_powerpc, fpr_f19_powerpc,
+ fpr_f20_powerpc, fpr_f21_powerpc, fpr_f22_powerpc, fpr_f23_powerpc,
+ fpr_f24_powerpc, fpr_f25_powerpc, fpr_f26_powerpc, fpr_f27_powerpc,
+ fpr_f28_powerpc, fpr_f29_powerpc, fpr_f30_powerpc, fpr_f31_powerpc,
+ fpr_fpscr_powerpc,
+};
+
+static const uint32_t g_vmx_regnums[] = {
+ vmx_v0_powerpc, vmx_v1_powerpc, vmx_v2_powerpc, vmx_v3_powerpc,
+ vmx_v4_powerpc, vmx_v5_powerpc, vmx_v6_powerpc, vmx_v7_powerpc,
+ vmx_v8_powerpc, vmx_v9_powerpc, vmx_v10_powerpc, vmx_v11_powerpc,
+ vmx_v12_powerpc, vmx_v13_powerpc, vmx_v14_powerpc, vmx_v15_powerpc,
+ vmx_v16_powerpc, vmx_v17_powerpc, vmx_v18_powerpc, vmx_v19_powerpc,
+ vmx_v20_powerpc, vmx_v21_powerpc, vmx_v22_powerpc, vmx_v23_powerpc,
+ vmx_v24_powerpc, vmx_v25_powerpc, vmx_v26_powerpc, vmx_v27_powerpc,
+ vmx_v28_powerpc, vmx_v29_powerpc, vmx_v30_powerpc, vmx_v31_powerpc,
+ vmx_vrsave_powerpc, vmx_vscr_powerpc,
+};
+
+// Number of register sets provided by this context.
+enum { k_num_register_sets = 3 };
+
+static const RegisterSet g_reg_sets_powerpc[k_num_register_sets] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_registers_powerpc,
+ g_gpr_regnums},
+ {"Floating Point Registers", "fpr", k_num_fpr_registers_powerpc,
+ g_fpr_regnums},
+ {"Altivec/VMX Registers", "vmx", k_num_vmx_registers_powerpc,
+ g_vmx_regnums},
+};
+
+static_assert(k_first_gpr_powerpc == 0,
+ "GPRs must index starting at 0, or fix IsGPR()");
+bool RegisterContextPOSIX_powerpc::IsGPR(unsigned reg) {
+ return (reg <= k_last_gpr_powerpc); // GPR's come first.
+}
+
+bool RegisterContextPOSIX_powerpc::IsFPR(unsigned reg) {
+ return (reg >= k_first_fpr) && (reg <= k_last_fpr);
+}
+
+bool RegisterContextPOSIX_powerpc::IsVMX(unsigned reg) {
+ return (reg >= k_first_vmx) && (reg <= k_last_vmx);
+}
+
+RegisterContextPOSIX_powerpc::RegisterContextPOSIX_powerpc(
+ Thread &thread, uint32_t concrete_frame_idx,
+ RegisterInfoInterface *register_info)
+ : RegisterContext(thread, concrete_frame_idx) {
+ m_register_info_up.reset(register_info);
+}
+
+RegisterContextPOSIX_powerpc::~RegisterContextPOSIX_powerpc() {}
+
+void RegisterContextPOSIX_powerpc::Invalidate() {}
+
+void RegisterContextPOSIX_powerpc::InvalidateAllRegisters() {}
+
+unsigned RegisterContextPOSIX_powerpc::GetRegisterOffset(unsigned reg) {
+ assert(reg < k_num_registers_powerpc && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_offset;
+}
+
+unsigned RegisterContextPOSIX_powerpc::GetRegisterSize(unsigned reg) {
+ assert(reg < k_num_registers_powerpc && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_size;
+}
+
+size_t RegisterContextPOSIX_powerpc::GetRegisterCount() {
+ size_t num_registers = k_num_registers_powerpc;
+ return num_registers;
+}
+
+size_t RegisterContextPOSIX_powerpc::GetGPRSize() {
+ return m_register_info_up->GetGPRSize();
+}
+
+const RegisterInfo *RegisterContextPOSIX_powerpc::GetRegisterInfo() {
+ // Commonly, this method is overridden and g_register_infos is copied and
+ // specialized. So, use GetRegisterInfo() rather than g_register_infos in
+ // this scope.
+ return m_register_info_up->GetRegisterInfo();
+}
+
+const RegisterInfo *
+RegisterContextPOSIX_powerpc::GetRegisterInfoAtIndex(size_t reg) {
+ if (reg < k_num_registers_powerpc)
+ return &GetRegisterInfo()[reg];
+ else
+ return nullptr;
+}
+
+size_t RegisterContextPOSIX_powerpc::GetRegisterSetCount() {
+ size_t sets = 0;
+ for (size_t set = 0; set < k_num_register_sets; ++set) {
+ if (IsRegisterSetAvailable(set))
+ ++sets;
+ }
+
+ return sets;
+}
+
+const RegisterSet *RegisterContextPOSIX_powerpc::GetRegisterSet(size_t set) {
+ if (IsRegisterSetAvailable(set))
+ return &g_reg_sets_powerpc[set];
+ else
+ return nullptr;
+}
+
+const char *RegisterContextPOSIX_powerpc::GetRegisterName(unsigned reg) {
+ assert(reg < k_num_registers_powerpc && "Invalid register offset.");
+ return GetRegisterInfo()[reg].name;
+}
+
+lldb::ByteOrder RegisterContextPOSIX_powerpc::GetByteOrder() {
+ // Get the target process whose privileged thread was used for the register
+ // read.
+ lldb::ByteOrder byte_order = eByteOrderInvalid;
+ Process *process = CalculateProcess().get();
+
+ if (process)
+ byte_order = process->GetByteOrder();
+ return byte_order;
+}
+
+bool RegisterContextPOSIX_powerpc::IsRegisterSetAvailable(size_t set_index) {
+ size_t num_sets = k_num_register_sets;
+
+ return (set_index < num_sets);
+}
+
+// Used when parsing DWARF and EH frame information and any other object file
+// sections that contain register numbers in them.
+uint32_t RegisterContextPOSIX_powerpc::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ const uint32_t num_regs = GetRegisterCount();
+
+ assert(kind < kNumRegisterKinds);
+ for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_idx);
+
+ if (reg_info->kinds[kind] == num)
+ return reg_idx;
+ }
+
+ return LLDB_INVALID_REGNUM;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h
new file mode 100644
index 000000000000..1a21a717b22b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h
@@ -0,0 +1,202 @@
+//===-- RegisterContextPOSIX_powerpc.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 liblldb_RegisterContextPOSIX_powerpc_h_
+#define liblldb_RegisterContextPOSIX_powerpc_h_
+
+#include "RegisterContext_powerpc.h"
+#include "RegisterInfoInterface.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/Log.h"
+
+class ProcessMonitor;
+
+// Internal codes for all powerpc registers.
+enum {
+ k_first_gpr_powerpc,
+ gpr_r0_powerpc = k_first_gpr_powerpc,
+ gpr_r1_powerpc,
+ gpr_r2_powerpc,
+ gpr_r3_powerpc,
+ gpr_r4_powerpc,
+ gpr_r5_powerpc,
+ gpr_r6_powerpc,
+ gpr_r7_powerpc,
+ gpr_r8_powerpc,
+ gpr_r9_powerpc,
+ gpr_r10_powerpc,
+ gpr_r11_powerpc,
+ gpr_r12_powerpc,
+ gpr_r13_powerpc,
+ gpr_r14_powerpc,
+ gpr_r15_powerpc,
+ gpr_r16_powerpc,
+ gpr_r17_powerpc,
+ gpr_r18_powerpc,
+ gpr_r19_powerpc,
+ gpr_r20_powerpc,
+ gpr_r21_powerpc,
+ gpr_r22_powerpc,
+ gpr_r23_powerpc,
+ gpr_r24_powerpc,
+ gpr_r25_powerpc,
+ gpr_r26_powerpc,
+ gpr_r27_powerpc,
+ gpr_r28_powerpc,
+ gpr_r29_powerpc,
+ gpr_r30_powerpc,
+ gpr_r31_powerpc,
+ gpr_lr_powerpc,
+ gpr_cr_powerpc,
+ gpr_xer_powerpc,
+ gpr_ctr_powerpc,
+ gpr_pc_powerpc,
+ k_last_gpr_powerpc = gpr_pc_powerpc,
+
+ k_first_fpr,
+ fpr_f0_powerpc = k_first_fpr,
+ fpr_f1_powerpc,
+ fpr_f2_powerpc,
+ fpr_f3_powerpc,
+ fpr_f4_powerpc,
+ fpr_f5_powerpc,
+ fpr_f6_powerpc,
+ fpr_f7_powerpc,
+ fpr_f8_powerpc,
+ fpr_f9_powerpc,
+ fpr_f10_powerpc,
+ fpr_f11_powerpc,
+ fpr_f12_powerpc,
+ fpr_f13_powerpc,
+ fpr_f14_powerpc,
+ fpr_f15_powerpc,
+ fpr_f16_powerpc,
+ fpr_f17_powerpc,
+ fpr_f18_powerpc,
+ fpr_f19_powerpc,
+ fpr_f20_powerpc,
+ fpr_f21_powerpc,
+ fpr_f22_powerpc,
+ fpr_f23_powerpc,
+ fpr_f24_powerpc,
+ fpr_f25_powerpc,
+ fpr_f26_powerpc,
+ fpr_f27_powerpc,
+ fpr_f28_powerpc,
+ fpr_f29_powerpc,
+ fpr_f30_powerpc,
+ fpr_f31_powerpc,
+ fpr_fpscr_powerpc,
+ k_last_fpr = fpr_fpscr_powerpc,
+
+ k_first_vmx,
+ vmx_v0_powerpc = k_first_vmx,
+ vmx_v1_powerpc,
+ vmx_v2_powerpc,
+ vmx_v3_powerpc,
+ vmx_v4_powerpc,
+ vmx_v5_powerpc,
+ vmx_v6_powerpc,
+ vmx_v7_powerpc,
+ vmx_v8_powerpc,
+ vmx_v9_powerpc,
+ vmx_v10_powerpc,
+ vmx_v11_powerpc,
+ vmx_v12_powerpc,
+ vmx_v13_powerpc,
+ vmx_v14_powerpc,
+ vmx_v15_powerpc,
+ vmx_v16_powerpc,
+ vmx_v17_powerpc,
+ vmx_v18_powerpc,
+ vmx_v19_powerpc,
+ vmx_v20_powerpc,
+ vmx_v21_powerpc,
+ vmx_v22_powerpc,
+ vmx_v23_powerpc,
+ vmx_v24_powerpc,
+ vmx_v25_powerpc,
+ vmx_v26_powerpc,
+ vmx_v27_powerpc,
+ vmx_v28_powerpc,
+ vmx_v29_powerpc,
+ vmx_v30_powerpc,
+ vmx_v31_powerpc,
+ vmx_vrsave_powerpc,
+ vmx_vscr_powerpc,
+ k_last_vmx = vmx_vscr_powerpc,
+
+ k_num_registers_powerpc,
+ k_num_gpr_registers_powerpc = k_last_gpr_powerpc - k_first_gpr_powerpc + 1,
+ k_num_fpr_registers_powerpc = k_last_fpr - k_first_fpr + 1,
+ k_num_vmx_registers_powerpc = k_last_vmx - k_first_vmx + 1,
+};
+
+class RegisterContextPOSIX_powerpc : public lldb_private::RegisterContext {
+public:
+ RegisterContextPOSIX_powerpc(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info);
+
+ ~RegisterContextPOSIX_powerpc() override;
+
+ void Invalidate();
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ virtual size_t GetGPRSize();
+
+ virtual unsigned GetRegisterSize(unsigned reg);
+
+ virtual unsigned GetRegisterOffset(unsigned reg);
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ const char *GetRegisterName(unsigned reg);
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+protected:
+ uint64_t
+ m_gpr_powerpc[k_num_gpr_registers_powerpc]; // general purpose registers.
+ uint64_t
+ m_fpr_powerpc[k_num_fpr_registers_powerpc]; // floating point registers.
+ uint32_t m_vmx_powerpc[k_num_vmx_registers_powerpc][4];
+ std::unique_ptr<lldb_private::RegisterInfoInterface>
+ m_register_info_up; // Register Info Interface (FreeBSD or Linux)
+
+ // Determines if an extended register set is supported on the processor
+ // running the inferior process.
+ virtual bool IsRegisterSetAvailable(size_t set_index);
+
+ virtual const lldb_private::RegisterInfo *GetRegisterInfo();
+
+ bool IsGPR(unsigned reg);
+
+ bool IsFPR(unsigned reg);
+
+ bool IsVMX(unsigned reg);
+
+ lldb::ByteOrder GetByteOrder();
+
+ virtual bool ReadGPR() = 0;
+ virtual bool ReadFPR() = 0;
+ virtual bool ReadVMX() = 0;
+ virtual bool WriteGPR() = 0;
+ virtual bool WriteFPR() = 0;
+ virtual bool WriteVMX() = 0;
+};
+
+#endif // liblldb_RegisterContextPOSIX_powerpc_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.cpp
new file mode 100644
index 000000000000..02546c0ed16f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.cpp
@@ -0,0 +1,211 @@
+//===-- RegisterContextPOSIX_ppc64le.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 <cstring>
+#include <errno.h>
+#include <stdint.h>
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/Support/Compiler.h"
+
+#include "RegisterContextPOSIX_ppc64le.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+static const uint32_t g_gpr_regnums[] = {
+ gpr_r0_ppc64le, gpr_r1_ppc64le, gpr_r2_ppc64le, gpr_r3_ppc64le,
+ gpr_r4_ppc64le, gpr_r5_ppc64le, gpr_r6_ppc64le, gpr_r7_ppc64le,
+ gpr_r8_ppc64le, gpr_r9_ppc64le, gpr_r10_ppc64le, gpr_r11_ppc64le,
+ gpr_r12_ppc64le, gpr_r13_ppc64le, gpr_r14_ppc64le, gpr_r15_ppc64le,
+ gpr_r16_ppc64le, gpr_r17_ppc64le, gpr_r18_ppc64le, gpr_r19_ppc64le,
+ gpr_r20_ppc64le, gpr_r21_ppc64le, gpr_r22_ppc64le, gpr_r23_ppc64le,
+ gpr_r24_ppc64le, gpr_r25_ppc64le, gpr_r26_ppc64le, gpr_r27_ppc64le,
+ gpr_r28_ppc64le, gpr_r29_ppc64le, gpr_r30_ppc64le, gpr_r31_ppc64le,
+ gpr_pc_ppc64le, gpr_msr_ppc64le, gpr_origr3_ppc64le, gpr_ctr_ppc64le,
+ gpr_lr_ppc64le, gpr_xer_ppc64le, gpr_cr_ppc64le, gpr_softe_ppc64le,
+ gpr_trap_ppc64le,
+};
+
+static const uint32_t g_fpr_regnums[] = {
+ fpr_f0_ppc64le, fpr_f1_ppc64le, fpr_f2_ppc64le, fpr_f3_ppc64le,
+ fpr_f4_ppc64le, fpr_f5_ppc64le, fpr_f6_ppc64le, fpr_f7_ppc64le,
+ fpr_f8_ppc64le, fpr_f9_ppc64le, fpr_f10_ppc64le, fpr_f11_ppc64le,
+ fpr_f12_ppc64le, fpr_f13_ppc64le, fpr_f14_ppc64le, fpr_f15_ppc64le,
+ fpr_f16_ppc64le, fpr_f17_ppc64le, fpr_f18_ppc64le, fpr_f19_ppc64le,
+ fpr_f20_ppc64le, fpr_f21_ppc64le, fpr_f22_ppc64le, fpr_f23_ppc64le,
+ fpr_f24_ppc64le, fpr_f25_ppc64le, fpr_f26_ppc64le, fpr_f27_ppc64le,
+ fpr_f28_ppc64le, fpr_f29_ppc64le, fpr_f30_ppc64le, fpr_f31_ppc64le,
+ fpr_fpscr_ppc64le,
+};
+
+static const uint32_t g_vmx_regnums[] = {
+ vmx_vr0_ppc64le, vmx_vr1_ppc64le, vmx_vr2_ppc64le, vmx_vr3_ppc64le,
+ vmx_vr4_ppc64le, vmx_vr5_ppc64le, vmx_vr6_ppc64le, vmx_vr7_ppc64le,
+ vmx_vr8_ppc64le, vmx_vr9_ppc64le, vmx_vr10_ppc64le, vmx_vr11_ppc64le,
+ vmx_vr12_ppc64le, vmx_vr13_ppc64le, vmx_vr14_ppc64le, vmx_vr15_ppc64le,
+ vmx_vr16_ppc64le, vmx_vr17_ppc64le, vmx_vr18_ppc64le, vmx_vr19_ppc64le,
+ vmx_vr20_ppc64le, vmx_vr21_ppc64le, vmx_vr22_ppc64le, vmx_vr23_ppc64le,
+ vmx_vr24_ppc64le, vmx_vr25_ppc64le, vmx_vr26_ppc64le, vmx_vr27_ppc64le,
+ vmx_vr28_ppc64le, vmx_vr29_ppc64le, vmx_vr30_ppc64le, vmx_vr31_ppc64le,
+ vmx_vscr_ppc64le, vmx_vrsave_ppc64le,
+};
+
+static const uint32_t g_vsx_regnums[] = {
+ vsx_vs0_ppc64le, vsx_vs1_ppc64le, vsx_vs2_ppc64le, vsx_vs3_ppc64le,
+ vsx_vs4_ppc64le, vsx_vs5_ppc64le, vsx_vs6_ppc64le, vsx_vs7_ppc64le,
+ vsx_vs8_ppc64le, vsx_vs9_ppc64le, vsx_vs10_ppc64le, vsx_vs11_ppc64le,
+ vsx_vs12_ppc64le, vsx_vs13_ppc64le, vsx_vs14_ppc64le, vsx_vs15_ppc64le,
+ vsx_vs16_ppc64le, vsx_vs17_ppc64le, vsx_vs18_ppc64le, vsx_vs19_ppc64le,
+ vsx_vs20_ppc64le, vsx_vs21_ppc64le, vsx_vs22_ppc64le, vsx_vs23_ppc64le,
+ vsx_vs24_ppc64le, vsx_vs25_ppc64le, vsx_vs26_ppc64le, vsx_vs27_ppc64le,
+ vsx_vs28_ppc64le, vsx_vs29_ppc64le, vsx_vs30_ppc64le, vsx_vs31_ppc64le,
+ vsx_vs32_ppc64le, vsx_vs33_ppc64le, vsx_vs34_ppc64le, vsx_vs35_ppc64le,
+ vsx_vs36_ppc64le, vsx_vs37_ppc64le, vsx_vs38_ppc64le, vsx_vs39_ppc64le,
+ vsx_vs40_ppc64le, vsx_vs41_ppc64le, vsx_vs42_ppc64le, vsx_vs43_ppc64le,
+ vsx_vs44_ppc64le, vsx_vs45_ppc64le, vsx_vs46_ppc64le, vsx_vs47_ppc64le,
+ vsx_vs48_ppc64le, vsx_vs49_ppc64le, vsx_vs50_ppc64le, vsx_vs51_ppc64le,
+ vsx_vs52_ppc64le, vsx_vs53_ppc64le, vsx_vs54_ppc64le, vsx_vs55_ppc64le,
+ vsx_vs56_ppc64le, vsx_vs57_ppc64le, vsx_vs58_ppc64le, vsx_vs59_ppc64le,
+ vsx_vs60_ppc64le, vsx_vs61_ppc64le, vsx_vs62_ppc64le, vsx_vs63_ppc64le,
+};
+
+// Number of register sets provided by this context.
+enum { k_num_register_sets = 4 };
+
+static const RegisterSet g_reg_sets_ppc64le[k_num_register_sets] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_registers_ppc64le,
+ g_gpr_regnums},
+ {"Floating Point Registers", "fpr", k_num_fpr_registers_ppc64le,
+ g_fpr_regnums},
+ {"Altivec/VMX Registers", "vmx", k_num_vmx_registers_ppc64le,
+ g_vmx_regnums},
+ {"VSX Registers", "vsx", k_num_vsx_registers_ppc64le, g_vsx_regnums},
+};
+
+bool RegisterContextPOSIX_ppc64le::IsGPR(unsigned reg) {
+ return (reg <= k_last_gpr_ppc64le); // GPR's come first.
+}
+
+bool RegisterContextPOSIX_ppc64le::IsFPR(unsigned reg) {
+ return (reg >= k_first_fpr_ppc64le) && (reg <= k_last_fpr_ppc64le);
+}
+
+bool RegisterContextPOSIX_ppc64le::IsVMX(unsigned reg) {
+ return (reg >= k_first_vmx_ppc64le) && (reg <= k_last_vmx_ppc64le);
+}
+
+bool RegisterContextPOSIX_ppc64le::IsVSX(unsigned reg) {
+ return (reg >= k_first_vsx_ppc64le) && (reg <= k_last_vsx_ppc64le);
+}
+
+RegisterContextPOSIX_ppc64le::RegisterContextPOSIX_ppc64le(
+ Thread &thread, uint32_t concrete_frame_idx,
+ RegisterInfoInterface *register_info)
+ : RegisterContext(thread, concrete_frame_idx) {
+ m_register_info_up.reset(register_info);
+}
+
+void RegisterContextPOSIX_ppc64le::InvalidateAllRegisters() {}
+
+unsigned RegisterContextPOSIX_ppc64le::GetRegisterOffset(unsigned reg) {
+ assert(reg < k_num_registers_ppc64le && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_offset;
+}
+
+unsigned RegisterContextPOSIX_ppc64le::GetRegisterSize(unsigned reg) {
+ assert(reg < k_num_registers_ppc64le && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_size;
+}
+
+size_t RegisterContextPOSIX_ppc64le::GetRegisterCount() {
+ size_t num_registers = k_num_registers_ppc64le;
+ return num_registers;
+}
+
+size_t RegisterContextPOSIX_ppc64le::GetGPRSize() {
+ return m_register_info_up->GetGPRSize();
+}
+
+const RegisterInfo *RegisterContextPOSIX_ppc64le::GetRegisterInfo() {
+ // Commonly, this method is overridden and g_register_infos is copied and
+ // specialized. So, use GetRegisterInfo() rather than g_register_infos in
+ // this scope.
+ return m_register_info_up->GetRegisterInfo();
+}
+
+const RegisterInfo *
+RegisterContextPOSIX_ppc64le::GetRegisterInfoAtIndex(size_t reg) {
+ if (reg < k_num_registers_ppc64le)
+ return &GetRegisterInfo()[reg];
+ else
+ return nullptr;
+}
+
+size_t RegisterContextPOSIX_ppc64le::GetRegisterSetCount() {
+ size_t sets = 0;
+ for (size_t set = 0; set < k_num_register_sets; ++set) {
+ if (IsRegisterSetAvailable(set))
+ ++sets;
+ }
+
+ return sets;
+}
+
+const RegisterSet *RegisterContextPOSIX_ppc64le::GetRegisterSet(size_t set) {
+ if (IsRegisterSetAvailable(set))
+ return &g_reg_sets_ppc64le[set];
+ else
+ return nullptr;
+}
+
+const char *RegisterContextPOSIX_ppc64le::GetRegisterName(unsigned reg) {
+ assert(reg < k_num_registers_ppc64le && "Invalid register offset.");
+ return GetRegisterInfo()[reg].name;
+}
+
+lldb::ByteOrder RegisterContextPOSIX_ppc64le::GetByteOrder() {
+ // Get the target process whose privileged thread was used for the register
+ // read.
+ lldb::ByteOrder byte_order = eByteOrderInvalid;
+ Process *process = CalculateProcess().get();
+
+ if (process)
+ byte_order = process->GetByteOrder();
+ return byte_order;
+}
+
+bool RegisterContextPOSIX_ppc64le::IsRegisterSetAvailable(size_t set_index) {
+ size_t num_sets = k_num_register_sets;
+
+ return (set_index < num_sets);
+}
+
+// Used when parsing DWARF and EH frame information and any other object file
+// sections that contain register numbers in them.
+uint32_t RegisterContextPOSIX_ppc64le::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ const uint32_t num_regs = GetRegisterCount();
+
+ assert(kind < kNumRegisterKinds);
+ for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_idx);
+
+ if (reg_info->kinds[kind] == num)
+ return reg_idx;
+ }
+
+ return LLDB_INVALID_REGNUM;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.h
new file mode 100644
index 000000000000..37079775a3c7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.h
@@ -0,0 +1,77 @@
+//===-- RegisterContextPOSIX_ppc64le.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 liblldb_RegisterContextPOSIX_ppc64le_h_
+#define liblldb_RegisterContextPOSIX_ppc64le_h_
+
+#include "Plugins/Process/Utility/lldb-ppc64le-register-enums.h"
+#include "RegisterInfoInterface.h"
+#include "Utility/PPC64LE_DWARF_Registers.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/Log.h"
+
+class RegisterContextPOSIX_ppc64le : public lldb_private::RegisterContext {
+public:
+ RegisterContextPOSIX_ppc64le(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info);
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ virtual size_t GetGPRSize();
+
+ virtual unsigned GetRegisterSize(unsigned reg);
+
+ virtual unsigned GetRegisterOffset(unsigned reg);
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ const char *GetRegisterName(unsigned reg);
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+protected:
+ // 64-bit general purpose registers.
+ uint64_t m_gpr_ppc64le[k_num_gpr_registers_ppc64le];
+
+ // floating-point registers including extended register.
+ uint64_t m_fpr_ppc64le[k_num_fpr_registers_ppc64le];
+
+ // VMX registers.
+ uint64_t m_vmx_ppc64le[k_num_vmx_registers_ppc64le * 2];
+
+ // VSX registers.
+ uint64_t m_vsx_ppc64le[k_num_vsx_registers_ppc64le * 2];
+
+ std::unique_ptr<lldb_private::RegisterInfoInterface> m_register_info_up;
+
+ // Determines if an extended register set is supported on the processor
+ // running the inferior process.
+ virtual bool IsRegisterSetAvailable(size_t set_index);
+
+ virtual const lldb_private::RegisterInfo *GetRegisterInfo();
+
+ bool IsGPR(unsigned reg);
+
+ bool IsFPR(unsigned reg);
+
+ bool IsVMX(unsigned reg);
+
+ bool IsVSX(unsigned reg);
+
+ lldb::ByteOrder GetByteOrder();
+};
+
+#endif // liblldb_RegisterContextPOSIX_ppc64le_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.cpp
new file mode 100644
index 000000000000..e040e5075721
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.cpp
@@ -0,0 +1,191 @@
+//===-- RegisterContextPOSIX_s390x.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 <cstring>
+#include <errno.h>
+#include <stdint.h>
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/Support/Compiler.h"
+
+#include "RegisterContextPOSIX_s390x.h"
+#include "RegisterContext_s390x.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+// s390x 64-bit general purpose registers.
+static const uint32_t g_gpr_regnums_s390x[] = {
+ lldb_r0_s390x, lldb_r1_s390x, lldb_r2_s390x, lldb_r3_s390x,
+ lldb_r4_s390x, lldb_r5_s390x, lldb_r6_s390x, lldb_r7_s390x,
+ lldb_r8_s390x, lldb_r9_s390x, lldb_r10_s390x, lldb_r11_s390x,
+ lldb_r12_s390x, lldb_r13_s390x, lldb_r14_s390x, lldb_r15_s390x,
+ lldb_acr0_s390x, lldb_acr1_s390x, lldb_acr2_s390x, lldb_acr3_s390x,
+ lldb_acr4_s390x, lldb_acr5_s390x, lldb_acr6_s390x, lldb_acr7_s390x,
+ lldb_acr8_s390x, lldb_acr9_s390x, lldb_acr10_s390x, lldb_acr11_s390x,
+ lldb_acr12_s390x, lldb_acr13_s390x, lldb_acr14_s390x, lldb_acr15_s390x,
+ lldb_pswm_s390x, lldb_pswa_s390x,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+static_assert((sizeof(g_gpr_regnums_s390x) / sizeof(g_gpr_regnums_s390x[0])) -
+ 1 ==
+ k_num_gpr_registers_s390x,
+ "g_gpr_regnums_s390x has wrong number of register infos");
+
+// s390x 64-bit floating point registers.
+static const uint32_t g_fpu_regnums_s390x[] = {
+ lldb_f0_s390x, lldb_f1_s390x, lldb_f2_s390x, lldb_f3_s390x,
+ lldb_f4_s390x, lldb_f5_s390x, lldb_f6_s390x, lldb_f7_s390x,
+ lldb_f8_s390x, lldb_f9_s390x, lldb_f10_s390x, lldb_f11_s390x,
+ lldb_f12_s390x, lldb_f13_s390x, lldb_f14_s390x, lldb_f15_s390x,
+ lldb_fpc_s390x,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+static_assert((sizeof(g_fpu_regnums_s390x) / sizeof(g_fpu_regnums_s390x[0])) -
+ 1 ==
+ k_num_fpr_registers_s390x,
+ "g_fpu_regnums_s390x has wrong number of register infos");
+
+// Number of register sets provided by this context.
+enum { k_num_register_sets = 2 };
+
+// Register sets for s390x 64-bit.
+static const RegisterSet g_reg_sets_s390x[k_num_register_sets] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_registers_s390x,
+ g_gpr_regnums_s390x},
+ {"Floating Point Registers", "fpr", k_num_fpr_registers_s390x,
+ g_fpu_regnums_s390x},
+};
+
+bool RegisterContextPOSIX_s390x::IsGPR(unsigned reg) {
+ return reg <= m_reg_info.last_gpr; // GPRs come first.
+}
+
+bool RegisterContextPOSIX_s390x::IsFPR(unsigned reg) {
+ return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr);
+}
+
+RegisterContextPOSIX_s390x::RegisterContextPOSIX_s390x(
+ Thread &thread, uint32_t concrete_frame_idx,
+ RegisterInfoInterface *register_info)
+ : RegisterContext(thread, concrete_frame_idx) {
+ m_register_info_up.reset(register_info);
+
+ switch (register_info->m_target_arch.GetMachine()) {
+ case llvm::Triple::systemz:
+ m_reg_info.num_registers = k_num_registers_s390x;
+ m_reg_info.num_gpr_registers = k_num_gpr_registers_s390x;
+ m_reg_info.num_fpr_registers = k_num_fpr_registers_s390x;
+ m_reg_info.last_gpr = k_last_gpr_s390x;
+ m_reg_info.first_fpr = k_first_fpr_s390x;
+ m_reg_info.last_fpr = k_last_fpr_s390x;
+ break;
+ default:
+ assert(false && "Unhandled target architecture.");
+ break;
+ }
+}
+
+RegisterContextPOSIX_s390x::~RegisterContextPOSIX_s390x() {}
+
+void RegisterContextPOSIX_s390x::Invalidate() {}
+
+void RegisterContextPOSIX_s390x::InvalidateAllRegisters() {}
+
+const RegisterInfo *RegisterContextPOSIX_s390x::GetRegisterInfo() {
+ return m_register_info_up->GetRegisterInfo();
+}
+
+const RegisterInfo *
+RegisterContextPOSIX_s390x::GetRegisterInfoAtIndex(size_t reg) {
+ if (reg < m_reg_info.num_registers)
+ return &GetRegisterInfo()[reg];
+ else
+ return nullptr;
+}
+
+size_t RegisterContextPOSIX_s390x::GetRegisterCount() {
+ return m_reg_info.num_registers;
+}
+
+unsigned RegisterContextPOSIX_s390x::GetRegisterOffset(unsigned reg) {
+ assert(reg < m_reg_info.num_registers && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_offset;
+}
+
+unsigned RegisterContextPOSIX_s390x::GetRegisterSize(unsigned reg) {
+ assert(reg < m_reg_info.num_registers && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_size;
+}
+
+const char *RegisterContextPOSIX_s390x::GetRegisterName(unsigned reg) {
+ assert(reg < m_reg_info.num_registers && "Invalid register offset.");
+ return GetRegisterInfo()[reg].name;
+}
+
+bool RegisterContextPOSIX_s390x::IsRegisterSetAvailable(size_t set_index) {
+ return set_index < k_num_register_sets;
+}
+
+size_t RegisterContextPOSIX_s390x::GetRegisterSetCount() {
+ size_t sets = 0;
+ for (size_t set = 0; set < k_num_register_sets; ++set) {
+ if (IsRegisterSetAvailable(set))
+ ++sets;
+ }
+
+ return sets;
+}
+
+const RegisterSet *RegisterContextPOSIX_s390x::GetRegisterSet(size_t set) {
+ if (IsRegisterSetAvailable(set)) {
+ switch (m_register_info_up->m_target_arch.GetMachine()) {
+ case llvm::Triple::systemz:
+ return &g_reg_sets_s390x[set];
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+ }
+ return nullptr;
+}
+
+lldb::ByteOrder RegisterContextPOSIX_s390x::GetByteOrder() {
+ // Get the target process whose privileged thread was used for the register
+ // read.
+ lldb::ByteOrder byte_order = eByteOrderInvalid;
+ Process *process = CalculateProcess().get();
+
+ if (process)
+ byte_order = process->GetByteOrder();
+ return byte_order;
+}
+
+// Used when parsing DWARF and EH frame information and any other object file
+// sections that contain register numbers in them.
+uint32_t RegisterContextPOSIX_s390x::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ const uint32_t num_regs = GetRegisterCount();
+
+ assert(kind < kNumRegisterKinds);
+ for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_idx);
+
+ if (reg_info->kinds[kind] == num)
+ return reg_idx;
+ }
+
+ return LLDB_INVALID_REGNUM;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.h
new file mode 100644
index 000000000000..54993ce6c3ec
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.h
@@ -0,0 +1,79 @@
+//===-- RegisterContextPOSIX_s390x.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 liblldb_RegisterContextPOSIX_s390x_h_
+#define liblldb_RegisterContextPOSIX_s390x_h_
+
+#include "RegisterContext_s390x.h"
+#include "RegisterInfoInterface.h"
+#include "lldb-s390x-register-enums.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/Log.h"
+
+class ProcessMonitor;
+
+class RegisterContextPOSIX_s390x : public lldb_private::RegisterContext {
+public:
+ RegisterContextPOSIX_s390x(
+ lldb_private::Thread &thread, uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info);
+
+ ~RegisterContextPOSIX_s390x() override;
+
+ void Invalidate();
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ virtual unsigned GetRegisterSize(unsigned reg);
+
+ virtual unsigned GetRegisterOffset(unsigned reg);
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ const char *GetRegisterName(unsigned reg);
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+protected:
+ struct RegInfo {
+ uint32_t num_registers;
+ uint32_t num_gpr_registers;
+ uint32_t num_fpr_registers;
+
+ uint32_t last_gpr;
+ uint32_t first_fpr;
+ uint32_t last_fpr;
+ };
+
+ RegInfo m_reg_info;
+ std::unique_ptr<lldb_private::RegisterInfoInterface> m_register_info_up;
+
+ virtual bool IsRegisterSetAvailable(size_t set_index);
+
+ virtual const lldb_private::RegisterInfo *GetRegisterInfo();
+
+ bool IsGPR(unsigned reg);
+
+ bool IsFPR(unsigned reg);
+
+ lldb::ByteOrder GetByteOrder();
+
+ virtual bool ReadGPR() = 0;
+ virtual bool ReadFPR() = 0;
+ virtual bool WriteGPR() = 0;
+ virtual bool WriteFPR() = 0;
+};
+
+#endif // liblldb_RegisterContextPOSIX_s390x_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.cpp
new file mode 100644
index 000000000000..4d5991f08f1d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.cpp
@@ -0,0 +1,528 @@
+//===-- RegisterContextPOSIX_x86.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 <cstring>
+#include <errno.h>
+#include <stdint.h>
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "llvm/Support/Compiler.h"
+
+#include "RegisterContextPOSIX_x86.h"
+#include "RegisterContext_x86.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+const uint32_t g_gpr_regnums_i386[] = {
+ lldb_eax_i386, lldb_ebx_i386, lldb_ecx_i386, lldb_edx_i386,
+ lldb_edi_i386, lldb_esi_i386, lldb_ebp_i386, lldb_esp_i386,
+ lldb_eip_i386, lldb_eflags_i386, lldb_cs_i386, lldb_fs_i386,
+ lldb_gs_i386, lldb_ss_i386, lldb_ds_i386, lldb_es_i386,
+ lldb_ax_i386, lldb_bx_i386, lldb_cx_i386, lldb_dx_i386,
+ lldb_di_i386, lldb_si_i386, lldb_bp_i386, lldb_sp_i386,
+ lldb_ah_i386, lldb_bh_i386, lldb_ch_i386, lldb_dh_i386,
+ lldb_al_i386, lldb_bl_i386, lldb_cl_i386, lldb_dl_i386,
+ LLDB_INVALID_REGNUM, // Register sets must be terminated with
+ // LLDB_INVALID_REGNUM.
+};
+static_assert((sizeof(g_gpr_regnums_i386) / sizeof(g_gpr_regnums_i386[0])) -
+ 1 ==
+ k_num_gpr_registers_i386,
+ "g_gpr_regnums_i386 has wrong number of register infos");
+
+const uint32_t g_lldb_regnums_i386[] = {
+ lldb_fctrl_i386, lldb_fstat_i386, lldb_ftag_i386, lldb_fop_i386,
+ lldb_fiseg_i386, lldb_fioff_i386, lldb_foseg_i386, lldb_fooff_i386,
+ lldb_mxcsr_i386, lldb_mxcsrmask_i386, lldb_st0_i386, lldb_st1_i386,
+ lldb_st2_i386, lldb_st3_i386, lldb_st4_i386, lldb_st5_i386,
+ lldb_st6_i386, lldb_st7_i386, lldb_mm0_i386, lldb_mm1_i386,
+ lldb_mm2_i386, lldb_mm3_i386, lldb_mm4_i386, lldb_mm5_i386,
+ lldb_mm6_i386, lldb_mm7_i386, lldb_xmm0_i386, lldb_xmm1_i386,
+ lldb_xmm2_i386, lldb_xmm3_i386, lldb_xmm4_i386, lldb_xmm5_i386,
+ lldb_xmm6_i386, lldb_xmm7_i386,
+ LLDB_INVALID_REGNUM // Register sets must be terminated with
+ // LLDB_INVALID_REGNUM.
+};
+static_assert((sizeof(g_lldb_regnums_i386) / sizeof(g_lldb_regnums_i386[0])) -
+ 1 ==
+ k_num_fpr_registers_i386,
+ "g_lldb_regnums_i386 has wrong number of register infos");
+
+const uint32_t g_avx_regnums_i386[] = {
+ lldb_ymm0_i386, lldb_ymm1_i386, lldb_ymm2_i386, lldb_ymm3_i386,
+ lldb_ymm4_i386, lldb_ymm5_i386, lldb_ymm6_i386, lldb_ymm7_i386,
+ LLDB_INVALID_REGNUM // Register sets must be terminated with
+ // LLDB_INVALID_REGNUM.
+};
+static_assert((sizeof(g_avx_regnums_i386) / sizeof(g_avx_regnums_i386[0])) -
+ 1 ==
+ k_num_avx_registers_i386,
+ " g_avx_regnums_i386 has wrong number of register infos");
+
+static const uint32_t g_gpr_regnums_x86_64[] = {
+ lldb_rax_x86_64, lldb_rbx_x86_64, lldb_rcx_x86_64, lldb_rdx_x86_64,
+ lldb_rdi_x86_64, lldb_rsi_x86_64, lldb_rbp_x86_64, lldb_rsp_x86_64,
+ lldb_r8_x86_64, lldb_r9_x86_64, lldb_r10_x86_64, lldb_r11_x86_64,
+ lldb_r12_x86_64, lldb_r13_x86_64, lldb_r14_x86_64, lldb_r15_x86_64,
+ lldb_rip_x86_64, lldb_rflags_x86_64, lldb_cs_x86_64, lldb_fs_x86_64,
+ lldb_gs_x86_64, lldb_ss_x86_64, lldb_ds_x86_64, lldb_es_x86_64,
+ lldb_eax_x86_64, lldb_ebx_x86_64, lldb_ecx_x86_64, lldb_edx_x86_64,
+ lldb_edi_x86_64, lldb_esi_x86_64, lldb_ebp_x86_64, lldb_esp_x86_64,
+ lldb_r8d_x86_64, // Low 32 bits or r8
+ lldb_r9d_x86_64, // Low 32 bits or r9
+ lldb_r10d_x86_64, // Low 32 bits or r10
+ lldb_r11d_x86_64, // Low 32 bits or r11
+ lldb_r12d_x86_64, // Low 32 bits or r12
+ lldb_r13d_x86_64, // Low 32 bits or r13
+ lldb_r14d_x86_64, // Low 32 bits or r14
+ lldb_r15d_x86_64, // Low 32 bits or r15
+ lldb_ax_x86_64, lldb_bx_x86_64, lldb_cx_x86_64, lldb_dx_x86_64,
+ lldb_di_x86_64, lldb_si_x86_64, lldb_bp_x86_64, lldb_sp_x86_64,
+ lldb_r8w_x86_64, // Low 16 bits or r8
+ lldb_r9w_x86_64, // Low 16 bits or r9
+ lldb_r10w_x86_64, // Low 16 bits or r10
+ lldb_r11w_x86_64, // Low 16 bits or r11
+ lldb_r12w_x86_64, // Low 16 bits or r12
+ lldb_r13w_x86_64, // Low 16 bits or r13
+ lldb_r14w_x86_64, // Low 16 bits or r14
+ lldb_r15w_x86_64, // Low 16 bits or r15
+ lldb_ah_x86_64, lldb_bh_x86_64, lldb_ch_x86_64, lldb_dh_x86_64,
+ lldb_al_x86_64, lldb_bl_x86_64, lldb_cl_x86_64, lldb_dl_x86_64,
+ lldb_dil_x86_64, lldb_sil_x86_64, lldb_bpl_x86_64, lldb_spl_x86_64,
+ lldb_r8l_x86_64, // Low 8 bits or r8
+ lldb_r9l_x86_64, // Low 8 bits or r9
+ lldb_r10l_x86_64, // Low 8 bits or r10
+ lldb_r11l_x86_64, // Low 8 bits or r11
+ lldb_r12l_x86_64, // Low 8 bits or r12
+ lldb_r13l_x86_64, // Low 8 bits or r13
+ lldb_r14l_x86_64, // Low 8 bits or r14
+ lldb_r15l_x86_64, // Low 8 bits or r15
+ LLDB_INVALID_REGNUM // Register sets must be terminated with
+ // LLDB_INVALID_REGNUM.
+};
+static_assert((sizeof(g_gpr_regnums_x86_64) / sizeof(g_gpr_regnums_x86_64[0])) -
+ 1 ==
+ k_num_gpr_registers_x86_64,
+ "g_gpr_regnums_x86_64 has wrong number of register infos");
+
+static const uint32_t g_lldb_regnums_x86_64[] = {
+ lldb_fctrl_x86_64, lldb_fstat_x86_64, lldb_ftag_x86_64,
+ lldb_fop_x86_64, lldb_fiseg_x86_64, lldb_fioff_x86_64,
+ lldb_foseg_x86_64, lldb_fooff_x86_64, lldb_mxcsr_x86_64,
+ lldb_mxcsrmask_x86_64, lldb_st0_x86_64, lldb_st1_x86_64,
+ lldb_st2_x86_64, lldb_st3_x86_64, lldb_st4_x86_64,
+ lldb_st5_x86_64, lldb_st6_x86_64, lldb_st7_x86_64,
+ lldb_mm0_x86_64, lldb_mm1_x86_64, lldb_mm2_x86_64,
+ lldb_mm3_x86_64, lldb_mm4_x86_64, lldb_mm5_x86_64,
+ lldb_mm6_x86_64, lldb_mm7_x86_64, lldb_xmm0_x86_64,
+ lldb_xmm1_x86_64, lldb_xmm2_x86_64, lldb_xmm3_x86_64,
+ lldb_xmm4_x86_64, lldb_xmm5_x86_64, lldb_xmm6_x86_64,
+ lldb_xmm7_x86_64, lldb_xmm8_x86_64, lldb_xmm9_x86_64,
+ lldb_xmm10_x86_64, lldb_xmm11_x86_64, lldb_xmm12_x86_64,
+ lldb_xmm13_x86_64, lldb_xmm14_x86_64, lldb_xmm15_x86_64,
+ LLDB_INVALID_REGNUM // Register sets must be terminated with
+ // LLDB_INVALID_REGNUM.
+};
+static_assert((sizeof(g_lldb_regnums_x86_64) /
+ sizeof(g_lldb_regnums_x86_64[0])) -
+ 1 ==
+ k_num_fpr_registers_x86_64,
+ "g_lldb_regnums_x86_64 has wrong number of register infos");
+
+static const uint32_t g_avx_regnums_x86_64[] = {
+ lldb_ymm0_x86_64, lldb_ymm1_x86_64, lldb_ymm2_x86_64, lldb_ymm3_x86_64,
+ lldb_ymm4_x86_64, lldb_ymm5_x86_64, lldb_ymm6_x86_64, lldb_ymm7_x86_64,
+ lldb_ymm8_x86_64, lldb_ymm9_x86_64, lldb_ymm10_x86_64, lldb_ymm11_x86_64,
+ lldb_ymm12_x86_64, lldb_ymm13_x86_64, lldb_ymm14_x86_64, lldb_ymm15_x86_64,
+ LLDB_INVALID_REGNUM // Register sets must be terminated with
+ // LLDB_INVALID_REGNUM.
+};
+static_assert((sizeof(g_avx_regnums_x86_64) / sizeof(g_avx_regnums_x86_64[0])) -
+ 1 ==
+ k_num_avx_registers_x86_64,
+ "g_avx_regnums_x86_64 has wrong number of register infos");
+
+uint32_t RegisterContextPOSIX_x86::g_contained_eax[] = {lldb_eax_i386,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_ebx[] = {lldb_ebx_i386,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_ecx[] = {lldb_ecx_i386,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_edx[] = {lldb_edx_i386,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_edi[] = {lldb_edi_i386,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_esi[] = {lldb_esi_i386,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_ebp[] = {lldb_ebp_i386,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_esp[] = {lldb_esp_i386,
+ LLDB_INVALID_REGNUM};
+
+uint32_t RegisterContextPOSIX_x86::g_invalidate_eax[] = {
+ lldb_eax_i386, lldb_ax_i386, lldb_ah_i386, lldb_al_i386,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_ebx[] = {
+ lldb_ebx_i386, lldb_bx_i386, lldb_bh_i386, lldb_bl_i386,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_ecx[] = {
+ lldb_ecx_i386, lldb_cx_i386, lldb_ch_i386, lldb_cl_i386,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_edx[] = {
+ lldb_edx_i386, lldb_dx_i386, lldb_dh_i386, lldb_dl_i386,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_edi[] = {
+ lldb_edi_i386, lldb_di_i386, LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_esi[] = {
+ lldb_esi_i386, lldb_si_i386, LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_ebp[] = {
+ lldb_ebp_i386, lldb_bp_i386, LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_esp[] = {
+ lldb_esp_i386, lldb_sp_i386, LLDB_INVALID_REGNUM};
+
+uint32_t RegisterContextPOSIX_x86::g_contained_rax[] = {lldb_rax_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_rbx[] = {lldb_rbx_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_rcx[] = {lldb_rcx_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_rdx[] = {lldb_rdx_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_rdi[] = {lldb_rdi_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_rsi[] = {lldb_rsi_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_rbp[] = {lldb_rbp_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_rsp[] = {lldb_rsp_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_r8[] = {lldb_r8_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_r9[] = {lldb_r9_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_r10[] = {lldb_r10_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_r11[] = {lldb_r11_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_r12[] = {lldb_r12_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_r13[] = {lldb_r13_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_r14[] = {lldb_r14_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_contained_r15[] = {lldb_r15_x86_64,
+ LLDB_INVALID_REGNUM};
+
+uint32_t RegisterContextPOSIX_x86::g_invalidate_rax[] = {
+ lldb_rax_x86_64, lldb_eax_x86_64, lldb_ax_x86_64,
+ lldb_ah_x86_64, lldb_al_x86_64, LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_rbx[] = {
+ lldb_rbx_x86_64, lldb_ebx_x86_64, lldb_bx_x86_64,
+ lldb_bh_x86_64, lldb_bl_x86_64, LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_rcx[] = {
+ lldb_rcx_x86_64, lldb_ecx_x86_64, lldb_cx_x86_64,
+ lldb_ch_x86_64, lldb_cl_x86_64, LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_rdx[] = {
+ lldb_rdx_x86_64, lldb_edx_x86_64, lldb_dx_x86_64,
+ lldb_dh_x86_64, lldb_dl_x86_64, LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_rdi[] = {
+ lldb_rdi_x86_64, lldb_edi_x86_64, lldb_di_x86_64, lldb_dil_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_rsi[] = {
+ lldb_rsi_x86_64, lldb_esi_x86_64, lldb_si_x86_64, lldb_sil_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_rbp[] = {
+ lldb_rbp_x86_64, lldb_ebp_x86_64, lldb_bp_x86_64, lldb_bpl_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_rsp[] = {
+ lldb_rsp_x86_64, lldb_esp_x86_64, lldb_sp_x86_64, lldb_spl_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_r8[] = {
+ lldb_r8_x86_64, lldb_r8d_x86_64, lldb_r8w_x86_64, lldb_r8l_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_r9[] = {
+ lldb_r9_x86_64, lldb_r9d_x86_64, lldb_r9w_x86_64, lldb_r9l_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_r10[] = {
+ lldb_r10_x86_64, lldb_r10d_x86_64, lldb_r10w_x86_64, lldb_r10l_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_r11[] = {
+ lldb_r11_x86_64, lldb_r11d_x86_64, lldb_r11w_x86_64, lldb_r11l_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_r12[] = {
+ lldb_r12_x86_64, lldb_r12d_x86_64, lldb_r12w_x86_64, lldb_r12l_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_r13[] = {
+ lldb_r13_x86_64, lldb_r13d_x86_64, lldb_r13w_x86_64, lldb_r13l_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_r14[] = {
+ lldb_r14_x86_64, lldb_r14d_x86_64, lldb_r14w_x86_64, lldb_r14l_x86_64,
+ LLDB_INVALID_REGNUM};
+uint32_t RegisterContextPOSIX_x86::g_invalidate_r15[] = {
+ lldb_r15_x86_64, lldb_r15d_x86_64, lldb_r15w_x86_64, lldb_r15l_x86_64,
+ LLDB_INVALID_REGNUM};
+
+// Number of register sets provided by this context.
+enum { k_num_extended_register_sets = 1, k_num_register_sets = 3 };
+
+static const RegisterSet g_reg_sets_i386[k_num_register_sets] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_registers_i386,
+ g_gpr_regnums_i386},
+ {"Floating Point Registers", "fpu", k_num_fpr_registers_i386,
+ g_lldb_regnums_i386},
+ {"Advanced Vector Extensions", "avx", k_num_avx_registers_i386,
+ g_avx_regnums_i386}};
+
+static const RegisterSet g_reg_sets_x86_64[k_num_register_sets] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_registers_x86_64,
+ g_gpr_regnums_x86_64},
+ {"Floating Point Registers", "fpu", k_num_fpr_registers_x86_64,
+ g_lldb_regnums_x86_64},
+ {"Advanced Vector Extensions", "avx", k_num_avx_registers_x86_64,
+ g_avx_regnums_x86_64}};
+
+bool RegisterContextPOSIX_x86::IsGPR(unsigned reg) {
+ return reg <= m_reg_info.last_gpr; // GPR's come first.
+}
+
+bool RegisterContextPOSIX_x86::IsFPR(unsigned reg) {
+ return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr);
+}
+
+bool RegisterContextPOSIX_x86::IsAVX(unsigned reg) {
+ return (m_reg_info.first_ymm <= reg && reg <= m_reg_info.last_ymm);
+}
+
+bool RegisterContextPOSIX_x86::IsFPR(unsigned reg, FPRType fpr_type) {
+ bool generic_fpr = IsFPR(reg);
+
+ if (fpr_type == eXSAVE)
+ return generic_fpr || IsAVX(reg);
+ return generic_fpr;
+}
+
+RegisterContextPOSIX_x86::RegisterContextPOSIX_x86(
+ Thread &thread, uint32_t concrete_frame_idx,
+ RegisterInfoInterface *register_info)
+ : RegisterContext(thread, concrete_frame_idx) {
+ m_register_info_up.reset(register_info);
+
+ switch (register_info->m_target_arch.GetMachine()) {
+ case llvm::Triple::x86:
+ m_reg_info.num_registers = k_num_registers_i386;
+ m_reg_info.num_gpr_registers = k_num_gpr_registers_i386;
+ m_reg_info.num_fpr_registers = k_num_fpr_registers_i386;
+ m_reg_info.num_avx_registers = k_num_avx_registers_i386;
+ m_reg_info.last_gpr = k_last_gpr_i386;
+ m_reg_info.first_fpr = k_first_fpr_i386;
+ m_reg_info.last_fpr = k_last_fpr_i386;
+ m_reg_info.first_st = lldb_st0_i386;
+ m_reg_info.last_st = lldb_st7_i386;
+ m_reg_info.first_mm = lldb_mm0_i386;
+ m_reg_info.last_mm = lldb_mm7_i386;
+ m_reg_info.first_xmm = lldb_xmm0_i386;
+ m_reg_info.last_xmm = lldb_xmm7_i386;
+ m_reg_info.first_ymm = lldb_ymm0_i386;
+ m_reg_info.last_ymm = lldb_ymm7_i386;
+ m_reg_info.first_dr = lldb_dr0_i386;
+ m_reg_info.gpr_flags = lldb_eflags_i386;
+ break;
+ case llvm::Triple::x86_64:
+ m_reg_info.num_registers = k_num_registers_x86_64;
+ m_reg_info.num_gpr_registers = k_num_gpr_registers_x86_64;
+ m_reg_info.num_fpr_registers = k_num_fpr_registers_x86_64;
+ m_reg_info.num_avx_registers = k_num_avx_registers_x86_64;
+ m_reg_info.last_gpr = k_last_gpr_x86_64;
+ m_reg_info.first_fpr = k_first_fpr_x86_64;
+ m_reg_info.last_fpr = k_last_fpr_x86_64;
+ m_reg_info.first_st = lldb_st0_x86_64;
+ m_reg_info.last_st = lldb_st7_x86_64;
+ m_reg_info.first_mm = lldb_mm0_x86_64;
+ m_reg_info.last_mm = lldb_mm7_x86_64;
+ m_reg_info.first_xmm = lldb_xmm0_x86_64;
+ m_reg_info.last_xmm = lldb_xmm15_x86_64;
+ m_reg_info.first_ymm = lldb_ymm0_x86_64;
+ m_reg_info.last_ymm = lldb_ymm15_x86_64;
+ m_reg_info.first_dr = lldb_dr0_x86_64;
+ m_reg_info.gpr_flags = lldb_rflags_x86_64;
+ break;
+ default:
+ assert(false && "Unhandled target architecture.");
+ break;
+ }
+
+ ::memset(&m_fpr, 0, sizeof(FPR));
+
+ m_fpr_type = eNotValid;
+}
+
+RegisterContextPOSIX_x86::~RegisterContextPOSIX_x86() {}
+
+RegisterContextPOSIX_x86::FPRType RegisterContextPOSIX_x86::GetFPRType() {
+ if (m_fpr_type == eNotValid) {
+ // TODO: Use assembly to call cpuid on the inferior and query ebx or ecx
+ m_fpr_type = eXSAVE; // extended floating-point registers, if available
+ if (!ReadFPR())
+ m_fpr_type = eFXSAVE; // assume generic floating-point registers
+ }
+ return m_fpr_type;
+}
+
+void RegisterContextPOSIX_x86::Invalidate() {}
+
+void RegisterContextPOSIX_x86::InvalidateAllRegisters() {}
+
+unsigned RegisterContextPOSIX_x86::GetRegisterOffset(unsigned reg) {
+ assert(reg < m_reg_info.num_registers && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_offset;
+}
+
+unsigned RegisterContextPOSIX_x86::GetRegisterSize(unsigned reg) {
+ assert(reg < m_reg_info.num_registers && "Invalid register number.");
+ return GetRegisterInfo()[reg].byte_size;
+}
+
+size_t RegisterContextPOSIX_x86::GetRegisterCount() {
+ size_t num_registers =
+ m_reg_info.num_gpr_registers + m_reg_info.num_fpr_registers;
+ if (GetFPRType() == eXSAVE)
+ return num_registers + m_reg_info.num_avx_registers;
+ return num_registers;
+}
+
+size_t RegisterContextPOSIX_x86::GetGPRSize() {
+ return m_register_info_up->GetGPRSize();
+}
+
+size_t RegisterContextPOSIX_x86::GetFXSAVEOffset() {
+ return GetRegisterInfo()[m_reg_info.first_fpr].byte_offset;
+}
+
+const RegisterInfo *RegisterContextPOSIX_x86::GetRegisterInfo() {
+ // Commonly, this method is overridden and g_register_infos is copied and
+ // specialized. So, use GetRegisterInfo() rather than g_register_infos in
+ // this scope.
+ return m_register_info_up->GetRegisterInfo();
+}
+
+const RegisterInfo *
+RegisterContextPOSIX_x86::GetRegisterInfoAtIndex(size_t reg) {
+ if (reg < m_reg_info.num_registers)
+ return &GetRegisterInfo()[reg];
+ else
+ return nullptr;
+}
+
+size_t RegisterContextPOSIX_x86::GetRegisterSetCount() {
+ size_t sets = 0;
+ for (size_t set = 0; set < k_num_register_sets; ++set) {
+ if (IsRegisterSetAvailable(set))
+ ++sets;
+ }
+
+ return sets;
+}
+
+const RegisterSet *RegisterContextPOSIX_x86::GetRegisterSet(size_t set) {
+ if (IsRegisterSetAvailable(set)) {
+ switch (m_register_info_up->m_target_arch.GetMachine()) {
+ case llvm::Triple::x86:
+ return &g_reg_sets_i386[set];
+ case llvm::Triple::x86_64:
+ return &g_reg_sets_x86_64[set];
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+ }
+ return nullptr;
+}
+
+const char *RegisterContextPOSIX_x86::GetRegisterName(unsigned reg) {
+ assert(reg < m_reg_info.num_registers && "Invalid register offset.");
+ return GetRegisterInfo()[reg].name;
+}
+
+lldb::ByteOrder RegisterContextPOSIX_x86::GetByteOrder() {
+ // Get the target process whose privileged thread was used for the register
+ // read.
+ lldb::ByteOrder byte_order = eByteOrderInvalid;
+ Process *process = CalculateProcess().get();
+
+ if (process)
+ byte_order = process->GetByteOrder();
+ return byte_order;
+}
+
+// Parse ymm registers and into xmm.bytes and ymmh.bytes.
+bool RegisterContextPOSIX_x86::CopyYMMtoXSTATE(uint32_t reg,
+ lldb::ByteOrder byte_order) {
+ if (!IsAVX(reg))
+ return false;
+
+ if (byte_order == eByteOrderLittle) {
+ uint32_t reg_no = reg - m_reg_info.first_ymm;
+ YMMToXState(m_ymm_set.ymm[reg_no],
+ m_fpr.fxsave.xmm[reg_no].bytes,
+ m_fpr.xsave.ymmh[reg_no].bytes);
+ return true;
+ }
+
+ return false; // unsupported or invalid byte order
+}
+
+// Concatenate xmm.bytes with ymmh.bytes
+bool RegisterContextPOSIX_x86::CopyXSTATEtoYMM(uint32_t reg,
+ lldb::ByteOrder byte_order) {
+ if (!IsAVX(reg))
+ return false;
+
+ if (byte_order == eByteOrderLittle) {
+ uint32_t reg_no = reg - m_reg_info.first_ymm;
+ m_ymm_set.ymm[reg_no] = XStateToYMM(
+ m_fpr.fxsave.xmm[reg_no].bytes,
+ m_fpr.xsave.ymmh[reg_no].bytes);
+ return true;
+ }
+
+ return false; // unsupported or invalid byte order
+}
+
+bool RegisterContextPOSIX_x86::IsRegisterSetAvailable(size_t set_index) {
+ // Note: Extended register sets are assumed to be at the end of g_reg_sets...
+ size_t num_sets = k_num_register_sets - k_num_extended_register_sets;
+
+ if (GetFPRType() == eXSAVE) // ...and to start with AVX registers.
+ ++num_sets;
+ return (set_index < num_sets);
+}
+
+// Used when parsing DWARF and EH frame information and any other object file
+// sections that contain register numbers in them.
+uint32_t RegisterContextPOSIX_x86::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ const uint32_t num_regs = GetRegisterCount();
+
+ assert(kind < kNumRegisterKinds);
+ for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_idx);
+
+ if (reg_info->kinds[kind] == num)
+ return reg_idx;
+ }
+
+ return LLDB_INVALID_REGNUM;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.h
new file mode 100644
index 000000000000..932f97bb567f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.h
@@ -0,0 +1,176 @@
+//===-- RegisterContextPOSIX_x86.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 liblldb_RegisterContextPOSIX_x86_h_
+#define liblldb_RegisterContextPOSIX_x86_h_
+
+#include "RegisterContext_x86.h"
+#include "RegisterInfoInterface.h"
+#include "lldb-x86-register-enums.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/Log.h"
+
+class ProcessMonitor;
+
+class RegisterContextPOSIX_x86 : public lldb_private::RegisterContext {
+public:
+ RegisterContextPOSIX_x86(lldb_private::Thread &thread,
+ uint32_t concrete_frame_idx,
+ lldb_private::RegisterInfoInterface *register_info);
+
+ ~RegisterContextPOSIX_x86() override;
+
+ void Invalidate();
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ virtual size_t GetGPRSize();
+
+ virtual size_t GetFXSAVEOffset();
+
+ virtual unsigned GetRegisterSize(unsigned reg);
+
+ virtual unsigned GetRegisterOffset(unsigned reg);
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ const char *GetRegisterName(unsigned reg);
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+ // Note: prefer kernel definitions over user-land
+ enum FPRType {
+ eNotValid = 0,
+ eFSAVE, // TODO
+ eFXSAVE,
+ eSOFT, // TODO
+ eXSAVE
+ };
+
+ static uint32_t g_contained_eax[];
+ static uint32_t g_contained_ebx[];
+ static uint32_t g_contained_ecx[];
+ static uint32_t g_contained_edx[];
+ static uint32_t g_contained_edi[];
+ static uint32_t g_contained_esi[];
+ static uint32_t g_contained_ebp[];
+ static uint32_t g_contained_esp[];
+
+ static uint32_t g_invalidate_eax[];
+ static uint32_t g_invalidate_ebx[];
+ static uint32_t g_invalidate_ecx[];
+ static uint32_t g_invalidate_edx[];
+ static uint32_t g_invalidate_edi[];
+ static uint32_t g_invalidate_esi[];
+ static uint32_t g_invalidate_ebp[];
+ static uint32_t g_invalidate_esp[];
+
+ static uint32_t g_contained_rax[];
+ static uint32_t g_contained_rbx[];
+ static uint32_t g_contained_rcx[];
+ static uint32_t g_contained_rdx[];
+ static uint32_t g_contained_rdi[];
+ static uint32_t g_contained_rsi[];
+ static uint32_t g_contained_rbp[];
+ static uint32_t g_contained_rsp[];
+ static uint32_t g_contained_r8[];
+ static uint32_t g_contained_r9[];
+ static uint32_t g_contained_r10[];
+ static uint32_t g_contained_r11[];
+ static uint32_t g_contained_r12[];
+ static uint32_t g_contained_r13[];
+ static uint32_t g_contained_r14[];
+ static uint32_t g_contained_r15[];
+
+ static uint32_t g_invalidate_rax[];
+ static uint32_t g_invalidate_rbx[];
+ static uint32_t g_invalidate_rcx[];
+ static uint32_t g_invalidate_rdx[];
+ static uint32_t g_invalidate_rdi[];
+ static uint32_t g_invalidate_rsi[];
+ static uint32_t g_invalidate_rbp[];
+ static uint32_t g_invalidate_rsp[];
+ static uint32_t g_invalidate_r8[];
+ static uint32_t g_invalidate_r9[];
+ static uint32_t g_invalidate_r10[];
+ static uint32_t g_invalidate_r11[];
+ static uint32_t g_invalidate_r12[];
+ static uint32_t g_invalidate_r13[];
+ static uint32_t g_invalidate_r14[];
+ static uint32_t g_invalidate_r15[];
+
+protected:
+ struct RegInfo {
+ uint32_t num_registers;
+ uint32_t num_gpr_registers;
+ uint32_t num_fpr_registers;
+ uint32_t num_avx_registers;
+
+ uint32_t last_gpr;
+ uint32_t first_fpr;
+ uint32_t last_fpr;
+
+ uint32_t first_st;
+ uint32_t last_st;
+ uint32_t first_mm;
+ uint32_t last_mm;
+ uint32_t first_xmm;
+ uint32_t last_xmm;
+ uint32_t first_ymm;
+ uint32_t last_ymm;
+
+ uint32_t first_dr;
+ uint32_t gpr_flags;
+ };
+
+ uint64_t m_gpr_x86_64[lldb_private::k_num_gpr_registers_x86_64]; // 64-bit
+ // general
+ // purpose
+ // registers.
+ RegInfo m_reg_info;
+ FPRType
+ m_fpr_type; // determines the type of data stored by union FPR, if any.
+ lldb_private::FPR m_fpr; // floating-point registers including extended
+ // register sets.
+ lldb_private::YMM m_ymm_set; // copy of ymmh and xmm register halves.
+ std::unique_ptr<lldb_private::RegisterInfoInterface>
+ m_register_info_up; // Register Info Interface (FreeBSD or Linux)
+
+ // Determines if an extended register set is supported on the processor
+ // running the inferior process.
+ virtual bool IsRegisterSetAvailable(size_t set_index);
+
+ virtual const lldb_private::RegisterInfo *GetRegisterInfo();
+
+ bool IsGPR(unsigned reg);
+
+ bool IsFPR(unsigned reg);
+
+ bool IsAVX(unsigned reg);
+
+ lldb::ByteOrder GetByteOrder();
+
+ bool CopyXSTATEtoYMM(uint32_t reg, lldb::ByteOrder byte_order);
+ bool CopyYMMtoXSTATE(uint32_t reg, lldb::ByteOrder byte_order);
+ bool IsFPR(unsigned reg, FPRType fpr_type);
+ FPRType GetFPRType();
+
+ virtual bool ReadGPR() = 0;
+ virtual bool ReadFPR() = 0;
+ virtual bool WriteGPR() = 0;
+ virtual bool WriteFPR() = 0;
+};
+
+#endif // liblldb_RegisterContextPOSIX_x86_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextThreadMemory.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextThreadMemory.cpp
new file mode 100644
index 000000000000..bcf60cc7a338
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextThreadMemory.cpp
@@ -0,0 +1,216 @@
+//===-- RegisterContextThreadMemory.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 "lldb/Target/OperatingSystem.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-private.h"
+
+#include "RegisterContextThreadMemory.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+RegisterContextThreadMemory::RegisterContextThreadMemory(
+ Thread &thread, lldb::addr_t register_data_addr)
+ : RegisterContext(thread, 0), m_thread_wp(thread.shared_from_this()),
+ m_reg_ctx_sp(), m_register_data_addr(register_data_addr), m_stop_id(0) {}
+
+RegisterContextThreadMemory::~RegisterContextThreadMemory() {}
+
+void RegisterContextThreadMemory::UpdateRegisterContext() {
+ ThreadSP thread_sp(m_thread_wp.lock());
+ if (thread_sp) {
+ ProcessSP process_sp(thread_sp->GetProcess());
+
+ if (process_sp) {
+ const uint32_t stop_id = process_sp->GetModID().GetStopID();
+ if (m_stop_id != stop_id) {
+ m_stop_id = stop_id;
+ m_reg_ctx_sp.reset();
+ }
+ if (!m_reg_ctx_sp) {
+ ThreadSP backing_thread_sp(thread_sp->GetBackingThread());
+ if (backing_thread_sp) {
+ m_reg_ctx_sp = backing_thread_sp->GetRegisterContext();
+ } else {
+ OperatingSystem *os = process_sp->GetOperatingSystem();
+ if (os->IsOperatingSystemPluginThread(thread_sp))
+ m_reg_ctx_sp = os->CreateRegisterContextForThread(
+ thread_sp.get(), m_register_data_addr);
+ }
+ }
+ } else {
+ m_reg_ctx_sp.reset();
+ }
+ } else {
+ m_reg_ctx_sp.reset();
+ }
+}
+
+// Subclasses must override these functions
+void RegisterContextThreadMemory::InvalidateAllRegisters() {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ m_reg_ctx_sp->InvalidateAllRegisters();
+}
+
+size_t RegisterContextThreadMemory::GetRegisterCount() {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->GetRegisterCount();
+ return 0;
+}
+
+const RegisterInfo *
+RegisterContextThreadMemory::GetRegisterInfoAtIndex(size_t reg) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->GetRegisterInfoAtIndex(reg);
+ return nullptr;
+}
+
+size_t RegisterContextThreadMemory::GetRegisterSetCount() {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->GetRegisterSetCount();
+ return 0;
+}
+
+const RegisterSet *RegisterContextThreadMemory::GetRegisterSet(size_t reg_set) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->GetRegisterSet(reg_set);
+ return nullptr;
+}
+
+bool RegisterContextThreadMemory::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->ReadRegister(reg_info, reg_value);
+ return false;
+}
+
+bool RegisterContextThreadMemory::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue &reg_value) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->WriteRegister(reg_info, reg_value);
+ return false;
+}
+
+bool RegisterContextThreadMemory::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->ReadAllRegisterValues(data_sp);
+ return false;
+}
+
+bool RegisterContextThreadMemory::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->WriteAllRegisterValues(data_sp);
+ return false;
+}
+
+bool RegisterContextThreadMemory::CopyFromRegisterContext(
+ lldb::RegisterContextSP reg_ctx_sp) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->CopyFromRegisterContext(reg_ctx_sp);
+ return false;
+}
+
+uint32_t RegisterContextThreadMemory::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->ConvertRegisterKindToRegisterNumber(kind, num);
+ return false;
+}
+
+uint32_t RegisterContextThreadMemory::NumSupportedHardwareBreakpoints() {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->NumSupportedHardwareBreakpoints();
+ return false;
+}
+
+uint32_t RegisterContextThreadMemory::SetHardwareBreakpoint(lldb::addr_t addr,
+ size_t size) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->SetHardwareBreakpoint(addr, size);
+ return 0;
+}
+
+bool RegisterContextThreadMemory::ClearHardwareBreakpoint(uint32_t hw_idx) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->ClearHardwareBreakpoint(hw_idx);
+ return false;
+}
+
+uint32_t RegisterContextThreadMemory::NumSupportedHardwareWatchpoints() {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->NumSupportedHardwareWatchpoints();
+ return 0;
+}
+
+uint32_t RegisterContextThreadMemory::SetHardwareWatchpoint(lldb::addr_t addr,
+ size_t size,
+ bool read,
+ bool write) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->SetHardwareWatchpoint(addr, size, read, write);
+ return 0;
+}
+
+bool RegisterContextThreadMemory::ClearHardwareWatchpoint(uint32_t hw_index) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->ClearHardwareWatchpoint(hw_index);
+ return false;
+}
+
+bool RegisterContextThreadMemory::HardwareSingleStep(bool enable) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->HardwareSingleStep(enable);
+ return false;
+}
+
+Status RegisterContextThreadMemory::ReadRegisterValueFromMemory(
+ const lldb_private::RegisterInfo *reg_info, lldb::addr_t src_addr,
+ uint32_t src_len, RegisterValue &reg_value) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->ReadRegisterValueFromMemory(reg_info, src_addr,
+ src_len, reg_value);
+ Status error;
+ error.SetErrorString("invalid register context");
+ return error;
+}
+
+Status RegisterContextThreadMemory::WriteRegisterValueToMemory(
+ const lldb_private::RegisterInfo *reg_info, lldb::addr_t dst_addr,
+ uint32_t dst_len, const RegisterValue &reg_value) {
+ UpdateRegisterContext();
+ if (m_reg_ctx_sp)
+ return m_reg_ctx_sp->WriteRegisterValueToMemory(reg_info, dst_addr, dst_len,
+ reg_value);
+ Status error;
+ error.SetErrorString("invalid register context");
+ return error;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextThreadMemory.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextThreadMemory.h
new file mode 100644
index 000000000000..09a679ab2c9f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextThreadMemory.h
@@ -0,0 +1,100 @@
+//===-- RegisterContextThreadMemory.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_RegisterContextThreadMemory_h_
+#define lldb_RegisterContextThreadMemory_h_
+
+#include <vector>
+
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class RegisterContextThreadMemory : public lldb_private::RegisterContext {
+public:
+ RegisterContextThreadMemory(Thread &thread, lldb::addr_t register_data_addr);
+
+ ~RegisterContextThreadMemory() override;
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ const RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const RegisterSet *GetRegisterSet(size_t reg_set) override;
+
+ bool ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &reg_value) override;
+
+ bool WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &reg_value) override;
+
+ // These two functions are used to implement "push" and "pop" of register
+ // states. They are used primarily
+ // for expression evaluation, where we need to push a new state (storing the
+ // old one in data_sp) and then
+ // restoring the original state by passing the data_sp we got from
+ // ReadAllRegisters to WriteAllRegisterValues.
+ // ReadAllRegisters will do what is necessary to return a coherent set of
+ // register values for this thread, which
+ // may mean e.g. interrupting a thread that is sitting in a kernel trap. That
+ // is a somewhat disruptive operation,
+ // so these API's should only be used when this behavior is needed.
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ bool CopyFromRegisterContext(lldb::RegisterContextSP context);
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+ uint32_t NumSupportedHardwareBreakpoints() override;
+
+ uint32_t SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override;
+
+ bool ClearHardwareBreakpoint(uint32_t hw_idx) override;
+
+ uint32_t NumSupportedHardwareWatchpoints() override;
+
+ uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read,
+ bool write) override;
+
+ bool ClearHardwareWatchpoint(uint32_t hw_index) override;
+
+ bool HardwareSingleStep(bool enable) override;
+
+ Status ReadRegisterValueFromMemory(const lldb_private::RegisterInfo *reg_info,
+ lldb::addr_t src_addr, uint32_t src_len,
+ RegisterValue &reg_value) override;
+
+ Status WriteRegisterValueToMemory(const lldb_private::RegisterInfo *reg_info,
+ lldb::addr_t dst_addr, uint32_t dst_len,
+ const RegisterValue &reg_value) override;
+
+protected:
+ void UpdateRegisterContext();
+
+ lldb::ThreadWP m_thread_wp;
+ lldb::RegisterContextSP m_reg_ctx_sp;
+ lldb::addr_t m_register_data_addr;
+ uint32_t m_stop_id;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(RegisterContextThreadMemory);
+};
+
+} // namespace lldb_private
+
+#endif // lldb_RegisterContextThreadMemory_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_mips.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_mips.h
new file mode 100644
index 000000000000..7780be51baad
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_mips.h
@@ -0,0 +1,374 @@
+//===-- RegisterContext_mips.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 liblldb_RegisterContext_mips64_H_
+#define liblldb_RegisterContext_mips64_H_
+
+#include <cstddef>
+#include <cstdint>
+
+// eh_frame and DWARF Register numbers (eRegisterKindEHFrame &
+// eRegisterKindDWARF)
+
+enum {
+ // GP Registers
+ dwarf_zero_mips = 0,
+ dwarf_r1_mips,
+ dwarf_r2_mips,
+ dwarf_r3_mips,
+ dwarf_r4_mips,
+ dwarf_r5_mips,
+ dwarf_r6_mips,
+ dwarf_r7_mips,
+ dwarf_r8_mips,
+ dwarf_r9_mips,
+ dwarf_r10_mips,
+ dwarf_r11_mips,
+ dwarf_r12_mips,
+ dwarf_r13_mips,
+ dwarf_r14_mips,
+ dwarf_r15_mips,
+ dwarf_r16_mips,
+ dwarf_r17_mips,
+ dwarf_r18_mips,
+ dwarf_r19_mips,
+ dwarf_r20_mips,
+ dwarf_r21_mips,
+ dwarf_r22_mips,
+ dwarf_r23_mips,
+ dwarf_r24_mips,
+ dwarf_r25_mips,
+ dwarf_r26_mips,
+ dwarf_r27_mips,
+ dwarf_gp_mips,
+ dwarf_sp_mips,
+ dwarf_r30_mips,
+ dwarf_ra_mips,
+ dwarf_sr_mips,
+ dwarf_lo_mips,
+ dwarf_hi_mips,
+ dwarf_bad_mips,
+ dwarf_cause_mips,
+ dwarf_pc_mips,
+ dwarf_f0_mips,
+ dwarf_f1_mips,
+ dwarf_f2_mips,
+ dwarf_f3_mips,
+ dwarf_f4_mips,
+ dwarf_f5_mips,
+ dwarf_f6_mips,
+ dwarf_f7_mips,
+ dwarf_f8_mips,
+ dwarf_f9_mips,
+ dwarf_f10_mips,
+ dwarf_f11_mips,
+ dwarf_f12_mips,
+ dwarf_f13_mips,
+ dwarf_f14_mips,
+ dwarf_f15_mips,
+ dwarf_f16_mips,
+ dwarf_f17_mips,
+ dwarf_f18_mips,
+ dwarf_f19_mips,
+ dwarf_f20_mips,
+ dwarf_f21_mips,
+ dwarf_f22_mips,
+ dwarf_f23_mips,
+ dwarf_f24_mips,
+ dwarf_f25_mips,
+ dwarf_f26_mips,
+ dwarf_f27_mips,
+ dwarf_f28_mips,
+ dwarf_f29_mips,
+ dwarf_f30_mips,
+ dwarf_f31_mips,
+ dwarf_fcsr_mips,
+ dwarf_fir_mips,
+ dwarf_w0_mips,
+ dwarf_w1_mips,
+ dwarf_w2_mips,
+ dwarf_w3_mips,
+ dwarf_w4_mips,
+ dwarf_w5_mips,
+ dwarf_w6_mips,
+ dwarf_w7_mips,
+ dwarf_w8_mips,
+ dwarf_w9_mips,
+ dwarf_w10_mips,
+ dwarf_w11_mips,
+ dwarf_w12_mips,
+ dwarf_w13_mips,
+ dwarf_w14_mips,
+ dwarf_w15_mips,
+ dwarf_w16_mips,
+ dwarf_w17_mips,
+ dwarf_w18_mips,
+ dwarf_w19_mips,
+ dwarf_w20_mips,
+ dwarf_w21_mips,
+ dwarf_w22_mips,
+ dwarf_w23_mips,
+ dwarf_w24_mips,
+ dwarf_w25_mips,
+ dwarf_w26_mips,
+ dwarf_w27_mips,
+ dwarf_w28_mips,
+ dwarf_w29_mips,
+ dwarf_w30_mips,
+ dwarf_w31_mips,
+ dwarf_mcsr_mips,
+ dwarf_mir_mips,
+ dwarf_config5_mips,
+ dwarf_ic_mips,
+ dwarf_dummy_mips
+};
+
+enum {
+ dwarf_zero_mips64 = 0,
+ dwarf_r1_mips64,
+ dwarf_r2_mips64,
+ dwarf_r3_mips64,
+ dwarf_r4_mips64,
+ dwarf_r5_mips64,
+ dwarf_r6_mips64,
+ dwarf_r7_mips64,
+ dwarf_r8_mips64,
+ dwarf_r9_mips64,
+ dwarf_r10_mips64,
+ dwarf_r11_mips64,
+ dwarf_r12_mips64,
+ dwarf_r13_mips64,
+ dwarf_r14_mips64,
+ dwarf_r15_mips64,
+ dwarf_r16_mips64,
+ dwarf_r17_mips64,
+ dwarf_r18_mips64,
+ dwarf_r19_mips64,
+ dwarf_r20_mips64,
+ dwarf_r21_mips64,
+ dwarf_r22_mips64,
+ dwarf_r23_mips64,
+ dwarf_r24_mips64,
+ dwarf_r25_mips64,
+ dwarf_r26_mips64,
+ dwarf_r27_mips64,
+ dwarf_gp_mips64,
+ dwarf_sp_mips64,
+ dwarf_r30_mips64,
+ dwarf_ra_mips64,
+ dwarf_sr_mips64,
+ dwarf_lo_mips64,
+ dwarf_hi_mips64,
+ dwarf_bad_mips64,
+ dwarf_cause_mips64,
+ dwarf_pc_mips64,
+ dwarf_f0_mips64,
+ dwarf_f1_mips64,
+ dwarf_f2_mips64,
+ dwarf_f3_mips64,
+ dwarf_f4_mips64,
+ dwarf_f5_mips64,
+ dwarf_f6_mips64,
+ dwarf_f7_mips64,
+ dwarf_f8_mips64,
+ dwarf_f9_mips64,
+ dwarf_f10_mips64,
+ dwarf_f11_mips64,
+ dwarf_f12_mips64,
+ dwarf_f13_mips64,
+ dwarf_f14_mips64,
+ dwarf_f15_mips64,
+ dwarf_f16_mips64,
+ dwarf_f17_mips64,
+ dwarf_f18_mips64,
+ dwarf_f19_mips64,
+ dwarf_f20_mips64,
+ dwarf_f21_mips64,
+ dwarf_f22_mips64,
+ dwarf_f23_mips64,
+ dwarf_f24_mips64,
+ dwarf_f25_mips64,
+ dwarf_f26_mips64,
+ dwarf_f27_mips64,
+ dwarf_f28_mips64,
+ dwarf_f29_mips64,
+ dwarf_f30_mips64,
+ dwarf_f31_mips64,
+ dwarf_fcsr_mips64,
+ dwarf_fir_mips64,
+ dwarf_ic_mips64,
+ dwarf_dummy_mips64,
+ dwarf_w0_mips64,
+ dwarf_w1_mips64,
+ dwarf_w2_mips64,
+ dwarf_w3_mips64,
+ dwarf_w4_mips64,
+ dwarf_w5_mips64,
+ dwarf_w6_mips64,
+ dwarf_w7_mips64,
+ dwarf_w8_mips64,
+ dwarf_w9_mips64,
+ dwarf_w10_mips64,
+ dwarf_w11_mips64,
+ dwarf_w12_mips64,
+ dwarf_w13_mips64,
+ dwarf_w14_mips64,
+ dwarf_w15_mips64,
+ dwarf_w16_mips64,
+ dwarf_w17_mips64,
+ dwarf_w18_mips64,
+ dwarf_w19_mips64,
+ dwarf_w20_mips64,
+ dwarf_w21_mips64,
+ dwarf_w22_mips64,
+ dwarf_w23_mips64,
+ dwarf_w24_mips64,
+ dwarf_w25_mips64,
+ dwarf_w26_mips64,
+ dwarf_w27_mips64,
+ dwarf_w28_mips64,
+ dwarf_w29_mips64,
+ dwarf_w30_mips64,
+ dwarf_w31_mips64,
+ dwarf_mcsr_mips64,
+ dwarf_mir_mips64,
+ dwarf_config5_mips64,
+};
+
+// GP registers
+struct GPR_linux_mips {
+ uint64_t zero;
+ uint64_t r1;
+ uint64_t r2;
+ uint64_t r3;
+ uint64_t r4;
+ uint64_t r5;
+ uint64_t r6;
+ uint64_t r7;
+ 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 r16;
+ uint64_t r17;
+ uint64_t r18;
+ uint64_t r19;
+ uint64_t r20;
+ uint64_t r21;
+ uint64_t r22;
+ uint64_t r23;
+ uint64_t r24;
+ uint64_t r25;
+ uint64_t r26;
+ uint64_t r27;
+ uint64_t gp;
+ uint64_t sp;
+ uint64_t r30;
+ uint64_t ra;
+ uint64_t mullo;
+ uint64_t mulhi;
+ uint64_t pc;
+ uint64_t badvaddr;
+ uint64_t sr;
+ uint64_t cause;
+ uint64_t config5;
+};
+
+struct FPR_linux_mips {
+ uint64_t f0;
+ uint64_t f1;
+ uint64_t f2;
+ uint64_t f3;
+ uint64_t f4;
+ uint64_t f5;
+ uint64_t f6;
+ uint64_t f7;
+ uint64_t f8;
+ uint64_t f9;
+ uint64_t f10;
+ uint64_t f11;
+ uint64_t f12;
+ uint64_t f13;
+ uint64_t f14;
+ uint64_t f15;
+ uint64_t f16;
+ uint64_t f17;
+ uint64_t f18;
+ uint64_t f19;
+ uint64_t f20;
+ uint64_t f21;
+ uint64_t f22;
+ uint64_t f23;
+ uint64_t f24;
+ uint64_t f25;
+ uint64_t f26;
+ uint64_t f27;
+ uint64_t f28;
+ uint64_t f29;
+ uint64_t f30;
+ uint64_t f31;
+ uint32_t fcsr;
+ uint32_t fir;
+ uint32_t config5;
+};
+
+struct MSAReg {
+ uint8_t byte[16];
+};
+
+struct MSA_linux_mips {
+ MSAReg w0;
+ MSAReg w1;
+ MSAReg w2;
+ MSAReg w3;
+ MSAReg w4;
+ MSAReg w5;
+ MSAReg w6;
+ MSAReg w7;
+ MSAReg w8;
+ MSAReg w9;
+ MSAReg w10;
+ MSAReg w11;
+ MSAReg w12;
+ MSAReg w13;
+ MSAReg w14;
+ MSAReg w15;
+ MSAReg w16;
+ MSAReg w17;
+ MSAReg w18;
+ MSAReg w19;
+ MSAReg w20;
+ MSAReg w21;
+ MSAReg w22;
+ MSAReg w23;
+ MSAReg w24;
+ MSAReg w25;
+ MSAReg w26;
+ MSAReg w27;
+ MSAReg w28;
+ MSAReg w29;
+ MSAReg w30;
+ MSAReg w31;
+ uint32_t fcsr; /* FPU control status register */
+ uint32_t fir; /* FPU implementaion revision */
+ uint32_t mcsr; /* MSA control status register */
+ uint32_t mir; /* MSA implementation revision */
+ uint32_t config5; /* Config5 register */
+};
+
+struct UserArea {
+ GPR_linux_mips gpr; // General purpose registers.
+ FPR_linux_mips fpr; // Floating point registers.
+ MSA_linux_mips msa; // MSA registers.
+};
+
+#endif // liblldb_RegisterContext_mips64_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_powerpc.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_powerpc.h
new file mode 100644
index 000000000000..1ffcbeb5ec48
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_powerpc.h
@@ -0,0 +1,123 @@
+//===-- RegisterContext_powerpc.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 liblldb_RegisterContext_powerpc_H_
+#define liblldb_RegisterContext_powerpc_H_
+
+// eh_frame and DWARF Register numbers (eRegisterKindEHFrame &
+// eRegisterKindDWARF)
+enum {
+ dwarf_r0_powerpc = 0,
+ dwarf_r1_powerpc,
+ dwarf_r2_powerpc,
+ dwarf_r3_powerpc,
+ dwarf_r4_powerpc,
+ dwarf_r5_powerpc,
+ dwarf_r6_powerpc,
+ dwarf_r7_powerpc,
+ dwarf_r8_powerpc,
+ dwarf_r9_powerpc,
+ dwarf_r10_powerpc,
+ dwarf_r11_powerpc,
+ dwarf_r12_powerpc,
+ dwarf_r13_powerpc,
+ dwarf_r14_powerpc,
+ dwarf_r15_powerpc,
+ dwarf_r16_powerpc,
+ dwarf_r17_powerpc,
+ dwarf_r18_powerpc,
+ dwarf_r19_powerpc,
+ dwarf_r20_powerpc,
+ dwarf_r21_powerpc,
+ dwarf_r22_powerpc,
+ dwarf_r23_powerpc,
+ dwarf_r24_powerpc,
+ dwarf_r25_powerpc,
+ dwarf_r26_powerpc,
+ dwarf_r27_powerpc,
+ dwarf_r28_powerpc,
+ dwarf_r29_powerpc,
+ dwarf_r30_powerpc,
+ dwarf_r31_powerpc,
+ dwarf_f0_powerpc,
+ dwarf_f1_powerpc,
+ dwarf_f2_powerpc,
+ dwarf_f3_powerpc,
+ dwarf_f4_powerpc,
+ dwarf_f5_powerpc,
+ dwarf_f6_powerpc,
+ dwarf_f7_powerpc,
+ dwarf_f8_powerpc,
+ dwarf_f9_powerpc,
+ dwarf_f10_powerpc,
+ dwarf_f11_powerpc,
+ dwarf_f12_powerpc,
+ dwarf_f13_powerpc,
+ dwarf_f14_powerpc,
+ dwarf_f15_powerpc,
+ dwarf_f16_powerpc,
+ dwarf_f17_powerpc,
+ dwarf_f18_powerpc,
+ dwarf_f19_powerpc,
+ dwarf_f20_powerpc,
+ dwarf_f21_powerpc,
+ dwarf_f22_powerpc,
+ dwarf_f23_powerpc,
+ dwarf_f24_powerpc,
+ dwarf_f25_powerpc,
+ dwarf_f26_powerpc,
+ dwarf_f27_powerpc,
+ dwarf_f28_powerpc,
+ dwarf_f29_powerpc,
+ dwarf_f30_powerpc,
+ dwarf_f31_powerpc,
+ dwarf_cr_powerpc,
+ dwarf_fpscr_powerpc,
+ dwarf_msr_powerpc,
+ dwarf_vscr_powerpc,
+ dwarf_xer_powerpc = 101,
+ dwarf_lr_powerpc = 108,
+ dwarf_ctr_powerpc,
+ dwarf_pc_powerpc,
+ dwarf_vrsave_powerpc = 356,
+ dwarf_v0_powerpc = 1124,
+ dwarf_v1_powerpc,
+ dwarf_v2_powerpc,
+ dwarf_v3_powerpc,
+ dwarf_v4_powerpc,
+ dwarf_v5_powerpc,
+ dwarf_v6_powerpc,
+ dwarf_v7_powerpc,
+ dwarf_v8_powerpc,
+ dwarf_v9_powerpc,
+ dwarf_v10_powerpc,
+ dwarf_v11_powerpc,
+ dwarf_v12_powerpc,
+ dwarf_v13_powerpc,
+ dwarf_v14_powerpc,
+ dwarf_v15_powerpc,
+ dwarf_v16_powerpc,
+ dwarf_v17_powerpc,
+ dwarf_v18_powerpc,
+ dwarf_v19_powerpc,
+ dwarf_v20_powerpc,
+ dwarf_v21_powerpc,
+ dwarf_v22_powerpc,
+ dwarf_v23_powerpc,
+ dwarf_v24_powerpc,
+ dwarf_v25_powerpc,
+ dwarf_v26_powerpc,
+ dwarf_v27_powerpc,
+ dwarf_v28_powerpc,
+ dwarf_v29_powerpc,
+ dwarf_v30_powerpc,
+ dwarf_v31_powerpc,
+};
+
+#endif // liblldb_RegisterContext_powerpc_H_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_s390x.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_s390x.h
new file mode 100644
index 000000000000..2cf39e9eb8e2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_s390x.h
@@ -0,0 +1,90 @@
+//===-- RegisterContext_s390x.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 liblldb_RegisterContext_s390x_h_
+#define liblldb_RegisterContext_s390x_h_
+
+// SystemZ ehframe, dwarf regnums
+
+// EHFrame and DWARF Register numbers (eRegisterKindEHFrame &
+// eRegisterKindDWARF)
+enum {
+ // General Purpose Registers
+ dwarf_r0_s390x = 0,
+ dwarf_r1_s390x,
+ dwarf_r2_s390x,
+ dwarf_r3_s390x,
+ dwarf_r4_s390x,
+ dwarf_r5_s390x,
+ dwarf_r6_s390x,
+ dwarf_r7_s390x,
+ dwarf_r8_s390x,
+ dwarf_r9_s390x,
+ dwarf_r10_s390x,
+ dwarf_r11_s390x,
+ dwarf_r12_s390x,
+ dwarf_r13_s390x,
+ dwarf_r14_s390x,
+ dwarf_r15_s390x,
+ // Floating Point Registers / Vector Registers 0-15
+ dwarf_f0_s390x = 16,
+ dwarf_f2_s390x,
+ dwarf_f4_s390x,
+ dwarf_f6_s390x,
+ dwarf_f1_s390x,
+ dwarf_f3_s390x,
+ dwarf_f5_s390x,
+ dwarf_f7_s390x,
+ dwarf_f8_s390x,
+ dwarf_f10_s390x,
+ dwarf_f12_s390x,
+ dwarf_f14_s390x,
+ dwarf_f9_s390x,
+ dwarf_f11_s390x,
+ dwarf_f13_s390x,
+ dwarf_f15_s390x,
+ // Access Registers
+ dwarf_acr0_s390x = 48,
+ dwarf_acr1_s390x,
+ dwarf_acr2_s390x,
+ dwarf_acr3_s390x,
+ dwarf_acr4_s390x,
+ dwarf_acr5_s390x,
+ dwarf_acr6_s390x,
+ dwarf_acr7_s390x,
+ dwarf_acr8_s390x,
+ dwarf_acr9_s390x,
+ dwarf_acr10_s390x,
+ dwarf_acr11_s390x,
+ dwarf_acr12_s390x,
+ dwarf_acr13_s390x,
+ dwarf_acr14_s390x,
+ dwarf_acr15_s390x,
+ // Program Status Word
+ dwarf_pswm_s390x = 64,
+ dwarf_pswa_s390x,
+ // Vector Registers 16-31
+ dwarf_v16_s390x = 68,
+ dwarf_v18_s390x,
+ dwarf_v20_s390x,
+ dwarf_v22_s390x,
+ dwarf_v17_s390x,
+ dwarf_v19_s390x,
+ dwarf_v21_s390x,
+ dwarf_v23_s390x,
+ dwarf_v24_s390x,
+ dwarf_v26_s390x,
+ dwarf_v28_s390x,
+ dwarf_v30_s390x,
+ dwarf_v25_s390x,
+ dwarf_v27_s390x,
+ dwarf_v29_s390x,
+ dwarf_v31_s390x,
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_x86.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_x86.h
new file mode 100644
index 000000000000..2b79f778aa56
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_x86.h
@@ -0,0 +1,374 @@
+//===-- RegisterContext_x86.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 liblldb_RegisterContext_x86_H_
+#define liblldb_RegisterContext_x86_H_
+
+#include <cstddef>
+#include <cstdint>
+
+#include "llvm/ADT/BitmaskEnum.h"
+#include "llvm/Support/Compiler.h"
+
+namespace lldb_private {
+// i386 ehframe, dwarf regnums
+
+// Register numbers seen in eh_frame (eRegisterKindEHFrame) on i386 systems
+// (non-Darwin)
+//
+enum {
+ ehframe_eax_i386 = 0,
+ ehframe_ecx_i386,
+ ehframe_edx_i386,
+ ehframe_ebx_i386,
+
+ // on Darwin esp & ebp are reversed in the eh_frame section for i386 (versus
+ // dwarf's reg numbering).
+ // To be specific:
+ // i386+darwin eh_frame: 4 is ebp, 5 is esp
+ // i386+everyone else eh_frame: 4 is esp, 5 is ebp
+ // i386 dwarf: 4 is esp, 5 is ebp
+ // lldb will get the darwin-specific eh_frame reg numberings from debugserver,
+ // or the ABI, so we
+ // only encode the generally correct 4 == esp, 5 == ebp numbers in this
+ // generic header.
+
+ ehframe_esp_i386,
+ ehframe_ebp_i386,
+ ehframe_esi_i386,
+ ehframe_edi_i386,
+ ehframe_eip_i386,
+ ehframe_eflags_i386,
+ ehframe_st0_i386 = 12,
+ ehframe_st1_i386,
+ ehframe_st2_i386,
+ ehframe_st3_i386,
+ ehframe_st4_i386,
+ ehframe_st5_i386,
+ ehframe_st6_i386,
+ ehframe_st7_i386,
+ ehframe_xmm0_i386 = 21,
+ ehframe_xmm1_i386,
+ ehframe_xmm2_i386,
+ ehframe_xmm3_i386,
+ ehframe_xmm4_i386,
+ ehframe_xmm5_i386,
+ ehframe_xmm6_i386,
+ ehframe_xmm7_i386,
+ ehframe_mm0_i386 = 29,
+ ehframe_mm1_i386,
+ ehframe_mm2_i386,
+ ehframe_mm3_i386,
+ ehframe_mm4_i386,
+ ehframe_mm5_i386,
+ ehframe_mm6_i386,
+ ehframe_mm7_i386,
+};
+
+// DWARF register numbers (eRegisterKindDWARF)
+// Intel's x86 or IA-32
+enum {
+ // General Purpose Registers.
+ dwarf_eax_i386 = 0,
+ dwarf_ecx_i386,
+ dwarf_edx_i386,
+ dwarf_ebx_i386,
+ dwarf_esp_i386,
+ dwarf_ebp_i386,
+ dwarf_esi_i386,
+ dwarf_edi_i386,
+ dwarf_eip_i386,
+ dwarf_eflags_i386,
+ // Floating Point Registers
+ dwarf_st0_i386 = 11,
+ dwarf_st1_i386,
+ dwarf_st2_i386,
+ dwarf_st3_i386,
+ dwarf_st4_i386,
+ dwarf_st5_i386,
+ dwarf_st6_i386,
+ dwarf_st7_i386,
+ // SSE Registers
+ dwarf_xmm0_i386 = 21,
+ dwarf_xmm1_i386,
+ dwarf_xmm2_i386,
+ dwarf_xmm3_i386,
+ dwarf_xmm4_i386,
+ dwarf_xmm5_i386,
+ dwarf_xmm6_i386,
+ dwarf_xmm7_i386,
+ // MMX Registers
+ dwarf_mm0_i386 = 29,
+ dwarf_mm1_i386,
+ dwarf_mm2_i386,
+ dwarf_mm3_i386,
+ dwarf_mm4_i386,
+ dwarf_mm5_i386,
+ dwarf_mm6_i386,
+ dwarf_mm7_i386,
+ dwarf_fctrl_i386 = 37, // x87 control word
+ dwarf_fstat_i386 = 38, // x87 status word
+ dwarf_mxcsr_i386 = 39,
+ dwarf_es_i386 = 40,
+ dwarf_cs_i386 = 41,
+ dwarf_ss_i386 = 42,
+ dwarf_ds_i386 = 43,
+ dwarf_fs_i386 = 44,
+ dwarf_gs_i386 = 45,
+
+ // I believe the ymm registers use the dwarf_xmm%_i386 register numbers and
+ // then differentiate based on size of the register.
+ dwarf_bnd0_i386 = 101,
+ dwarf_bnd1_i386,
+ dwarf_bnd2_i386,
+ dwarf_bnd3_i386,
+};
+
+// AMD x86_64, AMD64, Intel EM64T, or Intel 64 ehframe, dwarf regnums
+
+// EHFrame and DWARF Register numbers (eRegisterKindEHFrame &
+// eRegisterKindDWARF)
+// This is the spec I used (as opposed to x86-64-abi-0.99.pdf):
+// http://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf
+enum {
+ // GP Registers
+ dwarf_rax_x86_64 = 0,
+ dwarf_rdx_x86_64,
+ dwarf_rcx_x86_64,
+ dwarf_rbx_x86_64,
+ dwarf_rsi_x86_64,
+ dwarf_rdi_x86_64,
+ dwarf_rbp_x86_64,
+ dwarf_rsp_x86_64,
+ // Extended GP Registers
+ dwarf_r8_x86_64 = 8,
+ dwarf_r9_x86_64,
+ dwarf_r10_x86_64,
+ dwarf_r11_x86_64,
+ dwarf_r12_x86_64,
+ dwarf_r13_x86_64,
+ dwarf_r14_x86_64,
+ dwarf_r15_x86_64,
+ // Return Address (RA) mapped to RIP
+ dwarf_rip_x86_64 = 16,
+ // SSE Vector Registers
+ dwarf_xmm0_x86_64 = 17,
+ dwarf_xmm1_x86_64,
+ dwarf_xmm2_x86_64,
+ dwarf_xmm3_x86_64,
+ dwarf_xmm4_x86_64,
+ dwarf_xmm5_x86_64,
+ dwarf_xmm6_x86_64,
+ dwarf_xmm7_x86_64,
+ dwarf_xmm8_x86_64,
+ dwarf_xmm9_x86_64,
+ dwarf_xmm10_x86_64,
+ dwarf_xmm11_x86_64,
+ dwarf_xmm12_x86_64,
+ dwarf_xmm13_x86_64,
+ dwarf_xmm14_x86_64,
+ dwarf_xmm15_x86_64,
+ // Floating Point Registers
+ dwarf_st0_x86_64 = 33,
+ dwarf_st1_x86_64,
+ dwarf_st2_x86_64,
+ dwarf_st3_x86_64,
+ dwarf_st4_x86_64,
+ dwarf_st5_x86_64,
+ dwarf_st6_x86_64,
+ dwarf_st7_x86_64,
+ // MMX Registers
+ dwarf_mm0_x86_64 = 41,
+ dwarf_mm1_x86_64,
+ dwarf_mm2_x86_64,
+ dwarf_mm3_x86_64,
+ dwarf_mm4_x86_64,
+ dwarf_mm5_x86_64,
+ dwarf_mm6_x86_64,
+ dwarf_mm7_x86_64,
+ // Control and Status Flags Register
+ dwarf_rflags_x86_64 = 49,
+ // selector registers
+ dwarf_es_x86_64 = 50,
+ dwarf_cs_x86_64,
+ dwarf_ss_x86_64,
+ dwarf_ds_x86_64,
+ dwarf_fs_x86_64,
+ dwarf_gs_x86_64,
+ // Floating point control registers
+ dwarf_mxcsr_x86_64 = 64, // Media Control and Status
+ dwarf_fctrl_x86_64, // x87 control word
+ dwarf_fstat_x86_64, // x87 status word
+ // Upper Vector Registers
+ dwarf_ymm0h_x86_64 = 67,
+ dwarf_ymm1h_x86_64,
+ dwarf_ymm2h_x86_64,
+ dwarf_ymm3h_x86_64,
+ dwarf_ymm4h_x86_64,
+ dwarf_ymm5h_x86_64,
+ dwarf_ymm6h_x86_64,
+ dwarf_ymm7h_x86_64,
+ dwarf_ymm8h_x86_64,
+ dwarf_ymm9h_x86_64,
+ dwarf_ymm10h_x86_64,
+ dwarf_ymm11h_x86_64,
+ dwarf_ymm12h_x86_64,
+ dwarf_ymm13h_x86_64,
+ dwarf_ymm14h_x86_64,
+ dwarf_ymm15h_x86_64,
+ // MPX registers
+ dwarf_bnd0_x86_64 = 126,
+ dwarf_bnd1_x86_64,
+ dwarf_bnd2_x86_64,
+ dwarf_bnd3_x86_64,
+ // AVX2 Vector Mask Registers
+ // dwarf_k0_x86_64 = 118,
+ // dwarf_k1_x86_64,
+ // dwarf_k2_x86_64,
+ // dwarf_k3_x86_64,
+ // dwarf_k4_x86_64,
+ // dwarf_k5_x86_64,
+ // dwarf_k6_x86_64,
+ // dwarf_k7_x86_64,
+};
+
+// Generic floating-point registers
+
+struct MMSReg {
+ uint8_t bytes[10];
+ uint8_t pad[6];
+};
+
+struct XMMReg {
+ uint8_t bytes[16]; // 128-bits for each XMM register
+};
+
+// i387_fxsave_struct
+struct FXSAVE {
+ uint16_t fctrl; // FPU Control Word (fcw)
+ uint16_t fstat; // FPU Status Word (fsw)
+ uint16_t ftag; // FPU Tag Word (ftw)
+ uint16_t fop; // Last Instruction Opcode (fop)
+ union {
+ struct {
+ uint64_t fip; // Instruction Pointer
+ uint64_t fdp; // Data Pointer
+ } x86_64;
+ struct {
+ uint32_t fioff; // FPU IP Offset (fip)
+ uint32_t fiseg; // FPU IP Selector (fcs)
+ uint32_t fooff; // FPU Operand Pointer Offset (foo)
+ uint32_t foseg; // FPU Operand Pointer Selector (fos)
+ } i386_; // Added _ in the end to avoid error with gcc defining i386 in some
+ // cases
+ } ptr;
+ uint32_t mxcsr; // MXCSR Register State
+ uint32_t mxcsrmask; // MXCSR Mask
+ MMSReg stmm[8]; // 8*16 bytes for each FP-reg = 128 bytes
+ XMMReg xmm[16]; // 16*16 bytes for each XMM-reg = 256 bytes
+ uint8_t padding1[48];
+ uint64_t xcr0;
+ uint8_t padding2[40];
+};
+
+// Extended floating-point registers
+
+struct YMMHReg {
+ uint8_t bytes[16]; // 16 * 8 bits for the high bytes of each YMM register
+};
+
+struct YMMReg {
+ uint8_t bytes[32]; // 16 * 16 bits for each YMM register
+};
+
+struct YMM {
+ YMMReg ymm[16]; // assembled from ymmh and xmm registers
+};
+
+struct MPXReg {
+ uint8_t bytes[16]; // MPX 128 bit bound registers
+};
+
+struct MPXCsr {
+ uint8_t bytes[8]; // MPX 64 bit bndcfgu and bndstatus registers (collectively
+ // BNDCSR state)
+};
+
+struct MPX {
+ MPXReg mpxr[4];
+ MPXCsr mpxc[2];
+};
+
+LLVM_PACKED_START
+struct XSAVE_HDR {
+ enum class XFeature : uint64_t {
+ FP = 1,
+ SSE = FP << 1,
+ YMM = SSE << 1,
+ BNDREGS = YMM << 1,
+ BNDCSR = BNDREGS << 1,
+ OPMASK = BNDCSR << 1,
+ ZMM_Hi256 = OPMASK << 1,
+ Hi16_ZMM = ZMM_Hi256 << 1,
+ PT = Hi16_ZMM << 1,
+ PKRU = PT << 1,
+ LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue*/ PKRU)
+ };
+
+ XFeature xstate_bv; // OS enabled xstate mask to determine the extended states
+ // supported by the processor
+ XFeature xcomp_bv; // Mask to indicate the format of the XSAVE area and of
+ // the XRSTOR instruction
+ uint64_t reserved1[1];
+ uint64_t reserved2[5];
+};
+static_assert(sizeof(XSAVE_HDR) == 64, "XSAVE_HDR layout incorrect");
+LLVM_PACKED_END
+
+// x86 extensions to FXSAVE (i.e. for AVX and MPX processors)
+LLVM_PACKED_START
+struct XSAVE {
+ FXSAVE i387; // floating point registers typical in i387_fxsave_struct
+ XSAVE_HDR header; // The xsave_hdr_struct can be used to determine if the
+ // following extensions are usable
+ YMMHReg ymmh[16]; // High 16 bytes of each of 16 YMM registers (the low bytes
+ // are in FXSAVE.xmm for compatibility with SSE)
+ uint64_t reserved3[16];
+ MPXReg mpxr[4]; // MPX BNDREG state, containing 128-bit bound registers
+ MPXCsr mpxc[2]; // MPX BNDCSR state, containing 64-bit BNDCFGU and
+ // BNDSTATUS registers
+};
+LLVM_PACKED_END
+
+// Floating-point registers
+union FPR {
+ FXSAVE fxsave; // Generic floating-point registers.
+ XSAVE xsave; // x86 extended processor state.
+};
+
+LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
+
+// Convenience function to combine YMM register data from XSAVE-style input.
+inline YMMReg XStateToYMM(const void* xmm_bytes, const void* ymmh_bytes) {
+ YMMReg ret;
+
+ ::memcpy(ret.bytes, xmm_bytes, sizeof(XMMReg));
+ ::memcpy(ret.bytes + sizeof(XMMReg), ymmh_bytes, sizeof(YMMHReg));
+
+ return ret;
+}
+
+// Convenience function to copy YMM register data into XSAVE-style output.
+inline void YMMToXState(const YMMReg& input, void* xmm_bytes, void* ymmh_bytes) {
+ ::memcpy(xmm_bytes, input.bytes, sizeof(XMMReg));
+ ::memcpy(ymmh_bytes, input.bytes + sizeof(XMMReg), sizeof(YMMHReg));
+}
+
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoInterface.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoInterface.h
new file mode 100644
index 000000000000..4b58e749adce
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoInterface.h
@@ -0,0 +1,70 @@
+//===-- RegisterInfoInterface.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_RegisterInfoInterface_h
+#define lldb_RegisterInfoInterface_h
+
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/lldb-private-types.h"
+#include <vector>
+
+namespace lldb_private {
+
+/// \class RegisterInfoInterface
+///
+/// RegisterInfo interface to patch RegisterInfo structure for archs.
+class RegisterInfoInterface {
+public:
+ RegisterInfoInterface(const lldb_private::ArchSpec &target_arch)
+ : m_target_arch(target_arch) {}
+ virtual ~RegisterInfoInterface() {}
+
+ virtual size_t GetGPRSize() const = 0;
+
+ virtual const lldb_private::RegisterInfo *GetRegisterInfo() const = 0;
+
+ // Returns the number of registers including the user registers and the
+ // lldb internal registers also
+ virtual uint32_t GetRegisterCount() const = 0;
+
+ // Returns the number of the user registers (excluding the registers
+ // kept for lldb internal use only). Subclasses should override it if
+ // they belongs to an architecture with lldb internal registers.
+ virtual uint32_t GetUserRegisterCount() const { return GetRegisterCount(); }
+
+ const lldb_private::ArchSpec &GetTargetArchitecture() const {
+ return m_target_arch;
+ }
+
+ virtual const lldb_private::RegisterInfo *
+ GetDynamicRegisterInfo(const char *reg_name) const {
+ const std::vector<lldb_private::RegisterInfo> *d_register_infos =
+ GetDynamicRegisterInfoP();
+ if (d_register_infos != nullptr) {
+ std::vector<lldb_private::RegisterInfo>::const_iterator pos =
+ d_register_infos->begin();
+ for (; pos < d_register_infos->end(); pos++) {
+ if (::strcmp(reg_name, pos->name) == 0)
+ return (d_register_infos->data() + (pos - d_register_infos->begin()));
+ }
+ }
+ return nullptr;
+ }
+
+ virtual const std::vector<lldb_private::RegisterInfo> *
+ GetDynamicRegisterInfoP() const {
+ return nullptr;
+ }
+
+public:
+ // FIXME make private.
+ lldb_private::ArchSpec m_target_arch;
+};
+}
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp
new file mode 100644
index 000000000000..d392d3be1c41
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp
@@ -0,0 +1,91 @@
+//===-- RegisterInfoPOSIX_arm.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 <cassert>
+#include <stddef.h>
+#include <vector>
+
+#include "lldb/lldb-defines.h"
+#include "llvm/Support/Compiler.h"
+
+#include "RegisterInfoPOSIX_arm.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Based on RegisterContextDarwin_arm.cpp
+#define GPR_OFFSET(idx) ((idx)*4)
+#define FPU_OFFSET(idx) ((idx)*4 + sizeof(RegisterInfoPOSIX_arm::GPR))
+#define FPSCR_OFFSET \
+ (LLVM_EXTENSION offsetof(RegisterInfoPOSIX_arm::FPU, fpscr) + \
+ sizeof(RegisterInfoPOSIX_arm::GPR))
+#define EXC_OFFSET(idx) \
+ ((idx)*4 + sizeof(RegisterInfoPOSIX_arm::GPR) + \
+ sizeof(RegisterInfoPOSIX_arm::FPU))
+#define DBG_OFFSET(reg) \
+ ((LLVM_EXTENSION offsetof(RegisterInfoPOSIX_arm::DBG, reg) + \
+ sizeof(RegisterInfoPOSIX_arm::GPR) + sizeof(RegisterInfoPOSIX_arm::FPU) + \
+ sizeof(RegisterInfoPOSIX_arm::EXC)))
+
+#define DEFINE_DBG(reg, i) \
+ #reg, NULL, sizeof(((RegisterInfoPOSIX_arm::DBG *) NULL)->reg[i]), \
+ DBG_OFFSET(reg[i]), eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ dbg_##reg##i }, \
+ NULL, NULL, NULL, 0
+#define REG_CONTEXT_SIZE \
+ (sizeof(RegisterInfoPOSIX_arm::GPR) + sizeof(RegisterInfoPOSIX_arm::FPU) + \
+ sizeof(RegisterInfoPOSIX_arm::EXC))
+
+// Include RegisterInfos_arm to declare our g_register_infos_arm structure.
+#define DECLARE_REGISTER_INFOS_ARM_STRUCT
+#include "RegisterInfos_arm.h"
+#undef DECLARE_REGISTER_INFOS_ARM_STRUCT
+
+static const lldb_private::RegisterInfo *
+GetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::arm:
+ return g_register_infos_arm;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+static uint32_t
+GetRegisterInfoCount(const lldb_private::ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::arm:
+ return static_cast<uint32_t>(sizeof(g_register_infos_arm) /
+ sizeof(g_register_infos_arm[0]));
+ default:
+ assert(false && "Unhandled target architecture.");
+ return 0;
+ }
+}
+
+RegisterInfoPOSIX_arm::RegisterInfoPOSIX_arm(
+ const lldb_private::ArchSpec &target_arch)
+ : lldb_private::RegisterInfoInterface(target_arch),
+ m_register_info_p(GetRegisterInfoPtr(target_arch)),
+ m_register_info_count(GetRegisterInfoCount(target_arch)) {}
+
+size_t RegisterInfoPOSIX_arm::GetGPRSize() const {
+ return sizeof(struct RegisterInfoPOSIX_arm::GPR);
+}
+
+const lldb_private::RegisterInfo *
+RegisterInfoPOSIX_arm::GetRegisterInfo() const {
+ return m_register_info_p;
+}
+
+uint32_t RegisterInfoPOSIX_arm::GetRegisterCount() const {
+ return m_register_info_count;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.h
new file mode 100644
index 000000000000..39c2047600aa
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.h
@@ -0,0 +1,61 @@
+//===-- RegisterInfoPOSIX_arm.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 liblldb_RegisterInfoPOSIX_arm_h_
+#define liblldb_RegisterInfoPOSIX_arm_h_
+
+#include "RegisterInfoInterface.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/lldb-private.h"
+
+class RegisterInfoPOSIX_arm : public lldb_private::RegisterInfoInterface {
+public:
+ struct GPR {
+ uint32_t r[16]; // R0-R15
+ uint32_t cpsr; // CPSR
+ };
+
+ struct QReg {
+ uint8_t bytes[16];
+ };
+
+ struct FPU {
+ union {
+ uint32_t s[32];
+ uint64_t d[32];
+ QReg q[16]; // the 128-bit NEON registers
+ } floats;
+ uint32_t fpscr;
+ };
+ struct EXC {
+ uint32_t exception;
+ uint32_t fsr; /* Fault status */
+ uint32_t far; /* Virtual Fault Address */
+ };
+
+ struct DBG {
+ uint32_t bvr[16];
+ uint32_t bcr[16];
+ uint32_t wvr[16];
+ uint32_t wcr[16];
+ };
+
+ RegisterInfoPOSIX_arm(const lldb_private::ArchSpec &target_arch);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+
+private:
+ const lldb_private::RegisterInfo *m_register_info_p;
+ uint32_t m_register_info_count;
+};
+
+#endif // liblldb_RegisterInfoPOSIX_arm_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
new file mode 100644
index 000000000000..f7471526d054
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
@@ -0,0 +1,96 @@
+//===-- RegisterInfoPOSIX_arm64.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 <cassert>
+#include <stddef.h>
+#include <vector>
+
+#include "lldb/lldb-defines.h"
+#include "llvm/Support/Compiler.h"
+
+#include "RegisterInfoPOSIX_arm64.h"
+
+// Based on RegisterContextDarwin_arm64.cpp
+#define GPR_OFFSET(idx) ((idx)*8)
+#define GPR_OFFSET_NAME(reg) \
+ (LLVM_EXTENSION offsetof(RegisterInfoPOSIX_arm64::GPR, reg))
+
+#define FPU_OFFSET(idx) ((idx)*16 + sizeof(RegisterInfoPOSIX_arm64::GPR))
+#define FPU_OFFSET_NAME(reg) \
+ (LLVM_EXTENSION offsetof(RegisterInfoPOSIX_arm64::FPU, reg) + \
+ sizeof(RegisterInfoPOSIX_arm64::GPR))
+
+#define EXC_OFFSET_NAME(reg) \
+ (LLVM_EXTENSION offsetof(RegisterInfoPOSIX_arm64::EXC, reg) + \
+ sizeof(RegisterInfoPOSIX_arm64::GPR) + \
+ sizeof(RegisterInfoPOSIX_arm64::FPU))
+#define DBG_OFFSET_NAME(reg) \
+ (LLVM_EXTENSION offsetof(RegisterInfoPOSIX_arm64::DBG, reg) + \
+ sizeof(RegisterInfoPOSIX_arm64::GPR) + \
+ sizeof(RegisterInfoPOSIX_arm64::FPU) + \
+ sizeof(RegisterInfoPOSIX_arm64::EXC))
+
+#define DEFINE_DBG(reg, i) \
+ #reg, NULL, \
+ sizeof(((RegisterInfoPOSIX_arm64::DBG *) NULL)->reg[i]), \
+ DBG_OFFSET_NAME(reg[i]), lldb::eEncodingUint, lldb::eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ dbg_##reg##i }, \
+ NULL, NULL, NULL, 0
+#define REG_CONTEXT_SIZE \
+ (sizeof(RegisterInfoPOSIX_arm64::GPR) + \
+ sizeof(RegisterInfoPOSIX_arm64::FPU) + \
+ sizeof(RegisterInfoPOSIX_arm64::EXC))
+
+// Include RegisterInfos_arm64 to declare our g_register_infos_arm64 structure.
+#define DECLARE_REGISTER_INFOS_ARM64_STRUCT
+#include "RegisterInfos_arm64.h"
+#undef DECLARE_REGISTER_INFOS_ARM64_STRUCT
+
+static const lldb_private::RegisterInfo *
+GetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::aarch64:
+ return g_register_infos_arm64_le;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+static uint32_t
+GetRegisterInfoCount(const lldb_private::ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::aarch64:
+ return static_cast<uint32_t>(sizeof(g_register_infos_arm64_le) /
+ sizeof(g_register_infos_arm64_le[0]));
+ default:
+ assert(false && "Unhandled target architecture.");
+ return 0;
+ }
+}
+
+RegisterInfoPOSIX_arm64::RegisterInfoPOSIX_arm64(
+ const lldb_private::ArchSpec &target_arch)
+ : lldb_private::RegisterInfoInterface(target_arch),
+ m_register_info_p(GetRegisterInfoPtr(target_arch)),
+ m_register_info_count(GetRegisterInfoCount(target_arch)) {}
+
+size_t RegisterInfoPOSIX_arm64::GetGPRSize() const {
+ return sizeof(struct RegisterInfoPOSIX_arm64::GPR);
+}
+
+const lldb_private::RegisterInfo *
+RegisterInfoPOSIX_arm64::GetRegisterInfo() const {
+ return m_register_info_p;
+}
+
+uint32_t RegisterInfoPOSIX_arm64::GetRegisterCount() const {
+ return m_register_info_count;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h
new file mode 100644
index 000000000000..ace179a81814
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h
@@ -0,0 +1,69 @@
+//===-- RegisterInfoPOSIX_arm64.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 liblldb_RegisterContextLinux_arm64_H_
+#define liblldb_RegisterContextLinux_arm64_H_
+
+#include "RegisterInfoInterface.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/lldb-private.h"
+
+class RegisterInfoPOSIX_arm64 : public lldb_private::RegisterInfoInterface {
+public:
+ // based on RegisterContextDarwin_arm64.h
+ struct GPR {
+ uint64_t x[29]; // x0-x28
+ uint64_t fp; // x29
+ uint64_t lr; // x30
+ uint64_t sp; // x31
+ uint64_t pc; // pc
+ uint32_t cpsr; // cpsr
+ };
+
+ // based on RegisterContextDarwin_arm64.h
+ struct VReg {
+ uint8_t bytes[16];
+ };
+
+ // based on RegisterContextDarwin_arm64.h
+ struct FPU {
+ VReg v[32];
+ uint32_t fpsr;
+ uint32_t fpcr;
+ };
+
+ // based on RegisterContextDarwin_arm64.h
+ struct EXC {
+ uint64_t far; // Virtual Fault Address
+ uint32_t esr; // Exception syndrome
+ uint32_t exception; // number of arm exception token
+ };
+
+ // based on RegisterContextDarwin_arm64.h
+ struct DBG {
+ uint64_t bvr[16];
+ uint64_t bcr[16];
+ uint64_t wvr[16];
+ uint64_t wcr[16];
+ uint64_t mdscr_el1;
+ };
+
+ RegisterInfoPOSIX_arm64(const lldb_private::ArchSpec &target_arch);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+
+private:
+ const lldb_private::RegisterInfo *m_register_info_p;
+ uint32_t m_register_info_count;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.cpp
new file mode 100644
index 000000000000..35051a3ce095
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.cpp
@@ -0,0 +1,63 @@
+//===-- RegisterInfoPOSIX_ppc64le.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 <cassert>
+#include <stddef.h>
+#include <vector>
+
+#include "lldb/lldb-defines.h"
+#include "llvm/Support/Compiler.h"
+
+#include "RegisterInfoPOSIX_ppc64le.h"
+
+// Include RegisterInfoPOSIX_ppc64le to declare our g_register_infos_ppc64le
+#define DECLARE_REGISTER_INFOS_PPC64LE_STRUCT
+#include "RegisterInfos_ppc64le.h"
+#undef DECLARE_REGISTER_INFOS_PPC64LE_STRUCT
+
+static const lldb_private::RegisterInfo *
+GetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::ppc64le:
+ return g_register_infos_ppc64le;
+ default:
+ assert(false && "Unhandled target architecture.");
+ return nullptr;
+ }
+}
+
+static uint32_t
+GetRegisterInfoCount(const lldb_private::ArchSpec &target_arch) {
+ switch (target_arch.GetMachine()) {
+ case llvm::Triple::ppc64le:
+ return static_cast<uint32_t>(sizeof(g_register_infos_ppc64le) /
+ sizeof(g_register_infos_ppc64le[0]));
+ default:
+ assert(false && "Unhandled target architecture.");
+ return 0;
+ }
+}
+
+RegisterInfoPOSIX_ppc64le::RegisterInfoPOSIX_ppc64le(
+ const lldb_private::ArchSpec &target_arch)
+ : lldb_private::RegisterInfoInterface(target_arch),
+ m_register_info_p(GetRegisterInfoPtr(target_arch)),
+ m_register_info_count(GetRegisterInfoCount(target_arch)) {}
+
+size_t RegisterInfoPOSIX_ppc64le::GetGPRSize() const {
+ return sizeof(GPR);
+}
+
+const lldb_private::RegisterInfo *
+RegisterInfoPOSIX_ppc64le::GetRegisterInfo() const {
+ return m_register_info_p;
+}
+
+uint32_t RegisterInfoPOSIX_ppc64le::GetRegisterCount() const {
+ return m_register_info_count;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.h
new file mode 100644
index 000000000000..c4d4d3b546e2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.h
@@ -0,0 +1,31 @@
+//===-- RegisterInfoPOSIX_ppc64le.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 liblldb_RegisterContextLinux_ppc64le_H_
+#define liblldb_RegisterContextLinux_ppc64le_H_
+
+#include "RegisterInfoInterface.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/lldb-private.h"
+
+class RegisterInfoPOSIX_ppc64le : public lldb_private::RegisterInfoInterface {
+public:
+ RegisterInfoPOSIX_ppc64le(const lldb_private::ArchSpec &target_arch);
+
+ size_t GetGPRSize() const override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfo() const override;
+
+ uint32_t GetRegisterCount() const override;
+
+private:
+ const lldb_private::RegisterInfo *m_register_info_p;
+ uint32_t m_register_info_count;
+};
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h
new file mode 100644
index 000000000000..74b9e3b2db32
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h
@@ -0,0 +1,1619 @@
+//===-- RegisterInfos_arm.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifdef DECLARE_REGISTER_INFOS_ARM_STRUCT
+
+#include <stddef.h>
+
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-private.h"
+
+#include "Utility/ARM_DWARF_Registers.h"
+#include "Utility/ARM_ehframe_Registers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#ifndef GPR_OFFSET
+#error GPR_OFFSET must be defined before including this header file
+#endif
+
+#ifndef FPU_OFFSET
+#error FPU_OFFSET must be defined before including this header file
+#endif
+
+#ifndef FPSCR_OFFSET
+#error FPSCR_OFFSET must be defined before including this header file
+#endif
+
+#ifndef EXC_OFFSET
+#error EXC_OFFSET_NAME must be defined before including this header file
+#endif
+
+#ifndef DEFINE_DBG
+#error DEFINE_DBG must be defined before including this header file
+#endif
+
+enum {
+ 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_r13,
+ gpr_sp = gpr_r13,
+ gpr_r14,
+ gpr_lr = gpr_r14,
+ gpr_r15,
+ gpr_pc = gpr_r15,
+ gpr_cpsr,
+
+ fpu_s0,
+ fpu_s1,
+ fpu_s2,
+ fpu_s3,
+ fpu_s4,
+ fpu_s5,
+ fpu_s6,
+ fpu_s7,
+ fpu_s8,
+ fpu_s9,
+ fpu_s10,
+ fpu_s11,
+ fpu_s12,
+ fpu_s13,
+ fpu_s14,
+ fpu_s15,
+ fpu_s16,
+ fpu_s17,
+ fpu_s18,
+ fpu_s19,
+ fpu_s20,
+ fpu_s21,
+ fpu_s22,
+ fpu_s23,
+ fpu_s24,
+ fpu_s25,
+ fpu_s26,
+ fpu_s27,
+ fpu_s28,
+ fpu_s29,
+ fpu_s30,
+ fpu_s31,
+ fpu_fpscr,
+
+ fpu_d0,
+ fpu_d1,
+ fpu_d2,
+ fpu_d3,
+ fpu_d4,
+ fpu_d5,
+ fpu_d6,
+ fpu_d7,
+ fpu_d8,
+ fpu_d9,
+ fpu_d10,
+ fpu_d11,
+ fpu_d12,
+ fpu_d13,
+ fpu_d14,
+ fpu_d15,
+ fpu_d16,
+ fpu_d17,
+ fpu_d18,
+ fpu_d19,
+ fpu_d20,
+ fpu_d21,
+ fpu_d22,
+ fpu_d23,
+ fpu_d24,
+ fpu_d25,
+ fpu_d26,
+ fpu_d27,
+ fpu_d28,
+ fpu_d29,
+ fpu_d30,
+ fpu_d31,
+
+ fpu_q0,
+ fpu_q1,
+ fpu_q2,
+ fpu_q3,
+ fpu_q4,
+ fpu_q5,
+ fpu_q6,
+ fpu_q7,
+ fpu_q8,
+ fpu_q9,
+ fpu_q10,
+ fpu_q11,
+ fpu_q12,
+ fpu_q13,
+ fpu_q14,
+ fpu_q15,
+
+ exc_exception,
+ exc_fsr,
+ exc_far,
+
+ dbg_bvr0,
+ dbg_bvr1,
+ dbg_bvr2,
+ dbg_bvr3,
+ dbg_bvr4,
+ dbg_bvr5,
+ dbg_bvr6,
+ dbg_bvr7,
+ dbg_bvr8,
+ dbg_bvr9,
+ dbg_bvr10,
+ dbg_bvr11,
+ dbg_bvr12,
+ dbg_bvr13,
+ dbg_bvr14,
+ dbg_bvr15,
+
+ dbg_bcr0,
+ dbg_bcr1,
+ dbg_bcr2,
+ dbg_bcr3,
+ dbg_bcr4,
+ dbg_bcr5,
+ dbg_bcr6,
+ dbg_bcr7,
+ dbg_bcr8,
+ dbg_bcr9,
+ dbg_bcr10,
+ dbg_bcr11,
+ dbg_bcr12,
+ dbg_bcr13,
+ dbg_bcr14,
+ dbg_bcr15,
+
+ dbg_wvr0,
+ dbg_wvr1,
+ dbg_wvr2,
+ dbg_wvr3,
+ dbg_wvr4,
+ dbg_wvr5,
+ dbg_wvr6,
+ dbg_wvr7,
+ dbg_wvr8,
+ dbg_wvr9,
+ dbg_wvr10,
+ dbg_wvr11,
+ dbg_wvr12,
+ dbg_wvr13,
+ dbg_wvr14,
+ dbg_wvr15,
+
+ dbg_wcr0,
+ dbg_wcr1,
+ dbg_wcr2,
+ dbg_wcr3,
+ dbg_wcr4,
+ dbg_wcr5,
+ dbg_wcr6,
+ dbg_wcr7,
+ dbg_wcr8,
+ dbg_wcr9,
+ dbg_wcr10,
+ dbg_wcr11,
+ dbg_wcr12,
+ dbg_wcr13,
+ dbg_wcr14,
+ dbg_wcr15,
+
+ k_num_registers
+};
+
+static uint32_t g_s0_invalidates[] = {fpu_d0, fpu_q0, LLDB_INVALID_REGNUM};
+static uint32_t g_s1_invalidates[] = {fpu_d0, fpu_q0, LLDB_INVALID_REGNUM};
+static uint32_t g_s2_invalidates[] = {fpu_d1, fpu_q0, LLDB_INVALID_REGNUM};
+static uint32_t g_s3_invalidates[] = {fpu_d1, fpu_q0, LLDB_INVALID_REGNUM};
+static uint32_t g_s4_invalidates[] = {fpu_d2, fpu_q1, LLDB_INVALID_REGNUM};
+static uint32_t g_s5_invalidates[] = {fpu_d2, fpu_q1, LLDB_INVALID_REGNUM};
+static uint32_t g_s6_invalidates[] = {fpu_d3, fpu_q1, LLDB_INVALID_REGNUM};
+static uint32_t g_s7_invalidates[] = {fpu_d3, fpu_q1, LLDB_INVALID_REGNUM};
+static uint32_t g_s8_invalidates[] = {fpu_d4, fpu_q2, LLDB_INVALID_REGNUM};
+static uint32_t g_s9_invalidates[] = {fpu_d4, fpu_q2, LLDB_INVALID_REGNUM};
+static uint32_t g_s10_invalidates[] = {fpu_d5, fpu_q2, LLDB_INVALID_REGNUM};
+static uint32_t g_s11_invalidates[] = {fpu_d5, fpu_q2, LLDB_INVALID_REGNUM};
+static uint32_t g_s12_invalidates[] = {fpu_d6, fpu_q3, LLDB_INVALID_REGNUM};
+static uint32_t g_s13_invalidates[] = {fpu_d6, fpu_q3, LLDB_INVALID_REGNUM};
+static uint32_t g_s14_invalidates[] = {fpu_d7, fpu_q3, LLDB_INVALID_REGNUM};
+static uint32_t g_s15_invalidates[] = {fpu_d7, fpu_q3, LLDB_INVALID_REGNUM};
+static uint32_t g_s16_invalidates[] = {fpu_d8, fpu_q4, LLDB_INVALID_REGNUM};
+static uint32_t g_s17_invalidates[] = {fpu_d8, fpu_q4, LLDB_INVALID_REGNUM};
+static uint32_t g_s18_invalidates[] = {fpu_d9, fpu_q4, LLDB_INVALID_REGNUM};
+static uint32_t g_s19_invalidates[] = {fpu_d9, fpu_q4, LLDB_INVALID_REGNUM};
+static uint32_t g_s20_invalidates[] = {fpu_d10, fpu_q5, LLDB_INVALID_REGNUM};
+static uint32_t g_s21_invalidates[] = {fpu_d10, fpu_q5, LLDB_INVALID_REGNUM};
+static uint32_t g_s22_invalidates[] = {fpu_d11, fpu_q5, LLDB_INVALID_REGNUM};
+static uint32_t g_s23_invalidates[] = {fpu_d11, fpu_q5, LLDB_INVALID_REGNUM};
+static uint32_t g_s24_invalidates[] = {fpu_d12, fpu_q6, LLDB_INVALID_REGNUM};
+static uint32_t g_s25_invalidates[] = {fpu_d12, fpu_q6, LLDB_INVALID_REGNUM};
+static uint32_t g_s26_invalidates[] = {fpu_d13, fpu_q6, LLDB_INVALID_REGNUM};
+static uint32_t g_s27_invalidates[] = {fpu_d13, fpu_q6, LLDB_INVALID_REGNUM};
+static uint32_t g_s28_invalidates[] = {fpu_d14, fpu_q7, LLDB_INVALID_REGNUM};
+static uint32_t g_s29_invalidates[] = {fpu_d14, fpu_q7, LLDB_INVALID_REGNUM};
+static uint32_t g_s30_invalidates[] = {fpu_d15, fpu_q7, LLDB_INVALID_REGNUM};
+static uint32_t g_s31_invalidates[] = {fpu_d15, fpu_q7, LLDB_INVALID_REGNUM};
+
+static uint32_t g_d0_contains[] = {fpu_s0, fpu_s1, LLDB_INVALID_REGNUM};
+static uint32_t g_d1_contains[] = {fpu_s2, fpu_s3, LLDB_INVALID_REGNUM};
+static uint32_t g_d2_contains[] = {fpu_s4, fpu_s5, LLDB_INVALID_REGNUM};
+static uint32_t g_d3_contains[] = {fpu_s6, fpu_s7, LLDB_INVALID_REGNUM};
+static uint32_t g_d4_contains[] = {fpu_s8, fpu_s9, LLDB_INVALID_REGNUM};
+static uint32_t g_d5_contains[] = {fpu_s10, fpu_s11, LLDB_INVALID_REGNUM};
+static uint32_t g_d6_contains[] = {fpu_s12, fpu_s13, LLDB_INVALID_REGNUM};
+static uint32_t g_d7_contains[] = {fpu_s14, fpu_s15, LLDB_INVALID_REGNUM};
+static uint32_t g_d8_contains[] = {fpu_s16, fpu_s17, LLDB_INVALID_REGNUM};
+static uint32_t g_d9_contains[] = {fpu_s18, fpu_s19, LLDB_INVALID_REGNUM};
+static uint32_t g_d10_contains[] = {fpu_s20, fpu_s21, LLDB_INVALID_REGNUM};
+static uint32_t g_d11_contains[] = {fpu_s22, fpu_s23, LLDB_INVALID_REGNUM};
+static uint32_t g_d12_contains[] = {fpu_s24, fpu_s25, LLDB_INVALID_REGNUM};
+static uint32_t g_d13_contains[] = {fpu_s26, fpu_s27, LLDB_INVALID_REGNUM};
+static uint32_t g_d14_contains[] = {fpu_s28, fpu_s29, LLDB_INVALID_REGNUM};
+static uint32_t g_d15_contains[] = {fpu_s30, fpu_s31, LLDB_INVALID_REGNUM};
+
+static uint32_t g_d0_invalidates[] = {fpu_q0, LLDB_INVALID_REGNUM};
+static uint32_t g_d1_invalidates[] = {fpu_q0, LLDB_INVALID_REGNUM};
+static uint32_t g_d2_invalidates[] = {fpu_q1, LLDB_INVALID_REGNUM};
+static uint32_t g_d3_invalidates[] = {fpu_q1, LLDB_INVALID_REGNUM};
+static uint32_t g_d4_invalidates[] = {fpu_q2, LLDB_INVALID_REGNUM};
+static uint32_t g_d5_invalidates[] = {fpu_q2, LLDB_INVALID_REGNUM};
+static uint32_t g_d6_invalidates[] = {fpu_q3, LLDB_INVALID_REGNUM};
+static uint32_t g_d7_invalidates[] = {fpu_q3, LLDB_INVALID_REGNUM};
+static uint32_t g_d8_invalidates[] = {fpu_q4, LLDB_INVALID_REGNUM};
+static uint32_t g_d9_invalidates[] = {fpu_q4, LLDB_INVALID_REGNUM};
+static uint32_t g_d10_invalidates[] = {fpu_q5, LLDB_INVALID_REGNUM};
+static uint32_t g_d11_invalidates[] = {fpu_q5, LLDB_INVALID_REGNUM};
+static uint32_t g_d12_invalidates[] = {fpu_q6, LLDB_INVALID_REGNUM};
+static uint32_t g_d13_invalidates[] = {fpu_q6, LLDB_INVALID_REGNUM};
+static uint32_t g_d14_invalidates[] = {fpu_q7, LLDB_INVALID_REGNUM};
+static uint32_t g_d15_invalidates[] = {fpu_q7, LLDB_INVALID_REGNUM};
+static uint32_t g_d16_invalidates[] = {fpu_q8, LLDB_INVALID_REGNUM};
+static uint32_t g_d17_invalidates[] = {fpu_q8, LLDB_INVALID_REGNUM};
+static uint32_t g_d18_invalidates[] = {fpu_q9, LLDB_INVALID_REGNUM};
+static uint32_t g_d19_invalidates[] = {fpu_q9, LLDB_INVALID_REGNUM};
+static uint32_t g_d20_invalidates[] = {fpu_q10, LLDB_INVALID_REGNUM};
+static uint32_t g_d21_invalidates[] = {fpu_q10, LLDB_INVALID_REGNUM};
+static uint32_t g_d22_invalidates[] = {fpu_q11, LLDB_INVALID_REGNUM};
+static uint32_t g_d23_invalidates[] = {fpu_q11, LLDB_INVALID_REGNUM};
+static uint32_t g_d24_invalidates[] = {fpu_q12, LLDB_INVALID_REGNUM};
+static uint32_t g_d25_invalidates[] = {fpu_q12, LLDB_INVALID_REGNUM};
+static uint32_t g_d26_invalidates[] = {fpu_q13, LLDB_INVALID_REGNUM};
+static uint32_t g_d27_invalidates[] = {fpu_q13, LLDB_INVALID_REGNUM};
+static uint32_t g_d28_invalidates[] = {fpu_q14, LLDB_INVALID_REGNUM};
+static uint32_t g_d29_invalidates[] = {fpu_q14, LLDB_INVALID_REGNUM};
+static uint32_t g_d30_invalidates[] = {fpu_q15, LLDB_INVALID_REGNUM};
+static uint32_t g_d31_invalidates[] = {fpu_q15, LLDB_INVALID_REGNUM};
+
+static uint32_t g_q0_contains[] = {
+ fpu_d0, fpu_d1, fpu_s0, fpu_s1, fpu_s2, fpu_s3, LLDB_INVALID_REGNUM};
+static uint32_t g_q1_contains[] = {
+ fpu_d2, fpu_d3, fpu_s4, fpu_s5, fpu_s6, fpu_s7, LLDB_INVALID_REGNUM};
+static uint32_t g_q2_contains[] = {
+ fpu_d4, fpu_d5, fpu_s8, fpu_s9, fpu_s10, fpu_s11, LLDB_INVALID_REGNUM};
+static uint32_t g_q3_contains[] = {
+ fpu_d6, fpu_d7, fpu_s12, fpu_s13, fpu_s14, fpu_s15, LLDB_INVALID_REGNUM};
+static uint32_t g_q4_contains[] = {
+ fpu_d8, fpu_d9, fpu_s16, fpu_s17, fpu_s18, fpu_s19, LLDB_INVALID_REGNUM};
+static uint32_t g_q5_contains[] = {
+ fpu_d10, fpu_d11, fpu_s20, fpu_s21, fpu_s22, fpu_s23, LLDB_INVALID_REGNUM};
+static uint32_t g_q6_contains[] = {
+ fpu_d12, fpu_d13, fpu_s24, fpu_s25, fpu_s26, fpu_s27, LLDB_INVALID_REGNUM};
+static uint32_t g_q7_contains[] = {
+ fpu_d14, fpu_d15, fpu_s28, fpu_s29, fpu_s30, fpu_s31, LLDB_INVALID_REGNUM};
+static uint32_t g_q8_contains[] = {fpu_d16, fpu_d17, LLDB_INVALID_REGNUM};
+static uint32_t g_q9_contains[] = {fpu_d18, fpu_d19, LLDB_INVALID_REGNUM};
+static uint32_t g_q10_contains[] = {fpu_d20, fpu_d21, LLDB_INVALID_REGNUM};
+static uint32_t g_q11_contains[] = {fpu_d22, fpu_d23, LLDB_INVALID_REGNUM};
+static uint32_t g_q12_contains[] = {fpu_d24, fpu_d25, LLDB_INVALID_REGNUM};
+static uint32_t g_q13_contains[] = {fpu_d26, fpu_d27, LLDB_INVALID_REGNUM};
+static uint32_t g_q14_contains[] = {fpu_d28, fpu_d29, LLDB_INVALID_REGNUM};
+static uint32_t g_q15_contains[] = {fpu_d30, fpu_d31, LLDB_INVALID_REGNUM};
+
+static RegisterInfo g_register_infos_arm[] = {
+ // NAME ALT SZ OFFSET ENCODING FORMAT
+ // EH_FRAME DWARF GENERIC
+ // PROCESS PLUGIN LLDB NATIVE VALUE REGS INVALIDATE REGS
+ // =========== ======= == ============== ================
+ // ==================== =================== ===================
+ // ========================== =================== =============
+ // ============== =================
+ {"r0",
+ nullptr,
+ 4,
+ GPR_OFFSET(0),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r0, dwarf_r0, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM,
+ gpr_r0},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r1",
+ nullptr,
+ 4,
+ GPR_OFFSET(1),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r1, dwarf_r1, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM,
+ gpr_r1},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r2",
+ nullptr,
+ 4,
+ GPR_OFFSET(2),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r2, dwarf_r2, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM,
+ gpr_r2},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r3",
+ nullptr,
+ 4,
+ GPR_OFFSET(3),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r3, dwarf_r3, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM,
+ gpr_r3},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r4",
+ nullptr,
+ 4,
+ GPR_OFFSET(4),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r4, dwarf_r4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r4},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r5",
+ nullptr,
+ 4,
+ GPR_OFFSET(5),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r5, dwarf_r5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r5},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r6",
+ nullptr,
+ 4,
+ GPR_OFFSET(6),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r6, dwarf_r6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r6},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r7",
+ nullptr,
+ 4,
+ GPR_OFFSET(7),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r7, dwarf_r7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r7},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r8",
+ nullptr,
+ 4,
+ GPR_OFFSET(8),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r8, dwarf_r8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r8},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r9",
+ nullptr,
+ 4,
+ GPR_OFFSET(9),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r9, dwarf_r9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r9},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r10",
+ nullptr,
+ 4,
+ GPR_OFFSET(10),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r10, dwarf_r10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ gpr_r10},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r11",
+ nullptr,
+ 4,
+ GPR_OFFSET(11),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r11, dwarf_r11, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM,
+ gpr_r11},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"r12",
+ nullptr,
+ 4,
+ GPR_OFFSET(12),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_r12, dwarf_r12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ gpr_r12},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"sp",
+ "r13",
+ 4,
+ GPR_OFFSET(13),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM,
+ gpr_sp},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lr",
+ "r14",
+ 4,
+ GPR_OFFSET(14),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM,
+ gpr_lr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"pc",
+ "r15",
+ 4,
+ GPR_OFFSET(15),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM,
+ gpr_pc},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cpsr",
+ "psr",
+ 4,
+ GPR_OFFSET(16),
+ eEncodingUint,
+ eFormatHex,
+ {ehframe_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM,
+ gpr_cpsr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"s0",
+ nullptr,
+ 4,
+ FPU_OFFSET(0),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s0},
+ nullptr,
+ g_s0_invalidates,
+ nullptr,
+ 0},
+ {"s1",
+ nullptr,
+ 4,
+ FPU_OFFSET(1),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s1},
+ nullptr,
+ g_s1_invalidates,
+ nullptr,
+ 0},
+ {"s2",
+ nullptr,
+ 4,
+ FPU_OFFSET(2),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s2},
+ nullptr,
+ g_s2_invalidates,
+ nullptr,
+ 0},
+ {"s3",
+ nullptr,
+ 4,
+ FPU_OFFSET(3),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s3},
+ nullptr,
+ g_s3_invalidates,
+ nullptr,
+ 0},
+ {"s4",
+ nullptr,
+ 4,
+ FPU_OFFSET(4),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s4},
+ nullptr,
+ g_s4_invalidates,
+ nullptr,
+ 0},
+ {"s5",
+ nullptr,
+ 4,
+ FPU_OFFSET(5),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s5},
+ nullptr,
+ g_s5_invalidates,
+ nullptr,
+ 0},
+ {"s6",
+ nullptr,
+ 4,
+ FPU_OFFSET(6),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s6},
+ nullptr,
+ g_s6_invalidates,
+ nullptr,
+ 0},
+ {"s7",
+ nullptr,
+ 4,
+ FPU_OFFSET(7),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s7},
+ nullptr,
+ g_s7_invalidates,
+ nullptr,
+ 0},
+ {"s8",
+ nullptr,
+ 4,
+ FPU_OFFSET(8),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s8},
+ nullptr,
+ g_s8_invalidates,
+ nullptr,
+ 0},
+ {"s9",
+ nullptr,
+ 4,
+ FPU_OFFSET(9),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s9},
+ nullptr,
+ g_s9_invalidates,
+ nullptr,
+ 0},
+ {"s10",
+ nullptr,
+ 4,
+ FPU_OFFSET(10),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s10},
+ nullptr,
+ g_s10_invalidates,
+ nullptr,
+ 0},
+ {"s11",
+ nullptr,
+ 4,
+ FPU_OFFSET(11),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s11},
+ nullptr,
+ g_s11_invalidates,
+ nullptr,
+ 0},
+ {"s12",
+ nullptr,
+ 4,
+ FPU_OFFSET(12),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s12},
+ nullptr,
+ g_s12_invalidates,
+ nullptr,
+ 0},
+ {"s13",
+ nullptr,
+ 4,
+ FPU_OFFSET(13),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s13},
+ nullptr,
+ g_s13_invalidates,
+ nullptr,
+ 0},
+ {"s14",
+ nullptr,
+ 4,
+ FPU_OFFSET(14),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s14},
+ nullptr,
+ g_s14_invalidates,
+ nullptr,
+ 0},
+ {"s15",
+ nullptr,
+ 4,
+ FPU_OFFSET(15),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s15},
+ nullptr,
+ g_s15_invalidates,
+ nullptr,
+ 0},
+ {"s16",
+ nullptr,
+ 4,
+ FPU_OFFSET(16),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s16},
+ nullptr,
+ g_s16_invalidates,
+ nullptr,
+ 0},
+ {"s17",
+ nullptr,
+ 4,
+ FPU_OFFSET(17),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s17},
+ nullptr,
+ g_s17_invalidates,
+ nullptr,
+ 0},
+ {"s18",
+ nullptr,
+ 4,
+ FPU_OFFSET(18),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s18},
+ nullptr,
+ g_s18_invalidates,
+ nullptr,
+ 0},
+ {"s19",
+ nullptr,
+ 4,
+ FPU_OFFSET(19),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s19},
+ nullptr,
+ g_s19_invalidates,
+ nullptr,
+ 0},
+ {"s20",
+ nullptr,
+ 4,
+ FPU_OFFSET(20),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s20},
+ nullptr,
+ g_s20_invalidates,
+ nullptr,
+ 0},
+ {"s21",
+ nullptr,
+ 4,
+ FPU_OFFSET(21),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s21},
+ nullptr,
+ g_s21_invalidates,
+ nullptr,
+ 0},
+ {"s22",
+ nullptr,
+ 4,
+ FPU_OFFSET(22),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s22},
+ nullptr,
+ g_s22_invalidates,
+ nullptr,
+ 0},
+ {"s23",
+ nullptr,
+ 4,
+ FPU_OFFSET(23),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s23},
+ nullptr,
+ g_s23_invalidates,
+ nullptr,
+ 0},
+ {"s24",
+ nullptr,
+ 4,
+ FPU_OFFSET(24),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s24},
+ nullptr,
+ g_s24_invalidates,
+ nullptr,
+ 0},
+ {"s25",
+ nullptr,
+ 4,
+ FPU_OFFSET(25),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s25},
+ nullptr,
+ g_s25_invalidates,
+ nullptr,
+ 0},
+ {"s26",
+ nullptr,
+ 4,
+ FPU_OFFSET(26),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s26},
+ nullptr,
+ g_s26_invalidates,
+ nullptr,
+ 0},
+ {"s27",
+ nullptr,
+ 4,
+ FPU_OFFSET(27),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s27},
+ nullptr,
+ g_s27_invalidates,
+ nullptr,
+ 0},
+ {"s28",
+ nullptr,
+ 4,
+ FPU_OFFSET(28),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s28},
+ nullptr,
+ g_s28_invalidates,
+ nullptr,
+ 0},
+ {"s29",
+ nullptr,
+ 4,
+ FPU_OFFSET(29),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s29},
+ nullptr,
+ g_s29_invalidates,
+ nullptr,
+ 0},
+ {"s30",
+ nullptr,
+ 4,
+ FPU_OFFSET(30),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s30},
+ nullptr,
+ g_s30_invalidates,
+ nullptr,
+ 0},
+ {"s31",
+ nullptr,
+ 4,
+ FPU_OFFSET(31),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_s31},
+ nullptr,
+ g_s31_invalidates,
+ nullptr,
+ 0},
+ {"fpscr",
+ nullptr,
+ 4,
+ FPSCR_OFFSET,
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, fpu_fpscr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"d0",
+ nullptr,
+ 8,
+ FPU_OFFSET(0),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d0},
+ g_d0_contains,
+ g_d0_invalidates,
+ nullptr,
+ 0},
+ {"d1",
+ nullptr,
+ 8,
+ FPU_OFFSET(2),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d1},
+ g_d1_contains,
+ g_d1_invalidates,
+ nullptr,
+ 0},
+ {"d2",
+ nullptr,
+ 8,
+ FPU_OFFSET(4),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d2},
+ g_d2_contains,
+ g_d2_invalidates,
+ nullptr,
+ 0},
+ {"d3",
+ nullptr,
+ 8,
+ FPU_OFFSET(6),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d3},
+ g_d3_contains,
+ g_d3_invalidates,
+ nullptr,
+ 0},
+ {"d4",
+ nullptr,
+ 8,
+ FPU_OFFSET(8),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d4},
+ g_d4_contains,
+ g_d4_invalidates,
+ nullptr,
+ 0},
+ {"d5",
+ nullptr,
+ 8,
+ FPU_OFFSET(10),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d5},
+ g_d5_contains,
+ g_d5_invalidates,
+ nullptr,
+ 0},
+ {"d6",
+ nullptr,
+ 8,
+ FPU_OFFSET(12),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d6},
+ g_d6_contains,
+ g_d6_invalidates,
+ nullptr,
+ 0},
+ {"d7",
+ nullptr,
+ 8,
+ FPU_OFFSET(14),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d7},
+ g_d7_contains,
+ g_d7_invalidates,
+ nullptr,
+ 0},
+ {"d8",
+ nullptr,
+ 8,
+ FPU_OFFSET(16),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d8},
+ g_d8_contains,
+ g_d8_invalidates,
+ nullptr,
+ 0},
+ {"d9",
+ nullptr,
+ 8,
+ FPU_OFFSET(18),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d9},
+ g_d9_contains,
+ g_d9_invalidates,
+ nullptr,
+ 0},
+ {"d10",
+ nullptr,
+ 8,
+ FPU_OFFSET(20),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d10},
+ g_d10_contains,
+ g_d10_invalidates,
+ nullptr,
+ 0},
+ {"d11",
+ nullptr,
+ 8,
+ FPU_OFFSET(22),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d11},
+ g_d11_contains,
+ g_d11_invalidates,
+ nullptr,
+ 0},
+ {"d12",
+ nullptr,
+ 8,
+ FPU_OFFSET(24),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d12},
+ g_d12_contains,
+ g_d12_invalidates,
+ nullptr,
+ 0},
+ {"d13",
+ nullptr,
+ 8,
+ FPU_OFFSET(26),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d13},
+ g_d13_contains,
+ g_d13_invalidates,
+ nullptr,
+ 0},
+ {"d14",
+ nullptr,
+ 8,
+ FPU_OFFSET(28),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d14},
+ g_d14_contains,
+ g_d14_invalidates,
+ nullptr,
+ 0},
+ {"d15",
+ nullptr,
+ 8,
+ FPU_OFFSET(30),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d15},
+ g_d15_contains,
+ g_d15_invalidates,
+ nullptr,
+ 0},
+ {"d16",
+ nullptr,
+ 8,
+ FPU_OFFSET(32),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d16},
+ nullptr,
+ g_d16_invalidates,
+ nullptr,
+ 0},
+ {"d17",
+ nullptr,
+ 8,
+ FPU_OFFSET(34),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d17},
+ nullptr,
+ g_d17_invalidates,
+ nullptr,
+ 0},
+ {"d18",
+ nullptr,
+ 8,
+ FPU_OFFSET(36),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d18},
+ nullptr,
+ g_d18_invalidates,
+ nullptr,
+ 0},
+ {"d19",
+ nullptr,
+ 8,
+ FPU_OFFSET(38),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d19},
+ nullptr,
+ g_d19_invalidates,
+ nullptr,
+ 0},
+ {"d20",
+ nullptr,
+ 8,
+ FPU_OFFSET(40),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d20},
+ nullptr,
+ g_d20_invalidates,
+ nullptr,
+ 0},
+ {"d21",
+ nullptr,
+ 8,
+ FPU_OFFSET(42),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d21},
+ nullptr,
+ g_d21_invalidates,
+ nullptr,
+ 0},
+ {"d22",
+ nullptr,
+ 8,
+ FPU_OFFSET(44),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d22},
+ nullptr,
+ g_d22_invalidates,
+ nullptr,
+ 0},
+ {"d23",
+ nullptr,
+ 8,
+ FPU_OFFSET(46),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d23},
+ nullptr,
+ g_d23_invalidates,
+ nullptr,
+ 0},
+ {"d24",
+ nullptr,
+ 8,
+ FPU_OFFSET(48),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d24},
+ nullptr,
+ g_d24_invalidates,
+ nullptr,
+ 0},
+ {"d25",
+ nullptr,
+ 8,
+ FPU_OFFSET(50),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d25},
+ nullptr,
+ g_d25_invalidates,
+ nullptr,
+ 0},
+ {"d26",
+ nullptr,
+ 8,
+ FPU_OFFSET(52),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d26},
+ nullptr,
+ g_d26_invalidates,
+ nullptr,
+ 0},
+ {"d27",
+ nullptr,
+ 8,
+ FPU_OFFSET(54),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d27},
+ nullptr,
+ g_d27_invalidates,
+ nullptr,
+ 0},
+ {"d28",
+ nullptr,
+ 8,
+ FPU_OFFSET(56),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d28},
+ nullptr,
+ g_d28_invalidates,
+ nullptr,
+ 0},
+ {"d29",
+ nullptr,
+ 8,
+ FPU_OFFSET(58),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d29, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d29},
+ nullptr,
+ g_d29_invalidates,
+ nullptr,
+ 0},
+ {"d30",
+ nullptr,
+ 8,
+ FPU_OFFSET(60),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d30, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d30},
+ nullptr,
+ g_d30_invalidates,
+ nullptr,
+ 0},
+ {"d31",
+ nullptr,
+ 8,
+ FPU_OFFSET(62),
+ eEncodingIEEE754,
+ eFormatFloat,
+ {LLDB_INVALID_REGNUM, dwarf_d31, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_d31},
+ nullptr,
+ g_d31_invalidates,
+ nullptr,
+ 0},
+
+ {"q0",
+ nullptr,
+ 16,
+ FPU_OFFSET(0),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q0},
+ g_q0_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q1",
+ nullptr,
+ 16,
+ FPU_OFFSET(4),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q1},
+ g_q1_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q2",
+ nullptr,
+ 16,
+ FPU_OFFSET(8),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q2},
+ g_q2_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q3",
+ nullptr,
+ 16,
+ FPU_OFFSET(12),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q3},
+ g_q3_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q4",
+ nullptr,
+ 16,
+ FPU_OFFSET(16),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q4},
+ g_q4_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q5",
+ nullptr,
+ 16,
+ FPU_OFFSET(20),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q5},
+ g_q5_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q6",
+ nullptr,
+ 16,
+ FPU_OFFSET(24),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q6},
+ g_q6_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q7",
+ nullptr,
+ 16,
+ FPU_OFFSET(28),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q7},
+ g_q7_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q8",
+ nullptr,
+ 16,
+ FPU_OFFSET(32),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q8},
+ g_q8_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q9",
+ nullptr,
+ 16,
+ FPU_OFFSET(36),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q9},
+ g_q9_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q10",
+ nullptr,
+ 16,
+ FPU_OFFSET(40),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q10},
+ g_q10_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q11",
+ nullptr,
+ 16,
+ FPU_OFFSET(44),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q11},
+ g_q11_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q12",
+ nullptr,
+ 16,
+ FPU_OFFSET(48),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q12},
+ g_q12_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q13",
+ nullptr,
+ 16,
+ FPU_OFFSET(52),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q13},
+ g_q13_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q14",
+ nullptr,
+ 16,
+ FPU_OFFSET(56),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q14},
+ g_q14_contains,
+ nullptr,
+ nullptr,
+ 0},
+ {"q15",
+ nullptr,
+ 16,
+ FPU_OFFSET(60),
+ eEncodingVector,
+ eFormatVectorOfUInt8,
+ {LLDB_INVALID_REGNUM, dwarf_q15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ fpu_q15},
+ g_q15_contains,
+ nullptr,
+ nullptr,
+ 0},
+
+ {"exception",
+ nullptr,
+ 4,
+ EXC_OFFSET(0),
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, exc_exception},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fsr",
+ nullptr,
+ 4,
+ EXC_OFFSET(1),
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, exc_fsr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"far",
+ nullptr,
+ 4,
+ EXC_OFFSET(2),
+ eEncodingUint,
+ eFormatHex,
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, exc_far},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+
+ {DEFINE_DBG(bvr, 0)},
+ {DEFINE_DBG(bvr, 1)},
+ {DEFINE_DBG(bvr, 2)},
+ {DEFINE_DBG(bvr, 3)},
+ {DEFINE_DBG(bvr, 4)},
+ {DEFINE_DBG(bvr, 5)},
+ {DEFINE_DBG(bvr, 6)},
+ {DEFINE_DBG(bvr, 7)},
+ {DEFINE_DBG(bvr, 8)},
+ {DEFINE_DBG(bvr, 9)},
+ {DEFINE_DBG(bvr, 10)},
+ {DEFINE_DBG(bvr, 11)},
+ {DEFINE_DBG(bvr, 12)},
+ {DEFINE_DBG(bvr, 13)},
+ {DEFINE_DBG(bvr, 14)},
+ {DEFINE_DBG(bvr, 15)},
+
+ {DEFINE_DBG(bcr, 0)},
+ {DEFINE_DBG(bcr, 1)},
+ {DEFINE_DBG(bcr, 2)},
+ {DEFINE_DBG(bcr, 3)},
+ {DEFINE_DBG(bcr, 4)},
+ {DEFINE_DBG(bcr, 5)},
+ {DEFINE_DBG(bcr, 6)},
+ {DEFINE_DBG(bcr, 7)},
+ {DEFINE_DBG(bcr, 8)},
+ {DEFINE_DBG(bcr, 9)},
+ {DEFINE_DBG(bcr, 10)},
+ {DEFINE_DBG(bcr, 11)},
+ {DEFINE_DBG(bcr, 12)},
+ {DEFINE_DBG(bcr, 13)},
+ {DEFINE_DBG(bcr, 14)},
+ {DEFINE_DBG(bcr, 15)},
+
+ {DEFINE_DBG(wvr, 0)},
+ {DEFINE_DBG(wvr, 1)},
+ {DEFINE_DBG(wvr, 2)},
+ {DEFINE_DBG(wvr, 3)},
+ {DEFINE_DBG(wvr, 4)},
+ {DEFINE_DBG(wvr, 5)},
+ {DEFINE_DBG(wvr, 6)},
+ {DEFINE_DBG(wvr, 7)},
+ {DEFINE_DBG(wvr, 8)},
+ {DEFINE_DBG(wvr, 9)},
+ {DEFINE_DBG(wvr, 10)},
+ {DEFINE_DBG(wvr, 11)},
+ {DEFINE_DBG(wvr, 12)},
+ {DEFINE_DBG(wvr, 13)},
+ {DEFINE_DBG(wvr, 14)},
+ {DEFINE_DBG(wvr, 15)},
+
+ {DEFINE_DBG(wcr, 0)},
+ {DEFINE_DBG(wcr, 1)},
+ {DEFINE_DBG(wcr, 2)},
+ {DEFINE_DBG(wcr, 3)},
+ {DEFINE_DBG(wcr, 4)},
+ {DEFINE_DBG(wcr, 5)},
+ {DEFINE_DBG(wcr, 6)},
+ {DEFINE_DBG(wcr, 7)},
+ {DEFINE_DBG(wcr, 8)},
+ {DEFINE_DBG(wcr, 9)},
+ {DEFINE_DBG(wcr, 10)},
+ {DEFINE_DBG(wcr, 11)},
+ {DEFINE_DBG(wcr, 12)},
+ {DEFINE_DBG(wcr, 13)},
+ {DEFINE_DBG(wcr, 14)},
+ {DEFINE_DBG(wcr, 15)}};
+
+#endif // DECLARE_REGISTER_INFOS_ARM_STRUCT
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h
new file mode 100644
index 000000000000..4ee0b528f229
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h
@@ -0,0 +1,712 @@
+//===-- RegisterInfos_arm64.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifdef DECLARE_REGISTER_INFOS_ARM64_STRUCT
+
+#include <stddef.h>
+
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-private.h"
+
+#include "Utility/ARM64_DWARF_Registers.h"
+#include "Utility/ARM64_ehframe_Registers.h"
+
+#ifndef GPR_OFFSET
+#error GPR_OFFSET must be defined before including this header file
+#endif
+
+#ifndef GPR_OFFSET_NAME
+#error GPR_OFFSET_NAME must be defined before including this header file
+#endif
+
+#ifndef FPU_OFFSET
+#error FPU_OFFSET must be defined before including this header file
+#endif
+
+#ifndef FPU_OFFSET_NAME
+#error FPU_OFFSET_NAME must be defined before including this header file
+#endif
+
+#ifndef EXC_OFFSET_NAME
+#error EXC_OFFSET_NAME must be defined before including this header file
+#endif
+
+#ifndef DBG_OFFSET_NAME
+#error DBG_OFFSET_NAME must be defined before including this header file
+#endif
+
+#ifndef DEFINE_DBG
+#error DEFINE_DBG must be defined before including this header file
+#endif
+
+// Offsets for a little-endian layout of the register context
+#define GPR_W_PSEUDO_REG_ENDIAN_OFFSET 0
+#define FPU_S_PSEUDO_REG_ENDIAN_OFFSET 0
+#define FPU_D_PSEUDO_REG_ENDIAN_OFFSET 0
+
+enum {
+ 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_x29 = 29,
+ gpr_fp = gpr_x29,
+ gpr_x30 = 30,
+ gpr_lr = gpr_x30,
+ gpr_ra = gpr_x30,
+ gpr_x31 = 31,
+ gpr_sp = gpr_x31,
+ gpr_pc = 32,
+ 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,
+
+ fpu_v0,
+ fpu_v1,
+ fpu_v2,
+ fpu_v3,
+ fpu_v4,
+ fpu_v5,
+ fpu_v6,
+ fpu_v7,
+ fpu_v8,
+ fpu_v9,
+ fpu_v10,
+ fpu_v11,
+ fpu_v12,
+ fpu_v13,
+ fpu_v14,
+ fpu_v15,
+ fpu_v16,
+ fpu_v17,
+ fpu_v18,
+ fpu_v19,
+ fpu_v20,
+ fpu_v21,
+ fpu_v22,
+ fpu_v23,
+ fpu_v24,
+ fpu_v25,
+ fpu_v26,
+ fpu_v27,
+ fpu_v28,
+ fpu_v29,
+ fpu_v30,
+ fpu_v31,
+
+ fpu_s0,
+ fpu_s1,
+ fpu_s2,
+ fpu_s3,
+ fpu_s4,
+ fpu_s5,
+ fpu_s6,
+ fpu_s7,
+ fpu_s8,
+ fpu_s9,
+ fpu_s10,
+ fpu_s11,
+ fpu_s12,
+ fpu_s13,
+ fpu_s14,
+ fpu_s15,
+ fpu_s16,
+ fpu_s17,
+ fpu_s18,
+ fpu_s19,
+ fpu_s20,
+ fpu_s21,
+ fpu_s22,
+ fpu_s23,
+ fpu_s24,
+ fpu_s25,
+ fpu_s26,
+ fpu_s27,
+ fpu_s28,
+ fpu_s29,
+ fpu_s30,
+ fpu_s31,
+
+ fpu_d0,
+ fpu_d1,
+ fpu_d2,
+ fpu_d3,
+ fpu_d4,
+ fpu_d5,
+ fpu_d6,
+ fpu_d7,
+ fpu_d8,
+ fpu_d9,
+ fpu_d10,
+ fpu_d11,
+ fpu_d12,
+ fpu_d13,
+ fpu_d14,
+ fpu_d15,
+ fpu_d16,
+ fpu_d17,
+ fpu_d18,
+ fpu_d19,
+ fpu_d20,
+ fpu_d21,
+ fpu_d22,
+ fpu_d23,
+ fpu_d24,
+ fpu_d25,
+ fpu_d26,
+ fpu_d27,
+ fpu_d28,
+ fpu_d29,
+ fpu_d30,
+ fpu_d31,
+
+ fpu_fpsr,
+ fpu_fpcr,
+
+ exc_far,
+ exc_esr,
+ exc_exception,
+
+ dbg_bvr0,
+ dbg_bvr1,
+ dbg_bvr2,
+ dbg_bvr3,
+ dbg_bvr4,
+ dbg_bvr5,
+ dbg_bvr6,
+ dbg_bvr7,
+ dbg_bvr8,
+ dbg_bvr9,
+ dbg_bvr10,
+ dbg_bvr11,
+ dbg_bvr12,
+ dbg_bvr13,
+ dbg_bvr14,
+ dbg_bvr15,
+
+ dbg_bcr0,
+ dbg_bcr1,
+ dbg_bcr2,
+ dbg_bcr3,
+ dbg_bcr4,
+ dbg_bcr5,
+ dbg_bcr6,
+ dbg_bcr7,
+ dbg_bcr8,
+ dbg_bcr9,
+ dbg_bcr10,
+ dbg_bcr11,
+ dbg_bcr12,
+ dbg_bcr13,
+ dbg_bcr14,
+ dbg_bcr15,
+
+ dbg_wvr0,
+ dbg_wvr1,
+ dbg_wvr2,
+ dbg_wvr3,
+ dbg_wvr4,
+ dbg_wvr5,
+ dbg_wvr6,
+ dbg_wvr7,
+ dbg_wvr8,
+ dbg_wvr9,
+ dbg_wvr10,
+ dbg_wvr11,
+ dbg_wvr12,
+ dbg_wvr13,
+ dbg_wvr14,
+ dbg_wvr15,
+
+ dbg_wcr0,
+ dbg_wcr1,
+ dbg_wcr2,
+ dbg_wcr3,
+ dbg_wcr4,
+ dbg_wcr5,
+ dbg_wcr6,
+ dbg_wcr7,
+ dbg_wcr8,
+ dbg_wcr9,
+ dbg_wcr10,
+ dbg_wcr11,
+ dbg_wcr12,
+ dbg_wcr13,
+ dbg_wcr14,
+ dbg_wcr15,
+
+ k_num_registers
+};
+
+static uint32_t g_contained_x0[] = {gpr_x0, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x1[] = {gpr_x1, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x2[] = {gpr_x2, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x3[] = {gpr_x3, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x4[] = {gpr_x4, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x5[] = {gpr_x5, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x6[] = {gpr_x6, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x7[] = {gpr_x7, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x8[] = {gpr_x8, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x9[] = {gpr_x9, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x10[] = {gpr_x10, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x11[] = {gpr_x11, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x12[] = {gpr_x12, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x13[] = {gpr_x13, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x14[] = {gpr_x14, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x15[] = {gpr_x15, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x16[] = {gpr_x16, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x17[] = {gpr_x17, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x18[] = {gpr_x18, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x19[] = {gpr_x19, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x20[] = {gpr_x20, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x21[] = {gpr_x21, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x22[] = {gpr_x22, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x23[] = {gpr_x23, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x24[] = {gpr_x24, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x25[] = {gpr_x25, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x26[] = {gpr_x26, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x27[] = {gpr_x27, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_x28[] = {gpr_x28, LLDB_INVALID_REGNUM};
+
+static uint32_t g_w0_invalidates[] = {gpr_x0, LLDB_INVALID_REGNUM};
+static uint32_t g_w1_invalidates[] = {gpr_x1, LLDB_INVALID_REGNUM};
+static uint32_t g_w2_invalidates[] = {gpr_x2, LLDB_INVALID_REGNUM};
+static uint32_t g_w3_invalidates[] = {gpr_x3, LLDB_INVALID_REGNUM};
+static uint32_t g_w4_invalidates[] = {gpr_x4, LLDB_INVALID_REGNUM};
+static uint32_t g_w5_invalidates[] = {gpr_x5, LLDB_INVALID_REGNUM};
+static uint32_t g_w6_invalidates[] = {gpr_x6, LLDB_INVALID_REGNUM};
+static uint32_t g_w7_invalidates[] = {gpr_x7, LLDB_INVALID_REGNUM};
+static uint32_t g_w8_invalidates[] = {gpr_x8, LLDB_INVALID_REGNUM};
+static uint32_t g_w9_invalidates[] = {gpr_x9, LLDB_INVALID_REGNUM};
+static uint32_t g_w10_invalidates[] = {gpr_x10, LLDB_INVALID_REGNUM};
+static uint32_t g_w11_invalidates[] = {gpr_x11, LLDB_INVALID_REGNUM};
+static uint32_t g_w12_invalidates[] = {gpr_x12, LLDB_INVALID_REGNUM};
+static uint32_t g_w13_invalidates[] = {gpr_x13, LLDB_INVALID_REGNUM};
+static uint32_t g_w14_invalidates[] = {gpr_x14, LLDB_INVALID_REGNUM};
+static uint32_t g_w15_invalidates[] = {gpr_x15, LLDB_INVALID_REGNUM};
+static uint32_t g_w16_invalidates[] = {gpr_x16, LLDB_INVALID_REGNUM};
+static uint32_t g_w17_invalidates[] = {gpr_x17, LLDB_INVALID_REGNUM};
+static uint32_t g_w18_invalidates[] = {gpr_x18, LLDB_INVALID_REGNUM};
+static uint32_t g_w19_invalidates[] = {gpr_x19, LLDB_INVALID_REGNUM};
+static uint32_t g_w20_invalidates[] = {gpr_x20, LLDB_INVALID_REGNUM};
+static uint32_t g_w21_invalidates[] = {gpr_x21, LLDB_INVALID_REGNUM};
+static uint32_t g_w22_invalidates[] = {gpr_x22, LLDB_INVALID_REGNUM};
+static uint32_t g_w23_invalidates[] = {gpr_x23, LLDB_INVALID_REGNUM};
+static uint32_t g_w24_invalidates[] = {gpr_x24, LLDB_INVALID_REGNUM};
+static uint32_t g_w25_invalidates[] = {gpr_x25, LLDB_INVALID_REGNUM};
+static uint32_t g_w26_invalidates[] = {gpr_x26, LLDB_INVALID_REGNUM};
+static uint32_t g_w27_invalidates[] = {gpr_x27, LLDB_INVALID_REGNUM};
+static uint32_t g_w28_invalidates[] = {gpr_x28, LLDB_INVALID_REGNUM};
+
+static uint32_t g_contained_v0[] = {fpu_v0, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v1[] = {fpu_v1, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v2[] = {fpu_v2, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v3[] = {fpu_v3, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v4[] = {fpu_v4, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v5[] = {fpu_v5, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v6[] = {fpu_v6, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v7[] = {fpu_v7, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v8[] = {fpu_v8, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v9[] = {fpu_v9, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v10[] = {fpu_v10, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v11[] = {fpu_v11, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v12[] = {fpu_v12, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v13[] = {fpu_v13, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v14[] = {fpu_v14, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v15[] = {fpu_v15, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v16[] = {fpu_v16, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v17[] = {fpu_v17, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v18[] = {fpu_v18, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v19[] = {fpu_v19, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v20[] = {fpu_v20, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v21[] = {fpu_v21, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v22[] = {fpu_v22, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v23[] = {fpu_v23, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v24[] = {fpu_v24, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v25[] = {fpu_v25, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v26[] = {fpu_v26, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v27[] = {fpu_v27, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v28[] = {fpu_v28, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v29[] = {fpu_v29, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v30[] = {fpu_v30, LLDB_INVALID_REGNUM};
+static uint32_t g_contained_v31[] = {fpu_v31, LLDB_INVALID_REGNUM};
+
+static uint32_t g_s0_invalidates[] = {fpu_v0, fpu_d0, LLDB_INVALID_REGNUM};
+static uint32_t g_s1_invalidates[] = {fpu_v1, fpu_d1, LLDB_INVALID_REGNUM};
+static uint32_t g_s2_invalidates[] = {fpu_v2, fpu_d2, LLDB_INVALID_REGNUM};
+static uint32_t g_s3_invalidates[] = {fpu_v3, fpu_d3, LLDB_INVALID_REGNUM};
+static uint32_t g_s4_invalidates[] = {fpu_v4, fpu_d4, LLDB_INVALID_REGNUM};
+static uint32_t g_s5_invalidates[] = {fpu_v5, fpu_d5, LLDB_INVALID_REGNUM};
+static uint32_t g_s6_invalidates[] = {fpu_v6, fpu_d6, LLDB_INVALID_REGNUM};
+static uint32_t g_s7_invalidates[] = {fpu_v7, fpu_d7, LLDB_INVALID_REGNUM};
+static uint32_t g_s8_invalidates[] = {fpu_v8, fpu_d8, LLDB_INVALID_REGNUM};
+static uint32_t g_s9_invalidates[] = {fpu_v9, fpu_d9, LLDB_INVALID_REGNUM};
+static uint32_t g_s10_invalidates[] = {fpu_v10, fpu_d10, LLDB_INVALID_REGNUM};
+static uint32_t g_s11_invalidates[] = {fpu_v11, fpu_d11, LLDB_INVALID_REGNUM};
+static uint32_t g_s12_invalidates[] = {fpu_v12, fpu_d12, LLDB_INVALID_REGNUM};
+static uint32_t g_s13_invalidates[] = {fpu_v13, fpu_d13, LLDB_INVALID_REGNUM};
+static uint32_t g_s14_invalidates[] = {fpu_v14, fpu_d14, LLDB_INVALID_REGNUM};
+static uint32_t g_s15_invalidates[] = {fpu_v15, fpu_d15, LLDB_INVALID_REGNUM};
+static uint32_t g_s16_invalidates[] = {fpu_v16, fpu_d16, LLDB_INVALID_REGNUM};
+static uint32_t g_s17_invalidates[] = {fpu_v17, fpu_d17, LLDB_INVALID_REGNUM};
+static uint32_t g_s18_invalidates[] = {fpu_v18, fpu_d18, LLDB_INVALID_REGNUM};
+static uint32_t g_s19_invalidates[] = {fpu_v19, fpu_d19, LLDB_INVALID_REGNUM};
+static uint32_t g_s20_invalidates[] = {fpu_v20, fpu_d20, LLDB_INVALID_REGNUM};
+static uint32_t g_s21_invalidates[] = {fpu_v21, fpu_d21, LLDB_INVALID_REGNUM};
+static uint32_t g_s22_invalidates[] = {fpu_v22, fpu_d22, LLDB_INVALID_REGNUM};
+static uint32_t g_s23_invalidates[] = {fpu_v23, fpu_d23, LLDB_INVALID_REGNUM};
+static uint32_t g_s24_invalidates[] = {fpu_v24, fpu_d24, LLDB_INVALID_REGNUM};
+static uint32_t g_s25_invalidates[] = {fpu_v25, fpu_d25, LLDB_INVALID_REGNUM};
+static uint32_t g_s26_invalidates[] = {fpu_v26, fpu_d26, LLDB_INVALID_REGNUM};
+static uint32_t g_s27_invalidates[] = {fpu_v27, fpu_d27, LLDB_INVALID_REGNUM};
+static uint32_t g_s28_invalidates[] = {fpu_v28, fpu_d28, LLDB_INVALID_REGNUM};
+static uint32_t g_s29_invalidates[] = {fpu_v29, fpu_d29, LLDB_INVALID_REGNUM};
+static uint32_t g_s30_invalidates[] = {fpu_v30, fpu_d30, LLDB_INVALID_REGNUM};
+static uint32_t g_s31_invalidates[] = {fpu_v31, fpu_d31, LLDB_INVALID_REGNUM};
+
+static uint32_t g_d0_invalidates[] = {fpu_v0, fpu_s0, LLDB_INVALID_REGNUM};
+static uint32_t g_d1_invalidates[] = {fpu_v1, fpu_s1, LLDB_INVALID_REGNUM};
+static uint32_t g_d2_invalidates[] = {fpu_v2, fpu_s2, LLDB_INVALID_REGNUM};
+static uint32_t g_d3_invalidates[] = {fpu_v3, fpu_s3, LLDB_INVALID_REGNUM};
+static uint32_t g_d4_invalidates[] = {fpu_v4, fpu_s4, LLDB_INVALID_REGNUM};
+static uint32_t g_d5_invalidates[] = {fpu_v5, fpu_s5, LLDB_INVALID_REGNUM};
+static uint32_t g_d6_invalidates[] = {fpu_v6, fpu_s6, LLDB_INVALID_REGNUM};
+static uint32_t g_d7_invalidates[] = {fpu_v7, fpu_s7, LLDB_INVALID_REGNUM};
+static uint32_t g_d8_invalidates[] = {fpu_v8, fpu_s8, LLDB_INVALID_REGNUM};
+static uint32_t g_d9_invalidates[] = {fpu_v9, fpu_s9, LLDB_INVALID_REGNUM};
+static uint32_t g_d10_invalidates[] = {fpu_v10, fpu_s10, LLDB_INVALID_REGNUM};
+static uint32_t g_d11_invalidates[] = {fpu_v11, fpu_s11, LLDB_INVALID_REGNUM};
+static uint32_t g_d12_invalidates[] = {fpu_v12, fpu_s12, LLDB_INVALID_REGNUM};
+static uint32_t g_d13_invalidates[] = {fpu_v13, fpu_s13, LLDB_INVALID_REGNUM};
+static uint32_t g_d14_invalidates[] = {fpu_v14, fpu_s14, LLDB_INVALID_REGNUM};
+static uint32_t g_d15_invalidates[] = {fpu_v15, fpu_s15, LLDB_INVALID_REGNUM};
+static uint32_t g_d16_invalidates[] = {fpu_v16, fpu_s16, LLDB_INVALID_REGNUM};
+static uint32_t g_d17_invalidates[] = {fpu_v17, fpu_s17, LLDB_INVALID_REGNUM};
+static uint32_t g_d18_invalidates[] = {fpu_v18, fpu_s18, LLDB_INVALID_REGNUM};
+static uint32_t g_d19_invalidates[] = {fpu_v19, fpu_s19, LLDB_INVALID_REGNUM};
+static uint32_t g_d20_invalidates[] = {fpu_v20, fpu_s20, LLDB_INVALID_REGNUM};
+static uint32_t g_d21_invalidates[] = {fpu_v21, fpu_s21, LLDB_INVALID_REGNUM};
+static uint32_t g_d22_invalidates[] = {fpu_v22, fpu_s22, LLDB_INVALID_REGNUM};
+static uint32_t g_d23_invalidates[] = {fpu_v23, fpu_s23, LLDB_INVALID_REGNUM};
+static uint32_t g_d24_invalidates[] = {fpu_v24, fpu_s24, LLDB_INVALID_REGNUM};
+static uint32_t g_d25_invalidates[] = {fpu_v25, fpu_s25, LLDB_INVALID_REGNUM};
+static uint32_t g_d26_invalidates[] = {fpu_v26, fpu_s26, LLDB_INVALID_REGNUM};
+static uint32_t g_d27_invalidates[] = {fpu_v27, fpu_s27, LLDB_INVALID_REGNUM};
+static uint32_t g_d28_invalidates[] = {fpu_v28, fpu_s28, LLDB_INVALID_REGNUM};
+static uint32_t g_d29_invalidates[] = {fpu_v29, fpu_s29, LLDB_INVALID_REGNUM};
+static uint32_t g_d30_invalidates[] = {fpu_v30, fpu_s30, LLDB_INVALID_REGNUM};
+static uint32_t g_d31_invalidates[] = {fpu_v31, fpu_s31, LLDB_INVALID_REGNUM};
+
+static lldb_private::RegisterInfo g_register_infos_arm64_le[] = {
+ // clang-format off
+ // General purpose registers
+ // NAME ALT SZ OFFSET ENCODING FORMAT EH_FRAME DWARF GENERIC PROCESS PLUGIN LLDB VALUE REGS INVAL DYNEXPR SZ
+ // ===== ======= == ============= =================== ================ ================= =============== ======================== =================== ====== ============== ======= ======= ==
+ {"x0", nullptr, 8, GPR_OFFSET(0), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x0, arm64_dwarf::x0, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM, gpr_x0}, nullptr, nullptr, nullptr, 0},
+ {"x1", nullptr, 8, GPR_OFFSET(1), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x1, arm64_dwarf::x1, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM, gpr_x1}, nullptr, nullptr, nullptr, 0},
+ {"x2", nullptr, 8, GPR_OFFSET(2), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x2, arm64_dwarf::x2, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM, gpr_x2}, nullptr, nullptr, nullptr, 0},
+ {"x3", nullptr, 8, GPR_OFFSET(3), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x3, arm64_dwarf::x3, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM, gpr_x3}, nullptr, nullptr, nullptr, 0},
+ {"x4", nullptr, 8, GPR_OFFSET(4), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x4, arm64_dwarf::x4, LLDB_REGNUM_GENERIC_ARG5, LLDB_INVALID_REGNUM, gpr_x4}, nullptr, nullptr, nullptr, 0},
+ {"x5", nullptr, 8, GPR_OFFSET(5), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x5, arm64_dwarf::x5, LLDB_REGNUM_GENERIC_ARG6, LLDB_INVALID_REGNUM, gpr_x5}, nullptr, nullptr, nullptr, 0},
+ {"x6", nullptr, 8, GPR_OFFSET(6), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x6, arm64_dwarf::x6, LLDB_REGNUM_GENERIC_ARG7, LLDB_INVALID_REGNUM, gpr_x6}, nullptr, nullptr, nullptr, 0},
+ {"x7", nullptr, 8, GPR_OFFSET(7), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x7, arm64_dwarf::x7, LLDB_REGNUM_GENERIC_ARG8, LLDB_INVALID_REGNUM, gpr_x7}, nullptr, nullptr, nullptr, 0},
+ {"x8", nullptr, 8, GPR_OFFSET(8), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x8, arm64_dwarf::x8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x8}, nullptr, nullptr, nullptr, 0},
+ {"x9", nullptr, 8, GPR_OFFSET(9), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x9, arm64_dwarf::x9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x9}, nullptr, nullptr, nullptr, 0},
+ {"x10", nullptr, 8, GPR_OFFSET(10), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x10, arm64_dwarf::x10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x10}, nullptr, nullptr, nullptr, 0},
+ {"x11", nullptr, 8, GPR_OFFSET(11), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x11, arm64_dwarf::x11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x11}, nullptr, nullptr, nullptr, 0},
+ {"x12", nullptr, 8, GPR_OFFSET(12), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x12, arm64_dwarf::x12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x12}, nullptr, nullptr, nullptr, 0},
+ {"x13", nullptr, 8, GPR_OFFSET(13), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x13, arm64_dwarf::x13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x13}, nullptr, nullptr, nullptr, 0},
+ {"x14", nullptr, 8, GPR_OFFSET(14), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x14, arm64_dwarf::x14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x14}, nullptr, nullptr, nullptr, 0},
+ {"x15", nullptr, 8, GPR_OFFSET(15), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x15, arm64_dwarf::x15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x15}, nullptr, nullptr, nullptr, 0},
+ {"x16", nullptr, 8, GPR_OFFSET(16), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x16, arm64_dwarf::x16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x16}, nullptr, nullptr, nullptr, 0},
+ {"x17", nullptr, 8, GPR_OFFSET(17), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x17, arm64_dwarf::x17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x17}, nullptr, nullptr, nullptr, 0},
+ {"x18", nullptr, 8, GPR_OFFSET(18), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x18, arm64_dwarf::x18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x18}, nullptr, nullptr, nullptr, 0},
+ {"x19", nullptr, 8, GPR_OFFSET(19), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x19, arm64_dwarf::x19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x19}, nullptr, nullptr, nullptr, 0},
+ {"x20", nullptr, 8, GPR_OFFSET(20), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x20, arm64_dwarf::x20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x20}, nullptr, nullptr, nullptr, 0},
+ {"x21", nullptr, 8, GPR_OFFSET(21), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x21, arm64_dwarf::x21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x21}, nullptr, nullptr, nullptr, 0},
+ {"x22", nullptr, 8, GPR_OFFSET(22), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x22, arm64_dwarf::x22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x22}, nullptr, nullptr, nullptr, 0},
+ {"x23", nullptr, 8, GPR_OFFSET(23), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x23, arm64_dwarf::x23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x23}, nullptr, nullptr, nullptr, 0},
+ {"x24", nullptr, 8, GPR_OFFSET(24), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x24, arm64_dwarf::x24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x24}, nullptr, nullptr, nullptr, 0},
+ {"x25", nullptr, 8, GPR_OFFSET(25), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x25, arm64_dwarf::x25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x25}, nullptr, nullptr, nullptr, 0},
+ {"x26", nullptr, 8, GPR_OFFSET(26), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x26, arm64_dwarf::x26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x26}, nullptr, nullptr, nullptr, 0},
+ {"x27", nullptr, 8, GPR_OFFSET(27), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x27, arm64_dwarf::x27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x27}, nullptr, nullptr, nullptr, 0},
+ {"x28", nullptr, 8, GPR_OFFSET(28), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::x28, arm64_dwarf::x28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_x28}, nullptr, nullptr, nullptr, 0},
+ {"fp", "x29", 8, GPR_OFFSET(29), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::fp, arm64_dwarf::fp, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM, gpr_fp}, nullptr, nullptr, nullptr, 0},
+ {"lr", "x30", 8, GPR_OFFSET(30), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::lr, arm64_dwarf::lr, LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM, gpr_lr}, nullptr, nullptr, nullptr, 0},
+ {"sp", "x31", 8, GPR_OFFSET(31), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::sp, arm64_dwarf::sp, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM, gpr_sp}, nullptr, nullptr, nullptr, 0},
+ {"pc", nullptr, 8, GPR_OFFSET(32), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::pc, arm64_dwarf::pc, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM, gpr_pc}, nullptr, nullptr, nullptr, 0},
+
+ {"cpsr",nullptr, 4, GPR_OFFSET_NAME(cpsr), lldb::eEncodingUint, lldb::eFormatHex, {arm64_ehframe::cpsr, arm64_dwarf::cpsr, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM, gpr_cpsr}, nullptr, nullptr, nullptr, 0},
+
+ // NAME ALT SZ OFFSET ENCODING FORMAT EH_FRAME DWARF GENERIC PROCESS PLUGIN LLDB VALUE INVALIDATES DYNEXPR SZ
+ // ===== ======= == ============================================== =================== ================ ================= =============== =================== =================== ====== =============== ================= ======= ==
+ {"w0", nullptr, 4, GPR_OFFSET(0) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w0}, g_contained_x0, g_w0_invalidates, nullptr, 0},
+ {"w1", nullptr, 4, GPR_OFFSET(1) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w1}, g_contained_x1, g_w1_invalidates, nullptr, 0},
+ {"w2", nullptr, 4, GPR_OFFSET(2) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w2}, g_contained_x2, g_w2_invalidates, nullptr, 0},
+ {"w3", nullptr, 4, GPR_OFFSET(3) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w3}, g_contained_x3, g_w3_invalidates, nullptr, 0},
+ {"w4", nullptr, 4, GPR_OFFSET(4) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w4}, g_contained_x4, g_w4_invalidates, nullptr, 0},
+ {"w5", nullptr, 4, GPR_OFFSET(5) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w5}, g_contained_x5, g_w5_invalidates, nullptr, 0},
+ {"w6", nullptr, 4, GPR_OFFSET(6) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w6}, g_contained_x6, g_w6_invalidates, nullptr, 0},
+ {"w7", nullptr, 4, GPR_OFFSET(7) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w7}, g_contained_x7, g_w7_invalidates, nullptr, 0},
+ {"w8", nullptr, 4, GPR_OFFSET(8) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w8}, g_contained_x8, g_w8_invalidates, nullptr, 0},
+ {"w9", nullptr, 4, GPR_OFFSET(9) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w9}, g_contained_x9, g_w9_invalidates, nullptr, 0},
+ {"w10", nullptr, 4, GPR_OFFSET(10) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w10}, g_contained_x10, g_w10_invalidates, nullptr, 0},
+ {"w11", nullptr, 4, GPR_OFFSET(11) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w11}, g_contained_x11, g_w11_invalidates, nullptr, 0},
+ {"w12", nullptr, 4, GPR_OFFSET(12) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w12}, g_contained_x12, g_w12_invalidates, nullptr, 0},
+ {"w13", nullptr, 4, GPR_OFFSET(13) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w13}, g_contained_x13, g_w13_invalidates, nullptr, 0},
+ {"w14", nullptr, 4, GPR_OFFSET(14) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w14}, g_contained_x14, g_w14_invalidates, nullptr, 0},
+ {"w15", nullptr, 4, GPR_OFFSET(15) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w15}, g_contained_x15, g_w15_invalidates, nullptr, 0},
+ {"w16", nullptr, 4, GPR_OFFSET(16) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w16}, g_contained_x16, g_w16_invalidates, nullptr, 0},
+ {"w17", nullptr, 4, GPR_OFFSET(17) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w17}, g_contained_x17, g_w17_invalidates, nullptr, 0},
+ {"w18", nullptr, 4, GPR_OFFSET(18) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w18}, g_contained_x18, g_w18_invalidates, nullptr, 0},
+ {"w19", nullptr, 4, GPR_OFFSET(19) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w19}, g_contained_x19, g_w19_invalidates, nullptr, 0},
+ {"w20", nullptr, 4, GPR_OFFSET(20) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w20}, g_contained_x20, g_w20_invalidates, nullptr, 0},
+ {"w21", nullptr, 4, GPR_OFFSET(21) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w21}, g_contained_x21, g_w21_invalidates, nullptr, 0},
+ {"w22", nullptr, 4, GPR_OFFSET(22) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w22}, g_contained_x22, g_w22_invalidates, nullptr, 0},
+ {"w23", nullptr, 4, GPR_OFFSET(23) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w23}, g_contained_x23, g_w23_invalidates, nullptr, 0},
+ {"w24", nullptr, 4, GPR_OFFSET(24) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w24}, g_contained_x24, g_w24_invalidates, nullptr, 0},
+ {"w25", nullptr, 4, GPR_OFFSET(25) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w25}, g_contained_x25, g_w25_invalidates, nullptr, 0},
+ {"w26", nullptr, 4, GPR_OFFSET(26) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w26}, g_contained_x26, g_w26_invalidates, nullptr, 0},
+ {"w27", nullptr, 4, GPR_OFFSET(27) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w27}, g_contained_x27, g_w27_invalidates, nullptr, 0},
+ {"w28", nullptr, 4, GPR_OFFSET(28) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_w28}, g_contained_x28, g_w28_invalidates, nullptr, 0},
+
+ // NAME ALT SZ OFFSET ENCODING FORMAT EH_FRAME DWARF GENERIC PROCESS PLUGIN LLDB VALUE REGS INVAL DYNEXPR SZ
+ // ===== ======= == ============= =================== ================ ================= =============== =================== =================== ====== ============== ======= ======= ==
+ {"v0", nullptr, 16, FPU_OFFSET(0), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v0}, nullptr, nullptr, nullptr, 0},
+ {"v1", nullptr, 16, FPU_OFFSET(1), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v1}, nullptr, nullptr, nullptr, 0},
+ {"v2", nullptr, 16, FPU_OFFSET(2), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v2}, nullptr, nullptr, nullptr, 0},
+ {"v3", nullptr, 16, FPU_OFFSET(3), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v3}, nullptr, nullptr, nullptr, 0},
+ {"v4", nullptr, 16, FPU_OFFSET(4), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v4}, nullptr, nullptr, nullptr, 0},
+ {"v5", nullptr, 16, FPU_OFFSET(5), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v5}, nullptr, nullptr, nullptr, 0},
+ {"v6", nullptr, 16, FPU_OFFSET(6), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v6}, nullptr, nullptr, nullptr, 0},
+ {"v7", nullptr, 16, FPU_OFFSET(7), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v7}, nullptr, nullptr, nullptr, 0},
+ {"v8", nullptr, 16, FPU_OFFSET(8), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v8}, nullptr, nullptr, nullptr, 0},
+ {"v9", nullptr, 16, FPU_OFFSET(9), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v9}, nullptr, nullptr, nullptr, 0},
+ {"v10", nullptr, 16, FPU_OFFSET(10), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v10}, nullptr, nullptr, nullptr, 0},
+ {"v11", nullptr, 16, FPU_OFFSET(11), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v11}, nullptr, nullptr, nullptr, 0},
+ {"v12", nullptr, 16, FPU_OFFSET(12), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v12}, nullptr, nullptr, nullptr, 0},
+ {"v13", nullptr, 16, FPU_OFFSET(13), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v13}, nullptr, nullptr, nullptr, 0},
+ {"v14", nullptr, 16, FPU_OFFSET(14), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v14}, nullptr, nullptr, nullptr, 0},
+ {"v15", nullptr, 16, FPU_OFFSET(15), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v15}, nullptr, nullptr, nullptr, 0},
+ {"v16", nullptr, 16, FPU_OFFSET(16), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v16}, nullptr, nullptr, nullptr, 0},
+ {"v17", nullptr, 16, FPU_OFFSET(17), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v17}, nullptr, nullptr, nullptr, 0},
+ {"v18", nullptr, 16, FPU_OFFSET(18), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v18}, nullptr, nullptr, nullptr, 0},
+ {"v19", nullptr, 16, FPU_OFFSET(19), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v19}, nullptr, nullptr, nullptr, 0},
+ {"v20", nullptr, 16, FPU_OFFSET(20), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v20}, nullptr, nullptr, nullptr, 0},
+ {"v21", nullptr, 16, FPU_OFFSET(21), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v21}, nullptr, nullptr, nullptr, 0},
+ {"v22", nullptr, 16, FPU_OFFSET(22), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v22}, nullptr, nullptr, nullptr, 0},
+ {"v23", nullptr, 16, FPU_OFFSET(23), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v23}, nullptr, nullptr, nullptr, 0},
+ {"v24", nullptr, 16, FPU_OFFSET(24), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v24}, nullptr, nullptr, nullptr, 0},
+ {"v25", nullptr, 16, FPU_OFFSET(25), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v25}, nullptr, nullptr, nullptr, 0},
+ {"v26", nullptr, 16, FPU_OFFSET(26), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v26}, nullptr, nullptr, nullptr, 0},
+ {"v27", nullptr, 16, FPU_OFFSET(27), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v27}, nullptr, nullptr, nullptr, 0},
+ {"v28", nullptr, 16, FPU_OFFSET(28), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v28}, nullptr, nullptr, nullptr, 0},
+ {"v29", nullptr, 16, FPU_OFFSET(29), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v29, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v29}, nullptr, nullptr, nullptr, 0},
+ {"v30", nullptr, 16, FPU_OFFSET(30), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v30, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v30}, nullptr, nullptr, nullptr, 0},
+ {"v31", nullptr, 16, FPU_OFFSET(31), lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, arm64_dwarf::v31, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_v31}, nullptr, nullptr, nullptr, 0},
+
+ // NAME ALT SZ OFFSET ENCODING FORMAT EH_FRAME DWARF GENERIC PROCESS PLUGIN LLDB VALUE REGS INVALIDATES DYNEXPR SZ
+ // ===== ======= == ============================================== =================== ================ ================= =============== =================== =================== ====== =============== ================= ======= ==
+ {"s0", nullptr, 4, FPU_OFFSET(0) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s0}, g_contained_v0, g_s0_invalidates, nullptr, 0},
+ {"s1", nullptr, 4, FPU_OFFSET(1) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s1}, g_contained_v1, g_s1_invalidates, nullptr, 0},
+ {"s2", nullptr, 4, FPU_OFFSET(2) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s2}, g_contained_v2, g_s2_invalidates, nullptr, 0},
+ {"s3", nullptr, 4, FPU_OFFSET(3) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s3}, g_contained_v3, g_s3_invalidates, nullptr, 0},
+ {"s4", nullptr, 4, FPU_OFFSET(4) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s4}, g_contained_v4, g_s4_invalidates, nullptr, 0},
+ {"s5", nullptr, 4, FPU_OFFSET(5) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s5}, g_contained_v5, g_s5_invalidates, nullptr, 0},
+ {"s6", nullptr, 4, FPU_OFFSET(6) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s6}, g_contained_v6, g_s6_invalidates, nullptr, 0},
+ {"s7", nullptr, 4, FPU_OFFSET(7) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s7}, g_contained_v7, g_s7_invalidates, nullptr, 0},
+ {"s8", nullptr, 4, FPU_OFFSET(8) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s8}, g_contained_v8, g_s8_invalidates, nullptr, 0},
+ {"s9", nullptr, 4, FPU_OFFSET(9) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s9}, g_contained_v9, g_s9_invalidates, nullptr, 0},
+ {"s10", nullptr, 4, FPU_OFFSET(10) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s10}, g_contained_v10, g_s10_invalidates, nullptr, 0},
+ {"s11", nullptr, 4, FPU_OFFSET(11) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s11}, g_contained_v11, g_s11_invalidates, nullptr, 0},
+ {"s12", nullptr, 4, FPU_OFFSET(12) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s12}, g_contained_v12, g_s12_invalidates, nullptr, 0},
+ {"s13", nullptr, 4, FPU_OFFSET(13) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s13}, g_contained_v13, g_s13_invalidates, nullptr, 0},
+ {"s14", nullptr, 4, FPU_OFFSET(14) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s14}, g_contained_v14, g_s14_invalidates, nullptr, 0},
+ {"s15", nullptr, 4, FPU_OFFSET(15) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s15}, g_contained_v15, g_s15_invalidates, nullptr, 0},
+ {"s16", nullptr, 4, FPU_OFFSET(16) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s16}, g_contained_v16, g_s16_invalidates, nullptr, 0},
+ {"s17", nullptr, 4, FPU_OFFSET(17) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s17}, g_contained_v17, g_s17_invalidates, nullptr, 0},
+ {"s18", nullptr, 4, FPU_OFFSET(18) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s18}, g_contained_v18, g_s18_invalidates, nullptr, 0},
+ {"s19", nullptr, 4, FPU_OFFSET(19) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s19}, g_contained_v19, g_s19_invalidates, nullptr, 0},
+ {"s20", nullptr, 4, FPU_OFFSET(20) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s20}, g_contained_v20, g_s20_invalidates, nullptr, 0},
+ {"s21", nullptr, 4, FPU_OFFSET(21) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s21}, g_contained_v21, g_s21_invalidates, nullptr, 0},
+ {"s22", nullptr, 4, FPU_OFFSET(22) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s22}, g_contained_v22, g_s22_invalidates, nullptr, 0},
+ {"s23", nullptr, 4, FPU_OFFSET(23) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s23}, g_contained_v23, g_s23_invalidates, nullptr, 0},
+ {"s24", nullptr, 4, FPU_OFFSET(24) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s24}, g_contained_v24, g_s24_invalidates, nullptr, 0},
+ {"s25", nullptr, 4, FPU_OFFSET(25) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s25}, g_contained_v25, g_s25_invalidates, nullptr, 0},
+ {"s26", nullptr, 4, FPU_OFFSET(26) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s26}, g_contained_v26, g_s26_invalidates, nullptr, 0},
+ {"s27", nullptr, 4, FPU_OFFSET(27) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s27}, g_contained_v27, g_s27_invalidates, nullptr, 0},
+ {"s28", nullptr, 4, FPU_OFFSET(28) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s28}, g_contained_v28, g_s28_invalidates, nullptr, 0},
+ {"s29", nullptr, 4, FPU_OFFSET(29) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s29}, g_contained_v29, g_s29_invalidates, nullptr, 0},
+ {"s30", nullptr, 4, FPU_OFFSET(30) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s30}, g_contained_v30, g_s30_invalidates, nullptr, 0},
+ {"s31", nullptr, 4, FPU_OFFSET(31) + FPU_S_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_s31}, g_contained_v31, g_s31_invalidates, nullptr, 0},
+
+ {"d0", nullptr, 8, FPU_OFFSET(0) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d0}, g_contained_v0, g_d0_invalidates, nullptr, 0},
+ {"d1", nullptr, 8, FPU_OFFSET(1) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d1}, g_contained_v1, g_d1_invalidates, nullptr, 0},
+ {"d2", nullptr, 8, FPU_OFFSET(2) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d2}, g_contained_v2, g_d2_invalidates, nullptr, 0},
+ {"d3", nullptr, 8, FPU_OFFSET(3) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d3}, g_contained_v3, g_d3_invalidates, nullptr, 0},
+ {"d4", nullptr, 8, FPU_OFFSET(4) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d4}, g_contained_v4, g_d4_invalidates, nullptr, 0},
+ {"d5", nullptr, 8, FPU_OFFSET(5) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d5}, g_contained_v5, g_d5_invalidates, nullptr, 0},
+ {"d6", nullptr, 8, FPU_OFFSET(6) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d6}, g_contained_v6, g_d6_invalidates, nullptr, 0},
+ {"d7", nullptr, 8, FPU_OFFSET(7) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d7}, g_contained_v7, g_d7_invalidates, nullptr, 0},
+ {"d8", nullptr, 8, FPU_OFFSET(8) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d8}, g_contained_v8, g_d8_invalidates, nullptr, 0},
+ {"d9", nullptr, 8, FPU_OFFSET(9) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d9}, g_contained_v9, g_d9_invalidates, nullptr, 0},
+ {"d10", nullptr, 8, FPU_OFFSET(10) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d10}, g_contained_v10, g_d10_invalidates, nullptr, 0},
+ {"d11", nullptr, 8, FPU_OFFSET(11) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d11}, g_contained_v11, g_d11_invalidates, nullptr, 0},
+ {"d12", nullptr, 8, FPU_OFFSET(12) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d12}, g_contained_v12, g_d12_invalidates, nullptr, 0},
+ {"d13", nullptr, 8, FPU_OFFSET(13) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d13}, g_contained_v13, g_d13_invalidates, nullptr, 0},
+ {"d14", nullptr, 8, FPU_OFFSET(14) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d14}, g_contained_v14, g_d14_invalidates, nullptr, 0},
+ {"d15", nullptr, 8, FPU_OFFSET(15) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d15}, g_contained_v15, g_d15_invalidates, nullptr, 0},
+ {"d16", nullptr, 8, FPU_OFFSET(16) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d16}, g_contained_v16, g_d16_invalidates, nullptr, 0},
+ {"d17", nullptr, 8, FPU_OFFSET(17) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d17}, g_contained_v17, g_d17_invalidates, nullptr, 0},
+ {"d18", nullptr, 8, FPU_OFFSET(18) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d18}, g_contained_v18, g_d18_invalidates, nullptr, 0},
+ {"d19", nullptr, 8, FPU_OFFSET(19) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d19}, g_contained_v19, g_d19_invalidates, nullptr, 0},
+ {"d20", nullptr, 8, FPU_OFFSET(20) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d20}, g_contained_v20, g_d20_invalidates, nullptr, 0},
+ {"d21", nullptr, 8, FPU_OFFSET(21) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d21}, g_contained_v21, g_d21_invalidates, nullptr, 0},
+ {"d22", nullptr, 8, FPU_OFFSET(22) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d22}, g_contained_v22, g_d22_invalidates, nullptr, 0},
+ {"d23", nullptr, 8, FPU_OFFSET(23) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d23}, g_contained_v23, g_d23_invalidates, nullptr, 0},
+ {"d24", nullptr, 8, FPU_OFFSET(24) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d24}, g_contained_v24, g_d24_invalidates, nullptr, 0},
+ {"d25", nullptr, 8, FPU_OFFSET(25) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d25}, g_contained_v25, g_d25_invalidates, nullptr, 0},
+ {"d26", nullptr, 8, FPU_OFFSET(26) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d26}, g_contained_v26, g_d26_invalidates, nullptr, 0},
+ {"d27", nullptr, 8, FPU_OFFSET(27) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d27}, g_contained_v27, g_d27_invalidates, nullptr, 0},
+ {"d28", nullptr, 8, FPU_OFFSET(28) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d28}, g_contained_v28, g_d28_invalidates, nullptr, 0},
+ {"d29", nullptr, 8, FPU_OFFSET(29) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d29}, g_contained_v29, g_d29_invalidates, nullptr, 0},
+ {"d30", nullptr, 8, FPU_OFFSET(30) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d30}, g_contained_v30, g_d30_invalidates, nullptr, 0},
+ {"d31", nullptr, 8, FPU_OFFSET(31) + FPU_D_PSEUDO_REG_ENDIAN_OFFSET, lldb::eEncodingIEEE754, lldb::eFormatFloat, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_d31}, g_contained_v31, g_d31_invalidates, nullptr, 0},
+
+ {"fpsr", nullptr, 4, FPU_OFFSET_NAME(fpsr), lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_fpsr}, nullptr, nullptr, nullptr, 0},
+ {"fpcr", nullptr, 4, FPU_OFFSET_NAME(fpcr), lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fpu_fpcr}, nullptr, nullptr, nullptr, 0},
+
+ {"far", nullptr, 8, EXC_OFFSET_NAME(far), lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, exc_far}, nullptr, nullptr, nullptr, 0},
+ {"esr", nullptr, 4, EXC_OFFSET_NAME(esr), lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, exc_esr}, nullptr, nullptr, nullptr, 0},
+ {"exception", nullptr, 4, EXC_OFFSET_NAME(exception), lldb::eEncodingUint, lldb::eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, exc_exception}, nullptr, nullptr, nullptr, 0},
+
+ {DEFINE_DBG(bvr, 0)},
+ {DEFINE_DBG(bvr, 1)},
+ {DEFINE_DBG(bvr, 2)},
+ {DEFINE_DBG(bvr, 3)},
+ {DEFINE_DBG(bvr, 4)},
+ {DEFINE_DBG(bvr, 5)},
+ {DEFINE_DBG(bvr, 6)},
+ {DEFINE_DBG(bvr, 7)},
+ {DEFINE_DBG(bvr, 8)},
+ {DEFINE_DBG(bvr, 9)},
+ {DEFINE_DBG(bvr, 10)},
+ {DEFINE_DBG(bvr, 11)},
+ {DEFINE_DBG(bvr, 12)},
+ {DEFINE_DBG(bvr, 13)},
+ {DEFINE_DBG(bvr, 14)},
+ {DEFINE_DBG(bvr, 15)},
+
+ {DEFINE_DBG(bcr, 0)},
+ {DEFINE_DBG(bcr, 1)},
+ {DEFINE_DBG(bcr, 2)},
+ {DEFINE_DBG(bcr, 3)},
+ {DEFINE_DBG(bcr, 4)},
+ {DEFINE_DBG(bcr, 5)},
+ {DEFINE_DBG(bcr, 6)},
+ {DEFINE_DBG(bcr, 7)},
+ {DEFINE_DBG(bcr, 8)},
+ {DEFINE_DBG(bcr, 9)},
+ {DEFINE_DBG(bcr, 10)},
+ {DEFINE_DBG(bcr, 11)},
+ {DEFINE_DBG(bcr, 12)},
+ {DEFINE_DBG(bcr, 13)},
+ {DEFINE_DBG(bcr, 14)},
+ {DEFINE_DBG(bcr, 15)},
+
+ {DEFINE_DBG(wvr, 0)},
+ {DEFINE_DBG(wvr, 1)},
+ {DEFINE_DBG(wvr, 2)},
+ {DEFINE_DBG(wvr, 3)},
+ {DEFINE_DBG(wvr, 4)},
+ {DEFINE_DBG(wvr, 5)},
+ {DEFINE_DBG(wvr, 6)},
+ {DEFINE_DBG(wvr, 7)},
+ {DEFINE_DBG(wvr, 8)},
+ {DEFINE_DBG(wvr, 9)},
+ {DEFINE_DBG(wvr, 10)},
+ {DEFINE_DBG(wvr, 11)},
+ {DEFINE_DBG(wvr, 12)},
+ {DEFINE_DBG(wvr, 13)},
+ {DEFINE_DBG(wvr, 14)},
+ {DEFINE_DBG(wvr, 15)},
+
+ {DEFINE_DBG(wcr, 0)},
+ {DEFINE_DBG(wcr, 1)},
+ {DEFINE_DBG(wcr, 2)},
+ {DEFINE_DBG(wcr, 3)},
+ {DEFINE_DBG(wcr, 4)},
+ {DEFINE_DBG(wcr, 5)},
+ {DEFINE_DBG(wcr, 6)},
+ {DEFINE_DBG(wcr, 7)},
+ {DEFINE_DBG(wcr, 8)},
+ {DEFINE_DBG(wcr, 9)},
+ {DEFINE_DBG(wcr, 10)},
+ {DEFINE_DBG(wcr, 11)},
+ {DEFINE_DBG(wcr, 12)},
+ {DEFINE_DBG(wcr, 13)},
+ {DEFINE_DBG(wcr, 14)},
+ {DEFINE_DBG(wcr, 15)}
+ // clang-format on
+};
+
+#endif // DECLARE_REGISTER_INFOS_ARM64_STRUCT
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_i386.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_i386.h
new file mode 100644
index 000000000000..72ff904520ad
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_i386.h
@@ -0,0 +1,305 @@
+//===-- RegisterInfos_i386.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/Support/Compiler.h"
+#include <cstddef>
+#include <cstdint>
+
+
+#ifdef DECLARE_REGISTER_INFOS_I386_STRUCT
+
+// Computes the offset of the given GPR in the user data area.
+#define GPR_OFFSET(regname) (LLVM_EXTENSION offsetof(GPR, regname))
+
+// Computes the offset of the given FPR in the extended data area.
+#define FPR_OFFSET(regname) \
+ (LLVM_EXTENSION offsetof(UserArea, i387) + \
+ LLVM_EXTENSION offsetof(FPR_i386, regname))
+
+// Computes the offset of the YMM register assembled from register halves.
+// Based on DNBArchImplI386.cpp from debugserver
+#define YMM_OFFSET(reg_index) \
+ (LLVM_EXTENSION offsetof(UserArea, i387) + \
+ LLVM_EXTENSION offsetof(FPR, fxsave) + \
+ LLVM_EXTENSION offsetof(FXSAVE, xmm[7]) + sizeof(XMMReg) + \
+ (32 * reg_index))
+
+#define BNDR_OFFSET(reg_index) \
+ (LLVM_EXTENSION offsetof(UserArea, i387) + \
+ LLVM_EXTENSION offsetof(FPR, xsave) + \
+ LLVM_EXTENSION offsetof(XSAVE, mpxr[reg_index]))
+
+#define BNDC_OFFSET(reg_index) \
+ (LLVM_EXTENSION offsetof(UserArea, i387) + \
+ LLVM_EXTENSION offsetof(FPR, xsave) + \
+ LLVM_EXTENSION offsetof(XSAVE, mpxc[reg_index]))
+
+// Number of bytes needed to represent a FPR.
+#if !defined(FPR_SIZE)
+#define FPR_SIZE(reg) sizeof(((FXSAVE *)nullptr)->reg)
+#endif
+
+// Number of bytes needed to represent the i'th FP register.
+#define FP_SIZE sizeof(((MMSReg *)nullptr)->bytes)
+
+// Number of bytes needed to represent an XMM register.
+#define XMM_SIZE sizeof(XMMReg)
+
+// Number of bytes needed to represent a YMM register.
+#define YMM_SIZE sizeof(YMMReg)
+
+// Number of bytes needed to represent MPX registers.
+#define BNDR_SIZE sizeof(MPXReg)
+#define BNDC_SIZE sizeof(MPXCsr)
+
+// Note that the size and offset will be updated by platform-specific classes.
+#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \
+ { \
+ #reg, alt, sizeof(((GPR *)nullptr)->reg), \
+ GPR_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {kind1, kind2, kind3, kind4, \
+ lldb_##reg##_i386 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_FPR(name, reg, kind1, kind2, kind3, kind4) \
+ { \
+ #name, nullptr, FPR_SIZE(reg), FPR_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {kind1, kind2, kind3, kind4, \
+ lldb_##name##_i386 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+// RegisterKind: EHFrame, DWARF, Generic, Process Plugin, LLDB
+
+#define DEFINE_FP_ST(reg, i) \
+ { \
+ #reg #i, nullptr, FP_SIZE, \
+ LLVM_EXTENSION FPR_OFFSET( \
+ stmm[i]), eEncodingVector, eFormatVectorOfUInt8, \
+ {ehframe_st##i##_i386, dwarf_st##i##_i386, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, lldb_st##i##_i386 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_FP_MM(reg, i) \
+ { \
+ #reg #i, nullptr, sizeof(uint64_t), \
+ LLVM_EXTENSION FPR_OFFSET( \
+ stmm[i]), eEncodingUint, eFormatHex, \
+ {ehframe_mm##i##_i386, dwarf_mm##i##_i386, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_mm##i##_i386 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_XMM(reg, i) \
+ { \
+ #reg #i, nullptr, XMM_SIZE, \
+ LLVM_EXTENSION FPR_OFFSET( \
+ reg[i]), eEncodingVector, eFormatVectorOfUInt8, \
+ {ehframe_##reg##i##_i386, dwarf_##reg##i##_i386, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_##reg##i##_i386 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+// I believe the YMM registers use dwarf_xmm_%_i386 register numbers and then
+// differentiate based on register size.
+#define DEFINE_YMM(reg, i) \
+ { \
+ #reg #i, nullptr, YMM_SIZE, \
+ LLVM_EXTENSION YMM_OFFSET(i), eEncodingVector, eFormatVectorOfUInt8, \
+ {LLDB_INVALID_REGNUM, dwarf_xmm##i##_i386, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_##reg##i##_i386 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_BNDR(reg, i) \
+ { \
+ #reg #i, nullptr, BNDR_SIZE, \
+ LLVM_EXTENSION BNDR_OFFSET(i), eEncodingVector, eFormatVectorOfUInt64, \
+ {dwarf_##reg##i##_i386, dwarf_##reg##i##_i386, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, lldb_##reg##i##_i386 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_BNDC(name, i) \
+ { \
+ #name, nullptr, BNDC_SIZE, \
+ LLVM_EXTENSION BNDC_OFFSET(i), eEncodingVector, \
+ eFormatVectorOfUInt8, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, lldb_##name##_i386 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_DR(reg, i) \
+ { \
+ #reg #i, nullptr, DR_SIZE, \
+ DR_OFFSET(i), eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_GPR_PSEUDO_16(reg16, reg32) \
+ { \
+ #reg16, nullptr, 2, \
+ GPR_OFFSET(reg32), eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_##reg16##_i386 }, \
+ RegisterContextPOSIX_x86::g_contained_##reg32, \
+ RegisterContextPOSIX_x86::g_invalidate_##reg32, nullptr, 0 \
+ }
+
+#define DEFINE_GPR_PSEUDO_8H(reg8, reg32) \
+ { \
+ #reg8, nullptr, 1, \
+ GPR_OFFSET(reg32) + 1, eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_##reg8##_i386 }, \
+ RegisterContextPOSIX_x86::g_contained_##reg32, \
+ RegisterContextPOSIX_x86::g_invalidate_##reg32, nullptr, 0 \
+ }
+
+#define DEFINE_GPR_PSEUDO_8L(reg8, reg32) \
+ { \
+ #reg8, nullptr, 1, \
+ GPR_OFFSET(reg32), eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_##reg8##_i386 }, \
+ RegisterContextPOSIX_x86::g_contained_##reg32, \
+ RegisterContextPOSIX_x86::g_invalidate_##reg32, nullptr, 0 \
+ }
+
+static RegisterInfo g_register_infos_i386[] = {
+ // General purpose registers.
+ DEFINE_GPR(eax, nullptr, ehframe_eax_i386, dwarf_eax_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(ebx, nullptr, ehframe_ebx_i386, dwarf_ebx_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(ecx, nullptr, ehframe_ecx_i386, dwarf_ecx_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(edx, nullptr, ehframe_edx_i386, dwarf_edx_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(edi, nullptr, ehframe_edi_i386, dwarf_edi_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(esi, nullptr, ehframe_esi_i386, dwarf_esi_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(ebp, "fp", ehframe_ebp_i386, dwarf_ebp_i386,
+ LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(esp, "sp", ehframe_esp_i386, dwarf_esp_i386,
+ LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(eip, "pc", ehframe_eip_i386, dwarf_eip_i386,
+ LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(eflags, "flags", ehframe_eflags_i386, dwarf_eflags_i386,
+ LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(cs, nullptr, LLDB_INVALID_REGNUM, dwarf_cs_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(fs, nullptr, LLDB_INVALID_REGNUM, dwarf_fs_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(gs, nullptr, LLDB_INVALID_REGNUM, dwarf_gs_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(ss, nullptr, LLDB_INVALID_REGNUM, dwarf_ss_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(ds, nullptr, LLDB_INVALID_REGNUM, dwarf_ds_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(es, nullptr, LLDB_INVALID_REGNUM, dwarf_es_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+
+ 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),
+
+ // i387 Floating point registers.
+ DEFINE_FPR(fctrl, fctrl, LLDB_INVALID_REGNUM, dwarf_fctrl_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(fstat, fstat, LLDB_INVALID_REGNUM, dwarf_fstat_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(ftag, ftag, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(fop, fop, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(fiseg, ptr.i386_.fiseg, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(fioff, ptr.i386_.fioff, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(foseg, ptr.i386_.foseg, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(fooff, ptr.i386_.fooff, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(mxcsr, mxcsr, LLDB_INVALID_REGNUM, dwarf_mxcsr_i386,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(mxcsrmask, mxcsrmask, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+
+ // FP registers.
+ DEFINE_FP_ST(st, 0), DEFINE_FP_ST(st, 1), DEFINE_FP_ST(st, 2),
+ DEFINE_FP_ST(st, 3), DEFINE_FP_ST(st, 4), DEFINE_FP_ST(st, 5),
+ DEFINE_FP_ST(st, 6), DEFINE_FP_ST(st, 7), DEFINE_FP_MM(mm, 0),
+ DEFINE_FP_MM(mm, 1), DEFINE_FP_MM(mm, 2), DEFINE_FP_MM(mm, 3),
+ DEFINE_FP_MM(mm, 4), DEFINE_FP_MM(mm, 5), DEFINE_FP_MM(mm, 6),
+ DEFINE_FP_MM(mm, 7),
+
+ // XMM registers
+ DEFINE_XMM(xmm, 0), DEFINE_XMM(xmm, 1), DEFINE_XMM(xmm, 2),
+ DEFINE_XMM(xmm, 3), DEFINE_XMM(xmm, 4), DEFINE_XMM(xmm, 5),
+ DEFINE_XMM(xmm, 6), DEFINE_XMM(xmm, 7),
+
+ // Copy of YMM registers assembled from xmm and ymmh
+ DEFINE_YMM(ymm, 0), DEFINE_YMM(ymm, 1), DEFINE_YMM(ymm, 2),
+ DEFINE_YMM(ymm, 3), DEFINE_YMM(ymm, 4), DEFINE_YMM(ymm, 5),
+ DEFINE_YMM(ymm, 6), DEFINE_YMM(ymm, 7),
+
+ // MPX registers
+ DEFINE_BNDR(bnd, 0),
+ DEFINE_BNDR(bnd, 1),
+ DEFINE_BNDR(bnd, 2),
+ DEFINE_BNDR(bnd, 3),
+
+ DEFINE_BNDC(bndcfgu, 0),
+ DEFINE_BNDC(bndstatus, 1),
+
+ // Debug registers for lldb internal use
+ DEFINE_DR(dr, 0), DEFINE_DR(dr, 1), DEFINE_DR(dr, 2), DEFINE_DR(dr, 3),
+ DEFINE_DR(dr, 4), DEFINE_DR(dr, 5), DEFINE_DR(dr, 6), DEFINE_DR(dr, 7)};
+
+static_assert((sizeof(g_register_infos_i386) /
+ sizeof(g_register_infos_i386[0])) == k_num_registers_i386,
+ "g_register_infos_x86_64 has wrong number of register infos");
+
+#undef GPR_OFFSET
+#undef FPR_OFFSET
+#undef YMM_OFFSET
+#undef FPR_SIZE
+#undef FP_SIZE
+#undef XMM_SIZE
+#undef YMM_SIZE
+#undef DEFINE_GPR
+#undef DEFINE_FPR
+#undef DEFINE_FP
+#undef DEFINE_XMM
+#undef DEFINE_YMM
+#undef DEFINE_BNDR
+#undef DEFINE_BNDC
+#undef DEFINE_DR
+#undef DEFINE_GPR_PSEUDO_16
+#undef DEFINE_GPR_PSEUDO_8H
+#undef DEFINE_GPR_PSEUDO_8L
+
+#endif // DECLARE_REGISTER_INFOS_I386_STRUCT
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_mips.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_mips.h
new file mode 100644
index 000000000000..08201fd1191c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_mips.h
@@ -0,0 +1,305 @@
+//===-- RegisterInfos_mips.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 <stddef.h>
+
+#include "lldb/Core/dwarf.h"
+#include "llvm/Support/Compiler.h"
+
+
+#ifdef DECLARE_REGISTER_INFOS_MIPS_STRUCT
+
+// Computes the offset of the given GPR in the user data area.
+#define GPR_OFFSET(regname) \
+ (LLVM_EXTENSION offsetof(UserArea, gpr) + \
+ LLVM_EXTENSION offsetof(GPR_linux_mips, regname))
+
+// Computes the offset of the given FPR in the extended data area.
+#define FPR_OFFSET(regname) \
+ (LLVM_EXTENSION offsetof(UserArea, fpr) + \
+ LLVM_EXTENSION offsetof(FPR_linux_mips, regname))
+
+// Computes the offset of the given MSA in the extended data area.
+#define MSA_OFFSET(regname) \
+ (LLVM_EXTENSION offsetof(UserArea, msa) + \
+ LLVM_EXTENSION offsetof(MSA_linux_mips, regname))
+
+// Note that the size and offset will be updated by platform-specific classes.
+#define DEFINE_GPR(reg, alt, kind1, kind2, kind3) \
+ { \
+ #reg, alt, sizeof(((GPR_linux_mips *) NULL)->reg) / 2, \
+ GPR_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {kind1, kind2, kind3, ptrace_##reg##_mips, \
+ gpr_##reg##_mips }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+const uint8_t dwarf_opcode_mips[] = {
+ llvm::dwarf::DW_OP_regx, dwarf_sr_mips, llvm::dwarf::DW_OP_lit1,
+ llvm::dwarf::DW_OP_lit26, llvm::dwarf::DW_OP_shl, llvm::dwarf::DW_OP_and,
+ llvm::dwarf::DW_OP_lit26, llvm::dwarf::DW_OP_shr};
+
+#define DEFINE_FPR(reg, alt, kind1, kind2, kind3) \
+ { \
+ #reg, alt, sizeof(((FPR_linux_mips *) NULL)->reg), \
+ FPR_OFFSET(reg), eEncodingIEEE754, eFormatFloat, \
+ {kind1, kind2, kind3, ptrace_##reg##_mips, \
+ fpr_##reg##_mips }, \
+ NULL, NULL, dwarf_opcode_mips, \
+ sizeof(dwarf_opcode_mips) \
+ }
+
+#define DEFINE_FPR_INFO(reg, alt, kind1, kind2, kind3) \
+ { \
+ #reg, alt, sizeof(((FPR_linux_mips *) NULL)->reg), \
+ FPR_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {kind1, kind2, kind3, ptrace_##reg##_mips, \
+ fpr_##reg##_mips }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+#define DEFINE_MSA(reg, alt, kind1, kind2, kind3, kind4) \
+ { \
+ #reg, alt, sizeof(((MSA_linux_mips *) 0)->reg), \
+ MSA_OFFSET(reg), eEncodingVector, eFormatVectorOfUInt8, \
+ {kind1, kind2, kind3, kind4, \
+ msa_##reg##_mips }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+#define DEFINE_MSA_INFO(reg, alt, kind1, kind2, kind3, kind4) \
+ { \
+ #reg, alt, sizeof(((MSA_linux_mips *) 0)->reg), \
+ MSA_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {kind1, kind2, kind3, kind4, \
+ msa_##reg##_mips }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+// RegisterKind: EH_Frame, DWARF, Generic, Procss Plugin, LLDB
+
+static RegisterInfo g_register_infos_mips[] = {
+ DEFINE_GPR(zero, "zero", dwarf_zero_mips, dwarf_zero_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r1, "at", dwarf_r1_mips, dwarf_r1_mips, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r2, nullptr, dwarf_r2_mips, dwarf_r2_mips, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r3, nullptr, dwarf_r3_mips, dwarf_r3_mips, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r4, nullptr, dwarf_r4_mips, dwarf_r4_mips,
+ LLDB_REGNUM_GENERIC_ARG1),
+ DEFINE_GPR(r5, nullptr, dwarf_r5_mips, dwarf_r5_mips,
+ LLDB_REGNUM_GENERIC_ARG2),
+ DEFINE_GPR(r6, nullptr, dwarf_r6_mips, dwarf_r6_mips,
+ LLDB_REGNUM_GENERIC_ARG3),
+ DEFINE_GPR(r7, nullptr, dwarf_r7_mips, dwarf_r7_mips,
+ LLDB_REGNUM_GENERIC_ARG4),
+ DEFINE_GPR(r8, nullptr, dwarf_r8_mips, dwarf_r8_mips, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r9, nullptr, dwarf_r9_mips, dwarf_r9_mips, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r10, nullptr, dwarf_r10_mips, dwarf_r10_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r11, nullptr, dwarf_r11_mips, dwarf_r11_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r12, nullptr, dwarf_r12_mips, dwarf_r12_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r13, nullptr, dwarf_r13_mips, dwarf_r13_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r14, nullptr, dwarf_r14_mips, dwarf_r14_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r15, nullptr, dwarf_r15_mips, dwarf_r15_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r16, nullptr, dwarf_r16_mips, dwarf_r16_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r17, nullptr, dwarf_r17_mips, dwarf_r17_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r18, nullptr, dwarf_r18_mips, dwarf_r18_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r19, nullptr, dwarf_r19_mips, dwarf_r19_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r20, nullptr, dwarf_r20_mips, dwarf_r20_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r21, nullptr, dwarf_r21_mips, dwarf_r21_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r22, nullptr, dwarf_r22_mips, dwarf_r22_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r23, nullptr, dwarf_r23_mips, dwarf_r23_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r24, nullptr, dwarf_r24_mips, dwarf_r24_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r25, nullptr, dwarf_r25_mips, dwarf_r25_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r26, nullptr, dwarf_r26_mips, dwarf_r26_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r27, nullptr, dwarf_r27_mips, dwarf_r27_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(gp, "gp", dwarf_gp_mips, dwarf_gp_mips, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(sp, "sp", dwarf_sp_mips, dwarf_sp_mips, LLDB_REGNUM_GENERIC_SP),
+ DEFINE_GPR(r30, "fp", dwarf_r30_mips, dwarf_r30_mips,
+ LLDB_REGNUM_GENERIC_FP),
+ DEFINE_GPR(ra, "ra", dwarf_ra_mips, dwarf_ra_mips, LLDB_REGNUM_GENERIC_RA),
+ DEFINE_GPR(sr, "status", dwarf_sr_mips, dwarf_sr_mips,
+ LLDB_REGNUM_GENERIC_FLAGS),
+ DEFINE_GPR(mullo, nullptr, dwarf_lo_mips, dwarf_lo_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(mulhi, nullptr, dwarf_hi_mips, dwarf_hi_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(badvaddr, nullptr, dwarf_bad_mips, dwarf_bad_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(cause, nullptr, dwarf_cause_mips, dwarf_cause_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(pc, nullptr, dwarf_pc_mips, dwarf_pc_mips,
+ LLDB_REGNUM_GENERIC_PC),
+ DEFINE_GPR(config5, nullptr, dwarf_config5_mips, dwarf_config5_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f0, nullptr, dwarf_f0_mips, dwarf_f0_mips, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f1, nullptr, dwarf_f1_mips, dwarf_f1_mips, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f2, nullptr, dwarf_f2_mips, dwarf_f2_mips, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f3, nullptr, dwarf_f3_mips, dwarf_f3_mips, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f4, nullptr, dwarf_f4_mips, dwarf_f4_mips, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f5, nullptr, dwarf_f5_mips, dwarf_f5_mips, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f6, nullptr, dwarf_f6_mips, dwarf_f6_mips, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f7, nullptr, dwarf_f7_mips, dwarf_f7_mips, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f8, nullptr, dwarf_f8_mips, dwarf_f8_mips, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f9, nullptr, dwarf_f9_mips, dwarf_f9_mips, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f10, nullptr, dwarf_f10_mips, dwarf_f10_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f11, nullptr, dwarf_f11_mips, dwarf_f11_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f12, nullptr, dwarf_f12_mips, dwarf_f12_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f13, nullptr, dwarf_f13_mips, dwarf_f13_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f14, nullptr, dwarf_f14_mips, dwarf_f14_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f15, nullptr, dwarf_f15_mips, dwarf_f15_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f16, nullptr, dwarf_f16_mips, dwarf_f16_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f17, nullptr, dwarf_f17_mips, dwarf_f17_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f18, nullptr, dwarf_f18_mips, dwarf_f18_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f19, nullptr, dwarf_f19_mips, dwarf_f19_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f20, nullptr, dwarf_f20_mips, dwarf_f20_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f21, nullptr, dwarf_f21_mips, dwarf_f21_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f22, nullptr, dwarf_f22_mips, dwarf_f22_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f23, nullptr, dwarf_f23_mips, dwarf_f23_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f24, nullptr, dwarf_f24_mips, dwarf_f24_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f25, nullptr, dwarf_f25_mips, dwarf_f25_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f26, nullptr, dwarf_f26_mips, dwarf_f26_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f27, nullptr, dwarf_f27_mips, dwarf_f27_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f28, nullptr, dwarf_f28_mips, dwarf_f28_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f29, nullptr, dwarf_f29_mips, dwarf_f29_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f30, nullptr, dwarf_f30_mips, dwarf_f30_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f31, nullptr, dwarf_f31_mips, dwarf_f31_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR_INFO(fcsr, nullptr, dwarf_fcsr_mips, dwarf_fcsr_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR_INFO(fir, nullptr, dwarf_fir_mips, dwarf_fir_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR_INFO(config5, nullptr, dwarf_config5_mips, dwarf_config5_mips,
+ LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w0, nullptr, dwarf_w0_mips, dwarf_w0_mips, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w1, nullptr, dwarf_w1_mips, dwarf_w1_mips, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w2, nullptr, dwarf_w2_mips, dwarf_w2_mips, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w3, nullptr, dwarf_w3_mips, dwarf_w3_mips, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w4, nullptr, dwarf_w4_mips, dwarf_w4_mips, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w5, nullptr, dwarf_w5_mips, dwarf_w5_mips, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w6, nullptr, dwarf_w6_mips, dwarf_w6_mips, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w7, nullptr, dwarf_w7_mips, dwarf_w7_mips, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w8, nullptr, dwarf_w8_mips, dwarf_w8_mips, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w9, nullptr, dwarf_w9_mips, dwarf_w9_mips, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w10, nullptr, dwarf_w10_mips, dwarf_w10_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w11, nullptr, dwarf_w11_mips, dwarf_w11_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w12, nullptr, dwarf_w12_mips, dwarf_w12_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w13, nullptr, dwarf_w13_mips, dwarf_w13_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w14, nullptr, dwarf_w14_mips, dwarf_w14_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w15, nullptr, dwarf_w15_mips, dwarf_w15_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w16, nullptr, dwarf_w16_mips, dwarf_w16_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w17, nullptr, dwarf_w17_mips, dwarf_w17_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w18, nullptr, dwarf_w18_mips, dwarf_w18_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w19, nullptr, dwarf_w19_mips, dwarf_w19_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w20, nullptr, dwarf_w10_mips, dwarf_w20_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w21, nullptr, dwarf_w21_mips, dwarf_w21_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w22, nullptr, dwarf_w22_mips, dwarf_w22_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w23, nullptr, dwarf_w23_mips, dwarf_w23_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w24, nullptr, dwarf_w24_mips, dwarf_w24_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w25, nullptr, dwarf_w25_mips, dwarf_w25_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w26, nullptr, dwarf_w26_mips, dwarf_w26_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w27, nullptr, dwarf_w27_mips, dwarf_w27_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w28, nullptr, dwarf_w28_mips, dwarf_w28_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w29, nullptr, dwarf_w29_mips, dwarf_w29_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w30, nullptr, dwarf_w30_mips, dwarf_w30_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w31, nullptr, dwarf_w31_mips, dwarf_w31_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA_INFO(mcsr, nullptr, dwarf_mcsr_mips, dwarf_mcsr_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA_INFO(mir, nullptr, dwarf_mir_mips, dwarf_mir_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA_INFO(fcsr, nullptr, dwarf_fcsr_mips, dwarf_fcsr_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA_INFO(fir, nullptr, dwarf_fir_mips, dwarf_fir_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA_INFO(config5, nullptr, dwarf_config5_mips, dwarf_config5_mips,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM)};
+
+static_assert((sizeof(g_register_infos_mips) /
+ sizeof(g_register_infos_mips[0])) == k_num_registers_mips,
+ "g_register_infos_mips has wrong number of register infos");
+
+#undef GPR_OFFSET
+#undef FPR_OFFSET
+#undef MSA_OFFSET
+#undef DEFINE_GPR
+#undef DEFINE_FPR
+#undef DEFINE_FPR_INFO
+#undef DEFINE_MSA
+#undef DEFINE_MSA_INFO
+
+#endif // DECLARE_REGISTER_INFOS_MIPS_STRUCT
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_mips64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_mips64.h
new file mode 100644
index 000000000000..b6e218ea4414
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_mips64.h
@@ -0,0 +1,435 @@
+//===-- RegisterInfos_mips64.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 <stddef.h>
+
+#include "lldb/Core/dwarf.h"
+#include "llvm/Support/Compiler.h"
+
+
+#ifdef DECLARE_REGISTER_INFOS_MIPS64_STRUCT
+
+// Computes the offset of the given GPR in the user data area.
+#ifdef LINUX_MIPS64
+#define GPR_OFFSET(regname) \
+ (LLVM_EXTENSION offsetof(UserArea, gpr) + \
+ LLVM_EXTENSION offsetof(GPR_linux_mips, regname))
+#else
+#define GPR_OFFSET(regname) (LLVM_EXTENSION offsetof(GPR_freebsd_mips, regname))
+#endif
+
+// Computes the offset of the given FPR in the extended data area.
+#define FPR_OFFSET(regname) \
+ (LLVM_EXTENSION offsetof(UserArea, fpr) + \
+ LLVM_EXTENSION offsetof(FPR_linux_mips, regname))
+
+// Computes the offset of the given MSA in the extended data area.
+#define MSA_OFFSET(regname) \
+ (LLVM_EXTENSION offsetof(UserArea, msa) + \
+ LLVM_EXTENSION offsetof(MSA_linux_mips, regname))
+
+// RegisterKind: EHFrame, DWARF, Generic, Process Plugin, LLDB
+
+// Note that the size and offset will be updated by platform-specific classes.
+#ifdef LINUX_MIPS64
+#define DEFINE_GPR(reg, alt, kind1, kind2, kind3) \
+ { \
+ #reg, alt, sizeof(((GPR_linux_mips *) 0)->reg), \
+ GPR_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {kind1, kind2, kind3, ptrace_##reg##_mips, \
+ gpr_##reg##_mips64 }, \
+ NULL, NULL, NULL, 0 \
+ }
+#else
+#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \
+ { \
+ #reg, alt, sizeof(((GPR_freebsd_mips *) 0)->reg), \
+ GPR_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {kind1, kind2, kind3, kind4, \
+ gpr_##reg##_mips64 }, \
+ NULL, NULL, NULL, 0 \
+ }
+#endif
+
+#define DEFINE_GPR_INFO(reg, alt, kind1, kind2, kind3) \
+ { \
+ #reg, alt, sizeof(((GPR_linux_mips *) 0)->reg) / 2, \
+ GPR_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {kind1, kind2, kind3, ptrace_##reg##_mips, \
+ gpr_##reg##_mips64 }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+const uint8_t dwarf_opcode_mips64[] = {
+ llvm::dwarf::DW_OP_regx, dwarf_sr_mips64, llvm::dwarf::DW_OP_lit1,
+ llvm::dwarf::DW_OP_lit26, llvm::dwarf::DW_OP_shl, llvm::dwarf::DW_OP_and,
+ llvm::dwarf::DW_OP_lit26, llvm::dwarf::DW_OP_shr};
+
+#define DEFINE_FPR(reg, alt, kind1, kind2, kind3) \
+ { \
+ #reg, alt, sizeof(((FPR_linux_mips *) 0)->reg), \
+ FPR_OFFSET(reg), eEncodingIEEE754, eFormatFloat, \
+ {kind1, kind2, kind3, ptrace_##reg##_mips, \
+ fpr_##reg##_mips64 }, \
+ NULL, NULL, dwarf_opcode_mips64, \
+ sizeof(dwarf_opcode_mips64) \
+ }
+
+#define DEFINE_FPR_INFO(reg, alt, kind1, kind2, kind3) \
+ { \
+ #reg, alt, sizeof(((FPR_linux_mips *) 0)->reg), \
+ FPR_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {kind1, kind2, kind3, ptrace_##reg##_mips, \
+ fpr_##reg##_mips64 }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+#define DEFINE_MSA(reg, alt, kind1, kind2, kind3, kind4) \
+ { \
+ #reg, alt, sizeof(((MSA_linux_mips *) 0)->reg), \
+ MSA_OFFSET(reg), eEncodingVector, eFormatVectorOfUInt8, \
+ {kind1, kind2, kind3, kind4, \
+ msa_##reg##_mips64 }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+#define DEFINE_MSA_INFO(reg, alt, kind1, kind2, kind3, kind4) \
+ { \
+ #reg, alt, sizeof(((MSA_linux_mips *) 0)->reg), \
+ MSA_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {kind1, kind2, kind3, kind4, \
+ msa_##reg##_mips64 }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+static RegisterInfo g_register_infos_mips64[] = {
+// General purpose registers. EH_Frame, DWARF,
+// Generic, Process Plugin
+#ifndef LINUX_MIPS64
+ DEFINE_GPR(zero, "r0", dwarf_zero_mips64, dwarf_zero_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r1, nullptr, dwarf_r1_mips64, dwarf_r1_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r2, nullptr, dwarf_r2_mips64, dwarf_r2_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r3, nullptr, dwarf_r3_mips64, dwarf_r3_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r4, nullptr, dwarf_r4_mips64, dwarf_r4_mips64,
+ LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r5, nullptr, dwarf_r5_mips64, dwarf_r5_mips64,
+ LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r6, nullptr, dwarf_r6_mips64, dwarf_r6_mips64,
+ LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r7, nullptr, dwarf_r7_mips64, dwarf_r7_mips64,
+ LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r8, nullptr, dwarf_r8_mips64, dwarf_r8_mips64,
+ LLDB_REGNUM_GENERIC_ARG5, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r9, nullptr, dwarf_r9_mips64, dwarf_r9_mips64,
+ LLDB_REGNUM_GENERIC_ARG6, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r10, nullptr, dwarf_r10_mips64, dwarf_r10_mips64,
+ LLDB_REGNUM_GENERIC_ARG7, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r11, nullptr, dwarf_r11_mips64, dwarf_r11_mips64,
+ LLDB_REGNUM_GENERIC_ARG8, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r12, nullptr, dwarf_r12_mips64, dwarf_r12_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r13, nullptr, dwarf_r13_mips64, dwarf_r13_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r14, nullptr, dwarf_r14_mips64, dwarf_r14_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r15, nullptr, dwarf_r15_mips64, dwarf_r15_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r16, nullptr, dwarf_r16_mips64, dwarf_r16_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r17, nullptr, dwarf_r17_mips64, dwarf_r17_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r18, nullptr, dwarf_r18_mips64, dwarf_r18_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r19, nullptr, dwarf_r19_mips64, dwarf_r19_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r20, nullptr, dwarf_r20_mips64, dwarf_r20_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r21, nullptr, dwarf_r21_mips64, dwarf_r21_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r22, nullptr, dwarf_r22_mips64, dwarf_r22_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r23, nullptr, dwarf_r23_mips64, dwarf_r23_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r24, nullptr, dwarf_r24_mips64, dwarf_r24_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r25, nullptr, dwarf_r25_mips64, dwarf_r25_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r26, nullptr, dwarf_r26_mips64, dwarf_r26_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r27, nullptr, dwarf_r27_mips64, dwarf_r27_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(gp, "r28", dwarf_gp_mips64, dwarf_gp_mips64, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(sp, "r29", dwarf_sp_mips64, dwarf_sp_mips64,
+ LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r30, nullptr, dwarf_r30_mips64, dwarf_r30_mips64,
+ LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(ra, "r31", dwarf_ra_mips64, dwarf_ra_mips64,
+ LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(sr, nullptr, dwarf_sr_mips64, dwarf_sr_mips64,
+ LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(mullo, nullptr, dwarf_lo_mips64, dwarf_lo_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(mulhi, nullptr, dwarf_hi_mips64, dwarf_hi_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(badvaddr, nullptr, dwarf_bad_mips64, dwarf_bad_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(cause, nullptr, dwarf_cause_mips64, dwarf_cause_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(pc, "pc", dwarf_pc_mips64, dwarf_pc_mips64,
+ LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(ic, nullptr, dwarf_ic_mips64, dwarf_ic_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(dummy, nullptr, dwarf_dummy_mips64, dwarf_dummy_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+#else
+ DEFINE_GPR(zero, "r0", dwarf_zero_mips64, dwarf_zero_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r1, nullptr, dwarf_r1_mips64, dwarf_r1_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r2, nullptr, dwarf_r2_mips64, dwarf_r2_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r3, nullptr, dwarf_r3_mips64, dwarf_r3_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r4, nullptr, dwarf_r4_mips64, dwarf_r4_mips64,
+ LLDB_REGNUM_GENERIC_ARG1),
+ DEFINE_GPR(r5, nullptr, dwarf_r5_mips64, dwarf_r5_mips64,
+ LLDB_REGNUM_GENERIC_ARG2),
+ DEFINE_GPR(r6, nullptr, dwarf_r6_mips64, dwarf_r6_mips64,
+ LLDB_REGNUM_GENERIC_ARG3),
+ DEFINE_GPR(r7, nullptr, dwarf_r7_mips64, dwarf_r7_mips64,
+ LLDB_REGNUM_GENERIC_ARG4),
+ DEFINE_GPR(r8, nullptr, dwarf_r8_mips64, dwarf_r8_mips64,
+ LLDB_REGNUM_GENERIC_ARG5),
+ DEFINE_GPR(r9, nullptr, dwarf_r9_mips64, dwarf_r9_mips64,
+ LLDB_REGNUM_GENERIC_ARG6),
+ DEFINE_GPR(r10, nullptr, dwarf_r10_mips64, dwarf_r10_mips64,
+ LLDB_REGNUM_GENERIC_ARG7),
+ DEFINE_GPR(r11, nullptr, dwarf_r11_mips64, dwarf_r11_mips64,
+ LLDB_REGNUM_GENERIC_ARG8),
+ DEFINE_GPR(r12, nullptr, dwarf_r12_mips64, dwarf_r12_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r13, nullptr, dwarf_r13_mips64, dwarf_r13_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r14, nullptr, dwarf_r14_mips64, dwarf_r14_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r15, nullptr, dwarf_r15_mips64, dwarf_r15_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r16, nullptr, dwarf_r16_mips64, dwarf_r16_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r17, nullptr, dwarf_r17_mips64, dwarf_r17_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r18, nullptr, dwarf_r18_mips64, dwarf_r18_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r19, nullptr, dwarf_r19_mips64, dwarf_r19_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r20, nullptr, dwarf_r20_mips64, dwarf_r20_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r21, nullptr, dwarf_r21_mips64, dwarf_r21_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r22, nullptr, dwarf_r22_mips64, dwarf_r22_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r23, nullptr, dwarf_r23_mips64, dwarf_r23_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r24, nullptr, dwarf_r24_mips64, dwarf_r24_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r25, nullptr, dwarf_r25_mips64, dwarf_r25_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r26, nullptr, dwarf_r26_mips64, dwarf_r26_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r27, nullptr, dwarf_r27_mips64, dwarf_r27_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(gp, "r28", dwarf_gp_mips64, dwarf_gp_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(sp, "r29", dwarf_sp_mips64, dwarf_sp_mips64,
+ LLDB_REGNUM_GENERIC_SP),
+ DEFINE_GPR(r30, nullptr, dwarf_r30_mips64, dwarf_r30_mips64,
+ LLDB_REGNUM_GENERIC_FP),
+ DEFINE_GPR(ra, "r31", dwarf_ra_mips64, dwarf_ra_mips64,
+ LLDB_REGNUM_GENERIC_RA),
+ DEFINE_GPR_INFO(sr, nullptr, dwarf_sr_mips64, dwarf_sr_mips64,
+ LLDB_REGNUM_GENERIC_FLAGS),
+ DEFINE_GPR(mullo, nullptr, dwarf_lo_mips64, dwarf_lo_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(mulhi, nullptr, dwarf_hi_mips64, dwarf_hi_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(badvaddr, nullptr, dwarf_bad_mips64, dwarf_bad_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR_INFO(cause, nullptr, dwarf_cause_mips64, dwarf_cause_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR(pc, "pc", dwarf_pc_mips64, dwarf_pc_mips64,
+ LLDB_REGNUM_GENERIC_PC),
+ DEFINE_GPR_INFO(config5, nullptr, dwarf_config5_mips64,
+ dwarf_config5_mips64, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f0, nullptr, dwarf_f0_mips64, dwarf_f0_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f1, nullptr, dwarf_f1_mips64, dwarf_f1_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f2, nullptr, dwarf_f2_mips64, dwarf_f2_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f3, nullptr, dwarf_f3_mips64, dwarf_f3_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f4, nullptr, dwarf_f4_mips64, dwarf_f4_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f5, nullptr, dwarf_f5_mips64, dwarf_f5_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f6, nullptr, dwarf_f6_mips64, dwarf_f6_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f7, nullptr, dwarf_f7_mips64, dwarf_f7_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f8, nullptr, dwarf_f8_mips64, dwarf_f8_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f9, nullptr, dwarf_f9_mips64, dwarf_f9_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f10, nullptr, dwarf_f10_mips64, dwarf_f10_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f11, nullptr, dwarf_f11_mips64, dwarf_f11_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f12, nullptr, dwarf_f12_mips64, dwarf_f12_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f13, nullptr, dwarf_f13_mips64, dwarf_f13_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f14, nullptr, dwarf_f14_mips64, dwarf_f14_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f15, nullptr, dwarf_f15_mips64, dwarf_f15_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f16, nullptr, dwarf_f16_mips64, dwarf_f16_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f17, nullptr, dwarf_f17_mips64, dwarf_f17_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f18, nullptr, dwarf_f18_mips64, dwarf_f18_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f19, nullptr, dwarf_f19_mips64, dwarf_f19_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f20, nullptr, dwarf_f20_mips64, dwarf_f20_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f21, nullptr, dwarf_f21_mips64, dwarf_f21_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f22, nullptr, dwarf_f22_mips64, dwarf_f22_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f23, nullptr, dwarf_f23_mips64, dwarf_f23_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f24, nullptr, dwarf_f24_mips64, dwarf_f24_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f25, nullptr, dwarf_f25_mips64, dwarf_f25_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f26, nullptr, dwarf_f26_mips64, dwarf_f26_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f27, nullptr, dwarf_f27_mips64, dwarf_f27_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f28, nullptr, dwarf_f28_mips64, dwarf_f28_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f29, nullptr, dwarf_f29_mips64, dwarf_f29_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f30, nullptr, dwarf_f30_mips64, dwarf_f30_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR(f31, nullptr, dwarf_f31_mips64, dwarf_f31_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR_INFO(fcsr, nullptr, dwarf_fcsr_mips64, dwarf_fcsr_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR_INFO(fir, nullptr, dwarf_fir_mips64, dwarf_fir_mips64,
+ LLDB_INVALID_REGNUM),
+ DEFINE_FPR_INFO(config5, nullptr, dwarf_config5_mips64,
+ dwarf_config5_mips64, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w0, nullptr, dwarf_w0_mips64, dwarf_w0_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w1, nullptr, dwarf_w1_mips64, dwarf_w1_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w2, nullptr, dwarf_w2_mips64, dwarf_w2_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w3, nullptr, dwarf_w3_mips64, dwarf_w3_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w4, nullptr, dwarf_w4_mips64, dwarf_w4_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w5, nullptr, dwarf_w5_mips64, dwarf_w5_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w6, nullptr, dwarf_w6_mips64, dwarf_w6_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w7, nullptr, dwarf_w7_mips64, dwarf_w7_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w8, nullptr, dwarf_w8_mips64, dwarf_w8_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w9, nullptr, dwarf_w9_mips64, dwarf_w9_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w10, nullptr, dwarf_w10_mips64, dwarf_w10_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w11, nullptr, dwarf_w11_mips64, dwarf_w11_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w12, nullptr, dwarf_w12_mips64, dwarf_w12_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w13, nullptr, dwarf_w13_mips64, dwarf_w13_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w14, nullptr, dwarf_w14_mips64, dwarf_w14_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w15, nullptr, dwarf_w15_mips64, dwarf_w15_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w16, nullptr, dwarf_w16_mips64, dwarf_w16_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w17, nullptr, dwarf_w17_mips64, dwarf_w17_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w18, nullptr, dwarf_w18_mips64, dwarf_w18_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w19, nullptr, dwarf_w19_mips64, dwarf_w19_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w20, nullptr, dwarf_w10_mips64, dwarf_w20_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w21, nullptr, dwarf_w21_mips64, dwarf_w21_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w22, nullptr, dwarf_w22_mips64, dwarf_w22_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w23, nullptr, dwarf_w23_mips64, dwarf_w23_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w24, nullptr, dwarf_w24_mips64, dwarf_w24_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w25, nullptr, dwarf_w25_mips64, dwarf_w25_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w26, nullptr, dwarf_w26_mips64, dwarf_w26_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w27, nullptr, dwarf_w27_mips64, dwarf_w27_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w28, nullptr, dwarf_w28_mips64, dwarf_w28_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w29, nullptr, dwarf_w29_mips64, dwarf_w29_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w30, nullptr, dwarf_w30_mips64, dwarf_w30_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA(w31, nullptr, dwarf_w31_mips64, dwarf_w31_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA_INFO(mcsr, nullptr, dwarf_mcsr_mips64, dwarf_mcsr_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA_INFO(mir, nullptr, dwarf_mir_mips64, dwarf_mir_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA_INFO(fcsr, nullptr, dwarf_fcsr_mips64, dwarf_fcsr_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA_INFO(fir, nullptr, dwarf_fir_mips64, dwarf_fir_mips64,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_MSA_INFO(config5, nullptr, dwarf_config5_mips64,
+ dwarf_config5_mips64, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM)
+#endif
+};
+
+static_assert((sizeof(g_register_infos_mips64) /
+ sizeof(g_register_infos_mips64[0])) == k_num_registers_mips64,
+ "g_register_infos_mips64 has wrong number of register infos");
+
+#undef DEFINE_GPR
+#undef DEFINE_GPR_INFO
+#undef DEFINE_FPR
+#undef DEFINE_FPR_INFO
+#undef DEFINE_MSA
+#undef DEFINE_MSA_INFO
+#undef GPR_OFFSET
+#undef FPR_OFFSET
+#undef MSA_OFFSET
+
+#endif // DECLARE_REGISTER_INFOS_MIPS64_STRUCT
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_powerpc.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_powerpc.h
new file mode 100644
index 000000000000..51be31f8e028
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_powerpc.h
@@ -0,0 +1,228 @@
+//===-- RegisterInfos_powerpc.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 <stddef.h>
+
+// Computes the offset of the given GPR in the user data area.
+#define GPR_OFFSET(regname) (offsetof(GPR, regname))
+#define FPR_OFFSET(regname) (offsetof(FPR, regname))
+#define VMX_OFFSET(regname) (offsetof(VMX, regname))
+#define GPR_SIZE(regname) (sizeof(((GPR *)NULL)->regname))
+
+#ifdef DECLARE_REGISTER_INFOS_POWERPC_STRUCT
+
+// Note that the size and offset will be updated by platform-specific classes.
+#define DEFINE_GPR(reg, alt, lldb_kind) \
+ { \
+ #reg, alt, GPR_SIZE(reg), GPR_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {dwarf_##reg##_powerpc, \
+ dwarf_##reg##_powerpc, lldb_kind, \
+ LLDB_INVALID_REGNUM, \
+ gpr_##reg##_powerpc }, \
+ NULL, NULL, NULL, 0 \
+ }
+#define DEFINE_FPR(reg, lldb_kind) \
+ { \
+ #reg, NULL, 8, FPR_OFFSET(reg), eEncodingIEEE754, eFormatFloat, \
+ {dwarf_##reg##_powerpc, dwarf_##reg##_powerpc, \
+ lldb_kind, LLDB_INVALID_REGNUM, \
+ fpr_##reg##_powerpc }, \
+ NULL, NULL, NULL, 0 \
+ }
+#define DEFINE_VMX(reg, lldb_kind) \
+ { \
+ #reg, NULL, 16, VMX_OFFSET(reg), eEncodingVector, eFormatVectorOfUInt32, \
+ {dwarf_##reg##_powerpc, dwarf_##reg##_powerpc, \
+ lldb_kind, LLDB_INVALID_REGNUM, \
+ vmx_##reg##_powerpc }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+// General purpose registers. EH_Frame, DWARF,
+// Generic, Process Plugin
+#define POWERPC_REGS \
+ DEFINE_GPR(r0, NULL, LLDB_INVALID_REGNUM) \
+ , DEFINE_GPR(r1, "sp", LLDB_REGNUM_GENERIC_SP), \
+ DEFINE_GPR(r2, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r3, "arg1", LLDB_REGNUM_GENERIC_ARG1), \
+ DEFINE_GPR(r4, "arg2", LLDB_REGNUM_GENERIC_ARG2), \
+ DEFINE_GPR(r5, "arg3", LLDB_REGNUM_GENERIC_ARG3), \
+ DEFINE_GPR(r6, "arg4", LLDB_REGNUM_GENERIC_ARG4), \
+ DEFINE_GPR(r7, "arg5", LLDB_REGNUM_GENERIC_ARG5), \
+ DEFINE_GPR(r8, "arg6", LLDB_REGNUM_GENERIC_ARG6), \
+ DEFINE_GPR(r9, "arg7", LLDB_REGNUM_GENERIC_ARG7), \
+ DEFINE_GPR(r10, "arg8", LLDB_REGNUM_GENERIC_ARG8), \
+ DEFINE_GPR(r11, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r12, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r13, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r14, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r15, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r16, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r17, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r18, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r19, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r20, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r21, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r22, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r23, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r24, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r25, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r26, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r27, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r28, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r29, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r30, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r31, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(lr, "lr", LLDB_REGNUM_GENERIC_RA), \
+ DEFINE_GPR(cr, "cr", LLDB_REGNUM_GENERIC_FLAGS), \
+ DEFINE_GPR(xer, "xer", LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(ctr, "ctr", LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(pc, "pc", LLDB_REGNUM_GENERIC_PC), \
+ DEFINE_FPR(f0, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f1, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f2, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f3, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f4, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f5, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f6, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f7, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f8, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f9, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f10, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f11, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f12, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f13, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f14, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f15, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f16, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f17, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f18, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f19, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f20, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f21, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f22, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f23, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f24, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f25, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f26, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f27, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f28, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f29, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f30, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f31, LLDB_INVALID_REGNUM), \
+ {"fpscr", \
+ NULL, \
+ 8, \
+ FPR_OFFSET(fpscr), \
+ eEncodingUint, \
+ eFormatHex, \
+ {dwarf_fpscr_powerpc, dwarf_fpscr_powerpc, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, fpr_fpscr_powerpc}, \
+ NULL, \
+ NULL, \
+ NULL, \
+ 0}, \
+ DEFINE_VMX(v0, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v1, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v2, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v3, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v4, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v5, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v6, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v7, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v8, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v9, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v10, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v11, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v12, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v13, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v14, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v15, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v16, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v17, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v18, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v19, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v20, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v21, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v22, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v23, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v24, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v25, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v26, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v27, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v28, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v29, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v30, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(v31, LLDB_INVALID_REGNUM), \
+ {"vrsave", \
+ NULL, \
+ 4, \
+ VMX_OFFSET(vrsave), \
+ eEncodingUint, \
+ eFormatHex, \
+ {dwarf_vrsave_powerpc, dwarf_vrsave_powerpc, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, vmx_vrsave_powerpc}, \
+ NULL, \
+ NULL, \
+ NULL, \
+ 0}, \
+ {"vscr", \
+ NULL, \
+ 4, \
+ VMX_OFFSET(vscr), \
+ eEncodingUint, \
+ eFormatHex, \
+ {dwarf_vscr_powerpc, dwarf_vscr_powerpc, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, vmx_vscr_powerpc}, \
+ NULL, \
+ NULL, \
+ NULL, \
+ 0},
+
+static RegisterInfo g_register_infos_powerpc64[] = {
+#define GPR GPR64
+ POWERPC_REGS
+#undef GPR
+};
+
+static RegisterInfo g_register_infos_powerpc32[] = {
+#define GPR GPR32
+ POWERPC_REGS
+#undef GPR
+};
+
+static RegisterInfo g_register_infos_powerpc64_32[] = {
+#define GPR GPR64
+#undef GPR_SIZE
+#define GPR_SIZE(reg) (sizeof(uint32_t))
+#undef GPR_OFFSET
+#define GPR_OFFSET(regname) \
+ (offsetof(GPR, regname) + (sizeof(((GPR *)NULL)->regname) - GPR_SIZE(reg)))
+ POWERPC_REGS
+#undef GPR
+};
+
+static_assert((sizeof(g_register_infos_powerpc32) /
+ sizeof(g_register_infos_powerpc32[0])) ==
+ k_num_registers_powerpc,
+ "g_register_infos_powerpc32 has wrong number of register infos");
+static_assert((sizeof(g_register_infos_powerpc64) /
+ sizeof(g_register_infos_powerpc64[0])) ==
+ k_num_registers_powerpc,
+ "g_register_infos_powerpc64 has wrong number of register infos");
+static_assert(sizeof(g_register_infos_powerpc64_32) ==
+ sizeof(g_register_infos_powerpc64),
+ "g_register_infos_powerpc64_32 doesn't match size of "
+ "g_register_infos_powerpc64");
+
+#undef DEFINE_FPR
+#undef DEFINE_GPR
+
+#endif // DECLARE_REGISTER_INFOS_POWERPC_STRUCT
+
+#undef GPR_OFFSET
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_ppc64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_ppc64.h
new file mode 100644
index 000000000000..1086d3db0b06
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_ppc64.h
@@ -0,0 +1,329 @@
+//===-- RegisterInfos_ppc64.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifdef DECLARE_REGISTER_INFOS_PPC64_STRUCT
+
+#include <stddef.h>
+
+// Computes the offset of the given GPR_PPC64 in the user data area.
+#define GPR_PPC64_OFFSET(regname) (offsetof(GPR_PPC64, regname))
+#define FPR_PPC64_OFFSET(regname) (offsetof(FPR_PPC64, regname) \
+ + sizeof(GPR_PPC64))
+#define VMX_PPC64_OFFSET(regname) (offsetof(VMX_PPC64, regname) \
+ + sizeof(GPR_PPC64) + sizeof(FPR_PPC64))
+#define GPR_PPC64_SIZE(regname) (sizeof(((GPR_PPC64 *)NULL)->regname))
+
+#include "Utility/PPC64_DWARF_Registers.h"
+#include "lldb-ppc64-register-enums.h"
+
+// Note that the size and offset will be updated by platform-specific classes.
+#define DEFINE_GPR_PPC64(reg, alt, lldb_kind) \
+ { \
+ #reg, alt, GPR_PPC64_SIZE(reg), GPR_PPC64_OFFSET(reg), lldb::eEncodingUint,\
+ lldb::eFormatHex, \
+ {ppc64_dwarf::dwarf_##reg##_ppc64, \
+ ppc64_dwarf::dwarf_##reg##_ppc64, \
+ lldb_kind, \
+ LLDB_INVALID_REGNUM, \
+ gpr_##reg##_ppc64 }, \
+ NULL, NULL, NULL, 0 \
+ }
+#define DEFINE_FPR_PPC64(reg, alt, lldb_kind) \
+ { \
+#reg, alt, 8, FPR_PPC64_OFFSET(reg), lldb::eEncodingIEEE754, \
+ lldb::eFormatFloat, \
+ {ppc64_dwarf::dwarf_##reg##_ppc64, \
+ ppc64_dwarf::dwarf_##reg##_ppc64, lldb_kind, LLDB_INVALID_REGNUM, \
+ fpr_##reg##_ppc64 }, \
+ NULL, NULL, NULL, 0 \
+ }
+#define DEFINE_VMX_PPC64(reg, lldb_kind) \
+ { \
+#reg, NULL, 16, VMX_PPC64_OFFSET(reg), lldb::eEncodingVector, \
+ lldb::eFormatVectorOfUInt32, \
+ {ppc64_dwarf::dwarf_##reg##_ppc64, \
+ ppc64_dwarf::dwarf_##reg##_ppc64, lldb_kind, LLDB_INVALID_REGNUM, \
+ vmx_##reg##_ppc64 }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+// General purpose registers.
+// EH_Frame, Generic, Process Plugin
+#define PPC64_REGS \
+ DEFINE_GPR_PPC64(r0, NULL, LLDB_INVALID_REGNUM) \
+ , DEFINE_GPR_PPC64(r1, "sp", LLDB_REGNUM_GENERIC_SP), \
+ DEFINE_GPR_PPC64(r2, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r3, "arg1", LLDB_REGNUM_GENERIC_ARG1), \
+ DEFINE_GPR_PPC64(r4, "arg2", LLDB_REGNUM_GENERIC_ARG2), \
+ DEFINE_GPR_PPC64(r5, "arg3", LLDB_REGNUM_GENERIC_ARG3), \
+ DEFINE_GPR_PPC64(r6, "arg4", LLDB_REGNUM_GENERIC_ARG4), \
+ DEFINE_GPR_PPC64(r7, "arg5", LLDB_REGNUM_GENERIC_ARG5), \
+ DEFINE_GPR_PPC64(r8, "arg6", LLDB_REGNUM_GENERIC_ARG6), \
+ DEFINE_GPR_PPC64(r9, "arg7", LLDB_REGNUM_GENERIC_ARG7), \
+ DEFINE_GPR_PPC64(r10, "arg8", LLDB_REGNUM_GENERIC_ARG8), \
+ DEFINE_GPR_PPC64(r11, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r12, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r13, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r14, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r15, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r16, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r17, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r18, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r19, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r20, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r21, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r22, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r23, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r24, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r25, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r26, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r27, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r28, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r29, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r30, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(r31, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(cr, "cr", LLDB_REGNUM_GENERIC_FLAGS), \
+ DEFINE_GPR_PPC64(msr, "msr", LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(xer, "xer", LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(lr, "lr", LLDB_REGNUM_GENERIC_RA), \
+ DEFINE_GPR_PPC64(ctr, "ctr", LLDB_INVALID_REGNUM), \
+ DEFINE_GPR_PPC64(pc, "pc", LLDB_REGNUM_GENERIC_PC), \
+ DEFINE_FPR_PPC64(f0, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f1, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f2, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f3, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f4, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f5, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f6, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f7, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f8, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f9, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f10, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f11, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f12, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f13, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f14, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f15, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f16, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f17, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f18, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f19, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f20, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f21, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f22, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f23, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f24, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f25, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f26, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f27, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f28, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f29, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f30, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR_PPC64(f31, NULL, LLDB_INVALID_REGNUM), \
+ {"fpscr", \
+ NULL, \
+ 8, \
+ FPR_PPC64_OFFSET(fpscr), \
+ lldb::eEncodingUint, \
+ lldb::eFormatHex, \
+ {ppc64_dwarf::dwarf_fpscr_ppc64, \
+ ppc64_dwarf::dwarf_fpscr_ppc64, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, fpr_fpscr_ppc64}, \
+ NULL, \
+ NULL, \
+ NULL, \
+ 0}, \
+ DEFINE_VMX_PPC64(vr0, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr1, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr2, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr3, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr4, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr5, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr6, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr7, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr8, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr9, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr10, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr11, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr12, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr13, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr14, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr15, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr16, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr17, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr18, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr19, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr20, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr21, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr22, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr23, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr24, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr25, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr26, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr27, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr28, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr29, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr30, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX_PPC64(vr31, LLDB_INVALID_REGNUM), \
+ {"vscr", \
+ NULL, \
+ 4, \
+ VMX_PPC64_OFFSET(vscr), \
+ lldb::eEncodingUint, \
+ lldb::eFormatHex, \
+ {ppc64_dwarf::dwarf_vscr_ppc64, ppc64_dwarf::dwarf_vscr_ppc64, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, vmx_vscr_ppc64}, \
+ NULL, \
+ NULL, \
+ NULL, \
+ 0}, \
+ {"vrsave", \
+ NULL, \
+ 4, \
+ VMX_PPC64_OFFSET(vrsave), \
+ lldb::eEncodingUint, \
+ lldb::eFormatHex, \
+ {ppc64_dwarf::dwarf_vrsave_ppc64, \
+ ppc64_dwarf::dwarf_vrsave_ppc64, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, vmx_vrsave_ppc64}, \
+ NULL, \
+ NULL, \
+ NULL, \
+ 0}, /* */
+
+typedef struct _GPR_PPC64 {
+ uint64_t r0;
+ uint64_t r1;
+ uint64_t r2;
+ uint64_t r3;
+ uint64_t r4;
+ uint64_t r5;
+ uint64_t r6;
+ uint64_t r7;
+ 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 r16;
+ uint64_t r17;
+ uint64_t r18;
+ uint64_t r19;
+ uint64_t r20;
+ uint64_t r21;
+ uint64_t r22;
+ uint64_t r23;
+ uint64_t r24;
+ uint64_t r25;
+ uint64_t r26;
+ uint64_t r27;
+ uint64_t r28;
+ uint64_t r29;
+ uint64_t r30;
+ uint64_t r31;
+ uint64_t cr;
+ uint64_t msr;
+ uint64_t xer;
+ uint64_t lr;
+ uint64_t ctr;
+ uint64_t pc;
+ uint64_t pad[3];
+} GPR_PPC64;
+
+typedef struct _FPR_PPC64 {
+ uint64_t f0;
+ uint64_t f1;
+ uint64_t f2;
+ uint64_t f3;
+ uint64_t f4;
+ uint64_t f5;
+ uint64_t f6;
+ uint64_t f7;
+ uint64_t f8;
+ uint64_t f9;
+ uint64_t f10;
+ uint64_t f11;
+ uint64_t f12;
+ uint64_t f13;
+ uint64_t f14;
+ uint64_t f15;
+ uint64_t f16;
+ uint64_t f17;
+ uint64_t f18;
+ uint64_t f19;
+ uint64_t f20;
+ uint64_t f21;
+ uint64_t f22;
+ uint64_t f23;
+ uint64_t f24;
+ uint64_t f25;
+ uint64_t f26;
+ uint64_t f27;
+ uint64_t f28;
+ uint64_t f29;
+ uint64_t f30;
+ uint64_t f31;
+ uint64_t fpscr;
+} FPR_PPC64;
+
+typedef struct _VMX_PPC64 {
+ uint32_t vr0[4];
+ uint32_t vr1[4];
+ uint32_t vr2[4];
+ uint32_t vr3[4];
+ uint32_t vr4[4];
+ uint32_t vr5[4];
+ uint32_t vr6[4];
+ uint32_t vr7[4];
+ uint32_t vr8[4];
+ uint32_t vr9[4];
+ uint32_t vr10[4];
+ uint32_t vr11[4];
+ uint32_t vr12[4];
+ uint32_t vr13[4];
+ uint32_t vr14[4];
+ uint32_t vr15[4];
+ uint32_t vr16[4];
+ uint32_t vr17[4];
+ uint32_t vr18[4];
+ uint32_t vr19[4];
+ uint32_t vr20[4];
+ uint32_t vr21[4];
+ uint32_t vr22[4];
+ uint32_t vr23[4];
+ uint32_t vr24[4];
+ uint32_t vr25[4];
+ uint32_t vr26[4];
+ uint32_t vr27[4];
+ uint32_t vr28[4];
+ uint32_t vr29[4];
+ uint32_t vr30[4];
+ uint32_t vr31[4];
+ uint32_t pad[2];
+ uint32_t vscr[2];
+ uint32_t vrsave;
+} VMX_PPC64;
+
+
+static lldb_private::RegisterInfo g_register_infos_ppc64[] = {
+ PPC64_REGS
+};
+
+static_assert((sizeof(g_register_infos_ppc64) /
+ sizeof(g_register_infos_ppc64[0])) ==
+ k_num_registers_ppc64,
+ "g_register_infos_powerpc64 has wrong number of register infos");
+
+#undef DEFINE_FPR_PPC64
+#undef DEFINE_GPR_PPC64
+#undef DEFINE_VMX_PPC64
+
+#endif // DECLARE_REGISTER_INFOS_PPC64_STRUCT
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_ppc64le.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_ppc64le.h
new file mode 100644
index 000000000000..0b099a53d875
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_ppc64le.h
@@ -0,0 +1,474 @@
+//===-- RegisterInfos_ppc64le.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifdef DECLARE_REGISTER_INFOS_PPC64LE_STRUCT
+
+#include <stddef.h>
+
+// Computes the offset of the given GPR in the user data area.
+#define GPR_OFFSET(regname) (offsetof(GPR, regname))
+#define FPR_OFFSET(regname) (offsetof(FPR, regname) + sizeof(GPR))
+#define VMX_OFFSET(regname) (offsetof(VMX, regname) + sizeof(GPR) + sizeof(FPR))
+#define VSX_OFFSET(regname) \
+ (offsetof(VSX, regname) + sizeof(GPR) + sizeof(FPR) + sizeof(VMX))
+#define GPR_SIZE(regname) (sizeof(((GPR *)NULL)->regname))
+
+#include "Utility/PPC64LE_DWARF_Registers.h"
+#include "lldb-ppc64le-register-enums.h"
+
+// Note that the size and offset will be updated by platform-specific classes.
+#define DEFINE_GPR(reg, alt, lldb_kind) \
+ { \
+ #reg, alt, GPR_SIZE(reg), GPR_OFFSET(reg), lldb::eEncodingUint, \
+ lldb::eFormatHex, \
+ {ppc64le_dwarf::dwarf_##reg##_ppc64le,\
+ ppc64le_dwarf::dwarf_##reg##_ppc64le,\
+ lldb_kind, \
+ LLDB_INVALID_REGNUM, \
+ gpr_##reg##_ppc64le }, \
+ NULL, NULL, NULL, 0 \
+ }
+#define DEFINE_FPR(reg, alt, lldb_kind) \
+ { \
+#reg, alt, 8, FPR_OFFSET(reg), lldb::eEncodingIEEE754, lldb::eFormatFloat, \
+ {ppc64le_dwarf::dwarf_##reg##_ppc64le, \
+ ppc64le_dwarf::dwarf_##reg##_ppc64le, lldb_kind, LLDB_INVALID_REGNUM, \
+ fpr_##reg##_ppc64le }, \
+ NULL, NULL, NULL, 0 \
+ }
+#define DEFINE_VMX(reg, lldb_kind) \
+ { \
+#reg, NULL, 16, VMX_OFFSET(reg), lldb::eEncodingVector, \
+ lldb::eFormatVectorOfUInt32, \
+ {ppc64le_dwarf::dwarf_##reg##_ppc64le, \
+ ppc64le_dwarf::dwarf_##reg##_ppc64le, lldb_kind, LLDB_INVALID_REGNUM, \
+ vmx_##reg##_ppc64le }, \
+ NULL, NULL, NULL, 0 \
+ }
+#define DEFINE_VSX(reg, lldb_kind) \
+ { \
+#reg, NULL, 16, VSX_OFFSET(reg), lldb::eEncodingVector, \
+ lldb::eFormatVectorOfUInt32, \
+ {ppc64le_dwarf::dwarf_##reg##_ppc64le, \
+ ppc64le_dwarf::dwarf_##reg##_ppc64le, lldb_kind, LLDB_INVALID_REGNUM, \
+ vsx_##reg##_ppc64le }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+// General purpose registers.
+// EH_Frame, Generic, Process Plugin
+#define POWERPC_REGS \
+ DEFINE_GPR(r0, NULL, LLDB_INVALID_REGNUM) \
+ , DEFINE_GPR(r1, "sp", LLDB_REGNUM_GENERIC_SP), \
+ DEFINE_GPR(r2, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r3, "arg1", LLDB_REGNUM_GENERIC_ARG1), \
+ DEFINE_GPR(r4, "arg2", LLDB_REGNUM_GENERIC_ARG2), \
+ DEFINE_GPR(r5, "arg3", LLDB_REGNUM_GENERIC_ARG3), \
+ DEFINE_GPR(r6, "arg4", LLDB_REGNUM_GENERIC_ARG4), \
+ DEFINE_GPR(r7, "arg5", LLDB_REGNUM_GENERIC_ARG5), \
+ DEFINE_GPR(r8, "arg6", LLDB_REGNUM_GENERIC_ARG6), \
+ DEFINE_GPR(r9, "arg7", LLDB_REGNUM_GENERIC_ARG7), \
+ DEFINE_GPR(r10, "arg8", LLDB_REGNUM_GENERIC_ARG8), \
+ DEFINE_GPR(r11, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r12, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r13, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r14, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r15, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r16, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r17, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r18, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r19, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r20, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r21, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r22, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r23, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r24, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r25, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r26, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r27, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r28, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r29, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r30, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(r31, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(pc, "pc", LLDB_REGNUM_GENERIC_PC), \
+ DEFINE_GPR(msr, "msr", LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(origr3, "orig_r3", LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(ctr, "ctr", LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(lr, "lr", LLDB_REGNUM_GENERIC_RA), \
+ DEFINE_GPR(xer, "xer", LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(cr, "cr", LLDB_REGNUM_GENERIC_FLAGS), \
+ DEFINE_GPR(softe, "softe", LLDB_INVALID_REGNUM), \
+ DEFINE_GPR(trap, "trap", LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f0, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f1, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f2, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f3, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f4, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f5, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f6, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f7, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f8, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f9, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f10, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f11, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f12, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f13, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f14, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f15, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f16, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f17, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f18, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f19, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f20, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f21, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f22, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f23, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f24, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f25, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f26, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f27, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f28, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f29, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f30, NULL, LLDB_INVALID_REGNUM), \
+ DEFINE_FPR(f31, NULL, LLDB_INVALID_REGNUM), \
+ {"fpscr", \
+ NULL, \
+ 8, \
+ FPR_OFFSET(fpscr), \
+ lldb::eEncodingUint, \
+ lldb::eFormatHex, \
+ {ppc64le_dwarf::dwarf_fpscr_ppc64le, \
+ ppc64le_dwarf::dwarf_fpscr_ppc64le, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, fpr_fpscr_ppc64le}, \
+ NULL, \
+ NULL, \
+ NULL, \
+ 0}, \
+ DEFINE_VMX(vr0, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr1, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr2, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr3, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr4, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr5, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr6, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr7, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr8, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr9, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr10, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr11, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr12, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr13, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr14, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr15, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr16, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr17, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr18, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr19, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr20, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr21, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr22, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr23, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr24, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr25, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr26, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr27, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr28, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr29, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr30, LLDB_INVALID_REGNUM), \
+ DEFINE_VMX(vr31, LLDB_INVALID_REGNUM), \
+ {"vscr", \
+ NULL, \
+ 4, \
+ VMX_OFFSET(vscr), \
+ lldb::eEncodingUint, \
+ lldb::eFormatHex, \
+ {ppc64le_dwarf::dwarf_vscr_ppc64le, ppc64le_dwarf::dwarf_vscr_ppc64le, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, vmx_vscr_ppc64le}, \
+ NULL, \
+ NULL, \
+ NULL, \
+ 0}, \
+ {"vrsave", \
+ NULL, \
+ 4, \
+ VMX_OFFSET(vrsave), \
+ lldb::eEncodingUint, \
+ lldb::eFormatHex, \
+ {ppc64le_dwarf::dwarf_vrsave_ppc64le, \
+ ppc64le_dwarf::dwarf_vrsave_ppc64le, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, vmx_vrsave_ppc64le}, \
+ NULL, \
+ NULL, \
+ NULL, \
+ 0}, \
+ DEFINE_VSX(vs0, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs1, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs2, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs3, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs4, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs5, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs6, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs7, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs8, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs9, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs10, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs11, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs12, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs13, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs14, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs15, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs16, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs17, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs18, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs19, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs20, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs21, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs22, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs23, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs24, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs25, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs26, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs27, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs28, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs29, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs30, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs31, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs32, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs33, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs34, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs35, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs36, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs37, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs38, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs39, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs40, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs41, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs42, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs43, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs44, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs45, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs46, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs47, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs48, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs49, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs50, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs51, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs52, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs53, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs54, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs55, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs56, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs57, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs58, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs59, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs50, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs61, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs62, LLDB_INVALID_REGNUM), \
+ DEFINE_VSX(vs63, LLDB_INVALID_REGNUM), /* */
+
+typedef struct _GPR {
+ uint64_t r0;
+ uint64_t r1;
+ uint64_t r2;
+ uint64_t r3;
+ uint64_t r4;
+ uint64_t r5;
+ uint64_t r6;
+ uint64_t r7;
+ 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 r16;
+ uint64_t r17;
+ uint64_t r18;
+ uint64_t r19;
+ uint64_t r20;
+ uint64_t r21;
+ uint64_t r22;
+ uint64_t r23;
+ uint64_t r24;
+ uint64_t r25;
+ uint64_t r26;
+ uint64_t r27;
+ uint64_t r28;
+ uint64_t r29;
+ uint64_t r30;
+ uint64_t r31;
+ uint64_t pc;
+ uint64_t msr;
+ uint64_t origr3;
+ uint64_t ctr;
+ uint64_t lr;
+ uint64_t xer;
+ uint64_t cr;
+ uint64_t softe;
+ uint64_t trap;
+ uint64_t pad[3];
+} GPR;
+
+typedef struct _FPR {
+ uint64_t f0;
+ uint64_t f1;
+ uint64_t f2;
+ uint64_t f3;
+ uint64_t f4;
+ uint64_t f5;
+ uint64_t f6;
+ uint64_t f7;
+ uint64_t f8;
+ uint64_t f9;
+ uint64_t f10;
+ uint64_t f11;
+ uint64_t f12;
+ uint64_t f13;
+ uint64_t f14;
+ uint64_t f15;
+ uint64_t f16;
+ uint64_t f17;
+ uint64_t f18;
+ uint64_t f19;
+ uint64_t f20;
+ uint64_t f21;
+ uint64_t f22;
+ uint64_t f23;
+ uint64_t f24;
+ uint64_t f25;
+ uint64_t f26;
+ uint64_t f27;
+ uint64_t f28;
+ uint64_t f29;
+ uint64_t f30;
+ uint64_t f31;
+ uint64_t fpscr;
+} FPR;
+
+typedef struct _VMX {
+ uint32_t vr0[4];
+ uint32_t vr1[4];
+ uint32_t vr2[4];
+ uint32_t vr3[4];
+ uint32_t vr4[4];
+ uint32_t vr5[4];
+ uint32_t vr6[4];
+ uint32_t vr7[4];
+ uint32_t vr8[4];
+ uint32_t vr9[4];
+ uint32_t vr10[4];
+ uint32_t vr11[4];
+ uint32_t vr12[4];
+ uint32_t vr13[4];
+ uint32_t vr14[4];
+ uint32_t vr15[4];
+ uint32_t vr16[4];
+ uint32_t vr17[4];
+ uint32_t vr18[4];
+ uint32_t vr19[4];
+ uint32_t vr20[4];
+ uint32_t vr21[4];
+ uint32_t vr22[4];
+ uint32_t vr23[4];
+ uint32_t vr24[4];
+ uint32_t vr25[4];
+ uint32_t vr26[4];
+ uint32_t vr27[4];
+ uint32_t vr28[4];
+ uint32_t vr29[4];
+ uint32_t vr30[4];
+ uint32_t vr31[4];
+ uint32_t pad[2];
+ uint32_t vscr[2];
+ uint32_t vrsave;
+} VMX;
+
+typedef struct _VSX {
+ uint32_t vs0[4];
+ uint32_t vs1[4];
+ uint32_t vs2[4];
+ uint32_t vs3[4];
+ uint32_t vs4[4];
+ uint32_t vs5[4];
+ uint32_t vs6[4];
+ uint32_t vs7[4];
+ uint32_t vs8[4];
+ uint32_t vs9[4];
+ uint32_t vs10[4];
+ uint32_t vs11[4];
+ uint32_t vs12[4];
+ uint32_t vs13[4];
+ uint32_t vs14[4];
+ uint32_t vs15[4];
+ uint32_t vs16[4];
+ uint32_t vs17[4];
+ uint32_t vs18[4];
+ uint32_t vs19[4];
+ uint32_t vs20[4];
+ uint32_t vs21[4];
+ uint32_t vs22[4];
+ uint32_t vs23[4];
+ uint32_t vs24[4];
+ uint32_t vs25[4];
+ uint32_t vs26[4];
+ uint32_t vs27[4];
+ uint32_t vs28[4];
+ uint32_t vs29[4];
+ uint32_t vs30[4];
+ uint32_t vs31[4];
+ uint32_t vs32[4];
+ uint32_t vs33[4];
+ uint32_t vs34[4];
+ uint32_t vs35[4];
+ uint32_t vs36[4];
+ uint32_t vs37[4];
+ uint32_t vs38[4];
+ uint32_t vs39[4];
+ uint32_t vs40[4];
+ uint32_t vs41[4];
+ uint32_t vs42[4];
+ uint32_t vs43[4];
+ uint32_t vs44[4];
+ uint32_t vs45[4];
+ uint32_t vs46[4];
+ uint32_t vs47[4];
+ uint32_t vs48[4];
+ uint32_t vs49[4];
+ uint32_t vs50[4];
+ uint32_t vs51[4];
+ uint32_t vs52[4];
+ uint32_t vs53[4];
+ uint32_t vs54[4];
+ uint32_t vs55[4];
+ uint32_t vs56[4];
+ uint32_t vs57[4];
+ uint32_t vs58[4];
+ uint32_t vs59[4];
+ uint32_t vs60[4];
+ uint32_t vs61[4];
+ uint32_t vs62[4];
+ uint32_t vs63[4];
+} VSX;
+
+static lldb_private::RegisterInfo g_register_infos_ppc64le[] = {
+ POWERPC_REGS
+};
+
+static_assert((sizeof(g_register_infos_ppc64le) /
+ sizeof(g_register_infos_ppc64le[0])) ==
+ k_num_registers_ppc64le,
+ "g_register_infos_powerpc64 has wrong number of register infos");
+
+#undef DEFINE_FPR
+#undef DEFINE_GPR
+#undef DEFINE_VMX
+#undef DEFINE_VSX
+
+#endif // DECLARE_REGISTER_INFOS_PPC64LE_STRUCT
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_s390x.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_s390x.h
new file mode 100644
index 000000000000..11344ff8ee79
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_s390x.h
@@ -0,0 +1,124 @@
+//===-- RegisterInfos_s390x.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 <stddef.h>
+
+#include "llvm/Support/Compiler.h"
+
+
+#ifdef DECLARE_REGISTER_INFOS_S390X_STRUCT
+
+// Computes the offset of the given GPR in the user data area.
+#define GPR_OFFSET(num) (16 + 8 * num)
+// Computes the offset of the given ACR in the user data area.
+#define ACR_OFFSET(num) (16 + 8 * 16 + 4 * num)
+// Computes the offset of the given FPR in the extended data area.
+#define FPR_OFFSET(num) (8 + 8 * num)
+
+// RegisterKind: EHFrame, DWARF, Generic, Process Plugin, LLDB
+
+#define DEFINE_GPR(name, size, offset, alt, generic) \
+ { \
+ #name, alt, size, offset, eEncodingUint, eFormatHex, \
+ {dwarf_##name##_s390x, dwarf_##name##_s390x, generic, \
+ LLDB_INVALID_REGNUM, lldb_##name##_s390x }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+#define DEFINE_GPR_NODWARF(name, size, offset, alt, generic) \
+ { \
+ #name, alt, size, offset, eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, generic, \
+ LLDB_INVALID_REGNUM, lldb_##name##_s390x }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+#define DEFINE_FPR(name, size, offset) \
+ { \
+ #name, NULL, size, offset, eEncodingUint, eFormatHex, \
+ {dwarf_##name##_s390x, dwarf_##name##_s390x, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, lldb_##name##_s390x }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+#define DEFINE_FPR_NODWARF(name, size, offset) \
+ { \
+ #name, NULL, size, offset, eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, lldb_##name##_s390x }, \
+ NULL, NULL, NULL, 0 \
+ }
+
+static RegisterInfo g_register_infos_s390x[] = {
+ // General purpose registers.
+ DEFINE_GPR(r0, 8, GPR_OFFSET(0), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r1, 8, GPR_OFFSET(1), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r2, 8, GPR_OFFSET(2), "arg1", LLDB_REGNUM_GENERIC_ARG1),
+ DEFINE_GPR(r3, 8, GPR_OFFSET(3), "arg2", LLDB_REGNUM_GENERIC_ARG2),
+ DEFINE_GPR(r4, 8, GPR_OFFSET(4), "arg3", LLDB_REGNUM_GENERIC_ARG3),
+ DEFINE_GPR(r5, 8, GPR_OFFSET(5), "arg4", LLDB_REGNUM_GENERIC_ARG4),
+ DEFINE_GPR(r6, 8, GPR_OFFSET(6), "arg5", LLDB_REGNUM_GENERIC_ARG5),
+ DEFINE_GPR(r7, 8, GPR_OFFSET(7), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r8, 8, GPR_OFFSET(8), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r9, 8, GPR_OFFSET(9), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r10, 8, GPR_OFFSET(10), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r11, 8, GPR_OFFSET(11), "fp", LLDB_REGNUM_GENERIC_FP),
+ DEFINE_GPR(r12, 8, GPR_OFFSET(12), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r13, 8, GPR_OFFSET(13), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r14, 8, GPR_OFFSET(14), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r15, 8, GPR_OFFSET(15), "sp", LLDB_REGNUM_GENERIC_SP),
+ DEFINE_GPR(acr0, 4, ACR_OFFSET(0), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr1, 4, ACR_OFFSET(1), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr2, 4, ACR_OFFSET(2), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr3, 4, ACR_OFFSET(3), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr4, 4, ACR_OFFSET(4), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr5, 4, ACR_OFFSET(5), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr6, 4, ACR_OFFSET(6), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr7, 4, ACR_OFFSET(7), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr8, 4, ACR_OFFSET(8), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr9, 4, ACR_OFFSET(9), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr10, 4, ACR_OFFSET(10), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr11, 4, ACR_OFFSET(11), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr12, 4, ACR_OFFSET(12), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr13, 4, ACR_OFFSET(13), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr14, 4, ACR_OFFSET(14), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(acr15, 4, ACR_OFFSET(15), nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(pswm, 8, 0, "flags", LLDB_REGNUM_GENERIC_FLAGS),
+ DEFINE_GPR(pswa, 8, 8, "pc", LLDB_REGNUM_GENERIC_PC),
+
+ // Floating point registers.
+ DEFINE_FPR(f0, 8, FPR_OFFSET(0)), DEFINE_FPR(f1, 8, FPR_OFFSET(1)),
+ DEFINE_FPR(f2, 8, FPR_OFFSET(2)), DEFINE_FPR(f3, 8, FPR_OFFSET(3)),
+ DEFINE_FPR(f4, 8, FPR_OFFSET(4)), DEFINE_FPR(f5, 8, FPR_OFFSET(5)),
+ DEFINE_FPR(f6, 8, FPR_OFFSET(6)), DEFINE_FPR(f7, 8, FPR_OFFSET(7)),
+ DEFINE_FPR(f8, 8, FPR_OFFSET(8)), DEFINE_FPR(f9, 8, FPR_OFFSET(9)),
+ DEFINE_FPR(f10, 8, FPR_OFFSET(10)), DEFINE_FPR(f11, 8, FPR_OFFSET(11)),
+ DEFINE_FPR(f12, 8, FPR_OFFSET(12)), DEFINE_FPR(f13, 8, FPR_OFFSET(13)),
+ DEFINE_FPR(f14, 8, FPR_OFFSET(14)), DEFINE_FPR(f15, 8, FPR_OFFSET(15)),
+ DEFINE_FPR_NODWARF(fpc, 4, 0),
+
+ // Linux operating-specific info.
+ DEFINE_GPR_NODWARF(orig_r2, 8, 16 + 16 * 8 + 16 * 4, nullptr,
+ LLDB_INVALID_REGNUM),
+ DEFINE_GPR_NODWARF(last_break, 8, 0, nullptr, LLDB_INVALID_REGNUM),
+ DEFINE_GPR_NODWARF(system_call, 4, 0, nullptr, LLDB_INVALID_REGNUM),
+};
+
+static_assert((sizeof(g_register_infos_s390x) /
+ sizeof(g_register_infos_s390x[0])) == k_num_registers_s390x,
+ "g_register_infos_s390x has wrong number of register infos");
+
+#undef GPR_OFFSET
+#undef ACR_OFFSET
+#undef FPR_OFFSET
+#undef DEFINE_GPR
+#undef DEFINE_GPR_NODWARF
+#undef DEFINE_FPR
+#undef DEFINE_FPR_NODWARF
+
+#endif // DECLARE_REGISTER_INFOS_S390X_STRUCT
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_x86_64.h
new file mode 100644
index 000000000000..4a3b3c73fd6b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_x86_64.h
@@ -0,0 +1,466 @@
+//===-- RegisterInfos_x86_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
+//
+//===----------------------------------------------------------------------===//
+
+// This file is meant to be textually included. Do not #include modular
+// headers here.
+
+// Computes the offset of the given GPR in the user data area.
+#define GPR_OFFSET(regname) (LLVM_EXTENSION offsetof(GPR, regname))
+
+// Computes the offset of the given FPR in the extended data area.
+#define FPR_OFFSET(regname) \
+ (LLVM_EXTENSION offsetof(UserArea, fpr) + \
+ LLVM_EXTENSION offsetof(FPR, fxsave) + \
+ LLVM_EXTENSION offsetof(FXSAVE, regname))
+
+// Computes the offset of the YMM register assembled from register halves.
+// Based on DNBArchImplX86_64.cpp from debugserver
+#define YMM_OFFSET(reg_index) \
+ (LLVM_EXTENSION offsetof(UserArea, fpr) + \
+ LLVM_EXTENSION offsetof(FPR, xsave) + \
+ LLVM_EXTENSION offsetof(XSAVE, ymmh[0]) + (32 * reg_index))
+
+#define BNDR_OFFSET(reg_index) \
+ (LLVM_EXTENSION offsetof(UserArea, fpr) + \
+ LLVM_EXTENSION offsetof(FPR, xsave) + \
+ LLVM_EXTENSION offsetof(XSAVE, mpxr[reg_index]))
+
+#define BNDC_OFFSET(reg_index) \
+ (LLVM_EXTENSION offsetof(UserArea, fpr) + \
+ LLVM_EXTENSION offsetof(FPR, xsave) + \
+ LLVM_EXTENSION offsetof(XSAVE, mpxc[reg_index]))
+
+#ifdef DECLARE_REGISTER_INFOS_X86_64_STRUCT
+
+// Number of bytes needed to represent a FPR.
+#define FPR_SIZE(reg) sizeof(((FXSAVE *)nullptr)->reg)
+
+// Number of bytes needed to represent the i'th FP register.
+#define FP_SIZE sizeof(((MMSReg *)nullptr)->bytes)
+
+// Number of bytes needed to represent an XMM register.
+#define XMM_SIZE sizeof(XMMReg)
+
+// Number of bytes needed to represent a YMM register.
+#define YMM_SIZE sizeof(YMMReg)
+
+// Number of bytes needed to represent MPX registers.
+#define BNDR_SIZE sizeof(MPXReg)
+#define BNDC_SIZE sizeof(MPXCsr)
+
+#define DR_SIZE sizeof(((DBG *)nullptr)->dr[0])
+
+// RegisterKind: EHFrame, DWARF, Generic, Process Plugin, LLDB
+
+// Note that the size and offset will be updated by platform-specific classes.
+#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \
+ { \
+ #reg, alt, sizeof(((GPR *)nullptr)->reg), \
+ GPR_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {kind1, kind2, kind3, kind4, \
+ lldb_##reg##_x86_64 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_FPR(name, reg, kind1, kind2, kind3, kind4) \
+ { \
+ #name, nullptr, FPR_SIZE(reg), FPR_OFFSET(reg), eEncodingUint, eFormatHex, \
+ {kind1, kind2, kind3, kind4, \
+ lldb_##name##_x86_64 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_FP_ST(reg, i) \
+ { \
+ #reg #i, nullptr, FP_SIZE, \
+ LLVM_EXTENSION FPR_OFFSET( \
+ stmm[i]), eEncodingVector, eFormatVectorOfUInt8, \
+ {dwarf_st##i##_x86_64, dwarf_st##i##_x86_64, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, lldb_st##i##_x86_64 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_FP_MM(reg, i) \
+ { \
+ #reg #i, nullptr, sizeof(uint64_t), \
+ LLVM_EXTENSION FPR_OFFSET( \
+ stmm[i]), eEncodingUint, eFormatHex, \
+ {dwarf_mm##i##_x86_64, dwarf_mm##i##_x86_64, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_mm##i##_x86_64 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_XMM(reg, i) \
+ { \
+ #reg #i, nullptr, XMM_SIZE, \
+ LLVM_EXTENSION FPR_OFFSET( \
+ reg[i]), eEncodingVector, eFormatVectorOfUInt8, \
+ {dwarf_##reg##i##_x86_64, dwarf_##reg##i##_x86_64, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_##reg##i##_x86_64 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_YMM(reg, i) \
+ { \
+ #reg #i, nullptr, YMM_SIZE, \
+ LLVM_EXTENSION YMM_OFFSET(i), eEncodingVector, eFormatVectorOfUInt8, \
+ {dwarf_##reg##i##h_x86_64, \
+ dwarf_##reg##i##h_x86_64, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_##reg##i##_x86_64 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_BNDR(reg, i) \
+ { \
+ #reg #i, nullptr, BNDR_SIZE, \
+ LLVM_EXTENSION BNDR_OFFSET(i), eEncodingVector, eFormatVectorOfUInt64, \
+ {dwarf_##reg##i##_x86_64, \
+ dwarf_##reg##i##_x86_64, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_##reg##i##_x86_64 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_BNDC(name, i) \
+ { \
+ #name, nullptr, BNDC_SIZE, \
+ LLVM_EXTENSION BNDC_OFFSET(i), eEncodingVector, eFormatVectorOfUInt8, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, lldb_##name##_x86_64 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_DR(reg, i) \
+ { \
+ #reg #i, nullptr, DR_SIZE, \
+ DR_OFFSET(i), eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_##reg##i##_x86_64 }, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEFINE_GPR_PSEUDO_32(reg32, reg64) \
+ { \
+ #reg32, nullptr, 4, \
+ GPR_OFFSET(reg64), eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_##reg32##_x86_64 }, \
+ RegisterContextPOSIX_x86::g_contained_##reg64, \
+ RegisterContextPOSIX_x86::g_invalidate_##reg64, nullptr, 0 \
+ }
+
+#define DEFINE_GPR_PSEUDO_16(reg16, reg64) \
+ { \
+ #reg16, nullptr, 2, \
+ GPR_OFFSET(reg64), eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_##reg16##_x86_64 }, \
+ RegisterContextPOSIX_x86::g_contained_##reg64, \
+ RegisterContextPOSIX_x86::g_invalidate_##reg64, nullptr, 0 \
+ }
+
+#define DEFINE_GPR_PSEUDO_8H(reg8, reg64) \
+ { \
+ #reg8, nullptr, 1, \
+ GPR_OFFSET(reg64) + 1, eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_##reg8##_x86_64 }, \
+ RegisterContextPOSIX_x86::g_contained_##reg64, \
+ RegisterContextPOSIX_x86::g_invalidate_##reg64, nullptr, 0 \
+ }
+
+#define DEFINE_GPR_PSEUDO_8L(reg8, reg64) \
+ { \
+ #reg8, nullptr, 1, \
+ GPR_OFFSET(reg64), eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ lldb_##reg8##_x86_64 }, \
+ RegisterContextPOSIX_x86::g_contained_##reg64, \
+ RegisterContextPOSIX_x86::g_invalidate_##reg64, nullptr, 0 \
+ }
+
+// clang-format off
+static RegisterInfo g_register_infos_x86_64[] = {
+// General purpose registers EH_Frame DWARF Generic Process Plugin
+// =========================== ================== ================ ========================= ====================
+ DEFINE_GPR(rax, nullptr, dwarf_rax_x86_64, dwarf_rax_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(rbx, nullptr, dwarf_rbx_x86_64, dwarf_rbx_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(rcx, "arg4", dwarf_rcx_x86_64, dwarf_rcx_x86_64, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(rdx, "arg3", dwarf_rdx_x86_64, dwarf_rdx_x86_64, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(rdi, "arg1", dwarf_rdi_x86_64, dwarf_rdi_x86_64, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(rsi, "arg2", dwarf_rsi_x86_64, dwarf_rsi_x86_64, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(rbp, "fp", dwarf_rbp_x86_64, dwarf_rbp_x86_64, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(rsp, "sp", dwarf_rsp_x86_64, dwarf_rsp_x86_64, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r8, "arg5", dwarf_r8_x86_64, dwarf_r8_x86_64, LLDB_REGNUM_GENERIC_ARG5, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r9, "arg6", dwarf_r9_x86_64, dwarf_r9_x86_64, LLDB_REGNUM_GENERIC_ARG6, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r10, nullptr, dwarf_r10_x86_64, dwarf_r10_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r11, nullptr, dwarf_r11_x86_64, dwarf_r11_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r12, nullptr, dwarf_r12_x86_64, dwarf_r12_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r13, nullptr, dwarf_r13_x86_64, dwarf_r13_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r14, nullptr, dwarf_r14_x86_64, dwarf_r14_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(r15, nullptr, dwarf_r15_x86_64, dwarf_r15_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(rip, "pc", dwarf_rip_x86_64, dwarf_rip_x86_64, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(rflags, "flags", dwarf_rflags_x86_64, dwarf_rflags_x86_64, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(cs, nullptr, dwarf_cs_x86_64, dwarf_cs_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(fs, nullptr, dwarf_fs_x86_64, dwarf_fs_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(gs, nullptr, dwarf_gs_x86_64, dwarf_gs_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(ss, nullptr, dwarf_ss_x86_64, dwarf_ss_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(ds, nullptr, dwarf_ds_x86_64, dwarf_ds_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_GPR(es, nullptr, dwarf_es_x86_64, dwarf_es_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+
+ 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),
+
+// i387 Floating point registers. EH_frame DWARF Generic Process Plugin
+// ====================================== =============== ================== =================== ====================
+ DEFINE_FPR(fctrl, fctrl, dwarf_fctrl_x86_64, dwarf_fctrl_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(fstat, fstat, dwarf_fstat_x86_64, dwarf_fstat_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(ftag, ftag, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(fop, fop, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(fiseg, ptr.i386_.fiseg, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(fioff, ptr.i386_.fioff, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(foseg, ptr.i386_.foseg, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(fooff, ptr.i386_.fooff, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(mxcsr, mxcsr, dwarf_mxcsr_x86_64, dwarf_mxcsr_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+ DEFINE_FPR(mxcsrmask, mxcsrmask, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM),
+
+ // FP registers.
+ DEFINE_FP_ST(st, 0), DEFINE_FP_ST(st, 1), DEFINE_FP_ST(st, 2),
+ DEFINE_FP_ST(st, 3), DEFINE_FP_ST(st, 4), DEFINE_FP_ST(st, 5),
+ DEFINE_FP_ST(st, 6), DEFINE_FP_ST(st, 7), DEFINE_FP_MM(mm, 0),
+ DEFINE_FP_MM(mm, 1), DEFINE_FP_MM(mm, 2), DEFINE_FP_MM(mm, 3),
+ DEFINE_FP_MM(mm, 4), DEFINE_FP_MM(mm, 5), DEFINE_FP_MM(mm, 6),
+ DEFINE_FP_MM(mm, 7),
+
+ // XMM registers
+ DEFINE_XMM(xmm, 0), DEFINE_XMM(xmm, 1), DEFINE_XMM(xmm, 2),
+ DEFINE_XMM(xmm, 3), DEFINE_XMM(xmm, 4), DEFINE_XMM(xmm, 5),
+ DEFINE_XMM(xmm, 6), DEFINE_XMM(xmm, 7), DEFINE_XMM(xmm, 8),
+ DEFINE_XMM(xmm, 9), DEFINE_XMM(xmm, 10), DEFINE_XMM(xmm, 11),
+ DEFINE_XMM(xmm, 12), DEFINE_XMM(xmm, 13), DEFINE_XMM(xmm, 14),
+ DEFINE_XMM(xmm, 15),
+
+ // Copy of YMM registers assembled from xmm and ymmh
+ DEFINE_YMM(ymm, 0), DEFINE_YMM(ymm, 1), DEFINE_YMM(ymm, 2),
+ DEFINE_YMM(ymm, 3), DEFINE_YMM(ymm, 4), DEFINE_YMM(ymm, 5),
+ DEFINE_YMM(ymm, 6), DEFINE_YMM(ymm, 7), DEFINE_YMM(ymm, 8),
+ DEFINE_YMM(ymm, 9), DEFINE_YMM(ymm, 10), DEFINE_YMM(ymm, 11),
+ DEFINE_YMM(ymm, 12), DEFINE_YMM(ymm, 13), DEFINE_YMM(ymm, 14),
+ DEFINE_YMM(ymm, 15),
+
+ // MPX registers
+ DEFINE_BNDR(bnd, 0),
+ DEFINE_BNDR(bnd, 1),
+ DEFINE_BNDR(bnd, 2),
+ DEFINE_BNDR(bnd, 3),
+
+ DEFINE_BNDC(bndcfgu, 0),
+ DEFINE_BNDC(bndstatus, 1),
+
+ // Debug registers for lldb internal use
+ DEFINE_DR(dr, 0), DEFINE_DR(dr, 1), DEFINE_DR(dr, 2), DEFINE_DR(dr, 3),
+ DEFINE_DR(dr, 4), DEFINE_DR(dr, 5), DEFINE_DR(dr, 6), DEFINE_DR(dr, 7)};
+
+// clang-format on
+
+static_assert((sizeof(g_register_infos_x86_64) /
+ sizeof(g_register_infos_x86_64[0])) == k_num_registers_x86_64,
+ "g_register_infos_x86_64 has wrong number of register infos");
+
+#undef FPR_SIZE
+#undef FP_SIZE
+#undef XMM_SIZE
+#undef YMM_SIZE
+#undef DEFINE_GPR
+#undef DEFINE_FPR
+#undef DEFINE_FP
+#undef DEFINE_XMM
+#undef DEFINE_YMM
+#undef DEFINE_BNDR
+#undef DEFINE_BNDC
+#undef DEFINE_DR
+#undef DEFINE_GPR_PSEUDO_32
+#undef DEFINE_GPR_PSEUDO_16
+#undef DEFINE_GPR_PSEUDO_8H
+#undef DEFINE_GPR_PSEUDO_8L
+
+#endif // DECLARE_REGISTER_INFOS_X86_64_STRUCT
+
+#ifdef UPDATE_REGISTER_INFOS_I386_STRUCT_WITH_X86_64_OFFSETS
+
+#define UPDATE_GPR_INFO(reg, reg64) \
+ do { \
+ g_register_infos[lldb_##reg##_i386].byte_offset = GPR_OFFSET(reg64); \
+ } while (false);
+
+#define UPDATE_GPR_INFO_8H(reg, reg64) \
+ do { \
+ g_register_infos[lldb_##reg##_i386].byte_offset = GPR_OFFSET(reg64) + 1; \
+ } while (false);
+
+#define UPDATE_FPR_INFO(reg, reg64) \
+ do { \
+ g_register_infos[lldb_##reg##_i386].byte_offset = FPR_OFFSET(reg64); \
+ } while (false);
+
+#define UPDATE_FP_INFO(reg, i) \
+ do { \
+ g_register_infos[lldb_##reg##i##_i386].byte_offset = FPR_OFFSET(stmm[i]); \
+ } while (false);
+
+#define UPDATE_XMM_INFO(reg, i) \
+ do { \
+ g_register_infos[lldb_##reg##i##_i386].byte_offset = FPR_OFFSET(reg[i]); \
+ } while (false);
+
+#define UPDATE_YMM_INFO(reg, i) \
+ do { \
+ g_register_infos[lldb_##reg##i##_i386].byte_offset = YMM_OFFSET(i); \
+ } while (false);
+
+#define UPDATE_DR_INFO(reg_index) \
+ do { \
+ g_register_infos[lldb_dr##reg_index##_i386].byte_offset = \
+ DR_OFFSET(reg_index); \
+ } while (false);
+
+// Update the register offsets
+UPDATE_GPR_INFO(eax, rax);
+UPDATE_GPR_INFO(ebx, rbx);
+UPDATE_GPR_INFO(ecx, rcx);
+UPDATE_GPR_INFO(edx, rdx);
+UPDATE_GPR_INFO(edi, rdi);
+UPDATE_GPR_INFO(esi, rsi);
+UPDATE_GPR_INFO(ebp, rbp);
+UPDATE_GPR_INFO(esp, rsp);
+UPDATE_GPR_INFO(eip, rip);
+UPDATE_GPR_INFO(eflags, rflags);
+UPDATE_GPR_INFO(cs, cs);
+UPDATE_GPR_INFO(fs, fs);
+UPDATE_GPR_INFO(gs, gs);
+UPDATE_GPR_INFO(ss, ss);
+UPDATE_GPR_INFO(ds, ds);
+UPDATE_GPR_INFO(es, es);
+
+UPDATE_GPR_INFO(ax, rax);
+UPDATE_GPR_INFO(bx, rbx);
+UPDATE_GPR_INFO(cx, rcx);
+UPDATE_GPR_INFO(dx, rdx);
+UPDATE_GPR_INFO(di, rdi);
+UPDATE_GPR_INFO(si, rsi);
+UPDATE_GPR_INFO(bp, rbp);
+UPDATE_GPR_INFO(sp, rsp);
+UPDATE_GPR_INFO_8H(ah, rax);
+UPDATE_GPR_INFO_8H(bh, rbx);
+UPDATE_GPR_INFO_8H(ch, rcx);
+UPDATE_GPR_INFO_8H(dh, rdx);
+UPDATE_GPR_INFO(al, rax);
+UPDATE_GPR_INFO(bl, rbx);
+UPDATE_GPR_INFO(cl, rcx);
+UPDATE_GPR_INFO(dl, rdx);
+
+UPDATE_FPR_INFO(fctrl, fctrl);
+UPDATE_FPR_INFO(fstat, fstat);
+UPDATE_FPR_INFO(ftag, ftag);
+UPDATE_FPR_INFO(fop, fop);
+UPDATE_FPR_INFO(fiseg, ptr.i386_.fiseg);
+UPDATE_FPR_INFO(fioff, ptr.i386_.fioff);
+UPDATE_FPR_INFO(fooff, ptr.i386_.fooff);
+UPDATE_FPR_INFO(foseg, ptr.i386_.foseg);
+UPDATE_FPR_INFO(mxcsr, mxcsr);
+UPDATE_FPR_INFO(mxcsrmask, mxcsrmask);
+
+UPDATE_FP_INFO(st, 0);
+UPDATE_FP_INFO(st, 1);
+UPDATE_FP_INFO(st, 2);
+UPDATE_FP_INFO(st, 3);
+UPDATE_FP_INFO(st, 4);
+UPDATE_FP_INFO(st, 5);
+UPDATE_FP_INFO(st, 6);
+UPDATE_FP_INFO(st, 7);
+UPDATE_FP_INFO(mm, 0);
+UPDATE_FP_INFO(mm, 1);
+UPDATE_FP_INFO(mm, 2);
+UPDATE_FP_INFO(mm, 3);
+UPDATE_FP_INFO(mm, 4);
+UPDATE_FP_INFO(mm, 5);
+UPDATE_FP_INFO(mm, 6);
+UPDATE_FP_INFO(mm, 7);
+
+UPDATE_XMM_INFO(xmm, 0);
+UPDATE_XMM_INFO(xmm, 1);
+UPDATE_XMM_INFO(xmm, 2);
+UPDATE_XMM_INFO(xmm, 3);
+UPDATE_XMM_INFO(xmm, 4);
+UPDATE_XMM_INFO(xmm, 5);
+UPDATE_XMM_INFO(xmm, 6);
+UPDATE_XMM_INFO(xmm, 7);
+
+UPDATE_YMM_INFO(ymm, 0);
+UPDATE_YMM_INFO(ymm, 1);
+UPDATE_YMM_INFO(ymm, 2);
+UPDATE_YMM_INFO(ymm, 3);
+UPDATE_YMM_INFO(ymm, 4);
+UPDATE_YMM_INFO(ymm, 5);
+UPDATE_YMM_INFO(ymm, 6);
+UPDATE_YMM_INFO(ymm, 7);
+
+UPDATE_DR_INFO(0);
+UPDATE_DR_INFO(1);
+UPDATE_DR_INFO(2);
+UPDATE_DR_INFO(3);
+UPDATE_DR_INFO(4);
+UPDATE_DR_INFO(5);
+UPDATE_DR_INFO(6);
+UPDATE_DR_INFO(7);
+
+#undef UPDATE_GPR_INFO
+#undef UPDATE_GPR_INFO_8H
+#undef UPDATE_FPR_INFO
+#undef UPDATE_FP_INFO
+#undef UPDATE_XMM_INFO
+#undef UPDATE_YMM_INFO
+#undef UPDATE_DR_INFO
+
+#endif // UPDATE_REGISTER_INFOS_I386_STRUCT_WITH_X86_64_OFFSETS
+
+#undef GPR_OFFSET
+#undef FPR_OFFSET
+#undef YMM_OFFSET
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp
new file mode 100644
index 000000000000..588015a51ef1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp
@@ -0,0 +1,624 @@
+//===-- StopInfoMachException.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 "StopInfoMachException.h"
+
+
+#if defined(__APPLE__)
+// Needed for the EXC_RESOURCE interpretation macros
+#include <kern/exc_resource.h>
+#endif
+
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+const char *StopInfoMachException::GetDescription() {
+ if (m_description.empty() && m_value != 0) {
+ ExecutionContext exe_ctx(m_thread_wp.lock());
+ Target *target = exe_ctx.GetTargetPtr();
+ const llvm::Triple::ArchType cpu =
+ target ? target->GetArchitecture().GetMachine()
+ : llvm::Triple::UnknownArch;
+
+ const char *exc_desc = nullptr;
+ const char *code_label = "code";
+ const char *code_desc = nullptr;
+ const char *subcode_label = "subcode";
+ const char *subcode_desc = nullptr;
+
+#if defined(__APPLE__)
+ char code_desc_buf[32];
+ char subcode_desc_buf[32];
+#endif
+
+ switch (m_value) {
+ case 1: // EXC_BAD_ACCESS
+ exc_desc = "EXC_BAD_ACCESS";
+ subcode_label = "address";
+ switch (cpu) {
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ switch (m_exc_code) {
+ case 0xd:
+ code_desc = "EXC_I386_GPFLT";
+ m_exc_data_count = 1;
+ break;
+ }
+ break;
+ case llvm::Triple::arm:
+ case llvm::Triple::thumb:
+ switch (m_exc_code) {
+ case 0x101:
+ code_desc = "EXC_ARM_DA_ALIGN";
+ break;
+ case 0x102:
+ code_desc = "EXC_ARM_DA_DEBUG";
+ break;
+ }
+ break;
+
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ switch (m_exc_code) {
+ case 0x101:
+ code_desc = "EXC_PPC_VM_PROT_READ";
+ break;
+ case 0x102:
+ code_desc = "EXC_PPC_BADSPACE";
+ break;
+ case 0x103:
+ code_desc = "EXC_PPC_UNALIGNED";
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case 2: // EXC_BAD_INSTRUCTION
+ exc_desc = "EXC_BAD_INSTRUCTION";
+ switch (cpu) {
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ if (m_exc_code == 1)
+ code_desc = "EXC_I386_INVOP";
+ break;
+
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ switch (m_exc_code) {
+ case 1:
+ code_desc = "EXC_PPC_INVALID_SYSCALL";
+ break;
+ case 2:
+ code_desc = "EXC_PPC_UNIPL_INST";
+ break;
+ case 3:
+ code_desc = "EXC_PPC_PRIVINST";
+ break;
+ case 4:
+ code_desc = "EXC_PPC_PRIVREG";
+ break;
+ case 5:
+ code_desc = "EXC_PPC_TRACE";
+ break;
+ case 6:
+ code_desc = "EXC_PPC_PERFMON";
+ break;
+ }
+ break;
+
+ case llvm::Triple::arm:
+ case llvm::Triple::thumb:
+ if (m_exc_code == 1)
+ code_desc = "EXC_ARM_UNDEFINED";
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case 3: // EXC_ARITHMETIC
+ exc_desc = "EXC_ARITHMETIC";
+ switch (cpu) {
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ switch (m_exc_code) {
+ case 1:
+ code_desc = "EXC_I386_DIV";
+ break;
+ case 2:
+ code_desc = "EXC_I386_INTO";
+ break;
+ case 3:
+ code_desc = "EXC_I386_NOEXT";
+ break;
+ case 4:
+ code_desc = "EXC_I386_EXTOVR";
+ break;
+ case 5:
+ code_desc = "EXC_I386_EXTERR";
+ break;
+ case 6:
+ code_desc = "EXC_I386_EMERR";
+ break;
+ case 7:
+ code_desc = "EXC_I386_BOUND";
+ break;
+ case 8:
+ code_desc = "EXC_I386_SSEEXTERR";
+ break;
+ }
+ break;
+
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ switch (m_exc_code) {
+ case 1:
+ code_desc = "EXC_PPC_OVERFLOW";
+ break;
+ case 2:
+ code_desc = "EXC_PPC_ZERO_DIVIDE";
+ break;
+ case 3:
+ code_desc = "EXC_PPC_FLT_INEXACT";
+ break;
+ case 4:
+ code_desc = "EXC_PPC_FLT_ZERO_DIVIDE";
+ break;
+ case 5:
+ code_desc = "EXC_PPC_FLT_UNDERFLOW";
+ break;
+ case 6:
+ code_desc = "EXC_PPC_FLT_OVERFLOW";
+ break;
+ case 7:
+ code_desc = "EXC_PPC_FLT_NOT_A_NUMBER";
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case 4: // EXC_EMULATION
+ exc_desc = "EXC_EMULATION";
+ break;
+
+ case 5: // EXC_SOFTWARE
+ exc_desc = "EXC_SOFTWARE";
+ if (m_exc_code == 0x10003) {
+ subcode_desc = "EXC_SOFT_SIGNAL";
+ subcode_label = "signo";
+ }
+ break;
+
+ case 6: // EXC_BREAKPOINT
+ {
+ exc_desc = "EXC_BREAKPOINT";
+ switch (cpu) {
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ switch (m_exc_code) {
+ case 1:
+ code_desc = "EXC_I386_SGL";
+ break;
+ case 2:
+ code_desc = "EXC_I386_BPT";
+ break;
+ }
+ break;
+
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ switch (m_exc_code) {
+ case 1:
+ code_desc = "EXC_PPC_BREAKPOINT";
+ break;
+ }
+ break;
+
+ case llvm::Triple::arm:
+ case llvm::Triple::thumb:
+ switch (m_exc_code) {
+ case 0x101:
+ code_desc = "EXC_ARM_DA_ALIGN";
+ break;
+ case 0x102:
+ code_desc = "EXC_ARM_DA_DEBUG";
+ break;
+ case 1:
+ code_desc = "EXC_ARM_BREAKPOINT";
+ break;
+ // FIXME temporary workaround, exc_code 0 does not really mean
+ // EXC_ARM_BREAKPOINT
+ case 0:
+ code_desc = "EXC_ARM_BREAKPOINT";
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ } break;
+
+ case 7:
+ exc_desc = "EXC_SYSCALL";
+ break;
+
+ case 8:
+ exc_desc = "EXC_MACH_SYSCALL";
+ break;
+
+ case 9:
+ exc_desc = "EXC_RPC_ALERT";
+ break;
+
+ case 10:
+ exc_desc = "EXC_CRASH";
+ break;
+ case 11:
+ exc_desc = "EXC_RESOURCE";
+#if defined(__APPLE__)
+ {
+ int resource_type = EXC_RESOURCE_DECODE_RESOURCE_TYPE(m_exc_code);
+
+ code_label = "limit";
+ code_desc = code_desc_buf;
+ subcode_label = "observed";
+ subcode_desc = subcode_desc_buf;
+
+ switch (resource_type) {
+ case RESOURCE_TYPE_CPU:
+ exc_desc = "EXC_RESOURCE RESOURCE_TYPE_CPU";
+ snprintf(code_desc_buf, sizeof(code_desc_buf), "%d%%",
+ (int)EXC_RESOURCE_CPUMONITOR_DECODE_PERCENTAGE(m_exc_code));
+ snprintf(subcode_desc_buf, sizeof(subcode_desc_buf), "%d%%",
+ (int)EXC_RESOURCE_CPUMONITOR_DECODE_PERCENTAGE_OBSERVED(m_exc_subcode));
+ break;
+ case RESOURCE_TYPE_WAKEUPS:
+ exc_desc = "EXC_RESOURCE RESOURCE_TYPE_WAKEUPS";
+ snprintf(code_desc_buf, sizeof(code_desc_buf), "%d w/s",
+ (int)EXC_RESOURCE_CPUMONITOR_DECODE_WAKEUPS_PERMITTED(m_exc_code));
+ snprintf(subcode_desc_buf, sizeof(subcode_desc_buf), "%d w/s",
+ (int)EXC_RESOURCE_CPUMONITOR_DECODE_WAKEUPS_OBSERVED(m_exc_subcode));
+ break;
+ case RESOURCE_TYPE_MEMORY:
+ exc_desc = "EXC_RESOURCE RESOURCE_TYPE_MEMORY";
+ snprintf(code_desc_buf, sizeof(code_desc_buf), "%d MB",
+ (int)EXC_RESOURCE_HWM_DECODE_LIMIT(m_exc_code));
+ subcode_desc = nullptr;
+ subcode_label = "unused";
+ break;
+ case RESOURCE_TYPE_IO:
+ exc_desc = "EXC_RESOURCE RESOURCE_TYPE_IO";
+ snprintf(code_desc_buf, sizeof(code_desc_buf), "%d MB",
+ (int)EXC_RESOURCE_IO_DECODE_LIMIT(m_exc_code));
+ snprintf(subcode_desc_buf, sizeof(subcode_desc_buf), "%d MB",
+ (int)EXC_RESOURCE_IO_OBSERVED(m_exc_subcode));;
+ break;
+ }
+ }
+#endif
+ break;
+ case 12:
+ exc_desc = "EXC_GUARD";
+ break;
+ }
+
+ StreamString strm;
+
+ if (exc_desc)
+ strm.PutCString(exc_desc);
+ else
+ strm.Printf("EXC_??? (%" PRIu64 ")", m_value);
+
+ if (m_exc_data_count >= 1) {
+ if (code_desc)
+ strm.Printf(" (%s=%s", code_label, code_desc);
+ else
+ strm.Printf(" (%s=%" PRIu64, code_label, m_exc_code);
+ }
+
+ if (m_exc_data_count >= 2) {
+ if (subcode_desc)
+ strm.Printf(", %s=%s", subcode_label, subcode_desc);
+ else
+ strm.Printf(", %s=0x%" PRIx64, subcode_label, m_exc_subcode);
+ }
+
+ if (m_exc_data_count > 0)
+ strm.PutChar(')');
+
+ m_description = strm.GetString();
+ }
+ return m_description.c_str();
+}
+
+StopInfoSP StopInfoMachException::CreateStopReasonWithMachException(
+ Thread &thread, uint32_t exc_type, uint32_t exc_data_count,
+ uint64_t exc_code, uint64_t exc_sub_code, uint64_t exc_sub_sub_code,
+ bool pc_already_adjusted, bool adjust_pc_if_needed) {
+ if (exc_type != 0) {
+ uint32_t pc_decrement = 0;
+ ExecutionContext exe_ctx(thread.shared_from_this());
+ Target *target = exe_ctx.GetTargetPtr();
+ const llvm::Triple::ArchType cpu =
+ target ? target->GetArchitecture().GetMachine()
+ : llvm::Triple::UnknownArch;
+
+ switch (exc_type) {
+ case 1: // EXC_BAD_ACCESS
+ break;
+
+ case 2: // EXC_BAD_INSTRUCTION
+ switch (cpu) {
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ switch (exc_code) {
+ case 1: // EXC_PPC_INVALID_SYSCALL
+ case 2: // EXC_PPC_UNIPL_INST
+ case 3: // EXC_PPC_PRIVINST
+ case 4: // EXC_PPC_PRIVREG
+ break;
+ case 5: // EXC_PPC_TRACE
+ return StopInfo::CreateStopReasonToTrace(thread);
+ case 6: // EXC_PPC_PERFMON
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case 3: // EXC_ARITHMETIC
+ case 4: // EXC_EMULATION
+ break;
+
+ case 5: // EXC_SOFTWARE
+ if (exc_code == 0x10003) // EXC_SOFT_SIGNAL
+ {
+ if (exc_sub_code == 5) {
+ // On MacOSX, a SIGTRAP can signify that a process has called exec,
+ // so we should check with our dynamic loader to verify.
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp) {
+ DynamicLoader *dynamic_loader = process_sp->GetDynamicLoader();
+ if (dynamic_loader && dynamic_loader->ProcessDidExec()) {
+ // The program was re-exec'ed
+ return StopInfo::CreateStopReasonWithExec(thread);
+ }
+ // if (!process_did_exec)
+ // {
+ // // We have a SIGTRAP, make sure we
+ // didn't exec by checking
+ // // for the PC being at
+ // "_dyld_start"...
+ // lldb::StackFrameSP frame_sp
+ // (thread.GetStackFrameAtIndex(0));
+ // if (frame_sp)
+ // {
+ // const Symbol *symbol =
+ // frame_sp->GetSymbolContext(eSymbolContextSymbol).symbol;
+ // if (symbol)
+ // {
+ // if (symbol->GetName() ==
+ // ConstString("_dyld_start"))
+ // process_did_exec = true;
+ // }
+ // }
+ // }
+ }
+ }
+ return StopInfo::CreateStopReasonWithSignal(thread, exc_sub_code);
+ }
+ break;
+
+ case 6: // EXC_BREAKPOINT
+ {
+ bool is_actual_breakpoint = false;
+ bool is_trace_if_actual_breakpoint_missing = false;
+ switch (cpu) {
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ if (exc_code == 1) // EXC_I386_SGL
+ {
+ if (!exc_sub_code) {
+ // This looks like a plain trap.
+ // Have to check if there is a breakpoint here as well. When you
+ // single-step onto a trap, the single step stops you not to trap.
+ // Since we also do that check below, let's just use that logic.
+ is_actual_breakpoint = true;
+ is_trace_if_actual_breakpoint_missing = true;
+ } else {
+
+ // It's a watchpoint, then.
+ // The exc_sub_code indicates the data break address.
+ lldb::WatchpointSP wp_sp;
+ if (target)
+ wp_sp = target->GetWatchpointList().FindByAddress(
+ (lldb::addr_t)exc_sub_code);
+ if (wp_sp && wp_sp->IsEnabled()) {
+ // Debugserver may piggyback the hardware index of the fired
+ // watchpoint in the exception data. Set the hardware index if
+ // that's the case.
+ if (exc_data_count >= 3)
+ wp_sp->SetHardwareIndex((uint32_t)exc_sub_sub_code);
+ return StopInfo::CreateStopReasonWithWatchpointID(thread,
+ wp_sp->GetID());
+ }
+ }
+ } else if (exc_code == 2 || // EXC_I386_BPT
+ exc_code == 3) // EXC_I386_BPTFLT
+ {
+ // KDP returns EXC_I386_BPTFLT for trace breakpoints
+ if (exc_code == 3)
+ is_trace_if_actual_breakpoint_missing = true;
+
+ is_actual_breakpoint = true;
+ if (!pc_already_adjusted)
+ pc_decrement = 1;
+ }
+ break;
+
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ is_actual_breakpoint = exc_code == 1; // EXC_PPC_BREAKPOINT
+ break;
+
+ case llvm::Triple::arm:
+ case llvm::Triple::thumb:
+ if (exc_code == 0x102) // EXC_ARM_DA_DEBUG
+ {
+ // It's a watchpoint, then, if the exc_sub_code indicates a
+ // known/enabled data break address from our watchpoint list.
+ lldb::WatchpointSP wp_sp;
+ if (target)
+ wp_sp = target->GetWatchpointList().FindByAddress(
+ (lldb::addr_t)exc_sub_code);
+ if (wp_sp && wp_sp->IsEnabled()) {
+ // Debugserver may piggyback the hardware index of the fired
+ // watchpoint in the exception data. Set the hardware index if
+ // that's the case.
+ if (exc_data_count >= 3)
+ wp_sp->SetHardwareIndex((uint32_t)exc_sub_sub_code);
+ return StopInfo::CreateStopReasonWithWatchpointID(thread,
+ wp_sp->GetID());
+ } else {
+ is_actual_breakpoint = true;
+ is_trace_if_actual_breakpoint_missing = true;
+ }
+ } else if (exc_code == 1) // EXC_ARM_BREAKPOINT
+ {
+ is_actual_breakpoint = true;
+ is_trace_if_actual_breakpoint_missing = true;
+ } else if (exc_code == 0) // FIXME not EXC_ARM_BREAKPOINT but a kernel
+ // is currently returning this so accept it
+ // as indicating a breakpoint until the
+ // kernel is fixed
+ {
+ is_actual_breakpoint = true;
+ is_trace_if_actual_breakpoint_missing = true;
+ }
+ break;
+
+ case llvm::Triple::aarch64: {
+ if (exc_code == 1 && exc_sub_code == 0) // EXC_ARM_BREAKPOINT
+ {
+ // This is hit when we single instruction step aka MDSCR_EL1 SS bit 0
+ // is set
+ is_actual_breakpoint = false;
+ is_trace_if_actual_breakpoint_missing = true;
+ }
+ if (exc_code == 0x102) // EXC_ARM_DA_DEBUG
+ {
+ // It's a watchpoint, then, if the exc_sub_code indicates a
+ // known/enabled data break address from our watchpoint list.
+ lldb::WatchpointSP wp_sp;
+ if (target)
+ wp_sp = target->GetWatchpointList().FindByAddress(
+ (lldb::addr_t)exc_sub_code);
+ if (wp_sp && wp_sp->IsEnabled()) {
+ // Debugserver may piggyback the hardware index of the fired
+ // watchpoint in the exception data. Set the hardware index if
+ // that's the case.
+ if (exc_data_count >= 3)
+ wp_sp->SetHardwareIndex((uint32_t)exc_sub_sub_code);
+ return StopInfo::CreateStopReasonWithWatchpointID(thread,
+ wp_sp->GetID());
+ }
+ // EXC_ARM_DA_DEBUG seems to be reused for EXC_BREAKPOINT as well as
+ // EXC_BAD_ACCESS
+ if (thread.GetTemporaryResumeState() == eStateStepping)
+ return StopInfo::CreateStopReasonToTrace(thread);
+ }
+ // It looks like exc_sub_code has the 4 bytes of the instruction that
+ // triggered the exception, i.e. our breakpoint opcode
+ is_actual_breakpoint = exc_code == 1;
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (is_actual_breakpoint) {
+ RegisterContextSP reg_ctx_sp(thread.GetRegisterContext());
+ addr_t pc = reg_ctx_sp->GetPC() - pc_decrement;
+
+ ProcessSP process_sp(thread.CalculateProcess());
+
+ lldb::BreakpointSiteSP bp_site_sp;
+ if (process_sp)
+ bp_site_sp = process_sp->GetBreakpointSiteList().FindByAddress(pc);
+ if (bp_site_sp && bp_site_sp->IsEnabled()) {
+ // Update the PC if we were asked to do so, but only do so if we find
+ // a breakpoint that we know about cause this could be a trap
+ // instruction in the code
+ if (pc_decrement > 0 && adjust_pc_if_needed)
+ reg_ctx_sp->SetPC(pc);
+
+ // If the breakpoint is for this thread, then we'll report the hit,
+ // but if it is for another thread, we can just report no reason. We
+ // don't need to worry about stepping over the breakpoint here, that
+ // will be taken care of when the thread resumes and notices that
+ // there's a breakpoint under the pc. If we have an operating system
+ // plug-in, we might have set a thread specific breakpoint using the
+ // operating system thread ID, so we can't make any assumptions about
+ // the thread ID so we must always report the breakpoint regardless
+ // of the thread.
+ if (bp_site_sp->ValidForThisThread(&thread) ||
+ thread.GetProcess()->GetOperatingSystem() != nullptr)
+ return StopInfo::CreateStopReasonWithBreakpointSiteID(
+ thread, bp_site_sp->GetID());
+ else if (is_trace_if_actual_breakpoint_missing)
+ return StopInfo::CreateStopReasonToTrace(thread);
+ else
+ return StopInfoSP();
+ }
+
+ // Don't call this a trace if we weren't single stepping this thread.
+ if (is_trace_if_actual_breakpoint_missing &&
+ thread.GetTemporaryResumeState() == eStateStepping) {
+ return StopInfo::CreateStopReasonToTrace(thread);
+ }
+ }
+ } break;
+
+ case 7: // EXC_SYSCALL
+ case 8: // EXC_MACH_SYSCALL
+ case 9: // EXC_RPC_ALERT
+ case 10: // EXC_CRASH
+ break;
+ }
+
+ return StopInfoSP(new StopInfoMachException(
+ thread, exc_type, exc_data_count, exc_code, exc_sub_code));
+ }
+ return StopInfoSP();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/StopInfoMachException.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/StopInfoMachException.h
new file mode 100644
index 000000000000..74c05812ab00
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/StopInfoMachException.h
@@ -0,0 +1,51 @@
+//===-- StopInfoMachException.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 liblldb_StopInfoMachException_h_
+#define liblldb_StopInfoMachException_h_
+
+#include <string>
+
+#include "lldb/Target/StopInfo.h"
+
+namespace lldb_private {
+
+class StopInfoMachException : public StopInfo {
+public:
+ // Constructors and Destructors
+ StopInfoMachException(Thread &thread, uint32_t exc_type,
+ uint32_t exc_data_count, uint64_t exc_code,
+ uint64_t exc_subcode)
+ : StopInfo(thread, exc_type), m_exc_data_count(exc_data_count),
+ m_exc_code(exc_code), m_exc_subcode(exc_subcode) {}
+
+ ~StopInfoMachException() override = default;
+
+ lldb::StopReason GetStopReason() const override {
+ return lldb::eStopReasonException;
+ }
+
+ const char *GetDescription() override;
+
+ // Since some mach exceptions will be reported as breakpoints, signals,
+ // or trace, we use this static accessor which will translate the mach
+ // exception into the correct StopInfo.
+ static lldb::StopInfoSP CreateStopReasonWithMachException(
+ Thread &thread, uint32_t exc_type, uint32_t exc_data_count,
+ uint64_t exc_code, uint64_t exc_sub_code, uint64_t exc_sub_sub_code,
+ bool pc_already_adjusted = true, bool adjust_pc_if_needed = false);
+
+protected:
+ uint32_t m_exc_data_count;
+ uint64_t m_exc_code;
+ uint64_t m_exc_subcode;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_StopInfoMachException_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ThreadMemory.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ThreadMemory.cpp
new file mode 100644
index 000000000000..80b04bb14f77
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ThreadMemory.cpp
@@ -0,0 +1,103 @@
+//===-- ThreadMemory.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 "Plugins/Process/Utility/ThreadMemory.h"
+
+#include "Plugins/Process/Utility/RegisterContextThreadMemory.h"
+#include "lldb/Target/OperatingSystem.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Unwind.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+ThreadMemory::ThreadMemory(Process &process, tid_t tid,
+ const ValueObjectSP &thread_info_valobj_sp)
+ : Thread(process, tid), m_backing_thread_sp(),
+ m_thread_info_valobj_sp(thread_info_valobj_sp), m_name(), m_queue() {}
+
+ThreadMemory::ThreadMemory(Process &process, lldb::tid_t tid,
+ llvm::StringRef name, llvm::StringRef queue,
+ lldb::addr_t register_data_addr)
+ : Thread(process, tid), m_backing_thread_sp(), m_thread_info_valobj_sp(),
+ m_name(name), m_queue(queue), m_register_data_addr(register_data_addr) {}
+
+ThreadMemory::~ThreadMemory() { DestroyThread(); }
+
+void ThreadMemory::WillResume(StateType resume_state) {
+ if (m_backing_thread_sp)
+ m_backing_thread_sp->WillResume(resume_state);
+}
+
+void ThreadMemory::ClearStackFrames() {
+ if (m_backing_thread_sp)
+ m_backing_thread_sp->ClearStackFrames();
+ Thread::ClearStackFrames();
+}
+
+RegisterContextSP ThreadMemory::GetRegisterContext() {
+ if (!m_reg_context_sp)
+ m_reg_context_sp = std::make_shared<RegisterContextThreadMemory>(
+ *this, m_register_data_addr);
+ return m_reg_context_sp;
+}
+
+RegisterContextSP
+ThreadMemory::CreateRegisterContextForFrame(StackFrame *frame) {
+ RegisterContextSP reg_ctx_sp;
+ uint32_t concrete_frame_idx = 0;
+
+ if (frame)
+ concrete_frame_idx = frame->GetConcreteFrameIndex();
+
+ if (concrete_frame_idx == 0) {
+ reg_ctx_sp = GetRegisterContext();
+ } else {
+ Unwind *unwinder = GetUnwinder();
+ if (unwinder != nullptr)
+ reg_ctx_sp = unwinder->CreateRegisterContextForFrame(frame);
+ }
+ return reg_ctx_sp;
+}
+
+bool ThreadMemory::CalculateStopInfo() {
+ if (m_backing_thread_sp) {
+ lldb::StopInfoSP backing_stop_info_sp(
+ m_backing_thread_sp->GetPrivateStopInfo());
+ if (backing_stop_info_sp &&
+ backing_stop_info_sp->IsValidForOperatingSystemThread(*this)) {
+ backing_stop_info_sp->SetThread(shared_from_this());
+ SetStopInfo(backing_stop_info_sp);
+ return true;
+ }
+ } else {
+ ProcessSP process_sp(GetProcess());
+
+ if (process_sp) {
+ OperatingSystem *os = process_sp->GetOperatingSystem();
+ if (os) {
+ SetStopInfo(os->CreateThreadStopReason(this));
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void ThreadMemory::RefreshStateAfterStop() {
+ if (m_backing_thread_sp)
+ return m_backing_thread_sp->RefreshStateAfterStop();
+
+ if (m_reg_context_sp)
+ m_reg_context_sp->InvalidateAllRegisters();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ThreadMemory.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ThreadMemory.h
new file mode 100644
index 000000000000..85bc1451e4a0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ThreadMemory.h
@@ -0,0 +1,106 @@
+//===-- ThreadMemory.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 liblldb_ThreadMemory_h_
+#define liblldb_ThreadMemory_h_
+
+#include <string>
+
+#include "lldb/Target/Thread.h"
+
+class ThreadMemory : public lldb_private::Thread {
+public:
+ ThreadMemory(lldb_private::Process &process, lldb::tid_t tid,
+ const lldb::ValueObjectSP &thread_info_valobj_sp);
+
+ ThreadMemory(lldb_private::Process &process, lldb::tid_t tid,
+ llvm::StringRef name, llvm::StringRef queue,
+ lldb::addr_t register_data_addr);
+
+ ~ThreadMemory() override;
+
+ lldb::RegisterContextSP GetRegisterContext() override;
+
+ lldb::RegisterContextSP
+ CreateRegisterContextForFrame(lldb_private::StackFrame *frame) override;
+
+ bool CalculateStopInfo() override;
+
+ const char *GetInfo() override {
+ if (m_backing_thread_sp)
+ m_backing_thread_sp->GetInfo();
+ return nullptr;
+ }
+
+ const char *GetName() override {
+ if (!m_name.empty())
+ return m_name.c_str();
+ if (m_backing_thread_sp)
+ m_backing_thread_sp->GetName();
+ return nullptr;
+ }
+
+ const char *GetQueueName() override {
+ if (!m_queue.empty())
+ return m_queue.c_str();
+ if (m_backing_thread_sp)
+ m_backing_thread_sp->GetQueueName();
+ return nullptr;
+ }
+
+ void WillResume(lldb::StateType resume_state) override;
+
+ void DidResume() override {
+ if (m_backing_thread_sp)
+ m_backing_thread_sp->DidResume();
+ }
+
+ lldb::user_id_t GetProtocolID() const override {
+ if (m_backing_thread_sp)
+ return m_backing_thread_sp->GetProtocolID();
+ return Thread::GetProtocolID();
+ }
+
+ void RefreshStateAfterStop() override;
+
+ lldb::ValueObjectSP &GetValueObject() { return m_thread_info_valobj_sp; }
+
+ void ClearStackFrames() override;
+
+ void ClearBackingThread() override { m_backing_thread_sp.reset(); }
+
+ bool SetBackingThread(const lldb::ThreadSP &thread_sp) override {
+ // printf ("Thread 0x%llx is being backed by thread 0x%llx\n", GetID(),
+ // thread_sp->GetID());
+ m_backing_thread_sp = thread_sp;
+ return (bool)thread_sp;
+ }
+
+ lldb::ThreadSP GetBackingThread() const override {
+ return m_backing_thread_sp;
+ }
+
+protected:
+ bool IsOperatingSystemPluginThread() const override { return true; }
+
+ // If this memory thread is actually represented by a thread from the
+ // lldb_private::Process subclass, then fill in the thread here and
+ // all APIs will be routed through this thread object. If m_backing_thread_sp
+ // is empty, then this thread is simply in memory with no representation
+ // through the process plug-in.
+ lldb::ThreadSP m_backing_thread_sp;
+ lldb::ValueObjectSP m_thread_info_valobj_sp;
+ std::string m_name;
+ std::string m_queue;
+ lldb::addr_t m_register_data_addr;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ThreadMemory);
+};
+
+#endif // liblldb_ThreadMemory_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindLLDB.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindLLDB.cpp
new file mode 100644
index 000000000000..38209fb24948
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindLLDB.cpp
@@ -0,0 +1,505 @@
+//===-- UnwindLLDB.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 "lldb/Core/Module.h"
+#include "lldb/Symbol/FuncUnwinders.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Log.h"
+
+#include "RegisterContextLLDB.h"
+#include "UnwindLLDB.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+UnwindLLDB::UnwindLLDB(Thread &thread)
+ : Unwind(thread), m_frames(), m_unwind_complete(false),
+ m_user_supplied_trap_handler_functions() {
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp) {
+ Args args;
+ process_sp->GetTarget().GetUserSpecifiedTrapHandlerNames(args);
+ size_t count = args.GetArgumentCount();
+ for (size_t i = 0; i < count; i++) {
+ const char *func_name = args.GetArgumentAtIndex(i);
+ m_user_supplied_trap_handler_functions.push_back(ConstString(func_name));
+ }
+ }
+}
+
+uint32_t UnwindLLDB::DoGetFrameCount() {
+ if (!m_unwind_complete) {
+//#define DEBUG_FRAME_SPEED 1
+#if DEBUG_FRAME_SPEED
+#define FRAME_COUNT 10000
+ using namespace std::chrono;
+ auto time_value = steady_clock::now();
+#endif
+ if (!AddFirstFrame())
+ return 0;
+
+ ProcessSP process_sp(m_thread.GetProcess());
+ ABI *abi = process_sp ? process_sp->GetABI().get() : nullptr;
+
+ while (AddOneMoreFrame(abi)) {
+#if DEBUG_FRAME_SPEED
+ if ((m_frames.size() % FRAME_COUNT) == 0) {
+ const auto now = steady_clock::now();
+ const auto delta_t = now - time_value;
+ printf("%u frames in %.9f ms (%g frames/sec)\n", FRAME_COUNT,
+ duration<double, std::milli>(delta_t).count(),
+ (float)FRAME_COUNT / duration<double>(delta_t).count());
+ time_value = now;
+ }
+#endif
+ }
+ }
+ return m_frames.size();
+}
+
+bool UnwindLLDB::AddFirstFrame() {
+ if (m_frames.size() > 0)
+ return true;
+
+ ProcessSP process_sp(m_thread.GetProcess());
+ ABI *abi = process_sp ? process_sp->GetABI().get() : nullptr;
+
+ // First, set up the 0th (initial) frame
+ CursorSP first_cursor_sp(new Cursor());
+ RegisterContextLLDBSP reg_ctx_sp(new RegisterContextLLDB(
+ m_thread, RegisterContextLLDBSP(), first_cursor_sp->sctx, 0, *this));
+ if (reg_ctx_sp.get() == nullptr)
+ goto unwind_done;
+
+ if (!reg_ctx_sp->IsValid())
+ goto unwind_done;
+
+ if (!reg_ctx_sp->GetCFA(first_cursor_sp->cfa))
+ goto unwind_done;
+
+ if (!reg_ctx_sp->ReadPC(first_cursor_sp->start_pc))
+ goto unwind_done;
+
+ // Everything checks out, so release the auto pointer value and let the
+ // cursor own it in its shared pointer
+ first_cursor_sp->reg_ctx_lldb_sp = reg_ctx_sp;
+ m_frames.push_back(first_cursor_sp);
+
+ // Update the Full Unwind Plan for this frame if not valid
+ UpdateUnwindPlanForFirstFrameIfInvalid(abi);
+
+ return true;
+
+unwind_done:
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+ if (log) {
+ log->Printf("th%d Unwind of this thread is complete.",
+ m_thread.GetIndexID());
+ }
+ m_unwind_complete = true;
+ return false;
+}
+
+UnwindLLDB::CursorSP UnwindLLDB::GetOneMoreFrame(ABI *abi) {
+ assert(m_frames.size() != 0 &&
+ "Get one more frame called with empty frame list");
+
+ // If we've already gotten to the end of the stack, don't bother to try
+ // again...
+ if (m_unwind_complete)
+ return nullptr;
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+
+ CursorSP prev_frame = m_frames.back();
+ uint32_t cur_idx = m_frames.size();
+
+ CursorSP cursor_sp(new Cursor());
+ RegisterContextLLDBSP reg_ctx_sp(new RegisterContextLLDB(
+ m_thread, prev_frame->reg_ctx_lldb_sp, cursor_sp->sctx, cur_idx, *this));
+
+ uint64_t max_stack_depth = m_thread.GetMaxBacktraceDepth();
+
+ // We want to detect an unwind that cycles erroneously and stop backtracing.
+ // Don't want this maximum unwind limit to be too low -- if you have a
+ // backtrace with an "infinitely recursing" bug, it will crash when the stack
+ // blows out and the first 35,000 frames are uninteresting - it's the top
+ // most 5 frames that you actually care about. So you can't just cap the
+ // unwind at 10,000 or something. Realistically anything over around 200,000
+ // is going to blow out the stack space. If we're still unwinding at that
+ // point, we're probably never going to finish.
+ if (cur_idx >= max_stack_depth) {
+ if (log)
+ log->Printf("%*sFrame %d unwound too many frames, assuming unwind has "
+ "gone astray, stopping.",
+ cur_idx < 100 ? cur_idx : 100, "", cur_idx);
+ return nullptr;
+ }
+
+ if (reg_ctx_sp.get() == nullptr) {
+ // If the RegisterContextLLDB has a fallback UnwindPlan, it will switch to
+ // that and return true. Subsequent calls to TryFallbackUnwindPlan() will
+ // return false.
+ if (prev_frame->reg_ctx_lldb_sp->TryFallbackUnwindPlan()) {
+ // TryFallbackUnwindPlan for prev_frame succeeded and updated
+ // reg_ctx_lldb_sp field of prev_frame. However, cfa field of prev_frame
+ // still needs to be updated. Hence updating it.
+ if (!(prev_frame->reg_ctx_lldb_sp->GetCFA(prev_frame->cfa)))
+ return nullptr;
+
+ return GetOneMoreFrame(abi);
+ }
+
+ if (log)
+ log->Printf("%*sFrame %d did not get a RegisterContext, stopping.",
+ cur_idx < 100 ? cur_idx : 100, "", cur_idx);
+ return nullptr;
+ }
+
+ if (!reg_ctx_sp->IsValid()) {
+ // We failed to get a valid RegisterContext. See if the regctx below this
+ // on the stack has a fallback unwind plan it can use. Subsequent calls to
+ // TryFallbackUnwindPlan() will return false.
+ if (prev_frame->reg_ctx_lldb_sp->TryFallbackUnwindPlan()) {
+ // TryFallbackUnwindPlan for prev_frame succeeded and updated
+ // reg_ctx_lldb_sp field of prev_frame. However, cfa field of prev_frame
+ // still needs to be updated. Hence updating it.
+ if (!(prev_frame->reg_ctx_lldb_sp->GetCFA(prev_frame->cfa)))
+ return nullptr;
+
+ return GetOneMoreFrame(abi);
+ }
+
+ if (log)
+ log->Printf("%*sFrame %d invalid RegisterContext for this frame, "
+ "stopping stack walk",
+ cur_idx < 100 ? cur_idx : 100, "", cur_idx);
+ return nullptr;
+ }
+ if (!reg_ctx_sp->GetCFA(cursor_sp->cfa)) {
+ // If the RegisterContextLLDB has a fallback UnwindPlan, it will switch to
+ // that and return true. Subsequent calls to TryFallbackUnwindPlan() will
+ // return false.
+ if (prev_frame->reg_ctx_lldb_sp->TryFallbackUnwindPlan()) {
+ // TryFallbackUnwindPlan for prev_frame succeeded and updated
+ // reg_ctx_lldb_sp field of prev_frame. However, cfa field of prev_frame
+ // still needs to be updated. Hence updating it.
+ if (!(prev_frame->reg_ctx_lldb_sp->GetCFA(prev_frame->cfa)))
+ return nullptr;
+
+ return GetOneMoreFrame(abi);
+ }
+
+ if (log)
+ log->Printf(
+ "%*sFrame %d did not get CFA for this frame, stopping stack walk",
+ cur_idx < 100 ? cur_idx : 100, "", cur_idx);
+ return nullptr;
+ }
+ if (abi && !abi->CallFrameAddressIsValid(cursor_sp->cfa)) {
+ // On Mac OS X, the _sigtramp asynchronous signal trampoline frame may not
+ // have its (constructed) CFA aligned correctly -- don't do the abi
+ // alignment check for these.
+ if (!reg_ctx_sp->IsTrapHandlerFrame()) {
+ // See if we can find a fallback unwind plan for THIS frame. It may be
+ // that the UnwindPlan we're using for THIS frame was bad and gave us a
+ // bad CFA. If that's not it, then see if we can change the UnwindPlan
+ // for the frame below us ("NEXT") -- see if using that other UnwindPlan
+ // gets us a better unwind state.
+ if (!reg_ctx_sp->TryFallbackUnwindPlan() ||
+ !reg_ctx_sp->GetCFA(cursor_sp->cfa) ||
+ !abi->CallFrameAddressIsValid(cursor_sp->cfa)) {
+ if (prev_frame->reg_ctx_lldb_sp->TryFallbackUnwindPlan()) {
+ // TryFallbackUnwindPlan for prev_frame succeeded and updated
+ // reg_ctx_lldb_sp field of prev_frame. However, cfa field of
+ // prev_frame still needs to be updated. Hence updating it.
+ if (!(prev_frame->reg_ctx_lldb_sp->GetCFA(prev_frame->cfa)))
+ return nullptr;
+
+ return GetOneMoreFrame(abi);
+ }
+
+ if (log)
+ log->Printf("%*sFrame %d did not get a valid CFA for this frame, "
+ "stopping stack walk",
+ cur_idx < 100 ? cur_idx : 100, "", cur_idx);
+ return nullptr;
+ } else {
+ if (log)
+ log->Printf("%*sFrame %d had a bad CFA value but we switched the "
+ "UnwindPlan being used and got one that looks more "
+ "realistic.",
+ cur_idx < 100 ? cur_idx : 100, "", cur_idx);
+ }
+ }
+ }
+ if (!reg_ctx_sp->ReadPC(cursor_sp->start_pc)) {
+ // If the RegisterContextLLDB has a fallback UnwindPlan, it will switch to
+ // that and return true. Subsequent calls to TryFallbackUnwindPlan() will
+ // return false.
+ if (prev_frame->reg_ctx_lldb_sp->TryFallbackUnwindPlan()) {
+ // TryFallbackUnwindPlan for prev_frame succeeded and updated
+ // reg_ctx_lldb_sp field of prev_frame. However, cfa field of prev_frame
+ // still needs to be updated. Hence updating it.
+ if (!(prev_frame->reg_ctx_lldb_sp->GetCFA(prev_frame->cfa)))
+ return nullptr;
+
+ return GetOneMoreFrame(abi);
+ }
+
+ if (log)
+ log->Printf(
+ "%*sFrame %d did not get PC for this frame, stopping stack walk",
+ cur_idx < 100 ? cur_idx : 100, "", cur_idx);
+ return nullptr;
+ }
+ if (abi && !abi->CodeAddressIsValid(cursor_sp->start_pc)) {
+ // If the RegisterContextLLDB has a fallback UnwindPlan, it will switch to
+ // that and return true. Subsequent calls to TryFallbackUnwindPlan() will
+ // return false.
+ if (prev_frame->reg_ctx_lldb_sp->TryFallbackUnwindPlan()) {
+ // TryFallbackUnwindPlan for prev_frame succeeded and updated
+ // reg_ctx_lldb_sp field of prev_frame. However, cfa field of prev_frame
+ // still needs to be updated. Hence updating it.
+ if (!(prev_frame->reg_ctx_lldb_sp->GetCFA(prev_frame->cfa)))
+ return nullptr;
+
+ return GetOneMoreFrame(abi);
+ }
+
+ if (log)
+ log->Printf("%*sFrame %d did not get a valid PC, stopping stack walk",
+ cur_idx < 100 ? cur_idx : 100, "", cur_idx);
+ return nullptr;
+ }
+ // Infinite loop where the current cursor is the same as the previous one...
+ if (prev_frame->start_pc == cursor_sp->start_pc &&
+ prev_frame->cfa == cursor_sp->cfa) {
+ if (log)
+ log->Printf("th%d pc of this frame is the same as the previous frame and "
+ "CFAs for both frames are identical -- stopping unwind",
+ m_thread.GetIndexID());
+ return nullptr;
+ }
+
+ cursor_sp->reg_ctx_lldb_sp = reg_ctx_sp;
+ return cursor_sp;
+}
+
+void UnwindLLDB::UpdateUnwindPlanForFirstFrameIfInvalid(ABI *abi) {
+ // This function is called for First Frame only.
+ assert(m_frames.size() == 1 && "No. of cursor frames are not 1");
+
+ bool old_m_unwind_complete = m_unwind_complete;
+ CursorSP old_m_candidate_frame = m_candidate_frame;
+
+ // Try to unwind 2 more frames using the Unwinder. It uses Full UnwindPlan
+ // and if Full UnwindPlan fails, then uses FallBack UnwindPlan. Also update
+ // the cfa of Frame 0 (if required).
+ AddOneMoreFrame(abi);
+
+ // Remove all the frames added by above function as the purpose of using
+ // above function was just to check whether Unwinder of Frame 0 works or not.
+ for (uint32_t i = 1; i < m_frames.size(); i++)
+ m_frames.pop_back();
+
+ // Restore status after calling AddOneMoreFrame
+ m_unwind_complete = old_m_unwind_complete;
+ m_candidate_frame = old_m_candidate_frame;
+ return;
+}
+
+bool UnwindLLDB::AddOneMoreFrame(ABI *abi) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+
+ // Frame zero is a little different
+ if (m_frames.empty())
+ return false;
+
+ // If we've already gotten to the end of the stack, don't bother to try
+ // again...
+ if (m_unwind_complete)
+ return false;
+
+ CursorSP new_frame = m_candidate_frame;
+ if (new_frame == nullptr)
+ new_frame = GetOneMoreFrame(abi);
+
+ if (new_frame == nullptr) {
+ if (log)
+ log->Printf("th%d Unwind of this thread is complete.",
+ m_thread.GetIndexID());
+ m_unwind_complete = true;
+ return false;
+ }
+
+ m_frames.push_back(new_frame);
+
+ // If we can get one more frame further then accept that we get back a
+ // correct frame.
+ m_candidate_frame = GetOneMoreFrame(abi);
+ if (m_candidate_frame)
+ return true;
+
+ // We can't go further from the frame returned by GetOneMore frame. Lets try
+ // to get a different frame with using the fallback unwind plan.
+ if (!m_frames[m_frames.size() - 2]
+ ->reg_ctx_lldb_sp->TryFallbackUnwindPlan()) {
+ // We don't have a valid fallback unwind plan. Accept the frame as it is.
+ // This is a valid situation when we are at the bottom of the stack.
+ return true;
+ }
+
+ // Remove the possibly incorrect frame from the frame list and try to add a
+ // different one with the newly selected fallback unwind plan.
+ m_frames.pop_back();
+ CursorSP new_frame_v2 = GetOneMoreFrame(abi);
+ if (new_frame_v2 == nullptr) {
+ // We haven't got a new frame from the fallback unwind plan. Accept the
+ // frame from the original unwind plan. This is a valid situation when we
+ // are at the bottom of the stack.
+ m_frames.push_back(new_frame);
+ return true;
+ }
+
+ // Push the new frame to the list and try to continue from this frame. If we
+ // can get a new frame then accept it as the correct one.
+ m_frames.push_back(new_frame_v2);
+ m_candidate_frame = GetOneMoreFrame(abi);
+ if (m_candidate_frame) {
+ // If control reached here then TryFallbackUnwindPlan had succeeded for
+ // Cursor::m_frames[m_frames.size() - 2]. It also succeeded to Unwind next
+ // 2 frames i.e. m_frames[m_frames.size() - 1] and a frame after that. For
+ // Cursor::m_frames[m_frames.size() - 2], reg_ctx_lldb_sp field was already
+ // updated during TryFallbackUnwindPlan call above. However, cfa field
+ // still needs to be updated. Hence updating it here and then returning.
+ return m_frames[m_frames.size() - 2]->reg_ctx_lldb_sp->GetCFA(
+ m_frames[m_frames.size() - 2]->cfa);
+ }
+
+ // The new frame hasn't helped in unwinding. Fall back to the original one as
+ // the default unwind plan is usually more reliable then the fallback one.
+ m_frames.pop_back();
+ m_frames.push_back(new_frame);
+ return true;
+}
+
+bool UnwindLLDB::DoGetFrameInfoAtIndex(uint32_t idx, addr_t &cfa, addr_t &pc) {
+ if (m_frames.size() == 0) {
+ if (!AddFirstFrame())
+ return false;
+ }
+
+ ProcessSP process_sp(m_thread.GetProcess());
+ ABI *abi = process_sp ? process_sp->GetABI().get() : nullptr;
+
+ while (idx >= m_frames.size() && AddOneMoreFrame(abi))
+ ;
+
+ if (idx < m_frames.size()) {
+ cfa = m_frames[idx]->cfa;
+ pc = m_frames[idx]->start_pc;
+ return true;
+ }
+ return false;
+}
+
+lldb::RegisterContextSP
+UnwindLLDB::DoCreateRegisterContextForFrame(StackFrame *frame) {
+ lldb::RegisterContextSP reg_ctx_sp;
+ uint32_t idx = frame->GetConcreteFrameIndex();
+
+ if (idx == 0) {
+ return m_thread.GetRegisterContext();
+ }
+
+ if (m_frames.size() == 0) {
+ if (!AddFirstFrame())
+ return reg_ctx_sp;
+ }
+
+ ProcessSP process_sp(m_thread.GetProcess());
+ ABI *abi = process_sp ? process_sp->GetABI().get() : nullptr;
+
+ while (idx >= m_frames.size()) {
+ if (!AddOneMoreFrame(abi))
+ break;
+ }
+
+ const uint32_t num_frames = m_frames.size();
+ if (idx < num_frames) {
+ Cursor *frame_cursor = m_frames[idx].get();
+ reg_ctx_sp = frame_cursor->reg_ctx_lldb_sp;
+ }
+ return reg_ctx_sp;
+}
+
+UnwindLLDB::RegisterContextLLDBSP
+UnwindLLDB::GetRegisterContextForFrameNum(uint32_t frame_num) {
+ RegisterContextLLDBSP reg_ctx_sp;
+ if (frame_num < m_frames.size())
+ reg_ctx_sp = m_frames[frame_num]->reg_ctx_lldb_sp;
+ return reg_ctx_sp;
+}
+
+bool UnwindLLDB::SearchForSavedLocationForRegister(
+ uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation &regloc,
+ uint32_t starting_frame_num, bool pc_reg) {
+ int64_t frame_num = starting_frame_num;
+ if (static_cast<size_t>(frame_num) >= m_frames.size())
+ return false;
+
+ // Never interrogate more than one level while looking for the saved pc
+ // value. If the value isn't saved by frame_num, none of the frames lower on
+ // the stack will have a useful value.
+ if (pc_reg) {
+ UnwindLLDB::RegisterSearchResult result;
+ result = m_frames[frame_num]->reg_ctx_lldb_sp->SavedLocationForRegister(
+ lldb_regnum, regloc);
+ return result == UnwindLLDB::RegisterSearchResult::eRegisterFound;
+ }
+ while (frame_num >= 0) {
+ UnwindLLDB::RegisterSearchResult result;
+ result = m_frames[frame_num]->reg_ctx_lldb_sp->SavedLocationForRegister(
+ lldb_regnum, regloc);
+
+ // We descended down to the live register context aka stack frame 0 and are
+ // reading the value out of a live register.
+ if (result == UnwindLLDB::RegisterSearchResult::eRegisterFound &&
+ regloc.type ==
+ UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext) {
+ return true;
+ }
+
+ // If we have unwind instructions saying that register N is saved in
+ // register M in the middle of the stack (and N can equal M here, meaning
+ // the register was not used in this function), then change the register
+ // number we're looking for to M and keep looking for a concrete location
+ // down the stack, or an actual value from a live RegisterContext at frame
+ // 0.
+ if (result == UnwindLLDB::RegisterSearchResult::eRegisterFound &&
+ regloc.type == UnwindLLDB::RegisterLocation::eRegisterInRegister &&
+ frame_num > 0) {
+ result = UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ lldb_regnum = regloc.location.register_number;
+ }
+
+ if (result == UnwindLLDB::RegisterSearchResult::eRegisterFound)
+ return true;
+ if (result == UnwindLLDB::RegisterSearchResult::eRegisterIsVolatile)
+ return false;
+ frame_num--;
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindLLDB.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindLLDB.h
new file mode 100644
index 000000000000..c512929c185b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindLLDB.h
@@ -0,0 +1,157 @@
+//===-- UnwindLLDB.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_UnwindLLDB_h_
+#define lldb_UnwindLLDB_h_
+
+#include <vector>
+
+#include "lldb/Symbol/FuncUnwinders.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Unwind.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/lldb-public.h"
+
+namespace lldb_private {
+
+class RegisterContextLLDB;
+
+class UnwindLLDB : public lldb_private::Unwind {
+public:
+ UnwindLLDB(lldb_private::Thread &thread);
+
+ ~UnwindLLDB() override = default;
+
+ enum RegisterSearchResult {
+ eRegisterFound = 0,
+ eRegisterNotFound,
+ eRegisterIsVolatile
+ };
+
+protected:
+ friend class lldb_private::RegisterContextLLDB;
+
+ struct RegisterLocation {
+ enum RegisterLocationTypes {
+ eRegisterNotSaved = 0, // register was not preserved by callee. If
+ // volatile reg, is unavailable
+ eRegisterSavedAtMemoryLocation, // register is saved at a specific word of
+ // target mem (target_memory_location)
+ eRegisterInRegister, // register is available in a (possible other)
+ // register (register_number)
+ eRegisterSavedAtHostMemoryLocation, // register is saved at a word in
+ // lldb's address space
+ eRegisterValueInferred, // register val was computed (and is in
+ // inferred_value)
+ eRegisterInLiveRegisterContext // register value is in a live (stack frame
+ // #0) register
+ };
+ int type;
+ union {
+ lldb::addr_t target_memory_location;
+ uint32_t
+ register_number; // in eRegisterKindLLDB register numbering system
+ void *host_memory_location;
+ uint64_t inferred_value; // eRegisterValueInferred - e.g. stack pointer ==
+ // cfa + offset
+ } location;
+ };
+
+ void DoClear() override {
+ m_frames.clear();
+ m_candidate_frame.reset();
+ m_unwind_complete = false;
+ }
+
+ uint32_t DoGetFrameCount() override;
+
+ bool DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa,
+ lldb::addr_t &start_pc) override;
+
+ lldb::RegisterContextSP
+ DoCreateRegisterContextForFrame(lldb_private::StackFrame *frame) override;
+
+ typedef std::shared_ptr<RegisterContextLLDB> RegisterContextLLDBSP;
+
+ // Needed to retrieve the "next" frame (e.g. frame 2 needs to retrieve frame
+ // 1's RegisterContextLLDB)
+ // The RegisterContext for frame_num must already exist or this returns an
+ // empty shared pointer.
+ RegisterContextLLDBSP GetRegisterContextForFrameNum(uint32_t frame_num);
+
+ // Iterate over the RegisterContextLLDB's in our m_frames vector, look for the
+ // first one that
+ // has a saved location for this reg.
+ bool SearchForSavedLocationForRegister(
+ uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation &regloc,
+ uint32_t starting_frame_num, bool pc_register);
+
+ /// Provide the list of user-specified trap handler functions
+ ///
+ /// The Platform is one source of trap handler function names; that
+ /// may be augmented via a setting. The setting needs to be converted
+ /// into an array of ConstStrings before it can be used - we only want
+ /// to do that once per thread so it's here in the UnwindLLDB object.
+ ///
+ /// \return
+ /// Vector of ConstStrings of trap handler function names. May be
+ /// empty.
+ const std::vector<ConstString> &GetUserSpecifiedTrapHandlerFunctionNames() {
+ return m_user_supplied_trap_handler_functions;
+ }
+
+private:
+ struct Cursor {
+ lldb::addr_t start_pc; // The start address of the function/symbol for this
+ // frame - current pc if unknown
+ lldb::addr_t cfa; // The canonical frame address for this stack frame
+ lldb_private::SymbolContext sctx; // A symbol context we'll contribute to &
+ // provide to the StackFrame creation
+ RegisterContextLLDBSP
+ reg_ctx_lldb_sp; // These are all RegisterContextLLDB's
+
+ Cursor()
+ : start_pc(LLDB_INVALID_ADDRESS), cfa(LLDB_INVALID_ADDRESS), sctx(),
+ reg_ctx_lldb_sp() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Cursor);
+ };
+
+ typedef std::shared_ptr<Cursor> CursorSP;
+ std::vector<CursorSP> m_frames;
+ CursorSP m_candidate_frame;
+ bool m_unwind_complete; // If this is true, we've enumerated all the frames in
+ // the stack, and m_frames.size() is the
+ // number of frames, etc. Otherwise we've only gone as far as directly asked,
+ // and m_frames.size()
+ // is how far we've currently gone.
+
+ std::vector<ConstString> m_user_supplied_trap_handler_functions;
+
+ // Check if Full UnwindPlan of First frame is valid or not.
+ // If not then try Fallback UnwindPlan of the frame. If Fallback
+ // UnwindPlan succeeds then update the Full UnwindPlan with the
+ // Fallback UnwindPlan.
+ void UpdateUnwindPlanForFirstFrameIfInvalid(ABI *abi);
+
+ CursorSP GetOneMoreFrame(ABI *abi);
+
+ bool AddOneMoreFrame(ABI *abi);
+
+ bool AddFirstFrame();
+
+ // For UnwindLLDB only
+ DISALLOW_COPY_AND_ASSIGN(UnwindLLDB);
+};
+
+} // namespace lldb_private
+
+#endif // lldb_UnwindLLDB_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp
new file mode 100644
index 000000000000..7dc5a5f5fdd1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp
@@ -0,0 +1,247 @@
+//===-- UnwindMacOSXFrameBackchain.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 "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ArchSpec.h"
+
+#include "RegisterContextMacOSXFrameBackchain.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+UnwindMacOSXFrameBackchain::UnwindMacOSXFrameBackchain(Thread &thread)
+ : Unwind(thread), m_cursors() {}
+
+uint32_t UnwindMacOSXFrameBackchain::DoGetFrameCount() {
+ if (m_cursors.empty()) {
+ ExecutionContext exe_ctx(m_thread.shared_from_this());
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ const ArchSpec &target_arch = target->GetArchitecture();
+ // Frame zero should always be supplied by the thread...
+ exe_ctx.SetFrameSP(m_thread.GetStackFrameAtIndex(0));
+
+ if (target_arch.GetAddressByteSize() == 8)
+ GetStackFrameData_x86_64(exe_ctx);
+ else
+ GetStackFrameData_i386(exe_ctx);
+ }
+ }
+ return m_cursors.size();
+}
+
+bool UnwindMacOSXFrameBackchain::DoGetFrameInfoAtIndex(uint32_t idx,
+ addr_t &cfa,
+ addr_t &pc) {
+ const uint32_t frame_count = GetFrameCount();
+ if (idx < frame_count) {
+ if (m_cursors[idx].pc == LLDB_INVALID_ADDRESS)
+ return false;
+ if (m_cursors[idx].fp == LLDB_INVALID_ADDRESS)
+ return false;
+
+ pc = m_cursors[idx].pc;
+ cfa = m_cursors[idx].fp;
+
+ return true;
+ }
+ return false;
+}
+
+lldb::RegisterContextSP
+UnwindMacOSXFrameBackchain::DoCreateRegisterContextForFrame(StackFrame *frame) {
+ lldb::RegisterContextSP reg_ctx_sp;
+ uint32_t concrete_idx = frame->GetConcreteFrameIndex();
+ const uint32_t frame_count = GetFrameCount();
+ if (concrete_idx < frame_count)
+ reg_ctx_sp = std::make_shared<RegisterContextMacOSXFrameBackchain>(
+ m_thread, concrete_idx, m_cursors[concrete_idx]);
+ return reg_ctx_sp;
+}
+
+size_t UnwindMacOSXFrameBackchain::GetStackFrameData_i386(
+ const ExecutionContext &exe_ctx) {
+ m_cursors.clear();
+
+ StackFrame *first_frame = exe_ctx.GetFramePtr();
+
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process == nullptr)
+ return 0;
+
+ struct Frame_i386 {
+ uint32_t fp;
+ uint32_t pc;
+ };
+
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext().get();
+ assert(reg_ctx);
+
+ Cursor cursor;
+ cursor.pc = reg_ctx->GetPC(LLDB_INVALID_ADDRESS);
+ cursor.fp = reg_ctx->GetFP(0);
+
+ Frame_i386 frame = {static_cast<uint32_t>(cursor.fp),
+ static_cast<uint32_t>(cursor.pc)};
+
+ m_cursors.push_back(cursor);
+
+ const size_t k_frame_size = sizeof(frame);
+ Status error;
+ while (frame.fp != 0 && frame.pc != 0 && ((frame.fp & 7) == 0)) {
+ // Read both the FP and PC (8 bytes)
+ if (process->ReadMemory(frame.fp, &frame.fp, k_frame_size, error) !=
+ k_frame_size)
+ break;
+ if (frame.pc >= 0x1000) {
+ cursor.pc = frame.pc;
+ cursor.fp = frame.fp;
+ m_cursors.push_back(cursor);
+ }
+ }
+ if (!m_cursors.empty()) {
+ lldb::addr_t first_frame_pc = m_cursors.front().pc;
+ if (first_frame_pc != LLDB_INVALID_ADDRESS) {
+ const SymbolContextItem resolve_scope =
+ eSymbolContextModule | eSymbolContextCompUnit |
+ eSymbolContextFunction | eSymbolContextSymbol;
+
+ SymbolContext first_frame_sc(
+ first_frame->GetSymbolContext(resolve_scope));
+ const AddressRange *addr_range_ptr = nullptr;
+ AddressRange range;
+ if (first_frame_sc.function)
+ addr_range_ptr = &first_frame_sc.function->GetAddressRange();
+ else if (first_frame_sc.symbol) {
+ range.GetBaseAddress() = first_frame_sc.symbol->GetAddress();
+ range.SetByteSize(first_frame_sc.symbol->GetByteSize());
+ addr_range_ptr = &range;
+ }
+
+ if (addr_range_ptr) {
+ if (first_frame->GetFrameCodeAddress() ==
+ addr_range_ptr->GetBaseAddress()) {
+ // We are at the first instruction, so we can recover the previous PC
+ // by dereferencing the SP
+ lldb::addr_t first_frame_sp = reg_ctx->GetSP(0);
+ // Read the real second frame return address into frame.pc
+ if (first_frame_sp &&
+ process->ReadMemory(first_frame_sp, &frame.pc, sizeof(frame.pc),
+ error) == sizeof(frame.pc)) {
+ cursor.fp = m_cursors.front().fp;
+ cursor.pc = frame.pc; // Set the new second frame PC
+
+ // Insert the second frame
+ m_cursors.insert(m_cursors.begin() + 1, cursor);
+
+ m_cursors.front().fp = first_frame_sp;
+ }
+ }
+ }
+ }
+ }
+ // uint32_t i=0;
+ // printf(" PC FP\n");
+ // printf(" ------------------ ------------------ \n");
+ // for (i=0; i<m_cursors.size(); ++i)
+ // {
+ // printf("[%3u] 0x%16.16" PRIx64 " 0x%16.16" PRIx64 "\n", i,
+ // m_cursors[i].pc, m_cursors[i].fp);
+ // }
+ return m_cursors.size();
+}
+
+size_t UnwindMacOSXFrameBackchain::GetStackFrameData_x86_64(
+ const ExecutionContext &exe_ctx) {
+ m_cursors.clear();
+
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process == nullptr)
+ return 0;
+
+ StackFrame *first_frame = exe_ctx.GetFramePtr();
+
+ struct Frame_x86_64 {
+ uint64_t fp;
+ uint64_t pc;
+ };
+
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext().get();
+ assert(reg_ctx);
+
+ Cursor cursor;
+ cursor.pc = reg_ctx->GetPC(LLDB_INVALID_ADDRESS);
+ cursor.fp = reg_ctx->GetFP(0);
+
+ Frame_x86_64 frame = {cursor.fp, cursor.pc};
+
+ m_cursors.push_back(cursor);
+ Status error;
+ const size_t k_frame_size = sizeof(frame);
+ while (frame.fp != 0 && frame.pc != 0 && ((frame.fp & 7) == 0)) {
+ // Read both the FP and PC (16 bytes)
+ if (process->ReadMemory(frame.fp, &frame.fp, k_frame_size, error) !=
+ k_frame_size)
+ break;
+
+ if (frame.pc >= 0x1000) {
+ cursor.pc = frame.pc;
+ cursor.fp = frame.fp;
+ m_cursors.push_back(cursor);
+ }
+ }
+ if (!m_cursors.empty()) {
+ lldb::addr_t first_frame_pc = m_cursors.front().pc;
+ if (first_frame_pc != LLDB_INVALID_ADDRESS) {
+ const SymbolContextItem resolve_scope =
+ eSymbolContextModule | eSymbolContextCompUnit |
+ eSymbolContextFunction | eSymbolContextSymbol;
+
+ SymbolContext first_frame_sc(
+ first_frame->GetSymbolContext(resolve_scope));
+ const AddressRange *addr_range_ptr = nullptr;
+ AddressRange range;
+ if (first_frame_sc.function)
+ addr_range_ptr = &first_frame_sc.function->GetAddressRange();
+ else if (first_frame_sc.symbol) {
+ range.GetBaseAddress() = first_frame_sc.symbol->GetAddress();
+ range.SetByteSize(first_frame_sc.symbol->GetByteSize());
+ addr_range_ptr = &range;
+ }
+
+ if (addr_range_ptr) {
+ if (first_frame->GetFrameCodeAddress() ==
+ addr_range_ptr->GetBaseAddress()) {
+ // We are at the first instruction, so we can recover the previous PC
+ // by dereferencing the SP
+ lldb::addr_t first_frame_sp = reg_ctx->GetSP(0);
+ // Read the real second frame return address into frame.pc
+ if (process->ReadMemory(first_frame_sp, &frame.pc, sizeof(frame.pc),
+ error) == sizeof(frame.pc)) {
+ cursor.fp = m_cursors.front().fp;
+ cursor.pc = frame.pc; // Set the new second frame PC
+
+ // Insert the second frame
+ m_cursors.insert(m_cursors.begin() + 1, cursor);
+
+ m_cursors.front().fp = first_frame_sp;
+ }
+ }
+ }
+ }
+ }
+ return m_cursors.size();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h
new file mode 100644
index 000000000000..2208bcc2f2e4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h
@@ -0,0 +1,53 @@
+//===-- UnwindMacOSXFrameBackchain.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_UnwindMacOSXFrameBackchain_h_
+#define lldb_UnwindMacOSXFrameBackchain_h_
+
+#include <vector>
+
+#include "lldb/Target/Unwind.h"
+#include "lldb/lldb-private.h"
+
+class UnwindMacOSXFrameBackchain : public lldb_private::Unwind {
+public:
+ UnwindMacOSXFrameBackchain(lldb_private::Thread &thread);
+
+ ~UnwindMacOSXFrameBackchain() override = default;
+
+protected:
+ void DoClear() override { m_cursors.clear(); }
+
+ uint32_t DoGetFrameCount() override;
+
+ bool DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa,
+ lldb::addr_t &pc) override;
+
+ lldb::RegisterContextSP
+ DoCreateRegisterContextForFrame(lldb_private::StackFrame *frame) override;
+
+ friend class RegisterContextMacOSXFrameBackchain;
+
+ struct Cursor {
+ lldb::addr_t pc; // Program counter
+ lldb::addr_t fp; // Frame pointer for us with backchain
+ };
+
+private:
+ std::vector<Cursor> m_cursors;
+
+ size_t GetStackFrameData_i386(const lldb_private::ExecutionContext &exe_ctx);
+
+ size_t
+ GetStackFrameData_x86_64(const lldb_private::ExecutionContext &exe_ctx);
+
+ // For UnwindMacOSXFrameBackchain only
+ DISALLOW_COPY_AND_ASSIGN(UnwindMacOSXFrameBackchain);
+};
+
+#endif // lldb_UnwindMacOSXFrameBackchain_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-arm-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-arm-register-enums.h
new file mode 100644
index 000000000000..39cbf01ea9d2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-arm-register-enums.h
@@ -0,0 +1,199 @@
+//===-- lldb-arm-register-enums.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_arm_register_enums_h
+#define lldb_arm_register_enums_h
+
+namespace lldb_private {
+// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB)
+
+// Internal codes for all ARM registers.
+enum {
+ k_first_gpr_arm = 0,
+ gpr_r0_arm = k_first_gpr_arm,
+ gpr_r1_arm,
+ gpr_r2_arm,
+ gpr_r3_arm,
+ gpr_r4_arm,
+ gpr_r5_arm,
+ gpr_r6_arm,
+ gpr_r7_arm,
+ gpr_r8_arm,
+ gpr_r9_arm,
+ gpr_r10_arm,
+ gpr_r11_arm,
+ gpr_r12_arm,
+ gpr_r13_arm,
+ gpr_sp_arm = gpr_r13_arm,
+ gpr_r14_arm,
+ gpr_lr_arm = gpr_r14_arm,
+ gpr_r15_arm,
+ gpr_pc_arm = gpr_r15_arm,
+ gpr_cpsr_arm,
+
+ k_last_gpr_arm = gpr_cpsr_arm,
+
+ k_first_fpr_arm,
+ fpu_s0_arm = k_first_fpr_arm,
+ fpu_s1_arm,
+ fpu_s2_arm,
+ fpu_s3_arm,
+ fpu_s4_arm,
+ fpu_s5_arm,
+ fpu_s6_arm,
+ fpu_s7_arm,
+ fpu_s8_arm,
+ fpu_s9_arm,
+ fpu_s10_arm,
+ fpu_s11_arm,
+ fpu_s12_arm,
+ fpu_s13_arm,
+ fpu_s14_arm,
+ fpu_s15_arm,
+ fpu_s16_arm,
+ fpu_s17_arm,
+ fpu_s18_arm,
+ fpu_s19_arm,
+ fpu_s20_arm,
+ fpu_s21_arm,
+ fpu_s22_arm,
+ fpu_s23_arm,
+ fpu_s24_arm,
+ fpu_s25_arm,
+ fpu_s26_arm,
+ fpu_s27_arm,
+ fpu_s28_arm,
+ fpu_s29_arm,
+ fpu_s30_arm,
+ fpu_s31_arm,
+ fpu_fpscr_arm,
+ fpu_d0_arm,
+ fpu_d1_arm,
+ fpu_d2_arm,
+ fpu_d3_arm,
+ fpu_d4_arm,
+ fpu_d5_arm,
+ fpu_d6_arm,
+ fpu_d7_arm,
+ fpu_d8_arm,
+ fpu_d9_arm,
+ fpu_d10_arm,
+ fpu_d11_arm,
+ fpu_d12_arm,
+ fpu_d13_arm,
+ fpu_d14_arm,
+ fpu_d15_arm,
+ fpu_d16_arm,
+ fpu_d17_arm,
+ fpu_d18_arm,
+ fpu_d19_arm,
+ fpu_d20_arm,
+ fpu_d21_arm,
+ fpu_d22_arm,
+ fpu_d23_arm,
+ fpu_d24_arm,
+ fpu_d25_arm,
+ fpu_d26_arm,
+ fpu_d27_arm,
+ fpu_d28_arm,
+ fpu_d29_arm,
+ fpu_d30_arm,
+ fpu_d31_arm,
+ fpu_q0_arm,
+ fpu_q1_arm,
+ fpu_q2_arm,
+ fpu_q3_arm,
+ fpu_q4_arm,
+ fpu_q5_arm,
+ fpu_q6_arm,
+ fpu_q7_arm,
+ fpu_q8_arm,
+ fpu_q9_arm,
+ fpu_q10_arm,
+ fpu_q11_arm,
+ fpu_q12_arm,
+ fpu_q13_arm,
+ fpu_q14_arm,
+ fpu_q15_arm,
+ k_last_fpr_arm = fpu_q15_arm,
+ exc_exception_arm,
+ exc_fsr_arm,
+ exc_far_arm,
+
+ dbg_bvr0_arm,
+ dbg_bvr1_arm,
+ dbg_bvr2_arm,
+ dbg_bvr3_arm,
+ dbg_bvr4_arm,
+ dbg_bvr5_arm,
+ dbg_bvr6_arm,
+ dbg_bvr7_arm,
+ dbg_bvr8_arm,
+ dbg_bvr9_arm,
+ dbg_bvr10_arm,
+ dbg_bvr11_arm,
+ dbg_bvr12_arm,
+ dbg_bvr13_arm,
+ dbg_bvr14_arm,
+ dbg_bvr15_arm,
+ dbg_bcr0_arm,
+ dbg_bcr1_arm,
+ dbg_bcr2_arm,
+ dbg_bcr3_arm,
+ dbg_bcr4_arm,
+ dbg_bcr5_arm,
+ dbg_bcr6_arm,
+ dbg_bcr7_arm,
+ dbg_bcr8_arm,
+ dbg_bcr9_arm,
+ dbg_bcr10_arm,
+ dbg_bcr11_arm,
+ dbg_bcr12_arm,
+ dbg_bcr13_arm,
+ dbg_bcr14_arm,
+ dbg_bcr15_arm,
+ dbg_wvr0_arm,
+ dbg_wvr1_arm,
+ dbg_wvr2_arm,
+ dbg_wvr3_arm,
+ dbg_wvr4_arm,
+ dbg_wvr5_arm,
+ dbg_wvr6_arm,
+ dbg_wvr7_arm,
+ dbg_wvr8_arm,
+ dbg_wvr9_arm,
+ dbg_wvr10_arm,
+ dbg_wvr11_arm,
+ dbg_wvr12_arm,
+ dbg_wvr13_arm,
+ dbg_wvr14_arm,
+ dbg_wvr15_arm,
+ dbg_wcr0_arm,
+ dbg_wcr1_arm,
+ dbg_wcr2_arm,
+ dbg_wcr3_arm,
+ dbg_wcr4_arm,
+ dbg_wcr5_arm,
+ dbg_wcr6_arm,
+ dbg_wcr7_arm,
+ dbg_wcr8_arm,
+ dbg_wcr9_arm,
+ dbg_wcr10_arm,
+ dbg_wcr11_arm,
+ dbg_wcr12_arm,
+ dbg_wcr13_arm,
+ dbg_wcr14_arm,
+ dbg_wcr15_arm,
+
+ k_num_registers_arm,
+ k_num_gpr_registers_arm = k_last_gpr_arm - k_first_gpr_arm + 1,
+ k_num_fpr_registers_arm = k_last_fpr_arm - k_first_fpr_arm + 1
+};
+}
+
+#endif // #ifndef lldb_arm64_register_enums_h
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-arm64-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-arm64-register-enums.h
new file mode 100644
index 000000000000..cc414dcde3cf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-arm64-register-enums.h
@@ -0,0 +1,264 @@
+//===-- lldb-arm64-register-enums.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_arm64_register_enums_h
+#define lldb_arm64_register_enums_h
+
+namespace lldb_private {
+// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB)
+
+// Internal codes for all ARM64 registers.
+enum {
+ k_first_gpr_arm64,
+ gpr_x0_arm64 = k_first_gpr_arm64,
+ gpr_x1_arm64,
+ gpr_x2_arm64,
+ gpr_x3_arm64,
+ gpr_x4_arm64,
+ gpr_x5_arm64,
+ gpr_x6_arm64,
+ gpr_x7_arm64,
+ gpr_x8_arm64,
+ gpr_x9_arm64,
+ gpr_x10_arm64,
+ gpr_x11_arm64,
+ gpr_x12_arm64,
+ gpr_x13_arm64,
+ gpr_x14_arm64,
+ gpr_x15_arm64,
+ gpr_x16_arm64,
+ gpr_x17_arm64,
+ gpr_x18_arm64,
+ gpr_x19_arm64,
+ gpr_x20_arm64,
+ gpr_x21_arm64,
+ gpr_x22_arm64,
+ gpr_x23_arm64,
+ gpr_x24_arm64,
+ gpr_x25_arm64,
+ gpr_x26_arm64,
+ gpr_x27_arm64,
+ gpr_x28_arm64,
+ gpr_fp_arm64,
+ gpr_lr_arm64,
+ gpr_sp_arm64,
+ gpr_pc_arm64,
+ gpr_cpsr_arm64,
+
+ gpr_w0_arm64,
+ gpr_w1_arm64,
+ gpr_w2_arm64,
+ gpr_w3_arm64,
+ gpr_w4_arm64,
+ gpr_w5_arm64,
+ gpr_w6_arm64,
+ gpr_w7_arm64,
+ gpr_w8_arm64,
+ gpr_w9_arm64,
+ gpr_w10_arm64,
+ gpr_w11_arm64,
+ gpr_w12_arm64,
+ gpr_w13_arm64,
+ gpr_w14_arm64,
+ gpr_w15_arm64,
+ gpr_w16_arm64,
+ gpr_w17_arm64,
+ gpr_w18_arm64,
+ gpr_w19_arm64,
+ gpr_w20_arm64,
+ gpr_w21_arm64,
+ gpr_w22_arm64,
+ gpr_w23_arm64,
+ gpr_w24_arm64,
+ gpr_w25_arm64,
+ gpr_w26_arm64,
+ gpr_w27_arm64,
+ gpr_w28_arm64,
+
+ k_last_gpr_arm64 = gpr_w28_arm64,
+
+ k_first_fpr_arm64,
+ fpu_v0_arm64 = k_first_fpr_arm64,
+ fpu_v1_arm64,
+ fpu_v2_arm64,
+ fpu_v3_arm64,
+ fpu_v4_arm64,
+ fpu_v5_arm64,
+ fpu_v6_arm64,
+ fpu_v7_arm64,
+ fpu_v8_arm64,
+ fpu_v9_arm64,
+ fpu_v10_arm64,
+ fpu_v11_arm64,
+ fpu_v12_arm64,
+ fpu_v13_arm64,
+ fpu_v14_arm64,
+ fpu_v15_arm64,
+ fpu_v16_arm64,
+ fpu_v17_arm64,
+ fpu_v18_arm64,
+ fpu_v19_arm64,
+ fpu_v20_arm64,
+ fpu_v21_arm64,
+ fpu_v22_arm64,
+ fpu_v23_arm64,
+ fpu_v24_arm64,
+ fpu_v25_arm64,
+ fpu_v26_arm64,
+ fpu_v27_arm64,
+ fpu_v28_arm64,
+ fpu_v29_arm64,
+ fpu_v30_arm64,
+ fpu_v31_arm64,
+
+ fpu_s0_arm64,
+ fpu_s1_arm64,
+ fpu_s2_arm64,
+ fpu_s3_arm64,
+ fpu_s4_arm64,
+ fpu_s5_arm64,
+ fpu_s6_arm64,
+ fpu_s7_arm64,
+ fpu_s8_arm64,
+ fpu_s9_arm64,
+ fpu_s10_arm64,
+ fpu_s11_arm64,
+ fpu_s12_arm64,
+ fpu_s13_arm64,
+ fpu_s14_arm64,
+ fpu_s15_arm64,
+ fpu_s16_arm64,
+ fpu_s17_arm64,
+ fpu_s18_arm64,
+ fpu_s19_arm64,
+ fpu_s20_arm64,
+ fpu_s21_arm64,
+ fpu_s22_arm64,
+ fpu_s23_arm64,
+ fpu_s24_arm64,
+ fpu_s25_arm64,
+ fpu_s26_arm64,
+ fpu_s27_arm64,
+ fpu_s28_arm64,
+ fpu_s29_arm64,
+ fpu_s30_arm64,
+ fpu_s31_arm64,
+
+ fpu_d0_arm64,
+ fpu_d1_arm64,
+ fpu_d2_arm64,
+ fpu_d3_arm64,
+ fpu_d4_arm64,
+ fpu_d5_arm64,
+ fpu_d6_arm64,
+ fpu_d7_arm64,
+ fpu_d8_arm64,
+ fpu_d9_arm64,
+ fpu_d10_arm64,
+ fpu_d11_arm64,
+ fpu_d12_arm64,
+ fpu_d13_arm64,
+ fpu_d14_arm64,
+ fpu_d15_arm64,
+ fpu_d16_arm64,
+ fpu_d17_arm64,
+ fpu_d18_arm64,
+ fpu_d19_arm64,
+ fpu_d20_arm64,
+ fpu_d21_arm64,
+ fpu_d22_arm64,
+ fpu_d23_arm64,
+ fpu_d24_arm64,
+ fpu_d25_arm64,
+ fpu_d26_arm64,
+ fpu_d27_arm64,
+ fpu_d28_arm64,
+ fpu_d29_arm64,
+ fpu_d30_arm64,
+ fpu_d31_arm64,
+
+ fpu_fpsr_arm64,
+ fpu_fpcr_arm64,
+ k_last_fpr_arm64 = fpu_fpcr_arm64,
+
+ exc_far_arm64,
+ exc_esr_arm64,
+ exc_exception_arm64,
+
+ dbg_bvr0_arm64,
+ dbg_bvr1_arm64,
+ dbg_bvr2_arm64,
+ dbg_bvr3_arm64,
+ dbg_bvr4_arm64,
+ dbg_bvr5_arm64,
+ dbg_bvr6_arm64,
+ dbg_bvr7_arm64,
+ dbg_bvr8_arm64,
+ dbg_bvr9_arm64,
+ dbg_bvr10_arm64,
+ dbg_bvr11_arm64,
+ dbg_bvr12_arm64,
+ dbg_bvr13_arm64,
+ dbg_bvr14_arm64,
+ dbg_bvr15_arm64,
+ dbg_bcr0_arm64,
+ dbg_bcr1_arm64,
+ dbg_bcr2_arm64,
+ dbg_bcr3_arm64,
+ dbg_bcr4_arm64,
+ dbg_bcr5_arm64,
+ dbg_bcr6_arm64,
+ dbg_bcr7_arm64,
+ dbg_bcr8_arm64,
+ dbg_bcr9_arm64,
+ dbg_bcr10_arm64,
+ dbg_bcr11_arm64,
+ dbg_bcr12_arm64,
+ dbg_bcr13_arm64,
+ dbg_bcr14_arm64,
+ dbg_bcr15_arm64,
+ dbg_wvr0_arm64,
+ dbg_wvr1_arm64,
+ dbg_wvr2_arm64,
+ dbg_wvr3_arm64,
+ dbg_wvr4_arm64,
+ dbg_wvr5_arm64,
+ dbg_wvr6_arm64,
+ dbg_wvr7_arm64,
+ dbg_wvr8_arm64,
+ dbg_wvr9_arm64,
+ dbg_wvr10_arm64,
+ dbg_wvr11_arm64,
+ dbg_wvr12_arm64,
+ dbg_wvr13_arm64,
+ dbg_wvr14_arm64,
+ dbg_wvr15_arm64,
+ dbg_wcr0_arm64,
+ dbg_wcr1_arm64,
+ dbg_wcr2_arm64,
+ dbg_wcr3_arm64,
+ dbg_wcr4_arm64,
+ dbg_wcr5_arm64,
+ dbg_wcr6_arm64,
+ dbg_wcr7_arm64,
+ dbg_wcr8_arm64,
+ dbg_wcr9_arm64,
+ dbg_wcr10_arm64,
+ dbg_wcr11_arm64,
+ dbg_wcr12_arm64,
+ dbg_wcr13_arm64,
+ dbg_wcr14_arm64,
+ dbg_wcr15_arm64,
+
+ k_num_registers_arm64,
+ k_num_gpr_registers_arm64 = k_last_gpr_arm64 - k_first_gpr_arm64 + 1,
+ k_num_fpr_registers_arm64 = k_last_fpr_arm64 - k_first_fpr_arm64 + 1
+};
+}
+
+#endif // #ifndef lldb_arm64_register_enums_h
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-mips-freebsd-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-mips-freebsd-register-enums.h
new file mode 100644
index 000000000000..d97f77122426
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-mips-freebsd-register-enums.h
@@ -0,0 +1,65 @@
+//===-- lldb-mips-freebsd-register-enums.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_mips_freebsd_register_enums_h
+#define lldb_mips_freebsd_register_enums_h
+
+namespace lldb_private {
+// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB)
+
+// Internal codes for all mips registers.
+enum {
+ k_first_gpr_mips64,
+ gpr_zero_mips64 = k_first_gpr_mips64,
+ gpr_r1_mips64,
+ gpr_r2_mips64,
+ gpr_r3_mips64,
+ gpr_r4_mips64,
+ gpr_r5_mips64,
+ gpr_r6_mips64,
+ gpr_r7_mips64,
+ gpr_r8_mips64,
+ gpr_r9_mips64,
+ gpr_r10_mips64,
+ gpr_r11_mips64,
+ gpr_r12_mips64,
+ gpr_r13_mips64,
+ gpr_r14_mips64,
+ gpr_r15_mips64,
+ gpr_r16_mips64,
+ gpr_r17_mips64,
+ gpr_r18_mips64,
+ gpr_r19_mips64,
+ gpr_r20_mips64,
+ gpr_r21_mips64,
+ gpr_r22_mips64,
+ gpr_r23_mips64,
+ gpr_r24_mips64,
+ gpr_r25_mips64,
+ gpr_r26_mips64,
+ gpr_r27_mips64,
+ gpr_gp_mips64,
+ gpr_sp_mips64,
+ gpr_r30_mips64,
+ gpr_ra_mips64,
+ gpr_sr_mips64,
+ gpr_mullo_mips64,
+ gpr_mulhi_mips64,
+ gpr_badvaddr_mips64,
+ gpr_cause_mips64,
+ gpr_pc_mips64,
+ gpr_ic_mips64,
+ gpr_dummy_mips64,
+ k_last_gpr_mips64 = gpr_dummy_mips64,
+
+ k_num_registers_mips64,
+
+ k_num_gpr_registers_mips64 = k_last_gpr_mips64 - k_first_gpr_mips64 + 1
+};
+}
+#endif // #ifndef lldb_mips_freebsd_register_enums_h
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-mips-linux-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-mips-linux-register-enums.h
new file mode 100644
index 000000000000..2f68b8022c9a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-mips-linux-register-enums.h
@@ -0,0 +1,360 @@
+//===-- lldb-mips-linux-register-enums.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_mips_linux_register_enums_h
+#define lldb_mips_linux_register_enums_h
+
+namespace lldb_private {
+// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB)
+
+// Internal codes for all mips registers.
+enum {
+ k_first_gpr_mips,
+ gpr_zero_mips = k_first_gpr_mips,
+ gpr_r1_mips,
+ gpr_r2_mips,
+ gpr_r3_mips,
+ gpr_r4_mips,
+ gpr_r5_mips,
+ gpr_r6_mips,
+ gpr_r7_mips,
+ gpr_r8_mips,
+ gpr_r9_mips,
+ gpr_r10_mips,
+ gpr_r11_mips,
+ gpr_r12_mips,
+ gpr_r13_mips,
+ gpr_r14_mips,
+ gpr_r15_mips,
+ gpr_r16_mips,
+ gpr_r17_mips,
+ gpr_r18_mips,
+ gpr_r19_mips,
+ gpr_r20_mips,
+ gpr_r21_mips,
+ gpr_r22_mips,
+ gpr_r23_mips,
+ gpr_r24_mips,
+ gpr_r25_mips,
+ gpr_r26_mips,
+ gpr_r27_mips,
+ gpr_gp_mips,
+ gpr_sp_mips,
+ gpr_r30_mips,
+ gpr_ra_mips,
+ gpr_sr_mips,
+ gpr_mullo_mips,
+ gpr_mulhi_mips,
+ gpr_badvaddr_mips,
+ gpr_cause_mips,
+ gpr_pc_mips,
+ gpr_config5_mips,
+
+ k_last_gpr_mips = gpr_config5_mips,
+
+ k_first_fpr_mips,
+ fpr_f0_mips = k_first_fpr_mips,
+ fpr_f1_mips,
+ fpr_f2_mips,
+ fpr_f3_mips,
+ fpr_f4_mips,
+ fpr_f5_mips,
+ fpr_f6_mips,
+ fpr_f7_mips,
+ fpr_f8_mips,
+ fpr_f9_mips,
+ fpr_f10_mips,
+ fpr_f11_mips,
+ fpr_f12_mips,
+ fpr_f13_mips,
+ fpr_f14_mips,
+ fpr_f15_mips,
+ fpr_f16_mips,
+ fpr_f17_mips,
+ fpr_f18_mips,
+ fpr_f19_mips,
+ fpr_f20_mips,
+ fpr_f21_mips,
+ fpr_f22_mips,
+ fpr_f23_mips,
+ fpr_f24_mips,
+ fpr_f25_mips,
+ fpr_f26_mips,
+ fpr_f27_mips,
+ fpr_f28_mips,
+ fpr_f29_mips,
+ fpr_f30_mips,
+ fpr_f31_mips,
+ fpr_fcsr_mips,
+ fpr_fir_mips,
+ fpr_config5_mips,
+ k_last_fpr_mips = fpr_config5_mips,
+
+ k_first_msa_mips,
+ msa_w0_mips = k_first_msa_mips,
+ msa_w1_mips,
+ msa_w2_mips,
+ msa_w3_mips,
+ msa_w4_mips,
+ msa_w5_mips,
+ msa_w6_mips,
+ msa_w7_mips,
+ msa_w8_mips,
+ msa_w9_mips,
+ msa_w10_mips,
+ msa_w11_mips,
+ msa_w12_mips,
+ msa_w13_mips,
+ msa_w14_mips,
+ msa_w15_mips,
+ msa_w16_mips,
+ msa_w17_mips,
+ msa_w18_mips,
+ msa_w19_mips,
+ msa_w20_mips,
+ msa_w21_mips,
+ msa_w22_mips,
+ msa_w23_mips,
+ msa_w24_mips,
+ msa_w25_mips,
+ msa_w26_mips,
+ msa_w27_mips,
+ msa_w28_mips,
+ msa_w29_mips,
+ msa_w30_mips,
+ msa_w31_mips,
+ msa_fcsr_mips,
+ msa_fir_mips,
+ msa_mcsr_mips,
+ msa_mir_mips,
+ msa_config5_mips,
+ k_last_msa_mips = msa_config5_mips,
+
+ k_num_registers_mips,
+
+ k_num_gpr_registers_mips = k_last_gpr_mips - k_first_gpr_mips + 1,
+ k_num_fpr_registers_mips = k_last_fpr_mips - k_first_fpr_mips + 1,
+ k_num_msa_registers_mips = k_last_msa_mips - k_first_msa_mips + 1,
+ k_num_user_registers_mips = k_num_gpr_registers_mips +
+ k_num_fpr_registers_mips +
+ k_num_msa_registers_mips
+};
+
+// Internal codes for all mips64 registers.
+enum {
+ k_first_gpr_mips64,
+ gpr_zero_mips64 = k_first_gpr_mips64,
+ gpr_r1_mips64,
+ gpr_r2_mips64,
+ gpr_r3_mips64,
+ gpr_r4_mips64,
+ gpr_r5_mips64,
+ gpr_r6_mips64,
+ gpr_r7_mips64,
+ gpr_r8_mips64,
+ gpr_r9_mips64,
+ gpr_r10_mips64,
+ gpr_r11_mips64,
+ gpr_r12_mips64,
+ gpr_r13_mips64,
+ gpr_r14_mips64,
+ gpr_r15_mips64,
+ gpr_r16_mips64,
+ gpr_r17_mips64,
+ gpr_r18_mips64,
+ gpr_r19_mips64,
+ gpr_r20_mips64,
+ gpr_r21_mips64,
+ gpr_r22_mips64,
+ gpr_r23_mips64,
+ gpr_r24_mips64,
+ gpr_r25_mips64,
+ gpr_r26_mips64,
+ gpr_r27_mips64,
+ gpr_gp_mips64,
+ gpr_sp_mips64,
+ gpr_r30_mips64,
+ gpr_ra_mips64,
+ gpr_sr_mips64,
+ gpr_mullo_mips64,
+ gpr_mulhi_mips64,
+ gpr_badvaddr_mips64,
+ gpr_cause_mips64,
+ gpr_pc_mips64,
+ gpr_config5_mips64,
+ k_last_gpr_mips64 = gpr_config5_mips64,
+
+ k_first_fpr_mips64,
+ fpr_f0_mips64 = k_first_fpr_mips64,
+ fpr_f1_mips64,
+ fpr_f2_mips64,
+ fpr_f3_mips64,
+ fpr_f4_mips64,
+ fpr_f5_mips64,
+ fpr_f6_mips64,
+ fpr_f7_mips64,
+ fpr_f8_mips64,
+ fpr_f9_mips64,
+ fpr_f10_mips64,
+ fpr_f11_mips64,
+ fpr_f12_mips64,
+ fpr_f13_mips64,
+ fpr_f14_mips64,
+ fpr_f15_mips64,
+ fpr_f16_mips64,
+ fpr_f17_mips64,
+ fpr_f18_mips64,
+ fpr_f19_mips64,
+ fpr_f20_mips64,
+ fpr_f21_mips64,
+ fpr_f22_mips64,
+ fpr_f23_mips64,
+ fpr_f24_mips64,
+ fpr_f25_mips64,
+ fpr_f26_mips64,
+ fpr_f27_mips64,
+ fpr_f28_mips64,
+ fpr_f29_mips64,
+ fpr_f30_mips64,
+ fpr_f31_mips64,
+ fpr_fcsr_mips64,
+ fpr_fir_mips64,
+ fpr_config5_mips64,
+ k_last_fpr_mips64 = fpr_config5_mips64,
+
+ k_first_msa_mips64,
+ msa_w0_mips64 = k_first_msa_mips64,
+ msa_w1_mips64,
+ msa_w2_mips64,
+ msa_w3_mips64,
+ msa_w4_mips64,
+ msa_w5_mips64,
+ msa_w6_mips64,
+ msa_w7_mips64,
+ msa_w8_mips64,
+ msa_w9_mips64,
+ msa_w10_mips64,
+ msa_w11_mips64,
+ msa_w12_mips64,
+ msa_w13_mips64,
+ msa_w14_mips64,
+ msa_w15_mips64,
+ msa_w16_mips64,
+ msa_w17_mips64,
+ msa_w18_mips64,
+ msa_w19_mips64,
+ msa_w20_mips64,
+ msa_w21_mips64,
+ msa_w22_mips64,
+ msa_w23_mips64,
+ msa_w24_mips64,
+ msa_w25_mips64,
+ msa_w26_mips64,
+ msa_w27_mips64,
+ msa_w28_mips64,
+ msa_w29_mips64,
+ msa_w30_mips64,
+ msa_w31_mips64,
+ msa_fcsr_mips64,
+ msa_fir_mips64,
+ msa_mcsr_mips64,
+ msa_mir_mips64,
+ msa_config5_mips64,
+ k_last_msa_mips64 = msa_config5_mips64,
+
+ k_num_registers_mips64,
+
+ k_num_gpr_registers_mips64 = k_last_gpr_mips64 - k_first_gpr_mips64 + 1,
+ k_num_fpr_registers_mips64 = k_last_fpr_mips64 - k_first_fpr_mips64 + 1,
+ k_num_msa_registers_mips64 = k_last_msa_mips64 - k_first_msa_mips64 + 1,
+ k_num_user_registers_mips64 = k_num_gpr_registers_mips64 +
+ k_num_fpr_registers_mips64 +
+ k_num_msa_registers_mips64
+};
+
+// Register no. for RegisterKind = eRegisterKindProcessPlugin
+// The ptrace request PTRACE_PEEKUSER/PTRACE_POKEUSER used this number
+enum {
+ ptrace_zero_mips,
+ ptrace_r1_mips,
+ ptrace_r2_mips,
+ ptrace_r3_mips,
+ ptrace_r4_mips,
+ ptrace_r5_mips,
+ ptrace_r6_mips,
+ ptrace_r7_mips,
+ ptrace_r8_mips,
+ ptrace_r9_mips,
+ ptrace_r10_mips,
+ ptrace_r11_mips,
+ ptrace_r12_mips,
+ ptrace_r13_mips,
+ ptrace_r14_mips,
+ ptrace_r15_mips,
+ ptrace_r16_mips,
+ ptrace_r17_mips,
+ ptrace_r18_mips,
+ ptrace_r19_mips,
+ ptrace_r20_mips,
+ ptrace_r21_mips,
+ ptrace_r22_mips,
+ ptrace_r23_mips,
+ ptrace_r24_mips,
+ ptrace_r25_mips,
+ ptrace_r26_mips,
+ ptrace_r27_mips,
+ ptrace_gp_mips,
+ ptrace_sp_mips,
+ ptrace_r30_mips,
+ ptrace_ra_mips,
+ ptrace_f0_mips,
+ ptrace_f1_mips,
+ ptrace_f2_mips,
+ ptrace_f3_mips,
+ ptrace_f4_mips,
+ ptrace_f5_mips,
+ ptrace_f6_mips,
+ ptrace_f7_mips,
+ ptrace_f8_mips,
+ ptrace_f9_mips,
+ ptrace_f10_mips,
+ ptrace_f11_mips,
+ ptrace_f12_mips,
+ ptrace_f13_mips,
+ ptrace_f14_mips,
+ ptrace_f15_mips,
+ ptrace_f16_mips,
+ ptrace_f17_mips,
+ ptrace_f18_mips,
+ ptrace_f19_mips,
+ ptrace_f20_mips,
+ ptrace_f21_mips,
+ ptrace_f22_mips,
+ ptrace_f23_mips,
+ ptrace_f24_mips,
+ ptrace_f25_mips,
+ ptrace_f26_mips,
+ ptrace_f27_mips,
+ ptrace_f28_mips,
+ ptrace_f29_mips,
+ ptrace_f30_mips,
+ ptrace_f31_mips,
+ ptrace_pc_mips,
+ ptrace_cause_mips,
+ ptrace_badvaddr_mips,
+ ptrace_mulhi_mips,
+ ptrace_mullo_mips,
+ ptrace_fcsr_mips,
+ ptrace_fir_mips,
+ ptrace_sr_mips,
+ ptrace_config5_mips
+};
+}
+
+#endif // #ifndef lldb_mips_linux_register_enums_h
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-ppc64-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-ppc64-register-enums.h
new file mode 100644
index 000000000000..6edf7ee3864d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-ppc64-register-enums.h
@@ -0,0 +1,136 @@
+//===-- lldb-ppc64-register-enums.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_ppc64_register_enums_h
+#define lldb_ppc64_register_enums_h
+
+// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB)
+
+// Internal codes for all ppc64 registers.
+enum {
+ k_first_gpr_ppc64,
+ gpr_r0_ppc64 = k_first_gpr_ppc64,
+ gpr_r1_ppc64,
+ gpr_r2_ppc64,
+ gpr_r3_ppc64,
+ gpr_r4_ppc64,
+ gpr_r5_ppc64,
+ gpr_r6_ppc64,
+ gpr_r7_ppc64,
+ gpr_r8_ppc64,
+ gpr_r9_ppc64,
+ gpr_r10_ppc64,
+ gpr_r11_ppc64,
+ gpr_r12_ppc64,
+ gpr_r13_ppc64,
+ gpr_r14_ppc64,
+ gpr_r15_ppc64,
+ gpr_r16_ppc64,
+ gpr_r17_ppc64,
+ gpr_r18_ppc64,
+ gpr_r19_ppc64,
+ gpr_r20_ppc64,
+ gpr_r21_ppc64,
+ gpr_r22_ppc64,
+ gpr_r23_ppc64,
+ gpr_r24_ppc64,
+ gpr_r25_ppc64,
+ gpr_r26_ppc64,
+ gpr_r27_ppc64,
+ gpr_r28_ppc64,
+ gpr_r29_ppc64,
+ gpr_r30_ppc64,
+ gpr_r31_ppc64,
+ gpr_cr_ppc64,
+ gpr_msr_ppc64,
+ gpr_xer_ppc64,
+ gpr_lr_ppc64,
+ gpr_ctr_ppc64,
+ gpr_pc_ppc64,
+ k_last_gpr_ppc64 = gpr_pc_ppc64,
+
+ k_first_fpr_ppc64,
+ fpr_f0_ppc64 = k_first_fpr_ppc64,
+ fpr_f1_ppc64,
+ fpr_f2_ppc64,
+ fpr_f3_ppc64,
+ fpr_f4_ppc64,
+ fpr_f5_ppc64,
+ fpr_f6_ppc64,
+ fpr_f7_ppc64,
+ fpr_f8_ppc64,
+ fpr_f9_ppc64,
+ fpr_f10_ppc64,
+ fpr_f11_ppc64,
+ fpr_f12_ppc64,
+ fpr_f13_ppc64,
+ fpr_f14_ppc64,
+ fpr_f15_ppc64,
+ fpr_f16_ppc64,
+ fpr_f17_ppc64,
+ fpr_f18_ppc64,
+ fpr_f19_ppc64,
+ fpr_f20_ppc64,
+ fpr_f21_ppc64,
+ fpr_f22_ppc64,
+ fpr_f23_ppc64,
+ fpr_f24_ppc64,
+ fpr_f25_ppc64,
+ fpr_f26_ppc64,
+ fpr_f27_ppc64,
+ fpr_f28_ppc64,
+ fpr_f29_ppc64,
+ fpr_f30_ppc64,
+ fpr_f31_ppc64,
+ fpr_fpscr_ppc64,
+ k_last_fpr_ppc64 = fpr_fpscr_ppc64,
+
+ k_first_vmx_ppc64,
+ vmx_vr0_ppc64 = k_first_vmx_ppc64,
+ vmx_vr1_ppc64,
+ vmx_vr2_ppc64,
+ vmx_vr3_ppc64,
+ vmx_vr4_ppc64,
+ vmx_vr5_ppc64,
+ vmx_vr6_ppc64,
+ vmx_vr7_ppc64,
+ vmx_vr8_ppc64,
+ vmx_vr9_ppc64,
+ vmx_vr10_ppc64,
+ vmx_vr11_ppc64,
+ vmx_vr12_ppc64,
+ vmx_vr13_ppc64,
+ vmx_vr14_ppc64,
+ vmx_vr15_ppc64,
+ vmx_vr16_ppc64,
+ vmx_vr17_ppc64,
+ vmx_vr18_ppc64,
+ vmx_vr19_ppc64,
+ vmx_vr20_ppc64,
+ vmx_vr21_ppc64,
+ vmx_vr22_ppc64,
+ vmx_vr23_ppc64,
+ vmx_vr24_ppc64,
+ vmx_vr25_ppc64,
+ vmx_vr26_ppc64,
+ vmx_vr27_ppc64,
+ vmx_vr28_ppc64,
+ vmx_vr29_ppc64,
+ vmx_vr30_ppc64,
+ vmx_vr31_ppc64,
+ vmx_vscr_ppc64,
+ vmx_vrsave_ppc64,
+ k_last_vmx_ppc64 = vmx_vrsave_ppc64,
+
+ k_num_registers_ppc64,
+ k_num_gpr_registers_ppc64 = k_last_gpr_ppc64 - k_first_gpr_ppc64 + 1,
+ k_num_fpr_registers_ppc64 = k_last_fpr_ppc64 - k_first_fpr_ppc64 + 1,
+ k_num_vmx_registers_ppc64 = k_last_vmx_ppc64 - k_first_vmx_ppc64 + 1,
+};
+
+#endif // #ifndef lldb_ppc64_register_enums_h
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-ppc64le-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-ppc64le-register-enums.h
new file mode 100644
index 000000000000..0c381a5f3918
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-ppc64le-register-enums.h
@@ -0,0 +1,207 @@
+//===-- lldb-ppc64le-register-enums.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_ppc64le_register_enums_h
+#define lldb_ppc64le_register_enums_h
+
+// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB)
+
+// Internal codes for all ppc64le registers.
+enum {
+ k_first_gpr_ppc64le,
+ gpr_r0_ppc64le = k_first_gpr_ppc64le,
+ gpr_r1_ppc64le,
+ gpr_r2_ppc64le,
+ gpr_r3_ppc64le,
+ gpr_r4_ppc64le,
+ gpr_r5_ppc64le,
+ gpr_r6_ppc64le,
+ gpr_r7_ppc64le,
+ gpr_r8_ppc64le,
+ gpr_r9_ppc64le,
+ gpr_r10_ppc64le,
+ gpr_r11_ppc64le,
+ gpr_r12_ppc64le,
+ gpr_r13_ppc64le,
+ gpr_r14_ppc64le,
+ gpr_r15_ppc64le,
+ gpr_r16_ppc64le,
+ gpr_r17_ppc64le,
+ gpr_r18_ppc64le,
+ gpr_r19_ppc64le,
+ gpr_r20_ppc64le,
+ gpr_r21_ppc64le,
+ gpr_r22_ppc64le,
+ gpr_r23_ppc64le,
+ gpr_r24_ppc64le,
+ gpr_r25_ppc64le,
+ gpr_r26_ppc64le,
+ gpr_r27_ppc64le,
+ gpr_r28_ppc64le,
+ gpr_r29_ppc64le,
+ gpr_r30_ppc64le,
+ gpr_r31_ppc64le,
+ gpr_pc_ppc64le,
+ gpr_msr_ppc64le,
+ gpr_origr3_ppc64le,
+ gpr_ctr_ppc64le,
+ gpr_lr_ppc64le,
+ gpr_xer_ppc64le,
+ gpr_cr_ppc64le,
+ gpr_softe_ppc64le,
+ gpr_trap_ppc64le,
+ k_last_gpr_ppc64le = gpr_trap_ppc64le,
+
+ k_first_fpr_ppc64le,
+ fpr_f0_ppc64le = k_first_fpr_ppc64le,
+ fpr_f1_ppc64le,
+ fpr_f2_ppc64le,
+ fpr_f3_ppc64le,
+ fpr_f4_ppc64le,
+ fpr_f5_ppc64le,
+ fpr_f6_ppc64le,
+ fpr_f7_ppc64le,
+ fpr_f8_ppc64le,
+ fpr_f9_ppc64le,
+ fpr_f10_ppc64le,
+ fpr_f11_ppc64le,
+ fpr_f12_ppc64le,
+ fpr_f13_ppc64le,
+ fpr_f14_ppc64le,
+ fpr_f15_ppc64le,
+ fpr_f16_ppc64le,
+ fpr_f17_ppc64le,
+ fpr_f18_ppc64le,
+ fpr_f19_ppc64le,
+ fpr_f20_ppc64le,
+ fpr_f21_ppc64le,
+ fpr_f22_ppc64le,
+ fpr_f23_ppc64le,
+ fpr_f24_ppc64le,
+ fpr_f25_ppc64le,
+ fpr_f26_ppc64le,
+ fpr_f27_ppc64le,
+ fpr_f28_ppc64le,
+ fpr_f29_ppc64le,
+ fpr_f30_ppc64le,
+ fpr_f31_ppc64le,
+ fpr_fpscr_ppc64le,
+ k_last_fpr_ppc64le = fpr_fpscr_ppc64le,
+
+ k_first_vmx_ppc64le,
+ vmx_vr0_ppc64le = k_first_vmx_ppc64le,
+ vmx_vr1_ppc64le,
+ vmx_vr2_ppc64le,
+ vmx_vr3_ppc64le,
+ vmx_vr4_ppc64le,
+ vmx_vr5_ppc64le,
+ vmx_vr6_ppc64le,
+ vmx_vr7_ppc64le,
+ vmx_vr8_ppc64le,
+ vmx_vr9_ppc64le,
+ vmx_vr10_ppc64le,
+ vmx_vr11_ppc64le,
+ vmx_vr12_ppc64le,
+ vmx_vr13_ppc64le,
+ vmx_vr14_ppc64le,
+ vmx_vr15_ppc64le,
+ vmx_vr16_ppc64le,
+ vmx_vr17_ppc64le,
+ vmx_vr18_ppc64le,
+ vmx_vr19_ppc64le,
+ vmx_vr20_ppc64le,
+ vmx_vr21_ppc64le,
+ vmx_vr22_ppc64le,
+ vmx_vr23_ppc64le,
+ vmx_vr24_ppc64le,
+ vmx_vr25_ppc64le,
+ vmx_vr26_ppc64le,
+ vmx_vr27_ppc64le,
+ vmx_vr28_ppc64le,
+ vmx_vr29_ppc64le,
+ vmx_vr30_ppc64le,
+ vmx_vr31_ppc64le,
+ vmx_vscr_ppc64le,
+ vmx_vrsave_ppc64le,
+ k_last_vmx_ppc64le = vmx_vrsave_ppc64le,
+
+ k_first_vsx_ppc64le,
+ vsx_vs0_ppc64le = k_first_vsx_ppc64le,
+ vsx_vs1_ppc64le,
+ vsx_vs2_ppc64le,
+ vsx_vs3_ppc64le,
+ vsx_vs4_ppc64le,
+ vsx_vs5_ppc64le,
+ vsx_vs6_ppc64le,
+ vsx_vs7_ppc64le,
+ vsx_vs8_ppc64le,
+ vsx_vs9_ppc64le,
+ vsx_vs10_ppc64le,
+ vsx_vs11_ppc64le,
+ vsx_vs12_ppc64le,
+ vsx_vs13_ppc64le,
+ vsx_vs14_ppc64le,
+ vsx_vs15_ppc64le,
+ vsx_vs16_ppc64le,
+ vsx_vs17_ppc64le,
+ vsx_vs18_ppc64le,
+ vsx_vs19_ppc64le,
+ vsx_vs20_ppc64le,
+ vsx_vs21_ppc64le,
+ vsx_vs22_ppc64le,
+ vsx_vs23_ppc64le,
+ vsx_vs24_ppc64le,
+ vsx_vs25_ppc64le,
+ vsx_vs26_ppc64le,
+ vsx_vs27_ppc64le,
+ vsx_vs28_ppc64le,
+ vsx_vs29_ppc64le,
+ vsx_vs30_ppc64le,
+ vsx_vs31_ppc64le,
+ vsx_vs32_ppc64le,
+ vsx_vs33_ppc64le,
+ vsx_vs34_ppc64le,
+ vsx_vs35_ppc64le,
+ vsx_vs36_ppc64le,
+ vsx_vs37_ppc64le,
+ vsx_vs38_ppc64le,
+ vsx_vs39_ppc64le,
+ vsx_vs40_ppc64le,
+ vsx_vs41_ppc64le,
+ vsx_vs42_ppc64le,
+ vsx_vs43_ppc64le,
+ vsx_vs44_ppc64le,
+ vsx_vs45_ppc64le,
+ vsx_vs46_ppc64le,
+ vsx_vs47_ppc64le,
+ vsx_vs48_ppc64le,
+ vsx_vs49_ppc64le,
+ vsx_vs50_ppc64le,
+ vsx_vs51_ppc64le,
+ vsx_vs52_ppc64le,
+ vsx_vs53_ppc64le,
+ vsx_vs54_ppc64le,
+ vsx_vs55_ppc64le,
+ vsx_vs56_ppc64le,
+ vsx_vs57_ppc64le,
+ vsx_vs58_ppc64le,
+ vsx_vs59_ppc64le,
+ vsx_vs60_ppc64le,
+ vsx_vs61_ppc64le,
+ vsx_vs62_ppc64le,
+ vsx_vs63_ppc64le,
+ k_last_vsx_ppc64le = vsx_vs63_ppc64le,
+
+ k_num_registers_ppc64le,
+ k_num_gpr_registers_ppc64le = k_last_gpr_ppc64le - k_first_gpr_ppc64le + 1,
+ k_num_fpr_registers_ppc64le = k_last_fpr_ppc64le - k_first_fpr_ppc64le + 1,
+ k_num_vmx_registers_ppc64le = k_last_vmx_ppc64le - k_first_vmx_ppc64le + 1,
+ k_num_vsx_registers_ppc64le = k_last_vsx_ppc64le - k_first_vsx_ppc64le + 1,
+};
+
+#endif // #ifndef lldb_ppc64le_register_enums_h
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-s390x-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-s390x-register-enums.h
new file mode 100644
index 000000000000..bd6626108290
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-s390x-register-enums.h
@@ -0,0 +1,90 @@
+//===-- lldb-s390x-register-enums.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_s390x_register_enums_h
+#define lldb_s390x_register_enums_h
+
+namespace lldb_private {
+// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB)
+
+// Internal codes for all s390x registers.
+enum {
+ k_first_gpr_s390x,
+ lldb_r0_s390x = k_first_gpr_s390x,
+ lldb_r1_s390x,
+ lldb_r2_s390x,
+ lldb_r3_s390x,
+ lldb_r4_s390x,
+ lldb_r5_s390x,
+ lldb_r6_s390x,
+ lldb_r7_s390x,
+ lldb_r8_s390x,
+ lldb_r9_s390x,
+ lldb_r10_s390x,
+ lldb_r11_s390x,
+ lldb_r12_s390x,
+ lldb_r13_s390x,
+ lldb_r14_s390x,
+ lldb_r15_s390x,
+ lldb_acr0_s390x,
+ lldb_acr1_s390x,
+ lldb_acr2_s390x,
+ lldb_acr3_s390x,
+ lldb_acr4_s390x,
+ lldb_acr5_s390x,
+ lldb_acr6_s390x,
+ lldb_acr7_s390x,
+ lldb_acr8_s390x,
+ lldb_acr9_s390x,
+ lldb_acr10_s390x,
+ lldb_acr11_s390x,
+ lldb_acr12_s390x,
+ lldb_acr13_s390x,
+ lldb_acr14_s390x,
+ lldb_acr15_s390x,
+ lldb_pswm_s390x,
+ lldb_pswa_s390x,
+ k_last_gpr_s390x = lldb_pswa_s390x,
+
+ k_first_fpr_s390x,
+ lldb_f0_s390x = k_first_fpr_s390x,
+ lldb_f1_s390x,
+ lldb_f2_s390x,
+ lldb_f3_s390x,
+ lldb_f4_s390x,
+ lldb_f5_s390x,
+ lldb_f6_s390x,
+ lldb_f7_s390x,
+ lldb_f8_s390x,
+ lldb_f9_s390x,
+ lldb_f10_s390x,
+ lldb_f11_s390x,
+ lldb_f12_s390x,
+ lldb_f13_s390x,
+ lldb_f14_s390x,
+ lldb_f15_s390x,
+ lldb_fpc_s390x,
+ k_last_fpr_s390x = lldb_fpc_s390x,
+
+ // These are only available on Linux.
+ k_first_linux_s390x,
+ lldb_orig_r2_s390x = k_first_linux_s390x,
+ lldb_last_break_s390x,
+ lldb_system_call_s390x,
+ k_last_linux_s390x = lldb_system_call_s390x,
+
+ k_num_registers_s390x,
+ k_num_gpr_registers_s390x = k_last_gpr_s390x - k_first_gpr_s390x + 1,
+ k_num_fpr_registers_s390x = k_last_fpr_s390x - k_first_fpr_s390x + 1,
+ k_num_linux_registers_s390x = k_last_linux_s390x - k_first_linux_s390x + 1,
+ k_num_user_registers_s390x =
+ k_num_gpr_registers_s390x + k_num_fpr_registers_s390x,
+};
+}
+
+#endif // #ifndef lldb_s390x_register_enums_h
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-x86-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-x86-register-enums.h
new file mode 100644
index 000000000000..0d2149c83573
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-x86-register-enums.h
@@ -0,0 +1,318 @@
+//===-- lldb-x86-register-enums.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_x86_register_enums_h
+#define lldb_x86_register_enums_h
+
+namespace lldb_private {
+// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB)
+
+// Internal codes for all i386 registers.
+enum {
+ k_first_gpr_i386,
+ lldb_eax_i386 = k_first_gpr_i386,
+ lldb_ebx_i386,
+ lldb_ecx_i386,
+ lldb_edx_i386,
+ lldb_edi_i386,
+ lldb_esi_i386,
+ lldb_ebp_i386,
+ lldb_esp_i386,
+ lldb_eip_i386,
+ lldb_eflags_i386,
+ lldb_cs_i386,
+ lldb_fs_i386,
+ lldb_gs_i386,
+ lldb_ss_i386,
+ lldb_ds_i386,
+ lldb_es_i386,
+
+ k_first_alias_i386,
+ lldb_ax_i386 = k_first_alias_i386,
+ lldb_bx_i386,
+ lldb_cx_i386,
+ lldb_dx_i386,
+ lldb_di_i386,
+ lldb_si_i386,
+ lldb_bp_i386,
+ lldb_sp_i386,
+ lldb_ah_i386,
+ lldb_bh_i386,
+ lldb_ch_i386,
+ lldb_dh_i386,
+ lldb_al_i386,
+ lldb_bl_i386,
+ lldb_cl_i386,
+ lldb_dl_i386,
+ k_last_alias_i386 = lldb_dl_i386,
+
+ k_last_gpr_i386 = k_last_alias_i386,
+
+ k_first_fpr_i386,
+ lldb_fctrl_i386 = k_first_fpr_i386,
+ lldb_fstat_i386,
+ lldb_ftag_i386,
+ lldb_fop_i386,
+ lldb_fiseg_i386,
+ lldb_fioff_i386,
+ lldb_foseg_i386,
+ lldb_fooff_i386,
+ lldb_mxcsr_i386,
+ lldb_mxcsrmask_i386,
+ lldb_st0_i386,
+ lldb_st1_i386,
+ lldb_st2_i386,
+ lldb_st3_i386,
+ lldb_st4_i386,
+ lldb_st5_i386,
+ lldb_st6_i386,
+ lldb_st7_i386,
+ lldb_mm0_i386,
+ lldb_mm1_i386,
+ lldb_mm2_i386,
+ lldb_mm3_i386,
+ lldb_mm4_i386,
+ lldb_mm5_i386,
+ lldb_mm6_i386,
+ lldb_mm7_i386,
+ lldb_xmm0_i386,
+ lldb_xmm1_i386,
+ lldb_xmm2_i386,
+ lldb_xmm3_i386,
+ lldb_xmm4_i386,
+ lldb_xmm5_i386,
+ lldb_xmm6_i386,
+ lldb_xmm7_i386,
+ k_last_fpr_i386 = lldb_xmm7_i386,
+
+ k_first_avx_i386,
+ lldb_ymm0_i386 = k_first_avx_i386,
+ lldb_ymm1_i386,
+ lldb_ymm2_i386,
+ lldb_ymm3_i386,
+ lldb_ymm4_i386,
+ lldb_ymm5_i386,
+ lldb_ymm6_i386,
+ lldb_ymm7_i386,
+ k_last_avx_i386 = lldb_ymm7_i386,
+
+ k_first_mpxr_i386,
+ lldb_bnd0_i386 = k_first_mpxr_i386,
+ lldb_bnd1_i386,
+ lldb_bnd2_i386,
+ lldb_bnd3_i386,
+ k_last_mpxr = lldb_bnd3_i386,
+
+ k_first_mpxc_i386,
+ lldb_bndcfgu_i386 = k_first_mpxc_i386,
+ lldb_bndstatus_i386,
+ k_last_mpxc_i386 = lldb_bndstatus_i386,
+
+ lldb_dr0_i386,
+ lldb_dr1_i386,
+ lldb_dr2_i386,
+ lldb_dr3_i386,
+ lldb_dr4_i386,
+ lldb_dr5_i386,
+ lldb_dr6_i386,
+ lldb_dr7_i386,
+
+ k_num_registers_i386,
+ k_num_gpr_registers_i386 = k_last_gpr_i386 - k_first_gpr_i386 + 1,
+ k_num_fpr_registers_i386 = k_last_fpr_i386 - k_first_fpr_i386 + 1,
+ k_num_avx_registers_i386 = k_last_avx_i386 - k_first_avx_i386 + 1,
+ k_num_mpx_registers_i386 = k_last_mpxc_i386 - k_first_mpxr_i386 + 1,
+ k_num_user_registers_i386 = k_num_gpr_registers_i386 +
+ k_num_fpr_registers_i386 +
+ k_num_avx_registers_i386 +
+ k_num_mpx_registers_i386,
+};
+
+// Internal codes for all x86_64 registers.
+enum {
+ k_first_gpr_x86_64,
+ lldb_rax_x86_64 = k_first_gpr_x86_64,
+ lldb_rbx_x86_64,
+ lldb_rcx_x86_64,
+ lldb_rdx_x86_64,
+ lldb_rdi_x86_64,
+ lldb_rsi_x86_64,
+ lldb_rbp_x86_64,
+ lldb_rsp_x86_64,
+ lldb_r8_x86_64,
+ lldb_r9_x86_64,
+ lldb_r10_x86_64,
+ lldb_r11_x86_64,
+ lldb_r12_x86_64,
+ lldb_r13_x86_64,
+ lldb_r14_x86_64,
+ lldb_r15_x86_64,
+ lldb_rip_x86_64,
+ lldb_rflags_x86_64,
+ lldb_cs_x86_64,
+ lldb_fs_x86_64,
+ lldb_gs_x86_64,
+ lldb_ss_x86_64,
+ lldb_ds_x86_64,
+ lldb_es_x86_64,
+
+ k_first_alias_x86_64,
+ lldb_eax_x86_64 = k_first_alias_x86_64,
+ lldb_ebx_x86_64,
+ lldb_ecx_x86_64,
+ lldb_edx_x86_64,
+ lldb_edi_x86_64,
+ lldb_esi_x86_64,
+ lldb_ebp_x86_64,
+ lldb_esp_x86_64,
+ lldb_r8d_x86_64, // Low 32 bits of r8
+ lldb_r9d_x86_64, // Low 32 bits of r9
+ lldb_r10d_x86_64, // Low 32 bits of r10
+ lldb_r11d_x86_64, // Low 32 bits of r11
+ lldb_r12d_x86_64, // Low 32 bits of r12
+ lldb_r13d_x86_64, // Low 32 bits of r13
+ lldb_r14d_x86_64, // Low 32 bits of r14
+ lldb_r15d_x86_64, // Low 32 bits of r15
+ lldb_ax_x86_64,
+ lldb_bx_x86_64,
+ lldb_cx_x86_64,
+ lldb_dx_x86_64,
+ lldb_di_x86_64,
+ lldb_si_x86_64,
+ lldb_bp_x86_64,
+ lldb_sp_x86_64,
+ lldb_r8w_x86_64, // Low 16 bits of r8
+ lldb_r9w_x86_64, // Low 16 bits of r9
+ lldb_r10w_x86_64, // Low 16 bits of r10
+ lldb_r11w_x86_64, // Low 16 bits of r11
+ lldb_r12w_x86_64, // Low 16 bits of r12
+ lldb_r13w_x86_64, // Low 16 bits of r13
+ lldb_r14w_x86_64, // Low 16 bits of r14
+ lldb_r15w_x86_64, // Low 16 bits of r15
+ lldb_ah_x86_64,
+ lldb_bh_x86_64,
+ lldb_ch_x86_64,
+ lldb_dh_x86_64,
+ lldb_al_x86_64,
+ lldb_bl_x86_64,
+ lldb_cl_x86_64,
+ lldb_dl_x86_64,
+ lldb_dil_x86_64,
+ lldb_sil_x86_64,
+ lldb_bpl_x86_64,
+ lldb_spl_x86_64,
+ lldb_r8l_x86_64, // Low 8 bits of r8
+ lldb_r9l_x86_64, // Low 8 bits of r9
+ lldb_r10l_x86_64, // Low 8 bits of r10
+ lldb_r11l_x86_64, // Low 8 bits of r11
+ lldb_r12l_x86_64, // Low 8 bits of r12
+ lldb_r13l_x86_64, // Low 8 bits of r13
+ lldb_r14l_x86_64, // Low 8 bits of r14
+ lldb_r15l_x86_64, // Low 8 bits of r15
+ k_last_alias_x86_64 = lldb_r15l_x86_64,
+
+ k_last_gpr_x86_64 = k_last_alias_x86_64,
+
+ k_first_fpr_x86_64,
+ lldb_fctrl_x86_64 = k_first_fpr_x86_64,
+ lldb_fstat_x86_64,
+ lldb_ftag_x86_64,
+ lldb_fop_x86_64,
+ lldb_fiseg_x86_64,
+ lldb_fioff_x86_64,
+ lldb_foseg_x86_64,
+ lldb_fooff_x86_64,
+ lldb_mxcsr_x86_64,
+ lldb_mxcsrmask_x86_64,
+ lldb_st0_x86_64,
+ lldb_st1_x86_64,
+ lldb_st2_x86_64,
+ lldb_st3_x86_64,
+ lldb_st4_x86_64,
+ lldb_st5_x86_64,
+ lldb_st6_x86_64,
+ lldb_st7_x86_64,
+ lldb_mm0_x86_64,
+ lldb_mm1_x86_64,
+ lldb_mm2_x86_64,
+ lldb_mm3_x86_64,
+ lldb_mm4_x86_64,
+ lldb_mm5_x86_64,
+ lldb_mm6_x86_64,
+ lldb_mm7_x86_64,
+ lldb_xmm0_x86_64,
+ lldb_xmm1_x86_64,
+ lldb_xmm2_x86_64,
+ lldb_xmm3_x86_64,
+ lldb_xmm4_x86_64,
+ lldb_xmm5_x86_64,
+ lldb_xmm6_x86_64,
+ lldb_xmm7_x86_64,
+ lldb_xmm8_x86_64,
+ lldb_xmm9_x86_64,
+ lldb_xmm10_x86_64,
+ lldb_xmm11_x86_64,
+ lldb_xmm12_x86_64,
+ lldb_xmm13_x86_64,
+ lldb_xmm14_x86_64,
+ lldb_xmm15_x86_64,
+ k_last_fpr_x86_64 = lldb_xmm15_x86_64,
+
+ k_first_avx_x86_64,
+ lldb_ymm0_x86_64 = k_first_avx_x86_64,
+ lldb_ymm1_x86_64,
+ lldb_ymm2_x86_64,
+ lldb_ymm3_x86_64,
+ lldb_ymm4_x86_64,
+ lldb_ymm5_x86_64,
+ lldb_ymm6_x86_64,
+ lldb_ymm7_x86_64,
+ lldb_ymm8_x86_64,
+ lldb_ymm9_x86_64,
+ lldb_ymm10_x86_64,
+ lldb_ymm11_x86_64,
+ lldb_ymm12_x86_64,
+ lldb_ymm13_x86_64,
+ lldb_ymm14_x86_64,
+ lldb_ymm15_x86_64,
+ k_last_avx_x86_64 = lldb_ymm15_x86_64,
+
+ k_first_mpxr_x86_64,
+ lldb_bnd0_x86_64 = k_first_mpxr_x86_64,
+ lldb_bnd1_x86_64,
+ lldb_bnd2_x86_64,
+ lldb_bnd3_x86_64,
+ k_last_mpxr_x86_64 = lldb_bnd3_x86_64,
+
+ k_first_mpxc_x86_64,
+ lldb_bndcfgu_x86_64 = k_first_mpxc_x86_64,
+ lldb_bndstatus_x86_64,
+ k_last_mpxc_x86_64 = lldb_bndstatus_x86_64,
+
+ lldb_dr0_x86_64,
+ lldb_dr1_x86_64,
+ lldb_dr2_x86_64,
+ lldb_dr3_x86_64,
+ lldb_dr4_x86_64,
+ lldb_dr5_x86_64,
+ lldb_dr6_x86_64,
+ lldb_dr7_x86_64,
+
+ k_num_registers_x86_64,
+ k_num_gpr_registers_x86_64 = k_last_gpr_x86_64 - k_first_gpr_x86_64 + 1,
+ k_num_fpr_registers_x86_64 = k_last_fpr_x86_64 - k_first_fpr_x86_64 + 1,
+ k_num_avx_registers_x86_64 = k_last_avx_x86_64 - k_first_avx_x86_64 + 1,
+ k_num_mpx_registers_x86_64 = k_last_mpxc_x86_64 - k_first_mpxr_x86_64 + 1,
+ k_num_user_registers_x86_64 = k_num_gpr_registers_x86_64 +
+ k_num_fpr_registers_x86_64 +
+ k_num_avx_registers_x86_64 +
+ k_num_mpx_registers_x86_64,
+};
+}
+
+#endif // #ifndef lldb_x86_register_enums_h
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
new file mode 100644
index 000000000000..980169e16c51
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
@@ -0,0 +1,915 @@
+//===-- ProcessElfCore.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 <stdlib.h>
+
+#include <memory>
+#include <mutex>
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+
+#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/Support/Threading.h"
+
+#include "Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h"
+#include "Plugins/ObjectFile/ELF/ObjectFileELF.h"
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+#include "ProcessElfCore.h"
+#include "ThreadElfCore.h"
+
+using namespace lldb_private;
+
+ConstString ProcessElfCore::GetPluginNameStatic() {
+ static ConstString g_name("elf-core");
+ return g_name;
+}
+
+const char *ProcessElfCore::GetPluginDescriptionStatic() {
+ return "ELF core dump plug-in.";
+}
+
+void ProcessElfCore::Terminate() {
+ PluginManager::UnregisterPlugin(ProcessElfCore::CreateInstance);
+}
+
+lldb::ProcessSP ProcessElfCore::CreateInstance(lldb::TargetSP target_sp,
+ lldb::ListenerSP listener_sp,
+ const FileSpec *crash_file) {
+ lldb::ProcessSP process_sp;
+ if (crash_file) {
+ // Read enough data for a ELF32 header or ELF64 header Note: Here we care
+ // about e_type field only, so it is safe to ignore possible presence of
+ // the header extension.
+ const size_t header_size = sizeof(llvm::ELF::Elf64_Ehdr);
+
+ auto data_sp = FileSystem::Instance().CreateDataBuffer(
+ crash_file->GetPath(), header_size, 0);
+ if (data_sp && data_sp->GetByteSize() == header_size &&
+ elf::ELFHeader::MagicBytesMatch(data_sp->GetBytes())) {
+ elf::ELFHeader elf_header;
+ DataExtractor data(data_sp, lldb::eByteOrderLittle, 4);
+ lldb::offset_t data_offset = 0;
+ if (elf_header.Parse(data, &data_offset)) {
+ if (elf_header.e_type == llvm::ELF::ET_CORE)
+ process_sp = std::make_shared<ProcessElfCore>(target_sp, listener_sp,
+ *crash_file);
+ }
+ }
+ }
+ return process_sp;
+}
+
+bool ProcessElfCore::CanDebug(lldb::TargetSP target_sp,
+ bool plugin_specified_by_name) {
+ // For now we are just making sure the file exists for a given module
+ if (!m_core_module_sp && FileSystem::Instance().Exists(m_core_file)) {
+ ModuleSpec core_module_spec(m_core_file, target_sp->GetArchitecture());
+ Status error(ModuleList::GetSharedModule(core_module_spec, m_core_module_sp,
+ nullptr, nullptr, nullptr));
+ if (m_core_module_sp) {
+ ObjectFile *core_objfile = m_core_module_sp->GetObjectFile();
+ if (core_objfile && core_objfile->GetType() == ObjectFile::eTypeCoreFile)
+ return true;
+ }
+ }
+ return false;
+}
+
+// ProcessElfCore constructor
+ProcessElfCore::ProcessElfCore(lldb::TargetSP target_sp,
+ lldb::ListenerSP listener_sp,
+ const FileSpec &core_file)
+ : Process(target_sp, listener_sp), m_core_file(core_file) {}
+
+// Destructor
+ProcessElfCore::~ProcessElfCore() {
+ Clear();
+ // We need to call finalize on the process before destroying ourselves to
+ // make sure all of the broadcaster cleanup goes as planned. If we destruct
+ // this class, then Process::~Process() might have problems trying to fully
+ // destroy the broadcaster.
+ Finalize();
+}
+
+// PluginInterface
+ConstString ProcessElfCore::GetPluginName() { return GetPluginNameStatic(); }
+
+uint32_t ProcessElfCore::GetPluginVersion() { return 1; }
+
+lldb::addr_t ProcessElfCore::AddAddressRangeFromLoadSegment(
+ const elf::ELFProgramHeader &header) {
+ const lldb::addr_t addr = header.p_vaddr;
+ FileRange file_range(header.p_offset, header.p_filesz);
+ VMRangeToFileOffset::Entry range_entry(addr, header.p_memsz, file_range);
+
+ VMRangeToFileOffset::Entry *last_entry = m_core_aranges.Back();
+ if (last_entry && last_entry->GetRangeEnd() == range_entry.GetRangeBase() &&
+ last_entry->data.GetRangeEnd() == range_entry.data.GetRangeBase() &&
+ last_entry->GetByteSize() == last_entry->data.GetByteSize()) {
+ last_entry->SetRangeEnd(range_entry.GetRangeEnd());
+ last_entry->data.SetRangeEnd(range_entry.data.GetRangeEnd());
+ } else {
+ m_core_aranges.Append(range_entry);
+ }
+
+ // Keep a separate map of permissions that that isn't coalesced so all ranges
+ // are maintained.
+ const uint32_t permissions =
+ ((header.p_flags & llvm::ELF::PF_R) ? lldb::ePermissionsReadable : 0u) |
+ ((header.p_flags & llvm::ELF::PF_W) ? lldb::ePermissionsWritable : 0u) |
+ ((header.p_flags & llvm::ELF::PF_X) ? lldb::ePermissionsExecutable : 0u);
+
+ m_core_range_infos.Append(
+ VMRangeToPermissions::Entry(addr, header.p_memsz, permissions));
+
+ return addr;
+}
+
+// Process Control
+Status ProcessElfCore::DoLoadCore() {
+ Status error;
+ if (!m_core_module_sp) {
+ error.SetErrorString("invalid core module");
+ return error;
+ }
+
+ ObjectFileELF *core = (ObjectFileELF *)(m_core_module_sp->GetObjectFile());
+ if (core == nullptr) {
+ error.SetErrorString("invalid core object file");
+ return error;
+ }
+
+ llvm::ArrayRef<elf::ELFProgramHeader> segments = core->ProgramHeaders();
+ if (segments.size() == 0) {
+ error.SetErrorString("core file has no segments");
+ return error;
+ }
+
+ SetCanJIT(false);
+
+ m_thread_data_valid = true;
+
+ bool ranges_are_sorted = true;
+ lldb::addr_t vm_addr = 0;
+ /// Walk through segments and Thread and Address Map information.
+ /// PT_NOTE - Contains Thread and Register information
+ /// PT_LOAD - Contains a contiguous range of Process Address Space
+ for (const elf::ELFProgramHeader &H : segments) {
+ DataExtractor data = core->GetSegmentData(H);
+
+ // Parse thread contexts and auxv structure
+ if (H.p_type == llvm::ELF::PT_NOTE) {
+ if (llvm::Error error = ParseThreadContextsFromNoteSegment(H, data))
+ return Status(std::move(error));
+ }
+ // PT_LOAD segments contains address map
+ if (H.p_type == llvm::ELF::PT_LOAD) {
+ lldb::addr_t last_addr = AddAddressRangeFromLoadSegment(H);
+ if (vm_addr > last_addr)
+ ranges_are_sorted = false;
+ vm_addr = last_addr;
+ }
+ }
+
+ if (!ranges_are_sorted) {
+ m_core_aranges.Sort();
+ m_core_range_infos.Sort();
+ }
+
+ // Even if the architecture is set in the target, we need to override it to
+ // match the core file which is always single arch.
+ ArchSpec arch(m_core_module_sp->GetArchitecture());
+
+ ArchSpec target_arch = GetTarget().GetArchitecture();
+ ArchSpec core_arch(m_core_module_sp->GetArchitecture());
+ target_arch.MergeFrom(core_arch);
+ GetTarget().SetArchitecture(target_arch);
+
+ SetUnixSignals(UnixSignals::Create(GetArchitecture()));
+
+ // Ensure we found at least one thread that was stopped on a signal.
+ bool siginfo_signal_found = false;
+ bool prstatus_signal_found = false;
+ // Check we found a signal in a SIGINFO note.
+ for (const auto &thread_data : m_thread_data) {
+ if (thread_data.signo != 0)
+ siginfo_signal_found = true;
+ if (thread_data.prstatus_sig != 0)
+ prstatus_signal_found = true;
+ }
+ if (!siginfo_signal_found) {
+ // If we don't have signal from SIGINFO use the signal from each threads
+ // PRSTATUS note.
+ if (prstatus_signal_found) {
+ for (auto &thread_data : m_thread_data)
+ thread_data.signo = thread_data.prstatus_sig;
+ } else if (m_thread_data.size() > 0) {
+ // If all else fails force the first thread to be SIGSTOP
+ m_thread_data.begin()->signo =
+ GetUnixSignals()->GetSignalNumberFromName("SIGSTOP");
+ }
+ }
+
+ // Core files are useless without the main executable. See if we can locate
+ // the main executable using data we found in the core file notes.
+ lldb::ModuleSP exe_module_sp = GetTarget().GetExecutableModule();
+ if (!exe_module_sp) {
+ // The first entry in the NT_FILE might be our executable
+ if (!m_nt_file_entries.empty()) {
+ ModuleSpec exe_module_spec;
+ exe_module_spec.GetArchitecture() = arch;
+ exe_module_spec.GetFileSpec().SetFile(
+ m_nt_file_entries[0].path.GetCString(), FileSpec::Style::native);
+ if (exe_module_spec.GetFileSpec()) {
+ exe_module_sp = GetTarget().GetOrCreateModule(exe_module_spec,
+ true /* notify */);
+ if (exe_module_sp)
+ GetTarget().SetExecutableModule(exe_module_sp, eLoadDependentsNo);
+ }
+ }
+ }
+ return error;
+}
+
+lldb_private::DynamicLoader *ProcessElfCore::GetDynamicLoader() {
+ if (m_dyld_up.get() == nullptr)
+ m_dyld_up.reset(DynamicLoader::FindPlugin(
+ this, DynamicLoaderPOSIXDYLD::GetPluginNameStatic().GetCString()));
+ return m_dyld_up.get();
+}
+
+bool ProcessElfCore::UpdateThreadList(ThreadList &old_thread_list,
+ ThreadList &new_thread_list) {
+ const uint32_t num_threads = GetNumThreadContexts();
+ if (!m_thread_data_valid)
+ return false;
+
+ for (lldb::tid_t tid = 0; tid < num_threads; ++tid) {
+ const ThreadData &td = m_thread_data[tid];
+ lldb::ThreadSP thread_sp(new ThreadElfCore(*this, td));
+ new_thread_list.AddThread(thread_sp);
+ }
+ return new_thread_list.GetSize(false) > 0;
+}
+
+void ProcessElfCore::RefreshStateAfterStop() {}
+
+Status ProcessElfCore::DoDestroy() { return Status(); }
+
+// Process Queries
+
+bool ProcessElfCore::IsAlive() { return true; }
+
+// Process Memory
+size_t ProcessElfCore::ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ Status &error) {
+ // Don't allow the caching that lldb_private::Process::ReadMemory does since
+ // in core files we have it all cached our our core file anyway.
+ return DoReadMemory(addr, buf, size, error);
+}
+
+Status ProcessElfCore::GetMemoryRegionInfo(lldb::addr_t load_addr,
+ MemoryRegionInfo &region_info) {
+ region_info.Clear();
+ const VMRangeToPermissions::Entry *permission_entry =
+ m_core_range_infos.FindEntryThatContainsOrFollows(load_addr);
+ if (permission_entry) {
+ if (permission_entry->Contains(load_addr)) {
+ region_info.GetRange().SetRangeBase(permission_entry->GetRangeBase());
+ region_info.GetRange().SetRangeEnd(permission_entry->GetRangeEnd());
+ const Flags permissions(permission_entry->data);
+ region_info.SetReadable(permissions.Test(lldb::ePermissionsReadable)
+ ? MemoryRegionInfo::eYes
+ : MemoryRegionInfo::eNo);
+ region_info.SetWritable(permissions.Test(lldb::ePermissionsWritable)
+ ? MemoryRegionInfo::eYes
+ : MemoryRegionInfo::eNo);
+ region_info.SetExecutable(permissions.Test(lldb::ePermissionsExecutable)
+ ? MemoryRegionInfo::eYes
+ : MemoryRegionInfo::eNo);
+ region_info.SetMapped(MemoryRegionInfo::eYes);
+ } else if (load_addr < permission_entry->GetRangeBase()) {
+ region_info.GetRange().SetRangeBase(load_addr);
+ region_info.GetRange().SetRangeEnd(permission_entry->GetRangeBase());
+ region_info.SetReadable(MemoryRegionInfo::eNo);
+ region_info.SetWritable(MemoryRegionInfo::eNo);
+ region_info.SetExecutable(MemoryRegionInfo::eNo);
+ region_info.SetMapped(MemoryRegionInfo::eNo);
+ }
+ return Status();
+ }
+
+ region_info.GetRange().SetRangeBase(load_addr);
+ region_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS);
+ region_info.SetReadable(MemoryRegionInfo::eNo);
+ region_info.SetWritable(MemoryRegionInfo::eNo);
+ region_info.SetExecutable(MemoryRegionInfo::eNo);
+ region_info.SetMapped(MemoryRegionInfo::eNo);
+ return Status();
+}
+
+size_t ProcessElfCore::DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ Status &error) {
+ ObjectFile *core_objfile = m_core_module_sp->GetObjectFile();
+
+ if (core_objfile == nullptr)
+ return 0;
+
+ // Get the address range
+ const VMRangeToFileOffset::Entry *address_range =
+ m_core_aranges.FindEntryThatContains(addr);
+ if (address_range == nullptr || address_range->GetRangeEnd() < addr) {
+ error.SetErrorStringWithFormat("core file does not contain 0x%" PRIx64,
+ addr);
+ return 0;
+ }
+
+ // Convert the address into core file offset
+ const lldb::addr_t offset = addr - address_range->GetRangeBase();
+ const lldb::addr_t file_start = address_range->data.GetRangeBase();
+ const lldb::addr_t file_end = address_range->data.GetRangeEnd();
+ size_t bytes_to_read = size; // Number of bytes to read from the core file
+ size_t bytes_copied = 0; // Number of bytes actually read from the core file
+ size_t zero_fill_size = 0; // Padding
+ lldb::addr_t bytes_left =
+ 0; // Number of bytes available in the core file from the given address
+
+ // Don't proceed if core file doesn't contain the actual data for this
+ // address range.
+ if (file_start == file_end)
+ return 0;
+
+ // Figure out how many on-disk bytes remain in this segment starting at the
+ // given offset
+ if (file_end > file_start + offset)
+ bytes_left = file_end - (file_start + offset);
+
+ // Figure out how many bytes we need to zero-fill if we are reading more
+ // bytes than available in the on-disk segment
+ if (bytes_to_read > bytes_left) {
+ zero_fill_size = bytes_to_read - bytes_left;
+ bytes_to_read = bytes_left;
+ }
+
+ // If there is data available on the core file read it
+ if (bytes_to_read)
+ bytes_copied =
+ core_objfile->CopyData(offset + file_start, bytes_to_read, buf);
+
+ assert(zero_fill_size <= size);
+ // Pad remaining bytes
+ if (zero_fill_size)
+ memset(((char *)buf) + bytes_copied, 0, zero_fill_size);
+
+ return bytes_copied + zero_fill_size;
+}
+
+void ProcessElfCore::Clear() {
+ m_thread_list.Clear();
+
+ SetUnixSignals(std::make_shared<UnixSignals>());
+}
+
+void ProcessElfCore::Initialize() {
+ static llvm::once_flag g_once_flag;
+
+ llvm::call_once(g_once_flag, []() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+ });
+}
+
+lldb::addr_t ProcessElfCore::GetImageInfoAddress() {
+ ObjectFile *obj_file = GetTarget().GetExecutableModule()->GetObjectFile();
+ Address addr = obj_file->GetImageInfoAddress(&GetTarget());
+
+ if (addr.IsValid())
+ return addr.GetLoadAddress(&GetTarget());
+ return LLDB_INVALID_ADDRESS;
+}
+
+// Parse a FreeBSD NT_PRSTATUS note - see FreeBSD sys/procfs.h for details.
+static void ParseFreeBSDPrStatus(ThreadData &thread_data,
+ const DataExtractor &data,
+ const ArchSpec &arch) {
+ lldb::offset_t offset = 0;
+ bool lp64 = (arch.GetMachine() == llvm::Triple::aarch64 ||
+ arch.GetMachine() == llvm::Triple::mips64 ||
+ arch.GetMachine() == llvm::Triple::ppc64 ||
+ arch.GetMachine() == llvm::Triple::x86_64);
+ int pr_version = data.GetU32(&offset);
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log) {
+ if (pr_version > 1)
+ log->Printf("FreeBSD PRSTATUS unexpected version %d", pr_version);
+ }
+
+ // Skip padding, pr_statussz, pr_gregsetsz, pr_fpregsetsz, pr_osreldate
+ if (lp64)
+ offset += 32;
+ else
+ offset += 16;
+
+ thread_data.signo = data.GetU32(&offset); // pr_cursig
+ thread_data.tid = data.GetU32(&offset); // pr_pid
+ if (lp64)
+ offset += 4;
+
+ size_t len = data.GetByteSize() - offset;
+ thread_data.gpregset = DataExtractor(data, offset, len);
+}
+
+static llvm::Error ParseNetBSDProcInfo(const DataExtractor &data,
+ uint32_t &cpi_nlwps,
+ uint32_t &cpi_signo,
+ uint32_t &cpi_siglwp,
+ uint32_t &cpi_pid) {
+ lldb::offset_t offset = 0;
+
+ uint32_t version = data.GetU32(&offset);
+ if (version != 1)
+ return llvm::make_error<llvm::StringError>(
+ "Error parsing NetBSD core(5) notes: Unsupported procinfo version",
+ llvm::inconvertibleErrorCode());
+
+ uint32_t cpisize = data.GetU32(&offset);
+ if (cpisize != NETBSD::NT_PROCINFO_SIZE)
+ return llvm::make_error<llvm::StringError>(
+ "Error parsing NetBSD core(5) notes: Unsupported procinfo size",
+ llvm::inconvertibleErrorCode());
+
+ cpi_signo = data.GetU32(&offset); /* killing signal */
+
+ offset += NETBSD::NT_PROCINFO_CPI_SIGCODE_SIZE;
+ offset += NETBSD::NT_PROCINFO_CPI_SIGPEND_SIZE;
+ offset += NETBSD::NT_PROCINFO_CPI_SIGMASK_SIZE;
+ offset += NETBSD::NT_PROCINFO_CPI_SIGIGNORE_SIZE;
+ offset += NETBSD::NT_PROCINFO_CPI_SIGCATCH_SIZE;
+ cpi_pid = data.GetU32(&offset);
+ offset += NETBSD::NT_PROCINFO_CPI_PPID_SIZE;
+ offset += NETBSD::NT_PROCINFO_CPI_PGRP_SIZE;
+ offset += NETBSD::NT_PROCINFO_CPI_SID_SIZE;
+ offset += NETBSD::NT_PROCINFO_CPI_RUID_SIZE;
+ offset += NETBSD::NT_PROCINFO_CPI_EUID_SIZE;
+ offset += NETBSD::NT_PROCINFO_CPI_SVUID_SIZE;
+ offset += NETBSD::NT_PROCINFO_CPI_RGID_SIZE;
+ offset += NETBSD::NT_PROCINFO_CPI_EGID_SIZE;
+ offset += NETBSD::NT_PROCINFO_CPI_SVGID_SIZE;
+ cpi_nlwps = data.GetU32(&offset); /* number of LWPs */
+
+ offset += NETBSD::NT_PROCINFO_CPI_NAME_SIZE;
+ cpi_siglwp = data.GetU32(&offset); /* LWP target of killing signal */
+
+ return llvm::Error::success();
+}
+
+static void ParseOpenBSDProcInfo(ThreadData &thread_data,
+ const DataExtractor &data) {
+ lldb::offset_t offset = 0;
+
+ int version = data.GetU32(&offset);
+ if (version != 1)
+ return;
+
+ offset += 4;
+ thread_data.signo = data.GetU32(&offset);
+}
+
+llvm::Expected<std::vector<CoreNote>>
+ProcessElfCore::parseSegment(const DataExtractor &segment) {
+ lldb::offset_t offset = 0;
+ std::vector<CoreNote> result;
+
+ while (offset < segment.GetByteSize()) {
+ ELFNote note = ELFNote();
+ if (!note.Parse(segment, &offset))
+ return llvm::make_error<llvm::StringError>(
+ "Unable to parse note segment", llvm::inconvertibleErrorCode());
+
+ size_t note_start = offset;
+ size_t note_size = llvm::alignTo(note.n_descsz, 4);
+ DataExtractor note_data(segment, note_start, note_size);
+
+ result.push_back({note, note_data});
+ offset += note_size;
+ }
+
+ return std::move(result);
+}
+
+llvm::Error ProcessElfCore::parseFreeBSDNotes(llvm::ArrayRef<CoreNote> notes) {
+ bool have_prstatus = false;
+ bool have_prpsinfo = false;
+ ThreadData thread_data;
+ for (const auto &note : notes) {
+ if (note.info.n_name != "FreeBSD")
+ continue;
+
+ if ((note.info.n_type == FREEBSD::NT_PRSTATUS && have_prstatus) ||
+ (note.info.n_type == FREEBSD::NT_PRPSINFO && have_prpsinfo)) {
+ assert(thread_data.gpregset.GetByteSize() > 0);
+ // Add the new thread to thread list
+ m_thread_data.push_back(thread_data);
+ thread_data = ThreadData();
+ have_prstatus = false;
+ have_prpsinfo = false;
+ }
+
+ switch (note.info.n_type) {
+ case FREEBSD::NT_PRSTATUS:
+ have_prstatus = true;
+ ParseFreeBSDPrStatus(thread_data, note.data, GetArchitecture());
+ break;
+ case FREEBSD::NT_PRPSINFO:
+ have_prpsinfo = true;
+ break;
+ case FREEBSD::NT_THRMISC: {
+ lldb::offset_t offset = 0;
+ thread_data.name = note.data.GetCStr(&offset, 20);
+ break;
+ }
+ case FREEBSD::NT_PROCSTAT_AUXV:
+ // FIXME: FreeBSD sticks an int at the beginning of the note
+ m_auxv = DataExtractor(note.data, 4, note.data.GetByteSize() - 4);
+ break;
+ default:
+ thread_data.notes.push_back(note);
+ break;
+ }
+ }
+ if (!have_prstatus) {
+ return llvm::make_error<llvm::StringError>(
+ "Could not find NT_PRSTATUS note in core file.",
+ llvm::inconvertibleErrorCode());
+ }
+ m_thread_data.push_back(thread_data);
+ return llvm::Error::success();
+}
+
+/// NetBSD specific Thread context from PT_NOTE segment
+///
+/// NetBSD ELF core files use notes to provide information about
+/// the process's state. The note name is "NetBSD-CORE" for
+/// information that is global to the process, and "NetBSD-CORE@nn",
+/// where "nn" is the lwpid of the LWP that the information belongs
+/// to (such as register state).
+///
+/// NetBSD uses the following note identifiers:
+///
+/// ELF_NOTE_NETBSD_CORE_PROCINFO (value 1)
+/// Note is a "netbsd_elfcore_procinfo" structure.
+/// ELF_NOTE_NETBSD_CORE_AUXV (value 2; since NetBSD 8.0)
+/// Note is an array of AuxInfo structures.
+///
+/// NetBSD also uses ptrace(2) request numbers (the ones that exist in
+/// machine-dependent space) to identify register info notes. The
+/// info in such notes is in the same format that ptrace(2) would
+/// export that information.
+///
+/// For more information see /usr/include/sys/exec_elf.h
+///
+llvm::Error ProcessElfCore::parseNetBSDNotes(llvm::ArrayRef<CoreNote> notes) {
+ ThreadData thread_data;
+ bool had_nt_regs = false;
+
+ // To be extracted from struct netbsd_elfcore_procinfo
+ // Used to sanity check of the LWPs of the process
+ uint32_t nlwps = 0;
+ uint32_t signo; // killing signal
+ uint32_t siglwp; // LWP target of killing signal
+ uint32_t pr_pid;
+
+ for (const auto &note : notes) {
+ llvm::StringRef name = note.info.n_name;
+
+ if (name == "NetBSD-CORE") {
+ if (note.info.n_type == NETBSD::NT_PROCINFO) {
+ llvm::Error error = ParseNetBSDProcInfo(note.data, nlwps, signo,
+ siglwp, pr_pid);
+ if (error)
+ return error;
+ SetID(pr_pid);
+ } else if (note.info.n_type == NETBSD::NT_AUXV) {
+ m_auxv = note.data;
+ }
+ } else if (name.consume_front("NetBSD-CORE@")) {
+ lldb::tid_t tid;
+ if (name.getAsInteger(10, tid))
+ return llvm::make_error<llvm::StringError>(
+ "Error parsing NetBSD core(5) notes: Cannot convert LWP ID "
+ "to integer",
+ llvm::inconvertibleErrorCode());
+
+ switch (GetArchitecture().GetMachine()) {
+ case llvm::Triple::aarch64: {
+ // Assume order PT_GETREGS, PT_GETFPREGS
+ if (note.info.n_type == NETBSD::AARCH64::NT_REGS) {
+ // If this is the next thread, push the previous one first.
+ if (had_nt_regs) {
+ m_thread_data.push_back(thread_data);
+ thread_data = ThreadData();
+ had_nt_regs = false;
+ }
+
+ thread_data.gpregset = note.data;
+ thread_data.tid = tid;
+ if (thread_data.gpregset.GetByteSize() == 0)
+ return llvm::make_error<llvm::StringError>(
+ "Could not find general purpose registers note in core file.",
+ llvm::inconvertibleErrorCode());
+ had_nt_regs = true;
+ } else if (note.info.n_type == NETBSD::AARCH64::NT_FPREGS) {
+ if (!had_nt_regs || tid != thread_data.tid)
+ return llvm::make_error<llvm::StringError>(
+ "Error parsing NetBSD core(5) notes: Unexpected order "
+ "of NOTEs PT_GETFPREG before PT_GETREG",
+ llvm::inconvertibleErrorCode());
+ thread_data.notes.push_back(note);
+ }
+ } break;
+ case llvm::Triple::x86_64: {
+ // Assume order PT_GETREGS, PT_GETFPREGS
+ if (note.info.n_type == NETBSD::AMD64::NT_REGS) {
+ // If this is the next thread, push the previous one first.
+ if (had_nt_regs) {
+ m_thread_data.push_back(thread_data);
+ thread_data = ThreadData();
+ had_nt_regs = false;
+ }
+
+ thread_data.gpregset = note.data;
+ thread_data.tid = tid;
+ if (thread_data.gpregset.GetByteSize() == 0)
+ return llvm::make_error<llvm::StringError>(
+ "Could not find general purpose registers note in core file.",
+ llvm::inconvertibleErrorCode());
+ had_nt_regs = true;
+ } else if (note.info.n_type == NETBSD::AMD64::NT_FPREGS) {
+ if (!had_nt_regs || tid != thread_data.tid)
+ return llvm::make_error<llvm::StringError>(
+ "Error parsing NetBSD core(5) notes: Unexpected order "
+ "of NOTEs PT_GETFPREG before PT_GETREG",
+ llvm::inconvertibleErrorCode());
+ thread_data.notes.push_back(note);
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // Push the last thread.
+ if (had_nt_regs)
+ m_thread_data.push_back(thread_data);
+
+ if (m_thread_data.empty())
+ return llvm::make_error<llvm::StringError>(
+ "Error parsing NetBSD core(5) notes: No threads information "
+ "specified in notes",
+ llvm::inconvertibleErrorCode());
+
+ if (m_thread_data.size() != nlwps)
+ return llvm::make_error<llvm::StringError>(
+ "Error parsing NetBSD core(5) notes: Mismatch between the number "
+ "of LWPs in netbsd_elfcore_procinfo and the number of LWPs specified "
+ "by MD notes",
+ llvm::inconvertibleErrorCode());
+
+ // Signal targeted at the whole process.
+ if (siglwp == 0) {
+ for (auto &data : m_thread_data)
+ data.signo = signo;
+ }
+ // Signal destined for a particular LWP.
+ else {
+ bool passed = false;
+
+ for (auto &data : m_thread_data) {
+ if (data.tid == siglwp) {
+ data.signo = signo;
+ passed = true;
+ break;
+ }
+ }
+
+ if (!passed)
+ return llvm::make_error<llvm::StringError>(
+ "Error parsing NetBSD core(5) notes: Signal passed to unknown LWP",
+ llvm::inconvertibleErrorCode());
+ }
+
+ return llvm::Error::success();
+}
+
+llvm::Error ProcessElfCore::parseOpenBSDNotes(llvm::ArrayRef<CoreNote> notes) {
+ ThreadData thread_data;
+ for (const auto &note : notes) {
+ // OpenBSD per-thread information is stored in notes named "OpenBSD@nnn" so
+ // match on the initial part of the string.
+ if (!llvm::StringRef(note.info.n_name).startswith("OpenBSD"))
+ continue;
+
+ switch (note.info.n_type) {
+ case OPENBSD::NT_PROCINFO:
+ ParseOpenBSDProcInfo(thread_data, note.data);
+ break;
+ case OPENBSD::NT_AUXV:
+ m_auxv = note.data;
+ break;
+ case OPENBSD::NT_REGS:
+ thread_data.gpregset = note.data;
+ break;
+ default:
+ thread_data.notes.push_back(note);
+ break;
+ }
+ }
+ if (thread_data.gpregset.GetByteSize() == 0) {
+ return llvm::make_error<llvm::StringError>(
+ "Could not find general purpose registers note in core file.",
+ llvm::inconvertibleErrorCode());
+ }
+ m_thread_data.push_back(thread_data);
+ return llvm::Error::success();
+}
+
+/// A description of a linux process usually contains the following NOTE
+/// entries:
+/// - NT_PRPSINFO - General process information like pid, uid, name, ...
+/// - NT_SIGINFO - Information about the signal that terminated the process
+/// - NT_AUXV - Process auxiliary vector
+/// - NT_FILE - Files mapped into memory
+///
+/// Additionally, for each thread in the process the core file will contain at
+/// least the NT_PRSTATUS note, containing the thread id and general purpose
+/// registers. It may include additional notes for other register sets (floating
+/// point and vector registers, ...). The tricky part here is that some of these
+/// notes have "CORE" in their owner fields, while other set it to "LINUX".
+llvm::Error ProcessElfCore::parseLinuxNotes(llvm::ArrayRef<CoreNote> notes) {
+ const ArchSpec &arch = GetArchitecture();
+ bool have_prstatus = false;
+ bool have_prpsinfo = false;
+ ThreadData thread_data;
+ for (const auto &note : notes) {
+ if (note.info.n_name != "CORE" && note.info.n_name != "LINUX")
+ continue;
+
+ if ((note.info.n_type == LINUX::NT_PRSTATUS && have_prstatus) ||
+ (note.info.n_type == LINUX::NT_PRPSINFO && have_prpsinfo)) {
+ assert(thread_data.gpregset.GetByteSize() > 0);
+ // Add the new thread to thread list
+ m_thread_data.push_back(thread_data);
+ thread_data = ThreadData();
+ have_prstatus = false;
+ have_prpsinfo = false;
+ }
+
+ switch (note.info.n_type) {
+ case LINUX::NT_PRSTATUS: {
+ have_prstatus = true;
+ ELFLinuxPrStatus prstatus;
+ Status status = prstatus.Parse(note.data, arch);
+ if (status.Fail())
+ return status.ToError();
+ thread_data.prstatus_sig = prstatus.pr_cursig;
+ thread_data.tid = prstatus.pr_pid;
+ uint32_t header_size = ELFLinuxPrStatus::GetSize(arch);
+ size_t len = note.data.GetByteSize() - header_size;
+ thread_data.gpregset = DataExtractor(note.data, header_size, len);
+ break;
+ }
+ case LINUX::NT_PRPSINFO: {
+ have_prpsinfo = true;
+ ELFLinuxPrPsInfo prpsinfo;
+ Status status = prpsinfo.Parse(note.data, arch);
+ if (status.Fail())
+ return status.ToError();
+ thread_data.name.assign (prpsinfo.pr_fname, strnlen (prpsinfo.pr_fname, sizeof (prpsinfo.pr_fname)));
+ SetID(prpsinfo.pr_pid);
+ break;
+ }
+ case LINUX::NT_SIGINFO: {
+ ELFLinuxSigInfo siginfo;
+ Status status = siginfo.Parse(note.data, arch);
+ if (status.Fail())
+ return status.ToError();
+ thread_data.signo = siginfo.si_signo;
+ break;
+ }
+ case LINUX::NT_FILE: {
+ m_nt_file_entries.clear();
+ lldb::offset_t offset = 0;
+ const uint64_t count = note.data.GetAddress(&offset);
+ note.data.GetAddress(&offset); // Skip page size
+ for (uint64_t i = 0; i < count; ++i) {
+ NT_FILE_Entry entry;
+ entry.start = note.data.GetAddress(&offset);
+ entry.end = note.data.GetAddress(&offset);
+ entry.file_ofs = note.data.GetAddress(&offset);
+ m_nt_file_entries.push_back(entry);
+ }
+ for (uint64_t i = 0; i < count; ++i) {
+ const char *path = note.data.GetCStr(&offset);
+ if (path && path[0])
+ m_nt_file_entries[i].path.SetCString(path);
+ }
+ break;
+ }
+ case LINUX::NT_AUXV:
+ m_auxv = note.data;
+ break;
+ default:
+ thread_data.notes.push_back(note);
+ break;
+ }
+ }
+ // Add last entry in the note section
+ if (have_prstatus)
+ m_thread_data.push_back(thread_data);
+ return llvm::Error::success();
+}
+
+/// Parse Thread context from PT_NOTE segment and store it in the thread list
+/// A note segment consists of one or more NOTE entries, but their types and
+/// meaning differ depending on the OS.
+llvm::Error ProcessElfCore::ParseThreadContextsFromNoteSegment(
+ const elf::ELFProgramHeader &segment_header, DataExtractor segment_data) {
+ assert(segment_header.p_type == llvm::ELF::PT_NOTE);
+
+ auto notes_or_error = parseSegment(segment_data);
+ if(!notes_or_error)
+ return notes_or_error.takeError();
+ switch (GetArchitecture().GetTriple().getOS()) {
+ case llvm::Triple::FreeBSD:
+ return parseFreeBSDNotes(*notes_or_error);
+ case llvm::Triple::Linux:
+ return parseLinuxNotes(*notes_or_error);
+ case llvm::Triple::NetBSD:
+ return parseNetBSDNotes(*notes_or_error);
+ case llvm::Triple::OpenBSD:
+ return parseOpenBSDNotes(*notes_or_error);
+ default:
+ return llvm::make_error<llvm::StringError>(
+ "Don't know how to parse core file. Unsupported OS.",
+ llvm::inconvertibleErrorCode());
+ }
+}
+
+uint32_t ProcessElfCore::GetNumThreadContexts() {
+ if (!m_thread_data_valid)
+ DoLoadCore();
+ return m_thread_data.size();
+}
+
+ArchSpec ProcessElfCore::GetArchitecture() {
+ ArchSpec arch = m_core_module_sp->GetObjectFile()->GetArchitecture();
+
+ ArchSpec target_arch = GetTarget().GetArchitecture();
+ arch.MergeFrom(target_arch);
+
+ // On MIPS there is no way to differentiate betwenn 32bit and 64bit core
+ // files and this information can't be merged in from the target arch so we
+ // fail back to unconditionally returning the target arch in this config.
+ if (target_arch.IsMIPS()) {
+ return target_arch;
+ }
+
+ return arch;
+}
+
+DataExtractor ProcessElfCore::GetAuxvData() {
+ const uint8_t *start = m_auxv.GetDataStart();
+ size_t len = m_auxv.GetByteSize();
+ lldb::DataBufferSP buffer(new lldb_private::DataBufferHeap(start, len));
+ return DataExtractor(buffer, GetByteOrder(), GetAddressByteSize());
+}
+
+bool ProcessElfCore::GetProcessInfo(ProcessInstanceInfo &info) {
+ info.Clear();
+ info.SetProcessID(GetID());
+ info.SetArchitecture(GetArchitecture());
+ lldb::ModuleSP module_sp = GetTarget().GetExecutableModule();
+ if (module_sp) {
+ const bool add_exe_file_as_first_arg = false;
+ info.SetExecutableFile(GetTarget().GetExecutableModule()->GetFileSpec(),
+ add_exe_file_as_first_arg);
+ }
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
new file mode 100644
index 000000000000..829fe9ac7199
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
@@ -0,0 +1,168 @@
+//===-- ProcessElfCore.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
+//
+// Notes about Linux Process core dumps:
+// 1) Linux core dump is stored as ELF file.
+// 2) The ELF file's PT_NOTE and PT_LOAD segments describes the program's
+// address space and thread contexts.
+// 3) PT_NOTE segment contains note entries which describes a thread context.
+// 4) PT_LOAD segment describes a valid contiguous range of process address
+// space.
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ProcessElfCore_h_
+#define liblldb_ProcessElfCore_h_
+
+#include <list>
+#include <vector>
+
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Status.h"
+
+#include "Plugins/ObjectFile/ELF/ELFHeader.h"
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+
+struct ThreadData;
+
+class ProcessElfCore : public lldb_private::Process {
+public:
+ // Constructors and Destructors
+ static lldb::ProcessSP
+ CreateInstance(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp,
+ const lldb_private::FileSpec *crash_file_path);
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ // Constructors and Destructors
+ ProcessElfCore(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp,
+ const lldb_private::FileSpec &core_file);
+
+ ~ProcessElfCore() override;
+
+ // Check if a given Process
+ bool CanDebug(lldb::TargetSP target_sp,
+ bool plugin_specified_by_name) override;
+
+ // Creating a new process, or attaching to an existing one
+ lldb_private::Status DoLoadCore() override;
+
+ lldb_private::DynamicLoader *GetDynamicLoader() override;
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ // Process Control
+ lldb_private::Status DoDestroy() override;
+
+ void RefreshStateAfterStop() override;
+
+ lldb_private::Status WillResume() override {
+ lldb_private::Status error;
+ error.SetErrorStringWithFormat(
+ "error: %s does not support resuming processes",
+ GetPluginName().GetCString());
+ return error;
+ }
+
+ // Process Queries
+ bool IsAlive() override;
+
+ bool WarnBeforeDetach() const override { return false; }
+
+ // Process Memory
+ size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ lldb_private::Status &error) override;
+
+ size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ lldb_private::Status &error) override;
+
+ lldb_private::Status
+ GetMemoryRegionInfo(lldb::addr_t load_addr,
+ lldb_private::MemoryRegionInfo &region_info) override;
+
+ lldb::addr_t GetImageInfoAddress() override;
+
+ lldb_private::ArchSpec GetArchitecture();
+
+ // Returns AUXV structure found in the core file
+ lldb_private::DataExtractor GetAuxvData() override;
+
+ bool GetProcessInfo(lldb_private::ProcessInstanceInfo &info) override;
+
+protected:
+ void Clear();
+
+ bool UpdateThreadList(lldb_private::ThreadList &old_thread_list,
+ lldb_private::ThreadList &new_thread_list) override;
+
+private:
+ struct NT_FILE_Entry {
+ lldb::addr_t start;
+ lldb::addr_t end;
+ lldb::addr_t file_ofs;
+ lldb_private::ConstString path;
+ };
+
+ // For ProcessElfCore only
+ typedef lldb_private::Range<lldb::addr_t, lldb::addr_t> FileRange;
+ typedef lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t, FileRange>
+ VMRangeToFileOffset;
+ typedef lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t, uint32_t>
+ VMRangeToPermissions;
+
+ lldb::ModuleSP m_core_module_sp;
+ lldb_private::FileSpec m_core_file;
+ std::string m_dyld_plugin_name;
+ DISALLOW_COPY_AND_ASSIGN(ProcessElfCore);
+
+ // True if m_thread_contexts contains valid entries
+ bool m_thread_data_valid = false;
+
+ // Contain thread data read from NOTE segments
+ std::vector<ThreadData> m_thread_data;
+
+ // AUXV structure found from the NOTE segment
+ lldb_private::DataExtractor m_auxv;
+
+ // Address ranges found in the core
+ VMRangeToFileOffset m_core_aranges;
+
+ // Permissions for all ranges
+ VMRangeToPermissions m_core_range_infos;
+
+ // NT_FILE entries found from the NOTE segment
+ std::vector<NT_FILE_Entry> m_nt_file_entries;
+
+ // Parse thread(s) data structures(prstatus, prpsinfo) from given NOTE segment
+ llvm::Error ParseThreadContextsFromNoteSegment(
+ const elf::ELFProgramHeader &segment_header,
+ lldb_private::DataExtractor segment_data);
+
+ // Returns number of thread contexts stored in the core file
+ uint32_t GetNumThreadContexts();
+
+ // Parse a contiguous address range of the process from LOAD segment
+ lldb::addr_t
+ AddAddressRangeFromLoadSegment(const elf::ELFProgramHeader &header);
+
+ llvm::Expected<std::vector<lldb_private::CoreNote>>
+ parseSegment(const lldb_private::DataExtractor &segment);
+ llvm::Error parseFreeBSDNotes(llvm::ArrayRef<lldb_private::CoreNote> notes);
+ llvm::Error parseNetBSDNotes(llvm::ArrayRef<lldb_private::CoreNote> notes);
+ llvm::Error parseOpenBSDNotes(llvm::ArrayRef<lldb_private::CoreNote> notes);
+ llvm::Error parseLinuxNotes(llvm::ArrayRef<lldb_private::CoreNote> notes);
+};
+
+#endif // liblldb_ProcessElfCore_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp
new file mode 100644
index 000000000000..fa05c457fc61
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp
@@ -0,0 +1,72 @@
+//===-- RegisterContextPOSIXCore_arm.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 "RegisterContextPOSIXCore_arm.h"
+
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include <memory>
+
+using namespace lldb_private;
+
+RegisterContextCorePOSIX_arm::RegisterContextCorePOSIX_arm(
+ Thread &thread, RegisterInfoInterface *register_info,
+ const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes)
+ : RegisterContextPOSIX_arm(thread, 0, register_info) {
+ m_gpr_buffer = std::make_shared<DataBufferHeap>(gpregset.GetDataStart(),
+ gpregset.GetByteSize());
+ m_gpr.SetData(m_gpr_buffer);
+ m_gpr.SetByteOrder(gpregset.GetByteOrder());
+}
+
+RegisterContextCorePOSIX_arm::~RegisterContextCorePOSIX_arm() {}
+
+bool RegisterContextCorePOSIX_arm::ReadGPR() { return true; }
+
+bool RegisterContextCorePOSIX_arm::ReadFPR() { return false; }
+
+bool RegisterContextCorePOSIX_arm::WriteGPR() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_arm::WriteFPR() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_arm::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value) {
+ lldb::offset_t offset = reg_info->byte_offset;
+ uint64_t v = m_gpr.GetMaxU64(&offset, reg_info->byte_size);
+ if (offset == reg_info->byte_offset + reg_info->byte_size) {
+ value = v;
+ return true;
+ }
+ return false;
+}
+
+bool RegisterContextCorePOSIX_arm::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_arm::WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &value) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_arm::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_arm::HardwareSingleStep(bool enable) {
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h
new file mode 100644
index 000000000000..adda43ebccbc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h
@@ -0,0 +1,53 @@
+//===-- RegisterContextPOSIXCore_arm.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 liblldb_RegisterContextCorePOSIX_arm_h_
+#define liblldb_RegisterContextCorePOSIX_arm_h_
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_arm.h"
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+
+class RegisterContextCorePOSIX_arm : public RegisterContextPOSIX_arm {
+public:
+ RegisterContextCorePOSIX_arm(
+ lldb_private::Thread &thread,
+ lldb_private::RegisterInfoInterface *register_info,
+ const lldb_private::DataExtractor &gpregset,
+ llvm::ArrayRef<lldb_private::CoreNote> notes);
+
+ ~RegisterContextCorePOSIX_arm() override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ bool HardwareSingleStep(bool enable) override;
+
+protected:
+ bool ReadGPR() override;
+
+ bool ReadFPR() override;
+
+ bool WriteGPR() override;
+
+ bool WriteFPR() override;
+
+private:
+ lldb::DataBufferSP m_gpr_buffer;
+ lldb_private::DataExtractor m_gpr;
+};
+
+#endif // liblldb_RegisterContextCorePOSIX_arm_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp
new file mode 100644
index 000000000000..e477c438ba12
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp
@@ -0,0 +1,73 @@
+//===-- RegisterContextPOSIXCore_arm64.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 "RegisterContextPOSIXCore_arm64.h"
+
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include <memory>
+
+using namespace lldb_private;
+
+RegisterContextCorePOSIX_arm64::RegisterContextCorePOSIX_arm64(
+ Thread &thread, RegisterInfoInterface *register_info,
+ const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes)
+ : RegisterContextPOSIX_arm64(thread, 0, register_info) {
+ m_gpr_buffer = std::make_shared<DataBufferHeap>(gpregset.GetDataStart(),
+ gpregset.GetByteSize());
+ m_gpr.SetData(m_gpr_buffer);
+ m_gpr.SetByteOrder(gpregset.GetByteOrder());
+}
+
+RegisterContextCorePOSIX_arm64::~RegisterContextCorePOSIX_arm64() {}
+
+bool RegisterContextCorePOSIX_arm64::ReadGPR() { return true; }
+
+bool RegisterContextCorePOSIX_arm64::ReadFPR() { return false; }
+
+bool RegisterContextCorePOSIX_arm64::WriteGPR() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_arm64::WriteFPR() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_arm64::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value) {
+ lldb::offset_t offset = reg_info->byte_offset;
+ uint64_t v = m_gpr.GetMaxU64(&offset, reg_info->byte_size);
+ if (offset == reg_info->byte_offset + reg_info->byte_size) {
+ value = v;
+ return true;
+ }
+ return false;
+}
+
+bool RegisterContextCorePOSIX_arm64::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_arm64::WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &value) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_arm64::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_arm64::HardwareSingleStep(bool enable) {
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h
new file mode 100644
index 000000000000..de6d819c29ce
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h
@@ -0,0 +1,53 @@
+//===-- RegisterContextPOSIXCore_arm64.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 liblldb_RegisterContextCorePOSIX_arm64_h_
+#define liblldb_RegisterContextCorePOSIX_arm64_h_
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_arm64.h"
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+
+class RegisterContextCorePOSIX_arm64 : public RegisterContextPOSIX_arm64 {
+public:
+ RegisterContextCorePOSIX_arm64(
+ lldb_private::Thread &thread,
+ lldb_private::RegisterInfoInterface *register_info,
+ const lldb_private::DataExtractor &gpregset,
+ llvm::ArrayRef<lldb_private::CoreNote> notes);
+
+ ~RegisterContextCorePOSIX_arm64() override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ bool HardwareSingleStep(bool enable) override;
+
+protected:
+ bool ReadGPR() override;
+
+ bool ReadFPR() override;
+
+ bool WriteGPR() override;
+
+ bool WriteFPR() override;
+
+private:
+ lldb::DataBufferSP m_gpr_buffer;
+ lldb_private::DataExtractor m_gpr;
+};
+
+#endif // liblldb_RegisterContextCorePOSIX_arm64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.cpp
new file mode 100644
index 000000000000..3601f3b3b216
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.cpp
@@ -0,0 +1,91 @@
+//===-- RegisterContextPOSIXCore_mips64.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 "RegisterContextPOSIXCore_mips64.h"
+
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include <memory>
+
+using namespace lldb_private;
+
+RegisterContextCorePOSIX_mips64::RegisterContextCorePOSIX_mips64(
+ Thread &thread, RegisterInfoInterface *register_info,
+ const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes)
+ : RegisterContextPOSIX_mips64(thread, 0, register_info) {
+ m_gpr_buffer = std::make_shared<DataBufferHeap>(gpregset.GetDataStart(),
+ gpregset.GetByteSize());
+ m_gpr.SetData(m_gpr_buffer);
+ m_gpr.SetByteOrder(gpregset.GetByteOrder());
+
+ DataExtractor fpregset = getRegset(
+ notes, register_info->GetTargetArchitecture().GetTriple(), FPR_Desc);
+ m_fpr_buffer = std::make_shared<DataBufferHeap>(fpregset.GetDataStart(),
+ fpregset.GetByteSize());
+ m_fpr.SetData(m_fpr_buffer);
+ m_fpr.SetByteOrder(fpregset.GetByteOrder());
+}
+
+RegisterContextCorePOSIX_mips64::~RegisterContextCorePOSIX_mips64() {}
+
+bool RegisterContextCorePOSIX_mips64::ReadGPR() { return true; }
+
+bool RegisterContextCorePOSIX_mips64::ReadFPR() { return false; }
+
+bool RegisterContextCorePOSIX_mips64::WriteGPR() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_mips64::WriteFPR() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_mips64::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value) {
+
+ lldb::offset_t offset = reg_info->byte_offset;
+ lldb_private::ArchSpec arch = m_register_info_up->GetTargetArchitecture();
+ uint64_t v;
+ if (IsGPR(reg_info->kinds[lldb::eRegisterKindLLDB])) {
+ if (reg_info->byte_size == 4 && !(arch.GetMachine() == llvm::Triple::mips64el))
+ // In case of 32bit core file, the register data are placed at 4 byte
+ // offset.
+ offset = offset / 2;
+ v = m_gpr.GetMaxU64(&offset, reg_info->byte_size);
+ value = v;
+ return true;
+ } else if (IsFPR(reg_info->kinds[lldb::eRegisterKindLLDB])) {
+ offset = offset - sizeof(GPR_linux_mips);
+ v =m_fpr.GetMaxU64(&offset, reg_info->byte_size);
+ value = v;
+ return true;
+ }
+ return false;
+}
+
+bool RegisterContextCorePOSIX_mips64::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_mips64::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue &value) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_mips64::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_mips64::HardwareSingleStep(bool enable) {
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.h
new file mode 100644
index 000000000000..999c9451573b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.h
@@ -0,0 +1,55 @@
+//===-- RegisterContextPOSIXCore_mips64.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 liblldb_RegisterContextCorePOSIX_mips64_h_
+#define liblldb_RegisterContextCorePOSIX_mips64_h_
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_mips64.h"
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+
+class RegisterContextCorePOSIX_mips64 : public RegisterContextPOSIX_mips64 {
+public:
+ RegisterContextCorePOSIX_mips64(
+ lldb_private::Thread &thread,
+ lldb_private::RegisterInfoInterface *register_info,
+ const lldb_private::DataExtractor &gpregset,
+ llvm::ArrayRef<lldb_private::CoreNote> notes);
+
+ ~RegisterContextCorePOSIX_mips64() override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ bool HardwareSingleStep(bool enable) override;
+
+protected:
+ bool ReadGPR() override;
+
+ bool ReadFPR() override;
+
+ bool WriteGPR() override;
+
+ bool WriteFPR() override;
+
+private:
+ lldb::DataBufferSP m_gpr_buffer;
+ lldb::DataBufferSP m_fpr_buffer;
+ lldb_private::DataExtractor m_gpr;
+ lldb_private::DataExtractor m_fpr;
+};
+
+#endif // liblldb_RegisterContextCorePOSIX_mips64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.cpp
new file mode 100644
index 000000000000..6984bf4ee9b8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.cpp
@@ -0,0 +1,111 @@
+//===-- RegisterContextPOSIXCore_powerpc.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 "RegisterContextPOSIXCore_powerpc.h"
+
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include <memory>
+
+using namespace lldb_private;
+
+RegisterContextCorePOSIX_powerpc::RegisterContextCorePOSIX_powerpc(
+ Thread &thread, RegisterInfoInterface *register_info,
+ const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes)
+ : RegisterContextPOSIX_powerpc(thread, 0, register_info) {
+ m_gpr_buffer = std::make_shared<DataBufferHeap>(gpregset.GetDataStart(),
+ gpregset.GetByteSize());
+ m_gpr.SetData(m_gpr_buffer);
+ m_gpr.SetByteOrder(gpregset.GetByteOrder());
+
+ ArchSpec arch = register_info->GetTargetArchitecture();
+ DataExtractor fpregset = getRegset(notes, arch.GetTriple(), FPR_Desc);
+ m_fpr_buffer = std::make_shared<DataBufferHeap>(fpregset.GetDataStart(),
+ fpregset.GetByteSize());
+ m_fpr.SetData(m_fpr_buffer);
+ m_fpr.SetByteOrder(fpregset.GetByteOrder());
+
+ DataExtractor vregset = getRegset(notes, arch.GetTriple(), PPC_VMX_Desc);
+ m_vec_buffer = std::make_shared<DataBufferHeap>(vregset.GetDataStart(),
+ vregset.GetByteSize());
+ m_vec.SetData(m_vec_buffer);
+ m_vec.SetByteOrder(vregset.GetByteOrder());
+}
+
+RegisterContextCorePOSIX_powerpc::~RegisterContextCorePOSIX_powerpc() {}
+
+bool RegisterContextCorePOSIX_powerpc::ReadGPR() { return true; }
+
+bool RegisterContextCorePOSIX_powerpc::ReadFPR() { return true; }
+
+bool RegisterContextCorePOSIX_powerpc::ReadVMX() { return true; }
+
+bool RegisterContextCorePOSIX_powerpc::WriteGPR() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_powerpc::WriteFPR() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_powerpc::WriteVMX() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_powerpc::ReadRegister(
+ const RegisterInfo *reg_info, RegisterValue &value) {
+ lldb::offset_t offset = reg_info->byte_offset;
+ if (IsFPR(reg_info->kinds[lldb::eRegisterKindLLDB])) {
+ uint64_t v = m_fpr.GetMaxU64(&offset, reg_info->byte_size);
+ if (offset == reg_info->byte_offset + reg_info->byte_size) {
+ value = v;
+ return true;
+ }
+ } else if (IsVMX(reg_info->kinds[lldb::eRegisterKindLLDB])) {
+ uint32_t v[4];
+ offset = m_vec.CopyData(offset, reg_info->byte_size, &v);
+ if (offset == reg_info->byte_size) {
+ value.SetBytes(v, reg_info->byte_size, m_vec.GetByteOrder());
+ return true;
+ }
+ } else {
+ uint64_t v = m_gpr.GetMaxU64(&offset, reg_info->byte_size);
+ if (offset == reg_info->byte_offset + reg_info->byte_size) {
+ if (reg_info->byte_size < sizeof(v))
+ value = (uint32_t)v;
+ else
+ value = v;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool RegisterContextCorePOSIX_powerpc::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_powerpc::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue &value) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_powerpc::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_powerpc::HardwareSingleStep(bool enable) {
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.h
new file mode 100644
index 000000000000..7684c0b31837
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.h
@@ -0,0 +1,60 @@
+//===-- RegisterContextPOSIXCore_powerpc.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 liblldb_RegisterContextCorePOSIX_powerpc_h_
+#define liblldb_RegisterContextCorePOSIX_powerpc_h_
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h"
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+#include "lldb/Utility/DataExtractor.h"
+
+class RegisterContextCorePOSIX_powerpc : public RegisterContextPOSIX_powerpc {
+public:
+ RegisterContextCorePOSIX_powerpc(
+ lldb_private::Thread &thread,
+ lldb_private::RegisterInfoInterface *register_info,
+ const lldb_private::DataExtractor &gpregset,
+ llvm::ArrayRef<lldb_private::CoreNote> notes);
+
+ ~RegisterContextCorePOSIX_powerpc() override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ bool HardwareSingleStep(bool enable) override;
+
+protected:
+ bool ReadGPR() override;
+
+ bool ReadFPR() override;
+
+ bool ReadVMX() override;
+
+ bool WriteGPR() override;
+
+ bool WriteFPR() override;
+
+ bool WriteVMX() override;
+
+private:
+ lldb::DataBufferSP m_gpr_buffer;
+ lldb::DataBufferSP m_fpr_buffer;
+ lldb::DataBufferSP m_vec_buffer;
+ lldb_private::DataExtractor m_gpr;
+ lldb_private::DataExtractor m_fpr;
+ lldb_private::DataExtractor m_vec;
+};
+
+#endif // liblldb_RegisterContextCorePOSIX_powerpc_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_ppc64le.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_ppc64le.cpp
new file mode 100644
index 000000000000..0eebf474f60e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_ppc64le.cpp
@@ -0,0 +1,133 @@
+//===-- RegisterContextPOSIXCore_ppc64le.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 "RegisterContextPOSIXCore_ppc64le.h"
+
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include "Plugins/Process/Utility/lldb-ppc64le-register-enums.h"
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+
+#include <memory>
+
+using namespace lldb_private;
+
+RegisterContextCorePOSIX_ppc64le::RegisterContextCorePOSIX_ppc64le(
+ Thread &thread, RegisterInfoInterface *register_info,
+ const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes)
+ : RegisterContextPOSIX_ppc64le(thread, 0, register_info) {
+ m_gpr_buffer = std::make_shared<DataBufferHeap>(gpregset.GetDataStart(),
+ gpregset.GetByteSize());
+ m_gpr.SetData(m_gpr_buffer);
+ m_gpr.SetByteOrder(gpregset.GetByteOrder());
+
+ ArchSpec arch = register_info->GetTargetArchitecture();
+ DataExtractor fpregset = getRegset(notes, arch.GetTriple(), FPR_Desc);
+ m_fpr_buffer = std::make_shared<DataBufferHeap>(fpregset.GetDataStart(),
+ fpregset.GetByteSize());
+ m_fpr.SetData(m_fpr_buffer);
+ m_fpr.SetByteOrder(fpregset.GetByteOrder());
+
+ DataExtractor vmxregset = getRegset(notes, arch.GetTriple(), PPC_VMX_Desc);
+ m_vmx_buffer = std::make_shared<DataBufferHeap>(vmxregset.GetDataStart(),
+ vmxregset.GetByteSize());
+ m_vmx.SetData(m_vmx_buffer);
+ m_vmx.SetByteOrder(vmxregset.GetByteOrder());
+
+ DataExtractor vsxregset = getRegset(notes, arch.GetTriple(), PPC_VSX_Desc);
+ m_vsx_buffer = std::make_shared<DataBufferHeap>(vsxregset.GetDataStart(),
+ vsxregset.GetByteSize());
+ m_vsx.SetData(m_vsx_buffer);
+ m_vsx.SetByteOrder(vsxregset.GetByteOrder());
+}
+
+size_t RegisterContextCorePOSIX_ppc64le::GetFPRSize() const {
+ return k_num_fpr_registers_ppc64le * sizeof(uint64_t);
+}
+
+size_t RegisterContextCorePOSIX_ppc64le::GetVMXSize() const {
+ return (k_num_vmx_registers_ppc64le - 1) * sizeof(uint64_t) * 2 +
+ sizeof(uint32_t);
+}
+
+size_t RegisterContextCorePOSIX_ppc64le::GetVSXSize() const {
+ return k_num_vsx_registers_ppc64le * sizeof(uint64_t) * 2;
+}
+
+bool RegisterContextCorePOSIX_ppc64le::ReadRegister(
+ const RegisterInfo *reg_info, RegisterValue &value) {
+ lldb::offset_t offset = reg_info->byte_offset;
+
+ if (IsFPR(reg_info->kinds[lldb::eRegisterKindLLDB])) {
+ uint64_t v;
+ offset -= GetGPRSize();
+ offset = m_fpr.CopyData(offset, reg_info->byte_size, &v);
+
+ if (offset == reg_info->byte_size) {
+ value.SetBytes(&v, reg_info->byte_size, m_fpr.GetByteOrder());
+ return true;
+ }
+ } else if (IsVMX(reg_info->kinds[lldb::eRegisterKindLLDB])) {
+ uint32_t v[4];
+ offset -= GetGPRSize() + GetFPRSize();
+ offset = m_vmx.CopyData(offset, reg_info->byte_size, &v);
+
+ if (offset == reg_info->byte_size) {
+ value.SetBytes(v, reg_info->byte_size, m_vmx.GetByteOrder());
+ return true;
+ }
+ } else if (IsVSX(reg_info->kinds[lldb::eRegisterKindLLDB])) {
+ uint32_t v[4];
+ lldb::offset_t tmp_offset;
+ offset -= GetGPRSize() + GetFPRSize() + GetVMXSize();
+
+ if (offset < GetVSXSize() / 2) {
+ tmp_offset = m_vsx.CopyData(offset / 2, reg_info->byte_size / 2, &v);
+
+ if (tmp_offset != reg_info->byte_size / 2) {
+ return false;
+ }
+
+ uint8_t *dst = (uint8_t *)&v + sizeof(uint64_t);
+ tmp_offset = m_fpr.CopyData(offset / 2, reg_info->byte_size / 2, dst);
+
+ if (tmp_offset != reg_info->byte_size / 2) {
+ return false;
+ }
+
+ value.SetBytes(&v, reg_info->byte_size, m_vsx.GetByteOrder());
+ return true;
+ } else {
+ offset =
+ m_vmx.CopyData(offset - GetVSXSize() / 2, reg_info->byte_size, &v);
+ if (offset == reg_info->byte_size) {
+ value.SetBytes(v, reg_info->byte_size, m_vmx.GetByteOrder());
+ return true;
+ }
+ }
+ } else {
+ uint64_t v = m_gpr.GetMaxU64(&offset, reg_info->byte_size);
+
+ if (offset == reg_info->byte_offset + reg_info->byte_size) {
+ if (reg_info->byte_size < sizeof(v))
+ value = (uint32_t)v;
+ else
+ value = v;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool RegisterContextCorePOSIX_ppc64le::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue &value) {
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_ppc64le.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_ppc64le.h
new file mode 100644
index 000000000000..6e01d23dd656
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_ppc64le.h
@@ -0,0 +1,48 @@
+//===-- RegisterContextPOSIXCore_ppc64le.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 liblldb_RegisterContextCorePOSIX_ppc64le_h_
+#define liblldb_RegisterContextCorePOSIX_ppc64le_h_
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.h"
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+#include "lldb/Utility/DataExtractor.h"
+
+class RegisterContextCorePOSIX_ppc64le : public RegisterContextPOSIX_ppc64le {
+public:
+ RegisterContextCorePOSIX_ppc64le(
+ lldb_private::Thread &thread,
+ lldb_private::RegisterInfoInterface *register_info,
+ const lldb_private::DataExtractor &gpregset,
+ llvm::ArrayRef<lldb_private::CoreNote> notes);
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+protected:
+ size_t GetFPRSize() const;
+
+ size_t GetVMXSize() const;
+
+ size_t GetVSXSize() const;
+
+private:
+ lldb::DataBufferSP m_gpr_buffer;
+ lldb::DataBufferSP m_fpr_buffer;
+ lldb::DataBufferSP m_vmx_buffer;
+ lldb::DataBufferSP m_vsx_buffer;
+ lldb_private::DataExtractor m_gpr;
+ lldb_private::DataExtractor m_fpr;
+ lldb_private::DataExtractor m_vmx;
+ lldb_private::DataExtractor m_vsx;
+};
+
+#endif // liblldb_RegisterContextCorePOSIX_ppc64le_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.cpp
new file mode 100644
index 000000000000..d84fc3e74395
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.cpp
@@ -0,0 +1,96 @@
+//===-- RegisterContextPOSIXCore_s390x.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 "RegisterContextPOSIXCore_s390x.h"
+
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include <memory>
+
+using namespace lldb_private;
+
+RegisterContextCorePOSIX_s390x::RegisterContextCorePOSIX_s390x(
+ Thread &thread, RegisterInfoInterface *register_info,
+ const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes)
+ : RegisterContextPOSIX_s390x(thread, 0, register_info) {
+ m_gpr_buffer = std::make_shared<DataBufferHeap>(gpregset.GetDataStart(),
+ gpregset.GetByteSize());
+ m_gpr.SetData(m_gpr_buffer);
+ m_gpr.SetByteOrder(gpregset.GetByteOrder());
+
+ DataExtractor fpregset = getRegset(
+ notes, register_info->GetTargetArchitecture().GetTriple(), FPR_Desc);
+ m_fpr_buffer = std::make_shared<DataBufferHeap>(fpregset.GetDataStart(),
+ fpregset.GetByteSize());
+ m_fpr.SetData(m_fpr_buffer);
+ m_fpr.SetByteOrder(fpregset.GetByteOrder());
+}
+
+RegisterContextCorePOSIX_s390x::~RegisterContextCorePOSIX_s390x() {}
+
+bool RegisterContextCorePOSIX_s390x::ReadGPR() { return true; }
+
+bool RegisterContextCorePOSIX_s390x::ReadFPR() { return true; }
+
+bool RegisterContextCorePOSIX_s390x::WriteGPR() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_s390x::WriteFPR() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_s390x::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value) {
+ const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+ if (reg == LLDB_INVALID_REGNUM)
+ return false;
+
+ if (IsGPR(reg)) {
+ lldb::offset_t offset = reg_info->byte_offset;
+ uint64_t v = m_gpr.GetMaxU64(&offset, reg_info->byte_size);
+ if (offset == reg_info->byte_offset + reg_info->byte_size) {
+ value.SetUInt(v, reg_info->byte_size);
+ return true;
+ }
+ }
+
+ if (IsFPR(reg)) {
+ lldb::offset_t offset = reg_info->byte_offset;
+ uint64_t v = m_fpr.GetMaxU64(&offset, reg_info->byte_size);
+ if (offset == reg_info->byte_offset + reg_info->byte_size) {
+ value.SetUInt(v, reg_info->byte_size);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool RegisterContextCorePOSIX_s390x::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_s390x::WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &value) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_s390x::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_s390x::HardwareSingleStep(bool enable) {
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.h
new file mode 100644
index 000000000000..729617649c4f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.h
@@ -0,0 +1,55 @@
+//===-- RegisterContextPOSIXCore_s390x.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 liblldb_RegisterContextCorePOSIX_s390x_h_
+#define liblldb_RegisterContextCorePOSIX_s390x_h_
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_s390x.h"
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+#include "lldb/Utility/DataExtractor.h"
+
+class RegisterContextCorePOSIX_s390x : public RegisterContextPOSIX_s390x {
+public:
+ RegisterContextCorePOSIX_s390x(
+ lldb_private::Thread &thread,
+ lldb_private::RegisterInfoInterface *register_info,
+ const lldb_private::DataExtractor &gpregset,
+ llvm::ArrayRef<lldb_private::CoreNote> notes);
+
+ ~RegisterContextCorePOSIX_s390x() override;
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ bool HardwareSingleStep(bool enable) override;
+
+protected:
+ bool ReadGPR() override;
+
+ bool ReadFPR() override;
+
+ bool WriteGPR() override;
+
+ bool WriteFPR() override;
+
+private:
+ lldb::DataBufferSP m_gpr_buffer;
+ lldb_private::DataExtractor m_gpr;
+
+ lldb::DataBufferSP m_fpr_buffer;
+ lldb_private::DataExtractor m_fpr;
+};
+
+#endif // liblldb_RegisterContextCorePOSIX_s390x_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.cpp
new file mode 100644
index 000000000000..4454857e1799
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.cpp
@@ -0,0 +1,99 @@
+//===-- RegisterContextPOSIXCore_x86_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
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextPOSIXCore_x86_64.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/RegisterValue.h"
+
+using namespace lldb_private;
+
+RegisterContextCorePOSIX_x86_64::RegisterContextCorePOSIX_x86_64(
+ Thread &thread, RegisterInfoInterface *register_info,
+ const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes)
+ : RegisterContextPOSIX_x86(thread, 0, register_info) {
+ size_t size, len;
+
+ size = GetGPRSize();
+ m_gpregset.reset(new uint8_t[size]);
+ len =
+ gpregset.ExtractBytes(0, size, lldb::eByteOrderLittle, m_gpregset.get());
+ if (len != size)
+ m_gpregset.reset();
+
+ DataExtractor fpregset = getRegset(
+ notes, register_info->GetTargetArchitecture().GetTriple(), FPR_Desc);
+ size = sizeof(FXSAVE);
+ m_fpregset.reset(new uint8_t[size]);
+ len =
+ fpregset.ExtractBytes(0, size, lldb::eByteOrderLittle, m_fpregset.get());
+ if (len != size)
+ m_fpregset.reset();
+}
+
+bool RegisterContextCorePOSIX_x86_64::ReadGPR() {
+ return m_gpregset != nullptr;
+}
+
+bool RegisterContextCorePOSIX_x86_64::ReadFPR() {
+ return m_fpregset != nullptr;
+}
+
+bool RegisterContextCorePOSIX_x86_64::WriteGPR() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_x86_64::WriteFPR() {
+ assert(0);
+ return false;
+}
+
+bool RegisterContextCorePOSIX_x86_64::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value) {
+ const uint8_t *src;
+ size_t offset;
+ const size_t fxsave_offset = reg_info->byte_offset - GetFXSAVEOffset();
+ // make the offset relative to the beginning of the FXSAVE structure because
+ // this is the data that we have (not the entire UserArea)
+
+ if (m_gpregset && reg_info->byte_offset < GetGPRSize()) {
+ src = m_gpregset.get();
+ offset = reg_info->byte_offset;
+ } else if (m_fpregset && fxsave_offset < sizeof(FXSAVE)) {
+ src = m_fpregset.get();
+ offset = fxsave_offset;
+ } else {
+ return false;
+ }
+
+ Status error;
+ value.SetFromMemoryData(reg_info, src + offset, reg_info->byte_size,
+ lldb::eByteOrderLittle, error);
+
+ return error.Success();
+}
+
+bool RegisterContextCorePOSIX_x86_64::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_x86_64::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue &value) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_x86_64::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ return false;
+}
+
+bool RegisterContextCorePOSIX_x86_64::HardwareSingleStep(bool enable) {
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.h
new file mode 100644
index 000000000000..f41991c240d2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.h
@@ -0,0 +1,49 @@
+//===-- RegisterContextPOSIXCore_x86_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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextCorePOSIX_x86_64_h_
+#define liblldb_RegisterContextCorePOSIX_x86_64_h_
+
+#include "Plugins/Process/Utility/RegisterContextPOSIX_x86.h"
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+
+class RegisterContextCorePOSIX_x86_64 : public RegisterContextPOSIX_x86 {
+public:
+ RegisterContextCorePOSIX_x86_64(
+ lldb_private::Thread &thread,
+ lldb_private::RegisterInfoInterface *register_info,
+ const lldb_private::DataExtractor &gpregset,
+ llvm::ArrayRef<lldb_private::CoreNote> notes);
+
+ bool ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &value) override;
+
+ bool WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ bool HardwareSingleStep(bool enable) override;
+
+protected:
+ bool ReadGPR() override;
+
+ bool ReadFPR() override;
+
+ bool WriteGPR() override;
+
+ bool WriteFPR() override;
+
+private:
+ std::unique_ptr<uint8_t[]> m_gpregset;
+ std::unique_ptr<uint8_t[]> m_fpregset;
+};
+
+#endif // liblldb_RegisterContextCorePOSIX_x86_64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterUtilities.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterUtilities.cpp
new file mode 100644
index 000000000000..c8829d612b31
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterUtilities.cpp
@@ -0,0 +1,38 @@
+//===-- RegisterUtilities.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 "Plugins/Process/elf-core/RegisterUtilities.h"
+#include "llvm/ADT/STLExtras.h"
+
+using namespace lldb_private;
+
+static llvm::Optional<uint32_t>
+getNoteType(const llvm::Triple &Triple,
+ llvm::ArrayRef<RegsetDesc> RegsetDescs) {
+ for (const auto &Entry : RegsetDescs) {
+ if (Entry.OS != Triple.getOS())
+ continue;
+ if (Entry.Arch != llvm::Triple::UnknownArch &&
+ Entry.Arch != Triple.getArch())
+ continue;
+ return Entry.Note;
+ }
+ return llvm::None;
+}
+
+DataExtractor lldb_private::getRegset(llvm::ArrayRef<CoreNote> Notes,
+ const llvm::Triple &Triple,
+ llvm::ArrayRef<RegsetDesc> RegsetDescs) {
+ auto TypeOr = getNoteType(Triple, RegsetDescs);
+ if (!TypeOr)
+ return DataExtractor();
+ uint32_t Type = *TypeOr;
+ auto Iter = llvm::find_if(
+ Notes, [Type](const CoreNote &Note) { return Note.info.n_type == Type; });
+ return Iter == Notes.end() ? DataExtractor() : Iter->data;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h
new file mode 100644
index 000000000000..d3b3373150f8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h
@@ -0,0 +1,147 @@
+//===-- RegisterUtilities.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_REGISTERUTILITIES_H
+#define LLDB_REGISTERUTILITIES_H
+
+#include "Plugins/ObjectFile/ELF/ObjectFileELF.h"
+#include "lldb/Utility/DataExtractor.h"
+
+namespace lldb_private {
+/// Core files PT_NOTE segment descriptor types
+
+namespace FREEBSD {
+enum {
+ NT_PRSTATUS = 1,
+ NT_FPREGSET,
+ NT_PRPSINFO,
+ NT_THRMISC = 7,
+ NT_PROCSTAT_AUXV = 16,
+ NT_PPC_VMX = 0x100
+};
+}
+
+namespace NETBSD {
+enum { NT_PROCINFO = 1, NT_AUXV = 2 };
+
+/* Size in bytes */
+enum { NT_PROCINFO_SIZE = 160 };
+
+/* Size in bytes */
+enum {
+ NT_PROCINFO_CPI_VERSION_SIZE = 4,
+ NT_PROCINFO_CPI_CPISIZE_SIZE = 4,
+ NT_PROCINFO_CPI_SIGNO_SIZE = 4,
+ NT_PROCINFO_CPI_SIGCODE_SIZE = 4,
+ NT_PROCINFO_CPI_SIGPEND_SIZE = 16,
+ NT_PROCINFO_CPI_SIGMASK_SIZE = 16,
+ NT_PROCINFO_CPI_SIGIGNORE_SIZE = 16,
+ NT_PROCINFO_CPI_SIGCATCH_SIZE = 16,
+ NT_PROCINFO_CPI_PID_SIZE = 4,
+ NT_PROCINFO_CPI_PPID_SIZE = 4,
+ NT_PROCINFO_CPI_PGRP_SIZE = 4,
+ NT_PROCINFO_CPI_SID_SIZE = 4,
+ NT_PROCINFO_CPI_RUID_SIZE = 4,
+ NT_PROCINFO_CPI_EUID_SIZE = 4,
+ NT_PROCINFO_CPI_SVUID_SIZE = 4,
+ NT_PROCINFO_CPI_RGID_SIZE = 4,
+ NT_PROCINFO_CPI_EGID_SIZE = 4,
+ NT_PROCINFO_CPI_SVGID_SIZE = 4,
+ NT_PROCINFO_CPI_NLWPS_SIZE = 4,
+ NT_PROCINFO_CPI_NAME_SIZE = 32,
+ NT_PROCINFO_CPI_SIGLWP_SIZE = 4,
+};
+
+namespace AARCH64 {
+enum { NT_REGS = 32, NT_FPREGS = 34 };
+}
+
+namespace AMD64 {
+enum { NT_REGS = 33, NT_FPREGS = 35 };
+}
+
+} // namespace NETBSD
+
+namespace OPENBSD {
+enum {
+ NT_PROCINFO = 10,
+ NT_AUXV = 11,
+ NT_REGS = 20,
+ NT_FPREGS = 21,
+};
+}
+
+namespace LINUX {
+enum {
+ NT_PRSTATUS = 1,
+ NT_FPREGSET,
+ NT_PRPSINFO,
+ NT_TASKSTRUCT,
+ NT_PLATFORM,
+ NT_AUXV,
+ NT_FILE = 0x46494c45,
+ NT_SIGINFO = 0x53494749,
+ NT_PPC_VMX = 0x100,
+ NT_PPC_VSX = 0x102,
+ NT_PRXFPREG = 0x46e62b7f,
+};
+}
+
+struct CoreNote {
+ ELFNote info;
+ DataExtractor data;
+};
+
+// A structure describing how to find a register set in a core file from a given
+// OS.
+struct RegsetDesc {
+ // OS to which this entry applies to. Must not be UnknownOS.
+ llvm::Triple::OSType OS;
+
+ // Architecture to which this entry applies to. Can be UnknownArch, in which
+ // case it applies to all architectures of a given OS.
+ llvm::Triple::ArchType Arch;
+
+ // The note type under which the register set can be found.
+ uint32_t Note;
+};
+
+// Returns the register set in Notes which corresponds to the specified Triple
+// according to the list of register set descriptions in RegsetDescs. The list
+// is scanned linearly, so you can use a more specific entry (e.g. linux-i386)
+// to override a more general entry (e.g. general linux), as long as you place
+// it earlier in the list. If a register set is not found, it returns an empty
+// DataExtractor.
+DataExtractor getRegset(llvm::ArrayRef<CoreNote> Notes,
+ const llvm::Triple &Triple,
+ llvm::ArrayRef<RegsetDesc> RegsetDescs);
+
+constexpr RegsetDesc FPR_Desc[] = {
+ {llvm::Triple::FreeBSD, llvm::Triple::UnknownArch, FREEBSD::NT_FPREGSET},
+ // In a i386 core file NT_FPREGSET is present, but it's not the result
+ // of the FXSAVE instruction like in 64 bit files.
+ // The result from FXSAVE is in NT_PRXFPREG for i386 core files
+ {llvm::Triple::Linux, llvm::Triple::x86, LINUX::NT_PRXFPREG},
+ {llvm::Triple::Linux, llvm::Triple::UnknownArch, LINUX::NT_FPREGSET},
+ {llvm::Triple::NetBSD, llvm::Triple::aarch64, NETBSD::AARCH64::NT_FPREGS},
+ {llvm::Triple::NetBSD, llvm::Triple::x86_64, NETBSD::AMD64::NT_FPREGS},
+ {llvm::Triple::OpenBSD, llvm::Triple::UnknownArch, OPENBSD::NT_FPREGS},
+};
+
+constexpr RegsetDesc PPC_VMX_Desc[] = {
+ {llvm::Triple::FreeBSD, llvm::Triple::UnknownArch, FREEBSD::NT_PPC_VMX},
+ {llvm::Triple::Linux, llvm::Triple::UnknownArch, LINUX::NT_PPC_VMX},
+};
+
+constexpr RegsetDesc PPC_VSX_Desc[] = {
+ {llvm::Triple::Linux, llvm::Triple::UnknownArch, LINUX::NT_PPC_VSX},
+};
+
+} // namespace lldb_private
+
+#endif // #ifndef LLDB_REGISTERUTILITIES_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp
new file mode 100644
index 000000000000..6ae93fd1ec87
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp
@@ -0,0 +1,442 @@
+//===-- ThreadElfCore.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 "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Unwind.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+
+#include "Plugins/Process/Utility/RegisterContextFreeBSD_i386.h"
+#include "Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h"
+#include "Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h"
+#include "Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h"
+#include "Plugins/Process/Utility/RegisterContextLinux_i386.h"
+#include "Plugins/Process/Utility/RegisterContextLinux_mips.h"
+#include "Plugins/Process/Utility/RegisterContextLinux_mips64.h"
+#ifdef LLDB_ENABLE_ALL
+#include "Plugins/Process/Utility/RegisterContextLinux_s390x.h"
+#endif // LLDB_ENABLE_ALL
+#include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h"
+#include "Plugins/Process/Utility/RegisterContextNetBSD_x86_64.h"
+#include "Plugins/Process/Utility/RegisterContextOpenBSD_i386.h"
+#include "Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.h"
+#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm.h"
+#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"
+#include "Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.h"
+#include "ProcessElfCore.h"
+#include "RegisterContextPOSIXCore_arm.h"
+#include "RegisterContextPOSIXCore_arm64.h"
+#include "RegisterContextPOSIXCore_mips64.h"
+#include "RegisterContextPOSIXCore_powerpc.h"
+#include "RegisterContextPOSIXCore_ppc64le.h"
+#ifdef LLDB_ENABLE_ALL
+#include "RegisterContextPOSIXCore_s390x.h"
+#endif // LLDB_ENABLE_ALL
+#include "RegisterContextPOSIXCore_x86_64.h"
+#include "ThreadElfCore.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Construct a Thread object with given data
+ThreadElfCore::ThreadElfCore(Process &process, const ThreadData &td)
+ : Thread(process, td.tid), m_thread_name(td.name), m_thread_reg_ctx_sp(),
+ m_signo(td.signo), m_gpregset_data(td.gpregset), m_notes(td.notes) {}
+
+ThreadElfCore::~ThreadElfCore() { DestroyThread(); }
+
+void ThreadElfCore::RefreshStateAfterStop() {
+ GetRegisterContext()->InvalidateIfNeeded(false);
+}
+
+RegisterContextSP ThreadElfCore::GetRegisterContext() {
+ if (!m_reg_context_sp) {
+ m_reg_context_sp = CreateRegisterContextForFrame(nullptr);
+ }
+ return m_reg_context_sp;
+}
+
+RegisterContextSP
+ThreadElfCore::CreateRegisterContextForFrame(StackFrame *frame) {
+ RegisterContextSP reg_ctx_sp;
+ uint32_t concrete_frame_idx = 0;
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ if (frame)
+ concrete_frame_idx = frame->GetConcreteFrameIndex();
+
+ if (concrete_frame_idx == 0) {
+ if (m_thread_reg_ctx_sp)
+ return m_thread_reg_ctx_sp;
+
+ ProcessElfCore *process = static_cast<ProcessElfCore *>(GetProcess().get());
+ ArchSpec arch = process->GetArchitecture();
+ RegisterInfoInterface *reg_interface = nullptr;
+
+ switch (arch.GetTriple().getOS()) {
+ case llvm::Triple::FreeBSD: {
+ switch (arch.GetMachine()) {
+ case llvm::Triple::aarch64:
+ reg_interface = new RegisterInfoPOSIX_arm64(arch);
+ break;
+ case llvm::Triple::arm:
+ reg_interface = new RegisterInfoPOSIX_arm(arch);
+ break;
+ case llvm::Triple::ppc:
+ reg_interface = new RegisterContextFreeBSD_powerpc32(arch);
+ break;
+ case llvm::Triple::ppc64:
+ reg_interface = new RegisterContextFreeBSD_powerpc64(arch);
+ break;
+ case llvm::Triple::mips64:
+ reg_interface = new RegisterContextFreeBSD_mips64(arch);
+ break;
+ case llvm::Triple::x86:
+ reg_interface = new RegisterContextFreeBSD_i386(arch);
+ break;
+ case llvm::Triple::x86_64:
+ reg_interface = new RegisterContextFreeBSD_x86_64(arch);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ case llvm::Triple::NetBSD: {
+ switch (arch.GetMachine()) {
+ case llvm::Triple::aarch64:
+ reg_interface = new RegisterInfoPOSIX_arm64(arch);
+ break;
+ case llvm::Triple::x86_64:
+ reg_interface = new RegisterContextNetBSD_x86_64(arch);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ case llvm::Triple::Linux: {
+ switch (arch.GetMachine()) {
+ case llvm::Triple::arm:
+ reg_interface = new RegisterInfoPOSIX_arm(arch);
+ break;
+ case llvm::Triple::aarch64:
+ reg_interface = new RegisterInfoPOSIX_arm64(arch);
+ break;
+ case llvm::Triple::mipsel:
+ case llvm::Triple::mips:
+ reg_interface = new RegisterContextLinux_mips(arch);
+ break;
+ case llvm::Triple::mips64el:
+ case llvm::Triple::mips64:
+ reg_interface = new RegisterContextLinux_mips64(arch);
+ break;
+ case llvm::Triple::ppc64le:
+ reg_interface = new RegisterInfoPOSIX_ppc64le(arch);
+ break;
+#ifdef LLDB_ENABLE_ALL
+ case llvm::Triple::systemz:
+ reg_interface = new RegisterContextLinux_s390x(arch);
+ break;
+#endif // LLDB_ENABLE_ALL
+ case llvm::Triple::x86:
+ reg_interface = new RegisterContextLinux_i386(arch);
+ break;
+ case llvm::Triple::x86_64:
+ reg_interface = new RegisterContextLinux_x86_64(arch);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ case llvm::Triple::OpenBSD: {
+ switch (arch.GetMachine()) {
+ case llvm::Triple::aarch64:
+ reg_interface = new RegisterInfoPOSIX_arm64(arch);
+ break;
+ case llvm::Triple::arm:
+ reg_interface = new RegisterInfoPOSIX_arm(arch);
+ break;
+ case llvm::Triple::x86:
+ reg_interface = new RegisterContextOpenBSD_i386(arch);
+ break;
+ case llvm::Triple::x86_64:
+ reg_interface = new RegisterContextOpenBSD_x86_64(arch);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (!reg_interface) {
+ if (log)
+ log->Printf("elf-core::%s:: Architecture(%d) or OS(%d) not supported",
+ __FUNCTION__, arch.GetMachine(), arch.GetTriple().getOS());
+ assert(false && "Architecture or OS not supported");
+ }
+
+ switch (arch.GetMachine()) {
+ case llvm::Triple::aarch64:
+ m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_arm64>(
+ *this, reg_interface, m_gpregset_data, m_notes);
+ break;
+ case llvm::Triple::arm:
+ m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_arm>(
+ *this, reg_interface, m_gpregset_data, m_notes);
+ break;
+ case llvm::Triple::mipsel:
+ case llvm::Triple::mips:
+ m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_mips64>(
+ *this, reg_interface, m_gpregset_data, m_notes);
+ break;
+ case llvm::Triple::mips64:
+ case llvm::Triple::mips64el:
+ m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_mips64>(
+ *this, reg_interface, m_gpregset_data, m_notes);
+ break;
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppc64:
+ m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_powerpc>(
+ *this, reg_interface, m_gpregset_data, m_notes);
+ break;
+ case llvm::Triple::ppc64le:
+ m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_ppc64le>(
+ *this, reg_interface, m_gpregset_data, m_notes);
+ break;
+#ifdef LLDB_ENABLE_ALL
+ case llvm::Triple::systemz:
+ m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_s390x>(
+ *this, reg_interface, m_gpregset_data, m_notes);
+ break;
+#endif // LLDB_ENABLE_ALL
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_x86_64>(
+ *this, reg_interface, m_gpregset_data, m_notes);
+ break;
+ default:
+ break;
+ }
+
+ reg_ctx_sp = m_thread_reg_ctx_sp;
+ } else {
+ Unwind *unwinder = GetUnwinder();
+ if (unwinder != nullptr)
+ reg_ctx_sp = unwinder->CreateRegisterContextForFrame(frame);
+ }
+ return reg_ctx_sp;
+}
+
+bool ThreadElfCore::CalculateStopInfo() {
+ ProcessSP process_sp(GetProcess());
+ if (process_sp) {
+ SetStopInfo(StopInfo::CreateStopReasonWithSignal(*this, m_signo));
+ return true;
+ }
+ return false;
+}
+
+// Parse PRSTATUS from NOTE entry
+ELFLinuxPrStatus::ELFLinuxPrStatus() {
+ memset(this, 0, sizeof(ELFLinuxPrStatus));
+}
+
+size_t ELFLinuxPrStatus::GetSize(const lldb_private::ArchSpec &arch) {
+ constexpr size_t mips_linux_pr_status_size_o32 = 96;
+ constexpr size_t mips_linux_pr_status_size_n32 = 72;
+ constexpr size_t num_ptr_size_members = 10;
+ if (arch.IsMIPS()) {
+ std::string abi = arch.GetTargetABI();
+ assert(!abi.empty() && "ABI is not set");
+ if (!abi.compare("n64"))
+ return sizeof(ELFLinuxPrStatus);
+ else if (!abi.compare("o32"))
+ return mips_linux_pr_status_size_o32;
+ // N32 ABI
+ return mips_linux_pr_status_size_n32;
+ }
+ switch (arch.GetCore()) {
+ case lldb_private::ArchSpec::eCore_x86_32_i386:
+ case lldb_private::ArchSpec::eCore_x86_32_i486:
+ return 72;
+ default:
+ if (arch.GetAddressByteSize() == 8)
+ return sizeof(ELFLinuxPrStatus);
+ else
+ return sizeof(ELFLinuxPrStatus) - num_ptr_size_members * 4;
+ }
+}
+
+Status ELFLinuxPrStatus::Parse(const DataExtractor &data,
+ const ArchSpec &arch) {
+ Status error;
+ if (GetSize(arch) > data.GetByteSize()) {
+ error.SetErrorStringWithFormat(
+ "NT_PRSTATUS size should be %zu, but the remaining bytes are: %" PRIu64,
+ GetSize(arch), data.GetByteSize());
+ return error;
+ }
+
+ // Read field by field to correctly account for endianess of both the core
+ // dump and the platform running lldb.
+ offset_t offset = 0;
+ si_signo = data.GetU32(&offset);
+ si_code = data.GetU32(&offset);
+ si_errno = data.GetU32(&offset);
+
+ pr_cursig = data.GetU16(&offset);
+ offset += 2; // pad
+
+ pr_sigpend = data.GetPointer(&offset);
+ pr_sighold = data.GetPointer(&offset);
+
+ pr_pid = data.GetU32(&offset);
+ pr_ppid = data.GetU32(&offset);
+ pr_pgrp = data.GetU32(&offset);
+ pr_sid = data.GetU32(&offset);
+
+ pr_utime.tv_sec = data.GetPointer(&offset);
+ pr_utime.tv_usec = data.GetPointer(&offset);
+
+ pr_stime.tv_sec = data.GetPointer(&offset);
+ pr_stime.tv_usec = data.GetPointer(&offset);
+
+ pr_cutime.tv_sec = data.GetPointer(&offset);
+ pr_cutime.tv_usec = data.GetPointer(&offset);
+
+ pr_cstime.tv_sec = data.GetPointer(&offset);
+ pr_cstime.tv_usec = data.GetPointer(&offset);
+
+ return error;
+}
+
+// Parse PRPSINFO from NOTE entry
+ELFLinuxPrPsInfo::ELFLinuxPrPsInfo() {
+ memset(this, 0, sizeof(ELFLinuxPrPsInfo));
+}
+
+size_t ELFLinuxPrPsInfo::GetSize(const lldb_private::ArchSpec &arch) {
+ constexpr size_t mips_linux_pr_psinfo_size_o32_n32 = 128;
+ if (arch.IsMIPS()) {
+ uint8_t address_byte_size = arch.GetAddressByteSize();
+ if (address_byte_size == 8)
+ return sizeof(ELFLinuxPrPsInfo);
+ return mips_linux_pr_psinfo_size_o32_n32;
+ }
+
+ switch (arch.GetCore()) {
+ case lldb_private::ArchSpec::eCore_s390x_generic:
+ case lldb_private::ArchSpec::eCore_x86_64_x86_64:
+ return sizeof(ELFLinuxPrPsInfo);
+ case lldb_private::ArchSpec::eCore_x86_32_i386:
+ case lldb_private::ArchSpec::eCore_x86_32_i486:
+ return 124;
+ default:
+ return 0;
+ }
+}
+
+Status ELFLinuxPrPsInfo::Parse(const DataExtractor &data,
+ const ArchSpec &arch) {
+ Status error;
+ ByteOrder byteorder = data.GetByteOrder();
+ if (GetSize(arch) > data.GetByteSize()) {
+ error.SetErrorStringWithFormat(
+ "NT_PRPSINFO size should be %zu, but the remaining bytes are: %" PRIu64,
+ GetSize(arch), data.GetByteSize());
+ return error;
+ }
+ size_t size = 0;
+ offset_t offset = 0;
+
+ pr_state = data.GetU8(&offset);
+ pr_sname = data.GetU8(&offset);
+ pr_zomb = data.GetU8(&offset);
+ pr_nice = data.GetU8(&offset);
+ if (data.GetAddressByteSize() == 8) {
+ // Word align the next field on 64 bit.
+ offset += 4;
+ }
+
+ pr_flag = data.GetPointer(&offset);
+
+ if (arch.IsMIPS()) {
+ // The pr_uid and pr_gid is always 32 bit irrespective of platforms
+ pr_uid = data.GetU32(&offset);
+ pr_gid = data.GetU32(&offset);
+ } else {
+ // 16 bit on 32 bit platforms, 32 bit on 64 bit platforms
+ pr_uid = data.GetMaxU64(&offset, data.GetAddressByteSize() >> 1);
+ pr_gid = data.GetMaxU64(&offset, data.GetAddressByteSize() >> 1);
+ }
+
+ pr_pid = data.GetU32(&offset);
+ pr_ppid = data.GetU32(&offset);
+ pr_pgrp = data.GetU32(&offset);
+ pr_sid = data.GetU32(&offset);
+
+ size = 16;
+ data.ExtractBytes(offset, size, byteorder, pr_fname);
+ offset += size;
+
+ size = 80;
+ data.ExtractBytes(offset, size, byteorder, pr_psargs);
+ offset += size;
+
+ return error;
+}
+
+// Parse SIGINFO from NOTE entry
+ELFLinuxSigInfo::ELFLinuxSigInfo() { memset(this, 0, sizeof(ELFLinuxSigInfo)); }
+
+size_t ELFLinuxSigInfo::GetSize(const lldb_private::ArchSpec &arch) {
+ if (arch.IsMIPS())
+ return sizeof(ELFLinuxSigInfo);
+ switch (arch.GetCore()) {
+ case lldb_private::ArchSpec::eCore_x86_64_x86_64:
+ return sizeof(ELFLinuxSigInfo);
+ case lldb_private::ArchSpec::eCore_s390x_generic:
+ case lldb_private::ArchSpec::eCore_x86_32_i386:
+ case lldb_private::ArchSpec::eCore_x86_32_i486:
+ return 12;
+ default:
+ return 0;
+ }
+}
+
+Status ELFLinuxSigInfo::Parse(const DataExtractor &data, const ArchSpec &arch) {
+ Status error;
+ if (GetSize(arch) > data.GetByteSize()) {
+ error.SetErrorStringWithFormat(
+ "NT_SIGINFO size should be %zu, but the remaining bytes are: %" PRIu64,
+ GetSize(arch), data.GetByteSize());
+ return error;
+ }
+
+ // Parsing from a 32 bit ELF core file, and populating/reusing the structure
+ // properly, because the struct is for the 64 bit version
+ offset_t offset = 0;
+ si_signo = data.GetU32(&offset);
+ si_code = data.GetU32(&offset);
+ si_errno = data.GetU32(&offset);
+
+ return error;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ThreadElfCore.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ThreadElfCore.h
new file mode 100644
index 000000000000..ddcf35013b34
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ThreadElfCore.h
@@ -0,0 +1,176 @@
+//===-- ThreadElfCore.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 liblldb_ThreadElfCore_h_
+#define liblldb_ThreadElfCore_h_
+
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "llvm/ADT/DenseMap.h"
+#include <string>
+
+struct compat_timeval {
+ alignas(8) uint64_t tv_sec;
+ alignas(8) uint64_t tv_usec;
+};
+
+// PRSTATUS structure's size differs based on architecture.
+// This is the layout in the x86-64 arch.
+// In the i386 case we parse it manually and fill it again
+// in the same structure
+// The gp registers are also a part of this struct, but they are handled
+// separately
+
+#undef si_signo
+#undef si_code
+#undef si_errno
+
+struct ELFLinuxPrStatus {
+ int32_t si_signo;
+ int32_t si_code;
+ int32_t si_errno;
+
+ int16_t pr_cursig;
+
+ alignas(8) uint64_t pr_sigpend;
+ alignas(8) uint64_t pr_sighold;
+
+ uint32_t pr_pid;
+ uint32_t pr_ppid;
+ uint32_t pr_pgrp;
+ uint32_t pr_sid;
+
+ compat_timeval pr_utime;
+ compat_timeval pr_stime;
+ compat_timeval pr_cutime;
+ compat_timeval pr_cstime;
+
+ ELFLinuxPrStatus();
+
+ lldb_private::Status Parse(const lldb_private::DataExtractor &data,
+ const lldb_private::ArchSpec &arch);
+
+ // Return the bytesize of the structure
+ // 64 bit - just sizeof
+ // 32 bit - hardcoded because we are reusing the struct, but some of the
+ // members are smaller -
+ // so the layout is not the same
+ static size_t GetSize(const lldb_private::ArchSpec &arch);
+};
+
+static_assert(sizeof(ELFLinuxPrStatus) == 112,
+ "sizeof ELFLinuxPrStatus is not correct!");
+
+struct ELFLinuxSigInfo {
+ int32_t si_signo;
+ int32_t si_code;
+ int32_t si_errno;
+
+ ELFLinuxSigInfo();
+
+ lldb_private::Status Parse(const lldb_private::DataExtractor &data,
+ const lldb_private::ArchSpec &arch);
+
+ // Return the bytesize of the structure
+ // 64 bit - just sizeof
+ // 32 bit - hardcoded because we are reusing the struct, but some of the
+ // members are smaller -
+ // so the layout is not the same
+ static size_t GetSize(const lldb_private::ArchSpec &arch);
+};
+
+static_assert(sizeof(ELFLinuxSigInfo) == 12,
+ "sizeof ELFLinuxSigInfo is not correct!");
+
+// PRPSINFO structure's size differs based on architecture.
+// This is the layout in the x86-64 arch case.
+// In the i386 case we parse it manually and fill it again
+// in the same structure
+struct ELFLinuxPrPsInfo {
+ char pr_state;
+ char pr_sname;
+ char pr_zomb;
+ char pr_nice;
+ alignas(8) uint64_t pr_flag;
+ uint32_t pr_uid;
+ uint32_t pr_gid;
+ int32_t pr_pid;
+ int32_t pr_ppid;
+ int32_t pr_pgrp;
+ int32_t pr_sid;
+ char pr_fname[16];
+ char pr_psargs[80];
+
+ ELFLinuxPrPsInfo();
+
+ lldb_private::Status Parse(const lldb_private::DataExtractor &data,
+ const lldb_private::ArchSpec &arch);
+
+ // Return the bytesize of the structure
+ // 64 bit - just sizeof
+ // 32 bit - hardcoded because we are reusing the struct, but some of the
+ // members are smaller -
+ // so the layout is not the same
+ static size_t GetSize(const lldb_private::ArchSpec &arch);
+};
+
+static_assert(sizeof(ELFLinuxPrPsInfo) == 136,
+ "sizeof ELFLinuxPrPsInfo is not correct!");
+
+struct ThreadData {
+ lldb_private::DataExtractor gpregset;
+ std::vector<lldb_private::CoreNote> notes;
+ lldb::tid_t tid;
+ int signo = 0;
+ int prstatus_sig = 0;
+ std::string name;
+};
+
+class ThreadElfCore : public lldb_private::Thread {
+public:
+ ThreadElfCore(lldb_private::Process &process, const ThreadData &td);
+
+ ~ThreadElfCore() override;
+
+ void RefreshStateAfterStop() override;
+
+ lldb::RegisterContextSP GetRegisterContext() override;
+
+ lldb::RegisterContextSP
+ CreateRegisterContextForFrame(lldb_private::StackFrame *frame) override;
+
+ static bool ThreadIDIsValid(lldb::tid_t thread) { return thread != 0; }
+
+ const char *GetName() override {
+ if (m_thread_name.empty())
+ return nullptr;
+ return m_thread_name.c_str();
+ }
+
+ void SetName(const char *name) override {
+ if (name && name[0])
+ m_thread_name.assign(name);
+ else
+ m_thread_name.clear();
+ }
+
+protected:
+ // Member variables.
+ std::string m_thread_name;
+ lldb::RegisterContextSP m_thread_reg_ctx_sp;
+
+ int m_signo;
+
+ lldb_private::DataExtractor m_gpregset_data;
+ std::vector<lldb_private::CoreNote> m_notes;
+
+ bool CalculateStopInfo() override;
+};
+
+#endif // liblldb_ThreadElfCore_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
new file mode 100644
index 000000000000..fe7ef6b3acea
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
@@ -0,0 +1,394 @@
+//===-- GDBRemoteClientBase.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 "GDBRemoteClientBase.h"
+
+#include "llvm/ADT/StringExtras.h"
+
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/LLDBAssert.h"
+
+#include "ProcessGDBRemoteLog.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+using namespace std::chrono;
+
+static const seconds kInterruptTimeout(5);
+
+/////////////////////////
+// GDBRemoteClientBase //
+/////////////////////////
+
+GDBRemoteClientBase::ContinueDelegate::~ContinueDelegate() = default;
+
+GDBRemoteClientBase::GDBRemoteClientBase(const char *comm_name,
+ const char *listener_name)
+ : GDBRemoteCommunication(comm_name, listener_name), m_async_count(0),
+ m_is_running(false), m_should_stop(false) {}
+
+StateType GDBRemoteClientBase::SendContinuePacketAndWaitForResponse(
+ ContinueDelegate &delegate, const UnixSignals &signals,
+ llvm::StringRef payload, StringExtractorGDBRemote &response) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ response.Clear();
+
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ m_continue_packet = payload;
+ m_should_stop = false;
+ }
+ ContinueLock cont_lock(*this);
+ if (!cont_lock)
+ return eStateInvalid;
+ OnRunPacketSent(true);
+
+ for (;;) {
+ PacketResult read_result = ReadPacket(response, kInterruptTimeout, false);
+ switch (read_result) {
+ case PacketResult::ErrorReplyTimeout: {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ if (m_async_count == 0)
+ continue;
+ if (steady_clock::now() >= m_interrupt_time + kInterruptTimeout)
+ return eStateInvalid;
+ break;
+ }
+ case PacketResult::Success:
+ break;
+ default:
+ if (log)
+ log->Printf("GDBRemoteClientBase::%s () ReadPacket(...) => false",
+ __FUNCTION__);
+ return eStateInvalid;
+ }
+ if (response.Empty())
+ return eStateInvalid;
+
+ const char stop_type = response.GetChar();
+ if (log)
+ log->Printf("GDBRemoteClientBase::%s () got packet: %s", __FUNCTION__,
+ response.GetStringRef().c_str());
+
+ switch (stop_type) {
+ case 'W':
+ case 'X':
+ return eStateExited;
+ case 'E':
+ // ERROR
+ return eStateInvalid;
+ default:
+ if (log)
+ log->Printf("GDBRemoteClientBase::%s () unrecognized async packet",
+ __FUNCTION__);
+ return eStateInvalid;
+ case 'O': {
+ std::string inferior_stdout;
+ response.GetHexByteString(inferior_stdout);
+ delegate.HandleAsyncStdout(inferior_stdout);
+ break;
+ }
+ case 'A':
+ delegate.HandleAsyncMisc(
+ llvm::StringRef(response.GetStringRef()).substr(1));
+ break;
+ case 'J':
+ delegate.HandleAsyncStructuredDataPacket(response.GetStringRef());
+ break;
+ case 'T':
+ case 'S':
+ // Do this with the continue lock held.
+ const bool should_stop = ShouldStop(signals, response);
+ response.SetFilePos(0);
+
+ // The packet we should resume with. In the future we should check our
+ // thread list and "do the right thing" for new threads that show up
+ // while we stop and run async packets. Setting the packet to 'c' to
+ // continue all threads is the right thing to do 99.99% of the time
+ // because if a thread was single stepping, and we sent an interrupt, we
+ // will notice above that we didn't stop due to an interrupt but stopped
+ // due to stepping and we would _not_ continue. This packet may get
+ // modified by the async actions (e.g. to send a signal).
+ m_continue_packet = 'c';
+ cont_lock.unlock();
+
+ delegate.HandleStopReply();
+ if (should_stop)
+ return eStateStopped;
+
+ switch (cont_lock.lock()) {
+ case ContinueLock::LockResult::Success:
+ break;
+ case ContinueLock::LockResult::Failed:
+ return eStateInvalid;
+ case ContinueLock::LockResult::Cancelled:
+ return eStateStopped;
+ }
+ OnRunPacketSent(false);
+ break;
+ }
+ }
+}
+
+bool GDBRemoteClientBase::SendAsyncSignal(int signo) {
+ Lock lock(*this, true);
+ if (!lock || !lock.DidInterrupt())
+ return false;
+
+ m_continue_packet = 'C';
+ m_continue_packet += llvm::hexdigit((signo / 16) % 16);
+ m_continue_packet += llvm::hexdigit(signo % 16);
+ return true;
+}
+
+bool GDBRemoteClientBase::Interrupt() {
+ Lock lock(*this, true);
+ if (!lock.DidInterrupt())
+ return false;
+ m_should_stop = true;
+ return true;
+}
+GDBRemoteCommunication::PacketResult
+GDBRemoteClientBase::SendPacketAndWaitForResponse(
+ llvm::StringRef payload, StringExtractorGDBRemote &response,
+ bool send_async) {
+ Lock lock(*this, send_async);
+ if (!lock) {
+ if (Log *log =
+ ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS))
+ log->Printf("GDBRemoteClientBase::%s failed to get mutex, not sending "
+ "packet '%.*s' (send_async=%d)",
+ __FUNCTION__, int(payload.size()), payload.data(),
+ send_async);
+ return PacketResult::ErrorSendFailed;
+ }
+
+ return SendPacketAndWaitForResponseNoLock(payload, response);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteClientBase::SendPacketAndReceiveResponseWithOutputSupport(
+ llvm::StringRef payload, StringExtractorGDBRemote &response,
+ bool send_async,
+ llvm::function_ref<void(llvm::StringRef)> output_callback) {
+ Lock lock(*this, send_async);
+ if (!lock) {
+ if (Log *log =
+ ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS))
+ log->Printf("GDBRemoteClientBase::%s failed to get mutex, not sending "
+ "packet '%.*s' (send_async=%d)",
+ __FUNCTION__, int(payload.size()), payload.data(),
+ send_async);
+ return PacketResult::ErrorSendFailed;
+ }
+
+ PacketResult packet_result = SendPacketNoLock(payload);
+ if (packet_result != PacketResult::Success)
+ return packet_result;
+
+ return ReadPacketWithOutputSupport(response, GetPacketTimeout(), true,
+ output_callback);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteClientBase::SendPacketAndWaitForResponseNoLock(
+ llvm::StringRef payload, StringExtractorGDBRemote &response) {
+ PacketResult packet_result = SendPacketNoLock(payload);
+ if (packet_result != PacketResult::Success)
+ return packet_result;
+
+ const size_t max_response_retries = 3;
+ for (size_t i = 0; i < max_response_retries; ++i) {
+ packet_result = ReadPacket(response, GetPacketTimeout(), true);
+ // Make sure we received a response
+ if (packet_result != PacketResult::Success)
+ return packet_result;
+ // Make sure our response is valid for the payload that was sent
+ if (response.ValidateResponse())
+ return packet_result;
+ // Response says it wasn't valid
+ Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS);
+ if (log)
+ log->Printf(
+ "error: packet with payload \"%.*s\" got invalid response \"%s\": %s",
+ int(payload.size()), payload.data(), response.GetStringRef().c_str(),
+ (i == (max_response_retries - 1))
+ ? "using invalid response and giving up"
+ : "ignoring response and waiting for another");
+ }
+ return packet_result;
+}
+
+bool GDBRemoteClientBase::SendvContPacket(llvm::StringRef payload,
+ StringExtractorGDBRemote &response) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf("GDBRemoteCommunicationClient::%s ()", __FUNCTION__);
+
+ // we want to lock down packet sending while we continue
+ Lock lock(*this, true);
+
+ if (log)
+ log->Printf(
+ "GDBRemoteCommunicationClient::%s () sending vCont packet: %.*s",
+ __FUNCTION__, int(payload.size()), payload.data());
+
+ if (SendPacketNoLock(payload) != PacketResult::Success)
+ return false;
+
+ OnRunPacketSent(true);
+
+ // wait for the response to the vCont
+ if (ReadPacket(response, llvm::None, false) == PacketResult::Success) {
+ if (response.IsOKResponse())
+ return true;
+ }
+
+ return false;
+}
+bool GDBRemoteClientBase::ShouldStop(const UnixSignals &signals,
+ StringExtractorGDBRemote &response) {
+ std::lock_guard<std::mutex> lock(m_mutex);
+
+ if (m_async_count == 0)
+ return true; // We were not interrupted. The process stopped on its own.
+
+ // Older debugserver stubs (before April 2016) can return two stop-reply
+ // packets in response to a ^C packet. Additionally, all debugservers still
+ // return two stop replies if the inferior stops due to some other reason
+ // before the remote stub manages to interrupt it. We need to wait for this
+ // additional packet to make sure the packet sequence does not get skewed.
+ StringExtractorGDBRemote extra_stop_reply_packet;
+ ReadPacket(extra_stop_reply_packet, milliseconds(100), false);
+
+ // Interrupting is typically done using SIGSTOP or SIGINT, so if the process
+ // stops with some other signal, we definitely want to stop.
+ const uint8_t signo = response.GetHexU8(UINT8_MAX);
+ if (signo != signals.GetSignalNumberFromName("SIGSTOP") &&
+ signo != signals.GetSignalNumberFromName("SIGINT"))
+ return true;
+
+ // We probably only stopped to perform some async processing, so continue
+ // after that is done.
+ // TODO: This is not 100% correct, as the process may have been stopped with
+ // SIGINT or SIGSTOP that was not caused by us (e.g. raise(SIGINT)). This will
+ // normally cause a stop, but if it's done concurrently with a async
+ // interrupt, that stop will get eaten (llvm.org/pr20231).
+ return false;
+}
+
+void GDBRemoteClientBase::OnRunPacketSent(bool first) {
+ if (first)
+ BroadcastEvent(eBroadcastBitRunPacketSent, nullptr);
+}
+
+///////////////////////////////////////
+// GDBRemoteClientBase::ContinueLock //
+///////////////////////////////////////
+
+GDBRemoteClientBase::ContinueLock::ContinueLock(GDBRemoteClientBase &comm)
+ : m_comm(comm), m_acquired(false) {
+ lock();
+}
+
+GDBRemoteClientBase::ContinueLock::~ContinueLock() {
+ if (m_acquired)
+ unlock();
+}
+
+void GDBRemoteClientBase::ContinueLock::unlock() {
+ lldbassert(m_acquired);
+ {
+ std::unique_lock<std::mutex> lock(m_comm.m_mutex);
+ m_comm.m_is_running = false;
+ }
+ m_comm.m_cv.notify_all();
+ m_acquired = false;
+}
+
+GDBRemoteClientBase::ContinueLock::LockResult
+GDBRemoteClientBase::ContinueLock::lock() {
+ Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS);
+ if (log)
+ log->Printf("GDBRemoteClientBase::ContinueLock::%s() resuming with %s",
+ __FUNCTION__, m_comm.m_continue_packet.c_str());
+
+ lldbassert(!m_acquired);
+ std::unique_lock<std::mutex> lock(m_comm.m_mutex);
+ m_comm.m_cv.wait(lock, [this] { return m_comm.m_async_count == 0; });
+ if (m_comm.m_should_stop) {
+ m_comm.m_should_stop = false;
+ if (log)
+ log->Printf("GDBRemoteClientBase::ContinueLock::%s() cancelled",
+ __FUNCTION__);
+ return LockResult::Cancelled;
+ }
+ if (m_comm.SendPacketNoLock(m_comm.m_continue_packet) !=
+ PacketResult::Success)
+ return LockResult::Failed;
+
+ lldbassert(!m_comm.m_is_running);
+ m_comm.m_is_running = true;
+ m_acquired = true;
+ return LockResult::Success;
+}
+
+///////////////////////////////
+// GDBRemoteClientBase::Lock //
+///////////////////////////////
+
+GDBRemoteClientBase::Lock::Lock(GDBRemoteClientBase &comm, bool interrupt)
+ : m_async_lock(comm.m_async_mutex, std::defer_lock), m_comm(comm),
+ m_acquired(false), m_did_interrupt(false) {
+ SyncWithContinueThread(interrupt);
+ if (m_acquired)
+ m_async_lock.lock();
+}
+
+void GDBRemoteClientBase::Lock::SyncWithContinueThread(bool interrupt) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ std::unique_lock<std::mutex> lock(m_comm.m_mutex);
+ if (m_comm.m_is_running && !interrupt)
+ return; // We were asked to avoid interrupting the sender. Lock is not
+ // acquired.
+
+ ++m_comm.m_async_count;
+ if (m_comm.m_is_running) {
+ if (m_comm.m_async_count == 1) {
+ // The sender has sent the continue packet and we are the first async
+ // packet. Let's interrupt it.
+ const char ctrl_c = '\x03';
+ ConnectionStatus status = eConnectionStatusSuccess;
+ size_t bytes_written = m_comm.Write(&ctrl_c, 1, status, nullptr);
+ if (bytes_written == 0) {
+ --m_comm.m_async_count;
+ if (log)
+ log->Printf("GDBRemoteClientBase::Lock::Lock failed to send "
+ "interrupt packet");
+ return;
+ }
+ if (log)
+ log->PutCString("GDBRemoteClientBase::Lock::Lock sent packet: \\x03");
+ m_comm.m_interrupt_time = steady_clock::now();
+ }
+ m_comm.m_cv.wait(lock, [this] { return !m_comm.m_is_running; });
+ m_did_interrupt = true;
+ }
+ m_acquired = true;
+}
+
+GDBRemoteClientBase::Lock::~Lock() {
+ if (!m_acquired)
+ return;
+ {
+ std::unique_lock<std::mutex> lock(m_comm.m_mutex);
+ --m_comm.m_async_count;
+ }
+ m_comm.m_cv.notify_one();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h
new file mode 100644
index 000000000000..54f69e8caac6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h
@@ -0,0 +1,148 @@
+//===-- GDBRemoteClientBase.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 liblldb_GDBRemoteClientBase_h_
+#define liblldb_GDBRemoteClientBase_h_
+
+#include "GDBRemoteCommunication.h"
+
+#include <condition_variable>
+
+namespace lldb_private {
+namespace process_gdb_remote {
+
+class GDBRemoteClientBase : public GDBRemoteCommunication {
+public:
+ struct ContinueDelegate {
+ virtual ~ContinueDelegate();
+ virtual void HandleAsyncStdout(llvm::StringRef out) = 0;
+ virtual void HandleAsyncMisc(llvm::StringRef data) = 0;
+ virtual void HandleStopReply() = 0;
+
+ // =========================================================================
+ /// Process asynchronously-received structured data.
+ ///
+ /// \param[in] data
+ /// The complete data packet, expected to start with JSON-async.
+ // =========================================================================
+ virtual void HandleAsyncStructuredDataPacket(llvm::StringRef data) = 0;
+ };
+
+ GDBRemoteClientBase(const char *comm_name, const char *listener_name);
+
+ bool SendAsyncSignal(int signo);
+
+ bool Interrupt();
+
+ lldb::StateType SendContinuePacketAndWaitForResponse(
+ ContinueDelegate &delegate, const UnixSignals &signals,
+ llvm::StringRef payload, StringExtractorGDBRemote &response);
+
+ PacketResult SendPacketAndWaitForResponse(llvm::StringRef payload,
+ StringExtractorGDBRemote &response,
+ bool send_async);
+
+ PacketResult SendPacketAndReceiveResponseWithOutputSupport(
+ llvm::StringRef payload, StringExtractorGDBRemote &response,
+ bool send_async,
+ llvm::function_ref<void(llvm::StringRef)> output_callback);
+
+ bool SendvContPacket(llvm::StringRef payload,
+ StringExtractorGDBRemote &response);
+
+ class Lock {
+ public:
+ Lock(GDBRemoteClientBase &comm, bool interrupt);
+ ~Lock();
+
+ explicit operator bool() { return m_acquired; }
+
+ // Whether we had to interrupt the continue thread to acquire the
+ // connection.
+ bool DidInterrupt() const { return m_did_interrupt; }
+
+ private:
+ std::unique_lock<std::recursive_mutex> m_async_lock;
+ GDBRemoteClientBase &m_comm;
+ bool m_acquired;
+ bool m_did_interrupt;
+
+ void SyncWithContinueThread(bool interrupt);
+ };
+
+protected:
+ PacketResult
+ SendPacketAndWaitForResponseNoLock(llvm::StringRef payload,
+ StringExtractorGDBRemote &response);
+
+ virtual void OnRunPacketSent(bool first);
+
+private:
+ // Variables handling synchronization between the Continue thread and any
+ // other threads
+ // wishing to send packets over the connection. Either the continue thread has
+ // control over
+ // the connection (m_is_running == true) or the connection is free for an
+ // arbitrary number of
+ // other senders to take which indicate their interest by incrementing
+ // m_async_count.
+ // Semantics of individual states:
+ // - m_continue_packet == false, m_async_count == 0: connection is free
+ // - m_continue_packet == true, m_async_count == 0: only continue thread is
+ // present
+ // - m_continue_packet == true, m_async_count > 0: continue thread has
+ // control, async threads
+ // should interrupt it and wait for it to set m_continue_packet to false
+ // - m_continue_packet == false, m_async_count > 0: async threads have
+ // control, continue
+ // thread needs to wait for them to finish (m_async_count goes down to 0).
+ std::mutex m_mutex;
+ std::condition_variable m_cv;
+ // Packet with which to resume after an async interrupt. Can be changed by an
+ // async thread
+ // e.g. to inject a signal.
+ std::string m_continue_packet;
+ // When was the interrupt packet sent. Used to make sure we time out if the
+ // stub does not
+ // respond to interrupt requests.
+ std::chrono::time_point<std::chrono::steady_clock> m_interrupt_time;
+ uint32_t m_async_count;
+ bool m_is_running;
+ bool m_should_stop; // Whether we should resume after a stop.
+ // end of continue thread synchronization block
+
+ // This handles the synchronization between individual async threads. For now
+ // they just use a
+ // simple mutex.
+ std::recursive_mutex m_async_mutex;
+
+ bool ShouldStop(const UnixSignals &signals,
+ StringExtractorGDBRemote &response);
+
+ class ContinueLock {
+ public:
+ enum class LockResult { Success, Cancelled, Failed };
+
+ explicit ContinueLock(GDBRemoteClientBase &comm);
+ ~ContinueLock();
+ explicit operator bool() { return m_acquired; }
+
+ LockResult lock();
+
+ void unlock();
+
+ private:
+ GDBRemoteClientBase &m_comm;
+ bool m_acquired;
+ };
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+#endif // liblldb_GDBRemoteCommunicationClient_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
new file mode 100644
index 000000000000..11052eff948f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
@@ -0,0 +1,1398 @@
+//===-- GDBRemoteCommunication.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 "GDBRemoteCommunication.h"
+
+#include <future>
+#include <limits.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Host/ConnectionFileDescriptor.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/Pipe.h"
+#include "lldb/Host/ProcessLaunchInfo.h"
+#include "lldb/Host/Socket.h"
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Host/common/TCPSocket.h"
+#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Utility/Event.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/StreamString.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/ScopedPrinter.h"
+
+#include "ProcessGDBRemoteLog.h"
+
+#if defined(__APPLE__)
+#define DEBUGSERVER_BASENAME "debugserver"
+#else
+#define DEBUGSERVER_BASENAME "lldb-server"
+#endif
+
+#if defined(HAVE_LIBCOMPRESSION)
+#include <compression.h>
+#endif
+
+#if defined(HAVE_LIBZ)
+#include <zlib.h>
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+// GDBRemoteCommunication constructor
+GDBRemoteCommunication::GDBRemoteCommunication(const char *comm_name,
+ const char *listener_name)
+ : Communication(comm_name),
+#ifdef LLDB_CONFIGURATION_DEBUG
+ m_packet_timeout(1000),
+#else
+ m_packet_timeout(1),
+#endif
+ m_echo_number(0), m_supports_qEcho(eLazyBoolCalculate), m_history(512),
+ m_send_acks(true), m_compression_type(CompressionType::None),
+ m_listen_url() {
+}
+
+// Destructor
+GDBRemoteCommunication::~GDBRemoteCommunication() {
+ if (IsConnected()) {
+ Disconnect();
+ }
+
+#if defined(HAVE_LIBCOMPRESSION)
+ if (m_decompression_scratch)
+ free (m_decompression_scratch);
+#endif
+
+ // Stop the communications read thread which is used to parse all incoming
+ // packets. This function will block until the read thread returns.
+ if (m_read_thread_enabled)
+ StopReadThread();
+}
+
+char GDBRemoteCommunication::CalculcateChecksum(llvm::StringRef payload) {
+ int checksum = 0;
+
+ for (char c : payload)
+ checksum += c;
+
+ return checksum & 255;
+}
+
+size_t GDBRemoteCommunication::SendAck() {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS));
+ ConnectionStatus status = eConnectionStatusSuccess;
+ char ch = '+';
+ const size_t bytes_written = Write(&ch, 1, status, nullptr);
+ if (log)
+ log->Printf("<%4" PRIu64 "> send packet: %c", (uint64_t)bytes_written, ch);
+ m_history.AddPacket(ch, GDBRemoteCommunicationHistory::ePacketTypeSend,
+ bytes_written);
+ return bytes_written;
+}
+
+size_t GDBRemoteCommunication::SendNack() {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS));
+ ConnectionStatus status = eConnectionStatusSuccess;
+ char ch = '-';
+ const size_t bytes_written = Write(&ch, 1, status, nullptr);
+ if (log)
+ log->Printf("<%4" PRIu64 "> send packet: %c", (uint64_t)bytes_written, ch);
+ m_history.AddPacket(ch, GDBRemoteCommunicationHistory::ePacketTypeSend,
+ bytes_written);
+ return bytes_written;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunication::SendPacketNoLock(llvm::StringRef payload) {
+ StreamString packet(0, 4, eByteOrderBig);
+ packet.PutChar('$');
+ packet.Write(payload.data(), payload.size());
+ packet.PutChar('#');
+ packet.PutHex8(CalculcateChecksum(payload));
+ std::string packet_str = packet.GetString();
+
+ return SendRawPacketNoLock(packet_str);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunication::SendRawPacketNoLock(llvm::StringRef packet,
+ bool skip_ack) {
+ if (IsConnected()) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS));
+ ConnectionStatus status = eConnectionStatusSuccess;
+ const char *packet_data = packet.data();
+ const size_t packet_length = packet.size();
+ size_t bytes_written = Write(packet_data, packet_length, status, nullptr);
+ if (log) {
+ size_t binary_start_offset = 0;
+ if (strncmp(packet_data, "$vFile:pwrite:", strlen("$vFile:pwrite:")) ==
+ 0) {
+ const char *first_comma = strchr(packet_data, ',');
+ if (first_comma) {
+ const char *second_comma = strchr(first_comma + 1, ',');
+ if (second_comma)
+ binary_start_offset = second_comma - packet_data + 1;
+ }
+ }
+
+ // If logging was just enabled and we have history, then dump out what we
+ // have to the log so we get the historical context. The Dump() call that
+ // logs all of the packet will set a boolean so that we don't dump this
+ // more than once
+ if (!m_history.DidDumpToLog())
+ m_history.Dump(log);
+
+ if (binary_start_offset) {
+ StreamString strm;
+ // Print non binary data header
+ strm.Printf("<%4" PRIu64 "> send packet: %.*s", (uint64_t)bytes_written,
+ (int)binary_start_offset, packet_data);
+ const uint8_t *p;
+ // Print binary data exactly as sent
+ for (p = (const uint8_t *)packet_data + binary_start_offset; *p != '#';
+ ++p)
+ strm.Printf("\\x%2.2x", *p);
+ // Print the checksum
+ strm.Printf("%*s", (int)3, p);
+ log->PutString(strm.GetString());
+ } else
+ log->Printf("<%4" PRIu64 "> send packet: %.*s", (uint64_t)bytes_written,
+ (int)packet_length, packet_data);
+ }
+
+ m_history.AddPacket(packet.str(), packet_length,
+ GDBRemoteCommunicationHistory::ePacketTypeSend,
+ bytes_written);
+
+ if (bytes_written == packet_length) {
+ if (!skip_ack && GetSendAcks())
+ return GetAck();
+ else
+ return PacketResult::Success;
+ } else {
+ if (log)
+ log->Printf("error: failed to send packet: %.*s", (int)packet_length,
+ packet_data);
+ }
+ }
+ return PacketResult::ErrorSendFailed;
+}
+
+GDBRemoteCommunication::PacketResult GDBRemoteCommunication::GetAck() {
+ StringExtractorGDBRemote packet;
+ PacketResult result = ReadPacket(packet, GetPacketTimeout(), false);
+ if (result == PacketResult::Success) {
+ if (packet.GetResponseType() ==
+ StringExtractorGDBRemote::ResponseType::eAck)
+ return PacketResult::Success;
+ else
+ return PacketResult::ErrorSendAck;
+ }
+ return result;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunication::ReadPacketWithOutputSupport(
+ StringExtractorGDBRemote &response, Timeout<std::micro> timeout,
+ bool sync_on_timeout,
+ llvm::function_ref<void(llvm::StringRef)> output_callback) {
+ auto result = ReadPacket(response, timeout, sync_on_timeout);
+ while (result == PacketResult::Success && response.IsNormalResponse() &&
+ response.PeekChar() == 'O') {
+ response.GetChar();
+ std::string output;
+ if (response.GetHexByteString(output))
+ output_callback(output);
+ result = ReadPacket(response, timeout, sync_on_timeout);
+ }
+ return result;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunication::ReadPacket(StringExtractorGDBRemote &response,
+ Timeout<std::micro> timeout,
+ bool sync_on_timeout) {
+ if (m_read_thread_enabled)
+ return PopPacketFromQueue(response, timeout);
+ else
+ return WaitForPacketNoLock(response, timeout, sync_on_timeout);
+}
+
+// This function is called when a packet is requested.
+// A whole packet is popped from the packet queue and returned to the caller.
+// Packets are placed into this queue from the communication read thread. See
+// GDBRemoteCommunication::AppendBytesToCache.
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunication::PopPacketFromQueue(StringExtractorGDBRemote &response,
+ Timeout<std::micro> timeout) {
+ auto pred = [&] { return !m_packet_queue.empty() && IsConnected(); };
+ // lock down the packet queue
+ std::unique_lock<std::mutex> lock(m_packet_queue_mutex);
+
+ if (!timeout)
+ m_condition_queue_not_empty.wait(lock, pred);
+ else {
+ if (!m_condition_queue_not_empty.wait_for(lock, *timeout, pred))
+ return PacketResult::ErrorReplyTimeout;
+ if (!IsConnected())
+ return PacketResult::ErrorDisconnected;
+ }
+
+ // get the front element of the queue
+ response = m_packet_queue.front();
+
+ // remove the front element
+ m_packet_queue.pop();
+
+ // we got a packet
+ return PacketResult::Success;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunication::WaitForPacketNoLock(StringExtractorGDBRemote &packet,
+ Timeout<std::micro> timeout,
+ bool sync_on_timeout) {
+ uint8_t buffer[8192];
+ Status error;
+
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS));
+
+ // Check for a packet from our cache first without trying any reading...
+ if (CheckForPacket(nullptr, 0, packet) != PacketType::Invalid)
+ return PacketResult::Success;
+
+ bool timed_out = false;
+ bool disconnected = false;
+ while (IsConnected() && !timed_out) {
+ lldb::ConnectionStatus status = eConnectionStatusNoConnection;
+ size_t bytes_read = Read(buffer, sizeof(buffer), timeout, status, &error);
+
+ LLDB_LOGV(log,
+ "Read(buffer, sizeof(buffer), timeout = {0}, "
+ "status = {1}, error = {2}) => bytes_read = {3}",
+ timeout, Communication::ConnectionStatusAsCString(status), error,
+ bytes_read);
+
+ if (bytes_read > 0) {
+ if (CheckForPacket(buffer, bytes_read, packet) != PacketType::Invalid)
+ return PacketResult::Success;
+ } else {
+ switch (status) {
+ case eConnectionStatusTimedOut:
+ case eConnectionStatusInterrupted:
+ if (sync_on_timeout) {
+ /// Sync the remote GDB server and make sure we get a response that
+ /// corresponds to what we send.
+ ///
+ /// Sends a "qEcho" packet and makes sure it gets the exact packet
+ /// echoed back. If the qEcho packet isn't supported, we send a qC
+ /// packet and make sure we get a valid thread ID back. We use the
+ /// "qC" packet since its response if very unique: is responds with
+ /// "QC%x" where %x is the thread ID of the current thread. This
+ /// makes the response unique enough from other packet responses to
+ /// ensure we are back on track.
+ ///
+ /// This packet is needed after we time out sending a packet so we
+ /// can ensure that we are getting the response for the packet we
+ /// are sending. There are no sequence IDs in the GDB remote
+ /// protocol (there used to be, but they are not supported anymore)
+ /// so if you timeout sending packet "abc", you might then send
+ /// packet "cde" and get the response for the previous "abc" packet.
+ /// Many responses are "OK" or "" (unsupported) or "EXX" (error) so
+ /// many responses for packets can look like responses for other
+ /// packets. So if we timeout, we need to ensure that we can get
+ /// back on track. If we can't get back on track, we must
+ /// disconnect.
+ bool sync_success = false;
+ bool got_actual_response = false;
+ // We timed out, we need to sync back up with the
+ char echo_packet[32];
+ int echo_packet_len = 0;
+ RegularExpression response_regex;
+
+ if (m_supports_qEcho == eLazyBoolYes) {
+ echo_packet_len = ::snprintf(echo_packet, sizeof(echo_packet),
+ "qEcho:%u", ++m_echo_number);
+ std::string regex_str = "^";
+ regex_str += echo_packet;
+ regex_str += "$";
+ response_regex.Compile(regex_str);
+ } else {
+ echo_packet_len =
+ ::snprintf(echo_packet, sizeof(echo_packet), "qC");
+ response_regex.Compile(llvm::StringRef("^QC[0-9A-Fa-f]+$"));
+ }
+
+ PacketResult echo_packet_result =
+ SendPacketNoLock(llvm::StringRef(echo_packet, echo_packet_len));
+ if (echo_packet_result == PacketResult::Success) {
+ const uint32_t max_retries = 3;
+ uint32_t successful_responses = 0;
+ for (uint32_t i = 0; i < max_retries; ++i) {
+ StringExtractorGDBRemote echo_response;
+ echo_packet_result =
+ WaitForPacketNoLock(echo_response, timeout, false);
+ if (echo_packet_result == PacketResult::Success) {
+ ++successful_responses;
+ if (response_regex.Execute(echo_response.GetStringRef())) {
+ sync_success = true;
+ break;
+ } else if (successful_responses == 1) {
+ // We got something else back as the first successful
+ // response, it probably is the response to the packet we
+ // actually wanted, so copy it over if this is the first
+ // success and continue to try to get the qEcho response
+ packet = echo_response;
+ got_actual_response = true;
+ }
+ } else if (echo_packet_result == PacketResult::ErrorReplyTimeout)
+ continue; // Packet timed out, continue waiting for a response
+ else
+ break; // Something else went wrong getting the packet back, we
+ // failed and are done trying
+ }
+ }
+
+ // We weren't able to sync back up with the server, we must abort
+ // otherwise all responses might not be from the right packets...
+ if (sync_success) {
+ // We timed out, but were able to recover
+ if (got_actual_response) {
+ // We initially timed out, but we did get a response that came in
+ // before the successful reply to our qEcho packet, so lets say
+ // everything is fine...
+ return PacketResult::Success;
+ }
+ } else {
+ disconnected = true;
+ Disconnect();
+ }
+ }
+ timed_out = true;
+ break;
+ case eConnectionStatusSuccess:
+ // printf ("status = success but error = %s\n",
+ // error.AsCString("<invalid>"));
+ break;
+
+ case eConnectionStatusEndOfFile:
+ case eConnectionStatusNoConnection:
+ case eConnectionStatusLostConnection:
+ case eConnectionStatusError:
+ disconnected = true;
+ Disconnect();
+ break;
+ }
+ }
+ }
+ packet.Clear();
+ if (disconnected)
+ return PacketResult::ErrorDisconnected;
+ if (timed_out)
+ return PacketResult::ErrorReplyTimeout;
+ else
+ return PacketResult::ErrorReplyFailed;
+}
+
+bool GDBRemoteCommunication::DecompressPacket() {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS));
+
+ if (!CompressionIsEnabled())
+ return true;
+
+ size_t pkt_size = m_bytes.size();
+
+ // Smallest possible compressed packet is $N#00 - an uncompressed empty
+ // reply, most commonly indicating an unsupported packet. Anything less than
+ // 5 characters, it's definitely not a compressed packet.
+ if (pkt_size < 5)
+ return true;
+
+ if (m_bytes[0] != '$' && m_bytes[0] != '%')
+ return true;
+ if (m_bytes[1] != 'C' && m_bytes[1] != 'N')
+ return true;
+
+ size_t hash_mark_idx = m_bytes.find('#');
+ if (hash_mark_idx == std::string::npos)
+ return true;
+ if (hash_mark_idx + 2 >= m_bytes.size())
+ return true;
+
+ if (!::isxdigit(m_bytes[hash_mark_idx + 1]) ||
+ !::isxdigit(m_bytes[hash_mark_idx + 2]))
+ return true;
+
+ size_t content_length =
+ pkt_size -
+ 5; // not counting '$', 'C' | 'N', '#', & the two hex checksum chars
+ size_t content_start = 2; // The first character of the
+ // compressed/not-compressed text of the packet
+ size_t checksum_idx =
+ hash_mark_idx +
+ 1; // The first character of the two hex checksum characters
+
+ // Normally size_of_first_packet == m_bytes.size() but m_bytes may contain
+ // multiple packets. size_of_first_packet is the size of the initial packet
+ // which we'll replace with the decompressed version of, leaving the rest of
+ // m_bytes unmodified.
+ size_t size_of_first_packet = hash_mark_idx + 3;
+
+ // Compressed packets ("$C") start with a base10 number which is the size of
+ // the uncompressed payload, then a : and then the compressed data. e.g.
+ // $C1024:<binary>#00 Update content_start and content_length to only include
+ // the <binary> part of the packet.
+
+ uint64_t decompressed_bufsize = ULONG_MAX;
+ if (m_bytes[1] == 'C') {
+ size_t i = content_start;
+ while (i < hash_mark_idx && isdigit(m_bytes[i]))
+ i++;
+ if (i < hash_mark_idx && m_bytes[i] == ':') {
+ i++;
+ content_start = i;
+ content_length = hash_mark_idx - content_start;
+ std::string bufsize_str(m_bytes.data() + 2, i - 2 - 1);
+ errno = 0;
+ decompressed_bufsize = ::strtoul(bufsize_str.c_str(), nullptr, 10);
+ if (errno != 0 || decompressed_bufsize == ULONG_MAX) {
+ m_bytes.erase(0, size_of_first_packet);
+ return false;
+ }
+ }
+ }
+
+ if (GetSendAcks()) {
+ char packet_checksum_cstr[3];
+ packet_checksum_cstr[0] = m_bytes[checksum_idx];
+ packet_checksum_cstr[1] = m_bytes[checksum_idx + 1];
+ packet_checksum_cstr[2] = '\0';
+ long packet_checksum = strtol(packet_checksum_cstr, nullptr, 16);
+
+ long actual_checksum = CalculcateChecksum(
+ llvm::StringRef(m_bytes).substr(1, hash_mark_idx - 1));
+ bool success = packet_checksum == actual_checksum;
+ if (!success) {
+ if (log)
+ log->Printf(
+ "error: checksum mismatch: %.*s expected 0x%2.2x, got 0x%2.2x",
+ (int)(pkt_size), m_bytes.c_str(), (uint8_t)packet_checksum,
+ (uint8_t)actual_checksum);
+ }
+ // Send the ack or nack if needed
+ if (!success) {
+ SendNack();
+ m_bytes.erase(0, size_of_first_packet);
+ return false;
+ } else {
+ SendAck();
+ }
+ }
+
+ if (m_bytes[1] == 'N') {
+ // This packet was not compressed -- delete the 'N' character at the start
+ // and the packet may be processed as-is.
+ m_bytes.erase(1, 1);
+ return true;
+ }
+
+ // Reverse the gdb-remote binary escaping that was done to the compressed
+ // text to guard characters like '$', '#', '}', etc.
+ std::vector<uint8_t> unescaped_content;
+ unescaped_content.reserve(content_length);
+ size_t i = content_start;
+ while (i < hash_mark_idx) {
+ if (m_bytes[i] == '}') {
+ i++;
+ unescaped_content.push_back(m_bytes[i] ^ 0x20);
+ } else {
+ unescaped_content.push_back(m_bytes[i]);
+ }
+ i++;
+ }
+
+ uint8_t *decompressed_buffer = nullptr;
+ size_t decompressed_bytes = 0;
+
+ if (decompressed_bufsize != ULONG_MAX) {
+ decompressed_buffer = (uint8_t *)malloc(decompressed_bufsize);
+ if (decompressed_buffer == nullptr) {
+ m_bytes.erase(0, size_of_first_packet);
+ return false;
+ }
+ }
+
+#if defined(HAVE_LIBCOMPRESSION)
+ if (m_compression_type == CompressionType::ZlibDeflate ||
+ m_compression_type == CompressionType::LZFSE ||
+ m_compression_type == CompressionType::LZ4 ||
+ m_compression_type == CompressionType::LZMA) {
+ compression_algorithm compression_type;
+ if (m_compression_type == CompressionType::LZFSE)
+ compression_type = COMPRESSION_LZFSE;
+ else if (m_compression_type == CompressionType::ZlibDeflate)
+ compression_type = COMPRESSION_ZLIB;
+ else if (m_compression_type == CompressionType::LZ4)
+ compression_type = COMPRESSION_LZ4_RAW;
+ else if (m_compression_type == CompressionType::LZMA)
+ compression_type = COMPRESSION_LZMA;
+
+ if (m_decompression_scratch_type != m_compression_type) {
+ if (m_decompression_scratch) {
+ free (m_decompression_scratch);
+ m_decompression_scratch = nullptr;
+ }
+ size_t scratchbuf_size = 0;
+ if (m_compression_type == CompressionType::LZFSE)
+ scratchbuf_size = compression_decode_scratch_buffer_size (COMPRESSION_LZFSE);
+ else if (m_compression_type == CompressionType::LZ4)
+ scratchbuf_size = compression_decode_scratch_buffer_size (COMPRESSION_LZ4_RAW);
+ else if (m_compression_type == CompressionType::ZlibDeflate)
+ scratchbuf_size = compression_decode_scratch_buffer_size (COMPRESSION_ZLIB);
+ else if (m_compression_type == CompressionType::LZMA)
+ scratchbuf_size = compression_decode_scratch_buffer_size (COMPRESSION_LZMA);
+ else if (m_compression_type == CompressionType::LZFSE)
+ scratchbuf_size = compression_decode_scratch_buffer_size (COMPRESSION_LZFSE);
+ if (scratchbuf_size > 0) {
+ m_decompression_scratch = (void*) malloc (scratchbuf_size);
+ m_decompression_scratch_type = m_compression_type;
+ }
+ }
+
+ if (decompressed_bufsize != ULONG_MAX && decompressed_buffer != nullptr) {
+ decompressed_bytes = compression_decode_buffer(
+ decompressed_buffer, decompressed_bufsize,
+ (uint8_t *)unescaped_content.data(), unescaped_content.size(),
+ m_decompression_scratch, compression_type);
+ }
+ }
+#endif
+
+#if defined(HAVE_LIBZ)
+ if (decompressed_bytes == 0 && decompressed_bufsize != ULONG_MAX &&
+ decompressed_buffer != nullptr &&
+ m_compression_type == CompressionType::ZlibDeflate) {
+ z_stream stream;
+ memset(&stream, 0, sizeof(z_stream));
+ stream.next_in = (Bytef *)unescaped_content.data();
+ stream.avail_in = (uInt)unescaped_content.size();
+ stream.total_in = 0;
+ stream.next_out = (Bytef *)decompressed_buffer;
+ stream.avail_out = decompressed_bufsize;
+ stream.total_out = 0;
+ stream.zalloc = Z_NULL;
+ stream.zfree = Z_NULL;
+ stream.opaque = Z_NULL;
+
+ if (inflateInit2(&stream, -15) == Z_OK) {
+ int status = inflate(&stream, Z_NO_FLUSH);
+ inflateEnd(&stream);
+ if (status == Z_STREAM_END) {
+ decompressed_bytes = stream.total_out;
+ }
+ }
+ }
+#endif
+
+ if (decompressed_bytes == 0 || decompressed_buffer == nullptr) {
+ if (decompressed_buffer)
+ free(decompressed_buffer);
+ m_bytes.erase(0, size_of_first_packet);
+ return false;
+ }
+
+ std::string new_packet;
+ new_packet.reserve(decompressed_bytes + 6);
+ new_packet.push_back(m_bytes[0]);
+ new_packet.append((const char *)decompressed_buffer, decompressed_bytes);
+ new_packet.push_back('#');
+ if (GetSendAcks()) {
+ uint8_t decompressed_checksum = CalculcateChecksum(
+ llvm::StringRef((const char *)decompressed_buffer, decompressed_bytes));
+ char decompressed_checksum_str[3];
+ snprintf(decompressed_checksum_str, 3, "%02x", decompressed_checksum);
+ new_packet.append(decompressed_checksum_str);
+ } else {
+ new_packet.push_back('0');
+ new_packet.push_back('0');
+ }
+
+ m_bytes.replace(0, size_of_first_packet, new_packet.data(),
+ new_packet.size());
+
+ free(decompressed_buffer);
+ return true;
+}
+
+GDBRemoteCommunication::PacketType
+GDBRemoteCommunication::CheckForPacket(const uint8_t *src, size_t src_len,
+ StringExtractorGDBRemote &packet) {
+ // Put the packet data into the buffer in a thread safe fashion
+ std::lock_guard<std::recursive_mutex> guard(m_bytes_mutex);
+
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS));
+
+ if (src && src_len > 0) {
+ if (log && log->GetVerbose()) {
+ StreamString s;
+ log->Printf("GDBRemoteCommunication::%s adding %u bytes: %.*s",
+ __FUNCTION__, (uint32_t)src_len, (uint32_t)src_len, src);
+ }
+ m_bytes.append((const char *)src, src_len);
+ }
+
+ bool isNotifyPacket = false;
+
+ // Parse up the packets into gdb remote packets
+ if (!m_bytes.empty()) {
+ // 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 content_start = 0;
+ size_t content_length = 0;
+ size_t total_length = 0;
+ size_t checksum_idx = std::string::npos;
+
+ // Size of packet before it is decompressed, for logging purposes
+ size_t original_packet_size = m_bytes.size();
+ if (CompressionIsEnabled()) {
+ if (!DecompressPacket()) {
+ packet.Clear();
+ return GDBRemoteCommunication::PacketType::Standard;
+ }
+ }
+
+ switch (m_bytes[0]) {
+ case '+': // Look for ack
+ case '-': // Look for cancel
+ case '\x03': // ^C to halt target
+ content_length = total_length = 1; // The command is one byte long...
+ break;
+
+ case '%': // Async notify packet
+ isNotifyPacket = true;
+ LLVM_FALLTHROUGH;
+
+ case '$':
+ // Look for a standard gdb packet?
+ {
+ size_t hash_pos = m_bytes.find('#');
+ if (hash_pos != std::string::npos) {
+ if (hash_pos + 2 < m_bytes.size()) {
+ checksum_idx = hash_pos + 1;
+ // Skip the dollar sign
+ content_start = 1;
+ // Don't include the # in the content or the $ in the content
+ // length
+ content_length = hash_pos - 1;
+
+ total_length =
+ hash_pos + 3; // Skip the # and the two hex checksum bytes
+ } else {
+ // Checksum bytes aren't all here yet
+ content_length = std::string::npos;
+ }
+ }
+ }
+ break;
+
+ default: {
+ // We have an unexpected byte and we need to flush all bad data that is
+ // in m_bytes, so we need to find the first byte that is a '+' (ACK), '-'
+ // (NACK), \x03 (CTRL+C interrupt), or '$' character (start of packet
+ // header) or of course, the end of the data in m_bytes...
+ const size_t bytes_len = m_bytes.size();
+ bool done = false;
+ uint32_t idx;
+ for (idx = 1; !done && idx < bytes_len; ++idx) {
+ switch (m_bytes[idx]) {
+ case '+':
+ case '-':
+ case '\x03':
+ case '%':
+ case '$':
+ done = true;
+ break;
+
+ default:
+ break;
+ }
+ }
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s tossing %u junk bytes: '%.*s'",
+ __FUNCTION__, idx - 1, idx - 1, m_bytes.c_str());
+ m_bytes.erase(0, idx - 1);
+ } break;
+ }
+
+ if (content_length == std::string::npos) {
+ packet.Clear();
+ return GDBRemoteCommunication::PacketType::Invalid;
+ } else if (total_length > 0) {
+
+ // We have a valid packet...
+ assert(content_length <= m_bytes.size());
+ assert(total_length <= m_bytes.size());
+ assert(content_length <= total_length);
+ size_t content_end = content_start + content_length;
+
+ bool success = true;
+ std::string &packet_str = packet.GetStringRef();
+ if (log) {
+ // If logging was just enabled and we have history, then dump out what
+ // we have to the log so we get the historical context. The Dump() call
+ // that logs all of the packet will set a boolean so that we don't dump
+ // this more than once
+ if (!m_history.DidDumpToLog())
+ m_history.Dump(log);
+
+ bool binary = false;
+ // Only detect binary for packets that start with a '$' and have a
+ // '#CC' checksum
+ if (m_bytes[0] == '$' && total_length > 4) {
+ for (size_t i = 0; !binary && i < total_length; ++i) {
+ unsigned char c = m_bytes[i];
+ if (isprint(c) == 0 && isspace(c) == 0) {
+ binary = true;
+ }
+ }
+ }
+ if (binary) {
+ StreamString strm;
+ // Packet header...
+ if (CompressionIsEnabled())
+ strm.Printf("<%4" PRIu64 ":%" PRIu64 "> read packet: %c",
+ (uint64_t)original_packet_size, (uint64_t)total_length,
+ m_bytes[0]);
+ else
+ strm.Printf("<%4" PRIu64 "> read packet: %c",
+ (uint64_t)total_length, m_bytes[0]);
+ for (size_t i = content_start; i < content_end; ++i) {
+ // Remove binary escaped bytes when displaying the packet...
+ const char ch = m_bytes[i];
+ if (ch == 0x7d) {
+ // 0x7d is the escape character. The next character is to be
+ // XOR'd with 0x20.
+ const char escapee = m_bytes[++i] ^ 0x20;
+ strm.Printf("%2.2x", escapee);
+ } else {
+ strm.Printf("%2.2x", (uint8_t)ch);
+ }
+ }
+ // Packet footer...
+ strm.Printf("%c%c%c", m_bytes[total_length - 3],
+ m_bytes[total_length - 2], m_bytes[total_length - 1]);
+ log->PutString(strm.GetString());
+ } else {
+ if (CompressionIsEnabled())
+ log->Printf("<%4" PRIu64 ":%" PRIu64 "> read packet: %.*s",
+ (uint64_t)original_packet_size, (uint64_t)total_length,
+ (int)(total_length), m_bytes.c_str());
+ else
+ log->Printf("<%4" PRIu64 "> read packet: %.*s",
+ (uint64_t)total_length, (int)(total_length),
+ m_bytes.c_str());
+ }
+ }
+
+ m_history.AddPacket(m_bytes, total_length,
+ GDBRemoteCommunicationHistory::ePacketTypeRecv,
+ total_length);
+
+ // Clear packet_str in case there is some existing data in it.
+ packet_str.clear();
+ // Copy the packet from m_bytes to packet_str expanding the run-length
+ // encoding in the process. Reserve enough byte for the most common case
+ // (no RLE used)
+ packet_str.reserve(m_bytes.length());
+ for (std::string::const_iterator c = m_bytes.begin() + content_start;
+ c != m_bytes.begin() + content_end; ++c) {
+ if (*c == '*') {
+ // '*' indicates RLE. Next character will give us the repeat count
+ // and previous character is what is to be repeated.
+ char char_to_repeat = packet_str.back();
+ // Number of time the previous character is repeated
+ int repeat_count = *++c + 3 - ' ';
+ // We have the char_to_repeat and repeat_count. Now push it in the
+ // packet.
+ for (int i = 0; i < repeat_count; ++i)
+ packet_str.push_back(char_to_repeat);
+ } else if (*c == 0x7d) {
+ // 0x7d is the escape character. The next character is to be XOR'd
+ // with 0x20.
+ char escapee = *++c ^ 0x20;
+ packet_str.push_back(escapee);
+ } else {
+ packet_str.push_back(*c);
+ }
+ }
+
+ if (m_bytes[0] == '$' || m_bytes[0] == '%') {
+ assert(checksum_idx < m_bytes.size());
+ if (::isxdigit(m_bytes[checksum_idx + 0]) ||
+ ::isxdigit(m_bytes[checksum_idx + 1])) {
+ if (GetSendAcks()) {
+ const char *packet_checksum_cstr = &m_bytes[checksum_idx];
+ char packet_checksum = strtol(packet_checksum_cstr, nullptr, 16);
+ char actual_checksum = CalculcateChecksum(
+ llvm::StringRef(m_bytes).slice(content_start, content_end));
+ success = packet_checksum == actual_checksum;
+ if (!success) {
+ if (log)
+ log->Printf("error: checksum mismatch: %.*s expected 0x%2.2x, "
+ "got 0x%2.2x",
+ (int)(total_length), m_bytes.c_str(),
+ (uint8_t)packet_checksum, (uint8_t)actual_checksum);
+ }
+ // Send the ack or nack if needed
+ if (!success)
+ SendNack();
+ else
+ SendAck();
+ }
+ } else {
+ success = false;
+ if (log)
+ log->Printf("error: invalid checksum in packet: '%s'\n",
+ m_bytes.c_str());
+ }
+ }
+
+ m_bytes.erase(0, total_length);
+ packet.SetFilePos(0);
+
+ if (isNotifyPacket)
+ return GDBRemoteCommunication::PacketType::Notify;
+ else
+ return GDBRemoteCommunication::PacketType::Standard;
+ }
+ }
+ packet.Clear();
+ return GDBRemoteCommunication::PacketType::Invalid;
+}
+
+Status GDBRemoteCommunication::StartListenThread(const char *hostname,
+ uint16_t port) {
+ if (m_listen_thread.IsJoinable())
+ return Status("listen thread already running");
+
+ char listen_url[512];
+ if (hostname && hostname[0])
+ snprintf(listen_url, sizeof(listen_url), "listen://%s:%i", hostname, port);
+ else
+ snprintf(listen_url, sizeof(listen_url), "listen://%i", port);
+ m_listen_url = listen_url;
+ SetConnection(new ConnectionFileDescriptor());
+ llvm::Expected<HostThread> listen_thread = ThreadLauncher::LaunchThread(
+ listen_url, GDBRemoteCommunication::ListenThread, this);
+ if (!listen_thread)
+ return Status(listen_thread.takeError());
+ m_listen_thread = *listen_thread;
+
+ return Status();
+}
+
+bool GDBRemoteCommunication::JoinListenThread() {
+ if (m_listen_thread.IsJoinable())
+ m_listen_thread.Join(nullptr);
+ return true;
+}
+
+lldb::thread_result_t
+GDBRemoteCommunication::ListenThread(lldb::thread_arg_t arg) {
+ GDBRemoteCommunication *comm = (GDBRemoteCommunication *)arg;
+ Status error;
+ ConnectionFileDescriptor *connection =
+ (ConnectionFileDescriptor *)comm->GetConnection();
+
+ if (connection) {
+ // Do the listen on another thread so we can continue on...
+ if (connection->Connect(comm->m_listen_url.c_str(), &error) !=
+ eConnectionStatusSuccess)
+ comm->SetConnection(nullptr);
+ }
+ return {};
+}
+
+Status GDBRemoteCommunication::StartDebugserverProcess(
+ const char *url, Platform *platform, ProcessLaunchInfo &launch_info,
+ uint16_t *port, const Args *inferior_args, int pass_comm_fd) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s(url=%s, port=%" PRIu16 ")",
+ __FUNCTION__, url ? url : "<empty>",
+ port ? *port : uint16_t(0));
+
+ Status error;
+ // If we locate debugserver, keep that located version around
+ static FileSpec g_debugserver_file_spec;
+
+ char debugserver_path[PATH_MAX];
+ FileSpec &debugserver_file_spec = launch_info.GetExecutableFile();
+
+ Environment host_env = Host::GetEnvironment();
+
+ // Always check to see if we have an environment override for the path to the
+ // debugserver to use and use it if we do.
+ std::string env_debugserver_path = host_env.lookup("LLDB_DEBUGSERVER_PATH");
+ if (!env_debugserver_path.empty()) {
+ debugserver_file_spec.SetFile(env_debugserver_path,
+ FileSpec::Style::native);
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s() gdb-remote stub exe path set "
+ "from environment variable: %s",
+ __FUNCTION__, env_debugserver_path.c_str());
+ } else
+ debugserver_file_spec = g_debugserver_file_spec;
+ bool debugserver_exists =
+ FileSystem::Instance().Exists(debugserver_file_spec);
+ if (!debugserver_exists) {
+ // The debugserver binary is in the LLDB.framework/Resources directory.
+ debugserver_file_spec = HostInfo::GetSupportExeDir();
+ if (debugserver_file_spec) {
+ debugserver_file_spec.AppendPathComponent(DEBUGSERVER_BASENAME);
+ debugserver_exists = FileSystem::Instance().Exists(debugserver_file_spec);
+ if (debugserver_exists) {
+ if (log)
+ log->Printf(
+ "GDBRemoteCommunication::%s() found gdb-remote stub exe '%s'",
+ __FUNCTION__, debugserver_file_spec.GetPath().c_str());
+
+ g_debugserver_file_spec = debugserver_file_spec;
+ } else {
+ if (platform)
+ debugserver_file_spec =
+ platform->LocateExecutable(DEBUGSERVER_BASENAME);
+ else
+ debugserver_file_spec.Clear();
+ if (debugserver_file_spec) {
+ // Platform::LocateExecutable() wouldn't return a path if it doesn't
+ // exist
+ debugserver_exists = true;
+ } else {
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s() could not find "
+ "gdb-remote stub exe '%s'",
+ __FUNCTION__, debugserver_file_spec.GetPath().c_str());
+ }
+ // Don't cache the platform specific GDB server binary as it could
+ // change from platform to platform
+ g_debugserver_file_spec.Clear();
+ }
+ }
+ }
+
+ if (debugserver_exists) {
+ debugserver_file_spec.GetPath(debugserver_path, sizeof(debugserver_path));
+
+ Args &debugserver_args = launch_info.GetArguments();
+ debugserver_args.Clear();
+
+ // Start args with "debugserver /file/path -r --"
+ debugserver_args.AppendArgument(llvm::StringRef(debugserver_path));
+
+#if !defined(__APPLE__)
+ // First argument to lldb-server must be mode in which to run.
+ debugserver_args.AppendArgument(llvm::StringRef("gdbserver"));
+#endif
+
+ // If a url is supplied then use it
+ if (url)
+ debugserver_args.AppendArgument(llvm::StringRef(url));
+
+ if (pass_comm_fd >= 0) {
+ StreamString fd_arg;
+ fd_arg.Printf("--fd=%i", pass_comm_fd);
+ debugserver_args.AppendArgument(fd_arg.GetString());
+ // Send "pass_comm_fd" down to the inferior so it can use it to
+ // communicate back with this process
+ launch_info.AppendDuplicateFileAction(pass_comm_fd, pass_comm_fd);
+ }
+
+ // use native registers, not the GDB registers
+ debugserver_args.AppendArgument(llvm::StringRef("--native-regs"));
+
+ if (launch_info.GetLaunchInSeparateProcessGroup()) {
+ debugserver_args.AppendArgument(llvm::StringRef("--setsid"));
+ }
+
+ llvm::SmallString<128> named_pipe_path;
+ // socket_pipe is used by debug server to communicate back either
+ // TCP port or domain socket name which it listens on.
+ // The second purpose of the pipe to serve as a synchronization point -
+ // once data is written to the pipe, debug server is up and running.
+ Pipe socket_pipe;
+
+ // port is null when debug server should listen on domain socket - we're
+ // not interested in port value but rather waiting for debug server to
+ // become available.
+ if (pass_comm_fd == -1) {
+ if (url) {
+// Create a temporary file to get the stdout/stderr and redirect the output of
+// the command into this file. We will later read this file if all goes well
+// and fill the data into "command_output_ptr"
+#if defined(__APPLE__)
+ // Binding to port zero, we need to figure out what port it ends up
+ // using using a named pipe...
+ error = socket_pipe.CreateWithUniqueName("debugserver-named-pipe",
+ false, named_pipe_path);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s() "
+ "named pipe creation failed: %s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
+ debugserver_args.AppendArgument(llvm::StringRef("--named-pipe"));
+ debugserver_args.AppendArgument(named_pipe_path);
+#else
+ // Binding to port zero, we need to figure out what port it ends up
+ // using using an unnamed pipe...
+ error = socket_pipe.CreateNew(true);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s() "
+ "unnamed pipe creation failed: %s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
+ pipe_t write = socket_pipe.GetWritePipe();
+ debugserver_args.AppendArgument(llvm::StringRef("--pipe"));
+ debugserver_args.AppendArgument(llvm::to_string(write));
+ launch_info.AppendCloseFileAction(socket_pipe.GetReadFileDescriptor());
+#endif
+ } else {
+ // No host and port given, so lets listen on our end and make the
+ // debugserver connect to us..
+ error = StartListenThread("127.0.0.1", 0);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s() unable to start listen "
+ "thread: %s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
+
+ ConnectionFileDescriptor *connection =
+ (ConnectionFileDescriptor *)GetConnection();
+ // Wait for 10 seconds to resolve the bound port
+ uint16_t port_ = connection->GetListeningPort(std::chrono::seconds(10));
+ if (port_ > 0) {
+ char port_cstr[32];
+ snprintf(port_cstr, sizeof(port_cstr), "127.0.0.1:%i", port_);
+ // Send the host and port down that debugserver and specify an option
+ // so that it connects back to the port we are listening to in this
+ // process
+ debugserver_args.AppendArgument(llvm::StringRef("--reverse-connect"));
+ debugserver_args.AppendArgument(llvm::StringRef(port_cstr));
+ if (port)
+ *port = port_;
+ } else {
+ error.SetErrorString("failed to bind to port 0 on 127.0.0.1");
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s() failed: %s", __FUNCTION__,
+ error.AsCString());
+ return error;
+ }
+ }
+ }
+ std::string env_debugserver_log_file =
+ host_env.lookup("LLDB_DEBUGSERVER_LOG_FILE");
+ if (!env_debugserver_log_file.empty()) {
+ debugserver_args.AppendArgument(
+ llvm::formatv("--log-file={0}", env_debugserver_log_file).str());
+ }
+
+#if defined(__APPLE__)
+ const char *env_debugserver_log_flags =
+ getenv("LLDB_DEBUGSERVER_LOG_FLAGS");
+ if (env_debugserver_log_flags) {
+ debugserver_args.AppendArgument(
+ llvm::formatv("--log-flags={0}", env_debugserver_log_flags).str());
+ }
+#else
+ std::string env_debugserver_log_channels =
+ host_env.lookup("LLDB_SERVER_LOG_CHANNELS");
+ if (!env_debugserver_log_channels.empty()) {
+ debugserver_args.AppendArgument(
+ llvm::formatv("--log-channels={0}", env_debugserver_log_channels)
+ .str());
+ }
+#endif
+
+ // Add additional args, starting with LLDB_DEBUGSERVER_EXTRA_ARG_1 until an
+ // env var doesn't come back.
+ uint32_t env_var_index = 1;
+ bool has_env_var;
+ do {
+ char env_var_name[64];
+ snprintf(env_var_name, sizeof(env_var_name),
+ "LLDB_DEBUGSERVER_EXTRA_ARG_%" PRIu32, env_var_index++);
+ std::string extra_arg = host_env.lookup(env_var_name);
+ has_env_var = !extra_arg.empty();
+
+ if (has_env_var) {
+ debugserver_args.AppendArgument(llvm::StringRef(extra_arg));
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s adding env var %s contents "
+ "to stub command line (%s)",
+ __FUNCTION__, env_var_name, extra_arg.c_str());
+ }
+ } while (has_env_var);
+
+ if (inferior_args && inferior_args->GetArgumentCount() > 0) {
+ debugserver_args.AppendArgument(llvm::StringRef("--"));
+ debugserver_args.AppendArguments(*inferior_args);
+ }
+
+ // Copy the current environment to the gdbserver/debugserver instance
+ launch_info.GetEnvironment() = host_env;
+
+ // Close STDIN, STDOUT and STDERR.
+ launch_info.AppendCloseFileAction(STDIN_FILENO);
+ launch_info.AppendCloseFileAction(STDOUT_FILENO);
+ launch_info.AppendCloseFileAction(STDERR_FILENO);
+
+ // Redirect STDIN, STDOUT and STDERR to "/dev/null".
+ launch_info.AppendSuppressFileAction(STDIN_FILENO, true, false);
+ launch_info.AppendSuppressFileAction(STDOUT_FILENO, false, true);
+ launch_info.AppendSuppressFileAction(STDERR_FILENO, false, true);
+
+ if (log) {
+ StreamString string_stream;
+ Platform *const platform = nullptr;
+ launch_info.Dump(string_stream, platform);
+ log->Printf("launch info for gdb-remote stub:\n%s",
+ string_stream.GetData());
+ }
+ error = Host::LaunchProcess(launch_info);
+
+ if (error.Success() &&
+ (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) &&
+ pass_comm_fd == -1) {
+ if (named_pipe_path.size() > 0) {
+ error = socket_pipe.OpenAsReader(named_pipe_path, false);
+ if (error.Fail())
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s() "
+ "failed to open named pipe %s for reading: %s",
+ __FUNCTION__, named_pipe_path.c_str(),
+ error.AsCString());
+ }
+
+ if (socket_pipe.CanWrite())
+ socket_pipe.CloseWriteFileDescriptor();
+ if (socket_pipe.CanRead()) {
+ char port_cstr[PATH_MAX] = {0};
+ port_cstr[0] = '\0';
+ size_t num_bytes = sizeof(port_cstr);
+ // Read port from pipe with 10 second timeout.
+ error = socket_pipe.ReadWithTimeout(
+ port_cstr, num_bytes, std::chrono::seconds{10}, num_bytes);
+ if (error.Success() && (port != nullptr)) {
+ assert(num_bytes > 0 && port_cstr[num_bytes - 1] == '\0');
+ uint16_t child_port = StringConvert::ToUInt32(port_cstr, 0);
+ if (*port == 0 || *port == child_port) {
+ *port = child_port;
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s() "
+ "debugserver listens %u port",
+ __FUNCTION__, *port);
+ } else {
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s() "
+ "debugserver listening on port "
+ "%d but requested port was %d",
+ __FUNCTION__, (uint32_t)child_port,
+ (uint32_t)(*port));
+ }
+ } else {
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s() "
+ "failed to read a port value from pipe %s: %s",
+ __FUNCTION__, named_pipe_path.c_str(),
+ error.AsCString());
+ }
+ socket_pipe.Close();
+ }
+
+ if (named_pipe_path.size() > 0) {
+ const auto err = socket_pipe.Delete(named_pipe_path);
+ if (err.Fail()) {
+ if (log)
+ log->Printf(
+ "GDBRemoteCommunication::%s failed to delete pipe %s: %s",
+ __FUNCTION__, named_pipe_path.c_str(), err.AsCString());
+ }
+ }
+
+ // Make sure we actually connect with the debugserver...
+ JoinListenThread();
+ }
+ } else {
+ error.SetErrorStringWithFormat("unable to locate " DEBUGSERVER_BASENAME);
+ }
+
+ if (error.Fail()) {
+ if (log)
+ log->Printf("GDBRemoteCommunication::%s() failed: %s", __FUNCTION__,
+ error.AsCString());
+ }
+
+ return error;
+}
+
+void GDBRemoteCommunication::DumpHistory(Stream &strm) { m_history.Dump(strm); }
+
+void GDBRemoteCommunication::SetHistoryStream(llvm::raw_ostream *strm) {
+ m_history.SetStream(strm);
+}
+
+llvm::Error
+GDBRemoteCommunication::ConnectLocally(GDBRemoteCommunication &client,
+ GDBRemoteCommunication &server) {
+ const bool child_processes_inherit = false;
+ const int backlog = 5;
+ TCPSocket listen_socket(true, child_processes_inherit);
+ if (llvm::Error error =
+ listen_socket.Listen("127.0.0.1:0", backlog).ToError())
+ return error;
+
+ Socket *accept_socket;
+ std::future<Status> accept_status = std::async(
+ std::launch::async, [&] { return listen_socket.Accept(accept_socket); });
+
+ llvm::SmallString<32> remote_addr;
+ llvm::raw_svector_ostream(remote_addr)
+ << "connect://127.0.0.1:" << listen_socket.GetLocalPortNumber();
+
+ std::unique_ptr<ConnectionFileDescriptor> conn_up(
+ new ConnectionFileDescriptor());
+ Status status;
+ if (conn_up->Connect(remote_addr, &status) != lldb::eConnectionStatusSuccess)
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "Unable to connect: %s", status.AsCString());
+
+ client.SetConnection(conn_up.release());
+ if (llvm::Error error = accept_status.get().ToError())
+ return error;
+
+ server.SetConnection(new ConnectionFileDescriptor(accept_socket));
+ return llvm::Error::success();
+}
+
+GDBRemoteCommunication::ScopedTimeout::ScopedTimeout(
+ GDBRemoteCommunication &gdb_comm, std::chrono::seconds timeout)
+ : m_gdb_comm(gdb_comm), m_timeout_modified(false) {
+ auto curr_timeout = gdb_comm.GetPacketTimeout();
+ // Only update the timeout if the timeout is greater than the current
+ // timeout. If the current timeout is larger, then just use that.
+ if (curr_timeout < timeout) {
+ m_timeout_modified = true;
+ m_saved_timeout = m_gdb_comm.SetPacketTimeout(timeout);
+ }
+}
+
+GDBRemoteCommunication::ScopedTimeout::~ScopedTimeout() {
+ // Only restore the timeout if we set it in the constructor.
+ if (m_timeout_modified)
+ m_gdb_comm.SetPacketTimeout(m_saved_timeout);
+}
+
+// This function is called via the Communications class read thread when bytes
+// become available for this connection. This function will consume all
+// incoming bytes and try to parse whole packets as they become available. Full
+// packets are placed in a queue, so that all packet requests can simply pop
+// from this queue. Async notification packets will be dispatched immediately
+// to the ProcessGDBRemote Async thread via an event.
+void GDBRemoteCommunication::AppendBytesToCache(const uint8_t *bytes,
+ size_t len, bool broadcast,
+ lldb::ConnectionStatus status) {
+ StringExtractorGDBRemote packet;
+
+ while (true) {
+ PacketType type = CheckForPacket(bytes, len, packet);
+
+ // scrub the data so we do not pass it back to CheckForPacket on future
+ // passes of the loop
+ bytes = nullptr;
+ len = 0;
+
+ // we may have received no packet so lets bail out
+ if (type == PacketType::Invalid)
+ break;
+
+ if (type == PacketType::Standard) {
+ // scope for the mutex
+ {
+ // lock down the packet queue
+ std::lock_guard<std::mutex> guard(m_packet_queue_mutex);
+ // push a new packet into the queue
+ m_packet_queue.push(packet);
+ // Signal condition variable that we have a packet
+ m_condition_queue_not_empty.notify_one();
+ }
+ }
+
+ if (type == PacketType::Notify) {
+ // put this packet into an event
+ const char *pdata = packet.GetStringRef().c_str();
+
+ // as the communication class, we are a broadcaster and the async thread
+ // is tuned to listen to us
+ BroadcastEvent(eBroadcastBitGdbReadThreadGotNotify,
+ new EventDataBytes(pdata));
+ }
+ }
+}
+
+void llvm::format_provider<GDBRemoteCommunication::PacketResult>::format(
+ const GDBRemoteCommunication::PacketResult &result, raw_ostream &Stream,
+ StringRef Style) {
+ using PacketResult = GDBRemoteCommunication::PacketResult;
+
+ switch (result) {
+ case PacketResult::Success:
+ Stream << "Success";
+ break;
+ case PacketResult::ErrorSendFailed:
+ Stream << "ErrorSendFailed";
+ break;
+ case PacketResult::ErrorSendAck:
+ Stream << "ErrorSendAck";
+ break;
+ case PacketResult::ErrorReplyFailed:
+ Stream << "ErrorReplyFailed";
+ break;
+ case PacketResult::ErrorReplyTimeout:
+ Stream << "ErrorReplyTimeout";
+ break;
+ case PacketResult::ErrorReplyInvalid:
+ Stream << "ErrorReplyInvalid";
+ break;
+ case PacketResult::ErrorReplyAck:
+ Stream << "ErrorReplyAck";
+ break;
+ case PacketResult::ErrorDisconnected:
+ Stream << "ErrorDisconnected";
+ break;
+ case PacketResult::ErrorNoSequenceLock:
+ Stream << "ErrorNoSequenceLock";
+ break;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
new file mode 100644
index 000000000000..bb777a5c26a7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
@@ -0,0 +1,238 @@
+//===-- GDBRemoteCommunication.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 liblldb_GDBRemoteCommunication_h_
+#define liblldb_GDBRemoteCommunication_h_
+
+#include "GDBRemoteCommunicationHistory.h"
+
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "lldb/Core/Communication.h"
+#include "lldb/Host/Config.h"
+#include "lldb/Host/HostThread.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/Listener.h"
+#include "lldb/Utility/Predicate.h"
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+#include "lldb/lldb-public.h"
+
+namespace lldb_private {
+namespace process_gdb_remote {
+
+enum GDBStoppointType {
+ eStoppointInvalid = -1,
+ eBreakpointSoftware = 0,
+ eBreakpointHardware,
+ eWatchpointWrite,
+ eWatchpointRead,
+ eWatchpointReadWrite
+};
+
+enum class CompressionType {
+ None = 0, // no compression
+ ZlibDeflate, // zlib's deflate compression scheme, requires zlib or Apple's
+ // libcompression
+ LZFSE, // an Apple compression scheme, requires Apple's libcompression
+ LZ4, // lz compression - called "lz4 raw" in libcompression terms, compat with
+ // https://code.google.com/p/lz4/
+ LZMA, // Lempel–Ziv–Markov chain algorithm
+};
+
+class ProcessGDBRemote;
+
+class GDBRemoteCommunication : public Communication {
+public:
+ enum {
+ eBroadcastBitRunPacketSent = kLoUserBroadcastBit,
+ eBroadcastBitGdbReadThreadGotNotify =
+ kLoUserBroadcastBit << 1 // Sent when we received a notify packet.
+ };
+
+ enum class PacketType { Invalid = 0, Standard, Notify };
+
+ enum class PacketResult {
+ Success = 0, // Success
+ ErrorSendFailed, // Status sending the packet
+ ErrorSendAck, // Didn't get an ack back after sending a packet
+ ErrorReplyFailed, // Status getting the reply
+ ErrorReplyTimeout, // Timed out waiting for reply
+ ErrorReplyInvalid, // Got a reply but it wasn't valid for the packet that
+ // was sent
+ ErrorReplyAck, // Sending reply ack failed
+ ErrorDisconnected, // We were disconnected
+ ErrorNoSequenceLock // We couldn't get the sequence lock for a multi-packet
+ // request
+ };
+
+ // Class to change the timeout for a given scope and restore it to the
+ // original value when the
+ // created ScopedTimeout object got out of scope
+ class ScopedTimeout {
+ public:
+ ScopedTimeout(GDBRemoteCommunication &gdb_comm,
+ std::chrono::seconds timeout);
+ ~ScopedTimeout();
+
+ private:
+ GDBRemoteCommunication &m_gdb_comm;
+ std::chrono::seconds m_saved_timeout;
+ // Don't ever reduce the timeout for a packet, only increase it. If the
+ // requested timeout if less than the current timeout, we don't set it
+ // and won't need to restore it.
+ bool m_timeout_modified;
+ };
+
+ GDBRemoteCommunication(const char *comm_name, const char *listener_name);
+
+ ~GDBRemoteCommunication() override;
+
+ PacketResult GetAck();
+
+ size_t SendAck();
+
+ size_t SendNack();
+
+ char CalculcateChecksum(llvm::StringRef payload);
+
+ PacketType CheckForPacket(const uint8_t *src, size_t src_len,
+ StringExtractorGDBRemote &packet);
+
+ bool GetSendAcks() { return m_send_acks; }
+
+ // Set the global packet timeout.
+ //
+ // For clients, this is the timeout that gets used when sending
+ // packets and waiting for responses. For servers, this is used when waiting
+ // for ACKs.
+ std::chrono::seconds SetPacketTimeout(std::chrono::seconds packet_timeout) {
+ const auto old_packet_timeout = m_packet_timeout;
+ m_packet_timeout = packet_timeout;
+ return old_packet_timeout;
+ }
+
+ std::chrono::seconds GetPacketTimeout() const { return m_packet_timeout; }
+
+ // Start a debugserver instance on the current host using the
+ // supplied connection URL.
+ Status StartDebugserverProcess(
+ const char *url,
+ Platform *platform, // If non nullptr, then check with the platform for
+ // the GDB server binary if it can't be located
+ ProcessLaunchInfo &launch_info, uint16_t *port, const Args *inferior_args,
+ int pass_comm_fd); // Communication file descriptor to pass during
+ // fork/exec to avoid having to connect/accept
+
+ void DumpHistory(Stream &strm);
+ void SetHistoryStream(llvm::raw_ostream *strm);
+
+ static llvm::Error ConnectLocally(GDBRemoteCommunication &client,
+ GDBRemoteCommunication &server);
+
+protected:
+ std::chrono::seconds m_packet_timeout;
+ uint32_t m_echo_number;
+ LazyBool m_supports_qEcho;
+ GDBRemoteCommunicationHistory m_history;
+ bool m_send_acks;
+ bool m_is_platform; // Set to true if this class represents a platform,
+ // false if this class represents a debug session for
+ // a single process
+
+ CompressionType m_compression_type;
+
+ PacketResult SendPacketNoLock(llvm::StringRef payload);
+ PacketResult SendRawPacketNoLock(llvm::StringRef payload,
+ bool skip_ack = false);
+
+ PacketResult ReadPacket(StringExtractorGDBRemote &response,
+ Timeout<std::micro> timeout, bool sync_on_timeout);
+
+ PacketResult ReadPacketWithOutputSupport(
+ StringExtractorGDBRemote &response, Timeout<std::micro> timeout,
+ bool sync_on_timeout,
+ llvm::function_ref<void(llvm::StringRef)> output_callback);
+
+ // Pop a packet from the queue in a thread safe manner
+ PacketResult PopPacketFromQueue(StringExtractorGDBRemote &response,
+ Timeout<std::micro> timeout);
+
+ PacketResult WaitForPacketNoLock(StringExtractorGDBRemote &response,
+ Timeout<std::micro> timeout,
+ bool sync_on_timeout);
+
+ bool CompressionIsEnabled() {
+ return m_compression_type != CompressionType::None;
+ }
+
+ // If compression is enabled, decompress the packet in m_bytes and update
+ // m_bytes with the uncompressed version.
+ // Returns 'true' packet was decompressed and m_bytes is the now-decompressed
+ // text.
+ // Returns 'false' if unable to decompress or if the checksum was invalid.
+ //
+ // NB: Once the packet has been decompressed, checksum cannot be computed
+ // based
+ // on m_bytes. The checksum was for the compressed packet.
+ bool DecompressPacket();
+
+ Status StartListenThread(const char *hostname = "127.0.0.1",
+ uint16_t port = 0);
+
+ bool JoinListenThread();
+
+ static lldb::thread_result_t ListenThread(lldb::thread_arg_t arg);
+
+ // GDB-Remote read thread
+ // . this thread constantly tries to read from the communication
+ // class and stores all packets received in a queue. The usual
+ // threads read requests simply pop packets off the queue in the
+ // usual order.
+ // This setup allows us to intercept and handle async packets, such
+ // as the notify packet.
+
+ // This method is defined as part of communication.h
+ // when the read thread gets any bytes it will pass them on to this function
+ void AppendBytesToCache(const uint8_t *bytes, size_t len, bool broadcast,
+ lldb::ConnectionStatus status) override;
+
+private:
+ std::queue<StringExtractorGDBRemote> m_packet_queue; // The packet queue
+ std::mutex m_packet_queue_mutex; // Mutex for accessing queue
+ std::condition_variable
+ m_condition_queue_not_empty; // Condition variable to wait for packets
+
+ HostThread m_listen_thread;
+ std::string m_listen_url;
+
+#if defined(HAVE_LIBCOMPRESSION)
+ CompressionType m_decompression_scratch_type = CompressionType::None;
+ void *m_decompression_scratch = nullptr;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunication);
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+namespace llvm {
+template <>
+struct format_provider<
+ lldb_private::process_gdb_remote::GDBRemoteCommunication::PacketResult> {
+ static void format(const lldb_private::process_gdb_remote::
+ GDBRemoteCommunication::PacketResult &state,
+ raw_ostream &Stream, StringRef Style);
+};
+} // namespace llvm
+
+#endif // liblldb_GDBRemoteCommunication_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
new file mode 100644
index 000000000000..9797184026e0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -0,0 +1,4005 @@
+//===-- GDBRemoteCommunicationClient.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 "GDBRemoteCommunicationClient.h"
+
+#include <math.h>
+#include <sys/stat.h>
+
+#include <numeric>
+#include <sstream>
+
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/XML.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/JSON.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "ProcessGDBRemote.h"
+#include "ProcessGDBRemoteLog.h"
+#include "lldb/Host/Config.h"
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+
+#include "llvm/ADT/StringSwitch.h"
+
+#if defined(__APPLE__)
+#ifndef HAVE_LIBCOMPRESSION
+#define HAVE_LIBCOMPRESSION
+#endif
+#include <compression.h>
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+using namespace std::chrono;
+
+// GDBRemoteCommunicationClient constructor
+GDBRemoteCommunicationClient::GDBRemoteCommunicationClient()
+ : GDBRemoteClientBase("gdb-remote.client", "gdb-remote.client.rx_packet"),
+ m_supports_not_sending_acks(eLazyBoolCalculate),
+ m_supports_thread_suffix(eLazyBoolCalculate),
+ m_supports_threads_in_stop_reply(eLazyBoolCalculate),
+ m_supports_vCont_all(eLazyBoolCalculate),
+ m_supports_vCont_any(eLazyBoolCalculate),
+ m_supports_vCont_c(eLazyBoolCalculate),
+ m_supports_vCont_C(eLazyBoolCalculate),
+ m_supports_vCont_s(eLazyBoolCalculate),
+ m_supports_vCont_S(eLazyBoolCalculate),
+ m_qHostInfo_is_valid(eLazyBoolCalculate),
+ m_curr_pid_is_valid(eLazyBoolCalculate),
+ m_qProcessInfo_is_valid(eLazyBoolCalculate),
+ m_qGDBServerVersion_is_valid(eLazyBoolCalculate),
+ m_supports_alloc_dealloc_memory(eLazyBoolCalculate),
+ m_supports_memory_region_info(eLazyBoolCalculate),
+ m_supports_watchpoint_support_info(eLazyBoolCalculate),
+ m_supports_detach_stay_stopped(eLazyBoolCalculate),
+ m_watchpoints_trigger_after_instruction(eLazyBoolCalculate),
+ m_attach_or_wait_reply(eLazyBoolCalculate),
+ m_prepare_for_reg_writing_reply(eLazyBoolCalculate),
+ m_supports_p(eLazyBoolCalculate), m_supports_x(eLazyBoolCalculate),
+ m_avoid_g_packets(eLazyBoolCalculate),
+ m_supports_QSaveRegisterState(eLazyBoolCalculate),
+ m_supports_qXfer_auxv_read(eLazyBoolCalculate),
+ m_supports_qXfer_libraries_read(eLazyBoolCalculate),
+ m_supports_qXfer_libraries_svr4_read(eLazyBoolCalculate),
+ m_supports_qXfer_features_read(eLazyBoolCalculate),
+ m_supports_qXfer_memory_map_read(eLazyBoolCalculate),
+ m_supports_augmented_libraries_svr4_read(eLazyBoolCalculate),
+ m_supports_jThreadExtendedInfo(eLazyBoolCalculate),
+ m_supports_jLoadedDynamicLibrariesInfos(eLazyBoolCalculate),
+ m_supports_jGetSharedCacheInfo(eLazyBoolCalculate),
+ m_supports_QPassSignals(eLazyBoolCalculate),
+ m_supports_error_string_reply(eLazyBoolCalculate),
+ m_supports_qProcessInfoPID(true), m_supports_qfProcessInfo(true),
+ m_supports_qUserName(true), m_supports_qGroupName(true),
+ m_supports_qThreadStopInfo(true), m_supports_z0(true),
+ m_supports_z1(true), m_supports_z2(true), m_supports_z3(true),
+ m_supports_z4(true), m_supports_QEnvironment(true),
+ m_supports_QEnvironmentHexEncoded(true), m_supports_qSymbol(true),
+ m_qSymbol_requests_done(false), m_supports_qModuleInfo(true),
+ m_supports_jThreadsInfo(true), m_supports_jModulesInfo(true),
+ m_curr_pid(LLDB_INVALID_PROCESS_ID), m_curr_tid(LLDB_INVALID_THREAD_ID),
+ m_curr_tid_run(LLDB_INVALID_THREAD_ID),
+ m_num_supported_hardware_watchpoints(0), m_host_arch(), m_process_arch(),
+ m_os_build(), m_os_kernel(), m_hostname(), m_gdb_server_name(),
+ m_gdb_server_version(UINT32_MAX), m_default_packet_timeout(0),
+ m_max_packet_size(0), m_qSupported_response(),
+ m_supported_async_json_packets_is_valid(false),
+ m_supported_async_json_packets_sp(), m_qXfer_memory_map(),
+ m_qXfer_memory_map_loaded(false) {}
+
+// Destructor
+GDBRemoteCommunicationClient::~GDBRemoteCommunicationClient() {
+ if (IsConnected())
+ Disconnect();
+}
+
+bool GDBRemoteCommunicationClient::HandshakeWithServer(Status *error_ptr) {
+ ResetDiscoverableSettings(false);
+
+ // Start the read thread after we send the handshake ack since if we fail to
+ // send the handshake ack, there is no reason to continue...
+ if (SendAck()) {
+ // Wait for any responses that might have been queued up in the remote
+ // GDB server and flush them all
+ StringExtractorGDBRemote response;
+ PacketResult packet_result = PacketResult::Success;
+ while (packet_result == PacketResult::Success)
+ packet_result = ReadPacket(response, milliseconds(10), false);
+
+ // The return value from QueryNoAckModeSupported() is true if the packet
+ // was sent and _any_ response (including UNIMPLEMENTED) was received), or
+ // false if no response was received. This quickly tells us if we have a
+ // live connection to a remote GDB server...
+ if (QueryNoAckModeSupported()) {
+ return true;
+ } else {
+ if (error_ptr)
+ error_ptr->SetErrorString("failed to get reply to handshake packet");
+ }
+ } else {
+ if (error_ptr)
+ error_ptr->SetErrorString("failed to send the handshake ack");
+ }
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::GetEchoSupported() {
+ if (m_supports_qEcho == eLazyBoolCalculate) {
+ GetRemoteQSupported();
+ }
+ return m_supports_qEcho == eLazyBoolYes;
+}
+
+bool GDBRemoteCommunicationClient::GetQPassSignalsSupported() {
+ if (m_supports_QPassSignals == eLazyBoolCalculate) {
+ GetRemoteQSupported();
+ }
+ return m_supports_QPassSignals == eLazyBoolYes;
+}
+
+bool GDBRemoteCommunicationClient::GetAugmentedLibrariesSVR4ReadSupported() {
+ if (m_supports_augmented_libraries_svr4_read == eLazyBoolCalculate) {
+ GetRemoteQSupported();
+ }
+ return m_supports_augmented_libraries_svr4_read == eLazyBoolYes;
+}
+
+bool GDBRemoteCommunicationClient::GetQXferLibrariesSVR4ReadSupported() {
+ if (m_supports_qXfer_libraries_svr4_read == eLazyBoolCalculate) {
+ GetRemoteQSupported();
+ }
+ return m_supports_qXfer_libraries_svr4_read == eLazyBoolYes;
+}
+
+bool GDBRemoteCommunicationClient::GetQXferLibrariesReadSupported() {
+ if (m_supports_qXfer_libraries_read == eLazyBoolCalculate) {
+ GetRemoteQSupported();
+ }
+ return m_supports_qXfer_libraries_read == eLazyBoolYes;
+}
+
+bool GDBRemoteCommunicationClient::GetQXferAuxvReadSupported() {
+ if (m_supports_qXfer_auxv_read == eLazyBoolCalculate) {
+ GetRemoteQSupported();
+ }
+ return m_supports_qXfer_auxv_read == eLazyBoolYes;
+}
+
+bool GDBRemoteCommunicationClient::GetQXferFeaturesReadSupported() {
+ if (m_supports_qXfer_features_read == eLazyBoolCalculate) {
+ GetRemoteQSupported();
+ }
+ return m_supports_qXfer_features_read == eLazyBoolYes;
+}
+
+bool GDBRemoteCommunicationClient::GetQXferMemoryMapReadSupported() {
+ if (m_supports_qXfer_memory_map_read == eLazyBoolCalculate) {
+ GetRemoteQSupported();
+ }
+ return m_supports_qXfer_memory_map_read == eLazyBoolYes;
+}
+
+uint64_t GDBRemoteCommunicationClient::GetRemoteMaxPacketSize() {
+ if (m_max_packet_size == 0) {
+ GetRemoteQSupported();
+ }
+ return m_max_packet_size;
+}
+
+bool GDBRemoteCommunicationClient::QueryNoAckModeSupported() {
+ if (m_supports_not_sending_acks == eLazyBoolCalculate) {
+ m_send_acks = true;
+ m_supports_not_sending_acks = eLazyBoolNo;
+
+ // This is the first real packet that we'll send in a debug session and it
+ // may take a little longer than normal to receive a reply. Wait at least
+ // 6 seconds for a reply to this packet.
+
+ ScopedTimeout timeout(*this, std::max(GetPacketTimeout(), seconds(6)));
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("QStartNoAckMode", response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse()) {
+ m_send_acks = false;
+ m_supports_not_sending_acks = eLazyBoolYes;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+void GDBRemoteCommunicationClient::GetListThreadsInStopReplySupported() {
+ if (m_supports_threads_in_stop_reply == eLazyBoolCalculate) {
+ m_supports_threads_in_stop_reply = eLazyBoolNo;
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("QListThreadsInStopReply", response,
+ false) == PacketResult::Success) {
+ if (response.IsOKResponse())
+ m_supports_threads_in_stop_reply = eLazyBoolYes;
+ }
+ }
+}
+
+bool GDBRemoteCommunicationClient::GetVAttachOrWaitSupported() {
+ if (m_attach_or_wait_reply == eLazyBoolCalculate) {
+ m_attach_or_wait_reply = eLazyBoolNo;
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qVAttachOrWaitSupported", response,
+ false) == PacketResult::Success) {
+ if (response.IsOKResponse())
+ m_attach_or_wait_reply = eLazyBoolYes;
+ }
+ }
+ return m_attach_or_wait_reply == eLazyBoolYes;
+}
+
+bool GDBRemoteCommunicationClient::GetSyncThreadStateSupported() {
+ if (m_prepare_for_reg_writing_reply == eLazyBoolCalculate) {
+ m_prepare_for_reg_writing_reply = eLazyBoolNo;
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qSyncThreadStateSupported", response,
+ false) == PacketResult::Success) {
+ if (response.IsOKResponse())
+ m_prepare_for_reg_writing_reply = eLazyBoolYes;
+ }
+ }
+ return m_prepare_for_reg_writing_reply == eLazyBoolYes;
+}
+
+void GDBRemoteCommunicationClient::ResetDiscoverableSettings(bool did_exec) {
+ if (!did_exec) {
+ // Hard reset everything, this is when we first connect to a GDB server
+ m_supports_not_sending_acks = eLazyBoolCalculate;
+ m_supports_thread_suffix = eLazyBoolCalculate;
+ m_supports_threads_in_stop_reply = eLazyBoolCalculate;
+ m_supports_vCont_c = eLazyBoolCalculate;
+ m_supports_vCont_C = eLazyBoolCalculate;
+ m_supports_vCont_s = eLazyBoolCalculate;
+ m_supports_vCont_S = eLazyBoolCalculate;
+ m_supports_p = eLazyBoolCalculate;
+ m_supports_x = eLazyBoolCalculate;
+ m_supports_QSaveRegisterState = eLazyBoolCalculate;
+ m_qHostInfo_is_valid = eLazyBoolCalculate;
+ m_curr_pid_is_valid = eLazyBoolCalculate;
+ m_qGDBServerVersion_is_valid = eLazyBoolCalculate;
+ m_supports_alloc_dealloc_memory = eLazyBoolCalculate;
+ m_supports_memory_region_info = eLazyBoolCalculate;
+ m_prepare_for_reg_writing_reply = eLazyBoolCalculate;
+ m_attach_or_wait_reply = eLazyBoolCalculate;
+ m_avoid_g_packets = eLazyBoolCalculate;
+ m_supports_qXfer_auxv_read = eLazyBoolCalculate;
+ m_supports_qXfer_libraries_read = eLazyBoolCalculate;
+ m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate;
+ m_supports_qXfer_features_read = eLazyBoolCalculate;
+ m_supports_qXfer_memory_map_read = eLazyBoolCalculate;
+ m_supports_augmented_libraries_svr4_read = eLazyBoolCalculate;
+ m_supports_qProcessInfoPID = true;
+ m_supports_qfProcessInfo = true;
+ m_supports_qUserName = true;
+ m_supports_qGroupName = true;
+ m_supports_qThreadStopInfo = true;
+ m_supports_z0 = true;
+ m_supports_z1 = true;
+ m_supports_z2 = true;
+ m_supports_z3 = true;
+ m_supports_z4 = true;
+ m_supports_QEnvironment = true;
+ m_supports_QEnvironmentHexEncoded = true;
+ m_supports_qSymbol = true;
+ m_qSymbol_requests_done = false;
+ m_supports_qModuleInfo = true;
+ m_host_arch.Clear();
+ m_os_version = llvm::VersionTuple();
+ m_os_build.clear();
+ m_os_kernel.clear();
+ m_hostname.clear();
+ m_gdb_server_name.clear();
+ m_gdb_server_version = UINT32_MAX;
+ m_default_packet_timeout = seconds(0);
+ m_max_packet_size = 0;
+ m_qSupported_response.clear();
+ m_supported_async_json_packets_is_valid = false;
+ m_supported_async_json_packets_sp.reset();
+ m_supports_jModulesInfo = true;
+ }
+
+ // These flags should be reset when we first connect to a GDB server and when
+ // our inferior process execs
+ m_qProcessInfo_is_valid = eLazyBoolCalculate;
+ m_process_arch.Clear();
+}
+
+void GDBRemoteCommunicationClient::GetRemoteQSupported() {
+ // Clear out any capabilities we expect to see in the qSupported response
+ m_supports_qXfer_auxv_read = eLazyBoolNo;
+ m_supports_qXfer_libraries_read = eLazyBoolNo;
+ m_supports_qXfer_libraries_svr4_read = eLazyBoolNo;
+ m_supports_augmented_libraries_svr4_read = eLazyBoolNo;
+ m_supports_qXfer_features_read = eLazyBoolNo;
+ m_supports_qXfer_memory_map_read = eLazyBoolNo;
+ m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if
+ // not, we assume no limit
+
+ // build the qSupported packet
+ std::vector<std::string> features = {"xmlRegisters=i386,arm,mips"};
+ StreamString packet;
+ packet.PutCString("qSupported");
+ for (uint32_t i = 0; i < features.size(); ++i) {
+ packet.PutCString(i == 0 ? ":" : ";");
+ packet.PutCString(features[i]);
+ }
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet.GetString(), response,
+ /*send_async=*/false) ==
+ PacketResult::Success) {
+ const char *response_cstr = response.GetStringRef().c_str();
+
+ // Hang on to the qSupported packet, so that platforms can do custom
+ // configuration of the transport before attaching/launching the process.
+ m_qSupported_response = response_cstr;
+
+ if (::strstr(response_cstr, "qXfer:auxv:read+"))
+ m_supports_qXfer_auxv_read = eLazyBoolYes;
+ if (::strstr(response_cstr, "qXfer:libraries-svr4:read+"))
+ m_supports_qXfer_libraries_svr4_read = eLazyBoolYes;
+ if (::strstr(response_cstr, "augmented-libraries-svr4-read")) {
+ m_supports_qXfer_libraries_svr4_read = eLazyBoolYes; // implied
+ m_supports_augmented_libraries_svr4_read = eLazyBoolYes;
+ }
+ if (::strstr(response_cstr, "qXfer:libraries:read+"))
+ m_supports_qXfer_libraries_read = eLazyBoolYes;
+ if (::strstr(response_cstr, "qXfer:features:read+"))
+ m_supports_qXfer_features_read = eLazyBoolYes;
+ if (::strstr(response_cstr, "qXfer:memory-map:read+"))
+ m_supports_qXfer_memory_map_read = eLazyBoolYes;
+
+ // Look for a list of compressions in the features list e.g.
+ // qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib-
+ // deflate,lzma
+ const char *features_list = ::strstr(response_cstr, "qXfer:features:");
+ if (features_list) {
+ const char *compressions =
+ ::strstr(features_list, "SupportedCompressions=");
+ if (compressions) {
+ std::vector<std::string> supported_compressions;
+ compressions += sizeof("SupportedCompressions=") - 1;
+ const char *end_of_compressions = strchr(compressions, ';');
+ if (end_of_compressions == nullptr) {
+ end_of_compressions = strchr(compressions, '\0');
+ }
+ const char *current_compression = compressions;
+ while (current_compression < end_of_compressions) {
+ const char *next_compression_name = strchr(current_compression, ',');
+ const char *end_of_this_word = next_compression_name;
+ if (next_compression_name == nullptr ||
+ end_of_compressions < next_compression_name) {
+ end_of_this_word = end_of_compressions;
+ }
+
+ if (end_of_this_word) {
+ if (end_of_this_word == current_compression) {
+ current_compression++;
+ } else {
+ std::string this_compression(
+ current_compression, end_of_this_word - current_compression);
+ supported_compressions.push_back(this_compression);
+ current_compression = end_of_this_word + 1;
+ }
+ } else {
+ supported_compressions.push_back(current_compression);
+ current_compression = end_of_compressions;
+ }
+ }
+
+ if (supported_compressions.size() > 0) {
+ MaybeEnableCompression(supported_compressions);
+ }
+ }
+ }
+
+ if (::strstr(response_cstr, "qEcho"))
+ m_supports_qEcho = eLazyBoolYes;
+ else
+ m_supports_qEcho = eLazyBoolNo;
+
+ if (::strstr(response_cstr, "QPassSignals+"))
+ m_supports_QPassSignals = eLazyBoolYes;
+ else
+ m_supports_QPassSignals = eLazyBoolNo;
+
+ const char *packet_size_str = ::strstr(response_cstr, "PacketSize=");
+ if (packet_size_str) {
+ StringExtractorGDBRemote packet_response(packet_size_str +
+ strlen("PacketSize="));
+ m_max_packet_size =
+ packet_response.GetHexMaxU64(/*little_endian=*/false, UINT64_MAX);
+ if (m_max_packet_size == 0) {
+ m_max_packet_size = UINT64_MAX; // Must have been a garbled response
+ Log *log(
+ ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf("Garbled PacketSize spec in qSupported response");
+ }
+ }
+ }
+}
+
+bool GDBRemoteCommunicationClient::GetThreadSuffixSupported() {
+ if (m_supports_thread_suffix == eLazyBoolCalculate) {
+ StringExtractorGDBRemote response;
+ m_supports_thread_suffix = eLazyBoolNo;
+ if (SendPacketAndWaitForResponse("QThreadSuffixSupported", response,
+ false) == PacketResult::Success) {
+ if (response.IsOKResponse())
+ m_supports_thread_suffix = eLazyBoolYes;
+ }
+ }
+ return m_supports_thread_suffix;
+}
+bool GDBRemoteCommunicationClient::GetVContSupported(char flavor) {
+ if (m_supports_vCont_c == eLazyBoolCalculate) {
+ StringExtractorGDBRemote response;
+ m_supports_vCont_any = eLazyBoolNo;
+ m_supports_vCont_all = eLazyBoolNo;
+ m_supports_vCont_c = eLazyBoolNo;
+ m_supports_vCont_C = eLazyBoolNo;
+ m_supports_vCont_s = eLazyBoolNo;
+ m_supports_vCont_S = eLazyBoolNo;
+ if (SendPacketAndWaitForResponse("vCont?", response, false) ==
+ PacketResult::Success) {
+ const char *response_cstr = response.GetStringRef().c_str();
+ if (::strstr(response_cstr, ";c"))
+ m_supports_vCont_c = eLazyBoolYes;
+
+ if (::strstr(response_cstr, ";C"))
+ m_supports_vCont_C = eLazyBoolYes;
+
+ if (::strstr(response_cstr, ";s"))
+ m_supports_vCont_s = eLazyBoolYes;
+
+ if (::strstr(response_cstr, ";S"))
+ m_supports_vCont_S = eLazyBoolYes;
+
+ if (m_supports_vCont_c == eLazyBoolYes &&
+ m_supports_vCont_C == eLazyBoolYes &&
+ m_supports_vCont_s == eLazyBoolYes &&
+ m_supports_vCont_S == eLazyBoolYes) {
+ m_supports_vCont_all = eLazyBoolYes;
+ }
+
+ if (m_supports_vCont_c == eLazyBoolYes ||
+ m_supports_vCont_C == eLazyBoolYes ||
+ m_supports_vCont_s == eLazyBoolYes ||
+ m_supports_vCont_S == eLazyBoolYes) {
+ m_supports_vCont_any = eLazyBoolYes;
+ }
+ }
+ }
+
+ switch (flavor) {
+ case 'a':
+ return m_supports_vCont_any;
+ case 'A':
+ return m_supports_vCont_all;
+ case 'c':
+ return m_supports_vCont_c;
+ case 'C':
+ return m_supports_vCont_C;
+ case 's':
+ return m_supports_vCont_s;
+ case 'S':
+ return m_supports_vCont_S;
+ default:
+ break;
+ }
+ return false;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationClient::SendThreadSpecificPacketAndWaitForResponse(
+ lldb::tid_t tid, StreamString &&payload, StringExtractorGDBRemote &response,
+ bool send_async) {
+ Lock lock(*this, send_async);
+ if (!lock) {
+ if (Log *log = ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(
+ GDBR_LOG_PROCESS | GDBR_LOG_PACKETS))
+ log->Printf("GDBRemoteCommunicationClient::%s: Didn't get sequence mutex "
+ "for %s packet.",
+ __FUNCTION__, payload.GetData());
+ return PacketResult::ErrorNoSequenceLock;
+ }
+
+ if (GetThreadSuffixSupported())
+ payload.Printf(";thread:%4.4" PRIx64 ";", tid);
+ else {
+ if (!SetCurrentThread(tid))
+ return PacketResult::ErrorSendFailed;
+ }
+
+ return SendPacketAndWaitForResponseNoLock(payload.GetString(), response);
+}
+
+// Check if the target supports 'p' packet. It sends out a 'p' packet and
+// checks the response. A normal packet will tell us that support is available.
+//
+// Takes a valid thread ID because p needs to apply to a thread.
+bool GDBRemoteCommunicationClient::GetpPacketSupported(lldb::tid_t tid) {
+ if (m_supports_p == eLazyBoolCalculate) {
+ m_supports_p = eLazyBoolNo;
+ StreamString payload;
+ payload.PutCString("p0");
+ StringExtractorGDBRemote response;
+ if (SendThreadSpecificPacketAndWaitForResponse(tid, std::move(payload),
+ response, false) ==
+ PacketResult::Success &&
+ response.IsNormalResponse()) {
+ m_supports_p = eLazyBoolYes;
+ }
+ }
+ return m_supports_p;
+}
+
+StructuredData::ObjectSP GDBRemoteCommunicationClient::GetThreadsInfo() {
+ // Get information on all threads at one using the "jThreadsInfo" packet
+ StructuredData::ObjectSP object_sp;
+
+ if (m_supports_jThreadsInfo) {
+ StringExtractorGDBRemote response;
+ response.SetResponseValidatorToJSON();
+ if (SendPacketAndWaitForResponse("jThreadsInfo", response, false) ==
+ PacketResult::Success) {
+ if (response.IsUnsupportedResponse()) {
+ m_supports_jThreadsInfo = false;
+ } else if (!response.Empty()) {
+ object_sp = StructuredData::ParseJSON(response.GetStringRef());
+ }
+ }
+ }
+ return object_sp;
+}
+
+bool GDBRemoteCommunicationClient::GetThreadExtendedInfoSupported() {
+ if (m_supports_jThreadExtendedInfo == eLazyBoolCalculate) {
+ StringExtractorGDBRemote response;
+ m_supports_jThreadExtendedInfo = eLazyBoolNo;
+ if (SendPacketAndWaitForResponse("jThreadExtendedInfo:", response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse()) {
+ m_supports_jThreadExtendedInfo = eLazyBoolYes;
+ }
+ }
+ }
+ return m_supports_jThreadExtendedInfo;
+}
+
+void GDBRemoteCommunicationClient::EnableErrorStringInPacket() {
+ if (m_supports_error_string_reply == eLazyBoolCalculate) {
+ StringExtractorGDBRemote response;
+ // We try to enable error strings in remote packets but if we fail, we just
+ // work in the older way.
+ m_supports_error_string_reply = eLazyBoolNo;
+ if (SendPacketAndWaitForResponse("QEnableErrorStrings", response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse()) {
+ m_supports_error_string_reply = eLazyBoolYes;
+ }
+ }
+ }
+}
+
+bool GDBRemoteCommunicationClient::GetLoadedDynamicLibrariesInfosSupported() {
+ if (m_supports_jLoadedDynamicLibrariesInfos == eLazyBoolCalculate) {
+ StringExtractorGDBRemote response;
+ m_supports_jLoadedDynamicLibrariesInfos = eLazyBoolNo;
+ if (SendPacketAndWaitForResponse("jGetLoadedDynamicLibrariesInfos:",
+ response,
+ false) == PacketResult::Success) {
+ if (response.IsOKResponse()) {
+ m_supports_jLoadedDynamicLibrariesInfos = eLazyBoolYes;
+ }
+ }
+ }
+ return m_supports_jLoadedDynamicLibrariesInfos;
+}
+
+bool GDBRemoteCommunicationClient::GetSharedCacheInfoSupported() {
+ if (m_supports_jGetSharedCacheInfo == eLazyBoolCalculate) {
+ StringExtractorGDBRemote response;
+ m_supports_jGetSharedCacheInfo = eLazyBoolNo;
+ if (SendPacketAndWaitForResponse("jGetSharedCacheInfo:", response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse()) {
+ m_supports_jGetSharedCacheInfo = eLazyBoolYes;
+ }
+ }
+ }
+ return m_supports_jGetSharedCacheInfo;
+}
+
+bool GDBRemoteCommunicationClient::GetxPacketSupported() {
+ if (m_supports_x == eLazyBoolCalculate) {
+ StringExtractorGDBRemote response;
+ m_supports_x = eLazyBoolNo;
+ char packet[256];
+ snprintf(packet, sizeof(packet), "x0,0");
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ m_supports_x = eLazyBoolYes;
+ }
+ }
+ return m_supports_x;
+}
+
+GDBRemoteCommunicationClient::PacketResult
+GDBRemoteCommunicationClient::SendPacketsAndConcatenateResponses(
+ const char *payload_prefix, std::string &response_string) {
+ Lock lock(*this, false);
+ if (!lock) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS |
+ GDBR_LOG_PACKETS));
+ if (log)
+ log->Printf("error: failed to get packet sequence mutex, not sending "
+ "packets with prefix '%s'",
+ payload_prefix);
+ return PacketResult::ErrorNoSequenceLock;
+ }
+
+ response_string = "";
+ std::string payload_prefix_str(payload_prefix);
+ unsigned int response_size = 0x1000;
+ if (response_size > GetRemoteMaxPacketSize()) { // May send qSupported packet
+ response_size = GetRemoteMaxPacketSize();
+ }
+
+ for (unsigned int offset = 0; true; offset += response_size) {
+ StringExtractorGDBRemote this_response;
+ // Construct payload
+ char sizeDescriptor[128];
+ snprintf(sizeDescriptor, sizeof(sizeDescriptor), "%x,%x", offset,
+ response_size);
+ PacketResult result = SendPacketAndWaitForResponseNoLock(
+ payload_prefix_str + sizeDescriptor, this_response);
+ if (result != PacketResult::Success)
+ return result;
+
+ const std::string &this_string = this_response.GetStringRef();
+
+ // Check for m or l as first character; l seems to mean this is the last
+ // chunk
+ char first_char = *this_string.c_str();
+ if (first_char != 'm' && first_char != 'l') {
+ return PacketResult::ErrorReplyInvalid;
+ }
+ // Concatenate the result so far (skipping 'm' or 'l')
+ response_string.append(this_string, 1, std::string::npos);
+ if (first_char == 'l')
+ // We're done
+ return PacketResult::Success;
+ }
+}
+
+lldb::pid_t GDBRemoteCommunicationClient::GetCurrentProcessID(bool allow_lazy) {
+ if (allow_lazy && m_curr_pid_is_valid == eLazyBoolYes)
+ return m_curr_pid;
+
+ // First try to retrieve the pid via the qProcessInfo request.
+ GetCurrentProcessInfo(allow_lazy);
+ if (m_curr_pid_is_valid == eLazyBoolYes) {
+ // We really got it.
+ return m_curr_pid;
+ } else {
+ // If we don't get a response for qProcessInfo, check if $qC gives us a
+ // result. $qC only returns a real process id on older debugserver and
+ // lldb-platform stubs. The gdb remote protocol documents $qC as returning
+ // the thread id, which newer debugserver and lldb-gdbserver stubs return
+ // correctly.
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qC", response, false) ==
+ PacketResult::Success) {
+ if (response.GetChar() == 'Q') {
+ if (response.GetChar() == 'C') {
+ m_curr_pid = response.GetHexMaxU32(false, LLDB_INVALID_PROCESS_ID);
+ if (m_curr_pid != LLDB_INVALID_PROCESS_ID) {
+ m_curr_pid_is_valid = eLazyBoolYes;
+ return m_curr_pid;
+ }
+ }
+ }
+ }
+
+ // If we don't get a response for $qC, check if $qfThreadID gives us a
+ // result.
+ if (m_curr_pid == LLDB_INVALID_PROCESS_ID) {
+ std::vector<lldb::tid_t> thread_ids;
+ bool sequence_mutex_unavailable;
+ size_t size;
+ size = GetCurrentThreadIDs(thread_ids, sequence_mutex_unavailable);
+ if (size && !sequence_mutex_unavailable) {
+ m_curr_pid = thread_ids.front();
+ m_curr_pid_is_valid = eLazyBoolYes;
+ return m_curr_pid;
+ }
+ }
+ }
+
+ return LLDB_INVALID_PROCESS_ID;
+}
+
+bool GDBRemoteCommunicationClient::GetLaunchSuccess(std::string &error_str) {
+ error_str.clear();
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qLaunchSuccess", response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ return true;
+ if (response.GetChar() == 'E') {
+ // A string the describes what failed when launching...
+ error_str = response.GetStringRef().substr(1);
+ } else {
+ error_str.assign("unknown error occurred launching process");
+ }
+ } else {
+ error_str.assign("timed out waiting for app to launch");
+ }
+ return false;
+}
+
+int GDBRemoteCommunicationClient::SendArgumentsPacket(
+ const ProcessLaunchInfo &launch_info) {
+ // Since we don't get the send argv0 separate from the executable path, we
+ // need to make sure to use the actual executable path found in the
+ // launch_info...
+ std::vector<const char *> argv;
+ FileSpec exe_file = launch_info.GetExecutableFile();
+ std::string exe_path;
+ const char *arg = nullptr;
+ const Args &launch_args = launch_info.GetArguments();
+ if (exe_file)
+ exe_path = exe_file.GetPath(false);
+ else {
+ arg = launch_args.GetArgumentAtIndex(0);
+ if (arg)
+ exe_path = arg;
+ }
+ if (!exe_path.empty()) {
+ argv.push_back(exe_path.c_str());
+ for (uint32_t i = 1; (arg = launch_args.GetArgumentAtIndex(i)) != nullptr;
+ ++i) {
+ if (arg)
+ argv.push_back(arg);
+ }
+ }
+ if (!argv.empty()) {
+ StreamString packet;
+ packet.PutChar('A');
+ for (size_t i = 0, n = argv.size(); i < n; ++i) {
+ arg = argv[i];
+ const int arg_len = strlen(arg);
+ if (i > 0)
+ packet.PutChar(',');
+ packet.Printf("%i,%i,", arg_len * 2, (int)i);
+ packet.PutBytesAsRawHex8(arg, arg_len);
+ }
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ }
+ return -1;
+}
+
+int GDBRemoteCommunicationClient::SendEnvironment(const Environment &env) {
+ for (const auto &KV : env) {
+ int r = SendEnvironmentPacket(Environment::compose(KV).c_str());
+ if (r != 0)
+ return r;
+ }
+ return 0;
+}
+
+int GDBRemoteCommunicationClient::SendEnvironmentPacket(
+ char const *name_equal_value) {
+ if (name_equal_value && name_equal_value[0]) {
+ StreamString packet;
+ bool send_hex_encoding = false;
+ for (const char *p = name_equal_value; *p != '\0' && !send_hex_encoding;
+ ++p) {
+ if (isprint(*p)) {
+ switch (*p) {
+ case '$':
+ case '#':
+ case '*':
+ case '}':
+ send_hex_encoding = true;
+ break;
+ default:
+ break;
+ }
+ } else {
+ // We have non printable characters, lets hex encode this...
+ send_hex_encoding = true;
+ }
+ }
+
+ StringExtractorGDBRemote response;
+ if (send_hex_encoding) {
+ if (m_supports_QEnvironmentHexEncoded) {
+ packet.PutCString("QEnvironmentHexEncoded:");
+ packet.PutBytesAsRawHex8(name_equal_value, strlen(name_equal_value));
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ if (response.IsUnsupportedResponse())
+ m_supports_QEnvironmentHexEncoded = false;
+ }
+ }
+
+ } else if (m_supports_QEnvironment) {
+ packet.Printf("QEnvironment:%s", name_equal_value);
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ if (response.IsUnsupportedResponse())
+ m_supports_QEnvironment = false;
+ }
+ }
+ }
+ return -1;
+}
+
+int GDBRemoteCommunicationClient::SendLaunchArchPacket(char const *arch) {
+ if (arch && arch[0]) {
+ StreamString packet;
+ packet.Printf("QLaunchArch:%s", arch);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ }
+ return -1;
+}
+
+int GDBRemoteCommunicationClient::SendLaunchEventDataPacket(
+ char const *data, bool *was_supported) {
+ if (data && *data != '\0') {
+ StreamString packet;
+ packet.Printf("QSetProcessEvent:%s", data);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse()) {
+ if (was_supported)
+ *was_supported = true;
+ return 0;
+ } else if (response.IsUnsupportedResponse()) {
+ if (was_supported)
+ *was_supported = false;
+ return -1;
+ } else {
+ uint8_t error = response.GetError();
+ if (was_supported)
+ *was_supported = true;
+ if (error)
+ return error;
+ }
+ }
+ }
+ return -1;
+}
+
+llvm::VersionTuple GDBRemoteCommunicationClient::GetOSVersion() {
+ GetHostInfo();
+ return m_os_version;
+}
+
+bool GDBRemoteCommunicationClient::GetOSBuildString(std::string &s) {
+ if (GetHostInfo()) {
+ if (!m_os_build.empty()) {
+ s = m_os_build;
+ return true;
+ }
+ }
+ s.clear();
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::GetOSKernelDescription(std::string &s) {
+ if (GetHostInfo()) {
+ if (!m_os_kernel.empty()) {
+ s = m_os_kernel;
+ return true;
+ }
+ }
+ s.clear();
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::GetHostname(std::string &s) {
+ if (GetHostInfo()) {
+ if (!m_hostname.empty()) {
+ s = m_hostname;
+ return true;
+ }
+ }
+ s.clear();
+ return false;
+}
+
+ArchSpec GDBRemoteCommunicationClient::GetSystemArchitecture() {
+ if (GetHostInfo())
+ return m_host_arch;
+ return ArchSpec();
+}
+
+const lldb_private::ArchSpec &
+GDBRemoteCommunicationClient::GetProcessArchitecture() {
+ if (m_qProcessInfo_is_valid == eLazyBoolCalculate)
+ GetCurrentProcessInfo();
+ return m_process_arch;
+}
+
+bool GDBRemoteCommunicationClient::GetGDBServerVersion() {
+ if (m_qGDBServerVersion_is_valid == eLazyBoolCalculate) {
+ m_gdb_server_name.clear();
+ m_gdb_server_version = 0;
+ m_qGDBServerVersion_is_valid = eLazyBoolNo;
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qGDBServerVersion", response, false) ==
+ PacketResult::Success) {
+ if (response.IsNormalResponse()) {
+ llvm::StringRef name, value;
+ bool success = false;
+ while (response.GetNameColonValue(name, value)) {
+ if (name.equals("name")) {
+ success = true;
+ m_gdb_server_name = value;
+ } else if (name.equals("version")) {
+ llvm::StringRef major, minor;
+ std::tie(major, minor) = value.split('.');
+ if (!major.getAsInteger(0, m_gdb_server_version))
+ success = true;
+ }
+ }
+ if (success)
+ m_qGDBServerVersion_is_valid = eLazyBoolYes;
+ }
+ }
+ }
+ return m_qGDBServerVersion_is_valid == eLazyBoolYes;
+}
+
+void GDBRemoteCommunicationClient::MaybeEnableCompression(
+ std::vector<std::string> supported_compressions) {
+ CompressionType avail_type = CompressionType::None;
+ std::string avail_name;
+
+#if defined(HAVE_LIBCOMPRESSION)
+ if (avail_type == CompressionType::None) {
+ for (auto compression : supported_compressions) {
+ if (compression == "lzfse") {
+ avail_type = CompressionType::LZFSE;
+ avail_name = compression;
+ break;
+ }
+ }
+ }
+#endif
+
+#if defined(HAVE_LIBCOMPRESSION)
+ if (avail_type == CompressionType::None) {
+ for (auto compression : supported_compressions) {
+ if (compression == "zlib-deflate") {
+ avail_type = CompressionType::ZlibDeflate;
+ avail_name = compression;
+ break;
+ }
+ }
+ }
+#endif
+
+#if defined(HAVE_LIBZ)
+ if (avail_type == CompressionType::None) {
+ for (auto compression : supported_compressions) {
+ if (compression == "zlib-deflate") {
+ avail_type = CompressionType::ZlibDeflate;
+ avail_name = compression;
+ break;
+ }
+ }
+ }
+#endif
+
+#if defined(HAVE_LIBCOMPRESSION)
+ if (avail_type == CompressionType::None) {
+ for (auto compression : supported_compressions) {
+ if (compression == "lz4") {
+ avail_type = CompressionType::LZ4;
+ avail_name = compression;
+ break;
+ }
+ }
+ }
+#endif
+
+#if defined(HAVE_LIBCOMPRESSION)
+ if (avail_type == CompressionType::None) {
+ for (auto compression : supported_compressions) {
+ if (compression == "lzma") {
+ avail_type = CompressionType::LZMA;
+ avail_name = compression;
+ break;
+ }
+ }
+ }
+#endif
+
+ if (avail_type != CompressionType::None) {
+ StringExtractorGDBRemote response;
+ std::string packet = "QEnableCompression:type:" + avail_name + ";";
+ if (SendPacketAndWaitForResponse(packet, response, false) !=
+ PacketResult::Success)
+ return;
+
+ if (response.IsOKResponse()) {
+ m_compression_type = avail_type;
+ }
+ }
+}
+
+const char *GDBRemoteCommunicationClient::GetGDBServerProgramName() {
+ if (GetGDBServerVersion()) {
+ if (!m_gdb_server_name.empty())
+ return m_gdb_server_name.c_str();
+ }
+ return nullptr;
+}
+
+uint32_t GDBRemoteCommunicationClient::GetGDBServerProgramVersion() {
+ if (GetGDBServerVersion())
+ return m_gdb_server_version;
+ return 0;
+}
+
+bool GDBRemoteCommunicationClient::GetDefaultThreadId(lldb::tid_t &tid) {
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qC", response, false) !=
+ PacketResult::Success)
+ return false;
+
+ if (!response.IsNormalResponse())
+ return false;
+
+ if (response.GetChar() == 'Q' && response.GetChar() == 'C')
+ tid = response.GetHexMaxU32(true, -1);
+
+ return true;
+}
+
+bool GDBRemoteCommunicationClient::GetHostInfo(bool force) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS));
+
+ if (force || m_qHostInfo_is_valid == eLazyBoolCalculate) {
+ // host info computation can require DNS traffic and shelling out to external processes.
+ // Increase the timeout to account for that.
+ ScopedTimeout timeout(*this, seconds(10));
+ m_qHostInfo_is_valid = eLazyBoolNo;
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qHostInfo", response, false) ==
+ PacketResult::Success) {
+ if (response.IsNormalResponse()) {
+ llvm::StringRef name;
+ llvm::StringRef value;
+ uint32_t cpu = LLDB_INVALID_CPUTYPE;
+ uint32_t sub = 0;
+ std::string arch_name;
+ std::string os_name;
+ std::string vendor_name;
+ std::string triple;
+ std::string distribution_id;
+ uint32_t pointer_byte_size = 0;
+ ByteOrder byte_order = eByteOrderInvalid;
+ uint32_t num_keys_decoded = 0;
+ while (response.GetNameColonValue(name, value)) {
+ if (name.equals("cputype")) {
+ // exception type in big endian hex
+ if (!value.getAsInteger(0, cpu))
+ ++num_keys_decoded;
+ } else if (name.equals("cpusubtype")) {
+ // exception count in big endian hex
+ if (!value.getAsInteger(0, sub))
+ ++num_keys_decoded;
+ } else if (name.equals("arch")) {
+ arch_name = value;
+ ++num_keys_decoded;
+ } else if (name.equals("triple")) {
+ StringExtractor extractor(value);
+ extractor.GetHexByteString(triple);
+ ++num_keys_decoded;
+ } else if (name.equals("distribution_id")) {
+ StringExtractor extractor(value);
+ extractor.GetHexByteString(distribution_id);
+ ++num_keys_decoded;
+ } else if (name.equals("os_build")) {
+ StringExtractor extractor(value);
+ extractor.GetHexByteString(m_os_build);
+ ++num_keys_decoded;
+ } else if (name.equals("hostname")) {
+ StringExtractor extractor(value);
+ extractor.GetHexByteString(m_hostname);
+ ++num_keys_decoded;
+ } else if (name.equals("os_kernel")) {
+ StringExtractor extractor(value);
+ extractor.GetHexByteString(m_os_kernel);
+ ++num_keys_decoded;
+ } else if (name.equals("ostype")) {
+ os_name = value;
+ ++num_keys_decoded;
+ } else if (name.equals("vendor")) {
+ vendor_name = value;
+ ++num_keys_decoded;
+ } else if (name.equals("endian")) {
+ byte_order = llvm::StringSwitch<lldb::ByteOrder>(value)
+ .Case("little", eByteOrderLittle)
+ .Case("big", eByteOrderBig)
+ .Case("pdp", eByteOrderPDP)
+ .Default(eByteOrderInvalid);
+ if (byte_order != eByteOrderInvalid)
+ ++num_keys_decoded;
+ } else if (name.equals("ptrsize")) {
+ if (!value.getAsInteger(0, pointer_byte_size))
+ ++num_keys_decoded;
+ } else if (name.equals("os_version") ||
+ name.equals(
+ "version")) // Older debugserver binaries used the
+ // "version" key instead of
+ // "os_version"...
+ {
+ if (!m_os_version.tryParse(value))
+ ++num_keys_decoded;
+ } else if (name.equals("watchpoint_exceptions_received")) {
+ m_watchpoints_trigger_after_instruction =
+ llvm::StringSwitch<LazyBool>(value)
+ .Case("before", eLazyBoolNo)
+ .Case("after", eLazyBoolYes)
+ .Default(eLazyBoolCalculate);
+ if (m_watchpoints_trigger_after_instruction != eLazyBoolCalculate)
+ ++num_keys_decoded;
+ } else if (name.equals("default_packet_timeout")) {
+ uint32_t timeout_seconds;
+ if (!value.getAsInteger(0, timeout_seconds)) {
+ m_default_packet_timeout = seconds(timeout_seconds);
+ SetPacketTimeout(m_default_packet_timeout);
+ ++num_keys_decoded;
+ }
+ }
+ }
+
+ if (num_keys_decoded > 0)
+ m_qHostInfo_is_valid = eLazyBoolYes;
+
+ if (triple.empty()) {
+ if (arch_name.empty()) {
+ if (cpu != LLDB_INVALID_CPUTYPE) {
+ m_host_arch.SetArchitecture(eArchTypeMachO, cpu, sub);
+ if (pointer_byte_size) {
+ assert(pointer_byte_size == m_host_arch.GetAddressByteSize());
+ }
+ if (byte_order != eByteOrderInvalid) {
+ assert(byte_order == m_host_arch.GetByteOrder());
+ }
+
+ if (!vendor_name.empty())
+ m_host_arch.GetTriple().setVendorName(
+ llvm::StringRef(vendor_name));
+ if (!os_name.empty())
+ m_host_arch.GetTriple().setOSName(llvm::StringRef(os_name));
+ }
+ } else {
+ std::string triple;
+ triple += arch_name;
+ if (!vendor_name.empty() || !os_name.empty()) {
+ triple += '-';
+ if (vendor_name.empty())
+ triple += "unknown";
+ else
+ triple += vendor_name;
+ triple += '-';
+ if (os_name.empty())
+ triple += "unknown";
+ else
+ triple += os_name;
+ }
+ m_host_arch.SetTriple(triple.c_str());
+
+ llvm::Triple &host_triple = m_host_arch.GetTriple();
+ if (host_triple.getVendor() == llvm::Triple::Apple &&
+ host_triple.getOS() == llvm::Triple::Darwin) {
+ switch (m_host_arch.GetMachine()) {
+ case llvm::Triple::aarch64:
+ case llvm::Triple::arm:
+ case llvm::Triple::thumb:
+ host_triple.setOS(llvm::Triple::IOS);
+ break;
+ default:
+ host_triple.setOS(llvm::Triple::MacOSX);
+ break;
+ }
+ }
+ if (pointer_byte_size) {
+ assert(pointer_byte_size == m_host_arch.GetAddressByteSize());
+ }
+ if (byte_order != eByteOrderInvalid) {
+ assert(byte_order == m_host_arch.GetByteOrder());
+ }
+ }
+ } else {
+ m_host_arch.SetTriple(triple.c_str());
+ if (pointer_byte_size) {
+ assert(pointer_byte_size == m_host_arch.GetAddressByteSize());
+ }
+ if (byte_order != eByteOrderInvalid) {
+ assert(byte_order == m_host_arch.GetByteOrder());
+ }
+
+ if (log)
+ log->Printf("GDBRemoteCommunicationClient::%s parsed host "
+ "architecture as %s, triple as %s from triple text %s",
+ __FUNCTION__, m_host_arch.GetArchitectureName()
+ ? m_host_arch.GetArchitectureName()
+ : "<null-arch-name>",
+ m_host_arch.GetTriple().getTriple().c_str(),
+ triple.c_str());
+ }
+ if (!distribution_id.empty())
+ m_host_arch.SetDistributionId(distribution_id.c_str());
+ }
+ }
+ }
+ return m_qHostInfo_is_valid == eLazyBoolYes;
+}
+
+int GDBRemoteCommunicationClient::SendAttach(
+ lldb::pid_t pid, StringExtractorGDBRemote &response) {
+ if (pid != LLDB_INVALID_PROCESS_ID) {
+ char packet[64];
+ const int packet_len =
+ ::snprintf(packet, sizeof(packet), "vAttach;%" PRIx64, pid);
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ assert(packet_len < (int)sizeof(packet));
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ if (response.IsErrorResponse())
+ return response.GetError();
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int GDBRemoteCommunicationClient::SendStdinNotification(const char *data,
+ size_t data_len) {
+ StreamString packet;
+ packet.PutCString("I");
+ packet.PutBytesAsRawHex8(data, data_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ PacketResult::Success) {
+ return 0;
+ }
+ return response.GetError();
+}
+
+const lldb_private::ArchSpec &
+GDBRemoteCommunicationClient::GetHostArchitecture() {
+ if (m_qHostInfo_is_valid == eLazyBoolCalculate)
+ GetHostInfo();
+ return m_host_arch;
+}
+
+seconds GDBRemoteCommunicationClient::GetHostDefaultPacketTimeout() {
+ if (m_qHostInfo_is_valid == eLazyBoolCalculate)
+ GetHostInfo();
+ return m_default_packet_timeout;
+}
+
+addr_t GDBRemoteCommunicationClient::AllocateMemory(size_t size,
+ uint32_t permissions) {
+ if (m_supports_alloc_dealloc_memory != eLazyBoolNo) {
+ m_supports_alloc_dealloc_memory = eLazyBoolYes;
+ char packet[64];
+ const int packet_len = ::snprintf(
+ packet, sizeof(packet), "_M%" PRIx64 ",%s%s%s", (uint64_t)size,
+ permissions & lldb::ePermissionsReadable ? "r" : "",
+ permissions & lldb::ePermissionsWritable ? "w" : "",
+ permissions & lldb::ePermissionsExecutable ? "x" : "");
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ if (response.IsUnsupportedResponse())
+ m_supports_alloc_dealloc_memory = eLazyBoolNo;
+ else if (!response.IsErrorResponse())
+ return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS);
+ } else {
+ m_supports_alloc_dealloc_memory = eLazyBoolNo;
+ }
+ }
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool GDBRemoteCommunicationClient::DeallocateMemory(addr_t addr) {
+ if (m_supports_alloc_dealloc_memory != eLazyBoolNo) {
+ m_supports_alloc_dealloc_memory = eLazyBoolYes;
+ char packet[64];
+ const int packet_len =
+ ::snprintf(packet, sizeof(packet), "_m%" PRIx64, (uint64_t)addr);
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ if (response.IsUnsupportedResponse())
+ m_supports_alloc_dealloc_memory = eLazyBoolNo;
+ else if (response.IsOKResponse())
+ return true;
+ } else {
+ m_supports_alloc_dealloc_memory = eLazyBoolNo;
+ }
+ }
+ return false;
+}
+
+Status GDBRemoteCommunicationClient::Detach(bool keep_stopped) {
+ Status error;
+
+ if (keep_stopped) {
+ if (m_supports_detach_stay_stopped == eLazyBoolCalculate) {
+ char packet[64];
+ const int packet_len =
+ ::snprintf(packet, sizeof(packet), "qSupportsDetachAndStayStopped:");
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success &&
+ response.IsOKResponse()) {
+ m_supports_detach_stay_stopped = eLazyBoolYes;
+ } else {
+ m_supports_detach_stay_stopped = eLazyBoolNo;
+ }
+ }
+
+ if (m_supports_detach_stay_stopped == eLazyBoolNo) {
+ error.SetErrorString("Stays stopped not supported by this target.");
+ return error;
+ } else {
+ StringExtractorGDBRemote response;
+ PacketResult packet_result =
+ SendPacketAndWaitForResponse("D1", response, false);
+ if (packet_result != PacketResult::Success)
+ error.SetErrorString("Sending extended disconnect packet failed.");
+ }
+ } else {
+ StringExtractorGDBRemote response;
+ PacketResult packet_result =
+ SendPacketAndWaitForResponse("D", response, false);
+ if (packet_result != PacketResult::Success)
+ error.SetErrorString("Sending disconnect packet failed.");
+ }
+ return error;
+}
+
+Status GDBRemoteCommunicationClient::GetMemoryRegionInfo(
+ lldb::addr_t addr, lldb_private::MemoryRegionInfo &region_info) {
+ Status error;
+ region_info.Clear();
+
+ if (m_supports_memory_region_info != eLazyBoolNo) {
+ m_supports_memory_region_info = eLazyBoolYes;
+ char packet[64];
+ const int packet_len = ::snprintf(
+ packet, sizeof(packet), "qMemoryRegionInfo:%" PRIx64, (uint64_t)addr);
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success &&
+ response.GetResponseType() == StringExtractorGDBRemote::eResponse) {
+ llvm::StringRef name;
+ llvm::StringRef value;
+ addr_t addr_value = LLDB_INVALID_ADDRESS;
+ bool success = true;
+ bool saw_permissions = false;
+ while (success && response.GetNameColonValue(name, value)) {
+ if (name.equals("start")) {
+ if (!value.getAsInteger(16, addr_value))
+ region_info.GetRange().SetRangeBase(addr_value);
+ } else if (name.equals("size")) {
+ if (!value.getAsInteger(16, addr_value))
+ region_info.GetRange().SetByteSize(addr_value);
+ } else if (name.equals("permissions") &&
+ region_info.GetRange().IsValid()) {
+ saw_permissions = true;
+ if (region_info.GetRange().Contains(addr)) {
+ if (value.find('r') != llvm::StringRef::npos)
+ region_info.SetReadable(MemoryRegionInfo::eYes);
+ else
+ region_info.SetReadable(MemoryRegionInfo::eNo);
+
+ if (value.find('w') != llvm::StringRef::npos)
+ region_info.SetWritable(MemoryRegionInfo::eYes);
+ else
+ region_info.SetWritable(MemoryRegionInfo::eNo);
+
+ if (value.find('x') != llvm::StringRef::npos)
+ region_info.SetExecutable(MemoryRegionInfo::eYes);
+ else
+ region_info.SetExecutable(MemoryRegionInfo::eNo);
+
+ region_info.SetMapped(MemoryRegionInfo::eYes);
+ } else {
+ // The reported region does not contain this address -- we're
+ // looking at an unmapped page
+ region_info.SetReadable(MemoryRegionInfo::eNo);
+ region_info.SetWritable(MemoryRegionInfo::eNo);
+ region_info.SetExecutable(MemoryRegionInfo::eNo);
+ region_info.SetMapped(MemoryRegionInfo::eNo);
+ }
+ } else if (name.equals("name")) {
+ StringExtractorGDBRemote name_extractor(value);
+ std::string name;
+ name_extractor.GetHexByteString(name);
+ region_info.SetName(name.c_str());
+ } else if (name.equals("error")) {
+ StringExtractorGDBRemote error_extractor(value);
+ std::string error_string;
+ // Now convert the HEX bytes into a string value
+ error_extractor.GetHexByteString(error_string);
+ error.SetErrorString(error_string.c_str());
+ }
+ }
+
+ if (region_info.GetRange().IsValid()) {
+ // We got a valid address range back but no permissions -- which means
+ // this is an unmapped page
+ if (!saw_permissions) {
+ region_info.SetReadable(MemoryRegionInfo::eNo);
+ region_info.SetWritable(MemoryRegionInfo::eNo);
+ region_info.SetExecutable(MemoryRegionInfo::eNo);
+ region_info.SetMapped(MemoryRegionInfo::eNo);
+ }
+ } else {
+ // We got an invalid address range back
+ error.SetErrorString("Server returned invalid range");
+ }
+ } else {
+ m_supports_memory_region_info = eLazyBoolNo;
+ }
+ }
+
+ if (m_supports_memory_region_info == eLazyBoolNo) {
+ error.SetErrorString("qMemoryRegionInfo is not supported");
+ }
+
+ // Try qXfer:memory-map:read to get region information not included in
+ // qMemoryRegionInfo
+ MemoryRegionInfo qXfer_region_info;
+ Status qXfer_error = GetQXferMemoryMapRegionInfo(addr, qXfer_region_info);
+
+ if (error.Fail()) {
+ // If qMemoryRegionInfo failed, but qXfer:memory-map:read succeeded, use
+ // the qXfer result as a fallback
+ if (qXfer_error.Success()) {
+ region_info = qXfer_region_info;
+ error.Clear();
+ } else {
+ region_info.Clear();
+ }
+ } else if (qXfer_error.Success()) {
+ // If both qMemoryRegionInfo and qXfer:memory-map:read succeeded, and if
+ // both regions are the same range, update the result to include the flash-
+ // memory information that is specific to the qXfer result.
+ if (region_info.GetRange() == qXfer_region_info.GetRange()) {
+ region_info.SetFlash(qXfer_region_info.GetFlash());
+ region_info.SetBlocksize(qXfer_region_info.GetBlocksize());
+ }
+ }
+ return error;
+}
+
+Status GDBRemoteCommunicationClient::GetQXferMemoryMapRegionInfo(
+ lldb::addr_t addr, MemoryRegionInfo &region) {
+ Status error = LoadQXferMemoryMap();
+ if (!error.Success())
+ return error;
+ for (const auto &map_region : m_qXfer_memory_map) {
+ if (map_region.GetRange().Contains(addr)) {
+ region = map_region;
+ return error;
+ }
+ }
+ error.SetErrorString("Region not found");
+ return error;
+}
+
+Status GDBRemoteCommunicationClient::LoadQXferMemoryMap() {
+
+ Status error;
+
+ if (m_qXfer_memory_map_loaded)
+ // Already loaded, return success
+ return error;
+
+ if (!XMLDocument::XMLEnabled()) {
+ error.SetErrorString("XML is not supported");
+ return error;
+ }
+
+ if (!GetQXferMemoryMapReadSupported()) {
+ error.SetErrorString("Memory map is not supported");
+ return error;
+ }
+
+ std::string xml;
+ lldb_private::Status lldberr;
+ if (!ReadExtFeature(ConstString("memory-map"), ConstString(""), xml,
+ lldberr)) {
+ error.SetErrorString("Failed to read memory map");
+ return error;
+ }
+
+ XMLDocument xml_document;
+
+ if (!xml_document.ParseMemory(xml.c_str(), xml.size())) {
+ error.SetErrorString("Failed to parse memory map xml");
+ return error;
+ }
+
+ XMLNode map_node = xml_document.GetRootElement("memory-map");
+ if (!map_node) {
+ error.SetErrorString("Invalid root node in memory map xml");
+ return error;
+ }
+
+ m_qXfer_memory_map.clear();
+
+ map_node.ForEachChildElement([this](const XMLNode &memory_node) -> bool {
+ if (!memory_node.IsElement())
+ return true;
+ if (memory_node.GetName() != "memory")
+ return true;
+ auto type = memory_node.GetAttributeValue("type", "");
+ uint64_t start;
+ uint64_t length;
+ if (!memory_node.GetAttributeValueAsUnsigned("start", start))
+ return true;
+ if (!memory_node.GetAttributeValueAsUnsigned("length", length))
+ return true;
+ MemoryRegionInfo region;
+ region.GetRange().SetRangeBase(start);
+ region.GetRange().SetByteSize(length);
+ if (type == "rom") {
+ region.SetReadable(MemoryRegionInfo::eYes);
+ this->m_qXfer_memory_map.push_back(region);
+ } else if (type == "ram") {
+ region.SetReadable(MemoryRegionInfo::eYes);
+ region.SetWritable(MemoryRegionInfo::eYes);
+ this->m_qXfer_memory_map.push_back(region);
+ } else if (type == "flash") {
+ region.SetFlash(MemoryRegionInfo::eYes);
+ memory_node.ForEachChildElement(
+ [&region](const XMLNode &prop_node) -> bool {
+ if (!prop_node.IsElement())
+ return true;
+ if (prop_node.GetName() != "property")
+ return true;
+ auto propname = prop_node.GetAttributeValue("name", "");
+ if (propname == "blocksize") {
+ uint64_t blocksize;
+ if (prop_node.GetElementTextAsUnsigned(blocksize))
+ region.SetBlocksize(blocksize);
+ }
+ return true;
+ });
+ this->m_qXfer_memory_map.push_back(region);
+ }
+ return true;
+ });
+
+ m_qXfer_memory_map_loaded = true;
+
+ return error;
+}
+
+Status GDBRemoteCommunicationClient::GetWatchpointSupportInfo(uint32_t &num) {
+ Status error;
+
+ if (m_supports_watchpoint_support_info == eLazyBoolYes) {
+ num = m_num_supported_hardware_watchpoints;
+ return error;
+ }
+
+ // Set num to 0 first.
+ num = 0;
+ if (m_supports_watchpoint_support_info != eLazyBoolNo) {
+ char packet[64];
+ const int packet_len =
+ ::snprintf(packet, sizeof(packet), "qWatchpointSupportInfo:");
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ m_supports_watchpoint_support_info = eLazyBoolYes;
+ llvm::StringRef name;
+ llvm::StringRef value;
+ bool found_num_field = false;
+ while (response.GetNameColonValue(name, value)) {
+ if (name.equals("num")) {
+ value.getAsInteger(0, m_num_supported_hardware_watchpoints);
+ num = m_num_supported_hardware_watchpoints;
+ found_num_field = true;
+ }
+ }
+ if (!found_num_field) {
+ m_supports_watchpoint_support_info = eLazyBoolNo;
+ }
+ } else {
+ m_supports_watchpoint_support_info = eLazyBoolNo;
+ }
+ }
+
+ if (m_supports_watchpoint_support_info == eLazyBoolNo) {
+ error.SetErrorString("qWatchpointSupportInfo is not supported");
+ }
+ return error;
+}
+
+lldb_private::Status GDBRemoteCommunicationClient::GetWatchpointSupportInfo(
+ uint32_t &num, bool &after, const ArchSpec &arch) {
+ Status error(GetWatchpointSupportInfo(num));
+ if (error.Success())
+ error = GetWatchpointsTriggerAfterInstruction(after, arch);
+ return error;
+}
+
+lldb_private::Status
+GDBRemoteCommunicationClient::GetWatchpointsTriggerAfterInstruction(
+ bool &after, const ArchSpec &arch) {
+ Status error;
+ llvm::Triple triple = arch.GetTriple();
+
+ // we assume watchpoints will happen after running the relevant opcode and we
+ // only want to override this behavior if we have explicitly received a
+ // qHostInfo telling us otherwise
+ if (m_qHostInfo_is_valid != eLazyBoolYes) {
+ // On targets like MIPS and ppc64, watchpoint exceptions are always
+ // generated before the instruction is executed. The connected target may
+ // not support qHostInfo or qWatchpointSupportInfo packets.
+ after = !(triple.isMIPS() || triple.isPPC64());
+ } else {
+ // For MIPS and ppc64, set m_watchpoints_trigger_after_instruction to
+ // eLazyBoolNo if it is not calculated before.
+ if (m_watchpoints_trigger_after_instruction == eLazyBoolCalculate &&
+ (triple.isMIPS() || triple.isPPC64()))
+ m_watchpoints_trigger_after_instruction = eLazyBoolNo;
+
+ after = (m_watchpoints_trigger_after_instruction != eLazyBoolNo);
+ }
+ return error;
+}
+
+int GDBRemoteCommunicationClient::SetSTDIN(const FileSpec &file_spec) {
+ if (file_spec) {
+ std::string path{file_spec.GetPath(false)};
+ StreamString packet;
+ packet.PutCString("QSetSTDIN:");
+ packet.PutStringAsRawHex8(path);
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ }
+ return -1;
+}
+
+int GDBRemoteCommunicationClient::SetSTDOUT(const FileSpec &file_spec) {
+ if (file_spec) {
+ std::string path{file_spec.GetPath(false)};
+ StreamString packet;
+ packet.PutCString("QSetSTDOUT:");
+ packet.PutStringAsRawHex8(path);
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ }
+ return -1;
+}
+
+int GDBRemoteCommunicationClient::SetSTDERR(const FileSpec &file_spec) {
+ if (file_spec) {
+ std::string path{file_spec.GetPath(false)};
+ StreamString packet;
+ packet.PutCString("QSetSTDERR:");
+ packet.PutStringAsRawHex8(path);
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ }
+ return -1;
+}
+
+bool GDBRemoteCommunicationClient::GetWorkingDir(FileSpec &working_dir) {
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qGetWorkingDir", response, false) ==
+ PacketResult::Success) {
+ if (response.IsUnsupportedResponse())
+ return false;
+ if (response.IsErrorResponse())
+ return false;
+ std::string cwd;
+ response.GetHexByteString(cwd);
+ working_dir.SetFile(cwd, GetHostArchitecture().GetTriple());
+ return !cwd.empty();
+ }
+ return false;
+}
+
+int GDBRemoteCommunicationClient::SetWorkingDir(const FileSpec &working_dir) {
+ if (working_dir) {
+ std::string path{working_dir.GetPath(false)};
+ StreamString packet;
+ packet.PutCString("QSetWorkingDir:");
+ packet.PutStringAsRawHex8(path);
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ }
+ return -1;
+}
+
+int GDBRemoteCommunicationClient::SetDisableASLR(bool enable) {
+ char packet[32];
+ const int packet_len =
+ ::snprintf(packet, sizeof(packet), "QSetDisableASLR:%i", enable ? 1 : 0);
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ return -1;
+}
+
+int GDBRemoteCommunicationClient::SetDetachOnError(bool enable) {
+ char packet[32];
+ const int packet_len = ::snprintf(packet, sizeof(packet),
+ "QSetDetachOnError:%i", enable ? 1 : 0);
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ return -1;
+}
+
+bool GDBRemoteCommunicationClient::DecodeProcessInfoResponse(
+ StringExtractorGDBRemote &response, ProcessInstanceInfo &process_info) {
+ if (response.IsNormalResponse()) {
+ llvm::StringRef name;
+ llvm::StringRef value;
+ StringExtractor extractor;
+
+ uint32_t cpu = LLDB_INVALID_CPUTYPE;
+ uint32_t sub = 0;
+ std::string vendor;
+ std::string os_type;
+
+ while (response.GetNameColonValue(name, value)) {
+ if (name.equals("pid")) {
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+ value.getAsInteger(0, pid);
+ process_info.SetProcessID(pid);
+ } else if (name.equals("ppid")) {
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+ value.getAsInteger(0, pid);
+ process_info.SetParentProcessID(pid);
+ } else if (name.equals("uid")) {
+ uint32_t uid = UINT32_MAX;
+ value.getAsInteger(0, uid);
+ process_info.SetUserID(uid);
+ } else if (name.equals("euid")) {
+ uint32_t uid = UINT32_MAX;
+ value.getAsInteger(0, uid);
+ process_info.SetEffectiveGroupID(uid);
+ } else if (name.equals("gid")) {
+ uint32_t gid = UINT32_MAX;
+ value.getAsInteger(0, gid);
+ process_info.SetGroupID(gid);
+ } else if (name.equals("egid")) {
+ uint32_t gid = UINT32_MAX;
+ value.getAsInteger(0, gid);
+ process_info.SetEffectiveGroupID(gid);
+ } else if (name.equals("triple")) {
+ StringExtractor extractor(value);
+ std::string triple;
+ extractor.GetHexByteString(triple);
+ process_info.GetArchitecture().SetTriple(triple.c_str());
+ } else if (name.equals("name")) {
+ StringExtractor extractor(value);
+ // The process name from ASCII hex bytes since we can't control the
+ // characters in a process name
+ std::string name;
+ extractor.GetHexByteString(name);
+ process_info.GetExecutableFile().SetFile(name, FileSpec::Style::native);
+ } else if (name.equals("cputype")) {
+ value.getAsInteger(0, cpu);
+ } else if (name.equals("cpusubtype")) {
+ value.getAsInteger(0, sub);
+ } else if (name.equals("vendor")) {
+ vendor = value;
+ } else if (name.equals("ostype")) {
+ os_type = value;
+ }
+ }
+
+ if (cpu != LLDB_INVALID_CPUTYPE && !vendor.empty() && !os_type.empty()) {
+ if (vendor == "apple") {
+ process_info.GetArchitecture().SetArchitecture(eArchTypeMachO, cpu,
+ sub);
+ process_info.GetArchitecture().GetTriple().setVendorName(
+ llvm::StringRef(vendor));
+ process_info.GetArchitecture().GetTriple().setOSName(
+ llvm::StringRef(os_type));
+ }
+ }
+
+ if (process_info.GetProcessID() != LLDB_INVALID_PROCESS_ID)
+ return true;
+ }
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::GetProcessInfo(
+ lldb::pid_t pid, ProcessInstanceInfo &process_info) {
+ process_info.Clear();
+
+ if (m_supports_qProcessInfoPID) {
+ char packet[32];
+ const int packet_len =
+ ::snprintf(packet, sizeof(packet), "qProcessInfoPID:%" PRIu64, pid);
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ return DecodeProcessInfoResponse(response, process_info);
+ } else {
+ m_supports_qProcessInfoPID = false;
+ return false;
+ }
+ }
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::GetCurrentProcessInfo(bool allow_lazy) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS |
+ GDBR_LOG_PACKETS));
+
+ if (allow_lazy) {
+ if (m_qProcessInfo_is_valid == eLazyBoolYes)
+ return true;
+ if (m_qProcessInfo_is_valid == eLazyBoolNo)
+ return false;
+ }
+
+ GetHostInfo();
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qProcessInfo", response, false) ==
+ PacketResult::Success) {
+ if (response.IsNormalResponse()) {
+ llvm::StringRef name;
+ llvm::StringRef value;
+ uint32_t cpu = LLDB_INVALID_CPUTYPE;
+ uint32_t sub = 0;
+ std::string arch_name;
+ std::string os_name;
+ std::string vendor_name;
+ std::string triple;
+ std::string elf_abi;
+ uint32_t pointer_byte_size = 0;
+ StringExtractor extractor;
+ ByteOrder byte_order = eByteOrderInvalid;
+ uint32_t num_keys_decoded = 0;
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+ while (response.GetNameColonValue(name, value)) {
+ if (name.equals("cputype")) {
+ if (!value.getAsInteger(16, cpu))
+ ++num_keys_decoded;
+ } else if (name.equals("cpusubtype")) {
+ if (!value.getAsInteger(16, sub))
+ ++num_keys_decoded;
+ } else if (name.equals("triple")) {
+ StringExtractor extractor(value);
+ extractor.GetHexByteString(triple);
+ ++num_keys_decoded;
+ } else if (name.equals("ostype")) {
+ os_name = value;
+ ++num_keys_decoded;
+ } else if (name.equals("vendor")) {
+ vendor_name = value;
+ ++num_keys_decoded;
+ } else if (name.equals("endian")) {
+ byte_order = llvm::StringSwitch<lldb::ByteOrder>(value)
+ .Case("little", eByteOrderLittle)
+ .Case("big", eByteOrderBig)
+ .Case("pdp", eByteOrderPDP)
+ .Default(eByteOrderInvalid);
+ if (byte_order != eByteOrderInvalid)
+ ++num_keys_decoded;
+ } else if (name.equals("ptrsize")) {
+ if (!value.getAsInteger(16, pointer_byte_size))
+ ++num_keys_decoded;
+ } else if (name.equals("pid")) {
+ if (!value.getAsInteger(16, pid))
+ ++num_keys_decoded;
+ } else if (name.equals("elf_abi")) {
+ elf_abi = value;
+ ++num_keys_decoded;
+ }
+ }
+ if (num_keys_decoded > 0)
+ m_qProcessInfo_is_valid = eLazyBoolYes;
+ if (pid != LLDB_INVALID_PROCESS_ID) {
+ m_curr_pid_is_valid = eLazyBoolYes;
+ m_curr_pid = pid;
+ }
+
+ // Set the ArchSpec from the triple if we have it.
+ if (!triple.empty()) {
+ m_process_arch.SetTriple(triple.c_str());
+ m_process_arch.SetFlags(elf_abi);
+ if (pointer_byte_size) {
+ assert(pointer_byte_size == m_process_arch.GetAddressByteSize());
+ }
+ } else if (cpu != LLDB_INVALID_CPUTYPE && !os_name.empty() &&
+ !vendor_name.empty()) {
+ llvm::Triple triple(llvm::Twine("-") + vendor_name + "-" + os_name);
+
+ assert(triple.getObjectFormat() != llvm::Triple::UnknownObjectFormat);
+ assert(triple.getObjectFormat() != llvm::Triple::Wasm);
+ assert(triple.getObjectFormat() != llvm::Triple::XCOFF);
+ switch (triple.getObjectFormat()) {
+ case llvm::Triple::MachO:
+ m_process_arch.SetArchitecture(eArchTypeMachO, cpu, sub);
+ break;
+ case llvm::Triple::ELF:
+ m_process_arch.SetArchitecture(eArchTypeELF, cpu, sub);
+ break;
+ case llvm::Triple::COFF:
+ m_process_arch.SetArchitecture(eArchTypeCOFF, cpu, sub);
+ break;
+ case llvm::Triple::Wasm:
+ case llvm::Triple::XCOFF:
+ if (log)
+ log->Printf("error: not supported target architecture");
+ return false;
+ case llvm::Triple::UnknownObjectFormat:
+ if (log)
+ log->Printf("error: failed to determine target architecture");
+ return false;
+ }
+
+ if (pointer_byte_size) {
+ assert(pointer_byte_size == m_process_arch.GetAddressByteSize());
+ }
+ if (byte_order != eByteOrderInvalid) {
+ assert(byte_order == m_process_arch.GetByteOrder());
+ }
+ m_process_arch.GetTriple().setVendorName(llvm::StringRef(vendor_name));
+ m_process_arch.GetTriple().setOSName(llvm::StringRef(os_name));
+ m_host_arch.GetTriple().setVendorName(llvm::StringRef(vendor_name));
+ m_host_arch.GetTriple().setOSName(llvm::StringRef(os_name));
+ }
+ return true;
+ }
+ } else {
+ m_qProcessInfo_is_valid = eLazyBoolNo;
+ }
+
+ return false;
+}
+
+uint32_t GDBRemoteCommunicationClient::FindProcesses(
+ const ProcessInstanceInfoMatch &match_info,
+ ProcessInstanceInfoList &process_infos) {
+ process_infos.Clear();
+
+ if (m_supports_qfProcessInfo) {
+ StreamString packet;
+ packet.PutCString("qfProcessInfo");
+ if (!match_info.MatchAllProcesses()) {
+ packet.PutChar(':');
+ const char *name = match_info.GetProcessInfo().GetName();
+ bool has_name_match = false;
+ if (name && name[0]) {
+ has_name_match = true;
+ NameMatch name_match_type = match_info.GetNameMatchType();
+ switch (name_match_type) {
+ case NameMatch::Ignore:
+ has_name_match = false;
+ break;
+
+ case NameMatch::Equals:
+ packet.PutCString("name_match:equals;");
+ break;
+
+ case NameMatch::Contains:
+ packet.PutCString("name_match:contains;");
+ break;
+
+ case NameMatch::StartsWith:
+ packet.PutCString("name_match:starts_with;");
+ break;
+
+ case NameMatch::EndsWith:
+ packet.PutCString("name_match:ends_with;");
+ break;
+
+ case NameMatch::RegularExpression:
+ packet.PutCString("name_match:regex;");
+ break;
+ }
+ if (has_name_match) {
+ packet.PutCString("name:");
+ packet.PutBytesAsRawHex8(name, ::strlen(name));
+ packet.PutChar(';');
+ }
+ }
+
+ if (match_info.GetProcessInfo().ProcessIDIsValid())
+ packet.Printf("pid:%" PRIu64 ";",
+ match_info.GetProcessInfo().GetProcessID());
+ if (match_info.GetProcessInfo().ParentProcessIDIsValid())
+ packet.Printf("parent_pid:%" PRIu64 ";",
+ match_info.GetProcessInfo().GetParentProcessID());
+ if (match_info.GetProcessInfo().UserIDIsValid())
+ packet.Printf("uid:%u;", match_info.GetProcessInfo().GetUserID());
+ if (match_info.GetProcessInfo().GroupIDIsValid())
+ packet.Printf("gid:%u;", match_info.GetProcessInfo().GetGroupID());
+ if (match_info.GetProcessInfo().EffectiveUserIDIsValid())
+ packet.Printf("euid:%u;",
+ match_info.GetProcessInfo().GetEffectiveUserID());
+ if (match_info.GetProcessInfo().EffectiveGroupIDIsValid())
+ packet.Printf("egid:%u;",
+ match_info.GetProcessInfo().GetEffectiveGroupID());
+ if (match_info.GetProcessInfo().EffectiveGroupIDIsValid())
+ packet.Printf("all_users:%u;", match_info.GetMatchAllUsers() ? 1 : 0);
+ if (match_info.GetProcessInfo().GetArchitecture().IsValid()) {
+ const ArchSpec &match_arch =
+ match_info.GetProcessInfo().GetArchitecture();
+ const llvm::Triple &triple = match_arch.GetTriple();
+ packet.PutCString("triple:");
+ packet.PutCString(triple.getTriple());
+ packet.PutChar(';');
+ }
+ }
+ StringExtractorGDBRemote response;
+ // Increase timeout as the first qfProcessInfo packet takes a long time on
+ // Android. The value of 1min was arrived at empirically.
+ ScopedTimeout timeout(*this, minutes(1));
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ PacketResult::Success) {
+ do {
+ ProcessInstanceInfo process_info;
+ if (!DecodeProcessInfoResponse(response, process_info))
+ break;
+ process_infos.Append(process_info);
+ response.GetStringRef().clear();
+ response.SetFilePos(0);
+ } while (SendPacketAndWaitForResponse("qsProcessInfo", response, false) ==
+ PacketResult::Success);
+ } else {
+ m_supports_qfProcessInfo = false;
+ return 0;
+ }
+ }
+ return process_infos.GetSize();
+}
+
+bool GDBRemoteCommunicationClient::GetUserName(uint32_t uid,
+ std::string &name) {
+ if (m_supports_qUserName) {
+ char packet[32];
+ const int packet_len =
+ ::snprintf(packet, sizeof(packet), "qUserName:%i", uid);
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ if (response.IsNormalResponse()) {
+ // Make sure we parsed the right number of characters. The response is
+ // the hex encoded user name and should make up the entire packet. If
+ // there are any non-hex ASCII bytes, the length won't match below..
+ if (response.GetHexByteString(name) * 2 ==
+ response.GetStringRef().size())
+ return true;
+ }
+ } else {
+ m_supports_qUserName = false;
+ return false;
+ }
+ }
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::GetGroupName(uint32_t gid,
+ std::string &name) {
+ if (m_supports_qGroupName) {
+ char packet[32];
+ const int packet_len =
+ ::snprintf(packet, sizeof(packet), "qGroupName:%i", gid);
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ if (response.IsNormalResponse()) {
+ // Make sure we parsed the right number of characters. The response is
+ // the hex encoded group name and should make up the entire packet. If
+ // there are any non-hex ASCII bytes, the length won't match below..
+ if (response.GetHexByteString(name) * 2 ==
+ response.GetStringRef().size())
+ return true;
+ }
+ } else {
+ m_supports_qGroupName = false;
+ return false;
+ }
+ }
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::SetNonStopMode(const bool enable) {
+ // Form non-stop packet request
+ char packet[32];
+ const int packet_len =
+ ::snprintf(packet, sizeof(packet), "QNonStop:%1d", (int)enable);
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+
+ StringExtractorGDBRemote response;
+ // Send to target
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success)
+ if (response.IsOKResponse())
+ return true;
+
+ // Failed or not supported
+ return false;
+}
+
+static void MakeSpeedTestPacket(StreamString &packet, uint32_t send_size,
+ uint32_t recv_size) {
+ packet.Clear();
+ packet.Printf("qSpeedTest:response_size:%i;data:", recv_size);
+ uint32_t bytes_left = send_size;
+ while (bytes_left > 0) {
+ if (bytes_left >= 26) {
+ packet.PutCString("abcdefghijklmnopqrstuvwxyz");
+ bytes_left -= 26;
+ } else {
+ packet.Printf("%*.*s;", bytes_left, bytes_left,
+ "abcdefghijklmnopqrstuvwxyz");
+ bytes_left = 0;
+ }
+ }
+}
+
+duration<float>
+calculate_standard_deviation(const std::vector<duration<float>> &v) {
+ using Dur = duration<float>;
+ Dur sum = std::accumulate(std::begin(v), std::end(v), Dur());
+ Dur mean = sum / v.size();
+ float accum = 0;
+ for (auto d : v) {
+ float delta = (d - mean).count();
+ accum += delta * delta;
+ };
+
+ return Dur(sqrtf(accum / (v.size() - 1)));
+}
+
+void GDBRemoteCommunicationClient::TestPacketSpeed(const uint32_t num_packets,
+ uint32_t max_send,
+ uint32_t max_recv,
+ uint64_t recv_amount,
+ bool json, Stream &strm) {
+ uint32_t i;
+ if (SendSpeedTestPacket(0, 0)) {
+ StreamString packet;
+ if (json)
+ strm.Printf("{ \"packet_speeds\" : {\n \"num_packets\" : %u,\n "
+ "\"results\" : [",
+ num_packets);
+ else
+ strm.Printf("Testing sending %u packets of various sizes:\n",
+ num_packets);
+ strm.Flush();
+
+ uint32_t result_idx = 0;
+ uint32_t send_size;
+ std::vector<duration<float>> packet_times;
+
+ for (send_size = 0; send_size <= max_send;
+ send_size ? send_size *= 2 : send_size = 4) {
+ for (uint32_t recv_size = 0; recv_size <= max_recv;
+ recv_size ? recv_size *= 2 : recv_size = 4) {
+ MakeSpeedTestPacket(packet, send_size, recv_size);
+
+ packet_times.clear();
+ // Test how long it takes to send 'num_packets' packets
+ const auto start_time = steady_clock::now();
+ for (i = 0; i < num_packets; ++i) {
+ const auto packet_start_time = steady_clock::now();
+ StringExtractorGDBRemote response;
+ SendPacketAndWaitForResponse(packet.GetString(), response, false);
+ const auto packet_end_time = steady_clock::now();
+ packet_times.push_back(packet_end_time - packet_start_time);
+ }
+ const auto end_time = steady_clock::now();
+ const auto total_time = end_time - start_time;
+
+ float packets_per_second =
+ ((float)num_packets) / duration<float>(total_time).count();
+ auto average_per_packet = total_time / num_packets;
+ const duration<float> standard_deviation =
+ calculate_standard_deviation(packet_times);
+ if (json) {
+ strm.Format("{0}\n {{\"send_size\" : {1,6}, \"recv_size\" : "
+ "{2,6}, \"total_time_nsec\" : {3,12:ns-}, "
+ "\"standard_deviation_nsec\" : {4,9:ns-f0}}",
+ result_idx > 0 ? "," : "", send_size, recv_size,
+ total_time, standard_deviation);
+ ++result_idx;
+ } else {
+ strm.Format("qSpeedTest(send={0,7}, recv={1,7}) in {2:s+f9} for "
+ "{3,9:f2} packets/s ({4,10:ms+f6} per packet) with "
+ "standard deviation of {5,10:ms+f6}\n",
+ send_size, recv_size, duration<float>(total_time),
+ packets_per_second, duration<float>(average_per_packet),
+ standard_deviation);
+ }
+ strm.Flush();
+ }
+ }
+
+ const float k_recv_amount_mb = (float)recv_amount / (1024.0f * 1024.0f);
+ if (json)
+ strm.Printf("\n ]\n },\n \"download_speed\" : {\n \"byte_size\" "
+ ": %" PRIu64 ",\n \"results\" : [",
+ recv_amount);
+ else
+ strm.Printf("Testing receiving %2.1fMB of data using varying receive "
+ "packet sizes:\n",
+ k_recv_amount_mb);
+ strm.Flush();
+ send_size = 0;
+ result_idx = 0;
+ for (uint32_t recv_size = 32; recv_size <= max_recv; recv_size *= 2) {
+ MakeSpeedTestPacket(packet, send_size, recv_size);
+
+ // If we have a receive size, test how long it takes to receive 4MB of
+ // data
+ if (recv_size > 0) {
+ const auto start_time = steady_clock::now();
+ uint32_t bytes_read = 0;
+ uint32_t packet_count = 0;
+ while (bytes_read < recv_amount) {
+ StringExtractorGDBRemote response;
+ SendPacketAndWaitForResponse(packet.GetString(), response, false);
+ bytes_read += recv_size;
+ ++packet_count;
+ }
+ const auto end_time = steady_clock::now();
+ const auto total_time = end_time - start_time;
+ float mb_second = ((float)recv_amount) /
+ duration<float>(total_time).count() /
+ (1024.0 * 1024.0);
+ float packets_per_second =
+ ((float)packet_count) / duration<float>(total_time).count();
+ const auto average_per_packet = total_time / packet_count;
+
+ if (json) {
+ strm.Format("{0}\n {{\"send_size\" : {1,6}, \"recv_size\" : "
+ "{2,6}, \"total_time_nsec\" : {3,12:ns-}}",
+ result_idx > 0 ? "," : "", send_size, recv_size,
+ total_time);
+ ++result_idx;
+ } else {
+ strm.Format("qSpeedTest(send={0,7}, recv={1,7}) {2,6} packets needed "
+ "to receive {3:f1}MB in {4:s+f9} for {5} MB/sec for "
+ "{6,9:f2} packets/sec ({7,10:ms+f6} per packet)\n",
+ send_size, recv_size, packet_count, k_recv_amount_mb,
+ duration<float>(total_time), mb_second,
+ packets_per_second, duration<float>(average_per_packet));
+ }
+ strm.Flush();
+ }
+ }
+ if (json)
+ strm.Printf("\n ]\n }\n}\n");
+ else
+ strm.EOL();
+ }
+}
+
+bool GDBRemoteCommunicationClient::SendSpeedTestPacket(uint32_t send_size,
+ uint32_t recv_size) {
+ StreamString packet;
+ packet.Printf("qSpeedTest:response_size:%i;data:", recv_size);
+ uint32_t bytes_left = send_size;
+ while (bytes_left > 0) {
+ if (bytes_left >= 26) {
+ packet.PutCString("abcdefghijklmnopqrstuvwxyz");
+ bytes_left -= 26;
+ } else {
+ packet.Printf("%*.*s;", bytes_left, bytes_left,
+ "abcdefghijklmnopqrstuvwxyz");
+ bytes_left = 0;
+ }
+ }
+
+ StringExtractorGDBRemote response;
+ return SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ PacketResult::Success;
+}
+
+bool GDBRemoteCommunicationClient::LaunchGDBServer(
+ const char *remote_accept_hostname, lldb::pid_t &pid, uint16_t &port,
+ std::string &socket_name) {
+ pid = LLDB_INVALID_PROCESS_ID;
+ port = 0;
+ socket_name.clear();
+
+ StringExtractorGDBRemote response;
+ StreamString stream;
+ stream.PutCString("qLaunchGDBServer;");
+ std::string hostname;
+ if (remote_accept_hostname && remote_accept_hostname[0])
+ hostname = remote_accept_hostname;
+ else {
+ if (HostInfo::GetHostname(hostname)) {
+ // Make the GDB server we launch only accept connections from this host
+ stream.Printf("host:%s;", hostname.c_str());
+ } else {
+ // Make the GDB server we launch accept connections from any host since
+ // we can't figure out the hostname
+ stream.Printf("host:*;");
+ }
+ }
+ // give the process a few seconds to startup
+ ScopedTimeout timeout(*this, seconds(10));
+
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ llvm::StringRef name;
+ llvm::StringRef value;
+ while (response.GetNameColonValue(name, value)) {
+ if (name.equals("port"))
+ value.getAsInteger(0, port);
+ else if (name.equals("pid"))
+ value.getAsInteger(0, pid);
+ else if (name.compare("socket_name") == 0) {
+ StringExtractor extractor(value);
+ extractor.GetHexByteString(socket_name);
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+size_t GDBRemoteCommunicationClient::QueryGDBServer(
+ std::vector<std::pair<uint16_t, std::string>> &connection_urls) {
+ connection_urls.clear();
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qQueryGDBServer", response, false) !=
+ PacketResult::Success)
+ return 0;
+
+ StructuredData::ObjectSP data =
+ StructuredData::ParseJSON(response.GetStringRef());
+ if (!data)
+ return 0;
+
+ StructuredData::Array *array = data->GetAsArray();
+ if (!array)
+ return 0;
+
+ for (size_t i = 0, count = array->GetSize(); i < count; ++i) {
+ StructuredData::Dictionary *element = nullptr;
+ if (!array->GetItemAtIndexAsDictionary(i, element))
+ continue;
+
+ uint16_t port = 0;
+ if (StructuredData::ObjectSP port_osp =
+ element->GetValueForKey(llvm::StringRef("port")))
+ port = port_osp->GetIntegerValue(0);
+
+ std::string socket_name;
+ if (StructuredData::ObjectSP socket_name_osp =
+ element->GetValueForKey(llvm::StringRef("socket_name")))
+ socket_name = socket_name_osp->GetStringValue();
+
+ if (port != 0 || !socket_name.empty())
+ connection_urls.emplace_back(port, socket_name);
+ }
+ return connection_urls.size();
+}
+
+bool GDBRemoteCommunicationClient::KillSpawnedProcess(lldb::pid_t pid) {
+ StreamString stream;
+ stream.Printf("qKillSpawnedProcess:%" PRId64, pid);
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse())
+ return true;
+ }
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::SetCurrentThread(uint64_t tid) {
+ if (m_curr_tid == tid)
+ return true;
+
+ char packet[32];
+ int packet_len;
+ if (tid == UINT64_MAX)
+ packet_len = ::snprintf(packet, sizeof(packet), "Hg-1");
+ else
+ packet_len = ::snprintf(packet, sizeof(packet), "Hg%" PRIx64, tid);
+ assert(packet_len + 1 < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse()) {
+ m_curr_tid = tid;
+ return true;
+ }
+
+ /*
+ * Connected bare-iron target (like YAMON gdb-stub) may not have support for
+ * Hg packet.
+ * The reply from '?' packet could be as simple as 'S05'. There is no packet
+ * which can
+ * give us pid and/or tid. Assume pid=tid=1 in such cases.
+ */
+ if (response.IsUnsupportedResponse() && IsConnected()) {
+ m_curr_tid = 1;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::SetCurrentThreadForRun(uint64_t tid) {
+ if (m_curr_tid_run == tid)
+ return true;
+
+ char packet[32];
+ int packet_len;
+ if (tid == UINT64_MAX)
+ packet_len = ::snprintf(packet, sizeof(packet), "Hc-1");
+ else
+ packet_len = ::snprintf(packet, sizeof(packet), "Hc%" PRIx64, tid);
+
+ assert(packet_len + 1 < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse()) {
+ m_curr_tid_run = tid;
+ return true;
+ }
+
+ /*
+ * Connected bare-iron target (like YAMON gdb-stub) may not have support for
+ * Hc packet.
+ * The reply from '?' packet could be as simple as 'S05'. There is no packet
+ * which can
+ * give us pid and/or tid. Assume pid=tid=1 in such cases.
+ */
+ if (response.IsUnsupportedResponse() && IsConnected()) {
+ m_curr_tid_run = 1;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::GetStopReply(
+ StringExtractorGDBRemote &response) {
+ if (SendPacketAndWaitForResponse("?", response, false) ==
+ PacketResult::Success)
+ return response.IsNormalResponse();
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::GetThreadStopInfo(
+ lldb::tid_t tid, StringExtractorGDBRemote &response) {
+ if (m_supports_qThreadStopInfo) {
+ char packet[256];
+ int packet_len =
+ ::snprintf(packet, sizeof(packet), "qThreadStopInfo%" PRIx64, tid);
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ if (SendPacketAndWaitForResponse(packet, response, false) ==
+ PacketResult::Success) {
+ if (response.IsUnsupportedResponse())
+ m_supports_qThreadStopInfo = false;
+ else if (response.IsNormalResponse())
+ return true;
+ else
+ return false;
+ } else {
+ m_supports_qThreadStopInfo = false;
+ }
+ }
+ return false;
+}
+
+uint8_t GDBRemoteCommunicationClient::SendGDBStoppointTypePacket(
+ GDBStoppointType type, bool insert, addr_t addr, uint32_t length) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf("GDBRemoteCommunicationClient::%s() %s at addr = 0x%" PRIx64,
+ __FUNCTION__, insert ? "add" : "remove", addr);
+
+ // Check if the stub is known not to support this breakpoint type
+ if (!SupportsGDBStoppointPacket(type))
+ return UINT8_MAX;
+ // Construct the breakpoint packet
+ char packet[64];
+ const int packet_len =
+ ::snprintf(packet, sizeof(packet), "%c%i,%" PRIx64 ",%x",
+ insert ? 'Z' : 'z', type, addr, length);
+ // Check we haven't overwritten the end of the packet buffer
+ assert(packet_len + 1 < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ // Make sure the response is either "OK", "EXX" where XX are two hex digits,
+ // or "" (unsupported)
+ response.SetResponseValidatorToOKErrorNotSupported();
+ // Try to send the breakpoint packet, and check that it was correctly sent
+ if (SendPacketAndWaitForResponse(packet, response, true) ==
+ PacketResult::Success) {
+ // Receive and OK packet when the breakpoint successfully placed
+ if (response.IsOKResponse())
+ return 0;
+
+ // Status while setting breakpoint, send back specific error
+ if (response.IsErrorResponse())
+ return response.GetError();
+
+ // Empty packet informs us that breakpoint is not supported
+ if (response.IsUnsupportedResponse()) {
+ // Disable this breakpoint type since it is unsupported
+ switch (type) {
+ case eBreakpointSoftware:
+ m_supports_z0 = false;
+ break;
+ case eBreakpointHardware:
+ m_supports_z1 = false;
+ break;
+ case eWatchpointWrite:
+ m_supports_z2 = false;
+ break;
+ case eWatchpointRead:
+ m_supports_z3 = false;
+ break;
+ case eWatchpointReadWrite:
+ m_supports_z4 = false;
+ break;
+ case eStoppointInvalid:
+ return UINT8_MAX;
+ }
+ }
+ }
+ // Signal generic failure
+ return UINT8_MAX;
+}
+
+size_t GDBRemoteCommunicationClient::GetCurrentThreadIDs(
+ std::vector<lldb::tid_t> &thread_ids, bool &sequence_mutex_unavailable) {
+ thread_ids.clear();
+
+ Lock lock(*this, false);
+ if (lock) {
+ sequence_mutex_unavailable = false;
+ StringExtractorGDBRemote response;
+
+ PacketResult packet_result;
+ for (packet_result =
+ SendPacketAndWaitForResponseNoLock("qfThreadInfo", response);
+ packet_result == PacketResult::Success && response.IsNormalResponse();
+ packet_result =
+ SendPacketAndWaitForResponseNoLock("qsThreadInfo", response)) {
+ char ch = response.GetChar();
+ if (ch == 'l')
+ break;
+ if (ch == 'm') {
+ do {
+ tid_t tid = response.GetHexMaxU64(false, LLDB_INVALID_THREAD_ID);
+
+ if (tid != LLDB_INVALID_THREAD_ID) {
+ thread_ids.push_back(tid);
+ }
+ ch = response.GetChar(); // Skip the command separator
+ } while (ch == ','); // Make sure we got a comma separator
+ }
+ }
+
+ /*
+ * Connected bare-iron target (like YAMON gdb-stub) may not have support for
+ * qProcessInfo, qC and qfThreadInfo packets. The reply from '?' packet
+ * could
+ * be as simple as 'S05'. There is no packet which can give us pid and/or
+ * tid.
+ * Assume pid=tid=1 in such cases.
+ */
+ if ((response.IsUnsupportedResponse() || response.IsNormalResponse()) &&
+ thread_ids.size() == 0 && IsConnected()) {
+ thread_ids.push_back(1);
+ }
+ } else {
+#if !defined(LLDB_CONFIGURATION_DEBUG)
+ Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS |
+ GDBR_LOG_PACKETS));
+ if (log)
+ log->Printf("error: failed to get packet sequence mutex, not sending "
+ "packet 'qfThreadInfo'");
+#endif
+ sequence_mutex_unavailable = true;
+ }
+ return thread_ids.size();
+}
+
+lldb::addr_t GDBRemoteCommunicationClient::GetShlibInfoAddr() {
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qShlibInfoAddr", response, false) !=
+ PacketResult::Success ||
+ !response.IsNormalResponse())
+ return LLDB_INVALID_ADDRESS;
+ return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS);
+}
+
+lldb_private::Status GDBRemoteCommunicationClient::RunShellCommand(
+ const char *command, // Shouldn't be NULL
+ const FileSpec &
+ working_dir, // Pass empty FileSpec to use the current working directory
+ int *status_ptr, // Pass NULL if you don't want the process exit status
+ int *signo_ptr, // Pass NULL if you don't want the signal that caused the
+ // process to exit
+ std::string
+ *command_output, // Pass NULL if you don't want the command output
+ const Timeout<std::micro> &timeout) {
+ lldb_private::StreamString stream;
+ stream.PutCString("qPlatform_shell:");
+ stream.PutBytesAsRawHex8(command, strlen(command));
+ stream.PutChar(',');
+ uint32_t timeout_sec = UINT32_MAX;
+ if (timeout) {
+ // TODO: Use chrono version of std::ceil once c++17 is available.
+ timeout_sec = std::ceil(std::chrono::duration<double>(*timeout).count());
+ }
+ stream.PutHex32(timeout_sec);
+ if (working_dir) {
+ std::string path{working_dir.GetPath(false)};
+ stream.PutChar(',');
+ stream.PutStringAsRawHex8(path);
+ }
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.GetChar() != 'F')
+ return Status("malformed reply");
+ if (response.GetChar() != ',')
+ return Status("malformed reply");
+ uint32_t exitcode = response.GetHexMaxU32(false, UINT32_MAX);
+ if (exitcode == UINT32_MAX)
+ return Status("unable to run remote process");
+ else if (status_ptr)
+ *status_ptr = exitcode;
+ if (response.GetChar() != ',')
+ return Status("malformed reply");
+ uint32_t signo = response.GetHexMaxU32(false, UINT32_MAX);
+ if (signo_ptr)
+ *signo_ptr = signo;
+ if (response.GetChar() != ',')
+ return Status("malformed reply");
+ std::string output;
+ response.GetEscapedBinaryData(output);
+ if (command_output)
+ command_output->assign(output);
+ return Status();
+ }
+ return Status("unable to send packet");
+}
+
+Status GDBRemoteCommunicationClient::MakeDirectory(const FileSpec &file_spec,
+ uint32_t file_permissions) {
+ std::string path{file_spec.GetPath(false)};
+ lldb_private::StreamString stream;
+ stream.PutCString("qPlatform_mkdir:");
+ stream.PutHex32(file_permissions);
+ stream.PutChar(',');
+ stream.PutStringAsRawHex8(path);
+ llvm::StringRef packet = stream.GetString();
+ StringExtractorGDBRemote response;
+
+ if (SendPacketAndWaitForResponse(packet, response, false) !=
+ PacketResult::Success)
+ return Status("failed to send '%s' packet", packet.str().c_str());
+
+ if (response.GetChar() != 'F')
+ return Status("invalid response to '%s' packet", packet.str().c_str());
+
+ return Status(response.GetU32(UINT32_MAX), eErrorTypePOSIX);
+}
+
+Status
+GDBRemoteCommunicationClient::SetFilePermissions(const FileSpec &file_spec,
+ uint32_t file_permissions) {
+ std::string path{file_spec.GetPath(false)};
+ lldb_private::StreamString stream;
+ stream.PutCString("qPlatform_chmod:");
+ stream.PutHex32(file_permissions);
+ stream.PutChar(',');
+ stream.PutStringAsRawHex8(path);
+ llvm::StringRef packet = stream.GetString();
+ StringExtractorGDBRemote response;
+
+ if (SendPacketAndWaitForResponse(packet, response, false) !=
+ PacketResult::Success)
+ return Status("failed to send '%s' packet", stream.GetData());
+
+ if (response.GetChar() != 'F')
+ return Status("invalid response to '%s' packet", stream.GetData());
+
+ return Status(response.GetU32(UINT32_MAX), eErrorTypePOSIX);
+}
+
+static uint64_t ParseHostIOPacketResponse(StringExtractorGDBRemote &response,
+ uint64_t fail_result, Status &error) {
+ response.SetFilePos(0);
+ if (response.GetChar() != 'F')
+ return fail_result;
+ int32_t result = response.GetS32(-2);
+ if (result == -2)
+ return fail_result;
+ if (response.GetChar() == ',') {
+ int result_errno = response.GetS32(-2);
+ if (result_errno != -2)
+ error.SetError(result_errno, eErrorTypePOSIX);
+ else
+ error.SetError(-1, eErrorTypeGeneric);
+ } else
+ error.Clear();
+ return result;
+}
+lldb::user_id_t
+GDBRemoteCommunicationClient::OpenFile(const lldb_private::FileSpec &file_spec,
+ uint32_t flags, mode_t mode,
+ Status &error) {
+ std::string path(file_spec.GetPath(false));
+ lldb_private::StreamString stream;
+ stream.PutCString("vFile:open:");
+ if (path.empty())
+ return UINT64_MAX;
+ stream.PutStringAsRawHex8(path);
+ stream.PutChar(',');
+ stream.PutHex32(flags);
+ stream.PutChar(',');
+ stream.PutHex32(mode);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ return ParseHostIOPacketResponse(response, UINT64_MAX, error);
+ }
+ return UINT64_MAX;
+}
+
+bool GDBRemoteCommunicationClient::CloseFile(lldb::user_id_t fd,
+ Status &error) {
+ lldb_private::StreamString stream;
+ stream.Printf("vFile:close:%i", (int)fd);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ return ParseHostIOPacketResponse(response, -1, error) == 0;
+ }
+ return false;
+}
+
+// Extension of host I/O packets to get the file size.
+lldb::user_id_t GDBRemoteCommunicationClient::GetFileSize(
+ const lldb_private::FileSpec &file_spec) {
+ std::string path(file_spec.GetPath(false));
+ lldb_private::StreamString stream;
+ stream.PutCString("vFile:size:");
+ stream.PutStringAsRawHex8(path);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.GetChar() != 'F')
+ return UINT64_MAX;
+ uint32_t retcode = response.GetHexMaxU64(false, UINT64_MAX);
+ return retcode;
+ }
+ return UINT64_MAX;
+}
+
+Status
+GDBRemoteCommunicationClient::GetFilePermissions(const FileSpec &file_spec,
+ uint32_t &file_permissions) {
+ std::string path{file_spec.GetPath(false)};
+ Status error;
+ lldb_private::StreamString stream;
+ stream.PutCString("vFile:mode:");
+ stream.PutStringAsRawHex8(path);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.GetChar() != 'F') {
+ error.SetErrorStringWithFormat("invalid response to '%s' packet",
+ stream.GetData());
+ } else {
+ const uint32_t mode = response.GetS32(-1);
+ if (static_cast<int32_t>(mode) == -1) {
+ if (response.GetChar() == ',') {
+ int response_errno = response.GetS32(-1);
+ if (response_errno > 0)
+ error.SetError(response_errno, lldb::eErrorTypePOSIX);
+ else
+ error.SetErrorToGenericError();
+ } else
+ error.SetErrorToGenericError();
+ } else {
+ file_permissions = mode & (S_IRWXU | S_IRWXG | S_IRWXO);
+ }
+ }
+ } else {
+ error.SetErrorStringWithFormat("failed to send '%s' packet",
+ stream.GetData());
+ }
+ return error;
+}
+
+uint64_t GDBRemoteCommunicationClient::ReadFile(lldb::user_id_t fd,
+ uint64_t offset, void *dst,
+ uint64_t dst_len,
+ Status &error) {
+ lldb_private::StreamString stream;
+ stream.Printf("vFile:pread:%i,%" PRId64 ",%" PRId64, (int)fd, dst_len,
+ offset);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.GetChar() != 'F')
+ return 0;
+ uint32_t retcode = response.GetHexMaxU32(false, UINT32_MAX);
+ if (retcode == UINT32_MAX)
+ return retcode;
+ const char next = (response.Peek() ? *response.Peek() : 0);
+ if (next == ',')
+ return 0;
+ if (next == ';') {
+ response.GetChar(); // skip the semicolon
+ std::string buffer;
+ if (response.GetEscapedBinaryData(buffer)) {
+ const uint64_t data_to_write =
+ std::min<uint64_t>(dst_len, buffer.size());
+ if (data_to_write > 0)
+ memcpy(dst, &buffer[0], data_to_write);
+ return data_to_write;
+ }
+ }
+ }
+ return 0;
+}
+
+uint64_t GDBRemoteCommunicationClient::WriteFile(lldb::user_id_t fd,
+ uint64_t offset,
+ const void *src,
+ uint64_t src_len,
+ Status &error) {
+ lldb_private::StreamGDBRemote stream;
+ stream.Printf("vFile:pwrite:%i,%" PRId64 ",", (int)fd, offset);
+ stream.PutEscapedBytes(src, src_len);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.GetChar() != 'F') {
+ error.SetErrorStringWithFormat("write file failed");
+ return 0;
+ }
+ uint64_t bytes_written = response.GetU64(UINT64_MAX);
+ if (bytes_written == UINT64_MAX) {
+ error.SetErrorToGenericError();
+ if (response.GetChar() == ',') {
+ int response_errno = response.GetS32(-1);
+ if (response_errno > 0)
+ error.SetError(response_errno, lldb::eErrorTypePOSIX);
+ }
+ return 0;
+ }
+ return bytes_written;
+ } else {
+ error.SetErrorString("failed to send vFile:pwrite packet");
+ }
+ return 0;
+}
+
+Status GDBRemoteCommunicationClient::CreateSymlink(const FileSpec &src,
+ const FileSpec &dst) {
+ std::string src_path{src.GetPath(false)}, dst_path{dst.GetPath(false)};
+ Status error;
+ lldb_private::StreamGDBRemote stream;
+ stream.PutCString("vFile:symlink:");
+ // the unix symlink() command reverses its parameters where the dst if first,
+ // so we follow suit here
+ stream.PutStringAsRawHex8(dst_path);
+ stream.PutChar(',');
+ stream.PutStringAsRawHex8(src_path);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.GetChar() == 'F') {
+ uint32_t result = response.GetU32(UINT32_MAX);
+ if (result != 0) {
+ error.SetErrorToGenericError();
+ if (response.GetChar() == ',') {
+ int response_errno = response.GetS32(-1);
+ if (response_errno > 0)
+ error.SetError(response_errno, lldb::eErrorTypePOSIX);
+ }
+ }
+ } else {
+ // Should have returned with 'F<result>[,<errno>]'
+ error.SetErrorStringWithFormat("symlink failed");
+ }
+ } else {
+ error.SetErrorString("failed to send vFile:symlink packet");
+ }
+ return error;
+}
+
+Status GDBRemoteCommunicationClient::Unlink(const FileSpec &file_spec) {
+ std::string path{file_spec.GetPath(false)};
+ Status error;
+ lldb_private::StreamGDBRemote stream;
+ stream.PutCString("vFile:unlink:");
+ // the unix symlink() command reverses its parameters where the dst if first,
+ // so we follow suit here
+ stream.PutStringAsRawHex8(path);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.GetChar() == 'F') {
+ uint32_t result = response.GetU32(UINT32_MAX);
+ if (result != 0) {
+ error.SetErrorToGenericError();
+ if (response.GetChar() == ',') {
+ int response_errno = response.GetS32(-1);
+ if (response_errno > 0)
+ error.SetError(response_errno, lldb::eErrorTypePOSIX);
+ }
+ }
+ } else {
+ // Should have returned with 'F<result>[,<errno>]'
+ error.SetErrorStringWithFormat("unlink failed");
+ }
+ } else {
+ error.SetErrorString("failed to send vFile:unlink packet");
+ }
+ return error;
+}
+
+// Extension of host I/O packets to get whether a file exists.
+bool GDBRemoteCommunicationClient::GetFileExists(
+ const lldb_private::FileSpec &file_spec) {
+ std::string path(file_spec.GetPath(false));
+ lldb_private::StreamString stream;
+ stream.PutCString("vFile:exists:");
+ stream.PutStringAsRawHex8(path);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.GetChar() != 'F')
+ return false;
+ if (response.GetChar() != ',')
+ return false;
+ bool retcode = (response.GetChar() != '0');
+ return retcode;
+ }
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::CalculateMD5(
+ const lldb_private::FileSpec &file_spec, uint64_t &high, uint64_t &low) {
+ std::string path(file_spec.GetPath(false));
+ lldb_private::StreamString stream;
+ stream.PutCString("vFile:MD5:");
+ stream.PutStringAsRawHex8(path);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(stream.GetString(), response, false) ==
+ PacketResult::Success) {
+ if (response.GetChar() != 'F')
+ return false;
+ if (response.GetChar() != ',')
+ return false;
+ if (response.Peek() && *response.Peek() == 'x')
+ return false;
+ low = response.GetHexMaxU64(false, UINT64_MAX);
+ high = response.GetHexMaxU64(false, UINT64_MAX);
+ return true;
+ }
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::AvoidGPackets(ProcessGDBRemote *process) {
+ // Some targets have issues with g/G packets and we need to avoid using them
+ if (m_avoid_g_packets == eLazyBoolCalculate) {
+ if (process) {
+ m_avoid_g_packets = eLazyBoolNo;
+ const ArchSpec &arch = process->GetTarget().GetArchitecture();
+ if (arch.IsValid() &&
+ arch.GetTriple().getVendor() == llvm::Triple::Apple &&
+ arch.GetTriple().getOS() == llvm::Triple::IOS &&
+ arch.GetTriple().getArch() == llvm::Triple::aarch64) {
+ m_avoid_g_packets = eLazyBoolYes;
+ uint32_t gdb_server_version = GetGDBServerProgramVersion();
+ if (gdb_server_version != 0) {
+ const char *gdb_server_name = GetGDBServerProgramName();
+ if (gdb_server_name && strcmp(gdb_server_name, "debugserver") == 0) {
+ if (gdb_server_version >= 310)
+ m_avoid_g_packets = eLazyBoolNo;
+ }
+ }
+ }
+ }
+ }
+ return m_avoid_g_packets == eLazyBoolYes;
+}
+
+DataBufferSP GDBRemoteCommunicationClient::ReadRegister(lldb::tid_t tid,
+ uint32_t reg) {
+ StreamString payload;
+ payload.Printf("p%x", reg);
+ StringExtractorGDBRemote response;
+ if (SendThreadSpecificPacketAndWaitForResponse(
+ tid, std::move(payload), response, false) != PacketResult::Success ||
+ !response.IsNormalResponse())
+ return nullptr;
+
+ DataBufferSP buffer_sp(
+ new DataBufferHeap(response.GetStringRef().size() / 2, 0));
+ response.GetHexBytes(buffer_sp->GetData(), '\xcc');
+ return buffer_sp;
+}
+
+DataBufferSP GDBRemoteCommunicationClient::ReadAllRegisters(lldb::tid_t tid) {
+ StreamString payload;
+ payload.PutChar('g');
+ StringExtractorGDBRemote response;
+ if (SendThreadSpecificPacketAndWaitForResponse(
+ tid, std::move(payload), response, false) != PacketResult::Success ||
+ !response.IsNormalResponse())
+ return nullptr;
+
+ DataBufferSP buffer_sp(
+ new DataBufferHeap(response.GetStringRef().size() / 2, 0));
+ response.GetHexBytes(buffer_sp->GetData(), '\xcc');
+ return buffer_sp;
+}
+
+bool GDBRemoteCommunicationClient::WriteRegister(lldb::tid_t tid,
+ uint32_t reg_num,
+ llvm::ArrayRef<uint8_t> data) {
+ StreamString payload;
+ payload.Printf("P%x=", reg_num);
+ payload.PutBytesAsRawHex8(data.data(), data.size(),
+ endian::InlHostByteOrder(),
+ endian::InlHostByteOrder());
+ StringExtractorGDBRemote response;
+ return SendThreadSpecificPacketAndWaitForResponse(tid, std::move(payload),
+ response, false) ==
+ PacketResult::Success &&
+ response.IsOKResponse();
+}
+
+bool GDBRemoteCommunicationClient::WriteAllRegisters(
+ lldb::tid_t tid, llvm::ArrayRef<uint8_t> data) {
+ StreamString payload;
+ payload.PutChar('G');
+ payload.PutBytesAsRawHex8(data.data(), data.size(),
+ endian::InlHostByteOrder(),
+ endian::InlHostByteOrder());
+ StringExtractorGDBRemote response;
+ return SendThreadSpecificPacketAndWaitForResponse(tid, std::move(payload),
+ response, false) ==
+ PacketResult::Success &&
+ response.IsOKResponse();
+}
+
+bool GDBRemoteCommunicationClient::SaveRegisterState(lldb::tid_t tid,
+ uint32_t &save_id) {
+ save_id = 0; // Set to invalid save ID
+ if (m_supports_QSaveRegisterState == eLazyBoolNo)
+ return false;
+
+ m_supports_QSaveRegisterState = eLazyBoolYes;
+ StreamString payload;
+ payload.PutCString("QSaveRegisterState");
+ StringExtractorGDBRemote response;
+ if (SendThreadSpecificPacketAndWaitForResponse(
+ tid, std::move(payload), response, false) != PacketResult::Success)
+ return false;
+
+ if (response.IsUnsupportedResponse())
+ m_supports_QSaveRegisterState = eLazyBoolNo;
+
+ const uint32_t response_save_id = response.GetU32(0);
+ if (response_save_id == 0)
+ return false;
+
+ save_id = response_save_id;
+ return true;
+}
+
+bool GDBRemoteCommunicationClient::RestoreRegisterState(lldb::tid_t tid,
+ uint32_t save_id) {
+ // We use the "m_supports_QSaveRegisterState" variable here because the
+ // QSaveRegisterState and QRestoreRegisterState packets must both be
+ // supported in order to be useful
+ if (m_supports_QSaveRegisterState == eLazyBoolNo)
+ return false;
+
+ StreamString payload;
+ payload.Printf("QRestoreRegisterState:%u", save_id);
+ StringExtractorGDBRemote response;
+ if (SendThreadSpecificPacketAndWaitForResponse(
+ tid, std::move(payload), response, false) != PacketResult::Success)
+ return false;
+
+ if (response.IsOKResponse())
+ return true;
+
+ if (response.IsUnsupportedResponse())
+ m_supports_QSaveRegisterState = eLazyBoolNo;
+ return false;
+}
+
+bool GDBRemoteCommunicationClient::SyncThreadState(lldb::tid_t tid) {
+ if (!GetSyncThreadStateSupported())
+ return false;
+
+ StreamString packet;
+ StringExtractorGDBRemote response;
+ packet.Printf("QSyncThreadState:%4.4" PRIx64 ";", tid);
+ return SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
+ GDBRemoteCommunication::PacketResult::Success &&
+ response.IsOKResponse();
+}
+
+lldb::user_id_t
+GDBRemoteCommunicationClient::SendStartTracePacket(const TraceOptions &options,
+ Status &error) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ lldb::user_id_t ret_uid = LLDB_INVALID_UID;
+
+ StreamGDBRemote escaped_packet;
+ escaped_packet.PutCString("jTraceStart:");
+
+ StructuredData::Dictionary json_packet;
+ json_packet.AddIntegerItem("type", options.getType());
+ json_packet.AddIntegerItem("buffersize", options.getTraceBufferSize());
+ json_packet.AddIntegerItem("metabuffersize", options.getMetaDataBufferSize());
+
+ if (options.getThreadID() != LLDB_INVALID_THREAD_ID)
+ json_packet.AddIntegerItem("threadid", options.getThreadID());
+
+ StructuredData::DictionarySP custom_params = options.getTraceParams();
+ if (custom_params)
+ json_packet.AddItem("params", custom_params);
+
+ StreamString json_string;
+ json_packet.Dump(json_string, false);
+ escaped_packet.PutEscapedBytes(json_string.GetData(), json_string.GetSize());
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response,
+ true) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ if (!response.IsNormalResponse()) {
+ error = response.GetStatus();
+ LLDB_LOG(log, "Target does not support Tracing , error {0}", error);
+ } else {
+ ret_uid = response.GetHexMaxU64(false, LLDB_INVALID_UID);
+ }
+ } else {
+ LLDB_LOG(log, "failed to send packet");
+ error.SetErrorStringWithFormat("failed to send packet: '%s'",
+ escaped_packet.GetData());
+ }
+ return ret_uid;
+}
+
+Status
+GDBRemoteCommunicationClient::SendStopTracePacket(lldb::user_id_t uid,
+ lldb::tid_t thread_id) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ StringExtractorGDBRemote response;
+ Status error;
+
+ StructuredData::Dictionary json_packet;
+ StreamGDBRemote escaped_packet;
+ StreamString json_string;
+ escaped_packet.PutCString("jTraceStop:");
+
+ json_packet.AddIntegerItem("traceid", uid);
+
+ if (thread_id != LLDB_INVALID_THREAD_ID)
+ json_packet.AddIntegerItem("threadid", thread_id);
+
+ json_packet.Dump(json_string, false);
+
+ escaped_packet.PutEscapedBytes(json_string.GetData(), json_string.GetSize());
+
+ if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response,
+ true) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ if (!response.IsOKResponse()) {
+ error = response.GetStatus();
+ LLDB_LOG(log, "stop tracing failed");
+ }
+ } else {
+ LLDB_LOG(log, "failed to send packet");
+ error.SetErrorStringWithFormat(
+ "failed to send packet: '%s' with error '%d'", escaped_packet.GetData(),
+ response.GetError());
+ }
+ return error;
+}
+
+Status GDBRemoteCommunicationClient::SendGetDataPacket(
+ lldb::user_id_t uid, lldb::tid_t thread_id,
+ llvm::MutableArrayRef<uint8_t> &buffer, size_t offset) {
+
+ StreamGDBRemote escaped_packet;
+ escaped_packet.PutCString("jTraceBufferRead:");
+ return SendGetTraceDataPacket(escaped_packet, uid, thread_id, buffer, offset);
+}
+
+Status GDBRemoteCommunicationClient::SendGetMetaDataPacket(
+ lldb::user_id_t uid, lldb::tid_t thread_id,
+ llvm::MutableArrayRef<uint8_t> &buffer, size_t offset) {
+
+ StreamGDBRemote escaped_packet;
+ escaped_packet.PutCString("jTraceMetaRead:");
+ return SendGetTraceDataPacket(escaped_packet, uid, thread_id, buffer, offset);
+}
+
+Status
+GDBRemoteCommunicationClient::SendGetTraceConfigPacket(lldb::user_id_t uid,
+ TraceOptions &options) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ StringExtractorGDBRemote response;
+ Status error;
+
+ StreamString json_string;
+ StreamGDBRemote escaped_packet;
+ escaped_packet.PutCString("jTraceConfigRead:");
+
+ StructuredData::Dictionary json_packet;
+ json_packet.AddIntegerItem("traceid", uid);
+
+ if (options.getThreadID() != LLDB_INVALID_THREAD_ID)
+ json_packet.AddIntegerItem("threadid", options.getThreadID());
+
+ json_packet.Dump(json_string, false);
+ escaped_packet.PutEscapedBytes(json_string.GetData(), json_string.GetSize());
+
+ if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response,
+ true) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ if (response.IsNormalResponse()) {
+ uint64_t type = std::numeric_limits<uint64_t>::max();
+ uint64_t buffersize = std::numeric_limits<uint64_t>::max();
+ uint64_t metabuffersize = std::numeric_limits<uint64_t>::max();
+
+ auto json_object = StructuredData::ParseJSON(response.Peek());
+
+ if (!json_object ||
+ json_object->GetType() != lldb::eStructuredDataTypeDictionary) {
+ error.SetErrorString("Invalid Configuration obtained");
+ return error;
+ }
+
+ auto json_dict = json_object->GetAsDictionary();
+
+ json_dict->GetValueForKeyAsInteger<uint64_t>("metabuffersize",
+ metabuffersize);
+ options.setMetaDataBufferSize(metabuffersize);
+
+ json_dict->GetValueForKeyAsInteger<uint64_t>("buffersize", buffersize);
+ options.setTraceBufferSize(buffersize);
+
+ json_dict->GetValueForKeyAsInteger<uint64_t>("type", type);
+ options.setType(static_cast<lldb::TraceType>(type));
+
+ StructuredData::ObjectSP custom_params_sp =
+ json_dict->GetValueForKey("params");
+ if (custom_params_sp) {
+ if (custom_params_sp->GetType() !=
+ lldb::eStructuredDataTypeDictionary) {
+ error.SetErrorString("Invalid Configuration obtained");
+ return error;
+ } else
+ options.setTraceParams(
+ static_pointer_cast<StructuredData::Dictionary>(
+ custom_params_sp));
+ }
+ } else {
+ error = response.GetStatus();
+ }
+ } else {
+ LLDB_LOG(log, "failed to send packet");
+ error.SetErrorStringWithFormat("failed to send packet: '%s'",
+ escaped_packet.GetData());
+ }
+ return error;
+}
+
+Status GDBRemoteCommunicationClient::SendGetTraceDataPacket(
+ StreamGDBRemote &packet, lldb::user_id_t uid, lldb::tid_t thread_id,
+ llvm::MutableArrayRef<uint8_t> &buffer, size_t offset) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ Status error;
+
+ StructuredData::Dictionary json_packet;
+
+ json_packet.AddIntegerItem("traceid", uid);
+ json_packet.AddIntegerItem("offset", offset);
+ json_packet.AddIntegerItem("buffersize", buffer.size());
+
+ if (thread_id != LLDB_INVALID_THREAD_ID)
+ json_packet.AddIntegerItem("threadid", thread_id);
+
+ StreamString json_string;
+ json_packet.Dump(json_string, false);
+
+ packet.PutEscapedBytes(json_string.GetData(), json_string.GetSize());
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, true) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ if (response.IsNormalResponse()) {
+ size_t filled_size = response.GetHexBytesAvail(buffer);
+ buffer = llvm::MutableArrayRef<uint8_t>(buffer.data(), filled_size);
+ } else {
+ error = response.GetStatus();
+ buffer = buffer.slice(buffer.size());
+ }
+ } else {
+ LLDB_LOG(log, "failed to send packet");
+ error.SetErrorStringWithFormat("failed to send packet: '%s'",
+ packet.GetData());
+ buffer = buffer.slice(buffer.size());
+ }
+ return error;
+}
+
+bool GDBRemoteCommunicationClient::GetModuleInfo(
+ const FileSpec &module_file_spec, const lldb_private::ArchSpec &arch_spec,
+ ModuleSpec &module_spec) {
+ if (!m_supports_qModuleInfo)
+ return false;
+
+ std::string module_path = module_file_spec.GetPath(false);
+ if (module_path.empty())
+ return false;
+
+ StreamString packet;
+ packet.PutCString("qModuleInfo:");
+ packet.PutStringAsRawHex8(module_path);
+ packet.PutCString(";");
+ const auto &triple = arch_spec.GetTriple().getTriple();
+ packet.PutStringAsRawHex8(triple);
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(packet.GetString(), response, false) !=
+ PacketResult::Success)
+ return false;
+
+ if (response.IsErrorResponse())
+ return false;
+
+ if (response.IsUnsupportedResponse()) {
+ m_supports_qModuleInfo = false;
+ return false;
+ }
+
+ llvm::StringRef name;
+ llvm::StringRef value;
+
+ module_spec.Clear();
+ module_spec.GetFileSpec() = module_file_spec;
+
+ while (response.GetNameColonValue(name, value)) {
+ if (name == "uuid" || name == "md5") {
+ StringExtractor extractor(value);
+ std::string uuid;
+ extractor.GetHexByteString(uuid);
+ module_spec.GetUUID().SetFromStringRef(uuid, uuid.size() / 2);
+ } else if (name == "triple") {
+ StringExtractor extractor(value);
+ std::string triple;
+ extractor.GetHexByteString(triple);
+ module_spec.GetArchitecture().SetTriple(triple.c_str());
+ } else if (name == "file_offset") {
+ uint64_t ival = 0;
+ if (!value.getAsInteger(16, ival))
+ module_spec.SetObjectOffset(ival);
+ } else if (name == "file_size") {
+ uint64_t ival = 0;
+ if (!value.getAsInteger(16, ival))
+ module_spec.SetObjectSize(ival);
+ } else if (name == "file_path") {
+ StringExtractor extractor(value);
+ std::string path;
+ extractor.GetHexByteString(path);
+ module_spec.GetFileSpec() = FileSpec(path, arch_spec.GetTriple());
+ }
+ }
+
+ return true;
+}
+
+static llvm::Optional<ModuleSpec>
+ParseModuleSpec(StructuredData::Dictionary *dict) {
+ ModuleSpec result;
+ if (!dict)
+ return llvm::None;
+
+ llvm::StringRef string;
+ uint64_t integer;
+
+ if (!dict->GetValueForKeyAsString("uuid", string))
+ return llvm::None;
+ if (result.GetUUID().SetFromStringRef(string, string.size() / 2) !=
+ string.size())
+ return llvm::None;
+
+ if (!dict->GetValueForKeyAsInteger("file_offset", integer))
+ return llvm::None;
+ result.SetObjectOffset(integer);
+
+ if (!dict->GetValueForKeyAsInteger("file_size", integer))
+ return llvm::None;
+ result.SetObjectSize(integer);
+
+ if (!dict->GetValueForKeyAsString("triple", string))
+ return llvm::None;
+ result.GetArchitecture().SetTriple(string);
+
+ if (!dict->GetValueForKeyAsString("file_path", string))
+ return llvm::None;
+ result.GetFileSpec() = FileSpec(string, result.GetArchitecture().GetTriple());
+
+ return result;
+}
+
+llvm::Optional<std::vector<ModuleSpec>>
+GDBRemoteCommunicationClient::GetModulesInfo(
+ llvm::ArrayRef<FileSpec> module_file_specs, const llvm::Triple &triple) {
+ if (!m_supports_jModulesInfo)
+ return llvm::None;
+
+ JSONArray::SP module_array_sp = std::make_shared<JSONArray>();
+ for (const FileSpec &module_file_spec : module_file_specs) {
+ JSONObject::SP module_sp = std::make_shared<JSONObject>();
+ module_array_sp->AppendObject(module_sp);
+ module_sp->SetObject(
+ "file", std::make_shared<JSONString>(module_file_spec.GetPath(false)));
+ module_sp->SetObject("triple",
+ std::make_shared<JSONString>(triple.getTriple()));
+ }
+ StreamString unescaped_payload;
+ unescaped_payload.PutCString("jModulesInfo:");
+ module_array_sp->Write(unescaped_payload);
+ StreamGDBRemote payload;
+ payload.PutEscapedBytes(unescaped_payload.GetString().data(),
+ unescaped_payload.GetSize());
+
+ // Increase the timeout for jModulesInfo since this packet can take longer.
+ ScopedTimeout timeout(*this, std::chrono::seconds(10));
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(payload.GetString(), response, false) !=
+ PacketResult::Success ||
+ response.IsErrorResponse())
+ return llvm::None;
+
+ if (response.IsUnsupportedResponse()) {
+ m_supports_jModulesInfo = false;
+ return llvm::None;
+ }
+
+ StructuredData::ObjectSP response_object_sp =
+ StructuredData::ParseJSON(response.GetStringRef());
+ if (!response_object_sp)
+ return llvm::None;
+
+ StructuredData::Array *response_array = response_object_sp->GetAsArray();
+ if (!response_array)
+ return llvm::None;
+
+ std::vector<ModuleSpec> result;
+ for (size_t i = 0; i < response_array->GetSize(); ++i) {
+ if (llvm::Optional<ModuleSpec> module_spec = ParseModuleSpec(
+ response_array->GetItemAtIndex(i)->GetAsDictionary()))
+ result.push_back(*module_spec);
+ }
+
+ return result;
+}
+
+// query the target remote for extended information using the qXfer packet
+//
+// example: object='features', annex='target.xml', out=<xml output> return:
+// 'true' on success
+// 'false' on failure (err set)
+bool GDBRemoteCommunicationClient::ReadExtFeature(
+ const lldb_private::ConstString object,
+ const lldb_private::ConstString annex, std::string &out,
+ lldb_private::Status &err) {
+
+ std::stringstream output;
+ StringExtractorGDBRemote chunk;
+
+ uint64_t size = GetRemoteMaxPacketSize();
+ if (size == 0)
+ size = 0x1000;
+ size = size - 1; // Leave space for the 'm' or 'l' character in the response
+ int offset = 0;
+ bool active = true;
+
+ // loop until all data has been read
+ while (active) {
+
+ // send query extended feature packet
+ std::stringstream packet;
+ packet << "qXfer:" << object.AsCString("")
+ << ":read:" << annex.AsCString("") << ":" << std::hex << offset
+ << "," << std::hex << size;
+
+ GDBRemoteCommunication::PacketResult res =
+ SendPacketAndWaitForResponse(packet.str(), chunk, false);
+
+ if (res != GDBRemoteCommunication::PacketResult::Success) {
+ err.SetErrorString("Error sending $qXfer packet");
+ return false;
+ }
+
+ const std::string &str = chunk.GetStringRef();
+ if (str.length() == 0) {
+ // should have some data in chunk
+ err.SetErrorString("Empty response from $qXfer packet");
+ return false;
+ }
+
+ // check packet code
+ switch (str[0]) {
+ // last chunk
+ case ('l'):
+ active = false;
+ LLVM_FALLTHROUGH;
+
+ // more chunks
+ case ('m'):
+ if (str.length() > 1)
+ output << &str[1];
+ offset += size;
+ break;
+
+ // unknown chunk
+ default:
+ err.SetErrorString("Invalid continuation code from $qXfer packet");
+ return false;
+ }
+ }
+
+ out = output.str();
+ err.Success();
+ return true;
+}
+
+// Notify the target that gdb is prepared to serve symbol lookup requests.
+// packet: "qSymbol::"
+// reply:
+// OK The target does not need to look up any (more) symbols.
+// qSymbol:<sym_name> The target requests the value of symbol sym_name (hex
+// encoded).
+// LLDB may provide the value by sending another qSymbol
+// packet
+// in the form of"qSymbol:<sym_value>:<sym_name>".
+//
+// Three examples:
+//
+// lldb sends: qSymbol::
+// lldb receives: OK
+// Remote gdb stub does not need to know the addresses of any symbols, lldb
+// does not
+// need to ask again in this session.
+//
+// lldb sends: qSymbol::
+// lldb receives: qSymbol:64697370617463685f71756575655f6f666673657473
+// lldb sends: qSymbol::64697370617463685f71756575655f6f666673657473
+// lldb receives: OK
+// Remote gdb stub asks for address of 'dispatch_queue_offsets'. lldb does
+// not know
+// the address at this time. lldb needs to send qSymbol:: again when it has
+// more
+// solibs loaded.
+//
+// lldb sends: qSymbol::
+// lldb receives: qSymbol:64697370617463685f71756575655f6f666673657473
+// lldb sends: qSymbol:2bc97554:64697370617463685f71756575655f6f666673657473
+// lldb receives: OK
+// Remote gdb stub asks for address of 'dispatch_queue_offsets'. lldb says
+// that it
+// is at address 0x2bc97554. Remote gdb stub sends 'OK' indicating that it
+// does not
+// need any more symbols. lldb does not need to ask again in this session.
+
+void GDBRemoteCommunicationClient::ServeSymbolLookups(
+ lldb_private::Process *process) {
+ // Set to true once we've resolved a symbol to an address for the remote
+ // stub. If we get an 'OK' response after this, the remote stub doesn't need
+ // any more symbols and we can stop asking.
+ bool symbol_response_provided = false;
+
+ // Is this the initial qSymbol:: packet?
+ bool first_qsymbol_query = true;
+
+ if (m_supports_qSymbol && !m_qSymbol_requests_done) {
+ Lock lock(*this, false);
+ if (lock) {
+ StreamString packet;
+ packet.PutCString("qSymbol::");
+ StringExtractorGDBRemote response;
+ while (SendPacketAndWaitForResponseNoLock(packet.GetString(), response) ==
+ PacketResult::Success) {
+ if (response.IsOKResponse()) {
+ if (symbol_response_provided || first_qsymbol_query) {
+ m_qSymbol_requests_done = true;
+ }
+
+ // We are done serving symbols requests
+ return;
+ }
+ first_qsymbol_query = false;
+
+ if (response.IsUnsupportedResponse()) {
+ // qSymbol is not supported by the current GDB server we are
+ // connected to
+ m_supports_qSymbol = false;
+ return;
+ } else {
+ llvm::StringRef response_str(response.GetStringRef());
+ if (response_str.startswith("qSymbol:")) {
+ response.SetFilePos(strlen("qSymbol:"));
+ std::string symbol_name;
+ if (response.GetHexByteString(symbol_name)) {
+ if (symbol_name.empty())
+ return;
+
+ addr_t symbol_load_addr = LLDB_INVALID_ADDRESS;
+ lldb_private::SymbolContextList sc_list;
+ if (process->GetTarget().GetImages().FindSymbolsWithNameAndType(
+ ConstString(symbol_name), eSymbolTypeAny, sc_list)) {
+ const size_t num_scs = sc_list.GetSize();
+ for (size_t sc_idx = 0;
+ sc_idx < num_scs &&
+ symbol_load_addr == LLDB_INVALID_ADDRESS;
+ ++sc_idx) {
+ SymbolContext sc;
+ if (sc_list.GetContextAtIndex(sc_idx, sc)) {
+ if (sc.symbol) {
+ switch (sc.symbol->GetType()) {
+ case eSymbolTypeInvalid:
+ case eSymbolTypeAbsolute:
+ case eSymbolTypeUndefined:
+ case eSymbolTypeSourceFile:
+ case eSymbolTypeHeaderFile:
+ case eSymbolTypeObjectFile:
+ case eSymbolTypeCommonBlock:
+ case eSymbolTypeBlock:
+ case eSymbolTypeLocal:
+ case eSymbolTypeParam:
+ case eSymbolTypeVariable:
+ case eSymbolTypeVariableType:
+ case eSymbolTypeLineEntry:
+ case eSymbolTypeLineHeader:
+ case eSymbolTypeScopeBegin:
+ case eSymbolTypeScopeEnd:
+ case eSymbolTypeAdditional:
+ case eSymbolTypeCompiler:
+ case eSymbolTypeInstrumentation:
+ case eSymbolTypeTrampoline:
+ break;
+
+ case eSymbolTypeCode:
+ case eSymbolTypeResolver:
+ case eSymbolTypeData:
+ case eSymbolTypeRuntime:
+ case eSymbolTypeException:
+ case eSymbolTypeObjCClass:
+ case eSymbolTypeObjCMetaClass:
+ case eSymbolTypeObjCIVar:
+ case eSymbolTypeReExported:
+ symbol_load_addr =
+ sc.symbol->GetLoadAddress(&process->GetTarget());
+ break;
+ }
+ }
+ }
+ }
+ }
+ // This is the normal path where our symbol lookup was successful
+ // and we want to send a packet with the new symbol value and see
+ // if another lookup needs to be done.
+
+ // Change "packet" to contain the requested symbol value and name
+ packet.Clear();
+ packet.PutCString("qSymbol:");
+ if (symbol_load_addr != LLDB_INVALID_ADDRESS) {
+ packet.Printf("%" PRIx64, symbol_load_addr);
+ symbol_response_provided = true;
+ } else {
+ symbol_response_provided = false;
+ }
+ packet.PutCString(":");
+ packet.PutBytesAsRawHex8(symbol_name.data(), symbol_name.size());
+ continue; // go back to the while loop and send "packet" and wait
+ // for another response
+ }
+ }
+ }
+ }
+ // If we make it here, the symbol request packet response wasn't valid or
+ // our symbol lookup failed so we must abort
+ return;
+
+ } else if (Log *log = ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(
+ GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)) {
+ log->Printf(
+ "GDBRemoteCommunicationClient::%s: Didn't get sequence mutex.",
+ __FUNCTION__);
+ }
+ }
+}
+
+StructuredData::Array *
+GDBRemoteCommunicationClient::GetSupportedStructuredDataPlugins() {
+ if (!m_supported_async_json_packets_is_valid) {
+ // Query the server for the array of supported asynchronous JSON packets.
+ m_supported_async_json_packets_is_valid = true;
+
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+
+ // Poll it now.
+ StringExtractorGDBRemote response;
+ const bool send_async = false;
+ if (SendPacketAndWaitForResponse("qStructuredDataPlugins", response,
+ send_async) == PacketResult::Success) {
+ m_supported_async_json_packets_sp =
+ StructuredData::ParseJSON(response.GetStringRef());
+ if (m_supported_async_json_packets_sp &&
+ !m_supported_async_json_packets_sp->GetAsArray()) {
+ // We were returned something other than a JSON array. This is
+ // invalid. Clear it out.
+ if (log)
+ log->Printf("GDBRemoteCommunicationClient::%s(): "
+ "QSupportedAsyncJSONPackets returned invalid "
+ "result: %s",
+ __FUNCTION__, response.GetStringRef().c_str());
+ m_supported_async_json_packets_sp.reset();
+ }
+ } else {
+ if (log)
+ log->Printf("GDBRemoteCommunicationClient::%s(): "
+ "QSupportedAsyncJSONPackets unsupported",
+ __FUNCTION__);
+ }
+
+ if (log && m_supported_async_json_packets_sp) {
+ StreamString stream;
+ m_supported_async_json_packets_sp->Dump(stream);
+ log->Printf("GDBRemoteCommunicationClient::%s(): supported async "
+ "JSON packets: %s",
+ __FUNCTION__, stream.GetData());
+ }
+ }
+
+ return m_supported_async_json_packets_sp
+ ? m_supported_async_json_packets_sp->GetAsArray()
+ : nullptr;
+}
+
+Status GDBRemoteCommunicationClient::SendSignalsToIgnore(
+ llvm::ArrayRef<int32_t> signals) {
+ // Format packet:
+ // QPassSignals:<hex_sig1>;<hex_sig2>...;<hex_sigN>
+ auto range = llvm::make_range(signals.begin(), signals.end());
+ std::string packet = formatv("QPassSignals:{0:$[;]@(x-2)}", range).str();
+
+ StringExtractorGDBRemote response;
+ auto send_status = SendPacketAndWaitForResponse(packet, response, false);
+
+ if (send_status != GDBRemoteCommunication::PacketResult::Success)
+ return Status("Sending QPassSignals packet failed");
+
+ if (response.IsOKResponse()) {
+ return Status();
+ } else {
+ return Status("Unknown error happened during sending QPassSignals packet.");
+ }
+}
+
+Status GDBRemoteCommunicationClient::ConfigureRemoteStructuredData(
+ ConstString type_name, const StructuredData::ObjectSP &config_sp) {
+ Status error;
+
+ if (type_name.GetLength() == 0) {
+ error.SetErrorString("invalid type_name argument");
+ return error;
+ }
+
+ // Build command: Configure{type_name}: serialized config data.
+ StreamGDBRemote stream;
+ stream.PutCString("QConfigure");
+ stream.PutCString(type_name.AsCString());
+ stream.PutChar(':');
+ if (config_sp) {
+ // Gather the plain-text version of the configuration data.
+ StreamString unescaped_stream;
+ config_sp->Dump(unescaped_stream);
+ unescaped_stream.Flush();
+
+ // Add it to the stream in escaped fashion.
+ stream.PutEscapedBytes(unescaped_stream.GetString().data(),
+ unescaped_stream.GetSize());
+ }
+ stream.Flush();
+
+ // Send the packet.
+ const bool send_async = false;
+ StringExtractorGDBRemote response;
+ auto result =
+ SendPacketAndWaitForResponse(stream.GetString(), response, send_async);
+ if (result == PacketResult::Success) {
+ // We failed if the config result comes back other than OK.
+ if (strcmp(response.GetStringRef().c_str(), "OK") == 0) {
+ // Okay!
+ error.Clear();
+ } else {
+ error.SetErrorStringWithFormat("configuring StructuredData feature "
+ "%s failed with error %s",
+ type_name.AsCString(),
+ response.GetStringRef().c_str());
+ }
+ } else {
+ // Can we get more data here on the failure?
+ error.SetErrorStringWithFormat("configuring StructuredData feature %s "
+ "failed when sending packet: "
+ "PacketResult=%d",
+ type_name.AsCString(), (int)result);
+ }
+ return error;
+}
+
+void GDBRemoteCommunicationClient::OnRunPacketSent(bool first) {
+ GDBRemoteClientBase::OnRunPacketSent(first);
+ m_curr_tid = LLDB_INVALID_THREAD_ID;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
new file mode 100644
index 000000000000..de85c9f8b67b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -0,0 +1,602 @@
+//===-- GDBRemoteCommunicationClient.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 liblldb_GDBRemoteCommunicationClient_h_
+#define liblldb_GDBRemoteCommunicationClient_h_
+
+#include "GDBRemoteClientBase.h"
+
+#include <chrono>
+#include <map>
+#include <mutex>
+#include <string>
+#include <vector>
+
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/StreamGDBRemote.h"
+#include "lldb/Utility/StructuredData.h"
+#if defined(_WIN32)
+#include "lldb/Host/windows/PosixApi.h"
+#endif
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/VersionTuple.h"
+
+namespace lldb_private {
+namespace process_gdb_remote {
+
+class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
+public:
+ GDBRemoteCommunicationClient();
+
+ ~GDBRemoteCommunicationClient() override;
+
+ // After connecting, send the handshake to the server to make sure
+ // we are communicating with it.
+ bool HandshakeWithServer(Status *error_ptr);
+
+ // For packets which specify a range of output to be returned,
+ // return all of the output via a series of request packets of the form
+ // <prefix>0,<size>
+ // <prefix><size>,<size>
+ // <prefix><size>*2,<size>
+ // <prefix><size>*3,<size>
+ // ...
+ // until a "$l..." packet is received, indicating the end.
+ // (size is in hex; this format is used by a standard gdbserver to
+ // return the given portion of the output specified by <prefix>;
+ // for example, "qXfer:libraries-svr4:read::fff,1000" means
+ // "return a chunk of the xml description file for shared
+ // library load addresses, where the chunk starts at offset 0xfff
+ // and continues for 0x1000 bytes").
+ // Concatenate the resulting server response packets together and
+ // return in response_string. If any packet fails, the return value
+ // indicates that failure and the returned string value is undefined.
+ PacketResult
+ SendPacketsAndConcatenateResponses(const char *send_payload_prefix,
+ std::string &response_string);
+
+ bool GetThreadSuffixSupported();
+
+ // This packet is usually sent first and the boolean return value
+ // indicates if the packet was send and any response was received
+ // even in the response is UNIMPLEMENTED. If the packet failed to
+ // get a response, then false is returned. This quickly tells us
+ // if we were able to connect and communicate with the remote GDB
+ // server
+ bool QueryNoAckModeSupported();
+
+ void GetListThreadsInStopReplySupported();
+
+ lldb::pid_t GetCurrentProcessID(bool allow_lazy = true);
+
+ bool GetLaunchSuccess(std::string &error_str);
+
+ bool LaunchGDBServer(const char *remote_accept_hostname, lldb::pid_t &pid,
+ uint16_t &port, std::string &socket_name);
+
+ size_t QueryGDBServer(
+ std::vector<std::pair<uint16_t, std::string>> &connection_urls);
+
+ bool KillSpawnedProcess(lldb::pid_t pid);
+
+ /// Sends a GDB remote protocol 'A' packet that delivers program
+ /// arguments to the remote server.
+ ///
+ /// \param[in] argv
+ /// A NULL terminated array of const C strings to use as the
+ /// arguments.
+ ///
+ /// \return
+ /// Zero if the response was "OK", a positive value if the
+ /// the response was "Exx" where xx are two hex digits, or
+ /// -1 if the call is unsupported or any other unexpected
+ /// response was received.
+ int SendArgumentsPacket(const ProcessLaunchInfo &launch_info);
+
+ /// Sends a "QEnvironment:NAME=VALUE" packet that will build up the
+ /// environment that will get used when launching an application
+ /// in conjunction with the 'A' packet. This function can be called
+ /// multiple times in a row in order to pass on the desired
+ /// environment that the inferior should be launched with.
+ ///
+ /// \param[in] name_equal_value
+ /// A NULL terminated C string that contains a single environment
+ /// in the format "NAME=VALUE".
+ ///
+ /// \return
+ /// Zero if the response was "OK", a positive value if the
+ /// the response was "Exx" where xx are two hex digits, or
+ /// -1 if the call is unsupported or any other unexpected
+ /// response was received.
+ int SendEnvironmentPacket(char const *name_equal_value);
+ int SendEnvironment(const Environment &env);
+
+ int SendLaunchArchPacket(const char *arch);
+
+ int SendLaunchEventDataPacket(const char *data,
+ bool *was_supported = nullptr);
+
+ /// Sends a "vAttach:PID" where PID is in hex.
+ ///
+ /// \param[in] pid
+ /// A process ID for the remote gdb server to attach to.
+ ///
+ /// \param[out] response
+ /// The response received from the gdb server. If the return
+ /// value is zero, \a response will contain a stop reply
+ /// packet.
+ ///
+ /// \return
+ /// Zero if the attach was successful, or an error indicating
+ /// an error code.
+ int SendAttach(lldb::pid_t pid, StringExtractorGDBRemote &response);
+
+ /// Sends a GDB remote protocol 'I' packet that delivers stdin
+ /// data to the remote process.
+ ///
+ /// \param[in] data
+ /// A pointer to stdin data.
+ ///
+ /// \param[in] data_len
+ /// The number of bytes available at \a data.
+ ///
+ /// \return
+ /// Zero if the attach was successful, or an error indicating
+ /// an error code.
+ int SendStdinNotification(const char *data, size_t data_len);
+
+ /// Sets the path to use for stdin/out/err for a process
+ /// that will be launched with the 'A' packet.
+ ///
+ /// \param[in] path
+ /// The path to use for stdin/out/err
+ ///
+ /// \return
+ /// Zero if the for success, or an error code for failure.
+ int SetSTDIN(const FileSpec &file_spec);
+ int SetSTDOUT(const FileSpec &file_spec);
+ int SetSTDERR(const FileSpec &file_spec);
+
+ /// Sets the disable ASLR flag to \a enable for a process that will
+ /// be launched with the 'A' packet.
+ ///
+ /// \param[in] enable
+ /// A boolean value indicating whether to disable ASLR or not.
+ ///
+ /// \return
+ /// Zero if the for success, or an error code for failure.
+ int SetDisableASLR(bool enable);
+
+ /// Sets the DetachOnError flag to \a enable for the process controlled by the
+ /// stub.
+ ///
+ /// \param[in] enable
+ /// A boolean value indicating whether to detach on error or not.
+ ///
+ /// \return
+ /// Zero if the for success, or an error code for failure.
+ int SetDetachOnError(bool enable);
+
+ /// Sets the working directory to \a path for a process that will
+ /// be launched with the 'A' packet for non platform based
+ /// connections. If this packet is sent to a GDB server that
+ /// implements the platform, it will change the current working
+ /// directory for the platform process.
+ ///
+ /// \param[in] working_dir
+ /// The path to a directory to use when launching our process
+ ///
+ /// \return
+ /// Zero if the for success, or an error code for failure.
+ int SetWorkingDir(const FileSpec &working_dir);
+
+ /// Gets the current working directory of a remote platform GDB
+ /// server.
+ ///
+ /// \param[out] working_dir
+ /// The current working directory on the remote platform.
+ ///
+ /// \return
+ /// Boolean for success
+ bool GetWorkingDir(FileSpec &working_dir);
+
+ lldb::addr_t AllocateMemory(size_t size, uint32_t permissions);
+
+ bool DeallocateMemory(lldb::addr_t addr);
+
+ Status Detach(bool keep_stopped);
+
+ Status GetMemoryRegionInfo(lldb::addr_t addr, MemoryRegionInfo &range_info);
+
+ Status GetWatchpointSupportInfo(uint32_t &num);
+
+ Status GetWatchpointSupportInfo(uint32_t &num, bool &after,
+ const ArchSpec &arch);
+
+ Status GetWatchpointsTriggerAfterInstruction(bool &after,
+ const ArchSpec &arch);
+
+ const ArchSpec &GetHostArchitecture();
+
+ std::chrono::seconds GetHostDefaultPacketTimeout();
+
+ const ArchSpec &GetProcessArchitecture();
+
+ void GetRemoteQSupported();
+
+ bool GetVContSupported(char flavor);
+
+ bool GetpPacketSupported(lldb::tid_t tid);
+
+ bool GetxPacketSupported();
+
+ bool GetVAttachOrWaitSupported();
+
+ bool GetSyncThreadStateSupported();
+
+ void ResetDiscoverableSettings(bool did_exec);
+
+ bool GetHostInfo(bool force = false);
+
+ bool GetDefaultThreadId(lldb::tid_t &tid);
+
+ llvm::VersionTuple GetOSVersion();
+
+ bool GetOSBuildString(std::string &s);
+
+ bool GetOSKernelDescription(std::string &s);
+
+ ArchSpec GetSystemArchitecture();
+
+ bool GetHostname(std::string &s);
+
+ lldb::addr_t GetShlibInfoAddr();
+
+ bool GetSupportsThreadSuffix();
+
+ bool GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info);
+
+ uint32_t FindProcesses(const ProcessInstanceInfoMatch &process_match_info,
+ ProcessInstanceInfoList &process_infos);
+
+ bool GetUserName(uint32_t uid, std::string &name);
+
+ bool GetGroupName(uint32_t gid, std::string &name);
+
+ bool HasFullVContSupport() { return GetVContSupported('A'); }
+
+ bool HasAnyVContSupport() { return GetVContSupported('a'); }
+
+ bool GetStopReply(StringExtractorGDBRemote &response);
+
+ bool GetThreadStopInfo(lldb::tid_t tid, StringExtractorGDBRemote &response);
+
+ bool SupportsGDBStoppointPacket(GDBStoppointType type) {
+ switch (type) {
+ case eBreakpointSoftware:
+ return m_supports_z0;
+ case eBreakpointHardware:
+ return m_supports_z1;
+ case eWatchpointWrite:
+ return m_supports_z2;
+ case eWatchpointRead:
+ return m_supports_z3;
+ case eWatchpointReadWrite:
+ return m_supports_z4;
+ default:
+ return false;
+ }
+ }
+
+ uint8_t SendGDBStoppointTypePacket(
+ GDBStoppointType type, // Type of breakpoint or watchpoint
+ bool insert, // Insert or remove?
+ lldb::addr_t addr, // Address of breakpoint or watchpoint
+ uint32_t length); // Byte Size of breakpoint or watchpoint
+
+ bool SetNonStopMode(const bool enable);
+
+ void TestPacketSpeed(const uint32_t num_packets, uint32_t max_send,
+ uint32_t max_recv, uint64_t recv_amount, bool json,
+ Stream &strm);
+
+ // This packet is for testing the speed of the interface only. Both
+ // the client and server need to support it, but this allows us to
+ // measure the packet speed without any other work being done on the
+ // other end and avoids any of that work affecting the packet send
+ // and response times.
+ bool SendSpeedTestPacket(uint32_t send_size, uint32_t recv_size);
+
+ bool SetCurrentThread(uint64_t tid);
+
+ bool SetCurrentThreadForRun(uint64_t tid);
+
+ bool GetQXferAuxvReadSupported();
+
+ void EnableErrorStringInPacket();
+
+ bool GetQXferLibrariesReadSupported();
+
+ bool GetQXferLibrariesSVR4ReadSupported();
+
+ uint64_t GetRemoteMaxPacketSize();
+
+ bool GetEchoSupported();
+
+ bool GetQPassSignalsSupported();
+
+ bool GetAugmentedLibrariesSVR4ReadSupported();
+
+ bool GetQXferFeaturesReadSupported();
+
+ bool GetQXferMemoryMapReadSupported();
+
+ LazyBool SupportsAllocDeallocMemory() // const
+ {
+ // Uncomment this to have lldb pretend the debug server doesn't respond to
+ // alloc/dealloc memory packets.
+ // m_supports_alloc_dealloc_memory = lldb_private::eLazyBoolNo;
+ return m_supports_alloc_dealloc_memory;
+ }
+
+ size_t GetCurrentThreadIDs(std::vector<lldb::tid_t> &thread_ids,
+ bool &sequence_mutex_unavailable);
+
+ lldb::user_id_t OpenFile(const FileSpec &file_spec, uint32_t flags,
+ mode_t mode, Status &error);
+
+ bool CloseFile(lldb::user_id_t fd, Status &error);
+
+ lldb::user_id_t GetFileSize(const FileSpec &file_spec);
+
+ Status GetFilePermissions(const FileSpec &file_spec,
+ uint32_t &file_permissions);
+
+ Status SetFilePermissions(const FileSpec &file_spec,
+ uint32_t file_permissions);
+
+ uint64_t ReadFile(lldb::user_id_t fd, uint64_t offset, void *dst,
+ uint64_t dst_len, Status &error);
+
+ uint64_t WriteFile(lldb::user_id_t fd, uint64_t offset, const void *src,
+ uint64_t src_len, Status &error);
+
+ Status CreateSymlink(const FileSpec &src, const FileSpec &dst);
+
+ Status Unlink(const FileSpec &file_spec);
+
+ Status MakeDirectory(const FileSpec &file_spec, uint32_t mode);
+
+ bool GetFileExists(const FileSpec &file_spec);
+
+ Status RunShellCommand(
+ const char *command, // Shouldn't be nullptr
+ const FileSpec &working_dir, // Pass empty FileSpec to use the current
+ // working directory
+ int *status_ptr, // Pass nullptr if you don't want the process exit status
+ int *signo_ptr, // Pass nullptr if you don't want the signal that caused
+ // the process to exit
+ std::string
+ *command_output, // Pass nullptr if you don't want the command output
+ const Timeout<std::micro> &timeout);
+
+ bool CalculateMD5(const FileSpec &file_spec, uint64_t &high, uint64_t &low);
+
+ lldb::DataBufferSP ReadRegister(
+ lldb::tid_t tid,
+ uint32_t
+ reg_num); // Must be the eRegisterKindProcessPlugin register number
+
+ lldb::DataBufferSP ReadAllRegisters(lldb::tid_t tid);
+
+ bool
+ WriteRegister(lldb::tid_t tid,
+ uint32_t reg_num, // eRegisterKindProcessPlugin register number
+ llvm::ArrayRef<uint8_t> data);
+
+ bool WriteAllRegisters(lldb::tid_t tid, llvm::ArrayRef<uint8_t> data);
+
+ bool SaveRegisterState(lldb::tid_t tid, uint32_t &save_id);
+
+ bool RestoreRegisterState(lldb::tid_t tid, uint32_t save_id);
+
+ bool SyncThreadState(lldb::tid_t tid);
+
+ const char *GetGDBServerProgramName();
+
+ uint32_t GetGDBServerProgramVersion();
+
+ bool AvoidGPackets(ProcessGDBRemote *process);
+
+ StructuredData::ObjectSP GetThreadsInfo();
+
+ bool GetThreadExtendedInfoSupported();
+
+ bool GetLoadedDynamicLibrariesInfosSupported();
+
+ bool GetSharedCacheInfoSupported();
+
+ bool GetModuleInfo(const FileSpec &module_file_spec,
+ const ArchSpec &arch_spec, ModuleSpec &module_spec);
+
+ llvm::Optional<std::vector<ModuleSpec>>
+ GetModulesInfo(llvm::ArrayRef<FileSpec> module_file_specs,
+ const llvm::Triple &triple);
+
+ bool ReadExtFeature(const lldb_private::ConstString object,
+ const lldb_private::ConstString annex, std::string &out,
+ lldb_private::Status &err);
+
+ void ServeSymbolLookups(lldb_private::Process *process);
+
+ // Sends QPassSignals packet to the server with given signals to ignore.
+ Status SendSignalsToIgnore(llvm::ArrayRef<int32_t> signals);
+
+ /// Return the feature set supported by the gdb-remote server.
+ ///
+ /// This method returns the remote side's response to the qSupported
+ /// packet. The response is the complete string payload returned
+ /// to the client.
+ ///
+ /// \return
+ /// The string returned by the server to the qSupported query.
+ const std::string &GetServerSupportedFeatures() const {
+ return m_qSupported_response;
+ }
+
+ /// Return the array of async JSON packet types supported by the remote.
+ ///
+ /// This method returns the remote side's array of supported JSON
+ /// packet types as a list of type names. Each of the results are
+ /// expected to have an Enable{type_name} command to enable and configure
+ /// the related feature. Each type_name for an enabled feature will
+ /// possibly send async-style packets that contain a payload of a
+ /// binhex-encoded JSON dictionary. The dictionary will have a
+ /// string field named 'type', that contains the type_name of the
+ /// supported packet type.
+ ///
+ /// There is a Plugin category called structured-data plugins.
+ /// A plugin indicates whether it knows how to handle a type_name.
+ /// If so, it can be used to process the async JSON packet.
+ ///
+ /// \return
+ /// The string returned by the server to the qSupported query.
+ lldb_private::StructuredData::Array *GetSupportedStructuredDataPlugins();
+
+ /// Configure a StructuredData feature on the remote end.
+ ///
+ /// \see \b Process::ConfigureStructuredData(...) for details.
+ Status
+ ConfigureRemoteStructuredData(ConstString type_name,
+ const StructuredData::ObjectSP &config_sp);
+
+ lldb::user_id_t SendStartTracePacket(const TraceOptions &options,
+ Status &error);
+
+ Status SendStopTracePacket(lldb::user_id_t uid, lldb::tid_t thread_id);
+
+ Status SendGetDataPacket(lldb::user_id_t uid, lldb::tid_t thread_id,
+ llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset = 0);
+
+ Status SendGetMetaDataPacket(lldb::user_id_t uid, lldb::tid_t thread_id,
+ llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset = 0);
+
+ Status SendGetTraceConfigPacket(lldb::user_id_t uid, TraceOptions &options);
+
+protected:
+ LazyBool m_supports_not_sending_acks;
+ LazyBool m_supports_thread_suffix;
+ LazyBool m_supports_threads_in_stop_reply;
+ LazyBool m_supports_vCont_all;
+ LazyBool m_supports_vCont_any;
+ LazyBool m_supports_vCont_c;
+ LazyBool m_supports_vCont_C;
+ LazyBool m_supports_vCont_s;
+ LazyBool m_supports_vCont_S;
+ LazyBool m_qHostInfo_is_valid;
+ LazyBool m_curr_pid_is_valid;
+ LazyBool m_qProcessInfo_is_valid;
+ LazyBool m_qGDBServerVersion_is_valid;
+ LazyBool m_supports_alloc_dealloc_memory;
+ LazyBool m_supports_memory_region_info;
+ LazyBool m_supports_watchpoint_support_info;
+ LazyBool m_supports_detach_stay_stopped;
+ LazyBool m_watchpoints_trigger_after_instruction;
+ LazyBool m_attach_or_wait_reply;
+ LazyBool m_prepare_for_reg_writing_reply;
+ LazyBool m_supports_p;
+ LazyBool m_supports_x;
+ LazyBool m_avoid_g_packets;
+ LazyBool m_supports_QSaveRegisterState;
+ LazyBool m_supports_qXfer_auxv_read;
+ LazyBool m_supports_qXfer_libraries_read;
+ LazyBool m_supports_qXfer_libraries_svr4_read;
+ LazyBool m_supports_qXfer_features_read;
+ LazyBool m_supports_qXfer_memory_map_read;
+ LazyBool m_supports_augmented_libraries_svr4_read;
+ LazyBool m_supports_jThreadExtendedInfo;
+ LazyBool m_supports_jLoadedDynamicLibrariesInfos;
+ LazyBool m_supports_jGetSharedCacheInfo;
+ LazyBool m_supports_QPassSignals;
+ LazyBool m_supports_error_string_reply;
+
+ bool m_supports_qProcessInfoPID : 1, m_supports_qfProcessInfo : 1,
+ m_supports_qUserName : 1, m_supports_qGroupName : 1,
+ m_supports_qThreadStopInfo : 1, m_supports_z0 : 1, m_supports_z1 : 1,
+ m_supports_z2 : 1, m_supports_z3 : 1, m_supports_z4 : 1,
+ m_supports_QEnvironment : 1, m_supports_QEnvironmentHexEncoded : 1,
+ m_supports_qSymbol : 1, m_qSymbol_requests_done : 1,
+ m_supports_qModuleInfo : 1, m_supports_jThreadsInfo : 1,
+ m_supports_jModulesInfo : 1;
+
+ lldb::pid_t m_curr_pid;
+ lldb::tid_t m_curr_tid; // Current gdb remote protocol thread index for all
+ // other operations
+ lldb::tid_t m_curr_tid_run; // Current gdb remote protocol thread index for
+ // continue, step, etc
+
+ uint32_t m_num_supported_hardware_watchpoints;
+
+ ArchSpec m_host_arch;
+ ArchSpec m_process_arch;
+ llvm::VersionTuple m_os_version;
+ std::string m_os_build;
+ std::string m_os_kernel;
+ std::string m_hostname;
+ std::string m_gdb_server_name; // from reply to qGDBServerVersion, empty if
+ // qGDBServerVersion is not supported
+ uint32_t m_gdb_server_version; // from reply to qGDBServerVersion, zero if
+ // qGDBServerVersion is not supported
+ std::chrono::seconds m_default_packet_timeout;
+ uint64_t m_max_packet_size; // as returned by qSupported
+ std::string m_qSupported_response; // the complete response to qSupported
+
+ bool m_supported_async_json_packets_is_valid;
+ lldb_private::StructuredData::ObjectSP m_supported_async_json_packets_sp;
+
+ std::vector<MemoryRegionInfo> m_qXfer_memory_map;
+ bool m_qXfer_memory_map_loaded;
+
+ bool GetCurrentProcessInfo(bool allow_lazy_pid = true);
+
+ bool GetGDBServerVersion();
+
+ // Given the list of compression types that the remote debug stub can support,
+ // possibly enable compression if we find an encoding we can handle.
+ void MaybeEnableCompression(std::vector<std::string> supported_compressions);
+
+ bool DecodeProcessInfoResponse(StringExtractorGDBRemote &response,
+ ProcessInstanceInfo &process_info);
+
+ void OnRunPacketSent(bool first) override;
+
+ PacketResult SendThreadSpecificPacketAndWaitForResponse(
+ lldb::tid_t tid, StreamString &&payload,
+ StringExtractorGDBRemote &response, bool send_async);
+
+ Status SendGetTraceDataPacket(StreamGDBRemote &packet, lldb::user_id_t uid,
+ lldb::tid_t thread_id,
+ llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset);
+
+ Status LoadQXferMemoryMap();
+
+ Status GetQXferMemoryMapRegionInfo(lldb::addr_t addr,
+ MemoryRegionInfo &region);
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationClient);
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+#endif // liblldb_GDBRemoteCommunicationClient_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp
new file mode 100644
index 000000000000..bcddb4faf863
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp
@@ -0,0 +1,142 @@
+//===-- GDBRemoteCommunicationHistory.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 "GDBRemoteCommunicationHistory.h"
+
+// Other libraries and framework includes
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+
+using namespace llvm;
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+void GDBRemoteCommunicationHistory::Entry::Serialize(raw_ostream &strm) const {
+ yaml::Output yout(strm);
+ yout << const_cast<GDBRemoteCommunicationHistory::Entry &>(*this);
+ strm.flush();
+}
+
+GDBRemoteCommunicationHistory::GDBRemoteCommunicationHistory(uint32_t size)
+ : m_packets(), m_curr_idx(0), m_total_packet_count(0),
+ m_dumped_to_log(false) {
+ if (size)
+ m_packets.resize(size);
+}
+
+GDBRemoteCommunicationHistory::~GDBRemoteCommunicationHistory() {}
+
+void GDBRemoteCommunicationHistory::AddPacket(char packet_char, PacketType type,
+ uint32_t bytes_transmitted) {
+ const size_t size = m_packets.size();
+ if (size == 0)
+ return;
+
+ const uint32_t idx = GetNextIndex();
+ m_packets[idx].packet.data.assign(1, packet_char);
+ m_packets[idx].type = type;
+ m_packets[idx].bytes_transmitted = bytes_transmitted;
+ m_packets[idx].packet_idx = m_total_packet_count;
+ m_packets[idx].tid = llvm::get_threadid();
+ if (m_stream)
+ m_packets[idx].Serialize(*m_stream);
+}
+
+void GDBRemoteCommunicationHistory::AddPacket(const std::string &src,
+ uint32_t src_len, PacketType type,
+ uint32_t bytes_transmitted) {
+ const size_t size = m_packets.size();
+ if (size == 0)
+ return;
+
+ const uint32_t idx = GetNextIndex();
+ m_packets[idx].packet.data.assign(src, 0, src_len);
+ m_packets[idx].type = type;
+ m_packets[idx].bytes_transmitted = bytes_transmitted;
+ m_packets[idx].packet_idx = m_total_packet_count;
+ m_packets[idx].tid = llvm::get_threadid();
+ if (m_stream)
+ m_packets[idx].Serialize(*m_stream);
+}
+
+void GDBRemoteCommunicationHistory::Dump(Stream &strm) const {
+ const uint32_t size = GetNumPacketsInHistory();
+ const uint32_t first_idx = GetFirstSavedPacketIndex();
+ const uint32_t stop_idx = m_curr_idx + size;
+ for (uint32_t i = first_idx; i < stop_idx; ++i) {
+ const uint32_t idx = NormalizeIndex(i);
+ const Entry &entry = m_packets[idx];
+ if (entry.type == ePacketTypeInvalid || entry.packet.data.empty())
+ break;
+ strm.Printf("history[%u] tid=0x%4.4" PRIx64 " <%4u> %s packet: %s\n",
+ entry.packet_idx, entry.tid, entry.bytes_transmitted,
+ (entry.type == ePacketTypeSend) ? "send" : "read",
+ entry.packet.data.c_str());
+ }
+}
+
+void GDBRemoteCommunicationHistory::Dump(Log *log) const {
+ if (!log || m_dumped_to_log)
+ return;
+
+ m_dumped_to_log = true;
+ const uint32_t size = GetNumPacketsInHistory();
+ const uint32_t first_idx = GetFirstSavedPacketIndex();
+ const uint32_t stop_idx = m_curr_idx + size;
+ for (uint32_t i = first_idx; i < stop_idx; ++i) {
+ const uint32_t idx = NormalizeIndex(i);
+ const Entry &entry = m_packets[idx];
+ if (entry.type == ePacketTypeInvalid || entry.packet.data.empty())
+ break;
+ log->Printf("history[%u] tid=0x%4.4" PRIx64 " <%4u> %s packet: %s",
+ entry.packet_idx, entry.tid, entry.bytes_transmitted,
+ (entry.type == ePacketTypeSend) ? "send" : "read",
+ entry.packet.data.c_str());
+ }
+}
+
+void yaml::ScalarEnumerationTraits<GDBRemoteCommunicationHistory::PacketType>::
+ enumeration(IO &io, GDBRemoteCommunicationHistory::PacketType &value) {
+ io.enumCase(value, "Invalid",
+ GDBRemoteCommunicationHistory::ePacketTypeInvalid);
+ io.enumCase(value, "Send", GDBRemoteCommunicationHistory::ePacketTypeSend);
+ io.enumCase(value, "Recv", GDBRemoteCommunicationHistory::ePacketTypeRecv);
+}
+
+void yaml::ScalarTraits<GDBRemoteCommunicationHistory::Entry::BinaryData>::
+ output(const GDBRemoteCommunicationHistory::Entry::BinaryData &Val, void *,
+ raw_ostream &Out) {
+ Out << toHex(Val.data);
+}
+
+StringRef
+yaml::ScalarTraits<GDBRemoteCommunicationHistory::Entry::BinaryData>::input(
+ StringRef Scalar, void *,
+ GDBRemoteCommunicationHistory::Entry::BinaryData &Val) {
+ Val.data = fromHex(Scalar);
+ return {};
+}
+
+void yaml::MappingTraits<GDBRemoteCommunicationHistory::Entry>::mapping(
+ IO &io, GDBRemoteCommunicationHistory::Entry &Entry) {
+ io.mapRequired("packet", Entry.packet);
+ io.mapRequired("type", Entry.type);
+ io.mapRequired("bytes", Entry.bytes_transmitted);
+ io.mapRequired("index", Entry.packet_idx);
+ io.mapRequired("tid", Entry.tid);
+}
+
+StringRef yaml::MappingTraits<GDBRemoteCommunicationHistory::Entry>::validate(
+ IO &io, GDBRemoteCommunicationHistory::Entry &Entry) {
+ if (Entry.bytes_transmitted != Entry.packet.data.size())
+ return "BinaryData size doesn't match bytes transmitted";
+
+ return {};
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h
new file mode 100644
index 000000000000..85f112b50623
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h
@@ -0,0 +1,155 @@
+//===-- GDBRemoteCommunicationHistory.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 liblldb_GDBRemoteCommunicationHistory_h_
+#define liblldb_GDBRemoteCommunicationHistory_h_
+
+#include <string>
+#include <vector>
+
+#include "lldb/lldb-public.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace lldb_private {
+namespace process_gdb_remote {
+
+/// The history keeps a circular buffer of GDB remote packets. The history is
+/// used for logging and replaying GDB remote packets.
+class GDBRemoteCommunicationHistory {
+public:
+ friend llvm::yaml::MappingTraits<GDBRemoteCommunicationHistory>;
+
+ enum PacketType { ePacketTypeInvalid = 0, ePacketTypeSend, ePacketTypeRecv };
+
+ /// Entry in the ring buffer containing the packet data, its type, size and
+ /// index. Entries can be serialized to file.
+ struct Entry {
+ Entry()
+ : packet(), type(ePacketTypeInvalid), bytes_transmitted(0),
+ packet_idx(0), tid(LLDB_INVALID_THREAD_ID) {}
+
+ void Clear() {
+ packet.data.clear();
+ type = ePacketTypeInvalid;
+ bytes_transmitted = 0;
+ packet_idx = 0;
+ tid = LLDB_INVALID_THREAD_ID;
+ }
+
+ struct BinaryData {
+ std::string data;
+ };
+
+ void Serialize(llvm::raw_ostream &strm) const;
+
+ BinaryData packet;
+ PacketType type;
+ uint32_t bytes_transmitted;
+ uint32_t packet_idx;
+ lldb::tid_t tid;
+ };
+
+ GDBRemoteCommunicationHistory(uint32_t size = 0);
+
+ ~GDBRemoteCommunicationHistory();
+
+ // For single char packets for ack, nack and /x03
+ void AddPacket(char packet_char, PacketType type, uint32_t bytes_transmitted);
+
+ void AddPacket(const std::string &src, uint32_t src_len, PacketType type,
+ uint32_t bytes_transmitted);
+
+ void Dump(Stream &strm) const;
+ void Dump(Log *log) const;
+ bool DidDumpToLog() const { return m_dumped_to_log; }
+
+ void SetStream(llvm::raw_ostream *strm) { m_stream = strm; }
+
+private:
+ uint32_t GetFirstSavedPacketIndex() const {
+ if (m_total_packet_count < m_packets.size())
+ return 0;
+ else
+ return m_curr_idx + 1;
+ }
+
+ uint32_t GetNumPacketsInHistory() const {
+ if (m_total_packet_count < m_packets.size())
+ return m_total_packet_count;
+ else
+ return (uint32_t)m_packets.size();
+ }
+
+ uint32_t GetNextIndex() {
+ ++m_total_packet_count;
+ const uint32_t idx = m_curr_idx;
+ m_curr_idx = NormalizeIndex(idx + 1);
+ return idx;
+ }
+
+ uint32_t NormalizeIndex(uint32_t i) const {
+ return m_packets.empty() ? 0 : i % m_packets.size();
+ }
+
+ std::vector<Entry> m_packets;
+ uint32_t m_curr_idx;
+ uint32_t m_total_packet_count;
+ mutable bool m_dumped_to_log;
+ llvm::raw_ostream *m_stream = nullptr;
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(
+ lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry)
+
+namespace llvm {
+namespace yaml {
+
+template <>
+struct ScalarEnumerationTraits<lldb_private::process_gdb_remote::
+ GDBRemoteCommunicationHistory::PacketType> {
+ static void enumeration(IO &io,
+ lldb_private::process_gdb_remote::
+ GDBRemoteCommunicationHistory::PacketType &value);
+};
+
+template <>
+struct ScalarTraits<lldb_private::process_gdb_remote::
+ GDBRemoteCommunicationHistory::Entry::BinaryData> {
+ static void output(const lldb_private::process_gdb_remote::
+ GDBRemoteCommunicationHistory::Entry::BinaryData &,
+ void *, raw_ostream &);
+
+ static StringRef
+ input(StringRef, void *,
+ lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry::
+ BinaryData &);
+
+ static QuotingType mustQuote(StringRef S) { return QuotingType::None; }
+};
+
+template <>
+struct MappingTraits<
+ lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry> {
+ static void
+ mapping(IO &io,
+ lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry
+ &Entry);
+
+ static StringRef validate(
+ IO &io,
+ lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry &);
+};
+
+} // namespace yaml
+} // namespace llvm
+
+#endif // liblldb_GDBRemoteCommunicationHistory_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp
new file mode 100644
index 000000000000..417f5737a30f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp
@@ -0,0 +1,282 @@
+//===-- GDBRemoteCommunicationReplayServer.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 "lldb/Host/Config.h"
+
+#include "GDBRemoteCommunicationReplayServer.h"
+#include "ProcessGDBRemoteLog.h"
+
+// C Includes
+// C++ Includes
+#include <cstring>
+
+// Project includes
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Event.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+
+using namespace llvm;
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+/// Check if the given expected packet matches the actual packet.
+static bool unexpected(llvm::StringRef expected, llvm::StringRef actual) {
+ // The 'expected' string contains the raw data, including the leading $ and
+ // trailing checksum. The 'actual' string contains only the packet's content.
+ if (expected.contains(actual))
+ return false;
+ // Contains a PID which might be different.
+ if (expected.contains("vAttach"))
+ return false;
+ // Contains a ascii-hex-path.
+ if (expected.contains("QSetSTD"))
+ return false;
+ // Contains environment values.
+ if (expected.contains("QEnvironment"))
+ return false;
+
+ return true;
+}
+
+/// Check if we should reply to the given packet.
+static bool skip(llvm::StringRef data) {
+ assert(!data.empty() && "Empty packet?");
+
+ // We've already acknowledge the '+' packet so we're done here.
+ if (data == "+")
+ return true;
+
+ /// Don't 't reply to ^C. We need this because of stop reply packets, which
+ /// are only returned when the target halts. Reproducers synchronize these
+ /// 'asynchronous' replies, by recording them as a regular replies to the
+ /// previous packet (e.g. vCont). As a result, we should ignore real
+ /// asynchronous requests.
+ if (data.data()[0] == 0x03)
+ return true;
+
+ return false;
+}
+
+GDBRemoteCommunicationReplayServer::GDBRemoteCommunicationReplayServer()
+ : GDBRemoteCommunication("gdb-replay", "gdb-replay.rx_packet"),
+ m_async_broadcaster(nullptr, "lldb.gdb-replay.async-broadcaster"),
+ m_async_listener_sp(
+ Listener::MakeListener("lldb.gdb-replay.async-listener")),
+ m_async_thread_state_mutex(), m_skip_acks(false) {
+ m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue,
+ "async thread continue");
+ m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit,
+ "async thread should exit");
+
+ const uint32_t async_event_mask =
+ eBroadcastBitAsyncContinue | eBroadcastBitAsyncThreadShouldExit;
+ m_async_listener_sp->StartListeningForEvents(&m_async_broadcaster,
+ async_event_mask);
+}
+
+GDBRemoteCommunicationReplayServer::~GDBRemoteCommunicationReplayServer() {
+ StopAsyncThread();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationReplayServer::GetPacketAndSendResponse(
+ Timeout<std::micro> timeout, Status &error, bool &interrupt, bool &quit) {
+ std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex);
+
+ StringExtractorGDBRemote packet;
+ PacketResult packet_result = WaitForPacketNoLock(packet, timeout, false);
+
+ if (packet_result != PacketResult::Success) {
+ if (!IsConnected()) {
+ error.SetErrorString("lost connection");
+ quit = true;
+ } else {
+ error.SetErrorString("timeout");
+ }
+ return packet_result;
+ }
+
+ m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue);
+
+ // Check if we should reply to this packet.
+ if (skip(packet.GetStringRef()))
+ return PacketResult::Success;
+
+ // This completes the handshake. Since m_send_acks was true, we can unset it
+ // already.
+ if (packet.GetStringRef() == "QStartNoAckMode")
+ m_send_acks = false;
+
+ // A QEnvironment packet is sent for every environment variable. If the
+ // number of environment variables is different during replay, the replies
+ // become out of sync.
+ if (packet.GetStringRef().find("QEnvironment") == 0)
+ return SendRawPacketNoLock("$OK#9a");
+
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ while (!m_packet_history.empty()) {
+ // Pop last packet from the history.
+ GDBRemoteCommunicationHistory::Entry entry = m_packet_history.back();
+ m_packet_history.pop_back();
+
+ // We've handled the handshake implicitly before. Skip the packet and move
+ // on.
+ if (entry.packet.data == "+")
+ continue;
+
+ if (entry.type == GDBRemoteCommunicationHistory::ePacketTypeSend) {
+ if (unexpected(entry.packet.data, packet.GetStringRef())) {
+ LLDB_LOG(log,
+ "GDBRemoteCommunicationReplayServer expected packet: '{0}'",
+ entry.packet.data);
+ LLDB_LOG(log, "GDBRemoteCommunicationReplayServer actual packet: '{0}'",
+ packet.GetStringRef());
+ assert(false && "Encountered unexpected packet during replay");
+ return PacketResult::ErrorSendFailed;
+ }
+
+ // Ignore QEnvironment packets as they're handled earlier.
+ if (entry.packet.data.find("QEnvironment") == 1) {
+ assert(m_packet_history.back().type ==
+ GDBRemoteCommunicationHistory::ePacketTypeRecv);
+ m_packet_history.pop_back();
+ }
+
+ continue;
+ }
+
+ if (entry.type == GDBRemoteCommunicationHistory::ePacketTypeInvalid) {
+ LLDB_LOG(
+ log,
+ "GDBRemoteCommunicationReplayServer skipped invalid packet: '{0}'",
+ packet.GetStringRef());
+ continue;
+ }
+
+ LLDB_LOG(log,
+ "GDBRemoteCommunicationReplayServer replied to '{0}' with '{1}'",
+ packet.GetStringRef(), entry.packet.data);
+ return SendRawPacketNoLock(entry.packet.data);
+ }
+
+ quit = true;
+
+ return packet_result;
+}
+
+LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(
+ std::vector<
+ lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry>)
+
+llvm::Error
+GDBRemoteCommunicationReplayServer::LoadReplayHistory(const FileSpec &path) {
+ auto error_or_file = MemoryBuffer::getFile(path.GetPath());
+ if (auto err = error_or_file.getError())
+ return errorCodeToError(err);
+
+ yaml::Input yin((*error_or_file)->getBuffer());
+ yin >> m_packet_history;
+
+ if (auto err = yin.error())
+ return errorCodeToError(err);
+
+ // We want to manipulate the vector like a stack so we need to reverse the
+ // order of the packets to have the oldest on at the back.
+ std::reverse(m_packet_history.begin(), m_packet_history.end());
+
+ return Error::success();
+}
+
+bool GDBRemoteCommunicationReplayServer::StartAsyncThread() {
+ std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex);
+ if (!m_async_thread.IsJoinable()) {
+ // Create a thread that watches our internal state and controls which
+ // events make it to clients (into the DCProcess event queue).
+ llvm::Expected<HostThread> async_thread = ThreadLauncher::LaunchThread(
+ "<lldb.gdb-replay.async>",
+ GDBRemoteCommunicationReplayServer::AsyncThread, this);
+ if (!async_thread) {
+ LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST),
+ "failed to launch host thread: {}",
+ llvm::toString(async_thread.takeError()));
+ return false;
+ }
+ m_async_thread = *async_thread;
+ }
+
+ // Wait for handshake.
+ m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue);
+
+ return m_async_thread.IsJoinable();
+}
+
+void GDBRemoteCommunicationReplayServer::StopAsyncThread() {
+ std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex);
+
+ if (!m_async_thread.IsJoinable())
+ return;
+
+ // Request thread to stop.
+ m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncThreadShouldExit);
+
+ // Disconnect client.
+ Disconnect();
+
+ // Stop the thread.
+ m_async_thread.Join(nullptr);
+ m_async_thread.Reset();
+}
+
+void GDBRemoteCommunicationReplayServer::ReceivePacket(
+ GDBRemoteCommunicationReplayServer &server, bool &done) {
+ Status error;
+ bool interrupt;
+ auto packet_result = server.GetPacketAndSendResponse(std::chrono::seconds(1),
+ error, interrupt, done);
+ if (packet_result != GDBRemoteCommunication::PacketResult::Success &&
+ packet_result !=
+ GDBRemoteCommunication::PacketResult::ErrorReplyTimeout) {
+ done = true;
+ } else {
+ server.m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue);
+ }
+}
+
+thread_result_t GDBRemoteCommunicationReplayServer::AsyncThread(void *arg) {
+ GDBRemoteCommunicationReplayServer *server =
+ (GDBRemoteCommunicationReplayServer *)arg;
+
+ EventSP event_sp;
+ bool done = false;
+
+ while (true) {
+ if (server->m_async_listener_sp->GetEvent(event_sp, llvm::None)) {
+ const uint32_t event_type = event_sp->GetType();
+ if (event_sp->BroadcasterIs(&server->m_async_broadcaster)) {
+ switch (event_type) {
+ case eBroadcastBitAsyncContinue:
+ ReceivePacket(*server, done);
+ if (done)
+ return {};
+ break;
+ case eBroadcastBitAsyncThreadShouldExit:
+ default:
+ return {};
+ }
+ }
+ }
+ }
+
+ return {};
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.h
new file mode 100644
index 000000000000..26d65e265463
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.h
@@ -0,0 +1,82 @@
+//===-- GDBRemoteCommunicationReplayServer.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 liblldb_GDBRemoteCommunicationReplayServer_h_
+#define liblldb_GDBRemoteCommunicationReplayServer_h_
+
+// Other libraries and framework includes
+#include "GDBRemoteCommunication.h"
+#include "GDBRemoteCommunicationHistory.h"
+
+// Project includes
+#include "lldb/Host/HostThread.h"
+#include "lldb/Utility/Broadcaster.h"
+#include "lldb/lldb-private-forward.h"
+#include "llvm/Support/Error.h"
+
+// C Includes
+// C++ Includes
+#include <functional>
+#include <map>
+#include <thread>
+
+class StringExtractorGDBRemote;
+
+namespace lldb_private {
+namespace process_gdb_remote {
+
+class ProcessGDBRemote;
+
+/// Dummy GDB server that replays packets from the GDB Remote Communication
+/// history. This is used to replay GDB packets.
+class GDBRemoteCommunicationReplayServer : public GDBRemoteCommunication {
+public:
+ GDBRemoteCommunicationReplayServer();
+
+ ~GDBRemoteCommunicationReplayServer() override;
+
+ PacketResult GetPacketAndSendResponse(Timeout<std::micro> timeout,
+ Status &error, bool &interrupt,
+ bool &quit);
+
+ bool HandshakeWithClient() { return GetAck() == PacketResult::Success; }
+
+ llvm::Error LoadReplayHistory(const FileSpec &path);
+
+ bool StartAsyncThread();
+ void StopAsyncThread();
+
+protected:
+ enum {
+ eBroadcastBitAsyncContinue = (1 << 0),
+ eBroadcastBitAsyncThreadShouldExit = (1 << 1),
+ };
+
+ static void ReceivePacket(GDBRemoteCommunicationReplayServer &server,
+ bool &done);
+ static lldb::thread_result_t AsyncThread(void *arg);
+
+ /// Replay history with the oldest packet at the end.
+ std::vector<GDBRemoteCommunicationHistory::Entry> m_packet_history;
+
+ /// Server thread.
+ Broadcaster m_async_broadcaster;
+ lldb::ListenerSP m_async_listener_sp;
+ HostThread m_async_thread;
+ std::recursive_mutex m_async_thread_state_mutex;
+
+ bool m_skip_acks;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationReplayServer);
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+#endif // liblldb_GDBRemoteCommunicationReplayServer_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp
new file mode 100644
index 000000000000..49cbeb023fd5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp
@@ -0,0 +1,158 @@
+//===-- GDBRemoteCommunicationServer.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 "lldb/Host/Config.h"
+
+#include "GDBRemoteCommunicationServer.h"
+
+#include <cstring>
+
+#include "ProcessGDBRemoteLog.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+GDBRemoteCommunicationServer::GDBRemoteCommunicationServer(
+ const char *comm_name, const char *listener_name)
+ : GDBRemoteCommunication(comm_name, listener_name), m_exit_now(false) {
+ RegisterPacketHandler(
+ StringExtractorGDBRemote::eServerPacketType_QEnableErrorStrings,
+ [this](StringExtractorGDBRemote packet, Status &error, bool &interrupt,
+ bool &quit) { return this->Handle_QErrorStringEnable(packet); });
+}
+
+GDBRemoteCommunicationServer::~GDBRemoteCommunicationServer() {}
+
+void GDBRemoteCommunicationServer::RegisterPacketHandler(
+ StringExtractorGDBRemote::ServerPacketType packet_type,
+ PacketHandler handler) {
+ m_packet_handlers[packet_type] = std::move(handler);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServer::GetPacketAndSendResponse(
+ Timeout<std::micro> timeout, Status &error, bool &interrupt, bool &quit) {
+ StringExtractorGDBRemote packet;
+
+ PacketResult packet_result = WaitForPacketNoLock(packet, timeout, false);
+ if (packet_result == PacketResult::Success) {
+ const StringExtractorGDBRemote::ServerPacketType packet_type =
+ packet.GetServerPacketType();
+ switch (packet_type) {
+ case StringExtractorGDBRemote::eServerPacketType_nack:
+ case StringExtractorGDBRemote::eServerPacketType_ack:
+ break;
+
+ case StringExtractorGDBRemote::eServerPacketType_invalid:
+ error.SetErrorString("invalid packet");
+ quit = true;
+ break;
+
+ case StringExtractorGDBRemote::eServerPacketType_unimplemented:
+ packet_result = SendUnimplementedResponse(packet.GetStringRef().c_str());
+ break;
+
+ default:
+ auto handler_it = m_packet_handlers.find(packet_type);
+ if (handler_it == m_packet_handlers.end())
+ packet_result =
+ SendUnimplementedResponse(packet.GetStringRef().c_str());
+ else
+ packet_result = handler_it->second(packet, error, interrupt, quit);
+ break;
+ }
+ } else {
+ if (!IsConnected()) {
+ error.SetErrorString("lost connection");
+ quit = true;
+ } else {
+ error.SetErrorString("timeout");
+ }
+ }
+
+ // Check if anything occurred that would force us to want to exit.
+ if (m_exit_now)
+ quit = true;
+
+ return packet_result;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServer::SendUnimplementedResponse(const char *) {
+ // TODO: Log the packet we aren't handling...
+ return SendPacketNoLock("");
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServer::SendErrorResponse(uint8_t err) {
+ char packet[16];
+ int packet_len = ::snprintf(packet, sizeof(packet), "E%2.2x", err);
+ assert(packet_len < (int)sizeof(packet));
+ return SendPacketNoLock(llvm::StringRef(packet, packet_len));
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServer::SendErrorResponse(const Status &error) {
+ if (m_send_error_strings) {
+ lldb_private::StreamString packet;
+ packet.Printf("E%2.2x;", static_cast<uint8_t>(error.GetError()));
+ packet.PutStringAsRawHex8(error.AsCString());
+ return SendPacketNoLock(packet.GetString());
+ } else
+ return SendErrorResponse(error.GetError());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServer::SendErrorResponse(llvm::Error error) {
+ std::unique_ptr<llvm::ErrorInfoBase> EIB;
+ std::unique_ptr<PacketUnimplementedError> PUE;
+ llvm::handleAllErrors(
+ std::move(error),
+ [&](std::unique_ptr<PacketUnimplementedError> E) { PUE = std::move(E); },
+ [&](std::unique_ptr<llvm::ErrorInfoBase> E) { EIB = std::move(E); });
+
+ if (EIB)
+ return SendErrorResponse(Status(llvm::Error(std::move(EIB))));
+ if (PUE)
+ return SendUnimplementedResponse(PUE->message().c_str());
+ return SendErrorResponse(Status("Unknown Error"));
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServer::Handle_QErrorStringEnable(
+ StringExtractorGDBRemote &packet) {
+ m_send_error_strings = true;
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServer::SendIllFormedResponse(
+ const StringExtractorGDBRemote &failed_packet, const char *message) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS));
+ if (log)
+ log->Printf("GDBRemoteCommunicationServer::%s: ILLFORMED: '%s' (%s)",
+ __FUNCTION__, failed_packet.GetStringRef().c_str(),
+ message ? message : "");
+ return SendErrorResponse(0x03);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServer::SendOKResponse() {
+ return SendPacketNoLock("OK");
+}
+
+bool GDBRemoteCommunicationServer::HandshakeWithClient() {
+ return GetAck() == PacketResult::Success;
+}
+
+char PacketUnimplementedError::ID;
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h
new file mode 100644
index 000000000000..86f0abf45e06
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h
@@ -0,0 +1,95 @@
+//===-- GDBRemoteCommunicationServer.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 liblldb_GDBRemoteCommunicationServer_h_
+#define liblldb_GDBRemoteCommunicationServer_h_
+
+#include <functional>
+#include <map>
+
+#include "GDBRemoteCommunication.h"
+#include "lldb/lldb-private-forward.h"
+
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+
+class StringExtractorGDBRemote;
+
+namespace lldb_private {
+namespace process_gdb_remote {
+
+class ProcessGDBRemote;
+
+class GDBRemoteCommunicationServer : public GDBRemoteCommunication {
+public:
+ using PortMap = std::map<uint16_t, lldb::pid_t>;
+ using PacketHandler =
+ std::function<PacketResult(StringExtractorGDBRemote &packet,
+ Status &error, bool &interrupt, bool &quit)>;
+
+ GDBRemoteCommunicationServer(const char *comm_name,
+ const char *listener_name);
+
+ ~GDBRemoteCommunicationServer() override;
+
+ void
+ RegisterPacketHandler(StringExtractorGDBRemote::ServerPacketType packet_type,
+ PacketHandler handler);
+
+ PacketResult GetPacketAndSendResponse(Timeout<std::micro> timeout,
+ Status &error, bool &interrupt,
+ bool &quit);
+
+ // After connecting, do a little handshake with the client to make sure
+ // we are at least communicating
+ bool HandshakeWithClient();
+
+protected:
+ std::map<StringExtractorGDBRemote::ServerPacketType, PacketHandler>
+ m_packet_handlers;
+ bool m_exit_now; // use in asynchronous handling to indicate process should
+ // exit.
+
+ bool m_send_error_strings = false; // If the client enables this then
+ // we will send error strings as well.
+
+ PacketResult Handle_QErrorStringEnable(StringExtractorGDBRemote &packet);
+
+ PacketResult SendErrorResponse(const Status &error);
+
+ PacketResult SendErrorResponse(llvm::Error error);
+
+ PacketResult SendUnimplementedResponse(const char *packet);
+
+ PacketResult SendErrorResponse(uint8_t error);
+
+ PacketResult SendIllFormedResponse(const StringExtractorGDBRemote &packet,
+ const char *error_message);
+
+ PacketResult SendOKResponse();
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationServer);
+};
+
+class PacketUnimplementedError
+ : public llvm::ErrorInfo<PacketUnimplementedError, llvm::StringError> {
+public:
+ static char ID;
+ using llvm::ErrorInfo<PacketUnimplementedError,
+ llvm::StringError>::ErrorInfo; // inherit constructors
+ PacketUnimplementedError(const llvm::Twine &S)
+ : ErrorInfo(S, llvm::errc::not_supported) {}
+
+ PacketUnimplementedError() : ErrorInfo(llvm::errc::not_supported) {}
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+#endif // liblldb_GDBRemoteCommunicationServer_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
new file mode 100644
index 000000000000..d095c7a057ad
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
@@ -0,0 +1,1285 @@
+//===-- GDBRemoteCommunicationServerCommon.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 "GDBRemoteCommunicationServerCommon.h"
+
+#include <errno.h>
+
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#endif
+
+#include <chrono>
+#include <cstring>
+
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Host/Config.h"
+#include "lldb/Host/File.h"
+#include "lldb/Host/FileAction.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/SafeMachO.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/JSON.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamGDBRemote.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StructuredData.h"
+#include "llvm/ADT/Triple.h"
+
+#include "ProcessGDBRemoteLog.h"
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+
+#ifdef __ANDROID__
+#include "lldb/Host/android/HostInfoAndroid.h"
+#endif
+
+#include "llvm/ADT/StringSwitch.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+#ifdef __ANDROID__
+const static uint32_t g_default_packet_timeout_sec = 20; // seconds
+#else
+const static uint32_t g_default_packet_timeout_sec = 0; // not specified
+#endif
+
+// GDBRemoteCommunicationServerCommon constructor
+GDBRemoteCommunicationServerCommon::GDBRemoteCommunicationServerCommon(
+ const char *comm_name, const char *listener_name)
+ : GDBRemoteCommunicationServer(comm_name, listener_name),
+ m_process_launch_info(), m_process_launch_error(), m_proc_infos(),
+ m_proc_infos_index(0), m_thread_suffix_supported(false),
+ m_list_threads_in_stop_reply(false) {
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_A,
+ &GDBRemoteCommunicationServerCommon::Handle_A);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QEnvironment,
+ &GDBRemoteCommunicationServerCommon::Handle_QEnvironment);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QEnvironmentHexEncoded,
+ &GDBRemoteCommunicationServerCommon::Handle_QEnvironmentHexEncoded);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qfProcessInfo,
+ &GDBRemoteCommunicationServerCommon::Handle_qfProcessInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qGroupName,
+ &GDBRemoteCommunicationServerCommon::Handle_qGroupName);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qHostInfo,
+ &GDBRemoteCommunicationServerCommon::Handle_qHostInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QLaunchArch,
+ &GDBRemoteCommunicationServerCommon::Handle_QLaunchArch);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qLaunchSuccess,
+ &GDBRemoteCommunicationServerCommon::Handle_qLaunchSuccess);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QListThreadsInStopReply,
+ &GDBRemoteCommunicationServerCommon::Handle_QListThreadsInStopReply);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qEcho,
+ &GDBRemoteCommunicationServerCommon::Handle_qEcho);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qModuleInfo,
+ &GDBRemoteCommunicationServerCommon::Handle_qModuleInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_jModulesInfo,
+ &GDBRemoteCommunicationServerCommon::Handle_jModulesInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qPlatform_chmod,
+ &GDBRemoteCommunicationServerCommon::Handle_qPlatform_chmod);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qPlatform_mkdir,
+ &GDBRemoteCommunicationServerCommon::Handle_qPlatform_mkdir);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qPlatform_shell,
+ &GDBRemoteCommunicationServerCommon::Handle_qPlatform_shell);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qProcessInfoPID,
+ &GDBRemoteCommunicationServerCommon::Handle_qProcessInfoPID);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QSetDetachOnError,
+ &GDBRemoteCommunicationServerCommon::Handle_QSetDetachOnError);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QSetSTDERR,
+ &GDBRemoteCommunicationServerCommon::Handle_QSetSTDERR);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QSetSTDIN,
+ &GDBRemoteCommunicationServerCommon::Handle_QSetSTDIN);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QSetSTDOUT,
+ &GDBRemoteCommunicationServerCommon::Handle_QSetSTDOUT);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qSpeedTest,
+ &GDBRemoteCommunicationServerCommon::Handle_qSpeedTest);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qsProcessInfo,
+ &GDBRemoteCommunicationServerCommon::Handle_qsProcessInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QStartNoAckMode,
+ &GDBRemoteCommunicationServerCommon::Handle_QStartNoAckMode);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qSupported,
+ &GDBRemoteCommunicationServerCommon::Handle_qSupported);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QThreadSuffixSupported,
+ &GDBRemoteCommunicationServerCommon::Handle_QThreadSuffixSupported);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qUserName,
+ &GDBRemoteCommunicationServerCommon::Handle_qUserName);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vFile_close,
+ &GDBRemoteCommunicationServerCommon::Handle_vFile_Close);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vFile_exists,
+ &GDBRemoteCommunicationServerCommon::Handle_vFile_Exists);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vFile_md5,
+ &GDBRemoteCommunicationServerCommon::Handle_vFile_MD5);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vFile_mode,
+ &GDBRemoteCommunicationServerCommon::Handle_vFile_Mode);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vFile_open,
+ &GDBRemoteCommunicationServerCommon::Handle_vFile_Open);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vFile_pread,
+ &GDBRemoteCommunicationServerCommon::Handle_vFile_pRead);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vFile_pwrite,
+ &GDBRemoteCommunicationServerCommon::Handle_vFile_pWrite);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vFile_size,
+ &GDBRemoteCommunicationServerCommon::Handle_vFile_Size);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vFile_stat,
+ &GDBRemoteCommunicationServerCommon::Handle_vFile_Stat);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vFile_symlink,
+ &GDBRemoteCommunicationServerCommon::Handle_vFile_symlink);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vFile_unlink,
+ &GDBRemoteCommunicationServerCommon::Handle_vFile_unlink);
+}
+
+// Destructor
+GDBRemoteCommunicationServerCommon::~GDBRemoteCommunicationServerCommon() {}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qHostInfo(
+ StringExtractorGDBRemote &packet) {
+ StreamString response;
+
+ // $cputype:16777223;cpusubtype:3;ostype:Darwin;vendor:apple;endian:little;ptrsize:8;#00
+
+ ArchSpec host_arch(HostInfo::GetArchitecture());
+ const llvm::Triple &host_triple = host_arch.GetTriple();
+ response.PutCString("triple:");
+ response.PutStringAsRawHex8(host_triple.getTriple());
+ response.Printf(";ptrsize:%u;", host_arch.GetAddressByteSize());
+
+ const char *distribution_id = host_arch.GetDistributionId().AsCString();
+ if (distribution_id) {
+ response.PutCString("distribution_id:");
+ response.PutStringAsRawHex8(distribution_id);
+ response.PutCString(";");
+ }
+
+#if defined(__APPLE__)
+ // For parity with debugserver, we'll include the vendor key.
+ response.PutCString("vendor:apple;");
+
+ // Send out MachO info.
+ uint32_t cpu = host_arch.GetMachOCPUType();
+ uint32_t sub = host_arch.GetMachOCPUSubType();
+ if (cpu != LLDB_INVALID_CPUTYPE)
+ response.Printf("cputype:%u;", cpu);
+ if (sub != LLDB_INVALID_CPUTYPE)
+ response.Printf("cpusubtype:%u;", sub);
+
+ if (cpu == llvm::MachO::CPU_TYPE_ARM || cpu == llvm::MachO::CPU_TYPE_ARM64) {
+// Indicate the OS type.
+#if defined(TARGET_OS_TV) && TARGET_OS_TV == 1
+ response.PutCString("ostype:tvos;");
+#elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
+ response.PutCString("ostype:watchos;");
+#elif defined(TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1
+ response.PutCString("ostype:bridgeos;");
+#else
+ response.PutCString("ostype:ios;");
+#endif
+
+ // On arm, we use "synchronous" watchpoints which means the exception is
+ // delivered before the instruction executes.
+ response.PutCString("watchpoint_exceptions_received:before;");
+ } else {
+ response.PutCString("ostype:macosx;");
+ response.Printf("watchpoint_exceptions_received:after;");
+ }
+
+#else
+ if (host_arch.GetMachine() == llvm::Triple::aarch64 ||
+ host_arch.GetMachine() == llvm::Triple::aarch64_be ||
+ host_arch.GetMachine() == llvm::Triple::arm ||
+ host_arch.GetMachine() == llvm::Triple::armeb || host_arch.IsMIPS())
+ response.Printf("watchpoint_exceptions_received:before;");
+ else
+ response.Printf("watchpoint_exceptions_received:after;");
+#endif
+
+ switch (endian::InlHostByteOrder()) {
+ case eByteOrderBig:
+ response.PutCString("endian:big;");
+ break;
+ case eByteOrderLittle:
+ response.PutCString("endian:little;");
+ break;
+ case eByteOrderPDP:
+ response.PutCString("endian:pdp;");
+ break;
+ default:
+ response.PutCString("endian:unknown;");
+ break;
+ }
+
+ llvm::VersionTuple version = HostInfo::GetOSVersion();
+ if (!version.empty()) {
+ response.Format("os_version:{0}", version.getAsString());
+ response.PutChar(';');
+ }
+
+ std::string s;
+ if (HostInfo::GetOSBuildString(s)) {
+ response.PutCString("os_build:");
+ response.PutStringAsRawHex8(s);
+ response.PutChar(';');
+ }
+ if (HostInfo::GetOSKernelDescription(s)) {
+ response.PutCString("os_kernel:");
+ response.PutStringAsRawHex8(s);
+ response.PutChar(';');
+ }
+
+#if defined(__APPLE__)
+
+#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
+ // For iOS devices, we are connected through a USB Mux so we never pretend to
+ // actually have a hostname as far as the remote lldb that is connecting to
+ // this lldb-platform is concerned
+ response.PutCString("hostname:");
+ response.PutStringAsRawHex8("127.0.0.1");
+ response.PutChar(';');
+#else // #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
+ if (HostInfo::GetHostname(s)) {
+ response.PutCString("hostname:");
+ response.PutStringAsRawHex8(s);
+ response.PutChar(';');
+ }
+#endif // #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
+
+#else // #if defined(__APPLE__)
+ if (HostInfo::GetHostname(s)) {
+ response.PutCString("hostname:");
+ response.PutStringAsRawHex8(s);
+ response.PutChar(';');
+ }
+#endif // #if defined(__APPLE__)
+
+ if (g_default_packet_timeout_sec > 0)
+ response.Printf("default_packet_timeout:%u;", g_default_packet_timeout_sec);
+
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qProcessInfoPID(
+ StringExtractorGDBRemote &packet) {
+ // Packet format: "qProcessInfoPID:%i" where %i is the pid
+ packet.SetFilePos(::strlen("qProcessInfoPID:"));
+ lldb::pid_t pid = packet.GetU32(LLDB_INVALID_PROCESS_ID);
+ if (pid != LLDB_INVALID_PROCESS_ID) {
+ ProcessInstanceInfo proc_info;
+ if (Host::GetProcessInfo(pid, proc_info)) {
+ StreamString response;
+ CreateProcessInfoResponse(proc_info, response);
+ return SendPacketNoLock(response.GetString());
+ }
+ }
+ return SendErrorResponse(1);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qfProcessInfo(
+ StringExtractorGDBRemote &packet) {
+ m_proc_infos_index = 0;
+ m_proc_infos.Clear();
+
+ ProcessInstanceInfoMatch match_info;
+ packet.SetFilePos(::strlen("qfProcessInfo"));
+ if (packet.GetChar() == ':') {
+ llvm::StringRef key;
+ llvm::StringRef value;
+ while (packet.GetNameColonValue(key, value)) {
+ bool success = true;
+ if (key.equals("name")) {
+ StringExtractor extractor(value);
+ std::string file;
+ extractor.GetHexByteString(file);
+ match_info.GetProcessInfo().GetExecutableFile().SetFile(
+ file, FileSpec::Style::native);
+ } else if (key.equals("name_match")) {
+ NameMatch name_match = llvm::StringSwitch<NameMatch>(value)
+ .Case("equals", NameMatch::Equals)
+ .Case("starts_with", NameMatch::StartsWith)
+ .Case("ends_with", NameMatch::EndsWith)
+ .Case("contains", NameMatch::Contains)
+ .Case("regex", NameMatch::RegularExpression)
+ .Default(NameMatch::Ignore);
+ match_info.SetNameMatchType(name_match);
+ if (name_match == NameMatch::Ignore)
+ return SendErrorResponse(2);
+ } else if (key.equals("pid")) {
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+ if (value.getAsInteger(0, pid))
+ return SendErrorResponse(2);
+ match_info.GetProcessInfo().SetProcessID(pid);
+ } else if (key.equals("parent_pid")) {
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+ if (value.getAsInteger(0, pid))
+ return SendErrorResponse(2);
+ match_info.GetProcessInfo().SetParentProcessID(pid);
+ } else if (key.equals("uid")) {
+ uint32_t uid = UINT32_MAX;
+ if (value.getAsInteger(0, uid))
+ return SendErrorResponse(2);
+ match_info.GetProcessInfo().SetUserID(uid);
+ } else if (key.equals("gid")) {
+ uint32_t gid = UINT32_MAX;
+ if (value.getAsInteger(0, gid))
+ return SendErrorResponse(2);
+ match_info.GetProcessInfo().SetGroupID(gid);
+ } else if (key.equals("euid")) {
+ uint32_t uid = UINT32_MAX;
+ if (value.getAsInteger(0, uid))
+ return SendErrorResponse(2);
+ match_info.GetProcessInfo().SetEffectiveUserID(uid);
+ } else if (key.equals("egid")) {
+ uint32_t gid = UINT32_MAX;
+ if (value.getAsInteger(0, gid))
+ return SendErrorResponse(2);
+ match_info.GetProcessInfo().SetEffectiveGroupID(gid);
+ } else if (key.equals("all_users")) {
+ match_info.SetMatchAllUsers(
+ OptionArgParser::ToBoolean(value, false, &success));
+ } else if (key.equals("triple")) {
+ match_info.GetProcessInfo().GetArchitecture() =
+ HostInfo::GetAugmentedArchSpec(value);
+ } else {
+ success = false;
+ }
+
+ if (!success)
+ return SendErrorResponse(2);
+ }
+ }
+
+ if (Host::FindProcesses(match_info, m_proc_infos)) {
+ // We found something, return the first item by calling the get subsequent
+ // process info packet handler...
+ return Handle_qsProcessInfo(packet);
+ }
+ return SendErrorResponse(3);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qsProcessInfo(
+ StringExtractorGDBRemote &packet) {
+ if (m_proc_infos_index < m_proc_infos.GetSize()) {
+ StreamString response;
+ CreateProcessInfoResponse(
+ m_proc_infos.GetProcessInfoAtIndex(m_proc_infos_index), response);
+ ++m_proc_infos_index;
+ return SendPacketNoLock(response.GetString());
+ }
+ return SendErrorResponse(4);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qUserName(
+ StringExtractorGDBRemote &packet) {
+#if !defined(LLDB_DISABLE_POSIX)
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerCommon::%s begin", __FUNCTION__);
+
+ // Packet format: "qUserName:%i" where %i is the uid
+ packet.SetFilePos(::strlen("qUserName:"));
+ uint32_t uid = packet.GetU32(UINT32_MAX);
+ if (uid != UINT32_MAX) {
+ if (llvm::Optional<llvm::StringRef> name =
+ HostInfo::GetUserIDResolver().GetUserName(uid)) {
+ StreamString response;
+ response.PutStringAsRawHex8(*name);
+ return SendPacketNoLock(response.GetString());
+ }
+ }
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerCommon::%s end", __FUNCTION__);
+#endif
+ return SendErrorResponse(5);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qGroupName(
+ StringExtractorGDBRemote &packet) {
+#if !defined(LLDB_DISABLE_POSIX)
+ // Packet format: "qGroupName:%i" where %i is the gid
+ packet.SetFilePos(::strlen("qGroupName:"));
+ uint32_t gid = packet.GetU32(UINT32_MAX);
+ if (gid != UINT32_MAX) {
+ if (llvm::Optional<llvm::StringRef> name =
+ HostInfo::GetUserIDResolver().GetGroupName(gid)) {
+ StreamString response;
+ response.PutStringAsRawHex8(*name);
+ return SendPacketNoLock(response.GetString());
+ }
+ }
+#endif
+ return SendErrorResponse(6);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qSpeedTest(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("qSpeedTest:"));
+
+ llvm::StringRef key;
+ llvm::StringRef value;
+ bool success = packet.GetNameColonValue(key, value);
+ if (success && key.equals("response_size")) {
+ uint32_t response_size = 0;
+ if (!value.getAsInteger(0, response_size)) {
+ if (response_size == 0)
+ return SendOKResponse();
+ StreamString response;
+ uint32_t bytes_left = response_size;
+ response.PutCString("data:");
+ while (bytes_left > 0) {
+ if (bytes_left >= 26) {
+ response.PutCString("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ bytes_left -= 26;
+ } else {
+ response.Printf("%*.*s;", bytes_left, bytes_left,
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ bytes_left = 0;
+ }
+ }
+ return SendPacketNoLock(response.GetString());
+ }
+ }
+ return SendErrorResponse(7);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_vFile_Open(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("vFile:open:"));
+ std::string path;
+ packet.GetHexByteStringTerminatedBy(path, ',');
+ if (!path.empty()) {
+ if (packet.GetChar() == ',') {
+ uint32_t flags = packet.GetHexMaxU32(false, 0);
+ if (packet.GetChar() == ',') {
+ mode_t mode = packet.GetHexMaxU32(false, 0600);
+ FileSpec path_spec(path);
+ FileSystem::Instance().Resolve(path_spec);
+ File file;
+ // Do not close fd.
+ Status error =
+ FileSystem::Instance().Open(file, path_spec, flags, mode, false);
+ const int save_errno = error.GetError();
+ StreamString response;
+ response.PutChar('F');
+ response.Printf("%i", file.GetDescriptor());
+ if (save_errno)
+ response.Printf(",%i", save_errno);
+ return SendPacketNoLock(response.GetString());
+ }
+ }
+ }
+ return SendErrorResponse(18);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_vFile_Close(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("vFile:close:"));
+ int fd = packet.GetS32(-1);
+ int err = -1;
+ int save_errno = 0;
+ if (fd >= 0) {
+ File file(fd, true);
+ Status error = file.Close();
+ err = 0;
+ save_errno = error.GetError();
+ } else {
+ save_errno = EINVAL;
+ }
+ StreamString response;
+ response.PutChar('F');
+ response.Printf("%i", err);
+ if (save_errno)
+ response.Printf(",%i", save_errno);
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_vFile_pRead(
+ StringExtractorGDBRemote &packet) {
+ StreamGDBRemote response;
+ packet.SetFilePos(::strlen("vFile:pread:"));
+ int fd = packet.GetS32(-1);
+ if (packet.GetChar() == ',') {
+ size_t count = packet.GetU64(UINT64_MAX);
+ if (packet.GetChar() == ',') {
+ off_t offset = packet.GetU64(UINT32_MAX);
+ if (count == UINT64_MAX) {
+ response.Printf("F-1:%i", EINVAL);
+ return SendPacketNoLock(response.GetString());
+ }
+
+ std::string buffer(count, 0);
+ File file(fd, false);
+ Status error = file.Read(static_cast<void *>(&buffer[0]), count, offset);
+ const ssize_t bytes_read = error.Success() ? count : -1;
+ const int save_errno = error.GetError();
+ response.PutChar('F');
+ response.Printf("%zi", bytes_read);
+ if (save_errno)
+ response.Printf(",%i", save_errno);
+ else {
+ response.PutChar(';');
+ response.PutEscapedBytes(&buffer[0], bytes_read);
+ }
+ return SendPacketNoLock(response.GetString());
+ }
+ }
+ return SendErrorResponse(21);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_vFile_pWrite(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("vFile:pwrite:"));
+
+ StreamGDBRemote response;
+ response.PutChar('F');
+
+ int fd = packet.GetU32(UINT32_MAX);
+ if (packet.GetChar() == ',') {
+ off_t offset = packet.GetU64(UINT32_MAX);
+ if (packet.GetChar() == ',') {
+ std::string buffer;
+ if (packet.GetEscapedBinaryData(buffer)) {
+ File file(fd, false);
+ size_t count = buffer.size();
+ Status error =
+ file.Write(static_cast<const void *>(&buffer[0]), count, offset);
+ const ssize_t bytes_written = error.Success() ? count : -1;
+ const int save_errno = error.GetError();
+ response.Printf("%zi", bytes_written);
+ if (save_errno)
+ response.Printf(",%i", save_errno);
+ } else {
+ response.Printf("-1,%i", EINVAL);
+ }
+ return SendPacketNoLock(response.GetString());
+ }
+ }
+ return SendErrorResponse(27);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_vFile_Size(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("vFile:size:"));
+ std::string path;
+ packet.GetHexByteString(path);
+ if (!path.empty()) {
+ uint64_t Size;
+ if (llvm::sys::fs::file_size(path, Size))
+ return SendErrorResponse(5);
+ StreamString response;
+ response.PutChar('F');
+ response.PutHex64(Size);
+ if (Size == UINT64_MAX) {
+ response.PutChar(',');
+ response.PutHex64(Size); // TODO: replace with Host::GetSyswideErrorCode()
+ }
+ return SendPacketNoLock(response.GetString());
+ }
+ return SendErrorResponse(22);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_vFile_Mode(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("vFile:mode:"));
+ std::string path;
+ packet.GetHexByteString(path);
+ if (!path.empty()) {
+ FileSpec file_spec(path);
+ FileSystem::Instance().Resolve(file_spec);
+ std::error_code ec;
+ const uint32_t mode = FileSystem::Instance().GetPermissions(file_spec, ec);
+ StreamString response;
+ response.Printf("F%u", mode);
+ if (mode == 0 || ec)
+ response.Printf(",%i", (int)Status(ec).GetError());
+ return SendPacketNoLock(response.GetString());
+ }
+ return SendErrorResponse(23);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_vFile_Exists(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("vFile:exists:"));
+ std::string path;
+ packet.GetHexByteString(path);
+ if (!path.empty()) {
+ bool retcode = llvm::sys::fs::exists(path);
+ StreamString response;
+ response.PutChar('F');
+ response.PutChar(',');
+ if (retcode)
+ response.PutChar('1');
+ else
+ response.PutChar('0');
+ return SendPacketNoLock(response.GetString());
+ }
+ return SendErrorResponse(24);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_vFile_symlink(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("vFile:symlink:"));
+ std::string dst, src;
+ packet.GetHexByteStringTerminatedBy(dst, ',');
+ packet.GetChar(); // Skip ',' char
+ packet.GetHexByteString(src);
+
+ FileSpec src_spec(src);
+ FileSystem::Instance().Resolve(src_spec);
+ Status error = FileSystem::Instance().Symlink(src_spec, FileSpec(dst));
+
+ StreamString response;
+ response.Printf("F%u,%u", error.GetError(), error.GetError());
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_vFile_unlink(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("vFile:unlink:"));
+ std::string path;
+ packet.GetHexByteString(path);
+ Status error(llvm::sys::fs::remove(path));
+ StreamString response;
+ response.Printf("F%u,%u", error.GetError(), error.GetError());
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qPlatform_shell(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("qPlatform_shell:"));
+ std::string path;
+ std::string working_dir;
+ packet.GetHexByteStringTerminatedBy(path, ',');
+ if (!path.empty()) {
+ if (packet.GetChar() == ',') {
+ // FIXME: add timeout to qPlatform_shell packet
+ // uint32_t timeout = packet.GetHexMaxU32(false, 32);
+ if (packet.GetChar() == ',')
+ packet.GetHexByteString(working_dir);
+ int status, signo;
+ std::string output;
+ FileSpec working_spec(working_dir);
+ FileSystem::Instance().Resolve(working_spec);
+ Status err =
+ Host::RunShellCommand(path.c_str(), working_spec, &status, &signo,
+ &output, std::chrono::seconds(10));
+ StreamGDBRemote response;
+ if (err.Fail()) {
+ response.PutCString("F,");
+ response.PutHex32(UINT32_MAX);
+ } else {
+ response.PutCString("F,");
+ response.PutHex32(status);
+ response.PutChar(',');
+ response.PutHex32(signo);
+ response.PutChar(',');
+ response.PutEscapedBytes(output.c_str(), output.size());
+ }
+ return SendPacketNoLock(response.GetString());
+ }
+ }
+ return SendErrorResponse(24);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_vFile_Stat(
+ StringExtractorGDBRemote &packet) {
+ return SendUnimplementedResponse(
+ "GDBRemoteCommunicationServerCommon::Handle_vFile_Stat() unimplemented");
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_vFile_MD5(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("vFile:MD5:"));
+ std::string path;
+ packet.GetHexByteString(path);
+ if (!path.empty()) {
+ StreamGDBRemote response;
+ auto Result = llvm::sys::fs::md5_contents(path);
+ if (!Result) {
+ response.PutCString("F,");
+ response.PutCString("x");
+ } else {
+ response.PutCString("F,");
+ response.PutHex64(Result->low());
+ response.PutHex64(Result->high());
+ }
+ return SendPacketNoLock(response.GetString());
+ }
+ return SendErrorResponse(25);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qPlatform_mkdir(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("qPlatform_mkdir:"));
+ mode_t mode = packet.GetHexMaxU32(false, UINT32_MAX);
+ if (packet.GetChar() == ',') {
+ std::string path;
+ packet.GetHexByteString(path);
+ Status error(llvm::sys::fs::create_directory(path, mode));
+
+ StreamGDBRemote response;
+ response.Printf("F%u", error.GetError());
+
+ return SendPacketNoLock(response.GetString());
+ }
+ return SendErrorResponse(20);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qPlatform_chmod(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("qPlatform_chmod:"));
+
+ auto perms =
+ static_cast<llvm::sys::fs::perms>(packet.GetHexMaxU32(false, UINT32_MAX));
+ if (packet.GetChar() == ',') {
+ std::string path;
+ packet.GetHexByteString(path);
+ Status error(llvm::sys::fs::setPermissions(path, perms));
+
+ StreamGDBRemote response;
+ response.Printf("F%u", error.GetError());
+
+ return SendPacketNoLock(response.GetString());
+ }
+ return SendErrorResponse(19);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qSupported(
+ StringExtractorGDBRemote &packet) {
+ StreamGDBRemote response;
+
+ // Features common to lldb-platform and llgs.
+ uint32_t max_packet_size = 128 * 1024; // 128KBytes is a reasonable max packet
+ // size--debugger can always use less
+ response.Printf("PacketSize=%x", max_packet_size);
+
+ response.PutCString(";QStartNoAckMode+");
+ response.PutCString(";QThreadSuffixSupported+");
+ response.PutCString(";QListThreadsInStopReply+");
+ response.PutCString(";qEcho+");
+#if defined(__linux__) || defined(__NetBSD__)
+ response.PutCString(";QPassSignals+");
+ response.PutCString(";qXfer:auxv:read+");
+#endif
+
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_QThreadSuffixSupported(
+ StringExtractorGDBRemote &packet) {
+ m_thread_suffix_supported = true;
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_QListThreadsInStopReply(
+ StringExtractorGDBRemote &packet) {
+ m_list_threads_in_stop_reply = true;
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_QSetDetachOnError(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("QSetDetachOnError:"));
+ if (packet.GetU32(0))
+ m_process_launch_info.GetFlags().Set(eLaunchFlagDetachOnError);
+ else
+ m_process_launch_info.GetFlags().Clear(eLaunchFlagDetachOnError);
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_QStartNoAckMode(
+ StringExtractorGDBRemote &packet) {
+ // Send response first before changing m_send_acks to we ack this packet
+ PacketResult packet_result = SendOKResponse();
+ m_send_acks = false;
+ return packet_result;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_QSetSTDIN(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("QSetSTDIN:"));
+ FileAction file_action;
+ std::string path;
+ packet.GetHexByteString(path);
+ const bool read = true;
+ const bool write = false;
+ if (file_action.Open(STDIN_FILENO, FileSpec(path), read, write)) {
+ m_process_launch_info.AppendFileAction(file_action);
+ return SendOKResponse();
+ }
+ return SendErrorResponse(15);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_QSetSTDOUT(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("QSetSTDOUT:"));
+ FileAction file_action;
+ std::string path;
+ packet.GetHexByteString(path);
+ const bool read = false;
+ const bool write = true;
+ if (file_action.Open(STDOUT_FILENO, FileSpec(path), read, write)) {
+ m_process_launch_info.AppendFileAction(file_action);
+ return SendOKResponse();
+ }
+ return SendErrorResponse(16);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_QSetSTDERR(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("QSetSTDERR:"));
+ FileAction file_action;
+ std::string path;
+ packet.GetHexByteString(path);
+ const bool read = false;
+ const bool write = true;
+ if (file_action.Open(STDERR_FILENO, FileSpec(path), read, write)) {
+ m_process_launch_info.AppendFileAction(file_action);
+ return SendOKResponse();
+ }
+ return SendErrorResponse(17);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qLaunchSuccess(
+ StringExtractorGDBRemote &packet) {
+ if (m_process_launch_error.Success())
+ return SendOKResponse();
+ StreamString response;
+ response.PutChar('E');
+ response.PutCString(m_process_launch_error.AsCString("<unknown error>"));
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_QEnvironment(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("QEnvironment:"));
+ const uint32_t bytes_left = packet.GetBytesLeft();
+ if (bytes_left > 0) {
+ m_process_launch_info.GetEnvironment().insert(packet.Peek());
+ return SendOKResponse();
+ }
+ return SendErrorResponse(12);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_QEnvironmentHexEncoded(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("QEnvironmentHexEncoded:"));
+ const uint32_t bytes_left = packet.GetBytesLeft();
+ if (bytes_left > 0) {
+ std::string str;
+ packet.GetHexByteString(str);
+ m_process_launch_info.GetEnvironment().insert(str);
+ return SendOKResponse();
+ }
+ return SendErrorResponse(12);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_QLaunchArch(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("QLaunchArch:"));
+ const uint32_t bytes_left = packet.GetBytesLeft();
+ if (bytes_left > 0) {
+ const char *arch_triple = packet.Peek();
+ m_process_launch_info.SetArchitecture(
+ HostInfo::GetAugmentedArchSpec(arch_triple));
+ return SendOKResponse();
+ }
+ return SendErrorResponse(13);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_A(StringExtractorGDBRemote &packet) {
+ // The 'A' packet is the most over designed packet ever here with redundant
+ // argument indexes, redundant argument lengths and needed hex encoded
+ // argument string values. Really all that is needed is a comma separated hex
+ // encoded argument value list, but we will stay true to the documented
+ // version of the 'A' packet here...
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ int actual_arg_index = 0;
+
+ packet.SetFilePos(1); // Skip the 'A'
+ bool success = true;
+ while (success && packet.GetBytesLeft() > 0) {
+ // Decode the decimal argument string length. This length is the number of
+ // hex nibbles in the argument string value.
+ const uint32_t arg_len = packet.GetU32(UINT32_MAX);
+ if (arg_len == UINT32_MAX)
+ success = false;
+ else {
+ // Make sure the argument hex string length is followed by a comma
+ if (packet.GetChar() != ',')
+ success = false;
+ else {
+ // Decode the argument index. We ignore this really because who would
+ // really send down the arguments in a random order???
+ const uint32_t arg_idx = packet.GetU32(UINT32_MAX);
+ if (arg_idx == UINT32_MAX)
+ success = false;
+ else {
+ // Make sure the argument index is followed by a comma
+ if (packet.GetChar() != ',')
+ success = false;
+ else {
+ // Decode the argument string value from hex bytes back into a UTF8
+ // string and make sure the length matches the one supplied in the
+ // packet
+ std::string arg;
+ if (packet.GetHexByteStringFixedLength(arg, arg_len) !=
+ (arg_len / 2))
+ success = false;
+ else {
+ // If there are any bytes left
+ if (packet.GetBytesLeft()) {
+ if (packet.GetChar() != ',')
+ success = false;
+ }
+
+ if (success) {
+ if (arg_idx == 0)
+ m_process_launch_info.GetExecutableFile().SetFile(
+ arg, FileSpec::Style::native);
+ m_process_launch_info.GetArguments().AppendArgument(arg);
+ if (log)
+ log->Printf("LLGSPacketHandler::%s added arg %d: \"%s\"",
+ __FUNCTION__, actual_arg_index, arg.c_str());
+ ++actual_arg_index;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (success) {
+ m_process_launch_error = LaunchProcess();
+ if (m_process_launch_error.Success())
+ return SendOKResponse();
+ LLDB_LOG(log, "failed to launch exe: {0}", m_process_launch_error);
+ }
+ return SendErrorResponse(8);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qEcho(
+ StringExtractorGDBRemote &packet) {
+ // Just echo back the exact same packet for qEcho...
+ return SendPacketNoLock(packet.GetStringRef());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_qModuleInfo(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("qModuleInfo:"));
+
+ std::string module_path;
+ packet.GetHexByteStringTerminatedBy(module_path, ';');
+ if (module_path.empty())
+ return SendErrorResponse(1);
+
+ if (packet.GetChar() != ';')
+ return SendErrorResponse(2);
+
+ std::string triple;
+ packet.GetHexByteString(triple);
+
+ ModuleSpec matched_module_spec = GetModuleInfo(module_path, triple);
+ if (!matched_module_spec.GetFileSpec())
+ return SendErrorResponse(3);
+
+ const auto file_offset = matched_module_spec.GetObjectOffset();
+ const auto file_size = matched_module_spec.GetObjectSize();
+ const auto uuid_str = matched_module_spec.GetUUID().GetAsString("");
+
+ StreamGDBRemote response;
+
+ if (uuid_str.empty()) {
+ auto Result = llvm::sys::fs::md5_contents(
+ matched_module_spec.GetFileSpec().GetPath());
+ if (!Result)
+ return SendErrorResponse(5);
+ response.PutCString("md5:");
+ response.PutStringAsRawHex8(Result->digest());
+ } else {
+ response.PutCString("uuid:");
+ response.PutStringAsRawHex8(uuid_str);
+ }
+ response.PutChar(';');
+
+ const auto &module_arch = matched_module_spec.GetArchitecture();
+ response.PutCString("triple:");
+ response.PutStringAsRawHex8(module_arch.GetTriple().getTriple());
+ response.PutChar(';');
+
+ response.PutCString("file_path:");
+ response.PutStringAsRawHex8(matched_module_spec.GetFileSpec().GetCString());
+ response.PutChar(';');
+ response.PutCString("file_offset:");
+ response.PutHex64(file_offset);
+ response.PutChar(';');
+ response.PutCString("file_size:");
+ response.PutHex64(file_size);
+ response.PutChar(';');
+
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerCommon::Handle_jModulesInfo(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("jModulesInfo:"));
+
+ StructuredData::ObjectSP object_sp = StructuredData::ParseJSON(packet.Peek());
+ if (!object_sp)
+ return SendErrorResponse(1);
+
+ StructuredData::Array *packet_array = object_sp->GetAsArray();
+ if (!packet_array)
+ return SendErrorResponse(2);
+
+ JSONArray::SP response_array_sp = std::make_shared<JSONArray>();
+ for (size_t i = 0; i < packet_array->GetSize(); ++i) {
+ StructuredData::Dictionary *query =
+ packet_array->GetItemAtIndex(i)->GetAsDictionary();
+ if (!query)
+ continue;
+ llvm::StringRef file, triple;
+ if (!query->GetValueForKeyAsString("file", file) ||
+ !query->GetValueForKeyAsString("triple", triple))
+ continue;
+
+ ModuleSpec matched_module_spec = GetModuleInfo(file, triple);
+ if (!matched_module_spec.GetFileSpec())
+ continue;
+
+ const auto file_offset = matched_module_spec.GetObjectOffset();
+ const auto file_size = matched_module_spec.GetObjectSize();
+ const auto uuid_str = matched_module_spec.GetUUID().GetAsString("");
+
+ if (uuid_str.empty())
+ continue;
+
+ JSONObject::SP response = std::make_shared<JSONObject>();
+ response_array_sp->AppendObject(response);
+ response->SetObject("uuid", std::make_shared<JSONString>(uuid_str));
+ response->SetObject(
+ "triple",
+ std::make_shared<JSONString>(
+ matched_module_spec.GetArchitecture().GetTriple().getTriple()));
+ response->SetObject("file_path",
+ std::make_shared<JSONString>(
+ matched_module_spec.GetFileSpec().GetPath()));
+ response->SetObject("file_offset",
+ std::make_shared<JSONNumber>(file_offset));
+ response->SetObject("file_size", std::make_shared<JSONNumber>(file_size));
+ }
+
+ StreamString response;
+ response_array_sp->Write(response);
+ StreamGDBRemote escaped_response;
+ escaped_response.PutEscapedBytes(response.GetString().data(),
+ response.GetSize());
+ return SendPacketNoLock(escaped_response.GetString());
+}
+
+void GDBRemoteCommunicationServerCommon::CreateProcessInfoResponse(
+ const ProcessInstanceInfo &proc_info, StreamString &response) {
+ response.Printf(
+ "pid:%" PRIu64 ";ppid:%" PRIu64 ";uid:%i;gid:%i;euid:%i;egid:%i;",
+ proc_info.GetProcessID(), proc_info.GetParentProcessID(),
+ proc_info.GetUserID(), proc_info.GetGroupID(),
+ proc_info.GetEffectiveUserID(), proc_info.GetEffectiveGroupID());
+ response.PutCString("name:");
+ response.PutStringAsRawHex8(proc_info.GetExecutableFile().GetCString());
+ response.PutChar(';');
+ const ArchSpec &proc_arch = proc_info.GetArchitecture();
+ if (proc_arch.IsValid()) {
+ const llvm::Triple &proc_triple = proc_arch.GetTriple();
+ response.PutCString("triple:");
+ response.PutStringAsRawHex8(proc_triple.getTriple());
+ response.PutChar(';');
+ }
+}
+
+void GDBRemoteCommunicationServerCommon::
+ CreateProcessInfoResponse_DebugServerStyle(
+ const ProcessInstanceInfo &proc_info, StreamString &response) {
+ response.Printf("pid:%" PRIx64 ";parent-pid:%" PRIx64
+ ";real-uid:%x;real-gid:%x;effective-uid:%x;effective-gid:%x;",
+ proc_info.GetProcessID(), proc_info.GetParentProcessID(),
+ proc_info.GetUserID(), proc_info.GetGroupID(),
+ proc_info.GetEffectiveUserID(),
+ proc_info.GetEffectiveGroupID());
+
+ const ArchSpec &proc_arch = proc_info.GetArchitecture();
+ if (proc_arch.IsValid()) {
+ const llvm::Triple &proc_triple = proc_arch.GetTriple();
+#if defined(__APPLE__)
+ // We'll send cputype/cpusubtype.
+ const uint32_t cpu_type = proc_arch.GetMachOCPUType();
+ if (cpu_type != 0)
+ response.Printf("cputype:%" PRIx32 ";", cpu_type);
+
+ const uint32_t cpu_subtype = proc_arch.GetMachOCPUSubType();
+ if (cpu_subtype != 0)
+ response.Printf("cpusubtype:%" PRIx32 ";", cpu_subtype);
+
+ const std::string vendor = proc_triple.getVendorName();
+ if (!vendor.empty())
+ response.Printf("vendor:%s;", vendor.c_str());
+#else
+ // We'll send the triple.
+ response.PutCString("triple:");
+ response.PutStringAsRawHex8(proc_triple.getTriple());
+ response.PutChar(';');
+#endif
+ std::string ostype = proc_triple.getOSName();
+ // Adjust so ostype reports ios for Apple/ARM and Apple/ARM64.
+ if (proc_triple.getVendor() == llvm::Triple::Apple) {
+ switch (proc_triple.getArch()) {
+ case llvm::Triple::arm:
+ case llvm::Triple::thumb:
+ case llvm::Triple::aarch64:
+ ostype = "ios";
+ break;
+ default:
+ // No change.
+ break;
+ }
+ }
+ response.Printf("ostype:%s;", ostype.c_str());
+
+ switch (proc_arch.GetByteOrder()) {
+ case lldb::eByteOrderLittle:
+ response.PutCString("endian:little;");
+ break;
+ case lldb::eByteOrderBig:
+ response.PutCString("endian:big;");
+ break;
+ case lldb::eByteOrderPDP:
+ response.PutCString("endian:pdp;");
+ break;
+ default:
+ // Nothing.
+ break;
+ }
+ // In case of MIPS64, pointer size is depend on ELF ABI For N32 the pointer
+ // size is 4 and for N64 it is 8
+ std::string abi = proc_arch.GetTargetABI();
+ if (!abi.empty())
+ response.Printf("elf_abi:%s;", abi.c_str());
+ response.Printf("ptrsize:%d;", proc_arch.GetAddressByteSize());
+ }
+}
+
+FileSpec GDBRemoteCommunicationServerCommon::FindModuleFile(
+ const std::string &module_path, const ArchSpec &arch) {
+#ifdef __ANDROID__
+ return HostInfoAndroid::ResolveLibraryPath(module_path, arch);
+#else
+ FileSpec file_spec(module_path);
+ FileSystem::Instance().Resolve(file_spec);
+ return file_spec;
+#endif
+}
+
+ModuleSpec
+GDBRemoteCommunicationServerCommon::GetModuleInfo(llvm::StringRef module_path,
+ llvm::StringRef triple) {
+ ArchSpec arch(triple);
+
+ FileSpec req_module_path_spec(module_path);
+ FileSystem::Instance().Resolve(req_module_path_spec);
+
+ const FileSpec module_path_spec =
+ FindModuleFile(req_module_path_spec.GetPath(), arch);
+ const ModuleSpec module_spec(module_path_spec, arch);
+
+ ModuleSpecList module_specs;
+ if (!ObjectFile::GetModuleSpecifications(module_path_spec, 0, 0,
+ module_specs))
+ return ModuleSpec();
+
+ ModuleSpec matched_module_spec;
+ if (!module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec))
+ return ModuleSpec();
+
+ return matched_module_spec;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h
new file mode 100644
index 000000000000..525546312470
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h
@@ -0,0 +1,155 @@
+//===-- GDBRemoteCommunicationServerCommon.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 liblldb_GDBRemoteCommunicationServerCommon_h_
+#define liblldb_GDBRemoteCommunicationServerCommon_h_
+
+#include <string>
+
+#include "lldb/Host/ProcessLaunchInfo.h"
+#include "lldb/lldb-private-forward.h"
+
+#include "GDBRemoteCommunicationServer.h"
+#include "GDBRemoteCommunicationServerCommon.h"
+
+class StringExtractorGDBRemote;
+
+namespace lldb_private {
+namespace process_gdb_remote {
+
+class ProcessGDBRemote;
+
+class GDBRemoteCommunicationServerCommon : public GDBRemoteCommunicationServer {
+public:
+ GDBRemoteCommunicationServerCommon(const char *comm_name,
+ const char *listener_name);
+
+ ~GDBRemoteCommunicationServerCommon() override;
+
+protected:
+ ProcessLaunchInfo m_process_launch_info;
+ Status m_process_launch_error;
+ ProcessInstanceInfoList m_proc_infos;
+ uint32_t m_proc_infos_index;
+ bool m_thread_suffix_supported;
+ bool m_list_threads_in_stop_reply;
+
+ PacketResult Handle_A(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qHostInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qProcessInfoPID(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qfProcessInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qsProcessInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qUserName(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qGroupName(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qSpeedTest(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vFile_Open(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vFile_Close(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vFile_pRead(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vFile_pWrite(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vFile_Size(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vFile_Mode(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vFile_Exists(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vFile_symlink(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vFile_unlink(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vFile_Stat(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vFile_MD5(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qEcho(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qModuleInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_jModulesInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qPlatform_shell(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qPlatform_mkdir(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qPlatform_chmod(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qSupported(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QThreadSuffixSupported(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QListThreadsInStopReply(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QSetDetachOnError(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QStartNoAckMode(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QSetSTDIN(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QSetSTDOUT(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QSetSTDERR(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qLaunchSuccess(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QEnvironment(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QEnvironmentHexEncoded(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QLaunchArch(StringExtractorGDBRemote &packet);
+
+ static void CreateProcessInfoResponse(const ProcessInstanceInfo &proc_info,
+ StreamString &response);
+
+ static void CreateProcessInfoResponse_DebugServerStyle(
+ const ProcessInstanceInfo &proc_info, StreamString &response);
+
+ template <typename T>
+ void RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::ServerPacketType packet_type,
+ PacketResult (T::*handler)(StringExtractorGDBRemote &packet)) {
+ RegisterPacketHandler(packet_type,
+ [this, handler](StringExtractorGDBRemote packet,
+ Status &error, bool &interrupt,
+ bool &quit) {
+ return (static_cast<T *>(this)->*handler)(packet);
+ });
+ }
+
+ /// Launch a process with the current launch settings.
+ ///
+ /// This method supports running an lldb-gdbserver or similar
+ /// server in a situation where the startup code has been provided
+ /// with all the information for a child process to be launched.
+ ///
+ /// \return
+ /// An Status object indicating the success or failure of the
+ /// launch.
+ virtual Status LaunchProcess() = 0;
+
+ virtual FileSpec FindModuleFile(const std::string &module_path,
+ const ArchSpec &arch);
+
+private:
+ ModuleSpec GetModuleInfo(llvm::StringRef module_path, llvm::StringRef triple);
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+#endif // liblldb_GDBRemoteCommunicationServerCommon_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
new file mode 100644
index 000000000000..196607665bba
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -0,0 +1,3285 @@
+//===-- GDBRemoteCommunicationServerLLGS.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 "lldb/Host/Config.h"
+
+#include "GDBRemoteCommunicationServerLLGS.h"
+#include "lldb/Utility/StreamGDBRemote.h"
+
+#include <chrono>
+#include <cstring>
+#include <thread>
+
+#include "lldb/Host/ConnectionFileDescriptor.h"
+#include "lldb/Host/Debug.h"
+#include "lldb/Host/File.h"
+#include "lldb/Host/FileAction.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Host/common/NativeProcessProtocol.h"
+#include "lldb/Host/common/NativeRegisterContext.h"
+#include "lldb/Host/common/NativeThreadProtocol.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/DataBuffer.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/JSON.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/UriParser.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/ScopedPrinter.h"
+
+#include "ProcessGDBRemote.h"
+#include "ProcessGDBRemoteLog.h"
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+using namespace llvm;
+
+// GDBRemote Errors
+
+namespace {
+enum GDBRemoteServerError {
+ // Set to the first unused error number in literal form below
+ eErrorFirst = 29,
+ eErrorNoProcess = eErrorFirst,
+ eErrorResume,
+ eErrorExitStatus
+};
+}
+
+// GDBRemoteCommunicationServerLLGS constructor
+GDBRemoteCommunicationServerLLGS::GDBRemoteCommunicationServerLLGS(
+ MainLoop &mainloop, const NativeProcessProtocol::Factory &process_factory)
+ : GDBRemoteCommunicationServerCommon("gdb-remote.server",
+ "gdb-remote.server.rx_packet"),
+ m_mainloop(mainloop), m_process_factory(process_factory),
+ m_stdio_communication("process.stdio") {
+ RegisterPacketHandlers();
+}
+
+void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() {
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_C,
+ &GDBRemoteCommunicationServerLLGS::Handle_C);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_c,
+ &GDBRemoteCommunicationServerLLGS::Handle_c);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_D,
+ &GDBRemoteCommunicationServerLLGS::Handle_D);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_H,
+ &GDBRemoteCommunicationServerLLGS::Handle_H);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_I,
+ &GDBRemoteCommunicationServerLLGS::Handle_I);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_interrupt,
+ &GDBRemoteCommunicationServerLLGS::Handle_interrupt);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_m,
+ &GDBRemoteCommunicationServerLLGS::Handle_memory_read);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_M,
+ &GDBRemoteCommunicationServerLLGS::Handle_M);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_p,
+ &GDBRemoteCommunicationServerLLGS::Handle_p);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_P,
+ &GDBRemoteCommunicationServerLLGS::Handle_P);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qC,
+ &GDBRemoteCommunicationServerLLGS::Handle_qC);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qfThreadInfo,
+ &GDBRemoteCommunicationServerLLGS::Handle_qfThreadInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qFileLoadAddress,
+ &GDBRemoteCommunicationServerLLGS::Handle_qFileLoadAddress);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qGetWorkingDir,
+ &GDBRemoteCommunicationServerLLGS::Handle_qGetWorkingDir);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qMemoryRegionInfo,
+ &GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qMemoryRegionInfoSupported,
+ &GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfoSupported);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qProcessInfo,
+ &GDBRemoteCommunicationServerLLGS::Handle_qProcessInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qRegisterInfo,
+ &GDBRemoteCommunicationServerLLGS::Handle_qRegisterInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QRestoreRegisterState,
+ &GDBRemoteCommunicationServerLLGS::Handle_QRestoreRegisterState);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QSaveRegisterState,
+ &GDBRemoteCommunicationServerLLGS::Handle_QSaveRegisterState);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QSetDisableASLR,
+ &GDBRemoteCommunicationServerLLGS::Handle_QSetDisableASLR);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir,
+ &GDBRemoteCommunicationServerLLGS::Handle_QSetWorkingDir);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qsThreadInfo,
+ &GDBRemoteCommunicationServerLLGS::Handle_qsThreadInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qThreadStopInfo,
+ &GDBRemoteCommunicationServerLLGS::Handle_qThreadStopInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_jThreadsInfo,
+ &GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qWatchpointSupportInfo,
+ &GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qXfer,
+ &GDBRemoteCommunicationServerLLGS::Handle_qXfer);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_s,
+ &GDBRemoteCommunicationServerLLGS::Handle_s);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_stop_reason,
+ &GDBRemoteCommunicationServerLLGS::Handle_stop_reason); // ?
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vAttach,
+ &GDBRemoteCommunicationServerLLGS::Handle_vAttach);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vCont,
+ &GDBRemoteCommunicationServerLLGS::Handle_vCont);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_vCont_actions,
+ &GDBRemoteCommunicationServerLLGS::Handle_vCont_actions);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_x,
+ &GDBRemoteCommunicationServerLLGS::Handle_memory_read);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_Z,
+ &GDBRemoteCommunicationServerLLGS::Handle_Z);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_z,
+ &GDBRemoteCommunicationServerLLGS::Handle_z);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QPassSignals,
+ &GDBRemoteCommunicationServerLLGS::Handle_QPassSignals);
+
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_jTraceStart,
+ &GDBRemoteCommunicationServerLLGS::Handle_jTraceStart);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_jTraceBufferRead,
+ &GDBRemoteCommunicationServerLLGS::Handle_jTraceRead);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_jTraceMetaRead,
+ &GDBRemoteCommunicationServerLLGS::Handle_jTraceRead);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_jTraceStop,
+ &GDBRemoteCommunicationServerLLGS::Handle_jTraceStop);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_jTraceConfigRead,
+ &GDBRemoteCommunicationServerLLGS::Handle_jTraceConfigRead);
+
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_g,
+ &GDBRemoteCommunicationServerLLGS::Handle_g);
+
+ RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_k,
+ [this](StringExtractorGDBRemote packet, Status &error,
+ bool &interrupt, bool &quit) {
+ quit = true;
+ return this->Handle_k(packet);
+ });
+}
+
+void GDBRemoteCommunicationServerLLGS::SetLaunchInfo(const ProcessLaunchInfo &info) {
+ m_process_launch_info = info;
+}
+
+Status GDBRemoteCommunicationServerLLGS::LaunchProcess() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ if (!m_process_launch_info.GetArguments().GetArgumentCount())
+ return Status("%s: no process command line specified to launch",
+ __FUNCTION__);
+
+ const bool should_forward_stdio =
+ m_process_launch_info.GetFileActionForFD(STDIN_FILENO) == nullptr ||
+ m_process_launch_info.GetFileActionForFD(STDOUT_FILENO) == nullptr ||
+ m_process_launch_info.GetFileActionForFD(STDERR_FILENO) == nullptr;
+ m_process_launch_info.SetLaunchInSeparateProcessGroup(true);
+ m_process_launch_info.GetFlags().Set(eLaunchFlagDebug);
+
+ if (should_forward_stdio) {
+ if (llvm::Error Err = m_process_launch_info.SetUpPtyRedirection())
+ return Status(std::move(Err));
+ }
+
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_debugged_process_mutex);
+ assert(!m_debugged_process_up && "lldb-server creating debugged "
+ "process but one already exists");
+ auto process_or =
+ m_process_factory.Launch(m_process_launch_info, *this, m_mainloop);
+ if (!process_or)
+ return Status(process_or.takeError());
+ m_debugged_process_up = std::move(*process_or);
+ }
+
+ // Handle mirroring of inferior stdout/stderr over the gdb-remote protocol as
+ // needed. llgs local-process debugging may specify PTY paths, which will
+ // make these file actions non-null process launch -i/e/o will also make
+ // these file actions non-null nullptr means that the traffic is expected to
+ // flow over gdb-remote protocol
+ if (should_forward_stdio) {
+ // nullptr means it's not redirected to file or pty (in case of LLGS local)
+ // at least one of stdio will be transferred pty<->gdb-remote we need to
+ // give the pty master handle to this object to read and/or write
+ LLDB_LOG(log,
+ "pid = {0}: setting up stdout/stderr redirection via $O "
+ "gdb-remote commands",
+ m_debugged_process_up->GetID());
+
+ // Setup stdout/stderr mapping from inferior to $O
+ auto terminal_fd = m_debugged_process_up->GetTerminalFileDescriptor();
+ if (terminal_fd >= 0) {
+ if (log)
+ log->Printf("ProcessGDBRemoteCommunicationServerLLGS::%s setting "
+ "inferior STDIO fd to %d",
+ __FUNCTION__, terminal_fd);
+ Status status = SetSTDIOFileDescriptor(terminal_fd);
+ if (status.Fail())
+ return status;
+ } else {
+ if (log)
+ log->Printf("ProcessGDBRemoteCommunicationServerLLGS::%s ignoring "
+ "inferior STDIO since terminal fd reported as %d",
+ __FUNCTION__, terminal_fd);
+ }
+ } else {
+ LLDB_LOG(log,
+ "pid = {0} skipping stdout/stderr redirection via $O: inferior "
+ "will communicate over client-provided file descriptors",
+ m_debugged_process_up->GetID());
+ }
+
+ printf("Launched '%s' as process %" PRIu64 "...\n",
+ m_process_launch_info.GetArguments().GetArgumentAtIndex(0),
+ m_debugged_process_up->GetID());
+
+ return Status();
+}
+
+Status GDBRemoteCommunicationServerLLGS::AttachToProcess(lldb::pid_t pid) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64,
+ __FUNCTION__, pid);
+
+ // Before we try to attach, make sure we aren't already monitoring something
+ // else.
+ if (m_debugged_process_up &&
+ m_debugged_process_up->GetID() != LLDB_INVALID_PROCESS_ID)
+ return Status("cannot attach to process %" PRIu64
+ " when another process with pid %" PRIu64
+ " is being debugged.",
+ pid, m_debugged_process_up->GetID());
+
+ // Try to attach.
+ auto process_or = m_process_factory.Attach(pid, *this, m_mainloop);
+ if (!process_or) {
+ Status status(process_or.takeError());
+ llvm::errs() << llvm::formatv("failed to attach to process {0}: {1}", pid,
+ status);
+ return status;
+ }
+ m_debugged_process_up = std::move(*process_or);
+
+ // Setup stdout/stderr mapping from inferior.
+ auto terminal_fd = m_debugged_process_up->GetTerminalFileDescriptor();
+ if (terminal_fd >= 0) {
+ if (log)
+ log->Printf("ProcessGDBRemoteCommunicationServerLLGS::%s setting "
+ "inferior STDIO fd to %d",
+ __FUNCTION__, terminal_fd);
+ Status status = SetSTDIOFileDescriptor(terminal_fd);
+ if (status.Fail())
+ return status;
+ } else {
+ if (log)
+ log->Printf("ProcessGDBRemoteCommunicationServerLLGS::%s ignoring "
+ "inferior STDIO since terminal fd reported as %d",
+ __FUNCTION__, terminal_fd);
+ }
+
+ printf("Attached to process %" PRIu64 "...\n", pid);
+ return Status();
+}
+
+void GDBRemoteCommunicationServerLLGS::InitializeDelegate(
+ NativeProcessProtocol *process) {
+ assert(process && "process cannot be NULL");
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log) {
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s called with "
+ "NativeProcessProtocol pid %" PRIu64 ", current state: %s",
+ __FUNCTION__, process->GetID(),
+ StateAsCString(process->GetState()));
+ }
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::SendWResponse(
+ NativeProcessProtocol *process) {
+ assert(process && "process cannot be NULL");
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ // send W notification
+ auto wait_status = process->GetExitStatus();
+ if (!wait_status) {
+ LLDB_LOG(log, "pid = {0}, failed to retrieve process exit status",
+ process->GetID());
+
+ StreamGDBRemote response;
+ response.PutChar('E');
+ response.PutHex8(GDBRemoteServerError::eErrorExitStatus);
+ return SendPacketNoLock(response.GetString());
+ }
+
+ LLDB_LOG(log, "pid = {0}, returning exit type {1}", process->GetID(),
+ *wait_status);
+
+ StreamGDBRemote response;
+ response.Format("{0:g}", *wait_status);
+ return SendPacketNoLock(response.GetString());
+}
+
+static void AppendHexValue(StreamString &response, const uint8_t *buf,
+ uint32_t buf_size, bool swap) {
+ int64_t i;
+ if (swap) {
+ for (i = buf_size - 1; i >= 0; i--)
+ response.PutHex8(buf[i]);
+ } else {
+ for (i = 0; i < buf_size; i++)
+ response.PutHex8(buf[i]);
+ }
+}
+
+static void WriteRegisterValueInHexFixedWidth(
+ StreamString &response, NativeRegisterContext &reg_ctx,
+ const RegisterInfo &reg_info, const RegisterValue *reg_value_p,
+ lldb::ByteOrder byte_order) {
+ RegisterValue reg_value;
+ if (!reg_value_p) {
+ Status error = reg_ctx.ReadRegister(&reg_info, reg_value);
+ if (error.Success())
+ reg_value_p = &reg_value;
+ // else log.
+ }
+
+ if (reg_value_p) {
+ AppendHexValue(response, (const uint8_t *)reg_value_p->GetBytes(),
+ reg_value_p->GetByteSize(),
+ byte_order == lldb::eByteOrderLittle);
+ } else {
+ // Zero-out any unreadable values.
+ if (reg_info.byte_size > 0) {
+ std::basic_string<uint8_t> zeros(reg_info.byte_size, '\0');
+ AppendHexValue(response, zeros.data(), zeros.size(), false);
+ }
+ }
+}
+
+static JSONObject::SP GetRegistersAsJSON(NativeThreadProtocol &thread) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ NativeRegisterContext& reg_ctx = thread.GetRegisterContext();
+
+ JSONObject::SP register_object_sp = std::make_shared<JSONObject>();
+
+#ifdef LLDB_JTHREADSINFO_FULL_REGISTER_SET
+ // Expedite all registers in the first register set (i.e. should be GPRs)
+ // that are not contained in other registers.
+ const RegisterSet *reg_set_p = reg_ctx_sp->GetRegisterSet(0);
+ if (!reg_set_p)
+ return nullptr;
+ for (const uint32_t *reg_num_p = reg_set_p->registers;
+ *reg_num_p != LLDB_INVALID_REGNUM; ++reg_num_p) {
+ uint32_t reg_num = *reg_num_p;
+#else
+ // Expedite only a couple of registers until we figure out why sending
+ // registers is expensive.
+ static const uint32_t k_expedited_registers[] = {
+ LLDB_REGNUM_GENERIC_PC, LLDB_REGNUM_GENERIC_SP, LLDB_REGNUM_GENERIC_FP,
+ LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM};
+
+ for (const uint32_t *generic_reg_p = k_expedited_registers;
+ *generic_reg_p != LLDB_INVALID_REGNUM; ++generic_reg_p) {
+ uint32_t reg_num = reg_ctx.ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, *generic_reg_p);
+ if (reg_num == LLDB_INVALID_REGNUM)
+ continue; // Target does not support the given register.
+#endif
+
+ const RegisterInfo *const reg_info_p =
+ reg_ctx.GetRegisterInfoAtIndex(reg_num);
+ if (reg_info_p == nullptr) {
+ if (log)
+ log->Printf(
+ "%s failed to get register info for register index %" PRIu32,
+ __FUNCTION__, reg_num);
+ continue;
+ }
+
+ if (reg_info_p->value_regs != nullptr)
+ continue; // Only expedite registers that are not contained in other
+ // registers.
+
+ RegisterValue reg_value;
+ Status error = reg_ctx.ReadRegister(reg_info_p, reg_value);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("%s failed to read register '%s' index %" PRIu32 ": %s",
+ __FUNCTION__,
+ reg_info_p->name ? reg_info_p->name : "<unnamed-register>",
+ reg_num, error.AsCString());
+ continue;
+ }
+
+ StreamString stream;
+ WriteRegisterValueInHexFixedWidth(stream, reg_ctx, *reg_info_p,
+ &reg_value, lldb::eByteOrderBig);
+
+ register_object_sp->SetObject(
+ llvm::to_string(reg_num),
+ std::make_shared<JSONString>(stream.GetString()));
+ }
+
+ return register_object_sp;
+}
+
+static const char *GetStopReasonString(StopReason stop_reason) {
+ switch (stop_reason) {
+ case eStopReasonTrace:
+ return "trace";
+ case eStopReasonBreakpoint:
+ return "breakpoint";
+ case eStopReasonWatchpoint:
+ return "watchpoint";
+ case eStopReasonSignal:
+ return "signal";
+ case eStopReasonException:
+ return "exception";
+ case eStopReasonExec:
+ return "exec";
+ case eStopReasonInstrumentation:
+ case eStopReasonInvalid:
+ case eStopReasonPlanComplete:
+ case eStopReasonThreadExiting:
+ case eStopReasonNone:
+ break; // ignored
+ }
+ return nullptr;
+}
+
+static JSONArray::SP GetJSONThreadsInfo(NativeProcessProtocol &process,
+ bool abridged) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD));
+
+ JSONArray::SP threads_array_sp = std::make_shared<JSONArray>();
+
+ // Ensure we can get info on the given thread.
+ uint32_t thread_idx = 0;
+ for (NativeThreadProtocol *thread;
+ (thread = process.GetThreadAtIndex(thread_idx)) != nullptr;
+ ++thread_idx) {
+
+ lldb::tid_t tid = thread->GetID();
+
+ // Grab the reason this thread stopped.
+ struct ThreadStopInfo tid_stop_info;
+ std::string description;
+ if (!thread->GetStopReason(tid_stop_info, description))
+ return nullptr;
+
+ const int signum = tid_stop_info.details.signal.signo;
+ if (log) {
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64
+ " tid %" PRIu64
+ " got signal signo = %d, reason = %d, exc_type = %" PRIu64,
+ __FUNCTION__, process.GetID(), tid, signum,
+ tid_stop_info.reason, tid_stop_info.details.exception.type);
+ }
+
+ JSONObject::SP thread_obj_sp = std::make_shared<JSONObject>();
+ threads_array_sp->AppendObject(thread_obj_sp);
+
+ if (!abridged) {
+ if (JSONObject::SP registers_sp = GetRegistersAsJSON(*thread))
+ thread_obj_sp->SetObject("registers", registers_sp);
+ }
+
+ thread_obj_sp->SetObject("tid", std::make_shared<JSONNumber>(tid));
+ if (signum != 0)
+ thread_obj_sp->SetObject("signal", std::make_shared<JSONNumber>(signum));
+
+ const std::string thread_name = thread->GetName();
+ if (!thread_name.empty())
+ thread_obj_sp->SetObject("name",
+ std::make_shared<JSONString>(thread_name));
+
+ if (const char *stop_reason_str = GetStopReasonString(tid_stop_info.reason))
+ thread_obj_sp->SetObject("reason",
+ std::make_shared<JSONString>(stop_reason_str));
+
+ if (!description.empty())
+ thread_obj_sp->SetObject("description",
+ std::make_shared<JSONString>(description));
+
+ if ((tid_stop_info.reason == eStopReasonException) &&
+ tid_stop_info.details.exception.type) {
+ thread_obj_sp->SetObject(
+ "metype",
+ std::make_shared<JSONNumber>(tid_stop_info.details.exception.type));
+
+ JSONArray::SP medata_array_sp = std::make_shared<JSONArray>();
+ for (uint32_t i = 0; i < tid_stop_info.details.exception.data_count;
+ ++i) {
+ medata_array_sp->AppendObject(std::make_shared<JSONNumber>(
+ tid_stop_info.details.exception.data[i]));
+ }
+ thread_obj_sp->SetObject("medata", medata_array_sp);
+ }
+
+ // TODO: Expedite interesting regions of inferior memory
+ }
+
+ return threads_array_sp;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread(
+ lldb::tid_t tid) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD));
+
+ // Ensure we have a debugged process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
+ return SendErrorResponse(50);
+
+ LLDB_LOG(log, "preparing packet for pid {0} tid {1}",
+ m_debugged_process_up->GetID(), tid);
+
+ // Ensure we can get info on the given thread.
+ NativeThreadProtocol *thread = m_debugged_process_up->GetThreadByID(tid);
+ if (!thread)
+ return SendErrorResponse(51);
+
+ // Grab the reason this thread stopped.
+ struct ThreadStopInfo tid_stop_info;
+ std::string description;
+ if (!thread->GetStopReason(tid_stop_info, description))
+ return SendErrorResponse(52);
+
+ // FIXME implement register handling for exec'd inferiors.
+ // if (tid_stop_info.reason == eStopReasonExec) {
+ // const bool force = true;
+ // InitializeRegisters(force);
+ // }
+
+ StreamString response;
+ // Output the T packet with the thread
+ response.PutChar('T');
+ int signum = tid_stop_info.details.signal.signo;
+ LLDB_LOG(
+ log,
+ "pid {0}, tid {1}, got signal signo = {2}, reason = {3}, exc_type = {4}",
+ m_debugged_process_up->GetID(), tid, signum, int(tid_stop_info.reason),
+ tid_stop_info.details.exception.type);
+
+ // Print the signal number.
+ response.PutHex8(signum & 0xff);
+
+ // Include the tid.
+ response.Printf("thread:%" PRIx64 ";", tid);
+
+ // Include the thread name if there is one.
+ const std::string thread_name = thread->GetName();
+ if (!thread_name.empty()) {
+ size_t thread_name_len = thread_name.length();
+
+ if (::strcspn(thread_name.c_str(), "$#+-;:") == thread_name_len) {
+ response.PutCString("name:");
+ response.PutCString(thread_name);
+ } else {
+ // The thread name contains special chars, send as hex bytes.
+ response.PutCString("hexname:");
+ response.PutStringAsRawHex8(thread_name);
+ }
+ response.PutChar(';');
+ }
+
+ // 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) {
+ response.PutCString("threads:");
+
+ uint32_t thread_index = 0;
+ NativeThreadProtocol *listed_thread;
+ for (listed_thread = m_debugged_process_up->GetThreadAtIndex(thread_index);
+ listed_thread; ++thread_index,
+ listed_thread = m_debugged_process_up->GetThreadAtIndex(thread_index)) {
+ if (thread_index > 0)
+ response.PutChar(',');
+ response.Printf("%" PRIx64, listed_thread->GetID());
+ }
+ response.PutChar(';');
+
+ // 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 (thread_index > 0) {
+ const bool threads_with_valid_stop_info_only = true;
+ JSONArray::SP threads_info_sp = GetJSONThreadsInfo(
+ *m_debugged_process_up, threads_with_valid_stop_info_only);
+ if (threads_info_sp) {
+ response.PutCString("jstopinfo:");
+ StreamString unescaped_response;
+ threads_info_sp->Write(unescaped_response);
+ response.PutStringAsRawHex8(unescaped_response.GetData());
+ response.PutChar(';');
+ } else
+ LLDB_LOG(log, "failed to prepare a jstopinfo field for pid {0}",
+ m_debugged_process_up->GetID());
+ }
+
+ uint32_t i = 0;
+ response.PutCString("thread-pcs");
+ char delimiter = ':';
+ for (NativeThreadProtocol *thread;
+ (thread = m_debugged_process_up->GetThreadAtIndex(i)) != nullptr;
+ ++i) {
+ NativeRegisterContext& reg_ctx = thread->GetRegisterContext();
+
+ uint32_t reg_to_read = reg_ctx.ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ const RegisterInfo *const reg_info_p =
+ reg_ctx.GetRegisterInfoAtIndex(reg_to_read);
+
+ RegisterValue reg_value;
+ Status error = reg_ctx.ReadRegister(reg_info_p, reg_value);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("%s failed to read register '%s' index %" PRIu32 ": %s",
+ __FUNCTION__,
+ reg_info_p->name ? reg_info_p->name
+ : "<unnamed-register>",
+ reg_to_read, error.AsCString());
+ continue;
+ }
+
+ response.PutChar(delimiter);
+ delimiter = ',';
+ WriteRegisterValueInHexFixedWidth(response, reg_ctx, *reg_info_p,
+ &reg_value, endian::InlHostByteOrder());
+ }
+
+ response.PutChar(';');
+ }
+
+ //
+ // Expedite registers.
+ //
+
+ // Grab the register context.
+ NativeRegisterContext& reg_ctx = thread->GetRegisterContext();
+ // Expedite all registers in the first register set (i.e. should be GPRs)
+ // that are not contained in other registers.
+ const RegisterSet *reg_set_p;
+ if (reg_ctx.GetRegisterSetCount() > 0 &&
+ ((reg_set_p = reg_ctx.GetRegisterSet(0)) != nullptr)) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s expediting registers "
+ "from set '%s' (registers set count: %zu)",
+ __FUNCTION__,
+ reg_set_p->name ? reg_set_p->name : "<unnamed-set>",
+ reg_set_p->num_registers);
+
+ for (const uint32_t *reg_num_p = reg_set_p->registers;
+ *reg_num_p != LLDB_INVALID_REGNUM; ++reg_num_p) {
+ const RegisterInfo *const reg_info_p =
+ reg_ctx.GetRegisterInfoAtIndex(*reg_num_p);
+ if (reg_info_p == nullptr) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to get "
+ "register info for register set '%s', register index "
+ "%" PRIu32,
+ __FUNCTION__,
+ reg_set_p->name ? reg_set_p->name : "<unnamed-set>",
+ *reg_num_p);
+ } else if (reg_info_p->value_regs == nullptr) {
+ // Only expediate registers that are not contained in other registers.
+ RegisterValue reg_value;
+ Status error = reg_ctx.ReadRegister(reg_info_p, reg_value);
+ if (error.Success()) {
+ response.Printf("%.02x:", *reg_num_p);
+ WriteRegisterValueInHexFixedWidth(response, reg_ctx, *reg_info_p,
+ &reg_value, lldb::eByteOrderBig);
+ response.PutChar(';');
+ } else {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to read "
+ "register '%s' index %" PRIu32 ": %s",
+ __FUNCTION__,
+ reg_info_p->name ? reg_info_p->name
+ : "<unnamed-register>",
+ *reg_num_p, error.AsCString());
+ }
+ }
+ }
+ }
+
+ const char *reason_str = GetStopReasonString(tid_stop_info.reason);
+ if (reason_str != nullptr) {
+ response.Printf("reason:%s;", reason_str);
+ }
+
+ if (!description.empty()) {
+ // Description may contains special chars, send as hex bytes.
+ response.PutCString("description:");
+ response.PutStringAsRawHex8(description);
+ response.PutChar(';');
+ } else if ((tid_stop_info.reason == eStopReasonException) &&
+ tid_stop_info.details.exception.type) {
+ response.PutCString("metype:");
+ response.PutHex64(tid_stop_info.details.exception.type);
+ response.PutCString(";mecount:");
+ response.PutHex32(tid_stop_info.details.exception.data_count);
+ response.PutChar(';');
+
+ for (uint32_t i = 0; i < tid_stop_info.details.exception.data_count; ++i) {
+ response.PutCString("medata:");
+ response.PutHex64(tid_stop_info.details.exception.data[i]);
+ response.PutChar(';');
+ }
+ }
+
+ return SendPacketNoLock(response.GetString());
+}
+
+void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Exited(
+ NativeProcessProtocol *process) {
+ assert(process && "process cannot be NULL");
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__);
+
+ PacketResult result = SendStopReasonForState(StateType::eStateExited);
+ if (result != PacketResult::Success) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to send stop "
+ "notification for PID %" PRIu64 ", state: eStateExited",
+ __FUNCTION__, process->GetID());
+ }
+
+ // Close the pipe to the inferior terminal i/o if we launched it and set one
+ // up.
+ MaybeCloseInferiorTerminalConnection();
+
+ // We are ready to exit the debug monitor.
+ m_exit_now = true;
+ m_mainloop.RequestTermination();
+}
+
+void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Stopped(
+ NativeProcessProtocol *process) {
+ assert(process && "process cannot be NULL");
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__);
+
+ // Send the stop reason unless this is the stop after the launch or attach.
+ switch (m_inferior_prev_state) {
+ case eStateLaunching:
+ case eStateAttaching:
+ // Don't send anything per debugserver behavior.
+ break;
+ default:
+ // In all other cases, send the stop reason.
+ PacketResult result = SendStopReasonForState(StateType::eStateStopped);
+ if (result != PacketResult::Success) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to send stop "
+ "notification for PID %" PRIu64 ", state: eStateExited",
+ __FUNCTION__, process->GetID());
+ }
+ break;
+ }
+}
+
+void GDBRemoteCommunicationServerLLGS::ProcessStateChanged(
+ NativeProcessProtocol *process, lldb::StateType state) {
+ assert(process && "process cannot be NULL");
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log) {
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s called with "
+ "NativeProcessProtocol pid %" PRIu64 ", state: %s",
+ __FUNCTION__, process->GetID(), StateAsCString(state));
+ }
+
+ switch (state) {
+ case StateType::eStateRunning:
+ StartSTDIOForwarding();
+ break;
+
+ case StateType::eStateStopped:
+ // Make sure we get all of the pending stdout/stderr from the inferior and
+ // send it to the lldb host before we send the state change notification
+ SendProcessOutput();
+ // Then stop the forwarding, so that any late output (see llvm.org/pr25652)
+ // does not interfere with our protocol.
+ StopSTDIOForwarding();
+ HandleInferiorState_Stopped(process);
+ break;
+
+ case StateType::eStateExited:
+ // Same as above
+ SendProcessOutput();
+ StopSTDIOForwarding();
+ HandleInferiorState_Exited(process);
+ break;
+
+ default:
+ if (log) {
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s didn't handle state "
+ "change for pid %" PRIu64 ", new state: %s",
+ __FUNCTION__, process->GetID(), StateAsCString(state));
+ }
+ break;
+ }
+
+ // Remember the previous state reported to us.
+ m_inferior_prev_state = state;
+}
+
+void GDBRemoteCommunicationServerLLGS::DidExec(NativeProcessProtocol *process) {
+ ClearProcessSpecificData();
+}
+
+void GDBRemoteCommunicationServerLLGS::DataAvailableCallback() {
+ Log *log(GetLogIfAnyCategoriesSet(GDBR_LOG_COMM));
+
+ if (!m_handshake_completed) {
+ if (!HandshakeWithClient()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s handshake with "
+ "client failed, exiting",
+ __FUNCTION__);
+ m_mainloop.RequestTermination();
+ return;
+ }
+ m_handshake_completed = true;
+ }
+
+ bool interrupt = false;
+ bool done = false;
+ Status error;
+ while (true) {
+ const PacketResult result = GetPacketAndSendResponse(
+ std::chrono::microseconds(0), error, interrupt, done);
+ if (result == PacketResult::ErrorReplyTimeout)
+ break; // No more packets in the queue
+
+ if ((result != PacketResult::Success)) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s processing a packet "
+ "failed: %s",
+ __FUNCTION__, error.AsCString());
+ m_mainloop.RequestTermination();
+ break;
+ }
+ }
+}
+
+Status GDBRemoteCommunicationServerLLGS::InitializeConnection(
+ std::unique_ptr<Connection> &&connection) {
+ IOObjectSP read_object_sp = connection->GetReadObject();
+ GDBRemoteCommunicationServer::SetConnection(connection.release());
+
+ Status error;
+ m_network_handle_up = m_mainloop.RegisterReadObject(
+ read_object_sp, [this](MainLoopBase &) { DataAvailableCallback(); },
+ error);
+ return error;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::SendONotification(const char *buffer,
+ uint32_t len) {
+ if ((buffer == nullptr) || (len == 0)) {
+ // Nothing to send.
+ return PacketResult::Success;
+ }
+
+ StreamString response;
+ response.PutChar('O');
+ response.PutBytesAsRawHex8(buffer, len);
+
+ return SendPacketNoLock(response.GetString());
+}
+
+Status GDBRemoteCommunicationServerLLGS::SetSTDIOFileDescriptor(int fd) {
+ Status error;
+
+ // Set up the reading/handling of process I/O
+ std::unique_ptr<ConnectionFileDescriptor> conn_up(
+ new ConnectionFileDescriptor(fd, true));
+ if (!conn_up) {
+ error.SetErrorString("failed to create ConnectionFileDescriptor");
+ return error;
+ }
+
+ m_stdio_communication.SetCloseOnEOF(false);
+ m_stdio_communication.SetConnection(conn_up.release());
+ if (!m_stdio_communication.IsConnected()) {
+ error.SetErrorString(
+ "failed to set connection for inferior I/O communication");
+ return error;
+ }
+
+ return Status();
+}
+
+void GDBRemoteCommunicationServerLLGS::StartSTDIOForwarding() {
+ // Don't forward if not connected (e.g. when attaching).
+ if (!m_stdio_communication.IsConnected())
+ return;
+
+ Status error;
+ lldbassert(!m_stdio_handle_up);
+ m_stdio_handle_up = m_mainloop.RegisterReadObject(
+ m_stdio_communication.GetConnection()->GetReadObject(),
+ [this](MainLoopBase &) { SendProcessOutput(); }, error);
+
+ if (!m_stdio_handle_up) {
+ // Not much we can do about the failure. Log it and continue without
+ // forwarding.
+ if (Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS))
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s Failed to set up stdio "
+ "forwarding: %s",
+ __FUNCTION__, error.AsCString());
+ }
+}
+
+void GDBRemoteCommunicationServerLLGS::StopSTDIOForwarding() {
+ m_stdio_handle_up.reset();
+}
+
+void GDBRemoteCommunicationServerLLGS::SendProcessOutput() {
+ char buffer[1024];
+ ConnectionStatus status;
+ Status error;
+ while (true) {
+ size_t bytes_read = m_stdio_communication.Read(
+ buffer, sizeof buffer, std::chrono::microseconds(0), status, &error);
+ switch (status) {
+ case eConnectionStatusSuccess:
+ SendONotification(buffer, bytes_read);
+ break;
+ case eConnectionStatusLostConnection:
+ case eConnectionStatusEndOfFile:
+ case eConnectionStatusError:
+ case eConnectionStatusNoConnection:
+ if (Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS))
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s Stopping stdio "
+ "forwarding as communication returned status %d (error: "
+ "%s)",
+ __FUNCTION__, status, error.AsCString());
+ m_stdio_handle_up.reset();
+ return;
+
+ case eConnectionStatusInterrupted:
+ case eConnectionStatusTimedOut:
+ return;
+ }
+ }
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jTraceStart(
+ StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
+ return SendErrorResponse(68);
+
+ if (!packet.ConsumeFront("jTraceStart:"))
+ return SendIllFormedResponse(packet, "jTraceStart: Ill formed packet ");
+
+ TraceOptions options;
+ uint64_t type = std::numeric_limits<uint64_t>::max();
+ uint64_t buffersize = std::numeric_limits<uint64_t>::max();
+ lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
+ uint64_t metabuffersize = std::numeric_limits<uint64_t>::max();
+
+ auto json_object = StructuredData::ParseJSON(packet.Peek());
+
+ if (!json_object ||
+ json_object->GetType() != lldb::eStructuredDataTypeDictionary)
+ return SendIllFormedResponse(packet, "jTraceStart: Ill formed packet ");
+
+ auto json_dict = json_object->GetAsDictionary();
+
+ json_dict->GetValueForKeyAsInteger("metabuffersize", metabuffersize);
+ options.setMetaDataBufferSize(metabuffersize);
+
+ json_dict->GetValueForKeyAsInteger("buffersize", buffersize);
+ options.setTraceBufferSize(buffersize);
+
+ json_dict->GetValueForKeyAsInteger("type", type);
+ options.setType(static_cast<lldb::TraceType>(type));
+
+ json_dict->GetValueForKeyAsInteger("threadid", tid);
+ options.setThreadID(tid);
+
+ StructuredData::ObjectSP custom_params_sp =
+ json_dict->GetValueForKey("params");
+ if (custom_params_sp &&
+ custom_params_sp->GetType() != lldb::eStructuredDataTypeDictionary)
+ return SendIllFormedResponse(packet, "jTraceStart: Ill formed packet ");
+
+ options.setTraceParams(
+ static_pointer_cast<StructuredData::Dictionary>(custom_params_sp));
+
+ if (buffersize == std::numeric_limits<uint64_t>::max() ||
+ type != lldb::TraceType::eTraceTypeProcessorTrace) {
+ LLDB_LOG(log, "Ill formed packet buffersize = {0} type = {1}", buffersize,
+ type);
+ return SendIllFormedResponse(packet, "JTrace:start: Ill formed packet ");
+ }
+
+ Status error;
+ lldb::user_id_t uid = LLDB_INVALID_UID;
+ uid = m_debugged_process_up->StartTrace(options, error);
+ LLDB_LOG(log, "uid is {0} , error is {1}", uid, error.GetError());
+ if (error.Fail())
+ return SendErrorResponse(error);
+
+ StreamGDBRemote response;
+ response.Printf("%" PRIx64, uid);
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jTraceStop(
+ StringExtractorGDBRemote &packet) {
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
+ return SendErrorResponse(68);
+
+ if (!packet.ConsumeFront("jTraceStop:"))
+ return SendIllFormedResponse(packet, "jTraceStop: Ill formed packet ");
+
+ lldb::user_id_t uid = LLDB_INVALID_UID;
+ lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
+
+ auto json_object = StructuredData::ParseJSON(packet.Peek());
+
+ if (!json_object ||
+ json_object->GetType() != lldb::eStructuredDataTypeDictionary)
+ return SendIllFormedResponse(packet, "jTraceStop: Ill formed packet ");
+
+ auto json_dict = json_object->GetAsDictionary();
+
+ if (!json_dict->GetValueForKeyAsInteger("traceid", uid))
+ return SendIllFormedResponse(packet, "jTraceStop: Ill formed packet ");
+
+ json_dict->GetValueForKeyAsInteger("threadid", tid);
+
+ Status error = m_debugged_process_up->StopTrace(uid, tid);
+
+ if (error.Fail())
+ return SendErrorResponse(error);
+
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jTraceConfigRead(
+ StringExtractorGDBRemote &packet) {
+
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
+ return SendErrorResponse(68);
+
+ if (!packet.ConsumeFront("jTraceConfigRead:"))
+ return SendIllFormedResponse(packet,
+ "jTraceConfigRead: Ill formed packet ");
+
+ lldb::user_id_t uid = LLDB_INVALID_UID;
+ lldb::tid_t threadid = LLDB_INVALID_THREAD_ID;
+
+ auto json_object = StructuredData::ParseJSON(packet.Peek());
+
+ if (!json_object ||
+ json_object->GetType() != lldb::eStructuredDataTypeDictionary)
+ return SendIllFormedResponse(packet,
+ "jTraceConfigRead: Ill formed packet ");
+
+ auto json_dict = json_object->GetAsDictionary();
+
+ if (!json_dict->GetValueForKeyAsInteger("traceid", uid))
+ return SendIllFormedResponse(packet,
+ "jTraceConfigRead: Ill formed packet ");
+
+ json_dict->GetValueForKeyAsInteger("threadid", threadid);
+
+ TraceOptions options;
+ StreamGDBRemote response;
+
+ options.setThreadID(threadid);
+ Status error = m_debugged_process_up->GetTraceConfig(uid, options);
+
+ if (error.Fail())
+ return SendErrorResponse(error);
+
+ StreamGDBRemote escaped_response;
+ StructuredData::Dictionary json_packet;
+
+ json_packet.AddIntegerItem("type", options.getType());
+ json_packet.AddIntegerItem("buffersize", options.getTraceBufferSize());
+ json_packet.AddIntegerItem("metabuffersize", options.getMetaDataBufferSize());
+
+ StructuredData::DictionarySP custom_params = options.getTraceParams();
+ if (custom_params)
+ json_packet.AddItem("params", custom_params);
+
+ StreamString json_string;
+ json_packet.Dump(json_string, false);
+ escaped_response.PutEscapedBytes(json_string.GetData(),
+ json_string.GetSize());
+ return SendPacketNoLock(escaped_response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jTraceRead(
+ StringExtractorGDBRemote &packet) {
+
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
+ return SendErrorResponse(68);
+
+ enum PacketType { MetaData, BufferData };
+ PacketType tracetype = MetaData;
+
+ if (packet.ConsumeFront("jTraceBufferRead:"))
+ tracetype = BufferData;
+ else if (packet.ConsumeFront("jTraceMetaRead:"))
+ tracetype = MetaData;
+ else {
+ return SendIllFormedResponse(packet, "jTrace: Ill formed packet ");
+ }
+
+ lldb::user_id_t uid = LLDB_INVALID_UID;
+
+ uint64_t byte_count = std::numeric_limits<uint64_t>::max();
+ lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
+ uint64_t offset = std::numeric_limits<uint64_t>::max();
+
+ auto json_object = StructuredData::ParseJSON(packet.Peek());
+
+ if (!json_object ||
+ json_object->GetType() != lldb::eStructuredDataTypeDictionary)
+ return SendIllFormedResponse(packet, "jTrace: Ill formed packet ");
+
+ auto json_dict = json_object->GetAsDictionary();
+
+ if (!json_dict->GetValueForKeyAsInteger("traceid", uid) ||
+ !json_dict->GetValueForKeyAsInteger("offset", offset) ||
+ !json_dict->GetValueForKeyAsInteger("buffersize", byte_count))
+ return SendIllFormedResponse(packet, "jTrace: Ill formed packet ");
+
+ json_dict->GetValueForKeyAsInteger("threadid", tid);
+
+ // Allocate the response buffer.
+ std::unique_ptr<uint8_t[]> buffer (new (std::nothrow) uint8_t[byte_count]);
+ if (!buffer)
+ return SendErrorResponse(0x78);
+
+ StreamGDBRemote response;
+ Status error;
+ llvm::MutableArrayRef<uint8_t> buf(buffer.get(), byte_count);
+
+ if (tracetype == BufferData)
+ error = m_debugged_process_up->GetData(uid, tid, buf, offset);
+ else if (tracetype == MetaData)
+ error = m_debugged_process_up->GetMetaData(uid, tid, buf, offset);
+
+ if (error.Fail())
+ return SendErrorResponse(error);
+
+ for (auto i : buf)
+ response.PutHex8(i);
+
+ StreamGDBRemote escaped_response;
+ escaped_response.PutEscapedBytes(response.GetData(), response.GetSize());
+ return SendPacketNoLock(escaped_response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qProcessInfo(
+ StringExtractorGDBRemote &packet) {
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
+ return SendErrorResponse(68);
+
+ lldb::pid_t pid = m_debugged_process_up->GetID();
+
+ if (pid == LLDB_INVALID_PROCESS_ID)
+ return SendErrorResponse(1);
+
+ ProcessInstanceInfo proc_info;
+ if (!Host::GetProcessInfo(pid, proc_info))
+ return SendErrorResponse(1);
+
+ StreamString response;
+ CreateProcessInfoResponse_DebugServerStyle(proc_info, response);
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qC(StringExtractorGDBRemote &packet) {
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
+ return SendErrorResponse(68);
+
+ // Make sure we set the current thread so g and p packets return the data the
+ // gdb will expect.
+ lldb::tid_t tid = m_debugged_process_up->GetCurrentThreadID();
+ SetCurrentThreadID(tid);
+
+ NativeThreadProtocol *thread = m_debugged_process_up->GetCurrentThread();
+ if (!thread)
+ return SendErrorResponse(69);
+
+ StreamString response;
+ response.Printf("QC%" PRIx64, thread->GetID());
+
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_k(StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ StopSTDIOForwarding();
+
+ if (!m_debugged_process_up) {
+ LLDB_LOG(log, "No debugged process found.");
+ return PacketResult::Success;
+ }
+
+ Status error = m_debugged_process_up->Kill();
+ if (error.Fail())
+ LLDB_LOG(log, "Failed to kill debugged process {0}: {1}",
+ m_debugged_process_up->GetID(), error);
+
+ // No OK response for kill packet.
+ // return SendOKResponse ();
+ return PacketResult::Success;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_QSetDisableASLR(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("QSetDisableASLR:"));
+ if (packet.GetU32(0))
+ m_process_launch_info.GetFlags().Set(eLaunchFlagDisableASLR);
+ else
+ m_process_launch_info.GetFlags().Clear(eLaunchFlagDisableASLR);
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_QSetWorkingDir(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("QSetWorkingDir:"));
+ std::string path;
+ packet.GetHexByteString(path);
+ m_process_launch_info.SetWorkingDirectory(FileSpec(path));
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qGetWorkingDir(
+ StringExtractorGDBRemote &packet) {
+ FileSpec working_dir{m_process_launch_info.GetWorkingDirectory()};
+ if (working_dir) {
+ StreamString response;
+ response.PutStringAsRawHex8(working_dir.GetCString());
+ return SendPacketNoLock(response.GetString());
+ }
+
+ return SendErrorResponse(14);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_C(StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__);
+
+ // Ensure we have a native process.
+ if (!m_debugged_process_up) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s no debugged process "
+ "shared pointer",
+ __FUNCTION__);
+ return SendErrorResponse(0x36);
+ }
+
+ // Pull out the signal number.
+ packet.SetFilePos(::strlen("C"));
+ if (packet.GetBytesLeft() < 1) {
+ // Shouldn't be using a C without a signal.
+ return SendIllFormedResponse(packet, "C packet specified without signal.");
+ }
+ const uint32_t signo =
+ packet.GetHexMaxU32(false, std::numeric_limits<uint32_t>::max());
+ if (signo == std::numeric_limits<uint32_t>::max())
+ return SendIllFormedResponse(packet, "failed to parse signal number");
+
+ // Handle optional continue address.
+ if (packet.GetBytesLeft() > 0) {
+ // FIXME add continue at address support for $C{signo}[;{continue-address}].
+ if (*packet.Peek() == ';')
+ return SendUnimplementedResponse(packet.GetStringRef().c_str());
+ else
+ return SendIllFormedResponse(
+ packet, "unexpected content after $C{signal-number}");
+ }
+
+ ResumeActionList resume_actions(StateType::eStateRunning, 0);
+ Status error;
+
+ // We have two branches: what to do if a continue thread is specified (in
+ // which case we target sending the signal to that thread), or when we don't
+ // have a continue thread set (in which case we send a signal to the
+ // process).
+
+ // TODO discuss with Greg Clayton, make sure this makes sense.
+
+ lldb::tid_t signal_tid = GetContinueThreadID();
+ if (signal_tid != LLDB_INVALID_THREAD_ID) {
+ // The resume action for the continue thread (or all threads if a continue
+ // thread is not set).
+ ResumeAction action = {GetContinueThreadID(), StateType::eStateRunning,
+ static_cast<int>(signo)};
+
+ // Add the action for the continue thread (or all threads when the continue
+ // thread isn't present).
+ resume_actions.Append(action);
+ } else {
+ // Send the signal to the process since we weren't targeting a specific
+ // continue thread with the signal.
+ error = m_debugged_process_up->Signal(signo);
+ if (error.Fail()) {
+ LLDB_LOG(log, "failed to send signal for process {0}: {1}",
+ m_debugged_process_up->GetID(), error);
+
+ return SendErrorResponse(0x52);
+ }
+ }
+
+ // Resume the threads.
+ error = m_debugged_process_up->Resume(resume_actions);
+ if (error.Fail()) {
+ LLDB_LOG(log, "failed to resume threads for process {0}: {1}",
+ m_debugged_process_up->GetID(), error);
+
+ return SendErrorResponse(0x38);
+ }
+
+ // Don't send an "OK" packet; response is the stopped/exited message.
+ return PacketResult::Success;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_c(StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__);
+
+ packet.SetFilePos(packet.GetFilePos() + ::strlen("c"));
+
+ // For now just support all continue.
+ const bool has_continue_address = (packet.GetBytesLeft() > 0);
+ if (has_continue_address) {
+ LLDB_LOG(log, "not implemented for c[address] variant [{0} remains]",
+ packet.Peek());
+ return SendUnimplementedResponse(packet.GetStringRef().c_str());
+ }
+
+ // Ensure we have a native process.
+ if (!m_debugged_process_up) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s no debugged process "
+ "shared pointer",
+ __FUNCTION__);
+ return SendErrorResponse(0x36);
+ }
+
+ // Build the ResumeActionList
+ ResumeActionList actions(StateType::eStateRunning, 0);
+
+ Status error = m_debugged_process_up->Resume(actions);
+ if (error.Fail()) {
+ LLDB_LOG(log, "c failed for process {0}: {1}",
+ m_debugged_process_up->GetID(), error);
+ return SendErrorResponse(GDBRemoteServerError::eErrorResume);
+ }
+
+ LLDB_LOG(log, "continued process {0}", m_debugged_process_up->GetID());
+ // No response required from continue.
+ return PacketResult::Success;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_vCont_actions(
+ StringExtractorGDBRemote &packet) {
+ StreamString response;
+ response.Printf("vCont;c;C;s;S");
+
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_vCont(
+ StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s handling vCont packet",
+ __FUNCTION__);
+
+ packet.SetFilePos(::strlen("vCont"));
+
+ if (packet.GetBytesLeft() == 0) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s missing action from "
+ "vCont package",
+ __FUNCTION__);
+ return SendIllFormedResponse(packet, "Missing action from vCont package");
+ }
+
+ // Check if this is all continue (no options or ";c").
+ if (::strcmp(packet.Peek(), ";c") == 0) {
+ // Move past the ';', then do a simple 'c'.
+ packet.SetFilePos(packet.GetFilePos() + 1);
+ return Handle_c(packet);
+ } else if (::strcmp(packet.Peek(), ";s") == 0) {
+ // Move past the ';', then do a simple 's'.
+ packet.SetFilePos(packet.GetFilePos() + 1);
+ return Handle_s(packet);
+ }
+
+ // Ensure we have a native process.
+ if (!m_debugged_process_up) {
+ LLDB_LOG(log, "no debugged process");
+ return SendErrorResponse(0x36);
+ }
+
+ ResumeActionList thread_actions;
+
+ while (packet.GetBytesLeft() && *packet.Peek() == ';') {
+ // Skip the semi-colon.
+ packet.GetChar();
+
+ // Build up the thread action.
+ ResumeAction thread_action;
+ thread_action.tid = LLDB_INVALID_THREAD_ID;
+ thread_action.state = eStateInvalid;
+ thread_action.signal = 0;
+
+ const char action = packet.GetChar();
+ switch (action) {
+ case 'C':
+ thread_action.signal = packet.GetHexMaxU32(false, 0);
+ if (thread_action.signal == 0)
+ return SendIllFormedResponse(
+ packet, "Could not parse signal in vCont packet C action");
+ LLVM_FALLTHROUGH;
+
+ case 'c':
+ // Continue
+ thread_action.state = eStateRunning;
+ break;
+
+ case 'S':
+ thread_action.signal = packet.GetHexMaxU32(false, 0);
+ if (thread_action.signal == 0)
+ return SendIllFormedResponse(
+ packet, "Could not parse signal in vCont packet S action");
+ LLVM_FALLTHROUGH;
+
+ case 's':
+ // Step
+ thread_action.state = eStateStepping;
+ break;
+
+ default:
+ return SendIllFormedResponse(packet, "Unsupported vCont action");
+ break;
+ }
+
+ // Parse out optional :{thread-id} value.
+ if (packet.GetBytesLeft() && (*packet.Peek() == ':')) {
+ // Consume the separator.
+ packet.GetChar();
+
+ thread_action.tid = packet.GetHexMaxU32(false, LLDB_INVALID_THREAD_ID);
+ if (thread_action.tid == LLDB_INVALID_THREAD_ID)
+ return SendIllFormedResponse(
+ packet, "Could not parse thread number in vCont packet");
+ }
+
+ thread_actions.Append(thread_action);
+ }
+
+ Status error = m_debugged_process_up->Resume(thread_actions);
+ if (error.Fail()) {
+ LLDB_LOG(log, "vCont failed for process {0}: {1}",
+ m_debugged_process_up->GetID(), error);
+ return SendErrorResponse(GDBRemoteServerError::eErrorResume);
+ }
+
+ LLDB_LOG(log, "continued process {0}", m_debugged_process_up->GetID());
+ // No response required from vCont.
+ return PacketResult::Success;
+}
+
+void GDBRemoteCommunicationServerLLGS::SetCurrentThreadID(lldb::tid_t tid) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+ LLDB_LOG(log, "setting current thread id to {0}", tid);
+
+ m_current_tid = tid;
+ if (m_debugged_process_up)
+ m_debugged_process_up->SetCurrentThreadID(m_current_tid);
+}
+
+void GDBRemoteCommunicationServerLLGS::SetContinueThreadID(lldb::tid_t tid) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+ LLDB_LOG(log, "setting continue thread id to {0}", tid);
+
+ m_continue_tid = tid;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_stop_reason(
+ StringExtractorGDBRemote &packet) {
+ // Handle the $? gdbremote command.
+
+ // If no process, indicate error
+ if (!m_debugged_process_up)
+ return SendErrorResponse(02);
+
+ return SendStopReasonForState(m_debugged_process_up->GetState());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::SendStopReasonForState(
+ lldb::StateType process_state) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ switch (process_state) {
+ case eStateAttaching:
+ case eStateLaunching:
+ case eStateRunning:
+ case eStateStepping:
+ case eStateDetached:
+ // NOTE: gdb protocol doc looks like it should return $OK
+ // when everything is running (i.e. no stopped result).
+ return PacketResult::Success; // Ignore
+
+ case eStateSuspended:
+ case eStateStopped:
+ case eStateCrashed: {
+ lldb::tid_t tid = m_debugged_process_up->GetCurrentThreadID();
+ // Make sure we set the current thread so g and p packets return the data
+ // the gdb will expect.
+ SetCurrentThreadID(tid);
+ return SendStopReplyPacketForThread(tid);
+ }
+
+ case eStateInvalid:
+ case eStateUnloaded:
+ case eStateExited:
+ return SendWResponse(m_debugged_process_up.get());
+
+ default:
+ LLDB_LOG(log, "pid {0}, current state reporting not handled: {1}",
+ m_debugged_process_up->GetID(), process_state);
+ break;
+ }
+
+ return SendErrorResponse(0);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qRegisterInfo(
+ StringExtractorGDBRemote &packet) {
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
+ return SendErrorResponse(68);
+
+ // Ensure we have a thread.
+ NativeThreadProtocol *thread = m_debugged_process_up->GetThreadAtIndex(0);
+ if (!thread)
+ return SendErrorResponse(69);
+
+ // Get the register context for the first thread.
+ NativeRegisterContext &reg_context = thread->GetRegisterContext();
+
+ // Parse out the register number from the request.
+ packet.SetFilePos(strlen("qRegisterInfo"));
+ const uint32_t reg_index =
+ packet.GetHexMaxU32(false, std::numeric_limits<uint32_t>::max());
+ if (reg_index == std::numeric_limits<uint32_t>::max())
+ return SendErrorResponse(69);
+
+ // Return the end of registers response if we've iterated one past the end of
+ // the register set.
+ if (reg_index >= reg_context.GetUserRegisterCount())
+ return SendErrorResponse(69);
+
+ const RegisterInfo *reg_info = reg_context.GetRegisterInfoAtIndex(reg_index);
+ if (!reg_info)
+ return SendErrorResponse(69);
+
+ // Build the reginfos response.
+ StreamGDBRemote response;
+
+ response.PutCString("name:");
+ response.PutCString(reg_info->name);
+ response.PutChar(';');
+
+ if (reg_info->alt_name && reg_info->alt_name[0]) {
+ response.PutCString("alt-name:");
+ response.PutCString(reg_info->alt_name);
+ response.PutChar(';');
+ }
+
+ response.Printf("bitsize:%" PRIu32 ";offset:%" PRIu32 ";",
+ reg_info->byte_size * 8, reg_info->byte_offset);
+
+ switch (reg_info->encoding) {
+ case eEncodingUint:
+ response.PutCString("encoding:uint;");
+ break;
+ case eEncodingSint:
+ response.PutCString("encoding:sint;");
+ break;
+ case eEncodingIEEE754:
+ response.PutCString("encoding:ieee754;");
+ break;
+ case eEncodingVector:
+ response.PutCString("encoding:vector;");
+ break;
+ default:
+ break;
+ }
+
+ switch (reg_info->format) {
+ case eFormatBinary:
+ response.PutCString("format:binary;");
+ break;
+ case eFormatDecimal:
+ response.PutCString("format:decimal;");
+ break;
+ case eFormatHex:
+ response.PutCString("format:hex;");
+ break;
+ case eFormatFloat:
+ response.PutCString("format:float;");
+ break;
+ case eFormatVectorOfSInt8:
+ response.PutCString("format:vector-sint8;");
+ break;
+ case eFormatVectorOfUInt8:
+ response.PutCString("format:vector-uint8;");
+ break;
+ case eFormatVectorOfSInt16:
+ response.PutCString("format:vector-sint16;");
+ break;
+ case eFormatVectorOfUInt16:
+ response.PutCString("format:vector-uint16;");
+ break;
+ case eFormatVectorOfSInt32:
+ response.PutCString("format:vector-sint32;");
+ break;
+ case eFormatVectorOfUInt32:
+ response.PutCString("format:vector-uint32;");
+ break;
+ case eFormatVectorOfFloat32:
+ response.PutCString("format:vector-float32;");
+ break;
+ case eFormatVectorOfUInt64:
+ response.PutCString("format:vector-uint64;");
+ break;
+ case eFormatVectorOfUInt128:
+ response.PutCString("format:vector-uint128;");
+ break;
+ default:
+ break;
+ };
+
+ const char *const register_set_name =
+ reg_context.GetRegisterSetNameForRegisterAtIndex(reg_index);
+ if (register_set_name) {
+ response.PutCString("set:");
+ response.PutCString(register_set_name);
+ response.PutChar(';');
+ }
+
+ if (reg_info->kinds[RegisterKind::eRegisterKindEHFrame] !=
+ LLDB_INVALID_REGNUM)
+ response.Printf("ehframe:%" PRIu32 ";",
+ reg_info->kinds[RegisterKind::eRegisterKindEHFrame]);
+
+ if (reg_info->kinds[RegisterKind::eRegisterKindDWARF] != LLDB_INVALID_REGNUM)
+ response.Printf("dwarf:%" PRIu32 ";",
+ reg_info->kinds[RegisterKind::eRegisterKindDWARF]);
+
+ switch (reg_info->kinds[RegisterKind::eRegisterKindGeneric]) {
+ case LLDB_REGNUM_GENERIC_PC:
+ response.PutCString("generic:pc;");
+ break;
+ case LLDB_REGNUM_GENERIC_SP:
+ response.PutCString("generic:sp;");
+ break;
+ case LLDB_REGNUM_GENERIC_FP:
+ response.PutCString("generic:fp;");
+ break;
+ case LLDB_REGNUM_GENERIC_RA:
+ response.PutCString("generic:ra;");
+ break;
+ case LLDB_REGNUM_GENERIC_FLAGS:
+ response.PutCString("generic:flags;");
+ break;
+ case LLDB_REGNUM_GENERIC_ARG1:
+ response.PutCString("generic:arg1;");
+ break;
+ case LLDB_REGNUM_GENERIC_ARG2:
+ response.PutCString("generic:arg2;");
+ break;
+ case LLDB_REGNUM_GENERIC_ARG3:
+ response.PutCString("generic:arg3;");
+ break;
+ case LLDB_REGNUM_GENERIC_ARG4:
+ response.PutCString("generic:arg4;");
+ break;
+ case LLDB_REGNUM_GENERIC_ARG5:
+ response.PutCString("generic:arg5;");
+ break;
+ case LLDB_REGNUM_GENERIC_ARG6:
+ response.PutCString("generic:arg6;");
+ break;
+ case LLDB_REGNUM_GENERIC_ARG7:
+ response.PutCString("generic:arg7;");
+ break;
+ case LLDB_REGNUM_GENERIC_ARG8:
+ response.PutCString("generic:arg8;");
+ break;
+ default:
+ break;
+ }
+
+ if (reg_info->value_regs && reg_info->value_regs[0] != LLDB_INVALID_REGNUM) {
+ response.PutCString("container-regs:");
+ int i = 0;
+ for (const uint32_t *reg_num = reg_info->value_regs;
+ *reg_num != LLDB_INVALID_REGNUM; ++reg_num, ++i) {
+ if (i > 0)
+ response.PutChar(',');
+ response.Printf("%" PRIx32, *reg_num);
+ }
+ response.PutChar(';');
+ }
+
+ if (reg_info->invalidate_regs && reg_info->invalidate_regs[0]) {
+ response.PutCString("invalidate-regs:");
+ int i = 0;
+ for (const uint32_t *reg_num = reg_info->invalidate_regs;
+ *reg_num != LLDB_INVALID_REGNUM; ++reg_num, ++i) {
+ if (i > 0)
+ response.PutChar(',');
+ response.Printf("%" PRIx32, *reg_num);
+ }
+ response.PutChar(';');
+ }
+
+ if (reg_info->dynamic_size_dwarf_expr_bytes) {
+ const size_t dwarf_opcode_len = reg_info->dynamic_size_dwarf_len;
+ response.PutCString("dynamic_size_dwarf_expr_bytes:");
+ for (uint32_t i = 0; i < dwarf_opcode_len; ++i)
+ response.PutHex8(reg_info->dynamic_size_dwarf_expr_bytes[i]);
+ response.PutChar(';');
+ }
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qfThreadInfo(
+ StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ LLDB_LOG(log, "no process ({0}), returning OK",
+ m_debugged_process_up ? "invalid process id"
+ : "null m_debugged_process_up");
+ return SendOKResponse();
+ }
+
+ StreamGDBRemote response;
+ response.PutChar('m');
+
+ LLDB_LOG(log, "starting thread iteration");
+ NativeThreadProtocol *thread;
+ uint32_t thread_index;
+ for (thread_index = 0,
+ thread = m_debugged_process_up->GetThreadAtIndex(thread_index);
+ thread; ++thread_index,
+ thread = m_debugged_process_up->GetThreadAtIndex(thread_index)) {
+ LLDB_LOG(log, "iterated thread {0}(tid={2})", thread_index,
+ thread->GetID());
+ if (thread_index > 0)
+ response.PutChar(',');
+ response.Printf("%" PRIx64, thread->GetID());
+ }
+
+ LLDB_LOG(log, "finished thread iteration");
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qsThreadInfo(
+ StringExtractorGDBRemote &packet) {
+ // FIXME for now we return the full thread list in the initial packet and
+ // always do nothing here.
+ return SendPacketNoLock("l");
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_g(StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ // Move past packet name.
+ packet.SetFilePos(strlen("g"));
+
+ // Get the thread to use.
+ NativeThreadProtocol *thread = GetThreadFromSuffix(packet);
+ if (!thread) {
+ LLDB_LOG(log, "failed, no thread available");
+ return SendErrorResponse(0x15);
+ }
+
+ // Get the thread's register context.
+ NativeRegisterContext &reg_ctx = thread->GetRegisterContext();
+
+ std::vector<uint8_t> regs_buffer;
+ for (uint32_t reg_num = 0; reg_num < reg_ctx.GetUserRegisterCount();
+ ++reg_num) {
+ const RegisterInfo *reg_info = reg_ctx.GetRegisterInfoAtIndex(reg_num);
+
+ if (reg_info == nullptr) {
+ LLDB_LOG(log, "failed to get register info for register index {0}",
+ reg_num);
+ return SendErrorResponse(0x15);
+ }
+
+ if (reg_info->value_regs != nullptr)
+ continue; // skip registers that are contained in other registers
+
+ RegisterValue reg_value;
+ Status error = reg_ctx.ReadRegister(reg_info, reg_value);
+ if (error.Fail()) {
+ LLDB_LOG(log, "failed to read register at index {0}", reg_num);
+ return SendErrorResponse(0x15);
+ }
+
+ if (reg_info->byte_offset + reg_info->byte_size >= regs_buffer.size())
+ // Resize the buffer to guarantee it can store the register offsetted
+ // data.
+ regs_buffer.resize(reg_info->byte_offset + reg_info->byte_size);
+
+ // Copy the register offsetted data to the buffer.
+ memcpy(regs_buffer.data() + reg_info->byte_offset, reg_value.GetBytes(),
+ reg_info->byte_size);
+ }
+
+ // Write the response.
+ StreamGDBRemote response;
+ response.PutBytesAsRawHex8(regs_buffer.data(), regs_buffer.size());
+
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_p(StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ // Parse out the register number from the request.
+ packet.SetFilePos(strlen("p"));
+ const uint32_t reg_index =
+ packet.GetHexMaxU32(false, std::numeric_limits<uint32_t>::max());
+ if (reg_index == std::numeric_limits<uint32_t>::max()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, could not "
+ "parse register number from request \"%s\"",
+ __FUNCTION__, packet.GetStringRef().c_str());
+ return SendErrorResponse(0x15);
+ }
+
+ // Get the thread to use.
+ NativeThreadProtocol *thread = GetThreadFromSuffix(packet);
+ if (!thread) {
+ LLDB_LOG(log, "failed, no thread available");
+ return SendErrorResponse(0x15);
+ }
+
+ // Get the thread's register context.
+ NativeRegisterContext &reg_context = thread->GetRegisterContext();
+
+ // Return the end of registers response if we've iterated one past the end of
+ // the register set.
+ if (reg_index >= reg_context.GetUserRegisterCount()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, requested "
+ "register %" PRIu32 " beyond register count %" PRIu32,
+ __FUNCTION__, reg_index,
+ reg_context.GetUserRegisterCount());
+ return SendErrorResponse(0x15);
+ }
+
+ const RegisterInfo *reg_info = reg_context.GetRegisterInfoAtIndex(reg_index);
+ if (!reg_info) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, requested "
+ "register %" PRIu32 " returned NULL",
+ __FUNCTION__, reg_index);
+ return SendErrorResponse(0x15);
+ }
+
+ // Build the reginfos response.
+ StreamGDBRemote response;
+
+ // Retrieve the value
+ RegisterValue reg_value;
+ Status error = reg_context.ReadRegister(reg_info, reg_value);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, read of "
+ "requested register %" PRIu32 " (%s) failed: %s",
+ __FUNCTION__, reg_index, reg_info->name, error.AsCString());
+ return SendErrorResponse(0x15);
+ }
+
+ const uint8_t *const data =
+ reinterpret_cast<const uint8_t *>(reg_value.GetBytes());
+ if (!data) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to get data "
+ "bytes from requested register %" PRIu32,
+ __FUNCTION__, reg_index);
+ return SendErrorResponse(0x15);
+ }
+
+ // FIXME flip as needed to get data in big/little endian format for this host.
+ for (uint32_t i = 0; i < reg_value.GetByteSize(); ++i)
+ response.PutHex8(data[i]);
+
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_P(StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ // Ensure there is more content.
+ if (packet.GetBytesLeft() < 1)
+ return SendIllFormedResponse(packet, "Empty P packet");
+
+ // Parse out the register number from the request.
+ packet.SetFilePos(strlen("P"));
+ const uint32_t reg_index =
+ packet.GetHexMaxU32(false, std::numeric_limits<uint32_t>::max());
+ if (reg_index == std::numeric_limits<uint32_t>::max()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, could not "
+ "parse register number from request \"%s\"",
+ __FUNCTION__, packet.GetStringRef().c_str());
+ return SendErrorResponse(0x29);
+ }
+
+ // Note debugserver would send an E30 here.
+ if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != '='))
+ return SendIllFormedResponse(
+ packet, "P packet missing '=' char after register number");
+
+ // Parse out the value.
+ uint8_t reg_bytes[32]; // big enough to support up to 256 bit ymmN register
+ size_t reg_size = packet.GetHexBytesAvail(reg_bytes);
+
+ // Get the thread to use.
+ NativeThreadProtocol *thread = GetThreadFromSuffix(packet);
+ if (!thread) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, no thread "
+ "available (thread index 0)",
+ __FUNCTION__);
+ return SendErrorResponse(0x28);
+ }
+
+ // Get the thread's register context.
+ NativeRegisterContext &reg_context = thread->GetRegisterContext();
+ const RegisterInfo *reg_info = reg_context.GetRegisterInfoAtIndex(reg_index);
+ if (!reg_info) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, requested "
+ "register %" PRIu32 " returned NULL",
+ __FUNCTION__, reg_index);
+ return SendErrorResponse(0x48);
+ }
+
+ // Return the end of registers response if we've iterated one past the end of
+ // the register set.
+ if (reg_index >= reg_context.GetUserRegisterCount()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, requested "
+ "register %" PRIu32 " beyond register count %" PRIu32,
+ __FUNCTION__, reg_index, reg_context.GetUserRegisterCount());
+ return SendErrorResponse(0x47);
+ }
+
+ // The dwarf expression are evaluate on host site which may cause register
+ // size to change Hence the reg_size may not be same as reg_info->bytes_size
+ if ((reg_size != reg_info->byte_size) &&
+ !(reg_info->dynamic_size_dwarf_expr_bytes)) {
+ return SendIllFormedResponse(packet, "P packet register size is incorrect");
+ }
+
+ // Build the reginfos response.
+ StreamGDBRemote response;
+
+ RegisterValue reg_value(
+ reg_bytes, reg_size,
+ m_debugged_process_up->GetArchitecture().GetByteOrder());
+ Status error = reg_context.WriteRegister(reg_info, reg_value);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, write of "
+ "requested register %" PRIu32 " (%s) failed: %s",
+ __FUNCTION__, reg_index, reg_info->name, error.AsCString());
+ return SendErrorResponse(0x32);
+ }
+
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_H(StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ if (log)
+ log->Printf(
+ "GDBRemoteCommunicationServerLLGS::%s failed, no process available",
+ __FUNCTION__);
+ return SendErrorResponse(0x15);
+ }
+
+ // Parse out which variant of $H is requested.
+ packet.SetFilePos(strlen("H"));
+ if (packet.GetBytesLeft() < 1) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, H command "
+ "missing {g,c} variant",
+ __FUNCTION__);
+ return SendIllFormedResponse(packet, "H command missing {g,c} variant");
+ }
+
+ const char h_variant = packet.GetChar();
+ switch (h_variant) {
+ case 'g':
+ break;
+
+ case 'c':
+ break;
+
+ default:
+ if (log)
+ log->Printf(
+ "GDBRemoteCommunicationServerLLGS::%s failed, invalid $H variant %c",
+ __FUNCTION__, h_variant);
+ return SendIllFormedResponse(packet,
+ "H variant unsupported, should be c or g");
+ }
+
+ // Parse out the thread number.
+ // FIXME return a parse success/fail value. All values are valid here.
+ const lldb::tid_t tid =
+ packet.GetHexMaxU64(false, std::numeric_limits<lldb::tid_t>::max());
+
+ // Ensure we have the given thread when not specifying -1 (all threads) or 0
+ // (any thread).
+ if (tid != LLDB_INVALID_THREAD_ID && tid != 0) {
+ NativeThreadProtocol *thread = m_debugged_process_up->GetThreadByID(tid);
+ if (!thread) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, tid %" PRIu64
+ " not found",
+ __FUNCTION__, tid);
+ return SendErrorResponse(0x15);
+ }
+ }
+
+ // Now switch the given thread type.
+ switch (h_variant) {
+ case 'g':
+ SetCurrentThreadID(tid);
+ break;
+
+ case 'c':
+ SetContinueThreadID(tid);
+ break;
+
+ default:
+ assert(false && "unsupported $H variant - shouldn't get here");
+ return SendIllFormedResponse(packet,
+ "H variant unsupported, should be c or g");
+ }
+
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_I(StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ if (log)
+ log->Printf(
+ "GDBRemoteCommunicationServerLLGS::%s failed, no process available",
+ __FUNCTION__);
+ return SendErrorResponse(0x15);
+ }
+
+ packet.SetFilePos(::strlen("I"));
+ uint8_t tmp[4096];
+ for (;;) {
+ size_t read = packet.GetHexBytesAvail(tmp);
+ if (read == 0) {
+ break;
+ }
+ // write directly to stdin *this might block if stdin buffer is full*
+ // TODO: enqueue this block in circular buffer and send window size to
+ // remote host
+ ConnectionStatus status;
+ Status error;
+ m_stdio_communication.Write(tmp, read, status, &error);
+ if (error.Fail()) {
+ return SendErrorResponse(0x15);
+ }
+ }
+
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_interrupt(
+ StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD));
+
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ LLDB_LOG(log, "failed, no process available");
+ return SendErrorResponse(0x15);
+ }
+
+ // Interrupt the process.
+ Status error = m_debugged_process_up->Interrupt();
+ if (error.Fail()) {
+ LLDB_LOG(log, "failed for process {0}: {1}", m_debugged_process_up->GetID(),
+ error);
+ return SendErrorResponse(GDBRemoteServerError::eErrorResume);
+ }
+
+ LLDB_LOG(log, "stopped process {0}", m_debugged_process_up->GetID());
+
+ // No response required from stop all.
+ return PacketResult::Success;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_memory_read(
+ StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ if (log)
+ log->Printf(
+ "GDBRemoteCommunicationServerLLGS::%s failed, no process available",
+ __FUNCTION__);
+ return SendErrorResponse(0x15);
+ }
+
+ // Parse out the memory address.
+ packet.SetFilePos(strlen("m"));
+ if (packet.GetBytesLeft() < 1)
+ return SendIllFormedResponse(packet, "Too short m packet");
+
+ // Read the address. Punting on validation.
+ // FIXME replace with Hex U64 read with no default value that fails on failed
+ // read.
+ const lldb::addr_t read_addr = packet.GetHexMaxU64(false, 0);
+
+ // Validate comma.
+ if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ','))
+ return SendIllFormedResponse(packet, "Comma sep missing in m packet");
+
+ // Get # bytes to read.
+ if (packet.GetBytesLeft() < 1)
+ return SendIllFormedResponse(packet, "Length missing in m packet");
+
+ const uint64_t byte_count = packet.GetHexMaxU64(false, 0);
+ if (byte_count == 0) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s nothing to read: "
+ "zero-length packet",
+ __FUNCTION__);
+ return SendOKResponse();
+ }
+
+ // Allocate the response buffer.
+ std::string buf(byte_count, '\0');
+ if (buf.empty())
+ return SendErrorResponse(0x78);
+
+ // Retrieve the process memory.
+ size_t bytes_read = 0;
+ Status error = m_debugged_process_up->ReadMemoryWithoutTrap(
+ read_addr, &buf[0], byte_count, bytes_read);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64
+ " mem 0x%" PRIx64 ": failed to read. Error: %s",
+ __FUNCTION__, m_debugged_process_up->GetID(), read_addr,
+ error.AsCString());
+ return SendErrorResponse(0x08);
+ }
+
+ if (bytes_read == 0) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64
+ " mem 0x%" PRIx64 ": read 0 of %" PRIu64 " requested bytes",
+ __FUNCTION__, m_debugged_process_up->GetID(), read_addr,
+ byte_count);
+ return SendErrorResponse(0x08);
+ }
+
+ StreamGDBRemote response;
+ packet.SetFilePos(0);
+ char kind = packet.GetChar('?');
+ if (kind == 'x')
+ response.PutEscapedBytes(buf.data(), byte_count);
+ else {
+ assert(kind == 'm');
+ for (size_t i = 0; i < bytes_read; ++i)
+ response.PutHex8(buf[i]);
+ }
+
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_M(StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ if (log)
+ log->Printf(
+ "GDBRemoteCommunicationServerLLGS::%s failed, no process available",
+ __FUNCTION__);
+ return SendErrorResponse(0x15);
+ }
+
+ // Parse out the memory address.
+ packet.SetFilePos(strlen("M"));
+ if (packet.GetBytesLeft() < 1)
+ return SendIllFormedResponse(packet, "Too short M packet");
+
+ // Read the address. Punting on validation.
+ // FIXME replace with Hex U64 read with no default value that fails on failed
+ // read.
+ const lldb::addr_t write_addr = packet.GetHexMaxU64(false, 0);
+
+ // Validate comma.
+ if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ','))
+ return SendIllFormedResponse(packet, "Comma sep missing in M packet");
+
+ // Get # bytes to read.
+ if (packet.GetBytesLeft() < 1)
+ return SendIllFormedResponse(packet, "Length missing in M packet");
+
+ const uint64_t byte_count = packet.GetHexMaxU64(false, 0);
+ if (byte_count == 0) {
+ LLDB_LOG(log, "nothing to write: zero-length packet");
+ return PacketResult::Success;
+ }
+
+ // Validate colon.
+ if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ':'))
+ return SendIllFormedResponse(
+ packet, "Comma sep missing in M packet after byte length");
+
+ // Allocate the conversion buffer.
+ std::vector<uint8_t> buf(byte_count, 0);
+ if (buf.empty())
+ return SendErrorResponse(0x78);
+
+ // Convert the hex memory write contents to bytes.
+ StreamGDBRemote response;
+ const uint64_t convert_count = packet.GetHexBytes(buf, 0);
+ if (convert_count != byte_count) {
+ LLDB_LOG(log,
+ "pid {0} mem {1:x}: asked to write {2} bytes, but only found {3} "
+ "to convert.",
+ m_debugged_process_up->GetID(), write_addr, byte_count,
+ convert_count);
+ return SendIllFormedResponse(packet, "M content byte length specified did "
+ "not match hex-encoded content "
+ "length");
+ }
+
+ // Write the process memory.
+ size_t bytes_written = 0;
+ Status error = m_debugged_process_up->WriteMemory(write_addr, &buf[0],
+ byte_count, bytes_written);
+ if (error.Fail()) {
+ LLDB_LOG(log, "pid {0} mem {1:x}: failed to write. Error: {2}",
+ m_debugged_process_up->GetID(), write_addr, error);
+ return SendErrorResponse(0x09);
+ }
+
+ if (bytes_written == 0) {
+ LLDB_LOG(log, "pid {0} mem {1:x}: wrote 0 of {2} requested bytes",
+ m_debugged_process_up->GetID(), write_addr, byte_count);
+ return SendErrorResponse(0x09);
+ }
+
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfoSupported(
+ StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ // Currently only the NativeProcessProtocol knows if it can handle a
+ // qMemoryRegionInfoSupported request, but we're not guaranteed to be
+ // attached to a process. For now we'll assume the client only asks this
+ // when a process is being debugged.
+
+ // Ensure we have a process running; otherwise, we can't figure this out
+ // since we won't have a NativeProcessProtocol.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ if (log)
+ log->Printf(
+ "GDBRemoteCommunicationServerLLGS::%s failed, no process available",
+ __FUNCTION__);
+ return SendErrorResponse(0x15);
+ }
+
+ // Test if we can get any region back when asking for the region around NULL.
+ MemoryRegionInfo region_info;
+ const Status error =
+ m_debugged_process_up->GetMemoryRegionInfo(0, region_info);
+ if (error.Fail()) {
+ // We don't support memory region info collection for this
+ // NativeProcessProtocol.
+ return SendUnimplementedResponse("");
+ }
+
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfo(
+ StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ // Ensure we have a process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ if (log)
+ log->Printf(
+ "GDBRemoteCommunicationServerLLGS::%s failed, no process available",
+ __FUNCTION__);
+ return SendErrorResponse(0x15);
+ }
+
+ // Parse out the memory address.
+ packet.SetFilePos(strlen("qMemoryRegionInfo:"));
+ if (packet.GetBytesLeft() < 1)
+ return SendIllFormedResponse(packet, "Too short qMemoryRegionInfo: packet");
+
+ // Read the address. Punting on validation.
+ const lldb::addr_t read_addr = packet.GetHexMaxU64(false, 0);
+
+ StreamGDBRemote response;
+
+ // Get the memory region info for the target address.
+ MemoryRegionInfo region_info;
+ const Status error =
+ m_debugged_process_up->GetMemoryRegionInfo(read_addr, region_info);
+ if (error.Fail()) {
+ // Return the error message.
+
+ response.PutCString("error:");
+ response.PutStringAsRawHex8(error.AsCString());
+ response.PutChar(';');
+ } else {
+ // Range start and size.
+ response.Printf("start:%" PRIx64 ";size:%" PRIx64 ";",
+ region_info.GetRange().GetRangeBase(),
+ region_info.GetRange().GetByteSize());
+
+ // Permissions.
+ if (region_info.GetReadable() || region_info.GetWritable() ||
+ region_info.GetExecutable()) {
+ // Write permissions info.
+ response.PutCString("permissions:");
+
+ if (region_info.GetReadable())
+ response.PutChar('r');
+ if (region_info.GetWritable())
+ response.PutChar('w');
+ if (region_info.GetExecutable())
+ response.PutChar('x');
+
+ response.PutChar(';');
+ }
+
+ // Name
+ ConstString name = region_info.GetName();
+ if (name) {
+ response.PutCString("name:");
+ response.PutStringAsRawHex8(name.AsCString());
+ response.PutChar(';');
+ }
+ }
+
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_Z(StringExtractorGDBRemote &packet) {
+ // Ensure we have a process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ LLDB_LOG(log, "failed, no process available");
+ return SendErrorResponse(0x15);
+ }
+
+ // Parse out software or hardware breakpoint or watchpoint requested.
+ packet.SetFilePos(strlen("Z"));
+ if (packet.GetBytesLeft() < 1)
+ return SendIllFormedResponse(
+ packet, "Too short Z packet, missing software/hardware specifier");
+
+ bool want_breakpoint = true;
+ bool want_hardware = false;
+ uint32_t watch_flags = 0;
+
+ const GDBStoppointType stoppoint_type =
+ GDBStoppointType(packet.GetS32(eStoppointInvalid));
+ switch (stoppoint_type) {
+ case eBreakpointSoftware:
+ want_hardware = false;
+ want_breakpoint = true;
+ break;
+ case eBreakpointHardware:
+ want_hardware = true;
+ want_breakpoint = true;
+ break;
+ case eWatchpointWrite:
+ watch_flags = 1;
+ want_hardware = true;
+ want_breakpoint = false;
+ break;
+ case eWatchpointRead:
+ watch_flags = 2;
+ want_hardware = true;
+ want_breakpoint = false;
+ break;
+ case eWatchpointReadWrite:
+ watch_flags = 3;
+ want_hardware = true;
+ want_breakpoint = false;
+ break;
+ case eStoppointInvalid:
+ return SendIllFormedResponse(
+ packet, "Z packet had invalid software/hardware specifier");
+ }
+
+ if ((packet.GetBytesLeft() < 1) || packet.GetChar() != ',')
+ return SendIllFormedResponse(
+ packet, "Malformed Z packet, expecting comma after stoppoint type");
+
+ // Parse out the stoppoint address.
+ if (packet.GetBytesLeft() < 1)
+ return SendIllFormedResponse(packet, "Too short Z packet, missing address");
+ const lldb::addr_t addr = packet.GetHexMaxU64(false, 0);
+
+ if ((packet.GetBytesLeft() < 1) || packet.GetChar() != ',')
+ return SendIllFormedResponse(
+ packet, "Malformed Z packet, expecting comma after address");
+
+ // Parse out the stoppoint size (i.e. size hint for opcode size).
+ const uint32_t size =
+ packet.GetHexMaxU32(false, std::numeric_limits<uint32_t>::max());
+ if (size == std::numeric_limits<uint32_t>::max())
+ return SendIllFormedResponse(
+ packet, "Malformed Z packet, failed to parse size argument");
+
+ if (want_breakpoint) {
+ // Try to set the breakpoint.
+ const Status error =
+ m_debugged_process_up->SetBreakpoint(addr, size, want_hardware);
+ if (error.Success())
+ return SendOKResponse();
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ LLDB_LOG(log, "pid {0} failed to set breakpoint: {1}",
+ m_debugged_process_up->GetID(), error);
+ return SendErrorResponse(0x09);
+ } else {
+ // Try to set the watchpoint.
+ const Status error = m_debugged_process_up->SetWatchpoint(
+ addr, size, watch_flags, want_hardware);
+ if (error.Success())
+ return SendOKResponse();
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ LLDB_LOG(log, "pid {0} failed to set watchpoint: {1}",
+ m_debugged_process_up->GetID(), error);
+ return SendErrorResponse(0x09);
+ }
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_z(StringExtractorGDBRemote &packet) {
+ // Ensure we have a process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+ LLDB_LOG(log, "failed, no process available");
+ return SendErrorResponse(0x15);
+ }
+
+ // Parse out software or hardware breakpoint or watchpoint requested.
+ packet.SetFilePos(strlen("z"));
+ if (packet.GetBytesLeft() < 1)
+ return SendIllFormedResponse(
+ packet, "Too short z packet, missing software/hardware specifier");
+
+ bool want_breakpoint = true;
+ bool want_hardware = false;
+
+ const GDBStoppointType stoppoint_type =
+ GDBStoppointType(packet.GetS32(eStoppointInvalid));
+ switch (stoppoint_type) {
+ case eBreakpointHardware:
+ want_breakpoint = true;
+ want_hardware = true;
+ break;
+ case eBreakpointSoftware:
+ want_breakpoint = true;
+ break;
+ case eWatchpointWrite:
+ want_breakpoint = false;
+ break;
+ case eWatchpointRead:
+ want_breakpoint = false;
+ break;
+ case eWatchpointReadWrite:
+ want_breakpoint = false;
+ break;
+ default:
+ return SendIllFormedResponse(
+ packet, "z packet had invalid software/hardware specifier");
+ }
+
+ if ((packet.GetBytesLeft() < 1) || packet.GetChar() != ',')
+ return SendIllFormedResponse(
+ packet, "Malformed z packet, expecting comma after stoppoint type");
+
+ // Parse out the stoppoint address.
+ if (packet.GetBytesLeft() < 1)
+ return SendIllFormedResponse(packet, "Too short z packet, missing address");
+ const lldb::addr_t addr = packet.GetHexMaxU64(false, 0);
+
+ if ((packet.GetBytesLeft() < 1) || packet.GetChar() != ',')
+ return SendIllFormedResponse(
+ packet, "Malformed z packet, expecting comma after address");
+
+ /*
+ // Parse out the stoppoint size (i.e. size hint for opcode size).
+ const uint32_t size = packet.GetHexMaxU32 (false,
+ std::numeric_limits<uint32_t>::max ());
+ if (size == std::numeric_limits<uint32_t>::max ())
+ return SendIllFormedResponse(packet, "Malformed z packet, failed to parse
+ size argument");
+ */
+
+ if (want_breakpoint) {
+ // Try to clear the breakpoint.
+ const Status error =
+ m_debugged_process_up->RemoveBreakpoint(addr, want_hardware);
+ if (error.Success())
+ return SendOKResponse();
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+ LLDB_LOG(log, "pid {0} failed to remove breakpoint: {1}",
+ m_debugged_process_up->GetID(), error);
+ return SendErrorResponse(0x09);
+ } else {
+ // Try to clear the watchpoint.
+ const Status error = m_debugged_process_up->RemoveWatchpoint(addr);
+ if (error.Success())
+ return SendOKResponse();
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
+ LLDB_LOG(log, "pid {0} failed to remove watchpoint: {1}",
+ m_debugged_process_up->GetID(), error);
+ return SendErrorResponse(0x09);
+ }
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_s(StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD));
+
+ // Ensure we have a process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ if (log)
+ log->Printf(
+ "GDBRemoteCommunicationServerLLGS::%s failed, no process available",
+ __FUNCTION__);
+ return SendErrorResponse(0x32);
+ }
+
+ // We first try to use a continue thread id. If any one or any all set, use
+ // the current thread. Bail out if we don't have a thread id.
+ lldb::tid_t tid = GetContinueThreadID();
+ if (tid == 0 || tid == LLDB_INVALID_THREAD_ID)
+ tid = GetCurrentThreadID();
+ if (tid == LLDB_INVALID_THREAD_ID)
+ return SendErrorResponse(0x33);
+
+ // Double check that we have such a thread.
+ // TODO investigate: on MacOSX we might need to do an UpdateThreads () here.
+ NativeThreadProtocol *thread = m_debugged_process_up->GetThreadByID(tid);
+ if (!thread)
+ return SendErrorResponse(0x33);
+
+ // Create the step action for the given thread.
+ ResumeAction action = {tid, eStateStepping, 0};
+
+ // Setup the actions list.
+ ResumeActionList actions;
+ actions.Append(action);
+
+ // All other threads stop while we're single stepping a thread.
+ actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0);
+ Status error = m_debugged_process_up->Resume(actions);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64
+ " tid %" PRIu64 " Resume() failed with error: %s",
+ __FUNCTION__, m_debugged_process_up->GetID(), tid,
+ error.AsCString());
+ return SendErrorResponse(0x49);
+ }
+
+ // No response here - the stop or exit will come from the resulting action.
+ return PacketResult::Success;
+}
+
+llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
+GDBRemoteCommunicationServerLLGS::ReadXferObject(llvm::StringRef object,
+ llvm::StringRef annex) {
+ if (object == "auxv") {
+ // Make sure we have a valid process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "No process available");
+ }
+
+ // Grab the auxv data.
+ auto buffer_or_error = m_debugged_process_up->GetAuxvData();
+ if (!buffer_or_error)
+ return llvm::errorCodeToError(buffer_or_error.getError());
+ return std::move(*buffer_or_error);
+ }
+
+ return llvm::make_error<PacketUnimplementedError>(
+ "Xfer object not supported");
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qXfer(
+ StringExtractorGDBRemote &packet) {
+ SmallVector<StringRef, 5> fields;
+ // The packet format is "qXfer:<object>:<action>:<annex>:offset,length"
+ StringRef(packet.GetStringRef()).split(fields, ':', 4);
+ if (fields.size() != 5)
+ return SendIllFormedResponse(packet, "malformed qXfer packet");
+ StringRef &xfer_object = fields[1];
+ StringRef &xfer_action = fields[2];
+ StringRef &xfer_annex = fields[3];
+ StringExtractor offset_data(fields[4]);
+ if (xfer_action != "read")
+ return SendUnimplementedResponse("qXfer action not supported");
+ // Parse offset.
+ const uint64_t xfer_offset =
+ offset_data.GetHexMaxU64(false, std::numeric_limits<uint64_t>::max());
+ if (xfer_offset == std::numeric_limits<uint64_t>::max())
+ return SendIllFormedResponse(packet, "qXfer packet missing offset");
+ // Parse out comma.
+ if (offset_data.GetChar() != ',')
+ return SendIllFormedResponse(packet,
+ "qXfer packet missing comma after offset");
+ // Parse out the length.
+ const uint64_t xfer_length =
+ offset_data.GetHexMaxU64(false, std::numeric_limits<uint64_t>::max());
+ if (xfer_length == std::numeric_limits<uint64_t>::max())
+ return SendIllFormedResponse(packet, "qXfer packet missing length");
+
+ // Get a previously constructed buffer if it exists or create it now.
+ std::string buffer_key = (xfer_object + xfer_action + xfer_annex).str();
+ auto buffer_it = m_xfer_buffer_map.find(buffer_key);
+ if (buffer_it == m_xfer_buffer_map.end()) {
+ auto buffer_up = ReadXferObject(xfer_object, xfer_annex);
+ if (!buffer_up)
+ return SendErrorResponse(buffer_up.takeError());
+ buffer_it = m_xfer_buffer_map
+ .insert(std::make_pair(buffer_key, std::move(*buffer_up)))
+ .first;
+ }
+
+ // Send back the response
+ StreamGDBRemote response;
+ bool done_with_buffer = false;
+ llvm::StringRef buffer = buffer_it->second->getBuffer();
+ if (xfer_offset >= buffer.size()) {
+ // We have nothing left to send. Mark the buffer as complete.
+ response.PutChar('l');
+ done_with_buffer = true;
+ } else {
+ // Figure out how many bytes are available starting at the given offset.
+ buffer = buffer.drop_front(xfer_offset);
+ // Mark the response type according to whether we're reading the remainder
+ // of the data.
+ if (xfer_length >= buffer.size()) {
+ // There will be nothing left to read after this
+ response.PutChar('l');
+ done_with_buffer = true;
+ } else {
+ // There will still be bytes to read after this request.
+ response.PutChar('m');
+ buffer = buffer.take_front(xfer_length);
+ }
+ // Now write the data in encoded binary form.
+ response.PutEscapedBytes(buffer.data(), buffer.size());
+ }
+
+ if (done_with_buffer)
+ m_xfer_buffer_map.erase(buffer_it);
+
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_QSaveRegisterState(
+ StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ // Move past packet name.
+ packet.SetFilePos(strlen("QSaveRegisterState"));
+
+ // Get the thread to use.
+ NativeThreadProtocol *thread = GetThreadFromSuffix(packet);
+ if (!thread) {
+ if (m_thread_suffix_supported)
+ return SendIllFormedResponse(
+ packet, "No thread specified in QSaveRegisterState packet");
+ else
+ return SendIllFormedResponse(packet,
+ "No thread was is set with the Hg packet");
+ }
+
+ // Grab the register context for the thread.
+ NativeRegisterContext& reg_context = thread->GetRegisterContext();
+
+ // Save registers to a buffer.
+ DataBufferSP register_data_sp;
+ Status error = reg_context.ReadAllRegisterValues(register_data_sp);
+ if (error.Fail()) {
+ LLDB_LOG(log, "pid {0} failed to save all register values: {1}",
+ m_debugged_process_up->GetID(), error);
+ return SendErrorResponse(0x75);
+ }
+
+ // Allocate a new save id.
+ const uint32_t save_id = GetNextSavedRegistersID();
+ assert((m_saved_registers_map.find(save_id) == m_saved_registers_map.end()) &&
+ "GetNextRegisterSaveID() returned an existing register save id");
+
+ // Save the register data buffer under the save id.
+ {
+ std::lock_guard<std::mutex> guard(m_saved_registers_mutex);
+ m_saved_registers_map[save_id] = register_data_sp;
+ }
+
+ // Write the response.
+ StreamGDBRemote response;
+ response.Printf("%" PRIu32, save_id);
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_QRestoreRegisterState(
+ StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ // Parse out save id.
+ packet.SetFilePos(strlen("QRestoreRegisterState:"));
+ if (packet.GetBytesLeft() < 1)
+ return SendIllFormedResponse(
+ packet, "QRestoreRegisterState packet missing register save id");
+
+ const uint32_t save_id = packet.GetU32(0);
+ if (save_id == 0) {
+ LLDB_LOG(log, "QRestoreRegisterState packet has malformed save id, "
+ "expecting decimal uint32_t");
+ return SendErrorResponse(0x76);
+ }
+
+ // Get the thread to use.
+ NativeThreadProtocol *thread = GetThreadFromSuffix(packet);
+ if (!thread) {
+ if (m_thread_suffix_supported)
+ return SendIllFormedResponse(
+ packet, "No thread specified in QRestoreRegisterState packet");
+ else
+ return SendIllFormedResponse(packet,
+ "No thread was is set with the Hg packet");
+ }
+
+ // Grab the register context for the thread.
+ NativeRegisterContext &reg_context = thread->GetRegisterContext();
+
+ // Retrieve register state buffer, then remove from the list.
+ DataBufferSP register_data_sp;
+ {
+ std::lock_guard<std::mutex> guard(m_saved_registers_mutex);
+
+ // Find the register set buffer for the given save id.
+ auto it = m_saved_registers_map.find(save_id);
+ if (it == m_saved_registers_map.end()) {
+ LLDB_LOG(log,
+ "pid {0} does not have a register set save buffer for id {1}",
+ m_debugged_process_up->GetID(), save_id);
+ return SendErrorResponse(0x77);
+ }
+ register_data_sp = it->second;
+
+ // Remove it from the map.
+ m_saved_registers_map.erase(it);
+ }
+
+ Status error = reg_context.WriteAllRegisterValues(register_data_sp);
+ if (error.Fail()) {
+ LLDB_LOG(log, "pid {0} failed to restore all register values: {1}",
+ m_debugged_process_up->GetID(), error);
+ return SendErrorResponse(0x77);
+ }
+
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_vAttach(
+ StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ // Consume the ';' after vAttach.
+ packet.SetFilePos(strlen("vAttach"));
+ if (!packet.GetBytesLeft() || packet.GetChar() != ';')
+ return SendIllFormedResponse(packet, "vAttach missing expected ';'");
+
+ // Grab the PID to which we will attach (assume hex encoding).
+ lldb::pid_t pid = packet.GetU32(LLDB_INVALID_PROCESS_ID, 16);
+ if (pid == LLDB_INVALID_PROCESS_ID)
+ return SendIllFormedResponse(packet,
+ "vAttach failed to parse the process id");
+
+ // Attempt to attach.
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s attempting to attach to "
+ "pid %" PRIu64,
+ __FUNCTION__, pid);
+
+ Status error = AttachToProcess(pid);
+
+ if (error.Fail()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to attach to "
+ "pid %" PRIu64 ": %s\n",
+ __FUNCTION__, pid, error.AsCString());
+ return SendErrorResponse(error);
+ }
+
+ // Notify we attached by sending a stop packet.
+ return SendStopReasonForState(m_debugged_process_up->GetState());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_D(StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ StopSTDIOForwarding();
+
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+ if (log)
+ log->Printf(
+ "GDBRemoteCommunicationServerLLGS::%s failed, no process available",
+ __FUNCTION__);
+ return SendErrorResponse(0x15);
+ }
+
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+
+ // Consume the ';' after D.
+ packet.SetFilePos(1);
+ if (packet.GetBytesLeft()) {
+ if (packet.GetChar() != ';')
+ return SendIllFormedResponse(packet, "D missing expected ';'");
+
+ // Grab the PID from which we will detach (assume hex encoding).
+ pid = packet.GetU32(LLDB_INVALID_PROCESS_ID, 16);
+ if (pid == LLDB_INVALID_PROCESS_ID)
+ return SendIllFormedResponse(packet, "D failed to parse the process id");
+ }
+
+ if (pid != LLDB_INVALID_PROCESS_ID && m_debugged_process_up->GetID() != pid) {
+ return SendIllFormedResponse(packet, "Invalid pid");
+ }
+
+ const Status error = m_debugged_process_up->Detach();
+ if (error.Fail()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to detach from "
+ "pid %" PRIu64 ": %s\n",
+ __FUNCTION__, m_debugged_process_up->GetID(),
+ error.AsCString());
+ return SendErrorResponse(0x01);
+ }
+
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qThreadStopInfo(
+ StringExtractorGDBRemote &packet) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ packet.SetFilePos(strlen("qThreadStopInfo"));
+ const lldb::tid_t tid = packet.GetHexMaxU32(false, LLDB_INVALID_THREAD_ID);
+ if (tid == LLDB_INVALID_THREAD_ID) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, could not "
+ "parse thread id from request \"%s\"",
+ __FUNCTION__, packet.GetStringRef().c_str());
+ return SendErrorResponse(0x15);
+ }
+ return SendStopReplyPacketForThread(tid);
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo(
+ StringExtractorGDBRemote &) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD));
+
+ // Ensure we have a debugged process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
+ return SendErrorResponse(50);
+ LLDB_LOG(log, "preparing packet for pid {0}", m_debugged_process_up->GetID());
+
+ StreamString response;
+ const bool threads_with_valid_stop_info_only = false;
+ JSONArray::SP threads_array_sp = GetJSONThreadsInfo(
+ *m_debugged_process_up, threads_with_valid_stop_info_only);
+ if (!threads_array_sp) {
+ LLDB_LOG(log, "failed to prepare a packet for pid {0}",
+ m_debugged_process_up->GetID());
+ return SendErrorResponse(52);
+ }
+
+ threads_array_sp->Write(response);
+ StreamGDBRemote escaped_response;
+ escaped_response.PutEscapedBytes(response.GetData(), response.GetSize());
+ return SendPacketNoLock(escaped_response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo(
+ StringExtractorGDBRemote &packet) {
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)
+ return SendErrorResponse(68);
+
+ packet.SetFilePos(strlen("qWatchpointSupportInfo"));
+ if (packet.GetBytesLeft() == 0)
+ return SendOKResponse();
+ if (packet.GetChar() != ':')
+ return SendErrorResponse(67);
+
+ auto hw_debug_cap = m_debugged_process_up->GetHardwareDebugSupportInfo();
+
+ StreamGDBRemote response;
+ if (hw_debug_cap == llvm::None)
+ response.Printf("num:0;");
+ else
+ response.Printf("num:%d;", hw_debug_cap->second);
+
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_qFileLoadAddress(
+ StringExtractorGDBRemote &packet) {
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)
+ return SendErrorResponse(67);
+
+ packet.SetFilePos(strlen("qFileLoadAddress:"));
+ if (packet.GetBytesLeft() == 0)
+ return SendErrorResponse(68);
+
+ std::string file_name;
+ packet.GetHexByteString(file_name);
+
+ lldb::addr_t file_load_address = LLDB_INVALID_ADDRESS;
+ Status error =
+ m_debugged_process_up->GetFileLoadAddress(file_name, file_load_address);
+ if (error.Fail())
+ return SendErrorResponse(69);
+
+ if (file_load_address == LLDB_INVALID_ADDRESS)
+ return SendErrorResponse(1); // File not loaded
+
+ StreamGDBRemote response;
+ response.PutHex64(file_load_address);
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_QPassSignals(
+ StringExtractorGDBRemote &packet) {
+ std::vector<int> signals;
+ packet.SetFilePos(strlen("QPassSignals:"));
+
+ // Read sequence of hex signal numbers divided by a semicolon and optionally
+ // spaces.
+ while (packet.GetBytesLeft() > 0) {
+ int signal = packet.GetS32(-1, 16);
+ if (signal < 0)
+ return SendIllFormedResponse(packet, "Failed to parse signal number.");
+ signals.push_back(signal);
+
+ packet.SkipSpaces();
+ char separator = packet.GetChar();
+ if (separator == '\0')
+ break; // End of string
+ if (separator != ';')
+ return SendIllFormedResponse(packet, "Invalid separator,"
+ " expected semicolon.");
+ }
+
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up)
+ return SendErrorResponse(68);
+
+ Status error = m_debugged_process_up->IgnoreSignals(signals);
+ if (error.Fail())
+ return SendErrorResponse(69);
+
+ return SendOKResponse();
+}
+
+void GDBRemoteCommunicationServerLLGS::MaybeCloseInferiorTerminalConnection() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ // Tell the stdio connection to shut down.
+ if (m_stdio_communication.IsConnected()) {
+ auto connection = m_stdio_communication.GetConnection();
+ if (connection) {
+ Status error;
+ connection->Disconnect(&error);
+
+ if (error.Success()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s disconnect process "
+ "terminal stdio - SUCCESS",
+ __FUNCTION__);
+ } else {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s disconnect process "
+ "terminal stdio - FAIL: %s",
+ __FUNCTION__, error.AsCString());
+ }
+ }
+ }
+}
+
+NativeThreadProtocol *GDBRemoteCommunicationServerLLGS::GetThreadFromSuffix(
+ StringExtractorGDBRemote &packet) {
+ // We have no thread if we don't have a process.
+ if (!m_debugged_process_up ||
+ m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)
+ return nullptr;
+
+ // If the client hasn't asked for thread suffix support, there will not be a
+ // thread suffix. Use the current thread in that case.
+ if (!m_thread_suffix_supported) {
+ const lldb::tid_t current_tid = GetCurrentThreadID();
+ if (current_tid == LLDB_INVALID_THREAD_ID)
+ return nullptr;
+ else if (current_tid == 0) {
+ // Pick a thread.
+ return m_debugged_process_up->GetThreadAtIndex(0);
+ } else
+ return m_debugged_process_up->GetThreadByID(current_tid);
+ }
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+
+ // Parse out the ';'.
+ if (packet.GetBytesLeft() < 1 || packet.GetChar() != ';') {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s gdb-remote parse "
+ "error: expected ';' prior to start of thread suffix: packet "
+ "contents = '%s'",
+ __FUNCTION__, packet.GetStringRef().c_str());
+ return nullptr;
+ }
+
+ if (!packet.GetBytesLeft())
+ return nullptr;
+
+ // Parse out thread: portion.
+ if (strncmp(packet.Peek(), "thread:", strlen("thread:")) != 0) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerLLGS::%s gdb-remote parse "
+ "error: expected 'thread:' but not found, packet contents = "
+ "'%s'",
+ __FUNCTION__, packet.GetStringRef().c_str());
+ return nullptr;
+ }
+ packet.SetFilePos(packet.GetFilePos() + strlen("thread:"));
+ const lldb::tid_t tid = packet.GetHexMaxU64(false, 0);
+ if (tid != 0)
+ return m_debugged_process_up->GetThreadByID(tid);
+
+ return nullptr;
+}
+
+lldb::tid_t GDBRemoteCommunicationServerLLGS::GetCurrentThreadID() const {
+ if (m_current_tid == 0 || m_current_tid == LLDB_INVALID_THREAD_ID) {
+ // Use whatever the debug process says is the current thread id since the
+ // protocol either didn't specify or specified we want any/all threads
+ // marked as the current thread.
+ if (!m_debugged_process_up)
+ return LLDB_INVALID_THREAD_ID;
+ return m_debugged_process_up->GetCurrentThreadID();
+ }
+ // Use the specific current thread id set by the gdb remote protocol.
+ return m_current_tid;
+}
+
+uint32_t GDBRemoteCommunicationServerLLGS::GetNextSavedRegistersID() {
+ std::lock_guard<std::mutex> guard(m_saved_registers_mutex);
+ return m_next_saved_registers_id++;
+}
+
+void GDBRemoteCommunicationServerLLGS::ClearProcessSpecificData() {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ LLDB_LOG(log, "clearing {0} xfer buffers", m_xfer_buffer_map.size());
+ m_xfer_buffer_map.clear();
+}
+
+FileSpec
+GDBRemoteCommunicationServerLLGS::FindModuleFile(const std::string &module_path,
+ const ArchSpec &arch) {
+ if (m_debugged_process_up) {
+ FileSpec file_spec;
+ if (m_debugged_process_up
+ ->GetLoadedModuleFileSpec(module_path.c_str(), file_spec)
+ .Success()) {
+ if (FileSystem::Instance().Exists(file_spec))
+ return file_spec;
+ }
+ }
+
+ return GDBRemoteCommunicationServerCommon::FindModuleFile(module_path, arch);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
new file mode 100644
index 000000000000..068ea52caaaf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -0,0 +1,229 @@
+//===-- GDBRemoteCommunicationServerLLGS.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 liblldb_GDBRemoteCommunicationServerLLGS_h_
+#define liblldb_GDBRemoteCommunicationServerLLGS_h_
+
+#include <mutex>
+#include <unordered_map>
+
+#include "lldb/Core/Communication.h"
+#include "lldb/Host/MainLoop.h"
+#include "lldb/Host/common/NativeProcessProtocol.h"
+#include "lldb/lldb-private-forward.h"
+
+#include "GDBRemoteCommunicationServerCommon.h"
+
+class StringExtractorGDBRemote;
+
+namespace lldb_private {
+
+namespace process_gdb_remote {
+
+class ProcessGDBRemote;
+
+class GDBRemoteCommunicationServerLLGS
+ : public GDBRemoteCommunicationServerCommon,
+ public NativeProcessProtocol::NativeDelegate {
+public:
+ // Constructors and Destructors
+ GDBRemoteCommunicationServerLLGS(
+ MainLoop &mainloop,
+ const NativeProcessProtocol::Factory &process_factory);
+
+ void SetLaunchInfo(const ProcessLaunchInfo &info);
+
+ /// Launch a process with the current launch settings.
+ ///
+ /// This method supports running an lldb-gdbserver or similar
+ /// server in a situation where the startup code has been provided
+ /// with all the information for a child process to be launched.
+ ///
+ /// \return
+ /// An Status object indicating the success or failure of the
+ /// launch.
+ Status LaunchProcess() override;
+
+ /// Attach to a process.
+ ///
+ /// This method supports attaching llgs to a process accessible via the
+ /// configured Platform.
+ ///
+ /// \return
+ /// An Status object indicating the success or failure of the
+ /// attach operation.
+ Status AttachToProcess(lldb::pid_t pid);
+
+ // NativeProcessProtocol::NativeDelegate overrides
+ void InitializeDelegate(NativeProcessProtocol *process) override;
+
+ void ProcessStateChanged(NativeProcessProtocol *process,
+ lldb::StateType state) override;
+
+ void DidExec(NativeProcessProtocol *process) override;
+
+ Status InitializeConnection(std::unique_ptr<Connection> &&connection);
+
+protected:
+ MainLoop &m_mainloop;
+ MainLoop::ReadHandleUP m_network_handle_up;
+ const NativeProcessProtocol::Factory &m_process_factory;
+ lldb::tid_t m_current_tid = LLDB_INVALID_THREAD_ID;
+ lldb::tid_t m_continue_tid = LLDB_INVALID_THREAD_ID;
+ std::recursive_mutex m_debugged_process_mutex;
+ std::unique_ptr<NativeProcessProtocol> m_debugged_process_up;
+
+ Communication m_stdio_communication;
+ MainLoop::ReadHandleUP m_stdio_handle_up;
+
+ lldb::StateType m_inferior_prev_state = lldb::StateType::eStateInvalid;
+ llvm::StringMap<std::unique_ptr<llvm::MemoryBuffer>> m_xfer_buffer_map;
+ std::mutex m_saved_registers_mutex;
+ std::unordered_map<uint32_t, lldb::DataBufferSP> m_saved_registers_map;
+ uint32_t m_next_saved_registers_id = 1;
+ bool m_handshake_completed = false;
+
+ PacketResult SendONotification(const char *buffer, uint32_t len);
+
+ PacketResult SendWResponse(NativeProcessProtocol *process);
+
+ PacketResult SendStopReplyPacketForThread(lldb::tid_t tid);
+
+ PacketResult SendStopReasonForState(lldb::StateType process_state);
+
+ PacketResult Handle_k(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qProcessInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qC(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QSetDisableASLR(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QSetWorkingDir(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qGetWorkingDir(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_C(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_c(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vCont(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vCont_actions(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_stop_reason(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qRegisterInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qfThreadInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qsThreadInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_p(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_P(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_H(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_I(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_interrupt(StringExtractorGDBRemote &packet);
+
+ // Handles $m and $x packets.
+ PacketResult Handle_memory_read(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_M(StringExtractorGDBRemote &packet);
+
+ PacketResult
+ Handle_qMemoryRegionInfoSupported(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qMemoryRegionInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_Z(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_z(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_s(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qXfer(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QSaveRegisterState(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_jTraceStart(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_jTraceRead(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_jTraceStop(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_jTraceConfigRead(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QRestoreRegisterState(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_vAttach(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_D(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qThreadStopInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_jThreadsInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qWatchpointSupportInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qFileLoadAddress(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QPassSignals(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_g(StringExtractorGDBRemote &packet);
+
+ void SetCurrentThreadID(lldb::tid_t tid);
+
+ lldb::tid_t GetCurrentThreadID() const;
+
+ void SetContinueThreadID(lldb::tid_t tid);
+
+ lldb::tid_t GetContinueThreadID() const { return m_continue_tid; }
+
+ Status SetSTDIOFileDescriptor(int fd);
+
+ FileSpec FindModuleFile(const std::string &module_path,
+ const ArchSpec &arch) override;
+
+ llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
+ ReadXferObject(llvm::StringRef object, llvm::StringRef annex);
+
+private:
+ void HandleInferiorState_Exited(NativeProcessProtocol *process);
+
+ void HandleInferiorState_Stopped(NativeProcessProtocol *process);
+
+ NativeThreadProtocol *GetThreadFromSuffix(StringExtractorGDBRemote &packet);
+
+ uint32_t GetNextSavedRegistersID();
+
+ void MaybeCloseInferiorTerminalConnection();
+
+ void ClearProcessSpecificData();
+
+ void RegisterPacketHandlers();
+
+ void DataAvailableCallback();
+
+ void SendProcessOutput();
+
+ void StartSTDIOForwarding();
+
+ void StopSTDIOForwarding();
+
+ // For GDBRemoteCommunicationServerLLGS only
+ DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationServerLLGS);
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+#endif // liblldb_GDBRemoteCommunicationServerLLGS_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp
new file mode 100644
index 000000000000..6deb75f2f021
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp
@@ -0,0 +1,550 @@
+//===-- GDBRemoteCommunicationServerPlatform.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 "GDBRemoteCommunicationServerPlatform.h"
+
+#include <errno.h>
+
+#include <chrono>
+#include <csignal>
+#include <cstring>
+#include <mutex>
+#include <sstream>
+
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Threading.h"
+
+#include "lldb/Host/Config.h"
+#include "lldb/Host/ConnectionFileDescriptor.h"
+#include "lldb/Host/FileAction.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/JSON.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamGDBRemote.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StructuredData.h"
+#include "lldb/Utility/UriParser.h"
+
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+// GDBRemoteCommunicationServerPlatform constructor
+GDBRemoteCommunicationServerPlatform::GDBRemoteCommunicationServerPlatform(
+ const Socket::SocketProtocol socket_protocol, const char *socket_scheme)
+ : GDBRemoteCommunicationServerCommon("gdb-remote.server",
+ "gdb-remote.server.rx_packet"),
+ m_socket_protocol(socket_protocol), m_socket_scheme(socket_scheme),
+ m_spawned_pids_mutex(), m_port_map(), m_port_offset(0) {
+ m_pending_gdb_server.pid = LLDB_INVALID_PROCESS_ID;
+ m_pending_gdb_server.port = 0;
+
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qC,
+ &GDBRemoteCommunicationServerPlatform::Handle_qC);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qGetWorkingDir,
+ &GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qLaunchGDBServer,
+ &GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qQueryGDBServer,
+ &GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qKillSpawnedProcess,
+ &GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_qProcessInfo,
+ &GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir,
+ &GDBRemoteCommunicationServerPlatform::Handle_QSetWorkingDir);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_jSignalsInfo,
+ &GDBRemoteCommunicationServerPlatform::Handle_jSignalsInfo);
+
+ RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_interrupt,
+ [](StringExtractorGDBRemote packet, Status &error,
+ bool &interrupt, bool &quit) {
+ error.SetErrorString("interrupt received");
+ interrupt = true;
+ return PacketResult::Success;
+ });
+}
+
+// Destructor
+GDBRemoteCommunicationServerPlatform::~GDBRemoteCommunicationServerPlatform() {}
+
+Status GDBRemoteCommunicationServerPlatform::LaunchGDBServer(
+ const lldb_private::Args &args, std::string hostname, lldb::pid_t &pid,
+ uint16_t &port, std::string &socket_name) {
+ if (port == UINT16_MAX)
+ port = GetNextAvailablePort();
+
+ // Spawn a new thread to accept the port that gets bound after binding to
+ // port 0 (zero).
+
+ // ignore the hostname send from the remote end, just use the ip address that
+ // we're currently communicating with as the hostname
+
+ // Spawn a debugserver and try to get the port it listens to.
+ ProcessLaunchInfo debugserver_launch_info;
+ if (hostname.empty())
+ hostname = "127.0.0.1";
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
+ if (log)
+ log->Printf("Launching debugserver with: %s:%u...", hostname.c_str(), port);
+
+ // Do not run in a new session so that it can not linger after the platform
+ // closes.
+ debugserver_launch_info.SetLaunchInSeparateProcessGroup(false);
+ debugserver_launch_info.SetMonitorProcessCallback(
+ std::bind(&GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped,
+ this, std::placeholders::_1),
+ false);
+
+ std::ostringstream url;
+// debugserver does not accept the URL scheme prefix.
+#if !defined(__APPLE__)
+ url << m_socket_scheme << "://";
+#endif
+ uint16_t *port_ptr = &port;
+ if (m_socket_protocol == Socket::ProtocolTcp) {
+ llvm::StringRef platform_scheme;
+ llvm::StringRef platform_ip;
+ int platform_port;
+ llvm::StringRef platform_path;
+ std::string platform_uri = GetConnection()->GetURI();
+ bool ok = UriParser::Parse(platform_uri, platform_scheme, platform_ip,
+ platform_port, platform_path);
+ UNUSED_IF_ASSERT_DISABLED(ok);
+ assert(ok);
+ url << platform_ip.str() << ":" << port;
+ } else {
+ socket_name = GetDomainSocketPath("gdbserver").GetPath();
+ url << socket_name;
+ port_ptr = nullptr;
+ }
+
+ Status error = StartDebugserverProcess(
+ url.str().c_str(), nullptr, debugserver_launch_info, port_ptr, &args, -1);
+
+ pid = debugserver_launch_info.GetProcessID();
+ if (pid != LLDB_INVALID_PROCESS_ID) {
+ std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);
+ m_spawned_pids.insert(pid);
+ if (port > 0)
+ AssociatePortWithProcess(port, pid);
+ } else {
+ if (port > 0)
+ FreePort(port);
+ }
+ return error;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer(
+ StringExtractorGDBRemote &packet) {
+ // Spawn a local debugserver as a platform so we can then attach or launch a
+ // process...
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerPlatform::%s() called",
+ __FUNCTION__);
+
+ ConnectionFileDescriptor file_conn;
+ std::string hostname;
+ packet.SetFilePos(::strlen("qLaunchGDBServer;"));
+ llvm::StringRef name;
+ llvm::StringRef value;
+ uint16_t port = UINT16_MAX;
+ while (packet.GetNameColonValue(name, value)) {
+ if (name.equals("host"))
+ hostname = value;
+ else if (name.equals("port"))
+ value.getAsInteger(0, port);
+ }
+
+ lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID;
+ std::string socket_name;
+ Status error =
+ LaunchGDBServer(Args(), hostname, debugserver_pid, port, socket_name);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerPlatform::%s() debugserver "
+ "launch failed: %s",
+ __FUNCTION__, error.AsCString());
+ return SendErrorResponse(9);
+ }
+
+ if (log)
+ log->Printf("GDBRemoteCommunicationServerPlatform::%s() debugserver "
+ "launched successfully as pid %" PRIu64,
+ __FUNCTION__, debugserver_pid);
+
+ StreamGDBRemote response;
+ response.Printf("pid:%" PRIu64 ";port:%u;", debugserver_pid,
+ port + m_port_offset);
+ if (!socket_name.empty()) {
+ response.PutCString("socket_name:");
+ response.PutStringAsRawHex8(socket_name);
+ response.PutChar(';');
+ }
+
+ PacketResult packet_result = SendPacketNoLock(response.GetString());
+ if (packet_result != PacketResult::Success) {
+ if (debugserver_pid != LLDB_INVALID_PROCESS_ID)
+ Host::Kill(debugserver_pid, SIGINT);
+ }
+ return packet_result;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer(
+ StringExtractorGDBRemote &packet) {
+ if (m_pending_gdb_server.pid == LLDB_INVALID_PROCESS_ID)
+ return SendErrorResponse(4);
+
+ JSONObject::SP server_sp = std::make_shared<JSONObject>();
+ server_sp->SetObject("port",
+ std::make_shared<JSONNumber>(m_pending_gdb_server.port));
+ if (!m_pending_gdb_server.socket_name.empty())
+ server_sp->SetObject(
+ "socket_name",
+ std::make_shared<JSONString>(m_pending_gdb_server.socket_name.c_str()));
+
+ JSONArray server_list;
+ server_list.AppendObject(server_sp);
+
+ StreamGDBRemote response;
+ server_list.Write(response);
+
+ StreamGDBRemote escaped_response;
+ escaped_response.PutEscapedBytes(response.GetString().data(),
+ response.GetSize());
+ return SendPacketNoLock(escaped_response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("qKillSpawnedProcess:"));
+
+ lldb::pid_t pid = packet.GetU64(LLDB_INVALID_PROCESS_ID);
+
+ // verify that we know anything about this pid. Scope for locker
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);
+ if (m_spawned_pids.find(pid) == m_spawned_pids.end()) {
+ // not a pid we know about
+ return SendErrorResponse(10);
+ }
+ }
+
+ // go ahead and attempt to kill the spawned process
+ if (KillSpawnedProcess(pid))
+ return SendOKResponse();
+ else
+ return SendErrorResponse(11);
+}
+
+bool GDBRemoteCommunicationServerPlatform::KillSpawnedProcess(lldb::pid_t pid) {
+ // make sure we know about this process
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);
+ if (m_spawned_pids.find(pid) == m_spawned_pids.end())
+ return false;
+ }
+
+ // first try a SIGTERM (standard kill)
+ Host::Kill(pid, SIGTERM);
+
+ // check if that worked
+ for (size_t i = 0; i < 10; ++i) {
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);
+ if (m_spawned_pids.find(pid) == m_spawned_pids.end()) {
+ // it is now killed
+ return true;
+ }
+ }
+ usleep(10000);
+ }
+
+ // check one more time after the final usleep
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);
+ if (m_spawned_pids.find(pid) == m_spawned_pids.end())
+ return true;
+ }
+
+ // the launched process still lives. Now try killing it again, this time
+ // with an unblockable signal.
+ Host::Kill(pid, SIGKILL);
+
+ for (size_t i = 0; i < 10; ++i) {
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);
+ if (m_spawned_pids.find(pid) == m_spawned_pids.end()) {
+ // it is now killed
+ return true;
+ }
+ }
+ usleep(10000);
+ }
+
+ // check one more time after the final usleep Scope for locker
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);
+ if (m_spawned_pids.find(pid) == m_spawned_pids.end())
+ return true;
+ }
+
+ // no luck - the process still lives
+ return false;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo(
+ StringExtractorGDBRemote &packet) {
+ lldb::pid_t pid = m_process_launch_info.GetProcessID();
+ m_process_launch_info.Clear();
+
+ if (pid == LLDB_INVALID_PROCESS_ID)
+ return SendErrorResponse(1);
+
+ ProcessInstanceInfo proc_info;
+ if (!Host::GetProcessInfo(pid, proc_info))
+ return SendErrorResponse(1);
+
+ StreamString response;
+ CreateProcessInfoResponse_DebugServerStyle(proc_info, response);
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir(
+ StringExtractorGDBRemote &packet) {
+
+ llvm::SmallString<64> cwd;
+ if (std::error_code ec = llvm::sys::fs::current_path(cwd))
+ return SendErrorResponse(ec.value());
+
+ StreamString response;
+ response.PutBytesAsRawHex8(cwd.data(), cwd.size());
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerPlatform::Handle_QSetWorkingDir(
+ StringExtractorGDBRemote &packet) {
+ packet.SetFilePos(::strlen("QSetWorkingDir:"));
+ std::string path;
+ packet.GetHexByteString(path);
+
+ if (std::error_code ec = llvm::sys::fs::set_current_path(path))
+ return SendErrorResponse(ec.value());
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerPlatform::Handle_qC(
+ StringExtractorGDBRemote &packet) {
+ // NOTE: lldb should now be using qProcessInfo for process IDs. This path
+ // here
+ // should not be used. It is reporting process id instead of thread id. The
+ // correct answer doesn't seem to make much sense for lldb-platform.
+ // CONSIDER: flip to "unsupported".
+ lldb::pid_t pid = m_process_launch_info.GetProcessID();
+
+ StreamString response;
+ response.Printf("QC%" PRIx64, pid);
+
+ // If we launch a process and this GDB server is acting as a platform, then
+ // we need to clear the process launch state so we can start launching
+ // another process. In order to launch a process a bunch or packets need to
+ // be sent: environment packets, working directory, disable ASLR, and many
+ // more settings. When we launch a process we then need to know when to clear
+ // this information. Currently we are selecting the 'qC' packet as that
+ // packet which seems to make the most sense.
+ if (pid != LLDB_INVALID_PROCESS_ID) {
+ m_process_launch_info.Clear();
+ }
+
+ return SendPacketNoLock(response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerPlatform::Handle_jSignalsInfo(
+ StringExtractorGDBRemote &packet) {
+ StructuredData::Array signal_array;
+
+ lldb::UnixSignalsSP signals = UnixSignals::CreateForHost();
+ for (auto signo = signals->GetFirstSignalNumber();
+ signo != LLDB_INVALID_SIGNAL_NUMBER;
+ signo = signals->GetNextSignalNumber(signo)) {
+ auto dictionary = std::make_shared<StructuredData::Dictionary>();
+
+ dictionary->AddIntegerItem("signo", signo);
+ dictionary->AddStringItem("name", signals->GetSignalAsCString(signo));
+
+ bool suppress, stop, notify;
+ signals->GetSignalInfo(signo, suppress, stop, notify);
+ dictionary->AddBooleanItem("suppress", suppress);
+ dictionary->AddBooleanItem("stop", stop);
+ dictionary->AddBooleanItem("notify", notify);
+
+ signal_array.Push(dictionary);
+ }
+
+ StreamString response;
+ signal_array.Dump(response);
+ return SendPacketNoLock(response.GetString());
+}
+
+bool GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped(
+ lldb::pid_t pid) {
+ std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);
+ FreePortForProcess(pid);
+ m_spawned_pids.erase(pid);
+ return true;
+}
+
+Status GDBRemoteCommunicationServerPlatform::LaunchProcess() {
+ if (!m_process_launch_info.GetArguments().GetArgumentCount())
+ return Status("%s: no process command line specified to launch",
+ __FUNCTION__);
+
+ // specify the process monitor if not already set. This should generally be
+ // what happens since we need to reap started processes.
+ if (!m_process_launch_info.GetMonitorProcessCallback())
+ m_process_launch_info.SetMonitorProcessCallback(
+ std::bind(
+ &GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped,
+ this, std::placeholders::_1),
+ false);
+
+ Status error = Host::LaunchProcess(m_process_launch_info);
+ if (!error.Success()) {
+ fprintf(stderr, "%s: failed to launch executable %s", __FUNCTION__,
+ m_process_launch_info.GetArguments().GetArgumentAtIndex(0));
+ return error;
+ }
+
+ printf("Launched '%s' as process %" PRIu64 "...\n",
+ m_process_launch_info.GetArguments().GetArgumentAtIndex(0),
+ m_process_launch_info.GetProcessID());
+
+ // add to list of spawned processes. On an lldb-gdbserver, we would expect
+ // there to be only one.
+ const auto pid = m_process_launch_info.GetProcessID();
+ if (pid != LLDB_INVALID_PROCESS_ID) {
+ // add to spawned pids
+ std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex);
+ m_spawned_pids.insert(pid);
+ }
+
+ return error;
+}
+
+void GDBRemoteCommunicationServerPlatform::SetPortMap(PortMap &&port_map) {
+ m_port_map = port_map;
+}
+
+uint16_t GDBRemoteCommunicationServerPlatform::GetNextAvailablePort() {
+ if (m_port_map.empty())
+ return 0; // Bind to port zero and get a port, we didn't have any
+ // limitations
+
+ for (auto &pair : m_port_map) {
+ if (pair.second == LLDB_INVALID_PROCESS_ID) {
+ pair.second = ~(lldb::pid_t)LLDB_INVALID_PROCESS_ID;
+ return pair.first;
+ }
+ }
+ return UINT16_MAX;
+}
+
+bool GDBRemoteCommunicationServerPlatform::AssociatePortWithProcess(
+ uint16_t port, lldb::pid_t pid) {
+ PortMap::iterator pos = m_port_map.find(port);
+ if (pos != m_port_map.end()) {
+ pos->second = pid;
+ return true;
+ }
+ return false;
+}
+
+bool GDBRemoteCommunicationServerPlatform::FreePort(uint16_t port) {
+ PortMap::iterator pos = m_port_map.find(port);
+ if (pos != m_port_map.end()) {
+ pos->second = LLDB_INVALID_PROCESS_ID;
+ return true;
+ }
+ return false;
+}
+
+bool GDBRemoteCommunicationServerPlatform::FreePortForProcess(lldb::pid_t pid) {
+ if (!m_port_map.empty()) {
+ for (auto &pair : m_port_map) {
+ if (pair.second == pid) {
+ pair.second = LLDB_INVALID_PROCESS_ID;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+const FileSpec &GDBRemoteCommunicationServerPlatform::GetDomainSocketDir() {
+ static FileSpec g_domainsocket_dir;
+ static llvm::once_flag g_once_flag;
+
+ llvm::call_once(g_once_flag, []() {
+ const char *domainsocket_dir_env =
+ ::getenv("LLDB_DEBUGSERVER_DOMAINSOCKET_DIR");
+ if (domainsocket_dir_env != nullptr)
+ g_domainsocket_dir = FileSpec(domainsocket_dir_env);
+ else
+ g_domainsocket_dir = HostInfo::GetProcessTempDir();
+ });
+
+ return g_domainsocket_dir;
+}
+
+FileSpec
+GDBRemoteCommunicationServerPlatform::GetDomainSocketPath(const char *prefix) {
+ llvm::SmallString<128> socket_path;
+ llvm::SmallString<128> socket_name(
+ (llvm::StringRef(prefix) + ".%%%%%%").str());
+
+ FileSpec socket_path_spec(GetDomainSocketDir());
+ socket_path_spec.AppendPathComponent(socket_name.c_str());
+
+ llvm::sys::fs::createUniqueFile(socket_path_spec.GetCString(), socket_path);
+ return FileSpec(socket_path.c_str());
+}
+
+void GDBRemoteCommunicationServerPlatform::SetPortOffset(uint16_t port_offset) {
+ m_port_offset = port_offset;
+}
+
+void GDBRemoteCommunicationServerPlatform::SetPendingGdbServer(
+ lldb::pid_t pid, uint16_t port, const std::string &socket_name) {
+ m_pending_gdb_server.pid = pid;
+ m_pending_gdb_server.port = port;
+ m_pending_gdb_server.socket_name = socket_name;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h
new file mode 100644
index 000000000000..eacc99a012db
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h
@@ -0,0 +1,109 @@
+//===-- GDBRemoteCommunicationServerPlatform.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 liblldb_GDBRemoteCommunicationServerPlatform_h_
+#define liblldb_GDBRemoteCommunicationServerPlatform_h_
+
+#include <map>
+#include <mutex>
+#include <set>
+
+#include "GDBRemoteCommunicationServerCommon.h"
+#include "lldb/Host/Socket.h"
+
+namespace lldb_private {
+namespace process_gdb_remote {
+
+class GDBRemoteCommunicationServerPlatform
+ : public GDBRemoteCommunicationServerCommon {
+public:
+ typedef std::map<uint16_t, lldb::pid_t> PortMap;
+
+ GDBRemoteCommunicationServerPlatform(
+ const Socket::SocketProtocol socket_protocol, const char *socket_scheme);
+
+ ~GDBRemoteCommunicationServerPlatform() override;
+
+ Status LaunchProcess() override;
+
+ // Set both ports to zero to let the platform automatically bind to
+ // a port chosen by the OS.
+ void SetPortMap(PortMap &&port_map);
+
+ // If we are using a port map where we can only use certain ports,
+ // get the next available port.
+ //
+ // If we are using a port map and we are out of ports, return UINT16_MAX
+ //
+ // If we aren't using a port map, return 0 to indicate we should bind to
+ // port 0 and then figure out which port we used.
+ uint16_t GetNextAvailablePort();
+
+ bool AssociatePortWithProcess(uint16_t port, lldb::pid_t pid);
+
+ bool FreePort(uint16_t port);
+
+ bool FreePortForProcess(lldb::pid_t pid);
+
+ void SetPortOffset(uint16_t port_offset);
+
+ void SetInferiorArguments(const lldb_private::Args &args);
+
+ Status LaunchGDBServer(const lldb_private::Args &args, std::string hostname,
+ lldb::pid_t &pid, uint16_t &port,
+ std::string &socket_name);
+
+ void SetPendingGdbServer(lldb::pid_t pid, uint16_t port,
+ const std::string &socket_name);
+
+protected:
+ const Socket::SocketProtocol m_socket_protocol;
+ const std::string m_socket_scheme;
+ std::recursive_mutex m_spawned_pids_mutex;
+ std::set<lldb::pid_t> m_spawned_pids;
+
+ PortMap m_port_map;
+ uint16_t m_port_offset;
+ struct {
+ lldb::pid_t pid;
+ uint16_t port;
+ std::string socket_name;
+ } m_pending_gdb_server;
+
+ PacketResult Handle_qLaunchGDBServer(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qQueryGDBServer(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qKillSpawnedProcess(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qProcessInfo(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qGetWorkingDir(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_QSetWorkingDir(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_qC(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_jSignalsInfo(StringExtractorGDBRemote &packet);
+
+private:
+ bool KillSpawnedProcess(lldb::pid_t pid);
+
+ bool DebugserverProcessReaped(lldb::pid_t pid);
+
+ static const FileSpec &GetDomainSocketDir();
+
+ static FileSpec GetDomainSocketPath(const char *prefix);
+
+ DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationServerPlatform);
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+#endif // liblldb_GDBRemoteCommunicationServerPlatform_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp
new file mode 100644
index 000000000000..a77e659a55fa
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp
@@ -0,0 +1,958 @@
+//===-- GDBRemoteRegisterContext.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 "GDBRemoteRegisterContext.h"
+
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/StreamString.h"
+#include "ProcessGDBRemote.h"
+#include "ProcessGDBRemoteLog.h"
+#include "ThreadGDBRemote.h"
+#include "Utility/ARM_DWARF_Registers.h"
+#include "Utility/ARM_ehframe_Registers.h"
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+// GDBRemoteRegisterContext constructor
+GDBRemoteRegisterContext::GDBRemoteRegisterContext(
+ ThreadGDBRemote &thread, uint32_t concrete_frame_idx,
+ GDBRemoteDynamicRegisterInfo &reg_info, bool read_all_at_once)
+ : RegisterContext(thread, concrete_frame_idx), m_reg_info(reg_info),
+ m_reg_valid(), m_reg_data(), m_read_all_at_once(read_all_at_once) {
+ // Resize our vector of bools to contain one bool for every register. We will
+ // use these boolean values to know when a register value is valid in
+ // m_reg_data.
+ m_reg_valid.resize(reg_info.GetNumRegisters());
+
+ // Make a heap based buffer that is big enough to store all registers
+ DataBufferSP reg_data_sp(
+ new DataBufferHeap(reg_info.GetRegisterDataByteSize(), 0));
+ m_reg_data.SetData(reg_data_sp);
+ m_reg_data.SetByteOrder(thread.GetProcess()->GetByteOrder());
+}
+
+// Destructor
+GDBRemoteRegisterContext::~GDBRemoteRegisterContext() {}
+
+void GDBRemoteRegisterContext::InvalidateAllRegisters() {
+ SetAllRegisterValid(false);
+}
+
+void GDBRemoteRegisterContext::SetAllRegisterValid(bool b) {
+ std::vector<bool>::iterator pos, end = m_reg_valid.end();
+ for (pos = m_reg_valid.begin(); pos != end; ++pos)
+ *pos = b;
+}
+
+size_t GDBRemoteRegisterContext::GetRegisterCount() {
+ return m_reg_info.GetNumRegisters();
+}
+
+const RegisterInfo *
+GDBRemoteRegisterContext::GetRegisterInfoAtIndex(size_t reg) {
+ RegisterInfo *reg_info = m_reg_info.GetRegisterInfoAtIndex(reg);
+
+ if (reg_info && reg_info->dynamic_size_dwarf_expr_bytes) {
+ const ArchSpec &arch = m_thread.GetProcess()->GetTarget().GetArchitecture();
+ uint8_t reg_size = UpdateDynamicRegisterSize(arch, reg_info);
+ reg_info->byte_size = reg_size;
+ }
+ return reg_info;
+}
+
+size_t GDBRemoteRegisterContext::GetRegisterSetCount() {
+ return m_reg_info.GetNumRegisterSets();
+}
+
+const RegisterSet *GDBRemoteRegisterContext::GetRegisterSet(size_t reg_set) {
+ return m_reg_info.GetRegisterSet(reg_set);
+}
+
+bool GDBRemoteRegisterContext::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value) {
+ // Read the register
+ if (ReadRegisterBytes(reg_info, m_reg_data)) {
+ const bool partial_data_ok = false;
+ Status error(value.SetValueFromData(
+ reg_info, m_reg_data, reg_info->byte_offset, partial_data_ok));
+ return error.Success();
+ }
+ return false;
+}
+
+bool GDBRemoteRegisterContext::PrivateSetRegisterValue(
+ uint32_t reg, llvm::ArrayRef<uint8_t> data) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg);
+ if (reg_info == nullptr)
+ return false;
+
+ // Invalidate if needed
+ InvalidateIfNeeded(false);
+
+ const size_t reg_byte_size = reg_info->byte_size;
+ memcpy(const_cast<uint8_t *>(
+ m_reg_data.PeekData(reg_info->byte_offset, reg_byte_size)),
+ data.data(), std::min(data.size(), reg_byte_size));
+ bool success = data.size() >= reg_byte_size;
+ if (success) {
+ SetRegisterIsValid(reg, true);
+ } else if (data.size() > 0) {
+ // Only set register is valid to false if we copied some bytes, else leave
+ // it as it was.
+ SetRegisterIsValid(reg, false);
+ }
+ return success;
+}
+
+bool GDBRemoteRegisterContext::PrivateSetRegisterValue(uint32_t reg,
+ uint64_t new_reg_val) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg);
+ if (reg_info == nullptr)
+ return false;
+
+ // Early in process startup, we can get a thread that has an invalid byte
+ // order because the process hasn't been completely set up yet (see the ctor
+ // where the byte order is setfrom the process). If that's the case, we
+ // can't set the value here.
+ if (m_reg_data.GetByteOrder() == eByteOrderInvalid) {
+ return false;
+ }
+
+ // Invalidate if needed
+ InvalidateIfNeeded(false);
+
+ DataBufferSP buffer_sp(new DataBufferHeap(&new_reg_val, sizeof(new_reg_val)));
+ DataExtractor data(buffer_sp, endian::InlHostByteOrder(), sizeof(void *));
+
+ // If our register context and our register info disagree, which should never
+ // happen, don't overwrite past the end of the buffer.
+ if (m_reg_data.GetByteSize() < reg_info->byte_offset + reg_info->byte_size)
+ return false;
+
+ // Grab a pointer to where we are going to put this register
+ uint8_t *dst = const_cast<uint8_t *>(
+ m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size));
+
+ if (dst == nullptr)
+ return false;
+
+ if (data.CopyByteOrderedData(0, // src offset
+ reg_info->byte_size, // src length
+ dst, // dst
+ reg_info->byte_size, // dst length
+ m_reg_data.GetByteOrder())) // dst byte order
+ {
+ SetRegisterIsValid(reg, true);
+ return true;
+ }
+ return false;
+}
+
+// Helper function for GDBRemoteRegisterContext::ReadRegisterBytes().
+bool GDBRemoteRegisterContext::GetPrimordialRegister(
+ const RegisterInfo *reg_info, GDBRemoteCommunicationClient &gdb_comm) {
+ const uint32_t lldb_reg = reg_info->kinds[eRegisterKindLLDB];
+ const uint32_t remote_reg = reg_info->kinds[eRegisterKindProcessPlugin];
+
+ if (DataBufferSP buffer_sp =
+ gdb_comm.ReadRegister(m_thread.GetProtocolID(), remote_reg))
+ return PrivateSetRegisterValue(
+ lldb_reg, llvm::ArrayRef<uint8_t>(buffer_sp->GetBytes(),
+ buffer_sp->GetByteSize()));
+ return false;
+}
+
+bool GDBRemoteRegisterContext::ReadRegisterBytes(const RegisterInfo *reg_info,
+ DataExtractor &data) {
+ ExecutionContext exe_ctx(CalculateThread());
+
+ Process *process = exe_ctx.GetProcessPtr();
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (process == nullptr || thread == nullptr)
+ return false;
+
+ GDBRemoteCommunicationClient &gdb_comm(
+ ((ProcessGDBRemote *)process)->GetGDBRemote());
+
+ InvalidateIfNeeded(false);
+
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ if (!GetRegisterIsValid(reg)) {
+ if (m_read_all_at_once) {
+ if (DataBufferSP buffer_sp =
+ gdb_comm.ReadAllRegisters(m_thread.GetProtocolID())) {
+ memcpy(const_cast<uint8_t *>(m_reg_data.GetDataStart()),
+ buffer_sp->GetBytes(),
+ std::min(buffer_sp->GetByteSize(), m_reg_data.GetByteSize()));
+ if (buffer_sp->GetByteSize() >= m_reg_data.GetByteSize()) {
+ SetAllRegisterValid(true);
+ return true;
+ } else {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_THREAD |
+ GDBR_LOG_PACKETS));
+ if (log)
+ log->Printf ("error: GDBRemoteRegisterContext::ReadRegisterBytes tried to read the "
+ "entire register context at once, expected at least %" PRId64 " bytes "
+ "but only got %" PRId64 " bytes.", m_reg_data.GetByteSize(),
+ buffer_sp->GetByteSize());
+ }
+ }
+ return false;
+ }
+ if (reg_info->value_regs) {
+ // Process this composite register request by delegating to the
+ // constituent primordial registers.
+
+ // Index of the primordial register.
+ bool success = true;
+ for (uint32_t idx = 0; success; ++idx) {
+ const uint32_t prim_reg = reg_info->value_regs[idx];
+ if (prim_reg == LLDB_INVALID_REGNUM)
+ break;
+ // We have a valid primordial register as our constituent. Grab the
+ // corresponding register info.
+ const RegisterInfo *prim_reg_info = GetRegisterInfoAtIndex(prim_reg);
+ if (prim_reg_info == nullptr)
+ success = false;
+ else {
+ // Read the containing register if it hasn't already been read
+ if (!GetRegisterIsValid(prim_reg))
+ success = GetPrimordialRegister(prim_reg_info, gdb_comm);
+ }
+ }
+
+ if (success) {
+ // If we reach this point, all primordial register requests have
+ // succeeded. Validate this composite register.
+ SetRegisterIsValid(reg_info, true);
+ }
+ } else {
+ // Get each register individually
+ GetPrimordialRegister(reg_info, gdb_comm);
+ }
+
+ // Make sure we got a valid register value after reading it
+ if (!GetRegisterIsValid(reg))
+ return false;
+ }
+
+ if (&data != &m_reg_data) {
+ assert(m_reg_data.GetByteSize() >=
+ reg_info->byte_offset + reg_info->byte_size);
+ // If our register context and our register info disagree, which should
+ // never happen, don't read past the end of the buffer.
+ if (m_reg_data.GetByteSize() < reg_info->byte_offset + reg_info->byte_size)
+ return false;
+
+ // If we aren't extracting into our own buffer (which only happens when
+ // this function is called from ReadRegisterValue(uint32_t, Scalar&)) then
+ // we transfer bytes from our buffer into the data buffer that was passed
+ // in
+
+ data.SetByteOrder(m_reg_data.GetByteOrder());
+ data.SetData(m_reg_data, reg_info->byte_offset, reg_info->byte_size);
+ }
+ return true;
+}
+
+bool GDBRemoteRegisterContext::WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &value) {
+ DataExtractor data;
+ if (value.GetData(data))
+ return WriteRegisterBytes(reg_info, data, 0);
+ return false;
+}
+
+// Helper function for GDBRemoteRegisterContext::WriteRegisterBytes().
+bool GDBRemoteRegisterContext::SetPrimordialRegister(
+ const RegisterInfo *reg_info, GDBRemoteCommunicationClient &gdb_comm) {
+ StreamString packet;
+ StringExtractorGDBRemote response;
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ // Invalidate just this register
+ SetRegisterIsValid(reg, false);
+
+ return gdb_comm.WriteRegister(
+ m_thread.GetProtocolID(), reg_info->kinds[eRegisterKindProcessPlugin],
+ {m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size),
+ reg_info->byte_size});
+}
+
+bool GDBRemoteRegisterContext::WriteRegisterBytes(const RegisterInfo *reg_info,
+ DataExtractor &data,
+ uint32_t data_offset) {
+ ExecutionContext exe_ctx(CalculateThread());
+
+ Process *process = exe_ctx.GetProcessPtr();
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (process == nullptr || thread == nullptr)
+ return false;
+
+ GDBRemoteCommunicationClient &gdb_comm(
+ ((ProcessGDBRemote *)process)->GetGDBRemote());
+
+ assert(m_reg_data.GetByteSize() >=
+ reg_info->byte_offset + reg_info->byte_size);
+
+ // If our register context and our register info disagree, which should never
+ // happen, don't overwrite past the end of the buffer.
+ if (m_reg_data.GetByteSize() < reg_info->byte_offset + reg_info->byte_size)
+ return false;
+
+ // Grab a pointer to where we are going to put this register
+ uint8_t *dst = const_cast<uint8_t *>(
+ m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size));
+
+ if (dst == nullptr)
+ return false;
+
+ if (data.CopyByteOrderedData(data_offset, // src offset
+ reg_info->byte_size, // src length
+ dst, // dst
+ reg_info->byte_size, // dst length
+ m_reg_data.GetByteOrder())) // dst byte order
+ {
+ GDBRemoteClientBase::Lock lock(gdb_comm, false);
+ if (lock) {
+ if (m_read_all_at_once) {
+ // Invalidate all register values
+ InvalidateIfNeeded(true);
+
+ // Set all registers in one packet
+ if (gdb_comm.WriteAllRegisters(
+ m_thread.GetProtocolID(),
+ {m_reg_data.GetDataStart(), size_t(m_reg_data.GetByteSize())}))
+
+ {
+ SetAllRegisterValid(false);
+ return true;
+ }
+ } else {
+ bool success = true;
+
+ if (reg_info->value_regs) {
+ // This register is part of another register. In this case we read
+ // the actual register data for any "value_regs", and once all that
+ // data is read, we will have enough data in our register context
+ // bytes for the value of this register
+
+ // Invalidate this composite register first.
+
+ for (uint32_t idx = 0; success; ++idx) {
+ const uint32_t reg = reg_info->value_regs[idx];
+ if (reg == LLDB_INVALID_REGNUM)
+ break;
+ // We have a valid primordial register as our constituent. Grab the
+ // corresponding register info.
+ const RegisterInfo *value_reg_info = GetRegisterInfoAtIndex(reg);
+ if (value_reg_info == nullptr)
+ success = false;
+ else
+ success = SetPrimordialRegister(value_reg_info, gdb_comm);
+ }
+ } else {
+ // This is an actual register, write it
+ success = SetPrimordialRegister(reg_info, gdb_comm);
+ }
+
+ // Check if writing this register will invalidate any other register
+ // values? If so, invalidate them
+ if (reg_info->invalidate_regs) {
+ for (uint32_t idx = 0, reg = reg_info->invalidate_regs[0];
+ reg != LLDB_INVALID_REGNUM;
+ reg = reg_info->invalidate_regs[++idx]) {
+ SetRegisterIsValid(reg, false);
+ }
+ }
+
+ return success;
+ }
+ } else {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_THREAD |
+ GDBR_LOG_PACKETS));
+ if (log) {
+ if (log->GetVerbose()) {
+ StreamString strm;
+ gdb_comm.DumpHistory(strm);
+ log->Printf("error: failed to get packet sequence mutex, not sending "
+ "write register for \"%s\":\n%s",
+ reg_info->name, strm.GetData());
+ } else
+ log->Printf("error: failed to get packet sequence mutex, not sending "
+ "write register for \"%s\"",
+ reg_info->name);
+ }
+ }
+ }
+ return false;
+}
+
+bool GDBRemoteRegisterContext::ReadAllRegisterValues(
+ RegisterCheckpoint &reg_checkpoint) {
+ ExecutionContext exe_ctx(CalculateThread());
+
+ Process *process = exe_ctx.GetProcessPtr();
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (process == nullptr || thread == nullptr)
+ return false;
+
+ GDBRemoteCommunicationClient &gdb_comm(
+ ((ProcessGDBRemote *)process)->GetGDBRemote());
+
+ uint32_t save_id = 0;
+ if (gdb_comm.SaveRegisterState(thread->GetProtocolID(), save_id)) {
+ reg_checkpoint.SetID(save_id);
+ reg_checkpoint.GetData().reset();
+ return true;
+ } else {
+ reg_checkpoint.SetID(0); // Invalid save ID is zero
+ return ReadAllRegisterValues(reg_checkpoint.GetData());
+ }
+}
+
+bool GDBRemoteRegisterContext::WriteAllRegisterValues(
+ const RegisterCheckpoint &reg_checkpoint) {
+ uint32_t save_id = reg_checkpoint.GetID();
+ if (save_id != 0) {
+ ExecutionContext exe_ctx(CalculateThread());
+
+ Process *process = exe_ctx.GetProcessPtr();
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (process == nullptr || thread == nullptr)
+ return false;
+
+ GDBRemoteCommunicationClient &gdb_comm(
+ ((ProcessGDBRemote *)process)->GetGDBRemote());
+
+ return gdb_comm.RestoreRegisterState(m_thread.GetProtocolID(), save_id);
+ } else {
+ return WriteAllRegisterValues(reg_checkpoint.GetData());
+ }
+}
+
+bool GDBRemoteRegisterContext::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ ExecutionContext exe_ctx(CalculateThread());
+
+ Process *process = exe_ctx.GetProcessPtr();
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (process == nullptr || thread == nullptr)
+ return false;
+
+ GDBRemoteCommunicationClient &gdb_comm(
+ ((ProcessGDBRemote *)process)->GetGDBRemote());
+
+ const bool use_g_packet =
+ !gdb_comm.AvoidGPackets((ProcessGDBRemote *)process);
+
+ GDBRemoteClientBase::Lock lock(gdb_comm, false);
+ if (lock) {
+ if (gdb_comm.SyncThreadState(m_thread.GetProtocolID()))
+ InvalidateAllRegisters();
+
+ if (use_g_packet &&
+ (data_sp = gdb_comm.ReadAllRegisters(m_thread.GetProtocolID())))
+ return true;
+
+ // We're going to read each register
+ // individually and store them as binary data in a buffer.
+ const RegisterInfo *reg_info;
+
+ for (uint32_t i = 0; (reg_info = GetRegisterInfoAtIndex(i)) != nullptr;
+ i++) {
+ if (reg_info
+ ->value_regs) // skip registers that are slices of real registers
+ continue;
+ ReadRegisterBytes(reg_info, m_reg_data);
+ // ReadRegisterBytes saves the contents of the register in to the
+ // m_reg_data buffer
+ }
+ data_sp = std::make_shared<DataBufferHeap>(
+ m_reg_data.GetDataStart(), m_reg_info.GetRegisterDataByteSize());
+ return true;
+ } else {
+
+ Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_THREAD |
+ GDBR_LOG_PACKETS));
+ if (log) {
+ if (log->GetVerbose()) {
+ StreamString strm;
+ gdb_comm.DumpHistory(strm);
+ log->Printf("error: failed to get packet sequence mutex, not sending "
+ "read all registers:\n%s",
+ strm.GetData());
+ } else
+ log->Printf("error: failed to get packet sequence mutex, not sending "
+ "read all registers");
+ }
+ }
+
+ data_sp.reset();
+ return false;
+}
+
+bool GDBRemoteRegisterContext::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ if (!data_sp || data_sp->GetBytes() == nullptr || data_sp->GetByteSize() == 0)
+ return false;
+
+ ExecutionContext exe_ctx(CalculateThread());
+
+ Process *process = exe_ctx.GetProcessPtr();
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (process == nullptr || thread == nullptr)
+ return false;
+
+ GDBRemoteCommunicationClient &gdb_comm(
+ ((ProcessGDBRemote *)process)->GetGDBRemote());
+
+ const bool use_g_packet =
+ !gdb_comm.AvoidGPackets((ProcessGDBRemote *)process);
+
+ GDBRemoteClientBase::Lock lock(gdb_comm, false);
+ if (lock) {
+ // The data_sp contains the G response packet.
+ if (use_g_packet) {
+ if (gdb_comm.WriteAllRegisters(
+ m_thread.GetProtocolID(),
+ {data_sp->GetBytes(), size_t(data_sp->GetByteSize())}))
+ return true;
+
+ uint32_t num_restored = 0;
+ // We need to manually go through all of the registers and restore them
+ // manually
+ DataExtractor restore_data(data_sp, m_reg_data.GetByteOrder(),
+ m_reg_data.GetAddressByteSize());
+
+ const RegisterInfo *reg_info;
+
+ // The g packet contents may either include the slice registers
+ // (registers defined in terms of other registers, e.g. eax is a subset
+ // of rax) or not. The slice registers should NOT be in the g packet,
+ // but some implementations may incorrectly include them.
+ //
+ // If the slice registers are included in the packet, we must step over
+ // the slice registers when parsing the packet -- relying on the
+ // RegisterInfo byte_offset field would be incorrect. If the slice
+ // registers are not included, then using the byte_offset values into the
+ // data buffer is the best way to find individual register values.
+
+ uint64_t size_including_slice_registers = 0;
+ uint64_t size_not_including_slice_registers = 0;
+ uint64_t size_by_highest_offset = 0;
+
+ for (uint32_t reg_idx = 0;
+ (reg_info = GetRegisterInfoAtIndex(reg_idx)) != nullptr; ++reg_idx) {
+ size_including_slice_registers += reg_info->byte_size;
+ if (reg_info->value_regs == nullptr)
+ size_not_including_slice_registers += reg_info->byte_size;
+ if (reg_info->byte_offset >= size_by_highest_offset)
+ size_by_highest_offset = reg_info->byte_offset + reg_info->byte_size;
+ }
+
+ bool use_byte_offset_into_buffer;
+ if (size_by_highest_offset == restore_data.GetByteSize()) {
+ // The size of the packet agrees with the highest offset: + size in the
+ // register file
+ use_byte_offset_into_buffer = true;
+ } else if (size_not_including_slice_registers ==
+ restore_data.GetByteSize()) {
+ // The size of the packet is the same as concatenating all of the
+ // registers sequentially, skipping the slice registers
+ use_byte_offset_into_buffer = true;
+ } else if (size_including_slice_registers == restore_data.GetByteSize()) {
+ // The slice registers are present in the packet (when they shouldn't
+ // be). Don't try to use the RegisterInfo byte_offset into the
+ // restore_data, it will point to the wrong place.
+ use_byte_offset_into_buffer = false;
+ } else {
+ // None of our expected sizes match the actual g packet data we're
+ // looking at. The most conservative approach here is to use the
+ // running total byte offset.
+ use_byte_offset_into_buffer = false;
+ }
+
+ // In case our register definitions don't include the correct offsets,
+ // keep track of the size of each reg & compute offset based on that.
+ uint32_t running_byte_offset = 0;
+ for (uint32_t reg_idx = 0;
+ (reg_info = GetRegisterInfoAtIndex(reg_idx)) != nullptr;
+ ++reg_idx, running_byte_offset += reg_info->byte_size) {
+ // Skip composite aka slice registers (e.g. eax is a slice of rax).
+ if (reg_info->value_regs)
+ continue;
+
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ uint32_t register_offset;
+ if (use_byte_offset_into_buffer) {
+ register_offset = reg_info->byte_offset;
+ } else {
+ register_offset = running_byte_offset;
+ }
+
+ const uint32_t reg_byte_size = reg_info->byte_size;
+
+ const uint8_t *restore_src =
+ restore_data.PeekData(register_offset, reg_byte_size);
+ if (restore_src) {
+ SetRegisterIsValid(reg, false);
+ if (gdb_comm.WriteRegister(
+ m_thread.GetProtocolID(),
+ reg_info->kinds[eRegisterKindProcessPlugin],
+ {restore_src, reg_byte_size}))
+ ++num_restored;
+ }
+ }
+ return num_restored > 0;
+ } else {
+ // For the use_g_packet == false case, we're going to write each register
+ // individually. The data buffer is binary data in this case, instead of
+ // ascii characters.
+
+ bool arm64_debugserver = false;
+ if (m_thread.GetProcess().get()) {
+ const ArchSpec &arch =
+ m_thread.GetProcess()->GetTarget().GetArchitecture();
+ if (arch.IsValid() && arch.GetMachine() == llvm::Triple::aarch64 &&
+ arch.GetTriple().getVendor() == llvm::Triple::Apple &&
+ arch.GetTriple().getOS() == llvm::Triple::IOS) {
+ arm64_debugserver = true;
+ }
+ }
+ uint32_t num_restored = 0;
+ const RegisterInfo *reg_info;
+ for (uint32_t i = 0; (reg_info = GetRegisterInfoAtIndex(i)) != nullptr;
+ i++) {
+ if (reg_info->value_regs) // skip registers that are slices of real
+ // registers
+ continue;
+ // Skip the fpsr and fpcr floating point status/control register
+ // writing to work around a bug in an older version of debugserver that
+ // would lead to register context corruption when writing fpsr/fpcr.
+ if (arm64_debugserver && (strcmp(reg_info->name, "fpsr") == 0 ||
+ strcmp(reg_info->name, "fpcr") == 0)) {
+ continue;
+ }
+
+ SetRegisterIsValid(reg_info, false);
+ if (gdb_comm.WriteRegister(m_thread.GetProtocolID(),
+ reg_info->kinds[eRegisterKindProcessPlugin],
+ {data_sp->GetBytes() + reg_info->byte_offset,
+ reg_info->byte_size}))
+ ++num_restored;
+ }
+ return num_restored > 0;
+ }
+ } else {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_THREAD |
+ GDBR_LOG_PACKETS));
+ if (log) {
+ if (log->GetVerbose()) {
+ StreamString strm;
+ gdb_comm.DumpHistory(strm);
+ log->Printf("error: failed to get packet sequence mutex, not sending "
+ "write all registers:\n%s",
+ strm.GetData());
+ } else
+ log->Printf("error: failed to get packet sequence mutex, not sending "
+ "write all registers");
+ }
+ }
+ return false;
+}
+
+uint32_t GDBRemoteRegisterContext::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ return m_reg_info.ConvertRegisterKindToRegisterNumber(kind, num);
+}
+
+void GDBRemoteDynamicRegisterInfo::HardcodeARMRegisters(bool from_scratch) {
+ // For Advanced SIMD and VFP register mapping.
+ static uint32_t g_d0_regs[] = {26, 27, LLDB_INVALID_REGNUM}; // (s0, s1)
+ static uint32_t g_d1_regs[] = {28, 29, LLDB_INVALID_REGNUM}; // (s2, s3)
+ static uint32_t g_d2_regs[] = {30, 31, LLDB_INVALID_REGNUM}; // (s4, s5)
+ static uint32_t g_d3_regs[] = {32, 33, LLDB_INVALID_REGNUM}; // (s6, s7)
+ static uint32_t g_d4_regs[] = {34, 35, LLDB_INVALID_REGNUM}; // (s8, s9)
+ static uint32_t g_d5_regs[] = {36, 37, LLDB_INVALID_REGNUM}; // (s10, s11)
+ static uint32_t g_d6_regs[] = {38, 39, LLDB_INVALID_REGNUM}; // (s12, s13)
+ static uint32_t g_d7_regs[] = {40, 41, LLDB_INVALID_REGNUM}; // (s14, s15)
+ static uint32_t g_d8_regs[] = {42, 43, LLDB_INVALID_REGNUM}; // (s16, s17)
+ static uint32_t g_d9_regs[] = {44, 45, LLDB_INVALID_REGNUM}; // (s18, s19)
+ static uint32_t g_d10_regs[] = {46, 47, LLDB_INVALID_REGNUM}; // (s20, s21)
+ static uint32_t g_d11_regs[] = {48, 49, LLDB_INVALID_REGNUM}; // (s22, s23)
+ static uint32_t g_d12_regs[] = {50, 51, LLDB_INVALID_REGNUM}; // (s24, s25)
+ static uint32_t g_d13_regs[] = {52, 53, LLDB_INVALID_REGNUM}; // (s26, s27)
+ static uint32_t g_d14_regs[] = {54, 55, LLDB_INVALID_REGNUM}; // (s28, s29)
+ static uint32_t g_d15_regs[] = {56, 57, LLDB_INVALID_REGNUM}; // (s30, s31)
+ static uint32_t g_q0_regs[] = {
+ 26, 27, 28, 29, LLDB_INVALID_REGNUM}; // (d0, d1) -> (s0, s1, s2, s3)
+ static uint32_t g_q1_regs[] = {
+ 30, 31, 32, 33, LLDB_INVALID_REGNUM}; // (d2, d3) -> (s4, s5, s6, s7)
+ static uint32_t g_q2_regs[] = {
+ 34, 35, 36, 37, LLDB_INVALID_REGNUM}; // (d4, d5) -> (s8, s9, s10, s11)
+ static uint32_t g_q3_regs[] = {
+ 38, 39, 40, 41, LLDB_INVALID_REGNUM}; // (d6, d7) -> (s12, s13, s14, s15)
+ static uint32_t g_q4_regs[] = {
+ 42, 43, 44, 45, LLDB_INVALID_REGNUM}; // (d8, d9) -> (s16, s17, s18, s19)
+ static uint32_t g_q5_regs[] = {
+ 46, 47, 48, 49,
+ LLDB_INVALID_REGNUM}; // (d10, d11) -> (s20, s21, s22, s23)
+ static uint32_t g_q6_regs[] = {
+ 50, 51, 52, 53,
+ LLDB_INVALID_REGNUM}; // (d12, d13) -> (s24, s25, s26, s27)
+ static uint32_t g_q7_regs[] = {
+ 54, 55, 56, 57,
+ LLDB_INVALID_REGNUM}; // (d14, d15) -> (s28, s29, s30, s31)
+ static uint32_t g_q8_regs[] = {59, 60, LLDB_INVALID_REGNUM}; // (d16, d17)
+ static uint32_t g_q9_regs[] = {61, 62, LLDB_INVALID_REGNUM}; // (d18, d19)
+ static uint32_t g_q10_regs[] = {63, 64, LLDB_INVALID_REGNUM}; // (d20, d21)
+ static uint32_t g_q11_regs[] = {65, 66, LLDB_INVALID_REGNUM}; // (d22, d23)
+ static uint32_t g_q12_regs[] = {67, 68, LLDB_INVALID_REGNUM}; // (d24, d25)
+ static uint32_t g_q13_regs[] = {69, 70, LLDB_INVALID_REGNUM}; // (d26, d27)
+ static uint32_t g_q14_regs[] = {71, 72, LLDB_INVALID_REGNUM}; // (d28, d29)
+ static uint32_t g_q15_regs[] = {73, 74, LLDB_INVALID_REGNUM}; // (d30, d31)
+
+ // This is our array of composite registers, with each element coming from
+ // the above register mappings.
+ static uint32_t *g_composites[] = {
+ g_d0_regs, g_d1_regs, g_d2_regs, g_d3_regs, g_d4_regs, g_d5_regs,
+ g_d6_regs, g_d7_regs, g_d8_regs, g_d9_regs, g_d10_regs, g_d11_regs,
+ g_d12_regs, g_d13_regs, g_d14_regs, g_d15_regs, g_q0_regs, g_q1_regs,
+ g_q2_regs, g_q3_regs, g_q4_regs, g_q5_regs, g_q6_regs, g_q7_regs,
+ g_q8_regs, g_q9_regs, g_q10_regs, g_q11_regs, g_q12_regs, g_q13_regs,
+ g_q14_regs, g_q15_regs};
+
+ // clang-format off
+ static RegisterInfo g_register_infos[] = {
+// NAME ALT SZ OFF ENCODING FORMAT EH_FRAME DWARF GENERIC PROCESS PLUGIN LLDB VALUE REGS INVALIDATE REGS SIZE EXPR SIZE LEN
+// ====== ====== === === ============= ========== =================== =================== ====================== ============= ==== ========== =============== ========= ========
+ { "r0", "arg1", 4, 0, eEncodingUint, eFormatHex, { ehframe_r0, dwarf_r0, LLDB_REGNUM_GENERIC_ARG1,0, 0 }, nullptr, nullptr, nullptr, 0 },
+ { "r1", "arg2", 4, 0, eEncodingUint, eFormatHex, { ehframe_r1, dwarf_r1, LLDB_REGNUM_GENERIC_ARG2,1, 1 }, nullptr, nullptr, nullptr, 0 },
+ { "r2", "arg3", 4, 0, eEncodingUint, eFormatHex, { ehframe_r2, dwarf_r2, LLDB_REGNUM_GENERIC_ARG3,2, 2 }, nullptr, nullptr, nullptr, 0 },
+ { "r3", "arg4", 4, 0, eEncodingUint, eFormatHex, { ehframe_r3, dwarf_r3, LLDB_REGNUM_GENERIC_ARG4,3, 3 }, nullptr, nullptr, nullptr, 0 },
+ { "r4", nullptr, 4, 0, eEncodingUint, eFormatHex, { ehframe_r4, dwarf_r4, LLDB_INVALID_REGNUM, 4, 4 }, nullptr, nullptr, nullptr, 0 },
+ { "r5", nullptr, 4, 0, eEncodingUint, eFormatHex, { ehframe_r5, dwarf_r5, LLDB_INVALID_REGNUM, 5, 5 }, nullptr, nullptr, nullptr, 0 },
+ { "r6", nullptr, 4, 0, eEncodingUint, eFormatHex, { ehframe_r6, dwarf_r6, LLDB_INVALID_REGNUM, 6, 6 }, nullptr, nullptr, nullptr, 0 },
+ { "r7", "fp", 4, 0, eEncodingUint, eFormatHex, { ehframe_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP, 7, 7 }, nullptr, nullptr, nullptr, 0 },
+ { "r8", nullptr, 4, 0, eEncodingUint, eFormatHex, { ehframe_r8, dwarf_r8, LLDB_INVALID_REGNUM, 8, 8 }, nullptr, nullptr, nullptr, 0 },
+ { "r9", nullptr, 4, 0, eEncodingUint, eFormatHex, { ehframe_r9, dwarf_r9, LLDB_INVALID_REGNUM, 9, 9 }, nullptr, nullptr, nullptr, 0 },
+ { "r10", nullptr, 4, 0, eEncodingUint, eFormatHex, { ehframe_r10, dwarf_r10, LLDB_INVALID_REGNUM, 10, 10 }, nullptr, nullptr, nullptr, 0 },
+ { "r11", nullptr, 4, 0, eEncodingUint, eFormatHex, { ehframe_r11, dwarf_r11, LLDB_INVALID_REGNUM, 11, 11 }, nullptr, nullptr, nullptr, 0 },
+ { "r12", nullptr, 4, 0, eEncodingUint, eFormatHex, { ehframe_r12, dwarf_r12, LLDB_INVALID_REGNUM, 12, 12 }, nullptr, nullptr, nullptr, 0 },
+ { "sp", "r13", 4, 0, eEncodingUint, eFormatHex, { ehframe_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP, 13, 13 }, nullptr, nullptr, nullptr, 0 },
+ { "lr", "r14", 4, 0, eEncodingUint, eFormatHex, { ehframe_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA, 14, 14 }, nullptr, nullptr, nullptr, 0 },
+ { "pc", "r15", 4, 0, eEncodingUint, eFormatHex, { ehframe_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, 15, 15 }, nullptr, nullptr, nullptr, 0 },
+ { "f0", nullptr, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 16, 16 }, nullptr, nullptr, nullptr, 0 },
+ { "f1", nullptr, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 17, 17 }, nullptr, nullptr, nullptr, 0 },
+ { "f2", nullptr, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 18, 18 }, nullptr, nullptr, nullptr, 0 },
+ { "f3", nullptr, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 19, 19 }, nullptr, nullptr, nullptr, 0 },
+ { "f4", nullptr, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 20, 20 }, nullptr, nullptr, nullptr, 0 },
+ { "f5", nullptr, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 21, 21 }, nullptr, nullptr, nullptr, 0 },
+ { "f6", nullptr, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 22, 22 }, nullptr, nullptr, nullptr, 0 },
+ { "f7", nullptr, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 23, 23 }, nullptr, nullptr, nullptr, 0 },
+ { "fps", nullptr, 4, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 24, 24 }, nullptr, nullptr, nullptr, 0 },
+ { "cpsr","flags", 4, 0, eEncodingUint, eFormatHex, { ehframe_cpsr, dwarf_cpsr, LLDB_INVALID_REGNUM, 25, 25 }, nullptr, nullptr, nullptr, 0 },
+ { "s0", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM, 26, 26 }, nullptr, nullptr, nullptr, 0 },
+ { "s1", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM, 27, 27 }, nullptr, nullptr, nullptr, 0 },
+ { "s2", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM, 28, 28 }, nullptr, nullptr, nullptr, 0 },
+ { "s3", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM, 29, 29 }, nullptr, nullptr, nullptr, 0 },
+ { "s4", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM, 30, 30 }, nullptr, nullptr, nullptr, 0 },
+ { "s5", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM, 31, 31 }, nullptr, nullptr, nullptr, 0 },
+ { "s6", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM, 32, 32 }, nullptr, nullptr, nullptr, 0 },
+ { "s7", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM, 33, 33 }, nullptr, nullptr, nullptr, 0 },
+ { "s8", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM, 34, 34 }, nullptr, nullptr, nullptr, 0 },
+ { "s9", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM, 35, 35 }, nullptr, nullptr, nullptr, 0 },
+ { "s10", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM, 36, 36 }, nullptr, nullptr, nullptr, 0 },
+ { "s11", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM, 37, 37 }, nullptr, nullptr, nullptr, 0 },
+ { "s12", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM, 38, 38 }, nullptr, nullptr, nullptr, 0 },
+ { "s13", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM, 39, 39 }, nullptr, nullptr, nullptr, 0 },
+ { "s14", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM, 40, 40 }, nullptr, nullptr, nullptr, 0 },
+ { "s15", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM, 41, 41 }, nullptr, nullptr, nullptr, 0 },
+ { "s16", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM, 42, 42 }, nullptr, nullptr, nullptr, 0 },
+ { "s17", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM, 43, 43 }, nullptr, nullptr, nullptr, 0 },
+ { "s18", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM, 44, 44 }, nullptr, nullptr, nullptr, 0 },
+ { "s19", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM, 45, 45 }, nullptr, nullptr, nullptr, 0 },
+ { "s20", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM, 46, 46 }, nullptr, nullptr, nullptr, 0 },
+ { "s21", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM, 47, 47 }, nullptr, nullptr, nullptr, 0 },
+ { "s22", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM, 48, 48 }, nullptr, nullptr, nullptr, 0 },
+ { "s23", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM, 49, 49 }, nullptr, nullptr, nullptr, 0 },
+ { "s24", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM, 50, 50 }, nullptr, nullptr, nullptr, 0 },
+ { "s25", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM, 51, 51 }, nullptr, nullptr, nullptr, 0 },
+ { "s26", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM, 52, 52 }, nullptr, nullptr, nullptr, 0 },
+ { "s27", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM, 53, 53 }, nullptr, nullptr, nullptr, 0 },
+ { "s28", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM, 54, 54 }, nullptr, nullptr, nullptr, 0 },
+ { "s29", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM, 55, 55 }, nullptr, nullptr, nullptr, 0 },
+ { "s30", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM, 56, 56 }, nullptr, nullptr, nullptr, 0 },
+ { "s31", nullptr, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM, 57, 57 }, nullptr, nullptr, nullptr, 0 },
+ { "fpscr",nullptr, 4, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 58, 58 }, nullptr, nullptr, nullptr, 0 },
+ { "d16", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d16, LLDB_INVALID_REGNUM, 59, 59 }, nullptr, nullptr, nullptr, 0 },
+ { "d17", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d17, LLDB_INVALID_REGNUM, 60, 60 }, nullptr, nullptr, nullptr, 0 },
+ { "d18", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d18, LLDB_INVALID_REGNUM, 61, 61 }, nullptr, nullptr, nullptr, 0 },
+ { "d19", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d19, LLDB_INVALID_REGNUM, 62, 62 }, nullptr, nullptr, nullptr, 0 },
+ { "d20", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d20, LLDB_INVALID_REGNUM, 63, 63 }, nullptr, nullptr, nullptr, 0 },
+ { "d21", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d21, LLDB_INVALID_REGNUM, 64, 64 }, nullptr, nullptr, nullptr, 0 },
+ { "d22", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d22, LLDB_INVALID_REGNUM, 65, 65 }, nullptr, nullptr, nullptr, 0 },
+ { "d23", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d23, LLDB_INVALID_REGNUM, 66, 66 }, nullptr, nullptr, nullptr, 0 },
+ { "d24", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d24, LLDB_INVALID_REGNUM, 67, 67 }, nullptr, nullptr, nullptr, 0 },
+ { "d25", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d25, LLDB_INVALID_REGNUM, 68, 68 }, nullptr, nullptr, nullptr, 0 },
+ { "d26", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d26, LLDB_INVALID_REGNUM, 69, 69 }, nullptr, nullptr, nullptr, 0 },
+ { "d27", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d27, LLDB_INVALID_REGNUM, 70, 70 }, nullptr, nullptr, nullptr, 0 },
+ { "d28", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d28, LLDB_INVALID_REGNUM, 71, 71 }, nullptr, nullptr, nullptr, 0 },
+ { "d29", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d29, LLDB_INVALID_REGNUM, 72, 72 }, nullptr, nullptr, nullptr, 0 },
+ { "d30", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d30, LLDB_INVALID_REGNUM, 73, 73 }, nullptr, nullptr, nullptr, 0 },
+ { "d31", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d31, LLDB_INVALID_REGNUM, 74, 74 }, nullptr, nullptr, nullptr, 0 },
+ { "d0", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d0, LLDB_INVALID_REGNUM, 75, 75 }, g_d0_regs, nullptr, nullptr, 0 },
+ { "d1", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d1, LLDB_INVALID_REGNUM, 76, 76 }, g_d1_regs, nullptr, nullptr, 0 },
+ { "d2", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d2, LLDB_INVALID_REGNUM, 77, 77 }, g_d2_regs, nullptr, nullptr, 0 },
+ { "d3", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d3, LLDB_INVALID_REGNUM, 78, 78 }, g_d3_regs, nullptr, nullptr, 0 },
+ { "d4", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d4, LLDB_INVALID_REGNUM, 79, 79 }, g_d4_regs, nullptr, nullptr, 0 },
+ { "d5", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d5, LLDB_INVALID_REGNUM, 80, 80 }, g_d5_regs, nullptr, nullptr, 0 },
+ { "d6", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d6, LLDB_INVALID_REGNUM, 81, 81 }, g_d6_regs, nullptr, nullptr, 0 },
+ { "d7", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d7, LLDB_INVALID_REGNUM, 82, 82 }, g_d7_regs, nullptr, nullptr, 0 },
+ { "d8", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d8, LLDB_INVALID_REGNUM, 83, 83 }, g_d8_regs, nullptr, nullptr, 0 },
+ { "d9", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d9, LLDB_INVALID_REGNUM, 84, 84 }, g_d9_regs, nullptr, nullptr, 0 },
+ { "d10", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d10, LLDB_INVALID_REGNUM, 85, 85 }, g_d10_regs, nullptr, nullptr, 0 },
+ { "d11", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d11, LLDB_INVALID_REGNUM, 86, 86 }, g_d11_regs, nullptr, nullptr, 0 },
+ { "d12", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d12, LLDB_INVALID_REGNUM, 87, 87 }, g_d12_regs, nullptr, nullptr, 0 },
+ { "d13", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d13, LLDB_INVALID_REGNUM, 88, 88 }, g_d13_regs, nullptr, nullptr, 0 },
+ { "d14", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d14, LLDB_INVALID_REGNUM, 89, 89 }, g_d14_regs, nullptr, nullptr, 0 },
+ { "d15", nullptr, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d15, LLDB_INVALID_REGNUM, 90, 90 }, g_d15_regs, nullptr, nullptr, 0 },
+ { "q0", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q0, LLDB_INVALID_REGNUM, 91, 91 }, g_q0_regs, nullptr, nullptr, 0 },
+ { "q1", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q1, LLDB_INVALID_REGNUM, 92, 92 }, g_q1_regs, nullptr, nullptr, 0 },
+ { "q2", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q2, LLDB_INVALID_REGNUM, 93, 93 }, g_q2_regs, nullptr, nullptr, 0 },
+ { "q3", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q3, LLDB_INVALID_REGNUM, 94, 94 }, g_q3_regs, nullptr, nullptr, 0 },
+ { "q4", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q4, LLDB_INVALID_REGNUM, 95, 95 }, g_q4_regs, nullptr, nullptr, 0 },
+ { "q5", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q5, LLDB_INVALID_REGNUM, 96, 96 }, g_q5_regs, nullptr, nullptr, 0 },
+ { "q6", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q6, LLDB_INVALID_REGNUM, 97, 97 }, g_q6_regs, nullptr, nullptr, 0 },
+ { "q7", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q7, LLDB_INVALID_REGNUM, 98, 98 }, g_q7_regs, nullptr, nullptr, 0 },
+ { "q8", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q8, LLDB_INVALID_REGNUM, 99, 99 }, g_q8_regs, nullptr, nullptr, 0 },
+ { "q9", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q9, LLDB_INVALID_REGNUM, 100, 100 }, g_q9_regs, nullptr, nullptr, 0 },
+ { "q10", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q10, LLDB_INVALID_REGNUM, 101, 101 }, g_q10_regs, nullptr, nullptr, 0 },
+ { "q11", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q11, LLDB_INVALID_REGNUM, 102, 102 }, g_q11_regs, nullptr, nullptr, 0 },
+ { "q12", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q12, LLDB_INVALID_REGNUM, 103, 103 }, g_q12_regs, nullptr, nullptr, 0 },
+ { "q13", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q13, LLDB_INVALID_REGNUM, 104, 104 }, g_q13_regs, nullptr, nullptr, 0 },
+ { "q14", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q14, LLDB_INVALID_REGNUM, 105, 105 }, g_q14_regs, nullptr, nullptr, 0 },
+ { "q15", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q15, LLDB_INVALID_REGNUM, 106, 106 }, g_q15_regs, nullptr, nullptr, 0 }
+ };
+ // clang-format on
+
+ static const uint32_t num_registers = llvm::array_lengthof(g_register_infos);
+ static ConstString gpr_reg_set("General Purpose Registers");
+ static ConstString sfp_reg_set("Software Floating Point Registers");
+ static ConstString vfp_reg_set("Floating Point Registers");
+ size_t i;
+ if (from_scratch) {
+ // Calculate the offsets of the registers
+ // Note that the layout of the "composite" registers (d0-d15 and q0-q15)
+ // which comes after the "primordial" registers is important. This enables
+ // us to calculate the offset of the composite register by using the offset
+ // of its first primordial register. For example, to calculate the offset
+ // of q0, use s0's offset.
+ if (g_register_infos[2].byte_offset == 0) {
+ uint32_t byte_offset = 0;
+ for (i = 0; i < num_registers; ++i) {
+ // For primordial registers, increment the byte_offset by the byte_size
+ // to arrive at the byte_offset for the next register. Otherwise, we
+ // have a composite register whose offset can be calculated by
+ // consulting the offset of its first primordial register.
+ if (!g_register_infos[i].value_regs) {
+ g_register_infos[i].byte_offset = byte_offset;
+ byte_offset += g_register_infos[i].byte_size;
+ } else {
+ const uint32_t first_primordial_reg =
+ g_register_infos[i].value_regs[0];
+ g_register_infos[i].byte_offset =
+ g_register_infos[first_primordial_reg].byte_offset;
+ }
+ }
+ }
+ for (i = 0; i < num_registers; ++i) {
+ ConstString name;
+ ConstString alt_name;
+ if (g_register_infos[i].name && g_register_infos[i].name[0])
+ name.SetCString(g_register_infos[i].name);
+ if (g_register_infos[i].alt_name && g_register_infos[i].alt_name[0])
+ alt_name.SetCString(g_register_infos[i].alt_name);
+
+ if (i <= 15 || i == 25)
+ AddRegister(g_register_infos[i], name, alt_name, gpr_reg_set);
+ else if (i <= 24)
+ AddRegister(g_register_infos[i], name, alt_name, sfp_reg_set);
+ else
+ AddRegister(g_register_infos[i], name, alt_name, vfp_reg_set);
+ }
+ } else {
+ // Add composite registers to our primordial registers, then.
+ const size_t num_composites = llvm::array_lengthof(g_composites);
+ const size_t num_dynamic_regs = GetNumRegisters();
+ const size_t num_common_regs = num_registers - num_composites;
+ RegisterInfo *g_comp_register_infos = g_register_infos + num_common_regs;
+
+ // First we need to validate that all registers that we already have match
+ // the non composite regs. If so, then we can add the registers, else we
+ // need to bail
+ bool match = true;
+ if (num_dynamic_regs == num_common_regs) {
+ for (i = 0; match && i < num_dynamic_regs; ++i) {
+ // Make sure all register names match
+ if (m_regs[i].name && g_register_infos[i].name) {
+ if (strcmp(m_regs[i].name, g_register_infos[i].name)) {
+ match = false;
+ break;
+ }
+ }
+
+ // Make sure all register byte sizes match
+ if (m_regs[i].byte_size != g_register_infos[i].byte_size) {
+ match = false;
+ break;
+ }
+ }
+ } else {
+ // Wrong number of registers.
+ match = false;
+ }
+ // If "match" is true, then we can add extra registers.
+ if (match) {
+ for (i = 0; i < num_composites; ++i) {
+ ConstString name;
+ ConstString alt_name;
+ const uint32_t first_primordial_reg =
+ g_comp_register_infos[i].value_regs[0];
+ const char *reg_name = g_register_infos[first_primordial_reg].name;
+ if (reg_name && reg_name[0]) {
+ for (uint32_t j = 0; j < num_dynamic_regs; ++j) {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(j);
+ // Find a matching primordial register info entry.
+ if (reg_info && reg_info->name &&
+ ::strcasecmp(reg_info->name, reg_name) == 0) {
+ // The name matches the existing primordial entry. Find and
+ // assign the offset, and then add this composite register entry.
+ g_comp_register_infos[i].byte_offset = reg_info->byte_offset;
+ name.SetCString(g_comp_register_infos[i].name);
+ AddRegister(g_comp_register_infos[i], name, alt_name,
+ vfp_reg_set);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h
new file mode 100644
index 000000000000..25e9b716f8cb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h
@@ -0,0 +1,132 @@
+//===-- GDBRemoteRegisterContext.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_GDBRemoteRegisterContext_h_
+#define lldb_GDBRemoteRegisterContext_h_
+
+#include <vector>
+
+#include "Plugins/Process/Utility/DynamicRegisterInfo.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-private.h"
+
+#include "GDBRemoteCommunicationClient.h"
+
+class StringExtractor;
+
+namespace lldb_private {
+namespace process_gdb_remote {
+
+class ThreadGDBRemote;
+class ProcessGDBRemote;
+
+class GDBRemoteDynamicRegisterInfo : public DynamicRegisterInfo {
+public:
+ GDBRemoteDynamicRegisterInfo() : DynamicRegisterInfo() {}
+
+ ~GDBRemoteDynamicRegisterInfo() override = default;
+
+ void HardcodeARMRegisters(bool from_scratch);
+};
+
+class GDBRemoteRegisterContext : public RegisterContext {
+public:
+ GDBRemoteRegisterContext(ThreadGDBRemote &thread, uint32_t concrete_frame_idx,
+ GDBRemoteDynamicRegisterInfo &reg_info,
+ bool read_all_at_once);
+
+ ~GDBRemoteRegisterContext() override;
+
+ void InvalidateAllRegisters() override;
+
+ size_t GetRegisterCount() override;
+
+ const RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const RegisterSet *GetRegisterSet(size_t reg_set) override;
+
+ bool ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &value) override;
+
+ bool WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &value) override;
+
+ bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override;
+
+ bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
+
+ bool ReadAllRegisterValues(RegisterCheckpoint &reg_checkpoint) override;
+
+ bool
+ WriteAllRegisterValues(const RegisterCheckpoint &reg_checkpoint) override;
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+protected:
+ friend class ThreadGDBRemote;
+
+ bool ReadRegisterBytes(const RegisterInfo *reg_info, DataExtractor &data);
+
+ bool WriteRegisterBytes(const RegisterInfo *reg_info, DataExtractor &data,
+ uint32_t data_offset);
+
+ bool PrivateSetRegisterValue(uint32_t reg, llvm::ArrayRef<uint8_t> data);
+
+ bool PrivateSetRegisterValue(uint32_t reg, uint64_t val);
+
+ void SetAllRegisterValid(bool b);
+
+ bool GetRegisterIsValid(uint32_t reg) const {
+#if defined(LLDB_CONFIGURATION_DEBUG)
+ assert(reg < m_reg_valid.size());
+#endif
+ if (reg < m_reg_valid.size())
+ return m_reg_valid[reg];
+ return false;
+ }
+
+ void SetRegisterIsValid(const RegisterInfo *reg_info, bool valid) {
+ if (reg_info)
+ return SetRegisterIsValid(reg_info->kinds[lldb::eRegisterKindLLDB],
+ valid);
+ }
+
+ void SetRegisterIsValid(uint32_t reg, bool valid) {
+#if defined(LLDB_CONFIGURATION_DEBUG)
+ assert(reg < m_reg_valid.size());
+#endif
+ if (reg < m_reg_valid.size())
+ m_reg_valid[reg] = valid;
+ }
+
+ GDBRemoteDynamicRegisterInfo &m_reg_info;
+ std::vector<bool> m_reg_valid;
+ DataExtractor m_reg_data;
+ bool m_read_all_at_once;
+
+private:
+ // Helper function for ReadRegisterBytes().
+ bool GetPrimordialRegister(const RegisterInfo *reg_info,
+ GDBRemoteCommunicationClient &gdb_comm);
+ // Helper function for WriteRegisterBytes().
+ bool SetPrimordialRegister(const RegisterInfo *reg_info,
+ GDBRemoteCommunicationClient &gdb_comm);
+
+ DISALLOW_COPY_AND_ASSIGN(GDBRemoteRegisterContext);
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+#endif // lldb_GDBRemoteRegisterContext_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
new file mode 100644
index 000000000000..915e301ecc30
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -0,0 +1,5486 @@
+//===-- ProcessGDBRemote.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 "lldb/Host/Config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#ifndef LLDB_DISABLE_POSIX
+#include <netinet/in.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#endif
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <algorithm>
+#include <csignal>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <sstream>
+
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/Value.h"
+#include "lldb/DataFormatters/FormatManager.h"
+#include "lldb/Host/ConnectionFileDescriptor.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostThread.h"
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Host/PseudoTerminal.h"
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Host/XML.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionGroupBoolean.h"
+#include "lldb/Interpreter/OptionGroupUInt64.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Interpreter/Property.h"
+#include "lldb/Symbol/LocateSymbolFile.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Target/SystemRuntime.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/TargetList.h"
+#include "lldb/Target/ThreadPlanCallFunction.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/CleanUp.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Reproducer.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/Timer.h"
+
+#include "GDBRemoteRegisterContext.h"
+#ifdef LLDB_ENABLE_ALL
+#include "Plugins/Platform/MacOSX/PlatformRemoteiOS.h"
+#endif // LLDB_ENABLE_ALL
+#include "Plugins/Process/Utility/GDBRemoteSignals.h"
+#include "Plugins/Process/Utility/InferiorCallPOSIX.h"
+#include "Plugins/Process/Utility/StopInfoMachException.h"
+#include "ProcessGDBRemote.h"
+#include "ProcessGDBRemoteLog.h"
+#include "ThreadGDBRemote.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/Threading.h"
+#include "llvm/Support/raw_ostream.h"
+
+#define DEBUGSERVER_BASENAME "debugserver"
+using namespace llvm;
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+namespace lldb {
+// Provide a function that can easily dump the packet history if we know a
+// ProcessGDBRemote * value (which we can get from logs or from debugging). We
+// need the function in the lldb namespace so it makes it into the final
+// executable since the LLDB shared library only exports stuff in the lldb
+// namespace. This allows you to attach with a debugger and call this function
+// and get the packet history dumped to a file.
+void DumpProcessGDBRemotePacketHistory(void *p, const char *path) {
+ StreamFile strm;
+ Status error = FileSystem::Instance().Open(strm.GetFile(), FileSpec(path),
+ File::eOpenOptionWrite |
+ File::eOpenOptionCanCreate);
+ if (error.Success())
+ ((ProcessGDBRemote *)p)->GetGDBRemote().DumpHistory(strm);
+}
+} // namespace lldb
+
+namespace {
+
+static constexpr PropertyDefinition g_properties[] = {
+ {"packet-timeout",
+ OptionValue::eTypeUInt64,
+ true,
+ 5
+#if defined(__has_feature)
+#if __has_feature(address_sanitizer)
+ * 2
+#endif
+#endif
+ ,
+ nullptr,
+ {},
+ "Specify the default packet timeout in seconds."},
+ {"target-definition-file",
+ OptionValue::eTypeFileSpec,
+ true,
+ 0,
+ nullptr,
+ {},
+ "The file that provides the description for remote target registers."},
+ {"use-libraries-svr4",
+ OptionValue::eTypeBoolean,
+ true,
+ false,
+ nullptr,
+ {},
+ "If true, the libraries-svr4 feature will be used to get a hold of the "
+ "process's loaded modules."}};
+
+enum {
+ ePropertyPacketTimeout,
+ ePropertyTargetDefinitionFile,
+ ePropertyUseSVR4
+};
+
+class PluginProperties : public Properties {
+public:
+ static ConstString GetSettingName() {
+ return ProcessGDBRemote::GetPluginNameStatic();
+ }
+
+ PluginProperties() : Properties() {
+ m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
+ m_collection_sp->Initialize(g_properties);
+ }
+
+ ~PluginProperties() override {}
+
+ uint64_t GetPacketTimeout() {
+ const uint32_t idx = ePropertyPacketTimeout;
+ return m_collection_sp->GetPropertyAtIndexAsUInt64(
+ nullptr, idx, g_properties[idx].default_uint_value);
+ }
+
+ bool SetPacketTimeout(uint64_t timeout) {
+ const uint32_t idx = ePropertyPacketTimeout;
+ return m_collection_sp->SetPropertyAtIndexAsUInt64(nullptr, idx, timeout);
+ }
+
+ FileSpec GetTargetDefinitionFile() const {
+ const uint32_t idx = ePropertyTargetDefinitionFile;
+ return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr, idx);
+ }
+
+ bool GetUseSVR4() const {
+ const uint32_t idx = ePropertyUseSVR4;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+ }
+};
+
+typedef std::shared_ptr<PluginProperties> ProcessKDPPropertiesSP;
+
+static const ProcessKDPPropertiesSP &GetGlobalPluginProperties() {
+ static ProcessKDPPropertiesSP g_settings_sp;
+ if (!g_settings_sp)
+ g_settings_sp = std::make_shared<PluginProperties>();
+ return g_settings_sp;
+}
+
+class ProcessGDBRemoteProvider
+ : public repro::Provider<ProcessGDBRemoteProvider> {
+public:
+ struct Info {
+ static const char *name;
+ static const char *file;
+ };
+
+ ProcessGDBRemoteProvider(const FileSpec &directory) : Provider(directory) {
+ }
+
+ raw_ostream *GetHistoryStream() {
+ FileSpec history_file = GetRoot().CopyByAppendingPathComponent(Info::file);
+
+ std::error_code EC;
+ m_stream_up = llvm::make_unique<raw_fd_ostream>(history_file.GetPath(), EC,
+ sys::fs::OpenFlags::F_Text);
+ return m_stream_up.get();
+ }
+
+ void SetCallback(std::function<void()> callback) {
+ m_callback = std::move(callback);
+ }
+
+ void Keep() override { m_callback(); }
+
+ void Discard() override { m_callback(); }
+
+ static char ID;
+
+private:
+ std::function<void()> m_callback;
+ std::unique_ptr<raw_fd_ostream> m_stream_up;
+};
+
+char ProcessGDBRemoteProvider::ID = 0;
+const char *ProcessGDBRemoteProvider::Info::name = "gdb-remote";
+const char *ProcessGDBRemoteProvider::Info::file = "gdb-remote.yaml";
+
+} // namespace
+
+// TODO Randomly assigning a port is unsafe. We should get an unused
+// ephemeral port from the kernel and make sure we reserve it before passing it
+// to debugserver.
+
+#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(__APPLE__) && \
+ (defined(__arm__) || defined(__arm64__) || defined(__aarch64__))
+static bool rand_initialized = false;
+
+static inline uint16_t get_random_port() {
+ if (!rand_initialized) {
+ time_t seed = time(NULL);
+
+ rand_initialized = true;
+ srand(seed);
+ }
+ return (rand() % (HIGH_PORT - LOW_PORT)) + LOW_PORT;
+}
+#endif
+
+ConstString ProcessGDBRemote::GetPluginNameStatic() {
+ static ConstString g_name("gdb-remote");
+ return g_name;
+}
+
+const char *ProcessGDBRemote::GetPluginDescriptionStatic() {
+ return "GDB Remote protocol based debugging plug-in.";
+}
+
+void ProcessGDBRemote::Terminate() {
+ PluginManager::UnregisterPlugin(ProcessGDBRemote::CreateInstance);
+}
+
+lldb::ProcessSP
+ProcessGDBRemote::CreateInstance(lldb::TargetSP target_sp,
+ ListenerSP listener_sp,
+ const FileSpec *crash_file_path) {
+ lldb::ProcessSP process_sp;
+ if (crash_file_path == nullptr)
+ process_sp = std::make_shared<ProcessGDBRemote>(target_sp, listener_sp);
+ return process_sp;
+}
+
+bool ProcessGDBRemote::CanDebug(lldb::TargetSP target_sp,
+ bool plugin_specified_by_name) {
+ if (plugin_specified_by_name)
+ return true;
+
+ // For now we are just making sure the file exists for a given module
+ Module *exe_module = target_sp->GetExecutableModulePointer();
+ if (exe_module) {
+ ObjectFile *exe_objfile = exe_module->GetObjectFile();
+ // We can't debug core files...
+ switch (exe_objfile->GetType()) {
+ case ObjectFile::eTypeInvalid:
+ case ObjectFile::eTypeCoreFile:
+ case ObjectFile::eTypeDebugInfo:
+ case ObjectFile::eTypeObjectFile:
+ case ObjectFile::eTypeSharedLibrary:
+ case ObjectFile::eTypeStubLibrary:
+ case ObjectFile::eTypeJIT:
+ return false;
+ case ObjectFile::eTypeExecutable:
+ case ObjectFile::eTypeDynamicLinker:
+ case ObjectFile::eTypeUnknown:
+ break;
+ }
+ return FileSystem::Instance().Exists(exe_module->GetFileSpec());
+ }
+ // However, if there is no executable module, we return true since we might
+ // be preparing to attach.
+ return true;
+}
+
+// ProcessGDBRemote constructor
+ProcessGDBRemote::ProcessGDBRemote(lldb::TargetSP target_sp,
+ ListenerSP listener_sp)
+ : Process(target_sp, listener_sp),
+ m_debugserver_pid(LLDB_INVALID_PROCESS_ID), m_last_stop_packet_mutex(),
+ m_register_info(),
+ m_async_broadcaster(nullptr, "lldb.process.gdb-remote.async-broadcaster"),
+ m_async_listener_sp(
+ Listener::MakeListener("lldb.process.gdb-remote.async-listener")),
+ m_async_thread_state_mutex(), m_thread_ids(), m_thread_pcs(),
+ m_jstopinfo_sp(), m_jthreadsinfo_sp(), m_continue_c_tids(),
+ m_continue_C_tids(), m_continue_s_tids(), m_continue_S_tids(),
+ m_max_memory_size(0), m_remote_stub_max_memory_size(0),
+ m_addr_to_mmap_size(), m_thread_create_bp_sp(),
+ m_waiting_for_attach(false), m_destroy_tried_resuming(false),
+ m_command_sp(), m_breakpoint_pc_offset(0),
+ m_initial_tid(LLDB_INVALID_THREAD_ID), m_replay_mode(false),
+ m_allow_flash_writes(false), m_erased_flash_ranges() {
+ m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit,
+ "async thread should exit");
+ m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue,
+ "async thread continue");
+ m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadDidExit,
+ "async thread did exit");
+
+ if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) {
+ ProcessGDBRemoteProvider &provider =
+ g->GetOrCreate<ProcessGDBRemoteProvider>();
+ // Set the history stream to the stream owned by the provider.
+ m_gdb_comm.SetHistoryStream(provider.GetHistoryStream());
+ // Make sure to clear the stream again when we're finished.
+ provider.SetCallback([&]() { m_gdb_comm.SetHistoryStream(nullptr); });
+ }
+
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_ASYNC));
+
+ const uint32_t async_event_mask =
+ eBroadcastBitAsyncContinue | eBroadcastBitAsyncThreadShouldExit;
+
+ if (m_async_listener_sp->StartListeningForEvents(
+ &m_async_broadcaster, async_event_mask) != async_event_mask) {
+ if (log)
+ log->Printf("ProcessGDBRemote::%s failed to listen for "
+ "m_async_broadcaster events",
+ __FUNCTION__);
+ }
+
+ const uint32_t gdb_event_mask =
+ Communication::eBroadcastBitReadThreadDidExit |
+ GDBRemoteCommunication::eBroadcastBitGdbReadThreadGotNotify;
+ if (m_async_listener_sp->StartListeningForEvents(
+ &m_gdb_comm, gdb_event_mask) != gdb_event_mask) {
+ if (log)
+ log->Printf("ProcessGDBRemote::%s failed to listen for m_gdb_comm events",
+ __FUNCTION__);
+ }
+
+ const uint64_t timeout_seconds =
+ GetGlobalPluginProperties()->GetPacketTimeout();
+ if (timeout_seconds > 0)
+ m_gdb_comm.SetPacketTimeout(std::chrono::seconds(timeout_seconds));
+}
+
+// Destructor
+ProcessGDBRemote::~ProcessGDBRemote() {
+ // m_mach_process.UnregisterNotificationCallbacks (this);
+ Clear();
+ // We need to call finalize on the process before destroying ourselves to
+ // make sure all of the broadcaster cleanup goes as planned. If we destruct
+ // this class, then Process::~Process() might have problems trying to fully
+ // destroy the broadcaster.
+ Finalize();
+
+ // The general Finalize is going to try to destroy the process and that
+ // SHOULD shut down the async thread. However, if we don't kill it it will
+ // get stranded and its connection will go away so when it wakes up it will
+ // crash. So kill it for sure here.
+ StopAsyncThread();
+ KillDebugserverProcess();
+}
+
+// PluginInterface
+ConstString ProcessGDBRemote::GetPluginName() { return GetPluginNameStatic(); }
+
+uint32_t ProcessGDBRemote::GetPluginVersion() { return 1; }
+
+bool ProcessGDBRemote::ParsePythonTargetDefinition(
+ const FileSpec &target_definition_fspec) {
+ ScriptInterpreter *interpreter =
+ GetTarget().GetDebugger().GetScriptInterpreter();
+ Status error;
+ StructuredData::ObjectSP module_object_sp(
+ interpreter->LoadPluginModule(target_definition_fspec, error));
+ if (module_object_sp) {
+ StructuredData::DictionarySP target_definition_sp(
+ interpreter->GetDynamicSettings(module_object_sp, &GetTarget(),
+ "gdb-server-target-definition", error));
+
+ if (target_definition_sp) {
+ StructuredData::ObjectSP target_object(
+ target_definition_sp->GetValueForKey("host-info"));
+ if (target_object) {
+ if (auto host_info_dict = target_object->GetAsDictionary()) {
+ StructuredData::ObjectSP triple_value =
+ host_info_dict->GetValueForKey("triple");
+ if (auto triple_string_value = triple_value->GetAsString()) {
+ std::string triple_string = triple_string_value->GetValue();
+ ArchSpec host_arch(triple_string.c_str());
+ if (!host_arch.IsCompatibleMatch(GetTarget().GetArchitecture())) {
+ GetTarget().SetArchitecture(host_arch);
+ }
+ }
+ }
+ }
+ m_breakpoint_pc_offset = 0;
+ StructuredData::ObjectSP breakpoint_pc_offset_value =
+ target_definition_sp->GetValueForKey("breakpoint-pc-offset");
+ if (breakpoint_pc_offset_value) {
+ if (auto breakpoint_pc_int_value =
+ breakpoint_pc_offset_value->GetAsInteger())
+ m_breakpoint_pc_offset = breakpoint_pc_int_value->GetValue();
+ }
+
+ if (m_register_info.SetRegisterInfo(*target_definition_sp,
+ GetTarget().GetArchitecture()) > 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// If the remote stub didn't give us eh_frame or DWARF register numbers for a
+// register, see if the ABI can provide them.
+// DWARF and eh_frame register numbers are defined as a part of the ABI.
+static void AugmentRegisterInfoViaABI(RegisterInfo &reg_info,
+ ConstString reg_name, ABISP abi_sp) {
+ if (reg_info.kinds[eRegisterKindEHFrame] == LLDB_INVALID_REGNUM ||
+ reg_info.kinds[eRegisterKindDWARF] == LLDB_INVALID_REGNUM) {
+ if (abi_sp) {
+ RegisterInfo abi_reg_info;
+ if (abi_sp->GetRegisterInfoByName(reg_name, abi_reg_info)) {
+ if (reg_info.kinds[eRegisterKindEHFrame] == LLDB_INVALID_REGNUM &&
+ abi_reg_info.kinds[eRegisterKindEHFrame] != LLDB_INVALID_REGNUM) {
+ reg_info.kinds[eRegisterKindEHFrame] =
+ abi_reg_info.kinds[eRegisterKindEHFrame];
+ }
+ if (reg_info.kinds[eRegisterKindDWARF] == LLDB_INVALID_REGNUM &&
+ abi_reg_info.kinds[eRegisterKindDWARF] != LLDB_INVALID_REGNUM) {
+ reg_info.kinds[eRegisterKindDWARF] =
+ abi_reg_info.kinds[eRegisterKindDWARF];
+ }
+ if (reg_info.kinds[eRegisterKindGeneric] == LLDB_INVALID_REGNUM &&
+ abi_reg_info.kinds[eRegisterKindGeneric] != LLDB_INVALID_REGNUM) {
+ reg_info.kinds[eRegisterKindGeneric] =
+ abi_reg_info.kinds[eRegisterKindGeneric];
+ }
+ }
+ }
+ }
+}
+
+static size_t SplitCommaSeparatedRegisterNumberString(
+ const llvm::StringRef &comma_separated_regiter_numbers,
+ std::vector<uint32_t> &regnums, int base) {
+ regnums.clear();
+ std::pair<llvm::StringRef, llvm::StringRef> value_pair;
+ value_pair.second = comma_separated_regiter_numbers;
+ do {
+ value_pair = value_pair.second.split(',');
+ if (!value_pair.first.empty()) {
+ uint32_t reg = StringConvert::ToUInt32(value_pair.first.str().c_str(),
+ LLDB_INVALID_REGNUM, base);
+ if (reg != LLDB_INVALID_REGNUM)
+ regnums.push_back(reg);
+ }
+ } while (!value_pair.second.empty());
+ return regnums.size();
+}
+
+void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) {
+ if (!force && m_register_info.GetNumRegisters() > 0)
+ return;
+
+ m_register_info.Clear();
+
+ // Check if qHostInfo specified a specific packet timeout for this
+ // connection. If so then lets update our setting so the user knows what the
+ // timeout is and can see it.
+ const auto host_packet_timeout = m_gdb_comm.GetHostDefaultPacketTimeout();
+ if (host_packet_timeout > std::chrono::seconds(0)) {
+ GetGlobalPluginProperties()->SetPacketTimeout(host_packet_timeout.count());
+ }
+
+ // Register info search order:
+ // 1 - Use the target definition python file if one is specified.
+ // 2 - If the target definition doesn't have any of the info from the
+ // target.xml (registers) then proceed to read the target.xml.
+ // 3 - Fall back on the qRegisterInfo packets.
+
+ FileSpec target_definition_fspec =
+ GetGlobalPluginProperties()->GetTargetDefinitionFile();
+ if (!FileSystem::Instance().Exists(target_definition_fspec)) {
+ // If the filename doesn't exist, it may be a ~ not having been expanded -
+ // try to resolve it.
+ FileSystem::Instance().Resolve(target_definition_fspec);
+ }
+ if (target_definition_fspec) {
+ // See if we can get register definitions from a python file
+ if (ParsePythonTargetDefinition(target_definition_fspec)) {
+ return;
+ } else {
+ StreamSP stream_sp = GetTarget().GetDebugger().GetAsyncOutputStream();
+ stream_sp->Printf("ERROR: target description file %s failed to parse.\n",
+ target_definition_fspec.GetPath().c_str());
+ }
+ }
+
+ const ArchSpec &target_arch = GetTarget().GetArchitecture();
+ const ArchSpec &remote_host_arch = m_gdb_comm.GetHostArchitecture();
+ const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture();
+
+ // Use the process' architecture instead of the host arch, if available
+ ArchSpec arch_to_use;
+ if (remote_process_arch.IsValid())
+ arch_to_use = remote_process_arch;
+ else
+ arch_to_use = remote_host_arch;
+
+ if (!arch_to_use.IsValid())
+ arch_to_use = target_arch;
+
+ if (GetGDBServerRegisterInfo(arch_to_use))
+ return;
+
+ char packet[128];
+ uint32_t reg_offset = 0;
+ uint32_t reg_num = 0;
+ for (StringExtractorGDBRemote::ResponseType response_type =
+ StringExtractorGDBRemote::eResponse;
+ response_type == StringExtractorGDBRemote::eResponse; ++reg_num) {
+ const int packet_len =
+ ::snprintf(packet, sizeof(packet), "qRegisterInfo%x", reg_num);
+ assert(packet_len < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response, false) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ response_type = response.GetResponseType();
+ if (response_type == StringExtractorGDBRemote::eResponse) {
+ llvm::StringRef name;
+ llvm::StringRef value;
+ ConstString reg_name;
+ ConstString alt_name;
+ ConstString set_name;
+ std::vector<uint32_t> value_regs;
+ std::vector<uint32_t> invalidate_regs;
+ std::vector<uint8_t> dwarf_opcode_bytes;
+ RegisterInfo reg_info = {
+ nullptr, // Name
+ nullptr, // Alt name
+ 0, // byte size
+ reg_offset, // offset
+ eEncodingUint, // encoding
+ eFormatHex, // format
+ {
+ LLDB_INVALID_REGNUM, // eh_frame reg num
+ LLDB_INVALID_REGNUM, // DWARF reg num
+ LLDB_INVALID_REGNUM, // generic reg num
+ reg_num, // process plugin reg num
+ reg_num // native register number
+ },
+ nullptr,
+ nullptr,
+ nullptr, // Dwarf expression opcode bytes pointer
+ 0 // Dwarf expression opcode bytes length
+ };
+
+ while (response.GetNameColonValue(name, value)) {
+ if (name.equals("name")) {
+ reg_name.SetString(value);
+ } else if (name.equals("alt-name")) {
+ alt_name.SetString(value);
+ } else if (name.equals("bitsize")) {
+ value.getAsInteger(0, reg_info.byte_size);
+ reg_info.byte_size /= CHAR_BIT;
+ } else if (name.equals("offset")) {
+ if (value.getAsInteger(0, reg_offset))
+ reg_offset = UINT32_MAX;
+ } else if (name.equals("encoding")) {
+ const Encoding encoding = Args::StringToEncoding(value);
+ if (encoding != eEncodingInvalid)
+ reg_info.encoding = encoding;
+ } else if (name.equals("format")) {
+ Format format = eFormatInvalid;
+ if (OptionArgParser::ToFormat(value.str().c_str(), format, nullptr)
+ .Success())
+ reg_info.format = format;
+ else {
+ reg_info.format =
+ llvm::StringSwitch<Format>(value)
+ .Case("binary", eFormatBinary)
+ .Case("decimal", eFormatDecimal)
+ .Case("hex", eFormatHex)
+ .Case("float", eFormatFloat)
+ .Case("vector-sint8", eFormatVectorOfSInt8)
+ .Case("vector-uint8", eFormatVectorOfUInt8)
+ .Case("vector-sint16", eFormatVectorOfSInt16)
+ .Case("vector-uint16", eFormatVectorOfUInt16)
+ .Case("vector-sint32", eFormatVectorOfSInt32)
+ .Case("vector-uint32", eFormatVectorOfUInt32)
+ .Case("vector-float32", eFormatVectorOfFloat32)
+ .Case("vector-uint64", eFormatVectorOfUInt64)
+ .Case("vector-uint128", eFormatVectorOfUInt128)
+ .Default(eFormatInvalid);
+ }
+ } else if (name.equals("set")) {
+ set_name.SetString(value);
+ } else if (name.equals("gcc") || name.equals("ehframe")) {
+ if (value.getAsInteger(0, reg_info.kinds[eRegisterKindEHFrame]))
+ reg_info.kinds[eRegisterKindEHFrame] = LLDB_INVALID_REGNUM;
+ } else if (name.equals("dwarf")) {
+ if (value.getAsInteger(0, reg_info.kinds[eRegisterKindDWARF]))
+ reg_info.kinds[eRegisterKindDWARF] = LLDB_INVALID_REGNUM;
+ } else if (name.equals("generic")) {
+ reg_info.kinds[eRegisterKindGeneric] =
+ Args::StringToGenericRegister(value);
+ } else if (name.equals("container-regs")) {
+ SplitCommaSeparatedRegisterNumberString(value, value_regs, 16);
+ } else if (name.equals("invalidate-regs")) {
+ SplitCommaSeparatedRegisterNumberString(value, invalidate_regs, 16);
+ } else if (name.equals("dynamic_size_dwarf_expr_bytes")) {
+ size_t dwarf_opcode_len = value.size() / 2;
+ assert(dwarf_opcode_len > 0);
+
+ dwarf_opcode_bytes.resize(dwarf_opcode_len);
+ reg_info.dynamic_size_dwarf_len = dwarf_opcode_len;
+
+ StringExtractor opcode_extractor(value);
+ uint32_t ret_val =
+ opcode_extractor.GetHexBytesAvail(dwarf_opcode_bytes);
+ assert(dwarf_opcode_len == ret_val);
+ UNUSED_IF_ASSERT_DISABLED(ret_val);
+ reg_info.dynamic_size_dwarf_expr_bytes = dwarf_opcode_bytes.data();
+ }
+ }
+
+ reg_info.byte_offset = reg_offset;
+ assert(reg_info.byte_size != 0);
+ reg_offset += reg_info.byte_size;
+ if (!value_regs.empty()) {
+ value_regs.push_back(LLDB_INVALID_REGNUM);
+ reg_info.value_regs = value_regs.data();
+ }
+ if (!invalidate_regs.empty()) {
+ invalidate_regs.push_back(LLDB_INVALID_REGNUM);
+ reg_info.invalidate_regs = invalidate_regs.data();
+ }
+
+ // We have to make a temporary ABI here, and not use the GetABI because
+ // this code gets called in DidAttach, when the target architecture
+ // (and consequently the ABI we'll get from the process) may be wrong.
+ ABISP abi_to_use = ABI::FindPlugin(shared_from_this(), arch_to_use);
+
+ AugmentRegisterInfoViaABI(reg_info, reg_name, abi_to_use);
+
+ m_register_info.AddRegister(reg_info, reg_name, alt_name, set_name);
+ } else {
+ break; // ensure exit before reg_num is incremented
+ }
+ } else {
+ break;
+ }
+ }
+
+ if (m_register_info.GetNumRegisters() > 0) {
+ m_register_info.Finalize(GetTarget().GetArchitecture());
+ return;
+ }
+
+ // We didn't get anything if the accumulated reg_num is zero. See if we are
+ // debugging ARM and fill with a hard coded register set until we can get an
+ // updated debugserver down on the devices. On the other hand, if the
+ // accumulated reg_num is positive, see if we can add composite registers to
+ // the existing primordial ones.
+ bool from_scratch = (m_register_info.GetNumRegisters() == 0);
+
+ if (!target_arch.IsValid()) {
+ if (arch_to_use.IsValid() &&
+ (arch_to_use.GetMachine() == llvm::Triple::arm ||
+ arch_to_use.GetMachine() == llvm::Triple::thumb) &&
+ arch_to_use.GetTriple().getVendor() == llvm::Triple::Apple)
+ m_register_info.HardcodeARMRegisters(from_scratch);
+ } else if (target_arch.GetMachine() == llvm::Triple::arm ||
+ target_arch.GetMachine() == llvm::Triple::thumb) {
+ m_register_info.HardcodeARMRegisters(from_scratch);
+ }
+
+ // At this point, we can finalize our register info.
+ m_register_info.Finalize(GetTarget().GetArchitecture());
+}
+
+Status ProcessGDBRemote::WillLaunch(lldb_private::Module *module) {
+ return WillLaunchOrAttach();
+}
+
+Status ProcessGDBRemote::WillAttachToProcessWithID(lldb::pid_t pid) {
+ return WillLaunchOrAttach();
+}
+
+Status ProcessGDBRemote::WillAttachToProcessWithName(const char *process_name,
+ bool wait_for_launch) {
+ return WillLaunchOrAttach();
+}
+
+Status ProcessGDBRemote::DoConnectRemote(Stream *strm,
+ llvm::StringRef remote_url) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ Status error(WillLaunchOrAttach());
+
+ if (error.Fail())
+ return error;
+
+ error = ConnectToDebugserver(remote_url);
+
+ if (error.Fail())
+ return error;
+ StartAsyncThread();
+
+ lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID();
+ if (pid == LLDB_INVALID_PROCESS_ID) {
+ // We don't have a valid process ID, so note that we are connected and
+ // could now request to launch or attach, or get remote process listings...
+ SetPrivateState(eStateConnected);
+ } else {
+ // We have a valid process
+ SetID(pid);
+ GetThreadList();
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.GetStopReply(response)) {
+ SetLastStopPacket(response);
+
+ // '?' Packets must be handled differently in non-stop mode
+ if (GetTarget().GetNonStopModeEnabled())
+ HandleStopReplySequence();
+
+ Target &target = GetTarget();
+ if (!target.GetArchitecture().IsValid()) {
+ if (m_gdb_comm.GetProcessArchitecture().IsValid()) {
+ target.SetArchitecture(m_gdb_comm.GetProcessArchitecture());
+ } else {
+ if (m_gdb_comm.GetHostArchitecture().IsValid()) {
+ target.SetArchitecture(m_gdb_comm.GetHostArchitecture());
+ }
+ }
+ }
+
+ const StateType state = SetThreadStopInfo(response);
+ if (state != eStateInvalid) {
+ SetPrivateState(state);
+ } else
+ error.SetErrorStringWithFormat(
+ "Process %" PRIu64 " was reported after connecting to "
+ "'%s', but state was not stopped: %s",
+ pid, remote_url.str().c_str(), StateAsCString(state));
+ } else
+ error.SetErrorStringWithFormat("Process %" PRIu64
+ " was reported after connecting to '%s', "
+ "but no stop reply packet was received",
+ pid, remote_url.str().c_str());
+ }
+
+ if (log)
+ log->Printf("ProcessGDBRemote::%s pid %" PRIu64
+ ": normalizing target architecture initial triple: %s "
+ "(GetTarget().GetArchitecture().IsValid() %s, "
+ "m_gdb_comm.GetHostArchitecture().IsValid(): %s)",
+ __FUNCTION__, GetID(),
+ GetTarget().GetArchitecture().GetTriple().getTriple().c_str(),
+ GetTarget().GetArchitecture().IsValid() ? "true" : "false",
+ m_gdb_comm.GetHostArchitecture().IsValid() ? "true" : "false");
+
+ if (error.Success() && !GetTarget().GetArchitecture().IsValid() &&
+ m_gdb_comm.GetHostArchitecture().IsValid()) {
+ // Prefer the *process'* architecture over that of the *host*, if
+ // available.
+ if (m_gdb_comm.GetProcessArchitecture().IsValid())
+ GetTarget().SetArchitecture(m_gdb_comm.GetProcessArchitecture());
+ else
+ GetTarget().SetArchitecture(m_gdb_comm.GetHostArchitecture());
+ }
+
+ if (log)
+ log->Printf("ProcessGDBRemote::%s pid %" PRIu64
+ ": normalized target architecture triple: %s",
+ __FUNCTION__, GetID(),
+ GetTarget().GetArchitecture().GetTriple().getTriple().c_str());
+
+ if (error.Success()) {
+ PlatformSP platform_sp = GetTarget().GetPlatform();
+ if (platform_sp && platform_sp->IsConnected())
+ SetUnixSignals(platform_sp->GetUnixSignals());
+ else
+ SetUnixSignals(UnixSignals::Create(GetTarget().GetArchitecture()));
+ }
+
+ return error;
+}
+
+Status ProcessGDBRemote::WillLaunchOrAttach() {
+ Status error;
+ m_stdio_communication.Clear();
+ return error;
+}
+
+// Process Control
+Status ProcessGDBRemote::DoLaunch(lldb_private::Module *exe_module,
+ ProcessLaunchInfo &launch_info) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ Status error;
+
+ if (log)
+ log->Printf("ProcessGDBRemote::%s() entered", __FUNCTION__);
+
+ uint32_t launch_flags = launch_info.GetFlags().Get();
+ FileSpec stdin_file_spec{};
+ FileSpec stdout_file_spec{};
+ FileSpec stderr_file_spec{};
+ FileSpec working_dir = launch_info.GetWorkingDirectory();
+
+ const FileAction *file_action;
+ file_action = launch_info.GetFileActionForFD(STDIN_FILENO);
+ if (file_action) {
+ if (file_action->GetAction() == FileAction::eFileActionOpen)
+ stdin_file_spec = file_action->GetFileSpec();
+ }
+ file_action = launch_info.GetFileActionForFD(STDOUT_FILENO);
+ if (file_action) {
+ if (file_action->GetAction() == FileAction::eFileActionOpen)
+ stdout_file_spec = file_action->GetFileSpec();
+ }
+ file_action = launch_info.GetFileActionForFD(STDERR_FILENO);
+ if (file_action) {
+ if (file_action->GetAction() == FileAction::eFileActionOpen)
+ stderr_file_spec = file_action->GetFileSpec();
+ }
+
+ if (log) {
+ if (stdin_file_spec || stdout_file_spec || stderr_file_spec)
+ log->Printf("ProcessGDBRemote::%s provided with STDIO paths via "
+ "launch_info: stdin=%s, stdout=%s, stderr=%s",
+ __FUNCTION__,
+ stdin_file_spec ? stdin_file_spec.GetCString() : "<null>",
+ stdout_file_spec ? stdout_file_spec.GetCString() : "<null>",
+ stderr_file_spec ? stderr_file_spec.GetCString() : "<null>");
+ else
+ log->Printf("ProcessGDBRemote::%s no STDIO paths given via launch_info",
+ __FUNCTION__);
+ }
+
+ const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0;
+ if (stdin_file_spec || disable_stdio) {
+ // the inferior will be reading stdin from the specified file or stdio is
+ // completely disabled
+ m_stdin_forward = false;
+ } else {
+ m_stdin_forward = true;
+ }
+
+ // ::LogSetBitMask (GDBR_LOG_DEFAULT);
+ // ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE |
+ // LLDB_LOG_OPTION_PREPEND_TIMESTAMP |
+ // LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD);
+ // ::LogSetLogFile ("/dev/stdout");
+
+ ObjectFile *object_file = exe_module->GetObjectFile();
+ if (object_file) {
+ error = EstablishConnectionIfNeeded(launch_info);
+ if (error.Success()) {
+ PseudoTerminal pty;
+ const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0;
+
+ PlatformSP platform_sp(GetTarget().GetPlatform());
+ if (disable_stdio) {
+ // set to /dev/null unless redirected to a file above
+ if (!stdin_file_spec)
+ stdin_file_spec.SetFile(FileSystem::DEV_NULL,
+ FileSpec::Style::native);
+ if (!stdout_file_spec)
+ stdout_file_spec.SetFile(FileSystem::DEV_NULL,
+ FileSpec::Style::native);
+ if (!stderr_file_spec)
+ stderr_file_spec.SetFile(FileSystem::DEV_NULL,
+ FileSpec::Style::native);
+ } else if (platform_sp && platform_sp->IsHost()) {
+ // If the debugserver is local and we aren't disabling STDIO, lets use
+ // a pseudo terminal to instead of relying on the 'O' packets for stdio
+ // since 'O' packets can really slow down debugging if the inferior
+ // does a lot of output.
+ if ((!stdin_file_spec || !stdout_file_spec || !stderr_file_spec) &&
+ pty.OpenFirstAvailableMaster(O_RDWR | O_NOCTTY, nullptr, 0)) {
+ FileSpec slave_name{pty.GetSlaveName(nullptr, 0)};
+
+ if (!stdin_file_spec)
+ stdin_file_spec = slave_name;
+
+ if (!stdout_file_spec)
+ stdout_file_spec = slave_name;
+
+ if (!stderr_file_spec)
+ stderr_file_spec = slave_name;
+ }
+ if (log)
+ log->Printf(
+ "ProcessGDBRemote::%s adjusted STDIO paths for local platform "
+ "(IsHost() is true) using slave: stdin=%s, stdout=%s, stderr=%s",
+ __FUNCTION__,
+ stdin_file_spec ? stdin_file_spec.GetCString() : "<null>",
+ stdout_file_spec ? stdout_file_spec.GetCString() : "<null>",
+ stderr_file_spec ? stderr_file_spec.GetCString() : "<null>");
+ }
+
+ if (log)
+ log->Printf("ProcessGDBRemote::%s final STDIO paths after all "
+ "adjustments: stdin=%s, stdout=%s, stderr=%s",
+ __FUNCTION__,
+ stdin_file_spec ? stdin_file_spec.GetCString() : "<null>",
+ stdout_file_spec ? stdout_file_spec.GetCString() : "<null>",
+ stderr_file_spec ? stderr_file_spec.GetCString()
+ : "<null>");
+
+ if (stdin_file_spec)
+ m_gdb_comm.SetSTDIN(stdin_file_spec);
+ if (stdout_file_spec)
+ m_gdb_comm.SetSTDOUT(stdout_file_spec);
+ if (stderr_file_spec)
+ m_gdb_comm.SetSTDERR(stderr_file_spec);
+
+ m_gdb_comm.SetDisableASLR(launch_flags & eLaunchFlagDisableASLR);
+ m_gdb_comm.SetDetachOnError(launch_flags & eLaunchFlagDetachOnError);
+
+ m_gdb_comm.SendLaunchArchPacket(
+ GetTarget().GetArchitecture().GetArchitectureName());
+
+ const char *launch_event_data = launch_info.GetLaunchEventData();
+ if (launch_event_data != nullptr && *launch_event_data != '\0')
+ m_gdb_comm.SendLaunchEventDataPacket(launch_event_data);
+
+ if (working_dir) {
+ m_gdb_comm.SetWorkingDir(working_dir);
+ }
+
+ // Send the environment and the program + arguments after we connect
+ m_gdb_comm.SendEnvironment(launch_info.GetEnvironment());
+
+ {
+ // Scope for the scoped timeout object
+ GDBRemoteCommunication::ScopedTimeout timeout(m_gdb_comm,
+ std::chrono::seconds(10));
+
+ int arg_packet_err = m_gdb_comm.SendArgumentsPacket(launch_info);
+ if (arg_packet_err == 0) {
+ std::string error_str;
+ if (m_gdb_comm.GetLaunchSuccess(error_str)) {
+ SetID(m_gdb_comm.GetCurrentProcessID());
+ } else {
+ error.SetErrorString(error_str.c_str());
+ }
+ } else {
+ error.SetErrorStringWithFormat("'A' packet returned an error: %i",
+ arg_packet_err);
+ }
+ }
+
+ if (GetID() == LLDB_INVALID_PROCESS_ID) {
+ if (log)
+ log->Printf("failed to connect to debugserver: %s",
+ error.AsCString());
+ KillDebugserverProcess();
+ return error;
+ }
+
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.GetStopReply(response)) {
+ SetLastStopPacket(response);
+ // '?' Packets must be handled differently in non-stop mode
+ if (GetTarget().GetNonStopModeEnabled())
+ HandleStopReplySequence();
+
+ const ArchSpec &process_arch = m_gdb_comm.GetProcessArchitecture();
+
+ if (process_arch.IsValid()) {
+ GetTarget().MergeArchitecture(process_arch);
+ } else {
+ const ArchSpec &host_arch = m_gdb_comm.GetHostArchitecture();
+ if (host_arch.IsValid())
+ GetTarget().MergeArchitecture(host_arch);
+ }
+
+ SetPrivateState(SetThreadStopInfo(response));
+
+ if (!disable_stdio) {
+ if (pty.GetMasterFileDescriptor() != PseudoTerminal::invalid_fd)
+ SetSTDIOFileDescriptor(pty.ReleaseMasterFileDescriptor());
+ }
+ }
+ } else {
+ if (log)
+ log->Printf("failed to connect to debugserver: %s", error.AsCString());
+ }
+ } else {
+ // Set our user ID to an invalid process ID.
+ SetID(LLDB_INVALID_PROCESS_ID);
+ error.SetErrorStringWithFormat(
+ "failed to get object file from '%s' for arch %s",
+ exe_module->GetFileSpec().GetFilename().AsCString(),
+ exe_module->GetArchitecture().GetArchitectureName());
+ }
+ return error;
+}
+
+Status ProcessGDBRemote::ConnectToDebugserver(llvm::StringRef connect_url) {
+ Status error;
+ // Only connect if we have a valid connect URL
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+
+ if (!connect_url.empty()) {
+ if (log)
+ log->Printf("ProcessGDBRemote::%s Connecting to %s", __FUNCTION__,
+ connect_url.str().c_str());
+ std::unique_ptr<ConnectionFileDescriptor> conn_up(
+ new ConnectionFileDescriptor());
+ if (conn_up) {
+ const uint32_t max_retry_count = 50;
+ uint32_t retry_count = 0;
+ while (!m_gdb_comm.IsConnected()) {
+ if (conn_up->Connect(connect_url, &error) == eConnectionStatusSuccess) {
+ m_gdb_comm.SetConnection(conn_up.release());
+ break;
+ } else if (error.WasInterrupted()) {
+ // If we were interrupted, don't keep retrying.
+ break;
+ }
+
+ retry_count++;
+
+ if (retry_count >= max_retry_count)
+ break;
+
+ usleep(100000);
+ }
+ }
+ }
+
+ if (!m_gdb_comm.IsConnected()) {
+ if (error.Success())
+ error.SetErrorString("not connected to remote gdb server");
+ return error;
+ }
+
+ // Start the communications read thread so all incoming data can be parsed
+ // into packets and queued as they arrive.
+ if (GetTarget().GetNonStopModeEnabled())
+ m_gdb_comm.StartReadThread();
+
+ // We always seem to be able to open a connection to a local port so we need
+ // to make sure we can then send data to it. If we can't then we aren't
+ // actually connected to anything, so try and do the handshake with the
+ // remote GDB server and make sure that goes alright.
+ if (!m_gdb_comm.HandshakeWithServer(&error)) {
+ m_gdb_comm.Disconnect();
+ if (error.Success())
+ error.SetErrorString("not connected to remote gdb server");
+ return error;
+ }
+
+ // Send $QNonStop:1 packet on startup if required
+ if (GetTarget().GetNonStopModeEnabled())
+ GetTarget().SetNonStopModeEnabled(m_gdb_comm.SetNonStopMode(true));
+
+ m_gdb_comm.GetEchoSupported();
+ m_gdb_comm.GetThreadSuffixSupported();
+ m_gdb_comm.GetListThreadsInStopReplySupported();
+ m_gdb_comm.GetHostInfo();
+ m_gdb_comm.GetVContSupported('c');
+ m_gdb_comm.GetVAttachOrWaitSupported();
+ m_gdb_comm.EnableErrorStringInPacket();
+
+ // Ask the remote server for the default thread id
+ if (GetTarget().GetNonStopModeEnabled())
+ m_gdb_comm.GetDefaultThreadId(m_initial_tid);
+
+ size_t num_cmds = GetExtraStartupCommands().GetArgumentCount();
+ for (size_t idx = 0; idx < num_cmds; idx++) {
+ StringExtractorGDBRemote response;
+ m_gdb_comm.SendPacketAndWaitForResponse(
+ GetExtraStartupCommands().GetArgumentAtIndex(idx), response, false);
+ }
+ return error;
+}
+
+void ProcessGDBRemote::DidLaunchOrAttach(ArchSpec &process_arch) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf("ProcessGDBRemote::%s()", __FUNCTION__);
+ if (GetID() != LLDB_INVALID_PROCESS_ID) {
+ BuildDynamicRegisterInfo(false);
+
+ // See if the GDB server supports the qHostInfo information
+
+ // See if the GDB server supports the qProcessInfo packet, if so prefer
+ // that over the Host information as it will be more specific to our
+ // process.
+
+ const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture();
+ if (remote_process_arch.IsValid()) {
+ process_arch = remote_process_arch;
+ if (log)
+ log->Printf("ProcessGDBRemote::%s gdb-remote had process architecture, "
+ "using %s %s",
+ __FUNCTION__,
+ process_arch.GetArchitectureName()
+ ? process_arch.GetArchitectureName()
+ : "<null>",
+ process_arch.GetTriple().getTriple().c_str()
+ ? process_arch.GetTriple().getTriple().c_str()
+ : "<null>");
+ } else {
+ process_arch = m_gdb_comm.GetHostArchitecture();
+ if (log)
+ log->Printf("ProcessGDBRemote::%s gdb-remote did not have process "
+ "architecture, using gdb-remote host architecture %s %s",
+ __FUNCTION__,
+ process_arch.GetArchitectureName()
+ ? process_arch.GetArchitectureName()
+ : "<null>",
+ process_arch.GetTriple().getTriple().c_str()
+ ? process_arch.GetTriple().getTriple().c_str()
+ : "<null>");
+ }
+
+ if (process_arch.IsValid()) {
+ const ArchSpec &target_arch = GetTarget().GetArchitecture();
+ if (target_arch.IsValid()) {
+ if (log)
+ log->Printf(
+ "ProcessGDBRemote::%s analyzing target arch, currently %s %s",
+ __FUNCTION__,
+ target_arch.GetArchitectureName()
+ ? target_arch.GetArchitectureName()
+ : "<null>",
+ target_arch.GetTriple().getTriple().c_str()
+ ? target_arch.GetTriple().getTriple().c_str()
+ : "<null>");
+
+ // If the remote host is ARM and we have apple as the vendor, then
+ // ARM executables and shared libraries can have mixed ARM
+ // architectures.
+ // You can have an armv6 executable, and if the host is armv7, then the
+ // system will load the best possible architecture for all shared
+ // libraries it has, so we really need to take the remote host
+ // architecture as our defacto architecture in this case.
+
+ if ((process_arch.GetMachine() == llvm::Triple::arm ||
+ process_arch.GetMachine() == llvm::Triple::thumb) &&
+ process_arch.GetTriple().getVendor() == llvm::Triple::Apple) {
+ GetTarget().SetArchitecture(process_arch);
+ if (log)
+ log->Printf("ProcessGDBRemote::%s remote process is ARM/Apple, "
+ "setting target arch to %s %s",
+ __FUNCTION__,
+ process_arch.GetArchitectureName()
+ ? process_arch.GetArchitectureName()
+ : "<null>",
+ process_arch.GetTriple().getTriple().c_str()
+ ? process_arch.GetTriple().getTriple().c_str()
+ : "<null>");
+ } else {
+ // Fill in what is missing in the triple
+ const llvm::Triple &remote_triple = process_arch.GetTriple();
+ llvm::Triple new_target_triple = target_arch.GetTriple();
+ if (new_target_triple.getVendorName().size() == 0) {
+ new_target_triple.setVendor(remote_triple.getVendor());
+
+ if (new_target_triple.getOSName().size() == 0) {
+ new_target_triple.setOS(remote_triple.getOS());
+
+ if (new_target_triple.getEnvironmentName().size() == 0)
+ new_target_triple.setEnvironment(
+ remote_triple.getEnvironment());
+ }
+
+ ArchSpec new_target_arch = target_arch;
+ new_target_arch.SetTriple(new_target_triple);
+ GetTarget().SetArchitecture(new_target_arch);
+ }
+ }
+
+ if (log)
+ log->Printf("ProcessGDBRemote::%s final target arch after "
+ "adjustments for remote architecture: %s %s",
+ __FUNCTION__,
+ target_arch.GetArchitectureName()
+ ? target_arch.GetArchitectureName()
+ : "<null>",
+ target_arch.GetTriple().getTriple().c_str()
+ ? target_arch.GetTriple().getTriple().c_str()
+ : "<null>");
+ } else {
+ // The target doesn't have a valid architecture yet, set it from the
+ // architecture we got from the remote GDB server
+ GetTarget().SetArchitecture(process_arch);
+ }
+ }
+
+ // Find out which StructuredDataPlugins are supported by the debug monitor.
+ // These plugins transmit data over async $J packets.
+ auto supported_packets_array =
+ m_gdb_comm.GetSupportedStructuredDataPlugins();
+ if (supported_packets_array)
+ MapSupportedStructuredDataPlugins(*supported_packets_array);
+ }
+}
+
+void ProcessGDBRemote::DidLaunch() {
+ ArchSpec process_arch;
+ DidLaunchOrAttach(process_arch);
+}
+
+Status ProcessGDBRemote::DoAttachToProcessWithID(
+ lldb::pid_t attach_pid, const ProcessAttachInfo &attach_info) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ Status error;
+
+ if (log)
+ log->Printf("ProcessGDBRemote::%s()", __FUNCTION__);
+
+ // Clear out and clean up from any current state
+ Clear();
+ if (attach_pid != LLDB_INVALID_PROCESS_ID) {
+ error = EstablishConnectionIfNeeded(attach_info);
+ if (error.Success()) {
+ m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError());
+
+ char packet[64];
+ const int packet_len =
+ ::snprintf(packet, sizeof(packet), "vAttach;%" PRIx64, attach_pid);
+ SetID(attach_pid);
+ m_async_broadcaster.BroadcastEvent(
+ eBroadcastBitAsyncContinue, new EventDataBytes(packet, packet_len));
+ } else
+ SetExitStatus(-1, error.AsCString());
+ }
+
+ return error;
+}
+
+Status ProcessGDBRemote::DoAttachToProcessWithName(
+ const char *process_name, const ProcessAttachInfo &attach_info) {
+ Status error;
+ // Clear out and clean up from any current state
+ Clear();
+
+ if (process_name && process_name[0]) {
+ error = EstablishConnectionIfNeeded(attach_info);
+ if (error.Success()) {
+ StreamString packet;
+
+ m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError());
+
+ if (attach_info.GetWaitForLaunch()) {
+ if (!m_gdb_comm.GetVAttachOrWaitSupported()) {
+ packet.PutCString("vAttachWait");
+ } else {
+ if (attach_info.GetIgnoreExisting())
+ packet.PutCString("vAttachWait");
+ else
+ packet.PutCString("vAttachOrWait");
+ }
+ } else
+ packet.PutCString("vAttachName");
+ packet.PutChar(';');
+ packet.PutBytesAsRawHex8(process_name, strlen(process_name),
+ endian::InlHostByteOrder(),
+ endian::InlHostByteOrder());
+
+ m_async_broadcaster.BroadcastEvent(
+ eBroadcastBitAsyncContinue,
+ new EventDataBytes(packet.GetString().data(), packet.GetSize()));
+
+ } else
+ SetExitStatus(-1, error.AsCString());
+ }
+ return error;
+}
+
+lldb::user_id_t ProcessGDBRemote::StartTrace(const TraceOptions &options,
+ Status &error) {
+ return m_gdb_comm.SendStartTracePacket(options, error);
+}
+
+Status ProcessGDBRemote::StopTrace(lldb::user_id_t uid, lldb::tid_t thread_id) {
+ return m_gdb_comm.SendStopTracePacket(uid, thread_id);
+}
+
+Status ProcessGDBRemote::GetData(lldb::user_id_t uid, lldb::tid_t thread_id,
+ llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset) {
+ return m_gdb_comm.SendGetDataPacket(uid, thread_id, buffer, offset);
+}
+
+Status ProcessGDBRemote::GetMetaData(lldb::user_id_t uid, lldb::tid_t thread_id,
+ llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset) {
+ return m_gdb_comm.SendGetMetaDataPacket(uid, thread_id, buffer, offset);
+}
+
+Status ProcessGDBRemote::GetTraceConfig(lldb::user_id_t uid,
+ TraceOptions &options) {
+ return m_gdb_comm.SendGetTraceConfigPacket(uid, options);
+}
+
+void ProcessGDBRemote::DidExit() {
+ // When we exit, disconnect from the GDB server communications
+ m_gdb_comm.Disconnect();
+}
+
+void ProcessGDBRemote::DidAttach(ArchSpec &process_arch) {
+ // If you can figure out what the architecture is, fill it in here.
+ process_arch.Clear();
+ DidLaunchOrAttach(process_arch);
+}
+
+Status ProcessGDBRemote::WillResume() {
+ m_continue_c_tids.clear();
+ m_continue_C_tids.clear();
+ m_continue_s_tids.clear();
+ m_continue_S_tids.clear();
+ m_jstopinfo_sp.reset();
+ m_jthreadsinfo_sp.reset();
+ return Status();
+}
+
+Status ProcessGDBRemote::DoResume() {
+ Status error;
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf("ProcessGDBRemote::Resume()");
+
+ ListenerSP listener_sp(
+ Listener::MakeListener("gdb-remote.resume-packet-sent"));
+ if (listener_sp->StartListeningForEvents(
+ &m_gdb_comm, GDBRemoteCommunication::eBroadcastBitRunPacketSent)) {
+ listener_sp->StartListeningForEvents(
+ &m_async_broadcaster,
+ ProcessGDBRemote::eBroadcastBitAsyncThreadDidExit);
+
+ const size_t num_threads = GetThreadList().GetSize();
+
+ StreamString continue_packet;
+ bool continue_packet_error = false;
+ if (m_gdb_comm.HasAnyVContSupport()) {
+ if (!GetTarget().GetNonStopModeEnabled() &&
+ (m_continue_c_tids.size() == num_threads ||
+ (m_continue_c_tids.empty() && m_continue_C_tids.empty() &&
+ m_continue_s_tids.empty() && m_continue_S_tids.empty()))) {
+ // All threads are continuing, just send a "c" packet
+ continue_packet.PutCString("c");
+ } else {
+ continue_packet.PutCString("vCont");
+
+ if (!m_continue_c_tids.empty()) {
+ if (m_gdb_comm.GetVContSupported('c')) {
+ for (tid_collection::const_iterator
+ t_pos = m_continue_c_tids.begin(),
+ t_end = m_continue_c_tids.end();
+ t_pos != t_end; ++t_pos)
+ continue_packet.Printf(";c:%4.4" PRIx64, *t_pos);
+ } else
+ continue_packet_error = true;
+ }
+
+ if (!continue_packet_error && !m_continue_C_tids.empty()) {
+ if (m_gdb_comm.GetVContSupported('C')) {
+ for (tid_sig_collection::const_iterator
+ s_pos = m_continue_C_tids.begin(),
+ s_end = m_continue_C_tids.end();
+ s_pos != s_end; ++s_pos)
+ continue_packet.Printf(";C%2.2x:%4.4" PRIx64, s_pos->second,
+ s_pos->first);
+ } else
+ continue_packet_error = true;
+ }
+
+ if (!continue_packet_error && !m_continue_s_tids.empty()) {
+ if (m_gdb_comm.GetVContSupported('s')) {
+ for (tid_collection::const_iterator
+ t_pos = m_continue_s_tids.begin(),
+ t_end = m_continue_s_tids.end();
+ t_pos != t_end; ++t_pos)
+ continue_packet.Printf(";s:%4.4" PRIx64, *t_pos);
+ } else
+ continue_packet_error = true;
+ }
+
+ if (!continue_packet_error && !m_continue_S_tids.empty()) {
+ if (m_gdb_comm.GetVContSupported('S')) {
+ for (tid_sig_collection::const_iterator
+ s_pos = m_continue_S_tids.begin(),
+ s_end = m_continue_S_tids.end();
+ s_pos != s_end; ++s_pos)
+ continue_packet.Printf(";S%2.2x:%4.4" PRIx64, s_pos->second,
+ s_pos->first);
+ } else
+ continue_packet_error = true;
+ }
+
+ if (continue_packet_error)
+ continue_packet.Clear();
+ }
+ } else
+ continue_packet_error = true;
+
+ if (continue_packet_error) {
+ // Either no vCont support, or we tried to use part of the vCont packet
+ // that wasn't supported by the remote GDB server. We need to try and
+ // make a simple packet that can do our continue
+ const size_t num_continue_c_tids = m_continue_c_tids.size();
+ const size_t num_continue_C_tids = m_continue_C_tids.size();
+ const size_t num_continue_s_tids = m_continue_s_tids.size();
+ const size_t num_continue_S_tids = m_continue_S_tids.size();
+ if (num_continue_c_tids > 0) {
+ if (num_continue_c_tids == num_threads) {
+ // All threads are resuming...
+ m_gdb_comm.SetCurrentThreadForRun(-1);
+ continue_packet.PutChar('c');
+ continue_packet_error = false;
+ } else if (num_continue_c_tids == 1 && num_continue_C_tids == 0 &&
+ num_continue_s_tids == 0 && num_continue_S_tids == 0) {
+ // Only one thread is continuing
+ m_gdb_comm.SetCurrentThreadForRun(m_continue_c_tids.front());
+ continue_packet.PutChar('c');
+ continue_packet_error = false;
+ }
+ }
+
+ if (continue_packet_error && num_continue_C_tids > 0) {
+ if ((num_continue_C_tids + num_continue_c_tids) == num_threads &&
+ num_continue_C_tids > 0 && num_continue_s_tids == 0 &&
+ num_continue_S_tids == 0) {
+ const int continue_signo = m_continue_C_tids.front().second;
+ // Only one thread is continuing
+ if (num_continue_C_tids > 1) {
+ // More that one thread with a signal, yet we don't have vCont
+ // support and we are being asked to resume each thread with a
+ // signal, we need to make sure they are all the same signal, or we
+ // can't issue the continue accurately with the current support...
+ if (num_continue_C_tids > 1) {
+ continue_packet_error = false;
+ for (size_t i = 1; i < m_continue_C_tids.size(); ++i) {
+ if (m_continue_C_tids[i].second != continue_signo)
+ continue_packet_error = true;
+ }
+ }
+ if (!continue_packet_error)
+ m_gdb_comm.SetCurrentThreadForRun(-1);
+ } else {
+ // Set the continue thread ID
+ continue_packet_error = false;
+ m_gdb_comm.SetCurrentThreadForRun(m_continue_C_tids.front().first);
+ }
+ if (!continue_packet_error) {
+ // Add threads continuing with the same signo...
+ continue_packet.Printf("C%2.2x", continue_signo);
+ }
+ }
+ }
+
+ if (continue_packet_error && num_continue_s_tids > 0) {
+ if (num_continue_s_tids == num_threads) {
+ // All threads are resuming...
+ m_gdb_comm.SetCurrentThreadForRun(-1);
+
+ // If in Non-Stop-Mode use vCont when stepping
+ if (GetTarget().GetNonStopModeEnabled()) {
+ if (m_gdb_comm.GetVContSupported('s'))
+ continue_packet.PutCString("vCont;s");
+ else
+ continue_packet.PutChar('s');
+ } else
+ continue_packet.PutChar('s');
+
+ continue_packet_error = false;
+ } else if (num_continue_c_tids == 0 && num_continue_C_tids == 0 &&
+ num_continue_s_tids == 1 && num_continue_S_tids == 0) {
+ // Only one thread is stepping
+ m_gdb_comm.SetCurrentThreadForRun(m_continue_s_tids.front());
+ continue_packet.PutChar('s');
+ continue_packet_error = false;
+ }
+ }
+
+ if (!continue_packet_error && num_continue_S_tids > 0) {
+ if (num_continue_S_tids == num_threads) {
+ const int step_signo = m_continue_S_tids.front().second;
+ // Are all threads trying to step with the same signal?
+ continue_packet_error = false;
+ if (num_continue_S_tids > 1) {
+ for (size_t i = 1; i < num_threads; ++i) {
+ if (m_continue_S_tids[i].second != step_signo)
+ continue_packet_error = true;
+ }
+ }
+ if (!continue_packet_error) {
+ // Add threads stepping with the same signo...
+ m_gdb_comm.SetCurrentThreadForRun(-1);
+ continue_packet.Printf("S%2.2x", step_signo);
+ }
+ } else if (num_continue_c_tids == 0 && num_continue_C_tids == 0 &&
+ num_continue_s_tids == 0 && num_continue_S_tids == 1) {
+ // Only one thread is stepping with signal
+ m_gdb_comm.SetCurrentThreadForRun(m_continue_S_tids.front().first);
+ continue_packet.Printf("S%2.2x", m_continue_S_tids.front().second);
+ continue_packet_error = false;
+ }
+ }
+ }
+
+ if (continue_packet_error) {
+ error.SetErrorString("can't make continue packet for this resume");
+ } else {
+ EventSP event_sp;
+ if (!m_async_thread.IsJoinable()) {
+ error.SetErrorString("Trying to resume but the async thread is dead.");
+ if (log)
+ log->Printf("ProcessGDBRemote::DoResume: Trying to resume but the "
+ "async thread is dead.");
+ return error;
+ }
+
+ m_async_broadcaster.BroadcastEvent(
+ eBroadcastBitAsyncContinue,
+ new EventDataBytes(continue_packet.GetString().data(),
+ continue_packet.GetSize()));
+
+ if (!listener_sp->GetEvent(event_sp, std::chrono::seconds(5))) {
+ error.SetErrorString("Resume timed out.");
+ if (log)
+ log->Printf("ProcessGDBRemote::DoResume: Resume timed out.");
+ } else if (event_sp->BroadcasterIs(&m_async_broadcaster)) {
+ error.SetErrorString("Broadcast continue, but the async thread was "
+ "killed before we got an ack back.");
+ if (log)
+ log->Printf("ProcessGDBRemote::DoResume: Broadcast continue, but the "
+ "async thread was killed before we got an ack back.");
+ return error;
+ }
+ }
+ }
+
+ return error;
+}
+
+void ProcessGDBRemote::HandleStopReplySequence() {
+ while (true) {
+ // Send vStopped
+ StringExtractorGDBRemote response;
+ m_gdb_comm.SendPacketAndWaitForResponse("vStopped", response, false);
+
+ // OK represents end of signal list
+ if (response.IsOKResponse())
+ break;
+
+ // If not OK or a normal packet we have a problem
+ if (!response.IsNormalResponse())
+ break;
+
+ SetLastStopPacket(response);
+ }
+}
+
+void ProcessGDBRemote::ClearThreadIDList() {
+ std::lock_guard<std::recursive_mutex> guard(m_thread_list_real.GetMutex());
+ m_thread_ids.clear();
+ m_thread_pcs.clear();
+}
+
+size_t
+ProcessGDBRemote::UpdateThreadIDsFromStopReplyThreadsValue(std::string &value) {
+ m_thread_ids.clear();
+ size_t comma_pos;
+ lldb::tid_t tid;
+ while ((comma_pos = value.find(',')) != std::string::npos) {
+ value[comma_pos] = '\0';
+ // thread in big endian hex
+ tid = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_THREAD_ID, 16);
+ if (tid != LLDB_INVALID_THREAD_ID)
+ m_thread_ids.push_back(tid);
+ value.erase(0, comma_pos + 1);
+ }
+ tid = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_THREAD_ID, 16);
+ if (tid != LLDB_INVALID_THREAD_ID)
+ m_thread_ids.push_back(tid);
+ return m_thread_ids.size();
+}
+
+size_t
+ProcessGDBRemote::UpdateThreadPCsFromStopReplyThreadsValue(std::string &value) {
+ m_thread_pcs.clear();
+ size_t comma_pos;
+ lldb::addr_t pc;
+ while ((comma_pos = value.find(',')) != std::string::npos) {
+ value[comma_pos] = '\0';
+ pc = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_ADDRESS, 16);
+ if (pc != LLDB_INVALID_ADDRESS)
+ m_thread_pcs.push_back(pc);
+ value.erase(0, comma_pos + 1);
+ }
+ pc = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_ADDRESS, 16);
+ if (pc != LLDB_INVALID_THREAD_ID)
+ m_thread_pcs.push_back(pc);
+ return m_thread_pcs.size();
+}
+
+bool ProcessGDBRemote::UpdateThreadIDList() {
+ std::lock_guard<std::recursive_mutex> guard(m_thread_list_real.GetMutex());
+
+ if (m_jthreadsinfo_sp) {
+ // If we have the JSON threads info, we can get the thread list from that
+ StructuredData::Array *thread_infos = m_jthreadsinfo_sp->GetAsArray();
+ if (thread_infos && thread_infos->GetSize() > 0) {
+ m_thread_ids.clear();
+ m_thread_pcs.clear();
+ thread_infos->ForEach([this](StructuredData::Object *object) -> bool {
+ StructuredData::Dictionary *thread_dict = object->GetAsDictionary();
+ if (thread_dict) {
+ // Set the thread stop info from the JSON dictionary
+ SetThreadStopInfo(thread_dict);
+ lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
+ if (thread_dict->GetValueForKeyAsInteger<lldb::tid_t>("tid", tid))
+ m_thread_ids.push_back(tid);
+ }
+ return true; // Keep iterating through all thread_info objects
+ });
+ }
+ if (!m_thread_ids.empty())
+ return true;
+ } else {
+ // See if we can get the thread IDs from the current stop reply packets
+ // that might contain a "threads" key/value pair
+
+ // Lock the thread stack while we access it
+ // Mutex::Locker stop_stack_lock(m_last_stop_packet_mutex);
+ std::unique_lock<std::recursive_mutex> stop_stack_lock(
+ m_last_stop_packet_mutex, std::defer_lock);
+ if (stop_stack_lock.try_lock()) {
+ // Get the number of stop packets on the stack
+ int nItems = m_stop_packet_stack.size();
+ // Iterate over them
+ for (int i = 0; i < nItems; i++) {
+ // Get the thread stop info
+ StringExtractorGDBRemote &stop_info = m_stop_packet_stack[i];
+ const std::string &stop_info_str = stop_info.GetStringRef();
+
+ m_thread_pcs.clear();
+ const size_t thread_pcs_pos = stop_info_str.find(";thread-pcs:");
+ if (thread_pcs_pos != std::string::npos) {
+ const size_t start = thread_pcs_pos + strlen(";thread-pcs:");
+ const size_t end = stop_info_str.find(';', start);
+ if (end != std::string::npos) {
+ std::string value = stop_info_str.substr(start, end - start);
+ UpdateThreadPCsFromStopReplyThreadsValue(value);
+ }
+ }
+
+ const size_t threads_pos = stop_info_str.find(";threads:");
+ if (threads_pos != std::string::npos) {
+ const size_t start = threads_pos + strlen(";threads:");
+ const size_t end = stop_info_str.find(';', start);
+ if (end != std::string::npos) {
+ std::string value = stop_info_str.substr(start, end - start);
+ if (UpdateThreadIDsFromStopReplyThreadsValue(value))
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ bool sequence_mutex_unavailable = false;
+ m_gdb_comm.GetCurrentThreadIDs(m_thread_ids, sequence_mutex_unavailable);
+ if (sequence_mutex_unavailable) {
+ return false; // We just didn't get the list
+ }
+ return true;
+}
+
+bool ProcessGDBRemote::UpdateThreadList(ThreadList &old_thread_list,
+ ThreadList &new_thread_list) {
+ // locker will keep a mutex locked until it goes out of scope
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_THREAD));
+ LLDB_LOGV(log, "pid = {0}", GetID());
+
+ size_t num_thread_ids = m_thread_ids.size();
+ // The "m_thread_ids" thread ID list should always be updated after each stop
+ // reply packet, but in case it isn't, update it here.
+ if (num_thread_ids == 0) {
+ if (!UpdateThreadIDList())
+ return false;
+ num_thread_ids = m_thread_ids.size();
+ }
+
+ ThreadList old_thread_list_copy(old_thread_list);
+ if (num_thread_ids > 0) {
+ for (size_t i = 0; i < num_thread_ids; ++i) {
+ tid_t tid = m_thread_ids[i];
+ ThreadSP thread_sp(
+ old_thread_list_copy.RemoveThreadByProtocolID(tid, false));
+ if (!thread_sp) {
+ thread_sp = std::make_shared<ThreadGDBRemote>(*this, tid);
+ LLDB_LOGV(log, "Making new thread: {0} for thread ID: {1:x}.",
+ thread_sp.get(), thread_sp->GetID());
+ } else {
+ LLDB_LOGV(log, "Found old thread: {0} for thread ID: {1:x}.",
+ thread_sp.get(), thread_sp->GetID());
+ }
+
+ SetThreadPc(thread_sp, i);
+ new_thread_list.AddThreadSortedByIndexID(thread_sp);
+ }
+ }
+
+ // Whatever that is left in old_thread_list_copy are not present in
+ // new_thread_list. Remove non-existent threads from internal id table.
+ size_t old_num_thread_ids = old_thread_list_copy.GetSize(false);
+ for (size_t i = 0; i < old_num_thread_ids; i++) {
+ ThreadSP old_thread_sp(old_thread_list_copy.GetThreadAtIndex(i, false));
+ if (old_thread_sp) {
+ lldb::tid_t old_thread_id = old_thread_sp->GetProtocolID();
+ m_thread_id_to_index_id_map.erase(old_thread_id);
+ }
+ }
+
+ return true;
+}
+
+void ProcessGDBRemote::SetThreadPc(const ThreadSP &thread_sp, uint64_t index) {
+ if (m_thread_ids.size() == m_thread_pcs.size() && thread_sp.get() &&
+ GetByteOrder() != eByteOrderInvalid) {
+ ThreadGDBRemote *gdb_thread =
+ static_cast<ThreadGDBRemote *>(thread_sp.get());
+ RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext());
+ if (reg_ctx_sp) {
+ uint32_t pc_regnum = reg_ctx_sp->ConvertRegisterKindToRegisterNumber(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ if (pc_regnum != LLDB_INVALID_REGNUM) {
+ gdb_thread->PrivateSetRegisterValue(pc_regnum, m_thread_pcs[index]);
+ }
+ }
+ }
+}
+
+bool ProcessGDBRemote::GetThreadStopInfoFromJSON(
+ ThreadGDBRemote *thread, const StructuredData::ObjectSP &thread_infos_sp) {
+ // See if we got thread stop infos for all threads via the "jThreadsInfo"
+ // packet
+ if (thread_infos_sp) {
+ StructuredData::Array *thread_infos = thread_infos_sp->GetAsArray();
+ if (thread_infos) {
+ lldb::tid_t tid;
+ const size_t n = thread_infos->GetSize();
+ for (size_t i = 0; i < n; ++i) {
+ StructuredData::Dictionary *thread_dict =
+ thread_infos->GetItemAtIndex(i)->GetAsDictionary();
+ if (thread_dict) {
+ if (thread_dict->GetValueForKeyAsInteger<lldb::tid_t>(
+ "tid", tid, LLDB_INVALID_THREAD_ID)) {
+ if (tid == thread->GetID())
+ return (bool)SetThreadStopInfo(thread_dict);
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool ProcessGDBRemote::CalculateThreadStopInfo(ThreadGDBRemote *thread) {
+ // See if we got thread stop infos for all threads via the "jThreadsInfo"
+ // packet
+ if (GetThreadStopInfoFromJSON(thread, m_jthreadsinfo_sp))
+ return true;
+
+ // See if we got thread stop info for any threads valid stop info reasons
+ // threads via the "jstopinfo" packet stop reply packet key/value pair?
+ if (m_jstopinfo_sp) {
+ // If we have "jstopinfo" then we have stop descriptions for all threads
+ // that have stop reasons, and if there is no entry for a thread, then it
+ // has no stop reason.
+ thread->GetRegisterContext()->InvalidateIfNeeded(true);
+ if (!GetThreadStopInfoFromJSON(thread, m_jstopinfo_sp)) {
+ thread->SetStopInfo(StopInfoSP());
+ }
+ return true;
+ }
+
+ // Fall back to using the qThreadStopInfo packet
+ StringExtractorGDBRemote stop_packet;
+ if (GetGDBRemote().GetThreadStopInfo(thread->GetProtocolID(), stop_packet))
+ return SetThreadStopInfo(stop_packet) == eStateStopped;
+ return false;
+}
+
+ThreadSP ProcessGDBRemote::SetThreadStopInfo(
+ lldb::tid_t tid, ExpeditedRegisterMap &expedited_register_map,
+ uint8_t signo, const std::string &thread_name, const std::string &reason,
+ const std::string &description, uint32_t exc_type,
+ const std::vector<addr_t> &exc_data, addr_t thread_dispatch_qaddr,
+ bool queue_vars_valid, // Set to true if queue_name, queue_kind and
+ // queue_serial are valid
+ LazyBool associated_with_dispatch_queue, addr_t dispatch_queue_t,
+ std::string &queue_name, QueueKind queue_kind, uint64_t queue_serial) {
+ ThreadSP thread_sp;
+ if (tid != LLDB_INVALID_THREAD_ID) {
+ // Scope for "locker" below
+ {
+ // m_thread_list_real does have its own mutex, but we need to hold onto
+ // the mutex between the call to m_thread_list_real.FindThreadByID(...)
+ // and the m_thread_list_real.AddThread(...) so it doesn't change on us
+ std::lock_guard<std::recursive_mutex> guard(
+ m_thread_list_real.GetMutex());
+ thread_sp = m_thread_list_real.FindThreadByProtocolID(tid, false);
+
+ if (!thread_sp) {
+ // Create the thread if we need to
+ thread_sp = std::make_shared<ThreadGDBRemote>(*this, tid);
+ m_thread_list_real.AddThread(thread_sp);
+ }
+ }
+
+ if (thread_sp) {
+ ThreadGDBRemote *gdb_thread =
+ static_cast<ThreadGDBRemote *>(thread_sp.get());
+ gdb_thread->GetRegisterContext()->InvalidateIfNeeded(true);
+
+ auto iter = std::find(m_thread_ids.begin(), m_thread_ids.end(), tid);
+ if (iter != m_thread_ids.end()) {
+ SetThreadPc(thread_sp, iter - m_thread_ids.begin());
+ }
+
+ for (const auto &pair : expedited_register_map) {
+ StringExtractor reg_value_extractor;
+ reg_value_extractor.GetStringRef() = pair.second;
+ DataBufferSP buffer_sp(new DataBufferHeap(
+ reg_value_extractor.GetStringRef().size() / 2, 0));
+ reg_value_extractor.GetHexBytes(buffer_sp->GetData(), '\xcc');
+ gdb_thread->PrivateSetRegisterValue(pair.first, buffer_sp->GetData());
+ }
+
+ thread_sp->SetName(thread_name.empty() ? nullptr : thread_name.c_str());
+
+ gdb_thread->SetThreadDispatchQAddr(thread_dispatch_qaddr);
+ // Check if the GDB server was able to provide the queue name, kind and
+ // serial number
+ if (queue_vars_valid)
+ gdb_thread->SetQueueInfo(std::move(queue_name), queue_kind,
+ queue_serial, dispatch_queue_t,
+ associated_with_dispatch_queue);
+ else
+ gdb_thread->ClearQueueInfo();
+
+ gdb_thread->SetAssociatedWithLibdispatchQueue(
+ associated_with_dispatch_queue);
+
+ if (dispatch_queue_t != LLDB_INVALID_ADDRESS)
+ gdb_thread->SetQueueLibdispatchQueueAddress(dispatch_queue_t);
+
+ // Make sure we update our thread stop reason just once
+ if (!thread_sp->StopInfoIsUpToDate()) {
+ thread_sp->SetStopInfo(StopInfoSP());
+ // If there's a memory thread backed by this thread, we need to use it
+ // to calculate StopInfo.
+ if (ThreadSP memory_thread_sp =
+ m_thread_list.GetBackingThread(thread_sp))
+ thread_sp = memory_thread_sp;
+
+ if (exc_type != 0) {
+ const size_t exc_data_size = exc_data.size();
+
+ thread_sp->SetStopInfo(
+ StopInfoMachException::CreateStopReasonWithMachException(
+ *thread_sp, exc_type, exc_data_size,
+ exc_data_size >= 1 ? exc_data[0] : 0,
+ exc_data_size >= 2 ? exc_data[1] : 0,
+ exc_data_size >= 3 ? exc_data[2] : 0));
+ } else {
+ bool handled = false;
+ bool did_exec = false;
+ if (!reason.empty()) {
+ if (reason == "trace") {
+ addr_t pc = thread_sp->GetRegisterContext()->GetPC();
+ lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()
+ ->GetBreakpointSiteList()
+ .FindByAddress(pc);
+
+ // If the current pc is a breakpoint site then the StopInfo
+ // should be set to Breakpoint Otherwise, it will be set to
+ // Trace.
+ if (bp_site_sp &&
+ bp_site_sp->ValidForThisThread(thread_sp.get())) {
+ thread_sp->SetStopInfo(
+ StopInfo::CreateStopReasonWithBreakpointSiteID(
+ *thread_sp, bp_site_sp->GetID()));
+ } else
+ thread_sp->SetStopInfo(
+ StopInfo::CreateStopReasonToTrace(*thread_sp));
+ handled = true;
+ } else if (reason == "breakpoint") {
+ addr_t pc = thread_sp->GetRegisterContext()->GetPC();
+ lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()
+ ->GetBreakpointSiteList()
+ .FindByAddress(pc);
+ if (bp_site_sp) {
+ // If the breakpoint is for this thread, then we'll report the
+ // hit, but if it is for another thread, we can just report no
+ // reason. We don't need to worry about stepping over the
+ // breakpoint here, that will be taken care of when the thread
+ // resumes and notices that there's a breakpoint under the pc.
+ handled = true;
+ if (bp_site_sp->ValidForThisThread(thread_sp.get())) {
+ thread_sp->SetStopInfo(
+ StopInfo::CreateStopReasonWithBreakpointSiteID(
+ *thread_sp, bp_site_sp->GetID()));
+ } else {
+ StopInfoSP invalid_stop_info_sp;
+ thread_sp->SetStopInfo(invalid_stop_info_sp);
+ }
+ }
+ } else if (reason == "trap") {
+ // Let the trap just use the standard signal stop reason below...
+ } else if (reason == "watchpoint") {
+ StringExtractor desc_extractor(description.c_str());
+ addr_t wp_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS);
+ uint32_t wp_index = desc_extractor.GetU32(LLDB_INVALID_INDEX32);
+ addr_t wp_hit_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS);
+ watch_id_t watch_id = LLDB_INVALID_WATCH_ID;
+ if (wp_addr != LLDB_INVALID_ADDRESS) {
+ WatchpointSP wp_sp;
+ ArchSpec::Core core = GetTarget().GetArchitecture().GetCore();
+ if ((core >= ArchSpec::kCore_mips_first &&
+ core <= ArchSpec::kCore_mips_last) ||
+ (core >= ArchSpec::eCore_arm_generic &&
+ core <= ArchSpec::eCore_arm_aarch64))
+ wp_sp = GetTarget().GetWatchpointList().FindByAddress(
+ wp_hit_addr);
+ if (!wp_sp)
+ wp_sp =
+ GetTarget().GetWatchpointList().FindByAddress(wp_addr);
+ if (wp_sp) {
+ wp_sp->SetHardwareIndex(wp_index);
+ watch_id = wp_sp->GetID();
+ }
+ }
+ if (watch_id == LLDB_INVALID_WATCH_ID) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(
+ GDBR_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf("failed to find watchpoint");
+ }
+ thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithWatchpointID(
+ *thread_sp, watch_id, wp_hit_addr));
+ handled = true;
+ } else if (reason == "exception") {
+ thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithException(
+ *thread_sp, description.c_str()));
+ handled = true;
+ } else if (reason == "exec") {
+ did_exec = true;
+ thread_sp->SetStopInfo(
+ StopInfo::CreateStopReasonWithExec(*thread_sp));
+ handled = true;
+ }
+ } else if (!signo) {
+ addr_t pc = thread_sp->GetRegisterContext()->GetPC();
+ lldb::BreakpointSiteSP bp_site_sp =
+ thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(
+ pc);
+
+ // If the current pc is a breakpoint site then the StopInfo should
+ // be set to Breakpoint even though the remote stub did not set it
+ // as such. This can happen when the thread is involuntarily
+ // interrupted (e.g. due to stops on other threads) just as it is
+ // about to execute the breakpoint instruction.
+ if (bp_site_sp && bp_site_sp->ValidForThisThread(thread_sp.get())) {
+ thread_sp->SetStopInfo(
+ StopInfo::CreateStopReasonWithBreakpointSiteID(
+ *thread_sp, bp_site_sp->GetID()));
+ handled = true;
+ }
+ }
+
+ if (!handled && signo && !did_exec) {
+ if (signo == SIGTRAP) {
+ // Currently we are going to assume SIGTRAP means we are either
+ // hitting a breakpoint or hardware single stepping.
+ handled = true;
+ addr_t pc = thread_sp->GetRegisterContext()->GetPC() +
+ m_breakpoint_pc_offset;
+ lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()
+ ->GetBreakpointSiteList()
+ .FindByAddress(pc);
+
+ if (bp_site_sp) {
+ // If the breakpoint is for this thread, then we'll report the
+ // hit, but if it is for another thread, we can just report no
+ // reason. We don't need to worry about stepping over the
+ // breakpoint here, that will be taken care of when the thread
+ // resumes and notices that there's a breakpoint under the pc.
+ if (bp_site_sp->ValidForThisThread(thread_sp.get())) {
+ if (m_breakpoint_pc_offset != 0)
+ thread_sp->GetRegisterContext()->SetPC(pc);
+ thread_sp->SetStopInfo(
+ StopInfo::CreateStopReasonWithBreakpointSiteID(
+ *thread_sp, bp_site_sp->GetID()));
+ } else {
+ StopInfoSP invalid_stop_info_sp;
+ thread_sp->SetStopInfo(invalid_stop_info_sp);
+ }
+ } else {
+ // If we were stepping then assume the stop was the result of
+ // the trace. If we were not stepping then report the SIGTRAP.
+ // FIXME: We are still missing the case where we single step
+ // over a trap instruction.
+ if (thread_sp->GetTemporaryResumeState() == eStateStepping)
+ thread_sp->SetStopInfo(
+ StopInfo::CreateStopReasonToTrace(*thread_sp));
+ else
+ thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithSignal(
+ *thread_sp, signo, description.c_str()));
+ }
+ }
+ if (!handled)
+ thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithSignal(
+ *thread_sp, signo, description.c_str()));
+ }
+
+ if (!description.empty()) {
+ lldb::StopInfoSP stop_info_sp(thread_sp->GetStopInfo());
+ if (stop_info_sp) {
+ const char *stop_info_desc = stop_info_sp->GetDescription();
+ if (!stop_info_desc || !stop_info_desc[0])
+ stop_info_sp->SetDescription(description.c_str());
+ } else {
+ thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithException(
+ *thread_sp, description.c_str()));
+ }
+ }
+ }
+ }
+ }
+ }
+ return thread_sp;
+}
+
+lldb::ThreadSP
+ProcessGDBRemote::SetThreadStopInfo(StructuredData::Dictionary *thread_dict) {
+ static ConstString g_key_tid("tid");
+ static ConstString g_key_name("name");
+ static ConstString g_key_reason("reason");
+ static ConstString g_key_metype("metype");
+ static ConstString g_key_medata("medata");
+ static ConstString g_key_qaddr("qaddr");
+ static ConstString g_key_dispatch_queue_t("dispatch_queue_t");
+ static ConstString g_key_associated_with_dispatch_queue(
+ "associated_with_dispatch_queue");
+ static ConstString g_key_queue_name("qname");
+ static ConstString g_key_queue_kind("qkind");
+ static ConstString g_key_queue_serial_number("qserialnum");
+ static ConstString g_key_registers("registers");
+ static ConstString g_key_memory("memory");
+ static ConstString g_key_address("address");
+ static ConstString g_key_bytes("bytes");
+ static ConstString g_key_description("description");
+ static ConstString g_key_signal("signal");
+
+ // Stop with signal and thread info
+ lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
+ uint8_t signo = 0;
+ std::string value;
+ std::string thread_name;
+ std::string reason;
+ std::string description;
+ uint32_t exc_type = 0;
+ std::vector<addr_t> exc_data;
+ addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS;
+ ExpeditedRegisterMap expedited_register_map;
+ bool queue_vars_valid = false;
+ addr_t dispatch_queue_t = LLDB_INVALID_ADDRESS;
+ LazyBool associated_with_dispatch_queue = eLazyBoolCalculate;
+ std::string queue_name;
+ QueueKind queue_kind = eQueueKindUnknown;
+ uint64_t queue_serial_number = 0;
+ // Iterate through all of the thread dictionary key/value pairs from the
+ // structured data dictionary
+
+ thread_dict->ForEach([this, &tid, &expedited_register_map, &thread_name,
+ &signo, &reason, &description, &exc_type, &exc_data,
+ &thread_dispatch_qaddr, &queue_vars_valid,
+ &associated_with_dispatch_queue, &dispatch_queue_t,
+ &queue_name, &queue_kind, &queue_serial_number](
+ ConstString key,
+ StructuredData::Object *object) -> bool {
+ if (key == g_key_tid) {
+ // thread in big endian hex
+ tid = object->GetIntegerValue(LLDB_INVALID_THREAD_ID);
+ } else if (key == g_key_metype) {
+ // exception type in big endian hex
+ exc_type = object->GetIntegerValue(0);
+ } else if (key == g_key_medata) {
+ // exception data in big endian hex
+ StructuredData::Array *array = object->GetAsArray();
+ if (array) {
+ array->ForEach([&exc_data](StructuredData::Object *object) -> bool {
+ exc_data.push_back(object->GetIntegerValue());
+ return true; // Keep iterating through all array items
+ });
+ }
+ } else if (key == g_key_name) {
+ thread_name = object->GetStringValue();
+ } else if (key == g_key_qaddr) {
+ thread_dispatch_qaddr = object->GetIntegerValue(LLDB_INVALID_ADDRESS);
+ } else if (key == g_key_queue_name) {
+ queue_vars_valid = true;
+ queue_name = object->GetStringValue();
+ } else if (key == g_key_queue_kind) {
+ std::string queue_kind_str = object->GetStringValue();
+ if (queue_kind_str == "serial") {
+ queue_vars_valid = true;
+ queue_kind = eQueueKindSerial;
+ } else if (queue_kind_str == "concurrent") {
+ queue_vars_valid = true;
+ queue_kind = eQueueKindConcurrent;
+ }
+ } else if (key == g_key_queue_serial_number) {
+ queue_serial_number = object->GetIntegerValue(0);
+ if (queue_serial_number != 0)
+ queue_vars_valid = true;
+ } else if (key == g_key_dispatch_queue_t) {
+ dispatch_queue_t = object->GetIntegerValue(0);
+ if (dispatch_queue_t != 0 && dispatch_queue_t != LLDB_INVALID_ADDRESS)
+ queue_vars_valid = true;
+ } else if (key == g_key_associated_with_dispatch_queue) {
+ queue_vars_valid = true;
+ bool associated = object->GetBooleanValue();
+ if (associated)
+ associated_with_dispatch_queue = eLazyBoolYes;
+ else
+ associated_with_dispatch_queue = eLazyBoolNo;
+ } else if (key == g_key_reason) {
+ reason = object->GetStringValue();
+ } else if (key == g_key_description) {
+ description = object->GetStringValue();
+ } else if (key == g_key_registers) {
+ StructuredData::Dictionary *registers_dict = object->GetAsDictionary();
+
+ if (registers_dict) {
+ registers_dict->ForEach(
+ [&expedited_register_map](ConstString key,
+ StructuredData::Object *object) -> bool {
+ const uint32_t reg =
+ StringConvert::ToUInt32(key.GetCString(), UINT32_MAX, 10);
+ if (reg != UINT32_MAX)
+ expedited_register_map[reg] = object->GetStringValue();
+ return true; // Keep iterating through all array items
+ });
+ }
+ } else if (key == g_key_memory) {
+ StructuredData::Array *array = object->GetAsArray();
+ if (array) {
+ array->ForEach([this](StructuredData::Object *object) -> bool {
+ StructuredData::Dictionary *mem_cache_dict =
+ object->GetAsDictionary();
+ if (mem_cache_dict) {
+ lldb::addr_t mem_cache_addr = LLDB_INVALID_ADDRESS;
+ if (mem_cache_dict->GetValueForKeyAsInteger<lldb::addr_t>(
+ "address", mem_cache_addr)) {
+ if (mem_cache_addr != LLDB_INVALID_ADDRESS) {
+ llvm::StringRef str;
+ if (mem_cache_dict->GetValueForKeyAsString("bytes", str)) {
+ StringExtractor bytes(str);
+ bytes.SetFilePos(0);
+
+ const size_t byte_size = bytes.GetStringRef().size() / 2;
+ DataBufferSP data_buffer_sp(new DataBufferHeap(byte_size, 0));
+ const size_t bytes_copied =
+ bytes.GetHexBytes(data_buffer_sp->GetData(), 0);
+ if (bytes_copied == byte_size)
+ m_memory_cache.AddL1CacheData(mem_cache_addr,
+ data_buffer_sp);
+ }
+ }
+ }
+ }
+ return true; // Keep iterating through all array items
+ });
+ }
+
+ } else if (key == g_key_signal)
+ signo = object->GetIntegerValue(LLDB_INVALID_SIGNAL_NUMBER);
+ return true; // Keep iterating through all dictionary key/value pairs
+ });
+
+ return SetThreadStopInfo(tid, expedited_register_map, signo, thread_name,
+ reason, description, exc_type, exc_data,
+ thread_dispatch_qaddr, queue_vars_valid,
+ associated_with_dispatch_queue, dispatch_queue_t,
+ queue_name, queue_kind, queue_serial_number);
+}
+
+StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) {
+ stop_packet.SetFilePos(0);
+ const char stop_type = stop_packet.GetChar();
+ switch (stop_type) {
+ case 'T':
+ case 'S': {
+ // This is a bit of a hack, but is is required. If we did exec, we need to
+ // clear our thread lists and also know to rebuild our dynamic register
+ // info before we lookup and threads and populate the expedited register
+ // values so we need to know this right away so we can cleanup and update
+ // our registers.
+ const uint32_t stop_id = GetStopID();
+ if (stop_id == 0) {
+ // Our first stop, make sure we have a process ID, and also make sure we
+ // know about our registers
+ if (GetID() == LLDB_INVALID_PROCESS_ID) {
+ lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID();
+ if (pid != LLDB_INVALID_PROCESS_ID)
+ SetID(pid);
+ }
+ BuildDynamicRegisterInfo(true);
+ }
+ // Stop with signal and thread info
+ lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
+ const uint8_t signo = stop_packet.GetHexU8();
+ llvm::StringRef key;
+ llvm::StringRef value;
+ std::string thread_name;
+ std::string reason;
+ std::string description;
+ uint32_t exc_type = 0;
+ std::vector<addr_t> exc_data;
+ addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS;
+ bool queue_vars_valid =
+ false; // says if locals below that start with "queue_" are valid
+ addr_t dispatch_queue_t = LLDB_INVALID_ADDRESS;
+ LazyBool associated_with_dispatch_queue = eLazyBoolCalculate;
+ std::string queue_name;
+ QueueKind queue_kind = eQueueKindUnknown;
+ uint64_t queue_serial_number = 0;
+ ExpeditedRegisterMap expedited_register_map;
+ while (stop_packet.GetNameColonValue(key, value)) {
+ if (key.compare("metype") == 0) {
+ // exception type in big endian hex
+ value.getAsInteger(16, exc_type);
+ } else if (key.compare("medata") == 0) {
+ // exception data in big endian hex
+ uint64_t x;
+ value.getAsInteger(16, x);
+ exc_data.push_back(x);
+ } else if (key.compare("thread") == 0) {
+ // thread in big endian hex
+ if (value.getAsInteger(16, tid))
+ tid = LLDB_INVALID_THREAD_ID;
+ } else if (key.compare("threads") == 0) {
+ std::lock_guard<std::recursive_mutex> guard(
+ m_thread_list_real.GetMutex());
+
+ m_thread_ids.clear();
+ // A comma separated list of all threads in the current
+ // process that includes the thread for this stop reply packet
+ lldb::tid_t tid;
+ while (!value.empty()) {
+ llvm::StringRef tid_str;
+ std::tie(tid_str, value) = value.split(',');
+ if (tid_str.getAsInteger(16, tid))
+ tid = LLDB_INVALID_THREAD_ID;
+ m_thread_ids.push_back(tid);
+ }
+ } else if (key.compare("thread-pcs") == 0) {
+ m_thread_pcs.clear();
+ // A comma separated list of all threads in the current
+ // process that includes the thread for this stop reply packet
+ lldb::addr_t pc;
+ while (!value.empty()) {
+ llvm::StringRef pc_str;
+ std::tie(pc_str, value) = value.split(',');
+ if (pc_str.getAsInteger(16, pc))
+ pc = LLDB_INVALID_ADDRESS;
+ m_thread_pcs.push_back(pc);
+ }
+ } else if (key.compare("jstopinfo") == 0) {
+ StringExtractor json_extractor(value);
+ std::string json;
+ // Now convert the HEX bytes into a string value
+ json_extractor.GetHexByteString(json);
+
+ // This JSON contains thread IDs and thread stop info for all threads.
+ // It doesn't contain expedited registers, memory or queue info.
+ m_jstopinfo_sp = StructuredData::ParseJSON(json);
+ } else if (key.compare("hexname") == 0) {
+ StringExtractor name_extractor(value);
+ std::string name;
+ // Now convert the HEX bytes into a string value
+ name_extractor.GetHexByteString(thread_name);
+ } else if (key.compare("name") == 0) {
+ thread_name = value;
+ } else if (key.compare("qaddr") == 0) {
+ value.getAsInteger(16, thread_dispatch_qaddr);
+ } else if (key.compare("dispatch_queue_t") == 0) {
+ queue_vars_valid = true;
+ value.getAsInteger(16, dispatch_queue_t);
+ } else if (key.compare("qname") == 0) {
+ queue_vars_valid = true;
+ StringExtractor name_extractor(value);
+ // Now convert the HEX bytes into a string value
+ name_extractor.GetHexByteString(queue_name);
+ } else if (key.compare("qkind") == 0) {
+ queue_kind = llvm::StringSwitch<QueueKind>(value)
+ .Case("serial", eQueueKindSerial)
+ .Case("concurrent", eQueueKindConcurrent)
+ .Default(eQueueKindUnknown);
+ queue_vars_valid = queue_kind != eQueueKindUnknown;
+ } else if (key.compare("qserialnum") == 0) {
+ if (!value.getAsInteger(0, queue_serial_number))
+ queue_vars_valid = true;
+ } else if (key.compare("reason") == 0) {
+ reason = value;
+ } else if (key.compare("description") == 0) {
+ StringExtractor desc_extractor(value);
+ // Now convert the HEX bytes into a string value
+ desc_extractor.GetHexByteString(description);
+ } else if (key.compare("memory") == 0) {
+ // Expedited memory. GDB servers can choose to send back expedited
+ // memory that can populate the L1 memory cache in the process so that
+ // things like the frame pointer backchain can be expedited. This will
+ // help stack backtracing be more efficient by not having to send as
+ // many memory read requests down the remote GDB server.
+
+ // Key/value pair format: memory:<addr>=<bytes>;
+ // <addr> is a number whose base will be interpreted by the prefix:
+ // "0x[0-9a-fA-F]+" for hex
+ // "0[0-7]+" for octal
+ // "[1-9]+" for decimal
+ // <bytes> is native endian ASCII hex bytes just like the register
+ // values
+ llvm::StringRef addr_str, bytes_str;
+ std::tie(addr_str, bytes_str) = value.split('=');
+ if (!addr_str.empty() && !bytes_str.empty()) {
+ lldb::addr_t mem_cache_addr = LLDB_INVALID_ADDRESS;
+ if (!addr_str.getAsInteger(0, mem_cache_addr)) {
+ StringExtractor bytes(bytes_str);
+ const size_t byte_size = bytes.GetBytesLeft() / 2;
+ DataBufferSP data_buffer_sp(new DataBufferHeap(byte_size, 0));
+ const size_t bytes_copied =
+ bytes.GetHexBytes(data_buffer_sp->GetData(), 0);
+ if (bytes_copied == byte_size)
+ m_memory_cache.AddL1CacheData(mem_cache_addr, data_buffer_sp);
+ }
+ }
+ } else if (key.compare("watch") == 0 || key.compare("rwatch") == 0 ||
+ key.compare("awatch") == 0) {
+ // Support standard GDB remote stop reply packet 'TAAwatch:addr'
+ lldb::addr_t wp_addr = LLDB_INVALID_ADDRESS;
+ value.getAsInteger(16, wp_addr);
+
+ WatchpointSP wp_sp =
+ GetTarget().GetWatchpointList().FindByAddress(wp_addr);
+ uint32_t wp_index = LLDB_INVALID_INDEX32;
+
+ if (wp_sp)
+ wp_index = wp_sp->GetHardwareIndex();
+
+ reason = "watchpoint";
+ StreamString ostr;
+ ostr.Printf("%" PRIu64 " %" PRIu32, wp_addr, wp_index);
+ description = ostr.GetString();
+ } else if (key.compare("library") == 0) {
+ LoadModules();
+ } else if (key.size() == 2 && ::isxdigit(key[0]) && ::isxdigit(key[1])) {
+ uint32_t reg = UINT32_MAX;
+ if (!key.getAsInteger(16, reg))
+ expedited_register_map[reg] = std::move(value);
+ }
+ }
+
+ if (tid == LLDB_INVALID_THREAD_ID) {
+ // A thread id may be invalid if the response is old style 'S' packet
+ // which does not provide the
+ // thread information. So update the thread list and choose the first
+ // one.
+ UpdateThreadIDList();
+
+ if (!m_thread_ids.empty()) {
+ tid = m_thread_ids.front();
+ }
+ }
+
+ ThreadSP thread_sp = SetThreadStopInfo(
+ tid, expedited_register_map, signo, thread_name, reason, description,
+ exc_type, exc_data, thread_dispatch_qaddr, queue_vars_valid,
+ associated_with_dispatch_queue, dispatch_queue_t, queue_name,
+ queue_kind, queue_serial_number);
+
+ return eStateStopped;
+ } break;
+
+ case 'W':
+ case 'X':
+ // process exited
+ return eStateExited;
+
+ default:
+ break;
+ }
+ return eStateInvalid;
+}
+
+void ProcessGDBRemote::RefreshStateAfterStop() {
+ std::lock_guard<std::recursive_mutex> guard(m_thread_list_real.GetMutex());
+
+ m_thread_ids.clear();
+ m_thread_pcs.clear();
+ // Set the thread stop info. It might have a "threads" key whose value is a
+ // list of all thread IDs in the current process, so m_thread_ids might get
+ // set.
+
+ // Scope for the lock
+ {
+ // Check to see if SetThreadStopInfo() filled in m_thread_ids?
+ if (m_thread_ids.empty()) {
+ // No, we need to fetch the thread list manually
+ UpdateThreadIDList();
+ }
+ // We might set some stop info's so make sure the thread list is up to
+ // date before we do that or we might overwrite what was computed here.
+ UpdateThreadListIfNeeded();
+
+ // Lock the thread stack while we access it
+ std::lock_guard<std::recursive_mutex> guard(m_last_stop_packet_mutex);
+ // Get the number of stop packets on the stack
+ int nItems = m_stop_packet_stack.size();
+ // Iterate over them
+ for (int i = 0; i < nItems; i++) {
+ // Get the thread stop info
+ StringExtractorGDBRemote stop_info = m_stop_packet_stack[i];
+ // Process thread stop info
+ SetThreadStopInfo(stop_info);
+ }
+ // Clear the thread stop stack
+ m_stop_packet_stack.clear();
+ }
+
+ // If we have queried for a default thread id
+ if (m_initial_tid != LLDB_INVALID_THREAD_ID) {
+ m_thread_list.SetSelectedThreadByID(m_initial_tid);
+ m_initial_tid = LLDB_INVALID_THREAD_ID;
+ }
+
+ // Let all threads recover from stopping and do any clean up based on the
+ // previous thread state (if any).
+ m_thread_list_real.RefreshStateAfterStop();
+}
+
+Status ProcessGDBRemote::DoHalt(bool &caused_stop) {
+ Status error;
+
+ if (m_public_state.GetValue() == eStateAttaching) {
+ // We are being asked to halt during an attach. We need to just close our
+ // file handle and debugserver will go away, and we can be done...
+ m_gdb_comm.Disconnect();
+ } else
+ caused_stop = m_gdb_comm.Interrupt();
+ return error;
+}
+
+Status ProcessGDBRemote::DoDetach(bool keep_stopped) {
+ Status error;
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf("ProcessGDBRemote::DoDetach(keep_stopped: %i)", keep_stopped);
+
+ error = m_gdb_comm.Detach(keep_stopped);
+ if (log) {
+ if (error.Success())
+ log->PutCString(
+ "ProcessGDBRemote::DoDetach() detach packet sent successfully");
+ else
+ log->Printf("ProcessGDBRemote::DoDetach() detach packet send failed: %s",
+ error.AsCString() ? error.AsCString() : "<unknown error>");
+ }
+
+ if (!error.Success())
+ return error;
+
+ // Sleep for one second to let the process get all detached...
+ StopAsyncThread();
+
+ SetPrivateState(eStateDetached);
+ ResumePrivateStateThread();
+
+ // KillDebugserverProcess ();
+ return error;
+}
+
+Status ProcessGDBRemote::DoDestroy() {
+ Status error;
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf("ProcessGDBRemote::DoDestroy()");
+
+#ifdef LLDB_ENABLE_ALL // XXX Currently no iOS target support on FreeBSD
+ // There is a bug in older iOS debugservers where they don't shut down the
+ // process they are debugging properly. If the process is sitting at a
+ // breakpoint or an exception, this can cause problems with restarting. So
+ // we check to see if any of our threads are stopped at a breakpoint, and if
+ // so we remove all the breakpoints, resume the process, and THEN destroy it
+ // again.
+ //
+ // Note, we don't have a good way to test the version of debugserver, but I
+ // happen to know that the set of all the iOS debugservers which don't
+ // support GetThreadSuffixSupported() and that of the debugservers with this
+ // bug are equal. There really should be a better way to test this!
+ //
+ // We also use m_destroy_tried_resuming to make sure we only do this once, if
+ // we resume and then halt and get called here to destroy again and we're
+ // still at a breakpoint or exception, then we should just do the straight-
+ // forward kill.
+ //
+ // And of course, if we weren't able to stop the process by the time we get
+ // here, it isn't necessary (or helpful) to do any of this.
+
+ if (!m_gdb_comm.GetThreadSuffixSupported() &&
+ m_public_state.GetValue() != eStateRunning) {
+ PlatformSP platform_sp = GetTarget().GetPlatform();
+
+ // FIXME: These should be ConstStrings so we aren't doing strcmp'ing.
+ if (platform_sp && platform_sp->GetName() &&
+ platform_sp->GetName() == PlatformRemoteiOS::GetPluginNameStatic()) {
+ if (m_destroy_tried_resuming) {
+ if (log)
+ log->PutCString("ProcessGDBRemote::DoDestroy() - Tried resuming to "
+ "destroy once already, not doing it again.");
+ } else {
+ // At present, the plans are discarded and the breakpoints disabled
+ // Process::Destroy, but we really need it to happen here and it
+ // doesn't matter if we do it twice.
+ m_thread_list.DiscardThreadPlans();
+ DisableAllBreakpointSites();
+
+ bool stop_looks_like_crash = false;
+ ThreadList &threads = GetThreadList();
+
+ {
+ std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
+
+ size_t num_threads = threads.GetSize();
+ for (size_t i = 0; i < num_threads; i++) {
+ ThreadSP thread_sp = threads.GetThreadAtIndex(i);
+ StopInfoSP stop_info_sp = thread_sp->GetPrivateStopInfo();
+ StopReason reason = eStopReasonInvalid;
+ if (stop_info_sp)
+ reason = stop_info_sp->GetStopReason();
+ if (reason == eStopReasonBreakpoint ||
+ reason == eStopReasonException) {
+ if (log)
+ log->Printf(
+ "ProcessGDBRemote::DoDestroy() - thread: 0x%4.4" PRIx64
+ " stopped with reason: %s.",
+ thread_sp->GetProtocolID(), stop_info_sp->GetDescription());
+ stop_looks_like_crash = true;
+ break;
+ }
+ }
+ }
+
+ if (stop_looks_like_crash) {
+ if (log)
+ log->PutCString("ProcessGDBRemote::DoDestroy() - Stopped at a "
+ "breakpoint, continue and then kill.");
+ m_destroy_tried_resuming = true;
+
+ // If we are going to run again before killing, it would be good to
+ // suspend all the threads before resuming so they won't get into
+ // more trouble. Sadly, for the threads stopped with the breakpoint
+ // or exception, the exception doesn't get cleared if it is
+ // suspended, so we do have to run the risk of letting those threads
+ // proceed a bit.
+
+ {
+ std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
+
+ size_t num_threads = threads.GetSize();
+ for (size_t i = 0; i < num_threads; i++) {
+ ThreadSP thread_sp = threads.GetThreadAtIndex(i);
+ StopInfoSP stop_info_sp = thread_sp->GetPrivateStopInfo();
+ StopReason reason = eStopReasonInvalid;
+ if (stop_info_sp)
+ reason = stop_info_sp->GetStopReason();
+ if (reason != eStopReasonBreakpoint &&
+ reason != eStopReasonException) {
+ if (log)
+ log->Printf("ProcessGDBRemote::DoDestroy() - Suspending "
+ "thread: 0x%4.4" PRIx64 " before running.",
+ thread_sp->GetProtocolID());
+ thread_sp->SetResumeState(eStateSuspended);
+ }
+ }
+ }
+ Resume();
+ return Destroy(false);
+ }
+ }
+ }
+ }
+#endif // LLDB_ENABLE_ALL
+
+ // Interrupt if our inferior is running...
+ int exit_status = SIGABRT;
+ std::string exit_string;
+
+ if (m_gdb_comm.IsConnected()) {
+ if (m_public_state.GetValue() != eStateAttaching) {
+ StringExtractorGDBRemote response;
+ bool send_async = true;
+ GDBRemoteCommunication::ScopedTimeout(m_gdb_comm,
+ std::chrono::seconds(3));
+
+ if (m_gdb_comm.SendPacketAndWaitForResponse("k", response, send_async) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ char packet_cmd = response.GetChar(0);
+
+ if (packet_cmd == 'W' || packet_cmd == 'X') {
+#if defined(__APPLE__)
+ // For Native processes on Mac OS X, we launch through the Host
+ // Platform, then hand the process off to debugserver, which becomes
+ // the parent process through "PT_ATTACH". Then when we go to kill
+ // the process on Mac OS X we call ptrace(PT_KILL) to kill it, then
+ // we call waitpid which returns with no error and the correct
+ // status. But amusingly enough that doesn't seem to actually reap
+ // the process, but instead it is left around as a Zombie. Probably
+ // the kernel is in the process of switching ownership back to lldb
+ // which was the original parent, and gets confused in the handoff.
+ // Anyway, so call waitpid here to finally reap it.
+ PlatformSP platform_sp(GetTarget().GetPlatform());
+ if (platform_sp && platform_sp->IsHost()) {
+ int status;
+ ::pid_t reap_pid;
+ reap_pid = waitpid(GetID(), &status, WNOHANG);
+ if (log)
+ log->Printf("Reaped pid: %d, status: %d.\n", reap_pid, status);
+ }
+#endif
+ SetLastStopPacket(response);
+ ClearThreadIDList();
+ exit_status = response.GetHexU8();
+ } else {
+ if (log)
+ log->Printf("ProcessGDBRemote::DoDestroy - got unexpected response "
+ "to k packet: %s",
+ response.GetStringRef().c_str());
+ exit_string.assign("got unexpected response to k packet: ");
+ exit_string.append(response.GetStringRef());
+ }
+ } else {
+ if (log)
+ log->Printf("ProcessGDBRemote::DoDestroy - failed to send k packet");
+ exit_string.assign("failed to send the k packet");
+ }
+ } else {
+ if (log)
+ log->Printf("ProcessGDBRemote::DoDestroy - killed or interrupted while "
+ "attaching");
+ exit_string.assign("killed or interrupted while attaching.");
+ }
+ } else {
+ // If we missed setting the exit status on the way out, do it here.
+ // NB set exit status can be called multiple times, the first one sets the
+ // status.
+ exit_string.assign("destroying when not connected to debugserver");
+ }
+
+ SetExitStatus(exit_status, exit_string.c_str());
+
+ StopAsyncThread();
+ KillDebugserverProcess();
+ return error;
+}
+
+void ProcessGDBRemote::SetLastStopPacket(
+ const StringExtractorGDBRemote &response) {
+ const bool did_exec =
+ response.GetStringRef().find(";reason:exec;") != std::string::npos;
+ if (did_exec) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf("ProcessGDBRemote::SetLastStopPacket () - detected exec");
+
+ m_thread_list_real.Clear();
+ m_thread_list.Clear();
+ BuildDynamicRegisterInfo(true);
+ m_gdb_comm.ResetDiscoverableSettings(did_exec);
+ }
+
+ // Scope the lock
+ {
+ // Lock the thread stack while we access it
+ std::lock_guard<std::recursive_mutex> guard(m_last_stop_packet_mutex);
+
+ // We are are not using non-stop mode, there can only be one last stop
+ // reply packet, so clear the list.
+ if (!GetTarget().GetNonStopModeEnabled())
+ m_stop_packet_stack.clear();
+
+ // Add this stop packet to the stop packet stack This stack will get popped
+ // and examined when we switch to the Stopped state
+ m_stop_packet_stack.push_back(response);
+ }
+}
+
+void ProcessGDBRemote::SetUnixSignals(const UnixSignalsSP &signals_sp) {
+ Process::SetUnixSignals(std::make_shared<GDBRemoteSignals>(signals_sp));
+}
+
+// Process Queries
+
+bool ProcessGDBRemote::IsAlive() {
+ return m_gdb_comm.IsConnected() && Process::IsAlive();
+}
+
+addr_t ProcessGDBRemote::GetImageInfoAddress() {
+ // request the link map address via the $qShlibInfoAddr packet
+ lldb::addr_t addr = m_gdb_comm.GetShlibInfoAddr();
+
+ // the loaded module list can also provides a link map address
+ if (addr == LLDB_INVALID_ADDRESS) {
+ LoadedModuleInfoList list;
+ if (GetLoadedModuleList(list).Success())
+ addr = list.m_link_map;
+ }
+
+ return addr;
+}
+
+void ProcessGDBRemote::WillPublicStop() {
+ // See if the GDB remote client supports the JSON threads info. If so, we
+ // gather stop info for all threads, expedited registers, expedited memory,
+ // runtime queue information (iOS and MacOSX only), and more. Expediting
+ // memory will help stack backtracing be much faster. Expediting registers
+ // will make sure we don't have to read the thread registers for GPRs.
+ m_jthreadsinfo_sp = m_gdb_comm.GetThreadsInfo();
+
+ if (m_jthreadsinfo_sp) {
+ // Now set the stop info for each thread and also expedite any registers
+ // and memory that was in the jThreadsInfo response.
+ StructuredData::Array *thread_infos = m_jthreadsinfo_sp->GetAsArray();
+ if (thread_infos) {
+ const size_t n = thread_infos->GetSize();
+ for (size_t i = 0; i < n; ++i) {
+ StructuredData::Dictionary *thread_dict =
+ thread_infos->GetItemAtIndex(i)->GetAsDictionary();
+ if (thread_dict)
+ SetThreadStopInfo(thread_dict);
+ }
+ }
+ }
+}
+
+// Process Memory
+size_t ProcessGDBRemote::DoReadMemory(addr_t addr, void *buf, size_t size,
+ Status &error) {
+ GetMaxMemorySize();
+ bool binary_memory_read = m_gdb_comm.GetxPacketSupported();
+ // M and m packets take 2 bytes for 1 byte of memory
+ size_t max_memory_size =
+ binary_memory_read ? m_max_memory_size : m_max_memory_size / 2;
+ if (size > max_memory_size) {
+ // Keep memory read sizes down to a sane limit. This function will be
+ // called multiple times in order to complete the task by
+ // lldb_private::Process so it is ok to do this.
+ size = max_memory_size;
+ }
+
+ char packet[64];
+ int packet_len;
+ packet_len = ::snprintf(packet, sizeof(packet), "%c%" PRIx64 ",%" PRIx64,
+ binary_memory_read ? 'x' : 'm', (uint64_t)addr,
+ (uint64_t)size);
+ assert(packet_len + 1 < (int)sizeof(packet));
+ UNUSED_IF_ASSERT_DISABLED(packet_len);
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response, true) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ if (response.IsNormalResponse()) {
+ error.Clear();
+ if (binary_memory_read) {
+ // The lower level GDBRemoteCommunication packet receive layer has
+ // already de-quoted any 0x7d character escaping that was present in
+ // the packet
+
+ size_t data_received_size = response.GetBytesLeft();
+ if (data_received_size > size) {
+ // Don't write past the end of BUF if the remote debug server gave us
+ // too much data for some reason.
+ data_received_size = size;
+ }
+ memcpy(buf, response.GetStringRef().data(), data_received_size);
+ return data_received_size;
+ } else {
+ return response.GetHexBytes(
+ llvm::MutableArrayRef<uint8_t>((uint8_t *)buf, size), '\xdd');
+ }
+ } else if (response.IsErrorResponse())
+ error.SetErrorStringWithFormat("memory read failed for 0x%" PRIx64, addr);
+ else if (response.IsUnsupportedResponse())
+ error.SetErrorStringWithFormat(
+ "GDB server does not support reading memory");
+ else
+ error.SetErrorStringWithFormat(
+ "unexpected response to GDB server memory read packet '%s': '%s'",
+ packet, response.GetStringRef().c_str());
+ } else {
+ error.SetErrorStringWithFormat("failed to send packet: '%s'", packet);
+ }
+ return 0;
+}
+
+Status ProcessGDBRemote::WriteObjectFile(
+ std::vector<ObjectFile::LoadableData> entries) {
+ Status error;
+ // Sort the entries by address because some writes, like those to flash
+ // memory, must happen in order of increasing address.
+ std::stable_sort(
+ std::begin(entries), std::end(entries),
+ [](const ObjectFile::LoadableData a, const ObjectFile::LoadableData b) {
+ return a.Dest < b.Dest;
+ });
+ m_allow_flash_writes = true;
+ error = Process::WriteObjectFile(entries);
+ if (error.Success())
+ error = FlashDone();
+ else
+ // Even though some of the writing failed, try to send a flash done if some
+ // of the writing succeeded so the flash state is reset to normal, but
+ // don't stomp on the error status that was set in the write failure since
+ // that's the one we want to report back.
+ FlashDone();
+ m_allow_flash_writes = false;
+ return error;
+}
+
+bool ProcessGDBRemote::HasErased(FlashRange range) {
+ auto size = m_erased_flash_ranges.GetSize();
+ for (size_t i = 0; i < size; ++i)
+ if (m_erased_flash_ranges.GetEntryAtIndex(i)->Contains(range))
+ return true;
+ return false;
+}
+
+Status ProcessGDBRemote::FlashErase(lldb::addr_t addr, size_t size) {
+ Status status;
+
+ MemoryRegionInfo region;
+ status = GetMemoryRegionInfo(addr, region);
+ if (!status.Success())
+ return status;
+
+ // The gdb spec doesn't say if erasures are allowed across multiple regions,
+ // but we'll disallow it to be safe and to keep the logic simple by worring
+ // about only one region's block size. DoMemoryWrite is this function's
+ // primary user, and it can easily keep writes within a single memory region
+ if (addr + size > region.GetRange().GetRangeEnd()) {
+ status.SetErrorString("Unable to erase flash in multiple regions");
+ return status;
+ }
+
+ uint64_t blocksize = region.GetBlocksize();
+ if (blocksize == 0) {
+ status.SetErrorString("Unable to erase flash because blocksize is 0");
+ return status;
+ }
+
+ // Erasures can only be done on block boundary adresses, so round down addr
+ // and round up size
+ lldb::addr_t block_start_addr = addr - (addr % blocksize);
+ size += (addr - block_start_addr);
+ if ((size % blocksize) != 0)
+ size += (blocksize - size % blocksize);
+
+ FlashRange range(block_start_addr, size);
+
+ if (HasErased(range))
+ return status;
+
+ // We haven't erased the entire range, but we may have erased part of it.
+ // (e.g., block A is already erased and range starts in A and ends in B). So,
+ // adjust range if necessary to exclude already erased blocks.
+ if (!m_erased_flash_ranges.IsEmpty()) {
+ // Assuming that writes and erasures are done in increasing addr order,
+ // because that is a requirement of the vFlashWrite command. Therefore, we
+ // only need to look at the last range in the list for overlap.
+ const auto &last_range = *m_erased_flash_ranges.Back();
+ if (range.GetRangeBase() < last_range.GetRangeEnd()) {
+ auto overlap = last_range.GetRangeEnd() - range.GetRangeBase();
+ // overlap will be less than range.GetByteSize() or else HasErased()
+ // would have been true
+ range.SetByteSize(range.GetByteSize() - overlap);
+ range.SetRangeBase(range.GetRangeBase() + overlap);
+ }
+ }
+
+ StreamString packet;
+ packet.Printf("vFlashErase:%" PRIx64 ",%" PRIx64, range.GetRangeBase(),
+ (uint64_t)range.GetByteSize());
+
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response,
+ true) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ if (response.IsOKResponse()) {
+ m_erased_flash_ranges.Insert(range, true);
+ } else {
+ if (response.IsErrorResponse())
+ status.SetErrorStringWithFormat("flash erase failed for 0x%" PRIx64,
+ addr);
+ else if (response.IsUnsupportedResponse())
+ status.SetErrorStringWithFormat("GDB server does not support flashing");
+ else
+ status.SetErrorStringWithFormat(
+ "unexpected response to GDB server flash erase packet '%s': '%s'",
+ packet.GetData(), response.GetStringRef().c_str());
+ }
+ } else {
+ status.SetErrorStringWithFormat("failed to send packet: '%s'",
+ packet.GetData());
+ }
+ return status;
+}
+
+Status ProcessGDBRemote::FlashDone() {
+ Status status;
+ // If we haven't erased any blocks, then we must not have written anything
+ // either, so there is no need to actually send a vFlashDone command
+ if (m_erased_flash_ranges.IsEmpty())
+ return status;
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse("vFlashDone", response, true) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ if (response.IsOKResponse()) {
+ m_erased_flash_ranges.Clear();
+ } else {
+ if (response.IsErrorResponse())
+ status.SetErrorStringWithFormat("flash done failed");
+ else if (response.IsUnsupportedResponse())
+ status.SetErrorStringWithFormat("GDB server does not support flashing");
+ else
+ status.SetErrorStringWithFormat(
+ "unexpected response to GDB server flash done packet: '%s'",
+ response.GetStringRef().c_str());
+ }
+ } else {
+ status.SetErrorStringWithFormat("failed to send flash done packet");
+ }
+ return status;
+}
+
+size_t ProcessGDBRemote::DoWriteMemory(addr_t addr, const void *buf,
+ size_t size, Status &error) {
+ GetMaxMemorySize();
+ // M and m packets take 2 bytes for 1 byte of memory
+ size_t max_memory_size = m_max_memory_size / 2;
+ if (size > max_memory_size) {
+ // Keep memory read sizes down to a sane limit. This function will be
+ // called multiple times in order to complete the task by
+ // lldb_private::Process so it is ok to do this.
+ size = max_memory_size;
+ }
+
+ StreamGDBRemote packet;
+
+ MemoryRegionInfo region;
+ Status region_status = GetMemoryRegionInfo(addr, region);
+
+ bool is_flash =
+ region_status.Success() && region.GetFlash() == MemoryRegionInfo::eYes;
+
+ if (is_flash) {
+ if (!m_allow_flash_writes) {
+ error.SetErrorString("Writing to flash memory is not allowed");
+ return 0;
+ }
+ // Keep the write within a flash memory region
+ if (addr + size > region.GetRange().GetRangeEnd())
+ size = region.GetRange().GetRangeEnd() - addr;
+ // Flash memory must be erased before it can be written
+ error = FlashErase(addr, size);
+ if (!error.Success())
+ return 0;
+ packet.Printf("vFlashWrite:%" PRIx64 ":", addr);
+ packet.PutEscapedBytes(buf, size);
+ } else {
+ packet.Printf("M%" PRIx64 ",%" PRIx64 ":", addr, (uint64_t)size);
+ packet.PutBytesAsRawHex8(buf, size, endian::InlHostByteOrder(),
+ endian::InlHostByteOrder());
+ }
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response,
+ true) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ if (response.IsOKResponse()) {
+ error.Clear();
+ return size;
+ } else if (response.IsErrorResponse())
+ error.SetErrorStringWithFormat("memory write failed for 0x%" PRIx64,
+ addr);
+ else if (response.IsUnsupportedResponse())
+ error.SetErrorStringWithFormat(
+ "GDB server does not support writing memory");
+ else
+ error.SetErrorStringWithFormat(
+ "unexpected response to GDB server memory write packet '%s': '%s'",
+ packet.GetData(), response.GetStringRef().c_str());
+ } else {
+ error.SetErrorStringWithFormat("failed to send packet: '%s'",
+ packet.GetData());
+ }
+ return 0;
+}
+
+lldb::addr_t ProcessGDBRemote::DoAllocateMemory(size_t size,
+ uint32_t permissions,
+ Status &error) {
+ Log *log(
+ GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_EXPRESSIONS));
+ addr_t allocated_addr = LLDB_INVALID_ADDRESS;
+
+ if (m_gdb_comm.SupportsAllocDeallocMemory() != eLazyBoolNo) {
+ allocated_addr = m_gdb_comm.AllocateMemory(size, permissions);
+ if (allocated_addr != LLDB_INVALID_ADDRESS ||
+ m_gdb_comm.SupportsAllocDeallocMemory() == eLazyBoolYes)
+ return allocated_addr;
+ }
+
+ if (m_gdb_comm.SupportsAllocDeallocMemory() == eLazyBoolNo) {
+ // Call mmap() to create memory in the inferior..
+ unsigned prot = 0;
+ if (permissions & lldb::ePermissionsReadable)
+ prot |= eMmapProtRead;
+ if (permissions & lldb::ePermissionsWritable)
+ prot |= eMmapProtWrite;
+ if (permissions & lldb::ePermissionsExecutable)
+ prot |= eMmapProtExec;
+
+ if (InferiorCallMmap(this, allocated_addr, 0, size, prot,
+ eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0))
+ m_addr_to_mmap_size[allocated_addr] = size;
+ else {
+ allocated_addr = LLDB_INVALID_ADDRESS;
+ if (log)
+ log->Printf("ProcessGDBRemote::%s no direct stub support for memory "
+ "allocation, and InferiorCallMmap also failed - is stub "
+ "missing register context save/restore capability?",
+ __FUNCTION__);
+ }
+ }
+
+ if (allocated_addr == LLDB_INVALID_ADDRESS)
+ error.SetErrorStringWithFormat(
+ "unable to allocate %" PRIu64 " bytes of memory with permissions %s",
+ (uint64_t)size, GetPermissionsAsCString(permissions));
+ else
+ error.Clear();
+ return allocated_addr;
+}
+
+Status ProcessGDBRemote::GetMemoryRegionInfo(addr_t load_addr,
+ MemoryRegionInfo &region_info) {
+
+ Status error(m_gdb_comm.GetMemoryRegionInfo(load_addr, region_info));
+ return error;
+}
+
+Status ProcessGDBRemote::GetWatchpointSupportInfo(uint32_t &num) {
+
+ Status error(m_gdb_comm.GetWatchpointSupportInfo(num));
+ return error;
+}
+
+Status ProcessGDBRemote::GetWatchpointSupportInfo(uint32_t &num, bool &after) {
+ Status error(m_gdb_comm.GetWatchpointSupportInfo(
+ num, after, GetTarget().GetArchitecture()));
+ return error;
+}
+
+Status ProcessGDBRemote::DoDeallocateMemory(lldb::addr_t addr) {
+ Status error;
+ LazyBool supported = m_gdb_comm.SupportsAllocDeallocMemory();
+
+ switch (supported) {
+ case eLazyBoolCalculate:
+ // We should never be deallocating memory without allocating memory first
+ // so we should never get eLazyBoolCalculate
+ error.SetErrorString(
+ "tried to deallocate memory without ever allocating memory");
+ break;
+
+ case eLazyBoolYes:
+ if (!m_gdb_comm.DeallocateMemory(addr))
+ error.SetErrorStringWithFormat(
+ "unable to deallocate memory at 0x%" PRIx64, addr);
+ break;
+
+ case eLazyBoolNo:
+ // Call munmap() to deallocate memory in the inferior..
+ {
+ MMapMap::iterator pos = m_addr_to_mmap_size.find(addr);
+ if (pos != m_addr_to_mmap_size.end() &&
+ InferiorCallMunmap(this, addr, pos->second))
+ m_addr_to_mmap_size.erase(pos);
+ else
+ error.SetErrorStringWithFormat(
+ "unable to deallocate memory at 0x%" PRIx64, addr);
+ }
+ break;
+ }
+
+ return error;
+}
+
+// Process STDIO
+size_t ProcessGDBRemote::PutSTDIN(const char *src, size_t src_len,
+ Status &error) {
+ if (m_stdio_communication.IsConnected()) {
+ ConnectionStatus status;
+ m_stdio_communication.Write(src, src_len, status, nullptr);
+ } else if (m_stdin_forward) {
+ m_gdb_comm.SendStdinNotification(src, src_len);
+ }
+ return 0;
+}
+
+Status ProcessGDBRemote::EnableBreakpointSite(BreakpointSite *bp_site) {
+ Status error;
+ assert(bp_site != nullptr);
+
+ // Get logging info
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS));
+ user_id_t site_id = bp_site->GetID();
+
+ // Get the breakpoint address
+ const addr_t addr = bp_site->GetLoadAddress();
+
+ // Log that a breakpoint was requested
+ if (log)
+ log->Printf("ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64
+ ") address = 0x%" PRIx64,
+ site_id, (uint64_t)addr);
+
+ // Breakpoint already exists and is enabled
+ if (bp_site->IsEnabled()) {
+ if (log)
+ log->Printf("ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64
+ ") address = 0x%" PRIx64 " -- SUCCESS (already enabled)",
+ site_id, (uint64_t)addr);
+ return error;
+ }
+
+ // Get the software breakpoint trap opcode size
+ const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode(bp_site);
+
+ // SupportsGDBStoppointPacket() simply checks a boolean, indicating if this
+ // breakpoint type is supported by the remote stub. These are set to true by
+ // default, and later set to false only after we receive an unimplemented
+ // response when sending a breakpoint packet. This means initially that
+ // unless we were specifically instructed to use a hardware breakpoint, LLDB
+ // will attempt to set a software breakpoint. HardwareRequired() also queries
+ // a boolean variable which indicates if the user specifically asked for
+ // hardware breakpoints. If true then we will skip over software
+ // breakpoints.
+ if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware) &&
+ (!bp_site->HardwareRequired())) {
+ // Try to send off a software breakpoint packet ($Z0)
+ uint8_t error_no = m_gdb_comm.SendGDBStoppointTypePacket(
+ eBreakpointSoftware, true, addr, bp_op_size);
+ if (error_no == 0) {
+ // The breakpoint was placed successfully
+ bp_site->SetEnabled(true);
+ bp_site->SetType(BreakpointSite::eExternal);
+ return error;
+ }
+
+ // SendGDBStoppointTypePacket() will return an error if it was unable to
+ // set this breakpoint. We need to differentiate between a error specific
+ // to placing this breakpoint or if we have learned that this breakpoint
+ // type is unsupported. To do this, we must test the support boolean for
+ // this breakpoint type to see if it now indicates that this breakpoint
+ // type is unsupported. If they are still supported then we should return
+ // with the error code. If they are now unsupported, then we would like to
+ // fall through and try another form of breakpoint.
+ if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware)) {
+ if (error_no != UINT8_MAX)
+ error.SetErrorStringWithFormat(
+ "error: %d sending the breakpoint request", errno);
+ else
+ error.SetErrorString("error sending the breakpoint request");
+ return error;
+ }
+
+ // We reach here when software breakpoints have been found to be
+ // unsupported. For future calls to set a breakpoint, we will not attempt
+ // to set a breakpoint with a type that is known not to be supported.
+ if (log)
+ log->Printf("Software breakpoints are unsupported");
+
+ // So we will fall through and try a hardware breakpoint
+ }
+
+ // The process of setting a hardware breakpoint is much the same as above.
+ // We check the supported boolean for this breakpoint type, and if it is
+ // thought to be supported then we will try to set this breakpoint with a
+ // hardware breakpoint.
+ if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) {
+ // Try to send off a hardware breakpoint packet ($Z1)
+ uint8_t error_no = m_gdb_comm.SendGDBStoppointTypePacket(
+ eBreakpointHardware, true, addr, bp_op_size);
+ if (error_no == 0) {
+ // The breakpoint was placed successfully
+ bp_site->SetEnabled(true);
+ bp_site->SetType(BreakpointSite::eHardware);
+ return error;
+ }
+
+ // Check if the error was something other then an unsupported breakpoint
+ // type
+ if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) {
+ // Unable to set this hardware breakpoint
+ if (error_no != UINT8_MAX)
+ error.SetErrorStringWithFormat(
+ "error: %d sending the hardware breakpoint request "
+ "(hardware breakpoint resources might be exhausted or unavailable)",
+ error_no);
+ else
+ error.SetErrorString("error sending the hardware breakpoint request "
+ "(hardware breakpoint resources "
+ "might be exhausted or unavailable)");
+ return error;
+ }
+
+ // We will reach here when the stub gives an unsupported response to a
+ // hardware breakpoint
+ if (log)
+ log->Printf("Hardware breakpoints are unsupported");
+
+ // Finally we will falling through to a #trap style breakpoint
+ }
+
+ // Don't fall through when hardware breakpoints were specifically requested
+ if (bp_site->HardwareRequired()) {
+ error.SetErrorString("hardware breakpoints are not supported");
+ return error;
+ }
+
+ // As a last resort we want to place a manual breakpoint. An instruction is
+ // placed into the process memory using memory write packets.
+ return EnableSoftwareBreakpoint(bp_site);
+}
+
+Status ProcessGDBRemote::DisableBreakpointSite(BreakpointSite *bp_site) {
+ Status error;
+ assert(bp_site != nullptr);
+ addr_t addr = bp_site->GetLoadAddress();
+ user_id_t site_id = bp_site->GetID();
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf("ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64
+ ") addr = 0x%8.8" PRIx64,
+ site_id, (uint64_t)addr);
+
+ if (bp_site->IsEnabled()) {
+ const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode(bp_site);
+
+ BreakpointSite::Type bp_type = bp_site->GetType();
+ switch (bp_type) {
+ case BreakpointSite::eSoftware:
+ error = DisableSoftwareBreakpoint(bp_site);
+ break;
+
+ case BreakpointSite::eHardware:
+ if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointHardware, false,
+ addr, bp_op_size))
+ error.SetErrorToGenericError();
+ break;
+
+ case BreakpointSite::eExternal: {
+ GDBStoppointType stoppoint_type;
+ if (bp_site->IsHardware())
+ stoppoint_type = eBreakpointHardware;
+ else
+ stoppoint_type = eBreakpointSoftware;
+
+ if (m_gdb_comm.SendGDBStoppointTypePacket(stoppoint_type, false, addr,
+ bp_op_size))
+ error.SetErrorToGenericError();
+ } break;
+ }
+ if (error.Success())
+ bp_site->SetEnabled(false);
+ } else {
+ if (log)
+ log->Printf("ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64
+ ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)",
+ site_id, (uint64_t)addr);
+ return error;
+ }
+
+ if (error.Success())
+ error.SetErrorToGenericError();
+ return error;
+}
+
+// Pre-requisite: wp != NULL.
+static GDBStoppointType GetGDBStoppointType(Watchpoint *wp) {
+ assert(wp);
+ bool watch_read = wp->WatchpointRead();
+ bool watch_write = wp->WatchpointWrite();
+
+ // watch_read and watch_write cannot both be false.
+ assert(watch_read || watch_write);
+ if (watch_read && watch_write)
+ return eWatchpointReadWrite;
+ else if (watch_read)
+ return eWatchpointRead;
+ else // Must be watch_write, then.
+ return eWatchpointWrite;
+}
+
+Status ProcessGDBRemote::EnableWatchpoint(Watchpoint *wp, bool notify) {
+ Status error;
+ if (wp) {
+ user_id_t watchID = wp->GetID();
+ addr_t addr = wp->GetLoadAddress();
+ Log *log(
+ ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf("ProcessGDBRemote::EnableWatchpoint(watchID = %" PRIu64 ")",
+ watchID);
+ if (wp->IsEnabled()) {
+ if (log)
+ log->Printf("ProcessGDBRemote::EnableWatchpoint(watchID = %" PRIu64
+ ") addr = 0x%8.8" PRIx64 ": watchpoint already enabled.",
+ watchID, (uint64_t)addr);
+ return error;
+ }
+
+ GDBStoppointType type = GetGDBStoppointType(wp);
+ // Pass down an appropriate z/Z packet...
+ if (m_gdb_comm.SupportsGDBStoppointPacket(type)) {
+ if (m_gdb_comm.SendGDBStoppointTypePacket(type, true, addr,
+ wp->GetByteSize()) == 0) {
+ wp->SetEnabled(true, notify);
+ return error;
+ } else
+ error.SetErrorString("sending gdb watchpoint packet failed");
+ } else
+ error.SetErrorString("watchpoints not supported");
+ } else {
+ error.SetErrorString("Watchpoint argument was NULL.");
+ }
+ if (error.Success())
+ error.SetErrorToGenericError();
+ return error;
+}
+
+Status ProcessGDBRemote::DisableWatchpoint(Watchpoint *wp, bool notify) {
+ Status error;
+ if (wp) {
+ user_id_t watchID = wp->GetID();
+
+ Log *log(
+ ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS));
+
+ addr_t addr = wp->GetLoadAddress();
+
+ if (log)
+ log->Printf("ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64
+ ") addr = 0x%8.8" PRIx64,
+ watchID, (uint64_t)addr);
+
+ if (!wp->IsEnabled()) {
+ if (log)
+ log->Printf("ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64
+ ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)",
+ watchID, (uint64_t)addr);
+ // See also 'class WatchpointSentry' within StopInfo.cpp. This disabling
+ // attempt might come from the user-supplied actions, we'll route it in
+ // order for the watchpoint object to intelligently process this action.
+ wp->SetEnabled(false, notify);
+ return error;
+ }
+
+ if (wp->IsHardware()) {
+ GDBStoppointType type = GetGDBStoppointType(wp);
+ // Pass down an appropriate z/Z packet...
+ if (m_gdb_comm.SendGDBStoppointTypePacket(type, false, addr,
+ wp->GetByteSize()) == 0) {
+ wp->SetEnabled(false, notify);
+ return error;
+ } else
+ error.SetErrorString("sending gdb watchpoint packet failed");
+ }
+ // TODO: clear software watchpoints if we implement them
+ } else {
+ error.SetErrorString("Watchpoint argument was NULL.");
+ }
+ if (error.Success())
+ error.SetErrorToGenericError();
+ return error;
+}
+
+void ProcessGDBRemote::Clear() {
+ m_thread_list_real.Clear();
+ m_thread_list.Clear();
+}
+
+Status ProcessGDBRemote::DoSignal(int signo) {
+ Status error;
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf("ProcessGDBRemote::DoSignal (signal = %d)", signo);
+
+ if (!m_gdb_comm.SendAsyncSignal(signo))
+ error.SetErrorStringWithFormat("failed to send signal %i", signo);
+ return error;
+}
+
+Status ProcessGDBRemote::ConnectToReplayServer(repro::Loader *loader) {
+ if (!loader)
+ return Status("No loader provided.");
+
+ // Construct replay history path.
+ FileSpec history_file = loader->GetFile<ProcessGDBRemoteProvider::Info>();
+ if (!history_file)
+ return Status("No provider for gdb-remote.");
+
+ // Enable replay mode.
+ m_replay_mode = true;
+
+ // Load replay history.
+ if (auto error = m_gdb_replay_server.LoadReplayHistory(history_file))
+ return Status("Unable to load replay history");
+
+ // Make a local connection.
+ if (auto error = GDBRemoteCommunication::ConnectLocally(m_gdb_comm,
+ m_gdb_replay_server))
+ return Status("Unable to connect to replay server");
+
+ // Start server thread.
+ m_gdb_replay_server.StartAsyncThread();
+
+ // Start client thread.
+ StartAsyncThread();
+
+ // Do the usual setup.
+ return ConnectToDebugserver("");
+}
+
+Status
+ProcessGDBRemote::EstablishConnectionIfNeeded(const ProcessInfo &process_info) {
+ // Make sure we aren't already connected?
+ if (m_gdb_comm.IsConnected())
+ return Status();
+
+ PlatformSP platform_sp(GetTarget().GetPlatform());
+ if (platform_sp && !platform_sp->IsHost())
+ return Status("Lost debug server connection");
+
+ if (repro::Loader *loader = repro::Reproducer::Instance().GetLoader())
+ return ConnectToReplayServer(loader);
+
+ auto error = LaunchAndConnectToDebugserver(process_info);
+ if (error.Fail()) {
+ const char *error_string = error.AsCString();
+ if (error_string == nullptr)
+ error_string = "unable to launch " DEBUGSERVER_BASENAME;
+ }
+ return error;
+}
+#if !defined(_WIN32)
+#define USE_SOCKETPAIR_FOR_LOCAL_CONNECTION 1
+#endif
+
+#ifdef USE_SOCKETPAIR_FOR_LOCAL_CONNECTION
+static bool SetCloexecFlag(int fd) {
+#if defined(FD_CLOEXEC)
+ int flags = ::fcntl(fd, F_GETFD);
+ if (flags == -1)
+ return false;
+ return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0);
+#else
+ return false;
+#endif
+}
+#endif
+
+Status ProcessGDBRemote::LaunchAndConnectToDebugserver(
+ const ProcessInfo &process_info) {
+ using namespace std::placeholders; // For _1, _2, etc.
+
+ Status error;
+ if (m_debugserver_pid == LLDB_INVALID_PROCESS_ID) {
+ // If we locate debugserver, keep that located version around
+ static FileSpec g_debugserver_file_spec;
+
+ ProcessLaunchInfo debugserver_launch_info;
+ // Make debugserver run in its own session so signals generated by special
+ // terminal key sequences (^C) don't affect debugserver.
+ debugserver_launch_info.SetLaunchInSeparateProcessGroup(true);
+
+ const std::weak_ptr<ProcessGDBRemote> this_wp =
+ std::static_pointer_cast<ProcessGDBRemote>(shared_from_this());
+ debugserver_launch_info.SetMonitorProcessCallback(
+ std::bind(MonitorDebugserverProcess, this_wp, _1, _2, _3, _4), false);
+ debugserver_launch_info.SetUserID(process_info.GetUserID());
+
+ int communication_fd = -1;
+#ifdef USE_SOCKETPAIR_FOR_LOCAL_CONNECTION
+ // Use a socketpair on non-Windows systems for security and performance
+ // reasons.
+ int sockets[2]; /* the pair of socket descriptors */
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1) {
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ int our_socket = sockets[0];
+ int gdb_socket = sockets[1];
+ CleanUp cleanup_our(close, our_socket);
+ CleanUp cleanup_gdb(close, gdb_socket);
+
+ // Don't let any child processes inherit our communication socket
+ SetCloexecFlag(our_socket);
+ communication_fd = gdb_socket;
+#endif
+
+ error = m_gdb_comm.StartDebugserverProcess(
+ nullptr, GetTarget().GetPlatform().get(), debugserver_launch_info,
+ nullptr, nullptr, communication_fd);
+
+ if (error.Success())
+ m_debugserver_pid = debugserver_launch_info.GetProcessID();
+ else
+ m_debugserver_pid = LLDB_INVALID_PROCESS_ID;
+
+ if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) {
+#ifdef USE_SOCKETPAIR_FOR_LOCAL_CONNECTION
+ // Our process spawned correctly, we can now set our connection to use
+ // our end of the socket pair
+ cleanup_our.disable();
+ m_gdb_comm.SetConnection(new ConnectionFileDescriptor(our_socket, true));
+#endif
+ StartAsyncThread();
+ }
+
+ if (error.Fail()) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+
+ if (log)
+ log->Printf("failed to start debugserver process: %s",
+ error.AsCString());
+ return error;
+ }
+
+ if (m_gdb_comm.IsConnected()) {
+ // Finish the connection process by doing the handshake without
+ // connecting (send NULL URL)
+ error = ConnectToDebugserver("");
+ } else {
+ error.SetErrorString("connection failed");
+ }
+ }
+ return error;
+}
+
+bool ProcessGDBRemote::MonitorDebugserverProcess(
+ std::weak_ptr<ProcessGDBRemote> process_wp, lldb::pid_t debugserver_pid,
+ bool exited, // True if the process did exit
+ int signo, // Zero for no signal
+ int exit_status // Exit value of process if signal is zero
+) {
+ // "debugserver_pid" argument passed in is the process ID for debugserver
+ // that we are tracking...
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ const bool handled = true;
+
+ if (log)
+ log->Printf("ProcessGDBRemote::%s(process_wp, pid=%" PRIu64
+ ", signo=%i (0x%x), exit_status=%i)",
+ __FUNCTION__, debugserver_pid, signo, signo, exit_status);
+
+ std::shared_ptr<ProcessGDBRemote> process_sp = process_wp.lock();
+ if (log)
+ log->Printf("ProcessGDBRemote::%s(process = %p)", __FUNCTION__,
+ static_cast<void *>(process_sp.get()));
+ if (!process_sp || process_sp->m_debugserver_pid != debugserver_pid)
+ return handled;
+
+ // Sleep for a half a second to make sure our inferior process has time to
+ // set its exit status before we set it incorrectly when both the debugserver
+ // and the inferior process shut down.
+ usleep(500000);
+ // If our process hasn't yet exited, debugserver might have died. If the
+ // process did exit, then we are reaping it.
+ const StateType state = process_sp->GetState();
+
+ if (state != eStateInvalid && state != eStateUnloaded &&
+ state != eStateExited && state != eStateDetached) {
+ char error_str[1024];
+ if (signo) {
+ const char *signal_cstr =
+ process_sp->GetUnixSignals()->GetSignalAsCString(signo);
+ if (signal_cstr)
+ ::snprintf(error_str, sizeof(error_str),
+ DEBUGSERVER_BASENAME " died with signal %s", signal_cstr);
+ else
+ ::snprintf(error_str, sizeof(error_str),
+ DEBUGSERVER_BASENAME " died with signal %i", signo);
+ } else {
+ ::snprintf(error_str, sizeof(error_str),
+ DEBUGSERVER_BASENAME " died with an exit status of 0x%8.8x",
+ exit_status);
+ }
+
+ process_sp->SetExitStatus(-1, error_str);
+ }
+ // Debugserver has exited we need to let our ProcessGDBRemote know that it no
+ // longer has a debugserver instance
+ process_sp->m_debugserver_pid = LLDB_INVALID_PROCESS_ID;
+ return handled;
+}
+
+void ProcessGDBRemote::KillDebugserverProcess() {
+ m_gdb_comm.Disconnect();
+ if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) {
+ Host::Kill(m_debugserver_pid, SIGINT);
+ m_debugserver_pid = LLDB_INVALID_PROCESS_ID;
+ }
+}
+
+void ProcessGDBRemote::Initialize() {
+ static llvm::once_flag g_once_flag;
+
+ llvm::call_once(g_once_flag, []() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ DebuggerInitialize);
+ });
+}
+
+void ProcessGDBRemote::DebuggerInitialize(Debugger &debugger) {
+ if (!PluginManager::GetSettingForProcessPlugin(
+ debugger, PluginProperties::GetSettingName())) {
+ const bool is_global_setting = true;
+ PluginManager::CreateSettingForProcessPlugin(
+ debugger, GetGlobalPluginProperties()->GetValueProperties(),
+ ConstString("Properties for the gdb-remote process plug-in."),
+ is_global_setting);
+ }
+}
+
+bool ProcessGDBRemote::StartAsyncThread() {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+
+ if (log)
+ log->Printf("ProcessGDBRemote::%s ()", __FUNCTION__);
+
+ std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex);
+ if (!m_async_thread.IsJoinable()) {
+ // Create a thread that watches our internal state and controls which
+ // events make it to clients (into the DCProcess event queue).
+
+ llvm::Expected<HostThread> async_thread = ThreadLauncher::LaunchThread(
+ "<lldb.process.gdb-remote.async>", ProcessGDBRemote::AsyncThread, this);
+ if (!async_thread) {
+ LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST),
+ "failed to launch host thread: {}",
+ llvm::toString(async_thread.takeError()));
+ return false;
+ }
+ m_async_thread = *async_thread;
+ } else if (log)
+ log->Printf("ProcessGDBRemote::%s () - Called when Async thread was "
+ "already running.",
+ __FUNCTION__);
+
+ return m_async_thread.IsJoinable();
+}
+
+void ProcessGDBRemote::StopAsyncThread() {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+
+ if (log)
+ log->Printf("ProcessGDBRemote::%s ()", __FUNCTION__);
+
+ std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex);
+ if (m_async_thread.IsJoinable()) {
+ m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncThreadShouldExit);
+
+ // This will shut down the async thread.
+ m_gdb_comm.Disconnect(); // Disconnect from the debug server.
+
+ // Stop the stdio thread
+ m_async_thread.Join(nullptr);
+ m_async_thread.Reset();
+ } else if (log)
+ log->Printf(
+ "ProcessGDBRemote::%s () - Called when Async thread was not running.",
+ __FUNCTION__);
+}
+
+bool ProcessGDBRemote::HandleNotifyPacket(StringExtractorGDBRemote &packet) {
+ // get the packet at a string
+ const std::string &pkt = packet.GetStringRef();
+ // skip %stop:
+ StringExtractorGDBRemote stop_info(pkt.c_str() + 5);
+
+ // pass as a thread stop info packet
+ SetLastStopPacket(stop_info);
+
+ // check for more stop reasons
+ HandleStopReplySequence();
+
+ // if the process is stopped then we need to fake a resume so that we can
+ // stop properly with the new break. This is possible due to
+ // SetPrivateState() broadcasting the state change as a side effect.
+ if (GetPrivateState() == lldb::StateType::eStateStopped) {
+ SetPrivateState(lldb::StateType::eStateRunning);
+ }
+
+ // since we have some stopped packets we can halt the process
+ SetPrivateState(lldb::StateType::eStateStopped);
+
+ return true;
+}
+
+thread_result_t ProcessGDBRemote::AsyncThread(void *arg) {
+ ProcessGDBRemote *process = (ProcessGDBRemote *)arg;
+
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ if (log)
+ log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64
+ ") thread starting...",
+ __FUNCTION__, arg, process->GetID());
+
+ EventSP event_sp;
+ bool done = false;
+ while (!done) {
+ if (log)
+ log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64
+ ") listener.WaitForEvent (NULL, event_sp)...",
+ __FUNCTION__, arg, process->GetID());
+ if (process->m_async_listener_sp->GetEvent(event_sp, llvm::None)) {
+ const uint32_t event_type = event_sp->GetType();
+ if (event_sp->BroadcasterIs(&process->m_async_broadcaster)) {
+ if (log)
+ log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64
+ ") Got an event of type: %d...",
+ __FUNCTION__, arg, process->GetID(), event_type);
+
+ switch (event_type) {
+ case eBroadcastBitAsyncContinue: {
+ const EventDataBytes *continue_packet =
+ EventDataBytes::GetEventDataFromEvent(event_sp.get());
+
+ if (continue_packet) {
+ const char *continue_cstr =
+ (const char *)continue_packet->GetBytes();
+ const size_t continue_cstr_len = continue_packet->GetByteSize();
+ if (log)
+ log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64
+ ") got eBroadcastBitAsyncContinue: %s",
+ __FUNCTION__, arg, process->GetID(), continue_cstr);
+
+ if (::strstr(continue_cstr, "vAttach") == nullptr)
+ process->SetPrivateState(eStateRunning);
+ StringExtractorGDBRemote response;
+
+ // If in Non-Stop-Mode
+ if (process->GetTarget().GetNonStopModeEnabled()) {
+ // send the vCont packet
+ if (!process->GetGDBRemote().SendvContPacket(
+ llvm::StringRef(continue_cstr, continue_cstr_len),
+ response)) {
+ // Something went wrong
+ done = true;
+ break;
+ }
+ }
+ // If in All-Stop-Mode
+ else {
+ StateType stop_state =
+ process->GetGDBRemote().SendContinuePacketAndWaitForResponse(
+ *process, *process->GetUnixSignals(),
+ llvm::StringRef(continue_cstr, continue_cstr_len),
+ response);
+
+ // We need to immediately clear the thread ID list so we are sure
+ // to get a valid list of threads. The thread ID list might be
+ // contained within the "response", or the stop reply packet that
+ // caused the stop. So clear it now before we give the stop reply
+ // packet to the process using the
+ // process->SetLastStopPacket()...
+ process->ClearThreadIDList();
+
+ switch (stop_state) {
+ case eStateStopped:
+ case eStateCrashed:
+ case eStateSuspended:
+ process->SetLastStopPacket(response);
+ process->SetPrivateState(stop_state);
+ break;
+
+ case eStateExited: {
+ process->SetLastStopPacket(response);
+ process->ClearThreadIDList();
+ response.SetFilePos(1);
+
+ int exit_status = response.GetHexU8();
+ std::string desc_string;
+ if (response.GetBytesLeft() > 0 &&
+ response.GetChar('-') == ';') {
+ llvm::StringRef desc_str;
+ llvm::StringRef desc_token;
+ while (response.GetNameColonValue(desc_token, desc_str)) {
+ if (desc_token != "description")
+ continue;
+ StringExtractor extractor(desc_str);
+ extractor.GetHexByteString(desc_string);
+ }
+ }
+ process->SetExitStatus(exit_status, desc_string.c_str());
+ done = true;
+ break;
+ }
+ case eStateInvalid: {
+ // Check to see if we were trying to attach and if we got back
+ // the "E87" error code from debugserver -- this indicates that
+ // the process is not debuggable. Return a slightly more
+ // helpful error message about why the attach failed.
+ if (::strstr(continue_cstr, "vAttach") != nullptr &&
+ response.GetError() == 0x87) {
+ process->SetExitStatus(-1, "cannot attach to process due to "
+ "System Integrity Protection");
+ } else if (::strstr(continue_cstr, "vAttach") != nullptr &&
+ response.GetStatus().Fail()) {
+ process->SetExitStatus(-1, response.GetStatus().AsCString());
+ } else {
+ process->SetExitStatus(-1, "lost connection");
+ }
+ break;
+ }
+
+ default:
+ process->SetPrivateState(stop_state);
+ break;
+ } // switch(stop_state)
+ } // else // if in All-stop-mode
+ } // if (continue_packet)
+ } // case eBroadcastBitAysncContinue
+ break;
+
+ case eBroadcastBitAsyncThreadShouldExit:
+ if (log)
+ log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64
+ ") got eBroadcastBitAsyncThreadShouldExit...",
+ __FUNCTION__, arg, process->GetID());
+ done = true;
+ break;
+
+ default:
+ if (log)
+ log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64
+ ") got unknown event 0x%8.8x",
+ __FUNCTION__, arg, process->GetID(), event_type);
+ done = true;
+ break;
+ }
+ } else if (event_sp->BroadcasterIs(&process->m_gdb_comm)) {
+ switch (event_type) {
+ case Communication::eBroadcastBitReadThreadDidExit:
+ process->SetExitStatus(-1, "lost connection");
+ done = true;
+ break;
+
+ case GDBRemoteCommunication::eBroadcastBitGdbReadThreadGotNotify: {
+ lldb_private::Event *event = event_sp.get();
+ const EventDataBytes *continue_packet =
+ EventDataBytes::GetEventDataFromEvent(event);
+ StringExtractorGDBRemote notify(
+ (const char *)continue_packet->GetBytes());
+ // Hand this over to the process to handle
+ process->HandleNotifyPacket(notify);
+ break;
+ }
+
+ default:
+ if (log)
+ log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64
+ ") got unknown event 0x%8.8x",
+ __FUNCTION__, arg, process->GetID(), event_type);
+ done = true;
+ break;
+ }
+ }
+ } else {
+ if (log)
+ log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64
+ ") listener.WaitForEvent (NULL, event_sp) => false",
+ __FUNCTION__, arg, process->GetID());
+ done = true;
+ }
+ }
+
+ if (log)
+ log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64
+ ") thread exiting...",
+ __FUNCTION__, arg, process->GetID());
+
+ return {};
+}
+
+// uint32_t
+// ProcessGDBRemote::ListProcessesMatchingName (const char *name, StringList
+// &matches, std::vector<lldb::pid_t> &pids)
+//{
+// // If we are planning to launch the debugserver remotely, then we need to
+// fire up a debugserver
+// // process and ask it for the list of processes. But if we are local, we
+// can let the Host do it.
+// if (m_local_debugserver)
+// {
+// return Host::ListProcessesMatchingName (name, matches, pids);
+// }
+// else
+// {
+// // FIXME: Implement talking to the remote debugserver.
+// return 0;
+// }
+//
+//}
+//
+bool ProcessGDBRemote::NewThreadNotifyBreakpointHit(
+ void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id) {
+ // I don't think I have to do anything here, just make sure I notice the new
+ // thread when it starts to
+ // run so I can stop it if that's what I want to do.
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf("Hit New Thread Notification breakpoint.");
+ return false;
+}
+
+Status ProcessGDBRemote::UpdateAutomaticSignalFiltering() {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+ LLDB_LOG(log, "Check if need to update ignored signals");
+
+ // QPassSignals package is not supported by the server, there is no way we
+ // can ignore any signals on server side.
+ if (!m_gdb_comm.GetQPassSignalsSupported())
+ return Status();
+
+ // No signals, nothing to send.
+ if (m_unix_signals_sp == nullptr)
+ return Status();
+
+ // Signals' version hasn't changed, no need to send anything.
+ uint64_t new_signals_version = m_unix_signals_sp->GetVersion();
+ if (new_signals_version == m_last_signals_version) {
+ LLDB_LOG(log, "Signals' version hasn't changed. version={0}",
+ m_last_signals_version);
+ return Status();
+ }
+
+ auto signals_to_ignore =
+ m_unix_signals_sp->GetFilteredSignals(false, false, false);
+ Status error = m_gdb_comm.SendSignalsToIgnore(signals_to_ignore);
+
+ LLDB_LOG(log,
+ "Signals' version changed. old version={0}, new version={1}, "
+ "signals ignored={2}, update result={3}",
+ m_last_signals_version, new_signals_version,
+ signals_to_ignore.size(), error);
+
+ if (error.Success())
+ m_last_signals_version = new_signals_version;
+
+ return error;
+}
+
+bool ProcessGDBRemote::StartNoticingNewThreads() {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (m_thread_create_bp_sp) {
+ if (log && log->GetVerbose())
+ log->Printf("Enabled noticing new thread breakpoint.");
+ m_thread_create_bp_sp->SetEnabled(true);
+ } else {
+ PlatformSP platform_sp(GetTarget().GetPlatform());
+ if (platform_sp) {
+ m_thread_create_bp_sp =
+ platform_sp->SetThreadCreationBreakpoint(GetTarget());
+ if (m_thread_create_bp_sp) {
+ if (log && log->GetVerbose())
+ log->Printf(
+ "Successfully created new thread notification breakpoint %i",
+ m_thread_create_bp_sp->GetID());
+ m_thread_create_bp_sp->SetCallback(
+ ProcessGDBRemote::NewThreadNotifyBreakpointHit, this, true);
+ } else {
+ if (log)
+ log->Printf("Failed to create new thread notification breakpoint.");
+ }
+ }
+ }
+ return m_thread_create_bp_sp.get() != nullptr;
+}
+
+bool ProcessGDBRemote::StopNoticingNewThreads() {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ if (log && log->GetVerbose())
+ log->Printf("Disabling new thread notification breakpoint.");
+
+ if (m_thread_create_bp_sp)
+ m_thread_create_bp_sp->SetEnabled(false);
+
+ return true;
+}
+
+DynamicLoader *ProcessGDBRemote::GetDynamicLoader() {
+ if (m_dyld_up.get() == nullptr)
+ m_dyld_up.reset(DynamicLoader::FindPlugin(this, nullptr));
+ return m_dyld_up.get();
+}
+
+Status ProcessGDBRemote::SendEventData(const char *data) {
+ int return_value;
+ bool was_supported;
+
+ Status error;
+
+ return_value = m_gdb_comm.SendLaunchEventDataPacket(data, &was_supported);
+ if (return_value != 0) {
+ if (!was_supported)
+ error.SetErrorString("Sending events is not supported for this process.");
+ else
+ error.SetErrorStringWithFormat("Error sending event data: %d.",
+ return_value);
+ }
+ return error;
+}
+
+DataExtractor ProcessGDBRemote::GetAuxvData() {
+ DataBufferSP buf;
+ if (m_gdb_comm.GetQXferAuxvReadSupported()) {
+ std::string response_string;
+ if (m_gdb_comm.SendPacketsAndConcatenateResponses("qXfer:auxv:read::",
+ response_string) ==
+ GDBRemoteCommunication::PacketResult::Success)
+ buf = std::make_shared<DataBufferHeap>(response_string.c_str(),
+ response_string.length());
+ }
+ return DataExtractor(buf, GetByteOrder(), GetAddressByteSize());
+}
+
+StructuredData::ObjectSP
+ProcessGDBRemote::GetExtendedInfoForThread(lldb::tid_t tid) {
+ StructuredData::ObjectSP object_sp;
+
+ if (m_gdb_comm.GetThreadExtendedInfoSupported()) {
+ StructuredData::ObjectSP args_dict(new StructuredData::Dictionary());
+ SystemRuntime *runtime = GetSystemRuntime();
+ if (runtime) {
+ runtime->AddThreadExtendedInfoPacketHints(args_dict);
+ }
+ args_dict->GetAsDictionary()->AddIntegerItem("thread", tid);
+
+ StreamString packet;
+ packet << "jThreadExtendedInfo:";
+ args_dict->Dump(packet, false);
+
+ // FIXME the final character of a JSON dictionary, '}', is the escape
+ // character in gdb-remote binary mode. lldb currently doesn't escape
+ // these characters in its packet output -- so we add the quoted version of
+ // the } character here manually in case we talk to a debugserver which un-
+ // escapes the characters at packet read time.
+ packet << (char)(0x7d ^ 0x20);
+
+ StringExtractorGDBRemote response;
+ response.SetResponseValidatorToJSON();
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response,
+ false) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ StringExtractorGDBRemote::ResponseType response_type =
+ response.GetResponseType();
+ if (response_type == StringExtractorGDBRemote::eResponse) {
+ if (!response.Empty()) {
+ object_sp = StructuredData::ParseJSON(response.GetStringRef());
+ }
+ }
+ }
+ }
+ return object_sp;
+}
+
+StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos(
+ lldb::addr_t image_list_address, lldb::addr_t image_count) {
+
+ StructuredData::ObjectSP args_dict(new StructuredData::Dictionary());
+ args_dict->GetAsDictionary()->AddIntegerItem("image_list_address",
+ image_list_address);
+ args_dict->GetAsDictionary()->AddIntegerItem("image_count", image_count);
+
+ return GetLoadedDynamicLibrariesInfos_sender(args_dict);
+}
+
+StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos() {
+ StructuredData::ObjectSP args_dict(new StructuredData::Dictionary());
+
+ args_dict->GetAsDictionary()->AddBooleanItem("fetch_all_solibs", true);
+
+ return GetLoadedDynamicLibrariesInfos_sender(args_dict);
+}
+
+StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos(
+ const std::vector<lldb::addr_t> &load_addresses) {
+ StructuredData::ObjectSP args_dict(new StructuredData::Dictionary());
+ StructuredData::ArraySP addresses(new StructuredData::Array);
+
+ for (auto addr : load_addresses) {
+ StructuredData::ObjectSP addr_sp(new StructuredData::Integer(addr));
+ addresses->AddItem(addr_sp);
+ }
+
+ args_dict->GetAsDictionary()->AddItem("solib_addresses", addresses);
+
+ return GetLoadedDynamicLibrariesInfos_sender(args_dict);
+}
+
+StructuredData::ObjectSP
+ProcessGDBRemote::GetLoadedDynamicLibrariesInfos_sender(
+ StructuredData::ObjectSP args_dict) {
+ StructuredData::ObjectSP object_sp;
+
+ if (m_gdb_comm.GetLoadedDynamicLibrariesInfosSupported()) {
+ // Scope for the scoped timeout object
+ GDBRemoteCommunication::ScopedTimeout timeout(m_gdb_comm,
+ std::chrono::seconds(10));
+
+ StreamString packet;
+ packet << "jGetLoadedDynamicLibrariesInfos:";
+ args_dict->Dump(packet, false);
+
+ // FIXME the final character of a JSON dictionary, '}', is the escape
+ // character in gdb-remote binary mode. lldb currently doesn't escape
+ // these characters in its packet output -- so we add the quoted version of
+ // the } character here manually in case we talk to a debugserver which un-
+ // escapes the characters at packet read time.
+ packet << (char)(0x7d ^ 0x20);
+
+ StringExtractorGDBRemote response;
+ response.SetResponseValidatorToJSON();
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response,
+ false) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ StringExtractorGDBRemote::ResponseType response_type =
+ response.GetResponseType();
+ if (response_type == StringExtractorGDBRemote::eResponse) {
+ if (!response.Empty()) {
+ object_sp = StructuredData::ParseJSON(response.GetStringRef());
+ }
+ }
+ }
+ }
+ return object_sp;
+}
+
+StructuredData::ObjectSP ProcessGDBRemote::GetSharedCacheInfo() {
+ StructuredData::ObjectSP object_sp;
+ StructuredData::ObjectSP args_dict(new StructuredData::Dictionary());
+
+ if (m_gdb_comm.GetSharedCacheInfoSupported()) {
+ StreamString packet;
+ packet << "jGetSharedCacheInfo:";
+ args_dict->Dump(packet, false);
+
+ // FIXME the final character of a JSON dictionary, '}', is the escape
+ // character in gdb-remote binary mode. lldb currently doesn't escape
+ // these characters in its packet output -- so we add the quoted version of
+ // the } character here manually in case we talk to a debugserver which un-
+ // escapes the characters at packet read time.
+ packet << (char)(0x7d ^ 0x20);
+
+ StringExtractorGDBRemote response;
+ response.SetResponseValidatorToJSON();
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response,
+ false) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ StringExtractorGDBRemote::ResponseType response_type =
+ response.GetResponseType();
+ if (response_type == StringExtractorGDBRemote::eResponse) {
+ if (!response.Empty()) {
+ object_sp = StructuredData::ParseJSON(response.GetStringRef());
+ }
+ }
+ }
+ }
+ return object_sp;
+}
+
+Status ProcessGDBRemote::ConfigureStructuredData(
+ ConstString type_name, const StructuredData::ObjectSP &config_sp) {
+ return m_gdb_comm.ConfigureRemoteStructuredData(type_name, config_sp);
+}
+
+// Establish the largest memory read/write payloads we should use. If the
+// remote stub has a max packet size, stay under that size.
+//
+// If the remote stub's max packet size is crazy large, use a reasonable
+// largeish default.
+//
+// If the remote stub doesn't advertise a max packet size, use a conservative
+// default.
+
+void ProcessGDBRemote::GetMaxMemorySize() {
+ const uint64_t reasonable_largeish_default = 128 * 1024;
+ const uint64_t conservative_default = 512;
+
+ if (m_max_memory_size == 0) {
+ uint64_t stub_max_size = m_gdb_comm.GetRemoteMaxPacketSize();
+ if (stub_max_size != UINT64_MAX && stub_max_size != 0) {
+ // Save the stub's claimed maximum packet size
+ m_remote_stub_max_memory_size = stub_max_size;
+
+ // Even if the stub says it can support ginormous packets, don't exceed
+ // our reasonable largeish default packet size.
+ if (stub_max_size > reasonable_largeish_default) {
+ stub_max_size = reasonable_largeish_default;
+ }
+
+ // Memory packet have other overheads too like Maddr,size:#NN Instead of
+ // calculating the bytes taken by size and addr every time, we take a
+ // maximum guess here.
+ if (stub_max_size > 70)
+ stub_max_size -= 32 + 32 + 6;
+ else {
+ // In unlikely scenario that max packet size is less then 70, we will
+ // hope that data being written is small enough to fit.
+ Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(
+ GDBR_LOG_COMM | GDBR_LOG_MEMORY));
+ if (log)
+ log->Warning("Packet size is too small. "
+ "LLDB may face problems while writing memory");
+ }
+
+ m_max_memory_size = stub_max_size;
+ } else {
+ m_max_memory_size = conservative_default;
+ }
+ }
+}
+
+void ProcessGDBRemote::SetUserSpecifiedMaxMemoryTransferSize(
+ uint64_t user_specified_max) {
+ if (user_specified_max != 0) {
+ GetMaxMemorySize();
+
+ if (m_remote_stub_max_memory_size != 0) {
+ if (m_remote_stub_max_memory_size < user_specified_max) {
+ m_max_memory_size = m_remote_stub_max_memory_size; // user specified a
+ // packet size too
+ // big, go as big
+ // as the remote stub says we can go.
+ } else {
+ m_max_memory_size = user_specified_max; // user's packet size is good
+ }
+ } else {
+ m_max_memory_size =
+ user_specified_max; // user's packet size is probably fine
+ }
+ }
+}
+
+bool ProcessGDBRemote::GetModuleSpec(const FileSpec &module_file_spec,
+ const ArchSpec &arch,
+ ModuleSpec &module_spec) {
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
+
+ const ModuleCacheKey key(module_file_spec.GetPath(),
+ arch.GetTriple().getTriple());
+ auto cached = m_cached_module_specs.find(key);
+ if (cached != m_cached_module_specs.end()) {
+ module_spec = cached->second;
+ return bool(module_spec);
+ }
+
+ if (!m_gdb_comm.GetModuleInfo(module_file_spec, arch, module_spec)) {
+ if (log)
+ log->Printf("ProcessGDBRemote::%s - failed to get module info for %s:%s",
+ __FUNCTION__, module_file_spec.GetPath().c_str(),
+ arch.GetTriple().getTriple().c_str());
+ return false;
+ }
+
+ if (log) {
+ StreamString stream;
+ module_spec.Dump(stream);
+ log->Printf("ProcessGDBRemote::%s - got module info for (%s:%s) : %s",
+ __FUNCTION__, module_file_spec.GetPath().c_str(),
+ arch.GetTriple().getTriple().c_str(), stream.GetData());
+ }
+
+ m_cached_module_specs[key] = module_spec;
+ return true;
+}
+
+void ProcessGDBRemote::PrefetchModuleSpecs(
+ llvm::ArrayRef<FileSpec> module_file_specs, const llvm::Triple &triple) {
+ auto module_specs = m_gdb_comm.GetModulesInfo(module_file_specs, triple);
+ if (module_specs) {
+ for (const FileSpec &spec : module_file_specs)
+ m_cached_module_specs[ModuleCacheKey(spec.GetPath(),
+ triple.getTriple())] = ModuleSpec();
+ for (const ModuleSpec &spec : *module_specs)
+ m_cached_module_specs[ModuleCacheKey(spec.GetFileSpec().GetPath(),
+ triple.getTriple())] = spec;
+ }
+}
+
+llvm::VersionTuple ProcessGDBRemote::GetHostOSVersion() {
+ return m_gdb_comm.GetOSVersion();
+}
+
+namespace {
+
+typedef std::vector<std::string> stringVec;
+
+typedef std::vector<struct GdbServerRegisterInfo> GDBServerRegisterVec;
+struct RegisterSetInfo {
+ ConstString name;
+};
+
+typedef std::map<uint32_t, RegisterSetInfo> RegisterSetMap;
+
+struct GdbServerTargetInfo {
+ std::string arch;
+ std::string osabi;
+ stringVec includes;
+ RegisterSetMap reg_set_map;
+};
+
+bool ParseRegisters(XMLNode feature_node, GdbServerTargetInfo &target_info,
+ GDBRemoteDynamicRegisterInfo &dyn_reg_info, ABISP abi_sp,
+ uint32_t &cur_reg_num, uint32_t &reg_offset) {
+ if (!feature_node)
+ return false;
+
+ feature_node.ForEachChildElementWithName(
+ "reg",
+ [&target_info, &dyn_reg_info, &cur_reg_num, &reg_offset,
+ &abi_sp](const XMLNode &reg_node) -> bool {
+ std::string gdb_group;
+ std::string gdb_type;
+ ConstString reg_name;
+ ConstString alt_name;
+ ConstString set_name;
+ std::vector<uint32_t> value_regs;
+ std::vector<uint32_t> invalidate_regs;
+ std::vector<uint8_t> dwarf_opcode_bytes;
+ bool encoding_set = false;
+ bool format_set = false;
+ RegisterInfo reg_info = {
+ nullptr, // Name
+ nullptr, // Alt name
+ 0, // byte size
+ reg_offset, // offset
+ eEncodingUint, // encoding
+ eFormatHex, // format
+ {
+ LLDB_INVALID_REGNUM, // eh_frame reg num
+ LLDB_INVALID_REGNUM, // DWARF reg num
+ LLDB_INVALID_REGNUM, // generic reg num
+ cur_reg_num, // process plugin reg num
+ cur_reg_num // native register number
+ },
+ nullptr,
+ nullptr,
+ nullptr, // Dwarf Expression opcode bytes pointer
+ 0 // Dwarf Expression opcode bytes length
+ };
+
+ reg_node.ForEachAttribute([&target_info, &gdb_group, &gdb_type,
+ &reg_name, &alt_name, &set_name, &value_regs,
+ &invalidate_regs, &encoding_set, &format_set,
+ &reg_info, &reg_offset, &dwarf_opcode_bytes](
+ const llvm::StringRef &name,
+ const llvm::StringRef &value) -> bool {
+ if (name == "name") {
+ reg_name.SetString(value);
+ } else if (name == "bitsize") {
+ reg_info.byte_size =
+ StringConvert::ToUInt32(value.data(), 0, 0) / CHAR_BIT;
+ } else if (name == "type") {
+ gdb_type = value.str();
+ } else if (name == "group") {
+ gdb_group = value.str();
+ } else if (name == "regnum") {
+ const uint32_t regnum =
+ StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0);
+ if (regnum != LLDB_INVALID_REGNUM) {
+ reg_info.kinds[eRegisterKindProcessPlugin] = regnum;
+ }
+ } else if (name == "offset") {
+ reg_offset = StringConvert::ToUInt32(value.data(), UINT32_MAX, 0);
+ } else if (name == "altname") {
+ alt_name.SetString(value);
+ } else if (name == "encoding") {
+ encoding_set = true;
+ reg_info.encoding = Args::StringToEncoding(value, eEncodingUint);
+ } else if (name == "format") {
+ format_set = true;
+ Format format = eFormatInvalid;
+ if (OptionArgParser::ToFormat(value.data(), format, nullptr)
+ .Success())
+ reg_info.format = format;
+ else if (value == "vector-sint8")
+ reg_info.format = eFormatVectorOfSInt8;
+ else if (value == "vector-uint8")
+ reg_info.format = eFormatVectorOfUInt8;
+ else if (value == "vector-sint16")
+ reg_info.format = eFormatVectorOfSInt16;
+ else if (value == "vector-uint16")
+ reg_info.format = eFormatVectorOfUInt16;
+ else if (value == "vector-sint32")
+ reg_info.format = eFormatVectorOfSInt32;
+ else if (value == "vector-uint32")
+ reg_info.format = eFormatVectorOfUInt32;
+ else if (value == "vector-float32")
+ reg_info.format = eFormatVectorOfFloat32;
+ else if (value == "vector-uint64")
+ reg_info.format = eFormatVectorOfUInt64;
+ else if (value == "vector-uint128")
+ reg_info.format = eFormatVectorOfUInt128;
+ } else if (name == "group_id") {
+ const uint32_t set_id =
+ StringConvert::ToUInt32(value.data(), UINT32_MAX, 0);
+ RegisterSetMap::const_iterator pos =
+ target_info.reg_set_map.find(set_id);
+ if (pos != target_info.reg_set_map.end())
+ set_name = pos->second.name;
+ } else if (name == "gcc_regnum" || name == "ehframe_regnum") {
+ reg_info.kinds[eRegisterKindEHFrame] =
+ StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0);
+ } else if (name == "dwarf_regnum") {
+ reg_info.kinds[eRegisterKindDWARF] =
+ StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0);
+ } else if (name == "generic") {
+ reg_info.kinds[eRegisterKindGeneric] =
+ Args::StringToGenericRegister(value);
+ } else if (name == "value_regnums") {
+ SplitCommaSeparatedRegisterNumberString(value, value_regs, 0);
+ } else if (name == "invalidate_regnums") {
+ SplitCommaSeparatedRegisterNumberString(value, invalidate_regs, 0);
+ } else if (name == "dynamic_size_dwarf_expr_bytes") {
+ StringExtractor opcode_extractor;
+ std::string opcode_string = value.str();
+ size_t dwarf_opcode_len = opcode_string.length() / 2;
+ assert(dwarf_opcode_len > 0);
+
+ dwarf_opcode_bytes.resize(dwarf_opcode_len);
+ reg_info.dynamic_size_dwarf_len = dwarf_opcode_len;
+ opcode_extractor.GetStringRef().swap(opcode_string);
+ uint32_t ret_val =
+ opcode_extractor.GetHexBytesAvail(dwarf_opcode_bytes);
+ assert(dwarf_opcode_len == ret_val);
+ UNUSED_IF_ASSERT_DISABLED(ret_val);
+ reg_info.dynamic_size_dwarf_expr_bytes = dwarf_opcode_bytes.data();
+ } else {
+ printf("unhandled attribute %s = %s\n", name.data(), value.data());
+ }
+ return true; // Keep iterating through all attributes
+ });
+
+ if (!gdb_type.empty() && !(encoding_set || format_set)) {
+ if (gdb_type.find("int") == 0) {
+ reg_info.format = eFormatHex;
+ reg_info.encoding = eEncodingUint;
+ } else if (gdb_type == "data_ptr" || gdb_type == "code_ptr") {
+ reg_info.format = eFormatAddressInfo;
+ reg_info.encoding = eEncodingUint;
+ } else if (gdb_type == "i387_ext" || gdb_type == "float") {
+ reg_info.format = eFormatFloat;
+ reg_info.encoding = eEncodingIEEE754;
+ }
+ }
+
+ // Only update the register set name if we didn't get a "reg_set"
+ // attribute. "set_name" will be empty if we didn't have a "reg_set"
+ // attribute.
+ if (!set_name) {
+ if (!gdb_group.empty()) {
+ set_name.SetCString(gdb_group.c_str());
+ } else {
+ // If no register group name provided anywhere,
+ // we'll create a 'general' register set
+ set_name.SetCString("general");
+ }
+ }
+
+ reg_info.byte_offset = reg_offset;
+ assert(reg_info.byte_size != 0);
+ reg_offset += reg_info.byte_size;
+ if (!value_regs.empty()) {
+ value_regs.push_back(LLDB_INVALID_REGNUM);
+ reg_info.value_regs = value_regs.data();
+ }
+ if (!invalidate_regs.empty()) {
+ invalidate_regs.push_back(LLDB_INVALID_REGNUM);
+ reg_info.invalidate_regs = invalidate_regs.data();
+ }
+
+ ++cur_reg_num;
+ AugmentRegisterInfoViaABI(reg_info, reg_name, abi_sp);
+ dyn_reg_info.AddRegister(reg_info, reg_name, alt_name, set_name);
+
+ return true; // Keep iterating through all "reg" elements
+ });
+ return true;
+}
+
+} // namespace
+
+// This method fetches a register description feature xml file from
+// the remote stub and adds registers/register groupsets/architecture
+// information to the current process. It will call itself recursively
+// for nested register definition files. It returns true if it was able
+// to fetch and parse an xml file.
+bool ProcessGDBRemote::GetGDBServerRegisterInfoXMLAndProcess(ArchSpec &arch_to_use,
+ std::string xml_filename,
+ uint32_t &cur_reg_num,
+ uint32_t &reg_offset) {
+ // request the target xml file
+ std::string raw;
+ lldb_private::Status lldberr;
+ if (!m_gdb_comm.ReadExtFeature(ConstString("features"),
+ ConstString(xml_filename.c_str()),
+ raw, lldberr)) {
+ return false;
+ }
+
+ XMLDocument xml_document;
+
+ if (xml_document.ParseMemory(raw.c_str(), raw.size(), xml_filename.c_str())) {
+ GdbServerTargetInfo target_info;
+ std::vector<XMLNode> feature_nodes;
+
+ // The top level feature XML file will start with a <target> tag.
+ XMLNode target_node = xml_document.GetRootElement("target");
+ if (target_node) {
+ target_node.ForEachChildElement([&target_info, &feature_nodes](
+ const XMLNode &node) -> bool {
+ llvm::StringRef name = node.GetName();
+ if (name == "architecture") {
+ node.GetElementText(target_info.arch);
+ } else if (name == "osabi") {
+ node.GetElementText(target_info.osabi);
+ } else if (name == "xi:include" || name == "include") {
+ llvm::StringRef href = node.GetAttributeValue("href");
+ if (!href.empty())
+ target_info.includes.push_back(href.str());
+ } else if (name == "feature") {
+ feature_nodes.push_back(node);
+ } else if (name == "groups") {
+ node.ForEachChildElementWithName(
+ "group", [&target_info](const XMLNode &node) -> bool {
+ uint32_t set_id = UINT32_MAX;
+ RegisterSetInfo set_info;
+
+ node.ForEachAttribute(
+ [&set_id, &set_info](const llvm::StringRef &name,
+ const llvm::StringRef &value) -> bool {
+ if (name == "id")
+ set_id = StringConvert::ToUInt32(value.data(),
+ UINT32_MAX, 0);
+ if (name == "name")
+ set_info.name = ConstString(value);
+ return true; // Keep iterating through all attributes
+ });
+
+ if (set_id != UINT32_MAX)
+ target_info.reg_set_map[set_id] = set_info;
+ return true; // Keep iterating through all "group" elements
+ });
+ }
+ return true; // Keep iterating through all children of the target_node
+ });
+ } else {
+ // In an included XML feature file, we're already "inside" the <target>
+ // tag of the initial XML file; this included file will likely only have
+ // a <feature> tag. Need to check for any more included files in this
+ // <feature> element.
+ XMLNode feature_node = xml_document.GetRootElement("feature");
+ if (feature_node) {
+ feature_nodes.push_back(feature_node);
+ feature_node.ForEachChildElement([&target_info](
+ const XMLNode &node) -> bool {
+ llvm::StringRef name = node.GetName();
+ if (name == "xi:include" || name == "include") {
+ llvm::StringRef href = node.GetAttributeValue("href");
+ if (!href.empty())
+ target_info.includes.push_back(href.str());
+ }
+ return true;
+ });
+ }
+ }
+
+ // If the target.xml includes an architecture entry like
+ // <architecture>i386:x86-64</architecture> (seen from VMWare ESXi)
+ // <architecture>arm</architecture> (seen from Segger JLink on unspecified arm board)
+ // use that if we don't have anything better.
+ if (!arch_to_use.IsValid() && !target_info.arch.empty()) {
+ if (target_info.arch == "i386:x86-64") {
+ // We don't have any information about vendor or OS.
+ arch_to_use.SetTriple("x86_64--");
+ GetTarget().MergeArchitecture(arch_to_use);
+ }
+
+ // SEGGER J-Link jtag boards send this very-generic arch name,
+ // we'll need to use this if we have absolutely nothing better
+ // to work with or the register definitions won't be accepted.
+ if (target_info.arch == "arm") {
+ arch_to_use.SetTriple("arm--");
+ GetTarget().MergeArchitecture(arch_to_use);
+ }
+ }
+
+ if (arch_to_use.IsValid()) {
+ // Don't use Process::GetABI, this code gets called from DidAttach, and
+ // in that context we haven't set the Target's architecture yet, so the
+ // ABI is also potentially incorrect.
+ ABISP abi_to_use_sp = ABI::FindPlugin(shared_from_this(), arch_to_use);
+ for (auto &feature_node : feature_nodes) {
+ ParseRegisters(feature_node, target_info, this->m_register_info,
+ abi_to_use_sp, cur_reg_num, reg_offset);
+ }
+
+ for (const auto &include : target_info.includes) {
+ GetGDBServerRegisterInfoXMLAndProcess(arch_to_use, include,
+ cur_reg_num, reg_offset);
+ }
+ }
+ } else {
+ return false;
+ }
+ return true;
+}
+
+// query the target of gdb-remote for extended target information returns
+// true on success (got register definitions), false on failure (did not).
+bool ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) {
+ // Make sure LLDB has an XML parser it can use first
+ if (!XMLDocument::XMLEnabled())
+ return false;
+
+ // check that we have extended feature read support
+ if (!m_gdb_comm.GetQXferFeaturesReadSupported())
+ return false;
+
+ uint32_t cur_reg_num = 0;
+ uint32_t reg_offset = 0;
+ if (GetGDBServerRegisterInfoXMLAndProcess (arch_to_use, "target.xml", cur_reg_num, reg_offset))
+ this->m_register_info.Finalize(arch_to_use);
+
+ return m_register_info.GetNumRegisters() > 0;
+}
+
+Status ProcessGDBRemote::GetLoadedModuleList(LoadedModuleInfoList &list) {
+ // Make sure LLDB has an XML parser it can use first
+ if (!XMLDocument::XMLEnabled())
+ return Status(0, ErrorType::eErrorTypeGeneric);
+
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS);
+ if (log)
+ log->Printf("ProcessGDBRemote::%s", __FUNCTION__);
+
+ GDBRemoteCommunicationClient &comm = m_gdb_comm;
+ bool can_use_svr4 = GetGlobalPluginProperties()->GetUseSVR4();
+
+ // check that we have extended feature read support
+ if (can_use_svr4 && comm.GetQXferLibrariesSVR4ReadSupported()) {
+ list.clear();
+
+ // request the loaded library list
+ std::string raw;
+ lldb_private::Status lldberr;
+
+ if (!comm.ReadExtFeature(ConstString("libraries-svr4"), ConstString(""),
+ raw, lldberr))
+ return Status(0, ErrorType::eErrorTypeGeneric);
+
+ // parse the xml file in memory
+ if (log)
+ log->Printf("parsing: %s", raw.c_str());
+ XMLDocument doc;
+
+ if (!doc.ParseMemory(raw.c_str(), raw.size(), "noname.xml"))
+ return Status(0, ErrorType::eErrorTypeGeneric);
+
+ XMLNode root_element = doc.GetRootElement("library-list-svr4");
+ if (!root_element)
+ return Status();
+
+ // main link map structure
+ llvm::StringRef main_lm = root_element.GetAttributeValue("main-lm");
+ if (!main_lm.empty()) {
+ list.m_link_map =
+ StringConvert::ToUInt64(main_lm.data(), LLDB_INVALID_ADDRESS, 0);
+ }
+
+ root_element.ForEachChildElementWithName(
+ "library", [log, &list](const XMLNode &library) -> bool {
+
+ LoadedModuleInfoList::LoadedModuleInfo module;
+
+ library.ForEachAttribute(
+ [&module](const llvm::StringRef &name,
+ const llvm::StringRef &value) -> bool {
+
+ if (name == "name")
+ module.set_name(value.str());
+ else if (name == "lm") {
+ // the address of the link_map struct.
+ module.set_link_map(StringConvert::ToUInt64(
+ value.data(), LLDB_INVALID_ADDRESS, 0));
+ } else if (name == "l_addr") {
+ // the displacement as read from the field 'l_addr' of the
+ // link_map struct.
+ module.set_base(StringConvert::ToUInt64(
+ value.data(), LLDB_INVALID_ADDRESS, 0));
+ // base address is always a displacement, not an absolute
+ // value.
+ module.set_base_is_offset(true);
+ } else if (name == "l_ld") {
+ // the memory address of the libraries PT_DYAMIC section.
+ module.set_dynamic(StringConvert::ToUInt64(
+ value.data(), LLDB_INVALID_ADDRESS, 0));
+ }
+
+ return true; // Keep iterating over all properties of "library"
+ });
+
+ if (log) {
+ std::string name;
+ lldb::addr_t lm = 0, base = 0, ld = 0;
+ bool base_is_offset;
+
+ module.get_name(name);
+ module.get_link_map(lm);
+ module.get_base(base);
+ module.get_base_is_offset(base_is_offset);
+ module.get_dynamic(ld);
+
+ log->Printf("found (link_map:0x%08" PRIx64 ", base:0x%08" PRIx64
+ "[%s], ld:0x%08" PRIx64 ", name:'%s')",
+ lm, base, (base_is_offset ? "offset" : "absolute"), ld,
+ name.c_str());
+ }
+
+ list.add(module);
+ return true; // Keep iterating over all "library" elements in the root
+ // node
+ });
+
+ if (log)
+ log->Printf("found %" PRId32 " modules in total",
+ (int)list.m_list.size());
+ } else if (comm.GetQXferLibrariesReadSupported()) {
+ list.clear();
+
+ // request the loaded library list
+ std::string raw;
+ lldb_private::Status lldberr;
+
+ if (!comm.ReadExtFeature(ConstString("libraries"), ConstString(""), raw,
+ lldberr))
+ return Status(0, ErrorType::eErrorTypeGeneric);
+
+ if (log)
+ log->Printf("parsing: %s", raw.c_str());
+ XMLDocument doc;
+
+ if (!doc.ParseMemory(raw.c_str(), raw.size(), "noname.xml"))
+ return Status(0, ErrorType::eErrorTypeGeneric);
+
+ XMLNode root_element = doc.GetRootElement("library-list");
+ if (!root_element)
+ return Status();
+
+ root_element.ForEachChildElementWithName(
+ "library", [log, &list](const XMLNode &library) -> bool {
+ LoadedModuleInfoList::LoadedModuleInfo module;
+
+ llvm::StringRef name = library.GetAttributeValue("name");
+ module.set_name(name.str());
+
+ // The base address of a given library will be the address of its
+ // first section. Most remotes send only one section for Windows
+ // targets for example.
+ const XMLNode &section =
+ library.FindFirstChildElementWithName("section");
+ llvm::StringRef address = section.GetAttributeValue("address");
+ module.set_base(
+ StringConvert::ToUInt64(address.data(), LLDB_INVALID_ADDRESS, 0));
+ // These addresses are absolute values.
+ module.set_base_is_offset(false);
+
+ if (log) {
+ std::string name;
+ lldb::addr_t base = 0;
+ bool base_is_offset;
+ module.get_name(name);
+ module.get_base(base);
+ module.get_base_is_offset(base_is_offset);
+
+ log->Printf("found (base:0x%08" PRIx64 "[%s], name:'%s')", base,
+ (base_is_offset ? "offset" : "absolute"), name.c_str());
+ }
+
+ list.add(module);
+ return true; // Keep iterating over all "library" elements in the root
+ // node
+ });
+
+ if (log)
+ log->Printf("found %" PRId32 " modules in total",
+ (int)list.m_list.size());
+ } else {
+ return Status(0, ErrorType::eErrorTypeGeneric);
+ }
+
+ return Status();
+}
+
+lldb::ModuleSP ProcessGDBRemote::LoadModuleAtAddress(const FileSpec &file,
+ lldb::addr_t link_map,
+ lldb::addr_t base_addr,
+ bool value_is_offset) {
+ DynamicLoader *loader = GetDynamicLoader();
+ if (!loader)
+ return nullptr;
+
+ return loader->LoadModuleAtAddress(file, link_map, base_addr,
+ value_is_offset);
+}
+
+size_t ProcessGDBRemote::LoadModules(LoadedModuleInfoList &module_list) {
+ using lldb_private::process_gdb_remote::ProcessGDBRemote;
+
+ // request a list of loaded libraries from GDBServer
+ if (GetLoadedModuleList(module_list).Fail())
+ return 0;
+
+ // get a list of all the modules
+ ModuleList new_modules;
+
+ for (LoadedModuleInfoList::LoadedModuleInfo &modInfo : module_list.m_list) {
+ std::string mod_name;
+ lldb::addr_t mod_base;
+ lldb::addr_t link_map;
+ bool mod_base_is_offset;
+
+ bool valid = true;
+ valid &= modInfo.get_name(mod_name);
+ valid &= modInfo.get_base(mod_base);
+ valid &= modInfo.get_base_is_offset(mod_base_is_offset);
+ if (!valid)
+ continue;
+
+ if (!modInfo.get_link_map(link_map))
+ link_map = LLDB_INVALID_ADDRESS;
+
+ FileSpec file(mod_name);
+ FileSystem::Instance().Resolve(file);
+ lldb::ModuleSP module_sp =
+ LoadModuleAtAddress(file, link_map, mod_base, mod_base_is_offset);
+
+ if (module_sp.get())
+ new_modules.Append(module_sp);
+ }
+
+ if (new_modules.GetSize() > 0) {
+ ModuleList removed_modules;
+ Target &target = GetTarget();
+ ModuleList &loaded_modules = m_process->GetTarget().GetImages();
+
+ for (size_t i = 0; i < loaded_modules.GetSize(); ++i) {
+ const lldb::ModuleSP loaded_module = loaded_modules.GetModuleAtIndex(i);
+
+ bool found = false;
+ for (size_t j = 0; j < new_modules.GetSize(); ++j) {
+ if (new_modules.GetModuleAtIndex(j).get() == loaded_module.get())
+ found = true;
+ }
+
+ // The main executable will never be included in libraries-svr4, don't
+ // remove it
+ if (!found &&
+ loaded_module.get() != target.GetExecutableModulePointer()) {
+ removed_modules.Append(loaded_module);
+ }
+ }
+
+ loaded_modules.Remove(removed_modules);
+ m_process->GetTarget().ModulesDidUnload(removed_modules, false);
+
+ new_modules.ForEach([&target](const lldb::ModuleSP module_sp) -> bool {
+ lldb_private::ObjectFile *obj = module_sp->GetObjectFile();
+ if (!obj)
+ return true;
+
+ if (obj->GetType() != ObjectFile::Type::eTypeExecutable)
+ return true;
+
+ lldb::ModuleSP module_copy_sp = module_sp;
+ target.SetExecutableModule(module_copy_sp, eLoadDependentsNo);
+ return false;
+ });
+
+ loaded_modules.AppendIfNeeded(new_modules);
+ m_process->GetTarget().ModulesDidLoad(new_modules);
+ }
+
+ return new_modules.GetSize();
+}
+
+size_t ProcessGDBRemote::LoadModules() {
+ LoadedModuleInfoList module_list;
+ return LoadModules(module_list);
+}
+
+Status ProcessGDBRemote::GetFileLoadAddress(const FileSpec &file,
+ bool &is_loaded,
+ lldb::addr_t &load_addr) {
+ is_loaded = false;
+ load_addr = LLDB_INVALID_ADDRESS;
+
+ std::string file_path = file.GetPath(false);
+ if (file_path.empty())
+ return Status("Empty file name specified");
+
+ StreamString packet;
+ packet.PutCString("qFileLoadAddress:");
+ packet.PutStringAsRawHex8(file_path);
+
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response,
+ false) !=
+ GDBRemoteCommunication::PacketResult::Success)
+ return Status("Sending qFileLoadAddress packet failed");
+
+ if (response.IsErrorResponse()) {
+ if (response.GetError() == 1) {
+ // The file is not loaded into the inferior
+ is_loaded = false;
+ load_addr = LLDB_INVALID_ADDRESS;
+ return Status();
+ }
+
+ return Status(
+ "Fetching file load address from remote server returned an error");
+ }
+
+ if (response.IsNormalResponse()) {
+ is_loaded = true;
+ load_addr = response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS);
+ return Status();
+ }
+
+ return Status(
+ "Unknown error happened during sending the load address packet");
+}
+
+void ProcessGDBRemote::ModulesDidLoad(ModuleList &module_list) {
+ // We must call the lldb_private::Process::ModulesDidLoad () first before we
+ // do anything
+ Process::ModulesDidLoad(module_list);
+
+ // After loading shared libraries, we can ask our remote GDB server if it
+ // needs any symbols.
+ m_gdb_comm.ServeSymbolLookups(this);
+}
+
+void ProcessGDBRemote::HandleAsyncStdout(llvm::StringRef out) {
+ AppendSTDOUT(out.data(), out.size());
+}
+
+static const char *end_delimiter = "--end--;";
+static const int end_delimiter_len = 8;
+
+void ProcessGDBRemote::HandleAsyncMisc(llvm::StringRef data) {
+ std::string input = data.str(); // '1' to move beyond 'A'
+ if (m_partial_profile_data.length() > 0) {
+ m_partial_profile_data.append(input);
+ input = m_partial_profile_data;
+ m_partial_profile_data.clear();
+ }
+
+ size_t found, pos = 0, len = input.length();
+ while ((found = input.find(end_delimiter, pos)) != std::string::npos) {
+ StringExtractorGDBRemote profileDataExtractor(
+ input.substr(pos, found).c_str());
+ std::string profile_data =
+ HarmonizeThreadIdsForProfileData(profileDataExtractor);
+ BroadcastAsyncProfileData(profile_data);
+
+ pos = found + end_delimiter_len;
+ }
+
+ if (pos < len) {
+ // Last incomplete chunk.
+ m_partial_profile_data = input.substr(pos);
+ }
+}
+
+std::string ProcessGDBRemote::HarmonizeThreadIdsForProfileData(
+ StringExtractorGDBRemote &profileDataExtractor) {
+ std::map<uint64_t, uint32_t> new_thread_id_to_used_usec_map;
+ std::string output;
+ llvm::raw_string_ostream output_stream(output);
+ llvm::StringRef name, value;
+
+ // Going to assuming thread_used_usec comes first, else bail out.
+ while (profileDataExtractor.GetNameColonValue(name, value)) {
+ if (name.compare("thread_used_id") == 0) {
+ StringExtractor threadIDHexExtractor(value);
+ uint64_t thread_id = threadIDHexExtractor.GetHexMaxU64(false, 0);
+
+ bool has_used_usec = false;
+ uint32_t curr_used_usec = 0;
+ llvm::StringRef usec_name, usec_value;
+ uint32_t input_file_pos = profileDataExtractor.GetFilePos();
+ if (profileDataExtractor.GetNameColonValue(usec_name, usec_value)) {
+ if (usec_name.equals("thread_used_usec")) {
+ has_used_usec = true;
+ usec_value.getAsInteger(0, curr_used_usec);
+ } else {
+ // We didn't find what we want, it is probably an older version. Bail
+ // out.
+ profileDataExtractor.SetFilePos(input_file_pos);
+ }
+ }
+
+ if (has_used_usec) {
+ uint32_t prev_used_usec = 0;
+ std::map<uint64_t, uint32_t>::iterator iterator =
+ m_thread_id_to_used_usec_map.find(thread_id);
+ if (iterator != m_thread_id_to_used_usec_map.end()) {
+ prev_used_usec = m_thread_id_to_used_usec_map[thread_id];
+ }
+
+ uint32_t real_used_usec = curr_used_usec - prev_used_usec;
+ // A good first time record is one that runs for at least 0.25 sec
+ bool good_first_time =
+ (prev_used_usec == 0) && (real_used_usec > 250000);
+ bool good_subsequent_time =
+ (prev_used_usec > 0) &&
+ ((real_used_usec > 0) || (HasAssignedIndexIDToThread(thread_id)));
+
+ if (good_first_time || good_subsequent_time) {
+ // We try to avoid doing too many index id reservation, resulting in
+ // fast increase of index ids.
+
+ output_stream << name << ":";
+ int32_t index_id = AssignIndexIDToThread(thread_id);
+ output_stream << index_id << ";";
+
+ output_stream << usec_name << ":" << usec_value << ";";
+ } else {
+ // Skip past 'thread_used_name'.
+ llvm::StringRef local_name, local_value;
+ profileDataExtractor.GetNameColonValue(local_name, local_value);
+ }
+
+ // Store current time as previous time so that they can be compared
+ // later.
+ new_thread_id_to_used_usec_map[thread_id] = curr_used_usec;
+ } else {
+ // Bail out and use old string.
+ output_stream << name << ":" << value << ";";
+ }
+ } else {
+ output_stream << name << ":" << value << ";";
+ }
+ }
+ output_stream << end_delimiter;
+ m_thread_id_to_used_usec_map = new_thread_id_to_used_usec_map;
+
+ return output_stream.str();
+}
+
+void ProcessGDBRemote::HandleStopReply() {
+ if (GetStopID() != 0)
+ return;
+
+ if (GetID() == LLDB_INVALID_PROCESS_ID) {
+ lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID();
+ if (pid != LLDB_INVALID_PROCESS_ID)
+ SetID(pid);
+ }
+ BuildDynamicRegisterInfo(true);
+}
+
+static const char *const s_async_json_packet_prefix = "JSON-async:";
+
+static StructuredData::ObjectSP
+ParseStructuredDataPacket(llvm::StringRef packet) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+
+ if (!packet.consume_front(s_async_json_packet_prefix)) {
+ if (log) {
+ log->Printf(
+ "GDBRemoteCommunicationClientBase::%s() received $J packet "
+ "but was not a StructuredData packet: packet starts with "
+ "%s",
+ __FUNCTION__,
+ packet.slice(0, strlen(s_async_json_packet_prefix)).str().c_str());
+ }
+ return StructuredData::ObjectSP();
+ }
+
+ // This is an asynchronous JSON packet, destined for a StructuredDataPlugin.
+ StructuredData::ObjectSP json_sp = StructuredData::ParseJSON(packet);
+ if (log) {
+ if (json_sp) {
+ StreamString json_str;
+ json_sp->Dump(json_str);
+ json_str.Flush();
+ log->Printf("ProcessGDBRemote::%s() "
+ "received Async StructuredData packet: %s",
+ __FUNCTION__, json_str.GetData());
+ } else {
+ log->Printf("ProcessGDBRemote::%s"
+ "() received StructuredData packet:"
+ " parse failure",
+ __FUNCTION__);
+ }
+ }
+ return json_sp;
+}
+
+void ProcessGDBRemote::HandleAsyncStructuredDataPacket(llvm::StringRef data) {
+ auto structured_data_sp = ParseStructuredDataPacket(data);
+ if (structured_data_sp)
+ RouteAsyncStructuredData(structured_data_sp);
+}
+
+class CommandObjectProcessGDBRemoteSpeedTest : public CommandObjectParsed {
+public:
+ CommandObjectProcessGDBRemoteSpeedTest(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process plugin packet speed-test",
+ "Tests packet speeds of various sizes to determine "
+ "the performance characteristics of the GDB remote "
+ "connection. ",
+ nullptr),
+ m_option_group(),
+ m_num_packets(LLDB_OPT_SET_1, false, "count", 'c', 0, eArgTypeCount,
+ "The number of packets to send of each varying size "
+ "(default is 1000).",
+ 1000),
+ m_max_send(LLDB_OPT_SET_1, false, "max-send", 's', 0, eArgTypeCount,
+ "The maximum number of bytes to send in a packet. Sizes "
+ "increase in powers of 2 while the size is less than or "
+ "equal to this option value. (default 1024).",
+ 1024),
+ m_max_recv(LLDB_OPT_SET_1, false, "max-receive", 'r', 0, eArgTypeCount,
+ "The maximum number of bytes to receive in a packet. Sizes "
+ "increase in powers of 2 while the size is less than or "
+ "equal to this option value. (default 1024).",
+ 1024),
+ m_json(LLDB_OPT_SET_1, false, "json", 'j',
+ "Print the output as JSON data for easy parsing.", false, true) {
+ m_option_group.Append(&m_num_packets, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_max_send, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_max_recv, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_json, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectProcessGDBRemoteSpeedTest() override {}
+
+ Options *GetOptions() override { return &m_option_group; }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ if (argc == 0) {
+ ProcessGDBRemote *process =
+ (ProcessGDBRemote *)m_interpreter.GetExecutionContext()
+ .GetProcessPtr();
+ if (process) {
+ StreamSP output_stream_sp(
+ m_interpreter.GetDebugger().GetAsyncOutputStream());
+ result.SetImmediateOutputStream(output_stream_sp);
+
+ const uint32_t num_packets =
+ (uint32_t)m_num_packets.GetOptionValue().GetCurrentValue();
+ const uint64_t max_send = m_max_send.GetOptionValue().GetCurrentValue();
+ const uint64_t max_recv = m_max_recv.GetOptionValue().GetCurrentValue();
+ const bool json = m_json.GetOptionValue().GetCurrentValue();
+ const uint64_t k_recv_amount =
+ 4 * 1024 * 1024; // Receive amount in bytes
+ process->GetGDBRemote().TestPacketSpeed(
+ num_packets, max_send, max_recv, k_recv_amount, json,
+ output_stream_sp ? *output_stream_sp : result.GetOutputStream());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ } else {
+ result.AppendErrorWithFormat("'%s' takes no arguments",
+ m_cmd_name.c_str());
+ }
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+protected:
+ OptionGroupOptions m_option_group;
+ OptionGroupUInt64 m_num_packets;
+ OptionGroupUInt64 m_max_send;
+ OptionGroupUInt64 m_max_recv;
+ OptionGroupBoolean m_json;
+};
+
+class CommandObjectProcessGDBRemotePacketHistory : public CommandObjectParsed {
+private:
+public:
+ CommandObjectProcessGDBRemotePacketHistory(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process plugin packet history",
+ "Dumps the packet history buffer. ", nullptr) {}
+
+ ~CommandObjectProcessGDBRemotePacketHistory() override {}
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ if (argc == 0) {
+ ProcessGDBRemote *process =
+ (ProcessGDBRemote *)m_interpreter.GetExecutionContext()
+ .GetProcessPtr();
+ if (process) {
+ process->GetGDBRemote().DumpHistory(result.GetOutputStream());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ } else {
+ result.AppendErrorWithFormat("'%s' takes no arguments",
+ m_cmd_name.c_str());
+ }
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+};
+
+class CommandObjectProcessGDBRemotePacketXferSize : public CommandObjectParsed {
+private:
+public:
+ CommandObjectProcessGDBRemotePacketXferSize(CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "process plugin packet xfer-size",
+ "Maximum size that lldb will try to read/write one one chunk.",
+ nullptr) {}
+
+ ~CommandObjectProcessGDBRemotePacketXferSize() override {}
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ if (argc == 0) {
+ result.AppendErrorWithFormat("'%s' takes an argument to specify the max "
+ "amount to be transferred when "
+ "reading/writing",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ ProcessGDBRemote *process =
+ (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr();
+ if (process) {
+ const char *packet_size = command.GetArgumentAtIndex(0);
+ errno = 0;
+ uint64_t user_specified_max = strtoul(packet_size, nullptr, 10);
+ if (errno == 0 && user_specified_max != 0) {
+ process->SetUserSpecifiedMaxMemoryTransferSize(user_specified_max);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+ }
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+};
+
+class CommandObjectProcessGDBRemotePacketSend : public CommandObjectParsed {
+private:
+public:
+ CommandObjectProcessGDBRemotePacketSend(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process plugin packet send",
+ "Send a custom packet through the GDB remote "
+ "protocol and print the answer. "
+ "The packet header and footer will automatically "
+ "be added to the packet prior to sending and "
+ "stripped from the result.",
+ nullptr) {}
+
+ ~CommandObjectProcessGDBRemotePacketSend() override {}
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ if (argc == 0) {
+ result.AppendErrorWithFormat(
+ "'%s' takes a one or more packet content arguments",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ ProcessGDBRemote *process =
+ (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr();
+ if (process) {
+ for (size_t i = 0; i < argc; ++i) {
+ const char *packet_cstr = command.GetArgumentAtIndex(0);
+ bool send_async = true;
+ StringExtractorGDBRemote response;
+ process->GetGDBRemote().SendPacketAndWaitForResponse(
+ packet_cstr, response, send_async);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ Stream &output_strm = result.GetOutputStream();
+ output_strm.Printf(" packet: %s\n", packet_cstr);
+ std::string &response_str = response.GetStringRef();
+
+ if (strstr(packet_cstr, "qGetProfileData") != nullptr) {
+ response_str = process->HarmonizeThreadIdsForProfileData(response);
+ }
+
+ if (response_str.empty())
+ output_strm.PutCString("response: \nerror: UNIMPLEMENTED\n");
+ else
+ output_strm.Printf("response: %s\n", response.GetStringRef().c_str());
+ }
+ }
+ return true;
+ }
+};
+
+class CommandObjectProcessGDBRemotePacketMonitor : public CommandObjectRaw {
+private:
+public:
+ CommandObjectProcessGDBRemotePacketMonitor(CommandInterpreter &interpreter)
+ : CommandObjectRaw(interpreter, "process plugin packet monitor",
+ "Send a qRcmd packet through the GDB remote protocol "
+ "and print the response."
+ "The argument passed to this command will be hex "
+ "encoded into a valid 'qRcmd' packet, sent and the "
+ "response will be printed.") {}
+
+ ~CommandObjectProcessGDBRemotePacketMonitor() override {}
+
+ bool DoExecute(llvm::StringRef command,
+ CommandReturnObject &result) override {
+ if (command.empty()) {
+ result.AppendErrorWithFormat("'%s' takes a command string argument",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ ProcessGDBRemote *process =
+ (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr();
+ if (process) {
+ StreamString packet;
+ packet.PutCString("qRcmd,");
+ packet.PutBytesAsRawHex8(command.data(), command.size());
+
+ bool send_async = true;
+ StringExtractorGDBRemote response;
+ Stream &output_strm = result.GetOutputStream();
+ process->GetGDBRemote().SendPacketAndReceiveResponseWithOutputSupport(
+ packet.GetString(), response, send_async,
+ [&output_strm](llvm::StringRef output) { output_strm << output; });
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ output_strm.Printf(" packet: %s\n", packet.GetData());
+ const std::string &response_str = response.GetStringRef();
+
+ if (response_str.empty())
+ output_strm.PutCString("response: \nerror: UNIMPLEMENTED\n");
+ else
+ output_strm.Printf("response: %s\n", response.GetStringRef().c_str());
+ }
+ return true;
+ }
+};
+
+class CommandObjectProcessGDBRemotePacket : public CommandObjectMultiword {
+private:
+public:
+ CommandObjectProcessGDBRemotePacket(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "process plugin packet",
+ "Commands that deal with GDB remote packets.",
+ nullptr) {
+ LoadSubCommand(
+ "history",
+ CommandObjectSP(
+ new CommandObjectProcessGDBRemotePacketHistory(interpreter)));
+ LoadSubCommand(
+ "send", CommandObjectSP(
+ new CommandObjectProcessGDBRemotePacketSend(interpreter)));
+ LoadSubCommand(
+ "monitor",
+ CommandObjectSP(
+ new CommandObjectProcessGDBRemotePacketMonitor(interpreter)));
+ LoadSubCommand(
+ "xfer-size",
+ CommandObjectSP(
+ new CommandObjectProcessGDBRemotePacketXferSize(interpreter)));
+ LoadSubCommand("speed-test",
+ CommandObjectSP(new CommandObjectProcessGDBRemoteSpeedTest(
+ interpreter)));
+ }
+
+ ~CommandObjectProcessGDBRemotePacket() override {}
+};
+
+class CommandObjectMultiwordProcessGDBRemote : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordProcessGDBRemote(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "process plugin",
+ "Commands for operating on a ProcessGDBRemote process.",
+ "process plugin <subcommand> [<subcommand-options>]") {
+ LoadSubCommand(
+ "packet",
+ CommandObjectSP(new CommandObjectProcessGDBRemotePacket(interpreter)));
+ }
+
+ ~CommandObjectMultiwordProcessGDBRemote() override {}
+};
+
+CommandObject *ProcessGDBRemote::GetPluginCommandObject() {
+ if (!m_command_sp)
+ m_command_sp = std::make_shared<CommandObjectMultiwordProcessGDBRemote>(
+ GetTarget().GetDebugger().GetCommandInterpreter());
+ return m_command_sp.get();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
new file mode 100644
index 000000000000..9c41fc2e5e98
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -0,0 +1,459 @@
+//===-- ProcessGDBRemote.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 liblldb_ProcessGDBRemote_h_
+#define liblldb_ProcessGDBRemote_h_
+
+#include <atomic>
+#include <map>
+#include <mutex>
+#include <string>
+#include <vector>
+
+#include "lldb/Core/LoadedModuleInfoList.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/ThreadSafeValue.h"
+#include "lldb/Host/HostThread.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/Broadcaster.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamGDBRemote.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StringExtractor.h"
+#include "lldb/Utility/StringList.h"
+#include "lldb/Utility/StructuredData.h"
+#include "lldb/lldb-private-forward.h"
+
+#include "GDBRemoteCommunicationClient.h"
+#include "GDBRemoteCommunicationReplayServer.h"
+#include "GDBRemoteRegisterContext.h"
+
+#include "llvm/ADT/DenseMap.h"
+
+namespace lldb_private {
+namespace repro {
+class Loader;
+}
+namespace process_gdb_remote {
+
+class ThreadGDBRemote;
+
+class ProcessGDBRemote : public Process,
+ private GDBRemoteClientBase::ContinueDelegate {
+public:
+ ProcessGDBRemote(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp);
+
+ ~ProcessGDBRemote() override;
+
+ static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp,
+ lldb::ListenerSP listener_sp,
+ const FileSpec *crash_file_path);
+
+ static void Initialize();
+
+ static void DebuggerInitialize(Debugger &debugger);
+
+ static void Terminate();
+
+ static ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ // Check if a given Process
+ bool CanDebug(lldb::TargetSP target_sp,
+ bool plugin_specified_by_name) override;
+
+ CommandObject *GetPluginCommandObject() override;
+
+ // Creating a new process, or attaching to an existing one
+ Status WillLaunch(Module *module) override;
+
+ Status DoLaunch(Module *exe_module, ProcessLaunchInfo &launch_info) override;
+
+ void DidLaunch() override;
+
+ Status WillAttachToProcessWithID(lldb::pid_t pid) override;
+
+ Status WillAttachToProcessWithName(const char *process_name,
+ bool wait_for_launch) override;
+
+ Status DoConnectRemote(Stream *strm, llvm::StringRef remote_url) override;
+
+ Status WillLaunchOrAttach();
+
+ Status DoAttachToProcessWithID(lldb::pid_t pid,
+ const ProcessAttachInfo &attach_info) override;
+
+ Status
+ DoAttachToProcessWithName(const char *process_name,
+ const ProcessAttachInfo &attach_info) override;
+
+ void DidAttach(ArchSpec &process_arch) override;
+
+ // PluginInterface protocol
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ // Process Control
+ Status WillResume() override;
+
+ Status DoResume() override;
+
+ Status DoHalt(bool &caused_stop) override;
+
+ Status DoDetach(bool keep_stopped) override;
+
+ bool DetachRequiresHalt() override { return true; }
+
+ Status DoSignal(int signal) override;
+
+ Status DoDestroy() override;
+
+ void RefreshStateAfterStop() override;
+
+ void SetUnixSignals(const lldb::UnixSignalsSP &signals_sp);
+
+ // Process Queries
+ bool IsAlive() override;
+
+ lldb::addr_t GetImageInfoAddress() override;
+
+ void WillPublicStop() override;
+
+ // Process Memory
+ size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ Status &error) override;
+
+ Status
+ WriteObjectFile(std::vector<ObjectFile::LoadableData> entries) override;
+
+ size_t DoWriteMemory(lldb::addr_t addr, const void *buf, size_t size,
+ Status &error) override;
+
+ lldb::addr_t DoAllocateMemory(size_t size, uint32_t permissions,
+ Status &error) override;
+
+ Status GetMemoryRegionInfo(lldb::addr_t load_addr,
+ MemoryRegionInfo &region_info) override;
+
+ Status DoDeallocateMemory(lldb::addr_t ptr) override;
+
+ // Process STDIO
+ size_t PutSTDIN(const char *buf, size_t buf_size, Status &error) override;
+
+ // Process Breakpoints
+ Status EnableBreakpointSite(BreakpointSite *bp_site) override;
+
+ Status DisableBreakpointSite(BreakpointSite *bp_site) override;
+
+ // Process Watchpoints
+ Status EnableWatchpoint(Watchpoint *wp, bool notify = true) override;
+
+ Status DisableWatchpoint(Watchpoint *wp, bool notify = true) override;
+
+ Status GetWatchpointSupportInfo(uint32_t &num) override;
+
+ lldb::user_id_t StartTrace(const TraceOptions &options,
+ Status &error) override;
+
+ Status StopTrace(lldb::user_id_t uid, lldb::tid_t thread_id) override;
+
+ Status GetData(lldb::user_id_t uid, lldb::tid_t thread_id,
+ llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset = 0) override;
+
+ Status GetMetaData(lldb::user_id_t uid, lldb::tid_t thread_id,
+ llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset = 0) override;
+
+ Status GetTraceConfig(lldb::user_id_t uid, TraceOptions &options) override;
+
+ Status GetWatchpointSupportInfo(uint32_t &num, bool &after) override;
+
+ bool StartNoticingNewThreads() override;
+
+ bool StopNoticingNewThreads() override;
+
+ GDBRemoteCommunicationClient &GetGDBRemote() { return m_gdb_comm; }
+
+ Status SendEventData(const char *data) override;
+
+ // Override DidExit so we can disconnect from the remote GDB server
+ void DidExit() override;
+
+ void SetUserSpecifiedMaxMemoryTransferSize(uint64_t user_specified_max);
+
+ bool GetModuleSpec(const FileSpec &module_file_spec, const ArchSpec &arch,
+ ModuleSpec &module_spec) override;
+
+ void PrefetchModuleSpecs(llvm::ArrayRef<FileSpec> module_file_specs,
+ const llvm::Triple &triple) override;
+
+ llvm::VersionTuple GetHostOSVersion() override;
+
+ size_t LoadModules(LoadedModuleInfoList &module_list) override;
+
+ size_t LoadModules() override;
+
+ Status GetFileLoadAddress(const FileSpec &file, bool &is_loaded,
+ lldb::addr_t &load_addr) override;
+
+ void ModulesDidLoad(ModuleList &module_list) override;
+
+ StructuredData::ObjectSP
+ GetLoadedDynamicLibrariesInfos(lldb::addr_t image_list_address,
+ lldb::addr_t image_count) override;
+
+ Status
+ ConfigureStructuredData(ConstString type_name,
+ const StructuredData::ObjectSP &config_sp) override;
+
+ StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos() override;
+
+ StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos(
+ const std::vector<lldb::addr_t> &load_addresses) override;
+
+ StructuredData::ObjectSP
+ GetLoadedDynamicLibrariesInfos_sender(StructuredData::ObjectSP args);
+
+ StructuredData::ObjectSP GetSharedCacheInfo() override;
+
+ std::string HarmonizeThreadIdsForProfileData(
+ StringExtractorGDBRemote &inputStringExtractor);
+
+protected:
+ friend class ThreadGDBRemote;
+ friend class GDBRemoteCommunicationClient;
+ friend class GDBRemoteRegisterContext;
+
+ /// Broadcaster event bits definitions.
+ enum {
+ eBroadcastBitAsyncContinue = (1 << 0),
+ eBroadcastBitAsyncThreadShouldExit = (1 << 1),
+ eBroadcastBitAsyncThreadDidExit = (1 << 2)
+ };
+
+ GDBRemoteCommunicationClient m_gdb_comm;
+ GDBRemoteCommunicationReplayServer m_gdb_replay_server;
+ std::atomic<lldb::pid_t> m_debugserver_pid;
+ std::vector<StringExtractorGDBRemote> m_stop_packet_stack; // The stop packet
+ // stack replaces
+ // the last stop
+ // packet variable
+ std::recursive_mutex m_last_stop_packet_mutex;
+ GDBRemoteDynamicRegisterInfo m_register_info;
+ Broadcaster m_async_broadcaster;
+ lldb::ListenerSP m_async_listener_sp;
+ HostThread m_async_thread;
+ std::recursive_mutex m_async_thread_state_mutex;
+ typedef std::vector<lldb::tid_t> tid_collection;
+ typedef std::vector<std::pair<lldb::tid_t, int>> tid_sig_collection;
+ typedef std::map<lldb::addr_t, lldb::addr_t> MMapMap;
+ typedef std::map<uint32_t, std::string> ExpeditedRegisterMap;
+ tid_collection m_thread_ids; // Thread IDs for all threads. This list gets
+ // updated after stopping
+ std::vector<lldb::addr_t> m_thread_pcs; // PC values for all the threads.
+ StructuredData::ObjectSP m_jstopinfo_sp; // Stop info only for any threads
+ // that have valid stop infos
+ StructuredData::ObjectSP m_jthreadsinfo_sp; // Full stop info, expedited
+ // registers and memory for all
+ // threads if "jThreadsInfo"
+ // packet is supported
+ tid_collection m_continue_c_tids; // 'c' for continue
+ tid_sig_collection m_continue_C_tids; // 'C' for continue with signal
+ tid_collection m_continue_s_tids; // 's' for step
+ tid_sig_collection m_continue_S_tids; // 'S' for step with signal
+ uint64_t m_max_memory_size; // The maximum number of bytes to read/write when
+ // reading and writing memory
+ uint64_t m_remote_stub_max_memory_size; // The maximum memory size the remote
+ // gdb stub can handle
+ MMapMap m_addr_to_mmap_size;
+ lldb::BreakpointSP m_thread_create_bp_sp;
+ bool m_waiting_for_attach;
+ bool m_destroy_tried_resuming;
+ lldb::CommandObjectSP m_command_sp;
+ int64_t m_breakpoint_pc_offset;
+ lldb::tid_t m_initial_tid; // The initial thread ID, given by stub on attach
+
+ bool m_replay_mode;
+ bool m_allow_flash_writes;
+ using FlashRangeVector = lldb_private::RangeVector<lldb::addr_t, size_t>;
+ using FlashRange = FlashRangeVector::Entry;
+ FlashRangeVector m_erased_flash_ranges;
+
+ // Accessors
+ bool IsRunning(lldb::StateType state) {
+ return state == lldb::eStateRunning || IsStepping(state);
+ }
+
+ bool IsStepping(lldb::StateType state) {
+ return state == lldb::eStateStepping;
+ }
+
+ bool CanResume(lldb::StateType state) { return state == lldb::eStateStopped; }
+
+ bool HasExited(lldb::StateType state) { return state == lldb::eStateExited; }
+
+ bool ProcessIDIsValid() const;
+
+ void Clear();
+
+ bool UpdateThreadList(ThreadList &old_thread_list,
+ ThreadList &new_thread_list) override;
+
+ Status ConnectToReplayServer(repro::Loader *loader);
+
+ Status EstablishConnectionIfNeeded(const ProcessInfo &process_info);
+
+ Status LaunchAndConnectToDebugserver(const ProcessInfo &process_info);
+
+ void KillDebugserverProcess();
+
+ void BuildDynamicRegisterInfo(bool force);
+
+ void SetLastStopPacket(const StringExtractorGDBRemote &response);
+
+ bool ParsePythonTargetDefinition(const FileSpec &target_definition_fspec);
+
+ DataExtractor GetAuxvData() override;
+
+ StructuredData::ObjectSP GetExtendedInfoForThread(lldb::tid_t tid);
+
+ void GetMaxMemorySize();
+
+ bool CalculateThreadStopInfo(ThreadGDBRemote *thread);
+
+ size_t UpdateThreadPCsFromStopReplyThreadsValue(std::string &value);
+
+ size_t UpdateThreadIDsFromStopReplyThreadsValue(std::string &value);
+
+ bool HandleNotifyPacket(StringExtractorGDBRemote &packet);
+
+ bool StartAsyncThread();
+
+ void StopAsyncThread();
+
+ static lldb::thread_result_t AsyncThread(void *arg);
+
+ static bool
+ MonitorDebugserverProcess(std::weak_ptr<ProcessGDBRemote> process_wp,
+ lldb::pid_t pid, bool exited, int signo,
+ int exit_status);
+
+ lldb::StateType SetThreadStopInfo(StringExtractor &stop_packet);
+
+ bool
+ GetThreadStopInfoFromJSON(ThreadGDBRemote *thread,
+ const StructuredData::ObjectSP &thread_infos_sp);
+
+ lldb::ThreadSP SetThreadStopInfo(StructuredData::Dictionary *thread_dict);
+
+ lldb::ThreadSP
+ SetThreadStopInfo(lldb::tid_t tid,
+ ExpeditedRegisterMap &expedited_register_map, uint8_t signo,
+ const std::string &thread_name, const std::string &reason,
+ const std::string &description, uint32_t exc_type,
+ const std::vector<lldb::addr_t> &exc_data,
+ lldb::addr_t thread_dispatch_qaddr, bool queue_vars_valid,
+ lldb_private::LazyBool associated_with_libdispatch_queue,
+ lldb::addr_t dispatch_queue_t, std::string &queue_name,
+ lldb::QueueKind queue_kind, uint64_t queue_serial);
+
+ void HandleStopReplySequence();
+
+ void ClearThreadIDList();
+
+ bool UpdateThreadIDList();
+
+ void DidLaunchOrAttach(ArchSpec &process_arch);
+
+ Status ConnectToDebugserver(llvm::StringRef host_port);
+
+ const char *GetDispatchQueueNameForThread(lldb::addr_t thread_dispatch_qaddr,
+ std::string &dispatch_queue_name);
+
+ DynamicLoader *GetDynamicLoader() override;
+
+ bool GetGDBServerRegisterInfoXMLAndProcess(ArchSpec &arch_to_use,
+ std::string xml_filename,
+ uint32_t &cur_reg_num,
+ uint32_t &reg_offset);
+
+ // Query remote GDBServer for register information
+ bool GetGDBServerRegisterInfo(ArchSpec &arch);
+
+ // Query remote GDBServer for a detailed loaded library list
+ Status GetLoadedModuleList(LoadedModuleInfoList &);
+
+ lldb::ModuleSP LoadModuleAtAddress(const FileSpec &file,
+ lldb::addr_t link_map,
+ lldb::addr_t base_addr,
+ bool value_is_offset);
+
+ Status UpdateAutomaticSignalFiltering() override;
+
+ Status FlashErase(lldb::addr_t addr, size_t size);
+
+ Status FlashDone();
+
+ bool HasErased(FlashRange range);
+
+private:
+ // For ProcessGDBRemote only
+ std::string m_partial_profile_data;
+ std::map<uint64_t, uint32_t> m_thread_id_to_used_usec_map;
+ uint64_t m_last_signals_version = 0;
+
+ static bool NewThreadNotifyBreakpointHit(void *baton,
+ StoppointCallbackContext *context,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id);
+
+ // ContinueDelegate interface
+ void HandleAsyncStdout(llvm::StringRef out) override;
+ void HandleAsyncMisc(llvm::StringRef data) override;
+ void HandleStopReply() override;
+ void HandleAsyncStructuredDataPacket(llvm::StringRef data) override;
+
+ void SetThreadPc(const lldb::ThreadSP &thread_sp, uint64_t index);
+ using ModuleCacheKey = std::pair<std::string, std::string>;
+ // KeyInfo for the cached module spec DenseMap.
+ // The invariant is that all real keys will have the file and architecture
+ // set.
+ // The empty key has an empty file and an empty arch.
+ // The tombstone key has an invalid arch and an empty file.
+ // The comparison and hash functions take the file name and architecture
+ // triple into account.
+ struct ModuleCacheInfo {
+ static ModuleCacheKey getEmptyKey() { return ModuleCacheKey(); }
+
+ static ModuleCacheKey getTombstoneKey() { return ModuleCacheKey("", "T"); }
+
+ static unsigned getHashValue(const ModuleCacheKey &key) {
+ return llvm::hash_combine(key.first, key.second);
+ }
+
+ static bool isEqual(const ModuleCacheKey &LHS, const ModuleCacheKey &RHS) {
+ return LHS == RHS;
+ }
+ };
+
+ llvm::DenseMap<ModuleCacheKey, ModuleSpec, ModuleCacheInfo>
+ m_cached_module_specs;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessGDBRemote);
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+#endif // liblldb_ProcessGDBRemote_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp
new file mode 100644
index 000000000000..8cadc45824b3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp
@@ -0,0 +1,43 @@
+//===-- ProcessGDBRemoteLog.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 "ProcessGDBRemoteLog.h"
+#include "ProcessGDBRemote.h"
+#include "llvm/Support/Threading.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+static constexpr Log::Category g_categories[] = {
+ {{"async"}, {"log asynchronous activity"}, GDBR_LOG_ASYNC},
+ {{"break"}, {"log breakpoints"}, GDBR_LOG_BREAKPOINTS},
+ {{"comm"}, {"log communication activity"}, GDBR_LOG_COMM},
+ {{"packets"}, {"log gdb remote packets"}, GDBR_LOG_PACKETS},
+ {{"memory"}, {"log memory reads and writes"}, GDBR_LOG_MEMORY},
+ {{"data-short"},
+ {"log memory bytes for memory reads and writes for short transactions "
+ "only"},
+ GDBR_LOG_MEMORY_DATA_SHORT},
+ {{"data-long"},
+ {"log memory bytes for memory reads and writes for all transactions"},
+ GDBR_LOG_MEMORY_DATA_LONG},
+ {{"process"}, {"log process events and activities"}, GDBR_LOG_PROCESS},
+ {{"step"}, {"log step related activities"}, GDBR_LOG_STEP},
+ {{"thread"}, {"log thread events and activities"}, GDBR_LOG_THREAD},
+ {{"watch"}, {"log watchpoint related activities"}, GDBR_LOG_WATCHPOINTS},
+};
+
+Log::Channel ProcessGDBRemoteLog::g_channel(g_categories, GDBR_LOG_DEFAULT);
+
+void ProcessGDBRemoteLog::Initialize() {
+ static llvm::once_flag g_once_flag;
+ llvm::call_once(g_once_flag, []() {
+ Log::Register("gdb-remote", g_channel);
+ });
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h
new file mode 100644
index 000000000000..d9b8d4536afe
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h
@@ -0,0 +1,46 @@
+//===-- ProcessGDBRemoteLog.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 liblldb_ProcessGDBRemoteLog_h_
+#define liblldb_ProcessGDBRemoteLog_h_
+
+
+#include "lldb/Utility/Log.h"
+
+#define GDBR_LOG_PROCESS (1u << 1)
+#define GDBR_LOG_THREAD (1u << 2)
+#define GDBR_LOG_PACKETS (1u << 3)
+#define GDBR_LOG_MEMORY (1u << 4) // Log memory reads/writes calls
+#define GDBR_LOG_MEMORY_DATA_SHORT \
+ (1u << 5) // Log short memory reads/writes bytes
+#define GDBR_LOG_MEMORY_DATA_LONG (1u << 6) // Log all memory reads/writes bytes
+#define GDBR_LOG_BREAKPOINTS (1u << 7)
+#define GDBR_LOG_WATCHPOINTS (1u << 8)
+#define GDBR_LOG_STEP (1u << 9)
+#define GDBR_LOG_COMM (1u << 10)
+#define GDBR_LOG_ASYNC (1u << 11)
+#define GDBR_LOG_ALL (UINT32_MAX)
+#define GDBR_LOG_DEFAULT GDBR_LOG_PACKETS
+
+namespace lldb_private {
+namespace process_gdb_remote {
+
+class ProcessGDBRemoteLog {
+ static Log::Channel g_channel;
+
+public:
+ static void Initialize();
+
+ static Log *GetLogIfAllCategoriesSet(uint32_t mask) { return g_channel.GetLogIfAll(mask); }
+ static Log *GetLogIfAnyCategoryIsSet(uint32_t mask) { return g_channel.GetLogIfAny(mask); }
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+#endif // liblldb_ProcessGDBRemoteLog_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp
new file mode 100644
index 000000000000..6607bce4488b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp
@@ -0,0 +1,343 @@
+//===-- ThreadGDBRemote.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 "ThreadGDBRemote.h"
+
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/SystemRuntime.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Target/Unwind.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+
+#include "ProcessGDBRemote.h"
+#include "ProcessGDBRemoteLog.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+// Thread Registers
+
+ThreadGDBRemote::ThreadGDBRemote(Process &process, lldb::tid_t tid)
+ : Thread(process, tid), m_thread_name(), m_dispatch_queue_name(),
+ m_thread_dispatch_qaddr(LLDB_INVALID_ADDRESS),
+ m_dispatch_queue_t(LLDB_INVALID_ADDRESS), m_queue_kind(eQueueKindUnknown),
+ m_queue_serial_number(LLDB_INVALID_QUEUE_ID),
+ m_associated_with_libdispatch_queue(eLazyBoolCalculate) {
+ Log *log(GetLogIfAnyCategoriesSet(GDBR_LOG_THREAD));
+ LLDB_LOG(log, "this = {0}, pid = {1}, tid = {2}", this, process.GetID(),
+ GetID());
+}
+
+ThreadGDBRemote::~ThreadGDBRemote() {
+ ProcessSP process_sp(GetProcess());
+ Log *log(GetLogIfAnyCategoriesSet(GDBR_LOG_THREAD));
+ LLDB_LOG(log, "this = {0}, pid = {1}, tid = {2}", this,
+ process_sp ? process_sp->GetID() : LLDB_INVALID_PROCESS_ID, GetID());
+ DestroyThread();
+}
+
+const char *ThreadGDBRemote::GetName() {
+ if (m_thread_name.empty())
+ return nullptr;
+ return m_thread_name.c_str();
+}
+
+void ThreadGDBRemote::ClearQueueInfo() {
+ m_dispatch_queue_name.clear();
+ m_queue_kind = eQueueKindUnknown;
+ m_queue_serial_number = 0;
+ m_dispatch_queue_t = LLDB_INVALID_ADDRESS;
+ m_associated_with_libdispatch_queue = eLazyBoolCalculate;
+}
+
+void ThreadGDBRemote::SetQueueInfo(std::string &&queue_name,
+ QueueKind queue_kind, uint64_t queue_serial,
+ addr_t dispatch_queue_t,
+ LazyBool associated_with_libdispatch_queue) {
+ m_dispatch_queue_name = queue_name;
+ m_queue_kind = queue_kind;
+ m_queue_serial_number = queue_serial;
+ m_dispatch_queue_t = dispatch_queue_t;
+ m_associated_with_libdispatch_queue = associated_with_libdispatch_queue;
+}
+
+const char *ThreadGDBRemote::GetQueueName() {
+ // If our cached queue info is valid, then someone called
+ // ThreadGDBRemote::SetQueueInfo(...) with valid information that was gleaned
+ // from the stop reply packet. In this case we trust that the info is valid
+ // in m_dispatch_queue_name without refetching it
+ if (CachedQueueInfoIsValid()) {
+ if (m_dispatch_queue_name.empty())
+ return nullptr;
+ else
+ return m_dispatch_queue_name.c_str();
+ }
+ // Always re-fetch the dispatch queue name since it can change
+
+ if (m_associated_with_libdispatch_queue == eLazyBoolNo)
+ return nullptr;
+
+ if (m_thread_dispatch_qaddr != 0 &&
+ m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) {
+ ProcessSP process_sp(GetProcess());
+ if (process_sp) {
+ SystemRuntime *runtime = process_sp->GetSystemRuntime();
+ if (runtime)
+ m_dispatch_queue_name =
+ runtime->GetQueueNameFromThreadQAddress(m_thread_dispatch_qaddr);
+ else
+ m_dispatch_queue_name.clear();
+
+ if (!m_dispatch_queue_name.empty())
+ return m_dispatch_queue_name.c_str();
+ }
+ }
+ return nullptr;
+}
+
+QueueKind ThreadGDBRemote::GetQueueKind() {
+ // If our cached queue info is valid, then someone called
+ // ThreadGDBRemote::SetQueueInfo(...) with valid information that was gleaned
+ // from the stop reply packet. In this case we trust that the info is valid
+ // in m_dispatch_queue_name without refetching it
+ if (CachedQueueInfoIsValid()) {
+ return m_queue_kind;
+ }
+
+ if (m_associated_with_libdispatch_queue == eLazyBoolNo)
+ return eQueueKindUnknown;
+
+ if (m_thread_dispatch_qaddr != 0 &&
+ m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) {
+ ProcessSP process_sp(GetProcess());
+ if (process_sp) {
+ SystemRuntime *runtime = process_sp->GetSystemRuntime();
+ if (runtime)
+ m_queue_kind = runtime->GetQueueKind(m_thread_dispatch_qaddr);
+ return m_queue_kind;
+ }
+ }
+ return eQueueKindUnknown;
+}
+
+queue_id_t ThreadGDBRemote::GetQueueID() {
+ // If our cached queue info is valid, then someone called
+ // ThreadGDBRemote::SetQueueInfo(...) with valid information that was gleaned
+ // from the stop reply packet. In this case we trust that the info is valid
+ // in m_dispatch_queue_name without refetching it
+ if (CachedQueueInfoIsValid())
+ return m_queue_serial_number;
+
+ if (m_associated_with_libdispatch_queue == eLazyBoolNo)
+ return LLDB_INVALID_QUEUE_ID;
+
+ if (m_thread_dispatch_qaddr != 0 &&
+ m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) {
+ ProcessSP process_sp(GetProcess());
+ if (process_sp) {
+ SystemRuntime *runtime = process_sp->GetSystemRuntime();
+ if (runtime) {
+ return runtime->GetQueueIDFromThreadQAddress(m_thread_dispatch_qaddr);
+ }
+ }
+ }
+ return LLDB_INVALID_QUEUE_ID;
+}
+
+QueueSP ThreadGDBRemote::GetQueue() {
+ queue_id_t queue_id = GetQueueID();
+ QueueSP queue;
+ if (queue_id != LLDB_INVALID_QUEUE_ID) {
+ ProcessSP process_sp(GetProcess());
+ if (process_sp) {
+ queue = process_sp->GetQueueList().FindQueueByID(queue_id);
+ }
+ }
+ return queue;
+}
+
+addr_t ThreadGDBRemote::GetQueueLibdispatchQueueAddress() {
+ if (m_dispatch_queue_t == LLDB_INVALID_ADDRESS) {
+ if (m_thread_dispatch_qaddr != 0 &&
+ m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) {
+ ProcessSP process_sp(GetProcess());
+ if (process_sp) {
+ SystemRuntime *runtime = process_sp->GetSystemRuntime();
+ if (runtime) {
+ m_dispatch_queue_t =
+ runtime->GetLibdispatchQueueAddressFromThreadQAddress(
+ m_thread_dispatch_qaddr);
+ }
+ }
+ }
+ }
+ return m_dispatch_queue_t;
+}
+
+void ThreadGDBRemote::SetQueueLibdispatchQueueAddress(
+ lldb::addr_t dispatch_queue_t) {
+ m_dispatch_queue_t = dispatch_queue_t;
+}
+
+bool ThreadGDBRemote::ThreadHasQueueInformation() const {
+ return m_thread_dispatch_qaddr != 0 &&
+ m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS &&
+ m_dispatch_queue_t != LLDB_INVALID_ADDRESS &&
+ m_queue_kind != eQueueKindUnknown && m_queue_serial_number != 0;
+}
+
+LazyBool ThreadGDBRemote::GetAssociatedWithLibdispatchQueue() {
+ return m_associated_with_libdispatch_queue;
+}
+
+void ThreadGDBRemote::SetAssociatedWithLibdispatchQueue(
+ LazyBool associated_with_libdispatch_queue) {
+ m_associated_with_libdispatch_queue = associated_with_libdispatch_queue;
+}
+
+StructuredData::ObjectSP ThreadGDBRemote::FetchThreadExtendedInfo() {
+ StructuredData::ObjectSP object_sp;
+ const lldb::user_id_t tid = GetProtocolID();
+ Log *log(GetLogIfAnyCategoriesSet(GDBR_LOG_THREAD));
+ if (log)
+ log->Printf("Fetching extended information for thread %4.4" PRIx64, tid);
+ ProcessSP process_sp(GetProcess());
+ if (process_sp) {
+ ProcessGDBRemote *gdb_process =
+ static_cast<ProcessGDBRemote *>(process_sp.get());
+ object_sp = gdb_process->GetExtendedInfoForThread(tid);
+ }
+ return object_sp;
+}
+
+void ThreadGDBRemote::WillResume(StateType resume_state) {
+ int signo = GetResumeSignal();
+ const lldb::user_id_t tid = GetProtocolID();
+ Log *log(GetLogIfAnyCategoriesSet(GDBR_LOG_THREAD));
+ if (log)
+ log->Printf("Resuming thread: %4.4" PRIx64 " with state: %s.", tid,
+ StateAsCString(resume_state));
+
+ ProcessSP process_sp(GetProcess());
+ if (process_sp) {
+ ProcessGDBRemote *gdb_process =
+ static_cast<ProcessGDBRemote *>(process_sp.get());
+ switch (resume_state) {
+ case eStateSuspended:
+ case eStateStopped:
+ // Don't append anything for threads that should stay stopped.
+ break;
+
+ case eStateRunning:
+ if (gdb_process->GetUnixSignals()->SignalIsValid(signo))
+ gdb_process->m_continue_C_tids.push_back(std::make_pair(tid, signo));
+ else
+ gdb_process->m_continue_c_tids.push_back(tid);
+ break;
+
+ case eStateStepping:
+ if (gdb_process->GetUnixSignals()->SignalIsValid(signo))
+ gdb_process->m_continue_S_tids.push_back(std::make_pair(tid, signo));
+ else
+ gdb_process->m_continue_s_tids.push_back(tid);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+void ThreadGDBRemote::RefreshStateAfterStop() {
+ // Invalidate all registers in our register context. We don't set "force" to
+ // true because the stop reply packet might have had some register values
+ // that were expedited and these will already be copied into the register
+ // context by the time this function gets called. The
+ // GDBRemoteRegisterContext class has been made smart enough to detect when
+ // it needs to invalidate which registers are valid by putting hooks in the
+ // register read and register supply functions where they check the process
+ // stop ID and do the right thing.
+ const bool force = false;
+ GetRegisterContext()->InvalidateIfNeeded(force);
+}
+
+bool ThreadGDBRemote::ThreadIDIsValid(lldb::tid_t thread) {
+ return thread != 0;
+}
+
+void ThreadGDBRemote::Dump(Log *log, uint32_t index) {}
+
+bool ThreadGDBRemote::ShouldStop(bool &step_more) { return true; }
+lldb::RegisterContextSP ThreadGDBRemote::GetRegisterContext() {
+ if (!m_reg_context_sp)
+ m_reg_context_sp = CreateRegisterContextForFrame(nullptr);
+ return m_reg_context_sp;
+}
+
+lldb::RegisterContextSP
+ThreadGDBRemote::CreateRegisterContextForFrame(StackFrame *frame) {
+ lldb::RegisterContextSP reg_ctx_sp;
+ uint32_t concrete_frame_idx = 0;
+
+ if (frame)
+ concrete_frame_idx = frame->GetConcreteFrameIndex();
+
+ if (concrete_frame_idx == 0) {
+ ProcessSP process_sp(GetProcess());
+ if (process_sp) {
+ ProcessGDBRemote *gdb_process =
+ static_cast<ProcessGDBRemote *>(process_sp.get());
+ // read_all_registers_at_once will be true if 'p' packet is not
+ // supported.
+ bool read_all_registers_at_once =
+ !gdb_process->GetGDBRemote().GetpPacketSupported(GetID());
+ reg_ctx_sp = std::make_shared<GDBRemoteRegisterContext>(
+ *this, concrete_frame_idx, gdb_process->m_register_info,
+ read_all_registers_at_once);
+ }
+ } else {
+ Unwind *unwinder = GetUnwinder();
+ if (unwinder != nullptr)
+ reg_ctx_sp = unwinder->CreateRegisterContextForFrame(frame);
+ }
+ return reg_ctx_sp;
+}
+
+bool ThreadGDBRemote::PrivateSetRegisterValue(uint32_t reg,
+ llvm::ArrayRef<uint8_t> data) {
+ GDBRemoteRegisterContext *gdb_reg_ctx =
+ static_cast<GDBRemoteRegisterContext *>(GetRegisterContext().get());
+ assert(gdb_reg_ctx);
+ return gdb_reg_ctx->PrivateSetRegisterValue(reg, data);
+}
+
+bool ThreadGDBRemote::PrivateSetRegisterValue(uint32_t reg, uint64_t regval) {
+ GDBRemoteRegisterContext *gdb_reg_ctx =
+ static_cast<GDBRemoteRegisterContext *>(GetRegisterContext().get());
+ assert(gdb_reg_ctx);
+ return gdb_reg_ctx->PrivateSetRegisterValue(reg, regval);
+}
+
+bool ThreadGDBRemote::CalculateStopInfo() {
+ ProcessSP process_sp(GetProcess());
+ if (process_sp)
+ return static_cast<ProcessGDBRemote *>(process_sp.get())
+ ->CalculateThreadStopInfo(this);
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h
new file mode 100644
index 000000000000..c74be169abaf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h
@@ -0,0 +1,119 @@
+//===-- ThreadGDBRemote.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 liblldb_ThreadGDBRemote_h_
+#define liblldb_ThreadGDBRemote_h_
+
+#include <string>
+
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/StructuredData.h"
+
+class StringExtractor;
+
+namespace lldb_private {
+class Process;
+
+namespace process_gdb_remote {
+
+class ProcessGDBRemote;
+
+class ThreadGDBRemote : public Thread {
+public:
+ ThreadGDBRemote(Process &process, lldb::tid_t tid);
+
+ ~ThreadGDBRemote() override;
+
+ void WillResume(lldb::StateType resume_state) override;
+
+ void RefreshStateAfterStop() override;
+
+ const char *GetName() override;
+
+ const char *GetQueueName() override;
+
+ lldb::QueueKind GetQueueKind() override;
+
+ lldb::queue_id_t GetQueueID() override;
+
+ lldb::QueueSP GetQueue() override;
+
+ lldb::addr_t GetQueueLibdispatchQueueAddress() override;
+
+ void SetQueueLibdispatchQueueAddress(lldb::addr_t dispatch_queue_t) override;
+
+ bool ThreadHasQueueInformation() const override;
+
+ lldb::RegisterContextSP GetRegisterContext() override;
+
+ lldb::RegisterContextSP
+ CreateRegisterContextForFrame(StackFrame *frame) override;
+
+ void Dump(Log *log, uint32_t index);
+
+ static bool ThreadIDIsValid(lldb::tid_t thread);
+
+ bool ShouldStop(bool &step_more);
+
+ const char *GetBasicInfoAsString();
+
+ void SetName(const char *name) override {
+ if (name && name[0])
+ m_thread_name.assign(name);
+ else
+ m_thread_name.clear();
+ }
+
+ lldb::addr_t GetThreadDispatchQAddr() { return m_thread_dispatch_qaddr; }
+
+ void SetThreadDispatchQAddr(lldb::addr_t thread_dispatch_qaddr) {
+ m_thread_dispatch_qaddr = thread_dispatch_qaddr;
+ }
+
+ void ClearQueueInfo();
+
+ void SetQueueInfo(std::string &&queue_name, lldb::QueueKind queue_kind,
+ uint64_t queue_serial, lldb::addr_t dispatch_queue_t,
+ lldb_private::LazyBool associated_with_libdispatch_queue);
+
+ lldb_private::LazyBool GetAssociatedWithLibdispatchQueue() override;
+
+ void SetAssociatedWithLibdispatchQueue(
+ lldb_private::LazyBool associated_with_libdispatch_queue) override;
+
+ StructuredData::ObjectSP FetchThreadExtendedInfo() override;
+
+protected:
+ friend class ProcessGDBRemote;
+
+ std::string m_thread_name;
+ std::string m_dispatch_queue_name;
+ lldb::addr_t m_thread_dispatch_qaddr;
+ lldb::addr_t m_dispatch_queue_t;
+ lldb::QueueKind
+ m_queue_kind; // Queue info from stop reply/stop info for thread
+ uint64_t
+ m_queue_serial_number; // Queue info from stop reply/stop info for thread
+ lldb_private::LazyBool m_associated_with_libdispatch_queue;
+
+ bool PrivateSetRegisterValue(uint32_t reg, llvm::ArrayRef<uint8_t> data);
+
+ bool PrivateSetRegisterValue(uint32_t reg, uint64_t regval);
+
+ bool CachedQueueInfoIsValid() const {
+ return m_queue_kind != lldb::eQueueKindUnknown;
+ }
+ void SetStopInfoFromPacket(StringExtractor &stop_packet, uint32_t stop_id);
+
+ bool CalculateStopInfo() override;
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+#endif // liblldb_ThreadGDBRemote_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
new file mode 100644
index 000000000000..ff015aa54b76
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
@@ -0,0 +1,615 @@
+//===-- MinidumpParser.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 "MinidumpParser.h"
+#include "NtStructures.h"
+#include "RegisterContextMinidump_x86_32.h"
+
+#include "Plugins/Process/Utility/LinuxProcMaps.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+
+// C includes
+// C++ includes
+#include <algorithm>
+#include <map>
+#include <vector>
+#include <utility>
+
+using namespace lldb_private;
+using namespace minidump;
+
+llvm::Expected<MinidumpParser>
+MinidumpParser::Create(const lldb::DataBufferSP &data_sp) {
+ auto ExpectedFile = llvm::object::MinidumpFile::create(
+ llvm::MemoryBufferRef(toStringRef(data_sp->GetData()), "minidump"));
+ if (!ExpectedFile)
+ return ExpectedFile.takeError();
+
+ return MinidumpParser(data_sp, std::move(*ExpectedFile));
+}
+
+MinidumpParser::MinidumpParser(lldb::DataBufferSP data_sp,
+ std::unique_ptr<llvm::object::MinidumpFile> file)
+ : m_data_sp(std::move(data_sp)), m_file(std::move(file)) {}
+
+llvm::ArrayRef<uint8_t> MinidumpParser::GetData() {
+ return llvm::ArrayRef<uint8_t>(m_data_sp->GetBytes(),
+ m_data_sp->GetByteSize());
+}
+
+llvm::ArrayRef<uint8_t> MinidumpParser::GetStream(StreamType stream_type) {
+ return m_file->getRawStream(stream_type)
+ .getValueOr(llvm::ArrayRef<uint8_t>());
+}
+
+UUID MinidumpParser::GetModuleUUID(const minidump::Module *module) {
+ auto cv_record =
+ GetData().slice(module->CvRecord.RVA, module->CvRecord.DataSize);
+
+ // Read the CV record signature
+ const llvm::support::ulittle32_t *signature = nullptr;
+ Status error = consumeObject(cv_record, signature);
+ if (error.Fail())
+ return UUID();
+
+ const CvSignature cv_signature =
+ static_cast<CvSignature>(static_cast<uint32_t>(*signature));
+
+ if (cv_signature == CvSignature::Pdb70) {
+ const CvRecordPdb70 *pdb70_uuid = nullptr;
+ Status error = consumeObject(cv_record, pdb70_uuid);
+ if (error.Fail())
+ return UUID();
+
+ CvRecordPdb70 swapped;
+ if (!GetArchitecture().GetTriple().isOSBinFormatELF()) {
+ // LLDB's UUID class treats the data as a sequence of bytes, but breakpad
+ // interprets it as a sequence of little-endian fields, which it converts
+ // to big-endian when converting to text. Swap the bytes to big endian so
+ // that the string representation comes out right.
+ swapped = *pdb70_uuid;
+ llvm::sys::swapByteOrder(swapped.Uuid.Data1);
+ llvm::sys::swapByteOrder(swapped.Uuid.Data2);
+ llvm::sys::swapByteOrder(swapped.Uuid.Data3);
+ llvm::sys::swapByteOrder(swapped.Age);
+ pdb70_uuid = &swapped;
+ }
+ if (pdb70_uuid->Age != 0)
+ return UUID::fromOptionalData(pdb70_uuid, sizeof(*pdb70_uuid));
+ return UUID::fromOptionalData(&pdb70_uuid->Uuid, sizeof(pdb70_uuid->Uuid));
+ } else if (cv_signature == CvSignature::ElfBuildId)
+ return UUID::fromOptionalData(cv_record);
+
+ return UUID();
+}
+
+llvm::ArrayRef<minidump::Thread> MinidumpParser::GetThreads() {
+ auto ExpectedThreads = GetMinidumpFile().getThreadList();
+ if (ExpectedThreads)
+ return *ExpectedThreads;
+
+ LLDB_LOG_ERROR(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD),
+ ExpectedThreads.takeError(),
+ "Failed to read thread list: {0}");
+ return {};
+}
+
+llvm::ArrayRef<uint8_t>
+MinidumpParser::GetThreadContext(const LocationDescriptor &location) {
+ if (location.RVA + location.DataSize > GetData().size())
+ return {};
+ return GetData().slice(location.RVA, location.DataSize);
+}
+
+llvm::ArrayRef<uint8_t>
+MinidumpParser::GetThreadContext(const minidump::Thread &td) {
+ return GetThreadContext(td.Context);
+}
+
+llvm::ArrayRef<uint8_t>
+MinidumpParser::GetThreadContextWow64(const minidump::Thread &td) {
+ // On Windows, a 32-bit process can run on a 64-bit machine under WOW64. If
+ // the minidump was captured with a 64-bit debugger, then the CONTEXT we just
+ // grabbed from the mini_dump_thread is the one for the 64-bit "native"
+ // process rather than the 32-bit "guest" process we care about. In this
+ // case, we can get the 32-bit CONTEXT from the TEB (Thread Environment
+ // Block) of the 64-bit process.
+ auto teb_mem = GetMemory(td.EnvironmentBlock, sizeof(TEB64));
+ if (teb_mem.empty())
+ return {};
+
+ const TEB64 *wow64teb;
+ Status error = consumeObject(teb_mem, wow64teb);
+ if (error.Fail())
+ return {};
+
+ // Slot 1 of the thread-local storage in the 64-bit TEB points to a structure
+ // that includes the 32-bit CONTEXT (after a ULONG). See:
+ // https://msdn.microsoft.com/en-us/library/ms681670.aspx
+ auto context =
+ GetMemory(wow64teb->tls_slots[1] + 4, sizeof(MinidumpContext_x86_32));
+ if (context.size() < sizeof(MinidumpContext_x86_32))
+ return {};
+
+ return context;
+ // NOTE: We don't currently use the TEB for anything else. If we
+ // need it in the future, the 32-bit TEB is located according to the address
+ // stored in the first slot of the 64-bit TEB (wow64teb.Reserved1[0]).
+}
+
+ArchSpec MinidumpParser::GetArchitecture() {
+ if (m_arch.IsValid())
+ return m_arch;
+
+ // Set the architecture in m_arch
+ llvm::Expected<const SystemInfo &> system_info = m_file->getSystemInfo();
+
+ if (!system_info) {
+ LLDB_LOG_ERROR(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS),
+ system_info.takeError(),
+ "Failed to read SystemInfo stream: {0}");
+ return m_arch;
+ }
+
+ // TODO what to do about big endiand flavors of arm ?
+ // TODO set the arm subarch stuff if the minidump has info about it
+
+ llvm::Triple triple;
+ triple.setVendor(llvm::Triple::VendorType::UnknownVendor);
+
+ switch (system_info->ProcessorArch) {
+ case ProcessorArchitecture::X86:
+ triple.setArch(llvm::Triple::ArchType::x86);
+ break;
+ case ProcessorArchitecture::AMD64:
+ triple.setArch(llvm::Triple::ArchType::x86_64);
+ break;
+ case ProcessorArchitecture::ARM:
+ triple.setArch(llvm::Triple::ArchType::arm);
+ break;
+ case ProcessorArchitecture::ARM64:
+ triple.setArch(llvm::Triple::ArchType::aarch64);
+ break;
+ default:
+ triple.setArch(llvm::Triple::ArchType::UnknownArch);
+ break;
+ }
+
+ // TODO add all of the OSes that Minidump/breakpad distinguishes?
+ switch (system_info->PlatformId) {
+ case OSPlatform::Win32S:
+ case OSPlatform::Win32Windows:
+ case OSPlatform::Win32NT:
+ case OSPlatform::Win32CE:
+ triple.setOS(llvm::Triple::OSType::Win32);
+ break;
+ case OSPlatform::Linux:
+ triple.setOS(llvm::Triple::OSType::Linux);
+ break;
+ case OSPlatform::MacOSX:
+ triple.setOS(llvm::Triple::OSType::MacOSX);
+ triple.setVendor(llvm::Triple::Apple);
+ break;
+ case OSPlatform::IOS:
+ triple.setOS(llvm::Triple::OSType::IOS);
+ triple.setVendor(llvm::Triple::Apple);
+ break;
+ case OSPlatform::Android:
+ triple.setOS(llvm::Triple::OSType::Linux);
+ triple.setEnvironment(llvm::Triple::EnvironmentType::Android);
+ break;
+ default: {
+ triple.setOS(llvm::Triple::OSType::UnknownOS);
+ auto ExpectedCSD = m_file->getString(system_info->CSDVersionRVA);
+ if (!ExpectedCSD) {
+ LLDB_LOG_ERROR(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS),
+ ExpectedCSD.takeError(),
+ "Failed to CSD Version string: {0}");
+ } else {
+ if (ExpectedCSD->find("Linux") != std::string::npos)
+ triple.setOS(llvm::Triple::OSType::Linux);
+ }
+ break;
+ }
+ }
+ m_arch.SetTriple(triple);
+ return m_arch;
+}
+
+const MinidumpMiscInfo *MinidumpParser::GetMiscInfo() {
+ llvm::ArrayRef<uint8_t> data = GetStream(StreamType::MiscInfo);
+
+ if (data.size() == 0)
+ return nullptr;
+
+ return MinidumpMiscInfo::Parse(data);
+}
+
+llvm::Optional<LinuxProcStatus> MinidumpParser::GetLinuxProcStatus() {
+ llvm::ArrayRef<uint8_t> data = GetStream(StreamType::LinuxProcStatus);
+
+ if (data.size() == 0)
+ return llvm::None;
+
+ return LinuxProcStatus::Parse(data);
+}
+
+llvm::Optional<lldb::pid_t> MinidumpParser::GetPid() {
+ const MinidumpMiscInfo *misc_info = GetMiscInfo();
+ if (misc_info != nullptr) {
+ return misc_info->GetPid();
+ }
+
+ llvm::Optional<LinuxProcStatus> proc_status = GetLinuxProcStatus();
+ if (proc_status.hasValue()) {
+ return proc_status->GetPid();
+ }
+
+ return llvm::None;
+}
+
+llvm::ArrayRef<minidump::Module> MinidumpParser::GetModuleList() {
+ auto ExpectedModules = GetMinidumpFile().getModuleList();
+ if (ExpectedModules)
+ return *ExpectedModules;
+
+ LLDB_LOG_ERROR(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES),
+ ExpectedModules.takeError(),
+ "Failed to read module list: {0}");
+ return {};
+}
+
+std::vector<const minidump::Module *> MinidumpParser::GetFilteredModuleList() {
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES);
+ auto ExpectedModules = GetMinidumpFile().getModuleList();
+ if (!ExpectedModules) {
+ LLDB_LOG_ERROR(log, ExpectedModules.takeError(),
+ "Failed to read module list: {0}");
+ return {};
+ }
+
+ // map module_name -> filtered_modules index
+ typedef llvm::StringMap<size_t> MapType;
+ MapType module_name_to_filtered_index;
+
+ std::vector<const minidump::Module *> filtered_modules;
+
+ for (const auto &module : *ExpectedModules) {
+ auto ExpectedName = m_file->getString(module.ModuleNameRVA);
+ if (!ExpectedName) {
+ LLDB_LOG_ERROR(log, ExpectedName.takeError(),
+ "Failed to get module name: {0}");
+ continue;
+ }
+
+ MapType::iterator iter;
+ bool inserted;
+ // See if we have inserted this module aready into filtered_modules. If we
+ // haven't insert an entry into module_name_to_filtered_index with the
+ // index where we will insert it if it isn't in the vector already.
+ std::tie(iter, inserted) = module_name_to_filtered_index.try_emplace(
+ *ExpectedName, filtered_modules.size());
+
+ if (inserted) {
+ // This module has not been seen yet, insert it into filtered_modules at
+ // the index that was inserted into module_name_to_filtered_index using
+ // "filtered_modules.size()" above.
+ filtered_modules.push_back(&module);
+ } else {
+ // This module has been seen. Modules are sometimes mentioned multiple
+ // times when they are mapped discontiguously, so find the module with
+ // the lowest "base_of_image" and use that as the filtered module.
+ auto dup_module = filtered_modules[iter->second];
+ if (module.BaseOfImage < dup_module->BaseOfImage)
+ filtered_modules[iter->second] = &module;
+ }
+ }
+ return filtered_modules;
+}
+
+const MinidumpExceptionStream *MinidumpParser::GetExceptionStream() {
+ llvm::ArrayRef<uint8_t> data = GetStream(StreamType::Exception);
+
+ if (data.size() == 0)
+ return nullptr;
+
+ return MinidumpExceptionStream::Parse(data);
+}
+
+llvm::Optional<minidump::Range>
+MinidumpParser::FindMemoryRange(lldb::addr_t addr) {
+ llvm::ArrayRef<uint8_t> data64 = GetStream(StreamType::Memory64List);
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES);
+
+ auto ExpectedMemory = GetMinidumpFile().getMemoryList();
+ if (!ExpectedMemory) {
+ LLDB_LOG_ERROR(log, ExpectedMemory.takeError(),
+ "Failed to read memory list: {0}");
+ } else {
+ for (const auto &memory_desc : *ExpectedMemory) {
+ const LocationDescriptor &loc_desc = memory_desc.Memory;
+ const lldb::addr_t range_start = memory_desc.StartOfMemoryRange;
+ const size_t range_size = loc_desc.DataSize;
+
+ if (loc_desc.RVA + loc_desc.DataSize > GetData().size())
+ return llvm::None;
+
+ if (range_start <= addr && addr < range_start + range_size) {
+ auto ExpectedSlice = GetMinidumpFile().getRawData(loc_desc);
+ if (!ExpectedSlice) {
+ LLDB_LOG_ERROR(log, ExpectedSlice.takeError(),
+ "Failed to get memory slice: {0}");
+ return llvm::None;
+ }
+ return minidump::Range(range_start, *ExpectedSlice);
+ }
+ }
+ }
+
+ // Some Minidumps have a Memory64ListStream that captures all the heap memory
+ // (full-memory Minidumps). We can't exactly use the same loop as above,
+ // because the Minidump uses slightly different data structures to describe
+ // those
+
+ if (!data64.empty()) {
+ llvm::ArrayRef<MinidumpMemoryDescriptor64> memory64_list;
+ uint64_t base_rva;
+ std::tie(memory64_list, base_rva) =
+ MinidumpMemoryDescriptor64::ParseMemory64List(data64);
+
+ if (memory64_list.empty())
+ return llvm::None;
+
+ for (const auto &memory_desc64 : memory64_list) {
+ const lldb::addr_t range_start = memory_desc64.start_of_memory_range;
+ const size_t range_size = memory_desc64.data_size;
+
+ if (base_rva + range_size > GetData().size())
+ return llvm::None;
+
+ if (range_start <= addr && addr < range_start + range_size) {
+ return minidump::Range(range_start,
+ GetData().slice(base_rva, range_size));
+ }
+ base_rva += range_size;
+ }
+ }
+
+ return llvm::None;
+}
+
+llvm::ArrayRef<uint8_t> MinidumpParser::GetMemory(lldb::addr_t addr,
+ size_t size) {
+ // I don't have a sense of how frequently this is called or how many memory
+ // ranges a Minidump typically has, so I'm not sure if searching for the
+ // appropriate range linearly each time is stupid. Perhaps we should build
+ // an index for faster lookups.
+ llvm::Optional<minidump::Range> range = FindMemoryRange(addr);
+ if (!range)
+ return {};
+
+ // There's at least some overlap between the beginning of the desired range
+ // (addr) and the current range. Figure out where the overlap begins and how
+ // much overlap there is.
+
+ const size_t offset = addr - range->start;
+
+ if (addr < range->start || offset >= range->range_ref.size())
+ return {};
+
+ const size_t overlap = std::min(size, range->range_ref.size() - offset);
+ return range->range_ref.slice(offset, overlap);
+}
+
+static bool
+CreateRegionsCacheFromLinuxMaps(MinidumpParser &parser,
+ std::vector<MemoryRegionInfo> &regions) {
+ auto data = parser.GetStream(StreamType::LinuxMaps);
+ if (data.empty())
+ return false;
+ ParseLinuxMapRegions(llvm::toStringRef(data),
+ [&](const lldb_private::MemoryRegionInfo &region,
+ const lldb_private::Status &status) -> bool {
+ if (status.Success())
+ regions.push_back(region);
+ return true;
+ });
+ return !regions.empty();
+}
+
+static bool
+CreateRegionsCacheFromMemoryInfoList(MinidumpParser &parser,
+ std::vector<MemoryRegionInfo> &regions) {
+ auto data = parser.GetStream(StreamType::MemoryInfoList);
+ if (data.empty())
+ return false;
+ auto mem_info_list = MinidumpMemoryInfo::ParseMemoryInfoList(data);
+ if (mem_info_list.empty())
+ return false;
+ constexpr auto yes = MemoryRegionInfo::eYes;
+ constexpr auto no = MemoryRegionInfo::eNo;
+ regions.reserve(mem_info_list.size());
+ for (const auto &entry : mem_info_list) {
+ MemoryRegionInfo region;
+ region.GetRange().SetRangeBase(entry->base_address);
+ region.GetRange().SetByteSize(entry->region_size);
+ region.SetReadable(entry->isReadable() ? yes : no);
+ region.SetWritable(entry->isWritable() ? yes : no);
+ region.SetExecutable(entry->isExecutable() ? yes : no);
+ region.SetMapped(entry->isMapped() ? yes : no);
+ regions.push_back(region);
+ }
+ return !regions.empty();
+}
+
+static bool
+CreateRegionsCacheFromMemoryList(MinidumpParser &parser,
+ std::vector<MemoryRegionInfo> &regions) {
+ Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES);
+ auto ExpectedMemory = parser.GetMinidumpFile().getMemoryList();
+ if (!ExpectedMemory) {
+ LLDB_LOG_ERROR(log, ExpectedMemory.takeError(),
+ "Failed to read memory list: {0}");
+ return false;
+ }
+ regions.reserve(ExpectedMemory->size());
+ for (const MemoryDescriptor &memory_desc : *ExpectedMemory) {
+ if (memory_desc.Memory.DataSize == 0)
+ continue;
+ MemoryRegionInfo region;
+ region.GetRange().SetRangeBase(memory_desc.StartOfMemoryRange);
+ region.GetRange().SetByteSize(memory_desc.Memory.DataSize);
+ region.SetReadable(MemoryRegionInfo::eYes);
+ region.SetMapped(MemoryRegionInfo::eYes);
+ regions.push_back(region);
+ }
+ regions.shrink_to_fit();
+ return !regions.empty();
+}
+
+static bool
+CreateRegionsCacheFromMemory64List(MinidumpParser &parser,
+ std::vector<MemoryRegionInfo> &regions) {
+ llvm::ArrayRef<uint8_t> data =
+ parser.GetStream(StreamType::Memory64List);
+ if (data.empty())
+ return false;
+ llvm::ArrayRef<MinidumpMemoryDescriptor64> memory64_list;
+ uint64_t base_rva;
+ std::tie(memory64_list, base_rva) =
+ MinidumpMemoryDescriptor64::ParseMemory64List(data);
+
+ if (memory64_list.empty())
+ return false;
+
+ regions.reserve(memory64_list.size());
+ for (const auto &memory_desc : memory64_list) {
+ if (memory_desc.data_size == 0)
+ continue;
+ MemoryRegionInfo region;
+ region.GetRange().SetRangeBase(memory_desc.start_of_memory_range);
+ region.GetRange().SetByteSize(memory_desc.data_size);
+ region.SetReadable(MemoryRegionInfo::eYes);
+ region.SetMapped(MemoryRegionInfo::eYes);
+ regions.push_back(region);
+ }
+ regions.shrink_to_fit();
+ return !regions.empty();
+}
+
+MemoryRegionInfo
+MinidumpParser::FindMemoryRegion(lldb::addr_t load_addr) const {
+ auto begin = m_regions.begin();
+ auto end = m_regions.end();
+ auto pos = std::lower_bound(begin, end, load_addr);
+ if (pos != end && pos->GetRange().Contains(load_addr))
+ return *pos;
+
+ MemoryRegionInfo region;
+ if (pos == begin)
+ region.GetRange().SetRangeBase(0);
+ else {
+ auto prev = pos - 1;
+ if (prev->GetRange().Contains(load_addr))
+ return *prev;
+ region.GetRange().SetRangeBase(prev->GetRange().GetRangeEnd());
+ }
+ if (pos == end)
+ region.GetRange().SetRangeEnd(UINT64_MAX);
+ else
+ region.GetRange().SetRangeEnd(pos->GetRange().GetRangeBase());
+ region.SetReadable(MemoryRegionInfo::eNo);
+ region.SetWritable(MemoryRegionInfo::eNo);
+ region.SetExecutable(MemoryRegionInfo::eNo);
+ region.SetMapped(MemoryRegionInfo::eNo);
+ return region;
+}
+
+MemoryRegionInfo
+MinidumpParser::GetMemoryRegionInfo(lldb::addr_t load_addr) {
+ if (!m_parsed_regions)
+ GetMemoryRegions();
+ return FindMemoryRegion(load_addr);
+}
+
+const MemoryRegionInfos &MinidumpParser::GetMemoryRegions() {
+ if (!m_parsed_regions) {
+ m_parsed_regions = true;
+ // We haven't cached our memory regions yet we will create the region cache
+ // once. We create the region cache using the best source. We start with
+ // the linux maps since they are the most complete and have names for the
+ // regions. Next we try the MemoryInfoList since it has
+ // read/write/execute/map data, and then fall back to the MemoryList and
+ // Memory64List to just get a list of the memory that is mapped in this
+ // core file
+ if (!CreateRegionsCacheFromLinuxMaps(*this, m_regions))
+ if (!CreateRegionsCacheFromMemoryInfoList(*this, m_regions))
+ if (!CreateRegionsCacheFromMemoryList(*this, m_regions))
+ CreateRegionsCacheFromMemory64List(*this, m_regions);
+ llvm::sort(m_regions.begin(), m_regions.end());
+ }
+ return m_regions;
+}
+
+#define ENUM_TO_CSTR(ST) \
+ case StreamType::ST: \
+ return #ST
+
+llvm::StringRef
+MinidumpParser::GetStreamTypeAsString(StreamType stream_type) {
+ switch (stream_type) {
+ ENUM_TO_CSTR(Unused);
+ ENUM_TO_CSTR(ThreadList);
+ ENUM_TO_CSTR(ModuleList);
+ ENUM_TO_CSTR(MemoryList);
+ ENUM_TO_CSTR(Exception);
+ ENUM_TO_CSTR(SystemInfo);
+ ENUM_TO_CSTR(ThreadExList);
+ ENUM_TO_CSTR(Memory64List);
+ ENUM_TO_CSTR(CommentA);
+ ENUM_TO_CSTR(CommentW);
+ ENUM_TO_CSTR(HandleData);
+ ENUM_TO_CSTR(FunctionTable);
+ ENUM_TO_CSTR(UnloadedModuleList);
+ ENUM_TO_CSTR(MiscInfo);
+ ENUM_TO_CSTR(MemoryInfoList);
+ ENUM_TO_CSTR(ThreadInfoList);
+ ENUM_TO_CSTR(HandleOperationList);
+ ENUM_TO_CSTR(Token);
+ ENUM_TO_CSTR(JavascriptData);
+ ENUM_TO_CSTR(SystemMemoryInfo);
+ ENUM_TO_CSTR(ProcessVMCounters);
+ ENUM_TO_CSTR(LastReserved);
+ ENUM_TO_CSTR(BreakpadInfo);
+ ENUM_TO_CSTR(AssertionInfo);
+ ENUM_TO_CSTR(LinuxCPUInfo);
+ ENUM_TO_CSTR(LinuxProcStatus);
+ ENUM_TO_CSTR(LinuxLSBRelease);
+ ENUM_TO_CSTR(LinuxCMDLine);
+ ENUM_TO_CSTR(LinuxEnviron);
+ ENUM_TO_CSTR(LinuxAuxv);
+ ENUM_TO_CSTR(LinuxMaps);
+ ENUM_TO_CSTR(LinuxDSODebug);
+ ENUM_TO_CSTR(LinuxProcStat);
+ ENUM_TO_CSTR(LinuxProcUptime);
+ ENUM_TO_CSTR(LinuxProcFD);
+ ENUM_TO_CSTR(FacebookAppCustomData);
+ ENUM_TO_CSTR(FacebookBuildID);
+ ENUM_TO_CSTR(FacebookAppVersionName);
+ ENUM_TO_CSTR(FacebookJavaStack);
+ ENUM_TO_CSTR(FacebookDalvikInfo);
+ ENUM_TO_CSTR(FacebookUnwindSymbols);
+ ENUM_TO_CSTR(FacebookDumpErrorLog);
+ ENUM_TO_CSTR(FacebookAppStateLog);
+ ENUM_TO_CSTR(FacebookAbortReason);
+ ENUM_TO_CSTR(FacebookThreadName);
+ ENUM_TO_CSTR(FacebookLogcat);
+ }
+ return "unknown stream type";
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.h
new file mode 100644
index 000000000000..fce64f0ed5fc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.h
@@ -0,0 +1,115 @@
+//===-- MinidumpParser.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 liblldb_MinidumpParser_h_
+#define liblldb_MinidumpParser_h_
+
+#include "MinidumpTypes.h"
+
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/DataBuffer.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/UUID.h"
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Object/Minidump.h"
+
+// C includes
+
+// C++ includes
+#include <cstring>
+#include <unordered_map>
+
+namespace lldb_private {
+
+namespace minidump {
+
+// Describes a range of memory captured in the Minidump
+struct Range {
+ lldb::addr_t start; // virtual address of the beginning of the range
+ // range_ref - absolute pointer to the first byte of the range and size
+ llvm::ArrayRef<uint8_t> range_ref;
+
+ Range(lldb::addr_t start, llvm::ArrayRef<uint8_t> range_ref)
+ : start(start), range_ref(range_ref) {}
+
+ friend bool operator==(const Range &lhs, const Range &rhs) {
+ return lhs.start == rhs.start && lhs.range_ref == rhs.range_ref;
+ }
+};
+
+class MinidumpParser {
+public:
+ static llvm::Expected<MinidumpParser>
+ Create(const lldb::DataBufferSP &data_buf_sp);
+
+ llvm::ArrayRef<uint8_t> GetData();
+
+ llvm::ArrayRef<uint8_t> GetStream(StreamType stream_type);
+
+ UUID GetModuleUUID(const minidump::Module *module);
+
+ llvm::ArrayRef<minidump::Thread> GetThreads();
+
+ llvm::ArrayRef<uint8_t> GetThreadContext(const LocationDescriptor &location);
+
+ llvm::ArrayRef<uint8_t> GetThreadContext(const minidump::Thread &td);
+
+ llvm::ArrayRef<uint8_t> GetThreadContextWow64(const minidump::Thread &td);
+
+ ArchSpec GetArchitecture();
+
+ const MinidumpMiscInfo *GetMiscInfo();
+
+ llvm::Optional<LinuxProcStatus> GetLinuxProcStatus();
+
+ llvm::Optional<lldb::pid_t> GetPid();
+
+ llvm::ArrayRef<minidump::Module> GetModuleList();
+
+ // There are cases in which there is more than one record in the ModuleList
+ // for the same module name.(e.g. when the binary has non contiguous segments)
+ // So this function returns a filtered module list - if it finds records that
+ // have the same name, it keeps the copy with the lowest load address.
+ std::vector<const minidump::Module *> GetFilteredModuleList();
+
+ const MinidumpExceptionStream *GetExceptionStream();
+
+ llvm::Optional<Range> FindMemoryRange(lldb::addr_t addr);
+
+ llvm::ArrayRef<uint8_t> GetMemory(lldb::addr_t addr, size_t size);
+
+ MemoryRegionInfo GetMemoryRegionInfo(lldb::addr_t load_addr);
+
+ const MemoryRegionInfos &GetMemoryRegions();
+
+ static llvm::StringRef GetStreamTypeAsString(StreamType stream_type);
+
+ llvm::object::MinidumpFile &GetMinidumpFile() { return *m_file; }
+
+private:
+ MinidumpParser(lldb::DataBufferSP data_sp,
+ std::unique_ptr<llvm::object::MinidumpFile> file);
+
+ MemoryRegionInfo FindMemoryRegion(lldb::addr_t load_addr) const;
+
+private:
+ lldb::DataBufferSP m_data_sp;
+ std::unique_ptr<llvm::object::MinidumpFile> m_file;
+ ArchSpec m_arch;
+ MemoryRegionInfos m_regions;
+ bool m_parsed_regions = false;
+};
+
+} // end namespace minidump
+} // end namespace lldb_private
+#endif // liblldb_MinidumpParser_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp
new file mode 100644
index 000000000000..d7fc6e43d090
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp
@@ -0,0 +1,115 @@
+//===-- MinidumpTypes.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 "MinidumpTypes.h"
+
+// C includes
+// C++ includes
+
+using namespace lldb_private;
+using namespace minidump;
+
+// MinidumpMiscInfo
+const MinidumpMiscInfo *MinidumpMiscInfo::Parse(llvm::ArrayRef<uint8_t> &data) {
+ const MinidumpMiscInfo *misc_info;
+ Status error = consumeObject(data, misc_info);
+ if (error.Fail())
+ return nullptr;
+
+ return misc_info;
+}
+
+llvm::Optional<lldb::pid_t> MinidumpMiscInfo::GetPid() const {
+ uint32_t pid_flag = static_cast<uint32_t>(MinidumpMiscInfoFlags::ProcessID);
+ if (flags1 & pid_flag)
+ return llvm::Optional<lldb::pid_t>(process_id);
+
+ return llvm::None;
+}
+
+// Linux Proc Status
+// it's stored as an ascii string in the file
+llvm::Optional<LinuxProcStatus>
+LinuxProcStatus::Parse(llvm::ArrayRef<uint8_t> &data) {
+ LinuxProcStatus result;
+ result.proc_status =
+ llvm::StringRef(reinterpret_cast<const char *>(data.data()), data.size());
+ data = data.drop_front(data.size());
+
+ llvm::SmallVector<llvm::StringRef, 0> lines;
+ result.proc_status.split(lines, '\n', 42);
+ // /proc/$pid/status has 41 lines, but why not use 42?
+ for (auto line : lines) {
+ if (line.consume_front("Pid:")) {
+ line = line.trim();
+ if (!line.getAsInteger(10, result.pid))
+ return result;
+ }
+ }
+
+ return llvm::None;
+}
+
+lldb::pid_t LinuxProcStatus::GetPid() const { return pid; }
+
+// Exception stuff
+const MinidumpExceptionStream *
+MinidumpExceptionStream::Parse(llvm::ArrayRef<uint8_t> &data) {
+ const MinidumpExceptionStream *exception_stream = nullptr;
+ Status error = consumeObject(data, exception_stream);
+ if (error.Fail())
+ return nullptr;
+
+ return exception_stream;
+}
+
+std::pair<llvm::ArrayRef<MinidumpMemoryDescriptor64>, uint64_t>
+MinidumpMemoryDescriptor64::ParseMemory64List(llvm::ArrayRef<uint8_t> &data) {
+ const llvm::support::ulittle64_t *mem_ranges_count;
+ Status error = consumeObject(data, mem_ranges_count);
+ if (error.Fail() ||
+ *mem_ranges_count * sizeof(MinidumpMemoryDescriptor64) > data.size())
+ return {};
+
+ const llvm::support::ulittle64_t *base_rva;
+ error = consumeObject(data, base_rva);
+ if (error.Fail())
+ return {};
+
+ return std::make_pair(
+ llvm::makeArrayRef(
+ reinterpret_cast<const MinidumpMemoryDescriptor64 *>(data.data()),
+ *mem_ranges_count),
+ *base_rva);
+}
+
+std::vector<const MinidumpMemoryInfo *>
+MinidumpMemoryInfo::ParseMemoryInfoList(llvm::ArrayRef<uint8_t> &data) {
+ const MinidumpMemoryInfoListHeader *header;
+ Status error = consumeObject(data, header);
+ if (error.Fail() ||
+ header->size_of_header < sizeof(MinidumpMemoryInfoListHeader) ||
+ header->size_of_entry < sizeof(MinidumpMemoryInfo))
+ return {};
+
+ data = data.drop_front(header->size_of_header -
+ sizeof(MinidumpMemoryInfoListHeader));
+
+ if (header->size_of_entry * header->num_of_entries > data.size())
+ return {};
+
+ std::vector<const MinidumpMemoryInfo *> result;
+ result.reserve(header->num_of_entries);
+
+ for (uint64_t i = 0; i < header->num_of_entries; ++i) {
+ result.push_back(reinterpret_cast<const MinidumpMemoryInfo *>(
+ data.data() + i * header->size_of_entry));
+ }
+
+ return result;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.h
new file mode 100644
index 000000000000..b4878e82de5d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.h
@@ -0,0 +1,236 @@
+//===-- MinidumpTypes.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 liblldb_MinidumpTypes_h_
+#define liblldb_MinidumpTypes_h_
+
+
+#include "lldb/Utility/Status.h"
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/BitmaskEnum.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/Minidump.h"
+#include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/Endian.h"
+
+// C includes
+// C++ includes
+
+// Reference:
+// https://msdn.microsoft.com/en-us/library/windows/desktop/ms679293(v=vs.85).aspx
+// https://chromium.googlesource.com/breakpad/breakpad/
+
+namespace lldb_private {
+
+namespace minidump {
+
+using namespace llvm::minidump;
+
+LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
+
+enum class CvSignature : uint32_t {
+ Pdb70 = 0x53445352, // RSDS
+ ElfBuildId = 0x4270454c, // BpEL (Breakpad/Crashpad minidumps)
+};
+
+// Reference:
+// https://crashpad.chromium.org/doxygen/structcrashpad_1_1CodeViewRecordPDB70.html
+struct CvRecordPdb70 {
+ struct {
+ llvm::support::ulittle32_t Data1;
+ llvm::support::ulittle16_t Data2;
+ llvm::support::ulittle16_t Data3;
+ uint8_t Data4[8];
+ } Uuid;
+ llvm::support::ulittle32_t Age;
+ // char PDBFileName[];
+};
+static_assert(sizeof(CvRecordPdb70) == 20,
+ "sizeof CvRecordPdb70 is not correct!");
+
+enum class MinidumpMiscInfoFlags : uint32_t {
+ ProcessID = (1 << 0),
+ ProcessTimes = (1 << 1),
+ LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ ProcessTimes)
+};
+
+template <typename T>
+Status consumeObject(llvm::ArrayRef<uint8_t> &Buffer, const T *&Object) {
+ Status error;
+ if (Buffer.size() < sizeof(T)) {
+ error.SetErrorString("Insufficient buffer!");
+ return error;
+ }
+
+ Object = reinterpret_cast<const T *>(Buffer.data());
+ Buffer = Buffer.drop_front(sizeof(T));
+ return error;
+}
+
+struct MinidumpMemoryDescriptor64 {
+ llvm::support::ulittle64_t start_of_memory_range;
+ llvm::support::ulittle64_t data_size;
+
+ static std::pair<llvm::ArrayRef<MinidumpMemoryDescriptor64>, uint64_t>
+ ParseMemory64List(llvm::ArrayRef<uint8_t> &data);
+};
+static_assert(sizeof(MinidumpMemoryDescriptor64) == 16,
+ "sizeof MinidumpMemoryDescriptor64 is not correct!");
+
+// Reference:
+// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680385(v=vs.85).aspx
+struct MinidumpMemoryInfoListHeader {
+ llvm::support::ulittle32_t size_of_header;
+ llvm::support::ulittle32_t size_of_entry;
+ llvm::support::ulittle64_t num_of_entries;
+};
+static_assert(sizeof(MinidumpMemoryInfoListHeader) == 16,
+ "sizeof MinidumpMemoryInfoListHeader is not correct!");
+
+enum class MinidumpMemoryInfoState : uint32_t {
+ MemCommit = 0x1000,
+ MemFree = 0x10000,
+ MemReserve = 0x2000,
+ LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ MemFree)
+};
+
+enum class MinidumpMemoryInfoType : uint32_t {
+ MemImage = 0x1000000,
+ MemMapped = 0x40000,
+ MemPrivate = 0x20000,
+ LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ MemImage)
+};
+
+// Reference:
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa366786(v=vs.85).aspx
+enum class MinidumpMemoryProtectionContants : uint32_t {
+ PageExecute = 0x10,
+ PageExecuteRead = 0x20,
+ PageExecuteReadWrite = 0x40,
+ PageExecuteWriteCopy = 0x80,
+ PageNoAccess = 0x01,
+ PageReadOnly = 0x02,
+ PageReadWrite = 0x04,
+ PageWriteCopy = 0x08,
+ PageTargetsInvalid = 0x40000000,
+ PageTargetsNoUpdate = 0x40000000,
+
+ PageWritable = PageExecuteReadWrite | PageExecuteWriteCopy | PageReadWrite |
+ PageWriteCopy,
+ PageExecutable = PageExecute | PageExecuteRead | PageExecuteReadWrite |
+ PageExecuteWriteCopy,
+ LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ PageTargetsInvalid)
+};
+
+// Reference:
+// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680386(v=vs.85).aspx
+struct MinidumpMemoryInfo {
+ llvm::support::ulittle64_t base_address;
+ llvm::support::ulittle64_t allocation_base;
+ llvm::support::ulittle32_t allocation_protect;
+ llvm::support::ulittle32_t alignment1;
+ llvm::support::ulittle64_t region_size;
+ llvm::support::ulittle32_t state;
+ llvm::support::ulittle32_t protect;
+ llvm::support::ulittle32_t type;
+ llvm::support::ulittle32_t alignment2;
+
+ static std::vector<const MinidumpMemoryInfo *>
+ ParseMemoryInfoList(llvm::ArrayRef<uint8_t> &data);
+
+ bool isReadable() const {
+ const auto mask = MinidumpMemoryProtectionContants::PageNoAccess;
+ return (static_cast<uint32_t>(mask) & protect) == 0;
+ }
+
+ bool isWritable() const {
+ const auto mask = MinidumpMemoryProtectionContants::PageWritable;
+ return (static_cast<uint32_t>(mask) & protect) != 0;
+ }
+
+ bool isExecutable() const {
+ const auto mask = MinidumpMemoryProtectionContants::PageExecutable;
+ return (static_cast<uint32_t>(mask) & protect) != 0;
+ }
+
+ bool isMapped() const {
+ return state != static_cast<uint32_t>(MinidumpMemoryInfoState::MemFree);
+ }
+};
+
+static_assert(sizeof(MinidumpMemoryInfo) == 48,
+ "sizeof MinidumpMemoryInfo is not correct!");
+
+// TODO misc2, misc3 ?
+// Reference:
+// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680389(v=vs.85).aspx
+struct MinidumpMiscInfo {
+ llvm::support::ulittle32_t size;
+ // flags1 represents what info in the struct is valid
+ llvm::support::ulittle32_t flags1;
+ llvm::support::ulittle32_t process_id;
+ llvm::support::ulittle32_t process_create_time;
+ llvm::support::ulittle32_t process_user_time;
+ llvm::support::ulittle32_t process_kernel_time;
+
+ static const MinidumpMiscInfo *Parse(llvm::ArrayRef<uint8_t> &data);
+
+ llvm::Optional<lldb::pid_t> GetPid() const;
+};
+static_assert(sizeof(MinidumpMiscInfo) == 24,
+ "sizeof MinidumpMiscInfo is not correct!");
+
+// The /proc/pid/status is saved as an ascii string in the file
+class LinuxProcStatus {
+public:
+ llvm::StringRef proc_status;
+ lldb::pid_t pid;
+
+ static llvm::Optional<LinuxProcStatus> Parse(llvm::ArrayRef<uint8_t> &data);
+
+ lldb::pid_t GetPid() const;
+
+private:
+ LinuxProcStatus() = default;
+};
+
+// Exception stuff
+struct MinidumpException {
+ enum : unsigned {
+ ExceptonInfoMaxParams = 15,
+ DumpRequested = 0xFFFFFFFF,
+ };
+
+ llvm::support::ulittle32_t exception_code;
+ llvm::support::ulittle32_t exception_flags;
+ llvm::support::ulittle64_t exception_record;
+ llvm::support::ulittle64_t exception_address;
+ llvm::support::ulittle32_t number_parameters;
+ llvm::support::ulittle32_t unused_alignment;
+ llvm::support::ulittle64_t exception_information[ExceptonInfoMaxParams];
+};
+static_assert(sizeof(MinidumpException) == 152,
+ "sizeof MinidumpException is not correct!");
+
+struct MinidumpExceptionStream {
+ llvm::support::ulittle32_t thread_id;
+ llvm::support::ulittle32_t alignment;
+ MinidumpException exception_record;
+ LocationDescriptor thread_context;
+
+ static const MinidumpExceptionStream *Parse(llvm::ArrayRef<uint8_t> &data);
+};
+static_assert(sizeof(MinidumpExceptionStream) == 168,
+ "sizeof MinidumpExceptionStream is not correct!");
+
+} // namespace minidump
+} // namespace lldb_private
+#endif // liblldb_MinidumpTypes_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/NtStructures.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/NtStructures.h
new file mode 100644
index 000000000000..fdb0cfb7981e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/NtStructures.h
@@ -0,0 +1,36 @@
+//===-- NtStructures.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 liblldb_Plugins_Process_Minidump_NtStructures_h_
+#define liblldb_Plugins_Process_Minidump_NtStructures_h_
+
+#include "llvm/Support/Endian.h"
+
+namespace lldb_private {
+
+namespace minidump {
+
+// This describes the layout of a TEB (Thread Environment Block) for a 64-bit
+// process. It's adapted from the 32-bit TEB in winternl.h. Currently, we care
+// only about the position of the tls_slots.
+struct TEB64 {
+ llvm::support::ulittle64_t reserved1[12];
+ llvm::support::ulittle64_t process_environment_block;
+ llvm::support::ulittle64_t reserved2[399];
+ uint8_t reserved3[1952];
+ llvm::support::ulittle64_t tls_slots[64];
+ uint8_t reserved4[8];
+ llvm::support::ulittle64_t reserved5[26];
+ llvm::support::ulittle64_t reserved_for_ole; // Windows 2000 only
+ llvm::support::ulittle64_t reserved6[4];
+ llvm::support::ulittle64_t tls_expansion_slots;
+};
+
+#endif // liblldb_Plugins_Process_Minidump_NtStructures_h_
+} // namespace minidump
+} // namespace lldb_private
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
new file mode 100644
index 000000000000..a7fc42cad16c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
@@ -0,0 +1,822 @@
+//===-- ProcessMinidump.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 "ProcessMinidump.h"
+
+#include "ThreadMinidump.h"
+
+#include "lldb/Core/DumpDataExtractor.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionGroupBoolean.h"
+#include "lldb/Target/JITLoaderList.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Threading.h"
+
+#include "Plugins/Process/Utility/StopInfoMachException.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace minidump;
+
+namespace {
+
+/// A minimal ObjectFile implementation providing a dummy object file for the
+/// cases when the real module binary is not available. This allows the module
+/// to show up in "image list" and symbols to be added to it.
+class PlaceholderObjectFile : public ObjectFile {
+public:
+ PlaceholderObjectFile(const lldb::ModuleSP &module_sp,
+ const ModuleSpec &module_spec, lldb::offset_t base,
+ lldb::offset_t size)
+ : ObjectFile(module_sp, &module_spec.GetFileSpec(), /*file_offset*/ 0,
+ /*length*/ 0, /*data_sp*/ nullptr, /*data_offset*/ 0),
+ m_arch(module_spec.GetArchitecture()), m_uuid(module_spec.GetUUID()),
+ m_base(base), m_size(size) {
+ m_symtab_up = llvm::make_unique<Symtab>(this);
+ }
+
+ ConstString GetPluginName() override { return ConstString("placeholder"); }
+ uint32_t GetPluginVersion() override { return 1; }
+ bool ParseHeader() override { return true; }
+ Type CalculateType() override { return eTypeUnknown; }
+ Strata CalculateStrata() override { return eStrataUnknown; }
+ uint32_t GetDependentModules(FileSpecList &file_list) override { return 0; }
+ bool IsExecutable() const override { return false; }
+ ArchSpec GetArchitecture() override { return m_arch; }
+ UUID GetUUID() override { return m_uuid; }
+ Symtab *GetSymtab() override { return m_symtab_up.get(); }
+ bool IsStripped() override { return true; }
+ ByteOrder GetByteOrder() const override { return m_arch.GetByteOrder(); }
+
+ uint32_t GetAddressByteSize() const override {
+ return m_arch.GetAddressByteSize();
+ }
+
+ Address GetBaseAddress() override {
+ return Address(m_sections_up->GetSectionAtIndex(0), 0);
+ }
+
+ void CreateSections(SectionList &unified_section_list) override {
+ m_sections_up = llvm::make_unique<SectionList>();
+ auto section_sp = std::make_shared<Section>(
+ GetModule(), this, /*sect_id*/ 0, ConstString(".module_image"),
+ eSectionTypeOther, m_base, m_size, /*file_offset*/ 0, /*file_size*/ 0,
+ /*log2align*/ 0, /*flags*/ 0);
+ section_sp->SetPermissions(ePermissionsReadable | ePermissionsExecutable);
+ m_sections_up->AddSection(section_sp);
+ unified_section_list.AddSection(std::move(section_sp));
+ }
+
+ bool SetLoadAddress(Target &target, addr_t value,
+ bool value_is_offset) override {
+ assert(!value_is_offset);
+ assert(value == m_base);
+
+ // Create sections if they haven't been created already.
+ GetModule()->GetSectionList();
+ assert(m_sections_up->GetNumSections(0) == 1);
+
+ target.GetSectionLoadList().SetSectionLoadAddress(
+ m_sections_up->GetSectionAtIndex(0), m_base);
+ return true;
+ }
+
+ void Dump(Stream *s) override {
+ s->Format("Placeholder object file for {0} loaded at [{1:x}-{2:x})\n",
+ GetFileSpec(), m_base, m_base + m_size);
+ }
+
+private:
+ ArchSpec m_arch;
+ UUID m_uuid;
+ lldb::offset_t m_base;
+ lldb::offset_t m_size;
+};
+} // namespace
+
+ConstString ProcessMinidump::GetPluginNameStatic() {
+ static ConstString g_name("minidump");
+ return g_name;
+}
+
+const char *ProcessMinidump::GetPluginDescriptionStatic() {
+ return "Minidump plug-in.";
+}
+
+lldb::ProcessSP ProcessMinidump::CreateInstance(lldb::TargetSP target_sp,
+ lldb::ListenerSP listener_sp,
+ const FileSpec *crash_file) {
+ if (!crash_file)
+ return nullptr;
+
+ lldb::ProcessSP process_sp;
+ // Read enough data for the Minidump header
+ constexpr size_t header_size = sizeof(Header);
+ auto DataPtr = FileSystem::Instance().CreateDataBuffer(crash_file->GetPath(),
+ header_size, 0);
+ if (!DataPtr)
+ return nullptr;
+
+ lldbassert(DataPtr->GetByteSize() == header_size);
+ if (identify_magic(toStringRef(DataPtr->GetData())) != llvm::file_magic::minidump)
+ return nullptr;
+
+ auto AllData =
+ FileSystem::Instance().CreateDataBuffer(crash_file->GetPath(), -1, 0);
+ if (!AllData)
+ return nullptr;
+
+ return std::make_shared<ProcessMinidump>(target_sp, listener_sp, *crash_file,
+ std::move(AllData));
+}
+
+bool ProcessMinidump::CanDebug(lldb::TargetSP target_sp,
+ bool plugin_specified_by_name) {
+ return true;
+}
+
+ProcessMinidump::ProcessMinidump(lldb::TargetSP target_sp,
+ lldb::ListenerSP listener_sp,
+ const FileSpec &core_file,
+ DataBufferSP core_data)
+ : Process(target_sp, listener_sp), m_core_file(core_file),
+ m_core_data(std::move(core_data)), m_is_wow64(false) {}
+
+ProcessMinidump::~ProcessMinidump() {
+ Clear();
+ // We need to call finalize on the process before destroying ourselves to
+ // make sure all of the broadcaster cleanup goes as planned. If we destruct
+ // this class, then Process::~Process() might have problems trying to fully
+ // destroy the broadcaster.
+ Finalize();
+}
+
+void ProcessMinidump::Initialize() {
+ static llvm::once_flag g_once_flag;
+
+ llvm::call_once(g_once_flag, []() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(),
+ ProcessMinidump::CreateInstance);
+ });
+}
+
+void ProcessMinidump::Terminate() {
+ PluginManager::UnregisterPlugin(ProcessMinidump::CreateInstance);
+}
+
+Status ProcessMinidump::DoLoadCore() {
+ auto expected_parser = MinidumpParser::Create(m_core_data);
+ if (!expected_parser)
+ return Status(expected_parser.takeError());
+ m_minidump_parser = std::move(*expected_parser);
+
+ Status error;
+
+ // Do we support the minidump's architecture?
+ ArchSpec arch = GetArchitecture();
+ switch (arch.GetMachine()) {
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ case llvm::Triple::arm:
+ case llvm::Triple::aarch64:
+ // Any supported architectures must be listed here and also supported in
+ // ThreadMinidump::CreateRegisterContextForFrame().
+ break;
+ default:
+ error.SetErrorStringWithFormat("unsupported minidump architecture: %s",
+ arch.GetArchitectureName());
+ return error;
+ }
+ GetTarget().SetArchitecture(arch, true /*set_platform*/);
+
+ m_thread_list = m_minidump_parser->GetThreads();
+ m_active_exception = m_minidump_parser->GetExceptionStream();
+ ReadModuleList();
+
+ llvm::Optional<lldb::pid_t> pid = m_minidump_parser->GetPid();
+ if (!pid) {
+ error.SetErrorString("failed to parse PID");
+ return error;
+ }
+ SetID(pid.getValue());
+
+ return error;
+}
+
+ConstString ProcessMinidump::GetPluginName() { return GetPluginNameStatic(); }
+
+uint32_t ProcessMinidump::GetPluginVersion() { return 1; }
+
+Status ProcessMinidump::DoDestroy() { return Status(); }
+
+void ProcessMinidump::RefreshStateAfterStop() {
+ if (!m_active_exception)
+ return;
+
+ if (m_active_exception->exception_record.exception_code ==
+ MinidumpException::DumpRequested) {
+ return;
+ }
+
+ lldb::StopInfoSP stop_info;
+ lldb::ThreadSP stop_thread;
+
+ Process::m_thread_list.SetSelectedThreadByID(m_active_exception->thread_id);
+ stop_thread = Process::m_thread_list.GetSelectedThread();
+ ArchSpec arch = GetArchitecture();
+
+ if (arch.GetTriple().getOS() == llvm::Triple::Linux) {
+ stop_info = StopInfo::CreateStopReasonWithSignal(
+ *stop_thread, m_active_exception->exception_record.exception_code);
+ } else if (arch.GetTriple().getVendor() == llvm::Triple::Apple) {
+ stop_info = StopInfoMachException::CreateStopReasonWithMachException(
+ *stop_thread, m_active_exception->exception_record.exception_code, 2,
+ m_active_exception->exception_record.exception_flags,
+ m_active_exception->exception_record.exception_address, 0);
+ } else {
+ std::string desc;
+ llvm::raw_string_ostream desc_stream(desc);
+ desc_stream << "Exception "
+ << llvm::format_hex(
+ m_active_exception->exception_record.exception_code, 8)
+ << " encountered at address "
+ << llvm::format_hex(
+ m_active_exception->exception_record.exception_address,
+ 8);
+ stop_info = StopInfo::CreateStopReasonWithException(
+ *stop_thread, desc_stream.str().c_str());
+ }
+
+ stop_thread->SetStopInfo(stop_info);
+}
+
+bool ProcessMinidump::IsAlive() { return true; }
+
+bool ProcessMinidump::WarnBeforeDetach() const { return false; }
+
+size_t ProcessMinidump::ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ Status &error) {
+ // Don't allow the caching that lldb_private::Process::ReadMemory does since
+ // we have it all cached in our dump file anyway.
+ return DoReadMemory(addr, buf, size, error);
+}
+
+size_t ProcessMinidump::DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ Status &error) {
+
+ llvm::ArrayRef<uint8_t> mem = m_minidump_parser->GetMemory(addr, size);
+ if (mem.empty()) {
+ error.SetErrorString("could not parse memory info");
+ return 0;
+ }
+
+ std::memcpy(buf, mem.data(), mem.size());
+ return mem.size();
+}
+
+ArchSpec ProcessMinidump::GetArchitecture() {
+ if (!m_is_wow64) {
+ return m_minidump_parser->GetArchitecture();
+ }
+
+ llvm::Triple triple;
+ triple.setVendor(llvm::Triple::VendorType::UnknownVendor);
+ triple.setArch(llvm::Triple::ArchType::x86);
+ triple.setOS(llvm::Triple::OSType::Win32);
+ return ArchSpec(triple);
+}
+
+Status ProcessMinidump::GetMemoryRegionInfo(lldb::addr_t load_addr,
+ MemoryRegionInfo &range_info) {
+ range_info = m_minidump_parser->GetMemoryRegionInfo(load_addr);
+ return Status();
+}
+
+Status ProcessMinidump::GetMemoryRegions(
+ lldb_private::MemoryRegionInfos &region_list) {
+ region_list = m_minidump_parser->GetMemoryRegions();
+ return Status();
+}
+
+void ProcessMinidump::Clear() { Process::m_thread_list.Clear(); }
+
+bool ProcessMinidump::UpdateThreadList(ThreadList &old_thread_list,
+ ThreadList &new_thread_list) {
+ for (const minidump::Thread &thread : m_thread_list) {
+ LocationDescriptor context_location = thread.Context;
+
+ // If the minidump contains an exception context, use it
+ if (m_active_exception != nullptr &&
+ m_active_exception->thread_id == thread.ThreadId) {
+ context_location = m_active_exception->thread_context;
+ }
+
+ llvm::ArrayRef<uint8_t> context;
+ if (!m_is_wow64)
+ context = m_minidump_parser->GetThreadContext(context_location);
+ else
+ context = m_minidump_parser->GetThreadContextWow64(thread);
+
+ lldb::ThreadSP thread_sp(new ThreadMinidump(*this, thread, context));
+ new_thread_list.AddThread(thread_sp);
+ }
+ return new_thread_list.GetSize(false) > 0;
+}
+
+void ProcessMinidump::ReadModuleList() {
+ std::vector<const minidump::Module *> filtered_modules =
+ m_minidump_parser->GetFilteredModuleList();
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES));
+
+ for (auto module : filtered_modules) {
+ std::string name = cantFail(m_minidump_parser->GetMinidumpFile().getString(
+ module->ModuleNameRVA));
+ LLDB_LOG(log, "found module: name: {0} {1:x10}-{2:x10} size: {3}", name,
+ module->BaseOfImage, module->BaseOfImage + module->SizeOfImage,
+ module->SizeOfImage);
+
+ // check if the process is wow64 - a 32 bit windows process running on a
+ // 64 bit windows
+ if (llvm::StringRef(name).endswith_lower("wow64.dll")) {
+ m_is_wow64 = true;
+ }
+
+ const auto uuid = m_minidump_parser->GetModuleUUID(module);
+ auto file_spec = FileSpec(name, GetArchitecture().GetTriple());
+ ModuleSpec module_spec(file_spec, uuid);
+ module_spec.GetArchitecture() = GetArchitecture();
+ Status error;
+ // Try and find a module with a full UUID that matches. This function will
+ // add the module to the target if it finds one.
+ lldb::ModuleSP module_sp = GetTarget().GetOrCreateModule(module_spec,
+ true /* notify */, &error);
+ if (!module_sp) {
+ // Try and find a module without specifying the UUID and only looking for
+ // the file given a basename. We then will look for a partial UUID match
+ // if we find any matches. This function will add the module to the
+ // target if it finds one, so we need to remove the module from the target
+ // if the UUID doesn't match during our manual UUID verification. This
+ // allows the "target.exec-search-paths" setting to specify one or more
+ // directories that contain executables that can be searched for matches.
+ ModuleSpec basename_module_spec(module_spec);
+ basename_module_spec.GetUUID().Clear();
+ basename_module_spec.GetFileSpec().GetDirectory().Clear();
+ module_sp = GetTarget().GetOrCreateModule(basename_module_spec,
+ true /* notify */, &error);
+ if (module_sp) {
+ // We consider the module to be a match if the minidump UUID is a
+ // prefix of the actual UUID, or if either of the UUIDs are empty.
+ const auto dmp_bytes = uuid.GetBytes();
+ const auto mod_bytes = module_sp->GetUUID().GetBytes();
+ const bool match = dmp_bytes.empty() || mod_bytes.empty() ||
+ mod_bytes.take_front(dmp_bytes.size()) == dmp_bytes;
+ if (!match) {
+ GetTarget().GetImages().Remove(module_sp);
+ module_sp.reset();
+ }
+ }
+ }
+ if (!module_sp) {
+ // We failed to locate a matching local object file. Fortunately, the
+ // minidump format encodes enough information about each module's memory
+ // range to allow us to create placeholder modules.
+ //
+ // This enables most LLDB functionality involving address-to-module
+ // translations (ex. identifing the module for a stack frame PC) and
+ // modules/sections commands (ex. target modules list, ...)
+ LLDB_LOG(log,
+ "Unable to locate the matching object file, creating a "
+ "placeholder module for: {0}",
+ name);
+
+ module_sp = Module::CreateModuleFromObjectFile<PlaceholderObjectFile>(
+ module_spec, module->BaseOfImage, module->SizeOfImage);
+ GetTarget().GetImages().Append(module_sp, true /* notify */);
+ }
+
+ bool load_addr_changed = false;
+ module_sp->SetLoadAddress(GetTarget(), module->BaseOfImage, false,
+ load_addr_changed);
+ }
+}
+
+bool ProcessMinidump::GetProcessInfo(ProcessInstanceInfo &info) {
+ info.Clear();
+ info.SetProcessID(GetID());
+ info.SetArchitecture(GetArchitecture());
+ lldb::ModuleSP module_sp = GetTarget().GetExecutableModule();
+ if (module_sp) {
+ const bool add_exe_file_as_first_arg = false;
+ info.SetExecutableFile(GetTarget().GetExecutableModule()->GetFileSpec(),
+ add_exe_file_as_first_arg);
+ }
+ return true;
+}
+
+// For minidumps there's no runtime generated code so we don't need JITLoader(s)
+// Avoiding them will also speed up minidump loading since JITLoaders normally
+// try to set up symbolic breakpoints, which in turn may force loading more
+// debug information than needed.
+JITLoaderList &ProcessMinidump::GetJITLoaders() {
+ if (!m_jit_loaders_up) {
+ m_jit_loaders_up = llvm::make_unique<JITLoaderList>();
+ }
+ return *m_jit_loaders_up;
+}
+
+#define INIT_BOOL(VAR, LONG, SHORT, DESC) \
+ VAR(LLDB_OPT_SET_1, false, LONG, SHORT, DESC, false, true)
+#define APPEND_OPT(VAR) \
+ m_option_group.Append(&VAR, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1)
+
+class CommandObjectProcessMinidumpDump : public CommandObjectParsed {
+private:
+ OptionGroupOptions m_option_group;
+ OptionGroupBoolean m_dump_all;
+ OptionGroupBoolean m_dump_directory;
+ OptionGroupBoolean m_dump_linux_cpuinfo;
+ OptionGroupBoolean m_dump_linux_proc_status;
+ OptionGroupBoolean m_dump_linux_lsb_release;
+ OptionGroupBoolean m_dump_linux_cmdline;
+ OptionGroupBoolean m_dump_linux_environ;
+ OptionGroupBoolean m_dump_linux_auxv;
+ OptionGroupBoolean m_dump_linux_maps;
+ OptionGroupBoolean m_dump_linux_proc_stat;
+ OptionGroupBoolean m_dump_linux_proc_uptime;
+ OptionGroupBoolean m_dump_linux_proc_fd;
+ OptionGroupBoolean m_dump_linux_all;
+ OptionGroupBoolean m_fb_app_data;
+ OptionGroupBoolean m_fb_build_id;
+ OptionGroupBoolean m_fb_version;
+ OptionGroupBoolean m_fb_java_stack;
+ OptionGroupBoolean m_fb_dalvik;
+ OptionGroupBoolean m_fb_unwind;
+ OptionGroupBoolean m_fb_error_log;
+ OptionGroupBoolean m_fb_app_state;
+ OptionGroupBoolean m_fb_abort;
+ OptionGroupBoolean m_fb_thread;
+ OptionGroupBoolean m_fb_logcat;
+ OptionGroupBoolean m_fb_all;
+
+ void SetDefaultOptionsIfNoneAreSet() {
+ if (m_dump_all.GetOptionValue().GetCurrentValue() ||
+ m_dump_linux_all.GetOptionValue().GetCurrentValue() ||
+ m_fb_all.GetOptionValue().GetCurrentValue() ||
+ m_dump_directory.GetOptionValue().GetCurrentValue() ||
+ m_dump_linux_cpuinfo.GetOptionValue().GetCurrentValue() ||
+ m_dump_linux_proc_status.GetOptionValue().GetCurrentValue() ||
+ m_dump_linux_lsb_release.GetOptionValue().GetCurrentValue() ||
+ m_dump_linux_cmdline.GetOptionValue().GetCurrentValue() ||
+ m_dump_linux_environ.GetOptionValue().GetCurrentValue() ||
+ m_dump_linux_auxv.GetOptionValue().GetCurrentValue() ||
+ m_dump_linux_maps.GetOptionValue().GetCurrentValue() ||
+ m_dump_linux_proc_stat.GetOptionValue().GetCurrentValue() ||
+ m_dump_linux_proc_uptime.GetOptionValue().GetCurrentValue() ||
+ m_dump_linux_proc_fd.GetOptionValue().GetCurrentValue() ||
+ m_fb_app_data.GetOptionValue().GetCurrentValue() ||
+ m_fb_build_id.GetOptionValue().GetCurrentValue() ||
+ m_fb_version.GetOptionValue().GetCurrentValue() ||
+ m_fb_java_stack.GetOptionValue().GetCurrentValue() ||
+ m_fb_dalvik.GetOptionValue().GetCurrentValue() ||
+ m_fb_unwind.GetOptionValue().GetCurrentValue() ||
+ m_fb_error_log.GetOptionValue().GetCurrentValue() ||
+ m_fb_app_state.GetOptionValue().GetCurrentValue() ||
+ m_fb_abort.GetOptionValue().GetCurrentValue() ||
+ m_fb_thread.GetOptionValue().GetCurrentValue() ||
+ m_fb_logcat.GetOptionValue().GetCurrentValue())
+ return;
+ // If no options were set, then dump everything
+ m_dump_all.GetOptionValue().SetCurrentValue(true);
+ }
+ bool DumpAll() const {
+ return m_dump_all.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpDirectory() const {
+ return DumpAll() ||
+ m_dump_directory.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpLinux() const {
+ return DumpAll() || m_dump_linux_all.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpLinuxCPUInfo() const {
+ return DumpLinux() ||
+ m_dump_linux_cpuinfo.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpLinuxProcStatus() const {
+ return DumpLinux() ||
+ m_dump_linux_proc_status.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpLinuxProcStat() const {
+ return DumpLinux() ||
+ m_dump_linux_proc_stat.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpLinuxLSBRelease() const {
+ return DumpLinux() ||
+ m_dump_linux_lsb_release.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpLinuxCMDLine() const {
+ return DumpLinux() ||
+ m_dump_linux_cmdline.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpLinuxEnviron() const {
+ return DumpLinux() ||
+ m_dump_linux_environ.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpLinuxAuxv() const {
+ return DumpLinux() ||
+ m_dump_linux_auxv.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpLinuxMaps() const {
+ return DumpLinux() ||
+ m_dump_linux_maps.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpLinuxProcUptime() const {
+ return DumpLinux() ||
+ m_dump_linux_proc_uptime.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpLinuxProcFD() const {
+ return DumpLinux() ||
+ m_dump_linux_proc_fd.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpFacebook() const {
+ return DumpAll() || m_fb_all.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpFacebookAppData() const {
+ return DumpFacebook() || m_fb_app_data.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpFacebookBuildID() const {
+ return DumpFacebook() || m_fb_build_id.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpFacebookVersionName() const {
+ return DumpFacebook() || m_fb_version.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpFacebookJavaStack() const {
+ return DumpFacebook() || m_fb_java_stack.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpFacebookDalvikInfo() const {
+ return DumpFacebook() || m_fb_dalvik.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpFacebookUnwindSymbols() const {
+ return DumpFacebook() || m_fb_unwind.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpFacebookErrorLog() const {
+ return DumpFacebook() || m_fb_error_log.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpFacebookAppStateLog() const {
+ return DumpFacebook() || m_fb_app_state.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpFacebookAbortReason() const {
+ return DumpFacebook() || m_fb_abort.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpFacebookThreadName() const {
+ return DumpFacebook() || m_fb_thread.GetOptionValue().GetCurrentValue();
+ }
+ bool DumpFacebookLogcat() const {
+ return DumpFacebook() || m_fb_logcat.GetOptionValue().GetCurrentValue();
+ }
+public:
+ CommandObjectProcessMinidumpDump(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process plugin dump",
+ "Dump information from the minidump file.", nullptr),
+ m_option_group(),
+ INIT_BOOL(m_dump_all, "all", 'a',
+ "Dump the everything in the minidump."),
+ INIT_BOOL(m_dump_directory, "directory", 'd',
+ "Dump the minidump directory map."),
+ INIT_BOOL(m_dump_linux_cpuinfo, "cpuinfo", 'C',
+ "Dump linux /proc/cpuinfo."),
+ INIT_BOOL(m_dump_linux_proc_status, "status", 's',
+ "Dump linux /proc/<pid>/status."),
+ INIT_BOOL(m_dump_linux_lsb_release, "lsb-release", 'r',
+ "Dump linux /etc/lsb-release."),
+ INIT_BOOL(m_dump_linux_cmdline, "cmdline", 'c',
+ "Dump linux /proc/<pid>/cmdline."),
+ INIT_BOOL(m_dump_linux_environ, "environ", 'e',
+ "Dump linux /proc/<pid>/environ."),
+ INIT_BOOL(m_dump_linux_auxv, "auxv", 'x',
+ "Dump linux /proc/<pid>/auxv."),
+ INIT_BOOL(m_dump_linux_maps, "maps", 'm',
+ "Dump linux /proc/<pid>/maps."),
+ INIT_BOOL(m_dump_linux_proc_stat, "stat", 'S',
+ "Dump linux /proc/<pid>/stat."),
+ INIT_BOOL(m_dump_linux_proc_uptime, "uptime", 'u',
+ "Dump linux process uptime."),
+ INIT_BOOL(m_dump_linux_proc_fd, "fd", 'f',
+ "Dump linux /proc/<pid>/fd."),
+ INIT_BOOL(m_dump_linux_all, "linux", 'l',
+ "Dump all linux streams."),
+ INIT_BOOL(m_fb_app_data, "fb-app-data", 1,
+ "Dump Facebook application custom data."),
+ INIT_BOOL(m_fb_build_id, "fb-build-id", 2,
+ "Dump the Facebook build ID."),
+ INIT_BOOL(m_fb_version, "fb-version", 3,
+ "Dump Facebook application version string."),
+ INIT_BOOL(m_fb_java_stack, "fb-java-stack", 4,
+ "Dump Facebook java stack."),
+ INIT_BOOL(m_fb_dalvik, "fb-dalvik-info", 5,
+ "Dump Facebook Dalvik info."),
+ INIT_BOOL(m_fb_unwind, "fb-unwind-symbols", 6,
+ "Dump Facebook unwind symbols."),
+ INIT_BOOL(m_fb_error_log, "fb-error-log", 7,
+ "Dump Facebook error log."),
+ INIT_BOOL(m_fb_app_state, "fb-app-state-log", 8,
+ "Dump Facebook java stack."),
+ INIT_BOOL(m_fb_abort, "fb-abort-reason", 9,
+ "Dump Facebook abort reason."),
+ INIT_BOOL(m_fb_thread, "fb-thread-name", 10,
+ "Dump Facebook thread name."),
+ INIT_BOOL(m_fb_logcat, "fb-logcat", 11,
+ "Dump Facebook logcat."),
+ INIT_BOOL(m_fb_all, "facebook", 12, "Dump all Facebook streams.") {
+ APPEND_OPT(m_dump_all);
+ APPEND_OPT(m_dump_directory);
+ APPEND_OPT(m_dump_linux_cpuinfo);
+ APPEND_OPT(m_dump_linux_proc_status);
+ APPEND_OPT(m_dump_linux_lsb_release);
+ APPEND_OPT(m_dump_linux_cmdline);
+ APPEND_OPT(m_dump_linux_environ);
+ APPEND_OPT(m_dump_linux_auxv);
+ APPEND_OPT(m_dump_linux_maps);
+ APPEND_OPT(m_dump_linux_proc_stat);
+ APPEND_OPT(m_dump_linux_proc_uptime);
+ APPEND_OPT(m_dump_linux_proc_fd);
+ APPEND_OPT(m_dump_linux_all);
+ APPEND_OPT(m_fb_app_data);
+ APPEND_OPT(m_fb_build_id);
+ APPEND_OPT(m_fb_version);
+ APPEND_OPT(m_fb_java_stack);
+ APPEND_OPT(m_fb_dalvik);
+ APPEND_OPT(m_fb_unwind);
+ APPEND_OPT(m_fb_error_log);
+ APPEND_OPT(m_fb_app_state);
+ APPEND_OPT(m_fb_abort);
+ APPEND_OPT(m_fb_thread);
+ APPEND_OPT(m_fb_logcat);
+ APPEND_OPT(m_fb_all);
+ m_option_group.Finalize();
+ }
+
+ ~CommandObjectProcessMinidumpDump() override {}
+
+ Options *GetOptions() override { return &m_option_group; }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ const size_t argc = command.GetArgumentCount();
+ if (argc > 0) {
+ result.AppendErrorWithFormat("'%s' take no arguments, only options",
+ m_cmd_name.c_str());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ SetDefaultOptionsIfNoneAreSet();
+
+ ProcessMinidump *process = static_cast<ProcessMinidump *>(
+ m_interpreter.GetExecutionContext().GetProcessPtr());
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ Stream &s = result.GetOutputStream();
+ MinidumpParser &minidump = *process->m_minidump_parser;
+ if (DumpDirectory()) {
+ s.Printf("RVA SIZE TYPE StreamType\n");
+ s.Printf("---------- ---------- ---------- --------------------------\n");
+ for (const auto &stream_desc : minidump.GetMinidumpFile().streams())
+ s.Printf(
+ "0x%8.8x 0x%8.8x 0x%8.8x %s\n", (uint32_t)stream_desc.Location.RVA,
+ (uint32_t)stream_desc.Location.DataSize,
+ (unsigned)(StreamType)stream_desc.Type,
+ MinidumpParser::GetStreamTypeAsString(stream_desc.Type).data());
+ s.Printf("\n");
+ }
+ auto DumpTextStream = [&](StreamType stream_type,
+ llvm::StringRef label) -> void {
+ auto bytes = minidump.GetStream(stream_type);
+ if (!bytes.empty()) {
+ if (label.empty())
+ label = MinidumpParser::GetStreamTypeAsString(stream_type);
+ s.Printf("%s:\n%s\n\n", label.data(), bytes.data());
+ }
+ };
+ auto DumpBinaryStream = [&](StreamType stream_type,
+ llvm::StringRef label) -> void {
+ auto bytes = minidump.GetStream(stream_type);
+ if (!bytes.empty()) {
+ if (label.empty())
+ label = MinidumpParser::GetStreamTypeAsString(stream_type);
+ s.Printf("%s:\n", label.data());
+ DataExtractor data(bytes.data(), bytes.size(), eByteOrderLittle,
+ process->GetAddressByteSize());
+ DumpDataExtractor(data, &s, 0, lldb::eFormatBytesWithASCII, 1,
+ bytes.size(), 16, 0, 0, 0);
+ s.Printf("\n\n");
+ }
+ };
+
+ if (DumpLinuxCPUInfo())
+ DumpTextStream(StreamType::LinuxCPUInfo, "/proc/cpuinfo");
+ if (DumpLinuxProcStatus())
+ DumpTextStream(StreamType::LinuxProcStatus, "/proc/PID/status");
+ if (DumpLinuxLSBRelease())
+ DumpTextStream(StreamType::LinuxLSBRelease, "/etc/lsb-release");
+ if (DumpLinuxCMDLine())
+ DumpTextStream(StreamType::LinuxCMDLine, "/proc/PID/cmdline");
+ if (DumpLinuxEnviron())
+ DumpTextStream(StreamType::LinuxEnviron, "/proc/PID/environ");
+ if (DumpLinuxAuxv())
+ DumpBinaryStream(StreamType::LinuxAuxv, "/proc/PID/auxv");
+ if (DumpLinuxMaps())
+ DumpTextStream(StreamType::LinuxMaps, "/proc/PID/maps");
+ if (DumpLinuxProcStat())
+ DumpTextStream(StreamType::LinuxProcStat, "/proc/PID/stat");
+ if (DumpLinuxProcUptime())
+ DumpTextStream(StreamType::LinuxProcUptime, "uptime");
+ if (DumpLinuxProcFD())
+ DumpTextStream(StreamType::LinuxProcFD, "/proc/PID/fd");
+ if (DumpFacebookAppData())
+ DumpTextStream(StreamType::FacebookAppCustomData,
+ "Facebook App Data");
+ if (DumpFacebookBuildID()) {
+ auto bytes = minidump.GetStream(StreamType::FacebookBuildID);
+ if (bytes.size() >= 4) {
+ DataExtractor data(bytes.data(), bytes.size(), eByteOrderLittle,
+ process->GetAddressByteSize());
+ lldb::offset_t offset = 0;
+ uint32_t build_id = data.GetU32(&offset);
+ s.Printf("Facebook Build ID:\n");
+ s.Printf("%u\n", build_id);
+ s.Printf("\n");
+ }
+ }
+ if (DumpFacebookVersionName())
+ DumpTextStream(StreamType::FacebookAppVersionName,
+ "Facebook Version String");
+ if (DumpFacebookJavaStack())
+ DumpTextStream(StreamType::FacebookJavaStack,
+ "Facebook Java Stack");
+ if (DumpFacebookDalvikInfo())
+ DumpTextStream(StreamType::FacebookDalvikInfo,
+ "Facebook Dalvik Info");
+ if (DumpFacebookUnwindSymbols())
+ DumpBinaryStream(StreamType::FacebookUnwindSymbols,
+ "Facebook Unwind Symbols Bytes");
+ if (DumpFacebookErrorLog())
+ DumpTextStream(StreamType::FacebookDumpErrorLog,
+ "Facebook Error Log");
+ if (DumpFacebookAppStateLog())
+ DumpTextStream(StreamType::FacebookAppStateLog,
+ "Faceook Application State Log");
+ if (DumpFacebookAbortReason())
+ DumpTextStream(StreamType::FacebookAbortReason,
+ "Facebook Abort Reason");
+ if (DumpFacebookThreadName())
+ DumpTextStream(StreamType::FacebookThreadName,
+ "Facebook Thread Name");
+ if (DumpFacebookLogcat())
+ DumpTextStream(StreamType::FacebookLogcat,
+ "Facebook Logcat");
+ return true;
+ }
+};
+
+class CommandObjectMultiwordProcessMinidump : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordProcessMinidump(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "process plugin",
+ "Commands for operating on a ProcessMinidump process.",
+ "process plugin <subcommand> [<subcommand-options>]") {
+ LoadSubCommand("dump",
+ CommandObjectSP(new CommandObjectProcessMinidumpDump(interpreter)));
+ }
+
+ ~CommandObjectMultiwordProcessMinidump() override {}
+};
+
+CommandObject *ProcessMinidump::GetPluginCommandObject() {
+ if (!m_command_sp)
+ m_command_sp = std::make_shared<CommandObjectMultiwordProcessMinidump>(
+ GetTarget().GetDebugger().GetCommandInterpreter());
+ return m_command_sp.get();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.h
new file mode 100644
index 000000000000..c39040f61dc5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.h
@@ -0,0 +1,119 @@
+//===-- ProcessMinidump.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 liblldb_ProcessMinidump_h_
+#define liblldb_ProcessMinidump_h_
+
+#include "MinidumpParser.h"
+#include "MinidumpTypes.h"
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Status.h"
+
+#include "llvm/Support/Format.h"
+#include "llvm/Support/raw_ostream.h"
+
+
+namespace lldb_private {
+
+namespace minidump {
+
+class ProcessMinidump : public Process {
+public:
+ static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp,
+ lldb::ListenerSP listener_sp,
+ const FileSpec *crash_file_path);
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ ProcessMinidump(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp,
+ const FileSpec &core_file, lldb::DataBufferSP code_data);
+
+ ~ProcessMinidump() override;
+
+ bool CanDebug(lldb::TargetSP target_sp,
+ bool plugin_specified_by_name) override;
+
+ CommandObject *GetPluginCommandObject() override;
+
+ Status DoLoadCore() override;
+
+ DynamicLoader *GetDynamicLoader() override { return nullptr; }
+
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ SystemRuntime *GetSystemRuntime() override { return nullptr; }
+
+ Status DoDestroy() override;
+
+ void RefreshStateAfterStop() override;
+
+ bool IsAlive() override;
+
+ bool WarnBeforeDetach() const override;
+
+ size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ Status &error) override;
+
+ size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ Status &error) override;
+
+ ArchSpec GetArchitecture();
+
+ Status GetMemoryRegionInfo(lldb::addr_t load_addr,
+ MemoryRegionInfo &range_info) override;
+
+ Status GetMemoryRegions(
+ lldb_private::MemoryRegionInfos &region_list) override;
+
+ bool GetProcessInfo(ProcessInstanceInfo &info) override;
+
+ Status WillResume() override {
+ Status error;
+ error.SetErrorStringWithFormat(
+ "error: %s does not support resuming processes",
+ GetPluginName().GetCString());
+ return error;
+ }
+
+ llvm::Optional<MinidumpParser> m_minidump_parser;
+
+protected:
+ void Clear();
+
+ bool UpdateThreadList(ThreadList &old_thread_list,
+ ThreadList &new_thread_list) override;
+
+ void ReadModuleList();
+
+ JITLoaderList &GetJITLoaders() override;
+
+private:
+ FileSpec m_core_file;
+ lldb::DataBufferSP m_core_data;
+ llvm::ArrayRef<minidump::Thread> m_thread_list;
+ const MinidumpExceptionStream *m_active_exception;
+ lldb::CommandObjectSP m_command_sp;
+ bool m_is_wow64;
+};
+
+} // namespace minidump
+} // namespace lldb_private
+
+#endif // liblldb_ProcessMinidump_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.cpp
new file mode 100644
index 000000000000..f2e456097dfc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.cpp
@@ -0,0 +1,533 @@
+//===-- RegisterContextMinidump_ARM.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 "RegisterContextMinidump_ARM.h"
+
+#include "Utility/ARM_DWARF_Registers.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/lldb-enumerations.h"
+
+// C includes
+#include <assert.h>
+
+// C++ includes
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace minidump;
+
+#define INV LLDB_INVALID_REGNUM
+#define OFFSET(r) (offsetof(RegisterContextMinidump_ARM::Context, r))
+
+#define DEF_R(i) \
+ { \
+ "r" #i, nullptr, 4, OFFSET(r) + i * 4, eEncodingUint, eFormatHex, \
+ {dwarf_r##i, dwarf_r##i, INV, INV, reg_r##i}, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEF_R_ARG(i, n) \
+ { \
+ "r" #i, "arg" #n, 4, OFFSET(r) + i * 4, eEncodingUint, eFormatHex, \
+ {dwarf_r##i, dwarf_r##i, LLDB_REGNUM_GENERIC_ARG1 + i, INV, reg_r##i}, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEF_D(i) \
+ { \
+ "d" #i, nullptr, 8, OFFSET(d) + i * 8, eEncodingVector, \
+ eFormatVectorOfUInt8, {dwarf_d##i, dwarf_d##i, INV, INV, reg_d##i}, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEF_S(i) \
+ { \
+ "s" #i, nullptr, 4, OFFSET(s) + i * 4, eEncodingIEEE754, eFormatFloat, \
+ {dwarf_s##i, dwarf_s##i, INV, INV, reg_s##i}, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEF_Q(i) \
+ { \
+ "q" #i, nullptr, 16, OFFSET(q) + i * 16, eEncodingVector, \
+ eFormatVectorOfUInt8, {dwarf_q##i, dwarf_q##i, INV, INV, reg_q##i}, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+// Zero based LLDB register numbers for this register context
+enum {
+ // General Purpose Registers
+ reg_r0,
+ reg_r1,
+ reg_r2,
+ reg_r3,
+ reg_r4,
+ reg_r5,
+ reg_r6,
+ reg_r7,
+ reg_r8,
+ reg_r9,
+ reg_r10,
+ reg_r11,
+ reg_r12,
+ reg_sp,
+ reg_lr,
+ reg_pc,
+ reg_cpsr,
+ // Floating Point Registers
+ reg_fpscr,
+ reg_d0,
+ reg_d1,
+ reg_d2,
+ reg_d3,
+ reg_d4,
+ reg_d5,
+ reg_d6,
+ reg_d7,
+ reg_d8,
+ reg_d9,
+ reg_d10,
+ reg_d11,
+ reg_d12,
+ reg_d13,
+ reg_d14,
+ reg_d15,
+ reg_d16,
+ reg_d17,
+ reg_d18,
+ reg_d19,
+ reg_d20,
+ reg_d21,
+ reg_d22,
+ reg_d23,
+ reg_d24,
+ reg_d25,
+ reg_d26,
+ reg_d27,
+ reg_d28,
+ reg_d29,
+ reg_d30,
+ reg_d31,
+ reg_s0,
+ reg_s1,
+ reg_s2,
+ reg_s3,
+ reg_s4,
+ reg_s5,
+ reg_s6,
+ reg_s7,
+ reg_s8,
+ reg_s9,
+ reg_s10,
+ reg_s11,
+ reg_s12,
+ reg_s13,
+ reg_s14,
+ reg_s15,
+ reg_s16,
+ reg_s17,
+ reg_s18,
+ reg_s19,
+ reg_s20,
+ reg_s21,
+ reg_s22,
+ reg_s23,
+ reg_s24,
+ reg_s25,
+ reg_s26,
+ reg_s27,
+ reg_s28,
+ reg_s29,
+ reg_s30,
+ reg_s31,
+ reg_q0,
+ reg_q1,
+ reg_q2,
+ reg_q3,
+ reg_q4,
+ reg_q5,
+ reg_q6,
+ reg_q7,
+ reg_q8,
+ reg_q9,
+ reg_q10,
+ reg_q11,
+ reg_q12,
+ reg_q13,
+ reg_q14,
+ reg_q15,
+ k_num_regs
+};
+
+static RegisterInfo g_reg_info_apple_fp = {
+ "fp",
+ "r7",
+ 4,
+ OFFSET(r) + 7 * 4,
+ eEncodingUint,
+ eFormatHex,
+ {INV, dwarf_r7, LLDB_REGNUM_GENERIC_FP, INV, reg_r7},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0};
+
+static RegisterInfo g_reg_info_fp = {
+ "fp",
+ "r11",
+ 4,
+ OFFSET(r) + 11 * 4,
+ eEncodingUint,
+ eFormatHex,
+ {INV, dwarf_r11, LLDB_REGNUM_GENERIC_FP, INV, reg_r11},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0};
+
+// Register info definitions for this register context
+static RegisterInfo g_reg_infos[] = {
+ DEF_R_ARG(0, 1),
+ DEF_R_ARG(1, 2),
+ DEF_R_ARG(2, 3),
+ DEF_R_ARG(3, 4),
+ DEF_R(4),
+ DEF_R(5),
+ DEF_R(6),
+ DEF_R(7),
+ DEF_R(8),
+ DEF_R(9),
+ DEF_R(10),
+ DEF_R(11),
+ DEF_R(12),
+ {"sp",
+ "r13",
+ 4,
+ OFFSET(r) + 13 * 4,
+ eEncodingUint,
+ eFormatHex,
+ {INV, dwarf_sp, LLDB_REGNUM_GENERIC_SP, INV, reg_sp},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lr",
+ "r14",
+ 4,
+ OFFSET(r) + 14 * 4,
+ eEncodingUint,
+ eFormatHex,
+ {INV, dwarf_lr, LLDB_REGNUM_GENERIC_RA, INV, reg_lr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"pc",
+ "r15",
+ 4,
+ OFFSET(r) + 15 * 4,
+ eEncodingUint,
+ eFormatHex,
+ {INV, dwarf_pc, LLDB_REGNUM_GENERIC_PC, INV, reg_pc},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"cpsr",
+ "psr",
+ 4,
+ OFFSET(cpsr),
+ eEncodingUint,
+ eFormatHex,
+ {INV, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS, INV, reg_cpsr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fpscr",
+ nullptr,
+ 8,
+ OFFSET(fpscr),
+ eEncodingUint,
+ eFormatHex,
+ {INV, INV, INV, INV, reg_fpscr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ DEF_D(0),
+ DEF_D(1),
+ DEF_D(2),
+ DEF_D(3),
+ DEF_D(4),
+ DEF_D(5),
+ DEF_D(6),
+ DEF_D(7),
+ DEF_D(8),
+ DEF_D(9),
+ DEF_D(10),
+ DEF_D(11),
+ DEF_D(12),
+ DEF_D(13),
+ DEF_D(14),
+ DEF_D(15),
+ DEF_D(16),
+ DEF_D(17),
+ DEF_D(18),
+ DEF_D(19),
+ DEF_D(20),
+ DEF_D(21),
+ DEF_D(22),
+ DEF_D(23),
+ DEF_D(24),
+ DEF_D(25),
+ DEF_D(26),
+ DEF_D(27),
+ DEF_D(28),
+ DEF_D(29),
+ DEF_D(30),
+ DEF_D(31),
+ DEF_S(0),
+ DEF_S(1),
+ DEF_S(2),
+ DEF_S(3),
+ DEF_S(4),
+ DEF_S(5),
+ DEF_S(6),
+ DEF_S(7),
+ DEF_S(8),
+ DEF_S(9),
+ DEF_S(10),
+ DEF_S(11),
+ DEF_S(12),
+ DEF_S(13),
+ DEF_S(14),
+ DEF_S(15),
+ DEF_S(16),
+ DEF_S(17),
+ DEF_S(18),
+ DEF_S(19),
+ DEF_S(20),
+ DEF_S(21),
+ DEF_S(22),
+ DEF_S(23),
+ DEF_S(24),
+ DEF_S(25),
+ DEF_S(26),
+ DEF_S(27),
+ DEF_S(28),
+ DEF_S(29),
+ DEF_S(30),
+ DEF_S(31),
+ DEF_Q(0),
+ DEF_Q(1),
+ DEF_Q(2),
+ DEF_Q(3),
+ DEF_Q(4),
+ DEF_Q(5),
+ DEF_Q(6),
+ DEF_Q(7),
+ DEF_Q(8),
+ DEF_Q(9),
+ DEF_Q(10),
+ DEF_Q(11),
+ DEF_Q(12),
+ DEF_Q(13),
+ DEF_Q(14),
+ DEF_Q(15)};
+
+constexpr size_t k_num_reg_infos = llvm::array_lengthof(g_reg_infos);
+
+// ARM general purpose registers.
+const uint32_t g_gpr_regnums[] = {
+ reg_r0,
+ reg_r1,
+ reg_r2,
+ reg_r3,
+ reg_r4,
+ reg_r5,
+ reg_r6,
+ reg_r7,
+ reg_r8,
+ reg_r9,
+ reg_r10,
+ reg_r11,
+ reg_r12,
+ reg_sp,
+ reg_lr,
+ reg_pc,
+ reg_cpsr,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+const uint32_t g_fpu_regnums[] = {
+ reg_fpscr,
+ reg_d0,
+ reg_d1,
+ reg_d2,
+ reg_d3,
+ reg_d4,
+ reg_d5,
+ reg_d6,
+ reg_d7,
+ reg_d8,
+ reg_d9,
+ reg_d10,
+ reg_d11,
+ reg_d12,
+ reg_d13,
+ reg_d14,
+ reg_d15,
+ reg_d16,
+ reg_d17,
+ reg_d18,
+ reg_d19,
+ reg_d20,
+ reg_d21,
+ reg_d22,
+ reg_d23,
+ reg_d24,
+ reg_d25,
+ reg_d26,
+ reg_d27,
+ reg_d28,
+ reg_d29,
+ reg_d30,
+ reg_d31,
+ reg_s0,
+ reg_s1,
+ reg_s2,
+ reg_s3,
+ reg_s4,
+ reg_s5,
+ reg_s6,
+ reg_s7,
+ reg_s8,
+ reg_s9,
+ reg_s10,
+ reg_s11,
+ reg_s12,
+ reg_s13,
+ reg_s14,
+ reg_s15,
+ reg_s16,
+ reg_s17,
+ reg_s18,
+ reg_s19,
+ reg_s20,
+ reg_s21,
+ reg_s22,
+ reg_s23,
+ reg_s24,
+ reg_s25,
+ reg_s26,
+ reg_s27,
+ reg_s28,
+ reg_s29,
+ reg_s30,
+ reg_s31,
+ reg_q0,
+ reg_q1,
+ reg_q2,
+ reg_q3,
+ reg_q4,
+ reg_q5,
+ reg_q6,
+ reg_q7,
+ reg_q8,
+ reg_q9,
+ reg_q10,
+ reg_q11,
+ reg_q12,
+ reg_q13,
+ reg_q14,
+ reg_q15,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+
+// Skip the last LLDB_INVALID_REGNUM in each count below by subtracting 1
+constexpr size_t k_num_gpr_regs = llvm::array_lengthof(g_gpr_regnums) - 1;
+constexpr size_t k_num_fpu_regs = llvm::array_lengthof(g_fpu_regnums) - 1;
+
+static RegisterSet g_reg_sets[] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_regs, g_gpr_regnums},
+ {"Floating Point Registers", "fpu", k_num_fpu_regs, g_fpu_regnums},
+};
+
+constexpr size_t k_num_reg_sets = llvm::array_lengthof(g_reg_sets);
+
+RegisterContextMinidump_ARM::RegisterContextMinidump_ARM(
+ lldb_private::Thread &thread, const DataExtractor &data, bool apple)
+ : RegisterContext(thread, 0), m_apple(apple) {
+ lldb::offset_t offset = 0;
+ m_regs.context_flags = data.GetU32(&offset);
+ for (unsigned i = 0; i < llvm::array_lengthof(m_regs.r); ++i)
+ m_regs.r[i] = data.GetU32(&offset);
+ m_regs.cpsr = data.GetU32(&offset);
+ m_regs.fpscr = data.GetU64(&offset);
+ for (unsigned i = 0; i < llvm::array_lengthof(m_regs.d); ++i)
+ m_regs.d[i] = data.GetU64(&offset);
+ lldbassert(k_num_regs == k_num_reg_infos);
+}
+
+size_t RegisterContextMinidump_ARM::GetRegisterCount() { return k_num_regs; }
+
+const RegisterInfo *
+RegisterContextMinidump_ARM::GetRegisterInfoAtIndex(size_t reg) {
+ if (reg < k_num_reg_infos) {
+ if (m_apple) {
+ if (reg == reg_r7)
+ return &g_reg_info_apple_fp;
+ } else {
+ if (reg == reg_r11)
+ return &g_reg_info_fp;
+ }
+ return &g_reg_infos[reg];
+ }
+ return nullptr;
+}
+
+size_t RegisterContextMinidump_ARM::GetRegisterSetCount() {
+ return k_num_reg_sets;
+}
+
+const RegisterSet *RegisterContextMinidump_ARM::GetRegisterSet(size_t set) {
+ if (set < k_num_reg_sets)
+ return &g_reg_sets[set];
+ return nullptr;
+}
+
+const char *RegisterContextMinidump_ARM::GetRegisterName(unsigned reg) {
+ if (reg < k_num_reg_infos)
+ return g_reg_infos[reg].name;
+ return nullptr;
+}
+
+bool RegisterContextMinidump_ARM::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+ Status error;
+ reg_value.SetFromMemoryData(
+ reg_info, (const uint8_t *)&m_regs + reg_info->byte_offset,
+ reg_info->byte_size, lldb::eByteOrderLittle, error);
+ return error.Success();
+}
+
+bool RegisterContextMinidump_ARM::WriteRegister(const RegisterInfo *,
+ const RegisterValue &) {
+ return false;
+}
+
+uint32_t RegisterContextMinidump_ARM::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ for (size_t i = 0; i < k_num_regs; ++i) {
+ if (g_reg_infos[i].kinds[kind] == num)
+ return i;
+ }
+ return LLDB_INVALID_REGNUM;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.h
new file mode 100644
index 000000000000..eff8cdfef00a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.h
@@ -0,0 +1,92 @@
+//===-- RegisterContextMinidump_ARM.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 liblldb_RegisterContextMinidump_ARM_h_
+#define liblldb_RegisterContextMinidump_ARM_h_
+
+#include "MinidumpTypes.h"
+
+#include "Plugins/Process/Utility/RegisterInfoInterface.h"
+
+#include "lldb/Target/RegisterContext.h"
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/BitmaskEnum.h"
+
+// C includes
+// C++ includes
+
+namespace lldb_private {
+
+namespace minidump {
+
+LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
+
+class RegisterContextMinidump_ARM : public lldb_private::RegisterContext {
+public:
+ RegisterContextMinidump_ARM(lldb_private::Thread &thread,
+ const DataExtractor &data, bool apple);
+
+ ~RegisterContextMinidump_ARM() override = default;
+
+ void InvalidateAllRegisters() override {
+ // Do nothing... registers are always valid...
+ }
+
+ size_t GetRegisterCount() override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ const char *GetRegisterName(unsigned reg);
+
+ bool ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &reg_value) override;
+
+ bool WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &reg_value) override;
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+ // Reference: see breakpad/crashpad source
+ struct QRegValue {
+ uint64_t lo;
+ uint64_t hi;
+ };
+
+ struct Context {
+ uint32_t context_flags;
+ uint32_t r[16];
+ uint32_t cpsr;
+ uint64_t fpscr;
+ union {
+ uint64_t d[32];
+ uint32_t s[32];
+ QRegValue q[16];
+ };
+ uint32_t extra[8];
+ };
+
+protected:
+ enum class Flags : uint32_t {
+ ARM_Flag = 0x40000000,
+ Integer = ARM_Flag | 0x00000002,
+ FloatingPoint = ARM_Flag | 0x00000004,
+ LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ FloatingPoint)
+ };
+ Context m_regs;
+ const bool m_apple; // True if this is an Apple ARM where FP is R7
+};
+
+} // end namespace minidump
+} // end namespace lldb_private
+#endif // liblldb_RegisterContextMinidump_ARM_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.cpp
new file mode 100644
index 000000000000..bbd0e14a3267
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.cpp
@@ -0,0 +1,833 @@
+//===-- RegisterContextMinidump_ARM64.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 "RegisterContextMinidump_ARM64.h"
+
+#include "Utility/ARM64_DWARF_Registers.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/lldb-enumerations.h"
+
+// C includes
+#include <assert.h>
+
+// C++ includes
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace minidump;
+
+#define INV LLDB_INVALID_REGNUM
+#define OFFSET(r) (offsetof(RegisterContextMinidump_ARM64::Context, r))
+
+#define DEF_X(i) \
+ { \
+ "x" #i, nullptr, 8, OFFSET(x) + i * 8, eEncodingUint, eFormatHex, \
+ {arm64_dwarf::x##i, arm64_dwarf::x##i, INV, INV, reg_x##i}, \
+ nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEF_W(i) \
+ { \
+ "w" #i, nullptr, 4, OFFSET(x) + i * 8, eEncodingUint, eFormatHex, \
+ {INV, INV, INV, INV, reg_w##i}, nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEF_X_ARG(i, n) \
+ { \
+ "x" #i, "arg" #n, 8, OFFSET(x) + i * 8, eEncodingUint, eFormatHex, \
+ {arm64_dwarf::x##i, arm64_dwarf::x##i, LLDB_REGNUM_GENERIC_ARG1 + i, \
+ INV, reg_x##i}, nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEF_V(i) \
+ { \
+ "v" #i, nullptr, 16, OFFSET(v) + i * 16, eEncodingVector, \
+ eFormatVectorOfUInt8, {arm64_dwarf::v##i, arm64_dwarf::v##i, INV, INV, \
+ reg_v##i}, nullptr, nullptr, nullptr, 0 \
+ }
+
+#define DEF_D(i) \
+ { \
+ "d" #i, nullptr, 8, OFFSET(v) + i * 16, eEncodingVector, \
+ eFormatVectorOfUInt8, {INV, INV, INV, INV, reg_d##i}, nullptr, \
+ nullptr, nullptr, 0 \
+ }
+
+#define DEF_S(i) \
+ { \
+ "s" #i, nullptr, 4, OFFSET(v) + i * 16, eEncodingVector, \
+ eFormatVectorOfUInt8, {INV, INV, INV, INV, reg_s##i}, nullptr, \
+ nullptr, nullptr, 0 \
+ }
+
+#define DEF_H(i) \
+ { \
+ "h" #i, nullptr, 2, OFFSET(v) + i * 16, eEncodingVector, \
+ eFormatVectorOfUInt8, {INV, INV, INV, INV, reg_h##i}, nullptr, \
+ nullptr, nullptr, 0 \
+ }
+
+// Zero based LLDB register numbers for this register context
+enum {
+ // General Purpose Registers
+ reg_x0 = 0,
+ reg_x1,
+ reg_x2,
+ reg_x3,
+ reg_x4,
+ reg_x5,
+ reg_x6,
+ reg_x7,
+ reg_x8,
+ reg_x9,
+ reg_x10,
+ reg_x11,
+ reg_x12,
+ reg_x13,
+ reg_x14,
+ reg_x15,
+ reg_x16,
+ reg_x17,
+ reg_x18,
+ reg_x19,
+ reg_x20,
+ reg_x21,
+ reg_x22,
+ reg_x23,
+ reg_x24,
+ reg_x25,
+ reg_x26,
+ reg_x27,
+ reg_x28,
+ reg_fp,
+ reg_lr,
+ reg_sp,
+ reg_pc,
+ reg_w0,
+ reg_w1,
+ reg_w2,
+ reg_w3,
+ reg_w4,
+ reg_w5,
+ reg_w6,
+ reg_w7,
+ reg_w8,
+ reg_w9,
+ reg_w10,
+ reg_w11,
+ reg_w12,
+ reg_w13,
+ reg_w14,
+ reg_w15,
+ reg_w16,
+ reg_w17,
+ reg_w18,
+ reg_w19,
+ reg_w20,
+ reg_w21,
+ reg_w22,
+ reg_w23,
+ reg_w24,
+ reg_w25,
+ reg_w26,
+ reg_w27,
+ reg_w28,
+ reg_w29,
+ reg_w30,
+ reg_w31,
+ reg_cpsr,
+ // Floating Point Registers
+ reg_fpsr,
+ reg_fpcr,
+ reg_v0,
+ reg_v1,
+ reg_v2,
+ reg_v3,
+ reg_v4,
+ reg_v5,
+ reg_v6,
+ reg_v7,
+ reg_v8,
+ reg_v9,
+ reg_v10,
+ reg_v11,
+ reg_v12,
+ reg_v13,
+ reg_v14,
+ reg_v15,
+ reg_v16,
+ reg_v17,
+ reg_v18,
+ reg_v19,
+ reg_v20,
+ reg_v21,
+ reg_v22,
+ reg_v23,
+ reg_v24,
+ reg_v25,
+ reg_v26,
+ reg_v27,
+ reg_v28,
+ reg_v29,
+ reg_v30,
+ reg_v31,
+ reg_d0,
+ reg_d1,
+ reg_d2,
+ reg_d3,
+ reg_d4,
+ reg_d5,
+ reg_d6,
+ reg_d7,
+ reg_d8,
+ reg_d9,
+ reg_d10,
+ reg_d11,
+ reg_d12,
+ reg_d13,
+ reg_d14,
+ reg_d15,
+ reg_d16,
+ reg_d17,
+ reg_d18,
+ reg_d19,
+ reg_d20,
+ reg_d21,
+ reg_d22,
+ reg_d23,
+ reg_d24,
+ reg_d25,
+ reg_d26,
+ reg_d27,
+ reg_d28,
+ reg_d29,
+ reg_d30,
+ reg_d31,
+ reg_s0,
+ reg_s1,
+ reg_s2,
+ reg_s3,
+ reg_s4,
+ reg_s5,
+ reg_s6,
+ reg_s7,
+ reg_s8,
+ reg_s9,
+ reg_s10,
+ reg_s11,
+ reg_s12,
+ reg_s13,
+ reg_s14,
+ reg_s15,
+ reg_s16,
+ reg_s17,
+ reg_s18,
+ reg_s19,
+ reg_s20,
+ reg_s21,
+ reg_s22,
+ reg_s23,
+ reg_s24,
+ reg_s25,
+ reg_s26,
+ reg_s27,
+ reg_s28,
+ reg_s29,
+ reg_s30,
+ reg_s31,
+ reg_h0,
+ reg_h1,
+ reg_h2,
+ reg_h3,
+ reg_h4,
+ reg_h5,
+ reg_h6,
+ reg_h7,
+ reg_h8,
+ reg_h9,
+ reg_h10,
+ reg_h11,
+ reg_h12,
+ reg_h13,
+ reg_h14,
+ reg_h15,
+ reg_h16,
+ reg_h17,
+ reg_h18,
+ reg_h19,
+ reg_h20,
+ reg_h21,
+ reg_h22,
+ reg_h23,
+ reg_h24,
+ reg_h25,
+ reg_h26,
+ reg_h27,
+ reg_h28,
+ reg_h29,
+ reg_h30,
+ reg_h31,
+ k_num_regs
+};
+
+// Register info definitions for this register context
+static RegisterInfo g_reg_infos[] = {
+ DEF_X_ARG(0, 1),
+ DEF_X_ARG(1, 2),
+ DEF_X_ARG(2, 3),
+ DEF_X_ARG(3, 4),
+ DEF_X_ARG(4, 5),
+ DEF_X_ARG(5, 6),
+ DEF_X_ARG(6, 7),
+ DEF_X_ARG(7, 8),
+ DEF_X(8),
+ DEF_X(9),
+ DEF_X(10),
+ DEF_X(11),
+ DEF_X(12),
+ DEF_X(13),
+ DEF_X(14),
+ DEF_X(15),
+ DEF_X(16),
+ DEF_X(17),
+ DEF_X(18),
+ DEF_X(19),
+ DEF_X(20),
+ DEF_X(21),
+ DEF_X(22),
+ DEF_X(23),
+ DEF_X(24),
+ DEF_X(25),
+ DEF_X(26),
+ DEF_X(27),
+ DEF_X(28),
+ {"fp",
+ "x29",
+ 8,
+ OFFSET(x) + 29 * 8,
+ eEncodingUint,
+ eFormatHex,
+ {arm64_dwarf::x29, arm64_dwarf::x29, LLDB_REGNUM_GENERIC_FP, INV, reg_fp},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"lr",
+ "x30",
+ 8,
+ OFFSET(x) + 30 * 8,
+ eEncodingUint,
+ eFormatHex,
+ {arm64_dwarf::x30, arm64_dwarf::x30, LLDB_REGNUM_GENERIC_RA, INV, reg_lr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"sp",
+ "x31",
+ 8,
+ OFFSET(x) + 31 * 8,
+ eEncodingUint,
+ eFormatHex,
+ {arm64_dwarf::x31, arm64_dwarf::x31, LLDB_REGNUM_GENERIC_SP, INV, reg_sp},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"pc",
+ nullptr,
+ 8,
+ OFFSET(pc),
+ eEncodingUint,
+ eFormatHex,
+ {arm64_dwarf::pc, arm64_dwarf::pc, LLDB_REGNUM_GENERIC_PC, INV, reg_pc},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // w0 - w31
+ DEF_W(0),
+ DEF_W(1),
+ DEF_W(2),
+ DEF_W(3),
+ DEF_W(4),
+ DEF_W(5),
+ DEF_W(6),
+ DEF_W(7),
+ DEF_W(8),
+ DEF_W(9),
+ DEF_W(10),
+ DEF_W(11),
+ DEF_W(12),
+ DEF_W(13),
+ DEF_W(14),
+ DEF_W(15),
+ DEF_W(16),
+ DEF_W(17),
+ DEF_W(18),
+ DEF_W(19),
+ DEF_W(20),
+ DEF_W(21),
+ DEF_W(22),
+ DEF_W(23),
+ DEF_W(24),
+ DEF_W(25),
+ DEF_W(26),
+ DEF_W(27),
+ DEF_W(28),
+ DEF_W(29),
+ DEF_W(30),
+ DEF_W(31),
+ {"cpsr",
+ "psr",
+ 4,
+ OFFSET(cpsr),
+ eEncodingUint,
+ eFormatHex,
+ {INV, arm64_dwarf::cpsr, LLDB_REGNUM_GENERIC_FLAGS, INV, reg_cpsr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fpsr",
+ nullptr,
+ 4,
+ OFFSET(fpsr),
+ eEncodingUint,
+ eFormatHex,
+ {INV, INV, INV, INV, reg_fpsr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ {"fpcr",
+ nullptr,
+ 4,
+ OFFSET(fpcr),
+ eEncodingUint,
+ eFormatHex,
+ {INV, INV, INV, INV, reg_fpcr},
+ nullptr,
+ nullptr,
+ nullptr,
+ 0},
+ // v0 - v31
+ DEF_V(0),
+ DEF_V(1),
+ DEF_V(2),
+ DEF_V(3),
+ DEF_V(4),
+ DEF_V(5),
+ DEF_V(6),
+ DEF_V(7),
+ DEF_V(8),
+ DEF_V(9),
+ DEF_V(10),
+ DEF_V(11),
+ DEF_V(12),
+ DEF_V(13),
+ DEF_V(14),
+ DEF_V(15),
+ DEF_V(16),
+ DEF_V(17),
+ DEF_V(18),
+ DEF_V(19),
+ DEF_V(20),
+ DEF_V(21),
+ DEF_V(22),
+ DEF_V(23),
+ DEF_V(24),
+ DEF_V(25),
+ DEF_V(26),
+ DEF_V(27),
+ DEF_V(28),
+ DEF_V(29),
+ DEF_V(30),
+ DEF_V(31),
+ // d0 - d31
+ DEF_D(0),
+ DEF_D(1),
+ DEF_D(2),
+ DEF_D(3),
+ DEF_D(4),
+ DEF_D(5),
+ DEF_D(6),
+ DEF_D(7),
+ DEF_D(8),
+ DEF_D(9),
+ DEF_D(10),
+ DEF_D(11),
+ DEF_D(12),
+ DEF_D(13),
+ DEF_D(14),
+ DEF_D(15),
+ DEF_D(16),
+ DEF_D(17),
+ DEF_D(18),
+ DEF_D(19),
+ DEF_D(20),
+ DEF_D(21),
+ DEF_D(22),
+ DEF_D(23),
+ DEF_D(24),
+ DEF_D(25),
+ DEF_D(26),
+ DEF_D(27),
+ DEF_D(28),
+ DEF_D(29),
+ DEF_D(30),
+ DEF_D(31),
+ // s0 - s31
+ DEF_S(0),
+ DEF_S(1),
+ DEF_S(2),
+ DEF_S(3),
+ DEF_S(4),
+ DEF_S(5),
+ DEF_S(6),
+ DEF_S(7),
+ DEF_S(8),
+ DEF_S(9),
+ DEF_S(10),
+ DEF_S(11),
+ DEF_S(12),
+ DEF_S(13),
+ DEF_S(14),
+ DEF_S(15),
+ DEF_S(16),
+ DEF_S(17),
+ DEF_S(18),
+ DEF_S(19),
+ DEF_S(20),
+ DEF_S(21),
+ DEF_S(22),
+ DEF_S(23),
+ DEF_S(24),
+ DEF_S(25),
+ DEF_S(26),
+ DEF_S(27),
+ DEF_S(28),
+ DEF_S(29),
+ DEF_S(30),
+ DEF_S(31),
+ // h0 - h31
+ DEF_H(0),
+ DEF_H(1),
+ DEF_H(2),
+ DEF_H(3),
+ DEF_H(4),
+ DEF_H(5),
+ DEF_H(6),
+ DEF_H(7),
+ DEF_H(8),
+ DEF_H(9),
+ DEF_H(10),
+ DEF_H(11),
+ DEF_H(12),
+ DEF_H(13),
+ DEF_H(14),
+ DEF_H(15),
+ DEF_H(16),
+ DEF_H(17),
+ DEF_H(18),
+ DEF_H(19),
+ DEF_H(20),
+ DEF_H(21),
+ DEF_H(22),
+ DEF_H(23),
+ DEF_H(24),
+ DEF_H(25),
+ DEF_H(26),
+ DEF_H(27),
+ DEF_H(28),
+ DEF_H(29),
+ DEF_H(30),
+ DEF_H(31),
+};
+
+constexpr size_t k_num_reg_infos = llvm::array_lengthof(g_reg_infos);
+
+// ARM64 general purpose registers.
+const uint32_t g_gpr_regnums[] = {
+ reg_x0,
+ reg_x1,
+ reg_x2,
+ reg_x3,
+ reg_x4,
+ reg_x5,
+ reg_x6,
+ reg_x7,
+ reg_x8,
+ reg_x9,
+ reg_x10,
+ reg_x11,
+ reg_x12,
+ reg_x13,
+ reg_x14,
+ reg_x15,
+ reg_x16,
+ reg_x17,
+ reg_x18,
+ reg_x19,
+ reg_x20,
+ reg_x21,
+ reg_x22,
+ reg_x23,
+ reg_x24,
+ reg_x25,
+ reg_x26,
+ reg_x27,
+ reg_x28,
+ reg_fp,
+ reg_lr,
+ reg_sp,
+ reg_w0,
+ reg_w1,
+ reg_w2,
+ reg_w3,
+ reg_w4,
+ reg_w5,
+ reg_w6,
+ reg_w7,
+ reg_w8,
+ reg_w9,
+ reg_w10,
+ reg_w11,
+ reg_w12,
+ reg_w13,
+ reg_w14,
+ reg_w15,
+ reg_w16,
+ reg_w17,
+ reg_w18,
+ reg_w19,
+ reg_w20,
+ reg_w21,
+ reg_w22,
+ reg_w23,
+ reg_w24,
+ reg_w25,
+ reg_w26,
+ reg_w27,
+ reg_w28,
+ reg_w29,
+ reg_w30,
+ reg_w31,
+ reg_pc,
+ reg_cpsr,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+const uint32_t g_fpu_regnums[] = {
+ reg_v0,
+ reg_v1,
+ reg_v2,
+ reg_v3,
+ reg_v4,
+ reg_v5,
+ reg_v6,
+ reg_v7,
+ reg_v8,
+ reg_v9,
+ reg_v10,
+ reg_v11,
+ reg_v12,
+ reg_v13,
+ reg_v14,
+ reg_v15,
+ reg_v16,
+ reg_v17,
+ reg_v18,
+ reg_v19,
+ reg_v20,
+ reg_v21,
+ reg_v22,
+ reg_v23,
+ reg_v24,
+ reg_v25,
+ reg_v26,
+ reg_v27,
+ reg_v28,
+ reg_v29,
+ reg_v30,
+ reg_v31,
+ reg_d0,
+ reg_d1,
+ reg_d2,
+ reg_d3,
+ reg_d4,
+ reg_d5,
+ reg_d6,
+ reg_d7,
+ reg_d8,
+ reg_d9,
+ reg_d10,
+ reg_d11,
+ reg_d12,
+ reg_d13,
+ reg_d14,
+ reg_d15,
+ reg_d16,
+ reg_d17,
+ reg_d18,
+ reg_d19,
+ reg_d20,
+ reg_d21,
+ reg_d22,
+ reg_d23,
+ reg_d24,
+ reg_d25,
+ reg_d26,
+ reg_d27,
+ reg_d28,
+ reg_d29,
+ reg_d30,
+ reg_d31,
+ reg_s0,
+ reg_s1,
+ reg_s2,
+ reg_s3,
+ reg_s4,
+ reg_s5,
+ reg_s6,
+ reg_s7,
+ reg_s8,
+ reg_s9,
+ reg_s10,
+ reg_s11,
+ reg_s12,
+ reg_s13,
+ reg_s14,
+ reg_s15,
+ reg_s16,
+ reg_s17,
+ reg_s18,
+ reg_s19,
+ reg_s20,
+ reg_s21,
+ reg_s22,
+ reg_s23,
+ reg_s24,
+ reg_s25,
+ reg_s26,
+ reg_s27,
+ reg_s28,
+ reg_s29,
+ reg_s30,
+ reg_s31,
+ reg_h0,
+ reg_h1,
+ reg_h2,
+ reg_h3,
+ reg_h4,
+ reg_h5,
+ reg_h6,
+ reg_h7,
+ reg_h8,
+ reg_h9,
+ reg_h10,
+ reg_h11,
+ reg_h12,
+ reg_h13,
+ reg_h14,
+ reg_h15,
+ reg_h16,
+ reg_h17,
+ reg_h18,
+ reg_h19,
+ reg_h20,
+ reg_h21,
+ reg_h22,
+ reg_h23,
+ reg_h24,
+ reg_h25,
+ reg_h26,
+ reg_h27,
+ reg_h28,
+ reg_h29,
+ reg_h30,
+ reg_h31,
+ reg_fpsr,
+ reg_fpcr,
+ LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+
+// Skip the last LLDB_INVALID_REGNUM in each count below by subtracting 1
+constexpr size_t k_num_gpr_regs = llvm::array_lengthof(g_gpr_regnums) - 1;
+constexpr size_t k_num_fpu_regs = llvm::array_lengthof(g_fpu_regnums) - 1;
+
+static RegisterSet g_reg_sets[] = {
+ {"General Purpose Registers", "gpr", k_num_gpr_regs, g_gpr_regnums},
+ {"Floating Point Registers", "fpu", k_num_fpu_regs, g_fpu_regnums},
+};
+
+constexpr size_t k_num_reg_sets = llvm::array_lengthof(g_reg_sets);
+
+RegisterContextMinidump_ARM64::RegisterContextMinidump_ARM64(
+ lldb_private::Thread &thread, const DataExtractor &data)
+ : RegisterContext(thread, 0) {
+ lldb::offset_t offset = 0;
+ m_regs.context_flags = data.GetU64(&offset);
+ for (unsigned i = 0; i < 32; ++i)
+ m_regs.x[i] = data.GetU64(&offset);
+ m_regs.pc = data.GetU64(&offset);
+ m_regs.cpsr = data.GetU32(&offset);
+ m_regs.fpsr = data.GetU32(&offset);
+ m_regs.fpcr = data.GetU32(&offset);
+ auto regs_data = data.GetData(&offset, sizeof(m_regs.v));
+ if (regs_data)
+ memcpy(m_regs.v, regs_data, sizeof(m_regs.v));
+ static_assert(k_num_regs == k_num_reg_infos, "");
+}
+size_t RegisterContextMinidump_ARM64::GetRegisterCount() { return k_num_regs; }
+
+const RegisterInfo *
+RegisterContextMinidump_ARM64::GetRegisterInfoAtIndex(size_t reg) {
+ if (reg < k_num_reg_infos)
+ return &g_reg_infos[reg];
+ return nullptr;
+}
+
+size_t RegisterContextMinidump_ARM64::GetRegisterSetCount() {
+ return k_num_reg_sets;
+}
+
+const RegisterSet *RegisterContextMinidump_ARM64::GetRegisterSet(size_t set) {
+ if (set < k_num_reg_sets)
+ return &g_reg_sets[set];
+ return nullptr;
+}
+
+const char *RegisterContextMinidump_ARM64::GetRegisterName(unsigned reg) {
+ if (reg < k_num_reg_infos)
+ return g_reg_infos[reg].name;
+ return nullptr;
+}
+
+bool RegisterContextMinidump_ARM64::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+ Status error;
+ reg_value.SetFromMemoryData(
+ reg_info, (const uint8_t *)&m_regs + reg_info->byte_offset,
+ reg_info->byte_size, lldb::eByteOrderLittle, error);
+ return error.Success();
+}
+
+bool RegisterContextMinidump_ARM64::WriteRegister(const RegisterInfo *,
+ const RegisterValue &) {
+ return false;
+}
+
+uint32_t RegisterContextMinidump_ARM64::ConvertRegisterKindToRegisterNumber(
+ lldb::RegisterKind kind, uint32_t num) {
+ for (size_t i = 0; i < k_num_regs; ++i) {
+ if (g_reg_infos[i].kinds[kind] == num)
+ return i;
+ }
+ return LLDB_INVALID_REGNUM;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.h
new file mode 100644
index 000000000000..f9e7f39eea60
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.h
@@ -0,0 +1,82 @@
+//===-- RegisterContextMinidump_ARM64.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 liblldb_RegisterContextMinidump_ARM64_h_
+#define liblldb_RegisterContextMinidump_ARM64_h_
+
+#include "MinidumpTypes.h"
+
+#include "Plugins/Process/Utility/RegisterInfoInterface.h"
+#include "lldb/Target/RegisterContext.h"
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/BitmaskEnum.h"
+
+// C includes
+// C++ includes
+
+namespace lldb_private {
+
+namespace minidump {
+
+LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
+
+class RegisterContextMinidump_ARM64 : public lldb_private::RegisterContext {
+public:
+ RegisterContextMinidump_ARM64(lldb_private::Thread &thread,
+ const DataExtractor &data);
+
+ ~RegisterContextMinidump_ARM64() override = default;
+
+ void InvalidateAllRegisters() override {
+ // Do nothing... registers are always valid...
+ }
+
+ size_t GetRegisterCount() override;
+
+ const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const lldb_private::RegisterSet *GetRegisterSet(size_t set) override;
+
+ const char *GetRegisterName(unsigned reg);
+
+ bool ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &reg_value) override;
+
+ bool WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &reg_value) override;
+
+ uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,
+ uint32_t num) override;
+
+ // Reference: see breakpad/crashpad source
+ struct Context {
+ uint64_t context_flags;
+ uint64_t x[32];
+ uint64_t pc;
+ uint32_t cpsr;
+ uint32_t fpsr;
+ uint32_t fpcr;
+ uint8_t v[32 * 16]; // 32 128-bit floating point registers
+ };
+
+protected:
+ enum class Flags : uint32_t {
+ ARM64_Flag = 0x80000000,
+ Integer = ARM64_Flag | 0x00000002,
+ FloatingPoint = ARM64_Flag | 0x00000004,
+ LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ FloatingPoint)
+ };
+ Context m_regs;
+};
+
+} // end namespace minidump
+} // end namespace lldb_private
+#endif // liblldb_RegisterContextMinidump_ARM64_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.cpp
new file mode 100644
index 000000000000..8ac2abb22093
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.cpp
@@ -0,0 +1,96 @@
+//===-- RegisterContextMinidump_x86_32.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 "RegisterContextMinidump_x86_32.h"
+
+#include "lldb/Utility/DataBufferHeap.h"
+
+// C includes
+// C++ includes
+
+using namespace lldb_private;
+using namespace minidump;
+
+static void writeRegister(const void *reg_src,
+ llvm::MutableArrayRef<uint8_t> reg_dest) {
+ memcpy(reg_dest.data(), reg_src, reg_dest.size());
+}
+
+lldb::DataBufferSP lldb_private::minidump::ConvertMinidumpContext_x86_32(
+ llvm::ArrayRef<uint8_t> source_data,
+ RegisterInfoInterface *target_reg_interface) {
+
+ const RegisterInfo *reg_info = target_reg_interface->GetRegisterInfo();
+
+ lldb::DataBufferSP result_context_buf(
+ new DataBufferHeap(target_reg_interface->GetGPRSize(), 0));
+ uint8_t *result_base = result_context_buf->GetBytes();
+
+ if (source_data.size() < sizeof(MinidumpContext_x86_32))
+ return nullptr;
+
+ const MinidumpContext_x86_32 *context;
+ consumeObject(source_data, context);
+
+ const MinidumpContext_x86_32_Flags context_flags =
+ static_cast<MinidumpContext_x86_32_Flags>(
+ static_cast<uint32_t>(context->context_flags));
+ auto x86_32_Flag = MinidumpContext_x86_32_Flags::x86_32_Flag;
+ auto ControlFlag = MinidumpContext_x86_32_Flags::Control;
+ auto IntegerFlag = MinidumpContext_x86_32_Flags::Integer;
+ auto SegmentsFlag = MinidumpContext_x86_32_Flags::Segments;
+
+ if ((context_flags & x86_32_Flag) != x86_32_Flag) {
+ return nullptr;
+ }
+
+ if ((context_flags & ControlFlag) == ControlFlag) {
+ writeRegister(&context->ebp,
+ reg_info[lldb_ebp_i386].mutable_data(result_base));
+ writeRegister(&context->eip,
+ reg_info[lldb_eip_i386].mutable_data(result_base));
+ writeRegister(&context->cs,
+ reg_info[lldb_cs_i386].mutable_data(result_base));
+ writeRegister(&context->eflags,
+ reg_info[lldb_eflags_i386].mutable_data(result_base));
+ writeRegister(&context->esp,
+ reg_info[lldb_esp_i386].mutable_data(result_base));
+ writeRegister(&context->ss,
+ reg_info[lldb_ss_i386].mutable_data(result_base));
+ }
+
+ if ((context_flags & SegmentsFlag) == SegmentsFlag) {
+ writeRegister(&context->ds,
+ reg_info[lldb_ds_i386].mutable_data(result_base));
+ writeRegister(&context->es,
+ reg_info[lldb_es_i386].mutable_data(result_base));
+ writeRegister(&context->fs,
+ reg_info[lldb_fs_i386].mutable_data(result_base));
+ writeRegister(&context->gs,
+ reg_info[lldb_gs_i386].mutable_data(result_base));
+ }
+
+ if ((context_flags & IntegerFlag) == IntegerFlag) {
+ writeRegister(&context->eax,
+ reg_info[lldb_eax_i386].mutable_data(result_base));
+ writeRegister(&context->ecx,
+ reg_info[lldb_ecx_i386].mutable_data(result_base));
+ writeRegister(&context->edx,
+ reg_info[lldb_edx_i386].mutable_data(result_base));
+ writeRegister(&context->ebx,
+ reg_info[lldb_ebx_i386].mutable_data(result_base));
+ writeRegister(&context->esi,
+ reg_info[lldb_esi_i386].mutable_data(result_base));
+ writeRegister(&context->edi,
+ reg_info[lldb_edi_i386].mutable_data(result_base));
+ }
+
+ // TODO parse the floating point registers
+
+ return result_context_buf;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.h
new file mode 100644
index 000000000000..d787f78ec7d3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.h
@@ -0,0 +1,135 @@
+//===-- RegisterContextMinidump_x86_32.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 liblldb_RegisterContextMinidump_x86_32_h_
+#define liblldb_RegisterContextMinidump_x86_32_h_
+
+#include "MinidumpTypes.h"
+
+#include "Plugins/Process/Utility/RegisterInfoInterface.h"
+#include "Plugins/Process/Utility/lldb-x86-register-enums.h"
+
+#include "lldb/Target/RegisterContext.h"
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/BitmaskEnum.h"
+#include "llvm/Support/Endian.h"
+
+// C includes
+// C++ includes
+
+namespace lldb_private {
+
+namespace minidump {
+
+// This function receives an ArrayRef pointing to the bytes of the Minidump
+// register context and returns a DataBuffer that's ordered by the offsets
+// specified in the RegisterInfoInterface argument
+// This way we can reuse the already existing register contexts
+lldb::DataBufferSP
+ConvertMinidumpContext_x86_32(llvm::ArrayRef<uint8_t> source_data,
+ RegisterInfoInterface *target_reg_interface);
+
+// Reference: see breakpad/crashpad source or WinNT.h
+struct MinidumpFloatingSaveAreaX86 {
+ llvm::support::ulittle32_t control_word;
+ llvm::support::ulittle32_t status_word;
+ llvm::support::ulittle32_t tag_word;
+ llvm::support::ulittle32_t error_offset;
+ llvm::support::ulittle32_t error_selector;
+ llvm::support::ulittle32_t data_offset;
+ llvm::support::ulittle32_t data_selector;
+
+ enum {
+ RegisterAreaSize = 80,
+ };
+ // register_area contains eight 80-bit (x87 "long double") quantities for
+ // floating-point registers %st0 (%mm0) through %st7 (%mm7).
+ uint8_t register_area[RegisterAreaSize];
+ llvm::support::ulittle32_t cr0_npx_state;
+};
+
+struct MinidumpContext_x86_32 {
+ // The context_flags field determines which parts
+ // of the structure are populated (have valid values)
+ llvm::support::ulittle32_t context_flags;
+
+ // The next 6 registers are included with
+ // MinidumpContext_x86_32_Flags::DebugRegisters
+ llvm::support::ulittle32_t dr0;
+ llvm::support::ulittle32_t dr1;
+ llvm::support::ulittle32_t dr2;
+ llvm::support::ulittle32_t dr3;
+ llvm::support::ulittle32_t dr6;
+ llvm::support::ulittle32_t dr7;
+
+ // The next field is included with
+ // MinidumpContext_x86_32_Flags::FloatingPoint
+ MinidumpFloatingSaveAreaX86 float_save;
+
+ // The next 4 registers are included with
+ // MinidumpContext_x86_32_Flags::Segments
+ llvm::support::ulittle32_t gs;
+ llvm::support::ulittle32_t fs;
+ llvm::support::ulittle32_t es;
+ llvm::support::ulittle32_t ds;
+
+ // The next 6 registers are included with
+ // MinidumpContext_x86_32_Flags::Integer
+ llvm::support::ulittle32_t edi;
+ llvm::support::ulittle32_t esi;
+ llvm::support::ulittle32_t ebx;
+ llvm::support::ulittle32_t edx;
+ llvm::support::ulittle32_t ecx;
+ llvm::support::ulittle32_t eax;
+
+ // The next 6 registers are included with
+ // MinidumpContext_x86_32_Flags::Control
+ llvm::support::ulittle32_t ebp;
+ llvm::support::ulittle32_t eip;
+ llvm::support::ulittle32_t cs; // WinNT.h says "must be sanitized"
+ llvm::support::ulittle32_t eflags; // WinNT.h says "must be sanitized"
+ llvm::support::ulittle32_t esp;
+ llvm::support::ulittle32_t ss;
+
+ // The next field is included with
+ // MinidumpContext_x86_32_Flags::ExtendedRegisters
+ // It contains vector (MMX/SSE) registers. It it laid out in the
+ // format used by the fxsave and fsrstor instructions, so it includes
+ // a copy of the x87 floating-point registers as well. See FXSAVE in
+ // "Intel Architecture Software Developer's Manual, Volume 2."
+ enum {
+ ExtendedRegistersSize = 512,
+ };
+ uint8_t extended_registers[ExtendedRegistersSize];
+};
+
+LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
+
+// For context_flags. These values indicate the type of
+// context stored in the structure. The high 24 bits identify the CPU, the
+// low 8 bits identify the type of context saved.
+enum class MinidumpContext_x86_32_Flags : uint32_t {
+ x86_32_Flag = 0x00010000, // CONTEXT_i386, CONTEXT_i486
+ Control = x86_32_Flag | 0x00000001,
+ Integer = x86_32_Flag | 0x00000002,
+ Segments = x86_32_Flag | 0x00000004,
+ FloatingPoint = x86_32_Flag | 0x00000008,
+ DebugRegisters = x86_32_Flag | 0x00000010,
+ ExtendedRegisters = x86_32_Flag | 0x00000020,
+ XState = x86_32_Flag | 0x00000040,
+
+ Full = Control | Integer | Segments,
+ All = Full | FloatingPoint | DebugRegisters | ExtendedRegisters,
+
+ LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ All)
+};
+
+} // end namespace minidump
+} // end namespace lldb_private
+#endif // liblldb_RegisterContextMinidump_x86_32_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp
new file mode 100644
index 000000000000..515ccf6b2c3c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp
@@ -0,0 +1,110 @@
+//===-- RegisterContextMinidump_x86_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
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextMinidump_x86_64.h"
+
+#include "lldb/Utility/DataBufferHeap.h"
+
+// C includes
+// C++ includes
+
+using namespace lldb_private;
+using namespace minidump;
+
+static llvm::MutableArrayRef<uint8_t> getDestRegister(uint8_t *context,
+ const RegisterInfo &reg) {
+ auto bytes = reg.mutable_data(context);
+
+ switch (reg.kinds[lldb::eRegisterKindLLDB]) {
+ case lldb_cs_x86_64:
+ case lldb_ds_x86_64:
+ case lldb_es_x86_64:
+ case lldb_fs_x86_64:
+ case lldb_gs_x86_64:
+ case lldb_ss_x86_64:
+ return bytes.take_front(2);
+ break;
+ case lldb_rflags_x86_64:
+ return bytes.take_front(4);
+ break;
+ default:
+ return bytes.take_front(8);
+ break;
+ }
+}
+
+static void writeRegister(const void *reg_src, uint8_t *context,
+ const RegisterInfo &reg) {
+ llvm::MutableArrayRef<uint8_t> reg_dest = getDestRegister(context, reg);
+ memcpy(reg_dest.data(), reg_src, reg_dest.size());
+}
+
+lldb::DataBufferSP lldb_private::minidump::ConvertMinidumpContext_x86_64(
+ llvm::ArrayRef<uint8_t> source_data,
+ RegisterInfoInterface *target_reg_interface) {
+
+ const RegisterInfo *reg_info = target_reg_interface->GetRegisterInfo();
+
+ lldb::DataBufferSP result_context_buf(
+ new DataBufferHeap(target_reg_interface->GetGPRSize(), 0));
+ uint8_t *result_base = result_context_buf->GetBytes();
+
+ if (source_data.size() < sizeof(MinidumpContext_x86_64))
+ return nullptr;
+
+ const MinidumpContext_x86_64 *context;
+ consumeObject(source_data, context);
+
+ const MinidumpContext_x86_64_Flags context_flags =
+ static_cast<MinidumpContext_x86_64_Flags>(
+ static_cast<uint32_t>(context->context_flags));
+ auto x86_64_Flag = MinidumpContext_x86_64_Flags::x86_64_Flag;
+ auto ControlFlag = MinidumpContext_x86_64_Flags::Control;
+ auto IntegerFlag = MinidumpContext_x86_64_Flags::Integer;
+ auto SegmentsFlag = MinidumpContext_x86_64_Flags::Segments;
+
+ if ((context_flags & x86_64_Flag) != x86_64_Flag)
+ return nullptr;
+
+ if ((context_flags & ControlFlag) == ControlFlag) {
+ writeRegister(&context->cs, result_base, reg_info[lldb_cs_x86_64]);
+ writeRegister(&context->ss, result_base, reg_info[lldb_ss_x86_64]);
+ writeRegister(&context->eflags, result_base, reg_info[lldb_rflags_x86_64]);
+ writeRegister(&context->rsp, result_base, reg_info[lldb_rsp_x86_64]);
+ writeRegister(&context->rip, result_base, reg_info[lldb_rip_x86_64]);
+ }
+
+ if ((context_flags & SegmentsFlag) == SegmentsFlag) {
+ writeRegister(&context->ds, result_base, reg_info[lldb_ds_x86_64]);
+ writeRegister(&context->es, result_base, reg_info[lldb_es_x86_64]);
+ writeRegister(&context->fs, result_base, reg_info[lldb_fs_x86_64]);
+ writeRegister(&context->gs, result_base, reg_info[lldb_gs_x86_64]);
+ }
+
+ if ((context_flags & IntegerFlag) == IntegerFlag) {
+ writeRegister(&context->rax, result_base, reg_info[lldb_rax_x86_64]);
+ writeRegister(&context->rcx, result_base, reg_info[lldb_rcx_x86_64]);
+ writeRegister(&context->rdx, result_base, reg_info[lldb_rdx_x86_64]);
+ writeRegister(&context->rbx, result_base, reg_info[lldb_rbx_x86_64]);
+ writeRegister(&context->rbp, result_base, reg_info[lldb_rbp_x86_64]);
+ writeRegister(&context->rsi, result_base, reg_info[lldb_rsi_x86_64]);
+ writeRegister(&context->rdi, result_base, reg_info[lldb_rdi_x86_64]);
+ writeRegister(&context->r8, result_base, reg_info[lldb_r8_x86_64]);
+ writeRegister(&context->r9, result_base, reg_info[lldb_r9_x86_64]);
+ writeRegister(&context->r10, result_base, reg_info[lldb_r10_x86_64]);
+ writeRegister(&context->r11, result_base, reg_info[lldb_r11_x86_64]);
+ writeRegister(&context->r12, result_base, reg_info[lldb_r12_x86_64]);
+ writeRegister(&context->r13, result_base, reg_info[lldb_r13_x86_64]);
+ writeRegister(&context->r14, result_base, reg_info[lldb_r14_x86_64]);
+ writeRegister(&context->r15, result_base, reg_info[lldb_r15_x86_64]);
+ }
+
+ // TODO parse the floating point registers
+
+ return result_context_buf;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h
new file mode 100644
index 000000000000..34ddd477a9d1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h
@@ -0,0 +1,180 @@
+//===-- RegisterContextMinidump_x86_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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextMinidump_h_
+#define liblldb_RegisterContextMinidump_h_
+
+#include "MinidumpTypes.h"
+
+#include "Plugins/Process/Utility/RegisterInfoInterface.h"
+#include "Plugins/Process/Utility/lldb-x86-register-enums.h"
+
+#include "lldb/Target/RegisterContext.h"
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/BitmaskEnum.h"
+#include "llvm/Support/Endian.h"
+
+// C includes
+// C++ includes
+
+namespace lldb_private {
+
+namespace minidump {
+
+// This function receives an ArrayRef pointing to the bytes of the Minidump
+// register context and returns a DataBuffer that's ordered by the offsets
+// specified in the RegisterInfoInterface argument
+// This way we can reuse the already existing register contexts
+lldb::DataBufferSP
+ConvertMinidumpContext_x86_64(llvm::ArrayRef<uint8_t> source_data,
+ RegisterInfoInterface *target_reg_interface);
+
+struct Uint128 {
+ llvm::support::ulittle64_t high;
+ llvm::support::ulittle64_t low;
+};
+
+// Reference: see breakpad/crashpad source or WinNT.h
+struct MinidumpXMMSaveArea32AMD64 {
+ llvm::support::ulittle16_t control_word;
+ llvm::support::ulittle16_t status_word;
+ uint8_t tag_word;
+ uint8_t reserved1;
+ llvm::support::ulittle16_t error_opcode;
+ llvm::support::ulittle32_t error_offset;
+ llvm::support::ulittle16_t error_selector;
+ llvm::support::ulittle16_t reserved2;
+ llvm::support::ulittle32_t data_offset;
+ llvm::support::ulittle16_t data_selector;
+ llvm::support::ulittle16_t reserved3;
+ llvm::support::ulittle32_t mx_csr;
+ llvm::support::ulittle32_t mx_csr_mask;
+ Uint128 float_registers[8];
+ Uint128 xmm_registers[16];
+ uint8_t reserved4[96];
+};
+
+struct MinidumpContext_x86_64 {
+ // Register parameter home addresses.
+ llvm::support::ulittle64_t p1_home;
+ llvm::support::ulittle64_t p2_home;
+ llvm::support::ulittle64_t p3_home;
+ llvm::support::ulittle64_t p4_home;
+ llvm::support::ulittle64_t p5_home;
+ llvm::support::ulittle64_t p6_home;
+
+ // The context_flags field determines which parts
+ // of the structure are populated (have valid values)
+ llvm::support::ulittle32_t context_flags;
+ llvm::support::ulittle32_t mx_csr;
+
+ // The next register is included with
+ // MinidumpContext_x86_64_Flags::Control
+ llvm::support::ulittle16_t cs;
+
+ // The next 4 registers are included with
+ // MinidumpContext_x86_64_Flags::Segments
+ llvm::support::ulittle16_t ds;
+ llvm::support::ulittle16_t es;
+ llvm::support::ulittle16_t fs;
+ llvm::support::ulittle16_t gs;
+
+ // The next 2 registers are included with
+ // MinidumpContext_x86_64_Flags::Control
+ llvm::support::ulittle16_t ss;
+ llvm::support::ulittle32_t eflags;
+
+ // The next 6 registers are included with
+ // MinidumpContext_x86_64_Flags::DebugRegisters
+ llvm::support::ulittle64_t dr0;
+ llvm::support::ulittle64_t dr1;
+ llvm::support::ulittle64_t dr2;
+ llvm::support::ulittle64_t dr3;
+ llvm::support::ulittle64_t dr6;
+ llvm::support::ulittle64_t dr7;
+
+ // The next 4 registers are included with
+ // MinidumpContext_x86_64_Flags::Integer
+ llvm::support::ulittle64_t rax;
+ llvm::support::ulittle64_t rcx;
+ llvm::support::ulittle64_t rdx;
+ llvm::support::ulittle64_t rbx;
+
+ // The next register is included with
+ // MinidumpContext_x86_64_Flags::Control
+ llvm::support::ulittle64_t rsp;
+
+ // The next 11 registers are included with
+ // MinidumpContext_x86_64_Flags::Integer
+ llvm::support::ulittle64_t rbp;
+ llvm::support::ulittle64_t rsi;
+ llvm::support::ulittle64_t rdi;
+ llvm::support::ulittle64_t r8;
+ llvm::support::ulittle64_t r9;
+ llvm::support::ulittle64_t r10;
+ llvm::support::ulittle64_t r11;
+ llvm::support::ulittle64_t r12;
+ llvm::support::ulittle64_t r13;
+ llvm::support::ulittle64_t r14;
+ llvm::support::ulittle64_t r15;
+
+ // The next register is included with
+ // MinidumpContext_x86_64_Flags::Control
+ llvm::support::ulittle64_t rip;
+
+ // The next set of registers are included with
+ // MinidumpContext_x86_64_Flags:FloatingPoint
+ union FPR {
+ MinidumpXMMSaveArea32AMD64 flt_save;
+ struct {
+ Uint128 header[2];
+ Uint128 legacy[8];
+ Uint128 xmm[16];
+ } sse_registers;
+ };
+
+ enum {
+ VRCount = 26,
+ };
+
+ Uint128 vector_register[VRCount];
+ llvm::support::ulittle64_t vector_control;
+
+ // The next 5 registers are included with
+ // MinidumpContext_x86_64_Flags::DebugRegisters
+ llvm::support::ulittle64_t debug_control;
+ llvm::support::ulittle64_t last_branch_to_rip;
+ llvm::support::ulittle64_t last_branch_from_rip;
+ llvm::support::ulittle64_t last_exception_to_rip;
+ llvm::support::ulittle64_t last_exception_from_rip;
+};
+
+// For context_flags. These values indicate the type of
+// context stored in the structure. The high 24 bits identify the CPU, the
+// low 8 bits identify the type of context saved.
+LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
+
+enum class MinidumpContext_x86_64_Flags : uint32_t {
+ x86_64_Flag = 0x00100000,
+ Control = x86_64_Flag | 0x00000001,
+ Integer = x86_64_Flag | 0x00000002,
+ Segments = x86_64_Flag | 0x00000004,
+ FloatingPoint = x86_64_Flag | 0x00000008,
+ DebugRegisters = x86_64_Flag | 0x00000010,
+ XState = x86_64_Flag | 0x00000040,
+
+ Full = Control | Integer | FloatingPoint,
+ All = Full | Segments | DebugRegisters,
+
+ LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ All)
+};
+
+} // end namespace minidump
+} // end namespace lldb_private
+#endif // liblldb_RegisterContextMinidump_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp
new file mode 100644
index 000000000000..5262de5a94c4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp
@@ -0,0 +1,118 @@
+//===-- ThreadMinidump.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 "ThreadMinidump.h"
+
+#include "ProcessMinidump.h"
+
+#include "RegisterContextMinidump_ARM.h"
+#include "RegisterContextMinidump_ARM64.h"
+#include "RegisterContextMinidump_x86_32.h"
+#include "RegisterContextMinidump_x86_64.h"
+
+#include "Plugins/Process/Utility/RegisterContextLinux_i386.h"
+#include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h"
+#include "Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.h"
+#include "Plugins/Process/elf-core/RegisterUtilities.h"
+
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Unwind.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace minidump;
+
+ThreadMinidump::ThreadMinidump(Process &process, const minidump::Thread &td,
+ llvm::ArrayRef<uint8_t> gpregset_data)
+ : Thread(process, td.ThreadId), m_thread_reg_ctx_sp(),
+ m_gpregset_data(gpregset_data) {}
+
+ThreadMinidump::~ThreadMinidump() {}
+
+void ThreadMinidump::RefreshStateAfterStop() {}
+
+RegisterContextSP ThreadMinidump::GetRegisterContext() {
+ if (!m_reg_context_sp) {
+ m_reg_context_sp = CreateRegisterContextForFrame(nullptr);
+ }
+ return m_reg_context_sp;
+}
+
+RegisterContextSP
+ThreadMinidump::CreateRegisterContextForFrame(StackFrame *frame) {
+ RegisterContextSP reg_ctx_sp;
+ uint32_t concrete_frame_idx = 0;
+
+ if (frame)
+ concrete_frame_idx = frame->GetConcreteFrameIndex();
+
+ if (concrete_frame_idx == 0) {
+ if (m_thread_reg_ctx_sp)
+ return m_thread_reg_ctx_sp;
+
+ ProcessMinidump *process =
+ static_cast<ProcessMinidump *>(GetProcess().get());
+ ArchSpec arch = process->GetArchitecture();
+ RegisterInfoInterface *reg_interface = nullptr;
+
+ // TODO write other register contexts and add them here
+ switch (arch.GetMachine()) {
+ case llvm::Triple::x86: {
+ reg_interface = new RegisterContextLinux_i386(arch);
+ lldb::DataBufferSP buf =
+ ConvertMinidumpContext_x86_32(m_gpregset_data, reg_interface);
+ DataExtractor gpregset(buf, lldb::eByteOrderLittle, 4);
+ m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_x86_64>(
+ *this, reg_interface, gpregset,
+ llvm::ArrayRef<lldb_private::CoreNote>());
+ break;
+ }
+ case llvm::Triple::x86_64: {
+ reg_interface = new RegisterContextLinux_x86_64(arch);
+ lldb::DataBufferSP buf =
+ ConvertMinidumpContext_x86_64(m_gpregset_data, reg_interface);
+ DataExtractor gpregset(buf, lldb::eByteOrderLittle, 8);
+ m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_x86_64>(
+ *this, reg_interface, gpregset,
+ llvm::ArrayRef<lldb_private::CoreNote>());
+ break;
+ }
+ case llvm::Triple::aarch64: {
+ DataExtractor data(m_gpregset_data.data(), m_gpregset_data.size(),
+ lldb::eByteOrderLittle, 8);
+ m_thread_reg_ctx_sp =
+ std::make_shared<RegisterContextMinidump_ARM64>(*this, data);
+ break;
+ }
+ case llvm::Triple::arm: {
+ DataExtractor data(m_gpregset_data.data(), m_gpregset_data.size(),
+ lldb::eByteOrderLittle, 8);
+ const bool apple = arch.GetTriple().getVendor() == llvm::Triple::Apple;
+ m_thread_reg_ctx_sp =
+ std::make_shared<RegisterContextMinidump_ARM>(*this, data, apple);
+ break;
+ }
+ default:
+ break;
+ }
+
+ reg_ctx_sp = m_thread_reg_ctx_sp;
+ } else if (m_unwinder_up) {
+ reg_ctx_sp = m_unwinder_up->CreateRegisterContextForFrame(frame);
+ }
+
+ return reg_ctx_sp;
+}
+
+bool ThreadMinidump::CalculateStopInfo() { return false; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.h
new file mode 100644
index 000000000000..44c41bc9f50e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.h
@@ -0,0 +1,45 @@
+//===-- ThreadMinidump.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 liblldb_ThreadMinidump_h_
+#define liblldb_ThreadMinidump_h_
+
+#include "MinidumpTypes.h"
+
+#include "lldb/Target/Thread.h"
+
+
+namespace lldb_private {
+
+namespace minidump {
+
+class ThreadMinidump : public Thread {
+public:
+ ThreadMinidump(Process &process, const minidump::Thread &td,
+ llvm::ArrayRef<uint8_t> gpregset_data);
+
+ ~ThreadMinidump() override;
+
+ void RefreshStateAfterStop() override;
+
+ lldb::RegisterContextSP GetRegisterContext() override;
+
+ lldb::RegisterContextSP
+ CreateRegisterContextForFrame(StackFrame *frame) override;
+
+protected:
+ lldb::RegisterContextSP m_thread_reg_ctx_sp;
+ llvm::ArrayRef<uint8_t> m_gpregset_data;
+
+ bool CalculateStopInfo() override;
+};
+
+} // namespace minidump
+} // namespace lldb_private
+
+#endif // liblldb_ThreadMinidump_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/None/ScriptInterpreterNone.cpp b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/None/ScriptInterpreterNone.cpp
new file mode 100644
index 000000000000..7b9598379553
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/None/ScriptInterpreterNone.cpp
@@ -0,0 +1,71 @@
+//===-- ScriptInterpreterNone.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 "ScriptInterpreterNone.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StringList.h"
+
+#include "llvm/Support/Threading.h"
+
+#include <mutex>
+
+using namespace lldb;
+using namespace lldb_private;
+
+ScriptInterpreterNone::ScriptInterpreterNone(Debugger &debugger)
+ : ScriptInterpreter(debugger, eScriptLanguageNone) {}
+
+ScriptInterpreterNone::~ScriptInterpreterNone() {}
+
+bool ScriptInterpreterNone::ExecuteOneLine(llvm::StringRef command,
+ CommandReturnObject *,
+ const ExecuteScriptOptions &) {
+ m_debugger.GetErrorFile()->PutCString(
+ "error: there is no embedded script interpreter in this mode.\n");
+ return false;
+}
+
+void ScriptInterpreterNone::ExecuteInterpreterLoop() {
+ m_debugger.GetErrorFile()->PutCString(
+ "error: there is no embedded script interpreter in this mode.\n");
+}
+
+void ScriptInterpreterNone::Initialize() {
+ static llvm::once_flag g_once_flag;
+
+ llvm::call_once(g_once_flag, []() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(),
+ lldb::eScriptLanguageNone, CreateInstance);
+ });
+}
+
+void ScriptInterpreterNone::Terminate() {}
+
+lldb::ScriptInterpreterSP
+ScriptInterpreterNone::CreateInstance(Debugger &debugger) {
+ return std::make_shared<ScriptInterpreterNone>(debugger);
+}
+
+lldb_private::ConstString ScriptInterpreterNone::GetPluginNameStatic() {
+ static ConstString g_name("script-none");
+ return g_name;
+}
+
+const char *ScriptInterpreterNone::GetPluginDescriptionStatic() {
+ return "Null script interpreter";
+}
+
+lldb_private::ConstString ScriptInterpreterNone::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ScriptInterpreterNone::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h
new file mode 100644
index 000000000000..242065cc23e8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h
@@ -0,0 +1,47 @@
+//===-- ScriptInterpreterNone.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 liblldb_ScriptInterpreterNone_h_
+#define liblldb_ScriptInterpreterNone_h_
+
+#include "lldb/Interpreter/ScriptInterpreter.h"
+
+namespace lldb_private {
+
+class ScriptInterpreterNone : public ScriptInterpreter {
+public:
+ ScriptInterpreterNone(Debugger &debugger);
+
+ ~ScriptInterpreterNone() override;
+
+ bool ExecuteOneLine(
+ llvm::StringRef command, CommandReturnObject *result,
+ const ExecuteScriptOptions &options = ExecuteScriptOptions()) override;
+
+ void ExecuteInterpreterLoop() override;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ScriptInterpreterSP CreateInstance(Debugger &debugger);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ScriptInterpreterNone_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
new file mode 100644
index 000000000000..29dd037efd86
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
@@ -0,0 +1,1052 @@
+//===-- PythonDataObjects.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifdef LLDB_DISABLE_PYTHON
+
+// Python is disabled in this build
+
+#else
+
+#include "PythonDataObjects.h"
+#include "ScriptInterpreterPython.h"
+
+#include "lldb/Host/File.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Utility/Stream.h"
+
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/Errno.h"
+
+#include <stdio.h>
+
+using namespace lldb_private;
+using namespace lldb;
+
+void StructuredPythonObject::Dump(Stream &s, bool pretty_print) const {
+ s << "Python Obj: 0x" << GetValue();
+}
+
+// PythonObject
+
+void PythonObject::Dump(Stream &strm) const {
+ if (m_py_obj) {
+ FILE *file = llvm::sys::RetryAfterSignal(nullptr, ::tmpfile);
+ if (file) {
+ ::PyObject_Print(m_py_obj, file, 0);
+ const long length = ftell(file);
+ if (length) {
+ ::rewind(file);
+ std::vector<char> file_contents(length, '\0');
+ const size_t length_read =
+ ::fread(file_contents.data(), 1, file_contents.size(), file);
+ if (length_read > 0)
+ strm.Write(file_contents.data(), length_read);
+ }
+ ::fclose(file);
+ }
+ } else
+ strm.PutCString("NULL");
+}
+
+PyObjectType PythonObject::GetObjectType() const {
+ if (!IsAllocated())
+ return PyObjectType::None;
+
+ if (PythonModule::Check(m_py_obj))
+ return PyObjectType::Module;
+ if (PythonList::Check(m_py_obj))
+ return PyObjectType::List;
+ if (PythonTuple::Check(m_py_obj))
+ return PyObjectType::Tuple;
+ if (PythonDictionary::Check(m_py_obj))
+ return PyObjectType::Dictionary;
+ if (PythonString::Check(m_py_obj))
+ return PyObjectType::String;
+#if PY_MAJOR_VERSION >= 3
+ if (PythonBytes::Check(m_py_obj))
+ return PyObjectType::Bytes;
+#endif
+ if (PythonByteArray::Check(m_py_obj))
+ return PyObjectType::ByteArray;
+ if (PythonBoolean::Check(m_py_obj))
+ return PyObjectType::Boolean;
+ if (PythonInteger::Check(m_py_obj))
+ return PyObjectType::Integer;
+ if (PythonFile::Check(m_py_obj))
+ return PyObjectType::File;
+ if (PythonCallable::Check(m_py_obj))
+ return PyObjectType::Callable;
+ return PyObjectType::Unknown;
+}
+
+PythonString PythonObject::Repr() const {
+ if (!m_py_obj)
+ return PythonString();
+ PyObject *repr = PyObject_Repr(m_py_obj);
+ if (!repr)
+ return PythonString();
+ return PythonString(PyRefType::Owned, repr);
+}
+
+PythonString PythonObject::Str() const {
+ if (!m_py_obj)
+ return PythonString();
+ PyObject *str = PyObject_Str(m_py_obj);
+ if (!str)
+ return PythonString();
+ return PythonString(PyRefType::Owned, str);
+}
+
+PythonObject
+PythonObject::ResolveNameWithDictionary(llvm::StringRef name,
+ const PythonDictionary &dict) {
+ size_t dot_pos = name.find('.');
+ llvm::StringRef piece = name.substr(0, dot_pos);
+ PythonObject result = dict.GetItemForKey(PythonString(piece));
+ if (dot_pos == llvm::StringRef::npos) {
+ // There was no dot, we're done.
+ return result;
+ }
+
+ // There was a dot. The remaining portion of the name should be looked up in
+ // the context of the object that was found in the dictionary.
+ return result.ResolveName(name.substr(dot_pos + 1));
+}
+
+PythonObject PythonObject::ResolveName(llvm::StringRef name) const {
+ // Resolve the name in the context of the specified object. If, for example,
+ // `this` refers to a PyModule, then this will look for `name` in this
+ // module. If `this` refers to a PyType, then it will resolve `name` as an
+ // attribute of that type. If `this` refers to an instance of an object,
+ // then it will resolve `name` as the value of the specified field.
+ //
+ // This function handles dotted names so that, for example, if `m_py_obj`
+ // refers to the `sys` module, and `name` == "path.append", then it will find
+ // the function `sys.path.append`.
+
+ size_t dot_pos = name.find('.');
+ if (dot_pos == llvm::StringRef::npos) {
+ // No dots in the name, we should be able to find the value immediately as
+ // an attribute of `m_py_obj`.
+ return GetAttributeValue(name);
+ }
+
+ // Look up the first piece of the name, and resolve the rest as a child of
+ // that.
+ PythonObject parent = ResolveName(name.substr(0, dot_pos));
+ if (!parent.IsAllocated())
+ return PythonObject();
+
+ // Tail recursion.. should be optimized by the compiler
+ return parent.ResolveName(name.substr(dot_pos + 1));
+}
+
+bool PythonObject::HasAttribute(llvm::StringRef attr) const {
+ if (!IsValid())
+ return false;
+ PythonString py_attr(attr);
+ return !!PyObject_HasAttr(m_py_obj, py_attr.get());
+}
+
+PythonObject PythonObject::GetAttributeValue(llvm::StringRef attr) const {
+ if (!IsValid())
+ return PythonObject();
+
+ PythonString py_attr(attr);
+ if (!PyObject_HasAttr(m_py_obj, py_attr.get()))
+ return PythonObject();
+
+ return PythonObject(PyRefType::Owned,
+ PyObject_GetAttr(m_py_obj, py_attr.get()));
+}
+
+bool PythonObject::IsNone() const { return m_py_obj == Py_None; }
+
+bool PythonObject::IsValid() const { return m_py_obj != nullptr; }
+
+bool PythonObject::IsAllocated() const { return IsValid() && !IsNone(); }
+
+StructuredData::ObjectSP PythonObject::CreateStructuredObject() const {
+ switch (GetObjectType()) {
+ case PyObjectType::Dictionary:
+ return PythonDictionary(PyRefType::Borrowed, m_py_obj)
+ .CreateStructuredDictionary();
+ case PyObjectType::Boolean:
+ return PythonBoolean(PyRefType::Borrowed, m_py_obj)
+ .CreateStructuredBoolean();
+ case PyObjectType::Integer:
+ return PythonInteger(PyRefType::Borrowed, m_py_obj)
+ .CreateStructuredInteger();
+ case PyObjectType::List:
+ return PythonList(PyRefType::Borrowed, m_py_obj).CreateStructuredArray();
+ case PyObjectType::String:
+ return PythonString(PyRefType::Borrowed, m_py_obj).CreateStructuredString();
+ case PyObjectType::Bytes:
+ return PythonBytes(PyRefType::Borrowed, m_py_obj).CreateStructuredString();
+ case PyObjectType::ByteArray:
+ return PythonByteArray(PyRefType::Borrowed, m_py_obj)
+ .CreateStructuredString();
+ case PyObjectType::None:
+ return StructuredData::ObjectSP();
+ default:
+ return StructuredData::ObjectSP(new StructuredPythonObject(m_py_obj));
+ }
+}
+
+// PythonString
+PythonBytes::PythonBytes() : PythonObject() {}
+
+PythonBytes::PythonBytes(llvm::ArrayRef<uint8_t> bytes) : PythonObject() {
+ SetBytes(bytes);
+}
+
+PythonBytes::PythonBytes(const uint8_t *bytes, size_t length) : PythonObject() {
+ SetBytes(llvm::ArrayRef<uint8_t>(bytes, length));
+}
+
+PythonBytes::PythonBytes(PyRefType type, PyObject *py_obj) : PythonObject() {
+ Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a string
+}
+
+PythonBytes::~PythonBytes() {}
+
+bool PythonBytes::Check(PyObject *py_obj) {
+ if (!py_obj)
+ return false;
+ return PyBytes_Check(py_obj);
+}
+
+void PythonBytes::Reset(PyRefType type, PyObject *py_obj) {
+ // Grab the desired reference type so that if we end up rejecting `py_obj` it
+ // still gets decremented if necessary.
+ PythonObject result(type, py_obj);
+
+ if (!PythonBytes::Check(py_obj)) {
+ PythonObject::Reset();
+ return;
+ }
+
+ // Calling PythonObject::Reset(const PythonObject&) will lead to stack
+ // overflow since it calls back into the virtual implementation.
+ PythonObject::Reset(PyRefType::Borrowed, result.get());
+}
+
+llvm::ArrayRef<uint8_t> PythonBytes::GetBytes() const {
+ if (!IsValid())
+ return llvm::ArrayRef<uint8_t>();
+
+ Py_ssize_t size;
+ char *c;
+
+ PyBytes_AsStringAndSize(m_py_obj, &c, &size);
+ return llvm::ArrayRef<uint8_t>(reinterpret_cast<uint8_t *>(c), size);
+}
+
+size_t PythonBytes::GetSize() const {
+ if (!IsValid())
+ return 0;
+ return PyBytes_Size(m_py_obj);
+}
+
+void PythonBytes::SetBytes(llvm::ArrayRef<uint8_t> bytes) {
+ const char *data = reinterpret_cast<const char *>(bytes.data());
+ PyObject *py_bytes = PyBytes_FromStringAndSize(data, bytes.size());
+ PythonObject::Reset(PyRefType::Owned, py_bytes);
+}
+
+StructuredData::StringSP PythonBytes::CreateStructuredString() const {
+ StructuredData::StringSP result(new StructuredData::String);
+ Py_ssize_t size;
+ char *c;
+ PyBytes_AsStringAndSize(m_py_obj, &c, &size);
+ result->SetValue(std::string(c, size));
+ return result;
+}
+
+PythonByteArray::PythonByteArray(llvm::ArrayRef<uint8_t> bytes)
+ : PythonByteArray(bytes.data(), bytes.size()) {}
+
+PythonByteArray::PythonByteArray(const uint8_t *bytes, size_t length) {
+ const char *str = reinterpret_cast<const char *>(bytes);
+ Reset(PyRefType::Owned, PyByteArray_FromStringAndSize(str, length));
+}
+
+PythonByteArray::PythonByteArray(PyRefType type, PyObject *o) {
+ Reset(type, o);
+}
+
+PythonByteArray::PythonByteArray(const PythonBytes &object)
+ : PythonObject(object) {}
+
+PythonByteArray::~PythonByteArray() {}
+
+bool PythonByteArray::Check(PyObject *py_obj) {
+ if (!py_obj)
+ return false;
+ return PyByteArray_Check(py_obj);
+}
+
+void PythonByteArray::Reset(PyRefType type, PyObject *py_obj) {
+ // Grab the desired reference type so that if we end up rejecting `py_obj` it
+ // still gets decremented if necessary.
+ PythonObject result(type, py_obj);
+
+ if (!PythonByteArray::Check(py_obj)) {
+ PythonObject::Reset();
+ return;
+ }
+
+ // Calling PythonObject::Reset(const PythonObject&) will lead to stack
+ // overflow since it calls back into the virtual implementation.
+ PythonObject::Reset(PyRefType::Borrowed, result.get());
+}
+
+llvm::ArrayRef<uint8_t> PythonByteArray::GetBytes() const {
+ if (!IsValid())
+ return llvm::ArrayRef<uint8_t>();
+
+ char *c = PyByteArray_AsString(m_py_obj);
+ size_t size = GetSize();
+ return llvm::ArrayRef<uint8_t>(reinterpret_cast<uint8_t *>(c), size);
+}
+
+size_t PythonByteArray::GetSize() const {
+ if (!IsValid())
+ return 0;
+
+ return PyByteArray_Size(m_py_obj);
+}
+
+StructuredData::StringSP PythonByteArray::CreateStructuredString() const {
+ StructuredData::StringSP result(new StructuredData::String);
+ llvm::ArrayRef<uint8_t> bytes = GetBytes();
+ const char *str = reinterpret_cast<const char *>(bytes.data());
+ result->SetValue(std::string(str, bytes.size()));
+ return result;
+}
+
+// PythonString
+
+PythonString::PythonString(PyRefType type, PyObject *py_obj) : PythonObject() {
+ Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a string
+}
+
+PythonString::PythonString(llvm::StringRef string) : PythonObject() {
+ SetString(string);
+}
+
+PythonString::PythonString(const char *string) : PythonObject() {
+ SetString(llvm::StringRef(string));
+}
+
+PythonString::PythonString() : PythonObject() {}
+
+PythonString::~PythonString() {}
+
+bool PythonString::Check(PyObject *py_obj) {
+ if (!py_obj)
+ return false;
+
+ if (PyUnicode_Check(py_obj))
+ return true;
+#if PY_MAJOR_VERSION < 3
+ if (PyString_Check(py_obj))
+ return true;
+#endif
+ return false;
+}
+
+void PythonString::Reset(PyRefType type, PyObject *py_obj) {
+ // Grab the desired reference type so that if we end up rejecting `py_obj` it
+ // still gets decremented if necessary.
+ PythonObject result(type, py_obj);
+
+ if (!PythonString::Check(py_obj)) {
+ PythonObject::Reset();
+ return;
+ }
+#if PY_MAJOR_VERSION < 3
+ // In Python 2, Don't store PyUnicode objects directly, because we need
+ // access to their underlying character buffers which Python 2 doesn't
+ // provide.
+ if (PyUnicode_Check(py_obj))
+ result.Reset(PyRefType::Owned, PyUnicode_AsUTF8String(result.get()));
+#endif
+ // Calling PythonObject::Reset(const PythonObject&) will lead to stack
+ // overflow since it calls back into the virtual implementation.
+ PythonObject::Reset(PyRefType::Borrowed, result.get());
+}
+
+llvm::StringRef PythonString::GetString() const {
+ if (!IsValid())
+ return llvm::StringRef();
+
+ Py_ssize_t size;
+ const char *data;
+
+#if PY_MAJOR_VERSION >= 3
+ data = PyUnicode_AsUTF8AndSize(m_py_obj, &size);
+#else
+ char *c;
+ PyString_AsStringAndSize(m_py_obj, &c, &size);
+ data = c;
+#endif
+ return llvm::StringRef(data, size);
+}
+
+size_t PythonString::GetSize() const {
+ if (IsValid()) {
+#if PY_MAJOR_VERSION >= 3
+ return PyUnicode_GetSize(m_py_obj);
+#else
+ return PyString_Size(m_py_obj);
+#endif
+ }
+ return 0;
+}
+
+void PythonString::SetString(llvm::StringRef string) {
+#if PY_MAJOR_VERSION >= 3
+ PyObject *unicode = PyUnicode_FromStringAndSize(string.data(), string.size());
+ PythonObject::Reset(PyRefType::Owned, unicode);
+#else
+ PyObject *str = PyString_FromStringAndSize(string.data(), string.size());
+ PythonObject::Reset(PyRefType::Owned, str);
+#endif
+}
+
+StructuredData::StringSP PythonString::CreateStructuredString() const {
+ StructuredData::StringSP result(new StructuredData::String);
+ result->SetValue(GetString());
+ return result;
+}
+
+// PythonInteger
+
+PythonInteger::PythonInteger() : PythonObject() {}
+
+PythonInteger::PythonInteger(PyRefType type, PyObject *py_obj)
+ : PythonObject() {
+ Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a integer type
+}
+
+PythonInteger::PythonInteger(int64_t value) : PythonObject() {
+ SetInteger(value);
+}
+
+PythonInteger::~PythonInteger() {}
+
+bool PythonInteger::Check(PyObject *py_obj) {
+ if (!py_obj)
+ return false;
+
+#if PY_MAJOR_VERSION >= 3
+ // Python 3 does not have PyInt_Check. There is only one type of integral
+ // value, long.
+ return PyLong_Check(py_obj);
+#else
+ return PyLong_Check(py_obj) || PyInt_Check(py_obj);
+#endif
+}
+
+void PythonInteger::Reset(PyRefType type, PyObject *py_obj) {
+ // Grab the desired reference type so that if we end up rejecting `py_obj` it
+ // still gets decremented if necessary.
+ PythonObject result(type, py_obj);
+
+ if (!PythonInteger::Check(py_obj)) {
+ PythonObject::Reset();
+ return;
+ }
+
+#if PY_MAJOR_VERSION < 3
+ // Always store this as a PyLong, which makes interoperability between Python
+ // 2.x and Python 3.x easier. This is only necessary in 2.x, since 3.x
+ // doesn't even have a PyInt.
+ if (PyInt_Check(py_obj)) {
+ // Since we converted the original object to a different type, the new
+ // object is an owned object regardless of the ownership semantics
+ // requested by the user.
+ result.Reset(PyRefType::Owned, PyLong_FromLongLong(PyInt_AsLong(py_obj)));
+ }
+#endif
+
+ assert(PyLong_Check(result.get()) &&
+ "Couldn't get a PyLong from this PyObject");
+
+ // Calling PythonObject::Reset(const PythonObject&) will lead to stack
+ // overflow since it calls back into the virtual implementation.
+ PythonObject::Reset(PyRefType::Borrowed, result.get());
+}
+
+int64_t PythonInteger::GetInteger() const {
+ if (m_py_obj) {
+ assert(PyLong_Check(m_py_obj) &&
+ "PythonInteger::GetInteger has a PyObject that isn't a PyLong");
+
+ int overflow = 0;
+ int64_t result = PyLong_AsLongLongAndOverflow(m_py_obj, &overflow);
+ if (overflow != 0) {
+ // We got an integer that overflows, like 18446744072853913392L we can't
+ // use PyLong_AsLongLong() as it will return 0xffffffffffffffff. If we
+ // use the unsigned long long it will work as expected.
+ const uint64_t uval = PyLong_AsUnsignedLongLong(m_py_obj);
+ result = static_cast<int64_t>(uval);
+ }
+ return result;
+ }
+ return UINT64_MAX;
+}
+
+void PythonInteger::SetInteger(int64_t value) {
+ PythonObject::Reset(PyRefType::Owned, PyLong_FromLongLong(value));
+}
+
+StructuredData::IntegerSP PythonInteger::CreateStructuredInteger() const {
+ StructuredData::IntegerSP result(new StructuredData::Integer);
+ result->SetValue(GetInteger());
+ return result;
+}
+
+// PythonBoolean
+
+PythonBoolean::PythonBoolean(PyRefType type, PyObject *py_obj)
+ : PythonObject() {
+ Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a boolean type
+}
+
+PythonBoolean::PythonBoolean(bool value) {
+ SetValue(value);
+}
+
+bool PythonBoolean::Check(PyObject *py_obj) {
+ return py_obj ? PyBool_Check(py_obj) : false;
+}
+
+void PythonBoolean::Reset(PyRefType type, PyObject *py_obj) {
+ // Grab the desired reference type so that if we end up rejecting `py_obj` it
+ // still gets decremented if necessary.
+ PythonObject result(type, py_obj);
+
+ if (!PythonBoolean::Check(py_obj)) {
+ PythonObject::Reset();
+ return;
+ }
+
+ // Calling PythonObject::Reset(const PythonObject&) will lead to stack
+ // overflow since it calls back into the virtual implementation.
+ PythonObject::Reset(PyRefType::Borrowed, result.get());
+}
+
+bool PythonBoolean::GetValue() const {
+ return m_py_obj ? PyObject_IsTrue(m_py_obj) : false;
+}
+
+void PythonBoolean::SetValue(bool value) {
+ PythonObject::Reset(PyRefType::Owned, PyBool_FromLong(value));
+}
+
+StructuredData::BooleanSP PythonBoolean::CreateStructuredBoolean() const {
+ StructuredData::BooleanSP result(new StructuredData::Boolean);
+ result->SetValue(GetValue());
+ return result;
+}
+
+// PythonList
+
+PythonList::PythonList(PyInitialValue value) : PythonObject() {
+ if (value == PyInitialValue::Empty)
+ Reset(PyRefType::Owned, PyList_New(0));
+}
+
+PythonList::PythonList(int list_size) : PythonObject() {
+ Reset(PyRefType::Owned, PyList_New(list_size));
+}
+
+PythonList::PythonList(PyRefType type, PyObject *py_obj) : PythonObject() {
+ Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a list
+}
+
+PythonList::~PythonList() {}
+
+bool PythonList::Check(PyObject *py_obj) {
+ if (!py_obj)
+ return false;
+ return PyList_Check(py_obj);
+}
+
+void PythonList::Reset(PyRefType type, PyObject *py_obj) {
+ // Grab the desired reference type so that if we end up rejecting `py_obj` it
+ // still gets decremented if necessary.
+ PythonObject result(type, py_obj);
+
+ if (!PythonList::Check(py_obj)) {
+ PythonObject::Reset();
+ return;
+ }
+
+ // Calling PythonObject::Reset(const PythonObject&) will lead to stack
+ // overflow since it calls back into the virtual implementation.
+ PythonObject::Reset(PyRefType::Borrowed, result.get());
+}
+
+uint32_t PythonList::GetSize() const {
+ if (IsValid())
+ return PyList_GET_SIZE(m_py_obj);
+ return 0;
+}
+
+PythonObject PythonList::GetItemAtIndex(uint32_t index) const {
+ if (IsValid())
+ return PythonObject(PyRefType::Borrowed, PyList_GetItem(m_py_obj, index));
+ return PythonObject();
+}
+
+void PythonList::SetItemAtIndex(uint32_t index, const PythonObject &object) {
+ if (IsAllocated() && object.IsValid()) {
+ // PyList_SetItem is documented to "steal" a reference, so we need to
+ // convert it to an owned reference by incrementing it.
+ Py_INCREF(object.get());
+ PyList_SetItem(m_py_obj, index, object.get());
+ }
+}
+
+void PythonList::AppendItem(const PythonObject &object) {
+ if (IsAllocated() && object.IsValid()) {
+ // `PyList_Append` does *not* steal a reference, so do not call `Py_INCREF`
+ // here like we do with `PyList_SetItem`.
+ PyList_Append(m_py_obj, object.get());
+ }
+}
+
+StructuredData::ArraySP PythonList::CreateStructuredArray() const {
+ StructuredData::ArraySP result(new StructuredData::Array);
+ uint32_t count = GetSize();
+ for (uint32_t i = 0; i < count; ++i) {
+ PythonObject obj = GetItemAtIndex(i);
+ result->AddItem(obj.CreateStructuredObject());
+ }
+ return result;
+}
+
+// PythonTuple
+
+PythonTuple::PythonTuple(PyInitialValue value) : PythonObject() {
+ if (value == PyInitialValue::Empty)
+ Reset(PyRefType::Owned, PyTuple_New(0));
+}
+
+PythonTuple::PythonTuple(int tuple_size) : PythonObject() {
+ Reset(PyRefType::Owned, PyTuple_New(tuple_size));
+}
+
+PythonTuple::PythonTuple(PyRefType type, PyObject *py_obj) : PythonObject() {
+ Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a tuple
+}
+
+PythonTuple::PythonTuple(std::initializer_list<PythonObject> objects) {
+ m_py_obj = PyTuple_New(objects.size());
+
+ uint32_t idx = 0;
+ for (auto object : objects) {
+ if (object.IsValid())
+ SetItemAtIndex(idx, object);
+ idx++;
+ }
+}
+
+PythonTuple::PythonTuple(std::initializer_list<PyObject *> objects) {
+ m_py_obj = PyTuple_New(objects.size());
+
+ uint32_t idx = 0;
+ for (auto py_object : objects) {
+ PythonObject object(PyRefType::Borrowed, py_object);
+ if (object.IsValid())
+ SetItemAtIndex(idx, object);
+ idx++;
+ }
+}
+
+PythonTuple::~PythonTuple() {}
+
+bool PythonTuple::Check(PyObject *py_obj) {
+ if (!py_obj)
+ return false;
+ return PyTuple_Check(py_obj);
+}
+
+void PythonTuple::Reset(PyRefType type, PyObject *py_obj) {
+ // Grab the desired reference type so that if we end up rejecting `py_obj` it
+ // still gets decremented if necessary.
+ PythonObject result(type, py_obj);
+
+ if (!PythonTuple::Check(py_obj)) {
+ PythonObject::Reset();
+ return;
+ }
+
+ // Calling PythonObject::Reset(const PythonObject&) will lead to stack
+ // overflow since it calls back into the virtual implementation.
+ PythonObject::Reset(PyRefType::Borrowed, result.get());
+}
+
+uint32_t PythonTuple::GetSize() const {
+ if (IsValid())
+ return PyTuple_GET_SIZE(m_py_obj);
+ return 0;
+}
+
+PythonObject PythonTuple::GetItemAtIndex(uint32_t index) const {
+ if (IsValid())
+ return PythonObject(PyRefType::Borrowed, PyTuple_GetItem(m_py_obj, index));
+ return PythonObject();
+}
+
+void PythonTuple::SetItemAtIndex(uint32_t index, const PythonObject &object) {
+ if (IsAllocated() && object.IsValid()) {
+ // PyTuple_SetItem is documented to "steal" a reference, so we need to
+ // convert it to an owned reference by incrementing it.
+ Py_INCREF(object.get());
+ PyTuple_SetItem(m_py_obj, index, object.get());
+ }
+}
+
+StructuredData::ArraySP PythonTuple::CreateStructuredArray() const {
+ StructuredData::ArraySP result(new StructuredData::Array);
+ uint32_t count = GetSize();
+ for (uint32_t i = 0; i < count; ++i) {
+ PythonObject obj = GetItemAtIndex(i);
+ result->AddItem(obj.CreateStructuredObject());
+ }
+ return result;
+}
+
+// PythonDictionary
+
+PythonDictionary::PythonDictionary(PyInitialValue value) : PythonObject() {
+ if (value == PyInitialValue::Empty)
+ Reset(PyRefType::Owned, PyDict_New());
+}
+
+PythonDictionary::PythonDictionary(PyRefType type, PyObject *py_obj)
+ : PythonObject() {
+ Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a dictionary
+}
+
+PythonDictionary::~PythonDictionary() {}
+
+bool PythonDictionary::Check(PyObject *py_obj) {
+ if (!py_obj)
+ return false;
+
+ return PyDict_Check(py_obj);
+}
+
+void PythonDictionary::Reset(PyRefType type, PyObject *py_obj) {
+ // Grab the desired reference type so that if we end up rejecting `py_obj` it
+ // still gets decremented if necessary.
+ PythonObject result(type, py_obj);
+
+ if (!PythonDictionary::Check(py_obj)) {
+ PythonObject::Reset();
+ return;
+ }
+
+ // Calling PythonObject::Reset(const PythonObject&) will lead to stack
+ // overflow since it calls back into the virtual implementation.
+ PythonObject::Reset(PyRefType::Borrowed, result.get());
+}
+
+uint32_t PythonDictionary::GetSize() const {
+ if (IsValid())
+ return PyDict_Size(m_py_obj);
+ return 0;
+}
+
+PythonList PythonDictionary::GetKeys() const {
+ if (IsValid())
+ return PythonList(PyRefType::Owned, PyDict_Keys(m_py_obj));
+ return PythonList(PyInitialValue::Invalid);
+}
+
+PythonObject PythonDictionary::GetItemForKey(const PythonObject &key) const {
+ if (IsAllocated() && key.IsValid())
+ return PythonObject(PyRefType::Borrowed,
+ PyDict_GetItem(m_py_obj, key.get()));
+ return PythonObject();
+}
+
+void PythonDictionary::SetItemForKey(const PythonObject &key,
+ const PythonObject &value) {
+ if (IsAllocated() && key.IsValid() && value.IsValid())
+ PyDict_SetItem(m_py_obj, key.get(), value.get());
+}
+
+StructuredData::DictionarySP
+PythonDictionary::CreateStructuredDictionary() const {
+ StructuredData::DictionarySP result(new StructuredData::Dictionary);
+ PythonList keys(GetKeys());
+ uint32_t num_keys = keys.GetSize();
+ for (uint32_t i = 0; i < num_keys; ++i) {
+ PythonObject key = keys.GetItemAtIndex(i);
+ PythonObject value = GetItemForKey(key);
+ StructuredData::ObjectSP structured_value = value.CreateStructuredObject();
+ result->AddItem(key.Str().GetString(), structured_value);
+ }
+ return result;
+}
+
+PythonModule::PythonModule() : PythonObject() {}
+
+PythonModule::PythonModule(PyRefType type, PyObject *py_obj) {
+ Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a module
+}
+
+PythonModule::~PythonModule() {}
+
+PythonModule PythonModule::BuiltinsModule() {
+#if PY_MAJOR_VERSION >= 3
+ return AddModule("builtins");
+#else
+ return AddModule("__builtin__");
+#endif
+}
+
+PythonModule PythonModule::MainModule() { return AddModule("__main__"); }
+
+PythonModule PythonModule::AddModule(llvm::StringRef module) {
+ std::string str = module.str();
+ return PythonModule(PyRefType::Borrowed, PyImport_AddModule(str.c_str()));
+}
+
+PythonModule PythonModule::ImportModule(llvm::StringRef module) {
+ std::string str = module.str();
+ return PythonModule(PyRefType::Owned, PyImport_ImportModule(str.c_str()));
+}
+
+bool PythonModule::Check(PyObject *py_obj) {
+ if (!py_obj)
+ return false;
+
+ return PyModule_Check(py_obj);
+}
+
+void PythonModule::Reset(PyRefType type, PyObject *py_obj) {
+ // Grab the desired reference type so that if we end up rejecting `py_obj` it
+ // still gets decremented if necessary.
+ PythonObject result(type, py_obj);
+
+ if (!PythonModule::Check(py_obj)) {
+ PythonObject::Reset();
+ return;
+ }
+
+ // Calling PythonObject::Reset(const PythonObject&) will lead to stack
+ // overflow since it calls back into the virtual implementation.
+ PythonObject::Reset(PyRefType::Borrowed, result.get());
+}
+
+PythonDictionary PythonModule::GetDictionary() const {
+ return PythonDictionary(PyRefType::Borrowed, PyModule_GetDict(m_py_obj));
+}
+
+PythonCallable::PythonCallable() : PythonObject() {}
+
+PythonCallable::PythonCallable(PyRefType type, PyObject *py_obj) {
+ Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a callable
+}
+
+PythonCallable::~PythonCallable() {}
+
+bool PythonCallable::Check(PyObject *py_obj) {
+ if (!py_obj)
+ return false;
+
+ return PyCallable_Check(py_obj);
+}
+
+void PythonCallable::Reset(PyRefType type, PyObject *py_obj) {
+ // Grab the desired reference type so that if we end up rejecting `py_obj` it
+ // still gets decremented if necessary.
+ PythonObject result(type, py_obj);
+
+ if (!PythonCallable::Check(py_obj)) {
+ PythonObject::Reset();
+ return;
+ }
+
+ // Calling PythonObject::Reset(const PythonObject&) will lead to stack
+ // overflow since it calls back into the virtual implementation.
+ PythonObject::Reset(PyRefType::Borrowed, result.get());
+}
+
+PythonCallable::ArgInfo PythonCallable::GetNumArguments() const {
+ ArgInfo result = {0, false, false, false};
+ if (!IsValid())
+ return result;
+
+ PyObject *py_func_obj = m_py_obj;
+ if (PyMethod_Check(py_func_obj)) {
+ py_func_obj = PyMethod_GET_FUNCTION(py_func_obj);
+ PythonObject im_self = GetAttributeValue("im_self");
+ if (im_self.IsValid() && !im_self.IsNone())
+ result.is_bound_method = true;
+ } else {
+ // see if this is a callable object with an __call__ method
+ if (!PyFunction_Check(py_func_obj)) {
+ PythonObject __call__ = GetAttributeValue("__call__");
+ if (__call__.IsValid()) {
+ auto __callable__ = __call__.AsType<PythonCallable>();
+ if (__callable__.IsValid()) {
+ py_func_obj = PyMethod_GET_FUNCTION(__callable__.get());
+ PythonObject im_self = GetAttributeValue("im_self");
+ if (im_self.IsValid() && !im_self.IsNone())
+ result.is_bound_method = true;
+ }
+ }
+ }
+ }
+
+ if (!py_func_obj)
+ return result;
+
+ PyCodeObject *code = (PyCodeObject *)PyFunction_GET_CODE(py_func_obj);
+ if (!code)
+ return result;
+
+ result.count = code->co_argcount;
+ result.has_varargs = !!(code->co_flags & CO_VARARGS);
+ result.has_kwargs = !!(code->co_flags & CO_VARKEYWORDS);
+ return result;
+}
+
+PythonObject PythonCallable::operator()() {
+ return PythonObject(PyRefType::Owned, PyObject_CallObject(m_py_obj, nullptr));
+}
+
+PythonObject PythonCallable::
+operator()(std::initializer_list<PyObject *> args) {
+ PythonTuple arg_tuple(args);
+ return PythonObject(PyRefType::Owned,
+ PyObject_CallObject(m_py_obj, arg_tuple.get()));
+}
+
+PythonObject PythonCallable::
+operator()(std::initializer_list<PythonObject> args) {
+ PythonTuple arg_tuple(args);
+ return PythonObject(PyRefType::Owned,
+ PyObject_CallObject(m_py_obj, arg_tuple.get()));
+}
+
+PythonFile::PythonFile() : PythonObject() {}
+
+PythonFile::PythonFile(File &file, const char *mode) { Reset(file, mode); }
+
+PythonFile::PythonFile(const char *path, const char *mode) {
+ lldb_private::File file;
+ FileSystem::Instance().Open(file, FileSpec(path), GetOptionsFromMode(mode));
+ Reset(file, mode);
+}
+
+PythonFile::PythonFile(PyRefType type, PyObject *o) { Reset(type, o); }
+
+PythonFile::~PythonFile() {}
+
+bool PythonFile::Check(PyObject *py_obj) {
+#if PY_MAJOR_VERSION < 3
+ return PyFile_Check(py_obj);
+#else
+ // In Python 3, there is no `PyFile_Check`, and in fact PyFile is not even a
+ // first-class object type anymore. `PyFile_FromFd` is just a thin wrapper
+ // over `io.open()`, which returns some object derived from `io.IOBase`. As a
+ // result, the only way to detect a file in Python 3 is to check whether it
+ // inherits from `io.IOBase`. Since it is possible for non-files to also
+ // inherit from `io.IOBase`, we additionally verify that it has the `fileno`
+ // attribute, which should guarantee that it is backed by the file system.
+ PythonObject io_module(PyRefType::Owned, PyImport_ImportModule("io"));
+ PythonDictionary io_dict(PyRefType::Borrowed,
+ PyModule_GetDict(io_module.get()));
+ PythonObject io_base_class = io_dict.GetItemForKey(PythonString("IOBase"));
+
+ PythonObject object_type(PyRefType::Owned, PyObject_Type(py_obj));
+
+ if (1 != PyObject_IsSubclass(object_type.get(), io_base_class.get()))
+ return false;
+ if (!object_type.HasAttribute("fileno"))
+ return false;
+
+ return true;
+#endif
+}
+
+void PythonFile::Reset(PyRefType type, PyObject *py_obj) {
+ // Grab the desired reference type so that if we end up rejecting `py_obj` it
+ // still gets decremented if necessary.
+ PythonObject result(type, py_obj);
+
+ if (!PythonFile::Check(py_obj)) {
+ PythonObject::Reset();
+ return;
+ }
+
+ // Calling PythonObject::Reset(const PythonObject&) will lead to stack
+ // overflow since it calls back into the virtual implementation.
+ PythonObject::Reset(PyRefType::Borrowed, result.get());
+}
+
+void PythonFile::Reset(File &file, const char *mode) {
+ if (!file.IsValid()) {
+ Reset();
+ return;
+ }
+
+ char *cmode = const_cast<char *>(mode);
+#if PY_MAJOR_VERSION >= 3
+ Reset(PyRefType::Owned, PyFile_FromFd(file.GetDescriptor(), nullptr, cmode,
+ -1, nullptr, "ignore", nullptr, 0));
+#else
+ // Read through the Python source, doesn't seem to modify these strings
+ Reset(PyRefType::Owned,
+ PyFile_FromFile(file.GetStream(), const_cast<char *>(""), cmode,
+ nullptr));
+#endif
+}
+
+uint32_t PythonFile::GetOptionsFromMode(llvm::StringRef mode) {
+ if (mode.empty())
+ return 0;
+
+ return llvm::StringSwitch<uint32_t>(mode.str())
+ .Case("r", File::eOpenOptionRead)
+ .Case("w", File::eOpenOptionWrite)
+ .Case("a", File::eOpenOptionWrite | File::eOpenOptionAppend |
+ File::eOpenOptionCanCreate)
+ .Case("r+", File::eOpenOptionRead | File::eOpenOptionWrite)
+ .Case("w+", File::eOpenOptionRead | File::eOpenOptionWrite |
+ File::eOpenOptionCanCreate | File::eOpenOptionTruncate)
+ .Case("a+", File::eOpenOptionRead | File::eOpenOptionWrite |
+ File::eOpenOptionAppend | File::eOpenOptionCanCreate)
+ .Default(0);
+}
+
+bool PythonFile::GetUnderlyingFile(File &file) const {
+ if (!IsValid())
+ return false;
+
+ file.Close();
+ // We don't own the file descriptor returned by this function, make sure the
+ // File object knows about that.
+ file.SetDescriptor(PyObject_AsFileDescriptor(m_py_obj), false);
+ PythonString py_mode = GetAttributeValue("mode").AsType<PythonString>();
+ file.SetOptions(PythonFile::GetOptionsFromMode(py_mode.GetString()));
+ return file.IsValid();
+}
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
new file mode 100644
index 000000000000..049ce90bb1b1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
@@ -0,0 +1,479 @@
+//===-- PythonDataObjects.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_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONDATAOBJECTS_H
+#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONDATAOBJECTS_H
+
+#ifndef LLDB_DISABLE_PYTHON
+
+// LLDB Python header must be included first
+#include "lldb-python.h"
+
+#include "lldb/Host/File.h"
+#include "lldb/Utility/StructuredData.h"
+
+#include "llvm/ADT/ArrayRef.h"
+
+namespace lldb_private {
+
+class PythonBytes;
+class PythonString;
+class PythonList;
+class PythonDictionary;
+class PythonInteger;
+
+class StructuredPythonObject : public StructuredData::Generic {
+public:
+ StructuredPythonObject() : StructuredData::Generic() {}
+
+ StructuredPythonObject(void *obj) : StructuredData::Generic(obj) {
+ Py_XINCREF(GetValue());
+ }
+
+ ~StructuredPythonObject() override {
+ if (Py_IsInitialized())
+ Py_XDECREF(GetValue());
+ SetValue(nullptr);
+ }
+
+ bool IsValid() const override { return GetValue() && GetValue() != Py_None; }
+
+ void Dump(Stream &s, bool pretty_print = true) const override;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(StructuredPythonObject);
+};
+
+enum class PyObjectType {
+ Unknown,
+ None,
+ Boolean,
+ Integer,
+ Dictionary,
+ List,
+ String,
+ Bytes,
+ ByteArray,
+ Module,
+ Callable,
+ Tuple,
+ File
+};
+
+enum class PyRefType {
+ Borrowed, // We are not given ownership of the incoming PyObject.
+ // We cannot safely hold it without calling Py_INCREF.
+ Owned // We have ownership of the incoming PyObject. We should
+ // not call Py_INCREF.
+};
+
+enum class PyInitialValue { Invalid, Empty };
+
+class PythonObject {
+public:
+ PythonObject() : m_py_obj(nullptr) {}
+
+ PythonObject(PyRefType type, PyObject *py_obj) : m_py_obj(nullptr) {
+ Reset(type, py_obj);
+ }
+
+ PythonObject(const PythonObject &rhs) : m_py_obj(nullptr) { Reset(rhs); }
+
+ virtual ~PythonObject() { Reset(); }
+
+ void Reset() {
+ // Avoid calling the virtual method since it's not necessary
+ // to actually validate the type of the PyObject if we're
+ // just setting to null.
+ if (Py_IsInitialized())
+ Py_XDECREF(m_py_obj);
+ m_py_obj = nullptr;
+ }
+
+ void Reset(const PythonObject &rhs) {
+ // Avoid calling the virtual method if it's not necessary
+ // to actually validate the type of the PyObject.
+ if (!rhs.IsValid())
+ Reset();
+ else
+ Reset(PyRefType::Borrowed, rhs.m_py_obj);
+ }
+
+ // PythonObject is implicitly convertible to PyObject *, which will call the
+ // wrong overload. We want to explicitly disallow this, since a PyObject
+ // *always* owns its reference. Therefore the overload which takes a
+ // PyRefType doesn't make sense, and the copy constructor should be used.
+ void Reset(PyRefType type, const PythonObject &ref) = delete;
+
+ virtual void Reset(PyRefType type, PyObject *py_obj) {
+ if (py_obj == m_py_obj)
+ return;
+
+ if (Py_IsInitialized())
+ Py_XDECREF(m_py_obj);
+
+ m_py_obj = py_obj;
+
+ // If this is a borrowed reference, we need to convert it to
+ // an owned reference by incrementing it. If it is an owned
+ // reference (for example the caller allocated it with PyDict_New()
+ // then we must *not* increment it.
+ if (Py_IsInitialized() && type == PyRefType::Borrowed)
+ Py_XINCREF(m_py_obj);
+ }
+
+ void Dump() const {
+ if (m_py_obj)
+ _PyObject_Dump(m_py_obj);
+ else
+ puts("NULL");
+ }
+
+ void Dump(Stream &strm) const;
+
+ PyObject *get() const { return m_py_obj; }
+
+ PyObject *release() {
+ PyObject *result = m_py_obj;
+ m_py_obj = nullptr;
+ return result;
+ }
+
+ PythonObject &operator=(const PythonObject &other) {
+ Reset(PyRefType::Borrowed, other.get());
+ return *this;
+ }
+
+ PyObjectType GetObjectType() const;
+
+ PythonString Repr() const;
+
+ PythonString Str() const;
+
+ static PythonObject ResolveNameWithDictionary(llvm::StringRef name,
+ const PythonDictionary &dict);
+
+ template <typename T>
+ static T ResolveNameWithDictionary(llvm::StringRef name,
+ const PythonDictionary &dict) {
+ return ResolveNameWithDictionary(name, dict).AsType<T>();
+ }
+
+ PythonObject ResolveName(llvm::StringRef name) const;
+
+ template <typename T> T ResolveName(llvm::StringRef name) const {
+ return ResolveName(name).AsType<T>();
+ }
+
+ bool HasAttribute(llvm::StringRef attribute) const;
+
+ PythonObject GetAttributeValue(llvm::StringRef attribute) const;
+
+ bool IsValid() const;
+
+ bool IsAllocated() const;
+
+ bool IsNone() const;
+
+ template <typename T> T AsType() const {
+ if (!T::Check(m_py_obj))
+ return T();
+ return T(PyRefType::Borrowed, m_py_obj);
+ }
+
+ StructuredData::ObjectSP CreateStructuredObject() const;
+
+protected:
+ PyObject *m_py_obj;
+};
+
+class PythonBytes : public PythonObject {
+public:
+ PythonBytes();
+ explicit PythonBytes(llvm::ArrayRef<uint8_t> bytes);
+ PythonBytes(const uint8_t *bytes, size_t length);
+ PythonBytes(PyRefType type, PyObject *o);
+
+ ~PythonBytes() override;
+
+ static bool Check(PyObject *py_obj);
+
+ // Bring in the no-argument base class version
+ using PythonObject::Reset;
+
+ void Reset(PyRefType type, PyObject *py_obj) override;
+
+ llvm::ArrayRef<uint8_t> GetBytes() const;
+
+ size_t GetSize() const;
+
+ void SetBytes(llvm::ArrayRef<uint8_t> stringbytes);
+
+ StructuredData::StringSP CreateStructuredString() const;
+};
+
+class PythonByteArray : public PythonObject {
+public:
+ PythonByteArray();
+ explicit PythonByteArray(llvm::ArrayRef<uint8_t> bytes);
+ PythonByteArray(const uint8_t *bytes, size_t length);
+ PythonByteArray(PyRefType type, PyObject *o);
+ PythonByteArray(const PythonBytes &object);
+
+ ~PythonByteArray() override;
+
+ static bool Check(PyObject *py_obj);
+
+ // Bring in the no-argument base class version
+ using PythonObject::Reset;
+
+ void Reset(PyRefType type, PyObject *py_obj) override;
+
+ llvm::ArrayRef<uint8_t> GetBytes() const;
+
+ size_t GetSize() const;
+
+ void SetBytes(llvm::ArrayRef<uint8_t> stringbytes);
+
+ StructuredData::StringSP CreateStructuredString() const;
+};
+
+class PythonString : public PythonObject {
+public:
+ PythonString();
+ explicit PythonString(llvm::StringRef string);
+ explicit PythonString(const char *string);
+ PythonString(PyRefType type, PyObject *o);
+
+ ~PythonString() override;
+
+ static bool Check(PyObject *py_obj);
+
+ // Bring in the no-argument base class version
+ using PythonObject::Reset;
+
+ void Reset(PyRefType type, PyObject *py_obj) override;
+
+ llvm::StringRef GetString() const;
+
+ size_t GetSize() const;
+
+ void SetString(llvm::StringRef string);
+
+ StructuredData::StringSP CreateStructuredString() const;
+};
+
+class PythonInteger : public PythonObject {
+public:
+ PythonInteger();
+ explicit PythonInteger(int64_t value);
+ PythonInteger(PyRefType type, PyObject *o);
+
+ ~PythonInteger() override;
+
+ static bool Check(PyObject *py_obj);
+
+ // Bring in the no-argument base class version
+ using PythonObject::Reset;
+
+ void Reset(PyRefType type, PyObject *py_obj) override;
+
+ int64_t GetInteger() const;
+
+ void SetInteger(int64_t value);
+
+ StructuredData::IntegerSP CreateStructuredInteger() const;
+};
+
+class PythonBoolean : public PythonObject {
+public:
+ PythonBoolean() = default;
+ explicit PythonBoolean(bool value);
+ PythonBoolean(PyRefType type, PyObject *o);
+
+ ~PythonBoolean() override = default;
+
+ static bool Check(PyObject *py_obj);
+
+ // Bring in the no-argument base class version
+ using PythonObject::Reset;
+
+ void Reset(PyRefType type, PyObject *py_obj) override;
+
+ bool GetValue() const;
+
+ void SetValue(bool value);
+
+ StructuredData::BooleanSP CreateStructuredBoolean() const;
+};
+
+class PythonList : public PythonObject {
+public:
+ PythonList() {}
+ explicit PythonList(PyInitialValue value);
+ explicit PythonList(int list_size);
+ PythonList(PyRefType type, PyObject *o);
+
+ ~PythonList() override;
+
+ static bool Check(PyObject *py_obj);
+
+ // Bring in the no-argument base class version
+ using PythonObject::Reset;
+
+ void Reset(PyRefType type, PyObject *py_obj) override;
+
+ uint32_t GetSize() const;
+
+ PythonObject GetItemAtIndex(uint32_t index) const;
+
+ void SetItemAtIndex(uint32_t index, const PythonObject &object);
+
+ void AppendItem(const PythonObject &object);
+
+ StructuredData::ArraySP CreateStructuredArray() const;
+};
+
+class PythonTuple : public PythonObject {
+public:
+ PythonTuple() {}
+ explicit PythonTuple(PyInitialValue value);
+ explicit PythonTuple(int tuple_size);
+ PythonTuple(PyRefType type, PyObject *o);
+ PythonTuple(std::initializer_list<PythonObject> objects);
+ PythonTuple(std::initializer_list<PyObject *> objects);
+
+ ~PythonTuple() override;
+
+ static bool Check(PyObject *py_obj);
+
+ // Bring in the no-argument base class version
+ using PythonObject::Reset;
+
+ void Reset(PyRefType type, PyObject *py_obj) override;
+
+ uint32_t GetSize() const;
+
+ PythonObject GetItemAtIndex(uint32_t index) const;
+
+ void SetItemAtIndex(uint32_t index, const PythonObject &object);
+
+ StructuredData::ArraySP CreateStructuredArray() const;
+};
+
+class PythonDictionary : public PythonObject {
+public:
+ PythonDictionary() {}
+ explicit PythonDictionary(PyInitialValue value);
+ PythonDictionary(PyRefType type, PyObject *o);
+
+ ~PythonDictionary() override;
+
+ static bool Check(PyObject *py_obj);
+
+ // Bring in the no-argument base class version
+ using PythonObject::Reset;
+
+ void Reset(PyRefType type, PyObject *py_obj) override;
+
+ uint32_t GetSize() const;
+
+ PythonList GetKeys() const;
+
+ PythonObject GetItemForKey(const PythonObject &key) const;
+ void SetItemForKey(const PythonObject &key, const PythonObject &value);
+
+ StructuredData::DictionarySP CreateStructuredDictionary() const;
+};
+
+class PythonModule : public PythonObject {
+public:
+ PythonModule();
+ PythonModule(PyRefType type, PyObject *o);
+
+ ~PythonModule() override;
+
+ static bool Check(PyObject *py_obj);
+
+ static PythonModule BuiltinsModule();
+
+ static PythonModule MainModule();
+
+ static PythonModule AddModule(llvm::StringRef module);
+
+ static PythonModule ImportModule(llvm::StringRef module);
+
+ // Bring in the no-argument base class version
+ using PythonObject::Reset;
+
+ void Reset(PyRefType type, PyObject *py_obj) override;
+
+ PythonDictionary GetDictionary() const;
+};
+
+class PythonCallable : public PythonObject {
+public:
+ struct ArgInfo {
+ size_t count;
+ bool is_bound_method : 1;
+ bool has_varargs : 1;
+ bool has_kwargs : 1;
+ };
+
+ PythonCallable();
+ PythonCallable(PyRefType type, PyObject *o);
+
+ ~PythonCallable() override;
+
+ static bool Check(PyObject *py_obj);
+
+ // Bring in the no-argument base class version
+ using PythonObject::Reset;
+
+ void Reset(PyRefType type, PyObject *py_obj) override;
+
+ ArgInfo GetNumArguments() const;
+
+ PythonObject operator()();
+
+ PythonObject operator()(std::initializer_list<PyObject *> args);
+
+ PythonObject operator()(std::initializer_list<PythonObject> args);
+
+ template <typename Arg, typename... Args>
+ PythonObject operator()(const Arg &arg, Args... args) {
+ return operator()({arg, args...});
+ }
+};
+
+class PythonFile : public PythonObject {
+public:
+ PythonFile();
+ PythonFile(File &file, const char *mode);
+ PythonFile(const char *path, const char *mode);
+ PythonFile(PyRefType type, PyObject *o);
+
+ ~PythonFile() override;
+
+ static bool Check(PyObject *py_obj);
+
+ using PythonObject::Reset;
+
+ void Reset(PyRefType type, PyObject *py_obj) override;
+ void Reset(File &file, const char *mode);
+
+ static uint32_t GetOptionsFromMode(llvm::StringRef mode);
+
+ bool GetUnderlyingFile(File &file) const;
+};
+
+} // namespace lldb_private
+
+#endif
+
+#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONDATAOBJECTS_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.cpp b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.cpp
new file mode 100644
index 000000000000..c9d834ce6868
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.cpp
@@ -0,0 +1,169 @@
+//===-- PythonExceptionState.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_DISABLE_PYTHON
+
+// LLDB Python header must be included first
+#include "lldb-python.h"
+
+#include "PythonExceptionState.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace lldb_private;
+
+PythonExceptionState::PythonExceptionState(bool restore_on_exit)
+ : m_restore_on_exit(restore_on_exit) {
+ Acquire(restore_on_exit);
+}
+
+PythonExceptionState::~PythonExceptionState() {
+ if (m_restore_on_exit)
+ Restore();
+}
+
+void PythonExceptionState::Acquire(bool restore_on_exit) {
+ // If a state is already acquired, the user needs to decide whether they want
+ // to discard or restore it. Don't allow the potential silent loss of a
+ // valid state.
+ assert(!IsError());
+
+ if (!HasErrorOccurred())
+ return;
+
+ PyObject *py_type = nullptr;
+ PyObject *py_value = nullptr;
+ PyObject *py_traceback = nullptr;
+ PyErr_Fetch(&py_type, &py_value, &py_traceback);
+ // PyErr_Fetch clears the error flag.
+ assert(!HasErrorOccurred());
+
+ // Ownership of the objects returned by `PyErr_Fetch` is transferred to us.
+ m_type.Reset(PyRefType::Owned, py_type);
+ m_value.Reset(PyRefType::Owned, py_value);
+ m_traceback.Reset(PyRefType::Owned, py_traceback);
+ m_restore_on_exit = restore_on_exit;
+}
+
+void PythonExceptionState::Restore() {
+ if (m_type.IsValid()) {
+ // The documentation for PyErr_Restore says "Do not pass a null type and
+ // non-null value or traceback. So only restore if type was non-null to
+ // begin with. In this case we're passing ownership back to Python so
+ // release them all.
+ PyErr_Restore(m_type.release(), m_value.release(), m_traceback.release());
+ }
+
+ // After we restore, we should not hold onto the exception state. Demand
+ // that it be re-acquired.
+ Discard();
+}
+
+void PythonExceptionState::Discard() {
+ m_type.Reset();
+ m_value.Reset();
+ m_traceback.Reset();
+}
+
+void PythonExceptionState::Reset() {
+ if (m_restore_on_exit)
+ Restore();
+ else
+ Discard();
+}
+
+bool PythonExceptionState::HasErrorOccurred() { return PyErr_Occurred(); }
+
+bool PythonExceptionState::IsError() const {
+ return m_type.IsValid() || m_value.IsValid() || m_traceback.IsValid();
+}
+
+PythonObject PythonExceptionState::GetType() const { return m_type; }
+
+PythonObject PythonExceptionState::GetValue() const { return m_value; }
+
+PythonObject PythonExceptionState::GetTraceback() const { return m_traceback; }
+
+std::string PythonExceptionState::Format() const {
+ // Don't allow this function to modify the error state.
+ PythonExceptionState state(true);
+
+ std::string backtrace = ReadBacktrace();
+ if (!IsError())
+ return std::string();
+
+ // It's possible that ReadPythonBacktrace generated another exception. If
+ // this happens we have to clear the exception, because otherwise
+ // PyObject_Str() will assert below. That's why we needed to do the save /
+ // restore at the beginning of this function.
+ PythonExceptionState bt_error_state(false);
+
+ std::string error_string;
+ llvm::raw_string_ostream error_stream(error_string);
+ error_stream << m_value.Str().GetString() << "\n";
+
+ if (!bt_error_state.IsError()) {
+ // If we were able to read the backtrace, just append it.
+ error_stream << backtrace << "\n";
+ } else {
+ // Otherwise, append some information about why we were unable to obtain
+ // the backtrace.
+ PythonString bt_error = bt_error_state.GetValue().Str();
+ error_stream << "An error occurred while retrieving the backtrace: "
+ << bt_error.GetString() << "\n";
+ }
+ return error_stream.str();
+}
+
+std::string PythonExceptionState::ReadBacktrace() const {
+ std::string retval("backtrace unavailable");
+
+ auto traceback_module = PythonModule::ImportModule("traceback");
+#if PY_MAJOR_VERSION >= 3
+ auto stringIO_module = PythonModule::ImportModule("io");
+#else
+ auto stringIO_module = PythonModule::ImportModule("StringIO");
+#endif
+ if (!m_traceback.IsAllocated())
+ return retval;
+
+ if (!traceback_module.IsAllocated() || !stringIO_module.IsAllocated())
+ return retval;
+
+ auto stringIO_builder =
+ stringIO_module.ResolveName<PythonCallable>("StringIO");
+ if (!stringIO_builder.IsAllocated())
+ return retval;
+
+ auto stringIO_buffer = stringIO_builder();
+ if (!stringIO_buffer.IsAllocated())
+ return retval;
+
+ auto printTB = traceback_module.ResolveName<PythonCallable>("print_tb");
+ if (!printTB.IsAllocated())
+ return retval;
+
+ auto printTB_result =
+ printTB(m_traceback.get(), Py_None, stringIO_buffer.get());
+ auto stringIO_getvalue =
+ stringIO_buffer.ResolveName<PythonCallable>("getvalue");
+ if (!stringIO_getvalue.IsAllocated())
+ return retval;
+
+ auto printTB_string = stringIO_getvalue().AsType<PythonString>();
+ if (!printTB_string.IsAllocated())
+ return retval;
+
+ llvm::StringRef string_data(printTB_string.GetString());
+ retval.assign(string_data.data(), string_data.size());
+
+ return retval;
+}
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.h b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.h
new file mode 100644
index 000000000000..3a88aa037776
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.h
@@ -0,0 +1,56 @@
+//===-- PythonExceptionState.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_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONEXCEPTIONSTATE_H
+#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONEXCEPTIONSTATE_H
+
+#ifndef LLDB_DISABLE_PYTHON
+
+#include "PythonDataObjects.h"
+
+namespace lldb_private {
+
+class PythonExceptionState {
+public:
+ explicit PythonExceptionState(bool restore_on_exit);
+ ~PythonExceptionState();
+
+ void Acquire(bool restore_on_exit);
+
+ void Restore();
+
+ void Discard();
+
+ void Reset();
+
+ static bool HasErrorOccurred();
+
+ bool IsError() const;
+
+ PythonObject GetType() const;
+
+ PythonObject GetValue() const;
+
+ PythonObject GetTraceback() const;
+
+ std::string Format() const;
+
+private:
+ std::string ReadBacktrace() const;
+
+ bool m_restore_on_exit;
+
+ PythonObject m_type;
+ PythonObject m_value;
+ PythonObject m_traceback;
+};
+}
+
+#endif
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
new file mode 100644
index 000000000000..2d2b68ceaaa6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -0,0 +1,3259 @@
+//===-- ScriptInterpreterPython.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifdef LLDB_DISABLE_PYTHON
+
+// Python is disabled in this build
+
+#else
+
+// LLDB Python header must be included first
+#include "lldb-python.h"
+
+#include "PythonDataObjects.h"
+#include "PythonExceptionState.h"
+#include "ScriptInterpreterPythonImpl.h"
+
+#include "lldb/API/SBFrame.h"
+#include "lldb/API/SBValue.h"
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Breakpoint/WatchpointOptions.h"
+#include "lldb/Core/Communication.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/Host/ConnectionFileDescriptor.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/Pipe.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Utility/Timer.h"
+
+#if defined(_WIN32)
+#include "lldb/Host/windows/ConnectionGenericFileWindows.h"
+#endif
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/FileSystem.h"
+
+#include <memory>
+#include <mutex>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Defined in the SWIG source file
+#if PY_MAJOR_VERSION >= 3
+extern "C" PyObject *PyInit__lldb(void);
+
+#define LLDBSwigPyInit PyInit__lldb
+
+#else
+extern "C" void init_lldb(void);
+
+#define LLDBSwigPyInit init_lldb
+#endif
+
+// These prototypes are the Pythonic implementations of the required callbacks.
+// Although these are scripting-language specific, their definition depends on
+// the public API.
+extern "C" bool LLDBSwigPythonBreakpointCallbackFunction(
+ const char *python_function_name, const char *session_dictionary_name,
+ const lldb::StackFrameSP &sb_frame,
+ const lldb::BreakpointLocationSP &sb_bp_loc);
+
+extern "C" bool LLDBSwigPythonWatchpointCallbackFunction(
+ const char *python_function_name, const char *session_dictionary_name,
+ const lldb::StackFrameSP &sb_frame, const lldb::WatchpointSP &sb_wp);
+
+extern "C" bool LLDBSwigPythonCallTypeScript(
+ const char *python_function_name, void *session_dictionary,
+ const lldb::ValueObjectSP &valobj_sp, void **pyfunct_wrapper,
+ const lldb::TypeSummaryOptionsSP &options_sp, std::string &retval);
+
+extern "C" void *
+LLDBSwigPythonCreateSyntheticProvider(const char *python_class_name,
+ const char *session_dictionary_name,
+ const lldb::ValueObjectSP &valobj_sp);
+
+extern "C" void *
+LLDBSwigPythonCreateCommandObject(const char *python_class_name,
+ const char *session_dictionary_name,
+ const lldb::DebuggerSP debugger_sp);
+
+extern "C" void *LLDBSwigPythonCreateScriptedThreadPlan(
+ const char *python_class_name, const char *session_dictionary_name,
+ const lldb::ThreadPlanSP &thread_plan_sp);
+
+extern "C" bool LLDBSWIGPythonCallThreadPlan(void *implementor,
+ const char *method_name,
+ Event *event_sp, bool &got_error);
+
+extern "C" void *LLDBSwigPythonCreateScriptedBreakpointResolver(
+ const char *python_class_name, const char *session_dictionary_name,
+ lldb_private::StructuredDataImpl *args, lldb::BreakpointSP &bkpt_sp);
+
+extern "C" unsigned int
+LLDBSwigPythonCallBreakpointResolver(void *implementor, const char *method_name,
+ lldb_private::SymbolContext *sym_ctx);
+
+extern "C" size_t LLDBSwigPython_CalculateNumChildren(void *implementor,
+ uint32_t max);
+
+extern "C" void *LLDBSwigPython_GetChildAtIndex(void *implementor,
+ uint32_t idx);
+
+extern "C" int LLDBSwigPython_GetIndexOfChildWithName(void *implementor,
+ const char *child_name);
+
+extern "C" void *LLDBSWIGPython_CastPyObjectToSBValue(void *data);
+
+extern lldb::ValueObjectSP
+LLDBSWIGPython_GetValueObjectSPFromSBValue(void *data);
+
+extern "C" bool LLDBSwigPython_UpdateSynthProviderInstance(void *implementor);
+
+extern "C" bool
+LLDBSwigPython_MightHaveChildrenSynthProviderInstance(void *implementor);
+
+extern "C" void *
+LLDBSwigPython_GetValueSynthProviderInstance(void *implementor);
+
+extern "C" bool
+LLDBSwigPythonCallCommand(const char *python_function_name,
+ const char *session_dictionary_name,
+ lldb::DebuggerSP &debugger, const char *args,
+ lldb_private::CommandReturnObject &cmd_retobj,
+ lldb::ExecutionContextRefSP exe_ctx_ref_sp);
+
+extern "C" bool
+LLDBSwigPythonCallCommandObject(void *implementor, lldb::DebuggerSP &debugger,
+ const char *args,
+ lldb_private::CommandReturnObject &cmd_retobj,
+ lldb::ExecutionContextRefSP exe_ctx_ref_sp);
+
+extern "C" bool
+LLDBSwigPythonCallModuleInit(const char *python_module_name,
+ const char *session_dictionary_name,
+ lldb::DebuggerSP &debugger);
+
+extern "C" void *
+LLDBSWIGPythonCreateOSPlugin(const char *python_class_name,
+ const char *session_dictionary_name,
+ const lldb::ProcessSP &process_sp);
+
+extern "C" void *
+LLDBSWIGPython_CreateFrameRecognizer(const char *python_class_name,
+ const char *session_dictionary_name);
+
+extern "C" void *
+LLDBSwigPython_GetRecognizedArguments(void *implementor,
+ const lldb::StackFrameSP &frame_sp);
+
+extern "C" bool LLDBSWIGPythonRunScriptKeywordProcess(
+ const char *python_function_name, const char *session_dictionary_name,
+ lldb::ProcessSP &process, std::string &output);
+
+extern "C" bool LLDBSWIGPythonRunScriptKeywordThread(
+ const char *python_function_name, const char *session_dictionary_name,
+ lldb::ThreadSP &thread, std::string &output);
+
+extern "C" bool LLDBSWIGPythonRunScriptKeywordTarget(
+ const char *python_function_name, const char *session_dictionary_name,
+ lldb::TargetSP &target, std::string &output);
+
+extern "C" bool LLDBSWIGPythonRunScriptKeywordFrame(
+ const char *python_function_name, const char *session_dictionary_name,
+ lldb::StackFrameSP &frame, std::string &output);
+
+extern "C" bool LLDBSWIGPythonRunScriptKeywordValue(
+ const char *python_function_name, const char *session_dictionary_name,
+ lldb::ValueObjectSP &value, std::string &output);
+
+extern "C" void *
+LLDBSWIGPython_GetDynamicSetting(void *module, const char *setting,
+ const lldb::TargetSP &target_sp);
+
+static bool g_initialized = false;
+
+namespace {
+
+// Initializing Python is not a straightforward process. We cannot control
+// what external code may have done before getting to this point in LLDB,
+// including potentially having already initialized Python, so we need to do a
+// lot of work to ensure that the existing state of the system is maintained
+// across our initialization. We do this by using an RAII pattern where we
+// save off initial state at the beginning, and restore it at the end
+struct InitializePythonRAII {
+public:
+ InitializePythonRAII()
+ : m_gil_state(PyGILState_UNLOCKED), m_was_already_initialized(false) {
+ // Python will muck with STDIN terminal state, so save off any current TTY
+ // settings so we can restore them.
+ m_stdin_tty_state.Save(STDIN_FILENO, false);
+
+ InitializePythonHome();
+
+ // Register _lldb as a built-in module.
+ PyImport_AppendInittab("_lldb", LLDBSwigPyInit);
+
+// Python < 3.2 and Python >= 3.2 reversed the ordering requirements for
+// calling `Py_Initialize` and `PyEval_InitThreads`. < 3.2 requires that you
+// call `PyEval_InitThreads` first, and >= 3.2 requires that you call it last.
+#if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 2) || (PY_MAJOR_VERSION > 3)
+ Py_InitializeEx(0);
+ InitializeThreadsPrivate();
+#else
+ InitializeThreadsPrivate();
+ Py_InitializeEx(0);
+#endif
+ }
+
+ ~InitializePythonRAII() {
+ if (m_was_already_initialized) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT));
+ LLDB_LOGV(log, "Releasing PyGILState. Returning to state = {0}locked",
+ m_gil_state == PyGILState_UNLOCKED ? "un" : "");
+ PyGILState_Release(m_gil_state);
+ } else {
+ // We initialized the threads in this function, just unlock the GIL.
+ PyEval_SaveThread();
+ }
+
+ m_stdin_tty_state.Restore();
+ }
+
+private:
+ void InitializePythonHome() {
+#if defined(LLDB_PYTHON_HOME)
+#if PY_MAJOR_VERSION >= 3
+ size_t size = 0;
+ static wchar_t *g_python_home = Py_DecodeLocale(LLDB_PYTHON_HOME, &size);
+#else
+ static char g_python_home[] = LLDB_PYTHON_HOME;
+#endif
+ Py_SetPythonHome(g_python_home);
+#else
+#if defined(__APPLE__) && PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION == 7
+ // For Darwin, the only Python version supported is the one shipped in the
+ // OS OS and linked with lldb. Other installation of Python may have higher
+ // priorities in the path, overriding PYTHONHOME and causing
+ // problems/incompatibilities. In order to avoid confusion, always hardcode
+ // the PythonHome to be right, as it's not going to change.
+ static char path[] =
+ "/System/Library/Frameworks/Python.framework/Versions/2.7";
+ Py_SetPythonHome(path);
+#endif
+#endif
+ }
+
+ void InitializeThreadsPrivate() {
+// Since Python 3.7 `Py_Initialize` calls `PyEval_InitThreads` inside itself,
+// so there is no way to determine whether the embedded interpreter
+// was already initialized by some external code. `PyEval_ThreadsInitialized`
+// would always return `true` and `PyGILState_Ensure/Release` flow would be
+// executed instead of unlocking GIL with `PyEval_SaveThread`. When
+// an another thread calls `PyGILState_Ensure` it would get stuck in deadlock.
+#if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 7) || (PY_MAJOR_VERSION > 3)
+ // The only case we should go further and acquire the GIL: it is unlocked.
+ if (PyGILState_Check())
+ return;
+#endif
+
+ if (PyEval_ThreadsInitialized()) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT));
+
+ m_was_already_initialized = true;
+ m_gil_state = PyGILState_Ensure();
+ LLDB_LOGV(log, "Ensured PyGILState. Previous state = {0}locked\n",
+ m_gil_state == PyGILState_UNLOCKED ? "un" : "");
+ return;
+ }
+
+ // InitThreads acquires the GIL if it hasn't been called before.
+ PyEval_InitThreads();
+ }
+
+ TerminalState m_stdin_tty_state;
+ PyGILState_STATE m_gil_state;
+ bool m_was_already_initialized;
+};
+} // namespace
+
+void ScriptInterpreterPython::ComputePythonDirForApple(
+ llvm::SmallVectorImpl<char> &path) {
+ auto style = llvm::sys::path::Style::posix;
+
+ llvm::StringRef path_ref(path.begin(), path.size());
+ auto rbegin = llvm::sys::path::rbegin(path_ref, style);
+ auto rend = llvm::sys::path::rend(path_ref);
+ auto framework = std::find(rbegin, rend, "LLDB.framework");
+ if (framework == rend) {
+ ComputePythonDirForPosix(path);
+ return;
+ }
+ path.resize(framework - rend);
+ llvm::sys::path::append(path, style, "LLDB.framework", "Resources", "Python");
+}
+
+void ScriptInterpreterPython::ComputePythonDirForPosix(
+ llvm::SmallVectorImpl<char> &path) {
+ auto style = llvm::sys::path::Style::posix;
+#if defined(LLDB_PYTHON_RELATIVE_LIBDIR)
+ // Build the path by backing out of the lib dir, then building with whatever
+ // the real python interpreter uses. (e.g. lib for most, lib64 on RHEL
+ // x86_64).
+ llvm::sys::path::remove_filename(path, style);
+ llvm::sys::path::append(path, style, LLDB_PYTHON_RELATIVE_LIBDIR);
+#else
+ llvm::sys::path::append(path, style,
+ "python" + llvm::Twine(PY_MAJOR_VERSION) + "." +
+ llvm::Twine(PY_MINOR_VERSION),
+ "site-packages");
+#endif
+}
+
+void ScriptInterpreterPython::ComputePythonDirForWindows(
+ llvm::SmallVectorImpl<char> &path) {
+ auto style = llvm::sys::path::Style::windows;
+ llvm::sys::path::remove_filename(path, style);
+ llvm::sys::path::append(path, style, "lib", "site-packages");
+
+ // This will be injected directly through FileSpec.GetDirectory().SetString(),
+ // so we need to normalize manually.
+ std::replace(path.begin(), path.end(), '\\', '/');
+}
+
+FileSpec ScriptInterpreterPython::GetPythonDir() {
+ static FileSpec g_spec = []() {
+ FileSpec spec = HostInfo::GetShlibDir();
+ if (!spec)
+ return FileSpec();
+ llvm::SmallString<64> path;
+ spec.GetPath(path);
+
+#if defined(__APPLE__)
+ ComputePythonDirForApple(path);
+#elif defined(_WIN32)
+ ComputePythonDirForWindows(path);
+#else
+ ComputePythonDirForPosix(path);
+#endif
+ spec.GetDirectory().SetString(path);
+ return spec;
+ }();
+ return g_spec;
+}
+
+lldb_private::ConstString ScriptInterpreterPython::GetPluginNameStatic() {
+ static ConstString g_name("script-python");
+ return g_name;
+}
+
+const char *ScriptInterpreterPython::GetPluginDescriptionStatic() {
+ return "Embedded Python interpreter";
+}
+
+void ScriptInterpreterPython::Initialize() {
+ static llvm::once_flag g_once_flag;
+
+ llvm::call_once(g_once_flag, []() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(),
+ lldb::eScriptLanguagePython,
+ ScriptInterpreterPythonImpl::CreateInstance);
+ });
+}
+
+void ScriptInterpreterPython::Terminate() {}
+
+ScriptInterpreterPythonImpl::Locker::Locker(
+ ScriptInterpreterPythonImpl *py_interpreter, uint16_t on_entry,
+ uint16_t on_leave, FILE *in, FILE *out, FILE *err)
+ : ScriptInterpreterLocker(),
+ m_teardown_session((on_leave & TearDownSession) == TearDownSession),
+ m_python_interpreter(py_interpreter) {
+ DoAcquireLock();
+ if ((on_entry & InitSession) == InitSession) {
+ if (!DoInitSession(on_entry, in, out, err)) {
+ // Don't teardown the session if we didn't init it.
+ m_teardown_session = false;
+ }
+ }
+}
+
+bool ScriptInterpreterPythonImpl::Locker::DoAcquireLock() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT));
+ m_GILState = PyGILState_Ensure();
+ LLDB_LOGV(log, "Ensured PyGILState. Previous state = {0}locked",
+ m_GILState == PyGILState_UNLOCKED ? "un" : "");
+
+ // we need to save the thread state when we first start the command because
+ // we might decide to interrupt it while some action is taking place outside
+ // of Python (e.g. printing to screen, waiting for the network, ...) in that
+ // case, _PyThreadState_Current will be NULL - and we would be unable to set
+ // the asynchronous exception - not a desirable situation
+ m_python_interpreter->SetThreadState(PyThreadState_Get());
+ m_python_interpreter->IncrementLockCount();
+ return true;
+}
+
+bool ScriptInterpreterPythonImpl::Locker::DoInitSession(uint16_t on_entry_flags,
+ FILE *in, FILE *out,
+ FILE *err) {
+ if (!m_python_interpreter)
+ return false;
+ return m_python_interpreter->EnterSession(on_entry_flags, in, out, err);
+}
+
+bool ScriptInterpreterPythonImpl::Locker::DoFreeLock() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT));
+ LLDB_LOGV(log, "Releasing PyGILState. Returning to state = {0}locked",
+ m_GILState == PyGILState_UNLOCKED ? "un" : "");
+ PyGILState_Release(m_GILState);
+ m_python_interpreter->DecrementLockCount();
+ return true;
+}
+
+bool ScriptInterpreterPythonImpl::Locker::DoTearDownSession() {
+ if (!m_python_interpreter)
+ return false;
+ m_python_interpreter->LeaveSession();
+ return true;
+}
+
+ScriptInterpreterPythonImpl::Locker::~Locker() {
+ if (m_teardown_session)
+ DoTearDownSession();
+ DoFreeLock();
+}
+
+ScriptInterpreterPythonImpl::ScriptInterpreterPythonImpl(Debugger &debugger)
+ : ScriptInterpreterPython(debugger), m_saved_stdin(), m_saved_stdout(),
+ m_saved_stderr(), m_main_module(),
+ m_session_dict(PyInitialValue::Invalid),
+ m_sys_module_dict(PyInitialValue::Invalid), m_run_one_line_function(),
+ m_run_one_line_str_global(),
+ m_dictionary_name(m_debugger.GetInstanceName().AsCString()),
+ m_terminal_state(), m_active_io_handler(eIOHandlerNone),
+ m_session_is_active(false), m_pty_slave_is_open(false),
+ m_valid_session(true), m_lock_count(0), m_command_thread_state(nullptr) {
+ InitializePrivate();
+
+ m_dictionary_name.append("_dict");
+ StreamString run_string;
+ run_string.Printf("%s = dict()", m_dictionary_name.c_str());
+
+ Locker locker(this, Locker::AcquireLock, Locker::FreeAcquiredLock);
+ PyRun_SimpleString(run_string.GetData());
+
+ run_string.Clear();
+ run_string.Printf(
+ "run_one_line (%s, 'import copy, keyword, os, re, sys, uuid, lldb')",
+ m_dictionary_name.c_str());
+ PyRun_SimpleString(run_string.GetData());
+
+ // Reloading modules requires a different syntax in Python 2 and Python 3.
+ // This provides a consistent syntax no matter what version of Python.
+ run_string.Clear();
+ run_string.Printf("run_one_line (%s, 'from six.moves import reload_module')",
+ m_dictionary_name.c_str());
+ PyRun_SimpleString(run_string.GetData());
+
+ // WARNING: temporary code that loads Cocoa formatters - this should be done
+ // on a per-platform basis rather than loading the whole set and letting the
+ // individual formatter classes exploit APIs to check whether they can/cannot
+ // do their task
+ run_string.Clear();
+ run_string.Printf(
+ "run_one_line (%s, 'import lldb.formatters, lldb.formatters.cpp, pydoc')",
+ m_dictionary_name.c_str());
+ PyRun_SimpleString(run_string.GetData());
+ run_string.Clear();
+
+ run_string.Printf("run_one_line (%s, 'import lldb.embedded_interpreter; from "
+ "lldb.embedded_interpreter import run_python_interpreter; "
+ "from lldb.embedded_interpreter import run_one_line')",
+ m_dictionary_name.c_str());
+ PyRun_SimpleString(run_string.GetData());
+ run_string.Clear();
+
+ run_string.Printf("run_one_line (%s, 'lldb.debugger_unique_id = %" PRIu64
+ "; pydoc.pager = pydoc.plainpager')",
+ m_dictionary_name.c_str(), m_debugger.GetID());
+ PyRun_SimpleString(run_string.GetData());
+}
+
+ScriptInterpreterPythonImpl::~ScriptInterpreterPythonImpl() {
+ // the session dictionary may hold objects with complex state which means
+ // that they may need to be torn down with some level of smarts and that, in
+ // turn, requires a valid thread state force Python to procure itself such a
+ // thread state, nuke the session dictionary and then release it for others
+ // to use and proceed with the rest of the shutdown
+ auto gil_state = PyGILState_Ensure();
+ m_session_dict.Reset();
+ PyGILState_Release(gil_state);
+}
+
+lldb_private::ConstString ScriptInterpreterPythonImpl::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t ScriptInterpreterPythonImpl::GetPluginVersion() { return 1; }
+
+void ScriptInterpreterPythonImpl::IOHandlerActivated(IOHandler &io_handler,
+ bool interactive) {
+ const char *instructions = nullptr;
+
+ switch (m_active_io_handler) {
+ case eIOHandlerNone:
+ break;
+ case eIOHandlerBreakpoint:
+ instructions = R"(Enter your Python command(s). Type 'DONE' to end.
+def function (frame, bp_loc, internal_dict):
+ """frame: the lldb.SBFrame for the location at which you stopped
+ bp_loc: an lldb.SBBreakpointLocation for the breakpoint location information
+ internal_dict: an LLDB support object not to be used"""
+)";
+ break;
+ case eIOHandlerWatchpoint:
+ instructions = "Enter your Python command(s). Type 'DONE' to end.\n";
+ break;
+ }
+
+ if (instructions) {
+ StreamFileSP output_sp(io_handler.GetOutputStreamFile());
+ if (output_sp && interactive) {
+ output_sp->PutCString(instructions);
+ output_sp->Flush();
+ }
+ }
+}
+
+void ScriptInterpreterPythonImpl::IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &data) {
+ io_handler.SetIsDone(true);
+ bool batch_mode = m_debugger.GetCommandInterpreter().GetBatchCommandMode();
+
+ switch (m_active_io_handler) {
+ case eIOHandlerNone:
+ break;
+ case eIOHandlerBreakpoint: {
+ std::vector<BreakpointOptions *> *bp_options_vec =
+ (std::vector<BreakpointOptions *> *)io_handler.GetUserData();
+ for (auto bp_options : *bp_options_vec) {
+ if (!bp_options)
+ continue;
+
+ auto data_up = llvm::make_unique<CommandDataPython>();
+ if (!data_up)
+ break;
+ data_up->user_source.SplitIntoLines(data);
+
+ if (GenerateBreakpointCommandCallbackData(data_up->user_source,
+ data_up->script_source)
+ .Success()) {
+ auto baton_sp = std::make_shared<BreakpointOptions::CommandBaton>(
+ std::move(data_up));
+ bp_options->SetCallback(
+ ScriptInterpreterPythonImpl::BreakpointCallbackFunction, baton_sp);
+ } else if (!batch_mode) {
+ StreamFileSP error_sp = io_handler.GetErrorStreamFile();
+ if (error_sp) {
+ error_sp->Printf("Warning: No command attached to breakpoint.\n");
+ error_sp->Flush();
+ }
+ }
+ }
+ m_active_io_handler = eIOHandlerNone;
+ } break;
+ case eIOHandlerWatchpoint: {
+ WatchpointOptions *wp_options =
+ (WatchpointOptions *)io_handler.GetUserData();
+ auto data_up = llvm::make_unique<WatchpointOptions::CommandData>();
+ data_up->user_source.SplitIntoLines(data);
+
+ if (GenerateWatchpointCommandCallbackData(data_up->user_source,
+ data_up->script_source)) {
+ auto baton_sp =
+ std::make_shared<WatchpointOptions::CommandBaton>(std::move(data_up));
+ wp_options->SetCallback(
+ ScriptInterpreterPythonImpl::WatchpointCallbackFunction, baton_sp);
+ } else if (!batch_mode) {
+ StreamFileSP error_sp = io_handler.GetErrorStreamFile();
+ if (error_sp) {
+ error_sp->Printf("Warning: No command attached to breakpoint.\n");
+ error_sp->Flush();
+ }
+ }
+ m_active_io_handler = eIOHandlerNone;
+ } break;
+ }
+}
+
+lldb::ScriptInterpreterSP
+ScriptInterpreterPythonImpl::CreateInstance(Debugger &debugger) {
+ return std::make_shared<ScriptInterpreterPythonImpl>(debugger);
+}
+
+void ScriptInterpreterPythonImpl::ResetOutputFileHandle(FILE *fh) {}
+
+void ScriptInterpreterPythonImpl::SaveTerminalState(int fd) {
+ // Python mucks with the terminal state of STDIN. If we can possibly avoid
+ // this by setting the file handles up correctly prior to entering the
+ // interpreter we should. For now we save and restore the terminal state on
+ // the input file handle.
+ m_terminal_state.Save(fd, false);
+}
+
+void ScriptInterpreterPythonImpl::RestoreTerminalState() {
+ // Python mucks with the terminal state of STDIN. If we can possibly avoid
+ // this by setting the file handles up correctly prior to entering the
+ // interpreter we should. For now we save and restore the terminal state on
+ // the input file handle.
+ m_terminal_state.Restore();
+}
+
+void ScriptInterpreterPythonImpl::LeaveSession() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT));
+ if (log)
+ log->PutCString("ScriptInterpreterPythonImpl::LeaveSession()");
+
+ // checking that we have a valid thread state - since we use our own
+ // threading and locking in some (rare) cases during cleanup Python may end
+ // up believing we have no thread state and PyImport_AddModule will crash if
+ // that is the case - since that seems to only happen when destroying the
+ // SBDebugger, we can make do without clearing up stdout and stderr
+
+ // rdar://problem/11292882
+ // When the current thread state is NULL, PyThreadState_Get() issues a fatal
+ // error.
+ if (PyThreadState_GetDict()) {
+ PythonDictionary &sys_module_dict = GetSysModuleDictionary();
+ if (sys_module_dict.IsValid()) {
+ if (m_saved_stdin.IsValid()) {
+ sys_module_dict.SetItemForKey(PythonString("stdin"), m_saved_stdin);
+ m_saved_stdin.Reset();
+ }
+ if (m_saved_stdout.IsValid()) {
+ sys_module_dict.SetItemForKey(PythonString("stdout"), m_saved_stdout);
+ m_saved_stdout.Reset();
+ }
+ if (m_saved_stderr.IsValid()) {
+ sys_module_dict.SetItemForKey(PythonString("stderr"), m_saved_stderr);
+ m_saved_stderr.Reset();
+ }
+ }
+ }
+
+ m_session_is_active = false;
+}
+
+bool ScriptInterpreterPythonImpl::SetStdHandle(File &file, const char *py_name,
+ PythonFile &save_file,
+ const char *mode) {
+ if (file.IsValid()) {
+ // Flush the file before giving it to python to avoid interleaved output.
+ file.Flush();
+
+ PythonDictionary &sys_module_dict = GetSysModuleDictionary();
+
+ save_file = sys_module_dict.GetItemForKey(PythonString(py_name))
+ .AsType<PythonFile>();
+
+ PythonFile new_file(file, mode);
+ sys_module_dict.SetItemForKey(PythonString(py_name), new_file);
+ return true;
+ } else
+ save_file.Reset();
+ return false;
+}
+
+bool ScriptInterpreterPythonImpl::EnterSession(uint16_t on_entry_flags,
+ FILE *in, FILE *out, FILE *err) {
+ // If we have already entered the session, without having officially 'left'
+ // it, then there is no need to 'enter' it again.
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT));
+ if (m_session_is_active) {
+ if (log)
+ log->Printf(
+ "ScriptInterpreterPythonImpl::EnterSession(on_entry_flags=0x%" PRIx16
+ ") session is already active, returning without doing anything",
+ on_entry_flags);
+ return false;
+ }
+
+ if (log)
+ log->Printf(
+ "ScriptInterpreterPythonImpl::EnterSession(on_entry_flags=0x%" PRIx16
+ ")",
+ on_entry_flags);
+
+ m_session_is_active = true;
+
+ StreamString run_string;
+
+ if (on_entry_flags & Locker::InitGlobals) {
+ run_string.Printf("run_one_line (%s, 'lldb.debugger_unique_id = %" PRIu64,
+ m_dictionary_name.c_str(), m_debugger.GetID());
+ run_string.Printf(
+ "; lldb.debugger = lldb.SBDebugger.FindDebuggerWithID (%" PRIu64 ")",
+ m_debugger.GetID());
+ run_string.PutCString("; lldb.target = lldb.debugger.GetSelectedTarget()");
+ run_string.PutCString("; lldb.process = lldb.target.GetProcess()");
+ run_string.PutCString("; lldb.thread = lldb.process.GetSelectedThread ()");
+ run_string.PutCString("; lldb.frame = lldb.thread.GetSelectedFrame ()");
+ run_string.PutCString("')");
+ } else {
+ // If we aren't initing the globals, we should still always set the
+ // debugger (since that is always unique.)
+ run_string.Printf("run_one_line (%s, 'lldb.debugger_unique_id = %" PRIu64,
+ m_dictionary_name.c_str(), m_debugger.GetID());
+ run_string.Printf(
+ "; lldb.debugger = lldb.SBDebugger.FindDebuggerWithID (%" PRIu64 ")",
+ m_debugger.GetID());
+ run_string.PutCString("')");
+ }
+
+ PyRun_SimpleString(run_string.GetData());
+ run_string.Clear();
+
+ PythonDictionary &sys_module_dict = GetSysModuleDictionary();
+ if (sys_module_dict.IsValid()) {
+ File in_file(in, false);
+ File out_file(out, false);
+ File err_file(err, false);
+
+ lldb::StreamFileSP in_sp;
+ lldb::StreamFileSP out_sp;
+ lldb::StreamFileSP err_sp;
+ if (!in_file.IsValid() || !out_file.IsValid() || !err_file.IsValid())
+ m_debugger.AdoptTopIOHandlerFilesIfInvalid(in_sp, out_sp, err_sp);
+
+ if (on_entry_flags & Locker::NoSTDIN) {
+ m_saved_stdin.Reset();
+ } else {
+ if (!SetStdHandle(in_file, "stdin", m_saved_stdin, "r")) {
+ if (in_sp)
+ SetStdHandle(in_sp->GetFile(), "stdin", m_saved_stdin, "r");
+ }
+ }
+
+ if (!SetStdHandle(out_file, "stdout", m_saved_stdout, "w")) {
+ if (out_sp)
+ SetStdHandle(out_sp->GetFile(), "stdout", m_saved_stdout, "w");
+ }
+
+ if (!SetStdHandle(err_file, "stderr", m_saved_stderr, "w")) {
+ if (err_sp)
+ SetStdHandle(err_sp->GetFile(), "stderr", m_saved_stderr, "w");
+ }
+ }
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ return true;
+}
+
+PythonObject &ScriptInterpreterPythonImpl::GetMainModule() {
+ if (!m_main_module.IsValid())
+ m_main_module.Reset(PyRefType::Borrowed, PyImport_AddModule("__main__"));
+ return m_main_module;
+}
+
+PythonDictionary &ScriptInterpreterPythonImpl::GetSessionDictionary() {
+ if (m_session_dict.IsValid())
+ return m_session_dict;
+
+ PythonObject &main_module = GetMainModule();
+ if (!main_module.IsValid())
+ return m_session_dict;
+
+ PythonDictionary main_dict(PyRefType::Borrowed,
+ PyModule_GetDict(main_module.get()));
+ if (!main_dict.IsValid())
+ return m_session_dict;
+
+ PythonObject item = main_dict.GetItemForKey(PythonString(m_dictionary_name));
+ m_session_dict.Reset(PyRefType::Borrowed, item.get());
+ return m_session_dict;
+}
+
+PythonDictionary &ScriptInterpreterPythonImpl::GetSysModuleDictionary() {
+ if (m_sys_module_dict.IsValid())
+ return m_sys_module_dict;
+
+ PythonObject sys_module(PyRefType::Borrowed, PyImport_AddModule("sys"));
+ if (sys_module.IsValid())
+ m_sys_module_dict.Reset(PyRefType::Borrowed,
+ PyModule_GetDict(sys_module.get()));
+ return m_sys_module_dict;
+}
+
+static std::string GenerateUniqueName(const char *base_name_wanted,
+ uint32_t &functions_counter,
+ const void *name_token = nullptr) {
+ StreamString sstr;
+
+ if (!base_name_wanted)
+ return std::string();
+
+ if (!name_token)
+ sstr.Printf("%s_%d", base_name_wanted, functions_counter++);
+ else
+ sstr.Printf("%s_%p", base_name_wanted, name_token);
+
+ return sstr.GetString();
+}
+
+bool ScriptInterpreterPythonImpl::GetEmbeddedInterpreterModuleObjects() {
+ if (m_run_one_line_function.IsValid())
+ return true;
+
+ PythonObject module(PyRefType::Borrowed,
+ PyImport_AddModule("lldb.embedded_interpreter"));
+ if (!module.IsValid())
+ return false;
+
+ PythonDictionary module_dict(PyRefType::Borrowed,
+ PyModule_GetDict(module.get()));
+ if (!module_dict.IsValid())
+ return false;
+
+ m_run_one_line_function =
+ module_dict.GetItemForKey(PythonString("run_one_line"));
+ m_run_one_line_str_global =
+ module_dict.GetItemForKey(PythonString("g_run_one_line_str"));
+ return m_run_one_line_function.IsValid();
+}
+
+static void ReadThreadBytesReceived(void *baton, const void *src,
+ size_t src_len) {
+ if (src && src_len) {
+ Stream *strm = (Stream *)baton;
+ strm->Write(src, src_len);
+ strm->Flush();
+ }
+}
+
+bool ScriptInterpreterPythonImpl::ExecuteOneLine(
+ llvm::StringRef command, CommandReturnObject *result,
+ const ExecuteScriptOptions &options) {
+ std::string command_str = command.str();
+
+ if (!m_valid_session)
+ return false;
+
+ if (!command.empty()) {
+ // We want to call run_one_line, passing in the dictionary and the command
+ // string. We cannot do this through PyRun_SimpleString here because the
+ // command string may contain escaped characters, and putting it inside
+ // another string to pass to PyRun_SimpleString messes up the escaping. So
+ // we use the following more complicated method to pass the command string
+ // directly down to Python.
+ Debugger &debugger = m_debugger;
+
+ StreamFileSP input_file_sp;
+ StreamFileSP output_file_sp;
+ StreamFileSP error_file_sp;
+ Communication output_comm(
+ "lldb.ScriptInterpreterPythonImpl.ExecuteOneLine.comm");
+ bool join_read_thread = false;
+ if (options.GetEnableIO()) {
+ if (result) {
+ input_file_sp = debugger.GetInputFile();
+ // Set output to a temporary file so we can forward the results on to
+ // the result object
+
+ Pipe pipe;
+ Status pipe_result = pipe.CreateNew(false);
+ if (pipe_result.Success()) {
+#if defined(_WIN32)
+ lldb::file_t read_file = pipe.GetReadNativeHandle();
+ pipe.ReleaseReadFileDescriptor();
+ std::unique_ptr<ConnectionGenericFile> conn_up(
+ new ConnectionGenericFile(read_file, true));
+#else
+ std::unique_ptr<ConnectionFileDescriptor> conn_up(
+ new ConnectionFileDescriptor(pipe.ReleaseReadFileDescriptor(),
+ true));
+#endif
+ if (conn_up->IsConnected()) {
+ output_comm.SetConnection(conn_up.release());
+ output_comm.SetReadThreadBytesReceivedCallback(
+ ReadThreadBytesReceived, &result->GetOutputStream());
+ output_comm.StartReadThread();
+ join_read_thread = true;
+ FILE *outfile_handle =
+ fdopen(pipe.ReleaseWriteFileDescriptor(), "w");
+ output_file_sp = std::make_shared<StreamFile>(outfile_handle, true);
+ error_file_sp = output_file_sp;
+ if (outfile_handle)
+ ::setbuf(outfile_handle, nullptr);
+
+ result->SetImmediateOutputFile(
+ debugger.GetOutputFile()->GetFile().GetStream());
+ result->SetImmediateErrorFile(
+ debugger.GetErrorFile()->GetFile().GetStream());
+ }
+ }
+ }
+ if (!input_file_sp || !output_file_sp || !error_file_sp)
+ debugger.AdoptTopIOHandlerFilesIfInvalid(input_file_sp, output_file_sp,
+ error_file_sp);
+ } else {
+ input_file_sp = std::make_shared<StreamFile>();
+ FileSystem::Instance().Open(input_file_sp->GetFile(),
+ FileSpec(FileSystem::DEV_NULL),
+ File::eOpenOptionRead);
+
+ output_file_sp = std::make_shared<StreamFile>();
+ FileSystem::Instance().Open(output_file_sp->GetFile(),
+ FileSpec(FileSystem::DEV_NULL),
+ File::eOpenOptionWrite);
+
+ error_file_sp = output_file_sp;
+ }
+
+ FILE *in_file = input_file_sp->GetFile().GetStream();
+ FILE *out_file = output_file_sp->GetFile().GetStream();
+ FILE *err_file = error_file_sp->GetFile().GetStream();
+ bool success = false;
+ {
+ // WARNING! It's imperative that this RAII scope be as tight as
+ // possible. In particular, the scope must end *before* we try to join
+ // the read thread. The reason for this is that a pre-requisite for
+ // joining the read thread is that we close the write handle (to break
+ // the pipe and cause it to wake up and exit). But acquiring the GIL as
+ // below will redirect Python's stdio to use this same handle. If we
+ // close the handle while Python is still using it, bad things will
+ // happen.
+ Locker locker(
+ this,
+ Locker::AcquireLock | Locker::InitSession |
+ (options.GetSetLLDBGlobals() ? Locker::InitGlobals : 0) |
+ ((result && result->GetInteractive()) ? 0 : Locker::NoSTDIN),
+ Locker::FreeAcquiredLock | Locker::TearDownSession, in_file, out_file,
+ err_file);
+
+ // Find the correct script interpreter dictionary in the main module.
+ PythonDictionary &session_dict = GetSessionDictionary();
+ if (session_dict.IsValid()) {
+ if (GetEmbeddedInterpreterModuleObjects()) {
+ if (PyCallable_Check(m_run_one_line_function.get())) {
+ PythonObject pargs(
+ PyRefType::Owned,
+ Py_BuildValue("(Os)", session_dict.get(), command_str.c_str()));
+ if (pargs.IsValid()) {
+ PythonObject return_value(
+ PyRefType::Owned,
+ PyObject_CallObject(m_run_one_line_function.get(),
+ pargs.get()));
+ if (return_value.IsValid())
+ success = true;
+ else if (options.GetMaskoutErrors() && PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+ }
+ }
+ }
+ }
+
+ // Flush our output and error file handles
+ ::fflush(out_file);
+ if (out_file != err_file)
+ ::fflush(err_file);
+ }
+
+ if (join_read_thread) {
+ // Close the write end of the pipe since we are done with our one line
+ // script. This should cause the read thread that output_comm is using to
+ // exit
+ output_file_sp->GetFile().Close();
+ // The close above should cause this thread to exit when it gets to the
+ // end of file, so let it get all its data
+ output_comm.JoinReadThread();
+ // Now we can close the read end of the pipe
+ output_comm.Disconnect();
+ }
+
+ if (success)
+ return true;
+
+ // The one-liner failed. Append the error message.
+ if (result) {
+ result->AppendErrorWithFormat(
+ "python failed attempting to evaluate '%s'\n", command_str.c_str());
+ }
+ return false;
+ }
+
+ if (result)
+ result->AppendError("empty command passed to python\n");
+ return false;
+}
+
+void ScriptInterpreterPythonImpl::ExecuteInterpreterLoop() {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION);
+
+ Debugger &debugger = m_debugger;
+
+ // At the moment, the only time the debugger does not have an input file
+ // handle is when this is called directly from Python, in which case it is
+ // both dangerous and unnecessary (not to mention confusing) to try to embed
+ // a running interpreter loop inside the already running Python interpreter
+ // loop, so we won't do it.
+
+ if (!debugger.GetInputFile()->GetFile().IsValid())
+ return;
+
+ IOHandlerSP io_handler_sp(new IOHandlerPythonInterpreter(debugger, this));
+ if (io_handler_sp) {
+ debugger.PushIOHandler(io_handler_sp);
+ }
+}
+
+bool ScriptInterpreterPythonImpl::Interrupt() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT));
+
+ if (IsExecutingPython()) {
+ PyThreadState *state = PyThreadState_GET();
+ if (!state)
+ state = GetThreadState();
+ if (state) {
+ long tid = state->thread_id;
+ PyThreadState_Swap(state);
+ int num_threads = PyThreadState_SetAsyncExc(tid, PyExc_KeyboardInterrupt);
+ if (log)
+ log->Printf("ScriptInterpreterPythonImpl::Interrupt() sending "
+ "PyExc_KeyboardInterrupt (tid = %li, num_threads = %i)...",
+ tid, num_threads);
+ return true;
+ }
+ }
+ if (log)
+ log->Printf(
+ "ScriptInterpreterPythonImpl::Interrupt() python code not running, "
+ "can't interrupt");
+ return false;
+}
+bool ScriptInterpreterPythonImpl::ExecuteOneLineWithReturn(
+ llvm::StringRef in_string, ScriptInterpreter::ScriptReturnType return_type,
+ void *ret_value, const ExecuteScriptOptions &options) {
+
+ Locker locker(this,
+ Locker::AcquireLock | Locker::InitSession |
+ (options.GetSetLLDBGlobals() ? Locker::InitGlobals : 0) |
+ Locker::NoSTDIN,
+ Locker::FreeAcquiredLock | Locker::TearDownSession);
+
+ PythonObject py_return;
+ PythonObject &main_module = GetMainModule();
+ PythonDictionary globals(PyRefType::Borrowed,
+ PyModule_GetDict(main_module.get()));
+ PythonObject py_error;
+ bool ret_success = false;
+ int success;
+
+ PythonDictionary locals = GetSessionDictionary();
+
+ if (!locals.IsValid()) {
+ locals.Reset(
+ PyRefType::Owned,
+ PyObject_GetAttrString(globals.get(), m_dictionary_name.c_str()));
+ }
+
+ if (!locals.IsValid())
+ locals = globals;
+
+ py_error.Reset(PyRefType::Borrowed, PyErr_Occurred());
+ if (py_error.IsValid())
+ PyErr_Clear();
+
+ std::string as_string = in_string.str();
+ { // scope for PythonInputReaderManager
+ // PythonInputReaderManager py_input(options.GetEnableIO() ? this : NULL);
+ py_return.Reset(PyRefType::Owned,
+ PyRun_String(as_string.c_str(), Py_eval_input,
+ globals.get(), locals.get()));
+ if (!py_return.IsValid()) {
+ py_error.Reset(PyRefType::Borrowed, PyErr_Occurred());
+ if (py_error.IsValid())
+ PyErr_Clear();
+
+ py_return.Reset(PyRefType::Owned,
+ PyRun_String(as_string.c_str(), Py_single_input,
+ globals.get(), locals.get()));
+ }
+ }
+
+ if (py_return.IsValid()) {
+ switch (return_type) {
+ case eScriptReturnTypeCharPtr: // "char *"
+ {
+ const char format[3] = "s#";
+ success = PyArg_Parse(py_return.get(), format, (char **)ret_value);
+ break;
+ }
+ case eScriptReturnTypeCharStrOrNone: // char* or NULL if py_return ==
+ // Py_None
+ {
+ const char format[3] = "z";
+ success = PyArg_Parse(py_return.get(), format, (char **)ret_value);
+ break;
+ }
+ case eScriptReturnTypeBool: {
+ const char format[2] = "b";
+ success = PyArg_Parse(py_return.get(), format, (bool *)ret_value);
+ break;
+ }
+ case eScriptReturnTypeShortInt: {
+ const char format[2] = "h";
+ success = PyArg_Parse(py_return.get(), format, (short *)ret_value);
+ break;
+ }
+ case eScriptReturnTypeShortIntUnsigned: {
+ const char format[2] = "H";
+ success =
+ PyArg_Parse(py_return.get(), format, (unsigned short *)ret_value);
+ break;
+ }
+ case eScriptReturnTypeInt: {
+ const char format[2] = "i";
+ success = PyArg_Parse(py_return.get(), format, (int *)ret_value);
+ break;
+ }
+ case eScriptReturnTypeIntUnsigned: {
+ const char format[2] = "I";
+ success = PyArg_Parse(py_return.get(), format, (unsigned int *)ret_value);
+ break;
+ }
+ case eScriptReturnTypeLongInt: {
+ const char format[2] = "l";
+ success = PyArg_Parse(py_return.get(), format, (long *)ret_value);
+ break;
+ }
+ case eScriptReturnTypeLongIntUnsigned: {
+ const char format[2] = "k";
+ success =
+ PyArg_Parse(py_return.get(), format, (unsigned long *)ret_value);
+ break;
+ }
+ case eScriptReturnTypeLongLong: {
+ const char format[2] = "L";
+ success = PyArg_Parse(py_return.get(), format, (long long *)ret_value);
+ break;
+ }
+ case eScriptReturnTypeLongLongUnsigned: {
+ const char format[2] = "K";
+ success =
+ PyArg_Parse(py_return.get(), format, (unsigned long long *)ret_value);
+ break;
+ }
+ case eScriptReturnTypeFloat: {
+ const char format[2] = "f";
+ success = PyArg_Parse(py_return.get(), format, (float *)ret_value);
+ break;
+ }
+ case eScriptReturnTypeDouble: {
+ const char format[2] = "d";
+ success = PyArg_Parse(py_return.get(), format, (double *)ret_value);
+ break;
+ }
+ case eScriptReturnTypeChar: {
+ const char format[2] = "c";
+ success = PyArg_Parse(py_return.get(), format, (char *)ret_value);
+ break;
+ }
+ case eScriptReturnTypeOpaqueObject: {
+ success = true;
+ PyObject *saved_value = py_return.get();
+ Py_XINCREF(saved_value);
+ *((PyObject **)ret_value) = saved_value;
+ break;
+ }
+ }
+
+ ret_success = success;
+ }
+
+ py_error.Reset(PyRefType::Borrowed, PyErr_Occurred());
+ if (py_error.IsValid()) {
+ ret_success = false;
+ if (options.GetMaskoutErrors()) {
+ if (PyErr_GivenExceptionMatches(py_error.get(), PyExc_SyntaxError))
+ PyErr_Print();
+ PyErr_Clear();
+ }
+ }
+
+ return ret_success;
+}
+
+Status ScriptInterpreterPythonImpl::ExecuteMultipleLines(
+ const char *in_string, const ExecuteScriptOptions &options) {
+ Status error;
+
+ Locker locker(this,
+ Locker::AcquireLock | Locker::InitSession |
+ (options.GetSetLLDBGlobals() ? Locker::InitGlobals : 0) |
+ Locker::NoSTDIN,
+ Locker::FreeAcquiredLock | Locker::TearDownSession);
+
+ PythonObject return_value;
+ PythonObject &main_module = GetMainModule();
+ PythonDictionary globals(PyRefType::Borrowed,
+ PyModule_GetDict(main_module.get()));
+ PythonObject py_error;
+
+ PythonDictionary locals = GetSessionDictionary();
+
+ if (!locals.IsValid())
+ locals.Reset(
+ PyRefType::Owned,
+ PyObject_GetAttrString(globals.get(), m_dictionary_name.c_str()));
+
+ if (!locals.IsValid())
+ locals = globals;
+
+ py_error.Reset(PyRefType::Borrowed, PyErr_Occurred());
+ if (py_error.IsValid())
+ PyErr_Clear();
+
+ if (in_string != nullptr) {
+ PythonObject code_object;
+ code_object.Reset(PyRefType::Owned,
+ Py_CompileString(in_string, "temp.py", Py_file_input));
+
+ if (code_object.IsValid()) {
+// In Python 2.x, PyEval_EvalCode takes a PyCodeObject, but in Python 3.x, it
+// takes a PyObject. They are convertible (hence the function
+// PyCode_Check(PyObject*), so we have to do the cast for Python 2.x
+#if PY_MAJOR_VERSION >= 3
+ PyObject *py_code_obj = code_object.get();
+#else
+ PyCodeObject *py_code_obj =
+ reinterpret_cast<PyCodeObject *>(code_object.get());
+#endif
+ return_value.Reset(
+ PyRefType::Owned,
+ PyEval_EvalCode(py_code_obj, globals.get(), locals.get()));
+ }
+ }
+
+ PythonExceptionState exception_state(!options.GetMaskoutErrors());
+ if (exception_state.IsError())
+ error.SetErrorString(exception_state.Format().c_str());
+
+ return error;
+}
+
+void ScriptInterpreterPythonImpl::CollectDataForBreakpointCommandCallback(
+ std::vector<BreakpointOptions *> &bp_options_vec,
+ CommandReturnObject &result) {
+ m_active_io_handler = eIOHandlerBreakpoint;
+ m_debugger.GetCommandInterpreter().GetPythonCommandsFromIOHandler(
+ " ", *this, true, &bp_options_vec);
+}
+
+void ScriptInterpreterPythonImpl::CollectDataForWatchpointCommandCallback(
+ WatchpointOptions *wp_options, CommandReturnObject &result) {
+ m_active_io_handler = eIOHandlerWatchpoint;
+ m_debugger.GetCommandInterpreter().GetPythonCommandsFromIOHandler(
+ " ", *this, true, wp_options);
+}
+
+void ScriptInterpreterPythonImpl::SetBreakpointCommandCallbackFunction(
+ BreakpointOptions *bp_options, const char *function_name) {
+ // For now just cons up a oneliner that calls the provided function.
+ std::string oneliner("return ");
+ oneliner += function_name;
+ oneliner += "(frame, bp_loc, internal_dict)";
+ m_debugger.GetScriptInterpreter()->SetBreakpointCommandCallback(
+ bp_options, oneliner.c_str());
+}
+
+Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback(
+ BreakpointOptions *bp_options,
+ std::unique_ptr<BreakpointOptions::CommandData> &cmd_data_up) {
+ Status error;
+ error = GenerateBreakpointCommandCallbackData(cmd_data_up->user_source,
+ cmd_data_up->script_source);
+ if (error.Fail()) {
+ return error;
+ }
+ auto baton_sp =
+ std::make_shared<BreakpointOptions::CommandBaton>(std::move(cmd_data_up));
+ bp_options->SetCallback(
+ ScriptInterpreterPythonImpl::BreakpointCallbackFunction, baton_sp);
+ return error;
+}
+
+// Set a Python one-liner as the callback for the breakpoint.
+Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback(
+ BreakpointOptions *bp_options, const char *command_body_text) {
+ auto data_up = llvm::make_unique<CommandDataPython>();
+
+ // Split the command_body_text into lines, and pass that to
+ // GenerateBreakpointCommandCallbackData. That will wrap the body in an
+ // auto-generated function, and return the function name in script_source.
+ // That is what the callback will actually invoke.
+
+ data_up->user_source.SplitIntoLines(command_body_text);
+ Status error = GenerateBreakpointCommandCallbackData(data_up->user_source,
+ data_up->script_source);
+ if (error.Success()) {
+ auto baton_sp =
+ std::make_shared<BreakpointOptions::CommandBaton>(std::move(data_up));
+ bp_options->SetCallback(
+ ScriptInterpreterPythonImpl::BreakpointCallbackFunction, baton_sp);
+ return error;
+ } else
+ return error;
+}
+
+// Set a Python one-liner as the callback for the watchpoint.
+void ScriptInterpreterPythonImpl::SetWatchpointCommandCallback(
+ WatchpointOptions *wp_options, const char *oneliner) {
+ auto data_up = llvm::make_unique<WatchpointOptions::CommandData>();
+
+ // It's necessary to set both user_source and script_source to the oneliner.
+ // The former is used to generate callback description (as in watchpoint
+ // command list) while the latter is used for Python to interpret during the
+ // actual callback.
+
+ data_up->user_source.AppendString(oneliner);
+ data_up->script_source.assign(oneliner);
+
+ if (GenerateWatchpointCommandCallbackData(data_up->user_source,
+ data_up->script_source)) {
+ auto baton_sp =
+ std::make_shared<WatchpointOptions::CommandBaton>(std::move(data_up));
+ wp_options->SetCallback(
+ ScriptInterpreterPythonImpl::WatchpointCallbackFunction, baton_sp);
+ }
+
+ return;
+}
+
+Status ScriptInterpreterPythonImpl::ExportFunctionDefinitionToInterpreter(
+ StringList &function_def) {
+ // Convert StringList to one long, newline delimited, const char *.
+ std::string function_def_string(function_def.CopyList());
+
+ Status error = ExecuteMultipleLines(
+ function_def_string.c_str(),
+ ScriptInterpreter::ExecuteScriptOptions().SetEnableIO(false));
+ return error;
+}
+
+Status ScriptInterpreterPythonImpl::GenerateFunction(const char *signature,
+ const StringList &input) {
+ Status error;
+ int num_lines = input.GetSize();
+ if (num_lines == 0) {
+ error.SetErrorString("No input data.");
+ return error;
+ }
+
+ if (!signature || *signature == 0) {
+ error.SetErrorString("No output function name.");
+ return error;
+ }
+
+ StreamString sstr;
+ StringList auto_generated_function;
+ auto_generated_function.AppendString(signature);
+ auto_generated_function.AppendString(
+ " global_dict = globals()"); // Grab the global dictionary
+ auto_generated_function.AppendString(
+ " new_keys = internal_dict.keys()"); // Make a list of keys in the
+ // session dict
+ auto_generated_function.AppendString(
+ " old_keys = global_dict.keys()"); // Save list of keys in global dict
+ auto_generated_function.AppendString(
+ " global_dict.update (internal_dict)"); // Add the session dictionary
+ // to the
+ // global dictionary.
+
+ // Wrap everything up inside the function, increasing the indentation.
+
+ auto_generated_function.AppendString(" if True:");
+ for (int i = 0; i < num_lines; ++i) {
+ sstr.Clear();
+ sstr.Printf(" %s", input.GetStringAtIndex(i));
+ auto_generated_function.AppendString(sstr.GetData());
+ }
+ auto_generated_function.AppendString(
+ " for key in new_keys:"); // Iterate over all the keys from session
+ // dict
+ auto_generated_function.AppendString(
+ " internal_dict[key] = global_dict[key]"); // Update session dict
+ // values
+ auto_generated_function.AppendString(
+ " if key not in old_keys:"); // If key was not originally in
+ // global dict
+ auto_generated_function.AppendString(
+ " del global_dict[key]"); // ...then remove key/value from
+ // global dict
+
+ // Verify that the results are valid Python.
+
+ error = ExportFunctionDefinitionToInterpreter(auto_generated_function);
+
+ return error;
+}
+
+bool ScriptInterpreterPythonImpl::GenerateTypeScriptFunction(
+ StringList &user_input, std::string &output, const void *name_token) {
+ static uint32_t num_created_functions = 0;
+ user_input.RemoveBlankLines();
+ StreamString sstr;
+
+ // Check to see if we have any data; if not, just return.
+ if (user_input.GetSize() == 0)
+ return false;
+
+ // Take what the user wrote, wrap it all up inside one big auto-generated
+ // Python function, passing in the ValueObject as parameter to the function.
+
+ std::string auto_generated_function_name(
+ GenerateUniqueName("lldb_autogen_python_type_print_func",
+ num_created_functions, name_token));
+ sstr.Printf("def %s (valobj, internal_dict):",
+ auto_generated_function_name.c_str());
+
+ if (!GenerateFunction(sstr.GetData(), user_input).Success())
+ return false;
+
+ // Store the name of the auto-generated function to be called.
+ output.assign(auto_generated_function_name);
+ return true;
+}
+
+bool ScriptInterpreterPythonImpl::GenerateScriptAliasFunction(
+ StringList &user_input, std::string &output) {
+ static uint32_t num_created_functions = 0;
+ user_input.RemoveBlankLines();
+ StreamString sstr;
+
+ // Check to see if we have any data; if not, just return.
+ if (user_input.GetSize() == 0)
+ return false;
+
+ std::string auto_generated_function_name(GenerateUniqueName(
+ "lldb_autogen_python_cmd_alias_func", num_created_functions));
+
+ sstr.Printf("def %s (debugger, args, result, internal_dict):",
+ auto_generated_function_name.c_str());
+
+ if (!GenerateFunction(sstr.GetData(), user_input).Success())
+ return false;
+
+ // Store the name of the auto-generated function to be called.
+ output.assign(auto_generated_function_name);
+ return true;
+}
+
+bool ScriptInterpreterPythonImpl::GenerateTypeSynthClass(
+ StringList &user_input, std::string &output, const void *name_token) {
+ static uint32_t num_created_classes = 0;
+ user_input.RemoveBlankLines();
+ int num_lines = user_input.GetSize();
+ StreamString sstr;
+
+ // Check to see if we have any data; if not, just return.
+ if (user_input.GetSize() == 0)
+ return false;
+
+ // Wrap all user input into a Python class
+
+ std::string auto_generated_class_name(GenerateUniqueName(
+ "lldb_autogen_python_type_synth_class", num_created_classes, name_token));
+
+ StringList auto_generated_class;
+
+ // Create the function name & definition string.
+
+ sstr.Printf("class %s:", auto_generated_class_name.c_str());
+ auto_generated_class.AppendString(sstr.GetString());
+
+ // Wrap everything up inside the class, increasing the indentation. we don't
+ // need to play any fancy indentation tricks here because there is no
+ // surrounding code whose indentation we need to honor
+ for (int i = 0; i < num_lines; ++i) {
+ sstr.Clear();
+ sstr.Printf(" %s", user_input.GetStringAtIndex(i));
+ auto_generated_class.AppendString(sstr.GetString());
+ }
+
+ // Verify that the results are valid Python. (even though the method is
+ // ExportFunctionDefinitionToInterpreter, a class will actually be exported)
+ // (TODO: rename that method to ExportDefinitionToInterpreter)
+ if (!ExportFunctionDefinitionToInterpreter(auto_generated_class).Success())
+ return false;
+
+ // Store the name of the auto-generated class
+
+ output.assign(auto_generated_class_name);
+ return true;
+}
+
+StructuredData::GenericSP
+ScriptInterpreterPythonImpl::CreateFrameRecognizer(const char *class_name) {
+ if (class_name == nullptr || class_name[0] == '\0')
+ return StructuredData::GenericSP();
+
+ void *ret_val;
+
+ {
+ Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN,
+ Locker::FreeLock);
+ ret_val = LLDBSWIGPython_CreateFrameRecognizer(class_name,
+ m_dictionary_name.c_str());
+ }
+
+ return StructuredData::GenericSP(new StructuredPythonObject(ret_val));
+}
+
+lldb::ValueObjectListSP ScriptInterpreterPythonImpl::GetRecognizedArguments(
+ const StructuredData::ObjectSP &os_plugin_object_sp,
+ lldb::StackFrameSP frame_sp) {
+ Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock);
+
+ if (!os_plugin_object_sp)
+ return ValueObjectListSP();
+
+ StructuredData::Generic *generic = os_plugin_object_sp->GetAsGeneric();
+ if (!generic)
+ return nullptr;
+
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)generic->GetValue());
+
+ if (!implementor.IsAllocated())
+ return ValueObjectListSP();
+
+ PythonObject py_return(PyRefType::Owned,
+ (PyObject *)LLDBSwigPython_GetRecognizedArguments(
+ implementor.get(), frame_sp));
+
+ // if it fails, print the error but otherwise go on
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+ if (py_return.get()) {
+ PythonList result_list(PyRefType::Borrowed, py_return.get());
+ ValueObjectListSP result = ValueObjectListSP(new ValueObjectList());
+ for (size_t i = 0; i < result_list.GetSize(); i++) {
+ PyObject *item = result_list.GetItemAtIndex(i).get();
+ lldb::SBValue *sb_value_ptr =
+ (lldb::SBValue *)LLDBSWIGPython_CastPyObjectToSBValue(item);
+ auto valobj_sp = LLDBSWIGPython_GetValueObjectSPFromSBValue(sb_value_ptr);
+ if (valobj_sp)
+ result->Append(valobj_sp);
+ }
+ return result;
+ }
+ return ValueObjectListSP();
+}
+
+StructuredData::GenericSP
+ScriptInterpreterPythonImpl::OSPlugin_CreatePluginObject(
+ const char *class_name, lldb::ProcessSP process_sp) {
+ if (class_name == nullptr || class_name[0] == '\0')
+ return StructuredData::GenericSP();
+
+ if (!process_sp)
+ return StructuredData::GenericSP();
+
+ void *ret_val;
+
+ {
+ Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN,
+ Locker::FreeLock);
+ ret_val = LLDBSWIGPythonCreateOSPlugin(
+ class_name, m_dictionary_name.c_str(), process_sp);
+ }
+
+ return StructuredData::GenericSP(new StructuredPythonObject(ret_val));
+}
+
+StructuredData::DictionarySP ScriptInterpreterPythonImpl::OSPlugin_RegisterInfo(
+ StructuredData::ObjectSP os_plugin_object_sp) {
+ Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock);
+
+ static char callee_name[] = "get_register_info";
+
+ if (!os_plugin_object_sp)
+ return StructuredData::DictionarySP();
+
+ StructuredData::Generic *generic = os_plugin_object_sp->GetAsGeneric();
+ if (!generic)
+ return nullptr;
+
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)generic->GetValue());
+
+ if (!implementor.IsAllocated())
+ return StructuredData::DictionarySP();
+
+ PythonObject pmeth(PyRefType::Owned,
+ PyObject_GetAttrString(implementor.get(), callee_name));
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ if (!pmeth.IsAllocated())
+ return StructuredData::DictionarySP();
+
+ if (PyCallable_Check(pmeth.get()) == 0) {
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ return StructuredData::DictionarySP();
+ }
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ // right now we know this function exists and is callable..
+ PythonObject py_return(
+ PyRefType::Owned,
+ PyObject_CallMethod(implementor.get(), callee_name, nullptr));
+
+ // if it fails, print the error but otherwise go on
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+ if (py_return.get()) {
+ PythonDictionary result_dict(PyRefType::Borrowed, py_return.get());
+ return result_dict.CreateStructuredDictionary();
+ }
+ return StructuredData::DictionarySP();
+}
+
+StructuredData::ArraySP ScriptInterpreterPythonImpl::OSPlugin_ThreadsInfo(
+ StructuredData::ObjectSP os_plugin_object_sp) {
+ Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock);
+
+ static char callee_name[] = "get_thread_info";
+
+ if (!os_plugin_object_sp)
+ return StructuredData::ArraySP();
+
+ StructuredData::Generic *generic = os_plugin_object_sp->GetAsGeneric();
+ if (!generic)
+ return nullptr;
+
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)generic->GetValue());
+
+ if (!implementor.IsAllocated())
+ return StructuredData::ArraySP();
+
+ PythonObject pmeth(PyRefType::Owned,
+ PyObject_GetAttrString(implementor.get(), callee_name));
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ if (!pmeth.IsAllocated())
+ return StructuredData::ArraySP();
+
+ if (PyCallable_Check(pmeth.get()) == 0) {
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ return StructuredData::ArraySP();
+ }
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ // right now we know this function exists and is callable..
+ PythonObject py_return(
+ PyRefType::Owned,
+ PyObject_CallMethod(implementor.get(), callee_name, nullptr));
+
+ // if it fails, print the error but otherwise go on
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+
+ if (py_return.get()) {
+ PythonList result_list(PyRefType::Borrowed, py_return.get());
+ return result_list.CreateStructuredArray();
+ }
+ return StructuredData::ArraySP();
+}
+
+// GetPythonValueFormatString provides a system independent type safe way to
+// convert a variable's type into a python value format. Python value formats
+// are defined in terms of builtin C types and could change from system to as
+// the underlying typedef for uint* types, size_t, off_t and other values
+// change.
+
+template <typename T> const char *GetPythonValueFormatString(T t);
+template <> const char *GetPythonValueFormatString(char *) { return "s"; }
+template <> const char *GetPythonValueFormatString(char) { return "b"; }
+template <> const char *GetPythonValueFormatString(unsigned char) {
+ return "B";
+}
+template <> const char *GetPythonValueFormatString(short) { return "h"; }
+template <> const char *GetPythonValueFormatString(unsigned short) {
+ return "H";
+}
+template <> const char *GetPythonValueFormatString(int) { return "i"; }
+template <> const char *GetPythonValueFormatString(unsigned int) { return "I"; }
+template <> const char *GetPythonValueFormatString(long) { return "l"; }
+template <> const char *GetPythonValueFormatString(unsigned long) {
+ return "k";
+}
+template <> const char *GetPythonValueFormatString(long long) { return "L"; }
+template <> const char *GetPythonValueFormatString(unsigned long long) {
+ return "K";
+}
+template <> const char *GetPythonValueFormatString(float t) { return "f"; }
+template <> const char *GetPythonValueFormatString(double t) { return "d"; }
+
+StructuredData::StringSP
+ScriptInterpreterPythonImpl::OSPlugin_RegisterContextData(
+ StructuredData::ObjectSP os_plugin_object_sp, lldb::tid_t tid) {
+ Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock);
+
+ static char callee_name[] = "get_register_data";
+ static char *param_format =
+ const_cast<char *>(GetPythonValueFormatString(tid));
+
+ if (!os_plugin_object_sp)
+ return StructuredData::StringSP();
+
+ StructuredData::Generic *generic = os_plugin_object_sp->GetAsGeneric();
+ if (!generic)
+ return nullptr;
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)generic->GetValue());
+
+ if (!implementor.IsAllocated())
+ return StructuredData::StringSP();
+
+ PythonObject pmeth(PyRefType::Owned,
+ PyObject_GetAttrString(implementor.get(), callee_name));
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ if (!pmeth.IsAllocated())
+ return StructuredData::StringSP();
+
+ if (PyCallable_Check(pmeth.get()) == 0) {
+ if (PyErr_Occurred())
+ PyErr_Clear();
+ return StructuredData::StringSP();
+ }
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ // right now we know this function exists and is callable..
+ PythonObject py_return(
+ PyRefType::Owned,
+ PyObject_CallMethod(implementor.get(), callee_name, param_format, tid));
+
+ // if it fails, print the error but otherwise go on
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+
+ if (py_return.get()) {
+ PythonBytes result(PyRefType::Borrowed, py_return.get());
+ return result.CreateStructuredString();
+ }
+ return StructuredData::StringSP();
+}
+
+StructuredData::DictionarySP ScriptInterpreterPythonImpl::OSPlugin_CreateThread(
+ StructuredData::ObjectSP os_plugin_object_sp, lldb::tid_t tid,
+ lldb::addr_t context) {
+ Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock);
+
+ static char callee_name[] = "create_thread";
+ std::string param_format;
+ param_format += GetPythonValueFormatString(tid);
+ param_format += GetPythonValueFormatString(context);
+
+ if (!os_plugin_object_sp)
+ return StructuredData::DictionarySP();
+
+ StructuredData::Generic *generic = os_plugin_object_sp->GetAsGeneric();
+ if (!generic)
+ return nullptr;
+
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)generic->GetValue());
+
+ if (!implementor.IsAllocated())
+ return StructuredData::DictionarySP();
+
+ PythonObject pmeth(PyRefType::Owned,
+ PyObject_GetAttrString(implementor.get(), callee_name));
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ if (!pmeth.IsAllocated())
+ return StructuredData::DictionarySP();
+
+ if (PyCallable_Check(pmeth.get()) == 0) {
+ if (PyErr_Occurred())
+ PyErr_Clear();
+ return StructuredData::DictionarySP();
+ }
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ // right now we know this function exists and is callable..
+ PythonObject py_return(PyRefType::Owned,
+ PyObject_CallMethod(implementor.get(), callee_name,
+ &param_format[0], tid, context));
+
+ // if it fails, print the error but otherwise go on
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+
+ if (py_return.get()) {
+ PythonDictionary result_dict(PyRefType::Borrowed, py_return.get());
+ return result_dict.CreateStructuredDictionary();
+ }
+ return StructuredData::DictionarySP();
+}
+
+StructuredData::ObjectSP ScriptInterpreterPythonImpl::CreateScriptedThreadPlan(
+ const char *class_name, lldb::ThreadPlanSP thread_plan_sp) {
+ if (class_name == nullptr || class_name[0] == '\0')
+ return StructuredData::ObjectSP();
+
+ if (!thread_plan_sp.get())
+ return StructuredData::ObjectSP();
+
+ Debugger &debugger = thread_plan_sp->GetTarget().GetDebugger();
+ ScriptInterpreter *script_interpreter = debugger.GetScriptInterpreter();
+ ScriptInterpreterPythonImpl *python_interpreter =
+ static_cast<ScriptInterpreterPythonImpl *>(script_interpreter);
+
+ if (!script_interpreter)
+ return StructuredData::ObjectSP();
+
+ void *ret_val;
+
+ {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+
+ ret_val = LLDBSwigPythonCreateScriptedThreadPlan(
+ class_name, python_interpreter->m_dictionary_name.c_str(),
+ thread_plan_sp);
+ }
+
+ return StructuredData::ObjectSP(new StructuredPythonObject(ret_val));
+}
+
+bool ScriptInterpreterPythonImpl::ScriptedThreadPlanExplainsStop(
+ StructuredData::ObjectSP implementor_sp, Event *event, bool &script_error) {
+ bool explains_stop = true;
+ StructuredData::Generic *generic = nullptr;
+ if (implementor_sp)
+ generic = implementor_sp->GetAsGeneric();
+ if (generic) {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ explains_stop = LLDBSWIGPythonCallThreadPlan(
+ generic->GetValue(), "explains_stop", event, script_error);
+ if (script_error)
+ return true;
+ }
+ return explains_stop;
+}
+
+bool ScriptInterpreterPythonImpl::ScriptedThreadPlanShouldStop(
+ StructuredData::ObjectSP implementor_sp, Event *event, bool &script_error) {
+ bool should_stop = true;
+ StructuredData::Generic *generic = nullptr;
+ if (implementor_sp)
+ generic = implementor_sp->GetAsGeneric();
+ if (generic) {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ should_stop = LLDBSWIGPythonCallThreadPlan(
+ generic->GetValue(), "should_stop", event, script_error);
+ if (script_error)
+ return true;
+ }
+ return should_stop;
+}
+
+bool ScriptInterpreterPythonImpl::ScriptedThreadPlanIsStale(
+ StructuredData::ObjectSP implementor_sp, bool &script_error) {
+ bool is_stale = true;
+ StructuredData::Generic *generic = nullptr;
+ if (implementor_sp)
+ generic = implementor_sp->GetAsGeneric();
+ if (generic) {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ is_stale = LLDBSWIGPythonCallThreadPlan(generic->GetValue(), "is_stale",
+ nullptr, script_error);
+ if (script_error)
+ return true;
+ }
+ return is_stale;
+}
+
+lldb::StateType ScriptInterpreterPythonImpl::ScriptedThreadPlanGetRunState(
+ StructuredData::ObjectSP implementor_sp, bool &script_error) {
+ bool should_step = false;
+ StructuredData::Generic *generic = nullptr;
+ if (implementor_sp)
+ generic = implementor_sp->GetAsGeneric();
+ if (generic) {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ should_step = LLDBSWIGPythonCallThreadPlan(
+ generic->GetValue(), "should_step", nullptr, script_error);
+ if (script_error)
+ should_step = true;
+ }
+ if (should_step)
+ return lldb::eStateStepping;
+ else
+ return lldb::eStateRunning;
+}
+
+StructuredData::GenericSP
+ScriptInterpreterPythonImpl::CreateScriptedBreakpointResolver(
+ const char *class_name, StructuredDataImpl *args_data,
+ lldb::BreakpointSP &bkpt_sp) {
+
+ if (class_name == nullptr || class_name[0] == '\0')
+ return StructuredData::GenericSP();
+
+ if (!bkpt_sp.get())
+ return StructuredData::GenericSP();
+
+ Debugger &debugger = bkpt_sp->GetTarget().GetDebugger();
+ ScriptInterpreter *script_interpreter = debugger.GetScriptInterpreter();
+ ScriptInterpreterPythonImpl *python_interpreter =
+ static_cast<ScriptInterpreterPythonImpl *>(script_interpreter);
+
+ if (!script_interpreter)
+ return StructuredData::GenericSP();
+
+ void *ret_val;
+
+ {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+
+ ret_val = LLDBSwigPythonCreateScriptedBreakpointResolver(
+ class_name, python_interpreter->m_dictionary_name.c_str(), args_data,
+ bkpt_sp);
+ }
+
+ return StructuredData::GenericSP(new StructuredPythonObject(ret_val));
+}
+
+bool ScriptInterpreterPythonImpl::ScriptedBreakpointResolverSearchCallback(
+ StructuredData::GenericSP implementor_sp, SymbolContext *sym_ctx) {
+ bool should_continue = false;
+
+ if (implementor_sp) {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ should_continue = LLDBSwigPythonCallBreakpointResolver(
+ implementor_sp->GetValue(), "__callback__", sym_ctx);
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+ }
+ return should_continue;
+}
+
+lldb::SearchDepth
+ScriptInterpreterPythonImpl::ScriptedBreakpointResolverSearchDepth(
+ StructuredData::GenericSP implementor_sp) {
+ int depth_as_int = lldb::eSearchDepthModule;
+ if (implementor_sp) {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ depth_as_int = LLDBSwigPythonCallBreakpointResolver(
+ implementor_sp->GetValue(), "__get_depth__", nullptr);
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+ }
+ if (depth_as_int == lldb::eSearchDepthInvalid)
+ return lldb::eSearchDepthModule;
+
+ if (depth_as_int <= lldb::kLastSearchDepthKind)
+ return (lldb::SearchDepth)depth_as_int;
+ else
+ return lldb::eSearchDepthModule;
+}
+
+StructuredData::ObjectSP
+ScriptInterpreterPythonImpl::LoadPluginModule(const FileSpec &file_spec,
+ lldb_private::Status &error) {
+ if (!FileSystem::Instance().Exists(file_spec)) {
+ error.SetErrorString("no such file");
+ return StructuredData::ObjectSP();
+ }
+
+ StructuredData::ObjectSP module_sp;
+
+ if (LoadScriptingModule(file_spec.GetPath().c_str(), true, true, error,
+ &module_sp))
+ return module_sp;
+
+ return StructuredData::ObjectSP();
+}
+
+StructuredData::DictionarySP ScriptInterpreterPythonImpl::GetDynamicSettings(
+ StructuredData::ObjectSP plugin_module_sp, Target *target,
+ const char *setting_name, lldb_private::Status &error) {
+ if (!plugin_module_sp || !target || !setting_name || !setting_name[0])
+ return StructuredData::DictionarySP();
+ StructuredData::Generic *generic = plugin_module_sp->GetAsGeneric();
+ if (!generic)
+ return StructuredData::DictionarySP();
+
+ PythonObject reply_pyobj;
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ TargetSP target_sp(target->shared_from_this());
+ reply_pyobj.Reset(PyRefType::Owned,
+ (PyObject *)LLDBSWIGPython_GetDynamicSetting(
+ generic->GetValue(), setting_name, target_sp));
+
+ PythonDictionary py_dict(PyRefType::Borrowed, reply_pyobj.get());
+ return py_dict.CreateStructuredDictionary();
+}
+
+StructuredData::ObjectSP
+ScriptInterpreterPythonImpl::CreateSyntheticScriptedProvider(
+ const char *class_name, lldb::ValueObjectSP valobj) {
+ if (class_name == nullptr || class_name[0] == '\0')
+ return StructuredData::ObjectSP();
+
+ if (!valobj.get())
+ return StructuredData::ObjectSP();
+
+ ExecutionContext exe_ctx(valobj->GetExecutionContextRef());
+ Target *target = exe_ctx.GetTargetPtr();
+
+ if (!target)
+ return StructuredData::ObjectSP();
+
+ Debugger &debugger = target->GetDebugger();
+ ScriptInterpreter *script_interpreter = debugger.GetScriptInterpreter();
+ ScriptInterpreterPythonImpl *python_interpreter =
+ (ScriptInterpreterPythonImpl *)script_interpreter;
+
+ if (!script_interpreter)
+ return StructuredData::ObjectSP();
+
+ void *ret_val = nullptr;
+
+ {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ ret_val = LLDBSwigPythonCreateSyntheticProvider(
+ class_name, python_interpreter->m_dictionary_name.c_str(), valobj);
+ }
+
+ return StructuredData::ObjectSP(new StructuredPythonObject(ret_val));
+}
+
+StructuredData::GenericSP
+ScriptInterpreterPythonImpl::CreateScriptCommandObject(const char *class_name) {
+ DebuggerSP debugger_sp(m_debugger.shared_from_this());
+
+ if (class_name == nullptr || class_name[0] == '\0')
+ return StructuredData::GenericSP();
+
+ if (!debugger_sp.get())
+ return StructuredData::GenericSP();
+
+ void *ret_val;
+
+ {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ ret_val = LLDBSwigPythonCreateCommandObject(
+ class_name, m_dictionary_name.c_str(), debugger_sp);
+ }
+
+ return StructuredData::GenericSP(new StructuredPythonObject(ret_val));
+}
+
+bool ScriptInterpreterPythonImpl::GenerateTypeScriptFunction(
+ const char *oneliner, std::string &output, const void *name_token) {
+ StringList input;
+ input.SplitIntoLines(oneliner, strlen(oneliner));
+ return GenerateTypeScriptFunction(input, output, name_token);
+}
+
+bool ScriptInterpreterPythonImpl::GenerateTypeSynthClass(
+ const char *oneliner, std::string &output, const void *name_token) {
+ StringList input;
+ input.SplitIntoLines(oneliner, strlen(oneliner));
+ return GenerateTypeSynthClass(input, output, name_token);
+}
+
+Status ScriptInterpreterPythonImpl::GenerateBreakpointCommandCallbackData(
+ StringList &user_input, std::string &output) {
+ static uint32_t num_created_functions = 0;
+ user_input.RemoveBlankLines();
+ StreamString sstr;
+ Status error;
+ if (user_input.GetSize() == 0) {
+ error.SetErrorString("No input data.");
+ return error;
+ }
+
+ std::string auto_generated_function_name(GenerateUniqueName(
+ "lldb_autogen_python_bp_callback_func_", num_created_functions));
+ sstr.Printf("def %s (frame, bp_loc, internal_dict):",
+ auto_generated_function_name.c_str());
+
+ error = GenerateFunction(sstr.GetData(), user_input);
+ if (!error.Success())
+ return error;
+
+ // Store the name of the auto-generated function to be called.
+ output.assign(auto_generated_function_name);
+ return error;
+}
+
+bool ScriptInterpreterPythonImpl::GenerateWatchpointCommandCallbackData(
+ StringList &user_input, std::string &output) {
+ static uint32_t num_created_functions = 0;
+ user_input.RemoveBlankLines();
+ StreamString sstr;
+
+ if (user_input.GetSize() == 0)
+ return false;
+
+ std::string auto_generated_function_name(GenerateUniqueName(
+ "lldb_autogen_python_wp_callback_func_", num_created_functions));
+ sstr.Printf("def %s (frame, wp, internal_dict):",
+ auto_generated_function_name.c_str());
+
+ if (!GenerateFunction(sstr.GetData(), user_input).Success())
+ return false;
+
+ // Store the name of the auto-generated function to be called.
+ output.assign(auto_generated_function_name);
+ return true;
+}
+
+bool ScriptInterpreterPythonImpl::GetScriptedSummary(
+ const char *python_function_name, lldb::ValueObjectSP valobj,
+ StructuredData::ObjectSP &callee_wrapper_sp,
+ const TypeSummaryOptions &options, std::string &retval) {
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION);
+
+ if (!valobj.get()) {
+ retval.assign("<no object>");
+ return false;
+ }
+
+ void *old_callee = nullptr;
+ StructuredData::Generic *generic = nullptr;
+ if (callee_wrapper_sp) {
+ generic = callee_wrapper_sp->GetAsGeneric();
+ if (generic)
+ old_callee = generic->GetValue();
+ }
+ void *new_callee = old_callee;
+
+ bool ret_val;
+ if (python_function_name && *python_function_name) {
+ {
+ Locker py_lock(this, Locker::AcquireLock | Locker::InitSession |
+ Locker::NoSTDIN);
+ {
+ TypeSummaryOptionsSP options_sp(new TypeSummaryOptions(options));
+
+ static Timer::Category func_cat("LLDBSwigPythonCallTypeScript");
+ Timer scoped_timer(func_cat, "LLDBSwigPythonCallTypeScript");
+ ret_val = LLDBSwigPythonCallTypeScript(
+ python_function_name, GetSessionDictionary().get(), valobj,
+ &new_callee, options_sp, retval);
+ }
+ }
+ } else {
+ retval.assign("<no function name>");
+ return false;
+ }
+
+ if (new_callee && old_callee != new_callee)
+ callee_wrapper_sp = std::make_shared<StructuredPythonObject>(new_callee);
+
+ return ret_val;
+}
+
+void ScriptInterpreterPythonImpl::Clear() {
+ // Release any global variables that might have strong references to
+ // LLDB objects when clearing the python script interpreter.
+ Locker locker(this, Locker::AcquireLock, Locker::FreeAcquiredLock);
+
+ // This may be called as part of Py_Finalize. In that case the modules are
+ // destroyed in random order and we can't guarantee that we can access these.
+ if (Py_IsInitialized())
+ PyRun_SimpleString("lldb.debugger = None; lldb.target = None; lldb.process "
+ "= None; lldb.thread = None; lldb.frame = None");
+}
+
+bool ScriptInterpreterPythonImpl::BreakpointCallbackFunction(
+ void *baton, StoppointCallbackContext *context, user_id_t break_id,
+ user_id_t break_loc_id) {
+ CommandDataPython *bp_option_data = (CommandDataPython *)baton;
+ const char *python_function_name = bp_option_data->script_source.c_str();
+
+ if (!context)
+ return true;
+
+ ExecutionContext exe_ctx(context->exe_ctx_ref);
+ Target *target = exe_ctx.GetTargetPtr();
+
+ if (!target)
+ return true;
+
+ Debugger &debugger = target->GetDebugger();
+ ScriptInterpreter *script_interpreter = debugger.GetScriptInterpreter();
+ ScriptInterpreterPythonImpl *python_interpreter =
+ (ScriptInterpreterPythonImpl *)script_interpreter;
+
+ if (!script_interpreter)
+ return true;
+
+ if (python_function_name && python_function_name[0]) {
+ const StackFrameSP stop_frame_sp(exe_ctx.GetFrameSP());
+ BreakpointSP breakpoint_sp = target->GetBreakpointByID(break_id);
+ if (breakpoint_sp) {
+ const BreakpointLocationSP bp_loc_sp(
+ breakpoint_sp->FindLocationByID(break_loc_id));
+
+ if (stop_frame_sp && bp_loc_sp) {
+ bool ret_val = true;
+ {
+ Locker py_lock(python_interpreter, Locker::AcquireLock |
+ Locker::InitSession |
+ Locker::NoSTDIN);
+ ret_val = LLDBSwigPythonBreakpointCallbackFunction(
+ python_function_name,
+ python_interpreter->m_dictionary_name.c_str(), stop_frame_sp,
+ bp_loc_sp);
+ }
+ return ret_val;
+ }
+ }
+ }
+ // We currently always true so we stop in case anything goes wrong when
+ // trying to call the script function
+ return true;
+}
+
+bool ScriptInterpreterPythonImpl::WatchpointCallbackFunction(
+ void *baton, StoppointCallbackContext *context, user_id_t watch_id) {
+ WatchpointOptions::CommandData *wp_option_data =
+ (WatchpointOptions::CommandData *)baton;
+ const char *python_function_name = wp_option_data->script_source.c_str();
+
+ if (!context)
+ return true;
+
+ ExecutionContext exe_ctx(context->exe_ctx_ref);
+ Target *target = exe_ctx.GetTargetPtr();
+
+ if (!target)
+ return true;
+
+ Debugger &debugger = target->GetDebugger();
+ ScriptInterpreter *script_interpreter = debugger.GetScriptInterpreter();
+ ScriptInterpreterPythonImpl *python_interpreter =
+ (ScriptInterpreterPythonImpl *)script_interpreter;
+
+ if (!script_interpreter)
+ return true;
+
+ if (python_function_name && python_function_name[0]) {
+ const StackFrameSP stop_frame_sp(exe_ctx.GetFrameSP());
+ WatchpointSP wp_sp = target->GetWatchpointList().FindByID(watch_id);
+ if (wp_sp) {
+ if (stop_frame_sp && wp_sp) {
+ bool ret_val = true;
+ {
+ Locker py_lock(python_interpreter, Locker::AcquireLock |
+ Locker::InitSession |
+ Locker::NoSTDIN);
+ ret_val = LLDBSwigPythonWatchpointCallbackFunction(
+ python_function_name,
+ python_interpreter->m_dictionary_name.c_str(), stop_frame_sp,
+ wp_sp);
+ }
+ return ret_val;
+ }
+ }
+ }
+ // We currently always true so we stop in case anything goes wrong when
+ // trying to call the script function
+ return true;
+}
+
+size_t ScriptInterpreterPythonImpl::CalculateNumChildren(
+ const StructuredData::ObjectSP &implementor_sp, uint32_t max) {
+ if (!implementor_sp)
+ return 0;
+ StructuredData::Generic *generic = implementor_sp->GetAsGeneric();
+ if (!generic)
+ return 0;
+ void *implementor = generic->GetValue();
+ if (!implementor)
+ return 0;
+
+ size_t ret_val = 0;
+
+ {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ ret_val = LLDBSwigPython_CalculateNumChildren(implementor, max);
+ }
+
+ return ret_val;
+}
+
+lldb::ValueObjectSP ScriptInterpreterPythonImpl::GetChildAtIndex(
+ const StructuredData::ObjectSP &implementor_sp, uint32_t idx) {
+ if (!implementor_sp)
+ return lldb::ValueObjectSP();
+
+ StructuredData::Generic *generic = implementor_sp->GetAsGeneric();
+ if (!generic)
+ return lldb::ValueObjectSP();
+ void *implementor = generic->GetValue();
+ if (!implementor)
+ return lldb::ValueObjectSP();
+
+ lldb::ValueObjectSP ret_val;
+ {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ void *child_ptr = LLDBSwigPython_GetChildAtIndex(implementor, idx);
+ if (child_ptr != nullptr && child_ptr != Py_None) {
+ lldb::SBValue *sb_value_ptr =
+ (lldb::SBValue *)LLDBSWIGPython_CastPyObjectToSBValue(child_ptr);
+ if (sb_value_ptr == nullptr)
+ Py_XDECREF(child_ptr);
+ else
+ ret_val = LLDBSWIGPython_GetValueObjectSPFromSBValue(sb_value_ptr);
+ } else {
+ Py_XDECREF(child_ptr);
+ }
+ }
+
+ return ret_val;
+}
+
+int ScriptInterpreterPythonImpl::GetIndexOfChildWithName(
+ const StructuredData::ObjectSP &implementor_sp, const char *child_name) {
+ if (!implementor_sp)
+ return UINT32_MAX;
+
+ StructuredData::Generic *generic = implementor_sp->GetAsGeneric();
+ if (!generic)
+ return UINT32_MAX;
+ void *implementor = generic->GetValue();
+ if (!implementor)
+ return UINT32_MAX;
+
+ int ret_val = UINT32_MAX;
+
+ {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ ret_val = LLDBSwigPython_GetIndexOfChildWithName(implementor, child_name);
+ }
+
+ return ret_val;
+}
+
+bool ScriptInterpreterPythonImpl::UpdateSynthProviderInstance(
+ const StructuredData::ObjectSP &implementor_sp) {
+ bool ret_val = false;
+
+ if (!implementor_sp)
+ return ret_val;
+
+ StructuredData::Generic *generic = implementor_sp->GetAsGeneric();
+ if (!generic)
+ return ret_val;
+ void *implementor = generic->GetValue();
+ if (!implementor)
+ return ret_val;
+
+ {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ ret_val = LLDBSwigPython_UpdateSynthProviderInstance(implementor);
+ }
+
+ return ret_val;
+}
+
+bool ScriptInterpreterPythonImpl::MightHaveChildrenSynthProviderInstance(
+ const StructuredData::ObjectSP &implementor_sp) {
+ bool ret_val = false;
+
+ if (!implementor_sp)
+ return ret_val;
+
+ StructuredData::Generic *generic = implementor_sp->GetAsGeneric();
+ if (!generic)
+ return ret_val;
+ void *implementor = generic->GetValue();
+ if (!implementor)
+ return ret_val;
+
+ {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ ret_val =
+ LLDBSwigPython_MightHaveChildrenSynthProviderInstance(implementor);
+ }
+
+ return ret_val;
+}
+
+lldb::ValueObjectSP ScriptInterpreterPythonImpl::GetSyntheticValue(
+ const StructuredData::ObjectSP &implementor_sp) {
+ lldb::ValueObjectSP ret_val(nullptr);
+
+ if (!implementor_sp)
+ return ret_val;
+
+ StructuredData::Generic *generic = implementor_sp->GetAsGeneric();
+ if (!generic)
+ return ret_val;
+ void *implementor = generic->GetValue();
+ if (!implementor)
+ return ret_val;
+
+ {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ void *child_ptr = LLDBSwigPython_GetValueSynthProviderInstance(implementor);
+ if (child_ptr != nullptr && child_ptr != Py_None) {
+ lldb::SBValue *sb_value_ptr =
+ (lldb::SBValue *)LLDBSWIGPython_CastPyObjectToSBValue(child_ptr);
+ if (sb_value_ptr == nullptr)
+ Py_XDECREF(child_ptr);
+ else
+ ret_val = LLDBSWIGPython_GetValueObjectSPFromSBValue(sb_value_ptr);
+ } else {
+ Py_XDECREF(child_ptr);
+ }
+ }
+
+ return ret_val;
+}
+
+ConstString ScriptInterpreterPythonImpl::GetSyntheticTypeName(
+ const StructuredData::ObjectSP &implementor_sp) {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+
+ static char callee_name[] = "get_type_name";
+
+ ConstString ret_val;
+ bool got_string = false;
+ std::string buffer;
+
+ if (!implementor_sp)
+ return ret_val;
+
+ StructuredData::Generic *generic = implementor_sp->GetAsGeneric();
+ if (!generic)
+ return ret_val;
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)generic->GetValue());
+ if (!implementor.IsAllocated())
+ return ret_val;
+
+ PythonObject pmeth(PyRefType::Owned,
+ PyObject_GetAttrString(implementor.get(), callee_name));
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ if (!pmeth.IsAllocated())
+ return ret_val;
+
+ if (PyCallable_Check(pmeth.get()) == 0) {
+ if (PyErr_Occurred())
+ PyErr_Clear();
+ return ret_val;
+ }
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ // right now we know this function exists and is callable..
+ PythonObject py_return(
+ PyRefType::Owned,
+ PyObject_CallMethod(implementor.get(), callee_name, nullptr));
+
+ // if it fails, print the error but otherwise go on
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+
+ if (py_return.IsAllocated() && PythonString::Check(py_return.get())) {
+ PythonString py_string(PyRefType::Borrowed, py_return.get());
+ llvm::StringRef return_data(py_string.GetString());
+ if (!return_data.empty()) {
+ buffer.assign(return_data.data(), return_data.size());
+ got_string = true;
+ }
+ }
+
+ if (got_string)
+ ret_val.SetCStringWithLength(buffer.c_str(), buffer.size());
+
+ return ret_val;
+}
+
+bool ScriptInterpreterPythonImpl::RunScriptFormatKeyword(
+ const char *impl_function, Process *process, std::string &output,
+ Status &error) {
+ bool ret_val;
+ if (!process) {
+ error.SetErrorString("no process");
+ return false;
+ }
+ if (!impl_function || !impl_function[0]) {
+ error.SetErrorString("no function to execute");
+ return false;
+ }
+
+ {
+ ProcessSP process_sp(process->shared_from_this());
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ ret_val = LLDBSWIGPythonRunScriptKeywordProcess(
+ impl_function, m_dictionary_name.c_str(), process_sp, output);
+ if (!ret_val)
+ error.SetErrorString("python script evaluation failed");
+ }
+ return ret_val;
+}
+
+bool ScriptInterpreterPythonImpl::RunScriptFormatKeyword(
+ const char *impl_function, Thread *thread, std::string &output,
+ Status &error) {
+ bool ret_val;
+ if (!thread) {
+ error.SetErrorString("no thread");
+ return false;
+ }
+ if (!impl_function || !impl_function[0]) {
+ error.SetErrorString("no function to execute");
+ return false;
+ }
+
+ {
+ ThreadSP thread_sp(thread->shared_from_this());
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ ret_val = LLDBSWIGPythonRunScriptKeywordThread(
+ impl_function, m_dictionary_name.c_str(), thread_sp, output);
+ if (!ret_val)
+ error.SetErrorString("python script evaluation failed");
+ }
+ return ret_val;
+}
+
+bool ScriptInterpreterPythonImpl::RunScriptFormatKeyword(
+ const char *impl_function, Target *target, std::string &output,
+ Status &error) {
+ bool ret_val;
+ if (!target) {
+ error.SetErrorString("no thread");
+ return false;
+ }
+ if (!impl_function || !impl_function[0]) {
+ error.SetErrorString("no function to execute");
+ return false;
+ }
+
+ {
+ TargetSP target_sp(target->shared_from_this());
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ ret_val = LLDBSWIGPythonRunScriptKeywordTarget(
+ impl_function, m_dictionary_name.c_str(), target_sp, output);
+ if (!ret_val)
+ error.SetErrorString("python script evaluation failed");
+ }
+ return ret_val;
+}
+
+bool ScriptInterpreterPythonImpl::RunScriptFormatKeyword(
+ const char *impl_function, StackFrame *frame, std::string &output,
+ Status &error) {
+ bool ret_val;
+ if (!frame) {
+ error.SetErrorString("no frame");
+ return false;
+ }
+ if (!impl_function || !impl_function[0]) {
+ error.SetErrorString("no function to execute");
+ return false;
+ }
+
+ {
+ StackFrameSP frame_sp(frame->shared_from_this());
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ ret_val = LLDBSWIGPythonRunScriptKeywordFrame(
+ impl_function, m_dictionary_name.c_str(), frame_sp, output);
+ if (!ret_val)
+ error.SetErrorString("python script evaluation failed");
+ }
+ return ret_val;
+}
+
+bool ScriptInterpreterPythonImpl::RunScriptFormatKeyword(
+ const char *impl_function, ValueObject *value, std::string &output,
+ Status &error) {
+ bool ret_val;
+ if (!value) {
+ error.SetErrorString("no value");
+ return false;
+ }
+ if (!impl_function || !impl_function[0]) {
+ error.SetErrorString("no function to execute");
+ return false;
+ }
+
+ {
+ ValueObjectSP value_sp(value->GetSP());
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+ ret_val = LLDBSWIGPythonRunScriptKeywordValue(
+ impl_function, m_dictionary_name.c_str(), value_sp, output);
+ if (!ret_val)
+ error.SetErrorString("python script evaluation failed");
+ }
+ return ret_val;
+}
+
+uint64_t replace_all(std::string &str, const std::string &oldStr,
+ const std::string &newStr) {
+ size_t pos = 0;
+ uint64_t matches = 0;
+ while ((pos = str.find(oldStr, pos)) != std::string::npos) {
+ matches++;
+ str.replace(pos, oldStr.length(), newStr);
+ pos += newStr.length();
+ }
+ return matches;
+}
+
+bool ScriptInterpreterPythonImpl::LoadScriptingModule(
+ const char *pathname, bool can_reload, bool init_session,
+ lldb_private::Status &error, StructuredData::ObjectSP *module_sp) {
+ if (!pathname || !pathname[0]) {
+ error.SetErrorString("invalid pathname");
+ return false;
+ }
+
+ lldb::DebuggerSP debugger_sp = m_debugger.shared_from_this();
+
+ {
+ FileSpec target_file(pathname);
+ FileSystem::Instance().Resolve(target_file);
+ std::string basename(target_file.GetFilename().GetCString());
+
+ StreamString command_stream;
+
+ // Before executing Python code, lock the GIL.
+ Locker py_lock(this,
+ Locker::AcquireLock |
+ (init_session ? Locker::InitSession : 0) |
+ Locker::NoSTDIN,
+ Locker::FreeAcquiredLock |
+ (init_session ? Locker::TearDownSession : 0));
+ namespace fs = llvm::sys::fs;
+ fs::file_status st;
+ std::error_code ec = status(target_file.GetPath(), st);
+
+ if (ec || st.type() == fs::file_type::status_error ||
+ st.type() == fs::file_type::type_unknown ||
+ st.type() == fs::file_type::file_not_found) {
+ // if not a valid file of any sort, check if it might be a filename still
+ // dot can't be used but / and \ can, and if either is found, reject
+ if (strchr(pathname, '\\') || strchr(pathname, '/')) {
+ error.SetErrorString("invalid pathname");
+ return false;
+ }
+ basename = pathname; // not a filename, probably a package of some sort,
+ // let it go through
+ } else if (is_directory(st) || is_regular_file(st)) {
+ if (target_file.GetDirectory().IsEmpty()) {
+ error.SetErrorString("invalid directory name");
+ return false;
+ }
+
+ std::string directory = target_file.GetDirectory().GetCString();
+ replace_all(directory, "\\", "\\\\");
+ replace_all(directory, "'", "\\'");
+
+ // now make sure that Python has "directory" in the search path
+ StreamString command_stream;
+ command_stream.Printf("if not (sys.path.__contains__('%s')):\n "
+ "sys.path.insert(1,'%s');\n\n",
+ directory.c_str(), directory.c_str());
+ bool syspath_retval =
+ ExecuteMultipleLines(command_stream.GetData(),
+ ScriptInterpreter::ExecuteScriptOptions()
+ .SetEnableIO(false)
+ .SetSetLLDBGlobals(false))
+ .Success();
+ if (!syspath_retval) {
+ error.SetErrorString("Python sys.path handling failed");
+ return false;
+ }
+
+ // strip .py or .pyc extension
+ ConstString extension = target_file.GetFileNameExtension();
+ if (extension) {
+ if (llvm::StringRef(extension.GetCString()) == ".py")
+ basename.resize(basename.length() - 3);
+ else if (llvm::StringRef(extension.GetCString()) == ".pyc")
+ basename.resize(basename.length() - 4);
+ }
+ } else {
+ error.SetErrorString("no known way to import this module specification");
+ return false;
+ }
+
+ // check if the module is already import-ed
+ command_stream.Clear();
+ command_stream.Printf("sys.modules.__contains__('%s')", basename.c_str());
+ bool does_contain = false;
+ // this call will succeed if the module was ever imported in any Debugger
+ // in the lifetime of the process in which this LLDB framework is living
+ bool was_imported_globally =
+ (ExecuteOneLineWithReturn(
+ command_stream.GetData(),
+ ScriptInterpreterPythonImpl::eScriptReturnTypeBool, &does_contain,
+ ScriptInterpreter::ExecuteScriptOptions()
+ .SetEnableIO(false)
+ .SetSetLLDBGlobals(false)) &&
+ does_contain);
+ // this call will fail if the module was not imported in this Debugger
+ // before
+ command_stream.Clear();
+ command_stream.Printf("sys.getrefcount(%s)", basename.c_str());
+ bool was_imported_locally = GetSessionDictionary()
+ .GetItemForKey(PythonString(basename))
+ .IsAllocated();
+
+ bool was_imported = (was_imported_globally || was_imported_locally);
+
+ if (was_imported && !can_reload) {
+ error.SetErrorString("module already imported");
+ return false;
+ }
+
+ // now actually do the import
+ command_stream.Clear();
+
+ if (was_imported) {
+ if (!was_imported_locally)
+ command_stream.Printf("import %s ; reload_module(%s)", basename.c_str(),
+ basename.c_str());
+ else
+ command_stream.Printf("reload_module(%s)", basename.c_str());
+ } else
+ command_stream.Printf("import %s", basename.c_str());
+
+ error = ExecuteMultipleLines(command_stream.GetData(),
+ ScriptInterpreter::ExecuteScriptOptions()
+ .SetEnableIO(false)
+ .SetSetLLDBGlobals(false));
+ if (error.Fail())
+ return false;
+
+ // if we are here, everything worked
+ // call __lldb_init_module(debugger,dict)
+ if (!LLDBSwigPythonCallModuleInit(basename.c_str(),
+ m_dictionary_name.c_str(), debugger_sp)) {
+ error.SetErrorString("calling __lldb_init_module failed");
+ return false;
+ }
+
+ if (module_sp) {
+ // everything went just great, now set the module object
+ command_stream.Clear();
+ command_stream.Printf("%s", basename.c_str());
+ void *module_pyobj = nullptr;
+ if (ExecuteOneLineWithReturn(
+ command_stream.GetData(),
+ ScriptInterpreter::eScriptReturnTypeOpaqueObject,
+ &module_pyobj) &&
+ module_pyobj)
+ *module_sp = std::make_shared<StructuredPythonObject>(module_pyobj);
+ }
+
+ return true;
+ }
+}
+
+bool ScriptInterpreterPythonImpl::IsReservedWord(const char *word) {
+ if (!word || !word[0])
+ return false;
+
+ llvm::StringRef word_sr(word);
+
+ // filter out a few characters that would just confuse us and that are
+ // clearly not keyword material anyway
+ if (word_sr.find('"') != llvm::StringRef::npos ||
+ word_sr.find('\'') != llvm::StringRef::npos)
+ return false;
+
+ StreamString command_stream;
+ command_stream.Printf("keyword.iskeyword('%s')", word);
+ bool result;
+ ExecuteScriptOptions options;
+ options.SetEnableIO(false);
+ options.SetMaskoutErrors(true);
+ options.SetSetLLDBGlobals(false);
+ if (ExecuteOneLineWithReturn(command_stream.GetData(),
+ ScriptInterpreter::eScriptReturnTypeBool,
+ &result, options))
+ return result;
+ return false;
+}
+
+ScriptInterpreterPythonImpl::SynchronicityHandler::SynchronicityHandler(
+ lldb::DebuggerSP debugger_sp, ScriptedCommandSynchronicity synchro)
+ : m_debugger_sp(debugger_sp), m_synch_wanted(synchro),
+ m_old_asynch(debugger_sp->GetAsyncExecution()) {
+ if (m_synch_wanted == eScriptedCommandSynchronicitySynchronous)
+ m_debugger_sp->SetAsyncExecution(false);
+ else if (m_synch_wanted == eScriptedCommandSynchronicityAsynchronous)
+ m_debugger_sp->SetAsyncExecution(true);
+}
+
+ScriptInterpreterPythonImpl::SynchronicityHandler::~SynchronicityHandler() {
+ if (m_synch_wanted != eScriptedCommandSynchronicityCurrentValue)
+ m_debugger_sp->SetAsyncExecution(m_old_asynch);
+}
+
+bool ScriptInterpreterPythonImpl::RunScriptBasedCommand(
+ const char *impl_function, llvm::StringRef args,
+ ScriptedCommandSynchronicity synchronicity,
+ lldb_private::CommandReturnObject &cmd_retobj, Status &error,
+ const lldb_private::ExecutionContext &exe_ctx) {
+ if (!impl_function) {
+ error.SetErrorString("no function to execute");
+ return false;
+ }
+
+ lldb::DebuggerSP debugger_sp = m_debugger.shared_from_this();
+ lldb::ExecutionContextRefSP exe_ctx_ref_sp(new ExecutionContextRef(exe_ctx));
+
+ if (!debugger_sp.get()) {
+ error.SetErrorString("invalid Debugger pointer");
+ return false;
+ }
+
+ bool ret_val = false;
+
+ std::string err_msg;
+
+ {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession |
+ (cmd_retobj.GetInteractive() ? 0 : Locker::NoSTDIN),
+ Locker::FreeLock | Locker::TearDownSession);
+
+ SynchronicityHandler synch_handler(debugger_sp, synchronicity);
+
+ std::string args_str = args.str();
+ ret_val = LLDBSwigPythonCallCommand(
+ impl_function, m_dictionary_name.c_str(), debugger_sp, args_str.c_str(),
+ cmd_retobj, exe_ctx_ref_sp);
+ }
+
+ if (!ret_val)
+ error.SetErrorString("unable to execute script function");
+ else
+ error.Clear();
+
+ return ret_val;
+}
+
+bool ScriptInterpreterPythonImpl::RunScriptBasedCommand(
+ StructuredData::GenericSP impl_obj_sp, llvm::StringRef args,
+ ScriptedCommandSynchronicity synchronicity,
+ lldb_private::CommandReturnObject &cmd_retobj, Status &error,
+ const lldb_private::ExecutionContext &exe_ctx) {
+ if (!impl_obj_sp || !impl_obj_sp->IsValid()) {
+ error.SetErrorString("no function to execute");
+ return false;
+ }
+
+ lldb::DebuggerSP debugger_sp = m_debugger.shared_from_this();
+ lldb::ExecutionContextRefSP exe_ctx_ref_sp(new ExecutionContextRef(exe_ctx));
+
+ if (!debugger_sp.get()) {
+ error.SetErrorString("invalid Debugger pointer");
+ return false;
+ }
+
+ bool ret_val = false;
+
+ std::string err_msg;
+
+ {
+ Locker py_lock(this,
+ Locker::AcquireLock | Locker::InitSession |
+ (cmd_retobj.GetInteractive() ? 0 : Locker::NoSTDIN),
+ Locker::FreeLock | Locker::TearDownSession);
+
+ SynchronicityHandler synch_handler(debugger_sp, synchronicity);
+
+ std::string args_str = args.str();
+ ret_val = LLDBSwigPythonCallCommandObject(impl_obj_sp->GetValue(),
+ debugger_sp, args_str.c_str(),
+ cmd_retobj, exe_ctx_ref_sp);
+ }
+
+ if (!ret_val)
+ error.SetErrorString("unable to execute script function");
+ else
+ error.Clear();
+
+ return ret_val;
+}
+
+// in Python, a special attribute __doc__ contains the docstring for an object
+// (function, method, class, ...) if any is defined Otherwise, the attribute's
+// value is None
+bool ScriptInterpreterPythonImpl::GetDocumentationForItem(const char *item,
+ std::string &dest) {
+ dest.clear();
+ if (!item || !*item)
+ return false;
+ std::string command(item);
+ command += ".__doc__";
+
+ char *result_ptr = nullptr; // Python is going to point this to valid data if
+ // ExecuteOneLineWithReturn returns successfully
+
+ if (ExecuteOneLineWithReturn(
+ command.c_str(), ScriptInterpreter::eScriptReturnTypeCharStrOrNone,
+ &result_ptr,
+ ScriptInterpreter::ExecuteScriptOptions().SetEnableIO(false))) {
+ if (result_ptr)
+ dest.assign(result_ptr);
+ return true;
+ } else {
+ StreamString str_stream;
+ str_stream.Printf(
+ "Function %s was not found. Containing module might be missing.", item);
+ dest = str_stream.GetString();
+ return false;
+ }
+}
+
+bool ScriptInterpreterPythonImpl::GetShortHelpForCommandObject(
+ StructuredData::GenericSP cmd_obj_sp, std::string &dest) {
+ bool got_string = false;
+ dest.clear();
+
+ Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock);
+
+ static char callee_name[] = "get_short_help";
+
+ if (!cmd_obj_sp)
+ return false;
+
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)cmd_obj_sp->GetValue());
+
+ if (!implementor.IsAllocated())
+ return false;
+
+ PythonObject pmeth(PyRefType::Owned,
+ PyObject_GetAttrString(implementor.get(), callee_name));
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ if (!pmeth.IsAllocated())
+ return false;
+
+ if (PyCallable_Check(pmeth.get()) == 0) {
+ if (PyErr_Occurred())
+ PyErr_Clear();
+ return false;
+ }
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ // right now we know this function exists and is callable..
+ PythonObject py_return(
+ PyRefType::Owned,
+ PyObject_CallMethod(implementor.get(), callee_name, nullptr));
+
+ // if it fails, print the error but otherwise go on
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+
+ if (py_return.IsAllocated() && PythonString::Check(py_return.get())) {
+ PythonString py_string(PyRefType::Borrowed, py_return.get());
+ llvm::StringRef return_data(py_string.GetString());
+ dest.assign(return_data.data(), return_data.size());
+ got_string = true;
+ }
+ return got_string;
+}
+
+uint32_t ScriptInterpreterPythonImpl::GetFlagsForCommandObject(
+ StructuredData::GenericSP cmd_obj_sp) {
+ uint32_t result = 0;
+
+ Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock);
+
+ static char callee_name[] = "get_flags";
+
+ if (!cmd_obj_sp)
+ return result;
+
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)cmd_obj_sp->GetValue());
+
+ if (!implementor.IsAllocated())
+ return result;
+
+ PythonObject pmeth(PyRefType::Owned,
+ PyObject_GetAttrString(implementor.get(), callee_name));
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ if (!pmeth.IsAllocated())
+ return result;
+
+ if (PyCallable_Check(pmeth.get()) == 0) {
+ if (PyErr_Occurred())
+ PyErr_Clear();
+ return result;
+ }
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ // right now we know this function exists and is callable..
+ PythonObject py_return(
+ PyRefType::Owned,
+ PyObject_CallMethod(implementor.get(), callee_name, nullptr));
+
+ // if it fails, print the error but otherwise go on
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+
+ if (py_return.IsAllocated() && PythonInteger::Check(py_return.get())) {
+ PythonInteger int_value(PyRefType::Borrowed, py_return.get());
+ result = int_value.GetInteger();
+ }
+
+ return result;
+}
+
+bool ScriptInterpreterPythonImpl::GetLongHelpForCommandObject(
+ StructuredData::GenericSP cmd_obj_sp, std::string &dest) {
+ bool got_string = false;
+ dest.clear();
+
+ Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock);
+
+ static char callee_name[] = "get_long_help";
+
+ if (!cmd_obj_sp)
+ return false;
+
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)cmd_obj_sp->GetValue());
+
+ if (!implementor.IsAllocated())
+ return false;
+
+ PythonObject pmeth(PyRefType::Owned,
+ PyObject_GetAttrString(implementor.get(), callee_name));
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ if (!pmeth.IsAllocated())
+ return false;
+
+ if (PyCallable_Check(pmeth.get()) == 0) {
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ return false;
+ }
+
+ if (PyErr_Occurred())
+ PyErr_Clear();
+
+ // right now we know this function exists and is callable..
+ PythonObject py_return(
+ PyRefType::Owned,
+ PyObject_CallMethod(implementor.get(), callee_name, nullptr));
+
+ // if it fails, print the error but otherwise go on
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+
+ if (py_return.IsAllocated() && PythonString::Check(py_return.get())) {
+ PythonString str(PyRefType::Borrowed, py_return.get());
+ llvm::StringRef str_data(str.GetString());
+ dest.assign(str_data.data(), str_data.size());
+ got_string = true;
+ }
+
+ return got_string;
+}
+
+std::unique_ptr<ScriptInterpreterLocker>
+ScriptInterpreterPythonImpl::AcquireInterpreterLock() {
+ std::unique_ptr<ScriptInterpreterLocker> py_lock(new Locker(
+ this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN,
+ Locker::FreeLock | Locker::TearDownSession));
+ return py_lock;
+}
+
+void ScriptInterpreterPythonImpl::InitializePrivate() {
+ if (g_initialized)
+ return;
+
+ g_initialized = true;
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION);
+
+ // RAII-based initialization which correctly handles multiple-initialization,
+ // version- specific differences among Python 2 and Python 3, and saving and
+ // restoring various other pieces of state that can get mucked with during
+ // initialization.
+ InitializePythonRAII initialize_guard;
+
+ LLDBSwigPyInit();
+
+ // Update the path python uses to search for modules to include the current
+ // directory.
+
+ PyRun_SimpleString("import sys");
+ AddToSysPath(AddLocation::End, ".");
+
+ // Don't denormalize paths when calling file_spec.GetPath(). On platforms
+ // that use a backslash as the path separator, this will result in executing
+ // python code containing paths with unescaped backslashes. But Python also
+ // accepts forward slashes, so to make life easier we just use that.
+ if (FileSpec file_spec = GetPythonDir())
+ AddToSysPath(AddLocation::Beginning, file_spec.GetPath(false));
+ if (FileSpec file_spec = HostInfo::GetShlibDir())
+ AddToSysPath(AddLocation::Beginning, file_spec.GetPath(false));
+
+ PyRun_SimpleString("sys.dont_write_bytecode = 1; import "
+ "lldb.embedded_interpreter; from "
+ "lldb.embedded_interpreter import run_python_interpreter; "
+ "from lldb.embedded_interpreter import run_one_line");
+}
+
+void ScriptInterpreterPythonImpl::AddToSysPath(AddLocation location,
+ std::string path) {
+ std::string path_copy;
+
+ std::string statement;
+ if (location == AddLocation::Beginning) {
+ statement.assign("sys.path.insert(0,\"");
+ statement.append(path);
+ statement.append("\")");
+ } else {
+ statement.assign("sys.path.append(\"");
+ statement.append(path);
+ statement.append("\")");
+ }
+ PyRun_SimpleString(statement.c_str());
+}
+
+// We are intentionally NOT calling Py_Finalize here (this would be the logical
+// place to call it). Calling Py_Finalize here causes test suite runs to seg
+// fault: The test suite runs in Python. It registers SBDebugger::Terminate to
+// be called 'at_exit'. When the test suite Python harness finishes up, it
+// calls Py_Finalize, which calls all the 'at_exit' registered functions.
+// SBDebugger::Terminate calls Debugger::Terminate, which calls lldb::Terminate,
+// which calls ScriptInterpreter::Terminate, which calls
+// ScriptInterpreterPythonImpl::Terminate. So if we call Py_Finalize here, we
+// end up with Py_Finalize being called from within Py_Finalize, which results
+// in a seg fault. Since this function only gets called when lldb is shutting
+// down and going away anyway, the fact that we don't actually call Py_Finalize
+// should not cause any problems (everything should shut down/go away anyway
+// when the process exits).
+//
+// void ScriptInterpreterPythonImpl::Terminate() { Py_Finalize (); }
+
+#endif // LLDB_DISABLE_PYTHON
diff --git a/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h
new file mode 100644
index 000000000000..24941ec77452
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h
@@ -0,0 +1,57 @@
+//===-- ScriptInterpreterPython.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_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTINTERPRETERPYTHON_H
+#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTINTERPRETERPYTHON_H
+
+#ifdef LLDB_DISABLE_PYTHON
+
+// Python is disabled in this build
+
+#else
+
+#include "lldb/Breakpoint/BreakpointOptions.h"
+#include "lldb/Core/IOHandler.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/lldb-private.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace lldb_private {
+/// Abstract interface for the Python script interpreter.
+class ScriptInterpreterPython : public ScriptInterpreter,
+ public IOHandlerDelegateMultiline {
+public:
+ class CommandDataPython : public BreakpointOptions::CommandData {
+ public:
+ CommandDataPython() : BreakpointOptions::CommandData() {
+ interpreter = lldb::eScriptLanguagePython;
+ }
+ };
+
+ ScriptInterpreterPython(Debugger &debugger)
+ : ScriptInterpreter(debugger, lldb::eScriptLanguagePython),
+ IOHandlerDelegateMultiline("DONE") {}
+
+ static void Initialize();
+ static void Terminate();
+ static lldb_private::ConstString GetPluginNameStatic();
+ static const char *GetPluginDescriptionStatic();
+ static FileSpec GetPythonDir();
+
+protected:
+ static void ComputePythonDirForApple(llvm::SmallVectorImpl<char> &path);
+ static void ComputePythonDirForPosix(llvm::SmallVectorImpl<char> &path);
+ static void ComputePythonDirForWindows(llvm::SmallVectorImpl<char> &path);
+};
+} // namespace lldb_private
+
+#endif // LLDB_DISABLE_PYTHON
+#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTINTERPRETERPYTHON_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
new file mode 100644
index 000000000000..a9993c4068a2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
@@ -0,0 +1,473 @@
+//===-- ScriptInterpreterPythonImpl.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifdef LLDB_DISABLE_PYTHON
+
+// Python is disabled in this build
+
+#else
+
+#include "lldb-python.h"
+
+#include "PythonDataObjects.h"
+#include "ScriptInterpreterPython.h"
+
+#include "lldb/Host/Terminal.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace lldb_private {
+class IOHandlerPythonInterpreter;
+class ScriptInterpreterPythonImpl : public ScriptInterpreterPython {
+public:
+ friend class IOHandlerPythonInterpreter;
+
+ ScriptInterpreterPythonImpl(Debugger &debugger);
+
+ ~ScriptInterpreterPythonImpl() override;
+
+ bool Interrupt() override;
+
+ bool ExecuteOneLine(
+ llvm::StringRef command, CommandReturnObject *result,
+ const ExecuteScriptOptions &options = ExecuteScriptOptions()) override;
+
+ void ExecuteInterpreterLoop() override;
+
+ bool ExecuteOneLineWithReturn(
+ llvm::StringRef in_string,
+ ScriptInterpreter::ScriptReturnType return_type, void *ret_value,
+ const ExecuteScriptOptions &options = ExecuteScriptOptions()) override;
+
+ lldb_private::Status ExecuteMultipleLines(
+ const char *in_string,
+ const ExecuteScriptOptions &options = ExecuteScriptOptions()) override;
+
+ Status
+ ExportFunctionDefinitionToInterpreter(StringList &function_def) override;
+
+ bool GenerateTypeScriptFunction(StringList &input, std::string &output,
+ const void *name_token = nullptr) override;
+
+ bool GenerateTypeSynthClass(StringList &input, std::string &output,
+ const void *name_token = nullptr) override;
+
+ bool GenerateTypeSynthClass(const char *oneliner, std::string &output,
+ const void *name_token = nullptr) override;
+
+ // use this if the function code is just a one-liner script
+ bool GenerateTypeScriptFunction(const char *oneliner, std::string &output,
+ const void *name_token = nullptr) override;
+
+ bool GenerateScriptAliasFunction(StringList &input,
+ std::string &output) override;
+
+ StructuredData::ObjectSP
+ CreateSyntheticScriptedProvider(const char *class_name,
+ lldb::ValueObjectSP valobj) override;
+
+ StructuredData::GenericSP
+ CreateScriptCommandObject(const char *class_name) override;
+
+ StructuredData::ObjectSP
+ CreateScriptedThreadPlan(const char *class_name,
+ lldb::ThreadPlanSP thread_plan) override;
+
+ bool ScriptedThreadPlanExplainsStop(StructuredData::ObjectSP implementor_sp,
+ Event *event,
+ bool &script_error) override;
+
+ bool ScriptedThreadPlanShouldStop(StructuredData::ObjectSP implementor_sp,
+ Event *event, bool &script_error) override;
+
+ bool ScriptedThreadPlanIsStale(StructuredData::ObjectSP implementor_sp,
+ bool &script_error) override;
+
+ lldb::StateType
+ ScriptedThreadPlanGetRunState(StructuredData::ObjectSP implementor_sp,
+ bool &script_error) override;
+
+ StructuredData::GenericSP
+ CreateScriptedBreakpointResolver(const char *class_name,
+ StructuredDataImpl *args_data,
+ lldb::BreakpointSP &bkpt_sp) override;
+ bool ScriptedBreakpointResolverSearchCallback(
+ StructuredData::GenericSP implementor_sp,
+ SymbolContext *sym_ctx) override;
+
+ lldb::SearchDepth ScriptedBreakpointResolverSearchDepth(
+ StructuredData::GenericSP implementor_sp) override;
+
+ StructuredData::GenericSP
+ CreateFrameRecognizer(const char *class_name) override;
+
+ lldb::ValueObjectListSP
+ GetRecognizedArguments(const StructuredData::ObjectSP &implementor,
+ lldb::StackFrameSP frame_sp) override;
+
+ StructuredData::GenericSP
+ OSPlugin_CreatePluginObject(const char *class_name,
+ lldb::ProcessSP process_sp) override;
+
+ StructuredData::DictionarySP
+ OSPlugin_RegisterInfo(StructuredData::ObjectSP os_plugin_object_sp) override;
+
+ StructuredData::ArraySP
+ OSPlugin_ThreadsInfo(StructuredData::ObjectSP os_plugin_object_sp) override;
+
+ StructuredData::StringSP
+ OSPlugin_RegisterContextData(StructuredData::ObjectSP os_plugin_object_sp,
+ lldb::tid_t thread_id) override;
+
+ StructuredData::DictionarySP
+ OSPlugin_CreateThread(StructuredData::ObjectSP os_plugin_object_sp,
+ lldb::tid_t tid, lldb::addr_t context) override;
+
+ StructuredData::ObjectSP
+ LoadPluginModule(const FileSpec &file_spec,
+ lldb_private::Status &error) override;
+
+ StructuredData::DictionarySP
+ GetDynamicSettings(StructuredData::ObjectSP plugin_module_sp, Target *target,
+ const char *setting_name,
+ lldb_private::Status &error) override;
+
+ size_t CalculateNumChildren(const StructuredData::ObjectSP &implementor,
+ uint32_t max) override;
+
+ lldb::ValueObjectSP
+ GetChildAtIndex(const StructuredData::ObjectSP &implementor,
+ uint32_t idx) override;
+
+ int GetIndexOfChildWithName(const StructuredData::ObjectSP &implementor,
+ const char *child_name) override;
+
+ bool UpdateSynthProviderInstance(
+ const StructuredData::ObjectSP &implementor) override;
+
+ bool MightHaveChildrenSynthProviderInstance(
+ const StructuredData::ObjectSP &implementor) override;
+
+ lldb::ValueObjectSP
+ GetSyntheticValue(const StructuredData::ObjectSP &implementor) override;
+
+ ConstString
+ GetSyntheticTypeName(const StructuredData::ObjectSP &implementor) override;
+
+ bool
+ RunScriptBasedCommand(const char *impl_function, llvm::StringRef args,
+ ScriptedCommandSynchronicity synchronicity,
+ lldb_private::CommandReturnObject &cmd_retobj,
+ Status &error,
+ const lldb_private::ExecutionContext &exe_ctx) override;
+
+ bool RunScriptBasedCommand(
+ StructuredData::GenericSP impl_obj_sp, llvm::StringRef args,
+ ScriptedCommandSynchronicity synchronicity,
+ lldb_private::CommandReturnObject &cmd_retobj, Status &error,
+ const lldb_private::ExecutionContext &exe_ctx) override;
+
+ Status GenerateFunction(const char *signature,
+ const StringList &input) override;
+
+ Status GenerateBreakpointCommandCallbackData(StringList &input,
+ std::string &output) override;
+
+ bool GenerateWatchpointCommandCallbackData(StringList &input,
+ std::string &output) override;
+
+ bool GetScriptedSummary(const char *function_name, lldb::ValueObjectSP valobj,
+ StructuredData::ObjectSP &callee_wrapper_sp,
+ const TypeSummaryOptions &options,
+ std::string &retval) override;
+
+ void Clear() override;
+
+ bool GetDocumentationForItem(const char *item, std::string &dest) override;
+
+ bool GetShortHelpForCommandObject(StructuredData::GenericSP cmd_obj_sp,
+ std::string &dest) override;
+
+ uint32_t
+ GetFlagsForCommandObject(StructuredData::GenericSP cmd_obj_sp) override;
+
+ bool GetLongHelpForCommandObject(StructuredData::GenericSP cmd_obj_sp,
+ std::string &dest) override;
+
+ bool CheckObjectExists(const char *name) override {
+ if (!name || !name[0])
+ return false;
+ std::string temp;
+ return GetDocumentationForItem(name, temp);
+ }
+
+ bool RunScriptFormatKeyword(const char *impl_function, Process *process,
+ std::string &output, Status &error) override;
+
+ bool RunScriptFormatKeyword(const char *impl_function, Thread *thread,
+ std::string &output, Status &error) override;
+
+ bool RunScriptFormatKeyword(const char *impl_function, Target *target,
+ std::string &output, Status &error) override;
+
+ bool RunScriptFormatKeyword(const char *impl_function, StackFrame *frame,
+ std::string &output, Status &error) override;
+
+ bool RunScriptFormatKeyword(const char *impl_function, ValueObject *value,
+ std::string &output, Status &error) override;
+
+ bool
+ LoadScriptingModule(const char *filename, bool can_reload, bool init_session,
+ lldb_private::Status &error,
+ StructuredData::ObjectSP *module_sp = nullptr) override;
+
+ bool IsReservedWord(const char *word) override;
+
+ std::unique_ptr<ScriptInterpreterLocker> AcquireInterpreterLock() override;
+
+ void CollectDataForBreakpointCommandCallback(
+ std::vector<BreakpointOptions *> &bp_options_vec,
+ CommandReturnObject &result) override;
+
+ void
+ CollectDataForWatchpointCommandCallback(WatchpointOptions *wp_options,
+ CommandReturnObject &result) override;
+
+ /// Set the callback body text into the callback for the breakpoint.
+ Status SetBreakpointCommandCallback(BreakpointOptions *bp_options,
+ const char *callback_body) override;
+
+ void SetBreakpointCommandCallbackFunction(BreakpointOptions *bp_options,
+ const char *function_name) override;
+
+ /// This one is for deserialization:
+ Status SetBreakpointCommandCallback(
+ BreakpointOptions *bp_options,
+ std::unique_ptr<BreakpointOptions::CommandData> &data_up) override;
+
+ /// Set a one-liner as the callback for the watchpoint.
+ void SetWatchpointCommandCallback(WatchpointOptions *wp_options,
+ const char *oneliner) override;
+
+ void ResetOutputFileHandle(FILE *new_fh) override;
+
+ const char *GetDictionaryName() { return m_dictionary_name.c_str(); }
+
+ PyThreadState *GetThreadState() { return m_command_thread_state; }
+
+ void SetThreadState(PyThreadState *s) {
+ if (s)
+ m_command_thread_state = s;
+ }
+
+ // IOHandlerDelegate
+ void IOHandlerActivated(IOHandler &io_handler, bool interactive) override;
+
+ void IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &data) override;
+
+ static lldb::ScriptInterpreterSP CreateInstance(Debugger &debugger);
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ class Locker : public ScriptInterpreterLocker {
+ public:
+ enum OnEntry {
+ AcquireLock = 0x0001,
+ InitSession = 0x0002,
+ InitGlobals = 0x0004,
+ NoSTDIN = 0x0008
+ };
+
+ enum OnLeave {
+ FreeLock = 0x0001,
+ FreeAcquiredLock = 0x0002, // do not free the lock if we already held it
+ // when calling constructor
+ TearDownSession = 0x0004
+ };
+
+ Locker(ScriptInterpreterPythonImpl *py_interpreter = nullptr,
+ uint16_t on_entry = AcquireLock | InitSession,
+ uint16_t on_leave = FreeLock | TearDownSession, FILE *in = nullptr,
+ FILE *out = nullptr, FILE *err = nullptr);
+
+ ~Locker() override;
+
+ private:
+ bool DoAcquireLock();
+
+ bool DoInitSession(uint16_t on_entry_flags, FILE *in, FILE *out, FILE *err);
+
+ bool DoFreeLock();
+
+ bool DoTearDownSession();
+
+ bool m_teardown_session;
+ ScriptInterpreterPythonImpl *m_python_interpreter;
+ // FILE* m_tmp_fh;
+ PyGILState_STATE m_GILState;
+ };
+
+ static bool BreakpointCallbackFunction(void *baton,
+ StoppointCallbackContext *context,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id);
+ static bool WatchpointCallbackFunction(void *baton,
+ StoppointCallbackContext *context,
+ lldb::user_id_t watch_id);
+ static void InitializePrivate();
+
+ class SynchronicityHandler {
+ private:
+ lldb::DebuggerSP m_debugger_sp;
+ ScriptedCommandSynchronicity m_synch_wanted;
+ bool m_old_asynch;
+
+ public:
+ SynchronicityHandler(lldb::DebuggerSP, ScriptedCommandSynchronicity);
+
+ ~SynchronicityHandler();
+ };
+
+ enum class AddLocation { Beginning, End };
+
+ static void AddToSysPath(AddLocation location, std::string path);
+
+ bool EnterSession(uint16_t on_entry_flags, FILE *in, FILE *out, FILE *err);
+
+ void LeaveSession();
+
+ void SaveTerminalState(int fd);
+
+ void RestoreTerminalState();
+
+ uint32_t IsExecutingPython() const { return m_lock_count > 0; }
+
+ uint32_t IncrementLockCount() { return ++m_lock_count; }
+
+ uint32_t DecrementLockCount() {
+ if (m_lock_count > 0)
+ --m_lock_count;
+ return m_lock_count;
+ }
+
+ enum ActiveIOHandler {
+ eIOHandlerNone,
+ eIOHandlerBreakpoint,
+ eIOHandlerWatchpoint
+ };
+
+ PythonObject &GetMainModule();
+
+ PythonDictionary &GetSessionDictionary();
+
+ PythonDictionary &GetSysModuleDictionary();
+
+ bool GetEmbeddedInterpreterModuleObjects();
+
+ bool SetStdHandle(File &file, const char *py_name, PythonFile &save_file,
+ const char *mode);
+
+ PythonFile m_saved_stdin;
+ PythonFile m_saved_stdout;
+ PythonFile m_saved_stderr;
+ PythonObject m_main_module;
+ PythonDictionary m_session_dict;
+ PythonDictionary m_sys_module_dict;
+ PythonObject m_run_one_line_function;
+ PythonObject m_run_one_line_str_global;
+ std::string m_dictionary_name;
+ TerminalState m_terminal_state;
+ ActiveIOHandler m_active_io_handler;
+ bool m_session_is_active;
+ bool m_pty_slave_is_open;
+ bool m_valid_session;
+ uint32_t m_lock_count;
+ PyThreadState *m_command_thread_state;
+};
+
+class IOHandlerPythonInterpreter : public IOHandler {
+public:
+ IOHandlerPythonInterpreter(Debugger &debugger,
+ ScriptInterpreterPythonImpl *python)
+ : IOHandler(debugger, IOHandler::Type::PythonInterpreter),
+ m_python(python) {}
+
+ ~IOHandlerPythonInterpreter() override {}
+
+ ConstString GetControlSequence(char ch) override {
+ if (ch == 'd')
+ return ConstString("quit()\n");
+ return ConstString();
+ }
+
+ void Run() override {
+ if (m_python) {
+ int stdin_fd = GetInputFD();
+ if (stdin_fd >= 0) {
+ Terminal terminal(stdin_fd);
+ TerminalState terminal_state;
+ const bool is_a_tty = terminal.IsATerminal();
+
+ if (is_a_tty) {
+ terminal_state.Save(stdin_fd, false);
+ terminal.SetCanonical(false);
+ terminal.SetEcho(true);
+ }
+
+ ScriptInterpreterPythonImpl::Locker locker(
+ m_python,
+ ScriptInterpreterPythonImpl::Locker::AcquireLock |
+ ScriptInterpreterPythonImpl::Locker::InitSession |
+ ScriptInterpreterPythonImpl::Locker::InitGlobals,
+ ScriptInterpreterPythonImpl::Locker::FreeAcquiredLock |
+ ScriptInterpreterPythonImpl::Locker::TearDownSession);
+
+ // The following call drops into the embedded interpreter loop and
+ // stays there until the user chooses to exit from the Python
+ // interpreter. This embedded interpreter will, as any Python code that
+ // performs I/O, unlock the GIL before a system call that can hang, and
+ // lock it when the syscall has returned.
+
+ // We need to surround the call to the embedded interpreter with calls
+ // to PyGILState_Ensure and PyGILState_Release (using the Locker
+ // above). This is because Python has a global lock which must be held
+ // whenever we want to touch any Python objects. Otherwise, if the user
+ // calls Python code, the interpreter state will be off, and things
+ // could hang (it's happened before).
+
+ StreamString run_string;
+ run_string.Printf("run_python_interpreter (%s)",
+ m_python->GetDictionaryName());
+ PyRun_SimpleString(run_string.GetData());
+
+ if (is_a_tty)
+ terminal_state.Restore();
+ }
+ }
+ SetIsDone(true);
+ }
+
+ void Cancel() override {}
+
+ bool Interrupt() override { return m_python->Interrupt(); }
+
+ void GotEOF() override {}
+
+protected:
+ ScriptInterpreterPythonImpl *m_python;
+};
+
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/lldb-python.h b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/lldb-python.h
new file mode 100644
index 000000000000..884514da9924
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/lldb-python.h
@@ -0,0 +1,45 @@
+//===-- lldb-python.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_PLUGINS_SCRIPTINTERPRETER_PYTHON_LLDB_PYTHON_H
+#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_LLDB_PYTHON_H
+
+// Python.h needs to be included before any system headers in order to avoid
+// redefinition of macros
+
+#ifdef LLDB_DISABLE_PYTHON
+// Python is disabled in this build
+#else
+#include "llvm/Support/Compiler.h"
+#if defined(_WIN32)
+// If anyone #includes Host/PosixApi.h later, it will try to typedef pid_t. We
+// need to ensure this doesn't happen. At the same time, Python.h will also try
+// to redefine a bunch of stuff that PosixApi.h defines. So define it all now
+// so that PosixApi.h doesn't redefine it.
+#define NO_PID_T
+#endif
+#if defined(__linux__)
+// features.h will define _POSIX_C_SOURCE if _GNU_SOURCE is defined. This value
+// may be different from the value that Python defines it to be which results
+// in a warning. Undefine _POSIX_C_SOURCE before including Python.h The same
+// holds for _XOPEN_SOURCE.
+#undef _POSIX_C_SOURCE
+#undef _XOPEN_SOURCE
+#endif
+
+// Include locale before Python so _PY_PORT_CTYPE_UTF8_ISSUE doesn't cause
+// macro redefinitions.
+#if defined(__APPLE__)
+#include <locale>
+#endif
+
+// Include python for non windows machines
+#include <Python.h>
+#endif // LLDB_DISABLE_PYTHON
+
+#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_LLDB_PYTHON_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp b/contrib/llvm-project/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp
new file mode 100644
index 000000000000..81a27125c036
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp
@@ -0,0 +1,1924 @@
+//===-- StructuredDataDarwinLog.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 "StructuredDataDarwinLog.h"
+
+#include <string.h>
+
+#include <memory>
+#include <sstream>
+
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+#include "lldb/Interpreter/OptionValueString.h"
+#include "lldb/Interpreter/Property.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadPlanCallOnFunctionExit.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegularExpression.h"
+
+#define DARWIN_LOG_TYPE_VALUE "DarwinLog"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#pragma mark -
+#pragma mark Anonymous Namespace
+
+// Anonymous namespace
+
+namespace sddarwinlog_private {
+const uint64_t NANOS_PER_MICRO = 1000;
+const uint64_t NANOS_PER_MILLI = NANOS_PER_MICRO * 1000;
+const uint64_t NANOS_PER_SECOND = NANOS_PER_MILLI * 1000;
+const uint64_t NANOS_PER_MINUTE = NANOS_PER_SECOND * 60;
+const uint64_t NANOS_PER_HOUR = NANOS_PER_MINUTE * 60;
+
+static bool DEFAULT_FILTER_FALLTHROUGH_ACCEPTS = true;
+
+/// Global, sticky enable switch. If true, the user has explicitly
+/// run the enable command. When a process launches or is attached to,
+/// we will enable DarwinLog if either the settings for auto-enable is
+/// on, or if the user had explicitly run enable at some point prior
+/// to the launch/attach.
+static bool s_is_explicitly_enabled;
+
+class EnableOptions;
+using EnableOptionsSP = std::shared_ptr<EnableOptions>;
+
+using OptionsMap =
+ std::map<DebuggerWP, EnableOptionsSP, std::owner_less<DebuggerWP>>;
+
+static OptionsMap &GetGlobalOptionsMap() {
+ static OptionsMap s_options_map;
+ return s_options_map;
+}
+
+static std::mutex &GetGlobalOptionsMapLock() {
+ static std::mutex s_options_map_lock;
+ return s_options_map_lock;
+}
+
+EnableOptionsSP GetGlobalEnableOptions(const DebuggerSP &debugger_sp) {
+ if (!debugger_sp)
+ return EnableOptionsSP();
+
+ std::lock_guard<std::mutex> locker(GetGlobalOptionsMapLock());
+ OptionsMap &options_map = GetGlobalOptionsMap();
+ DebuggerWP debugger_wp(debugger_sp);
+ auto find_it = options_map.find(debugger_wp);
+ if (find_it != options_map.end())
+ return find_it->second;
+ else
+ return EnableOptionsSP();
+}
+
+void SetGlobalEnableOptions(const DebuggerSP &debugger_sp,
+ const EnableOptionsSP &options_sp) {
+ std::lock_guard<std::mutex> locker(GetGlobalOptionsMapLock());
+ OptionsMap &options_map = GetGlobalOptionsMap();
+ DebuggerWP debugger_wp(debugger_sp);
+ auto find_it = options_map.find(debugger_wp);
+ if (find_it != options_map.end())
+ find_it->second = options_sp;
+ else
+ options_map.insert(std::make_pair(debugger_wp, options_sp));
+}
+
+#pragma mark -
+#pragma mark Settings Handling
+
+/// Code to handle the StructuredDataDarwinLog settings
+
+static constexpr PropertyDefinition g_properties[] = {
+ {
+ "enable-on-startup", // name
+ OptionValue::eTypeBoolean, // type
+ true, // global
+ false, // default uint value
+ nullptr, // default cstring value
+ {}, // enum values
+ "Enable Darwin os_log collection when debugged process is launched "
+ "or attached." // description
+ },
+ {
+ "auto-enable-options", // name
+ OptionValue::eTypeString, // type
+ true, // global
+ 0, // default uint value
+ "", // default cstring value
+ {}, // enum values
+ "Specify the options to 'plugin structured-data darwin-log enable' "
+ "that should be applied when automatically enabling logging on "
+ "startup/attach." // description
+ }};
+
+enum { ePropertyEnableOnStartup = 0, ePropertyAutoEnableOptions = 1 };
+
+class StructuredDataDarwinLogProperties : public Properties {
+public:
+ static ConstString &GetSettingName() {
+ static ConstString g_setting_name("darwin-log");
+ return g_setting_name;
+ }
+
+ StructuredDataDarwinLogProperties() : Properties() {
+ m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
+ m_collection_sp->Initialize(g_properties);
+ }
+
+ ~StructuredDataDarwinLogProperties() override {}
+
+ bool GetEnableOnStartup() const {
+ const uint32_t idx = ePropertyEnableOnStartup;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value != 0);
+ }
+
+ llvm::StringRef GetAutoEnableOptions() const {
+ const uint32_t idx = ePropertyAutoEnableOptions;
+ return m_collection_sp->GetPropertyAtIndexAsString(
+ nullptr, idx, g_properties[idx].default_cstr_value);
+ }
+
+ const char *GetLoggingModuleName() const { return "libsystem_trace.dylib"; }
+};
+
+using StructuredDataDarwinLogPropertiesSP =
+ std::shared_ptr<StructuredDataDarwinLogProperties>;
+
+static const StructuredDataDarwinLogPropertiesSP &GetGlobalProperties() {
+ static StructuredDataDarwinLogPropertiesSP g_settings_sp;
+ if (!g_settings_sp)
+ g_settings_sp = std::make_shared<StructuredDataDarwinLogProperties>();
+ return g_settings_sp;
+}
+
+const char *const s_filter_attributes[] = {
+ "activity", // current activity
+ "activity-chain", // entire activity chain, each level separated by ':'
+ "category", // category of the log message
+ "message", // message contents, fully expanded
+ "subsystem" // subsystem of the log message
+
+ // Consider implementing this action as it would be cheaper to filter.
+ // "message" requires always formatting the message, which is a waste of
+ // cycles if it ends up being rejected. "format", // format string
+ // used to format message text
+};
+
+static ConstString GetDarwinLogTypeName() {
+ static const ConstString s_key_name("DarwinLog");
+ return s_key_name;
+}
+
+static ConstString GetLogEventType() {
+ static const ConstString s_event_type("log");
+ return s_event_type;
+}
+
+class FilterRule;
+using FilterRuleSP = std::shared_ptr<FilterRule>;
+
+class FilterRule {
+public:
+ virtual ~FilterRule() {}
+
+ using OperationCreationFunc =
+ std::function<FilterRuleSP(bool accept, size_t attribute_index,
+ const std::string &op_arg, Status &error)>;
+
+ static void RegisterOperation(ConstString operation,
+ const OperationCreationFunc &creation_func) {
+ GetCreationFuncMap().insert(std::make_pair(operation, creation_func));
+ }
+
+ static FilterRuleSP CreateRule(bool match_accepts, size_t attribute,
+ ConstString operation,
+ const std::string &op_arg, Status &error) {
+ // Find the creation func for this type of filter rule.
+ auto map = GetCreationFuncMap();
+ auto find_it = map.find(operation);
+ if (find_it == map.end()) {
+ error.SetErrorStringWithFormat("unknown filter operation \""
+ "%s\"",
+ operation.GetCString());
+ return FilterRuleSP();
+ }
+
+ return find_it->second(match_accepts, attribute, op_arg, error);
+ }
+
+ StructuredData::ObjectSP Serialize() const {
+ StructuredData::Dictionary *dict_p = new StructuredData::Dictionary();
+
+ // Indicate whether this is an accept or reject rule.
+ dict_p->AddBooleanItem("accept", m_accept);
+
+ // Indicate which attribute of the message this filter references. This can
+ // drop into the rule-specific DoSerialization if we get to the point where
+ // not all FilterRule derived classes work on an attribute. (e.g. logical
+ // and/or and other compound operations).
+ dict_p->AddStringItem("attribute", s_filter_attributes[m_attribute_index]);
+
+ // Indicate the type of the rule.
+ dict_p->AddStringItem("type", GetOperationType().GetCString());
+
+ // Let the rule add its own specific details here.
+ DoSerialization(*dict_p);
+
+ return StructuredData::ObjectSP(dict_p);
+ }
+
+ virtual void Dump(Stream &stream) const = 0;
+
+ ConstString GetOperationType() const { return m_operation; }
+
+protected:
+ FilterRule(bool accept, size_t attribute_index, ConstString operation)
+ : m_accept(accept), m_attribute_index(attribute_index),
+ m_operation(operation) {}
+
+ virtual void DoSerialization(StructuredData::Dictionary &dict) const = 0;
+
+ bool GetMatchAccepts() const { return m_accept; }
+
+ const char *GetFilterAttribute() const {
+ return s_filter_attributes[m_attribute_index];
+ }
+
+private:
+ using CreationFuncMap = std::map<ConstString, OperationCreationFunc>;
+
+ static CreationFuncMap &GetCreationFuncMap() {
+ static CreationFuncMap s_map;
+ return s_map;
+ }
+
+ const bool m_accept;
+ const size_t m_attribute_index;
+ const ConstString m_operation;
+};
+
+using FilterRules = std::vector<FilterRuleSP>;
+
+class RegexFilterRule : public FilterRule {
+public:
+ static void RegisterOperation() {
+ FilterRule::RegisterOperation(StaticGetOperation(), CreateOperation);
+ }
+
+ void Dump(Stream &stream) const override {
+ stream.Printf("%s %s regex %s", GetMatchAccepts() ? "accept" : "reject",
+ GetFilterAttribute(), m_regex_text.c_str());
+ }
+
+protected:
+ void DoSerialization(StructuredData::Dictionary &dict) const override {
+ dict.AddStringItem("regex", m_regex_text);
+ }
+
+private:
+ static FilterRuleSP CreateOperation(bool accept, size_t attribute_index,
+ const std::string &op_arg,
+ Status &error) {
+ // We treat the op_arg as a regex. Validate it.
+ if (op_arg.empty()) {
+ error.SetErrorString("regex filter type requires a regex "
+ "argument");
+ return FilterRuleSP();
+ }
+
+ // Instantiate the regex so we can report any errors.
+ auto regex = RegularExpression(op_arg);
+ if (!regex.IsValid()) {
+ char error_text[256];
+ error_text[0] = '\0';
+ regex.GetErrorAsCString(error_text, sizeof(error_text));
+ error.SetErrorString(error_text);
+ return FilterRuleSP();
+ }
+
+ // We passed all our checks, this appears fine.
+ error.Clear();
+ return FilterRuleSP(new RegexFilterRule(accept, attribute_index, op_arg));
+ }
+
+ static ConstString StaticGetOperation() {
+ static ConstString s_operation("regex");
+ return s_operation;
+ }
+
+ RegexFilterRule(bool accept, size_t attribute_index,
+ const std::string &regex_text)
+ : FilterRule(accept, attribute_index, StaticGetOperation()),
+ m_regex_text(regex_text) {}
+
+ const std::string m_regex_text;
+};
+
+class ExactMatchFilterRule : public FilterRule {
+public:
+ static void RegisterOperation() {
+ FilterRule::RegisterOperation(StaticGetOperation(), CreateOperation);
+ }
+
+ void Dump(Stream &stream) const override {
+ stream.Printf("%s %s match %s", GetMatchAccepts() ? "accept" : "reject",
+ GetFilterAttribute(), m_match_text.c_str());
+ }
+
+protected:
+ void DoSerialization(StructuredData::Dictionary &dict) const override {
+ dict.AddStringItem("exact_text", m_match_text);
+ }
+
+private:
+ static FilterRuleSP CreateOperation(bool accept, size_t attribute_index,
+ const std::string &op_arg,
+ Status &error) {
+ if (op_arg.empty()) {
+ error.SetErrorString("exact match filter type requires an "
+ "argument containing the text that must "
+ "match the specified message attribute.");
+ return FilterRuleSP();
+ }
+
+ error.Clear();
+ return FilterRuleSP(
+ new ExactMatchFilterRule(accept, attribute_index, op_arg));
+ }
+
+ static ConstString StaticGetOperation() {
+ static ConstString s_operation("match");
+ return s_operation;
+ }
+
+ ExactMatchFilterRule(bool accept, size_t attribute_index,
+ const std::string &match_text)
+ : FilterRule(accept, attribute_index, StaticGetOperation()),
+ m_match_text(match_text) {}
+
+ const std::string m_match_text;
+};
+
+static void RegisterFilterOperations() {
+ ExactMatchFilterRule::RegisterOperation();
+ RegexFilterRule::RegisterOperation();
+}
+
+// =========================================================================
+// Commands
+// =========================================================================
+
+/// Provides the main on-off switch for enabling darwin logging.
+///
+/// It is valid to run the enable command when logging is already enabled.
+/// This resets the logging with whatever settings are currently set.
+
+static constexpr OptionDefinition g_enable_option_table[] = {
+ // Source stream include/exclude options (the first-level filter). This one
+ // should be made as small as possible as everything that goes through here
+ // must be processed by the process monitor.
+ {LLDB_OPT_SET_ALL, false, "any-process", 'a', OptionParser::eNoArgument,
+ nullptr, {}, 0, eArgTypeNone,
+ "Specifies log messages from other related processes should be "
+ "included."},
+ {LLDB_OPT_SET_ALL, false, "debug", 'd', OptionParser::eNoArgument, nullptr,
+ {}, 0, eArgTypeNone,
+ "Specifies debug-level log messages should be included. Specifying"
+ " --debug implies --info."},
+ {LLDB_OPT_SET_ALL, false, "info", 'i', OptionParser::eNoArgument, nullptr,
+ {}, 0, eArgTypeNone,
+ "Specifies info-level log messages should be included."},
+ {LLDB_OPT_SET_ALL, false, "filter", 'f', OptionParser::eRequiredArgument,
+ nullptr, {}, 0, eArgRawInput,
+ // There doesn't appear to be a great way for me to have these multi-line,
+ // formatted tables in help. This looks mostly right but there are extra
+ // linefeeds added at seemingly random spots, and indentation isn't
+ // handled properly on those lines.
+ "Appends a filter rule to the log message filter chain. Multiple "
+ "rules may be added by specifying this option multiple times, "
+ "once per filter rule. Filter rules are processed in the order "
+ "they are specified, with the --no-match-accepts setting used "
+ "for any message that doesn't match one of the rules.\n"
+ "\n"
+ " Filter spec format:\n"
+ "\n"
+ " --filter \"{action} {attribute} {op}\"\n"
+ "\n"
+ " {action} :=\n"
+ " accept |\n"
+ " reject\n"
+ "\n"
+ " {attribute} :=\n"
+ " activity | // message's most-derived activity\n"
+ " activity-chain | // message's {parent}:{child} activity\n"
+ " category | // message's category\n"
+ " message | // message's expanded contents\n"
+ " subsystem | // message's subsystem\n"
+ "\n"
+ " {op} :=\n"
+ " match {exact-match-text} |\n"
+ " regex {search-regex}\n"
+ "\n"
+ "The regex flavor used is the C++ std::regex ECMAScript format. "
+ "Prefer character classes like [[:digit:]] to \\d and the like, as "
+ "getting the backslashes escaped through properly is error-prone."},
+ {LLDB_OPT_SET_ALL, false, "live-stream", 'l',
+ OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean,
+ "Specify whether logging events are live-streamed or buffered. "
+ "True indicates live streaming, false indicates buffered. The "
+ "default is true (live streaming). Live streaming will deliver "
+ "log messages with less delay, but buffered capture mode has less "
+ "of an observer effect."},
+ {LLDB_OPT_SET_ALL, false, "no-match-accepts", 'n',
+ OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean,
+ "Specify whether a log message that doesn't match any filter rule "
+ "is accepted or rejected, where true indicates accept. The "
+ "default is true."},
+ {LLDB_OPT_SET_ALL, false, "echo-to-stderr", 'e',
+ OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean,
+ "Specify whether os_log()/NSLog() messages are echoed to the "
+ "target program's stderr. When DarwinLog is enabled, we shut off "
+ "the mirroring of os_log()/NSLog() to the program's stderr. "
+ "Setting this flag to true will restore the stderr mirroring."
+ "The default is false."},
+ {LLDB_OPT_SET_ALL, false, "broadcast-events", 'b',
+ OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean,
+ "Specify if the plugin should broadcast events. Broadcasting "
+ "log events is a requirement for displaying the log entries in "
+ "LLDB command-line. It is also required if LLDB clients want to "
+ "process log events. The default is true."},
+ // Message formatting options
+ {LLDB_OPT_SET_ALL, false, "timestamp-relative", 'r',
+ OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone,
+ "Include timestamp in the message header when printing a log "
+ "message. The timestamp is relative to the first displayed "
+ "message."},
+ {LLDB_OPT_SET_ALL, false, "subsystem", 's', OptionParser::eNoArgument,
+ nullptr, {}, 0, eArgTypeNone,
+ "Include the subsystem in the message header when displaying "
+ "a log message."},
+ {LLDB_OPT_SET_ALL, false, "category", 'c', OptionParser::eNoArgument,
+ nullptr, {}, 0, eArgTypeNone,
+ "Include the category in the message header when displaying "
+ "a log message."},
+ {LLDB_OPT_SET_ALL, false, "activity-chain", 'C', OptionParser::eNoArgument,
+ nullptr, {}, 0, eArgTypeNone,
+ "Include the activity parent-child chain in the message header "
+ "when displaying a log message. The activity hierarchy is "
+ "displayed as {grandparent-activity}:"
+ "{parent-activity}:{activity}[:...]."},
+ {LLDB_OPT_SET_ALL, false, "all-fields", 'A', OptionParser::eNoArgument,
+ nullptr, {}, 0, eArgTypeNone,
+ "Shortcut to specify that all header fields should be displayed."}};
+
+class EnableOptions : public Options {
+public:
+ EnableOptions()
+ : Options(), m_include_debug_level(false), m_include_info_level(false),
+ m_include_any_process(false),
+ m_filter_fall_through_accepts(DEFAULT_FILTER_FALLTHROUGH_ACCEPTS),
+ m_echo_to_stderr(false), m_display_timestamp_relative(false),
+ m_display_subsystem(false), m_display_category(false),
+ m_display_activity_chain(false), m_broadcast_events(true),
+ m_live_stream(true), m_filter_rules() {}
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_include_debug_level = false;
+ m_include_info_level = false;
+ m_include_any_process = false;
+ m_filter_fall_through_accepts = DEFAULT_FILTER_FALLTHROUGH_ACCEPTS;
+ m_echo_to_stderr = false;
+ m_display_timestamp_relative = false;
+ m_display_subsystem = false;
+ m_display_category = false;
+ m_display_activity_chain = false;
+ m_broadcast_events = true;
+ m_live_stream = true;
+ m_filter_rules.clear();
+ }
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ Status error;
+
+ const int short_option = m_getopt_table[option_idx].val;
+ switch (short_option) {
+ case 'a':
+ m_include_any_process = true;
+ break;
+
+ case 'A':
+ m_display_timestamp_relative = true;
+ m_display_category = true;
+ m_display_subsystem = true;
+ m_display_activity_chain = true;
+ break;
+
+ case 'b':
+ m_broadcast_events =
+ OptionArgParser::ToBoolean(option_arg, true, nullptr);
+ break;
+
+ case 'c':
+ m_display_category = true;
+ break;
+
+ case 'C':
+ m_display_activity_chain = true;
+ break;
+
+ case 'd':
+ m_include_debug_level = true;
+ break;
+
+ case 'e':
+ m_echo_to_stderr = OptionArgParser::ToBoolean(option_arg, false, nullptr);
+ break;
+
+ case 'f':
+ return ParseFilterRule(option_arg);
+
+ case 'i':
+ m_include_info_level = true;
+ break;
+
+ case 'l':
+ m_live_stream = OptionArgParser::ToBoolean(option_arg, false, nullptr);
+ break;
+
+ case 'n':
+ m_filter_fall_through_accepts =
+ OptionArgParser::ToBoolean(option_arg, true, nullptr);
+ break;
+
+ case 'r':
+ m_display_timestamp_relative = true;
+ break;
+
+ case 's':
+ m_display_subsystem = true;
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unsupported option '%c'", short_option);
+ }
+ return error;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_enable_option_table);
+ }
+
+ StructuredData::DictionarySP BuildConfigurationData(bool enabled) {
+ StructuredData::DictionarySP config_sp(new StructuredData::Dictionary());
+
+ // Set the basic enabled state.
+ config_sp->AddBooleanItem("enabled", enabled);
+
+ // If we're disabled, there's nothing more to add.
+ if (!enabled)
+ return config_sp;
+
+ // Handle source stream flags.
+ auto source_flags_sp =
+ StructuredData::DictionarySP(new StructuredData::Dictionary());
+ config_sp->AddItem("source-flags", source_flags_sp);
+
+ source_flags_sp->AddBooleanItem("any-process", m_include_any_process);
+ source_flags_sp->AddBooleanItem("debug-level", m_include_debug_level);
+ // The debug-level flag, if set, implies info-level.
+ source_flags_sp->AddBooleanItem("info-level", m_include_info_level ||
+ m_include_debug_level);
+ source_flags_sp->AddBooleanItem("live-stream", m_live_stream);
+
+ // Specify default filter rule (the fall-through)
+ config_sp->AddBooleanItem("filter-fall-through-accepts",
+ m_filter_fall_through_accepts);
+
+ // Handle filter rules
+ if (!m_filter_rules.empty()) {
+ auto json_filter_rules_sp =
+ StructuredData::ArraySP(new StructuredData::Array);
+ config_sp->AddItem("filter-rules", json_filter_rules_sp);
+ for (auto &rule_sp : m_filter_rules) {
+ if (!rule_sp)
+ continue;
+ json_filter_rules_sp->AddItem(rule_sp->Serialize());
+ }
+ }
+ return config_sp;
+ }
+
+ bool GetIncludeDebugLevel() const { return m_include_debug_level; }
+
+ bool GetIncludeInfoLevel() const {
+ // Specifying debug level implies info level.
+ return m_include_info_level || m_include_debug_level;
+ }
+
+ const FilterRules &GetFilterRules() const { return m_filter_rules; }
+
+ bool GetFallthroughAccepts() const { return m_filter_fall_through_accepts; }
+
+ bool GetEchoToStdErr() const { return m_echo_to_stderr; }
+
+ bool GetDisplayTimestampRelative() const {
+ return m_display_timestamp_relative;
+ }
+
+ bool GetDisplaySubsystem() const { return m_display_subsystem; }
+ bool GetDisplayCategory() const { return m_display_category; }
+ bool GetDisplayActivityChain() const { return m_display_activity_chain; }
+
+ bool GetDisplayAnyHeaderFields() const {
+ return m_display_timestamp_relative || m_display_activity_chain ||
+ m_display_subsystem || m_display_category;
+ }
+
+ bool GetBroadcastEvents() const { return m_broadcast_events; }
+
+private:
+ Status ParseFilterRule(llvm::StringRef rule_text) {
+ Status error;
+
+ if (rule_text.empty()) {
+ error.SetErrorString("invalid rule_text");
+ return error;
+ }
+
+ // filter spec format:
+ //
+ // {action} {attribute} {op}
+ //
+ // {action} :=
+ // accept |
+ // reject
+ //
+ // {attribute} :=
+ // category |
+ // subsystem |
+ // activity |
+ // activity-chain |
+ // message |
+ // format
+ //
+ // {op} :=
+ // match {exact-match-text} |
+ // regex {search-regex}
+
+ // Parse action.
+ auto action_end_pos = rule_text.find(" ");
+ if (action_end_pos == std::string::npos) {
+ error.SetErrorStringWithFormat("could not parse filter rule "
+ "action from \"%s\"",
+ rule_text.str().c_str());
+ return error;
+ }
+ auto action = rule_text.substr(0, action_end_pos);
+ bool accept;
+ if (action == "accept")
+ accept = true;
+ else if (action == "reject")
+ accept = false;
+ else {
+ error.SetErrorString("filter action must be \"accept\" or \"deny\"");
+ return error;
+ }
+
+ // parse attribute
+ auto attribute_end_pos = rule_text.find(" ", action_end_pos + 1);
+ if (attribute_end_pos == std::string::npos) {
+ error.SetErrorStringWithFormat("could not parse filter rule "
+ "attribute from \"%s\"",
+ rule_text.str().c_str());
+ return error;
+ }
+ auto attribute = rule_text.substr(action_end_pos + 1,
+ attribute_end_pos - (action_end_pos + 1));
+ auto attribute_index = MatchAttributeIndex(attribute);
+ if (attribute_index < 0) {
+ error.SetErrorStringWithFormat("filter rule attribute unknown: "
+ "%s",
+ attribute.str().c_str());
+ return error;
+ }
+
+ // parse operation
+ auto operation_end_pos = rule_text.find(" ", attribute_end_pos + 1);
+ auto operation = rule_text.substr(
+ attribute_end_pos + 1, operation_end_pos - (attribute_end_pos + 1));
+
+ // add filter spec
+ auto rule_sp =
+ FilterRule::CreateRule(accept, attribute_index, ConstString(operation),
+ rule_text.substr(operation_end_pos + 1), error);
+
+ if (rule_sp && error.Success())
+ m_filter_rules.push_back(rule_sp);
+
+ return error;
+ }
+
+ int MatchAttributeIndex(llvm::StringRef attribute_name) const {
+ for (const auto &Item : llvm::enumerate(s_filter_attributes)) {
+ if (attribute_name == Item.value())
+ return Item.index();
+ }
+
+ // We didn't match anything.
+ return -1;
+ }
+
+ bool m_include_debug_level;
+ bool m_include_info_level;
+ bool m_include_any_process;
+ bool m_filter_fall_through_accepts;
+ bool m_echo_to_stderr;
+ bool m_display_timestamp_relative;
+ bool m_display_subsystem;
+ bool m_display_category;
+ bool m_display_activity_chain;
+ bool m_broadcast_events;
+ bool m_live_stream;
+ FilterRules m_filter_rules;
+};
+
+class EnableCommand : public CommandObjectParsed {
+public:
+ EnableCommand(CommandInterpreter &interpreter, bool enable, const char *name,
+ const char *help, const char *syntax)
+ : CommandObjectParsed(interpreter, name, help, syntax), m_enable(enable),
+ m_options_sp(enable ? new EnableOptions() : nullptr) {}
+
+protected:
+ void AppendStrictSourcesWarning(CommandReturnObject &result,
+ const char *source_name) {
+ if (!source_name)
+ return;
+
+ // Check if we're *not* using strict sources. If not, then the user is
+ // going to get debug-level info anyways, probably not what they're
+ // expecting. Unfortunately we can only fix this by adding an env var,
+ // which would have had to have happened already. Thus, a warning is the
+ // best we can do here.
+ StreamString stream;
+ stream.Printf("darwin-log source settings specify to exclude "
+ "%s messages, but setting "
+ "'plugin.structured-data.darwin-log."
+ "strict-sources' is disabled. This process will "
+ "automatically have %s messages included. Enable"
+ " the property and relaunch the target binary to have"
+ " these messages excluded.",
+ source_name, source_name);
+ result.AppendWarning(stream.GetString());
+ }
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ // First off, set the global sticky state of enable/disable based on this
+ // command execution.
+ s_is_explicitly_enabled = m_enable;
+
+ // Next, if this is an enable, save off the option data. We will need it
+ // later if a process hasn't been launched or attached yet.
+ if (m_enable) {
+ // Save off enabled configuration so we can apply these parsed options
+ // the next time an attach or launch occurs.
+ DebuggerSP debugger_sp =
+ GetCommandInterpreter().GetDebugger().shared_from_this();
+ SetGlobalEnableOptions(debugger_sp, m_options_sp);
+ }
+
+ // Now check if we have a running process. If so, we should instruct the
+ // process monitor to enable/disable DarwinLog support now.
+ Target *target = GetSelectedOrDummyTarget();
+ if (!target) {
+ // No target, so there is nothing more to do right now.
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+
+ // Grab the active process.
+ auto process_sp = target->GetProcessSP();
+ if (!process_sp) {
+ // No active process, so there is nothing more to do right now.
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+
+ // If the process is no longer alive, we can't do this now. We'll catch it
+ // the next time the process is started up.
+ if (!process_sp->IsAlive()) {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ return true;
+ }
+
+ // Get the plugin for the process.
+ auto plugin_sp =
+ process_sp->GetStructuredDataPlugin(GetDarwinLogTypeName());
+ if (!plugin_sp || (plugin_sp->GetPluginName() !=
+ StructuredDataDarwinLog::GetStaticPluginName())) {
+ result.AppendError("failed to get StructuredDataPlugin for "
+ "the process");
+ result.SetStatus(eReturnStatusFailed);
+ }
+ StructuredDataDarwinLog &plugin =
+ *static_cast<StructuredDataDarwinLog *>(plugin_sp.get());
+
+ if (m_enable) {
+ // Hook up the breakpoint for the process that detects when libtrace has
+ // been sufficiently initialized to really start the os_log stream. This
+ // is insurance to assure us that logging is really enabled. Requesting
+ // that logging be enabled for a process before libtrace is initialized
+ // results in a scenario where no errors occur, but no logging is
+ // captured, either. This step is to eliminate that possibility.
+ plugin.AddInitCompletionHook(*process_sp);
+ }
+
+ // Send configuration to the feature by way of the process. Construct the
+ // options we will use.
+ auto config_sp = m_options_sp->BuildConfigurationData(m_enable);
+ const Status error =
+ process_sp->ConfigureStructuredData(GetDarwinLogTypeName(), config_sp);
+
+ // Report results.
+ if (!error.Success()) {
+ result.AppendError(error.AsCString());
+ result.SetStatus(eReturnStatusFailed);
+ // Our configuration failed, so we're definitely disabled.
+ plugin.SetEnabled(false);
+ } else {
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ // Our configuration succeeded, so we're enabled/disabled per whichever
+ // one this command is setup to do.
+ plugin.SetEnabled(m_enable);
+ }
+ return result.Succeeded();
+ }
+
+ Options *GetOptions() override {
+ // We don't have options when this represents disable.
+ return m_enable ? m_options_sp.get() : nullptr;
+ }
+
+private:
+ const bool m_enable;
+ EnableOptionsSP m_options_sp;
+};
+
+/// Provides the status command.
+class StatusCommand : public CommandObjectParsed {
+public:
+ StatusCommand(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "status",
+ "Show whether Darwin log supported is available"
+ " and enabled.",
+ "plugin structured-data darwin-log status") {}
+
+protected:
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ auto &stream = result.GetOutputStream();
+
+ // Figure out if we've got a process. If so, we can tell if DarwinLog is
+ // available for that process.
+ Target *target = GetSelectedOrDummyTarget();
+ auto process_sp = target ? target->GetProcessSP() : ProcessSP();
+ if (!target || !process_sp) {
+ stream.PutCString("Availability: unknown (requires process)\n");
+ stream.PutCString("Enabled: not applicable "
+ "(requires process)\n");
+ } else {
+ auto plugin_sp =
+ process_sp->GetStructuredDataPlugin(GetDarwinLogTypeName());
+ stream.Printf("Availability: %s\n",
+ plugin_sp ? "available" : "unavailable");
+ ConstString plugin_name = StructuredDataDarwinLog::GetStaticPluginName();
+ const bool enabled =
+ plugin_sp ? plugin_sp->GetEnabled(plugin_name) : false;
+ stream.Printf("Enabled: %s\n", enabled ? "true" : "false");
+ }
+
+ // Display filter settings.
+ DebuggerSP debugger_sp =
+ GetCommandInterpreter().GetDebugger().shared_from_this();
+ auto options_sp = GetGlobalEnableOptions(debugger_sp);
+ if (!options_sp) {
+ // Nothing more to do.
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+
+ // Print filter rules
+ stream.PutCString("DarwinLog filter rules:\n");
+
+ stream.IndentMore();
+
+ if (options_sp->GetFilterRules().empty()) {
+ stream.Indent();
+ stream.PutCString("none\n");
+ } else {
+ // Print each of the filter rules.
+ int rule_number = 0;
+ for (auto rule_sp : options_sp->GetFilterRules()) {
+ ++rule_number;
+ if (!rule_sp)
+ continue;
+
+ stream.Indent();
+ stream.Printf("%02d: ", rule_number);
+ rule_sp->Dump(stream);
+ stream.PutChar('\n');
+ }
+ }
+ stream.IndentLess();
+
+ // Print no-match handling.
+ stream.Indent();
+ stream.Printf("no-match behavior: %s\n",
+ options_sp->GetFallthroughAccepts() ? "accept" : "reject");
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+ }
+};
+
+/// Provides the darwin-log base command
+class BaseCommand : public CommandObjectMultiword {
+public:
+ BaseCommand(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(interpreter, "plugin structured-data darwin-log",
+ "Commands for configuring Darwin os_log "
+ "support.",
+ "") {
+ // enable
+ auto enable_help = "Enable Darwin log collection, or re-enable "
+ "with modified configuration.";
+ auto enable_syntax = "plugin structured-data darwin-log enable";
+ auto enable_cmd_sp = CommandObjectSP(
+ new EnableCommand(interpreter,
+ true, // enable
+ "enable", enable_help, enable_syntax));
+ LoadSubCommand("enable", enable_cmd_sp);
+
+ // disable
+ auto disable_help = "Disable Darwin log collection.";
+ auto disable_syntax = "plugin structured-data darwin-log disable";
+ auto disable_cmd_sp = CommandObjectSP(
+ new EnableCommand(interpreter,
+ false, // disable
+ "disable", disable_help, disable_syntax));
+ LoadSubCommand("disable", disable_cmd_sp);
+
+ // status
+ auto status_cmd_sp = CommandObjectSP(new StatusCommand(interpreter));
+ LoadSubCommand("status", status_cmd_sp);
+ }
+};
+
+EnableOptionsSP ParseAutoEnableOptions(Status &error, Debugger &debugger) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS);
+ // We are abusing the options data model here so that we can parse options
+ // without requiring the Debugger instance.
+
+ // We have an empty execution context at this point. We only want to parse
+ // options, and we don't need any context to do this here. In fact, we want
+ // to be able to parse the enable options before having any context.
+ ExecutionContext exe_ctx;
+
+ EnableOptionsSP options_sp(new EnableOptions());
+ options_sp->NotifyOptionParsingStarting(&exe_ctx);
+
+ CommandReturnObject result;
+
+ // Parse the arguments.
+ auto options_property_sp =
+ debugger.GetPropertyValue(nullptr, "plugin.structured-data.darwin-log."
+ "auto-enable-options",
+ false, error);
+ if (!error.Success())
+ return EnableOptionsSP();
+ if (!options_property_sp) {
+ error.SetErrorString("failed to find option setting for "
+ "plugin.structured-data.darwin-log.");
+ return EnableOptionsSP();
+ }
+
+ const char *enable_options =
+ options_property_sp->GetAsString()->GetCurrentValue();
+ Args args(enable_options);
+ if (args.GetArgumentCount() > 0) {
+ // Eliminate the initial '--' that would be required to set the settings
+ // that themselves include '-' and/or '--'.
+ const char *first_arg = args.GetArgumentAtIndex(0);
+ if (first_arg && (strcmp(first_arg, "--") == 0))
+ args.Shift();
+ }
+
+ bool require_validation = false;
+ llvm::Expected<Args> args_or =
+ options_sp->Parse(args, &exe_ctx, PlatformSP(), require_validation);
+ if (!args_or) {
+ LLDB_LOG_ERROR(
+ log, args_or.takeError(),
+ "Parsing plugin.structured-data.darwin-log.auto-enable-options value "
+ "failed: {0}");
+ return EnableOptionsSP();
+ }
+
+ if (!options_sp->VerifyOptions(result))
+ return EnableOptionsSP();
+
+ // We successfully parsed and validated the options.
+ return options_sp;
+}
+
+bool RunEnableCommand(CommandInterpreter &interpreter) {
+ StreamString command_stream;
+
+ command_stream << "plugin structured-data darwin-log enable";
+ auto enable_options = GetGlobalProperties()->GetAutoEnableOptions();
+ if (!enable_options.empty()) {
+ command_stream << ' ';
+ command_stream << enable_options;
+ }
+
+ // Run the command.
+ CommandReturnObject return_object;
+ interpreter.HandleCommand(command_stream.GetData(), eLazyBoolNo,
+ return_object);
+ return return_object.Succeeded();
+}
+}
+using namespace sddarwinlog_private;
+
+#pragma mark -
+#pragma mark Public static API
+
+// Public static API
+
+void StructuredDataDarwinLog::Initialize() {
+ RegisterFilterOperations();
+ PluginManager::RegisterPlugin(
+ GetStaticPluginName(), "Darwin os_log() and os_activity() support",
+ &CreateInstance, &DebuggerInitialize, &FilterLaunchInfo);
+}
+
+void StructuredDataDarwinLog::Terminate() {
+ PluginManager::UnregisterPlugin(&CreateInstance);
+}
+
+ConstString StructuredDataDarwinLog::GetStaticPluginName() {
+ static ConstString s_plugin_name("darwin-log");
+ return s_plugin_name;
+}
+
+#pragma mark -
+#pragma mark PluginInterface API
+
+// PluginInterface API
+
+ConstString StructuredDataDarwinLog::GetPluginName() {
+ return GetStaticPluginName();
+}
+
+uint32_t StructuredDataDarwinLog::GetPluginVersion() { return 1; }
+
+#pragma mark -
+#pragma mark StructuredDataPlugin API
+
+// StructuredDataPlugin API
+
+bool StructuredDataDarwinLog::SupportsStructuredDataType(
+ ConstString type_name) {
+ return type_name == GetDarwinLogTypeName();
+}
+
+void StructuredDataDarwinLog::HandleArrivalOfStructuredData(
+ Process &process, ConstString type_name,
+ const StructuredData::ObjectSP &object_sp) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log) {
+ StreamString json_stream;
+ if (object_sp)
+ object_sp->Dump(json_stream);
+ else
+ json_stream.PutCString("<null>");
+ log->Printf("StructuredDataDarwinLog::%s() called with json: %s",
+ __FUNCTION__, json_stream.GetData());
+ }
+
+ // Ignore empty structured data.
+ if (!object_sp) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() StructuredData object "
+ "is null",
+ __FUNCTION__);
+ return;
+ }
+
+ // Ignore any data that isn't for us.
+ if (type_name != GetDarwinLogTypeName()) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() StructuredData type "
+ "expected to be %s but was %s, ignoring",
+ __FUNCTION__, GetDarwinLogTypeName().AsCString(),
+ type_name.AsCString());
+ return;
+ }
+
+ // Broadcast the structured data event if we have that enabled. This is the
+ // way that the outside world (all clients) get access to this data. This
+ // plugin sets policy as to whether we do that.
+ DebuggerSP debugger_sp = process.GetTarget().GetDebugger().shared_from_this();
+ auto options_sp = GetGlobalEnableOptions(debugger_sp);
+ if (options_sp && options_sp->GetBroadcastEvents()) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() broadcasting event",
+ __FUNCTION__);
+ process.BroadcastStructuredData(object_sp, shared_from_this());
+ }
+
+ // Later, hang on to a configurable amount of these and allow commands to
+ // inspect, including showing backtraces.
+}
+
+static void SetErrorWithJSON(Status &error, const char *message,
+ StructuredData::Object &object) {
+ if (!message) {
+ error.SetErrorString("Internal error: message not set.");
+ return;
+ }
+
+ StreamString object_stream;
+ object.Dump(object_stream);
+ object_stream.Flush();
+
+ error.SetErrorStringWithFormat("%s: %s", message, object_stream.GetData());
+}
+
+Status StructuredDataDarwinLog::GetDescription(
+ const StructuredData::ObjectSP &object_sp, lldb_private::Stream &stream) {
+ Status error;
+
+ if (!object_sp) {
+ error.SetErrorString("No structured data.");
+ return error;
+ }
+
+ // Log message payload objects will be dictionaries.
+ const StructuredData::Dictionary *dictionary = object_sp->GetAsDictionary();
+ if (!dictionary) {
+ SetErrorWithJSON(error, "Structured data should have been a dictionary "
+ "but wasn't",
+ *object_sp);
+ return error;
+ }
+
+ // Validate this is really a message for our plugin.
+ ConstString type_name;
+ if (!dictionary->GetValueForKeyAsString("type", type_name)) {
+ SetErrorWithJSON(error, "Structured data doesn't contain mandatory "
+ "type field",
+ *object_sp);
+ return error;
+ }
+
+ if (type_name != GetDarwinLogTypeName()) {
+ // This is okay - it simply means the data we received is not a log
+ // message. We'll just format it as is.
+ object_sp->Dump(stream);
+ return error;
+ }
+
+ // DarwinLog dictionaries store their data
+ // in an array with key name "events".
+ StructuredData::Array *events = nullptr;
+ if (!dictionary->GetValueForKeyAsArray("events", events) || !events) {
+ SetErrorWithJSON(error, "Log structured data is missing mandatory "
+ "'events' field, expected to be an array",
+ *object_sp);
+ return error;
+ }
+
+ events->ForEach(
+ [&stream, &error, &object_sp, this](StructuredData::Object *object) {
+ if (!object) {
+ // Invalid. Stop iterating.
+ SetErrorWithJSON(error, "Log event entry is null", *object_sp);
+ return false;
+ }
+
+ auto event = object->GetAsDictionary();
+ if (!event) {
+ // Invalid, stop iterating.
+ SetErrorWithJSON(error, "Log event is not a dictionary", *object_sp);
+ return false;
+ }
+
+ // If we haven't already grabbed the first timestamp value, do that
+ // now.
+ if (!m_recorded_first_timestamp) {
+ uint64_t timestamp = 0;
+ if (event->GetValueForKeyAsInteger("timestamp", timestamp)) {
+ m_first_timestamp_seen = timestamp;
+ m_recorded_first_timestamp = true;
+ }
+ }
+
+ HandleDisplayOfEvent(*event, stream);
+ return true;
+ });
+
+ stream.Flush();
+ return error;
+}
+
+bool StructuredDataDarwinLog::GetEnabled(ConstString type_name) const {
+ if (type_name == GetStaticPluginName())
+ return m_is_enabled;
+ else
+ return false;
+}
+
+void StructuredDataDarwinLog::SetEnabled(bool enabled) {
+ m_is_enabled = enabled;
+}
+
+void StructuredDataDarwinLog::ModulesDidLoad(Process &process,
+ ModuleList &module_list) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s called (process uid %u)",
+ __FUNCTION__, process.GetUniqueID());
+
+ // Check if we should enable the darwin log support on startup/attach.
+ if (!GetGlobalProperties()->GetEnableOnStartup() &&
+ !s_is_explicitly_enabled) {
+ // We're neither auto-enabled or explicitly enabled, so we shouldn't try to
+ // enable here.
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s not applicable, we're not "
+ "enabled (process uid %u)",
+ __FUNCTION__, process.GetUniqueID());
+ return;
+ }
+
+ // If we already added the breakpoint, we've got nothing left to do.
+ {
+ std::lock_guard<std::mutex> locker(m_added_breakpoint_mutex);
+ if (m_added_breakpoint) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s process uid %u's "
+ "post-libtrace-init breakpoint is already set",
+ __FUNCTION__, process.GetUniqueID());
+ return;
+ }
+ }
+
+ // The logging support module name, specifies the name of the image name that
+ // must be loaded into the debugged process before we can try to enable
+ // logging.
+ const char *logging_module_cstr =
+ GetGlobalProperties()->GetLoggingModuleName();
+ if (!logging_module_cstr || (logging_module_cstr[0] == 0)) {
+ // We need this. Bail.
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s no logging module name "
+ "specified, we don't know where to set a breakpoint "
+ "(process uid %u)",
+ __FUNCTION__, process.GetUniqueID());
+ return;
+ }
+
+ const ConstString logging_module_name = ConstString(logging_module_cstr);
+
+ // We need to see libtrace in the list of modules before we can enable
+ // tracing for the target process.
+ bool found_logging_support_module = false;
+ for (size_t i = 0; i < module_list.GetSize(); ++i) {
+ auto module_sp = module_list.GetModuleAtIndex(i);
+ if (!module_sp)
+ continue;
+
+ auto &file_spec = module_sp->GetFileSpec();
+ found_logging_support_module =
+ (file_spec.GetLastPathComponent() == logging_module_name);
+ if (found_logging_support_module)
+ break;
+ }
+
+ if (!found_logging_support_module) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s logging module %s "
+ "has not yet been loaded, can't set a breakpoint "
+ "yet (process uid %u)",
+ __FUNCTION__, logging_module_name.AsCString(),
+ process.GetUniqueID());
+ return;
+ }
+
+ // Time to enqueue the breakpoint so we can wait for logging support to be
+ // initialized before we try to tap the libtrace stream.
+ AddInitCompletionHook(process);
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s post-init hook breakpoint "
+ "set for logging module %s (process uid %u)",
+ __FUNCTION__, logging_module_name.AsCString(),
+ process.GetUniqueID());
+
+ // We need to try the enable here as well, which will succeed in the event
+ // that we're attaching to (rather than launching) the process and the
+ // process is already past initialization time. In that case, the completion
+ // breakpoint will never get hit and therefore won't start that way. It
+ // doesn't hurt much beyond a bit of bandwidth if we end up doing this twice.
+ // It hurts much more if we don't get the logging enabled when the user
+ // expects it.
+ EnableNow();
+}
+
+// public destructor
+
+StructuredDataDarwinLog::~StructuredDataDarwinLog() {
+ if (m_breakpoint_id != LLDB_INVALID_BREAK_ID) {
+ ProcessSP process_sp(GetProcess());
+ if (process_sp) {
+ process_sp->GetTarget().RemoveBreakpointByID(m_breakpoint_id);
+ m_breakpoint_id = LLDB_INVALID_BREAK_ID;
+ }
+ }
+}
+
+#pragma mark -
+#pragma mark Private instance methods
+
+// Private constructors
+
+StructuredDataDarwinLog::StructuredDataDarwinLog(const ProcessWP &process_wp)
+ : StructuredDataPlugin(process_wp), m_recorded_first_timestamp(false),
+ m_first_timestamp_seen(0), m_is_enabled(false),
+ m_added_breakpoint_mutex(), m_added_breakpoint(),
+ m_breakpoint_id(LLDB_INVALID_BREAK_ID) {}
+
+// Private static methods
+
+StructuredDataPluginSP
+StructuredDataDarwinLog::CreateInstance(Process &process) {
+ // Currently only Apple targets support the os_log/os_activity protocol.
+ if (process.GetTarget().GetArchitecture().GetTriple().getVendor() ==
+ llvm::Triple::VendorType::Apple) {
+ auto process_wp = ProcessWP(process.shared_from_this());
+ return StructuredDataPluginSP(new StructuredDataDarwinLog(process_wp));
+ } else {
+ return StructuredDataPluginSP();
+ }
+}
+
+void StructuredDataDarwinLog::DebuggerInitialize(Debugger &debugger) {
+ // Setup parent class first.
+ StructuredDataPlugin::InitializeBasePluginForDebugger(debugger);
+
+ // Get parent command.
+ auto &interpreter = debugger.GetCommandInterpreter();
+ llvm::StringRef parent_command_text = "plugin structured-data";
+ auto parent_command =
+ interpreter.GetCommandObjectForCommand(parent_command_text);
+ if (!parent_command) {
+ // Ut oh, parent failed to create parent command.
+ // TODO log
+ return;
+ }
+
+ auto command_name = "darwin-log";
+ auto command_sp = CommandObjectSP(new BaseCommand(interpreter));
+ bool result = parent_command->LoadSubCommand(command_name, command_sp);
+ if (!result) {
+ // TODO log it once we setup structured data logging
+ }
+
+ if (!PluginManager::GetSettingForPlatformPlugin(
+ debugger, StructuredDataDarwinLogProperties::GetSettingName())) {
+ const bool is_global_setting = true;
+ PluginManager::CreateSettingForStructuredDataPlugin(
+ debugger, GetGlobalProperties()->GetValueProperties(),
+ ConstString("Properties for the darwin-log"
+ " plug-in."),
+ is_global_setting);
+ }
+}
+
+Status StructuredDataDarwinLog::FilterLaunchInfo(ProcessLaunchInfo &launch_info,
+ Target *target) {
+ Status error;
+
+ // If we're not debugging this launched process, there's nothing for us to do
+ // here.
+ if (!launch_info.GetFlags().AnySet(eLaunchFlagDebug))
+ return error;
+
+ // Darwin os_log() support automatically adds debug-level and info-level
+ // messages when a debugger is attached to a process. However, with
+ // integrated support for debugging built into the command-line LLDB, the
+ // user may specifically set to *not* include debug-level and info-level
+ // content. When the user is using the integrated log support, we want to
+ // put the kabosh on that automatic adding of info and debug level. This is
+ // done by adding an environment variable to the process on launch. (This
+ // also means it is not possible to suppress this behavior if attaching to an
+ // already-running app).
+ // Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
+
+ // If the target architecture is not one that supports DarwinLog, we have
+ // nothing to do here.
+ auto &triple = target ? target->GetArchitecture().GetTriple()
+ : launch_info.GetArchitecture().GetTriple();
+ if (triple.getVendor() != llvm::Triple::Apple) {
+ return error;
+ }
+
+ // If DarwinLog is not enabled (either by explicit user command or via the
+ // auto-enable option), then we have nothing to do.
+ if (!GetGlobalProperties()->GetEnableOnStartup() &&
+ !s_is_explicitly_enabled) {
+ // Nothing to do, DarwinLog is not enabled.
+ return error;
+ }
+
+ // If we don't have parsed configuration info, that implies we have enable-
+ // on-startup set up, but we haven't yet attempted to run the enable command.
+ if (!target) {
+ // We really can't do this without a target. We need to be able to get to
+ // the debugger to get the proper options to do this right.
+ // TODO log.
+ error.SetErrorString("requires a target to auto-enable DarwinLog.");
+ return error;
+ }
+
+ DebuggerSP debugger_sp = target->GetDebugger().shared_from_this();
+ auto options_sp = GetGlobalEnableOptions(debugger_sp);
+ if (!options_sp && debugger_sp) {
+ options_sp = ParseAutoEnableOptions(error, *debugger_sp.get());
+ if (!options_sp || !error.Success())
+ return error;
+
+ // We already parsed the options, save them now so we don't generate them
+ // again until the user runs the command manually.
+ SetGlobalEnableOptions(debugger_sp, options_sp);
+ }
+
+ if (!options_sp->GetEchoToStdErr()) {
+ // The user doesn't want to see os_log/NSLog messages echo to stderr. That
+ // mechanism is entirely separate from the DarwinLog support. By default we
+ // don't want to get it via stderr, because that would be in duplicate of
+ // the explicit log support here.
+
+ // Here we need to strip out any OS_ACTIVITY_DT_MODE setting to prevent
+ // echoing of os_log()/NSLog() to stderr in the target program.
+ launch_info.GetEnvironment().erase("OS_ACTIVITY_DT_MODE");
+
+ // We will also set the env var that tells any downstream launcher from
+ // adding OS_ACTIVITY_DT_MODE.
+ launch_info.GetEnvironment()["IDE_DISABLED_OS_ACTIVITY_DT_MODE"] = "1";
+ }
+
+ // Set the OS_ACTIVITY_MODE env var appropriately to enable/disable debug and
+ // info level messages.
+ const char *env_var_value;
+ if (options_sp->GetIncludeDebugLevel())
+ env_var_value = "debug";
+ else if (options_sp->GetIncludeInfoLevel())
+ env_var_value = "info";
+ else
+ env_var_value = "default";
+
+ launch_info.GetEnvironment()["OS_ACTIVITY_MODE"] = env_var_value;
+
+ return error;
+}
+
+bool StructuredDataDarwinLog::InitCompletionHookCallback(
+ void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id) {
+ // We hit the init function. We now want to enqueue our new thread plan,
+ // which will in turn enqueue a StepOut thread plan. When the StepOut
+ // finishes and control returns to our new thread plan, that is the time when
+ // we can execute our logic to enable the logging support.
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() called", __FUNCTION__);
+
+ // Get the current thread.
+ if (!context) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() warning: no context, "
+ "ignoring",
+ __FUNCTION__);
+ return false;
+ }
+
+ // Get the plugin from the process.
+ auto process_sp = context->exe_ctx_ref.GetProcessSP();
+ if (!process_sp) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() warning: invalid "
+ "process in context, ignoring",
+ __FUNCTION__);
+ return false;
+ }
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() call is for process uid %d",
+ __FUNCTION__, process_sp->GetUniqueID());
+
+ auto plugin_sp = process_sp->GetStructuredDataPlugin(GetDarwinLogTypeName());
+ if (!plugin_sp) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() warning: no plugin for "
+ "feature %s in process uid %u",
+ __FUNCTION__, GetDarwinLogTypeName().AsCString(),
+ process_sp->GetUniqueID());
+ return false;
+ }
+
+ // Create the callback for when the thread plan completes.
+ bool called_enable_method = false;
+ const auto process_uid = process_sp->GetUniqueID();
+
+ std::weak_ptr<StructuredDataPlugin> plugin_wp(plugin_sp);
+ ThreadPlanCallOnFunctionExit::Callback callback =
+ [plugin_wp, &called_enable_method, log, process_uid]() {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::post-init callback: "
+ "called (process uid %u)",
+ process_uid);
+
+ auto strong_plugin_sp = plugin_wp.lock();
+ if (!strong_plugin_sp) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::post-init callback: "
+ "plugin no longer exists, ignoring (process "
+ "uid %u)",
+ process_uid);
+ return;
+ }
+ // Make sure we only call it once, just in case the thread plan hits
+ // the breakpoint twice.
+ if (!called_enable_method) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::post-init callback: "
+ "calling EnableNow() (process uid %u)",
+ process_uid);
+ static_cast<StructuredDataDarwinLog *>(strong_plugin_sp.get())
+ ->EnableNow();
+ called_enable_method = true;
+ } else {
+ // Our breakpoint was hit more than once. Unexpected but no harm
+ // done. Log it.
+ if (log)
+ log->Printf("StructuredDataDarwinLog::post-init callback: "
+ "skipping EnableNow(), already called by "
+ "callback [we hit this more than once] "
+ "(process uid %u)",
+ process_uid);
+ }
+ };
+
+ // Grab the current thread.
+ auto thread_sp = context->exe_ctx_ref.GetThreadSP();
+ if (!thread_sp) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() warning: failed to "
+ "retrieve the current thread from the execution "
+ "context, nowhere to run the thread plan (process uid "
+ "%u)",
+ __FUNCTION__, process_sp->GetUniqueID());
+ return false;
+ }
+
+ // Queue the thread plan.
+ auto thread_plan_sp =
+ ThreadPlanSP(new ThreadPlanCallOnFunctionExit(*thread_sp, callback));
+ const bool abort_other_plans = false;
+ thread_sp->QueueThreadPlan(thread_plan_sp, abort_other_plans);
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() queuing thread plan on "
+ "trace library init method entry (process uid %u)",
+ __FUNCTION__, process_sp->GetUniqueID());
+
+ // We return false here to indicate that it isn't a public stop.
+ return false;
+}
+
+void StructuredDataDarwinLog::AddInitCompletionHook(Process &process) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() called (process uid %u)",
+ __FUNCTION__, process.GetUniqueID());
+
+ // Make sure we haven't already done this.
+ {
+ std::lock_guard<std::mutex> locker(m_added_breakpoint_mutex);
+ if (m_added_breakpoint) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() ignoring request, "
+ "breakpoint already set (process uid %u)",
+ __FUNCTION__, process.GetUniqueID());
+ return;
+ }
+
+ // We're about to do this, don't let anybody else try to do it.
+ m_added_breakpoint = true;
+ }
+
+ // Set a breakpoint for the process that will kick in when libtrace has
+ // finished its initialization.
+ Target &target = process.GetTarget();
+
+ // Build up the module list.
+ FileSpecList module_spec_list;
+ auto module_file_spec =
+ FileSpec(GetGlobalProperties()->GetLoggingModuleName());
+ module_spec_list.Append(module_file_spec);
+
+ // We aren't specifying a source file set.
+ FileSpecList *source_spec_list = nullptr;
+
+ const char *func_name = "_libtrace_init";
+ const lldb::addr_t offset = 0;
+ const LazyBool skip_prologue = eLazyBoolCalculate;
+ // This is an internal breakpoint - the user shouldn't see it.
+ const bool internal = true;
+ const bool hardware = false;
+
+ auto breakpoint_sp = target.CreateBreakpoint(
+ &module_spec_list, source_spec_list, func_name, eFunctionNameTypeFull,
+ eLanguageTypeC, offset, skip_prologue, internal, hardware);
+ if (!breakpoint_sp) {
+ // Huh? Bail here.
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() failed to set "
+ "breakpoint in module %s, function %s (process uid %u)",
+ __FUNCTION__, GetGlobalProperties()->GetLoggingModuleName(),
+ func_name, process.GetUniqueID());
+ return;
+ }
+
+ // Set our callback.
+ breakpoint_sp->SetCallback(InitCompletionHookCallback, nullptr);
+ m_breakpoint_id = breakpoint_sp->GetID();
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() breakpoint set in module %s,"
+ "function %s (process uid %u)",
+ __FUNCTION__, GetGlobalProperties()->GetLoggingModuleName(),
+ func_name, process.GetUniqueID());
+}
+
+void StructuredDataDarwinLog::DumpTimestamp(Stream &stream,
+ uint64_t timestamp) {
+ const uint64_t delta_nanos = timestamp - m_first_timestamp_seen;
+
+ const uint64_t hours = delta_nanos / NANOS_PER_HOUR;
+ uint64_t nanos_remaining = delta_nanos % NANOS_PER_HOUR;
+
+ const uint64_t minutes = nanos_remaining / NANOS_PER_MINUTE;
+ nanos_remaining = nanos_remaining % NANOS_PER_MINUTE;
+
+ const uint64_t seconds = nanos_remaining / NANOS_PER_SECOND;
+ nanos_remaining = nanos_remaining % NANOS_PER_SECOND;
+
+ stream.Printf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%09" PRIu64, hours,
+ minutes, seconds, nanos_remaining);
+}
+
+size_t
+StructuredDataDarwinLog::DumpHeader(Stream &output_stream,
+ const StructuredData::Dictionary &event) {
+ StreamString stream;
+
+ ProcessSP process_sp = GetProcess();
+ if (!process_sp) {
+ // TODO log
+ return 0;
+ }
+
+ DebuggerSP debugger_sp =
+ process_sp->GetTarget().GetDebugger().shared_from_this();
+ if (!debugger_sp) {
+ // TODO log
+ return 0;
+ }
+
+ auto options_sp = GetGlobalEnableOptions(debugger_sp);
+ if (!options_sp) {
+ // TODO log
+ return 0;
+ }
+
+ // Check if we should even display a header.
+ if (!options_sp->GetDisplayAnyHeaderFields())
+ return 0;
+
+ stream.PutChar('[');
+
+ int header_count = 0;
+ if (options_sp->GetDisplayTimestampRelative()) {
+ uint64_t timestamp = 0;
+ if (event.GetValueForKeyAsInteger("timestamp", timestamp)) {
+ DumpTimestamp(stream, timestamp);
+ ++header_count;
+ }
+ }
+
+ if (options_sp->GetDisplayActivityChain()) {
+ llvm::StringRef activity_chain;
+ if (event.GetValueForKeyAsString("activity-chain", activity_chain) &&
+ !activity_chain.empty()) {
+ if (header_count > 0)
+ stream.PutChar(',');
+
+ // Display the activity chain, from parent-most to child-most activity,
+ // separated by a colon (:).
+ stream.PutCString("activity-chain=");
+ stream.PutCString(activity_chain);
+ ++header_count;
+ }
+ }
+
+ if (options_sp->GetDisplaySubsystem()) {
+ llvm::StringRef subsystem;
+ if (event.GetValueForKeyAsString("subsystem", subsystem) &&
+ !subsystem.empty()) {
+ if (header_count > 0)
+ stream.PutChar(',');
+ stream.PutCString("subsystem=");
+ stream.PutCString(subsystem);
+ ++header_count;
+ }
+ }
+
+ if (options_sp->GetDisplayCategory()) {
+ llvm::StringRef category;
+ if (event.GetValueForKeyAsString("category", category) &&
+ !category.empty()) {
+ if (header_count > 0)
+ stream.PutChar(',');
+ stream.PutCString("category=");
+ stream.PutCString(category);
+ ++header_count;
+ }
+ }
+ stream.PutCString("] ");
+
+ output_stream.PutCString(stream.GetString());
+
+ return stream.GetSize();
+}
+
+size_t StructuredDataDarwinLog::HandleDisplayOfEvent(
+ const StructuredData::Dictionary &event, Stream &stream) {
+ // Check the type of the event.
+ ConstString event_type;
+ if (!event.GetValueForKeyAsString("type", event_type)) {
+ // Hmm, we expected to get events that describe what they are. Continue
+ // anyway.
+ return 0;
+ }
+
+ if (event_type != GetLogEventType())
+ return 0;
+
+ size_t total_bytes = 0;
+
+ // Grab the message content.
+ llvm::StringRef message;
+ if (!event.GetValueForKeyAsString("message", message))
+ return true;
+
+ // Display the log entry.
+ const auto len = message.size();
+
+ total_bytes += DumpHeader(stream, event);
+
+ stream.Write(message.data(), len);
+ total_bytes += len;
+
+ // Add an end of line.
+ stream.PutChar('\n');
+ total_bytes += sizeof(char);
+
+ return total_bytes;
+}
+
+void StructuredDataDarwinLog::EnableNow() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() called", __FUNCTION__);
+
+ // Run the enable command.
+ auto process_sp = GetProcess();
+ if (!process_sp) {
+ // Nothing to do.
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() warning: failed to get "
+ "valid process, skipping",
+ __FUNCTION__);
+ return;
+ }
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() call is for process uid %u",
+ __FUNCTION__, process_sp->GetUniqueID());
+
+ // If we have configuration data, we can directly enable it now. Otherwise,
+ // we need to run through the command interpreter to parse the auto-run
+ // options (which is the only way we get here without having already-parsed
+ // configuration data).
+ DebuggerSP debugger_sp =
+ process_sp->GetTarget().GetDebugger().shared_from_this();
+ if (!debugger_sp) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() warning: failed to get "
+ "debugger shared pointer, skipping (process uid %u)",
+ __FUNCTION__, process_sp->GetUniqueID());
+ return;
+ }
+
+ auto options_sp = GetGlobalEnableOptions(debugger_sp);
+ if (!options_sp) {
+ // We haven't run the enable command yet. Just do that now, it'll take
+ // care of the rest.
+ auto &interpreter = debugger_sp->GetCommandInterpreter();
+ const bool success = RunEnableCommand(interpreter);
+ if (log) {
+ if (success)
+ log->Printf("StructuredDataDarwinLog::%s() ran enable command "
+ "successfully for (process uid %u)",
+ __FUNCTION__, process_sp->GetUniqueID());
+ else
+ log->Printf("StructuredDataDarwinLog::%s() error: running "
+ "enable command failed (process uid %u)",
+ __FUNCTION__, process_sp->GetUniqueID());
+ }
+ // Report failures to the debugger error stream.
+ auto error_stream_sp = debugger_sp->GetAsyncErrorStream();
+ if (error_stream_sp) {
+ error_stream_sp->Printf("failed to configure DarwinLog "
+ "support\n");
+ error_stream_sp->Flush();
+ }
+ return;
+ }
+
+ // We've previously been enabled. We will re-enable now with the previously
+ // specified options.
+ auto config_sp = options_sp->BuildConfigurationData(true);
+ if (!config_sp) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() warning: failed to "
+ "build configuration data for enable options, skipping "
+ "(process uid %u)",
+ __FUNCTION__, process_sp->GetUniqueID());
+ return;
+ }
+
+ // We can run it directly.
+ // Send configuration to the feature by way of the process.
+ const Status error =
+ process_sp->ConfigureStructuredData(GetDarwinLogTypeName(), config_sp);
+
+ // Report results.
+ if (!error.Success()) {
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() "
+ "ConfigureStructuredData() call failed "
+ "(process uid %u): %s",
+ __FUNCTION__, process_sp->GetUniqueID(), error.AsCString());
+ auto error_stream_sp = debugger_sp->GetAsyncErrorStream();
+ if (error_stream_sp) {
+ error_stream_sp->Printf("failed to configure DarwinLog "
+ "support: %s\n",
+ error.AsCString());
+ error_stream_sp->Flush();
+ }
+ m_is_enabled = false;
+ } else {
+ m_is_enabled = true;
+ if (log)
+ log->Printf("StructuredDataDarwinLog::%s() success via direct "
+ "configuration (process uid %u)",
+ __FUNCTION__, process_sp->GetUniqueID());
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.h b/contrib/llvm-project/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.h
new file mode 100644
index 000000000000..8fa1eed66efb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.h
@@ -0,0 +1,118 @@
+//===-- StructuredDataDarwinLog.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 StructuredDataDarwinLog_h
+#define StructuredDataDarwinLog_h
+
+#include "lldb/Target/StructuredDataPlugin.h"
+
+#include <mutex>
+
+// Forward declarations
+namespace sddarwinlog_private {
+class EnableCommand;
+}
+
+namespace lldb_private {
+
+class StructuredDataDarwinLog : public StructuredDataPlugin {
+ friend sddarwinlog_private::EnableCommand;
+
+public:
+ // Public static API
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static ConstString GetStaticPluginName();
+
+ /// Return whether the DarwinLog functionality is enabled.
+ ///
+ /// The DarwinLog functionality is enabled if the user expicitly enabled
+ /// it with the enable command, or if the user has the setting set
+ /// that controls if we always enable it for newly created/attached
+ /// processes.
+ ///
+ /// \return
+ /// True if DarwinLog support is/will be enabled for existing or
+ /// newly launched/attached processes.
+ static bool IsEnabled();
+
+ // PluginInterface API
+
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ // StructuredDataPlugin API
+
+ bool SupportsStructuredDataType(ConstString type_name) override;
+
+ void HandleArrivalOfStructuredData(
+ Process &process, ConstString type_name,
+ const StructuredData::ObjectSP &object_sp) override;
+
+ Status GetDescription(const StructuredData::ObjectSP &object_sp,
+ lldb_private::Stream &stream) override;
+
+ bool GetEnabled(ConstString type_name) const override;
+
+ void ModulesDidLoad(Process &process, ModuleList &module_list) override;
+
+ ~StructuredDataDarwinLog() override;
+
+private:
+ // Private constructors
+
+ StructuredDataDarwinLog(const lldb::ProcessWP &process_wp);
+
+ // Private static methods
+
+ static lldb::StructuredDataPluginSP CreateInstance(Process &process);
+
+ static void DebuggerInitialize(Debugger &debugger);
+
+ static bool InitCompletionHookCallback(void *baton,
+ StoppointCallbackContext *context,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id);
+
+ static Status FilterLaunchInfo(ProcessLaunchInfo &launch_info,
+ Target *target);
+
+ // Internal helper methods used by friend classes
+ void SetEnabled(bool enabled);
+
+ void AddInitCompletionHook(Process &process);
+
+ // Private methods
+
+ void DumpTimestamp(Stream &stream, uint64_t timestamp);
+
+ size_t DumpHeader(Stream &stream, const StructuredData::Dictionary &event);
+
+ size_t HandleDisplayOfEvent(const StructuredData::Dictionary &event,
+ Stream &stream);
+
+ /// Call the enable command again, using whatever settings were initially
+ /// made.
+
+ void EnableNow();
+
+ // Private data
+ bool m_recorded_first_timestamp;
+ uint64_t m_first_timestamp_seen;
+ bool m_is_enabled;
+ std::mutex m_added_breakpoint_mutex;
+ bool m_added_breakpoint;
+ lldb::user_id_t m_breakpoint_id;
+};
+}
+
+#endif /* StructuredDataPluginDarwinLog_hpp */
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp
new file mode 100644
index 000000000000..d4258274a38c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp
@@ -0,0 +1,653 @@
+//===-- SymbolFileBreakpad.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 "Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h"
+#include "Plugins/ObjectFile/Breakpad/BreakpadRecords.h"
+#include "Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/PostfixExpression.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/TypeMap.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+#include "llvm/ADT/StringExtras.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::breakpad;
+
+class SymbolFileBreakpad::LineIterator {
+public:
+ // begin iterator for sections of given type
+ LineIterator(ObjectFile &obj, Record::Kind section_type)
+ : m_obj(&obj), m_section_type(toString(section_type)),
+ m_next_section_idx(0), m_next_line(llvm::StringRef::npos) {
+ ++*this;
+ }
+
+ // An iterator starting at the position given by the bookmark.
+ LineIterator(ObjectFile &obj, Record::Kind section_type, Bookmark bookmark);
+
+ // end iterator
+ explicit LineIterator(ObjectFile &obj)
+ : m_obj(&obj),
+ m_next_section_idx(m_obj->GetSectionList()->GetNumSections(0)),
+ m_current_line(llvm::StringRef::npos),
+ m_next_line(llvm::StringRef::npos) {}
+
+ friend bool operator!=(const LineIterator &lhs, const LineIterator &rhs) {
+ assert(lhs.m_obj == rhs.m_obj);
+ if (lhs.m_next_section_idx != rhs.m_next_section_idx)
+ return true;
+ if (lhs.m_current_line != rhs.m_current_line)
+ return true;
+ assert(lhs.m_next_line == rhs.m_next_line);
+ return false;
+ }
+
+ const LineIterator &operator++();
+ llvm::StringRef operator*() const {
+ return m_section_text.slice(m_current_line, m_next_line);
+ }
+
+ Bookmark GetBookmark() const {
+ return Bookmark{m_next_section_idx, m_current_line};
+ }
+
+private:
+ ObjectFile *m_obj;
+ ConstString m_section_type;
+ uint32_t m_next_section_idx;
+ llvm::StringRef m_section_text;
+ size_t m_current_line;
+ size_t m_next_line;
+
+ void FindNextLine() {
+ m_next_line = m_section_text.find('\n', m_current_line);
+ if (m_next_line != llvm::StringRef::npos) {
+ ++m_next_line;
+ if (m_next_line >= m_section_text.size())
+ m_next_line = llvm::StringRef::npos;
+ }
+ }
+};
+
+SymbolFileBreakpad::LineIterator::LineIterator(ObjectFile &obj,
+ Record::Kind section_type,
+ Bookmark bookmark)
+ : m_obj(&obj), m_section_type(toString(section_type)),
+ m_next_section_idx(bookmark.section), m_current_line(bookmark.offset) {
+ Section &sect =
+ *obj.GetSectionList()->GetSectionAtIndex(m_next_section_idx - 1);
+ assert(sect.GetName() == m_section_type);
+
+ DataExtractor data;
+ obj.ReadSectionData(&sect, data);
+ m_section_text = toStringRef(data.GetData());
+
+ assert(m_current_line < m_section_text.size());
+ FindNextLine();
+}
+
+const SymbolFileBreakpad::LineIterator &
+SymbolFileBreakpad::LineIterator::operator++() {
+ const SectionList &list = *m_obj->GetSectionList();
+ size_t num_sections = list.GetNumSections(0);
+ while (m_next_line != llvm::StringRef::npos ||
+ m_next_section_idx < num_sections) {
+ if (m_next_line != llvm::StringRef::npos) {
+ m_current_line = m_next_line;
+ FindNextLine();
+ return *this;
+ }
+
+ Section &sect = *list.GetSectionAtIndex(m_next_section_idx++);
+ if (sect.GetName() != m_section_type)
+ continue;
+ DataExtractor data;
+ m_obj->ReadSectionData(&sect, data);
+ m_section_text = toStringRef(data.GetData());
+ m_next_line = 0;
+ }
+ // We've reached the end.
+ m_current_line = m_next_line;
+ return *this;
+}
+
+llvm::iterator_range<SymbolFileBreakpad::LineIterator>
+SymbolFileBreakpad::lines(Record::Kind section_type) {
+ return llvm::make_range(LineIterator(*m_obj_file, section_type),
+ LineIterator(*m_obj_file));
+}
+
+namespace {
+// A helper class for constructing the list of support files for a given compile
+// unit.
+class SupportFileMap {
+public:
+ // Given a breakpad file ID, return a file ID to be used in the support files
+ // for this compile unit.
+ size_t operator[](size_t file) {
+ return m_map.try_emplace(file, m_map.size() + 1).first->second;
+ }
+
+ // Construct a FileSpecList containing only the support files relevant for
+ // this compile unit (in the correct order).
+ FileSpecList translate(const FileSpec &cu_spec,
+ llvm::ArrayRef<FileSpec> all_files);
+
+private:
+ llvm::DenseMap<size_t, size_t> m_map;
+};
+} // namespace
+
+FileSpecList SupportFileMap::translate(const FileSpec &cu_spec,
+ llvm::ArrayRef<FileSpec> all_files) {
+ std::vector<FileSpec> result;
+ result.resize(m_map.size() + 1);
+ result[0] = cu_spec;
+ for (const auto &KV : m_map) {
+ if (KV.first < all_files.size())
+ result[KV.second] = all_files[KV.first];
+ }
+ return FileSpecList(std::move(result));
+}
+
+void SymbolFileBreakpad::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ DebuggerInitialize);
+}
+
+void SymbolFileBreakpad::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+ConstString SymbolFileBreakpad::GetPluginNameStatic() {
+ static ConstString g_name("breakpad");
+ return g_name;
+}
+
+uint32_t SymbolFileBreakpad::CalculateAbilities() {
+ if (!m_obj_file)
+ return 0;
+ if (m_obj_file->GetPluginName() != ObjectFileBreakpad::GetPluginNameStatic())
+ return 0;
+
+ return CompileUnits | Functions | LineTables;
+}
+
+uint32_t SymbolFileBreakpad::GetNumCompileUnits() {
+ ParseCUData();
+ return m_cu_data->GetSize();
+}
+
+CompUnitSP SymbolFileBreakpad::ParseCompileUnitAtIndex(uint32_t index) {
+ if (index >= m_cu_data->GetSize())
+ return nullptr;
+
+ CompUnitData &data = m_cu_data->GetEntryRef(index).data;
+
+ ParseFileRecords();
+
+ FileSpec spec;
+
+ // The FileSpec of the compile unit will be the file corresponding to the
+ // first LINE record.
+ LineIterator It(*m_obj_file, Record::Func, data.bookmark), End(*m_obj_file);
+ assert(Record::classify(*It) == Record::Func);
+ ++It; // Skip FUNC record.
+ if (It != End) {
+ auto record = LineRecord::parse(*It);
+ if (record && record->FileNum < m_files->size())
+ spec = (*m_files)[record->FileNum];
+ }
+
+ auto cu_sp = std::make_shared<CompileUnit>(m_obj_file->GetModule(),
+ /*user_data*/ nullptr, spec, index,
+ eLanguageTypeUnknown,
+ /*is_optimized*/ eLazyBoolNo);
+
+ GetSymbolVendor().SetCompileUnitAtIndex(index, cu_sp);
+ return cu_sp;
+}
+
+size_t SymbolFileBreakpad::ParseFunctions(CompileUnit &comp_unit) {
+ // TODO
+ return 0;
+}
+
+bool SymbolFileBreakpad::ParseLineTable(CompileUnit &comp_unit) {
+ CompUnitData &data = m_cu_data->GetEntryRef(comp_unit.GetID()).data;
+
+ if (!data.line_table_up)
+ ParseLineTableAndSupportFiles(comp_unit, data);
+
+ comp_unit.SetLineTable(data.line_table_up.release());
+ return true;
+}
+
+bool SymbolFileBreakpad::ParseSupportFiles(CompileUnit &comp_unit,
+ FileSpecList &support_files) {
+ CompUnitData &data = m_cu_data->GetEntryRef(comp_unit.GetID()).data;
+ if (!data.support_files)
+ ParseLineTableAndSupportFiles(comp_unit, data);
+
+ support_files = std::move(*data.support_files);
+ return true;
+}
+
+uint32_t
+SymbolFileBreakpad::ResolveSymbolContext(const Address &so_addr,
+ SymbolContextItem resolve_scope,
+ SymbolContext &sc) {
+ if (!(resolve_scope & (eSymbolContextCompUnit | eSymbolContextLineEntry)))
+ return 0;
+
+ ParseCUData();
+ uint32_t idx =
+ m_cu_data->FindEntryIndexThatContains(so_addr.GetFileAddress());
+ if (idx == UINT32_MAX)
+ return 0;
+
+ sc.comp_unit = GetSymbolVendor().GetCompileUnitAtIndex(idx).get();
+ SymbolContextItem result = eSymbolContextCompUnit;
+ if (resolve_scope & eSymbolContextLineEntry) {
+ if (sc.comp_unit->GetLineTable()->FindLineEntryByAddress(so_addr,
+ sc.line_entry)) {
+ result |= eSymbolContextLineEntry;
+ }
+ }
+
+ return result;
+}
+
+uint32_t SymbolFileBreakpad::ResolveSymbolContext(
+ const FileSpec &file_spec, uint32_t line, bool check_inlines,
+ lldb::SymbolContextItem resolve_scope, SymbolContextList &sc_list) {
+ if (!(resolve_scope & eSymbolContextCompUnit))
+ return 0;
+
+ uint32_t old_size = sc_list.GetSize();
+ for (size_t i = 0, size = GetNumCompileUnits(); i < size; ++i) {
+ CompileUnit &cu = *GetSymbolVendor().GetCompileUnitAtIndex(i);
+ cu.ResolveSymbolContext(file_spec, line, check_inlines,
+ /*exact*/ false, resolve_scope, sc_list);
+ }
+ return sc_list.GetSize() - old_size;
+}
+
+uint32_t SymbolFileBreakpad::FindFunctions(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ FunctionNameType name_type_mask, bool include_inlines, bool append,
+ SymbolContextList &sc_list) {
+ // TODO
+ if (!append)
+ sc_list.Clear();
+ return sc_list.GetSize();
+}
+
+uint32_t SymbolFileBreakpad::FindFunctions(const RegularExpression &regex,
+ bool include_inlines, bool append,
+ SymbolContextList &sc_list) {
+ // TODO
+ if (!append)
+ sc_list.Clear();
+ return sc_list.GetSize();
+}
+
+uint32_t SymbolFileBreakpad::FindTypes(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ bool append, uint32_t max_matches,
+ llvm::DenseSet<SymbolFile *> &searched_symbol_files, TypeMap &types) {
+ if (!append)
+ types.Clear();
+ return types.GetSize();
+}
+
+size_t
+SymbolFileBreakpad::FindTypes(const std::vector<CompilerContext> &context,
+ bool append, TypeMap &types) {
+ if (!append)
+ types.Clear();
+ return types.GetSize();
+}
+
+void SymbolFileBreakpad::AddSymbols(Symtab &symtab) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS);
+ Module &module = *m_obj_file->GetModule();
+ addr_t base = GetBaseFileAddress();
+ if (base == LLDB_INVALID_ADDRESS) {
+ LLDB_LOG(log, "Unable to fetch the base address of object file. Skipping "
+ "symtab population.");
+ return;
+ }
+
+ const SectionList &list = *module.GetSectionList();
+ llvm::DenseMap<addr_t, Symbol> symbols;
+ auto add_symbol = [&](addr_t address, llvm::Optional<addr_t> size,
+ llvm::StringRef name) {
+ address += base;
+ SectionSP section_sp = list.FindSectionContainingFileAddress(address);
+ if (!section_sp) {
+ LLDB_LOG(log,
+ "Ignoring symbol {0}, whose address ({1}) is outside of the "
+ "object file. Mismatched symbol file?",
+ name, address);
+ return;
+ }
+ symbols.try_emplace(
+ address, /*symID*/ 0, Mangled(name, /*is_mangled*/ false),
+ eSymbolTypeCode, /*is_global*/ true, /*is_debug*/ false,
+ /*is_trampoline*/ false, /*is_artificial*/ false,
+ AddressRange(section_sp, address - section_sp->GetFileAddress(),
+ size.getValueOr(0)),
+ size.hasValue(), /*contains_linker_annotations*/ false, /*flags*/ 0);
+ };
+
+ for (llvm::StringRef line : lines(Record::Func)) {
+ if (auto record = FuncRecord::parse(line))
+ add_symbol(record->Address, record->Size, record->Name);
+ }
+
+ for (llvm::StringRef line : lines(Record::Public)) {
+ if (auto record = PublicRecord::parse(line))
+ add_symbol(record->Address, llvm::None, record->Name);
+ else
+ LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", line);
+ }
+
+ for (auto &KV : symbols)
+ symtab.AddSymbol(std::move(KV.second));
+ symtab.CalculateSymbolSizes();
+}
+
+static llvm::Optional<std::pair<llvm::StringRef, llvm::StringRef>>
+GetRule(llvm::StringRef &unwind_rules) {
+ // Unwind rules are of the form
+ // register1: expression1 register2: expression2 ...
+ // We assume none of the tokens in expression<n> end with a colon.
+
+ llvm::StringRef lhs, rest;
+ std::tie(lhs, rest) = getToken(unwind_rules);
+ if (!lhs.consume_back(":"))
+ return llvm::None;
+
+ // Seek forward to the next register: expression pair
+ llvm::StringRef::size_type pos = rest.find(": ");
+ if (pos == llvm::StringRef::npos) {
+ // No pair found, this means the rest of the string is a single expression.
+ unwind_rules = llvm::StringRef();
+ return std::make_pair(lhs, rest);
+ }
+
+ // Go back one token to find the end of the current rule.
+ pos = rest.rfind(' ', pos);
+ if (pos == llvm::StringRef::npos)
+ return llvm::None;
+
+ llvm::StringRef rhs = rest.take_front(pos);
+ unwind_rules = rest.drop_front(pos);
+ return std::make_pair(lhs, rhs);
+}
+
+static const RegisterInfo *
+ResolveRegister(const SymbolFile::RegisterInfoResolver &resolver,
+ llvm::StringRef name) {
+ if (name.consume_front("$"))
+ return resolver.ResolveName(name);
+
+ return nullptr;
+}
+
+static const RegisterInfo *
+ResolveRegisterOrRA(const SymbolFile::RegisterInfoResolver &resolver,
+ llvm::StringRef name) {
+ if (name == ".ra")
+ return resolver.ResolveNumber(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ return ResolveRegister(resolver, name);
+}
+
+bool SymbolFileBreakpad::ParseUnwindRow(llvm::StringRef unwind_rules,
+ const RegisterInfoResolver &resolver,
+ UnwindPlan::Row &row) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS);
+
+ llvm::BumpPtrAllocator node_alloc;
+ while (auto rule = GetRule(unwind_rules)) {
+ node_alloc.Reset();
+ llvm::StringRef lhs = rule->first;
+ postfix::Node *rhs = postfix::Parse(rule->second, node_alloc);
+ if (!rhs) {
+ LLDB_LOG(log, "Could not parse `{0}` as unwind rhs.", rule->second);
+ return false;
+ }
+
+ bool success = postfix::ResolveSymbols(
+ rhs, [&](postfix::SymbolNode &symbol) -> postfix::Node * {
+ llvm::StringRef name = symbol.GetName();
+ if (name == ".cfa" && lhs != ".cfa")
+ return postfix::MakeNode<postfix::InitialValueNode>(node_alloc);
+
+ if (const RegisterInfo *info = ResolveRegister(resolver, name)) {
+ return postfix::MakeNode<postfix::RegisterNode>(
+ node_alloc, info->kinds[eRegisterKindLLDB]);
+ }
+ return nullptr;
+ });
+
+ if (!success) {
+ LLDB_LOG(log, "Resolving symbols in `{0}` failed.", rule->second);
+ return false;
+ }
+
+ ArchSpec arch = m_obj_file->GetArchitecture();
+ StreamString dwarf(Stream::eBinary, arch.GetAddressByteSize(),
+ arch.GetByteOrder());
+ ToDWARF(*rhs, dwarf);
+ uint8_t *saved = m_allocator.Allocate<uint8_t>(dwarf.GetSize());
+ std::memcpy(saved, dwarf.GetData(), dwarf.GetSize());
+
+ if (lhs == ".cfa") {
+ row.GetCFAValue().SetIsDWARFExpression(saved, dwarf.GetSize());
+ } else if (const RegisterInfo *info = ResolveRegisterOrRA(resolver, lhs)) {
+ UnwindPlan::Row::RegisterLocation loc;
+ loc.SetIsDWARFExpression(saved, dwarf.GetSize());
+ row.SetRegisterInfo(info->kinds[eRegisterKindLLDB], loc);
+ } else
+ LLDB_LOG(log, "Invalid register `{0}` in unwind rule.", lhs);
+ }
+ if (unwind_rules.empty())
+ return true;
+
+ LLDB_LOG(log, "Could not parse `{0}` as an unwind rule.", unwind_rules);
+ return false;
+}
+
+UnwindPlanSP
+SymbolFileBreakpad::GetUnwindPlan(const Address &address,
+ const RegisterInfoResolver &resolver) {
+ ParseUnwindData();
+ const UnwindMap::Entry *entry =
+ m_unwind_data->FindEntryThatContains(address.GetFileAddress());
+ if (!entry)
+ return nullptr;
+
+ addr_t base = GetBaseFileAddress();
+ if (base == LLDB_INVALID_ADDRESS)
+ return nullptr;
+
+ LineIterator It(*m_obj_file, Record::StackCFI, entry->data), End(*m_obj_file);
+ llvm::Optional<StackCFIRecord> init_record = StackCFIRecord::parse(*It);
+ assert(init_record.hasValue());
+ assert(init_record->Size.hasValue());
+
+ auto plan_sp = std::make_shared<UnwindPlan>(lldb::eRegisterKindLLDB);
+ plan_sp->SetSourceName("breakpad STACK CFI");
+ plan_sp->SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+ plan_sp->SetSourcedFromCompiler(eLazyBoolYes);
+ plan_sp->SetPlanValidAddressRange(
+ AddressRange(base + init_record->Address, *init_record->Size,
+ m_obj_file->GetModule()->GetSectionList()));
+
+ auto row_sp = std::make_shared<UnwindPlan::Row>();
+ row_sp->SetOffset(0);
+ if (!ParseUnwindRow(init_record->UnwindRules, resolver, *row_sp))
+ return nullptr;
+ plan_sp->AppendRow(row_sp);
+ for (++It; It != End; ++It) {
+ llvm::Optional<StackCFIRecord> record = StackCFIRecord::parse(*It);
+ if (!record.hasValue())
+ return nullptr;
+ if (record->Size.hasValue())
+ break;
+
+ row_sp = std::make_shared<UnwindPlan::Row>(*row_sp);
+ row_sp->SetOffset(record->Address - init_record->Address);
+ if (!ParseUnwindRow(record->UnwindRules, resolver, *row_sp))
+ return nullptr;
+ plan_sp->AppendRow(row_sp);
+ }
+ return plan_sp;
+}
+
+SymbolVendor &SymbolFileBreakpad::GetSymbolVendor() {
+ return *m_obj_file->GetModule()->GetSymbolVendor();
+}
+
+addr_t SymbolFileBreakpad::GetBaseFileAddress() {
+ return m_obj_file->GetModule()
+ ->GetObjectFile()
+ ->GetBaseAddress()
+ .GetFileAddress();
+}
+
+// Parse out all the FILE records from the breakpad file. These will be needed
+// when constructing the support file lists for individual compile units.
+void SymbolFileBreakpad::ParseFileRecords() {
+ if (m_files)
+ return;
+ m_files.emplace();
+
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS);
+ for (llvm::StringRef line : lines(Record::File)) {
+ auto record = FileRecord::parse(line);
+ if (!record) {
+ LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", line);
+ continue;
+ }
+
+ if (record->Number >= m_files->size())
+ m_files->resize(record->Number + 1);
+ FileSpec::Style style = FileSpec::GuessPathStyle(record->Name)
+ .getValueOr(FileSpec::Style::native);
+ (*m_files)[record->Number] = FileSpec(record->Name, style);
+ }
+}
+
+void SymbolFileBreakpad::ParseCUData() {
+ if (m_cu_data)
+ return;
+
+ m_cu_data.emplace();
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS);
+ addr_t base = GetBaseFileAddress();
+ if (base == LLDB_INVALID_ADDRESS) {
+ LLDB_LOG(log, "SymbolFile parsing failed: Unable to fetch the base address "
+ "of object file.");
+ }
+
+ // We shall create one compile unit for each FUNC record. So, count the number
+ // of FUNC records, and store them in m_cu_data, together with their ranges.
+ for (LineIterator It(*m_obj_file, Record::Func), End(*m_obj_file); It != End;
+ ++It) {
+ if (auto record = FuncRecord::parse(*It)) {
+ m_cu_data->Append(CompUnitMap::Entry(base + record->Address, record->Size,
+ CompUnitData(It.GetBookmark())));
+ } else
+ LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", *It);
+ }
+ m_cu_data->Sort();
+}
+
+// Construct the list of support files and line table entries for the given
+// compile unit.
+void SymbolFileBreakpad::ParseLineTableAndSupportFiles(CompileUnit &cu,
+ CompUnitData &data) {
+ addr_t base = GetBaseFileAddress();
+ assert(base != LLDB_INVALID_ADDRESS &&
+ "How did we create compile units without a base address?");
+
+ SupportFileMap map;
+ data.line_table_up = llvm::make_unique<LineTable>(&cu);
+ std::unique_ptr<LineSequence> line_seq_up(
+ data.line_table_up->CreateLineSequenceContainer());
+ llvm::Optional<addr_t> next_addr;
+ auto finish_sequence = [&]() {
+ data.line_table_up->AppendLineEntryToSequence(
+ line_seq_up.get(), *next_addr, /*line*/ 0, /*column*/ 0,
+ /*file_idx*/ 0, /*is_start_of_statement*/ false,
+ /*is_start_of_basic_block*/ false, /*is_prologue_end*/ false,
+ /*is_epilogue_begin*/ false, /*is_terminal_entry*/ true);
+ data.line_table_up->InsertSequence(line_seq_up.get());
+ line_seq_up->Clear();
+ };
+
+ LineIterator It(*m_obj_file, Record::Func, data.bookmark), End(*m_obj_file);
+ assert(Record::classify(*It) == Record::Func);
+ for (++It; It != End; ++It) {
+ auto record = LineRecord::parse(*It);
+ if (!record)
+ break;
+
+ record->Address += base;
+
+ if (next_addr && *next_addr != record->Address) {
+ // Discontiguous entries. Finish off the previous sequence and reset.
+ finish_sequence();
+ }
+ data.line_table_up->AppendLineEntryToSequence(
+ line_seq_up.get(), record->Address, record->LineNum, /*column*/ 0,
+ map[record->FileNum], /*is_start_of_statement*/ true,
+ /*is_start_of_basic_block*/ false, /*is_prologue_end*/ false,
+ /*is_epilogue_begin*/ false, /*is_terminal_entry*/ false);
+ next_addr = record->Address + record->Size;
+ }
+ if (next_addr)
+ finish_sequence();
+ data.support_files = map.translate(cu, *m_files);
+}
+
+void SymbolFileBreakpad::ParseUnwindData() {
+ if (m_unwind_data)
+ return;
+
+ m_unwind_data.emplace();
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS);
+ addr_t base = GetBaseFileAddress();
+ if (base == LLDB_INVALID_ADDRESS) {
+ LLDB_LOG(log, "SymbolFile parsing failed: Unable to fetch the base address "
+ "of object file.");
+ }
+
+ for (LineIterator It(*m_obj_file, Record::StackCFI), End(*m_obj_file);
+ It != End; ++It) {
+ if (auto record = StackCFIRecord::parse(*It)) {
+ if (record->Size)
+ m_unwind_data->Append(UnwindMap::Entry(
+ base + record->Address, *record->Size, It.GetBookmark()));
+ } else
+ LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", *It);
+ }
+ m_unwind_data->Sort();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h
new file mode 100644
index 000000000000..8a0b7645fd0a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h
@@ -0,0 +1,222 @@
+//===-- SymbolFileBreakpad.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_PLUGINS_SYMBOLFILE_BREAKPAD_SYMBOLFILEBREAKPAD_H
+#define LLDB_PLUGINS_SYMBOLFILE_BREAKPAD_SYMBOLFILEBREAKPAD_H
+
+#include "Plugins/ObjectFile/Breakpad/BreakpadRecords.h"
+#include "lldb/Core/FileSpecList.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/UnwindPlan.h"
+
+namespace lldb_private {
+
+namespace breakpad {
+
+class SymbolFileBreakpad : public SymbolFile {
+public:
+ // Static Functions
+ static void Initialize();
+ static void Terminate();
+ static void DebuggerInitialize(Debugger &debugger) {}
+ static ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic() {
+ return "Breakpad debug symbol file reader.";
+ }
+
+ static SymbolFile *CreateInstance(ObjectFile *obj_file) {
+ return new SymbolFileBreakpad(obj_file);
+ }
+
+ // Constructors and Destructors
+ SymbolFileBreakpad(ObjectFile *object_file) : SymbolFile(object_file) {}
+
+ ~SymbolFileBreakpad() override {}
+
+ uint32_t CalculateAbilities() override;
+
+ void InitializeObject() override {}
+
+ // Compile Unit function calls
+
+ uint32_t GetNumCompileUnits() override;
+
+ lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override;
+
+ lldb::LanguageType ParseLanguage(CompileUnit &comp_unit) override {
+ return lldb::eLanguageTypeUnknown;
+ }
+
+ size_t ParseFunctions(CompileUnit &comp_unit) override;
+
+ bool ParseLineTable(CompileUnit &comp_unit) override;
+
+ bool ParseDebugMacros(CompileUnit &comp_unit) override { return false; }
+
+ bool ParseSupportFiles(CompileUnit &comp_unit,
+ FileSpecList &support_files) override;
+ size_t ParseTypes(CompileUnit &cu) override { return 0; }
+
+ bool ParseImportedModules(
+ const SymbolContext &sc,
+ std::vector<lldb_private::SourceModule> &imported_modules) override {
+ return false;
+ }
+
+ size_t ParseBlocksRecursive(Function &func) override { return 0; }
+
+ uint32_t FindGlobalVariables(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches,
+ VariableList &variables) override {
+ return 0;
+ }
+
+ size_t ParseVariablesForContext(const SymbolContext &sc) override {
+ return 0;
+ }
+ Type *ResolveTypeUID(lldb::user_id_t type_uid) override { return nullptr; }
+ llvm::Optional<ArrayInfo> GetDynamicArrayInfoForUID(
+ lldb::user_id_t type_uid,
+ const lldb_private::ExecutionContext *exe_ctx) override {
+ return llvm::None;
+ }
+
+ bool CompleteType(CompilerType &compiler_type) override { return false; }
+ uint32_t ResolveSymbolContext(const Address &so_addr,
+ lldb::SymbolContextItem resolve_scope,
+ SymbolContext &sc) override;
+
+ uint32_t ResolveSymbolContext(const FileSpec &file_spec, uint32_t line,
+ bool check_inlines,
+ lldb::SymbolContextItem resolve_scope,
+ SymbolContextList &sc_list) override;
+
+ size_t GetTypes(SymbolContextScope *sc_scope, lldb::TypeClass type_mask,
+ TypeList &type_list) override {
+ return 0;
+ }
+
+ uint32_t FindFunctions(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx,
+ lldb::FunctionNameType name_type_mask,
+ bool include_inlines, bool append,
+ SymbolContextList &sc_list) override;
+
+ uint32_t FindFunctions(const RegularExpression &regex, bool include_inlines,
+ bool append, SymbolContextList &sc_list) override;
+
+ uint32_t FindTypes(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx, bool append,
+ uint32_t max_matches,
+ llvm::DenseSet<SymbolFile *> &searched_symbol_files,
+ TypeMap &types) override;
+
+ size_t FindTypes(const std::vector<CompilerContext> &context, bool append,
+ TypeMap &types) override;
+
+ TypeSystem *GetTypeSystemForLanguage(lldb::LanguageType language) override {
+ return nullptr;
+ }
+
+ CompilerDeclContext
+ FindNamespace(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx) override {
+ return CompilerDeclContext();
+ }
+
+ void AddSymbols(Symtab &symtab) override;
+
+ lldb::UnwindPlanSP
+ GetUnwindPlan(const Address &address,
+ const RegisterInfoResolver &resolver) override;
+
+ ConstString GetPluginName() override { return GetPluginNameStatic(); }
+ uint32_t GetPluginVersion() override { return 1; }
+
+private:
+ // A class representing a position in the breakpad file. Useful for
+ // remembering the position so we can go back to it later and parse more data.
+ // Can be converted to/from a LineIterator, but it has a much smaller memory
+ // footprint.
+ struct Bookmark {
+ uint32_t section;
+ size_t offset;
+
+ friend bool operator<(const Bookmark &lhs, const Bookmark &rhs) {
+ return std::tie(lhs.section, lhs.offset) <
+ std::tie(rhs.section, rhs.offset);
+ }
+ };
+
+ // At iterator class for simplifying algorithms reading data from the breakpad
+ // file. It iterates over all records (lines) in the sections of a given type.
+ // It also supports saving a specific position (via the GetBookmark() method)
+ // and then resuming from it afterwards.
+ class LineIterator;
+
+ // Return an iterator range for all records in the given object file of the
+ // given type.
+ llvm::iterator_range<LineIterator> lines(Record::Kind section_type);
+
+ // Breakpad files do not contain sufficient information to correctly
+ // reconstruct compile units. The approach chosen here is to treat each
+ // function as a compile unit. The compile unit name is the name if the first
+ // line entry belonging to this function.
+ // This class is our internal representation of a compile unit. It stores the
+ // CompileUnit object and a bookmark pointing to the FUNC record of the
+ // compile unit function. It also lazily construct the list of support files
+ // and line table entries for the compile unit, when these are needed.
+ class CompUnitData {
+ public:
+ CompUnitData(Bookmark bookmark) : bookmark(bookmark) {}
+
+ CompUnitData() = default;
+ CompUnitData(const CompUnitData &rhs) : bookmark(rhs.bookmark) {}
+ CompUnitData &operator=(const CompUnitData &rhs) {
+ bookmark = rhs.bookmark;
+ support_files.reset();
+ line_table_up.reset();
+ return *this;
+ }
+ friend bool operator<(const CompUnitData &lhs, const CompUnitData &rhs) {
+ return lhs.bookmark < rhs.bookmark;
+ }
+
+ Bookmark bookmark;
+ llvm::Optional<FileSpecList> support_files;
+ std::unique_ptr<LineTable> line_table_up;
+
+ };
+
+ SymbolVendor &GetSymbolVendor();
+ lldb::addr_t GetBaseFileAddress();
+ void ParseFileRecords();
+ void ParseCUData();
+ void ParseLineTableAndSupportFiles(CompileUnit &cu, CompUnitData &data);
+ void ParseUnwindData();
+ bool ParseUnwindRow(llvm::StringRef unwind_rules,
+ const RegisterInfoResolver &resolver,
+ UnwindPlan::Row &row);
+
+ using CompUnitMap = RangeDataVector<lldb::addr_t, lldb::addr_t, CompUnitData>;
+
+ llvm::Optional<std::vector<FileSpec>> m_files;
+ llvm::Optional<CompUnitMap> m_cu_data;
+
+ using UnwindMap = RangeDataVector<lldb::addr_t, lldb::addr_t, Bookmark>;
+ llvm::Optional<UnwindMap> m_unwind_data;
+ llvm::BumpPtrAllocator m_allocator;
+};
+
+} // namespace breakpad
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp
new file mode 100644
index 000000000000..9ae047da1325
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp
@@ -0,0 +1,175 @@
+//===-- AppleDWARFIndex.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 "Plugins/SymbolFile/DWARF/AppleDWARFIndex.h"
+#include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h"
+#include "Plugins/SymbolFile/DWARF/DWARFUnit.h"
+#include "Plugins/SymbolFile/DWARF/LogChannelDWARF.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/Function.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+std::unique_ptr<AppleDWARFIndex> AppleDWARFIndex::Create(
+ Module &module, DWARFDataExtractor apple_names,
+ DWARFDataExtractor apple_namespaces, DWARFDataExtractor apple_types,
+ DWARFDataExtractor apple_objc, DWARFDataExtractor debug_str) {
+ auto apple_names_table_up = llvm::make_unique<DWARFMappedHash::MemoryTable>(
+ apple_names, debug_str, ".apple_names");
+ if (!apple_names_table_up->IsValid())
+ apple_names_table_up.reset();
+
+ auto apple_namespaces_table_up =
+ llvm::make_unique<DWARFMappedHash::MemoryTable>(
+ apple_namespaces, debug_str, ".apple_namespaces");
+ if (!apple_namespaces_table_up->IsValid())
+ apple_namespaces_table_up.reset();
+
+ auto apple_types_table_up = llvm::make_unique<DWARFMappedHash::MemoryTable>(
+ apple_types, debug_str, ".apple_types");
+ if (!apple_types_table_up->IsValid())
+ apple_types_table_up.reset();
+
+ auto apple_objc_table_up = llvm::make_unique<DWARFMappedHash::MemoryTable>(
+ apple_objc, debug_str, ".apple_objc");
+ if (!apple_objc_table_up->IsValid())
+ apple_objc_table_up.reset();
+
+ if (apple_names_table_up || apple_names_table_up || apple_types_table_up ||
+ apple_objc_table_up)
+ return llvm::make_unique<AppleDWARFIndex>(
+ module, std::move(apple_names_table_up),
+ std::move(apple_namespaces_table_up), std::move(apple_types_table_up),
+ std::move(apple_objc_table_up));
+
+ return nullptr;
+}
+
+void AppleDWARFIndex::GetGlobalVariables(ConstString basename, DIEArray &offsets) {
+ if (m_apple_names_up)
+ m_apple_names_up->FindByName(basename.GetStringRef(), offsets);
+}
+
+void AppleDWARFIndex::GetGlobalVariables(const RegularExpression &regex,
+ DIEArray &offsets) {
+ if (!m_apple_names_up)
+ return;
+
+ DWARFMappedHash::DIEInfoArray hash_data;
+ if (m_apple_names_up->AppendAllDIEsThatMatchingRegex(regex, hash_data))
+ DWARFMappedHash::ExtractDIEArray(hash_data, offsets);
+}
+
+void AppleDWARFIndex::GetGlobalVariables(const DWARFUnit &cu,
+ DIEArray &offsets) {
+ if (!m_apple_names_up)
+ return;
+
+ DWARFMappedHash::DIEInfoArray hash_data;
+ if (m_apple_names_up->AppendAllDIEsInRange(cu.GetOffset(),
+ cu.GetNextUnitOffset(), hash_data))
+ DWARFMappedHash::ExtractDIEArray(hash_data, offsets);
+}
+
+void AppleDWARFIndex::GetObjCMethods(ConstString class_name,
+ DIEArray &offsets) {
+ if (m_apple_objc_up)
+ m_apple_objc_up->FindByName(class_name.GetStringRef(), offsets);
+}
+
+void AppleDWARFIndex::GetCompleteObjCClass(ConstString class_name,
+ bool must_be_implementation,
+ DIEArray &offsets) {
+ if (m_apple_types_up) {
+ m_apple_types_up->FindCompleteObjCClassByName(
+ class_name.GetStringRef(), offsets, must_be_implementation);
+ }
+}
+
+void AppleDWARFIndex::GetTypes(ConstString name, DIEArray &offsets) {
+ if (m_apple_types_up)
+ m_apple_types_up->FindByName(name.GetStringRef(), offsets);
+}
+
+void AppleDWARFIndex::GetTypes(const DWARFDeclContext &context,
+ DIEArray &offsets) {
+ if (!m_apple_types_up)
+ return;
+
+ Log *log = LogChannelDWARF::GetLogIfAny(DWARF_LOG_TYPE_COMPLETION |
+ DWARF_LOG_LOOKUPS);
+ const bool has_tag = m_apple_types_up->GetHeader().header_data.ContainsAtom(
+ DWARFMappedHash::eAtomTypeTag);
+ const bool has_qualified_name_hash =
+ m_apple_types_up->GetHeader().header_data.ContainsAtom(
+ DWARFMappedHash::eAtomTypeQualNameHash);
+ const ConstString type_name(context[0].name);
+ const dw_tag_t tag = context[0].tag;
+ if (has_tag && has_qualified_name_hash) {
+ const char *qualified_name = context.GetQualifiedName();
+ const uint32_t qualified_name_hash = llvm::djbHash(qualified_name);
+ if (log)
+ m_module.LogMessage(log, "FindByNameAndTagAndQualifiedNameHash()");
+ m_apple_types_up->FindByNameAndTagAndQualifiedNameHash(
+ type_name.GetStringRef(), tag, qualified_name_hash, offsets);
+ } else if (has_tag) {
+ if (log)
+ m_module.LogMessage(log, "FindByNameAndTag()");
+ m_apple_types_up->FindByNameAndTag(type_name.GetStringRef(), tag, offsets);
+ } else
+ m_apple_types_up->FindByName(type_name.GetStringRef(), offsets);
+}
+
+void AppleDWARFIndex::GetNamespaces(ConstString name, DIEArray &offsets) {
+ if (m_apple_namespaces_up)
+ m_apple_namespaces_up->FindByName(name.GetStringRef(), offsets);
+}
+
+void AppleDWARFIndex::GetFunctions(ConstString name, SymbolFileDWARF &dwarf,
+ const CompilerDeclContext &parent_decl_ctx,
+ uint32_t name_type_mask,
+ std::vector<DWARFDIE> &dies) {
+ DIEArray offsets;
+ m_apple_names_up->FindByName(name.GetStringRef(), offsets);
+ for (const DIERef &die_ref : offsets) {
+ ProcessFunctionDIE(name.GetStringRef(), die_ref, dwarf, parent_decl_ctx,
+ name_type_mask, dies);
+ }
+}
+
+void AppleDWARFIndex::GetFunctions(const RegularExpression &regex,
+ DIEArray &offsets) {
+ if (!m_apple_names_up)
+ return;
+
+ DWARFMappedHash::DIEInfoArray hash_data;
+ if (m_apple_names_up->AppendAllDIEsThatMatchingRegex(regex, hash_data))
+ DWARFMappedHash::ExtractDIEArray(hash_data, offsets);
+}
+
+void AppleDWARFIndex::ReportInvalidDIERef(const DIERef &ref,
+ llvm::StringRef name) {
+ m_module.ReportErrorIfModifyDetected(
+ "the DWARF debug information has been modified (accelerator table had "
+ "bad die 0x%8.8x for '%s')\n",
+ ref.die_offset(), name.str().c_str());
+}
+
+void AppleDWARFIndex::Dump(Stream &s) {
+ if (m_apple_names_up)
+ s.PutCString(".apple_names index present\n");
+ if (m_apple_namespaces_up)
+ s.PutCString(".apple_namespaces index present\n");
+ if (m_apple_types_up)
+ s.PutCString(".apple_types index present\n");
+ if (m_apple_objc_up)
+ s.PutCString(".apple_objc index present\n");
+ // TODO: Dump index contents
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h
new file mode 100644
index 000000000000..d15d61e270b3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h
@@ -0,0 +1,62 @@
+//===-- AppleDWARFIndex.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_APPLEDWARFINDEX_H
+#define LLDB_APPLEDWARFINDEX_H
+
+#include "Plugins/SymbolFile/DWARF/DWARFIndex.h"
+#include "Plugins/SymbolFile/DWARF/HashedNameToDIE.h"
+
+namespace lldb_private {
+class AppleDWARFIndex : public DWARFIndex {
+public:
+ static std::unique_ptr<AppleDWARFIndex>
+ Create(Module &module, DWARFDataExtractor apple_names,
+ DWARFDataExtractor apple_namespaces, DWARFDataExtractor apple_types,
+ DWARFDataExtractor apple_objc, DWARFDataExtractor debug_str);
+
+ AppleDWARFIndex(
+ Module &module, std::unique_ptr<DWARFMappedHash::MemoryTable> apple_names,
+ std::unique_ptr<DWARFMappedHash::MemoryTable> apple_namespaces,
+ std::unique_ptr<DWARFMappedHash::MemoryTable> apple_types,
+ std::unique_ptr<DWARFMappedHash::MemoryTable> apple_objc)
+ : DWARFIndex(module), m_apple_names_up(std::move(apple_names)),
+ m_apple_namespaces_up(std::move(apple_namespaces)),
+ m_apple_types_up(std::move(apple_types)),
+ m_apple_objc_up(std::move(apple_objc)) {}
+
+ void Preload() override {}
+
+ void GetGlobalVariables(ConstString basename, DIEArray &offsets) override;
+ void GetGlobalVariables(const RegularExpression &regex,
+ DIEArray &offsets) override;
+ void GetGlobalVariables(const DWARFUnit &cu, DIEArray &offsets) override;
+ void GetObjCMethods(ConstString class_name, DIEArray &offsets) override;
+ void GetCompleteObjCClass(ConstString class_name, bool must_be_implementation,
+ DIEArray &offsets) override;
+ void GetTypes(ConstString name, DIEArray &offsets) override;
+ void GetTypes(const DWARFDeclContext &context, DIEArray &offsets) override;
+ void GetNamespaces(ConstString name, DIEArray &offsets) override;
+ void GetFunctions(ConstString name, SymbolFileDWARF &dwarf,
+ const CompilerDeclContext &parent_decl_ctx,
+ uint32_t name_type_mask,
+ std::vector<DWARFDIE> &dies) override;
+ void GetFunctions(const RegularExpression &regex, DIEArray &offsets) override;
+
+ void ReportInvalidDIERef(const DIERef &ref, llvm::StringRef name) override;
+ void Dump(Stream &s) override;
+
+private:
+ std::unique_ptr<DWARFMappedHash::MemoryTable> m_apple_names_up;
+ std::unique_ptr<DWARFMappedHash::MemoryTable> m_apple_namespaces_up;
+ std::unique_ptr<DWARFMappedHash::MemoryTable> m_apple_types_up;
+ std::unique_ptr<DWARFMappedHash::MemoryTable> m_apple_objc_up;
+};
+} // namespace lldb_private
+
+#endif // LLDB_APPLEDWARFINDEX_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DIERef.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DIERef.cpp
new file mode 100644
index 000000000000..f7f2a5bf006b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DIERef.cpp
@@ -0,0 +1,18 @@
+//===-- DIERef.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 "DIERef.h"
+#include "llvm/Support/Format.h"
+
+void llvm::format_provider<DIERef>::format(const DIERef &ref, raw_ostream &OS,
+ StringRef Style) {
+ if (ref.dwo_num())
+ OS << format_hex_no_prefix(*ref.dwo_num(), 8) << "/";
+ OS << (ref.section() == DIERef::DebugInfo ? "INFO" : "TYPE");
+ OS << "/" << format_hex_no_prefix(ref.die_offset(), 8);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DIERef.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DIERef.h
new file mode 100644
index 000000000000..5546bb7e8b86
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DIERef.h
@@ -0,0 +1,63 @@
+//===-- DIERef.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 SymbolFileDWARF_DIERef_h_
+#define SymbolFileDWARF_DIERef_h_
+
+#include "lldb/Core/dwarf.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/FormatProviders.h"
+#include <cassert>
+#include <vector>
+
+/// Identifies a DWARF debug info entry within a given Module. It contains three
+/// "coordinates":
+/// - dwo_num: identifies the dwo file in the Module. If this field is not set,
+/// the DIERef references the main file.
+/// - section: identifies the section of the debug info entry in the given file:
+/// debug_info or debug_types.
+/// - die_offset: The offset of the debug info entry as an absolute offset from
+/// the beginning of the section specified in the section field.
+class DIERef {
+public:
+ enum Section : uint8_t { DebugInfo, DebugTypes };
+
+ DIERef(llvm::Optional<uint32_t> dwo_num, Section section,
+ dw_offset_t die_offset)
+ : m_dwo_num(dwo_num.getValueOr(0)), m_dwo_num_valid(bool(dwo_num)),
+ m_section(section), m_die_offset(die_offset) {
+ assert(this->dwo_num() == dwo_num && "Dwo number out of range?");
+ }
+
+ llvm::Optional<uint32_t> dwo_num() const {
+ if (m_dwo_num_valid)
+ return m_dwo_num;
+ return llvm::None;
+ }
+
+ Section section() const { return static_cast<Section>(m_section); }
+
+ dw_offset_t die_offset() const { return m_die_offset; }
+
+private:
+ uint32_t m_dwo_num : 30;
+ uint32_t m_dwo_num_valid : 1;
+ uint32_t m_section : 1;
+ dw_offset_t m_die_offset;
+};
+static_assert(sizeof(DIERef) == 8, "");
+
+typedef std::vector<DIERef> DIEArray;
+
+namespace llvm {
+template<> struct format_provider<DIERef> {
+ static void format(const DIERef &ref, raw_ostream &OS, StringRef Style);
+};
+} // namespace llvm
+
+#endif // SymbolFileDWARF_DIERef_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.h
new file mode 100644
index 000000000000..e7927b31b9c3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.h
@@ -0,0 +1,59 @@
+//===-- DWARFASTParser.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 SymbolFileDWARF_DWARFASTParser_h_
+#define SymbolFileDWARF_DWARFASTParser_h_
+
+#include "DWARFDefines.h"
+#include "lldb/Core/PluginInterface.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/CompilerDecl.h"
+#include "lldb/Symbol/CompilerDeclContext.h"
+
+class DWARFDIE;
+namespace lldb_private {
+class CompileUnit;
+class ExecutionContext;
+}
+class SymbolFileDWARF;
+
+class DWARFASTParser {
+public:
+ virtual ~DWARFASTParser() {}
+
+ virtual lldb::TypeSP ParseTypeFromDWARF(const lldb_private::SymbolContext &sc,
+ const DWARFDIE &die,
+ lldb_private::Log *log,
+ bool *type_is_new_ptr) = 0;
+
+ virtual lldb_private::Function *
+ ParseFunctionFromDWARF(lldb_private::CompileUnit &comp_unit,
+ const DWARFDIE &die) = 0;
+
+ virtual bool
+ CompleteTypeFromDWARF(const DWARFDIE &die, lldb_private::Type *type,
+ lldb_private::CompilerType &compiler_type) = 0;
+
+ virtual lldb_private::CompilerDecl
+ GetDeclForUIDFromDWARF(const DWARFDIE &die) = 0;
+
+ virtual lldb_private::CompilerDeclContext
+ GetDeclContextForUIDFromDWARF(const DWARFDIE &die) = 0;
+
+ virtual lldb_private::CompilerDeclContext
+ GetDeclContextContainingUIDFromDWARF(const DWARFDIE &die) = 0;
+
+ virtual std::vector<DWARFDIE>
+ GetDIEForDeclContext(lldb_private::CompilerDeclContext decl_context) = 0;
+
+ static llvm::Optional<lldb_private::SymbolFile::ArrayInfo>
+ ParseChildArrayInfo(const DWARFDIE &parent_die,
+ const lldb_private::ExecutionContext *exe_ctx = nullptr);
+};
+
+#endif // SymbolFileDWARF_DWARFASTParser_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
new file mode 100644
index 000000000000..b85ab54a10d3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -0,0 +1,3885 @@
+//===-- DWARFASTParserClang.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 <stdlib.h>
+
+#include "DWARFASTParserClang.h"
+#include "DWARFDIE.h"
+#include "DWARFDebugInfo.h"
+#include "DWARFDeclContext.h"
+#include "DWARFDefines.h"
+#include "SymbolFileDWARF.h"
+#include "SymbolFileDWARFDwo.h"
+#include "SymbolFileDWARFDebugMap.h"
+#include "UniqueDWARFASTType.h"
+
+#include "Plugins/Language/ObjC/ObjCLanguage.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Symbol/ClangASTImporter.h"
+#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
+#include "lldb/Symbol/ClangUtil.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Symbol/TypeMap.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/DeclTemplate.h"
+
+#include <map>
+#include <memory>
+#include <vector>
+
+//#define ENABLE_DEBUG_PRINTF // COMMENT OUT THIS LINE PRIOR TO CHECKIN
+
+#ifdef ENABLE_DEBUG_PRINTF
+#include <stdio.h>
+#define DEBUG_PRINTF(fmt, ...) printf(fmt, __VA_ARGS__)
+#else
+#define DEBUG_PRINTF(fmt, ...)
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+DWARFASTParserClang::DWARFASTParserClang(ClangASTContext &ast)
+ : m_ast(ast), m_die_to_decl_ctx(), m_decl_ctx_to_die() {}
+
+DWARFASTParserClang::~DWARFASTParserClang() {}
+
+static AccessType DW_ACCESS_to_AccessType(uint32_t dwarf_accessibility) {
+ switch (dwarf_accessibility) {
+ case DW_ACCESS_public:
+ return eAccessPublic;
+ case DW_ACCESS_private:
+ return eAccessPrivate;
+ case DW_ACCESS_protected:
+ return eAccessProtected;
+ default:
+ break;
+ }
+ return eAccessNone;
+}
+
+static bool DeclKindIsCXXClass(clang::Decl::Kind decl_kind) {
+ switch (decl_kind) {
+ case clang::Decl::CXXRecord:
+ case clang::Decl::ClassTemplateSpecialization:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+struct BitfieldInfo {
+ uint64_t bit_size;
+ uint64_t bit_offset;
+
+ BitfieldInfo()
+ : bit_size(LLDB_INVALID_ADDRESS), bit_offset(LLDB_INVALID_ADDRESS) {}
+
+ void Clear() {
+ bit_size = LLDB_INVALID_ADDRESS;
+ bit_offset = LLDB_INVALID_ADDRESS;
+ }
+
+ bool IsValid() const {
+ return (bit_size != LLDB_INVALID_ADDRESS) &&
+ (bit_offset != LLDB_INVALID_ADDRESS);
+ }
+
+ bool NextBitfieldOffsetIsValid(const uint64_t next_bit_offset) const {
+ if (IsValid()) {
+ // This bitfield info is valid, so any subsequent bitfields must not
+ // overlap and must be at a higher bit offset than any previous bitfield
+ // + size.
+ return (bit_size + bit_offset) <= next_bit_offset;
+ } else {
+ // If the this BitfieldInfo is not valid, then any offset isOK
+ return true;
+ }
+ }
+};
+
+ClangASTImporter &DWARFASTParserClang::GetClangASTImporter() {
+ if (!m_clang_ast_importer_up) {
+ m_clang_ast_importer_up.reset(new ClangASTImporter);
+ }
+ return *m_clang_ast_importer_up;
+}
+
+/// Detect a forward declaration that is nested in a DW_TAG_module.
+static bool IsClangModuleFwdDecl(const DWARFDIE &Die) {
+ if (!Die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0))
+ return false;
+ auto Parent = Die.GetParent();
+ while (Parent.IsValid()) {
+ if (Parent.Tag() == DW_TAG_module)
+ return true;
+ Parent = Parent.GetParent();
+ }
+ return false;
+}
+
+TypeSP DWARFASTParserClang::ParseTypeFromDWO(const DWARFDIE &die, Log *log) {
+ ModuleSP dwo_module_sp = die.GetContainingDWOModule();
+ if (!dwo_module_sp)
+ return TypeSP();
+
+ // If this type comes from a Clang module, look in the DWARF section
+ // of the pcm file in the module cache. Clang generates DWO skeleton
+ // units as breadcrumbs to find them.
+ std::vector<CompilerContext> decl_context;
+ die.GetDeclContext(decl_context);
+ TypeMap dwo_types;
+
+ if (!dwo_module_sp->GetSymbolVendor()->FindTypes(decl_context, true,
+ dwo_types)) {
+ if (!IsClangModuleFwdDecl(die))
+ return TypeSP();
+
+ // Since this this type is defined in one of the Clang modules imported by
+ // this symbol file, search all of them.
+ auto &sym_file = die.GetCU()->GetSymbolFileDWARF();
+ for (const auto &name_module : sym_file.getExternalTypeModules()) {
+ if (!name_module.second)
+ continue;
+ SymbolVendor *sym_vendor = name_module.second->GetSymbolVendor();
+ if (sym_vendor->FindTypes(decl_context, true, dwo_types))
+ break;
+ }
+ }
+
+ if (dwo_types.GetSize() != 1)
+ return TypeSP();
+
+ // We found a real definition for this type in the Clang module, so lets use
+ // it and cache the fact that we found a complete type for this die.
+ TypeSP dwo_type_sp = dwo_types.GetTypeAtIndex(0);
+ if (!dwo_type_sp)
+ return TypeSP();
+
+ lldb_private::CompilerType dwo_type = dwo_type_sp->GetForwardCompilerType();
+
+ lldb_private::CompilerType type =
+ GetClangASTImporter().CopyType(m_ast, dwo_type);
+
+ if (!type)
+ return TypeSP();
+
+ SymbolFileDWARF *dwarf = die.GetDWARF();
+ TypeSP type_sp(new Type(
+ die.GetID(), dwarf, dwo_type_sp->GetName(), dwo_type_sp->GetByteSize(),
+ nullptr, LLDB_INVALID_UID, Type::eEncodingInvalid,
+ &dwo_type_sp->GetDeclaration(), type, Type::eResolveStateForward));
+
+ dwarf->GetTypeList()->Insert(type_sp);
+ dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get();
+ clang::TagDecl *tag_decl = ClangASTContext::GetAsTagDecl(type);
+ if (tag_decl)
+ LinkDeclContextToDIE(tag_decl, die);
+ else {
+ clang::DeclContext *defn_decl_ctx = GetCachedClangDeclContextForDIE(die);
+ if (defn_decl_ctx)
+ LinkDeclContextToDIE(defn_decl_ctx, die);
+ }
+
+ return type_sp;
+}
+
+static void CompleteExternalTagDeclType(ClangASTImporter &ast_importer,
+ clang::DeclContext *decl_ctx,
+ DWARFDIE die,
+ const char *type_name_cstr) {
+ auto *tag_decl_ctx = clang::dyn_cast<clang::TagDecl>(decl_ctx);
+ if (!tag_decl_ctx)
+ return;
+
+ // If this type was not imported from an external AST, there's nothing to do.
+ CompilerType type = ClangASTContext::GetTypeForDecl(tag_decl_ctx);
+ if (!type || !ast_importer.CanImport(type))
+ return;
+
+ auto qual_type = ClangUtil::GetQualType(type);
+ if (!ast_importer.RequireCompleteType(qual_type)) {
+ die.GetDWARF()->GetObjectFile()->GetModule()->ReportError(
+ "Unable to complete the Decl context for DIE '%s' at offset "
+ "0x%8.8x.\nPlease file a bug report.",
+ type_name_cstr ? type_name_cstr : "", die.GetOffset());
+ // We need to make the type look complete otherwise, we might crash in
+ // Clang when adding children.
+ if (ClangASTContext::StartTagDeclarationDefinition(type))
+ ClangASTContext::CompleteTagDeclarationDefinition(type);
+ }
+}
+
+namespace {
+/// Parsed form of all attributes that are relevant for type reconstruction.
+/// Some attributes are relevant for all kinds of types (declaration), while
+/// others are only meaningful to a specific type (is_virtual)
+struct ParsedTypeAttributes {
+ explicit ParsedTypeAttributes(const DWARFDIE &die);
+
+ AccessType accessibility = eAccessNone;
+ bool is_artificial = false;
+ bool is_complete_objc_class = false;
+ bool is_explicit = false;
+ bool is_forward_declaration = false;
+ bool is_inline = false;
+ bool is_scoped_enum = false;
+ bool is_vector = false;
+ bool is_virtual = false;
+ clang::StorageClass storage = clang::SC_None;
+ const char *mangled_name = nullptr;
+ ConstString name;
+ Declaration decl;
+ DWARFDIE object_pointer;
+ DWARFFormValue abstract_origin;
+ DWARFFormValue containing_type;
+ DWARFFormValue signature;
+ DWARFFormValue specification;
+ DWARFFormValue type;
+ LanguageType class_language = eLanguageTypeUnknown;
+ llvm::Optional<uint64_t> byte_size;
+ size_t calling_convention = llvm::dwarf::DW_CC_normal;
+ uint32_t bit_stride = 0;
+ uint32_t byte_stride = 0;
+ uint32_t encoding = 0;
+};
+} // namespace
+
+ParsedTypeAttributes::ParsedTypeAttributes(const DWARFDIE &die) {
+ DWARFAttributes attributes;
+ size_t num_attributes = die.GetAttributes(attributes);
+ for (size_t i = 0; i < num_attributes; ++i) {
+ dw_attr_t attr = attributes.AttributeAtIndex(i);
+ DWARFFormValue form_value;
+ if (!attributes.ExtractFormValueAtIndex(i, form_value))
+ continue;
+ switch (attr) {
+ case DW_AT_abstract_origin:
+ abstract_origin = form_value;
+ break;
+
+ case DW_AT_accessibility:
+ accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned());
+ break;
+
+ case DW_AT_artificial:
+ is_artificial = form_value.Boolean();
+ break;
+
+ case DW_AT_bit_stride:
+ bit_stride = form_value.Unsigned();
+ break;
+
+ case DW_AT_byte_size:
+ byte_size = form_value.Unsigned();
+ break;
+
+ case DW_AT_byte_stride:
+ byte_stride = form_value.Unsigned();
+ break;
+
+ case DW_AT_calling_convention:
+ calling_convention = form_value.Unsigned();
+ break;
+
+ case DW_AT_containing_type:
+ containing_type = form_value;
+ break;
+
+ case DW_AT_decl_file:
+ decl.SetFile(die.GetCU()->GetFile(form_value.Unsigned()));
+ break;
+ case DW_AT_decl_line:
+ decl.SetLine(form_value.Unsigned());
+ break;
+ case DW_AT_decl_column:
+ decl.SetColumn(form_value.Unsigned());
+ break;
+
+ case DW_AT_declaration:
+ is_forward_declaration = form_value.Boolean();
+ break;
+
+ case DW_AT_encoding:
+ encoding = form_value.Unsigned();
+ break;
+
+ case DW_AT_enum_class:
+ is_scoped_enum = form_value.Boolean();
+ break;
+
+ case DW_AT_explicit:
+ is_explicit = form_value.Boolean();
+ break;
+
+ case DW_AT_external:
+ if (form_value.Unsigned())
+ storage = clang::SC_Extern;
+ break;
+
+ case DW_AT_inline:
+ is_inline = form_value.Boolean();
+ break;
+
+ case DW_AT_linkage_name:
+ case DW_AT_MIPS_linkage_name:
+ mangled_name = form_value.AsCString();
+ break;
+
+ case DW_AT_name:
+ name.SetCString(form_value.AsCString());
+ break;
+
+ case DW_AT_object_pointer:
+ object_pointer = form_value.Reference();
+ break;
+
+ case DW_AT_signature:
+ signature = form_value;
+ break;
+
+ case DW_AT_specification:
+ specification = form_value;
+ break;
+
+ case DW_AT_type:
+ type = form_value;
+ break;
+
+ case DW_AT_virtuality:
+ is_virtual = form_value.Boolean();
+ break;
+
+ case DW_AT_APPLE_objc_complete_type:
+ is_complete_objc_class = form_value.Signed();
+ break;
+
+ case DW_AT_APPLE_runtime_class:
+ class_language = (LanguageType)form_value.Signed();
+ break;
+
+ case DW_AT_GNU_vector:
+ is_vector = form_value.Boolean();
+ break;
+ }
+ }
+}
+
+static std::string GetUnitName(const DWARFDIE &die) {
+ if (DWARFUnit *unit = die.GetCU())
+ return unit->GetAbsolutePath().GetPath();
+ return "<missing DWARF unit path>";
+}
+
+TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc,
+ const DWARFDIE &die, Log *log,
+ bool *type_is_new_ptr) {
+ if (type_is_new_ptr)
+ *type_is_new_ptr = false;
+
+ if (!die)
+ return nullptr;
+ SymbolFileDWARF *dwarf = die.GetDWARF();
+ if (log) {
+ DWARFDIE context_die;
+ clang::DeclContext *context =
+ GetClangDeclContextContainingDIE(die, &context_die);
+
+ dwarf->GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF::ParseType (die = 0x%8.8x, decl_ctx = %p (die "
+ "0x%8.8x)) %s name = '%s')",
+ die.GetOffset(), static_cast<void *>(context), context_die.GetOffset(),
+ die.GetTagAsCString(), die.GetName());
+ }
+
+
+ Type *type_ptr = dwarf->GetDIEToType().lookup(die.GetDIE());
+ if (type_ptr == DIE_IS_BEING_PARSED)
+ return nullptr;
+ if (type_ptr)
+ return type_ptr->shared_from_this();
+ // Set a bit that lets us know that we are currently parsing this
+ dwarf->GetDIEToType()[die.GetDIE()] = DIE_IS_BEING_PARSED;
+
+ ParsedTypeAttributes attrs(die);
+
+ if (DWARFDIE signature_die = attrs.signature.Reference()) {
+ if (TypeSP type_sp =
+ ParseTypeFromDWARF(sc, signature_die, log, type_is_new_ptr)) {
+ dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get();
+ if (clang::DeclContext *decl_ctx =
+ GetCachedClangDeclContextForDIE(signature_die))
+ LinkDeclContextToDIE(decl_ctx, die);
+ return type_sp;
+ }
+ return nullptr;
+ }
+
+ TypeList *type_list = dwarf->GetTypeList();
+ if (type_is_new_ptr)
+ *type_is_new_ptr = true;
+
+ const dw_tag_t tag = die.Tag();
+
+ Type::ResolveState resolve_state = Type::eResolveStateUnresolved;
+
+ Type::EncodingDataType encoding_data_type = Type::eEncodingIsUID;
+ CompilerType clang_type;
+
+ TypeSP type_sp;
+ LanguageType cu_language = die.GetLanguage();
+ switch (tag) {
+ case DW_TAG_typedef:
+ case DW_TAG_base_type:
+ case DW_TAG_pointer_type:
+ case DW_TAG_reference_type:
+ case DW_TAG_rvalue_reference_type:
+ case DW_TAG_const_type:
+ case DW_TAG_restrict_type:
+ case DW_TAG_volatile_type:
+ case DW_TAG_unspecified_type: {
+ if (tag == DW_TAG_typedef && attrs.type.IsValid()) {
+ // Try to parse a typedef from the DWO file first as modules can
+ // contain typedef'ed structures that have no names like:
+ //
+ // typedef struct { int a; } Foo;
+ //
+ // In this case we will have a structure with no name and a typedef
+ // named "Foo" that points to this unnamed structure. The name in the
+ // typedef is the only identifier for the struct, so always try to
+ // get typedefs from DWO files if possible.
+ //
+ // The type_sp returned will be empty if the typedef doesn't exist in
+ // a DWO file, so it is cheap to call this function just to check.
+ //
+ // If we don't do this we end up creating a TypeSP that says this is
+ // a typedef to type 0x123 (the DW_AT_type value would be 0x123 in
+ // the DW_TAG_typedef), and this is the unnamed structure type. We
+ // will have a hard time tracking down an unnammed structure type in
+ // the module DWO file, so we make sure we don't get into this
+ // situation by always resolving typedefs from the DWO file.
+ const DWARFDIE encoding_die = attrs.type.Reference();
+
+ // First make sure that the die that this is typedef'ed to _is_ just
+ // a declaration (DW_AT_declaration == 1), not a full definition
+ // since template types can't be represented in modules since only
+ // concrete instances of templates are ever emitted and modules won't
+ // contain those
+ if (encoding_die &&
+ encoding_die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0) == 1) {
+ type_sp = ParseTypeFromDWO(die, log);
+ if (type_sp)
+ return type_sp;
+ }
+ }
+
+ DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\") type => 0x%8.8lx\n",
+ die.GetID(), DW_TAG_value_to_name(tag), type_name_cstr,
+ encoding_uid.Reference());
+
+ switch (tag) {
+ default:
+ break;
+
+ case DW_TAG_unspecified_type:
+ if (attrs.name == "nullptr_t" || attrs.name == "decltype(nullptr)") {
+ resolve_state = Type::eResolveStateFull;
+ clang_type = m_ast.GetBasicType(eBasicTypeNullPtr);
+ break;
+ }
+ // Fall through to base type below in case we can handle the type
+ // there...
+ LLVM_FALLTHROUGH;
+
+ case DW_TAG_base_type:
+ resolve_state = Type::eResolveStateFull;
+ clang_type = m_ast.GetBuiltinTypeForDWARFEncodingAndBitSize(
+ attrs.name.GetCString(), attrs.encoding,
+ attrs.byte_size.getValueOr(0) * 8);
+ break;
+
+ case DW_TAG_pointer_type:
+ encoding_data_type = Type::eEncodingIsPointerUID;
+ break;
+ case DW_TAG_reference_type:
+ encoding_data_type = Type::eEncodingIsLValueReferenceUID;
+ break;
+ case DW_TAG_rvalue_reference_type:
+ encoding_data_type = Type::eEncodingIsRValueReferenceUID;
+ break;
+ case DW_TAG_typedef:
+ encoding_data_type = Type::eEncodingIsTypedefUID;
+ break;
+ case DW_TAG_const_type:
+ encoding_data_type = Type::eEncodingIsConstUID;
+ break;
+ case DW_TAG_restrict_type:
+ encoding_data_type = Type::eEncodingIsRestrictUID;
+ break;
+ case DW_TAG_volatile_type:
+ encoding_data_type = Type::eEncodingIsVolatileUID;
+ break;
+ }
+
+ if (!clang_type && (encoding_data_type == Type::eEncodingIsPointerUID ||
+ encoding_data_type == Type::eEncodingIsTypedefUID)) {
+ if (tag == DW_TAG_pointer_type) {
+ DWARFDIE target_die = die.GetReferencedDIE(DW_AT_type);
+
+ if (target_die.GetAttributeValueAsUnsigned(DW_AT_APPLE_block, 0)) {
+ // Blocks have a __FuncPtr inside them which is a pointer to a
+ // function of the proper type.
+
+ for (DWARFDIE child_die = target_die.GetFirstChild();
+ child_die.IsValid(); child_die = child_die.GetSibling()) {
+ if (!strcmp(child_die.GetAttributeValueAsString(DW_AT_name, ""),
+ "__FuncPtr")) {
+ DWARFDIE function_pointer_type =
+ child_die.GetReferencedDIE(DW_AT_type);
+
+ if (function_pointer_type) {
+ DWARFDIE function_type =
+ function_pointer_type.GetReferencedDIE(DW_AT_type);
+
+ bool function_type_is_new_pointer;
+ TypeSP lldb_function_type_sp = ParseTypeFromDWARF(
+ sc, function_type, log, &function_type_is_new_pointer);
+
+ if (lldb_function_type_sp) {
+ clang_type = m_ast.CreateBlockPointerType(
+ lldb_function_type_sp->GetForwardCompilerType());
+ encoding_data_type = Type::eEncodingIsUID;
+ attrs.type.Clear();
+ resolve_state = Type::eResolveStateFull;
+ }
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ if (cu_language == eLanguageTypeObjC ||
+ cu_language == eLanguageTypeObjC_plus_plus) {
+ if (attrs.name) {
+ static ConstString g_objc_type_name_id("id");
+ static ConstString g_objc_type_name_Class("Class");
+ static ConstString g_objc_type_name_selector("SEL");
+
+ if (attrs.name == g_objc_type_name_id) {
+ if (log)
+ dwarf->GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF::ParseType (die = 0x%8.8x) %s '%s' "
+ "is Objective-C 'id' built-in type.",
+ die.GetOffset(), die.GetTagAsCString(), die.GetName());
+ clang_type = m_ast.GetBasicType(eBasicTypeObjCID);
+ encoding_data_type = Type::eEncodingIsUID;
+ attrs.type.Clear();
+ resolve_state = Type::eResolveStateFull;
+
+ } else if (attrs.name == g_objc_type_name_Class) {
+ if (log)
+ dwarf->GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF::ParseType (die = 0x%8.8x) %s '%s' "
+ "is Objective-C 'Class' built-in type.",
+ die.GetOffset(), die.GetTagAsCString(), die.GetName());
+ clang_type = m_ast.GetBasicType(eBasicTypeObjCClass);
+ encoding_data_type = Type::eEncodingIsUID;
+ attrs.type.Clear();
+ resolve_state = Type::eResolveStateFull;
+ } else if (attrs.name == g_objc_type_name_selector) {
+ if (log)
+ dwarf->GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF::ParseType (die = 0x%8.8x) %s '%s' "
+ "is Objective-C 'selector' built-in type.",
+ die.GetOffset(), die.GetTagAsCString(), die.GetName());
+ clang_type = m_ast.GetBasicType(eBasicTypeObjCSel);
+ encoding_data_type = Type::eEncodingIsUID;
+ attrs.type.Clear();
+ resolve_state = Type::eResolveStateFull;
+ }
+ } else if (encoding_data_type == Type::eEncodingIsPointerUID &&
+ attrs.type.IsValid()) {
+ // Clang sometimes erroneously emits id as objc_object*. In that
+ // case we fix up the type to "id".
+
+ const DWARFDIE encoding_die = attrs.type.Reference();
+
+ if (encoding_die && encoding_die.Tag() == DW_TAG_structure_type) {
+ if (const char *struct_name = encoding_die.GetName()) {
+ if (!strcmp(struct_name, "objc_object")) {
+ if (log)
+ dwarf->GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF::ParseType (die = 0x%8.8x) %s "
+ "'%s' is 'objc_object*', which we overrode to "
+ "'id'.",
+ die.GetOffset(), die.GetTagAsCString(), die.GetName());
+ clang_type = m_ast.GetBasicType(eBasicTypeObjCID);
+ encoding_data_type = Type::eEncodingIsUID;
+ attrs.type.Clear();
+ resolve_state = Type::eResolveStateFull;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ type_sp = std::make_shared<Type>(
+ die.GetID(), dwarf, attrs.name, attrs.byte_size, nullptr,
+ dwarf->GetUID(attrs.type.Reference()), encoding_data_type, &attrs.decl,
+ clang_type, resolve_state);
+
+ dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get();
+ } break;
+
+ case DW_TAG_structure_type:
+ case DW_TAG_union_type:
+ case DW_TAG_class_type: {
+ // UniqueDWARFASTType is large, so don't create a local variables on
+ // the stack, put it on the heap. This function is often called
+ // recursively and clang isn't good and sharing the stack space for
+ // variables in different blocks.
+ std::unique_ptr<UniqueDWARFASTType> unique_ast_entry_up(
+ new UniqueDWARFASTType());
+
+ ConstString unique_typename(attrs.name);
+ Declaration unique_decl(attrs.decl);
+
+ if (attrs.name) {
+ if (Language::LanguageIsCPlusPlus(cu_language)) {
+ // For C++, we rely solely upon the one definition rule that says
+ // only one thing can exist at a given decl context. We ignore the
+ // file and line that things are declared on.
+ std::string qualified_name;
+ if (die.GetQualifiedName(qualified_name))
+ unique_typename = ConstString(qualified_name);
+ unique_decl.Clear();
+ }
+
+ if (dwarf->GetUniqueDWARFASTTypeMap().Find(
+ unique_typename, die, unique_decl, attrs.byte_size.getValueOr(-1),
+ *unique_ast_entry_up)) {
+ type_sp = unique_ast_entry_up->m_type_sp;
+ if (type_sp) {
+ dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get();
+ return type_sp;
+ }
+ }
+ }
+
+ DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(),
+ DW_TAG_value_to_name(tag), type_name_cstr);
+
+ int tag_decl_kind = -1;
+ AccessType default_accessibility = eAccessNone;
+ if (tag == DW_TAG_structure_type) {
+ tag_decl_kind = clang::TTK_Struct;
+ default_accessibility = eAccessPublic;
+ } else if (tag == DW_TAG_union_type) {
+ tag_decl_kind = clang::TTK_Union;
+ default_accessibility = eAccessPublic;
+ } else if (tag == DW_TAG_class_type) {
+ tag_decl_kind = clang::TTK_Class;
+ default_accessibility = eAccessPrivate;
+ }
+
+ if (attrs.byte_size && *attrs.byte_size == 0 && attrs.name &&
+ !die.HasChildren() && cu_language == eLanguageTypeObjC) {
+ // Work around an issue with clang at the moment where forward
+ // declarations for objective C classes are emitted as:
+ // DW_TAG_structure_type [2]
+ // DW_AT_name( "ForwardObjcClass" )
+ // DW_AT_byte_size( 0x00 )
+ // DW_AT_decl_file( "..." )
+ // DW_AT_decl_line( 1 )
+ //
+ // Note that there is no DW_AT_declaration and there are no children,
+ // and the byte size is zero.
+ attrs.is_forward_declaration = true;
+ }
+
+ if (attrs.class_language == eLanguageTypeObjC ||
+ attrs.class_language == eLanguageTypeObjC_plus_plus) {
+ if (!attrs.is_complete_objc_class &&
+ die.Supports_DW_AT_APPLE_objc_complete_type()) {
+ // We have a valid eSymbolTypeObjCClass class symbol whose name
+ // matches the current objective C class that we are trying to find
+ // and this DIE isn't the complete definition (we checked
+ // is_complete_objc_class above and know it is false), so the real
+ // definition is in here somewhere
+ type_sp =
+ dwarf->FindCompleteObjCDefinitionTypeForDIE(die, attrs.name, true);
+
+ if (!type_sp) {
+ SymbolFileDWARFDebugMap *debug_map_symfile =
+ dwarf->GetDebugMapSymfile();
+ if (debug_map_symfile) {
+ // We weren't able to find a full declaration in this DWARF,
+ // see if we have a declaration anywhere else...
+ type_sp = debug_map_symfile->FindCompleteObjCDefinitionTypeForDIE(
+ die, attrs.name, true);
+ }
+ }
+
+ if (type_sp) {
+ if (log) {
+ dwarf->GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" is an "
+ "incomplete objc type, complete type is 0x%8.8" PRIx64,
+ static_cast<void *>(this), die.GetOffset(),
+ DW_TAG_value_to_name(tag), attrs.name.GetCString(),
+ type_sp->GetID());
+ }
+
+ // We found a real definition for this type elsewhere so lets use
+ // it and cache the fact that we found a complete type for this
+ // die
+ dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get();
+ return type_sp;
+ }
+ }
+ }
+
+ if (attrs.is_forward_declaration) {
+ // We have a forward declaration to a type and we need to try and
+ // find a full declaration. We look in the current type index just in
+ // case we have a forward declaration followed by an actual
+ // declarations in the DWARF. If this fails, we need to look
+ // elsewhere...
+ if (log) {
+ dwarf->GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" is a "
+ "forward declaration, trying to find complete type",
+ static_cast<void *>(this), die.GetOffset(),
+ DW_TAG_value_to_name(tag), attrs.name.GetCString());
+ }
+
+ // See if the type comes from a DWO module and if so, track down that
+ // type.
+ type_sp = ParseTypeFromDWO(die, log);
+ if (type_sp)
+ return type_sp;
+
+ DWARFDeclContext die_decl_ctx;
+ die.GetDWARFDeclContext(die_decl_ctx);
+
+ // type_sp = FindDefinitionTypeForDIE (dwarf_cu, die,
+ // type_name_const_str);
+ type_sp = dwarf->FindDefinitionTypeForDWARFDeclContext(die_decl_ctx);
+
+ if (!type_sp) {
+ SymbolFileDWARFDebugMap *debug_map_symfile =
+ dwarf->GetDebugMapSymfile();
+ if (debug_map_symfile) {
+ // We weren't able to find a full declaration in this DWARF, see
+ // if we have a declaration anywhere else...
+ type_sp = debug_map_symfile->FindDefinitionTypeForDWARFDeclContext(
+ die_decl_ctx);
+ }
+ }
+
+ if (type_sp) {
+ if (log) {
+ dwarf->GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" is a "
+ "forward declaration, complete type is 0x%8.8" PRIx64,
+ static_cast<void *>(this), die.GetOffset(),
+ DW_TAG_value_to_name(tag), attrs.name.GetCString(),
+ type_sp->GetID());
+ }
+
+ // We found a real definition for this type elsewhere so lets use
+ // it and cache the fact that we found a complete type for this die
+ dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get();
+ clang::DeclContext *defn_decl_ctx =
+ GetCachedClangDeclContextForDIE(dwarf->GetDIE(type_sp->GetID()));
+ if (defn_decl_ctx)
+ LinkDeclContextToDIE(defn_decl_ctx, die);
+ return type_sp;
+ }
+ }
+ assert(tag_decl_kind != -1);
+ bool clang_type_was_created = false;
+ clang_type.SetCompilerType(
+ &m_ast, dwarf->GetForwardDeclDieToClangType().lookup(die.GetDIE()));
+ if (!clang_type) {
+ clang::DeclContext *decl_ctx =
+ GetClangDeclContextContainingDIE(die, nullptr);
+
+ // If your decl context is a record that was imported from another
+ // AST context (in the gmodules case), we need to make sure the type
+ // backing the Decl is complete before adding children to it. This is
+ // not an issue in the non-gmodules case because the debug info will
+ // always contain a full definition of parent types in that case.
+ CompleteExternalTagDeclType(GetClangASTImporter(), decl_ctx, die,
+ attrs.name.GetCString());
+
+ if (attrs.accessibility == eAccessNone && decl_ctx) {
+ // Check the decl context that contains this class/struct/union. If
+ // it is a class we must give it an accessibility.
+ const clang::Decl::Kind containing_decl_kind = decl_ctx->getDeclKind();
+ if (DeclKindIsCXXClass(containing_decl_kind))
+ attrs.accessibility = default_accessibility;
+ }
+
+ ClangASTMetadata metadata;
+ metadata.SetUserID(die.GetID());
+ metadata.SetIsDynamicCXXType(dwarf->ClassOrStructIsVirtual(die));
+
+ if (attrs.name.GetStringRef().contains('<')) {
+ ClangASTContext::TemplateParameterInfos template_param_infos;
+ if (ParseTemplateParameterInfos(die, template_param_infos)) {
+ clang::ClassTemplateDecl *class_template_decl =
+ m_ast.ParseClassTemplateDecl(decl_ctx, attrs.accessibility,
+ attrs.name.GetCString(),
+ tag_decl_kind, template_param_infos);
+ if (!class_template_decl) {
+ if (log) {
+ dwarf->GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" "
+ "clang::ClassTemplateDecl failed to return a decl.",
+ static_cast<void *>(this), die.GetOffset(),
+ DW_TAG_value_to_name(tag), attrs.name.GetCString());
+ }
+ return TypeSP();
+ }
+
+ clang::ClassTemplateSpecializationDecl *class_specialization_decl =
+ m_ast.CreateClassTemplateSpecializationDecl(
+ decl_ctx, class_template_decl, tag_decl_kind,
+ template_param_infos);
+ clang_type = m_ast.CreateClassTemplateSpecializationType(
+ class_specialization_decl);
+ clang_type_was_created = true;
+
+ m_ast.SetMetadata(class_template_decl, metadata);
+ m_ast.SetMetadata(class_specialization_decl, metadata);
+ }
+ }
+
+ if (!clang_type_was_created) {
+ clang_type_was_created = true;
+ clang_type = m_ast.CreateRecordType(
+ decl_ctx, attrs.accessibility, attrs.name.GetCString(),
+ tag_decl_kind, attrs.class_language, &metadata);
+ }
+ }
+
+ // Store a forward declaration to this class type in case any
+ // parameters in any class methods need it for the clang types for
+ // function prototypes.
+ LinkDeclContextToDIE(m_ast.GetDeclContextForType(clang_type), die);
+ type_sp = std::make_shared<Type>(die.GetID(), dwarf, attrs.name,
+ attrs.byte_size, nullptr, LLDB_INVALID_UID,
+ Type::eEncodingIsUID, &attrs.decl, clang_type,
+ Type::eResolveStateForward);
+
+ type_sp->SetIsCompleteObjCClass(attrs.is_complete_objc_class);
+
+ // Add our type to the unique type map so we don't end up creating many
+ // copies of the same type over and over in the ASTContext for our
+ // module
+ unique_ast_entry_up->m_type_sp = type_sp;
+ unique_ast_entry_up->m_die = die;
+ unique_ast_entry_up->m_declaration = unique_decl;
+ unique_ast_entry_up->m_byte_size = attrs.byte_size.getValueOr(0);
+ dwarf->GetUniqueDWARFASTTypeMap().Insert(unique_typename,
+ *unique_ast_entry_up);
+
+ if (attrs.is_forward_declaration && die.HasChildren()) {
+ // Check to see if the DIE actually has a definition, some version of
+ // GCC will
+ // emit DIEs with DW_AT_declaration set to true, but yet still have
+ // subprogram, members, or inheritance, so we can't trust it
+ DWARFDIE child_die = die.GetFirstChild();
+ while (child_die) {
+ switch (child_die.Tag()) {
+ case DW_TAG_inheritance:
+ case DW_TAG_subprogram:
+ case DW_TAG_member:
+ case DW_TAG_APPLE_property:
+ case DW_TAG_class_type:
+ case DW_TAG_structure_type:
+ case DW_TAG_enumeration_type:
+ case DW_TAG_typedef:
+ case DW_TAG_union_type:
+ child_die.Clear();
+ attrs.is_forward_declaration = false;
+ break;
+ default:
+ child_die = child_die.GetSibling();
+ break;
+ }
+ }
+ }
+
+ if (!attrs.is_forward_declaration) {
+ // Always start the definition for a class type so that if the class
+ // has child classes or types that require the class to be created
+ // for use as their decl contexts the class will be ready to accept
+ // these child definitions.
+ if (!die.HasChildren()) {
+ // No children for this struct/union/class, lets finish it
+ if (ClangASTContext::StartTagDeclarationDefinition(clang_type)) {
+ ClangASTContext::CompleteTagDeclarationDefinition(clang_type);
+ } else {
+ dwarf->GetObjectFile()->GetModule()->ReportError(
+ "DWARF DIE at 0x%8.8x named \"%s\" was not able to start its "
+ "definition.\nPlease file a bug and attach the file at the "
+ "start of this error message",
+ die.GetOffset(), attrs.name.GetCString());
+ }
+
+ if (tag == DW_TAG_structure_type) // this only applies in C
+ {
+ clang::RecordDecl *record_decl =
+ ClangASTContext::GetAsRecordDecl(clang_type);
+
+ if (record_decl) {
+ GetClangASTImporter().InsertRecordDecl(
+ record_decl, ClangASTImporter::LayoutInfo());
+ }
+ }
+ } else if (clang_type_was_created) {
+ // Start the definition if the class is not objective C since the
+ // underlying decls respond to isCompleteDefinition(). Objective
+ // C decls don't respond to isCompleteDefinition() so we can't
+ // start the declaration definition right away. For C++
+ // class/union/structs we want to start the definition in case the
+ // class is needed as the declaration context for a contained class
+ // or type without the need to complete that type..
+
+ if (attrs.class_language != eLanguageTypeObjC &&
+ attrs.class_language != eLanguageTypeObjC_plus_plus)
+ ClangASTContext::StartTagDeclarationDefinition(clang_type);
+
+ // Leave this as a forward declaration until we need to know the
+ // details of the type. lldb_private::Type will automatically call
+ // the SymbolFile virtual function
+ // "SymbolFileDWARF::CompleteType(Type *)" When the definition
+ // needs to be defined.
+ assert(!dwarf->GetForwardDeclClangTypeToDie().count(
+ ClangUtil::RemoveFastQualifiers(clang_type)
+ .GetOpaqueQualType()) &&
+ "Type already in the forward declaration map!");
+ // Can't assume m_ast.GetSymbolFile() is actually a
+ // SymbolFileDWARF, it can be a SymbolFileDWARFDebugMap for Apple
+ // binaries.
+ dwarf->GetForwardDeclDieToClangType()[die.GetDIE()] =
+ clang_type.GetOpaqueQualType();
+ dwarf->GetForwardDeclClangTypeToDie()
+ [ClangUtil::RemoveFastQualifiers(clang_type).GetOpaqueQualType()] =
+ die.GetID();
+ m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), true);
+ }
+ }
+
+ // If we made a clang type, set the trivial abi if applicable: We only
+ // do this for pass by value - which implies the Trivial ABI. There
+ // isn't a way to assert that something that would normally be pass by
+ // value is pass by reference, so we ignore that attribute if set.
+ if (attrs.calling_convention == llvm::dwarf::DW_CC_pass_by_value) {
+ clang::CXXRecordDecl *record_decl =
+ m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType());
+ if (record_decl) {
+ record_decl->setHasTrivialSpecialMemberForCall();
+ }
+ }
+
+ if (attrs.calling_convention == llvm::dwarf::DW_CC_pass_by_reference) {
+ clang::CXXRecordDecl *record_decl =
+ m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType());
+ if (record_decl)
+ record_decl->setArgPassingRestrictions(
+ clang::RecordDecl::APK_CannotPassInRegs);
+ }
+
+ } break;
+
+ case DW_TAG_enumeration_type: {
+ if (attrs.is_forward_declaration) {
+ type_sp = ParseTypeFromDWO(die, log);
+ if (type_sp)
+ return type_sp;
+
+ DWARFDeclContext die_decl_ctx;
+ die.GetDWARFDeclContext(die_decl_ctx);
+
+ type_sp = dwarf->FindDefinitionTypeForDWARFDeclContext(die_decl_ctx);
+
+ if (!type_sp) {
+ SymbolFileDWARFDebugMap *debug_map_symfile =
+ dwarf->GetDebugMapSymfile();
+ if (debug_map_symfile) {
+ // We weren't able to find a full declaration in this DWARF,
+ // see if we have a declaration anywhere else...
+ type_sp = debug_map_symfile->FindDefinitionTypeForDWARFDeclContext(
+ die_decl_ctx);
+ }
+ }
+
+ if (type_sp) {
+ if (log) {
+ dwarf->GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" is a "
+ "forward declaration, complete type is 0x%8.8" PRIx64,
+ static_cast<void *>(this), die.GetOffset(),
+ DW_TAG_value_to_name(tag), attrs.name.GetCString(),
+ type_sp->GetID());
+ }
+
+ // We found a real definition for this type elsewhere so lets use
+ // it and cache the fact that we found a complete type for this
+ // die
+ dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get();
+ clang::DeclContext *defn_decl_ctx =
+ GetCachedClangDeclContextForDIE(dwarf->GetDIE(type_sp->GetID()));
+ if (defn_decl_ctx)
+ LinkDeclContextToDIE(defn_decl_ctx, die);
+ return type_sp;
+ }
+ }
+ DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(),
+ DW_TAG_value_to_name(tag), type_name_cstr);
+
+ CompilerType enumerator_clang_type;
+ clang_type.SetCompilerType(
+ &m_ast, dwarf->GetForwardDeclDieToClangType().lookup(die.GetDIE()));
+ if (!clang_type) {
+ if (attrs.type.IsValid()) {
+ Type *enumerator_type =
+ dwarf->ResolveTypeUID(attrs.type.Reference(), true);
+ if (enumerator_type)
+ enumerator_clang_type = enumerator_type->GetFullCompilerType();
+ }
+
+ if (!enumerator_clang_type) {
+ if (attrs.byte_size) {
+ enumerator_clang_type =
+ m_ast.GetBuiltinTypeForDWARFEncodingAndBitSize(
+ NULL, DW_ATE_signed, *attrs.byte_size * 8);
+ } else {
+ enumerator_clang_type = m_ast.GetBasicType(eBasicTypeInt);
+ }
+ }
+
+ clang_type = m_ast.CreateEnumerationType(
+ attrs.name.GetCString(),
+ GetClangDeclContextContainingDIE(die, nullptr), attrs.decl,
+ enumerator_clang_type, attrs.is_scoped_enum);
+ } else {
+ enumerator_clang_type =
+ m_ast.GetEnumerationIntegerType(clang_type.GetOpaqueQualType());
+ }
+
+ LinkDeclContextToDIE(ClangASTContext::GetDeclContextForType(clang_type),
+ die);
+
+ type_sp = std::make_shared<Type>(
+ die.GetID(), dwarf, attrs.name, attrs.byte_size, nullptr,
+ dwarf->GetUID(attrs.type.Reference()), Type::eEncodingIsUID,
+ &attrs.decl, clang_type, Type::eResolveStateForward);
+
+ if (ClangASTContext::StartTagDeclarationDefinition(clang_type)) {
+ if (die.HasChildren()) {
+ bool is_signed = false;
+ enumerator_clang_type.IsIntegerType(is_signed);
+ ParseChildEnumerators(clang_type, is_signed,
+ type_sp->GetByteSize().getValueOr(0), die);
+ }
+ ClangASTContext::CompleteTagDeclarationDefinition(clang_type);
+ } else {
+ dwarf->GetObjectFile()->GetModule()->ReportError(
+ "DWARF DIE at 0x%8.8x named \"%s\" was not able to start its "
+ "definition.\nPlease file a bug and attach the file at the "
+ "start of this error message",
+ die.GetOffset(), attrs.name.GetCString());
+ }
+ } break;
+
+ case DW_TAG_inlined_subroutine:
+ case DW_TAG_subprogram:
+ case DW_TAG_subroutine_type: {
+ bool is_variadic = false;
+ bool is_static = false;
+ bool has_template_params = false;
+
+ unsigned type_quals = 0;
+
+ std::string object_pointer_name;
+ if (attrs.object_pointer) {
+ const char *object_pointer_name_cstr = attrs.object_pointer.GetName();
+ if (object_pointer_name_cstr)
+ object_pointer_name = object_pointer_name_cstr;
+ }
+
+ DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(),
+ DW_TAG_value_to_name(tag), type_name_cstr);
+
+ CompilerType return_clang_type;
+ Type *func_type = NULL;
+
+ if (attrs.type.IsValid())
+ func_type = dwarf->ResolveTypeUID(attrs.type.Reference(), true);
+
+ if (func_type)
+ return_clang_type = func_type->GetForwardCompilerType();
+ else
+ return_clang_type = m_ast.GetBasicType(eBasicTypeVoid);
+
+ std::vector<CompilerType> function_param_types;
+ std::vector<clang::ParmVarDecl *> function_param_decls;
+
+ // Parse the function children for the parameters
+
+ DWARFDIE decl_ctx_die;
+ clang::DeclContext *containing_decl_ctx =
+ GetClangDeclContextContainingDIE(die, &decl_ctx_die);
+ const clang::Decl::Kind containing_decl_kind =
+ containing_decl_ctx->getDeclKind();
+
+ bool is_cxx_method = DeclKindIsCXXClass(containing_decl_kind);
+ // Start off static. This will be set to false in
+ // ParseChildParameters(...) if we find a "this" parameters as the
+ // first parameter
+ if (is_cxx_method) {
+ is_static = true;
+ }
+
+ if (die.HasChildren()) {
+ bool skip_artificial = true;
+ ParseChildParameters(containing_decl_ctx, die, skip_artificial, is_static,
+ is_variadic, has_template_params,
+ function_param_types, function_param_decls,
+ type_quals);
+ }
+
+ bool ignore_containing_context = false;
+ // Check for templatized class member functions. If we had any
+ // DW_TAG_template_type_parameter or DW_TAG_template_value_parameter
+ // the DW_TAG_subprogram DIE, then we can't let this become a method in
+ // a class. Why? Because templatized functions are only emitted if one
+ // of the templatized methods is used in the current compile unit and
+ // we will end up with classes that may or may not include these member
+ // functions and this means one class won't match another class
+ // definition and it affects our ability to use a class in the clang
+ // expression parser. So for the greater good, we currently must not
+ // allow any template member functions in a class definition.
+ if (is_cxx_method && has_template_params) {
+ ignore_containing_context = true;
+ is_cxx_method = false;
+ }
+
+ // clang_type will get the function prototype clang type after this
+ // call
+ clang_type = m_ast.CreateFunctionType(
+ return_clang_type, function_param_types.data(),
+ function_param_types.size(), is_variadic, type_quals);
+
+ if (attrs.name) {
+ bool type_handled = false;
+ if (tag == DW_TAG_subprogram || tag == DW_TAG_inlined_subroutine) {
+ ObjCLanguage::MethodName objc_method(attrs.name.GetStringRef(), true);
+ if (objc_method.IsValid(true)) {
+ CompilerType class_opaque_type;
+ ConstString class_name(objc_method.GetClassName());
+ if (class_name) {
+ TypeSP complete_objc_class_type_sp(
+ dwarf->FindCompleteObjCDefinitionTypeForDIE(DWARFDIE(),
+ class_name, false));
+
+ if (complete_objc_class_type_sp) {
+ CompilerType type_clang_forward_type =
+ complete_objc_class_type_sp->GetForwardCompilerType();
+ if (ClangASTContext::IsObjCObjectOrInterfaceType(
+ type_clang_forward_type))
+ class_opaque_type = type_clang_forward_type;
+ }
+ }
+
+ if (class_opaque_type) {
+ // If accessibility isn't set to anything valid, assume public
+ // for now...
+ if (attrs.accessibility == eAccessNone)
+ attrs.accessibility = eAccessPublic;
+
+ clang::ObjCMethodDecl *objc_method_decl =
+ m_ast.AddMethodToObjCObjectType(
+ class_opaque_type, attrs.name.GetCString(), clang_type,
+ attrs.accessibility, attrs.is_artificial, is_variadic);
+ type_handled = objc_method_decl != NULL;
+ if (type_handled) {
+ LinkDeclContextToDIE(
+ ClangASTContext::GetAsDeclContext(objc_method_decl), die);
+ m_ast.SetMetadataAsUserID(objc_method_decl, die.GetID());
+ } else {
+ dwarf->GetObjectFile()->GetModule()->ReportError(
+ "{0x%8.8x}: invalid Objective-C method 0x%4.4x (%s), "
+ "please file a bug and attach the file at the start of "
+ "this error message",
+ die.GetOffset(), tag, DW_TAG_value_to_name(tag));
+ }
+ }
+ } else if (is_cxx_method) {
+ // Look at the parent of this DIE and see if is is a class or
+ // struct and see if this is actually a C++ method
+ Type *class_type = dwarf->ResolveType(decl_ctx_die);
+ if (class_type) {
+ bool alternate_defn = false;
+ if (class_type->GetID() != decl_ctx_die.GetID() ||
+ decl_ctx_die.GetContainingDWOModuleDIE()) {
+ alternate_defn = true;
+
+ // We uniqued the parent class of this function to another
+ // class so we now need to associate all dies under
+ // "decl_ctx_die" to DIEs in the DIE for "class_type"...
+ DWARFDIE class_type_die = dwarf->GetDIE(class_type->GetID());
+
+ if (class_type_die) {
+ std::vector<DWARFDIE> failures;
+
+ CopyUniqueClassMethodTypes(decl_ctx_die, class_type_die,
+ class_type, failures);
+
+ // FIXME do something with these failures that's smarter
+ // than
+ // just dropping them on the ground. Unfortunately classes
+ // don't like having stuff added to them after their
+ // definitions are complete...
+
+ type_ptr = dwarf->GetDIEToType()[die.GetDIE()];
+ if (type_ptr && type_ptr != DIE_IS_BEING_PARSED) {
+ type_sp = type_ptr->shared_from_this();
+ break;
+ }
+ }
+ }
+
+ if (attrs.specification.IsValid()) {
+ // We have a specification which we are going to base our
+ // function prototype off of, so we need this type to be
+ // completed so that the m_die_to_decl_ctx for the method in
+ // the specification has a valid clang decl context.
+ class_type->GetForwardCompilerType();
+ // If we have a specification, then the function type should
+ // have been made with the specification and not with this
+ // die.
+ DWARFDIE spec_die = attrs.specification.Reference();
+ clang::DeclContext *spec_clang_decl_ctx =
+ GetClangDeclContextForDIE(spec_die);
+ if (spec_clang_decl_ctx) {
+ LinkDeclContextToDIE(spec_clang_decl_ctx, die);
+ } else {
+ dwarf->GetObjectFile()->GetModule()->ReportWarning(
+ "0x%8.8" PRIx64 ": DW_AT_specification(0x%8.8x"
+ ") has no decl\n",
+ die.GetID(), spec_die.GetOffset());
+ }
+ type_handled = true;
+ } else if (attrs.abstract_origin.IsValid()) {
+ // We have a specification which we are going to base our
+ // function prototype off of, so we need this type to be
+ // completed so that the m_die_to_decl_ctx for the method in
+ // the abstract origin has a valid clang decl context.
+ class_type->GetForwardCompilerType();
+
+ DWARFDIE abs_die = attrs.abstract_origin.Reference();
+ clang::DeclContext *abs_clang_decl_ctx =
+ GetClangDeclContextForDIE(abs_die);
+ if (abs_clang_decl_ctx) {
+ LinkDeclContextToDIE(abs_clang_decl_ctx, die);
+ } else {
+ dwarf->GetObjectFile()->GetModule()->ReportWarning(
+ "0x%8.8" PRIx64 ": DW_AT_abstract_origin(0x%8.8x"
+ ") has no decl\n",
+ die.GetID(), abs_die.GetOffset());
+ }
+ type_handled = true;
+ } else {
+ CompilerType class_opaque_type =
+ class_type->GetForwardCompilerType();
+ if (ClangASTContext::IsCXXClassType(class_opaque_type)) {
+ if (class_opaque_type.IsBeingDefined() || alternate_defn) {
+ if (!is_static && !die.HasChildren()) {
+ // We have a C++ member function with no children (this
+ // pointer!) and clang will get mad if we try and make
+ // a function that isn't well formed in the DWARF, so
+ // we will just skip it...
+ type_handled = true;
+ } else {
+ bool add_method = true;
+ if (alternate_defn) {
+ // If an alternate definition for the class exists,
+ // then add the method only if an equivalent is not
+ // already present.
+ clang::CXXRecordDecl *record_decl =
+ m_ast.GetAsCXXRecordDecl(
+ class_opaque_type.GetOpaqueQualType());
+ if (record_decl) {
+ for (auto method_iter = record_decl->method_begin();
+ method_iter != record_decl->method_end();
+ method_iter++) {
+ clang::CXXMethodDecl *method_decl = *method_iter;
+ if (method_decl->getNameInfo().getAsString() ==
+ attrs.name.GetStringRef()) {
+ if (method_decl->getType() ==
+ ClangUtil::GetQualType(clang_type)) {
+ add_method = false;
+ LinkDeclContextToDIE(
+ ClangASTContext::GetAsDeclContext(
+ method_decl),
+ die);
+ type_handled = true;
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (add_method) {
+ llvm::PrettyStackTraceFormat stack_trace(
+ "SymbolFileDWARF::ParseType() is adding a method "
+ "%s to class %s in DIE 0x%8.8" PRIx64 " from %s",
+ attrs.name.GetCString(),
+ class_type->GetName().GetCString(), die.GetID(),
+ dwarf->GetObjectFile()
+ ->GetFileSpec()
+ .GetPath()
+ .c_str());
+
+ const bool is_attr_used = false;
+ // Neither GCC 4.2 nor clang++ currently set a valid
+ // accessibility in the DWARF for C++ methods...
+ // Default to public for now...
+ if (attrs.accessibility == eAccessNone)
+ attrs.accessibility = eAccessPublic;
+
+ clang::CXXMethodDecl *cxx_method_decl =
+ m_ast.AddMethodToCXXRecordType(
+ class_opaque_type.GetOpaqueQualType(),
+ attrs.name.GetCString(), attrs.mangled_name,
+ clang_type, attrs.accessibility, attrs.is_virtual,
+ is_static, attrs.is_inline, attrs.is_explicit,
+ is_attr_used, attrs.is_artificial);
+
+ type_handled = cxx_method_decl != NULL;
+
+ if (type_handled) {
+ LinkDeclContextToDIE(
+ ClangASTContext::GetAsDeclContext(cxx_method_decl),
+ die);
+
+ ClangASTMetadata metadata;
+ metadata.SetUserID(die.GetID());
+
+ if (!object_pointer_name.empty()) {
+ metadata.SetObjectPtrName(
+ object_pointer_name.c_str());
+ if (log)
+ log->Printf(
+ "Setting object pointer name: %s on method "
+ "object %p.\n",
+ object_pointer_name.c_str(),
+ static_cast<void *>(cxx_method_decl));
+ }
+ m_ast.SetMetadata(cxx_method_decl, metadata);
+ } else {
+ ignore_containing_context = true;
+ }
+ }
+ }
+ } else {
+ // We were asked to parse the type for a method in a
+ // class, yet the class hasn't been asked to complete
+ // itself through the clang::ExternalASTSource protocol,
+ // so we need to just have the class complete itself and
+ // do things the right way, then our
+ // DIE should then have an entry in the
+ // dwarf->GetDIEToType() map. First
+ // we need to modify the dwarf->GetDIEToType() so it
+ // doesn't think we are trying to parse this DIE
+ // anymore...
+ dwarf->GetDIEToType()[die.GetDIE()] = NULL;
+
+ // Now we get the full type to force our class type to
+ // complete itself using the clang::ExternalASTSource
+ // protocol which will parse all base classes and all
+ // methods (including the method for this DIE).
+ class_type->GetFullCompilerType();
+
+ // The type for this DIE should have been filled in the
+ // function call above
+ type_ptr = dwarf->GetDIEToType()[die.GetDIE()];
+ if (type_ptr && type_ptr != DIE_IS_BEING_PARSED) {
+ type_sp = type_ptr->shared_from_this();
+ break;
+ }
+
+ // FIXME This is fixing some even uglier behavior but we
+ // really need to
+ // uniq the methods of each class as well as the class
+ // itself. <rdar://problem/11240464>
+ type_handled = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (!type_handled) {
+ clang::FunctionDecl *function_decl = nullptr;
+ clang::FunctionDecl *template_function_decl = nullptr;
+
+ if (attrs.abstract_origin.IsValid()) {
+ DWARFDIE abs_die = attrs.abstract_origin.Reference();
+
+ if (dwarf->ResolveType(abs_die)) {
+ function_decl = llvm::dyn_cast_or_null<clang::FunctionDecl>(
+ GetCachedClangDeclContextForDIE(abs_die));
+
+ if (function_decl) {
+ LinkDeclContextToDIE(function_decl, die);
+ }
+ }
+ }
+
+ if (!function_decl) {
+ // We just have a function that isn't part of a class
+ function_decl = m_ast.CreateFunctionDeclaration(
+ ignore_containing_context ? m_ast.GetTranslationUnitDecl()
+ : containing_decl_ctx,
+ attrs.name.GetCString(), clang_type, attrs.storage,
+ attrs.is_inline);
+
+ if (has_template_params) {
+ ClangASTContext::TemplateParameterInfos template_param_infos;
+ ParseTemplateParameterInfos(die, template_param_infos);
+ template_function_decl = m_ast.CreateFunctionDeclaration(
+ ignore_containing_context ? m_ast.GetTranslationUnitDecl()
+ : containing_decl_ctx,
+ attrs.name.GetCString(), clang_type, attrs.storage,
+ attrs.is_inline);
+ clang::FunctionTemplateDecl *func_template_decl =
+ m_ast.CreateFunctionTemplateDecl(
+ containing_decl_ctx, template_function_decl,
+ attrs.name.GetCString(), template_param_infos);
+ m_ast.CreateFunctionTemplateSpecializationInfo(
+ function_decl, func_template_decl, template_param_infos);
+ }
+
+ lldbassert(function_decl);
+
+ if (function_decl) {
+ LinkDeclContextToDIE(function_decl, die);
+
+ if (!function_param_decls.empty()) {
+ m_ast.SetFunctionParameters(function_decl,
+ &function_param_decls.front(),
+ function_param_decls.size());
+ if (template_function_decl)
+ m_ast.SetFunctionParameters(template_function_decl,
+ &function_param_decls.front(),
+ function_param_decls.size());
+ }
+
+ ClangASTMetadata metadata;
+ metadata.SetUserID(die.GetID());
+
+ if (!object_pointer_name.empty()) {
+ metadata.SetObjectPtrName(object_pointer_name.c_str());
+ if (log)
+ log->Printf("Setting object pointer name: %s on function "
+ "object %p.",
+ object_pointer_name.c_str(),
+ static_cast<void *>(function_decl));
+ }
+ m_ast.SetMetadata(function_decl, metadata);
+ }
+ }
+ }
+ }
+ type_sp = std::make_shared<Type>(
+ die.GetID(), dwarf, attrs.name, llvm::None, nullptr, LLDB_INVALID_UID,
+ Type::eEncodingIsUID, &attrs.decl, clang_type, Type::eResolveStateFull);
+ assert(type_sp.get());
+ } break;
+
+ case DW_TAG_array_type: {
+ DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(),
+ DW_TAG_value_to_name(tag), type_name_cstr);
+
+ DWARFDIE type_die = attrs.type.Reference();
+ Type *element_type = dwarf->ResolveTypeUID(type_die, true);
+
+ if (element_type) {
+ auto array_info = ParseChildArrayInfo(die);
+ if (array_info) {
+ attrs.byte_stride = array_info->byte_stride;
+ attrs.bit_stride = array_info->bit_stride;
+ }
+ if (attrs.byte_stride == 0 && attrs.bit_stride == 0)
+ attrs.byte_stride = element_type->GetByteSize().getValueOr(0);
+ CompilerType array_element_type = element_type->GetForwardCompilerType();
+
+ if (ClangASTContext::IsCXXClassType(array_element_type) &&
+ !array_element_type.GetCompleteType()) {
+ ModuleSP module_sp = die.GetModule();
+ if (module_sp) {
+ if (die.GetCU()->GetProducer() == eProducerClang)
+ module_sp->ReportError(
+ "DWARF DW_TAG_array_type DIE at 0x%8.8x has a "
+ "class/union/struct element type DIE 0x%8.8x that is a "
+ "forward declaration, not a complete definition.\nTry "
+ "compiling the source file with -fstandalone-debug or "
+ "disable -gmodules",
+ die.GetOffset(), type_die.GetOffset());
+ else
+ module_sp->ReportError(
+ "DWARF DW_TAG_array_type DIE at 0x%8.8x has a "
+ "class/union/struct element type DIE 0x%8.8x that is a "
+ "forward declaration, not a complete definition.\nPlease "
+ "file a bug against the compiler and include the "
+ "preprocessed output for %s",
+ die.GetOffset(), type_die.GetOffset(),
+ GetUnitName(die).c_str());
+ }
+
+ // We have no choice other than to pretend that the element class
+ // type is complete. If we don't do this, clang will crash when
+ // trying to layout the class. Since we provide layout
+ // assistance, all ivars in this class and other classes will be
+ // fine, this is the best we can do short of crashing.
+ if (ClangASTContext::StartTagDeclarationDefinition(
+ array_element_type)) {
+ ClangASTContext::CompleteTagDeclarationDefinition(array_element_type);
+ } else {
+ module_sp->ReportError("DWARF DIE at 0x%8.8x was not able to "
+ "start its definition.\nPlease file a "
+ "bug and attach the file at the start "
+ "of this error message",
+ type_die.GetOffset());
+ }
+ }
+
+ uint64_t array_element_bit_stride =
+ attrs.byte_stride * 8 + attrs.bit_stride;
+ if (array_info && array_info->element_orders.size() > 0) {
+ uint64_t num_elements = 0;
+ auto end = array_info->element_orders.rend();
+ for (auto pos = array_info->element_orders.rbegin(); pos != end;
+ ++pos) {
+ num_elements = *pos;
+ clang_type = m_ast.CreateArrayType(array_element_type, num_elements,
+ attrs.is_vector);
+ array_element_type = clang_type;
+ array_element_bit_stride =
+ num_elements ? array_element_bit_stride * num_elements
+ : array_element_bit_stride;
+ }
+ } else {
+ clang_type = m_ast.CreateArrayType(array_element_type, 0, attrs.is_vector);
+ }
+ ConstString empty_name;
+ type_sp = std::make_shared<Type>(
+ die.GetID(), dwarf, empty_name, array_element_bit_stride / 8, nullptr,
+ dwarf->GetUID(type_die), Type::eEncodingIsUID, &attrs.decl,
+ clang_type, Type::eResolveStateFull);
+ type_sp->SetEncodingType(element_type);
+ m_ast.SetMetadataAsUserID(clang_type.GetOpaqueQualType(), die.GetID());
+ }
+ } break;
+
+ case DW_TAG_ptr_to_member_type: {
+ Type *pointee_type = dwarf->ResolveTypeUID(attrs.type.Reference(), true);
+ Type *class_type =
+ dwarf->ResolveTypeUID(attrs.containing_type.Reference(), true);
+
+ CompilerType pointee_clang_type = pointee_type->GetForwardCompilerType();
+ CompilerType class_clang_type = class_type->GetLayoutCompilerType();
+
+ clang_type = ClangASTContext::CreateMemberPointerType(class_clang_type,
+ pointee_clang_type);
+
+ if (llvm::Optional<uint64_t> clang_type_size =
+ clang_type.GetByteSize(nullptr)) {
+ type_sp = std::make_shared<Type>(
+ die.GetID(), dwarf, attrs.name, *clang_type_size, nullptr,
+ LLDB_INVALID_UID, Type::eEncodingIsUID, nullptr, clang_type,
+ Type::eResolveStateForward);
+ }
+
+ break;
+ }
+ default:
+ dwarf->GetObjectFile()->GetModule()->ReportError(
+ "{0x%8.8x}: unhandled type tag 0x%4.4x (%s), please file a bug and "
+ "attach the file at the start of this error message",
+ die.GetOffset(), tag, DW_TAG_value_to_name(tag));
+ break;
+ }
+
+ if (type_sp.get()) {
+ DWARFDIE sc_parent_die = SymbolFileDWARF::GetParentSymbolContextDIE(die);
+ dw_tag_t sc_parent_tag = sc_parent_die.Tag();
+
+ SymbolContextScope *symbol_context_scope = NULL;
+ if (sc_parent_tag == DW_TAG_compile_unit ||
+ sc_parent_tag == DW_TAG_partial_unit) {
+ symbol_context_scope = sc.comp_unit;
+ } else if (sc.function != NULL && sc_parent_die) {
+ symbol_context_scope =
+ sc.function->GetBlock(true).FindBlockByID(sc_parent_die.GetID());
+ if (symbol_context_scope == NULL)
+ symbol_context_scope = sc.function;
+ } else
+ symbol_context_scope = sc.module_sp.get();
+
+ if (symbol_context_scope != NULL) {
+ type_sp->SetSymbolContextScope(symbol_context_scope);
+ }
+
+ // We are ready to put this type into the uniqued list up at the module
+ // level
+ type_list->Insert(type_sp);
+
+ dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get();
+ }
+ return type_sp;
+}
+
+// DWARF parsing functions
+
+class DWARFASTParserClang::DelayedAddObjCClassProperty {
+public:
+ DelayedAddObjCClassProperty(
+ const CompilerType &class_opaque_type, const char *property_name,
+ const CompilerType &property_opaque_type, // The property type is only
+ // required if you don't have an
+ // ivar decl
+ clang::ObjCIvarDecl *ivar_decl, const char *property_setter_name,
+ const char *property_getter_name, uint32_t property_attributes,
+ const ClangASTMetadata *metadata)
+ : m_class_opaque_type(class_opaque_type), m_property_name(property_name),
+ m_property_opaque_type(property_opaque_type), m_ivar_decl(ivar_decl),
+ m_property_setter_name(property_setter_name),
+ m_property_getter_name(property_getter_name),
+ m_property_attributes(property_attributes) {
+ if (metadata != nullptr) {
+ m_metadata_up.reset(new ClangASTMetadata());
+ *m_metadata_up = *metadata;
+ }
+ }
+
+ DelayedAddObjCClassProperty(const DelayedAddObjCClassProperty &rhs) {
+ *this = rhs;
+ }
+
+ DelayedAddObjCClassProperty &
+ operator=(const DelayedAddObjCClassProperty &rhs) {
+ m_class_opaque_type = rhs.m_class_opaque_type;
+ m_property_name = rhs.m_property_name;
+ m_property_opaque_type = rhs.m_property_opaque_type;
+ m_ivar_decl = rhs.m_ivar_decl;
+ m_property_setter_name = rhs.m_property_setter_name;
+ m_property_getter_name = rhs.m_property_getter_name;
+ m_property_attributes = rhs.m_property_attributes;
+
+ if (rhs.m_metadata_up) {
+ m_metadata_up.reset(new ClangASTMetadata());
+ *m_metadata_up = *rhs.m_metadata_up;
+ }
+ return *this;
+ }
+
+ bool Finalize() {
+ return ClangASTContext::AddObjCClassProperty(
+ m_class_opaque_type, m_property_name, m_property_opaque_type,
+ m_ivar_decl, m_property_setter_name, m_property_getter_name,
+ m_property_attributes, m_metadata_up.get());
+ }
+
+private:
+ CompilerType m_class_opaque_type;
+ const char *m_property_name;
+ CompilerType m_property_opaque_type;
+ clang::ObjCIvarDecl *m_ivar_decl;
+ const char *m_property_setter_name;
+ const char *m_property_getter_name;
+ uint32_t m_property_attributes;
+ std::unique_ptr<ClangASTMetadata> m_metadata_up;
+};
+
+bool DWARFASTParserClang::ParseTemplateDIE(
+ const DWARFDIE &die,
+ ClangASTContext::TemplateParameterInfos &template_param_infos) {
+ const dw_tag_t tag = die.Tag();
+ bool is_template_template_argument = false;
+
+ switch (tag) {
+ case DW_TAG_GNU_template_parameter_pack: {
+ template_param_infos.packed_args.reset(
+ new ClangASTContext::TemplateParameterInfos);
+ for (DWARFDIE child_die = die.GetFirstChild(); child_die.IsValid();
+ child_die = child_die.GetSibling()) {
+ if (!ParseTemplateDIE(child_die, *template_param_infos.packed_args))
+ return false;
+ }
+ if (const char *name = die.GetName()) {
+ template_param_infos.pack_name = name;
+ }
+ return true;
+ }
+ case DW_TAG_GNU_template_template_param:
+ is_template_template_argument = true;
+ LLVM_FALLTHROUGH;
+ case DW_TAG_template_type_parameter:
+ case DW_TAG_template_value_parameter: {
+ DWARFAttributes attributes;
+ const size_t num_attributes = die.GetAttributes(attributes);
+ const char *name = nullptr;
+ const char *template_name = nullptr;
+ CompilerType clang_type;
+ uint64_t uval64 = 0;
+ bool uval64_valid = false;
+ if (num_attributes > 0) {
+ DWARFFormValue form_value;
+ for (size_t i = 0; i < num_attributes; ++i) {
+ const dw_attr_t attr = attributes.AttributeAtIndex(i);
+
+ switch (attr) {
+ case DW_AT_name:
+ if (attributes.ExtractFormValueAtIndex(i, form_value))
+ name = form_value.AsCString();
+ break;
+
+ case DW_AT_GNU_template_name:
+ if (attributes.ExtractFormValueAtIndex(i, form_value))
+ template_name = form_value.AsCString();
+ break;
+
+ case DW_AT_type:
+ if (attributes.ExtractFormValueAtIndex(i, form_value)) {
+ Type *lldb_type = die.ResolveTypeUID(form_value.Reference());
+ if (lldb_type)
+ clang_type = lldb_type->GetForwardCompilerType();
+ }
+ break;
+
+ case DW_AT_const_value:
+ if (attributes.ExtractFormValueAtIndex(i, form_value)) {
+ uval64_valid = true;
+ uval64 = form_value.Unsigned();
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ clang::ASTContext *ast = m_ast.getASTContext();
+ if (!clang_type)
+ clang_type = m_ast.GetBasicType(eBasicTypeVoid);
+
+ if (!is_template_template_argument) {
+ bool is_signed = false;
+ if (name && name[0])
+ template_param_infos.names.push_back(name);
+ else
+ template_param_infos.names.push_back(NULL);
+
+ // Get the signed value for any integer or enumeration if available
+ clang_type.IsIntegerOrEnumerationType(is_signed);
+
+ if (tag == DW_TAG_template_value_parameter && uval64_valid) {
+ llvm::Optional<uint64_t> size = clang_type.GetBitSize(nullptr);
+ if (!size)
+ return false;
+ llvm::APInt apint(*size, uval64, is_signed);
+ template_param_infos.args.push_back(
+ clang::TemplateArgument(*ast, llvm::APSInt(apint, !is_signed),
+ ClangUtil::GetQualType(clang_type)));
+ } else {
+ template_param_infos.args.push_back(
+ clang::TemplateArgument(ClangUtil::GetQualType(clang_type)));
+ }
+ } else {
+ auto *tplt_type = m_ast.CreateTemplateTemplateParmDecl(template_name);
+ template_param_infos.names.push_back(name);
+ template_param_infos.args.push_back(
+ clang::TemplateArgument(clang::TemplateName(tplt_type)));
+ }
+ }
+ }
+ return true;
+
+ default:
+ break;
+ }
+ return false;
+}
+
+bool DWARFASTParserClang::ParseTemplateParameterInfos(
+ const DWARFDIE &parent_die,
+ ClangASTContext::TemplateParameterInfos &template_param_infos) {
+
+ if (!parent_die)
+ return false;
+
+ for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid();
+ die = die.GetSibling()) {
+ const dw_tag_t tag = die.Tag();
+
+ switch (tag) {
+ case DW_TAG_template_type_parameter:
+ case DW_TAG_template_value_parameter:
+ case DW_TAG_GNU_template_parameter_pack:
+ case DW_TAG_GNU_template_template_param:
+ ParseTemplateDIE(die, template_param_infos);
+ break;
+
+ default:
+ break;
+ }
+ }
+ if (template_param_infos.args.empty())
+ return false;
+ return template_param_infos.args.size() == template_param_infos.names.size();
+}
+
+bool DWARFASTParserClang::CompleteTypeFromDWARF(const DWARFDIE &die,
+ lldb_private::Type *type,
+ CompilerType &clang_type) {
+ SymbolFileDWARF *dwarf = die.GetDWARF();
+
+ std::lock_guard<std::recursive_mutex> guard(
+ dwarf->GetObjectFile()->GetModule()->GetMutex());
+
+ // Disable external storage for this type so we don't get anymore
+ // clang::ExternalASTSource queries for this type.
+ m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), false);
+
+ if (!die)
+ return false;
+
+#if defined LLDB_CONFIGURATION_DEBUG
+ // For debugging purposes, the LLDB_DWARF_DONT_COMPLETE_TYPENAMES environment
+ // variable can be set with one or more typenames separated by ';'
+ // characters. This will cause this function to not complete any types whose
+ // names match.
+ //
+ // Examples of setting this environment variable:
+ //
+ // LLDB_DWARF_DONT_COMPLETE_TYPENAMES=Foo
+ // LLDB_DWARF_DONT_COMPLETE_TYPENAMES=Foo;Bar;Baz
+ const char *dont_complete_typenames_cstr =
+ getenv("LLDB_DWARF_DONT_COMPLETE_TYPENAMES");
+ if (dont_complete_typenames_cstr && dont_complete_typenames_cstr[0]) {
+ const char *die_name = die.GetName();
+ if (die_name && die_name[0]) {
+ const char *match = strstr(dont_complete_typenames_cstr, die_name);
+ if (match) {
+ size_t die_name_length = strlen(die_name);
+ while (match) {
+ const char separator_char = ';';
+ const char next_char = match[die_name_length];
+ if (next_char == '\0' || next_char == separator_char) {
+ if (match == dont_complete_typenames_cstr ||
+ match[-1] == separator_char)
+ return false;
+ }
+ match = strstr(match + 1, die_name);
+ }
+ }
+ }
+ }
+#endif
+
+ const dw_tag_t tag = die.Tag();
+
+ Log *log =
+ nullptr; // (LogChannelDWARF::GetLogIfAny(DWARF_LOG_DEBUG_INFO|DWARF_LOG_TYPE_COMPLETION));
+ if (log)
+ dwarf->GetObjectFile()->GetModule()->LogMessageVerboseBacktrace(
+ log, "0x%8.8" PRIx64 ": %s '%s' resolving forward declaration...",
+ die.GetID(), die.GetTagAsCString(), type->GetName().AsCString());
+ assert(clang_type);
+ DWARFAttributes attributes;
+ switch (tag) {
+ case DW_TAG_structure_type:
+ case DW_TAG_union_type:
+ case DW_TAG_class_type: {
+ ClangASTImporter::LayoutInfo layout_info;
+
+ {
+ if (die.HasChildren()) {
+ LanguageType class_language = eLanguageTypeUnknown;
+ if (ClangASTContext::IsObjCObjectOrInterfaceType(clang_type)) {
+ class_language = eLanguageTypeObjC;
+ // For objective C we don't start the definition when the class is
+ // created.
+ ClangASTContext::StartTagDeclarationDefinition(clang_type);
+ }
+
+ int tag_decl_kind = -1;
+ AccessType default_accessibility = eAccessNone;
+ if (tag == DW_TAG_structure_type) {
+ tag_decl_kind = clang::TTK_Struct;
+ default_accessibility = eAccessPublic;
+ } else if (tag == DW_TAG_union_type) {
+ tag_decl_kind = clang::TTK_Union;
+ default_accessibility = eAccessPublic;
+ } else if (tag == DW_TAG_class_type) {
+ tag_decl_kind = clang::TTK_Class;
+ default_accessibility = eAccessPrivate;
+ }
+
+ std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> bases;
+ std::vector<int> member_accessibilities;
+ bool is_a_class = false;
+ // Parse members and base classes first
+ std::vector<DWARFDIE> member_function_dies;
+
+ DelayedPropertyList delayed_properties;
+ ParseChildMembers(die, clang_type, class_language, bases,
+ member_accessibilities, member_function_dies,
+ delayed_properties, default_accessibility, is_a_class,
+ layout_info);
+
+ // Now parse any methods if there were any...
+ for (const DWARFDIE &die : member_function_dies)
+ dwarf->ResolveType(die);
+
+ if (class_language == eLanguageTypeObjC) {
+ ConstString class_name(clang_type.GetTypeName());
+ if (class_name) {
+ DIEArray method_die_offsets;
+ dwarf->GetObjCMethodDIEOffsets(class_name, method_die_offsets);
+
+ if (!method_die_offsets.empty()) {
+ DWARFDebugInfo *debug_info = dwarf->DebugInfo();
+
+ const size_t num_matches = method_die_offsets.size();
+ for (size_t i = 0; i < num_matches; ++i) {
+ const DIERef &die_ref = method_die_offsets[i];
+ DWARFDIE method_die = debug_info->GetDIE(die_ref);
+
+ if (method_die)
+ method_die.ResolveType();
+ }
+ }
+
+ for (DelayedPropertyList::iterator pi = delayed_properties.begin(),
+ pe = delayed_properties.end();
+ pi != pe; ++pi)
+ pi->Finalize();
+ }
+ }
+
+ // If we have a DW_TAG_structure_type instead of a DW_TAG_class_type we
+ // need to tell the clang type it is actually a class.
+ if (class_language != eLanguageTypeObjC) {
+ if (is_a_class && tag_decl_kind != clang::TTK_Class)
+ m_ast.SetTagTypeKind(ClangUtil::GetQualType(clang_type),
+ clang::TTK_Class);
+ }
+
+ // Since DW_TAG_structure_type gets used for both classes and
+ // structures, we may need to set any DW_TAG_member fields to have a
+ // "private" access if none was specified. When we parsed the child
+ // members we tracked that actual accessibility value for each
+ // DW_TAG_member in the "member_accessibilities" array. If the value
+ // for the member is zero, then it was set to the
+ // "default_accessibility" which for structs was "public". Below we
+ // correct this by setting any fields to "private" that weren't
+ // correctly set.
+ if (is_a_class && !member_accessibilities.empty()) {
+ // This is a class and all members that didn't have their access
+ // specified are private.
+ m_ast.SetDefaultAccessForRecordFields(
+ m_ast.GetAsRecordDecl(clang_type), eAccessPrivate,
+ &member_accessibilities.front(), member_accessibilities.size());
+ }
+
+ if (!bases.empty()) {
+ // Make sure all base classes refer to complete types and not forward
+ // declarations. If we don't do this, clang will crash with an
+ // assertion in the call to clang_type.TransferBaseClasses()
+ for (const auto &base_class : bases) {
+ clang::TypeSourceInfo *type_source_info =
+ base_class->getTypeSourceInfo();
+ if (type_source_info) {
+ CompilerType base_class_type(
+ &m_ast, type_source_info->getType().getAsOpaquePtr());
+ if (!base_class_type.GetCompleteType()) {
+ auto module = dwarf->GetObjectFile()->GetModule();
+ module->ReportError(":: Class '%s' has a base class '%s' which "
+ "does not have a complete definition.",
+ die.GetName(),
+ base_class_type.GetTypeName().GetCString());
+ if (die.GetCU()->GetProducer() == eProducerClang)
+ module->ReportError(":: Try compiling the source file with "
+ "-fstandalone-debug.");
+
+ // We have no choice other than to pretend that the base class
+ // is complete. If we don't do this, clang will crash when we
+ // call setBases() inside of
+ // "clang_type.TransferBaseClasses()" below. Since we
+ // provide layout assistance, all ivars in this class and other
+ // classes will be fine, this is the best we can do short of
+ // crashing.
+ if (ClangASTContext::StartTagDeclarationDefinition(
+ base_class_type)) {
+ ClangASTContext::CompleteTagDeclarationDefinition(
+ base_class_type);
+ }
+ }
+ }
+ }
+
+ m_ast.TransferBaseClasses(clang_type.GetOpaqueQualType(),
+ std::move(bases));
+ }
+ }
+ }
+
+ m_ast.AddMethodOverridesForCXXRecordType(clang_type.GetOpaqueQualType());
+ ClangASTContext::BuildIndirectFields(clang_type);
+ ClangASTContext::CompleteTagDeclarationDefinition(clang_type);
+
+ if (!layout_info.field_offsets.empty() ||
+ !layout_info.base_offsets.empty() ||
+ !layout_info.vbase_offsets.empty()) {
+ if (type)
+ layout_info.bit_size = type->GetByteSize().getValueOr(0) * 8;
+ if (layout_info.bit_size == 0)
+ layout_info.bit_size =
+ die.GetAttributeValueAsUnsigned(DW_AT_byte_size, 0) * 8;
+
+ clang::CXXRecordDecl *record_decl =
+ m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType());
+ if (record_decl) {
+ if (log) {
+ ModuleSP module_sp = dwarf->GetObjectFile()->GetModule();
+
+ if (module_sp) {
+ module_sp->LogMessage(
+ log,
+ "ClangASTContext::CompleteTypeFromDWARF (clang_type = %p) "
+ "caching layout info for record_decl = %p, bit_size = %" PRIu64
+ ", alignment = %" PRIu64
+ ", field_offsets[%u], base_offsets[%u], vbase_offsets[%u])",
+ static_cast<void *>(clang_type.GetOpaqueQualType()),
+ static_cast<void *>(record_decl), layout_info.bit_size,
+ layout_info.alignment,
+ static_cast<uint32_t>(layout_info.field_offsets.size()),
+ static_cast<uint32_t>(layout_info.base_offsets.size()),
+ static_cast<uint32_t>(layout_info.vbase_offsets.size()));
+
+ uint32_t idx;
+ {
+ llvm::DenseMap<const clang::FieldDecl *, uint64_t>::const_iterator
+ pos,
+ end = layout_info.field_offsets.end();
+ for (idx = 0, pos = layout_info.field_offsets.begin(); pos != end;
+ ++pos, ++idx) {
+ module_sp->LogMessage(
+ log, "ClangASTContext::CompleteTypeFromDWARF (clang_type = "
+ "%p) field[%u] = { bit_offset=%u, name='%s' }",
+ static_cast<void *>(clang_type.GetOpaqueQualType()), idx,
+ static_cast<uint32_t>(pos->second),
+ pos->first->getNameAsString().c_str());
+ }
+ }
+
+ {
+ llvm::DenseMap<const clang::CXXRecordDecl *,
+ clang::CharUnits>::const_iterator base_pos,
+ base_end = layout_info.base_offsets.end();
+ for (idx = 0, base_pos = layout_info.base_offsets.begin();
+ base_pos != base_end; ++base_pos, ++idx) {
+ module_sp->LogMessage(
+ log, "ClangASTContext::CompleteTypeFromDWARF (clang_type = "
+ "%p) base[%u] = { byte_offset=%u, name='%s' }",
+ clang_type.GetOpaqueQualType(), idx,
+ (uint32_t)base_pos->second.getQuantity(),
+ base_pos->first->getNameAsString().c_str());
+ }
+ }
+ {
+ llvm::DenseMap<const clang::CXXRecordDecl *,
+ clang::CharUnits>::const_iterator vbase_pos,
+ vbase_end = layout_info.vbase_offsets.end();
+ for (idx = 0, vbase_pos = layout_info.vbase_offsets.begin();
+ vbase_pos != vbase_end; ++vbase_pos, ++idx) {
+ module_sp->LogMessage(
+ log, "ClangASTContext::CompleteTypeFromDWARF (clang_type = "
+ "%p) vbase[%u] = { byte_offset=%u, name='%s' }",
+ static_cast<void *>(clang_type.GetOpaqueQualType()), idx,
+ static_cast<uint32_t>(vbase_pos->second.getQuantity()),
+ vbase_pos->first->getNameAsString().c_str());
+ }
+ }
+ }
+ }
+ GetClangASTImporter().InsertRecordDecl(record_decl, layout_info);
+ }
+ }
+ }
+
+ return (bool)clang_type;
+
+ case DW_TAG_enumeration_type:
+ if (ClangASTContext::StartTagDeclarationDefinition(clang_type)) {
+ if (die.HasChildren()) {
+ bool is_signed = false;
+ clang_type.IsIntegerType(is_signed);
+ ParseChildEnumerators(clang_type, is_signed,
+ type->GetByteSize().getValueOr(0), die);
+ }
+ ClangASTContext::CompleteTagDeclarationDefinition(clang_type);
+ }
+ return (bool)clang_type;
+
+ default:
+ assert(false && "not a forward clang type decl!");
+ break;
+ }
+
+ return false;
+}
+
+std::vector<DWARFDIE> DWARFASTParserClang::GetDIEForDeclContext(
+ lldb_private::CompilerDeclContext decl_context) {
+ std::vector<DWARFDIE> result;
+ for (auto it = m_decl_ctx_to_die.find(
+ (clang::DeclContext *)decl_context.GetOpaqueDeclContext());
+ it != m_decl_ctx_to_die.end(); it++)
+ result.push_back(it->second);
+ return result;
+}
+
+CompilerDecl DWARFASTParserClang::GetDeclForUIDFromDWARF(const DWARFDIE &die) {
+ clang::Decl *clang_decl = GetClangDeclForDIE(die);
+ if (clang_decl != nullptr)
+ return CompilerDecl(&m_ast, clang_decl);
+ return CompilerDecl();
+}
+
+CompilerDeclContext
+DWARFASTParserClang::GetDeclContextForUIDFromDWARF(const DWARFDIE &die) {
+ clang::DeclContext *clang_decl_ctx = GetClangDeclContextForDIE(die);
+ if (clang_decl_ctx)
+ return CompilerDeclContext(&m_ast, clang_decl_ctx);
+ return CompilerDeclContext();
+}
+
+CompilerDeclContext
+DWARFASTParserClang::GetDeclContextContainingUIDFromDWARF(const DWARFDIE &die) {
+ clang::DeclContext *clang_decl_ctx =
+ GetClangDeclContextContainingDIE(die, nullptr);
+ if (clang_decl_ctx)
+ return CompilerDeclContext(&m_ast, clang_decl_ctx);
+ return CompilerDeclContext();
+}
+
+size_t DWARFASTParserClang::ParseChildEnumerators(
+ lldb_private::CompilerType &clang_type, bool is_signed,
+ uint32_t enumerator_byte_size, const DWARFDIE &parent_die) {
+ if (!parent_die)
+ return 0;
+
+ size_t enumerators_added = 0;
+
+ for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid();
+ die = die.GetSibling()) {
+ const dw_tag_t tag = die.Tag();
+ if (tag == DW_TAG_enumerator) {
+ DWARFAttributes attributes;
+ const size_t num_child_attributes = die.GetAttributes(attributes);
+ if (num_child_attributes > 0) {
+ const char *name = nullptr;
+ bool got_value = false;
+ int64_t enum_value = 0;
+ Declaration decl;
+
+ uint32_t i;
+ for (i = 0; i < num_child_attributes; ++i) {
+ const dw_attr_t attr = attributes.AttributeAtIndex(i);
+ DWARFFormValue form_value;
+ if (attributes.ExtractFormValueAtIndex(i, form_value)) {
+ switch (attr) {
+ case DW_AT_const_value:
+ got_value = true;
+ if (is_signed)
+ enum_value = form_value.Signed();
+ else
+ enum_value = form_value.Unsigned();
+ break;
+
+ case DW_AT_name:
+ name = form_value.AsCString();
+ break;
+
+ case DW_AT_description:
+ default:
+ case DW_AT_decl_file:
+ decl.SetFile(die.GetCU()->GetFile(form_value.Unsigned()));
+ break;
+ case DW_AT_decl_line:
+ decl.SetLine(form_value.Unsigned());
+ break;
+ case DW_AT_decl_column:
+ decl.SetColumn(form_value.Unsigned());
+ break;
+ case DW_AT_sibling:
+ break;
+ }
+ }
+ }
+
+ if (name && name[0] && got_value) {
+ m_ast.AddEnumerationValueToEnumerationType(
+ clang_type, decl, name, enum_value, enumerator_byte_size * 8);
+ ++enumerators_added;
+ }
+ }
+ }
+ }
+ return enumerators_added;
+}
+
+#if defined(LLDB_CONFIGURATION_DEBUG) || defined(LLDB_CONFIGURATION_RELEASE)
+
+class DIEStack {
+public:
+ void Push(const DWARFDIE &die) { m_dies.push_back(die); }
+
+ void LogDIEs(Log *log) {
+ StreamString log_strm;
+ const size_t n = m_dies.size();
+ log_strm.Printf("DIEStack[%" PRIu64 "]:\n", (uint64_t)n);
+ for (size_t i = 0; i < n; i++) {
+ std::string qualified_name;
+ const DWARFDIE &die = m_dies[i];
+ die.GetQualifiedName(qualified_name);
+ log_strm.Printf("[%" PRIu64 "] 0x%8.8x: %s name='%s'\n", (uint64_t)i,
+ die.GetOffset(), die.GetTagAsCString(),
+ qualified_name.c_str());
+ }
+ log->PutCString(log_strm.GetData());
+ }
+ void Pop() { m_dies.pop_back(); }
+
+ class ScopedPopper {
+ public:
+ ScopedPopper(DIEStack &die_stack)
+ : m_die_stack(die_stack), m_valid(false) {}
+
+ void Push(const DWARFDIE &die) {
+ m_valid = true;
+ m_die_stack.Push(die);
+ }
+
+ ~ScopedPopper() {
+ if (m_valid)
+ m_die_stack.Pop();
+ }
+
+ protected:
+ DIEStack &m_die_stack;
+ bool m_valid;
+ };
+
+protected:
+ typedef std::vector<DWARFDIE> Stack;
+ Stack m_dies;
+};
+#endif
+
+Function *DWARFASTParserClang::ParseFunctionFromDWARF(CompileUnit &comp_unit,
+ const DWARFDIE &die) {
+ DWARFRangeList func_ranges;
+ const char *name = nullptr;
+ const char *mangled = nullptr;
+ int decl_file = 0;
+ int decl_line = 0;
+ int decl_column = 0;
+ int call_file = 0;
+ int call_line = 0;
+ int call_column = 0;
+ DWARFExpression frame_base;
+
+ const dw_tag_t tag = die.Tag();
+
+ if (tag != DW_TAG_subprogram)
+ return nullptr;
+
+ if (die.GetDIENamesAndRanges(name, mangled, func_ranges, decl_file, decl_line,
+ decl_column, call_file, call_line, call_column,
+ &frame_base)) {
+
+ // Union of all ranges in the function DIE (if the function is
+ // discontiguous)
+ AddressRange func_range;
+ lldb::addr_t lowest_func_addr = func_ranges.GetMinRangeBase(0);
+ lldb::addr_t highest_func_addr = func_ranges.GetMaxRangeEnd(0);
+ if (lowest_func_addr != LLDB_INVALID_ADDRESS &&
+ lowest_func_addr <= highest_func_addr) {
+ ModuleSP module_sp(die.GetModule());
+ func_range.GetBaseAddress().ResolveAddressUsingFileSections(
+ lowest_func_addr, module_sp->GetSectionList());
+ if (func_range.GetBaseAddress().IsValid())
+ func_range.SetByteSize(highest_func_addr - lowest_func_addr);
+ }
+
+ if (func_range.GetBaseAddress().IsValid()) {
+ Mangled func_name;
+ if (mangled)
+ func_name.SetValue(ConstString(mangled), true);
+ else if ((die.GetParent().Tag() == DW_TAG_compile_unit ||
+ die.GetParent().Tag() == DW_TAG_partial_unit) &&
+ Language::LanguageIsCPlusPlus(die.GetLanguage()) &&
+ !Language::LanguageIsObjC(die.GetLanguage()) && name &&
+ strcmp(name, "main") != 0) {
+ // If the mangled name is not present in the DWARF, generate the
+ // demangled name using the decl context. We skip if the function is
+ // "main" as its name is never mangled.
+ bool is_static = false;
+ bool is_variadic = false;
+ bool has_template_params = false;
+ unsigned type_quals = 0;
+ std::vector<CompilerType> param_types;
+ std::vector<clang::ParmVarDecl *> param_decls;
+ DWARFDeclContext decl_ctx;
+ StreamString sstr;
+
+ die.GetDWARFDeclContext(decl_ctx);
+ sstr << decl_ctx.GetQualifiedName();
+
+ clang::DeclContext *containing_decl_ctx =
+ GetClangDeclContextContainingDIE(die, nullptr);
+ ParseChildParameters(containing_decl_ctx, die, true, is_static,
+ is_variadic, has_template_params, param_types,
+ param_decls, type_quals);
+ sstr << "(";
+ for (size_t i = 0; i < param_types.size(); i++) {
+ if (i > 0)
+ sstr << ", ";
+ sstr << param_types[i].GetTypeName();
+ }
+ if (is_variadic)
+ sstr << ", ...";
+ sstr << ")";
+ if (type_quals & clang::Qualifiers::Const)
+ sstr << " const";
+
+ func_name.SetValue(ConstString(sstr.GetString()), false);
+ } else
+ func_name.SetValue(ConstString(name), false);
+
+ FunctionSP func_sp;
+ std::unique_ptr<Declaration> decl_up;
+ if (decl_file != 0 || decl_line != 0 || decl_column != 0)
+ decl_up.reset(new Declaration(die.GetCU()->GetFile(decl_file),
+ decl_line, decl_column));
+
+ SymbolFileDWARF *dwarf = die.GetDWARF();
+ // Supply the type _only_ if it has already been parsed
+ Type *func_type = dwarf->GetDIEToType().lookup(die.GetDIE());
+
+ assert(func_type == nullptr || func_type != DIE_IS_BEING_PARSED);
+
+ if (dwarf->FixupAddress(func_range.GetBaseAddress())) {
+ const user_id_t func_user_id = die.GetID();
+ func_sp =
+ std::make_shared<Function>(&comp_unit,
+ func_user_id, // UserID is the DIE offset
+ func_user_id, func_name, func_type,
+ func_range); // first address range
+
+ if (func_sp.get() != nullptr) {
+ if (frame_base.IsValid())
+ func_sp->GetFrameBaseExpression() = frame_base;
+ comp_unit.AddFunction(func_sp);
+ return func_sp.get();
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+bool DWARFASTParserClang::ParseChildMembers(
+ const DWARFDIE &parent_die, CompilerType &class_clang_type,
+ const LanguageType class_language,
+ std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> &base_classes,
+ std::vector<int> &member_accessibilities,
+ std::vector<DWARFDIE> &member_function_dies,
+ DelayedPropertyList &delayed_properties, AccessType &default_accessibility,
+ bool &is_a_class, ClangASTImporter::LayoutInfo &layout_info) {
+ if (!parent_die)
+ return false;
+
+ // Get the parent byte size so we can verify any members will fit
+ const uint64_t parent_byte_size =
+ parent_die.GetAttributeValueAsUnsigned(DW_AT_byte_size, UINT64_MAX);
+ const uint64_t parent_bit_size =
+ parent_byte_size == UINT64_MAX ? UINT64_MAX : parent_byte_size * 8;
+
+ uint32_t member_idx = 0;
+ BitfieldInfo last_field_info;
+
+ ModuleSP module_sp = parent_die.GetDWARF()->GetObjectFile()->GetModule();
+ ClangASTContext *ast =
+ llvm::dyn_cast_or_null<ClangASTContext>(class_clang_type.GetTypeSystem());
+ if (ast == nullptr)
+ return false;
+
+ for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid();
+ die = die.GetSibling()) {
+ dw_tag_t tag = die.Tag();
+
+ switch (tag) {
+ case DW_TAG_member:
+ case DW_TAG_APPLE_property: {
+ DWARFAttributes attributes;
+ const size_t num_attributes = die.GetAttributes(attributes);
+ if (num_attributes > 0) {
+ const char *name = nullptr;
+ const char *prop_name = nullptr;
+ const char *prop_getter_name = nullptr;
+ const char *prop_setter_name = nullptr;
+ uint32_t prop_attributes = 0;
+
+ bool is_artificial = false;
+ DWARFFormValue encoding_form;
+ AccessType accessibility = eAccessNone;
+ uint32_t member_byte_offset =
+ (parent_die.Tag() == DW_TAG_union_type) ? 0 : UINT32_MAX;
+ llvm::Optional<uint64_t> byte_size;
+ int64_t bit_offset = 0;
+ uint64_t data_bit_offset = UINT64_MAX;
+ size_t bit_size = 0;
+ bool is_external =
+ false; // On DW_TAG_members, this means the member is static
+ uint32_t i;
+ for (i = 0; i < num_attributes && !is_artificial; ++i) {
+ const dw_attr_t attr = attributes.AttributeAtIndex(i);
+ DWARFFormValue form_value;
+ if (attributes.ExtractFormValueAtIndex(i, form_value)) {
+ switch (attr) {
+ case DW_AT_name:
+ name = form_value.AsCString();
+ break;
+ case DW_AT_type:
+ encoding_form = form_value;
+ break;
+ case DW_AT_bit_offset:
+ bit_offset = form_value.Signed();
+ break;
+ case DW_AT_bit_size:
+ bit_size = form_value.Unsigned();
+ break;
+ case DW_AT_byte_size:
+ byte_size = form_value.Unsigned();
+ break;
+ case DW_AT_data_bit_offset:
+ data_bit_offset = form_value.Unsigned();
+ break;
+ case DW_AT_data_member_location:
+ if (form_value.BlockData()) {
+ Value initialValue(0);
+ Value memberOffset(0);
+ const DWARFDataExtractor &debug_info_data = die.GetData();
+ uint32_t block_length = form_value.Unsigned();
+ uint32_t block_offset =
+ form_value.BlockData() - debug_info_data.GetDataStart();
+ if (DWARFExpression::Evaluate(
+ nullptr, // ExecutionContext *
+ nullptr, // RegisterContext *
+ module_sp, debug_info_data, die.GetCU(), block_offset,
+ block_length, eRegisterKindDWARF, &initialValue,
+ nullptr, memberOffset, nullptr)) {
+ member_byte_offset =
+ memberOffset.ResolveValue(nullptr).UInt();
+ }
+ } else {
+ // With DWARF 3 and later, if the value is an integer constant,
+ // this form value is the offset in bytes from the beginning of
+ // the containing entity.
+ member_byte_offset = form_value.Unsigned();
+ }
+ break;
+
+ case DW_AT_accessibility:
+ accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned());
+ break;
+ case DW_AT_artificial:
+ is_artificial = form_value.Boolean();
+ break;
+ case DW_AT_APPLE_property_name:
+ prop_name = form_value.AsCString();
+ break;
+ case DW_AT_APPLE_property_getter:
+ prop_getter_name = form_value.AsCString();
+ break;
+ case DW_AT_APPLE_property_setter:
+ prop_setter_name = form_value.AsCString();
+ break;
+ case DW_AT_APPLE_property_attribute:
+ prop_attributes = form_value.Unsigned();
+ break;
+ case DW_AT_external:
+ is_external = form_value.Boolean();
+ break;
+
+ default:
+ case DW_AT_declaration:
+ case DW_AT_description:
+ case DW_AT_mutable:
+ case DW_AT_visibility:
+ case DW_AT_sibling:
+ break;
+ }
+ }
+ }
+
+ if (prop_name) {
+ ConstString fixed_getter;
+ ConstString fixed_setter;
+
+ // Check if the property getter/setter were provided as full names.
+ // We want basenames, so we extract them.
+
+ if (prop_getter_name && prop_getter_name[0] == '-') {
+ ObjCLanguage::MethodName prop_getter_method(prop_getter_name, true);
+ prop_getter_name = prop_getter_method.GetSelector().GetCString();
+ }
+
+ if (prop_setter_name && prop_setter_name[0] == '-') {
+ ObjCLanguage::MethodName prop_setter_method(prop_setter_name, true);
+ prop_setter_name = prop_setter_method.GetSelector().GetCString();
+ }
+
+ // If the names haven't been provided, they need to be filled in.
+
+ if (!prop_getter_name) {
+ prop_getter_name = prop_name;
+ }
+ if (!prop_setter_name && prop_name[0] &&
+ !(prop_attributes & DW_APPLE_PROPERTY_readonly)) {
+ StreamString ss;
+
+ ss.Printf("set%c%s:", toupper(prop_name[0]), &prop_name[1]);
+
+ fixed_setter.SetString(ss.GetString());
+ prop_setter_name = fixed_setter.GetCString();
+ }
+ }
+
+ // Clang has a DWARF generation bug where sometimes it represents
+ // fields that are references with bad byte size and bit size/offset
+ // information such as:
+ //
+ // DW_AT_byte_size( 0x00 )
+ // DW_AT_bit_size( 0x40 )
+ // DW_AT_bit_offset( 0xffffffffffffffc0 )
+ //
+ // So check the bit offset to make sure it is sane, and if the values
+ // are not sane, remove them. If we don't do this then we will end up
+ // with a crash if we try to use this type in an expression when clang
+ // becomes unhappy with its recycled debug info.
+
+ if (byte_size.getValueOr(0) == 0 && bit_offset < 0) {
+ bit_size = 0;
+ bit_offset = 0;
+ }
+
+ // FIXME: Make Clang ignore Objective-C accessibility for expressions
+ if (class_language == eLanguageTypeObjC ||
+ class_language == eLanguageTypeObjC_plus_plus)
+ accessibility = eAccessNone;
+
+ // Handle static members
+ if (is_external && member_byte_offset == UINT32_MAX) {
+ Type *var_type = die.ResolveTypeUID(encoding_form.Reference());
+
+ if (var_type) {
+ if (accessibility == eAccessNone)
+ accessibility = eAccessPublic;
+ ClangASTContext::AddVariableToRecordType(
+ class_clang_type, name, var_type->GetLayoutCompilerType(),
+ accessibility);
+ }
+ break;
+ }
+
+ if (!is_artificial) {
+ Type *member_type = die.ResolveTypeUID(encoding_form.Reference());
+
+ clang::FieldDecl *field_decl = nullptr;
+ if (tag == DW_TAG_member) {
+ if (member_type) {
+ if (accessibility == eAccessNone)
+ accessibility = default_accessibility;
+ member_accessibilities.push_back(accessibility);
+
+ uint64_t field_bit_offset =
+ (member_byte_offset == UINT32_MAX ? 0
+ : (member_byte_offset * 8));
+ if (bit_size > 0) {
+
+ BitfieldInfo this_field_info;
+ this_field_info.bit_offset = field_bit_offset;
+ this_field_info.bit_size = bit_size;
+
+ /////////////////////////////////////////////////////////////
+ // How to locate a field given the DWARF debug information
+ //
+ // AT_byte_size indicates the size of the word in which the bit
+ // offset must be interpreted.
+ //
+ // AT_data_member_location indicates the byte offset of the
+ // word from the base address of the structure.
+ //
+ // AT_bit_offset indicates how many bits into the word
+ // (according to the host endianness) the low-order bit of the
+ // field starts. AT_bit_offset can be negative.
+ //
+ // AT_bit_size indicates the size of the field in bits.
+ /////////////////////////////////////////////////////////////
+
+ if (data_bit_offset != UINT64_MAX) {
+ this_field_info.bit_offset = data_bit_offset;
+ } else {
+ if (!byte_size)
+ byte_size = member_type->GetByteSize();
+
+ ObjectFile *objfile = die.GetDWARF()->GetObjectFile();
+ if (objfile->GetByteOrder() == eByteOrderLittle) {
+ this_field_info.bit_offset += byte_size.getValueOr(0) * 8;
+ this_field_info.bit_offset -= (bit_offset + bit_size);
+ } else {
+ this_field_info.bit_offset += bit_offset;
+ }
+ }
+
+ if ((this_field_info.bit_offset >= parent_bit_size) ||
+ !last_field_info.NextBitfieldOffsetIsValid(
+ this_field_info.bit_offset)) {
+ ObjectFile *objfile = die.GetDWARF()->GetObjectFile();
+ objfile->GetModule()->ReportWarning(
+ "0x%8.8" PRIx64 ": %s bitfield named \"%s\" has invalid "
+ "bit offset (0x%8.8" PRIx64
+ ") member will be ignored. Please file a bug against the "
+ "compiler and include the preprocessed output for %s\n",
+ die.GetID(), DW_TAG_value_to_name(tag), name,
+ this_field_info.bit_offset,
+ GetUnitName(parent_die).c_str());
+ this_field_info.Clear();
+ continue;
+ }
+
+ // Update the field bit offset we will report for layout
+ field_bit_offset = this_field_info.bit_offset;
+
+ // If the member to be emitted did not start on a character
+ // boundary and there is empty space between the last field and
+ // this one, then we need to emit an anonymous member filling
+ // up the space up to its start. There are three cases here:
+ //
+ // 1 If the previous member ended on a character boundary, then
+ // we can emit an
+ // anonymous member starting at the most recent character
+ // boundary.
+ //
+ // 2 If the previous member did not end on a character boundary
+ // and the distance
+ // from the end of the previous member to the current member
+ // is less than a
+ // word width, then we can emit an anonymous member starting
+ // right after the
+ // previous member and right before this member.
+ //
+ // 3 If the previous member did not end on a character boundary
+ // and the distance
+ // from the end of the previous member to the current member
+ // is greater than
+ // or equal a word width, then we act as in Case 1.
+
+ const uint64_t character_width = 8;
+ const uint64_t word_width = 32;
+
+ // Objective-C has invalid DW_AT_bit_offset values in older
+ // versions of clang, so we have to be careful and only insert
+ // unnamed bitfields if we have a new enough clang.
+ bool detect_unnamed_bitfields = true;
+
+ if (class_language == eLanguageTypeObjC ||
+ class_language == eLanguageTypeObjC_plus_plus)
+ detect_unnamed_bitfields =
+ die.GetCU()->Supports_unnamed_objc_bitfields();
+
+ if (detect_unnamed_bitfields) {
+ BitfieldInfo anon_field_info;
+
+ if ((this_field_info.bit_offset % character_width) !=
+ 0) // not char aligned
+ {
+ uint64_t last_field_end = 0;
+
+ if (last_field_info.IsValid())
+ last_field_end =
+ last_field_info.bit_offset + last_field_info.bit_size;
+
+ if (this_field_info.bit_offset != last_field_end) {
+ if (((last_field_end % character_width) == 0) || // case 1
+ (this_field_info.bit_offset - last_field_end >=
+ word_width)) // case 3
+ {
+ anon_field_info.bit_size =
+ this_field_info.bit_offset % character_width;
+ anon_field_info.bit_offset =
+ this_field_info.bit_offset -
+ anon_field_info.bit_size;
+ } else // case 2
+ {
+ anon_field_info.bit_size =
+ this_field_info.bit_offset - last_field_end;
+ anon_field_info.bit_offset = last_field_end;
+ }
+ }
+ }
+
+ if (anon_field_info.IsValid()) {
+ clang::FieldDecl *unnamed_bitfield_decl =
+ ClangASTContext::AddFieldToRecordType(
+ class_clang_type, llvm::StringRef(),
+ m_ast.GetBuiltinTypeForEncodingAndBitSize(
+ eEncodingSint, word_width),
+ accessibility, anon_field_info.bit_size);
+
+ layout_info.field_offsets.insert(std::make_pair(
+ unnamed_bitfield_decl, anon_field_info.bit_offset));
+ }
+ }
+ last_field_info = this_field_info;
+ } else {
+ last_field_info.Clear();
+ }
+
+ CompilerType member_clang_type =
+ member_type->GetLayoutCompilerType();
+ if (!member_clang_type.IsCompleteType())
+ member_clang_type.GetCompleteType();
+
+ {
+ // Older versions of clang emit array[0] and array[1] in the
+ // same way (<rdar://problem/12566646>). If the current field
+ // is at the end of the structure, then there is definitely no
+ // room for extra elements and we override the type to
+ // array[0].
+
+ CompilerType member_array_element_type;
+ uint64_t member_array_size;
+ bool member_array_is_incomplete;
+
+ if (member_clang_type.IsArrayType(
+ &member_array_element_type, &member_array_size,
+ &member_array_is_incomplete) &&
+ !member_array_is_incomplete) {
+ uint64_t parent_byte_size =
+ parent_die.GetAttributeValueAsUnsigned(DW_AT_byte_size,
+ UINT64_MAX);
+
+ if (member_byte_offset >= parent_byte_size) {
+ if (member_array_size != 1 &&
+ (member_array_size != 0 ||
+ member_byte_offset > parent_byte_size)) {
+ module_sp->ReportError(
+ "0x%8.8" PRIx64
+ ": DW_TAG_member '%s' refers to type 0x%8.8x"
+ " which extends beyond the bounds of 0x%8.8" PRIx64,
+ die.GetID(), name,
+ encoding_form.Reference().GetOffset(),
+ parent_die.GetID());
+ }
+
+ member_clang_type = m_ast.CreateArrayType(
+ member_array_element_type, 0, false);
+ }
+ }
+ }
+
+ if (ClangASTContext::IsCXXClassType(member_clang_type) &&
+ !member_clang_type.GetCompleteType()) {
+ if (die.GetCU()->GetProducer() == eProducerClang)
+ module_sp->ReportError(
+ "DWARF DIE at 0x%8.8x (class %s) has a member variable "
+ "0x%8.8x (%s) whose type is a forward declaration, not a "
+ "complete definition.\nTry compiling the source file "
+ "with -fstandalone-debug",
+ parent_die.GetOffset(), parent_die.GetName(),
+ die.GetOffset(), name);
+ else
+ module_sp->ReportError(
+ "DWARF DIE at 0x%8.8x (class %s) has a member variable "
+ "0x%8.8x (%s) whose type is a forward declaration, not a "
+ "complete definition.\nPlease file a bug against the "
+ "compiler and include the preprocessed output for %s",
+ parent_die.GetOffset(), parent_die.GetName(),
+ die.GetOffset(), name, GetUnitName(parent_die).c_str());
+ // We have no choice other than to pretend that the member
+ // class is complete. If we don't do this, clang will crash
+ // when trying to layout the class. Since we provide layout
+ // assistance, all ivars in this class and other classes will
+ // be fine, this is the best we can do short of crashing.
+ if (ClangASTContext::StartTagDeclarationDefinition(
+ member_clang_type)) {
+ ClangASTContext::CompleteTagDeclarationDefinition(
+ member_clang_type);
+ } else {
+ module_sp->ReportError(
+ "DWARF DIE at 0x%8.8x (class %s) has a member variable "
+ "0x%8.8x (%s) whose type claims to be a C++ class but we "
+ "were not able to start its definition.\nPlease file a "
+ "bug and attach the file at the start of this error "
+ "message",
+ parent_die.GetOffset(), parent_die.GetName(),
+ die.GetOffset(), name);
+ }
+ }
+
+ field_decl = ClangASTContext::AddFieldToRecordType(
+ class_clang_type, name, member_clang_type, accessibility,
+ bit_size);
+
+ m_ast.SetMetadataAsUserID(field_decl, die.GetID());
+
+ layout_info.field_offsets.insert(
+ std::make_pair(field_decl, field_bit_offset));
+ } else {
+ if (name)
+ module_sp->ReportError(
+ "0x%8.8" PRIx64
+ ": DW_TAG_member '%s' refers to type 0x%8.8x"
+ " which was unable to be parsed",
+ die.GetID(), name, encoding_form.Reference().GetOffset());
+ else
+ module_sp->ReportError(
+ "0x%8.8" PRIx64 ": DW_TAG_member refers to type 0x%8.8x"
+ " which was unable to be parsed",
+ die.GetID(), encoding_form.Reference().GetOffset());
+ }
+ }
+
+ if (prop_name != nullptr && member_type) {
+ clang::ObjCIvarDecl *ivar_decl = nullptr;
+
+ if (field_decl) {
+ ivar_decl = clang::dyn_cast<clang::ObjCIvarDecl>(field_decl);
+ assert(ivar_decl != nullptr);
+ }
+
+ ClangASTMetadata metadata;
+ metadata.SetUserID(die.GetID());
+ delayed_properties.push_back(DelayedAddObjCClassProperty(
+ class_clang_type, prop_name,
+ member_type->GetLayoutCompilerType(), ivar_decl,
+ prop_setter_name, prop_getter_name, prop_attributes,
+ &metadata));
+
+ if (ivar_decl)
+ m_ast.SetMetadataAsUserID(ivar_decl, die.GetID());
+ }
+ }
+ }
+ ++member_idx;
+ } break;
+
+ case DW_TAG_subprogram:
+ // Let the type parsing code handle this one for us.
+ member_function_dies.push_back(die);
+ break;
+
+ case DW_TAG_inheritance: {
+ is_a_class = true;
+ if (default_accessibility == eAccessNone)
+ default_accessibility = eAccessPrivate;
+ // TODO: implement DW_TAG_inheritance type parsing
+ DWARFAttributes attributes;
+ const size_t num_attributes = die.GetAttributes(attributes);
+ if (num_attributes > 0) {
+ DWARFFormValue encoding_form;
+ AccessType accessibility = default_accessibility;
+ bool is_virtual = false;
+ bool is_base_of_class = true;
+ off_t member_byte_offset = 0;
+ uint32_t i;
+ for (i = 0; i < num_attributes; ++i) {
+ const dw_attr_t attr = attributes.AttributeAtIndex(i);
+ DWARFFormValue form_value;
+ if (attributes.ExtractFormValueAtIndex(i, form_value)) {
+ switch (attr) {
+ case DW_AT_type:
+ encoding_form = form_value;
+ break;
+ case DW_AT_data_member_location:
+ if (form_value.BlockData()) {
+ Value initialValue(0);
+ Value memberOffset(0);
+ const DWARFDataExtractor &debug_info_data = die.GetData();
+ uint32_t block_length = form_value.Unsigned();
+ uint32_t block_offset =
+ form_value.BlockData() - debug_info_data.GetDataStart();
+ if (DWARFExpression::Evaluate(nullptr, nullptr, module_sp,
+ debug_info_data, die.GetCU(),
+ block_offset, block_length,
+ eRegisterKindDWARF, &initialValue,
+ nullptr, memberOffset, nullptr)) {
+ member_byte_offset =
+ memberOffset.ResolveValue(nullptr).UInt();
+ }
+ } else {
+ // With DWARF 3 and later, if the value is an integer constant,
+ // this form value is the offset in bytes from the beginning of
+ // the containing entity.
+ member_byte_offset = form_value.Unsigned();
+ }
+ break;
+
+ case DW_AT_accessibility:
+ accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned());
+ break;
+
+ case DW_AT_virtuality:
+ is_virtual = form_value.Boolean();
+ break;
+
+ case DW_AT_sibling:
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ Type *base_class_type = die.ResolveTypeUID(encoding_form.Reference());
+ if (base_class_type == nullptr) {
+ module_sp->ReportError("0x%8.8x: DW_TAG_inheritance failed to "
+ "resolve the base class at 0x%8.8x"
+ " from enclosing type 0x%8.8x. \nPlease file "
+ "a bug and attach the file at the start of "
+ "this error message",
+ die.GetOffset(),
+ encoding_form.Reference().GetOffset(),
+ parent_die.GetOffset());
+ break;
+ }
+
+ CompilerType base_class_clang_type =
+ base_class_type->GetFullCompilerType();
+ assert(base_class_clang_type);
+ if (class_language == eLanguageTypeObjC) {
+ ast->SetObjCSuperClass(class_clang_type, base_class_clang_type);
+ } else {
+ std::unique_ptr<clang::CXXBaseSpecifier> result =
+ ast->CreateBaseClassSpecifier(
+ base_class_clang_type.GetOpaqueQualType(), accessibility,
+ is_virtual, is_base_of_class);
+ if (!result)
+ break;
+
+ base_classes.push_back(std::move(result));
+
+ if (is_virtual) {
+ // Do not specify any offset for virtual inheritance. The DWARF
+ // produced by clang doesn't give us a constant offset, but gives
+ // us a DWARF expressions that requires an actual object in memory.
+ // the DW_AT_data_member_location for a virtual base class looks
+ // like:
+ // DW_AT_data_member_location( DW_OP_dup, DW_OP_deref,
+ // DW_OP_constu(0x00000018), DW_OP_minus, DW_OP_deref,
+ // DW_OP_plus )
+ // Given this, there is really no valid response we can give to
+ // clang for virtual base class offsets, and this should eventually
+ // be removed from LayoutRecordType() in the external
+ // AST source in clang.
+ } else {
+ layout_info.base_offsets.insert(std::make_pair(
+ ast->GetAsCXXRecordDecl(
+ base_class_clang_type.GetOpaqueQualType()),
+ clang::CharUnits::fromQuantity(member_byte_offset)));
+ }
+ }
+ }
+ } break;
+
+ default:
+ break;
+ }
+ }
+
+ return true;
+}
+
+size_t DWARFASTParserClang::ParseChildParameters(
+ clang::DeclContext *containing_decl_ctx, const DWARFDIE &parent_die,
+ bool skip_artificial, bool &is_static, bool &is_variadic,
+ bool &has_template_params, std::vector<CompilerType> &function_param_types,
+ std::vector<clang::ParmVarDecl *> &function_param_decls,
+ unsigned &type_quals) {
+ if (!parent_die)
+ return 0;
+
+ size_t arg_idx = 0;
+ for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid();
+ die = die.GetSibling()) {
+ const dw_tag_t tag = die.Tag();
+ switch (tag) {
+ case DW_TAG_formal_parameter: {
+ DWARFAttributes attributes;
+ const size_t num_attributes = die.GetAttributes(attributes);
+ if (num_attributes > 0) {
+ const char *name = nullptr;
+ DWARFFormValue param_type_die_form;
+ bool is_artificial = false;
+ // one of None, Auto, Register, Extern, Static, PrivateExtern
+
+ clang::StorageClass storage = clang::SC_None;
+ uint32_t i;
+ for (i = 0; i < num_attributes; ++i) {
+ const dw_attr_t attr = attributes.AttributeAtIndex(i);
+ DWARFFormValue form_value;
+ if (attributes.ExtractFormValueAtIndex(i, form_value)) {
+ switch (attr) {
+ case DW_AT_name:
+ name = form_value.AsCString();
+ break;
+ case DW_AT_type:
+ param_type_die_form = form_value;
+ break;
+ case DW_AT_artificial:
+ is_artificial = form_value.Boolean();
+ break;
+ case DW_AT_location:
+ case DW_AT_const_value:
+ case DW_AT_default_value:
+ case DW_AT_description:
+ case DW_AT_endianity:
+ case DW_AT_is_optional:
+ case DW_AT_segment:
+ case DW_AT_variable_parameter:
+ default:
+ case DW_AT_abstract_origin:
+ case DW_AT_sibling:
+ break;
+ }
+ }
+ }
+
+ bool skip = false;
+ if (skip_artificial && is_artificial) {
+ // In order to determine if a C++ member function is "const" we
+ // have to look at the const-ness of "this"...
+ if (arg_idx == 0 &&
+ DeclKindIsCXXClass(containing_decl_ctx->getDeclKind()) &&
+ // Often times compilers omit the "this" name for the
+ // specification DIEs, so we can't rely upon the name being in
+ // the formal parameter DIE...
+ (name == nullptr || ::strcmp(name, "this") == 0)) {
+ Type *this_type =
+ die.ResolveTypeUID(param_type_die_form.Reference());
+ if (this_type) {
+ uint32_t encoding_mask = this_type->GetEncodingMask();
+ if (encoding_mask & Type::eEncodingIsPointerUID) {
+ is_static = false;
+
+ if (encoding_mask & (1u << Type::eEncodingIsConstUID))
+ type_quals |= clang::Qualifiers::Const;
+ if (encoding_mask & (1u << Type::eEncodingIsVolatileUID))
+ type_quals |= clang::Qualifiers::Volatile;
+ }
+ }
+ }
+ skip = true;
+ }
+
+ if (!skip) {
+ Type *type = die.ResolveTypeUID(param_type_die_form.Reference());
+ if (type) {
+ function_param_types.push_back(type->GetForwardCompilerType());
+
+ clang::ParmVarDecl *param_var_decl =
+ m_ast.CreateParameterDeclaration(containing_decl_ctx, name,
+ type->GetForwardCompilerType(),
+ storage);
+ assert(param_var_decl);
+ function_param_decls.push_back(param_var_decl);
+
+ m_ast.SetMetadataAsUserID(param_var_decl, die.GetID());
+ }
+ }
+ }
+ arg_idx++;
+ } break;
+
+ case DW_TAG_unspecified_parameters:
+ is_variadic = true;
+ break;
+
+ case DW_TAG_template_type_parameter:
+ case DW_TAG_template_value_parameter:
+ case DW_TAG_GNU_template_parameter_pack:
+ // The one caller of this was never using the template_param_infos, and
+ // the local variable was taking up a large amount of stack space in
+ // SymbolFileDWARF::ParseType() so this was removed. If we ever need the
+ // template params back, we can add them back.
+ // ParseTemplateDIE (dwarf_cu, die, template_param_infos);
+ has_template_params = true;
+ break;
+
+ default:
+ break;
+ }
+ }
+ return arg_idx;
+}
+
+llvm::Optional<SymbolFile::ArrayInfo>
+DWARFASTParser::ParseChildArrayInfo(const DWARFDIE &parent_die,
+ const ExecutionContext *exe_ctx) {
+ SymbolFile::ArrayInfo array_info;
+ if (!parent_die)
+ return llvm::None;
+
+ for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid();
+ die = die.GetSibling()) {
+ const dw_tag_t tag = die.Tag();
+ switch (tag) {
+ case DW_TAG_subrange_type: {
+ DWARFAttributes attributes;
+ const size_t num_child_attributes = die.GetAttributes(attributes);
+ if (num_child_attributes > 0) {
+ uint64_t num_elements = 0;
+ uint64_t lower_bound = 0;
+ uint64_t upper_bound = 0;
+ bool upper_bound_valid = false;
+ uint32_t i;
+ for (i = 0; i < num_child_attributes; ++i) {
+ const dw_attr_t attr = attributes.AttributeAtIndex(i);
+ DWARFFormValue form_value;
+ if (attributes.ExtractFormValueAtIndex(i, form_value)) {
+ switch (attr) {
+ case DW_AT_name:
+ break;
+
+ case DW_AT_count:
+ if (DWARFDIE var_die = die.GetReferencedDIE(DW_AT_count)) {
+ if (var_die.Tag() == DW_TAG_variable)
+ if (exe_ctx) {
+ if (auto frame = exe_ctx->GetFrameSP()) {
+ Status error;
+ lldb::VariableSP var_sp;
+ auto valobj_sp = frame->GetValueForVariableExpressionPath(
+ var_die.GetName(), eNoDynamicValues, 0, var_sp,
+ error);
+ if (valobj_sp) {
+ num_elements = valobj_sp->GetValueAsUnsigned(0);
+ break;
+ }
+ }
+ }
+ } else
+ num_elements = form_value.Unsigned();
+ break;
+
+ case DW_AT_bit_stride:
+ array_info.bit_stride = form_value.Unsigned();
+ break;
+
+ case DW_AT_byte_stride:
+ array_info.byte_stride = form_value.Unsigned();
+ break;
+
+ case DW_AT_lower_bound:
+ lower_bound = form_value.Unsigned();
+ break;
+
+ case DW_AT_upper_bound:
+ upper_bound_valid = true;
+ upper_bound = form_value.Unsigned();
+ break;
+
+ default:
+ case DW_AT_abstract_origin:
+ case DW_AT_accessibility:
+ case DW_AT_allocated:
+ case DW_AT_associated:
+ case DW_AT_data_location:
+ case DW_AT_declaration:
+ case DW_AT_description:
+ case DW_AT_sibling:
+ case DW_AT_threads_scaled:
+ case DW_AT_type:
+ case DW_AT_visibility:
+ break;
+ }
+ }
+ }
+
+ if (num_elements == 0) {
+ if (upper_bound_valid && upper_bound >= lower_bound)
+ num_elements = upper_bound - lower_bound + 1;
+ }
+
+ array_info.element_orders.push_back(num_elements);
+ }
+ } break;
+ }
+ }
+ return array_info;
+}
+
+Type *DWARFASTParserClang::GetTypeForDIE(const DWARFDIE &die) {
+ if (die) {
+ SymbolFileDWARF *dwarf = die.GetDWARF();
+ DWARFAttributes attributes;
+ const size_t num_attributes = die.GetAttributes(attributes);
+ if (num_attributes > 0) {
+ DWARFFormValue type_die_form;
+ for (size_t i = 0; i < num_attributes; ++i) {
+ dw_attr_t attr = attributes.AttributeAtIndex(i);
+ DWARFFormValue form_value;
+
+ if (attr == DW_AT_type &&
+ attributes.ExtractFormValueAtIndex(i, form_value))
+ return dwarf->ResolveTypeUID(form_value.Reference(), true);
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+clang::Decl *DWARFASTParserClang::GetClangDeclForDIE(const DWARFDIE &die) {
+ if (!die)
+ return nullptr;
+
+ switch (die.Tag()) {
+ case DW_TAG_variable:
+ case DW_TAG_constant:
+ case DW_TAG_formal_parameter:
+ case DW_TAG_imported_declaration:
+ case DW_TAG_imported_module:
+ break;
+ default:
+ return nullptr;
+ }
+
+ DIEToDeclMap::iterator cache_pos = m_die_to_decl.find(die.GetDIE());
+ if (cache_pos != m_die_to_decl.end())
+ return cache_pos->second;
+
+ if (DWARFDIE spec_die = die.GetReferencedDIE(DW_AT_specification)) {
+ clang::Decl *decl = GetClangDeclForDIE(spec_die);
+ m_die_to_decl[die.GetDIE()] = decl;
+ m_decl_to_die[decl].insert(die.GetDIE());
+ return decl;
+ }
+
+ if (DWARFDIE abstract_origin_die =
+ die.GetReferencedDIE(DW_AT_abstract_origin)) {
+ clang::Decl *decl = GetClangDeclForDIE(abstract_origin_die);
+ m_die_to_decl[die.GetDIE()] = decl;
+ m_decl_to_die[decl].insert(die.GetDIE());
+ return decl;
+ }
+
+ clang::Decl *decl = nullptr;
+ switch (die.Tag()) {
+ case DW_TAG_variable:
+ case DW_TAG_constant:
+ case DW_TAG_formal_parameter: {
+ SymbolFileDWARF *dwarf = die.GetDWARF();
+ Type *type = GetTypeForDIE(die);
+ if (dwarf && type) {
+ const char *name = die.GetName();
+ clang::DeclContext *decl_context =
+ ClangASTContext::DeclContextGetAsDeclContext(
+ dwarf->GetDeclContextContainingUID(die.GetID()));
+ decl = m_ast.CreateVariableDeclaration(
+ decl_context, name,
+ ClangUtil::GetQualType(type->GetForwardCompilerType()));
+ }
+ break;
+ }
+ case DW_TAG_imported_declaration: {
+ SymbolFileDWARF *dwarf = die.GetDWARF();
+ DWARFDIE imported_uid = die.GetAttributeValueAsReferenceDIE(DW_AT_import);
+ if (imported_uid) {
+ CompilerDecl imported_decl = imported_uid.GetDecl();
+ if (imported_decl) {
+ clang::DeclContext *decl_context =
+ ClangASTContext::DeclContextGetAsDeclContext(
+ dwarf->GetDeclContextContainingUID(die.GetID()));
+ if (clang::NamedDecl *clang_imported_decl =
+ llvm::dyn_cast<clang::NamedDecl>(
+ (clang::Decl *)imported_decl.GetOpaqueDecl()))
+ decl =
+ m_ast.CreateUsingDeclaration(decl_context, clang_imported_decl);
+ }
+ }
+ break;
+ }
+ case DW_TAG_imported_module: {
+ SymbolFileDWARF *dwarf = die.GetDWARF();
+ DWARFDIE imported_uid = die.GetAttributeValueAsReferenceDIE(DW_AT_import);
+
+ if (imported_uid) {
+ CompilerDeclContext imported_decl_ctx = imported_uid.GetDeclContext();
+ if (imported_decl_ctx) {
+ clang::DeclContext *decl_context =
+ ClangASTContext::DeclContextGetAsDeclContext(
+ dwarf->GetDeclContextContainingUID(die.GetID()));
+ if (clang::NamespaceDecl *ns_decl =
+ ClangASTContext::DeclContextGetAsNamespaceDecl(
+ imported_decl_ctx))
+ decl = m_ast.CreateUsingDirectiveDeclaration(decl_context, ns_decl);
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ m_die_to_decl[die.GetDIE()] = decl;
+ m_decl_to_die[decl].insert(die.GetDIE());
+
+ return decl;
+}
+
+clang::DeclContext *
+DWARFASTParserClang::GetClangDeclContextForDIE(const DWARFDIE &die) {
+ if (die) {
+ clang::DeclContext *decl_ctx = GetCachedClangDeclContextForDIE(die);
+ if (decl_ctx)
+ return decl_ctx;
+
+ bool try_parsing_type = true;
+ switch (die.Tag()) {
+ case DW_TAG_compile_unit:
+ case DW_TAG_partial_unit:
+ decl_ctx = m_ast.GetTranslationUnitDecl();
+ try_parsing_type = false;
+ break;
+
+ case DW_TAG_namespace:
+ decl_ctx = ResolveNamespaceDIE(die);
+ try_parsing_type = false;
+ break;
+
+ case DW_TAG_lexical_block:
+ decl_ctx = GetDeclContextForBlock(die);
+ try_parsing_type = false;
+ break;
+
+ default:
+ break;
+ }
+
+ if (decl_ctx == nullptr && try_parsing_type) {
+ Type *type = die.GetDWARF()->ResolveType(die);
+ if (type)
+ decl_ctx = GetCachedClangDeclContextForDIE(die);
+ }
+
+ if (decl_ctx) {
+ LinkDeclContextToDIE(decl_ctx, die);
+ return decl_ctx;
+ }
+ }
+ return nullptr;
+}
+
+static bool IsSubroutine(const DWARFDIE &die) {
+ switch (die.Tag()) {
+ case DW_TAG_subprogram:
+ case DW_TAG_inlined_subroutine:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static DWARFDIE GetContainingFunctionWithAbstractOrigin(const DWARFDIE &die) {
+ for (DWARFDIE candidate = die; candidate; candidate = candidate.GetParent()) {
+ if (IsSubroutine(candidate)) {
+ if (candidate.GetReferencedDIE(DW_AT_abstract_origin)) {
+ return candidate;
+ } else {
+ return DWARFDIE();
+ }
+ }
+ }
+ assert(0 && "Shouldn't call GetContainingFunctionWithAbstractOrigin on "
+ "something not in a function");
+ return DWARFDIE();
+}
+
+static DWARFDIE FindAnyChildWithAbstractOrigin(const DWARFDIE &context) {
+ for (DWARFDIE candidate = context.GetFirstChild(); candidate.IsValid();
+ candidate = candidate.GetSibling()) {
+ if (candidate.GetReferencedDIE(DW_AT_abstract_origin)) {
+ return candidate;
+ }
+ }
+ return DWARFDIE();
+}
+
+static DWARFDIE FindFirstChildWithAbstractOrigin(const DWARFDIE &block,
+ const DWARFDIE &function) {
+ assert(IsSubroutine(function));
+ for (DWARFDIE context = block; context != function.GetParent();
+ context = context.GetParent()) {
+ assert(!IsSubroutine(context) || context == function);
+ if (DWARFDIE child = FindAnyChildWithAbstractOrigin(context)) {
+ return child;
+ }
+ }
+ return DWARFDIE();
+}
+
+clang::DeclContext *
+DWARFASTParserClang::GetDeclContextForBlock(const DWARFDIE &die) {
+ assert(die.Tag() == DW_TAG_lexical_block);
+ DWARFDIE containing_function_with_abstract_origin =
+ GetContainingFunctionWithAbstractOrigin(die);
+ if (!containing_function_with_abstract_origin) {
+ return (clang::DeclContext *)ResolveBlockDIE(die);
+ }
+ DWARFDIE child = FindFirstChildWithAbstractOrigin(
+ die, containing_function_with_abstract_origin);
+ CompilerDeclContext decl_context =
+ GetDeclContextContainingUIDFromDWARF(child);
+ return (clang::DeclContext *)decl_context.GetOpaqueDeclContext();
+}
+
+clang::BlockDecl *DWARFASTParserClang::ResolveBlockDIE(const DWARFDIE &die) {
+ if (die && die.Tag() == DW_TAG_lexical_block) {
+ clang::BlockDecl *decl =
+ llvm::cast_or_null<clang::BlockDecl>(m_die_to_decl_ctx[die.GetDIE()]);
+
+ if (!decl) {
+ DWARFDIE decl_context_die;
+ clang::DeclContext *decl_context =
+ GetClangDeclContextContainingDIE(die, &decl_context_die);
+ decl = m_ast.CreateBlockDeclaration(decl_context);
+
+ if (decl)
+ LinkDeclContextToDIE((clang::DeclContext *)decl, die);
+ }
+
+ return decl;
+ }
+ return nullptr;
+}
+
+clang::NamespaceDecl *
+DWARFASTParserClang::ResolveNamespaceDIE(const DWARFDIE &die) {
+ if (die && die.Tag() == DW_TAG_namespace) {
+ // See if we already parsed this namespace DIE and associated it with a
+ // uniqued namespace declaration
+ clang::NamespaceDecl *namespace_decl =
+ static_cast<clang::NamespaceDecl *>(m_die_to_decl_ctx[die.GetDIE()]);
+ if (namespace_decl)
+ return namespace_decl;
+ else {
+ const char *namespace_name = die.GetName();
+ clang::DeclContext *containing_decl_ctx =
+ GetClangDeclContextContainingDIE(die, nullptr);
+ bool is_inline =
+ die.GetAttributeValueAsUnsigned(DW_AT_export_symbols, 0) != 0;
+
+ namespace_decl = m_ast.GetUniqueNamespaceDeclaration(
+ namespace_name, containing_decl_ctx, is_inline);
+ Log *log =
+ nullptr; // (LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO));
+ if (log) {
+ SymbolFileDWARF *dwarf = die.GetDWARF();
+ if (namespace_name) {
+ dwarf->GetObjectFile()->GetModule()->LogMessage(
+ log, "ASTContext => %p: 0x%8.8" PRIx64
+ ": DW_TAG_namespace with DW_AT_name(\"%s\") => "
+ "clang::NamespaceDecl *%p (original = %p)",
+ static_cast<void *>(m_ast.getASTContext()), die.GetID(),
+ namespace_name, static_cast<void *>(namespace_decl),
+ static_cast<void *>(namespace_decl->getOriginalNamespace()));
+ } else {
+ dwarf->GetObjectFile()->GetModule()->LogMessage(
+ log, "ASTContext => %p: 0x%8.8" PRIx64
+ ": DW_TAG_namespace (anonymous) => clang::NamespaceDecl *%p "
+ "(original = %p)",
+ static_cast<void *>(m_ast.getASTContext()), die.GetID(),
+ static_cast<void *>(namespace_decl),
+ static_cast<void *>(namespace_decl->getOriginalNamespace()));
+ }
+ }
+
+ if (namespace_decl)
+ LinkDeclContextToDIE((clang::DeclContext *)namespace_decl, die);
+ return namespace_decl;
+ }
+ }
+ return nullptr;
+}
+
+clang::DeclContext *DWARFASTParserClang::GetClangDeclContextContainingDIE(
+ const DWARFDIE &die, DWARFDIE *decl_ctx_die_copy) {
+ SymbolFileDWARF *dwarf = die.GetDWARF();
+
+ DWARFDIE decl_ctx_die = dwarf->GetDeclContextDIEContainingDIE(die);
+
+ if (decl_ctx_die_copy)
+ *decl_ctx_die_copy = decl_ctx_die;
+
+ if (decl_ctx_die) {
+ clang::DeclContext *clang_decl_ctx =
+ GetClangDeclContextForDIE(decl_ctx_die);
+ if (clang_decl_ctx)
+ return clang_decl_ctx;
+ }
+ return m_ast.GetTranslationUnitDecl();
+}
+
+clang::DeclContext *
+DWARFASTParserClang::GetCachedClangDeclContextForDIE(const DWARFDIE &die) {
+ if (die) {
+ DIEToDeclContextMap::iterator pos = m_die_to_decl_ctx.find(die.GetDIE());
+ if (pos != m_die_to_decl_ctx.end())
+ return pos->second;
+ }
+ return nullptr;
+}
+
+void DWARFASTParserClang::LinkDeclContextToDIE(clang::DeclContext *decl_ctx,
+ const DWARFDIE &die) {
+ m_die_to_decl_ctx[die.GetDIE()] = decl_ctx;
+ // There can be many DIEs for a single decl context
+ // m_decl_ctx_to_die[decl_ctx].insert(die.GetDIE());
+ m_decl_ctx_to_die.insert(std::make_pair(decl_ctx, die));
+}
+
+bool DWARFASTParserClang::CopyUniqueClassMethodTypes(
+ const DWARFDIE &src_class_die, const DWARFDIE &dst_class_die,
+ lldb_private::Type *class_type, std::vector<DWARFDIE> &failures) {
+ if (!class_type || !src_class_die || !dst_class_die)
+ return false;
+ if (src_class_die.Tag() != dst_class_die.Tag())
+ return false;
+
+ // We need to complete the class type so we can get all of the method types
+ // parsed so we can then unique those types to their equivalent counterparts
+ // in "dst_cu" and "dst_class_die"
+ class_type->GetFullCompilerType();
+
+ DWARFDIE src_die;
+ DWARFDIE dst_die;
+ UniqueCStringMap<DWARFDIE> src_name_to_die;
+ UniqueCStringMap<DWARFDIE> dst_name_to_die;
+ UniqueCStringMap<DWARFDIE> src_name_to_die_artificial;
+ UniqueCStringMap<DWARFDIE> dst_name_to_die_artificial;
+ for (src_die = src_class_die.GetFirstChild(); src_die.IsValid();
+ src_die = src_die.GetSibling()) {
+ if (src_die.Tag() == DW_TAG_subprogram) {
+ // Make sure this is a declaration and not a concrete instance by looking
+ // for DW_AT_declaration set to 1. Sometimes concrete function instances
+ // are placed inside the class definitions and shouldn't be included in
+ // the list of things are are tracking here.
+ if (src_die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0) == 1) {
+ const char *src_name = src_die.GetMangledName();
+ if (src_name) {
+ ConstString src_const_name(src_name);
+ if (src_die.GetAttributeValueAsUnsigned(DW_AT_artificial, 0))
+ src_name_to_die_artificial.Append(src_const_name, src_die);
+ else
+ src_name_to_die.Append(src_const_name, src_die);
+ }
+ }
+ }
+ }
+ for (dst_die = dst_class_die.GetFirstChild(); dst_die.IsValid();
+ dst_die = dst_die.GetSibling()) {
+ if (dst_die.Tag() == DW_TAG_subprogram) {
+ // Make sure this is a declaration and not a concrete instance by looking
+ // for DW_AT_declaration set to 1. Sometimes concrete function instances
+ // are placed inside the class definitions and shouldn't be included in
+ // the list of things are are tracking here.
+ if (dst_die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0) == 1) {
+ const char *dst_name = dst_die.GetMangledName();
+ if (dst_name) {
+ ConstString dst_const_name(dst_name);
+ if (dst_die.GetAttributeValueAsUnsigned(DW_AT_artificial, 0))
+ dst_name_to_die_artificial.Append(dst_const_name, dst_die);
+ else
+ dst_name_to_die.Append(dst_const_name, dst_die);
+ }
+ }
+ }
+ }
+ const uint32_t src_size = src_name_to_die.GetSize();
+ const uint32_t dst_size = dst_name_to_die.GetSize();
+ Log *log = nullptr; // (LogChannelDWARF::GetLogIfAny(DWARF_LOG_DEBUG_INFO |
+ // DWARF_LOG_TYPE_COMPLETION));
+
+ // Is everything kosher so we can go through the members at top speed?
+ bool fast_path = true;
+
+ if (src_size != dst_size) {
+ if (src_size != 0 && dst_size != 0) {
+ if (log)
+ log->Printf("warning: trying to unique class DIE 0x%8.8x to 0x%8.8x, "
+ "but they didn't have the same size (src=%d, dst=%d)",
+ src_class_die.GetOffset(), dst_class_die.GetOffset(),
+ src_size, dst_size);
+ }
+
+ fast_path = false;
+ }
+
+ uint32_t idx;
+
+ if (fast_path) {
+ for (idx = 0; idx < src_size; ++idx) {
+ src_die = src_name_to_die.GetValueAtIndexUnchecked(idx);
+ dst_die = dst_name_to_die.GetValueAtIndexUnchecked(idx);
+
+ if (src_die.Tag() != dst_die.Tag()) {
+ if (log)
+ log->Printf("warning: tried to unique class DIE 0x%8.8x to 0x%8.8x, "
+ "but 0x%8.8x (%s) tags didn't match 0x%8.8x (%s)",
+ src_class_die.GetOffset(), dst_class_die.GetOffset(),
+ src_die.GetOffset(), src_die.GetTagAsCString(),
+ dst_die.GetOffset(), dst_die.GetTagAsCString());
+ fast_path = false;
+ }
+
+ const char *src_name = src_die.GetMangledName();
+ const char *dst_name = dst_die.GetMangledName();
+
+ // Make sure the names match
+ if (src_name == dst_name || (strcmp(src_name, dst_name) == 0))
+ continue;
+
+ if (log)
+ log->Printf("warning: tried to unique class DIE 0x%8.8x to 0x%8.8x, "
+ "but 0x%8.8x (%s) names didn't match 0x%8.8x (%s)",
+ src_class_die.GetOffset(), dst_class_die.GetOffset(),
+ src_die.GetOffset(), src_name, dst_die.GetOffset(),
+ dst_name);
+
+ fast_path = false;
+ }
+ }
+
+ DWARFASTParserClang *src_dwarf_ast_parser =
+ (DWARFASTParserClang *)src_die.GetDWARFParser();
+ DWARFASTParserClang *dst_dwarf_ast_parser =
+ (DWARFASTParserClang *)dst_die.GetDWARFParser();
+
+ // Now do the work of linking the DeclContexts and Types.
+ if (fast_path) {
+ // We can do this quickly. Just run across the tables index-for-index
+ // since we know each node has matching names and tags.
+ for (idx = 0; idx < src_size; ++idx) {
+ src_die = src_name_to_die.GetValueAtIndexUnchecked(idx);
+ dst_die = dst_name_to_die.GetValueAtIndexUnchecked(idx);
+
+ clang::DeclContext *src_decl_ctx =
+ src_dwarf_ast_parser->m_die_to_decl_ctx[src_die.GetDIE()];
+ if (src_decl_ctx) {
+ if (log)
+ log->Printf("uniquing decl context %p from 0x%8.8x for 0x%8.8x",
+ static_cast<void *>(src_decl_ctx), src_die.GetOffset(),
+ dst_die.GetOffset());
+ dst_dwarf_ast_parser->LinkDeclContextToDIE(src_decl_ctx, dst_die);
+ } else {
+ if (log)
+ log->Printf("warning: tried to unique decl context from 0x%8.8x for "
+ "0x%8.8x, but none was found",
+ src_die.GetOffset(), dst_die.GetOffset());
+ }
+
+ Type *src_child_type =
+ dst_die.GetDWARF()->GetDIEToType()[src_die.GetDIE()];
+ if (src_child_type) {
+ if (log)
+ log->Printf(
+ "uniquing type %p (uid=0x%" PRIx64 ") from 0x%8.8x for 0x%8.8x",
+ static_cast<void *>(src_child_type), src_child_type->GetID(),
+ src_die.GetOffset(), dst_die.GetOffset());
+ dst_die.GetDWARF()->GetDIEToType()[dst_die.GetDIE()] = src_child_type;
+ } else {
+ if (log)
+ log->Printf("warning: tried to unique lldb_private::Type from "
+ "0x%8.8x for 0x%8.8x, but none was found",
+ src_die.GetOffset(), dst_die.GetOffset());
+ }
+ }
+ } else {
+ // We must do this slowly. For each member of the destination, look up a
+ // member in the source with the same name, check its tag, and unique them
+ // if everything matches up. Report failures.
+
+ if (!src_name_to_die.IsEmpty() && !dst_name_to_die.IsEmpty()) {
+ src_name_to_die.Sort();
+
+ for (idx = 0; idx < dst_size; ++idx) {
+ ConstString dst_name = dst_name_to_die.GetCStringAtIndex(idx);
+ dst_die = dst_name_to_die.GetValueAtIndexUnchecked(idx);
+ src_die = src_name_to_die.Find(dst_name, DWARFDIE());
+
+ if (src_die && (src_die.Tag() == dst_die.Tag())) {
+ clang::DeclContext *src_decl_ctx =
+ src_dwarf_ast_parser->m_die_to_decl_ctx[src_die.GetDIE()];
+ if (src_decl_ctx) {
+ if (log)
+ log->Printf("uniquing decl context %p from 0x%8.8x for 0x%8.8x",
+ static_cast<void *>(src_decl_ctx),
+ src_die.GetOffset(), dst_die.GetOffset());
+ dst_dwarf_ast_parser->LinkDeclContextToDIE(src_decl_ctx, dst_die);
+ } else {
+ if (log)
+ log->Printf("warning: tried to unique decl context from 0x%8.8x "
+ "for 0x%8.8x, but none was found",
+ src_die.GetOffset(), dst_die.GetOffset());
+ }
+
+ Type *src_child_type =
+ dst_die.GetDWARF()->GetDIEToType()[src_die.GetDIE()];
+ if (src_child_type) {
+ if (log)
+ log->Printf("uniquing type %p (uid=0x%" PRIx64
+ ") from 0x%8.8x for 0x%8.8x",
+ static_cast<void *>(src_child_type),
+ src_child_type->GetID(), src_die.GetOffset(),
+ dst_die.GetOffset());
+ dst_die.GetDWARF()->GetDIEToType()[dst_die.GetDIE()] =
+ src_child_type;
+ } else {
+ if (log)
+ log->Printf("warning: tried to unique lldb_private::Type from "
+ "0x%8.8x for 0x%8.8x, but none was found",
+ src_die.GetOffset(), dst_die.GetOffset());
+ }
+ } else {
+ if (log)
+ log->Printf("warning: couldn't find a match for 0x%8.8x",
+ dst_die.GetOffset());
+
+ failures.push_back(dst_die);
+ }
+ }
+ }
+ }
+
+ const uint32_t src_size_artificial = src_name_to_die_artificial.GetSize();
+ const uint32_t dst_size_artificial = dst_name_to_die_artificial.GetSize();
+
+ if (src_size_artificial && dst_size_artificial) {
+ dst_name_to_die_artificial.Sort();
+
+ for (idx = 0; idx < src_size_artificial; ++idx) {
+ ConstString src_name_artificial =
+ src_name_to_die_artificial.GetCStringAtIndex(idx);
+ src_die = src_name_to_die_artificial.GetValueAtIndexUnchecked(idx);
+ dst_die =
+ dst_name_to_die_artificial.Find(src_name_artificial, DWARFDIE());
+
+ if (dst_die) {
+ // Both classes have the artificial types, link them
+ clang::DeclContext *src_decl_ctx =
+ src_dwarf_ast_parser->m_die_to_decl_ctx[src_die.GetDIE()];
+ if (src_decl_ctx) {
+ if (log)
+ log->Printf("uniquing decl context %p from 0x%8.8x for 0x%8.8x",
+ static_cast<void *>(src_decl_ctx), src_die.GetOffset(),
+ dst_die.GetOffset());
+ dst_dwarf_ast_parser->LinkDeclContextToDIE(src_decl_ctx, dst_die);
+ } else {
+ if (log)
+ log->Printf("warning: tried to unique decl context from 0x%8.8x "
+ "for 0x%8.8x, but none was found",
+ src_die.GetOffset(), dst_die.GetOffset());
+ }
+
+ Type *src_child_type =
+ dst_die.GetDWARF()->GetDIEToType()[src_die.GetDIE()];
+ if (src_child_type) {
+ if (log)
+ log->Printf(
+ "uniquing type %p (uid=0x%" PRIx64 ") from 0x%8.8x for 0x%8.8x",
+ static_cast<void *>(src_child_type), src_child_type->GetID(),
+ src_die.GetOffset(), dst_die.GetOffset());
+ dst_die.GetDWARF()->GetDIEToType()[dst_die.GetDIE()] = src_child_type;
+ } else {
+ if (log)
+ log->Printf("warning: tried to unique lldb_private::Type from "
+ "0x%8.8x for 0x%8.8x, but none was found",
+ src_die.GetOffset(), dst_die.GetOffset());
+ }
+ }
+ }
+ }
+
+ if (dst_size_artificial) {
+ for (idx = 0; idx < dst_size_artificial; ++idx) {
+ ConstString dst_name_artificial =
+ dst_name_to_die_artificial.GetCStringAtIndex(idx);
+ dst_die = dst_name_to_die_artificial.GetValueAtIndexUnchecked(idx);
+ if (log)
+ log->Printf("warning: need to create artificial method for 0x%8.8x for "
+ "method '%s'",
+ dst_die.GetOffset(), dst_name_artificial.GetCString());
+
+ failures.push_back(dst_die);
+ }
+ }
+
+ return !failures.empty();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
new file mode 100644
index 000000000000..5b5d83d65932
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
@@ -0,0 +1,151 @@
+//===-- DWARFASTParserClang.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 SymbolFileDWARF_DWARFASTParserClang_h_
+#define SymbolFileDWARF_DWARFASTParserClang_h_
+
+#include "clang/AST/CharUnits.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallVector.h"
+
+#include "DWARFASTParser.h"
+#include "DWARFDefines.h"
+#include "lldb/Core/ClangForward.h"
+#include "lldb/Core/PluginInterface.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangASTImporter.h"
+
+#include <vector>
+
+namespace lldb_private {
+class CompileUnit;
+}
+class DWARFDebugInfoEntry;
+class SymbolFileDWARF;
+
+class DWARFASTParserClang : public DWARFASTParser {
+public:
+ DWARFASTParserClang(lldb_private::ClangASTContext &ast);
+
+ ~DWARFASTParserClang() override;
+
+ // DWARFASTParser interface.
+ lldb::TypeSP ParseTypeFromDWARF(const lldb_private::SymbolContext &sc,
+ const DWARFDIE &die, lldb_private::Log *log,
+ bool *type_is_new_ptr) override;
+
+ lldb_private::Function *
+ ParseFunctionFromDWARF(lldb_private::CompileUnit &comp_unit,
+ const DWARFDIE &die) override;
+
+ bool
+ CompleteTypeFromDWARF(const DWARFDIE &die, lldb_private::Type *type,
+ lldb_private::CompilerType &compiler_type) override;
+
+ lldb_private::CompilerDecl
+ GetDeclForUIDFromDWARF(const DWARFDIE &die) override;
+
+ std::vector<DWARFDIE>
+ GetDIEForDeclContext(lldb_private::CompilerDeclContext decl_context) override;
+
+ lldb_private::CompilerDeclContext
+ GetDeclContextForUIDFromDWARF(const DWARFDIE &die) override;
+
+ lldb_private::CompilerDeclContext
+ GetDeclContextContainingUIDFromDWARF(const DWARFDIE &die) override;
+
+ lldb_private::ClangASTImporter &GetClangASTImporter();
+
+protected:
+ class DelayedAddObjCClassProperty;
+ typedef std::vector<DelayedAddObjCClassProperty> DelayedPropertyList;
+
+ clang::DeclContext *GetDeclContextForBlock(const DWARFDIE &die);
+
+ clang::BlockDecl *ResolveBlockDIE(const DWARFDIE &die);
+
+ clang::NamespaceDecl *ResolveNamespaceDIE(const DWARFDIE &die);
+
+ bool ParseTemplateDIE(const DWARFDIE &die,
+ lldb_private::ClangASTContext::TemplateParameterInfos
+ &template_param_infos);
+ bool ParseTemplateParameterInfos(
+ const DWARFDIE &parent_die,
+ lldb_private::ClangASTContext::TemplateParameterInfos
+ &template_param_infos);
+
+ bool ParseChildMembers(
+ const DWARFDIE &die, lldb_private::CompilerType &class_compiler_type,
+ const lldb::LanguageType class_language,
+ std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> &base_classes,
+ std::vector<int> &member_accessibilities,
+ std::vector<DWARFDIE> &member_function_dies,
+ DelayedPropertyList &delayed_properties,
+ lldb::AccessType &default_accessibility, bool &is_a_class,
+ lldb_private::ClangASTImporter::LayoutInfo &layout_info);
+
+ size_t
+ ParseChildParameters(clang::DeclContext *containing_decl_ctx,
+ const DWARFDIE &parent_die, bool skip_artificial,
+ bool &is_static, bool &is_variadic,
+ bool &has_template_params,
+ std::vector<lldb_private::CompilerType> &function_args,
+ std::vector<clang::ParmVarDecl *> &function_param_decls,
+ unsigned &type_quals);
+
+ size_t ParseChildEnumerators(lldb_private::CompilerType &compiler_type,
+ bool is_signed, uint32_t enumerator_byte_size,
+ const DWARFDIE &parent_die);
+
+ lldb_private::Type *GetTypeForDIE(const DWARFDIE &die);
+
+ clang::Decl *GetClangDeclForDIE(const DWARFDIE &die);
+
+ clang::DeclContext *GetClangDeclContextForDIE(const DWARFDIE &die);
+
+ clang::DeclContext *GetClangDeclContextContainingDIE(const DWARFDIE &die,
+ DWARFDIE *decl_ctx_die);
+
+ bool CopyUniqueClassMethodTypes(const DWARFDIE &src_class_die,
+ const DWARFDIE &dst_class_die,
+ lldb_private::Type *class_type,
+ std::vector<DWARFDIE> &failures);
+
+ clang::DeclContext *GetCachedClangDeclContextForDIE(const DWARFDIE &die);
+
+ void LinkDeclContextToDIE(clang::DeclContext *decl_ctx, const DWARFDIE &die);
+
+ void LinkDeclToDIE(clang::Decl *decl, const DWARFDIE &die);
+
+ lldb::TypeSP ParseTypeFromDWO(const DWARFDIE &die, lldb_private::Log *log);
+
+ // Return true if this type is a declaration to a type in an external
+ // module.
+ lldb::ModuleSP GetModuleForType(const DWARFDIE &die);
+
+ typedef llvm::SmallPtrSet<const DWARFDebugInfoEntry *, 4> DIEPointerSet;
+ typedef llvm::DenseMap<const DWARFDebugInfoEntry *, clang::DeclContext *>
+ DIEToDeclContextMap;
+ // typedef llvm::DenseMap<const clang::DeclContext *, DIEPointerSet>
+ // DeclContextToDIEMap;
+ typedef std::multimap<const clang::DeclContext *, const DWARFDIE>
+ DeclContextToDIEMap;
+ typedef llvm::DenseMap<const DWARFDebugInfoEntry *, clang::Decl *>
+ DIEToDeclMap;
+ typedef llvm::DenseMap<const clang::Decl *, DIEPointerSet> DeclToDIEMap;
+
+ lldb_private::ClangASTContext &m_ast;
+ DIEToDeclMap m_die_to_decl;
+ DeclToDIEMap m_decl_to_die;
+ DIEToDeclContextMap m_die_to_decl_ctx;
+ DeclContextToDIEMap m_decl_ctx_to_die;
+ std::unique_ptr<lldb_private::ClangASTImporter> m_clang_ast_importer_up;
+};
+
+#endif // SymbolFileDWARF_DWARFASTParserClang_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp
new file mode 100644
index 000000000000..6128163a2926
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp
@@ -0,0 +1,89 @@
+//===-- DWARFAbbreviationDeclaration.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 "DWARFAbbreviationDeclaration.h"
+
+#include "lldb/Core/dwarf.h"
+#include "lldb/Utility/Stream.h"
+
+#include "llvm/Object/Error.h"
+
+#include "DWARFFormValue.h"
+
+using namespace lldb_private;
+
+DWARFAbbreviationDeclaration::DWARFAbbreviationDeclaration()
+ : m_code(InvalidCode), m_tag(0), m_has_children(0), m_attributes() {}
+
+DWARFAbbreviationDeclaration::DWARFAbbreviationDeclaration(dw_tag_t tag,
+ uint8_t has_children)
+ : m_code(InvalidCode), m_tag(tag), m_has_children(has_children),
+ m_attributes() {}
+
+llvm::Expected<DWARFEnumState>
+DWARFAbbreviationDeclaration::extract(const DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr) {
+ m_code = data.GetULEB128(offset_ptr);
+ if (m_code == 0)
+ return DWARFEnumState::Complete;
+
+ m_attributes.clear();
+ m_tag = data.GetULEB128(offset_ptr);
+ if (m_tag == DW_TAG_null)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "abbrev decl requires non-null tag.");
+
+ m_has_children = data.GetU8(offset_ptr);
+
+ while (data.ValidOffset(*offset_ptr)) {
+ dw_attr_t attr = data.GetULEB128(offset_ptr);
+ dw_form_t form = data.GetULEB128(offset_ptr);
+
+ // This is the last attribute for this abbrev decl, but there may still be
+ // more abbrev decls, so return MoreItems to indicate to the caller that
+ // they should call this function again.
+ if (!attr && !form)
+ return DWARFEnumState::MoreItems;
+
+ if (!attr || !form)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "malformed abbreviation declaration attribute");
+
+ DWARFFormValue::ValueType val;
+
+ if (form == DW_FORM_implicit_const)
+ val.value.sval = data.GetULEB128(offset_ptr);
+
+ m_attributes.push_back(DWARFAttribute(attr, form, val));
+ }
+
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "abbreviation declaration attribute list not terminated with a null "
+ "entry");
+}
+
+bool DWARFAbbreviationDeclaration::IsValid() {
+ return m_code != 0 && m_tag != 0;
+}
+
+uint32_t
+DWARFAbbreviationDeclaration::FindAttributeIndex(dw_attr_t attr) const {
+ uint32_t i;
+ const uint32_t kNumAttributes = m_attributes.size();
+ for (i = 0; i < kNumAttributes; ++i) {
+ if (m_attributes[i].get_attr() == attr)
+ return i;
+ }
+ return DW_INVALID_INDEX;
+}
+
+bool DWARFAbbreviationDeclaration::
+operator==(const DWARFAbbreviationDeclaration &rhs) const {
+ return Tag() == rhs.Tag() && HasChildren() == rhs.HasChildren() &&
+ m_attributes == rhs.m_attributes;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.h
new file mode 100644
index 000000000000..c0cf8823a368
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.h
@@ -0,0 +1,65 @@
+//===-- DWARFAbbreviationDeclaration.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 liblldb_DWARFAbbreviationDeclaration_h_
+#define liblldb_DWARFAbbreviationDeclaration_h_
+
+#include "DWARFAttribute.h"
+#include "DWARFDefines.h"
+#include "SymbolFileDWARF.h"
+#include "llvm/Support/Error.h"
+
+class DWARFAbbreviationDeclaration {
+public:
+ enum { InvalidCode = 0 };
+ DWARFAbbreviationDeclaration();
+
+ // For hand crafting an abbreviation declaration
+ DWARFAbbreviationDeclaration(dw_tag_t tag, uint8_t has_children);
+
+ dw_uleb128_t Code() const { return m_code; }
+ void SetCode(dw_uleb128_t code) { m_code = code; }
+ dw_tag_t Tag() const { return m_tag; }
+ bool HasChildren() const { return m_has_children; }
+ size_t NumAttributes() const { return m_attributes.size(); }
+ dw_form_t GetFormByIndex(uint32_t idx) const {
+ return m_attributes.size() > idx ? m_attributes[idx].get_form() : 0;
+ }
+
+ // idx is assumed to be valid when calling GetAttrAndFormByIndex()
+ void GetAttrAndFormValueByIndex(uint32_t idx, dw_attr_t &attr,
+ DWARFFormValue &form_value) const {
+ m_attributes[idx].get(attr, form_value.FormRef(), form_value.ValueRef());
+ }
+ dw_form_t GetFormByIndexUnchecked(uint32_t idx) const {
+ return m_attributes[idx].get_form();
+ }
+ uint32_t FindAttributeIndex(dw_attr_t attr) const;
+
+ /// Extract one abbreviation declaration and all of its associated attributes.
+ /// Possible return values:
+ /// DWARFEnumState::Complete - the extraction completed successfully. This
+ /// was the last abbrev decl in a sequence, and the user should not call
+ /// this function again.
+ /// DWARFEnumState::MoreItems - the extraction completed successfully. The
+ /// user should call this function again to retrieve the next decl.
+ /// llvm::Error - A parsing error occurred. The debug info is malformed.
+ llvm::Expected<lldb_private::DWARFEnumState>
+ extract(const lldb_private::DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr);
+ bool IsValid();
+ bool operator==(const DWARFAbbreviationDeclaration &rhs) const;
+
+protected:
+ dw_uleb128_t m_code;
+ dw_tag_t m_tag;
+ uint8_t m_has_children;
+ DWARFAttribute::collection m_attributes;
+};
+
+#endif // liblldb_DWARFAbbreviationDeclaration_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.cpp
new file mode 100644
index 000000000000..b3594a455680
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.cpp
@@ -0,0 +1,58 @@
+//===-- DWARFAttribute.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 "DWARFAttribute.h"
+#include "DWARFUnit.h"
+#include "DWARFDebugInfo.h"
+
+DWARFAttributes::DWARFAttributes() : m_infos() {}
+
+DWARFAttributes::~DWARFAttributes() {}
+
+uint32_t DWARFAttributes::FindAttributeIndex(dw_attr_t attr) const {
+ collection::const_iterator end = m_infos.end();
+ collection::const_iterator beg = m_infos.begin();
+ collection::const_iterator pos;
+ for (pos = beg; pos != end; ++pos) {
+ if (pos->attr.get_attr() == attr)
+ return std::distance(beg, pos);
+ }
+ return UINT32_MAX;
+}
+
+void DWARFAttributes::Append(const DWARFUnit *cu, dw_offset_t attr_die_offset,
+ dw_attr_t attr, dw_form_t form) {
+ AttributeValue attr_value = {
+ cu, attr_die_offset, {attr, form, DWARFFormValue::ValueType()}};
+ m_infos.push_back(attr_value);
+}
+
+bool DWARFAttributes::ExtractFormValueAtIndex(
+ uint32_t i, DWARFFormValue &form_value) const {
+ const DWARFUnit *cu = CompileUnitAtIndex(i);
+ form_value.SetUnit(cu);
+ form_value.SetForm(FormAtIndex(i));
+ lldb::offset_t offset = DIEOffsetAtIndex(i);
+ return form_value.ExtractValue(cu->GetData(), &offset);
+}
+
+DWARFDIE
+DWARFAttributes::FormValueAsReference(dw_attr_t attr) const {
+ const uint32_t attr_idx = FindAttributeIndex(attr);
+ if (attr_idx != UINT32_MAX)
+ return FormValueAsReferenceAtIndex(attr_idx);
+ return {};
+}
+
+DWARFDIE
+DWARFAttributes::FormValueAsReferenceAtIndex(uint32_t i) const {
+ DWARFFormValue form_value;
+ if (ExtractFormValueAtIndex(i, form_value))
+ return form_value.Reference();
+ return {};
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.h
new file mode 100644
index 000000000000..58427b19100a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.h
@@ -0,0 +1,85 @@
+//===-- DWARFAttribute.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 SymbolFileDWARF_DWARFAttribute_h_
+#define SymbolFileDWARF_DWARFAttribute_h_
+
+#include "DWARFDefines.h"
+#include "DWARFFormValue.h"
+#include "llvm/ADT/SmallVector.h"
+#include <vector>
+
+class DWARFUnit;
+
+class DWARFAttribute {
+public:
+ DWARFAttribute(dw_attr_t attr, dw_form_t form,
+ DWARFFormValue::ValueType value)
+ : m_attr(attr), m_form(form), m_value(value) {}
+
+ void set(dw_attr_t attr, dw_form_t form) {
+ m_attr = attr;
+ m_form = form;
+ }
+ dw_attr_t get_attr() const { return m_attr; }
+ dw_form_t get_form() const { return m_form; }
+ void get(dw_attr_t &attr, dw_form_t &form,
+ DWARFFormValue::ValueType &val) const {
+ attr = m_attr;
+ form = m_form;
+ val = m_value;
+ }
+ bool operator==(const DWARFAttribute &rhs) const {
+ return m_attr == rhs.m_attr && m_form == rhs.m_form;
+ }
+ typedef std::vector<DWARFAttribute> collection;
+ typedef collection::iterator iterator;
+ typedef collection::const_iterator const_iterator;
+
+protected:
+ dw_attr_t m_attr;
+ dw_form_t m_form;
+ DWARFFormValue::ValueType m_value;
+};
+
+class DWARFAttributes {
+public:
+ DWARFAttributes();
+ ~DWARFAttributes();
+
+ void Append(const DWARFUnit *cu, dw_offset_t attr_die_offset,
+ dw_attr_t attr, dw_form_t form);
+ const DWARFUnit *CompileUnitAtIndex(uint32_t i) const {
+ return m_infos[i].cu;
+ }
+ dw_offset_t DIEOffsetAtIndex(uint32_t i) const {
+ return m_infos[i].die_offset;
+ }
+ dw_attr_t AttributeAtIndex(uint32_t i) const {
+ return m_infos[i].attr.get_attr();
+ }
+ dw_attr_t FormAtIndex(uint32_t i) const { return m_infos[i].attr.get_form(); }
+ bool ExtractFormValueAtIndex(uint32_t i, DWARFFormValue &form_value) const;
+ DWARFDIE FormValueAsReferenceAtIndex(uint32_t i) const;
+ DWARFDIE FormValueAsReference(dw_attr_t attr) const;
+ uint32_t FindAttributeIndex(dw_attr_t attr) const;
+ void Clear() { m_infos.clear(); }
+ size_t Size() const { return m_infos.size(); }
+
+protected:
+ struct AttributeValue {
+ const DWARFUnit *cu; // Keep the compile unit with each attribute in
+ // case we have DW_FORM_ref_addr values
+ dw_offset_t die_offset;
+ DWARFAttribute attr;
+ };
+ typedef llvm::SmallVector<AttributeValue, 8> collection;
+ collection m_infos;
+};
+
+#endif // SymbolFileDWARF_DWARFAttribute_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.cpp
new file mode 100644
index 000000000000..96adb72c9532
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.cpp
@@ -0,0 +1,149 @@
+//===-- DWARFBaseDIE.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 "DWARFBaseDIE.h"
+
+#include "DWARFUnit.h"
+#include "DWARFDebugInfoEntry.h"
+#include "SymbolFileDWARF.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/ObjectFile.h"
+
+using namespace lldb_private;
+
+llvm::Optional<DIERef> DWARFBaseDIE::GetDIERef() const {
+ if (!IsValid())
+ return llvm::None;
+
+ return DIERef(m_cu->GetSymbolFileDWARF().GetDwoNum(), m_cu->GetDebugSection(),
+ m_die->GetOffset());
+}
+
+dw_tag_t DWARFBaseDIE::Tag() const {
+ if (m_die)
+ return m_die->Tag();
+ else
+ return 0;
+}
+
+const char *DWARFBaseDIE::GetTagAsCString() const {
+ return lldb_private::DW_TAG_value_to_name(Tag());
+}
+
+const char *DWARFBaseDIE::GetAttributeValueAsString(const dw_attr_t attr,
+ const char *fail_value) const {
+ if (IsValid())
+ return m_die->GetAttributeValueAsString(GetCU(), attr, fail_value);
+ else
+ return fail_value;
+}
+
+uint64_t DWARFBaseDIE::GetAttributeValueAsUnsigned(const dw_attr_t attr,
+ uint64_t fail_value) const {
+ if (IsValid())
+ return m_die->GetAttributeValueAsUnsigned(GetCU(), attr, fail_value);
+ else
+ return fail_value;
+}
+
+uint64_t DWARFBaseDIE::GetAttributeValueAsAddress(const dw_attr_t attr,
+ uint64_t fail_value) const {
+ if (IsValid())
+ return m_die->GetAttributeValueAsAddress(GetCU(), attr, fail_value);
+ else
+ return fail_value;
+}
+
+lldb::user_id_t DWARFBaseDIE::GetID() const {
+ if (IsValid())
+ return GetDWARF()->GetUID(*this);
+ return LLDB_INVALID_UID;
+}
+
+const char *DWARFBaseDIE::GetName() const {
+ if (IsValid())
+ return m_die->GetName(m_cu);
+ else
+ return nullptr;
+}
+
+lldb::LanguageType DWARFBaseDIE::GetLanguage() const {
+ if (IsValid())
+ return m_cu->GetLanguageType();
+ else
+ return lldb::eLanguageTypeUnknown;
+}
+
+lldb::ModuleSP DWARFBaseDIE::GetModule() const {
+ SymbolFileDWARF *dwarf = GetDWARF();
+ if (dwarf)
+ return dwarf->GetObjectFile()->GetModule();
+ else
+ return lldb::ModuleSP();
+}
+
+dw_offset_t DWARFBaseDIE::GetOffset() const {
+ if (IsValid())
+ return m_die->GetOffset();
+ else
+ return DW_INVALID_OFFSET;
+}
+
+SymbolFileDWARF *DWARFBaseDIE::GetDWARF() const {
+ if (m_cu)
+ return &m_cu->GetSymbolFileDWARF();
+ else
+ return nullptr;
+}
+
+lldb_private::TypeSystem *DWARFBaseDIE::GetTypeSystem() const {
+ if (m_cu)
+ return m_cu->GetTypeSystem();
+ else
+ return nullptr;
+}
+
+DWARFASTParser *DWARFBaseDIE::GetDWARFParser() const {
+ lldb_private::TypeSystem *type_system = GetTypeSystem();
+ if (type_system)
+ return type_system->GetDWARFParser();
+ else
+ return nullptr;
+}
+
+bool DWARFBaseDIE::HasChildren() const {
+ return m_die && m_die->HasChildren();
+}
+
+bool DWARFBaseDIE::Supports_DW_AT_APPLE_objc_complete_type() const {
+ return IsValid() && GetDWARF()->Supports_DW_AT_APPLE_objc_complete_type(m_cu);
+}
+
+size_t DWARFBaseDIE::GetAttributes(DWARFAttributes &attributes,
+ uint32_t depth) const {
+ if (IsValid())
+ return m_die->GetAttributes(m_cu, attributes, depth);
+ if (depth == 0)
+ attributes.Clear();
+ return 0;
+}
+
+bool operator==(const DWARFBaseDIE &lhs, const DWARFBaseDIE &rhs) {
+ return lhs.GetDIE() == rhs.GetDIE() && lhs.GetCU() == rhs.GetCU();
+}
+
+bool operator!=(const DWARFBaseDIE &lhs, const DWARFBaseDIE &rhs) {
+ return !(lhs == rhs);
+}
+
+const DWARFDataExtractor &DWARFBaseDIE::GetData() const {
+ // Clients must check if this DIE is valid before calling this function.
+ assert(IsValid());
+ return m_cu->GetData();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.h
new file mode 100644
index 000000000000..0058043017cd
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.h
@@ -0,0 +1,127 @@
+//===-- DWARFBaseDIE.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 SymbolFileDWARF_DWARFBaseDIE_h_
+#define SymbolFileDWARF_DWARFBaseDIE_h_
+
+#include "lldb/Core/dwarf.h"
+#include "lldb/lldb-types.h"
+
+class DIERef;
+class DWARFASTParser;
+class DWARFAttributes;
+class DWARFUnit;
+class DWARFDebugInfoEntry;
+class DWARFDeclContext;
+class SymbolFileDWARF;
+
+class DWARFBaseDIE {
+public:
+ DWARFBaseDIE() : m_cu(nullptr), m_die(nullptr) {}
+
+ DWARFBaseDIE(DWARFUnit *cu, DWARFDebugInfoEntry *die)
+ : m_cu(cu), m_die(die) {}
+
+ DWARFBaseDIE(const DWARFUnit *cu, DWARFDebugInfoEntry *die)
+ : m_cu(const_cast<DWARFUnit *>(cu)), m_die(die) {}
+
+ DWARFBaseDIE(DWARFUnit *cu, const DWARFDebugInfoEntry *die)
+ : m_cu(cu), m_die(const_cast<DWARFDebugInfoEntry *>(die)) {}
+
+ DWARFBaseDIE(const DWARFUnit *cu, const DWARFDebugInfoEntry *die)
+ : m_cu(const_cast<DWARFUnit *>(cu)),
+ m_die(const_cast<DWARFDebugInfoEntry *>(die)) {}
+
+ // Tests
+ explicit operator bool() const { return IsValid(); }
+
+ bool IsValid() const { return m_cu && m_die; }
+
+ bool HasChildren() const;
+
+ bool Supports_DW_AT_APPLE_objc_complete_type() const;
+
+ // Accessors
+ SymbolFileDWARF *GetDWARF() const;
+
+ DWARFUnit *GetCU() const { return m_cu; }
+
+ DWARFDebugInfoEntry *GetDIE() const { return m_die; }
+
+ llvm::Optional<DIERef> GetDIERef() const;
+
+ lldb_private::TypeSystem *GetTypeSystem() const;
+
+ DWARFASTParser *GetDWARFParser() const;
+
+ void Set(DWARFUnit *cu, DWARFDebugInfoEntry *die) {
+ if (cu && die) {
+ m_cu = cu;
+ m_die = die;
+ } else {
+ Clear();
+ }
+ }
+
+ void Clear() {
+ m_cu = nullptr;
+ m_die = nullptr;
+ }
+
+ // Get the data that contains the attribute values for this DIE. Support
+ // for .debug_types means that any DIE can have its data either in the
+ // .debug_info or the .debug_types section; this method will return the
+ // correct section data.
+ //
+ // Clients must validate that this object is valid before calling this.
+ const lldb_private::DWARFDataExtractor &GetData() const;
+
+ // Accessing information about a DIE
+ dw_tag_t Tag() const;
+
+ const char *GetTagAsCString() const;
+
+ dw_offset_t GetOffset() const;
+
+ // Get the LLDB user ID for this DIE. This is often just the DIE offset,
+ // but it might have a SymbolFileDWARF::GetID() in the high 32 bits if
+ // we are doing Darwin DWARF in .o file, or DWARF stand alone debug
+ // info.
+ lldb::user_id_t GetID() const;
+
+ const char *GetName() const;
+
+ lldb::LanguageType GetLanguage() const;
+
+ lldb::ModuleSP GetModule() const;
+
+ // Getting attribute values from the DIE.
+ //
+ // GetAttributeValueAsXXX() functions should only be used if you are
+ // looking for one or two attributes on a DIE. If you are trying to
+ // parse all attributes, use GetAttributes (...) instead
+ const char *GetAttributeValueAsString(const dw_attr_t attr,
+ const char *fail_value) const;
+
+ uint64_t GetAttributeValueAsUnsigned(const dw_attr_t attr,
+ uint64_t fail_value) const;
+
+ uint64_t GetAttributeValueAsAddress(const dw_attr_t attr,
+ uint64_t fail_value) const;
+
+ size_t GetAttributes(DWARFAttributes &attributes, uint32_t depth = 0) const;
+
+protected:
+ DWARFUnit *m_cu;
+ DWARFDebugInfoEntry *m_die;
+};
+
+bool operator==(const DWARFBaseDIE &lhs, const DWARFBaseDIE &rhs);
+bool operator!=(const DWARFBaseDIE &lhs, const DWARFBaseDIE &rhs);
+
+#endif // SymbolFileDWARF_DWARFBaseDIE_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp
new file mode 100644
index 000000000000..718f0537d6ed
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp
@@ -0,0 +1,115 @@
+//===-- DWARFCompileUnit.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 "DWARFCompileUnit.h"
+#include "DWARFDebugAranges.h"
+#include "SymbolFileDWARFDebugMap.h"
+
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void DWARFCompileUnit::Dump(Stream *s) const {
+ s->Printf("0x%8.8x: Compile Unit: length = 0x%8.8x, version = 0x%4.4x, "
+ "abbr_offset = 0x%8.8x, addr_size = 0x%2.2x (next CU at "
+ "{0x%8.8x})\n",
+ GetOffset(), GetLength(), GetVersion(), GetAbbrevOffset(),
+ GetAddressByteSize(), GetNextUnitOffset());
+}
+
+void DWARFCompileUnit::BuildAddressRangeTable(
+ DWARFDebugAranges *debug_aranges) {
+ // This function is usually called if there in no .debug_aranges section in
+ // order to produce a compile unit level set of address ranges that is
+ // accurate.
+
+ size_t num_debug_aranges = debug_aranges->GetNumRanges();
+
+ // First get the compile unit DIE only and check if it has a DW_AT_ranges
+ const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly();
+
+ const dw_offset_t cu_offset = GetOffset();
+ if (die) {
+ DWARFRangeList ranges;
+ const size_t num_ranges =
+ die->GetAttributeAddressRanges(this, ranges, false);
+ if (num_ranges > 0) {
+ // This compile unit has DW_AT_ranges, assume this is correct if it is
+ // present since clang no longer makes .debug_aranges by default and it
+ // emits DW_AT_ranges for DW_TAG_compile_units. GCC also does this with
+ // recent GCC builds.
+ for (size_t i = 0; i < num_ranges; ++i) {
+ const DWARFRangeList::Entry &range = ranges.GetEntryRef(i);
+ debug_aranges->AppendRange(cu_offset, range.GetRangeBase(),
+ range.GetRangeEnd());
+ }
+
+ return; // We got all of our ranges from the DW_AT_ranges attribute
+ }
+ }
+ // We don't have a DW_AT_ranges attribute, so we need to parse the DWARF
+
+ // If the DIEs weren't parsed, then we don't want all dies for all compile
+ // units to stay loaded when they weren't needed. So we can end up parsing
+ // the DWARF and then throwing them all away to keep memory usage down.
+ ScopedExtractDIEs clear_dies(ExtractDIEsScoped());
+
+ die = DIEPtr();
+ if (die)
+ die->BuildAddressRangeTable(this, debug_aranges);
+
+ if (debug_aranges->GetNumRanges() == num_debug_aranges) {
+ // We got nothing from the functions, maybe we have a line tables only
+ // situation. Check the line tables and build the arange table from this.
+ SymbolContext sc;
+ sc.comp_unit = m_dwarf.GetCompUnitForDWARFCompUnit(*this);
+ if (sc.comp_unit) {
+ SymbolFileDWARFDebugMap *debug_map_sym_file =
+ m_dwarf.GetDebugMapSymfile();
+ if (debug_map_sym_file == nullptr) {
+ if (LineTable *line_table = sc.comp_unit->GetLineTable()) {
+ LineTable::FileAddressRanges file_ranges;
+ const bool append = true;
+ const size_t num_ranges =
+ line_table->GetContiguousFileAddressRanges(file_ranges, append);
+ for (uint32_t idx = 0; idx < num_ranges; ++idx) {
+ const LineTable::FileAddressRanges::Entry &range =
+ file_ranges.GetEntryRef(idx);
+ debug_aranges->AppendRange(cu_offset, range.GetRangeBase(),
+ range.GetRangeEnd());
+ }
+ }
+ } else
+ debug_map_sym_file->AddOSOARanges(&m_dwarf, debug_aranges);
+ }
+ }
+
+ if (debug_aranges->GetNumRanges() == num_debug_aranges) {
+ // We got nothing from the functions, maybe we have a line tables only
+ // situation. Check the line tables and build the arange table from this.
+ SymbolContext sc;
+ sc.comp_unit = m_dwarf.GetCompUnitForDWARFCompUnit(*this);
+ if (sc.comp_unit) {
+ if (LineTable *line_table = sc.comp_unit->GetLineTable()) {
+ LineTable::FileAddressRanges file_ranges;
+ const bool append = true;
+ const size_t num_ranges =
+ line_table->GetContiguousFileAddressRanges(file_ranges, append);
+ for (uint32_t idx = 0; idx < num_ranges; ++idx) {
+ const LineTable::FileAddressRanges::Entry &range =
+ file_ranges.GetEntryRef(idx);
+ debug_aranges->AppendRange(GetOffset(), range.GetRangeBase(),
+ range.GetRangeEnd());
+ }
+ }
+ }
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h
new file mode 100644
index 000000000000..75647dbb082f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h
@@ -0,0 +1,35 @@
+//===-- DWARFCompileUnit.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 SymbolFileDWARF_DWARFCompileUnit_h_
+#define SymbolFileDWARF_DWARFCompileUnit_h_
+
+#include "DWARFUnit.h"
+#include "llvm/Support/Error.h"
+
+class DWARFCompileUnit : public DWARFUnit {
+public:
+ void BuildAddressRangeTable(DWARFDebugAranges *debug_aranges) override;
+
+ void Dump(lldb_private::Stream *s) const override;
+
+ static bool classof(const DWARFUnit *unit) { return !unit->IsTypeUnit(); }
+
+private:
+ DWARFCompileUnit(SymbolFileDWARF &dwarf, lldb::user_id_t uid,
+ const DWARFUnitHeader &header,
+ const DWARFAbbreviationDeclarationSet &abbrevs,
+ DIERef::Section section)
+ : DWARFUnit(dwarf, uid, header, abbrevs, section) {}
+
+ DISALLOW_COPY_AND_ASSIGN(DWARFCompileUnit);
+
+ friend class DWARFUnit;
+};
+
+#endif // SymbolFileDWARF_DWARFCompileUnit_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFContext.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFContext.cpp
new file mode 100644
index 000000000000..eb307ce1cce1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFContext.cpp
@@ -0,0 +1,138 @@
+//===-- DWARFContext.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 "DWARFContext.h"
+
+#include "lldb/Core/Section.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static DWARFDataExtractor LoadSection(SectionList *section_list,
+ SectionType section_type) {
+ if (!section_list)
+ return DWARFDataExtractor();
+
+ auto section_sp = section_list->FindSectionByType(section_type, true);
+ if (!section_sp)
+ return DWARFDataExtractor();
+
+ DWARFDataExtractor data;
+ section_sp->GetSectionData(data);
+ return data;
+}
+
+const DWARFDataExtractor &
+DWARFContext::LoadOrGetSection(SectionType main_section_type,
+ llvm::Optional<SectionType> dwo_section_type,
+ SectionData &data) {
+ llvm::call_once(data.flag, [&] {
+ if (dwo_section_type && isDwo())
+ data.data = LoadSection(m_dwo_section_list, *dwo_section_type);
+ else
+ data.data = LoadSection(m_main_section_list, main_section_type);
+ });
+ return data.data;
+}
+
+const DWARFDataExtractor &DWARFContext::getOrLoadAbbrevData() {
+ return LoadOrGetSection(eSectionTypeDWARFDebugAbbrev,
+ eSectionTypeDWARFDebugAbbrevDwo, m_data_debug_abbrev);
+}
+
+const DWARFDataExtractor &DWARFContext::getOrLoadArangesData() {
+ return LoadOrGetSection(eSectionTypeDWARFDebugAranges, llvm::None,
+ m_data_debug_aranges);
+}
+
+const DWARFDataExtractor &DWARFContext::getOrLoadAddrData() {
+ return LoadOrGetSection(eSectionTypeDWARFDebugAddr, llvm::None,
+ m_data_debug_addr);
+}
+
+const DWARFDataExtractor &DWARFContext::getOrLoadDebugInfoData() {
+ return LoadOrGetSection(eSectionTypeDWARFDebugInfo,
+ eSectionTypeDWARFDebugInfoDwo, m_data_debug_info);
+}
+
+const DWARFDataExtractor &DWARFContext::getOrLoadLineData() {
+ return LoadOrGetSection(eSectionTypeDWARFDebugLine, llvm::None,
+ m_data_debug_line);
+}
+
+const DWARFDataExtractor &DWARFContext::getOrLoadLineStrData() {
+ return LoadOrGetSection(eSectionTypeDWARFDebugLineStr, llvm::None,
+ m_data_debug_line_str);
+}
+
+const DWARFDataExtractor &DWARFContext::getOrLoadMacroData() {
+ return LoadOrGetSection(eSectionTypeDWARFDebugMacro, llvm::None,
+ m_data_debug_macro);
+}
+
+const DWARFDataExtractor &DWARFContext::getOrLoadRangesData() {
+ return LoadOrGetSection(eSectionTypeDWARFDebugRanges, llvm::None,
+ m_data_debug_ranges);
+}
+
+const DWARFDataExtractor &DWARFContext::getOrLoadRngListsData() {
+ return LoadOrGetSection(eSectionTypeDWARFDebugRngLists, llvm::None,
+ m_data_debug_rnglists);
+}
+
+const DWARFDataExtractor &DWARFContext::getOrLoadStrData() {
+ return LoadOrGetSection(eSectionTypeDWARFDebugStr,
+ eSectionTypeDWARFDebugStrDwo, m_data_debug_str);
+}
+
+const DWARFDataExtractor &DWARFContext::getOrLoadStrOffsetsData() {
+ return LoadOrGetSection(eSectionTypeDWARFDebugStrOffsets,
+ eSectionTypeDWARFDebugStrOffsetsDwo,
+ m_data_debug_str_offsets);
+}
+
+const DWARFDataExtractor &DWARFContext::getOrLoadDebugTypesData() {
+ return LoadOrGetSection(eSectionTypeDWARFDebugTypes,
+ eSectionTypeDWARFDebugTypesDwo, m_data_debug_types);
+}
+
+llvm::DWARFContext &DWARFContext::GetAsLLVM() {
+ if (!m_llvm_context) {
+ llvm::StringMap<std::unique_ptr<llvm::MemoryBuffer>> section_map;
+ uint8_t addr_size = 0;
+
+ auto AddSection = [&](Section &section) {
+ DataExtractor section_data;
+ section.GetSectionData(section_data);
+
+ // Set the address size the first time we see it.
+ if (addr_size == 0)
+ addr_size = section_data.GetByteSize();
+
+ llvm::StringRef data = llvm::toStringRef(section_data.GetData());
+ llvm::StringRef name = section.GetName().GetStringRef();
+ if (name.startswith("."))
+ name = name.drop_front();
+ section_map.try_emplace(
+ name, llvm::MemoryBuffer::getMemBuffer(data, name, false));
+ };
+
+ if (m_main_section_list) {
+ for (auto &section : *m_main_section_list)
+ AddSection(*section);
+ }
+
+ if (m_dwo_section_list) {
+ for (auto &section : *m_dwo_section_list)
+ AddSection(*section);
+ }
+
+ m_llvm_context = llvm::DWARFContext::create(section_map, addr_size);
+ }
+ return *m_llvm_context;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFContext.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFContext.h
new file mode 100644
index 000000000000..add042384039
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFContext.h
@@ -0,0 +1,74 @@
+//===-- DWARFContext.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_PLUGINS_SYMBOLFILE_DWARF_DWARFCONTEXT_H
+#define LLDB_PLUGINS_SYMBOLFILE_DWARF_DWARFCONTEXT_H
+
+#include "DWARFDataExtractor.h"
+#include "lldb/Core/Section.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/Support/Threading.h"
+#include <memory>
+
+namespace lldb_private {
+class DWARFContext {
+private:
+ SectionList *m_main_section_list;
+ SectionList *m_dwo_section_list;
+ mutable std::unique_ptr<llvm::DWARFContext> m_llvm_context;
+
+ struct SectionData {
+ llvm::once_flag flag;
+ DWARFDataExtractor data;
+ };
+
+ SectionData m_data_debug_abbrev;
+ SectionData m_data_debug_addr;
+ SectionData m_data_debug_aranges;
+ SectionData m_data_debug_info;
+ SectionData m_data_debug_line;
+ SectionData m_data_debug_line_str;
+ SectionData m_data_debug_macro;
+ SectionData m_data_debug_ranges;
+ SectionData m_data_debug_rnglists;
+ SectionData m_data_debug_str;
+ SectionData m_data_debug_str_offsets;
+ SectionData m_data_debug_types;
+
+ bool isDwo() { return m_dwo_section_list != nullptr; }
+
+ const DWARFDataExtractor &
+ LoadOrGetSection(lldb::SectionType main_section_type,
+ llvm::Optional<lldb::SectionType> dwo_section_type,
+ SectionData &data);
+
+public:
+ explicit DWARFContext(SectionList *main_section_list,
+ SectionList *dwo_section_list)
+ : m_main_section_list(main_section_list),
+ m_dwo_section_list(dwo_section_list) {}
+
+ const DWARFDataExtractor &getOrLoadAbbrevData();
+ const DWARFDataExtractor &getOrLoadAddrData();
+ const DWARFDataExtractor &getOrLoadArangesData();
+ const DWARFDataExtractor &getOrLoadDebugInfoData();
+ const DWARFDataExtractor &getOrLoadLineData();
+ const DWARFDataExtractor &getOrLoadLineStrData();
+ const DWARFDataExtractor &getOrLoadMacroData();
+ const DWARFDataExtractor &getOrLoadRangesData();
+ const DWARFDataExtractor &getOrLoadRngListsData();
+ const DWARFDataExtractor &getOrLoadStrData();
+ const DWARFDataExtractor &getOrLoadStrOffsetsData();
+ const DWARFDataExtractor &getOrLoadDebugTypesData();
+
+ llvm::DWARFContext &GetAsLLVM();
+};
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp
new file mode 100644
index 000000000000..9d97ca15a252
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp
@@ -0,0 +1,483 @@
+//===-- DWARFDIE.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 "DWARFDIE.h"
+
+#include "DWARFASTParser.h"
+#include "DWARFDebugInfo.h"
+#include "DWARFDebugInfoEntry.h"
+#include "DWARFDeclContext.h"
+#include "DWARFUnit.h"
+
+using namespace lldb_private;
+
+namespace {
+
+/// Iterate through all DIEs elaborating (i.e. reachable by a chain of
+/// DW_AT_specification and DW_AT_abstract_origin attributes) a given DIE. For
+/// convenience, the starting die is included in the sequence as the first
+/// item.
+class ElaboratingDIEIterator
+ : public std::iterator<std::input_iterator_tag, DWARFDIE> {
+
+ // The operating invariant is: top of m_worklist contains the "current" item
+ // and the rest of the list are items yet to be visited. An empty worklist
+ // means we've reached the end.
+ // Infinite recursion is prevented by maintaining a list of seen DIEs.
+ // Container sizes are optimized for the case of following DW_AT_specification
+ // and DW_AT_abstract_origin just once.
+ llvm::SmallVector<DWARFDIE, 2> m_worklist;
+ llvm::SmallSet<lldb::user_id_t, 3> m_seen;
+
+ void Next() {
+ assert(!m_worklist.empty() && "Incrementing end iterator?");
+
+ // Pop the current item from the list.
+ DWARFDIE die = m_worklist.back();
+ m_worklist.pop_back();
+
+ // And add back any items that elaborate it.
+ for (dw_attr_t attr : {DW_AT_specification, DW_AT_abstract_origin}) {
+ if (DWARFDIE d = die.GetReferencedDIE(attr))
+ if (m_seen.insert(die.GetID()).second)
+ m_worklist.push_back(d);
+ }
+ }
+
+public:
+ /// An iterator starting at die d.
+ explicit ElaboratingDIEIterator(DWARFDIE d) : m_worklist(1, d) {}
+
+ /// End marker
+ ElaboratingDIEIterator() {}
+
+ const DWARFDIE &operator*() const { return m_worklist.back(); }
+ ElaboratingDIEIterator &operator++() {
+ Next();
+ return *this;
+ }
+ ElaboratingDIEIterator operator++(int) {
+ ElaboratingDIEIterator I = *this;
+ Next();
+ return I;
+ }
+
+ friend bool operator==(const ElaboratingDIEIterator &a,
+ const ElaboratingDIEIterator &b) {
+ if (a.m_worklist.empty() || b.m_worklist.empty())
+ return a.m_worklist.empty() == b.m_worklist.empty();
+ return a.m_worklist.back() == b.m_worklist.back();
+ }
+ friend bool operator!=(const ElaboratingDIEIterator &a,
+ const ElaboratingDIEIterator &b) {
+ return !(a == b);
+ }
+};
+
+llvm::iterator_range<ElaboratingDIEIterator>
+elaborating_dies(const DWARFDIE &die) {
+ return llvm::make_range(ElaboratingDIEIterator(die),
+ ElaboratingDIEIterator());
+}
+} // namespace
+
+DWARFDIE
+DWARFDIE::GetParent() const {
+ if (IsValid())
+ return DWARFDIE(m_cu, m_die->GetParent());
+ else
+ return DWARFDIE();
+}
+
+DWARFDIE
+DWARFDIE::GetFirstChild() const {
+ if (IsValid())
+ return DWARFDIE(m_cu, m_die->GetFirstChild());
+ else
+ return DWARFDIE();
+}
+
+DWARFDIE
+DWARFDIE::GetSibling() const {
+ if (IsValid())
+ return DWARFDIE(m_cu, m_die->GetSibling());
+ else
+ return DWARFDIE();
+}
+
+DWARFDIE
+DWARFDIE::GetReferencedDIE(const dw_attr_t attr) const {
+ if (IsValid())
+ return m_die->GetAttributeValueAsReference(GetCU(), attr);
+ else
+ return {};
+}
+
+DWARFDIE
+DWARFDIE::GetDIE(dw_offset_t die_offset) const {
+ if (IsValid())
+ return m_cu->GetDIE(die_offset);
+ else
+ return DWARFDIE();
+}
+
+DWARFDIE
+DWARFDIE::GetAttributeValueAsReferenceDIE(const dw_attr_t attr) const {
+ if (IsValid()) {
+ DWARFUnit *cu = GetCU();
+ const bool check_specification_or_abstract_origin = true;
+ DWARFFormValue form_value;
+ if (m_die->GetAttributeValue(cu, attr, form_value, nullptr,
+ check_specification_or_abstract_origin))
+ return form_value.Reference();
+ }
+ return DWARFDIE();
+}
+
+DWARFDIE
+DWARFDIE::LookupDeepestBlock(lldb::addr_t file_addr) const {
+ if (IsValid()) {
+ SymbolFileDWARF *dwarf = GetDWARF();
+ DWARFUnit *cu = GetCU();
+ DWARFDebugInfoEntry *function_die = nullptr;
+ DWARFDebugInfoEntry *block_die = nullptr;
+ if (m_die->LookupAddress(file_addr, cu, &function_die, &block_die)) {
+ if (block_die && block_die != function_die) {
+ if (cu->ContainsDIEOffset(block_die->GetOffset()))
+ return DWARFDIE(cu, block_die);
+ else
+ return DWARFDIE(dwarf->DebugInfo()->GetUnit(DIERef(
+ cu->GetSymbolFileDWARF().GetDwoNum(),
+ cu->GetDebugSection(), block_die->GetOffset())),
+ block_die);
+ }
+ }
+ }
+ return DWARFDIE();
+}
+
+const char *DWARFDIE::GetMangledName() const {
+ if (IsValid())
+ return m_die->GetMangledName(m_cu);
+ else
+ return nullptr;
+}
+
+const char *DWARFDIE::GetPubname() const {
+ if (IsValid())
+ return m_die->GetPubname(m_cu);
+ else
+ return nullptr;
+}
+
+const char *DWARFDIE::GetQualifiedName(std::string &storage) const {
+ if (IsValid())
+ return m_die->GetQualifiedName(m_cu, storage);
+ else
+ return nullptr;
+}
+
+// GetName
+//
+// Get value of the DW_AT_name attribute and place that value into the supplied
+// stream object. If the DIE is a NULL object "NULL" is placed into the stream,
+// and if no DW_AT_name attribute exists for the DIE then nothing is printed.
+void DWARFDIE::GetName(Stream &s) const {
+ if (!IsValid())
+ return;
+ if (GetDIE()->IsNULL()) {
+ s.PutCString("NULL");
+ return;
+ }
+ const char *name = GetDIE()->GetAttributeValueAsString(GetCU(), DW_AT_name, nullptr, true);
+ if (!name)
+ return;
+ s.PutCString(name);
+}
+
+// AppendTypeName
+//
+// Follows the type name definition down through all needed tags to end up with
+// a fully qualified type name and dump the results to the supplied stream.
+// This is used to show the name of types given a type identifier.
+void DWARFDIE::AppendTypeName(Stream &s) const {
+ if (!IsValid())
+ return;
+ if (GetDIE()->IsNULL()) {
+ s.PutCString("NULL");
+ return;
+ }
+ if (const char *name = GetPubname()) {
+ s.PutCString(name);
+ return;
+ }
+ switch (Tag()) {
+ case DW_TAG_array_type:
+ break; // print out a "[]" after printing the full type of the element
+ // below
+ case DW_TAG_base_type:
+ s.PutCString("base ");
+ break;
+ case DW_TAG_class_type:
+ s.PutCString("class ");
+ break;
+ case DW_TAG_const_type:
+ s.PutCString("const ");
+ break;
+ case DW_TAG_enumeration_type:
+ s.PutCString("enum ");
+ break;
+ case DW_TAG_file_type:
+ s.PutCString("file ");
+ break;
+ case DW_TAG_interface_type:
+ s.PutCString("interface ");
+ break;
+ case DW_TAG_packed_type:
+ s.PutCString("packed ");
+ break;
+ case DW_TAG_pointer_type:
+ break; // print out a '*' after printing the full type below
+ case DW_TAG_ptr_to_member_type:
+ break; // print out a '*' after printing the full type below
+ case DW_TAG_reference_type:
+ break; // print out a '&' after printing the full type below
+ case DW_TAG_restrict_type:
+ s.PutCString("restrict ");
+ break;
+ case DW_TAG_set_type:
+ s.PutCString("set ");
+ break;
+ case DW_TAG_shared_type:
+ s.PutCString("shared ");
+ break;
+ case DW_TAG_string_type:
+ s.PutCString("string ");
+ break;
+ case DW_TAG_structure_type:
+ s.PutCString("struct ");
+ break;
+ case DW_TAG_subrange_type:
+ s.PutCString("subrange ");
+ break;
+ case DW_TAG_subroutine_type:
+ s.PutCString("function ");
+ break;
+ case DW_TAG_thrown_type:
+ s.PutCString("thrown ");
+ break;
+ case DW_TAG_union_type:
+ s.PutCString("union ");
+ break;
+ case DW_TAG_unspecified_type:
+ s.PutCString("unspecified ");
+ break;
+ case DW_TAG_volatile_type:
+ s.PutCString("volatile ");
+ break;
+ default:
+ return;
+ }
+
+ // Follow the DW_AT_type if possible
+ if (DWARFDIE next_die = GetAttributeValueAsReferenceDIE(DW_AT_type))
+ next_die.AppendTypeName(s);
+
+ switch (Tag()) {
+ case DW_TAG_array_type:
+ s.PutCString("[]");
+ break;
+ case DW_TAG_pointer_type:
+ s.PutChar('*');
+ break;
+ case DW_TAG_ptr_to_member_type:
+ s.PutChar('*');
+ break;
+ case DW_TAG_reference_type:
+ s.PutChar('&');
+ break;
+ default:
+ break;
+ }
+}
+
+lldb_private::Type *DWARFDIE::ResolveType() const {
+ if (IsValid())
+ return GetDWARF()->ResolveType(*this, true);
+ else
+ return nullptr;
+}
+
+lldb_private::Type *DWARFDIE::ResolveTypeUID(const DWARFDIE &die) const {
+ if (SymbolFileDWARF *dwarf = GetDWARF())
+ return dwarf->ResolveTypeUID(die, true);
+ return nullptr;
+}
+
+std::vector<DWARFDIE> DWARFDIE::GetDeclContextDIEs() const {
+ if (!IsValid())
+ return {};
+
+ std::vector<DWARFDIE> result;
+ DWARFDIE parent = GetParentDeclContextDIE();
+ while (parent.IsValid() && parent.GetDIE() != GetDIE()) {
+ result.push_back(std::move(parent));
+ parent = parent.GetParentDeclContextDIE();
+ }
+
+ return result;
+}
+
+void DWARFDIE::GetDWARFDeclContext(DWARFDeclContext &dwarf_decl_ctx) const {
+ if (IsValid()) {
+ dwarf_decl_ctx.SetLanguage(GetLanguage());
+ m_die->GetDWARFDeclContext(GetCU(), dwarf_decl_ctx);
+ } else {
+ dwarf_decl_ctx.Clear();
+ }
+}
+
+void DWARFDIE::GetDeclContext(std::vector<CompilerContext> &context) const {
+ const dw_tag_t tag = Tag();
+ if (tag == DW_TAG_compile_unit || tag == DW_TAG_partial_unit)
+ return;
+ DWARFDIE parent = GetParent();
+ if (parent)
+ parent.GetDeclContext(context);
+ switch (tag) {
+ case DW_TAG_module:
+ context.push_back(
+ CompilerContext(CompilerContextKind::Module, ConstString(GetName())));
+ break;
+ case DW_TAG_namespace:
+ context.push_back(CompilerContext(CompilerContextKind::Namespace,
+ ConstString(GetName())));
+ break;
+ case DW_TAG_structure_type:
+ context.push_back(CompilerContext(CompilerContextKind::Structure,
+ ConstString(GetName())));
+ break;
+ case DW_TAG_union_type:
+ context.push_back(
+ CompilerContext(CompilerContextKind::Union, ConstString(GetName())));
+ break;
+ case DW_TAG_class_type:
+ context.push_back(
+ CompilerContext(CompilerContextKind::Class, ConstString(GetName())));
+ break;
+ case DW_TAG_enumeration_type:
+ context.push_back(CompilerContext(CompilerContextKind::Enumeration,
+ ConstString(GetName())));
+ break;
+ case DW_TAG_subprogram:
+ context.push_back(CompilerContext(CompilerContextKind::Function,
+ ConstString(GetPubname())));
+ break;
+ case DW_TAG_variable:
+ context.push_back(CompilerContext(CompilerContextKind::Variable,
+ ConstString(GetPubname())));
+ break;
+ case DW_TAG_typedef:
+ context.push_back(
+ CompilerContext(CompilerContextKind::Typedef, ConstString(GetName())));
+ break;
+ default:
+ break;
+ }
+}
+
+DWARFDIE
+DWARFDIE::GetParentDeclContextDIE() const {
+ if (IsValid())
+ return m_die->GetParentDeclContextDIE(m_cu);
+ else
+ return DWARFDIE();
+}
+
+bool DWARFDIE::IsStructUnionOrClass() const {
+ const dw_tag_t tag = Tag();
+ return tag == DW_TAG_class_type || tag == DW_TAG_structure_type ||
+ tag == DW_TAG_union_type;
+}
+
+bool DWARFDIE::IsMethod() const {
+ for (DWARFDIE d : elaborating_dies(*this))
+ if (d.GetParent().IsStructUnionOrClass())
+ return true;
+ return false;
+}
+
+DWARFDIE
+DWARFDIE::GetContainingDWOModuleDIE() const {
+ if (IsValid()) {
+ DWARFDIE top_module_die;
+ // Now make sure this DIE is scoped in a DW_TAG_module tag and return true
+ // if so
+ for (DWARFDIE parent = GetParent(); parent.IsValid();
+ parent = parent.GetParent()) {
+ const dw_tag_t tag = parent.Tag();
+ if (tag == DW_TAG_module)
+ top_module_die = parent;
+ else if (tag == DW_TAG_compile_unit || tag == DW_TAG_partial_unit)
+ break;
+ }
+
+ return top_module_die;
+ }
+ return DWARFDIE();
+}
+
+lldb::ModuleSP DWARFDIE::GetContainingDWOModule() const {
+ if (IsValid()) {
+ DWARFDIE dwo_module_die = GetContainingDWOModuleDIE();
+
+ if (dwo_module_die) {
+ const char *module_name = dwo_module_die.GetName();
+ if (module_name)
+ return GetDWARF()->GetDWOModule(lldb_private::ConstString(module_name));
+ }
+ }
+ return lldb::ModuleSP();
+}
+
+bool DWARFDIE::GetDIENamesAndRanges(
+ const char *&name, const char *&mangled, DWARFRangeList &ranges,
+ int &decl_file, int &decl_line, int &decl_column, int &call_file,
+ int &call_line, int &call_column,
+ lldb_private::DWARFExpression *frame_base) const {
+ if (IsValid()) {
+ return m_die->GetDIENamesAndRanges(
+ GetCU(), name, mangled, ranges, decl_file, decl_line, decl_column,
+ call_file, call_line, call_column, frame_base);
+ } else
+ return false;
+}
+
+CompilerDecl DWARFDIE::GetDecl() const {
+ DWARFASTParser *dwarf_ast = GetDWARFParser();
+ if (dwarf_ast)
+ return dwarf_ast->GetDeclForUIDFromDWARF(*this);
+ else
+ return CompilerDecl();
+}
+
+CompilerDeclContext DWARFDIE::GetDeclContext() const {
+ DWARFASTParser *dwarf_ast = GetDWARFParser();
+ if (dwarf_ast)
+ return dwarf_ast->GetDeclContextForUIDFromDWARF(*this);
+ else
+ return CompilerDeclContext();
+}
+
+CompilerDeclContext DWARFDIE::GetContainingDeclContext() const {
+ DWARFASTParser *dwarf_ast = GetDWARFParser();
+ if (dwarf_ast)
+ return dwarf_ast->GetDeclContextContainingUIDFromDWARF(*this);
+ else
+ return CompilerDeclContext();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h
new file mode 100644
index 000000000000..7753ec9008cb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h
@@ -0,0 +1,107 @@
+//===-- DWARFDIE.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 SymbolFileDWARF_DWARFDIE_h_
+#define SymbolFileDWARF_DWARFDIE_h_
+
+#include "DWARFBaseDIE.h"
+#include "llvm/ADT/SmallSet.h"
+
+class DWARFDIE : public DWARFBaseDIE {
+public:
+ using DWARFBaseDIE::DWARFBaseDIE;
+
+ // Tests
+ bool IsStructUnionOrClass() const;
+
+ bool IsMethod() const;
+
+ // Accessors
+ lldb::ModuleSP GetContainingDWOModule() const;
+
+ DWARFDIE
+ GetContainingDWOModuleDIE() const;
+
+ // Accessing information about a DIE
+ const char *GetMangledName() const;
+
+ const char *GetPubname() const;
+
+ const char *GetQualifiedName(std::string &storage) const;
+
+ using DWARFBaseDIE::GetName;
+ void GetName(lldb_private::Stream &s) const;
+
+ void AppendTypeName(lldb_private::Stream &s) const;
+
+ lldb_private::Type *ResolveType() const;
+
+ // Resolve a type by UID using this DIE's DWARF file
+ lldb_private::Type *ResolveTypeUID(const DWARFDIE &die) const;
+
+ // Functions for obtaining DIE relations and references
+
+ DWARFDIE
+ GetParent() const;
+
+ DWARFDIE
+ GetFirstChild() const;
+
+ DWARFDIE
+ GetSibling() const;
+
+ DWARFDIE
+ GetReferencedDIE(const dw_attr_t attr) const;
+
+ // Get a another DIE from the same DWARF file as this DIE. This will
+ // check the current DIE's compile unit first to see if "die_offset" is
+ // in the same compile unit, and fall back to checking the DWARF file.
+ DWARFDIE
+ GetDIE(dw_offset_t die_offset) const;
+ using DWARFBaseDIE::GetDIE;
+
+ DWARFDIE
+ LookupDeepestBlock(lldb::addr_t file_addr) const;
+
+ DWARFDIE
+ GetParentDeclContextDIE() const;
+
+ // DeclContext related functions
+ std::vector<DWARFDIE> GetDeclContextDIEs() const;
+
+ void GetDWARFDeclContext(DWARFDeclContext &dwarf_decl_ctx) const;
+
+ /// Return this DIE's decl context as it is needed to look up types
+ /// in Clang's -gmodules debug info format.
+ void
+ GetDeclContext(std::vector<lldb_private::CompilerContext> &context) const;
+
+ // Getting attribute values from the DIE.
+ //
+ // GetAttributeValueAsXXX() functions should only be used if you are
+ // looking for one or two attributes on a DIE. If you are trying to
+ // parse all attributes, use GetAttributes (...) instead
+ DWARFDIE
+ GetAttributeValueAsReferenceDIE(const dw_attr_t attr) const;
+
+ bool GetDIENamesAndRanges(const char *&name, const char *&mangled,
+ DWARFRangeList &ranges, int &decl_file,
+ int &decl_line, int &decl_column, int &call_file,
+ int &call_line, int &call_column,
+ lldb_private::DWARFExpression *frame_base) const;
+
+ // CompilerDecl related functions
+
+ lldb_private::CompilerDecl GetDecl() const;
+
+ lldb_private::CompilerDeclContext GetDeclContext() const;
+
+ lldb_private::CompilerDeclContext GetContainingDeclContext() const;
+};
+
+#endif // SymbolFileDWARF_DWARFDIE_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.cpp
new file mode 100644
index 000000000000..1678b228137f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.cpp
@@ -0,0 +1,30 @@
+//===-- DWARFDataExtractor.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 "DWARFDataExtractor.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace lldb_private {
+
+uint64_t
+DWARFDataExtractor::GetDWARFInitialLength(lldb::offset_t *offset_ptr) const {
+ return GetU32(offset_ptr);
+}
+
+dw_offset_t
+DWARFDataExtractor::GetDWARFOffset(lldb::offset_t *offset_ptr) const {
+ return GetMaxU64(offset_ptr, GetDWARFSizeOfOffset());
+}
+
+llvm::DWARFDataExtractor DWARFDataExtractor::GetAsLLVM() const {
+ return llvm::DWARFDataExtractor(
+ llvm::StringRef(reinterpret_cast<const char *>(GetDataStart()),
+ GetByteSize()),
+ GetByteOrder() == lldb::eByteOrderLittle, GetAddressByteSize());
+}
+} // namespace lldb_private
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.h
new file mode 100644
index 000000000000..22db5e8c0b79
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.h
@@ -0,0 +1,37 @@
+//===-- DWARFDataExtractor.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 liblldb_DWARFDataExtractor_h_
+#define liblldb_DWARFDataExtractor_h_
+
+#include "lldb/Core/dwarf.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"
+
+namespace lldb_private {
+
+class DWARFDataExtractor : public DataExtractor {
+public:
+ DWARFDataExtractor() = default;
+
+ DWARFDataExtractor(const DWARFDataExtractor &data, lldb::offset_t offset,
+ lldb::offset_t length)
+ : DataExtractor(data, offset, length) {}
+
+ uint64_t GetDWARFInitialLength(lldb::offset_t *offset_ptr) const;
+
+ dw_offset_t GetDWARFOffset(lldb::offset_t *offset_ptr) const;
+
+ size_t GetDWARFSizeofInitialLength() const { return 4; }
+ size_t GetDWARFSizeOfOffset() const { return 4; }
+
+ llvm::DWARFDataExtractor GetAsLLVM() const;
+};
+}
+
+#endif // liblldb_DWARFDataExtractor_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.cpp
new file mode 100644
index 000000000000..26301566a8e1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.cpp
@@ -0,0 +1,146 @@
+//===-- DWARFDebugAbbrev.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 "DWARFDebugAbbrev.h"
+#include "DWARFDataExtractor.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace std;
+
+// DWARFAbbreviationDeclarationSet::Clear()
+void DWARFAbbreviationDeclarationSet::Clear() {
+ m_idx_offset = 0;
+ m_decls.clear();
+}
+
+// DWARFAbbreviationDeclarationSet::Extract()
+llvm::Error
+DWARFAbbreviationDeclarationSet::extract(const DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr) {
+ const lldb::offset_t begin_offset = *offset_ptr;
+ m_offset = begin_offset;
+ Clear();
+ DWARFAbbreviationDeclaration abbrevDeclaration;
+ dw_uleb128_t prev_abbr_code = 0;
+ while (true) {
+ llvm::Expected<DWARFEnumState> es =
+ abbrevDeclaration.extract(data, offset_ptr);
+ if (!es)
+ return es.takeError();
+ if (*es == DWARFEnumState::Complete)
+ break;
+ m_decls.push_back(abbrevDeclaration);
+ if (m_idx_offset == 0)
+ m_idx_offset = abbrevDeclaration.Code();
+ else if (prev_abbr_code + 1 != abbrevDeclaration.Code()) {
+ // Out of order indexes, we can't do O(1) lookups...
+ m_idx_offset = UINT32_MAX;
+ }
+ prev_abbr_code = abbrevDeclaration.Code();
+ }
+ return llvm::ErrorSuccess();
+}
+
+// DWARFAbbreviationDeclarationSet::GetAbbreviationDeclaration()
+const DWARFAbbreviationDeclaration *
+DWARFAbbreviationDeclarationSet::GetAbbreviationDeclaration(
+ dw_uleb128_t abbrCode) const {
+ if (m_idx_offset == UINT32_MAX) {
+ DWARFAbbreviationDeclarationCollConstIter pos;
+ DWARFAbbreviationDeclarationCollConstIter end = m_decls.end();
+ for (pos = m_decls.begin(); pos != end; ++pos) {
+ if (pos->Code() == abbrCode)
+ return &(*pos);
+ }
+ } else {
+ uint32_t idx = abbrCode - m_idx_offset;
+ if (idx < m_decls.size())
+ return &m_decls[idx];
+ }
+ return nullptr;
+}
+
+
+// DWARFAbbreviationDeclarationSet::GetUnsupportedForms()
+void DWARFAbbreviationDeclarationSet::GetUnsupportedForms(
+ std::set<dw_form_t> &invalid_forms) const {
+ for (const auto &abbr_decl : m_decls) {
+ const size_t num_attrs = abbr_decl.NumAttributes();
+ for (size_t i=0; i<num_attrs; ++i) {
+ dw_form_t form = abbr_decl.GetFormByIndex(i);
+ if (!DWARFFormValue::FormIsSupported(form))
+ invalid_forms.insert(form);
+ }
+ }
+}
+
+// Encode
+//
+// Encode the abbreviation table onto the end of the buffer provided into a
+// byte representation as would be found in a ".debug_abbrev" debug information
+// section.
+// void
+// DWARFAbbreviationDeclarationSet::Encode(BinaryStreamBuf& debug_abbrev_buf)
+// const
+//{
+// DWARFAbbreviationDeclarationCollConstIter pos;
+// DWARFAbbreviationDeclarationCollConstIter end = m_decls.end();
+// for (pos = m_decls.begin(); pos != end; ++pos)
+// pos->Append(debug_abbrev_buf);
+// debug_abbrev_buf.Append8(0);
+//}
+
+// DWARFDebugAbbrev constructor
+DWARFDebugAbbrev::DWARFDebugAbbrev()
+ : m_abbrevCollMap(), m_prev_abbr_offset_pos(m_abbrevCollMap.end()) {}
+
+// DWARFDebugAbbrev::Parse()
+llvm::Error DWARFDebugAbbrev::parse(const DWARFDataExtractor &data) {
+ lldb::offset_t offset = 0;
+
+ while (data.ValidOffset(offset)) {
+ uint32_t initial_cu_offset = offset;
+ DWARFAbbreviationDeclarationSet abbrevDeclSet;
+
+ llvm::Error error = abbrevDeclSet.extract(data, &offset);
+ if (error)
+ return error;
+
+ m_abbrevCollMap[initial_cu_offset] = abbrevDeclSet;
+ }
+ m_prev_abbr_offset_pos = m_abbrevCollMap.end();
+ return llvm::ErrorSuccess();
+}
+
+// DWARFDebugAbbrev::GetAbbreviationDeclarationSet()
+const DWARFAbbreviationDeclarationSet *
+DWARFDebugAbbrev::GetAbbreviationDeclarationSet(
+ dw_offset_t cu_abbr_offset) const {
+ DWARFAbbreviationDeclarationCollMapConstIter end = m_abbrevCollMap.end();
+ DWARFAbbreviationDeclarationCollMapConstIter pos;
+ if (m_prev_abbr_offset_pos != end &&
+ m_prev_abbr_offset_pos->first == cu_abbr_offset)
+ return &(m_prev_abbr_offset_pos->second);
+ else {
+ pos = m_abbrevCollMap.find(cu_abbr_offset);
+ m_prev_abbr_offset_pos = pos;
+ }
+
+ if (pos != m_abbrevCollMap.end())
+ return &(pos->second);
+ return nullptr;
+}
+
+// DWARFDebugAbbrev::GetUnsupportedForms()
+void DWARFDebugAbbrev::GetUnsupportedForms(
+ std::set<dw_form_t> &invalid_forms) const {
+ for (const auto &pair : m_abbrevCollMap)
+ pair.second.GetUnsupportedForms(invalid_forms);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.h
new file mode 100644
index 000000000000..9c4729326081
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.h
@@ -0,0 +1,81 @@
+//===-- DWARFDebugAbbrev.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 SymbolFileDWARF_DWARFDebugAbbrev_h_
+#define SymbolFileDWARF_DWARFDebugAbbrev_h_
+
+#include <list>
+#include <map>
+
+#include "lldb/lldb-private.h"
+
+#include "DWARFAbbreviationDeclaration.h"
+#include "DWARFDefines.h"
+
+typedef std::vector<DWARFAbbreviationDeclaration>
+ DWARFAbbreviationDeclarationColl;
+typedef DWARFAbbreviationDeclarationColl::iterator
+ DWARFAbbreviationDeclarationCollIter;
+typedef DWARFAbbreviationDeclarationColl::const_iterator
+ DWARFAbbreviationDeclarationCollConstIter;
+
+class DWARFAbbreviationDeclarationSet {
+public:
+ DWARFAbbreviationDeclarationSet()
+ : m_offset(DW_INVALID_OFFSET), m_idx_offset(0), m_decls() {}
+
+ DWARFAbbreviationDeclarationSet(dw_offset_t offset, uint32_t idx_offset)
+ : m_offset(offset), m_idx_offset(idx_offset), m_decls() {}
+
+ void Clear();
+ dw_offset_t GetOffset() const { return m_offset; }
+
+ /// Extract all abbrev decls in a set. Returns llvm::ErrorSuccess() on
+ /// success, and an appropriate llvm::Error object otherwise.
+ llvm::Error extract(const lldb_private::DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr);
+ // void Encode(BinaryStreamBuf& debug_abbrev_buf) const;
+ void GetUnsupportedForms(std::set<dw_form_t> &invalid_forms) const;
+
+ const DWARFAbbreviationDeclaration *
+ GetAbbreviationDeclaration(dw_uleb128_t abbrCode) const;
+
+ /// Unit test accessor functions.
+ /// @{
+ uint32_t GetIndexOffset() const { return m_idx_offset; }
+ /// @}
+private:
+ dw_offset_t m_offset;
+ uint32_t m_idx_offset;
+ std::vector<DWARFAbbreviationDeclaration> m_decls;
+};
+
+typedef std::map<dw_offset_t, DWARFAbbreviationDeclarationSet>
+ DWARFAbbreviationDeclarationCollMap;
+typedef DWARFAbbreviationDeclarationCollMap::iterator
+ DWARFAbbreviationDeclarationCollMapIter;
+typedef DWARFAbbreviationDeclarationCollMap::const_iterator
+ DWARFAbbreviationDeclarationCollMapConstIter;
+
+class DWARFDebugAbbrev {
+public:
+ DWARFDebugAbbrev();
+ const DWARFAbbreviationDeclarationSet *
+ GetAbbreviationDeclarationSet(dw_offset_t cu_abbr_offset) const;
+ /// Extract all abbreviations for a particular compile unit. Returns
+ /// llvm::ErrorSuccess() on success, and an appropriate llvm::Error object
+ /// otherwise.
+ llvm::Error parse(const lldb_private::DWARFDataExtractor &data);
+ void GetUnsupportedForms(std::set<dw_form_t> &invalid_forms) const;
+
+protected:
+ DWARFAbbreviationDeclarationCollMap m_abbrevCollMap;
+ mutable DWARFAbbreviationDeclarationCollMapConstIter m_prev_abbr_offset_pos;
+};
+
+#endif // SymbolFileDWARF_DWARFDebugAbbrev_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.cpp
new file mode 100644
index 000000000000..86ce3b329b25
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.cpp
@@ -0,0 +1,140 @@
+//===-- DWARFDebugArangeSet.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 "DWARFDebugArangeSet.h"
+#include "DWARFDataExtractor.h"
+#include "llvm/Object/Error.h"
+#include <cassert>
+
+using namespace lldb_private;
+
+DWARFDebugArangeSet::DWARFDebugArangeSet()
+ : m_offset(DW_INVALID_OFFSET), m_header(), m_arange_descriptors() {
+ m_header.length = 0;
+ m_header.version = 0;
+ m_header.cu_offset = 0;
+ m_header.addr_size = 0;
+ m_header.seg_size = 0;
+}
+
+void DWARFDebugArangeSet::Clear() {
+ m_offset = DW_INVALID_OFFSET;
+ m_header.length = 0;
+ m_header.version = 0;
+ m_header.cu_offset = 0;
+ m_header.addr_size = 0;
+ m_header.seg_size = 0;
+ m_arange_descriptors.clear();
+}
+
+llvm::Error DWARFDebugArangeSet::extract(const DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr) {
+ assert(data.ValidOffset(*offset_ptr));
+
+ m_arange_descriptors.clear();
+ m_offset = *offset_ptr;
+
+ // 7.20 Address Range Table
+ //
+ // Each set of entries in the table of address ranges contained in the
+ // .debug_aranges section begins with a header consisting of: a 4-byte
+ // length containing the length of the set of entries for this compilation
+ // unit, not including the length field itself; a 2-byte version identifier
+ // containing the value 2 for DWARF Version 2; a 4-byte offset into
+ // the.debug_infosection; a 1-byte unsigned integer containing the size in
+ // bytes of an address (or the offset portion of an address for segmented
+ // addressing) on the target system; and a 1-byte unsigned integer
+ // containing the size in bytes of a segment descriptor on the target
+ // system. This header is followed by a series of tuples. Each tuple
+ // consists of an address and a length, each in the size appropriate for an
+ // address on the target architecture.
+ m_header.length = data.GetDWARFInitialLength(offset_ptr);
+ m_header.version = data.GetU16(offset_ptr);
+ m_header.cu_offset = data.GetDWARFOffset(offset_ptr);
+ m_header.addr_size = data.GetU8(offset_ptr);
+ m_header.seg_size = data.GetU8(offset_ptr);
+
+ // Try to avoid reading invalid arange sets by making sure:
+ // 1 - the version looks good
+ // 2 - the address byte size looks plausible
+ // 3 - the length seems to make sense
+ // size looks plausible
+ if (m_header.version < 2 || m_header.version > 5)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "Invalid arange header version");
+
+ if (m_header.addr_size != 4 && m_header.addr_size != 8)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "Invalid arange header address size");
+
+ if (m_header.length == 0)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "Invalid arange header length");
+
+ if (!data.ValidOffset(m_offset + sizeof(m_header.length) + m_header.length -
+ 1))
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "Invalid arange header length");
+
+ // The first tuple following the header in each set begins at an offset
+ // that is a multiple of the size of a single tuple (that is, twice the
+ // size of an address). The header is padded, if necessary, to the
+ // appropriate boundary.
+ const uint32_t header_size = *offset_ptr - m_offset;
+ const uint32_t tuple_size = m_header.addr_size << 1;
+ uint32_t first_tuple_offset = 0;
+ while (first_tuple_offset < header_size)
+ first_tuple_offset += tuple_size;
+
+ *offset_ptr = m_offset + first_tuple_offset;
+
+ Descriptor arangeDescriptor;
+
+ static_assert(sizeof(arangeDescriptor.address) ==
+ sizeof(arangeDescriptor.length),
+ "DWARFDebugArangeSet::Descriptor.address and "
+ "DWARFDebugArangeSet::Descriptor.length must have same size");
+
+ while (data.ValidOffset(*offset_ptr)) {
+ arangeDescriptor.address = data.GetMaxU64(offset_ptr, m_header.addr_size);
+ arangeDescriptor.length = data.GetMaxU64(offset_ptr, m_header.addr_size);
+
+ // Each set of tuples is terminated by a 0 for the address and 0 for
+ // the length.
+ if (!arangeDescriptor.address && !arangeDescriptor.length)
+ return llvm::ErrorSuccess();
+
+ m_arange_descriptors.push_back(arangeDescriptor);
+ }
+
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "arange descriptors not terminated by null entry");
+}
+
+class DescriptorContainsAddress {
+public:
+ DescriptorContainsAddress(dw_addr_t address) : m_address(address) {}
+ bool operator()(const DWARFDebugArangeSet::Descriptor &desc) const {
+ return (m_address >= desc.address) &&
+ (m_address < (desc.address + desc.length));
+ }
+
+private:
+ const dw_addr_t m_address;
+};
+
+dw_offset_t DWARFDebugArangeSet::FindAddress(dw_addr_t address) const {
+ DescriptorConstIter end = m_arange_descriptors.end();
+ DescriptorConstIter pos =
+ std::find_if(m_arange_descriptors.begin(), end, // Range
+ DescriptorContainsAddress(address)); // Predicate
+ if (pos != end)
+ return m_header.cu_offset;
+
+ return DW_INVALID_OFFSET;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.h
new file mode 100644
index 000000000000..db0cf22a3f45
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.h
@@ -0,0 +1,62 @@
+//===-- DWARFDebugArangeSet.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 SymbolFileDWARF_DWARFDebugArangeSet_h_
+#define SymbolFileDWARF_DWARFDebugArangeSet_h_
+
+#include "lldb/Core/dwarf.h"
+#include <cstdint>
+#include <vector>
+
+class DWARFDebugArangeSet {
+public:
+ struct Header {
+ uint32_t length; // The total length of the entries for that set, not
+ // including the length field itself.
+ uint16_t version; // The DWARF version number
+ uint32_t cu_offset; // The offset from the beginning of the .debug_info
+ // section of the compilation unit entry referenced by
+ // the table.
+ uint8_t addr_size; // The size in bytes of an address on the target
+ // architecture. For segmented addressing, this is the
+ // size of the offset portion of the address
+ uint8_t seg_size; // The size in bytes of a segment descriptor on the target
+ // architecture. If the target system uses a flat address
+ // space, this value is 0.
+ };
+
+ struct Descriptor {
+ dw_addr_t address;
+ dw_addr_t length;
+ dw_addr_t end_address() const { return address + length; }
+ };
+
+ DWARFDebugArangeSet();
+ void Clear();
+ void SetOffset(uint32_t offset) { m_offset = offset; }
+ llvm::Error extract(const lldb_private::DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr);
+ dw_offset_t FindAddress(dw_addr_t address) const;
+ size_t NumDescriptors() const { return m_arange_descriptors.size(); }
+ const Header &GetHeader() const { return m_header; }
+
+ const Descriptor &GetDescriptorRef(uint32_t i) const {
+ return m_arange_descriptors[i];
+ }
+
+protected:
+ typedef std::vector<Descriptor> DescriptorColl;
+ typedef DescriptorColl::iterator DescriptorIter;
+ typedef DescriptorColl::const_iterator DescriptorConstIter;
+
+ uint32_t m_offset;
+ Header m_header;
+ DescriptorColl m_arange_descriptors;
+};
+
+#endif // SymbolFileDWARF_DWARFDebugArangeSet_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp
new file mode 100644
index 000000000000..ccf33e6dc341
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp
@@ -0,0 +1,95 @@
+//===-- DWARFDebugAranges.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 "DWARFDebugAranges.h"
+#include "DWARFDebugArangeSet.h"
+#include "DWARFUnit.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Timer.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Constructor
+DWARFDebugAranges::DWARFDebugAranges() : m_aranges() {}
+
+// CountArangeDescriptors
+class CountArangeDescriptors {
+public:
+ CountArangeDescriptors(uint32_t &count_ref) : count(count_ref) {
+ // printf("constructor CountArangeDescriptors()\n");
+ }
+ void operator()(const DWARFDebugArangeSet &set) {
+ count += set.NumDescriptors();
+ }
+ uint32_t &count;
+};
+
+// Extract
+llvm::Error
+DWARFDebugAranges::extract(const DWARFDataExtractor &debug_aranges_data) {
+ lldb::offset_t offset = 0;
+
+ DWARFDebugArangeSet set;
+ Range range;
+ while (debug_aranges_data.ValidOffset(offset)) {
+ llvm::Error error = set.extract(debug_aranges_data, &offset);
+ if (!error)
+ return error;
+
+ const uint32_t num_descriptors = set.NumDescriptors();
+ if (num_descriptors > 0) {
+ const dw_offset_t cu_offset = set.GetHeader().cu_offset;
+
+ for (uint32_t i = 0; i < num_descriptors; ++i) {
+ const DWARFDebugArangeSet::Descriptor &descriptor =
+ set.GetDescriptorRef(i);
+ m_aranges.Append(RangeToDIE::Entry(descriptor.address,
+ descriptor.length, cu_offset));
+ }
+ }
+ set.Clear();
+ }
+ return llvm::ErrorSuccess();
+}
+
+void DWARFDebugAranges::Dump(Log *log) const {
+ if (log == nullptr)
+ return;
+
+ const size_t num_entries = m_aranges.GetSize();
+ for (size_t i = 0; i < num_entries; ++i) {
+ const RangeToDIE::Entry *entry = m_aranges.GetEntryAtIndex(i);
+ if (entry)
+ log->Printf("0x%8.8x: [0x%" PRIx64 " - 0x%" PRIx64 ")", entry->data,
+ entry->GetRangeBase(), entry->GetRangeEnd());
+ }
+}
+
+void DWARFDebugAranges::AppendRange(dw_offset_t offset, dw_addr_t low_pc,
+ dw_addr_t high_pc) {
+ if (high_pc > low_pc)
+ m_aranges.Append(RangeToDIE::Entry(low_pc, high_pc - low_pc, offset));
+}
+
+void DWARFDebugAranges::Sort(bool minimize) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "%s this = %p", LLVM_PRETTY_FUNCTION,
+ static_cast<void *>(this));
+
+ m_aranges.Sort();
+ m_aranges.CombineConsecutiveEntriesWithEqualData();
+}
+
+// FindAddress
+dw_offset_t DWARFDebugAranges::FindAddress(dw_addr_t address) const {
+ const RangeToDIE::Entry *entry = m_aranges.FindEntryThatContains(address);
+ if (entry)
+ return entry->data;
+ return DW_INVALID_OFFSET;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h
new file mode 100644
index 000000000000..74ba011b27af
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h
@@ -0,0 +1,55 @@
+//===-- DWARFDebugAranges.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 SymbolFileDWARF_DWARFDebugAranges_h_
+#define SymbolFileDWARF_DWARFDebugAranges_h_
+
+#include "lldb/Core/dwarf.h"
+#include "lldb/Utility/RangeMap.h"
+#include "llvm/Support/Error.h"
+
+class DWARFDebugAranges {
+protected:
+ typedef lldb_private::RangeDataVector<dw_addr_t, uint32_t, dw_offset_t>
+ RangeToDIE;
+
+public:
+ typedef RangeToDIE::Entry Range;
+ typedef std::vector<RangeToDIE::Entry> RangeColl;
+
+ DWARFDebugAranges();
+
+ void Clear() { m_aranges.Clear(); }
+
+ llvm::Error
+ extract(const lldb_private::DWARFDataExtractor &debug_aranges_data);
+
+ // Use append range multiple times and then call sort
+ void AppendRange(dw_offset_t cu_offset, dw_addr_t low_pc, dw_addr_t high_pc);
+
+ void Sort(bool minimize);
+
+ void Dump(lldb_private::Log *log) const;
+
+ dw_offset_t FindAddress(dw_addr_t address) const;
+
+ bool IsEmpty() const { return m_aranges.IsEmpty(); }
+ size_t GetNumRanges() const { return m_aranges.GetSize(); }
+
+ dw_offset_t OffsetAtIndex(uint32_t idx) const {
+ const Range *range = m_aranges.GetEntryAtIndex(idx);
+ if (range)
+ return range->data;
+ return DW_INVALID_OFFSET;
+ }
+
+protected:
+ RangeToDIE m_aranges;
+};
+
+#endif // SymbolFileDWARF_DWARFDebugAranges_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp
new file mode 100644
index 000000000000..100f35f8c6b0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp
@@ -0,0 +1,197 @@
+//===-- DWARFDebugInfo.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 "SymbolFileDWARF.h"
+
+#include <algorithm>
+#include <set>
+
+#include "lldb/Host/PosixApi.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Stream.h"
+#include "llvm/Support/Casting.h"
+
+#include "DWARFCompileUnit.h"
+#include "DWARFContext.h"
+#include "DWARFDebugAranges.h"
+#include "DWARFDebugInfo.h"
+#include "DWARFDebugInfoEntry.h"
+#include "DWARFFormValue.h"
+#include "DWARFTypeUnit.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace std;
+
+// Constructor
+DWARFDebugInfo::DWARFDebugInfo(SymbolFileDWARF &dwarf,
+ lldb_private::DWARFContext &context)
+ : m_dwarf(dwarf), m_context(context), m_units(), m_cu_aranges_up() {}
+
+llvm::Expected<DWARFDebugAranges &> DWARFDebugInfo::GetCompileUnitAranges() {
+ if (m_cu_aranges_up)
+ return *m_cu_aranges_up;
+
+ m_cu_aranges_up = llvm::make_unique<DWARFDebugAranges>();
+ const DWARFDataExtractor &debug_aranges_data =
+ m_context.getOrLoadArangesData();
+ if (llvm::Error error = m_cu_aranges_up->extract(debug_aranges_data))
+ return std::move(error);
+
+ // Make a list of all CUs represented by the arange data in the file.
+ std::set<dw_offset_t> cus_with_data;
+ for (size_t n = 0; n < m_cu_aranges_up->GetNumRanges(); n++) {
+ dw_offset_t offset = m_cu_aranges_up->OffsetAtIndex(n);
+ if (offset != DW_INVALID_OFFSET)
+ cus_with_data.insert(offset);
+ }
+
+ // Manually build arange data for everything that wasn't in the
+ // .debug_aranges table.
+ const size_t num_units = GetNumUnits();
+ for (size_t idx = 0; idx < num_units; ++idx) {
+ DWARFUnit *cu = GetUnitAtIndex(idx);
+
+ dw_offset_t offset = cu->GetOffset();
+ if (cus_with_data.find(offset) == cus_with_data.end())
+ cu->BuildAddressRangeTable(m_cu_aranges_up.get());
+ }
+
+ const bool minimize = true;
+ m_cu_aranges_up->Sort(minimize);
+ return *m_cu_aranges_up;
+}
+
+void DWARFDebugInfo::ParseUnitsFor(DIERef::Section section) {
+ DWARFDataExtractor data = section == DIERef::Section::DebugTypes
+ ? m_context.getOrLoadDebugTypesData()
+ : m_context.getOrLoadDebugInfoData();
+ lldb::offset_t offset = 0;
+ while (data.ValidOffset(offset)) {
+ llvm::Expected<DWARFUnitSP> unit_sp =
+ DWARFUnit::extract(m_dwarf, m_units.size(), data, section, &offset);
+
+ if (!unit_sp) {
+ // FIXME: Propagate this error up.
+ llvm::consumeError(unit_sp.takeError());
+ return;
+ }
+
+ // If it didn't return an error, then it should be returning a valid Unit.
+ assert(*unit_sp);
+ m_units.push_back(*unit_sp);
+ offset = (*unit_sp)->GetNextUnitOffset();
+
+ if (auto *type_unit = llvm::dyn_cast<DWARFTypeUnit>(unit_sp->get())) {
+ m_type_hash_to_unit_index.emplace_back(type_unit->GetTypeHash(),
+ unit_sp.get()->GetID());
+ }
+ }
+}
+
+void DWARFDebugInfo::ParseUnitHeadersIfNeeded() {
+ if (!m_units.empty())
+ return;
+
+ ParseUnitsFor(DIERef::Section::DebugInfo);
+ ParseUnitsFor(DIERef::Section::DebugTypes);
+ llvm::sort(m_type_hash_to_unit_index, llvm::less_first());
+}
+
+size_t DWARFDebugInfo::GetNumUnits() {
+ ParseUnitHeadersIfNeeded();
+ return m_units.size();
+}
+
+DWARFUnit *DWARFDebugInfo::GetUnitAtIndex(user_id_t idx) {
+ DWARFUnit *cu = nullptr;
+ if (idx < GetNumUnits())
+ cu = m_units[idx].get();
+ return cu;
+}
+
+uint32_t DWARFDebugInfo::FindUnitIndex(DIERef::Section section,
+ dw_offset_t offset) {
+ ParseUnitHeadersIfNeeded();
+
+ // llvm::lower_bound is not used as for DIE offsets it would still return
+ // index +1 and GetOffset() returning index itself would be a special case.
+ auto pos = llvm::upper_bound(
+ m_units, std::make_pair(section, offset),
+ [](const std::pair<DIERef::Section, dw_offset_t> &lhs,
+ const DWARFUnitSP &rhs) {
+ return lhs < std::make_pair(rhs->GetDebugSection(), rhs->GetOffset());
+ });
+ uint32_t idx = std::distance(m_units.begin(), pos);
+ if (idx == 0)
+ return DW_INVALID_OFFSET;
+ return idx - 1;
+}
+
+DWARFUnit *DWARFDebugInfo::GetUnitAtOffset(DIERef::Section section,
+ dw_offset_t cu_offset,
+ uint32_t *idx_ptr) {
+ uint32_t idx = FindUnitIndex(section, cu_offset);
+ DWARFUnit *result = GetUnitAtIndex(idx);
+ if (result && result->GetOffset() != cu_offset) {
+ result = nullptr;
+ idx = DW_INVALID_INDEX;
+ }
+ if (idx_ptr)
+ *idx_ptr = idx;
+ return result;
+}
+
+DWARFUnit *DWARFDebugInfo::GetUnit(const DIERef &die_ref) {
+ return GetUnitContainingDIEOffset(die_ref.section(), die_ref.die_offset());
+}
+
+DWARFUnit *
+DWARFDebugInfo::GetUnitContainingDIEOffset(DIERef::Section section,
+ dw_offset_t die_offset) {
+ uint32_t idx = FindUnitIndex(section, die_offset);
+ DWARFUnit *result = GetUnitAtIndex(idx);
+ if (result && !result->ContainsDIEOffset(die_offset))
+ return nullptr;
+ return result;
+}
+
+DWARFTypeUnit *DWARFDebugInfo::GetTypeUnitForHash(uint64_t hash) {
+ auto pos = llvm::lower_bound(m_type_hash_to_unit_index,
+ std::make_pair(hash, 0u), llvm::less_first());
+ if (pos == m_type_hash_to_unit_index.end() || pos->first != hash)
+ return nullptr;
+ return llvm::cast<DWARFTypeUnit>(GetUnitAtIndex(pos->second));
+}
+
+bool DWARFDebugInfo::ContainsTypeUnits() {
+ ParseUnitHeadersIfNeeded();
+ return !m_type_hash_to_unit_index.empty();
+}
+
+DWARFDIE
+DWARFDebugInfo::GetDIEForDIEOffset(DIERef::Section section,
+ dw_offset_t die_offset) {
+ DWARFUnit *cu = GetUnitContainingDIEOffset(section, die_offset);
+ if (cu)
+ return cu->GetDIE(die_offset);
+ return DWARFDIE();
+}
+
+// GetDIE()
+//
+// Get the DIE (Debug Information Entry) with the specified offset.
+DWARFDIE
+DWARFDebugInfo::GetDIE(const DIERef &die_ref) {
+ DWARFUnit *cu = GetUnit(die_ref);
+ if (cu)
+ return cu->GetDIE(die_ref.die_offset());
+ return DWARFDIE(); // Not found
+}
+
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h
new file mode 100644
index 000000000000..d1b066ffe80c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h
@@ -0,0 +1,88 @@
+//===-- DWARFDebugInfo.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 SymbolFileDWARF_DWARFDebugInfo_h_
+#define SymbolFileDWARF_DWARFDebugInfo_h_
+
+#include <map>
+#include <vector>
+
+#include "DWARFDIE.h"
+#include "DWARFTypeUnit.h"
+#include "DWARFUnit.h"
+#include "SymbolFileDWARF.h"
+#include "lldb/Core/STLUtils.h"
+#include "lldb/lldb-private.h"
+#include "llvm/Support/Error.h"
+
+namespace lldb_private {
+class DWARFContext;
+}
+
+typedef std::multimap<const char *, dw_offset_t, CStringCompareFunctionObject>
+ CStringToDIEMap;
+typedef CStringToDIEMap::iterator CStringToDIEMapIter;
+typedef CStringToDIEMap::const_iterator CStringToDIEMapConstIter;
+
+class DWARFDebugInfo {
+public:
+ typedef dw_offset_t (*Callback)(SymbolFileDWARF *dwarf2Data,
+ DWARFUnit *cu,
+ DWARFDebugInfoEntry *die,
+ const dw_offset_t next_offset,
+ const uint32_t depth, void *userData);
+
+ explicit DWARFDebugInfo(SymbolFileDWARF &dwarf,
+ lldb_private::DWARFContext &context);
+
+ size_t GetNumUnits();
+ DWARFUnit *GetUnitAtIndex(lldb::user_id_t idx);
+ DWARFUnit *GetUnitAtOffset(DIERef::Section section, dw_offset_t cu_offset,
+ uint32_t *idx_ptr = nullptr);
+ DWARFUnit *GetUnitContainingDIEOffset(DIERef::Section section,
+ dw_offset_t die_offset);
+ DWARFUnit *GetUnit(const DIERef &die_ref);
+ DWARFTypeUnit *GetTypeUnitForHash(uint64_t hash);
+ bool ContainsTypeUnits();
+ DWARFDIE GetDIEForDIEOffset(DIERef::Section section,
+ dw_offset_t die_offset);
+ DWARFDIE GetDIE(const DIERef &die_ref);
+
+ enum {
+ eDumpFlag_Verbose = (1 << 0), // Verbose dumping
+ eDumpFlag_ShowForm = (1 << 1), // Show the DW_form type
+ eDumpFlag_ShowAncestors =
+ (1 << 2) // Show all parent DIEs when dumping single DIEs
+ };
+
+ llvm::Expected<DWARFDebugAranges &> GetCompileUnitAranges();
+
+protected:
+ typedef std::vector<DWARFUnitSP> UnitColl;
+
+ SymbolFileDWARF &m_dwarf;
+ lldb_private::DWARFContext &m_context;
+ UnitColl m_units;
+ std::unique_ptr<DWARFDebugAranges>
+ m_cu_aranges_up; // A quick address to compile unit table
+
+ std::vector<std::pair<uint64_t, uint32_t>> m_type_hash_to_unit_index;
+
+private:
+ // All parsing needs to be done partially any managed by this class as
+ // accessors are called.
+ void ParseUnitHeadersIfNeeded();
+
+ void ParseUnitsFor(DIERef::Section section);
+
+ uint32_t FindUnitIndex(DIERef::Section section, dw_offset_t offset);
+
+ DISALLOW_COPY_AND_ASSIGN(DWARFDebugInfo);
+};
+
+#endif // SymbolFileDWARF_DWARFDebugInfo_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp
new file mode 100644
index 000000000000..2f55b7d40ed9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp
@@ -0,0 +1,1246 @@
+//===-- DWARFDebugInfoEntry.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 "DWARFDebugInfoEntry.h"
+
+#include <assert.h>
+
+#include <algorithm>
+
+#include "llvm/Support/LEB128.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/Stream.h"
+
+#include "DWARFCompileUnit.h"
+#include "DWARFDebugAbbrev.h"
+#include "DWARFDebugAranges.h"
+#include "DWARFDebugInfo.h"
+#include "DWARFDebugRanges.h"
+#include "DWARFDeclContext.h"
+#include "DWARFFormValue.h"
+#include "DWARFUnit.h"
+#include "SymbolFileDWARF.h"
+#include "SymbolFileDWARFDwo.h"
+
+using namespace lldb_private;
+using namespace std;
+extern int g_verbose;
+
+// Extract a debug info entry for a given DWARFUnit from the data
+// starting at the offset in offset_ptr
+bool DWARFDebugInfoEntry::Extract(const DWARFDataExtractor &data,
+ const DWARFUnit *cu,
+ lldb::offset_t *offset_ptr) {
+ m_offset = *offset_ptr;
+ m_parent_idx = 0;
+ m_sibling_idx = 0;
+ const uint64_t abbr_idx = data.GetULEB128(offset_ptr);
+ lldbassert(abbr_idx <= UINT16_MAX);
+ m_abbr_idx = abbr_idx;
+
+ // assert (fixed_form_sizes); // For best performance this should be
+ // specified!
+
+ if (m_abbr_idx) {
+ lldb::offset_t offset = *offset_ptr;
+ const auto *abbrevDecl = GetAbbreviationDeclarationPtr(cu);
+ if (abbrevDecl == nullptr) {
+ cu->GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError(
+ "{0x%8.8x}: invalid abbreviation code %u, please file a bug and "
+ "attach the file at the start of this error message",
+ m_offset, (unsigned)abbr_idx);
+ // WE can't parse anymore if the DWARF is borked...
+ *offset_ptr = UINT32_MAX;
+ return false;
+ }
+ m_tag = abbrevDecl->Tag();
+ m_has_children = abbrevDecl->HasChildren();
+ // Skip all data in the .debug_info or .debug_types for the attributes
+ const uint32_t numAttributes = abbrevDecl->NumAttributes();
+ uint32_t i;
+ dw_form_t form;
+ for (i = 0; i < numAttributes; ++i) {
+ form = abbrevDecl->GetFormByIndexUnchecked(i);
+ llvm::Optional<uint8_t> fixed_skip_size =
+ DWARFFormValue::GetFixedSize(form, cu);
+ if (fixed_skip_size)
+ offset += *fixed_skip_size;
+ else {
+ bool form_is_indirect = false;
+ do {
+ form_is_indirect = false;
+ uint32_t form_size = 0;
+ switch (form) {
+ // Blocks if inlined data that have a length field and the data bytes
+ // inlined in the .debug_info/.debug_types
+ case DW_FORM_exprloc:
+ case DW_FORM_block:
+ form_size = data.GetULEB128(&offset);
+ break;
+ case DW_FORM_block1:
+ form_size = data.GetU8_unchecked(&offset);
+ break;
+ case DW_FORM_block2:
+ form_size = data.GetU16_unchecked(&offset);
+ break;
+ case DW_FORM_block4:
+ form_size = data.GetU32_unchecked(&offset);
+ break;
+
+ // Inlined NULL terminated C-strings
+ case DW_FORM_string:
+ data.GetCStr(&offset);
+ break;
+
+ // Compile unit address sized values
+ case DW_FORM_addr:
+ form_size = cu->GetAddressByteSize();
+ break;
+ case DW_FORM_ref_addr:
+ if (cu->GetVersion() <= 2)
+ form_size = cu->GetAddressByteSize();
+ else
+ form_size = 4;
+ break;
+
+ // 0 sized form
+ case DW_FORM_flag_present:
+ form_size = 0;
+ break;
+
+ // 1 byte values
+ case DW_FORM_addrx1:
+ case DW_FORM_data1:
+ case DW_FORM_flag:
+ case DW_FORM_ref1:
+ case DW_FORM_strx1:
+ form_size = 1;
+ break;
+
+ // 2 byte values
+ case DW_FORM_addrx2:
+ case DW_FORM_data2:
+ case DW_FORM_ref2:
+ case DW_FORM_strx2:
+ form_size = 2;
+ break;
+
+ // 3 byte values
+ case DW_FORM_addrx3:
+ case DW_FORM_strx3:
+ form_size = 3;
+ break;
+
+ // 4 byte values
+ case DW_FORM_addrx4:
+ case DW_FORM_data4:
+ case DW_FORM_ref4:
+ case DW_FORM_strx4:
+ form_size = 4;
+ break;
+
+ // 8 byte values
+ case DW_FORM_data8:
+ case DW_FORM_ref8:
+ case DW_FORM_ref_sig8:
+ form_size = 8;
+ break;
+
+ // signed or unsigned LEB 128 values
+ case DW_FORM_addrx:
+ case DW_FORM_rnglistx:
+ case DW_FORM_sdata:
+ case DW_FORM_udata:
+ case DW_FORM_ref_udata:
+ case DW_FORM_GNU_addr_index:
+ case DW_FORM_GNU_str_index:
+ case DW_FORM_strx:
+ data.Skip_LEB128(&offset);
+ break;
+
+ case DW_FORM_indirect:
+ form_is_indirect = true;
+ form = data.GetULEB128(&offset);
+ break;
+
+ case DW_FORM_strp:
+ case DW_FORM_sec_offset:
+ data.GetU32(&offset);
+ break;
+
+ case DW_FORM_implicit_const:
+ form_size = 0;
+ break;
+
+ default:
+ *offset_ptr = m_offset;
+ return false;
+ }
+ offset += form_size;
+
+ } while (form_is_indirect);
+ }
+ }
+ *offset_ptr = offset;
+ return true;
+ } else {
+ m_tag = 0;
+ m_has_children = false;
+ return true; // NULL debug tag entry
+ }
+
+ return false;
+}
+
+static DWARFRangeList GetRangesOrReportError(const DWARFUnit &unit,
+ const DWARFDebugInfoEntry &die,
+ const DWARFFormValue &value) {
+ llvm::Expected<DWARFRangeList> expected_ranges =
+ (value.Form() == DW_FORM_rnglistx)
+ ? unit.FindRnglistFromIndex(value.Unsigned())
+ : unit.FindRnglistFromOffset(value.Unsigned());
+ if (expected_ranges)
+ return std::move(*expected_ranges);
+ unit.GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError(
+ "{0x%8.8x}: DIE has DW_AT_ranges(0x%" PRIx64 ") attribute, but "
+ "range extraction failed (%s), please file a bug "
+ "and attach the file at the start of this error message",
+ die.GetOffset(), value.Unsigned(),
+ toString(expected_ranges.takeError()).c_str());
+ return DWARFRangeList();
+}
+
+// GetDIENamesAndRanges
+//
+// Gets the valid address ranges for a given DIE by looking for a
+// DW_AT_low_pc/DW_AT_high_pc pair, DW_AT_entry_pc, or DW_AT_ranges attributes.
+bool DWARFDebugInfoEntry::GetDIENamesAndRanges(
+ const DWARFUnit *cu, const char *&name, const char *&mangled,
+ DWARFRangeList &ranges, int &decl_file, int &decl_line, int &decl_column,
+ int &call_file, int &call_line, int &call_column,
+ DWARFExpression *frame_base) const {
+ dw_addr_t lo_pc = LLDB_INVALID_ADDRESS;
+ dw_addr_t hi_pc = LLDB_INVALID_ADDRESS;
+ std::vector<DWARFDIE> dies;
+ bool set_frame_base_loclist_addr = false;
+
+ const auto *abbrevDecl = GetAbbreviationDeclarationPtr(cu);
+
+ SymbolFileDWARF &dwarf = cu->GetSymbolFileDWARF();
+ lldb::ModuleSP module = dwarf.GetObjectFile()->GetModule();
+
+ if (abbrevDecl) {
+ const DWARFDataExtractor &data = cu->GetData();
+ lldb::offset_t offset = GetFirstAttributeOffset();
+
+ if (!data.ValidOffset(offset))
+ return false;
+
+ const uint32_t numAttributes = abbrevDecl->NumAttributes();
+ bool do_offset = false;
+
+ for (uint32_t i = 0; i < numAttributes; ++i) {
+ DWARFFormValue form_value(cu);
+ dw_attr_t attr;
+ abbrevDecl->GetAttrAndFormValueByIndex(i, attr, form_value);
+
+ if (form_value.ExtractValue(data, &offset)) {
+ switch (attr) {
+ case DW_AT_low_pc:
+ lo_pc = form_value.Address();
+
+ if (do_offset)
+ hi_pc += lo_pc;
+ do_offset = false;
+ break;
+
+ case DW_AT_entry_pc:
+ lo_pc = form_value.Address();
+ break;
+
+ case DW_AT_high_pc:
+ if (form_value.Form() == DW_FORM_addr ||
+ form_value.Form() == DW_FORM_addrx ||
+ form_value.Form() == DW_FORM_GNU_addr_index) {
+ hi_pc = form_value.Address();
+ } else {
+ hi_pc = form_value.Unsigned();
+ if (lo_pc == LLDB_INVALID_ADDRESS)
+ do_offset = hi_pc != LLDB_INVALID_ADDRESS;
+ else
+ hi_pc += lo_pc; // DWARF 4 introduces <offset-from-lo-pc> to save
+ // on relocations
+ }
+ break;
+
+ case DW_AT_ranges:
+ ranges = GetRangesOrReportError(*cu, *this, form_value);
+ break;
+
+ case DW_AT_name:
+ if (name == nullptr)
+ name = form_value.AsCString();
+ break;
+
+ case DW_AT_MIPS_linkage_name:
+ case DW_AT_linkage_name:
+ if (mangled == nullptr)
+ mangled = form_value.AsCString();
+ break;
+
+ case DW_AT_abstract_origin:
+ dies.push_back(form_value.Reference());
+ break;
+
+ case DW_AT_specification:
+ dies.push_back(form_value.Reference());
+ break;
+
+ case DW_AT_decl_file:
+ if (decl_file == 0)
+ decl_file = form_value.Unsigned();
+ break;
+
+ case DW_AT_decl_line:
+ if (decl_line == 0)
+ decl_line = form_value.Unsigned();
+ break;
+
+ case DW_AT_decl_column:
+ if (decl_column == 0)
+ decl_column = form_value.Unsigned();
+ break;
+
+ case DW_AT_call_file:
+ if (call_file == 0)
+ call_file = form_value.Unsigned();
+ break;
+
+ case DW_AT_call_line:
+ if (call_line == 0)
+ call_line = form_value.Unsigned();
+ break;
+
+ case DW_AT_call_column:
+ if (call_column == 0)
+ call_column = form_value.Unsigned();
+ break;
+
+ case DW_AT_frame_base:
+ if (frame_base) {
+ if (form_value.BlockData()) {
+ uint32_t block_offset =
+ form_value.BlockData() - data.GetDataStart();
+ uint32_t block_length = form_value.Unsigned();
+ *frame_base = DWARFExpression(module, data, cu,
+ block_offset, block_length);
+ } else {
+ const DWARFDataExtractor &debug_loc_data = dwarf.DebugLocData();
+ const dw_offset_t debug_loc_offset = form_value.Unsigned();
+
+ size_t loc_list_length = DWARFExpression::LocationListSize(
+ cu, debug_loc_data, debug_loc_offset);
+ if (loc_list_length > 0) {
+ *frame_base =
+ DWARFExpression(module, debug_loc_data, cu,
+ debug_loc_offset, loc_list_length);
+ if (lo_pc != LLDB_INVALID_ADDRESS) {
+ assert(lo_pc >= cu->GetBaseAddress());
+ frame_base->SetLocationListSlide(lo_pc -
+ cu->GetBaseAddress());
+ } else {
+ set_frame_base_loclist_addr = true;
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ if (ranges.IsEmpty()) {
+ if (lo_pc != LLDB_INVALID_ADDRESS) {
+ if (hi_pc != LLDB_INVALID_ADDRESS && hi_pc > lo_pc)
+ ranges.Append(DWARFRangeList::Entry(lo_pc, hi_pc - lo_pc));
+ else
+ ranges.Append(DWARFRangeList::Entry(lo_pc, 0));
+ }
+ }
+
+ if (set_frame_base_loclist_addr) {
+ dw_addr_t lowest_range_pc = ranges.GetMinRangeBase(0);
+ assert(lowest_range_pc >= cu->GetBaseAddress());
+ frame_base->SetLocationListSlide(lowest_range_pc - cu->GetBaseAddress());
+ }
+
+ if (ranges.IsEmpty() || name == nullptr || mangled == nullptr) {
+ for (const DWARFDIE &die : dies) {
+ if (die) {
+ die.GetDIE()->GetDIENamesAndRanges(die.GetCU(), name, mangled, ranges,
+ decl_file, decl_line, decl_column,
+ call_file, call_line, call_column);
+ }
+ }
+ }
+ return !ranges.IsEmpty();
+}
+
+// Dump
+//
+// Dumps a debug information entry and all of it's attributes to the specified
+// stream.
+void DWARFDebugInfoEntry::Dump(const DWARFUnit *cu, Stream &s,
+ uint32_t recurse_depth) const {
+ const DWARFDataExtractor &data = cu->GetData();
+ lldb::offset_t offset = m_offset;
+
+ if (data.ValidOffset(offset)) {
+ dw_uleb128_t abbrCode = data.GetULEB128(&offset);
+
+ s.Printf("\n0x%8.8x: ", m_offset);
+ s.Indent();
+ if (abbrCode != m_abbr_idx) {
+ s.Printf("error: DWARF has been modified\n");
+ } else if (abbrCode) {
+ const auto *abbrevDecl = GetAbbreviationDeclarationPtr(cu);
+ if (abbrevDecl) {
+ s.PutCString(DW_TAG_value_to_name(abbrevDecl->Tag()));
+ s.Printf(" [%u] %c\n", abbrCode, abbrevDecl->HasChildren() ? '*' : ' ');
+
+ // Dump all data in the .debug_info/.debug_types for the attributes
+ const uint32_t numAttributes = abbrevDecl->NumAttributes();
+ for (uint32_t i = 0; i < numAttributes; ++i) {
+ DWARFFormValue form_value(cu);
+ dw_attr_t attr;
+ abbrevDecl->GetAttrAndFormValueByIndex(i, attr, form_value);
+
+ DumpAttribute(cu, data, &offset, s, attr, form_value);
+ }
+
+ const DWARFDebugInfoEntry *child = GetFirstChild();
+ if (recurse_depth > 0 && child) {
+ s.IndentMore();
+
+ while (child) {
+ child->Dump(cu, s, recurse_depth - 1);
+ child = child->GetSibling();
+ }
+ s.IndentLess();
+ }
+ } else
+ s.Printf("Abbreviation code note found in 'debug_abbrev' class for "
+ "code: %u\n",
+ abbrCode);
+ } else {
+ s.Printf("NULL\n");
+ }
+ }
+}
+
+// DumpAttribute
+//
+// Dumps a debug information entry attribute along with it's form. Any special
+// display of attributes is done (disassemble location lists, show enumeration
+// values for attributes, etc).
+void DWARFDebugInfoEntry::DumpAttribute(
+ const DWARFUnit *cu, const DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr, Stream &s, dw_attr_t attr,
+ DWARFFormValue &form_value) {
+ bool show_form = s.GetFlags().Test(DWARFDebugInfo::eDumpFlag_ShowForm);
+
+ s.Printf(" ");
+ s.Indent(DW_AT_value_to_name(attr));
+
+ if (show_form) {
+ s.Printf("[%s", DW_FORM_value_to_name(form_value.Form()));
+ }
+
+ if (!form_value.ExtractValue(data, offset_ptr))
+ return;
+
+ if (show_form) {
+ if (form_value.Form() == DW_FORM_indirect) {
+ s.Printf(" [%s]", DW_FORM_value_to_name(form_value.Form()));
+ }
+
+ s.PutCString("] ");
+ }
+
+ s.PutCString("( ");
+
+ SymbolFileDWARF &dwarf = cu->GetSymbolFileDWARF();
+
+ // Check to see if we have any special attribute formatters
+ switch (attr) {
+ case DW_AT_stmt_list:
+ s.Printf("0x%8.8" PRIx64, form_value.Unsigned());
+ break;
+
+ case DW_AT_language:
+ s.PutCString(DW_LANG_value_to_name(form_value.Unsigned()));
+ break;
+
+ case DW_AT_encoding:
+ s.PutCString(DW_ATE_value_to_name(form_value.Unsigned()));
+ break;
+
+ case DW_AT_frame_base:
+ case DW_AT_location:
+ case DW_AT_data_member_location: {
+ const uint8_t *blockData = form_value.BlockData();
+ if (blockData) {
+ // Location description is inlined in data in the form value
+ DWARFDataExtractor locationData(data,
+ (*offset_ptr) - form_value.Unsigned(),
+ form_value.Unsigned());
+ DWARFExpression::PrintDWARFExpression(
+ s, locationData, DWARFUnit::GetAddressByteSize(cu), 4, false);
+ } else {
+ // We have a location list offset as the value that is the offset into
+ // the .debug_loc section that describes the value over it's lifetime
+ uint64_t debug_loc_offset = form_value.Unsigned();
+ DWARFExpression::PrintDWARFLocationList(s, cu, dwarf.DebugLocData(),
+ debug_loc_offset);
+ }
+ } break;
+
+ case DW_AT_abstract_origin:
+ case DW_AT_specification: {
+ DWARFDIE abstract_die = form_value.Reference();
+ form_value.Dump(s);
+ // *ostrm_ptr << HEX32 << abstract_die.GetOffset() << " ( ";
+ abstract_die.GetName(s);
+ } break;
+
+ case DW_AT_type: {
+ DWARFDIE type_die = form_value.Reference();
+ s.PutCString(" ( ");
+ type_die.AppendTypeName(s);
+ s.PutCString(" )");
+ } break;
+
+ default:
+ break;
+ }
+
+ s.PutCString(" )\n");
+}
+
+// Get all attribute values for a given DIE, including following any
+// specification or abstract origin attributes and including those in the
+// results. Any duplicate attributes will have the first instance take
+// precedence (this can happen for declaration attributes).
+size_t DWARFDebugInfoEntry::GetAttributes(
+ const DWARFUnit *cu, DWARFAttributes &attributes,
+ uint32_t curr_depth) const {
+ const auto *abbrevDecl = GetAbbreviationDeclarationPtr(cu);
+ if (abbrevDecl) {
+ const DWARFDataExtractor &data = cu->GetData();
+ lldb::offset_t offset = GetFirstAttributeOffset();
+
+ const uint32_t num_attributes = abbrevDecl->NumAttributes();
+ for (uint32_t i = 0; i < num_attributes; ++i) {
+ DWARFFormValue form_value(cu);
+ dw_attr_t attr;
+ abbrevDecl->GetAttrAndFormValueByIndex(i, attr, form_value);
+ const dw_form_t form = form_value.Form();
+
+ // If we are tracking down DW_AT_specification or DW_AT_abstract_origin
+ // attributes, the depth will be non-zero. We need to omit certain
+ // attributes that don't make sense.
+ switch (attr) {
+ case DW_AT_sibling:
+ case DW_AT_declaration:
+ if (curr_depth > 0) {
+ // This attribute doesn't make sense when combined with the DIE that
+ // references this DIE. We know a DIE is referencing this DIE because
+ // curr_depth is not zero
+ break;
+ }
+ LLVM_FALLTHROUGH;
+ default:
+ attributes.Append(cu, offset, attr, form);
+ break;
+ }
+
+ if ((attr == DW_AT_specification) || (attr == DW_AT_abstract_origin)) {
+ if (form_value.ExtractValue(data, &offset)) {
+ DWARFDIE spec_die = form_value.Reference();
+ if (spec_die)
+ spec_die.GetAttributes(attributes, curr_depth + 1);
+ }
+ } else {
+ llvm::Optional<uint8_t> fixed_skip_size = DWARFFormValue::GetFixedSize(form, cu);
+ if (fixed_skip_size)
+ offset += *fixed_skip_size;
+ else
+ DWARFFormValue::SkipValue(form, data, &offset, cu);
+ }
+ }
+ } else {
+ attributes.Clear();
+ }
+ return attributes.Size();
+}
+
+// GetAttributeValue
+//
+// Get the value of an attribute and return the .debug_info or .debug_types
+// offset of the attribute if it was properly extracted into form_value,
+// or zero if we fail since an offset of zero is invalid for an attribute (it
+// would be a compile unit header).
+dw_offset_t DWARFDebugInfoEntry::GetAttributeValue(
+ const DWARFUnit *cu, const dw_attr_t attr, DWARFFormValue &form_value,
+ dw_offset_t *end_attr_offset_ptr,
+ bool check_specification_or_abstract_origin) const {
+ if (const auto *abbrevDecl = GetAbbreviationDeclarationPtr(cu)) {
+ uint32_t attr_idx = abbrevDecl->FindAttributeIndex(attr);
+
+ if (attr_idx != DW_INVALID_INDEX) {
+ const DWARFDataExtractor &data = cu->GetData();
+ lldb::offset_t offset = GetFirstAttributeOffset();
+
+ uint32_t idx = 0;
+ while (idx < attr_idx)
+ DWARFFormValue::SkipValue(abbrevDecl->GetFormByIndex(idx++),
+ data, &offset, cu);
+
+ const dw_offset_t attr_offset = offset;
+ form_value.SetUnit(cu);
+ form_value.SetForm(abbrevDecl->GetFormByIndex(idx));
+ if (form_value.ExtractValue(data, &offset)) {
+ if (end_attr_offset_ptr)
+ *end_attr_offset_ptr = offset;
+ return attr_offset;
+ }
+ }
+ }
+
+ if (check_specification_or_abstract_origin) {
+ if (GetAttributeValue(cu, DW_AT_specification, form_value)) {
+ DWARFDIE die = form_value.Reference();
+ if (die) {
+ dw_offset_t die_offset = die.GetDIE()->GetAttributeValue(
+ die.GetCU(), attr, form_value, end_attr_offset_ptr, false);
+ if (die_offset)
+ return die_offset;
+ }
+ }
+
+ if (GetAttributeValue(cu, DW_AT_abstract_origin, form_value)) {
+ DWARFDIE die = form_value.Reference();
+ if (die) {
+ dw_offset_t die_offset = die.GetDIE()->GetAttributeValue(
+ die.GetCU(), attr, form_value, end_attr_offset_ptr, false);
+ if (die_offset)
+ return die_offset;
+ }
+ }
+ }
+
+ // If we're a unit DIE, also check the attributes of the dwo unit (if any).
+ if (GetParent())
+ return 0;
+ SymbolFileDWARFDwo *dwo_symbol_file = cu->GetDwoSymbolFile();
+ if (!dwo_symbol_file)
+ return 0;
+
+ DWARFCompileUnit *dwo_cu = dwo_symbol_file->GetCompileUnit();
+ if (!dwo_cu)
+ return 0;
+
+ DWARFBaseDIE dwo_cu_die = dwo_cu->GetUnitDIEOnly();
+ if (!dwo_cu_die.IsValid())
+ return 0;
+
+ return dwo_cu_die.GetDIE()->GetAttributeValue(
+ dwo_cu, attr, form_value, end_attr_offset_ptr,
+ check_specification_or_abstract_origin);
+}
+
+// GetAttributeValueAsString
+//
+// Get the value of an attribute as a string return it. The resulting pointer
+// to the string data exists within the supplied SymbolFileDWARF and will only
+// be available as long as the SymbolFileDWARF is still around and it's content
+// doesn't change.
+const char *DWARFDebugInfoEntry::GetAttributeValueAsString(
+ const DWARFUnit *cu, const dw_attr_t attr, const char *fail_value,
+ bool check_specification_or_abstract_origin) const {
+ DWARFFormValue form_value;
+ if (GetAttributeValue(cu, attr, form_value, nullptr,
+ check_specification_or_abstract_origin))
+ return form_value.AsCString();
+ return fail_value;
+}
+
+// GetAttributeValueAsUnsigned
+//
+// Get the value of an attribute as unsigned and return it.
+uint64_t DWARFDebugInfoEntry::GetAttributeValueAsUnsigned(
+ const DWARFUnit *cu, const dw_attr_t attr, uint64_t fail_value,
+ bool check_specification_or_abstract_origin) const {
+ DWARFFormValue form_value;
+ if (GetAttributeValue(cu, attr, form_value, nullptr,
+ check_specification_or_abstract_origin))
+ return form_value.Unsigned();
+ return fail_value;
+}
+
+// GetAttributeValueAsReference
+//
+// Get the value of an attribute as reference and fix up and compile unit
+// relative offsets as needed.
+DWARFDIE DWARFDebugInfoEntry::GetAttributeValueAsReference(
+ const DWARFUnit *cu, const dw_attr_t attr,
+ bool check_specification_or_abstract_origin) const {
+ DWARFFormValue form_value;
+ if (GetAttributeValue(cu, attr, form_value, nullptr,
+ check_specification_or_abstract_origin))
+ return form_value.Reference();
+ return {};
+}
+
+uint64_t DWARFDebugInfoEntry::GetAttributeValueAsAddress(
+ const DWARFUnit *cu, const dw_attr_t attr, uint64_t fail_value,
+ bool check_specification_or_abstract_origin) const {
+ DWARFFormValue form_value;
+ if (GetAttributeValue(cu, attr, form_value, nullptr,
+ check_specification_or_abstract_origin))
+ return form_value.Address();
+ return fail_value;
+}
+
+// GetAttributeHighPC
+//
+// Get the hi_pc, adding hi_pc to lo_pc when specified as an <offset-from-low-
+// pc>.
+//
+// Returns the hi_pc or fail_value.
+dw_addr_t DWARFDebugInfoEntry::GetAttributeHighPC(
+ const DWARFUnit *cu, dw_addr_t lo_pc, uint64_t fail_value,
+ bool check_specification_or_abstract_origin) const {
+ DWARFFormValue form_value;
+ if (GetAttributeValue(cu, DW_AT_high_pc, form_value, nullptr,
+ check_specification_or_abstract_origin)) {
+ dw_form_t form = form_value.Form();
+ if (form == DW_FORM_addr || form == DW_FORM_addrx ||
+ form == DW_FORM_GNU_addr_index)
+ return form_value.Address();
+
+ // DWARF4 can specify the hi_pc as an <offset-from-lowpc>
+ return lo_pc + form_value.Unsigned();
+ }
+ return fail_value;
+}
+
+// GetAttributeAddressRange
+//
+// Get the lo_pc and hi_pc, adding hi_pc to lo_pc when specified as an <offset-
+// from-low-pc>.
+//
+// Returns true or sets lo_pc and hi_pc to fail_value.
+bool DWARFDebugInfoEntry::GetAttributeAddressRange(
+ const DWARFUnit *cu, dw_addr_t &lo_pc, dw_addr_t &hi_pc,
+ uint64_t fail_value, bool check_specification_or_abstract_origin) const {
+ lo_pc = GetAttributeValueAsAddress(cu, DW_AT_low_pc, fail_value,
+ check_specification_or_abstract_origin);
+ if (lo_pc != fail_value) {
+ hi_pc = GetAttributeHighPC(cu, lo_pc, fail_value,
+ check_specification_or_abstract_origin);
+ if (hi_pc != fail_value)
+ return true;
+ }
+ lo_pc = fail_value;
+ hi_pc = fail_value;
+ return false;
+}
+
+size_t DWARFDebugInfoEntry::GetAttributeAddressRanges(
+ const DWARFUnit *cu, DWARFRangeList &ranges, bool check_hi_lo_pc,
+ bool check_specification_or_abstract_origin) const {
+ ranges.Clear();
+
+ DWARFFormValue form_value;
+ if (GetAttributeValue(cu, DW_AT_ranges, form_value)) {
+ ranges = GetRangesOrReportError(*cu, *this, form_value);
+ } else if (check_hi_lo_pc) {
+ dw_addr_t lo_pc = LLDB_INVALID_ADDRESS;
+ dw_addr_t hi_pc = LLDB_INVALID_ADDRESS;
+ if (GetAttributeAddressRange(cu, lo_pc, hi_pc, LLDB_INVALID_ADDRESS,
+ check_specification_or_abstract_origin)) {
+ if (lo_pc < hi_pc)
+ ranges.Append(DWARFRangeList::Entry(lo_pc, hi_pc - lo_pc));
+ }
+ }
+ return ranges.GetSize();
+}
+
+// GetName
+//
+// Get value of the DW_AT_name attribute and return it if one exists, else
+// return NULL.
+const char *DWARFDebugInfoEntry::GetName(const DWARFUnit *cu) const {
+ return GetAttributeValueAsString(cu, DW_AT_name, nullptr, true);
+}
+
+// GetMangledName
+//
+// Get value of the DW_AT_MIPS_linkage_name attribute and return it if one
+// exists, else return the value of the DW_AT_name attribute
+const char *
+DWARFDebugInfoEntry::GetMangledName(const DWARFUnit *cu,
+ bool substitute_name_allowed) const {
+ const char *name = nullptr;
+
+ name = GetAttributeValueAsString(cu, DW_AT_MIPS_linkage_name, nullptr, true);
+ if (name)
+ return name;
+
+ name = GetAttributeValueAsString(cu, DW_AT_linkage_name, nullptr, true);
+ if (name)
+ return name;
+
+ if (!substitute_name_allowed)
+ return nullptr;
+
+ name = GetAttributeValueAsString(cu, DW_AT_name, nullptr, true);
+ return name;
+}
+
+// GetPubname
+//
+// Get value the name for a DIE as it should appear for a .debug_pubnames or
+// .debug_pubtypes section.
+const char *DWARFDebugInfoEntry::GetPubname(const DWARFUnit *cu) const {
+ const char *name = nullptr;
+ if (!cu)
+ return name;
+
+ name = GetAttributeValueAsString(cu, DW_AT_MIPS_linkage_name, nullptr, true);
+ if (name)
+ return name;
+
+ name = GetAttributeValueAsString(cu, DW_AT_linkage_name, nullptr, true);
+ if (name)
+ return name;
+
+ name = GetAttributeValueAsString(cu, DW_AT_name, nullptr, true);
+ return name;
+}
+
+// BuildAddressRangeTable
+void DWARFDebugInfoEntry::BuildAddressRangeTable(
+ const DWARFUnit *cu, DWARFDebugAranges *debug_aranges) const {
+ if (m_tag) {
+ if (m_tag == DW_TAG_subprogram) {
+ dw_addr_t lo_pc = LLDB_INVALID_ADDRESS;
+ dw_addr_t hi_pc = LLDB_INVALID_ADDRESS;
+ if (GetAttributeAddressRange(cu, lo_pc, hi_pc, LLDB_INVALID_ADDRESS)) {
+ /// printf("BuildAddressRangeTable() 0x%8.8x: %30s: [0x%8.8x -
+ /// 0x%8.8x)\n", m_offset, DW_TAG_value_to_name(tag), lo_pc, hi_pc);
+ debug_aranges->AppendRange(cu->GetOffset(), lo_pc, hi_pc);
+ }
+ }
+
+ const DWARFDebugInfoEntry *child = GetFirstChild();
+ while (child) {
+ child->BuildAddressRangeTable(cu, debug_aranges);
+ child = child->GetSibling();
+ }
+ }
+}
+
+// BuildFunctionAddressRangeTable
+//
+// This function is very similar to the BuildAddressRangeTable function except
+// that the actual DIE offset for the function is placed in the table instead
+// of the compile unit offset (which is the way the standard .debug_aranges
+// section does it).
+void DWARFDebugInfoEntry::BuildFunctionAddressRangeTable(
+ const DWARFUnit *cu, DWARFDebugAranges *debug_aranges) const {
+ if (m_tag) {
+ if (m_tag == DW_TAG_subprogram) {
+ dw_addr_t lo_pc = LLDB_INVALID_ADDRESS;
+ dw_addr_t hi_pc = LLDB_INVALID_ADDRESS;
+ if (GetAttributeAddressRange(cu, lo_pc, hi_pc, LLDB_INVALID_ADDRESS)) {
+ // printf("BuildAddressRangeTable() 0x%8.8x: [0x%16.16" PRIx64 " -
+ // 0x%16.16" PRIx64 ")\n", m_offset, lo_pc, hi_pc); // DEBUG ONLY
+ debug_aranges->AppendRange(GetOffset(), lo_pc, hi_pc);
+ }
+ }
+
+ const DWARFDebugInfoEntry *child = GetFirstChild();
+ while (child) {
+ child->BuildFunctionAddressRangeTable(cu, debug_aranges);
+ child = child->GetSibling();
+ }
+ }
+}
+
+void DWARFDebugInfoEntry::GetDWARFDeclContext(
+ DWARFUnit *cu, DWARFDeclContext &dwarf_decl_ctx) const {
+ const dw_tag_t tag = Tag();
+ if (tag != DW_TAG_compile_unit && tag != DW_TAG_partial_unit) {
+ dwarf_decl_ctx.AppendDeclContext(tag, GetName(cu));
+ DWARFDIE parent_decl_ctx_die = GetParentDeclContextDIE(cu);
+ if (parent_decl_ctx_die && parent_decl_ctx_die.GetDIE() != this) {
+ if (parent_decl_ctx_die.Tag() != DW_TAG_compile_unit &&
+ parent_decl_ctx_die.Tag() != DW_TAG_partial_unit)
+ parent_decl_ctx_die.GetDIE()->GetDWARFDeclContext(
+ parent_decl_ctx_die.GetCU(), dwarf_decl_ctx);
+ }
+ }
+}
+
+DWARFDIE
+DWARFDebugInfoEntry::GetParentDeclContextDIE(DWARFUnit *cu) const {
+ DWARFAttributes attributes;
+ GetAttributes(cu, attributes);
+ return GetParentDeclContextDIE(cu, attributes);
+}
+
+DWARFDIE
+DWARFDebugInfoEntry::GetParentDeclContextDIE(
+ DWARFUnit *cu, const DWARFAttributes &attributes) const {
+ DWARFDIE die(cu, const_cast<DWARFDebugInfoEntry *>(this));
+
+ while (die) {
+ // If this is the original DIE that we are searching for a declaration for,
+ // then don't look in the cache as we don't want our own decl context to be
+ // our decl context...
+ if (die.GetDIE() != this) {
+ switch (die.Tag()) {
+ case DW_TAG_compile_unit:
+ case DW_TAG_partial_unit:
+ case DW_TAG_namespace:
+ case DW_TAG_structure_type:
+ case DW_TAG_union_type:
+ case DW_TAG_class_type:
+ return die;
+
+ default:
+ break;
+ }
+ }
+
+ DWARFDIE spec_die = attributes.FormValueAsReference(DW_AT_specification);
+ if (spec_die) {
+ DWARFDIE decl_ctx_die = spec_die.GetParentDeclContextDIE();
+ if (decl_ctx_die)
+ return decl_ctx_die;
+ }
+
+ DWARFDIE abs_die = attributes.FormValueAsReference(DW_AT_abstract_origin);
+ if (abs_die) {
+ DWARFDIE decl_ctx_die = abs_die.GetParentDeclContextDIE();
+ if (decl_ctx_die)
+ return decl_ctx_die;
+ }
+
+ die = die.GetParent();
+ }
+ return DWARFDIE();
+}
+
+const char *DWARFDebugInfoEntry::GetQualifiedName(DWARFUnit *cu,
+ std::string &storage) const {
+ DWARFAttributes attributes;
+ GetAttributes(cu, attributes);
+ return GetQualifiedName(cu, attributes, storage);
+}
+
+const char *
+DWARFDebugInfoEntry::GetQualifiedName(DWARFUnit *cu,
+ const DWARFAttributes &attributes,
+ std::string &storage) const {
+
+ const char *name = GetName(cu);
+
+ if (name) {
+ DWARFDIE parent_decl_ctx_die = GetParentDeclContextDIE(cu);
+ storage.clear();
+ // TODO: change this to get the correct decl context parent....
+ while (parent_decl_ctx_die) {
+ const dw_tag_t parent_tag = parent_decl_ctx_die.Tag();
+ switch (parent_tag) {
+ case DW_TAG_namespace: {
+ const char *namespace_name = parent_decl_ctx_die.GetName();
+ if (namespace_name) {
+ storage.insert(0, "::");
+ storage.insert(0, namespace_name);
+ } else {
+ storage.insert(0, "(anonymous namespace)::");
+ }
+ parent_decl_ctx_die = parent_decl_ctx_die.GetParentDeclContextDIE();
+ } break;
+
+ case DW_TAG_class_type:
+ case DW_TAG_structure_type:
+ case DW_TAG_union_type: {
+ const char *class_union_struct_name = parent_decl_ctx_die.GetName();
+
+ if (class_union_struct_name) {
+ storage.insert(0, "::");
+ storage.insert(0, class_union_struct_name);
+ }
+ parent_decl_ctx_die = parent_decl_ctx_die.GetParentDeclContextDIE();
+ } break;
+
+ default:
+ parent_decl_ctx_die.Clear();
+ break;
+ }
+ }
+
+ if (storage.empty())
+ storage.append("::");
+
+ storage.append(name);
+ }
+ if (storage.empty())
+ return nullptr;
+ return storage.c_str();
+}
+
+bool DWARFDebugInfoEntry::LookupAddress(const dw_addr_t address,
+ const DWARFUnit *cu,
+ DWARFDebugInfoEntry **function_die,
+ DWARFDebugInfoEntry **block_die) {
+ bool found_address = false;
+ if (m_tag) {
+ bool check_children = false;
+ bool match_addr_range = false;
+ // printf("0x%8.8x: %30s: address = 0x%8.8x - ", m_offset,
+ // DW_TAG_value_to_name(tag), address);
+ switch (m_tag) {
+ case DW_TAG_array_type:
+ break;
+ case DW_TAG_class_type:
+ check_children = true;
+ break;
+ case DW_TAG_entry_point:
+ case DW_TAG_enumeration_type:
+ case DW_TAG_formal_parameter:
+ case DW_TAG_imported_declaration:
+ case DW_TAG_label:
+ break;
+ case DW_TAG_lexical_block:
+ check_children = true;
+ match_addr_range = true;
+ break;
+ case DW_TAG_member:
+ case DW_TAG_pointer_type:
+ case DW_TAG_reference_type:
+ break;
+ case DW_TAG_compile_unit:
+ match_addr_range = true;
+ break;
+ case DW_TAG_string_type:
+ break;
+ case DW_TAG_structure_type:
+ check_children = true;
+ break;
+ case DW_TAG_subroutine_type:
+ case DW_TAG_typedef:
+ case DW_TAG_union_type:
+ case DW_TAG_unspecified_parameters:
+ case DW_TAG_variant:
+ break;
+ case DW_TAG_common_block:
+ check_children = true;
+ break;
+ case DW_TAG_common_inclusion:
+ case DW_TAG_inheritance:
+ break;
+ case DW_TAG_inlined_subroutine:
+ check_children = true;
+ match_addr_range = true;
+ break;
+ case DW_TAG_module:
+ match_addr_range = true;
+ break;
+ case DW_TAG_ptr_to_member_type:
+ case DW_TAG_set_type:
+ case DW_TAG_subrange_type:
+ case DW_TAG_with_stmt:
+ case DW_TAG_access_declaration:
+ case DW_TAG_base_type:
+ break;
+ case DW_TAG_catch_block:
+ match_addr_range = true;
+ break;
+ case DW_TAG_const_type:
+ case DW_TAG_constant:
+ case DW_TAG_enumerator:
+ case DW_TAG_file_type:
+ case DW_TAG_friend:
+ case DW_TAG_namelist:
+ case DW_TAG_namelist_item:
+ case DW_TAG_packed_type:
+ break;
+ case DW_TAG_subprogram:
+ match_addr_range = true;
+ break;
+ case DW_TAG_template_type_parameter:
+ case DW_TAG_template_value_parameter:
+ case DW_TAG_GNU_template_parameter_pack:
+ case DW_TAG_thrown_type:
+ break;
+ case DW_TAG_try_block:
+ match_addr_range = true;
+ break;
+ case DW_TAG_variant_part:
+ case DW_TAG_variable:
+ case DW_TAG_volatile_type:
+ case DW_TAG_dwarf_procedure:
+ case DW_TAG_restrict_type:
+ case DW_TAG_interface_type:
+ break;
+ case DW_TAG_namespace:
+ check_children = true;
+ break;
+ case DW_TAG_imported_module:
+ case DW_TAG_unspecified_type:
+ break;
+ case DW_TAG_partial_unit:
+ match_addr_range = true;
+ break;
+ case DW_TAG_imported_unit:
+ case DW_TAG_shared_type:
+ default:
+ break;
+ }
+
+ if (match_addr_range) {
+ dw_addr_t lo_pc =
+ GetAttributeValueAsAddress(cu, DW_AT_low_pc, LLDB_INVALID_ADDRESS);
+ if (lo_pc != LLDB_INVALID_ADDRESS) {
+ dw_addr_t hi_pc = GetAttributeHighPC(cu, lo_pc, LLDB_INVALID_ADDRESS);
+ if (hi_pc != LLDB_INVALID_ADDRESS) {
+ // printf("\n0x%8.8x: %30s: address = 0x%8.8x [0x%8.8x - 0x%8.8x) ",
+ // m_offset, DW_TAG_value_to_name(tag), address, lo_pc, hi_pc);
+ if ((lo_pc <= address) && (address < hi_pc)) {
+ found_address = true;
+ // puts("***MATCH***");
+ switch (m_tag) {
+ case DW_TAG_compile_unit: // File
+ case DW_TAG_partial_unit: // File
+ check_children =
+ ((function_die != nullptr) || (block_die != nullptr));
+ break;
+
+ case DW_TAG_subprogram: // Function
+ if (function_die)
+ *function_die = this;
+ check_children = (block_die != nullptr);
+ break;
+
+ case DW_TAG_inlined_subroutine: // Inlined Function
+ case DW_TAG_lexical_block: // Block { } in code
+ if (block_die) {
+ *block_die = this;
+ check_children = true;
+ }
+ break;
+
+ default:
+ check_children = true;
+ break;
+ }
+ }
+ } else {
+ // Compile units may not have a valid high/low pc when there
+ // are address gaps in subroutines so we must always search
+ // if there is no valid high and low PC.
+ check_children =
+ (m_tag == DW_TAG_compile_unit || m_tag == DW_TAG_partial_unit) &&
+ ((function_die != nullptr) || (block_die != nullptr));
+ }
+ } else {
+ DWARFRangeList ranges;
+ if (GetAttributeAddressRanges(cu, ranges, /*check_hi_lo_pc*/ false) &&
+ ranges.FindEntryThatContains(address)) {
+ found_address = true;
+ // puts("***MATCH***");
+ switch (m_tag) {
+ case DW_TAG_compile_unit: // File
+ case DW_TAG_partial_unit: // File
+ check_children =
+ ((function_die != nullptr) || (block_die != nullptr));
+ break;
+
+ case DW_TAG_subprogram: // Function
+ if (function_die)
+ *function_die = this;
+ check_children = (block_die != nullptr);
+ break;
+
+ case DW_TAG_inlined_subroutine: // Inlined Function
+ case DW_TAG_lexical_block: // Block { } in code
+ if (block_die) {
+ *block_die = this;
+ check_children = true;
+ }
+ break;
+
+ default:
+ check_children = true;
+ break;
+ }
+ } else {
+ check_children = false;
+ }
+ }
+ }
+
+ if (check_children) {
+ // printf("checking children\n");
+ DWARFDebugInfoEntry *child = GetFirstChild();
+ while (child) {
+ if (child->LookupAddress(address, cu, function_die, block_die))
+ return true;
+ child = child->GetSibling();
+ }
+ }
+ }
+ return found_address;
+}
+
+lldb::offset_t DWARFDebugInfoEntry::GetFirstAttributeOffset() const {
+ return GetOffset() + llvm::getULEB128Size(m_abbr_idx);
+}
+
+const DWARFAbbreviationDeclaration *
+DWARFDebugInfoEntry::GetAbbreviationDeclarationPtr(const DWARFUnit *cu) const {
+ if (cu) {
+ const DWARFAbbreviationDeclarationSet *abbrev_set = cu->GetAbbreviations();
+ if (abbrev_set)
+ return abbrev_set->GetAbbreviationDeclaration(m_abbr_idx);
+ }
+ return nullptr;
+}
+
+bool DWARFDebugInfoEntry::operator==(const DWARFDebugInfoEntry &rhs) const {
+ return m_offset == rhs.m_offset && m_parent_idx == rhs.m_parent_idx &&
+ m_sibling_idx == rhs.m_sibling_idx &&
+ m_abbr_idx == rhs.m_abbr_idx && m_has_children == rhs.m_has_children &&
+ m_tag == rhs.m_tag;
+}
+
+bool DWARFDebugInfoEntry::operator!=(const DWARFDebugInfoEntry &rhs) const {
+ return !(*this == rhs);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h
new file mode 100644
index 000000000000..1e7b5f27642d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h
@@ -0,0 +1,185 @@
+//===-- DWARFDebugInfoEntry.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 SymbolFileDWARF_DWARFDebugInfoEntry_h_
+#define SymbolFileDWARF_DWARFDebugInfoEntry_h_
+
+#include "SymbolFileDWARF.h"
+#include "llvm/ADT/SmallVector.h"
+
+#include "DWARFAbbreviationDeclaration.h"
+#include "DWARFDebugAbbrev.h"
+#include "DWARFDebugRanges.h"
+#include <map>
+#include <set>
+#include <vector>
+
+class DWARFDeclContext;
+
+#define DIE_SIBLING_IDX_BITSIZE 31
+
+class DWARFDebugInfoEntry {
+public:
+ typedef std::vector<DWARFDebugInfoEntry> collection;
+ typedef collection::iterator iterator;
+ typedef collection::const_iterator const_iterator;
+
+ DWARFDebugInfoEntry()
+ : m_offset(DW_INVALID_OFFSET), m_parent_idx(0), m_sibling_idx(0),
+ m_has_children(false), m_abbr_idx(0), m_tag(0) {}
+
+ explicit operator bool() const { return m_offset != DW_INVALID_OFFSET; }
+ bool operator==(const DWARFDebugInfoEntry &rhs) const;
+ bool operator!=(const DWARFDebugInfoEntry &rhs) const;
+
+ void BuildAddressRangeTable(const DWARFUnit *cu,
+ DWARFDebugAranges *debug_aranges) const;
+
+ void BuildFunctionAddressRangeTable(const DWARFUnit *cu,
+ DWARFDebugAranges *debug_aranges) const;
+
+ bool Extract(const lldb_private::DWARFDataExtractor &data,
+ const DWARFUnit *cu, lldb::offset_t *offset_ptr);
+
+ bool LookupAddress(const dw_addr_t address, const DWARFUnit *cu,
+ DWARFDebugInfoEntry **function_die,
+ DWARFDebugInfoEntry **block_die);
+
+ size_t GetAttributes(const DWARFUnit *cu,
+ DWARFAttributes &attrs,
+ uint32_t curr_depth = 0)
+ const; // "curr_depth" for internal use only, don't set this yourself!!!
+
+ dw_offset_t
+ GetAttributeValue(const DWARFUnit *cu, const dw_attr_t attr,
+ DWARFFormValue &formValue,
+ dw_offset_t *end_attr_offset_ptr = nullptr,
+ bool check_specification_or_abstract_origin = false) const;
+
+ const char *GetAttributeValueAsString(
+ const DWARFUnit *cu, const dw_attr_t attr, const char *fail_value,
+ bool check_specification_or_abstract_origin = false) const;
+
+ uint64_t GetAttributeValueAsUnsigned(
+ const DWARFUnit *cu, const dw_attr_t attr, uint64_t fail_value,
+ bool check_specification_or_abstract_origin = false) const;
+
+ DWARFDIE GetAttributeValueAsReference(
+ const DWARFUnit *cu, const dw_attr_t attr,
+ bool check_specification_or_abstract_origin = false) const;
+
+ uint64_t GetAttributeValueAsAddress(
+ const DWARFUnit *cu, const dw_attr_t attr, uint64_t fail_value,
+ bool check_specification_or_abstract_origin = false) const;
+
+ dw_addr_t
+ GetAttributeHighPC(const DWARFUnit *cu, dw_addr_t lo_pc, uint64_t fail_value,
+ bool check_specification_or_abstract_origin = false) const;
+
+ bool GetAttributeAddressRange(
+ const DWARFUnit *cu, dw_addr_t &lo_pc, dw_addr_t &hi_pc,
+ uint64_t fail_value,
+ bool check_specification_or_abstract_origin = false) const;
+
+ size_t GetAttributeAddressRanges(
+ const DWARFUnit *cu, DWARFRangeList &ranges, bool check_hi_lo_pc,
+ bool check_specification_or_abstract_origin = false) const;
+
+ const char *GetName(const DWARFUnit *cu) const;
+
+ const char *GetMangledName(const DWARFUnit *cu,
+ bool substitute_name_allowed = true) const;
+
+ const char *GetPubname(const DWARFUnit *cu) const;
+
+ const char *GetQualifiedName(DWARFUnit *cu, std::string &storage) const;
+
+ const char *GetQualifiedName(DWARFUnit *cu, const DWARFAttributes &attributes,
+ std::string &storage) const;
+
+ void Dump(const DWARFUnit *cu, lldb_private::Stream &s,
+ uint32_t recurse_depth) const;
+
+ static void
+ DumpAttribute(const DWARFUnit *cu,
+ const lldb_private::DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr, lldb_private::Stream &s,
+ dw_attr_t attr, DWARFFormValue &form_value);
+
+ bool GetDIENamesAndRanges(
+ const DWARFUnit *cu, const char *&name, const char *&mangled,
+ DWARFRangeList &rangeList, int &decl_file, int &decl_line,
+ int &decl_column, int &call_file, int &call_line, int &call_column,
+ lldb_private::DWARFExpression *frame_base = nullptr) const;
+
+ const DWARFAbbreviationDeclaration *
+ GetAbbreviationDeclarationPtr(const DWARFUnit *cu) const;
+
+ lldb::offset_t GetFirstAttributeOffset() const;
+
+ dw_tag_t Tag() const { return m_tag; }
+
+ bool IsNULL() const { return m_abbr_idx == 0; }
+
+ dw_offset_t GetOffset() const { return m_offset; }
+
+ bool HasChildren() const { return m_has_children; }
+
+ void SetHasChildren(bool b) { m_has_children = b; }
+
+ // We know we are kept in a vector of contiguous entries, so we know
+ // our parent will be some index behind "this".
+ DWARFDebugInfoEntry *GetParent() {
+ return m_parent_idx > 0 ? this - m_parent_idx : nullptr;
+ }
+ const DWARFDebugInfoEntry *GetParent() const {
+ return m_parent_idx > 0 ? this - m_parent_idx : nullptr;
+ }
+ // We know we are kept in a vector of contiguous entries, so we know
+ // our sibling will be some index after "this".
+ DWARFDebugInfoEntry *GetSibling() {
+ return m_sibling_idx > 0 ? this + m_sibling_idx : nullptr;
+ }
+ const DWARFDebugInfoEntry *GetSibling() const {
+ return m_sibling_idx > 0 ? this + m_sibling_idx : nullptr;
+ }
+ // We know we are kept in a vector of contiguous entries, so we know
+ // we don't need to store our child pointer, if we have a child it will
+ // be the next entry in the list...
+ DWARFDebugInfoEntry *GetFirstChild() {
+ return HasChildren() ? this + 1 : nullptr;
+ }
+ const DWARFDebugInfoEntry *GetFirstChild() const {
+ return HasChildren() ? this + 1 : nullptr;
+ }
+
+ void GetDWARFDeclContext(DWARFUnit *cu,
+ DWARFDeclContext &dwarf_decl_ctx) const;
+
+ DWARFDIE GetParentDeclContextDIE(DWARFUnit *cu) const;
+ DWARFDIE GetParentDeclContextDIE(DWARFUnit *cu,
+ const DWARFAttributes &attributes) const;
+
+ void SetSiblingIndex(uint32_t idx) { m_sibling_idx = idx; }
+ void SetParentIndex(uint32_t idx) { m_parent_idx = idx; }
+
+protected:
+ dw_offset_t m_offset; // Offset within the .debug_info/.debug_types
+ uint32_t m_parent_idx; // How many to subtract from "this" to get the parent.
+ // If zero this die has no parent
+ uint32_t m_sibling_idx : 31, // How many to add to "this" to get the sibling.
+ // If it is zero, then the DIE doesn't have children, or the
+ // DWARF claimed it had children but the DIE only contained
+ // a single NULL terminating child.
+ m_has_children : 1;
+ uint16_t m_abbr_idx;
+ uint16_t m_tag; // A copy of the DW_TAG value so we don't have to go through
+ // the compile unit abbrev table
+};
+
+#endif // SymbolFileDWARF_DWARFDebugInfoEntry_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp
new file mode 100644
index 000000000000..953089fee22b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp
@@ -0,0 +1,1038 @@
+//===-- DWARFDebugLine.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 "DWARFDebugLine.h"
+
+//#define ENABLE_DEBUG_PRINTF // DO NOT LEAVE THIS DEFINED: DEBUG ONLY!!!
+#include <assert.h>
+
+#include <memory>
+
+#include "lldb/Core/FileSpecList.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Timer.h"
+
+#include "DWARFUnit.h"
+#include "LogChannelDWARF.h"
+#include "SymbolFileDWARF.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace std;
+
+// Parse
+//
+// Parse all information in the debug_line_data into an internal
+// representation.
+void DWARFDebugLine::Parse(const DWARFDataExtractor &debug_line_data) {
+ m_lineTableMap.clear();
+ lldb::offset_t offset = 0;
+ LineTable::shared_ptr line_table_sp(new LineTable);
+ while (debug_line_data.ValidOffset(offset)) {
+ const lldb::offset_t debug_line_offset = offset;
+
+ if (line_table_sp.get() == nullptr)
+ break;
+
+ if (ParseStatementTable(debug_line_data, &offset, line_table_sp.get(), nullptr)) {
+ // Make sure we don't don't loop infinitely
+ if (offset <= debug_line_offset)
+ break;
+ // DEBUG_PRINTF("m_lineTableMap[0x%8.8x] = line_table_sp\n",
+ // debug_line_offset);
+ m_lineTableMap[debug_line_offset] = line_table_sp;
+ line_table_sp = std::make_shared<LineTable>();
+ } else
+ ++offset; // Try next byte in line table
+ }
+}
+
+void DWARFDebugLine::ParseIfNeeded(const DWARFDataExtractor &debug_line_data) {
+ if (m_lineTableMap.empty())
+ Parse(debug_line_data);
+}
+
+// DWARFDebugLine::GetLineTable
+DWARFDebugLine::LineTable::shared_ptr
+DWARFDebugLine::GetLineTable(const dw_offset_t offset) const {
+ DWARFDebugLine::LineTable::shared_ptr line_table_shared_ptr;
+ LineTableConstIter pos = m_lineTableMap.find(offset);
+ if (pos != m_lineTableMap.end())
+ line_table_shared_ptr = pos->second;
+ return line_table_shared_ptr;
+}
+
+// Parse
+//
+// Parse the entire line table contents calling callback each time a new
+// prologue is parsed and every time a new row is to be added to the line
+// table.
+void DWARFDebugLine::Parse(const DWARFDataExtractor &debug_line_data,
+ DWARFDebugLine::State::Callback callback,
+ void *userData) {
+ lldb::offset_t offset = 0;
+ if (debug_line_data.ValidOffset(offset)) {
+ if (!ParseStatementTable(debug_line_data, &offset, callback, userData, nullptr))
+ ++offset; // Skip to next byte in .debug_line section
+ }
+}
+
+namespace {
+struct EntryDescriptor {
+ dw_sleb128_t code;
+ dw_sleb128_t form;
+};
+
+static std::vector<EntryDescriptor>
+ReadDescriptors(const DWARFDataExtractor &debug_line_data,
+ lldb::offset_t *offset_ptr) {
+ std::vector<EntryDescriptor> ret;
+ uint8_t n = debug_line_data.GetU8(offset_ptr);
+ for (uint8_t i = 0; i < n; ++i) {
+ EntryDescriptor ent;
+ ent.code = debug_line_data.GetULEB128(offset_ptr);
+ ent.form = debug_line_data.GetULEB128(offset_ptr);
+ ret.push_back(ent);
+ }
+ return ret;
+}
+} // namespace
+
+// DWARFDebugLine::ParsePrologue
+bool DWARFDebugLine::ParsePrologue(const DWARFDataExtractor &debug_line_data,
+ lldb::offset_t *offset_ptr,
+ Prologue *prologue, DWARFUnit *dwarf_cu) {
+ const lldb::offset_t prologue_offset = *offset_ptr;
+
+ // DEBUG_PRINTF("0x%8.8x: ParsePrologue()\n", *offset_ptr);
+
+ prologue->Clear();
+ uint32_t i;
+ const char *s;
+ prologue->total_length = debug_line_data.GetDWARFInitialLength(offset_ptr);
+ prologue->version = debug_line_data.GetU16(offset_ptr);
+ if (prologue->version < 2 || prologue->version > 5)
+ return false;
+
+ if (prologue->version >= 5) {
+ prologue->address_size = debug_line_data.GetU8(offset_ptr);
+ prologue->segment_selector_size = debug_line_data.GetU8(offset_ptr);
+ }
+
+ prologue->prologue_length = debug_line_data.GetDWARFOffset(offset_ptr);
+ const lldb::offset_t end_prologue_offset =
+ prologue->prologue_length + *offset_ptr;
+ prologue->min_inst_length = debug_line_data.GetU8(offset_ptr);
+ if (prologue->version >= 4)
+ prologue->maximum_operations_per_instruction =
+ debug_line_data.GetU8(offset_ptr);
+ else
+ prologue->maximum_operations_per_instruction = 1;
+ prologue->default_is_stmt = debug_line_data.GetU8(offset_ptr);
+ prologue->line_base = debug_line_data.GetU8(offset_ptr);
+ prologue->line_range = debug_line_data.GetU8(offset_ptr);
+ prologue->opcode_base = debug_line_data.GetU8(offset_ptr);
+
+ prologue->standard_opcode_lengths.reserve(prologue->opcode_base - 1);
+
+ for (i = 1; i < prologue->opcode_base; ++i) {
+ uint8_t op_len = debug_line_data.GetU8(offset_ptr);
+ prologue->standard_opcode_lengths.push_back(op_len);
+ }
+
+ if (prologue->version >= 5) {
+ std::vector<EntryDescriptor> dirEntryFormatV =
+ ReadDescriptors(debug_line_data, offset_ptr);
+ uint8_t dirCount = debug_line_data.GetULEB128(offset_ptr);
+ for (int i = 0; i < dirCount; ++i) {
+ for (EntryDescriptor &ent : dirEntryFormatV) {
+ DWARFFormValue value(dwarf_cu, ent.form);
+ if (ent.code != DW_LNCT_path) {
+ if (!value.SkipValue(debug_line_data, offset_ptr))
+ return false;
+ continue;
+ }
+
+ if (!value.ExtractValue(debug_line_data, offset_ptr))
+ return false;
+ prologue->include_directories.push_back(value.AsCString());
+ }
+ }
+
+ std::vector<EntryDescriptor> filesEntryFormatV =
+ ReadDescriptors(debug_line_data, offset_ptr);
+ llvm::DenseSet<std::pair<uint64_t, uint64_t>> seen;
+ uint8_t n = debug_line_data.GetULEB128(offset_ptr);
+ for (int i = 0; i < n; ++i) {
+ FileNameEntry entry;
+ for (EntryDescriptor &ent : filesEntryFormatV) {
+ DWARFFormValue value(dwarf_cu, ent.form);
+ if (!value.ExtractValue(debug_line_data, offset_ptr))
+ return false;
+
+ switch (ent.code) {
+ case DW_LNCT_path:
+ entry.name = value.AsCString();
+ break;
+ case DW_LNCT_directory_index:
+ entry.dir_idx = value.Unsigned();
+ break;
+ case DW_LNCT_timestamp:
+ entry.mod_time = value.Unsigned();
+ break;
+ case DW_LNCT_size:
+ entry.length = value.Unsigned();
+ break;
+ case DW_LNCT_MD5:
+ assert(value.Unsigned() == 16);
+ std::uninitialized_copy_n(value.BlockData(), 16,
+ entry.checksum.Bytes.begin());
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (seen.insert(entry.checksum.words()).second)
+ prologue->file_names.push_back(entry);
+ }
+ } else {
+ while (*offset_ptr < end_prologue_offset) {
+ s = debug_line_data.GetCStr(offset_ptr);
+ if (s && s[0])
+ prologue->include_directories.push_back(s);
+ else
+ break;
+ }
+
+ while (*offset_ptr < end_prologue_offset) {
+ const char *name = debug_line_data.GetCStr(offset_ptr);
+ if (name && name[0]) {
+ FileNameEntry fileEntry;
+ fileEntry.name = name;
+ fileEntry.dir_idx = debug_line_data.GetULEB128(offset_ptr);
+ fileEntry.mod_time = debug_line_data.GetULEB128(offset_ptr);
+ fileEntry.length = debug_line_data.GetULEB128(offset_ptr);
+ prologue->file_names.push_back(fileEntry);
+ } else
+ break;
+ }
+ }
+
+ // XXX GNU as is broken for 64-Bit DWARF
+ if (*offset_ptr != end_prologue_offset) {
+ Host::SystemLog(Host::eSystemLogWarning,
+ "warning: parsing line table prologue at 0x%8.8" PRIx64
+ " should have ended at 0x%8.8" PRIx64
+ " but it ended at 0x%8.8" PRIx64 "\n",
+ prologue_offset, end_prologue_offset, *offset_ptr);
+ }
+ return end_prologue_offset;
+}
+
+bool DWARFDebugLine::ParseSupportFiles(
+ const lldb::ModuleSP &module_sp, const DWARFDataExtractor &debug_line_data,
+ dw_offset_t stmt_list, FileSpecList &support_files, DWARFUnit *dwarf_cu) {
+ lldb::offset_t offset = stmt_list;
+
+ Prologue prologue;
+ if (!ParsePrologue(debug_line_data, &offset, &prologue, dwarf_cu)) {
+ Host::SystemLog(Host::eSystemLogError, "error: parsing line table prologue "
+ "at 0x%8.8x (parsing ended around "
+ "0x%8.8" PRIx64 "\n",
+ stmt_list, offset);
+ return false;
+ }
+
+ FileSpec file_spec;
+ std::string remapped_file;
+
+ for (uint32_t file_idx = 1;
+ prologue.GetFile(file_idx, dwarf_cu->GetCompilationDirectory(),
+ dwarf_cu->GetPathStyle(), file_spec);
+ ++file_idx) {
+ if (module_sp->RemapSourceFile(file_spec.GetPath(), remapped_file))
+ file_spec.SetFile(remapped_file, FileSpec::Style::native);
+ support_files.Append(file_spec);
+ }
+ return true;
+}
+
+// ParseStatementTable
+//
+// Parse a single line table (prologue and all rows) and call the callback
+// function once for the prologue (row in state will be zero) and each time a
+// row is to be added to the line table.
+bool DWARFDebugLine::ParseStatementTable(
+ const DWARFDataExtractor &debug_line_data, lldb::offset_t *offset_ptr,
+ DWARFDebugLine::State::Callback callback, void *userData, DWARFUnit *dwarf_cu) {
+ Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_LINE));
+ Prologue::shared_ptr prologue(new Prologue());
+
+ const dw_offset_t debug_line_offset = *offset_ptr;
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(
+ func_cat, "DWARFDebugLine::ParseStatementTable (.debug_line[0x%8.8x])",
+ debug_line_offset);
+
+ if (!ParsePrologue(debug_line_data, offset_ptr, prologue.get(), dwarf_cu)) {
+ if (log)
+ log->Error("failed to parse DWARF line table prologue");
+ // Restore our offset and return false to indicate failure!
+ *offset_ptr = debug_line_offset;
+ return false;
+ }
+
+ if (log)
+ prologue->Dump(log);
+
+ const dw_offset_t end_offset =
+ debug_line_offset + prologue->total_length +
+ (debug_line_data.GetDWARFSizeofInitialLength());
+
+ State state(prologue, log, callback, userData);
+
+ while (*offset_ptr < end_offset) {
+ // DEBUG_PRINTF("0x%8.8x: ", *offset_ptr);
+ uint8_t opcode = debug_line_data.GetU8(offset_ptr);
+
+ if (opcode == 0) {
+ // Extended Opcodes always start with a zero opcode followed by a uleb128
+ // length so you can skip ones you don't know about
+ lldb::offset_t ext_offset = *offset_ptr;
+ dw_uleb128_t len = debug_line_data.GetULEB128(offset_ptr);
+ dw_offset_t arg_size = len - (*offset_ptr - ext_offset);
+
+ // DEBUG_PRINTF("Extended: <%2u> ", len);
+ uint8_t sub_opcode = debug_line_data.GetU8(offset_ptr);
+ switch (sub_opcode) {
+ case DW_LNE_end_sequence:
+ // Set the end_sequence register of the state machine to true and
+ // append a row to the matrix using the current values of the state-
+ // machine registers. Then reset the registers to the initial values
+ // specified above. Every statement program sequence must end with a
+ // DW_LNE_end_sequence instruction which creates a row whose address is
+ // that of the byte after the last target machine instruction of the
+ // sequence.
+ state.end_sequence = true;
+ state.AppendRowToMatrix(*offset_ptr);
+ state.Reset();
+ break;
+
+ case DW_LNE_set_address:
+ // Takes a single relocatable address as an operand. The size of the
+ // operand is the size appropriate to hold an address on the target
+ // machine. Set the address register to the value given by the
+ // relocatable address. All of the other statement program opcodes that
+ // affect the address register add a delta to it. This instruction
+ // stores a relocatable value into it instead.
+ if (arg_size == 4)
+ state.address = debug_line_data.GetU32(offset_ptr);
+ else // arg_size == 8
+ state.address = debug_line_data.GetU64(offset_ptr);
+ break;
+
+ case DW_LNE_define_file:
+ // Takes 4 arguments. The first is a null terminated string containing
+ // a source file name. The second is an unsigned LEB128 number
+ // representing the directory index of the directory in which the file
+ // was found. The third is an unsigned LEB128 number representing the
+ // time of last modification of the file. The fourth is an unsigned
+ // LEB128 number representing the length in bytes of the file. The time
+ // and length fields may contain LEB128(0) if the information is not
+ // available.
+ //
+ // The directory index represents an entry in the include_directories
+ // section of the statement program prologue. The index is LEB128(0) if
+ // the file was found in the current directory of the compilation,
+ // LEB128(1) if it was found in the first directory in the
+ // include_directories section, and so on. The directory index is
+ // ignored for file names that represent full path names.
+ //
+ // The files are numbered, starting at 1, in the order in which they
+ // appear; the names in the prologue come before names defined by the
+ // DW_LNE_define_file instruction. These numbers are used in the file
+ // register of the state machine.
+ {
+ FileNameEntry fileEntry;
+ fileEntry.name = debug_line_data.GetCStr(offset_ptr);
+ fileEntry.dir_idx = debug_line_data.GetULEB128(offset_ptr);
+ fileEntry.mod_time = debug_line_data.GetULEB128(offset_ptr);
+ fileEntry.length = debug_line_data.GetULEB128(offset_ptr);
+ state.prologue->file_names.push_back(fileEntry);
+ }
+ break;
+
+ default:
+ // Length doesn't include the zero opcode byte or the length itself,
+ // but it does include the sub_opcode, so we have to adjust for that
+ // below
+ (*offset_ptr) += arg_size;
+ break;
+ }
+ } else if (opcode < prologue->opcode_base) {
+ switch (opcode) {
+ // Standard Opcodes
+ case DW_LNS_copy:
+ // Takes no arguments. Append a row to the matrix using the current
+ // values of the state-machine registers. Then set the basic_block
+ // register to false.
+ state.AppendRowToMatrix(*offset_ptr);
+ break;
+
+ case DW_LNS_advance_pc:
+ // Takes a single unsigned LEB128 operand, multiplies it by the
+ // min_inst_length field of the prologue, and adds the result to the
+ // address register of the state machine.
+ state.address +=
+ debug_line_data.GetULEB128(offset_ptr) * prologue->min_inst_length;
+ break;
+
+ case DW_LNS_advance_line:
+ // Takes a single signed LEB128 operand and adds that value to the line
+ // register of the state machine.
+ state.line += debug_line_data.GetSLEB128(offset_ptr);
+ break;
+
+ case DW_LNS_set_file:
+ // Takes a single unsigned LEB128 operand and stores it in the file
+ // register of the state machine.
+ state.file = debug_line_data.GetULEB128(offset_ptr);
+ break;
+
+ case DW_LNS_set_column:
+ // Takes a single unsigned LEB128 operand and stores it in the column
+ // register of the state machine.
+ state.column = debug_line_data.GetULEB128(offset_ptr);
+ break;
+
+ case DW_LNS_negate_stmt:
+ // Takes no arguments. Set the is_stmt register of the state machine to
+ // the logical negation of its current value.
+ state.is_stmt = !state.is_stmt;
+ break;
+
+ case DW_LNS_set_basic_block:
+ // Takes no arguments. Set the basic_block register of the state
+ // machine to true
+ state.basic_block = true;
+ break;
+
+ case DW_LNS_const_add_pc:
+ // Takes no arguments. Add to the address register of the state machine
+ // the address increment value corresponding to special opcode 255. The
+ // motivation for DW_LNS_const_add_pc is this: when the statement
+ // program needs to advance the address by a small amount, it can use a
+ // single special opcode, which occupies a single byte. When it needs
+ // to advance the address by up to twice the range of the last special
+ // opcode, it can use DW_LNS_const_add_pc followed by a special opcode,
+ // for a total of two bytes. Only if it needs to advance the address by
+ // more than twice that range will it need to use both
+ // DW_LNS_advance_pc and a special opcode, requiring three or more
+ // bytes.
+ {
+ uint8_t adjust_opcode = 255 - prologue->opcode_base;
+ dw_addr_t addr_offset = (adjust_opcode / prologue->line_range) *
+ prologue->min_inst_length;
+ state.address += addr_offset;
+ }
+ break;
+
+ case DW_LNS_fixed_advance_pc:
+ // Takes a single uhalf operand. Add to the address register of the
+ // state machine the value of the (unencoded) operand. This is the only
+ // extended opcode that takes an argument that is not a variable length
+ // number. The motivation for DW_LNS_fixed_advance_pc is this: existing
+ // assemblers cannot emit DW_LNS_advance_pc or special opcodes because
+ // they cannot encode LEB128 numbers or judge when the computation of a
+ // special opcode overflows and requires the use of DW_LNS_advance_pc.
+ // Such assemblers, however, can use DW_LNS_fixed_advance_pc instead,
+ // sacrificing compression.
+ state.address += debug_line_data.GetU16(offset_ptr);
+ break;
+
+ case DW_LNS_set_prologue_end:
+ // Takes no arguments. Set the prologue_end register of the state
+ // machine to true
+ state.prologue_end = true;
+ break;
+
+ case DW_LNS_set_epilogue_begin:
+ // Takes no arguments. Set the basic_block register of the state
+ // machine to true
+ state.epilogue_begin = true;
+ break;
+
+ case DW_LNS_set_isa:
+ // Takes a single unsigned LEB128 operand and stores it in the column
+ // register of the state machine.
+ state.isa = debug_line_data.GetULEB128(offset_ptr);
+ break;
+
+ default:
+ // Handle any unknown standard opcodes here. We know the lengths of
+ // such opcodes because they are specified in the prologue as a
+ // multiple of LEB128 operands for each opcode.
+ {
+ uint8_t i;
+ assert(static_cast<size_t>(opcode - 1) <
+ prologue->standard_opcode_lengths.size());
+ const uint8_t opcode_length =
+ prologue->standard_opcode_lengths[opcode - 1];
+ for (i = 0; i < opcode_length; ++i)
+ debug_line_data.Skip_LEB128(offset_ptr);
+ }
+ break;
+ }
+ } else {
+ // Special Opcodes
+
+ // A special opcode value is chosen based on the amount that needs
+ // to be added to the line and address registers. The maximum line
+ // increment for a special opcode is the value of the line_base field in
+ // the header, plus the value of the line_range field, minus 1 (line base
+ // + line range - 1). If the desired line increment is greater than the
+ // maximum line increment, a standard opcode must be used instead of a
+ // special opcode. The "address advance" is calculated by dividing the
+ // desired address increment by the minimum_instruction_length field from
+ // the header. The special opcode is then calculated using the following
+ // formula:
+ //
+ // opcode = (desired line increment - line_base) + (line_range * address
+ // advance) + opcode_base
+ //
+ // If the resulting opcode is greater than 255, a standard opcode must be
+ // used instead.
+ //
+ // To decode a special opcode, subtract the opcode_base from the opcode
+ // itself to give the adjusted opcode. The amount to increment the
+ // address register is the result of the adjusted opcode divided by the
+ // line_range multiplied by the minimum_instruction_length field from the
+ // header. That is:
+ //
+ // address increment = (adjusted opcode / line_range) *
+ // minimum_instruction_length
+ //
+ // The amount to increment the line register is the line_base plus the
+ // result of the adjusted opcode modulo the line_range. That is:
+ //
+ // line increment = line_base + (adjusted opcode % line_range)
+
+ uint8_t adjust_opcode = opcode - prologue->opcode_base;
+ dw_addr_t addr_offset =
+ (adjust_opcode / prologue->line_range) * prologue->min_inst_length;
+ int32_t line_offset =
+ prologue->line_base + (adjust_opcode % prologue->line_range);
+ state.line += line_offset;
+ state.address += addr_offset;
+ state.AppendRowToMatrix(*offset_ptr);
+ }
+ }
+
+ state.Finalize(*offset_ptr);
+
+ return end_offset;
+}
+
+// ParseStatementTableCallback
+static void ParseStatementTableCallback(dw_offset_t offset,
+ const DWARFDebugLine::State &state,
+ void *userData) {
+ DWARFDebugLine::LineTable *line_table = (DWARFDebugLine::LineTable *)userData;
+ if (state.row == DWARFDebugLine::State::StartParsingLineTable) {
+ // Just started parsing the line table, so lets keep a reference to the
+ // prologue using the supplied shared pointer
+ line_table->prologue = state.prologue;
+ } else if (state.row == DWARFDebugLine::State::DoneParsingLineTable) {
+ // Done parsing line table, nothing to do for the cleanup
+ } else {
+ // We have a new row, lets append it
+ line_table->AppendRow(state);
+ }
+}
+
+// ParseStatementTable
+//
+// Parse a line table at offset and populate the LineTable class with the
+// prologue and all rows.
+bool DWARFDebugLine::ParseStatementTable(
+ const DWARFDataExtractor &debug_line_data, lldb::offset_t *offset_ptr,
+ LineTable *line_table, DWARFUnit *dwarf_cu) {
+ return ParseStatementTable(debug_line_data, offset_ptr,
+ ParseStatementTableCallback, line_table, dwarf_cu);
+}
+
+inline bool DWARFDebugLine::Prologue::IsValid() const {
+ return SymbolFileDWARF::SupportedVersion(version);
+}
+
+// DWARFDebugLine::Prologue::Dump
+void DWARFDebugLine::Prologue::Dump(Log *log) {
+ uint32_t i;
+
+ log->Printf("Line table prologue:");
+ log->Printf(" total_length: 0x%8.8x", total_length);
+ log->Printf(" version: %u", version);
+ log->Printf("prologue_length: 0x%8.8x", prologue_length);
+ log->Printf("min_inst_length: %u", min_inst_length);
+ log->Printf("default_is_stmt: %u", default_is_stmt);
+ log->Printf(" line_base: %i", line_base);
+ log->Printf(" line_range: %u", line_range);
+ log->Printf(" opcode_base: %u", opcode_base);
+
+ for (i = 0; i < standard_opcode_lengths.size(); ++i) {
+ log->Printf("standard_opcode_lengths[%s] = %u", DW_LNS_value_to_name(i + 1),
+ standard_opcode_lengths[i]);
+ }
+
+ if (!include_directories.empty()) {
+ for (i = 0; i < include_directories.size(); ++i) {
+ log->Printf("include_directories[%3u] = '%s'", i + 1,
+ include_directories[i]);
+ }
+ }
+
+ if (!file_names.empty()) {
+ log->PutCString(" Dir Mod Time File Len File Name");
+ log->PutCString(" ---- ---------- ---------- "
+ "---------------------------");
+ for (i = 0; i < file_names.size(); ++i) {
+ const FileNameEntry &fileEntry = file_names[i];
+ log->Printf("file_names[%3u] %4u 0x%8.8x 0x%8.8x %s", i + 1,
+ fileEntry.dir_idx, fileEntry.mod_time, fileEntry.length,
+ fileEntry.name);
+ }
+ }
+}
+
+// DWARFDebugLine::ParsePrologue::Append
+//
+// Append the contents of the prologue to the binary stream buffer
+// void
+// DWARFDebugLine::Prologue::Append(BinaryStreamBuf& buff) const
+//{
+// uint32_t i;
+//
+// buff.Append32(total_length);
+// buff.Append16(version);
+// buff.Append32(prologue_length);
+// buff.Append8(min_inst_length);
+// buff.Append8(default_is_stmt);
+// buff.Append8(line_base);
+// buff.Append8(line_range);
+// buff.Append8(opcode_base);
+//
+// for (i=0; i<standard_opcode_lengths.size(); ++i)
+// buff.Append8(standard_opcode_lengths[i]);
+//
+// for (i=0; i<include_directories.size(); ++i)
+// buff.AppendCStr(include_directories[i].c_str());
+// buff.Append8(0); // Terminate the include directory section with empty
+// string
+//
+// for (i=0; i<file_names.size(); ++i)
+// {
+// buff.AppendCStr(file_names[i].name.c_str());
+// buff.Append32_as_ULEB128(file_names[i].dir_idx);
+// buff.Append32_as_ULEB128(file_names[i].mod_time);
+// buff.Append32_as_ULEB128(file_names[i].length);
+// }
+// buff.Append8(0); // Terminate the file names section with empty string
+//}
+
+bool DWARFDebugLine::Prologue::GetFile(uint32_t file_idx,
+ const FileSpec &comp_dir,
+ FileSpec::Style style,
+ FileSpec &file) const {
+ uint32_t idx = file_idx - 1; // File indexes are 1 based...
+ if (idx < file_names.size()) {
+ file.SetFile(file_names[idx].name, style);
+ if (file.IsRelative()) {
+ if (file_names[idx].dir_idx > 0) {
+ const uint32_t dir_idx = file_names[idx].dir_idx - 1;
+ if (dir_idx < include_directories.size()) {
+ file.PrependPathComponent(include_directories[dir_idx]);
+ if (!file.IsRelative())
+ return true;
+ }
+ }
+
+ if (comp_dir)
+ file.PrependPathComponent(comp_dir);
+ }
+ return true;
+ }
+ return false;
+}
+
+void DWARFDebugLine::LineTable::AppendRow(const DWARFDebugLine::Row &state) {
+ rows.push_back(state);
+}
+
+// Compare function for the binary search in
+// DWARFDebugLine::LineTable::LookupAddress()
+static bool FindMatchingAddress(const DWARFDebugLine::Row &row1,
+ const DWARFDebugLine::Row &row2) {
+ return row1.address < row2.address;
+}
+
+// DWARFDebugLine::LineTable::LookupAddress
+uint32_t DWARFDebugLine::LineTable::LookupAddress(dw_addr_t address,
+ dw_addr_t cu_high_pc) const {
+ uint32_t index = UINT32_MAX;
+ if (!rows.empty()) {
+ // Use the lower_bound algorithm to perform a binary search since we know
+ // that our line table data is ordered by address.
+ DWARFDebugLine::Row row;
+ row.address = address;
+ Row::const_iterator begin_pos = rows.begin();
+ Row::const_iterator end_pos = rows.end();
+ Row::const_iterator pos =
+ lower_bound(begin_pos, end_pos, row, FindMatchingAddress);
+ if (pos == end_pos) {
+ if (address < cu_high_pc)
+ return rows.size() - 1;
+ } else {
+ // Rely on fact that we are using a std::vector and we can do pointer
+ // arithmetic to find the row index (which will be one less that what we
+ // found since it will find the first position after the current address)
+ // since std::vector iterators are just pointers to the container type.
+ index = pos - begin_pos;
+ if (pos->address > address) {
+ if (index > 0)
+ --index;
+ else
+ index = UINT32_MAX;
+ }
+ }
+ }
+ return index; // Failed to find address
+}
+
+// DWARFDebugLine::Row::Row
+DWARFDebugLine::Row::Row(bool default_is_stmt)
+ : address(0), line(1), column(0), file(1), is_stmt(default_is_stmt),
+ basic_block(false), end_sequence(false), prologue_end(false),
+ epilogue_begin(false), isa(0) {}
+
+// Called after a row is appended to the matrix
+void DWARFDebugLine::Row::PostAppend() {
+ basic_block = false;
+ prologue_end = false;
+ epilogue_begin = false;
+}
+
+// DWARFDebugLine::Row::Reset
+void DWARFDebugLine::Row::Reset(bool default_is_stmt) {
+ address = 0;
+ line = 1;
+ column = 0;
+ file = 1;
+ is_stmt = default_is_stmt;
+ basic_block = false;
+ end_sequence = false;
+ prologue_end = false;
+ epilogue_begin = false;
+ isa = 0;
+}
+// DWARFDebugLine::Row::Dump
+void DWARFDebugLine::Row::Dump(Log *log) const {
+ log->Printf("0x%16.16" PRIx64 " %6u %6u %6u %3u %s%s%s%s%s", address, line,
+ column, file, isa, is_stmt ? " is_stmt" : "",
+ basic_block ? " basic_block" : "",
+ prologue_end ? " prologue_end" : "",
+ epilogue_begin ? " epilogue_begin" : "",
+ end_sequence ? " end_sequence" : "");
+}
+
+// Compare function LineTable structures
+static bool AddressLessThan(const DWARFDebugLine::Row &a,
+ const DWARFDebugLine::Row &b) {
+ return a.address < b.address;
+}
+
+// Insert a row at the correct address if the addresses can be out of order
+// which can only happen when we are linking a line table that may have had
+// it's contents rearranged.
+void DWARFDebugLine::Row::Insert(Row::collection &state_coll,
+ const Row &state) {
+ // If we don't have anything yet, or if the address of the last state in our
+ // line table is less than the current one, just append the current state
+ if (state_coll.empty() || AddressLessThan(state_coll.back(), state)) {
+ state_coll.push_back(state);
+ } else {
+ // Do a binary search for the correct entry
+ pair<Row::iterator, Row::iterator> range(equal_range(
+ state_coll.begin(), state_coll.end(), state, AddressLessThan));
+
+ // If the addresses are equal, we can safely replace the previous entry
+ // with the current one if the one it is replacing is an end_sequence
+ // entry. We currently always place an extra end sequence when ever we exit
+ // a valid address range for a function in case the functions get
+ // rearranged by optimizations or by order specifications. These extra end
+ // sequences will disappear by getting replaced with valid consecutive
+ // entries within a compile unit if there are no gaps.
+ if (range.first == range.second) {
+ state_coll.insert(range.first, state);
+ } else {
+ if ((distance(range.first, range.second) == 1) &&
+ range.first->end_sequence == true) {
+ *range.first = state;
+ } else {
+ state_coll.insert(range.second, state);
+ }
+ }
+ }
+}
+
+// DWARFDebugLine::State::State
+DWARFDebugLine::State::State(Prologue::shared_ptr &p, Log *l,
+ DWARFDebugLine::State::Callback cb, void *userData)
+ : Row(p->default_is_stmt), prologue(p), log(l), callback(cb),
+ callbackUserData(userData), row(StartParsingLineTable) {
+ // Call the callback with the initial row state of zero for the prologue
+ if (callback)
+ callback(0, *this, callbackUserData);
+}
+
+// DWARFDebugLine::State::Reset
+void DWARFDebugLine::State::Reset() { Row::Reset(prologue->default_is_stmt); }
+
+// DWARFDebugLine::State::AppendRowToMatrix
+void DWARFDebugLine::State::AppendRowToMatrix(dw_offset_t offset) {
+ // Each time we are to add an entry into the line table matrix call the
+ // callback function so that someone can do something with the current state
+ // of the state machine (like build a line table or dump the line table!)
+ if (log) {
+ if (row == 0) {
+ log->PutCString("Address Line Column File ISA Flags");
+ log->PutCString(
+ "------------------ ------ ------ ------ --- -------------");
+ }
+ Dump(log);
+ }
+
+ ++row; // Increase the row number before we call our callback for a real row
+ if (callback)
+ callback(offset, *this, callbackUserData);
+ PostAppend();
+}
+
+// DWARFDebugLine::State::Finalize
+void DWARFDebugLine::State::Finalize(dw_offset_t offset) {
+ // Call the callback with a special row state when we are done parsing a line
+ // table
+ row = DoneParsingLineTable;
+ if (callback)
+ callback(offset, *this, callbackUserData);
+}
+
+// void
+// DWARFDebugLine::AppendLineTableData
+//(
+// const DWARFDebugLine::Prologue* prologue,
+// const DWARFDebugLine::Row::collection& state_coll,
+// const uint32_t addr_size,
+// BinaryStreamBuf &debug_line_data
+//)
+//{
+// if (state_coll.empty())
+// {
+// // We have no entries, just make an empty line table
+// debug_line_data.Append8(0);
+// debug_line_data.Append8(1);
+// debug_line_data.Append8(DW_LNE_end_sequence);
+// }
+// else
+// {
+// DWARFDebugLine::Row::const_iterator pos;
+// Row::const_iterator end = state_coll.end();
+// bool default_is_stmt = prologue->default_is_stmt;
+// const DWARFDebugLine::Row reset_state(default_is_stmt);
+// const DWARFDebugLine::Row* prev_state = &reset_state;
+// const int32_t max_line_increment_for_special_opcode =
+// prologue->MaxLineIncrementForSpecialOpcode();
+// for (pos = state_coll.begin(); pos != end; ++pos)
+// {
+// const DWARFDebugLine::Row& curr_state = *pos;
+// int32_t line_increment = 0;
+// dw_addr_t addr_offset = curr_state.address - prev_state->address;
+// dw_addr_t addr_advance = (addr_offset) / prologue->min_inst_length;
+// line_increment = (int32_t)(curr_state.line - prev_state->line);
+//
+// // If our previous state was the reset state, then let's emit the
+// // address to keep GDB's DWARF parser happy. If we don't start each
+// // sequence with a DW_LNE_set_address opcode, the line table won't
+// // get slid properly in GDB.
+//
+// if (prev_state == &reset_state)
+// {
+// debug_line_data.Append8(0); // Extended opcode
+// debug_line_data.Append32_as_ULEB128(addr_size + 1); // Length of
+// opcode bytes
+// debug_line_data.Append8(DW_LNE_set_address);
+// debug_line_data.AppendMax64(curr_state.address, addr_size);
+// addr_advance = 0;
+// }
+//
+// if (prev_state->file != curr_state.file)
+// {
+// debug_line_data.Append8(DW_LNS_set_file);
+// debug_line_data.Append32_as_ULEB128(curr_state.file);
+// }
+//
+// if (prev_state->column != curr_state.column)
+// {
+// debug_line_data.Append8(DW_LNS_set_column);
+// debug_line_data.Append32_as_ULEB128(curr_state.column);
+// }
+//
+// // Don't do anything fancy if we are at the end of a sequence
+// // as we don't want to push any extra rows since the
+// DW_LNE_end_sequence
+// // will push a row itself!
+// if (curr_state.end_sequence)
+// {
+// if (line_increment != 0)
+// {
+// debug_line_data.Append8(DW_LNS_advance_line);
+// debug_line_data.Append32_as_SLEB128(line_increment);
+// }
+//
+// if (addr_advance > 0)
+// {
+// debug_line_data.Append8(DW_LNS_advance_pc);
+// debug_line_data.Append32_as_ULEB128(addr_advance);
+// }
+//
+// // Now push the end sequence on!
+// debug_line_data.Append8(0);
+// debug_line_data.Append8(1);
+// debug_line_data.Append8(DW_LNE_end_sequence);
+//
+// prev_state = &reset_state;
+// }
+// else
+// {
+// if (line_increment || addr_advance)
+// {
+// if (line_increment > max_line_increment_for_special_opcode)
+// {
+// debug_line_data.Append8(DW_LNS_advance_line);
+// debug_line_data.Append32_as_SLEB128(line_increment);
+// line_increment = 0;
+// }
+//
+// uint32_t special_opcode = (line_increment >=
+// prologue->line_base) ? ((line_increment -
+// prologue->line_base) + (prologue->line_range * addr_advance)
+// + prologue->opcode_base) : 256;
+// if (special_opcode > 255)
+// {
+// // Both the address and line won't fit in one special
+// opcode
+// // check to see if just the line advance will?
+// uint32_t special_opcode_line = ((line_increment >=
+// prologue->line_base) && (line_increment != 0)) ?
+// ((line_increment - prologue->line_base) +
+// prologue->opcode_base) : 256;
+//
+//
+// if (special_opcode_line > 255)
+// {
+// // Nope, the line advance won't fit by itself, check
+// the address increment by itself
+// uint32_t special_opcode_addr = addr_advance ?
+// ((0 - prologue->line_base) +
+// (prologue->line_range * addr_advance) +
+// prologue->opcode_base) : 256;
+//
+// if (special_opcode_addr > 255)
+// {
+// // Neither the address nor the line will fit in
+// a
+// // special opcode, we must manually enter both
+// then
+// // do a DW_LNS_copy to push a row (special
+// opcode
+// // automatically imply a new row is pushed)
+// if (line_increment != 0)
+// {
+// debug_line_data.Append8(DW_LNS_advance_line);
+// debug_line_data.Append32_as_SLEB128(line_increment);
+// }
+//
+// if (addr_advance > 0)
+// {
+// debug_line_data.Append8(DW_LNS_advance_pc);
+// debug_line_data.Append32_as_ULEB128(addr_advance);
+// }
+//
+// // Now push a row onto the line table manually
+// debug_line_data.Append8(DW_LNS_copy);
+//
+// }
+// else
+// {
+// // The address increment alone will fit into a
+// special opcode
+// // so modify our line change, then issue a
+// special opcode
+// // for the address increment and it will push a
+// row into the
+// // line table
+// if (line_increment != 0)
+// {
+// debug_line_data.Append8(DW_LNS_advance_line);
+// debug_line_data.Append32_as_SLEB128(line_increment);
+// }
+//
+// // Advance of line and address will fit into a
+// single byte special opcode
+// // and this will also push a row onto the line
+// table
+// debug_line_data.Append8(special_opcode_addr);
+// }
+// }
+// else
+// {
+// // The line change alone will fit into a special
+// opcode
+// // so modify our address increment first, then issue
+// a
+// // special opcode for the line change and it will
+// push
+// // a row into the line table
+// if (addr_advance > 0)
+// {
+// debug_line_data.Append8(DW_LNS_advance_pc);
+// debug_line_data.Append32_as_ULEB128(addr_advance);
+// }
+//
+// // Advance of line and address will fit into a
+// single byte special opcode
+// // and this will also push a row onto the line table
+// debug_line_data.Append8(special_opcode_line);
+// }
+// }
+// else
+// {
+// // Advance of line and address will fit into a single
+// byte special opcode
+// // and this will also push a row onto the line table
+// debug_line_data.Append8(special_opcode);
+// }
+// }
+// prev_state = &curr_state;
+// }
+// }
+// }
+//}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h
new file mode 100644
index 000000000000..0d236ca686b5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h
@@ -0,0 +1,227 @@
+//===-- DWARFDebugLine.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 SymbolFileDWARF_DWARFDebugLine_h_
+#define SymbolFileDWARF_DWARFDebugLine_h_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/lldb-private.h"
+
+#include "DWARFDataExtractor.h"
+#include "DWARFDefines.h"
+
+#include "llvm/Support/MD5.h"
+
+class DWARFUnit;
+class SymbolFileDWARF;
+
+// DWARFDebugLine
+class DWARFDebugLine {
+public:
+ // FileNameEntry
+ struct FileNameEntry {
+ FileNameEntry()
+ : name(nullptr), dir_idx(0), mod_time(0), length(0), checksum() {}
+
+ const char *name;
+ dw_sleb128_t dir_idx;
+ dw_sleb128_t mod_time;
+ dw_sleb128_t length;
+ llvm::MD5::MD5Result checksum;
+ };
+
+ // Prologue
+ struct Prologue {
+
+ Prologue()
+ : total_length(0), version(0), prologue_length(0), min_inst_length(0),
+ default_is_stmt(0), line_base(0), line_range(0), opcode_base(0),
+ standard_opcode_lengths(), include_directories(), file_names() {}
+
+ typedef std::shared_ptr<Prologue> shared_ptr;
+
+ uint32_t total_length; // The size in bytes of the statement information for
+ // this compilation unit (not including the
+ // total_length field itself).
+ uint16_t
+ version; // Version identifier for the statement information format.
+
+ uint8_t address_size;
+ uint8_t segment_selector_size;
+
+ uint32_t prologue_length; // The number of bytes following the
+ // prologue_length field to the beginning of the
+ // first byte of the statement program itself.
+ uint8_t min_inst_length; // The size in bytes of the smallest target machine
+ // instruction. Statement program opcodes that
+ // alter the address register first multiply their
+ // operands by this value.
+ uint8_t maximum_operations_per_instruction; // New in DWARF4. The maximum
+ // number of individual
+ // operations that may be
+ // encoded in an instruction.
+ uint8_t default_is_stmt; // The initial value of theis_stmtregister.
+ int8_t line_base; // This parameter affects the meaning of the special
+ // opcodes. See below.
+ uint8_t line_range; // This parameter affects the meaning of the special
+ // opcodes. See below.
+ uint8_t opcode_base; // The number assigned to the first special opcode.
+ std::vector<uint8_t> standard_opcode_lengths;
+ std::vector<const char *> include_directories;
+ std::vector<FileNameEntry> file_names;
+
+ int32_t MaxLineIncrementForSpecialOpcode() const {
+ return line_base + (int8_t)line_range - 1;
+ }
+ bool IsValid() const;
+ // void Append(BinaryStreamBuf& buff) const;
+ void Dump(lldb_private::Log *log);
+ void Clear() {
+ total_length = version = prologue_length = min_inst_length = line_base =
+ line_range = opcode_base = 0;
+ line_base = 0;
+ standard_opcode_lengths.clear();
+ include_directories.clear();
+ file_names.clear();
+ }
+ bool GetFile(uint32_t file_idx, const lldb_private::FileSpec &cu_comp_dir,
+ lldb_private::FileSpec::Style style,
+ lldb_private::FileSpec &file) const;
+ };
+
+ // Standard .debug_line state machine structure
+ struct Row {
+ typedef std::vector<Row> collection;
+ typedef collection::iterator iterator;
+ typedef collection::const_iterator const_iterator;
+
+ Row(bool default_is_stmt = false);
+ virtual ~Row() {}
+ void PostAppend();
+ void Reset(bool default_is_stmt);
+ void Dump(lldb_private::Log *log) const;
+ static void Insert(Row::collection &state_coll, const Row &state);
+
+ dw_addr_t address; // The program-counter value corresponding to a machine
+ // instruction generated by the compiler.
+ uint32_t line; // An unsigned integer indicating a source line number. Lines
+ // are numbered beginning at 1. The compiler may emit the
+ // value 0 in cases where an instruction cannot be attributed
+ // to any source line.
+ uint16_t column; // An unsigned integer indicating a column number within a
+ // source line. Columns are numbered beginning at 1. The
+ // value 0 is reserved to indicate that a statement begins
+ // at the 'left edge' of the line.
+ uint16_t file; // An unsigned integer indicating the identity of the source
+ // file corresponding to a machine instruction.
+ uint8_t is_stmt : 1, // A boolean indicating that the current instruction is
+ // the beginning of a statement.
+ basic_block : 1, // A boolean indicating that the current instruction is
+ // the beginning of a basic block.
+ end_sequence : 1, // A boolean indicating that the current address is
+ // that of the first byte after the end of a sequence
+ // of target machine instructions.
+ prologue_end : 1, // A boolean indicating that the current address is
+ // one (of possibly many) where execution should be
+ // suspended for an entry breakpoint of a function.
+ epilogue_begin : 1; // A boolean indicating that the current address is
+ // one (of possibly many) where execution should be
+ // suspended for an exit breakpoint of a function.
+ uint32_t isa; // An unsigned integer whose value encodes the applicable
+ // instruction set architecture for the current instruction.
+ };
+
+ // LineTable
+ struct LineTable {
+ typedef std::shared_ptr<LineTable> shared_ptr;
+
+ LineTable() : prologue(), rows() {}
+
+ void AppendRow(const DWARFDebugLine::Row &state);
+ void Clear() {
+ prologue.reset();
+ rows.clear();
+ }
+
+ uint32_t LookupAddress(dw_addr_t address, dw_addr_t cu_high_pc) const;
+
+ Prologue::shared_ptr prologue;
+ Row::collection rows;
+ };
+
+ // State
+ struct State : public Row {
+ typedef void (*Callback)(dw_offset_t offset, const State &state,
+ void *userData);
+
+ // Special row codes used when calling the callback
+ enum { StartParsingLineTable = 0, DoneParsingLineTable = -1 };
+
+ State(Prologue::shared_ptr &prologue_sp, lldb_private::Log *log,
+ Callback callback, void *userData);
+
+ void AppendRowToMatrix(dw_offset_t offset);
+
+ void Finalize(dw_offset_t offset);
+
+ void Reset();
+
+ Prologue::shared_ptr prologue;
+ lldb_private::Log *log;
+ Callback callback; // Callback function that gets called each time an entry
+ // is to be added to the matrix
+ void *callbackUserData;
+ int row; // The row number that starts at zero for the prologue, and
+ // increases for each row added to the matrix
+ private:
+ DISALLOW_COPY_AND_ASSIGN(State);
+ };
+
+ static bool
+ ParseSupportFiles(const lldb::ModuleSP &module_sp,
+ const lldb_private::DWARFDataExtractor &debug_line_data,
+ dw_offset_t stmt_list,
+ lldb_private::FileSpecList &support_files,
+ DWARFUnit *dwarf_cu);
+ static bool
+ ParsePrologue(const lldb_private::DWARFDataExtractor &debug_line_data,
+ lldb::offset_t *offset_ptr, Prologue *prologue,
+ DWARFUnit *dwarf_cu = nullptr);
+ static bool
+ ParseStatementTable(const lldb_private::DWARFDataExtractor &debug_line_data,
+ lldb::offset_t *offset_ptr, State::Callback callback,
+ void *userData, DWARFUnit *dwarf_cu);
+ static bool
+ ParseStatementTable(const lldb_private::DWARFDataExtractor &debug_line_data,
+ lldb::offset_t *offset_ptr, LineTable *line_table,
+ DWARFUnit *dwarf_cu);
+ static void Parse(const lldb_private::DWARFDataExtractor &debug_line_data,
+ DWARFDebugLine::State::Callback callback, void *userData);
+ // static void AppendLineTableData(const DWARFDebugLine::Prologue* prologue,
+ // const DWARFDebugLine::Row::collection& state_coll, const uint32_t
+ // addr_size, BinaryStreamBuf &debug_line_data);
+
+ DWARFDebugLine() : m_lineTableMap() {}
+
+ void Parse(const lldb_private::DWARFDataExtractor &debug_line_data);
+ void ParseIfNeeded(const lldb_private::DWARFDataExtractor &debug_line_data);
+ LineTable::shared_ptr GetLineTable(const dw_offset_t offset) const;
+
+protected:
+ typedef std::map<dw_offset_t, LineTable::shared_ptr> LineTableMap;
+ typedef LineTableMap::iterator LineTableIter;
+ typedef LineTableMap::const_iterator LineTableConstIter;
+
+ LineTableMap m_lineTableMap;
+};
+
+#endif // SymbolFileDWARF_DWARFDebugLine_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacro.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacro.cpp
new file mode 100644
index 000000000000..4238be7ec1c3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacro.cpp
@@ -0,0 +1,126 @@
+//===-- DWARFDebugMacro.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 "DWARFDebugMacro.h"
+#include "SymbolFileDWARF.h"
+
+#include "lldb/Symbol/DebugMacros.h"
+
+#include "DWARFDataExtractor.h"
+
+using namespace lldb_private;
+
+DWARFDebugMacroHeader
+DWARFDebugMacroHeader::ParseHeader(const DWARFDataExtractor &debug_macro_data,
+ lldb::offset_t *offset) {
+ DWARFDebugMacroHeader header;
+
+ // Skip over the version field in header.
+ header.m_version = debug_macro_data.GetU16(offset);
+
+ uint8_t flags = debug_macro_data.GetU8(offset);
+ header.m_offset_is_64_bit = (flags & OFFSET_SIZE_MASK) != 0;
+
+ if (flags & DEBUG_LINE_OFFSET_MASK) {
+ if (header.m_offset_is_64_bit)
+ header.m_debug_line_offset = debug_macro_data.GetU64(offset);
+ else
+ header.m_debug_line_offset = debug_macro_data.GetU32(offset);
+ }
+
+ // Skip over the operands table if it is present.
+ if (flags & OPCODE_OPERANDS_TABLE_MASK)
+ SkipOperandTable(debug_macro_data, offset);
+
+ return header;
+}
+
+void DWARFDebugMacroHeader::SkipOperandTable(
+ const DWARFDataExtractor &debug_macro_data, lldb::offset_t *offset) {
+ uint8_t entry_count = debug_macro_data.GetU8(offset);
+ for (uint8_t i = 0; i < entry_count; i++) {
+ // Skip over the opcode number.
+ debug_macro_data.GetU8(offset);
+
+ uint64_t operand_count = debug_macro_data.GetULEB128(offset);
+
+ for (uint64_t j = 0; j < operand_count; j++) {
+ // Skip over the operand form
+ debug_macro_data.GetU8(offset);
+ }
+ }
+}
+
+void DWARFDebugMacroEntry::ReadMacroEntries(
+ const DWARFDataExtractor &debug_macro_data,
+ const DWARFDataExtractor &debug_str_data, const bool offset_is_64_bit,
+ lldb::offset_t *offset, SymbolFileDWARF *sym_file_dwarf,
+ DebugMacrosSP &debug_macros_sp) {
+ llvm::dwarf::MacroEntryType type =
+ static_cast<llvm::dwarf::MacroEntryType>(debug_macro_data.GetU8(offset));
+ while (type != 0) {
+ lldb::offset_t new_offset = 0, str_offset = 0;
+ uint32_t line = 0;
+ const char *macro_str = nullptr;
+ uint32_t debug_line_file_idx = 0;
+
+ switch (type) {
+ case DW_MACRO_define:
+ case DW_MACRO_undef:
+ line = debug_macro_data.GetULEB128(offset);
+ macro_str = debug_macro_data.GetCStr(offset);
+ if (type == DW_MACRO_define)
+ debug_macros_sp->AddMacroEntry(
+ DebugMacroEntry::CreateDefineEntry(line, macro_str));
+ else
+ debug_macros_sp->AddMacroEntry(
+ DebugMacroEntry::CreateUndefEntry(line, macro_str));
+ break;
+ case DW_MACRO_define_strp:
+ case DW_MACRO_undef_strp:
+ line = debug_macro_data.GetULEB128(offset);
+ if (offset_is_64_bit)
+ str_offset = debug_macro_data.GetU64(offset);
+ else
+ str_offset = debug_macro_data.GetU32(offset);
+ macro_str = debug_str_data.GetCStr(&str_offset);
+ if (type == DW_MACRO_define_strp)
+ debug_macros_sp->AddMacroEntry(
+ DebugMacroEntry::CreateDefineEntry(line, macro_str));
+ else
+ debug_macros_sp->AddMacroEntry(
+ DebugMacroEntry::CreateUndefEntry(line, macro_str));
+ break;
+ case DW_MACRO_start_file:
+ line = debug_macro_data.GetULEB128(offset);
+ debug_line_file_idx = debug_macro_data.GetULEB128(offset);
+ debug_macros_sp->AddMacroEntry(
+ DebugMacroEntry::CreateStartFileEntry(line, debug_line_file_idx));
+ break;
+ case DW_MACRO_end_file:
+ // This operation has no operands.
+ debug_macros_sp->AddMacroEntry(DebugMacroEntry::CreateEndFileEntry());
+ break;
+ case DW_MACRO_import:
+ if (offset_is_64_bit)
+ new_offset = debug_macro_data.GetU64(offset);
+ else
+ new_offset = debug_macro_data.GetU32(offset);
+ debug_macros_sp->AddMacroEntry(DebugMacroEntry::CreateIndirectEntry(
+ sym_file_dwarf->ParseDebugMacros(&new_offset)));
+ break;
+ default:
+ // TODO: Add support for other standard operations.
+ // TODO: Provide mechanism to hook handling of non-standard/extension
+ // operands.
+ return;
+ }
+ type = static_cast<llvm::dwarf::MacroEntryType>(
+ debug_macro_data.GetU8(offset));
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacro.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacro.h
new file mode 100644
index 000000000000..c3a93a9f4d14
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacro.h
@@ -0,0 +1,61 @@
+//===-- DWARFDebugMacro.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 SymbolFileDWARF_DWARFDebugMacro_h_
+#define SymbolFileDWARF_DWARFDebugMacro_h_
+
+#include <map>
+
+#include "lldb/Core/dwarf.h"
+#include "lldb/Symbol/DebugMacros.h"
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+
+class DWARFDataExtractor;
+
+} // namespace lldb_private
+
+class SymbolFileDWARF;
+
+class DWARFDebugMacroHeader {
+public:
+ enum HeaderFlagMask {
+ OFFSET_SIZE_MASK = 0x1,
+ DEBUG_LINE_OFFSET_MASK = 0x2,
+ OPCODE_OPERANDS_TABLE_MASK = 0x4
+ };
+
+ static DWARFDebugMacroHeader
+ ParseHeader(const lldb_private::DWARFDataExtractor &debug_macro_data,
+ lldb::offset_t *offset);
+
+ bool OffsetIs64Bit() const { return m_offset_is_64_bit; }
+
+private:
+ static void
+ SkipOperandTable(const lldb_private::DWARFDataExtractor &debug_macro_data,
+ lldb::offset_t *offset);
+
+ uint16_t m_version;
+ bool m_offset_is_64_bit;
+ uint64_t m_debug_line_offset;
+};
+
+class DWARFDebugMacroEntry {
+public:
+ static void
+ ReadMacroEntries(const lldb_private::DWARFDataExtractor &debug_macro_data,
+ const lldb_private::DWARFDataExtractor &debug_str_data,
+ const bool offset_is_64_bit, lldb::offset_t *sect_offset,
+ SymbolFileDWARF *sym_file_dwarf,
+ lldb_private::DebugMacrosSP &debug_macros_sp);
+};
+
+#endif // SymbolFileDWARF_DWARFDebugMacro_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp
new file mode 100644
index 000000000000..207c71211c9a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp
@@ -0,0 +1,290 @@
+//===-- DWARFDebugRanges.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 "DWARFDebugRanges.h"
+#include "DWARFUnit.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb_private;
+
+static dw_addr_t GetBaseAddressMarker(uint32_t addr_size) {
+ switch(addr_size) {
+ case 2:
+ return 0xffff;
+ case 4:
+ return 0xffffffff;
+ case 8:
+ return 0xffffffffffffffff;
+ }
+ llvm_unreachable("GetBaseAddressMarker unsupported address size.");
+}
+
+DWARFDebugRanges::DWARFDebugRanges() : m_range_map() {}
+
+void DWARFDebugRanges::Extract(DWARFContext &context) {
+ DWARFRangeList range_list;
+ lldb::offset_t offset = 0;
+ dw_offset_t debug_ranges_offset = offset;
+ while (Extract(context, &offset, range_list)) {
+ range_list.Sort();
+ m_range_map[debug_ranges_offset] = range_list;
+ debug_ranges_offset = offset;
+ }
+}
+
+bool DWARFDebugRanges::Extract(DWARFContext &context,
+ lldb::offset_t *offset_ptr,
+ DWARFRangeList &range_list) {
+ range_list.Clear();
+
+ lldb::offset_t range_offset = *offset_ptr;
+ const DWARFDataExtractor &debug_ranges_data = context.getOrLoadRangesData();
+ uint32_t addr_size = debug_ranges_data.GetAddressByteSize();
+ dw_addr_t base_addr = 0;
+ dw_addr_t base_addr_marker = GetBaseAddressMarker(addr_size);
+
+ while (
+ debug_ranges_data.ValidOffsetForDataOfSize(*offset_ptr, 2 * addr_size)) {
+ dw_addr_t begin = debug_ranges_data.GetMaxU64(offset_ptr, addr_size);
+ dw_addr_t end = debug_ranges_data.GetMaxU64(offset_ptr, addr_size);
+
+ if (!begin && !end) {
+ // End of range list
+ break;
+ }
+
+ if (begin == base_addr_marker) {
+ base_addr = end;
+ continue;
+ }
+
+ // Filter out empty ranges
+ if (begin < end)
+ range_list.Append(DWARFRangeList::Entry(begin + base_addr, end - begin));
+ }
+
+ // Make sure we consumed at least something
+ return range_offset != *offset_ptr;
+}
+
+void DWARFDebugRanges::Dump(Stream &s,
+ const DWARFDataExtractor &debug_ranges_data,
+ lldb::offset_t *offset_ptr,
+ dw_addr_t cu_base_addr) {
+ uint32_t addr_size = s.GetAddressByteSize();
+
+ dw_addr_t base_addr = cu_base_addr;
+ while (
+ debug_ranges_data.ValidOffsetForDataOfSize(*offset_ptr, 2 * addr_size)) {
+ dw_addr_t begin = debug_ranges_data.GetMaxU64(offset_ptr, addr_size);
+ dw_addr_t end = debug_ranges_data.GetMaxU64(offset_ptr, addr_size);
+ // Extend 4 byte addresses that consists of 32 bits of 1's to be 64 bits of
+ // ones
+ if (begin == 0xFFFFFFFFull && addr_size == 4)
+ begin = LLDB_INVALID_ADDRESS;
+
+ s.Indent();
+ if (begin == 0 && end == 0) {
+ s.PutCString(" End");
+ break;
+ } else if (begin == LLDB_INVALID_ADDRESS) {
+ // A base address selection entry
+ base_addr = end;
+ s.Address(base_addr, sizeof(dw_addr_t), " Base address = ");
+ } else {
+ // Convert from offset to an address
+ dw_addr_t begin_addr = begin + base_addr;
+ dw_addr_t end_addr = end + base_addr;
+
+ s.AddressRange(begin_addr, end_addr, sizeof(dw_addr_t), nullptr);
+ }
+ }
+}
+
+bool DWARFDebugRanges::FindRanges(const DWARFUnit *cu,
+ dw_offset_t debug_ranges_offset,
+ DWARFRangeList &range_list) const {
+ dw_addr_t debug_ranges_address = cu->GetRangesBase() + debug_ranges_offset;
+ range_map_const_iterator pos = m_range_map.find(debug_ranges_address);
+ if (pos != m_range_map.end()) {
+ range_list = pos->second;
+
+ // All DW_AT_ranges are relative to the base address of the compile
+ // unit. We add the compile unit base address to make sure all the
+ // addresses are properly fixed up.
+ range_list.Slide(cu->GetBaseAddress());
+ return true;
+ }
+ return false;
+}
+
+uint64_t DWARFDebugRanges::GetOffset(size_t Index) const {
+ lldbassert(false && "DW_FORM_rnglistx is not present before DWARF5");
+ return 0;
+}
+
+bool DWARFDebugRngLists::ExtractRangeList(
+ const DWARFDataExtractor &data, uint8_t addrSize,
+ lldb::offset_t *offset_ptr, std::vector<RngListEntry> &rangeList) {
+ rangeList.clear();
+
+ bool error = false;
+ while (!error) {
+ switch (data.GetU8(offset_ptr)) {
+ case DW_RLE_end_of_list:
+ return true;
+
+ case DW_RLE_start_length: {
+ dw_addr_t begin = data.GetMaxU64(offset_ptr, addrSize);
+ dw_addr_t len = data.GetULEB128(offset_ptr);
+ rangeList.push_back({DW_RLE_start_length, begin, len});
+ break;
+ }
+
+ case DW_RLE_start_end: {
+ dw_addr_t begin = data.GetMaxU64(offset_ptr, addrSize);
+ dw_addr_t end = data.GetMaxU64(offset_ptr, addrSize);
+ rangeList.push_back({DW_RLE_start_end, begin, end});
+ break;
+ }
+
+ case DW_RLE_base_address: {
+ dw_addr_t base = data.GetMaxU64(offset_ptr, addrSize);
+ rangeList.push_back({DW_RLE_base_address, base, 0});
+ break;
+ }
+
+ case DW_RLE_offset_pair: {
+ dw_addr_t begin = data.GetULEB128(offset_ptr);
+ dw_addr_t end = data.GetULEB128(offset_ptr);
+ rangeList.push_back({DW_RLE_offset_pair, begin, end});
+ break;
+ }
+
+ case DW_RLE_base_addressx: {
+ dw_addr_t base = data.GetULEB128(offset_ptr);
+ rangeList.push_back({DW_RLE_base_addressx, base, 0});
+ break;
+ }
+
+ case DW_RLE_startx_endx: {
+ dw_addr_t start = data.GetULEB128(offset_ptr);
+ dw_addr_t end = data.GetULEB128(offset_ptr);
+ rangeList.push_back({DW_RLE_startx_endx, start, end});
+ break;
+ }
+
+ case DW_RLE_startx_length: {
+ dw_addr_t start = data.GetULEB128(offset_ptr);
+ dw_addr_t length = data.GetULEB128(offset_ptr);
+ rangeList.push_back({DW_RLE_startx_length, start, length});
+ break;
+ }
+
+ default:
+ lldbassert(0 && "unknown range list entry encoding");
+ error = true;
+ }
+ }
+
+ return false;
+}
+
+static uint64_t ReadAddressFromDebugAddrSection(const DWARFUnit *cu,
+ uint32_t index) {
+ uint32_t index_size = cu->GetAddressByteSize();
+ dw_offset_t addr_base = cu->GetAddrBase();
+ lldb::offset_t offset = addr_base + index * index_size;
+ return cu->GetSymbolFileDWARF()
+ .GetDWARFContext()
+ .getOrLoadAddrData()
+ .GetMaxU64(&offset, index_size);
+}
+
+bool DWARFDebugRngLists::FindRanges(const DWARFUnit *cu,
+ dw_offset_t debug_ranges_offset,
+ DWARFRangeList &range_list) const {
+ range_list.Clear();
+ dw_addr_t debug_ranges_address = cu->GetRangesBase() + debug_ranges_offset;
+ auto pos = m_range_map.find(debug_ranges_address);
+ if (pos != m_range_map.end()) {
+ dw_addr_t BaseAddr = cu->GetBaseAddress();
+ for (const RngListEntry &E : pos->second) {
+ switch (E.encoding) {
+ case DW_RLE_start_length:
+ range_list.Append(DWARFRangeList::Entry(E.value0, E.value1));
+ break;
+ case DW_RLE_base_address:
+ BaseAddr = E.value0;
+ break;
+ case DW_RLE_start_end:
+ range_list.Append(DWARFRangeList::Entry(E.value0, E.value1 - E.value0));
+ break;
+ case DW_RLE_offset_pair:
+ range_list.Append(
+ DWARFRangeList::Entry(BaseAddr + E.value0, E.value1 - E.value0));
+ break;
+ case DW_RLE_base_addressx: {
+ BaseAddr = ReadAddressFromDebugAddrSection(cu, E.value0);
+ break;
+ }
+ case DW_RLE_startx_endx: {
+ dw_addr_t start = ReadAddressFromDebugAddrSection(cu, E.value0);
+ dw_addr_t end = ReadAddressFromDebugAddrSection(cu, E.value1);
+ range_list.Append(DWARFRangeList::Entry(start, end - start));
+ break;
+ }
+ case DW_RLE_startx_length: {
+ dw_addr_t start = ReadAddressFromDebugAddrSection(cu, E.value0);
+ range_list.Append(DWARFRangeList::Entry(start, E.value1));
+ break;
+ }
+ default:
+ llvm_unreachable("unexpected encoding");
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+void DWARFDebugRngLists::Extract(DWARFContext &context) {
+ const DWARFDataExtractor &data = context.getOrLoadRngListsData();
+ lldb::offset_t offset = 0;
+
+ uint64_t length = data.GetU32(&offset);
+ // FIXME: Handle DWARF64.
+ lldb::offset_t end = offset + length;
+
+ // Check version.
+ if (data.GetU16(&offset) < 5)
+ return;
+
+ uint8_t addrSize = data.GetU8(&offset);
+
+ // We do not support non-zero segment selector size.
+ if (data.GetU8(&offset) != 0) {
+ lldbassert(0 && "not implemented");
+ return;
+ }
+
+ uint32_t offsetsAmount = data.GetU32(&offset);
+ for (uint32_t i = 0; i < offsetsAmount; ++i)
+ Offsets.push_back(data.GetMaxU64(&offset, 4));
+
+ lldb::offset_t listOffset = offset;
+ std::vector<RngListEntry> rangeList;
+ while (offset < end && ExtractRangeList(data, addrSize, &offset, rangeList)) {
+ m_range_map[listOffset] = rangeList;
+ listOffset = offset;
+ }
+}
+
+uint64_t DWARFDebugRngLists::GetOffset(size_t Index) const {
+ return Offsets[Index];
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h
new file mode 100644
index 000000000000..baf2667f0afe
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h
@@ -0,0 +1,76 @@
+//===-- DWARFDebugRanges.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 SymbolFileDWARF_DWARFDebugRanges_h_
+#define SymbolFileDWARF_DWARFDebugRanges_h_
+
+#include "lldb/Core/dwarf.h"
+#include <map>
+
+class DWARFUnit;
+namespace lldb_private {
+class DWARFContext;
+}
+
+class DWARFDebugRangesBase {
+public:
+ virtual ~DWARFDebugRangesBase(){};
+
+ virtual void Extract(lldb_private::DWARFContext &context) = 0;
+ virtual bool FindRanges(const DWARFUnit *cu, dw_offset_t debug_ranges_offset,
+ DWARFRangeList &range_list) const = 0;
+ virtual uint64_t GetOffset(size_t Index) const = 0;
+};
+
+class DWARFDebugRanges final : public DWARFDebugRangesBase {
+public:
+ DWARFDebugRanges();
+
+ void Extract(lldb_private::DWARFContext &context) override;
+ bool FindRanges(const DWARFUnit *cu, dw_offset_t debug_ranges_offset,
+ DWARFRangeList &range_list) const override;
+ uint64_t GetOffset(size_t Index) const override;
+
+ static void Dump(lldb_private::Stream &s,
+ const lldb_private::DWARFDataExtractor &debug_ranges_data,
+ lldb::offset_t *offset_ptr, dw_addr_t cu_base_addr);
+
+protected:
+ bool Extract(lldb_private::DWARFContext &context, lldb::offset_t *offset_ptr,
+ DWARFRangeList &range_list);
+
+ typedef std::map<dw_offset_t, DWARFRangeList> range_map;
+ typedef range_map::iterator range_map_iterator;
+ typedef range_map::const_iterator range_map_const_iterator;
+ range_map m_range_map;
+};
+
+// DWARF v5 .debug_rnglists section.
+class DWARFDebugRngLists final : public DWARFDebugRangesBase {
+ struct RngListEntry {
+ uint8_t encoding;
+ uint64_t value0;
+ uint64_t value1;
+ };
+
+public:
+ void Extract(lldb_private::DWARFContext &context) override;
+ bool FindRanges(const DWARFUnit *cu, dw_offset_t debug_ranges_offset,
+ DWARFRangeList &range_list) const override;
+ uint64_t GetOffset(size_t Index) const override;
+
+protected:
+ bool ExtractRangeList(const lldb_private::DWARFDataExtractor &data,
+ uint8_t addrSize, lldb::offset_t *offset_ptr,
+ std::vector<RngListEntry> &list);
+
+ std::vector<uint64_t> Offsets;
+ std::map<dw_offset_t, std::vector<RngListEntry>> m_range_map;
+};
+
+#endif // SymbolFileDWARF_DWARFDebugRanges_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp
new file mode 100644
index 000000000000..a664314035e4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp
@@ -0,0 +1,87 @@
+//===-- DWARFDeclContext.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 "DWARFDeclContext.h"
+
+const char *DWARFDeclContext::GetQualifiedName() const {
+ if (m_qualified_name.empty()) {
+ // The declaration context array for a class named "foo" in namespace
+ // "a::b::c" will be something like:
+ // [0] DW_TAG_class_type "foo"
+ // [1] DW_TAG_namespace "c"
+ // [2] DW_TAG_namespace "b"
+ // [3] DW_TAG_namespace "a"
+ if (!m_entries.empty()) {
+ if (m_entries.size() == 1) {
+ if (m_entries[0].name) {
+ m_qualified_name.append("::");
+ m_qualified_name.append(m_entries[0].name);
+ }
+ } else {
+ collection::const_reverse_iterator pos;
+ collection::const_reverse_iterator begin = m_entries.rbegin();
+ collection::const_reverse_iterator end = m_entries.rend();
+ for (pos = begin; pos != end; ++pos) {
+ if (pos != begin)
+ m_qualified_name.append("::");
+ if (pos->name == nullptr) {
+ if (pos->tag == DW_TAG_namespace)
+ m_qualified_name.append("(anonymous namespace)");
+ else if (pos->tag == DW_TAG_class_type)
+ m_qualified_name.append("(anonymous class)");
+ else if (pos->tag == DW_TAG_structure_type)
+ m_qualified_name.append("(anonymous struct)");
+ else if (pos->tag == DW_TAG_union_type)
+ m_qualified_name.append("(anonymous union)");
+ else
+ m_qualified_name.append("(anonymous)");
+ } else
+ m_qualified_name.append(pos->name);
+ }
+ }
+ }
+ }
+ if (m_qualified_name.empty())
+ return nullptr;
+ return m_qualified_name.c_str();
+}
+
+bool DWARFDeclContext::operator==(const DWARFDeclContext &rhs) const {
+ if (m_entries.size() != rhs.m_entries.size())
+ return false;
+
+ collection::const_iterator pos;
+ collection::const_iterator begin = m_entries.begin();
+ collection::const_iterator end = m_entries.end();
+
+ collection::const_iterator rhs_pos;
+ collection::const_iterator rhs_begin = rhs.m_entries.begin();
+ // The two entry arrays have the same size
+
+ // First compare the tags before we do expensive name compares
+ for (pos = begin, rhs_pos = rhs_begin; pos != end; ++pos, ++rhs_pos) {
+ if (pos->tag != rhs_pos->tag) {
+ // Check for DW_TAG_structure_type and DW_TAG_class_type as they are
+ // often used interchangeably in GCC
+ if (pos->tag == DW_TAG_structure_type &&
+ rhs_pos->tag == DW_TAG_class_type)
+ continue;
+ if (pos->tag == DW_TAG_class_type &&
+ rhs_pos->tag == DW_TAG_structure_type)
+ continue;
+ return false;
+ }
+ }
+ // The tags all match, now compare the names
+ for (pos = begin, rhs_pos = rhs_begin; pos != end; ++pos, ++rhs_pos) {
+ if (!pos->NameMatches(*rhs_pos))
+ return false;
+ }
+ // All tags and names match
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h
new file mode 100644
index 000000000000..d0d70dd5123e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h
@@ -0,0 +1,88 @@
+//===-- DWARFDeclContext.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 SymbolFileDWARF_DWARFDeclContext_h_
+#define SymbolFileDWARF_DWARFDeclContext_h_
+
+#include <string>
+#include <vector>
+#include "lldb/Utility/ConstString.h"
+#include "DWARFDefines.h"
+
+// DWARFDeclContext
+//
+// A class that represents a declaration context all the way down to a
+// DIE. This is useful when trying to find a DIE in one DWARF to a DIE
+// in another DWARF file.
+
+class DWARFDeclContext {
+public:
+ struct Entry {
+ Entry() : tag(0), name(nullptr) {}
+ Entry(dw_tag_t t, const char *n) : tag(t), name(n) {}
+
+ bool NameMatches(const Entry &rhs) const {
+ if (name == rhs.name)
+ return true;
+ else if (name && rhs.name)
+ return strcmp(name, rhs.name) == 0;
+ return false;
+ }
+
+ // Test operator
+ explicit operator bool() const { return tag != 0; }
+
+ dw_tag_t tag;
+ const char *name;
+ };
+
+ DWARFDeclContext() : m_entries(), m_language(lldb::eLanguageTypeUnknown) {}
+
+ void AppendDeclContext(dw_tag_t tag, const char *name) {
+ m_entries.push_back(Entry(tag, name));
+ }
+
+ bool operator==(const DWARFDeclContext &rhs) const;
+
+ uint32_t GetSize() const { return m_entries.size(); }
+
+ Entry &operator[](uint32_t idx) {
+ // "idx" must be valid
+ return m_entries[idx];
+ }
+
+ const Entry &operator[](uint32_t idx) const {
+ // "idx" must be valid
+ return m_entries[idx];
+ }
+
+ const char *GetQualifiedName() const;
+
+ // Same as GetQaulifiedName, but the life time of the returned string will
+ // be that of the LLDB session.
+ lldb_private::ConstString GetQualifiedNameAsConstString() const {
+ return lldb_private::ConstString(GetQualifiedName());
+ }
+
+ void Clear() {
+ m_entries.clear();
+ m_qualified_name.clear();
+ }
+
+ lldb::LanguageType GetLanguage() const { return m_language; }
+
+ void SetLanguage(lldb::LanguageType language) { m_language = language; }
+
+protected:
+ typedef std::vector<Entry> collection;
+ collection m_entries;
+ mutable std::string m_qualified_name;
+ lldb::LanguageType m_language;
+};
+
+#endif // SymbolFileDWARF_DWARFDeclContext_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp
new file mode 100644
index 000000000000..3bf0bb088227
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp
@@ -0,0 +1,402 @@
+//===-- DWARFDefines.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 "DWARFDefines.h"
+#include "lldb/Utility/ConstString.h"
+#include <cstdio>
+#include <cstring>
+#include <string>
+
+namespace lldb_private {
+
+const char *DW_TAG_value_to_name(uint32_t val) {
+ static char invalid[100];
+
+ if (val == 0)
+ return "NULL";
+
+ llvm::StringRef llvmstr = llvm::dwarf::TagString(val);
+ if (llvmstr.empty()) {
+ snprintf(invalid, sizeof(invalid), "Unknown DW_TAG constant: 0x%x", val);
+ return invalid;
+ }
+ return llvmstr.data();
+}
+
+const char *DW_AT_value_to_name(uint32_t val) {
+ static char invalid[100];
+ llvm::StringRef llvmstr = llvm::dwarf::AttributeString(val);
+ if (llvmstr.empty()) {
+ snprintf(invalid, sizeof(invalid), "Unknown DW_AT constant: 0x%x", val);
+ return invalid;
+ }
+ return llvmstr.data();
+}
+
+const char *DW_FORM_value_to_name(uint32_t val) {
+ static char invalid[100];
+ llvm::StringRef llvmstr = llvm::dwarf::FormEncodingString(val);
+ if (llvmstr.empty()) {
+ snprintf(invalid, sizeof(invalid), "Unknown DW_FORM constant: 0x%x", val);
+ return invalid;
+ }
+ return llvmstr.data();
+}
+
+const char *DW_OP_value_to_name(uint32_t val) {
+ static char invalid[100];
+ llvm::StringRef llvmstr = llvm::dwarf::OperationEncodingString(val);
+ if (llvmstr.empty()) {
+ snprintf(invalid, sizeof(invalid), "Unknown DW_OP constant: 0x%x", val);
+ return invalid;
+ }
+ return llvmstr.data();
+}
+
+DRC_class DW_OP_value_to_class(uint32_t val) {
+ switch (val) {
+ case 0x03:
+ return DRC_ONEOPERAND;
+ case 0x06:
+ return DRC_ZEROOPERANDS;
+ case 0x08:
+ return DRC_ONEOPERAND;
+ case 0x09:
+ return DRC_ONEOPERAND;
+ case 0x0a:
+ return DRC_ONEOPERAND;
+ case 0x0b:
+ return DRC_ONEOPERAND;
+ case 0x0c:
+ return DRC_ONEOPERAND;
+ case 0x0d:
+ return DRC_ONEOPERAND;
+ case 0x0e:
+ return DRC_ONEOPERAND;
+ case 0x0f:
+ return DRC_ONEOPERAND;
+ case 0x10:
+ return DRC_ONEOPERAND;
+ case 0x11:
+ return DRC_ONEOPERAND;
+ case 0x12:
+ return DRC_ZEROOPERANDS;
+ case 0x13:
+ return DRC_ZEROOPERANDS;
+ case 0x14:
+ return DRC_ZEROOPERANDS;
+ case 0x15:
+ return DRC_ONEOPERAND;
+ case 0x16:
+ return DRC_ZEROOPERANDS;
+ case 0x17:
+ return DRC_ZEROOPERANDS;
+ case 0x18:
+ return DRC_ZEROOPERANDS;
+ case 0x19:
+ return DRC_ZEROOPERANDS;
+ case 0x1a:
+ return DRC_ZEROOPERANDS;
+ case 0x1b:
+ return DRC_ZEROOPERANDS;
+ case 0x1c:
+ return DRC_ZEROOPERANDS;
+ case 0x1d:
+ return DRC_ZEROOPERANDS;
+ case 0x1e:
+ return DRC_ZEROOPERANDS;
+ case 0x1f:
+ return DRC_ZEROOPERANDS;
+ case 0x20:
+ return DRC_ZEROOPERANDS;
+ case 0x21:
+ return DRC_ZEROOPERANDS;
+ case 0x22:
+ return DRC_ZEROOPERANDS;
+ case 0x23:
+ return DRC_ONEOPERAND;
+ case 0x24:
+ return DRC_ZEROOPERANDS;
+ case 0x25:
+ return DRC_ZEROOPERANDS;
+ case 0x26:
+ return DRC_ZEROOPERANDS;
+ case 0x27:
+ return DRC_ZEROOPERANDS;
+ case 0x2f:
+ return DRC_ONEOPERAND;
+ case 0x28:
+ return DRC_ONEOPERAND;
+ case 0x29:
+ return DRC_ZEROOPERANDS;
+ case 0x2a:
+ return DRC_ZEROOPERANDS;
+ case 0x2b:
+ return DRC_ZEROOPERANDS;
+ case 0x2c:
+ return DRC_ZEROOPERANDS;
+ case 0x2d:
+ return DRC_ZEROOPERANDS;
+ case 0x2e:
+ return DRC_ZEROOPERANDS;
+ case 0x30:
+ return DRC_ZEROOPERANDS;
+ case 0x31:
+ return DRC_ZEROOPERANDS;
+ case 0x32:
+ return DRC_ZEROOPERANDS;
+ case 0x33:
+ return DRC_ZEROOPERANDS;
+ case 0x34:
+ return DRC_ZEROOPERANDS;
+ case 0x35:
+ return DRC_ZEROOPERANDS;
+ case 0x36:
+ return DRC_ZEROOPERANDS;
+ case 0x37:
+ return DRC_ZEROOPERANDS;
+ case 0x38:
+ return DRC_ZEROOPERANDS;
+ case 0x39:
+ return DRC_ZEROOPERANDS;
+ case 0x3a:
+ return DRC_ZEROOPERANDS;
+ case 0x3b:
+ return DRC_ZEROOPERANDS;
+ case 0x3c:
+ return DRC_ZEROOPERANDS;
+ case 0x3d:
+ return DRC_ZEROOPERANDS;
+ case 0x3e:
+ return DRC_ZEROOPERANDS;
+ case 0x3f:
+ return DRC_ZEROOPERANDS;
+ case 0x40:
+ return DRC_ZEROOPERANDS;
+ case 0x41:
+ return DRC_ZEROOPERANDS;
+ case 0x42:
+ return DRC_ZEROOPERANDS;
+ case 0x43:
+ return DRC_ZEROOPERANDS;
+ case 0x44:
+ return DRC_ZEROOPERANDS;
+ case 0x45:
+ return DRC_ZEROOPERANDS;
+ case 0x46:
+ return DRC_ZEROOPERANDS;
+ case 0x47:
+ return DRC_ZEROOPERANDS;
+ case 0x48:
+ return DRC_ZEROOPERANDS;
+ case 0x49:
+ return DRC_ZEROOPERANDS;
+ case 0x4a:
+ return DRC_ZEROOPERANDS;
+ case 0x4b:
+ return DRC_ZEROOPERANDS;
+ case 0x4c:
+ return DRC_ZEROOPERANDS;
+ case 0x4d:
+ return DRC_ZEROOPERANDS;
+ case 0x4e:
+ return DRC_ZEROOPERANDS;
+ case 0x4f:
+ return DRC_ZEROOPERANDS;
+ case 0x50:
+ return DRC_ZEROOPERANDS;
+ case 0x51:
+ return DRC_ZEROOPERANDS;
+ case 0x52:
+ return DRC_ZEROOPERANDS;
+ case 0x53:
+ return DRC_ZEROOPERANDS;
+ case 0x54:
+ return DRC_ZEROOPERANDS;
+ case 0x55:
+ return DRC_ZEROOPERANDS;
+ case 0x56:
+ return DRC_ZEROOPERANDS;
+ case 0x57:
+ return DRC_ZEROOPERANDS;
+ case 0x58:
+ return DRC_ZEROOPERANDS;
+ case 0x59:
+ return DRC_ZEROOPERANDS;
+ case 0x5a:
+ return DRC_ZEROOPERANDS;
+ case 0x5b:
+ return DRC_ZEROOPERANDS;
+ case 0x5c:
+ return DRC_ZEROOPERANDS;
+ case 0x5d:
+ return DRC_ZEROOPERANDS;
+ case 0x5e:
+ return DRC_ZEROOPERANDS;
+ case 0x5f:
+ return DRC_ZEROOPERANDS;
+ case 0x60:
+ return DRC_ZEROOPERANDS;
+ case 0x61:
+ return DRC_ZEROOPERANDS;
+ case 0x62:
+ return DRC_ZEROOPERANDS;
+ case 0x63:
+ return DRC_ZEROOPERANDS;
+ case 0x64:
+ return DRC_ZEROOPERANDS;
+ case 0x65:
+ return DRC_ZEROOPERANDS;
+ case 0x66:
+ return DRC_ZEROOPERANDS;
+ case 0x67:
+ return DRC_ZEROOPERANDS;
+ case 0x68:
+ return DRC_ZEROOPERANDS;
+ case 0x69:
+ return DRC_ZEROOPERANDS;
+ case 0x6a:
+ return DRC_ZEROOPERANDS;
+ case 0x6b:
+ return DRC_ZEROOPERANDS;
+ case 0x6c:
+ return DRC_ZEROOPERANDS;
+ case 0x6d:
+ return DRC_ZEROOPERANDS;
+ case 0x6e:
+ return DRC_ZEROOPERANDS;
+ case 0x6f:
+ return DRC_ZEROOPERANDS;
+ case 0x70:
+ return DRC_ONEOPERAND;
+ case 0x71:
+ return DRC_ONEOPERAND;
+ case 0x72:
+ return DRC_ONEOPERAND;
+ case 0x73:
+ return DRC_ONEOPERAND;
+ case 0x74:
+ return DRC_ONEOPERAND;
+ case 0x75:
+ return DRC_ONEOPERAND;
+ case 0x76:
+ return DRC_ONEOPERAND;
+ case 0x77:
+ return DRC_ONEOPERAND;
+ case 0x78:
+ return DRC_ONEOPERAND;
+ case 0x79:
+ return DRC_ONEOPERAND;
+ case 0x7a:
+ return DRC_ONEOPERAND;
+ case 0x7b:
+ return DRC_ONEOPERAND;
+ case 0x7c:
+ return DRC_ONEOPERAND;
+ case 0x7d:
+ return DRC_ONEOPERAND;
+ case 0x7e:
+ return DRC_ONEOPERAND;
+ case 0x7f:
+ return DRC_ONEOPERAND;
+ case 0x80:
+ return DRC_ONEOPERAND;
+ case 0x81:
+ return DRC_ONEOPERAND;
+ case 0x82:
+ return DRC_ONEOPERAND;
+ case 0x83:
+ return DRC_ONEOPERAND;
+ case 0x84:
+ return DRC_ONEOPERAND;
+ case 0x85:
+ return DRC_ONEOPERAND;
+ case 0x86:
+ return DRC_ONEOPERAND;
+ case 0x87:
+ return DRC_ONEOPERAND;
+ case 0x88:
+ return DRC_ONEOPERAND;
+ case 0x89:
+ return DRC_ONEOPERAND;
+ case 0x8a:
+ return DRC_ONEOPERAND;
+ case 0x8b:
+ return DRC_ONEOPERAND;
+ case 0x8c:
+ return DRC_ONEOPERAND;
+ case 0x8d:
+ return DRC_ONEOPERAND;
+ case 0x8e:
+ return DRC_ONEOPERAND;
+ case 0x8f:
+ return DRC_ONEOPERAND;
+ case 0x90:
+ return DRC_ONEOPERAND;
+ case 0x91:
+ return DRC_ONEOPERAND;
+ case 0x92:
+ return DRC_TWOOPERANDS;
+ case 0x93:
+ return DRC_ONEOPERAND;
+ case 0x94:
+ return DRC_ONEOPERAND;
+ case 0x95:
+ return DRC_ONEOPERAND;
+ case 0x96:
+ return DRC_ZEROOPERANDS;
+ case 0x97:
+ return DRC_DWARFv3 | DRC_ZEROOPERANDS;
+ case 0x98:
+ return DRC_DWARFv3 | DRC_ONEOPERAND;
+ case 0x99:
+ return DRC_DWARFv3 | DRC_ONEOPERAND;
+ case 0x9a:
+ return DRC_DWARFv3 | DRC_ONEOPERAND;
+ case 0xf0:
+ return DRC_ZEROOPERANDS; /* DW_OP_APPLE_uninit */
+ case 0xe0:
+ return 0;
+ case 0xff:
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+const char *DW_ATE_value_to_name(uint32_t val) {
+ static char invalid[100];
+ llvm::StringRef llvmstr = llvm::dwarf::AttributeEncodingString(val);
+ if (llvmstr.empty()) {
+ snprintf(invalid, sizeof(invalid), "Unknown DW_ATE constant: 0x%x", val);
+ return invalid;
+ }
+ return llvmstr.data();
+}
+
+const char *DW_LANG_value_to_name(uint32_t val) {
+ static char invalid[100];
+ llvm::StringRef llvmstr = llvm::dwarf::LanguageString(val);
+ if (llvmstr.empty()) {
+ snprintf(invalid, sizeof(invalid), "Unknown DW_LANG constant: 0x%x", val);
+ return invalid;
+ }
+ return llvmstr.data();
+}
+
+const char *DW_LNS_value_to_name(uint32_t val) {
+ static char invalid[100];
+ llvm::StringRef llvmstr = llvm::dwarf::LNStandardString(val);
+ if (llvmstr.empty()) {
+ snprintf(invalid, sizeof(invalid), "Unknown DW_LNS constant: 0x%x", val);
+ return invalid;
+ }
+ return llvmstr.data();
+}
+
+} // namespace lldb_private
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.h
new file mode 100644
index 000000000000..d16cb07baf88
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.h
@@ -0,0 +1,77 @@
+//===-- DWARFDefines.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 SymbolFileDWARF_DWARFDefines_h_
+#define SymbolFileDWARF_DWARFDefines_h_
+
+#include "lldb/Core/dwarf.h"
+#include <stdint.h>
+
+namespace lldb_private {
+
+enum class DWARFEnumState { MoreItems, Complete };
+
+typedef uint32_t DRC_class; // Holds DRC_* class bitfields
+
+const char *DW_TAG_value_to_name(uint32_t val);
+
+const char *DW_AT_value_to_name(uint32_t val);
+
+const char *DW_FORM_value_to_name(uint32_t val);
+
+const char *DW_OP_value_to_name(uint32_t val);
+
+DRC_class DW_OP_value_to_class(uint32_t val);
+
+const char *DW_ATE_value_to_name(uint32_t val);
+
+const char *DW_LANG_value_to_name(uint32_t val);
+
+const char *DW_LNS_value_to_name(uint32_t val);
+
+/* These DRC are entirely our own construction,
+ although they are derived from various comments in the DWARF standard.
+ Most of these are not useful to the parser, but the DW_AT and DW_FORM
+ classes should prove to be usable in some fashion. */
+
+#define DRC_0x65 0x1
+#define DRC_ADDRESS 0x2
+#define DRC_BLOCK 0x4
+#define DRC_CONSTANT 0x8
+#define DRC_DWARFv3 0x10
+#define DRC_FLAG 0x20
+#define DRC_INDIRECT_SPECIAL 0x40
+#define DRC_LINEPTR 0x80
+#define DRC_LOCEXPR 0x100
+#define DRC_LOCLISTPTR 0x200
+#define DRC_MACPTR 0x400
+#define DRC_ONEOPERAND 0x800
+#define DRC_OPERANDONE_1BYTE_DELTA 0x1000
+#define DRC_OPERANDONE_2BYTE_DELTA 0x2000
+#define DRC_OPERANDONE_4BYTE_DELTA 0x4000
+#define DRC_OPERANDONE_ADDRESS 0x8000
+#define DRC_OPERANDONE_BLOCK 0x10000
+#define DRC_OPERANDONE_SLEB128_OFFSET 0x20000
+#define DRC_OPERANDONE_ULEB128_OFFSET 0x40000
+#define DRC_OPERANDONE_ULEB128_REGISTER 0x80000
+#define DRC_OPERANDTWO_BLOCK 0x100000
+#define DRC_OPERANDTWO_SLEB128_OFFSET 0x200000
+#define DRC_OPERANDTWO_ULEB128_OFFSET 0x400000
+#define DRC_OPERANDTWO_ULEB128_REGISTER 0x800000
+#define DRC_OPERNADONE_ULEB128_REGISTER 0x1000000
+#define DRC_RANGELISTPTR 0x2000000
+#define DRC_REFERENCE 0x4000000
+#define DRC_STRING 0x8000000
+#define DRC_TWOOPERANDS 0x10000000
+#define DRC_VENDOR_GNU 0x20000000
+#define DRC_VENDOR_MIPS 0x40000000
+#define DRC_ZEROOPERANDS 0x80000000
+
+} // namespace lldb_private
+
+#endif // SymbolFileDWARF_DWARFDefines_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp
new file mode 100644
index 000000000000..046ae67446af
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp
@@ -0,0 +1,739 @@
+//===-- DWARFFormValue.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 "lldb/Core/Module.h"
+#include "lldb/Core/dwarf.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/Stream.h"
+
+#include "DWARFDebugInfo.h"
+#include "DWARFFormValue.h"
+#include "DWARFUnit.h"
+
+class DWARFUnit;
+
+using namespace lldb_private;
+
+void DWARFFormValue::Clear() {
+ m_unit = nullptr;
+ m_form = 0;
+ m_value = ValueTypeTag();
+}
+
+bool DWARFFormValue::ExtractValue(const DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr) {
+ if (m_form == DW_FORM_implicit_const)
+ return true;
+
+ bool indirect = false;
+ bool is_block = false;
+ m_value.data = nullptr;
+ uint8_t ref_addr_size;
+ // Read the value for the form into value and follow and DW_FORM_indirect
+ // instances we run into
+ do {
+ indirect = false;
+ switch (m_form) {
+ case DW_FORM_addr:
+ assert(m_unit);
+ m_value.value.uval =
+ data.GetMaxU64(offset_ptr, DWARFUnit::GetAddressByteSize(m_unit));
+ break;
+ case DW_FORM_block1:
+ m_value.value.uval = data.GetU8(offset_ptr);
+ is_block = true;
+ break;
+ case DW_FORM_block2:
+ m_value.value.uval = data.GetU16(offset_ptr);
+ is_block = true;
+ break;
+ case DW_FORM_block4:
+ m_value.value.uval = data.GetU32(offset_ptr);
+ is_block = true;
+ break;
+ case DW_FORM_data16:
+ m_value.value.uval = 16;
+ is_block = true;
+ break;
+ case DW_FORM_exprloc:
+ case DW_FORM_block:
+ m_value.value.uval = data.GetULEB128(offset_ptr);
+ is_block = true;
+ break;
+ case DW_FORM_string:
+ m_value.value.cstr = data.GetCStr(offset_ptr);
+ break;
+ case DW_FORM_sdata:
+ m_value.value.sval = data.GetSLEB128(offset_ptr);
+ break;
+ case DW_FORM_strp:
+ case DW_FORM_line_strp:
+ case DW_FORM_sec_offset:
+ m_value.value.uval = data.GetMaxU64(offset_ptr, 4);
+ break;
+ case DW_FORM_addrx1:
+ case DW_FORM_strx1:
+ case DW_FORM_ref1:
+ case DW_FORM_data1:
+ case DW_FORM_flag:
+ m_value.value.uval = data.GetU8(offset_ptr);
+ break;
+ case DW_FORM_addrx2:
+ case DW_FORM_strx2:
+ case DW_FORM_ref2:
+ case DW_FORM_data2:
+ m_value.value.uval = data.GetU16(offset_ptr);
+ break;
+ case DW_FORM_addrx3:
+ case DW_FORM_strx3:
+ m_value.value.uval = data.GetMaxU64(offset_ptr, 3);
+ break;
+ case DW_FORM_addrx4:
+ case DW_FORM_strx4:
+ case DW_FORM_ref4:
+ case DW_FORM_data4:
+ m_value.value.uval = data.GetU32(offset_ptr);
+ break;
+ case DW_FORM_data8:
+ case DW_FORM_ref8:
+ case DW_FORM_ref_sig8:
+ m_value.value.uval = data.GetU64(offset_ptr);
+ break;
+ case DW_FORM_addrx:
+ case DW_FORM_rnglistx:
+ case DW_FORM_strx:
+ case DW_FORM_udata:
+ case DW_FORM_ref_udata:
+ case DW_FORM_GNU_str_index:
+ case DW_FORM_GNU_addr_index:
+ m_value.value.uval = data.GetULEB128(offset_ptr);
+ break;
+ case DW_FORM_ref_addr:
+ assert(m_unit);
+ if (m_unit->GetVersion() <= 2)
+ ref_addr_size = m_unit->GetAddressByteSize();
+ else
+ ref_addr_size = 4;
+ m_value.value.uval = data.GetMaxU64(offset_ptr, ref_addr_size);
+ break;
+ case DW_FORM_indirect:
+ m_form = data.GetULEB128(offset_ptr);
+ indirect = true;
+ break;
+ case DW_FORM_flag_present:
+ m_value.value.uval = 1;
+ break;
+ default:
+ return false;
+ }
+ } while (indirect);
+
+ if (is_block) {
+ m_value.data = data.PeekData(*offset_ptr, m_value.value.uval);
+ if (m_value.data != nullptr) {
+ *offset_ptr += m_value.value.uval;
+ }
+ }
+
+ return true;
+}
+
+struct FormSize {
+ uint8_t valid:1, size:7;
+};
+static FormSize g_form_sizes[] = {
+ {0,0}, // 0x00 unused
+ {0,0}, // 0x01 DW_FORM_addr
+ {0,0}, // 0x02 unused
+ {0,0}, // 0x03 DW_FORM_block2
+ {0,0}, // 0x04 DW_FORM_block4
+ {1,2}, // 0x05 DW_FORM_data2
+ {1,4}, // 0x06 DW_FORM_data4
+ {1,8}, // 0x07 DW_FORM_data8
+ {0,0}, // 0x08 DW_FORM_string
+ {0,0}, // 0x09 DW_FORM_block
+ {0,0}, // 0x0a DW_FORM_block1
+ {1,1}, // 0x0b DW_FORM_data1
+ {1,1}, // 0x0c DW_FORM_flag
+ {0,0}, // 0x0d DW_FORM_sdata
+ {1,4}, // 0x0e DW_FORM_strp
+ {0,0}, // 0x0f DW_FORM_udata
+ {0,0}, // 0x10 DW_FORM_ref_addr (addr size for DWARF2 and earlier, 4 bytes for
+ // DWARF32, 8 bytes for DWARF32 in DWARF 3 and later
+ {1,1}, // 0x11 DW_FORM_ref1
+ {1,2}, // 0x12 DW_FORM_ref2
+ {1,4}, // 0x13 DW_FORM_ref4
+ {1,8}, // 0x14 DW_FORM_ref8
+ {0,0}, // 0x15 DW_FORM_ref_udata
+ {0,0}, // 0x16 DW_FORM_indirect
+ {1,4}, // 0x17 DW_FORM_sec_offset
+ {0,0}, // 0x18 DW_FORM_exprloc
+ {1,0}, // 0x19 DW_FORM_flag_present
+ {0,0}, // 0x1a
+ {0,0}, // 0x1b
+ {0,0}, // 0x1c
+ {0,0}, // 0x1d
+ {0,0}, // 0x1e
+ {0,0}, // 0x1f
+ {1,8}, // 0x20 DW_FORM_ref_sig8
+};
+
+llvm::Optional<uint8_t>
+DWARFFormValue::GetFixedSize(dw_form_t form, const DWARFUnit *u) {
+ if (form <= DW_FORM_ref_sig8 && g_form_sizes[form].valid)
+ return g_form_sizes[form].size;
+ if (form == DW_FORM_addr && u)
+ return u->GetAddressByteSize();
+ return llvm::None;
+}
+
+llvm::Optional<uint8_t> DWARFFormValue::GetFixedSize() const {
+ return GetFixedSize(m_form, m_unit);
+}
+
+bool DWARFFormValue::SkipValue(const DWARFDataExtractor &debug_info_data,
+ lldb::offset_t *offset_ptr) const {
+ return DWARFFormValue::SkipValue(m_form, debug_info_data, offset_ptr, m_unit);
+}
+
+bool DWARFFormValue::SkipValue(dw_form_t form,
+ const DWARFDataExtractor &debug_info_data,
+ lldb::offset_t *offset_ptr,
+ const DWARFUnit *unit) {
+ uint8_t ref_addr_size;
+ switch (form) {
+ // Blocks if inlined data that have a length field and the data bytes inlined
+ // in the .debug_info
+ case DW_FORM_exprloc:
+ case DW_FORM_block: {
+ dw_uleb128_t size = debug_info_data.GetULEB128(offset_ptr);
+ *offset_ptr += size;
+ }
+ return true;
+ case DW_FORM_block1: {
+ dw_uleb128_t size = debug_info_data.GetU8(offset_ptr);
+ *offset_ptr += size;
+ }
+ return true;
+ case DW_FORM_block2: {
+ dw_uleb128_t size = debug_info_data.GetU16(offset_ptr);
+ *offset_ptr += size;
+ }
+ return true;
+ case DW_FORM_block4: {
+ dw_uleb128_t size = debug_info_data.GetU32(offset_ptr);
+ *offset_ptr += size;
+ }
+ return true;
+
+ // Inlined NULL terminated C-strings
+ case DW_FORM_string:
+ debug_info_data.GetCStr(offset_ptr);
+ return true;
+
+ // Compile unit address sized values
+ case DW_FORM_addr:
+ *offset_ptr += DWARFUnit::GetAddressByteSize(unit);
+ return true;
+
+ case DW_FORM_ref_addr:
+ ref_addr_size = 4;
+ assert(unit); // Unit must be valid for DW_FORM_ref_addr objects or we will
+ // get this wrong
+ if (unit->GetVersion() <= 2)
+ ref_addr_size = unit->GetAddressByteSize();
+ else
+ ref_addr_size = 4;
+ *offset_ptr += ref_addr_size;
+ return true;
+
+ // 0 bytes values (implied from DW_FORM)
+ case DW_FORM_flag_present:
+ case DW_FORM_implicit_const:
+ return true;
+
+ // 1 byte values
+ case DW_FORM_addrx1:
+ case DW_FORM_data1:
+ case DW_FORM_flag:
+ case DW_FORM_ref1:
+ case DW_FORM_strx1:
+ *offset_ptr += 1;
+ return true;
+
+ // 2 byte values
+ case DW_FORM_addrx2:
+ case DW_FORM_data2:
+ case DW_FORM_ref2:
+ case DW_FORM_strx2:
+ *offset_ptr += 2;
+ return true;
+
+ // 3 byte values
+ case DW_FORM_addrx3:
+ case DW_FORM_strx3:
+ *offset_ptr += 3;
+ return true;
+
+ // 32 bit for DWARF 32, 64 for DWARF 64
+ case DW_FORM_sec_offset:
+ case DW_FORM_strp:
+ *offset_ptr += 4;
+ return true;
+
+ // 4 byte values
+ case DW_FORM_addrx4:
+ case DW_FORM_data4:
+ case DW_FORM_ref4:
+ case DW_FORM_strx4:
+ *offset_ptr += 4;
+ return true;
+
+ // 8 byte values
+ case DW_FORM_data8:
+ case DW_FORM_ref8:
+ case DW_FORM_ref_sig8:
+ *offset_ptr += 8;
+ return true;
+
+ // signed or unsigned LEB 128 values
+ case DW_FORM_addrx:
+ case DW_FORM_rnglistx:
+ case DW_FORM_sdata:
+ case DW_FORM_udata:
+ case DW_FORM_ref_udata:
+ case DW_FORM_GNU_addr_index:
+ case DW_FORM_GNU_str_index:
+ case DW_FORM_strx:
+ debug_info_data.Skip_LEB128(offset_ptr);
+ return true;
+
+ case DW_FORM_indirect: {
+ dw_form_t indirect_form = debug_info_data.GetULEB128(offset_ptr);
+ return DWARFFormValue::SkipValue(indirect_form, debug_info_data, offset_ptr,
+ unit);
+ }
+
+ default:
+ break;
+ }
+ return false;
+}
+
+void DWARFFormValue::Dump(Stream &s) const {
+ uint64_t uvalue = Unsigned();
+ bool unit_relative_offset = false;
+
+ switch (m_form) {
+ case DW_FORM_addr:
+ s.Address(uvalue, sizeof(uint64_t));
+ break;
+ case DW_FORM_flag:
+ case DW_FORM_data1:
+ s.PutHex8(uvalue);
+ break;
+ case DW_FORM_data2:
+ s.PutHex16(uvalue);
+ break;
+ case DW_FORM_sec_offset:
+ case DW_FORM_data4:
+ s.PutHex32(uvalue);
+ break;
+ case DW_FORM_ref_sig8:
+ case DW_FORM_data8:
+ s.PutHex64(uvalue);
+ break;
+ case DW_FORM_string:
+ s.QuotedCString(AsCString());
+ break;
+ case DW_FORM_exprloc:
+ case DW_FORM_block:
+ case DW_FORM_block1:
+ case DW_FORM_block2:
+ case DW_FORM_block4:
+ if (uvalue > 0) {
+ switch (m_form) {
+ case DW_FORM_exprloc:
+ case DW_FORM_block:
+ s.Printf("<0x%" PRIx64 "> ", uvalue);
+ break;
+ case DW_FORM_block1:
+ s.Printf("<0x%2.2x> ", (uint8_t)uvalue);
+ break;
+ case DW_FORM_block2:
+ s.Printf("<0x%4.4x> ", (uint16_t)uvalue);
+ break;
+ case DW_FORM_block4:
+ s.Printf("<0x%8.8x> ", (uint32_t)uvalue);
+ break;
+ default:
+ break;
+ }
+
+ const uint8_t *data_ptr = m_value.data;
+ if (data_ptr) {
+ const uint8_t *end_data_ptr =
+ data_ptr + uvalue; // uvalue contains size of block
+ while (data_ptr < end_data_ptr) {
+ s.Printf("%2.2x ", *data_ptr);
+ ++data_ptr;
+ }
+ } else
+ s.PutCString("NULL");
+ }
+ break;
+
+ case DW_FORM_sdata:
+ s.PutSLEB128(uvalue);
+ break;
+ case DW_FORM_udata:
+ s.PutULEB128(uvalue);
+ break;
+ case DW_FORM_strp: {
+ const char *dbg_str = AsCString();
+ if (dbg_str) {
+ s.QuotedCString(dbg_str);
+ } else {
+ s.PutHex32(uvalue);
+ }
+ } break;
+
+ case DW_FORM_ref_addr: {
+ assert(m_unit); // Unit must be valid for DW_FORM_ref_addr objects or we
+ // will get this wrong
+ if (m_unit->GetVersion() <= 2)
+ s.Address(uvalue, sizeof(uint64_t) * 2);
+ else
+ s.Address(uvalue, 4 * 2); // 4 for DWARF32, 8 for DWARF64, but we don't
+ // support DWARF64 yet
+ break;
+ }
+ case DW_FORM_ref1:
+ unit_relative_offset = true;
+ break;
+ case DW_FORM_ref2:
+ unit_relative_offset = true;
+ break;
+ case DW_FORM_ref4:
+ unit_relative_offset = true;
+ break;
+ case DW_FORM_ref8:
+ unit_relative_offset = true;
+ break;
+ case DW_FORM_ref_udata:
+ unit_relative_offset = true;
+ break;
+
+ // All DW_FORM_indirect attributes should be resolved prior to calling this
+ // function
+ case DW_FORM_indirect:
+ s.PutCString("DW_FORM_indirect");
+ break;
+ case DW_FORM_flag_present:
+ break;
+ default:
+ s.Printf("DW_FORM(0x%4.4x)", m_form);
+ break;
+ }
+
+ if (unit_relative_offset) {
+ assert(m_unit); // Unit must be valid for DW_FORM_ref forms that are compile
+ // unit relative or we will get this wrong
+ s.Printf("{0x%8.8" PRIx64 "}", uvalue + m_unit->GetOffset());
+ }
+}
+
+const char *DWARFFormValue::AsCString() const {
+ SymbolFileDWARF &symbol_file = m_unit->GetSymbolFileDWARF();
+
+ if (m_form == DW_FORM_string) {
+ return m_value.value.cstr;
+ } else if (m_form == DW_FORM_strp) {
+ return symbol_file.GetDWARFContext().getOrLoadStrData().PeekCStr(
+ m_value.value.uval);
+ } else if (m_form == DW_FORM_GNU_str_index) {
+ uint32_t index_size = 4;
+ lldb::offset_t offset = m_value.value.uval * index_size;
+ dw_offset_t str_offset =
+ symbol_file.GetDWARFContext().getOrLoadStrOffsetsData().GetMaxU64(
+ &offset, index_size);
+ return symbol_file.GetDWARFContext().getOrLoadStrData().PeekCStr(
+ str_offset);
+ }
+
+ if (m_form == DW_FORM_strx || m_form == DW_FORM_strx1 ||
+ m_form == DW_FORM_strx2 || m_form == DW_FORM_strx3 ||
+ m_form == DW_FORM_strx4) {
+
+ // The same code as above.
+ uint32_t indexSize = 4;
+ lldb::offset_t offset =
+ m_unit->GetStrOffsetsBase() + m_value.value.uval * indexSize;
+ dw_offset_t strOffset =
+ symbol_file.GetDWARFContext().getOrLoadStrOffsetsData().GetMaxU64(
+ &offset, indexSize);
+ return symbol_file.GetDWARFContext().getOrLoadStrData().PeekCStr(strOffset);
+ }
+
+ if (m_form == DW_FORM_line_strp)
+ return symbol_file.GetDWARFContext().getOrLoadLineStrData().PeekCStr(
+ m_value.value.uval);
+
+ return nullptr;
+}
+
+dw_addr_t DWARFFormValue::Address() const {
+ SymbolFileDWARF &symbol_file = m_unit->GetSymbolFileDWARF();
+
+ if (m_form == DW_FORM_addr)
+ return Unsigned();
+
+ assert(m_unit);
+ assert(m_form == DW_FORM_GNU_addr_index || m_form == DW_FORM_addrx ||
+ m_form == DW_FORM_addrx1 || m_form == DW_FORM_addrx2 ||
+ m_form == DW_FORM_addrx3 || m_form == DW_FORM_addrx4);
+
+ uint32_t index_size = m_unit->GetAddressByteSize();
+ dw_offset_t addr_base = m_unit->GetAddrBase();
+ lldb::offset_t offset = addr_base + m_value.value.uval * index_size;
+ return symbol_file.GetDWARFContext().getOrLoadAddrData().GetMaxU64(
+ &offset, index_size);
+}
+
+DWARFDIE DWARFFormValue::Reference() const {
+ uint64_t value = m_value.value.uval;
+ switch (m_form) {
+ case DW_FORM_ref1:
+ case DW_FORM_ref2:
+ case DW_FORM_ref4:
+ case DW_FORM_ref8:
+ case DW_FORM_ref_udata:
+ assert(m_unit); // Unit must be valid for DW_FORM_ref forms that are compile
+ // unit relative or we will get this wrong
+ value += m_unit->GetOffset();
+ if (!m_unit->ContainsDIEOffset(value)) {
+ m_unit->GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError(
+ "DW_FORM_ref* DIE reference 0x%" PRIx64 " is outside of its CU",
+ value);
+ return {};
+ }
+ return const_cast<DWARFUnit *>(m_unit)->GetDIE(value);
+
+ case DW_FORM_ref_addr: {
+ DWARFUnit *ref_cu =
+ m_unit->GetSymbolFileDWARF().DebugInfo()->GetUnitContainingDIEOffset(
+ DIERef::Section::DebugInfo, value);
+ if (!ref_cu) {
+ m_unit->GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError(
+ "DW_FORM_ref_addr DIE reference 0x%" PRIx64 " has no matching CU",
+ value);
+ return {};
+ }
+ return ref_cu->GetDIE(value);
+ }
+
+ case DW_FORM_ref_sig8: {
+ DWARFTypeUnit *tu =
+ m_unit->GetSymbolFileDWARF().DebugInfo()->GetTypeUnitForHash(value);
+ if (!tu)
+ return {};
+ return tu->GetDIE(tu->GetTypeOffset());
+ }
+
+ default:
+ return {};
+ }
+}
+
+uint64_t DWARFFormValue::Reference(dw_offset_t base_offset) const {
+ uint64_t value = m_value.value.uval;
+ switch (m_form) {
+ case DW_FORM_ref1:
+ case DW_FORM_ref2:
+ case DW_FORM_ref4:
+ case DW_FORM_ref8:
+ case DW_FORM_ref_udata:
+ return value + base_offset;
+
+ case DW_FORM_ref_addr:
+ case DW_FORM_ref_sig8:
+ case DW_FORM_GNU_ref_alt:
+ return value;
+
+ default:
+ return DW_INVALID_OFFSET;
+ }
+}
+
+const uint8_t *DWARFFormValue::BlockData() const { return m_value.data; }
+
+bool DWARFFormValue::IsBlockForm(const dw_form_t form) {
+ switch (form) {
+ case DW_FORM_exprloc:
+ case DW_FORM_block:
+ case DW_FORM_block1:
+ case DW_FORM_block2:
+ case DW_FORM_block4:
+ return true;
+ }
+ return false;
+}
+
+bool DWARFFormValue::IsDataForm(const dw_form_t form) {
+ switch (form) {
+ case DW_FORM_sdata:
+ case DW_FORM_udata:
+ case DW_FORM_data1:
+ case DW_FORM_data2:
+ case DW_FORM_data4:
+ case DW_FORM_data8:
+ return true;
+ }
+ return false;
+}
+
+int DWARFFormValue::Compare(const DWARFFormValue &a_value,
+ const DWARFFormValue &b_value) {
+ dw_form_t a_form = a_value.Form();
+ dw_form_t b_form = b_value.Form();
+ if (a_form < b_form)
+ return -1;
+ if (a_form > b_form)
+ return 1;
+ switch (a_form) {
+ case DW_FORM_addr:
+ case DW_FORM_addrx:
+ case DW_FORM_flag:
+ case DW_FORM_data1:
+ case DW_FORM_data2:
+ case DW_FORM_data4:
+ case DW_FORM_data8:
+ case DW_FORM_udata:
+ case DW_FORM_ref_addr:
+ case DW_FORM_sec_offset:
+ case DW_FORM_flag_present:
+ case DW_FORM_ref_sig8:
+ case DW_FORM_GNU_addr_index: {
+ uint64_t a = a_value.Unsigned();
+ uint64_t b = b_value.Unsigned();
+ if (a < b)
+ return -1;
+ if (a > b)
+ return 1;
+ return 0;
+ }
+
+ case DW_FORM_sdata: {
+ int64_t a = a_value.Signed();
+ int64_t b = b_value.Signed();
+ if (a < b)
+ return -1;
+ if (a > b)
+ return 1;
+ return 0;
+ }
+
+ case DW_FORM_string:
+ case DW_FORM_strp:
+ case DW_FORM_GNU_str_index: {
+ const char *a_string = a_value.AsCString();
+ const char *b_string = b_value.AsCString();
+ if (a_string == b_string)
+ return 0;
+ else if (a_string && b_string)
+ return strcmp(a_string, b_string);
+ else if (a_string == nullptr)
+ return -1; // A string is NULL, and B is valid
+ else
+ return 1; // A string valid, and B is NULL
+ }
+
+ case DW_FORM_block:
+ case DW_FORM_block1:
+ case DW_FORM_block2:
+ case DW_FORM_block4:
+ case DW_FORM_exprloc: {
+ uint64_t a_len = a_value.Unsigned();
+ uint64_t b_len = b_value.Unsigned();
+ if (a_len < b_len)
+ return -1;
+ if (a_len > b_len)
+ return 1;
+ // The block lengths are the same
+ return memcmp(a_value.BlockData(), b_value.BlockData(), a_value.Unsigned());
+ } break;
+
+ case DW_FORM_ref1:
+ case DW_FORM_ref2:
+ case DW_FORM_ref4:
+ case DW_FORM_ref8:
+ case DW_FORM_ref_udata: {
+ uint64_t a = a_value.m_value.value.uval;
+ uint64_t b = b_value.m_value.value.uval;
+ if (a < b)
+ return -1;
+ if (a > b)
+ return 1;
+ return 0;
+ }
+
+ case DW_FORM_indirect:
+ llvm_unreachable(
+ "This shouldn't happen after the form has been extracted...");
+
+ default:
+ llvm_unreachable("Unhandled DW_FORM");
+ }
+ return -1;
+}
+
+bool DWARFFormValue::FormIsSupported(dw_form_t form) {
+ switch (form) {
+ case DW_FORM_addr:
+ case DW_FORM_addrx:
+ case DW_FORM_rnglistx:
+ case DW_FORM_block2:
+ case DW_FORM_block4:
+ case DW_FORM_data2:
+ case DW_FORM_data4:
+ case DW_FORM_data8:
+ case DW_FORM_string:
+ case DW_FORM_block:
+ case DW_FORM_block1:
+ case DW_FORM_data1:
+ case DW_FORM_flag:
+ case DW_FORM_sdata:
+ case DW_FORM_strp:
+ case DW_FORM_strx:
+ case DW_FORM_strx1:
+ case DW_FORM_strx2:
+ case DW_FORM_strx3:
+ case DW_FORM_strx4:
+ case DW_FORM_udata:
+ case DW_FORM_ref_addr:
+ case DW_FORM_ref1:
+ case DW_FORM_ref2:
+ case DW_FORM_ref4:
+ case DW_FORM_ref8:
+ case DW_FORM_ref_udata:
+ case DW_FORM_indirect:
+ case DW_FORM_sec_offset:
+ case DW_FORM_exprloc:
+ case DW_FORM_flag_present:
+ case DW_FORM_ref_sig8:
+ case DW_FORM_GNU_str_index:
+ case DW_FORM_GNU_addr_index:
+ case DW_FORM_implicit_const:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h
new file mode 100644
index 000000000000..848db2990ded
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h
@@ -0,0 +1,89 @@
+//===-- DWARFFormValue.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 SymbolFileDWARF_DWARFFormValue_h_
+#define SymbolFileDWARF_DWARFFormValue_h_
+
+#include "DWARFDataExtractor.h"
+#include <stddef.h>
+#include "llvm/ADT/Optional.h"
+
+class DWARFUnit;
+class SymbolFileDWARF;
+class DWARFDIE;
+
+class DWARFFormValue {
+public:
+ typedef struct ValueTypeTag {
+ ValueTypeTag() : value(), data(nullptr) { value.uval = 0; }
+
+ union {
+ uint64_t uval;
+ int64_t sval;
+ const char *cstr;
+ } value;
+ const uint8_t *data;
+ } ValueType;
+
+ enum {
+ eValueTypeInvalid = 0,
+ eValueTypeUnsigned,
+ eValueTypeSigned,
+ eValueTypeCStr,
+ eValueTypeBlock
+ };
+
+ DWARFFormValue() = default;
+ DWARFFormValue(const DWARFUnit *unit) : m_unit(unit) {}
+ DWARFFormValue(const DWARFUnit *unit, dw_form_t form)
+ : m_unit(unit), m_form(form) {}
+ void SetUnit(const DWARFUnit *unit) { m_unit = unit; }
+ dw_form_t Form() const { return m_form; }
+ dw_form_t& FormRef() { return m_form; }
+ void SetForm(dw_form_t form) { m_form = form; }
+ const ValueType &Value() const { return m_value; }
+ ValueType &ValueRef() { return m_value; }
+ void SetValue(const ValueType &val) { m_value = val; }
+
+ void Dump(lldb_private::Stream &s) const;
+ bool ExtractValue(const lldb_private::DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr);
+ const uint8_t *BlockData() const;
+ static llvm::Optional<uint8_t> GetFixedSize(dw_form_t form,
+ const DWARFUnit *u);
+ llvm::Optional<uint8_t> GetFixedSize() const;
+ DWARFDIE Reference() const;
+ uint64_t Reference(dw_offset_t offset) const;
+ bool Boolean() const { return m_value.value.uval != 0; }
+ uint64_t Unsigned() const { return m_value.value.uval; }
+ void SetUnsigned(uint64_t uval) { m_value.value.uval = uval; }
+ int64_t Signed() const { return m_value.value.sval; }
+ void SetSigned(int64_t sval) { m_value.value.sval = sval; }
+ const char *AsCString() const;
+ dw_addr_t Address() const;
+ bool IsValid() const { return m_form != 0; }
+ bool SkipValue(const lldb_private::DWARFDataExtractor &debug_info_data,
+ lldb::offset_t *offset_ptr) const;
+ static bool SkipValue(const dw_form_t form,
+ const lldb_private::DWARFDataExtractor &debug_info_data,
+ lldb::offset_t *offset_ptr, const DWARFUnit *unit);
+ static bool IsBlockForm(const dw_form_t form);
+ static bool IsDataForm(const dw_form_t form);
+ static int Compare(const DWARFFormValue &a, const DWARFFormValue &b);
+ void Clear();
+ static bool FormIsSupported(dw_form_t form);
+
+protected:
+ // Compile unit where m_value was located.
+ // It may be different from compile unit where m_value refers to.
+ const DWARFUnit *m_unit = nullptr; // Unit for this form
+ dw_form_t m_form = 0; // Form for this value
+ ValueType m_value; // Contains all data for the form
+};
+
+#endif // SymbolFileDWARF_DWARFFormValue_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp
new file mode 100644
index 000000000000..c122f756e81a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp
@@ -0,0 +1,66 @@
+//===-- DWARFIndex.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 "Plugins/SymbolFile/DWARF/DWARFIndex.h"
+#include "Plugins/Language/ObjC/ObjCLanguage.h"
+#include "Plugins/SymbolFile/DWARF/DWARFDIE.h"
+#include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+DWARFIndex::~DWARFIndex() = default;
+
+void DWARFIndex::ProcessFunctionDIE(llvm::StringRef name, DIERef ref,
+ SymbolFileDWARF &dwarf,
+ const CompilerDeclContext &parent_decl_ctx,
+ uint32_t name_type_mask,
+ std::vector<DWARFDIE> &dies) {
+ DWARFDIE die = dwarf.GetDIE(ref);
+ if (!die) {
+ ReportInvalidDIERef(ref, name);
+ return;
+ }
+
+ // Exit early if we're searching exclusively for methods or selectors and
+ // we have a context specified (no methods in namespaces).
+ uint32_t looking_for_nonmethods =
+ name_type_mask & ~(eFunctionNameTypeMethod | eFunctionNameTypeSelector);
+ if (!looking_for_nonmethods && parent_decl_ctx.IsValid())
+ return;
+
+ // Otherwise, we need to also check that the context matches. If it does not
+ // match, we do nothing.
+ if (!SymbolFileDWARF::DIEInDeclContext(&parent_decl_ctx, die))
+ return;
+
+ // In case of a full match, we just insert everything we find.
+ if (name_type_mask & eFunctionNameTypeFull) {
+ dies.push_back(die);
+ return;
+ }
+
+ // If looking for ObjC selectors, we need to also check if the name is a
+ // possible selector.
+ if (name_type_mask & eFunctionNameTypeSelector &&
+ ObjCLanguage::IsPossibleObjCMethodName(die.GetName())) {
+ dies.push_back(die);
+ return;
+ }
+
+ bool looking_for_methods = name_type_mask & lldb::eFunctionNameTypeMethod;
+ bool looking_for_functions = name_type_mask & lldb::eFunctionNameTypeBase;
+ if (looking_for_methods || looking_for_functions) {
+ // If we're looking for either methods or functions, we definitely want this
+ // die. Otherwise, only keep it if the die type matches what we are
+ // searching for.
+ if ((looking_for_methods && looking_for_functions) ||
+ looking_for_methods == die.IsMethod())
+ dies.push_back(die);
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h
new file mode 100644
index 000000000000..e3c7c5e63ac8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h
@@ -0,0 +1,66 @@
+//===-- DWARFIndex.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_DWARFINDEX_H
+#define LLDB_DWARFINDEX_H
+
+#include "Plugins/SymbolFile/DWARF/DIERef.h"
+#include "Plugins/SymbolFile/DWARF/DWARFDIE.h"
+#include "Plugins/SymbolFile/DWARF/DWARFFormValue.h"
+
+class DWARFDeclContext;
+class DWARFDIE;
+
+namespace lldb_private {
+class DWARFIndex {
+public:
+ DWARFIndex(Module &module) : m_module(module) {}
+ virtual ~DWARFIndex();
+
+ virtual void Preload() = 0;
+
+ /// Finds global variables with the given base name. Any additional filtering
+ /// (e.g., to only retrieve variables from a given context) should be done by
+ /// the consumer.
+ virtual void GetGlobalVariables(ConstString basename, DIEArray &offsets) = 0;
+
+ virtual void GetGlobalVariables(const RegularExpression &regex,
+ DIEArray &offsets) = 0;
+ virtual void GetGlobalVariables(const DWARFUnit &cu, DIEArray &offsets) = 0;
+ virtual void GetObjCMethods(ConstString class_name, DIEArray &offsets) = 0;
+ virtual void GetCompleteObjCClass(ConstString class_name,
+ bool must_be_implementation,
+ DIEArray &offsets) = 0;
+ virtual void GetTypes(ConstString name, DIEArray &offsets) = 0;
+ virtual void GetTypes(const DWARFDeclContext &context, DIEArray &offsets) = 0;
+ virtual void GetNamespaces(ConstString name, DIEArray &offsets) = 0;
+ virtual void GetFunctions(ConstString name, SymbolFileDWARF &dwarf,
+ const CompilerDeclContext &parent_decl_ctx,
+ uint32_t name_type_mask,
+ std::vector<DWARFDIE> &dies) = 0;
+ virtual void GetFunctions(const RegularExpression &regex,
+ DIEArray &offsets) = 0;
+
+ virtual void ReportInvalidDIERef(const DIERef &ref, llvm::StringRef name) = 0;
+ virtual void Dump(Stream &s) = 0;
+
+protected:
+ Module &m_module;
+
+ /// Helper function implementing common logic for processing function dies. If
+ /// the function given by "ref" matches search criteria given by
+ /// "parent_decl_ctx" and "name_type_mask", it is inserted into the "dies"
+ /// vector.
+ void ProcessFunctionDIE(llvm::StringRef name, DIERef ref,
+ SymbolFileDWARF &dwarf,
+ const CompilerDeclContext &parent_decl_ctx,
+ uint32_t name_type_mask, std::vector<DWARFDIE> &dies);
+};
+} // namespace lldb_private
+
+#endif // LLDB_DWARFINDEX_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.cpp
new file mode 100644
index 000000000000..fcc031bf1ea0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.cpp
@@ -0,0 +1,23 @@
+//===-- DWARFTypeUnit.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 "DWARFTypeUnit.h"
+
+#include "SymbolFileDWARF.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void DWARFTypeUnit::Dump(Stream *s) const {
+ s->Printf("0x%8.8x: Type Unit: length = 0x%8.8x, version = 0x%4.4x, "
+ "abbr_offset = 0x%8.8x, addr_size = 0x%2.2x (next CU at "
+ "{0x%8.8x})\n",
+ GetOffset(), GetLength(), GetVersion(), GetAbbrevOffset(),
+ GetAddressByteSize(), GetNextUnitOffset());
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.h
new file mode 100644
index 000000000000..6ff73ecd8efa
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.h
@@ -0,0 +1,37 @@
+//===-- DWARFTypeUnit.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 SymbolFileDWARF_DWARFTypeUnit_h_
+#define SymbolFileDWARF_DWARFTypeUnit_h_
+
+#include "DWARFUnit.h"
+#include "llvm/Support/Error.h"
+
+class DWARFTypeUnit : public DWARFUnit {
+public:
+ void BuildAddressRangeTable(DWARFDebugAranges *debug_aranges) override {}
+
+ void Dump(lldb_private::Stream *s) const override;
+
+ uint64_t GetTypeHash() { return m_header.GetTypeHash(); }
+
+ dw_offset_t GetTypeOffset() { return GetOffset() + m_header.GetTypeOffset(); }
+
+ static bool classof(const DWARFUnit *unit) { return unit->IsTypeUnit(); }
+
+private:
+ DWARFTypeUnit(SymbolFileDWARF &dwarf, lldb::user_id_t uid,
+ const DWARFUnitHeader &header,
+ const DWARFAbbreviationDeclarationSet &abbrevs,
+ DIERef::Section section)
+ : DWARFUnit(dwarf, uid, header, abbrevs, section) {}
+
+ friend class DWARFUnit;
+};
+
+#endif // SymbolFileDWARF_DWARFTypeUnit_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
new file mode 100644
index 000000000000..33e83d1fe57f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
@@ -0,0 +1,878 @@
+//===-- DWARFUnit.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 "DWARFUnit.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/Timer.h"
+#include "llvm/Object/Error.h"
+
+#include "DWARFCompileUnit.h"
+#include "DWARFDebugAranges.h"
+#include "DWARFDebugInfo.h"
+#include "DWARFTypeUnit.h"
+#include "LogChannelDWARF.h"
+#include "SymbolFileDWARFDwo.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace std;
+
+extern int g_verbose;
+
+DWARFUnit::DWARFUnit(SymbolFileDWARF &dwarf, lldb::user_id_t uid,
+ const DWARFUnitHeader &header,
+ const DWARFAbbreviationDeclarationSet &abbrevs,
+ DIERef::Section section)
+ : UserID(uid), m_dwarf(dwarf), m_header(header), m_abbrevs(&abbrevs),
+ m_cancel_scopes(false), m_section(section) {}
+
+DWARFUnit::~DWARFUnit() = default;
+
+// Parses first DIE of a compile unit.
+void DWARFUnit::ExtractUnitDIEIfNeeded() {
+ {
+ llvm::sys::ScopedReader lock(m_first_die_mutex);
+ if (m_first_die)
+ return; // Already parsed
+ }
+ llvm::sys::ScopedWriter lock(m_first_die_mutex);
+ if (m_first_die)
+ return; // Already parsed
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "%8.8x: DWARFUnit::ExtractUnitDIEIfNeeded()",
+ GetOffset());
+
+ // Set the offset to that of the first DIE and calculate the start of the
+ // next compilation unit header.
+ lldb::offset_t offset = GetFirstDIEOffset();
+
+ // We are in our compile unit, parse starting at the offset we were told to
+ // parse
+ const DWARFDataExtractor &data = GetData();
+ if (offset < GetNextUnitOffset() &&
+ m_first_die.Extract(data, this, &offset)) {
+ AddUnitDIE(m_first_die);
+ return;
+ }
+}
+
+// Parses a compile unit and indexes its DIEs if it hasn't already been done.
+// It will leave this compile unit extracted forever.
+void DWARFUnit::ExtractDIEsIfNeeded() {
+ m_cancel_scopes = true;
+
+ {
+ llvm::sys::ScopedReader lock(m_die_array_mutex);
+ if (!m_die_array.empty())
+ return; // Already parsed
+ }
+ llvm::sys::ScopedWriter lock(m_die_array_mutex);
+ if (!m_die_array.empty())
+ return; // Already parsed
+
+ ExtractDIEsRWLocked();
+}
+
+// Parses a compile unit and indexes its DIEs if it hasn't already been done.
+// It will clear this compile unit after returned instance gets out of scope,
+// no other ScopedExtractDIEs instance is running for this compile unit
+// and no ExtractDIEsIfNeeded() has been executed during this ScopedExtractDIEs
+// lifetime.
+DWARFUnit::ScopedExtractDIEs DWARFUnit::ExtractDIEsScoped() {
+ ScopedExtractDIEs scoped(*this);
+
+ {
+ llvm::sys::ScopedReader lock(m_die_array_mutex);
+ if (!m_die_array.empty())
+ return scoped; // Already parsed
+ }
+ llvm::sys::ScopedWriter lock(m_die_array_mutex);
+ if (!m_die_array.empty())
+ return scoped; // Already parsed
+
+ // Otherwise m_die_array would be already populated.
+ lldbassert(!m_cancel_scopes);
+
+ ExtractDIEsRWLocked();
+ scoped.m_clear_dies = true;
+ return scoped;
+}
+
+DWARFUnit::ScopedExtractDIEs::ScopedExtractDIEs(DWARFUnit &cu) : m_cu(&cu) {
+ m_cu->m_die_array_scoped_mutex.lock_shared();
+}
+
+DWARFUnit::ScopedExtractDIEs::~ScopedExtractDIEs() {
+ if (!m_cu)
+ return;
+ m_cu->m_die_array_scoped_mutex.unlock_shared();
+ if (!m_clear_dies || m_cu->m_cancel_scopes)
+ return;
+ // Be sure no other ScopedExtractDIEs is running anymore.
+ llvm::sys::ScopedWriter lock_scoped(m_cu->m_die_array_scoped_mutex);
+ llvm::sys::ScopedWriter lock(m_cu->m_die_array_mutex);
+ if (m_cu->m_cancel_scopes)
+ return;
+ m_cu->ClearDIEsRWLocked();
+}
+
+DWARFUnit::ScopedExtractDIEs::ScopedExtractDIEs(ScopedExtractDIEs &&rhs)
+ : m_cu(rhs.m_cu), m_clear_dies(rhs.m_clear_dies) {
+ rhs.m_cu = nullptr;
+}
+
+DWARFUnit::ScopedExtractDIEs &DWARFUnit::ScopedExtractDIEs::operator=(
+ DWARFUnit::ScopedExtractDIEs &&rhs) {
+ m_cu = rhs.m_cu;
+ rhs.m_cu = nullptr;
+ m_clear_dies = rhs.m_clear_dies;
+ return *this;
+}
+
+// Parses a compile unit and indexes its DIEs, m_die_array_mutex must be
+// held R/W and m_die_array must be empty.
+void DWARFUnit::ExtractDIEsRWLocked() {
+ llvm::sys::ScopedWriter first_die_lock(m_first_die_mutex);
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "%8.8x: DWARFUnit::ExtractDIEsIfNeeded()",
+ GetOffset());
+
+ // Set the offset to that of the first DIE and calculate the start of the
+ // next compilation unit header.
+ lldb::offset_t offset = GetFirstDIEOffset();
+ lldb::offset_t next_cu_offset = GetNextUnitOffset();
+
+ DWARFDebugInfoEntry die;
+
+ uint32_t depth = 0;
+ // We are in our compile unit, parse starting at the offset we were told to
+ // parse
+ const DWARFDataExtractor &data = GetData();
+ std::vector<uint32_t> die_index_stack;
+ die_index_stack.reserve(32);
+ die_index_stack.push_back(0);
+ bool prev_die_had_children = false;
+ while (offset < next_cu_offset && die.Extract(data, this, &offset)) {
+ const bool null_die = die.IsNULL();
+ if (depth == 0) {
+ assert(m_die_array.empty() && "Compile unit DIE already added");
+
+ // The average bytes per DIE entry has been seen to be around 14-20 so
+ // lets pre-reserve half of that since we are now stripping the NULL
+ // tags.
+
+ // Only reserve the memory if we are adding children of the main
+ // compile unit DIE. The compile unit DIE is always the first entry, so
+ // if our size is 1, then we are adding the first compile unit child
+ // DIE and should reserve the memory.
+ m_die_array.reserve(GetDebugInfoSize() / 24);
+ m_die_array.push_back(die);
+
+ if (!m_first_die)
+ AddUnitDIE(m_die_array.front());
+
+ // With -fsplit-dwarf-inlining, clang will emit non-empty skeleton compile
+ // units. We are not able to access these DIE *and* the dwo file
+ // simultaneously. We also don't need to do that as the dwo file will
+ // contain a superset of information. So, we don't even attempt to parse
+ // any remaining DIEs.
+ if (m_dwo_symbol_file) {
+ m_die_array.front().SetHasChildren(false);
+ break;
+ }
+
+ } else {
+ if (null_die) {
+ if (prev_die_had_children) {
+ // This will only happen if a DIE says is has children but all it
+ // contains is a NULL tag. Since we are removing the NULL DIEs from
+ // the list (saves up to 25% in C++ code), we need a way to let the
+ // DIE know that it actually doesn't have children.
+ if (!m_die_array.empty())
+ m_die_array.back().SetHasChildren(false);
+ }
+ } else {
+ die.SetParentIndex(m_die_array.size() - die_index_stack[depth - 1]);
+
+ if (die_index_stack.back())
+ m_die_array[die_index_stack.back()].SetSiblingIndex(
+ m_die_array.size() - die_index_stack.back());
+
+ // Only push the DIE if it isn't a NULL DIE
+ m_die_array.push_back(die);
+ }
+ }
+
+ if (null_die) {
+ // NULL DIE.
+ if (!die_index_stack.empty())
+ die_index_stack.pop_back();
+
+ if (depth > 0)
+ --depth;
+ prev_die_had_children = false;
+ } else {
+ die_index_stack.back() = m_die_array.size() - 1;
+ // Normal DIE
+ const bool die_has_children = die.HasChildren();
+ if (die_has_children) {
+ die_index_stack.push_back(0);
+ ++depth;
+ }
+ prev_die_had_children = die_has_children;
+ }
+
+ if (depth == 0)
+ break; // We are done with this compile unit!
+ }
+
+ if (!m_die_array.empty()) {
+ if (m_first_die) {
+ // Only needed for the assertion.
+ m_first_die.SetHasChildren(m_die_array.front().HasChildren());
+ lldbassert(m_first_die == m_die_array.front());
+ }
+ m_first_die = m_die_array.front();
+ }
+
+ m_die_array.shrink_to_fit();
+
+ if (m_dwo_symbol_file) {
+ DWARFUnit *dwo_cu = m_dwo_symbol_file->GetCompileUnit();
+ dwo_cu->ExtractDIEsIfNeeded();
+ }
+}
+
+// This is used when a split dwarf is enabled.
+// A skeleton compilation unit may contain the DW_AT_str_offsets_base attribute
+// that points to the first string offset of the CU contribution to the
+// .debug_str_offsets. At the same time, the corresponding split debug unit also
+// may use DW_FORM_strx* forms pointing to its own .debug_str_offsets.dwo and
+// for that case, we should find the offset (skip the section header).
+static void SetDwoStrOffsetsBase(DWARFUnit *dwo_cu) {
+ lldb::offset_t baseOffset = 0;
+
+ const DWARFDataExtractor &strOffsets =
+ dwo_cu->GetSymbolFileDWARF().GetDWARFContext().getOrLoadStrOffsetsData();
+ uint64_t length = strOffsets.GetU32(&baseOffset);
+ if (length == 0xffffffff)
+ length = strOffsets.GetU64(&baseOffset);
+
+ // Check version.
+ if (strOffsets.GetU16(&baseOffset) < 5)
+ return;
+
+ // Skip padding.
+ baseOffset += 2;
+
+ dwo_cu->SetStrOffsetsBase(baseOffset);
+}
+
+// m_die_array_mutex must be already held as read/write.
+void DWARFUnit::AddUnitDIE(const DWARFDebugInfoEntry &cu_die) {
+ llvm::Optional<uint64_t> addr_base, gnu_addr_base, ranges_base,
+ gnu_ranges_base;
+
+ DWARFAttributes attributes;
+ size_t num_attributes = cu_die.GetAttributes(this, attributes);
+ for (size_t i = 0; i < num_attributes; ++i) {
+ dw_attr_t attr = attributes.AttributeAtIndex(i);
+ DWARFFormValue form_value;
+ if (!attributes.ExtractFormValueAtIndex(i, form_value))
+ continue;
+ switch (attr) {
+ case DW_AT_addr_base:
+ addr_base = form_value.Unsigned();
+ SetAddrBase(*addr_base);
+ break;
+ case DW_AT_rnglists_base:
+ ranges_base = form_value.Unsigned();
+ SetRangesBase(*ranges_base);
+ break;
+ case DW_AT_str_offsets_base:
+ SetStrOffsetsBase(form_value.Unsigned());
+ break;
+ case DW_AT_low_pc:
+ SetBaseAddress(form_value.Address());
+ break;
+ case DW_AT_entry_pc:
+ // If the value was already set by DW_AT_low_pc, don't update it.
+ if (m_base_addr == LLDB_INVALID_ADDRESS)
+ SetBaseAddress(form_value.Address());
+ break;
+ case DW_AT_stmt_list:
+ m_line_table_offset = form_value.Unsigned();
+ break;
+ case DW_AT_GNU_addr_base:
+ gnu_addr_base = form_value.Unsigned();
+ break;
+ case DW_AT_GNU_ranges_base:
+ gnu_ranges_base = form_value.Unsigned();
+ break;
+ }
+ }
+
+ std::unique_ptr<SymbolFileDWARFDwo> dwo_symbol_file =
+ m_dwarf.GetDwoSymbolFileForCompileUnit(*this, cu_die);
+ if (!dwo_symbol_file)
+ return;
+
+ DWARFUnit *dwo_cu = dwo_symbol_file->GetCompileUnit();
+ if (!dwo_cu)
+ return; // Can't fetch the compile unit from the dwo file.
+
+ DWARFBaseDIE dwo_cu_die = dwo_cu->GetUnitDIEOnly();
+ if (!dwo_cu_die.IsValid())
+ return; // Can't fetch the compile unit DIE from the dwo file.
+
+ uint64_t main_dwo_id =
+ cu_die.GetAttributeValueAsUnsigned(this, DW_AT_GNU_dwo_id, 0);
+ uint64_t sub_dwo_id =
+ dwo_cu_die.GetAttributeValueAsUnsigned(DW_AT_GNU_dwo_id, 0);
+ if (main_dwo_id != sub_dwo_id)
+ return; // The 2 dwo ID isn't match. Don't use the dwo file as it belongs to
+ // a differectn compilation.
+
+ m_dwo_symbol_file = std::move(dwo_symbol_file);
+
+ // Here for DWO CU we want to use the address base set in the skeleton unit
+ // (DW_AT_addr_base) if it is available and use the DW_AT_GNU_addr_base
+ // otherwise. We do that because pre-DWARF v5 could use the DW_AT_GNU_*
+ // attributes which were applicable to the DWO units. The corresponding
+ // DW_AT_* attributes standardized in DWARF v5 are also applicable to the main
+ // unit in contrast.
+ if (addr_base)
+ dwo_cu->SetAddrBase(*addr_base);
+ else if (gnu_addr_base)
+ dwo_cu->SetAddrBase(*gnu_addr_base);
+
+ if (ranges_base)
+ dwo_cu->SetRangesBase(*ranges_base);
+ else if (gnu_ranges_base)
+ dwo_cu->SetRangesBase(*gnu_ranges_base);
+
+ for (size_t i = 0; i < m_dwo_symbol_file->DebugInfo()->GetNumUnits(); ++i) {
+ DWARFUnit *unit = m_dwo_symbol_file->DebugInfo()->GetUnitAtIndex(i);
+ SetDwoStrOffsetsBase(unit);
+ }
+}
+
+DWARFDIE DWARFUnit::LookupAddress(const dw_addr_t address) {
+ if (DIE()) {
+ const DWARFDebugAranges &func_aranges = GetFunctionAranges();
+
+ // Re-check the aranges auto pointer contents in case it was created above
+ if (!func_aranges.IsEmpty())
+ return GetDIE(func_aranges.FindAddress(address));
+ }
+ return DWARFDIE();
+}
+
+size_t DWARFUnit::AppendDIEsWithTag(const dw_tag_t tag,
+ std::vector<DWARFDIE> &dies,
+ uint32_t depth) const {
+ size_t old_size = dies.size();
+ {
+ llvm::sys::ScopedReader lock(m_die_array_mutex);
+ DWARFDebugInfoEntry::const_iterator pos;
+ DWARFDebugInfoEntry::const_iterator end = m_die_array.end();
+ for (pos = m_die_array.begin(); pos != end; ++pos) {
+ if (pos->Tag() == tag)
+ dies.emplace_back(this, &(*pos));
+ }
+ }
+
+ // Return the number of DIEs added to the collection
+ return dies.size() - old_size;
+}
+
+size_t DWARFUnit::GetDebugInfoSize() const {
+ return GetLengthByteSize() + GetLength() - GetHeaderByteSize();
+}
+
+const DWARFAbbreviationDeclarationSet *DWARFUnit::GetAbbreviations() const {
+ return m_abbrevs;
+}
+
+dw_offset_t DWARFUnit::GetAbbrevOffset() const {
+ return m_abbrevs ? m_abbrevs->GetOffset() : DW_INVALID_OFFSET;
+}
+
+dw_offset_t DWARFUnit::GetLineTableOffset() {
+ ExtractUnitDIEIfNeeded();
+ return m_line_table_offset;
+}
+
+void DWARFUnit::SetAddrBase(dw_addr_t addr_base) { m_addr_base = addr_base; }
+
+void DWARFUnit::SetRangesBase(dw_addr_t ranges_base) {
+ m_ranges_base = ranges_base;
+}
+
+void DWARFUnit::SetStrOffsetsBase(dw_offset_t str_offsets_base) {
+ m_str_offsets_base = str_offsets_base;
+}
+
+// It may be called only with m_die_array_mutex held R/W.
+void DWARFUnit::ClearDIEsRWLocked() {
+ m_die_array.clear();
+ m_die_array.shrink_to_fit();
+
+ if (m_dwo_symbol_file)
+ m_dwo_symbol_file->GetCompileUnit()->ClearDIEsRWLocked();
+}
+
+lldb::ByteOrder DWARFUnit::GetByteOrder() const {
+ return m_dwarf.GetObjectFile()->GetByteOrder();
+}
+
+TypeSystem *DWARFUnit::GetTypeSystem() {
+ return m_dwarf.GetTypeSystemForLanguage(GetLanguageType());
+}
+
+void DWARFUnit::SetBaseAddress(dw_addr_t base_addr) { m_base_addr = base_addr; }
+
+// Compare function DWARFDebugAranges::Range structures
+static bool CompareDIEOffset(const DWARFDebugInfoEntry &die,
+ const dw_offset_t die_offset) {
+ return die.GetOffset() < die_offset;
+}
+
+// GetDIE()
+//
+// Get the DIE (Debug Information Entry) with the specified offset by first
+// checking if the DIE is contained within this compile unit and grabbing the
+// DIE from this compile unit. Otherwise we grab the DIE from the DWARF file.
+DWARFDIE
+DWARFUnit::GetDIE(dw_offset_t die_offset) {
+ if (die_offset != DW_INVALID_OFFSET) {
+ if (GetDwoSymbolFile())
+ return GetDwoSymbolFile()->GetCompileUnit()->GetDIE(die_offset);
+
+ if (ContainsDIEOffset(die_offset)) {
+ ExtractDIEsIfNeeded();
+ DWARFDebugInfoEntry::const_iterator end = m_die_array.cend();
+ DWARFDebugInfoEntry::const_iterator pos =
+ lower_bound(m_die_array.cbegin(), end, die_offset, CompareDIEOffset);
+ if (pos != end) {
+ if (die_offset == (*pos).GetOffset())
+ return DWARFDIE(this, &(*pos));
+ }
+ } else
+ GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError(
+ "GetDIE for DIE 0x%" PRIx32 " is outside of its CU 0x%" PRIx32,
+ die_offset, GetOffset());
+ }
+ return DWARFDIE(); // Not found
+}
+
+DWARFUnit &DWARFUnit::GetNonSkeletonUnit() {
+ if (SymbolFileDWARFDwo *dwo = GetDwoSymbolFile())
+ return *dwo->GetCompileUnit();
+ return *this;
+}
+
+uint8_t DWARFUnit::GetAddressByteSize(const DWARFUnit *cu) {
+ if (cu)
+ return cu->GetAddressByteSize();
+ return DWARFUnit::GetDefaultAddressSize();
+}
+
+uint8_t DWARFUnit::GetDefaultAddressSize() { return 4; }
+
+void *DWARFUnit::GetUserData() const { return m_user_data; }
+
+void DWARFUnit::SetUserData(void *d) {
+ m_user_data = d;
+ if (m_dwo_symbol_file)
+ m_dwo_symbol_file->GetCompileUnit()->SetUserData(d);
+}
+
+bool DWARFUnit::Supports_DW_AT_APPLE_objc_complete_type() {
+ return GetProducer() != eProducerLLVMGCC;
+}
+
+bool DWARFUnit::DW_AT_decl_file_attributes_are_invalid() {
+ // llvm-gcc makes completely invalid decl file attributes and won't ever be
+ // fixed, so we need to know to ignore these.
+ return GetProducer() == eProducerLLVMGCC;
+}
+
+bool DWARFUnit::Supports_unnamed_objc_bitfields() {
+ if (GetProducer() == eProducerClang) {
+ const uint32_t major_version = GetProducerVersionMajor();
+ return major_version > 425 ||
+ (major_version == 425 && GetProducerVersionUpdate() >= 13);
+ }
+ return true; // Assume all other compilers didn't have incorrect ObjC bitfield
+ // info
+}
+
+void DWARFUnit::ParseProducerInfo() {
+ m_producer_version_major = UINT32_MAX;
+ m_producer_version_minor = UINT32_MAX;
+ m_producer_version_update = UINT32_MAX;
+
+ const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly();
+ if (die) {
+
+ const char *producer_cstr =
+ die->GetAttributeValueAsString(this, DW_AT_producer, nullptr);
+ if (producer_cstr) {
+ RegularExpression llvm_gcc_regex(
+ llvm::StringRef("^4\\.[012]\\.[01] \\(Based on Apple "
+ "Inc\\. build [0-9]+\\) \\(LLVM build "
+ "[\\.0-9]+\\)$"));
+ if (llvm_gcc_regex.Execute(llvm::StringRef(producer_cstr))) {
+ m_producer = eProducerLLVMGCC;
+ } else if (strstr(producer_cstr, "clang")) {
+ static RegularExpression g_clang_version_regex(
+ llvm::StringRef("clang-([0-9]+)\\.([0-9]+)\\.([0-9]+)"));
+ RegularExpression::Match regex_match(3);
+ if (g_clang_version_regex.Execute(llvm::StringRef(producer_cstr),
+ &regex_match)) {
+ std::string str;
+ if (regex_match.GetMatchAtIndex(producer_cstr, 1, str))
+ m_producer_version_major =
+ StringConvert::ToUInt32(str.c_str(), UINT32_MAX, 10);
+ if (regex_match.GetMatchAtIndex(producer_cstr, 2, str))
+ m_producer_version_minor =
+ StringConvert::ToUInt32(str.c_str(), UINT32_MAX, 10);
+ if (regex_match.GetMatchAtIndex(producer_cstr, 3, str))
+ m_producer_version_update =
+ StringConvert::ToUInt32(str.c_str(), UINT32_MAX, 10);
+ }
+ m_producer = eProducerClang;
+ } else if (strstr(producer_cstr, "GNU"))
+ m_producer = eProducerGCC;
+ }
+ }
+ if (m_producer == eProducerInvalid)
+ m_producer = eProcucerOther;
+}
+
+DWARFProducer DWARFUnit::GetProducer() {
+ if (m_producer == eProducerInvalid)
+ ParseProducerInfo();
+ return m_producer;
+}
+
+uint32_t DWARFUnit::GetProducerVersionMajor() {
+ if (m_producer_version_major == 0)
+ ParseProducerInfo();
+ return m_producer_version_major;
+}
+
+uint32_t DWARFUnit::GetProducerVersionMinor() {
+ if (m_producer_version_minor == 0)
+ ParseProducerInfo();
+ return m_producer_version_minor;
+}
+
+uint32_t DWARFUnit::GetProducerVersionUpdate() {
+ if (m_producer_version_update == 0)
+ ParseProducerInfo();
+ return m_producer_version_update;
+}
+LanguageType DWARFUnit::LanguageTypeFromDWARF(uint64_t val) {
+ // Note: user languages between lo_user and hi_user must be handled
+ // explicitly here.
+ switch (val) {
+ case DW_LANG_Mips_Assembler:
+ return eLanguageTypeMipsAssembler;
+ case DW_LANG_GOOGLE_RenderScript:
+ return eLanguageTypeExtRenderScript;
+ default:
+ return static_cast<LanguageType>(val);
+ }
+}
+
+LanguageType DWARFUnit::GetLanguageType() {
+ if (m_language_type != eLanguageTypeUnknown)
+ return m_language_type;
+
+ const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly();
+ if (die)
+ m_language_type = LanguageTypeFromDWARF(
+ die->GetAttributeValueAsUnsigned(this, DW_AT_language, 0));
+ return m_language_type;
+}
+
+bool DWARFUnit::GetIsOptimized() {
+ if (m_is_optimized == eLazyBoolCalculate) {
+ const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly();
+ if (die) {
+ m_is_optimized = eLazyBoolNo;
+ if (die->GetAttributeValueAsUnsigned(this, DW_AT_APPLE_optimized, 0) ==
+ 1) {
+ m_is_optimized = eLazyBoolYes;
+ }
+ }
+ }
+ return m_is_optimized == eLazyBoolYes;
+}
+
+FileSpec::Style DWARFUnit::GetPathStyle() {
+ if (!m_comp_dir)
+ ComputeCompDirAndGuessPathStyle();
+ return m_comp_dir->GetPathStyle();
+}
+
+const FileSpec &DWARFUnit::GetCompilationDirectory() {
+ if (!m_comp_dir)
+ ComputeCompDirAndGuessPathStyle();
+ return *m_comp_dir;
+}
+
+const FileSpec &DWARFUnit::GetAbsolutePath() {
+ if (!m_file_spec)
+ ComputeAbsolutePath();
+ return *m_file_spec;
+}
+
+FileSpec DWARFUnit::GetFile(size_t file_idx) {
+ return m_dwarf.GetFile(*this, file_idx);
+}
+
+// DWARF2/3 suggests the form hostname:pathname for compilation directory.
+// Remove the host part if present.
+static llvm::StringRef
+removeHostnameFromPathname(llvm::StringRef path_from_dwarf) {
+ llvm::StringRef host, path;
+ std::tie(host, path) = path_from_dwarf.split(':');
+
+ if (host.contains('/'))
+ return path_from_dwarf;
+
+ // check whether we have a windows path, and so the first character is a
+ // drive-letter not a hostname.
+ if (host.size() == 1 && llvm::isAlpha(host[0]) && path.startswith("\\"))
+ return path_from_dwarf;
+
+ return path;
+}
+
+static FileSpec resolveCompDir(const FileSpec &path) {
+ bool is_symlink = SymbolFileDWARF::GetSymlinkPaths().FindFileIndex(
+ 0, path, /*full*/ true) != UINT32_MAX;
+
+ if (!is_symlink)
+ return path;
+
+ namespace fs = llvm::sys::fs;
+ if (fs::get_file_type(path.GetPath(), false) != fs::file_type::symlink_file)
+ return path;
+
+ FileSpec resolved_symlink;
+ const auto error = FileSystem::Instance().Readlink(path, resolved_symlink);
+ if (error.Success())
+ return resolved_symlink;
+
+ return path;
+}
+
+void DWARFUnit::ComputeCompDirAndGuessPathStyle() {
+ m_comp_dir = FileSpec();
+ const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly();
+ if (!die)
+ return;
+
+ llvm::StringRef comp_dir = removeHostnameFromPathname(
+ die->GetAttributeValueAsString(this, DW_AT_comp_dir, nullptr));
+ if (!comp_dir.empty()) {
+ FileSpec::Style comp_dir_style =
+ FileSpec::GuessPathStyle(comp_dir).getValueOr(FileSpec::Style::native);
+ m_comp_dir = resolveCompDir(FileSpec(comp_dir, comp_dir_style));
+ } else {
+ // Try to detect the style based on the DW_AT_name attribute, but just store
+ // the detected style in the m_comp_dir field.
+ const char *name =
+ die->GetAttributeValueAsString(this, DW_AT_name, nullptr);
+ m_comp_dir = FileSpec(
+ "", FileSpec::GuessPathStyle(name).getValueOr(FileSpec::Style::native));
+ }
+}
+
+void DWARFUnit::ComputeAbsolutePath() {
+ m_file_spec = FileSpec();
+ const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly();
+ if (!die)
+ return;
+
+ m_file_spec =
+ FileSpec(die->GetAttributeValueAsString(this, DW_AT_name, nullptr),
+ GetPathStyle());
+
+ if (m_file_spec->IsRelative())
+ m_file_spec->MakeAbsolute(GetCompilationDirectory());
+}
+
+SymbolFileDWARFDwo *DWARFUnit::GetDwoSymbolFile() const {
+ return m_dwo_symbol_file.get();
+}
+
+const DWARFDebugAranges &DWARFUnit::GetFunctionAranges() {
+ if (m_func_aranges_up == nullptr) {
+ m_func_aranges_up.reset(new DWARFDebugAranges());
+ const DWARFDebugInfoEntry *die = DIEPtr();
+ if (die)
+ die->BuildFunctionAddressRangeTable(this, m_func_aranges_up.get());
+
+ if (m_dwo_symbol_file) {
+ DWARFUnit *dwo_cu = m_dwo_symbol_file->GetCompileUnit();
+ const DWARFDebugInfoEntry *dwo_die = dwo_cu->DIEPtr();
+ if (dwo_die)
+ dwo_die->BuildFunctionAddressRangeTable(dwo_cu,
+ m_func_aranges_up.get());
+ }
+
+ const bool minimize = false;
+ m_func_aranges_up->Sort(minimize);
+ }
+ return *m_func_aranges_up;
+}
+
+llvm::Expected<DWARFUnitHeader>
+DWARFUnitHeader::extract(const DWARFDataExtractor &data, DIERef::Section section,
+ lldb::offset_t *offset_ptr) {
+ DWARFUnitHeader header;
+ header.m_offset = *offset_ptr;
+ header.m_length = data.GetDWARFInitialLength(offset_ptr);
+ header.m_version = data.GetU16(offset_ptr);
+ if (header.m_version == 5) {
+ header.m_unit_type = data.GetU8(offset_ptr);
+ header.m_addr_size = data.GetU8(offset_ptr);
+ header.m_abbr_offset = data.GetDWARFOffset(offset_ptr);
+ if (header.m_unit_type == llvm::dwarf::DW_UT_skeleton)
+ header.m_dwo_id = data.GetU64(offset_ptr);
+ } else {
+ header.m_abbr_offset = data.GetDWARFOffset(offset_ptr);
+ header.m_addr_size = data.GetU8(offset_ptr);
+ header.m_unit_type =
+ section == DIERef::Section::DebugTypes ? DW_UT_type : DW_UT_compile;
+ }
+
+ if (header.IsTypeUnit()) {
+ header.m_type_hash = data.GetU64(offset_ptr);
+ header.m_type_offset = data.GetDWARFOffset(offset_ptr);
+ }
+
+ bool length_OK = data.ValidOffset(header.GetNextUnitOffset() - 1);
+ bool version_OK = SymbolFileDWARF::SupportedVersion(header.m_version);
+ bool addr_size_OK = (header.m_addr_size == 4) || (header.m_addr_size == 8);
+ bool type_offset_OK =
+ !header.IsTypeUnit() || (header.m_type_offset <= header.GetLength());
+
+ if (!length_OK)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "Invalid unit length");
+ if (!version_OK)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "Unsupported unit version");
+ if (!addr_size_OK)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "Invalid unit address size");
+ if (!type_offset_OK)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "Type offset out of range");
+
+ return header;
+}
+
+llvm::Expected<DWARFUnitSP>
+DWARFUnit::extract(SymbolFileDWARF &dwarf, user_id_t uid,
+ const DWARFDataExtractor &debug_info,
+ DIERef::Section section, lldb::offset_t *offset_ptr) {
+ assert(debug_info.ValidOffset(*offset_ptr));
+
+ auto expected_header =
+ DWARFUnitHeader::extract(debug_info, section, offset_ptr);
+ if (!expected_header)
+ return expected_header.takeError();
+
+ const DWARFDebugAbbrev *abbr = dwarf.DebugAbbrev();
+ if (!abbr)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "No debug_abbrev data");
+
+ bool abbr_offset_OK =
+ dwarf.GetDWARFContext().getOrLoadAbbrevData().ValidOffset(
+ expected_header->GetAbbrOffset());
+ if (!abbr_offset_OK)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "Abbreviation offset for unit is not valid");
+
+ const DWARFAbbreviationDeclarationSet *abbrevs =
+ abbr->GetAbbreviationDeclarationSet(expected_header->GetAbbrOffset());
+ if (!abbrevs)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "No abbrev exists at the specified offset.");
+
+ if (expected_header->IsTypeUnit())
+ return DWARFUnitSP(
+ new DWARFTypeUnit(dwarf, uid, *expected_header, *abbrevs, section));
+ return DWARFUnitSP(
+ new DWARFCompileUnit(dwarf, uid, *expected_header, *abbrevs, section));
+}
+
+const lldb_private::DWARFDataExtractor &DWARFUnit::GetData() const {
+ return m_section == DIERef::Section::DebugTypes
+ ? m_dwarf.GetDWARFContext().getOrLoadDebugTypesData()
+ : m_dwarf.GetDWARFContext().getOrLoadDebugInfoData();
+}
+
+uint32_t DWARFUnit::GetHeaderByteSize() const {
+ switch (m_header.GetUnitType()) {
+ case llvm::dwarf::DW_UT_compile:
+ case llvm::dwarf::DW_UT_partial:
+ return GetVersion() < 5 ? 11 : 12;
+ case llvm::dwarf::DW_UT_skeleton:
+ case llvm::dwarf::DW_UT_split_compile:
+ return 20;
+ case llvm::dwarf::DW_UT_type:
+ case llvm::dwarf::DW_UT_split_type:
+ return GetVersion() < 5 ? 23 : 24;
+ }
+ llvm_unreachable("invalid UnitType.");
+}
+
+llvm::Expected<DWARFRangeList>
+DWARFUnit::FindRnglistFromOffset(dw_offset_t offset) const {
+ const DWARFDebugRangesBase *debug_ranges;
+ llvm::StringRef section;
+ if (GetVersion() <= 4) {
+ debug_ranges = m_dwarf.GetDebugRanges();
+ section = "debug_ranges";
+ } else {
+ debug_ranges = m_dwarf.GetDebugRngLists();
+ section = "debug_rnglists";
+ }
+ if (!debug_ranges)
+ return llvm::make_error<llvm::object::GenericBinaryError>("No " + section +
+ " section");
+
+ DWARFRangeList ranges;
+ debug_ranges->FindRanges(this, offset, ranges);
+ return ranges;
+}
+
+llvm::Expected<DWARFRangeList>
+DWARFUnit::FindRnglistFromIndex(uint32_t index) const {
+ const DWARFDebugRangesBase *debug_rnglists = m_dwarf.GetDebugRngLists();
+ if (!debug_rnglists)
+ return llvm::make_error<llvm::object::GenericBinaryError>(
+ "No debug_rnglists section");
+ return FindRnglistFromOffset(debug_rnglists->GetOffset(index));
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h
new file mode 100644
index 000000000000..8aa1e449f3ed
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h
@@ -0,0 +1,306 @@
+//===-- DWARFUnit.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 SymbolFileDWARF_DWARFUnit_h_
+#define SymbolFileDWARF_DWARFUnit_h_
+
+#include "DWARFDIE.h"
+#include "DWARFDebugInfoEntry.h"
+#include "lldb/lldb-enumerations.h"
+#include "llvm/Support/RWMutex.h"
+#include <atomic>
+
+class DWARFUnit;
+class DWARFCompileUnit;
+class NameToDIE;
+class SymbolFileDWARF;
+class SymbolFileDWARFDwo;
+
+typedef std::shared_ptr<DWARFUnit> DWARFUnitSP;
+
+enum DWARFProducer {
+ eProducerInvalid = 0,
+ eProducerClang,
+ eProducerGCC,
+ eProducerLLVMGCC,
+ eProcucerOther
+};
+
+/// Base class describing the header of any kind of "unit." Some information
+/// is specific to certain unit types. We separate this class out so we can
+/// parse the header before deciding what specific kind of unit to construct.
+class DWARFUnitHeader {
+ dw_offset_t m_offset = 0;
+ dw_offset_t m_length = 0;
+ uint16_t m_version = 0;
+ dw_offset_t m_abbr_offset = 0;
+ uint8_t m_unit_type = 0;
+ uint8_t m_addr_size = 0;
+
+ uint64_t m_type_hash = 0;
+ uint32_t m_type_offset = 0;
+
+ uint64_t m_dwo_id = 0;
+
+ DWARFUnitHeader() = default;
+
+public:
+ dw_offset_t GetOffset() const { return m_offset; }
+ uint16_t GetVersion() const { return m_version; }
+ uint16_t GetAddressByteSize() const { return m_addr_size; }
+ dw_offset_t GetLength() const { return m_length; }
+ dw_offset_t GetAbbrOffset() const { return m_abbr_offset; }
+ uint8_t GetUnitType() const { return m_unit_type; }
+ uint64_t GetTypeHash() const { return m_type_hash; }
+ dw_offset_t GetTypeOffset() const { return m_type_offset; }
+ bool IsTypeUnit() const {
+ return m_unit_type == DW_UT_type || m_unit_type == DW_UT_split_type;
+ }
+ uint32_t GetNextUnitOffset() const { return m_offset + m_length + 4; }
+
+ static llvm::Expected<DWARFUnitHeader>
+ extract(const lldb_private::DWARFDataExtractor &data, DIERef::Section section,
+ lldb::offset_t *offset_ptr);
+};
+
+class DWARFUnit : public lldb_private::UserID {
+ using die_iterator_range =
+ llvm::iterator_range<DWARFDebugInfoEntry::collection::iterator>;
+
+public:
+ static llvm::Expected<DWARFUnitSP>
+ extract(SymbolFileDWARF &dwarf2Data, lldb::user_id_t uid,
+ const lldb_private::DWARFDataExtractor &debug_info,
+ DIERef::Section section, lldb::offset_t *offset_ptr);
+ virtual ~DWARFUnit();
+
+ void ExtractUnitDIEIfNeeded();
+ void ExtractDIEsIfNeeded();
+
+ class ScopedExtractDIEs {
+ DWARFUnit *m_cu;
+ public:
+ bool m_clear_dies = false;
+ ScopedExtractDIEs(DWARFUnit &cu);
+ ~ScopedExtractDIEs();
+ DISALLOW_COPY_AND_ASSIGN(ScopedExtractDIEs);
+ ScopedExtractDIEs(ScopedExtractDIEs &&rhs);
+ ScopedExtractDIEs &operator=(ScopedExtractDIEs &&rhs);
+ };
+ ScopedExtractDIEs ExtractDIEsScoped();
+
+ DWARFDIE LookupAddress(const dw_addr_t address);
+ size_t AppendDIEsWithTag(const dw_tag_t tag, std::vector<DWARFDIE> &dies,
+ uint32_t depth = UINT32_MAX) const;
+ bool Verify(lldb_private::Stream *s) const;
+ virtual void Dump(lldb_private::Stream *s) const = 0;
+ /// Get the data that contains the DIE information for this unit.
+ ///
+ /// This will return the correct bytes that contain the data for
+ /// this DWARFUnit. It could be .debug_info or .debug_types
+ /// depending on where the data for this unit originates.
+ ///
+ /// \return
+ /// The correct data for the DIE information in this unit.
+ const lldb_private::DWARFDataExtractor &GetData() const;
+
+ /// Get the size in bytes of the unit header.
+ ///
+ /// \return
+ /// Byte size of the unit header
+ uint32_t GetHeaderByteSize() const;
+
+ // Offset of the initial length field.
+ dw_offset_t GetOffset() const { return m_header.GetOffset(); }
+ /// Get the size in bytes of the length field in the header.
+ ///
+ /// In DWARF32 this is just 4 bytes
+ ///
+ /// \return
+ /// Byte size of the compile unit header length field
+ size_t GetLengthByteSize() const { return 4; }
+
+ bool ContainsDIEOffset(dw_offset_t die_offset) const {
+ return die_offset >= GetFirstDIEOffset() &&
+ die_offset < GetNextUnitOffset();
+ }
+ dw_offset_t GetFirstDIEOffset() const {
+ return GetOffset() + GetHeaderByteSize();
+ }
+ dw_offset_t GetNextUnitOffset() const { return m_header.GetNextUnitOffset(); }
+ // Size of the CU data (without initial length and without header).
+ size_t GetDebugInfoSize() const;
+ // Size of the CU data incl. header but without initial length.
+ uint32_t GetLength() const { return m_header.GetLength(); }
+ uint16_t GetVersion() const { return m_header.GetVersion(); }
+ const DWARFAbbreviationDeclarationSet *GetAbbreviations() const;
+ dw_offset_t GetAbbrevOffset() const;
+ uint8_t GetAddressByteSize() const { return m_header.GetAddressByteSize(); }
+ dw_addr_t GetAddrBase() const { return m_addr_base; }
+ dw_addr_t GetBaseAddress() const { return m_base_addr; }
+ dw_offset_t GetLineTableOffset();
+ dw_addr_t GetRangesBase() const { return m_ranges_base; }
+ dw_addr_t GetStrOffsetsBase() const { return m_str_offsets_base; }
+ void SetAddrBase(dw_addr_t addr_base);
+ void SetRangesBase(dw_addr_t ranges_base);
+ void SetStrOffsetsBase(dw_offset_t str_offsets_base);
+ virtual void BuildAddressRangeTable(DWARFDebugAranges *debug_aranges) = 0;
+
+ lldb::ByteOrder GetByteOrder() const;
+
+ lldb_private::TypeSystem *GetTypeSystem();
+
+ const DWARFDebugAranges &GetFunctionAranges();
+
+ void SetBaseAddress(dw_addr_t base_addr);
+
+ DWARFBaseDIE GetUnitDIEOnly() { return DWARFDIE(this, GetUnitDIEPtrOnly()); }
+
+ DWARFDIE DIE() { return DWARFDIE(this, DIEPtr()); }
+
+ DWARFDIE GetDIE(dw_offset_t die_offset);
+
+ DWARFUnit &GetNonSkeletonUnit();
+
+ static uint8_t GetAddressByteSize(const DWARFUnit *cu);
+
+ static uint8_t GetDefaultAddressSize();
+
+ void *GetUserData() const;
+
+ void SetUserData(void *d);
+
+ bool Supports_DW_AT_APPLE_objc_complete_type();
+
+ bool DW_AT_decl_file_attributes_are_invalid();
+
+ bool Supports_unnamed_objc_bitfields();
+
+ SymbolFileDWARF &GetSymbolFileDWARF() const { return m_dwarf; }
+
+ DWARFProducer GetProducer();
+
+ uint32_t GetProducerVersionMajor();
+
+ uint32_t GetProducerVersionMinor();
+
+ uint32_t GetProducerVersionUpdate();
+
+ static lldb::LanguageType LanguageTypeFromDWARF(uint64_t val);
+
+ lldb::LanguageType GetLanguageType();
+
+ bool GetIsOptimized();
+
+ const lldb_private::FileSpec &GetCompilationDirectory();
+ const lldb_private::FileSpec &GetAbsolutePath();
+ lldb_private::FileSpec GetFile(size_t file_idx);
+ lldb_private::FileSpec::Style GetPathStyle();
+
+ SymbolFileDWARFDwo *GetDwoSymbolFile() const;
+
+ die_iterator_range dies() {
+ ExtractDIEsIfNeeded();
+ return die_iterator_range(m_die_array.begin(), m_die_array.end());
+ }
+
+ DIERef::Section GetDebugSection() const { return m_section; }
+
+ uint8_t GetUnitType() const { return m_header.GetUnitType(); }
+ bool IsTypeUnit() const { return m_header.IsTypeUnit(); }
+
+ /// Return a list of address ranges resulting from a (possibly encoded)
+ /// range list starting at a given offset in the appropriate ranges section.
+ llvm::Expected<DWARFRangeList> FindRnglistFromOffset(dw_offset_t offset) const;
+
+ /// Return a list of address ranges retrieved from an encoded range
+ /// list whose offset is found via a table lookup given an index (DWARF v5
+ /// and later).
+ llvm::Expected<DWARFRangeList> FindRnglistFromIndex(uint32_t index) const;
+
+protected:
+ DWARFUnit(SymbolFileDWARF &dwarf, lldb::user_id_t uid,
+ const DWARFUnitHeader &header,
+ const DWARFAbbreviationDeclarationSet &abbrevs,
+ DIERef::Section section);
+
+ llvm::Error ExtractHeader(SymbolFileDWARF &dwarf,
+ const lldb_private::DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr);
+
+ // Get the DWARF unit DWARF debug information entry. Parse the single DIE
+ // if needed.
+ const DWARFDebugInfoEntry *GetUnitDIEPtrOnly() {
+ ExtractUnitDIEIfNeeded();
+ // m_first_die_mutex is not required as m_first_die is never cleared.
+ if (!m_first_die)
+ return NULL;
+ return &m_first_die;
+ }
+
+ // Get all DWARF debug informration entries. Parse all DIEs if needed.
+ const DWARFDebugInfoEntry *DIEPtr() {
+ ExtractDIEsIfNeeded();
+ if (m_die_array.empty())
+ return NULL;
+ return &m_die_array[0];
+ }
+
+ SymbolFileDWARF &m_dwarf;
+ std::unique_ptr<SymbolFileDWARFDwo> m_dwo_symbol_file;
+ DWARFUnitHeader m_header;
+ const DWARFAbbreviationDeclarationSet *m_abbrevs = nullptr;
+ void *m_user_data = nullptr;
+ // The compile unit debug information entry item
+ DWARFDebugInfoEntry::collection m_die_array;
+ mutable llvm::sys::RWMutex m_die_array_mutex;
+ // It is used for tracking of ScopedExtractDIEs instances.
+ mutable llvm::sys::RWMutex m_die_array_scoped_mutex;
+ // ScopedExtractDIEs instances should not call ClearDIEsRWLocked()
+ // as someone called ExtractDIEsIfNeeded().
+ std::atomic<bool> m_cancel_scopes;
+ // GetUnitDIEPtrOnly() needs to return pointer to the first DIE.
+ // But the first element of m_die_array after ExtractUnitDIEIfNeeded()
+ // would possibly move in memory after later ExtractDIEsIfNeeded().
+ DWARFDebugInfoEntry m_first_die;
+ llvm::sys::RWMutex m_first_die_mutex;
+ // A table similar to the .debug_aranges table, but this one points to the
+ // exact DW_TAG_subprogram DIEs
+ std::unique_ptr<DWARFDebugAranges> m_func_aranges_up;
+ dw_addr_t m_base_addr = 0;
+ DWARFProducer m_producer = eProducerInvalid;
+ uint32_t m_producer_version_major = 0;
+ uint32_t m_producer_version_minor = 0;
+ uint32_t m_producer_version_update = 0;
+ lldb::LanguageType m_language_type = lldb::eLanguageTypeUnknown;
+ lldb_private::LazyBool m_is_optimized = lldb_private::eLazyBoolCalculate;
+ llvm::Optional<lldb_private::FileSpec> m_comp_dir;
+ llvm::Optional<lldb_private::FileSpec> m_file_spec;
+ dw_addr_t m_addr_base = 0; // Value of DW_AT_addr_base
+ dw_addr_t m_ranges_base = 0; // Value of DW_AT_ranges_base
+
+ /// Value of DW_AT_stmt_list.
+ dw_offset_t m_line_table_offset = DW_INVALID_OFFSET;
+
+ dw_offset_t m_str_offsets_base = 0; // Value of DW_AT_str_offsets_base.
+ const DIERef::Section m_section;
+
+private:
+ void ParseProducerInfo();
+ void ExtractDIEsRWLocked();
+ void ClearDIEsRWLocked();
+
+ void AddUnitDIE(const DWARFDebugInfoEntry &cu_die);
+
+ void ComputeCompDirAndGuessPathStyle();
+ void ComputeAbsolutePath();
+
+ DISALLOW_COPY_AND_ASSIGN(DWARFUnit);
+};
+
+#endif // SymbolFileDWARF_DWARFUnit_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp
new file mode 100644
index 000000000000..9746ad76c930
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp
@@ -0,0 +1,272 @@
+//===-- DebugNamesDWARFIndex.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 "Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h"
+#include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h"
+#include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h"
+#include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+llvm::Expected<std::unique_ptr<DebugNamesDWARFIndex>>
+DebugNamesDWARFIndex::Create(Module &module, DWARFDataExtractor debug_names,
+ DWARFDataExtractor debug_str,
+ DWARFDebugInfo *debug_info) {
+ if (!debug_info) {
+ return llvm::make_error<llvm::StringError>("debug info null",
+ llvm::inconvertibleErrorCode());
+ }
+ auto index_up = llvm::make_unique<DebugNames>(debug_names.GetAsLLVM(),
+ debug_str.GetAsLLVM());
+ if (llvm::Error E = index_up->extract())
+ return std::move(E);
+
+ return std::unique_ptr<DebugNamesDWARFIndex>(new DebugNamesDWARFIndex(
+ module, std::move(index_up), debug_names, debug_str, *debug_info));
+}
+
+llvm::DenseSet<dw_offset_t>
+DebugNamesDWARFIndex::GetUnits(const DebugNames &debug_names) {
+ llvm::DenseSet<dw_offset_t> result;
+ for (const DebugNames::NameIndex &ni : debug_names) {
+ for (uint32_t cu = 0; cu < ni.getCUCount(); ++cu)
+ result.insert(ni.getCUOffset(cu));
+ }
+ return result;
+}
+
+llvm::Optional<DIERef>
+DebugNamesDWARFIndex::ToDIERef(const DebugNames::Entry &entry) {
+ llvm::Optional<uint64_t> cu_offset = entry.getCUOffset();
+ if (!cu_offset)
+ return llvm::None;
+
+ DWARFUnit *cu = m_debug_info.GetUnitAtOffset(DIERef::Section::DebugInfo, *cu_offset);
+ if (!cu)
+ return llvm::None;
+
+ // This initializes the DWO symbol file. It's not possible for
+ // GetDwoSymbolFile to call this automatically because of mutual recursion
+ // between this and DWARFDebugInfoEntry::GetAttributeValue.
+ cu->ExtractUnitDIEIfNeeded();
+ cu = &cu->GetNonSkeletonUnit();
+
+ if (llvm::Optional<uint64_t> die_offset = entry.getDIEUnitOffset())
+ return DIERef(cu->GetSymbolFileDWARF().GetDwoNum(),
+ DIERef::Section::DebugInfo, cu->GetOffset() + *die_offset);
+
+ return llvm::None;
+}
+
+void DebugNamesDWARFIndex::Append(const DebugNames::Entry &entry,
+ DIEArray &offsets) {
+ if (llvm::Optional<DIERef> ref = ToDIERef(entry))
+ offsets.push_back(*ref);
+}
+
+void DebugNamesDWARFIndex::MaybeLogLookupError(llvm::Error error,
+ const DebugNames::NameIndex &ni,
+ llvm::StringRef name) {
+ // Ignore SentinelErrors, log everything else.
+ LLDB_LOG_ERROR(
+ LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS),
+ handleErrors(std::move(error), [](const DebugNames::SentinelError &) {}),
+ "Failed to parse index entries for index at {1:x}, name {2}: {0}",
+ ni.getUnitOffset(), name);
+}
+
+void DebugNamesDWARFIndex::GetGlobalVariables(ConstString basename,
+ DIEArray &offsets) {
+ m_fallback.GetGlobalVariables(basename, offsets);
+
+ for (const DebugNames::Entry &entry :
+ m_debug_names_up->equal_range(basename.GetStringRef())) {
+ if (entry.tag() != DW_TAG_variable)
+ continue;
+
+ Append(entry, offsets);
+ }
+}
+
+void DebugNamesDWARFIndex::GetGlobalVariables(const RegularExpression &regex,
+ DIEArray &offsets) {
+ m_fallback.GetGlobalVariables(regex, offsets);
+
+ for (const DebugNames::NameIndex &ni: *m_debug_names_up) {
+ for (DebugNames::NameTableEntry nte: ni) {
+ if (!regex.Execute(nte.getString()))
+ continue;
+
+ uint32_t entry_offset = nte.getEntryOffset();
+ llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);
+ for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {
+ if (entry_or->tag() != DW_TAG_variable)
+ continue;
+
+ Append(*entry_or, offsets);
+ }
+ MaybeLogLookupError(entry_or.takeError(), ni, nte.getString());
+ }
+ }
+}
+
+void DebugNamesDWARFIndex::GetGlobalVariables(const DWARFUnit &cu,
+ DIEArray &offsets) {
+ m_fallback.GetGlobalVariables(cu, offsets);
+
+ uint64_t cu_offset = cu.GetOffset();
+ for (const DebugNames::NameIndex &ni: *m_debug_names_up) {
+ for (DebugNames::NameTableEntry nte: ni) {
+ uint32_t entry_offset = nte.getEntryOffset();
+ llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);
+ for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {
+ if (entry_or->tag() != DW_TAG_variable)
+ continue;
+ if (entry_or->getCUOffset() != cu_offset)
+ continue;
+
+ Append(*entry_or, offsets);
+ }
+ MaybeLogLookupError(entry_or.takeError(), ni, nte.getString());
+ }
+ }
+}
+
+void DebugNamesDWARFIndex::GetCompleteObjCClass(ConstString class_name,
+ bool must_be_implementation,
+ DIEArray &offsets) {
+ m_fallback.GetCompleteObjCClass(class_name, must_be_implementation, offsets);
+
+ // Keep a list of incomplete types as fallback for when we don't find the
+ // complete type.
+ DIEArray incomplete_types;
+
+ for (const DebugNames::Entry &entry :
+ m_debug_names_up->equal_range(class_name.GetStringRef())) {
+ if (entry.tag() != DW_TAG_structure_type &&
+ entry.tag() != DW_TAG_class_type)
+ continue;
+
+ llvm::Optional<DIERef> ref = ToDIERef(entry);
+ if (!ref)
+ continue;
+
+ DWARFUnit *cu = m_debug_info.GetUnit(*ref);
+ if (!cu || !cu->Supports_DW_AT_APPLE_objc_complete_type()) {
+ incomplete_types.push_back(*ref);
+ continue;
+ }
+
+ // FIXME: We should return DWARFDIEs so we don't have to resolve it twice.
+ DWARFDIE die = m_debug_info.GetDIE(*ref);
+ if (!die)
+ continue;
+
+ if (die.GetAttributeValueAsUnsigned(DW_AT_APPLE_objc_complete_type, 0)) {
+ // If we find the complete version we're done.
+ offsets.push_back(*ref);
+ return;
+ } else {
+ incomplete_types.push_back(*ref);
+ }
+ }
+
+ offsets.insert(offsets.end(), incomplete_types.begin(),
+ incomplete_types.end());
+}
+
+void DebugNamesDWARFIndex::GetTypes(ConstString name, DIEArray &offsets) {
+ m_fallback.GetTypes(name, offsets);
+
+ for (const DebugNames::Entry &entry :
+ m_debug_names_up->equal_range(name.GetStringRef())) {
+ if (isType(entry.tag()))
+ Append(entry, offsets);
+ }
+}
+
+void DebugNamesDWARFIndex::GetTypes(const DWARFDeclContext &context,
+ DIEArray &offsets) {
+ m_fallback.GetTypes(context, offsets);
+
+ for (const DebugNames::Entry &entry :
+ m_debug_names_up->equal_range(context[0].name)) {
+ if (entry.tag() == context[0].tag)
+ Append(entry, offsets);
+ }
+}
+
+void DebugNamesDWARFIndex::GetNamespaces(ConstString name, DIEArray &offsets) {
+ m_fallback.GetNamespaces(name, offsets);
+
+ for (const DebugNames::Entry &entry :
+ m_debug_names_up->equal_range(name.GetStringRef())) {
+ if (entry.tag() == DW_TAG_namespace)
+ Append(entry, offsets);
+ }
+}
+
+void DebugNamesDWARFIndex::GetFunctions(
+ ConstString name, SymbolFileDWARF &dwarf,
+ const CompilerDeclContext &parent_decl_ctx, uint32_t name_type_mask,
+ std::vector<DWARFDIE> &dies) {
+
+ std::vector<DWARFDIE> v;
+ m_fallback.GetFunctions(name, dwarf, parent_decl_ctx, name_type_mask, v);
+
+ for (const DebugNames::Entry &entry :
+ m_debug_names_up->equal_range(name.GetStringRef())) {
+ Tag tag = entry.tag();
+ if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine)
+ continue;
+
+ if (llvm::Optional<DIERef> ref = ToDIERef(entry))
+ ProcessFunctionDIE(name.GetStringRef(), *ref, dwarf, parent_decl_ctx,
+ name_type_mask, v);
+ }
+
+ std::set<DWARFDebugInfoEntry *> seen;
+ for (DWARFDIE die : v)
+ if (seen.insert(die.GetDIE()).second)
+ dies.push_back(die);
+}
+
+void DebugNamesDWARFIndex::GetFunctions(const RegularExpression &regex,
+ DIEArray &offsets) {
+ m_fallback.GetFunctions(regex, offsets);
+
+ for (const DebugNames::NameIndex &ni: *m_debug_names_up) {
+ for (DebugNames::NameTableEntry nte: ni) {
+ if (!regex.Execute(nte.getString()))
+ continue;
+
+ uint32_t entry_offset = nte.getEntryOffset();
+ llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);
+ for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {
+ Tag tag = entry_or->tag();
+ if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine)
+ continue;
+
+ Append(*entry_or, offsets);
+ }
+ MaybeLogLookupError(entry_or.takeError(), ni, nte.getString());
+ }
+ }
+}
+
+void DebugNamesDWARFIndex::Dump(Stream &s) {
+ m_fallback.Dump(s);
+
+ std::string data;
+ llvm::raw_string_ostream os(data);
+ m_debug_names_up->dump(os);
+ s.PutCString(os.str());
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h
new file mode 100644
index 000000000000..dca25496373f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h
@@ -0,0 +1,81 @@
+//===-- DebugNamesDWARFIndex.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_DEBUGNAMESDWARFINDEX_H
+#define LLDB_DEBUGNAMESDWARFINDEX_H
+
+#include "Plugins/SymbolFile/DWARF/DWARFIndex.h"
+#include "Plugins/SymbolFile/DWARF/LogChannelDWARF.h"
+#include "Plugins/SymbolFile/DWARF/ManualDWARFIndex.h"
+#include "lldb/Utility/ConstString.h"
+#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h"
+
+namespace lldb_private {
+class DebugNamesDWARFIndex : public DWARFIndex {
+public:
+ static llvm::Expected<std::unique_ptr<DebugNamesDWARFIndex>>
+ Create(Module &module, DWARFDataExtractor debug_names,
+ DWARFDataExtractor debug_str, DWARFDebugInfo *debug_info);
+
+ void Preload() override { m_fallback.Preload(); }
+
+ void GetGlobalVariables(ConstString basename, DIEArray &offsets) override;
+ void GetGlobalVariables(const RegularExpression &regex,
+ DIEArray &offsets) override;
+ void GetGlobalVariables(const DWARFUnit &cu, DIEArray &offsets) override;
+ void GetObjCMethods(ConstString class_name, DIEArray &offsets) override {}
+ void GetCompleteObjCClass(ConstString class_name, bool must_be_implementation,
+ DIEArray &offsets) override;
+ void GetTypes(ConstString name, DIEArray &offsets) override;
+ void GetTypes(const DWARFDeclContext &context, DIEArray &offsets) override;
+ void GetNamespaces(ConstString name, DIEArray &offsets) override;
+ void GetFunctions(ConstString name, SymbolFileDWARF &dwarf,
+ const CompilerDeclContext &parent_decl_ctx,
+ uint32_t name_type_mask,
+ std::vector<DWARFDIE> &dies) override;
+ void GetFunctions(const RegularExpression &regex,
+ DIEArray &offsets) override;
+
+ void ReportInvalidDIERef(const DIERef &ref, llvm::StringRef name) override {}
+ void Dump(Stream &s) override;
+
+private:
+ DebugNamesDWARFIndex(Module &module,
+ std::unique_ptr<llvm::DWARFDebugNames> debug_names_up,
+ DWARFDataExtractor debug_names_data,
+ DWARFDataExtractor debug_str_data,
+ DWARFDebugInfo &debug_info)
+ : DWARFIndex(module), m_debug_info(debug_info),
+ m_debug_names_data(debug_names_data), m_debug_str_data(debug_str_data),
+ m_debug_names_up(std::move(debug_names_up)),
+ m_fallback(module, &debug_info, GetUnits(*m_debug_names_up)) {}
+
+ DWARFDebugInfo &m_debug_info;
+
+ // LLVM DWARFDebugNames will hold a non-owning reference to this data, so keep
+ // track of the ownership here.
+ DWARFDataExtractor m_debug_names_data;
+ DWARFDataExtractor m_debug_str_data;
+
+ using DebugNames = llvm::DWARFDebugNames;
+ std::unique_ptr<DebugNames> m_debug_names_up;
+ ManualDWARFIndex m_fallback;
+
+ llvm::Optional<DIERef> ToDIERef(const DebugNames::Entry &entry);
+ void Append(const DebugNames::Entry &entry, DIEArray &offsets);
+
+ static void MaybeLogLookupError(llvm::Error error,
+ const DebugNames::NameIndex &ni,
+ llvm::StringRef name);
+
+ static llvm::DenseSet<dw_offset_t> GetUnits(const DebugNames &debug_names);
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_DEBUGNAMESDWARFINDEX_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.cpp
new file mode 100644
index 000000000000..62b0ad37a9fc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.cpp
@@ -0,0 +1,583 @@
+//===-- HashedNameToDIE.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 "HashedNameToDIE.h"
+#include "llvm/ADT/StringRef.h"
+
+void DWARFMappedHash::ExtractDIEArray(const DIEInfoArray &die_info_array,
+ DIEArray &die_offsets) {
+ const size_t count = die_info_array.size();
+ for (size_t i = 0; i < count; ++i)
+ die_offsets.emplace_back(die_info_array[i]);
+}
+
+void DWARFMappedHash::ExtractDIEArray(const DIEInfoArray &die_info_array,
+ const dw_tag_t tag,
+ DIEArray &die_offsets) {
+ if (tag == 0) {
+ ExtractDIEArray(die_info_array, die_offsets);
+ } else {
+ const size_t count = die_info_array.size();
+ for (size_t i = 0; i < count; ++i) {
+ const dw_tag_t die_tag = die_info_array[i].tag;
+ bool tag_matches = die_tag == 0 || tag == die_tag;
+ if (!tag_matches) {
+ if (die_tag == DW_TAG_class_type || die_tag == DW_TAG_structure_type)
+ tag_matches =
+ tag == DW_TAG_structure_type || tag == DW_TAG_class_type;
+ }
+ if (tag_matches)
+ die_offsets.emplace_back(die_info_array[i]);
+ }
+ }
+}
+
+void DWARFMappedHash::ExtractDIEArray(const DIEInfoArray &die_info_array,
+ const dw_tag_t tag,
+ const uint32_t qualified_name_hash,
+ DIEArray &die_offsets) {
+ if (tag == 0) {
+ ExtractDIEArray(die_info_array, die_offsets);
+ } else {
+ const size_t count = die_info_array.size();
+ for (size_t i = 0; i < count; ++i) {
+ if (qualified_name_hash != die_info_array[i].qualified_name_hash)
+ continue;
+ const dw_tag_t die_tag = die_info_array[i].tag;
+ bool tag_matches = die_tag == 0 || tag == die_tag;
+ if (!tag_matches) {
+ if (die_tag == DW_TAG_class_type || die_tag == DW_TAG_structure_type)
+ tag_matches =
+ tag == DW_TAG_structure_type || tag == DW_TAG_class_type;
+ }
+ if (tag_matches)
+ die_offsets.emplace_back(die_info_array[i]);
+ }
+ }
+}
+
+void DWARFMappedHash::ExtractClassOrStructDIEArray(
+ const DIEInfoArray &die_info_array,
+ bool return_implementation_only_if_available, DIEArray &die_offsets) {
+ const size_t count = die_info_array.size();
+ for (size_t i = 0; i < count; ++i) {
+ const dw_tag_t die_tag = die_info_array[i].tag;
+ if (die_tag == 0 || die_tag == DW_TAG_class_type ||
+ die_tag == DW_TAG_structure_type) {
+ if (die_info_array[i].type_flags & eTypeFlagClassIsImplementation) {
+ if (return_implementation_only_if_available) {
+ // We found the one true definition for this class, so only return
+ // that
+ die_offsets.clear();
+ die_offsets.emplace_back(die_info_array[i]);
+ return;
+ } else {
+ // Put the one true definition as the first entry so it matches first
+ die_offsets.emplace(die_offsets.begin(), die_info_array[i]);
+ }
+ } else {
+ die_offsets.emplace_back(die_info_array[i]);
+ }
+ }
+ }
+}
+
+void DWARFMappedHash::ExtractTypesFromDIEArray(
+ const DIEInfoArray &die_info_array, uint32_t type_flag_mask,
+ uint32_t type_flag_value, DIEArray &die_offsets) {
+ const size_t count = die_info_array.size();
+ for (size_t i = 0; i < count; ++i) {
+ if ((die_info_array[i].type_flags & type_flag_mask) == type_flag_value)
+ die_offsets.emplace_back(die_info_array[i]);
+ }
+}
+
+const char *DWARFMappedHash::GetAtomTypeName(uint16_t atom) {
+ switch (atom) {
+ case eAtomTypeNULL:
+ return "NULL";
+ case eAtomTypeDIEOffset:
+ return "die-offset";
+ case eAtomTypeCUOffset:
+ return "cu-offset";
+ case eAtomTypeTag:
+ return "die-tag";
+ case eAtomTypeNameFlags:
+ return "name-flags";
+ case eAtomTypeTypeFlags:
+ return "type-flags";
+ case eAtomTypeQualNameHash:
+ return "qualified-name-hash";
+ }
+ return "<invalid>";
+}
+
+DWARFMappedHash::DIEInfo::DIEInfo(dw_offset_t o, dw_tag_t t, uint32_t f,
+ uint32_t h)
+ : die_offset(o), tag(t), type_flags(f), qualified_name_hash(h) {}
+
+DWARFMappedHash::Prologue::Prologue(dw_offset_t _die_base_offset)
+ : die_base_offset(_die_base_offset), atoms(), atom_mask(0),
+ min_hash_data_byte_size(0), hash_data_has_fixed_byte_size(true) {
+ // Define an array of DIE offsets by first defining an array, and then define
+ // the atom type for the array, in this case we have an array of DIE offsets
+ AppendAtom(eAtomTypeDIEOffset, DW_FORM_data4);
+}
+
+void DWARFMappedHash::Prologue::ClearAtoms() {
+ hash_data_has_fixed_byte_size = true;
+ min_hash_data_byte_size = 0;
+ atom_mask = 0;
+ atoms.clear();
+}
+
+bool DWARFMappedHash::Prologue::ContainsAtom(AtomType atom_type) const {
+ return (atom_mask & (1u << atom_type)) != 0;
+}
+
+void DWARFMappedHash::Prologue::Clear() {
+ die_base_offset = 0;
+ ClearAtoms();
+}
+
+void DWARFMappedHash::Prologue::AppendAtom(AtomType type, dw_form_t form) {
+ atoms.push_back({type, form});
+ atom_mask |= 1u << type;
+ switch (form) {
+ case DW_FORM_indirect:
+ case DW_FORM_exprloc:
+ case DW_FORM_flag_present:
+ case DW_FORM_ref_sig8:
+ llvm_unreachable("Unhandled atom form");
+
+ case DW_FORM_addrx:
+ case DW_FORM_string:
+ case DW_FORM_block:
+ case DW_FORM_block1:
+ case DW_FORM_sdata:
+ case DW_FORM_udata:
+ case DW_FORM_ref_udata:
+ case DW_FORM_GNU_addr_index:
+ case DW_FORM_GNU_str_index:
+ hash_data_has_fixed_byte_size = false;
+ LLVM_FALLTHROUGH;
+ case DW_FORM_flag:
+ case DW_FORM_data1:
+ case DW_FORM_ref1:
+ case DW_FORM_sec_offset:
+ min_hash_data_byte_size += 1;
+ break;
+
+ case DW_FORM_block2:
+ hash_data_has_fixed_byte_size = false;
+ LLVM_FALLTHROUGH;
+ case DW_FORM_data2:
+ case DW_FORM_ref2:
+ min_hash_data_byte_size += 2;
+ break;
+
+ case DW_FORM_block4:
+ hash_data_has_fixed_byte_size = false;
+ LLVM_FALLTHROUGH;
+ case DW_FORM_data4:
+ case DW_FORM_ref4:
+ case DW_FORM_addr:
+ case DW_FORM_ref_addr:
+ case DW_FORM_strp:
+ min_hash_data_byte_size += 4;
+ break;
+
+ case DW_FORM_data8:
+ case DW_FORM_ref8:
+ min_hash_data_byte_size += 8;
+ break;
+ }
+}
+
+lldb::offset_t
+DWARFMappedHash::Prologue::Read(const lldb_private::DataExtractor &data,
+ lldb::offset_t offset) {
+ ClearAtoms();
+
+ die_base_offset = data.GetU32(&offset);
+
+ const uint32_t atom_count = data.GetU32(&offset);
+ if (atom_count == 0x00060003u) {
+ // Old format, deal with contents of old pre-release format
+ while (data.GetU32(&offset))
+ /* do nothing */;
+
+ // Hardcode to the only known value for now.
+ AppendAtom(eAtomTypeDIEOffset, DW_FORM_data4);
+ } else {
+ for (uint32_t i = 0; i < atom_count; ++i) {
+ AtomType type = (AtomType)data.GetU16(&offset);
+ dw_form_t form = (dw_form_t)data.GetU16(&offset);
+ AppendAtom(type, form);
+ }
+ }
+ return offset;
+}
+
+size_t DWARFMappedHash::Prologue::GetByteSize() const {
+ // Add an extra count to the atoms size for the zero termination Atom that
+ // gets written to disk
+ return sizeof(die_base_offset) + sizeof(uint32_t) +
+ atoms.size() * sizeof(Atom);
+}
+
+size_t DWARFMappedHash::Prologue::GetMinimumHashDataByteSize() const {
+ return min_hash_data_byte_size;
+}
+
+bool DWARFMappedHash::Prologue::HashDataHasFixedByteSize() const {
+ return hash_data_has_fixed_byte_size;
+}
+
+size_t DWARFMappedHash::Header::GetByteSize(const HeaderData &header_data) {
+ return header_data.GetByteSize();
+}
+
+lldb::offset_t DWARFMappedHash::Header::Read(lldb_private::DataExtractor &data,
+ lldb::offset_t offset) {
+ offset = MappedHash::Header<Prologue>::Read(data, offset);
+ if (offset != UINT32_MAX) {
+ offset = header_data.Read(data, offset);
+ }
+ return offset;
+}
+
+bool DWARFMappedHash::Header::Read(const lldb_private::DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr,
+ DIEInfo &hash_data) const {
+ const size_t num_atoms = header_data.atoms.size();
+ if (num_atoms == 0)
+ return false;
+
+ for (size_t i = 0; i < num_atoms; ++i) {
+ DWARFFormValue form_value(nullptr, header_data.atoms[i].form);
+
+ if (!form_value.ExtractValue(data, offset_ptr))
+ return false;
+
+ switch (header_data.atoms[i].type) {
+ case eAtomTypeDIEOffset: // DIE offset, check form for encoding
+ hash_data.die_offset =
+ DWARFFormValue::IsDataForm(form_value.Form())
+ ? form_value.Unsigned()
+ : form_value.Reference(header_data.die_base_offset);
+ break;
+
+ case eAtomTypeTag: // DW_TAG value for the DIE
+ hash_data.tag = (dw_tag_t)form_value.Unsigned();
+ break;
+
+ case eAtomTypeTypeFlags: // Flags from enum TypeFlags
+ hash_data.type_flags = (uint32_t)form_value.Unsigned();
+ break;
+
+ case eAtomTypeQualNameHash: // Flags from enum TypeFlags
+ hash_data.qualified_name_hash = form_value.Unsigned();
+ break;
+
+ default:
+ // We can always skip atoms we don't know about
+ break;
+ }
+ }
+ return hash_data.die_offset != DW_INVALID_OFFSET;
+}
+
+DWARFMappedHash::MemoryTable::MemoryTable(
+ lldb_private::DWARFDataExtractor &table_data,
+ const lldb_private::DWARFDataExtractor &string_table, const char *name)
+ : MappedHash::MemoryTable<uint32_t, Header, DIEInfoArray>(table_data),
+ m_data(table_data), m_string_table(string_table), m_name(name) {}
+
+const char *
+DWARFMappedHash::MemoryTable::GetStringForKeyType(KeyType key) const {
+ // The key in the DWARF table is the .debug_str offset for the string
+ return m_string_table.PeekCStr(key);
+}
+
+bool DWARFMappedHash::MemoryTable::ReadHashData(uint32_t hash_data_offset,
+ HashData &hash_data) const {
+ lldb::offset_t offset = hash_data_offset;
+ offset += 4; // Skip string table offset that contains offset of hash name in
+ // .debug_str
+ const uint32_t count = m_data.GetU32(&offset);
+ if (count > 0) {
+ hash_data.resize(count);
+ for (uint32_t i = 0; i < count; ++i) {
+ if (!m_header.Read(m_data, &offset, hash_data[i]))
+ return false;
+ }
+ } else
+ hash_data.clear();
+ return true;
+}
+
+DWARFMappedHash::MemoryTable::Result
+DWARFMappedHash::MemoryTable::GetHashDataForName(
+ llvm::StringRef name, lldb::offset_t *hash_data_offset_ptr,
+ Pair &pair) const {
+ pair.key = m_data.GetU32(hash_data_offset_ptr);
+ pair.value.clear();
+
+ // If the key is zero, this terminates our chain of HashData objects for this
+ // hash value.
+ if (pair.key == 0)
+ return eResultEndOfHashData;
+
+ // There definitely should be a string for this string offset, if there
+ // isn't, there is something wrong, return and error
+ const char *strp_cstr = m_string_table.PeekCStr(pair.key);
+ if (strp_cstr == nullptr) {
+ *hash_data_offset_ptr = UINT32_MAX;
+ return eResultError;
+ }
+
+ const uint32_t count = m_data.GetU32(hash_data_offset_ptr);
+ const size_t min_total_hash_data_size =
+ count * m_header.header_data.GetMinimumHashDataByteSize();
+ if (count > 0 &&
+ m_data.ValidOffsetForDataOfSize(*hash_data_offset_ptr,
+ min_total_hash_data_size)) {
+ // We have at least one HashData entry, and we have enough data to parse at
+ // least "count" HashData entries.
+
+ // First make sure the entire C string matches...
+ const bool match = name == strp_cstr;
+
+ if (!match && m_header.header_data.HashDataHasFixedByteSize()) {
+ // If the string doesn't match and we have fixed size data, we can just
+ // add the total byte size of all HashData objects to the hash data
+ // offset and be done...
+ *hash_data_offset_ptr += min_total_hash_data_size;
+ } else {
+ // If the string does match, or we don't have fixed size data then we
+ // need to read the hash data as a stream. If the string matches we also
+ // append all HashData objects to the value array.
+ for (uint32_t i = 0; i < count; ++i) {
+ DIEInfo die_info;
+ if (m_header.Read(m_data, hash_data_offset_ptr, die_info)) {
+ // Only happened if the HashData of the string matched...
+ if (match)
+ pair.value.push_back(die_info);
+ } else {
+ // Something went wrong while reading the data
+ *hash_data_offset_ptr = UINT32_MAX;
+ return eResultError;
+ }
+ }
+ }
+ // Return the correct response depending on if the string matched or not...
+ if (match)
+ return eResultKeyMatch; // The key (cstring) matches and we have lookup
+ // results!
+ else
+ return eResultKeyMismatch; // The key doesn't match, this function will
+ // get called
+ // again for the next key/value or the key terminator which in our case is
+ // a zero .debug_str offset.
+ } else {
+ *hash_data_offset_ptr = UINT32_MAX;
+ return eResultError;
+ }
+}
+
+DWARFMappedHash::MemoryTable::Result
+DWARFMappedHash::MemoryTable::AppendHashDataForRegularExpression(
+ const lldb_private::RegularExpression &regex,
+ lldb::offset_t *hash_data_offset_ptr, Pair &pair) const {
+ pair.key = m_data.GetU32(hash_data_offset_ptr);
+ // If the key is zero, this terminates our chain of HashData objects for this
+ // hash value.
+ if (pair.key == 0)
+ return eResultEndOfHashData;
+
+ // There definitely should be a string for this string offset, if there
+ // isn't, there is something wrong, return and error
+ const char *strp_cstr = m_string_table.PeekCStr(pair.key);
+ if (strp_cstr == nullptr)
+ return eResultError;
+
+ const uint32_t count = m_data.GetU32(hash_data_offset_ptr);
+ const size_t min_total_hash_data_size =
+ count * m_header.header_data.GetMinimumHashDataByteSize();
+ if (count > 0 &&
+ m_data.ValidOffsetForDataOfSize(*hash_data_offset_ptr,
+ min_total_hash_data_size)) {
+ const bool match = regex.Execute(llvm::StringRef(strp_cstr));
+
+ if (!match && m_header.header_data.HashDataHasFixedByteSize()) {
+ // If the regex doesn't match and we have fixed size data, we can just
+ // add the total byte size of all HashData objects to the hash data
+ // offset and be done...
+ *hash_data_offset_ptr += min_total_hash_data_size;
+ } else {
+ // If the string does match, or we don't have fixed size data then we
+ // need to read the hash data as a stream. If the string matches we also
+ // append all HashData objects to the value array.
+ for (uint32_t i = 0; i < count; ++i) {
+ DIEInfo die_info;
+ if (m_header.Read(m_data, hash_data_offset_ptr, die_info)) {
+ // Only happened if the HashData of the string matched...
+ if (match)
+ pair.value.push_back(die_info);
+ } else {
+ // Something went wrong while reading the data
+ *hash_data_offset_ptr = UINT32_MAX;
+ return eResultError;
+ }
+ }
+ }
+ // Return the correct response depending on if the string matched or not...
+ if (match)
+ return eResultKeyMatch; // The key (cstring) matches and we have lookup
+ // results!
+ else
+ return eResultKeyMismatch; // The key doesn't match, this function will
+ // get called
+ // again for the next key/value or the key terminator which in our case is
+ // a zero .debug_str offset.
+ } else {
+ *hash_data_offset_ptr = UINT32_MAX;
+ return eResultError;
+ }
+}
+
+size_t DWARFMappedHash::MemoryTable::AppendAllDIEsThatMatchingRegex(
+ const lldb_private::RegularExpression &regex,
+ DIEInfoArray &die_info_array) const {
+ const uint32_t hash_count = m_header.hashes_count;
+ Pair pair;
+ for (uint32_t offset_idx = 0; offset_idx < hash_count; ++offset_idx) {
+ lldb::offset_t hash_data_offset = GetHashDataOffset(offset_idx);
+ while (hash_data_offset != UINT32_MAX) {
+ const lldb::offset_t prev_hash_data_offset = hash_data_offset;
+ Result hash_result =
+ AppendHashDataForRegularExpression(regex, &hash_data_offset, pair);
+ if (prev_hash_data_offset == hash_data_offset)
+ break;
+
+ // Check the result of getting our hash data
+ switch (hash_result) {
+ case eResultKeyMatch:
+ case eResultKeyMismatch:
+ // Whether we matches or not, it doesn't matter, we keep looking.
+ break;
+
+ case eResultEndOfHashData:
+ case eResultError:
+ hash_data_offset = UINT32_MAX;
+ break;
+ }
+ }
+ }
+ die_info_array.swap(pair.value);
+ return die_info_array.size();
+}
+
+size_t DWARFMappedHash::MemoryTable::AppendAllDIEsInRange(
+ const uint32_t die_offset_start, const uint32_t die_offset_end,
+ DIEInfoArray &die_info_array) const {
+ const uint32_t hash_count = m_header.hashes_count;
+ for (uint32_t offset_idx = 0; offset_idx < hash_count; ++offset_idx) {
+ bool done = false;
+ lldb::offset_t hash_data_offset = GetHashDataOffset(offset_idx);
+ while (!done && hash_data_offset != UINT32_MAX) {
+ KeyType key = m_data.GetU32(&hash_data_offset);
+ // If the key is zero, this terminates our chain of HashData objects for
+ // this hash value.
+ if (key == 0)
+ break;
+
+ const uint32_t count = m_data.GetU32(&hash_data_offset);
+ for (uint32_t i = 0; i < count; ++i) {
+ DIEInfo die_info;
+ if (m_header.Read(m_data, &hash_data_offset, die_info)) {
+ if (die_info.die_offset == 0)
+ done = true;
+ if (die_offset_start <= die_info.die_offset &&
+ die_info.die_offset < die_offset_end)
+ die_info_array.push_back(die_info);
+ }
+ }
+ }
+ }
+ return die_info_array.size();
+}
+
+size_t DWARFMappedHash::MemoryTable::FindByName(llvm::StringRef name,
+ DIEArray &die_offsets) {
+ if (name.empty())
+ return 0;
+
+ DIEInfoArray die_info_array;
+ if (FindByName(name, die_info_array))
+ DWARFMappedHash::ExtractDIEArray(die_info_array, die_offsets);
+ return die_info_array.size();
+}
+
+size_t DWARFMappedHash::MemoryTable::FindByNameAndTag(llvm::StringRef name,
+ const dw_tag_t tag,
+ DIEArray &die_offsets) {
+ DIEInfoArray die_info_array;
+ if (FindByName(name, die_info_array))
+ DWARFMappedHash::ExtractDIEArray(die_info_array, tag, die_offsets);
+ return die_info_array.size();
+}
+
+size_t DWARFMappedHash::MemoryTable::FindByNameAndTagAndQualifiedNameHash(
+ llvm::StringRef name, const dw_tag_t tag,
+ const uint32_t qualified_name_hash, DIEArray &die_offsets) {
+ DIEInfoArray die_info_array;
+ if (FindByName(name, die_info_array))
+ DWARFMappedHash::ExtractDIEArray(die_info_array, tag, qualified_name_hash,
+ die_offsets);
+ return die_info_array.size();
+}
+
+size_t DWARFMappedHash::MemoryTable::FindCompleteObjCClassByName(
+ llvm::StringRef name, DIEArray &die_offsets, bool must_be_implementation) {
+ DIEInfoArray die_info_array;
+ if (FindByName(name, die_info_array)) {
+ if (must_be_implementation &&
+ GetHeader().header_data.ContainsAtom(eAtomTypeTypeFlags)) {
+ // If we have two atoms, then we have the DIE offset and the type flags
+ // so we can find the objective C class efficiently.
+ DWARFMappedHash::ExtractTypesFromDIEArray(die_info_array, UINT32_MAX,
+ eTypeFlagClassIsImplementation,
+ die_offsets);
+ } else {
+ // We don't only want the one true definition, so try and see what we can
+ // find, and only return class or struct DIEs. If we do have the full
+ // implementation, then return it alone, else return all possible
+ // matches.
+ const bool return_implementation_only_if_available = true;
+ DWARFMappedHash::ExtractClassOrStructDIEArray(
+ die_info_array, return_implementation_only_if_available, die_offsets);
+ }
+ }
+ return die_offsets.size();
+}
+
+size_t DWARFMappedHash::MemoryTable::FindByName(llvm::StringRef name,
+ DIEInfoArray &die_info_array) {
+ if (name.empty())
+ return 0;
+
+ Pair kv_pair;
+ size_t old_size = die_info_array.size();
+ if (Find(name, kv_pair)) {
+ die_info_array.swap(kv_pair.value);
+ return die_info_array.size() - old_size;
+ }
+ return 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.h
new file mode 100644
index 000000000000..a01612b59528
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.h
@@ -0,0 +1,193 @@
+//===-- HashedNameToDIE.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 SymbolFileDWARF_HashedNameToDIE_h_
+#define SymbolFileDWARF_HashedNameToDIE_h_
+
+#include <vector>
+
+#include "lldb/Core/MappedHash.h"
+#include "lldb/Core/dwarf.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/lldb-defines.h"
+
+#include "DWARFDefines.h"
+#include "DWARFFormValue.h"
+#include "NameToDIE.h"
+
+class DWARFMappedHash {
+public:
+ enum AtomType : uint16_t {
+ eAtomTypeNULL = 0u,
+ eAtomTypeDIEOffset = 1u, // DIE offset, check form for encoding
+ eAtomTypeCUOffset = 2u, // DIE offset of the compiler unit header that
+ // contains the item in question
+ eAtomTypeTag = 3u, // DW_TAG_xxx value, should be encoded as DW_FORM_data1
+ // (if no tags exceed 255) or DW_FORM_data2
+ eAtomTypeNameFlags = 4u, // Flags from enum NameFlags
+ eAtomTypeTypeFlags = 5u, // Flags from enum TypeFlags,
+ eAtomTypeQualNameHash = 6u // A 32 bit hash of the full qualified name
+ // (since all hash entries are basename only)
+ // For example a type like "std::vector<int>::iterator" would have a name of
+ // "iterator"
+ // and a 32 bit hash for "std::vector<int>::iterator" to allow us to not
+ // have to pull
+ // in debug info for a type when we know the fully qualified name.
+ };
+
+ // Bit definitions for the eAtomTypeTypeFlags flags
+ enum TypeFlags {
+ // Always set for C++, only set for ObjC if this is the
+ // @implementation for class
+ eTypeFlagClassIsImplementation = (1u << 1)
+ };
+
+ struct DIEInfo {
+ dw_offset_t die_offset = DW_INVALID_OFFSET;
+ dw_tag_t tag = 0;
+
+ /// Any flags for this DIEInfo
+ uint32_t type_flags = 0;
+
+ /// A 32 bit hash of the fully qualified name
+ uint32_t qualified_name_hash = 0;
+
+ DIEInfo() = default;
+ DIEInfo(dw_offset_t o, dw_tag_t t, uint32_t f, uint32_t h);
+
+ explicit operator DIERef() const {
+ return DIERef(llvm::None, DIERef::Section::DebugInfo, die_offset);
+ }
+ };
+
+ struct Atom {
+ AtomType type;
+ dw_form_t form;
+ };
+
+ typedef std::vector<DIEInfo> DIEInfoArray;
+ typedef std::vector<Atom> AtomArray;
+
+ class Prologue {
+ public:
+ Prologue(dw_offset_t _die_base_offset = 0);
+
+ void ClearAtoms();
+
+ bool ContainsAtom(AtomType atom_type) const;
+
+ void Clear();
+
+ void AppendAtom(AtomType type, dw_form_t form);
+
+ lldb::offset_t Read(const lldb_private::DataExtractor &data,
+ lldb::offset_t offset);
+
+ size_t GetByteSize() const;
+
+ size_t GetMinimumHashDataByteSize() const;
+
+ bool HashDataHasFixedByteSize() const;
+
+ // DIE offset base so die offsets in hash_data can be CU relative
+ dw_offset_t die_base_offset;
+ AtomArray atoms;
+ uint32_t atom_mask;
+ size_t min_hash_data_byte_size;
+ bool hash_data_has_fixed_byte_size;
+ };
+
+ class Header : public MappedHash::Header<Prologue> {
+ public:
+ size_t GetByteSize(const HeaderData &header_data) override;
+
+ lldb::offset_t Read(lldb_private::DataExtractor &data,
+ lldb::offset_t offset) override;
+
+ bool Read(const lldb_private::DWARFDataExtractor &data,
+ lldb::offset_t *offset_ptr, DIEInfo &hash_data) const;
+ };
+
+ // A class for reading and using a saved hash table from a block of data
+ // in memory
+ class MemoryTable
+ : public MappedHash::MemoryTable<uint32_t, DWARFMappedHash::Header,
+ DIEInfoArray> {
+ public:
+ MemoryTable(lldb_private::DWARFDataExtractor &table_data,
+ const lldb_private::DWARFDataExtractor &string_table,
+ const char *name);
+
+ const char *GetStringForKeyType(KeyType key) const override;
+
+ bool ReadHashData(uint32_t hash_data_offset,
+ HashData &hash_data) const override;
+
+ size_t
+ AppendAllDIEsThatMatchingRegex(const lldb_private::RegularExpression &regex,
+ DIEInfoArray &die_info_array) const;
+
+ size_t AppendAllDIEsInRange(const uint32_t die_offset_start,
+ const uint32_t die_offset_end,
+ DIEInfoArray &die_info_array) const;
+
+ size_t FindByName(llvm::StringRef name, DIEArray &die_offsets);
+
+ size_t FindByNameAndTag(llvm::StringRef name, const dw_tag_t tag,
+ DIEArray &die_offsets);
+
+ size_t FindByNameAndTagAndQualifiedNameHash(
+ llvm::StringRef name, const dw_tag_t tag,
+ const uint32_t qualified_name_hash, DIEArray &die_offsets);
+
+ size_t FindCompleteObjCClassByName(llvm::StringRef name,
+ DIEArray &die_offsets,
+ bool must_be_implementation);
+
+ protected:
+ Result AppendHashDataForRegularExpression(
+ const lldb_private::RegularExpression &regex,
+ lldb::offset_t *hash_data_offset_ptr, Pair &pair) const;
+
+ size_t FindByName(llvm::StringRef name, DIEInfoArray &die_info_array);
+
+ Result GetHashDataForName(llvm::StringRef name,
+ lldb::offset_t *hash_data_offset_ptr,
+ Pair &pair) const override;
+
+ lldb_private::DWARFDataExtractor m_data;
+ lldb_private::DWARFDataExtractor m_string_table;
+ std::string m_name;
+ };
+
+ static void ExtractDIEArray(const DIEInfoArray &die_info_array,
+ DIEArray &die_offsets);
+
+protected:
+ static void ExtractDIEArray(const DIEInfoArray &die_info_array,
+ const dw_tag_t tag, DIEArray &die_offsets);
+
+ static void ExtractDIEArray(const DIEInfoArray &die_info_array,
+ const dw_tag_t tag,
+ const uint32_t qualified_name_hash,
+ DIEArray &die_offsets);
+
+ static void
+ ExtractClassOrStructDIEArray(const DIEInfoArray &die_info_array,
+ bool return_implementation_only_if_available,
+ DIEArray &die_offsets);
+
+ static void ExtractTypesFromDIEArray(const DIEInfoArray &die_info_array,
+ uint32_t type_flag_mask,
+ uint32_t type_flag_value,
+ DIEArray &die_offsets);
+
+ static const char *GetAtomTypeName(uint16_t atom);
+};
+
+#endif // SymbolFileDWARF_HashedNameToDIE_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.cpp
new file mode 100644
index 000000000000..8495016d4280
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.cpp
@@ -0,0 +1,33 @@
+//===-- LogChannelDWARF.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 "LogChannelDWARF.h"
+
+using namespace lldb_private;
+
+static constexpr Log::Category g_categories[] = {
+ {{"comp"},
+ {"log insertions of object files into DWARF debug maps"},
+ DWARF_LOG_TYPE_COMPLETION},
+ {{"info"}, {"log the parsing of .debug_info"}, DWARF_LOG_DEBUG_INFO},
+ {{"line"}, {"log the parsing of .debug_line"}, DWARF_LOG_DEBUG_LINE},
+ {{"lookups"},
+ {"log any lookups that happen by name, regex, or address"},
+ DWARF_LOG_LOOKUPS},
+ {{"map"},
+ {"log struct/unions/class type completions"},
+ DWARF_LOG_DEBUG_MAP},
+};
+
+Log::Channel LogChannelDWARF::g_channel(g_categories, DWARF_LOG_DEFAULT);
+
+void LogChannelDWARF::Initialize() {
+ Log::Register("dwarf", g_channel);
+}
+
+void LogChannelDWARF::Terminate() { Log::Unregister("dwarf"); }
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.h
new file mode 100644
index 000000000000..a89c686735d2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.h
@@ -0,0 +1,35 @@
+//===-- LogChannelDWARF.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 SymbolFileDWARF_LogChannelDWARF_h_
+#define SymbolFileDWARF_LogChannelDWARF_h_
+
+#include "lldb/Utility/Log.h"
+
+#define DWARF_LOG_DEBUG_INFO (1u << 1)
+#define DWARF_LOG_DEBUG_LINE (1u << 2)
+#define DWARF_LOG_LOOKUPS (1u << 3)
+#define DWARF_LOG_TYPE_COMPLETION (1u << 4)
+#define DWARF_LOG_DEBUG_MAP (1u << 5)
+#define DWARF_LOG_ALL (UINT32_MAX)
+#define DWARF_LOG_DEFAULT (DWARF_LOG_DEBUG_INFO)
+
+namespace lldb_private {
+class LogChannelDWARF {
+ static Log::Channel g_channel;
+
+public:
+ static void Initialize();
+ static void Terminate();
+
+ static Log *GetLogIfAll(uint32_t mask) { return g_channel.GetLogIfAll(mask); }
+ static Log *GetLogIfAny(uint32_t mask) { return g_channel.GetLogIfAny(mask); }
+};
+}
+
+#endif // SymbolFileDWARF_LogChannelDWARF_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp
new file mode 100644
index 000000000000..aff8b5d8c15f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp
@@ -0,0 +1,476 @@
+//===-- ManualDWARFIndex.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 "Plugins/SymbolFile/DWARF/ManualDWARFIndex.h"
+#include "Plugins/Language/ObjC/ObjCLanguage.h"
+#include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h"
+#include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h"
+#include "Plugins/SymbolFile/DWARF/LogChannelDWARF.h"
+#include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Host/TaskPool.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/Timer.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+void ManualDWARFIndex::Index() {
+ if (!m_debug_info)
+ return;
+
+ DWARFDebugInfo &debug_info = *m_debug_info;
+ m_debug_info = nullptr;
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "%p", static_cast<void *>(&debug_info));
+
+ std::vector<DWARFUnit *> units_to_index;
+ units_to_index.reserve(debug_info.GetNumUnits());
+ for (size_t U = 0; U < debug_info.GetNumUnits(); ++U) {
+ DWARFUnit *unit = debug_info.GetUnitAtIndex(U);
+ if (unit && m_units_to_avoid.count(unit->GetOffset()) == 0)
+ units_to_index.push_back(unit);
+ }
+ if (units_to_index.empty())
+ return;
+
+ std::vector<IndexSet> sets(units_to_index.size());
+
+ // Keep memory down by clearing DIEs for any units if indexing
+ // caused us to load the unit's DIEs.
+ std::vector<llvm::Optional<DWARFUnit::ScopedExtractDIEs>> clear_cu_dies(
+ units_to_index.size());
+ auto parser_fn = [&](size_t cu_idx) {
+ IndexUnit(*units_to_index[cu_idx], sets[cu_idx]);
+ };
+
+ auto extract_fn = [&units_to_index, &clear_cu_dies](size_t cu_idx) {
+ clear_cu_dies[cu_idx] = units_to_index[cu_idx]->ExtractDIEsScoped();
+ };
+
+ // Create a task runner that extracts dies for each DWARF unit in a
+ // separate thread
+ // First figure out which units didn't have their DIEs already
+ // parsed and remember this. If no DIEs were parsed prior to this index
+ // function call, we are going to want to clear the CU dies after we are
+ // done indexing to make sure we don't pull in all DWARF dies, but we need
+ // to wait until all units have been indexed in case a DIE in one
+ // unit refers to another and the indexes accesses those DIEs.
+ TaskMapOverInt(0, units_to_index.size(), extract_fn);
+
+ // Now create a task runner that can index each DWARF unit in a
+ // separate thread so we can index quickly.
+
+ TaskMapOverInt(0, units_to_index.size(), parser_fn);
+
+ auto finalize_fn = [this, &sets](NameToDIE(IndexSet::*index)) {
+ NameToDIE &result = m_set.*index;
+ for (auto &set : sets)
+ result.Append(set.*index);
+ result.Finalize();
+ };
+
+ TaskPool::RunTasks([&]() { finalize_fn(&IndexSet::function_basenames); },
+ [&]() { finalize_fn(&IndexSet::function_fullnames); },
+ [&]() { finalize_fn(&IndexSet::function_methods); },
+ [&]() { finalize_fn(&IndexSet::function_selectors); },
+ [&]() { finalize_fn(&IndexSet::objc_class_selectors); },
+ [&]() { finalize_fn(&IndexSet::globals); },
+ [&]() { finalize_fn(&IndexSet::types); },
+ [&]() { finalize_fn(&IndexSet::namespaces); });
+}
+
+void ManualDWARFIndex::IndexUnit(DWARFUnit &unit, IndexSet &set) {
+ assert(
+ !unit.GetSymbolFileDWARF().GetBaseCompileUnit() &&
+ "DWARFUnit associated with .dwo or .dwp should not be indexed directly");
+
+ Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS);
+
+ if (log) {
+ m_module.LogMessage(
+ log, "ManualDWARFIndex::IndexUnit for unit at .debug_info[0x%8.8x]",
+ unit.GetOffset());
+ }
+
+ const LanguageType cu_language = unit.GetLanguageType();
+
+ IndexUnitImpl(unit, cu_language, set);
+
+ if (SymbolFileDWARFDwo *dwo_symbol_file = unit.GetDwoSymbolFile()) {
+ DWARFDebugInfo &dwo_info = *dwo_symbol_file->DebugInfo();
+ for (size_t i = 0; i < dwo_info.GetNumUnits(); ++i)
+ IndexUnitImpl(*dwo_info.GetUnitAtIndex(i), cu_language, set);
+ }
+}
+
+void ManualDWARFIndex::IndexUnitImpl(DWARFUnit &unit,
+ const LanguageType cu_language,
+ IndexSet &set) {
+ for (const DWARFDebugInfoEntry &die : unit.dies()) {
+ const dw_tag_t tag = die.Tag();
+
+ switch (tag) {
+ case DW_TAG_array_type:
+ case DW_TAG_base_type:
+ case DW_TAG_class_type:
+ case DW_TAG_constant:
+ case DW_TAG_enumeration_type:
+ case DW_TAG_inlined_subroutine:
+ case DW_TAG_namespace:
+ case DW_TAG_string_type:
+ case DW_TAG_structure_type:
+ case DW_TAG_subprogram:
+ case DW_TAG_subroutine_type:
+ case DW_TAG_typedef:
+ case DW_TAG_union_type:
+ case DW_TAG_unspecified_type:
+ case DW_TAG_variable:
+ break;
+
+ default:
+ continue;
+ }
+
+ DWARFAttributes attributes;
+ const char *name = nullptr;
+ const char *mangled_cstr = nullptr;
+ bool is_declaration = false;
+ // bool is_artificial = false;
+ bool has_address = false;
+ bool has_location_or_const_value = false;
+ bool is_global_or_static_variable = false;
+
+ DWARFFormValue specification_die_form;
+ const size_t num_attributes = die.GetAttributes(&unit, attributes);
+ if (num_attributes > 0) {
+ for (uint32_t i = 0; i < num_attributes; ++i) {
+ dw_attr_t attr = attributes.AttributeAtIndex(i);
+ DWARFFormValue form_value;
+ switch (attr) {
+ case DW_AT_name:
+ if (attributes.ExtractFormValueAtIndex(i, form_value))
+ name = form_value.AsCString();
+ break;
+
+ case DW_AT_declaration:
+ if (attributes.ExtractFormValueAtIndex(i, form_value))
+ is_declaration = form_value.Unsigned() != 0;
+ break;
+
+ // case DW_AT_artificial:
+ // if (attributes.ExtractFormValueAtIndex(i,
+ // form_value))
+ // is_artificial = form_value.Unsigned() != 0;
+ // break;
+
+ case DW_AT_MIPS_linkage_name:
+ case DW_AT_linkage_name:
+ if (attributes.ExtractFormValueAtIndex(i, form_value))
+ mangled_cstr = form_value.AsCString();
+ break;
+
+ case DW_AT_low_pc:
+ case DW_AT_high_pc:
+ case DW_AT_ranges:
+ has_address = true;
+ break;
+
+ case DW_AT_entry_pc:
+ has_address = true;
+ break;
+
+ case DW_AT_location:
+ case DW_AT_const_value:
+ has_location_or_const_value = true;
+ if (tag == DW_TAG_variable) {
+ const DWARFDebugInfoEntry *parent_die = die.GetParent();
+ while (parent_die != nullptr) {
+ switch (parent_die->Tag()) {
+ case DW_TAG_subprogram:
+ case DW_TAG_lexical_block:
+ case DW_TAG_inlined_subroutine:
+ // Even if this is a function level static, we don't add it. We
+ // could theoretically add these if we wanted to by
+ // introspecting into the DW_AT_location and seeing if the
+ // location describes a hard coded address, but we don't want
+ // the performance penalty of that right now.
+ is_global_or_static_variable = false;
+ // if (attributes.ExtractFormValueAtIndex(dwarf, i,
+ // form_value)) {
+ // // If we have valid block data, then we have location
+ // // expression bytesthat are fixed (not a location list).
+ // const uint8_t *block_data = form_value.BlockData();
+ // if (block_data) {
+ // uint32_t block_length = form_value.Unsigned();
+ // if (block_length == 1 +
+ // attributes.UnitAtIndex(i)->GetAddressByteSize()) {
+ // if (block_data[0] == DW_OP_addr)
+ // add_die = true;
+ // }
+ // }
+ // }
+ parent_die = nullptr; // Terminate the while loop.
+ break;
+
+ case DW_TAG_compile_unit:
+ case DW_TAG_partial_unit:
+ is_global_or_static_variable = true;
+ parent_die = nullptr; // Terminate the while loop.
+ break;
+
+ default:
+ parent_die =
+ parent_die->GetParent(); // Keep going in the while loop.
+ break;
+ }
+ }
+ }
+ break;
+
+ case DW_AT_specification:
+ if (attributes.ExtractFormValueAtIndex(i, form_value))
+ specification_die_form = form_value;
+ break;
+ }
+ }
+ }
+
+ DIERef ref = *DWARFDIE(&unit, &die).GetDIERef();
+ switch (tag) {
+ case DW_TAG_inlined_subroutine:
+ case DW_TAG_subprogram:
+ if (has_address) {
+ if (name) {
+ bool is_objc_method = false;
+ if (cu_language == eLanguageTypeObjC ||
+ cu_language == eLanguageTypeObjC_plus_plus) {
+ ObjCLanguage::MethodName objc_method(name, true);
+ if (objc_method.IsValid(true)) {
+ is_objc_method = true;
+ ConstString class_name_with_category(
+ objc_method.GetClassNameWithCategory());
+ ConstString objc_selector_name(objc_method.GetSelector());
+ ConstString objc_fullname_no_category_name(
+ objc_method.GetFullNameWithoutCategory(true));
+ ConstString class_name_no_category(objc_method.GetClassName());
+ set.function_fullnames.Insert(ConstString(name), ref);
+ if (class_name_with_category)
+ set.objc_class_selectors.Insert(class_name_with_category, ref);
+ if (class_name_no_category &&
+ class_name_no_category != class_name_with_category)
+ set.objc_class_selectors.Insert(class_name_no_category, ref);
+ if (objc_selector_name)
+ set.function_selectors.Insert(objc_selector_name, ref);
+ if (objc_fullname_no_category_name)
+ set.function_fullnames.Insert(objc_fullname_no_category_name,
+ ref);
+ }
+ }
+ // If we have a mangled name, then the DW_AT_name attribute is
+ // usually the method name without the class or any parameters
+ bool is_method = DWARFDIE(&unit, &die).IsMethod();
+
+ if (is_method)
+ set.function_methods.Insert(ConstString(name), ref);
+ else
+ set.function_basenames.Insert(ConstString(name), ref);
+
+ if (!is_method && !mangled_cstr && !is_objc_method)
+ set.function_fullnames.Insert(ConstString(name), ref);
+ }
+ if (mangled_cstr) {
+ // Make sure our mangled name isn't the same string table entry as
+ // our name. If it starts with '_', then it is ok, else compare the
+ // string to make sure it isn't the same and we don't end up with
+ // duplicate entries
+ if (name && name != mangled_cstr &&
+ ((mangled_cstr[0] == '_') ||
+ (::strcmp(name, mangled_cstr) != 0))) {
+ set.function_fullnames.Insert(ConstString(mangled_cstr), ref);
+ }
+ }
+ }
+ break;
+
+ case DW_TAG_array_type:
+ case DW_TAG_base_type:
+ case DW_TAG_class_type:
+ case DW_TAG_constant:
+ case DW_TAG_enumeration_type:
+ case DW_TAG_string_type:
+ case DW_TAG_structure_type:
+ case DW_TAG_subroutine_type:
+ case DW_TAG_typedef:
+ case DW_TAG_union_type:
+ case DW_TAG_unspecified_type:
+ if (name && !is_declaration)
+ set.types.Insert(ConstString(name), ref);
+ if (mangled_cstr && !is_declaration)
+ set.types.Insert(ConstString(mangled_cstr), ref);
+ break;
+
+ case DW_TAG_namespace:
+ if (name)
+ set.namespaces.Insert(ConstString(name), ref);
+ break;
+
+ case DW_TAG_variable:
+ if (name && has_location_or_const_value && is_global_or_static_variable) {
+ set.globals.Insert(ConstString(name), ref);
+ // Be sure to include variables by their mangled and demangled names if
+ // they have any since a variable can have a basename "i", a mangled
+ // named "_ZN12_GLOBAL__N_11iE" and a demangled mangled name
+ // "(anonymous namespace)::i"...
+
+ // Make sure our mangled name isn't the same string table entry as our
+ // name. If it starts with '_', then it is ok, else compare the string
+ // to make sure it isn't the same and we don't end up with duplicate
+ // entries
+ if (mangled_cstr && name != mangled_cstr &&
+ ((mangled_cstr[0] == '_') || (::strcmp(name, mangled_cstr) != 0))) {
+ set.globals.Insert(ConstString(mangled_cstr), ref);
+ }
+ }
+ break;
+
+ default:
+ continue;
+ }
+ }
+}
+
+void ManualDWARFIndex::GetGlobalVariables(ConstString basename, DIEArray &offsets) {
+ Index();
+ m_set.globals.Find(basename, offsets);
+}
+
+void ManualDWARFIndex::GetGlobalVariables(const RegularExpression &regex,
+ DIEArray &offsets) {
+ Index();
+ m_set.globals.Find(regex, offsets);
+}
+
+void ManualDWARFIndex::GetGlobalVariables(const DWARFUnit &unit,
+ DIEArray &offsets) {
+ Index();
+ m_set.globals.FindAllEntriesForUnit(unit, offsets);
+}
+
+void ManualDWARFIndex::GetObjCMethods(ConstString class_name,
+ DIEArray &offsets) {
+ Index();
+ m_set.objc_class_selectors.Find(class_name, offsets);
+}
+
+void ManualDWARFIndex::GetCompleteObjCClass(ConstString class_name,
+ bool must_be_implementation,
+ DIEArray &offsets) {
+ Index();
+ m_set.types.Find(class_name, offsets);
+}
+
+void ManualDWARFIndex::GetTypes(ConstString name, DIEArray &offsets) {
+ Index();
+ m_set.types.Find(name, offsets);
+}
+
+void ManualDWARFIndex::GetTypes(const DWARFDeclContext &context,
+ DIEArray &offsets) {
+ Index();
+ m_set.types.Find(ConstString(context[0].name), offsets);
+}
+
+void ManualDWARFIndex::GetNamespaces(ConstString name, DIEArray &offsets) {
+ Index();
+ m_set.namespaces.Find(name, offsets);
+}
+
+void ManualDWARFIndex::GetFunctions(ConstString name, SymbolFileDWARF &dwarf,
+ const CompilerDeclContext &parent_decl_ctx,
+ uint32_t name_type_mask,
+ std::vector<DWARFDIE> &dies) {
+ Index();
+
+ if (name_type_mask & eFunctionNameTypeFull) {
+ DIEArray offsets;
+ m_set.function_basenames.Find(name, offsets);
+ m_set.function_methods.Find(name, offsets);
+ m_set.function_fullnames.Find(name, offsets);
+ for (const DIERef &die_ref: offsets) {
+ DWARFDIE die = dwarf.GetDIE(die_ref);
+ if (!die)
+ continue;
+ if (SymbolFileDWARF::DIEInDeclContext(&parent_decl_ctx, die))
+ dies.push_back(die);
+ }
+ }
+ if (name_type_mask & eFunctionNameTypeBase) {
+ DIEArray offsets;
+ m_set.function_basenames.Find(name, offsets);
+ for (const DIERef &die_ref: offsets) {
+ DWARFDIE die = dwarf.GetDIE(die_ref);
+ if (!die)
+ continue;
+ if (SymbolFileDWARF::DIEInDeclContext(&parent_decl_ctx, die))
+ dies.push_back(die);
+ }
+ offsets.clear();
+ }
+
+ if (name_type_mask & eFunctionNameTypeMethod && !parent_decl_ctx.IsValid()) {
+ DIEArray offsets;
+ m_set.function_methods.Find(name, offsets);
+ for (const DIERef &die_ref: offsets) {
+ if (DWARFDIE die = dwarf.GetDIE(die_ref))
+ dies.push_back(die);
+ }
+ }
+
+ if (name_type_mask & eFunctionNameTypeSelector &&
+ !parent_decl_ctx.IsValid()) {
+ DIEArray offsets;
+ m_set.function_selectors.Find(name, offsets);
+ for (const DIERef &die_ref: offsets) {
+ if (DWARFDIE die = dwarf.GetDIE(die_ref))
+ dies.push_back(die);
+ }
+ }
+}
+
+void ManualDWARFIndex::GetFunctions(const RegularExpression &regex,
+ DIEArray &offsets) {
+ Index();
+
+ m_set.function_basenames.Find(regex, offsets);
+ m_set.function_fullnames.Find(regex, offsets);
+}
+
+void ManualDWARFIndex::Dump(Stream &s) {
+ s.Format("Manual DWARF index for ({0}) '{1:F}':",
+ m_module.GetArchitecture().GetArchitectureName(),
+ m_module.GetObjectFile()->GetFileSpec());
+ s.Printf("\nFunction basenames:\n");
+ m_set.function_basenames.Dump(&s);
+ s.Printf("\nFunction fullnames:\n");
+ m_set.function_fullnames.Dump(&s);
+ s.Printf("\nFunction methods:\n");
+ m_set.function_methods.Dump(&s);
+ s.Printf("\nFunction selectors:\n");
+ m_set.function_selectors.Dump(&s);
+ s.Printf("\nObjective-C class selectors:\n");
+ m_set.objc_class_selectors.Dump(&s);
+ s.Printf("\nGlobals and statics:\n");
+ m_set.globals.Dump(&s);
+ s.Printf("\nTypes:\n");
+ m_set.types.Dump(&s);
+ s.Printf("\nNamespaces:\n");
+ m_set.namespaces.Dump(&s);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h
new file mode 100644
index 000000000000..dd03b103c936
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h
@@ -0,0 +1,74 @@
+//===-- ManualDWARFIndex.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_MANUALDWARFINDEX_H
+#define LLDB_MANUALDWARFINDEX_H
+
+#include "Plugins/SymbolFile/DWARF/DWARFIndex.h"
+#include "Plugins/SymbolFile/DWARF/NameToDIE.h"
+#include "llvm/ADT/DenseSet.h"
+
+class DWARFDebugInfo;
+
+namespace lldb_private {
+class ManualDWARFIndex : public DWARFIndex {
+public:
+ ManualDWARFIndex(Module &module, DWARFDebugInfo *debug_info,
+ llvm::DenseSet<dw_offset_t> units_to_avoid = {})
+ : DWARFIndex(module), m_debug_info(debug_info),
+ m_units_to_avoid(std::move(units_to_avoid)) {}
+
+ void Preload() override { Index(); }
+
+ void GetGlobalVariables(ConstString basename, DIEArray &offsets) override;
+ void GetGlobalVariables(const RegularExpression &regex,
+ DIEArray &offsets) override;
+ void GetGlobalVariables(const DWARFUnit &unit, DIEArray &offsets) override;
+ void GetObjCMethods(ConstString class_name, DIEArray &offsets) override;
+ void GetCompleteObjCClass(ConstString class_name, bool must_be_implementation,
+ DIEArray &offsets) override;
+ void GetTypes(ConstString name, DIEArray &offsets) override;
+ void GetTypes(const DWARFDeclContext &context, DIEArray &offsets) override;
+ void GetNamespaces(ConstString name, DIEArray &offsets) override;
+ void GetFunctions(ConstString name, SymbolFileDWARF &dwarf,
+ const CompilerDeclContext &parent_decl_ctx,
+ uint32_t name_type_mask,
+ std::vector<DWARFDIE> &dies) override;
+ void GetFunctions(const RegularExpression &regex, DIEArray &offsets) override;
+
+ void ReportInvalidDIERef(const DIERef &ref, llvm::StringRef name) override {}
+ void Dump(Stream &s) override;
+
+private:
+ struct IndexSet {
+ NameToDIE function_basenames;
+ NameToDIE function_fullnames;
+ NameToDIE function_methods;
+ NameToDIE function_selectors;
+ NameToDIE objc_class_selectors;
+ NameToDIE globals;
+ NameToDIE types;
+ NameToDIE namespaces;
+ };
+ void Index();
+ void IndexUnit(DWARFUnit &unit, IndexSet &set);
+
+ static void IndexUnitImpl(DWARFUnit &unit,
+ const lldb::LanguageType cu_language,
+ IndexSet &set);
+
+ /// Non-null value means we haven't built the index yet.
+ DWARFDebugInfo *m_debug_info;
+ /// Which dwarf units should we skip while building the index.
+ llvm::DenseSet<dw_offset_t> m_units_to_avoid;
+
+ IndexSet m_set;
+};
+} // namespace lldb_private
+
+#endif // LLDB_MANUALDWARFINDEX_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp
new file mode 100644
index 000000000000..7d81afb1b226
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp
@@ -0,0 +1,78 @@
+//===-- NameToDIE.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 "NameToDIE.h"
+#include "DWARFUnit.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void NameToDIE::Finalize() {
+ m_map.Sort();
+ m_map.SizeToFit();
+}
+
+void NameToDIE::Insert(ConstString name, const DIERef &die_ref) {
+ m_map.Append(name, die_ref);
+}
+
+size_t NameToDIE::Find(ConstString name, DIEArray &info_array) const {
+ return m_map.GetValues(name, info_array);
+}
+
+size_t NameToDIE::Find(const RegularExpression &regex,
+ DIEArray &info_array) const {
+ return m_map.GetValues(regex, info_array);
+}
+
+size_t NameToDIE::FindAllEntriesForUnit(const DWARFUnit &unit,
+ DIEArray &info_array) const {
+ const size_t initial_size = info_array.size();
+ const uint32_t size = m_map.GetSize();
+ for (uint32_t i = 0; i < size; ++i) {
+ const DIERef &die_ref = m_map.GetValueAtIndexUnchecked(i);
+ if (unit.GetSymbolFileDWARF().GetDwoNum() == die_ref.dwo_num() &&
+ unit.GetDebugSection() == die_ref.section() &&
+ unit.GetOffset() <= die_ref.die_offset() &&
+ die_ref.die_offset() < unit.GetNextUnitOffset())
+ info_array.push_back(die_ref);
+ }
+ return info_array.size() - initial_size;
+}
+
+void NameToDIE::Dump(Stream *s) {
+ const uint32_t size = m_map.GetSize();
+ for (uint32_t i = 0; i < size; ++i) {
+ s->Format("{0} \"{1}\"\n", m_map.GetValueAtIndexUnchecked(i),
+ m_map.GetCStringAtIndexUnchecked(i));
+ }
+}
+
+void NameToDIE::ForEach(
+ std::function<bool(ConstString name, const DIERef &die_ref)> const
+ &callback) const {
+ const uint32_t size = m_map.GetSize();
+ for (uint32_t i = 0; i < size; ++i) {
+ if (!callback(m_map.GetCStringAtIndexUnchecked(i),
+ m_map.GetValueAtIndexUnchecked(i)))
+ break;
+ }
+}
+
+void NameToDIE::Append(const NameToDIE &other) {
+ const uint32_t size = other.m_map.GetSize();
+ for (uint32_t i = 0; i < size; ++i) {
+ m_map.Append(other.m_map.GetCStringAtIndexUnchecked(i),
+ other.m_map.GetValueAtIndexUnchecked(i));
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.h
new file mode 100644
index 000000000000..b504f45e81b5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.h
@@ -0,0 +1,53 @@
+//===-- NameToDIE.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 SymbolFileDWARF_NameToDIE_h_
+#define SymbolFileDWARF_NameToDIE_h_
+
+#include <functional>
+
+#include "DIERef.h"
+#include "lldb/Core/UniqueCStringMap.h"
+#include "lldb/Core/dwarf.h"
+#include "lldb/lldb-defines.h"
+
+class DWARFUnit;
+
+class NameToDIE {
+public:
+ NameToDIE() : m_map() {}
+
+ ~NameToDIE() {}
+
+ void Dump(lldb_private::Stream *s);
+
+ void Insert(lldb_private::ConstString name, const DIERef &die_ref);
+
+ void Append(const NameToDIE &other);
+
+ void Finalize();
+
+ size_t Find(lldb_private::ConstString name,
+ DIEArray &info_array) const;
+
+ size_t Find(const lldb_private::RegularExpression &regex,
+ DIEArray &info_array) const;
+
+ size_t FindAllEntriesForUnit(const DWARFUnit &unit,
+ DIEArray &info_array) const;
+
+ void
+ ForEach(std::function<bool(lldb_private::ConstString name,
+ const DIERef &die_ref)> const
+ &callback) const;
+
+protected:
+ lldb_private::UniqueCStringMap<DIERef> m_map;
+};
+
+#endif // SymbolFileDWARF_NameToDIE_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
new file mode 100644
index 000000000000..e2ddcfc5d64b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -0,0 +1,3762 @@
+//===-- SymbolFileDWARF.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 "SymbolFileDWARF.h"
+
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Threading.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/Timer.h"
+
+#include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h"
+#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
+
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+
+#include "lldb/Interpreter/OptionValueFileSpecList.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangUtil.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/CompilerDecl.h"
+#include "lldb/Symbol/CompilerDeclContext.h"
+#include "lldb/Symbol/DebugMacros.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Symbol/LocateSymbolFile.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/TypeMap.h"
+#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Symbol/VariableList.h"
+
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Target.h"
+
+#include "AppleDWARFIndex.h"
+#include "DWARFASTParser.h"
+#include "DWARFASTParserClang.h"
+#include "DWARFCompileUnit.h"
+#include "DWARFDebugAbbrev.h"
+#include "DWARFDebugAranges.h"
+#include "DWARFDebugInfo.h"
+#include "DWARFDebugLine.h"
+#include "DWARFDebugMacro.h"
+#include "DWARFDebugRanges.h"
+#include "DWARFDeclContext.h"
+#include "DWARFFormValue.h"
+#include "DWARFTypeUnit.h"
+#include "DWARFUnit.h"
+#include "DebugNamesDWARFIndex.h"
+#include "LogChannelDWARF.h"
+#include "ManualDWARFIndex.h"
+#include "SymbolFileDWARFDebugMap.h"
+#include "SymbolFileDWARFDwo.h"
+#include "SymbolFileDWARFDwp.h"
+
+#include "llvm/Support/FileSystem.h"
+
+#include <algorithm>
+#include <map>
+#include <memory>
+
+#include <ctype.h>
+#include <string.h>
+
+//#define ENABLE_DEBUG_PRINTF // COMMENT OUT THIS LINE PRIOR TO CHECKIN
+
+#ifdef ENABLE_DEBUG_PRINTF
+#include <stdio.h>
+#define DEBUG_PRINTF(fmt, ...) printf(fmt, __VA_ARGS__)
+#else
+#define DEBUG_PRINTF(fmt, ...)
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+
+// static inline bool
+// child_requires_parent_class_union_or_struct_to_be_completed (dw_tag_t tag)
+//{
+// switch (tag)
+// {
+// default:
+// break;
+// case DW_TAG_subprogram:
+// case DW_TAG_inlined_subroutine:
+// case DW_TAG_class_type:
+// case DW_TAG_structure_type:
+// case DW_TAG_union_type:
+// return true;
+// }
+// return false;
+//}
+//
+
+namespace {
+
+static constexpr PropertyDefinition g_properties[] = {
+ {"comp-dir-symlink-paths", OptionValue::eTypeFileSpecList, true, 0, nullptr,
+ {},
+ "If the DW_AT_comp_dir matches any of these paths the symbolic "
+ "links will be resolved at DWARF parse time."},
+ {"ignore-file-indexes", OptionValue::eTypeBoolean, true, 0, nullptr, {},
+ "Ignore indexes present in the object files and always index DWARF "
+ "manually."}};
+
+enum {
+ ePropertySymLinkPaths,
+ ePropertyIgnoreIndexes,
+};
+
+class PluginProperties : public Properties {
+public:
+ static ConstString GetSettingName() {
+ return SymbolFileDWARF::GetPluginNameStatic();
+ }
+
+ PluginProperties() {
+ m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
+ m_collection_sp->Initialize(g_properties);
+ }
+
+ FileSpecList GetSymLinkPaths() {
+ const OptionValueFileSpecList *option_value =
+ m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(
+ nullptr, true, ePropertySymLinkPaths);
+ assert(option_value);
+ return option_value->GetCurrentValue();
+ }
+
+ bool IgnoreFileIndexes() const {
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, ePropertyIgnoreIndexes, false);
+ }
+};
+
+typedef std::shared_ptr<PluginProperties> SymbolFileDWARFPropertiesSP;
+
+static const SymbolFileDWARFPropertiesSP &GetGlobalPluginProperties() {
+ static const auto g_settings_sp(std::make_shared<PluginProperties>());
+ return g_settings_sp;
+}
+
+} // anonymous namespace end
+
+FileSpecList SymbolFileDWARF::GetSymlinkPaths() {
+ return GetGlobalPluginProperties()->GetSymLinkPaths();
+}
+
+void SymbolFileDWARF::Initialize() {
+ LogChannelDWARF::Initialize();
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ DebuggerInitialize);
+}
+
+void SymbolFileDWARF::DebuggerInitialize(Debugger &debugger) {
+ if (!PluginManager::GetSettingForSymbolFilePlugin(
+ debugger, PluginProperties::GetSettingName())) {
+ const bool is_global_setting = true;
+ PluginManager::CreateSettingForSymbolFilePlugin(
+ debugger, GetGlobalPluginProperties()->GetValueProperties(),
+ ConstString("Properties for the dwarf symbol-file plug-in."),
+ is_global_setting);
+ }
+}
+
+void SymbolFileDWARF::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+ LogChannelDWARF::Terminate();
+}
+
+lldb_private::ConstString SymbolFileDWARF::GetPluginNameStatic() {
+ static ConstString g_name("dwarf");
+ return g_name;
+}
+
+const char *SymbolFileDWARF::GetPluginDescriptionStatic() {
+ return "DWARF and DWARF3 debug symbol file reader.";
+}
+
+SymbolFile *SymbolFileDWARF::CreateInstance(ObjectFile *obj_file) {
+ return new SymbolFileDWARF(obj_file,
+ /*dwo_section_list*/ nullptr);
+}
+
+TypeList *SymbolFileDWARF::GetTypeList() {
+ // This method can be called without going through the symbol vendor so we
+ // need to lock the module.
+ std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
+ SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile();
+ if (debug_map_symfile)
+ return debug_map_symfile->GetTypeList();
+ else
+ return m_obj_file->GetModule()->GetTypeList();
+}
+void SymbolFileDWARF::GetTypes(const DWARFDIE &die, dw_offset_t min_die_offset,
+ dw_offset_t max_die_offset, uint32_t type_mask,
+ TypeSet &type_set) {
+ if (die) {
+ const dw_offset_t die_offset = die.GetOffset();
+
+ if (die_offset >= max_die_offset)
+ return;
+
+ if (die_offset >= min_die_offset) {
+ const dw_tag_t tag = die.Tag();
+
+ bool add_type = false;
+
+ switch (tag) {
+ case DW_TAG_array_type:
+ add_type = (type_mask & eTypeClassArray) != 0;
+ break;
+ case DW_TAG_unspecified_type:
+ case DW_TAG_base_type:
+ add_type = (type_mask & eTypeClassBuiltin) != 0;
+ break;
+ case DW_TAG_class_type:
+ add_type = (type_mask & eTypeClassClass) != 0;
+ break;
+ case DW_TAG_structure_type:
+ add_type = (type_mask & eTypeClassStruct) != 0;
+ break;
+ case DW_TAG_union_type:
+ add_type = (type_mask & eTypeClassUnion) != 0;
+ break;
+ case DW_TAG_enumeration_type:
+ add_type = (type_mask & eTypeClassEnumeration) != 0;
+ break;
+ case DW_TAG_subroutine_type:
+ case DW_TAG_subprogram:
+ case DW_TAG_inlined_subroutine:
+ add_type = (type_mask & eTypeClassFunction) != 0;
+ break;
+ case DW_TAG_pointer_type:
+ add_type = (type_mask & eTypeClassPointer) != 0;
+ break;
+ case DW_TAG_rvalue_reference_type:
+ case DW_TAG_reference_type:
+ add_type = (type_mask & eTypeClassReference) != 0;
+ break;
+ case DW_TAG_typedef:
+ add_type = (type_mask & eTypeClassTypedef) != 0;
+ break;
+ case DW_TAG_ptr_to_member_type:
+ add_type = (type_mask & eTypeClassMemberPointer) != 0;
+ break;
+ }
+
+ if (add_type) {
+ const bool assert_not_being_parsed = true;
+ Type *type = ResolveTypeUID(die, assert_not_being_parsed);
+ if (type) {
+ if (type_set.find(type) == type_set.end())
+ type_set.insert(type);
+ }
+ }
+ }
+
+ for (DWARFDIE child_die = die.GetFirstChild(); child_die.IsValid();
+ child_die = child_die.GetSibling()) {
+ GetTypes(child_die, min_die_offset, max_die_offset, type_mask, type_set);
+ }
+ }
+}
+
+size_t SymbolFileDWARF::GetTypes(SymbolContextScope *sc_scope,
+ TypeClass type_mask, TypeList &type_list)
+
+{
+ ASSERT_MODULE_LOCK(this);
+ TypeSet type_set;
+
+ CompileUnit *comp_unit = nullptr;
+ DWARFUnit *dwarf_cu = nullptr;
+ if (sc_scope)
+ comp_unit = sc_scope->CalculateSymbolContextCompileUnit();
+
+ if (comp_unit) {
+ dwarf_cu = GetDWARFCompileUnit(comp_unit);
+ if (dwarf_cu == nullptr)
+ return 0;
+ GetTypes(dwarf_cu->DIE(), dwarf_cu->GetOffset(),
+ dwarf_cu->GetNextUnitOffset(), type_mask, type_set);
+ } else {
+ DWARFDebugInfo *info = DebugInfo();
+ if (info) {
+ const size_t num_cus = info->GetNumUnits();
+ for (size_t cu_idx = 0; cu_idx < num_cus; ++cu_idx) {
+ dwarf_cu = info->GetUnitAtIndex(cu_idx);
+ if (dwarf_cu) {
+ GetTypes(dwarf_cu->DIE(), 0, UINT32_MAX, type_mask, type_set);
+ }
+ }
+ }
+ }
+
+ std::set<CompilerType> compiler_type_set;
+ size_t num_types_added = 0;
+ for (Type *type : type_set) {
+ CompilerType compiler_type = type->GetForwardCompilerType();
+ if (compiler_type_set.find(compiler_type) == compiler_type_set.end()) {
+ compiler_type_set.insert(compiler_type);
+ type_list.Insert(type->shared_from_this());
+ ++num_types_added;
+ }
+ }
+ return num_types_added;
+}
+
+// Gets the first parent that is a lexical block, function or inlined
+// subroutine, or compile unit.
+DWARFDIE
+SymbolFileDWARF::GetParentSymbolContextDIE(const DWARFDIE &child_die) {
+ DWARFDIE die;
+ for (die = child_die.GetParent(); die; die = die.GetParent()) {
+ dw_tag_t tag = die.Tag();
+
+ switch (tag) {
+ case DW_TAG_compile_unit:
+ case DW_TAG_partial_unit:
+ case DW_TAG_subprogram:
+ case DW_TAG_inlined_subroutine:
+ case DW_TAG_lexical_block:
+ return die;
+ }
+ }
+ return DWARFDIE();
+}
+
+SymbolFileDWARF::SymbolFileDWARF(ObjectFile *objfile,
+ SectionList *dwo_section_list)
+ : SymbolFile(objfile),
+ UserID(0x7fffffff00000000), // Used by SymbolFileDWARFDebugMap to
+ // when this class parses .o files to
+ // contain the .o file index/ID
+ m_debug_map_module_wp(), m_debug_map_symfile(nullptr),
+ m_context(objfile->GetModule()->GetSectionList(), dwo_section_list),
+ m_data_debug_loc(), m_abbr(), m_info(), m_fetched_external_modules(false),
+ m_supports_DW_AT_APPLE_objc_complete_type(eLazyBoolCalculate),
+ m_unique_ast_type_map() {}
+
+SymbolFileDWARF::~SymbolFileDWARF() {}
+
+static ConstString GetDWARFMachOSegmentName() {
+ static ConstString g_dwarf_section_name("__DWARF");
+ return g_dwarf_section_name;
+}
+
+UniqueDWARFASTTypeMap &SymbolFileDWARF::GetUniqueDWARFASTTypeMap() {
+ SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile();
+ if (debug_map_symfile)
+ return debug_map_symfile->GetUniqueDWARFASTTypeMap();
+ else
+ return m_unique_ast_type_map;
+}
+
+TypeSystem *SymbolFileDWARF::GetTypeSystemForLanguage(LanguageType language) {
+ SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile();
+ TypeSystem *type_system;
+ if (debug_map_symfile) {
+ type_system = debug_map_symfile->GetTypeSystemForLanguage(language);
+ } else {
+ type_system = m_obj_file->GetModule()->GetTypeSystemForLanguage(language);
+ if (type_system)
+ type_system->SetSymbolFile(this);
+ }
+ return type_system;
+}
+
+void SymbolFileDWARF::InitializeObject() {
+ Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO);
+
+ if (!GetGlobalPluginProperties()->IgnoreFileIndexes()) {
+ DWARFDataExtractor apple_names, apple_namespaces, apple_types, apple_objc;
+ LoadSectionData(eSectionTypeDWARFAppleNames, apple_names);
+ LoadSectionData(eSectionTypeDWARFAppleNamespaces, apple_namespaces);
+ LoadSectionData(eSectionTypeDWARFAppleTypes, apple_types);
+ LoadSectionData(eSectionTypeDWARFAppleObjC, apple_objc);
+
+ m_index = AppleDWARFIndex::Create(
+ *GetObjectFile()->GetModule(), apple_names, apple_namespaces,
+ apple_types, apple_objc, m_context.getOrLoadStrData());
+
+ if (m_index)
+ return;
+
+ DWARFDataExtractor debug_names;
+ LoadSectionData(eSectionTypeDWARFDebugNames, debug_names);
+ if (debug_names.GetByteSize() > 0) {
+ llvm::Expected<std::unique_ptr<DebugNamesDWARFIndex>> index_or =
+ DebugNamesDWARFIndex::Create(
+ *GetObjectFile()->GetModule(), debug_names,
+ m_context.getOrLoadStrData(), DebugInfo());
+ if (index_or) {
+ m_index = std::move(*index_or);
+ return;
+ }
+ LLDB_LOG_ERROR(log, index_or.takeError(),
+ "Unable to read .debug_names data: {0}");
+ }
+ }
+
+ m_index = llvm::make_unique<ManualDWARFIndex>(*GetObjectFile()->GetModule(),
+ DebugInfo());
+}
+
+bool SymbolFileDWARF::SupportedVersion(uint16_t version) {
+ return version >= 2 && version <= 5;
+}
+
+uint32_t SymbolFileDWARF::CalculateAbilities() {
+ uint32_t abilities = 0;
+ if (m_obj_file != nullptr) {
+ const Section *section = nullptr;
+ const SectionList *section_list = m_obj_file->GetSectionList();
+ if (section_list == nullptr)
+ return 0;
+
+ uint64_t debug_abbrev_file_size = 0;
+ uint64_t debug_info_file_size = 0;
+ uint64_t debug_line_file_size = 0;
+
+ section = section_list->FindSectionByName(GetDWARFMachOSegmentName()).get();
+
+ if (section)
+ section_list = &section->GetChildren();
+
+ section =
+ section_list->FindSectionByType(eSectionTypeDWARFDebugInfo, true).get();
+ if (section != nullptr) {
+ debug_info_file_size = section->GetFileSize();
+
+ section =
+ section_list->FindSectionByType(eSectionTypeDWARFDebugAbbrev, true)
+ .get();
+ if (section)
+ debug_abbrev_file_size = section->GetFileSize();
+
+ DWARFDebugAbbrev *abbrev = DebugAbbrev();
+ if (abbrev) {
+ std::set<dw_form_t> invalid_forms;
+ abbrev->GetUnsupportedForms(invalid_forms);
+ if (!invalid_forms.empty()) {
+ StreamString error;
+ error.Printf("unsupported DW_FORM value%s:", invalid_forms.size() > 1 ? "s" : "");
+ for (auto form : invalid_forms)
+ error.Printf(" %#x", form);
+ m_obj_file->GetModule()->ReportWarning("%s", error.GetString().str().c_str());
+ return 0;
+ }
+ }
+
+ section =
+ section_list->FindSectionByType(eSectionTypeDWARFDebugLine, true)
+ .get();
+ if (section)
+ debug_line_file_size = section->GetFileSize();
+ } else {
+ const char *symfile_dir_cstr =
+ m_obj_file->GetFileSpec().GetDirectory().GetCString();
+ if (symfile_dir_cstr) {
+ if (strcasestr(symfile_dir_cstr, ".dsym")) {
+ if (m_obj_file->GetType() == ObjectFile::eTypeDebugInfo) {
+ // We have a dSYM file that didn't have a any debug info. If the
+ // string table has a size of 1, then it was made from an
+ // executable with no debug info, or from an executable that was
+ // stripped.
+ section =
+ section_list->FindSectionByType(eSectionTypeDWARFDebugStr, true)
+ .get();
+ if (section && section->GetFileSize() == 1) {
+ m_obj_file->GetModule()->ReportWarning(
+ "empty dSYM file detected, dSYM was created with an "
+ "executable with no debug info.");
+ }
+ }
+ }
+ }
+ }
+
+ if (debug_abbrev_file_size > 0 && debug_info_file_size > 0)
+ abilities |= CompileUnits | Functions | Blocks | GlobalVariables |
+ LocalVariables | VariableTypes;
+
+ if (debug_line_file_size > 0)
+ abilities |= LineTables;
+ }
+ return abilities;
+}
+
+const DWARFDataExtractor &
+SymbolFileDWARF::GetCachedSectionData(lldb::SectionType sect_type,
+ DWARFDataSegment &data_segment) {
+ llvm::call_once(data_segment.m_flag, [this, sect_type, &data_segment] {
+ this->LoadSectionData(sect_type, std::ref(data_segment.m_data));
+ });
+ return data_segment.m_data;
+}
+
+void SymbolFileDWARF::LoadSectionData(lldb::SectionType sect_type,
+ DWARFDataExtractor &data) {
+ ModuleSP module_sp(m_obj_file->GetModule());
+ const SectionList *section_list = module_sp->GetSectionList();
+ if (!section_list)
+ return;
+
+ SectionSP section_sp(section_list->FindSectionByType(sect_type, true));
+ if (!section_sp)
+ return;
+
+ data.Clear();
+ m_obj_file->ReadSectionData(section_sp.get(), data);
+}
+
+const DWARFDataExtractor &SymbolFileDWARF::DebugLocData() {
+ const DWARFDataExtractor &debugLocData = get_debug_loc_data();
+ if (debugLocData.GetByteSize() > 0)
+ return debugLocData;
+ return get_debug_loclists_data();
+}
+
+const DWARFDataExtractor &SymbolFileDWARF::get_debug_loc_data() {
+ return GetCachedSectionData(eSectionTypeDWARFDebugLoc, m_data_debug_loc);
+}
+
+const DWARFDataExtractor &SymbolFileDWARF::get_debug_loclists_data() {
+ return GetCachedSectionData(eSectionTypeDWARFDebugLocLists,
+ m_data_debug_loclists);
+}
+
+DWARFDebugAbbrev *SymbolFileDWARF::DebugAbbrev() {
+ if (m_abbr)
+ return m_abbr.get();
+
+ const DWARFDataExtractor &debug_abbrev_data = m_context.getOrLoadAbbrevData();
+ if (debug_abbrev_data.GetByteSize() == 0)
+ return nullptr;
+
+ auto abbr = llvm::make_unique<DWARFDebugAbbrev>();
+ llvm::Error error = abbr->parse(debug_abbrev_data);
+ if (error) {
+ Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO);
+ LLDB_LOG_ERROR(log, std::move(error),
+ "Unable to read .debug_abbrev section: {0}");
+ return nullptr;
+ }
+
+ m_abbr = std::move(abbr);
+ return m_abbr.get();
+}
+
+const DWARFDebugAbbrev *SymbolFileDWARF::DebugAbbrev() const {
+ return m_abbr.get();
+}
+
+DWARFDebugInfo *SymbolFileDWARF::DebugInfo() {
+ if (m_info == nullptr) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "%s this = %p", LLVM_PRETTY_FUNCTION,
+ static_cast<void *>(this));
+ if (m_context.getOrLoadDebugInfoData().GetByteSize() > 0)
+ m_info = llvm::make_unique<DWARFDebugInfo>(*this, m_context);
+ }
+ return m_info.get();
+}
+
+const DWARFDebugInfo *SymbolFileDWARF::DebugInfo() const {
+ return m_info.get();
+}
+
+DWARFUnit *
+SymbolFileDWARF::GetDWARFCompileUnit(lldb_private::CompileUnit *comp_unit) {
+ if (!comp_unit)
+ return nullptr;
+
+ DWARFDebugInfo *info = DebugInfo();
+ if (info) {
+ // The compile unit ID is the index of the DWARF unit.
+ DWARFUnit *dwarf_cu = info->GetUnitAtIndex(comp_unit->GetID());
+ if (dwarf_cu && dwarf_cu->GetUserData() == nullptr)
+ dwarf_cu->SetUserData(comp_unit);
+ return dwarf_cu;
+ }
+ return nullptr;
+}
+
+DWARFDebugRangesBase *SymbolFileDWARF::GetDebugRanges() {
+ if (!m_ranges) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "%s this = %p", LLVM_PRETTY_FUNCTION,
+ static_cast<void *>(this));
+
+ if (m_context.getOrLoadRangesData().GetByteSize() > 0)
+ m_ranges.reset(new DWARFDebugRanges());
+
+ if (m_ranges)
+ m_ranges->Extract(m_context);
+ }
+ return m_ranges.get();
+}
+
+DWARFDebugRangesBase *SymbolFileDWARF::GetDebugRngLists() {
+ if (!m_rnglists) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "%s this = %p", LLVM_PRETTY_FUNCTION,
+ static_cast<void *>(this));
+
+ if (m_context.getOrLoadRngListsData().GetByteSize() > 0)
+ m_rnglists.reset(new DWARFDebugRngLists());
+
+ if (m_rnglists)
+ m_rnglists->Extract(m_context);
+ }
+ return m_rnglists.get();
+}
+
+lldb::CompUnitSP SymbolFileDWARF::ParseCompileUnit(DWARFCompileUnit &dwarf_cu) {
+ CompUnitSP cu_sp;
+ CompileUnit *comp_unit = (CompileUnit *)dwarf_cu.GetUserData();
+ if (comp_unit) {
+ // We already parsed this compile unit, had out a shared pointer to it
+ cu_sp = comp_unit->shared_from_this();
+ } else {
+ if (&dwarf_cu.GetSymbolFileDWARF() != this) {
+ return dwarf_cu.GetSymbolFileDWARF().ParseCompileUnit(dwarf_cu);
+ } else if (dwarf_cu.GetOffset() == 0 && GetDebugMapSymfile()) {
+ // Let the debug map create the compile unit
+ cu_sp = m_debug_map_symfile->GetCompileUnit(this);
+ dwarf_cu.SetUserData(cu_sp.get());
+ } else {
+ ModuleSP module_sp(m_obj_file->GetModule());
+ if (module_sp) {
+ const DWARFDIE cu_die = dwarf_cu.DIE();
+ if (cu_die) {
+ FileSpec cu_file_spec(cu_die.GetName(), dwarf_cu.GetPathStyle());
+ if (cu_file_spec) {
+ // If we have a full path to the compile unit, we don't need to
+ // resolve the file. This can be expensive e.g. when the source
+ // files are NFS mounted.
+ cu_file_spec.MakeAbsolute(dwarf_cu.GetCompilationDirectory());
+
+ std::string remapped_file;
+ if (module_sp->RemapSourceFile(cu_file_spec.GetPath(),
+ remapped_file))
+ cu_file_spec.SetFile(remapped_file, FileSpec::Style::native);
+ }
+
+ LanguageType cu_language = DWARFUnit::LanguageTypeFromDWARF(
+ cu_die.GetAttributeValueAsUnsigned(DW_AT_language, 0));
+
+ bool is_optimized = dwarf_cu.GetIsOptimized();
+ BuildCuTranslationTable();
+ cu_sp = std::make_shared<CompileUnit>(
+ module_sp, &dwarf_cu, cu_file_spec,
+ *GetDWARFUnitIndex(dwarf_cu.GetID()), cu_language,
+ is_optimized ? eLazyBoolYes : eLazyBoolNo);
+
+ dwarf_cu.SetUserData(cu_sp.get());
+
+ m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex(
+ dwarf_cu.GetID(), cu_sp);
+ }
+ }
+ }
+ }
+ return cu_sp;
+}
+
+void SymbolFileDWARF::BuildCuTranslationTable() {
+ if (!m_lldb_cu_to_dwarf_unit.empty())
+ return;
+
+ DWARFDebugInfo *info = DebugInfo();
+ if (!info)
+ return;
+
+ if (!info->ContainsTypeUnits()) {
+ // We can use a 1-to-1 mapping. No need to build a translation table.
+ return;
+ }
+ for (uint32_t i = 0, num = info->GetNumUnits(); i < num; ++i) {
+ if (auto *cu = llvm::dyn_cast<DWARFCompileUnit>(info->GetUnitAtIndex(i))) {
+ cu->SetID(m_lldb_cu_to_dwarf_unit.size());
+ m_lldb_cu_to_dwarf_unit.push_back(i);
+ }
+ }
+}
+
+llvm::Optional<uint32_t> SymbolFileDWARF::GetDWARFUnitIndex(uint32_t cu_idx) {
+ BuildCuTranslationTable();
+ if (m_lldb_cu_to_dwarf_unit.empty())
+ return cu_idx;
+ if (cu_idx >= m_lldb_cu_to_dwarf_unit.size())
+ return llvm::None;
+ return m_lldb_cu_to_dwarf_unit[cu_idx];
+}
+
+uint32_t SymbolFileDWARF::GetNumCompileUnits() {
+ DWARFDebugInfo *info = DebugInfo();
+ if (!info)
+ return 0;
+ BuildCuTranslationTable();
+ return m_lldb_cu_to_dwarf_unit.empty() ? info->GetNumUnits()
+ : m_lldb_cu_to_dwarf_unit.size();
+}
+
+CompUnitSP SymbolFileDWARF::ParseCompileUnitAtIndex(uint32_t cu_idx) {
+ ASSERT_MODULE_LOCK(this);
+ DWARFDebugInfo *info = DebugInfo();
+ if (!info)
+ return {};
+
+ if (llvm::Optional<uint32_t> dwarf_idx = GetDWARFUnitIndex(cu_idx)) {
+ if (auto *dwarf_cu = llvm::cast_or_null<DWARFCompileUnit>(
+ info->GetUnitAtIndex(*dwarf_idx)))
+ return ParseCompileUnit(*dwarf_cu);
+ }
+ return {};
+}
+
+Function *SymbolFileDWARF::ParseFunction(CompileUnit &comp_unit,
+ const DWARFDIE &die) {
+ ASSERT_MODULE_LOCK(this);
+ if (die.IsValid()) {
+ TypeSystem *type_system =
+ GetTypeSystemForLanguage(die.GetCU()->GetLanguageType());
+
+ if (type_system) {
+ DWARFASTParser *dwarf_ast = type_system->GetDWARFParser();
+ if (dwarf_ast)
+ return dwarf_ast->ParseFunctionFromDWARF(comp_unit, die);
+ }
+ }
+ return nullptr;
+}
+
+bool SymbolFileDWARF::FixupAddress(Address &addr) {
+ SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile();
+ if (debug_map_symfile) {
+ return debug_map_symfile->LinkOSOAddress(addr);
+ }
+ // This is a normal DWARF file, no address fixups need to happen
+ return true;
+}
+lldb::LanguageType SymbolFileDWARF::ParseLanguage(CompileUnit &comp_unit) {
+ ASSERT_MODULE_LOCK(this);
+ DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit);
+ if (dwarf_cu)
+ return dwarf_cu->GetLanguageType();
+ else
+ return eLanguageTypeUnknown;
+}
+
+size_t SymbolFileDWARF::ParseFunctions(CompileUnit &comp_unit) {
+ ASSERT_MODULE_LOCK(this);
+ DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit);
+ if (!dwarf_cu)
+ return 0;
+
+ size_t functions_added = 0;
+ std::vector<DWARFDIE> function_dies;
+ dwarf_cu->AppendDIEsWithTag(DW_TAG_subprogram, function_dies);
+ for (const DWARFDIE &die : function_dies) {
+ if (comp_unit.FindFunctionByUID(die.GetID()))
+ continue;
+ if (ParseFunction(comp_unit, die))
+ ++functions_added;
+ }
+ // FixupTypes();
+ return functions_added;
+}
+
+bool SymbolFileDWARF::ParseSupportFiles(CompileUnit &comp_unit,
+ FileSpecList &support_files) {
+ ASSERT_MODULE_LOCK(this);
+ if (DWARFUnit *unit = GetDWARFCompileUnit(&comp_unit)) {
+ const dw_offset_t stmt_list = unit->GetLineTableOffset();
+ if (stmt_list != DW_INVALID_OFFSET) {
+ // All file indexes in DWARF are one based and a file of index zero is
+ // supposed to be the compile unit itself.
+ support_files.Append(comp_unit);
+ return DWARFDebugLine::ParseSupportFiles(comp_unit.GetModule(),
+ m_context.getOrLoadLineData(),
+ stmt_list, support_files, unit);
+ }
+ }
+ return false;
+}
+
+FileSpec SymbolFileDWARF::GetFile(DWARFUnit &unit, size_t file_idx) {
+ if (auto *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(&unit)) {
+ if (CompileUnit *lldb_cu = GetCompUnitForDWARFCompUnit(*dwarf_cu))
+ return lldb_cu->GetSupportFiles().GetFileSpecAtIndex(file_idx);
+ return FileSpec();
+ }
+
+ auto &tu = llvm::cast<DWARFTypeUnit>(unit);
+ return GetTypeUnitSupportFiles(tu).GetFileSpecAtIndex(file_idx);
+}
+
+const FileSpecList &
+SymbolFileDWARF::GetTypeUnitSupportFiles(DWARFTypeUnit &tu) {
+ static FileSpecList empty_list;
+
+ dw_offset_t offset = tu.GetLineTableOffset();
+ if (offset == DW_INVALID_OFFSET ||
+ offset == llvm::DenseMapInfo<dw_offset_t>::getEmptyKey() ||
+ offset == llvm::DenseMapInfo<dw_offset_t>::getTombstoneKey())
+ return empty_list;
+
+ // Many type units can share a line table, so parse the support file list
+ // once, and cache it based on the offset field.
+ auto iter_bool = m_type_unit_support_files.try_emplace(offset);
+ FileSpecList &list = iter_bool.first->second;
+ if (iter_bool.second) {
+ list.Append(FileSpec());
+ DWARFDebugLine::ParseSupportFiles(GetObjectFile()->GetModule(),
+ m_context.getOrLoadLineData(), offset,
+ list, &tu);
+ }
+ return list;
+}
+
+bool SymbolFileDWARF::ParseIsOptimized(CompileUnit &comp_unit) {
+ ASSERT_MODULE_LOCK(this);
+ DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit);
+ if (dwarf_cu)
+ return dwarf_cu->GetIsOptimized();
+ return false;
+}
+
+bool SymbolFileDWARF::ParseImportedModules(
+ const lldb_private::SymbolContext &sc,
+ std::vector<SourceModule> &imported_modules) {
+ ASSERT_MODULE_LOCK(this);
+ assert(sc.comp_unit);
+ DWARFUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit);
+ if (!dwarf_cu)
+ return false;
+ if (!ClangModulesDeclVendor::LanguageSupportsClangModules(
+ sc.comp_unit->GetLanguage()))
+ return false;
+ UpdateExternalModuleListIfNeeded();
+
+ const DWARFDIE die = dwarf_cu->DIE();
+ if (!die)
+ return false;
+
+ for (DWARFDIE child_die = die.GetFirstChild(); child_die;
+ child_die = child_die.GetSibling()) {
+ if (child_die.Tag() != DW_TAG_imported_declaration)
+ continue;
+
+ DWARFDIE module_die = child_die.GetReferencedDIE(DW_AT_import);
+ if (module_die.Tag() != DW_TAG_module)
+ continue;
+
+ if (const char *name =
+ module_die.GetAttributeValueAsString(DW_AT_name, nullptr)) {
+ SourceModule module;
+ module.path.push_back(ConstString(name));
+
+ DWARFDIE parent_die = module_die;
+ while ((parent_die = parent_die.GetParent())) {
+ if (parent_die.Tag() != DW_TAG_module)
+ break;
+ if (const char *name =
+ parent_die.GetAttributeValueAsString(DW_AT_name, nullptr))
+ module.path.push_back(ConstString(name));
+ }
+ std::reverse(module.path.begin(), module.path.end());
+ if (const char *include_path = module_die.GetAttributeValueAsString(
+ DW_AT_LLVM_include_path, nullptr))
+ module.search_path = ConstString(include_path);
+ if (const char *sysroot = module_die.GetAttributeValueAsString(
+ DW_AT_LLVM_isysroot, nullptr))
+ module.sysroot = ConstString(sysroot);
+ imported_modules.push_back(module);
+ }
+ }
+ return true;
+}
+
+struct ParseDWARFLineTableCallbackInfo {
+ LineTable *line_table;
+ std::unique_ptr<LineSequence> sequence_up;
+ lldb::addr_t addr_mask;
+};
+
+// ParseStatementTableCallback
+static void ParseDWARFLineTableCallback(dw_offset_t offset,
+ const DWARFDebugLine::State &state,
+ void *userData) {
+ if (state.row == DWARFDebugLine::State::StartParsingLineTable) {
+ // Just started parsing the line table
+ } else if (state.row == DWARFDebugLine::State::DoneParsingLineTable) {
+ // Done parsing line table, nothing to do for the cleanup
+ } else {
+ ParseDWARFLineTableCallbackInfo *info =
+ (ParseDWARFLineTableCallbackInfo *)userData;
+ LineTable *line_table = info->line_table;
+
+ // If this is our first time here, we need to create a sequence container.
+ if (!info->sequence_up) {
+ info->sequence_up.reset(line_table->CreateLineSequenceContainer());
+ assert(info->sequence_up.get());
+ }
+ line_table->AppendLineEntryToSequence(
+ info->sequence_up.get(), state.address & info->addr_mask, state.line,
+ state.column, state.file, state.is_stmt, state.basic_block,
+ state.prologue_end, state.epilogue_begin, state.end_sequence);
+ if (state.end_sequence) {
+ // First, put the current sequence into the line table.
+ line_table->InsertSequence(info->sequence_up.get());
+ // Then, empty it to prepare for the next sequence.
+ info->sequence_up->Clear();
+ }
+ }
+}
+
+bool SymbolFileDWARF::ParseLineTable(CompileUnit &comp_unit) {
+ ASSERT_MODULE_LOCK(this);
+ if (comp_unit.GetLineTable() != nullptr)
+ return true;
+
+ DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit);
+ if (dwarf_cu) {
+ const DWARFBaseDIE dwarf_cu_die = dwarf_cu->GetUnitDIEOnly();
+ if (dwarf_cu_die) {
+ const dw_offset_t cu_line_offset =
+ dwarf_cu_die.GetAttributeValueAsUnsigned(DW_AT_stmt_list,
+ DW_INVALID_OFFSET);
+ if (cu_line_offset != DW_INVALID_OFFSET) {
+ std::unique_ptr<LineTable> line_table_up(new LineTable(&comp_unit));
+ if (line_table_up) {
+ ParseDWARFLineTableCallbackInfo info;
+ info.line_table = line_table_up.get();
+
+ /*
+ * MIPS:
+ * The SymbolContext may not have a valid target, thus we may not be
+ * able
+ * to call Address::GetOpcodeLoadAddress() which would clear the bit
+ * #0
+ * for MIPS. Use ArchSpec to clear the bit #0.
+ */
+ switch (GetObjectFile()->GetArchitecture().GetMachine()) {
+ case llvm::Triple::mips:
+ case llvm::Triple::mipsel:
+ case llvm::Triple::mips64:
+ case llvm::Triple::mips64el:
+ info.addr_mask = ~((lldb::addr_t)1);
+ break;
+ default:
+ info.addr_mask = ~((lldb::addr_t)0);
+ break;
+ }
+
+ lldb::offset_t offset = cu_line_offset;
+ DWARFDebugLine::ParseStatementTable(
+ m_context.getOrLoadLineData(), &offset,
+ ParseDWARFLineTableCallback, &info, dwarf_cu);
+ SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile();
+ if (debug_map_symfile) {
+ // We have an object file that has a line table with addresses that
+ // are not linked. We need to link the line table and convert the
+ // addresses that are relative to the .o file into addresses for
+ // the main executable.
+ comp_unit.SetLineTable(
+ debug_map_symfile->LinkOSOLineTable(this, line_table_up.get()));
+ } else {
+ comp_unit.SetLineTable(line_table_up.release());
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+lldb_private::DebugMacrosSP
+SymbolFileDWARF::ParseDebugMacros(lldb::offset_t *offset) {
+ auto iter = m_debug_macros_map.find(*offset);
+ if (iter != m_debug_macros_map.end())
+ return iter->second;
+
+ const DWARFDataExtractor &debug_macro_data = m_context.getOrLoadMacroData();
+ if (debug_macro_data.GetByteSize() == 0)
+ return DebugMacrosSP();
+
+ lldb_private::DebugMacrosSP debug_macros_sp(new lldb_private::DebugMacros());
+ m_debug_macros_map[*offset] = debug_macros_sp;
+
+ const DWARFDebugMacroHeader &header =
+ DWARFDebugMacroHeader::ParseHeader(debug_macro_data, offset);
+ DWARFDebugMacroEntry::ReadMacroEntries(
+ debug_macro_data, m_context.getOrLoadStrData(), header.OffsetIs64Bit(),
+ offset, this, debug_macros_sp);
+
+ return debug_macros_sp;
+}
+
+bool SymbolFileDWARF::ParseDebugMacros(CompileUnit &comp_unit) {
+ ASSERT_MODULE_LOCK(this);
+
+ DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit);
+ if (dwarf_cu == nullptr)
+ return false;
+
+ const DWARFBaseDIE dwarf_cu_die = dwarf_cu->GetUnitDIEOnly();
+ if (!dwarf_cu_die)
+ return false;
+
+ lldb::offset_t sect_offset =
+ dwarf_cu_die.GetAttributeValueAsUnsigned(DW_AT_macros, DW_INVALID_OFFSET);
+ if (sect_offset == DW_INVALID_OFFSET)
+ sect_offset = dwarf_cu_die.GetAttributeValueAsUnsigned(DW_AT_GNU_macros,
+ DW_INVALID_OFFSET);
+ if (sect_offset == DW_INVALID_OFFSET)
+ return false;
+
+ comp_unit.SetDebugMacros(ParseDebugMacros(&sect_offset));
+
+ return true;
+}
+
+size_t SymbolFileDWARF::ParseBlocksRecursive(
+ lldb_private::CompileUnit &comp_unit, Block *parent_block,
+ const DWARFDIE &orig_die, addr_t subprogram_low_pc, uint32_t depth) {
+ size_t blocks_added = 0;
+ DWARFDIE die = orig_die;
+ while (die) {
+ dw_tag_t tag = die.Tag();
+
+ switch (tag) {
+ case DW_TAG_inlined_subroutine:
+ case DW_TAG_subprogram:
+ case DW_TAG_lexical_block: {
+ Block *block = nullptr;
+ if (tag == DW_TAG_subprogram) {
+ // Skip any DW_TAG_subprogram DIEs that are inside of a normal or
+ // inlined functions. These will be parsed on their own as separate
+ // entities.
+
+ if (depth > 0)
+ break;
+
+ block = parent_block;
+ } else {
+ BlockSP block_sp(new Block(die.GetID()));
+ parent_block->AddChild(block_sp);
+ block = block_sp.get();
+ }
+ DWARFRangeList ranges;
+ const char *name = nullptr;
+ const char *mangled_name = nullptr;
+
+ int decl_file = 0;
+ int decl_line = 0;
+ int decl_column = 0;
+ int call_file = 0;
+ int call_line = 0;
+ int call_column = 0;
+ if (die.GetDIENamesAndRanges(name, mangled_name, ranges, decl_file,
+ decl_line, decl_column, call_file, call_line,
+ call_column, nullptr)) {
+ if (tag == DW_TAG_subprogram) {
+ assert(subprogram_low_pc == LLDB_INVALID_ADDRESS);
+ subprogram_low_pc = ranges.GetMinRangeBase(0);
+ } else if (tag == DW_TAG_inlined_subroutine) {
+ // We get called here for inlined subroutines in two ways. The first
+ // time is when we are making the Function object for this inlined
+ // concrete instance. Since we're creating a top level block at
+ // here, the subprogram_low_pc will be LLDB_INVALID_ADDRESS. So we
+ // need to adjust the containing address. The second time is when we
+ // are parsing the blocks inside the function that contains the
+ // inlined concrete instance. Since these will be blocks inside the
+ // containing "real" function the offset will be for that function.
+ if (subprogram_low_pc == LLDB_INVALID_ADDRESS) {
+ subprogram_low_pc = ranges.GetMinRangeBase(0);
+ }
+ }
+
+ const size_t num_ranges = ranges.GetSize();
+ for (size_t i = 0; i < num_ranges; ++i) {
+ const DWARFRangeList::Entry &range = ranges.GetEntryRef(i);
+ const addr_t range_base = range.GetRangeBase();
+ if (range_base >= subprogram_low_pc)
+ block->AddRange(Block::Range(range_base - subprogram_low_pc,
+ range.GetByteSize()));
+ else {
+ GetObjectFile()->GetModule()->ReportError(
+ "0x%8.8" PRIx64 ": adding range [0x%" PRIx64 "-0x%" PRIx64
+ ") which has a base that is less than the function's low PC "
+ "0x%" PRIx64 ". Please file a bug and attach the file at the "
+ "start of this error message",
+ block->GetID(), range_base, range.GetRangeEnd(),
+ subprogram_low_pc);
+ }
+ }
+ block->FinalizeRanges();
+
+ if (tag != DW_TAG_subprogram &&
+ (name != nullptr || mangled_name != nullptr)) {
+ std::unique_ptr<Declaration> decl_up;
+ if (decl_file != 0 || decl_line != 0 || decl_column != 0)
+ decl_up.reset(new Declaration(
+ comp_unit.GetSupportFiles().GetFileSpecAtIndex(decl_file),
+ decl_line, decl_column));
+
+ std::unique_ptr<Declaration> call_up;
+ if (call_file != 0 || call_line != 0 || call_column != 0)
+ call_up.reset(new Declaration(
+ comp_unit.GetSupportFiles().GetFileSpecAtIndex(call_file),
+ call_line, call_column));
+
+ block->SetInlinedFunctionInfo(name, mangled_name, decl_up.get(),
+ call_up.get());
+ }
+
+ ++blocks_added;
+
+ if (die.HasChildren()) {
+ blocks_added +=
+ ParseBlocksRecursive(comp_unit, block, die.GetFirstChild(),
+ subprogram_low_pc, depth + 1);
+ }
+ }
+ } break;
+ default:
+ break;
+ }
+
+ // Only parse siblings of the block if we are not at depth zero. A depth of
+ // zero indicates we are currently parsing the top level DW_TAG_subprogram
+ // DIE
+
+ if (depth == 0)
+ die.Clear();
+ else
+ die = die.GetSibling();
+ }
+ return blocks_added;
+}
+
+bool SymbolFileDWARF::ClassOrStructIsVirtual(const DWARFDIE &parent_die) {
+ if (parent_die) {
+ for (DWARFDIE die = parent_die.GetFirstChild(); die;
+ die = die.GetSibling()) {
+ dw_tag_t tag = die.Tag();
+ bool check_virtuality = false;
+ switch (tag) {
+ case DW_TAG_inheritance:
+ case DW_TAG_subprogram:
+ check_virtuality = true;
+ break;
+ default:
+ break;
+ }
+ if (check_virtuality) {
+ if (die.GetAttributeValueAsUnsigned(DW_AT_virtuality, 0) != 0)
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void SymbolFileDWARF::ParseDeclsForContext(CompilerDeclContext decl_ctx) {
+ TypeSystem *type_system = decl_ctx.GetTypeSystem();
+ DWARFASTParser *ast_parser = type_system->GetDWARFParser();
+ std::vector<DWARFDIE> decl_ctx_die_list =
+ ast_parser->GetDIEForDeclContext(decl_ctx);
+
+ for (DWARFDIE decl_ctx_die : decl_ctx_die_list)
+ for (DWARFDIE decl = decl_ctx_die.GetFirstChild(); decl;
+ decl = decl.GetSibling())
+ ast_parser->GetDeclForUIDFromDWARF(decl);
+}
+
+user_id_t SymbolFileDWARF::GetUID(DIERef ref) {
+ if (GetDebugMapSymfile())
+ return GetID() | ref.die_offset();
+
+ return user_id_t(GetDwoNum().getValueOr(0x7fffffff)) << 32 |
+ ref.die_offset() |
+ (lldb::user_id_t(ref.section() == DIERef::Section::DebugTypes) << 63);
+}
+
+llvm::Optional<SymbolFileDWARF::DecodedUID>
+SymbolFileDWARF::DecodeUID(lldb::user_id_t uid) {
+ // This method can be called without going through the symbol vendor so we
+ // need to lock the module.
+ std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
+ // Anytime we get a "lldb::user_id_t" from an lldb_private::SymbolFile API we
+ // must make sure we use the correct DWARF file when resolving things. On
+ // MacOSX, when using SymbolFileDWARFDebugMap, we will use multiple
+ // SymbolFileDWARF classes, one for each .o file. We can often end up with
+ // references to other DWARF objects and we must be ready to receive a
+ // "lldb::user_id_t" that specifies a DIE from another SymbolFileDWARF
+ // instance.
+ if (SymbolFileDWARFDebugMap *debug_map = GetDebugMapSymfile()) {
+ SymbolFileDWARF *dwarf = debug_map->GetSymbolFileByOSOIndex(
+ debug_map->GetOSOIndexFromUserID(uid));
+ return DecodedUID{
+ *dwarf, {llvm::None, DIERef::Section::DebugInfo, dw_offset_t(uid)}};
+ }
+ dw_offset_t die_offset = uid;
+ if (die_offset == DW_INVALID_OFFSET)
+ return llvm::None;
+
+ DIERef::Section section =
+ uid >> 63 ? DIERef::Section::DebugTypes : DIERef::Section::DebugInfo;
+
+ llvm::Optional<uint32_t> dwo_num = uid >> 32 & 0x7fffffff;
+ if (*dwo_num == 0x7fffffff)
+ dwo_num = llvm::None;
+
+ return DecodedUID{*this, {dwo_num, section, die_offset}};
+}
+
+DWARFDIE
+SymbolFileDWARF::GetDIE(lldb::user_id_t uid) {
+ // This method can be called without going through the symbol vendor so we
+ // need to lock the module.
+ std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
+
+ llvm::Optional<DecodedUID> decoded = DecodeUID(uid);
+
+ if (decoded)
+ return decoded->dwarf.GetDIE(decoded->ref);
+
+ return DWARFDIE();
+}
+
+CompilerDecl SymbolFileDWARF::GetDeclForUID(lldb::user_id_t type_uid) {
+ // This method can be called without going through the symbol vendor so we
+ // need to lock the module.
+ std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
+ // Anytime we have a lldb::user_id_t, we must get the DIE by calling
+ // SymbolFileDWARF::GetDIE(). See comments inside the
+ // SymbolFileDWARF::GetDIE() for details.
+ if (DWARFDIE die = GetDIE(type_uid))
+ return die.GetDecl();
+ return CompilerDecl();
+}
+
+CompilerDeclContext
+SymbolFileDWARF::GetDeclContextForUID(lldb::user_id_t type_uid) {
+ // This method can be called without going through the symbol vendor so we
+ // need to lock the module.
+ std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
+ // Anytime we have a lldb::user_id_t, we must get the DIE by calling
+ // SymbolFileDWARF::GetDIE(). See comments inside the
+ // SymbolFileDWARF::GetDIE() for details.
+ if (DWARFDIE die = GetDIE(type_uid))
+ return die.GetDeclContext();
+ return CompilerDeclContext();
+}
+
+CompilerDeclContext
+SymbolFileDWARF::GetDeclContextContainingUID(lldb::user_id_t type_uid) {
+ // This method can be called without going through the symbol vendor so we
+ // need to lock the module.
+ std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
+ // Anytime we have a lldb::user_id_t, we must get the DIE by calling
+ // SymbolFileDWARF::GetDIE(). See comments inside the
+ // SymbolFileDWARF::GetDIE() for details.
+ if (DWARFDIE die = GetDIE(type_uid))
+ return die.GetContainingDeclContext();
+ return CompilerDeclContext();
+}
+
+Type *SymbolFileDWARF::ResolveTypeUID(lldb::user_id_t type_uid) {
+ // This method can be called without going through the symbol vendor so we
+ // need to lock the module.
+ std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
+ // Anytime we have a lldb::user_id_t, we must get the DIE by calling
+ // SymbolFileDWARF::GetDIE(). See comments inside the
+ // SymbolFileDWARF::GetDIE() for details.
+ if (DWARFDIE type_die = GetDIE(type_uid))
+ return type_die.ResolveType();
+ else
+ return nullptr;
+}
+
+llvm::Optional<SymbolFile::ArrayInfo>
+SymbolFileDWARF::GetDynamicArrayInfoForUID(
+ lldb::user_id_t type_uid, const lldb_private::ExecutionContext *exe_ctx) {
+ std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
+ if (DWARFDIE type_die = GetDIE(type_uid))
+ return DWARFASTParser::ParseChildArrayInfo(type_die, exe_ctx);
+ else
+ return llvm::None;
+}
+
+Type *SymbolFileDWARF::ResolveTypeUID(const DIERef &die_ref) {
+ return ResolveType(GetDIE(die_ref), true);
+}
+
+Type *SymbolFileDWARF::ResolveTypeUID(const DWARFDIE &die,
+ bool assert_not_being_parsed) {
+ if (die) {
+ Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO));
+ if (log)
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::ResolveTypeUID (die = 0x%8.8x) %s '%s'",
+ die.GetOffset(), die.GetTagAsCString(), die.GetName());
+
+ // We might be coming in in the middle of a type tree (a class within a
+ // class, an enum within a class), so parse any needed parent DIEs before
+ // we get to this one...
+ DWARFDIE decl_ctx_die = GetDeclContextDIEContainingDIE(die);
+ if (decl_ctx_die) {
+ if (log) {
+ switch (decl_ctx_die.Tag()) {
+ case DW_TAG_structure_type:
+ case DW_TAG_union_type:
+ case DW_TAG_class_type: {
+ // Get the type, which could be a forward declaration
+ if (log)
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::ResolveTypeUID (die = 0x%8.8x) %s '%s' "
+ "resolve parent forward type for 0x%8.8x",
+ die.GetOffset(), die.GetTagAsCString(), die.GetName(),
+ decl_ctx_die.GetOffset());
+ } break;
+
+ default:
+ break;
+ }
+ }
+ }
+ return ResolveType(die);
+ }
+ return nullptr;
+}
+
+// This function is used when SymbolFileDWARFDebugMap owns a bunch of
+// SymbolFileDWARF objects to detect if this DWARF file is the one that can
+// resolve a compiler_type.
+bool SymbolFileDWARF::HasForwardDeclForClangType(
+ const CompilerType &compiler_type) {
+ CompilerType compiler_type_no_qualifiers =
+ ClangUtil::RemoveFastQualifiers(compiler_type);
+ if (GetForwardDeclClangTypeToDie().count(
+ compiler_type_no_qualifiers.GetOpaqueQualType())) {
+ return true;
+ }
+ TypeSystem *type_system = compiler_type.GetTypeSystem();
+
+ ClangASTContext *clang_type_system =
+ llvm::dyn_cast_or_null<ClangASTContext>(type_system);
+ if (!clang_type_system)
+ return false;
+ DWARFASTParserClang *ast_parser =
+ static_cast<DWARFASTParserClang *>(clang_type_system->GetDWARFParser());
+ return ast_parser->GetClangASTImporter().CanImport(compiler_type);
+}
+
+bool SymbolFileDWARF::CompleteType(CompilerType &compiler_type) {
+ std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
+
+ ClangASTContext *clang_type_system =
+ llvm::dyn_cast_or_null<ClangASTContext>(compiler_type.GetTypeSystem());
+ if (clang_type_system) {
+ DWARFASTParserClang *ast_parser =
+ static_cast<DWARFASTParserClang *>(clang_type_system->GetDWARFParser());
+ if (ast_parser &&
+ ast_parser->GetClangASTImporter().CanImport(compiler_type))
+ return ast_parser->GetClangASTImporter().CompleteType(compiler_type);
+ }
+
+ // We have a struct/union/class/enum that needs to be fully resolved.
+ CompilerType compiler_type_no_qualifiers =
+ ClangUtil::RemoveFastQualifiers(compiler_type);
+ auto die_it = GetForwardDeclClangTypeToDie().find(
+ compiler_type_no_qualifiers.GetOpaqueQualType());
+ if (die_it == GetForwardDeclClangTypeToDie().end()) {
+ // We have already resolved this type...
+ return true;
+ }
+
+ DWARFDIE dwarf_die = GetDIE(die_it->getSecond());
+ if (dwarf_die) {
+ // Once we start resolving this type, remove it from the forward
+ // declaration map in case anyone child members or other types require this
+ // type to get resolved. The type will get resolved when all of the calls
+ // to SymbolFileDWARF::ResolveClangOpaqueTypeDefinition are done.
+ GetForwardDeclClangTypeToDie().erase(die_it);
+
+ Type *type = GetDIEToType().lookup(dwarf_die.GetDIE());
+
+ Log *log(LogChannelDWARF::GetLogIfAny(DWARF_LOG_DEBUG_INFO |
+ DWARF_LOG_TYPE_COMPLETION));
+ if (log)
+ GetObjectFile()->GetModule()->LogMessageVerboseBacktrace(
+ log, "0x%8.8" PRIx64 ": %s '%s' resolving forward declaration...",
+ dwarf_die.GetID(), dwarf_die.GetTagAsCString(),
+ type->GetName().AsCString());
+ assert(compiler_type);
+ DWARFASTParser *dwarf_ast = dwarf_die.GetDWARFParser();
+ if (dwarf_ast)
+ return dwarf_ast->CompleteTypeFromDWARF(dwarf_die, type, compiler_type);
+ }
+ return false;
+}
+
+Type *SymbolFileDWARF::ResolveType(const DWARFDIE &die,
+ bool assert_not_being_parsed,
+ bool resolve_function_context) {
+ if (die) {
+ Type *type = GetTypeForDIE(die, resolve_function_context).get();
+
+ if (assert_not_being_parsed) {
+ if (type != DIE_IS_BEING_PARSED)
+ return type;
+
+ GetObjectFile()->GetModule()->ReportError(
+ "Parsing a die that is being parsed die: 0x%8.8x: %s %s",
+ die.GetOffset(), die.GetTagAsCString(), die.GetName());
+
+ } else
+ return type;
+ }
+ return nullptr;
+}
+
+CompileUnit *
+SymbolFileDWARF::GetCompUnitForDWARFCompUnit(DWARFCompileUnit &dwarf_cu) {
+ // Check if the symbol vendor already knows about this compile unit?
+ if (dwarf_cu.GetUserData() == nullptr) {
+ // The symbol vendor doesn't know about this compile unit, we need to parse
+ // and add it to the symbol vendor object.
+ return ParseCompileUnit(dwarf_cu).get();
+ }
+ return (CompileUnit *)dwarf_cu.GetUserData();
+}
+
+size_t SymbolFileDWARF::GetObjCMethodDIEOffsets(ConstString class_name,
+ DIEArray &method_die_offsets) {
+ method_die_offsets.clear();
+ m_index->GetObjCMethods(class_name, method_die_offsets);
+ return method_die_offsets.size();
+}
+
+bool SymbolFileDWARF::GetFunction(const DWARFDIE &die, SymbolContext &sc) {
+ sc.Clear(false);
+
+ if (die && llvm::isa<DWARFCompileUnit>(die.GetCU())) {
+ // Check if the symbol vendor already knows about this compile unit?
+ sc.comp_unit =
+ GetCompUnitForDWARFCompUnit(llvm::cast<DWARFCompileUnit>(*die.GetCU()));
+
+ sc.function = sc.comp_unit->FindFunctionByUID(die.GetID()).get();
+ if (sc.function == nullptr)
+ sc.function = ParseFunction(*sc.comp_unit, die);
+
+ if (sc.function) {
+ sc.module_sp = sc.function->CalculateSymbolContextModule();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+lldb::ModuleSP SymbolFileDWARF::GetDWOModule(ConstString name) {
+ UpdateExternalModuleListIfNeeded();
+ const auto &pos = m_external_type_modules.find(name);
+ if (pos != m_external_type_modules.end())
+ return pos->second;
+ else
+ return lldb::ModuleSP();
+}
+
+DWARFDIE
+SymbolFileDWARF::GetDIE(const DIERef &die_ref) {
+ if (die_ref.dwo_num()) {
+ return DebugInfo()
+ ->GetUnitAtIndex(*die_ref.dwo_num())
+ ->GetDwoSymbolFile()
+ ->GetDIE(die_ref);
+ }
+
+
+ DWARFDebugInfo *debug_info = DebugInfo();
+ if (debug_info)
+ return debug_info->GetDIE(die_ref);
+ else
+ return DWARFDIE();
+}
+
+std::unique_ptr<SymbolFileDWARFDwo>
+SymbolFileDWARF::GetDwoSymbolFileForCompileUnit(
+ DWARFUnit &unit, const DWARFDebugInfoEntry &cu_die) {
+ // If we are using a dSYM file, we never want the standard DWO files since
+ // the -gmodules support uses the same DWO machanism to specify full debug
+ // info files for modules.
+ if (GetDebugMapSymfile())
+ return nullptr;
+
+ DWARFCompileUnit *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(&unit);
+ // Only compile units can be split into two parts.
+ if (!dwarf_cu)
+ return nullptr;
+
+ const char *dwo_name =
+ cu_die.GetAttributeValueAsString(dwarf_cu, DW_AT_GNU_dwo_name, nullptr);
+ if (!dwo_name)
+ return nullptr;
+
+ SymbolFileDWARFDwp *dwp_symfile = GetDwpSymbolFile();
+ if (dwp_symfile) {
+ uint64_t dwo_id =
+ cu_die.GetAttributeValueAsUnsigned(dwarf_cu, DW_AT_GNU_dwo_id, 0);
+ std::unique_ptr<SymbolFileDWARFDwo> dwo_symfile =
+ dwp_symfile->GetSymbolFileForDwoId(*dwarf_cu, dwo_id);
+ if (dwo_symfile)
+ return dwo_symfile;
+ }
+
+ FileSpec dwo_file(dwo_name);
+ FileSystem::Instance().Resolve(dwo_file);
+ if (dwo_file.IsRelative()) {
+ const char *comp_dir =
+ cu_die.GetAttributeValueAsString(dwarf_cu, DW_AT_comp_dir, nullptr);
+ if (!comp_dir)
+ return nullptr;
+
+ dwo_file.SetFile(comp_dir, FileSpec::Style::native);
+ FileSystem::Instance().Resolve(dwo_file);
+ dwo_file.AppendPathComponent(dwo_name);
+ }
+
+ if (!FileSystem::Instance().Exists(dwo_file))
+ return nullptr;
+
+ const lldb::offset_t file_offset = 0;
+ DataBufferSP dwo_file_data_sp;
+ lldb::offset_t dwo_file_data_offset = 0;
+ ObjectFileSP dwo_obj_file = ObjectFile::FindPlugin(
+ GetObjectFile()->GetModule(), &dwo_file, file_offset,
+ FileSystem::Instance().GetByteSize(dwo_file), dwo_file_data_sp,
+ dwo_file_data_offset);
+ if (dwo_obj_file == nullptr)
+ return nullptr;
+
+ return llvm::make_unique<SymbolFileDWARFDwo>(dwo_obj_file, *dwarf_cu);
+}
+
+void SymbolFileDWARF::UpdateExternalModuleListIfNeeded() {
+ if (m_fetched_external_modules)
+ return;
+ m_fetched_external_modules = true;
+
+ DWARFDebugInfo *debug_info = DebugInfo();
+
+ const uint32_t num_compile_units = GetNumCompileUnits();
+ for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) {
+ DWARFUnit *dwarf_cu = debug_info->GetUnitAtIndex(cu_idx);
+
+ const DWARFBaseDIE die = dwarf_cu->GetUnitDIEOnly();
+ if (die && !die.HasChildren()) {
+ const char *name = die.GetAttributeValueAsString(DW_AT_name, nullptr);
+
+ if (name) {
+ ConstString const_name(name);
+ if (m_external_type_modules.find(const_name) ==
+ m_external_type_modules.end()) {
+ ModuleSP module_sp;
+ const char *dwo_path =
+ die.GetAttributeValueAsString(DW_AT_GNU_dwo_name, nullptr);
+ if (dwo_path) {
+ ModuleSpec dwo_module_spec;
+ dwo_module_spec.GetFileSpec().SetFile(dwo_path,
+ FileSpec::Style::native);
+ if (dwo_module_spec.GetFileSpec().IsRelative()) {
+ const char *comp_dir =
+ die.GetAttributeValueAsString(DW_AT_comp_dir, nullptr);
+ if (comp_dir) {
+ dwo_module_spec.GetFileSpec().SetFile(comp_dir,
+ FileSpec::Style::native);
+ FileSystem::Instance().Resolve(dwo_module_spec.GetFileSpec());
+ dwo_module_spec.GetFileSpec().AppendPathComponent(dwo_path);
+ }
+ }
+ dwo_module_spec.GetArchitecture() =
+ m_obj_file->GetModule()->GetArchitecture();
+
+ // When LLDB loads "external" modules it looks at the presence of
+ // DW_AT_GNU_dwo_name. However, when the already created module
+ // (corresponding to .dwo itself) is being processed, it will see
+ // the presence of DW_AT_GNU_dwo_name (which contains the name of
+ // dwo file) and will try to call ModuleList::GetSharedModule
+ // again. In some cases (i.e. for empty files) Clang 4.0 generates
+ // a *.dwo file which has DW_AT_GNU_dwo_name, but no
+ // DW_AT_comp_dir. In this case the method
+ // ModuleList::GetSharedModule will fail and the warning will be
+ // printed. However, as one can notice in this case we don't
+ // actually need to try to load the already loaded module
+ // (corresponding to .dwo) so we simply skip it.
+ if (m_obj_file->GetFileSpec().GetFileNameExtension() == ".dwo" &&
+ llvm::StringRef(m_obj_file->GetFileSpec().GetPath())
+ .endswith(dwo_module_spec.GetFileSpec().GetPath())) {
+ continue;
+ }
+
+ Status error = ModuleList::GetSharedModule(
+ dwo_module_spec, module_sp, nullptr, nullptr, nullptr);
+ if (!module_sp) {
+ GetObjectFile()->GetModule()->ReportWarning(
+ "0x%8.8x: unable to locate module needed for external types: "
+ "%s\nerror: %s\nDebugging will be degraded due to missing "
+ "types. Rebuilding your project will regenerate the needed "
+ "module files.",
+ die.GetOffset(),
+ dwo_module_spec.GetFileSpec().GetPath().c_str(),
+ error.AsCString("unknown error"));
+ }
+ }
+ m_external_type_modules[const_name] = module_sp;
+ }
+ }
+ }
+ }
+}
+
+SymbolFileDWARF::GlobalVariableMap &SymbolFileDWARF::GetGlobalAranges() {
+ if (!m_global_aranges_up) {
+ m_global_aranges_up.reset(new GlobalVariableMap());
+
+ ModuleSP module_sp = GetObjectFile()->GetModule();
+ if (module_sp) {
+ const size_t num_cus = module_sp->GetNumCompileUnits();
+ for (size_t i = 0; i < num_cus; ++i) {
+ CompUnitSP cu_sp = module_sp->GetCompileUnitAtIndex(i);
+ if (cu_sp) {
+ VariableListSP globals_sp = cu_sp->GetVariableList(true);
+ if (globals_sp) {
+ const size_t num_globals = globals_sp->GetSize();
+ for (size_t g = 0; g < num_globals; ++g) {
+ VariableSP var_sp = globals_sp->GetVariableAtIndex(g);
+ if (var_sp && !var_sp->GetLocationIsConstantValueData()) {
+ const DWARFExpression &location = var_sp->LocationExpression();
+ Value location_result;
+ Status error;
+ if (location.Evaluate(nullptr, LLDB_INVALID_ADDRESS, nullptr,
+ nullptr, location_result, &error)) {
+ if (location_result.GetValueType() ==
+ Value::eValueTypeFileAddress) {
+ lldb::addr_t file_addr =
+ location_result.GetScalar().ULongLong();
+ lldb::addr_t byte_size = 1;
+ if (var_sp->GetType())
+ byte_size =
+ var_sp->GetType()->GetByteSize().getValueOr(0);
+ m_global_aranges_up->Append(GlobalVariableMap::Entry(
+ file_addr, byte_size, var_sp.get()));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ m_global_aranges_up->Sort();
+ }
+ return *m_global_aranges_up;
+}
+
+uint32_t SymbolFileDWARF::ResolveSymbolContext(const Address &so_addr,
+ SymbolContextItem resolve_scope,
+ SymbolContext &sc) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat,
+ "SymbolFileDWARF::"
+ "ResolveSymbolContext (so_addr = { "
+ "section = %p, offset = 0x%" PRIx64
+ " }, resolve_scope = 0x%8.8x)",
+ static_cast<void *>(so_addr.GetSection().get()),
+ so_addr.GetOffset(), resolve_scope);
+ uint32_t resolved = 0;
+ if (resolve_scope &
+ (eSymbolContextCompUnit | eSymbolContextFunction | eSymbolContextBlock |
+ eSymbolContextLineEntry | eSymbolContextVariable)) {
+ lldb::addr_t file_vm_addr = so_addr.GetFileAddress();
+
+ DWARFDebugInfo *debug_info = DebugInfo();
+ if (debug_info) {
+ llvm::Expected<DWARFDebugAranges &> aranges =
+ debug_info->GetCompileUnitAranges();
+ if (!aranges) {
+ Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO);
+ LLDB_LOG_ERROR(log, aranges.takeError(),
+ "SymbolFileDWARF::ResolveSymbolContext failed to get cu "
+ "aranges. {0}");
+ return 0;
+ }
+
+ const dw_offset_t cu_offset = aranges->FindAddress(file_vm_addr);
+ if (cu_offset == DW_INVALID_OFFSET) {
+ // Global variables are not in the compile unit address ranges. The
+ // only way to currently find global variables is to iterate over the
+ // .debug_pubnames or the __apple_names table and find all items in
+ // there that point to DW_TAG_variable DIEs and then find the address
+ // that matches.
+ if (resolve_scope & eSymbolContextVariable) {
+ GlobalVariableMap &map = GetGlobalAranges();
+ const GlobalVariableMap::Entry *entry =
+ map.FindEntryThatContains(file_vm_addr);
+ if (entry && entry->data) {
+ Variable *variable = entry->data;
+ SymbolContextScope *scc = variable->GetSymbolContextScope();
+ if (scc) {
+ scc->CalculateSymbolContext(&sc);
+ sc.variable = variable;
+ }
+ return sc.GetResolvedMask();
+ }
+ }
+ } else {
+ uint32_t cu_idx = DW_INVALID_INDEX;
+ if (auto *dwarf_cu = llvm::dyn_cast_or_null<DWARFCompileUnit>(
+ debug_info->GetUnitAtOffset(DIERef::Section::DebugInfo,
+ cu_offset, &cu_idx))) {
+ sc.comp_unit = GetCompUnitForDWARFCompUnit(*dwarf_cu);
+ if (sc.comp_unit) {
+ resolved |= eSymbolContextCompUnit;
+
+ bool force_check_line_table = false;
+ if (resolve_scope &
+ (eSymbolContextFunction | eSymbolContextBlock)) {
+ DWARFDIE function_die = dwarf_cu->LookupAddress(file_vm_addr);
+ DWARFDIE block_die;
+ if (function_die) {
+ sc.function =
+ sc.comp_unit->FindFunctionByUID(function_die.GetID()).get();
+ if (sc.function == nullptr)
+ sc.function = ParseFunction(*sc.comp_unit, function_die);
+
+ if (sc.function && (resolve_scope & eSymbolContextBlock))
+ block_die = function_die.LookupDeepestBlock(file_vm_addr);
+ } else {
+ // We might have had a compile unit that had discontiguous
+ // address ranges where the gaps are symbols that don't have
+ // any debug info. Discontiguous compile unit address ranges
+ // should only happen when there aren't other functions from
+ // other compile units in these gaps. This helps keep the size
+ // of the aranges down.
+ force_check_line_table = true;
+ }
+
+ if (sc.function != nullptr) {
+ resolved |= eSymbolContextFunction;
+
+ if (resolve_scope & eSymbolContextBlock) {
+ Block &block = sc.function->GetBlock(true);
+
+ if (block_die)
+ sc.block = block.FindBlockByID(block_die.GetID());
+ else
+ sc.block = block.FindBlockByID(function_die.GetID());
+ if (sc.block)
+ resolved |= eSymbolContextBlock;
+ }
+ }
+ }
+
+ if ((resolve_scope & eSymbolContextLineEntry) ||
+ force_check_line_table) {
+ LineTable *line_table = sc.comp_unit->GetLineTable();
+ if (line_table != nullptr) {
+ // And address that makes it into this function should be in
+ // terms of this debug file if there is no debug map, or it
+ // will be an address in the .o file which needs to be fixed up
+ // to be in terms of the debug map executable. Either way,
+ // calling FixupAddress() will work for us.
+ Address exe_so_addr(so_addr);
+ if (FixupAddress(exe_so_addr)) {
+ if (line_table->FindLineEntryByAddress(exe_so_addr,
+ sc.line_entry)) {
+ resolved |= eSymbolContextLineEntry;
+ }
+ }
+ }
+ }
+
+ if (force_check_line_table &&
+ !(resolved & eSymbolContextLineEntry)) {
+ // We might have had a compile unit that had discontiguous
+ // address ranges where the gaps are symbols that don't have any
+ // debug info. Discontiguous compile unit address ranges should
+ // only happen when there aren't other functions from other
+ // compile units in these gaps. This helps keep the size of the
+ // aranges down.
+ sc.comp_unit = nullptr;
+ resolved &= ~eSymbolContextCompUnit;
+ }
+ } else {
+ GetObjectFile()->GetModule()->ReportWarning(
+ "0x%8.8x: compile unit %u failed to create a valid "
+ "lldb_private::CompileUnit class.",
+ cu_offset, cu_idx);
+ }
+ }
+ }
+ }
+ }
+ return resolved;
+}
+
+uint32_t SymbolFileDWARF::ResolveSymbolContext(const FileSpec &file_spec,
+ uint32_t line,
+ bool check_inlines,
+ SymbolContextItem resolve_scope,
+ SymbolContextList &sc_list) {
+ const uint32_t prev_size = sc_list.GetSize();
+ if (resolve_scope & eSymbolContextCompUnit) {
+ for (uint32_t cu_idx = 0, num_cus = GetNumCompileUnits(); cu_idx < num_cus;
+ ++cu_idx) {
+ CompileUnit *dc_cu = ParseCompileUnitAtIndex(cu_idx).get();
+ if (!dc_cu)
+ continue;
+
+ const bool full_match = (bool)file_spec.GetDirectory();
+ bool file_spec_matches_cu_file_spec =
+ FileSpec::Equal(file_spec, *dc_cu, full_match);
+ if (check_inlines || file_spec_matches_cu_file_spec) {
+ SymbolContext sc(m_obj_file->GetModule());
+ sc.comp_unit = dc_cu;
+ uint32_t file_idx = UINT32_MAX;
+
+ // If we are looking for inline functions only and we don't find it
+ // in the support files, we are done.
+ if (check_inlines) {
+ file_idx =
+ sc.comp_unit->GetSupportFiles().FindFileIndex(1, file_spec, true);
+ if (file_idx == UINT32_MAX)
+ continue;
+ }
+
+ if (line != 0) {
+ LineTable *line_table = sc.comp_unit->GetLineTable();
+
+ if (line_table != nullptr && line != 0) {
+ // We will have already looked up the file index if we are
+ // searching for inline entries.
+ if (!check_inlines)
+ file_idx = sc.comp_unit->GetSupportFiles().FindFileIndex(
+ 1, file_spec, true);
+
+ if (file_idx != UINT32_MAX) {
+ uint32_t found_line;
+ uint32_t line_idx = line_table->FindLineEntryIndexByFileIndex(
+ 0, file_idx, line, false, &sc.line_entry);
+ found_line = sc.line_entry.line;
+
+ while (line_idx != UINT32_MAX) {
+ sc.function = nullptr;
+ sc.block = nullptr;
+ if (resolve_scope &
+ (eSymbolContextFunction | eSymbolContextBlock)) {
+ const lldb::addr_t file_vm_addr =
+ sc.line_entry.range.GetBaseAddress().GetFileAddress();
+ if (file_vm_addr != LLDB_INVALID_ADDRESS) {
+ DWARFDIE function_die =
+ GetDWARFCompileUnit(dc_cu)->LookupAddress(file_vm_addr);
+ DWARFDIE block_die;
+ if (function_die) {
+ sc.function =
+ sc.comp_unit->FindFunctionByUID(function_die.GetID())
+ .get();
+ if (sc.function == nullptr)
+ sc.function =
+ ParseFunction(*sc.comp_unit, function_die);
+
+ if (sc.function && (resolve_scope & eSymbolContextBlock))
+ block_die =
+ function_die.LookupDeepestBlock(file_vm_addr);
+ }
+
+ if (sc.function != nullptr) {
+ Block &block = sc.function->GetBlock(true);
+
+ if (block_die)
+ sc.block = block.FindBlockByID(block_die.GetID());
+ else if (function_die)
+ sc.block = block.FindBlockByID(function_die.GetID());
+ }
+ }
+ }
+
+ sc_list.Append(sc);
+ line_idx = line_table->FindLineEntryIndexByFileIndex(
+ line_idx + 1, file_idx, found_line, true, &sc.line_entry);
+ }
+ }
+ } else if (file_spec_matches_cu_file_spec && !check_inlines) {
+ // only append the context if we aren't looking for inline call
+ // sites by file and line and if the file spec matches that of
+ // the compile unit
+ sc_list.Append(sc);
+ }
+ } else if (file_spec_matches_cu_file_spec && !check_inlines) {
+ // only append the context if we aren't looking for inline call
+ // sites by file and line and if the file spec matches that of
+ // the compile unit
+ sc_list.Append(sc);
+ }
+
+ if (!check_inlines)
+ break;
+ }
+ }
+ }
+ return sc_list.GetSize() - prev_size;
+}
+
+void SymbolFileDWARF::PreloadSymbols() {
+ std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
+ m_index->Preload();
+}
+
+std::recursive_mutex &SymbolFileDWARF::GetModuleMutex() const {
+ lldb::ModuleSP module_sp(m_debug_map_module_wp.lock());
+ if (module_sp)
+ return module_sp->GetMutex();
+ return GetObjectFile()->GetModule()->GetMutex();
+}
+
+bool SymbolFileDWARF::DeclContextMatchesThisSymbolFile(
+ const lldb_private::CompilerDeclContext *decl_ctx) {
+ if (decl_ctx == nullptr || !decl_ctx->IsValid()) {
+ // Invalid namespace decl which means we aren't matching only things in
+ // this symbol file, so return true to indicate it matches this symbol
+ // file.
+ return true;
+ }
+
+ TypeSystem *decl_ctx_type_system = decl_ctx->GetTypeSystem();
+ TypeSystem *type_system = GetTypeSystemForLanguage(
+ decl_ctx_type_system->GetMinimumLanguage(nullptr));
+ if (decl_ctx_type_system == type_system)
+ return true; // The type systems match, return true
+
+ // The namespace AST was valid, and it does not match...
+ Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS));
+
+ if (log)
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "Valid namespace does not match symbol file");
+
+ return false;
+}
+
+uint32_t SymbolFileDWARF::FindGlobalVariables(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches, VariableList &variables) {
+ Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS));
+
+ if (log)
+ GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF::FindGlobalVariables (name=\"%s\", "
+ "parent_decl_ctx=%p, max_matches=%u, variables)",
+ name.GetCString(), static_cast<const void *>(parent_decl_ctx),
+ max_matches);
+
+ if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx))
+ return 0;
+
+ DWARFDebugInfo *info = DebugInfo();
+ if (info == nullptr)
+ return 0;
+
+ // Remember how many variables are in the list before we search.
+ const uint32_t original_size = variables.GetSize();
+
+ llvm::StringRef basename;
+ llvm::StringRef context;
+ bool name_is_mangled = (bool)Mangled(name);
+
+ if (!CPlusPlusLanguage::ExtractContextAndIdentifier(name.GetCString(),
+ context, basename))
+ basename = name.GetStringRef();
+
+ DIEArray die_offsets;
+ m_index->GetGlobalVariables(ConstString(basename), die_offsets);
+ const size_t num_die_matches = die_offsets.size();
+ if (num_die_matches) {
+ SymbolContext sc;
+ sc.module_sp = m_obj_file->GetModule();
+ assert(sc.module_sp);
+
+ // Loop invariant: Variables up to this index have been checked for context
+ // matches.
+ uint32_t pruned_idx = original_size;
+
+ bool done = false;
+ for (size_t i = 0; i < num_die_matches && !done; ++i) {
+ const DIERef &die_ref = die_offsets[i];
+ DWARFDIE die = GetDIE(die_ref);
+
+ if (die) {
+ switch (die.Tag()) {
+ default:
+ case DW_TAG_subprogram:
+ case DW_TAG_inlined_subroutine:
+ case DW_TAG_try_block:
+ case DW_TAG_catch_block:
+ break;
+
+ case DW_TAG_variable: {
+ auto *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(die.GetCU());
+ if (!dwarf_cu)
+ continue;
+ sc.comp_unit = GetCompUnitForDWARFCompUnit(*dwarf_cu);
+
+ if (parent_decl_ctx) {
+ DWARFASTParser *dwarf_ast = die.GetDWARFParser();
+ if (dwarf_ast) {
+ CompilerDeclContext actual_parent_decl_ctx =
+ dwarf_ast->GetDeclContextContainingUIDFromDWARF(die);
+ if (!actual_parent_decl_ctx ||
+ actual_parent_decl_ctx != *parent_decl_ctx)
+ continue;
+ }
+ }
+
+ ParseVariables(sc, die, LLDB_INVALID_ADDRESS, false, false,
+ &variables);
+ while (pruned_idx < variables.GetSize()) {
+ VariableSP var_sp = variables.GetVariableAtIndex(pruned_idx);
+ if (name_is_mangled ||
+ var_sp->GetName().GetStringRef().contains(name.GetStringRef()))
+ ++pruned_idx;
+ else
+ variables.RemoveVariableAtIndex(pruned_idx);
+ }
+
+ if (variables.GetSize() - original_size >= max_matches)
+ done = true;
+ } break;
+ }
+ } else {
+ m_index->ReportInvalidDIERef(die_ref, name.GetStringRef());
+ }
+ }
+ }
+
+ // Return the number of variable that were appended to the list
+ const uint32_t num_matches = variables.GetSize() - original_size;
+ if (log && num_matches > 0) {
+ GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF::FindGlobalVariables (name=\"%s\", "
+ "parent_decl_ctx=%p, max_matches=%u, variables) => %u",
+ name.GetCString(), static_cast<const void *>(parent_decl_ctx),
+ max_matches, num_matches);
+ }
+ return num_matches;
+}
+
+uint32_t SymbolFileDWARF::FindGlobalVariables(const RegularExpression &regex,
+ uint32_t max_matches,
+ VariableList &variables) {
+ Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS));
+
+ if (log) {
+ GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF::FindGlobalVariables (regex=\"%s\", "
+ "max_matches=%u, variables)",
+ regex.GetText().str().c_str(), max_matches);
+ }
+
+ DWARFDebugInfo *info = DebugInfo();
+ if (info == nullptr)
+ return 0;
+
+ // Remember how many variables are in the list before we search.
+ const uint32_t original_size = variables.GetSize();
+
+ DIEArray die_offsets;
+ m_index->GetGlobalVariables(regex, die_offsets);
+
+ SymbolContext sc;
+ sc.module_sp = m_obj_file->GetModule();
+ assert(sc.module_sp);
+
+ const size_t num_matches = die_offsets.size();
+ if (num_matches) {
+ for (size_t i = 0; i < num_matches; ++i) {
+ const DIERef &die_ref = die_offsets[i];
+ DWARFDIE die = GetDIE(die_ref);
+
+ if (die) {
+ DWARFCompileUnit *dwarf_cu =
+ llvm::dyn_cast<DWARFCompileUnit>(die.GetCU());
+ if (!dwarf_cu)
+ continue;
+ sc.comp_unit = GetCompUnitForDWARFCompUnit(*dwarf_cu);
+
+ ParseVariables(sc, die, LLDB_INVALID_ADDRESS, false, false, &variables);
+
+ if (variables.GetSize() - original_size >= max_matches)
+ break;
+ } else
+ m_index->ReportInvalidDIERef(die_ref, regex.GetText());
+ }
+ }
+
+ // Return the number of variable that were appended to the list
+ return variables.GetSize() - original_size;
+}
+
+bool SymbolFileDWARF::ResolveFunction(const DWARFDIE &orig_die,
+ bool include_inlines,
+ SymbolContextList &sc_list) {
+ SymbolContext sc;
+
+ if (!orig_die)
+ return false;
+
+ // If we were passed a die that is not a function, just return false...
+ if (!(orig_die.Tag() == DW_TAG_subprogram ||
+ (include_inlines && orig_die.Tag() == DW_TAG_inlined_subroutine)))
+ return false;
+
+ DWARFDIE die = orig_die;
+ DWARFDIE inlined_die;
+ if (die.Tag() == DW_TAG_inlined_subroutine) {
+ inlined_die = die;
+
+ while (true) {
+ die = die.GetParent();
+
+ if (die) {
+ if (die.Tag() == DW_TAG_subprogram)
+ break;
+ } else
+ break;
+ }
+ }
+ assert(die && die.Tag() == DW_TAG_subprogram);
+ if (GetFunction(die, sc)) {
+ Address addr;
+ // Parse all blocks if needed
+ if (inlined_die) {
+ Block &function_block = sc.function->GetBlock(true);
+ sc.block = function_block.FindBlockByID(inlined_die.GetID());
+ if (sc.block == nullptr)
+ sc.block = function_block.FindBlockByID(inlined_die.GetOffset());
+ if (sc.block == nullptr || !sc.block->GetStartAddress(addr))
+ addr.Clear();
+ } else {
+ sc.block = nullptr;
+ addr = sc.function->GetAddressRange().GetBaseAddress();
+ }
+
+ if (addr.IsValid()) {
+ sc_list.Append(sc);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool SymbolFileDWARF::DIEInDeclContext(const CompilerDeclContext *decl_ctx,
+ const DWARFDIE &die) {
+ // If we have no parent decl context to match this DIE matches, and if the
+ // parent decl context isn't valid, we aren't trying to look for any
+ // particular decl context so any die matches.
+ if (decl_ctx == nullptr || !decl_ctx->IsValid())
+ return true;
+
+ if (die) {
+ DWARFASTParser *dwarf_ast = die.GetDWARFParser();
+ if (dwarf_ast) {
+ CompilerDeclContext actual_decl_ctx =
+ dwarf_ast->GetDeclContextContainingUIDFromDWARF(die);
+ if (actual_decl_ctx)
+ return decl_ctx->IsContainedInLookup(actual_decl_ctx);
+ }
+ }
+ return false;
+}
+
+uint32_t SymbolFileDWARF::FindFunctions(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ FunctionNameType name_type_mask, bool include_inlines, bool append,
+ SymbolContextList &sc_list) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "SymbolFileDWARF::FindFunctions (name = '%s')",
+ name.AsCString());
+
+ // eFunctionNameTypeAuto should be pre-resolved by a call to
+ // Module::LookupInfo::LookupInfo()
+ assert((name_type_mask & eFunctionNameTypeAuto) == 0);
+
+ Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS));
+
+ if (log) {
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::FindFunctions (name=\"%s\", "
+ "name_type_mask=0x%x, append=%u, sc_list)",
+ name.GetCString(), name_type_mask, append);
+ }
+
+ // If we aren't appending the results to this list, then clear the list
+ if (!append)
+ sc_list.Clear();
+
+ if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx))
+ return 0;
+
+ // If name is empty then we won't find anything.
+ if (name.IsEmpty())
+ return 0;
+
+ // Remember how many sc_list are in the list before we search in case we are
+ // appending the results to a variable list.
+
+ const uint32_t original_size = sc_list.GetSize();
+
+ llvm::DenseSet<const DWARFDebugInfoEntry *> resolved_dies;
+ DIEArray offsets;
+ CompilerDeclContext empty_decl_ctx;
+ if (!parent_decl_ctx)
+ parent_decl_ctx = &empty_decl_ctx;
+
+ std::vector<DWARFDIE> dies;
+ m_index->GetFunctions(name, *this, *parent_decl_ctx, name_type_mask, dies);
+ for (const DWARFDIE &die: dies) {
+ if (resolved_dies.insert(die.GetDIE()).second)
+ ResolveFunction(die, include_inlines, sc_list);
+ }
+
+ // Return the number of variable that were appended to the list
+ const uint32_t num_matches = sc_list.GetSize() - original_size;
+
+ if (log && num_matches > 0) {
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::FindFunctions (name=\"%s\", "
+ "name_type_mask=0x%x, include_inlines=%d, append=%u, sc_list) => "
+ "%u",
+ name.GetCString(), name_type_mask, include_inlines, append,
+ num_matches);
+ }
+ return num_matches;
+}
+
+uint32_t SymbolFileDWARF::FindFunctions(const RegularExpression &regex,
+ bool include_inlines, bool append,
+ SymbolContextList &sc_list) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "SymbolFileDWARF::FindFunctions (regex = '%s')",
+ regex.GetText().str().c_str());
+
+ Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS));
+
+ if (log) {
+ GetObjectFile()->GetModule()->LogMessage(
+ log,
+ "SymbolFileDWARF::FindFunctions (regex=\"%s\", append=%u, sc_list)",
+ regex.GetText().str().c_str(), append);
+ }
+
+ // If we aren't appending the results to this list, then clear the list
+ if (!append)
+ sc_list.Clear();
+
+ DWARFDebugInfo *info = DebugInfo();
+ if (!info)
+ return 0;
+
+ // Remember how many sc_list are in the list before we search in case we are
+ // appending the results to a variable list.
+ uint32_t original_size = sc_list.GetSize();
+
+ DIEArray offsets;
+ m_index->GetFunctions(regex, offsets);
+
+ llvm::DenseSet<const DWARFDebugInfoEntry *> resolved_dies;
+ for (DIERef ref : offsets) {
+ DWARFDIE die = info->GetDIE(ref);
+ if (!die) {
+ m_index->ReportInvalidDIERef(ref, regex.GetText());
+ continue;
+ }
+ if (resolved_dies.insert(die.GetDIE()).second)
+ ResolveFunction(die, include_inlines, sc_list);
+ }
+
+ // Return the number of variable that were appended to the list
+ return sc_list.GetSize() - original_size;
+}
+
+void SymbolFileDWARF::GetMangledNamesForFunction(
+ const std::string &scope_qualified_name,
+ std::vector<ConstString> &mangled_names) {
+ DWARFDebugInfo *info = DebugInfo();
+ uint32_t num_comp_units = 0;
+ if (info)
+ num_comp_units = info->GetNumUnits();
+
+ for (uint32_t i = 0; i < num_comp_units; i++) {
+ DWARFUnit *cu = info->GetUnitAtIndex(i);
+ if (cu == nullptr)
+ continue;
+
+ SymbolFileDWARFDwo *dwo = cu->GetDwoSymbolFile();
+ if (dwo)
+ dwo->GetMangledNamesForFunction(scope_qualified_name, mangled_names);
+ }
+
+ for (lldb::user_id_t uid :
+ m_function_scope_qualified_name_map.lookup(scope_qualified_name)) {
+ DWARFDIE die = GetDIE(uid);
+ mangled_names.push_back(ConstString(die.GetMangledName()));
+ }
+}
+
+uint32_t SymbolFileDWARF::FindTypes(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ bool append, uint32_t max_matches,
+ llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files,
+ TypeMap &types) {
+ // If we aren't appending the results to this list, then clear the list
+ if (!append)
+ types.Clear();
+
+ // Make sure we haven't already searched this SymbolFile before...
+ if (searched_symbol_files.count(this))
+ return 0;
+ else
+ searched_symbol_files.insert(this);
+
+ DWARFDebugInfo *info = DebugInfo();
+ if (info == nullptr)
+ return 0;
+
+ Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS));
+
+ if (log) {
+ if (parent_decl_ctx)
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx = "
+ "%p (\"%s\"), append=%u, max_matches=%u, type_list)",
+ name.GetCString(), static_cast<const void *>(parent_decl_ctx),
+ parent_decl_ctx->GetName().AsCString("<NULL>"), append, max_matches);
+ else
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx = "
+ "NULL, append=%u, max_matches=%u, type_list)",
+ name.GetCString(), append, max_matches);
+ }
+
+ if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx))
+ return 0;
+
+ DIEArray die_offsets;
+ m_index->GetTypes(name, die_offsets);
+ const size_t num_die_matches = die_offsets.size();
+
+ if (num_die_matches) {
+ const uint32_t initial_types_size = types.GetSize();
+ for (size_t i = 0; i < num_die_matches; ++i) {
+ const DIERef &die_ref = die_offsets[i];
+ DWARFDIE die = GetDIE(die_ref);
+
+ if (die) {
+ if (!DIEInDeclContext(parent_decl_ctx, die))
+ continue; // The containing decl contexts don't match
+
+ Type *matching_type = ResolveType(die, true, true);
+ if (matching_type) {
+ // We found a type pointer, now find the shared pointer form our type
+ // list
+ types.InsertUnique(matching_type->shared_from_this());
+ if (types.GetSize() >= max_matches)
+ break;
+ }
+ } else {
+ m_index->ReportInvalidDIERef(die_ref, name.GetStringRef());
+ }
+ }
+ const uint32_t num_matches = types.GetSize() - initial_types_size;
+ if (log && num_matches) {
+ if (parent_decl_ctx) {
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx "
+ "= %p (\"%s\"), append=%u, max_matches=%u, type_list) => %u",
+ name.GetCString(), static_cast<const void *>(parent_decl_ctx),
+ parent_decl_ctx->GetName().AsCString("<NULL>"), append, max_matches,
+ num_matches);
+ } else {
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx "
+ "= NULL, append=%u, max_matches=%u, type_list) => %u",
+ name.GetCString(), append, max_matches, num_matches);
+ }
+ }
+ return num_matches;
+ } else {
+ UpdateExternalModuleListIfNeeded();
+
+ for (const auto &pair : m_external_type_modules) {
+ ModuleSP external_module_sp = pair.second;
+ if (external_module_sp) {
+ SymbolVendor *sym_vendor = external_module_sp->GetSymbolVendor();
+ if (sym_vendor) {
+ const uint32_t num_external_matches =
+ sym_vendor->FindTypes(name, parent_decl_ctx, append, max_matches,
+ searched_symbol_files, types);
+ if (num_external_matches)
+ return num_external_matches;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+size_t SymbolFileDWARF::FindTypes(const std::vector<CompilerContext> &context,
+ bool append, TypeMap &types) {
+ if (!append)
+ types.Clear();
+
+ if (context.empty())
+ return 0;
+
+ ConstString name = context.back().name;
+
+ if (!name)
+ return 0;
+
+ DIEArray die_offsets;
+ m_index->GetTypes(name, die_offsets);
+ const size_t num_die_matches = die_offsets.size();
+
+ if (num_die_matches) {
+ size_t num_matches = 0;
+ for (size_t i = 0; i < num_die_matches; ++i) {
+ const DIERef &die_ref = die_offsets[i];
+ DWARFDIE die = GetDIE(die_ref);
+
+ if (die) {
+ std::vector<CompilerContext> die_context;
+ die.GetDeclContext(die_context);
+ if (die_context != context)
+ continue;
+
+ Type *matching_type = ResolveType(die, true, true);
+ if (matching_type) {
+ // We found a type pointer, now find the shared pointer form our type
+ // list
+ types.InsertUnique(matching_type->shared_from_this());
+ ++num_matches;
+ }
+ } else {
+ m_index->ReportInvalidDIERef(die_ref, name.GetStringRef());
+ }
+ }
+ return num_matches;
+ }
+ return 0;
+}
+
+CompilerDeclContext
+SymbolFileDWARF::FindNamespace(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx) {
+ Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS));
+
+ if (log) {
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::FindNamespace (sc, name=\"%s\")",
+ name.GetCString());
+ }
+
+ CompilerDeclContext namespace_decl_ctx;
+
+ if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx))
+ return namespace_decl_ctx;
+
+ DWARFDebugInfo *info = DebugInfo();
+ if (info) {
+ DIEArray die_offsets;
+ m_index->GetNamespaces(name, die_offsets);
+ const size_t num_matches = die_offsets.size();
+ if (num_matches) {
+ for (size_t i = 0; i < num_matches; ++i) {
+ const DIERef &die_ref = die_offsets[i];
+ DWARFDIE die = GetDIE(die_ref);
+
+ if (die) {
+ if (!DIEInDeclContext(parent_decl_ctx, die))
+ continue; // The containing decl contexts don't match
+
+ DWARFASTParser *dwarf_ast = die.GetDWARFParser();
+ if (dwarf_ast) {
+ namespace_decl_ctx = dwarf_ast->GetDeclContextForUIDFromDWARF(die);
+ if (namespace_decl_ctx)
+ break;
+ }
+ } else {
+ m_index->ReportInvalidDIERef(die_ref, name.GetStringRef());
+ }
+ }
+ }
+ }
+ if (log && namespace_decl_ctx) {
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::FindNamespace (sc, name=\"%s\") => "
+ "CompilerDeclContext(%p/%p) \"%s\"",
+ name.GetCString(),
+ static_cast<const void *>(namespace_decl_ctx.GetTypeSystem()),
+ static_cast<const void *>(namespace_decl_ctx.GetOpaqueDeclContext()),
+ namespace_decl_ctx.GetName().AsCString("<NULL>"));
+ }
+
+ return namespace_decl_ctx;
+}
+
+TypeSP SymbolFileDWARF::GetTypeForDIE(const DWARFDIE &die,
+ bool resolve_function_context) {
+ TypeSP type_sp;
+ if (die) {
+ Type *type_ptr = GetDIEToType().lookup(die.GetDIE());
+ if (type_ptr == nullptr) {
+ SymbolContextScope *scope;
+ if (auto *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(die.GetCU()))
+ scope = GetCompUnitForDWARFCompUnit(*dwarf_cu);
+ else
+ scope = GetObjectFile()->GetModule().get();
+ assert(scope);
+ SymbolContext sc(scope);
+ const DWARFDebugInfoEntry *parent_die = die.GetParent().GetDIE();
+ while (parent_die != nullptr) {
+ if (parent_die->Tag() == DW_TAG_subprogram)
+ break;
+ parent_die = parent_die->GetParent();
+ }
+ SymbolContext sc_backup = sc;
+ if (resolve_function_context && parent_die != nullptr &&
+ !GetFunction(DWARFDIE(die.GetCU(), parent_die), sc))
+ sc = sc_backup;
+
+ type_sp = ParseType(sc, die, nullptr);
+ } else if (type_ptr != DIE_IS_BEING_PARSED) {
+ // Grab the existing type from the master types lists
+ type_sp = type_ptr->shared_from_this();
+ }
+ }
+ return type_sp;
+}
+
+DWARFDIE
+SymbolFileDWARF::GetDeclContextDIEContainingDIE(const DWARFDIE &orig_die) {
+ if (orig_die) {
+ DWARFDIE die = orig_die;
+
+ while (die) {
+ // If this is the original DIE that we are searching for a declaration
+ // for, then don't look in the cache as we don't want our own decl
+ // context to be our decl context...
+ if (orig_die != die) {
+ switch (die.Tag()) {
+ case DW_TAG_compile_unit:
+ case DW_TAG_partial_unit:
+ case DW_TAG_namespace:
+ case DW_TAG_structure_type:
+ case DW_TAG_union_type:
+ case DW_TAG_class_type:
+ case DW_TAG_lexical_block:
+ case DW_TAG_subprogram:
+ return die;
+ case DW_TAG_inlined_subroutine: {
+ DWARFDIE abs_die = die.GetReferencedDIE(DW_AT_abstract_origin);
+ if (abs_die) {
+ return abs_die;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ DWARFDIE spec_die = die.GetReferencedDIE(DW_AT_specification);
+ if (spec_die) {
+ DWARFDIE decl_ctx_die = GetDeclContextDIEContainingDIE(spec_die);
+ if (decl_ctx_die)
+ return decl_ctx_die;
+ }
+
+ DWARFDIE abs_die = die.GetReferencedDIE(DW_AT_abstract_origin);
+ if (abs_die) {
+ DWARFDIE decl_ctx_die = GetDeclContextDIEContainingDIE(abs_die);
+ if (decl_ctx_die)
+ return decl_ctx_die;
+ }
+
+ die = die.GetParent();
+ }
+ }
+ return DWARFDIE();
+}
+
+Symbol *
+SymbolFileDWARF::GetObjCClassSymbol(ConstString objc_class_name) {
+ Symbol *objc_class_symbol = nullptr;
+ if (m_obj_file) {
+ Symtab *symtab = m_obj_file->GetSymtab();
+ if (symtab) {
+ objc_class_symbol = symtab->FindFirstSymbolWithNameAndType(
+ objc_class_name, eSymbolTypeObjCClass, Symtab::eDebugNo,
+ Symtab::eVisibilityAny);
+ }
+ }
+ return objc_class_symbol;
+}
+
+// Some compilers don't emit the DW_AT_APPLE_objc_complete_type attribute. If
+// they don't then we can end up looking through all class types for a complete
+// type and never find the full definition. We need to know if this attribute
+// is supported, so we determine this here and cache th result. We also need to
+// worry about the debug map
+// DWARF file
+// if we are doing darwin DWARF in .o file debugging.
+bool SymbolFileDWARF::Supports_DW_AT_APPLE_objc_complete_type(
+ DWARFUnit *cu) {
+ if (m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolCalculate) {
+ m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolNo;
+ if (cu && cu->Supports_DW_AT_APPLE_objc_complete_type())
+ m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolYes;
+ else {
+ DWARFDebugInfo *debug_info = DebugInfo();
+ const uint32_t num_compile_units = GetNumCompileUnits();
+ for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) {
+ DWARFUnit *dwarf_cu = debug_info->GetUnitAtIndex(cu_idx);
+ if (dwarf_cu != cu &&
+ dwarf_cu->Supports_DW_AT_APPLE_objc_complete_type()) {
+ m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolYes;
+ break;
+ }
+ }
+ }
+ if (m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolNo &&
+ GetDebugMapSymfile())
+ return m_debug_map_symfile->Supports_DW_AT_APPLE_objc_complete_type(this);
+ }
+ return m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolYes;
+}
+
+// This function can be used when a DIE is found that is a forward declaration
+// DIE and we want to try and find a type that has the complete definition.
+TypeSP SymbolFileDWARF::FindCompleteObjCDefinitionTypeForDIE(
+ const DWARFDIE &die, ConstString type_name,
+ bool must_be_implementation) {
+
+ TypeSP type_sp;
+
+ if (!type_name || (must_be_implementation && !GetObjCClassSymbol(type_name)))
+ return type_sp;
+
+ DIEArray die_offsets;
+ m_index->GetCompleteObjCClass(type_name, must_be_implementation, die_offsets);
+
+ const size_t num_matches = die_offsets.size();
+
+ if (num_matches) {
+ for (size_t i = 0; i < num_matches; ++i) {
+ const DIERef &die_ref = die_offsets[i];
+ DWARFDIE type_die = GetDIE(die_ref);
+
+ if (type_die) {
+ bool try_resolving_type = false;
+
+ // Don't try and resolve the DIE we are looking for with the DIE
+ // itself!
+ if (type_die != die) {
+ switch (type_die.Tag()) {
+ case DW_TAG_class_type:
+ case DW_TAG_structure_type:
+ try_resolving_type = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (try_resolving_type) {
+ if (must_be_implementation &&
+ type_die.Supports_DW_AT_APPLE_objc_complete_type())
+ try_resolving_type = type_die.GetAttributeValueAsUnsigned(
+ DW_AT_APPLE_objc_complete_type, 0);
+
+ if (try_resolving_type) {
+ Type *resolved_type = ResolveType(type_die, false, true);
+ if (resolved_type && resolved_type != DIE_IS_BEING_PARSED) {
+ DEBUG_PRINTF("resolved 0x%8.8" PRIx64 " from %s to 0x%8.8" PRIx64
+ " (cu 0x%8.8" PRIx64 ")\n",
+ die.GetID(),
+ m_obj_file->GetFileSpec().GetFilename().AsCString(
+ "<Unknown>"),
+ type_die.GetID(), type_cu->GetID());
+
+ if (die)
+ GetDIEToType()[die.GetDIE()] = resolved_type;
+ type_sp = resolved_type->shared_from_this();
+ break;
+ }
+ }
+ }
+ } else {
+ m_index->ReportInvalidDIERef(die_ref, type_name.GetStringRef());
+ }
+ }
+ }
+ return type_sp;
+}
+
+// This function helps to ensure that the declaration contexts match for two
+// different DIEs. Often times debug information will refer to a forward
+// declaration of a type (the equivalent of "struct my_struct;". There will
+// often be a declaration of that type elsewhere that has the full definition.
+// When we go looking for the full type "my_struct", we will find one or more
+// matches in the accelerator tables and we will then need to make sure the
+// type was in the same declaration context as the original DIE. This function
+// can efficiently compare two DIEs and will return true when the declaration
+// context matches, and false when they don't.
+bool SymbolFileDWARF::DIEDeclContextsMatch(const DWARFDIE &die1,
+ const DWARFDIE &die2) {
+ if (die1 == die2)
+ return true;
+
+ std::vector<DWARFDIE> decl_ctx_1;
+ std::vector<DWARFDIE> decl_ctx_2;
+ // The declaration DIE stack is a stack of the declaration context DIEs all
+ // the way back to the compile unit. If a type "T" is declared inside a class
+ // "B", and class "B" is declared inside a class "A" and class "A" is in a
+ // namespace "lldb", and the namespace is in a compile unit, there will be a
+ // stack of DIEs:
+ //
+ // [0] DW_TAG_class_type for "B"
+ // [1] DW_TAG_class_type for "A"
+ // [2] DW_TAG_namespace for "lldb"
+ // [3] DW_TAG_compile_unit or DW_TAG_partial_unit for the source file.
+ //
+ // We grab both contexts and make sure that everything matches all the way
+ // back to the compiler unit.
+
+ // First lets grab the decl contexts for both DIEs
+ decl_ctx_1 = die1.GetDeclContextDIEs();
+ decl_ctx_2 = die2.GetDeclContextDIEs();
+ // Make sure the context arrays have the same size, otherwise we are done
+ const size_t count1 = decl_ctx_1.size();
+ const size_t count2 = decl_ctx_2.size();
+ if (count1 != count2)
+ return false;
+
+ // Make sure the DW_TAG values match all the way back up the compile unit. If
+ // they don't, then we are done.
+ DWARFDIE decl_ctx_die1;
+ DWARFDIE decl_ctx_die2;
+ size_t i;
+ for (i = 0; i < count1; i++) {
+ decl_ctx_die1 = decl_ctx_1[i];
+ decl_ctx_die2 = decl_ctx_2[i];
+ if (decl_ctx_die1.Tag() != decl_ctx_die2.Tag())
+ return false;
+ }
+#ifndef NDEBUG
+
+ // Make sure the top item in the decl context die array is always
+ // DW_TAG_compile_unit or DW_TAG_partial_unit. If it isn't then
+ // something went wrong in the DWARFDIE::GetDeclContextDIEs()
+ // function.
+ dw_tag_t cu_tag = decl_ctx_1[count1 - 1].Tag();
+ UNUSED_IF_ASSERT_DISABLED(cu_tag);
+ assert(cu_tag == DW_TAG_compile_unit || cu_tag == DW_TAG_partial_unit);
+
+#endif
+ // Always skip the compile unit when comparing by only iterating up to "count
+ // - 1". Here we compare the names as we go.
+ for (i = 0; i < count1 - 1; i++) {
+ decl_ctx_die1 = decl_ctx_1[i];
+ decl_ctx_die2 = decl_ctx_2[i];
+ const char *name1 = decl_ctx_die1.GetName();
+ const char *name2 = decl_ctx_die2.GetName();
+ // If the string was from a DW_FORM_strp, then the pointer will often be
+ // the same!
+ if (name1 == name2)
+ continue;
+
+ // Name pointers are not equal, so only compare the strings if both are not
+ // NULL.
+ if (name1 && name2) {
+ // If the strings don't compare, we are done...
+ if (strcmp(name1, name2) != 0)
+ return false;
+ } else {
+ // One name was NULL while the other wasn't
+ return false;
+ }
+ }
+ // We made it through all of the checks and the declaration contexts are
+ // equal.
+ return true;
+}
+
+TypeSP SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext(
+ const DWARFDeclContext &dwarf_decl_ctx) {
+ TypeSP type_sp;
+
+ const uint32_t dwarf_decl_ctx_count = dwarf_decl_ctx.GetSize();
+ if (dwarf_decl_ctx_count > 0) {
+ const ConstString type_name(dwarf_decl_ctx[0].name);
+ const dw_tag_t tag = dwarf_decl_ctx[0].tag;
+
+ if (type_name) {
+ Log *log(LogChannelDWARF::GetLogIfAny(DWARF_LOG_TYPE_COMPLETION |
+ DWARF_LOG_LOOKUPS));
+ if (log) {
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext(tag=%"
+ "s, qualified-name='%s')",
+ DW_TAG_value_to_name(dwarf_decl_ctx[0].tag),
+ dwarf_decl_ctx.GetQualifiedName());
+ }
+
+ DIEArray die_offsets;
+ m_index->GetTypes(dwarf_decl_ctx, die_offsets);
+ const size_t num_matches = die_offsets.size();
+
+ // Get the type system that we are looking to find a type for. We will
+ // use this to ensure any matches we find are in a language that this
+ // type system supports
+ const LanguageType language = dwarf_decl_ctx.GetLanguage();
+ TypeSystem *type_system = (language == eLanguageTypeUnknown)
+ ? nullptr
+ : GetTypeSystemForLanguage(language);
+
+ if (num_matches) {
+ for (size_t i = 0; i < num_matches; ++i) {
+ const DIERef &die_ref = die_offsets[i];
+ DWARFDIE type_die = GetDIE(die_ref);
+
+ if (type_die) {
+ // Make sure type_die's langauge matches the type system we are
+ // looking for. We don't want to find a "Foo" type from Java if we
+ // are looking for a "Foo" type for C, C++, ObjC, or ObjC++.
+ if (type_system &&
+ !type_system->SupportsLanguage(type_die.GetLanguage()))
+ continue;
+ bool try_resolving_type = false;
+
+ // Don't try and resolve the DIE we are looking for with the DIE
+ // itself!
+ const dw_tag_t type_tag = type_die.Tag();
+ // Make sure the tags match
+ if (type_tag == tag) {
+ // The tags match, lets try resolving this type
+ try_resolving_type = true;
+ } else {
+ // The tags don't match, but we need to watch our for a forward
+ // declaration for a struct and ("struct foo") ends up being a
+ // class ("class foo { ... };") or vice versa.
+ switch (type_tag) {
+ case DW_TAG_class_type:
+ // We had a "class foo", see if we ended up with a "struct foo
+ // { ... };"
+ try_resolving_type = (tag == DW_TAG_structure_type);
+ break;
+ case DW_TAG_structure_type:
+ // We had a "struct foo", see if we ended up with a "class foo
+ // { ... };"
+ try_resolving_type = (tag == DW_TAG_class_type);
+ break;
+ default:
+ // Tags don't match, don't event try to resolve using this type
+ // whose name matches....
+ break;
+ }
+ }
+
+ if (try_resolving_type) {
+ DWARFDeclContext type_dwarf_decl_ctx;
+ type_die.GetDWARFDeclContext(type_dwarf_decl_ctx);
+
+ if (log) {
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::"
+ "FindDefinitionTypeForDWARFDeclContext(tag=%s, "
+ "qualified-name='%s') trying die=0x%8.8x (%s)",
+ DW_TAG_value_to_name(dwarf_decl_ctx[0].tag),
+ dwarf_decl_ctx.GetQualifiedName(), type_die.GetOffset(),
+ type_dwarf_decl_ctx.GetQualifiedName());
+ }
+
+ // Make sure the decl contexts match all the way up
+ if (dwarf_decl_ctx == type_dwarf_decl_ctx) {
+ Type *resolved_type = ResolveType(type_die, false);
+ if (resolved_type && resolved_type != DIE_IS_BEING_PARSED) {
+ type_sp = resolved_type->shared_from_this();
+ break;
+ }
+ }
+ } else {
+ if (log) {
+ std::string qualified_name;
+ type_die.GetQualifiedName(qualified_name);
+ GetObjectFile()->GetModule()->LogMessage(
+ log, "SymbolFileDWARF::"
+ "FindDefinitionTypeForDWARFDeclContext(tag=%s, "
+ "qualified-name='%s') ignoring die=0x%8.8x (%s)",
+ DW_TAG_value_to_name(dwarf_decl_ctx[0].tag),
+ dwarf_decl_ctx.GetQualifiedName(), type_die.GetOffset(),
+ qualified_name.c_str());
+ }
+ }
+ } else {
+ m_index->ReportInvalidDIERef(die_ref, type_name.GetStringRef());
+ }
+ }
+ }
+ }
+ }
+ return type_sp;
+}
+
+TypeSP SymbolFileDWARF::ParseType(const SymbolContext &sc, const DWARFDIE &die,
+ bool *type_is_new_ptr) {
+ if (!die)
+ return {};
+
+ TypeSystem *type_system =
+ GetTypeSystemForLanguage(die.GetCU()->GetLanguageType());
+ if (!type_system)
+ return {};
+
+ DWARFASTParser *dwarf_ast = type_system->GetDWARFParser();
+ if (!dwarf_ast)
+ return {};
+
+ Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO);
+ TypeSP type_sp = dwarf_ast->ParseTypeFromDWARF(sc, die, log, type_is_new_ptr);
+ if (type_sp) {
+ TypeList *type_list = GetTypeList();
+ if (type_list)
+ type_list->Insert(type_sp);
+
+ if (die.Tag() == DW_TAG_subprogram) {
+ std::string scope_qualified_name(GetDeclContextForUID(die.GetID())
+ .GetScopeQualifiedName()
+ .AsCString(""));
+ if (scope_qualified_name.size()) {
+ m_function_scope_qualified_name_map[scope_qualified_name].insert(
+ die.GetID());
+ }
+ }
+ }
+
+ return type_sp;
+}
+
+size_t SymbolFileDWARF::ParseTypes(const SymbolContext &sc,
+ const DWARFDIE &orig_die,
+ bool parse_siblings, bool parse_children) {
+ size_t types_added = 0;
+ DWARFDIE die = orig_die;
+ while (die) {
+ bool type_is_new = false;
+ if (ParseType(sc, die, &type_is_new).get()) {
+ if (type_is_new)
+ ++types_added;
+ }
+
+ if (parse_children && die.HasChildren()) {
+ if (die.Tag() == DW_TAG_subprogram) {
+ SymbolContext child_sc(sc);
+ child_sc.function = sc.comp_unit->FindFunctionByUID(die.GetID()).get();
+ types_added += ParseTypes(child_sc, die.GetFirstChild(), true, true);
+ } else
+ types_added += ParseTypes(sc, die.GetFirstChild(), true, true);
+ }
+
+ if (parse_siblings)
+ die = die.GetSibling();
+ else
+ die.Clear();
+ }
+ return types_added;
+}
+
+size_t SymbolFileDWARF::ParseBlocksRecursive(Function &func) {
+ ASSERT_MODULE_LOCK(this);
+ CompileUnit *comp_unit = func.GetCompileUnit();
+ lldbassert(comp_unit);
+
+ DWARFUnit *dwarf_cu = GetDWARFCompileUnit(comp_unit);
+ if (!dwarf_cu)
+ return 0;
+
+ size_t functions_added = 0;
+ const dw_offset_t function_die_offset = func.GetID();
+ DWARFDIE function_die = dwarf_cu->GetDIE(function_die_offset);
+ if (function_die) {
+ ParseBlocksRecursive(*comp_unit, &func.GetBlock(false), function_die,
+ LLDB_INVALID_ADDRESS, 0);
+ }
+
+ return functions_added;
+}
+
+size_t SymbolFileDWARF::ParseTypes(CompileUnit &comp_unit) {
+ ASSERT_MODULE_LOCK(this);
+ size_t types_added = 0;
+ DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit);
+ if (dwarf_cu) {
+ DWARFDIE dwarf_cu_die = dwarf_cu->DIE();
+ if (dwarf_cu_die && dwarf_cu_die.HasChildren()) {
+ SymbolContext sc;
+ sc.comp_unit = &comp_unit;
+ types_added = ParseTypes(sc, dwarf_cu_die.GetFirstChild(), true, true);
+ }
+ }
+
+ return types_added;
+}
+
+size_t SymbolFileDWARF::ParseVariablesForContext(const SymbolContext &sc) {
+ ASSERT_MODULE_LOCK(this);
+ if (sc.comp_unit != nullptr) {
+ DWARFDebugInfo *info = DebugInfo();
+ if (info == nullptr)
+ return 0;
+
+ if (sc.function) {
+ DWARFDIE function_die = GetDIE(sc.function->GetID());
+
+ const dw_addr_t func_lo_pc = function_die.GetAttributeValueAsAddress(
+ DW_AT_low_pc, LLDB_INVALID_ADDRESS);
+ if (func_lo_pc != LLDB_INVALID_ADDRESS) {
+ const size_t num_variables = ParseVariables(
+ sc, function_die.GetFirstChild(), func_lo_pc, true, true);
+
+ // Let all blocks know they have parse all their variables
+ sc.function->GetBlock(false).SetDidParseVariables(true, true);
+ return num_variables;
+ }
+ } else if (sc.comp_unit) {
+ DWARFUnit *dwarf_cu = info->GetUnitAtIndex(sc.comp_unit->GetID());
+
+ if (dwarf_cu == nullptr)
+ return 0;
+
+ uint32_t vars_added = 0;
+ VariableListSP variables(sc.comp_unit->GetVariableList(false));
+
+ if (variables.get() == nullptr) {
+ variables = std::make_shared<VariableList>();
+ sc.comp_unit->SetVariableList(variables);
+
+ DIEArray die_offsets;
+ m_index->GetGlobalVariables(dwarf_cu->GetNonSkeletonUnit(),
+ die_offsets);
+ const size_t num_matches = die_offsets.size();
+ if (num_matches) {
+ for (size_t i = 0; i < num_matches; ++i) {
+ const DIERef &die_ref = die_offsets[i];
+ DWARFDIE die = GetDIE(die_ref);
+ if (die) {
+ VariableSP var_sp(
+ ParseVariableDIE(sc, die, LLDB_INVALID_ADDRESS));
+ if (var_sp) {
+ variables->AddVariableIfUnique(var_sp);
+ ++vars_added;
+ }
+ } else
+ m_index->ReportInvalidDIERef(die_ref, "");
+ }
+ }
+ }
+ return vars_added;
+ }
+ }
+ return 0;
+}
+
+VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc,
+ const DWARFDIE &die,
+ const lldb::addr_t func_low_pc) {
+ if (die.GetDWARF() != this)
+ return die.GetDWARF()->ParseVariableDIE(sc, die, func_low_pc);
+
+ VariableSP var_sp;
+ if (!die)
+ return var_sp;
+
+ var_sp = GetDIEToVariable()[die.GetDIE()];
+ if (var_sp)
+ return var_sp; // Already been parsed!
+
+ const dw_tag_t tag = die.Tag();
+ ModuleSP module = GetObjectFile()->GetModule();
+
+ if ((tag == DW_TAG_variable) || (tag == DW_TAG_constant) ||
+ (tag == DW_TAG_formal_parameter && sc.function)) {
+ DWARFAttributes attributes;
+ const size_t num_attributes = die.GetAttributes(attributes);
+ DWARFDIE spec_die;
+ if (num_attributes > 0) {
+ const char *name = nullptr;
+ const char *mangled = nullptr;
+ Declaration decl;
+ uint32_t i;
+ DWARFFormValue type_die_form;
+ DWARFExpression location;
+ bool is_external = false;
+ bool is_artificial = false;
+ bool location_is_const_value_data = false;
+ bool has_explicit_location = false;
+ DWARFFormValue const_value;
+ Variable::RangeList scope_ranges;
+ // AccessType accessibility = eAccessNone;
+
+ for (i = 0; i < num_attributes; ++i) {
+ dw_attr_t attr = attributes.AttributeAtIndex(i);
+ DWARFFormValue form_value;
+
+ if (attributes.ExtractFormValueAtIndex(i, form_value)) {
+ switch (attr) {
+ case DW_AT_decl_file:
+ decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(
+ form_value.Unsigned()));
+ break;
+ case DW_AT_decl_line:
+ decl.SetLine(form_value.Unsigned());
+ break;
+ case DW_AT_decl_column:
+ decl.SetColumn(form_value.Unsigned());
+ break;
+ case DW_AT_name:
+ name = form_value.AsCString();
+ break;
+ case DW_AT_linkage_name:
+ case DW_AT_MIPS_linkage_name:
+ mangled = form_value.AsCString();
+ break;
+ case DW_AT_type:
+ type_die_form = form_value;
+ break;
+ case DW_AT_external:
+ is_external = form_value.Boolean();
+ break;
+ case DW_AT_const_value:
+ // If we have already found a DW_AT_location attribute, ignore this
+ // attribute.
+ if (!has_explicit_location) {
+ location_is_const_value_data = true;
+ // The constant value will be either a block, a data value or a
+ // string.
+ auto debug_info_data = die.GetData();
+ if (DWARFFormValue::IsBlockForm(form_value.Form())) {
+ // Retrieve the value as a block expression.
+ uint32_t block_offset =
+ form_value.BlockData() - debug_info_data.GetDataStart();
+ uint32_t block_length = form_value.Unsigned();
+ location = DWARFExpression(module, debug_info_data, die.GetCU(),
+ block_offset, block_length);
+ } else if (DWARFFormValue::IsDataForm(form_value.Form())) {
+ // Retrieve the value as a data expression.
+ uint32_t data_offset = attributes.DIEOffsetAtIndex(i);
+ if (auto data_length = form_value.GetFixedSize())
+ location =
+ DWARFExpression(module, debug_info_data, die.GetCU(),
+ data_offset, *data_length);
+ else {
+ const uint8_t *data_pointer = form_value.BlockData();
+ if (data_pointer) {
+ form_value.Unsigned();
+ } else if (DWARFFormValue::IsDataForm(form_value.Form())) {
+ // we need to get the byte size of the type later after we
+ // create the variable
+ const_value = form_value;
+ }
+ }
+ } else {
+ // Retrieve the value as a string expression.
+ if (form_value.Form() == DW_FORM_strp) {
+ uint32_t data_offset = attributes.DIEOffsetAtIndex(i);
+ if (auto data_length = form_value.GetFixedSize())
+ location =
+ DWARFExpression(module, debug_info_data, die.GetCU(),
+ data_offset, *data_length);
+ } else {
+ const char *str = form_value.AsCString();
+ uint32_t string_offset =
+ str - (const char *)debug_info_data.GetDataStart();
+ uint32_t string_length = strlen(str) + 1;
+ location =
+ DWARFExpression(module, debug_info_data, die.GetCU(),
+ string_offset, string_length);
+ }
+ }
+ }
+ break;
+ case DW_AT_location: {
+ location_is_const_value_data = false;
+ has_explicit_location = true;
+ if (DWARFFormValue::IsBlockForm(form_value.Form())) {
+ auto data = die.GetData();
+
+ uint32_t block_offset =
+ form_value.BlockData() - data.GetDataStart();
+ uint32_t block_length = form_value.Unsigned();
+ location = DWARFExpression(module, data, die.GetCU(),
+ block_offset, block_length);
+ } else {
+ const DWARFDataExtractor &debug_loc_data = DebugLocData();
+ const dw_offset_t debug_loc_offset = form_value.Unsigned();
+
+ size_t loc_list_length = DWARFExpression::LocationListSize(
+ die.GetCU(), debug_loc_data, debug_loc_offset);
+ if (loc_list_length > 0) {
+ location = DWARFExpression(module, debug_loc_data, die.GetCU(),
+ debug_loc_offset, loc_list_length);
+ assert(func_low_pc != LLDB_INVALID_ADDRESS);
+ location.SetLocationListSlide(
+ func_low_pc -
+ attributes.CompileUnitAtIndex(i)->GetBaseAddress());
+ }
+ }
+ } break;
+ case DW_AT_specification:
+ spec_die = form_value.Reference();
+ break;
+ case DW_AT_start_scope:
+ // TODO: Implement this.
+ break;
+ case DW_AT_artificial:
+ is_artificial = form_value.Boolean();
+ break;
+ case DW_AT_accessibility:
+ break; // accessibility =
+ // DW_ACCESS_to_AccessType(form_value.Unsigned()); break;
+ case DW_AT_declaration:
+ case DW_AT_description:
+ case DW_AT_endianity:
+ case DW_AT_segment:
+ case DW_AT_visibility:
+ default:
+ case DW_AT_abstract_origin:
+ case DW_AT_sibling:
+ break;
+ }
+ }
+ }
+
+ const DWARFDIE parent_context_die = GetDeclContextDIEContainingDIE(die);
+ const dw_tag_t parent_tag = die.GetParent().Tag();
+ bool is_static_member =
+ (parent_tag == DW_TAG_compile_unit ||
+ parent_tag == DW_TAG_partial_unit) &&
+ (parent_context_die.Tag() == DW_TAG_class_type ||
+ parent_context_die.Tag() == DW_TAG_structure_type);
+
+ ValueType scope = eValueTypeInvalid;
+
+ const DWARFDIE sc_parent_die = GetParentSymbolContextDIE(die);
+ SymbolContextScope *symbol_context_scope = nullptr;
+
+ bool has_explicit_mangled = mangled != nullptr;
+ if (!mangled) {
+ // LLDB relies on the mangled name (DW_TAG_linkage_name or
+ // DW_AT_MIPS_linkage_name) to generate fully qualified names
+ // of global variables with commands like "frame var j". For
+ // example, if j were an int variable holding a value 4 and
+ // declared in a namespace B which in turn is contained in a
+ // namespace A, the command "frame var j" returns
+ // "(int) A::B::j = 4".
+ // If the compiler does not emit a linkage name, we should be
+ // able to generate a fully qualified name from the
+ // declaration context.
+ if ((parent_tag == DW_TAG_compile_unit ||
+ parent_tag == DW_TAG_partial_unit) &&
+ Language::LanguageIsCPlusPlus(die.GetLanguage())) {
+ DWARFDeclContext decl_ctx;
+
+ die.GetDWARFDeclContext(decl_ctx);
+ mangled = decl_ctx.GetQualifiedNameAsConstString().GetCString();
+ }
+ }
+
+ if (tag == DW_TAG_formal_parameter)
+ scope = eValueTypeVariableArgument;
+ else {
+ // DWARF doesn't specify if a DW_TAG_variable is a local, global
+ // or static variable, so we have to do a little digging:
+ // 1) DW_AT_linkage_name implies static lifetime (but may be missing)
+ // 2) An empty DW_AT_location is an (optimized-out) static lifetime var.
+ // 3) DW_AT_location containing a DW_OP_addr implies static lifetime.
+ // Clang likes to combine small global variables into the same symbol
+ // with locations like: DW_OP_addr(0x1000), DW_OP_constu(2), DW_OP_plus
+ // so we need to look through the whole expression.
+ bool is_static_lifetime =
+ has_explicit_mangled ||
+ (has_explicit_location && !location.IsValid());
+ // Check if the location has a DW_OP_addr with any address value...
+ lldb::addr_t location_DW_OP_addr = LLDB_INVALID_ADDRESS;
+ if (!location_is_const_value_data) {
+ bool op_error = false;
+ location_DW_OP_addr = location.GetLocation_DW_OP_addr(0, op_error);
+ if (op_error) {
+ StreamString strm;
+ location.DumpLocationForAddress(&strm, eDescriptionLevelFull, 0, 0,
+ nullptr);
+ GetObjectFile()->GetModule()->ReportError(
+ "0x%8.8x: %s has an invalid location: %s", die.GetOffset(),
+ die.GetTagAsCString(), strm.GetData());
+ }
+ if (location_DW_OP_addr != LLDB_INVALID_ADDRESS)
+ is_static_lifetime = true;
+ }
+ SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile();
+ if (debug_map_symfile)
+ // Set the module of the expression to the linked module
+ // instead of the oject file so the relocated address can be
+ // found there.
+ location.SetModule(debug_map_symfile->GetObjectFile()->GetModule());
+
+ if (is_static_lifetime) {
+ if (is_external)
+ scope = eValueTypeVariableGlobal;
+ else
+ scope = eValueTypeVariableStatic;
+
+ if (debug_map_symfile) {
+ // When leaving the DWARF in the .o files on darwin, when we have a
+ // global variable that wasn't initialized, the .o file might not
+ // have allocated a virtual address for the global variable. In
+ // this case it will have created a symbol for the global variable
+ // that is undefined/data and external and the value will be the
+ // byte size of the variable. When we do the address map in
+ // SymbolFileDWARFDebugMap we rely on having an address, we need to
+ // do some magic here so we can get the correct address for our
+ // global variable. The address for all of these entries will be
+ // zero, and there will be an undefined symbol in this object file,
+ // and the executable will have a matching symbol with a good
+ // address. So here we dig up the correct address and replace it in
+ // the location for the variable, and set the variable's symbol
+ // context scope to be that of the main executable so the file
+ // address will resolve correctly.
+ bool linked_oso_file_addr = false;
+ if (is_external && location_DW_OP_addr == 0) {
+ // we have a possible uninitialized extern global
+ ConstString const_name(mangled ? mangled : name);
+ ObjectFile *debug_map_objfile =
+ debug_map_symfile->GetObjectFile();
+ if (debug_map_objfile) {
+ Symtab *debug_map_symtab = debug_map_objfile->GetSymtab();
+ if (debug_map_symtab) {
+ Symbol *exe_symbol =
+ debug_map_symtab->FindFirstSymbolWithNameAndType(
+ const_name, eSymbolTypeData, Symtab::eDebugYes,
+ Symtab::eVisibilityExtern);
+ if (exe_symbol) {
+ if (exe_symbol->ValueIsAddress()) {
+ const addr_t exe_file_addr =
+ exe_symbol->GetAddressRef().GetFileAddress();
+ if (exe_file_addr != LLDB_INVALID_ADDRESS) {
+ if (location.Update_DW_OP_addr(exe_file_addr)) {
+ linked_oso_file_addr = true;
+ symbol_context_scope = exe_symbol;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (!linked_oso_file_addr) {
+ // The DW_OP_addr is not zero, but it contains a .o file address
+ // which needs to be linked up correctly.
+ const lldb::addr_t exe_file_addr =
+ debug_map_symfile->LinkOSOFileAddress(this,
+ location_DW_OP_addr);
+ if (exe_file_addr != LLDB_INVALID_ADDRESS) {
+ // Update the file address for this variable
+ location.Update_DW_OP_addr(exe_file_addr);
+ } else {
+ // Variable didn't make it into the final executable
+ return var_sp;
+ }
+ }
+ }
+ } else {
+ if (location_is_const_value_data)
+ scope = eValueTypeVariableStatic;
+ else {
+ scope = eValueTypeVariableLocal;
+ if (debug_map_symfile) {
+ // We need to check for TLS addresses that we need to fixup
+ if (location.ContainsThreadLocalStorage()) {
+ location.LinkThreadLocalStorage(
+ debug_map_symfile->GetObjectFile()->GetModule(),
+ [this, debug_map_symfile](
+ lldb::addr_t unlinked_file_addr) -> lldb::addr_t {
+ return debug_map_symfile->LinkOSOFileAddress(
+ this, unlinked_file_addr);
+ });
+ scope = eValueTypeVariableThreadLocal;
+ }
+ }
+ }
+ }
+ }
+
+ if (symbol_context_scope == nullptr) {
+ switch (parent_tag) {
+ case DW_TAG_subprogram:
+ case DW_TAG_inlined_subroutine:
+ case DW_TAG_lexical_block:
+ if (sc.function) {
+ symbol_context_scope = sc.function->GetBlock(true).FindBlockByID(
+ sc_parent_die.GetID());
+ if (symbol_context_scope == nullptr)
+ symbol_context_scope = sc.function;
+ }
+ break;
+
+ default:
+ symbol_context_scope = sc.comp_unit;
+ break;
+ }
+ }
+
+ if (symbol_context_scope) {
+ SymbolFileTypeSP type_sp(
+ new SymbolFileType(*this, GetUID(type_die_form.Reference())));
+
+ if (const_value.Form() && type_sp && type_sp->GetType())
+ location.UpdateValue(const_value.Unsigned(),
+ type_sp->GetType()->GetByteSize().getValueOr(0),
+ die.GetCU()->GetAddressByteSize());
+
+ var_sp = std::make_shared<Variable>(
+ die.GetID(), name, mangled, type_sp, scope, symbol_context_scope,
+ scope_ranges, &decl, location, is_external, is_artificial,
+ is_static_member);
+
+ var_sp->SetLocationIsConstantValueData(location_is_const_value_data);
+ } else {
+ // Not ready to parse this variable yet. It might be a global or static
+ // variable that is in a function scope and the function in the symbol
+ // context wasn't filled in yet
+ return var_sp;
+ }
+ }
+ // Cache var_sp even if NULL (the variable was just a specification or was
+ // missing vital information to be able to be displayed in the debugger
+ // (missing location due to optimization, etc)) so we don't re-parse this
+ // DIE over and over later...
+ GetDIEToVariable()[die.GetDIE()] = var_sp;
+ if (spec_die)
+ GetDIEToVariable()[spec_die.GetDIE()] = var_sp;
+ }
+ return var_sp;
+}
+
+DWARFDIE
+SymbolFileDWARF::FindBlockContainingSpecification(
+ const DIERef &func_die_ref, dw_offset_t spec_block_die_offset) {
+ // Give the concrete function die specified by "func_die_offset", find the
+ // concrete block whose DW_AT_specification or DW_AT_abstract_origin points
+ // to "spec_block_die_offset"
+ return FindBlockContainingSpecification(DebugInfo()->GetDIE(func_die_ref),
+ spec_block_die_offset);
+}
+
+DWARFDIE
+SymbolFileDWARF::FindBlockContainingSpecification(
+ const DWARFDIE &die, dw_offset_t spec_block_die_offset) {
+ if (die) {
+ switch (die.Tag()) {
+ case DW_TAG_subprogram:
+ case DW_TAG_inlined_subroutine:
+ case DW_TAG_lexical_block: {
+ if (die.GetReferencedDIE(DW_AT_specification).GetOffset() ==
+ spec_block_die_offset)
+ return die;
+
+ if (die.GetReferencedDIE(DW_AT_abstract_origin).GetOffset() ==
+ spec_block_die_offset)
+ return die;
+ } break;
+ }
+
+ // Give the concrete function die specified by "func_die_offset", find the
+ // concrete block whose DW_AT_specification or DW_AT_abstract_origin points
+ // to "spec_block_die_offset"
+ for (DWARFDIE child_die = die.GetFirstChild(); child_die;
+ child_die = child_die.GetSibling()) {
+ DWARFDIE result_die =
+ FindBlockContainingSpecification(child_die, spec_block_die_offset);
+ if (result_die)
+ return result_die;
+ }
+ }
+
+ return DWARFDIE();
+}
+
+size_t SymbolFileDWARF::ParseVariables(const SymbolContext &sc,
+ const DWARFDIE &orig_die,
+ const lldb::addr_t func_low_pc,
+ bool parse_siblings, bool parse_children,
+ VariableList *cc_variable_list) {
+ if (!orig_die)
+ return 0;
+
+ VariableListSP variable_list_sp;
+
+ size_t vars_added = 0;
+ DWARFDIE die = orig_die;
+ while (die) {
+ dw_tag_t tag = die.Tag();
+
+ // Check to see if we have already parsed this variable or constant?
+ VariableSP var_sp = GetDIEToVariable()[die.GetDIE()];
+ if (var_sp) {
+ if (cc_variable_list)
+ cc_variable_list->AddVariableIfUnique(var_sp);
+ } else {
+ // We haven't already parsed it, lets do that now.
+ if ((tag == DW_TAG_variable) || (tag == DW_TAG_constant) ||
+ (tag == DW_TAG_formal_parameter && sc.function)) {
+ if (variable_list_sp.get() == nullptr) {
+ DWARFDIE sc_parent_die = GetParentSymbolContextDIE(orig_die);
+ dw_tag_t parent_tag = sc_parent_die.Tag();
+ switch (parent_tag) {
+ case DW_TAG_compile_unit:
+ case DW_TAG_partial_unit:
+ if (sc.comp_unit != nullptr) {
+ variable_list_sp = sc.comp_unit->GetVariableList(false);
+ if (variable_list_sp.get() == nullptr) {
+ variable_list_sp = std::make_shared<VariableList>();
+ }
+ } else {
+ GetObjectFile()->GetModule()->ReportError(
+ "parent 0x%8.8" PRIx64 " %s with no valid compile unit in "
+ "symbol context for 0x%8.8" PRIx64
+ " %s.\n",
+ sc_parent_die.GetID(), sc_parent_die.GetTagAsCString(),
+ orig_die.GetID(), orig_die.GetTagAsCString());
+ }
+ break;
+
+ case DW_TAG_subprogram:
+ case DW_TAG_inlined_subroutine:
+ case DW_TAG_lexical_block:
+ if (sc.function != nullptr) {
+ // Check to see if we already have parsed the variables for the
+ // given scope
+
+ Block *block = sc.function->GetBlock(true).FindBlockByID(
+ sc_parent_die.GetID());
+ if (block == nullptr) {
+ // This must be a specification or abstract origin with a
+ // concrete block counterpart in the current function. We need
+ // to find the concrete block so we can correctly add the
+ // variable to it
+ const DWARFDIE concrete_block_die =
+ FindBlockContainingSpecification(
+ GetDIE(sc.function->GetID()),
+ sc_parent_die.GetOffset());
+ if (concrete_block_die)
+ block = sc.function->GetBlock(true).FindBlockByID(
+ concrete_block_die.GetID());
+ }
+
+ if (block != nullptr) {
+ const bool can_create = false;
+ variable_list_sp = block->GetBlockVariableList(can_create);
+ if (variable_list_sp.get() == nullptr) {
+ variable_list_sp = std::make_shared<VariableList>();
+ block->SetVariableList(variable_list_sp);
+ }
+ }
+ }
+ break;
+
+ default:
+ GetObjectFile()->GetModule()->ReportError(
+ "didn't find appropriate parent DIE for variable list for "
+ "0x%8.8" PRIx64 " %s.\n",
+ orig_die.GetID(), orig_die.GetTagAsCString());
+ break;
+ }
+ }
+
+ if (variable_list_sp) {
+ VariableSP var_sp(ParseVariableDIE(sc, die, func_low_pc));
+ if (var_sp) {
+ variable_list_sp->AddVariableIfUnique(var_sp);
+ if (cc_variable_list)
+ cc_variable_list->AddVariableIfUnique(var_sp);
+ ++vars_added;
+ }
+ }
+ }
+ }
+
+ bool skip_children = (sc.function == nullptr && tag == DW_TAG_subprogram);
+
+ if (!skip_children && parse_children && die.HasChildren()) {
+ vars_added += ParseVariables(sc, die.GetFirstChild(), func_low_pc, true,
+ true, cc_variable_list);
+ }
+
+ if (parse_siblings)
+ die = die.GetSibling();
+ else
+ die.Clear();
+ }
+ return vars_added;
+}
+
+/// Collect call graph edges present in a function DIE.
+static std::vector<lldb_private::CallEdge>
+CollectCallEdges(DWARFDIE function_die) {
+ // Check if the function has a supported call site-related attribute.
+ // TODO: In the future it may be worthwhile to support call_all_source_calls.
+ uint64_t has_call_edges =
+ function_die.GetAttributeValueAsUnsigned(DW_AT_call_all_calls, 0);
+ if (!has_call_edges)
+ return {};
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+ LLDB_LOG(log, "CollectCallEdges: Found call site info in {0}",
+ function_die.GetPubname());
+
+ // Scan the DIE for TAG_call_site entries.
+ // TODO: A recursive scan of all blocks in the subprogram is needed in order
+ // to be DWARF5-compliant. This may need to be done lazily to be performant.
+ // For now, assume that all entries are nested directly under the subprogram
+ // (this is the kind of DWARF LLVM produces) and parse them eagerly.
+ std::vector<CallEdge> call_edges;
+ for (DWARFDIE child = function_die.GetFirstChild(); child.IsValid();
+ child = child.GetSibling()) {
+ if (child.Tag() != DW_TAG_call_site)
+ continue;
+
+ // Extract DW_AT_call_origin (the call target's DIE).
+ DWARFDIE call_origin = child.GetReferencedDIE(DW_AT_call_origin);
+ if (!call_origin.IsValid()) {
+ LLDB_LOG(log, "CollectCallEdges: Invalid call origin in {0}",
+ function_die.GetPubname());
+ continue;
+ }
+
+ // Extract DW_AT_call_return_pc (the PC the call returns to) if it's
+ // available. It should only ever be unavailable for tail call edges, in
+ // which case use LLDB_INVALID_ADDRESS.
+ addr_t return_pc = child.GetAttributeValueAsAddress(DW_AT_call_return_pc,
+ LLDB_INVALID_ADDRESS);
+
+ LLDB_LOG(log, "CollectCallEdges: Found call origin: {0} (retn-PC: {1:x})",
+ call_origin.GetPubname(), return_pc);
+ call_edges.emplace_back(call_origin.GetMangledName(), return_pc);
+ }
+ return call_edges;
+}
+
+std::vector<lldb_private::CallEdge>
+SymbolFileDWARF::ParseCallEdgesInFunction(UserID func_id) {
+ DWARFDIE func_die = GetDIE(func_id.GetID());
+ if (func_die.IsValid())
+ return CollectCallEdges(func_die);
+ return {};
+}
+
+// PluginInterface protocol
+ConstString SymbolFileDWARF::GetPluginName() { return GetPluginNameStatic(); }
+
+uint32_t SymbolFileDWARF::GetPluginVersion() { return 1; }
+
+void SymbolFileDWARF::Dump(lldb_private::Stream &s) { m_index->Dump(s); }
+
+void SymbolFileDWARF::DumpClangAST(Stream &s) {
+ TypeSystem *ts = GetTypeSystemForLanguage(eLanguageTypeC_plus_plus);
+ ClangASTContext *clang = llvm::dyn_cast_or_null<ClangASTContext>(ts);
+ if (!clang)
+ return;
+ clang->Dump(s);
+}
+
+SymbolFileDWARFDebugMap *SymbolFileDWARF::GetDebugMapSymfile() {
+ if (m_debug_map_symfile == nullptr && !m_debug_map_module_wp.expired()) {
+ lldb::ModuleSP module_sp(m_debug_map_module_wp.lock());
+ if (module_sp) {
+ SymbolVendor *sym_vendor = module_sp->GetSymbolVendor();
+ if (sym_vendor)
+ m_debug_map_symfile =
+ (SymbolFileDWARFDebugMap *)sym_vendor->GetSymbolFile();
+ }
+ }
+ return m_debug_map_symfile;
+}
+
+DWARFExpression::LocationListFormat
+SymbolFileDWARF::GetLocationListFormat() const {
+ if (m_data_debug_loclists.m_data.GetByteSize() > 0)
+ return DWARFExpression::LocLists;
+ return DWARFExpression::RegularLocationList;
+}
+
+SymbolFileDWARFDwp *SymbolFileDWARF::GetDwpSymbolFile() {
+ llvm::call_once(m_dwp_symfile_once_flag, [this]() {
+ ModuleSpec module_spec;
+ module_spec.GetFileSpec() = m_obj_file->GetFileSpec();
+ module_spec.GetSymbolFileSpec() =
+ FileSpec(m_obj_file->GetFileSpec().GetPath() + ".dwp");
+
+ FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths();
+ FileSpec dwp_filespec =
+ Symbols::LocateExecutableSymbolFile(module_spec, search_paths);
+ if (FileSystem::Instance().Exists(dwp_filespec)) {
+ m_dwp_symfile = SymbolFileDWARFDwp::Create(GetObjectFile()->GetModule(),
+ dwp_filespec);
+ }
+ });
+ return m_dwp_symfile.get();
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
new file mode 100644
index 000000000000..018af47305f4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
@@ -0,0 +1,492 @@
+//===-- SymbolFileDWARF.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 SymbolFileDWARF_SymbolFileDWARF_h_
+#define SymbolFileDWARF_SymbolFileDWARF_h_
+
+#include <list>
+#include <map>
+#include <mutex>
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/Support/Threading.h"
+
+#include "lldb/Core/UniqueCStringMap.h"
+#include "lldb/Core/dwarf.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Symbol/DebugMacros.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Flags.h"
+#include "lldb/Utility/RangeMap.h"
+#include "lldb/lldb-private.h"
+
+#include "DWARFContext.h"
+#include "DWARFDataExtractor.h"
+#include "DWARFDefines.h"
+#include "DWARFIndex.h"
+#include "UniqueDWARFASTType.h"
+
+// Forward Declarations for this DWARF plugin
+class DebugMapModule;
+class DWARFAbbreviationDeclaration;
+class DWARFAbbreviationDeclarationSet;
+class DWARFCompileUnit;
+class DWARFDebugAbbrev;
+class DWARFDebugAranges;
+class DWARFDebugInfo;
+class DWARFDebugInfoEntry;
+class DWARFDebugLine;
+class DWARFDebugRangesBase;
+class DWARFDeclContext;
+class DWARFFormValue;
+class DWARFTypeUnit;
+class SymbolFileDWARFDebugMap;
+class SymbolFileDWARFDwo;
+class SymbolFileDWARFDwp;
+
+#define DIE_IS_BEING_PARSED ((lldb_private::Type *)1)
+
+class SymbolFileDWARF : public lldb_private::SymbolFile,
+ public lldb_private::UserID {
+public:
+ friend class SymbolFileDWARFDebugMap;
+ friend class SymbolFileDWARFDwo;
+ friend class DebugMapModule;
+ friend class DWARFCompileUnit;
+ friend class DWARFDIE;
+ friend class DWARFASTParserClang;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static void DebuggerInitialize(lldb_private::Debugger &debugger);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::SymbolFile *
+ CreateInstance(lldb_private::ObjectFile *obj_file);
+
+ static lldb_private::FileSpecList GetSymlinkPaths();
+
+ // Constructors and Destructors
+
+ SymbolFileDWARF(lldb_private::ObjectFile *ofile,
+ lldb_private::SectionList *dwo_section_list);
+
+ ~SymbolFileDWARF() override;
+
+ uint32_t CalculateAbilities() override;
+
+ void InitializeObject() override;
+
+ // Compile Unit function calls
+
+ uint32_t GetNumCompileUnits() override;
+
+ lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override;
+
+ lldb::LanguageType
+ ParseLanguage(lldb_private::CompileUnit &comp_unit) override;
+
+ size_t ParseFunctions(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseLineTable(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseDebugMacros(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseSupportFiles(lldb_private::CompileUnit &comp_unit,
+ lldb_private::FileSpecList &support_files) override;
+
+ bool ParseIsOptimized(lldb_private::CompileUnit &comp_unit) override;
+
+ size_t ParseTypes(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseImportedModules(
+ const lldb_private::SymbolContext &sc,
+ std::vector<lldb_private::SourceModule> &imported_modules) override;
+
+ size_t ParseBlocksRecursive(lldb_private::Function &func) override;
+
+ size_t
+ ParseVariablesForContext(const lldb_private::SymbolContext &sc) override;
+
+ lldb_private::Type *ResolveTypeUID(lldb::user_id_t type_uid) override;
+ llvm::Optional<ArrayInfo> GetDynamicArrayInfoForUID(
+ lldb::user_id_t type_uid,
+ const lldb_private::ExecutionContext *exe_ctx) override;
+
+ bool CompleteType(lldb_private::CompilerType &compiler_type) override;
+
+ lldb_private::Type *ResolveType(const DWARFDIE &die,
+ bool assert_not_being_parsed = true,
+ bool resolve_function_context = false);
+
+ lldb_private::CompilerDecl GetDeclForUID(lldb::user_id_t uid) override;
+
+ lldb_private::CompilerDeclContext
+ GetDeclContextForUID(lldb::user_id_t uid) override;
+
+ lldb_private::CompilerDeclContext
+ GetDeclContextContainingUID(lldb::user_id_t uid) override;
+
+ void
+ ParseDeclsForContext(lldb_private::CompilerDeclContext decl_ctx) override;
+
+ uint32_t ResolveSymbolContext(const lldb_private::Address &so_addr,
+ lldb::SymbolContextItem resolve_scope,
+ lldb_private::SymbolContext &sc) override;
+
+ uint32_t
+ ResolveSymbolContext(const lldb_private::FileSpec &file_spec, uint32_t line,
+ bool check_inlines,
+ lldb::SymbolContextItem resolve_scope,
+ lldb_private::SymbolContextList &sc_list) override;
+
+ uint32_t
+ FindGlobalVariables(lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches,
+ lldb_private::VariableList &variables) override;
+
+ uint32_t FindGlobalVariables(const lldb_private::RegularExpression &regex,
+ uint32_t max_matches,
+ lldb_private::VariableList &variables) override;
+
+ uint32_t
+ FindFunctions(lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ lldb::FunctionNameType name_type_mask, bool include_inlines,
+ bool append, lldb_private::SymbolContextList &sc_list) override;
+
+ uint32_t FindFunctions(const lldb_private::RegularExpression &regex,
+ bool include_inlines, bool append,
+ lldb_private::SymbolContextList &sc_list) override;
+
+ void GetMangledNamesForFunction(
+ const std::string &scope_qualified_name,
+ std::vector<lldb_private::ConstString> &mangled_names) override;
+
+ uint32_t
+ FindTypes(lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ bool append, uint32_t max_matches,
+ llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files,
+ lldb_private::TypeMap &types) override;
+
+ size_t FindTypes(const std::vector<lldb_private::CompilerContext> &context,
+ bool append, lldb_private::TypeMap &types) override;
+
+ lldb_private::TypeList *GetTypeList() override;
+
+ size_t GetTypes(lldb_private::SymbolContextScope *sc_scope,
+ lldb::TypeClass type_mask,
+ lldb_private::TypeList &type_list) override;
+
+ lldb_private::TypeSystem *
+ GetTypeSystemForLanguage(lldb::LanguageType language) override;
+
+ lldb_private::CompilerDeclContext FindNamespace(
+ lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx) override;
+
+ void PreloadSymbols() override;
+
+ std::recursive_mutex &GetModuleMutex() const override;
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ const lldb_private::DWARFDataExtractor &get_debug_loc_data();
+ const lldb_private::DWARFDataExtractor &get_debug_loclists_data();
+
+ DWARFDebugAbbrev *DebugAbbrev();
+
+ const DWARFDebugAbbrev *DebugAbbrev() const;
+
+ DWARFDebugInfo *DebugInfo();
+
+ const DWARFDebugInfo *DebugInfo() const;
+
+ DWARFDebugRangesBase *GetDebugRanges();
+ DWARFDebugRangesBase *GetDebugRngLists();
+
+ const lldb_private::DWARFDataExtractor &DebugLocData();
+
+ static bool SupportedVersion(uint16_t version);
+
+ DWARFDIE
+ GetDeclContextDIEContainingDIE(const DWARFDIE &die);
+
+ bool
+ HasForwardDeclForClangType(const lldb_private::CompilerType &compiler_type);
+
+ lldb_private::CompileUnit *
+ GetCompUnitForDWARFCompUnit(DWARFCompileUnit &dwarf_cu);
+
+ virtual size_t GetObjCMethodDIEOffsets(lldb_private::ConstString class_name,
+ DIEArray &method_die_offsets);
+
+ bool Supports_DW_AT_APPLE_objc_complete_type(DWARFUnit *cu);
+
+ lldb_private::DebugMacrosSP ParseDebugMacros(lldb::offset_t *offset);
+
+ static DWARFDIE GetParentSymbolContextDIE(const DWARFDIE &die);
+
+ virtual lldb::CompUnitSP ParseCompileUnit(DWARFCompileUnit &dwarf_cu);
+
+ virtual lldb_private::DWARFExpression::LocationListFormat
+ GetLocationListFormat() const;
+
+ lldb::ModuleSP GetDWOModule(lldb_private::ConstString name);
+
+ typedef std::map<lldb_private::ConstString, lldb::ModuleSP>
+ ExternalTypeModuleMap;
+
+ /// Return the list of Clang modules imported by this SymbolFile.
+ const ExternalTypeModuleMap& getExternalTypeModules() const {
+ return m_external_type_modules;
+ }
+
+ virtual DWARFDIE GetDIE(const DIERef &die_ref);
+
+ DWARFDIE GetDIE(lldb::user_id_t uid);
+
+ lldb::user_id_t GetUID(const DWARFBaseDIE &die) {
+ return GetUID(die.GetDIERef());
+ }
+
+ lldb::user_id_t GetUID(const llvm::Optional<DIERef> &ref) {
+ return ref ? GetUID(*ref) : LLDB_INVALID_UID;
+ }
+
+ lldb::user_id_t GetUID(DIERef ref);
+
+ virtual std::unique_ptr<SymbolFileDWARFDwo>
+ GetDwoSymbolFileForCompileUnit(DWARFUnit &dwarf_cu,
+ const DWARFDebugInfoEntry &cu_die);
+
+ // For regular SymbolFileDWARF instances the method returns nullptr,
+ // for the instances of the subclass SymbolFileDWARFDwo
+ // the method returns a pointer to the base compile unit.
+ virtual DWARFCompileUnit *GetBaseCompileUnit() { return nullptr; }
+
+ virtual llvm::Optional<uint32_t> GetDwoNum() { return llvm::None; }
+
+ static bool
+ DIEInDeclContext(const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ const DWARFDIE &die);
+
+ std::vector<lldb_private::CallEdge>
+ ParseCallEdgesInFunction(UserID func_id) override;
+
+ void Dump(lldb_private::Stream &s) override;
+
+ void DumpClangAST(lldb_private::Stream &s) override;
+
+ lldb_private::DWARFContext &GetDWARFContext() { return m_context; }
+
+ lldb_private::FileSpec GetFile(DWARFUnit &unit, size_t file_idx);
+
+protected:
+ typedef llvm::DenseMap<const DWARFDebugInfoEntry *, lldb_private::Type *>
+ DIEToTypePtr;
+ typedef llvm::DenseMap<const DWARFDebugInfoEntry *, lldb::VariableSP>
+ DIEToVariableSP;
+ typedef llvm::DenseMap<const DWARFDebugInfoEntry *,
+ lldb::opaque_compiler_type_t>
+ DIEToClangType;
+ typedef llvm::DenseMap<lldb::opaque_compiler_type_t, lldb::user_id_t>
+ ClangTypeToDIE;
+
+ struct DWARFDataSegment {
+ llvm::once_flag m_flag;
+ lldb_private::DWARFDataExtractor m_data;
+ };
+
+ DISALLOW_COPY_AND_ASSIGN(SymbolFileDWARF);
+
+ const lldb_private::DWARFDataExtractor &
+ GetCachedSectionData(lldb::SectionType sect_type,
+ DWARFDataSegment &data_segment);
+
+ virtual void LoadSectionData(lldb::SectionType sect_type,
+ lldb_private::DWARFDataExtractor &data);
+
+ bool DeclContextMatchesThisSymbolFile(
+ const lldb_private::CompilerDeclContext *decl_ctx);
+
+ virtual DWARFUnit *
+ GetDWARFCompileUnit(lldb_private::CompileUnit *comp_unit);
+
+ DWARFUnit *GetNextUnparsedDWARFCompileUnit(DWARFUnit *prev_cu);
+
+ bool GetFunction(const DWARFDIE &die, lldb_private::SymbolContext &sc);
+
+ lldb_private::Function *ParseFunction(lldb_private::CompileUnit &comp_unit,
+ const DWARFDIE &die);
+
+ size_t ParseBlocksRecursive(lldb_private::CompileUnit &comp_unit,
+ lldb_private::Block *parent_block,
+ const DWARFDIE &die,
+ lldb::addr_t subprogram_low_pc, uint32_t depth);
+
+ size_t ParseTypes(const lldb_private::SymbolContext &sc, const DWARFDIE &die,
+ bool parse_siblings, bool parse_children);
+
+ lldb::TypeSP ParseType(const lldb_private::SymbolContext &sc,
+ const DWARFDIE &die, bool *type_is_new);
+
+ lldb_private::Type *ResolveTypeUID(const DWARFDIE &die,
+ bool assert_not_being_parsed);
+
+ lldb_private::Type *ResolveTypeUID(const DIERef &die_ref);
+
+ lldb::VariableSP ParseVariableDIE(const lldb_private::SymbolContext &sc,
+ const DWARFDIE &die,
+ const lldb::addr_t func_low_pc);
+
+ size_t ParseVariables(const lldb_private::SymbolContext &sc,
+ const DWARFDIE &orig_die,
+ const lldb::addr_t func_low_pc, bool parse_siblings,
+ bool parse_children,
+ lldb_private::VariableList *cc_variable_list = nullptr);
+
+ bool ClassOrStructIsVirtual(const DWARFDIE &die);
+
+ // Given a die_offset, figure out the symbol context representing that die.
+ bool ResolveFunction(const DWARFDIE &die, bool include_inlines,
+ lldb_private::SymbolContextList &sc_list);
+
+ virtual lldb::TypeSP
+ FindDefinitionTypeForDWARFDeclContext(const DWARFDeclContext &die_decl_ctx);
+
+ virtual lldb::TypeSP FindCompleteObjCDefinitionTypeForDIE(
+ const DWARFDIE &die, lldb_private::ConstString type_name,
+ bool must_be_implementation);
+
+ lldb_private::Symbol *
+ GetObjCClassSymbol(lldb_private::ConstString objc_class_name);
+
+ lldb::TypeSP GetTypeForDIE(const DWARFDIE &die,
+ bool resolve_function_context = false);
+
+ void SetDebugMapModule(const lldb::ModuleSP &module_sp) {
+ m_debug_map_module_wp = module_sp;
+ }
+
+ SymbolFileDWARFDebugMap *GetDebugMapSymfile();
+
+ DWARFDIE
+ FindBlockContainingSpecification(const DIERef &func_die_ref,
+ dw_offset_t spec_block_die_offset);
+
+ DWARFDIE
+ FindBlockContainingSpecification(const DWARFDIE &die,
+ dw_offset_t spec_block_die_offset);
+
+ virtual UniqueDWARFASTTypeMap &GetUniqueDWARFASTTypeMap();
+
+ bool DIEDeclContextsMatch(const DWARFDIE &die1, const DWARFDIE &die2);
+
+ bool ClassContainsSelector(const DWARFDIE &class_die,
+ lldb_private::ConstString selector);
+
+ bool FixupAddress(lldb_private::Address &addr);
+
+ typedef std::set<lldb_private::Type *> TypeSet;
+
+ void GetTypes(const DWARFDIE &die, dw_offset_t min_die_offset,
+ dw_offset_t max_die_offset, uint32_t type_mask,
+ TypeSet &type_set);
+
+ typedef lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t,
+ lldb_private::Variable *>
+ GlobalVariableMap;
+
+ GlobalVariableMap &GetGlobalAranges();
+
+ void UpdateExternalModuleListIfNeeded();
+
+ virtual DIEToTypePtr &GetDIEToType() { return m_die_to_type; }
+
+ virtual DIEToVariableSP &GetDIEToVariable() { return m_die_to_variable_sp; }
+
+ virtual DIEToClangType &GetForwardDeclDieToClangType() {
+ return m_forward_decl_die_to_clang_type;
+ }
+
+ virtual ClangTypeToDIE &GetForwardDeclClangTypeToDie() {
+ return m_forward_decl_clang_type_to_die;
+ }
+
+ void BuildCuTranslationTable();
+ llvm::Optional<uint32_t> GetDWARFUnitIndex(uint32_t cu_idx);
+
+ struct DecodedUID {
+ SymbolFileDWARF &dwarf;
+ DIERef ref;
+ };
+ llvm::Optional<DecodedUID> DecodeUID(lldb::user_id_t uid);
+
+ SymbolFileDWARFDwp *GetDwpSymbolFile();
+
+ const lldb_private::FileSpecList &GetTypeUnitSupportFiles(DWARFTypeUnit &tu);
+
+ lldb::ModuleWP m_debug_map_module_wp;
+ SymbolFileDWARFDebugMap *m_debug_map_symfile;
+
+ llvm::once_flag m_dwp_symfile_once_flag;
+ std::unique_ptr<SymbolFileDWARFDwp> m_dwp_symfile;
+
+ lldb_private::DWARFContext m_context;
+
+ DWARFDataSegment m_data_debug_loc;
+ DWARFDataSegment m_data_debug_loclists;
+
+ // The unique pointer items below are generated on demand if and when someone
+ // accesses them through a non const version of this class.
+ std::unique_ptr<DWARFDebugAbbrev> m_abbr;
+ std::unique_ptr<DWARFDebugInfo> m_info;
+ std::unique_ptr<GlobalVariableMap> m_global_aranges_up;
+
+ typedef std::unordered_map<lldb::offset_t, lldb_private::DebugMacrosSP>
+ DebugMacrosMap;
+ DebugMacrosMap m_debug_macros_map;
+
+ ExternalTypeModuleMap m_external_type_modules;
+ std::unique_ptr<lldb_private::DWARFIndex> m_index;
+ bool m_fetched_external_modules : 1;
+ lldb_private::LazyBool m_supports_DW_AT_APPLE_objc_complete_type;
+
+ typedef std::set<lldb::user_id_t> DIERefSet;
+ typedef llvm::StringMap<DIERefSet> NameToOffsetMap;
+ NameToOffsetMap m_function_scope_qualified_name_map;
+ std::unique_ptr<DWARFDebugRangesBase> m_ranges;
+ std::unique_ptr<DWARFDebugRangesBase> m_rnglists;
+ UniqueDWARFASTTypeMap m_unique_ast_type_map;
+ DIEToTypePtr m_die_to_type;
+ DIEToVariableSP m_die_to_variable_sp;
+ DIEToClangType m_forward_decl_die_to_clang_type;
+ ClangTypeToDIE m_forward_decl_clang_type_to_die;
+ llvm::DenseMap<dw_offset_t, lldb_private::FileSpecList>
+ m_type_unit_support_files;
+ std::vector<uint32_t> m_lldb_cu_to_dwarf_unit;
+};
+
+#endif // SymbolFileDWARF_SymbolFileDWARF_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
new file mode 100644
index 000000000000..8ec64dbaf764
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
@@ -0,0 +1,1440 @@
+//===-- SymbolFileDWARFDebugMap.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 "SymbolFileDWARFDebugMap.h"
+#include "DWARFDebugAranges.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Utility/RangeMap.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Timer.h"
+
+//#define DEBUG_OSO_DMAP // DO NOT CHECKIN WITH THIS NOT COMMENTED OUT
+#if defined(DEBUG_OSO_DMAP)
+#include "lldb/Core/StreamFile.h"
+#endif
+
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/TypeMap.h"
+#include "lldb/Symbol/VariableList.h"
+#include "llvm/Support/ScopedPrinter.h"
+
+#include "LogChannelDWARF.h"
+#include "SymbolFileDWARF.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Subclass lldb_private::Module so we can intercept the
+// "Module::GetObjectFile()" (so we can fixup the object file sections) and
+// also for "Module::GetSymbolVendor()" (so we can fixup the symbol file id.
+
+const SymbolFileDWARFDebugMap::FileRangeMap &
+SymbolFileDWARFDebugMap::CompileUnitInfo::GetFileRangeMap(
+ SymbolFileDWARFDebugMap *exe_symfile) {
+ if (file_range_map_valid)
+ return file_range_map;
+
+ file_range_map_valid = true;
+
+ Module *oso_module = exe_symfile->GetModuleByCompUnitInfo(this);
+ if (!oso_module)
+ return file_range_map;
+
+ ObjectFile *oso_objfile = oso_module->GetObjectFile();
+ if (!oso_objfile)
+ return file_range_map;
+
+ Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_MAP));
+ if (log)
+ log->Printf(
+ "%p: SymbolFileDWARFDebugMap::CompileUnitInfo::GetFileRangeMap ('%s')",
+ static_cast<void *>(this),
+ oso_module->GetSpecificationDescription().c_str());
+
+ std::vector<SymbolFileDWARFDebugMap::CompileUnitInfo *> cu_infos;
+ if (exe_symfile->GetCompUnitInfosForModule(oso_module, cu_infos)) {
+ for (auto comp_unit_info : cu_infos) {
+ Symtab *exe_symtab = exe_symfile->GetObjectFile()->GetSymtab();
+ ModuleSP oso_module_sp(oso_objfile->GetModule());
+ Symtab *oso_symtab = oso_objfile->GetSymtab();
+
+ /// const uint32_t fun_resolve_flags = SymbolContext::Module |
+ /// eSymbolContextCompUnit | eSymbolContextFunction;
+ // SectionList *oso_sections = oso_objfile->Sections();
+ // Now we need to make sections that map from zero based object file
+ // addresses to where things ended up in the main executable.
+
+ assert(comp_unit_info->first_symbol_index != UINT32_MAX);
+ // End index is one past the last valid symbol index
+ const uint32_t oso_end_idx = comp_unit_info->last_symbol_index + 1;
+ for (uint32_t idx = comp_unit_info->first_symbol_index +
+ 2; // Skip the N_SO and N_OSO
+ idx < oso_end_idx; ++idx) {
+ Symbol *exe_symbol = exe_symtab->SymbolAtIndex(idx);
+ if (exe_symbol) {
+ if (!exe_symbol->IsDebug())
+ continue;
+
+ switch (exe_symbol->GetType()) {
+ default:
+ break;
+
+ case eSymbolTypeCode: {
+ // For each N_FUN, or function that we run into in the debug map we
+ // make a new section that we add to the sections found in the .o
+ // file. This new section has the file address set to what the
+ // addresses are in the .o file, and the load address is adjusted
+ // to match where it ended up in the final executable! We do this
+ // before we parse any dwarf info so that when it goes get parsed
+ // all section/offset addresses that get registered will resolve
+ // correctly to the new addresses in the main executable.
+
+ // First we find the original symbol in the .o file's symbol table
+ Symbol *oso_fun_symbol = oso_symtab->FindFirstSymbolWithNameAndType(
+ exe_symbol->GetMangled().GetName(lldb::eLanguageTypeUnknown,
+ Mangled::ePreferMangled),
+ eSymbolTypeCode, Symtab::eDebugNo, Symtab::eVisibilityAny);
+ if (oso_fun_symbol) {
+ // Add the inverse OSO file address to debug map entry mapping
+ exe_symfile->AddOSOFileRange(
+ this, exe_symbol->GetAddressRef().GetFileAddress(),
+ exe_symbol->GetByteSize(),
+ oso_fun_symbol->GetAddressRef().GetFileAddress(),
+ oso_fun_symbol->GetByteSize());
+ }
+ } break;
+
+ case eSymbolTypeData: {
+ // For each N_GSYM we remap the address for the global by making a
+ // new section that we add to the sections found in the .o file.
+ // This new section has the file address set to what the addresses
+ // are in the .o file, and the load address is adjusted to match
+ // where it ended up in the final executable! We do this before we
+ // parse any dwarf info so that when it goes get parsed all
+ // section/offset addresses that get registered will resolve
+ // correctly to the new addresses in the main executable. We
+ // initially set the section size to be 1 byte, but will need to
+ // fix up these addresses further after all globals have been
+ // parsed to span the gaps, or we can find the global variable
+ // sizes from the DWARF info as we are parsing.
+
+ // Next we find the non-stab entry that corresponds to the N_GSYM
+ // in the .o file
+ Symbol *oso_gsym_symbol =
+ oso_symtab->FindFirstSymbolWithNameAndType(
+ exe_symbol->GetMangled().GetName(lldb::eLanguageTypeUnknown,
+ Mangled::ePreferMangled),
+ eSymbolTypeData, Symtab::eDebugNo, Symtab::eVisibilityAny);
+ if (exe_symbol && oso_gsym_symbol && exe_symbol->ValueIsAddress() &&
+ oso_gsym_symbol->ValueIsAddress()) {
+ // Add the inverse OSO file address to debug map entry mapping
+ exe_symfile->AddOSOFileRange(
+ this, exe_symbol->GetAddressRef().GetFileAddress(),
+ exe_symbol->GetByteSize(),
+ oso_gsym_symbol->GetAddressRef().GetFileAddress(),
+ oso_gsym_symbol->GetByteSize());
+ }
+ } break;
+ }
+ }
+ }
+
+ exe_symfile->FinalizeOSOFileRanges(this);
+ // We don't need the symbols anymore for the .o files
+ oso_objfile->ClearSymtab();
+ }
+ }
+ return file_range_map;
+}
+
+class DebugMapModule : public Module {
+public:
+ DebugMapModule(const ModuleSP &exe_module_sp, uint32_t cu_idx,
+ const FileSpec &file_spec, const ArchSpec &arch,
+ const ConstString *object_name, off_t object_offset,
+ const llvm::sys::TimePoint<> object_mod_time)
+ : Module(file_spec, arch, object_name, object_offset, object_mod_time),
+ m_exe_module_wp(exe_module_sp), m_cu_idx(cu_idx) {}
+
+ ~DebugMapModule() override = default;
+
+ SymbolVendor *
+ GetSymbolVendor(bool can_create = true,
+ lldb_private::Stream *feedback_strm = nullptr) override {
+ // Scope for locker
+ if (m_symfile_up.get() || !can_create)
+ return m_symfile_up.get();
+
+ ModuleSP exe_module_sp(m_exe_module_wp.lock());
+ if (exe_module_sp) {
+ // Now get the object file outside of a locking scope
+ ObjectFile *oso_objfile = GetObjectFile();
+ if (oso_objfile) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ SymbolVendor *symbol_vendor =
+ Module::GetSymbolVendor(can_create, feedback_strm);
+ if (symbol_vendor) {
+ // Set a pointer to this class to set our OSO DWARF file know that
+ // the DWARF is being used along with a debug map and that it will
+ // have the remapped sections that we do below.
+ SymbolFileDWARF *oso_symfile =
+ SymbolFileDWARFDebugMap::GetSymbolFileAsSymbolFileDWARF(
+ symbol_vendor->GetSymbolFile());
+
+ if (!oso_symfile)
+ return nullptr;
+
+ ObjectFile *exe_objfile = exe_module_sp->GetObjectFile();
+ SymbolVendor *exe_sym_vendor = exe_module_sp->GetSymbolVendor();
+
+ if (exe_objfile && exe_sym_vendor) {
+ oso_symfile->SetDebugMapModule(exe_module_sp);
+ // Set the ID of the symbol file DWARF to the index of the OSO
+ // shifted left by 32 bits to provide a unique prefix for any
+ // UserID's that get created in the symbol file.
+ oso_symfile->SetID(((uint64_t)m_cu_idx + 1ull) << 32ull);
+ }
+ return symbol_vendor;
+ }
+ }
+ }
+ return nullptr;
+ }
+
+protected:
+ ModuleWP m_exe_module_wp;
+ const uint32_t m_cu_idx;
+};
+
+void SymbolFileDWARFDebugMap::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void SymbolFileDWARFDebugMap::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString SymbolFileDWARFDebugMap::GetPluginNameStatic() {
+ static ConstString g_name("dwarf-debugmap");
+ return g_name;
+}
+
+const char *SymbolFileDWARFDebugMap::GetPluginDescriptionStatic() {
+ return "DWARF and DWARF3 debug symbol file reader (debug map).";
+}
+
+SymbolFile *SymbolFileDWARFDebugMap::CreateInstance(ObjectFile *obj_file) {
+ return new SymbolFileDWARFDebugMap(obj_file);
+}
+
+SymbolFileDWARFDebugMap::SymbolFileDWARFDebugMap(ObjectFile *ofile)
+ : SymbolFile(ofile), m_flags(), m_compile_unit_infos(), m_func_indexes(),
+ m_glob_indexes(),
+ m_supports_DW_AT_APPLE_objc_complete_type(eLazyBoolCalculate) {}
+
+SymbolFileDWARFDebugMap::~SymbolFileDWARFDebugMap() {}
+
+void SymbolFileDWARFDebugMap::InitializeObject() {}
+
+void SymbolFileDWARFDebugMap::InitOSO() {
+ if (m_flags.test(kHaveInitializedOSOs))
+ return;
+
+ m_flags.set(kHaveInitializedOSOs);
+
+ // If the object file has been stripped, there is no sense in looking further
+ // as all of the debug symbols for the debug map will not be available
+ if (m_obj_file->IsStripped())
+ return;
+
+ // Also make sure the file type is some sort of executable. Core files, debug
+ // info files (dSYM), object files (.o files), and stub libraries all can
+ switch (m_obj_file->GetType()) {
+ case ObjectFile::eTypeInvalid:
+ case ObjectFile::eTypeCoreFile:
+ case ObjectFile::eTypeDebugInfo:
+ case ObjectFile::eTypeObjectFile:
+ case ObjectFile::eTypeStubLibrary:
+ case ObjectFile::eTypeUnknown:
+ case ObjectFile::eTypeJIT:
+ return;
+
+ case ObjectFile::eTypeExecutable:
+ case ObjectFile::eTypeDynamicLinker:
+ case ObjectFile::eTypeSharedLibrary:
+ break;
+ }
+
+ // In order to get the abilities of this plug-in, we look at the list of
+ // N_OSO entries (object files) from the symbol table and make sure that
+ // these files exist and also contain valid DWARF. If we get any of that then
+ // we return the abilities of the first N_OSO's DWARF.
+
+ Symtab *symtab = m_obj_file->GetSymtab();
+ if (symtab) {
+ Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_MAP));
+
+ std::vector<uint32_t> oso_indexes;
+ // When a mach-o symbol is encoded, the n_type field is encoded in bits
+ // 23:16, and the n_desc field is encoded in bits 15:0.
+ //
+ // To find all N_OSO entries that are part of the DWARF + debug map we find
+ // only object file symbols with the flags value as follows: bits 23:16 ==
+ // 0x66 (N_OSO) bits 15: 0 == 0x0001 (specifies this is a debug map object
+ // file)
+ const uint32_t k_oso_symbol_flags_value = 0x660001u;
+
+ const uint32_t oso_index_count =
+ symtab->AppendSymbolIndexesWithTypeAndFlagsValue(
+ eSymbolTypeObjectFile, k_oso_symbol_flags_value, oso_indexes);
+
+ if (oso_index_count > 0) {
+ symtab->AppendSymbolIndexesWithType(eSymbolTypeCode, Symtab::eDebugYes,
+ Symtab::eVisibilityAny,
+ m_func_indexes);
+ symtab->AppendSymbolIndexesWithType(eSymbolTypeData, Symtab::eDebugYes,
+ Symtab::eVisibilityAny,
+ m_glob_indexes);
+
+ symtab->SortSymbolIndexesByValue(m_func_indexes, true);
+ symtab->SortSymbolIndexesByValue(m_glob_indexes, true);
+
+ for (uint32_t sym_idx : m_func_indexes) {
+ const Symbol *symbol = symtab->SymbolAtIndex(sym_idx);
+ lldb::addr_t file_addr = symbol->GetAddressRef().GetFileAddress();
+ lldb::addr_t byte_size = symbol->GetByteSize();
+ DebugMap::Entry debug_map_entry(
+ file_addr, byte_size, OSOEntry(sym_idx, LLDB_INVALID_ADDRESS));
+ m_debug_map.Append(debug_map_entry);
+ }
+ for (uint32_t sym_idx : m_glob_indexes) {
+ const Symbol *symbol = symtab->SymbolAtIndex(sym_idx);
+ lldb::addr_t file_addr = symbol->GetAddressRef().GetFileAddress();
+ lldb::addr_t byte_size = symbol->GetByteSize();
+ DebugMap::Entry debug_map_entry(
+ file_addr, byte_size, OSOEntry(sym_idx, LLDB_INVALID_ADDRESS));
+ m_debug_map.Append(debug_map_entry);
+ }
+ m_debug_map.Sort();
+
+ m_compile_unit_infos.resize(oso_index_count);
+
+ for (uint32_t i = 0; i < oso_index_count; ++i) {
+ const uint32_t so_idx = oso_indexes[i] - 1;
+ const uint32_t oso_idx = oso_indexes[i];
+ const Symbol *so_symbol = symtab->SymbolAtIndex(so_idx);
+ const Symbol *oso_symbol = symtab->SymbolAtIndex(oso_idx);
+ if (so_symbol && oso_symbol &&
+ so_symbol->GetType() == eSymbolTypeSourceFile &&
+ oso_symbol->GetType() == eSymbolTypeObjectFile) {
+ m_compile_unit_infos[i].so_file.SetFile(
+ so_symbol->GetName().AsCString(), FileSpec::Style::native);
+ m_compile_unit_infos[i].oso_path = oso_symbol->GetName();
+ m_compile_unit_infos[i].oso_mod_time =
+ llvm::sys::toTimePoint(oso_symbol->GetIntegerValue(0));
+ uint32_t sibling_idx = so_symbol->GetSiblingIndex();
+ // The sibling index can't be less that or equal to the current index
+ // "i"
+ if (sibling_idx == UINT32_MAX) {
+ m_obj_file->GetModule()->ReportError(
+ "N_SO in symbol with UID %u has invalid sibling in debug map, "
+ "please file a bug and attach the binary listed in this error",
+ so_symbol->GetID());
+ } else {
+ const Symbol *last_symbol = symtab->SymbolAtIndex(sibling_idx - 1);
+ m_compile_unit_infos[i].first_symbol_index = so_idx;
+ m_compile_unit_infos[i].last_symbol_index = sibling_idx - 1;
+ m_compile_unit_infos[i].first_symbol_id = so_symbol->GetID();
+ m_compile_unit_infos[i].last_symbol_id = last_symbol->GetID();
+
+ if (log)
+ log->Printf("Initialized OSO 0x%8.8x: file=%s", i,
+ oso_symbol->GetName().GetCString());
+ }
+ } else {
+ if (oso_symbol == nullptr)
+ m_obj_file->GetModule()->ReportError(
+ "N_OSO symbol[%u] can't be found, please file a bug and attach "
+ "the binary listed in this error",
+ oso_idx);
+ else if (so_symbol == nullptr)
+ m_obj_file->GetModule()->ReportError(
+ "N_SO not found for N_OSO symbol[%u], please file a bug and "
+ "attach the binary listed in this error",
+ oso_idx);
+ else if (so_symbol->GetType() != eSymbolTypeSourceFile)
+ m_obj_file->GetModule()->ReportError(
+ "N_SO has incorrect symbol type (%u) for N_OSO symbol[%u], "
+ "please file a bug and attach the binary listed in this error",
+ so_symbol->GetType(), oso_idx);
+ else if (oso_symbol->GetType() != eSymbolTypeSourceFile)
+ m_obj_file->GetModule()->ReportError(
+ "N_OSO has incorrect symbol type (%u) for N_OSO symbol[%u], "
+ "please file a bug and attach the binary listed in this error",
+ oso_symbol->GetType(), oso_idx);
+ }
+ }
+ }
+ }
+}
+
+Module *SymbolFileDWARFDebugMap::GetModuleByOSOIndex(uint32_t oso_idx) {
+ const uint32_t cu_count = GetNumCompileUnits();
+ if (oso_idx < cu_count)
+ return GetModuleByCompUnitInfo(&m_compile_unit_infos[oso_idx]);
+ return nullptr;
+}
+
+Module *SymbolFileDWARFDebugMap::GetModuleByCompUnitInfo(
+ CompileUnitInfo *comp_unit_info) {
+ if (!comp_unit_info->oso_sp) {
+ auto pos = m_oso_map.find(
+ {comp_unit_info->oso_path, comp_unit_info->oso_mod_time});
+ if (pos != m_oso_map.end()) {
+ comp_unit_info->oso_sp = pos->second;
+ } else {
+ ObjectFile *obj_file = GetObjectFile();
+ comp_unit_info->oso_sp = std::make_shared<OSOInfo>();
+ m_oso_map[{comp_unit_info->oso_path, comp_unit_info->oso_mod_time}] =
+ comp_unit_info->oso_sp;
+ const char *oso_path = comp_unit_info->oso_path.GetCString();
+ FileSpec oso_file(oso_path);
+ ConstString oso_object;
+ if (FileSystem::Instance().Exists(oso_file)) {
+ // The modification time returned by the FS can have a higher precision
+ // than the one from the CU.
+ auto oso_mod_time = std::chrono::time_point_cast<std::chrono::seconds>(
+ FileSystem::Instance().GetModificationTime(oso_file));
+ if (oso_mod_time != comp_unit_info->oso_mod_time) {
+ obj_file->GetModule()->ReportError(
+ "debug map object file '%s' has changed (actual time is "
+ "%s, debug map time is %s"
+ ") since this executable was linked, file will be ignored",
+ oso_file.GetPath().c_str(), llvm::to_string(oso_mod_time).c_str(),
+ llvm::to_string(comp_unit_info->oso_mod_time).c_str());
+ return nullptr;
+ }
+
+ } else {
+ const bool must_exist = true;
+
+ if (!ObjectFile::SplitArchivePathWithObject(oso_path, oso_file,
+ oso_object, must_exist)) {
+ return nullptr;
+ }
+ }
+ // Always create a new module for .o files. Why? Because we use the debug
+ // map, to add new sections to each .o file and even though a .o file
+ // might not have changed, the sections that get added to the .o file can
+ // change.
+ ArchSpec oso_arch;
+ // Only adopt the architecture from the module (not the vendor or OS)
+ // since .o files for "i386-apple-ios" will historically show up as "i386
+ // -apple-macosx" due to the lack of a LC_VERSION_MIN_MACOSX or
+ // LC_VERSION_MIN_IPHONEOS load command...
+ oso_arch.SetTriple(m_obj_file->GetModule()
+ ->GetArchitecture()
+ .GetTriple()
+ .getArchName()
+ .str()
+ .c_str());
+ comp_unit_info->oso_sp->module_sp = std::make_shared<DebugMapModule>(
+ obj_file->GetModule(), GetCompUnitInfoIndex(comp_unit_info), oso_file,
+ oso_arch, oso_object ? &oso_object : nullptr, 0,
+ oso_object ? comp_unit_info->oso_mod_time : llvm::sys::TimePoint<>());
+ }
+ }
+ if (comp_unit_info->oso_sp)
+ return comp_unit_info->oso_sp->module_sp.get();
+ return nullptr;
+}
+
+bool SymbolFileDWARFDebugMap::GetFileSpecForSO(uint32_t oso_idx,
+ FileSpec &file_spec) {
+ if (oso_idx < m_compile_unit_infos.size()) {
+ if (m_compile_unit_infos[oso_idx].so_file) {
+ file_spec = m_compile_unit_infos[oso_idx].so_file;
+ return true;
+ }
+ }
+ return false;
+}
+
+ObjectFile *SymbolFileDWARFDebugMap::GetObjectFileByOSOIndex(uint32_t oso_idx) {
+ Module *oso_module = GetModuleByOSOIndex(oso_idx);
+ if (oso_module)
+ return oso_module->GetObjectFile();
+ return nullptr;
+}
+
+SymbolFileDWARF *
+SymbolFileDWARFDebugMap::GetSymbolFile(const SymbolContext &sc) {
+ return GetSymbolFile(*sc.comp_unit);
+}
+
+SymbolFileDWARF *
+SymbolFileDWARFDebugMap::GetSymbolFile(const CompileUnit &comp_unit) {
+ CompileUnitInfo *comp_unit_info = GetCompUnitInfo(comp_unit);
+ if (comp_unit_info)
+ return GetSymbolFileByCompUnitInfo(comp_unit_info);
+ return nullptr;
+}
+
+ObjectFile *SymbolFileDWARFDebugMap::GetObjectFileByCompUnitInfo(
+ CompileUnitInfo *comp_unit_info) {
+ Module *oso_module = GetModuleByCompUnitInfo(comp_unit_info);
+ if (oso_module)
+ return oso_module->GetObjectFile();
+ return nullptr;
+}
+
+uint32_t SymbolFileDWARFDebugMap::GetCompUnitInfoIndex(
+ const CompileUnitInfo *comp_unit_info) {
+ if (!m_compile_unit_infos.empty()) {
+ const CompileUnitInfo *first_comp_unit_info = &m_compile_unit_infos.front();
+ const CompileUnitInfo *last_comp_unit_info = &m_compile_unit_infos.back();
+ if (first_comp_unit_info <= comp_unit_info &&
+ comp_unit_info <= last_comp_unit_info)
+ return comp_unit_info - first_comp_unit_info;
+ }
+ return UINT32_MAX;
+}
+
+SymbolFileDWARF *
+SymbolFileDWARFDebugMap::GetSymbolFileByOSOIndex(uint32_t oso_idx) {
+ unsigned size = m_compile_unit_infos.size();
+ if (oso_idx < size)
+ return GetSymbolFileByCompUnitInfo(&m_compile_unit_infos[oso_idx]);
+ return nullptr;
+}
+
+SymbolFileDWARF *
+SymbolFileDWARFDebugMap::GetSymbolFileAsSymbolFileDWARF(SymbolFile *sym_file) {
+ if (sym_file &&
+ sym_file->GetPluginName() == SymbolFileDWARF::GetPluginNameStatic())
+ return (SymbolFileDWARF *)sym_file;
+ return nullptr;
+}
+
+SymbolFileDWARF *SymbolFileDWARFDebugMap::GetSymbolFileByCompUnitInfo(
+ CompileUnitInfo *comp_unit_info) {
+ Module *oso_module = GetModuleByCompUnitInfo(comp_unit_info);
+ if (oso_module) {
+ SymbolVendor *sym_vendor = oso_module->GetSymbolVendor();
+ if (sym_vendor)
+ return GetSymbolFileAsSymbolFileDWARF(sym_vendor->GetSymbolFile());
+ }
+ return nullptr;
+}
+
+uint32_t SymbolFileDWARFDebugMap::CalculateAbilities() {
+ // In order to get the abilities of this plug-in, we look at the list of
+ // N_OSO entries (object files) from the symbol table and make sure that
+ // these files exist and also contain valid DWARF. If we get any of that then
+ // we return the abilities of the first N_OSO's DWARF.
+
+ const uint32_t oso_index_count = GetNumCompileUnits();
+ if (oso_index_count > 0) {
+ InitOSO();
+ if (!m_compile_unit_infos.empty()) {
+ return SymbolFile::CompileUnits | SymbolFile::Functions |
+ SymbolFile::Blocks | SymbolFile::GlobalVariables |
+ SymbolFile::LocalVariables | SymbolFile::VariableTypes |
+ SymbolFile::LineTables;
+ }
+ }
+ return 0;
+}
+
+uint32_t SymbolFileDWARFDebugMap::GetNumCompileUnits() {
+ InitOSO();
+ return m_compile_unit_infos.size();
+}
+
+CompUnitSP SymbolFileDWARFDebugMap::ParseCompileUnitAtIndex(uint32_t cu_idx) {
+ CompUnitSP comp_unit_sp;
+ const uint32_t cu_count = GetNumCompileUnits();
+
+ if (cu_idx < cu_count) {
+ Module *oso_module = GetModuleByCompUnitInfo(&m_compile_unit_infos[cu_idx]);
+ if (oso_module) {
+ FileSpec so_file_spec;
+ if (GetFileSpecForSO(cu_idx, so_file_spec)) {
+ // User zero as the ID to match the compile unit at offset zero in each
+ // .o file since each .o file can only have one compile unit for now.
+ lldb::user_id_t cu_id = 0;
+ m_compile_unit_infos[cu_idx].compile_unit_sp =
+ std::make_shared<CompileUnit>(
+ m_obj_file->GetModule(), nullptr, so_file_spec, cu_id,
+ eLanguageTypeUnknown, eLazyBoolCalculate);
+
+ if (m_compile_unit_infos[cu_idx].compile_unit_sp) {
+ // Let our symbol vendor know about this compile unit
+ m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex(
+ cu_idx, m_compile_unit_infos[cu_idx].compile_unit_sp);
+ }
+ }
+ }
+ comp_unit_sp = m_compile_unit_infos[cu_idx].compile_unit_sp;
+ }
+
+ return comp_unit_sp;
+}
+
+SymbolFileDWARFDebugMap::CompileUnitInfo *
+SymbolFileDWARFDebugMap::GetCompUnitInfo(const SymbolContext &sc) {
+ return GetCompUnitInfo(*sc.comp_unit);
+}
+
+SymbolFileDWARFDebugMap::CompileUnitInfo *
+SymbolFileDWARFDebugMap::GetCompUnitInfo(const CompileUnit &comp_unit) {
+ const uint32_t cu_count = GetNumCompileUnits();
+ for (uint32_t i = 0; i < cu_count; ++i) {
+ if (comp_unit == m_compile_unit_infos[i].compile_unit_sp.get())
+ return &m_compile_unit_infos[i];
+ }
+ return nullptr;
+}
+
+size_t SymbolFileDWARFDebugMap::GetCompUnitInfosForModule(
+ const lldb_private::Module *module,
+ std::vector<CompileUnitInfo *> &cu_infos) {
+ const uint32_t cu_count = GetNumCompileUnits();
+ for (uint32_t i = 0; i < cu_count; ++i) {
+ if (module == GetModuleByCompUnitInfo(&m_compile_unit_infos[i]))
+ cu_infos.push_back(&m_compile_unit_infos[i]);
+ }
+ return cu_infos.size();
+}
+
+lldb::LanguageType
+SymbolFileDWARFDebugMap::ParseLanguage(CompileUnit &comp_unit) {
+ SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit);
+ if (oso_dwarf)
+ return oso_dwarf->ParseLanguage(comp_unit);
+ return eLanguageTypeUnknown;
+}
+
+size_t SymbolFileDWARFDebugMap::ParseFunctions(CompileUnit &comp_unit) {
+ SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit);
+ if (oso_dwarf)
+ return oso_dwarf->ParseFunctions(comp_unit);
+ return 0;
+}
+
+bool SymbolFileDWARFDebugMap::ParseLineTable(CompileUnit &comp_unit) {
+ SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit);
+ if (oso_dwarf)
+ return oso_dwarf->ParseLineTable(comp_unit);
+ return false;
+}
+
+bool SymbolFileDWARFDebugMap::ParseDebugMacros(CompileUnit &comp_unit) {
+ SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit);
+ if (oso_dwarf)
+ return oso_dwarf->ParseDebugMacros(comp_unit);
+ return false;
+}
+
+bool SymbolFileDWARFDebugMap::ParseSupportFiles(CompileUnit &comp_unit,
+ FileSpecList &support_files) {
+ SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit);
+ if (oso_dwarf)
+ return oso_dwarf->ParseSupportFiles(comp_unit, support_files);
+ return false;
+}
+
+bool SymbolFileDWARFDebugMap::ParseIsOptimized(CompileUnit &comp_unit) {
+ SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit);
+ if (oso_dwarf)
+ return oso_dwarf->ParseIsOptimized(comp_unit);
+ return false;
+}
+
+bool SymbolFileDWARFDebugMap::ParseImportedModules(
+ const SymbolContext &sc, std::vector<SourceModule> &imported_modules) {
+ SymbolFileDWARF *oso_dwarf = GetSymbolFile(sc);
+ if (oso_dwarf)
+ return oso_dwarf->ParseImportedModules(sc, imported_modules);
+ return false;
+}
+
+size_t SymbolFileDWARFDebugMap::ParseBlocksRecursive(Function &func) {
+ CompileUnit *comp_unit = func.GetCompileUnit();
+ if (!comp_unit)
+ return 0;
+
+ SymbolFileDWARF *oso_dwarf = GetSymbolFile(*comp_unit);
+ if (oso_dwarf)
+ return oso_dwarf->ParseBlocksRecursive(func);
+ return 0;
+}
+
+size_t SymbolFileDWARFDebugMap::ParseTypes(CompileUnit &comp_unit) {
+ SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit);
+ if (oso_dwarf)
+ return oso_dwarf->ParseTypes(comp_unit);
+ return 0;
+}
+
+size_t
+SymbolFileDWARFDebugMap::ParseVariablesForContext(const SymbolContext &sc) {
+ SymbolFileDWARF *oso_dwarf = GetSymbolFile(sc);
+ if (oso_dwarf)
+ return oso_dwarf->ParseVariablesForContext(sc);
+ return 0;
+}
+
+Type *SymbolFileDWARFDebugMap::ResolveTypeUID(lldb::user_id_t type_uid) {
+ const uint64_t oso_idx = GetOSOIndexFromUserID(type_uid);
+ SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx);
+ if (oso_dwarf)
+ return oso_dwarf->ResolveTypeUID(type_uid);
+ return nullptr;
+}
+
+llvm::Optional<SymbolFile::ArrayInfo>
+SymbolFileDWARFDebugMap::GetDynamicArrayInfoForUID(
+ lldb::user_id_t type_uid, const lldb_private::ExecutionContext *exe_ctx) {
+ const uint64_t oso_idx = GetOSOIndexFromUserID(type_uid);
+ SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx);
+ if (oso_dwarf)
+ return oso_dwarf->GetDynamicArrayInfoForUID(type_uid, exe_ctx);
+ return llvm::None;
+}
+
+bool SymbolFileDWARFDebugMap::CompleteType(CompilerType &compiler_type) {
+ bool success = false;
+ if (compiler_type) {
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ if (oso_dwarf->HasForwardDeclForClangType(compiler_type)) {
+ oso_dwarf->CompleteType(compiler_type);
+ success = true;
+ return true;
+ }
+ return false;
+ });
+ }
+ return success;
+}
+
+uint32_t
+SymbolFileDWARFDebugMap::ResolveSymbolContext(const Address &exe_so_addr,
+ SymbolContextItem resolve_scope,
+ SymbolContext &sc) {
+ uint32_t resolved_flags = 0;
+ Symtab *symtab = m_obj_file->GetSymtab();
+ if (symtab) {
+ const addr_t exe_file_addr = exe_so_addr.GetFileAddress();
+
+ const DebugMap::Entry *debug_map_entry =
+ m_debug_map.FindEntryThatContains(exe_file_addr);
+ if (debug_map_entry) {
+
+ sc.symbol =
+ symtab->SymbolAtIndex(debug_map_entry->data.GetExeSymbolIndex());
+
+ if (sc.symbol != nullptr) {
+ resolved_flags |= eSymbolContextSymbol;
+
+ uint32_t oso_idx = 0;
+ CompileUnitInfo *comp_unit_info =
+ GetCompileUnitInfoForSymbolWithID(sc.symbol->GetID(), &oso_idx);
+ if (comp_unit_info) {
+ comp_unit_info->GetFileRangeMap(this);
+ Module *oso_module = GetModuleByCompUnitInfo(comp_unit_info);
+ if (oso_module) {
+ lldb::addr_t oso_file_addr =
+ exe_file_addr - debug_map_entry->GetRangeBase() +
+ debug_map_entry->data.GetOSOFileAddress();
+ Address oso_so_addr;
+ if (oso_module->ResolveFileAddress(oso_file_addr, oso_so_addr)) {
+ resolved_flags |=
+ oso_module->GetSymbolVendor()->ResolveSymbolContext(
+ oso_so_addr, resolve_scope, sc);
+ }
+ }
+ }
+ }
+ }
+ }
+ return resolved_flags;
+}
+
+uint32_t SymbolFileDWARFDebugMap::ResolveSymbolContext(
+ const FileSpec &file_spec, uint32_t line, bool check_inlines,
+ SymbolContextItem resolve_scope, SymbolContextList &sc_list) {
+ const uint32_t initial = sc_list.GetSize();
+ const uint32_t cu_count = GetNumCompileUnits();
+
+ for (uint32_t i = 0; i < cu_count; ++i) {
+ // If we are checking for inlines, then we need to look through all compile
+ // units no matter if "file_spec" matches.
+ bool resolve = check_inlines;
+
+ if (!resolve) {
+ FileSpec so_file_spec;
+ if (GetFileSpecForSO(i, so_file_spec)) {
+ // Match the full path if the incoming file_spec has a directory (not
+ // just a basename)
+ const bool full_match = (bool)file_spec.GetDirectory();
+ resolve = FileSpec::Equal(file_spec, so_file_spec, full_match);
+ }
+ }
+ if (resolve) {
+ SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(i);
+ if (oso_dwarf)
+ oso_dwarf->ResolveSymbolContext(file_spec, line, check_inlines,
+ resolve_scope, sc_list);
+ }
+ }
+ return sc_list.GetSize() - initial;
+}
+
+uint32_t SymbolFileDWARFDebugMap::PrivateFindGlobalVariables(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ const std::vector<uint32_t>
+ &indexes, // Indexes into the symbol table that match "name"
+ uint32_t max_matches, VariableList &variables) {
+ const uint32_t original_size = variables.GetSize();
+ const size_t match_count = indexes.size();
+ for (size_t i = 0; i < match_count; ++i) {
+ uint32_t oso_idx;
+ CompileUnitInfo *comp_unit_info =
+ GetCompileUnitInfoForSymbolWithIndex(indexes[i], &oso_idx);
+ if (comp_unit_info) {
+ SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx);
+ if (oso_dwarf) {
+ if (oso_dwarf->FindGlobalVariables(name, parent_decl_ctx, max_matches,
+ variables))
+ if (variables.GetSize() > max_matches)
+ break;
+ }
+ }
+ }
+ return variables.GetSize() - original_size;
+}
+
+uint32_t SymbolFileDWARFDebugMap::FindGlobalVariables(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches, VariableList &variables) {
+
+ // Remember how many variables are in the list before we search.
+ const uint32_t original_size = variables.GetSize();
+
+ uint32_t total_matches = 0;
+
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ const uint32_t oso_matches = oso_dwarf->FindGlobalVariables(
+ name, parent_decl_ctx, max_matches, variables);
+ if (oso_matches > 0) {
+ total_matches += oso_matches;
+
+ // Are we getting all matches?
+ if (max_matches == UINT32_MAX)
+ return false; // Yep, continue getting everything
+
+ // If we have found enough matches, lets get out
+ if (max_matches >= total_matches)
+ return true;
+
+ // Update the max matches for any subsequent calls to find globals in any
+ // other object files with DWARF
+ max_matches -= oso_matches;
+ }
+
+ return false;
+ });
+
+ // Return the number of variable that were appended to the list
+ return variables.GetSize() - original_size;
+}
+
+uint32_t
+SymbolFileDWARFDebugMap::FindGlobalVariables(const RegularExpression &regex,
+ uint32_t max_matches,
+ VariableList &variables) {
+ // Remember how many variables are in the list before we search.
+ const uint32_t original_size = variables.GetSize();
+
+ uint32_t total_matches = 0;
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ const uint32_t oso_matches =
+ oso_dwarf->FindGlobalVariables(regex, max_matches, variables);
+ if (oso_matches > 0) {
+ total_matches += oso_matches;
+
+ // Are we getting all matches?
+ if (max_matches == UINT32_MAX)
+ return false; // Yep, continue getting everything
+
+ // If we have found enough matches, lets get out
+ if (max_matches >= total_matches)
+ return true;
+
+ // Update the max matches for any subsequent calls to find globals in any
+ // other object files with DWARF
+ max_matches -= oso_matches;
+ }
+
+ return false;
+ });
+
+ // Return the number of variable that were appended to the list
+ return variables.GetSize() - original_size;
+}
+
+int SymbolFileDWARFDebugMap::SymbolContainsSymbolWithIndex(
+ uint32_t *symbol_idx_ptr, const CompileUnitInfo *comp_unit_info) {
+ const uint32_t symbol_idx = *symbol_idx_ptr;
+
+ if (symbol_idx < comp_unit_info->first_symbol_index)
+ return -1;
+
+ if (symbol_idx <= comp_unit_info->last_symbol_index)
+ return 0;
+
+ return 1;
+}
+
+int SymbolFileDWARFDebugMap::SymbolContainsSymbolWithID(
+ user_id_t *symbol_idx_ptr, const CompileUnitInfo *comp_unit_info) {
+ const user_id_t symbol_id = *symbol_idx_ptr;
+
+ if (symbol_id < comp_unit_info->first_symbol_id)
+ return -1;
+
+ if (symbol_id <= comp_unit_info->last_symbol_id)
+ return 0;
+
+ return 1;
+}
+
+SymbolFileDWARFDebugMap::CompileUnitInfo *
+SymbolFileDWARFDebugMap::GetCompileUnitInfoForSymbolWithIndex(
+ uint32_t symbol_idx, uint32_t *oso_idx_ptr) {
+ const uint32_t oso_index_count = m_compile_unit_infos.size();
+ CompileUnitInfo *comp_unit_info = nullptr;
+ if (oso_index_count) {
+ comp_unit_info = (CompileUnitInfo *)bsearch(
+ &symbol_idx, &m_compile_unit_infos[0], m_compile_unit_infos.size(),
+ sizeof(CompileUnitInfo),
+ (ComparisonFunction)SymbolContainsSymbolWithIndex);
+ }
+
+ if (oso_idx_ptr) {
+ if (comp_unit_info != nullptr)
+ *oso_idx_ptr = comp_unit_info - &m_compile_unit_infos[0];
+ else
+ *oso_idx_ptr = UINT32_MAX;
+ }
+ return comp_unit_info;
+}
+
+SymbolFileDWARFDebugMap::CompileUnitInfo *
+SymbolFileDWARFDebugMap::GetCompileUnitInfoForSymbolWithID(
+ user_id_t symbol_id, uint32_t *oso_idx_ptr) {
+ const uint32_t oso_index_count = m_compile_unit_infos.size();
+ CompileUnitInfo *comp_unit_info = nullptr;
+ if (oso_index_count) {
+ comp_unit_info = (CompileUnitInfo *)::bsearch(
+ &symbol_id, &m_compile_unit_infos[0], m_compile_unit_infos.size(),
+ sizeof(CompileUnitInfo),
+ (ComparisonFunction)SymbolContainsSymbolWithID);
+ }
+
+ if (oso_idx_ptr) {
+ if (comp_unit_info != nullptr)
+ *oso_idx_ptr = comp_unit_info - &m_compile_unit_infos[0];
+ else
+ *oso_idx_ptr = UINT32_MAX;
+ }
+ return comp_unit_info;
+}
+
+static void RemoveFunctionsWithModuleNotEqualTo(const ModuleSP &module_sp,
+ SymbolContextList &sc_list,
+ uint32_t start_idx) {
+ // We found functions in .o files. Not all functions in the .o files will
+ // have made it into the final output file. The ones that did make it into
+ // the final output file will have a section whose module matches the module
+ // from the ObjectFile for this SymbolFile. When the modules don't match,
+ // then we have something that was in a .o file, but doesn't map to anything
+ // in the final executable.
+ uint32_t i = start_idx;
+ while (i < sc_list.GetSize()) {
+ SymbolContext sc;
+ sc_list.GetContextAtIndex(i, sc);
+ if (sc.function) {
+ const SectionSP section_sp(
+ sc.function->GetAddressRange().GetBaseAddress().GetSection());
+ if (section_sp->GetModule() != module_sp) {
+ sc_list.RemoveContextAtIndex(i);
+ continue;
+ }
+ }
+ ++i;
+ }
+}
+
+uint32_t SymbolFileDWARFDebugMap::FindFunctions(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ FunctionNameType name_type_mask, bool include_inlines, bool append,
+ SymbolContextList &sc_list) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat,
+ "SymbolFileDWARFDebugMap::FindFunctions (name = %s)",
+ name.GetCString());
+
+ uint32_t initial_size = 0;
+ if (append)
+ initial_size = sc_list.GetSize();
+ else
+ sc_list.Clear();
+
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ uint32_t sc_idx = sc_list.GetSize();
+ if (oso_dwarf->FindFunctions(name, parent_decl_ctx, name_type_mask,
+ include_inlines, true, sc_list)) {
+ RemoveFunctionsWithModuleNotEqualTo(m_obj_file->GetModule(), sc_list,
+ sc_idx);
+ }
+ return false;
+ });
+
+ return sc_list.GetSize() - initial_size;
+}
+
+uint32_t SymbolFileDWARFDebugMap::FindFunctions(const RegularExpression &regex,
+ bool include_inlines,
+ bool append,
+ SymbolContextList &sc_list) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat,
+ "SymbolFileDWARFDebugMap::FindFunctions (regex = '%s')",
+ regex.GetText().str().c_str());
+
+ uint32_t initial_size = 0;
+ if (append)
+ initial_size = sc_list.GetSize();
+ else
+ sc_list.Clear();
+
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ uint32_t sc_idx = sc_list.GetSize();
+
+ if (oso_dwarf->FindFunctions(regex, include_inlines, true, sc_list)) {
+ RemoveFunctionsWithModuleNotEqualTo(m_obj_file->GetModule(), sc_list,
+ sc_idx);
+ }
+ return false;
+ });
+
+ return sc_list.GetSize() - initial_size;
+}
+
+size_t SymbolFileDWARFDebugMap::GetTypes(SymbolContextScope *sc_scope,
+ lldb::TypeClass type_mask,
+ TypeList &type_list) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat,
+ "SymbolFileDWARFDebugMap::GetTypes (type_mask = 0x%8.8x)",
+ type_mask);
+
+ uint32_t initial_size = type_list.GetSize();
+ SymbolFileDWARF *oso_dwarf = nullptr;
+ if (sc_scope) {
+ SymbolContext sc;
+ sc_scope->CalculateSymbolContext(&sc);
+
+ CompileUnitInfo *cu_info = GetCompUnitInfo(sc);
+ if (cu_info) {
+ oso_dwarf = GetSymbolFileByCompUnitInfo(cu_info);
+ if (oso_dwarf)
+ oso_dwarf->GetTypes(sc_scope, type_mask, type_list);
+ }
+ } else {
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ oso_dwarf->GetTypes(sc_scope, type_mask, type_list);
+ return false;
+ });
+ }
+ return type_list.GetSize() - initial_size;
+}
+
+std::vector<lldb_private::CallEdge>
+SymbolFileDWARFDebugMap::ParseCallEdgesInFunction(UserID func_id) {
+ uint32_t oso_idx = GetOSOIndexFromUserID(func_id.GetID());
+ SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx);
+ if (oso_dwarf)
+ return oso_dwarf->ParseCallEdgesInFunction(func_id);
+ return {};
+}
+
+TypeSP SymbolFileDWARFDebugMap::FindDefinitionTypeForDWARFDeclContext(
+ const DWARFDeclContext &die_decl_ctx) {
+ TypeSP type_sp;
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ type_sp = oso_dwarf->FindDefinitionTypeForDWARFDeclContext(die_decl_ctx);
+ return ((bool)type_sp);
+ });
+ return type_sp;
+}
+
+bool SymbolFileDWARFDebugMap::Supports_DW_AT_APPLE_objc_complete_type(
+ SymbolFileDWARF *skip_dwarf_oso) {
+ if (m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolCalculate) {
+ m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolNo;
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ if (skip_dwarf_oso != oso_dwarf &&
+ oso_dwarf->Supports_DW_AT_APPLE_objc_complete_type(nullptr)) {
+ m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolYes;
+ return true;
+ }
+ return false;
+ });
+ }
+ return m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolYes;
+}
+
+TypeSP SymbolFileDWARFDebugMap::FindCompleteObjCDefinitionTypeForDIE(
+ const DWARFDIE &die, ConstString type_name,
+ bool must_be_implementation) {
+ // If we have a debug map, we will have an Objective-C symbol whose name is
+ // the type name and whose type is eSymbolTypeObjCClass. If we can find that
+ // symbol and find its containing parent, we can locate the .o file that will
+ // contain the implementation definition since it will be scoped inside the
+ // N_SO and we can then locate the SymbolFileDWARF that corresponds to that
+ // N_SO.
+ SymbolFileDWARF *oso_dwarf = nullptr;
+ TypeSP type_sp;
+ ObjectFile *module_objfile = m_obj_file->GetModule()->GetObjectFile();
+ if (module_objfile) {
+ Symtab *symtab = module_objfile->GetSymtab();
+ if (symtab) {
+ Symbol *objc_class_symbol = symtab->FindFirstSymbolWithNameAndType(
+ type_name, eSymbolTypeObjCClass, Symtab::eDebugAny,
+ Symtab::eVisibilityAny);
+ if (objc_class_symbol) {
+ // Get the N_SO symbol that contains the objective C class symbol as
+ // this should be the .o file that contains the real definition...
+ const Symbol *source_file_symbol = symtab->GetParent(objc_class_symbol);
+
+ if (source_file_symbol &&
+ source_file_symbol->GetType() == eSymbolTypeSourceFile) {
+ const uint32_t source_file_symbol_idx =
+ symtab->GetIndexForSymbol(source_file_symbol);
+ if (source_file_symbol_idx != UINT32_MAX) {
+ CompileUnitInfo *compile_unit_info =
+ GetCompileUnitInfoForSymbolWithIndex(source_file_symbol_idx,
+ nullptr);
+ if (compile_unit_info) {
+ oso_dwarf = GetSymbolFileByCompUnitInfo(compile_unit_info);
+ if (oso_dwarf) {
+ TypeSP type_sp(oso_dwarf->FindCompleteObjCDefinitionTypeForDIE(
+ die, type_name, must_be_implementation));
+ if (type_sp) {
+ return type_sp;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Only search all .o files for the definition if we don't need the
+ // implementation because otherwise, with a valid debug map we should have
+ // the ObjC class symbol and the code above should have found it.
+ if (!must_be_implementation) {
+ TypeSP type_sp;
+
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ type_sp = oso_dwarf->FindCompleteObjCDefinitionTypeForDIE(
+ die, type_name, must_be_implementation);
+ return (bool)type_sp;
+ });
+
+ return type_sp;
+ }
+ return TypeSP();
+}
+
+uint32_t SymbolFileDWARFDebugMap::FindTypes(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ bool append, uint32_t max_matches,
+ llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files,
+ TypeMap &types) {
+ if (!append)
+ types.Clear();
+
+ const uint32_t initial_types_size = types.GetSize();
+
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ oso_dwarf->FindTypes(name, parent_decl_ctx, append, max_matches,
+ searched_symbol_files, types);
+ return types.GetSize() >= max_matches;
+ });
+
+ return types.GetSize() - initial_types_size;
+}
+
+//
+// uint32_t
+// SymbolFileDWARFDebugMap::FindTypes (const SymbolContext& sc, const
+// RegularExpression& regex, bool append, uint32_t max_matches, Type::Encoding
+// encoding, lldb::user_id_t udt_uid, TypeList& types)
+//{
+// SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc);
+// if (oso_dwarf)
+// return oso_dwarf->FindTypes (sc, regex, append, max_matches, encoding,
+// udt_uid, types);
+// return 0;
+//}
+
+CompilerDeclContext SymbolFileDWARFDebugMap::FindNamespace(
+ lldb_private::ConstString name,
+ const CompilerDeclContext *parent_decl_ctx) {
+ CompilerDeclContext matching_namespace;
+
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ matching_namespace = oso_dwarf->FindNamespace(name, parent_decl_ctx);
+
+ return (bool)matching_namespace;
+ });
+
+ return matching_namespace;
+}
+
+void SymbolFileDWARFDebugMap::DumpClangAST(Stream &s) {
+ ForEachSymbolFile([&s](SymbolFileDWARF *oso_dwarf) -> bool {
+ oso_dwarf->DumpClangAST(s);
+ return true;
+ });
+}
+
+// PluginInterface protocol
+lldb_private::ConstString SymbolFileDWARFDebugMap::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t SymbolFileDWARFDebugMap::GetPluginVersion() { return 1; }
+
+lldb::CompUnitSP
+SymbolFileDWARFDebugMap::GetCompileUnit(SymbolFileDWARF *oso_dwarf) {
+ if (oso_dwarf) {
+ const uint32_t cu_count = GetNumCompileUnits();
+ for (uint32_t cu_idx = 0; cu_idx < cu_count; ++cu_idx) {
+ SymbolFileDWARF *oso_symfile =
+ GetSymbolFileByCompUnitInfo(&m_compile_unit_infos[cu_idx]);
+ if (oso_symfile == oso_dwarf) {
+ if (!m_compile_unit_infos[cu_idx].compile_unit_sp)
+ m_compile_unit_infos[cu_idx].compile_unit_sp =
+ ParseCompileUnitAtIndex(cu_idx);
+
+ return m_compile_unit_infos[cu_idx].compile_unit_sp;
+ }
+ }
+ }
+ llvm_unreachable("this shouldn't happen");
+}
+
+SymbolFileDWARFDebugMap::CompileUnitInfo *
+SymbolFileDWARFDebugMap::GetCompileUnitInfo(SymbolFileDWARF *oso_dwarf) {
+ if (oso_dwarf) {
+ const uint32_t cu_count = GetNumCompileUnits();
+ for (uint32_t cu_idx = 0; cu_idx < cu_count; ++cu_idx) {
+ SymbolFileDWARF *oso_symfile =
+ GetSymbolFileByCompUnitInfo(&m_compile_unit_infos[cu_idx]);
+ if (oso_symfile == oso_dwarf) {
+ return &m_compile_unit_infos[cu_idx];
+ }
+ }
+ }
+ return nullptr;
+}
+
+void SymbolFileDWARFDebugMap::SetCompileUnit(SymbolFileDWARF *oso_dwarf,
+ const CompUnitSP &cu_sp) {
+ if (oso_dwarf) {
+ const uint32_t cu_count = GetNumCompileUnits();
+ for (uint32_t cu_idx = 0; cu_idx < cu_count; ++cu_idx) {
+ SymbolFileDWARF *oso_symfile =
+ GetSymbolFileByCompUnitInfo(&m_compile_unit_infos[cu_idx]);
+ if (oso_symfile == oso_dwarf) {
+ if (m_compile_unit_infos[cu_idx].compile_unit_sp) {
+ assert(m_compile_unit_infos[cu_idx].compile_unit_sp.get() ==
+ cu_sp.get());
+ } else {
+ m_compile_unit_infos[cu_idx].compile_unit_sp = cu_sp;
+ m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex(
+ cu_idx, cu_sp);
+ }
+ }
+ }
+ }
+}
+
+CompilerDeclContext
+SymbolFileDWARFDebugMap::GetDeclContextForUID(lldb::user_id_t type_uid) {
+ const uint64_t oso_idx = GetOSOIndexFromUserID(type_uid);
+ SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx);
+ if (oso_dwarf)
+ return oso_dwarf->GetDeclContextForUID(type_uid);
+ return CompilerDeclContext();
+}
+
+CompilerDeclContext
+SymbolFileDWARFDebugMap::GetDeclContextContainingUID(lldb::user_id_t type_uid) {
+ const uint64_t oso_idx = GetOSOIndexFromUserID(type_uid);
+ SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx);
+ if (oso_dwarf)
+ return oso_dwarf->GetDeclContextContainingUID(type_uid);
+ return CompilerDeclContext();
+}
+
+void SymbolFileDWARFDebugMap::ParseDeclsForContext(
+ lldb_private::CompilerDeclContext decl_ctx) {
+ ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool {
+ oso_dwarf->ParseDeclsForContext(decl_ctx);
+ return true; // Keep iterating
+ });
+}
+
+bool SymbolFileDWARFDebugMap::AddOSOFileRange(CompileUnitInfo *cu_info,
+ lldb::addr_t exe_file_addr,
+ lldb::addr_t exe_byte_size,
+ lldb::addr_t oso_file_addr,
+ lldb::addr_t oso_byte_size) {
+ const uint32_t debug_map_idx =
+ m_debug_map.FindEntryIndexThatContains(exe_file_addr);
+ if (debug_map_idx != UINT32_MAX) {
+ DebugMap::Entry *debug_map_entry =
+ m_debug_map.FindEntryThatContains(exe_file_addr);
+ debug_map_entry->data.SetOSOFileAddress(oso_file_addr);
+ addr_t range_size = std::min<addr_t>(exe_byte_size, oso_byte_size);
+ if (range_size == 0) {
+ range_size = std::max<addr_t>(exe_byte_size, oso_byte_size);
+ if (range_size == 0)
+ range_size = 1;
+ }
+ cu_info->file_range_map.Append(
+ FileRangeMap::Entry(oso_file_addr, range_size, exe_file_addr));
+ return true;
+ }
+ return false;
+}
+
+void SymbolFileDWARFDebugMap::FinalizeOSOFileRanges(CompileUnitInfo *cu_info) {
+ cu_info->file_range_map.Sort();
+#if defined(DEBUG_OSO_DMAP)
+ const FileRangeMap &oso_file_range_map = cu_info->GetFileRangeMap(this);
+ const size_t n = oso_file_range_map.GetSize();
+ printf("SymbolFileDWARFDebugMap::FinalizeOSOFileRanges (cu_info = %p) %s\n",
+ cu_info, cu_info->oso_sp->module_sp->GetFileSpec().GetPath().c_str());
+ for (size_t i = 0; i < n; ++i) {
+ const FileRangeMap::Entry &entry = oso_file_range_map.GetEntryRef(i);
+ printf("oso [0x%16.16" PRIx64 " - 0x%16.16" PRIx64
+ ") ==> exe [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 ")\n",
+ entry.GetRangeBase(), entry.GetRangeEnd(), entry.data,
+ entry.data + entry.GetByteSize());
+ }
+#endif
+}
+
+lldb::addr_t
+SymbolFileDWARFDebugMap::LinkOSOFileAddress(SymbolFileDWARF *oso_symfile,
+ lldb::addr_t oso_file_addr) {
+ CompileUnitInfo *cu_info = GetCompileUnitInfo(oso_symfile);
+ if (cu_info) {
+ const FileRangeMap::Entry *oso_range_entry =
+ cu_info->GetFileRangeMap(this).FindEntryThatContains(oso_file_addr);
+ if (oso_range_entry) {
+ const DebugMap::Entry *debug_map_entry =
+ m_debug_map.FindEntryThatContains(oso_range_entry->data);
+ if (debug_map_entry) {
+ const lldb::addr_t offset =
+ oso_file_addr - oso_range_entry->GetRangeBase();
+ const lldb::addr_t exe_file_addr =
+ debug_map_entry->GetRangeBase() + offset;
+ return exe_file_addr;
+ }
+ }
+ }
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool SymbolFileDWARFDebugMap::LinkOSOAddress(Address &addr) {
+ // Make sure this address hasn't been fixed already
+ Module *exe_module = GetObjectFile()->GetModule().get();
+ Module *addr_module = addr.GetModule().get();
+ if (addr_module == exe_module)
+ return true; // Address is already in terms of the main executable module
+
+ CompileUnitInfo *cu_info = GetCompileUnitInfo(GetSymbolFileAsSymbolFileDWARF(
+ addr_module->GetSymbolVendor()->GetSymbolFile()));
+ if (cu_info) {
+ const lldb::addr_t oso_file_addr = addr.GetFileAddress();
+ const FileRangeMap::Entry *oso_range_entry =
+ cu_info->GetFileRangeMap(this).FindEntryThatContains(oso_file_addr);
+ if (oso_range_entry) {
+ const DebugMap::Entry *debug_map_entry =
+ m_debug_map.FindEntryThatContains(oso_range_entry->data);
+ if (debug_map_entry) {
+ const lldb::addr_t offset =
+ oso_file_addr - oso_range_entry->GetRangeBase();
+ const lldb::addr_t exe_file_addr =
+ debug_map_entry->GetRangeBase() + offset;
+ return exe_module->ResolveFileAddress(exe_file_addr, addr);
+ }
+ }
+ }
+ return true;
+}
+
+LineTable *SymbolFileDWARFDebugMap::LinkOSOLineTable(SymbolFileDWARF *oso_dwarf,
+ LineTable *line_table) {
+ CompileUnitInfo *cu_info = GetCompileUnitInfo(oso_dwarf);
+ if (cu_info)
+ return line_table->LinkLineTable(cu_info->GetFileRangeMap(this));
+ return nullptr;
+}
+
+size_t
+SymbolFileDWARFDebugMap::AddOSOARanges(SymbolFileDWARF *dwarf2Data,
+ DWARFDebugAranges *debug_aranges) {
+ size_t num_line_entries_added = 0;
+ if (debug_aranges && dwarf2Data) {
+ CompileUnitInfo *compile_unit_info = GetCompileUnitInfo(dwarf2Data);
+ if (compile_unit_info) {
+ const FileRangeMap &file_range_map =
+ compile_unit_info->GetFileRangeMap(this);
+ for (size_t idx = 0; idx < file_range_map.GetSize(); idx++) {
+ const FileRangeMap::Entry *entry = file_range_map.GetEntryAtIndex(idx);
+ if (entry) {
+ debug_aranges->AppendRange(dwarf2Data->GetID(), entry->GetRangeBase(),
+ entry->GetRangeEnd());
+ num_line_entries_added++;
+ }
+ }
+ }
+ }
+ return num_line_entries_added;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
new file mode 100644
index 000000000000..afc6142e8231
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
@@ -0,0 +1,362 @@
+//===-- SymbolFileDWARFDebugMap.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 SymbolFileDWARF_SymbolFileDWARFDebugMap_h_
+#define SymbolFileDWARF_SymbolFileDWARFDebugMap_h_
+
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Utility/RangeMap.h"
+#include "llvm/Support/Chrono.h"
+#include <bitset>
+#include <map>
+#include <vector>
+
+#include "UniqueDWARFASTType.h"
+
+class SymbolFileDWARF;
+class DWARFDebugAranges;
+class DWARFDeclContext;
+
+class SymbolFileDWARFDebugMap : public lldb_private::SymbolFile {
+public:
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::SymbolFile *
+ CreateInstance(lldb_private::ObjectFile *obj_file);
+
+ // Constructors and Destructors
+ SymbolFileDWARFDebugMap(lldb_private::ObjectFile *ofile);
+ ~SymbolFileDWARFDebugMap() override;
+
+ uint32_t CalculateAbilities() override;
+ void InitializeObject() override;
+
+ // Compile Unit function calls
+ uint32_t GetNumCompileUnits() override;
+ lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override;
+
+ lldb::LanguageType
+ ParseLanguage(lldb_private::CompileUnit &comp_unit) override;
+
+ size_t ParseFunctions(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseLineTable(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseDebugMacros(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseSupportFiles(lldb_private::CompileUnit &comp_unit,
+ lldb_private::FileSpecList &support_files) override;
+
+ bool ParseIsOptimized(lldb_private::CompileUnit &comp_unit) override;
+
+ size_t ParseTypes(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseImportedModules(
+ const lldb_private::SymbolContext &sc,
+ std::vector<lldb_private::SourceModule> &imported_modules) override;
+ size_t ParseBlocksRecursive(lldb_private::Function &func) override;
+ size_t
+ ParseVariablesForContext(const lldb_private::SymbolContext &sc) override;
+
+ lldb_private::Type *ResolveTypeUID(lldb::user_id_t type_uid) override;
+ llvm::Optional<ArrayInfo> GetDynamicArrayInfoForUID(
+ lldb::user_id_t type_uid,
+ const lldb_private::ExecutionContext *exe_ctx) override;
+
+ lldb_private::CompilerDeclContext
+ GetDeclContextForUID(lldb::user_id_t uid) override;
+ lldb_private::CompilerDeclContext
+ GetDeclContextContainingUID(lldb::user_id_t uid) override;
+ void
+ ParseDeclsForContext(lldb_private::CompilerDeclContext decl_ctx) override;
+
+ bool CompleteType(lldb_private::CompilerType &compiler_type) override;
+ uint32_t ResolveSymbolContext(const lldb_private::Address &so_addr,
+ lldb::SymbolContextItem resolve_scope,
+ lldb_private::SymbolContext &sc) override;
+ uint32_t
+ ResolveSymbolContext(const lldb_private::FileSpec &file_spec, uint32_t line,
+ bool check_inlines,
+ lldb::SymbolContextItem resolve_scope,
+ lldb_private::SymbolContextList &sc_list) override;
+ uint32_t
+ FindGlobalVariables(lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches,
+ lldb_private::VariableList &variables) override;
+ uint32_t FindGlobalVariables(const lldb_private::RegularExpression &regex,
+ uint32_t max_matches,
+ lldb_private::VariableList &variables) override;
+ uint32_t
+ FindFunctions(lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ lldb::FunctionNameType name_type_mask, bool include_inlines,
+ bool append, lldb_private::SymbolContextList &sc_list) override;
+ uint32_t FindFunctions(const lldb_private::RegularExpression &regex,
+ bool include_inlines, bool append,
+ lldb_private::SymbolContextList &sc_list) override;
+ uint32_t
+ FindTypes(lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ bool append, uint32_t max_matches,
+ llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files,
+ lldb_private::TypeMap &types) override;
+ lldb_private::CompilerDeclContext FindNamespace(
+ lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx) override;
+ size_t GetTypes(lldb_private::SymbolContextScope *sc_scope,
+ lldb::TypeClass type_mask,
+ lldb_private::TypeList &type_list) override;
+ std::vector<lldb_private::CallEdge>
+ ParseCallEdgesInFunction(lldb_private::UserID func_id) override;
+
+ void DumpClangAST(lldb_private::Stream &s) override;
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ enum { kHaveInitializedOSOs = (1 << 0), kNumFlags };
+
+ friend class DebugMapModule;
+ friend class DWARFASTParserClang;
+ friend class DWARFCompileUnit;
+ friend class SymbolFileDWARF;
+ struct OSOInfo {
+ lldb::ModuleSP module_sp;
+
+ OSOInfo() : module_sp() {}
+ };
+
+ typedef std::shared_ptr<OSOInfo> OSOInfoSP;
+
+ typedef lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t,
+ lldb::addr_t>
+ FileRangeMap;
+
+ // Class specific types
+ struct CompileUnitInfo {
+ lldb_private::FileSpec so_file;
+ lldb_private::ConstString oso_path;
+ llvm::sys::TimePoint<> oso_mod_time;
+ OSOInfoSP oso_sp;
+ lldb::CompUnitSP compile_unit_sp;
+ uint32_t first_symbol_index;
+ uint32_t last_symbol_index;
+ uint32_t first_symbol_id;
+ uint32_t last_symbol_id;
+ FileRangeMap file_range_map;
+ bool file_range_map_valid;
+
+ CompileUnitInfo()
+ : so_file(), oso_path(), oso_mod_time(), oso_sp(), compile_unit_sp(),
+ first_symbol_index(UINT32_MAX), last_symbol_index(UINT32_MAX),
+ first_symbol_id(UINT32_MAX), last_symbol_id(UINT32_MAX),
+ file_range_map(), file_range_map_valid(false) {}
+
+ const FileRangeMap &GetFileRangeMap(SymbolFileDWARFDebugMap *exe_symfile);
+ };
+
+ // Protected Member Functions
+ void InitOSO();
+
+ static uint32_t GetOSOIndexFromUserID(lldb::user_id_t uid) {
+ return (uint32_t)((uid >> 32ull) - 1ull);
+ }
+
+ static SymbolFileDWARF *GetSymbolFileAsSymbolFileDWARF(SymbolFile *sym_file);
+
+ bool GetFileSpecForSO(uint32_t oso_idx, lldb_private::FileSpec &file_spec);
+
+ CompileUnitInfo *GetCompUnitInfo(const lldb_private::SymbolContext &sc);
+ CompileUnitInfo *GetCompUnitInfo(const lldb_private::CompileUnit &comp_unit);
+
+ size_t GetCompUnitInfosForModule(const lldb_private::Module *oso_module,
+ std::vector<CompileUnitInfo *> &cu_infos);
+
+ lldb_private::Module *
+ GetModuleByCompUnitInfo(CompileUnitInfo *comp_unit_info);
+
+ lldb_private::Module *GetModuleByOSOIndex(uint32_t oso_idx);
+
+ lldb_private::ObjectFile *
+ GetObjectFileByCompUnitInfo(CompileUnitInfo *comp_unit_info);
+
+ lldb_private::ObjectFile *GetObjectFileByOSOIndex(uint32_t oso_idx);
+
+ uint32_t GetCompUnitInfoIndex(const CompileUnitInfo *comp_unit_info);
+
+ SymbolFileDWARF *GetSymbolFile(const lldb_private::SymbolContext &sc);
+ SymbolFileDWARF *GetSymbolFile(const lldb_private::CompileUnit &comp_unit);
+
+ SymbolFileDWARF *GetSymbolFileByCompUnitInfo(CompileUnitInfo *comp_unit_info);
+
+ SymbolFileDWARF *GetSymbolFileByOSOIndex(uint32_t oso_idx);
+
+ // If closure returns "false", iteration continues. If it returns
+ // "true", iteration terminates.
+ void ForEachSymbolFile(std::function<bool(SymbolFileDWARF *)> closure) {
+ for (uint32_t oso_idx = 0, num_oso_idxs = m_compile_unit_infos.size();
+ oso_idx < num_oso_idxs; ++oso_idx) {
+ if (SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx)) {
+ if (closure(oso_dwarf))
+ return;
+ }
+ }
+ }
+
+ CompileUnitInfo *GetCompileUnitInfoForSymbolWithIndex(uint32_t symbol_idx,
+ uint32_t *oso_idx_ptr);
+
+ CompileUnitInfo *GetCompileUnitInfoForSymbolWithID(lldb::user_id_t symbol_id,
+ uint32_t *oso_idx_ptr);
+
+ static int
+ SymbolContainsSymbolWithIndex(uint32_t *symbol_idx_ptr,
+ const CompileUnitInfo *comp_unit_info);
+
+ static int SymbolContainsSymbolWithID(lldb::user_id_t *symbol_idx_ptr,
+ const CompileUnitInfo *comp_unit_info);
+
+ uint32_t PrivateFindGlobalVariables(
+ lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ const std::vector<uint32_t> &name_symbol_indexes, uint32_t max_matches,
+ lldb_private::VariableList &variables);
+
+ void SetCompileUnit(SymbolFileDWARF *oso_dwarf,
+ const lldb::CompUnitSP &cu_sp);
+
+ lldb::CompUnitSP GetCompileUnit(SymbolFileDWARF *oso_dwarf);
+
+ CompileUnitInfo *GetCompileUnitInfo(SymbolFileDWARF *oso_dwarf);
+
+ lldb::TypeSP
+ FindDefinitionTypeForDWARFDeclContext(const DWARFDeclContext &die_decl_ctx);
+
+ bool Supports_DW_AT_APPLE_objc_complete_type(SymbolFileDWARF *skip_dwarf_oso);
+
+ lldb::TypeSP FindCompleteObjCDefinitionTypeForDIE(
+ const DWARFDIE &die, lldb_private::ConstString type_name,
+ bool must_be_implementation);
+
+ UniqueDWARFASTTypeMap &GetUniqueDWARFASTTypeMap() {
+ return m_unique_ast_type_map;
+ }
+
+ // OSOEntry
+ class OSOEntry {
+ public:
+ OSOEntry()
+ : m_exe_sym_idx(UINT32_MAX), m_oso_file_addr(LLDB_INVALID_ADDRESS) {}
+
+ OSOEntry(uint32_t exe_sym_idx, lldb::addr_t oso_file_addr)
+ : m_exe_sym_idx(exe_sym_idx), m_oso_file_addr(oso_file_addr) {}
+
+ uint32_t GetExeSymbolIndex() const { return m_exe_sym_idx; }
+
+ bool operator<(const OSOEntry &rhs) const {
+ return m_exe_sym_idx < rhs.m_exe_sym_idx;
+ }
+
+ lldb::addr_t GetOSOFileAddress() const { return m_oso_file_addr; }
+
+ void SetOSOFileAddress(lldb::addr_t oso_file_addr) {
+ m_oso_file_addr = oso_file_addr;
+ }
+
+ protected:
+ uint32_t m_exe_sym_idx;
+ lldb::addr_t m_oso_file_addr;
+ };
+
+ typedef lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t, OSOEntry>
+ DebugMap;
+
+ // Member Variables
+ std::bitset<kNumFlags> m_flags;
+ std::vector<CompileUnitInfo> m_compile_unit_infos;
+ std::vector<uint32_t> m_func_indexes; // Sorted by address
+ std::vector<uint32_t> m_glob_indexes;
+ std::map<std::pair<lldb_private::ConstString, llvm::sys::TimePoint<>>,
+ OSOInfoSP>
+ m_oso_map;
+ UniqueDWARFASTTypeMap m_unique_ast_type_map;
+ lldb_private::LazyBool m_supports_DW_AT_APPLE_objc_complete_type;
+ DebugMap m_debug_map;
+
+ // When an object file from the debug map gets parsed in
+ // SymbolFileDWARF, it needs to tell the debug map about the object
+ // files addresses by calling this function once for each N_FUN,
+ // N_GSYM and N_STSYM and after all entries in the debug map have
+ // been matched up, FinalizeOSOFileRanges() should be called.
+ bool AddOSOFileRange(CompileUnitInfo *cu_info, lldb::addr_t exe_file_addr,
+ lldb::addr_t exe_byte_size, lldb::addr_t oso_file_addr,
+ lldb::addr_t oso_byte_size);
+
+ // Called after calling AddOSOFileRange() for each object file debug
+ // map entry to finalize the info for the unlinked compile unit.
+ void FinalizeOSOFileRanges(CompileUnitInfo *cu_info);
+
+ /// Convert \a addr from a .o file address, to an executable address.
+ ///
+ /// \param[in] addr
+ /// A section offset address from a .o file
+ ///
+ /// \return
+ /// Returns true if \a addr was converted to be an executable
+ /// section/offset address, false otherwise.
+ bool LinkOSOAddress(lldb_private::Address &addr);
+
+ /// Convert a .o file "file address" to an executable "file address".
+ ///
+ /// \param[in] oso_symfile
+ /// The DWARF symbol file that contains \a oso_file_addr
+ ///
+ /// \param[in] oso_file_addr
+ /// A .o file "file address" to convert.
+ ///
+ /// \return
+ /// LLDB_INVALID_ADDRESS if \a oso_file_addr is not in the
+ /// linked executable, otherwise a valid "file address" from the
+ /// linked executable that contains the debug map.
+ lldb::addr_t LinkOSOFileAddress(SymbolFileDWARF *oso_symfile,
+ lldb::addr_t oso_file_addr);
+
+ /// Given a line table full of lines with "file addresses" that are
+ /// for a .o file represented by \a oso_symfile, link a new line table
+ /// and return it.
+ ///
+ /// \param[in] oso_symfile
+ /// The DWARF symbol file that produced the \a line_table
+ ///
+ /// \param[in] addr
+ /// A section offset address from a .o file
+ ///
+ /// \return
+ /// Returns a valid line table full of linked addresses, or NULL
+ /// if none of the line table addresses exist in the main
+ /// executable.
+ lldb_private::LineTable *
+ LinkOSOLineTable(SymbolFileDWARF *oso_symfile,
+ lldb_private::LineTable *line_table);
+
+ size_t AddOSOARanges(SymbolFileDWARF *dwarf2Data,
+ DWARFDebugAranges *debug_aranges);
+};
+
+#endif // #ifndef SymbolFileDWARF_SymbolFileDWARFDebugMap_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp
new file mode 100644
index 000000000000..c5b54b65ea29
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp
@@ -0,0 +1,153 @@
+//===-- SymbolFileDWARFDwo.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 "SymbolFileDWARFDwo.h"
+
+#include "lldb/Core/Section.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "llvm/Support/Casting.h"
+
+#include "DWARFCompileUnit.h"
+#include "DWARFDebugInfo.h"
+#include "DWARFUnit.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SymbolFileDWARFDwo::SymbolFileDWARFDwo(ObjectFileSP objfile,
+ DWARFCompileUnit &dwarf_cu)
+ : SymbolFileDWARF(objfile.get(), objfile->GetSectionList(
+ /*update_module_section_list*/ false)),
+ m_obj_file_sp(objfile), m_base_dwarf_cu(dwarf_cu) {
+ SetID(((lldb::user_id_t)dwarf_cu.GetID()) << 32);
+}
+
+void SymbolFileDWARFDwo::LoadSectionData(lldb::SectionType sect_type,
+ DWARFDataExtractor &data) {
+ const SectionList *section_list =
+ m_obj_file->GetSectionList(false /* update_module_section_list */);
+ if (section_list) {
+ SectionSP section_sp(section_list->FindSectionByType(sect_type, true));
+ if (section_sp) {
+
+ if (m_obj_file->ReadSectionData(section_sp.get(), data) != 0)
+ return;
+
+ data.Clear();
+ }
+ }
+
+ SymbolFileDWARF::LoadSectionData(sect_type, data);
+}
+
+lldb::CompUnitSP
+SymbolFileDWARFDwo::ParseCompileUnit(DWARFCompileUnit &dwarf_cu) {
+ assert(GetCompileUnit() == &dwarf_cu &&
+ "SymbolFileDWARFDwo::ParseCompileUnit called with incompatible "
+ "compile unit");
+ return GetBaseSymbolFile().ParseCompileUnit(m_base_dwarf_cu);
+}
+
+DWARFCompileUnit *SymbolFileDWARFDwo::GetCompileUnit() {
+ if (!m_cu)
+ m_cu = ComputeCompileUnit();
+ return m_cu;
+}
+
+DWARFCompileUnit *SymbolFileDWARFDwo::ComputeCompileUnit() {
+ DWARFDebugInfo *debug_info = DebugInfo();
+ if (!debug_info)
+ return nullptr;
+
+ // Right now we only support dwo files with one compile unit. If we don't have
+ // type units, we can just check for the unit count.
+ if (!debug_info->ContainsTypeUnits() && debug_info->GetNumUnits() == 1)
+ return llvm::cast<DWARFCompileUnit>(debug_info->GetUnitAtIndex(0));
+
+ // Otherwise, we have to run through all units, and find the compile unit that
+ // way.
+ DWARFCompileUnit *cu = nullptr;
+ for (size_t i = 0; i < debug_info->GetNumUnits(); ++i) {
+ if (auto *candidate =
+ llvm::dyn_cast<DWARFCompileUnit>(debug_info->GetUnitAtIndex(i))) {
+ if (cu)
+ return nullptr; // More that one CU found.
+ cu = candidate;
+ }
+ }
+ return cu;
+}
+
+DWARFUnit *
+SymbolFileDWARFDwo::GetDWARFCompileUnit(lldb_private::CompileUnit *comp_unit) {
+ return GetCompileUnit();
+}
+
+SymbolFileDWARF::DIEToTypePtr &SymbolFileDWARFDwo::GetDIEToType() {
+ return GetBaseSymbolFile().GetDIEToType();
+}
+
+SymbolFileDWARF::DIEToVariableSP &SymbolFileDWARFDwo::GetDIEToVariable() {
+ return GetBaseSymbolFile().GetDIEToVariable();
+}
+
+SymbolFileDWARF::DIEToClangType &
+SymbolFileDWARFDwo::GetForwardDeclDieToClangType() {
+ return GetBaseSymbolFile().GetForwardDeclDieToClangType();
+}
+
+SymbolFileDWARF::ClangTypeToDIE &
+SymbolFileDWARFDwo::GetForwardDeclClangTypeToDie() {
+ return GetBaseSymbolFile().GetForwardDeclClangTypeToDie();
+}
+
+size_t SymbolFileDWARFDwo::GetObjCMethodDIEOffsets(
+ lldb_private::ConstString class_name, DIEArray &method_die_offsets) {
+ return GetBaseSymbolFile().GetObjCMethodDIEOffsets(class_name,
+ method_die_offsets);
+}
+
+UniqueDWARFASTTypeMap &SymbolFileDWARFDwo::GetUniqueDWARFASTTypeMap() {
+ return GetBaseSymbolFile().GetUniqueDWARFASTTypeMap();
+}
+
+lldb::TypeSP SymbolFileDWARFDwo::FindDefinitionTypeForDWARFDeclContext(
+ const DWARFDeclContext &die_decl_ctx) {
+ return GetBaseSymbolFile().FindDefinitionTypeForDWARFDeclContext(
+ die_decl_ctx);
+}
+
+lldb::TypeSP SymbolFileDWARFDwo::FindCompleteObjCDefinitionTypeForDIE(
+ const DWARFDIE &die, lldb_private::ConstString type_name,
+ bool must_be_implementation) {
+ return GetBaseSymbolFile().FindCompleteObjCDefinitionTypeForDIE(
+ die, type_name, must_be_implementation);
+}
+
+SymbolFileDWARF &SymbolFileDWARFDwo::GetBaseSymbolFile() {
+ return m_base_dwarf_cu.GetSymbolFileDWARF();
+}
+
+DWARFExpression::LocationListFormat
+SymbolFileDWARFDwo::GetLocationListFormat() const {
+ return DWARFExpression::SplitDwarfLocationList;
+}
+
+TypeSystem *
+SymbolFileDWARFDwo::GetTypeSystemForLanguage(LanguageType language) {
+ return GetBaseSymbolFile().GetTypeSystemForLanguage(language);
+}
+
+DWARFDIE
+SymbolFileDWARFDwo::GetDIE(const DIERef &die_ref) {
+ if (*die_ref.dwo_num() == GetDwoNum())
+ return DebugInfo()->GetDIE(die_ref);
+ return GetBaseSymbolFile().GetDIE(die_ref);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h
new file mode 100644
index 000000000000..9b2f3bb84c4f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h
@@ -0,0 +1,79 @@
+//===-- SymbolFileDWARFDwo.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 SymbolFileDWARFDwo_SymbolFileDWARFDwo_h_
+#define SymbolFileDWARFDwo_SymbolFileDWARFDwo_h_
+
+#include "SymbolFileDWARF.h"
+
+class SymbolFileDWARFDwo : public SymbolFileDWARF {
+public:
+ SymbolFileDWARFDwo(lldb::ObjectFileSP objfile, DWARFCompileUnit &dwarf_cu);
+
+ ~SymbolFileDWARFDwo() override = default;
+
+ lldb::CompUnitSP ParseCompileUnit(DWARFCompileUnit &dwarf_cu) override;
+
+ DWARFCompileUnit *GetCompileUnit();
+
+ DWARFUnit *
+ GetDWARFCompileUnit(lldb_private::CompileUnit *comp_unit) override;
+
+ lldb_private::DWARFExpression::LocationListFormat
+ GetLocationListFormat() const override;
+
+ size_t GetObjCMethodDIEOffsets(lldb_private::ConstString class_name,
+ DIEArray &method_die_offsets) override;
+
+ lldb_private::TypeSystem *
+ GetTypeSystemForLanguage(lldb::LanguageType language) override;
+
+ DWARFDIE
+ GetDIE(const DIERef &die_ref) override;
+
+ std::unique_ptr<SymbolFileDWARFDwo>
+ GetDwoSymbolFileForCompileUnit(DWARFUnit &dwarf_cu,
+ const DWARFDebugInfoEntry &cu_die) override {
+ return nullptr;
+ }
+
+ DWARFCompileUnit *GetBaseCompileUnit() override { return &m_base_dwarf_cu; }
+
+ llvm::Optional<uint32_t> GetDwoNum() override { return GetID() >> 32; }
+
+protected:
+ void LoadSectionData(lldb::SectionType sect_type,
+ lldb_private::DWARFDataExtractor &data) override;
+
+ DIEToTypePtr &GetDIEToType() override;
+
+ DIEToVariableSP &GetDIEToVariable() override;
+
+ DIEToClangType &GetForwardDeclDieToClangType() override;
+
+ ClangTypeToDIE &GetForwardDeclClangTypeToDie() override;
+
+ UniqueDWARFASTTypeMap &GetUniqueDWARFASTTypeMap() override;
+
+ lldb::TypeSP FindDefinitionTypeForDWARFDeclContext(
+ const DWARFDeclContext &die_decl_ctx) override;
+
+ lldb::TypeSP FindCompleteObjCDefinitionTypeForDIE(
+ const DWARFDIE &die, lldb_private::ConstString type_name,
+ bool must_be_implementation) override;
+
+ SymbolFileDWARF &GetBaseSymbolFile();
+
+ DWARFCompileUnit *ComputeCompileUnit();
+
+ lldb::ObjectFileSP m_obj_file_sp;
+ DWARFCompileUnit &m_base_dwarf_cu;
+ DWARFCompileUnit *m_cu = nullptr;
+};
+
+#endif // SymbolFileDWARFDwo_SymbolFileDWARFDwo_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwoDwp.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwoDwp.cpp
new file mode 100644
index 000000000000..efea192b17ce
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwoDwp.cpp
@@ -0,0 +1,35 @@
+//===-- SymbolFileDWARFDwoDwp.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 "SymbolFileDWARFDwoDwp.h"
+
+#include "lldb/Core/Section.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/LLDBAssert.h"
+
+#include "DWARFUnit.h"
+#include "DWARFDebugInfo.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SymbolFileDWARFDwoDwp::SymbolFileDWARFDwoDwp(SymbolFileDWARFDwp *dwp_symfile,
+ ObjectFileSP objfile,
+ DWARFCompileUnit &dwarf_cu,
+ uint64_t dwo_id)
+ : SymbolFileDWARFDwo(objfile, dwarf_cu), m_dwp_symfile(dwp_symfile),
+ m_dwo_id(dwo_id) {}
+
+void SymbolFileDWARFDwoDwp::LoadSectionData(lldb::SectionType sect_type,
+ DWARFDataExtractor &data) {
+ if (m_dwp_symfile->LoadSectionData(m_dwo_id, sect_type, data))
+ return;
+
+ SymbolFileDWARF::LoadSectionData(sect_type, data);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwoDwp.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwoDwp.h
new file mode 100644
index 000000000000..2105e1a8f6cb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwoDwp.h
@@ -0,0 +1,29 @@
+//===-- SymbolFileDWARFDwoDwp.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 SymbolFileDWARFDwoDwp_SymbolFileDWARFDwoDwp_h_
+#define SymbolFileDWARFDwoDwp_SymbolFileDWARFDwoDwp_h_
+
+#include "SymbolFileDWARFDwo.h"
+#include "SymbolFileDWARFDwp.h"
+
+class SymbolFileDWARFDwoDwp : public SymbolFileDWARFDwo {
+public:
+ SymbolFileDWARFDwoDwp(SymbolFileDWARFDwp *dwp_symfile,
+ lldb::ObjectFileSP objfile, DWARFCompileUnit &dwarf_cu,
+ uint64_t dwo_id);
+
+protected:
+ void LoadSectionData(lldb::SectionType sect_type,
+ lldb_private::DWARFDataExtractor &data) override;
+
+ SymbolFileDWARFDwp *m_dwp_symfile;
+ uint64_t m_dwo_id;
+};
+
+#endif // SymbolFileDWARFDwoDwp_SymbolFileDWARFDwoDwp_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwp.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwp.cpp
new file mode 100644
index 000000000000..08e6e1c8c2f3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwp.cpp
@@ -0,0 +1,138 @@
+//===-- SymbolFileDWARFDwp.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 "SymbolFileDWARFDwp.h"
+
+#include "lldb/Core/Section.h"
+#include "lldb/Symbol/ObjectFile.h"
+
+#include "SymbolFileDWARFDwoDwp.h"
+
+static llvm::DWARFSectionKind
+lldbSectTypeToLlvmSectionKind(lldb::SectionType type) {
+ switch (type) {
+ case lldb::eSectionTypeDWARFDebugInfo:
+ return llvm::DW_SECT_INFO;
+ // case lldb::eSectionTypeDWARFDebugTypes:
+ // return llvm::DW_SECT_TYPES;
+ case lldb::eSectionTypeDWARFDebugAbbrev:
+ return llvm::DW_SECT_ABBREV;
+ case lldb::eSectionTypeDWARFDebugLine:
+ return llvm::DW_SECT_LINE;
+ case lldb::eSectionTypeDWARFDebugLoc:
+ return llvm::DW_SECT_LOC;
+ case lldb::eSectionTypeDWARFDebugStrOffsets:
+ return llvm::DW_SECT_STR_OFFSETS;
+ // case lldb::eSectionTypeDWARFDebugMacinfo:
+ // return llvm::DW_SECT_MACINFO;
+ case lldb::eSectionTypeDWARFDebugMacro:
+ return llvm::DW_SECT_MACRO;
+ default:
+ // Note: 0 is an invalid dwarf section kind.
+ return llvm::DWARFSectionKind(0);
+ }
+}
+
+std::unique_ptr<SymbolFileDWARFDwp>
+SymbolFileDWARFDwp::Create(lldb::ModuleSP module_sp,
+ const lldb_private::FileSpec &file_spec) {
+ const lldb::offset_t file_offset = 0;
+ lldb::DataBufferSP file_data_sp;
+ lldb::offset_t file_data_offset = 0;
+ lldb::ObjectFileSP obj_file = lldb_private::ObjectFile::FindPlugin(
+ module_sp, &file_spec, file_offset,
+ lldb_private::FileSystem::Instance().GetByteSize(file_spec), file_data_sp,
+ file_data_offset);
+ if (obj_file == nullptr)
+ return nullptr;
+
+ std::unique_ptr<SymbolFileDWARFDwp> dwp_symfile(
+ new SymbolFileDWARFDwp(module_sp, obj_file));
+
+ lldb_private::DWARFDataExtractor debug_cu_index;
+ if (!dwp_symfile->LoadRawSectionData(lldb::eSectionTypeDWARFDebugCuIndex,
+ debug_cu_index))
+ return nullptr;
+
+ llvm::DataExtractor llvm_debug_cu_index(
+ llvm::StringRef(debug_cu_index.PeekCStr(0), debug_cu_index.GetByteSize()),
+ debug_cu_index.GetByteOrder() == lldb::eByteOrderLittle,
+ debug_cu_index.GetAddressByteSize());
+ if (!dwp_symfile->m_debug_cu_index.parse(llvm_debug_cu_index))
+ return nullptr;
+ dwp_symfile->InitDebugCUIndexMap();
+ return dwp_symfile;
+}
+
+void SymbolFileDWARFDwp::InitDebugCUIndexMap() {
+ m_debug_cu_index_map.clear();
+ for (const auto &entry : m_debug_cu_index.getRows())
+ m_debug_cu_index_map.emplace(entry.getSignature(), &entry);
+}
+
+SymbolFileDWARFDwp::SymbolFileDWARFDwp(lldb::ModuleSP module_sp,
+ lldb::ObjectFileSP obj_file)
+ : m_obj_file(std::move(obj_file)), m_debug_cu_index(llvm::DW_SECT_INFO)
+{}
+
+std::unique_ptr<SymbolFileDWARFDwo>
+SymbolFileDWARFDwp::GetSymbolFileForDwoId(DWARFCompileUnit &dwarf_cu,
+ uint64_t dwo_id) {
+ return std::unique_ptr<SymbolFileDWARFDwo>(
+ new SymbolFileDWARFDwoDwp(this, m_obj_file, dwarf_cu, dwo_id));
+}
+
+bool SymbolFileDWARFDwp::LoadSectionData(
+ uint64_t dwo_id, lldb::SectionType sect_type,
+ lldb_private::DWARFDataExtractor &data) {
+ lldb_private::DWARFDataExtractor section_data;
+ if (!LoadRawSectionData(sect_type, section_data))
+ return false;
+
+ auto it = m_debug_cu_index_map.find(dwo_id);
+ if (it == m_debug_cu_index_map.end())
+ return false;
+
+ auto *offsets =
+ it->second->getOffset(lldbSectTypeToLlvmSectionKind(sect_type));
+ if (offsets) {
+ data.SetData(section_data, offsets->Offset, offsets->Length);
+ } else {
+ data.SetData(section_data, 0, section_data.GetByteSize());
+ }
+ return true;
+}
+
+bool SymbolFileDWARFDwp::LoadRawSectionData(
+ lldb::SectionType sect_type, lldb_private::DWARFDataExtractor &data) {
+ std::lock_guard<std::mutex> lock(m_sections_mutex);
+
+ auto it = m_sections.find(sect_type);
+ if (it != m_sections.end()) {
+ if (it->second.GetByteSize() == 0)
+ return false;
+
+ data = it->second;
+ return true;
+ }
+
+ const lldb_private::SectionList *section_list =
+ m_obj_file->GetSectionList(false /* update_module_section_list */);
+ if (section_list) {
+ lldb::SectionSP section_sp(
+ section_list->FindSectionByType(sect_type, true));
+ if (section_sp) {
+ if (m_obj_file->ReadSectionData(section_sp.get(), data) != 0) {
+ m_sections[sect_type] = data;
+ return true;
+ }
+ }
+ }
+ m_sections[sect_type].Clear();
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwp.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwp.h
new file mode 100644
index 000000000000..ef06b9dca8bb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwp.h
@@ -0,0 +1,50 @@
+//===-- SymbolFileDWARFDwp.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 SymbolFileDWARFDwp_SymbolFileDWARFDwp_h_
+#define SymbolFileDWARFDwp_SymbolFileDWARFDwp_h_
+
+#include <memory>
+
+#include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h"
+
+#include "lldb/Core/Module.h"
+
+#include "DWARFDataExtractor.h"
+#include "SymbolFileDWARFDwo.h"
+
+class SymbolFileDWARFDwp {
+public:
+ static std::unique_ptr<SymbolFileDWARFDwp>
+ Create(lldb::ModuleSP module_sp, const lldb_private::FileSpec &file_spec);
+
+ std::unique_ptr<SymbolFileDWARFDwo>
+ GetSymbolFileForDwoId(DWARFCompileUnit &dwarf_cu, uint64_t dwo_id);
+
+ bool LoadSectionData(uint64_t dwo_id, lldb::SectionType sect_type,
+ lldb_private::DWARFDataExtractor &data);
+
+private:
+ explicit SymbolFileDWARFDwp(lldb::ModuleSP module_sp,
+ lldb::ObjectFileSP obj_file);
+
+ bool LoadRawSectionData(lldb::SectionType sect_type,
+ lldb_private::DWARFDataExtractor &data);
+
+ void InitDebugCUIndexMap();
+
+ lldb::ObjectFileSP m_obj_file;
+
+ std::mutex m_sections_mutex;
+ std::map<lldb::SectionType, lldb_private::DWARFDataExtractor> m_sections;
+
+ llvm::DWARFUnitIndex m_debug_cu_index;
+ std::map<uint64_t, const llvm::DWARFUnitIndex::Entry *> m_debug_cu_index_map;
+};
+
+#endif // SymbolFileDWARFDwp_SymbolFileDWARFDwp_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.cpp
new file mode 100644
index 000000000000..8da7e2226266
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.cpp
@@ -0,0 +1,73 @@
+//===-- UniqueDWARFASTType.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 "UniqueDWARFASTType.h"
+
+#include "lldb/Symbol/Declaration.h"
+
+bool UniqueDWARFASTTypeList::Find(const DWARFDIE &die,
+ const lldb_private::Declaration &decl,
+ const int32_t byte_size,
+ UniqueDWARFASTType &entry) const {
+ for (const UniqueDWARFASTType &udt : m_collection) {
+ // Make sure the tags match
+ if (udt.m_die.Tag() == die.Tag()) {
+ // Validate byte sizes of both types only if both are valid.
+ if (udt.m_byte_size < 0 || byte_size < 0 ||
+ udt.m_byte_size == byte_size) {
+ // Make sure the file and line match
+ if (udt.m_declaration == decl) {
+ // The type has the same name, and was defined on the same file and
+ // line. Now verify all of the parent DIEs match.
+ DWARFDIE parent_arg_die = die.GetParent();
+ DWARFDIE parent_pos_die = udt.m_die.GetParent();
+ bool match = true;
+ bool done = false;
+ while (!done && match && parent_arg_die && parent_pos_die) {
+ const dw_tag_t parent_arg_tag = parent_arg_die.Tag();
+ const dw_tag_t parent_pos_tag = parent_pos_die.Tag();
+ if (parent_arg_tag == parent_pos_tag) {
+ switch (parent_arg_tag) {
+ case DW_TAG_class_type:
+ case DW_TAG_structure_type:
+ case DW_TAG_union_type:
+ case DW_TAG_namespace: {
+ const char *parent_arg_die_name = parent_arg_die.GetName();
+ if (parent_arg_die_name ==
+ nullptr) // Anonymous (i.e. no-name) struct
+ {
+ match = false;
+ } else {
+ const char *parent_pos_die_name = parent_pos_die.GetName();
+ if (parent_pos_die_name == nullptr ||
+ ((parent_arg_die_name != parent_pos_die_name) &&
+ strcmp(parent_arg_die_name, parent_pos_die_name)))
+ match = false;
+ }
+ } break;
+
+ case DW_TAG_compile_unit:
+ case DW_TAG_partial_unit:
+ done = true;
+ break;
+ }
+ }
+ parent_arg_die = parent_arg_die.GetParent();
+ parent_pos_die = parent_pos_die.GetParent();
+ }
+
+ if (match) {
+ entry = udt;
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.h
new file mode 100644
index 000000000000..1269dbac7126
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.h
@@ -0,0 +1,103 @@
+//===-- UniqueDWARFASTType.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_UniqueDWARFASTType_h_
+#define lldb_UniqueDWARFASTType_h_
+
+#include <vector>
+
+#include "llvm/ADT/DenseMap.h"
+
+#include "DWARFDIE.h"
+#include "lldb/Symbol/Declaration.h"
+
+class UniqueDWARFASTType {
+public:
+ // Constructors and Destructors
+ UniqueDWARFASTType()
+ : m_type_sp(), m_die(), m_declaration(),
+ m_byte_size(
+ -1) // Set to negative value to make sure we have a valid value
+ {}
+
+ UniqueDWARFASTType(lldb::TypeSP &type_sp, const DWARFDIE &die,
+ const lldb_private::Declaration &decl, int32_t byte_size)
+ : m_type_sp(type_sp), m_die(die), m_declaration(decl),
+ m_byte_size(byte_size) {}
+
+ UniqueDWARFASTType(const UniqueDWARFASTType &rhs)
+ : m_type_sp(rhs.m_type_sp), m_die(rhs.m_die),
+ m_declaration(rhs.m_declaration), m_byte_size(rhs.m_byte_size) {}
+
+ ~UniqueDWARFASTType() {}
+
+ UniqueDWARFASTType &operator=(const UniqueDWARFASTType &rhs) {
+ if (this != &rhs) {
+ m_type_sp = rhs.m_type_sp;
+ m_die = rhs.m_die;
+ m_declaration = rhs.m_declaration;
+ m_byte_size = rhs.m_byte_size;
+ }
+ return *this;
+ }
+
+ lldb::TypeSP m_type_sp;
+ DWARFDIE m_die;
+ lldb_private::Declaration m_declaration;
+ int32_t m_byte_size;
+};
+
+class UniqueDWARFASTTypeList {
+public:
+ UniqueDWARFASTTypeList() : m_collection() {}
+
+ ~UniqueDWARFASTTypeList() {}
+
+ uint32_t GetSize() { return (uint32_t)m_collection.size(); }
+
+ void Append(const UniqueDWARFASTType &entry) {
+ m_collection.push_back(entry);
+ }
+
+ bool Find(const DWARFDIE &die, const lldb_private::Declaration &decl,
+ const int32_t byte_size, UniqueDWARFASTType &entry) const;
+
+protected:
+ typedef std::vector<UniqueDWARFASTType> collection;
+ collection m_collection;
+};
+
+class UniqueDWARFASTTypeMap {
+public:
+ UniqueDWARFASTTypeMap() : m_collection() {}
+
+ ~UniqueDWARFASTTypeMap() {}
+
+ void Insert(lldb_private::ConstString name,
+ const UniqueDWARFASTType &entry) {
+ m_collection[name.GetCString()].Append(entry);
+ }
+
+ bool Find(lldb_private::ConstString name, const DWARFDIE &die,
+ const lldb_private::Declaration &decl, const int32_t byte_size,
+ UniqueDWARFASTType &entry) const {
+ const char *unique_name_cstr = name.GetCString();
+ collection::const_iterator pos = m_collection.find(unique_name_cstr);
+ if (pos != m_collection.end()) {
+ return pos->second.Find(die, decl, byte_size, entry);
+ }
+ return false;
+ }
+
+protected:
+ // A unique name string should be used
+ typedef llvm::DenseMap<const char *, UniqueDWARFASTTypeList> collection;
+ collection m_collection;
+};
+
+#endif // lldb_UniqueDWARFASTType_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.cpp
new file mode 100644
index 000000000000..3834165c71c0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.cpp
@@ -0,0 +1,457 @@
+//===-- CodeViewRegisterMapping.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 "CodeViewRegisterMapping.h"
+
+#include "lldb/lldb-defines.h"
+
+#include "Plugins/Process/Utility/lldb-x86-register-enums.h"
+
+using namespace lldb_private;
+
+static const uint32_t g_code_view_to_lldb_registers_x86[] = {
+ LLDB_INVALID_REGNUM, // NONE
+ lldb_al_i386, // AL
+ lldb_cl_i386, // CL
+ lldb_dl_i386, // DL
+ lldb_bl_i386, // BL
+ lldb_ah_i386, // AH
+ lldb_ch_i386, // CH
+ lldb_dh_i386, // DH
+ lldb_bh_i386, // BH
+ lldb_ax_i386, // AX
+ lldb_cx_i386, // CX
+ lldb_dx_i386, // DX
+ lldb_bx_i386, // BX
+ lldb_sp_i386, // SP
+ lldb_bp_i386, // BP
+ lldb_si_i386, // SI
+ lldb_di_i386, // DI
+ lldb_eax_i386, // EAX
+ lldb_ecx_i386, // ECX
+ lldb_edx_i386, // EDX
+ lldb_ebx_i386, // EBX
+ lldb_esp_i386, // ESP
+ lldb_ebp_i386, // EBP
+ lldb_esi_i386, // ESI
+ lldb_edi_i386, // EDI
+ lldb_es_i386, // ES
+ lldb_cs_i386, // CS
+ lldb_ss_i386, // SS
+ lldb_ds_i386, // DS
+ lldb_fs_i386, // FS
+ lldb_gs_i386, // GS
+ LLDB_INVALID_REGNUM, // IP
+ LLDB_INVALID_REGNUM, // FLAGS
+ lldb_eip_i386, // EIP
+ lldb_eflags_i386, // EFLAGS
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, // TEMP
+ LLDB_INVALID_REGNUM, // TEMPH
+ LLDB_INVALID_REGNUM, // QUOTE
+ LLDB_INVALID_REGNUM, // PCDR3
+ LLDB_INVALID_REGNUM, // PCDR4
+ LLDB_INVALID_REGNUM, // PCDR5
+ LLDB_INVALID_REGNUM, // PCDR6
+ LLDB_INVALID_REGNUM, // PCDR7
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, // CR0
+ LLDB_INVALID_REGNUM, // CR1
+ LLDB_INVALID_REGNUM, // CR2
+ LLDB_INVALID_REGNUM, // CR3
+ LLDB_INVALID_REGNUM, // CR4
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ lldb_dr0_i386, // DR0
+ lldb_dr1_i386, // DR1
+ lldb_dr2_i386, // DR2
+ lldb_dr3_i386, // DR3
+ lldb_dr4_i386, // DR4
+ lldb_dr5_i386, // DR5
+ lldb_dr6_i386, // DR6
+ lldb_dr7_i386, // DR7
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, // GDTR
+ LLDB_INVALID_REGNUM, // GDTL
+ LLDB_INVALID_REGNUM, // IDTR
+ LLDB_INVALID_REGNUM, // IDTL
+ LLDB_INVALID_REGNUM, // LDTR
+ LLDB_INVALID_REGNUM, // TR
+ LLDB_INVALID_REGNUM, // PSEUDO1
+ LLDB_INVALID_REGNUM, // PSEUDO2
+ LLDB_INVALID_REGNUM, // PSEUDO3
+ LLDB_INVALID_REGNUM, // PSEUDO4
+ LLDB_INVALID_REGNUM, // PSEUDO5
+ LLDB_INVALID_REGNUM, // PSEUDO6
+ LLDB_INVALID_REGNUM, // PSEUDO7
+ LLDB_INVALID_REGNUM, // PSEUDO8
+ LLDB_INVALID_REGNUM, // PSEUDO9
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ lldb_st0_i386, // ST0
+ lldb_st1_i386, // ST1
+ lldb_st2_i386, // ST2
+ lldb_st3_i386, // ST3
+ lldb_st4_i386, // ST4
+ lldb_st5_i386, // ST5
+ lldb_st6_i386, // ST6
+ lldb_st7_i386, // ST7
+ LLDB_INVALID_REGNUM, // CTRL
+ LLDB_INVALID_REGNUM, // STAT
+ LLDB_INVALID_REGNUM, // TAG
+ LLDB_INVALID_REGNUM, // FPIP
+ LLDB_INVALID_REGNUM, // FPCS
+ LLDB_INVALID_REGNUM, // FPDO
+ LLDB_INVALID_REGNUM, // FPDS
+ LLDB_INVALID_REGNUM, // ISEM
+ LLDB_INVALID_REGNUM, // FPEIP
+ LLDB_INVALID_REGNUM, // FPEDO
+ lldb_mm0_i386, // MM0
+ lldb_mm1_i386, // MM1
+ lldb_mm2_i386, // MM2
+ lldb_mm3_i386, // MM3
+ lldb_mm4_i386, // MM4
+ lldb_mm5_i386, // MM5
+ lldb_mm6_i386, // MM6
+ lldb_mm7_i386, // MM7
+ lldb_xmm0_i386, // XMM0
+ lldb_xmm1_i386, // XMM1
+ lldb_xmm2_i386, // XMM2
+ lldb_xmm3_i386, // XMM3
+ lldb_xmm4_i386, // XMM4
+ lldb_xmm5_i386, // XMM5
+ lldb_xmm6_i386, // XMM6
+ lldb_xmm7_i386 // XMM7
+};
+
+static const uint32_t g_code_view_to_lldb_registers_x86_64[] = {
+ LLDB_INVALID_REGNUM, // NONE
+ lldb_al_x86_64, // AL
+ lldb_cl_x86_64, // CL
+ lldb_dl_x86_64, // DL
+ lldb_bl_x86_64, // BL
+ lldb_ah_x86_64, // AH
+ lldb_ch_x86_64, // CH
+ lldb_dh_x86_64, // DH
+ lldb_bh_x86_64, // BH
+ lldb_ax_x86_64, // AX
+ lldb_cx_x86_64, // CX
+ lldb_dx_x86_64, // DX
+ lldb_bx_x86_64, // BX
+ lldb_sp_x86_64, // SP
+ lldb_bp_x86_64, // BP
+ lldb_si_x86_64, // SI
+ lldb_di_x86_64, // DI
+ lldb_eax_x86_64, // EAX
+ lldb_ecx_x86_64, // ECX
+ lldb_edx_x86_64, // EDX
+ lldb_ebx_x86_64, // EBX
+ lldb_esp_x86_64, // ESP
+ lldb_ebp_x86_64, // EBP
+ lldb_esi_x86_64, // ESI
+ lldb_edi_x86_64, // EDI
+ lldb_es_x86_64, // ES
+ lldb_cs_x86_64, // CS
+ lldb_ss_x86_64, // SS
+ lldb_ds_x86_64, // DS
+ lldb_fs_x86_64, // FS
+ lldb_gs_x86_64, // GS
+ LLDB_INVALID_REGNUM, // IP
+ LLDB_INVALID_REGNUM, // FLAGS
+ LLDB_INVALID_REGNUM, // EIP
+ LLDB_INVALID_REGNUM, // EFLAGS
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, // TEMP
+ LLDB_INVALID_REGNUM, // TEMPH
+ LLDB_INVALID_REGNUM, // QUOTE
+ LLDB_INVALID_REGNUM, // PCDR3
+ LLDB_INVALID_REGNUM, // PCDR4
+ LLDB_INVALID_REGNUM, // PCDR5
+ LLDB_INVALID_REGNUM, // PCDR6
+ LLDB_INVALID_REGNUM, // PCDR7
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, // CR0
+ LLDB_INVALID_REGNUM, // CR1
+ LLDB_INVALID_REGNUM, // CR2
+ LLDB_INVALID_REGNUM, // CR3
+ LLDB_INVALID_REGNUM, // CR4
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ lldb_dr0_x86_64, // DR0
+ lldb_dr1_x86_64, // DR1
+ lldb_dr2_x86_64, // DR2
+ lldb_dr3_x86_64, // DR3
+ lldb_dr4_x86_64, // DR4
+ lldb_dr5_x86_64, // DR5
+ lldb_dr6_x86_64, // DR6
+ lldb_dr7_x86_64, // DR7
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, // GDTR
+ LLDB_INVALID_REGNUM, // GDTL
+ LLDB_INVALID_REGNUM, // IDTR
+ LLDB_INVALID_REGNUM, // IDTL
+ LLDB_INVALID_REGNUM, // LDTR
+ LLDB_INVALID_REGNUM, // TR
+ LLDB_INVALID_REGNUM, // PSEUDO1
+ LLDB_INVALID_REGNUM, // PSEUDO2
+ LLDB_INVALID_REGNUM, // PSEUDO3
+ LLDB_INVALID_REGNUM, // PSEUDO4
+ LLDB_INVALID_REGNUM, // PSEUDO5
+ LLDB_INVALID_REGNUM, // PSEUDO6
+ LLDB_INVALID_REGNUM, // PSEUDO7
+ LLDB_INVALID_REGNUM, // PSEUDO8
+ LLDB_INVALID_REGNUM, // PSEUDO9
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ lldb_st0_x86_64, // ST0
+ lldb_st1_x86_64, // ST1
+ lldb_st2_x86_64, // ST2
+ lldb_st3_x86_64, // ST3
+ lldb_st4_x86_64, // ST4
+ lldb_st5_x86_64, // ST5
+ lldb_st6_x86_64, // ST6
+ lldb_st7_x86_64, // ST7
+ LLDB_INVALID_REGNUM, // CTRL
+ LLDB_INVALID_REGNUM, // STAT
+ LLDB_INVALID_REGNUM, // TAG
+ LLDB_INVALID_REGNUM, // FPIP
+ LLDB_INVALID_REGNUM, // FPCS
+ LLDB_INVALID_REGNUM, // FPDO
+ LLDB_INVALID_REGNUM, // FPDS
+ LLDB_INVALID_REGNUM, // ISEM
+ LLDB_INVALID_REGNUM, // FPEIP
+ LLDB_INVALID_REGNUM, // FPEDO
+ lldb_mm0_x86_64, // MM0
+ lldb_mm1_x86_64, // MM1
+ lldb_mm2_x86_64, // MM2
+ lldb_mm3_x86_64, // MM3
+ lldb_mm4_x86_64, // MM4
+ lldb_mm5_x86_64, // MM5
+ lldb_mm6_x86_64, // MM6
+ lldb_mm7_x86_64, // MM7
+ lldb_xmm0_x86_64, // XMM0
+ lldb_xmm1_x86_64, // XMM1
+ lldb_xmm2_x86_64, // XMM2
+ lldb_xmm3_x86_64, // XMM3
+ lldb_xmm4_x86_64, // XMM4
+ lldb_xmm5_x86_64, // XMM5
+ lldb_xmm6_x86_64, // XMM6
+ lldb_xmm7_x86_64, // XMM7
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM,
+ lldb_mxcsr_x86_64, // MXCSR
+ LLDB_INVALID_REGNUM, // EDXEAX
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, // EMM0L
+ LLDB_INVALID_REGNUM, // EMM1L
+ LLDB_INVALID_REGNUM, // EMM2L
+ LLDB_INVALID_REGNUM, // EMM3L
+ LLDB_INVALID_REGNUM, // EMM4L
+ LLDB_INVALID_REGNUM, // EMM5L
+ LLDB_INVALID_REGNUM, // EMM6L
+ LLDB_INVALID_REGNUM, // EMM7L
+ LLDB_INVALID_REGNUM, // EMM0H
+ LLDB_INVALID_REGNUM, // EMM1H
+ LLDB_INVALID_REGNUM, // EMM2H
+ LLDB_INVALID_REGNUM, // EMM3H
+ LLDB_INVALID_REGNUM, // EMM4H
+ LLDB_INVALID_REGNUM, // EMM5H
+ LLDB_INVALID_REGNUM, // EMM6H
+ LLDB_INVALID_REGNUM, // EMM7H
+ LLDB_INVALID_REGNUM, // MM00
+ LLDB_INVALID_REGNUM, // MM01
+ LLDB_INVALID_REGNUM, // MM10
+ LLDB_INVALID_REGNUM, // MM11
+ LLDB_INVALID_REGNUM, // MM20
+ LLDB_INVALID_REGNUM, // MM21
+ LLDB_INVALID_REGNUM, // MM30
+ LLDB_INVALID_REGNUM, // MM31
+ LLDB_INVALID_REGNUM, // MM40
+ LLDB_INVALID_REGNUM, // MM41
+ LLDB_INVALID_REGNUM, // MM50
+ LLDB_INVALID_REGNUM, // MM51
+ LLDB_INVALID_REGNUM, // MM60
+ LLDB_INVALID_REGNUM, // MM61
+ LLDB_INVALID_REGNUM, // MM70
+ LLDB_INVALID_REGNUM, // MM71
+ lldb_xmm8_x86_64, // XMM8
+ lldb_xmm9_x86_64, // XMM9
+ lldb_xmm10_x86_64, // XMM10
+ lldb_xmm11_x86_64, // XMM11
+ lldb_xmm12_x86_64, // XMM12
+ lldb_xmm13_x86_64, // XMM13
+ lldb_xmm14_x86_64, // XMM14
+ lldb_xmm15_x86_64, // XMM15
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM,
+ lldb_sil_x86_64, // SIL
+ lldb_dil_x86_64, // DIL
+ lldb_bpl_x86_64, // BPL
+ lldb_spl_x86_64, // SPL
+ lldb_rax_x86_64, // RAX
+ lldb_rbx_x86_64, // RBX
+ lldb_rcx_x86_64, // RCX
+ lldb_rdx_x86_64, // RDX
+ lldb_rsi_x86_64, // RSI
+ lldb_rdi_x86_64, // RDI
+ lldb_rbp_x86_64, // RBP
+ lldb_rsp_x86_64, // RSP
+ lldb_r8_x86_64, // R8
+ lldb_r9_x86_64, // R9
+ lldb_r10_x86_64, // R10
+ lldb_r11_x86_64, // R11
+ lldb_r12_x86_64, // R12
+ lldb_r13_x86_64, // R13
+ lldb_r14_x86_64, // R14
+ lldb_r15_x86_64, // R15
+ lldb_r8l_x86_64, // R8B
+ lldb_r9l_x86_64, // R9B
+ lldb_r10l_x86_64, // R10B
+ lldb_r11l_x86_64, // R11B
+ lldb_r12l_x86_64, // R12B
+ lldb_r13l_x86_64, // R13B
+ lldb_r14l_x86_64, // R14B
+ lldb_r15l_x86_64, // R15B
+ lldb_r8w_x86_64, // R8W
+ lldb_r9w_x86_64, // R9W
+ lldb_r10w_x86_64, // R10W
+ lldb_r11w_x86_64, // R11W
+ lldb_r12w_x86_64, // R12W
+ lldb_r13w_x86_64, // R13W
+ lldb_r14w_x86_64, // R14W
+ lldb_r15w_x86_64, // R15W
+ lldb_r8d_x86_64, // R8D
+ lldb_r9d_x86_64, // R9D
+ lldb_r10d_x86_64, // R10D
+ lldb_r11d_x86_64, // R11D
+ lldb_r12d_x86_64, // R12D
+ lldb_r13d_x86_64, // R13D
+ lldb_r14d_x86_64, // R14D
+ lldb_r15d_x86_64, // R15D
+ lldb_ymm0_x86_64, // AMD64_YMM0
+ lldb_ymm1_x86_64, // AMD64_YMM1
+ lldb_ymm2_x86_64, // AMD64_YMM2
+ lldb_ymm3_x86_64, // AMD64_YMM3
+ lldb_ymm4_x86_64, // AMD64_YMM4
+ lldb_ymm5_x86_64, // AMD64_YMM5
+ lldb_ymm6_x86_64, // AMD64_YMM6
+ lldb_ymm7_x86_64, // AMD64_YMM7
+ lldb_ymm8_x86_64, // AMD64_YMM8
+ lldb_ymm9_x86_64, // AMD64_YMM9
+ lldb_ymm10_x86_64, // AMD64_YMM10
+ lldb_ymm11_x86_64, // AMD64_YMM11
+ lldb_ymm12_x86_64, // AMD64_YMM12
+ lldb_ymm13_x86_64, // AMD64_YMM13
+ lldb_ymm14_x86_64, // AMD64_YMM14
+ lldb_ymm15_x86_64, // AMD64_YMM15
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,
+ lldb_bnd0_x86_64, // BND0
+ lldb_bnd1_x86_64, // BND1
+ lldb_bnd2_x86_64 // BND2
+};
+
+uint32_t lldb_private::npdb::GetLLDBRegisterNumber(
+ llvm::Triple::ArchType arch_type, llvm::codeview::RegisterId register_id) {
+ switch (arch_type) {
+ case llvm::Triple::x86:
+ if (static_cast<uint16_t>(register_id) <
+ sizeof(g_code_view_to_lldb_registers_x86) /
+ sizeof(g_code_view_to_lldb_registers_x86[0]))
+ return g_code_view_to_lldb_registers_x86[static_cast<uint16_t>(
+ register_id)];
+
+ switch (register_id) {
+ case llvm::codeview::RegisterId::MXCSR:
+ return lldb_mxcsr_i386;
+ case llvm::codeview::RegisterId::BND0:
+ return lldb_bnd0_i386;
+ case llvm::codeview::RegisterId::BND1:
+ return lldb_bnd1_i386;
+ case llvm::codeview::RegisterId::BND2:
+ return lldb_bnd2_i386;
+ default:
+ return LLDB_INVALID_REGNUM;
+ }
+ case llvm::Triple::x86_64:
+ if (static_cast<uint16_t>(register_id) <
+ sizeof(g_code_view_to_lldb_registers_x86_64) /
+ sizeof(g_code_view_to_lldb_registers_x86_64[0]))
+ return g_code_view_to_lldb_registers_x86_64[static_cast<uint16_t>(
+ register_id)];
+
+ return LLDB_INVALID_REGNUM;
+ default:
+ return LLDB_INVALID_REGNUM;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.h
new file mode 100644
index 000000000000..b1c31e0c1785
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.h
@@ -0,0 +1,24 @@
+//===-- CodeViewRegisterMapping.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_Plugins_SymbolFile_PDB_CodeViewRegisterMapping_h_
+#define lldb_Plugins_SymbolFile_PDB_CodeViewRegisterMapping_h_
+
+#include "llvm/ADT/Triple.h"
+#include "llvm/DebugInfo/CodeView/CodeView.h"
+
+namespace lldb_private {
+namespace npdb {
+
+uint32_t GetLLDBRegisterNumber(llvm::Triple::ArchType arch_type,
+ llvm::codeview::RegisterId register_id);
+
+} // namespace npdb
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp
new file mode 100644
index 000000000000..1838204e4ca6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp
@@ -0,0 +1,225 @@
+//===-- CompileUnitIndex.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 "CompileUnitIndex.h"
+
+#include "PdbIndex.h"
+#include "PdbUtil.h"
+
+#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
+#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
+#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
+#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
+#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
+#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
+#include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/Support/Path.h"
+
+#include "lldb/Utility/LLDBAssert.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::npdb;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+static bool IsMainFile(llvm::StringRef main, llvm::StringRef other) {
+ if (main == other)
+ return true;
+
+ // If the files refer to the local file system, we can just ask the file
+ // system if they're equivalent. But if the source isn't present on disk
+ // then we still want to try.
+ if (llvm::sys::fs::equivalent(main, other))
+ return true;
+
+ llvm::SmallString<64> normalized(other);
+ llvm::sys::path::native(normalized);
+ return main.equals_lower(normalized);
+}
+
+static void ParseCompile3(const CVSymbol &sym, CompilandIndexItem &cci) {
+ cci.m_compile_opts.emplace();
+ llvm::cantFail(
+ SymbolDeserializer::deserializeAs<Compile3Sym>(sym, *cci.m_compile_opts));
+}
+
+static void ParseObjname(const CVSymbol &sym, CompilandIndexItem &cci) {
+ cci.m_obj_name.emplace();
+ llvm::cantFail(
+ SymbolDeserializer::deserializeAs<ObjNameSym>(sym, *cci.m_obj_name));
+}
+
+static void ParseBuildInfo(PdbIndex &index, const CVSymbol &sym,
+ CompilandIndexItem &cci) {
+ BuildInfoSym bis(SymbolRecordKind::BuildInfoSym);
+ llvm::cantFail(SymbolDeserializer::deserializeAs<BuildInfoSym>(sym, bis));
+
+ // S_BUILDINFO just points to an LF_BUILDINFO in the IPI stream. Let's do
+ // a little extra work to pull out the LF_BUILDINFO.
+ LazyRandomTypeCollection &types = index.ipi().typeCollection();
+ llvm::Optional<CVType> cvt = types.tryGetType(bis.BuildId);
+
+ if (!cvt || cvt->kind() != LF_BUILDINFO)
+ return;
+
+ BuildInfoRecord bir;
+ llvm::cantFail(TypeDeserializer::deserializeAs<BuildInfoRecord>(*cvt, bir));
+ cci.m_build_info.assign(bir.ArgIndices.begin(), bir.ArgIndices.end());
+}
+
+static void ParseExtendedInfo(PdbIndex &index, CompilandIndexItem &item) {
+ const CVSymbolArray &syms = item.m_debug_stream.getSymbolArray();
+
+ // This is a private function, it shouldn't be called if the information
+ // has already been parsed.
+ lldbassert(!item.m_obj_name);
+ lldbassert(!item.m_compile_opts);
+ lldbassert(item.m_build_info.empty());
+
+ // We're looking for 3 things. S_COMPILE3, S_OBJNAME, and S_BUILDINFO.
+ int found = 0;
+ for (const CVSymbol &sym : syms) {
+ switch (sym.kind()) {
+ case S_COMPILE3:
+ ParseCompile3(sym, item);
+ break;
+ case S_OBJNAME:
+ ParseObjname(sym, item);
+ break;
+ case S_BUILDINFO:
+ ParseBuildInfo(index, sym, item);
+ break;
+ default:
+ continue;
+ }
+ if (++found >= 3)
+ break;
+ }
+}
+
+CompilandIndexItem::CompilandIndexItem(
+ PdbCompilandId id, llvm::pdb::ModuleDebugStreamRef debug_stream,
+ llvm::pdb::DbiModuleDescriptor descriptor)
+ : m_id(id), m_debug_stream(std::move(debug_stream)),
+ m_module_descriptor(std::move(descriptor)) {}
+
+CompilandIndexItem &CompileUnitIndex::GetOrCreateCompiland(uint16_t modi) {
+ auto result = m_comp_units.try_emplace(modi, nullptr);
+ if (!result.second)
+ return *result.first->second;
+
+ // Find the module list and load its debug information stream and cache it
+ // since we need to use it for almost all interesting operations.
+ const DbiModuleList &modules = m_index.dbi().modules();
+ llvm::pdb::DbiModuleDescriptor descriptor = modules.getModuleDescriptor(modi);
+ uint16_t stream = descriptor.getModuleStreamIndex();
+ std::unique_ptr<llvm::msf::MappedBlockStream> stream_data =
+ m_index.pdb().createIndexedStream(stream);
+
+
+ std::unique_ptr<CompilandIndexItem>& cci = result.first->second;
+
+ if (!stream_data) {
+ llvm::pdb::ModuleDebugStreamRef debug_stream(descriptor, nullptr);
+ cci = llvm::make_unique<CompilandIndexItem>(PdbCompilandId{ modi }, debug_stream, std::move(descriptor));
+ return *cci;
+ }
+
+ llvm::pdb::ModuleDebugStreamRef debug_stream(descriptor,
+ std::move(stream_data));
+
+ cantFail(debug_stream.reload());
+
+ cci = llvm::make_unique<CompilandIndexItem>(
+ PdbCompilandId{modi}, std::move(debug_stream), std::move(descriptor));
+ ParseExtendedInfo(m_index, *cci);
+
+ cci->m_strings.initialize(debug_stream.getSubsectionsArray());
+ PDBStringTable &strings = cantFail(m_index.pdb().getStringTable());
+ cci->m_strings.setStrings(strings.getStringTable());
+
+ // We want the main source file to always comes first. Note that we can't
+ // just push_back the main file onto the front because `GetMainSourceFile`
+ // computes it in such a way that it doesn't own the resulting memory. So we
+ // have to iterate the module file list comparing each one to the main file
+ // name until we find it, and we can cache that one since the memory is backed
+ // by a contiguous chunk inside the mapped PDB.
+ llvm::SmallString<64> main_file = GetMainSourceFile(*cci);
+ std::string s = main_file.str();
+ llvm::sys::path::native(main_file);
+
+ uint32_t file_count = modules.getSourceFileCount(modi);
+ cci->m_file_list.reserve(file_count);
+ bool found_main_file = false;
+ for (llvm::StringRef file : modules.source_files(modi)) {
+ if (!found_main_file && IsMainFile(main_file, file)) {
+ cci->m_file_list.insert(cci->m_file_list.begin(), file);
+ found_main_file = true;
+ continue;
+ }
+ cci->m_file_list.push_back(file);
+ }
+
+ return *cci;
+}
+
+const CompilandIndexItem *CompileUnitIndex::GetCompiland(uint16_t modi) const {
+ auto iter = m_comp_units.find(modi);
+ if (iter == m_comp_units.end())
+ return nullptr;
+ return iter->second.get();
+}
+
+CompilandIndexItem *CompileUnitIndex::GetCompiland(uint16_t modi) {
+ auto iter = m_comp_units.find(modi);
+ if (iter == m_comp_units.end())
+ return nullptr;
+ return iter->second.get();
+}
+
+llvm::SmallString<64>
+CompileUnitIndex::GetMainSourceFile(const CompilandIndexItem &item) const {
+ // LF_BUILDINFO contains a list of arg indices which point to LF_STRING_ID
+ // records in the IPI stream. The order of the arg indices is as follows:
+ // [0] - working directory where compiler was invoked.
+ // [1] - absolute path to compiler binary
+ // [2] - source file name
+ // [3] - path to compiler generated PDB (the /Zi PDB, although this entry gets
+ // added even when using /Z7)
+ // [4] - full command line invocation.
+ //
+ // We need to form the path [0]\[2] to generate the full path to the main
+ // file.source
+ if (item.m_build_info.size() < 3)
+ return {""};
+
+ LazyRandomTypeCollection &types = m_index.ipi().typeCollection();
+
+ StringIdRecord working_dir;
+ StringIdRecord file_name;
+ CVType dir_cvt = types.getType(item.m_build_info[0]);
+ CVType file_cvt = types.getType(item.m_build_info[2]);
+ llvm::cantFail(
+ TypeDeserializer::deserializeAs<StringIdRecord>(dir_cvt, working_dir));
+ llvm::cantFail(
+ TypeDeserializer::deserializeAs<StringIdRecord>(file_cvt, file_name));
+
+ llvm::sys::path::Style style = working_dir.String.startswith("/")
+ ? llvm::sys::path::Style::posix
+ : llvm::sys::path::Style::windows;
+ if (llvm::sys::path::is_absolute(file_name.String, style))
+ return file_name.String;
+
+ llvm::SmallString<64> absolute_path = working_dir.String;
+ llvm::sys::path::append(absolute_path, file_name.String);
+ return absolute_path;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h
new file mode 100644
index 000000000000..44a1c8cdd9c2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h
@@ -0,0 +1,94 @@
+//===-- CompileUnitIndex.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_PLUGINS_SYMBOLFILENATIVEPDB_COMPILEUNITINDEX_H
+#define LLDB_PLUGINS_SYMBOLFILENATIVEPDB_COMPILEUNITINDEX_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/IntervalMap.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/DebugInfo/CodeView/StringsAndChecksums.h"
+#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeIndex.h"
+#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h"
+#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
+#include "llvm/DebugInfo/PDB/PDBTypes.h"
+
+#include "PdbSymUid.h"
+
+#include <map>
+#include <memory>
+
+namespace lldb_private {
+
+namespace npdb {
+class PdbIndex;
+
+/// Represents a single compile unit. This class is useful for collecting the
+/// important accessors and information about a compile unit from disparate
+/// parts of the PDB into a single place, simplifying acess to compile unit
+/// information for the callers.
+struct CompilandIndexItem {
+ CompilandIndexItem(PdbCompilandId m_id,
+ llvm::pdb::ModuleDebugStreamRef debug_stream,
+ llvm::pdb::DbiModuleDescriptor descriptor);
+
+ // index of this compile unit.
+ PdbCompilandId m_id;
+
+ // debug stream.
+ llvm::pdb::ModuleDebugStreamRef m_debug_stream;
+
+ // dbi module descriptor.
+ llvm::pdb::DbiModuleDescriptor m_module_descriptor;
+
+ llvm::codeview::StringsAndChecksumsRef m_strings;
+
+ // List of files which contribute to this compiland.
+ std::vector<llvm::StringRef> m_file_list;
+
+ // Maps virtual address to global symbol id, which can then be used to
+ // locate the exact compile unit and offset of the symbol. Note that this
+ // is intentionally an ordered map so that we can find all symbols up to a
+ // given starting address.
+ std::map<lldb::addr_t, PdbSymUid> m_symbols_by_va;
+
+ // S_COMPILE3 sym describing compilation settings for the module.
+ llvm::Optional<llvm::codeview::Compile3Sym> m_compile_opts;
+
+ // S_OBJNAME sym describing object name.
+ llvm::Optional<llvm::codeview::ObjNameSym> m_obj_name;
+
+ // LF_BUILDINFO sym describing source file name, working directory,
+ // command line, etc. This usually contains exactly 5 items which
+ // are references to other strings.
+ llvm::SmallVector<llvm::codeview::TypeIndex, 5> m_build_info;
+};
+
+/// Indexes information about all compile units. This is really just a map of
+/// global compile unit index to |CompilandIndexItem| structures.
+class CompileUnitIndex {
+ PdbIndex &m_index;
+ llvm::DenseMap<uint16_t, std::unique_ptr<CompilandIndexItem>> m_comp_units;
+
+public:
+ explicit CompileUnitIndex(PdbIndex &index) : m_index(index) {}
+
+ CompilandIndexItem &GetOrCreateCompiland(uint16_t modi);
+
+ const CompilandIndexItem *GetCompiland(uint16_t modi) const;
+
+ CompilandIndexItem *GetCompiland(uint16_t modi);
+
+ llvm::SmallString<64> GetMainSourceFile(const CompilandIndexItem &item) const;
+};
+} // namespace npdb
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp
new file mode 100644
index 000000000000..3d8bfb058721
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp
@@ -0,0 +1,252 @@
+//===-- DWARFLocationExpression.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 "DWARFLocationExpression.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/StreamBuffer.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/DataBufferHeap.h"
+
+#include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
+#include "llvm/DebugInfo/CodeView/TypeIndex.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/Support/Endian.h"
+
+#include "PdbUtil.h"
+#include "CodeViewRegisterMapping.h"
+#include "PdbFPOProgramToDWARFExpression.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::npdb;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+uint32_t GetGenericRegisterNumber(llvm::codeview::RegisterId register_id) {
+ if (register_id == llvm::codeview::RegisterId::VFRAME)
+ return LLDB_REGNUM_GENERIC_FP;
+
+ return LLDB_INVALID_REGNUM;
+}
+
+static uint32_t GetRegisterNumber(llvm::Triple::ArchType arch_type,
+ llvm::codeview::RegisterId register_id,
+ RegisterKind &register_kind) {
+ register_kind = eRegisterKindLLDB;
+ uint32_t reg_num = GetLLDBRegisterNumber(arch_type, register_id);
+ if (reg_num != LLDB_INVALID_REGNUM)
+ return reg_num;
+
+ register_kind = eRegisterKindGeneric;
+ return GetGenericRegisterNumber(register_id);
+}
+
+static bool IsSimpleTypeSignedInteger(SimpleTypeKind kind) {
+ switch (kind) {
+ case SimpleTypeKind::Int128:
+ case SimpleTypeKind::Int64:
+ case SimpleTypeKind::Int64Quad:
+ case SimpleTypeKind::Int32:
+ case SimpleTypeKind::Int32Long:
+ case SimpleTypeKind::Int16:
+ case SimpleTypeKind::Int16Short:
+ case SimpleTypeKind::Float128:
+ case SimpleTypeKind::Float80:
+ case SimpleTypeKind::Float64:
+ case SimpleTypeKind::Float32:
+ case SimpleTypeKind::Float16:
+ case SimpleTypeKind::NarrowCharacter:
+ case SimpleTypeKind::SignedCharacter:
+ case SimpleTypeKind::SByte:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static std::pair<size_t, bool> GetIntegralTypeInfo(TypeIndex ti,
+ TpiStream &tpi) {
+ if (ti.isSimple()) {
+ SimpleTypeKind stk = ti.getSimpleKind();
+ return {GetTypeSizeForSimpleKind(stk), IsSimpleTypeSignedInteger(stk)};
+ }
+
+ CVType cvt = tpi.getType(ti);
+ switch (cvt.kind()) {
+ case LF_MODIFIER: {
+ ModifierRecord mfr;
+ llvm::cantFail(TypeDeserializer::deserializeAs<ModifierRecord>(cvt, mfr));
+ return GetIntegralTypeInfo(mfr.ModifiedType, tpi);
+ }
+ case LF_POINTER: {
+ PointerRecord pr;
+ llvm::cantFail(TypeDeserializer::deserializeAs<PointerRecord>(cvt, pr));
+ return GetIntegralTypeInfo(pr.ReferentType, tpi);
+ }
+ case LF_ENUM: {
+ EnumRecord er;
+ llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, er));
+ return GetIntegralTypeInfo(er.UnderlyingType, tpi);
+ }
+ default:
+ assert(false && "Type is not integral!");
+ return {0, false};
+ }
+}
+
+template <typename StreamWriter>
+static DWARFExpression MakeLocationExpressionInternal(lldb::ModuleSP module,
+ StreamWriter &&writer) {
+ const ArchSpec &architecture = module->GetArchitecture();
+ ByteOrder byte_order = architecture.GetByteOrder();
+ uint32_t address_size = architecture.GetAddressByteSize();
+ uint32_t byte_size = architecture.GetDataByteSize();
+ if (byte_order == eByteOrderInvalid || address_size == 0)
+ return DWARFExpression();
+
+ RegisterKind register_kind = eRegisterKindDWARF;
+ StreamBuffer<32> stream(Stream::eBinary, address_size, byte_order);
+
+ if (!writer(stream, register_kind))
+ return DWARFExpression();
+
+ DataBufferSP buffer =
+ std::make_shared<DataBufferHeap>(stream.GetData(), stream.GetSize());
+ DataExtractor extractor(buffer, byte_order, address_size, byte_size);
+ DWARFExpression result(module, extractor, nullptr, 0, buffer->GetByteSize());
+ result.SetRegisterKind(register_kind);
+
+ return result;
+}
+
+static DWARFExpression MakeRegisterBasedLocationExpressionInternal(
+ llvm::codeview::RegisterId reg, llvm::Optional<int32_t> relative_offset,
+ lldb::ModuleSP module) {
+ return MakeLocationExpressionInternal(
+ module, [&](Stream &stream, RegisterKind &register_kind) -> bool {
+ uint32_t reg_num = GetRegisterNumber(
+ module->GetArchitecture().GetMachine(), reg, register_kind);
+ if (reg_num == LLDB_INVALID_REGNUM)
+ return false;
+
+ if (reg_num > 31) {
+ llvm::dwarf::LocationAtom base = relative_offset
+ ? llvm::dwarf::DW_OP_bregx
+ : llvm::dwarf::DW_OP_regx;
+ stream.PutHex8(base);
+ stream.PutULEB128(reg_num);
+ } else {
+ llvm::dwarf::LocationAtom base = relative_offset
+ ? llvm::dwarf::DW_OP_breg0
+ : llvm::dwarf::DW_OP_reg0;
+ stream.PutHex8(base + reg_num);
+ }
+
+ if (relative_offset)
+ stream.PutSLEB128(*relative_offset);
+
+ return true;
+ });
+}
+
+DWARFExpression lldb_private::npdb::MakeEnregisteredLocationExpression(
+ llvm::codeview::RegisterId reg, lldb::ModuleSP module) {
+ return MakeRegisterBasedLocationExpressionInternal(reg, llvm::None, module);
+}
+
+DWARFExpression lldb_private::npdb::MakeRegRelLocationExpression(
+ llvm::codeview::RegisterId reg, int32_t offset, lldb::ModuleSP module) {
+ return MakeRegisterBasedLocationExpressionInternal(reg, offset, module);
+}
+
+static bool EmitVFrameEvaluationDWARFExpression(
+ llvm::StringRef program, llvm::Triple::ArchType arch_type, Stream &stream) {
+ // VFrame value always stored in $TO pseudo-register
+ return TranslateFPOProgramToDWARFExpression(program, "$T0", arch_type,
+ stream);
+}
+
+DWARFExpression lldb_private::npdb::MakeVFrameRelLocationExpression(
+ llvm::StringRef fpo_program, int32_t offset, lldb::ModuleSP module) {
+ return MakeLocationExpressionInternal(
+ module, [&](Stream &stream, RegisterKind &register_kind) -> bool {
+ const ArchSpec &architecture = module->GetArchitecture();
+
+ if (!EmitVFrameEvaluationDWARFExpression(fpo_program, architecture.GetMachine(),
+ stream))
+ return false;
+
+ stream.PutHex8(llvm::dwarf::DW_OP_consts);
+ stream.PutSLEB128(offset);
+ stream.PutHex8(llvm::dwarf::DW_OP_plus);
+
+ register_kind = eRegisterKindLLDB;
+
+ return true;
+ });
+}
+
+DWARFExpression lldb_private::npdb::MakeGlobalLocationExpression(
+ uint16_t section, uint32_t offset, ModuleSP module) {
+ assert(section > 0);
+ assert(module);
+
+ return MakeLocationExpressionInternal(
+ module, [&](Stream &stream, RegisterKind &register_kind) -> bool {
+ stream.PutHex8(llvm::dwarf::DW_OP_addr);
+
+ SectionList *section_list = module->GetSectionList();
+ assert(section_list);
+
+ auto section_ptr = section_list->FindSectionByID(section);
+ if (!section_ptr)
+ return false;
+
+ stream.PutMaxHex64(section_ptr->GetFileAddress() + offset,
+ stream.GetAddressByteSize(), stream.GetByteOrder());
+
+ return true;
+ });
+}
+
+DWARFExpression lldb_private::npdb::MakeConstantLocationExpression(
+ TypeIndex underlying_ti, TpiStream &tpi, const llvm::APSInt &constant,
+ ModuleSP module) {
+ const ArchSpec &architecture = module->GetArchitecture();
+ uint32_t address_size = architecture.GetAddressByteSize();
+
+ size_t size = 0;
+ bool is_signed = false;
+ std::tie(size, is_signed) = GetIntegralTypeInfo(underlying_ti, tpi);
+
+ union {
+ llvm::support::little64_t I;
+ llvm::support::ulittle64_t U;
+ } Value;
+
+ std::shared_ptr<DataBufferHeap> buffer = std::make_shared<DataBufferHeap>();
+ buffer->SetByteSize(size);
+
+ llvm::ArrayRef<uint8_t> bytes;
+ if (is_signed) {
+ Value.I = constant.getSExtValue();
+ } else {
+ Value.U = constant.getZExtValue();
+ }
+
+ bytes = llvm::makeArrayRef(reinterpret_cast<const uint8_t *>(&Value), 8)
+ .take_front(size);
+ buffer->CopyData(bytes.data(), size);
+ DataExtractor extractor(buffer, lldb::eByteOrderLittle, address_size);
+ DWARFExpression result(nullptr, extractor, nullptr, 0, size);
+ return result;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h
new file mode 100644
index 000000000000..c37d715babdc
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h
@@ -0,0 +1,44 @@
+//===-- DWARFLocationExpression.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_PLUGINS_SYMBOLFILE_NATIVEPDB_DWARFLOCATIONEXPRESSION_H
+#define LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_DWARFLOCATIONEXPRESSION_H
+
+#include "lldb/lldb-forward.h"
+#include "llvm/DebugInfo/CodeView/CodeView.h"
+
+namespace llvm {
+class APSInt;
+namespace codeview {
+class TypeIndex;
+}
+namespace pdb {
+class TpiStream;
+}
+} // namespace llvm
+namespace lldb_private {
+namespace npdb {
+DWARFExpression
+MakeEnregisteredLocationExpression(llvm::codeview::RegisterId reg,
+ lldb::ModuleSP module);
+
+DWARFExpression MakeRegRelLocationExpression(llvm::codeview::RegisterId reg,
+ int32_t offset,
+ lldb::ModuleSP module);
+DWARFExpression MakeVFrameRelLocationExpression(llvm::StringRef fpo_program,
+ int32_t offset,
+ lldb::ModuleSP module);
+DWARFExpression MakeGlobalLocationExpression(uint16_t section, uint32_t offset,
+ lldb::ModuleSP module);
+DWARFExpression MakeConstantLocationExpression(
+ llvm::codeview::TypeIndex underlying_ti, llvm::pdb::TpiStream &tpi,
+ const llvm::APSInt &constant, lldb::ModuleSP module);
+} // namespace npdb
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp
new file mode 100644
index 000000000000..4991be8e70ce
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp
@@ -0,0 +1,1366 @@
+#include "PdbAstBuilder.h"
+
+#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
+#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
+#include "llvm/DebugInfo/CodeView/RecordName.h"
+#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
+#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
+#include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h"
+#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
+#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/PublicsStream.h"
+#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/Demangle/MicrosoftDemangle.h"
+
+#include "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
+#include "lldb/Symbol/ClangUtil.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/LLDBAssert.h"
+
+#include "PdbUtil.h"
+#include "UdtRecordCompleter.h"
+
+using namespace lldb_private;
+using namespace lldb_private::npdb;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+static llvm::Optional<PdbCompilandSymId> FindSymbolScope(PdbIndex &index,
+ PdbCompilandSymId id) {
+ CVSymbol sym = index.ReadSymbolRecord(id);
+ if (symbolOpensScope(sym.kind())) {
+ // If this exact symbol opens a scope, we can just directly access its
+ // parent.
+ id.offset = getScopeParentOffset(sym);
+ // Global symbols have parent offset of 0. Return llvm::None to indicate
+ // this.
+ if (id.offset == 0)
+ return llvm::None;
+ return id;
+ }
+
+ // Otherwise we need to start at the beginning and iterate forward until we
+ // reach (or pass) this particular symbol
+ CompilandIndexItem &cii = index.compilands().GetOrCreateCompiland(id.modi);
+ const CVSymbolArray &syms = cii.m_debug_stream.getSymbolArray();
+
+ auto begin = syms.begin();
+ auto end = syms.at(id.offset);
+ std::vector<PdbCompilandSymId> scope_stack;
+
+ while (begin != end) {
+ if (id.offset == begin.offset()) {
+ // We have a match! Return the top of the stack
+ if (scope_stack.empty())
+ return llvm::None;
+ return scope_stack.back();
+ }
+ if (begin.offset() > id.offset) {
+ // We passed it. We couldn't even find this symbol record.
+ lldbassert(false && "Invalid compiland symbol id!");
+ return llvm::None;
+ }
+
+ // We haven't found the symbol yet. Check if we need to open or close the
+ // scope stack.
+ if (symbolOpensScope(begin->kind())) {
+ // We can use the end offset of the scope to determine whether or not
+ // we can just outright skip this entire scope.
+ uint32_t scope_end = getScopeEndOffset(*begin);
+ if (scope_end < id.modi) {
+ begin = syms.at(scope_end);
+ } else {
+ // The symbol we're looking for is somewhere in this scope.
+ scope_stack.emplace_back(id.modi, begin.offset());
+ }
+ } else if (symbolEndsScope(begin->kind())) {
+ scope_stack.pop_back();
+ }
+ ++begin;
+ }
+
+ return llvm::None;
+}
+
+static clang::TagTypeKind TranslateUdtKind(const TagRecord &cr) {
+ switch (cr.Kind) {
+ case TypeRecordKind::Class:
+ return clang::TTK_Class;
+ case TypeRecordKind::Struct:
+ return clang::TTK_Struct;
+ case TypeRecordKind::Union:
+ return clang::TTK_Union;
+ case TypeRecordKind::Interface:
+ return clang::TTK_Interface;
+ case TypeRecordKind::Enum:
+ return clang::TTK_Enum;
+ default:
+ lldbassert(false && "Invalid tag record kind!");
+ return clang::TTK_Struct;
+ }
+}
+
+static bool IsCVarArgsFunction(llvm::ArrayRef<TypeIndex> args) {
+ if (args.empty())
+ return false;
+ return args.back() == TypeIndex::None();
+}
+
+static bool
+AnyScopesHaveTemplateParams(llvm::ArrayRef<llvm::ms_demangle::Node *> scopes) {
+ for (llvm::ms_demangle::Node *n : scopes) {
+ auto *idn = static_cast<llvm::ms_demangle::IdentifierNode *>(n);
+ if (idn->TemplateParams)
+ return true;
+ }
+ return false;
+}
+
+static ClangASTContext &GetClangASTContext(ObjectFile &obj) {
+ TypeSystem *ts =
+ obj.GetModule()->GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus);
+ lldbassert(ts);
+ return static_cast<ClangASTContext &>(*ts);
+}
+
+static llvm::Optional<clang::CallingConv>
+TranslateCallingConvention(llvm::codeview::CallingConvention conv) {
+ using CC = llvm::codeview::CallingConvention;
+ switch (conv) {
+
+ case CC::NearC:
+ case CC::FarC:
+ return clang::CallingConv::CC_C;
+ case CC::NearPascal:
+ case CC::FarPascal:
+ return clang::CallingConv::CC_X86Pascal;
+ case CC::NearFast:
+ case CC::FarFast:
+ return clang::CallingConv::CC_X86FastCall;
+ case CC::NearStdCall:
+ case CC::FarStdCall:
+ return clang::CallingConv::CC_X86StdCall;
+ case CC::ThisCall:
+ return clang::CallingConv::CC_X86ThisCall;
+ case CC::NearVector:
+ return clang::CallingConv::CC_X86VectorCall;
+ default:
+ return llvm::None;
+ }
+}
+
+static llvm::Optional<CVTagRecord>
+GetNestedTagDefinition(const NestedTypeRecord &Record,
+ const CVTagRecord &parent, TpiStream &tpi) {
+ // An LF_NESTTYPE is essentially a nested typedef / using declaration, but it
+ // is also used to indicate the primary definition of a nested class. That is
+ // to say, if you have:
+ // struct A {
+ // struct B {};
+ // using C = B;
+ // };
+ // Then in the debug info, this will appear as:
+ // LF_STRUCTURE `A::B` [type index = N]
+ // LF_STRUCTURE `A`
+ // LF_NESTTYPE [name = `B`, index = N]
+ // LF_NESTTYPE [name = `C`, index = N]
+ // In order to accurately reconstruct the decl context hierarchy, we need to
+ // know which ones are actual definitions and which ones are just aliases.
+
+ // If it's a simple type, then this is something like `using foo = int`.
+ if (Record.Type.isSimple())
+ return llvm::None;
+
+ CVType cvt = tpi.getType(Record.Type);
+
+ if (!IsTagRecord(cvt))
+ return llvm::None;
+
+ // If it's an inner definition, then treat whatever name we have here as a
+ // single component of a mangled name. So we can inject it into the parent's
+ // mangled name to see if it matches.
+ CVTagRecord child = CVTagRecord::create(cvt);
+ std::string qname = parent.asTag().getUniqueName();
+ if (qname.size() < 4 || child.asTag().getUniqueName().size() < 4)
+ return llvm::None;
+
+ // qname[3] is the tag type identifier (struct, class, union, etc). Since the
+ // inner tag type is not necessarily the same as the outer tag type, re-write
+ // it to match the inner tag type.
+ qname[3] = child.asTag().getUniqueName()[3];
+ std::string piece;
+ if (qname[3] == 'W')
+ piece = "4";
+ piece += Record.Name;
+ piece.push_back('@');
+ qname.insert(4, std::move(piece));
+ if (qname != child.asTag().UniqueName)
+ return llvm::None;
+
+ return std::move(child);
+}
+
+static bool IsAnonymousNamespaceName(llvm::StringRef name) {
+ return name == "`anonymous namespace'" || name == "`anonymous-namespace'";
+}
+
+PdbAstBuilder::PdbAstBuilder(ObjectFile &obj, PdbIndex &index)
+ : m_index(index), m_clang(GetClangASTContext(obj)) {
+ BuildParentMap();
+}
+
+lldb_private::CompilerDeclContext PdbAstBuilder::GetTranslationUnitDecl() {
+ return ToCompilerDeclContext(*m_clang.GetTranslationUnitDecl());
+}
+
+std::pair<clang::DeclContext *, std::string>
+PdbAstBuilder::CreateDeclInfoForType(const TagRecord &record, TypeIndex ti) {
+ // FIXME: Move this to GetDeclContextContainingUID.
+ if (!record.hasUniqueName())
+ return CreateDeclInfoForUndecoratedName(record.Name);
+
+ llvm::ms_demangle::Demangler demangler;
+ StringView sv(record.UniqueName.begin(), record.UniqueName.size());
+ llvm::ms_demangle::TagTypeNode *ttn = demangler.parseTagUniqueName(sv);
+ if (demangler.Error)
+ return {m_clang.GetTranslationUnitDecl(), record.UniqueName};
+
+ llvm::ms_demangle::IdentifierNode *idn =
+ ttn->QualifiedName->getUnqualifiedIdentifier();
+ std::string uname = idn->toString(llvm::ms_demangle::OF_NoTagSpecifier);
+
+ llvm::ms_demangle::NodeArrayNode *name_components =
+ ttn->QualifiedName->Components;
+ llvm::ArrayRef<llvm::ms_demangle::Node *> scopes(name_components->Nodes,
+ name_components->Count - 1);
+
+ clang::DeclContext *context = m_clang.GetTranslationUnitDecl();
+
+ // If this type doesn't have a parent type in the debug info, then the best we
+ // can do is to say that it's either a series of namespaces (if the scope is
+ // non-empty), or the translation unit (if the scope is empty).
+ auto parent_iter = m_parent_types.find(ti);
+ if (parent_iter == m_parent_types.end()) {
+ if (scopes.empty())
+ return {context, uname};
+
+ // If there is no parent in the debug info, but some of the scopes have
+ // template params, then this is a case of bad debug info. See, for
+ // example, llvm.org/pr39607. We don't want to create an ambiguity between
+ // a NamespaceDecl and a CXXRecordDecl, so instead we create a class at
+ // global scope with the fully qualified name.
+ if (AnyScopesHaveTemplateParams(scopes))
+ return {context, record.Name};
+
+ for (llvm::ms_demangle::Node *scope : scopes) {
+ auto *nii = static_cast<llvm::ms_demangle::NamedIdentifierNode *>(scope);
+ std::string str = nii->toString();
+ context = GetOrCreateNamespaceDecl(str.c_str(), *context);
+ }
+ return {context, uname};
+ }
+
+ // Otherwise, all we need to do is get the parent type of this type and
+ // recurse into our lazy type creation / AST reconstruction logic to get an
+ // LLDB TypeSP for the parent. This will cause the AST to automatically get
+ // the right DeclContext created for any parent.
+ clang::QualType parent_qt = GetOrCreateType(parent_iter->second);
+
+ context = clang::TagDecl::castToDeclContext(parent_qt->getAsTagDecl());
+ return {context, uname};
+}
+
+void PdbAstBuilder::BuildParentMap() {
+ LazyRandomTypeCollection &types = m_index.tpi().typeCollection();
+
+ llvm::DenseMap<TypeIndex, TypeIndex> forward_to_full;
+ llvm::DenseMap<TypeIndex, TypeIndex> full_to_forward;
+
+ struct RecordIndices {
+ TypeIndex forward;
+ TypeIndex full;
+ };
+
+ llvm::StringMap<RecordIndices> record_indices;
+
+ for (auto ti = types.getFirst(); ti; ti = types.getNext(*ti)) {
+ CVType type = types.getType(*ti);
+ if (!IsTagRecord(type))
+ continue;
+
+ CVTagRecord tag = CVTagRecord::create(type);
+
+ RecordIndices &indices = record_indices[tag.asTag().getUniqueName()];
+ if (tag.asTag().isForwardRef())
+ indices.forward = *ti;
+ else
+ indices.full = *ti;
+
+ if (indices.full != TypeIndex::None() &&
+ indices.forward != TypeIndex::None()) {
+ forward_to_full[indices.forward] = indices.full;
+ full_to_forward[indices.full] = indices.forward;
+ }
+
+ // We're looking for LF_NESTTYPE records in the field list, so ignore
+ // forward references (no field list), and anything without a nested class
+ // (since there won't be any LF_NESTTYPE records).
+ if (tag.asTag().isForwardRef() || !tag.asTag().containsNestedClass())
+ continue;
+
+ struct ProcessTpiStream : public TypeVisitorCallbacks {
+ ProcessTpiStream(PdbIndex &index, TypeIndex parent,
+ const CVTagRecord &parent_cvt,
+ llvm::DenseMap<TypeIndex, TypeIndex> &parents)
+ : index(index), parents(parents), parent(parent),
+ parent_cvt(parent_cvt) {}
+
+ PdbIndex &index;
+ llvm::DenseMap<TypeIndex, TypeIndex> &parents;
+
+ unsigned unnamed_type_index = 1;
+ TypeIndex parent;
+ const CVTagRecord &parent_cvt;
+
+ llvm::Error visitKnownMember(CVMemberRecord &CVR,
+ NestedTypeRecord &Record) override {
+ std::string unnamed_type_name;
+ if (Record.Name.empty()) {
+ unnamed_type_name =
+ llvm::formatv("<unnamed-type-$S{0}>", unnamed_type_index).str();
+ Record.Name = unnamed_type_name;
+ ++unnamed_type_index;
+ }
+ llvm::Optional<CVTagRecord> tag =
+ GetNestedTagDefinition(Record, parent_cvt, index.tpi());
+ if (!tag)
+ return llvm::ErrorSuccess();
+
+ parents[Record.Type] = parent;
+ return llvm::ErrorSuccess();
+ }
+ };
+
+ CVType field_list = m_index.tpi().getType(tag.asTag().FieldList);
+ ProcessTpiStream process(m_index, *ti, tag, m_parent_types);
+ llvm::Error error = visitMemberRecordStream(field_list.data(), process);
+ if (error)
+ llvm::consumeError(std::move(error));
+ }
+
+ // Now that we know the forward -> full mapping of all type indices, we can
+ // re-write all the indices. At the end of this process, we want a mapping
+ // consisting of fwd -> full and full -> full for all child -> parent indices.
+ // We can re-write the values in place, but for the keys, we must save them
+ // off so that we don't modify the map in place while also iterating it.
+ std::vector<TypeIndex> full_keys;
+ std::vector<TypeIndex> fwd_keys;
+ for (auto &entry : m_parent_types) {
+ TypeIndex key = entry.first;
+ TypeIndex value = entry.second;
+
+ auto iter = forward_to_full.find(value);
+ if (iter != forward_to_full.end())
+ entry.second = iter->second;
+
+ iter = forward_to_full.find(key);
+ if (iter != forward_to_full.end())
+ fwd_keys.push_back(key);
+ else
+ full_keys.push_back(key);
+ }
+ for (TypeIndex fwd : fwd_keys) {
+ TypeIndex full = forward_to_full[fwd];
+ m_parent_types[full] = m_parent_types[fwd];
+ }
+ for (TypeIndex full : full_keys) {
+ TypeIndex fwd = full_to_forward[full];
+ m_parent_types[fwd] = m_parent_types[full];
+ }
+
+ // Now that
+}
+
+static bool isLocalVariableType(SymbolKind K) {
+ switch (K) {
+ case S_REGISTER:
+ case S_REGREL32:
+ case S_LOCAL:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+static std::string
+RenderScopeList(llvm::ArrayRef<llvm::ms_demangle::Node *> nodes) {
+ lldbassert(!nodes.empty());
+
+ std::string result = nodes.front()->toString();
+ nodes = nodes.drop_front();
+ while (!nodes.empty()) {
+ result += "::";
+ result += nodes.front()->toString(llvm::ms_demangle::OF_NoTagSpecifier);
+ nodes = nodes.drop_front();
+ }
+ return result;
+}
+
+static llvm::Optional<PublicSym32> FindPublicSym(const SegmentOffset &addr,
+ SymbolStream &syms,
+ PublicsStream &publics) {
+ llvm::FixedStreamArray<ulittle32_t> addr_map = publics.getAddressMap();
+ auto iter = std::lower_bound(
+ addr_map.begin(), addr_map.end(), addr,
+ [&](const ulittle32_t &x, const SegmentOffset &y) {
+ CVSymbol s1 = syms.readRecord(x);
+ lldbassert(s1.kind() == S_PUB32);
+ PublicSym32 p1;
+ llvm::cantFail(SymbolDeserializer::deserializeAs<PublicSym32>(s1, p1));
+ if (p1.Segment < y.segment)
+ return true;
+ return p1.Offset < y.offset;
+ });
+ if (iter == addr_map.end())
+ return llvm::None;
+ CVSymbol sym = syms.readRecord(*iter);
+ lldbassert(sym.kind() == S_PUB32);
+ PublicSym32 p;
+ llvm::cantFail(SymbolDeserializer::deserializeAs<PublicSym32>(sym, p));
+ if (p.Segment == addr.segment && p.Offset == addr.offset)
+ return p;
+ return llvm::None;
+}
+
+clang::Decl *PdbAstBuilder::GetOrCreateSymbolForId(PdbCompilandSymId id) {
+ CVSymbol cvs = m_index.ReadSymbolRecord(id);
+
+ if (isLocalVariableType(cvs.kind())) {
+ clang::DeclContext *scope = GetParentDeclContext(id);
+ clang::Decl *scope_decl = clang::Decl::castFromDeclContext(scope);
+ PdbCompilandSymId scope_id(id.modi, m_decl_to_status[scope_decl].uid);
+ return GetOrCreateVariableDecl(scope_id, id);
+ }
+
+ switch (cvs.kind()) {
+ case S_GPROC32:
+ case S_LPROC32:
+ return GetOrCreateFunctionDecl(id);
+ case S_GDATA32:
+ case S_LDATA32:
+ case S_GTHREAD32:
+ case S_CONSTANT:
+ // global variable
+ return nullptr;
+ case S_BLOCK32:
+ return GetOrCreateBlockDecl(id);
+ default:
+ return nullptr;
+ }
+}
+
+clang::Decl *PdbAstBuilder::GetOrCreateDeclForUid(PdbSymUid uid) {
+ if (clang::Decl *result = TryGetDecl(uid))
+ return result;
+
+ clang::Decl *result = nullptr;
+ switch (uid.kind()) {
+ case PdbSymUidKind::CompilandSym:
+ result = GetOrCreateSymbolForId(uid.asCompilandSym());
+ break;
+ case PdbSymUidKind::Type: {
+ clang::QualType qt = GetOrCreateType(uid.asTypeSym());
+ if (auto *tag = qt->getAsTagDecl()) {
+ result = tag;
+ break;
+ }
+ return nullptr;
+ }
+ default:
+ return nullptr;
+ }
+ m_uid_to_decl[toOpaqueUid(uid)] = result;
+ return result;
+}
+
+clang::DeclContext *PdbAstBuilder::GetOrCreateDeclContextForUid(PdbSymUid uid) {
+ if (uid.kind() == PdbSymUidKind::CompilandSym) {
+ if (uid.asCompilandSym().offset == 0)
+ return FromCompilerDeclContext(GetTranslationUnitDecl());
+ }
+
+ clang::Decl *decl = GetOrCreateDeclForUid(uid);
+ if (!decl)
+ return nullptr;
+
+ return clang::Decl::castToDeclContext(decl);
+}
+
+std::pair<clang::DeclContext *, std::string>
+PdbAstBuilder::CreateDeclInfoForUndecoratedName(llvm::StringRef name) {
+ MSVCUndecoratedNameParser parser(name);
+ llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs = parser.GetSpecifiers();
+
+ auto context = FromCompilerDeclContext(GetTranslationUnitDecl());
+
+ llvm::StringRef uname = specs.back().GetBaseName();
+ specs = specs.drop_back();
+ if (specs.empty())
+ return {context, name};
+
+ llvm::StringRef scope_name = specs.back().GetFullName();
+
+ // It might be a class name, try that first.
+ std::vector<TypeIndex> types = m_index.tpi().findRecordsByName(scope_name);
+ while (!types.empty()) {
+ clang::QualType qt = GetOrCreateType(types.back());
+ clang::TagDecl *tag = qt->getAsTagDecl();
+ if (tag)
+ return {clang::TagDecl::castToDeclContext(tag), uname};
+ types.pop_back();
+ }
+
+ // If that fails, treat it as a series of namespaces.
+ for (const MSVCUndecoratedNameSpecifier &spec : specs) {
+ std::string ns_name = spec.GetBaseName().str();
+ context = GetOrCreateNamespaceDecl(ns_name.c_str(), *context);
+ }
+ return {context, uname};
+}
+
+clang::DeclContext *
+PdbAstBuilder::GetParentDeclContextForSymbol(const CVSymbol &sym) {
+ if (!SymbolHasAddress(sym))
+ return CreateDeclInfoForUndecoratedName(getSymbolName(sym)).first;
+ SegmentOffset addr = GetSegmentAndOffset(sym);
+ llvm::Optional<PublicSym32> pub =
+ FindPublicSym(addr, m_index.symrecords(), m_index.publics());
+ if (!pub)
+ return CreateDeclInfoForUndecoratedName(getSymbolName(sym)).first;
+
+ llvm::ms_demangle::Demangler demangler;
+ StringView name{pub->Name.begin(), pub->Name.size()};
+ llvm::ms_demangle::SymbolNode *node = demangler.parse(name);
+ if (!node)
+ return FromCompilerDeclContext(GetTranslationUnitDecl());
+ llvm::ArrayRef<llvm::ms_demangle::Node *> name_components{
+ node->Name->Components->Nodes, node->Name->Components->Count - 1};
+
+ if (!name_components.empty()) {
+ // Render the current list of scope nodes as a fully qualified name, and
+ // look it up in the debug info as a type name. If we find something,
+ // this is a type (which may itself be prefixed by a namespace). If we
+ // don't, this is a list of namespaces.
+ std::string qname = RenderScopeList(name_components);
+ std::vector<TypeIndex> matches = m_index.tpi().findRecordsByName(qname);
+ while (!matches.empty()) {
+ clang::QualType qt = GetOrCreateType(matches.back());
+ clang::TagDecl *tag = qt->getAsTagDecl();
+ if (tag)
+ return clang::TagDecl::castToDeclContext(tag);
+ matches.pop_back();
+ }
+ }
+
+ // It's not a type. It must be a series of namespaces.
+ auto context = FromCompilerDeclContext(GetTranslationUnitDecl());
+ while (!name_components.empty()) {
+ std::string ns = name_components.front()->toString();
+ context = GetOrCreateNamespaceDecl(ns.c_str(), *context);
+ name_components = name_components.drop_front();
+ }
+ return context;
+}
+
+clang::DeclContext *PdbAstBuilder::GetParentDeclContext(PdbSymUid uid) {
+ // We must do this *without* calling GetOrCreate on the current uid, as
+ // that would be an infinite recursion.
+ switch (uid.kind()) {
+ case PdbSymUidKind::CompilandSym: {
+ llvm::Optional<PdbCompilandSymId> scope =
+ FindSymbolScope(m_index, uid.asCompilandSym());
+ if (scope)
+ return GetOrCreateDeclContextForUid(*scope);
+
+ CVSymbol sym = m_index.ReadSymbolRecord(uid.asCompilandSym());
+ return GetParentDeclContextForSymbol(sym);
+ }
+ case PdbSymUidKind::Type: {
+ // It could be a namespace, class, or global. We don't support nested
+ // functions yet. Anyway, we just need to consult the parent type map.
+ PdbTypeSymId type_id = uid.asTypeSym();
+ auto iter = m_parent_types.find(type_id.index);
+ if (iter == m_parent_types.end())
+ return FromCompilerDeclContext(GetTranslationUnitDecl());
+ return GetOrCreateDeclContextForUid(PdbTypeSymId(iter->second));
+ }
+ case PdbSymUidKind::FieldListMember:
+ // In this case the parent DeclContext is the one for the class that this
+ // member is inside of.
+ break;
+ case PdbSymUidKind::GlobalSym: {
+ // If this refers to a compiland symbol, just recurse in with that symbol.
+ // The only other possibilities are S_CONSTANT and S_UDT, in which case we
+ // need to parse the undecorated name to figure out the scope, then look
+ // that up in the TPI stream. If it's found, it's a type, othewrise it's
+ // a series of namespaces.
+ // FIXME: do this.
+ CVSymbol global = m_index.ReadSymbolRecord(uid.asGlobalSym());
+ switch (global.kind()) {
+ case SymbolKind::S_GDATA32:
+ case SymbolKind::S_LDATA32:
+ return GetParentDeclContextForSymbol(global);
+ case SymbolKind::S_PROCREF:
+ case SymbolKind::S_LPROCREF: {
+ ProcRefSym ref{global.kind()};
+ llvm::cantFail(
+ SymbolDeserializer::deserializeAs<ProcRefSym>(global, ref));
+ PdbCompilandSymId cu_sym_id{ref.modi(), ref.SymOffset};
+ return GetParentDeclContext(cu_sym_id);
+ }
+ case SymbolKind::S_CONSTANT:
+ case SymbolKind::S_UDT:
+ return CreateDeclInfoForUndecoratedName(getSymbolName(global)).first;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return FromCompilerDeclContext(GetTranslationUnitDecl());
+}
+
+bool PdbAstBuilder::CompleteType(clang::QualType qt) {
+ clang::TagDecl *tag = qt->getAsTagDecl();
+ if (!tag)
+ return false;
+
+ return CompleteTagDecl(*tag);
+}
+
+bool PdbAstBuilder::CompleteTagDecl(clang::TagDecl &tag) {
+ // If this is not in our map, it's an error.
+ auto status_iter = m_decl_to_status.find(&tag);
+ lldbassert(status_iter != m_decl_to_status.end());
+
+ // If it's already complete, just return.
+ DeclStatus &status = status_iter->second;
+ if (status.resolved)
+ return true;
+
+ PdbTypeSymId type_id = PdbSymUid(status.uid).asTypeSym();
+
+ lldbassert(IsTagRecord(type_id, m_index.tpi()));
+
+ clang::QualType tag_qt = m_clang.getASTContext()->getTypeDeclType(&tag);
+ ClangASTContext::SetHasExternalStorage(tag_qt.getAsOpaquePtr(), false);
+
+ TypeIndex tag_ti = type_id.index;
+ CVType cvt = m_index.tpi().getType(tag_ti);
+ if (cvt.kind() == LF_MODIFIER)
+ tag_ti = LookThroughModifierRecord(cvt);
+
+ PdbTypeSymId best_ti = GetBestPossibleDecl(tag_ti, m_index.tpi());
+ cvt = m_index.tpi().getType(best_ti.index);
+ lldbassert(IsTagRecord(cvt));
+
+ if (IsForwardRefUdt(cvt)) {
+ // If we can't find a full decl for this forward ref anywhere in the debug
+ // info, then we have no way to complete it.
+ return false;
+ }
+
+ TypeIndex field_list_ti = GetFieldListIndex(cvt);
+ CVType field_list_cvt = m_index.tpi().getType(field_list_ti);
+ if (field_list_cvt.kind() != LF_FIELDLIST)
+ return false;
+
+ // Visit all members of this class, then perform any finalization necessary
+ // to complete the class.
+ CompilerType ct = ToCompilerType(tag_qt);
+ UdtRecordCompleter completer(best_ti, ct, tag, *this, m_index.tpi());
+ auto error =
+ llvm::codeview::visitMemberRecordStream(field_list_cvt.data(), completer);
+ completer.complete();
+
+ status.resolved = true;
+ if (!error)
+ return true;
+
+ llvm::consumeError(std::move(error));
+ return false;
+}
+
+clang::QualType PdbAstBuilder::CreateSimpleType(TypeIndex ti) {
+ if (ti == TypeIndex::NullptrT())
+ return GetBasicType(lldb::eBasicTypeNullPtr);
+
+ if (ti.getSimpleMode() != SimpleTypeMode::Direct) {
+ clang::QualType direct_type = GetOrCreateType(ti.makeDirect());
+ return m_clang.getASTContext()->getPointerType(direct_type);
+ }
+
+ if (ti.getSimpleKind() == SimpleTypeKind::NotTranslated)
+ return {};
+
+ lldb::BasicType bt = GetCompilerTypeForSimpleKind(ti.getSimpleKind());
+ if (bt == lldb::eBasicTypeInvalid)
+ return {};
+
+ return GetBasicType(bt);
+}
+
+clang::QualType PdbAstBuilder::CreatePointerType(const PointerRecord &pointer) {
+ clang::QualType pointee_type = GetOrCreateType(pointer.ReferentType);
+
+ // This can happen for pointers to LF_VTSHAPE records, which we shouldn't
+ // create in the AST.
+ if (pointee_type.isNull())
+ return {};
+
+ if (pointer.isPointerToMember()) {
+ MemberPointerInfo mpi = pointer.getMemberInfo();
+ clang::QualType class_type = GetOrCreateType(mpi.ContainingType);
+
+ return m_clang.getASTContext()->getMemberPointerType(
+ pointee_type, class_type.getTypePtr());
+ }
+
+ clang::QualType pointer_type;
+ if (pointer.getMode() == PointerMode::LValueReference)
+ pointer_type =
+ m_clang.getASTContext()->getLValueReferenceType(pointee_type);
+ else if (pointer.getMode() == PointerMode::RValueReference)
+ pointer_type =
+ m_clang.getASTContext()->getRValueReferenceType(pointee_type);
+ else
+ pointer_type = m_clang.getASTContext()->getPointerType(pointee_type);
+
+ if ((pointer.getOptions() & PointerOptions::Const) != PointerOptions::None)
+ pointer_type.addConst();
+
+ if ((pointer.getOptions() & PointerOptions::Volatile) != PointerOptions::None)
+ pointer_type.addVolatile();
+
+ if ((pointer.getOptions() & PointerOptions::Restrict) != PointerOptions::None)
+ pointer_type.addRestrict();
+
+ return pointer_type;
+}
+
+clang::QualType
+PdbAstBuilder::CreateModifierType(const ModifierRecord &modifier) {
+ clang::QualType unmodified_type = GetOrCreateType(modifier.ModifiedType);
+ if (unmodified_type.isNull())
+ return {};
+
+ if ((modifier.Modifiers & ModifierOptions::Const) != ModifierOptions::None)
+ unmodified_type.addConst();
+ if ((modifier.Modifiers & ModifierOptions::Volatile) != ModifierOptions::None)
+ unmodified_type.addVolatile();
+
+ return unmodified_type;
+}
+
+clang::QualType PdbAstBuilder::CreateRecordType(PdbTypeSymId id,
+ const TagRecord &record) {
+ clang::DeclContext *context = nullptr;
+ std::string uname;
+ std::tie(context, uname) = CreateDeclInfoForType(record, id.index);
+ clang::TagTypeKind ttk = TranslateUdtKind(record);
+ lldb::AccessType access =
+ (ttk == clang::TTK_Class) ? lldb::eAccessPrivate : lldb::eAccessPublic;
+
+ ClangASTMetadata metadata;
+ metadata.SetUserID(toOpaqueUid(id));
+ metadata.SetIsDynamicCXXType(false);
+
+ CompilerType ct =
+ m_clang.CreateRecordType(context, access, uname.c_str(), ttk,
+ lldb::eLanguageTypeC_plus_plus, &metadata);
+
+ lldbassert(ct.IsValid());
+
+ ClangASTContext::StartTagDeclarationDefinition(ct);
+
+ // Even if it's possible, don't complete it at this point. Just mark it
+ // forward resolved, and if/when LLDB needs the full definition, it can
+ // ask us.
+ clang::QualType result =
+ clang::QualType::getFromOpaquePtr(ct.GetOpaqueQualType());
+
+ ClangASTContext::SetHasExternalStorage(result.getAsOpaquePtr(), true);
+ return result;
+}
+
+clang::Decl *PdbAstBuilder::TryGetDecl(PdbSymUid uid) const {
+ auto iter = m_uid_to_decl.find(toOpaqueUid(uid));
+ if (iter != m_uid_to_decl.end())
+ return iter->second;
+ return nullptr;
+}
+
+clang::NamespaceDecl *
+PdbAstBuilder::GetOrCreateNamespaceDecl(const char *name,
+ clang::DeclContext &context) {
+ return m_clang.GetUniqueNamespaceDeclaration(
+ IsAnonymousNamespaceName(name) ? nullptr : name, &context);
+}
+
+clang::BlockDecl *
+PdbAstBuilder::GetOrCreateBlockDecl(PdbCompilandSymId block_id) {
+ if (clang::Decl *decl = TryGetDecl(block_id))
+ return llvm::dyn_cast<clang::BlockDecl>(decl);
+
+ clang::DeclContext *scope = GetParentDeclContext(block_id);
+
+ clang::BlockDecl *block_decl = m_clang.CreateBlockDeclaration(scope);
+ m_uid_to_decl.insert({toOpaqueUid(block_id), block_decl});
+
+ DeclStatus status;
+ status.resolved = true;
+ status.uid = toOpaqueUid(block_id);
+ m_decl_to_status.insert({block_decl, status});
+
+ return block_decl;
+}
+
+clang::VarDecl *PdbAstBuilder::CreateVariableDecl(PdbSymUid uid, CVSymbol sym,
+ clang::DeclContext &scope) {
+ VariableInfo var_info = GetVariableNameInfo(sym);
+ clang::QualType qt = GetOrCreateType(var_info.type);
+
+ clang::VarDecl *var_decl = m_clang.CreateVariableDeclaration(
+ &scope, var_info.name.str().c_str(), qt);
+
+ m_uid_to_decl[toOpaqueUid(uid)] = var_decl;
+ DeclStatus status;
+ status.resolved = true;
+ status.uid = toOpaqueUid(uid);
+ m_decl_to_status.insert({var_decl, status});
+ return var_decl;
+}
+
+clang::VarDecl *
+PdbAstBuilder::GetOrCreateVariableDecl(PdbCompilandSymId scope_id,
+ PdbCompilandSymId var_id) {
+ if (clang::Decl *decl = TryGetDecl(var_id))
+ return llvm::dyn_cast<clang::VarDecl>(decl);
+
+ clang::DeclContext *scope = GetOrCreateDeclContextForUid(scope_id);
+
+ CVSymbol sym = m_index.ReadSymbolRecord(var_id);
+ return CreateVariableDecl(PdbSymUid(var_id), sym, *scope);
+}
+
+clang::VarDecl *PdbAstBuilder::GetOrCreateVariableDecl(PdbGlobalSymId var_id) {
+ if (clang::Decl *decl = TryGetDecl(var_id))
+ return llvm::dyn_cast<clang::VarDecl>(decl);
+
+ CVSymbol sym = m_index.ReadSymbolRecord(var_id);
+ auto context = FromCompilerDeclContext(GetTranslationUnitDecl());
+ return CreateVariableDecl(PdbSymUid(var_id), sym, *context);
+}
+
+clang::TypedefNameDecl *
+PdbAstBuilder::GetOrCreateTypedefDecl(PdbGlobalSymId id) {
+ if (clang::Decl *decl = TryGetDecl(id))
+ return llvm::dyn_cast<clang::TypedefNameDecl>(decl);
+
+ CVSymbol sym = m_index.ReadSymbolRecord(id);
+ lldbassert(sym.kind() == S_UDT);
+ UDTSym udt = llvm::cantFail(SymbolDeserializer::deserializeAs<UDTSym>(sym));
+
+ clang::DeclContext *scope = GetParentDeclContext(id);
+
+ PdbTypeSymId real_type_id{udt.Type, false};
+ clang::QualType qt = GetOrCreateType(real_type_id);
+
+ std::string uname = DropNameScope(udt.Name);
+
+ CompilerType ct = m_clang.CreateTypedefType(ToCompilerType(qt), uname.c_str(),
+ ToCompilerDeclContext(*scope));
+ clang::TypedefNameDecl *tnd = m_clang.GetAsTypedefDecl(ct);
+ DeclStatus status;
+ status.resolved = true;
+ status.uid = toOpaqueUid(id);
+ m_decl_to_status.insert({tnd, status});
+ return tnd;
+}
+
+clang::QualType PdbAstBuilder::GetBasicType(lldb::BasicType type) {
+ CompilerType ct = m_clang.GetBasicType(type);
+ return clang::QualType::getFromOpaquePtr(ct.GetOpaqueQualType());
+}
+
+clang::QualType PdbAstBuilder::CreateType(PdbTypeSymId type) {
+ if (type.index.isSimple())
+ return CreateSimpleType(type.index);
+
+ CVType cvt = m_index.tpi().getType(type.index);
+
+ if (cvt.kind() == LF_MODIFIER) {
+ ModifierRecord modifier;
+ llvm::cantFail(
+ TypeDeserializer::deserializeAs<ModifierRecord>(cvt, modifier));
+ return CreateModifierType(modifier);
+ }
+
+ if (cvt.kind() == LF_POINTER) {
+ PointerRecord pointer;
+ llvm::cantFail(
+ TypeDeserializer::deserializeAs<PointerRecord>(cvt, pointer));
+ return CreatePointerType(pointer);
+ }
+
+ if (IsTagRecord(cvt)) {
+ CVTagRecord tag = CVTagRecord::create(cvt);
+ if (tag.kind() == CVTagRecord::Union)
+ return CreateRecordType(type.index, tag.asUnion());
+ if (tag.kind() == CVTagRecord::Enum)
+ return CreateEnumType(type.index, tag.asEnum());
+ return CreateRecordType(type.index, tag.asClass());
+ }
+
+ if (cvt.kind() == LF_ARRAY) {
+ ArrayRecord ar;
+ llvm::cantFail(TypeDeserializer::deserializeAs<ArrayRecord>(cvt, ar));
+ return CreateArrayType(ar);
+ }
+
+ if (cvt.kind() == LF_PROCEDURE) {
+ ProcedureRecord pr;
+ llvm::cantFail(TypeDeserializer::deserializeAs<ProcedureRecord>(cvt, pr));
+ return CreateFunctionType(pr.ArgumentList, pr.ReturnType, pr.CallConv);
+ }
+
+ if (cvt.kind() == LF_MFUNCTION) {
+ MemberFunctionRecord mfr;
+ llvm::cantFail(
+ TypeDeserializer::deserializeAs<MemberFunctionRecord>(cvt, mfr));
+ return CreateFunctionType(mfr.ArgumentList, mfr.ReturnType, mfr.CallConv);
+ }
+
+ return {};
+}
+
+clang::QualType PdbAstBuilder::GetOrCreateType(PdbTypeSymId type) {
+ lldb::user_id_t uid = toOpaqueUid(type);
+ auto iter = m_uid_to_type.find(uid);
+ if (iter != m_uid_to_type.end())
+ return iter->second;
+
+ PdbTypeSymId best_type = GetBestPossibleDecl(type, m_index.tpi());
+
+ clang::QualType qt;
+ if (best_type.index != type.index) {
+ // This is a forward decl. Call GetOrCreate on the full decl, then map the
+ // forward decl id to the full decl QualType.
+ clang::QualType qt = GetOrCreateType(best_type);
+ m_uid_to_type[toOpaqueUid(type)] = qt;
+ return qt;
+ }
+
+ // This is either a full decl, or a forward decl with no matching full decl
+ // in the debug info.
+ qt = CreateType(type);
+ m_uid_to_type[toOpaqueUid(type)] = qt;
+ if (IsTagRecord(type, m_index.tpi())) {
+ clang::TagDecl *tag = qt->getAsTagDecl();
+ lldbassert(m_decl_to_status.count(tag) == 0);
+
+ DeclStatus &status = m_decl_to_status[tag];
+ status.uid = uid;
+ status.resolved = false;
+ }
+ return qt;
+}
+
+clang::FunctionDecl *
+PdbAstBuilder::GetOrCreateFunctionDecl(PdbCompilandSymId func_id) {
+ if (clang::Decl *decl = TryGetDecl(func_id))
+ return llvm::dyn_cast<clang::FunctionDecl>(decl);
+
+ clang::DeclContext *parent = GetParentDeclContext(PdbSymUid(func_id));
+ std::string context_name;
+ if (clang::NamespaceDecl *ns = llvm::dyn_cast<clang::NamespaceDecl>(parent)) {
+ context_name = ns->getQualifiedNameAsString();
+ } else if (clang::TagDecl *tag = llvm::dyn_cast<clang::TagDecl>(parent)) {
+ context_name = tag->getQualifiedNameAsString();
+ }
+
+ CVSymbol cvs = m_index.ReadSymbolRecord(func_id);
+ ProcSym proc(static_cast<SymbolRecordKind>(cvs.kind()));
+ llvm::cantFail(SymbolDeserializer::deserializeAs<ProcSym>(cvs, proc));
+
+ PdbTypeSymId type_id(proc.FunctionType);
+ clang::QualType qt = GetOrCreateType(type_id);
+ if (qt.isNull())
+ return nullptr;
+
+ clang::StorageClass storage = clang::SC_None;
+ if (proc.Kind == SymbolRecordKind::ProcSym)
+ storage = clang::SC_Static;
+
+ const clang::FunctionProtoType *func_type =
+ llvm::dyn_cast<clang::FunctionProtoType>(qt);
+
+ CompilerType func_ct = ToCompilerType(qt);
+
+ llvm::StringRef proc_name = proc.Name;
+ proc_name.consume_front(context_name);
+ proc_name.consume_front("::");
+
+ clang::FunctionDecl *function_decl = m_clang.CreateFunctionDeclaration(
+ parent, proc_name.str().c_str(), func_ct, storage, false);
+
+ lldbassert(m_uid_to_decl.count(toOpaqueUid(func_id)) == 0);
+ m_uid_to_decl[toOpaqueUid(func_id)] = function_decl;
+ DeclStatus status;
+ status.resolved = true;
+ status.uid = toOpaqueUid(func_id);
+ m_decl_to_status.insert({function_decl, status});
+
+ CreateFunctionParameters(func_id, *function_decl, func_type->getNumParams());
+
+ return function_decl;
+}
+
+void PdbAstBuilder::CreateFunctionParameters(PdbCompilandSymId func_id,
+ clang::FunctionDecl &function_decl,
+ uint32_t param_count) {
+ CompilandIndexItem *cii = m_index.compilands().GetCompiland(func_id.modi);
+ CVSymbolArray scope =
+ cii->m_debug_stream.getSymbolArrayForScope(func_id.offset);
+
+ auto begin = scope.begin();
+ auto end = scope.end();
+ std::vector<clang::ParmVarDecl *> params;
+ while (begin != end && param_count > 0) {
+ uint32_t record_offset = begin.offset();
+ CVSymbol sym = *begin++;
+
+ TypeIndex param_type;
+ llvm::StringRef param_name;
+ switch (sym.kind()) {
+ case S_REGREL32: {
+ RegRelativeSym reg(SymbolRecordKind::RegRelativeSym);
+ cantFail(SymbolDeserializer::deserializeAs<RegRelativeSym>(sym, reg));
+ param_type = reg.Type;
+ param_name = reg.Name;
+ break;
+ }
+ case S_REGISTER: {
+ RegisterSym reg(SymbolRecordKind::RegisterSym);
+ cantFail(SymbolDeserializer::deserializeAs<RegisterSym>(sym, reg));
+ param_type = reg.Index;
+ param_name = reg.Name;
+ break;
+ }
+ case S_LOCAL: {
+ LocalSym local(SymbolRecordKind::LocalSym);
+ cantFail(SymbolDeserializer::deserializeAs<LocalSym>(sym, local));
+ if ((local.Flags & LocalSymFlags::IsParameter) == LocalSymFlags::None)
+ continue;
+ param_type = local.Type;
+ param_name = local.Name;
+ break;
+ }
+ case S_BLOCK32:
+ // All parameters should come before the first block. If that isn't the
+ // case, then perhaps this is bad debug info that doesn't contain
+ // information about all parameters.
+ return;
+ default:
+ continue;
+ }
+
+ PdbCompilandSymId param_uid(func_id.modi, record_offset);
+ clang::QualType qt = GetOrCreateType(param_type);
+
+ CompilerType param_type_ct(&m_clang, qt.getAsOpaquePtr());
+ clang::ParmVarDecl *param = m_clang.CreateParameterDeclaration(
+ &function_decl, param_name.str().c_str(), param_type_ct,
+ clang::SC_None);
+ lldbassert(m_uid_to_decl.count(toOpaqueUid(param_uid)) == 0);
+
+ m_uid_to_decl[toOpaqueUid(param_uid)] = param;
+ params.push_back(param);
+ --param_count;
+ }
+
+ if (!params.empty())
+ m_clang.SetFunctionParameters(&function_decl, params.data(), params.size());
+}
+
+clang::QualType PdbAstBuilder::CreateEnumType(PdbTypeSymId id,
+ const EnumRecord &er) {
+ clang::DeclContext *decl_context = nullptr;
+ std::string uname;
+ std::tie(decl_context, uname) = CreateDeclInfoForType(er, id.index);
+ clang::QualType underlying_type = GetOrCreateType(er.UnderlyingType);
+
+ Declaration declaration;
+ CompilerType enum_ct = m_clang.CreateEnumerationType(
+ uname.c_str(), decl_context, declaration, ToCompilerType(underlying_type),
+ er.isScoped());
+
+ ClangASTContext::StartTagDeclarationDefinition(enum_ct);
+ ClangASTContext::SetHasExternalStorage(enum_ct.GetOpaqueQualType(), true);
+
+ return clang::QualType::getFromOpaquePtr(enum_ct.GetOpaqueQualType());
+}
+
+clang::QualType PdbAstBuilder::CreateArrayType(const ArrayRecord &ar) {
+ clang::QualType element_type = GetOrCreateType(ar.ElementType);
+
+ uint64_t element_count =
+ ar.Size / GetSizeOfType({ar.ElementType}, m_index.tpi());
+
+ CompilerType array_ct = m_clang.CreateArrayType(ToCompilerType(element_type),
+ element_count, false);
+ return clang::QualType::getFromOpaquePtr(array_ct.GetOpaqueQualType());
+}
+
+clang::QualType PdbAstBuilder::CreateFunctionType(
+ TypeIndex args_type_idx, TypeIndex return_type_idx,
+ llvm::codeview::CallingConvention calling_convention) {
+ TpiStream &stream = m_index.tpi();
+ CVType args_cvt = stream.getType(args_type_idx);
+ ArgListRecord args;
+ llvm::cantFail(
+ TypeDeserializer::deserializeAs<ArgListRecord>(args_cvt, args));
+
+ llvm::ArrayRef<TypeIndex> arg_indices = llvm::makeArrayRef(args.ArgIndices);
+ bool is_variadic = IsCVarArgsFunction(arg_indices);
+ if (is_variadic)
+ arg_indices = arg_indices.drop_back();
+
+ std::vector<CompilerType> arg_types;
+ arg_types.reserve(arg_indices.size());
+
+ for (TypeIndex arg_index : arg_indices) {
+ clang::QualType arg_type = GetOrCreateType(arg_index);
+ arg_types.push_back(ToCompilerType(arg_type));
+ }
+
+ clang::QualType return_type = GetOrCreateType(return_type_idx);
+
+ llvm::Optional<clang::CallingConv> cc =
+ TranslateCallingConvention(calling_convention);
+ if (!cc)
+ return {};
+
+ CompilerType return_ct = ToCompilerType(return_type);
+ CompilerType func_sig_ast_type = m_clang.CreateFunctionType(
+ return_ct, arg_types.data(), arg_types.size(), is_variadic, 0, *cc);
+
+ return clang::QualType::getFromOpaquePtr(
+ func_sig_ast_type.GetOpaqueQualType());
+}
+
+static bool isTagDecl(clang::DeclContext &context) {
+ return !!llvm::dyn_cast<clang::TagDecl>(&context);
+}
+
+static bool isFunctionDecl(clang::DeclContext &context) {
+ return !!llvm::dyn_cast<clang::FunctionDecl>(&context);
+}
+
+static bool isBlockDecl(clang::DeclContext &context) {
+ return !!llvm::dyn_cast<clang::BlockDecl>(&context);
+}
+
+void PdbAstBuilder::ParseAllNamespacesPlusChildrenOf(
+ llvm::Optional<llvm::StringRef> parent) {
+ TypeIndex ti{m_index.tpi().TypeIndexBegin()};
+ for (const CVType &cvt : m_index.tpi().typeArray()) {
+ PdbTypeSymId tid{ti};
+ ++ti;
+
+ if (!IsTagRecord(cvt))
+ continue;
+
+ CVTagRecord tag = CVTagRecord::create(cvt);
+
+ if (!parent.hasValue()) {
+ clang::QualType qt = GetOrCreateType(tid);
+ CompleteType(qt);
+ continue;
+ }
+
+ // Call CreateDeclInfoForType unconditionally so that the namespace info
+ // gets created. But only call CreateRecordType if the namespace name
+ // matches.
+ clang::DeclContext *context = nullptr;
+ std::string uname;
+ std::tie(context, uname) = CreateDeclInfoForType(tag.asTag(), tid.index);
+ if (!context->isNamespace())
+ continue;
+
+ clang::NamespaceDecl *ns = llvm::dyn_cast<clang::NamespaceDecl>(context);
+ std::string actual_ns = ns->getQualifiedNameAsString();
+ if (llvm::StringRef(actual_ns).startswith(*parent)) {
+ clang::QualType qt = GetOrCreateType(tid);
+ CompleteType(qt);
+ continue;
+ }
+ }
+
+ uint32_t module_count = m_index.dbi().modules().getModuleCount();
+ for (uint16_t modi = 0; modi < module_count; ++modi) {
+ CompilandIndexItem &cii = m_index.compilands().GetOrCreateCompiland(modi);
+ const CVSymbolArray &symbols = cii.m_debug_stream.getSymbolArray();
+ auto iter = symbols.begin();
+ while (iter != symbols.end()) {
+ PdbCompilandSymId sym_id{modi, iter.offset()};
+
+ switch (iter->kind()) {
+ case S_GPROC32:
+ case S_LPROC32:
+ GetOrCreateFunctionDecl(sym_id);
+ iter = symbols.at(getScopeEndOffset(*iter));
+ break;
+ case S_GDATA32:
+ case S_GTHREAD32:
+ case S_LDATA32:
+ case S_LTHREAD32:
+ GetOrCreateVariableDecl(PdbCompilandSymId(modi, 0), sym_id);
+ ++iter;
+ break;
+ default:
+ ++iter;
+ continue;
+ }
+ }
+ }
+}
+
+static CVSymbolArray skipFunctionParameters(clang::Decl &decl,
+ const CVSymbolArray &symbols) {
+ clang::FunctionDecl *func_decl = llvm::dyn_cast<clang::FunctionDecl>(&decl);
+ if (!func_decl)
+ return symbols;
+ unsigned int params = func_decl->getNumParams();
+ if (params == 0)
+ return symbols;
+
+ CVSymbolArray result = symbols;
+
+ while (!result.empty()) {
+ if (params == 0)
+ return result;
+
+ CVSymbol sym = *result.begin();
+ result.drop_front();
+
+ if (!isLocalVariableType(sym.kind()))
+ continue;
+
+ --params;
+ }
+ return result;
+}
+
+void PdbAstBuilder::ParseBlockChildren(PdbCompilandSymId block_id) {
+ CVSymbol sym = m_index.ReadSymbolRecord(block_id);
+ lldbassert(sym.kind() == S_GPROC32 || sym.kind() == S_LPROC32 ||
+ sym.kind() == S_BLOCK32);
+ CompilandIndexItem &cii =
+ m_index.compilands().GetOrCreateCompiland(block_id.modi);
+ CVSymbolArray symbols =
+ cii.m_debug_stream.getSymbolArrayForScope(block_id.offset);
+
+ // Function parameters should already have been created when the function was
+ // parsed.
+ if (sym.kind() == S_GPROC32 || sym.kind() == S_LPROC32)
+ symbols =
+ skipFunctionParameters(*m_uid_to_decl[toOpaqueUid(block_id)], symbols);
+
+ auto begin = symbols.begin();
+ while (begin != symbols.end()) {
+ PdbCompilandSymId child_sym_id(block_id.modi, begin.offset());
+ GetOrCreateSymbolForId(child_sym_id);
+ if (begin->kind() == S_BLOCK32) {
+ ParseBlockChildren(child_sym_id);
+ begin = symbols.at(getScopeEndOffset(*begin));
+ }
+ ++begin;
+ }
+}
+
+void PdbAstBuilder::ParseDeclsForSimpleContext(clang::DeclContext &context) {
+
+ clang::Decl *decl = clang::Decl::castFromDeclContext(&context);
+ lldbassert(decl);
+
+ auto iter = m_decl_to_status.find(decl);
+ lldbassert(iter != m_decl_to_status.end());
+
+ if (auto *tag = llvm::dyn_cast<clang::TagDecl>(&context)) {
+ CompleteTagDecl(*tag);
+ return;
+ }
+
+ if (isFunctionDecl(context) || isBlockDecl(context)) {
+ PdbCompilandSymId block_id = PdbSymUid(iter->second.uid).asCompilandSym();
+ ParseBlockChildren(block_id);
+ }
+}
+
+void PdbAstBuilder::ParseDeclsForContext(clang::DeclContext &context) {
+ // Namespaces aren't explicitly represented in the debug info, and the only
+ // way to parse them is to parse all type info, demangling every single type
+ // and trying to reconstruct the DeclContext hierarchy this way. Since this
+ // is an expensive operation, we have to special case it so that we do other
+ // work (such as parsing the items that appear within the namespaces) at the
+ // same time.
+ if (context.isTranslationUnit()) {
+ ParseAllNamespacesPlusChildrenOf(llvm::None);
+ return;
+ }
+
+ if (context.isNamespace()) {
+ clang::NamespaceDecl &ns = *llvm::dyn_cast<clang::NamespaceDecl>(&context);
+ std::string qname = ns.getQualifiedNameAsString();
+ ParseAllNamespacesPlusChildrenOf(llvm::StringRef{qname});
+ return;
+ }
+
+ if (isTagDecl(context) || isFunctionDecl(context) || isBlockDecl(context)) {
+ ParseDeclsForSimpleContext(context);
+ return;
+ }
+}
+
+CompilerDecl PdbAstBuilder::ToCompilerDecl(clang::Decl &decl) {
+ return {&m_clang, &decl};
+}
+
+CompilerType PdbAstBuilder::ToCompilerType(clang::QualType qt) {
+ return {&m_clang, qt.getAsOpaquePtr()};
+}
+
+CompilerDeclContext
+PdbAstBuilder::ToCompilerDeclContext(clang::DeclContext &context) {
+ return {&m_clang, &context};
+}
+
+clang::Decl * PdbAstBuilder::FromCompilerDecl(CompilerDecl decl) {
+ return static_cast<clang::Decl *>(decl.GetOpaqueDecl());
+}
+
+clang::DeclContext *
+PdbAstBuilder::FromCompilerDeclContext(CompilerDeclContext context) {
+ return static_cast<clang::DeclContext *>(context.GetOpaqueDeclContext());
+}
+
+void PdbAstBuilder::Dump(Stream &stream) { m_clang.Dump(stream); }
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h
new file mode 100644
index 000000000000..67d024741e0d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h
@@ -0,0 +1,144 @@
+//===-- PdbAstBuilder.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_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBASTBUILDER_H
+#define LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBASTBUILDER_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringRef.h"
+
+#include "lldb/Symbol/ClangASTImporter.h"
+
+#include "PdbIndex.h"
+#include "PdbSymUid.h"
+
+namespace clang {
+class TagDecl;
+class DeclContext;
+class Decl;
+class QualType;
+class FunctionDecl;
+class NamespaceDecl;
+} // namespace clang
+
+namespace llvm {
+namespace codeview {
+class ProcSym;
+}
+} // namespace llvm
+
+namespace lldb_private {
+class ClangASTImporter;
+class ObjectFile;
+
+namespace npdb {
+class PdbIndex;
+struct VariableInfo;
+
+struct DeclStatus {
+ DeclStatus() = default;
+ DeclStatus(lldb::user_id_t uid, bool resolved)
+ : uid(uid), resolved(resolved) {}
+ lldb::user_id_t uid = 0;
+ bool resolved = false;
+};
+
+class PdbAstBuilder {
+public:
+ // Constructors and Destructors
+ PdbAstBuilder(ObjectFile &obj, PdbIndex &index);
+
+ lldb_private::CompilerDeclContext GetTranslationUnitDecl();
+
+ clang::Decl *GetOrCreateDeclForUid(PdbSymUid uid);
+ clang::DeclContext *GetOrCreateDeclContextForUid(PdbSymUid uid);
+ clang::DeclContext *GetParentDeclContext(PdbSymUid uid);
+
+ clang::FunctionDecl *GetOrCreateFunctionDecl(PdbCompilandSymId func_id);
+ clang::BlockDecl *GetOrCreateBlockDecl(PdbCompilandSymId block_id);
+ clang::VarDecl *GetOrCreateVariableDecl(PdbCompilandSymId scope_id,
+ PdbCompilandSymId var_id);
+ clang::VarDecl *GetOrCreateVariableDecl(PdbGlobalSymId var_id);
+ clang::TypedefNameDecl *GetOrCreateTypedefDecl(PdbGlobalSymId id);
+ void ParseDeclsForContext(clang::DeclContext &context);
+
+ clang::QualType GetBasicType(lldb::BasicType type);
+ clang::QualType GetOrCreateType(PdbTypeSymId type);
+
+ bool CompleteTagDecl(clang::TagDecl &tag);
+ bool CompleteType(clang::QualType qt);
+
+ CompilerDecl ToCompilerDecl(clang::Decl &decl);
+ CompilerType ToCompilerType(clang::QualType qt);
+ CompilerDeclContext ToCompilerDeclContext(clang::DeclContext &context);
+ clang::Decl * FromCompilerDecl(CompilerDecl decl);
+ clang::DeclContext *FromCompilerDeclContext(CompilerDeclContext context);
+
+ ClangASTContext &clang() { return m_clang; }
+ ClangASTImporter &importer() { return m_importer; }
+
+ void Dump(Stream &stream);
+
+private:
+ clang::Decl *TryGetDecl(PdbSymUid uid) const;
+
+ using TypeIndex = llvm::codeview::TypeIndex;
+
+ clang::QualType
+ CreatePointerType(const llvm::codeview::PointerRecord &pointer);
+ clang::QualType
+ CreateModifierType(const llvm::codeview::ModifierRecord &modifier);
+ clang::QualType CreateArrayType(const llvm::codeview::ArrayRecord &array);
+ clang::QualType CreateRecordType(PdbTypeSymId id,
+ const llvm::codeview::TagRecord &record);
+ clang::QualType CreateEnumType(PdbTypeSymId id,
+ const llvm::codeview::EnumRecord &record);
+ clang::QualType
+ CreateFunctionType(TypeIndex args_type_idx, TypeIndex return_type_idx,
+ llvm::codeview::CallingConvention calling_convention);
+ clang::QualType CreateType(PdbTypeSymId type);
+
+ void CreateFunctionParameters(PdbCompilandSymId func_id,
+ clang::FunctionDecl &function_decl,
+ uint32_t param_count);
+ clang::Decl *GetOrCreateSymbolForId(PdbCompilandSymId id);
+ clang::VarDecl *CreateVariableDecl(PdbSymUid uid,
+ llvm::codeview::CVSymbol sym,
+ clang::DeclContext &scope);
+ clang::DeclContext *
+ GetParentDeclContextForSymbol(const llvm::codeview::CVSymbol &sym);
+
+ clang::NamespaceDecl *GetOrCreateNamespaceDecl(const char *name,
+ clang::DeclContext &context);
+
+ void ParseAllNamespacesPlusChildrenOf(llvm::Optional<llvm::StringRef> parent);
+ void ParseDeclsForSimpleContext(clang::DeclContext &context);
+ void ParseBlockChildren(PdbCompilandSymId block_id);
+
+ void BuildParentMap();
+ std::pair<clang::DeclContext *, std::string>
+ CreateDeclInfoForType(const llvm::codeview::TagRecord &record, TypeIndex ti);
+ std::pair<clang::DeclContext *, std::string>
+ CreateDeclInfoForUndecoratedName(llvm::StringRef uname);
+ clang::QualType CreateSimpleType(TypeIndex ti);
+
+ PdbIndex &m_index;
+ ClangASTContext &m_clang;
+
+ ClangASTImporter m_importer;
+
+ llvm::DenseMap<TypeIndex, TypeIndex> m_parent_types;
+ llvm::DenseMap<clang::Decl *, DeclStatus> m_decl_to_status;
+ llvm::DenseMap<lldb::user_id_t, clang::Decl *> m_uid_to_decl;
+ llvm::DenseMap<lldb::user_id_t, clang::QualType> m_uid_to_type;
+};
+
+} // namespace npdb
+} // namespace lldb_private
+
+#endif // lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.cpp
new file mode 100644
index 000000000000..79dd010ff311
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.cpp
@@ -0,0 +1,137 @@
+//===-- PdbFPOProgramToDWARFExpression.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 "PdbFPOProgramToDWARFExpression.h"
+#include "CodeViewRegisterMapping.h"
+
+#include "lldb/Core/StreamBuffer.h"
+#include "lldb/Symbol/PostfixExpression.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Stream.h"
+#include "llvm/ADT/DenseMap.h"
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/DebugInfo/CodeView/CodeView.h"
+#include "llvm/DebugInfo/CodeView/EnumTables.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::postfix;
+
+static uint32_t ResolveLLDBRegisterNum(llvm::StringRef reg_name, llvm::Triple::ArchType arch_type) {
+ // lookup register name to get lldb register number
+ llvm::codeview::CPUType cpu_type;
+ switch (arch_type) {
+ case llvm::Triple::ArchType::aarch64:
+ cpu_type = llvm::codeview::CPUType::ARM64;
+ break;
+
+ default:
+ cpu_type = llvm::codeview::CPUType::X64;
+ break;
+ }
+
+ llvm::ArrayRef<llvm::EnumEntry<uint16_t>> register_names =
+ llvm::codeview::getRegisterNames(cpu_type);
+ auto it = llvm::find_if(
+ register_names,
+ [&reg_name](const llvm::EnumEntry<uint16_t> &register_entry) {
+ return reg_name.compare_lower(register_entry.Name) == 0;
+ });
+
+ if (it == register_names.end())
+ return LLDB_INVALID_REGNUM;
+
+ auto reg_id = static_cast<llvm::codeview::RegisterId>(it->Value);
+ return npdb::GetLLDBRegisterNumber(arch_type, reg_id);
+}
+
+static bool ParseFPOSingleAssignmentProgram(llvm::StringRef program,
+ llvm::BumpPtrAllocator &alloc,
+ llvm::StringRef &register_name,
+ Node *&ast) {
+ // lvalue of assignment is always first token
+ // rvalue program goes next
+ std::tie(register_name, program) = getToken(program);
+ if (register_name.empty())
+ return false;
+
+ ast = Parse(program, alloc);
+ return ast != nullptr;
+}
+
+static Node *ParseFPOProgram(llvm::StringRef program,
+ llvm::StringRef register_name,
+ llvm::Triple::ArchType arch_type,
+ llvm::BumpPtrAllocator &alloc) {
+ llvm::DenseMap<llvm::StringRef, Node *> dependent_programs;
+
+ size_t cur = 0;
+ while (true) {
+ size_t assign_index = program.find('=', cur);
+ if (assign_index == llvm::StringRef::npos) {
+ llvm::StringRef tail = program.slice(cur, llvm::StringRef::npos);
+ if (!tail.trim().empty()) {
+ // missing assign operator
+ return nullptr;
+ }
+ break;
+ }
+ llvm::StringRef assignment_program = program.slice(cur, assign_index);
+
+ llvm::StringRef lvalue_name;
+ Node *rvalue_ast = nullptr;
+ if (!ParseFPOSingleAssignmentProgram(assignment_program, alloc, lvalue_name,
+ rvalue_ast)) {
+ return nullptr;
+ }
+
+ lldbassert(rvalue_ast);
+
+ // Emplace valid dependent subtrees to make target assignment independent
+ // from predecessors. Resolve all other SymbolNodes as registers.
+ bool success =
+ ResolveSymbols(rvalue_ast, [&](SymbolNode &symbol) -> Node * {
+ if (Node *node = dependent_programs.lookup(symbol.GetName()))
+ return node;
+ uint32_t reg_num =
+ ResolveLLDBRegisterNum(symbol.GetName().drop_front(1), arch_type);
+
+ if (reg_num == LLDB_INVALID_REGNUM)
+ return nullptr;
+
+ return MakeNode<RegisterNode>(alloc, reg_num);
+ });
+ if (!success)
+ return nullptr;
+
+ if (lvalue_name == register_name) {
+ // found target assignment program - no need to parse further
+ return rvalue_ast;
+ }
+
+ dependent_programs[lvalue_name] = rvalue_ast;
+ cur = assign_index + 1;
+ }
+
+ return nullptr;
+}
+
+bool lldb_private::npdb::TranslateFPOProgramToDWARFExpression(
+ llvm::StringRef program, llvm::StringRef register_name,
+ llvm::Triple::ArchType arch_type, Stream &stream) {
+ llvm::BumpPtrAllocator node_alloc;
+ Node *target_program =
+ ParseFPOProgram(program, register_name, arch_type, node_alloc);
+ if (target_program == nullptr) {
+ return false;
+ }
+
+ ToDWARF(*target_program, stream);
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.h
new file mode 100644
index 000000000000..107e26fb04cb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.h
@@ -0,0 +1,28 @@
+//===-- PdbFPOProgramToDWARFExpression.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_Plugins_SymbolFile_PDB_PDBFPOProgramToDWARFExpression_h_
+#define lldb_Plugins_SymbolFile_PDB_PDBFPOProgramToDWARFExpression_h_
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Triple.h"
+
+namespace lldb_private {
+class Stream;
+
+namespace npdb {
+
+bool TranslateFPOProgramToDWARFExpression(llvm::StringRef program,
+ llvm::StringRef register_name,
+ llvm::Triple::ArchType arch_type,
+ lldb_private::Stream &stream);
+
+} // namespace npdb
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp
new file mode 100644
index 000000000000..ba9a95b16e18
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp
@@ -0,0 +1,196 @@
+//===-- PdbIndex.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 "PdbIndex.h"
+#include "PdbUtil.h"
+
+#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
+#include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/PublicsStream.h"
+#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Support/Error.h"
+
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/lldb-defines.h"
+
+using namespace lldb_private;
+using namespace lldb_private::npdb;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+PdbIndex::PdbIndex() : m_cus(*this), m_va_to_modi(m_allocator) {}
+
+#define ASSIGN_PTR_OR_RETURN(result_ptr, expr) \
+ { \
+ auto expected_result = expr; \
+ if (!expected_result) \
+ return expected_result.takeError(); \
+ result_ptr = &expected_result.get(); \
+ }
+
+llvm::Expected<std::unique_ptr<PdbIndex>>
+PdbIndex::create(std::unique_ptr<llvm::pdb::PDBFile> file) {
+ lldbassert(file);
+
+ std::unique_ptr<PdbIndex> result(new PdbIndex());
+ ASSIGN_PTR_OR_RETURN(result->m_dbi, file->getPDBDbiStream());
+ ASSIGN_PTR_OR_RETURN(result->m_tpi, file->getPDBTpiStream());
+ ASSIGN_PTR_OR_RETURN(result->m_ipi, file->getPDBIpiStream());
+ ASSIGN_PTR_OR_RETURN(result->m_info, file->getPDBInfoStream());
+ ASSIGN_PTR_OR_RETURN(result->m_publics, file->getPDBPublicsStream());
+ ASSIGN_PTR_OR_RETURN(result->m_globals, file->getPDBGlobalsStream());
+ ASSIGN_PTR_OR_RETURN(result->m_symrecords, file->getPDBSymbolStream());
+
+ result->m_tpi->buildHashMap();
+
+ result->m_file = std::move(file);
+
+ return std::move(result);
+}
+
+lldb::addr_t PdbIndex::MakeVirtualAddress(uint16_t segment,
+ uint32_t offset) const {
+ // Segment indices are 1-based.
+ lldbassert(segment > 0);
+
+ uint32_t max_section = dbi().getSectionHeaders().size();
+ lldbassert(segment <= max_section + 1);
+
+ // If this is an absolute symbol, it's indicated by the magic section index
+ // |max_section+1|. In this case, the offset is meaningless, so just return.
+ if (segment == max_section + 1)
+ return LLDB_INVALID_ADDRESS;
+
+ const llvm::object::coff_section &cs = dbi().getSectionHeaders()[segment - 1];
+ return m_load_address + static_cast<lldb::addr_t>(cs.VirtualAddress) +
+ static_cast<lldb::addr_t>(offset);
+}
+
+lldb::addr_t PdbIndex::MakeVirtualAddress(const SegmentOffset &so) const {
+ return MakeVirtualAddress(so.segment, so.offset);
+}
+
+llvm::Optional<uint16_t>
+PdbIndex::GetModuleIndexForAddr(uint16_t segment, uint32_t offset) const {
+ return GetModuleIndexForVa(MakeVirtualAddress(segment, offset));
+}
+
+llvm::Optional<uint16_t> PdbIndex::GetModuleIndexForVa(lldb::addr_t va) const {
+ auto iter = m_va_to_modi.find(va);
+ if (iter == m_va_to_modi.end())
+ return llvm::None;
+
+ return iter.value();
+}
+
+void PdbIndex::ParseSectionContribs() {
+ class Visitor : public ISectionContribVisitor {
+ PdbIndex &m_ctx;
+ llvm::IntervalMap<uint64_t, uint16_t> &m_imap;
+
+ public:
+ Visitor(PdbIndex &ctx, llvm::IntervalMap<uint64_t, uint16_t> &imap)
+ : m_ctx(ctx), m_imap(imap) {}
+
+ void visit(const SectionContrib &C) override {
+ if (C.Size == 0)
+ return;
+
+ uint64_t va = m_ctx.MakeVirtualAddress(C.ISect, C.Off);
+ uint64_t end = va + C.Size;
+ // IntervalMap's start and end represent a closed range, not a half-open
+ // range, so we have to subtract 1.
+ m_imap.insert(va, end - 1, C.Imod);
+ }
+ void visit(const SectionContrib2 &C) override { visit(C.Base); }
+ };
+ Visitor v(*this, m_va_to_modi);
+ dbi().visitSectionContributions(v);
+}
+
+void PdbIndex::BuildAddrToSymbolMap(CompilandIndexItem &cci) {
+ lldbassert(cci.m_symbols_by_va.empty() &&
+ "Addr to symbol map is already built!");
+ uint16_t modi = cci.m_id.modi;
+ const CVSymbolArray &syms = cci.m_debug_stream.getSymbolArray();
+ for (auto iter = syms.begin(); iter != syms.end(); ++iter) {
+ if (!SymbolHasAddress(*iter))
+ continue;
+
+ SegmentOffset so = GetSegmentAndOffset(*iter);
+ lldb::addr_t va = MakeVirtualAddress(so);
+
+ PdbCompilandSymId cu_sym_id(modi, iter.offset());
+
+ // It's rare, but we could have multiple symbols with the same address
+ // because of identical comdat folding. Right now, the first one will win.
+ cci.m_symbols_by_va.insert(std::make_pair(va, PdbSymUid(cu_sym_id)));
+ }
+}
+
+std::vector<SymbolAndUid> PdbIndex::FindSymbolsByVa(lldb::addr_t va) {
+ std::vector<SymbolAndUid> result;
+
+ llvm::Optional<uint16_t> modi = GetModuleIndexForVa(va);
+ if (!modi)
+ return result;
+
+ CompilandIndexItem &cci = compilands().GetOrCreateCompiland(*modi);
+ if (cci.m_symbols_by_va.empty())
+ BuildAddrToSymbolMap(cci);
+
+ // The map is sorted by starting address of the symbol. So for example
+ // we could (in theory) have this situation
+ //
+ // [------------------]
+ // [----------]
+ // [-----------]
+ // [-------------]
+ // [----]
+ // [-----]
+ // ^ Address we're searching for
+ // In order to find this, we use the upper_bound of the key value which would
+ // be the first symbol whose starting address is higher than the element we're
+ // searching for.
+
+ auto ub = cci.m_symbols_by_va.upper_bound(va);
+
+ for (auto iter = cci.m_symbols_by_va.begin(); iter != ub; ++iter) {
+ PdbCompilandSymId cu_sym_id = iter->second.asCompilandSym();
+ CVSymbol sym = ReadSymbolRecord(cu_sym_id);
+
+ SegmentOffsetLength sol;
+ if (SymbolIsCode(sym))
+ sol = GetSegmentOffsetAndLength(sym);
+ else
+ sol.so = GetSegmentAndOffset(sym);
+
+ lldb::addr_t start = MakeVirtualAddress(sol.so);
+ lldb::addr_t end = start + sol.length;
+ if (va >= start && va < end)
+ result.push_back({std::move(sym), iter->second});
+ }
+
+ return result;
+}
+
+CVSymbol PdbIndex::ReadSymbolRecord(PdbCompilandSymId cu_sym) const {
+ const CompilandIndexItem *cci = compilands().GetCompiland(cu_sym.modi);
+ auto iter = cci->m_debug_stream.getSymbolArray().at(cu_sym.offset);
+ lldbassert(iter != cci->m_debug_stream.getSymbolArray().end());
+ return *iter;
+}
+
+CVSymbol PdbIndex::ReadSymbolRecord(PdbGlobalSymId global) const {
+ return symrecords().readRecord(global.offset);
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.h
new file mode 100644
index 000000000000..b30e7870bbdb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.h
@@ -0,0 +1,162 @@
+//===-- PdbIndex.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_PLUGINS_SYMBOLFILENATIVEPDB_PDBINDEX_H
+#define LLDB_PLUGINS_SYMBOLFILENATIVEPDB_PDBINDEX_H
+
+#include "lldb/lldb-types.h"
+#include "llvm/ADT/IntervalMap.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/PDBTypes.h"
+
+#include "CompileUnitIndex.h"
+#include "PdbSymUid.h"
+
+#include <map>
+#include <memory>
+
+namespace llvm {
+namespace pdb {
+class DbiStream;
+class TpiStream;
+class TpiStream;
+class InfoStream;
+class PublicsStream;
+class GlobalsStream;
+class SymbolStream;
+} // namespace pdb
+} // namespace llvm
+
+namespace lldb_private {
+namespace npdb {
+struct SegmentOffset;
+
+/// PdbIndex - Lazy access to the important parts of a PDB file.
+///
+/// This is a layer on top of LLVM's native PDB support libraries which cache
+/// certain data when it is accessed the first time. The entire PDB file is
+/// mapped into memory, and the underlying support libraries vend out memory
+/// that is always backed by the file, so it is safe to hold StringRefs and
+/// ArrayRefs into the backing memory as long as the PdbIndex instance is
+/// alive.
+class PdbIndex {
+
+ /// The underlying PDB file.
+ std::unique_ptr<llvm::pdb::PDBFile> m_file;
+
+ /// The DBI stream. This contains general high level information about the
+ /// features present in the PDB file, compile units (such as the information
+ /// necessary to locate full symbol information for each compile unit),
+ /// section contributions, and other data which is not specifically symbol or
+ /// type records.
+ llvm::pdb::DbiStream *m_dbi = nullptr;
+
+ /// TPI (types) and IPI (indices) streams. These are both in the exact same
+ /// format with different data. Most type records are stored in the TPI
+ /// stream but certain specific types of records are stored in the IPI stream.
+ /// The IPI stream records can refer to the records in the TPI stream, but not
+ /// the other way around.
+ llvm::pdb::TpiStream *m_tpi = nullptr;
+ llvm::pdb::TpiStream *m_ipi = nullptr;
+
+ /// This is called the "PDB Stream" in the Microsoft reference implementation.
+ /// It contains information about the structure of the file, as well as fields
+ /// used to match EXE and PDB.
+ llvm::pdb::InfoStream *m_info = nullptr;
+
+ /// Publics stream. Is actually a serialized hash table where the keys are
+ /// addresses of symbols in the executable, and values are a record containing
+ /// mangled names and an index which can be used to locate more detailed info
+ /// about the symbol in the Symbol Records stream. The publics stream only
+ /// contains info about externally visible symbols.
+ llvm::pdb::PublicsStream *m_publics = nullptr;
+
+ /// Globals stream. Contrary to its name, this does not contain information
+ /// about all "global variables" or "global functions". Rather, it is the
+ /// "global symbol table", i.e. it contains information about *every* symbol
+ /// in the executable. It is a hash table keyed on name, whose values are
+ /// indices into the symbol records stream to find the full record.
+ llvm::pdb::GlobalsStream *m_globals = nullptr;
+
+ /// Symbol records stream. The publics and globals stream refer to records
+ /// in this stream. For some records, like constants and typedefs, the
+ /// complete record lives in this stream. For other symbol types, such as
+ /// functions, data, and other things that have been materialied into a
+ /// specific compile unit, the records here simply provide a reference
+ /// necessary to locate the full information.
+ llvm::pdb::SymbolStream *m_symrecords = nullptr;
+
+ /// Index of all compile units, mapping identifier to |CompilandIndexItem|
+ /// instance.
+ CompileUnitIndex m_cus;
+
+ /// An allocator for the interval maps
+ llvm::IntervalMap<lldb::addr_t, uint32_t>::Allocator m_allocator;
+
+ /// Maps virtual address to module index
+ llvm::IntervalMap<lldb::addr_t, uint16_t> m_va_to_modi;
+
+ /// The address at which the program has been loaded into memory.
+ lldb::addr_t m_load_address = 0;
+
+ PdbIndex();
+
+ void BuildAddrToSymbolMap(CompilandIndexItem &cci);
+
+public:
+ static llvm::Expected<std::unique_ptr<PdbIndex>>
+ create(std::unique_ptr<llvm::pdb::PDBFile>);
+
+ void SetLoadAddress(lldb::addr_t addr) { m_load_address = addr; }
+ lldb::addr_t GetLoadAddress() const { return m_load_address; }
+ void ParseSectionContribs();
+
+ llvm::pdb::PDBFile &pdb() { return *m_file; }
+ const llvm::pdb::PDBFile &pdb() const { return *m_file; }
+
+ llvm::pdb::DbiStream &dbi() { return *m_dbi; }
+ const llvm::pdb::DbiStream &dbi() const { return *m_dbi; }
+
+ llvm::pdb::TpiStream &tpi() { return *m_tpi; }
+ const llvm::pdb::TpiStream &tpi() const { return *m_tpi; }
+
+ llvm::pdb::TpiStream &ipi() { return *m_ipi; }
+ const llvm::pdb::TpiStream &ipi() const { return *m_ipi; }
+
+ llvm::pdb::InfoStream &info() { return *m_info; }
+ const llvm::pdb::InfoStream &info() const { return *m_info; }
+
+ llvm::pdb::PublicsStream &publics() { return *m_publics; }
+ const llvm::pdb::PublicsStream &publics() const { return *m_publics; }
+
+ llvm::pdb::GlobalsStream &globals() { return *m_globals; }
+ const llvm::pdb::GlobalsStream &globals() const { return *m_globals; }
+
+ llvm::pdb::SymbolStream &symrecords() { return *m_symrecords; }
+ const llvm::pdb::SymbolStream &symrecords() const { return *m_symrecords; }
+
+ CompileUnitIndex &compilands() { return m_cus; }
+ const CompileUnitIndex &compilands() const { return m_cus; }
+
+ lldb::addr_t MakeVirtualAddress(uint16_t segment, uint32_t offset) const;
+ lldb::addr_t MakeVirtualAddress(const SegmentOffset &so) const;
+
+ std::vector<SymbolAndUid> FindSymbolsByVa(lldb::addr_t va);
+
+ llvm::codeview::CVSymbol ReadSymbolRecord(PdbCompilandSymId cu_sym) const;
+ llvm::codeview::CVSymbol ReadSymbolRecord(PdbGlobalSymId global) const;
+
+ llvm::Optional<uint16_t> GetModuleIndexForAddr(uint16_t segment,
+ uint32_t offset) const;
+ llvm::Optional<uint16_t> GetModuleIndexForVa(lldb::addr_t va) const;
+};
+} // namespace npdb
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.cpp
new file mode 100644
index 000000000000..e5ad23f813eb
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.cpp
@@ -0,0 +1,160 @@
+//===-- PdbSymUid.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 "PdbSymUid.h"
+
+using namespace lldb_private;
+using namespace lldb_private::npdb;
+using namespace llvm::codeview;
+
+namespace {
+struct GenericIdRepr {
+ uint64_t tag : 4;
+ uint64_t data : 60;
+};
+
+struct CompilandIdRepr {
+ uint64_t tag : 4;
+ uint64_t modi : 16;
+ uint64_t unused : 44;
+};
+
+struct CompilandSymIdRepr {
+ uint64_t tag : 4;
+ uint64_t modi : 16;
+ uint64_t offset : 32;
+ uint64_t unused : 12;
+};
+
+struct GlobalSymIdRepr {
+ uint64_t tag : 4;
+ uint64_t offset : 32;
+ uint64_t pub : 1;
+ uint64_t unused : 27;
+};
+
+struct TypeSymIdRepr {
+ uint64_t tag : 4;
+ uint64_t index : 32;
+ uint64_t ipi : 1;
+ uint64_t unused : 27;
+};
+
+struct FieldListMemberIdRepr {
+ uint64_t tag : 4;
+ uint64_t index : 32;
+ uint64_t offset : 16;
+ uint64_t unused : 12;
+};
+
+static_assert(sizeof(CompilandIdRepr) == 8, "Invalid structure size!");
+static_assert(sizeof(CompilandSymIdRepr) == 8, "Invalid structure size!");
+static_assert(sizeof(GlobalSymIdRepr) == 8, "Invalid structure size!");
+static_assert(sizeof(TypeSymIdRepr) == 8, "Invalid structure size!");
+static_assert(sizeof(FieldListMemberIdRepr) == 8, "Invalid structure size!");
+} // namespace
+
+template <typename OutT, typename InT> static OutT repr_cast(const InT &value) {
+ OutT result;
+ ::memcpy(&result, &value, sizeof(value));
+ return result;
+}
+
+PdbSymUid::PdbSymUid(const PdbCompilandId &cid) {
+ CompilandIdRepr repr;
+ ::memset(&repr, 0, sizeof(repr));
+ repr.modi = cid.modi;
+ repr.tag = static_cast<uint64_t>(PdbSymUidKind::Compiland);
+ m_repr = repr_cast<uint64_t>(repr);
+}
+
+PdbSymUid::PdbSymUid(const PdbCompilandSymId &csid) {
+ CompilandSymIdRepr repr;
+ ::memset(&repr, 0, sizeof(repr));
+ repr.modi = csid.modi;
+ repr.offset = csid.offset;
+ repr.tag = static_cast<uint64_t>(PdbSymUidKind::CompilandSym);
+ m_repr = repr_cast<uint64_t>(repr);
+}
+
+PdbSymUid::PdbSymUid(const PdbGlobalSymId &gsid) {
+ GlobalSymIdRepr repr;
+ ::memset(&repr, 0, sizeof(repr));
+ repr.pub = gsid.is_public;
+ repr.offset = gsid.offset;
+ repr.tag = static_cast<uint64_t>(PdbSymUidKind::GlobalSym);
+ m_repr = repr_cast<uint64_t>(repr);
+}
+
+PdbSymUid::PdbSymUid(const PdbTypeSymId &tsid) {
+ TypeSymIdRepr repr;
+ ::memset(&repr, 0, sizeof(repr));
+ repr.index = tsid.index.getIndex();
+ repr.ipi = tsid.is_ipi;
+ repr.tag = static_cast<uint64_t>(PdbSymUidKind::Type);
+ m_repr = repr_cast<uint64_t>(repr);
+}
+
+PdbSymUid::PdbSymUid(const PdbFieldListMemberId &flmid) {
+ FieldListMemberIdRepr repr;
+ ::memset(&repr, 0, sizeof(repr));
+ repr.index = flmid.index.getIndex();
+ repr.offset = flmid.offset;
+ repr.tag = static_cast<uint64_t>(PdbSymUidKind::FieldListMember);
+ m_repr = repr_cast<uint64_t>(repr);
+}
+
+PdbSymUidKind PdbSymUid::kind() const {
+ GenericIdRepr generic = repr_cast<GenericIdRepr>(m_repr);
+ return static_cast<PdbSymUidKind>(generic.tag);
+}
+
+PdbCompilandId PdbSymUid::asCompiland() const {
+ assert(kind() == PdbSymUidKind::Compiland);
+ auto repr = repr_cast<CompilandIdRepr>(m_repr);
+ PdbCompilandId result;
+ result.modi = repr.modi;
+ return result;
+}
+
+PdbCompilandSymId PdbSymUid::asCompilandSym() const {
+ assert(kind() == PdbSymUidKind::CompilandSym);
+ auto repr = repr_cast<CompilandSymIdRepr>(m_repr);
+ PdbCompilandSymId result;
+ result.modi = repr.modi;
+ result.offset = repr.offset;
+ return result;
+}
+
+PdbGlobalSymId PdbSymUid::asGlobalSym() const {
+ assert(kind() == PdbSymUidKind::GlobalSym ||
+ kind() == PdbSymUidKind::PublicSym);
+ auto repr = repr_cast<GlobalSymIdRepr>(m_repr);
+ PdbGlobalSymId result;
+ result.is_public = repr.pub;
+ result.offset = repr.offset;
+ return result;
+}
+
+PdbTypeSymId PdbSymUid::asTypeSym() const {
+ assert(kind() == PdbSymUidKind::Type);
+ auto repr = repr_cast<TypeSymIdRepr>(m_repr);
+ PdbTypeSymId result;
+ result.index.setIndex(repr.index);
+ result.is_ipi = repr.ipi;
+ return result;
+}
+
+PdbFieldListMemberId PdbSymUid::asFieldListMember() const {
+ assert(kind() == PdbSymUidKind::FieldListMember);
+ auto repr = repr_cast<FieldListMemberIdRepr>(m_repr);
+ PdbFieldListMemberId result;
+ result.index.setIndex(repr.index);
+ result.offset = repr.offset;
+ return result;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.h
new file mode 100644
index 000000000000..7252d63c1ab4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.h
@@ -0,0 +1,125 @@
+//===-- PdbSymUid.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
+//
+//===----------------------------------------------------------------------===//
+// A unique identification scheme for Pdb records.
+// The scheme is to partition a 64-bit integer into an 8-bit tag field, which
+// will contain some value from the PDB_SymType enumeration. The format of the
+// other 48-bits depend on the tag, but must be sufficient to locate the
+// corresponding entry in the underlying PDB file quickly. For example, for
+// a compile unit, we use 2 bytes to represent the index, which allows fast
+// access to the compile unit's information.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_SYMBOLFILENATIVEPDB_PDBSYMUID_H
+#define LLDB_PLUGINS_SYMBOLFILENATIVEPDB_PDBSYMUID_H
+
+#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
+#include "llvm/DebugInfo/PDB/PDBTypes.h"
+#include "llvm/Support/Compiler.h"
+
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+namespace npdb {
+
+enum class PdbSymUidKind : uint8_t {
+ Compiland,
+ CompilandSym,
+ PublicSym,
+ GlobalSym,
+ Type,
+ FieldListMember
+};
+
+struct PdbCompilandId {
+ // 0-based index of module in PDB
+ uint16_t modi;
+};
+
+struct PdbCompilandSymId {
+ PdbCompilandSymId() = default;
+ PdbCompilandSymId(uint16_t modi, uint32_t offset)
+ : modi(modi), offset(offset) {}
+ // 0-based index of module in PDB
+ uint16_t modi = 0;
+
+ // Offset of symbol's record in module stream. This is
+ // offset by 4 from the CVSymbolArray's notion of offset
+ // due to the debug magic at the beginning of the stream.
+ uint32_t offset = 0;
+};
+
+struct PdbGlobalSymId {
+ PdbGlobalSymId() = default;
+ PdbGlobalSymId(uint32_t offset, bool is_public)
+ : offset(offset), is_public(is_public) {}
+
+ // Offset of symbol's record in globals or publics stream.
+ uint32_t offset = 0;
+
+ // True if this symbol is in the public stream, false if it's in the globals
+ // stream.
+ bool is_public = false;
+};
+
+struct PdbTypeSymId {
+ PdbTypeSymId() = default;
+ PdbTypeSymId(llvm::codeview::TypeIndex index, bool is_ipi = false)
+ : index(index), is_ipi(is_ipi) {}
+
+ // The index of the of the type in the TPI or IPI stream.
+ llvm::codeview::TypeIndex index;
+
+ // True if this symbol comes from the IPI stream, false if it's from the TPI
+ // stream.
+ bool is_ipi = false;
+};
+
+struct PdbFieldListMemberId {
+ // The TypeIndex of the LF_FIELDLIST record.
+ llvm::codeview::TypeIndex index;
+
+ // The offset from the beginning of the LF_FIELDLIST record to this record.
+ uint16_t offset = 0;
+};
+
+class PdbSymUid {
+ uint64_t m_repr = 0;
+
+public:
+ PdbSymUid() = default;
+ PdbSymUid(uint64_t repr) : m_repr(repr) {}
+ PdbSymUid(const PdbCompilandId &cid);
+ PdbSymUid(const PdbCompilandSymId &csid);
+ PdbSymUid(const PdbGlobalSymId &gsid);
+ PdbSymUid(const PdbTypeSymId &tsid);
+ PdbSymUid(const PdbFieldListMemberId &flmid);
+
+ uint64_t toOpaqueId() const { return m_repr; }
+
+ PdbSymUidKind kind() const;
+
+ PdbCompilandId asCompiland() const;
+ PdbCompilandSymId asCompilandSym() const;
+ PdbGlobalSymId asGlobalSym() const;
+ PdbTypeSymId asTypeSym() const;
+ PdbFieldListMemberId asFieldListMember() const;
+};
+
+template <typename T> uint64_t toOpaqueUid(const T &cid) {
+ return PdbSymUid(cid).toOpaqueId();
+}
+
+struct SymbolAndUid {
+ llvm::codeview::CVSymbol sym;
+ PdbSymUid uid;
+};
+} // namespace npdb
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
new file mode 100644
index 000000000000..1f5c97da81cf
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
@@ -0,0 +1,877 @@
+//===-- PdbUtil.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 "PdbUtil.h"
+
+#include "DWARFLocationExpression.h"
+#include "PdbIndex.h"
+#include "PdbSymUid.h"
+
+#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
+#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+
+#include "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/lldb-enumerations.h"
+
+using namespace lldb_private;
+using namespace lldb_private::npdb;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+static Variable::RangeList
+MakeRangeList(const PdbIndex &index, const LocalVariableAddrRange &range,
+ llvm::ArrayRef<LocalVariableAddrGap> gaps) {
+ lldb::addr_t start =
+ index.MakeVirtualAddress(range.ISectStart, range.OffsetStart);
+ lldb::addr_t end = start + range.Range;
+
+ Variable::RangeList result;
+ while (!gaps.empty()) {
+ const LocalVariableAddrGap &gap = gaps.front();
+
+ lldb::addr_t size = gap.GapStartOffset - start;
+ result.Append(start, size);
+ start += gap.Range;
+ gaps = gaps.drop_front();
+ }
+
+ result.Append(start, end - start);
+ return result;
+}
+
+CVTagRecord CVTagRecord::create(CVType type) {
+ assert(IsTagRecord(type) && "type is not a tag record!");
+ switch (type.kind()) {
+ case LF_CLASS:
+ case LF_STRUCTURE:
+ case LF_INTERFACE: {
+ ClassRecord cr;
+ llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(type, cr));
+ return CVTagRecord(std::move(cr));
+ }
+ case LF_UNION: {
+ UnionRecord ur;
+ llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(type, ur));
+ return CVTagRecord(std::move(ur));
+ }
+ case LF_ENUM: {
+ EnumRecord er;
+ llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(type, er));
+ return CVTagRecord(std::move(er));
+ }
+ default:
+ llvm_unreachable("Unreachable!");
+ }
+}
+
+CVTagRecord::CVTagRecord(ClassRecord &&c)
+ : cvclass(std::move(c)),
+ m_kind(cvclass.Kind == TypeRecordKind::Struct ? Struct : Class) {}
+CVTagRecord::CVTagRecord(UnionRecord &&u)
+ : cvunion(std::move(u)), m_kind(Union) {}
+CVTagRecord::CVTagRecord(EnumRecord &&e) : cvenum(std::move(e)), m_kind(Enum) {}
+
+PDB_SymType lldb_private::npdb::CVSymToPDBSym(SymbolKind kind) {
+ switch (kind) {
+ case S_COMPILE3:
+ case S_OBJNAME:
+ return PDB_SymType::CompilandDetails;
+ case S_ENVBLOCK:
+ return PDB_SymType::CompilandEnv;
+ case S_THUNK32:
+ case S_TRAMPOLINE:
+ return PDB_SymType::Thunk;
+ case S_COFFGROUP:
+ return PDB_SymType::CoffGroup;
+ case S_EXPORT:
+ return PDB_SymType::Export;
+ case S_LPROC32:
+ case S_GPROC32:
+ case S_LPROC32_DPC:
+ return PDB_SymType::Function;
+ case S_PUB32:
+ return PDB_SymType::PublicSymbol;
+ case S_INLINESITE:
+ return PDB_SymType::InlineSite;
+ case S_LOCAL:
+ case S_BPREL32:
+ case S_REGREL32:
+ case S_MANCONSTANT:
+ case S_CONSTANT:
+ case S_LDATA32:
+ case S_GDATA32:
+ case S_LMANDATA:
+ case S_GMANDATA:
+ case S_LTHREAD32:
+ case S_GTHREAD32:
+ return PDB_SymType::Data;
+ case S_BLOCK32:
+ return PDB_SymType::Block;
+ case S_LABEL32:
+ return PDB_SymType::Label;
+ case S_CALLSITEINFO:
+ return PDB_SymType::CallSite;
+ case S_HEAPALLOCSITE:
+ return PDB_SymType::HeapAllocationSite;
+ case S_CALLEES:
+ return PDB_SymType::Callee;
+ case S_CALLERS:
+ return PDB_SymType::Caller;
+ default:
+ lldbassert(false && "Invalid symbol record kind!");
+ }
+ return PDB_SymType::None;
+}
+
+PDB_SymType lldb_private::npdb::CVTypeToPDBType(TypeLeafKind kind) {
+ switch (kind) {
+ case LF_ARRAY:
+ return PDB_SymType::ArrayType;
+ case LF_ARGLIST:
+ return PDB_SymType::FunctionSig;
+ case LF_BCLASS:
+ return PDB_SymType::BaseClass;
+ case LF_BINTERFACE:
+ return PDB_SymType::BaseInterface;
+ case LF_CLASS:
+ case LF_STRUCTURE:
+ case LF_INTERFACE:
+ case LF_UNION:
+ return PDB_SymType::UDT;
+ case LF_POINTER:
+ return PDB_SymType::PointerType;
+ case LF_ENUM:
+ return PDB_SymType::Enum;
+ case LF_PROCEDURE:
+ return PDB_SymType::FunctionSig;
+ case LF_BITFIELD:
+ return PDB_SymType::BuiltinType;
+ default:
+ lldbassert(false && "Invalid type record kind!");
+ }
+ return PDB_SymType::None;
+}
+
+bool lldb_private::npdb::SymbolHasAddress(const CVSymbol &sym) {
+ switch (sym.kind()) {
+ case S_GPROC32:
+ case S_LPROC32:
+ case S_GPROC32_ID:
+ case S_LPROC32_ID:
+ case S_LPROC32_DPC:
+ case S_LPROC32_DPC_ID:
+ case S_THUNK32:
+ case S_TRAMPOLINE:
+ case S_COFFGROUP:
+ case S_BLOCK32:
+ case S_LABEL32:
+ case S_CALLSITEINFO:
+ case S_HEAPALLOCSITE:
+ case S_LDATA32:
+ case S_GDATA32:
+ case S_LMANDATA:
+ case S_GMANDATA:
+ case S_LTHREAD32:
+ case S_GTHREAD32:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool lldb_private::npdb::SymbolIsCode(const CVSymbol &sym) {
+ switch (sym.kind()) {
+ case S_GPROC32:
+ case S_LPROC32:
+ case S_GPROC32_ID:
+ case S_LPROC32_ID:
+ case S_LPROC32_DPC:
+ case S_LPROC32_DPC_ID:
+ case S_THUNK32:
+ case S_TRAMPOLINE:
+ case S_COFFGROUP:
+ case S_BLOCK32:
+ return true;
+ default:
+ return false;
+ }
+}
+
+template <typename RecordT> RecordT createRecord(const CVSymbol &sym) {
+ RecordT record(static_cast<SymbolRecordKind>(sym.kind()));
+ cantFail(SymbolDeserializer::deserializeAs<RecordT>(sym, record));
+ return record;
+}
+
+template <typename RecordT>
+static SegmentOffset GetSegmentAndOffset(const CVSymbol &sym) {
+ RecordT record = createRecord<RecordT>(sym);
+ return {record.Segment, record.CodeOffset};
+}
+
+template <>
+SegmentOffset GetSegmentAndOffset<TrampolineSym>(const CVSymbol &sym) {
+ TrampolineSym record = createRecord<TrampolineSym>(sym);
+ return {record.ThunkSection, record.ThunkOffset};
+}
+
+template <> SegmentOffset GetSegmentAndOffset<Thunk32Sym>(const CVSymbol &sym) {
+ Thunk32Sym record = createRecord<Thunk32Sym>(sym);
+ return {record.Segment, record.Offset};
+}
+
+template <>
+SegmentOffset GetSegmentAndOffset<CoffGroupSym>(const CVSymbol &sym) {
+ CoffGroupSym record = createRecord<CoffGroupSym>(sym);
+ return {record.Segment, record.Offset};
+}
+
+template <> SegmentOffset GetSegmentAndOffset<DataSym>(const CVSymbol &sym) {
+ DataSym record = createRecord<DataSym>(sym);
+ return {record.Segment, record.DataOffset};
+}
+
+template <>
+SegmentOffset GetSegmentAndOffset<ThreadLocalDataSym>(const CVSymbol &sym) {
+ ThreadLocalDataSym record = createRecord<ThreadLocalDataSym>(sym);
+ return {record.Segment, record.DataOffset};
+}
+
+SegmentOffset lldb_private::npdb::GetSegmentAndOffset(const CVSymbol &sym) {
+ switch (sym.kind()) {
+ case S_GPROC32:
+ case S_LPROC32:
+ case S_GPROC32_ID:
+ case S_LPROC32_ID:
+ case S_LPROC32_DPC:
+ case S_LPROC32_DPC_ID:
+ return ::GetSegmentAndOffset<ProcSym>(sym);
+ case S_THUNK32:
+ return ::GetSegmentAndOffset<Thunk32Sym>(sym);
+ break;
+ case S_TRAMPOLINE:
+ return ::GetSegmentAndOffset<TrampolineSym>(sym);
+ break;
+ case S_COFFGROUP:
+ return ::GetSegmentAndOffset<CoffGroupSym>(sym);
+ break;
+ case S_BLOCK32:
+ return ::GetSegmentAndOffset<BlockSym>(sym);
+ break;
+ case S_LABEL32:
+ return ::GetSegmentAndOffset<LabelSym>(sym);
+ break;
+ case S_CALLSITEINFO:
+ return ::GetSegmentAndOffset<CallSiteInfoSym>(sym);
+ break;
+ case S_HEAPALLOCSITE:
+ return ::GetSegmentAndOffset<HeapAllocationSiteSym>(sym);
+ break;
+ case S_LDATA32:
+ case S_GDATA32:
+ case S_LMANDATA:
+ case S_GMANDATA:
+ return ::GetSegmentAndOffset<DataSym>(sym);
+ break;
+ case S_LTHREAD32:
+ case S_GTHREAD32:
+ return ::GetSegmentAndOffset<ThreadLocalDataSym>(sym);
+ break;
+ default:
+ lldbassert(false && "Record does not have a segment/offset!");
+ }
+ return {0, 0};
+}
+
+template <typename RecordT>
+SegmentOffsetLength GetSegmentOffsetAndLength(const CVSymbol &sym) {
+ RecordT record = createRecord<RecordT>(sym);
+ return {record.Segment, record.CodeOffset, record.CodeSize};
+}
+
+template <>
+SegmentOffsetLength
+GetSegmentOffsetAndLength<TrampolineSym>(const CVSymbol &sym) {
+ TrampolineSym record = createRecord<TrampolineSym>(sym);
+ return {record.ThunkSection, record.ThunkOffset, record.Size};
+}
+
+template <>
+SegmentOffsetLength GetSegmentOffsetAndLength<Thunk32Sym>(const CVSymbol &sym) {
+ Thunk32Sym record = createRecord<Thunk32Sym>(sym);
+ return SegmentOffsetLength{record.Segment, record.Offset, record.Length};
+}
+
+template <>
+SegmentOffsetLength
+GetSegmentOffsetAndLength<CoffGroupSym>(const CVSymbol &sym) {
+ CoffGroupSym record = createRecord<CoffGroupSym>(sym);
+ return SegmentOffsetLength{record.Segment, record.Offset, record.Size};
+}
+
+SegmentOffsetLength
+lldb_private::npdb::GetSegmentOffsetAndLength(const CVSymbol &sym) {
+ switch (sym.kind()) {
+ case S_GPROC32:
+ case S_LPROC32:
+ case S_GPROC32_ID:
+ case S_LPROC32_ID:
+ case S_LPROC32_DPC:
+ case S_LPROC32_DPC_ID:
+ return ::GetSegmentOffsetAndLength<ProcSym>(sym);
+ case S_THUNK32:
+ return ::GetSegmentOffsetAndLength<Thunk32Sym>(sym);
+ break;
+ case S_TRAMPOLINE:
+ return ::GetSegmentOffsetAndLength<TrampolineSym>(sym);
+ break;
+ case S_COFFGROUP:
+ return ::GetSegmentOffsetAndLength<CoffGroupSym>(sym);
+ break;
+ case S_BLOCK32:
+ return ::GetSegmentOffsetAndLength<BlockSym>(sym);
+ break;
+ default:
+ lldbassert(false && "Record does not have a segment/offset/length triple!");
+ }
+ return {0, 0, 0};
+}
+
+bool lldb_private::npdb::IsForwardRefUdt(CVType cvt) {
+ ClassRecord cr;
+ UnionRecord ur;
+ EnumRecord er;
+ switch (cvt.kind()) {
+ case LF_CLASS:
+ case LF_STRUCTURE:
+ case LF_INTERFACE:
+ llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, cr));
+ return cr.isForwardRef();
+ case LF_UNION:
+ llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, ur));
+ return ur.isForwardRef();
+ case LF_ENUM:
+ llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, er));
+ return er.isForwardRef();
+ default:
+ return false;
+ }
+}
+
+bool lldb_private::npdb::IsTagRecord(llvm::codeview::CVType cvt) {
+ switch (cvt.kind()) {
+ case LF_CLASS:
+ case LF_STRUCTURE:
+ case LF_UNION:
+ case LF_ENUM:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool lldb_private::npdb::IsClassStructUnion(llvm::codeview::CVType cvt) {
+ switch (cvt.kind()) {
+ case LF_CLASS:
+ case LF_STRUCTURE:
+ case LF_UNION:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool lldb_private::npdb::IsForwardRefUdt(const PdbTypeSymId &id,
+ TpiStream &tpi) {
+ if (id.is_ipi || id.index.isSimple())
+ return false;
+ return IsForwardRefUdt(tpi.getType(id.index));
+}
+
+bool lldb_private::npdb::IsTagRecord(const PdbTypeSymId &id, TpiStream &tpi) {
+ if (id.is_ipi || id.index.isSimple())
+ return false;
+ return IsTagRecord(tpi.getType(id.index));
+}
+
+lldb::AccessType
+lldb_private::npdb::TranslateMemberAccess(MemberAccess access) {
+ switch (access) {
+ case MemberAccess::Private:
+ return lldb::eAccessPrivate;
+ case MemberAccess::Protected:
+ return lldb::eAccessProtected;
+ case MemberAccess::Public:
+ return lldb::eAccessPublic;
+ case MemberAccess::None:
+ return lldb::eAccessNone;
+ }
+ llvm_unreachable("unreachable");
+}
+
+TypeIndex lldb_private::npdb::GetFieldListIndex(CVType cvt) {
+ switch (cvt.kind()) {
+ case LF_CLASS:
+ case LF_STRUCTURE:
+ case LF_INTERFACE: {
+ ClassRecord cr;
+ cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, cr));
+ return cr.FieldList;
+ }
+ case LF_UNION: {
+ UnionRecord ur;
+ cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, ur));
+ return ur.FieldList;
+ }
+ case LF_ENUM: {
+ EnumRecord er;
+ cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, er));
+ return er.FieldList;
+ }
+ default:
+ llvm_unreachable("Unreachable!");
+ }
+}
+
+TypeIndex lldb_private::npdb::LookThroughModifierRecord(CVType modifier) {
+ lldbassert(modifier.kind() == LF_MODIFIER);
+ ModifierRecord mr;
+ llvm::cantFail(TypeDeserializer::deserializeAs<ModifierRecord>(modifier, mr));
+ return mr.ModifiedType;
+}
+
+llvm::StringRef lldb_private::npdb::DropNameScope(llvm::StringRef name) {
+ return MSVCUndecoratedNameParser::DropScope(name);
+}
+
+VariableInfo lldb_private::npdb::GetVariableNameInfo(CVSymbol sym) {
+ VariableInfo result;
+
+ if (sym.kind() == S_REGREL32) {
+ RegRelativeSym reg(SymbolRecordKind::RegRelativeSym);
+ cantFail(SymbolDeserializer::deserializeAs<RegRelativeSym>(sym, reg));
+ result.type = reg.Type;
+ result.name = reg.Name;
+ return result;
+ }
+
+ if (sym.kind() == S_REGISTER) {
+ RegisterSym reg(SymbolRecordKind::RegisterSym);
+ cantFail(SymbolDeserializer::deserializeAs<RegisterSym>(sym, reg));
+ result.type = reg.Index;
+ result.name = reg.Name;
+ return result;
+ }
+
+ if (sym.kind() == S_LOCAL) {
+ LocalSym local(SymbolRecordKind::LocalSym);
+ cantFail(SymbolDeserializer::deserializeAs<LocalSym>(sym, local));
+ result.type = local.Type;
+ result.name = local.Name;
+ return result;
+ }
+
+ if (sym.kind() == S_GDATA32 || sym.kind() == S_LDATA32) {
+ DataSym data(SymbolRecordKind::DataSym);
+ cantFail(SymbolDeserializer::deserializeAs<DataSym>(sym, data));
+ result.type = data.Type;
+ result.name = data.Name;
+ return result;
+ }
+
+ if (sym.kind() == S_GTHREAD32 || sym.kind() == S_LTHREAD32) {
+ ThreadLocalDataSym data(SymbolRecordKind::ThreadLocalDataSym);
+ cantFail(SymbolDeserializer::deserializeAs<ThreadLocalDataSym>(sym, data));
+ result.type = data.Type;
+ result.name = data.Name;
+ return result;
+ }
+
+ if (sym.kind() == S_CONSTANT) {
+ ConstantSym constant(SymbolRecordKind::ConstantSym);
+ cantFail(SymbolDeserializer::deserializeAs<ConstantSym>(sym, constant));
+ result.type = constant.Type;
+ result.name = constant.Name;
+ return result;
+ }
+
+ lldbassert(false && "Invalid variable record kind!");
+ return {};
+}
+
+static llvm::FixedStreamArray<FrameData>::Iterator
+GetCorrespondingFrameData(lldb::addr_t load_addr,
+ const DebugFrameDataSubsectionRef &fpo_data,
+ const Variable::RangeList &ranges) {
+ lldbassert(!ranges.IsEmpty());
+
+ // assume that all variable ranges correspond to one frame data
+ using RangeListEntry = Variable::RangeList::Entry;
+ const RangeListEntry &range = ranges.GetEntryRef(0);
+
+ auto it = fpo_data.begin();
+
+ // start by searching first frame data range containing variable range
+ for (; it != fpo_data.end(); ++it) {
+ RangeListEntry fd_range(load_addr + it->RvaStart, it->CodeSize);
+
+ if (fd_range.Contains(range)) {
+ break;
+ }
+ }
+
+ // then first most nested entry that still contains variable range
+ auto found = it;
+ for (; it != fpo_data.end(); ++it) {
+ RangeListEntry fd_range(load_addr + it->RvaStart, it->CodeSize);
+
+ if (!fd_range.Contains(range)) {
+ break;
+ }
+ found = it;
+ }
+
+ return found;
+}
+
+static bool GetFrameDataProgram(PdbIndex &index,
+ const Variable::RangeList &ranges,
+ llvm::StringRef &out_program) {
+ const DebugFrameDataSubsectionRef &new_fpo_data =
+ index.dbi().getNewFpoRecords();
+
+ auto frame_data_it =
+ GetCorrespondingFrameData(index.GetLoadAddress(), new_fpo_data, ranges);
+ if (frame_data_it == new_fpo_data.end())
+ return false;
+
+ PDBStringTable &strings = cantFail(index.pdb().getStringTable());
+ out_program = cantFail(strings.getStringForID(frame_data_it->FrameFunc));
+ return true;
+}
+
+static RegisterId GetBaseFrameRegister(PdbIndex &index,
+ PdbCompilandSymId frame_proc_id,
+ bool is_parameter) {
+ CVSymbol frame_proc_cvs = index.ReadSymbolRecord(frame_proc_id);
+ lldbassert(frame_proc_cvs.kind() == S_FRAMEPROC);
+
+ FrameProcSym frame_proc(SymbolRecordKind::FrameProcSym);
+ cantFail(SymbolDeserializer::deserializeAs<FrameProcSym>(frame_proc_cvs,
+ frame_proc));
+
+ CPUType cpu_type = index.compilands()
+ .GetCompiland(frame_proc_id.modi)
+ ->m_compile_opts->Machine;
+
+ return is_parameter ? frame_proc.getParamFramePtrReg(cpu_type)
+ : frame_proc.getLocalFramePtrReg(cpu_type);
+}
+
+VariableInfo lldb_private::npdb::GetVariableLocationInfo(
+ PdbIndex &index, PdbCompilandSymId var_id, Block &block,
+ lldb::ModuleSP module) {
+
+ CVSymbol sym = index.ReadSymbolRecord(var_id);
+
+ VariableInfo result = GetVariableNameInfo(sym);
+
+ if (sym.kind() == S_REGREL32) {
+ RegRelativeSym reg(SymbolRecordKind::RegRelativeSym);
+ cantFail(SymbolDeserializer::deserializeAs<RegRelativeSym>(sym, reg));
+ result.location =
+ MakeRegRelLocationExpression(reg.Register, reg.Offset, module);
+ result.ranges.emplace();
+ return result;
+ }
+
+ if (sym.kind() == S_REGISTER) {
+ RegisterSym reg(SymbolRecordKind::RegisterSym);
+ cantFail(SymbolDeserializer::deserializeAs<RegisterSym>(sym, reg));
+ result.location = MakeEnregisteredLocationExpression(reg.Register, module);
+ result.ranges.emplace();
+ return result;
+ }
+
+ if (sym.kind() == S_LOCAL) {
+ LocalSym local(SymbolRecordKind::LocalSym);
+ cantFail(SymbolDeserializer::deserializeAs<LocalSym>(sym, local));
+
+ PdbCompilandSymId loc_specifier_id(var_id.modi,
+ var_id.offset + sym.RecordData.size());
+ CVSymbol loc_specifier_cvs = index.ReadSymbolRecord(loc_specifier_id);
+ if (loc_specifier_cvs.kind() == S_DEFRANGE_FRAMEPOINTER_REL) {
+ DefRangeFramePointerRelSym loc(
+ SymbolRecordKind::DefRangeFramePointerRelSym);
+ cantFail(SymbolDeserializer::deserializeAs<DefRangeFramePointerRelSym>(
+ loc_specifier_cvs, loc));
+
+ Variable::RangeList ranges = MakeRangeList(index, loc.Range, loc.Gaps);
+
+ // TODO: may be better to pass function scope and not lookup it every
+ // time? find nearest parent function block
+ Block *cur = &block;
+ while (cur->GetParent()) {
+ cur = cur->GetParent();
+ }
+ PdbCompilandSymId func_scope_id =
+ PdbSymUid(cur->GetID()).asCompilandSym();
+ CVSymbol func_block_cvs = index.ReadSymbolRecord(func_scope_id);
+ lldbassert(func_block_cvs.kind() == S_GPROC32 ||
+ func_block_cvs.kind() == S_LPROC32);
+
+ PdbCompilandSymId frame_proc_id(
+ func_scope_id.modi, func_scope_id.offset + func_block_cvs.length());
+
+ bool is_parameter =
+ ((local.Flags & LocalSymFlags::IsParameter) != LocalSymFlags::None);
+ RegisterId base_reg =
+ GetBaseFrameRegister(index, frame_proc_id, is_parameter);
+
+ if (base_reg == RegisterId::VFRAME) {
+ llvm::StringRef program;
+ if (GetFrameDataProgram(index, ranges, program)) {
+ result.location =
+ MakeVFrameRelLocationExpression(program, loc.Offset, module);
+ result.ranges = std::move(ranges);
+ } else {
+ // invalid variable
+ }
+ } else {
+ result.location =
+ MakeRegRelLocationExpression(base_reg, loc.Offset, module);
+ result.ranges = std::move(ranges);
+ }
+ } else if (loc_specifier_cvs.kind() == S_DEFRANGE_REGISTER_REL) {
+ DefRangeRegisterRelSym loc(SymbolRecordKind::DefRangeRegisterRelSym);
+ cantFail(SymbolDeserializer::deserializeAs<DefRangeRegisterRelSym>(
+ loc_specifier_cvs, loc));
+
+ Variable::RangeList ranges = MakeRangeList(index, loc.Range, loc.Gaps);
+
+ RegisterId base_reg = (RegisterId)(uint16_t)loc.Hdr.Register;
+
+ if (base_reg == RegisterId::VFRAME) {
+ llvm::StringRef program;
+ if (GetFrameDataProgram(index, ranges, program)) {
+ result.location = MakeVFrameRelLocationExpression(
+ program, loc.Hdr.BasePointerOffset, module);
+ result.ranges = std::move(ranges);
+ } else {
+ // invalid variable
+ }
+ } else {
+ result.location = MakeRegRelLocationExpression(
+ base_reg, loc.Hdr.BasePointerOffset, module);
+ result.ranges = std::move(ranges);
+ }
+ }
+
+ // FIXME: Handle other kinds
+ return result;
+ }
+ llvm_unreachable("Symbol is not a local variable!");
+ return result;
+}
+
+lldb::BasicType
+lldb_private::npdb::GetCompilerTypeForSimpleKind(SimpleTypeKind kind) {
+ switch (kind) {
+ case SimpleTypeKind::Boolean128:
+ case SimpleTypeKind::Boolean16:
+ case SimpleTypeKind::Boolean32:
+ case SimpleTypeKind::Boolean64:
+ case SimpleTypeKind::Boolean8:
+ return lldb::eBasicTypeBool;
+ case SimpleTypeKind::Byte:
+ case SimpleTypeKind::UnsignedCharacter:
+ return lldb::eBasicTypeUnsignedChar;
+ case SimpleTypeKind::NarrowCharacter:
+ return lldb::eBasicTypeChar;
+ case SimpleTypeKind::SignedCharacter:
+ case SimpleTypeKind::SByte:
+ return lldb::eBasicTypeSignedChar;
+ case SimpleTypeKind::Character16:
+ return lldb::eBasicTypeChar16;
+ case SimpleTypeKind::Character32:
+ return lldb::eBasicTypeChar32;
+ case SimpleTypeKind::Complex80:
+ return lldb::eBasicTypeLongDoubleComplex;
+ case SimpleTypeKind::Complex64:
+ return lldb::eBasicTypeDoubleComplex;
+ case SimpleTypeKind::Complex32:
+ return lldb::eBasicTypeFloatComplex;
+ case SimpleTypeKind::Float128:
+ case SimpleTypeKind::Float80:
+ return lldb::eBasicTypeLongDouble;
+ case SimpleTypeKind::Float64:
+ return lldb::eBasicTypeDouble;
+ case SimpleTypeKind::Float32:
+ return lldb::eBasicTypeFloat;
+ case SimpleTypeKind::Float16:
+ return lldb::eBasicTypeHalf;
+ case SimpleTypeKind::Int128:
+ return lldb::eBasicTypeInt128;
+ case SimpleTypeKind::Int64:
+ case SimpleTypeKind::Int64Quad:
+ return lldb::eBasicTypeLongLong;
+ case SimpleTypeKind::Int32:
+ return lldb::eBasicTypeInt;
+ case SimpleTypeKind::Int16:
+ case SimpleTypeKind::Int16Short:
+ return lldb::eBasicTypeShort;
+ case SimpleTypeKind::UInt128:
+ return lldb::eBasicTypeUnsignedInt128;
+ case SimpleTypeKind::UInt64:
+ case SimpleTypeKind::UInt64Quad:
+ return lldb::eBasicTypeUnsignedLongLong;
+ case SimpleTypeKind::HResult:
+ case SimpleTypeKind::UInt32:
+ return lldb::eBasicTypeUnsignedInt;
+ case SimpleTypeKind::UInt16:
+ case SimpleTypeKind::UInt16Short:
+ return lldb::eBasicTypeUnsignedShort;
+ case SimpleTypeKind::Int32Long:
+ return lldb::eBasicTypeLong;
+ case SimpleTypeKind::UInt32Long:
+ return lldb::eBasicTypeUnsignedLong;
+ case SimpleTypeKind::Void:
+ return lldb::eBasicTypeVoid;
+ case SimpleTypeKind::WideCharacter:
+ return lldb::eBasicTypeWChar;
+ default:
+ return lldb::eBasicTypeInvalid;
+ }
+}
+
+size_t lldb_private::npdb::GetTypeSizeForSimpleKind(SimpleTypeKind kind) {
+ switch (kind) {
+ case SimpleTypeKind::Boolean128:
+ case SimpleTypeKind::Int128:
+ case SimpleTypeKind::UInt128:
+ case SimpleTypeKind::Float128:
+ return 16;
+ case SimpleTypeKind::Complex80:
+ case SimpleTypeKind::Float80:
+ return 10;
+ case SimpleTypeKind::Boolean64:
+ case SimpleTypeKind::Complex64:
+ case SimpleTypeKind::UInt64:
+ case SimpleTypeKind::UInt64Quad:
+ case SimpleTypeKind::Float64:
+ case SimpleTypeKind::Int64:
+ case SimpleTypeKind::Int64Quad:
+ return 8;
+ case SimpleTypeKind::Boolean32:
+ case SimpleTypeKind::Character32:
+ case SimpleTypeKind::Complex32:
+ case SimpleTypeKind::Float32:
+ case SimpleTypeKind::Int32:
+ case SimpleTypeKind::Int32Long:
+ case SimpleTypeKind::UInt32Long:
+ case SimpleTypeKind::HResult:
+ case SimpleTypeKind::UInt32:
+ return 4;
+ case SimpleTypeKind::Boolean16:
+ case SimpleTypeKind::Character16:
+ case SimpleTypeKind::Float16:
+ case SimpleTypeKind::Int16:
+ case SimpleTypeKind::Int16Short:
+ case SimpleTypeKind::UInt16:
+ case SimpleTypeKind::UInt16Short:
+ case SimpleTypeKind::WideCharacter:
+ return 2;
+ case SimpleTypeKind::Boolean8:
+ case SimpleTypeKind::Byte:
+ case SimpleTypeKind::UnsignedCharacter:
+ case SimpleTypeKind::NarrowCharacter:
+ case SimpleTypeKind::SignedCharacter:
+ case SimpleTypeKind::SByte:
+ return 1;
+ case SimpleTypeKind::Void:
+ default:
+ return 0;
+ }
+}
+
+PdbTypeSymId lldb_private::npdb::GetBestPossibleDecl(PdbTypeSymId id,
+ TpiStream &tpi) {
+ if (id.index.isSimple())
+ return id;
+
+ CVType cvt = tpi.getType(id.index);
+
+ // Only tag records have a best and a worst record.
+ if (!IsTagRecord(cvt))
+ return id;
+
+ // Tag records that are not forward decls are full decls, hence they are the
+ // best.
+ if (!IsForwardRefUdt(cvt))
+ return id;
+
+ return llvm::cantFail(tpi.findFullDeclForForwardRef(id.index));
+}
+
+template <typename RecordType> static size_t GetSizeOfTypeInternal(CVType cvt) {
+ RecordType record;
+ llvm::cantFail(TypeDeserializer::deserializeAs<RecordType>(cvt, record));
+ return record.getSize();
+}
+
+size_t lldb_private::npdb::GetSizeOfType(PdbTypeSymId id,
+ llvm::pdb::TpiStream &tpi) {
+ if (id.index.isSimple()) {
+ switch (id.index.getSimpleMode()) {
+ case SimpleTypeMode::Direct:
+ return GetTypeSizeForSimpleKind(id.index.getSimpleKind());
+ case SimpleTypeMode::NearPointer32:
+ case SimpleTypeMode::FarPointer32:
+ return 4;
+ case SimpleTypeMode::NearPointer64:
+ return 8;
+ case SimpleTypeMode::NearPointer128:
+ return 16;
+ default:
+ break;
+ }
+ return 0;
+ }
+
+ TypeIndex index = id.index;
+ if (IsForwardRefUdt(index, tpi))
+ index = llvm::cantFail(tpi.findFullDeclForForwardRef(index));
+
+ CVType cvt = tpi.getType(index);
+ switch (cvt.kind()) {
+ case LF_MODIFIER:
+ return GetSizeOfType({LookThroughModifierRecord(cvt)}, tpi);
+ case LF_ENUM: {
+ EnumRecord record;
+ llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, record));
+ return GetSizeOfType({record.UnderlyingType}, tpi);
+ }
+ case LF_POINTER:
+ return GetSizeOfTypeInternal<PointerRecord>(cvt);
+ case LF_ARRAY:
+ return GetSizeOfTypeInternal<ArrayRecord>(cvt);
+ case LF_CLASS:
+ case LF_STRUCTURE:
+ case LF_INTERFACE:
+ return GetSizeOfTypeInternal<ClassRecord>(cvt);
+ case LF_UNION:
+ return GetSizeOfTypeInternal<UnionRecord>(cvt);
+ default:
+ break;
+ }
+ return 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h
new file mode 100644
index 000000000000..6f675b56dca4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h
@@ -0,0 +1,158 @@
+//===-- PdbUtil.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_PLUGINS_SYMBOLFILENATIVEPDB_PDBUTIL_H
+#define LLDB_PLUGINS_SYMBOLFILENATIVEPDB_PDBUTIL_H
+
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/lldb-enumerations.h"
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/DebugInfo/CodeView/CodeView.h"
+#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeRecord.h"
+#include "llvm/DebugInfo/PDB/PDBTypes.h"
+
+#include "PdbSymUid.h"
+
+#include <tuple>
+#include <utility>
+
+namespace llvm {
+namespace pdb {
+class TpiStream;
+}
+} // namespace llvm
+
+namespace lldb_private {
+namespace npdb {
+
+class PdbIndex;
+
+struct CVTagRecord {
+ enum Kind { Class, Struct, Union, Enum };
+
+ static CVTagRecord create(llvm::codeview::CVType type);
+
+ Kind kind() const { return m_kind; }
+
+ const llvm::codeview::TagRecord &asTag() const {
+ if (m_kind == Struct || m_kind == Class)
+ return cvclass;
+ if (m_kind == Enum)
+ return cvenum;
+ return cvunion;
+ }
+
+ const llvm::codeview::ClassRecord &asClass() const {
+ assert(m_kind == Struct || m_kind == Class);
+ return cvclass;
+ }
+
+ const llvm::codeview::EnumRecord &asEnum() const {
+ assert(m_kind == Enum);
+ return cvenum;
+ }
+
+ const llvm::codeview::UnionRecord &asUnion() const {
+ assert(m_kind == Union);
+ return cvunion;
+ }
+
+ llvm::StringRef name() const {
+ if (m_kind == Struct || m_kind == Union)
+ return cvclass.Name;
+ if (m_kind == Enum)
+ return cvenum.Name;
+ return cvunion.Name;
+ }
+
+private:
+ CVTagRecord(llvm::codeview::ClassRecord &&c);
+ CVTagRecord(llvm::codeview::UnionRecord &&u);
+ CVTagRecord(llvm::codeview::EnumRecord &&e);
+ union {
+ llvm::codeview::ClassRecord cvclass;
+ llvm::codeview::EnumRecord cvenum;
+ llvm::codeview::UnionRecord cvunion;
+ };
+ Kind m_kind;
+};
+
+struct SegmentOffset {
+ SegmentOffset() = default;
+ SegmentOffset(uint16_t s, uint32_t o) : segment(s), offset(o) {}
+ uint16_t segment = 0;
+ uint32_t offset = 0;
+};
+
+struct SegmentOffsetLength {
+ SegmentOffsetLength() = default;
+ SegmentOffsetLength(uint16_t s, uint32_t o, uint32_t l)
+ : so(s, o), length(l) {}
+ SegmentOffset so;
+ uint32_t length = 0;
+};
+
+struct VariableInfo {
+ llvm::StringRef name;
+ llvm::codeview::TypeIndex type;
+ llvm::Optional<DWARFExpression> location;
+ llvm::Optional<Variable::RangeList> ranges;
+};
+
+llvm::pdb::PDB_SymType CVSymToPDBSym(llvm::codeview::SymbolKind kind);
+llvm::pdb::PDB_SymType CVTypeToPDBType(llvm::codeview::TypeLeafKind kind);
+
+bool SymbolHasAddress(const llvm::codeview::CVSymbol &sym);
+bool SymbolIsCode(const llvm::codeview::CVSymbol &sym);
+
+SegmentOffset GetSegmentAndOffset(const llvm::codeview::CVSymbol &sym);
+SegmentOffsetLength
+GetSegmentOffsetAndLength(const llvm::codeview::CVSymbol &sym);
+
+template <typename RecordT> bool IsValidRecord(const RecordT &sym) {
+ return true;
+}
+
+inline bool IsValidRecord(const llvm::codeview::ProcRefSym &sym) {
+ // S_PROCREF symbols have 1-based module indices.
+ return sym.Module > 0;
+}
+
+bool IsForwardRefUdt(llvm::codeview::CVType cvt);
+bool IsTagRecord(llvm::codeview::CVType cvt);
+bool IsClassStructUnion(llvm::codeview::CVType cvt);
+
+bool IsForwardRefUdt(const PdbTypeSymId &id, llvm::pdb::TpiStream &tpi);
+bool IsTagRecord(const PdbTypeSymId &id, llvm::pdb::TpiStream &tpi);
+
+lldb::AccessType TranslateMemberAccess(llvm::codeview::MemberAccess access);
+llvm::codeview::TypeIndex GetFieldListIndex(llvm::codeview::CVType cvt);
+llvm::codeview::TypeIndex
+LookThroughModifierRecord(llvm::codeview::CVType modifier);
+
+llvm::StringRef DropNameScope(llvm::StringRef name);
+
+VariableInfo GetVariableNameInfo(llvm::codeview::CVSymbol symbol);
+VariableInfo GetVariableLocationInfo(PdbIndex &index, PdbCompilandSymId var_id, Block& block,
+ lldb::ModuleSP module);
+
+size_t GetTypeSizeForSimpleKind(llvm::codeview::SimpleTypeKind kind);
+lldb::BasicType
+GetCompilerTypeForSimpleKind(llvm::codeview::SimpleTypeKind kind);
+
+PdbTypeSymId GetBestPossibleDecl(PdbTypeSymId id, llvm::pdb::TpiStream &tpi);
+
+size_t GetSizeOfType(PdbTypeSymId id, llvm::pdb::TpiStream &tpi);
+
+} // namespace npdb
+} // namespace lldb_private
+
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
new file mode 100644
index 000000000000..e27d4699ae2f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -0,0 +1,1590 @@
+//===-- SymbolFileNativePDB.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 "SymbolFileNativePDB.h"
+
+#include "clang/AST/Attr.h"
+#include "clang/AST/CharUnits.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Type.h"
+
+#include "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamBuffer.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangASTImporter.h"
+#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
+#include "lldb/Symbol/ClangUtil.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Symbol/VariableList.h"
+
+#include "llvm/DebugInfo/CodeView/CVRecord.h"
+#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
+#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h"
+#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
+#include "llvm/DebugInfo/CodeView/RecordName.h"
+#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
+#include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h"
+#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
+#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
+#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/DebugInfo/PDB/PDBTypes.h"
+#include "llvm/Demangle/MicrosoftDemangle.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/BinaryStreamReader.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+#include "DWARFLocationExpression.h"
+#include "PdbAstBuilder.h"
+#include "PdbSymUid.h"
+#include "PdbUtil.h"
+#include "UdtRecordCompleter.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace npdb;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+static lldb::LanguageType TranslateLanguage(PDB_Lang lang) {
+ switch (lang) {
+ case PDB_Lang::Cpp:
+ return lldb::LanguageType::eLanguageTypeC_plus_plus;
+ case PDB_Lang::C:
+ return lldb::LanguageType::eLanguageTypeC;
+ case PDB_Lang::Swift:
+ return lldb::LanguageType::eLanguageTypeSwift;
+ default:
+ return lldb::LanguageType::eLanguageTypeUnknown;
+ }
+}
+
+static std::unique_ptr<PDBFile> loadPDBFile(std::string PdbPath,
+ llvm::BumpPtrAllocator &Allocator) {
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ErrorOrBuffer =
+ llvm::MemoryBuffer::getFile(PdbPath, /*FileSize=*/-1,
+ /*RequiresNullTerminator=*/false);
+ if (!ErrorOrBuffer)
+ return nullptr;
+ std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(*ErrorOrBuffer);
+
+ llvm::StringRef Path = Buffer->getBufferIdentifier();
+ auto Stream = llvm::make_unique<llvm::MemoryBufferByteStream>(
+ std::move(Buffer), llvm::support::little);
+
+ auto File = llvm::make_unique<PDBFile>(Path, std::move(Stream), Allocator);
+ if (auto EC = File->parseFileHeaders()) {
+ llvm::consumeError(std::move(EC));
+ return nullptr;
+ }
+ if (auto EC = File->parseStreamData()) {
+ llvm::consumeError(std::move(EC));
+ return nullptr;
+ }
+
+ return File;
+}
+
+static std::unique_ptr<PDBFile>
+loadMatchingPDBFile(std::string exe_path, llvm::BumpPtrAllocator &allocator) {
+ // Try to find a matching PDB for an EXE.
+ using namespace llvm::object;
+ auto expected_binary = createBinary(exe_path);
+
+ // If the file isn't a PE/COFF executable, fail.
+ if (!expected_binary) {
+ llvm::consumeError(expected_binary.takeError());
+ return nullptr;
+ }
+ OwningBinary<Binary> binary = std::move(*expected_binary);
+
+ auto *obj = llvm::dyn_cast<llvm::object::COFFObjectFile>(binary.getBinary());
+ if (!obj)
+ return nullptr;
+ const llvm::codeview::DebugInfo *pdb_info = nullptr;
+
+ // If it doesn't have a debug directory, fail.
+ llvm::StringRef pdb_file;
+ auto ec = obj->getDebugPDBInfo(pdb_info, pdb_file);
+ if (ec)
+ return nullptr;
+
+ // if the file doesn't exist, is not a pdb, or doesn't have a matching guid,
+ // fail.
+ llvm::file_magic magic;
+ ec = llvm::identify_magic(pdb_file, magic);
+ if (ec || magic != llvm::file_magic::pdb)
+ return nullptr;
+ std::unique_ptr<PDBFile> pdb = loadPDBFile(pdb_file, allocator);
+ if (!pdb)
+ return nullptr;
+
+ auto expected_info = pdb->getPDBInfoStream();
+ if (!expected_info) {
+ llvm::consumeError(expected_info.takeError());
+ return nullptr;
+ }
+ llvm::codeview::GUID guid;
+ memcpy(&guid, pdb_info->PDB70.Signature, 16);
+
+ if (expected_info->getGuid() != guid)
+ return nullptr;
+ return pdb;
+}
+
+static bool IsFunctionPrologue(const CompilandIndexItem &cci,
+ lldb::addr_t addr) {
+ // FIXME: Implement this.
+ return false;
+}
+
+static bool IsFunctionEpilogue(const CompilandIndexItem &cci,
+ lldb::addr_t addr) {
+ // FIXME: Implement this.
+ return false;
+}
+
+static llvm::StringRef GetSimpleTypeName(SimpleTypeKind kind) {
+ switch (kind) {
+ case SimpleTypeKind::Boolean128:
+ case SimpleTypeKind::Boolean16:
+ case SimpleTypeKind::Boolean32:
+ case SimpleTypeKind::Boolean64:
+ case SimpleTypeKind::Boolean8:
+ return "bool";
+ case SimpleTypeKind::Byte:
+ case SimpleTypeKind::UnsignedCharacter:
+ return "unsigned char";
+ case SimpleTypeKind::NarrowCharacter:
+ return "char";
+ case SimpleTypeKind::SignedCharacter:
+ case SimpleTypeKind::SByte:
+ return "signed char";
+ case SimpleTypeKind::Character16:
+ return "char16_t";
+ case SimpleTypeKind::Character32:
+ return "char32_t";
+ case SimpleTypeKind::Complex80:
+ case SimpleTypeKind::Complex64:
+ case SimpleTypeKind::Complex32:
+ return "complex";
+ case SimpleTypeKind::Float128:
+ case SimpleTypeKind::Float80:
+ return "long double";
+ case SimpleTypeKind::Float64:
+ return "double";
+ case SimpleTypeKind::Float32:
+ return "float";
+ case SimpleTypeKind::Float16:
+ return "single";
+ case SimpleTypeKind::Int128:
+ return "__int128";
+ case SimpleTypeKind::Int64:
+ case SimpleTypeKind::Int64Quad:
+ return "int64_t";
+ case SimpleTypeKind::Int32:
+ return "int";
+ case SimpleTypeKind::Int16:
+ return "short";
+ case SimpleTypeKind::UInt128:
+ return "unsigned __int128";
+ case SimpleTypeKind::UInt64:
+ case SimpleTypeKind::UInt64Quad:
+ return "uint64_t";
+ case SimpleTypeKind::HResult:
+ return "HRESULT";
+ case SimpleTypeKind::UInt32:
+ return "unsigned";
+ case SimpleTypeKind::UInt16:
+ case SimpleTypeKind::UInt16Short:
+ return "unsigned short";
+ case SimpleTypeKind::Int32Long:
+ return "long";
+ case SimpleTypeKind::UInt32Long:
+ return "unsigned long";
+ case SimpleTypeKind::Void:
+ return "void";
+ case SimpleTypeKind::WideCharacter:
+ return "wchar_t";
+ default:
+ return "";
+ }
+}
+
+static bool IsClassRecord(TypeLeafKind kind) {
+ switch (kind) {
+ case LF_STRUCTURE:
+ case LF_CLASS:
+ case LF_INTERFACE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void SymbolFileNativePDB::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ DebuggerInitialize);
+}
+
+void SymbolFileNativePDB::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+void SymbolFileNativePDB::DebuggerInitialize(Debugger &debugger) {}
+
+ConstString SymbolFileNativePDB::GetPluginNameStatic() {
+ static ConstString g_name("native-pdb");
+ return g_name;
+}
+
+const char *SymbolFileNativePDB::GetPluginDescriptionStatic() {
+ return "Microsoft PDB debug symbol cross-platform file reader.";
+}
+
+SymbolFile *SymbolFileNativePDB::CreateInstance(ObjectFile *obj_file) {
+ return new SymbolFileNativePDB(obj_file);
+}
+
+SymbolFileNativePDB::SymbolFileNativePDB(ObjectFile *object_file)
+ : SymbolFile(object_file) {}
+
+SymbolFileNativePDB::~SymbolFileNativePDB() {}
+
+uint32_t SymbolFileNativePDB::CalculateAbilities() {
+ uint32_t abilities = 0;
+ if (!m_obj_file)
+ return 0;
+
+ if (!m_index) {
+ // Lazily load and match the PDB file, but only do this once.
+ std::unique_ptr<PDBFile> file_up =
+ loadMatchingPDBFile(m_obj_file->GetFileSpec().GetPath(), m_allocator);
+
+ if (!file_up) {
+ auto module_sp = m_obj_file->GetModule();
+ if (!module_sp)
+ return 0;
+ // See if any symbol file is specified through `--symfile` option.
+ FileSpec symfile = module_sp->GetSymbolFileFileSpec();
+ if (!symfile)
+ return 0;
+ file_up = loadPDBFile(symfile.GetPath(), m_allocator);
+ }
+
+ if (!file_up)
+ return 0;
+
+ auto expected_index = PdbIndex::create(std::move(file_up));
+ if (!expected_index) {
+ llvm::consumeError(expected_index.takeError());
+ return 0;
+ }
+ m_index = std::move(*expected_index);
+ }
+ if (!m_index)
+ return 0;
+
+ // We don't especially have to be precise here. We only distinguish between
+ // stripped and not stripped.
+ abilities = kAllAbilities;
+
+ if (m_index->dbi().isStripped())
+ abilities &= ~(Blocks | LocalVariables);
+ return abilities;
+}
+
+void SymbolFileNativePDB::InitializeObject() {
+ m_obj_load_address = m_obj_file->GetBaseAddress().GetFileAddress();
+ m_index->SetLoadAddress(m_obj_load_address);
+ m_index->ParseSectionContribs();
+
+ TypeSystem *ts = m_obj_file->GetModule()->GetTypeSystemForLanguage(
+ lldb::eLanguageTypeC_plus_plus);
+ if (ts)
+ ts->SetSymbolFile(this);
+
+ m_ast = llvm::make_unique<PdbAstBuilder>(*m_obj_file, *m_index);
+}
+
+uint32_t SymbolFileNativePDB::GetNumCompileUnits() {
+ const DbiModuleList &modules = m_index->dbi().modules();
+ uint32_t count = modules.getModuleCount();
+ if (count == 0)
+ return count;
+
+ // The linker can inject an additional "dummy" compilation unit into the
+ // PDB. Ignore this special compile unit for our purposes, if it is there.
+ // It is always the last one.
+ DbiModuleDescriptor last = modules.getModuleDescriptor(count - 1);
+ if (last.getModuleName() == "* Linker *")
+ --count;
+ return count;
+}
+
+Block &SymbolFileNativePDB::CreateBlock(PdbCompilandSymId block_id) {
+ CompilandIndexItem *cii = m_index->compilands().GetCompiland(block_id.modi);
+ CVSymbol sym = cii->m_debug_stream.readSymbolAtOffset(block_id.offset);
+
+ if (sym.kind() == S_GPROC32 || sym.kind() == S_LPROC32) {
+ // This is a function. It must be global. Creating the Function entry for
+ // it automatically creates a block for it.
+ CompUnitSP comp_unit = GetOrCreateCompileUnit(*cii);
+ return GetOrCreateFunction(block_id, *comp_unit)->GetBlock(false);
+ }
+
+ lldbassert(sym.kind() == S_BLOCK32);
+
+ // This is a block. Its parent is either a function or another block. In
+ // either case, its parent can be viewed as a block (e.g. a function contains
+ // 1 big block. So just get the parent block and add this block to it.
+ BlockSym block(static_cast<SymbolRecordKind>(sym.kind()));
+ cantFail(SymbolDeserializer::deserializeAs<BlockSym>(sym, block));
+ lldbassert(block.Parent != 0);
+ PdbCompilandSymId parent_id(block_id.modi, block.Parent);
+ Block &parent_block = GetOrCreateBlock(parent_id);
+ lldb::user_id_t opaque_block_uid = toOpaqueUid(block_id);
+ BlockSP child_block = std::make_shared<Block>(opaque_block_uid);
+ parent_block.AddChild(child_block);
+
+ m_ast->GetOrCreateBlockDecl(block_id);
+
+ m_blocks.insert({opaque_block_uid, child_block});
+ return *child_block;
+}
+
+lldb::FunctionSP SymbolFileNativePDB::CreateFunction(PdbCompilandSymId func_id,
+ CompileUnit &comp_unit) {
+ const CompilandIndexItem *cci =
+ m_index->compilands().GetCompiland(func_id.modi);
+ lldbassert(cci);
+ CVSymbol sym_record = cci->m_debug_stream.readSymbolAtOffset(func_id.offset);
+
+ lldbassert(sym_record.kind() == S_LPROC32 || sym_record.kind() == S_GPROC32);
+ SegmentOffsetLength sol = GetSegmentOffsetAndLength(sym_record);
+
+ auto file_vm_addr = m_index->MakeVirtualAddress(sol.so);
+ if (file_vm_addr == LLDB_INVALID_ADDRESS || file_vm_addr == 0)
+ return nullptr;
+
+ AddressRange func_range(file_vm_addr, sol.length,
+ comp_unit.GetModule()->GetSectionList());
+ if (!func_range.GetBaseAddress().IsValid())
+ return nullptr;
+
+ ProcSym proc(static_cast<SymbolRecordKind>(sym_record.kind()));
+ cantFail(SymbolDeserializer::deserializeAs<ProcSym>(sym_record, proc));
+ if (proc.FunctionType == TypeIndex::None())
+ return nullptr;
+ TypeSP func_type = GetOrCreateType(proc.FunctionType);
+ if (!func_type)
+ return nullptr;
+
+ PdbTypeSymId sig_id(proc.FunctionType, false);
+ Mangled mangled(proc.Name);
+ FunctionSP func_sp = std::make_shared<Function>(
+ &comp_unit, toOpaqueUid(func_id), toOpaqueUid(sig_id), mangled,
+ func_type.get(), func_range);
+
+ comp_unit.AddFunction(func_sp);
+
+ m_ast->GetOrCreateFunctionDecl(func_id);
+
+ return func_sp;
+}
+
+CompUnitSP
+SymbolFileNativePDB::CreateCompileUnit(const CompilandIndexItem &cci) {
+ lldb::LanguageType lang =
+ cci.m_compile_opts ? TranslateLanguage(cci.m_compile_opts->getLanguage())
+ : lldb::eLanguageTypeUnknown;
+
+ LazyBool optimized = eLazyBoolNo;
+ if (cci.m_compile_opts && cci.m_compile_opts->hasOptimizations())
+ optimized = eLazyBoolYes;
+
+ llvm::SmallString<64> source_file_name =
+ m_index->compilands().GetMainSourceFile(cci);
+ FileSpec fs(source_file_name);
+
+ CompUnitSP cu_sp =
+ std::make_shared<CompileUnit>(m_obj_file->GetModule(), nullptr, fs,
+ toOpaqueUid(cci.m_id), lang, optimized);
+
+ m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex(
+ cci.m_id.modi, cu_sp);
+ return cu_sp;
+}
+
+lldb::TypeSP SymbolFileNativePDB::CreateModifierType(PdbTypeSymId type_id,
+ const ModifierRecord &mr,
+ CompilerType ct) {
+ TpiStream &stream = m_index->tpi();
+
+ std::string name;
+ if (mr.ModifiedType.isSimple())
+ name = GetSimpleTypeName(mr.ModifiedType.getSimpleKind());
+ else
+ name = computeTypeName(stream.typeCollection(), mr.ModifiedType);
+ Declaration decl;
+ lldb::TypeSP modified_type = GetOrCreateType(mr.ModifiedType);
+
+ return std::make_shared<Type>(toOpaqueUid(type_id), this, ConstString(name),
+ modified_type->GetByteSize(), nullptr,
+ LLDB_INVALID_UID, Type::eEncodingIsUID, decl,
+ ct, Type::eResolveStateFull);
+}
+
+lldb::TypeSP
+SymbolFileNativePDB::CreatePointerType(PdbTypeSymId type_id,
+ const llvm::codeview::PointerRecord &pr,
+ CompilerType ct) {
+ TypeSP pointee = GetOrCreateType(pr.ReferentType);
+ if (!pointee)
+ return nullptr;
+
+ if (pr.isPointerToMember()) {
+ MemberPointerInfo mpi = pr.getMemberInfo();
+ GetOrCreateType(mpi.ContainingType);
+ }
+
+ Declaration decl;
+ return std::make_shared<Type>(toOpaqueUid(type_id), this, ConstString(),
+ pr.getSize(), nullptr, LLDB_INVALID_UID,
+ Type::eEncodingIsUID, decl, ct,
+ Type::eResolveStateFull);
+}
+
+lldb::TypeSP SymbolFileNativePDB::CreateSimpleType(TypeIndex ti,
+ CompilerType ct) {
+ uint64_t uid = toOpaqueUid(PdbTypeSymId(ti, false));
+ if (ti == TypeIndex::NullptrT()) {
+ Declaration decl;
+ return std::make_shared<Type>(
+ uid, this, ConstString("std::nullptr_t"), 0, nullptr, LLDB_INVALID_UID,
+ Type::eEncodingIsUID, decl, ct, Type::eResolveStateFull);
+ }
+
+ if (ti.getSimpleMode() != SimpleTypeMode::Direct) {
+ TypeSP direct_sp = GetOrCreateType(ti.makeDirect());
+ uint32_t pointer_size = 0;
+ switch (ti.getSimpleMode()) {
+ case SimpleTypeMode::FarPointer32:
+ case SimpleTypeMode::NearPointer32:
+ pointer_size = 4;
+ break;
+ case SimpleTypeMode::NearPointer64:
+ pointer_size = 8;
+ break;
+ default:
+ // 128-bit and 16-bit pointers unsupported.
+ return nullptr;
+ }
+ Declaration decl;
+ return std::make_shared<Type>(
+ uid, this, ConstString(), pointer_size, nullptr, LLDB_INVALID_UID,
+ Type::eEncodingIsUID, decl, ct, Type::eResolveStateFull);
+ }
+
+ if (ti.getSimpleKind() == SimpleTypeKind::NotTranslated)
+ return nullptr;
+
+ size_t size = GetTypeSizeForSimpleKind(ti.getSimpleKind());
+ llvm::StringRef type_name = GetSimpleTypeName(ti.getSimpleKind());
+
+ Declaration decl;
+ return std::make_shared<Type>(uid, this, ConstString(type_name), size,
+ nullptr, LLDB_INVALID_UID, Type::eEncodingIsUID,
+ decl, ct, Type::eResolveStateFull);
+}
+
+static std::string GetUnqualifiedTypeName(const TagRecord &record) {
+ if (!record.hasUniqueName()) {
+ MSVCUndecoratedNameParser parser(record.Name);
+ llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs = parser.GetSpecifiers();
+
+ return specs.back().GetBaseName();
+ }
+
+ llvm::ms_demangle::Demangler demangler;
+ StringView sv(record.UniqueName.begin(), record.UniqueName.size());
+ llvm::ms_demangle::TagTypeNode *ttn = demangler.parseTagUniqueName(sv);
+ if (demangler.Error)
+ return record.Name;
+
+ llvm::ms_demangle::IdentifierNode *idn =
+ ttn->QualifiedName->getUnqualifiedIdentifier();
+ return idn->toString();
+}
+
+lldb::TypeSP
+SymbolFileNativePDB::CreateClassStructUnion(PdbTypeSymId type_id,
+ const TagRecord &record,
+ size_t size, CompilerType ct) {
+
+ std::string uname = GetUnqualifiedTypeName(record);
+
+ // FIXME: Search IPI stream for LF_UDT_MOD_SRC_LINE.
+ Declaration decl;
+ return std::make_shared<Type>(toOpaqueUid(type_id), this, ConstString(uname),
+ size, nullptr, LLDB_INVALID_UID,
+ Type::eEncodingIsUID, decl, ct,
+ Type::eResolveStateForward);
+}
+
+lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbTypeSymId type_id,
+ const ClassRecord &cr,
+ CompilerType ct) {
+ return CreateClassStructUnion(type_id, cr, cr.getSize(), ct);
+}
+
+lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbTypeSymId type_id,
+ const UnionRecord &ur,
+ CompilerType ct) {
+ return CreateClassStructUnion(type_id, ur, ur.getSize(), ct);
+}
+
+lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbTypeSymId type_id,
+ const EnumRecord &er,
+ CompilerType ct) {
+ std::string uname = GetUnqualifiedTypeName(er);
+
+ Declaration decl;
+ TypeSP underlying_type = GetOrCreateType(er.UnderlyingType);
+
+ return std::make_shared<lldb_private::Type>(
+ toOpaqueUid(type_id), this, ConstString(uname),
+ underlying_type->GetByteSize(), nullptr, LLDB_INVALID_UID,
+ lldb_private::Type::eEncodingIsUID, decl, ct,
+ lldb_private::Type::eResolveStateForward);
+}
+
+TypeSP SymbolFileNativePDB::CreateArrayType(PdbTypeSymId type_id,
+ const ArrayRecord &ar,
+ CompilerType ct) {
+ TypeSP element_type = GetOrCreateType(ar.ElementType);
+
+ Declaration decl;
+ TypeSP array_sp = std::make_shared<lldb_private::Type>(
+ toOpaqueUid(type_id), this, ConstString(), ar.Size, nullptr,
+ LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, ct,
+ lldb_private::Type::eResolveStateFull);
+ array_sp->SetEncodingType(element_type.get());
+ return array_sp;
+}
+
+
+TypeSP SymbolFileNativePDB::CreateFunctionType(PdbTypeSymId type_id,
+ const MemberFunctionRecord &mfr,
+ CompilerType ct) {
+ Declaration decl;
+ return std::make_shared<lldb_private::Type>(
+ toOpaqueUid(type_id), this, ConstString(), 0, nullptr, LLDB_INVALID_UID,
+ lldb_private::Type::eEncodingIsUID, decl, ct,
+ lldb_private::Type::eResolveStateFull);
+}
+
+TypeSP SymbolFileNativePDB::CreateProcedureType(PdbTypeSymId type_id,
+ const ProcedureRecord &pr,
+ CompilerType ct) {
+ Declaration decl;
+ return std::make_shared<lldb_private::Type>(
+ toOpaqueUid(type_id), this, ConstString(), 0, nullptr, LLDB_INVALID_UID,
+ lldb_private::Type::eEncodingIsUID, decl, ct,
+ lldb_private::Type::eResolveStateFull);
+}
+
+TypeSP SymbolFileNativePDB::CreateType(PdbTypeSymId type_id, CompilerType ct) {
+ if (type_id.index.isSimple())
+ return CreateSimpleType(type_id.index, ct);
+
+ TpiStream &stream = type_id.is_ipi ? m_index->ipi() : m_index->tpi();
+ CVType cvt = stream.getType(type_id.index);
+
+ if (cvt.kind() == LF_MODIFIER) {
+ ModifierRecord modifier;
+ llvm::cantFail(
+ TypeDeserializer::deserializeAs<ModifierRecord>(cvt, modifier));
+ return CreateModifierType(type_id, modifier, ct);
+ }
+
+ if (cvt.kind() == LF_POINTER) {
+ PointerRecord pointer;
+ llvm::cantFail(
+ TypeDeserializer::deserializeAs<PointerRecord>(cvt, pointer));
+ return CreatePointerType(type_id, pointer, ct);
+ }
+
+ if (IsClassRecord(cvt.kind())) {
+ ClassRecord cr;
+ llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, cr));
+ return CreateTagType(type_id, cr, ct);
+ }
+
+ if (cvt.kind() == LF_ENUM) {
+ EnumRecord er;
+ llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, er));
+ return CreateTagType(type_id, er, ct);
+ }
+
+ if (cvt.kind() == LF_UNION) {
+ UnionRecord ur;
+ llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, ur));
+ return CreateTagType(type_id, ur, ct);
+ }
+
+ if (cvt.kind() == LF_ARRAY) {
+ ArrayRecord ar;
+ llvm::cantFail(TypeDeserializer::deserializeAs<ArrayRecord>(cvt, ar));
+ return CreateArrayType(type_id, ar, ct);
+ }
+
+ if (cvt.kind() == LF_PROCEDURE) {
+ ProcedureRecord pr;
+ llvm::cantFail(TypeDeserializer::deserializeAs<ProcedureRecord>(cvt, pr));
+ return CreateProcedureType(type_id, pr, ct);
+ }
+ if (cvt.kind() == LF_MFUNCTION) {
+ MemberFunctionRecord mfr;
+ llvm::cantFail(TypeDeserializer::deserializeAs<MemberFunctionRecord>(cvt, mfr));
+ return CreateFunctionType(type_id, mfr, ct);
+ }
+
+ return nullptr;
+}
+
+TypeSP SymbolFileNativePDB::CreateAndCacheType(PdbTypeSymId type_id) {
+ // If they search for a UDT which is a forward ref, try and resolve the full
+ // decl and just map the forward ref uid to the full decl record.
+ llvm::Optional<PdbTypeSymId> full_decl_uid;
+ if (IsForwardRefUdt(type_id, m_index->tpi())) {
+ auto expected_full_ti =
+ m_index->tpi().findFullDeclForForwardRef(type_id.index);
+ if (!expected_full_ti)
+ llvm::consumeError(expected_full_ti.takeError());
+ else if (*expected_full_ti != type_id.index) {
+ full_decl_uid = PdbTypeSymId(*expected_full_ti, false);
+
+ // It's possible that a lookup would occur for the full decl causing it
+ // to be cached, then a second lookup would occur for the forward decl.
+ // We don't want to create a second full decl, so make sure the full
+ // decl hasn't already been cached.
+ auto full_iter = m_types.find(toOpaqueUid(*full_decl_uid));
+ if (full_iter != m_types.end()) {
+ TypeSP result = full_iter->second;
+ // Map the forward decl to the TypeSP for the full decl so we can take
+ // the fast path next time.
+ m_types[toOpaqueUid(type_id)] = result;
+ return result;
+ }
+ }
+ }
+
+ PdbTypeSymId best_decl_id = full_decl_uid ? *full_decl_uid : type_id;
+
+ clang::QualType qt = m_ast->GetOrCreateType(best_decl_id);
+
+ TypeSP result = CreateType(best_decl_id, m_ast->ToCompilerType(qt));
+ if (!result)
+ return nullptr;
+
+ uint64_t best_uid = toOpaqueUid(best_decl_id);
+ m_types[best_uid] = result;
+ // If we had both a forward decl and a full decl, make both point to the new
+ // type.
+ if (full_decl_uid)
+ m_types[toOpaqueUid(type_id)] = result;
+
+ return result;
+}
+
+TypeSP SymbolFileNativePDB::GetOrCreateType(PdbTypeSymId type_id) {
+ // We can't use try_emplace / overwrite here because the process of creating
+ // a type could create nested types, which could invalidate iterators. So
+ // we have to do a 2-phase lookup / insert.
+ auto iter = m_types.find(toOpaqueUid(type_id));
+ if (iter != m_types.end())
+ return iter->second;
+
+ TypeSP type = CreateAndCacheType(type_id);
+ if (type)
+ m_obj_file->GetModule()->GetTypeList()->Insert(type);
+ return type;
+}
+
+VariableSP SymbolFileNativePDB::CreateGlobalVariable(PdbGlobalSymId var_id) {
+ CVSymbol sym = m_index->symrecords().readRecord(var_id.offset);
+ if (sym.kind() == S_CONSTANT)
+ return CreateConstantSymbol(var_id, sym);
+
+ lldb::ValueType scope = eValueTypeInvalid;
+ TypeIndex ti;
+ llvm::StringRef name;
+ lldb::addr_t addr = 0;
+ uint16_t section = 0;
+ uint32_t offset = 0;
+ bool is_external = false;
+ switch (sym.kind()) {
+ case S_GDATA32:
+ is_external = true;
+ LLVM_FALLTHROUGH;
+ case S_LDATA32: {
+ DataSym ds(sym.kind());
+ llvm::cantFail(SymbolDeserializer::deserializeAs<DataSym>(sym, ds));
+ ti = ds.Type;
+ scope = (sym.kind() == S_GDATA32) ? eValueTypeVariableGlobal
+ : eValueTypeVariableStatic;
+ name = ds.Name;
+ section = ds.Segment;
+ offset = ds.DataOffset;
+ addr = m_index->MakeVirtualAddress(ds.Segment, ds.DataOffset);
+ break;
+ }
+ case S_GTHREAD32:
+ is_external = true;
+ LLVM_FALLTHROUGH;
+ case S_LTHREAD32: {
+ ThreadLocalDataSym tlds(sym.kind());
+ llvm::cantFail(
+ SymbolDeserializer::deserializeAs<ThreadLocalDataSym>(sym, tlds));
+ ti = tlds.Type;
+ name = tlds.Name;
+ section = tlds.Segment;
+ offset = tlds.DataOffset;
+ addr = m_index->MakeVirtualAddress(tlds.Segment, tlds.DataOffset);
+ scope = eValueTypeVariableThreadLocal;
+ break;
+ }
+ default:
+ llvm_unreachable("unreachable!");
+ }
+
+ CompUnitSP comp_unit;
+ llvm::Optional<uint16_t> modi = m_index->GetModuleIndexForVa(addr);
+ if (modi) {
+ CompilandIndexItem &cci = m_index->compilands().GetOrCreateCompiland(*modi);
+ comp_unit = GetOrCreateCompileUnit(cci);
+ }
+
+ Declaration decl;
+ PdbTypeSymId tid(ti, false);
+ SymbolFileTypeSP type_sp =
+ std::make_shared<SymbolFileType>(*this, toOpaqueUid(tid));
+ Variable::RangeList ranges;
+
+ m_ast->GetOrCreateVariableDecl(var_id);
+
+ DWARFExpression location = MakeGlobalLocationExpression(
+ section, offset, GetObjectFile()->GetModule());
+
+ std::string global_name("::");
+ global_name += name;
+ VariableSP var_sp = std::make_shared<Variable>(
+ toOpaqueUid(var_id), name.str().c_str(), global_name.c_str(), type_sp,
+ scope, comp_unit.get(), ranges, &decl, location, is_external, false,
+ false);
+ var_sp->SetLocationIsConstantValueData(false);
+
+ return var_sp;
+}
+
+lldb::VariableSP
+SymbolFileNativePDB::CreateConstantSymbol(PdbGlobalSymId var_id,
+ const CVSymbol &cvs) {
+ TpiStream &tpi = m_index->tpi();
+ ConstantSym constant(cvs.kind());
+
+ llvm::cantFail(SymbolDeserializer::deserializeAs<ConstantSym>(cvs, constant));
+ std::string global_name("::");
+ global_name += constant.Name;
+ PdbTypeSymId tid(constant.Type, false);
+ SymbolFileTypeSP type_sp =
+ std::make_shared<SymbolFileType>(*this, toOpaqueUid(tid));
+
+ Declaration decl;
+ Variable::RangeList ranges;
+ ModuleSP module = GetObjectFile()->GetModule();
+ DWARFExpression location = MakeConstantLocationExpression(
+ constant.Type, tpi, constant.Value, module);
+
+ VariableSP var_sp = std::make_shared<Variable>(
+ toOpaqueUid(var_id), constant.Name.str().c_str(), global_name.c_str(),
+ type_sp, eValueTypeVariableGlobal, module.get(), ranges, &decl, location,
+ false, false, false);
+ var_sp->SetLocationIsConstantValueData(true);
+ return var_sp;
+}
+
+VariableSP
+SymbolFileNativePDB::GetOrCreateGlobalVariable(PdbGlobalSymId var_id) {
+ auto emplace_result = m_global_vars.try_emplace(toOpaqueUid(var_id), nullptr);
+ if (emplace_result.second)
+ emplace_result.first->second = CreateGlobalVariable(var_id);
+
+ return emplace_result.first->second;
+}
+
+lldb::TypeSP SymbolFileNativePDB::GetOrCreateType(TypeIndex ti) {
+ return GetOrCreateType(PdbTypeSymId(ti, false));
+}
+
+FunctionSP SymbolFileNativePDB::GetOrCreateFunction(PdbCompilandSymId func_id,
+ CompileUnit &comp_unit) {
+ auto emplace_result = m_functions.try_emplace(toOpaqueUid(func_id), nullptr);
+ if (emplace_result.second)
+ emplace_result.first->second = CreateFunction(func_id, comp_unit);
+
+ return emplace_result.first->second;
+}
+
+CompUnitSP
+SymbolFileNativePDB::GetOrCreateCompileUnit(const CompilandIndexItem &cci) {
+
+ auto emplace_result =
+ m_compilands.try_emplace(toOpaqueUid(cci.m_id), nullptr);
+ if (emplace_result.second)
+ emplace_result.first->second = CreateCompileUnit(cci);
+
+ lldbassert(emplace_result.first->second);
+ return emplace_result.first->second;
+}
+
+Block &SymbolFileNativePDB::GetOrCreateBlock(PdbCompilandSymId block_id) {
+ auto iter = m_blocks.find(toOpaqueUid(block_id));
+ if (iter != m_blocks.end())
+ return *iter->second;
+
+ return CreateBlock(block_id);
+}
+
+void SymbolFileNativePDB::ParseDeclsForContext(
+ lldb_private::CompilerDeclContext decl_ctx) {
+ clang::DeclContext *context = m_ast->FromCompilerDeclContext(decl_ctx);
+ if (!context)
+ return;
+ m_ast->ParseDeclsForContext(*context);
+}
+
+lldb::CompUnitSP SymbolFileNativePDB::ParseCompileUnitAtIndex(uint32_t index) {
+ if (index >= GetNumCompileUnits())
+ return CompUnitSP();
+ lldbassert(index < UINT16_MAX);
+ if (index >= UINT16_MAX)
+ return nullptr;
+
+ CompilandIndexItem &item = m_index->compilands().GetOrCreateCompiland(index);
+
+ return GetOrCreateCompileUnit(item);
+}
+
+lldb::LanguageType SymbolFileNativePDB::ParseLanguage(CompileUnit &comp_unit) {
+ PdbSymUid uid(comp_unit.GetID());
+ lldbassert(uid.kind() == PdbSymUidKind::Compiland);
+
+ CompilandIndexItem *item =
+ m_index->compilands().GetCompiland(uid.asCompiland().modi);
+ lldbassert(item);
+ if (!item->m_compile_opts)
+ return lldb::eLanguageTypeUnknown;
+
+ return TranslateLanguage(item->m_compile_opts->getLanguage());
+}
+
+void SymbolFileNativePDB::AddSymbols(Symtab &symtab) { return; }
+
+size_t SymbolFileNativePDB::ParseFunctions(CompileUnit &comp_unit) {
+ PdbSymUid uid{comp_unit.GetID()};
+ lldbassert(uid.kind() == PdbSymUidKind::Compiland);
+ uint16_t modi = uid.asCompiland().modi;
+ CompilandIndexItem &cii = m_index->compilands().GetOrCreateCompiland(modi);
+
+ size_t count = comp_unit.GetNumFunctions();
+ const CVSymbolArray &syms = cii.m_debug_stream.getSymbolArray();
+ for (auto iter = syms.begin(); iter != syms.end(); ++iter) {
+ if (iter->kind() != S_LPROC32 && iter->kind() != S_GPROC32)
+ continue;
+
+ PdbCompilandSymId sym_id{modi, iter.offset()};
+
+ FunctionSP func = GetOrCreateFunction(sym_id, comp_unit);
+ }
+
+ size_t new_count = comp_unit.GetNumFunctions();
+ lldbassert(new_count >= count);
+ return new_count - count;
+}
+
+static bool NeedsResolvedCompileUnit(uint32_t resolve_scope) {
+ // If any of these flags are set, we need to resolve the compile unit.
+ uint32_t flags = eSymbolContextCompUnit;
+ flags |= eSymbolContextVariable;
+ flags |= eSymbolContextFunction;
+ flags |= eSymbolContextBlock;
+ flags |= eSymbolContextLineEntry;
+ return (resolve_scope & flags) != 0;
+}
+
+uint32_t SymbolFileNativePDB::ResolveSymbolContext(
+ const Address &addr, SymbolContextItem resolve_scope, SymbolContext &sc) {
+ uint32_t resolved_flags = 0;
+ lldb::addr_t file_addr = addr.GetFileAddress();
+
+ if (NeedsResolvedCompileUnit(resolve_scope)) {
+ llvm::Optional<uint16_t> modi = m_index->GetModuleIndexForVa(file_addr);
+ if (!modi)
+ return 0;
+ CompilandIndexItem *cci = m_index->compilands().GetCompiland(*modi);
+ if (!cci)
+ return 0;
+
+ sc.comp_unit = GetOrCreateCompileUnit(*cci).get();
+ resolved_flags |= eSymbolContextCompUnit;
+ }
+
+ if (resolve_scope & eSymbolContextFunction ||
+ resolve_scope & eSymbolContextBlock) {
+ lldbassert(sc.comp_unit);
+ std::vector<SymbolAndUid> matches = m_index->FindSymbolsByVa(file_addr);
+ // Search the matches in reverse. This way if there are multiple matches
+ // (for example we are 3 levels deep in a nested scope) it will find the
+ // innermost one first.
+ for (const auto &match : llvm::reverse(matches)) {
+ if (match.uid.kind() != PdbSymUidKind::CompilandSym)
+ continue;
+
+ PdbCompilandSymId csid = match.uid.asCompilandSym();
+ CVSymbol cvs = m_index->ReadSymbolRecord(csid);
+ PDB_SymType type = CVSymToPDBSym(cvs.kind());
+ if (type != PDB_SymType::Function && type != PDB_SymType::Block)
+ continue;
+ if (type == PDB_SymType::Function) {
+ sc.function = GetOrCreateFunction(csid, *sc.comp_unit).get();
+ sc.block = sc.GetFunctionBlock();
+ }
+
+ if (type == PDB_SymType::Block) {
+ sc.block = &GetOrCreateBlock(csid);
+ sc.function = sc.block->CalculateSymbolContextFunction();
+ }
+ resolved_flags |= eSymbolContextFunction;
+ resolved_flags |= eSymbolContextBlock;
+ break;
+ }
+ }
+
+ if (resolve_scope & eSymbolContextLineEntry) {
+ lldbassert(sc.comp_unit);
+ if (auto *line_table = sc.comp_unit->GetLineTable()) {
+ if (line_table->FindLineEntryByAddress(addr, sc.line_entry))
+ resolved_flags |= eSymbolContextLineEntry;
+ }
+ }
+
+ return resolved_flags;
+}
+
+uint32_t SymbolFileNativePDB::ResolveSymbolContext(
+ const FileSpec &file_spec, uint32_t line, bool check_inlines,
+ lldb::SymbolContextItem resolve_scope, SymbolContextList &sc_list) {
+ return 0;
+}
+
+static void AppendLineEntryToSequence(LineTable &table, LineSequence &sequence,
+ const CompilandIndexItem &cci,
+ lldb::addr_t base_addr,
+ uint32_t file_number,
+ const LineFragmentHeader &block,
+ const LineNumberEntry &cur) {
+ LineInfo cur_info(cur.Flags);
+
+ if (cur_info.isAlwaysStepInto() || cur_info.isNeverStepInto())
+ return;
+
+ uint64_t addr = base_addr + cur.Offset;
+
+ bool is_statement = cur_info.isStatement();
+ bool is_prologue = IsFunctionPrologue(cci, addr);
+ bool is_epilogue = IsFunctionEpilogue(cci, addr);
+
+ uint32_t lno = cur_info.getStartLine();
+
+ table.AppendLineEntryToSequence(&sequence, addr, lno, 0, file_number,
+ is_statement, false, is_prologue, is_epilogue,
+ false);
+}
+
+static void TerminateLineSequence(LineTable &table,
+ const LineFragmentHeader &block,
+ lldb::addr_t base_addr, uint32_t file_number,
+ uint32_t last_line,
+ std::unique_ptr<LineSequence> seq) {
+ // The end is always a terminal entry, so insert it regardless.
+ table.AppendLineEntryToSequence(seq.get(), base_addr + block.CodeSize,
+ last_line, 0, file_number, false, false,
+ false, false, true);
+ table.InsertSequence(seq.release());
+}
+
+bool SymbolFileNativePDB::ParseLineTable(CompileUnit &comp_unit) {
+ // Unfortunately LLDB is set up to parse the entire compile unit line table
+ // all at once, even if all it really needs is line info for a specific
+ // function. In the future it would be nice if it could set the sc.m_function
+ // member, and we could only get the line info for the function in question.
+ PdbSymUid cu_id(comp_unit.GetID());
+ lldbassert(cu_id.kind() == PdbSymUidKind::Compiland);
+ CompilandIndexItem *cci =
+ m_index->compilands().GetCompiland(cu_id.asCompiland().modi);
+ lldbassert(cci);
+ auto line_table = llvm::make_unique<LineTable>(&comp_unit);
+
+ // This is basically a copy of the .debug$S subsections from all original COFF
+ // object files merged together with address relocations applied. We are
+ // looking for all DEBUG_S_LINES subsections.
+ for (const DebugSubsectionRecord &dssr :
+ cci->m_debug_stream.getSubsectionsArray()) {
+ if (dssr.kind() != DebugSubsectionKind::Lines)
+ continue;
+
+ DebugLinesSubsectionRef lines;
+ llvm::BinaryStreamReader reader(dssr.getRecordData());
+ if (auto EC = lines.initialize(reader)) {
+ llvm::consumeError(std::move(EC));
+ return false;
+ }
+
+ const LineFragmentHeader *lfh = lines.header();
+ uint64_t virtual_addr =
+ m_index->MakeVirtualAddress(lfh->RelocSegment, lfh->RelocOffset);
+
+ const auto &checksums = cci->m_strings.checksums().getArray();
+ const auto &strings = cci->m_strings.strings();
+ for (const LineColumnEntry &group : lines) {
+ // Indices in this structure are actually offsets of records in the
+ // DEBUG_S_FILECHECKSUMS subsection. Those entries then have an index
+ // into the global PDB string table.
+ auto iter = checksums.at(group.NameIndex);
+ if (iter == checksums.end())
+ continue;
+
+ llvm::Expected<llvm::StringRef> efn =
+ strings.getString(iter->FileNameOffset);
+ if (!efn) {
+ llvm::consumeError(efn.takeError());
+ continue;
+ }
+
+ // LLDB wants the index of the file in the list of support files.
+ auto fn_iter = llvm::find(cci->m_file_list, *efn);
+ lldbassert(fn_iter != cci->m_file_list.end());
+ // LLDB support file indices are 1-based.
+ uint32_t file_index =
+ 1 + std::distance(cci->m_file_list.begin(), fn_iter);
+
+ std::unique_ptr<LineSequence> sequence(
+ line_table->CreateLineSequenceContainer());
+ lldbassert(!group.LineNumbers.empty());
+
+ for (const LineNumberEntry &entry : group.LineNumbers) {
+ AppendLineEntryToSequence(*line_table, *sequence, *cci, virtual_addr,
+ file_index, *lfh, entry);
+ }
+ LineInfo last_line(group.LineNumbers.back().Flags);
+ TerminateLineSequence(*line_table, *lfh, virtual_addr, file_index,
+ last_line.getEndLine(), std::move(sequence));
+ }
+ }
+
+ if (line_table->GetSize() == 0)
+ return false;
+
+ comp_unit.SetLineTable(line_table.release());
+ return true;
+}
+
+bool SymbolFileNativePDB::ParseDebugMacros(CompileUnit &comp_unit) {
+ // PDB doesn't contain information about macros
+ return false;
+}
+
+bool SymbolFileNativePDB::ParseSupportFiles(CompileUnit &comp_unit,
+ FileSpecList &support_files) {
+ PdbSymUid cu_id(comp_unit.GetID());
+ lldbassert(cu_id.kind() == PdbSymUidKind::Compiland);
+ CompilandIndexItem *cci =
+ m_index->compilands().GetCompiland(cu_id.asCompiland().modi);
+ lldbassert(cci);
+
+ for (llvm::StringRef f : cci->m_file_list) {
+ FileSpec::Style style =
+ f.startswith("/") ? FileSpec::Style::posix : FileSpec::Style::windows;
+ FileSpec spec(f, style);
+ support_files.Append(spec);
+ }
+
+ llvm::SmallString<64> main_source_file =
+ m_index->compilands().GetMainSourceFile(*cci);
+ FileSpec::Style style = main_source_file.startswith("/")
+ ? FileSpec::Style::posix
+ : FileSpec::Style::windows;
+ FileSpec spec(main_source_file, style);
+ support_files.Insert(0, spec);
+ return true;
+}
+
+bool SymbolFileNativePDB::ParseImportedModules(
+ const SymbolContext &sc, std::vector<SourceModule> &imported_modules) {
+ // PDB does not yet support module debug info
+ return false;
+}
+
+size_t SymbolFileNativePDB::ParseBlocksRecursive(Function &func) {
+ GetOrCreateBlock(PdbSymUid(func.GetID()).asCompilandSym());
+ // FIXME: Parse child blocks
+ return 1;
+}
+
+void SymbolFileNativePDB::DumpClangAST(Stream &s) { m_ast->Dump(s); }
+
+uint32_t SymbolFileNativePDB::FindGlobalVariables(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches, VariableList &variables) {
+ using SymbolAndOffset = std::pair<uint32_t, llvm::codeview::CVSymbol>;
+
+ std::vector<SymbolAndOffset> results = m_index->globals().findRecordsByName(
+ name.GetStringRef(), m_index->symrecords());
+ for (const SymbolAndOffset &result : results) {
+ VariableSP var;
+ switch (result.second.kind()) {
+ case SymbolKind::S_GDATA32:
+ case SymbolKind::S_LDATA32:
+ case SymbolKind::S_GTHREAD32:
+ case SymbolKind::S_LTHREAD32:
+ case SymbolKind::S_CONSTANT: {
+ PdbGlobalSymId global(result.first, false);
+ var = GetOrCreateGlobalVariable(global);
+ variables.AddVariable(var);
+ break;
+ }
+ default:
+ continue;
+ }
+ }
+ return variables.GetSize();
+}
+
+uint32_t SymbolFileNativePDB::FindFunctions(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ FunctionNameType name_type_mask, bool include_inlines, bool append,
+ SymbolContextList &sc_list) {
+ // For now we only support lookup by method name.
+ if (!(name_type_mask & eFunctionNameTypeMethod))
+ return 0;
+
+ using SymbolAndOffset = std::pair<uint32_t, llvm::codeview::CVSymbol>;
+
+ std::vector<SymbolAndOffset> matches = m_index->globals().findRecordsByName(
+ name.GetStringRef(), m_index->symrecords());
+ for (const SymbolAndOffset &match : matches) {
+ if (match.second.kind() != S_PROCREF && match.second.kind() != S_LPROCREF)
+ continue;
+ ProcRefSym proc(match.second.kind());
+ cantFail(SymbolDeserializer::deserializeAs<ProcRefSym>(match.second, proc));
+
+ if (!IsValidRecord(proc))
+ continue;
+
+ CompilandIndexItem &cci =
+ m_index->compilands().GetOrCreateCompiland(proc.modi());
+ SymbolContext sc;
+
+ sc.comp_unit = GetOrCreateCompileUnit(cci).get();
+ PdbCompilandSymId func_id(proc.modi(), proc.SymOffset);
+ sc.function = GetOrCreateFunction(func_id, *sc.comp_unit).get();
+
+ sc_list.Append(sc);
+ }
+
+ return sc_list.GetSize();
+}
+
+uint32_t SymbolFileNativePDB::FindFunctions(const RegularExpression &regex,
+ bool include_inlines, bool append,
+ SymbolContextList &sc_list) {
+ return 0;
+}
+
+uint32_t SymbolFileNativePDB::FindTypes(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ bool append, uint32_t max_matches,
+ llvm::DenseSet<SymbolFile *> &searched_symbol_files, TypeMap &types) {
+ if (!append)
+ types.Clear();
+ if (!name)
+ return 0;
+
+ searched_symbol_files.clear();
+ searched_symbol_files.insert(this);
+
+ // There is an assumption 'name' is not a regex
+ size_t match_count = FindTypesByName(name.GetStringRef(), max_matches, types);
+
+ return match_count;
+}
+
+size_t
+SymbolFileNativePDB::FindTypes(const std::vector<CompilerContext> &context,
+ bool append, TypeMap &types) {
+ return 0;
+}
+
+size_t SymbolFileNativePDB::FindTypesByName(llvm::StringRef name,
+ uint32_t max_matches,
+ TypeMap &types) {
+
+ size_t match_count = 0;
+ std::vector<TypeIndex> matches = m_index->tpi().findRecordsByName(name);
+ if (max_matches > 0 && max_matches < matches.size())
+ matches.resize(max_matches);
+
+ for (TypeIndex ti : matches) {
+ TypeSP type = GetOrCreateType(ti);
+ if (!type)
+ continue;
+
+ types.Insert(type);
+ ++match_count;
+ }
+ return match_count;
+}
+
+size_t SymbolFileNativePDB::ParseTypes(CompileUnit &comp_unit) {
+ // Only do the full type scan the first time.
+ if (m_done_full_type_scan)
+ return 0;
+
+ size_t old_count = m_obj_file->GetModule()->GetTypeList()->GetSize();
+ LazyRandomTypeCollection &types = m_index->tpi().typeCollection();
+
+ // First process the entire TPI stream.
+ for (auto ti = types.getFirst(); ti; ti = types.getNext(*ti)) {
+ TypeSP type = GetOrCreateType(*ti);
+ if (type)
+ (void)type->GetFullCompilerType();
+ }
+
+ // Next look for S_UDT records in the globals stream.
+ for (const uint32_t gid : m_index->globals().getGlobalsTable()) {
+ PdbGlobalSymId global{gid, false};
+ CVSymbol sym = m_index->ReadSymbolRecord(global);
+ if (sym.kind() != S_UDT)
+ continue;
+
+ UDTSym udt = llvm::cantFail(SymbolDeserializer::deserializeAs<UDTSym>(sym));
+ bool is_typedef = true;
+ if (IsTagRecord(PdbTypeSymId{udt.Type, false}, m_index->tpi())) {
+ CVType cvt = m_index->tpi().getType(udt.Type);
+ llvm::StringRef name = CVTagRecord::create(cvt).name();
+ if (name == udt.Name)
+ is_typedef = false;
+ }
+
+ if (is_typedef)
+ GetOrCreateTypedef(global);
+ }
+
+ size_t new_count = m_obj_file->GetModule()->GetTypeList()->GetSize();
+
+ m_done_full_type_scan = true;
+
+ return new_count - old_count;
+}
+
+size_t
+SymbolFileNativePDB::ParseVariablesForCompileUnit(CompileUnit &comp_unit,
+ VariableList &variables) {
+ PdbSymUid sym_uid(comp_unit.GetID());
+ lldbassert(sym_uid.kind() == PdbSymUidKind::Compiland);
+ return 0;
+}
+
+VariableSP SymbolFileNativePDB::CreateLocalVariable(PdbCompilandSymId scope_id,
+ PdbCompilandSymId var_id,
+ bool is_param) {
+ ModuleSP module = GetObjectFile()->GetModule();
+ Block &block = GetOrCreateBlock(scope_id);
+ VariableInfo var_info =
+ GetVariableLocationInfo(*m_index, var_id, block, module);
+ if (!var_info.location || !var_info.ranges)
+ return nullptr;
+
+ CompilandIndexItem *cii = m_index->compilands().GetCompiland(var_id.modi);
+ CompUnitSP comp_unit_sp = GetOrCreateCompileUnit(*cii);
+ TypeSP type_sp = GetOrCreateType(var_info.type);
+ std::string name = var_info.name.str();
+ Declaration decl;
+ SymbolFileTypeSP sftype =
+ std::make_shared<SymbolFileType>(*this, type_sp->GetID());
+
+ ValueType var_scope =
+ is_param ? eValueTypeVariableArgument : eValueTypeVariableLocal;
+ VariableSP var_sp = std::make_shared<Variable>(
+ toOpaqueUid(var_id), name.c_str(), name.c_str(), sftype, var_scope,
+ comp_unit_sp.get(), *var_info.ranges, &decl, *var_info.location, false,
+ false, false);
+
+ if (!is_param)
+ m_ast->GetOrCreateVariableDecl(scope_id, var_id);
+
+ m_local_variables[toOpaqueUid(var_id)] = var_sp;
+ return var_sp;
+}
+
+VariableSP SymbolFileNativePDB::GetOrCreateLocalVariable(
+ PdbCompilandSymId scope_id, PdbCompilandSymId var_id, bool is_param) {
+ auto iter = m_local_variables.find(toOpaqueUid(var_id));
+ if (iter != m_local_variables.end())
+ return iter->second;
+
+ return CreateLocalVariable(scope_id, var_id, is_param);
+}
+
+TypeSP SymbolFileNativePDB::CreateTypedef(PdbGlobalSymId id) {
+ CVSymbol sym = m_index->ReadSymbolRecord(id);
+ lldbassert(sym.kind() == SymbolKind::S_UDT);
+
+ UDTSym udt = llvm::cantFail(SymbolDeserializer::deserializeAs<UDTSym>(sym));
+
+ TypeSP target_type = GetOrCreateType(udt.Type);
+
+ (void)m_ast->GetOrCreateTypedefDecl(id);
+
+ Declaration decl;
+ return std::make_shared<lldb_private::Type>(
+ toOpaqueUid(id), this, ConstString(udt.Name), target_type->GetByteSize(),
+ nullptr, target_type->GetID(), lldb_private::Type::eEncodingIsTypedefUID,
+ decl, target_type->GetForwardCompilerType(),
+ lldb_private::Type::eResolveStateForward);
+}
+
+TypeSP SymbolFileNativePDB::GetOrCreateTypedef(PdbGlobalSymId id) {
+ auto iter = m_types.find(toOpaqueUid(id));
+ if (iter != m_types.end())
+ return iter->second;
+
+ return CreateTypedef(id);
+}
+
+size_t SymbolFileNativePDB::ParseVariablesForBlock(PdbCompilandSymId block_id) {
+ Block &block = GetOrCreateBlock(block_id);
+
+ size_t count = 0;
+
+ CompilandIndexItem *cii = m_index->compilands().GetCompiland(block_id.modi);
+ CVSymbol sym = cii->m_debug_stream.readSymbolAtOffset(block_id.offset);
+ uint32_t params_remaining = 0;
+ switch (sym.kind()) {
+ case S_GPROC32:
+ case S_LPROC32: {
+ ProcSym proc(static_cast<SymbolRecordKind>(sym.kind()));
+ cantFail(SymbolDeserializer::deserializeAs<ProcSym>(sym, proc));
+ CVType signature = m_index->tpi().getType(proc.FunctionType);
+ ProcedureRecord sig;
+ cantFail(TypeDeserializer::deserializeAs<ProcedureRecord>(signature, sig));
+ params_remaining = sig.getParameterCount();
+ break;
+ }
+ case S_BLOCK32:
+ break;
+ default:
+ lldbassert(false && "Symbol is not a block!");
+ return 0;
+ }
+
+ VariableListSP variables = block.GetBlockVariableList(false);
+ if (!variables) {
+ variables = std::make_shared<VariableList>();
+ block.SetVariableList(variables);
+ }
+
+ CVSymbolArray syms = limitSymbolArrayToScope(
+ cii->m_debug_stream.getSymbolArray(), block_id.offset);
+
+ // Skip the first record since it's a PROC32 or BLOCK32, and there's
+ // no point examining it since we know it's not a local variable.
+ syms.drop_front();
+ auto iter = syms.begin();
+ auto end = syms.end();
+
+ while (iter != end) {
+ uint32_t record_offset = iter.offset();
+ CVSymbol variable_cvs = *iter;
+ PdbCompilandSymId child_sym_id(block_id.modi, record_offset);
+ ++iter;
+
+ // If this is a block, recurse into its children and then skip it.
+ if (variable_cvs.kind() == S_BLOCK32) {
+ uint32_t block_end = getScopeEndOffset(variable_cvs);
+ count += ParseVariablesForBlock(child_sym_id);
+ iter = syms.at(block_end);
+ continue;
+ }
+
+ bool is_param = params_remaining > 0;
+ VariableSP variable;
+ switch (variable_cvs.kind()) {
+ case S_REGREL32:
+ case S_REGISTER:
+ case S_LOCAL:
+ variable = GetOrCreateLocalVariable(block_id, child_sym_id, is_param);
+ if (is_param)
+ --params_remaining;
+ if (variable)
+ variables->AddVariableIfUnique(variable);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Pass false for set_children, since we call this recursively so that the
+ // children will call this for themselves.
+ block.SetDidParseVariables(true, false);
+
+ return count;
+}
+
+size_t SymbolFileNativePDB::ParseVariablesForContext(const SymbolContext &sc) {
+ lldbassert(sc.function || sc.comp_unit);
+
+ VariableListSP variables;
+ if (sc.block) {
+ PdbSymUid block_id(sc.block->GetID());
+
+ size_t count = ParseVariablesForBlock(block_id.asCompilandSym());
+ return count;
+ }
+
+ if (sc.function) {
+ PdbSymUid block_id(sc.function->GetID());
+
+ size_t count = ParseVariablesForBlock(block_id.asCompilandSym());
+ return count;
+ }
+
+ if (sc.comp_unit) {
+ variables = sc.comp_unit->GetVariableList(false);
+ if (!variables) {
+ variables = std::make_shared<VariableList>();
+ sc.comp_unit->SetVariableList(variables);
+ }
+ return ParseVariablesForCompileUnit(*sc.comp_unit, *variables);
+ }
+
+ llvm_unreachable("Unreachable!");
+}
+
+CompilerDecl SymbolFileNativePDB::GetDeclForUID(lldb::user_id_t uid) {
+ clang::Decl *decl = m_ast->GetOrCreateDeclForUid(PdbSymUid(uid));
+
+ return m_ast->ToCompilerDecl(*decl);
+}
+
+CompilerDeclContext
+SymbolFileNativePDB::GetDeclContextForUID(lldb::user_id_t uid) {
+ clang::DeclContext *context =
+ m_ast->GetOrCreateDeclContextForUid(PdbSymUid(uid));
+ if (!context)
+ return {};
+
+ return m_ast->ToCompilerDeclContext(*context);
+}
+
+CompilerDeclContext
+SymbolFileNativePDB::GetDeclContextContainingUID(lldb::user_id_t uid) {
+ clang::DeclContext *context = m_ast->GetParentDeclContext(PdbSymUid(uid));
+ return m_ast->ToCompilerDeclContext(*context);
+}
+
+Type *SymbolFileNativePDB::ResolveTypeUID(lldb::user_id_t type_uid) {
+ auto iter = m_types.find(type_uid);
+ // lldb should not be passing us non-sensical type uids. the only way it
+ // could have a type uid in the first place is if we handed it out, in which
+ // case we should know about the type. However, that doesn't mean we've
+ // instantiated it yet. We can vend out a UID for a future type. So if the
+ // type doesn't exist, let's instantiate it now.
+ if (iter != m_types.end())
+ return &*iter->second;
+
+ PdbSymUid uid(type_uid);
+ lldbassert(uid.kind() == PdbSymUidKind::Type);
+ PdbTypeSymId type_id = uid.asTypeSym();
+ if (type_id.index.isNoneType())
+ return nullptr;
+
+ TypeSP type_sp = CreateAndCacheType(type_id);
+ return &*type_sp;
+}
+
+llvm::Optional<SymbolFile::ArrayInfo>
+SymbolFileNativePDB::GetDynamicArrayInfoForUID(
+ lldb::user_id_t type_uid, const lldb_private::ExecutionContext *exe_ctx) {
+ return llvm::None;
+}
+
+
+bool SymbolFileNativePDB::CompleteType(CompilerType &compiler_type) {
+ clang::QualType qt =
+ clang::QualType::getFromOpaquePtr(compiler_type.GetOpaqueQualType());
+
+ return m_ast->CompleteType(qt);
+}
+
+size_t SymbolFileNativePDB::GetTypes(lldb_private::SymbolContextScope *sc_scope,
+ TypeClass type_mask,
+ lldb_private::TypeList &type_list) {
+ return 0;
+}
+
+CompilerDeclContext
+SymbolFileNativePDB::FindNamespace(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx) {
+ return {};
+}
+
+TypeSystem *
+SymbolFileNativePDB::GetTypeSystemForLanguage(lldb::LanguageType language) {
+ auto type_system =
+ m_obj_file->GetModule()->GetTypeSystemForLanguage(language);
+ if (type_system)
+ type_system->SetSymbolFile(this);
+ return type_system;
+}
+
+ConstString SymbolFileNativePDB::GetPluginName() {
+ static ConstString g_name("pdb");
+ return g_name;
+}
+
+uint32_t SymbolFileNativePDB::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
new file mode 100644
index 000000000000..20daff219a0a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
@@ -0,0 +1,241 @@
+//===-- SymbolFileNativePDB.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_PLUGINS_SYMBOLFILE_NATIVEPDB_SYMBOLFILENATIVEPDB_H
+#define LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_SYMBOLFILENATIVEPDB_H
+
+#include "lldb/Symbol/ClangASTImporter.h"
+#include "lldb/Symbol/SymbolFile.h"
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/DebugInfo/CodeView/CVRecord.h"
+#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
+#include "llvm/DebugInfo/PDB/PDBTypes.h"
+
+#include "CompileUnitIndex.h"
+#include "PdbIndex.h"
+
+namespace clang {
+class TagDecl;
+}
+
+namespace llvm {
+namespace codeview {
+class ClassRecord;
+class EnumRecord;
+class ModifierRecord;
+class PointerRecord;
+struct UnionRecord;
+} // namespace codeview
+} // namespace llvm
+
+namespace lldb_private {
+class ClangASTImporter;
+
+namespace npdb {
+class PdbAstBuilder;
+
+class SymbolFileNativePDB : public SymbolFile {
+ friend class UdtRecordCompleter;
+
+public:
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static void DebuggerInitialize(Debugger &debugger);
+
+ static ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static SymbolFile *CreateInstance(ObjectFile *obj_file);
+
+ // Constructors and Destructors
+ SymbolFileNativePDB(ObjectFile *ofile);
+
+ ~SymbolFileNativePDB() override;
+
+ uint32_t CalculateAbilities() override;
+
+ void InitializeObject() override;
+
+ // Compile Unit function calls
+
+ uint32_t GetNumCompileUnits() override;
+
+ void
+ ParseDeclsForContext(lldb_private::CompilerDeclContext decl_ctx) override;
+
+ lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override;
+
+ lldb::LanguageType
+ ParseLanguage(lldb_private::CompileUnit &comp_unit) override;
+
+ size_t ParseFunctions(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseLineTable(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseDebugMacros(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseSupportFiles(lldb_private::CompileUnit &comp_unit,
+ FileSpecList &support_files) override;
+ size_t ParseTypes(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseImportedModules(
+ const SymbolContext &sc,
+ std::vector<lldb_private::SourceModule> &imported_modules) override;
+
+ size_t ParseBlocksRecursive(Function &func) override;
+
+ uint32_t FindGlobalVariables(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches,
+ VariableList &variables) override;
+
+ size_t ParseVariablesForContext(const SymbolContext &sc) override;
+
+ void AddSymbols(Symtab &symtab) override;
+
+ CompilerDecl GetDeclForUID(lldb::user_id_t uid) override;
+ CompilerDeclContext GetDeclContextForUID(lldb::user_id_t uid) override;
+ CompilerDeclContext GetDeclContextContainingUID(lldb::user_id_t uid) override;
+ Type *ResolveTypeUID(lldb::user_id_t type_uid) override;
+ llvm::Optional<ArrayInfo> GetDynamicArrayInfoForUID(
+ lldb::user_id_t type_uid,
+ const lldb_private::ExecutionContext *exe_ctx) override;
+
+ bool CompleteType(CompilerType &compiler_type) override;
+ uint32_t ResolveSymbolContext(const Address &so_addr,
+ lldb::SymbolContextItem resolve_scope,
+ SymbolContext &sc) override;
+ uint32_t ResolveSymbolContext(const FileSpec &file_spec, uint32_t line,
+ bool check_inlines,
+ lldb::SymbolContextItem resolve_scope,
+ SymbolContextList &sc_list) override;
+
+ size_t GetTypes(SymbolContextScope *sc_scope, lldb::TypeClass type_mask,
+ TypeList &type_list) override;
+
+ uint32_t FindFunctions(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx,
+ lldb::FunctionNameType name_type_mask,
+ bool include_inlines, bool append,
+ SymbolContextList &sc_list) override;
+
+ uint32_t FindFunctions(const RegularExpression &regex, bool include_inlines,
+ bool append, SymbolContextList &sc_list) override;
+
+ uint32_t FindTypes(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx, bool append,
+ uint32_t max_matches,
+ llvm::DenseSet<SymbolFile *> &searched_symbol_files,
+ TypeMap &types) override;
+
+ size_t FindTypes(const std::vector<CompilerContext> &context, bool append,
+ TypeMap &types) override;
+
+ TypeSystem *GetTypeSystemForLanguage(lldb::LanguageType language) override;
+
+ CompilerDeclContext
+ FindNamespace(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx) override;
+
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ llvm::pdb::PDBFile &GetPDBFile() { return m_index->pdb(); }
+ const llvm::pdb::PDBFile &GetPDBFile() const { return m_index->pdb(); }
+
+ void DumpClangAST(Stream &s) override;
+
+private:
+
+ size_t FindTypesByName(llvm::StringRef name, uint32_t max_matches,
+ TypeMap &types);
+
+ lldb::TypeSP CreateModifierType(PdbTypeSymId type_id,
+ const llvm::codeview::ModifierRecord &mr,
+ CompilerType ct);
+ lldb::TypeSP CreatePointerType(PdbTypeSymId type_id,
+ const llvm::codeview::PointerRecord &pr,
+ CompilerType ct);
+ lldb::TypeSP CreateSimpleType(llvm::codeview::TypeIndex ti, CompilerType ct);
+ lldb::TypeSP CreateTagType(PdbTypeSymId type_id,
+ const llvm::codeview::ClassRecord &cr,
+ CompilerType ct);
+ lldb::TypeSP CreateTagType(PdbTypeSymId type_id,
+ const llvm::codeview::EnumRecord &er,
+ CompilerType ct);
+ lldb::TypeSP CreateTagType(PdbTypeSymId type_id,
+ const llvm::codeview::UnionRecord &ur,
+ CompilerType ct);
+ lldb::TypeSP CreateArrayType(PdbTypeSymId type_id,
+ const llvm::codeview::ArrayRecord &ar,
+ CompilerType ct);
+ lldb::TypeSP CreateFunctionType(PdbTypeSymId type_id,
+ const llvm::codeview::MemberFunctionRecord &pr,
+ CompilerType ct);
+ lldb::TypeSP CreateProcedureType(PdbTypeSymId type_id,
+ const llvm::codeview::ProcedureRecord &pr,
+ CompilerType ct);
+ lldb::TypeSP CreateClassStructUnion(PdbTypeSymId type_id,
+ const llvm::codeview::TagRecord &record,
+ size_t size, CompilerType ct);
+
+ lldb::FunctionSP GetOrCreateFunction(PdbCompilandSymId func_id,
+ CompileUnit &comp_unit);
+ lldb::CompUnitSP GetOrCreateCompileUnit(const CompilandIndexItem &cci);
+ lldb::TypeSP GetOrCreateType(PdbTypeSymId type_id);
+ lldb::TypeSP GetOrCreateType(llvm::codeview::TypeIndex ti);
+ lldb::VariableSP GetOrCreateGlobalVariable(PdbGlobalSymId var_id);
+ Block &GetOrCreateBlock(PdbCompilandSymId block_id);
+ lldb::VariableSP GetOrCreateLocalVariable(PdbCompilandSymId scope_id,
+ PdbCompilandSymId var_id,
+ bool is_param);
+ lldb::TypeSP GetOrCreateTypedef(PdbGlobalSymId id);
+
+ lldb::FunctionSP CreateFunction(PdbCompilandSymId func_id,
+ CompileUnit &comp_unit);
+ Block &CreateBlock(PdbCompilandSymId block_id);
+ lldb::VariableSP CreateLocalVariable(PdbCompilandSymId scope_id,
+ PdbCompilandSymId var_id, bool is_param);
+ lldb::TypeSP CreateTypedef(PdbGlobalSymId id);
+ lldb::CompUnitSP CreateCompileUnit(const CompilandIndexItem &cci);
+ lldb::TypeSP CreateType(PdbTypeSymId type_id, CompilerType ct);
+ lldb::TypeSP CreateAndCacheType(PdbTypeSymId type_id);
+ lldb::VariableSP CreateGlobalVariable(PdbGlobalSymId var_id);
+ lldb::VariableSP CreateConstantSymbol(PdbGlobalSymId var_id,
+ const llvm::codeview::CVSymbol &cvs);
+ size_t ParseVariablesForCompileUnit(CompileUnit &comp_unit,
+ VariableList &variables);
+ size_t ParseVariablesForBlock(PdbCompilandSymId block_id);
+
+ llvm::BumpPtrAllocator m_allocator;
+
+ lldb::addr_t m_obj_load_address = 0;
+ bool m_done_full_type_scan = false;
+
+ std::unique_ptr<PdbIndex> m_index;
+
+ std::unique_ptr<PdbAstBuilder> m_ast;
+
+ llvm::DenseMap<lldb::user_id_t, lldb::VariableSP> m_global_vars;
+ llvm::DenseMap<lldb::user_id_t, lldb::VariableSP> m_local_variables;
+ llvm::DenseMap<lldb::user_id_t, lldb::BlockSP> m_blocks;
+ llvm::DenseMap<lldb::user_id_t, lldb::FunctionSP> m_functions;
+ llvm::DenseMap<lldb::user_id_t, lldb::CompUnitSP> m_compilands;
+ llvm::DenseMap<lldb::user_id_t, lldb::TypeSP> m_types;
+};
+
+} // namespace npdb
+} // namespace lldb_private
+
+#endif // lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp
new file mode 100644
index 000000000000..3c494dc83986
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp
@@ -0,0 +1,236 @@
+#include "UdtRecordCompleter.h"
+
+#include "PdbAstBuilder.h"
+#include "PdbIndex.h"
+#include "PdbSymUid.h"
+#include "PdbUtil.h"
+
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangASTImporter.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
+
+#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
+#include "llvm/DebugInfo/CodeView/TypeIndex.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/DebugInfo/PDB/PDBTypes.h"
+
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::npdb;
+
+using Error = llvm::Error;
+
+UdtRecordCompleter::UdtRecordCompleter(PdbTypeSymId id,
+ CompilerType &derived_ct,
+ clang::TagDecl &tag_decl,
+ PdbAstBuilder &ast_builder,
+ TpiStream &tpi)
+ : m_id(id), m_derived_ct(derived_ct), m_tag_decl(tag_decl),
+ m_ast_builder(ast_builder), m_tpi(tpi) {
+ CVType cvt = m_tpi.getType(m_id.index);
+ switch (cvt.kind()) {
+ case LF_ENUM:
+ llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, m_cvr.er));
+ break;
+ case LF_UNION:
+ llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, m_cvr.ur));
+ break;
+ case LF_CLASS:
+ case LF_STRUCTURE:
+ llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, m_cvr.cr));
+ break;
+ default:
+ llvm_unreachable("unreachable!");
+ }
+}
+
+clang::QualType UdtRecordCompleter::AddBaseClassForTypeIndex(
+ llvm::codeview::TypeIndex ti, llvm::codeview::MemberAccess access,
+ llvm::Optional<uint64_t> vtable_idx) {
+ PdbTypeSymId type_id(ti);
+ clang::QualType qt = m_ast_builder.GetOrCreateType(type_id);
+
+ CVType udt_cvt = m_tpi.getType(ti);
+
+ std::unique_ptr<clang::CXXBaseSpecifier> base_spec =
+ m_ast_builder.clang().CreateBaseClassSpecifier(
+ qt.getAsOpaquePtr(), TranslateMemberAccess(access),
+ vtable_idx.hasValue(), udt_cvt.kind() == LF_CLASS);
+ lldbassert(base_spec);
+
+ m_bases.push_back(
+ std::make_pair(vtable_idx.getValueOr(0), std::move(base_spec)));
+
+ return qt;
+}
+
+void UdtRecordCompleter::AddMethod(llvm::StringRef name, TypeIndex type_idx,
+ MemberAccess access, MethodOptions options,
+ MemberAttributes attrs) {
+ clang::QualType method_qt =
+ m_ast_builder.GetOrCreateType(PdbTypeSymId(type_idx));
+ m_ast_builder.CompleteType(method_qt);
+
+ lldb::AccessType access_type = TranslateMemberAccess(access);
+ bool is_artificial = (options & MethodOptions::CompilerGenerated) ==
+ MethodOptions::CompilerGenerated;
+ m_ast_builder.clang().AddMethodToCXXRecordType(
+ m_derived_ct.GetOpaqueQualType(), name.data(), nullptr,
+ m_ast_builder.ToCompilerType(method_qt), access_type, attrs.isVirtual(),
+ attrs.isStatic(), false, false, false, is_artificial);
+}
+
+Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr,
+ BaseClassRecord &base) {
+ clang::QualType base_qt =
+ AddBaseClassForTypeIndex(base.Type, base.getAccess());
+
+ auto decl =
+ m_ast_builder.clang().GetAsCXXRecordDecl(base_qt.getAsOpaquePtr());
+ lldbassert(decl);
+
+ auto offset = clang::CharUnits::fromQuantity(base.getBaseOffset());
+ m_layout.base_offsets.insert(std::make_pair(decl, offset));
+
+ return llvm::Error::success();
+}
+
+Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr,
+ VirtualBaseClassRecord &base) {
+ AddBaseClassForTypeIndex(base.BaseType, base.getAccess(), base.VTableIndex);
+
+ return Error::success();
+}
+
+Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr,
+ ListContinuationRecord &cont) {
+ return Error::success();
+}
+
+Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr,
+ VFPtrRecord &vfptr) {
+ return Error::success();
+}
+
+Error UdtRecordCompleter::visitKnownMember(
+ CVMemberRecord &cvr, StaticDataMemberRecord &static_data_member) {
+ clang::QualType member_type =
+ m_ast_builder.GetOrCreateType(PdbTypeSymId(static_data_member.Type));
+
+ m_ast_builder.CompleteType(member_type);
+
+ CompilerType member_ct = m_ast_builder.ToCompilerType(member_type);
+
+ lldb::AccessType access =
+ TranslateMemberAccess(static_data_member.getAccess());
+ ClangASTContext::AddVariableToRecordType(
+ m_derived_ct, static_data_member.Name, member_ct, access);
+
+ // FIXME: Add a PdbSymUid namespace for field list members and update
+ // the m_uid_to_decl map with this decl.
+ return Error::success();
+}
+
+Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr,
+ NestedTypeRecord &nested) {
+ return Error::success();
+}
+
+Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr,
+ DataMemberRecord &data_member) {
+
+ uint64_t offset = data_member.FieldOffset * 8;
+ uint32_t bitfield_width = 0;
+
+ TypeIndex ti(data_member.Type);
+ if (!ti.isSimple()) {
+ CVType cvt = m_tpi.getType(ti);
+ if (cvt.kind() == LF_BITFIELD) {
+ BitFieldRecord bfr;
+ llvm::cantFail(TypeDeserializer::deserializeAs<BitFieldRecord>(cvt, bfr));
+ offset += bfr.BitOffset;
+ bitfield_width = bfr.BitSize;
+ ti = bfr.Type;
+ }
+ }
+
+ clang::QualType member_qt = m_ast_builder.GetOrCreateType(PdbTypeSymId(ti));
+ m_ast_builder.CompleteType(member_qt);
+
+ lldb::AccessType access = TranslateMemberAccess(data_member.getAccess());
+
+ clang::FieldDecl *decl = ClangASTContext::AddFieldToRecordType(
+ m_derived_ct, data_member.Name, m_ast_builder.ToCompilerType(member_qt),
+ access, bitfield_width);
+ // FIXME: Add a PdbSymUid namespace for field list members and update
+ // the m_uid_to_decl map with this decl.
+
+ m_layout.field_offsets.insert(std::make_pair(decl, offset));
+
+ return Error::success();
+}
+
+Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr,
+ OneMethodRecord &one_method) {
+ AddMethod(one_method.Name, one_method.Type, one_method.getAccess(),
+ one_method.getOptions(), one_method.Attrs);
+
+ return Error::success();
+}
+
+Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr,
+ OverloadedMethodRecord &overloaded) {
+ TypeIndex method_list_idx = overloaded.MethodList;
+
+ CVType method_list_type = m_tpi.getType(method_list_idx);
+ assert(method_list_type.kind() == LF_METHODLIST);
+
+ MethodOverloadListRecord method_list;
+ llvm::cantFail(TypeDeserializer::deserializeAs<MethodOverloadListRecord>(
+ method_list_type, method_list));
+
+ for (const OneMethodRecord &method : method_list.Methods)
+ AddMethod(overloaded.Name, method.Type, method.getAccess(),
+ method.getOptions(), method.Attrs);
+
+ return Error::success();
+}
+
+Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr,
+ EnumeratorRecord &enumerator) {
+ Declaration decl;
+ llvm::StringRef name = DropNameScope(enumerator.getName());
+
+ m_ast_builder.clang().AddEnumerationValueToEnumerationType(
+ m_derived_ct, decl, name.str().c_str(), enumerator.Value);
+ return Error::success();
+}
+
+void UdtRecordCompleter::complete() {
+ // Ensure the correct order for virtual bases.
+ std::stable_sort(m_bases.begin(), m_bases.end(),
+ [](const IndexedBase &lhs, const IndexedBase &rhs) {
+ return lhs.first < rhs.first;
+ });
+
+ std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> bases;
+ bases.reserve(m_bases.size());
+ for (auto &ib : m_bases)
+ bases.push_back(std::move(ib.second));
+
+ ClangASTContext &clang = m_ast_builder.clang();
+ clang.TransferBaseClasses(m_derived_ct.GetOpaqueQualType(), std::move(bases));
+
+ clang.AddMethodOverridesForCXXRecordType(m_derived_ct.GetOpaqueQualType());
+ ClangASTContext::BuildIndirectFields(m_derived_ct);
+ ClangASTContext::CompleteTagDeclarationDefinition(m_derived_ct);
+
+ if (auto *record_decl = llvm::dyn_cast<clang::CXXRecordDecl>(&m_tag_decl)) {
+ m_ast_builder.importer().InsertRecordDecl(record_decl, m_layout);
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h
new file mode 100644
index 000000000000..55397582209b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h
@@ -0,0 +1,82 @@
+//===-- UdtRecordCompleter.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_PLUGINS_SYMBOLFILE_NATIVEPDB_UDTRECORDCOMPLETER_H
+#define LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_UDTRECORDCOMPLETER_H
+
+#include "lldb/Symbol/ClangASTImporter.h"
+#include "llvm/DebugInfo/CodeView/CVRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
+
+#include "PdbSymUid.h"
+
+namespace clang {
+class CXXBaseSpecifier;
+class QualType;
+class TagDecl;
+} // namespace clang
+
+namespace llvm {
+namespace pdb {
+class TpiStream;
+}
+} // namespace llvm
+
+namespace lldb_private {
+class Type;
+class CompilerType;
+namespace npdb {
+class PdbAstBuilder;
+
+class UdtRecordCompleter : public llvm::codeview::TypeVisitorCallbacks {
+ using IndexedBase =
+ std::pair<uint64_t, std::unique_ptr<clang::CXXBaseSpecifier>>;
+
+ union UdtTagRecord {
+ UdtTagRecord() {}
+ llvm::codeview::UnionRecord ur;
+ llvm::codeview::ClassRecord cr;
+ llvm::codeview::EnumRecord er;
+ } m_cvr;
+
+ PdbTypeSymId m_id;
+ CompilerType &m_derived_ct;
+ clang::TagDecl &m_tag_decl;
+ PdbAstBuilder &m_ast_builder;
+ llvm::pdb::TpiStream &m_tpi;
+ std::vector<IndexedBase> m_bases;
+ ClangASTImporter::LayoutInfo m_layout;
+
+public:
+ UdtRecordCompleter(PdbTypeSymId id, CompilerType &derived_ct,
+ clang::TagDecl &tag_decl, PdbAstBuilder &ast_builder,
+ llvm::pdb::TpiStream &tpi);
+
+#define MEMBER_RECORD(EnumName, EnumVal, Name) \
+ llvm::Error visitKnownMember(llvm::codeview::CVMemberRecord &CVR, \
+ llvm::codeview::Name##Record &Record) override;
+#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
+#include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
+
+ void complete();
+
+private:
+ clang::QualType AddBaseClassForTypeIndex(
+ llvm::codeview::TypeIndex ti, llvm::codeview::MemberAccess access,
+ llvm::Optional<uint64_t> vtable_idx = llvm::Optional<uint64_t>());
+ void AddMethod(llvm::StringRef name, llvm::codeview::TypeIndex type_idx,
+ llvm::codeview::MemberAccess access,
+ llvm::codeview::MethodOptions options,
+ llvm::codeview::MemberAttributes attrs);
+};
+
+} // namespace npdb
+} // namespace lldb_private
+
+#endif // LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_UDTRECORDCOMPLETER_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp
new file mode 100644
index 000000000000..82cfcfbb040f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp
@@ -0,0 +1,1367 @@
+//===-- PDBASTParser.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 "PDBASTParser.h"
+
+#include "SymbolFilePDB.h"
+
+#include "clang/AST/CharUnits.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
+#include "lldb/Symbol/ClangUtil.h"
+#include "lldb/Symbol/Declaration.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/TypeMap.h"
+#include "lldb/Symbol/TypeSystem.h"
+
+#include "llvm/DebugInfo/PDB/IPDBLineNumber.h"
+#include "llvm/DebugInfo/PDB/IPDBSourceFile.h"
+#include "llvm/DebugInfo/PDB/PDBSymbol.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeArray.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionArg.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h"
+
+#include "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm::pdb;
+
+static int TranslateUdtKind(PDB_UdtType pdb_kind) {
+ switch (pdb_kind) {
+ case PDB_UdtType::Class:
+ return clang::TTK_Class;
+ case PDB_UdtType::Struct:
+ return clang::TTK_Struct;
+ case PDB_UdtType::Union:
+ return clang::TTK_Union;
+ case PDB_UdtType::Interface:
+ return clang::TTK_Interface;
+ }
+ llvm_unreachable("unsuported PDB UDT type");
+}
+
+static lldb::Encoding TranslateBuiltinEncoding(PDB_BuiltinType type) {
+ switch (type) {
+ case PDB_BuiltinType::Float:
+ return lldb::eEncodingIEEE754;
+ case PDB_BuiltinType::Int:
+ case PDB_BuiltinType::Long:
+ case PDB_BuiltinType::Char:
+ return lldb::eEncodingSint;
+ case PDB_BuiltinType::Bool:
+ case PDB_BuiltinType::Char16:
+ case PDB_BuiltinType::Char32:
+ case PDB_BuiltinType::UInt:
+ case PDB_BuiltinType::ULong:
+ case PDB_BuiltinType::HResult:
+ case PDB_BuiltinType::WCharT:
+ return lldb::eEncodingUint;
+ default:
+ return lldb::eEncodingInvalid;
+ }
+}
+
+static lldb::Encoding TranslateEnumEncoding(PDB_VariantType type) {
+ switch (type) {
+ case PDB_VariantType::Int8:
+ case PDB_VariantType::Int16:
+ case PDB_VariantType::Int32:
+ case PDB_VariantType::Int64:
+ return lldb::eEncodingSint;
+
+ case PDB_VariantType::UInt8:
+ case PDB_VariantType::UInt16:
+ case PDB_VariantType::UInt32:
+ case PDB_VariantType::UInt64:
+ return lldb::eEncodingUint;
+
+ default:
+ break;
+ }
+
+ return lldb::eEncodingSint;
+}
+
+static CompilerType
+GetBuiltinTypeForPDBEncodingAndBitSize(ClangASTContext &clang_ast,
+ const PDBSymbolTypeBuiltin &pdb_type,
+ Encoding encoding, uint32_t width) {
+ auto *ast = clang_ast.getASTContext();
+ if (!ast)
+ return CompilerType();
+
+ switch (pdb_type.getBuiltinType()) {
+ default:
+ break;
+ case PDB_BuiltinType::None:
+ return CompilerType();
+ case PDB_BuiltinType::Void:
+ return clang_ast.GetBasicType(eBasicTypeVoid);
+ case PDB_BuiltinType::Char:
+ return clang_ast.GetBasicType(eBasicTypeChar);
+ case PDB_BuiltinType::Bool:
+ return clang_ast.GetBasicType(eBasicTypeBool);
+ case PDB_BuiltinType::Long:
+ if (width == ast->getTypeSize(ast->LongTy))
+ return CompilerType(ast, ast->LongTy);
+ if (width == ast->getTypeSize(ast->LongLongTy))
+ return CompilerType(ast, ast->LongLongTy);
+ break;
+ case PDB_BuiltinType::ULong:
+ if (width == ast->getTypeSize(ast->UnsignedLongTy))
+ return CompilerType(ast, ast->UnsignedLongTy);
+ if (width == ast->getTypeSize(ast->UnsignedLongLongTy))
+ return CompilerType(ast, ast->UnsignedLongLongTy);
+ break;
+ case PDB_BuiltinType::WCharT:
+ if (width == ast->getTypeSize(ast->WCharTy))
+ return CompilerType(ast, ast->WCharTy);
+ break;
+ case PDB_BuiltinType::Char16:
+ return CompilerType(ast, ast->Char16Ty);
+ case PDB_BuiltinType::Char32:
+ return CompilerType(ast, ast->Char32Ty);
+ case PDB_BuiltinType::Float:
+ // Note: types `long double` and `double` have same bit size in MSVC and
+ // there is no information in the PDB to distinguish them. So when falling
+ // back to default search, the compiler type of `long double` will be
+ // represented by the one generated for `double`.
+ break;
+ }
+ // If there is no match on PDB_BuiltinType, fall back to default search by
+ // encoding and width only
+ return clang_ast.GetBuiltinTypeForEncodingAndBitSize(encoding, width);
+}
+
+static ConstString GetPDBBuiltinTypeName(const PDBSymbolTypeBuiltin &pdb_type,
+ CompilerType &compiler_type) {
+ PDB_BuiltinType kind = pdb_type.getBuiltinType();
+ switch (kind) {
+ default:
+ break;
+ case PDB_BuiltinType::Currency:
+ return ConstString("CURRENCY");
+ case PDB_BuiltinType::Date:
+ return ConstString("DATE");
+ case PDB_BuiltinType::Variant:
+ return ConstString("VARIANT");
+ case PDB_BuiltinType::Complex:
+ return ConstString("complex");
+ case PDB_BuiltinType::Bitfield:
+ return ConstString("bitfield");
+ case PDB_BuiltinType::BSTR:
+ return ConstString("BSTR");
+ case PDB_BuiltinType::HResult:
+ return ConstString("HRESULT");
+ case PDB_BuiltinType::BCD:
+ return ConstString("BCD");
+ case PDB_BuiltinType::Char16:
+ return ConstString("char16_t");
+ case PDB_BuiltinType::Char32:
+ return ConstString("char32_t");
+ case PDB_BuiltinType::None:
+ return ConstString("...");
+ }
+ return compiler_type.GetTypeName();
+}
+
+static bool GetDeclarationForSymbol(const PDBSymbol &symbol,
+ Declaration &decl) {
+ auto &raw_sym = symbol.getRawSymbol();
+ auto first_line_up = raw_sym.getSrcLineOnTypeDefn();
+
+ if (!first_line_up) {
+ auto lines_up = symbol.getSession().findLineNumbersByAddress(
+ raw_sym.getVirtualAddress(), raw_sym.getLength());
+ if (!lines_up)
+ return false;
+ first_line_up = lines_up->getNext();
+ if (!first_line_up)
+ return false;
+ }
+ uint32_t src_file_id = first_line_up->getSourceFileId();
+ auto src_file_up = symbol.getSession().getSourceFileById(src_file_id);
+ if (!src_file_up)
+ return false;
+
+ FileSpec spec(src_file_up->getFileName());
+ decl.SetFile(spec);
+ decl.SetColumn(first_line_up->getColumnNumber());
+ decl.SetLine(first_line_up->getLineNumber());
+ return true;
+}
+
+static AccessType TranslateMemberAccess(PDB_MemberAccess access) {
+ switch (access) {
+ case PDB_MemberAccess::Private:
+ return eAccessPrivate;
+ case PDB_MemberAccess::Protected:
+ return eAccessProtected;
+ case PDB_MemberAccess::Public:
+ return eAccessPublic;
+ }
+ return eAccessNone;
+}
+
+static AccessType GetDefaultAccessibilityForUdtKind(PDB_UdtType udt_kind) {
+ switch (udt_kind) {
+ case PDB_UdtType::Struct:
+ case PDB_UdtType::Union:
+ return eAccessPublic;
+ case PDB_UdtType::Class:
+ case PDB_UdtType::Interface:
+ return eAccessPrivate;
+ }
+ llvm_unreachable("unsupported PDB UDT type");
+}
+
+static AccessType GetAccessibilityForUdt(const PDBSymbolTypeUDT &udt) {
+ AccessType access = TranslateMemberAccess(udt.getAccess());
+ if (access != lldb::eAccessNone || !udt.isNested())
+ return access;
+
+ auto parent = udt.getClassParent();
+ if (!parent)
+ return lldb::eAccessNone;
+
+ auto parent_udt = llvm::dyn_cast<PDBSymbolTypeUDT>(parent.get());
+ if (!parent_udt)
+ return lldb::eAccessNone;
+
+ return GetDefaultAccessibilityForUdtKind(parent_udt->getUdtKind());
+}
+
+static clang::MSInheritanceAttr::Spelling
+GetMSInheritance(const PDBSymbolTypeUDT &udt) {
+ int base_count = 0;
+ bool has_virtual = false;
+
+ auto bases_enum = udt.findAllChildren<PDBSymbolTypeBaseClass>();
+ if (bases_enum) {
+ while (auto base = bases_enum->getNext()) {
+ base_count++;
+ has_virtual |= base->isVirtualBaseClass();
+ }
+ }
+
+ if (has_virtual)
+ return clang::MSInheritanceAttr::Keyword_virtual_inheritance;
+ if (base_count > 1)
+ return clang::MSInheritanceAttr::Keyword_multiple_inheritance;
+ return clang::MSInheritanceAttr::Keyword_single_inheritance;
+}
+
+static std::unique_ptr<llvm::pdb::PDBSymbol>
+GetClassOrFunctionParent(const llvm::pdb::PDBSymbol &symbol) {
+ const IPDBSession &session = symbol.getSession();
+ const IPDBRawSymbol &raw = symbol.getRawSymbol();
+ auto tag = symbol.getSymTag();
+
+ // For items that are nested inside of a class, return the class that it is
+ // nested inside of.
+ // Note that only certain items can be nested inside of classes.
+ switch (tag) {
+ case PDB_SymType::Function:
+ case PDB_SymType::Data:
+ case PDB_SymType::UDT:
+ case PDB_SymType::Enum:
+ case PDB_SymType::FunctionSig:
+ case PDB_SymType::Typedef:
+ case PDB_SymType::BaseClass:
+ case PDB_SymType::VTable: {
+ auto class_parent_id = raw.getClassParentId();
+ if (auto class_parent = session.getSymbolById(class_parent_id))
+ return class_parent;
+ break;
+ }
+ default:
+ break;
+ }
+
+ // Otherwise, if it is nested inside of a function, return the function.
+ // Note that only certain items can be nested inside of functions.
+ switch (tag) {
+ case PDB_SymType::Block:
+ case PDB_SymType::Data: {
+ auto lexical_parent_id = raw.getLexicalParentId();
+ auto lexical_parent = session.getSymbolById(lexical_parent_id);
+ if (!lexical_parent)
+ return nullptr;
+
+ auto lexical_parent_tag = lexical_parent->getSymTag();
+ if (lexical_parent_tag == PDB_SymType::Function)
+ return lexical_parent;
+ if (lexical_parent_tag == PDB_SymType::Exe)
+ return nullptr;
+
+ return GetClassOrFunctionParent(*lexical_parent);
+ }
+ default:
+ return nullptr;
+ }
+}
+
+static clang::NamedDecl *
+GetDeclFromContextByName(const clang::ASTContext &ast,
+ const clang::DeclContext &decl_context,
+ llvm::StringRef name) {
+ clang::IdentifierInfo &ident = ast.Idents.get(name);
+ clang::DeclarationName decl_name = ast.DeclarationNames.getIdentifier(&ident);
+ clang::DeclContext::lookup_result result = decl_context.lookup(decl_name);
+ if (result.empty())
+ return nullptr;
+
+ return result[0];
+}
+
+static bool IsAnonymousNamespaceName(llvm::StringRef name) {
+ return name == "`anonymous namespace'" || name == "`anonymous-namespace'";
+}
+
+static clang::CallingConv TranslateCallingConvention(PDB_CallingConv pdb_cc) {
+ switch (pdb_cc) {
+ case llvm::codeview::CallingConvention::NearC:
+ return clang::CC_C;
+ case llvm::codeview::CallingConvention::NearStdCall:
+ return clang::CC_X86StdCall;
+ case llvm::codeview::CallingConvention::NearFast:
+ return clang::CC_X86FastCall;
+ case llvm::codeview::CallingConvention::ThisCall:
+ return clang::CC_X86ThisCall;
+ case llvm::codeview::CallingConvention::NearVector:
+ return clang::CC_X86VectorCall;
+ case llvm::codeview::CallingConvention::NearPascal:
+ return clang::CC_X86Pascal;
+ default:
+ assert(false && "Unknown calling convention");
+ return clang::CC_C;
+ }
+}
+
+PDBASTParser::PDBASTParser(lldb_private::ClangASTContext &ast) : m_ast(ast) {}
+
+PDBASTParser::~PDBASTParser() {}
+
+// DebugInfoASTParser interface
+
+lldb::TypeSP PDBASTParser::CreateLLDBTypeFromPDBType(const PDBSymbol &type) {
+ Declaration decl;
+ switch (type.getSymTag()) {
+ case PDB_SymType::BaseClass: {
+ auto symbol_file = m_ast.GetSymbolFile();
+ if (!symbol_file)
+ return nullptr;
+
+ auto ty = symbol_file->ResolveTypeUID(type.getRawSymbol().getTypeId());
+ return ty ? ty->shared_from_this() : nullptr;
+ } break;
+ case PDB_SymType::UDT: {
+ auto udt = llvm::dyn_cast<PDBSymbolTypeUDT>(&type);
+ assert(udt);
+
+ // Note that, unnamed UDT being typedef-ed is generated as a UDT symbol
+ // other than a Typedef symbol in PDB. For example,
+ // typedef union { short Row; short Col; } Union;
+ // is generated as a named UDT in PDB:
+ // union Union { short Row; short Col; }
+ // Such symbols will be handled here.
+
+ // Some UDT with trival ctor has zero length. Just ignore.
+ if (udt->getLength() == 0)
+ return nullptr;
+
+ // Ignore unnamed-tag UDTs.
+ std::string name = MSVCUndecoratedNameParser::DropScope(udt->getName());
+ if (name.empty())
+ return nullptr;
+
+ auto decl_context = GetDeclContextContainingSymbol(type);
+
+ // Check if such an UDT already exists in the current context.
+ // This may occur with const or volatile types. There are separate type
+ // symbols in PDB for types with const or volatile modifiers, but we need
+ // to create only one declaration for them all.
+ Type::ResolveStateTag type_resolve_state_tag;
+ CompilerType clang_type = m_ast.GetTypeForIdentifier<clang::CXXRecordDecl>(
+ ConstString(name), decl_context);
+ if (!clang_type.IsValid()) {
+ auto access = GetAccessibilityForUdt(*udt);
+
+ auto tag_type_kind = TranslateUdtKind(udt->getUdtKind());
+
+ ClangASTMetadata metadata;
+ metadata.SetUserID(type.getSymIndexId());
+ metadata.SetIsDynamicCXXType(false);
+
+ clang_type = m_ast.CreateRecordType(
+ decl_context, access, name.c_str(), tag_type_kind,
+ lldb::eLanguageTypeC_plus_plus, &metadata);
+ assert(clang_type.IsValid());
+
+ auto record_decl =
+ m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType());
+ assert(record_decl);
+ m_uid_to_decl[type.getSymIndexId()] = record_decl;
+
+ auto inheritance_attr = clang::MSInheritanceAttr::CreateImplicit(
+ *m_ast.getASTContext(), GetMSInheritance(*udt));
+ record_decl->addAttr(inheritance_attr);
+
+ ClangASTContext::StartTagDeclarationDefinition(clang_type);
+
+ auto children = udt->findAllChildren();
+ if (!children || children->getChildCount() == 0) {
+ // PDB does not have symbol of forwarder. We assume we get an udt w/o
+ // any fields. Just complete it at this point.
+ ClangASTContext::CompleteTagDeclarationDefinition(clang_type);
+
+ ClangASTContext::SetHasExternalStorage(clang_type.GetOpaqueQualType(),
+ false);
+
+ type_resolve_state_tag = Type::eResolveStateFull;
+ } else {
+ // Add the type to the forward declarations. It will help us to avoid
+ // an endless recursion in CompleteTypeFromUdt function.
+ m_forward_decl_to_uid[record_decl] = type.getSymIndexId();
+
+ ClangASTContext::SetHasExternalStorage(clang_type.GetOpaqueQualType(),
+ true);
+
+ type_resolve_state_tag = Type::eResolveStateForward;
+ }
+ } else
+ type_resolve_state_tag = Type::eResolveStateForward;
+
+ if (udt->isConstType())
+ clang_type = clang_type.AddConstModifier();
+
+ if (udt->isVolatileType())
+ clang_type = clang_type.AddVolatileModifier();
+
+ GetDeclarationForSymbol(type, decl);
+ return std::make_shared<lldb_private::Type>(
+ type.getSymIndexId(), m_ast.GetSymbolFile(), ConstString(name),
+ udt->getLength(), nullptr, LLDB_INVALID_UID,
+ lldb_private::Type::eEncodingIsUID, decl, clang_type,
+ type_resolve_state_tag);
+ } break;
+ case PDB_SymType::Enum: {
+ auto enum_type = llvm::dyn_cast<PDBSymbolTypeEnum>(&type);
+ assert(enum_type);
+
+ std::string name =
+ MSVCUndecoratedNameParser::DropScope(enum_type->getName());
+ auto decl_context = GetDeclContextContainingSymbol(type);
+ uint64_t bytes = enum_type->getLength();
+
+ // Check if such an enum already exists in the current context
+ CompilerType ast_enum = m_ast.GetTypeForIdentifier<clang::EnumDecl>(
+ ConstString(name), decl_context);
+ if (!ast_enum.IsValid()) {
+ auto underlying_type_up = enum_type->getUnderlyingType();
+ if (!underlying_type_up)
+ return nullptr;
+
+ lldb::Encoding encoding =
+ TranslateBuiltinEncoding(underlying_type_up->getBuiltinType());
+ // FIXME: Type of underlying builtin is always `Int`. We correct it with
+ // the very first enumerator's encoding if any.
+ auto first_child = enum_type->findOneChild<PDBSymbolData>();
+ if (first_child)
+ encoding = TranslateEnumEncoding(first_child->getValue().Type);
+
+ CompilerType builtin_type;
+ if (bytes > 0)
+ builtin_type = GetBuiltinTypeForPDBEncodingAndBitSize(
+ m_ast, *underlying_type_up, encoding, bytes * 8);
+ else
+ builtin_type = m_ast.GetBasicType(eBasicTypeInt);
+
+ // FIXME: PDB does not have information about scoped enumeration (Enum
+ // Class). Set it false for now.
+ bool isScoped = false;
+
+ ast_enum = m_ast.CreateEnumerationType(name.c_str(), decl_context, decl,
+ builtin_type, isScoped);
+
+ auto enum_decl = ClangASTContext::GetAsEnumDecl(ast_enum);
+ assert(enum_decl);
+ m_uid_to_decl[type.getSymIndexId()] = enum_decl;
+
+ auto enum_values = enum_type->findAllChildren<PDBSymbolData>();
+ if (enum_values) {
+ while (auto enum_value = enum_values->getNext()) {
+ if (enum_value->getDataKind() != PDB_DataKind::Constant)
+ continue;
+ AddEnumValue(ast_enum, *enum_value);
+ }
+ }
+
+ if (ClangASTContext::StartTagDeclarationDefinition(ast_enum))
+ ClangASTContext::CompleteTagDeclarationDefinition(ast_enum);
+ }
+
+ if (enum_type->isConstType())
+ ast_enum = ast_enum.AddConstModifier();
+
+ if (enum_type->isVolatileType())
+ ast_enum = ast_enum.AddVolatileModifier();
+
+ GetDeclarationForSymbol(type, decl);
+ return std::make_shared<lldb_private::Type>(
+ type.getSymIndexId(), m_ast.GetSymbolFile(), ConstString(name), bytes,
+ nullptr, LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl,
+ ast_enum, lldb_private::Type::eResolveStateFull);
+ } break;
+ case PDB_SymType::Typedef: {
+ auto type_def = llvm::dyn_cast<PDBSymbolTypeTypedef>(&type);
+ assert(type_def);
+
+ lldb_private::Type *target_type =
+ m_ast.GetSymbolFile()->ResolveTypeUID(type_def->getTypeId());
+ if (!target_type)
+ return nullptr;
+
+ std::string name =
+ MSVCUndecoratedNameParser::DropScope(type_def->getName());
+ auto decl_ctx = GetDeclContextContainingSymbol(type);
+
+ // Check if such a typedef already exists in the current context
+ CompilerType ast_typedef =
+ m_ast.GetTypeForIdentifier<clang::TypedefNameDecl>(ConstString(name),
+ decl_ctx);
+ if (!ast_typedef.IsValid()) {
+ CompilerType target_ast_type = target_type->GetFullCompilerType();
+
+ ast_typedef = m_ast.CreateTypedefType(
+ target_ast_type, name.c_str(), CompilerDeclContext(&m_ast, decl_ctx));
+ if (!ast_typedef)
+ return nullptr;
+
+ auto typedef_decl = ClangASTContext::GetAsTypedefDecl(ast_typedef);
+ assert(typedef_decl);
+ m_uid_to_decl[type.getSymIndexId()] = typedef_decl;
+ }
+
+ if (type_def->isConstType())
+ ast_typedef = ast_typedef.AddConstModifier();
+
+ if (type_def->isVolatileType())
+ ast_typedef = ast_typedef.AddVolatileModifier();
+
+ GetDeclarationForSymbol(type, decl);
+ llvm::Optional<uint64_t> size;
+ if (type_def->getLength())
+ size = type_def->getLength();
+ return std::make_shared<lldb_private::Type>(
+ type_def->getSymIndexId(), m_ast.GetSymbolFile(), ConstString(name),
+ size, nullptr, target_type->GetID(),
+ lldb_private::Type::eEncodingIsTypedefUID, decl, ast_typedef,
+ lldb_private::Type::eResolveStateFull);
+ } break;
+ case PDB_SymType::Function:
+ case PDB_SymType::FunctionSig: {
+ std::string name;
+ PDBSymbolTypeFunctionSig *func_sig = nullptr;
+ if (auto pdb_func = llvm::dyn_cast<PDBSymbolFunc>(&type)) {
+ if (pdb_func->isCompilerGenerated())
+ return nullptr;
+
+ auto sig = pdb_func->getSignature();
+ if (!sig)
+ return nullptr;
+ func_sig = sig.release();
+ // Function type is named.
+ name = MSVCUndecoratedNameParser::DropScope(pdb_func->getName());
+ } else if (auto pdb_func_sig =
+ llvm::dyn_cast<PDBSymbolTypeFunctionSig>(&type)) {
+ func_sig = const_cast<PDBSymbolTypeFunctionSig *>(pdb_func_sig);
+ } else
+ llvm_unreachable("Unexpected PDB symbol!");
+
+ auto arg_enum = func_sig->getArguments();
+ uint32_t num_args = arg_enum->getChildCount();
+ std::vector<CompilerType> arg_list;
+
+ bool is_variadic = func_sig->isCVarArgs();
+ // Drop last variadic argument.
+ if (is_variadic)
+ --num_args;
+ for (uint32_t arg_idx = 0; arg_idx < num_args; arg_idx++) {
+ auto arg = arg_enum->getChildAtIndex(arg_idx);
+ if (!arg)
+ break;
+ lldb_private::Type *arg_type =
+ m_ast.GetSymbolFile()->ResolveTypeUID(arg->getSymIndexId());
+ // If there's some error looking up one of the dependent types of this
+ // function signature, bail.
+ if (!arg_type)
+ return nullptr;
+ CompilerType arg_ast_type = arg_type->GetFullCompilerType();
+ arg_list.push_back(arg_ast_type);
+ }
+ lldbassert(arg_list.size() <= num_args);
+
+ auto pdb_return_type = func_sig->getReturnType();
+ lldb_private::Type *return_type =
+ m_ast.GetSymbolFile()->ResolveTypeUID(pdb_return_type->getSymIndexId());
+ // If there's some error looking up one of the dependent types of this
+ // function signature, bail.
+ if (!return_type)
+ return nullptr;
+ CompilerType return_ast_type = return_type->GetFullCompilerType();
+ uint32_t type_quals = 0;
+ if (func_sig->isConstType())
+ type_quals |= clang::Qualifiers::Const;
+ if (func_sig->isVolatileType())
+ type_quals |= clang::Qualifiers::Volatile;
+ auto cc = TranslateCallingConvention(func_sig->getCallingConvention());
+ CompilerType func_sig_ast_type =
+ m_ast.CreateFunctionType(return_ast_type, arg_list.data(),
+ arg_list.size(), is_variadic, type_quals, cc);
+
+ GetDeclarationForSymbol(type, decl);
+ return std::make_shared<lldb_private::Type>(
+ type.getSymIndexId(), m_ast.GetSymbolFile(), ConstString(name),
+ llvm::None, nullptr, LLDB_INVALID_UID,
+ lldb_private::Type::eEncodingIsUID, decl, func_sig_ast_type,
+ lldb_private::Type::eResolveStateFull);
+ } break;
+ case PDB_SymType::ArrayType: {
+ auto array_type = llvm::dyn_cast<PDBSymbolTypeArray>(&type);
+ assert(array_type);
+ uint32_t num_elements = array_type->getCount();
+ uint32_t element_uid = array_type->getElementTypeId();
+ llvm::Optional<uint64_t> bytes;
+ if (uint64_t size = array_type->getLength())
+ bytes = size;
+
+ // If array rank > 0, PDB gives the element type at N=0. So element type
+ // will parsed in the order N=0, N=1,..., N=rank sequentially.
+ lldb_private::Type *element_type =
+ m_ast.GetSymbolFile()->ResolveTypeUID(element_uid);
+ if (!element_type)
+ return nullptr;
+
+ CompilerType element_ast_type = element_type->GetForwardCompilerType();
+ // If element type is UDT, it needs to be complete.
+ if (ClangASTContext::IsCXXClassType(element_ast_type) &&
+ !element_ast_type.GetCompleteType()) {
+ if (ClangASTContext::StartTagDeclarationDefinition(element_ast_type)) {
+ ClangASTContext::CompleteTagDeclarationDefinition(element_ast_type);
+ } else {
+ // We are not able to start defintion.
+ return nullptr;
+ }
+ }
+ CompilerType array_ast_type = m_ast.CreateArrayType(
+ element_ast_type, num_elements, /*is_gnu_vector*/ false);
+ TypeSP type_sp = std::make_shared<lldb_private::Type>(
+ array_type->getSymIndexId(), m_ast.GetSymbolFile(), ConstString(),
+ bytes, nullptr, LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID,
+ decl, array_ast_type, lldb_private::Type::eResolveStateFull);
+ type_sp->SetEncodingType(element_type);
+ return type_sp;
+ } break;
+ case PDB_SymType::BuiltinType: {
+ auto *builtin_type = llvm::dyn_cast<PDBSymbolTypeBuiltin>(&type);
+ assert(builtin_type);
+ PDB_BuiltinType builtin_kind = builtin_type->getBuiltinType();
+ if (builtin_kind == PDB_BuiltinType::None)
+ return nullptr;
+
+ llvm::Optional<uint64_t> bytes;
+ if (uint64_t size = builtin_type->getLength())
+ bytes = size;
+ Encoding encoding = TranslateBuiltinEncoding(builtin_kind);
+ CompilerType builtin_ast_type = GetBuiltinTypeForPDBEncodingAndBitSize(
+ m_ast, *builtin_type, encoding, bytes.getValueOr(0) * 8);
+
+ if (builtin_type->isConstType())
+ builtin_ast_type = builtin_ast_type.AddConstModifier();
+
+ if (builtin_type->isVolatileType())
+ builtin_ast_type = builtin_ast_type.AddVolatileModifier();
+
+ auto type_name = GetPDBBuiltinTypeName(*builtin_type, builtin_ast_type);
+
+ return std::make_shared<lldb_private::Type>(
+ builtin_type->getSymIndexId(), m_ast.GetSymbolFile(), type_name, bytes,
+ nullptr, LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl,
+ builtin_ast_type, lldb_private::Type::eResolveStateFull);
+ } break;
+ case PDB_SymType::PointerType: {
+ auto *pointer_type = llvm::dyn_cast<PDBSymbolTypePointer>(&type);
+ assert(pointer_type);
+ Type *pointee_type = m_ast.GetSymbolFile()->ResolveTypeUID(
+ pointer_type->getPointeeType()->getSymIndexId());
+ if (!pointee_type)
+ return nullptr;
+
+ if (pointer_type->isPointerToDataMember() ||
+ pointer_type->isPointerToMemberFunction()) {
+ auto class_parent_uid = pointer_type->getRawSymbol().getClassParentId();
+ auto class_parent_type =
+ m_ast.GetSymbolFile()->ResolveTypeUID(class_parent_uid);
+ assert(class_parent_type);
+
+ CompilerType pointer_ast_type;
+ pointer_ast_type = ClangASTContext::CreateMemberPointerType(
+ class_parent_type->GetLayoutCompilerType(),
+ pointee_type->GetForwardCompilerType());
+ assert(pointer_ast_type);
+
+ return std::make_shared<lldb_private::Type>(
+ pointer_type->getSymIndexId(), m_ast.GetSymbolFile(), ConstString(),
+ pointer_type->getLength(), nullptr, LLDB_INVALID_UID,
+ lldb_private::Type::eEncodingIsUID, decl, pointer_ast_type,
+ lldb_private::Type::eResolveStateForward);
+ }
+
+ CompilerType pointer_ast_type;
+ pointer_ast_type = pointee_type->GetFullCompilerType();
+ if (pointer_type->isReference())
+ pointer_ast_type = pointer_ast_type.GetLValueReferenceType();
+ else if (pointer_type->isRValueReference())
+ pointer_ast_type = pointer_ast_type.GetRValueReferenceType();
+ else
+ pointer_ast_type = pointer_ast_type.GetPointerType();
+
+ if (pointer_type->isConstType())
+ pointer_ast_type = pointer_ast_type.AddConstModifier();
+
+ if (pointer_type->isVolatileType())
+ pointer_ast_type = pointer_ast_type.AddVolatileModifier();
+
+ if (pointer_type->isRestrictedType())
+ pointer_ast_type = pointer_ast_type.AddRestrictModifier();
+
+ return std::make_shared<lldb_private::Type>(
+ pointer_type->getSymIndexId(), m_ast.GetSymbolFile(), ConstString(),
+ pointer_type->getLength(), nullptr, LLDB_INVALID_UID,
+ lldb_private::Type::eEncodingIsUID, decl, pointer_ast_type,
+ lldb_private::Type::eResolveStateFull);
+ } break;
+ default:
+ break;
+ }
+ return nullptr;
+}
+
+bool PDBASTParser::CompleteTypeFromPDB(
+ lldb_private::CompilerType &compiler_type) {
+ if (GetClangASTImporter().CanImport(compiler_type))
+ return GetClangASTImporter().CompleteType(compiler_type);
+
+ // Remove the type from the forward declarations to avoid
+ // an endless recursion for types like a linked list.
+ clang::CXXRecordDecl *record_decl =
+ m_ast.GetAsCXXRecordDecl(compiler_type.GetOpaqueQualType());
+ auto uid_it = m_forward_decl_to_uid.find(record_decl);
+ if (uid_it == m_forward_decl_to_uid.end())
+ return true;
+
+ auto symbol_file = static_cast<SymbolFilePDB *>(m_ast.GetSymbolFile());
+ if (!symbol_file)
+ return false;
+
+ std::unique_ptr<PDBSymbol> symbol =
+ symbol_file->GetPDBSession().getSymbolById(uid_it->getSecond());
+ if (!symbol)
+ return false;
+
+ m_forward_decl_to_uid.erase(uid_it);
+
+ ClangASTContext::SetHasExternalStorage(compiler_type.GetOpaqueQualType(),
+ false);
+
+ switch (symbol->getSymTag()) {
+ case PDB_SymType::UDT: {
+ auto udt = llvm::dyn_cast<PDBSymbolTypeUDT>(symbol.get());
+ if (!udt)
+ return false;
+
+ return CompleteTypeFromUDT(*symbol_file, compiler_type, *udt);
+ }
+ default:
+ llvm_unreachable("not a forward clang type decl!");
+ }
+}
+
+clang::Decl *
+PDBASTParser::GetDeclForSymbol(const llvm::pdb::PDBSymbol &symbol) {
+ uint32_t sym_id = symbol.getSymIndexId();
+ auto it = m_uid_to_decl.find(sym_id);
+ if (it != m_uid_to_decl.end())
+ return it->second;
+
+ auto symbol_file = static_cast<SymbolFilePDB *>(m_ast.GetSymbolFile());
+ if (!symbol_file)
+ return nullptr;
+
+ // First of all, check if the symbol is a member of a class. Resolve the full
+ // class type and return the declaration from the cache if so.
+ auto tag = symbol.getSymTag();
+ if (tag == PDB_SymType::Data || tag == PDB_SymType::Function) {
+ const IPDBSession &session = symbol.getSession();
+ const IPDBRawSymbol &raw = symbol.getRawSymbol();
+
+ auto class_parent_id = raw.getClassParentId();
+ if (std::unique_ptr<PDBSymbol> class_parent =
+ session.getSymbolById(class_parent_id)) {
+ auto class_parent_type = symbol_file->ResolveTypeUID(class_parent_id);
+ if (!class_parent_type)
+ return nullptr;
+
+ CompilerType class_parent_ct = class_parent_type->GetFullCompilerType();
+
+ // Look a declaration up in the cache after completing the class
+ clang::Decl *decl = m_uid_to_decl.lookup(sym_id);
+ if (decl)
+ return decl;
+
+ // A declaration was not found in the cache. It means that the symbol
+ // has the class parent, but the class doesn't have the symbol in its
+ // children list.
+ if (auto func = llvm::dyn_cast_or_null<PDBSymbolFunc>(&symbol)) {
+ // Try to find a class child method with the same RVA and use its
+ // declaration if found.
+ if (uint32_t rva = func->getRelativeVirtualAddress()) {
+ if (std::unique_ptr<ConcreteSymbolEnumerator<PDBSymbolFunc>>
+ methods_enum =
+ class_parent->findAllChildren<PDBSymbolFunc>()) {
+ while (std::unique_ptr<PDBSymbolFunc> method =
+ methods_enum->getNext()) {
+ if (method->getRelativeVirtualAddress() == rva) {
+ decl = m_uid_to_decl.lookup(method->getSymIndexId());
+ if (decl)
+ break;
+ }
+ }
+ }
+ }
+
+ // If no class methods with the same RVA were found, then create a new
+ // method. It is possible for template methods.
+ if (!decl)
+ decl = AddRecordMethod(*symbol_file, class_parent_ct, *func);
+ }
+
+ if (decl)
+ m_uid_to_decl[sym_id] = decl;
+
+ return decl;
+ }
+ }
+
+ // If we are here, then the symbol is not belonging to a class and is not
+ // contained in the cache. So create a declaration for it.
+ switch (symbol.getSymTag()) {
+ case PDB_SymType::Data: {
+ auto data = llvm::dyn_cast<PDBSymbolData>(&symbol);
+ assert(data);
+
+ auto decl_context = GetDeclContextContainingSymbol(symbol);
+ assert(decl_context);
+
+ // May be the current context is a class really, but we haven't found
+ // any class parent. This happens e.g. in the case of class static
+ // variables - they has two symbols, one is a child of the class when
+ // another is a child of the exe. So always complete the parent and use
+ // an existing declaration if possible.
+ if (auto parent_decl = llvm::dyn_cast_or_null<clang::TagDecl>(decl_context))
+ m_ast.GetCompleteDecl(parent_decl);
+
+ std::string name = MSVCUndecoratedNameParser::DropScope(data->getName());
+
+ // Check if the current context already contains the symbol with the name.
+ clang::Decl *decl =
+ GetDeclFromContextByName(*m_ast.getASTContext(), *decl_context, name);
+ if (!decl) {
+ auto type = symbol_file->ResolveTypeUID(data->getTypeId());
+ if (!type)
+ return nullptr;
+
+ decl = m_ast.CreateVariableDeclaration(
+ decl_context, name.c_str(),
+ ClangUtil::GetQualType(type->GetLayoutCompilerType()));
+ }
+
+ m_uid_to_decl[sym_id] = decl;
+
+ return decl;
+ }
+ case PDB_SymType::Function: {
+ auto func = llvm::dyn_cast<PDBSymbolFunc>(&symbol);
+ assert(func);
+
+ auto decl_context = GetDeclContextContainingSymbol(symbol);
+ assert(decl_context);
+
+ std::string name = MSVCUndecoratedNameParser::DropScope(func->getName());
+
+ Type *type = symbol_file->ResolveTypeUID(sym_id);
+ if (!type)
+ return nullptr;
+
+ auto storage = func->isStatic() ? clang::StorageClass::SC_Static
+ : clang::StorageClass::SC_None;
+
+ auto decl = m_ast.CreateFunctionDeclaration(
+ decl_context, name.c_str(), type->GetForwardCompilerType(), storage,
+ func->hasInlineAttribute());
+
+ std::vector<clang::ParmVarDecl *> params;
+ if (std::unique_ptr<PDBSymbolTypeFunctionSig> sig = func->getSignature()) {
+ if (std::unique_ptr<ConcreteSymbolEnumerator<PDBSymbolTypeFunctionArg>>
+ arg_enum = sig->findAllChildren<PDBSymbolTypeFunctionArg>()) {
+ while (std::unique_ptr<PDBSymbolTypeFunctionArg> arg =
+ arg_enum->getNext()) {
+ Type *arg_type = symbol_file->ResolveTypeUID(arg->getTypeId());
+ if (!arg_type)
+ continue;
+
+ clang::ParmVarDecl *param = m_ast.CreateParameterDeclaration(
+ decl, nullptr, arg_type->GetForwardCompilerType(),
+ clang::SC_None);
+ if (param)
+ params.push_back(param);
+ }
+ }
+ }
+ if (params.size())
+ m_ast.SetFunctionParameters(decl, params.data(), params.size());
+
+ m_uid_to_decl[sym_id] = decl;
+
+ return decl;
+ }
+ default: {
+ // It's not a variable and not a function, check if it's a type
+ Type *type = symbol_file->ResolveTypeUID(sym_id);
+ if (!type)
+ return nullptr;
+
+ return m_uid_to_decl.lookup(sym_id);
+ }
+ }
+}
+
+clang::DeclContext *
+PDBASTParser::GetDeclContextForSymbol(const llvm::pdb::PDBSymbol &symbol) {
+ if (symbol.getSymTag() == PDB_SymType::Function) {
+ clang::DeclContext *result =
+ llvm::dyn_cast_or_null<clang::FunctionDecl>(GetDeclForSymbol(symbol));
+
+ if (result)
+ m_decl_context_to_uid[result] = symbol.getSymIndexId();
+
+ return result;
+ }
+
+ auto symbol_file = static_cast<SymbolFilePDB *>(m_ast.GetSymbolFile());
+ if (!symbol_file)
+ return nullptr;
+
+ auto type = symbol_file->ResolveTypeUID(symbol.getSymIndexId());
+ if (!type)
+ return nullptr;
+
+ clang::DeclContext *result =
+ m_ast.GetDeclContextForType(type->GetForwardCompilerType());
+
+ if (result)
+ m_decl_context_to_uid[result] = symbol.getSymIndexId();
+
+ return result;
+}
+
+clang::DeclContext *PDBASTParser::GetDeclContextContainingSymbol(
+ const llvm::pdb::PDBSymbol &symbol) {
+ auto parent = GetClassOrFunctionParent(symbol);
+ while (parent) {
+ if (auto parent_context = GetDeclContextForSymbol(*parent))
+ return parent_context;
+
+ parent = GetClassOrFunctionParent(*parent);
+ }
+
+ // We can't find any class or function parent of the symbol. So analyze
+ // the full symbol name. The symbol may be belonging to a namespace
+ // or function (or even to a class if it's e.g. a static variable symbol).
+
+ // TODO: Make clang to emit full names for variables in namespaces
+ // (as MSVC does)
+
+ std::string name(symbol.getRawSymbol().getName());
+ MSVCUndecoratedNameParser parser(name);
+ llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs = parser.GetSpecifiers();
+ if (specs.empty())
+ return m_ast.GetTranslationUnitDecl();
+
+ auto symbol_file = static_cast<SymbolFilePDB *>(m_ast.GetSymbolFile());
+ if (!symbol_file)
+ return m_ast.GetTranslationUnitDecl();
+
+ auto global = symbol_file->GetPDBSession().getGlobalScope();
+ if (!global)
+ return m_ast.GetTranslationUnitDecl();
+
+ bool has_type_or_function_parent = false;
+ clang::DeclContext *curr_context = m_ast.GetTranslationUnitDecl();
+ for (std::size_t i = 0; i < specs.size() - 1; i++) {
+ // Check if there is a function or a type with the current context's name.
+ if (std::unique_ptr<IPDBEnumSymbols> children_enum = global->findChildren(
+ PDB_SymType::None, specs[i].GetFullName(), NS_CaseSensitive)) {
+ while (IPDBEnumChildren<PDBSymbol>::ChildTypePtr child =
+ children_enum->getNext()) {
+ if (clang::DeclContext *child_context =
+ GetDeclContextForSymbol(*child)) {
+ // Note that `GetDeclContextForSymbol' retrieves
+ // a declaration context for functions and types only,
+ // so if we are here then `child_context' is guaranteed
+ // a function or a type declaration context.
+ has_type_or_function_parent = true;
+ curr_context = child_context;
+ }
+ }
+ }
+
+ // If there were no functions or types above then retrieve a namespace with
+ // the current context's name. There can be no namespaces inside a function
+ // or a type. We check it to avoid fake namespaces such as `__l2':
+ // `N0::N1::CClass::PrivateFunc::__l2::InnerFuncStruct'
+ if (!has_type_or_function_parent) {
+ std::string namespace_name = specs[i].GetBaseName();
+ const char *namespace_name_c_str =
+ IsAnonymousNamespaceName(namespace_name) ? nullptr
+ : namespace_name.data();
+ clang::NamespaceDecl *namespace_decl =
+ m_ast.GetUniqueNamespaceDeclaration(namespace_name_c_str,
+ curr_context);
+
+ m_parent_to_namespaces[curr_context].insert(namespace_decl);
+ m_namespaces.insert(namespace_decl);
+
+ curr_context = namespace_decl;
+ }
+ }
+
+ return curr_context;
+}
+
+void PDBASTParser::ParseDeclsForDeclContext(
+ const clang::DeclContext *decl_context) {
+ auto symbol_file = static_cast<SymbolFilePDB *>(m_ast.GetSymbolFile());
+ if (!symbol_file)
+ return;
+
+ IPDBSession &session = symbol_file->GetPDBSession();
+ auto symbol_up =
+ session.getSymbolById(m_decl_context_to_uid.lookup(decl_context));
+ auto global_up = session.getGlobalScope();
+
+ PDBSymbol *symbol;
+ if (symbol_up)
+ symbol = symbol_up.get();
+ else if (global_up)
+ symbol = global_up.get();
+ else
+ return;
+
+ if (auto children = symbol->findAllChildren())
+ while (auto child = children->getNext())
+ GetDeclForSymbol(*child);
+}
+
+clang::NamespaceDecl *
+PDBASTParser::FindNamespaceDecl(const clang::DeclContext *parent,
+ llvm::StringRef name) {
+ NamespacesSet *set;
+ if (parent) {
+ auto pit = m_parent_to_namespaces.find(parent);
+ if (pit == m_parent_to_namespaces.end())
+ return nullptr;
+
+ set = &pit->second;
+ } else {
+ set = &m_namespaces;
+ }
+ assert(set);
+
+ for (clang::NamespaceDecl *namespace_decl : *set)
+ if (namespace_decl->getName().equals(name))
+ return namespace_decl;
+
+ for (clang::NamespaceDecl *namespace_decl : *set)
+ if (namespace_decl->isAnonymousNamespace())
+ return FindNamespaceDecl(namespace_decl, name);
+
+ return nullptr;
+}
+
+bool PDBASTParser::AddEnumValue(CompilerType enum_type,
+ const PDBSymbolData &enum_value) {
+ Declaration decl;
+ Variant v = enum_value.getValue();
+ std::string name = MSVCUndecoratedNameParser::DropScope(enum_value.getName());
+ int64_t raw_value;
+ switch (v.Type) {
+ case PDB_VariantType::Int8:
+ raw_value = v.Value.Int8;
+ break;
+ case PDB_VariantType::Int16:
+ raw_value = v.Value.Int16;
+ break;
+ case PDB_VariantType::Int32:
+ raw_value = v.Value.Int32;
+ break;
+ case PDB_VariantType::Int64:
+ raw_value = v.Value.Int64;
+ break;
+ case PDB_VariantType::UInt8:
+ raw_value = v.Value.UInt8;
+ break;
+ case PDB_VariantType::UInt16:
+ raw_value = v.Value.UInt16;
+ break;
+ case PDB_VariantType::UInt32:
+ raw_value = v.Value.UInt32;
+ break;
+ case PDB_VariantType::UInt64:
+ raw_value = v.Value.UInt64;
+ break;
+ default:
+ return false;
+ }
+ CompilerType underlying_type =
+ m_ast.GetEnumerationIntegerType(enum_type.GetOpaqueQualType());
+ uint32_t byte_size = m_ast.getASTContext()->getTypeSize(
+ ClangUtil::GetQualType(underlying_type));
+ auto enum_constant_decl = m_ast.AddEnumerationValueToEnumerationType(
+ enum_type, decl, name.c_str(), raw_value, byte_size * 8);
+ if (!enum_constant_decl)
+ return false;
+
+ m_uid_to_decl[enum_value.getSymIndexId()] = enum_constant_decl;
+
+ return true;
+}
+
+bool PDBASTParser::CompleteTypeFromUDT(
+ lldb_private::SymbolFile &symbol_file,
+ lldb_private::CompilerType &compiler_type,
+ llvm::pdb::PDBSymbolTypeUDT &udt) {
+ ClangASTImporter::LayoutInfo layout_info;
+ layout_info.bit_size = udt.getLength() * 8;
+
+ auto nested_enums = udt.findAllChildren<PDBSymbolTypeUDT>();
+ if (nested_enums)
+ while (auto nested = nested_enums->getNext())
+ symbol_file.ResolveTypeUID(nested->getSymIndexId());
+
+ auto bases_enum = udt.findAllChildren<PDBSymbolTypeBaseClass>();
+ if (bases_enum)
+ AddRecordBases(symbol_file, compiler_type,
+ TranslateUdtKind(udt.getUdtKind()), *bases_enum,
+ layout_info);
+
+ auto members_enum = udt.findAllChildren<PDBSymbolData>();
+ if (members_enum)
+ AddRecordMembers(symbol_file, compiler_type, *members_enum, layout_info);
+
+ auto methods_enum = udt.findAllChildren<PDBSymbolFunc>();
+ if (methods_enum)
+ AddRecordMethods(symbol_file, compiler_type, *methods_enum);
+
+ m_ast.AddMethodOverridesForCXXRecordType(compiler_type.GetOpaqueQualType());
+ ClangASTContext::BuildIndirectFields(compiler_type);
+ ClangASTContext::CompleteTagDeclarationDefinition(compiler_type);
+
+ clang::CXXRecordDecl *record_decl =
+ m_ast.GetAsCXXRecordDecl(compiler_type.GetOpaqueQualType());
+ if (!record_decl)
+ return static_cast<bool>(compiler_type);
+
+ GetClangASTImporter().InsertRecordDecl(record_decl, layout_info);
+
+ return static_cast<bool>(compiler_type);
+}
+
+void PDBASTParser::AddRecordMembers(
+ lldb_private::SymbolFile &symbol_file,
+ lldb_private::CompilerType &record_type,
+ PDBDataSymbolEnumerator &members_enum,
+ lldb_private::ClangASTImporter::LayoutInfo &layout_info) {
+ while (auto member = members_enum.getNext()) {
+ if (member->isCompilerGenerated())
+ continue;
+
+ auto member_name = member->getName();
+
+ auto member_type = symbol_file.ResolveTypeUID(member->getTypeId());
+ if (!member_type)
+ continue;
+
+ auto member_comp_type = member_type->GetLayoutCompilerType();
+ if (!member_comp_type.GetCompleteType()) {
+ symbol_file.GetObjectFile()->GetModule()->ReportError(
+ ":: Class '%s' has a member '%s' of type '%s' "
+ "which does not have a complete definition.",
+ record_type.GetTypeName().GetCString(), member_name.c_str(),
+ member_comp_type.GetTypeName().GetCString());
+ if (ClangASTContext::StartTagDeclarationDefinition(member_comp_type))
+ ClangASTContext::CompleteTagDeclarationDefinition(member_comp_type);
+ }
+
+ auto access = TranslateMemberAccess(member->getAccess());
+
+ switch (member->getDataKind()) {
+ case PDB_DataKind::Member: {
+ auto location_type = member->getLocationType();
+
+ auto bit_size = member->getLength();
+ if (location_type == PDB_LocType::ThisRel)
+ bit_size *= 8;
+
+ auto decl = ClangASTContext::AddFieldToRecordType(
+ record_type, member_name.c_str(), member_comp_type, access, bit_size);
+ if (!decl)
+ continue;
+
+ m_uid_to_decl[member->getSymIndexId()] = decl;
+
+ auto offset = member->getOffset() * 8;
+ if (location_type == PDB_LocType::BitField)
+ offset += member->getBitPosition();
+
+ layout_info.field_offsets.insert(std::make_pair(decl, offset));
+
+ break;
+ }
+ case PDB_DataKind::StaticMember: {
+ auto decl = ClangASTContext::AddVariableToRecordType(
+ record_type, member_name.c_str(), member_comp_type, access);
+ if (!decl)
+ continue;
+
+ m_uid_to_decl[member->getSymIndexId()] = decl;
+
+ break;
+ }
+ default:
+ llvm_unreachable("unsupported PDB data kind");
+ }
+ }
+}
+
+void PDBASTParser::AddRecordBases(
+ lldb_private::SymbolFile &symbol_file,
+ lldb_private::CompilerType &record_type, int record_kind,
+ PDBBaseClassSymbolEnumerator &bases_enum,
+ lldb_private::ClangASTImporter::LayoutInfo &layout_info) const {
+ std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> base_classes;
+
+ while (auto base = bases_enum.getNext()) {
+ auto base_type = symbol_file.ResolveTypeUID(base->getTypeId());
+ if (!base_type)
+ continue;
+
+ auto base_comp_type = base_type->GetFullCompilerType();
+ if (!base_comp_type.GetCompleteType()) {
+ symbol_file.GetObjectFile()->GetModule()->ReportError(
+ ":: Class '%s' has a base class '%s' "
+ "which does not have a complete definition.",
+ record_type.GetTypeName().GetCString(),
+ base_comp_type.GetTypeName().GetCString());
+ if (ClangASTContext::StartTagDeclarationDefinition(base_comp_type))
+ ClangASTContext::CompleteTagDeclarationDefinition(base_comp_type);
+ }
+
+ auto access = TranslateMemberAccess(base->getAccess());
+
+ auto is_virtual = base->isVirtualBaseClass();
+
+ std::unique_ptr<clang::CXXBaseSpecifier> base_spec =
+ m_ast.CreateBaseClassSpecifier(base_comp_type.GetOpaqueQualType(),
+ access, is_virtual,
+ record_kind == clang::TTK_Class);
+ lldbassert(base_spec);
+
+ base_classes.push_back(std::move(base_spec));
+
+ if (is_virtual)
+ continue;
+
+ auto decl = m_ast.GetAsCXXRecordDecl(base_comp_type.GetOpaqueQualType());
+ if (!decl)
+ continue;
+
+ auto offset = clang::CharUnits::fromQuantity(base->getOffset());
+ layout_info.base_offsets.insert(std::make_pair(decl, offset));
+ }
+
+ m_ast.TransferBaseClasses(record_type.GetOpaqueQualType(),
+ std::move(base_classes));
+}
+
+void PDBASTParser::AddRecordMethods(lldb_private::SymbolFile &symbol_file,
+ lldb_private::CompilerType &record_type,
+ PDBFuncSymbolEnumerator &methods_enum) {
+ while (std::unique_ptr<PDBSymbolFunc> method = methods_enum.getNext())
+ if (clang::CXXMethodDecl *decl =
+ AddRecordMethod(symbol_file, record_type, *method))
+ m_uid_to_decl[method->getSymIndexId()] = decl;
+}
+
+clang::CXXMethodDecl *
+PDBASTParser::AddRecordMethod(lldb_private::SymbolFile &symbol_file,
+ lldb_private::CompilerType &record_type,
+ const llvm::pdb::PDBSymbolFunc &method) const {
+ std::string name = MSVCUndecoratedNameParser::DropScope(method.getName());
+
+ Type *method_type = symbol_file.ResolveTypeUID(method.getSymIndexId());
+ // MSVC specific __vecDelDtor.
+ if (!method_type)
+ return nullptr;
+
+ CompilerType method_comp_type = method_type->GetFullCompilerType();
+ if (!method_comp_type.GetCompleteType()) {
+ symbol_file.GetObjectFile()->GetModule()->ReportError(
+ ":: Class '%s' has a method '%s' whose type cannot be completed.",
+ record_type.GetTypeName().GetCString(),
+ method_comp_type.GetTypeName().GetCString());
+ if (ClangASTContext::StartTagDeclarationDefinition(method_comp_type))
+ ClangASTContext::CompleteTagDeclarationDefinition(method_comp_type);
+ }
+
+ AccessType access = TranslateMemberAccess(method.getAccess());
+ if (access == eAccessNone)
+ access = eAccessPublic;
+
+ // TODO: get mangled name for the method.
+ return m_ast.AddMethodToCXXRecordType(
+ record_type.GetOpaqueQualType(), name.c_str(),
+ /*mangled_name*/ nullptr, method_comp_type, access, method.isVirtual(),
+ method.isStatic(), method.hasInlineAttribute(),
+ /*is_explicit*/ false, // FIXME: Need this field in CodeView.
+ /*is_attr_used*/ false,
+ /*is_artificial*/ method.isCompilerGenerated());
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.h
new file mode 100644
index 000000000000..9221d42b2020
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.h
@@ -0,0 +1,116 @@
+//===-- PDBASTParser.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_PLUGINS_SYMBOLFILE_PDB_PDBASTPARSER_H
+#define LLDB_PLUGINS_SYMBOLFILE_PDB_PDBASTPARSER_H
+
+#include "lldb/lldb-forward.h"
+
+#include "lldb/Symbol/ClangASTImporter.h"
+
+class SymbolFilePDB;
+
+namespace clang {
+class CharUnits;
+class CXXRecordDecl;
+class FieldDecl;
+class RecordDecl;
+} // namespace clang
+
+namespace lldb_private {
+class ClangASTContext;
+class CompilerType;
+} // namespace lldb_private
+
+namespace llvm {
+namespace pdb {
+template <typename ChildType> class ConcreteSymbolEnumerator;
+
+class PDBSymbol;
+class PDBSymbolData;
+class PDBSymbolFunc;
+class PDBSymbolTypeBaseClass;
+class PDBSymbolTypeBuiltin;
+class PDBSymbolTypeUDT;
+} // namespace pdb
+} // namespace llvm
+
+class PDBASTParser {
+public:
+ PDBASTParser(lldb_private::ClangASTContext &ast);
+ ~PDBASTParser();
+
+ lldb::TypeSP CreateLLDBTypeFromPDBType(const llvm::pdb::PDBSymbol &type);
+ bool CompleteTypeFromPDB(lldb_private::CompilerType &compiler_type);
+
+ clang::Decl *GetDeclForSymbol(const llvm::pdb::PDBSymbol &symbol);
+
+ clang::DeclContext *
+ GetDeclContextForSymbol(const llvm::pdb::PDBSymbol &symbol);
+ clang::DeclContext *
+ GetDeclContextContainingSymbol(const llvm::pdb::PDBSymbol &symbol);
+
+ void ParseDeclsForDeclContext(const clang::DeclContext *decl_context);
+
+ clang::NamespaceDecl *FindNamespaceDecl(const clang::DeclContext *parent,
+ llvm::StringRef name);
+
+ lldb_private::ClangASTImporter &GetClangASTImporter() {
+ return m_ast_importer;
+ }
+
+private:
+ typedef llvm::DenseMap<clang::CXXRecordDecl *, lldb::user_id_t>
+ CXXRecordDeclToUidMap;
+ typedef llvm::DenseMap<lldb::user_id_t, clang::Decl *> UidToDeclMap;
+ typedef std::set<clang::NamespaceDecl *> NamespacesSet;
+ typedef llvm::DenseMap<clang::DeclContext *, NamespacesSet>
+ ParentToNamespacesMap;
+ typedef llvm::DenseMap<clang::DeclContext *, lldb::user_id_t>
+ DeclContextToUidMap;
+ typedef llvm::pdb::ConcreteSymbolEnumerator<llvm::pdb::PDBSymbolData>
+ PDBDataSymbolEnumerator;
+ typedef llvm::pdb::ConcreteSymbolEnumerator<llvm::pdb::PDBSymbolTypeBaseClass>
+ PDBBaseClassSymbolEnumerator;
+ typedef llvm::pdb::ConcreteSymbolEnumerator<llvm::pdb::PDBSymbolFunc>
+ PDBFuncSymbolEnumerator;
+
+ bool AddEnumValue(lldb_private::CompilerType enum_type,
+ const llvm::pdb::PDBSymbolData &data);
+ bool CompleteTypeFromUDT(lldb_private::SymbolFile &symbol_file,
+ lldb_private::CompilerType &compiler_type,
+ llvm::pdb::PDBSymbolTypeUDT &udt);
+ void
+ AddRecordMembers(lldb_private::SymbolFile &symbol_file,
+ lldb_private::CompilerType &record_type,
+ PDBDataSymbolEnumerator &members_enum,
+ lldb_private::ClangASTImporter::LayoutInfo &layout_info);
+ void
+ AddRecordBases(lldb_private::SymbolFile &symbol_file,
+ lldb_private::CompilerType &record_type, int record_kind,
+ PDBBaseClassSymbolEnumerator &bases_enum,
+ lldb_private::ClangASTImporter::LayoutInfo &layout_info) const;
+ void AddRecordMethods(lldb_private::SymbolFile &symbol_file,
+ lldb_private::CompilerType &record_type,
+ PDBFuncSymbolEnumerator &methods_enum);
+ clang::CXXMethodDecl *
+ AddRecordMethod(lldb_private::SymbolFile &symbol_file,
+ lldb_private::CompilerType &record_type,
+ const llvm::pdb::PDBSymbolFunc &method) const;
+
+ lldb_private::ClangASTContext &m_ast;
+ lldb_private::ClangASTImporter m_ast_importer;
+
+ CXXRecordDeclToUidMap m_forward_decl_to_uid;
+ UidToDeclMap m_uid_to_decl;
+ ParentToNamespacesMap m_parent_to_namespaces;
+ NamespacesSet m_namespaces;
+ DeclContextToUidMap m_decl_context_to_uid;
+};
+
+#endif // LLDB_PLUGINS_SYMBOLFILE_PDB_PDBASTPARSER_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.cpp
new file mode 100644
index 000000000000..1c17bf6563b3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.cpp
@@ -0,0 +1,181 @@
+//===-- PDBLocationToDWARFExpression.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 "PDBLocationToDWARFExpression.h"
+
+#include "lldb/Core/Section.h"
+#include "lldb/Core/StreamBuffer.h"
+#include "lldb/Core/dwarf.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Utility/DataBufferHeap.h"
+
+#include "llvm/DebugInfo/CodeView/CodeView.h"
+#include "llvm/DebugInfo/PDB/IPDBSession.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
+
+#include "Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.h"
+#include "Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::npdb;
+using namespace llvm::pdb;
+
+static std::unique_ptr<IPDBFrameData>
+GetCorrespondingFrameData(const IPDBSession &session,
+ const Variable::RangeList &ranges) {
+ auto enumFrameData = session.getFrameData();
+ if (!enumFrameData)
+ return nullptr;
+
+ std::unique_ptr<IPDBFrameData> found;
+ while (auto fd = enumFrameData->getNext()) {
+ Range<lldb::addr_t, lldb::addr_t> fdRange(fd->getVirtualAddress(),
+ fd->getLengthBlock());
+
+ for (size_t i = 0; i < ranges.GetSize(); i++) {
+ auto range = ranges.GetEntryAtIndex(i);
+ if (!range)
+ continue;
+
+ if (!range->DoesIntersect(fdRange))
+ continue;
+
+ found = std::move(fd);
+
+ break;
+ }
+ }
+
+ return found;
+}
+
+static bool EmitVFrameEvaluationDWARFExpression(
+ llvm::StringRef program, llvm::Triple::ArchType arch_type, Stream &stream) {
+ // VFrame value always stored in $TO pseudo-register
+ return TranslateFPOProgramToDWARFExpression(program, "$T0", arch_type,
+ stream);
+}
+
+DWARFExpression ConvertPDBLocationToDWARFExpression(
+ ModuleSP module, const PDBSymbolData &symbol,
+ const Variable::RangeList &ranges, bool &is_constant) {
+ is_constant = true;
+
+ if (!module)
+ return DWARFExpression();
+
+ const ArchSpec &architecture = module->GetArchitecture();
+ llvm::Triple::ArchType arch_type = architecture.GetMachine();
+ ByteOrder byte_order = architecture.GetByteOrder();
+ uint32_t address_size = architecture.GetAddressByteSize();
+ uint32_t byte_size = architecture.GetDataByteSize();
+ if (byte_order == eByteOrderInvalid || address_size == 0)
+ return DWARFExpression();
+
+ RegisterKind register_kind = eRegisterKindDWARF;
+ StreamBuffer<32> stream(Stream::eBinary, address_size, byte_order);
+ switch (symbol.getLocationType()) {
+ case PDB_LocType::Static:
+ case PDB_LocType::TLS: {
+ stream.PutHex8(DW_OP_addr);
+
+ SectionList *section_list = module->GetSectionList();
+ if (!section_list)
+ return DWARFExpression();
+
+ uint32_t section_id = symbol.getAddressSection();
+
+ auto section = section_list->FindSectionByID(section_id);
+ if (!section)
+ return DWARFExpression();
+
+ uint32_t offset = symbol.getAddressOffset();
+ stream.PutMaxHex64(section->GetFileAddress() + offset, address_size,
+ byte_order);
+
+ is_constant = false;
+
+ break;
+ }
+ case PDB_LocType::RegRel: {
+ uint32_t reg_num;
+ auto reg_id = symbol.getRegisterId();
+ if (reg_id == llvm::codeview::RegisterId::VFRAME) {
+ if (auto fd = GetCorrespondingFrameData(symbol.getSession(), ranges)) {
+ if (EmitVFrameEvaluationDWARFExpression(fd->getProgram(), arch_type,
+ stream)) {
+ int32_t offset = symbol.getOffset();
+ stream.PutHex8(DW_OP_consts);
+ stream.PutSLEB128(offset);
+ stream.PutHex8(DW_OP_plus);
+
+ register_kind = eRegisterKindLLDB;
+
+ is_constant = false;
+ break;
+ }
+ }
+
+ register_kind = eRegisterKindGeneric;
+ reg_num = LLDB_REGNUM_GENERIC_FP;
+ } else {
+ register_kind = eRegisterKindLLDB;
+ reg_num = GetLLDBRegisterNumber(arch_type, reg_id);
+ if (reg_num == LLDB_INVALID_REGNUM)
+ return DWARFExpression();
+ }
+
+ if (reg_num > 31) {
+ stream.PutHex8(DW_OP_bregx);
+ stream.PutULEB128(reg_num);
+ } else
+ stream.PutHex8(DW_OP_breg0 + reg_num);
+
+ int32_t offset = symbol.getOffset();
+ stream.PutSLEB128(offset);
+
+ is_constant = false;
+
+ break;
+ }
+ case PDB_LocType::Enregistered: {
+ register_kind = eRegisterKindLLDB;
+ uint32_t reg_num = GetLLDBRegisterNumber(arch_type, symbol.getRegisterId());
+ if (reg_num == LLDB_INVALID_REGNUM)
+ return DWARFExpression();
+
+ if (reg_num > 31) {
+ stream.PutHex8(DW_OP_regx);
+ stream.PutULEB128(reg_num);
+ } else
+ stream.PutHex8(DW_OP_reg0 + reg_num);
+
+ is_constant = false;
+
+ break;
+ }
+ case PDB_LocType::Constant: {
+ Variant value = symbol.getValue();
+ stream.PutRawBytes(&value.Value, sizeof(value.Value),
+ endian::InlHostByteOrder());
+ break;
+ }
+ default:
+ return DWARFExpression();
+ }
+
+ DataBufferSP buffer =
+ std::make_shared<DataBufferHeap>(stream.GetData(), stream.GetSize());
+ DataExtractor extractor(buffer, byte_order, address_size, byte_size);
+ DWARFExpression result(module, extractor, nullptr, 0, buffer->GetByteSize());
+ result.SetRegisterKind(register_kind);
+
+ return result;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.h
new file mode 100644
index 000000000000..2e9d1386d537
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.h
@@ -0,0 +1,47 @@
+//===-- PDBLocationToDWARFExpression.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_Plugins_SymbolFile_PDB_PDBLocationToDWARFExpression_h_
+#define lldb_Plugins_SymbolFile_PDB_PDBLocationToDWARFExpression_h_
+
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/Variable.h"
+
+namespace lldb_private {
+class DWARFExpression;
+}
+
+namespace llvm {
+namespace pdb {
+class PDBSymbolData;
+}
+} // namespace llvm
+
+/// Converts a location information from a PDB symbol to a DWARF expression
+///
+/// \param[in] module
+/// The module \a symbol belongs to.
+///
+/// \param[in] symbol
+/// The symbol with a location information to convert.
+///
+/// \param[in] ranges
+/// Ranges where this variable is valid.
+///
+/// \param[out] is_constant
+/// Set to \b true if the result expression is a constant value data,
+/// and \b false if it is a DWARF bytecode.
+///
+/// \return
+/// The DWARF expression corresponding to the location data of \a symbol.
+lldb_private::DWARFExpression
+ConvertPDBLocationToDWARFExpression(lldb::ModuleSP module,
+ const llvm::pdb::PDBSymbolData &symbol,
+ const lldb_private::Variable::RangeList &ranges,
+ bool &is_constant);
+#endif
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp
new file mode 100644
index 000000000000..17dfcdaceb9c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp
@@ -0,0 +1,1999 @@
+//===-- SymbolFilePDB.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 "SymbolFilePDB.h"
+
+#include "PDBASTParser.h"
+#include "PDBLocationToDWARFExpression.h"
+
+#include "clang/Lex/Lexer.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Symbol/TypeMap.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Utility/RegularExpression.h"
+
+#include "llvm/DebugInfo/PDB/GenericError.h"
+#include "llvm/DebugInfo/PDB/IPDBDataStream.h"
+#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"
+#include "llvm/DebugInfo/PDB/IPDBLineNumber.h"
+#include "llvm/DebugInfo/PDB/IPDBSectionContrib.h"
+#include "llvm/DebugInfo/PDB/IPDBSourceFile.h"
+#include "llvm/DebugInfo/PDB/IPDBTable.h"
+#include "llvm/DebugInfo/PDB/PDBSymbol.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolBlock.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolCompilandDetails.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolExe.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugEnd.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugStart.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolPublicSymbol.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h"
+
+#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
+#include "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h"
+#include "Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h"
+
+#include <regex>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm::pdb;
+
+namespace {
+lldb::LanguageType TranslateLanguage(PDB_Lang lang) {
+ switch (lang) {
+ case PDB_Lang::Cpp:
+ return lldb::LanguageType::eLanguageTypeC_plus_plus;
+ case PDB_Lang::C:
+ return lldb::LanguageType::eLanguageTypeC;
+ case PDB_Lang::Swift:
+ return lldb::LanguageType::eLanguageTypeSwift;
+ default:
+ return lldb::LanguageType::eLanguageTypeUnknown;
+ }
+}
+
+bool ShouldAddLine(uint32_t requested_line, uint32_t actual_line,
+ uint32_t addr_length) {
+ return ((requested_line == 0 || actual_line == requested_line) &&
+ addr_length > 0);
+}
+} // namespace
+
+static bool ShouldUseNativeReader() {
+#if defined(_WIN32)
+ llvm::StringRef use_native = ::getenv("LLDB_USE_NATIVE_PDB_READER");
+ return use_native.equals_lower("on") || use_native.equals_lower("yes") ||
+ use_native.equals_lower("1") || use_native.equals_lower("true");
+#else
+ return true;
+#endif
+}
+
+void SymbolFilePDB::Initialize() {
+ if (ShouldUseNativeReader()) {
+ npdb::SymbolFileNativePDB::Initialize();
+ } else {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ DebuggerInitialize);
+ }
+}
+
+void SymbolFilePDB::Terminate() {
+ if (ShouldUseNativeReader()) {
+ npdb::SymbolFileNativePDB::Terminate();
+ } else {
+ PluginManager::UnregisterPlugin(CreateInstance);
+ }
+}
+
+void SymbolFilePDB::DebuggerInitialize(lldb_private::Debugger &debugger) {}
+
+lldb_private::ConstString SymbolFilePDB::GetPluginNameStatic() {
+ static ConstString g_name("pdb");
+ return g_name;
+}
+
+const char *SymbolFilePDB::GetPluginDescriptionStatic() {
+ return "Microsoft PDB debug symbol file reader.";
+}
+
+lldb_private::SymbolFile *
+SymbolFilePDB::CreateInstance(lldb_private::ObjectFile *obj_file) {
+ return new SymbolFilePDB(obj_file);
+}
+
+SymbolFilePDB::SymbolFilePDB(lldb_private::ObjectFile *object_file)
+ : SymbolFile(object_file), m_session_up(), m_global_scope_up(),
+ m_cached_compile_unit_count(0) {}
+
+SymbolFilePDB::~SymbolFilePDB() {}
+
+uint32_t SymbolFilePDB::CalculateAbilities() {
+ uint32_t abilities = 0;
+ if (!m_obj_file)
+ return 0;
+
+ if (!m_session_up) {
+ // Lazily load and match the PDB file, but only do this once.
+ std::string exePath = m_obj_file->GetFileSpec().GetPath();
+ auto error = loadDataForEXE(PDB_ReaderType::DIA, llvm::StringRef(exePath),
+ m_session_up);
+ if (error) {
+ llvm::consumeError(std::move(error));
+ auto module_sp = m_obj_file->GetModule();
+ if (!module_sp)
+ return 0;
+ // See if any symbol file is specified through `--symfile` option.
+ FileSpec symfile = module_sp->GetSymbolFileFileSpec();
+ if (!symfile)
+ return 0;
+ error = loadDataForPDB(PDB_ReaderType::DIA,
+ llvm::StringRef(symfile.GetPath()), m_session_up);
+ if (error) {
+ llvm::consumeError(std::move(error));
+ return 0;
+ }
+ }
+ }
+ if (!m_session_up)
+ return 0;
+
+ auto enum_tables_up = m_session_up->getEnumTables();
+ if (!enum_tables_up)
+ return 0;
+ while (auto table_up = enum_tables_up->getNext()) {
+ if (table_up->getItemCount() == 0)
+ continue;
+ auto type = table_up->getTableType();
+ switch (type) {
+ case PDB_TableType::Symbols:
+ // This table represents a store of symbols with types listed in
+ // PDBSym_Type
+ abilities |= (CompileUnits | Functions | Blocks | GlobalVariables |
+ LocalVariables | VariableTypes);
+ break;
+ case PDB_TableType::LineNumbers:
+ abilities |= LineTables;
+ break;
+ default:
+ break;
+ }
+ }
+ return abilities;
+}
+
+void SymbolFilePDB::InitializeObject() {
+ lldb::addr_t obj_load_address = m_obj_file->GetBaseAddress().GetFileAddress();
+ lldbassert(obj_load_address && obj_load_address != LLDB_INVALID_ADDRESS);
+ m_session_up->setLoadAddress(obj_load_address);
+ if (!m_global_scope_up)
+ m_global_scope_up = m_session_up->getGlobalScope();
+ lldbassert(m_global_scope_up.get());
+}
+
+uint32_t SymbolFilePDB::GetNumCompileUnits() {
+ if (m_cached_compile_unit_count == 0) {
+ auto compilands = m_global_scope_up->findAllChildren<PDBSymbolCompiland>();
+ if (!compilands)
+ return 0;
+
+ // The linker could link *.dll (compiland language = LINK), or import
+ // *.dll. For example, a compiland with name `Import:KERNEL32.dll` could be
+ // found as a child of the global scope (PDB executable). Usually, such
+ // compilands contain `thunk` symbols in which we are not interested for
+ // now. However we still count them in the compiland list. If we perform
+ // any compiland related activity, like finding symbols through
+ // llvm::pdb::IPDBSession methods, such compilands will all be searched
+ // automatically no matter whether we include them or not.
+ m_cached_compile_unit_count = compilands->getChildCount();
+
+ // The linker can inject an additional "dummy" compilation unit into the
+ // PDB. Ignore this special compile unit for our purposes, if it is there.
+ // It is always the last one.
+ auto last_compiland_up =
+ compilands->getChildAtIndex(m_cached_compile_unit_count - 1);
+ lldbassert(last_compiland_up.get());
+ std::string name = last_compiland_up->getName();
+ if (name == "* Linker *")
+ --m_cached_compile_unit_count;
+ }
+ return m_cached_compile_unit_count;
+}
+
+void SymbolFilePDB::GetCompileUnitIndex(
+ const llvm::pdb::PDBSymbolCompiland &pdb_compiland, uint32_t &index) {
+ auto results_up = m_global_scope_up->findAllChildren<PDBSymbolCompiland>();
+ if (!results_up)
+ return;
+ auto uid = pdb_compiland.getSymIndexId();
+ for (uint32_t cu_idx = 0; cu_idx < GetNumCompileUnits(); ++cu_idx) {
+ auto compiland_up = results_up->getChildAtIndex(cu_idx);
+ if (!compiland_up)
+ continue;
+ if (compiland_up->getSymIndexId() == uid) {
+ index = cu_idx;
+ return;
+ }
+ }
+ index = UINT32_MAX;
+ return;
+}
+
+std::unique_ptr<llvm::pdb::PDBSymbolCompiland>
+SymbolFilePDB::GetPDBCompilandByUID(uint32_t uid) {
+ return m_session_up->getConcreteSymbolById<PDBSymbolCompiland>(uid);
+}
+
+lldb::CompUnitSP SymbolFilePDB::ParseCompileUnitAtIndex(uint32_t index) {
+ if (index >= GetNumCompileUnits())
+ return CompUnitSP();
+
+ // Assuming we always retrieve same compilands listed in same order through
+ // `PDBSymbolExe::findAllChildren` method, otherwise using `index` to get a
+ // compile unit makes no sense.
+ auto results = m_global_scope_up->findAllChildren<PDBSymbolCompiland>();
+ if (!results)
+ return CompUnitSP();
+ auto compiland_up = results->getChildAtIndex(index);
+ if (!compiland_up)
+ return CompUnitSP();
+ return ParseCompileUnitForUID(compiland_up->getSymIndexId(), index);
+}
+
+lldb::LanguageType SymbolFilePDB::ParseLanguage(CompileUnit &comp_unit) {
+ auto compiland_up = GetPDBCompilandByUID(comp_unit.GetID());
+ if (!compiland_up)
+ return lldb::eLanguageTypeUnknown;
+ auto details = compiland_up->findOneChild<PDBSymbolCompilandDetails>();
+ if (!details)
+ return lldb::eLanguageTypeUnknown;
+ return TranslateLanguage(details->getLanguage());
+}
+
+lldb_private::Function *
+SymbolFilePDB::ParseCompileUnitFunctionForPDBFunc(const PDBSymbolFunc &pdb_func,
+ CompileUnit &comp_unit) {
+ if (FunctionSP result = comp_unit.FindFunctionByUID(pdb_func.getSymIndexId()))
+ return result.get();
+
+ auto file_vm_addr = pdb_func.getVirtualAddress();
+ if (file_vm_addr == LLDB_INVALID_ADDRESS || file_vm_addr == 0)
+ return nullptr;
+
+ auto func_length = pdb_func.getLength();
+ AddressRange func_range =
+ AddressRange(file_vm_addr, func_length,
+ GetObjectFile()->GetModule()->GetSectionList());
+ if (!func_range.GetBaseAddress().IsValid())
+ return nullptr;
+
+ lldb_private::Type *func_type = ResolveTypeUID(pdb_func.getSymIndexId());
+ if (!func_type)
+ return nullptr;
+
+ user_id_t func_type_uid = pdb_func.getSignatureId();
+
+ Mangled mangled = GetMangledForPDBFunc(pdb_func);
+
+ FunctionSP func_sp =
+ std::make_shared<Function>(&comp_unit, pdb_func.getSymIndexId(),
+ func_type_uid, mangled, func_type, func_range);
+
+ comp_unit.AddFunction(func_sp);
+
+ LanguageType lang = ParseLanguage(comp_unit);
+ TypeSystem *type_system = GetTypeSystemForLanguage(lang);
+ if (!type_system)
+ return nullptr;
+ ClangASTContext *clang_type_system =
+ llvm::dyn_cast_or_null<ClangASTContext>(type_system);
+ if (!clang_type_system)
+ return nullptr;
+ clang_type_system->GetPDBParser()->GetDeclForSymbol(pdb_func);
+
+ return func_sp.get();
+}
+
+size_t SymbolFilePDB::ParseFunctions(CompileUnit &comp_unit) {
+ size_t func_added = 0;
+ auto compiland_up = GetPDBCompilandByUID(comp_unit.GetID());
+ if (!compiland_up)
+ return 0;
+ auto results_up = compiland_up->findAllChildren<PDBSymbolFunc>();
+ if (!results_up)
+ return 0;
+ while (auto pdb_func_up = results_up->getNext()) {
+ auto func_sp = comp_unit.FindFunctionByUID(pdb_func_up->getSymIndexId());
+ if (!func_sp) {
+ if (ParseCompileUnitFunctionForPDBFunc(*pdb_func_up, comp_unit))
+ ++func_added;
+ }
+ }
+ return func_added;
+}
+
+bool SymbolFilePDB::ParseLineTable(CompileUnit &comp_unit) {
+ if (comp_unit.GetLineTable())
+ return true;
+ return ParseCompileUnitLineTable(comp_unit, 0);
+}
+
+bool SymbolFilePDB::ParseDebugMacros(CompileUnit &comp_unit) {
+ // PDB doesn't contain information about macros
+ return false;
+}
+
+bool SymbolFilePDB::ParseSupportFiles(
+ CompileUnit &comp_unit, lldb_private::FileSpecList &support_files) {
+
+ // In theory this is unnecessary work for us, because all of this information
+ // is easily (and quickly) accessible from DebugInfoPDB, so caching it a
+ // second time seems like a waste. Unfortunately, there's no good way around
+ // this short of a moderate refactor since SymbolVendor depends on being able
+ // to cache this list.
+ auto compiland_up = GetPDBCompilandByUID(comp_unit.GetID());
+ if (!compiland_up)
+ return false;
+ auto files = m_session_up->getSourceFilesForCompiland(*compiland_up);
+ if (!files || files->getChildCount() == 0)
+ return false;
+
+ while (auto file = files->getNext()) {
+ FileSpec spec(file->getFileName(), FileSpec::Style::windows);
+ support_files.AppendIfUnique(spec);
+ }
+
+ // LLDB uses the DWARF-like file numeration (one based),
+ // the zeroth file is the compile unit itself
+ support_files.Insert(0, comp_unit);
+
+ return true;
+}
+
+bool SymbolFilePDB::ParseImportedModules(
+ const lldb_private::SymbolContext &sc,
+ std::vector<SourceModule> &imported_modules) {
+ // PDB does not yet support module debug info
+ return false;
+}
+
+static size_t ParseFunctionBlocksForPDBSymbol(
+ uint64_t func_file_vm_addr, const llvm::pdb::PDBSymbol *pdb_symbol,
+ lldb_private::Block *parent_block, bool is_top_parent) {
+ assert(pdb_symbol && parent_block);
+
+ size_t num_added = 0;
+ switch (pdb_symbol->getSymTag()) {
+ case PDB_SymType::Block:
+ case PDB_SymType::Function: {
+ Block *block = nullptr;
+ auto &raw_sym = pdb_symbol->getRawSymbol();
+ if (auto *pdb_func = llvm::dyn_cast<PDBSymbolFunc>(pdb_symbol)) {
+ if (pdb_func->hasNoInlineAttribute())
+ break;
+ if (is_top_parent)
+ block = parent_block;
+ else
+ break;
+ } else if (llvm::dyn_cast<PDBSymbolBlock>(pdb_symbol)) {
+ auto uid = pdb_symbol->getSymIndexId();
+ if (parent_block->FindBlockByID(uid))
+ break;
+ if (raw_sym.getVirtualAddress() < func_file_vm_addr)
+ break;
+
+ auto block_sp = std::make_shared<Block>(pdb_symbol->getSymIndexId());
+ parent_block->AddChild(block_sp);
+ block = block_sp.get();
+ } else
+ llvm_unreachable("Unexpected PDB symbol!");
+
+ block->AddRange(Block::Range(
+ raw_sym.getVirtualAddress() - func_file_vm_addr, raw_sym.getLength()));
+ block->FinalizeRanges();
+ ++num_added;
+
+ auto results_up = pdb_symbol->findAllChildren();
+ if (!results_up)
+ break;
+ while (auto symbol_up = results_up->getNext()) {
+ num_added += ParseFunctionBlocksForPDBSymbol(
+ func_file_vm_addr, symbol_up.get(), block, false);
+ }
+ } break;
+ default:
+ break;
+ }
+ return num_added;
+}
+
+size_t SymbolFilePDB::ParseBlocksRecursive(Function &func) {
+ size_t num_added = 0;
+ auto uid = func.GetID();
+ auto pdb_func_up = m_session_up->getConcreteSymbolById<PDBSymbolFunc>(uid);
+ if (!pdb_func_up)
+ return 0;
+ Block &parent_block = func.GetBlock(false);
+ num_added = ParseFunctionBlocksForPDBSymbol(
+ pdb_func_up->getVirtualAddress(), pdb_func_up.get(), &parent_block, true);
+ return num_added;
+}
+
+size_t SymbolFilePDB::ParseTypes(CompileUnit &comp_unit) {
+
+ size_t num_added = 0;
+ auto compiland = GetPDBCompilandByUID(comp_unit.GetID());
+ if (!compiland)
+ return 0;
+
+ auto ParseTypesByTagFn = [&num_added, this](const PDBSymbol &raw_sym) {
+ std::unique_ptr<IPDBEnumSymbols> results;
+ PDB_SymType tags_to_search[] = {PDB_SymType::Enum, PDB_SymType::Typedef,
+ PDB_SymType::UDT};
+ for (auto tag : tags_to_search) {
+ results = raw_sym.findAllChildren(tag);
+ if (!results || results->getChildCount() == 0)
+ continue;
+ while (auto symbol = results->getNext()) {
+ switch (symbol->getSymTag()) {
+ case PDB_SymType::Enum:
+ case PDB_SymType::UDT:
+ case PDB_SymType::Typedef:
+ break;
+ default:
+ continue;
+ }
+
+ // This should cause the type to get cached and stored in the `m_types`
+ // lookup.
+ if (auto type = ResolveTypeUID(symbol->getSymIndexId())) {
+ // Resolve the type completely to avoid a completion
+ // (and so a list change, which causes an iterators invalidation)
+ // during a TypeList dumping
+ type->GetFullCompilerType();
+ ++num_added;
+ }
+ }
+ }
+ };
+
+ ParseTypesByTagFn(*compiland);
+
+ // Also parse global types particularly coming from this compiland.
+ // Unfortunately, PDB has no compiland information for each global type. We
+ // have to parse them all. But ensure we only do this once.
+ static bool parse_all_global_types = false;
+ if (!parse_all_global_types) {
+ ParseTypesByTagFn(*m_global_scope_up);
+ parse_all_global_types = true;
+ }
+ return num_added;
+}
+
+size_t
+SymbolFilePDB::ParseVariablesForContext(const lldb_private::SymbolContext &sc) {
+ if (!sc.comp_unit)
+ return 0;
+
+ size_t num_added = 0;
+ if (sc.function) {
+ auto pdb_func = m_session_up->getConcreteSymbolById<PDBSymbolFunc>(
+ sc.function->GetID());
+ if (!pdb_func)
+ return 0;
+
+ num_added += ParseVariables(sc, *pdb_func);
+ sc.function->GetBlock(false).SetDidParseVariables(true, true);
+ } else if (sc.comp_unit) {
+ auto compiland = GetPDBCompilandByUID(sc.comp_unit->GetID());
+ if (!compiland)
+ return 0;
+
+ if (sc.comp_unit->GetVariableList(false))
+ return 0;
+
+ auto results = m_global_scope_up->findAllChildren<PDBSymbolData>();
+ if (results && results->getChildCount()) {
+ while (auto result = results->getNext()) {
+ auto cu_id = GetCompilandId(*result);
+ // FIXME: We are not able to determine variable's compile unit.
+ if (cu_id == 0)
+ continue;
+
+ if (cu_id == sc.comp_unit->GetID())
+ num_added += ParseVariables(sc, *result);
+ }
+ }
+
+ // FIXME: A `file static` or `global constant` variable appears both in
+ // compiland's children and global scope's children with unexpectedly
+ // different symbol's Id making it ambiguous.
+
+ // FIXME: 'local constant', for example, const char var[] = "abc", declared
+ // in a function scope, can't be found in PDB.
+
+ // Parse variables in this compiland.
+ num_added += ParseVariables(sc, *compiland);
+ }
+
+ return num_added;
+}
+
+lldb_private::Type *SymbolFilePDB::ResolveTypeUID(lldb::user_id_t type_uid) {
+ auto find_result = m_types.find(type_uid);
+ if (find_result != m_types.end())
+ return find_result->second.get();
+
+ TypeSystem *type_system =
+ GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus);
+ ClangASTContext *clang_type_system =
+ llvm::dyn_cast_or_null<ClangASTContext>(type_system);
+ if (!clang_type_system)
+ return nullptr;
+ PDBASTParser *pdb = clang_type_system->GetPDBParser();
+ if (!pdb)
+ return nullptr;
+
+ auto pdb_type = m_session_up->getSymbolById(type_uid);
+ if (pdb_type == nullptr)
+ return nullptr;
+
+ lldb::TypeSP result = pdb->CreateLLDBTypeFromPDBType(*pdb_type);
+ if (result) {
+ m_types.insert(std::make_pair(type_uid, result));
+ auto type_list = GetTypeList();
+ if (type_list)
+ type_list->Insert(result);
+ }
+ return result.get();
+}
+
+llvm::Optional<SymbolFile::ArrayInfo> SymbolFilePDB::GetDynamicArrayInfoForUID(
+ lldb::user_id_t type_uid, const lldb_private::ExecutionContext *exe_ctx) {
+ return llvm::None;
+}
+
+bool SymbolFilePDB::CompleteType(lldb_private::CompilerType &compiler_type) {
+ std::lock_guard<std::recursive_mutex> guard(
+ GetObjectFile()->GetModule()->GetMutex());
+
+ ClangASTContext *clang_ast_ctx = llvm::dyn_cast_or_null<ClangASTContext>(
+ GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus));
+ if (!clang_ast_ctx)
+ return false;
+
+ PDBASTParser *pdb = clang_ast_ctx->GetPDBParser();
+ if (!pdb)
+ return false;
+
+ return pdb->CompleteTypeFromPDB(compiler_type);
+}
+
+lldb_private::CompilerDecl SymbolFilePDB::GetDeclForUID(lldb::user_id_t uid) {
+ ClangASTContext *clang_ast_ctx = llvm::dyn_cast_or_null<ClangASTContext>(
+ GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus));
+ if (!clang_ast_ctx)
+ return CompilerDecl();
+
+ PDBASTParser *pdb = clang_ast_ctx->GetPDBParser();
+ if (!pdb)
+ return CompilerDecl();
+
+ auto symbol = m_session_up->getSymbolById(uid);
+ if (!symbol)
+ return CompilerDecl();
+
+ auto decl = pdb->GetDeclForSymbol(*symbol);
+ if (!decl)
+ return CompilerDecl();
+
+ return CompilerDecl(clang_ast_ctx, decl);
+}
+
+lldb_private::CompilerDeclContext
+SymbolFilePDB::GetDeclContextForUID(lldb::user_id_t uid) {
+ ClangASTContext *clang_ast_ctx = llvm::dyn_cast_or_null<ClangASTContext>(
+ GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus));
+ if (!clang_ast_ctx)
+ return CompilerDeclContext();
+
+ PDBASTParser *pdb = clang_ast_ctx->GetPDBParser();
+ if (!pdb)
+ return CompilerDeclContext();
+
+ auto symbol = m_session_up->getSymbolById(uid);
+ if (!symbol)
+ return CompilerDeclContext();
+
+ auto decl_context = pdb->GetDeclContextForSymbol(*symbol);
+ if (!decl_context)
+ return GetDeclContextContainingUID(uid);
+
+ return CompilerDeclContext(clang_ast_ctx, decl_context);
+}
+
+lldb_private::CompilerDeclContext
+SymbolFilePDB::GetDeclContextContainingUID(lldb::user_id_t uid) {
+ ClangASTContext *clang_ast_ctx = llvm::dyn_cast_or_null<ClangASTContext>(
+ GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus));
+ if (!clang_ast_ctx)
+ return CompilerDeclContext();
+
+ PDBASTParser *pdb = clang_ast_ctx->GetPDBParser();
+ if (!pdb)
+ return CompilerDeclContext();
+
+ auto symbol = m_session_up->getSymbolById(uid);
+ if (!symbol)
+ return CompilerDeclContext();
+
+ auto decl_context = pdb->GetDeclContextContainingSymbol(*symbol);
+ assert(decl_context);
+
+ return CompilerDeclContext(clang_ast_ctx, decl_context);
+}
+
+void SymbolFilePDB::ParseDeclsForContext(
+ lldb_private::CompilerDeclContext decl_ctx) {
+ ClangASTContext *clang_ast_ctx = llvm::dyn_cast_or_null<ClangASTContext>(
+ GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus));
+ if (!clang_ast_ctx)
+ return;
+
+ PDBASTParser *pdb = clang_ast_ctx->GetPDBParser();
+ if (!pdb)
+ return;
+
+ pdb->ParseDeclsForDeclContext(
+ static_cast<clang::DeclContext *>(decl_ctx.GetOpaqueDeclContext()));
+}
+
+uint32_t
+SymbolFilePDB::ResolveSymbolContext(const lldb_private::Address &so_addr,
+ SymbolContextItem resolve_scope,
+ lldb_private::SymbolContext &sc) {
+ uint32_t resolved_flags = 0;
+ if (resolve_scope & eSymbolContextCompUnit ||
+ resolve_scope & eSymbolContextVariable ||
+ resolve_scope & eSymbolContextFunction ||
+ resolve_scope & eSymbolContextBlock ||
+ resolve_scope & eSymbolContextLineEntry) {
+ auto cu_sp = GetCompileUnitContainsAddress(so_addr);
+ if (!cu_sp) {
+ if (resolved_flags | eSymbolContextVariable) {
+ // TODO: Resolve variables
+ }
+ return 0;
+ }
+ sc.comp_unit = cu_sp.get();
+ resolved_flags |= eSymbolContextCompUnit;
+ lldbassert(sc.module_sp == cu_sp->GetModule());
+ }
+
+ if (resolve_scope & eSymbolContextFunction ||
+ resolve_scope & eSymbolContextBlock) {
+ addr_t file_vm_addr = so_addr.GetFileAddress();
+ auto symbol_up =
+ m_session_up->findSymbolByAddress(file_vm_addr, PDB_SymType::Function);
+ if (symbol_up) {
+ auto *pdb_func = llvm::dyn_cast<PDBSymbolFunc>(symbol_up.get());
+ assert(pdb_func);
+ auto func_uid = pdb_func->getSymIndexId();
+ sc.function = sc.comp_unit->FindFunctionByUID(func_uid).get();
+ if (sc.function == nullptr)
+ sc.function =
+ ParseCompileUnitFunctionForPDBFunc(*pdb_func, *sc.comp_unit);
+ if (sc.function) {
+ resolved_flags |= eSymbolContextFunction;
+ if (resolve_scope & eSymbolContextBlock) {
+ auto block_symbol = m_session_up->findSymbolByAddress(
+ file_vm_addr, PDB_SymType::Block);
+ auto block_id = block_symbol ? block_symbol->getSymIndexId()
+ : sc.function->GetID();
+ sc.block = sc.function->GetBlock(true).FindBlockByID(block_id);
+ if (sc.block)
+ resolved_flags |= eSymbolContextBlock;
+ }
+ }
+ }
+ }
+
+ if (resolve_scope & eSymbolContextLineEntry) {
+ if (auto *line_table = sc.comp_unit->GetLineTable()) {
+ Address addr(so_addr);
+ if (line_table->FindLineEntryByAddress(addr, sc.line_entry))
+ resolved_flags |= eSymbolContextLineEntry;
+ }
+ }
+
+ return resolved_flags;
+}
+
+uint32_t SymbolFilePDB::ResolveSymbolContext(
+ const lldb_private::FileSpec &file_spec, uint32_t line, bool check_inlines,
+ SymbolContextItem resolve_scope, lldb_private::SymbolContextList &sc_list) {
+ const size_t old_size = sc_list.GetSize();
+ if (resolve_scope & lldb::eSymbolContextCompUnit) {
+ // Locate all compilation units with line numbers referencing the specified
+ // file. For example, if `file_spec` is <vector>, then this should return
+ // all source files and header files that reference <vector>, either
+ // directly or indirectly.
+ auto compilands = m_session_up->findCompilandsForSourceFile(
+ file_spec.GetPath(), PDB_NameSearchFlags::NS_CaseInsensitive);
+
+ if (!compilands)
+ return 0;
+
+ // For each one, either find its previously parsed data or parse it afresh
+ // and add it to the symbol context list.
+ while (auto compiland = compilands->getNext()) {
+ // If we're not checking inlines, then don't add line information for
+ // this file unless the FileSpec matches. For inline functions, we don't
+ // have to match the FileSpec since they could be defined in headers
+ // other than file specified in FileSpec.
+ if (!check_inlines) {
+ std::string source_file = compiland->getSourceFileFullPath();
+ if (source_file.empty())
+ continue;
+ FileSpec this_spec(source_file, FileSpec::Style::windows);
+ bool need_full_match = !file_spec.GetDirectory().IsEmpty();
+ if (FileSpec::Compare(file_spec, this_spec, need_full_match) != 0)
+ continue;
+ }
+
+ SymbolContext sc;
+ auto cu = ParseCompileUnitForUID(compiland->getSymIndexId());
+ if (!cu)
+ continue;
+ sc.comp_unit = cu.get();
+ sc.module_sp = cu->GetModule();
+
+ // If we were asked to resolve line entries, add all entries to the line
+ // table that match the requested line (or all lines if `line` == 0).
+ if (resolve_scope & (eSymbolContextFunction | eSymbolContextBlock |
+ eSymbolContextLineEntry)) {
+ bool has_line_table = ParseCompileUnitLineTable(*sc.comp_unit, line);
+
+ if ((resolve_scope & eSymbolContextLineEntry) && !has_line_table) {
+ // The query asks for line entries, but we can't get them for the
+ // compile unit. This is not normal for `line` = 0. So just assert
+ // it.
+ assert(line && "Couldn't get all line entries!\n");
+
+ // Current compiland does not have the requested line. Search next.
+ continue;
+ }
+
+ if (resolve_scope & (eSymbolContextFunction | eSymbolContextBlock)) {
+ if (!has_line_table)
+ continue;
+
+ auto *line_table = sc.comp_unit->GetLineTable();
+ lldbassert(line_table);
+
+ uint32_t num_line_entries = line_table->GetSize();
+ // Skip the terminal line entry.
+ --num_line_entries;
+
+ // If `line `!= 0, see if we can resolve function for each line entry
+ // in the line table.
+ for (uint32_t line_idx = 0; line && line_idx < num_line_entries;
+ ++line_idx) {
+ if (!line_table->GetLineEntryAtIndex(line_idx, sc.line_entry))
+ continue;
+
+ auto file_vm_addr =
+ sc.line_entry.range.GetBaseAddress().GetFileAddress();
+ if (file_vm_addr == LLDB_INVALID_ADDRESS || file_vm_addr == 0)
+ continue;
+
+ auto symbol_up = m_session_up->findSymbolByAddress(
+ file_vm_addr, PDB_SymType::Function);
+ if (symbol_up) {
+ auto func_uid = symbol_up->getSymIndexId();
+ sc.function = sc.comp_unit->FindFunctionByUID(func_uid).get();
+ if (sc.function == nullptr) {
+ auto pdb_func = llvm::dyn_cast<PDBSymbolFunc>(symbol_up.get());
+ assert(pdb_func);
+ sc.function = ParseCompileUnitFunctionForPDBFunc(*pdb_func,
+ *sc.comp_unit);
+ }
+ if (sc.function && (resolve_scope & eSymbolContextBlock)) {
+ Block &block = sc.function->GetBlock(true);
+ sc.block = block.FindBlockByID(sc.function->GetID());
+ }
+ }
+ sc_list.Append(sc);
+ }
+ } else if (has_line_table) {
+ // We can parse line table for the compile unit. But no query to
+ // resolve function or block. We append `sc` to the list anyway.
+ sc_list.Append(sc);
+ }
+ } else {
+ // No query for line entry, function or block. But we have a valid
+ // compile unit, append `sc` to the list.
+ sc_list.Append(sc);
+ }
+ }
+ }
+ return sc_list.GetSize() - old_size;
+}
+
+std::string SymbolFilePDB::GetMangledForPDBData(const PDBSymbolData &pdb_data) {
+ // Cache public names at first
+ if (m_public_names.empty())
+ if (auto result_up =
+ m_global_scope_up->findAllChildren(PDB_SymType::PublicSymbol))
+ while (auto symbol_up = result_up->getNext())
+ if (auto addr = symbol_up->getRawSymbol().getVirtualAddress())
+ m_public_names[addr] = symbol_up->getRawSymbol().getName();
+
+ // Look up the name in the cache
+ return m_public_names.lookup(pdb_data.getVirtualAddress());
+}
+
+VariableSP SymbolFilePDB::ParseVariableForPDBData(
+ const lldb_private::SymbolContext &sc,
+ const llvm::pdb::PDBSymbolData &pdb_data) {
+ VariableSP var_sp;
+ uint32_t var_uid = pdb_data.getSymIndexId();
+ auto result = m_variables.find(var_uid);
+ if (result != m_variables.end())
+ return result->second;
+
+ ValueType scope = eValueTypeInvalid;
+ bool is_static_member = false;
+ bool is_external = false;
+ bool is_artificial = false;
+
+ switch (pdb_data.getDataKind()) {
+ case PDB_DataKind::Global:
+ scope = eValueTypeVariableGlobal;
+ is_external = true;
+ break;
+ case PDB_DataKind::Local:
+ scope = eValueTypeVariableLocal;
+ break;
+ case PDB_DataKind::FileStatic:
+ scope = eValueTypeVariableStatic;
+ break;
+ case PDB_DataKind::StaticMember:
+ is_static_member = true;
+ scope = eValueTypeVariableStatic;
+ break;
+ case PDB_DataKind::Member:
+ scope = eValueTypeVariableStatic;
+ break;
+ case PDB_DataKind::Param:
+ scope = eValueTypeVariableArgument;
+ break;
+ case PDB_DataKind::Constant:
+ scope = eValueTypeConstResult;
+ break;
+ default:
+ break;
+ }
+
+ switch (pdb_data.getLocationType()) {
+ case PDB_LocType::TLS:
+ scope = eValueTypeVariableThreadLocal;
+ break;
+ case PDB_LocType::RegRel: {
+ // It is a `this` pointer.
+ if (pdb_data.getDataKind() == PDB_DataKind::ObjectPtr) {
+ scope = eValueTypeVariableArgument;
+ is_artificial = true;
+ }
+ } break;
+ default:
+ break;
+ }
+
+ Declaration decl;
+ if (!is_artificial && !pdb_data.isCompilerGenerated()) {
+ if (auto lines = pdb_data.getLineNumbers()) {
+ if (auto first_line = lines->getNext()) {
+ uint32_t src_file_id = first_line->getSourceFileId();
+ auto src_file = m_session_up->getSourceFileById(src_file_id);
+ if (src_file) {
+ FileSpec spec(src_file->getFileName());
+ decl.SetFile(spec);
+ decl.SetColumn(first_line->getColumnNumber());
+ decl.SetLine(first_line->getLineNumber());
+ }
+ }
+ }
+ }
+
+ Variable::RangeList ranges;
+ SymbolContextScope *context_scope = sc.comp_unit;
+ if (scope == eValueTypeVariableLocal || scope == eValueTypeVariableArgument) {
+ if (sc.function) {
+ Block &function_block = sc.function->GetBlock(true);
+ Block *block =
+ function_block.FindBlockByID(pdb_data.getLexicalParentId());
+ if (!block)
+ block = &function_block;
+
+ context_scope = block;
+
+ for (size_t i = 0, num_ranges = block->GetNumRanges(); i < num_ranges;
+ ++i) {
+ AddressRange range;
+ if (!block->GetRangeAtIndex(i, range))
+ continue;
+
+ ranges.Append(range.GetBaseAddress().GetFileAddress(),
+ range.GetByteSize());
+ }
+ }
+ }
+
+ SymbolFileTypeSP type_sp =
+ std::make_shared<SymbolFileType>(*this, pdb_data.getTypeId());
+
+ auto var_name = pdb_data.getName();
+ auto mangled = GetMangledForPDBData(pdb_data);
+ auto mangled_cstr = mangled.empty() ? nullptr : mangled.c_str();
+
+ bool is_constant;
+ DWARFExpression location = ConvertPDBLocationToDWARFExpression(
+ GetObjectFile()->GetModule(), pdb_data, ranges, is_constant);
+
+ var_sp = std::make_shared<Variable>(
+ var_uid, var_name.c_str(), mangled_cstr, type_sp, scope, context_scope,
+ ranges, &decl, location, is_external, is_artificial, is_static_member);
+ var_sp->SetLocationIsConstantValueData(is_constant);
+
+ m_variables.insert(std::make_pair(var_uid, var_sp));
+ return var_sp;
+}
+
+size_t
+SymbolFilePDB::ParseVariables(const lldb_private::SymbolContext &sc,
+ const llvm::pdb::PDBSymbol &pdb_symbol,
+ lldb_private::VariableList *variable_list) {
+ size_t num_added = 0;
+
+ if (auto pdb_data = llvm::dyn_cast<PDBSymbolData>(&pdb_symbol)) {
+ VariableListSP local_variable_list_sp;
+
+ auto result = m_variables.find(pdb_data->getSymIndexId());
+ if (result != m_variables.end()) {
+ if (variable_list)
+ variable_list->AddVariableIfUnique(result->second);
+ } else {
+ // Prepare right VariableList for this variable.
+ if (auto lexical_parent = pdb_data->getLexicalParent()) {
+ switch (lexical_parent->getSymTag()) {
+ case PDB_SymType::Exe:
+ assert(sc.comp_unit);
+ LLVM_FALLTHROUGH;
+ case PDB_SymType::Compiland: {
+ if (sc.comp_unit) {
+ local_variable_list_sp = sc.comp_unit->GetVariableList(false);
+ if (!local_variable_list_sp) {
+ local_variable_list_sp = std::make_shared<VariableList>();
+ sc.comp_unit->SetVariableList(local_variable_list_sp);
+ }
+ }
+ } break;
+ case PDB_SymType::Block:
+ case PDB_SymType::Function: {
+ if (sc.function) {
+ Block *block = sc.function->GetBlock(true).FindBlockByID(
+ lexical_parent->getSymIndexId());
+ if (block) {
+ local_variable_list_sp = block->GetBlockVariableList(false);
+ if (!local_variable_list_sp) {
+ local_variable_list_sp = std::make_shared<VariableList>();
+ block->SetVariableList(local_variable_list_sp);
+ }
+ }
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+
+ if (local_variable_list_sp) {
+ if (auto var_sp = ParseVariableForPDBData(sc, *pdb_data)) {
+ local_variable_list_sp->AddVariableIfUnique(var_sp);
+ if (variable_list)
+ variable_list->AddVariableIfUnique(var_sp);
+ ++num_added;
+ PDBASTParser *ast = GetPDBAstParser();
+ if (ast)
+ ast->GetDeclForSymbol(*pdb_data);
+ }
+ }
+ }
+ }
+
+ if (auto results = pdb_symbol.findAllChildren()) {
+ while (auto result = results->getNext())
+ num_added += ParseVariables(sc, *result, variable_list);
+ }
+
+ return num_added;
+}
+
+uint32_t SymbolFilePDB::FindGlobalVariables(
+ lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches, lldb_private::VariableList &variables) {
+ if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx))
+ return 0;
+ if (name.IsEmpty())
+ return 0;
+
+ auto results = m_global_scope_up->findAllChildren<PDBSymbolData>();
+ if (!results)
+ return 0;
+
+ uint32_t matches = 0;
+ size_t old_size = variables.GetSize();
+ while (auto result = results->getNext()) {
+ auto pdb_data = llvm::dyn_cast<PDBSymbolData>(result.get());
+ if (max_matches > 0 && matches >= max_matches)
+ break;
+
+ SymbolContext sc;
+ sc.module_sp = m_obj_file->GetModule();
+ lldbassert(sc.module_sp.get());
+
+ if (!name.GetStringRef().equals(
+ MSVCUndecoratedNameParser::DropScope(pdb_data->getName())))
+ continue;
+
+ sc.comp_unit = ParseCompileUnitForUID(GetCompilandId(*pdb_data)).get();
+ // FIXME: We are not able to determine the compile unit.
+ if (sc.comp_unit == nullptr)
+ continue;
+
+ if (parent_decl_ctx && GetDeclContextContainingUID(
+ result->getSymIndexId()) != *parent_decl_ctx)
+ continue;
+
+ ParseVariables(sc, *pdb_data, &variables);
+ matches = variables.GetSize() - old_size;
+ }
+
+ return matches;
+}
+
+uint32_t
+SymbolFilePDB::FindGlobalVariables(const lldb_private::RegularExpression &regex,
+ uint32_t max_matches,
+ lldb_private::VariableList &variables) {
+ if (!regex.IsValid())
+ return 0;
+ auto results = m_global_scope_up->findAllChildren<PDBSymbolData>();
+ if (!results)
+ return 0;
+
+ uint32_t matches = 0;
+ size_t old_size = variables.GetSize();
+ while (auto pdb_data = results->getNext()) {
+ if (max_matches > 0 && matches >= max_matches)
+ break;
+
+ auto var_name = pdb_data->getName();
+ if (var_name.empty())
+ continue;
+ if (!regex.Execute(var_name))
+ continue;
+ SymbolContext sc;
+ sc.module_sp = m_obj_file->GetModule();
+ lldbassert(sc.module_sp.get());
+
+ sc.comp_unit = ParseCompileUnitForUID(GetCompilandId(*pdb_data)).get();
+ // FIXME: We are not able to determine the compile unit.
+ if (sc.comp_unit == nullptr)
+ continue;
+
+ ParseVariables(sc, *pdb_data, &variables);
+ matches = variables.GetSize() - old_size;
+ }
+
+ return matches;
+}
+
+bool SymbolFilePDB::ResolveFunction(const llvm::pdb::PDBSymbolFunc &pdb_func,
+ bool include_inlines,
+ lldb_private::SymbolContextList &sc_list) {
+ lldb_private::SymbolContext sc;
+ sc.comp_unit = ParseCompileUnitForUID(pdb_func.getCompilandId()).get();
+ if (!sc.comp_unit)
+ return false;
+ sc.module_sp = sc.comp_unit->GetModule();
+ sc.function = ParseCompileUnitFunctionForPDBFunc(pdb_func, *sc.comp_unit);
+ if (!sc.function)
+ return false;
+
+ sc_list.Append(sc);
+ return true;
+}
+
+bool SymbolFilePDB::ResolveFunction(uint32_t uid, bool include_inlines,
+ lldb_private::SymbolContextList &sc_list) {
+ auto pdb_func_up = m_session_up->getConcreteSymbolById<PDBSymbolFunc>(uid);
+ if (!pdb_func_up && !(include_inlines && pdb_func_up->hasInlineAttribute()))
+ return false;
+ return ResolveFunction(*pdb_func_up, include_inlines, sc_list);
+}
+
+void SymbolFilePDB::CacheFunctionNames() {
+ if (!m_func_full_names.IsEmpty())
+ return;
+
+ std::map<uint64_t, uint32_t> addr_ids;
+
+ if (auto results_up = m_global_scope_up->findAllChildren<PDBSymbolFunc>()) {
+ while (auto pdb_func_up = results_up->getNext()) {
+ if (pdb_func_up->isCompilerGenerated())
+ continue;
+
+ auto name = pdb_func_up->getName();
+ auto demangled_name = pdb_func_up->getUndecoratedName();
+ if (name.empty() && demangled_name.empty())
+ continue;
+
+ auto uid = pdb_func_up->getSymIndexId();
+ if (!demangled_name.empty() && pdb_func_up->getVirtualAddress())
+ addr_ids.insert(std::make_pair(pdb_func_up->getVirtualAddress(), uid));
+
+ if (auto parent = pdb_func_up->getClassParent()) {
+
+ // PDB have symbols for class/struct methods or static methods in Enum
+ // Class. We won't bother to check if the parent is UDT or Enum here.
+ m_func_method_names.Append(ConstString(name), uid);
+
+ // To search a method name, like NS::Class:MemberFunc, LLDB searches
+ // its base name, i.e. MemberFunc by default. Since PDBSymbolFunc does
+ // not have inforamtion of this, we extract base names and cache them
+ // by our own effort.
+ llvm::StringRef basename = MSVCUndecoratedNameParser::DropScope(name);
+ if (!basename.empty())
+ m_func_base_names.Append(ConstString(basename), uid);
+ else {
+ m_func_base_names.Append(ConstString(name), uid);
+ }
+
+ if (!demangled_name.empty())
+ m_func_full_names.Append(ConstString(demangled_name), uid);
+
+ } else {
+ // Handle not-method symbols.
+
+ // The function name might contain namespace, or its lexical scope.
+ llvm::StringRef basename = MSVCUndecoratedNameParser::DropScope(name);
+ if (!basename.empty())
+ m_func_base_names.Append(ConstString(basename), uid);
+ else
+ m_func_base_names.Append(ConstString(name), uid);
+
+ if (name == "main") {
+ m_func_full_names.Append(ConstString(name), uid);
+
+ if (!demangled_name.empty() && name != demangled_name) {
+ m_func_full_names.Append(ConstString(demangled_name), uid);
+ m_func_base_names.Append(ConstString(demangled_name), uid);
+ }
+ } else if (!demangled_name.empty()) {
+ m_func_full_names.Append(ConstString(demangled_name), uid);
+ } else {
+ m_func_full_names.Append(ConstString(name), uid);
+ }
+ }
+ }
+ }
+
+ if (auto results_up =
+ m_global_scope_up->findAllChildren<PDBSymbolPublicSymbol>()) {
+ while (auto pub_sym_up = results_up->getNext()) {
+ if (!pub_sym_up->isFunction())
+ continue;
+ auto name = pub_sym_up->getName();
+ if (name.empty())
+ continue;
+
+ if (CPlusPlusLanguage::IsCPPMangledName(name.c_str())) {
+ auto vm_addr = pub_sym_up->getVirtualAddress();
+
+ // PDB public symbol has mangled name for its associated function.
+ if (vm_addr && addr_ids.find(vm_addr) != addr_ids.end()) {
+ // Cache mangled name.
+ m_func_full_names.Append(ConstString(name), addr_ids[vm_addr]);
+ }
+ }
+ }
+ }
+ // Sort them before value searching is working properly
+ m_func_full_names.Sort();
+ m_func_full_names.SizeToFit();
+ m_func_method_names.Sort();
+ m_func_method_names.SizeToFit();
+ m_func_base_names.Sort();
+ m_func_base_names.SizeToFit();
+}
+
+uint32_t SymbolFilePDB::FindFunctions(
+ lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ FunctionNameType name_type_mask, bool include_inlines, bool append,
+ lldb_private::SymbolContextList &sc_list) {
+ if (!append)
+ sc_list.Clear();
+ lldbassert((name_type_mask & eFunctionNameTypeAuto) == 0);
+
+ if (name_type_mask == eFunctionNameTypeNone)
+ return 0;
+ if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx))
+ return 0;
+ if (name.IsEmpty())
+ return 0;
+
+ auto old_size = sc_list.GetSize();
+ if (name_type_mask & eFunctionNameTypeFull ||
+ name_type_mask & eFunctionNameTypeBase ||
+ name_type_mask & eFunctionNameTypeMethod) {
+ CacheFunctionNames();
+
+ std::set<uint32_t> resolved_ids;
+ auto ResolveFn = [this, &name, parent_decl_ctx, include_inlines, &sc_list,
+ &resolved_ids](UniqueCStringMap<uint32_t> &Names) {
+ std::vector<uint32_t> ids;
+ if (!Names.GetValues(name, ids))
+ return;
+
+ for (uint32_t id : ids) {
+ if (resolved_ids.find(id) != resolved_ids.end())
+ continue;
+
+ if (parent_decl_ctx &&
+ GetDeclContextContainingUID(id) != *parent_decl_ctx)
+ continue;
+
+ if (ResolveFunction(id, include_inlines, sc_list))
+ resolved_ids.insert(id);
+ }
+ };
+ if (name_type_mask & eFunctionNameTypeFull) {
+ ResolveFn(m_func_full_names);
+ ResolveFn(m_func_base_names);
+ ResolveFn(m_func_method_names);
+ }
+ if (name_type_mask & eFunctionNameTypeBase) {
+ ResolveFn(m_func_base_names);
+ }
+ if (name_type_mask & eFunctionNameTypeMethod) {
+ ResolveFn(m_func_method_names);
+ }
+ }
+ return sc_list.GetSize() - old_size;
+}
+
+uint32_t
+SymbolFilePDB::FindFunctions(const lldb_private::RegularExpression &regex,
+ bool include_inlines, bool append,
+ lldb_private::SymbolContextList &sc_list) {
+ if (!append)
+ sc_list.Clear();
+ if (!regex.IsValid())
+ return 0;
+
+ auto old_size = sc_list.GetSize();
+ CacheFunctionNames();
+
+ std::set<uint32_t> resolved_ids;
+ auto ResolveFn = [&regex, include_inlines, &sc_list, &resolved_ids,
+ this](UniqueCStringMap<uint32_t> &Names) {
+ std::vector<uint32_t> ids;
+ if (Names.GetValues(regex, ids)) {
+ for (auto id : ids) {
+ if (resolved_ids.find(id) == resolved_ids.end())
+ if (ResolveFunction(id, include_inlines, sc_list))
+ resolved_ids.insert(id);
+ }
+ }
+ };
+ ResolveFn(m_func_full_names);
+ ResolveFn(m_func_base_names);
+
+ return sc_list.GetSize() - old_size;
+}
+
+void SymbolFilePDB::GetMangledNamesForFunction(
+ const std::string &scope_qualified_name,
+ std::vector<lldb_private::ConstString> &mangled_names) {}
+
+void SymbolFilePDB::AddSymbols(lldb_private::Symtab &symtab) {
+ std::set<lldb::addr_t> sym_addresses;
+ for (size_t i = 0; i < symtab.GetNumSymbols(); i++)
+ sym_addresses.insert(symtab.SymbolAtIndex(i)->GetFileAddress());
+
+ auto results = m_global_scope_up->findAllChildren<PDBSymbolPublicSymbol>();
+ if (!results)
+ return;
+
+ auto section_list = m_obj_file->GetSectionList();
+ if (!section_list)
+ return;
+
+ while (auto pub_symbol = results->getNext()) {
+ auto section_id = pub_symbol->getAddressSection();
+
+ auto section = section_list->FindSectionByID(section_id);
+ if (!section)
+ continue;
+
+ auto offset = pub_symbol->getAddressOffset();
+
+ auto file_addr = section->GetFileAddress() + offset;
+ if (sym_addresses.find(file_addr) != sym_addresses.end())
+ continue;
+ sym_addresses.insert(file_addr);
+
+ auto size = pub_symbol->getLength();
+ symtab.AddSymbol(
+ Symbol(pub_symbol->getSymIndexId(), // symID
+ pub_symbol->getName().c_str(), // name
+ true, // name_is_mangled
+ pub_symbol->isCode() ? eSymbolTypeCode : eSymbolTypeData, // type
+ true, // external
+ false, // is_debug
+ false, // is_trampoline
+ false, // is_artificial
+ section, // section_sp
+ offset, // value
+ size, // size
+ size != 0, // size_is_valid
+ false, // contains_linker_annotations
+ 0 // flags
+ ));
+ }
+
+ symtab.CalculateSymbolSizes();
+ symtab.Finalize();
+}
+
+uint32_t SymbolFilePDB::FindTypes(
+ lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx, bool append,
+ uint32_t max_matches,
+ llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files,
+ lldb_private::TypeMap &types) {
+ if (!append)
+ types.Clear();
+ if (!name)
+ return 0;
+ if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx))
+ return 0;
+
+ searched_symbol_files.clear();
+ searched_symbol_files.insert(this);
+
+ // There is an assumption 'name' is not a regex
+ FindTypesByName(name.GetStringRef(), parent_decl_ctx, max_matches, types);
+
+ return types.GetSize();
+}
+
+void SymbolFilePDB::DumpClangAST(Stream &s) {
+ auto type_system = GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus);
+ auto clang = llvm::dyn_cast_or_null<ClangASTContext>(type_system);
+ if (!clang)
+ return;
+ clang->Dump(s);
+}
+
+void SymbolFilePDB::FindTypesByRegex(
+ const lldb_private::RegularExpression &regex, uint32_t max_matches,
+ lldb_private::TypeMap &types) {
+ // When searching by regex, we need to go out of our way to limit the search
+ // space as much as possible since this searches EVERYTHING in the PDB,
+ // manually doing regex comparisons. PDB library isn't optimized for regex
+ // searches or searches across multiple symbol types at the same time, so the
+ // best we can do is to search enums, then typedefs, then classes one by one,
+ // and do a regex comparison against each of them.
+ PDB_SymType tags_to_search[] = {PDB_SymType::Enum, PDB_SymType::Typedef,
+ PDB_SymType::UDT};
+ std::unique_ptr<IPDBEnumSymbols> results;
+
+ uint32_t matches = 0;
+
+ for (auto tag : tags_to_search) {
+ results = m_global_scope_up->findAllChildren(tag);
+ if (!results)
+ continue;
+
+ while (auto result = results->getNext()) {
+ if (max_matches > 0 && matches >= max_matches)
+ break;
+
+ std::string type_name;
+ if (auto enum_type = llvm::dyn_cast<PDBSymbolTypeEnum>(result.get()))
+ type_name = enum_type->getName();
+ else if (auto typedef_type =
+ llvm::dyn_cast<PDBSymbolTypeTypedef>(result.get()))
+ type_name = typedef_type->getName();
+ else if (auto class_type = llvm::dyn_cast<PDBSymbolTypeUDT>(result.get()))
+ type_name = class_type->getName();
+ else {
+ // We're looking only for types that have names. Skip symbols, as well
+ // as unnamed types such as arrays, pointers, etc.
+ continue;
+ }
+
+ if (!regex.Execute(type_name))
+ continue;
+
+ // This should cause the type to get cached and stored in the `m_types`
+ // lookup.
+ if (!ResolveTypeUID(result->getSymIndexId()))
+ continue;
+
+ auto iter = m_types.find(result->getSymIndexId());
+ if (iter == m_types.end())
+ continue;
+ types.Insert(iter->second);
+ ++matches;
+ }
+ }
+}
+
+void SymbolFilePDB::FindTypesByName(
+ llvm::StringRef name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches, lldb_private::TypeMap &types) {
+ std::unique_ptr<IPDBEnumSymbols> results;
+ if (name.empty())
+ return;
+ results = m_global_scope_up->findAllChildren(PDB_SymType::None);
+ if (!results)
+ return;
+
+ uint32_t matches = 0;
+
+ while (auto result = results->getNext()) {
+ if (max_matches > 0 && matches >= max_matches)
+ break;
+
+ if (MSVCUndecoratedNameParser::DropScope(
+ result->getRawSymbol().getName()) != name)
+ continue;
+
+ switch (result->getSymTag()) {
+ case PDB_SymType::Enum:
+ case PDB_SymType::UDT:
+ case PDB_SymType::Typedef:
+ break;
+ default:
+ // We're looking only for types that have names. Skip symbols, as well
+ // as unnamed types such as arrays, pointers, etc.
+ continue;
+ }
+
+ // This should cause the type to get cached and stored in the `m_types`
+ // lookup.
+ if (!ResolveTypeUID(result->getSymIndexId()))
+ continue;
+
+ if (parent_decl_ctx && GetDeclContextContainingUID(
+ result->getSymIndexId()) != *parent_decl_ctx)
+ continue;
+
+ auto iter = m_types.find(result->getSymIndexId());
+ if (iter == m_types.end())
+ continue;
+ types.Insert(iter->second);
+ ++matches;
+ }
+}
+
+size_t SymbolFilePDB::FindTypes(
+ const std::vector<lldb_private::CompilerContext> &contexts, bool append,
+ lldb_private::TypeMap &types) {
+ return 0;
+}
+
+lldb_private::TypeList *SymbolFilePDB::GetTypeList() {
+ return m_obj_file->GetModule()->GetTypeList();
+}
+
+void SymbolFilePDB::GetTypesForPDBSymbol(const llvm::pdb::PDBSymbol &pdb_symbol,
+ uint32_t type_mask,
+ TypeCollection &type_collection) {
+ bool can_parse = false;
+ switch (pdb_symbol.getSymTag()) {
+ case PDB_SymType::ArrayType:
+ can_parse = ((type_mask & eTypeClassArray) != 0);
+ break;
+ case PDB_SymType::BuiltinType:
+ can_parse = ((type_mask & eTypeClassBuiltin) != 0);
+ break;
+ case PDB_SymType::Enum:
+ can_parse = ((type_mask & eTypeClassEnumeration) != 0);
+ break;
+ case PDB_SymType::Function:
+ case PDB_SymType::FunctionSig:
+ can_parse = ((type_mask & eTypeClassFunction) != 0);
+ break;
+ case PDB_SymType::PointerType:
+ can_parse = ((type_mask & (eTypeClassPointer | eTypeClassBlockPointer |
+ eTypeClassMemberPointer)) != 0);
+ break;
+ case PDB_SymType::Typedef:
+ can_parse = ((type_mask & eTypeClassTypedef) != 0);
+ break;
+ case PDB_SymType::UDT: {
+ auto *udt = llvm::dyn_cast<PDBSymbolTypeUDT>(&pdb_symbol);
+ assert(udt);
+ can_parse = (udt->getUdtKind() != PDB_UdtType::Interface &&
+ ((type_mask & (eTypeClassClass | eTypeClassStruct |
+ eTypeClassUnion)) != 0));
+ } break;
+ default:
+ break;
+ }
+
+ if (can_parse) {
+ if (auto *type = ResolveTypeUID(pdb_symbol.getSymIndexId())) {
+ auto result =
+ std::find(type_collection.begin(), type_collection.end(), type);
+ if (result == type_collection.end())
+ type_collection.push_back(type);
+ }
+ }
+
+ auto results_up = pdb_symbol.findAllChildren();
+ while (auto symbol_up = results_up->getNext())
+ GetTypesForPDBSymbol(*symbol_up, type_mask, type_collection);
+}
+
+size_t SymbolFilePDB::GetTypes(lldb_private::SymbolContextScope *sc_scope,
+ TypeClass type_mask,
+ lldb_private::TypeList &type_list) {
+ TypeCollection type_collection;
+ uint32_t old_size = type_list.GetSize();
+ CompileUnit *cu =
+ sc_scope ? sc_scope->CalculateSymbolContextCompileUnit() : nullptr;
+ if (cu) {
+ auto compiland_up = GetPDBCompilandByUID(cu->GetID());
+ if (!compiland_up)
+ return 0;
+ GetTypesForPDBSymbol(*compiland_up, type_mask, type_collection);
+ } else {
+ for (uint32_t cu_idx = 0; cu_idx < GetNumCompileUnits(); ++cu_idx) {
+ auto cu_sp = ParseCompileUnitAtIndex(cu_idx);
+ if (cu_sp) {
+ if (auto compiland_up = GetPDBCompilandByUID(cu_sp->GetID()))
+ GetTypesForPDBSymbol(*compiland_up, type_mask, type_collection);
+ }
+ }
+ }
+
+ for (auto type : type_collection) {
+ type->GetForwardCompilerType();
+ type_list.Insert(type->shared_from_this());
+ }
+ return type_list.GetSize() - old_size;
+}
+
+lldb_private::TypeSystem *
+SymbolFilePDB::GetTypeSystemForLanguage(lldb::LanguageType language) {
+ auto type_system =
+ m_obj_file->GetModule()->GetTypeSystemForLanguage(language);
+ if (type_system)
+ type_system->SetSymbolFile(this);
+ return type_system;
+}
+
+PDBASTParser *SymbolFilePDB::GetPDBAstParser() {
+ auto type_system = GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus);
+ auto clang_type_system = llvm::dyn_cast_or_null<ClangASTContext>(type_system);
+ if (!clang_type_system)
+ return nullptr;
+
+ return clang_type_system->GetPDBParser();
+}
+
+
+lldb_private::CompilerDeclContext SymbolFilePDB::FindNamespace(
+ lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx) {
+ auto type_system = GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus);
+ auto clang_type_system = llvm::dyn_cast_or_null<ClangASTContext>(type_system);
+ if (!clang_type_system)
+ return CompilerDeclContext();
+
+ PDBASTParser *pdb = clang_type_system->GetPDBParser();
+ if (!pdb)
+ return CompilerDeclContext();
+
+ clang::DeclContext *decl_context = nullptr;
+ if (parent_decl_ctx)
+ decl_context = static_cast<clang::DeclContext *>(
+ parent_decl_ctx->GetOpaqueDeclContext());
+
+ auto namespace_decl =
+ pdb->FindNamespaceDecl(decl_context, name.GetStringRef());
+ if (!namespace_decl)
+ return CompilerDeclContext();
+
+ return CompilerDeclContext(type_system,
+ static_cast<clang::DeclContext *>(namespace_decl));
+}
+
+lldb_private::ConstString SymbolFilePDB::GetPluginName() {
+ static ConstString g_name("pdb");
+ return g_name;
+}
+
+uint32_t SymbolFilePDB::GetPluginVersion() { return 1; }
+
+IPDBSession &SymbolFilePDB::GetPDBSession() { return *m_session_up; }
+
+const IPDBSession &SymbolFilePDB::GetPDBSession() const {
+ return *m_session_up;
+}
+
+lldb::CompUnitSP SymbolFilePDB::ParseCompileUnitForUID(uint32_t id,
+ uint32_t index) {
+ auto found_cu = m_comp_units.find(id);
+ if (found_cu != m_comp_units.end())
+ return found_cu->second;
+
+ auto compiland_up = GetPDBCompilandByUID(id);
+ if (!compiland_up)
+ return CompUnitSP();
+
+ lldb::LanguageType lang;
+ auto details = compiland_up->findOneChild<PDBSymbolCompilandDetails>();
+ if (!details)
+ lang = lldb::eLanguageTypeC_plus_plus;
+ else
+ lang = TranslateLanguage(details->getLanguage());
+
+ if (lang == lldb::LanguageType::eLanguageTypeUnknown)
+ return CompUnitSP();
+
+ std::string path = compiland_up->getSourceFileFullPath();
+ if (path.empty())
+ return CompUnitSP();
+
+ // Don't support optimized code for now, DebugInfoPDB does not return this
+ // information.
+ LazyBool optimized = eLazyBoolNo;
+ auto cu_sp = std::make_shared<CompileUnit>(m_obj_file->GetModule(), nullptr,
+ path.c_str(), id, lang, optimized);
+
+ if (!cu_sp)
+ return CompUnitSP();
+
+ m_comp_units.insert(std::make_pair(id, cu_sp));
+ if (index == UINT32_MAX)
+ GetCompileUnitIndex(*compiland_up, index);
+ lldbassert(index != UINT32_MAX);
+ m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex(index,
+ cu_sp);
+ return cu_sp;
+}
+
+bool SymbolFilePDB::ParseCompileUnitLineTable(CompileUnit &comp_unit,
+ uint32_t match_line) {
+ auto compiland_up = GetPDBCompilandByUID(comp_unit.GetID());
+ if (!compiland_up)
+ return false;
+
+ // LineEntry needs the *index* of the file into the list of support files
+ // returned by ParseCompileUnitSupportFiles. But the underlying SDK gives us
+ // a globally unique idenfitifier in the namespace of the PDB. So, we have
+ // to do a mapping so that we can hand out indices.
+ llvm::DenseMap<uint32_t, uint32_t> index_map;
+ BuildSupportFileIdToSupportFileIndexMap(*compiland_up, index_map);
+ auto line_table = llvm::make_unique<LineTable>(&comp_unit);
+
+ // Find contributions to `compiland` from all source and header files.
+ std::string path = comp_unit.GetPath();
+ auto files = m_session_up->getSourceFilesForCompiland(*compiland_up);
+ if (!files)
+ return false;
+
+ // For each source and header file, create a LineSequence for contributions
+ // to the compiland from that file, and add the sequence.
+ while (auto file = files->getNext()) {
+ std::unique_ptr<LineSequence> sequence(
+ line_table->CreateLineSequenceContainer());
+ auto lines = m_session_up->findLineNumbers(*compiland_up, *file);
+ if (!lines)
+ continue;
+ int entry_count = lines->getChildCount();
+
+ uint64_t prev_addr;
+ uint32_t prev_length;
+ uint32_t prev_line;
+ uint32_t prev_source_idx;
+
+ for (int i = 0; i < entry_count; ++i) {
+ auto line = lines->getChildAtIndex(i);
+
+ uint64_t lno = line->getLineNumber();
+ uint64_t addr = line->getVirtualAddress();
+ uint32_t length = line->getLength();
+ uint32_t source_id = line->getSourceFileId();
+ uint32_t col = line->getColumnNumber();
+ uint32_t source_idx = index_map[source_id];
+
+ // There was a gap between the current entry and the previous entry if
+ // the addresses don't perfectly line up.
+ bool is_gap = (i > 0) && (prev_addr + prev_length < addr);
+
+ // Before inserting the current entry, insert a terminal entry at the end
+ // of the previous entry's address range if the current entry resulted in
+ // a gap from the previous entry.
+ if (is_gap && ShouldAddLine(match_line, prev_line, prev_length)) {
+ line_table->AppendLineEntryToSequence(
+ sequence.get(), prev_addr + prev_length, prev_line, 0,
+ prev_source_idx, false, false, false, false, true);
+
+ line_table->InsertSequence(sequence.release());
+ sequence.reset(line_table->CreateLineSequenceContainer());
+ }
+
+ if (ShouldAddLine(match_line, lno, length)) {
+ bool is_statement = line->isStatement();
+ bool is_prologue = false;
+ bool is_epilogue = false;
+ auto func =
+ m_session_up->findSymbolByAddress(addr, PDB_SymType::Function);
+ if (func) {
+ auto prologue = func->findOneChild<PDBSymbolFuncDebugStart>();
+ if (prologue)
+ is_prologue = (addr == prologue->getVirtualAddress());
+
+ auto epilogue = func->findOneChild<PDBSymbolFuncDebugEnd>();
+ if (epilogue)
+ is_epilogue = (addr == epilogue->getVirtualAddress());
+ }
+
+ line_table->AppendLineEntryToSequence(sequence.get(), addr, lno, col,
+ source_idx, is_statement, false,
+ is_prologue, is_epilogue, false);
+ }
+
+ prev_addr = addr;
+ prev_length = length;
+ prev_line = lno;
+ prev_source_idx = source_idx;
+ }
+
+ if (entry_count > 0 && ShouldAddLine(match_line, prev_line, prev_length)) {
+ // The end is always a terminal entry, so insert it regardless.
+ line_table->AppendLineEntryToSequence(
+ sequence.get(), prev_addr + prev_length, prev_line, 0,
+ prev_source_idx, false, false, false, false, true);
+ }
+
+ line_table->InsertSequence(sequence.release());
+ }
+
+ if (line_table->GetSize()) {
+ comp_unit.SetLineTable(line_table.release());
+ return true;
+ }
+ return false;
+}
+
+void SymbolFilePDB::BuildSupportFileIdToSupportFileIndexMap(
+ const PDBSymbolCompiland &compiland,
+ llvm::DenseMap<uint32_t, uint32_t> &index_map) const {
+ // This is a hack, but we need to convert the source id into an index into
+ // the support files array. We don't want to do path comparisons to avoid
+ // basename / full path issues that may or may not even be a problem, so we
+ // use the globally unique source file identifiers. Ideally we could use the
+ // global identifiers everywhere, but LineEntry currently assumes indices.
+ auto source_files = m_session_up->getSourceFilesForCompiland(compiland);
+ if (!source_files)
+ return;
+
+ // LLDB uses the DWARF-like file numeration (one based)
+ int index = 1;
+
+ while (auto file = source_files->getNext()) {
+ uint32_t source_id = file->getUniqueId();
+ index_map[source_id] = index++;
+ }
+}
+
+lldb::CompUnitSP SymbolFilePDB::GetCompileUnitContainsAddress(
+ const lldb_private::Address &so_addr) {
+ lldb::addr_t file_vm_addr = so_addr.GetFileAddress();
+ if (file_vm_addr == LLDB_INVALID_ADDRESS || file_vm_addr == 0)
+ return nullptr;
+
+ // If it is a PDB function's vm addr, this is the first sure bet.
+ if (auto lines =
+ m_session_up->findLineNumbersByAddress(file_vm_addr, /*Length=*/1)) {
+ if (auto first_line = lines->getNext())
+ return ParseCompileUnitForUID(first_line->getCompilandId());
+ }
+
+ // Otherwise we resort to section contributions.
+ if (auto sec_contribs = m_session_up->getSectionContribs()) {
+ while (auto section = sec_contribs->getNext()) {
+ auto va = section->getVirtualAddress();
+ if (file_vm_addr >= va && file_vm_addr < va + section->getLength())
+ return ParseCompileUnitForUID(section->getCompilandId());
+ }
+ }
+ return nullptr;
+}
+
+Mangled
+SymbolFilePDB::GetMangledForPDBFunc(const llvm::pdb::PDBSymbolFunc &pdb_func) {
+ Mangled mangled;
+ auto func_name = pdb_func.getName();
+ auto func_undecorated_name = pdb_func.getUndecoratedName();
+ std::string func_decorated_name;
+
+ // Seek from public symbols for non-static function's decorated name if any.
+ // For static functions, they don't have undecorated names and aren't exposed
+ // in Public Symbols either.
+ if (!func_undecorated_name.empty()) {
+ auto result_up = m_global_scope_up->findChildren(
+ PDB_SymType::PublicSymbol, func_undecorated_name,
+ PDB_NameSearchFlags::NS_UndecoratedName);
+ if (result_up) {
+ while (auto symbol_up = result_up->getNext()) {
+ // For a public symbol, it is unique.
+ lldbassert(result_up->getChildCount() == 1);
+ if (auto *pdb_public_sym =
+ llvm::dyn_cast_or_null<PDBSymbolPublicSymbol>(
+ symbol_up.get())) {
+ if (pdb_public_sym->isFunction()) {
+ func_decorated_name = pdb_public_sym->getName();
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (!func_decorated_name.empty()) {
+ mangled.SetMangledName(ConstString(func_decorated_name));
+
+ // For MSVC, format of C funciton's decorated name depends on calling
+ // conventon. Unfortunately none of the format is recognized by current
+ // LLDB. For example, `_purecall` is a __cdecl C function. From PDB,
+ // `__purecall` is retrieved as both its decorated and undecorated name
+ // (using PDBSymbolFunc::getUndecoratedName method). However `__purecall`
+ // string is not treated as mangled in LLDB (neither `?` nor `_Z` prefix).
+ // Mangled::GetDemangledName method will fail internally and caches an
+ // empty string as its undecorated name. So we will face a contradition
+ // here for the same symbol:
+ // non-empty undecorated name from PDB
+ // empty undecorated name from LLDB
+ if (!func_undecorated_name.empty() &&
+ mangled.GetDemangledName(mangled.GuessLanguage()).IsEmpty())
+ mangled.SetDemangledName(ConstString(func_undecorated_name));
+
+ // LLDB uses several flags to control how a C++ decorated name is
+ // undecorated for MSVC. See `safeUndecorateName` in Class Mangled. So the
+ // yielded name could be different from what we retrieve from
+ // PDB source unless we also apply same flags in getting undecorated
+ // name through PDBSymbolFunc::getUndecoratedNameEx method.
+ if (!func_undecorated_name.empty() &&
+ mangled.GetDemangledName(mangled.GuessLanguage()) !=
+ ConstString(func_undecorated_name))
+ mangled.SetDemangledName(ConstString(func_undecorated_name));
+ } else if (!func_undecorated_name.empty()) {
+ mangled.SetDemangledName(ConstString(func_undecorated_name));
+ } else if (!func_name.empty())
+ mangled.SetValue(ConstString(func_name), false);
+
+ return mangled;
+}
+
+bool SymbolFilePDB::DeclContextMatchesThisSymbolFile(
+ const lldb_private::CompilerDeclContext *decl_ctx) {
+ if (decl_ctx == nullptr || !decl_ctx->IsValid())
+ return true;
+
+ TypeSystem *decl_ctx_type_system = decl_ctx->GetTypeSystem();
+ if (!decl_ctx_type_system)
+ return false;
+ TypeSystem *type_system = GetTypeSystemForLanguage(
+ decl_ctx_type_system->GetMinimumLanguage(nullptr));
+ if (decl_ctx_type_system == type_system)
+ return true; // The type systems match, return true
+
+ return false;
+}
+
+uint32_t SymbolFilePDB::GetCompilandId(const llvm::pdb::PDBSymbolData &data) {
+ static const auto pred_upper = [](uint32_t lhs, SecContribInfo rhs) {
+ return lhs < rhs.Offset;
+ };
+
+ // Cache section contributions
+ if (m_sec_contribs.empty()) {
+ if (auto SecContribs = m_session_up->getSectionContribs()) {
+ while (auto SectionContrib = SecContribs->getNext()) {
+ auto comp_id = SectionContrib->getCompilandId();
+ if (!comp_id)
+ continue;
+
+ auto sec = SectionContrib->getAddressSection();
+ auto &sec_cs = m_sec_contribs[sec];
+
+ auto offset = SectionContrib->getAddressOffset();
+ auto it =
+ std::upper_bound(sec_cs.begin(), sec_cs.end(), offset, pred_upper);
+
+ auto size = SectionContrib->getLength();
+ sec_cs.insert(it, {offset, size, comp_id});
+ }
+ }
+ }
+
+ // Check by line number
+ if (auto Lines = data.getLineNumbers()) {
+ if (auto FirstLine = Lines->getNext())
+ return FirstLine->getCompilandId();
+ }
+
+ // Retrieve section + offset
+ uint32_t DataSection = data.getAddressSection();
+ uint32_t DataOffset = data.getAddressOffset();
+ if (DataSection == 0) {
+ if (auto RVA = data.getRelativeVirtualAddress())
+ m_session_up->addressForRVA(RVA, DataSection, DataOffset);
+ }
+
+ if (DataSection) {
+ // Search by section contributions
+ auto &sec_cs = m_sec_contribs[DataSection];
+ auto it =
+ std::upper_bound(sec_cs.begin(), sec_cs.end(), DataOffset, pred_upper);
+ if (it != sec_cs.begin()) {
+ --it;
+ if (DataOffset < it->Offset + it->Size)
+ return it->CompilandId;
+ }
+ } else {
+ // Search in lexical tree
+ auto LexParentId = data.getLexicalParentId();
+ while (auto LexParent = m_session_up->getSymbolById(LexParentId)) {
+ if (LexParent->getSymTag() == PDB_SymType::Exe)
+ break;
+ if (LexParent->getSymTag() == PDB_SymType::Compiland)
+ return LexParentId;
+ LexParentId = LexParent->getRawSymbol().getLexicalParentId();
+ }
+ }
+
+ return 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h
new file mode 100644
index 000000000000..ba3099aaec4d
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h
@@ -0,0 +1,255 @@
+//===-- SymbolFilePDB.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_Plugins_SymbolFile_PDB_SymbolFilePDB_h_
+#define lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_
+
+#include "lldb/Core/UniqueCStringMap.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Utility/UserID.h"
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/DebugInfo/PDB/IPDBSession.h"
+#include "llvm/DebugInfo/PDB/PDB.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolExe.h"
+
+class PDBASTParser;
+
+class SymbolFilePDB : public lldb_private::SymbolFile {
+public:
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static void DebuggerInitialize(lldb_private::Debugger &debugger);
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::SymbolFile *
+ CreateInstance(lldb_private::ObjectFile *obj_file);
+
+ // Constructors and Destructors
+ SymbolFilePDB(lldb_private::ObjectFile *ofile);
+
+ ~SymbolFilePDB() override;
+
+ uint32_t CalculateAbilities() override;
+
+ void InitializeObject() override;
+
+ // Compile Unit function calls
+
+ uint32_t GetNumCompileUnits() override;
+
+ lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override;
+
+ lldb::LanguageType
+ ParseLanguage(lldb_private::CompileUnit &comp_unit) override;
+
+ size_t ParseFunctions(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseLineTable(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseDebugMacros(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseSupportFiles(lldb_private::CompileUnit &comp_unit,
+ lldb_private::FileSpecList &support_files) override;
+
+ size_t ParseTypes(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseImportedModules(
+ const lldb_private::SymbolContext &sc,
+ std::vector<lldb_private::SourceModule> &imported_modules) override;
+
+ size_t ParseBlocksRecursive(lldb_private::Function &func) override;
+
+ size_t
+ ParseVariablesForContext(const lldb_private::SymbolContext &sc) override;
+
+ lldb_private::Type *ResolveTypeUID(lldb::user_id_t type_uid) override;
+ llvm::Optional<ArrayInfo> GetDynamicArrayInfoForUID(
+ lldb::user_id_t type_uid,
+ const lldb_private::ExecutionContext *exe_ctx) override;
+
+ bool CompleteType(lldb_private::CompilerType &compiler_type) override;
+
+ lldb_private::CompilerDecl GetDeclForUID(lldb::user_id_t uid) override;
+
+ lldb_private::CompilerDeclContext
+ GetDeclContextForUID(lldb::user_id_t uid) override;
+
+ lldb_private::CompilerDeclContext
+ GetDeclContextContainingUID(lldb::user_id_t uid) override;
+
+ void
+ ParseDeclsForContext(lldb_private::CompilerDeclContext decl_ctx) override;
+
+ uint32_t ResolveSymbolContext(const lldb_private::Address &so_addr,
+ lldb::SymbolContextItem resolve_scope,
+ lldb_private::SymbolContext &sc) override;
+
+ uint32_t
+ ResolveSymbolContext(const lldb_private::FileSpec &file_spec, uint32_t line,
+ bool check_inlines,
+ lldb::SymbolContextItem resolve_scope,
+ lldb_private::SymbolContextList &sc_list) override;
+
+ uint32_t
+ FindGlobalVariables(lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches,
+ lldb_private::VariableList &variables) override;
+
+ uint32_t FindGlobalVariables(const lldb_private::RegularExpression &regex,
+ uint32_t max_matches,
+ lldb_private::VariableList &variables) override;
+
+ uint32_t
+ FindFunctions(lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ lldb::FunctionNameType name_type_mask, bool include_inlines,
+ bool append, lldb_private::SymbolContextList &sc_list) override;
+
+ uint32_t FindFunctions(const lldb_private::RegularExpression &regex,
+ bool include_inlines, bool append,
+ lldb_private::SymbolContextList &sc_list) override;
+
+ void GetMangledNamesForFunction(
+ const std::string &scope_qualified_name,
+ std::vector<lldb_private::ConstString> &mangled_names) override;
+
+ void AddSymbols(lldb_private::Symtab &symtab) override;
+
+ uint32_t
+ FindTypes(lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ bool append, uint32_t max_matches,
+ llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files,
+ lldb_private::TypeMap &types) override;
+
+ size_t FindTypes(const std::vector<lldb_private::CompilerContext> &context,
+ bool append, lldb_private::TypeMap &types) override;
+
+ void FindTypesByRegex(const lldb_private::RegularExpression &regex,
+ uint32_t max_matches, lldb_private::TypeMap &types);
+
+ lldb_private::TypeList *GetTypeList() override;
+
+ size_t GetTypes(lldb_private::SymbolContextScope *sc_scope,
+ lldb::TypeClass type_mask,
+ lldb_private::TypeList &type_list) override;
+
+ lldb_private::TypeSystem *
+ GetTypeSystemForLanguage(lldb::LanguageType language) override;
+
+ lldb_private::CompilerDeclContext FindNamespace(
+ lldb_private::ConstString name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx) override;
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ llvm::pdb::IPDBSession &GetPDBSession();
+
+ const llvm::pdb::IPDBSession &GetPDBSession() const;
+
+ void DumpClangAST(lldb_private::Stream &s) override;
+
+private:
+ struct SecContribInfo {
+ uint32_t Offset;
+ uint32_t Size;
+ uint32_t CompilandId;
+ };
+ using SecContribsMap = std::map<uint32_t, std::vector<SecContribInfo>>;
+
+ lldb::CompUnitSP ParseCompileUnitForUID(uint32_t id,
+ uint32_t index = UINT32_MAX);
+
+ bool ParseCompileUnitLineTable(lldb_private::CompileUnit &comp_unit,
+ uint32_t match_line);
+
+ void BuildSupportFileIdToSupportFileIndexMap(
+ const llvm::pdb::PDBSymbolCompiland &pdb_compiland,
+ llvm::DenseMap<uint32_t, uint32_t> &index_map) const;
+
+ void FindTypesByName(llvm::StringRef name,
+ const lldb_private::CompilerDeclContext *parent_decl_ctx,
+ uint32_t max_matches, lldb_private::TypeMap &types);
+
+ std::string GetMangledForPDBData(const llvm::pdb::PDBSymbolData &pdb_data);
+
+ lldb::VariableSP
+ ParseVariableForPDBData(const lldb_private::SymbolContext &sc,
+ const llvm::pdb::PDBSymbolData &pdb_data);
+
+ size_t ParseVariables(const lldb_private::SymbolContext &sc,
+ const llvm::pdb::PDBSymbol &pdb_data,
+ lldb_private::VariableList *variable_list = nullptr);
+
+ lldb::CompUnitSP
+ GetCompileUnitContainsAddress(const lldb_private::Address &so_addr);
+
+ typedef std::vector<lldb_private::Type *> TypeCollection;
+
+ void GetTypesForPDBSymbol(const llvm::pdb::PDBSymbol &pdb_symbol,
+ uint32_t type_mask,
+ TypeCollection &type_collection);
+
+ lldb_private::Function *
+ ParseCompileUnitFunctionForPDBFunc(const llvm::pdb::PDBSymbolFunc &pdb_func,
+ lldb_private::CompileUnit &comp_unit);
+
+ void GetCompileUnitIndex(const llvm::pdb::PDBSymbolCompiland &pdb_compiland,
+ uint32_t &index);
+
+ PDBASTParser *GetPDBAstParser();
+
+ std::unique_ptr<llvm::pdb::PDBSymbolCompiland>
+ GetPDBCompilandByUID(uint32_t uid);
+
+ lldb_private::Mangled
+ GetMangledForPDBFunc(const llvm::pdb::PDBSymbolFunc &pdb_func);
+
+ bool ResolveFunction(const llvm::pdb::PDBSymbolFunc &pdb_func,
+ bool include_inlines,
+ lldb_private::SymbolContextList &sc_list);
+
+ bool ResolveFunction(uint32_t uid, bool include_inlines,
+ lldb_private::SymbolContextList &sc_list);
+
+ void CacheFunctionNames();
+
+ bool DeclContextMatchesThisSymbolFile(
+ const lldb_private::CompilerDeclContext *decl_ctx);
+
+ uint32_t GetCompilandId(const llvm::pdb::PDBSymbolData &data);
+
+ llvm::DenseMap<uint32_t, lldb::CompUnitSP> m_comp_units;
+ llvm::DenseMap<uint32_t, lldb::TypeSP> m_types;
+ llvm::DenseMap<uint32_t, lldb::VariableSP> m_variables;
+ llvm::DenseMap<uint64_t, std::string> m_public_names;
+
+ SecContribsMap m_sec_contribs;
+
+ std::vector<lldb::TypeSP> m_builtin_types;
+ std::unique_ptr<llvm::pdb::IPDBSession> m_session_up;
+ std::unique_ptr<llvm::pdb::PDBSymbolExe> m_global_scope_up;
+ uint32_t m_cached_compile_unit_count;
+
+ lldb_private::UniqueCStringMap<uint32_t> m_func_full_names;
+ lldb_private::UniqueCStringMap<uint32_t> m_func_base_names;
+ lldb_private::UniqueCStringMap<uint32_t> m_func_method_names;
+};
+
+#endif // lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp
new file mode 100644
index 000000000000..a1b21e51b0fe
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp
@@ -0,0 +1,267 @@
+//===-- SymbolFileSymtab.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 "SymbolFileSymtab.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/Symtab.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Timer.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+void SymbolFileSymtab::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void SymbolFileSymtab::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString SymbolFileSymtab::GetPluginNameStatic() {
+ static ConstString g_name("symtab");
+ return g_name;
+}
+
+const char *SymbolFileSymtab::GetPluginDescriptionStatic() {
+ return "Reads debug symbols from an object file's symbol table.";
+}
+
+SymbolFile *SymbolFileSymtab::CreateInstance(ObjectFile *obj_file) {
+ return new SymbolFileSymtab(obj_file);
+}
+
+size_t SymbolFileSymtab::GetTypes(SymbolContextScope *sc_scope,
+ TypeClass type_mask,
+ lldb_private::TypeList &type_list) {
+ return 0;
+}
+
+SymbolFileSymtab::SymbolFileSymtab(ObjectFile *obj_file)
+ : SymbolFile(obj_file), m_source_indexes(), m_func_indexes(),
+ m_code_indexes(), m_objc_class_name_to_index() {}
+
+SymbolFileSymtab::~SymbolFileSymtab() {}
+
+uint32_t SymbolFileSymtab::CalculateAbilities() {
+ uint32_t abilities = 0;
+ if (m_obj_file) {
+ const Symtab *symtab = m_obj_file->GetSymtab();
+ if (symtab) {
+ // The snippet of code below will get the indexes the module symbol table
+ // entries that are code, data, or function related (debug info), sort
+ // them by value (address) and dump the sorted symbols.
+ if (symtab->AppendSymbolIndexesWithType(eSymbolTypeSourceFile,
+ m_source_indexes)) {
+ abilities |= CompileUnits;
+ }
+
+ if (symtab->AppendSymbolIndexesWithType(
+ eSymbolTypeCode, Symtab::eDebugYes, Symtab::eVisibilityAny,
+ m_func_indexes)) {
+ symtab->SortSymbolIndexesByValue(m_func_indexes, true);
+ abilities |= Functions;
+ }
+
+ if (symtab->AppendSymbolIndexesWithType(eSymbolTypeCode, Symtab::eDebugNo,
+ Symtab::eVisibilityAny,
+ m_code_indexes)) {
+ symtab->SortSymbolIndexesByValue(m_code_indexes, true);
+ abilities |= Functions;
+ }
+
+ if (symtab->AppendSymbolIndexesWithType(eSymbolTypeData,
+ m_data_indexes)) {
+ symtab->SortSymbolIndexesByValue(m_data_indexes, true);
+ abilities |= GlobalVariables;
+ }
+
+ lldb_private::Symtab::IndexCollection objc_class_indexes;
+ if (symtab->AppendSymbolIndexesWithType(eSymbolTypeObjCClass,
+ objc_class_indexes)) {
+ symtab->AppendSymbolNamesToMap(objc_class_indexes, true, true,
+ m_objc_class_name_to_index);
+ m_objc_class_name_to_index.Sort();
+ }
+ }
+ }
+ return abilities;
+}
+
+uint32_t SymbolFileSymtab::GetNumCompileUnits() {
+ // If we don't have any source file symbols we will just have one compile
+ // unit for the entire object file
+ if (m_source_indexes.empty())
+ return 0;
+
+ // If we have any source file symbols we will logically organize the object
+ // symbols using these.
+ return m_source_indexes.size();
+}
+
+CompUnitSP SymbolFileSymtab::ParseCompileUnitAtIndex(uint32_t idx) {
+ CompUnitSP cu_sp;
+
+ // If we don't have any source file symbols we will just have one compile
+ // unit for the entire object file
+ if (idx < m_source_indexes.size()) {
+ const Symbol *cu_symbol =
+ m_obj_file->GetSymtab()->SymbolAtIndex(m_source_indexes[idx]);
+ if (cu_symbol)
+ cu_sp = std::make_shared<CompileUnit>(m_obj_file->GetModule(), nullptr,
+ cu_symbol->GetName().AsCString(), 0,
+ eLanguageTypeUnknown, eLazyBoolNo);
+ }
+ return cu_sp;
+}
+
+lldb::LanguageType SymbolFileSymtab::ParseLanguage(CompileUnit &comp_unit) {
+ return eLanguageTypeUnknown;
+}
+
+size_t SymbolFileSymtab::ParseFunctions(CompileUnit &comp_unit) {
+ size_t num_added = 0;
+ // We must at least have a valid compile unit
+ const Symtab *symtab = m_obj_file->GetSymtab();
+ const Symbol *curr_symbol = nullptr;
+ const Symbol *next_symbol = nullptr;
+ // const char *prefix = m_obj_file->SymbolPrefix();
+ // if (prefix == NULL)
+ // prefix == "";
+ //
+ // const uint32_t prefix_len = strlen(prefix);
+
+ // If we don't have any source file symbols we will just have one compile
+ // unit for the entire object file
+ if (m_source_indexes.empty()) {
+ // The only time we will have a user ID of zero is when we don't have and
+ // source file symbols and we declare one compile unit for the entire
+ // object file
+ if (!m_func_indexes.empty()) {
+ }
+
+ if (!m_code_indexes.empty()) {
+ // StreamFile s(stdout);
+ // symtab->Dump(&s, m_code_indexes);
+
+ uint32_t idx = 0; // Index into the indexes
+ const uint32_t num_indexes = m_code_indexes.size();
+ for (idx = 0; idx < num_indexes; ++idx) {
+ uint32_t symbol_idx = m_code_indexes[idx];
+ curr_symbol = symtab->SymbolAtIndex(symbol_idx);
+ if (curr_symbol) {
+ // Union of all ranges in the function DIE (if the function is
+ // discontiguous)
+ AddressRange func_range(curr_symbol->GetAddress(), 0);
+ if (func_range.GetBaseAddress().IsSectionOffset()) {
+ uint32_t symbol_size = curr_symbol->GetByteSize();
+ if (symbol_size != 0 && !curr_symbol->GetSizeIsSibling())
+ func_range.SetByteSize(symbol_size);
+ else if (idx + 1 < num_indexes) {
+ next_symbol = symtab->SymbolAtIndex(m_code_indexes[idx + 1]);
+ if (next_symbol) {
+ func_range.SetByteSize(
+ next_symbol->GetAddressRef().GetOffset() -
+ curr_symbol->GetAddressRef().GetOffset());
+ }
+ }
+
+ FunctionSP func_sp(
+ new Function(&comp_unit,
+ symbol_idx, // UserID is the DIE offset
+ LLDB_INVALID_UID, // We don't have any type info
+ // for this function
+ curr_symbol->GetMangled(), // Linker/mangled name
+ nullptr, // no return type for a code symbol...
+ func_range)); // first address range
+
+ if (func_sp.get() != nullptr) {
+ comp_unit.AddFunction(func_sp);
+ ++num_added;
+ }
+ }
+ }
+ }
+ }
+ } else {
+ // We assume we
+ }
+ return num_added;
+}
+
+size_t SymbolFileSymtab::ParseTypes(CompileUnit &comp_unit) { return 0; }
+
+bool SymbolFileSymtab::ParseLineTable(CompileUnit &comp_unit) { return false; }
+
+bool SymbolFileSymtab::ParseDebugMacros(CompileUnit &comp_unit) {
+ return false;
+}
+
+bool SymbolFileSymtab::ParseSupportFiles(CompileUnit &comp_unit,
+ FileSpecList &support_files) {
+ return false;
+}
+
+bool SymbolFileSymtab::ParseImportedModules(
+ const SymbolContext &sc, std::vector<SourceModule> &imported_modules) {
+ return false;
+}
+
+size_t SymbolFileSymtab::ParseBlocksRecursive(Function &func) { return 0; }
+
+size_t SymbolFileSymtab::ParseVariablesForContext(const SymbolContext &sc) {
+ return 0;
+}
+
+Type *SymbolFileSymtab::ResolveTypeUID(lldb::user_id_t type_uid) {
+ return nullptr;
+}
+
+llvm::Optional<SymbolFile::ArrayInfo>
+SymbolFileSymtab::GetDynamicArrayInfoForUID(
+ lldb::user_id_t type_uid, const lldb_private::ExecutionContext *exe_ctx) {
+ return llvm::None;
+}
+
+bool SymbolFileSymtab::CompleteType(lldb_private::CompilerType &compiler_type) {
+ return false;
+}
+
+uint32_t SymbolFileSymtab::ResolveSymbolContext(const Address &so_addr,
+ SymbolContextItem resolve_scope,
+ SymbolContext &sc) {
+ if (m_obj_file->GetSymtab() == nullptr)
+ return 0;
+
+ uint32_t resolved_flags = 0;
+ if (resolve_scope & eSymbolContextSymbol) {
+ sc.symbol = m_obj_file->GetSymtab()->FindSymbolContainingFileAddress(
+ so_addr.GetFileAddress());
+ if (sc.symbol)
+ resolved_flags |= eSymbolContextSymbol;
+ }
+ return resolved_flags;
+}
+
+// PluginInterface protocol
+lldb_private::ConstString SymbolFileSymtab::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t SymbolFileSymtab::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h
new file mode 100644
index 000000000000..bc9a531419ae
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h
@@ -0,0 +1,101 @@
+//===-- SymbolFileSymtab.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 liblldb_SymbolFileSymtab_h_
+#define liblldb_SymbolFileSymtab_h_
+
+#include <map>
+#include <vector>
+
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/Symtab.h"
+
+class SymbolFileSymtab : public lldb_private::SymbolFile {
+public:
+ // Constructors and Destructors
+ SymbolFileSymtab(lldb_private::ObjectFile *obj_file);
+
+ ~SymbolFileSymtab() override;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::SymbolFile *
+ CreateInstance(lldb_private::ObjectFile *obj_file);
+
+ uint32_t CalculateAbilities() override;
+
+ // Compile Unit function calls
+ uint32_t GetNumCompileUnits() override;
+
+ lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override;
+
+ lldb::LanguageType
+ ParseLanguage(lldb_private::CompileUnit &comp_unit) override;
+
+ size_t ParseFunctions(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseLineTable(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseDebugMacros(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseSupportFiles(lldb_private::CompileUnit &comp_unit,
+ lldb_private::FileSpecList &support_files) override;
+
+ size_t ParseTypes(lldb_private::CompileUnit &comp_unit) override;
+
+ bool ParseImportedModules(
+ const lldb_private::SymbolContext &sc,
+ std::vector<lldb_private::SourceModule> &imported_modules) override;
+
+ size_t ParseBlocksRecursive(lldb_private::Function &func) override;
+
+ size_t
+ ParseVariablesForContext(const lldb_private::SymbolContext &sc) override;
+
+ lldb_private::Type *ResolveTypeUID(lldb::user_id_t type_uid) override;
+ llvm::Optional<ArrayInfo> GetDynamicArrayInfoForUID(
+ lldb::user_id_t type_uid,
+ const lldb_private::ExecutionContext *exe_ctx) override;
+
+ bool CompleteType(lldb_private::CompilerType &compiler_type) override;
+
+ uint32_t ResolveSymbolContext(const lldb_private::Address &so_addr,
+ lldb::SymbolContextItem resolve_scope,
+ lldb_private::SymbolContext &sc) override;
+
+ size_t GetTypes(lldb_private::SymbolContextScope *sc_scope,
+ lldb::TypeClass type_mask,
+ lldb_private::TypeList &type_list) override;
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+protected:
+ typedef std::map<lldb_private::ConstString, lldb::TypeSP> TypeMap;
+
+ lldb_private::Symtab::IndexCollection m_source_indexes;
+ lldb_private::Symtab::IndexCollection m_func_indexes;
+ lldb_private::Symtab::IndexCollection m_code_indexes;
+ lldb_private::Symtab::IndexCollection m_data_indexes;
+ lldb_private::Symtab::NameToIndexMap m_objc_class_name_to_index;
+ TypeMap m_objc_class_types;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(SymbolFileSymtab);
+};
+
+#endif // liblldb_SymbolFileSymtab_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.cpp
new file mode 100644
index 000000000000..f279af61a131
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.cpp
@@ -0,0 +1,162 @@
+//===-- SymbolVendorELF.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 "SymbolVendorELF.h"
+
+#include <string.h>
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Symbol/LocateSymbolFile.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/Timer.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// SymbolVendorELF constructor
+SymbolVendorELF::SymbolVendorELF(const lldb::ModuleSP &module_sp)
+ : SymbolVendor(module_sp) {}
+
+// Destructor
+SymbolVendorELF::~SymbolVendorELF() {}
+
+void SymbolVendorELF::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void SymbolVendorELF::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString SymbolVendorELF::GetPluginNameStatic() {
+ static ConstString g_name("ELF");
+ return g_name;
+}
+
+const char *SymbolVendorELF::GetPluginDescriptionStatic() {
+ return "Symbol vendor for ELF that looks for dSYM files that match "
+ "executables.";
+}
+
+// CreateInstance
+//
+// Platforms can register a callback to use when creating symbol vendors to
+// allow for complex debug information file setups, and to also allow for
+// finding separate debug information files.
+SymbolVendor *
+SymbolVendorELF::CreateInstance(const lldb::ModuleSP &module_sp,
+ lldb_private::Stream *feedback_strm) {
+ if (!module_sp)
+ return nullptr;
+
+ ObjectFile *obj_file = module_sp->GetObjectFile();
+ if (!obj_file)
+ return nullptr;
+
+ static ConstString obj_file_elf("elf");
+ ConstString obj_name = obj_file->GetPluginName();
+ if (obj_name != obj_file_elf)
+ return nullptr;
+
+ lldb_private::UUID uuid = obj_file->GetUUID();
+ if (!uuid)
+ return nullptr;
+
+ // Get the .gnu_debuglink file (if specified).
+ FileSpecList file_spec_list = obj_file->GetDebugSymbolFilePaths();
+
+ // If the module specified a filespec, use it first.
+ FileSpec debug_symbol_fspec(module_sp->GetSymbolFileFileSpec());
+ if (debug_symbol_fspec)
+ file_spec_list.Insert(0, debug_symbol_fspec);
+
+ // If we have no debug symbol files, then nothing to do.
+ if (file_spec_list.IsEmpty())
+ return nullptr;
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "SymbolVendorELF::CreateInstance (module = %s)",
+ module_sp->GetFileSpec().GetPath().c_str());
+
+ for (size_t idx = 0; idx < file_spec_list.GetSize(); ++idx) {
+ ModuleSpec module_spec;
+ const FileSpec fspec = file_spec_list.GetFileSpecAtIndex(idx);
+
+ module_spec.GetFileSpec() = obj_file->GetFileSpec();
+ FileSystem::Instance().Resolve(module_spec.GetFileSpec());
+ module_spec.GetSymbolFileSpec() = fspec;
+ module_spec.GetUUID() = uuid;
+ FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths();
+ FileSpec dsym_fspec =
+ Symbols::LocateExecutableSymbolFile(module_spec, search_paths);
+ if (dsym_fspec) {
+ DataBufferSP dsym_file_data_sp;
+ lldb::offset_t dsym_file_data_offset = 0;
+ ObjectFileSP dsym_objfile_sp =
+ ObjectFile::FindPlugin(module_sp, &dsym_fspec, 0,
+ FileSystem::Instance().GetByteSize(dsym_fspec),
+ dsym_file_data_sp, dsym_file_data_offset);
+ if (dsym_objfile_sp) {
+ // This objfile is for debugging purposes. Sadly, ObjectFileELF won't
+ // be able to figure this out consistently as the symbol file may not
+ // have stripped the code sections, etc.
+ dsym_objfile_sp->SetType(ObjectFile::eTypeDebugInfo);
+
+ SymbolVendorELF *symbol_vendor = new SymbolVendorELF(module_sp);
+ if (symbol_vendor) {
+ // Get the module unified section list and add our debug sections to
+ // that.
+ SectionList *module_section_list = module_sp->GetSectionList();
+ SectionList *objfile_section_list = dsym_objfile_sp->GetSectionList();
+
+ static const SectionType g_sections[] = {
+ eSectionTypeDWARFDebugAbbrev, eSectionTypeDWARFDebugAddr,
+ eSectionTypeDWARFDebugAranges, eSectionTypeDWARFDebugCuIndex,
+ eSectionTypeDWARFDebugFrame, eSectionTypeDWARFDebugInfo,
+ eSectionTypeDWARFDebugLine, eSectionTypeDWARFDebugLoc,
+ eSectionTypeDWARFDebugMacInfo, eSectionTypeDWARFDebugPubNames,
+ eSectionTypeDWARFDebugPubTypes, eSectionTypeDWARFDebugRanges,
+ eSectionTypeDWARFDebugStr, eSectionTypeDWARFDebugStrOffsets,
+ eSectionTypeELFSymbolTable, eSectionTypeDWARFGNUDebugAltLink,
+ };
+ for (size_t idx = 0; idx < sizeof(g_sections) / sizeof(g_sections[0]);
+ ++idx) {
+ SectionType section_type = g_sections[idx];
+ SectionSP section_sp(
+ objfile_section_list->FindSectionByType(section_type, true));
+ if (section_sp) {
+ SectionSP module_section_sp(
+ module_section_list->FindSectionByType(section_type, true));
+ if (module_section_sp)
+ module_section_list->ReplaceSection(module_section_sp->GetID(),
+ section_sp);
+ else
+ module_section_list->AddSection(section_sp);
+ }
+ }
+
+ symbol_vendor->AddSymbolFileRepresentation(dsym_objfile_sp);
+ return symbol_vendor;
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+// PluginInterface protocol
+ConstString SymbolVendorELF::GetPluginName() { return GetPluginNameStatic(); }
+
+uint32_t SymbolVendorELF::GetPluginVersion() { return 1; }
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.h b/contrib/llvm-project/lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.h
new file mode 100644
index 000000000000..0cd740da5ce3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.h
@@ -0,0 +1,44 @@
+//===-- SymbolVendorELF.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 liblldb_SymbolVendorELF_h_
+#define liblldb_SymbolVendorELF_h_
+
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/lldb-private.h"
+
+class SymbolVendorELF : public lldb_private::SymbolVendor {
+public:
+ // Constructors and Destructors
+ SymbolVendorELF(const lldb::ModuleSP &module_sp);
+
+ ~SymbolVendorELF() override;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ static lldb_private::SymbolVendor *
+ CreateInstance(const lldb::ModuleSP &module_sp,
+ lldb_private::Stream *feedback_strm);
+
+ // PluginInterface protocol
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(SymbolVendorELF);
+};
+
+#endif // liblldb_SymbolVendorELF_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp b/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp
new file mode 100644
index 000000000000..4aa9fb634b61
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp
@@ -0,0 +1,656 @@
+//===-- UnwindAssemblyInstEmulation.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 "UnwindAssemblyInstEmulation.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/DumpDataExtractor.h"
+#include "lldb/Core/DumpRegisterValue.h"
+#include "lldb/Core/FormatEntity.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// UnwindAssemblyInstEmulation method definitions
+
+bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
+ AddressRange &range, Thread &thread, UnwindPlan &unwind_plan) {
+ std::vector<uint8_t> function_text(range.GetByteSize());
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp) {
+ Status error;
+ const bool prefer_file_cache = true;
+ if (process_sp->GetTarget().ReadMemory(
+ range.GetBaseAddress(), prefer_file_cache, function_text.data(),
+ range.GetByteSize(), error) != range.GetByteSize()) {
+ return false;
+ }
+ }
+ return GetNonCallSiteUnwindPlanFromAssembly(
+ range, function_text.data(), function_text.size(), unwind_plan);
+}
+
+bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
+ AddressRange &range, uint8_t *opcode_data, size_t opcode_size,
+ UnwindPlan &unwind_plan) {
+ if (opcode_data == nullptr || opcode_size == 0)
+ return false;
+
+ if (range.GetByteSize() > 0 && range.GetBaseAddress().IsValid() &&
+ m_inst_emulator_up.get()) {
+
+ // The instruction emulation subclass setup the unwind plan for the first
+ // instruction.
+ m_inst_emulator_up->CreateFunctionEntryUnwind(unwind_plan);
+
+ // CreateFunctionEntryUnwind should have created the first row. If it
+ // doesn't, then we are done.
+ if (unwind_plan.GetRowCount() == 0)
+ return false;
+
+ const bool prefer_file_cache = true;
+ DisassemblerSP disasm_sp(Disassembler::DisassembleBytes(
+ m_arch, nullptr, nullptr, range.GetBaseAddress(), opcode_data,
+ opcode_size, 99999, prefer_file_cache));
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+
+ if (disasm_sp) {
+
+ m_range_ptr = &range;
+ m_unwind_plan_ptr = &unwind_plan;
+
+ const uint32_t addr_byte_size = m_arch.GetAddressByteSize();
+ const bool show_address = true;
+ const bool show_bytes = true;
+ m_inst_emulator_up->GetRegisterInfo(unwind_plan.GetRegisterKind(),
+ unwind_plan.GetInitialCFARegister(),
+ m_cfa_reg_info);
+
+ m_fp_is_cfa = false;
+ m_register_values.clear();
+ m_pushed_regs.clear();
+
+ // Initialize the CFA with a known value. In the 32 bit case it will be
+ // 0x80000000, and in the 64 bit case 0x8000000000000000. We use the
+ // address byte size to be safe for any future address sizes
+ m_initial_sp = (1ull << ((addr_byte_size * 8) - 1));
+ RegisterValue cfa_reg_value;
+ cfa_reg_value.SetUInt(m_initial_sp, m_cfa_reg_info.byte_size);
+ SetRegisterValue(m_cfa_reg_info, cfa_reg_value);
+
+ const InstructionList &inst_list = disasm_sp->GetInstructionList();
+ const size_t num_instructions = inst_list.GetSize();
+
+ if (num_instructions > 0) {
+ Instruction *inst = inst_list.GetInstructionAtIndex(0).get();
+ const lldb::addr_t base_addr = inst->GetAddress().GetFileAddress();
+
+ // Map for storing the unwind plan row and the value of the registers
+ // at a given offset. When we see a forward branch we add a new entry
+ // to this map with the actual unwind plan row and register context for
+ // the target address of the branch as the current data have to be
+ // valid for the target address of the branch too if we are in the same
+ // function.
+ std::map<lldb::addr_t, std::pair<UnwindPlan::RowSP, RegisterValueMap>>
+ saved_unwind_states;
+
+ // Make a copy of the current instruction Row and save it in m_curr_row
+ // so we can add updates as we process the instructions.
+ UnwindPlan::RowSP last_row = unwind_plan.GetLastRow();
+ UnwindPlan::Row *newrow = new UnwindPlan::Row;
+ if (last_row.get())
+ *newrow = *last_row.get();
+ m_curr_row.reset(newrow);
+
+ // Add the initial state to the save list with offset 0.
+ saved_unwind_states.insert({0, {last_row, m_register_values}});
+
+ // cache the pc register number (in whatever register numbering this
+ // UnwindPlan uses) for quick reference during instruction parsing.
+ RegisterInfo pc_reg_info;
+ m_inst_emulator_up->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc_reg_info);
+
+ // cache the return address register number (in whatever register
+ // numbering this UnwindPlan uses) for quick reference during
+ // instruction parsing.
+ RegisterInfo ra_reg_info;
+ m_inst_emulator_up->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, ra_reg_info);
+
+ // The architecture dependent condition code of the last processed
+ // instruction.
+ EmulateInstruction::InstructionCondition last_condition =
+ EmulateInstruction::UnconditionalCondition;
+ lldb::addr_t condition_block_start_offset = 0;
+
+ for (size_t idx = 0; idx < num_instructions; ++idx) {
+ m_curr_row_modified = false;
+ m_forward_branch_offset = 0;
+
+ inst = inst_list.GetInstructionAtIndex(idx).get();
+ if (inst) {
+ lldb::addr_t current_offset =
+ inst->GetAddress().GetFileAddress() - base_addr;
+ auto it = saved_unwind_states.upper_bound(current_offset);
+ assert(it != saved_unwind_states.begin() &&
+ "Unwind row for the function entry missing");
+ --it; // Move it to the row corresponding to the current offset
+
+ // If the offset of m_curr_row don't match with the offset we see
+ // in saved_unwind_states then we have to update m_curr_row and
+ // m_register_values based on the saved values. It is happening
+ // after we processed an epilogue and a return to caller
+ // instruction.
+ if (it->second.first->GetOffset() != m_curr_row->GetOffset()) {
+ UnwindPlan::Row *newrow = new UnwindPlan::Row;
+ *newrow = *it->second.first;
+ m_curr_row.reset(newrow);
+ m_register_values = it->second.second;
+ }
+
+ m_inst_emulator_up->SetInstruction(inst->GetOpcode(),
+ inst->GetAddress(), nullptr);
+
+ if (last_condition !=
+ m_inst_emulator_up->GetInstructionCondition()) {
+ if (m_inst_emulator_up->GetInstructionCondition() !=
+ EmulateInstruction::UnconditionalCondition &&
+ saved_unwind_states.count(current_offset) == 0) {
+ // If we don't have a saved row for the current offset then
+ // save our current state because we will have to restore it
+ // after the conditional block.
+ auto new_row =
+ std::make_shared<UnwindPlan::Row>(*m_curr_row.get());
+ saved_unwind_states.insert(
+ {current_offset, {new_row, m_register_values}});
+ }
+
+ // If the last instruction was conditional with a different
+ // condition then the then current condition then restore the
+ // condition.
+ if (last_condition !=
+ EmulateInstruction::UnconditionalCondition) {
+ const auto &saved_state =
+ saved_unwind_states.at(condition_block_start_offset);
+ m_curr_row =
+ std::make_shared<UnwindPlan::Row>(*saved_state.first);
+ m_curr_row->SetOffset(current_offset);
+ m_register_values = saved_state.second;
+ bool replace_existing =
+ true; // The last instruction might already
+ // created a row for this offset and
+ // we want to overwrite it.
+ unwind_plan.InsertRow(
+ std::make_shared<UnwindPlan::Row>(*m_curr_row),
+ replace_existing);
+ }
+
+ // We are starting a new conditional block at the actual offset
+ condition_block_start_offset = current_offset;
+ }
+
+ if (log && log->GetVerbose()) {
+ StreamString strm;
+ lldb_private::FormatEntity::Entry format;
+ FormatEntity::Parse("${frame.pc}: ", format);
+ inst->Dump(&strm, inst_list.GetMaxOpcocdeByteSize(), show_address,
+ show_bytes, nullptr, nullptr, nullptr, &format, 0);
+ log->PutString(strm.GetString());
+ }
+
+ last_condition = m_inst_emulator_up->GetInstructionCondition();
+
+ m_inst_emulator_up->EvaluateInstruction(
+ eEmulateInstructionOptionIgnoreConditions);
+
+ // If the current instruction is a branch forward then save the
+ // current CFI information for the offset where we are branching.
+ if (m_forward_branch_offset != 0 &&
+ range.ContainsFileAddress(inst->GetAddress().GetFileAddress() +
+ m_forward_branch_offset)) {
+ auto newrow =
+ std::make_shared<UnwindPlan::Row>(*m_curr_row.get());
+ newrow->SetOffset(current_offset + m_forward_branch_offset);
+ saved_unwind_states.insert(
+ {current_offset + m_forward_branch_offset,
+ {newrow, m_register_values}});
+ unwind_plan.InsertRow(newrow);
+ }
+
+ // Were there any changes to the CFI while evaluating this
+ // instruction?
+ if (m_curr_row_modified) {
+ // Save the modified row if we don't already have a CFI row in
+ // the current address
+ if (saved_unwind_states.count(
+ current_offset + inst->GetOpcode().GetByteSize()) == 0) {
+ m_curr_row->SetOffset(current_offset +
+ inst->GetOpcode().GetByteSize());
+ unwind_plan.InsertRow(m_curr_row);
+ saved_unwind_states.insert(
+ {current_offset + inst->GetOpcode().GetByteSize(),
+ {m_curr_row, m_register_values}});
+
+ // Allocate a new Row for m_curr_row, copy the current state
+ // into it
+ UnwindPlan::Row *newrow = new UnwindPlan::Row;
+ *newrow = *m_curr_row.get();
+ m_curr_row.reset(newrow);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (log && log->GetVerbose()) {
+ StreamString strm;
+ lldb::addr_t base_addr = range.GetBaseAddress().GetFileAddress();
+ strm.Printf("Resulting unwind rows for [0x%" PRIx64 " - 0x%" PRIx64 "):",
+ base_addr, base_addr + range.GetByteSize());
+ unwind_plan.Dump(strm, nullptr, base_addr);
+ log->PutString(strm.GetString());
+ }
+ return unwind_plan.GetRowCount() > 0;
+ }
+ return false;
+}
+
+bool UnwindAssemblyInstEmulation::AugmentUnwindPlanFromCallSite(
+ AddressRange &func, Thread &thread, UnwindPlan &unwind_plan) {
+ return false;
+}
+
+bool UnwindAssemblyInstEmulation::GetFastUnwindPlan(AddressRange &func,
+ Thread &thread,
+ UnwindPlan &unwind_plan) {
+ return false;
+}
+
+bool UnwindAssemblyInstEmulation::FirstNonPrologueInsn(
+ AddressRange &func, const ExecutionContext &exe_ctx,
+ Address &first_non_prologue_insn) {
+ return false;
+}
+
+UnwindAssembly *
+UnwindAssemblyInstEmulation::CreateInstance(const ArchSpec &arch) {
+ std::unique_ptr<EmulateInstruction> inst_emulator_up(
+ EmulateInstruction::FindPlugin(arch, eInstructionTypePrologueEpilogue,
+ nullptr));
+ // Make sure that all prologue instructions are handled
+ if (inst_emulator_up)
+ return new UnwindAssemblyInstEmulation(arch, inst_emulator_up.release());
+ return nullptr;
+}
+
+// PluginInterface protocol in UnwindAssemblyParser_x86
+ConstString UnwindAssemblyInstEmulation::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t UnwindAssemblyInstEmulation::GetPluginVersion() { return 1; }
+
+void UnwindAssemblyInstEmulation::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void UnwindAssemblyInstEmulation::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+ConstString UnwindAssemblyInstEmulation::GetPluginNameStatic() {
+ static ConstString g_name("inst-emulation");
+ return g_name;
+}
+
+const char *UnwindAssemblyInstEmulation::GetPluginDescriptionStatic() {
+ return "Instruction emulation based unwind information.";
+}
+
+uint64_t UnwindAssemblyInstEmulation::MakeRegisterKindValuePair(
+ const RegisterInfo &reg_info) {
+ lldb::RegisterKind reg_kind;
+ uint32_t reg_num;
+ if (EmulateInstruction::GetBestRegisterKindAndNumber(&reg_info, reg_kind,
+ reg_num))
+ return (uint64_t)reg_kind << 24 | reg_num;
+ return 0ull;
+}
+
+void UnwindAssemblyInstEmulation::SetRegisterValue(
+ const RegisterInfo &reg_info, const RegisterValue &reg_value) {
+ m_register_values[MakeRegisterKindValuePair(reg_info)] = reg_value;
+}
+
+bool UnwindAssemblyInstEmulation::GetRegisterValue(const RegisterInfo &reg_info,
+ RegisterValue &reg_value) {
+ const uint64_t reg_id = MakeRegisterKindValuePair(reg_info);
+ RegisterValueMap::const_iterator pos = m_register_values.find(reg_id);
+ if (pos != m_register_values.end()) {
+ reg_value = pos->second;
+ return true; // We had a real value that comes from an opcode that wrote
+ // to it...
+ }
+ // We are making up a value that is recognizable...
+ reg_value.SetUInt(reg_id, reg_info.byte_size);
+ return false;
+}
+
+size_t UnwindAssemblyInstEmulation::ReadMemory(
+ EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context, lldb::addr_t addr, void *dst,
+ size_t dst_len) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+
+ if (log && log->GetVerbose()) {
+ StreamString strm;
+ strm.Printf(
+ "UnwindAssemblyInstEmulation::ReadMemory (addr = 0x%16.16" PRIx64
+ ", dst = %p, dst_len = %" PRIu64 ", context = ",
+ addr, dst, (uint64_t)dst_len);
+ context.Dump(strm, instruction);
+ log->PutString(strm.GetString());
+ }
+ memset(dst, 0, dst_len);
+ return dst_len;
+}
+
+size_t UnwindAssemblyInstEmulation::WriteMemory(
+ EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context, lldb::addr_t addr,
+ const void *dst, size_t dst_len) {
+ if (baton && dst && dst_len)
+ return ((UnwindAssemblyInstEmulation *)baton)
+ ->WriteMemory(instruction, context, addr, dst, dst_len);
+ return 0;
+}
+
+size_t UnwindAssemblyInstEmulation::WriteMemory(
+ EmulateInstruction *instruction, const EmulateInstruction::Context &context,
+ lldb::addr_t addr, const void *dst, size_t dst_len) {
+ DataExtractor data(dst, dst_len,
+ instruction->GetArchitecture().GetByteOrder(),
+ instruction->GetArchitecture().GetAddressByteSize());
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+
+ if (log && log->GetVerbose()) {
+ StreamString strm;
+
+ strm.PutCString("UnwindAssemblyInstEmulation::WriteMemory (");
+ DumpDataExtractor(data, &strm, 0, eFormatBytes, 1, dst_len, UINT32_MAX,
+ addr, 0, 0);
+ strm.PutCString(", context = ");
+ context.Dump(strm, instruction);
+ log->PutString(strm.GetString());
+ }
+
+ const bool cant_replace = false;
+
+ switch (context.type) {
+ default:
+ case EmulateInstruction::eContextInvalid:
+ case EmulateInstruction::eContextReadOpcode:
+ case EmulateInstruction::eContextImmediate:
+ case EmulateInstruction::eContextAdjustBaseRegister:
+ case EmulateInstruction::eContextRegisterPlusOffset:
+ case EmulateInstruction::eContextAdjustPC:
+ case EmulateInstruction::eContextRegisterStore:
+ case EmulateInstruction::eContextRegisterLoad:
+ case EmulateInstruction::eContextRelativeBranchImmediate:
+ case EmulateInstruction::eContextAbsoluteBranchRegister:
+ case EmulateInstruction::eContextSupervisorCall:
+ case EmulateInstruction::eContextTableBranchReadMemory:
+ case EmulateInstruction::eContextWriteRegisterRandomBits:
+ case EmulateInstruction::eContextWriteMemoryRandomBits:
+ case EmulateInstruction::eContextArithmetic:
+ case EmulateInstruction::eContextAdvancePC:
+ case EmulateInstruction::eContextReturnFromException:
+ case EmulateInstruction::eContextPopRegisterOffStack:
+ case EmulateInstruction::eContextAdjustStackPointer:
+ break;
+
+ case EmulateInstruction::eContextPushRegisterOnStack: {
+ uint32_t reg_num = LLDB_INVALID_REGNUM;
+ uint32_t generic_regnum = LLDB_INVALID_REGNUM;
+ assert(context.info_type ==
+ EmulateInstruction::eInfoTypeRegisterToRegisterPlusOffset &&
+ "unhandled case, add code to handle this!");
+ const uint32_t unwind_reg_kind = m_unwind_plan_ptr->GetRegisterKind();
+ reg_num = context.info.RegisterToRegisterPlusOffset.data_reg
+ .kinds[unwind_reg_kind];
+ generic_regnum = context.info.RegisterToRegisterPlusOffset.data_reg
+ .kinds[eRegisterKindGeneric];
+
+ if (reg_num != LLDB_INVALID_REGNUM &&
+ generic_regnum != LLDB_REGNUM_GENERIC_SP) {
+ if (m_pushed_regs.find(reg_num) == m_pushed_regs.end()) {
+ m_pushed_regs[reg_num] = addr;
+ const int32_t offset = addr - m_initial_sp;
+ m_curr_row->SetRegisterLocationToAtCFAPlusOffset(reg_num, offset,
+ cant_replace);
+ m_curr_row_modified = true;
+ }
+ }
+ } break;
+ }
+
+ return dst_len;
+}
+
+bool UnwindAssemblyInstEmulation::ReadRegister(EmulateInstruction *instruction,
+ void *baton,
+ const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+
+ if (baton && reg_info)
+ return ((UnwindAssemblyInstEmulation *)baton)
+ ->ReadRegister(instruction, reg_info, reg_value);
+ return false;
+}
+bool UnwindAssemblyInstEmulation::ReadRegister(EmulateInstruction *instruction,
+ const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+ bool synthetic = GetRegisterValue(*reg_info, reg_value);
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+
+ if (log && log->GetVerbose()) {
+
+ StreamString strm;
+ strm.Printf("UnwindAssemblyInstEmulation::ReadRegister (name = \"%s\") => "
+ "synthetic_value = %i, value = ",
+ reg_info->name, synthetic);
+ DumpRegisterValue(reg_value, &strm, reg_info, false, false, eFormatDefault);
+ log->PutString(strm.GetString());
+ }
+ return true;
+}
+
+bool UnwindAssemblyInstEmulation::WriteRegister(
+ EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context, const RegisterInfo *reg_info,
+ const RegisterValue &reg_value) {
+ if (baton && reg_info)
+ return ((UnwindAssemblyInstEmulation *)baton)
+ ->WriteRegister(instruction, context, reg_info, reg_value);
+ return false;
+}
+bool UnwindAssemblyInstEmulation::WriteRegister(
+ EmulateInstruction *instruction, const EmulateInstruction::Context &context,
+ const RegisterInfo *reg_info, const RegisterValue &reg_value) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+
+ if (log && log->GetVerbose()) {
+
+ StreamString strm;
+ strm.Printf(
+ "UnwindAssemblyInstEmulation::WriteRegister (name = \"%s\", value = ",
+ reg_info->name);
+ DumpRegisterValue(reg_value, &strm, reg_info, false, false, eFormatDefault);
+ strm.PutCString(", context = ");
+ context.Dump(strm, instruction);
+ log->PutString(strm.GetString());
+ }
+
+ SetRegisterValue(*reg_info, reg_value);
+
+ switch (context.type) {
+ case EmulateInstruction::eContextInvalid:
+ case EmulateInstruction::eContextReadOpcode:
+ case EmulateInstruction::eContextImmediate:
+ case EmulateInstruction::eContextAdjustBaseRegister:
+ case EmulateInstruction::eContextRegisterPlusOffset:
+ case EmulateInstruction::eContextAdjustPC:
+ case EmulateInstruction::eContextRegisterStore:
+ case EmulateInstruction::eContextSupervisorCall:
+ case EmulateInstruction::eContextTableBranchReadMemory:
+ case EmulateInstruction::eContextWriteRegisterRandomBits:
+ case EmulateInstruction::eContextWriteMemoryRandomBits:
+ case EmulateInstruction::eContextAdvancePC:
+ case EmulateInstruction::eContextReturnFromException:
+ case EmulateInstruction::eContextPushRegisterOnStack:
+ case EmulateInstruction::eContextRegisterLoad:
+ // {
+ // const uint32_t reg_num =
+ // reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
+ // if (reg_num != LLDB_INVALID_REGNUM)
+ // {
+ // const bool can_replace_only_if_unspecified = true;
+ //
+ // m_curr_row.SetRegisterLocationToUndefined (reg_num,
+ // can_replace_only_if_unspecified,
+ // can_replace_only_if_unspecified);
+ // m_curr_row_modified = true;
+ // }
+ // }
+ break;
+
+ case EmulateInstruction::eContextArithmetic: {
+ // If we adjusted the current frame pointer by a constant then adjust the
+ // CFA offset
+ // with the same amount.
+ lldb::RegisterKind kind = m_unwind_plan_ptr->GetRegisterKind();
+ if (m_fp_is_cfa && reg_info->kinds[kind] == m_cfa_reg_info.kinds[kind] &&
+ context.info_type == EmulateInstruction::eInfoTypeRegisterPlusOffset &&
+ context.info.RegisterPlusOffset.reg.kinds[kind] ==
+ m_cfa_reg_info.kinds[kind]) {
+ const int64_t offset = context.info.RegisterPlusOffset.signed_offset;
+ m_curr_row->GetCFAValue().IncOffset(-1 * offset);
+ m_curr_row_modified = true;
+ }
+ } break;
+
+ case EmulateInstruction::eContextAbsoluteBranchRegister:
+ case EmulateInstruction::eContextRelativeBranchImmediate: {
+ if (context.info_type == EmulateInstruction::eInfoTypeISAAndImmediate &&
+ context.info.ISAAndImmediate.unsigned_data32 > 0) {
+ m_forward_branch_offset =
+ context.info.ISAAndImmediateSigned.signed_data32;
+ } else if (context.info_type ==
+ EmulateInstruction::eInfoTypeISAAndImmediateSigned &&
+ context.info.ISAAndImmediateSigned.signed_data32 > 0) {
+ m_forward_branch_offset = context.info.ISAAndImmediate.unsigned_data32;
+ } else if (context.info_type == EmulateInstruction::eInfoTypeImmediate &&
+ context.info.unsigned_immediate > 0) {
+ m_forward_branch_offset = context.info.unsigned_immediate;
+ } else if (context.info_type ==
+ EmulateInstruction::eInfoTypeImmediateSigned &&
+ context.info.signed_immediate > 0) {
+ m_forward_branch_offset = context.info.signed_immediate;
+ }
+ } break;
+
+ case EmulateInstruction::eContextPopRegisterOffStack: {
+ const uint32_t reg_num =
+ reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
+ const uint32_t generic_regnum = reg_info->kinds[eRegisterKindGeneric];
+ if (reg_num != LLDB_INVALID_REGNUM &&
+ generic_regnum != LLDB_REGNUM_GENERIC_SP) {
+ switch (context.info_type) {
+ case EmulateInstruction::eInfoTypeAddress:
+ if (m_pushed_regs.find(reg_num) != m_pushed_regs.end() &&
+ context.info.address == m_pushed_regs[reg_num]) {
+ m_curr_row->SetRegisterLocationToSame(reg_num,
+ false /*must_replace*/);
+ m_curr_row_modified = true;
+ }
+ break;
+ case EmulateInstruction::eInfoTypeISA:
+ assert(
+ (generic_regnum == LLDB_REGNUM_GENERIC_PC ||
+ generic_regnum == LLDB_REGNUM_GENERIC_FLAGS) &&
+ "eInfoTypeISA used for popping a register other the PC/FLAGS");
+ if (generic_regnum != LLDB_REGNUM_GENERIC_FLAGS) {
+ m_curr_row->SetRegisterLocationToSame(reg_num,
+ false /*must_replace*/);
+ m_curr_row_modified = true;
+ }
+ break;
+ default:
+ assert(false && "unhandled case, add code to handle this!");
+ break;
+ }
+ }
+ } break;
+
+ case EmulateInstruction::eContextSetFramePointer:
+ if (!m_fp_is_cfa) {
+ m_fp_is_cfa = true;
+ m_cfa_reg_info = *reg_info;
+ const uint32_t cfa_reg_num =
+ reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
+ assert(cfa_reg_num != LLDB_INVALID_REGNUM);
+ m_curr_row->GetCFAValue().SetIsRegisterPlusOffset(
+ cfa_reg_num, m_initial_sp - reg_value.GetAsUInt64());
+ m_curr_row_modified = true;
+ }
+ break;
+
+ case EmulateInstruction::eContextRestoreStackPointer:
+ if (m_fp_is_cfa) {
+ m_fp_is_cfa = false;
+ m_cfa_reg_info = *reg_info;
+ const uint32_t cfa_reg_num =
+ reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()];
+ assert(cfa_reg_num != LLDB_INVALID_REGNUM);
+ m_curr_row->GetCFAValue().SetIsRegisterPlusOffset(
+ cfa_reg_num, m_initial_sp - reg_value.GetAsUInt64());
+ m_curr_row_modified = true;
+ }
+ break;
+
+ case EmulateInstruction::eContextAdjustStackPointer:
+ // If we have created a frame using the frame pointer, don't follow
+ // subsequent adjustments to the stack pointer.
+ if (!m_fp_is_cfa) {
+ m_curr_row->GetCFAValue().SetIsRegisterPlusOffset(
+ m_curr_row->GetCFAValue().GetRegisterNumber(),
+ m_initial_sp - reg_value.GetAsUInt64());
+ m_curr_row_modified = true;
+ }
+ break;
+ }
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h b/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h
new file mode 100644
index 000000000000..9125bd5b1fe3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h
@@ -0,0 +1,154 @@
+//===-- UnwindAssemblyInstEmulation.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 liblldb_UnwindAssemblyInstEmulation_h_
+#define liblldb_UnwindAssemblyInstEmulation_h_
+
+#include "lldb/Core/EmulateInstruction.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/UnwindAssembly.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/lldb-private.h"
+
+class UnwindAssemblyInstEmulation : public lldb_private::UnwindAssembly {
+public:
+ ~UnwindAssemblyInstEmulation() override = default;
+
+ bool GetNonCallSiteUnwindPlanFromAssembly(
+ lldb_private::AddressRange &func, lldb_private::Thread &thread,
+ lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool
+ GetNonCallSiteUnwindPlanFromAssembly(lldb_private::AddressRange &func,
+ uint8_t *opcode_data, size_t opcode_size,
+ lldb_private::UnwindPlan &unwind_plan);
+
+ bool
+ AugmentUnwindPlanFromCallSite(lldb_private::AddressRange &func,
+ lldb_private::Thread &thread,
+ lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool GetFastUnwindPlan(lldb_private::AddressRange &func,
+ lldb_private::Thread &thread,
+ lldb_private::UnwindPlan &unwind_plan) override;
+
+ // thread may be NULL in which case we only use the Target (e.g. if this is
+ // called pre-process-launch).
+ bool
+ FirstNonPrologueInsn(lldb_private::AddressRange &func,
+ const lldb_private::ExecutionContext &exe_ctx,
+ lldb_private::Address &first_non_prologue_insn) override;
+
+ static lldb_private::UnwindAssembly *
+ CreateInstance(const lldb_private::ArchSpec &arch);
+
+ // PluginInterface protocol
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+private:
+ // Call CreateInstance to get an instance of this class
+ UnwindAssemblyInstEmulation(const lldb_private::ArchSpec &arch,
+ lldb_private::EmulateInstruction *inst_emulator)
+ : UnwindAssembly(arch), m_inst_emulator_up(inst_emulator),
+ m_range_ptr(nullptr), m_unwind_plan_ptr(nullptr), m_curr_row(),
+ m_cfa_reg_info(), m_fp_is_cfa(false), m_register_values(),
+ m_pushed_regs(), m_curr_row_modified(false),
+ m_forward_branch_offset(0) {
+ if (m_inst_emulator_up.get()) {
+ m_inst_emulator_up->SetBaton(this);
+ m_inst_emulator_up->SetCallbacks(ReadMemory, WriteMemory, ReadRegister,
+ WriteRegister);
+ }
+ }
+
+ static size_t
+ ReadMemory(lldb_private::EmulateInstruction *instruction, void *baton,
+ const lldb_private::EmulateInstruction::Context &context,
+ lldb::addr_t addr, void *dst, size_t length);
+
+ static size_t
+ WriteMemory(lldb_private::EmulateInstruction *instruction, void *baton,
+ const lldb_private::EmulateInstruction::Context &context,
+ lldb::addr_t addr, const void *dst, size_t length);
+
+ static bool ReadRegister(lldb_private::EmulateInstruction *instruction,
+ void *baton,
+ const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &reg_value);
+
+ static bool
+ WriteRegister(lldb_private::EmulateInstruction *instruction, void *baton,
+ const lldb_private::EmulateInstruction::Context &context,
+ const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &reg_value);
+
+ // size_t
+ // ReadMemory (lldb_private::EmulateInstruction *instruction,
+ // const lldb_private::EmulateInstruction::Context &context,
+ // lldb::addr_t addr,
+ // void *dst,
+ // size_t length);
+
+ size_t WriteMemory(lldb_private::EmulateInstruction *instruction,
+ const lldb_private::EmulateInstruction::Context &context,
+ lldb::addr_t addr, const void *dst, size_t length);
+
+ bool ReadRegister(lldb_private::EmulateInstruction *instruction,
+ const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &reg_value);
+
+ bool WriteRegister(lldb_private::EmulateInstruction *instruction,
+ const lldb_private::EmulateInstruction::Context &context,
+ const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &reg_value);
+
+ static uint64_t
+ MakeRegisterKindValuePair(const lldb_private::RegisterInfo &reg_info);
+
+ void SetRegisterValue(const lldb_private::RegisterInfo &reg_info,
+ const lldb_private::RegisterValue &reg_value);
+
+ bool GetRegisterValue(const lldb_private::RegisterInfo &reg_info,
+ lldb_private::RegisterValue &reg_value);
+
+ std::unique_ptr<lldb_private::EmulateInstruction> m_inst_emulator_up;
+ lldb_private::AddressRange *m_range_ptr;
+ lldb_private::UnwindPlan *m_unwind_plan_ptr;
+ lldb_private::UnwindPlan::RowSP m_curr_row;
+ typedef std::map<uint64_t, uint64_t> PushedRegisterToAddrMap;
+ uint64_t m_initial_sp;
+ lldb_private::RegisterInfo m_cfa_reg_info;
+ bool m_fp_is_cfa;
+ typedef std::map<uint64_t, lldb_private::RegisterValue> RegisterValueMap;
+ RegisterValueMap m_register_values;
+ PushedRegisterToAddrMap m_pushed_regs;
+
+ // While processing the instruction stream, we need to communicate some state
+ // change
+ // information up to the higher level loop that makes decisions about how to
+ // push
+ // the unwind instructions for the UnwindPlan we're constructing.
+
+ // The instruction we're processing updated the UnwindPlan::Row contents
+ bool m_curr_row_modified;
+ // The instruction is branching forward with the given offset. 0 value means
+ // no branching.
+ uint32_t m_forward_branch_offset;
+};
+
+#endif // liblldb_UnwindAssemblyInstEmulation_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp b/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp
new file mode 100644
index 000000000000..ce168f021047
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp
@@ -0,0 +1,269 @@
+//===-- UnwindAssembly-x86.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 "UnwindAssembly-x86.h"
+#include "x86AssemblyInspectionEngine.h"
+
+#include "llvm-c/Disassembler.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/TargetSelect.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/RegisterNumber.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/UnwindAssembly.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// UnwindAssemblyParser_x86 method definitions
+
+UnwindAssembly_x86::UnwindAssembly_x86(const ArchSpec &arch)
+ : lldb_private::UnwindAssembly(arch),
+ m_assembly_inspection_engine(new x86AssemblyInspectionEngine(arch)) {}
+
+UnwindAssembly_x86::~UnwindAssembly_x86() {
+ delete m_assembly_inspection_engine;
+}
+
+bool UnwindAssembly_x86::GetNonCallSiteUnwindPlanFromAssembly(
+ AddressRange &func, Thread &thread, UnwindPlan &unwind_plan) {
+ if (!func.GetBaseAddress().IsValid() || func.GetByteSize() == 0)
+ return false;
+ if (m_assembly_inspection_engine == nullptr)
+ return false;
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp.get() == nullptr)
+ return false;
+ const bool prefer_file_cache = true;
+ std::vector<uint8_t> function_text(func.GetByteSize());
+ Status error;
+ if (process_sp->GetTarget().ReadMemory(
+ func.GetBaseAddress(), prefer_file_cache, function_text.data(),
+ func.GetByteSize(), error) == func.GetByteSize()) {
+ RegisterContextSP reg_ctx(thread.GetRegisterContext());
+ m_assembly_inspection_engine->Initialize(reg_ctx);
+ return m_assembly_inspection_engine->GetNonCallSiteUnwindPlanFromAssembly(
+ function_text.data(), func.GetByteSize(), func, unwind_plan);
+ }
+ return false;
+}
+
+bool UnwindAssembly_x86::AugmentUnwindPlanFromCallSite(
+ AddressRange &func, Thread &thread, UnwindPlan &unwind_plan) {
+ bool do_augment_unwindplan = true;
+
+ UnwindPlan::RowSP first_row = unwind_plan.GetRowForFunctionOffset(0);
+ UnwindPlan::RowSP last_row = unwind_plan.GetRowForFunctionOffset(-1);
+
+ int wordsize = 8;
+ ProcessSP process_sp(thread.GetProcess());
+ if (process_sp.get() == nullptr)
+ return false;
+
+ wordsize = process_sp->GetTarget().GetArchitecture().GetAddressByteSize();
+
+ RegisterNumber sp_regnum(thread, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_SP);
+ RegisterNumber pc_regnum(thread, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC);
+
+ // Does this UnwindPlan describe the prologue? I want to see that the CFA is
+ // set in terms of the stack pointer plus an offset, and I want to see that
+ // rip is retrieved at the CFA-wordsize. If there is no description of the
+ // prologue, don't try to augment this eh_frame unwinder code, fall back to
+ // assembly parsing instead.
+
+ if (first_row->GetCFAValue().GetValueType() !=
+ UnwindPlan::Row::FAValue::isRegisterPlusOffset ||
+ RegisterNumber(thread, unwind_plan.GetRegisterKind(),
+ first_row->GetCFAValue().GetRegisterNumber()) !=
+ sp_regnum ||
+ first_row->GetCFAValue().GetOffset() != wordsize) {
+ return false;
+ }
+ UnwindPlan::Row::RegisterLocation first_row_pc_loc;
+ if (!first_row->GetRegisterInfo(
+ pc_regnum.GetAsKind(unwind_plan.GetRegisterKind()),
+ first_row_pc_loc) ||
+ !first_row_pc_loc.IsAtCFAPlusOffset() ||
+ first_row_pc_loc.GetOffset() != -wordsize) {
+ return false;
+ }
+
+ // It looks like the prologue is described. Is the epilogue described? If it
+ // is, no need to do any augmentation.
+
+ if (first_row != last_row &&
+ first_row->GetOffset() != last_row->GetOffset()) {
+ // The first & last row have the same CFA register and the same CFA offset
+ // value and the CFA register is esp/rsp (the stack pointer).
+
+ // We're checking that both of them have an unwind rule like "CFA=esp+4" or
+ // CFA+rsp+8".
+
+ if (first_row->GetCFAValue().GetValueType() ==
+ last_row->GetCFAValue().GetValueType() &&
+ first_row->GetCFAValue().GetRegisterNumber() ==
+ last_row->GetCFAValue().GetRegisterNumber() &&
+ first_row->GetCFAValue().GetOffset() ==
+ last_row->GetCFAValue().GetOffset()) {
+ // Get the register locations for eip/rip from the first & last rows. Are
+ // they both CFA plus an offset? Is it the same offset?
+
+ UnwindPlan::Row::RegisterLocation last_row_pc_loc;
+ if (last_row->GetRegisterInfo(
+ pc_regnum.GetAsKind(unwind_plan.GetRegisterKind()),
+ last_row_pc_loc)) {
+ if (last_row_pc_loc.IsAtCFAPlusOffset() &&
+ first_row_pc_loc.GetOffset() == last_row_pc_loc.GetOffset()) {
+
+ // One last sanity check: Is the unwind rule for getting the caller
+ // pc value "deref the CFA-4" or "deref the CFA-8"?
+
+ // If so, we have an UnwindPlan that already describes the epilogue
+ // and we don't need to modify it at all.
+
+ if (first_row_pc_loc.GetOffset() == -wordsize) {
+ do_augment_unwindplan = false;
+ }
+ }
+ }
+ }
+ }
+
+ if (do_augment_unwindplan) {
+ if (!func.GetBaseAddress().IsValid() || func.GetByteSize() == 0)
+ return false;
+ if (m_assembly_inspection_engine == nullptr)
+ return false;
+ const bool prefer_file_cache = true;
+ std::vector<uint8_t> function_text(func.GetByteSize());
+ Status error;
+ if (process_sp->GetTarget().ReadMemory(
+ func.GetBaseAddress(), prefer_file_cache, function_text.data(),
+ func.GetByteSize(), error) == func.GetByteSize()) {
+ RegisterContextSP reg_ctx(thread.GetRegisterContext());
+ m_assembly_inspection_engine->Initialize(reg_ctx);
+ return m_assembly_inspection_engine->AugmentUnwindPlanFromCallSite(
+ function_text.data(), func.GetByteSize(), func, unwind_plan, reg_ctx);
+ }
+ }
+
+ return false;
+}
+
+bool UnwindAssembly_x86::GetFastUnwindPlan(AddressRange &func, Thread &thread,
+ UnwindPlan &unwind_plan) {
+ // if prologue is
+ // 55 pushl %ebp
+ // 89 e5 movl %esp, %ebp
+ // or
+ // 55 pushq %rbp
+ // 48 89 e5 movq %rsp, %rbp
+
+ // We should pull in the ABI architecture default unwind plan and return that
+
+ llvm::SmallVector<uint8_t, 4> opcode_data;
+
+ ProcessSP process_sp = thread.GetProcess();
+ if (process_sp) {
+ Target &target(process_sp->GetTarget());
+ const bool prefer_file_cache = true;
+ Status error;
+ if (target.ReadMemory(func.GetBaseAddress(), prefer_file_cache,
+ opcode_data.data(), 4, error) == 4) {
+ uint8_t i386_push_mov[] = {0x55, 0x89, 0xe5};
+ uint8_t x86_64_push_mov[] = {0x55, 0x48, 0x89, 0xe5};
+
+ if (memcmp(opcode_data.data(), i386_push_mov, sizeof(i386_push_mov)) ==
+ 0 ||
+ memcmp(opcode_data.data(), x86_64_push_mov,
+ sizeof(x86_64_push_mov)) == 0) {
+ ABISP abi_sp = process_sp->GetABI();
+ if (abi_sp) {
+ return abi_sp->CreateDefaultUnwindPlan(unwind_plan);
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool UnwindAssembly_x86::FirstNonPrologueInsn(
+ AddressRange &func, const ExecutionContext &exe_ctx,
+ Address &first_non_prologue_insn) {
+
+ if (!func.GetBaseAddress().IsValid())
+ return false;
+
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target == nullptr)
+ return false;
+
+ if (m_assembly_inspection_engine == nullptr)
+ return false;
+
+ const bool prefer_file_cache = true;
+ std::vector<uint8_t> function_text(func.GetByteSize());
+ Status error;
+ if (target->ReadMemory(func.GetBaseAddress(), prefer_file_cache,
+ function_text.data(), func.GetByteSize(),
+ error) == func.GetByteSize()) {
+ size_t offset;
+ if (m_assembly_inspection_engine->FindFirstNonPrologueInstruction(
+ function_text.data(), func.GetByteSize(), offset)) {
+ first_non_prologue_insn = func.GetBaseAddress();
+ first_non_prologue_insn.Slide(offset);
+ }
+ return true;
+ }
+ return false;
+}
+
+UnwindAssembly *UnwindAssembly_x86::CreateInstance(const ArchSpec &arch) {
+ const llvm::Triple::ArchType cpu = arch.GetMachine();
+ if (cpu == llvm::Triple::x86 || cpu == llvm::Triple::x86_64)
+ return new UnwindAssembly_x86(arch);
+ return nullptr;
+}
+
+// PluginInterface protocol in UnwindAssemblyParser_x86
+
+ConstString UnwindAssembly_x86::GetPluginName() {
+ return GetPluginNameStatic();
+}
+
+uint32_t UnwindAssembly_x86::GetPluginVersion() { return 1; }
+
+void UnwindAssembly_x86::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void UnwindAssembly_x86::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::ConstString UnwindAssembly_x86::GetPluginNameStatic() {
+ static ConstString g_name("x86");
+ return g_name;
+}
+
+const char *UnwindAssembly_x86::GetPluginDescriptionStatic() {
+ return "i386 and x86_64 assembly language profiler plugin.";
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h b/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h
new file mode 100644
index 000000000000..7c198bbc33af
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h
@@ -0,0 +1,65 @@
+//===-- UnwindAssembly-x86.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 liblldb_UnwindAssembly_x86_h_
+#define liblldb_UnwindAssembly_x86_h_
+
+#include "x86AssemblyInspectionEngine.h"
+
+#include "lldb/Target/UnwindAssembly.h"
+#include "lldb/lldb-private.h"
+
+class UnwindAssembly_x86 : public lldb_private::UnwindAssembly {
+public:
+ ~UnwindAssembly_x86() override;
+
+ bool GetNonCallSiteUnwindPlanFromAssembly(
+ lldb_private::AddressRange &func, lldb_private::Thread &thread,
+ lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool
+ AugmentUnwindPlanFromCallSite(lldb_private::AddressRange &func,
+ lldb_private::Thread &thread,
+ lldb_private::UnwindPlan &unwind_plan) override;
+
+ bool GetFastUnwindPlan(lldb_private::AddressRange &func,
+ lldb_private::Thread &thread,
+ lldb_private::UnwindPlan &unwind_plan) override;
+
+ // thread may be NULL in which case we only use the Target (e.g. if this is
+ // called pre-process-launch).
+ bool
+ FirstNonPrologueInsn(lldb_private::AddressRange &func,
+ const lldb_private::ExecutionContext &exe_ctx,
+ lldb_private::Address &first_non_prologue_insn) override;
+
+ static lldb_private::UnwindAssembly *
+ CreateInstance(const lldb_private::ArchSpec &arch);
+
+ // PluginInterface protocol
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ lldb_private::ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+private:
+ UnwindAssembly_x86(const lldb_private::ArchSpec &arch);
+
+ lldb_private::ArchSpec m_arch;
+
+ lldb_private::x86AssemblyInspectionEngine *m_assembly_inspection_engine;
+};
+
+#endif // liblldb_UnwindAssembly_x86_h_
diff --git a/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp b/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp
new file mode 100644
index 000000000000..43041ca1bb2f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp
@@ -0,0 +1,1614 @@
+//===-- x86AssemblyInspectionEngine.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 "x86AssemblyInspectionEngine.h"
+
+#include <memory>
+
+#include "llvm-c/Disassembler.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/UnwindAssembly.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+x86AssemblyInspectionEngine::x86AssemblyInspectionEngine(const ArchSpec &arch)
+ : m_cur_insn(nullptr), m_machine_ip_regnum(LLDB_INVALID_REGNUM),
+ m_machine_sp_regnum(LLDB_INVALID_REGNUM),
+ m_machine_fp_regnum(LLDB_INVALID_REGNUM),
+ m_lldb_ip_regnum(LLDB_INVALID_REGNUM),
+ m_lldb_sp_regnum(LLDB_INVALID_REGNUM),
+ m_lldb_fp_regnum(LLDB_INVALID_REGNUM),
+
+ m_reg_map(), m_arch(arch), m_cpu(k_cpu_unspecified), m_wordsize(-1),
+ m_register_map_initialized(false), m_disasm_context() {
+ m_disasm_context =
+ ::LLVMCreateDisasm(arch.GetTriple().getTriple().c_str(), nullptr,
+ /*TagType=*/1, nullptr, nullptr);
+}
+
+x86AssemblyInspectionEngine::~x86AssemblyInspectionEngine() {
+ ::LLVMDisasmDispose(m_disasm_context);
+}
+
+void x86AssemblyInspectionEngine::Initialize(RegisterContextSP &reg_ctx) {
+ m_cpu = k_cpu_unspecified;
+ m_wordsize = -1;
+ m_register_map_initialized = false;
+
+ const llvm::Triple::ArchType cpu = m_arch.GetMachine();
+ if (cpu == llvm::Triple::x86)
+ m_cpu = k_i386;
+ else if (cpu == llvm::Triple::x86_64)
+ m_cpu = k_x86_64;
+
+ if (m_cpu == k_cpu_unspecified)
+ return;
+
+ if (reg_ctx.get() == nullptr)
+ return;
+
+ if (m_cpu == k_i386) {
+ m_machine_ip_regnum = k_machine_eip;
+ m_machine_sp_regnum = k_machine_esp;
+ m_machine_fp_regnum = k_machine_ebp;
+ m_machine_alt_fp_regnum = k_machine_ebx;
+ m_wordsize = 4;
+
+ struct lldb_reg_info reginfo;
+ reginfo.name = "eax";
+ m_reg_map[k_machine_eax] = reginfo;
+ reginfo.name = "edx";
+ m_reg_map[k_machine_edx] = reginfo;
+ reginfo.name = "esp";
+ m_reg_map[k_machine_esp] = reginfo;
+ reginfo.name = "esi";
+ m_reg_map[k_machine_esi] = reginfo;
+ reginfo.name = "eip";
+ m_reg_map[k_machine_eip] = reginfo;
+ reginfo.name = "ecx";
+ m_reg_map[k_machine_ecx] = reginfo;
+ reginfo.name = "ebx";
+ m_reg_map[k_machine_ebx] = reginfo;
+ reginfo.name = "ebp";
+ m_reg_map[k_machine_ebp] = reginfo;
+ reginfo.name = "edi";
+ m_reg_map[k_machine_edi] = reginfo;
+ } else {
+ m_machine_ip_regnum = k_machine_rip;
+ m_machine_sp_regnum = k_machine_rsp;
+ m_machine_fp_regnum = k_machine_rbp;
+ m_machine_alt_fp_regnum = k_machine_rbx;
+ m_wordsize = 8;
+
+ struct lldb_reg_info reginfo;
+ reginfo.name = "rax";
+ m_reg_map[k_machine_rax] = reginfo;
+ reginfo.name = "rdx";
+ m_reg_map[k_machine_rdx] = reginfo;
+ reginfo.name = "rsp";
+ m_reg_map[k_machine_rsp] = reginfo;
+ reginfo.name = "rsi";
+ m_reg_map[k_machine_rsi] = reginfo;
+ reginfo.name = "r8";
+ m_reg_map[k_machine_r8] = reginfo;
+ reginfo.name = "r10";
+ m_reg_map[k_machine_r10] = reginfo;
+ reginfo.name = "r12";
+ m_reg_map[k_machine_r12] = reginfo;
+ reginfo.name = "r14";
+ m_reg_map[k_machine_r14] = reginfo;
+ reginfo.name = "rip";
+ m_reg_map[k_machine_rip] = reginfo;
+ reginfo.name = "rcx";
+ m_reg_map[k_machine_rcx] = reginfo;
+ reginfo.name = "rbx";
+ m_reg_map[k_machine_rbx] = reginfo;
+ reginfo.name = "rbp";
+ m_reg_map[k_machine_rbp] = reginfo;
+ reginfo.name = "rdi";
+ m_reg_map[k_machine_rdi] = reginfo;
+ reginfo.name = "r9";
+ m_reg_map[k_machine_r9] = reginfo;
+ reginfo.name = "r11";
+ m_reg_map[k_machine_r11] = reginfo;
+ reginfo.name = "r13";
+ m_reg_map[k_machine_r13] = reginfo;
+ reginfo.name = "r15";
+ m_reg_map[k_machine_r15] = reginfo;
+ }
+
+ for (MachineRegnumToNameAndLLDBRegnum::iterator it = m_reg_map.begin();
+ it != m_reg_map.end(); ++it) {
+ const RegisterInfo *ri = reg_ctx->GetRegisterInfoByName(it->second.name);
+ if (ri)
+ it->second.lldb_regnum = ri->kinds[eRegisterKindLLDB];
+ }
+
+ uint32_t lldb_regno;
+ if (machine_regno_to_lldb_regno(m_machine_sp_regnum, lldb_regno))
+ m_lldb_sp_regnum = lldb_regno;
+ if (machine_regno_to_lldb_regno(m_machine_fp_regnum, lldb_regno))
+ m_lldb_fp_regnum = lldb_regno;
+ if (machine_regno_to_lldb_regno(m_machine_alt_fp_regnum, lldb_regno))
+ m_lldb_alt_fp_regnum = lldb_regno;
+ if (machine_regno_to_lldb_regno(m_machine_ip_regnum, lldb_regno))
+ m_lldb_ip_regnum = lldb_regno;
+
+ m_register_map_initialized = true;
+}
+
+void x86AssemblyInspectionEngine::Initialize(
+ std::vector<lldb_reg_info> &reg_info) {
+ m_cpu = k_cpu_unspecified;
+ m_wordsize = -1;
+ m_register_map_initialized = false;
+
+ const llvm::Triple::ArchType cpu = m_arch.GetMachine();
+ if (cpu == llvm::Triple::x86)
+ m_cpu = k_i386;
+ else if (cpu == llvm::Triple::x86_64)
+ m_cpu = k_x86_64;
+
+ if (m_cpu == k_cpu_unspecified)
+ return;
+
+ if (m_cpu == k_i386) {
+ m_machine_ip_regnum = k_machine_eip;
+ m_machine_sp_regnum = k_machine_esp;
+ m_machine_fp_regnum = k_machine_ebp;
+ m_machine_alt_fp_regnum = k_machine_ebx;
+ m_wordsize = 4;
+
+ struct lldb_reg_info reginfo;
+ reginfo.name = "eax";
+ m_reg_map[k_machine_eax] = reginfo;
+ reginfo.name = "edx";
+ m_reg_map[k_machine_edx] = reginfo;
+ reginfo.name = "esp";
+ m_reg_map[k_machine_esp] = reginfo;
+ reginfo.name = "esi";
+ m_reg_map[k_machine_esi] = reginfo;
+ reginfo.name = "eip";
+ m_reg_map[k_machine_eip] = reginfo;
+ reginfo.name = "ecx";
+ m_reg_map[k_machine_ecx] = reginfo;
+ reginfo.name = "ebx";
+ m_reg_map[k_machine_ebx] = reginfo;
+ reginfo.name = "ebp";
+ m_reg_map[k_machine_ebp] = reginfo;
+ reginfo.name = "edi";
+ m_reg_map[k_machine_edi] = reginfo;
+ } else {
+ m_machine_ip_regnum = k_machine_rip;
+ m_machine_sp_regnum = k_machine_rsp;
+ m_machine_fp_regnum = k_machine_rbp;
+ m_machine_alt_fp_regnum = k_machine_rbx;
+ m_wordsize = 8;
+
+ struct lldb_reg_info reginfo;
+ reginfo.name = "rax";
+ m_reg_map[k_machine_rax] = reginfo;
+ reginfo.name = "rdx";
+ m_reg_map[k_machine_rdx] = reginfo;
+ reginfo.name = "rsp";
+ m_reg_map[k_machine_rsp] = reginfo;
+ reginfo.name = "rsi";
+ m_reg_map[k_machine_rsi] = reginfo;
+ reginfo.name = "r8";
+ m_reg_map[k_machine_r8] = reginfo;
+ reginfo.name = "r10";
+ m_reg_map[k_machine_r10] = reginfo;
+ reginfo.name = "r12";
+ m_reg_map[k_machine_r12] = reginfo;
+ reginfo.name = "r14";
+ m_reg_map[k_machine_r14] = reginfo;
+ reginfo.name = "rip";
+ m_reg_map[k_machine_rip] = reginfo;
+ reginfo.name = "rcx";
+ m_reg_map[k_machine_rcx] = reginfo;
+ reginfo.name = "rbx";
+ m_reg_map[k_machine_rbx] = reginfo;
+ reginfo.name = "rbp";
+ m_reg_map[k_machine_rbp] = reginfo;
+ reginfo.name = "rdi";
+ m_reg_map[k_machine_rdi] = reginfo;
+ reginfo.name = "r9";
+ m_reg_map[k_machine_r9] = reginfo;
+ reginfo.name = "r11";
+ m_reg_map[k_machine_r11] = reginfo;
+ reginfo.name = "r13";
+ m_reg_map[k_machine_r13] = reginfo;
+ reginfo.name = "r15";
+ m_reg_map[k_machine_r15] = reginfo;
+ }
+
+ for (MachineRegnumToNameAndLLDBRegnum::iterator it = m_reg_map.begin();
+ it != m_reg_map.end(); ++it) {
+ for (size_t i = 0; i < reg_info.size(); ++i) {
+ if (::strcmp(reg_info[i].name, it->second.name) == 0) {
+ it->second.lldb_regnum = reg_info[i].lldb_regnum;
+ break;
+ }
+ }
+ }
+
+ uint32_t lldb_regno;
+ if (machine_regno_to_lldb_regno(m_machine_sp_regnum, lldb_regno))
+ m_lldb_sp_regnum = lldb_regno;
+ if (machine_regno_to_lldb_regno(m_machine_fp_regnum, lldb_regno))
+ m_lldb_fp_regnum = lldb_regno;
+ if (machine_regno_to_lldb_regno(m_machine_alt_fp_regnum, lldb_regno))
+ m_lldb_alt_fp_regnum = lldb_regno;
+ if (machine_regno_to_lldb_regno(m_machine_ip_regnum, lldb_regno))
+ m_lldb_ip_regnum = lldb_regno;
+
+ m_register_map_initialized = true;
+}
+
+// This function expects an x86 native register number (i.e. the bits stripped
+// out of the actual instruction), not an lldb register number.
+//
+// FIXME: This is ABI dependent, it shouldn't be hardcoded here.
+
+bool x86AssemblyInspectionEngine::nonvolatile_reg_p(int machine_regno) {
+ if (m_cpu == k_i386) {
+ switch (machine_regno) {
+ case k_machine_ebx:
+ case k_machine_ebp: // not actually a nonvolatile but often treated as such
+ // by convention
+ case k_machine_esi:
+ case k_machine_edi:
+ case k_machine_esp:
+ return true;
+ default:
+ return false;
+ }
+ }
+ if (m_cpu == k_x86_64) {
+ switch (machine_regno) {
+ case k_machine_rbx:
+ case k_machine_rsp:
+ case k_machine_rbp: // not actually a nonvolatile but often treated as such
+ // by convention
+ case k_machine_r12:
+ case k_machine_r13:
+ case k_machine_r14:
+ case k_machine_r15:
+ return true;
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+// Macro to detect if this is a REX mode prefix byte.
+#define REX_W_PREFIX_P(opcode) (((opcode) & (~0x5)) == 0x48)
+
+// The high bit which should be added to the source register number (the "R"
+// bit)
+#define REX_W_SRCREG(opcode) (((opcode)&0x4) >> 2)
+
+// The high bit which should be added to the destination register number (the
+// "B" bit)
+#define REX_W_DSTREG(opcode) ((opcode)&0x1)
+
+// pushq %rbp [0x55]
+bool x86AssemblyInspectionEngine::push_rbp_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ return *p == 0x55;
+}
+
+// pushq $0 ; the first instruction in start() [0x6a 0x00]
+bool x86AssemblyInspectionEngine::push_0_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ return *p == 0x6a && *(p + 1) == 0x0;
+}
+
+// pushq $0
+// pushl $0
+bool x86AssemblyInspectionEngine::push_imm_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ return *p == 0x68 || *p == 0x6a;
+}
+
+// pushl imm8(%esp)
+//
+// e.g. 0xff 0x74 0x24 0x20 - 'pushl 0x20(%esp)' (same byte pattern for 'pushq
+// 0x20(%rsp)' in an x86_64 program)
+//
+// 0xff (with opcode bits '6' in next byte, PUSH r/m32) 0x74 (ModR/M byte with
+// three bits used to specify the opcode)
+// mod == b01, opcode == b110, R/M == b100
+// "+disp8"
+// 0x24 (SIB byte - scaled index = 0, r32 == esp) 0x20 imm8 value
+
+bool x86AssemblyInspectionEngine::push_extended_pattern_p() {
+ if (*m_cur_insn == 0xff) {
+ // Get the 3 opcode bits from the ModR/M byte
+ uint8_t opcode = (*(m_cur_insn + 1) >> 3) & 7;
+ if (opcode == 6) {
+ // I'm only looking for 0xff /6 here - I
+ // don't really care what value is being pushed, just that we're pushing
+ // a 32/64 bit value on to the stack is enough.
+ return true;
+ }
+ }
+ return false;
+}
+
+// instructions only valid in 32-bit mode:
+// 0x0e - push cs
+// 0x16 - push ss
+// 0x1e - push ds
+// 0x06 - push es
+bool x86AssemblyInspectionEngine::push_misc_reg_p() {
+ uint8_t p = *m_cur_insn;
+ if (m_wordsize == 4) {
+ if (p == 0x0e || p == 0x16 || p == 0x1e || p == 0x06)
+ return true;
+ }
+ return false;
+}
+
+// pushq %rbx
+// pushl %ebx
+bool x86AssemblyInspectionEngine::push_reg_p(int &regno) {
+ uint8_t *p = m_cur_insn;
+ int regno_prefix_bit = 0;
+ // If we have a rex prefix byte, check to see if a B bit is set
+ if (m_wordsize == 8 && (*p & 0xfe) == 0x40) {
+ regno_prefix_bit = (*p & 1) << 3;
+ p++;
+ }
+ if (*p >= 0x50 && *p <= 0x57) {
+ regno = (*p - 0x50) | regno_prefix_bit;
+ return true;
+ }
+ return false;
+}
+
+// movq %rsp, %rbp [0x48 0x8b 0xec] or [0x48 0x89 0xe5] movl %esp, %ebp [0x8b
+// 0xec] or [0x89 0xe5]
+bool x86AssemblyInspectionEngine::mov_rsp_rbp_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ if (m_wordsize == 8 && *p == 0x48)
+ p++;
+ if (*(p) == 0x8b && *(p + 1) == 0xec)
+ return true;
+ if (*(p) == 0x89 && *(p + 1) == 0xe5)
+ return true;
+ return false;
+}
+
+// movq %rsp, %rbx [0x48 0x8b 0xdc] or [0x48 0x89 0xe3]
+// movl %esp, %ebx [0x8b 0xdc] or [0x89 0xe3]
+bool x86AssemblyInspectionEngine::mov_rsp_rbx_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ if (m_wordsize == 8 && *p == 0x48)
+ p++;
+ if (*(p) == 0x8b && *(p + 1) == 0xdc)
+ return true;
+ if (*(p) == 0x89 && *(p + 1) == 0xe3)
+ return true;
+ return false;
+}
+
+// movq %rbp, %rsp [0x48 0x8b 0xe5] or [0x48 0x89 0xec]
+// movl %ebp, %esp [0x8b 0xe5] or [0x89 0xec]
+bool x86AssemblyInspectionEngine::mov_rbp_rsp_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ if (m_wordsize == 8 && *p == 0x48)
+ p++;
+ if (*(p) == 0x8b && *(p + 1) == 0xe5)
+ return true;
+ if (*(p) == 0x89 && *(p + 1) == 0xec)
+ return true;
+ return false;
+}
+
+// movq %rbx, %rsp [0x48 0x8b 0xe3] or [0x48 0x89 0xdc]
+// movl %ebx, %esp [0x8b 0xe3] or [0x89 0xdc]
+bool x86AssemblyInspectionEngine::mov_rbx_rsp_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ if (m_wordsize == 8 && *p == 0x48)
+ p++;
+ if (*(p) == 0x8b && *(p + 1) == 0xe3)
+ return true;
+ if (*(p) == 0x89 && *(p + 1) == 0xdc)
+ return true;
+ return false;
+}
+
+// subq $0x20, %rsp
+bool x86AssemblyInspectionEngine::sub_rsp_pattern_p(int &amount) {
+ uint8_t *p = m_cur_insn;
+ if (m_wordsize == 8 && *p == 0x48)
+ p++;
+ // 8-bit immediate operand
+ if (*p == 0x83 && *(p + 1) == 0xec) {
+ amount = (int8_t) * (p + 2);
+ return true;
+ }
+ // 32-bit immediate operand
+ if (*p == 0x81 && *(p + 1) == 0xec) {
+ amount = (int32_t)extract_4(p + 2);
+ return true;
+ }
+ return false;
+}
+
+// addq $0x20, %rsp
+bool x86AssemblyInspectionEngine::add_rsp_pattern_p(int &amount) {
+ uint8_t *p = m_cur_insn;
+ if (m_wordsize == 8 && *p == 0x48)
+ p++;
+ // 8-bit immediate operand
+ if (*p == 0x83 && *(p + 1) == 0xc4) {
+ amount = (int8_t) * (p + 2);
+ return true;
+ }
+ // 32-bit immediate operand
+ if (*p == 0x81 && *(p + 1) == 0xc4) {
+ amount = (int32_t)extract_4(p + 2);
+ return true;
+ }
+ return false;
+}
+
+// lea esp, [esp - 0x28]
+// lea esp, [esp + 0x28]
+bool x86AssemblyInspectionEngine::lea_rsp_pattern_p(int &amount) {
+ uint8_t *p = m_cur_insn;
+ if (m_wordsize == 8 && *p == 0x48)
+ p++;
+
+ // Check opcode
+ if (*p != 0x8d)
+ return false;
+
+ // 8 bit displacement
+ if (*(p + 1) == 0x64 && (*(p + 2) & 0x3f) == 0x24) {
+ amount = (int8_t) * (p + 3);
+ return true;
+ }
+
+ // 32 bit displacement
+ if (*(p + 1) == 0xa4 && (*(p + 2) & 0x3f) == 0x24) {
+ amount = (int32_t)extract_4(p + 3);
+ return true;
+ }
+
+ return false;
+}
+
+// lea -0x28(%ebp), %esp
+// (32-bit and 64-bit variants, 8-bit and 32-bit displacement)
+bool x86AssemblyInspectionEngine::lea_rbp_rsp_pattern_p(int &amount) {
+ uint8_t *p = m_cur_insn;
+ if (m_wordsize == 8 && *p == 0x48)
+ p++;
+
+ // Check opcode
+ if (*p != 0x8d)
+ return false;
+ ++p;
+
+ // 8 bit displacement
+ if (*p == 0x65) {
+ amount = (int8_t)p[1];
+ return true;
+ }
+
+ // 32 bit displacement
+ if (*p == 0xa5) {
+ amount = (int32_t)extract_4(p + 1);
+ return true;
+ }
+
+ return false;
+}
+
+// lea -0x28(%ebx), %esp
+// (32-bit and 64-bit variants, 8-bit and 32-bit displacement)
+bool x86AssemblyInspectionEngine::lea_rbx_rsp_pattern_p(int &amount) {
+ uint8_t *p = m_cur_insn;
+ if (m_wordsize == 8 && *p == 0x48)
+ p++;
+
+ // Check opcode
+ if (*p != 0x8d)
+ return false;
+ ++p;
+
+ // 8 bit displacement
+ if (*p == 0x63) {
+ amount = (int8_t)p[1];
+ return true;
+ }
+
+ // 32 bit displacement
+ if (*p == 0xa3) {
+ amount = (int32_t)extract_4(p + 1);
+ return true;
+ }
+
+ return false;
+}
+
+// and -0xfffffff0, %esp
+// (32-bit and 64-bit variants, 8-bit and 32-bit displacement)
+bool x86AssemblyInspectionEngine::and_rsp_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ if (m_wordsize == 8 && *p == 0x48)
+ p++;
+
+ if (*p != 0x81 && *p != 0x83)
+ return false;
+
+ return *++p == 0xe4;
+}
+
+// popq %rbx
+// popl %ebx
+bool x86AssemblyInspectionEngine::pop_reg_p(int &regno) {
+ uint8_t *p = m_cur_insn;
+ int regno_prefix_bit = 0;
+ // If we have a rex prefix byte, check to see if a B bit is set
+ if (m_wordsize == 8 && (*p & 0xfe) == 0x40) {
+ regno_prefix_bit = (*p & 1) << 3;
+ p++;
+ }
+ if (*p >= 0x58 && *p <= 0x5f) {
+ regno = (*p - 0x58) | regno_prefix_bit;
+ return true;
+ }
+ return false;
+}
+
+// popq %rbp [0x5d]
+// popl %ebp [0x5d]
+bool x86AssemblyInspectionEngine::pop_rbp_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ return (*p == 0x5d);
+}
+
+// instructions valid only in 32-bit mode:
+// 0x1f - pop ds
+// 0x07 - pop es
+// 0x17 - pop ss
+bool x86AssemblyInspectionEngine::pop_misc_reg_p() {
+ uint8_t p = *m_cur_insn;
+ if (m_wordsize == 4) {
+ if (p == 0x1f || p == 0x07 || p == 0x17)
+ return true;
+ }
+ return false;
+}
+
+// leave [0xc9]
+bool x86AssemblyInspectionEngine::leave_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ return (*p == 0xc9);
+}
+
+// call $0 [0xe8 0x0 0x0 0x0 0x0]
+bool x86AssemblyInspectionEngine::call_next_insn_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ return (*p == 0xe8) && (*(p + 1) == 0x0) && (*(p + 2) == 0x0) &&
+ (*(p + 3) == 0x0) && (*(p + 4) == 0x0);
+}
+
+// Look for an instruction sequence storing a nonvolatile register on to the
+// stack frame.
+
+// movq %rax, -0x10(%rbp) [0x48 0x89 0x45 0xf0]
+// movl %eax, -0xc(%ebp) [0x89 0x45 0xf4]
+
+// The offset value returned in rbp_offset will be positive -- but it must be
+// subtraced from the frame base register to get the actual location. The
+// positive value returned for the offset is a convention used elsewhere for
+// CFA offsets et al.
+
+bool x86AssemblyInspectionEngine::mov_reg_to_local_stack_frame_p(
+ int &regno, int &rbp_offset) {
+ uint8_t *p = m_cur_insn;
+ int src_reg_prefix_bit = 0;
+ int target_reg_prefix_bit = 0;
+
+ if (m_wordsize == 8 && REX_W_PREFIX_P(*p)) {
+ src_reg_prefix_bit = REX_W_SRCREG(*p) << 3;
+ target_reg_prefix_bit = REX_W_DSTREG(*p) << 3;
+ if (target_reg_prefix_bit == 1) {
+ // rbp/ebp don't need a prefix bit - we know this isn't the reg we care
+ // about.
+ return false;
+ }
+ p++;
+ }
+
+ if (*p == 0x89) {
+ /* Mask off the 3-5 bits which indicate the destination register
+ if this is a ModR/M byte. */
+ int opcode_destreg_masked_out = *(p + 1) & (~0x38);
+
+ /* Is this a ModR/M byte with Mod bits 01 and R/M bits 101
+ and three bits between them, e.g. 01nnn101
+ We're looking for a destination of ebp-disp8 or ebp-disp32. */
+ int immsize;
+ if (opcode_destreg_masked_out == 0x45)
+ immsize = 2;
+ else if (opcode_destreg_masked_out == 0x85)
+ immsize = 4;
+ else
+ return false;
+
+ int offset = 0;
+ if (immsize == 2)
+ offset = (int8_t) * (p + 2);
+ if (immsize == 4)
+ offset = (uint32_t)extract_4(p + 2);
+ if (offset > 0)
+ return false;
+
+ regno = ((*(p + 1) >> 3) & 0x7) | src_reg_prefix_bit;
+ rbp_offset = offset > 0 ? offset : -offset;
+ return true;
+ }
+ return false;
+}
+
+// Returns true if this is a jmp instruction where we can't
+// know the destination address statically.
+//
+// ff e0 jmpq *%rax
+// ff e1 jmpq *%rcx
+// ff 60 28 jmpq *0x28(%rax)
+// ff 60 60 jmpq *0x60(%rax)
+bool x86AssemblyInspectionEngine::jmp_to_reg_p() {
+ if (*m_cur_insn != 0xff)
+ return false;
+
+ // The second byte is a ModR/M /4 byte, strip off the registers
+ uint8_t second_byte_sans_reg = *(m_cur_insn + 1) & ~7;
+
+ // Don't handle 0x24 disp32, because the target address is
+ // knowable statically - pc_rel_branch_or_jump_p() will
+ // return the target address.
+
+ // [reg]
+ if (second_byte_sans_reg == 0x20)
+ return true;
+
+ // [reg]+disp8
+ if (second_byte_sans_reg == 0x60)
+ return true;
+
+ // [reg]+disp32
+ if (second_byte_sans_reg == 0xa0)
+ return true;
+
+ // reg
+ if (second_byte_sans_reg == 0xe0)
+ return true;
+
+ // disp32
+ // jumps to an address stored in memory, the value can't be cached
+ // in an unwind plan.
+ if (second_byte_sans_reg == 0x24)
+ return true;
+
+ // use SIB byte
+ // ff 24 fe jmpq *(%rsi,%rdi,8)
+ if (second_byte_sans_reg == 0x24)
+ return true;
+
+ return false;
+}
+
+// Detect branches to fixed pc-relative offsets.
+// Returns the offset from the address of the next instruction
+// that may be branch/jumped to.
+//
+// Cannot determine the offset of a JMP that jumps to the address in
+// a register ("jmpq *%rax") or offset from a register value
+// ("jmpq *0x28(%rax)"), this method will return false on those
+// instructions.
+//
+// These instructions all end in either a relative 8/16/32 bit value
+// depending on the instruction and the current execution mode of the
+// inferior process. Once we know the size of the opcode instruction,
+// we can use the total instruction length to determine the size of
+// the relative offset without having to compute it correctly.
+
+bool x86AssemblyInspectionEngine::pc_rel_branch_or_jump_p (
+ const int instruction_length, int &offset)
+{
+ int opcode_size = 0;
+
+ uint8_t b1 = m_cur_insn[0];
+
+ switch (b1) {
+ case 0x77: // JA/JNBE rel8
+ case 0x73: // JAE/JNB/JNC rel8
+ case 0x72: // JB/JC/JNAE rel8
+ case 0x76: // JBE/JNA rel8
+ case 0xe3: // JCXZ/JECXZ/JRCXZ rel8
+ case 0x74: // JE/JZ rel8
+ case 0x7f: // JG/JNLE rel8
+ case 0x7d: // JGE/JNL rel8
+ case 0x7c: // JL/JNGE rel8
+ case 0x7e: // JNG/JLE rel8
+ case 0x71: // JNO rel8
+ case 0x7b: // JNP/JPO rel8
+ case 0x79: // JNS rel8
+ case 0x75: // JNE/JNZ rel8
+ case 0x70: // JO rel8
+ case 0x7a: // JP/JPE rel8
+ case 0x78: // JS rel8
+ case 0xeb: // JMP rel8
+ case 0xe9: // JMP rel16/rel32
+ opcode_size = 1;
+ break;
+ default:
+ break;
+ }
+ if (b1 == 0x0f && opcode_size == 0) {
+ uint8_t b2 = m_cur_insn[1];
+ switch (b2) {
+ case 0x87: // JA/JNBE rel16/rel32
+ case 0x86: // JBE/JNA rel16/rel32
+ case 0x84: // JE/JZ rel16/rel32
+ case 0x8f: // JG/JNLE rel16/rel32
+ case 0x8d: // JNL/JGE rel16/rel32
+ case 0x8e: // JLE rel16/rel32
+ case 0x82: // JB/JC/JNAE rel16/rel32
+ case 0x83: // JAE/JNB/JNC rel16/rel32
+ case 0x85: // JNE/JNZ rel16/rel32
+ case 0x8c: // JL/JNGE rel16/rel32
+ case 0x81: // JNO rel16/rel32
+ case 0x8b: // JNP/JPO rel16/rel32
+ case 0x89: // JNS rel16/rel32
+ case 0x80: // JO rel16/rel32
+ case 0x8a: // JP rel16/rel32
+ case 0x88: // JS rel16/rel32
+ opcode_size = 2;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (opcode_size == 0)
+ return false;
+
+ offset = 0;
+ if (instruction_length - opcode_size == 1) {
+ int8_t rel8 = (int8_t) *(m_cur_insn + opcode_size);
+ offset = rel8;
+ } else if (instruction_length - opcode_size == 2) {
+ int16_t rel16 = extract_2_signed (m_cur_insn + opcode_size);
+ offset = rel16;
+ } else if (instruction_length - opcode_size == 4) {
+ int32_t rel32 = extract_4_signed (m_cur_insn + opcode_size);
+ offset = rel32;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+// Returns true if this instruction is a intra-function branch or jump -
+// a branch/jump within the bounds of this same function.
+// Cannot predict where a jump through a register value ("jmpq *%rax")
+// will go, so it will return false on that instruction.
+bool x86AssemblyInspectionEngine::local_branch_p (
+ const addr_t current_func_text_offset,
+ const AddressRange &func_range,
+ const int instruction_length,
+ addr_t &target_insn_offset) {
+ int offset;
+ if (pc_rel_branch_or_jump_p (instruction_length, offset) && offset != 0) {
+ addr_t next_pc_value = current_func_text_offset + instruction_length;
+ if (offset < 0 && addr_t(-offset) > current_func_text_offset) {
+ // Branch target is before the start of this function
+ return false;
+ }
+ if (offset + next_pc_value > func_range.GetByteSize()) {
+ // Branch targets outside this function's bounds
+ return false;
+ }
+ // This instruction branches to target_insn_offset (byte offset into the function)
+ target_insn_offset = next_pc_value + offset;
+ return true;
+ }
+ return false;
+}
+
+// Returns true if this instruction is a inter-function branch or jump - a
+// branch/jump to another function.
+// Cannot predict where a jump through a register value ("jmpq *%rax")
+// will go, so it will return false on that instruction.
+bool x86AssemblyInspectionEngine::non_local_branch_p (
+ const addr_t current_func_text_offset,
+ const AddressRange &func_range,
+ const int instruction_length) {
+ int offset;
+ addr_t target_insn_offset;
+ if (pc_rel_branch_or_jump_p (instruction_length, offset)) {
+ return !local_branch_p(current_func_text_offset,func_range,instruction_length,target_insn_offset);
+ }
+ return false;
+}
+
+// ret [0xc3] or [0xcb] or [0xc2 imm16] or [0xca imm16]
+bool x86AssemblyInspectionEngine::ret_pattern_p() {
+ uint8_t *p = m_cur_insn;
+ return *p == 0xc3 || *p == 0xc2 || *p == 0xca || *p == 0xcb;
+}
+
+uint16_t x86AssemblyInspectionEngine::extract_2(uint8_t *b) {
+ uint16_t v = 0;
+ for (int i = 1; i >= 0; i--)
+ v = (v << 8) | b[i];
+ return v;
+}
+
+int16_t x86AssemblyInspectionEngine::extract_2_signed(uint8_t *b) {
+ int16_t v = 0;
+ for (int i = 1; i >= 0; i--)
+ v = (v << 8) | b[i];
+ return v;
+}
+
+uint32_t x86AssemblyInspectionEngine::extract_4(uint8_t *b) {
+ uint32_t v = 0;
+ for (int i = 3; i >= 0; i--)
+ v = (v << 8) | b[i];
+ return v;
+}
+
+int32_t x86AssemblyInspectionEngine::extract_4_signed(uint8_t *b) {
+ int32_t v = 0;
+ for (int i = 3; i >= 0; i--)
+ v = (v << 8) | b[i];
+ return v;
+}
+
+
+bool x86AssemblyInspectionEngine::instruction_length(uint8_t *insn_p,
+ int &length,
+ uint32_t buffer_remaining_bytes) {
+
+ uint32_t max_op_byte_size = std::min(buffer_remaining_bytes, m_arch.GetMaximumOpcodeByteSize());
+ llvm::SmallVector<uint8_t, 32> opcode_data;
+ opcode_data.resize(max_op_byte_size);
+
+ char out_string[512];
+ const size_t inst_size =
+ ::LLVMDisasmInstruction(m_disasm_context, insn_p, max_op_byte_size, 0,
+ out_string, sizeof(out_string));
+
+ length = inst_size;
+ return true;
+}
+
+bool x86AssemblyInspectionEngine::machine_regno_to_lldb_regno(
+ int machine_regno, uint32_t &lldb_regno) {
+ MachineRegnumToNameAndLLDBRegnum::iterator it = m_reg_map.find(machine_regno);
+ if (it != m_reg_map.end()) {
+ lldb_regno = it->second.lldb_regnum;
+ return true;
+ }
+ return false;
+}
+
+bool x86AssemblyInspectionEngine::GetNonCallSiteUnwindPlanFromAssembly(
+ uint8_t *data, size_t size, AddressRange &func_range,
+ UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+
+ if (data == nullptr || size == 0)
+ return false;
+
+ if (!m_register_map_initialized)
+ return false;
+
+ addr_t current_func_text_offset = 0;
+ int current_sp_bytes_offset_from_fa = 0;
+ bool is_aligned = false;
+ UnwindPlan::Row::RegisterLocation initial_regloc;
+ UnwindPlan::RowSP row(new UnwindPlan::Row);
+
+ unwind_plan.SetPlanValidAddressRange(func_range);
+ unwind_plan.SetRegisterKind(eRegisterKindLLDB);
+
+ // At the start of the function, find the CFA by adding wordsize to the SP
+ // register
+ row->SetOffset(current_func_text_offset);
+ row->GetCFAValue().SetIsRegisterPlusOffset(m_lldb_sp_regnum, m_wordsize);
+
+ // caller's stack pointer value before the call insn is the CFA address
+ initial_regloc.SetIsCFAPlusOffset(0);
+ row->SetRegisterInfo(m_lldb_sp_regnum, initial_regloc);
+
+ // saved instruction pointer can be found at CFA - wordsize.
+ current_sp_bytes_offset_from_fa = m_wordsize;
+ initial_regloc.SetAtCFAPlusOffset(-current_sp_bytes_offset_from_fa);
+ row->SetRegisterInfo(m_lldb_ip_regnum, initial_regloc);
+
+ unwind_plan.AppendRow(row);
+
+ // Allocate a new Row, populate it with the existing Row contents.
+ UnwindPlan::Row *newrow = new UnwindPlan::Row;
+ *newrow = *row.get();
+ row.reset(newrow);
+
+ // Track which registers have been saved so far in the prologue. If we see
+ // another push of that register, it's not part of the prologue. The register
+ // numbers used here are the machine register #'s (i386_register_numbers,
+ // x86_64_register_numbers).
+ std::vector<bool> saved_registers(32, false);
+
+ // Once the prologue has completed we'll save a copy of the unwind
+ // instructions If there is an epilogue in the middle of the function, after
+ // that epilogue we'll reinstate the unwind setup -- we assume that some code
+ // path jumps over the mid-function epilogue
+
+ UnwindPlan::RowSP prologue_completed_row; // copy of prologue row of CFI
+ int prologue_completed_sp_bytes_offset_from_cfa; // The sp value before the
+ // epilogue started executed
+ bool prologue_completed_is_aligned;
+ std::vector<bool> prologue_completed_saved_registers;
+
+ while (current_func_text_offset < size) {
+ int stack_offset, insn_len;
+ int machine_regno; // register numbers masked directly out of instructions
+ uint32_t lldb_regno; // register numbers in lldb's eRegisterKindLLDB
+ // numbering scheme
+
+ bool in_epilogue = false; // we're in the middle of an epilogue sequence
+ bool row_updated = false; // The UnwindPlan::Row 'row' has been updated
+
+ m_cur_insn = data + current_func_text_offset;
+ if (!instruction_length(m_cur_insn, insn_len, size - current_func_text_offset)
+ || insn_len == 0
+ || insn_len > kMaxInstructionByteSize) {
+ // An unrecognized/junk instruction
+ break;
+ }
+
+ auto &cfa_value = row->GetCFAValue();
+ auto &afa_value = row->GetAFAValue();
+ auto fa_value_ptr = is_aligned ? &afa_value : &cfa_value;
+
+ if (mov_rsp_rbp_pattern_p()) {
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) {
+ fa_value_ptr->SetIsRegisterPlusOffset(
+ m_lldb_fp_regnum, fa_value_ptr->GetOffset());
+ row_updated = true;
+ }
+ }
+
+ else if (mov_rsp_rbx_pattern_p()) {
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) {
+ fa_value_ptr->SetIsRegisterPlusOffset(
+ m_lldb_alt_fp_regnum, fa_value_ptr->GetOffset());
+ row_updated = true;
+ }
+ }
+
+ else if (and_rsp_pattern_p()) {
+ current_sp_bytes_offset_from_fa = 0;
+ afa_value.SetIsRegisterPlusOffset(
+ m_lldb_sp_regnum, current_sp_bytes_offset_from_fa);
+ fa_value_ptr = &afa_value;
+ is_aligned = true;
+ row_updated = true;
+ }
+
+ else if (mov_rbp_rsp_pattern_p()) {
+ if (is_aligned && cfa_value.GetRegisterNumber() == m_lldb_fp_regnum)
+ {
+ is_aligned = false;
+ fa_value_ptr = &cfa_value;
+ afa_value.SetUnspecified();
+ row_updated = true;
+ }
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_fp_regnum)
+ current_sp_bytes_offset_from_fa = fa_value_ptr->GetOffset();
+ }
+
+ else if (mov_rbx_rsp_pattern_p()) {
+ if (is_aligned && cfa_value.GetRegisterNumber() == m_lldb_alt_fp_regnum)
+ {
+ is_aligned = false;
+ fa_value_ptr = &cfa_value;
+ afa_value.SetUnspecified();
+ row_updated = true;
+ }
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_alt_fp_regnum)
+ current_sp_bytes_offset_from_fa = fa_value_ptr->GetOffset();
+ }
+
+ // This is the start() function (or a pthread equivalent), it starts with a
+ // pushl $0x0 which puts the saved pc value of 0 on the stack. In this
+ // case we want to pretend we didn't see a stack movement at all --
+ // normally the saved pc value is already on the stack by the time the
+ // function starts executing.
+ else if (push_0_pattern_p()) {
+ }
+
+ else if (push_reg_p(machine_regno)) {
+ current_sp_bytes_offset_from_fa += m_wordsize;
+ // the PUSH instruction has moved the stack pointer - if the FA is set
+ // in terms of the stack pointer, we need to add a new row of
+ // instructions.
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) {
+ fa_value_ptr->SetOffset(current_sp_bytes_offset_from_fa);
+ row_updated = true;
+ }
+ // record where non-volatile (callee-saved, spilled) registers are saved
+ // on the stack
+ if (nonvolatile_reg_p(machine_regno) &&
+ machine_regno_to_lldb_regno(machine_regno, lldb_regno) &&
+ !saved_registers[machine_regno]) {
+ UnwindPlan::Row::RegisterLocation regloc;
+ if (is_aligned)
+ regloc.SetAtAFAPlusOffset(-current_sp_bytes_offset_from_fa);
+ else
+ regloc.SetAtCFAPlusOffset(-current_sp_bytes_offset_from_fa);
+ row->SetRegisterInfo(lldb_regno, regloc);
+ saved_registers[machine_regno] = true;
+ row_updated = true;
+ }
+ }
+
+ else if (pop_reg_p(machine_regno)) {
+ current_sp_bytes_offset_from_fa -= m_wordsize;
+
+ if (nonvolatile_reg_p(machine_regno) &&
+ machine_regno_to_lldb_regno(machine_regno, lldb_regno) &&
+ saved_registers[machine_regno]) {
+ saved_registers[machine_regno] = false;
+ row->RemoveRegisterInfo(lldb_regno);
+
+ if (lldb_regno == fa_value_ptr->GetRegisterNumber()) {
+ fa_value_ptr->SetIsRegisterPlusOffset(
+ m_lldb_sp_regnum, fa_value_ptr->GetOffset());
+ }
+
+ in_epilogue = true;
+ row_updated = true;
+ }
+
+ // the POP instruction has moved the stack pointer - if the FA is set in
+ // terms of the stack pointer, we need to add a new row of instructions.
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) {
+ fa_value_ptr->SetIsRegisterPlusOffset(
+ m_lldb_sp_regnum, current_sp_bytes_offset_from_fa);
+ row_updated = true;
+ }
+ }
+
+ else if (pop_misc_reg_p()) {
+ current_sp_bytes_offset_from_fa -= m_wordsize;
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) {
+ fa_value_ptr->SetIsRegisterPlusOffset(
+ m_lldb_sp_regnum, current_sp_bytes_offset_from_fa);
+ row_updated = true;
+ }
+ }
+
+ // The LEAVE instruction moves the value from rbp into rsp and pops a value
+ // off the stack into rbp (restoring the caller's rbp value). It is the
+ // opposite of ENTER, or 'push rbp, mov rsp rbp'.
+ else if (leave_pattern_p()) {
+ if (saved_registers[m_machine_fp_regnum]) {
+ saved_registers[m_machine_fp_regnum] = false;
+ row->RemoveRegisterInfo(m_lldb_fp_regnum);
+
+ row_updated = true;
+ }
+
+ if (is_aligned && cfa_value.GetRegisterNumber() == m_lldb_fp_regnum)
+ {
+ is_aligned = false;
+ fa_value_ptr = &cfa_value;
+ afa_value.SetUnspecified();
+ row_updated = true;
+ }
+
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_fp_regnum)
+ {
+ fa_value_ptr->SetIsRegisterPlusOffset(
+ m_lldb_sp_regnum, fa_value_ptr->GetOffset());
+
+ current_sp_bytes_offset_from_fa = fa_value_ptr->GetOffset();
+ }
+
+ current_sp_bytes_offset_from_fa -= m_wordsize;
+
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) {
+ fa_value_ptr->SetIsRegisterPlusOffset(
+ m_lldb_sp_regnum, current_sp_bytes_offset_from_fa);
+ row_updated = true;
+ }
+
+ in_epilogue = true;
+ }
+
+ else if (mov_reg_to_local_stack_frame_p(machine_regno, stack_offset) &&
+ nonvolatile_reg_p(machine_regno) &&
+ machine_regno_to_lldb_regno(machine_regno, lldb_regno) &&
+ !saved_registers[machine_regno]) {
+ saved_registers[machine_regno] = true;
+
+ UnwindPlan::Row::RegisterLocation regloc;
+
+ // stack_offset for 'movq %r15, -80(%rbp)' will be 80. In the Row, we
+ // want to express this as the offset from the FA. If the frame base is
+ // rbp (like the above instruction), the FA offset for rbp is probably
+ // 16. So we want to say that the value is stored at the FA address -
+ // 96.
+ if (is_aligned)
+ regloc.SetAtAFAPlusOffset(-(stack_offset + fa_value_ptr->GetOffset()));
+ else
+ regloc.SetAtCFAPlusOffset(-(stack_offset + fa_value_ptr->GetOffset()));
+
+ row->SetRegisterInfo(lldb_regno, regloc);
+
+ row_updated = true;
+ }
+
+ else if (sub_rsp_pattern_p(stack_offset)) {
+ current_sp_bytes_offset_from_fa += stack_offset;
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) {
+ fa_value_ptr->SetOffset(current_sp_bytes_offset_from_fa);
+ row_updated = true;
+ }
+ }
+
+ else if (add_rsp_pattern_p(stack_offset)) {
+ current_sp_bytes_offset_from_fa -= stack_offset;
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) {
+ fa_value_ptr->SetOffset(current_sp_bytes_offset_from_fa);
+ row_updated = true;
+ }
+ in_epilogue = true;
+ }
+
+ else if (push_extended_pattern_p() || push_imm_pattern_p() ||
+ push_misc_reg_p()) {
+ current_sp_bytes_offset_from_fa += m_wordsize;
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) {
+ fa_value_ptr->SetOffset(current_sp_bytes_offset_from_fa);
+ row_updated = true;
+ }
+ }
+
+ else if (lea_rsp_pattern_p(stack_offset)) {
+ current_sp_bytes_offset_from_fa -= stack_offset;
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) {
+ fa_value_ptr->SetOffset(current_sp_bytes_offset_from_fa);
+ row_updated = true;
+ }
+ if (stack_offset > 0)
+ in_epilogue = true;
+ }
+
+ else if (lea_rbp_rsp_pattern_p(stack_offset)) {
+ if (is_aligned &&
+ cfa_value.GetRegisterNumber() == m_lldb_fp_regnum) {
+ is_aligned = false;
+ fa_value_ptr = &cfa_value;
+ afa_value.SetUnspecified();
+ row_updated = true;
+ }
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_fp_regnum) {
+ current_sp_bytes_offset_from_fa =
+ fa_value_ptr->GetOffset() - stack_offset;
+ }
+ }
+
+ else if (lea_rbx_rsp_pattern_p(stack_offset)) {
+ if (is_aligned &&
+ cfa_value.GetRegisterNumber() == m_lldb_alt_fp_regnum) {
+ is_aligned = false;
+ fa_value_ptr = &cfa_value;
+ afa_value.SetUnspecified();
+ row_updated = true;
+ }
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_alt_fp_regnum) {
+ current_sp_bytes_offset_from_fa = fa_value_ptr->GetOffset() - stack_offset;
+ }
+ }
+
+ else if (prologue_completed_row.get() &&
+ (ret_pattern_p() ||
+ non_local_branch_p (current_func_text_offset, func_range, insn_len) ||
+ jmp_to_reg_p())) {
+ // Check if the current instruction is the end of an epilogue sequence,
+ // and if so, re-instate the prologue-completed unwind state.
+
+ // The current instruction is a branch/jump outside this function,
+ // a ret, or a jump through a register value which we cannot
+ // determine the effcts of. Verify that the stack frame state
+ // has been unwound to the same as it was at function entry to avoid
+ // mis-identifying a JMP instruction as an epilogue.
+ UnwindPlan::Row::RegisterLocation sp, pc;
+ if (row->GetRegisterInfo(m_lldb_sp_regnum, sp) &&
+ row->GetRegisterInfo(m_lldb_ip_regnum, pc)) {
+ // Any ret instruction variant is definitely indicative of an
+ // epilogue; for other insn patterns verify that we're back to
+ // the original unwind state.
+ if (ret_pattern_p() ||
+ (sp.IsCFAPlusOffset() && sp.GetOffset() == 0 &&
+ pc.IsAtCFAPlusOffset() && pc.GetOffset() == -m_wordsize)) {
+ // Reinstate the saved prologue setup for any instructions that come
+ // after the epilogue
+
+ UnwindPlan::Row *newrow = new UnwindPlan::Row;
+ *newrow = *prologue_completed_row.get();
+ row.reset(newrow);
+ current_sp_bytes_offset_from_fa =
+ prologue_completed_sp_bytes_offset_from_cfa;
+ is_aligned = prologue_completed_is_aligned;
+
+ saved_registers.clear();
+ saved_registers.resize(prologue_completed_saved_registers.size(), false);
+ for (size_t i = 0; i < prologue_completed_saved_registers.size(); ++i) {
+ saved_registers[i] = prologue_completed_saved_registers[i];
+ }
+
+ in_epilogue = true;
+ row_updated = true;
+ }
+ }
+ }
+
+ // call next instruction
+ // call 0
+ // => pop %ebx
+ // This is used in i386 programs to get the PIC base address for finding
+ // global data
+ else if (call_next_insn_pattern_p()) {
+ current_sp_bytes_offset_from_fa += m_wordsize;
+ if (fa_value_ptr->GetRegisterNumber() == m_lldb_sp_regnum) {
+ fa_value_ptr->SetOffset(current_sp_bytes_offset_from_fa);
+ row_updated = true;
+ }
+ }
+
+ if (row_updated) {
+ if (current_func_text_offset + insn_len < size) {
+ row->SetOffset(current_func_text_offset + insn_len);
+ unwind_plan.AppendRow(row);
+ // Allocate a new Row, populate it with the existing Row contents.
+ newrow = new UnwindPlan::Row;
+ *newrow = *row.get();
+ row.reset(newrow);
+ }
+ }
+
+ if (!in_epilogue && row_updated) {
+ // If we're not in an epilogue sequence, save the updated Row
+ UnwindPlan::Row *newrow = new UnwindPlan::Row;
+ *newrow = *row.get();
+ prologue_completed_row.reset(newrow);
+
+ prologue_completed_saved_registers.clear();
+ prologue_completed_saved_registers.resize(saved_registers.size(), false);
+ for (size_t i = 0; i < saved_registers.size(); ++i) {
+ prologue_completed_saved_registers[i] = saved_registers[i];
+ }
+ }
+
+ // We may change the sp value without adding a new Row necessarily -- keep
+ // track of it either way.
+ if (!in_epilogue) {
+ prologue_completed_sp_bytes_offset_from_cfa =
+ current_sp_bytes_offset_from_fa;
+ prologue_completed_is_aligned = is_aligned;
+ }
+
+ m_cur_insn = m_cur_insn + insn_len;
+ current_func_text_offset += insn_len;
+ }
+
+ unwind_plan.SetSourceName("assembly insn profiling");
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);
+
+ return true;
+}
+
+bool x86AssemblyInspectionEngine::AugmentUnwindPlanFromCallSite(
+ uint8_t *data, size_t size, AddressRange &func_range,
+ UnwindPlan &unwind_plan, RegisterContextSP &reg_ctx) {
+ Address addr_start = func_range.GetBaseAddress();
+ if (!addr_start.IsValid())
+ return false;
+
+ // We either need a live RegisterContext, or we need the UnwindPlan to
+ // already be in the lldb register numbering scheme.
+ if (reg_ctx.get() == nullptr &&
+ unwind_plan.GetRegisterKind() != eRegisterKindLLDB)
+ return false;
+
+ // Is original unwind_plan valid?
+ // unwind_plan should have at least one row which is ABI-default (CFA
+ // register is sp), and another row in mid-function.
+ if (unwind_plan.GetRowCount() < 2)
+ return false;
+
+ UnwindPlan::RowSP first_row = unwind_plan.GetRowAtIndex(0);
+ if (first_row->GetOffset() != 0)
+ return false;
+ uint32_t cfa_reg = first_row->GetCFAValue().GetRegisterNumber();
+ if (unwind_plan.GetRegisterKind() != eRegisterKindLLDB) {
+ cfa_reg = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ unwind_plan.GetRegisterKind(),
+ first_row->GetCFAValue().GetRegisterNumber());
+ }
+ if (cfa_reg != m_lldb_sp_regnum ||
+ first_row->GetCFAValue().GetOffset() != m_wordsize)
+ return false;
+
+ UnwindPlan::RowSP original_last_row = unwind_plan.GetRowForFunctionOffset(-1);
+
+ size_t offset = 0;
+ int row_id = 1;
+ bool unwind_plan_updated = false;
+ UnwindPlan::RowSP row(new UnwindPlan::Row(*first_row));
+ m_cur_insn = data + offset;
+
+ // After a mid-function epilogue we will need to re-insert the original
+ // unwind rules so unwinds work for the remainder of the function. These
+ // aren't common with clang/gcc on x86 but it is possible.
+ bool reinstate_unwind_state = false;
+
+ while (offset < size) {
+ m_cur_insn = data + offset;
+ int insn_len;
+ if (!instruction_length(m_cur_insn, insn_len, size - offset)
+ || insn_len == 0
+ || insn_len > kMaxInstructionByteSize) {
+ // An unrecognized/junk instruction.
+ break;
+ }
+
+ // Advance offsets.
+ offset += insn_len;
+ m_cur_insn = data + offset;
+
+ // offset is pointing beyond the bounds of the function; stop looping.
+ if (offset >= size)
+ continue;
+
+ if (reinstate_unwind_state) {
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row());
+ *new_row = *original_last_row;
+ new_row->SetOffset(offset);
+ unwind_plan.AppendRow(new_row);
+ row = std::make_shared<UnwindPlan::Row>();
+ *row = *new_row;
+ reinstate_unwind_state = false;
+ unwind_plan_updated = true;
+ continue;
+ }
+
+ // If we already have one row for this instruction, we can continue.
+ while (row_id < unwind_plan.GetRowCount() &&
+ unwind_plan.GetRowAtIndex(row_id)->GetOffset() <= offset) {
+ row_id++;
+ }
+ UnwindPlan::RowSP original_row = unwind_plan.GetRowAtIndex(row_id - 1);
+ if (original_row->GetOffset() == offset) {
+ *row = *original_row;
+ continue;
+ }
+
+ if (row_id == 0) {
+ // If we are here, compiler didn't generate CFI for prologue. This won't
+ // happen to GCC or clang. In this case, bail out directly.
+ return false;
+ }
+
+ // Inspect the instruction to check if we need a new row for it.
+ cfa_reg = row->GetCFAValue().GetRegisterNumber();
+ if (unwind_plan.GetRegisterKind() != eRegisterKindLLDB) {
+ cfa_reg = reg_ctx->ConvertRegisterKindToRegisterNumber(
+ unwind_plan.GetRegisterKind(),
+ row->GetCFAValue().GetRegisterNumber());
+ }
+ if (cfa_reg == m_lldb_sp_regnum) {
+ // CFA register is sp.
+
+ // call next instruction
+ // call 0
+ // => pop %ebx
+ if (call_next_insn_pattern_p()) {
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(m_wordsize);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+
+ // push/pop register
+ int regno;
+ if (push_reg_p(regno)) {
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(m_wordsize);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+ if (pop_reg_p(regno)) {
+ // Technically, this might be a nonvolatile register recover in
+ // epilogue. We should reset RegisterInfo for the register. But in
+ // practice, previous rule for the register is still valid... So we
+ // ignore this case.
+
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(-m_wordsize);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+
+ if (pop_misc_reg_p()) {
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(-m_wordsize);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+
+ // push imm
+ if (push_imm_pattern_p()) {
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(m_wordsize);
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+
+ // push extended
+ if (push_extended_pattern_p() || push_misc_reg_p()) {
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(m_wordsize);
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+
+ // add/sub %rsp/%esp
+ int amount;
+ if (add_rsp_pattern_p(amount)) {
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(-amount);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+ if (sub_rsp_pattern_p(amount)) {
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(amount);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+
+ // lea %rsp, [%rsp + $offset]
+ if (lea_rsp_pattern_p(amount)) {
+ row->SetOffset(offset);
+ row->GetCFAValue().IncOffset(-amount);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ continue;
+ }
+
+ if (ret_pattern_p()) {
+ reinstate_unwind_state = true;
+ continue;
+ }
+ } else if (cfa_reg == m_lldb_fp_regnum) {
+ // CFA register is fp.
+
+ // The only case we care about is epilogue:
+ // [0x5d] pop %rbp/%ebp
+ // => [0xc3] ret
+ if (pop_rbp_pattern_p() || leave_pattern_p()) {
+ offset += 1;
+ row->SetOffset(offset);
+ row->GetCFAValue().SetIsRegisterPlusOffset(
+ first_row->GetCFAValue().GetRegisterNumber(), m_wordsize);
+
+ UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row));
+ unwind_plan.InsertRow(new_row);
+ unwind_plan_updated = true;
+ reinstate_unwind_state = true;
+ continue;
+ }
+ } else {
+ // CFA register is not sp or fp.
+
+ // This must be hand-written assembly.
+ // Just trust eh_frame and assume we have finished.
+ break;
+ }
+ }
+
+ unwind_plan.SetPlanValidAddressRange(func_range);
+ if (unwind_plan_updated) {
+ std::string unwind_plan_source(unwind_plan.GetSourceName().AsCString());
+ unwind_plan_source += " plus augmentation from assembly parsing";
+ unwind_plan.SetSourceName(unwind_plan_source.c_str());
+ unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
+ unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);
+ }
+ return true;
+}
+
+bool x86AssemblyInspectionEngine::FindFirstNonPrologueInstruction(
+ uint8_t *data, size_t size, size_t &offset) {
+ offset = 0;
+
+ if (!m_register_map_initialized)
+ return false;
+
+ while (offset < size) {
+ int regno;
+ int insn_len;
+ int scratch;
+
+ m_cur_insn = data + offset;
+ if (!instruction_length(m_cur_insn, insn_len, size - offset)
+ || insn_len > kMaxInstructionByteSize
+ || insn_len == 0) {
+ // An error parsing the instruction, i.e. probably data/garbage - stop
+ // scanning
+ break;
+ }
+
+ if (push_rbp_pattern_p() || mov_rsp_rbp_pattern_p() ||
+ sub_rsp_pattern_p(scratch) || push_reg_p(regno) ||
+ mov_reg_to_local_stack_frame_p(regno, scratch) ||
+ (lea_rsp_pattern_p(scratch) && offset == 0)) {
+ offset += insn_len;
+ continue;
+ }
+ //
+ // Unknown non-prologue instruction - stop scanning
+ break;
+ }
+
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h b/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h
new file mode 100644
index 000000000000..680598abdeff
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.h
@@ -0,0 +1,199 @@
+//===-- x86AssemblyInspectionEngine.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 liblldb_x86AssemblyInspectionEngine_h_
+#define liblldb_x86AssemblyInspectionEngine_h_
+
+#include "llvm-c/Disassembler.h"
+
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-private.h"
+
+#include <map>
+#include <vector>
+
+namespace lldb_private {
+
+// x86AssemblyInspectionEngine - a class which will take a buffer of bytes
+// of i386/x86_64 instructions and create an UnwindPlan based on those
+// assembly instructions.
+class x86AssemblyInspectionEngine {
+
+public:
+ /// default ctor
+ x86AssemblyInspectionEngine(const lldb_private::ArchSpec &arch);
+
+ /// default dtor
+ ~x86AssemblyInspectionEngine();
+
+ /// One of the two initialize methods that can be called on this object;
+ /// they must be called before any of the assembly inspection methods
+ /// are called. This one should be used if the caller has access to a
+ /// valid RegisterContext.
+ void Initialize(lldb::RegisterContextSP &reg_ctx);
+
+ /// One of the two initialize methods that can be called on this object;
+ /// they must be called before any of the assembly inspection methods
+ /// are called. This one takes a vector of register name and lldb
+ /// register numbers.
+ struct lldb_reg_info {
+ const char *name;
+ uint32_t lldb_regnum;
+ lldb_reg_info() : name(nullptr), lldb_regnum(LLDB_INVALID_REGNUM) {}
+ };
+ void Initialize(std::vector<lldb_reg_info> &reg_info);
+
+ /// Create an UnwindPlan for a "non-call site" stack frame situation.
+ /// This is usually when this function/method is currently executing, and may
+ /// be at
+ /// a location where exception-handling style unwind information (eh_frame,
+ /// compact unwind info, arm unwind info)
+ /// are not valid.
+ /// \p data is a pointer to the instructions for the function
+ /// \p size is the size of the instruction buffer above
+ /// \p func_range is the start Address and size of the function, to be
+ /// included in the UnwindPlan
+ /// \p unwind_plan is the unwind plan that this method creates
+ /// \returns true if it was able to create an UnwindPlan; false if not.
+ bool
+ GetNonCallSiteUnwindPlanFromAssembly(uint8_t *data, size_t size,
+ lldb_private::AddressRange &func_range,
+ lldb_private::UnwindPlan &unwind_plan);
+
+ /// Take an existing UnwindPlan, probably from eh_frame which may be missing
+ /// description
+ /// of the epilogue instructions, and add the epilogue description to it based
+ /// on the
+ /// instructions in the function.
+ ///
+ /// The \p unwind_plan 's register numbers must be converted into the lldb
+ /// register numbering
+ /// scheme OR a RegisterContext must be provided in \p reg_ctx. If the \p
+ /// unwind_plan
+ /// register numbers are already in lldb register numbering, \p reg_ctx may be
+ /// null.
+ /// \returns true if the \p unwind_plan was updated, false if it was not.
+ bool AugmentUnwindPlanFromCallSite(uint8_t *data, size_t size,
+ lldb_private::AddressRange &func_range,
+ lldb_private::UnwindPlan &unwind_plan,
+ lldb::RegisterContextSP &reg_ctx);
+
+ bool FindFirstNonPrologueInstruction(uint8_t *data, size_t size,
+ size_t &offset);
+
+private:
+ bool nonvolatile_reg_p(int machine_regno);
+ bool push_rbp_pattern_p();
+ bool push_0_pattern_p();
+ bool push_imm_pattern_p();
+ bool push_extended_pattern_p();
+ bool push_misc_reg_p();
+ bool mov_rsp_rbp_pattern_p();
+ bool mov_rsp_rbx_pattern_p();
+ bool mov_rbp_rsp_pattern_p();
+ bool mov_rbx_rsp_pattern_p();
+ bool sub_rsp_pattern_p(int &amount);
+ bool add_rsp_pattern_p(int &amount);
+ bool lea_rsp_pattern_p(int &amount);
+ bool lea_rbp_rsp_pattern_p(int &amount);
+ bool lea_rbx_rsp_pattern_p(int &amount);
+ bool and_rsp_pattern_p();
+ bool push_reg_p(int &regno);
+ bool pop_reg_p(int &regno);
+ bool pop_rbp_pattern_p();
+ bool pop_misc_reg_p();
+ bool leave_pattern_p();
+ bool call_next_insn_pattern_p();
+ bool mov_reg_to_local_stack_frame_p(int &regno, int &rbp_offset);
+ bool ret_pattern_p();
+ bool jmp_to_reg_p();
+ bool pc_rel_branch_or_jump_p (const int instruction_length, int &offset);
+ bool non_local_branch_p (const lldb::addr_t current_func_text_offset,
+ const lldb_private::AddressRange &func_range,
+ const int instruction_length);
+ bool local_branch_p (const lldb::addr_t current_func_text_offset,
+ const lldb_private::AddressRange &func_range,
+ const int instruction_length,
+ lldb::addr_t &target_insn_offset);
+ uint16_t extract_2(uint8_t *b);
+ int16_t extract_2_signed(uint8_t *b);
+ uint32_t extract_4(uint8_t *b);
+ int32_t extract_4_signed(uint8_t *b);
+
+ bool instruction_length(uint8_t *insn, int &length, uint32_t buffer_remaining_bytes);
+
+ bool machine_regno_to_lldb_regno(int machine_regno, uint32_t &lldb_regno);
+
+ enum CPU { k_i386, k_x86_64, k_cpu_unspecified };
+
+ enum i386_register_numbers {
+ k_machine_eax = 0,
+ k_machine_ecx = 1,
+ k_machine_edx = 2,
+ k_machine_ebx = 3,
+ k_machine_esp = 4,
+ k_machine_ebp = 5,
+ k_machine_esi = 6,
+ k_machine_edi = 7,
+ k_machine_eip = 8
+ };
+
+ enum x86_64_register_numbers {
+ k_machine_rax = 0,
+ k_machine_rcx = 1,
+ k_machine_rdx = 2,
+ k_machine_rbx = 3,
+ k_machine_rsp = 4,
+ k_machine_rbp = 5,
+ k_machine_rsi = 6,
+ k_machine_rdi = 7,
+ k_machine_r8 = 8,
+ k_machine_r9 = 9,
+ k_machine_r10 = 10,
+ k_machine_r11 = 11,
+ k_machine_r12 = 12,
+ k_machine_r13 = 13,
+ k_machine_r14 = 14,
+ k_machine_r15 = 15,
+ k_machine_rip = 16
+ };
+
+ enum { kMaxInstructionByteSize = 32 };
+
+ uint8_t *m_cur_insn;
+
+ uint32_t m_machine_ip_regnum;
+ uint32_t m_machine_sp_regnum;
+ uint32_t m_machine_fp_regnum;
+ uint32_t m_machine_alt_fp_regnum;
+ uint32_t m_lldb_ip_regnum;
+ uint32_t m_lldb_sp_regnum;
+ uint32_t m_lldb_fp_regnum;
+ uint32_t m_lldb_alt_fp_regnum;
+
+ typedef std::map<uint32_t, lldb_reg_info> MachineRegnumToNameAndLLDBRegnum;
+
+ MachineRegnumToNameAndLLDBRegnum m_reg_map;
+
+ lldb_private::ArchSpec m_arch;
+ CPU m_cpu;
+ int m_wordsize;
+
+ bool m_register_map_initialized;
+
+ ::LLVMDisasmContextRef m_disasm_context;
+
+ DISALLOW_COPY_AND_ASSIGN(x86AssemblyInspectionEngine);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_x86AssemblyInspectionEngine_h_