| 1 | //===-- PlatformLinux.cpp -------------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "PlatformLinux.h" |
| 10 | #include "lldb/Host/Config.h" |
| 11 | |
| 12 | #include <cstdio> |
| 13 | #if LLDB_ENABLE_POSIX |
| 14 | #include <sys/utsname.h> |
| 15 | #endif |
| 16 | |
| 17 | #include "Plugins/Process/Utility/LinuxSignals.h" |
| 18 | #include "Utility/ARM64_DWARF_Registers.h" |
| 19 | #include "lldb/Core/Debugger.h" |
| 20 | #include "lldb/Core/PluginManager.h" |
| 21 | #include "lldb/Host/HostInfo.h" |
| 22 | #include "lldb/Symbol/UnwindPlan.h" |
| 23 | #include "lldb/Target/Process.h" |
| 24 | #include "lldb/Target/Target.h" |
| 25 | #include "lldb/Utility/FileSpec.h" |
| 26 | #include "lldb/Utility/LLDBLog.h" |
| 27 | #include "lldb/Utility/Log.h" |
| 28 | #include "lldb/Utility/State.h" |
| 29 | #include "lldb/Utility/Status.h" |
| 30 | #include "lldb/Utility/StreamString.h" |
| 31 | |
| 32 | // Define these constants from Linux mman.h for use when targeting remote linux |
| 33 | // systems even when host has different values. |
| 34 | #define MAP_PRIVATE 2 |
| 35 | #define MAP_ANON 0x20 |
| 36 | |
| 37 | // For other platforms that use platform linux |
| 38 | #ifndef SIGILL |
| 39 | #define SIGILL 4 |
| 40 | #endif |
| 41 | #ifndef SIGBUS |
| 42 | #define SIGBUS 7 |
| 43 | #endif |
| 44 | #ifndef SIGFPE |
| 45 | #define SIGFPE 8 |
| 46 | #endif |
| 47 | #ifndef SIGSEGV |
| 48 | #define SIGSEGV 11 |
| 49 | #endif |
| 50 | |
| 51 | using namespace lldb; |
| 52 | using namespace lldb_private; |
| 53 | using namespace lldb_private::platform_linux; |
| 54 | |
| 55 | LLDB_PLUGIN_DEFINE(PlatformLinux) |
| 56 | |
| 57 | static uint32_t g_initialize_count = 0; |
| 58 | |
| 59 | |
| 60 | PlatformSP PlatformLinux::CreateInstance(bool force, const ArchSpec *arch) { |
| 61 | Log *log = GetLog(mask: LLDBLog::Platform); |
| 62 | LLDB_LOG(log, "force = {0}, arch=({1}, {2})" , force, |
| 63 | arch ? arch->GetArchitectureName() : "<null>" , |
| 64 | arch ? arch->GetTriple().getTriple() : "<null>" ); |
| 65 | |
| 66 | bool create = force; |
| 67 | if (!create && arch && arch->IsValid()) { |
| 68 | const llvm::Triple &triple = arch->GetTriple(); |
| 69 | switch (triple.getOS()) { |
| 70 | case llvm::Triple::Linux: |
| 71 | create = true; |
| 72 | break; |
| 73 | |
| 74 | #if defined(__linux__) |
| 75 | // Only accept "unknown" for the OS if the host is linux and it "unknown" |
| 76 | // wasn't specified (it was just returned because it was NOT specified) |
| 77 | case llvm::Triple::OSType::UnknownOS: |
| 78 | create = !arch->TripleOSWasSpecified(); |
| 79 | break; |
| 80 | #endif |
| 81 | default: |
| 82 | break; |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | LLDB_LOG(log, "create = {0}" , create); |
| 87 | if (create) { |
| 88 | return PlatformSP(new PlatformLinux(false)); |
| 89 | } |
| 90 | return PlatformSP(); |
| 91 | } |
| 92 | |
| 93 | llvm::StringRef PlatformLinux::GetPluginDescriptionStatic(bool is_host) { |
| 94 | if (is_host) |
| 95 | return "Local Linux user platform plug-in." ; |
| 96 | return "Remote Linux user platform plug-in." ; |
| 97 | } |
| 98 | |
| 99 | void PlatformLinux::Initialize() { |
| 100 | PlatformPOSIX::Initialize(); |
| 101 | |
| 102 | if (g_initialize_count++ == 0) { |
| 103 | #if defined(__linux__) && !defined(__ANDROID__) |
| 104 | PlatformSP default_platform_sp(new PlatformLinux(true)); |
| 105 | default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); |
| 106 | Platform::SetHostPlatform(default_platform_sp); |
| 107 | #endif |
| 108 | PluginManager::RegisterPlugin( |
| 109 | name: PlatformLinux::GetPluginNameStatic(is_host: false), |
| 110 | description: PlatformLinux::GetPluginDescriptionStatic(is_host: false), |
| 111 | create_callback: PlatformLinux::CreateInstance, debugger_init_callback: nullptr); |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | void PlatformLinux::Terminate() { |
| 116 | if (g_initialize_count > 0) { |
| 117 | if (--g_initialize_count == 0) { |
| 118 | PluginManager::UnregisterPlugin(create_callback: PlatformLinux::CreateInstance); |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | PlatformPOSIX::Terminate(); |
| 123 | } |
| 124 | |
| 125 | /// Default Constructor |
| 126 | PlatformLinux::PlatformLinux(bool is_host) |
| 127 | : PlatformPOSIX(is_host) // This is the local host platform |
| 128 | { |
| 129 | if (is_host) { |
| 130 | ArchSpec hostArch = HostInfo::GetArchitecture(arch_kind: HostInfo::eArchKindDefault); |
| 131 | m_supported_architectures.push_back(x: hostArch); |
| 132 | if (hostArch.GetTriple().isArch64Bit()) { |
| 133 | m_supported_architectures.push_back( |
| 134 | x: HostInfo::GetArchitecture(arch_kind: HostInfo::eArchKind32)); |
| 135 | } |
| 136 | } else { |
| 137 | m_supported_architectures = CreateArchList( |
| 138 | archs: {llvm::Triple::x86_64, llvm::Triple::x86, llvm::Triple::arm, |
| 139 | llvm::Triple::aarch64, llvm::Triple::mips64, llvm::Triple::mips64, |
| 140 | llvm::Triple::hexagon, llvm::Triple::mips, llvm::Triple::mips64el, |
| 141 | llvm::Triple::mipsel, llvm::Triple::msp430, llvm::Triple::systemz, |
| 142 | llvm::Triple::loongarch64, llvm::Triple::ppc64le, |
| 143 | llvm::Triple::riscv64}, |
| 144 | os: llvm::Triple::Linux); |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | std::vector<ArchSpec> |
| 149 | PlatformLinux::GetSupportedArchitectures(const ArchSpec &process_host_arch) { |
| 150 | if (m_remote_platform_sp) |
| 151 | return m_remote_platform_sp->GetSupportedArchitectures(process_host_arch); |
| 152 | return m_supported_architectures; |
| 153 | } |
| 154 | |
| 155 | void PlatformLinux::GetStatus(Stream &strm) { |
| 156 | Platform::GetStatus(strm); |
| 157 | |
| 158 | #if LLDB_ENABLE_POSIX |
| 159 | // Display local kernel information only when we are running in host mode. |
| 160 | // Otherwise, we would end up printing non-Linux information (when running on |
| 161 | // Mac OS for example). |
| 162 | if (IsHost()) { |
| 163 | struct utsname un; |
| 164 | |
| 165 | if (uname(name: &un)) |
| 166 | return; |
| 167 | |
| 168 | strm.Printf(format: " Kernel: %s\n" , un.sysname); |
| 169 | strm.Printf(format: " Release: %s\n" , un.release); |
| 170 | strm.Printf(format: " Version: %s\n" , un.version); |
| 171 | } |
| 172 | #endif |
| 173 | } |
| 174 | |
| 175 | uint32_t |
| 176 | PlatformLinux::GetResumeCountForLaunchInfo(ProcessLaunchInfo &launch_info) { |
| 177 | uint32_t resume_count = 0; |
| 178 | |
| 179 | // Always resume past the initial stop when we use eLaunchFlagDebug |
| 180 | if (launch_info.GetFlags().Test(bit: eLaunchFlagDebug)) { |
| 181 | // Resume past the stop for the final exec into the true inferior. |
| 182 | ++resume_count; |
| 183 | } |
| 184 | |
| 185 | // If we're not launching a shell, we're done. |
| 186 | const FileSpec &shell = launch_info.GetShell(); |
| 187 | if (!shell) |
| 188 | return resume_count; |
| 189 | |
| 190 | std::string shell_string = shell.GetPath(); |
| 191 | // We're in a shell, so for sure we have to resume past the shell exec. |
| 192 | ++resume_count; |
| 193 | |
| 194 | // Figure out what shell we're planning on using. |
| 195 | const char *shell_name = strrchr(s: shell_string.c_str(), c: '/'); |
| 196 | if (shell_name == nullptr) |
| 197 | shell_name = shell_string.c_str(); |
| 198 | else |
| 199 | shell_name++; |
| 200 | |
| 201 | if (strcmp(s1: shell_name, s2: "csh" ) == 0 || strcmp(s1: shell_name, s2: "tcsh" ) == 0 || |
| 202 | strcmp(s1: shell_name, s2: "zsh" ) == 0 || strcmp(s1: shell_name, s2: "sh" ) == 0) { |
| 203 | // These shells seem to re-exec themselves. Add another resume. |
| 204 | ++resume_count; |
| 205 | } |
| 206 | |
| 207 | return resume_count; |
| 208 | } |
| 209 | |
| 210 | bool PlatformLinux::CanDebugProcess() { |
| 211 | if (IsHost()) { |
| 212 | return true; |
| 213 | } else { |
| 214 | // If we're connected, we can debug. |
| 215 | return IsConnected(); |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | void PlatformLinux::CalculateTrapHandlerSymbolNames() { |
| 220 | m_trap_handlers.push_back(x: ConstString("_sigtramp" )); |
| 221 | m_trap_handlers.push_back(x: ConstString("__kernel_rt_sigreturn" )); |
| 222 | m_trap_handlers.push_back(x: ConstString("__restore_rt" )); |
| 223 | } |
| 224 | |
| 225 | static lldb::UnwindPlanSP GetAArch64TrapHandlerUnwindPlan(ConstString name) { |
| 226 | UnwindPlanSP unwind_plan_sp; |
| 227 | if (name != "__kernel_rt_sigreturn" ) |
| 228 | return unwind_plan_sp; |
| 229 | |
| 230 | UnwindPlan::Row row; |
| 231 | |
| 232 | // In the signal trampoline frame, sp points to an rt_sigframe[1], which is: |
| 233 | // - 128-byte siginfo struct |
| 234 | // - ucontext struct: |
| 235 | // - 8-byte long (uc_flags) |
| 236 | // - 8-byte pointer (uc_link) |
| 237 | // - 24-byte stack_t |
| 238 | // - 128-byte signal set |
| 239 | // - 8 bytes of padding because sigcontext has 16-byte alignment |
| 240 | // - sigcontext/mcontext_t |
| 241 | // [1] |
| 242 | // https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/signal.c |
| 243 | int32_t offset = 128 + 8 + 8 + 24 + 128 + 8; |
| 244 | // Then sigcontext[2] is: |
| 245 | // - 8 byte fault address |
| 246 | // - 31 8 byte registers |
| 247 | // - 8 byte sp |
| 248 | // - 8 byte pc |
| 249 | // [2] |
| 250 | // https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/sigcontext.h |
| 251 | |
| 252 | // Skip fault address |
| 253 | offset += 8; |
| 254 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: arm64_dwarf::sp, offset); |
| 255 | |
| 256 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x0, offset: 0 * 8, can_replace: false); |
| 257 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x1, offset: 1 * 8, can_replace: false); |
| 258 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x2, offset: 2 * 8, can_replace: false); |
| 259 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x3, offset: 3 * 8, can_replace: false); |
| 260 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x4, offset: 4 * 8, can_replace: false); |
| 261 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x5, offset: 5 * 8, can_replace: false); |
| 262 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x6, offset: 6 * 8, can_replace: false); |
| 263 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x7, offset: 7 * 8, can_replace: false); |
| 264 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x8, offset: 8 * 8, can_replace: false); |
| 265 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x9, offset: 9 * 8, can_replace: false); |
| 266 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x10, offset: 10 * 8, can_replace: false); |
| 267 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x11, offset: 11 * 8, can_replace: false); |
| 268 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x12, offset: 12 * 8, can_replace: false); |
| 269 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x13, offset: 13 * 8, can_replace: false); |
| 270 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x14, offset: 14 * 8, can_replace: false); |
| 271 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x15, offset: 15 * 8, can_replace: false); |
| 272 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x16, offset: 16 * 8, can_replace: false); |
| 273 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x17, offset: 17 * 8, can_replace: false); |
| 274 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x18, offset: 18 * 8, can_replace: false); |
| 275 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x19, offset: 19 * 8, can_replace: false); |
| 276 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x20, offset: 20 * 8, can_replace: false); |
| 277 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x21, offset: 21 * 8, can_replace: false); |
| 278 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x22, offset: 22 * 8, can_replace: false); |
| 279 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x23, offset: 23 * 8, can_replace: false); |
| 280 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x24, offset: 24 * 8, can_replace: false); |
| 281 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x25, offset: 25 * 8, can_replace: false); |
| 282 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x26, offset: 26 * 8, can_replace: false); |
| 283 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x27, offset: 27 * 8, can_replace: false); |
| 284 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x28, offset: 28 * 8, can_replace: false); |
| 285 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::fp, offset: 29 * 8, can_replace: false); |
| 286 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::x30, offset: 30 * 8, can_replace: false); |
| 287 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::sp, offset: 31 * 8, can_replace: false); |
| 288 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_dwarf::pc, offset: 32 * 8, can_replace: false); |
| 289 | |
| 290 | // The sigcontext may also contain floating point and SVE registers. |
| 291 | // However this would require a dynamic unwind plan so they are not included |
| 292 | // here. |
| 293 | |
| 294 | unwind_plan_sp = std::make_shared<UnwindPlan>(args: eRegisterKindDWARF); |
| 295 | unwind_plan_sp->AppendRow(row: std::move(row)); |
| 296 | unwind_plan_sp->SetSourceName("AArch64 Linux sigcontext" ); |
| 297 | unwind_plan_sp->SetSourcedFromCompiler(eLazyBoolYes); |
| 298 | // Because sp is the same throughout the function |
| 299 | unwind_plan_sp->SetUnwindPlanValidAtAllInstructions(eLazyBoolYes); |
| 300 | unwind_plan_sp->SetUnwindPlanForSignalTrap(eLazyBoolYes); |
| 301 | |
| 302 | return unwind_plan_sp; |
| 303 | } |
| 304 | |
| 305 | lldb::UnwindPlanSP |
| 306 | PlatformLinux::GetTrapHandlerUnwindPlan(const llvm::Triple &triple, |
| 307 | ConstString name) { |
| 308 | if (triple.isAArch64()) |
| 309 | return GetAArch64TrapHandlerUnwindPlan(name); |
| 310 | |
| 311 | return {}; |
| 312 | } |
| 313 | |
| 314 | MmapArgList PlatformLinux::GetMmapArgumentList(const ArchSpec &arch, |
| 315 | addr_t addr, addr_t length, |
| 316 | unsigned prot, unsigned flags, |
| 317 | addr_t fd, addr_t offset) { |
| 318 | uint64_t flags_platform = 0; |
| 319 | uint64_t map_anon = arch.IsMIPS() ? 0x800 : MAP_ANON; |
| 320 | |
| 321 | if (flags & eMmapFlagsPrivate) |
| 322 | flags_platform |= MAP_PRIVATE; |
| 323 | if (flags & eMmapFlagsAnon) |
| 324 | flags_platform |= map_anon; |
| 325 | |
| 326 | MmapArgList args({addr, length, prot, flags_platform, fd, offset}); |
| 327 | return args; |
| 328 | } |
| 329 | |
| 330 | CompilerType PlatformLinux::GetSiginfoType(const llvm::Triple &triple) { |
| 331 | { |
| 332 | std::lock_guard<std::mutex> guard(m_mutex); |
| 333 | if (!m_type_system) |
| 334 | m_type_system = std::make_shared<TypeSystemClang>(args: "siginfo" , args: triple); |
| 335 | } |
| 336 | TypeSystemClang *ast = m_type_system.get(); |
| 337 | |
| 338 | bool si_errno_then_code = true; |
| 339 | |
| 340 | switch (triple.getArch()) { |
| 341 | case llvm::Triple::mips: |
| 342 | case llvm::Triple::mipsel: |
| 343 | case llvm::Triple::mips64: |
| 344 | case llvm::Triple::mips64el: |
| 345 | // mips has si_code and si_errno swapped |
| 346 | si_errno_then_code = false; |
| 347 | break; |
| 348 | default: |
| 349 | break; |
| 350 | } |
| 351 | |
| 352 | // generic types |
| 353 | CompilerType int_type = ast->GetBasicType(type: eBasicTypeInt); |
| 354 | CompilerType uint_type = ast->GetBasicType(type: eBasicTypeUnsignedInt); |
| 355 | CompilerType short_type = ast->GetBasicType(type: eBasicTypeShort); |
| 356 | CompilerType long_type = ast->GetBasicType(type: eBasicTypeLong); |
| 357 | CompilerType voidp_type = ast->GetBasicType(type: eBasicTypeVoid).GetPointerType(); |
| 358 | |
| 359 | // platform-specific types |
| 360 | CompilerType &pid_type = int_type; |
| 361 | CompilerType &uid_type = uint_type; |
| 362 | CompilerType &clock_type = long_type; |
| 363 | CompilerType &band_type = long_type; |
| 364 | |
| 365 | CompilerType sigval_type = ast->CreateRecordType( |
| 366 | decl_ctx: nullptr, owning_module: OptionalClangModuleID(), access_type: lldb::eAccessPublic, name: "__lldb_sigval_t" , |
| 367 | kind: llvm::to_underlying(E: clang::TagTypeKind::Union), language: lldb::eLanguageTypeC); |
| 368 | ast->StartTagDeclarationDefinition(type: sigval_type); |
| 369 | ast->AddFieldToRecordType(type: sigval_type, name: "sival_int" , field_type: int_type, |
| 370 | access: lldb::eAccessPublic, bitfield_bit_size: 0); |
| 371 | ast->AddFieldToRecordType(type: sigval_type, name: "sival_ptr" , field_type: voidp_type, |
| 372 | access: lldb::eAccessPublic, bitfield_bit_size: 0); |
| 373 | ast->CompleteTagDeclarationDefinition(type: sigval_type); |
| 374 | |
| 375 | CompilerType sigfault_bounds_type = ast->CreateRecordType( |
| 376 | decl_ctx: nullptr, owning_module: OptionalClangModuleID(), access_type: lldb::eAccessPublic, name: "" , |
| 377 | kind: llvm::to_underlying(E: clang::TagTypeKind::Union), language: lldb::eLanguageTypeC); |
| 378 | ast->StartTagDeclarationDefinition(type: sigfault_bounds_type); |
| 379 | ast->AddFieldToRecordType( |
| 380 | type: sigfault_bounds_type, name: "_addr_bnd" , |
| 381 | field_type: ast->CreateStructForIdentifier(type_name: llvm::StringRef(), |
| 382 | type_fields: { |
| 383 | {"_lower" , voidp_type}, |
| 384 | {"_upper" , voidp_type}, |
| 385 | }), |
| 386 | access: lldb::eAccessPublic, bitfield_bit_size: 0); |
| 387 | ast->AddFieldToRecordType(type: sigfault_bounds_type, name: "_pkey" , field_type: uint_type, |
| 388 | access: lldb::eAccessPublic, bitfield_bit_size: 0); |
| 389 | ast->CompleteTagDeclarationDefinition(type: sigfault_bounds_type); |
| 390 | |
| 391 | // siginfo_t |
| 392 | CompilerType siginfo_type = ast->CreateRecordType( |
| 393 | decl_ctx: nullptr, owning_module: OptionalClangModuleID(), access_type: lldb::eAccessPublic, name: "__lldb_siginfo_t" , |
| 394 | kind: llvm::to_underlying(E: clang::TagTypeKind::Struct), language: lldb::eLanguageTypeC); |
| 395 | ast->StartTagDeclarationDefinition(type: siginfo_type); |
| 396 | ast->AddFieldToRecordType(type: siginfo_type, name: "si_signo" , field_type: int_type, |
| 397 | access: lldb::eAccessPublic, bitfield_bit_size: 0); |
| 398 | |
| 399 | if (si_errno_then_code) { |
| 400 | ast->AddFieldToRecordType(type: siginfo_type, name: "si_errno" , field_type: int_type, |
| 401 | access: lldb::eAccessPublic, bitfield_bit_size: 0); |
| 402 | ast->AddFieldToRecordType(type: siginfo_type, name: "si_code" , field_type: int_type, |
| 403 | access: lldb::eAccessPublic, bitfield_bit_size: 0); |
| 404 | } else { |
| 405 | ast->AddFieldToRecordType(type: siginfo_type, name: "si_code" , field_type: int_type, |
| 406 | access: lldb::eAccessPublic, bitfield_bit_size: 0); |
| 407 | ast->AddFieldToRecordType(type: siginfo_type, name: "si_errno" , field_type: int_type, |
| 408 | access: lldb::eAccessPublic, bitfield_bit_size: 0); |
| 409 | } |
| 410 | |
| 411 | // the structure is padded on 64-bit arches to fix alignment |
| 412 | if (triple.isArch64Bit()) |
| 413 | ast->AddFieldToRecordType(type: siginfo_type, name: "__pad0" , field_type: int_type, |
| 414 | access: lldb::eAccessPublic, bitfield_bit_size: 0); |
| 415 | |
| 416 | // union used to hold the signal data |
| 417 | CompilerType union_type = ast->CreateRecordType( |
| 418 | decl_ctx: nullptr, owning_module: OptionalClangModuleID(), access_type: lldb::eAccessPublic, name: "" , |
| 419 | kind: llvm::to_underlying(E: clang::TagTypeKind::Union), language: lldb::eLanguageTypeC); |
| 420 | ast->StartTagDeclarationDefinition(type: union_type); |
| 421 | |
| 422 | ast->AddFieldToRecordType( |
| 423 | type: union_type, name: "_kill" , |
| 424 | field_type: ast->CreateStructForIdentifier(type_name: llvm::StringRef(), |
| 425 | type_fields: { |
| 426 | {"si_pid" , pid_type}, |
| 427 | {"si_uid" , uid_type}, |
| 428 | }), |
| 429 | access: lldb::eAccessPublic, bitfield_bit_size: 0); |
| 430 | |
| 431 | ast->AddFieldToRecordType( |
| 432 | type: union_type, name: "_timer" , |
| 433 | field_type: ast->CreateStructForIdentifier(type_name: llvm::StringRef(), |
| 434 | type_fields: { |
| 435 | {"si_tid" , int_type}, |
| 436 | {"si_overrun" , int_type}, |
| 437 | {"si_sigval" , sigval_type}, |
| 438 | }), |
| 439 | access: lldb::eAccessPublic, bitfield_bit_size: 0); |
| 440 | |
| 441 | ast->AddFieldToRecordType( |
| 442 | type: union_type, name: "_rt" , |
| 443 | field_type: ast->CreateStructForIdentifier(type_name: llvm::StringRef(), |
| 444 | type_fields: { |
| 445 | {"si_pid" , pid_type}, |
| 446 | {"si_uid" , uid_type}, |
| 447 | {"si_sigval" , sigval_type}, |
| 448 | }), |
| 449 | access: lldb::eAccessPublic, bitfield_bit_size: 0); |
| 450 | |
| 451 | ast->AddFieldToRecordType( |
| 452 | type: union_type, name: "_sigchld" , |
| 453 | field_type: ast->CreateStructForIdentifier(type_name: llvm::StringRef(), |
| 454 | type_fields: { |
| 455 | {"si_pid" , pid_type}, |
| 456 | {"si_uid" , uid_type}, |
| 457 | {"si_status" , int_type}, |
| 458 | {"si_utime" , clock_type}, |
| 459 | {"si_stime" , clock_type}, |
| 460 | }), |
| 461 | access: lldb::eAccessPublic, bitfield_bit_size: 0); |
| 462 | |
| 463 | ast->AddFieldToRecordType( |
| 464 | type: union_type, name: "_sigfault" , |
| 465 | field_type: ast->CreateStructForIdentifier(type_name: llvm::StringRef(), |
| 466 | type_fields: { |
| 467 | {"si_addr" , voidp_type}, |
| 468 | {"si_addr_lsb" , short_type}, |
| 469 | {"_bounds" , sigfault_bounds_type}, |
| 470 | }), |
| 471 | access: lldb::eAccessPublic, bitfield_bit_size: 0); |
| 472 | |
| 473 | ast->AddFieldToRecordType( |
| 474 | type: union_type, name: "_sigpoll" , |
| 475 | field_type: ast->CreateStructForIdentifier(type_name: llvm::StringRef(), |
| 476 | type_fields: { |
| 477 | {"si_band" , band_type}, |
| 478 | {"si_fd" , int_type}, |
| 479 | }), |
| 480 | access: lldb::eAccessPublic, bitfield_bit_size: 0); |
| 481 | |
| 482 | // NB: SIGSYS is not present on ia64 but we don't seem to support that |
| 483 | ast->AddFieldToRecordType( |
| 484 | type: union_type, name: "_sigsys" , |
| 485 | field_type: ast->CreateStructForIdentifier(type_name: llvm::StringRef(), |
| 486 | type_fields: { |
| 487 | {"_call_addr" , voidp_type}, |
| 488 | {"_syscall" , int_type}, |
| 489 | {"_arch" , uint_type}, |
| 490 | }), |
| 491 | access: lldb::eAccessPublic, bitfield_bit_size: 0); |
| 492 | |
| 493 | ast->CompleteTagDeclarationDefinition(type: union_type); |
| 494 | ast->AddFieldToRecordType(type: siginfo_type, name: "_sifields" , field_type: union_type, |
| 495 | access: lldb::eAccessPublic, bitfield_bit_size: 0); |
| 496 | |
| 497 | ast->CompleteTagDeclarationDefinition(type: siginfo_type); |
| 498 | return siginfo_type; |
| 499 | } |
| 500 | |
| 501 | static std::string GetDescriptionFromSiginfo(lldb::ValueObjectSP siginfo_sp) { |
| 502 | if (!siginfo_sp) |
| 503 | return "" ; |
| 504 | |
| 505 | lldb_private::LinuxSignals linux_signals; |
| 506 | int code = siginfo_sp->GetChildMemberWithName(name: "si_code" )->GetValueAsSigned(fail_value: 0); |
| 507 | int signo = |
| 508 | siginfo_sp->GetChildMemberWithName(name: "si_signo" )->GetValueAsSigned(fail_value: -1); |
| 509 | |
| 510 | auto sifields = siginfo_sp->GetChildMemberWithName(name: "_sifields" ); |
| 511 | if (!sifields) |
| 512 | return linux_signals.GetSignalDescription(signo, code); |
| 513 | |
| 514 | // declare everything that we can populate later. |
| 515 | std::optional<lldb::addr_t> addr; |
| 516 | std::optional<lldb::addr_t> upper; |
| 517 | std::optional<lldb::addr_t> lower; |
| 518 | std::optional<uint32_t> pid; |
| 519 | std::optional<uint32_t> uid; |
| 520 | |
| 521 | // The negative si_codes are special and mean this signal was sent from user |
| 522 | // space not the kernel. These take precedence because they break some of the |
| 523 | // invariants around kernel sent signals. Such as SIGSEGV won't have an |
| 524 | // address. |
| 525 | if (code < 0) { |
| 526 | auto sikill = sifields->GetChildMemberWithName(name: "_kill" ); |
| 527 | if (sikill) { |
| 528 | auto pid_sp = sikill->GetChildMemberWithName(name: "si_pid" ); |
| 529 | if (pid_sp) |
| 530 | pid = pid_sp->GetValueAsUnsigned(fail_value: -1); |
| 531 | auto uid_sp = sikill->GetChildMemberWithName(name: "si_uid" ); |
| 532 | if (uid_sp) |
| 533 | uid = uid_sp->GetValueAsUnsigned(fail_value: -1); |
| 534 | } |
| 535 | } else { |
| 536 | |
| 537 | switch (signo) { |
| 538 | case SIGILL: |
| 539 | case SIGFPE: |
| 540 | case SIGBUS: { |
| 541 | auto sigfault = sifields->GetChildMemberWithName(name: "_sigfault" ); |
| 542 | if (!sigfault) |
| 543 | break; |
| 544 | |
| 545 | auto addr_sp = sigfault->GetChildMemberWithName(name: "si_addr" ); |
| 546 | if (addr_sp) |
| 547 | addr = addr_sp->GetValueAsUnsigned(fail_value: -1); |
| 548 | break; |
| 549 | } |
| 550 | case SIGSEGV: { |
| 551 | auto sigfault = sifields->GetChildMemberWithName(name: "_sigfault" ); |
| 552 | if (!sigfault) |
| 553 | break; |
| 554 | |
| 555 | auto addr_sp = sigfault->GetChildMemberWithName(name: "si_addr" ); |
| 556 | if (addr_sp) |
| 557 | addr = addr_sp->GetValueAsUnsigned(fail_value: -1); |
| 558 | |
| 559 | auto bounds_sp = sigfault->GetChildMemberWithName(name: "_bounds" ); |
| 560 | if (!bounds_sp) |
| 561 | break; |
| 562 | |
| 563 | auto addr_bnds_sp = bounds_sp->GetChildMemberWithName(name: "_addr_bnd" ); |
| 564 | if (!addr_bnds_sp) |
| 565 | break; |
| 566 | |
| 567 | auto lower_sp = addr_bnds_sp->GetChildMemberWithName(name: "_lower" ); |
| 568 | if (lower_sp) |
| 569 | lower = lower_sp->GetValueAsUnsigned(fail_value: -1); |
| 570 | |
| 571 | auto upper_sp = addr_bnds_sp->GetChildMemberWithName(name: "_upper" ); |
| 572 | if (upper_sp) |
| 573 | upper = upper_sp->GetValueAsUnsigned(fail_value: -1); |
| 574 | |
| 575 | break; |
| 576 | } |
| 577 | default: |
| 578 | break; |
| 579 | } |
| 580 | } |
| 581 | |
| 582 | return linux_signals.GetSignalDescription(signo, code, addr, lower, upper, |
| 583 | pid, uid); |
| 584 | } |
| 585 | |
| 586 | lldb::StopInfoSP PlatformLinux::GetStopInfoFromSiginfo(Thread &thread) { |
| 587 | ValueObjectSP siginfo_sp = thread.GetSiginfoValue(); |
| 588 | if (!siginfo_sp) |
| 589 | return {}; |
| 590 | auto signo_sp = siginfo_sp->GetChildMemberWithName(name: "si_signo" ); |
| 591 | auto sicode_sp = siginfo_sp->GetChildMemberWithName(name: "si_code" ); |
| 592 | if (!signo_sp || !sicode_sp) |
| 593 | return {}; |
| 594 | |
| 595 | std::string siginfo_description = GetDescriptionFromSiginfo(siginfo_sp); |
| 596 | if (siginfo_description.empty()) |
| 597 | return StopInfo::CreateStopReasonWithSignal( |
| 598 | thread, signo: signo_sp->GetValueAsUnsigned(fail_value: -1)); |
| 599 | |
| 600 | return StopInfo::CreateStopReasonWithSignal( |
| 601 | thread, signo: signo_sp->GetValueAsUnsigned(fail_value: -1), description: siginfo_description.c_str(), |
| 602 | code: sicode_sp->GetValueAsUnsigned(fail_value: 0)); |
| 603 | } |
| 604 | |