1 //===-- UnwindPlan.cpp ----------------------------------*- C++ -*-===// 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 "lldb/Symbol/UnwindPlan.h" 10 11 #include "lldb/Target/Process.h" 12 #include "lldb/Target/RegisterContext.h" 13 #include "lldb/Target/Thread.h" 14 #include "lldb/Utility/ConstString.h" 15 #include "lldb/Utility/Log.h" 16 17 using namespace lldb; 18 using namespace lldb_private; 19 20 bool UnwindPlan::Row::RegisterLocation:: 21 operator==(const UnwindPlan::Row::RegisterLocation &rhs) const { 22 if (m_type == rhs.m_type) { 23 switch (m_type) { 24 case unspecified: 25 case undefined: 26 case same: 27 return true; 28 29 case atCFAPlusOffset: 30 case isCFAPlusOffset: 31 case atAFAPlusOffset: 32 case isAFAPlusOffset: 33 return m_location.offset == rhs.m_location.offset; 34 35 case inOtherRegister: 36 return m_location.reg_num == rhs.m_location.reg_num; 37 38 case atDWARFExpression: 39 case isDWARFExpression: 40 if (m_location.expr.length == rhs.m_location.expr.length) 41 return !memcmp(m_location.expr.opcodes, rhs.m_location.expr.opcodes, 42 m_location.expr.length); 43 break; 44 } 45 } 46 return false; 47 } 48 49 // This function doesn't copy the dwarf expression bytes; they must remain in 50 // allocated memory for the lifespan of this UnwindPlan object. 51 void UnwindPlan::Row::RegisterLocation::SetAtDWARFExpression( 52 const uint8_t *opcodes, uint32_t len) { 53 m_type = atDWARFExpression; 54 m_location.expr.opcodes = opcodes; 55 m_location.expr.length = len; 56 } 57 58 // This function doesn't copy the dwarf expression bytes; they must remain in 59 // allocated memory for the lifespan of this UnwindPlan object. 60 void UnwindPlan::Row::RegisterLocation::SetIsDWARFExpression( 61 const uint8_t *opcodes, uint32_t len) { 62 m_type = isDWARFExpression; 63 m_location.expr.opcodes = opcodes; 64 m_location.expr.length = len; 65 } 66 67 void UnwindPlan::Row::RegisterLocation::Dump(Stream &s, 68 const UnwindPlan *unwind_plan, 69 const UnwindPlan::Row *row, 70 Thread *thread, 71 bool verbose) const { 72 switch (m_type) { 73 case unspecified: 74 if (verbose) 75 s.PutCString("=<unspec>"); 76 else 77 s.PutCString("=!"); 78 break; 79 case undefined: 80 if (verbose) 81 s.PutCString("=<undef>"); 82 else 83 s.PutCString("=?"); 84 break; 85 case same: 86 s.PutCString("= <same>"); 87 break; 88 89 case atCFAPlusOffset: 90 case isCFAPlusOffset: { 91 s.PutChar('='); 92 if (m_type == atCFAPlusOffset) 93 s.PutChar('['); 94 s.Printf("CFA%+d", m_location.offset); 95 if (m_type == atCFAPlusOffset) 96 s.PutChar(']'); 97 } break; 98 99 case atAFAPlusOffset: 100 case isAFAPlusOffset: { 101 s.PutChar('='); 102 if (m_type == atAFAPlusOffset) 103 s.PutChar('['); 104 s.Printf("AFA%+d", m_location.offset); 105 if (m_type == atAFAPlusOffset) 106 s.PutChar(']'); 107 } break; 108 109 case inOtherRegister: { 110 const RegisterInfo *other_reg_info = nullptr; 111 if (unwind_plan) 112 other_reg_info = unwind_plan->GetRegisterInfo(thread, m_location.reg_num); 113 if (other_reg_info) 114 s.Printf("=%s", other_reg_info->name); 115 else 116 s.Printf("=reg(%u)", m_location.reg_num); 117 } break; 118 119 case atDWARFExpression: 120 case isDWARFExpression: { 121 s.PutChar('='); 122 if (m_type == atDWARFExpression) 123 s.PutCString("[dwarf-expr]"); 124 else 125 s.PutCString("dwarf-expr"); 126 } break; 127 } 128 } 129 130 static void DumpRegisterName(Stream &s, const UnwindPlan *unwind_plan, 131 Thread *thread, uint32_t reg_num) { 132 const RegisterInfo *reg_info = unwind_plan->GetRegisterInfo(thread, reg_num); 133 if (reg_info) 134 s.PutCString(reg_info->name); 135 else 136 s.Printf("reg(%u)", reg_num); 137 } 138 139 bool UnwindPlan::Row::FAValue:: 140 operator==(const UnwindPlan::Row::FAValue &rhs) const { 141 if (m_type == rhs.m_type) { 142 switch (m_type) { 143 case unspecified: 144 return true; 145 146 case isRegisterPlusOffset: 147 return m_value.reg.offset == rhs.m_value.reg.offset; 148 149 case isRegisterDereferenced: 150 return m_value.reg.reg_num == rhs.m_value.reg.reg_num; 151 152 case isDWARFExpression: 153 if (m_value.expr.length == rhs.m_value.expr.length) 154 return !memcmp(m_value.expr.opcodes, rhs.m_value.expr.opcodes, 155 m_value.expr.length); 156 break; 157 } 158 } 159 return false; 160 } 161 162 void UnwindPlan::Row::FAValue::Dump(Stream &s, const UnwindPlan *unwind_plan, 163 Thread *thread) const { 164 switch (m_type) { 165 case isRegisterPlusOffset: 166 DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num); 167 s.Printf("%+3d", m_value.reg.offset); 168 break; 169 case isRegisterDereferenced: 170 s.PutChar('['); 171 DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num); 172 s.PutChar(']'); 173 break; 174 case isDWARFExpression: 175 s.PutCString("dwarf-expr"); 176 break; 177 default: 178 s.PutCString("unspecified"); 179 break; 180 } 181 } 182 183 void UnwindPlan::Row::Clear() { 184 m_cfa_value.SetUnspecified(); 185 m_afa_value.SetUnspecified(); 186 m_offset = 0; 187 m_register_locations.clear(); 188 } 189 190 void UnwindPlan::Row::Dump(Stream &s, const UnwindPlan *unwind_plan, 191 Thread *thread, addr_t base_addr) const { 192 if (base_addr != LLDB_INVALID_ADDRESS) 193 s.Printf("0x%16.16" PRIx64 ": CFA=", base_addr + GetOffset()); 194 else 195 s.Printf("%4" PRId64 ": CFA=", GetOffset()); 196 197 m_cfa_value.Dump(s, unwind_plan, thread); 198 199 if (!m_afa_value.IsUnspecified()) { 200 s.Printf(" AFA="); 201 m_afa_value.Dump(s, unwind_plan, thread); 202 } 203 204 s.Printf(" => "); 205 for (collection::const_iterator idx = m_register_locations.begin(); 206 idx != m_register_locations.end(); ++idx) { 207 DumpRegisterName(s, unwind_plan, thread, idx->first); 208 const bool verbose = false; 209 idx->second.Dump(s, unwind_plan, this, thread, verbose); 210 s.PutChar(' '); 211 } 212 s.EOL(); 213 } 214 215 UnwindPlan::Row::Row() 216 : m_offset(0), m_cfa_value(), m_afa_value(), m_register_locations() {} 217 218 bool UnwindPlan::Row::GetRegisterInfo( 219 uint32_t reg_num, 220 UnwindPlan::Row::RegisterLocation ®ister_location) const { 221 collection::const_iterator pos = m_register_locations.find(reg_num); 222 if (pos != m_register_locations.end()) { 223 register_location = pos->second; 224 return true; 225 } 226 return false; 227 } 228 229 void UnwindPlan::Row::RemoveRegisterInfo(uint32_t reg_num) { 230 collection::const_iterator pos = m_register_locations.find(reg_num); 231 if (pos != m_register_locations.end()) { 232 m_register_locations.erase(pos); 233 } 234 } 235 236 void UnwindPlan::Row::SetRegisterInfo( 237 uint32_t reg_num, 238 const UnwindPlan::Row::RegisterLocation register_location) { 239 m_register_locations[reg_num] = register_location; 240 } 241 242 bool UnwindPlan::Row::SetRegisterLocationToAtCFAPlusOffset(uint32_t reg_num, 243 int32_t offset, 244 bool can_replace) { 245 if (!can_replace && 246 m_register_locations.find(reg_num) != m_register_locations.end()) 247 return false; 248 RegisterLocation reg_loc; 249 reg_loc.SetAtCFAPlusOffset(offset); 250 m_register_locations[reg_num] = reg_loc; 251 return true; 252 } 253 254 bool UnwindPlan::Row::SetRegisterLocationToIsCFAPlusOffset(uint32_t reg_num, 255 int32_t offset, 256 bool can_replace) { 257 if (!can_replace && 258 m_register_locations.find(reg_num) != m_register_locations.end()) 259 return false; 260 RegisterLocation reg_loc; 261 reg_loc.SetIsCFAPlusOffset(offset); 262 m_register_locations[reg_num] = reg_loc; 263 return true; 264 } 265 266 bool UnwindPlan::Row::SetRegisterLocationToUndefined( 267 uint32_t reg_num, bool can_replace, bool can_replace_only_if_unspecified) { 268 collection::iterator pos = m_register_locations.find(reg_num); 269 collection::iterator end = m_register_locations.end(); 270 271 if (pos != end) { 272 if (!can_replace) 273 return false; 274 if (can_replace_only_if_unspecified && !pos->second.IsUnspecified()) 275 return false; 276 } 277 RegisterLocation reg_loc; 278 reg_loc.SetUndefined(); 279 m_register_locations[reg_num] = reg_loc; 280 return true; 281 } 282 283 bool UnwindPlan::Row::SetRegisterLocationToUnspecified(uint32_t reg_num, 284 bool can_replace) { 285 if (!can_replace && 286 m_register_locations.find(reg_num) != m_register_locations.end()) 287 return false; 288 RegisterLocation reg_loc; 289 reg_loc.SetUnspecified(); 290 m_register_locations[reg_num] = reg_loc; 291 return true; 292 } 293 294 bool UnwindPlan::Row::SetRegisterLocationToRegister(uint32_t reg_num, 295 uint32_t other_reg_num, 296 bool can_replace) { 297 if (!can_replace && 298 m_register_locations.find(reg_num) != m_register_locations.end()) 299 return false; 300 RegisterLocation reg_loc; 301 reg_loc.SetInRegister(other_reg_num); 302 m_register_locations[reg_num] = reg_loc; 303 return true; 304 } 305 306 bool UnwindPlan::Row::SetRegisterLocationToSame(uint32_t reg_num, 307 bool must_replace) { 308 if (must_replace && 309 m_register_locations.find(reg_num) == m_register_locations.end()) 310 return false; 311 RegisterLocation reg_loc; 312 reg_loc.SetSame(); 313 m_register_locations[reg_num] = reg_loc; 314 return true; 315 } 316 317 bool UnwindPlan::Row::operator==(const UnwindPlan::Row &rhs) const { 318 return m_offset == rhs.m_offset && 319 m_cfa_value == rhs.m_cfa_value && 320 m_afa_value == rhs.m_afa_value && 321 m_register_locations == rhs.m_register_locations; 322 } 323 324 void UnwindPlan::AppendRow(const UnwindPlan::RowSP &row_sp) { 325 if (m_row_list.empty() || 326 m_row_list.back()->GetOffset() != row_sp->GetOffset()) 327 m_row_list.push_back(row_sp); 328 else 329 m_row_list.back() = row_sp; 330 } 331 332 void UnwindPlan::InsertRow(const UnwindPlan::RowSP &row_sp, 333 bool replace_existing) { 334 collection::iterator it = m_row_list.begin(); 335 while (it != m_row_list.end()) { 336 RowSP row = *it; 337 if (row->GetOffset() >= row_sp->GetOffset()) 338 break; 339 it++; 340 } 341 if (it == m_row_list.end() || (*it)->GetOffset() != row_sp->GetOffset()) 342 m_row_list.insert(it, row_sp); 343 else if (replace_existing) 344 *it = row_sp; 345 } 346 347 UnwindPlan::RowSP UnwindPlan::GetRowForFunctionOffset(int offset) const { 348 RowSP row; 349 if (!m_row_list.empty()) { 350 if (offset == -1) 351 row = m_row_list.back(); 352 else { 353 collection::const_iterator pos, end = m_row_list.end(); 354 for (pos = m_row_list.begin(); pos != end; ++pos) { 355 if ((*pos)->GetOffset() <= static_cast<lldb::offset_t>(offset)) 356 row = *pos; 357 else 358 break; 359 } 360 } 361 } 362 return row; 363 } 364 365 bool UnwindPlan::IsValidRowIndex(uint32_t idx) const { 366 return idx < m_row_list.size(); 367 } 368 369 const UnwindPlan::RowSP UnwindPlan::GetRowAtIndex(uint32_t idx) const { 370 if (idx < m_row_list.size()) 371 return m_row_list[idx]; 372 else { 373 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); 374 if (log) 375 log->Printf("error: UnwindPlan::GetRowAtIndex(idx = %u) invalid index " 376 "(number rows is %u)", 377 idx, (uint32_t)m_row_list.size()); 378 return UnwindPlan::RowSP(); 379 } 380 } 381 382 const UnwindPlan::RowSP UnwindPlan::GetLastRow() const { 383 if (m_row_list.empty()) { 384 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); 385 if (log) 386 log->Printf("UnwindPlan::GetLastRow() when rows are empty"); 387 return UnwindPlan::RowSP(); 388 } 389 return m_row_list.back(); 390 } 391 392 int UnwindPlan::GetRowCount() const { return m_row_list.size(); } 393 394 void UnwindPlan::SetPlanValidAddressRange(const AddressRange &range) { 395 if (range.GetBaseAddress().IsValid() && range.GetByteSize() != 0) 396 m_plan_valid_address_range = range; 397 } 398 399 bool UnwindPlan::PlanValidAtAddress(Address addr) { 400 // If this UnwindPlan has no rows, it is an invalid UnwindPlan. 401 if (GetRowCount() == 0) { 402 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); 403 if (log) { 404 StreamString s; 405 if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) { 406 log->Printf("UnwindPlan is invalid -- no unwind rows for UnwindPlan " 407 "'%s' at address %s", 408 m_source_name.GetCString(), s.GetData()); 409 } else { 410 log->Printf( 411 "UnwindPlan is invalid -- no unwind rows for UnwindPlan '%s'", 412 m_source_name.GetCString()); 413 } 414 } 415 return false; 416 } 417 418 // If the 0th Row of unwind instructions is missing, or if it doesn't provide 419 // a register to use to find the Canonical Frame Address, this is not a valid 420 // UnwindPlan. 421 if (GetRowAtIndex(0).get() == nullptr || 422 GetRowAtIndex(0)->GetCFAValue().GetValueType() == 423 Row::FAValue::unspecified) { 424 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); 425 if (log) { 426 StreamString s; 427 if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) { 428 log->Printf("UnwindPlan is invalid -- no CFA register defined in row 0 " 429 "for UnwindPlan '%s' at address %s", 430 m_source_name.GetCString(), s.GetData()); 431 } else { 432 log->Printf("UnwindPlan is invalid -- no CFA register defined in row 0 " 433 "for UnwindPlan '%s'", 434 m_source_name.GetCString()); 435 } 436 } 437 return false; 438 } 439 440 if (!m_plan_valid_address_range.GetBaseAddress().IsValid() || 441 m_plan_valid_address_range.GetByteSize() == 0) 442 return true; 443 444 if (!addr.IsValid()) 445 return true; 446 447 if (m_plan_valid_address_range.ContainsFileAddress(addr)) 448 return true; 449 450 return false; 451 } 452 453 void UnwindPlan::Dump(Stream &s, Thread *thread, lldb::addr_t base_addr) const { 454 if (!m_source_name.IsEmpty()) { 455 s.Printf("This UnwindPlan originally sourced from %s\n", 456 m_source_name.GetCString()); 457 } 458 if (m_lsda_address.IsValid() && m_personality_func_addr.IsValid()) { 459 TargetSP target_sp(thread->CalculateTarget()); 460 addr_t lsda_load_addr = m_lsda_address.GetLoadAddress(target_sp.get()); 461 addr_t personality_func_load_addr = 462 m_personality_func_addr.GetLoadAddress(target_sp.get()); 463 464 if (lsda_load_addr != LLDB_INVALID_ADDRESS && 465 personality_func_load_addr != LLDB_INVALID_ADDRESS) { 466 s.Printf("LSDA address 0x%" PRIx64 467 ", personality routine is at address 0x%" PRIx64 "\n", 468 lsda_load_addr, personality_func_load_addr); 469 } 470 } 471 s.Printf("This UnwindPlan is sourced from the compiler: "); 472 switch (m_plan_is_sourced_from_compiler) { 473 case eLazyBoolYes: 474 s.Printf("yes.\n"); 475 break; 476 case eLazyBoolNo: 477 s.Printf("no.\n"); 478 break; 479 case eLazyBoolCalculate: 480 s.Printf("not specified.\n"); 481 break; 482 } 483 s.Printf("This UnwindPlan is valid at all instruction locations: "); 484 switch (m_plan_is_valid_at_all_instruction_locations) { 485 case eLazyBoolYes: 486 s.Printf("yes.\n"); 487 break; 488 case eLazyBoolNo: 489 s.Printf("no.\n"); 490 break; 491 case eLazyBoolCalculate: 492 s.Printf("not specified.\n"); 493 break; 494 } 495 if (m_plan_valid_address_range.GetBaseAddress().IsValid() && 496 m_plan_valid_address_range.GetByteSize() > 0) { 497 s.PutCString("Address range of this UnwindPlan: "); 498 TargetSP target_sp(thread->CalculateTarget()); 499 m_plan_valid_address_range.Dump(&s, target_sp.get(), 500 Address::DumpStyleSectionNameOffset); 501 s.EOL(); 502 } 503 collection::const_iterator pos, begin = m_row_list.begin(), 504 end = m_row_list.end(); 505 for (pos = begin; pos != end; ++pos) { 506 s.Printf("row[%u]: ", (uint32_t)std::distance(begin, pos)); 507 (*pos)->Dump(s, this, thread, base_addr); 508 } 509 } 510 511 void UnwindPlan::SetSourceName(const char *source) { 512 m_source_name = ConstString(source); 513 } 514 515 ConstString UnwindPlan::GetSourceName() const { return m_source_name; } 516 517 const RegisterInfo *UnwindPlan::GetRegisterInfo(Thread *thread, 518 uint32_t unwind_reg) const { 519 if (thread) { 520 RegisterContext *reg_ctx = thread->GetRegisterContext().get(); 521 if (reg_ctx) { 522 uint32_t reg; 523 if (m_register_kind == eRegisterKindLLDB) 524 reg = unwind_reg; 525 else 526 reg = reg_ctx->ConvertRegisterKindToRegisterNumber(m_register_kind, 527 unwind_reg); 528 if (reg != LLDB_INVALID_REGNUM) 529 return reg_ctx->GetRegisterInfoAtIndex(reg); 530 } 531 } 532 return nullptr; 533 } 534