1 /* 2 * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2012-2016, Rene Gollent, rene@gollent.com. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include "DwarfImageDebugInfo.h" 9 10 #include <errno.h> 11 #include <stdio.h> 12 #include <unistd.h> 13 14 #include <algorithm> 15 #include <new> 16 17 #include <AutoDeleter.h> 18 #include <AutoLocker.h> 19 20 #include "Architecture.h" 21 #include "BasicFunctionDebugInfo.h" 22 #include "CLanguage.h" 23 #include "CompilationUnit.h" 24 #include "CppLanguage.h" 25 #include "CpuState.h" 26 #include "DebuggerInterface.h" 27 #include "DebugInfoEntries.h" 28 #include "Demangler.h" 29 #include "DisassembledCode.h" 30 #include "Dwarf.h" 31 #include "DwarfFile.h" 32 #include "DwarfFunctionDebugInfo.h" 33 #include "DwarfStackFrameDebugInfo.h" 34 #include "DwarfTargetInterface.h" 35 #include "DwarfTypeFactory.h" 36 #include "DwarfTypes.h" 37 #include "DwarfUtils.h" 38 #include "ElfFile.h" 39 #include "FileManager.h" 40 #include "FileSourceCode.h" 41 #include "FunctionID.h" 42 #include "FunctionInstance.h" 43 #include "GlobalTypeLookup.h" 44 #include "Image.h" 45 #include "ImageDebugInfo.h" 46 #include "InstructionInfo.h" 47 #include "LocatableFile.h" 48 #include "Register.h" 49 #include "RegisterMap.h" 50 #include "SourceFile.h" 51 #include "StackFrame.h" 52 #include "Statement.h" 53 #include "StringUtils.h" 54 #include "SymbolInfo.h" 55 #include "TargetAddressRangeList.h" 56 #include "Team.h" 57 #include "TeamFunctionSourceInformation.h" 58 #include "TeamMemory.h" 59 #include "Tracing.h" 60 #include "TypeLookupConstraints.h" 61 #include "UnsupportedLanguage.h" 62 #include "Variable.h" 63 #include "ValueLocation.h" 64 65 66 namespace { 67 68 69 // #pragma mark - HasTypePredicate 70 71 72 template<typename EntryType> 73 struct HasTypePredicate { 74 inline bool operator()(EntryType* entry) const 75 { 76 return entry->GetType() != NULL; 77 } 78 }; 79 80 } 81 82 83 // #pragma mark - BasicTargetInterface 84 85 86 struct DwarfImageDebugInfo::BasicTargetInterface : DwarfTargetInterface { 87 BasicTargetInterface(const Register* registers, int32 registerCount, 88 RegisterMap* fromDwarfMap, Architecture* architecture, 89 TeamMemory* teamMemory) 90 : 91 fRegisters(registers), 92 fRegisterCount(registerCount), 93 fFromDwarfMap(fromDwarfMap), 94 fArchitecture(architecture), 95 fTeamMemory(teamMemory) 96 { 97 fFromDwarfMap->AcquireReference(); 98 } 99 100 ~BasicTargetInterface() 101 { 102 fFromDwarfMap->ReleaseReference(); 103 } 104 105 virtual uint32 CountRegisters() const 106 { 107 return fRegisterCount; 108 } 109 110 virtual uint32 RegisterValueType(uint32 index) const 111 { 112 const Register* reg = _RegisterAt(index); 113 return reg != NULL ? reg->ValueType() : 0; 114 } 115 116 virtual bool GetRegisterValue(uint32 index, BVariant& _value) const 117 { 118 return false; 119 } 120 121 virtual bool SetRegisterValue(uint32 index, const BVariant& value) 122 { 123 return false; 124 } 125 126 virtual bool IsCalleePreservedRegister(uint32 index) const 127 { 128 const Register* reg = _RegisterAt(index); 129 return reg != NULL && reg->IsCalleePreserved(); 130 } 131 132 virtual status_t InitRegisterRules(CfaContext& context) const 133 { 134 return fArchitecture->InitRegisterRules(context); 135 } 136 137 virtual bool ReadMemory(target_addr_t address, void* buffer, 138 size_t size) const 139 { 140 ssize_t bytesRead = fTeamMemory->ReadMemory(address, buffer, size); 141 return bytesRead >= 0 && (size_t)bytesRead == size; 142 } 143 144 virtual bool ReadValueFromMemory(target_addr_t address, 145 uint32 valueType, BVariant& _value) const 146 { 147 return fArchitecture->ReadValueFromMemory(address, valueType, _value) 148 == B_OK; 149 } 150 151 virtual bool ReadValueFromMemory(target_addr_t addressSpace, 152 target_addr_t address, uint32 valueType, BVariant& _value) const 153 { 154 return fArchitecture->ReadValueFromMemory(addressSpace, address, 155 valueType, _value) == B_OK; 156 } 157 158 protected: 159 const Register* _RegisterAt(uint32 dwarfIndex) const 160 { 161 int32 index = fFromDwarfMap->MapRegisterIndex(dwarfIndex); 162 return index >= 0 && index < fRegisterCount ? fRegisters + index : NULL; 163 } 164 165 protected: 166 const Register* fRegisters; 167 int32 fRegisterCount; 168 RegisterMap* fFromDwarfMap; 169 Architecture* fArchitecture; 170 TeamMemory* fTeamMemory; 171 }; 172 173 174 // #pragma mark - UnwindTargetInterface 175 176 177 struct DwarfImageDebugInfo::UnwindTargetInterface : BasicTargetInterface { 178 UnwindTargetInterface(const Register* registers, int32 registerCount, 179 RegisterMap* fromDwarfMap, RegisterMap* toDwarfMap, CpuState* cpuState, 180 Architecture* architecture, TeamMemory* teamMemory) 181 : 182 BasicTargetInterface(registers, registerCount, fromDwarfMap, 183 architecture, teamMemory), 184 fToDwarfMap(toDwarfMap), 185 fCpuState(cpuState) 186 { 187 fToDwarfMap->AcquireReference(); 188 fCpuState->AcquireReference(); 189 } 190 191 ~UnwindTargetInterface() 192 { 193 fToDwarfMap->ReleaseReference(); 194 fCpuState->ReleaseReference(); 195 } 196 197 virtual bool GetRegisterValue(uint32 index, BVariant& _value) const 198 { 199 const Register* reg = _RegisterAt(index); 200 if (reg == NULL) 201 return false; 202 return fCpuState->GetRegisterValue(reg, _value); 203 } 204 205 virtual bool SetRegisterValue(uint32 index, const BVariant& value) 206 { 207 const Register* reg = _RegisterAt(index); 208 if (reg == NULL) 209 return false; 210 return fCpuState->SetRegisterValue(reg, value); 211 } 212 213 private: 214 RegisterMap* fToDwarfMap; 215 CpuState* fCpuState; 216 }; 217 218 219 // #pragma mark - EntryListWrapper 220 221 222 /*! Wraps a DebugInfoEntryList, which is a typedef and thus cannot appear in 223 the header, since our policy disallows us to include DWARF headers there. 224 */ 225 struct DwarfImageDebugInfo::EntryListWrapper { 226 const DebugInfoEntryList& list; 227 228 EntryListWrapper(const DebugInfoEntryList& list) 229 : 230 list(list) 231 { 232 } 233 }; 234 235 236 // #pragma mark - DwarfImageDebugInfo::TypeNameKey 237 238 239 struct DwarfImageDebugInfo::TypeNameKey { 240 BString typeName; 241 242 TypeNameKey(const BString& typeName) 243 : 244 typeName(typeName) 245 { 246 } 247 248 uint32 HashValue() const 249 { 250 return StringUtils::HashValue(typeName); 251 } 252 253 bool operator==(const TypeNameKey& other) const 254 { 255 return typeName == other.typeName; 256 } 257 }; 258 259 260 // #pragma mark - DwarfImageDebugInfo::TypeNameEntry 261 262 263 struct DwarfImageDebugInfo::TypeNameEntry : TypeNameKey { 264 TypeNameEntry* next; 265 TypeEntryList types; 266 267 TypeNameEntry(const BString& name) 268 : 269 TypeNameKey(name), 270 types(10, true) 271 { 272 } 273 274 ~TypeNameEntry() 275 { 276 } 277 278 }; 279 280 281 // #pragma mark - DwarfImageDebugInfo::TypeNameEntryHashDefinition 282 283 284 struct DwarfImageDebugInfo::TypeNameEntryHashDefinition { 285 typedef TypeNameKey KeyType; 286 typedef TypeNameEntry ValueType; 287 288 size_t HashKey(const TypeNameKey& key) const 289 { 290 return key.HashValue(); 291 } 292 293 size_t Hash(const TypeNameEntry* value) const 294 { 295 return value->HashValue(); 296 } 297 298 bool Compare(const TypeNameKey& key, 299 const TypeNameEntry* value) const 300 { 301 return key == *value; 302 } 303 304 TypeNameEntry*& GetLink(TypeNameEntry* value) const 305 { 306 return value->next; 307 } 308 }; 309 310 311 // #pragma mark - DwarfImageDebugInfo::TypeEntryInfo 312 313 314 struct DwarfImageDebugInfo::TypeEntryInfo { 315 DIEType* type; 316 CompilationUnit* unit; 317 318 TypeEntryInfo(DIEType* type, CompilationUnit* unit) 319 : 320 type(type), 321 unit(unit) 322 { 323 } 324 }; 325 326 327 // #pragma mark - DwarfImageDebugInfo 328 329 330 DwarfImageDebugInfo::DwarfImageDebugInfo(const ImageInfo& imageInfo, 331 DebuggerInterface* interface, Architecture* architecture, 332 FileManager* fileManager, GlobalTypeLookup* typeLookup, 333 GlobalTypeCache* typeCache, TeamFunctionSourceInformation* sourceInfo, 334 DwarfFile* file) 335 : 336 fLock("dwarf image debug info"), 337 fImageInfo(imageInfo), 338 fDebuggerInterface(interface), 339 fArchitecture(architecture), 340 fFileManager(fileManager), 341 fTypeLookup(typeLookup), 342 fTypeCache(typeCache), 343 fSourceInfo(sourceInfo), 344 fTypeNameTable(NULL), 345 fFile(file), 346 fTextSegment(NULL), 347 fRelocationDelta(0), 348 fTextSectionStart(0), 349 fTextSectionEnd(0), 350 fPLTSectionStart(0), 351 fPLTSectionEnd(0) 352 { 353 fDebuggerInterface->AcquireReference(); 354 fFile->AcquireReference(); 355 fTypeCache->AcquireReference(); 356 } 357 358 359 DwarfImageDebugInfo::~DwarfImageDebugInfo() 360 { 361 fDebuggerInterface->ReleaseReference(); 362 fFile->ReleaseReference(); 363 fTypeCache->ReleaseReference(); 364 365 TypeNameEntry* entry = fTypeNameTable->Clear(true); 366 while (entry != NULL) { 367 TypeNameEntry* next = entry->next; 368 delete entry; 369 entry = next; 370 } 371 delete fTypeNameTable; 372 } 373 374 375 status_t 376 DwarfImageDebugInfo::Init() 377 { 378 status_t error = fLock.InitCheck(); 379 if (error != B_OK) 380 return error; 381 382 fTextSegment = fFile->GetElfFile()->TextSegment(); 383 if (fTextSegment == NULL) 384 return B_ENTRY_NOT_FOUND; 385 386 fRelocationDelta = fImageInfo.TextBase() - fTextSegment->LoadAddress(); 387 388 ElfSection* section = fFile->GetElfFile()->FindSection(".text"); 389 if (section != NULL) { 390 fTextSectionStart = section->LoadAddress() + fRelocationDelta; 391 fTextSectionEnd = fTextSectionStart + section->Size(); 392 } 393 394 section = fFile->GetElfFile()->FindSection(".plt"); 395 if (section != NULL) { 396 fPLTSectionStart = section->LoadAddress() + fRelocationDelta; 397 fPLTSectionEnd = fPLTSectionStart + section->Size(); 398 } 399 400 return _BuildTypeNameTable(); 401 } 402 403 404 status_t 405 DwarfImageDebugInfo::GetFunctions(const BObjectList<SymbolInfo>& symbols, 406 BObjectList<FunctionDebugInfo>& functions) 407 { 408 TRACE_IMAGES("DwarfImageDebugInfo::GetFunctions()\n"); 409 TRACE_IMAGES(" %" B_PRId32 " compilation units\n", 410 fFile->CountCompilationUnits()); 411 412 for (int32 i = 0; CompilationUnit* unit = fFile->CompilationUnitAt(i); 413 i++) { 414 DIECompileUnitBase* unitEntry = unit->UnitEntry(); 415 // printf(" %s:\n", unitEntry->Name()); 416 // printf(" address ranges:\n"); 417 // TargetAddressRangeList* rangeList = unitEntry->AddressRanges(); 418 // if (rangeList != NULL) { 419 // int32 count = rangeList->CountRanges(); 420 // for (int32 i = 0; i < count; i++) { 421 // TargetAddressRange range = rangeList->RangeAt(i); 422 // printf(" %#llx - %#llx\n", range.Start(), range.End()); 423 // } 424 // } else { 425 // printf(" %#llx - %#llx\n", (target_addr_t)unitEntry->LowPC(), 426 // (target_addr_t)unitEntry->HighPC()); 427 // } 428 429 // printf(" functions:\n"); 430 for (DebugInfoEntryList::ConstIterator it 431 = unitEntry->OtherChildren().GetIterator(); 432 DebugInfoEntry* entry = it.Next();) { 433 if (entry->Tag() != DW_TAG_subprogram) 434 continue; 435 436 DIESubprogram* subprogramEntry = static_cast<DIESubprogram*>(entry); 437 438 // ignore declarations and inlined functions 439 if (subprogramEntry->IsDeclaration() 440 || subprogramEntry->Inline() == DW_INL_inlined 441 || subprogramEntry->Inline() == DW_INL_declared_inlined) { 442 continue; 443 } 444 445 // get the name 446 BString name; 447 DwarfUtils::GetFullyQualifiedDIEName(subprogramEntry, name); 448 if (name.Length() == 0) 449 continue; 450 451 // get the address ranges 452 TargetAddressRangeList* rangeList = fFile->ResolveRangeList(unit, 453 subprogramEntry->AddressRangesOffset()); 454 if (rangeList == NULL) { 455 target_addr_t lowPC = subprogramEntry->LowPC(); 456 target_addr_t highPC = subprogramEntry->HighPC(); 457 if (highPC <= lowPC) 458 continue; 459 460 rangeList = new(std::nothrow) TargetAddressRangeList( 461 TargetAddressRange(lowPC, highPC - lowPC)); 462 if (rangeList == NULL) 463 return B_NO_MEMORY; 464 // TODO: Clean up already added functions! 465 } 466 BReference<TargetAddressRangeList> rangeListReference(rangeList, 467 true); 468 469 // get the source location 470 const char* directoryPath = NULL; 471 const char* fileName = NULL; 472 int32 line = -1; 473 int32 column = -1; 474 DwarfUtils::GetDeclarationLocation(fFile, subprogramEntry, 475 directoryPath, fileName, line, column); 476 477 LocatableFile* file = NULL; 478 if (fileName != NULL) { 479 file = fFileManager->GetSourceFile(directoryPath, 480 fileName); 481 } 482 BReference<LocatableFile> fileReference(file, true); 483 484 // create and add the functions 485 DwarfFunctionDebugInfo* function 486 = new(std::nothrow) DwarfFunctionDebugInfo(this, unit, 487 subprogramEntry, rangeList, name, file, 488 SourceLocation(line, std::max(column, (int32)0))); 489 if (function == NULL || !functions.AddItem(function)) { 490 delete function; 491 return B_NO_MEMORY; 492 // TODO: Clean up already added functions! 493 } 494 495 // BString name; 496 // DwarfUtils::GetFullyQualifiedDIEName(subprogramEntry, name); 497 // printf(" subprogram entry: %p, name: %s, declaration: %d\n", 498 // subprogramEntry, name.String(), 499 // subprogramEntry->IsDeclaration()); 500 // 501 // rangeList = subprogramEntry->AddressRanges(); 502 // if (rangeList != NULL) { 503 // int32 count = rangeList->CountRanges(); 504 // for (int32 i = 0; i < count; i++) { 505 // TargetAddressRange range = rangeList->RangeAt(i); 506 // printf(" %#llx - %#llx\n", range.Start(), range.End()); 507 // } 508 // } else { 509 // printf(" %#llx - %#llx\n", 510 // (target_addr_t)subprogramEntry->LowPC(), 511 // (target_addr_t)subprogramEntry->HighPC()); 512 // } 513 } 514 } 515 516 if (fFile->CountCompilationUnits() != 0) 517 return B_OK; 518 519 // if we had no compilation units, fall back to providing basic 520 // debug infos with DWARF-supported call frame unwinding, 521 // if available. 522 if (fFile->HasFrameInformation()) { 523 return SpecificImageDebugInfo::GetFunctionsFromSymbols(symbols, 524 functions, fDebuggerInterface, fImageInfo, this); 525 } 526 527 return B_OK; 528 } 529 530 531 status_t 532 DwarfImageDebugInfo::GetType(GlobalTypeCache* cache, const BString& name, 533 const TypeLookupConstraints& constraints, Type*& _type) 534 { 535 TypeNameEntry* entry = fTypeNameTable->Lookup(name); 536 if (entry == NULL) 537 return B_ENTRY_NOT_FOUND; 538 539 for (int32 i = 0; TypeEntryInfo* info = entry->types.ItemAt(i); i++) { 540 DIEType* typeEntry = info->type; 541 if (constraints.HasTypeKind()) { 542 if (dwarf_tag_to_type_kind(typeEntry->Tag()) 543 != constraints.TypeKind()) { 544 continue; 545 } 546 547 if (!_EvaluateBaseTypeConstraints(typeEntry, constraints)) 548 continue; 549 } 550 551 if (constraints.HasSubtypeKind() 552 && dwarf_tag_to_subtype_kind(typeEntry->Tag()) 553 != constraints.SubtypeKind()) { 554 continue; 555 } 556 557 DwarfTypeContext* typeContext = new(std::nothrow) 558 DwarfTypeContext(fArchitecture, fImageInfo.ImageID(), fFile, 559 info->unit, NULL, 0, 0, fRelocationDelta, NULL, NULL); 560 if (typeContext == NULL) 561 return B_NO_MEMORY; 562 BReference<DwarfTypeContext> typeContextReference(typeContext, true); 563 564 // create the type 565 DwarfType* type; 566 DwarfTypeFactory typeFactory(typeContext, fTypeLookup, cache); 567 status_t error = typeFactory.CreateType(typeEntry, type); 568 if (error != B_OK) 569 continue; 570 571 _type = type; 572 return B_OK; 573 } 574 575 return B_ENTRY_NOT_FOUND; 576 } 577 578 579 bool 580 DwarfImageDebugInfo::HasType(const BString& name, 581 const TypeLookupConstraints& constraints) const 582 { 583 TypeNameEntry* entry = fTypeNameTable->Lookup(name); 584 if (entry == NULL) 585 return false; 586 587 for (int32 i = 0; TypeEntryInfo* info = entry->types.ItemAt(i); i++) { 588 DIEType* typeEntry = info->type; 589 if (constraints.HasTypeKind()) { 590 if (dwarf_tag_to_type_kind(typeEntry->Tag()) 591 != constraints.TypeKind()) { 592 continue; 593 } 594 595 if (!_EvaluateBaseTypeConstraints(typeEntry, constraints)) 596 continue; 597 } 598 599 if (constraints.HasSubtypeKind() 600 && dwarf_tag_to_subtype_kind(typeEntry->Tag()) 601 != constraints.SubtypeKind()) { 602 continue; 603 } 604 605 return true; 606 } 607 608 return false; 609 } 610 611 612 AddressSectionType 613 DwarfImageDebugInfo::GetAddressSectionType(target_addr_t address) 614 { 615 if (address >= fTextSectionStart && address < fTextSectionEnd) 616 return ADDRESS_SECTION_TYPE_FUNCTION; 617 618 if (address >= fPLTSectionStart && address < fPLTSectionEnd) 619 return ADDRESS_SECTION_TYPE_PLT; 620 621 return ADDRESS_SECTION_TYPE_UNKNOWN; 622 } 623 624 625 status_t 626 DwarfImageDebugInfo::CreateFrame(Image* image, 627 FunctionInstance* functionInstance, CpuState* cpuState, 628 bool getFullFrameInfo, ReturnValueInfoList* returnValueInfos, 629 StackFrame*& _frame, CpuState*& _previousCpuState) 630 { 631 DwarfFunctionDebugInfo* function = dynamic_cast<DwarfFunctionDebugInfo*>( 632 functionInstance->GetFunctionDebugInfo()); 633 634 FunctionID* functionID = functionInstance->GetFunctionID(); 635 BReference<FunctionID> functionIDReference; 636 if (functionID != NULL) 637 functionIDReference.SetTo(functionID, true); 638 639 DIESubprogram* entry = function != NULL 640 ? function->SubprogramEntry() : NULL; 641 642 TRACE_CFI("DwarfImageDebugInfo::CreateFrame(): subprogram DIE: %p, " 643 "function: %s\n", entry, 644 functionID->FunctionName().String()); 645 646 int32 registerCount = fArchitecture->CountRegisters(); 647 const Register* registers = fArchitecture->Registers(); 648 649 // get the DWARF <-> architecture register maps 650 RegisterMap* toDwarfMap; 651 RegisterMap* fromDwarfMap; 652 status_t error = fArchitecture->GetDwarfRegisterMaps(&toDwarfMap, 653 &fromDwarfMap); 654 if (error != B_OK) 655 return error; 656 BReference<RegisterMap> toDwarfMapReference(toDwarfMap, true); 657 BReference<RegisterMap> fromDwarfMapReference(fromDwarfMap, true); 658 659 // create a clean CPU state for the previous frame 660 CpuState* previousCpuState; 661 error = fArchitecture->CreateCpuState(previousCpuState); 662 if (error != B_OK) 663 return error; 664 BReference<CpuState> previousCpuStateReference(previousCpuState, true); 665 666 // create the target interfaces 667 UnwindTargetInterface* inputInterface 668 = new(std::nothrow) UnwindTargetInterface(registers, registerCount, 669 fromDwarfMap, toDwarfMap, cpuState, fArchitecture, 670 fDebuggerInterface); 671 if (inputInterface == NULL) 672 return B_NO_MEMORY; 673 BReference<UnwindTargetInterface> inputInterfaceReference(inputInterface, 674 true); 675 676 UnwindTargetInterface* outputInterface 677 = new(std::nothrow) UnwindTargetInterface(registers, registerCount, 678 fromDwarfMap, toDwarfMap, previousCpuState, fArchitecture, 679 fDebuggerInterface); 680 if (outputInterface == NULL) 681 return B_NO_MEMORY; 682 BReference<UnwindTargetInterface> outputInterfaceReference(outputInterface, 683 true); 684 685 // do the unwinding 686 target_addr_t instructionPointer 687 = cpuState->InstructionPointer() - fRelocationDelta; 688 target_addr_t framePointer; 689 CompilationUnit* unit = function != NULL ? function->GetCompilationUnit() 690 : NULL; 691 error = fFile->UnwindCallFrame(unit, fArchitecture->AddressSize(), entry, 692 instructionPointer, inputInterface, outputInterface, framePointer); 693 694 if (error != B_OK) { 695 TRACE_CFI("Failed to unwind call frame: %s\n", strerror(error)); 696 return B_UNSUPPORTED; 697 } 698 699 TRACE_CFI_ONLY( 700 TRACE_CFI("unwound registers:\n"); 701 for (int32 i = 0; i < registerCount; i++) { 702 const Register* reg = registers + i; 703 BVariant value; 704 if (previousCpuState->GetRegisterValue(reg, value)) { 705 TRACE_CFI(" %3s: %#" B_PRIx64 "\n", reg->Name(), 706 value.ToUInt64()); 707 } else 708 TRACE_CFI(" %3s: undefined\n", reg->Name()); 709 } 710 ) 711 712 // create the stack frame debug info 713 DIESubprogram* subprogramEntry = function != NULL ? 714 function->SubprogramEntry() : NULL; 715 DwarfStackFrameDebugInfo* stackFrameDebugInfo 716 = new(std::nothrow) DwarfStackFrameDebugInfo(fArchitecture, 717 fImageInfo.ImageID(), fFile, unit, subprogramEntry, fTypeLookup, 718 fTypeCache, instructionPointer, framePointer, fRelocationDelta, 719 inputInterface, fromDwarfMap); 720 if (stackFrameDebugInfo == NULL) 721 return B_NO_MEMORY; 722 BReference<DwarfStackFrameDebugInfo> stackFrameDebugInfoReference( 723 stackFrameDebugInfo, true); 724 725 error = stackFrameDebugInfo->Init(); 726 if (error != B_OK) 727 return error; 728 729 // create the stack frame 730 StackFrame* frame = new(std::nothrow) StackFrame(STACK_FRAME_TYPE_STANDARD, 731 cpuState, framePointer, cpuState->InstructionPointer(), 732 stackFrameDebugInfo); 733 if (frame == NULL) 734 return B_NO_MEMORY; 735 BReference<StackFrame> frameReference(frame, true); 736 737 error = frame->Init(); 738 if (error != B_OK) 739 return error; 740 741 frame->SetReturnAddress(previousCpuState->InstructionPointer()); 742 // Note, this is correct, since we actually retrieved the return 743 // address. Our caller will fix the IP for us. 744 745 // The subprogram entry may not be available since this may be a case 746 // where .eh_frame was used to unwind the stack without other DWARF 747 // info being available. 748 if (subprogramEntry != NULL && getFullFrameInfo) { 749 // create function parameter objects 750 for (DebugInfoEntryList::ConstIterator it 751 = subprogramEntry->Parameters().GetIterator(); 752 DebugInfoEntry* entry = it.Next();) { 753 if (entry->Tag() != DW_TAG_formal_parameter) 754 continue; 755 756 BString parameterName; 757 DwarfUtils::GetDIEName(entry, parameterName); 758 if (parameterName.Length() == 0) 759 continue; 760 761 DIEFormalParameter* parameterEntry 762 = dynamic_cast<DIEFormalParameter*>(entry); 763 Variable* parameter; 764 if (stackFrameDebugInfo->CreateParameter(functionID, 765 parameterEntry, parameter) != B_OK) { 766 continue; 767 } 768 BReference<Variable> parameterReference(parameter, true); 769 770 if (!frame->AddParameter(parameter)) 771 return B_NO_MEMORY; 772 } 773 774 // create objects for the local variables 775 _CreateLocalVariables(unit, frame, functionID, *stackFrameDebugInfo, 776 instructionPointer, functionInstance->Address() - fRelocationDelta, 777 subprogramEntry->Variables(), subprogramEntry->Blocks()); 778 779 if (returnValueInfos != NULL && !returnValueInfos->IsEmpty()) { 780 _CreateReturnValues(returnValueInfos, image, frame, 781 *stackFrameDebugInfo); 782 } 783 } 784 785 _frame = frameReference.Detach(); 786 _previousCpuState = previousCpuStateReference.Detach(); 787 788 frame->SetPreviousCpuState(_previousCpuState); 789 790 return B_OK; 791 } 792 793 794 status_t 795 DwarfImageDebugInfo::GetStatement(FunctionDebugInfo* _function, 796 target_addr_t address, Statement*& _statement) 797 { 798 TRACE_CODE("DwarfImageDebugInfo::GetStatement(function: %p, address: %#" 799 B_PRIx64 ")\n", _function, address); 800 801 DwarfFunctionDebugInfo* function 802 = dynamic_cast<DwarfFunctionDebugInfo*>(_function); 803 if (function == NULL) { 804 TRACE_LINES(" -> no dwarf function\n"); 805 // fall back to assembly 806 return fArchitecture->GetStatement(function, address, _statement); 807 } 808 809 AutoLocker<BLocker> locker(fLock); 810 811 // check whether we have the source code 812 CompilationUnit* unit = function->GetCompilationUnit(); 813 LocatableFile* file = function->SourceFile(); 814 if (file == NULL) { 815 TRACE_CODE(" -> no source file\n"); 816 817 // no source code -- rather return the assembly statement 818 return fArchitecture->GetStatement(function, address, _statement); 819 } 820 821 SourceCode* sourceCode = NULL; 822 status_t error = fSourceInfo->GetActiveSourceCode(_function, sourceCode); 823 BReference<SourceCode> sourceReference(sourceCode, true); 824 if (error != B_OK || dynamic_cast<DisassembledCode*>(sourceCode) != NULL) { 825 // either no source code or disassembly is currently active (i.e. 826 // due to failing to locate the source file on disk or the user 827 // deliberately switching to disassembly view). 828 // return the assembly statement. 829 return fArchitecture->GetStatement(function, address, _statement); 830 } 831 832 // get the index of the source file in the compilation unit for cheaper 833 // comparison below 834 int32 fileIndex = _GetSourceFileIndex(unit, file); 835 836 // Get the statement by executing the line number program for the 837 // compilation unit. 838 LineNumberProgram& program = unit->GetLineNumberProgram(); 839 if (!program.IsValid()) { 840 TRACE_CODE(" -> no line number program\n"); 841 return B_BAD_DATA; 842 } 843 844 // adjust address 845 address -= fRelocationDelta; 846 847 LineNumberProgram::State state; 848 program.GetInitialState(state); 849 850 target_addr_t statementAddress = 0; 851 int32 statementLine = -1; 852 int32 statementColumn = -1; 853 while (program.GetNextRow(state)) { 854 // skip statements of other files 855 if (state.file != fileIndex) 856 continue; 857 858 if (statementAddress != 0 859 && (state.isStatement || state.isSequenceEnd)) { 860 target_addr_t endAddress = state.address; 861 if (address >= statementAddress && address < endAddress) { 862 ContiguousStatement* statement = new(std::nothrow) 863 ContiguousStatement( 864 SourceLocation(statementLine, statementColumn), 865 TargetAddressRange(fRelocationDelta + statementAddress, 866 endAddress - statementAddress)); 867 if (statement == NULL) 868 return B_NO_MEMORY; 869 870 _statement = statement; 871 return B_OK; 872 } 873 874 statementAddress = 0; 875 } 876 877 if (state.isStatement) { 878 statementAddress = state.address; 879 statementLine = state.line - 1; 880 statementColumn = std::max(state.column - 1, (int32)0); 881 } 882 } 883 884 TRACE_CODE(" -> no line number program match\n"); 885 return B_ENTRY_NOT_FOUND; 886 } 887 888 889 status_t 890 DwarfImageDebugInfo::GetStatementAtSourceLocation(FunctionDebugInfo* _function, 891 const SourceLocation& sourceLocation, Statement*& _statement) 892 { 893 DwarfFunctionDebugInfo* function 894 = dynamic_cast<DwarfFunctionDebugInfo*>(_function); 895 if (function == NULL) 896 return B_BAD_VALUE; 897 898 target_addr_t functionStartAddress = function->Address() - fRelocationDelta; 899 target_addr_t functionEndAddress = functionStartAddress + function->Size(); 900 901 TRACE_LINES2("DwarfImageDebugInfo::GetStatementAtSourceLocation(%p, " 902 "(%" B_PRId32 ", %" B_PRId32 ")): function range: %#" B_PRIx64 " - %#" 903 B_PRIx64 "\n", function, sourceLocation.Line(), sourceLocation.Column(), 904 functionStartAddress, functionEndAddress); 905 906 AutoLocker<BLocker> locker(fLock); 907 908 // get the source file 909 LocatableFile* file = function->SourceFile(); 910 if (file == NULL) 911 return B_ENTRY_NOT_FOUND; 912 913 CompilationUnit* unit = function->GetCompilationUnit(); 914 915 // get the index of the source file in the compilation unit for cheaper 916 // comparison below 917 int32 fileIndex = _GetSourceFileIndex(unit, file); 918 919 // Get the statement by executing the line number program for the 920 // compilation unit. 921 LineNumberProgram& program = unit->GetLineNumberProgram(); 922 if (!program.IsValid()) 923 return B_BAD_DATA; 924 925 LineNumberProgram::State state; 926 program.GetInitialState(state); 927 928 target_addr_t statementAddress = 0; 929 int32 statementLine = -1; 930 int32 statementColumn = -1; 931 while (program.GetNextRow(state)) { 932 bool isOurFile = state.file == fileIndex; 933 934 if (statementAddress != 0 935 && (!isOurFile || state.isStatement || state.isSequenceEnd)) { 936 target_addr_t endAddress = state.address; 937 938 if (statementAddress < endAddress) { 939 TRACE_LINES2(" statement: %#" B_PRIx64 " - %#" B_PRIx64 940 ", location: (%" B_PRId32 ", %" B_PRId32 ")\n", 941 statementAddress, endAddress, statementLine, 942 statementColumn); 943 } 944 945 if (statementAddress < endAddress 946 && statementAddress >= functionStartAddress 947 && statementAddress < functionEndAddress 948 && statementLine == (int32)sourceLocation.Line() 949 && statementColumn == (int32)sourceLocation.Column()) { 950 TRACE_LINES2(" -> found statement!\n"); 951 952 ContiguousStatement* statement = new(std::nothrow) 953 ContiguousStatement( 954 SourceLocation(statementLine, statementColumn), 955 TargetAddressRange(fRelocationDelta + statementAddress, 956 endAddress - statementAddress)); 957 if (statement == NULL) 958 return B_NO_MEMORY; 959 960 _statement = statement; 961 return B_OK; 962 } 963 964 statementAddress = 0; 965 } 966 967 // skip statements of other files 968 if (!isOurFile) 969 continue; 970 971 if (state.isStatement) { 972 statementAddress = state.address; 973 statementLine = state.line - 1; 974 statementColumn = std::max(state.column - 1, (int32)0); 975 } 976 } 977 978 return B_ENTRY_NOT_FOUND; 979 } 980 981 982 status_t 983 DwarfImageDebugInfo::GetSourceLanguage(FunctionDebugInfo* _function, 984 SourceLanguage*& _language) 985 { 986 DwarfFunctionDebugInfo* function 987 = dynamic_cast<DwarfFunctionDebugInfo*>(_function); 988 if (function == NULL) 989 return B_BAD_VALUE; 990 991 SourceLanguage* language; 992 CompilationUnit* unit = function->GetCompilationUnit(); 993 switch (unit->UnitEntry()->Language()) { 994 case DW_LANG_C89: 995 case DW_LANG_C: 996 case DW_LANG_C99: 997 language = new(std::nothrow) CLanguage; 998 break; 999 case DW_LANG_C_plus_plus: 1000 language = new(std::nothrow) CppLanguage; 1001 break; 1002 case 0: 1003 default: 1004 language = new(std::nothrow) UnsupportedLanguage; 1005 break; 1006 } 1007 1008 if (language == NULL) 1009 return B_NO_MEMORY; 1010 1011 _language = language; 1012 return B_OK; 1013 } 1014 1015 1016 ssize_t 1017 DwarfImageDebugInfo::ReadCode(target_addr_t address, void* buffer, size_t size) 1018 { 1019 target_addr_t offset = address - fRelocationDelta 1020 - fTextSegment->LoadAddress() + fTextSegment->FileOffset(); 1021 ssize_t bytesRead = pread(fFile->GetElfFile()->FD(), buffer, size, offset); 1022 return bytesRead >= 0 ? bytesRead : errno; 1023 } 1024 1025 1026 status_t 1027 DwarfImageDebugInfo::AddSourceCodeInfo(LocatableFile* file, 1028 FileSourceCode* sourceCode) 1029 { 1030 bool addedAny = false; 1031 for (int32 i = 0; CompilationUnit* unit = fFile->CompilationUnitAt(i); 1032 i++) { 1033 int32 fileIndex = _GetSourceFileIndex(unit, file); 1034 if (fileIndex < 0) 1035 continue; 1036 1037 status_t error = _AddSourceCodeInfo(unit, sourceCode, fileIndex); 1038 if (error == B_NO_MEMORY) 1039 return error; 1040 addedAny |= error == B_OK; 1041 } 1042 1043 return addedAny ? B_OK : B_ENTRY_NOT_FOUND; 1044 } 1045 1046 1047 status_t 1048 DwarfImageDebugInfo::_AddSourceCodeInfo(CompilationUnit* unit, 1049 FileSourceCode* sourceCode, int32 fileIndex) 1050 { 1051 // Get the statements by executing the line number program for the 1052 // compilation unit and filtering the rows for our source file. 1053 LineNumberProgram& program = unit->GetLineNumberProgram(); 1054 if (!program.IsValid()) 1055 return B_BAD_DATA; 1056 1057 LineNumberProgram::State state; 1058 program.GetInitialState(state); 1059 1060 target_addr_t statementAddress = 0; 1061 int32 statementLine = -1; 1062 int32 statementColumn = -1; 1063 while (program.GetNextRow(state)) { 1064 TRACE_LINES2(" %#" B_PRIx64 " (%" B_PRId32 ", %" B_PRId32 ", %" 1065 B_PRId32 ") %d\n", state.address, state.file, state.line, 1066 state.column, state.isStatement); 1067 1068 bool isOurFile = state.file == fileIndex; 1069 1070 if (statementAddress != 0 1071 && (!isOurFile || state.isStatement || state.isSequenceEnd)) { 1072 target_addr_t endAddress = state.address; 1073 if (endAddress > statementAddress) { 1074 // add the statement 1075 status_t error = sourceCode->AddSourceLocation( 1076 SourceLocation(statementLine, statementColumn)); 1077 if (error != B_OK) 1078 return error; 1079 1080 TRACE_LINES2(" -> statement: %#" B_PRIx64 " - %#" B_PRIx64 1081 ", source location: (%" B_PRId32 ", %" B_PRId32 ")\n", 1082 statementAddress, endAddress, statementLine, 1083 statementColumn); 1084 } 1085 1086 statementAddress = 0; 1087 } 1088 1089 // skip statements of other files 1090 if (!isOurFile) 1091 continue; 1092 1093 if (state.isStatement) { 1094 statementAddress = state.address; 1095 statementLine = state.line - 1; 1096 statementColumn = std::max(state.column - 1, (int32)0); 1097 } 1098 } 1099 1100 return B_OK; 1101 } 1102 1103 1104 int32 1105 DwarfImageDebugInfo::_GetSourceFileIndex(CompilationUnit* unit, 1106 LocatableFile* sourceFile) const 1107 { 1108 // get the index of the source file in the compilation unit for cheaper 1109 // comparison below 1110 const char* directory; 1111 for (int32 i = 0; const char* fileName = unit->FileAt(i, &directory); i++) { 1112 LocatableFile* file = fFileManager->GetSourceFile(directory, fileName); 1113 if (file != NULL) { 1114 BReference<LocatableFile> fileReference(file, true); 1115 if (file == sourceFile) { 1116 return i + 1; 1117 // indices are one-based 1118 } 1119 } 1120 } 1121 1122 return -1; 1123 } 1124 1125 1126 status_t 1127 DwarfImageDebugInfo::_CreateLocalVariables(CompilationUnit* unit, 1128 StackFrame* frame, FunctionID* functionID, 1129 DwarfStackFrameDebugInfo& factory, target_addr_t instructionPointer, 1130 target_addr_t lowPC, const EntryListWrapper& variableEntries, 1131 const EntryListWrapper& blockEntries) 1132 { 1133 TRACE_LOCALS("DwarfImageDebugInfo::_CreateLocalVariables(): ip: %#" B_PRIx64 1134 ", low PC: %#" B_PRIx64 "\n", instructionPointer, lowPC); 1135 1136 // iterate through the variables and add the ones in scope 1137 for (DebugInfoEntryList::ConstIterator it 1138 = variableEntries.list.GetIterator(); 1139 DIEVariable* variableEntry = dynamic_cast<DIEVariable*>(it.Next());) { 1140 1141 TRACE_LOCALS(" variableEntry %p, scope start: %" B_PRIu64 "\n", 1142 variableEntry, variableEntry->StartScope()); 1143 1144 // check the variable's scope 1145 if (instructionPointer < lowPC + variableEntry->StartScope()) 1146 continue; 1147 1148 // add the variable 1149 Variable* variable; 1150 if (factory.CreateLocalVariable(functionID, variableEntry, variable) 1151 != B_OK) { 1152 continue; 1153 } 1154 BReference<Variable> variableReference(variable, true); 1155 1156 if (!frame->AddLocalVariable(variable)) 1157 return B_NO_MEMORY; 1158 } 1159 1160 // iterate through the blocks and find the one we're currently in (if any) 1161 for (DebugInfoEntryList::ConstIterator it = blockEntries.list.GetIterator(); 1162 DIELexicalBlock* block = dynamic_cast<DIELexicalBlock*>(it.Next());) { 1163 1164 TRACE_LOCALS(" lexical block: %p\n", block); 1165 1166 // check whether the block has low/high PC attributes 1167 if (block->LowPC() != 0) { 1168 TRACE_LOCALS(" has lowPC\n"); 1169 1170 // yep, compare with the instruction pointer 1171 if (instructionPointer < block->LowPC() 1172 || instructionPointer >= block->HighPC()) { 1173 continue; 1174 } 1175 } else { 1176 TRACE_LOCALS(" no lowPC\n"); 1177 1178 // check the address ranges instead 1179 TargetAddressRangeList* rangeList = fFile->ResolveRangeList(unit, 1180 block->AddressRangesOffset()); 1181 if (rangeList == NULL) { 1182 TRACE_LOCALS(" failed to get ranges\n"); 1183 continue; 1184 } 1185 BReference<TargetAddressRangeList> rangeListReference(rangeList, 1186 true); 1187 1188 if (!rangeList->Contains(instructionPointer)) { 1189 TRACE_LOCALS(" ranges don't contain IP\n"); 1190 continue; 1191 } 1192 } 1193 1194 // found a block -- recurse 1195 return _CreateLocalVariables(unit, frame, functionID, factory, 1196 instructionPointer, lowPC, block->Variables(), block->Blocks()); 1197 } 1198 1199 return B_OK; 1200 } 1201 1202 1203 status_t 1204 DwarfImageDebugInfo::_CreateReturnValues(ReturnValueInfoList* returnValueInfos, 1205 Image* image, StackFrame* frame, DwarfStackFrameDebugInfo& factory) 1206 { 1207 for (int32 i = 0; i < returnValueInfos->CountItems(); i++) { 1208 Image* targetImage = image; 1209 ReturnValueInfo* valueInfo = returnValueInfos->ItemAt(i); 1210 target_addr_t subroutineAddress = valueInfo->SubroutineAddress(); 1211 CpuState* subroutineState = valueInfo->State(); 1212 if (!targetImage->ContainsAddress(subroutineAddress)) { 1213 // our current image doesn't contain the target function, 1214 // locate the one which does. 1215 targetImage = image->GetTeam()->ImageByAddress(subroutineAddress); 1216 if (targetImage == NULL) { 1217 // nothing we can do, try the next entry (if any) 1218 continue; 1219 } 1220 } 1221 1222 status_t result = B_OK; 1223 ImageDebugInfo* imageInfo = targetImage->GetImageDebugInfo(); 1224 if (imageInfo == NULL) { 1225 // the subroutine may have resolved to a different image 1226 // that doesn't have debug information available. 1227 continue; 1228 } 1229 1230 FunctionInstance* targetFunction; 1231 if (imageInfo->GetAddressSectionType(subroutineAddress) 1232 == ADDRESS_SECTION_TYPE_PLT) { 1233 result = fArchitecture->ResolvePICFunctionAddress( 1234 subroutineAddress, subroutineState, subroutineAddress); 1235 if (result != B_OK) 1236 continue; 1237 if (!targetImage->ContainsAddress(subroutineAddress)) { 1238 // the PLT entry doesn't necessarily point to a function 1239 // in the same image; as such we may need to try to 1240 // resolve the target address again. 1241 targetImage = image->GetTeam()->ImageByAddress( 1242 subroutineAddress); 1243 if (targetImage == NULL) 1244 continue; 1245 imageInfo = targetImage->GetImageDebugInfo(); 1246 if (imageInfo == NULL) { 1247 // As above, since the indirection here may have 1248 // landed us in an entirely different image, there is 1249 // no guarantee that debug info is available, 1250 // depending on which image it was. 1251 continue; 1252 } 1253 1254 } 1255 } 1256 1257 targetFunction = imageInfo->FunctionAtAddress(subroutineAddress); 1258 if (targetFunction != NULL) { 1259 DwarfFunctionDebugInfo* targetInfo = 1260 dynamic_cast<DwarfFunctionDebugInfo*>( 1261 targetFunction->GetFunctionDebugInfo()); 1262 if (targetInfo != NULL) { 1263 DIESubprogram* subProgram = targetInfo->SubprogramEntry(); 1264 DIEType* returnType = subProgram->ReturnType(); 1265 if (returnType == NULL) { 1266 // check if we have a specification, and if so, if that has 1267 // a return type 1268 subProgram = dynamic_cast<DIESubprogram*>( 1269 subProgram->Specification()); 1270 if (subProgram != NULL) 1271 returnType = subProgram->ReturnType(); 1272 1273 // function doesn't return a value, we're done. 1274 if (returnType == NULL) 1275 return B_OK; 1276 } 1277 1278 uint32 byteSize = 0; 1279 if (returnType->ByteSize() == NULL) { 1280 if (dynamic_cast<DIEAddressingType*>(returnType) != NULL) 1281 byteSize = fArchitecture->AddressSize(); 1282 } else 1283 byteSize = returnType->ByteSize()->constant; 1284 1285 // if we were unable to determine a size for the type, 1286 // simply default to the architecture's register width. 1287 if (byteSize == 0) 1288 byteSize = fArchitecture->AddressSize(); 1289 1290 ValueLocation* location; 1291 result = fArchitecture->GetReturnAddressLocation(frame, 1292 byteSize, location); 1293 if (result != B_OK) 1294 return result; 1295 1296 BReference<ValueLocation> locationReference(location, true); 1297 Variable* variable = NULL; 1298 BReference<FunctionID> idReference( 1299 targetFunction->GetFunctionID(), true); 1300 result = factory.CreateReturnValue(idReference, returnType, 1301 location, subroutineState, variable); 1302 if (result != B_OK) 1303 return result; 1304 1305 BReference<Variable> variableReference(variable, true); 1306 if (!frame->AddLocalVariable(variable)) 1307 return B_NO_MEMORY; 1308 } 1309 } 1310 } 1311 1312 return B_OK; 1313 } 1314 1315 1316 bool 1317 DwarfImageDebugInfo::_EvaluateBaseTypeConstraints(DIEType* type, 1318 const TypeLookupConstraints& constraints) const 1319 { 1320 if (constraints.HasBaseTypeName()) { 1321 BString baseEntryName; 1322 DIEType* baseTypeOwnerEntry = NULL; 1323 1324 switch (constraints.TypeKind()) { 1325 case TYPE_ADDRESS: 1326 { 1327 DIEAddressingType* addressType = 1328 dynamic_cast<DIEAddressingType*>(type); 1329 if (addressType != NULL) { 1330 baseTypeOwnerEntry = DwarfUtils::GetDIEByPredicate( 1331 addressType, HasTypePredicate<DIEAddressingType>()); 1332 } 1333 break; 1334 } 1335 case TYPE_ARRAY: 1336 { 1337 DIEArrayType* arrayType = 1338 dynamic_cast<DIEArrayType*>(type); 1339 if (arrayType != NULL) { 1340 baseTypeOwnerEntry = DwarfUtils::GetDIEByPredicate( 1341 arrayType, HasTypePredicate<DIEArrayType>()); 1342 } 1343 break; 1344 } 1345 default: 1346 break; 1347 } 1348 1349 if (baseTypeOwnerEntry != NULL) { 1350 DwarfUtils::GetFullyQualifiedDIEName(baseTypeOwnerEntry, 1351 baseEntryName); 1352 1353 if (baseEntryName != constraints.BaseTypeName()) 1354 return false; 1355 } 1356 } 1357 1358 return true; 1359 } 1360 1361 1362 status_t 1363 DwarfImageDebugInfo::_BuildTypeNameTable() 1364 { 1365 fTypeNameTable = new(std::nothrow) TypeNameTable; 1366 if (fTypeNameTable == NULL) 1367 return B_NO_MEMORY; 1368 1369 status_t error = fTypeNameTable->Init(); 1370 if (error != B_OK) 1371 return error; 1372 1373 // iterate through all compilation units 1374 for (int32 i = 0; CompilationUnit* unit = fFile->CompilationUnitAt(i); 1375 i++) { 1376 1377 // iterate through all types of the compilation unit 1378 for (DebugInfoEntryList::ConstIterator it 1379 = unit->UnitEntry()->Types().GetIterator(); 1380 DIEType* typeEntry = dynamic_cast<DIEType*>(it.Next());) { 1381 if (typeEntry->IsDeclaration()) 1382 continue; 1383 1384 BString typeEntryName; 1385 DwarfUtils::GetFullyQualifiedDIEName(typeEntry, typeEntryName); 1386 1387 TypeNameEntry* entry = fTypeNameTable->Lookup(typeEntryName); 1388 if (entry == NULL) { 1389 entry = new(std::nothrow) TypeNameEntry(typeEntryName); 1390 if (entry == NULL) 1391 return B_NO_MEMORY; 1392 1393 error = fTypeNameTable->Insert(entry); 1394 if (error != B_OK) 1395 return error; 1396 } 1397 1398 TypeEntryInfo* info = new(std::nothrow) TypeEntryInfo(typeEntry, 1399 unit); 1400 if (info == NULL) 1401 return B_NO_MEMORY; 1402 1403 if (!entry->types.AddItem(info)) { 1404 delete info; 1405 return B_NO_MEMORY; 1406 } 1407 } 1408 } 1409 1410 return B_OK; 1411 } 1412